smp_scu.c (3043B)
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 */ 29unsigned 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 */ 38void 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 73static 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 */ 99int 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 */ 107int 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 112int 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}