diff options
-rw-r--r-- | arch/arm/mach-vexpress/Kconfig | 25 | ||||
-rw-r--r-- | arch/arm/mach-vexpress/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mach-vexpress/cpuidle-tc2.c | 141 | ||||
-rw-r--r-- | arch/arm/mach-vexpress/hotplug-asm.S | 28 | ||||
-rw-r--r-- | arch/arm/mach-vexpress/tc2-sleep.S | 76 | ||||
-rw-r--r-- | arch/arm/mach-vexpress/tc2_pm.c | 23 |
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, }; |