summaryrefslogtreecommitdiff
path: root/arch/arm/mach-shmobile/smp-sh73a0.c
blob: d613113a04bda27f12042803a92fe239be567a9b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/*
 * SMP support for R-Mobile / SH-Mobile - sh73a0 portion
 *
 * Copyright (C) 2010  Magnus Damm
 * Copyright (C) 2010  Takashi Yoshii
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/smp.h>
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <mach/common.h>
#include <asm/cacheflush.h>
#include <asm/smp_plat.h>
#include <mach/sh73a0.h>
#include <asm/smp_scu.h>
#include <asm/smp_twd.h>

#define WUPCR		IOMEM(0xe6151010)
#define SRESCR		IOMEM(0xe6151018)
#define PSTR		IOMEM(0xe6151040)
#define SBAR		IOMEM(0xe6180020)
#define APARMBAREA	IOMEM(0xe6f10020)

#define PSTR_SHUTDOWN_MODE	3

#define SH73A0_SCU_BASE 0xf0000000

#ifdef CONFIG_HAVE_ARM_TWD
static DEFINE_TWD_LOCAL_TIMER(twd_local_timer, SH73A0_SCU_BASE + 0x600, 29);
void __init sh73a0_register_twd(void)
{
	twd_local_timer_register(&twd_local_timer);
}
#endif

static int __cpuinit sh73a0_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
	cpu = cpu_logical_map(cpu);

	if (((__raw_readl(PSTR) >> (4 * cpu)) & 3) == 3)
		__raw_writel(1 << cpu, WUPCR);	/* wake up */
	else
		__raw_writel(1 << cpu, SRESCR);	/* reset */

	return 0;
}

static void __init sh73a0_smp_prepare_cpus(unsigned int max_cpus)
{
	scu_enable(shmobile_scu_base);

	/* Map the reset vector (in headsmp-scu.S, headsmp.S) */
	__raw_writel(0, APARMBAREA);      /* 4k */
	__raw_writel(__pa(shmobile_boot_vector), SBAR);
	shmobile_boot_fn = virt_to_phys(shmobile_boot_scu);
	shmobile_boot_arg = (unsigned long)shmobile_scu_base;

	/* enable cache coherency on booting CPU */
	scu_power_mode(shmobile_scu_base, SCU_PM_NORMAL);
}

static void __init sh73a0_smp_init_cpus(void)
{
	/* setup sh73a0 specific SCU base */
	shmobile_scu_base = IOMEM(SH73A0_SCU_BASE);

	shmobile_smp_init_cpus(scu_get_core_count(shmobile_scu_base));
}

#ifdef CONFIG_HOTPLUG_CPU
static int sh73a0_cpu_kill(unsigned int cpu)
{

	int k;
	u32 pstr;

	/*
	 * wait until the power status register confirms the shutdown of the
	 * offline target
	 */
	for (k = 0; k < 1000; k++) {
		pstr = (__raw_readl(PSTR) >> (4 * cpu)) & 3;
		if (pstr == PSTR_SHUTDOWN_MODE)
			return 1;

		mdelay(1);
	}

	return 0;
}

static void sh73a0_cpu_die(unsigned int cpu)
{
	/* Set power off mode. This takes the CPU out of the MP cluster */
	scu_power_mode(shmobile_scu_base, SCU_PM_POWEROFF);

	/* Enter shutdown mode */
	cpu_do_idle();
}

static int sh73a0_cpu_disable(unsigned int cpu)
{
	return 0; /* CPU0 and CPU1 supported */
}
#endif /* CONFIG_HOTPLUG_CPU */

struct smp_operations sh73a0_smp_ops __initdata = {
	.smp_init_cpus		= sh73a0_smp_init_cpus,
	.smp_prepare_cpus	= sh73a0_smp_prepare_cpus,
	.smp_boot_secondary	= sh73a0_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
	.cpu_kill		= sh73a0_cpu_kill,
	.cpu_die		= sh73a0_cpu_die,
	.cpu_disable		= sh73a0_cpu_disable,
#endif
};