Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * SMP support for SoCs with APMU
0004  *
0005  * Copyright (C) 2014  Renesas Electronics Corporation
0006  * Copyright (C) 2013  Magnus Damm
0007  */
0008 #include <linux/cpu_pm.h>
0009 #include <linux/delay.h>
0010 #include <linux/init.h>
0011 #include <linux/io.h>
0012 #include <linux/ioport.h>
0013 #include <linux/of_address.h>
0014 #include <linux/smp.h>
0015 #include <linux/suspend.h>
0016 #include <linux/threads.h>
0017 #include <asm/cacheflush.h>
0018 #include <asm/cp15.h>
0019 #include <asm/proc-fns.h>
0020 #include <asm/smp_plat.h>
0021 #include <asm/suspend.h>
0022 #include "common.h"
0023 #include "rcar-gen2.h"
0024 
0025 static struct {
0026     void __iomem *iomem;
0027     int bit;
0028 } apmu_cpus[NR_CPUS];
0029 
0030 #define WUPCR_OFFS   0x10       /* Wake Up Control Register */
0031 #define PSTR_OFFS    0x40       /* Power Status Register */
0032 #define CPUNCR_OFFS(n)  (0x100 + (0x10 * (n)))
0033                     /* CPUn Power Status Control Register */
0034 #define DBGRCR_OFFS 0x180       /* Debug Resource Reset Control Reg. */
0035 
0036 /* Power Status Register */
0037 #define CPUNST(r, n)    (((r) >> (n * 4)) & 3)  /* CPUn Status Bit */
0038 #define CPUST_RUN   0       /* Run Mode */
0039 #define CPUST_STANDBY   3       /* CoreStandby Mode */
0040 
0041 /* Debug Resource Reset Control Register */
0042 #define DBGCPUREN   BIT(24)     /* CPU Other Reset Request Enable */
0043 #define DBGCPUNREN(n)   BIT((n) + 20)   /* CPUn Reset Request Enable */
0044 #define DBGCPUPREN  BIT(19)     /* CPU Peripheral Reset Req. Enable */
0045 
0046 static int __maybe_unused apmu_power_on(void __iomem *p, int bit)
0047 {
0048     /* request power on */
0049     writel_relaxed(BIT(bit), p + WUPCR_OFFS);
0050 
0051     /* wait for APMU to finish */
0052     while (readl_relaxed(p + WUPCR_OFFS) != 0)
0053         ;
0054 
0055     return 0;
0056 }
0057 
0058 static int __maybe_unused apmu_power_off(void __iomem *p, int bit)
0059 {
0060     /* request Core Standby for next WFI */
0061     writel_relaxed(3, p + CPUNCR_OFFS(bit));
0062     return 0;
0063 }
0064 
0065 static int __maybe_unused apmu_power_off_poll(void __iomem *p, int bit)
0066 {
0067     int k;
0068 
0069     for (k = 0; k < 1000; k++) {
0070         if (CPUNST(readl_relaxed(p + PSTR_OFFS), bit) == CPUST_STANDBY)
0071             return 1;
0072 
0073         mdelay(1);
0074     }
0075 
0076     return 0;
0077 }
0078 
0079 static int __maybe_unused apmu_wrap(int cpu, int (*fn)(void __iomem *p, int cpu))
0080 {
0081     void __iomem *p = apmu_cpus[cpu].iomem;
0082 
0083     return p ? fn(p, apmu_cpus[cpu].bit) : -EINVAL;
0084 }
0085 
0086 #if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_SUSPEND)
0087 /* nicked from arch/arm/mach-exynos/hotplug.c */
0088 static inline void cpu_enter_lowpower_a15(void)
0089 {
0090     unsigned int v;
0091 
0092     asm volatile(
0093     "       mrc     p15, 0, %0, c1, c0, 0\n"
0094     "       bic     %0, %0, %1\n"
0095     "       mcr     p15, 0, %0, c1, c0, 0\n"
0096         : "=&r" (v)
0097         : "Ir" (CR_C)
0098         : "cc");
0099 
0100     flush_cache_louis();
0101 
0102     asm volatile(
0103     /*
0104      * Turn off coherency
0105      */
0106     "       mrc     p15, 0, %0, c1, c0, 1\n"
0107     "       bic     %0, %0, %1\n"
0108     "       mcr     p15, 0, %0, c1, c0, 1\n"
0109         : "=&r" (v)
0110         : "Ir" (0x40)
0111         : "cc");
0112 
0113     isb();
0114     dsb();
0115 }
0116 
0117 static void shmobile_smp_apmu_cpu_shutdown(unsigned int cpu)
0118 {
0119 
0120     /* Select next sleep mode using the APMU */
0121     apmu_wrap(cpu, apmu_power_off);
0122 
0123     /* Do ARM specific CPU shutdown */
0124     cpu_enter_lowpower_a15();
0125 }
0126 #endif
0127 
0128 #if defined(CONFIG_HOTPLUG_CPU)
0129 static void shmobile_smp_apmu_cpu_die(unsigned int cpu)
0130 {
0131     /* For this particular CPU deregister boot vector */
0132     shmobile_smp_hook(cpu, 0, 0);
0133 
0134     /* Shutdown CPU core */
0135     shmobile_smp_apmu_cpu_shutdown(cpu);
0136 
0137     /* jump to shared mach-shmobile sleep / reset code */
0138     shmobile_smp_sleep();
0139 }
0140 
0141 static int shmobile_smp_apmu_cpu_kill(unsigned int cpu)
0142 {
0143     return apmu_wrap(cpu, apmu_power_off_poll);
0144 }
0145 #endif
0146 
0147 #if defined(CONFIG_SUSPEND)
0148 static int shmobile_smp_apmu_do_suspend(unsigned long cpu)
0149 {
0150     shmobile_smp_hook(cpu, __pa_symbol(cpu_resume), 0);
0151     shmobile_smp_apmu_cpu_shutdown(cpu);
0152     cpu_do_idle(); /* WFI selects Core Standby */
0153     return 1;
0154 }
0155 
0156 static inline void cpu_leave_lowpower(void)
0157 {
0158     unsigned int v;
0159 
0160     asm volatile("mrc    p15, 0, %0, c1, c0, 0\n"
0161              "       orr     %0, %0, %1\n"
0162              "       mcr     p15, 0, %0, c1, c0, 0\n"
0163              "       mrc     p15, 0, %0, c1, c0, 1\n"
0164              "       orr     %0, %0, %2\n"
0165              "       mcr     p15, 0, %0, c1, c0, 1\n"
0166              : "=&r" (v)
0167              : "Ir" (CR_C), "Ir" (0x40)
0168              : "cc");
0169 }
0170 
0171 static int shmobile_smp_apmu_enter_suspend(suspend_state_t state)
0172 {
0173     cpu_suspend(smp_processor_id(), shmobile_smp_apmu_do_suspend);
0174     cpu_leave_lowpower();
0175     return 0;
0176 }
0177 
0178 void __init shmobile_smp_apmu_suspend_init(void)
0179 {
0180     shmobile_suspend_ops.enter = shmobile_smp_apmu_enter_suspend;
0181 }
0182 #endif
0183 
0184 #ifdef CONFIG_SMP
0185 static void apmu_init_cpu(struct resource *res, int cpu, int bit)
0186 {
0187     u32 x;
0188 
0189     if ((cpu >= ARRAY_SIZE(apmu_cpus)) || apmu_cpus[cpu].iomem)
0190         return;
0191 
0192     apmu_cpus[cpu].iomem = ioremap(res->start, resource_size(res));
0193     apmu_cpus[cpu].bit = bit;
0194 
0195     pr_debug("apmu ioremap %d %d %pr\n", cpu, bit, res);
0196 
0197     /* Setup for debug mode */
0198     x = readl(apmu_cpus[cpu].iomem + DBGRCR_OFFS);
0199     x |= DBGCPUREN | DBGCPUNREN(bit) | DBGCPUPREN;
0200     writel(x, apmu_cpus[cpu].iomem + DBGRCR_OFFS);
0201 }
0202 
0203 static const struct of_device_id apmu_ids[] = {
0204     { .compatible = "renesas,apmu" },
0205     { /*sentinel*/ }
0206 };
0207 
0208 static void apmu_parse_dt(void (*fn)(struct resource *res, int cpu, int bit))
0209 {
0210     struct device_node *np_apmu, *np_cpu;
0211     struct resource res;
0212     int bit, index;
0213     u32 id;
0214 
0215     for_each_matching_node(np_apmu, apmu_ids) {
0216         /* only enable the cluster that includes the boot CPU */
0217         bool is_allowed = false;
0218 
0219         for (bit = 0; bit < CONFIG_NR_CPUS; bit++) {
0220             np_cpu = of_parse_phandle(np_apmu, "cpus", bit);
0221             if (np_cpu) {
0222                 if (!of_property_read_u32(np_cpu, "reg", &id)) {
0223                     if (id == cpu_logical_map(0)) {
0224                         is_allowed = true;
0225                         of_node_put(np_cpu);
0226                         break;
0227                     }
0228 
0229                 }
0230                 of_node_put(np_cpu);
0231             }
0232         }
0233         if (!is_allowed)
0234             continue;
0235 
0236         for (bit = 0; bit < CONFIG_NR_CPUS; bit++) {
0237             np_cpu = of_parse_phandle(np_apmu, "cpus", bit);
0238             if (np_cpu) {
0239                 if (!of_property_read_u32(np_cpu, "reg", &id)) {
0240                     index = get_logical_index(id);
0241                     if ((index >= 0) &&
0242                         !of_address_to_resource(np_apmu,
0243                                     0, &res))
0244                         fn(&res, index, bit);
0245                 }
0246                 of_node_put(np_cpu);
0247             }
0248         }
0249     }
0250 }
0251 
0252 static void __init shmobile_smp_apmu_setup_boot(void)
0253 {
0254     /* install boot code shared by all CPUs */
0255     shmobile_boot_fn = __pa_symbol(shmobile_smp_boot);
0256     shmobile_boot_fn_gen2 = shmobile_boot_fn;
0257 }
0258 
0259 static int shmobile_smp_apmu_boot_secondary(unsigned int cpu,
0260                         struct task_struct *idle)
0261 {
0262     /* For this particular CPU register boot vector */
0263     shmobile_smp_hook(cpu, __pa_symbol(shmobile_boot_apmu), 0);
0264 
0265     return apmu_wrap(cpu, apmu_power_on);
0266 }
0267 
0268 static void __init shmobile_smp_apmu_prepare_cpus_dt(unsigned int max_cpus)
0269 {
0270     shmobile_smp_apmu_setup_boot();
0271     apmu_parse_dt(apmu_init_cpu);
0272     rcar_gen2_pm_init();
0273 }
0274 
0275 static struct smp_operations apmu_smp_ops __initdata = {
0276     .smp_prepare_cpus   = shmobile_smp_apmu_prepare_cpus_dt,
0277     .smp_boot_secondary = shmobile_smp_apmu_boot_secondary,
0278 #ifdef CONFIG_HOTPLUG_CPU
0279     .cpu_can_disable    = shmobile_smp_cpu_can_disable,
0280     .cpu_die        = shmobile_smp_apmu_cpu_die,
0281     .cpu_kill       = shmobile_smp_apmu_cpu_kill,
0282 #endif
0283 };
0284 
0285 CPU_METHOD_OF_DECLARE(shmobile_smp_apmu, "renesas,apmu", &apmu_smp_ops);
0286 #endif /* CONFIG_SMP */