blob: d92188d702ab384901e7e1caa2a4d65f68aac09f [file] [log] [blame]
Magnus Dammf40aaf62012-01-10 17:44:39 +09001/*
2 * SMP support for R-Mobile / SH-Mobile - r8a7779 portion
3 *
4 * Copyright (C) 2011 Renesas Solutions Corp.
5 * Copyright (C) 2011 Magnus Damm
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20#include <linux/kernel.h>
21#include <linux/init.h>
22#include <linux/smp.h>
23#include <linux/spinlock.h>
24#include <linux/io.h>
25#include <linux/delay.h>
Rob Herring520f7bd2012-12-27 13:10:24 -060026#include <linux/irqchip/arm-gic.h>
Magnus Dammf40aaf62012-01-10 17:44:39 +090027#include <mach/common.h>
28#include <mach/r8a7779.h>
Will Deaconeb504392012-01-20 12:01:12 +010029#include <asm/smp_plat.h>
Magnus Dammf40aaf62012-01-10 17:44:39 +090030#include <asm/smp_scu.h>
31#include <asm/smp_twd.h>
Magnus Dammf40aaf62012-01-10 17:44:39 +090032
Rob Herringa2a47ca2012-03-09 17:16:40 -060033#define AVECR IOMEM(0xfe700040)
Magnus Damm3b94afa2013-02-13 22:46:48 +090034#define R8A7779_SCU_BASE IOMEM(0xf0000000)
35
36static void __iomem *shmobile_scu_base;
Magnus Dammf40aaf62012-01-10 17:44:39 +090037
38static struct r8a7779_pm_ch r8a7779_ch_cpu1 = {
39 .chan_offs = 0x40, /* PWRSR0 .. PWRER0 */
40 .chan_bit = 1, /* ARM1 */
41 .isr_bit = 1, /* ARM1 */
42};
43
44static struct r8a7779_pm_ch r8a7779_ch_cpu2 = {
45 .chan_offs = 0x40, /* PWRSR0 .. PWRER0 */
46 .chan_bit = 2, /* ARM2 */
47 .isr_bit = 2, /* ARM2 */
48};
49
50static struct r8a7779_pm_ch r8a7779_ch_cpu3 = {
51 .chan_offs = 0x40, /* PWRSR0 .. PWRER0 */
52 .chan_bit = 3, /* ARM3 */
53 .isr_bit = 3, /* ARM3 */
54};
55
56static struct r8a7779_pm_ch *r8a7779_ch_cpu[4] = {
57 [1] = &r8a7779_ch_cpu1,
58 [2] = &r8a7779_ch_cpu2,
59 [3] = &r8a7779_ch_cpu3,
60};
61
Simon Horman872b59832012-11-13 11:42:54 +090062static DEFINE_SPINLOCK(scu_lock);
63static unsigned long tmp;
64
Magnus Dammb759bd12012-05-10 14:57:22 +090065#ifdef CONFIG_HAVE_ARM_TWD
Marc Zyngier4200b162012-01-10 19:44:19 +000066static DEFINE_TWD_LOCAL_TIMER(twd_local_timer, 0xf0000600, 29);
67
Magnus Dammb759bd12012-05-10 14:57:22 +090068void __init r8a7779_register_twd(void)
69{
70 twd_local_timer_register(&twd_local_timer);
71}
72#endif
73
Simon Horman872b59832012-11-13 11:42:54 +090074static void modify_scu_cpu_psr(unsigned long set, unsigned long clr)
75{
Magnus Damm3b94afa2013-02-13 22:46:48 +090076 void __iomem *scu_base = shmobile_scu_base;
Simon Horman872b59832012-11-13 11:42:54 +090077
78 spin_lock(&scu_lock);
79 tmp = __raw_readl(scu_base + 8);
80 tmp &= ~clr;
81 tmp |= set;
82 spin_unlock(&scu_lock);
83
84 /* disable cache coherency after releasing the lock */
85 __raw_writel(tmp, scu_base + 8);
86}
87
Marc Zyngiera62580e2011-09-08 13:15:22 +010088static int r8a7779_platform_cpu_kill(unsigned int cpu)
Magnus Dammf40aaf62012-01-10 17:44:39 +090089{
90 struct r8a7779_pm_ch *ch = NULL;
91 int ret = -EIO;
92
93 cpu = cpu_logical_map(cpu);
94
95 /* disable cache coherency */
Simon Horman872b59832012-11-13 11:42:54 +090096 modify_scu_cpu_psr(3 << (cpu * 8), 0);
Magnus Dammf40aaf62012-01-10 17:44:39 +090097
98 if (cpu < ARRAY_SIZE(r8a7779_ch_cpu))
99 ch = r8a7779_ch_cpu[cpu];
100
101 if (ch)
102 ret = r8a7779_sysc_power_down(ch);
103
104 return ret ? ret : 1;
105}
106
Marc Zyngiera62580e2011-09-08 13:15:22 +0100107static int __maybe_unused r8a7779_cpu_kill(unsigned int cpu)
108{
109 int k;
110
111 /* this function is running on another CPU than the offline target,
112 * here we need wait for shutdown code in platform_cpu_die() to
113 * finish before asking SoC-specific code to power off the CPU core.
114 */
115 for (k = 0; k < 1000; k++) {
116 if (shmobile_cpu_is_dead(cpu))
117 return r8a7779_platform_cpu_kill(cpu);
118
119 mdelay(1);
120 }
121
122 return 0;
123}
124
125
126static void __cpuinit r8a7779_secondary_init(unsigned int cpu)
Magnus Dammf40aaf62012-01-10 17:44:39 +0900127{
128 gic_secondary_init(0);
129}
130
Marc Zyngiera62580e2011-09-08 13:15:22 +0100131static int __cpuinit r8a7779_boot_secondary(unsigned int cpu, struct task_struct *idle)
Magnus Dammf40aaf62012-01-10 17:44:39 +0900132{
133 struct r8a7779_pm_ch *ch = NULL;
134 int ret = -EIO;
135
136 cpu = cpu_logical_map(cpu);
137
138 /* enable cache coherency */
Simon Horman872b59832012-11-13 11:42:54 +0900139 modify_scu_cpu_psr(0, 3 << (cpu * 8));
Magnus Dammf40aaf62012-01-10 17:44:39 +0900140
141 if (cpu < ARRAY_SIZE(r8a7779_ch_cpu))
142 ch = r8a7779_ch_cpu[cpu];
143
144 if (ch)
145 ret = r8a7779_sysc_power_up(ch);
146
147 return ret;
148}
149
Marc Zyngiera62580e2011-09-08 13:15:22 +0100150static void __init r8a7779_smp_prepare_cpus(unsigned int max_cpus)
Magnus Dammf40aaf62012-01-10 17:44:39 +0900151{
Simon Horman872b59832012-11-13 11:42:54 +0900152 int cpu = cpu_logical_map(0);
153
Magnus Damm3b94afa2013-02-13 22:46:48 +0900154 scu_enable(shmobile_scu_base);
Magnus Dammf40aaf62012-01-10 17:44:39 +0900155
156 /* Map the reset vector (in headsmp.S) */
Rob Herringa2a47ca2012-03-09 17:16:40 -0600157 __raw_writel(__pa(shmobile_secondary_vector), AVECR);
Magnus Dammf40aaf62012-01-10 17:44:39 +0900158
159 /* enable cache coherency on CPU0 */
Simon Horman872b59832012-11-13 11:42:54 +0900160 modify_scu_cpu_psr(0, 3 << (cpu * 8));
Magnus Dammf40aaf62012-01-10 17:44:39 +0900161
162 r8a7779_pm_init();
163
164 /* power off secondary CPUs */
165 r8a7779_platform_cpu_kill(1);
166 r8a7779_platform_cpu_kill(2);
167 r8a7779_platform_cpu_kill(3);
168}
Marc Zyngiera62580e2011-09-08 13:15:22 +0100169
170static void __init r8a7779_smp_init_cpus(void)
171{
Magnus Damm3b94afa2013-02-13 22:46:48 +0900172 /* setup r8a7779 specific SCU base */
173 shmobile_scu_base = R8A7779_SCU_BASE;
Marc Zyngiera62580e2011-09-08 13:15:22 +0100174
Magnus Damm3b94afa2013-02-13 22:46:48 +0900175 shmobile_smp_init_cpus(scu_get_core_count(shmobile_scu_base));
Marc Zyngiera62580e2011-09-08 13:15:22 +0100176}
177
178struct smp_operations r8a7779_smp_ops __initdata = {
179 .smp_init_cpus = r8a7779_smp_init_cpus,
180 .smp_prepare_cpus = r8a7779_smp_prepare_cpus,
181 .smp_secondary_init = r8a7779_secondary_init,
182 .smp_boot_secondary = r8a7779_boot_secondary,
183#ifdef CONFIG_HOTPLUG_CPU
184 .cpu_kill = r8a7779_cpu_kill,
185 .cpu_die = shmobile_cpu_die,
186 .cpu_disable = shmobile_cpu_disable,
187#endif
188};