ARM: tegra114: cpuidle: add power down state
Subsystems (3)
v3 cover ARM: tegra114: cpuidle: add power down state
This series introduce CPU core power down state for CPU idle. When CPU go into this state, it saves it's context and needs a proper configuration in flow controller to power gate the CPU when CPU runs into WFI instruction. And the CPU also needs to set the IRQ as CPU power down idle wake up event in flow controller.
To prevent race conditions and ensure proper interrupt routing on Cortex-A15 CPUs when they are power-gated, add a CPU PM notifier call-back to reprogram the GIC CPU interface on PM entry. The GIC CPU interface will be reset back to its normal state by the common GIC CPU PM exit callback when the CPU wakes up.
This series depends on the patch of "tick: Fixtick_broadcast_pending_mask not cleared".
V3:
* use CPUIDLE_FLAG_TIMER_STOP flag
V2:
* clean up the CPUidle driver to make it more generic
Joseph Lo (3):
ARM: tegra114: Reprogram GIC CPU interface to bypass IRQ on CPU PM
entry
ARM: tegra114: add low level support for CPU idle powered-down mode
ARM: tegra114: cpuidle: add powered-down state
arch/arm/mach-tegra/cpuidle-tegra114.c | 48 +++++++++++++++++++++++++++++++++- arch/arm/mach-tegra/flowctrl.h | 2 ++ arch/arm/mach-tegra/irq.c | 40 ++++++++++++++++++++++++++++ arch/arm/mach-tegra/sleep-tegra30.S | 2 ++ 4 files changed, 91 insertions(+), 1 deletion(-)
Signature · 1 line
1.8.3.1
PATCH v3 1/3 ARM: tegra114: Reprogram GIC CPU interface to bypass IRQ on CPU PM entry
There is a difference between GICv1 and v2 when CPU in power management mode (aka CPU power down on Tegra). For GICv1, IRQ/FIQ interrupt lines going to CPU are same lines which are also used for wake-interrupt. Therefore, we cannot disable the GIC CPU interface if we need to use same interrupts for CPU wake purpose. This creates a race condition for CPU power off entry. Also, in GICv1, disabling GICv1 CPU interface puts GICv1 into bypass mode such that incoming legacy IRQ/FIQ are sent to CPU, which means disabling GIC CPU interface doesn't really disable IRQ/FIQ to CPU.
GICv2 provides a wake IRQ/FIQ (for wake-event purpose), which are not disabled by GIC CPU interface. This is done by adding a bypass override capability when the interrupts are disabled at the CPU interface. To support this, there are four bits about IRQ/FIQ BypassDisable in CPU interface Control Register. When the IRQ/FIQ not being driver by the CPU interface, each interrupt output signal can be deasserted rather than being driven by the legacy interrupt input.
So the wake-event can be used as wakeup signals to SoC (system power controller).
To prevent race conditions and ensure proper interrupt routing on Cortex-A15 CPUs when they are power-gated, add a CPU PM notifier call-back to reprogram the GIC CPU interface on PM entry. The GIC CPU interface will be reset back to its normal state by the common GIC CPU PM exit callback when the CPU wakes up.
Based on the work by: Scott Williams <[email protected]>
---
V3:
* no change
V2:
* no change
--- arch/arm/mach-tegra/irq.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+)
arch/arm/mach-tegra/irq.c +40 -0
@@ -18,10 +18,12 @@*/ #include <linux/kernel.h> +#include <linux/cpu_pm.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/io.h> #include <linux/of.h> +#include <linux/of_address.h> #include <linux/irqchip/arm-gic.h> #include <linux/syscore_ops.h>
@@ -65,6 +67,7 @@ static u32 cpu_ier[TEGRA_MAX_NUM_ICTLRS];static u32 cpu_iep[TEGRA_MAX_NUM_ICTLRS ];static u32 ictlr_wake_mask[TEGRA_MAX_NUM_ICTLRS ]; +static void __iomem *tegra_gic_cpu_base;#endif bool tegra_pending_sgi (void )
@@ -213,8 +216,43 @@ int tegra_legacy_irq_syscore_init(void)return 0 ; } + +static int tegra_gic_notifier (struct notifier_block *self, +unsigned long cmd,void *v) +{ +switch (cmd) { +case CPU_PM_ENTER : +writel_relaxed (0x1E0 , tegra_gic_cpu_base +GIC_CPU_CTRL ); +break ; + } + +return NOTIFY_OK ; +} + +static struct notifier_block tegra_gic_notifier_block = { + .notifier_call = tegra_gic_notifier, +}; + +static const struct of_device_id tegra114_dt_gic_match[]__initconst = { + { .compatible ="arm,cortex-a15-gic" }, + { } +}; + +static void tegra114_gic_cpu_pm_registration (void ) +{ +struct device_node *dn; + + dn =of_find_matching_node (NULL , tegra114_dt_gic_match); +if (!dn) +return ; + + tegra_gic_cpu_base =of_iomap (dn,1 ); + +cpu_pm_register_notifier (&tegra_gic_notifier_block); +}#else #define tegra_set_wake NULL +static void tegra114_gic_cpu_pm_registration (void ) { }#endif void __init tegra_init_irq (void )
@@ -252,4 +290,6 @@ void __init tegra_init_irq(void)if (!of_have_populated_dt ())gic_init (0 ,29 , distbase,IO_ADDRESS (TEGRA_ARM_PERIF_BASE +0x100 )); + +tegra114_gic_cpu_pm_registration (); }
Signature · 1 line
1.8.3.1
PATCH v3 1/3 ARM: tegra114: Reprogram GIC CPU interface to bypass IRQ on CPU PM entry
Quoted 17 lines · Joseph Lo
There is a difference between GICv1 and v2 when CPU in power management mode (aka CPU power down on Tegra). For GICv1, IRQ/FIQ interrupt lines going to CPU are same lines which are also used for wake-interrupt. Therefore, we cannot disable the GIC CPU interface if we need to use same interrupts for CPU wake purpose. This creates a race condition for CPU power off entry. Also, in GICv1, disabling GICv1 CPU interface puts GICv1 into bypass mode such that incoming legacy IRQ/FIQ are sent to CPU, which means disabling GIC CPU interface doesn't really disable IRQ/FIQ to CPU.
GICv2 provides a wake IRQ/FIQ (for wake-event purpose), which are not disabled by GIC CPU interface. This is done by adding a bypass override capability when the interrupts are disabled at the CPU interface. To support this, there are four bits about IRQ/FIQ BypassDisable in CPU interface Control Register. When the IRQ/FIQ not being driver by the CPU interface, each interrupt output signal can be deasserted rather than being driven by the legacy interrupt input.
...
arch/arm/mach-tegra/irq.c int tegra_legacy_irq_syscore_init(void) · line 225
Quoted 8 lines
arch/arm/mach-tegra/irq.c +6
-0+static int tegra_gic_notifier (struct notifier_block *self, +unsigned long cmd,void *v) +{ +switch (cmd) { +case CPU_PM_ENTER : +writel_relaxed (0x1E0 , tegra_gic_cpu_base +GIC_CPU_CTRL );
I assume that 0x1e0 is the "four bits" for IRQ/FIQ bypass disable that are mentioned in the commit description. are there #defines that can be used instead of literal 0x1e0 here?
PATCH v3 1/3 ARM: tegra114: Reprogram GIC CPU interface to bypass IRQ on CPU PM entry
Quoted 21 lines · Stephen Warren
Quoted 17 lines · Joseph Lo
There is a difference between GICv1 and v2 when CPU in power management mode (aka CPU power down on Tegra). For GICv1, IRQ/FIQ interrupt lines going to CPU are same lines which are also used for wake-interrupt. Therefore, we cannot disable the GIC CPU interface if we need to use same interrupts for CPU wake purpose. This creates a race condition for CPU power off entry. Also, in GICv1, disabling GICv1 CPU interface puts GICv1 into bypass mode such that incoming legacy IRQ/FIQ are sent to CPU, which means disabling GIC CPU interface doesn't really disable IRQ/FIQ to CPU.
GICv2 provides a wake IRQ/FIQ (for wake-event purpose), which are not disabled by GIC CPU interface. This is done by adding a bypass override capability when the interrupts are disabled at the CPU interface. To support this, there are four bits about IRQ/FIQ BypassDisable in CPU interface Control Register. When the IRQ/FIQ not being driver by the CPU interface, each interrupt output signal can be deasserted rather than being driven by the legacy interrupt input.
...
Quoted 1 line
arch/arm/mach-tegra/irq.c +0
-0
Quoted 10 lines
arch/arm/mach-tegra/irq.c int tegra_legacy_irq_syscore_init(void) · line 225
Quoted 6 lines
arch/arm/mach-tegra/irq.c +6
-0+static int tegra_gic_notifier (struct notifier_block *self, +unsigned long cmd,void *v) +{ +switch (cmd) { +case CPU_PM_ENTER : +writel_relaxed (0x1E0 , tegra_gic_cpu_base +GIC_CPU_CTRL );I assume that
0x1e0is the "four bits" for IRQ/FIQ bypass disable that are mentioned in the commit description. are there #defines that can be used instead of literal0x1e0here?
The common "arm-gic.h" didn't have the new define for GICv2 yet. So I didn't define something like GIC_CPU_{FIQ,IRQ}BypDisGrp{0,1} for Tegra.
Thanks,
Joseph
PATCH v3 2/3 ARM: tegra114: add low level support for CPU idle powered-down mode
In reply to Joseph LoThe flow controller would take care the power sequence when CPU idle in powered-down mode. It powered gate the CPU when CPU runs into WFI instruction. And wake up the CPU when event be triggered.
The sequence is below.
* setting wfi bitmap for the CPU as the halt event in the FLOW_CTRL_CPU_HALT_REG to monitor the CPU running into WFI,then power gate it
* setting IRQ and FIQ as wake up event to wake up CPU when event triggered
---
V3:
* no change
V2:
* no change
--- arch/arm/mach-tegra/flowctrl.h | 2 ++ arch/arm/mach-tegra/sleep-tegra30.S | 2 ++ 2 files changed, 4 insertions(+)
arch/arm/mach-tegra/flowctrl.h +2 -0
@@ -28,6 +28,8 @@#define FLOW_CTRL_SCLK_RESUME (1 << 27)#define FLOW_CTRL_HALT_CPU_IRQ (1 << 10)#define FLOW_CTRL_HALT_CPU_FIQ (1 << 8) +#define FLOW_CTRL_HALT_GIC_IRQ (1 << 9) +#define FLOW_CTRL_HALT_GIC_FIQ (1 << 8)#define FLOW_CTRL_CPU0_CSR 0x8#define FLOW_CTRL_CSR_INTR_FLAG (1 << 15)#define FLOW_CTRL_CSR_EVENT_FLAG (1 << 14)
arch/arm/mach-tegra/sleep-tegra30.S +2 -0
@@ -99,6 +99,8 @@ flow_ctrl_setting_for_lp2: cmp r10, #TEGRA30 moveq r3, #FLOW_CTRL_WAIT_FOR_INTERRUPT @ For LP2 movne r3, #FLOW_CTRL_WAITEVENT + orrne r3, r3, #FLOW_CTRL_HALT_GIC_IRQ + orrne r3, r3, #FLOW_CTRL_HALT_GIC_FIQ flow_ctrl_done: cmp r10, #TEGRA30 str r3, [r2]
Signature · 1 line
1.8.3.1
PATCH v3 3/3 ARM: tegra114: cpuidle: add powered-down state
In reply to Joseph LoThis supports CPU core power down on each CPU when CPU idle. When CPU go into this state, it saves it's context and needs a proper configuration in flow controller to power gate the CPU when CPU runs into WFI instruction. And the CPU also needs to set the IRQ as CPU power down idle wake up event in flow controller.
---
V3:
* use CPUIDLE_FLAG_TIMER_STOP flag
V2:
* remove some redundant code of memory barrier
* remove the function declaration by rearranging the coding sequence
--- arch/arm/mach-tegra/cpuidle-tegra114.c | 48 +++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-)
arch/arm/mach-tegra/cpuidle-tegra114.c +47 -1
@@ -17,15 +17,61 @@#include <linux/kernel.h> #include <linux/module.h> #include <linux/cpuidle.h> +#include <linux/cpu_pm.h> +#include <linux/clockchips.h> #include <asm/cpuidle.h> +#include <asm/suspend.h> +#include <asm/smp_plat.h> + +#include "pm.h" +#include "sleep.h" + +#ifdef CONFIG_PM_SLEEP +#define TEGRA114_MAX_STATES 2 +#else +#define TEGRA114_MAX_STATES 1 +#endif + +#ifdef CONFIG_PM_SLEEP +static int tegra114_idle_power_down (struct cpuidle_device *dev, +struct cpuidle_driver *drv, +int index) +{ +local_fiq_disable (); + +tegra_set_cpu_in_lp2 (); +cpu_pm_enter (); + +cpu_suspend (0 , tegra30_sleep_cpu_secondary_finish); + +cpu_pm_exit (); +tegra_clear_cpu_in_lp2 (); + +local_fiq_enable (); + +return index; +} +#endif static struct cpuidle_driver tegra_idle_driver = { .name ="tegra_idle" , .owner =THIS_MODULE , - .state_count =1 , + .state_count =TEGRA114_MAX_STATES , .states = { [0 ] =ARM_CPUIDLE_WFI_STATE_PWR (600 ), +#ifdef CONFIG_PM_SLEEP + [1] = { + .enter = tegra114_idle_power_down, + .exit_latency =500 , + .target_residency =1000 , + .power_usage =0 , + .flags =CPUIDLE_FLAG_TIME_VALID | +CPUIDLE_FLAG_TIMER_STOP , + .name ="powered-down" , + .desc ="CPU power gated" , + }, +#endif }, };
Signature · 1 line
1.8.3.1
v3 cover ARM: tegra114: cpuidle: add power down state
In reply to Joseph LoQuoted 6 lines · Joseph Lo
This series introduce CPU core power down state for CPU idle. When CPU go into this state, it saves it's context and needs a proper configuration in flow controller to power gate the CPU when CPU runs into WFI instruction. And the CPU also needs to set the IRQ as CPU power down idle wake up event in flow controller.
Quoted 8 lines
To prevent race conditions and ensure proper interrupt routing on Cortex-A15 CPUs when they are power-gated, add a CPU PM notifier call-back to reprogram the GIC CPU interface on PM entry. The GIC CPU interface will be reset back to its normal state by the common GIC CPU PM exit callback when the CPU wakes up.
This series depends on the patch of "tick: Fix
tick_broadcast_pending_masknot cleared".
Joseph, I applied all 3 of your patches/series on top of next-20130701, which does include dependency "tick: Fix tick_broadcast_pending_mask not cleared":
* ARM: tegra: cpuidle: use CPUIDLE_FLAG_TIMER_STOP flag
* This series
* ARM: tegra114: add support for system suspend
I find that at least on on Tegra114/Dalmore, CPU hotplug doesn't work correctly.
If I unplug CPU0, I get an immediate system hang:
root at localhost:~# ./cpuonline.py
echo 0 > /sys/devices/system/cpu/cpu0/online
[ 73.555696] IRQ73 no longer affine to CPU0 [ 73.561965] CPU0: shutdown
If I adjust my script not to hotplug CPU0, I get a hang when plugging a CPU back in:
root at localhost:~# ./cpuonline.py
echo 0 > /sys/devices/system/cpu/cpu2/online
[ 87.141128] CPU2: shutdown echo 0 > /sys/devices/system/cpu/cpu1/online [ 89.227597] CPU1: shutdown echo 0 > /sys/devices/system/cpu/cpu3/online [ 91.253870] CPU3: shutdown
echo 1 > /sys/devices/system/cpu/cpu1/online
or simpler:
root at localhost:~# echo 0 > /sys/devices/system/cpu/cpu1/online
[ 80.767825] CPU1: shutdown
root at localhost:~# echo 1 > /sys/devices/system/cpu/cpu1/online
Finally, if I boot afresh and enter system suspend, I see the system suspend OK, but pressing the power button on either the board itself or the attached PM342 debug board will not wake the system from sleep.
v3 cover ARM: tegra114: cpuidle: add power down state
Quoted 20 lines · Stephen Warren
Quoted 6 lines · Joseph Lo
This series introduce CPU core power down state for CPU idle. When CPU go into this state, it saves it's context and needs a proper configuration in flow controller to power gate the CPU when CPU runs into WFI instruction. And the CPU also needs to set the IRQ as CPU power down idle wake up event in flow controller.
Quoted 8 lines
To prevent race conditions and ensure proper interrupt routing on Cortex-A15 CPUs when they are power-gated, add a CPU PM notifier call-back to reprogram the GIC CPU interface on PM entry. The GIC CPU interface will be reset back to its normal state by the common GIC CPU PM exit callback when the CPU wakes up.
This series depends on the patch of "tick: Fix
tick_broadcast_pending_masknot cleared".Joseph, I applied all 3 of your patches/series on top of next-20130701, which does include dependency "tick: Fix
tick_broadcast_pending_masknot cleared":
Quoted 7 lines
* ARM: tegra: cpuidle: use
CPUIDLE_FLAG_TIMER_STOPflag
* This series
* ARM: tegra114: add support for system suspendI find that at least on on Tegra114/Dalmore, CPU hotplug doesn't work correctly.
Yes, it's due to the CPU hotplug function broken in next-20130701. The last working version was next-20130624 that I had tested and verified. It also breaks the resume function when enable non-boot CPUs. You can verify the suspend function by un-plug secondary CPUs first. I am checking what cause the CPU hotplug function broken on next-20130701.
Thanks,
Joseph