xref: /linux/arch/arm/mach-shmobile/platsmp-apmu.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1c44e182eSWolfram Sang // SPDX-License-Identifier: GPL-2.0
2a112de8cSMagnus Damm /*
3a112de8cSMagnus Damm  * SMP support for SoCs with APMU
4a112de8cSMagnus Damm  *
5a8d2ff39SHisashi Nakamura  * Copyright (C) 2014  Renesas Electronics Corporation
6a112de8cSMagnus Damm  * Copyright (C) 2013  Magnus Damm
7a112de8cSMagnus Damm  */
8d6d757c9Skeita kobayashi #include <linux/cpu_pm.h>
9a112de8cSMagnus Damm #include <linux/delay.h>
10a112de8cSMagnus Damm #include <linux/init.h>
11a112de8cSMagnus Damm #include <linux/io.h>
12a112de8cSMagnus Damm #include <linux/ioport.h>
13*6050cb1cSRob Herring #include <linux/of.h>
14a112de8cSMagnus Damm #include <linux/of_address.h>
15a112de8cSMagnus Damm #include <linux/smp.h>
16d6d757c9Skeita kobayashi #include <linux/suspend.h>
17784500beSMagnus Damm #include <linux/threads.h>
18a112de8cSMagnus Damm #include <asm/cacheflush.h>
19a112de8cSMagnus Damm #include <asm/cp15.h>
20d6d757c9Skeita kobayashi #include <asm/proc-fns.h>
21a112de8cSMagnus Damm #include <asm/smp_plat.h>
22d6d757c9Skeita kobayashi #include <asm/suspend.h>
23fd44aa5eSMagnus Damm #include "common.h"
245f3bca0dSMagnus Damm #include "rcar-gen2.h"
25a112de8cSMagnus Damm 
26a112de8cSMagnus Damm static struct {
27a112de8cSMagnus Damm 	void __iomem *iomem;
28a112de8cSMagnus Damm 	int bit;
29784500beSMagnus Damm } apmu_cpus[NR_CPUS];
30a112de8cSMagnus Damm 
31460d4117SGeert Uytterhoeven #define WUPCR_OFFS	 0x10		/* Wake Up Control Register */
32460d4117SGeert Uytterhoeven #define PSTR_OFFS	 0x40		/* Power Status Register */
33a112de8cSMagnus Damm #define CPUNCR_OFFS(n)	(0x100 + (0x10 * (n)))
34460d4117SGeert Uytterhoeven 					/* CPUn Power Status Control Register */
3510f778a9SGeert Uytterhoeven #define DBGRCR_OFFS	0x180		/* Debug Resource Reset Control Reg. */
36460d4117SGeert Uytterhoeven 
37460d4117SGeert Uytterhoeven /* Power Status Register */
38460d4117SGeert Uytterhoeven #define CPUNST(r, n)	(((r) >> (n * 4)) & 3)	/* CPUn Status Bit */
39460d4117SGeert Uytterhoeven #define CPUST_RUN	0		/* Run Mode */
40460d4117SGeert Uytterhoeven #define CPUST_STANDBY	3		/* CoreStandby Mode */
41a112de8cSMagnus Damm 
4210f778a9SGeert Uytterhoeven /* Debug Resource Reset Control Register */
4310f778a9SGeert Uytterhoeven #define DBGCPUREN	BIT(24)		/* CPU Other Reset Request Enable */
4410f778a9SGeert Uytterhoeven #define DBGCPUNREN(n)	BIT((n) + 20)	/* CPUn Reset Request Enable */
4510f778a9SGeert Uytterhoeven #define DBGCPUPREN	BIT(19)		/* CPU Peripheral Reset Req. Enable */
4610f778a9SGeert Uytterhoeven 
apmu_power_on(void __iomem * p,int bit)47784500beSMagnus Damm static int __maybe_unused apmu_power_on(void __iomem *p, int bit)
48a112de8cSMagnus Damm {
49a112de8cSMagnus Damm 	/* request power on */
50a112de8cSMagnus Damm 	writel_relaxed(BIT(bit), p + WUPCR_OFFS);
51a112de8cSMagnus Damm 
52a112de8cSMagnus Damm 	/* wait for APMU to finish */
53a112de8cSMagnus Damm 	while (readl_relaxed(p + WUPCR_OFFS) != 0)
54a112de8cSMagnus Damm 		;
55a112de8cSMagnus Damm 
56a112de8cSMagnus Damm 	return 0;
57a112de8cSMagnus Damm }
58a112de8cSMagnus Damm 
apmu_power_off(void __iomem * p,int bit)59151dd346SWolfram Sang static int __maybe_unused apmu_power_off(void __iomem *p, int bit)
60a112de8cSMagnus Damm {
61a112de8cSMagnus Damm 	/* request Core Standby for next WFI */
62a112de8cSMagnus Damm 	writel_relaxed(3, p + CPUNCR_OFFS(bit));
63a112de8cSMagnus Damm 	return 0;
64a112de8cSMagnus Damm }
65a112de8cSMagnus Damm 
apmu_power_off_poll(void __iomem * p,int bit)66784500beSMagnus Damm static int __maybe_unused apmu_power_off_poll(void __iomem *p, int bit)
67a112de8cSMagnus Damm {
68a112de8cSMagnus Damm 	int k;
69a112de8cSMagnus Damm 
70a112de8cSMagnus Damm 	for (k = 0; k < 1000; k++) {
71460d4117SGeert Uytterhoeven 		if (CPUNST(readl_relaxed(p + PSTR_OFFS), bit) == CPUST_STANDBY)
72a112de8cSMagnus Damm 			return 1;
73a112de8cSMagnus Damm 
74a112de8cSMagnus Damm 		mdelay(1);
75a112de8cSMagnus Damm 	}
76a112de8cSMagnus Damm 
77a112de8cSMagnus Damm 	return 0;
78a112de8cSMagnus Damm }
79a112de8cSMagnus Damm 
apmu_wrap(int cpu,int (* fn)(void __iomem * p,int cpu))80151dd346SWolfram Sang static int __maybe_unused apmu_wrap(int cpu, int (*fn)(void __iomem *p, int cpu))
81a112de8cSMagnus Damm {
82a112de8cSMagnus Damm 	void __iomem *p = apmu_cpus[cpu].iomem;
83a112de8cSMagnus Damm 
84a112de8cSMagnus Damm 	return p ? fn(p, apmu_cpus[cpu].bit) : -EINVAL;
85a112de8cSMagnus Damm }
86a112de8cSMagnus Damm 
873bf6d773SGeert Uytterhoeven #if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_SUSPEND)
883bf6d773SGeert Uytterhoeven /* nicked from arch/arm/mach-exynos/hotplug.c */
cpu_enter_lowpower_a15(void)893bf6d773SGeert Uytterhoeven static inline void cpu_enter_lowpower_a15(void)
903bf6d773SGeert Uytterhoeven {
913bf6d773SGeert Uytterhoeven 	unsigned int v;
923bf6d773SGeert Uytterhoeven 
933bf6d773SGeert Uytterhoeven 	asm volatile(
943bf6d773SGeert Uytterhoeven 	"       mrc     p15, 0, %0, c1, c0, 0\n"
953bf6d773SGeert Uytterhoeven 	"       bic     %0, %0, %1\n"
963bf6d773SGeert Uytterhoeven 	"       mcr     p15, 0, %0, c1, c0, 0\n"
973bf6d773SGeert Uytterhoeven 		: "=&r" (v)
983bf6d773SGeert Uytterhoeven 		: "Ir" (CR_C)
993bf6d773SGeert Uytterhoeven 		: "cc");
1003bf6d773SGeert Uytterhoeven 
1013bf6d773SGeert Uytterhoeven 	flush_cache_louis();
1023bf6d773SGeert Uytterhoeven 
1033bf6d773SGeert Uytterhoeven 	asm volatile(
1043bf6d773SGeert Uytterhoeven 	/*
1053bf6d773SGeert Uytterhoeven 	 * Turn off coherency
1063bf6d773SGeert Uytterhoeven 	 */
1073bf6d773SGeert Uytterhoeven 	"       mrc     p15, 0, %0, c1, c0, 1\n"
1083bf6d773SGeert Uytterhoeven 	"       bic     %0, %0, %1\n"
1093bf6d773SGeert Uytterhoeven 	"       mcr     p15, 0, %0, c1, c0, 1\n"
1103bf6d773SGeert Uytterhoeven 		: "=&r" (v)
1113bf6d773SGeert Uytterhoeven 		: "Ir" (0x40)
1123bf6d773SGeert Uytterhoeven 		: "cc");
1133bf6d773SGeert Uytterhoeven 
1143bf6d773SGeert Uytterhoeven 	isb();
1153bf6d773SGeert Uytterhoeven 	dsb();
1163bf6d773SGeert Uytterhoeven }
1173bf6d773SGeert Uytterhoeven 
shmobile_smp_apmu_cpu_shutdown(unsigned int cpu)1183bf6d773SGeert Uytterhoeven static void shmobile_smp_apmu_cpu_shutdown(unsigned int cpu)
1193bf6d773SGeert Uytterhoeven {
1203bf6d773SGeert Uytterhoeven 
1213bf6d773SGeert Uytterhoeven 	/* Select next sleep mode using the APMU */
1223bf6d773SGeert Uytterhoeven 	apmu_wrap(cpu, apmu_power_off);
1233bf6d773SGeert Uytterhoeven 
1243bf6d773SGeert Uytterhoeven 	/* Do ARM specific CPU shutdown */
1253bf6d773SGeert Uytterhoeven 	cpu_enter_lowpower_a15();
1263bf6d773SGeert Uytterhoeven }
1273bf6d773SGeert Uytterhoeven #endif
1283bf6d773SGeert Uytterhoeven 
1293bf6d773SGeert Uytterhoeven #if defined(CONFIG_HOTPLUG_CPU)
shmobile_smp_apmu_cpu_die(unsigned int cpu)1303bf6d773SGeert Uytterhoeven static void shmobile_smp_apmu_cpu_die(unsigned int cpu)
1313bf6d773SGeert Uytterhoeven {
1323bf6d773SGeert Uytterhoeven 	/* For this particular CPU deregister boot vector */
1333bf6d773SGeert Uytterhoeven 	shmobile_smp_hook(cpu, 0, 0);
1343bf6d773SGeert Uytterhoeven 
1353bf6d773SGeert Uytterhoeven 	/* Shutdown CPU core */
1363bf6d773SGeert Uytterhoeven 	shmobile_smp_apmu_cpu_shutdown(cpu);
1373bf6d773SGeert Uytterhoeven 
1383bf6d773SGeert Uytterhoeven 	/* jump to shared mach-shmobile sleep / reset code */
1393bf6d773SGeert Uytterhoeven 	shmobile_smp_sleep();
1403bf6d773SGeert Uytterhoeven }
1413bf6d773SGeert Uytterhoeven 
shmobile_smp_apmu_cpu_kill(unsigned int cpu)1423bf6d773SGeert Uytterhoeven static int shmobile_smp_apmu_cpu_kill(unsigned int cpu)
1433bf6d773SGeert Uytterhoeven {
1443bf6d773SGeert Uytterhoeven 	return apmu_wrap(cpu, apmu_power_off_poll);
1453bf6d773SGeert Uytterhoeven }
1463bf6d773SGeert Uytterhoeven #endif
1473bf6d773SGeert Uytterhoeven 
1483bf6d773SGeert Uytterhoeven #if defined(CONFIG_SUSPEND)
shmobile_smp_apmu_do_suspend(unsigned long cpu)1493bf6d773SGeert Uytterhoeven static int shmobile_smp_apmu_do_suspend(unsigned long cpu)
1503bf6d773SGeert Uytterhoeven {
1513bf6d773SGeert Uytterhoeven 	shmobile_smp_hook(cpu, __pa_symbol(cpu_resume), 0);
1523bf6d773SGeert Uytterhoeven 	shmobile_smp_apmu_cpu_shutdown(cpu);
1533bf6d773SGeert Uytterhoeven 	cpu_do_idle(); /* WFI selects Core Standby */
1543bf6d773SGeert Uytterhoeven 	return 1;
1553bf6d773SGeert Uytterhoeven }
1563bf6d773SGeert Uytterhoeven 
cpu_leave_lowpower(void)1573bf6d773SGeert Uytterhoeven static inline void cpu_leave_lowpower(void)
1583bf6d773SGeert Uytterhoeven {
1593bf6d773SGeert Uytterhoeven 	unsigned int v;
1603bf6d773SGeert Uytterhoeven 
1613bf6d773SGeert Uytterhoeven 	asm volatile("mrc    p15, 0, %0, c1, c0, 0\n"
1623bf6d773SGeert Uytterhoeven 		     "       orr     %0, %0, %1\n"
1633bf6d773SGeert Uytterhoeven 		     "       mcr     p15, 0, %0, c1, c0, 0\n"
1643bf6d773SGeert Uytterhoeven 		     "       mrc     p15, 0, %0, c1, c0, 1\n"
1653bf6d773SGeert Uytterhoeven 		     "       orr     %0, %0, %2\n"
1663bf6d773SGeert Uytterhoeven 		     "       mcr     p15, 0, %0, c1, c0, 1\n"
1673bf6d773SGeert Uytterhoeven 		     : "=&r" (v)
1683bf6d773SGeert Uytterhoeven 		     : "Ir" (CR_C), "Ir" (0x40)
1693bf6d773SGeert Uytterhoeven 		     : "cc");
1703bf6d773SGeert Uytterhoeven }
1713bf6d773SGeert Uytterhoeven 
shmobile_smp_apmu_enter_suspend(suspend_state_t state)1723bf6d773SGeert Uytterhoeven static int shmobile_smp_apmu_enter_suspend(suspend_state_t state)
1733bf6d773SGeert Uytterhoeven {
1743bf6d773SGeert Uytterhoeven 	cpu_suspend(smp_processor_id(), shmobile_smp_apmu_do_suspend);
1753bf6d773SGeert Uytterhoeven 	cpu_leave_lowpower();
1763bf6d773SGeert Uytterhoeven 	return 0;
1773bf6d773SGeert Uytterhoeven }
1783bf6d773SGeert Uytterhoeven 
shmobile_smp_apmu_suspend_init(void)1793bf6d773SGeert Uytterhoeven void __init shmobile_smp_apmu_suspend_init(void)
1803bf6d773SGeert Uytterhoeven {
1813bf6d773SGeert Uytterhoeven 	shmobile_suspend_ops.enter = shmobile_smp_apmu_enter_suspend;
1823bf6d773SGeert Uytterhoeven }
1833bf6d773SGeert Uytterhoeven #endif
1843bf6d773SGeert Uytterhoeven 
185d3f3fb0cSGeert Uytterhoeven #ifdef CONFIG_SMP
apmu_init_cpu(struct resource * res,int cpu,int bit)186a112de8cSMagnus Damm static void apmu_init_cpu(struct resource *res, int cpu, int bit)
187a112de8cSMagnus Damm {
18810f778a9SGeert Uytterhoeven 	u32 x;
18910f778a9SGeert Uytterhoeven 
190784500beSMagnus Damm 	if ((cpu >= ARRAY_SIZE(apmu_cpus)) || apmu_cpus[cpu].iomem)
191a112de8cSMagnus Damm 		return;
192a112de8cSMagnus Damm 
1934bdc0d67SChristoph Hellwig 	apmu_cpus[cpu].iomem = ioremap(res->start, resource_size(res));
194a112de8cSMagnus Damm 	apmu_cpus[cpu].bit = bit;
195a112de8cSMagnus Damm 
19656ff8731SLaurent Pinchart 	pr_debug("apmu ioremap %d %d %pr\n", cpu, bit, res);
19710f778a9SGeert Uytterhoeven 
19810f778a9SGeert Uytterhoeven 	/* Setup for debug mode */
19910f778a9SGeert Uytterhoeven 	x = readl(apmu_cpus[cpu].iomem + DBGRCR_OFFS);
20010f778a9SGeert Uytterhoeven 	x |= DBGCPUREN | DBGCPUNREN(bit) | DBGCPUPREN;
20110f778a9SGeert Uytterhoeven 	writel(x, apmu_cpus[cpu].iomem + DBGRCR_OFFS);
202a112de8cSMagnus Damm }
203a112de8cSMagnus Damm 
2045f3bca0dSMagnus Damm static const struct of_device_id apmu_ids[] = {
2055f3bca0dSMagnus Damm 	{ .compatible = "renesas,apmu" },
2065f3bca0dSMagnus Damm 	{ /*sentinel*/ }
2075f3bca0dSMagnus Damm };
2085f3bca0dSMagnus Damm 
apmu_parse_dt(void (* fn)(struct resource * res,int cpu,int bit))2095f3bca0dSMagnus Damm static void apmu_parse_dt(void (*fn)(struct resource *res, int cpu, int bit))
2105f3bca0dSMagnus Damm {
2115f3bca0dSMagnus Damm 	struct device_node *np_apmu, *np_cpu;
2125f3bca0dSMagnus Damm 	struct resource res;
2135f3bca0dSMagnus Damm 	int bit, index;
2145f3bca0dSMagnus Damm 
2155f3bca0dSMagnus Damm 	for_each_matching_node(np_apmu, apmu_ids) {
2165f3bca0dSMagnus Damm 		/* only enable the cluster that includes the boot CPU */
2175f3bca0dSMagnus Damm 		bool is_allowed = false;
2185f3bca0dSMagnus Damm 
2195f3bca0dSMagnus Damm 		for (bit = 0; bit < CONFIG_NR_CPUS; bit++) {
2205f3bca0dSMagnus Damm 			np_cpu = of_parse_phandle(np_apmu, "cpus", bit);
221*6050cb1cSRob Herring 			if (!np_cpu)
222*6050cb1cSRob Herring 				break;
223*6050cb1cSRob Herring 			if (of_cpu_node_to_id(np_cpu) == 0) {
2245f3bca0dSMagnus Damm 				is_allowed = true;
2255f3bca0dSMagnus Damm 				of_node_put(np_cpu);
2265f3bca0dSMagnus Damm 				break;
2275f3bca0dSMagnus Damm 			}
2285f3bca0dSMagnus Damm 			of_node_put(np_cpu);
2295f3bca0dSMagnus Damm 		}
2305f3bca0dSMagnus Damm 		if (!is_allowed)
2315f3bca0dSMagnus Damm 			continue;
2325f3bca0dSMagnus Damm 
2335f3bca0dSMagnus Damm 		for (bit = 0; bit < CONFIG_NR_CPUS; bit++) {
2345f3bca0dSMagnus Damm 			np_cpu = of_parse_phandle(np_apmu, "cpus", bit);
235*6050cb1cSRob Herring 			if (!np_cpu)
236*6050cb1cSRob Herring 				break;
237*6050cb1cSRob Herring 
238*6050cb1cSRob Herring 			index = of_cpu_node_to_id(np_cpu);
2395f3bca0dSMagnus Damm 			if ((index >= 0) &&
240*6050cb1cSRob Herring 			    !of_address_to_resource(np_apmu, 0, &res))
2415f3bca0dSMagnus Damm 				fn(&res, index, bit);
242*6050cb1cSRob Herring 
2435f3bca0dSMagnus Damm 			of_node_put(np_cpu);
2445f3bca0dSMagnus Damm 		}
2455f3bca0dSMagnus Damm 	}
2465f3bca0dSMagnus Damm }
2475f3bca0dSMagnus Damm 
shmobile_smp_apmu_setup_boot(void)2485f3bca0dSMagnus Damm static void __init shmobile_smp_apmu_setup_boot(void)
2495f3bca0dSMagnus Damm {
2505f3bca0dSMagnus Damm 	/* install boot code shared by all CPUs */
25164fc2a94SFlorian Fainelli 	shmobile_boot_fn = __pa_symbol(shmobile_smp_boot);
25201d675f1SFabrizio Castro 	shmobile_boot_fn_gen2 = shmobile_boot_fn;
2535f3bca0dSMagnus Damm }
2545f3bca0dSMagnus Damm 
shmobile_smp_apmu_boot_secondary(unsigned int cpu,struct task_struct * idle)2553bf6d773SGeert Uytterhoeven static int shmobile_smp_apmu_boot_secondary(unsigned int cpu,
2563bf6d773SGeert Uytterhoeven 					    struct task_struct *idle)
257a112de8cSMagnus Damm {
258a112de8cSMagnus Damm 	/* For this particular CPU register boot vector */
2593fd45a13SGeert Uytterhoeven 	shmobile_smp_hook(cpu, __pa_symbol(shmobile_boot_apmu), 0);
260a112de8cSMagnus Damm 
261a112de8cSMagnus Damm 	return apmu_wrap(cpu, apmu_power_on);
262a112de8cSMagnus Damm }
2635f3bca0dSMagnus Damm 
shmobile_smp_apmu_prepare_cpus_dt(unsigned int max_cpus)2645f3bca0dSMagnus Damm static void __init shmobile_smp_apmu_prepare_cpus_dt(unsigned int max_cpus)
2655f3bca0dSMagnus Damm {
2665f3bca0dSMagnus Damm 	shmobile_smp_apmu_setup_boot();
2675f3bca0dSMagnus Damm 	apmu_parse_dt(apmu_init_cpu);
2685f3bca0dSMagnus Damm 	rcar_gen2_pm_init();
2695f3bca0dSMagnus Damm }
2705f3bca0dSMagnus Damm 
2715f3bca0dSMagnus Damm static struct smp_operations apmu_smp_ops __initdata = {
2725f3bca0dSMagnus Damm 	.smp_prepare_cpus	= shmobile_smp_apmu_prepare_cpus_dt,
273d03c8f78SGeert Uytterhoeven 	.smp_boot_secondary	= shmobile_smp_apmu_boot_secondary,
2745f3bca0dSMagnus Damm #ifdef CONFIG_HOTPLUG_CPU
2755f3bca0dSMagnus Damm 	.cpu_can_disable	= shmobile_smp_cpu_can_disable,
2765f3bca0dSMagnus Damm 	.cpu_die		= shmobile_smp_apmu_cpu_die,
2775f3bca0dSMagnus Damm 	.cpu_kill		= shmobile_smp_apmu_cpu_kill,
278784500beSMagnus Damm #endif
2795f3bca0dSMagnus Damm };
2805f3bca0dSMagnus Damm 
2815f3bca0dSMagnus Damm CPU_METHOD_OF_DECLARE(shmobile_smp_apmu, "renesas,apmu", &apmu_smp_ops);
2825f3bca0dSMagnus Damm #endif /* CONFIG_SMP */
283