summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-shmobile/smp-sh73a0.c
blob: acb46a94ccdfb8ec967ed5e52e5a9593f531c414 (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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/*
 * 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 <linux/irqchip/arm-gic.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

static void __iomem *scu_base_addr(void)
{
	return (void __iomem *)0xf0000000;
}

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

static unsigned int __init sh73a0_get_core_count(void)
{
	void __iomem *scu_base = scu_base_addr();

	return scu_get_core_count(scu_base);
}

static void __cpuinit sh73a0_secondary_init(unsigned int cpu)
{
	gic_secondary_init(0);
}

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(scu_base_addr());

	/* Map the reset vector (in headsmp-sh73a0.S) */
	__raw_writel(0, APARMBAREA);      /* 4k */
	__raw_writel(__pa(sh73a0_secondary_vector), SBAR);

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

static void __init sh73a0_smp_init_cpus(void)
{
	unsigned int ncores = sh73a0_get_core_count();

	shmobile_smp_init_cpus(ncores);
}

#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)
{
	/*
	 * The ARM MPcore does not issue a cache coherency request for the L1
	 * cache when powering off single CPUs. We must take care of this and
	 * further caches.
	 */
	dsb();
	flush_cache_all();

	/* Set power off mode. This takes the CPU out of the MP cluster */
	scu_power_mode(scu_base_addr(), SCU_PM_POWEROFF);

	/* Enter shutdown mode */
	cpu_do_idle();
}
#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_secondary_init	= sh73a0_secondary_init,
	.smp_boot_secondary	= sh73a0_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
	.cpu_kill		= sh73a0_cpu_kill,
	.cpu_die		= sh73a0_cpu_die,
	.cpu_disable		= shmobile_cpu_disable_any,
#endif
};