linux/arch/arm/kernel/smp_scu.c
<<
>>
Prefs
   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 */
  32unsigned 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 */
  41void 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
  76static 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 */
 102int 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 */
 110int 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
 115int 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