linux/arch/arm/kernel/smp_scu.c
<<
>>
Prefs
   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}
 125