xref: /openbmc/linux/arch/arm/kernel/smp_scu.c (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
1*d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2a8cbcd92SRussell King /*
3a8cbcd92SRussell King  *  linux/arch/arm/kernel/smp_scu.c
4a8cbcd92SRussell King  *
5a8cbcd92SRussell King  *  Copyright (C) 2002 ARM Ltd.
6a8cbcd92SRussell King  *  All Rights Reserved
7a8cbcd92SRussell King  */
8a8cbcd92SRussell King #include <linux/init.h>
9a8cbcd92SRussell King #include <linux/io.h>
10a8cbcd92SRussell King 
110bd82adeSWill Deacon #include <asm/smp_plat.h>
12a8cbcd92SRussell King #include <asm/smp_scu.h>
13af73110dSCatalin Marinas #include <asm/cacheflush.h>
14f630c1bdSWill Deacon #include <asm/cputype.h>
15a8cbcd92SRussell King 
16a8cbcd92SRussell King #define SCU_CTRL		0x00
17f8f3d4edSShawn Guo #define SCU_ENABLE		(1 << 0)
18c716483cSShawn Guo #define SCU_STANDBY_ENABLE	(1 << 5)
19a8cbcd92SRussell King #define SCU_CONFIG		0x04
20a8cbcd92SRussell King #define SCU_CPU_STATUS		0x08
21936a4174SMartin Blumenstingl #define SCU_CPU_STATUS_MASK	GENMASK(1, 0)
22a8cbcd92SRussell King #define SCU_INVALIDATE		0x0c
23a8cbcd92SRussell King #define SCU_FPGA_REVISION	0x10
24a8cbcd92SRussell King 
2510cdc7e5SRob Herring #ifdef CONFIG_SMP
26a8cbcd92SRussell King /*
27a8cbcd92SRussell King  * Get the number of CPU cores from the SCU configuration
28a8cbcd92SRussell King  */
scu_get_core_count(void __iomem * scu_base)29a8cbcd92SRussell King unsigned int __init scu_get_core_count(void __iomem *scu_base)
30a8cbcd92SRussell King {
31099a4809SBen Dooks 	unsigned int ncores = readl_relaxed(scu_base + SCU_CONFIG);
32a8cbcd92SRussell King 	return (ncores & 0x03) + 1;
33a8cbcd92SRussell King }
34a8cbcd92SRussell King 
35a8cbcd92SRussell King /*
36a8cbcd92SRussell King  * Enable the SCU
37a8cbcd92SRussell King  */
scu_enable(void __iomem * scu_base)3826a527e6SShawn Guo void scu_enable(void __iomem *scu_base)
39a8cbcd92SRussell King {
40a8cbcd92SRussell King 	u32 scu_ctrl;
41a8cbcd92SRussell King 
42f630c1bdSWill Deacon #ifdef CONFIG_ARM_ERRATA_764369
43f630c1bdSWill Deacon 	/* Cortex-A9 only */
44ac52e83fSUwe Kleine-König 	if ((read_cpuid_id() & 0xff0ffff0) == 0x410fc090) {
45099a4809SBen Dooks 		scu_ctrl = readl_relaxed(scu_base + 0x30);
46f630c1bdSWill Deacon 		if (!(scu_ctrl & 1))
47099a4809SBen Dooks 			writel_relaxed(scu_ctrl | 0x1, scu_base + 0x30);
48f630c1bdSWill Deacon 	}
49f630c1bdSWill Deacon #endif
50f630c1bdSWill Deacon 
51099a4809SBen Dooks 	scu_ctrl = readl_relaxed(scu_base + SCU_CTRL);
529b229fa0SCatalin Marinas 	/* already enabled? */
53f8f3d4edSShawn Guo 	if (scu_ctrl & SCU_ENABLE)
549b229fa0SCatalin Marinas 		return;
559b229fa0SCatalin Marinas 
56f8f3d4edSShawn Guo 	scu_ctrl |= SCU_ENABLE;
57c716483cSShawn Guo 
58c716483cSShawn Guo 	/* Cortex-A9 earlier than r2p0 has no standby bit in SCU */
59c716483cSShawn Guo 	if ((read_cpuid_id() & 0xff0ffff0) == 0x410fc090 &&
60c716483cSShawn Guo 	    (read_cpuid_id() & 0x00f0000f) >= 0x00200000)
61c716483cSShawn Guo 		scu_ctrl |= SCU_STANDBY_ENABLE;
62c716483cSShawn Guo 
63099a4809SBen Dooks 	writel_relaxed(scu_ctrl, scu_base + SCU_CTRL);
64af73110dSCatalin Marinas 
65af73110dSCatalin Marinas 	/*
66af73110dSCatalin Marinas 	 * Ensure that the data accessed by CPU0 before the SCU was
67af73110dSCatalin Marinas 	 * initialised is visible to the other CPUs.
68af73110dSCatalin Marinas 	 */
69af73110dSCatalin Marinas 	flush_cache_all();
70a8cbcd92SRussell King }
7110cdc7e5SRob Herring #endif
72292ec42aSRussell King 
scu_set_power_mode_internal(void __iomem * scu_base,unsigned int logical_cpu,unsigned int mode)730606326eSMartin Blumenstingl static int scu_set_power_mode_internal(void __iomem *scu_base,
740606326eSMartin Blumenstingl 				       unsigned int logical_cpu,
750606326eSMartin Blumenstingl 				       unsigned int mode)
760606326eSMartin Blumenstingl {
770606326eSMartin Blumenstingl 	unsigned int val;
780606326eSMartin Blumenstingl 	int cpu = MPIDR_AFFINITY_LEVEL(cpu_logical_map(logical_cpu), 0);
790606326eSMartin Blumenstingl 
800606326eSMartin Blumenstingl 	if (mode > 3 || mode == 1 || cpu > 3)
810606326eSMartin Blumenstingl 		return -EINVAL;
820606326eSMartin Blumenstingl 
83936a4174SMartin Blumenstingl 	val = readb_relaxed(scu_base + SCU_CPU_STATUS + cpu);
84936a4174SMartin Blumenstingl 	val &= ~SCU_CPU_STATUS_MASK;
850606326eSMartin Blumenstingl 	val |= mode;
860606326eSMartin Blumenstingl 	writeb_relaxed(val, scu_base + SCU_CPU_STATUS + cpu);
870606326eSMartin Blumenstingl 
880606326eSMartin Blumenstingl 	return 0;
890606326eSMartin Blumenstingl }
900606326eSMartin Blumenstingl 
91292ec42aSRussell King /*
92292ec42aSRussell King  * Set the executing CPUs power mode as defined.  This will be in
93292ec42aSRussell King  * preparation for it executing a WFI instruction.
94292ec42aSRussell King  *
95292ec42aSRussell King  * This function must be called with preemption disabled, and as it
96292ec42aSRussell King  * has the side effect of disabling coherency, caches must have been
97292ec42aSRussell King  * flushed.  Interrupts must also have been disabled.
98292ec42aSRussell King  */
scu_power_mode(void __iomem * scu_base,unsigned int mode)99292ec42aSRussell King int scu_power_mode(void __iomem *scu_base, unsigned int mode)
100292ec42aSRussell King {
1010606326eSMartin Blumenstingl 	return scu_set_power_mode_internal(scu_base, smp_processor_id(), mode);
1020606326eSMartin Blumenstingl }
103292ec42aSRussell King 
1040606326eSMartin Blumenstingl /*
1050606326eSMartin Blumenstingl  * Set the given (logical) CPU's power mode to SCU_PM_NORMAL.
1060606326eSMartin Blumenstingl  */
scu_cpu_power_enable(void __iomem * scu_base,unsigned int cpu)1070606326eSMartin Blumenstingl int scu_cpu_power_enable(void __iomem *scu_base, unsigned int cpu)
1080606326eSMartin Blumenstingl {
1090606326eSMartin Blumenstingl 	return scu_set_power_mode_internal(scu_base, cpu, SCU_PM_NORMAL);
110292ec42aSRussell King }
111936a4174SMartin Blumenstingl 
scu_get_cpu_power_mode(void __iomem * scu_base,unsigned int logical_cpu)112936a4174SMartin Blumenstingl int scu_get_cpu_power_mode(void __iomem *scu_base, unsigned int logical_cpu)
113936a4174SMartin Blumenstingl {
114936a4174SMartin Blumenstingl 	unsigned int val;
115936a4174SMartin Blumenstingl 	int cpu = MPIDR_AFFINITY_LEVEL(cpu_logical_map(logical_cpu), 0);
116936a4174SMartin Blumenstingl 
117936a4174SMartin Blumenstingl 	if (cpu > 3)
118936a4174SMartin Blumenstingl 		return -EINVAL;
119936a4174SMartin Blumenstingl 
120936a4174SMartin Blumenstingl 	val = readb_relaxed(scu_base + SCU_CPU_STATUS + cpu);
121936a4174SMartin Blumenstingl 	val &= SCU_CPU_STATUS_MASK;
122936a4174SMartin Blumenstingl 
123936a4174SMartin Blumenstingl 	return val;
124936a4174SMartin Blumenstingl }
125