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