diff options
-rw-r--r-- | arch/arm/mach-mx5/Makefile | 3 | ||||
-rw-r--r-- | arch/arm/mach-mx5/cpuidle.c | 82 | ||||
-rw-r--r-- | arch/arm/mach-mx5/cpuidle.h | 13 | ||||
-rw-r--r-- | arch/arm/mach-mx5/mm.c | 4 | ||||
-rw-r--r-- | arch/arm/plat-mxc/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/plat-mxc/cpuidle.c | 159 | ||||
-rw-r--r-- | arch/arm/plat-mxc/include/mach/cpuidle.h | 56 |
7 files changed, 316 insertions, 2 deletions
diff --git a/arch/arm/mach-mx5/Makefile b/arch/arm/mach-mx5/Makefile index 383e7cd3fbc..4471ce76e39 100644 --- a/arch/arm/mach-mx5/Makefile +++ b/arch/arm/mach-mx5/Makefile @@ -3,9 +3,8 @@ # # Object file lists. -obj-y := cpu.o mm.o clock-mx51-mx53.o devices.o ehci.o system.o +obj-y := cpu.o mm.o clock-mx51-mx53.o devices.o ehci.o system.o cpuidle.o obj-$(CONFIG_SOC_IMX50) += mm-mx50.o - obj-$(CONFIG_PM) += pm-imx5.o obj-$(CONFIG_CPU_FREQ_IMX) += cpu_op-mx51.o obj-$(CONFIG_MACH_MX51_BABBAGE) += board-mx51_babbage.o diff --git a/arch/arm/mach-mx5/cpuidle.c b/arch/arm/mach-mx5/cpuidle.c new file mode 100644 index 00000000000..fa3ea6a66f8 --- /dev/null +++ b/arch/arm/mach-mx5/cpuidle.c @@ -0,0 +1,82 @@ +/* + * Copyright 2011 Freescale Semiconductor, Inc. + * Copyright 2011 Linaro Ltd. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <asm/proc-fns.h> +#include <mach/cpuidle.h> +#include <mach/system.h> + +static int mx5_cpuidle_init(void *init_data); +static int mx5_cpuidle(struct cpuidle_device *dev, struct cpuidle_state *state); + +static struct imx_cpuidle_state_data mx5_cpuidle_state_data[] __initdata = { + { + .name = "powered_noclock", + .desc = "idle cpu powered, unclocked.", + .exit_latency = 12, + .mach_cpu_pwr_state = WAIT_UNCLOCKED, + }, { + .name = "nopower_noclock", + .desc = "idle cpu unpowered, unclocked.", + .exit_latency = 20, + .mach_cpu_pwr_state = WAIT_UNCLOCKED_POWER_OFF, + } +}; + +static struct cpuidle_driver mx5_cpuidle_driver = { + .name = "imx5_cpuidle", + .owner = THIS_MODULE, +}; + +struct imx_cpuidle_data mx5_cpuidle_data __initdata = { + .imx_cpuidle_driver = &mx5_cpuidle_driver, + .state_data = mx5_cpuidle_state_data, + .mach_cpuidle = mx5_cpuidle, + .mach_cpuidle_init = mx5_cpuidle_init, + .num_states = ARRAY_SIZE(mx5_cpuidle_state_data), +}; + +int mx5_cpuidle(struct cpuidle_device *dev, struct cpuidle_state *state) +{ + mx5_cpu_lp_set((enum mxc_cpu_pwr_mode)state->driver_data); + + cpu_do_idle(); + + return 0; +} + +int __init mx5_cpuidle_init(void * init_data) +{ + int ret; + struct clk *gpc_dvfs_clk; + + gpc_dvfs_clk = clk_get(NULL, "gpc_dvfs"); + + if (IS_ERR(gpc_dvfs_clk)) { + pr_err("%s: Failed to get gpc_dvfs clock\n", __func__); + return (int)gpc_dvfs_clk; + } + + ret = clk_enable(gpc_dvfs_clk); + + if (IS_ERR(&ret)) { + pr_err("%s: Failed to enable gpc_dvfs clock\n", __func__); + return ret; + } + + return 0; +} diff --git a/arch/arm/mach-mx5/cpuidle.h b/arch/arm/mach-mx5/cpuidle.h new file mode 100644 index 00000000000..270ffa651d6 --- /dev/null +++ b/arch/arm/mach-mx5/cpuidle.h @@ -0,0 +1,13 @@ +/* + * Copyright 2011 Freescale Semiconductor, Inc. + * Copyright 2011 Linaro Ltd. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +extern struct imx_cpuidle_data mx5_cpuidle_data; diff --git a/arch/arm/mach-mx5/mm.c b/arch/arm/mach-mx5/mm.c index baea6e5cddd..29bb785a398 100644 --- a/arch/arm/mach-mx5/mm.c +++ b/arch/arm/mach-mx5/mm.c @@ -20,6 +20,8 @@ #include <mach/common.h> #include <mach/devices-common.h> #include <mach/iomux-v3.h> +#include <mach/cpuidle.h> +#include "cpuidle.h" /* * Define the MX51 memory map. @@ -148,6 +150,8 @@ void __init imx51_soc_init(void) /* i.mx51 has the i.mx35 type sdma */ imx_add_imx_sdma("imx35-sdma", MX51_SDMA_BASE_ADDR, MX51_INT_SDMA, &imx51_sdma_pdata); + + imx_cpuidle_init(&mx5_cpuidle_data); } void __init imx53_soc_init(void) diff --git a/arch/arm/plat-mxc/Makefile b/arch/arm/plat-mxc/Makefile index d53c35fe2ea..8939f79e670 100644 --- a/arch/arm/plat-mxc/Makefile +++ b/arch/arm/plat-mxc/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_ARCH_MXC_AUDMUX_V1) += audmux-v1.o obj-$(CONFIG_ARCH_MXC_AUDMUX_V2) += audmux-v2.o obj-$(CONFIG_MXC_DEBUG_BOARD) += 3ds_debugboard.o obj-$(CONFIG_CPU_FREQ_IMX) += cpufreq.o +obj-$(CONFIG_CPU_IDLE) += cpuidle.o ifdef CONFIG_SND_IMX_SOC obj-y += ssi-fiq.o obj-y += ssi-fiq-ksym.o diff --git a/arch/arm/plat-mxc/cpuidle.c b/arch/arm/plat-mxc/cpuidle.c new file mode 100644 index 00000000000..40c192420f4 --- /dev/null +++ b/arch/arm/plat-mxc/cpuidle.c @@ -0,0 +1,159 @@ +/* + * Copyright 2011 Freescale Semiconductor, Inc. + * Copyright 2011 Linaro Ltd. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/cpuidle.h> +#include <linux/err.h> +#include <asm/proc-fns.h> +#include <mach/cpuidle.h> + +static int (*mach_cpuidle)(struct cpuidle_device *dev, + struct cpuidle_state *state); +static struct cpuidle_driver *imx_cpuidle_driver; +static struct cpuidle_device *device; + +static int imx_enter_idle(struct cpuidle_device *dev, + struct cpuidle_state *state) +{ + struct timeval before, after; + int idle_time; + + local_irq_disable(); + local_fiq_disable(); + + do_gettimeofday(&before); + + mach_cpuidle(dev, state); + + do_gettimeofday(&after); + + local_fiq_enable(); + local_irq_enable(); + + idle_time = (after.tv_sec - before.tv_sec) * USEC_PER_SEC + + (after.tv_usec - before.tv_usec); + + return idle_time; +} + +static DEFINE_PER_CPU(struct cpuidle_device, imx_cpuidle_device); + +int __init imx_cpuidle_init(struct imx_cpuidle_data *cpuidle_data) +{ + int i, cpu_id; + + if (cpuidle_data == NULL) { + pr_err("%s: cpuidle_data pointer NULL\n", __func__); + return -EINVAL; + } + + if (cpuidle_data->mach_cpuidle == NULL) { + pr_err("%s: idle callback function NULL\n", __func__); + return -EINVAL; + } + + imx_cpuidle_driver = cpuidle_data->imx_cpuidle_driver; + + mach_cpuidle = cpuidle_data->mach_cpuidle; + + /* register imx_cpuidle driver */ + if (cpuidle_register_driver(imx_cpuidle_driver)) { + pr_err("%s: Failed to register cpuidle driver\n", __func__); + return -ENODEV; + } + + /* if provided, initialize the mach level cpuidle functionality */ + if (cpuidle_data->mach_cpuidle_init) { + if (cpuidle_data->mach_cpuidle_init(cpuidle_data)) { + pr_err("%s: Failed to register cpuidle driver\n", + __func__); + cpuidle_unregister_driver(imx_cpuidle_driver); + return -ENODEV; + } + } + + /* initialize state data for each cpuidle_device(one per present cpu)*/ + for_each_cpu(cpu_id, cpu_present_mask) { + + device = &per_cpu(imx_cpuidle_device, cpu_id); + device->cpu = cpu_id; + + device->state_count = min((unsigned int) CPUIDLE_STATE_MAX, + cpuidle_data->num_states); + + device->prepare = cpuidle_data->prepare; + + for (i = 0; i < device->state_count; i++) { + strlcpy(device->states[i].name, + cpuidle_data->state_data[i].name, + CPUIDLE_NAME_LEN); + + strlcpy(device->states[i].desc, + cpuidle_data->state_data[i].desc, + CPUIDLE_DESC_LEN); + + device->states[i].driver_data = + (void *)cpuidle_data-> + state_data[i].mach_cpu_pwr_state; + + /* + * Because the imx_enter_idle function measures + * and returns a valid time for all imx SoCs, + * we always set this flag. + */ + device->states[i].flags = CPUIDLE_FLAG_TIME_VALID; + + device->states[i].exit_latency = + cpuidle_data->state_data[i].exit_latency; + + device->states[i].power_usage = + cpuidle_data->state_data[i].power_usage; + + device->states[i].target_residency = + cpuidle_data->state_data[i].target_residency; + + device->states[i].enter = imx_enter_idle; + } + } + + return 0; +} + +int __init imx_cpuidle_dev_init(void) +{ + int cpu_id; + + /* + * Register online cpus. If maxcpus is specified as a boot + * argument and secondary cpus are brought online after boot, + * this function can be called again to register a cpuidle + * device for those secondary cpus. + */ + for_each_cpu(cpu_id, cpu_online_mask) { + device = &per_cpu(imx_cpuidle_device, cpu_id); + if (device == NULL) { + pr_err("%s: Failed to register (No device)\n", + __func__); + return -ENODEV; + } + + if (!device->registered) + if (cpuidle_register_device(device)) { + pr_err("%s: Failed to register\n", __func__); + return -ENODEV; + } + } + + return 0; +} +late_initcall(imx_cpuidle_dev_init); diff --git a/arch/arm/plat-mxc/include/mach/cpuidle.h b/arch/arm/plat-mxc/include/mach/cpuidle.h new file mode 100644 index 00000000000..2bb109e243b --- /dev/null +++ b/arch/arm/plat-mxc/include/mach/cpuidle.h @@ -0,0 +1,56 @@ +/* + * Copyright 2011 Freescale Semiconductor, Inc. + * Copyright 2011 Linaro Ltd. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef __ARCH_ARM_PLAT_MXC_CPUIDLE_H__ +#define __ARCH_ARM_PLAT_MXC_CPUIDLE_H__ + +#include <linux/cpuidle.h> +#include <mach/hardware.h> + +/* for passing cpuidle state info to the cpuidle driver. */ +struct imx_cpuidle_state_data { + enum mxc_cpu_pwr_mode mach_cpu_pwr_state; + char *name; + char *desc; + /* time in uS to exit this idle state */ + unsigned int exit_latency; + /* OPTIONAL - power usage of this idle state in mW */ + unsigned int power_usage; + /* OPTIONAL - in uS. See drivers/cpuidle/governors/menu.c for usage */ + unsigned int target_residency; +}; + +struct imx_cpuidle_data { + unsigned int num_states; + struct cpuidle_driver *imx_cpuidle_driver; + struct imx_cpuidle_state_data *state_data; + int (*mach_cpuidle)(struct cpuidle_device *dev, + struct cpuidle_state *state); + + /* OPTIONAL - parameter of mach_cpuidle_init func below */ + void *mach_init_data; + /* OPTIONAL - callback for mach level cpuidle initialization */ + int (*mach_cpuidle_init)(void *mach_init_data); + /* OPTIONAL - Search drivers/cpuidle/cpuidle.c for usage */ + int (*prepare)(struct cpuidle_device *dev); +}; + +#ifdef CONFIG_CPU_IDLE +int imx_cpuidle_init(struct imx_cpuidle_data *cpuidle_data); +#else +static inline int imx_cpuidle_init(struct imx_cpuidle_data *cpuidle_data) +{ + return -EINVAL; +} +#endif /* CONFIG_CPU_IDLE */ + +#endif /* __ARCH_ARM_PLAT_MXC_CPUIDLE_H__ */ |