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