From: mark hambleton Date: Fri, 18 Jan 2013 14:16:19 +0000 (+0000) Subject: Use dts compatible node to init cpuidle-tc2 X-Git-Tag: firefly_0821_release~3680^2~236^2^2~2^2~6 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=13a7b5d65608f7a8051487eb5cd5a343ed9da896;p=firefly-linux-kernel-4.4.55.git Use dts compatible node to init cpuidle-tc2 Change the init code for cpuidle-tc2 to check for a compatible node in the devicetree of "arm,generic" in preparation for moving it to driver/cpuidle. Rename functions / variable from tc2_ to bl_. Signed-off-by: mark hambleton --- diff --git a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts index d2803be4e1a8..1abef13f9e97 100644 --- a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts +++ b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts @@ -13,7 +13,7 @@ model = "V2P-CA15_CA7"; arm,hbi = <0x249>; arm,vexpress,site = <0xf>; - compatible = "arm,vexpress,v2p-ca15_a7", "arm,vexpress"; + compatible = "arm,vexpress,v2p-ca15_a7", "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 ceb7ef03d0f9..1ce8798e7ee2 100644 --- a/arch/arm/mach-vexpress/Kconfig +++ b/arch/arm/mach-vexpress/Kconfig @@ -65,15 +65,4 @@ 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 caa322455199..fd9bcfd74316 100644 --- a/arch/arm/mach-vexpress/Makefile +++ b/arch/arm/mach-vexpress/Makefile @@ -10,4 +10,3 @@ obj-$(CONFIG_ARCH_VEXPRESS_TC2) += tc2_pm.o tc2_pm_setup.o CFLAGS_REMOVE_tc2_pm.o = -pg 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 1f1efc4936f4..000000000000 --- 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 - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -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 0d8bd55e776f..7d8256a5ea97 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile @@ -4,6 +4,6 @@ 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 obj-$(CONFIG_ARCH_KIRKWOOD) += cpuidle-kirkwood.o diff --git a/drivers/cpuidle/arm_big_little.c b/drivers/cpuidle/arm_big_little.c new file mode 100644 index 000000000000..b97ebe018b6e --- /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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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);