aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-vexpress/Kconfig25
-rw-r--r--arch/arm/mach-vexpress/Makefile2
-rw-r--r--arch/arm/mach-vexpress/cpuidle-tc2.c141
-rw-r--r--arch/arm/mach-vexpress/hotplug-asm.S28
-rw-r--r--arch/arm/mach-vexpress/tc2-sleep.S76
-rw-r--r--arch/arm/mach-vexpress/tc2_pm.c23
6 files changed, 50 insertions, 245 deletions
diff --git a/arch/arm/mach-vexpress/Kconfig b/arch/arm/mach-vexpress/Kconfig
index 5a99485138c..bca54a33598 100644
--- a/arch/arm/mach-vexpress/Kconfig
+++ b/arch/arm/mach-vexpress/Kconfig
@@ -49,20 +49,6 @@ config ARCH_VEXPRESS_CORTEX_A5_A9_ERRATA
build a working kernel, you must also enable relevant core
tile support or Flattened Device Tree based support options.
-config ARCH_VEXPRESS_TC2_PM
- bool "Power Management Support for TC2 test-chip (EXPERIMENTAL)"
- depends on CPU_IDLE && PM
- select ARM_CPU_SUSPEND
- select ARCH_NEEDS_CPU_IDLE_COUPLED
- select ARM_SPC
- select ARM_CCI
- help
- Provides code that enables CPU idle power management on the
- TC2 testchip. It enables the CPU idle driver so that the kernel
- can enter cluster power down states provided by the power
- controller. Code is built on top of coupled C-state idle code
- since all CPUs need to be idle to enter cluster shutdown.
-
config ARCH_VEXPRESS_CA9X4
bool "Versatile Express Cortex-A9x4 tile"
@@ -74,4 +60,15 @@ config ARCH_VEXPRESS_TC2
help
Support for CPU and cluster power management on TC2.
+config VEXPRESS_TC2_CPUIDLE
+ bool "cpuidle support for TC2 test-chip (EXPERIMENTAL)"
+ depends on CPU_IDLE && PM && ARCH_VEXPRESS_TC2
+ select ARM_CPU_SUSPEND
+ select ARM_SPC
+ help
+ Provides code that enables CPU idle power management on the
+ TC2 testchip. It enables the CPU idle driver so that the kernel
+ can enter cluster power down states provided by the power
+ controller.
+
endmenu
diff --git a/arch/arm/mach-vexpress/Makefile b/arch/arm/mach-vexpress/Makefile
index e3948d02d4f..50a57510d76 100644
--- a/arch/arm/mach-vexpress/Makefile
+++ b/arch/arm/mach-vexpress/Makefile
@@ -9,4 +9,4 @@ obj-$(CONFIG_ARCH_VEXPRESS_CA9X4) += ct-ca9x4.o
obj-$(CONFIG_ARCH_VEXPRESS_TC2) += tc2_pm.o tc2_pm_setup.o
obj-$(CONFIG_SMP) += platsmp.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
-obj-$(CONFIG_ARCH_VEXPRESS_TC2_PM) += cpuidle-tc2.o hotplug-asm.o tc2-sleep.o
+obj-$(CONFIG_VEXPRESS_TC2_CPUIDLE) += cpuidle-tc2.o
diff --git a/arch/arm/mach-vexpress/cpuidle-tc2.c b/arch/arm/mach-vexpress/cpuidle-tc2.c
index 3b73d4a5dc5..0a983dd531b 100644
--- a/arch/arm/mach-vexpress/cpuidle-tc2.c
+++ b/arch/arm/mach-vexpress/cpuidle-tc2.c
@@ -20,6 +20,7 @@
#include <linux/module.h>
#include <linux/tick.h>
#include <linux/vexpress.h>
+#include <asm/bL_entry.h>
#include <asm/cpuidle.h>
#include <asm/cputype.h>
#include <asm/idmap.h>
@@ -51,7 +52,7 @@ static int tc2_cpuidle_simple_enter(struct cpuidle_device *dev,
return index;
}
-static int tc2_enter_coupled(struct cpuidle_device *dev,
+static int tc2_enter_powerdown(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int idx);
static struct cpuidle_state tc2_cpuidle_set[] __initdata = {
@@ -65,11 +66,10 @@ static struct cpuidle_state tc2_cpuidle_set[] __initdata = {
.desc = "ARM WFI",
},
[1] = {
- .enter = tc2_enter_coupled,
+ .enter = tc2_enter_powerdown,
.exit_latency = 300,
.target_residency = 1000,
- .flags = CPUIDLE_FLAG_TIME_VALID |
- CPUIDLE_FLAG_COUPLED,
+ .flags = CPUIDLE_FLAG_TIME_VALID,
.name = "C1",
.desc = "ARM power down",
},
@@ -83,45 +83,21 @@ struct cpuidle_driver tc2_idle_driver = {
static DEFINE_PER_CPU(struct cpuidle_device, tc2_idle_dev);
-#define NR_CLUSTERS 2
-static cpumask_t cluster_mask = CPU_MASK_NONE;
-
-extern void disable_clean_inv_dcache(int);
-static atomic_t abort_barrier[NR_CLUSTERS];
-
-extern void tc2_cpu_resume(void);
-extern void disable_snoops(void);
-
-static int notrace tc2_coupled_finisher(unsigned long arg)
+static int notrace tc2_powerdown_finisher(unsigned long arg)
{
unsigned int mpidr = read_cpuid_mpidr();
- unsigned int cpu = smp_processor_id();
unsigned int cluster = (mpidr >> 8) & 0xf;
- unsigned int weight = cpumask_weight(topology_core_cpumask(cpu));
- u8 wfi_weight = 0;
-
- cpuidle_coupled_parallel_barrier((struct cpuidle_device *)arg,
- &abort_barrier[cluster]);
- if (mpidr & 0xf) {
- disable_clean_inv_dcache(0);
- wfi();
- /* not reached */
- }
-
- while (wfi_weight != (weight - 1)) {
- wfi_weight = vexpress_spc_wfi_cpustat(cluster);
- wfi_weight = hweight8(wfi_weight);
- }
+ unsigned int cpu = mpidr & 0xf;
- vexpress_spc_powerdown_enable(cluster, 1);
- disable_clean_inv_dcache(1);
- disable_cci(cluster);
- disable_snoops();
+ bL_set_entry_vector(cpu, cluster, cpu_resume);
+ vexpress_spc_write_bxaddr_reg(cluster, cpu,
+ virt_to_phys(bL_entry_point));
+ bL_cpu_power_down();
return 1;
}
/*
- * tc2_enter_coupled - Programs CPU to enter the specified state
+ * tc2_enter_powerdown - Programs CPU to enter the specified state
* @dev: cpuidle device
* @drv: The target state to be programmed
* @idx: state index
@@ -129,38 +105,33 @@ static int notrace tc2_coupled_finisher(unsigned long arg)
* Called from the CPUidle framework to program the device to the
* specified target state selected by the governor.
*/
-static int tc2_enter_coupled(struct cpuidle_device *dev,
+static int tc2_enter_powerdown(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int idx)
{
struct timespec ts_preidle, ts_postidle, ts_idle;
int ret;
- int cluster = (read_cpuid_mpidr() >> 8) & 0xf;
+
/* Used to keep track of the total time in idle */
getnstimeofday(&ts_preidle);
- if (!cpu_isset(cluster, cluster_mask)) {
- cpuidle_coupled_parallel_barrier(dev,
- &abort_barrier[cluster]);
- goto shallow_out;
- }
-
BUG_ON(!irqs_disabled());
cpu_pm_enter();
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
- ret = cpu_suspend((unsigned long) dev, tc2_coupled_finisher);
-
+ ret = cpu_suspend((unsigned long) dev, tc2_powerdown_finisher);
if (ret)
BUG();
+ bL_cpu_powered_up();
+
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
cpu_pm_exit();
-shallow_out:
getnstimeofday(&ts_postidle);
+ local_irq_enable();
ts_idle = timespec_sub(ts_postidle, ts_preidle);
dev->last_residency = ts_idle.tv_nsec / NSEC_PER_USEC +
@@ -168,46 +139,6 @@ shallow_out:
return idx;
}
-static int idle_mask_show(struct seq_file *f, void *p)
-{
- char buf[256];
- bitmap_scnlistprintf(buf, 256, cpumask_bits(&cluster_mask),
- NR_CLUSTERS);
-
- seq_printf(f, "%s\n", buf);
-
- return 0;
-}
-
-static int idle_mask_open(struct inode *inode, struct file *file)
-{
- return single_open(file, idle_mask_show, inode->i_private);
-}
-
-static const struct file_operations cpuidle_fops = {
- .open = idle_mask_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static int idle_debug_set(void *data, u64 val)
-{
- if (val >= (unsigned)NR_CLUSTERS && val != 0xff) {
- pr_warning("Wrong parameter passed\n");
- return -EINVAL;
- }
- cpuidle_pause_and_lock();
- if (val == 0xff)
- cpumask_clear(&cluster_mask);
- else
- cpumask_set_cpu(val, &cluster_mask);
-
- cpuidle_resume_and_unlock();
- return 0;
-}
-DEFINE_SIMPLE_ATTRIBUTE(idle_debug_fops, NULL, idle_debug_set, "%llu\n");
-
/*
* tc2_idle_init
*
@@ -218,7 +149,6 @@ int __init tc2_idle_init(void)
{
struct cpuidle_device *dev;
int i, cpu_id;
- struct dentry *idle_debug, *file_debug;
struct cpuidle_driver *drv = &tc2_idle_driver;
if (!vexpress_spc_check_loaded()) {
@@ -240,10 +170,7 @@ int __init tc2_idle_init(void)
pr_err("CPUidle for CPU%d registered\n", cpu_id);
dev = &per_cpu(tc2_idle_dev, cpu_id);
dev->cpu = cpu_id;
- dev->safe_state_index = 0;
- cpumask_copy(&dev->coupled_cpus,
- topology_core_cpumask(cpu_id));
dev->state_count = drv->state_count;
if (cpuidle_register_device(dev)) {
@@ -253,40 +180,6 @@ int __init tc2_idle_init(void)
}
}
- idle_debug = debugfs_create_dir("idle_debug", NULL);
-
- if (IS_ERR_OR_NULL(idle_debug)) {
- printk(KERN_INFO "Error in creating idle debugfs directory\n");
- return 0;
- }
-
- file_debug = debugfs_create_file("enable_idle", S_IRUGO | S_IWGRP,
- idle_debug, NULL, &idle_debug_fops);
-
- if (IS_ERR_OR_NULL(file_debug)) {
- printk(KERN_INFO "Error in creating enable_idle file\n");
- return 0;
- }
-
- file_debug = debugfs_create_file("enable_mask", S_IRUGO | S_IWGRP,
- idle_debug, NULL, &cpuidle_fops);
-
- if (IS_ERR_OR_NULL(file_debug))
- printk(KERN_INFO "Error in creating enable_mask file\n");
-
- /* enable all wake-up IRQs by default */
- vexpress_spc_set_wake_intr(0x7ff);
- vexpress_flags_set(virt_to_phys(tc2_cpu_resume));
-
- /*
- * Enable idle by default for all possible clusters.
- * This must be done after all other setup to prevent the
- * possibility of clusters being powered down before they
- * are fully configured.
- */
- for (i = 0; i < NR_CLUSTERS; i++)
- cpumask_set_cpu(i, &cluster_mask);
-
return 0;
}
diff --git a/arch/arm/mach-vexpress/hotplug-asm.S b/arch/arm/mach-vexpress/hotplug-asm.S
deleted file mode 100644
index f63472edcc4..00000000000
--- a/arch/arm/mach-vexpress/hotplug-asm.S
+++ /dev/null
@@ -1,28 +0,0 @@
-#include <linux/linkage.h>
-#include <asm/asm-offsets.h>
-
- .text
-ENTRY(disable_clean_inv_dcache)
- ARM( stmfd sp!, {r4-r5, r7, r9-r11, lr} )
- THUMB( stmfd sp!, {r4-r7, r9-r11, lr} )
-
- mrc p15, 0, r3, c1, c0, 0
- bic r3, #4 @ clear C bit
- mcr p15, 0, r3, c1, c0, 0
- dsb
- isb
- mov r12, r0
- cmp r12, #0
- bleq v7_flush_dcache_louis
- cmp r12, #0
- blne v7_flush_dcache_all
- clrex
- mrc p15, 0, r3, c1, c0, 1
- bic r3, #0x40 @ clear SMP bit
- mcr p15, 0, r3, c1, c0, 1
- isb
- dsb
- ARM( ldmfd sp!, {r4-r5, r7, r9-r11, lr} )
- THUMB( ldmfd sp!, {r4-r7, r9-r11, lr} )
- mov pc, lr
-ENDPROC(disable_clean_inv_dcache)
diff --git a/arch/arm/mach-vexpress/tc2-sleep.S b/arch/arm/mach-vexpress/tc2-sleep.S
deleted file mode 100644
index 9bf8348fcc8..00000000000
--- a/arch/arm/mach-vexpress/tc2-sleep.S
+++ /dev/null
@@ -1,76 +0,0 @@
-#include <linux/linkage.h>
-
-#define SPC_PHYS_BASE 0x7FFF0000
-#define A15_CONF 0x400
-
-ENTRY(tc2_cpu_resume)
- mrc p15, 0, r0, c0, c0, 5
- ands r0, r0, #0xff00
- ldr r1, =SPC_PHYS_BASE
- mov r2, #A15_CONF
- add r1, r1, r2
- ldr r1, [r1]
- and r1, r1, #0x7
- cmp r1, r0, lsr #8
- adr r0, value
- addne r0, r0, #16
- ldmia r0, {r1, r2, r3, r4} @ CCI address, SCC snoop control & val
- mvn r3, r3 @ undo actions done at shutdown
- ldr r0, [r2]
- and r5, r0, r3
- str r5, [r2]
- mov r0, #3 @ enable CCI for the cluster
- str r0, [r1]
- adr r1, cci_ctrl
- ldr r1, [r1]
-loop:
- ldr r0, [r1]
- ands r0, r0, #1
- bne loop
- mov r0, #0 @ disable power down enable
- str r0, [r4]
- b cpu_resume
-ENDPROC(tc2_cpu_resume)
-
-ENTRY(disable_snoops)
- mrc p15, 0, r0, c0, c0, 5
- ands r0, r0, #0xff00
- ldr r1, scc_ptr
- ldr r1, [r1]
- mov r2, #A15_CONF
- add r1, r1, r2
- ldr r1, [r1]
- and r1, r1, #0x7
- cmp r1, r0, lsr #8
- adr r0, vvalue
- addne r0, r0, #8
- ldmia r0, {r2, r3} @ CCI address, SCC snoop control & val
- ldr r1, scc_ptr
- ldr r1, [r1]
- add r2, r1, r2
- ldr r0, [r2]
- orr r0, r0, r3
- dsb
- isb
- str r0, [r2]
- wfi
-ENDPROC(disable_snoops)
-
-cci_ctrl:
- .long 0x2c09000c
-value:
- .long 0x2c094000
- .long 0x7fff0404
- .long 0x180
- .long 0x7fff0b30
- .long 0x2c095000
- .long 0x7fff0504
- .long 0x2000
- .long 0x7fff0b34
-vvalue:
- .long 0x404
- .long 0x180
- .long 0x504
- .long 0x2000
-scc_ptr:
- .long vscc
diff --git a/arch/arm/mach-vexpress/tc2_pm.c b/arch/arm/mach-vexpress/tc2_pm.c
index 8be975e4b31..a5e18df801d 100644
--- a/arch/arm/mach-vexpress/tc2_pm.c
+++ b/arch/arm/mach-vexpress/tc2_pm.c
@@ -80,7 +80,7 @@ static int tc2_pm_power_up(unsigned int cpu, unsigned int cluster)
return 0;
}
-static void tc2_pm_power_down(void)
+static void tc2_pm_down(u64 residency)
{
unsigned int mpidr, cpu, cluster;
bool last_man = false, skip_wfi = false;
@@ -101,7 +101,8 @@ static void tc2_pm_power_down(void)
vexpress_spc_set_cpu_wakeup_irq(cpu, cluster, 1);
if (!tc2_pm_use_count[0][cluster] &&
!tc2_pm_use_count[1][cluster] &&
- !tc2_pm_use_count[2][cluster]) {
+ !tc2_pm_use_count[2][cluster] &&
+ (!residency || residency > 5000)) {
vexpress_spc_powerdown_enable(cluster, 1);
vexpress_spc_set_global_wakeup_intr(1);
last_man = true;
@@ -164,6 +165,23 @@ static void tc2_pm_power_down(void)
/* Not dead at this point? Let our caller cope. */
}
+static void tc2_pm_power_down(void)
+{
+ tc2_pm_down(0);
+}
+
+static void tc2_pm_suspend(u64 residency)
+{
+ unsigned int mpidr, cpu, cluster;
+
+ mpidr = read_cpuid_mpidr();
+ cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
+ cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
+ vexpress_spc_write_bxaddr_reg(cluster, cpu,
+ virt_to_phys(mcpm_entry_point));
+ tc2_pm_down(residency);
+}
+
static void tc2_pm_powered_up(void)
{
unsigned int mpidr, cpu, cluster;
@@ -199,6 +217,7 @@ static void tc2_pm_powered_up(void)
static const struct mcpm_platform_ops tc2_pm_power_ops = {
.power_up = tc2_pm_power_up,
.power_down = tc2_pm_power_down,
+ .suspend = tc2_pm_suspend,
.powered_up = tc2_pm_powered_up,
};