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