With SCU standby enabled, SCU CLK will be turned off when all processors are in WFI mode. And the clock will be turned on when any processor leaves WFI mode. This behavior should be preferable in terms of power efficiency of system idle. So let's set the SCU standby bit to enable the support in function scu_enable(). Cortex-A9 earlier than r2p0 has no standby bit in SCU, so we need to skip setting the bit for those. Signed-off-by: Shawn Guo <shawn.guo@freescale.com> Acked-by: Soren Brinkmann <soren.brinkmann@xilinx.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
96 lines
2.4 KiB
C
96 lines
2.4 KiB
C
/*
|
|
* linux/arch/arm/kernel/smp_scu.c
|
|
*
|
|
* Copyright (C) 2002 ARM Ltd.
|
|
* All Rights Reserved
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
#include <linux/init.h>
|
|
#include <linux/io.h>
|
|
|
|
#include <asm/smp_plat.h>
|
|
#include <asm/smp_scu.h>
|
|
#include <asm/cacheflush.h>
|
|
#include <asm/cputype.h>
|
|
|
|
#define SCU_CTRL 0x00
|
|
#define SCU_ENABLE (1 << 0)
|
|
#define SCU_STANDBY_ENABLE (1 << 5)
|
|
#define SCU_CONFIG 0x04
|
|
#define SCU_CPU_STATUS 0x08
|
|
#define SCU_INVALIDATE 0x0c
|
|
#define SCU_FPGA_REVISION 0x10
|
|
|
|
#ifdef CONFIG_SMP
|
|
/*
|
|
* Get the number of CPU cores from the SCU configuration
|
|
*/
|
|
unsigned int __init scu_get_core_count(void __iomem *scu_base)
|
|
{
|
|
unsigned int ncores = readl_relaxed(scu_base + SCU_CONFIG);
|
|
return (ncores & 0x03) + 1;
|
|
}
|
|
|
|
/*
|
|
* Enable the SCU
|
|
*/
|
|
void scu_enable(void __iomem *scu_base)
|
|
{
|
|
u32 scu_ctrl;
|
|
|
|
#ifdef CONFIG_ARM_ERRATA_764369
|
|
/* Cortex-A9 only */
|
|
if ((read_cpuid_id() & 0xff0ffff0) == 0x410fc090) {
|
|
scu_ctrl = readl_relaxed(scu_base + 0x30);
|
|
if (!(scu_ctrl & 1))
|
|
writel_relaxed(scu_ctrl | 0x1, scu_base + 0x30);
|
|
}
|
|
#endif
|
|
|
|
scu_ctrl = readl_relaxed(scu_base + SCU_CTRL);
|
|
/* already enabled? */
|
|
if (scu_ctrl & SCU_ENABLE)
|
|
return;
|
|
|
|
scu_ctrl |= SCU_ENABLE;
|
|
|
|
/* Cortex-A9 earlier than r2p0 has no standby bit in SCU */
|
|
if ((read_cpuid_id() & 0xff0ffff0) == 0x410fc090 &&
|
|
(read_cpuid_id() & 0x00f0000f) >= 0x00200000)
|
|
scu_ctrl |= SCU_STANDBY_ENABLE;
|
|
|
|
writel_relaxed(scu_ctrl, scu_base + SCU_CTRL);
|
|
|
|
/*
|
|
* Ensure that the data accessed by CPU0 before the SCU was
|
|
* initialised is visible to the other CPUs.
|
|
*/
|
|
flush_cache_all();
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Set the executing CPUs power mode as defined. This will be in
|
|
* preparation for it executing a WFI instruction.
|
|
*
|
|
* This function must be called with preemption disabled, and as it
|
|
* has the side effect of disabling coherency, caches must have been
|
|
* flushed. Interrupts must also have been disabled.
|
|
*/
|
|
int scu_power_mode(void __iomem *scu_base, unsigned int mode)
|
|
{
|
|
unsigned int val;
|
|
int cpu = MPIDR_AFFINITY_LEVEL(cpu_logical_map(smp_processor_id()), 0);
|
|
|
|
if (mode > 3 || mode == 1 || cpu > 3)
|
|
return -EINVAL;
|
|
|
|
val = readb_relaxed(scu_base + SCU_CPU_STATUS + cpu) & ~0x03;
|
|
val |= mode;
|
|
writeb_relaxed(val, scu_base + SCU_CPU_STATUS + cpu);
|
|
|
|
return 0;
|
|
}
|