1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * linux/arch/arm/kernel/smp_scu.c 4 * 5 * Copyright (C) 2002 ARM Ltd. 6 * All Rights Reserved 7 */ 8 #include <linux/init.h> 9 #include <linux/io.h> 10 11 #include <asm/smp_plat.h> 12 #include <asm/smp_scu.h> 13 #include <asm/cacheflush.h> 14 #include <asm/cputype.h> 15 16 #define SCU_CTRL 0x00 17 #define SCU_ENABLE (1 << 0) 18 #define SCU_STANDBY_ENABLE (1 << 5) 19 #define SCU_CONFIG 0x04 20 #define SCU_CPU_STATUS 0x08 21 #define SCU_CPU_STATUS_MASK GENMASK(1, 0) 22 #define SCU_INVALIDATE 0x0c 23 #define SCU_FPGA_REVISION 0x10 24 25 #ifdef CONFIG_SMP 26 /* 27 * Get the number of CPU cores from the SCU configuration 28 */ 29 unsigned int __init scu_get_core_count(void __iomem *scu_base) 30 { 31 unsigned int ncores = readl_relaxed(scu_base + SCU_CONFIG); 32 return (ncores & 0x03) + 1; 33 } 34 35 /* 36 * Enable the SCU 37 */ 38 void scu_enable(void __iomem *scu_base) 39 { 40 u32 scu_ctrl; 41 42 #ifdef CONFIG_ARM_ERRATA_764369 43 /* Cortex-A9 only */ 44 if ((read_cpuid_id() & 0xff0ffff0) == 0x410fc090) { 45 scu_ctrl = readl_relaxed(scu_base + 0x30); 46 if (!(scu_ctrl & 1)) 47 writel_relaxed(scu_ctrl | 0x1, scu_base + 0x30); 48 } 49 #endif 50 51 scu_ctrl = readl_relaxed(scu_base + SCU_CTRL); 52 /* already enabled? */ 53 if (scu_ctrl & SCU_ENABLE) 54 return; 55 56 scu_ctrl |= SCU_ENABLE; 57 58 /* Cortex-A9 earlier than r2p0 has no standby bit in SCU */ 59 if ((read_cpuid_id() & 0xff0ffff0) == 0x410fc090 && 60 (read_cpuid_id() & 0x00f0000f) >= 0x00200000) 61 scu_ctrl |= SCU_STANDBY_ENABLE; 62 63 writel_relaxed(scu_ctrl, scu_base + SCU_CTRL); 64 65 /* 66 * Ensure that the data accessed by CPU0 before the SCU was 67 * initialised is visible to the other CPUs. 68 */ 69 flush_cache_all(); 70 } 71 #endif 72 73 static int scu_set_power_mode_internal(void __iomem *scu_base, 74 unsigned int logical_cpu, 75 unsigned int mode) 76 { 77 unsigned int val; 78 int cpu = MPIDR_AFFINITY_LEVEL(cpu_logical_map(logical_cpu), 0); 79 80 if (mode > 3 || mode == 1 || cpu > 3) 81 return -EINVAL; 82 83 val = readb_relaxed(scu_base + SCU_CPU_STATUS + cpu); 84 val &= ~SCU_CPU_STATUS_MASK; 85 val |= mode; 86 writeb_relaxed(val, scu_base + SCU_CPU_STATUS + cpu); 87 88 return 0; 89 } 90 91 /* 92 * Set the executing CPUs power mode as defined. This will be in 93 * preparation for it executing a WFI instruction. 94 * 95 * This function must be called with preemption disabled, and as it 96 * has the side effect of disabling coherency, caches must have been 97 * flushed. Interrupts must also have been disabled. 98 */ 99 int scu_power_mode(void __iomem *scu_base, unsigned int mode) 100 { 101 return scu_set_power_mode_internal(scu_base, smp_processor_id(), mode); 102 } 103 104 /* 105 * Set the given (logical) CPU's power mode to SCU_PM_NORMAL. 106 */ 107 int scu_cpu_power_enable(void __iomem *scu_base, unsigned int cpu) 108 { 109 return scu_set_power_mode_internal(scu_base, cpu, SCU_PM_NORMAL); 110 } 111 112 int scu_get_cpu_power_mode(void __iomem *scu_base, unsigned int logical_cpu) 113 { 114 unsigned int val; 115 int cpu = MPIDR_AFFINITY_LEVEL(cpu_logical_map(logical_cpu), 0); 116 117 if (cpu > 3) 118 return -EINVAL; 119 120 val = readb_relaxed(scu_base + SCU_CPU_STATUS + cpu); 121 val &= SCU_CPU_STATUS_MASK; 122 123 return val; 124 } 125
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.