ARM: mvebu: add cpuidle support for Armada 38x

Unlike the Armada XP and the Armada 370, this SoC uses a Cortex A9
core. Consequently, the procedure to enter the idle state is
different: interaction with the SCU, not disabling snooping, etc.

Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Link: https://lkml.kernel.org/r/1406120453-29291-16-git-send-email-thomas.petazzoni@free-electrons.com
Signed-off-by: Jason Cooper <jason@lakedaemon.net>
This commit is contained in:
Gregory CLEMENT 2014-07-23 15:00:52 +02:00 committed by Jason Cooper
parent 3b9e4b1441
commit e53b1fd432
2 changed files with 98 additions and 1 deletions

View File

@ -29,6 +29,7 @@
#include <linux/smp.h>
#include <asm/cacheflush.h>
#include <asm/cp15.h>
#include <asm/smp_scu.h>
#include <asm/smp_plat.h>
#include <asm/suspend.h>
#include <asm/tlbflush.h>
@ -63,6 +64,18 @@
#define L2C_NFABRIC_PM_CTL 0x4
#define L2C_NFABRIC_PM_CTL_PWR_DOWN BIT(20)
/* PMSU delay registers */
#define PMSU_POWERDOWN_DELAY 0xF04
#define PMSU_POWERDOWN_DELAY_PMU BIT(1)
#define PMSU_POWERDOWN_DELAY_MASK 0xFFFE
#define PMSU_DFLT_ARMADA38X_DELAY 0x64
/* CA9 MPcore SoC Control registers */
#define MPCORE_RESET_CTL 0x64
#define MPCORE_RESET_CTL_L2 BIT(0)
#define MPCORE_RESET_CTL_DEBUG BIT(16)
#define SRAM_PHYS_BASE 0xFFFF0000
#define BOOTROM_BASE 0xFFF00000
#define BOOTROM_SIZE 0x100000
@ -74,6 +87,8 @@ extern void ll_disable_coherency(void);
extern void ll_enable_coherency(void);
extern void armada_370_xp_cpu_resume(void);
extern void armada_38x_cpu_resume(void);
static phys_addr_t pmsu_mp_phys_base;
static void __iomem *pmsu_mp_base;
@ -287,6 +302,32 @@ static int armada_370_xp_cpu_suspend(unsigned long deepidle)
return cpu_suspend(deepidle, armada_370_xp_pmsu_idle_enter);
}
static int armada_38x_do_cpu_suspend(unsigned long deepidle)
{
unsigned long flags = 0;
if (deepidle)
flags |= PMSU_PREPARE_DEEP_IDLE;
mvebu_v7_pmsu_idle_prepare(flags);
/*
* Already flushed cache, but do it again as the outer cache
* functions dirty the cache with spinlocks
*/
v7_exit_coherency_flush(louis);
scu_power_mode(mvebu_get_scu_base(), SCU_PM_POWEROFF);
cpu_do_idle();
return 1;
}
static int armada_38x_cpu_suspend(unsigned long deepidle)
{
return cpu_suspend(false, armada_38x_do_cpu_suspend);
}
/* No locking is needed because we only access per-CPU registers */
void mvebu_v7_pmsu_idle_exit(void)
{
@ -295,7 +336,6 @@ void mvebu_v7_pmsu_idle_exit(void)
if (pmsu_mp_base == NULL)
return;
/* cancel ask HW to power down the L2 Cache if possible */
reg = readl(pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(hw_cpu));
reg &= ~PMSU_CONTROL_AND_CONFIG_L2_PWDDN;
@ -359,6 +399,47 @@ static __init int armada_370_cpuidle_init(void)
return 0;
}
static __init int armada_38x_cpuidle_init(void)
{
struct device_node *np;
void __iomem *mpsoc_base;
u32 reg;
np = of_find_compatible_node(NULL, NULL,
"marvell,armada-380-coherency-fabric");
if (!np)
return -ENODEV;
of_node_put(np);
np = of_find_compatible_node(NULL, NULL,
"marvell,armada-380-mpcore-soc-ctrl");
if (!np)
return -ENODEV;
mpsoc_base = of_iomap(np, 0);
BUG_ON(!mpsoc_base);
of_node_put(np);
/* Set up reset mask when powering down the cpus */
reg = readl(mpsoc_base + MPCORE_RESET_CTL);
reg |= MPCORE_RESET_CTL_L2;
reg |= MPCORE_RESET_CTL_DEBUG;
writel(reg, mpsoc_base + MPCORE_RESET_CTL);
iounmap(mpsoc_base);
/* Set up delay */
reg = readl(pmsu_mp_base + PMSU_POWERDOWN_DELAY);
reg &= ~PMSU_POWERDOWN_DELAY_MASK;
reg |= PMSU_DFLT_ARMADA38X_DELAY;
reg |= PMSU_POWERDOWN_DELAY_PMU;
writel(reg, pmsu_mp_base + PMSU_POWERDOWN_DELAY);
mvebu_cpu_resume = armada_38x_cpu_resume;
mvebu_v7_cpuidle_device.dev.platform_data = armada_38x_cpu_suspend;
mvebu_v7_cpuidle_device.name = "cpuidle-armada-38x";
return 0;
}
static __init int armada_xp_cpuidle_init(void)
{
struct device_node *np;
@ -389,6 +470,8 @@ static int __init mvebu_v7_cpu_pm_init(void)
ret = armada_xp_cpuidle_init();
else if (of_machine_is_compatible("marvell,armada370"))
ret = armada_370_cpuidle_init();
else if (of_machine_is_compatible("marvell,armada380"))
ret = armada_38x_cpuidle_init();
else
return 0;

View File

@ -23,6 +23,20 @@ ARM_BE8(setend be ) @ go BE8 if entered LE
b cpu_resume
ENDPROC(armada_370_xp_cpu_resume)
ENTRY(armada_38x_cpu_resume)
/* do we need it for Armada 38x*/
ARM_BE8(setend be ) @ go BE8 if entered LE
bl v7_invalidate_l1
mrc p15, 4, r1, c15, c0 @ get SCU base address
orr r1, r1, #0x8 @ SCU CPU Power Status Register
mrc 15, 0, r0, cr0, cr0, 5 @ get the CPU ID
and r0, r0, #15
add r1, r1, r0
mov r0, #0x0
strb r0, [r1] @ switch SCU power state to Normal mode
b cpu_resume
ENDPROC(armada_38x_cpu_resume)
.global mvebu_boot_wa_start
.global mvebu_boot_wa_end