![Will Deacon](/assets/img/avatar_default.png)
scu_power_mode changes the power mode for the current CPU, which it determines from smp_processor_id(). However, this assumes that the physical CPU number is equal to Linux's logical CPU number and if this is not true, we will power off the wrong CPU. This patch uses cpu_logical_map to translate the logical CPU number into a physical one in scu_power_mode. Reported-by: Lorenzo Pieralisi <Lorenzo.Pieralisi@arm.com> Signed-off-by: Will Deacon <will.deacon@arm.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
89 lines
2.1 KiB
C
89 lines
2.1 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_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 = __raw_readl(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(CPUID_ID) & 0xff0ffff0) == 0x410fc090) {
|
|
scu_ctrl = __raw_readl(scu_base + 0x30);
|
|
if (!(scu_ctrl & 1))
|
|
__raw_writel(scu_ctrl | 0x1, scu_base + 0x30);
|
|
}
|
|
#endif
|
|
|
|
scu_ctrl = __raw_readl(scu_base + SCU_CTRL);
|
|
/* already enabled? */
|
|
if (scu_ctrl & 1)
|
|
return;
|
|
|
|
scu_ctrl |= 1;
|
|
__raw_writel(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 = cpu_logical_map(smp_processor_id());
|
|
|
|
if (mode > 3 || mode == 1 || cpu > 3)
|
|
return -EINVAL;
|
|
|
|
val = __raw_readb(scu_base + SCU_CPU_STATUS + cpu) & ~0x03;
|
|
val |= mode;
|
|
__raw_writeb(val, scu_base + SCU_CPU_STATUS + cpu);
|
|
|
|
return 0;
|
|
}
|