~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/arm/mach-shmobile/platsmp-apmu.c

Version: ~ [ linux-5.5-rc6 ] ~ [ linux-5.4.11 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.95 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.164 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.209 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.209 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.81 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  * SMP support for SoCs with APMU
  3  *
  4  * Copyright (C) 2014  Renesas Electronics Corporation
  5  * Copyright (C) 2013  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 version 2 as
  9  * published by the Free Software Foundation.
 10  */
 11 #include <linux/cpu_pm.h>
 12 #include <linux/delay.h>
 13 #include <linux/init.h>
 14 #include <linux/io.h>
 15 #include <linux/ioport.h>
 16 #include <linux/of_address.h>
 17 #include <linux/smp.h>
 18 #include <linux/suspend.h>
 19 #include <linux/threads.h>
 20 #include <asm/cacheflush.h>
 21 #include <asm/cp15.h>
 22 #include <asm/proc-fns.h>
 23 #include <asm/smp_plat.h>
 24 #include <asm/suspend.h>
 25 #include "common.h"
 26 #include "platsmp-apmu.h"
 27 #include "rcar-gen2.h"
 28 
 29 static struct {
 30         void __iomem *iomem;
 31         int bit;
 32 } apmu_cpus[NR_CPUS];
 33 
 34 #define WUPCR_OFFS       0x10           /* Wake Up Control Register */
 35 #define PSTR_OFFS        0x40           /* Power Status Register */
 36 #define CPUNCR_OFFS(n)  (0x100 + (0x10 * (n)))
 37                                         /* CPUn Power Status Control Register */
 38 #define DBGRCR_OFFS     0x180           /* Debug Resource Reset Control Reg. */
 39 
 40 /* Power Status Register */
 41 #define CPUNST(r, n)    (((r) >> (n * 4)) & 3)  /* CPUn Status Bit */
 42 #define CPUST_RUN       0               /* Run Mode */
 43 #define CPUST_STANDBY   3               /* CoreStandby Mode */
 44 
 45 /* Debug Resource Reset Control Register */
 46 #define DBGCPUREN       BIT(24)         /* CPU Other Reset Request Enable */
 47 #define DBGCPUNREN(n)   BIT((n) + 20)   /* CPUn Reset Request Enable */
 48 #define DBGCPUPREN      BIT(19)         /* CPU Peripheral Reset Req. Enable */
 49 
 50 static int __maybe_unused apmu_power_on(void __iomem *p, int bit)
 51 {
 52         /* request power on */
 53         writel_relaxed(BIT(bit), p + WUPCR_OFFS);
 54 
 55         /* wait for APMU to finish */
 56         while (readl_relaxed(p + WUPCR_OFFS) != 0)
 57                 ;
 58 
 59         return 0;
 60 }
 61 
 62 static int __maybe_unused apmu_power_off(void __iomem *p, int bit)
 63 {
 64         /* request Core Standby for next WFI */
 65         writel_relaxed(3, p + CPUNCR_OFFS(bit));
 66         return 0;
 67 }
 68 
 69 static int __maybe_unused apmu_power_off_poll(void __iomem *p, int bit)
 70 {
 71         int k;
 72 
 73         for (k = 0; k < 1000; k++) {
 74                 if (CPUNST(readl_relaxed(p + PSTR_OFFS), bit) == CPUST_STANDBY)
 75                         return 1;
 76 
 77                 mdelay(1);
 78         }
 79 
 80         return 0;
 81 }
 82 
 83 static int __maybe_unused apmu_wrap(int cpu, int (*fn)(void __iomem *p, int cpu))
 84 {
 85         void __iomem *p = apmu_cpus[cpu].iomem;
 86 
 87         return p ? fn(p, apmu_cpus[cpu].bit) : -EINVAL;
 88 }
 89 
 90 #ifdef CONFIG_SMP
 91 static void apmu_init_cpu(struct resource *res, int cpu, int bit)
 92 {
 93         u32 x;
 94 
 95         if ((cpu >= ARRAY_SIZE(apmu_cpus)) || apmu_cpus[cpu].iomem)
 96                 return;
 97 
 98         apmu_cpus[cpu].iomem = ioremap_nocache(res->start, resource_size(res));
 99         apmu_cpus[cpu].bit = bit;
100 
101         pr_debug("apmu ioremap %d %d %pr\n", cpu, bit, res);
102 
103         /* Setup for debug mode */
104         x = readl(apmu_cpus[cpu].iomem + DBGRCR_OFFS);
105         x |= DBGCPUREN | DBGCPUNREN(bit) | DBGCPUPREN;
106         writel(x, apmu_cpus[cpu].iomem + DBGRCR_OFFS);
107 }
108 
109 static void apmu_parse_cfg(void (*fn)(struct resource *res, int cpu, int bit),
110                            struct rcar_apmu_config *apmu_config, int num)
111 {
112         int id;
113         int k;
114         int bit, index;
115         bool is_allowed;
116 
117         for (k = 0; k < num; k++) {
118                 /* only enable the cluster that includes the boot CPU */
119                 is_allowed = false;
120                 for (bit = 0; bit < ARRAY_SIZE(apmu_config[k].cpus); bit++) {
121                         id = apmu_config[k].cpus[bit];
122                         if (id >= 0) {
123                                 if (id == cpu_logical_map(0))
124                                         is_allowed = true;
125                         }
126                 }
127                 if (!is_allowed)
128                         continue;
129 
130                 for (bit = 0; bit < ARRAY_SIZE(apmu_config[k].cpus); bit++) {
131                         id = apmu_config[k].cpus[bit];
132                         if (id >= 0) {
133                                 index = get_logical_index(id);
134                                 if (index >= 0)
135                                         fn(&apmu_config[k].iomem, index, bit);
136                         }
137                 }
138         }
139 }
140 
141 static const struct of_device_id apmu_ids[] = {
142         { .compatible = "renesas,apmu" },
143         { /*sentinel*/ }
144 };
145 
146 static void apmu_parse_dt(void (*fn)(struct resource *res, int cpu, int bit))
147 {
148         struct device_node *np_apmu, *np_cpu;
149         struct resource res;
150         int bit, index;
151         u32 id;
152 
153         for_each_matching_node(np_apmu, apmu_ids) {
154                 /* only enable the cluster that includes the boot CPU */
155                 bool is_allowed = false;
156 
157                 for (bit = 0; bit < CONFIG_NR_CPUS; bit++) {
158                         np_cpu = of_parse_phandle(np_apmu, "cpus", bit);
159                         if (np_cpu) {
160                                 if (!of_property_read_u32(np_cpu, "reg", &id)) {
161                                         if (id == cpu_logical_map(0)) {
162                                                 is_allowed = true;
163                                                 of_node_put(np_cpu);
164                                                 break;
165                                         }
166 
167                                 }
168                                 of_node_put(np_cpu);
169                         }
170                 }
171                 if (!is_allowed)
172                         continue;
173 
174                 for (bit = 0; bit < CONFIG_NR_CPUS; bit++) {
175                         np_cpu = of_parse_phandle(np_apmu, "cpus", bit);
176                         if (np_cpu) {
177                                 if (!of_property_read_u32(np_cpu, "reg", &id)) {
178                                         index = get_logical_index(id);
179                                         if ((index >= 0) &&
180                                             !of_address_to_resource(np_apmu,
181                                                                     0, &res))
182                                                 fn(&res, index, bit);
183                                 }
184                                 of_node_put(np_cpu);
185                         }
186                 }
187         }
188 }
189 
190 static void __init shmobile_smp_apmu_setup_boot(void)
191 {
192         /* install boot code shared by all CPUs */
193         shmobile_boot_fn = __pa_symbol(shmobile_smp_boot);
194         shmobile_boot_fn_gen2 = shmobile_boot_fn;
195 }
196 
197 void __init shmobile_smp_apmu_prepare_cpus(unsigned int max_cpus,
198                                            struct rcar_apmu_config *apmu_config,
199                                            int num)
200 {
201         shmobile_smp_apmu_setup_boot();
202         apmu_parse_cfg(apmu_init_cpu, apmu_config, num);
203 }
204 
205 int shmobile_smp_apmu_boot_secondary(unsigned int cpu, struct task_struct *idle)
206 {
207         /* For this particular CPU register boot vector */
208         shmobile_smp_hook(cpu, __pa_symbol(shmobile_boot_apmu), 0);
209 
210         return apmu_wrap(cpu, apmu_power_on);
211 }
212 
213 static void __init shmobile_smp_apmu_prepare_cpus_dt(unsigned int max_cpus)
214 {
215         shmobile_smp_apmu_setup_boot();
216         apmu_parse_dt(apmu_init_cpu);
217         rcar_gen2_pm_init();
218 }
219 
220 static struct smp_operations apmu_smp_ops __initdata = {
221         .smp_prepare_cpus       = shmobile_smp_apmu_prepare_cpus_dt,
222         .smp_boot_secondary     = shmobile_smp_apmu_boot_secondary,
223 #ifdef CONFIG_HOTPLUG_CPU
224         .cpu_can_disable        = shmobile_smp_cpu_can_disable,
225         .cpu_die                = shmobile_smp_apmu_cpu_die,
226         .cpu_kill               = shmobile_smp_apmu_cpu_kill,
227 #endif
228 };
229 
230 CPU_METHOD_OF_DECLARE(shmobile_smp_apmu, "renesas,apmu", &apmu_smp_ops);
231 #endif /* CONFIG_SMP */
232 
233 #if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_SUSPEND)
234 /* nicked from arch/arm/mach-exynos/hotplug.c */
235 static inline void cpu_enter_lowpower_a15(void)
236 {
237         unsigned int v;
238 
239         asm volatile(
240         "       mrc     p15, 0, %0, c1, c0, 0\n"
241         "       bic     %0, %0, %1\n"
242         "       mcr     p15, 0, %0, c1, c0, 0\n"
243                 : "=&r" (v)
244                 : "Ir" (CR_C)
245                 : "cc");
246 
247         flush_cache_louis();
248 
249         asm volatile(
250         /*
251          * Turn off coherency
252          */
253         "       mrc     p15, 0, %0, c1, c0, 1\n"
254         "       bic     %0, %0, %1\n"
255         "       mcr     p15, 0, %0, c1, c0, 1\n"
256                 : "=&r" (v)
257                 : "Ir" (0x40)
258                 : "cc");
259 
260         isb();
261         dsb();
262 }
263 
264 static void shmobile_smp_apmu_cpu_shutdown(unsigned int cpu)
265 {
266 
267         /* Select next sleep mode using the APMU */
268         apmu_wrap(cpu, apmu_power_off);
269 
270         /* Do ARM specific CPU shutdown */
271         cpu_enter_lowpower_a15();
272 }
273 
274 static inline void cpu_leave_lowpower(void)
275 {
276         unsigned int v;
277 
278         asm volatile("mrc    p15, 0, %0, c1, c0, 0\n"
279                      "       orr     %0, %0, %1\n"
280                      "       mcr     p15, 0, %0, c1, c0, 0\n"
281                      "       mrc     p15, 0, %0, c1, c0, 1\n"
282                      "       orr     %0, %0, %2\n"
283                      "       mcr     p15, 0, %0, c1, c0, 1\n"
284                      : "=&r" (v)
285                      : "Ir" (CR_C), "Ir" (0x40)
286                      : "cc");
287 }
288 #endif
289 
290 #if defined(CONFIG_HOTPLUG_CPU)
291 void shmobile_smp_apmu_cpu_die(unsigned int cpu)
292 {
293         /* For this particular CPU deregister boot vector */
294         shmobile_smp_hook(cpu, 0, 0);
295 
296         /* Shutdown CPU core */
297         shmobile_smp_apmu_cpu_shutdown(cpu);
298 
299         /* jump to shared mach-shmobile sleep / reset code */
300         shmobile_smp_sleep();
301 }
302 
303 int shmobile_smp_apmu_cpu_kill(unsigned int cpu)
304 {
305         return apmu_wrap(cpu, apmu_power_off_poll);
306 }
307 #endif
308 
309 #if defined(CONFIG_SUSPEND)
310 static int shmobile_smp_apmu_do_suspend(unsigned long cpu)
311 {
312         shmobile_smp_hook(cpu, __pa_symbol(cpu_resume), 0);
313         shmobile_smp_apmu_cpu_shutdown(cpu);
314         cpu_do_idle(); /* WFI selects Core Standby */
315         return 1;
316 }
317 
318 static int shmobile_smp_apmu_enter_suspend(suspend_state_t state)
319 {
320         cpu_suspend(smp_processor_id(), shmobile_smp_apmu_do_suspend);
321         cpu_leave_lowpower();
322         return 0;
323 }
324 
325 void __init shmobile_smp_apmu_suspend_init(void)
326 {
327         shmobile_suspend_ops.enter = shmobile_smp_apmu_enter_suspend;
328 }
329 #endif
330 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp