Thanks for this Mark,
CCing Lorenzo as this doesn't seem to have come through on the linaro-devs list.
Cheers,
Steve
On 15 Jan 2013, at 13:35, Mark Hambleton mark.hambleton@broadcom.com wrote:
Take the TC2 version of CPUIdle and remove TC2 specific line. Move the file to drivers/cpuidle. Introduce the concept of "arm,generic" compatible drivers to avoid the need for a dummy dts node.
Build the new CPUIdle based on CONFIG_BIG_LITTLE.
Signed-off-by: mark mahamble@broadcom.com
Hi Nicolas / Dave,
After plugging in our ASIC support to the new big.LITTLE frameworks I realised that the TC2 CPUIdle driver was fundamentally generic, this patch moves it from mach-vexpress into drivers/cpuidle and then checks for the presence of a compatible node in the devicetree instead of checking for an SPC.
I haven't added a new config define as I figured it was specific to the big.LITTLE framework, so just used that instead.
I have tested the change on TC2 and am looking at extending the DCSCB code to make it work on fastmodels.
Any comments?
Regards,
Mark
arch/arm/boot/dts/vexpress-v2p-ca15-tc2.dts | 2 +- arch/arm/mach-vexpress/Kconfig | 12 -- arch/arm/mach-vexpress/Makefile | 1 - arch/arm/mach-vexpress/cpuidle-tc2.c | 184 --------------------------- drivers/cpuidle/Makefile | 2 +- drivers/cpuidle/arm_big_little.c | 183 ++++++++++++++++++++++++++ 6 files changed, 185 insertions(+), 199 deletions(-) delete mode 100644 arch/arm/mach-vexpress/cpuidle-tc2.c create mode 100755 drivers/cpuidle/arm_big_little.c
diff --git a/arch/arm/boot/dts/vexpress-v2p-ca15-tc2.dts b/arch/arm/boot/dts/vexpress-v2p-ca15-tc2.dts index 3b7cd86..b261bde 100644 --- a/arch/arm/boot/dts/vexpress-v2p-ca15-tc2.dts +++ b/arch/arm/boot/dts/vexpress-v2p-ca15-tc2.dts @@ -15,7 +15,7 @@ model = "V2P-CA15_CA7"; arm,hbi = <0x249>; arm,vexpress,site = <0xf>;
compatible = "arm,vexpress,v2p-ca15,tc2", "arm,vexpress,v2p-ca15", "arm,vexpress";
compatible = "arm,vexpress,v2p-ca15,tc2", "arm,vexpress,v2p-ca15", "arm,vexpress", "arm,generic"; interrupt-parent = <&gic>; #address-cells = <2>; #size-cells = <2>;
diff --git a/arch/arm/mach-vexpress/Kconfig b/arch/arm/mach-vexpress/Kconfig index f302fa9..3fd44c4 100644 --- a/arch/arm/mach-vexpress/Kconfig +++ b/arch/arm/mach-vexpress/Kconfig @@ -61,18 +61,6 @@ 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 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.
config ARCH_VEXPRESS_DCSCB bool "Dual Cluster System Control Block (DCSCB) support" depends on BIG_LITTLE diff --git a/arch/arm/mach-vexpress/Makefile b/arch/arm/mach-vexpress/Makefile index 05c6fed..b10390e 100644 --- a/arch/arm/mach-vexpress/Makefile +++ b/arch/arm/mach-vexpress/Makefile @@ -10,4 +10,3 @@ obj-$(CONFIG_ARCH_VEXPRESS_DCSCB) += dcscb.o dcscb_setup.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_VEXPRESS_TC2_CPUIDLE) += cpuidle-tc2.o diff --git a/arch/arm/mach-vexpress/cpuidle-tc2.c b/arch/arm/mach-vexpress/cpuidle-tc2.c deleted file mode 100644 index 1f1efc4..0000000 --- a/arch/arm/mach-vexpress/cpuidle-tc2.c +++ /dev/null @@ -1,184 +0,0 @@ -/*
- TC2 CPU idle driver.
- Copyright (C) 2012 ARM Ltd.
- Author: Lorenzo Pieralisi lorenzo.pieralisi@arm.com
- 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/arm-cci.h> -#include <linux/bitmap.h> -#include <linux/cpuidle.h> -#include <linux/cpu_pm.h> -#include <linux/clockchips.h> -#include <linux/debugfs.h> -#include <linux/hrtimer.h> -#include <linux/kernel.h> -#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> -#include <asm/proc-fns.h> -#include <asm/suspend.h>
-#include <mach/motherboard.h>
-static int tc2_cpuidle_simple_enter(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int index)
-{
ktime_t time_start, time_end;
s64 diff;
time_start = ktime_get();
cpu_do_idle();
time_end = ktime_get();
local_irq_enable();
diff = ktime_to_us(ktime_sub(time_end, time_start));
if (diff > INT_MAX)
diff = INT_MAX;
dev->last_residency = (int) diff;
return index;
-}
-static int tc2_enter_powerdown(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int idx);
-static struct cpuidle_state tc2_cpuidle_set[] __initdata = {
[0] = {
.enter = tc2_cpuidle_simple_enter,
.exit_latency = 1,
.target_residency = 1,
.power_usage = UINT_MAX,
.flags = CPUIDLE_FLAG_TIME_VALID,
.name = "WFI",
.desc = "ARM WFI",
},
[1] = {
.enter = tc2_enter_powerdown,
.exit_latency = 300,
.target_residency = 1000,
.flags = CPUIDLE_FLAG_TIME_VALID,
.name = "C1",
.desc = "ARM power down",
},
-};
-struct cpuidle_driver tc2_idle_driver = {
.name = "tc2_idle",
.owner = THIS_MODULE,
.safe_state_index = 0
-};
-static DEFINE_PER_CPU(struct cpuidle_device, tc2_idle_dev);
-static int notrace tc2_powerdown_finisher(unsigned long arg) -{
unsigned int mpidr = read_cpuid_mpidr();
unsigned int cluster = (mpidr >> 8) & 0xf;
unsigned int cpu = mpidr & 0xf;
bL_set_entry_vector(cpu, cluster, cpu_resume);
bL_cpu_suspend(0); /* 0 should be replaced with better value here */
return 1;
-}
-/*
- tc2_enter_powerdown - Programs CPU to enter the specified state
- @dev: cpuidle device
- @drv: The target state to be programmed
- @idx: state index
- Called from the CPUidle framework to program the device to the
- specified target state selected by the governor.
- */
-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;
/* Used to keep track of the total time in idle */
getnstimeofday(&ts_preidle);
BUG_ON(!irqs_disabled());
cpu_pm_enter();
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
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();
getnstimeofday(&ts_postidle);
local_irq_enable();
ts_idle = timespec_sub(ts_postidle, ts_preidle);
dev->last_residency = ts_idle.tv_nsec / NSEC_PER_USEC +
ts_idle.tv_sec * USEC_PER_SEC;
return idx;
-}
-/*
- tc2_idle_init
- Registers the TC2 specific cpuidle driver with the cpuidle
- framework with the valid set of states.
- */
-int __init tc2_idle_init(void) -{
struct cpuidle_device *dev;
int i, cpu_id;
struct cpuidle_driver *drv = &tc2_idle_driver;
if (!vexpress_spc_check_loaded()) {
pr_info("TC2 CPUidle not registered because no SPC found\n");
return -ENODEV;
}
drv->state_count = (sizeof(tc2_cpuidle_set) /
sizeof(struct cpuidle_state));
for (i = 0; i < drv->state_count; i++) {
memcpy(&drv->states[i], &tc2_cpuidle_set[i],
sizeof(struct cpuidle_state));
}
cpuidle_register_driver(drv);
for_each_cpu(cpu_id, cpu_online_mask) {
pr_err("CPUidle for CPU%d registered\n", cpu_id);
dev = &per_cpu(tc2_idle_dev, cpu_id);
dev->cpu = cpu_id;
dev->state_count = drv->state_count;
if (cpuidle_register_device(dev)) {
printk(KERN_ERR "%s: Cpuidle register device failed\n",
__func__);
return -EIO;
}
}
return 0;
-}
-late_initcall(tc2_idle_init); diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index 03ee874..398ece7 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile @@ -4,5 +4,5 @@
obj-y += cpuidle.o driver.o governor.o sysfs.o governors/ obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o
+obj-$(CONFIG_BIG_LITTLE) += arm_big_little.o obj-$(CONFIG_CPU_IDLE_CALXEDA) += cpuidle-calxeda.o diff --git a/drivers/cpuidle/arm_big_little.c b/drivers/cpuidle/arm_big_little.c new file mode 100755 index 0000000..b97ebe0 --- /dev/null +++ b/drivers/cpuidle/arm_big_little.c @@ -0,0 +1,183 @@ +/*
- big.LITTLE CPU idle driver.
- Copyright (C) 2012 ARM Ltd.
- Author: Lorenzo Pieralisi lorenzo.pieralisi@arm.com
- 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/arm-cci.h> +#include <linux/bitmap.h> +#include <linux/cpuidle.h> +#include <linux/cpu_pm.h> +#include <linux/clockchips.h> +#include <linux/debugfs.h> +#include <linux/hrtimer.h> +#include <linux/kernel.h> +#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> +#include <asm/proc-fns.h> +#include <asm/suspend.h> +#include <linux/of.h>
+static int bl_cpuidle_simple_enter(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int index)
+{
ktime_t time_start, time_end;
s64 diff;
time_start = ktime_get();
cpu_do_idle();
time_end = ktime_get();
local_irq_enable();
diff = ktime_to_us(ktime_sub(time_end, time_start));
if (diff > INT_MAX)
diff = INT_MAX;
dev->last_residency = (int) diff;
return index;
+}
+static int bl_enter_powerdown(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int idx);
+static struct cpuidle_state bl_cpuidle_set[] __initdata = {
[0] = {
.enter = bl_cpuidle_simple_enter,
.exit_latency = 1,
.target_residency = 1,
.power_usage = UINT_MAX,
.flags = CPUIDLE_FLAG_TIME_VALID,
.name = "WFI",
.desc = "ARM WFI",
},
[1] = {
.enter = bl_enter_powerdown,
.exit_latency = 300,
.target_residency = 1000,
.flags = CPUIDLE_FLAG_TIME_VALID,
.name = "C1",
.desc = "ARM power down",
},
+};
+struct cpuidle_driver bl_idle_driver = {
.name = "bl_idle",
.owner = THIS_MODULE,
.safe_state_index = 0
+};
+static DEFINE_PER_CPU(struct cpuidle_device, bl_idle_dev);
+static int notrace bl_powerdown_finisher(unsigned long arg) +{
unsigned int mpidr = read_cpuid_mpidr();
unsigned int cluster = (mpidr >> 8) & 0xf;
unsigned int cpu = mpidr & 0xf;
bL_set_entry_vector(cpu, cluster, cpu_resume);
bL_cpu_suspend(0); /* 0 should be replaced with better value here */
return 1;
+}
+/*
- bl_enter_powerdown - Programs CPU to enter the specified state
- @dev: cpuidle device
- @drv: The target state to be programmed
- @idx: state index
- Called from the CPUidle framework to program the device to the
- specified target state selected by the governor.
- */
+static int bl_enter_powerdown(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int idx)
+{
struct timespec ts_preidle, ts_postidle, ts_idle;
int ret;
/* Used to keep track of the total time in idle */
getnstimeofday(&ts_preidle);
BUG_ON(!irqs_disabled());
cpu_pm_enter();
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
ret = cpu_suspend((unsigned long) dev, bl_powerdown_finisher);
if (ret)
BUG();
bL_cpu_powered_up();
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
cpu_pm_exit();
getnstimeofday(&ts_postidle);
local_irq_enable();
ts_idle = timespec_sub(ts_postidle, ts_preidle);
dev->last_residency = ts_idle.tv_nsec / NSEC_PER_USEC +
ts_idle.tv_sec * USEC_PER_SEC;
return idx;
+}
+/*
- bl_idle_init
- Registers the bl specific cpuidle driver with the cpuidle
- framework with the valid set of states.
- */
+int __init bl_idle_init(void) +{
struct cpuidle_device *dev;
int i, cpu_id;
struct cpuidle_driver *drv = &bl_idle_driver;
if (!of_find_compatible_node(NULL, NULL, "arm,generic")) {
pr_info("%s: No compatible node found\n", __func__);
return -ENODEV;
}
drv->state_count = (sizeof(bl_cpuidle_set) /
sizeof(struct cpuidle_state));
for (i = 0; i < drv->state_count; i++) {
memcpy(&drv->states[i], &bl_cpuidle_set[i],
sizeof(struct cpuidle_state));
}
cpuidle_register_driver(drv);
for_each_cpu(cpu_id, cpu_online_mask) {
pr_err("CPUidle for CPU%d registered\n", cpu_id);
dev = &per_cpu(bl_idle_dev, cpu_id);
dev->cpu = cpu_id;
dev->state_count = drv->state_count;
if (cpuidle_register_device(dev)) {
printk(KERN_ERR "%s: Cpuidle register device failed\n",
__func__);
return -EIO;
}
}
return 0;
+}
+late_initcall(bl_idle_init);
1.7.9.5
-- IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.