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_INVALIDATE 0x0c 25#define SCU_FPGA_REVISION 0x10 26 27#ifdef CONFIG_SMP 28/* 29 * Get the number of CPU cores from the SCU configuration 30 */ 31unsigned int __init scu_get_core_count(void __iomem *scu_base) 32{ 33 unsigned int ncores = readl_relaxed(scu_base + SCU_CONFIG); 34 return (ncores & 0x03) + 1; 35} 36 37/* 38 * Enable the SCU 39 */ 40void scu_enable(void __iomem *scu_base) 41{ 42 u32 scu_ctrl; 43 44#ifdef CONFIG_ARM_ERRATA_764369 45 /* Cortex-A9 only */ 46 if ((read_cpuid_id() & 0xff0ffff0) == 0x410fc090) { 47 scu_ctrl = readl_relaxed(scu_base + 0x30); 48 if (!(scu_ctrl & 1)) 49 writel_relaxed(scu_ctrl | 0x1, scu_base + 0x30); 50 } 51#endif 52 53 scu_ctrl = readl_relaxed(scu_base + SCU_CTRL); 54 /* already enabled? */ 55 if (scu_ctrl & SCU_ENABLE) 56 return; 57 58 scu_ctrl |= SCU_ENABLE; 59 60 /* Cortex-A9 earlier than r2p0 has no standby bit in SCU */ 61 if ((read_cpuid_id() & 0xff0ffff0) == 0x410fc090 && 62 (read_cpuid_id() & 0x00f0000f) >= 0x00200000) 63 scu_ctrl |= SCU_STANDBY_ENABLE; 64 65 writel_relaxed(scu_ctrl, scu_base + SCU_CTRL); 66 67 /* 68 * Ensure that the data accessed by CPU0 before the SCU was 69 * initialised is visible to the other CPUs. 70 */ 71 flush_cache_all(); 72} 73#endif 74 75/* 76 * Set the executing CPUs power mode as defined. This will be in 77 * preparation for it executing a WFI instruction. 78 * 79 * This function must be called with preemption disabled, and as it 80 * has the side effect of disabling coherency, caches must have been 81 * flushed. Interrupts must also have been disabled. 82 */ 83int scu_power_mode(void __iomem *scu_base, unsigned int mode) 84{ 85 unsigned int val; 86 int cpu = MPIDR_AFFINITY_LEVEL(cpu_logical_map(smp_processor_id()), 0); 87 88 if (mode > 3 || mode == 1 || cpu > 3) 89 return -EINVAL; 90 91 val = readb_relaxed(scu_base + SCU_CPU_STATUS + cpu) & ~0x03; 92 val |= mode; 93 writeb_relaxed(val, scu_base + SCU_CPU_STATUS + cpu); 94 95 return 0; 96} 97