Merge branch 'pm-cpuidle'
* pm-cpuidle: (51 commits) cpuidle: add maintainer entry ARM: s3c64xx: cpuidle: use init/exit common routine SH: cpuidle: use init/exit common routine cpuidle: fix comment format ARM: imx: cpuidle: use init/exit common routine ARM: davinci: cpuidle: use init/exit common routine ARM: kirkwood: cpuidle: use init/exit common routine ARM: calxeda: cpuidle: use init/exit common routine ARM: tegra: cpuidle: use init/exit common routine for tegra3 ARM: tegra: cpuidle: use init/exit common routine for tegra2 ARM: OMAP4: cpuidle: use init/exit common routine ARM: shmobile: cpuidle: use init/exit common routine ARM: tegra: cpuidle: use init/exit common routine ARM: OMAP3: cpuidle: use init/exit common routine ARM: at91: cpuidle: use init/exit common routine ARM: ux500: cpuidle: use init/exit common routine cpuidle: make a single register function for all ARM: ux500: cpuidle: replace for_each_online_cpu by for_each_possible_cpu cpuidle: remove en_core_tk_irqen flag ARM: OMAP3: remove cpuidle_wrap_enter ...
This commit is contained in:
commit
e4f5a3adc4
39 changed files with 362 additions and 757 deletions
|
@ -15,11 +15,17 @@ has mechanisms in place to support actual entry-exit into CPU idle states.
|
|||
cpuidle driver initializes the cpuidle_device structure for each CPU device
|
||||
and registers with cpuidle using cpuidle_register_device.
|
||||
|
||||
If all the idle states are the same, the wrapper function cpuidle_register
|
||||
could be used instead.
|
||||
|
||||
It can also support the dynamic changes (like battery <-> AC), by using
|
||||
cpuidle_pause_and_lock, cpuidle_disable_device and cpuidle_enable_device,
|
||||
cpuidle_resume_and_unlock.
|
||||
|
||||
Interfaces:
|
||||
extern int cpuidle_register(struct cpuidle_driver *drv,
|
||||
const struct cpumask *const coupled_cpus);
|
||||
extern int cpuidle_unregister(struct cpuidle_driver *drv);
|
||||
extern int cpuidle_register_driver(struct cpuidle_driver *drv);
|
||||
extern void cpuidle_unregister_driver(struct cpuidle_driver *drv);
|
||||
extern int cpuidle_register_device(struct cpuidle_device *dev);
|
||||
|
|
|
@ -2206,6 +2206,15 @@ S: Maintained
|
|||
F: drivers/cpufreq/
|
||||
F: include/linux/cpufreq.h
|
||||
|
||||
CPUIDLE DRIVERS
|
||||
M: Rafael J. Wysocki <rjw@sisk.pl>
|
||||
M: Daniel Lezcano <daniel.lezcano@linaro.org>
|
||||
L: linux-pm@vger.kernel.org
|
||||
S: Maintained
|
||||
T: git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git
|
||||
F: drivers/cpuidle/*
|
||||
F: include/linux/cpuidle.h
|
||||
|
||||
CPUID/MSR DRIVER
|
||||
M: "H. Peter Anvin" <hpa@zytor.com>
|
||||
S: Maintained
|
||||
|
|
|
@ -56,7 +56,6 @@ CONFIG_AEABI=y
|
|||
CONFIG_ZBOOT_ROM_TEXT=0x0
|
||||
CONFIG_ZBOOT_ROM_BSS=0x0
|
||||
CONFIG_CPU_IDLE=y
|
||||
CONFIG_CPU_IDLE_KIRKWOOD=y
|
||||
CONFIG_NET=y
|
||||
CONFIG_PACKET=y
|
||||
CONFIG_UNIX=y
|
||||
|
|
|
@ -27,8 +27,6 @@
|
|||
|
||||
#define AT91_MAX_STATES 2
|
||||
|
||||
static DEFINE_PER_CPU(struct cpuidle_device, at91_cpuidle_device);
|
||||
|
||||
/* Actual code that puts the SoC in different idle states */
|
||||
static int at91_enter_idle(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv,
|
||||
|
@ -47,7 +45,6 @@ static int at91_enter_idle(struct cpuidle_device *dev,
|
|||
static struct cpuidle_driver at91_idle_driver = {
|
||||
.name = "at91_idle",
|
||||
.owner = THIS_MODULE,
|
||||
.en_core_tk_irqen = 1,
|
||||
.states[0] = ARM_CPUIDLE_WFI_STATE,
|
||||
.states[1] = {
|
||||
.enter = at91_enter_idle,
|
||||
|
@ -61,20 +58,9 @@ static struct cpuidle_driver at91_idle_driver = {
|
|||
};
|
||||
|
||||
/* Initialize CPU idle by registering the idle states */
|
||||
static int at91_init_cpuidle(void)
|
||||
static int __init at91_init_cpuidle(void)
|
||||
{
|
||||
struct cpuidle_device *device;
|
||||
|
||||
device = &per_cpu(at91_cpuidle_device, smp_processor_id());
|
||||
device->state_count = AT91_MAX_STATES;
|
||||
|
||||
cpuidle_register_driver(&at91_idle_driver);
|
||||
|
||||
if (cpuidle_register_device(device)) {
|
||||
printk(KERN_ERR "at91_init_cpuidle: Failed registering\n");
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
return cpuidle_register(&at91_idle_driver, NULL);
|
||||
}
|
||||
|
||||
device_initcall(at91_init_cpuidle);
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
|
||||
#define DAVINCI_CPUIDLE_MAX_STATES 2
|
||||
|
||||
static DEFINE_PER_CPU(struct cpuidle_device, davinci_cpuidle_device);
|
||||
static void __iomem *ddr2_reg_base;
|
||||
static bool ddr2_pdown;
|
||||
|
||||
|
@ -50,14 +49,10 @@ static void davinci_save_ddr_power(int enter, bool pdown)
|
|||
|
||||
/* Actual code that puts the SoC in different idle states */
|
||||
static int davinci_enter_idle(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv,
|
||||
int index)
|
||||
struct cpuidle_driver *drv, int index)
|
||||
{
|
||||
davinci_save_ddr_power(1, ddr2_pdown);
|
||||
|
||||
index = cpuidle_wrap_enter(dev, drv, index,
|
||||
arm_cpuidle_simple_enter);
|
||||
|
||||
cpu_do_idle();
|
||||
davinci_save_ddr_power(0, ddr2_pdown);
|
||||
|
||||
return index;
|
||||
|
@ -66,7 +61,6 @@ static int davinci_enter_idle(struct cpuidle_device *dev,
|
|||
static struct cpuidle_driver davinci_idle_driver = {
|
||||
.name = "cpuidle-davinci",
|
||||
.owner = THIS_MODULE,
|
||||
.en_core_tk_irqen = 1,
|
||||
.states[0] = ARM_CPUIDLE_WFI_STATE,
|
||||
.states[1] = {
|
||||
.enter = davinci_enter_idle,
|
||||
|
@ -81,12 +75,8 @@ static struct cpuidle_driver davinci_idle_driver = {
|
|||
|
||||
static int __init davinci_cpuidle_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct cpuidle_device *device;
|
||||
struct davinci_cpuidle_config *pdata = pdev->dev.platform_data;
|
||||
|
||||
device = &per_cpu(davinci_cpuidle_device, smp_processor_id());
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(&pdev->dev, "cannot get platform data\n");
|
||||
return -ENOENT;
|
||||
|
@ -96,20 +86,7 @@ static int __init davinci_cpuidle_probe(struct platform_device *pdev)
|
|||
|
||||
ddr2_pdown = pdata->ddr2_pdown;
|
||||
|
||||
ret = cpuidle_register_driver(&davinci_idle_driver);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register driver\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = cpuidle_register_device(device);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register device\n");
|
||||
cpuidle_unregister_driver(&davinci_idle_driver);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return cpuidle_register(&davinci_idle_driver, NULL);
|
||||
}
|
||||
|
||||
static struct platform_driver davinci_cpuidle_driver = {
|
||||
|
|
|
@ -58,7 +58,6 @@ static DEFINE_PER_CPU(struct cpuidle_device, exynos4_cpuidle_device);
|
|||
static struct cpuidle_driver exynos4_idle_driver = {
|
||||
.name = "exynos4_idle",
|
||||
.owner = THIS_MODULE,
|
||||
.en_core_tk_irqen = 1,
|
||||
};
|
||||
|
||||
/* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */
|
||||
|
|
|
@ -30,7 +30,7 @@ obj-$(CONFIG_MXC_DEBUG_BOARD) += 3ds_debugboard.o
|
|||
obj-$(CONFIG_CPU_FREQ_IMX) += cpufreq.o
|
||||
|
||||
ifeq ($(CONFIG_CPU_IDLE),y)
|
||||
obj-y += cpuidle.o
|
||||
obj-$(CONFIG_SOC_IMX5) += cpuidle-imx5.o
|
||||
obj-$(CONFIG_SOC_IMX6Q) += cpuidle-imx6q.o
|
||||
endif
|
||||
|
||||
|
|
37
arch/arm/mach-imx/cpuidle-imx5.c
Normal file
37
arch/arm/mach-imx/cpuidle-imx5.c
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* 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/cpuidle.h>
|
||||
#include <linux/module.h>
|
||||
#include <asm/system_misc.h>
|
||||
|
||||
static int imx5_cpuidle_enter(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int index)
|
||||
{
|
||||
arm_pm_idle();
|
||||
return index;
|
||||
}
|
||||
|
||||
static struct cpuidle_driver imx5_cpuidle_driver = {
|
||||
.name = "imx5_cpuidle",
|
||||
.owner = THIS_MODULE,
|
||||
.states[0] = {
|
||||
.enter = imx5_cpuidle_enter,
|
||||
.exit_latency = 2,
|
||||
.target_residency = 1,
|
||||
.flags = CPUIDLE_FLAG_TIME_VALID,
|
||||
.name = "IMX5 SRPG",
|
||||
.desc = "CPU state retained,powered off",
|
||||
},
|
||||
.state_count = 1,
|
||||
};
|
||||
|
||||
int __init imx5_cpuidle_init(void)
|
||||
{
|
||||
return cpuidle_register(&imx5_cpuidle_driver, NULL);
|
||||
}
|
|
@ -6,7 +6,6 @@
|
|||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/cpuidle.h>
|
||||
#include <linux/module.h>
|
||||
#include <asm/cpuidle.h>
|
||||
|
@ -21,10 +20,6 @@ static DEFINE_SPINLOCK(master_lock);
|
|||
static int imx6q_enter_wait(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int index)
|
||||
{
|
||||
int cpu = dev->cpu;
|
||||
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu);
|
||||
|
||||
if (atomic_inc_return(&master) == num_online_cpus()) {
|
||||
/*
|
||||
* With this lock, we prevent other cpu to exit and enter
|
||||
|
@ -43,26 +38,13 @@ static int imx6q_enter_wait(struct cpuidle_device *dev,
|
|||
cpu_do_idle();
|
||||
done:
|
||||
atomic_dec(&master);
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/*
|
||||
* For each cpu, setup the broadcast timer because local timer
|
||||
* stops for the states other than WFI.
|
||||
*/
|
||||
static void imx6q_setup_broadcast_timer(void *arg)
|
||||
{
|
||||
int cpu = smp_processor_id();
|
||||
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ON, &cpu);
|
||||
}
|
||||
|
||||
static struct cpuidle_driver imx6q_cpuidle_driver = {
|
||||
.name = "imx6q_cpuidle",
|
||||
.owner = THIS_MODULE,
|
||||
.en_core_tk_irqen = 1,
|
||||
.states = {
|
||||
/* WFI */
|
||||
ARM_CPUIDLE_WFI_STATE,
|
||||
|
@ -70,7 +52,8 @@ static struct cpuidle_driver imx6q_cpuidle_driver = {
|
|||
{
|
||||
.exit_latency = 50,
|
||||
.target_residency = 75,
|
||||
.flags = CPUIDLE_FLAG_TIME_VALID,
|
||||
.flags = CPUIDLE_FLAG_TIME_VALID |
|
||||
CPUIDLE_FLAG_TIMER_STOP,
|
||||
.enter = imx6q_enter_wait,
|
||||
.name = "WAIT",
|
||||
.desc = "Clock off",
|
||||
|
@ -88,8 +71,5 @@ int __init imx6q_cpuidle_init(void)
|
|||
/* Set chicken bit to get a reliable WAIT mode support */
|
||||
imx6q_set_chicken_bit();
|
||||
|
||||
/* Configure the broadcast timer on each cpu */
|
||||
on_each_cpu(imx6q_setup_broadcast_timer, NULL, 1);
|
||||
|
||||
return imx_cpuidle_init(&imx6q_cpuidle_driver);
|
||||
return cpuidle_register(&imx6q_cpuidle_driver, NULL);
|
||||
}
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012 Freescale Semiconductor, Inc.
|
||||
* Copyright 2012 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/cpuidle.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
static struct cpuidle_device __percpu * imx_cpuidle_devices;
|
||||
|
||||
static void __init imx_cpuidle_devices_uninit(void)
|
||||
{
|
||||
int cpu_id;
|
||||
struct cpuidle_device *dev;
|
||||
|
||||
for_each_possible_cpu(cpu_id) {
|
||||
dev = per_cpu_ptr(imx_cpuidle_devices, cpu_id);
|
||||
cpuidle_unregister_device(dev);
|
||||
}
|
||||
|
||||
free_percpu(imx_cpuidle_devices);
|
||||
}
|
||||
|
||||
int __init imx_cpuidle_init(struct cpuidle_driver *drv)
|
||||
{
|
||||
struct cpuidle_device *dev;
|
||||
int cpu_id, ret;
|
||||
|
||||
if (drv->state_count > CPUIDLE_STATE_MAX) {
|
||||
pr_err("%s: state_count exceeds maximum\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = cpuidle_register_driver(drv);
|
||||
if (ret) {
|
||||
pr_err("%s: Failed to register cpuidle driver with error: %d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
imx_cpuidle_devices = alloc_percpu(struct cpuidle_device);
|
||||
if (imx_cpuidle_devices == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto unregister_drv;
|
||||
}
|
||||
|
||||
/* initialize state data for each cpuidle_device */
|
||||
for_each_possible_cpu(cpu_id) {
|
||||
dev = per_cpu_ptr(imx_cpuidle_devices, cpu_id);
|
||||
dev->cpu = cpu_id;
|
||||
dev->state_count = drv->state_count;
|
||||
|
||||
ret = cpuidle_register_device(dev);
|
||||
if (ret) {
|
||||
pr_err("%s: Failed to register cpu %u, error: %d\n",
|
||||
__func__, cpu_id, ret);
|
||||
goto uninit;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
uninit:
|
||||
imx_cpuidle_devices_uninit();
|
||||
|
||||
unregister_drv:
|
||||
cpuidle_unregister_driver(drv);
|
||||
return ret;
|
||||
}
|
|
@ -10,18 +10,16 @@
|
|||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
|
||||
#include <linux/cpuidle.h>
|
||||
|
||||
#ifdef CONFIG_CPU_IDLE
|
||||
extern int imx_cpuidle_init(struct cpuidle_driver *drv);
|
||||
extern int imx5_cpuidle_init(void);
|
||||
extern int imx6q_cpuidle_init(void);
|
||||
#else
|
||||
static inline int imx_cpuidle_init(struct cpuidle_driver *drv)
|
||||
static inline int imx5_cpuidle_init(void)
|
||||
{
|
||||
return -ENODEV;
|
||||
return 0;
|
||||
}
|
||||
static inline int imx6q_cpuidle_init(void)
|
||||
{
|
||||
return -ENODEV;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -149,33 +149,6 @@ static void imx5_pm_idle(void)
|
|||
imx5_cpu_do_idle();
|
||||
}
|
||||
|
||||
static int imx5_cpuidle_enter(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int idx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = imx5_cpu_do_idle();
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
static struct cpuidle_driver imx5_cpuidle_driver = {
|
||||
.name = "imx5_cpuidle",
|
||||
.owner = THIS_MODULE,
|
||||
.en_core_tk_irqen = 1,
|
||||
.states[0] = {
|
||||
.enter = imx5_cpuidle_enter,
|
||||
.exit_latency = 2,
|
||||
.target_residency = 1,
|
||||
.flags = CPUIDLE_FLAG_TIME_VALID,
|
||||
.name = "IMX5 SRPG",
|
||||
.desc = "CPU state retained,powered off",
|
||||
},
|
||||
.state_count = 1,
|
||||
};
|
||||
|
||||
static int __init imx5_pm_common_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
@ -193,8 +166,7 @@ static int __init imx5_pm_common_init(void)
|
|||
/* Set the registers to the default cpu idle state. */
|
||||
mx5_cpu_lp_set(IMX5_DEFAULT_CPU_IDLE_STATE);
|
||||
|
||||
imx_cpuidle_init(&imx5_cpuidle_driver);
|
||||
return 0;
|
||||
return imx5_cpuidle_init();
|
||||
}
|
||||
|
||||
void __init imx51_pm_init(void)
|
||||
|
|
|
@ -249,7 +249,6 @@ extern int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state);
|
|||
extern int omap4_finish_suspend(unsigned long cpu_state);
|
||||
extern void omap4_cpu_resume(void);
|
||||
extern int omap4_hotplug_cpu(unsigned int cpu, unsigned int power_state);
|
||||
extern u32 omap4_mpuss_read_prev_context_state(void);
|
||||
#else
|
||||
static inline int omap4_enter_lowpower(unsigned int cpu,
|
||||
unsigned int power_state)
|
||||
|
@ -277,10 +276,6 @@ static inline int omap4_finish_suspend(unsigned long cpu_state)
|
|||
static inline void omap4_cpu_resume(void)
|
||||
{}
|
||||
|
||||
static inline u32 omap4_mpuss_read_prev_context_state(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct omap_sdrc_params;
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <linux/cpuidle.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/cpu_pm.h>
|
||||
#include <asm/cpuidle.h>
|
||||
|
||||
#include "powerdomain.h"
|
||||
#include "clockdomain.h"
|
||||
|
@ -99,11 +100,15 @@ static struct omap3_idle_statedata omap3_idle_data[] = {
|
|||
},
|
||||
};
|
||||
|
||||
/* Private functions */
|
||||
|
||||
static int __omap3_enter_idle(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv,
|
||||
int index)
|
||||
/**
|
||||
* omap3_enter_idle - Programs OMAP3 to enter the specified state
|
||||
* @dev: cpuidle device
|
||||
* @drv: cpuidle driver
|
||||
* @index: the index of state to be entered
|
||||
*/
|
||||
static int omap3_enter_idle(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv,
|
||||
int index)
|
||||
{
|
||||
struct omap3_idle_statedata *cx = &omap3_idle_data[index];
|
||||
|
||||
|
@ -148,22 +153,6 @@ static int __omap3_enter_idle(struct cpuidle_device *dev,
|
|||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* omap3_enter_idle - Programs OMAP3 to enter the specified state
|
||||
* @dev: cpuidle device
|
||||
* @drv: cpuidle driver
|
||||
* @index: the index of state to be entered
|
||||
*
|
||||
* Called from the CPUidle framework to program the device to the
|
||||
* specified target state selected by the governor.
|
||||
*/
|
||||
static inline int omap3_enter_idle(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv,
|
||||
int index)
|
||||
{
|
||||
return cpuidle_wrap_enter(dev, drv, index, __omap3_enter_idle);
|
||||
}
|
||||
|
||||
/**
|
||||
* next_valid_state - Find next valid C-state
|
||||
* @dev: cpuidle device
|
||||
|
@ -271,11 +260,9 @@ static int omap3_enter_idle_bm(struct cpuidle_device *dev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static DEFINE_PER_CPU(struct cpuidle_device, omap3_idle_dev);
|
||||
|
||||
static struct cpuidle_driver omap3_idle_driver = {
|
||||
.name = "omap3_idle",
|
||||
.owner = THIS_MODULE,
|
||||
.name = "omap3_idle",
|
||||
.owner = THIS_MODULE,
|
||||
.states = {
|
||||
{
|
||||
.enter = omap3_enter_idle_bm,
|
||||
|
@ -348,8 +335,6 @@ static struct cpuidle_driver omap3_idle_driver = {
|
|||
*/
|
||||
int __init omap3_idle_init(void)
|
||||
{
|
||||
struct cpuidle_device *dev;
|
||||
|
||||
mpu_pd = pwrdm_lookup("mpu_pwrdm");
|
||||
core_pd = pwrdm_lookup("core_pwrdm");
|
||||
per_pd = pwrdm_lookup("per_pwrdm");
|
||||
|
@ -358,16 +343,5 @@ int __init omap3_idle_init(void)
|
|||
if (!mpu_pd || !core_pd || !per_pd || !cam_pd)
|
||||
return -ENODEV;
|
||||
|
||||
cpuidle_register_driver(&omap3_idle_driver);
|
||||
|
||||
dev = &per_cpu(omap3_idle_dev, smp_processor_id());
|
||||
dev->cpu = 0;
|
||||
|
||||
if (cpuidle_register_device(dev)) {
|
||||
printk(KERN_ERR "%s: CPUidle register device failed\n",
|
||||
__func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return cpuidle_register(&omap3_idle_driver, NULL);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* OMAP4 CPU idle Routines
|
||||
* OMAP4+ CPU idle Routines
|
||||
*
|
||||
* Copyright (C) 2011 Texas Instruments, Inc.
|
||||
* Copyright (C) 2011-2013 Texas Instruments, Inc.
|
||||
* Santosh Shilimkar <santosh.shilimkar@ti.com>
|
||||
* Rajendra Nayak <rnayak@ti.com>
|
||||
*
|
||||
|
@ -14,8 +14,8 @@
|
|||
#include <linux/cpuidle.h>
|
||||
#include <linux/cpu_pm.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/clockchips.h>
|
||||
|
||||
#include <asm/cpuidle.h>
|
||||
#include <asm/proc-fns.h>
|
||||
|
||||
#include "common.h"
|
||||
|
@ -24,13 +24,13 @@
|
|||
#include "clockdomain.h"
|
||||
|
||||
/* Machine specific information */
|
||||
struct omap4_idle_statedata {
|
||||
struct idle_statedata {
|
||||
u32 cpu_state;
|
||||
u32 mpu_logic_state;
|
||||
u32 mpu_state;
|
||||
};
|
||||
|
||||
static struct omap4_idle_statedata omap4_idle_data[] = {
|
||||
static struct idle_statedata omap4_idle_data[] = {
|
||||
{
|
||||
.cpu_state = PWRDM_POWER_ON,
|
||||
.mpu_state = PWRDM_POWER_ON,
|
||||
|
@ -53,11 +53,12 @@ static struct clockdomain *cpu_clkdm[NR_CPUS];
|
|||
|
||||
static atomic_t abort_barrier;
|
||||
static bool cpu_done[NR_CPUS];
|
||||
static struct idle_statedata *state_ptr = &omap4_idle_data[0];
|
||||
|
||||
/* Private functions */
|
||||
|
||||
/**
|
||||
* omap4_enter_idle_coupled_[simple/coupled] - OMAP4 cpuidle entry functions
|
||||
* omap_enter_idle_[simple/coupled] - OMAP4PLUS cpuidle entry functions
|
||||
* @dev: cpuidle device
|
||||
* @drv: cpuidle driver
|
||||
* @index: the index of state to be entered
|
||||
|
@ -66,7 +67,7 @@ static bool cpu_done[NR_CPUS];
|
|||
* specified low power state selected by the governor.
|
||||
* Returns the amount of time spent in the low power state.
|
||||
*/
|
||||
static int omap4_enter_idle_simple(struct cpuidle_device *dev,
|
||||
static int omap_enter_idle_simple(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv,
|
||||
int index)
|
||||
{
|
||||
|
@ -77,12 +78,11 @@ static int omap4_enter_idle_simple(struct cpuidle_device *dev,
|
|||
return index;
|
||||
}
|
||||
|
||||
static int omap4_enter_idle_coupled(struct cpuidle_device *dev,
|
||||
static int omap_enter_idle_coupled(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv,
|
||||
int index)
|
||||
{
|
||||
struct omap4_idle_statedata *cx = &omap4_idle_data[index];
|
||||
int cpu_id = smp_processor_id();
|
||||
struct idle_statedata *cx = state_ptr + index;
|
||||
|
||||
local_fiq_disable();
|
||||
|
||||
|
@ -109,8 +109,6 @@ static int omap4_enter_idle_coupled(struct cpuidle_device *dev,
|
|||
}
|
||||
}
|
||||
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu_id);
|
||||
|
||||
/*
|
||||
* Call idle CPU PM enter notifier chain so that
|
||||
* VFP and per CPU interrupt context is saved.
|
||||
|
@ -149,11 +147,10 @@ static int omap4_enter_idle_coupled(struct cpuidle_device *dev,
|
|||
* Call idle CPU cluster PM exit notifier chain
|
||||
* to restore GIC and wakeupgen context.
|
||||
*/
|
||||
if (omap4_mpuss_read_prev_context_state())
|
||||
if ((cx->mpu_state == PWRDM_POWER_RET) &&
|
||||
(cx->mpu_logic_state == PWRDM_POWER_OFF))
|
||||
cpu_cluster_pm_exit();
|
||||
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu_id);
|
||||
|
||||
fail:
|
||||
cpuidle_coupled_parallel_barrier(dev, &abort_barrier);
|
||||
cpu_done[dev->cpu] = false;
|
||||
|
@ -163,49 +160,38 @@ static int omap4_enter_idle_coupled(struct cpuidle_device *dev,
|
|||
return index;
|
||||
}
|
||||
|
||||
/*
|
||||
* For each cpu, setup the broadcast timer because local timers
|
||||
* stops for the states above C1.
|
||||
*/
|
||||
static void omap_setup_broadcast_timer(void *arg)
|
||||
{
|
||||
int cpu = smp_processor_id();
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ON, &cpu);
|
||||
}
|
||||
|
||||
static DEFINE_PER_CPU(struct cpuidle_device, omap4_idle_dev);
|
||||
|
||||
static struct cpuidle_driver omap4_idle_driver = {
|
||||
.name = "omap4_idle",
|
||||
.owner = THIS_MODULE,
|
||||
.en_core_tk_irqen = 1,
|
||||
.states = {
|
||||
{
|
||||
/* C1 - CPU0 ON + CPU1 ON + MPU ON */
|
||||
.exit_latency = 2 + 2,
|
||||
.target_residency = 5,
|
||||
.flags = CPUIDLE_FLAG_TIME_VALID,
|
||||
.enter = omap4_enter_idle_simple,
|
||||
.enter = omap_enter_idle_simple,
|
||||
.name = "C1",
|
||||
.desc = "MPUSS ON"
|
||||
.desc = "CPUx ON, MPUSS ON"
|
||||
},
|
||||
{
|
||||
/* C2 - CPU0 OFF + CPU1 OFF + MPU CSWR */
|
||||
.exit_latency = 328 + 440,
|
||||
.target_residency = 960,
|
||||
.flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_COUPLED,
|
||||
.enter = omap4_enter_idle_coupled,
|
||||
.flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_COUPLED |
|
||||
CPUIDLE_FLAG_TIMER_STOP,
|
||||
.enter = omap_enter_idle_coupled,
|
||||
.name = "C2",
|
||||
.desc = "MPUSS CSWR",
|
||||
.desc = "CPUx OFF, MPUSS CSWR",
|
||||
},
|
||||
{
|
||||
/* C3 - CPU0 OFF + CPU1 OFF + MPU OSWR */
|
||||
.exit_latency = 460 + 518,
|
||||
.target_residency = 1100,
|
||||
.flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_COUPLED,
|
||||
.enter = omap4_enter_idle_coupled,
|
||||
.flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_COUPLED |
|
||||
CPUIDLE_FLAG_TIMER_STOP,
|
||||
.enter = omap_enter_idle_coupled,
|
||||
.name = "C3",
|
||||
.desc = "MPUSS OSWR",
|
||||
.desc = "CPUx OFF, MPUSS OSWR",
|
||||
},
|
||||
},
|
||||
.state_count = ARRAY_SIZE(omap4_idle_data),
|
||||
|
@ -215,16 +201,13 @@ static struct cpuidle_driver omap4_idle_driver = {
|
|||
/* Public functions */
|
||||
|
||||
/**
|
||||
* omap4_idle_init - Init routine for OMAP4 idle
|
||||
* omap4_idle_init - Init routine for OMAP4+ idle
|
||||
*
|
||||
* Registers the OMAP4 specific cpuidle driver to the cpuidle
|
||||
* Registers the OMAP4+ specific cpuidle driver to the cpuidle
|
||||
* framework with the valid set of states.
|
||||
*/
|
||||
int __init omap4_idle_init(void)
|
||||
{
|
||||
struct cpuidle_device *dev;
|
||||
unsigned int cpu_id = 0;
|
||||
|
||||
mpu_pd = pwrdm_lookup("mpu_pwrdm");
|
||||
cpu_pd[0] = pwrdm_lookup("cpu0_pwrdm");
|
||||
cpu_pd[1] = pwrdm_lookup("cpu1_pwrdm");
|
||||
|
@ -236,22 +219,5 @@ int __init omap4_idle_init(void)
|
|||
if (!cpu_clkdm[0] || !cpu_clkdm[1])
|
||||
return -ENODEV;
|
||||
|
||||
/* Configure the broadcast timer on each cpu */
|
||||
on_each_cpu(omap_setup_broadcast_timer, NULL, 1);
|
||||
|
||||
for_each_cpu(cpu_id, cpu_online_mask) {
|
||||
dev = &per_cpu(omap4_idle_dev, cpu_id);
|
||||
dev->cpu = cpu_id;
|
||||
#ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED
|
||||
dev->coupled_cpus = *cpu_online_mask;
|
||||
#endif
|
||||
cpuidle_register_driver(&omap4_idle_driver);
|
||||
|
||||
if (cpuidle_register_device(dev)) {
|
||||
pr_err("%s: CPUidle register failed\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return cpuidle_register(&omap4_idle_driver, cpu_online_mask);
|
||||
}
|
||||
|
|
|
@ -139,20 +139,6 @@ static inline void cpu_clear_prev_logic_pwrst(unsigned int cpu_id)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* omap4_mpuss_read_prev_context_state:
|
||||
* Function returns the MPUSS previous context state
|
||||
*/
|
||||
u32 omap4_mpuss_read_prev_context_state(void)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = omap4_prminst_read_inst_reg(OMAP4430_PRM_PARTITION,
|
||||
OMAP4430_PRM_MPU_INST, OMAP4_RM_MPU_MPU_CONTEXT_OFFSET);
|
||||
reg &= OMAP4430_LOSTCONTEXT_DFF_MASK;
|
||||
return reg;
|
||||
}
|
||||
|
||||
/*
|
||||
* Store the CPU cluster state for L2X0 low power operations.
|
||||
*/
|
||||
|
|
|
@ -40,12 +40,9 @@ static int s3c64xx_enter_idle(struct cpuidle_device *dev,
|
|||
return index;
|
||||
}
|
||||
|
||||
static DEFINE_PER_CPU(struct cpuidle_device, s3c64xx_cpuidle_device);
|
||||
|
||||
static struct cpuidle_driver s3c64xx_cpuidle_driver = {
|
||||
.name = "s3c64xx_cpuidle",
|
||||
.owner = THIS_MODULE,
|
||||
.en_core_tk_irqen = 1,
|
||||
.states = {
|
||||
{
|
||||
.enter = s3c64xx_enter_idle,
|
||||
|
@ -61,16 +58,6 @@ static struct cpuidle_driver s3c64xx_cpuidle_driver = {
|
|||
|
||||
static int __init s3c64xx_init_cpuidle(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
cpuidle_register_driver(&s3c64xx_cpuidle_driver);
|
||||
|
||||
ret = cpuidle_register_device(&s3c64xx_cpuidle_device);
|
||||
if (ret) {
|
||||
pr_err("Failed to register cpuidle device: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return cpuidle_register(&s3c64xx_cpuidle_driver, NULL);
|
||||
}
|
||||
device_initcall(s3c64xx_init_cpuidle);
|
||||
|
|
|
@ -16,39 +16,22 @@
|
|||
#include <asm/cpuidle.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
int shmobile_enter_wfi(struct cpuidle_device *dev, struct cpuidle_driver *drv,
|
||||
int index)
|
||||
{
|
||||
cpu_do_idle();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct cpuidle_device shmobile_cpuidle_dev;
|
||||
static struct cpuidle_driver shmobile_cpuidle_default_driver = {
|
||||
.name = "shmobile_cpuidle",
|
||||
.owner = THIS_MODULE,
|
||||
.en_core_tk_irqen = 1,
|
||||
.states[0] = ARM_CPUIDLE_WFI_STATE,
|
||||
.states[0].enter = shmobile_enter_wfi,
|
||||
.safe_state_index = 0, /* C1 */
|
||||
.state_count = 1,
|
||||
};
|
||||
|
||||
static struct cpuidle_driver *cpuidle_drv = &shmobile_cpuidle_default_driver;
|
||||
|
||||
void shmobile_cpuidle_set_driver(struct cpuidle_driver *drv)
|
||||
void __init shmobile_cpuidle_set_driver(struct cpuidle_driver *drv)
|
||||
{
|
||||
cpuidle_drv = drv;
|
||||
}
|
||||
|
||||
int shmobile_cpuidle_init(void)
|
||||
int __init shmobile_cpuidle_init(void)
|
||||
{
|
||||
struct cpuidle_device *dev = &shmobile_cpuidle_dev;
|
||||
|
||||
cpuidle_register_driver(cpuidle_drv);
|
||||
|
||||
dev->state_count = cpuidle_drv->state_count;
|
||||
cpuidle_register_device(dev);
|
||||
|
||||
return 0;
|
||||
return cpuidle_register(cpuidle_drv, NULL);
|
||||
}
|
||||
|
|
|
@ -13,9 +13,6 @@ extern int shmobile_clk_init(void);
|
|||
extern void shmobile_handle_irq_intc(struct pt_regs *);
|
||||
extern struct platform_suspend_ops shmobile_suspend_ops;
|
||||
struct cpuidle_driver;
|
||||
struct cpuidle_device;
|
||||
extern int shmobile_enter_wfi(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int index);
|
||||
extern void shmobile_cpuidle_set_driver(struct cpuidle_driver *drv);
|
||||
|
||||
extern void sh7372_init_irq(void);
|
||||
|
|
|
@ -410,11 +410,9 @@ static int sh7372_enter_a4s(struct cpuidle_device *dev,
|
|||
static struct cpuidle_driver sh7372_cpuidle_driver = {
|
||||
.name = "sh7372_cpuidle",
|
||||
.owner = THIS_MODULE,
|
||||
.en_core_tk_irqen = 1,
|
||||
.state_count = 5,
|
||||
.safe_state_index = 0, /* C1 */
|
||||
.states[0] = ARM_CPUIDLE_WFI_STATE,
|
||||
.states[0].enter = shmobile_enter_wfi,
|
||||
.states[1] = {
|
||||
.name = "C2",
|
||||
.desc = "Core Standby Mode",
|
||||
|
@ -450,12 +448,12 @@ static struct cpuidle_driver sh7372_cpuidle_driver = {
|
|||
},
|
||||
};
|
||||
|
||||
static void sh7372_cpuidle_init(void)
|
||||
static void __init sh7372_cpuidle_init(void)
|
||||
{
|
||||
shmobile_cpuidle_set_driver(&sh7372_cpuidle_driver);
|
||||
}
|
||||
#else
|
||||
static void sh7372_cpuidle_init(void) {}
|
||||
static void __init sh7372_cpuidle_init(void) {}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SUSPEND
|
||||
|
|
|
@ -23,39 +23,13 @@
|
|||
static struct cpuidle_driver tegra_idle_driver = {
|
||||
.name = "tegra_idle",
|
||||
.owner = THIS_MODULE,
|
||||
.en_core_tk_irqen = 1,
|
||||
.state_count = 1,
|
||||
.states = {
|
||||
[0] = ARM_CPUIDLE_WFI_STATE_PWR(600),
|
||||
},
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device);
|
||||
|
||||
int __init tegra114_cpuidle_init(void)
|
||||
{
|
||||
int ret;
|
||||
unsigned int cpu;
|
||||
struct cpuidle_device *dev;
|
||||
struct cpuidle_driver *drv = &tegra_idle_driver;
|
||||
|
||||
ret = cpuidle_register_driver(&tegra_idle_driver);
|
||||
if (ret) {
|
||||
pr_err("CPUidle driver registration failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
dev = &per_cpu(tegra_idle_device, cpu);
|
||||
dev->cpu = cpu;
|
||||
|
||||
dev->state_count = drv->state_count;
|
||||
ret = cpuidle_register_device(dev);
|
||||
if (ret) {
|
||||
pr_err("CPU%u: CPUidle device registration failed\n",
|
||||
cpu);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return cpuidle_register(&tegra_idle_driver, NULL);
|
||||
}
|
||||
|
|
|
@ -43,32 +43,33 @@ static atomic_t abort_barrier;
|
|||
static int tegra20_idle_lp2_coupled(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv,
|
||||
int index);
|
||||
#define TEGRA20_MAX_STATES 2
|
||||
#else
|
||||
#define TEGRA20_MAX_STATES 1
|
||||
#endif
|
||||
|
||||
static struct cpuidle_state tegra_idle_states[] = {
|
||||
[0] = ARM_CPUIDLE_WFI_STATE_PWR(600),
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
[1] = {
|
||||
.enter = tegra20_idle_lp2_coupled,
|
||||
.exit_latency = 5000,
|
||||
.target_residency = 10000,
|
||||
.power_usage = 0,
|
||||
.flags = CPUIDLE_FLAG_TIME_VALID |
|
||||
CPUIDLE_FLAG_COUPLED,
|
||||
.name = "powered-down",
|
||||
.desc = "CPU power gated",
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct cpuidle_driver tegra_idle_driver = {
|
||||
.name = "tegra_idle",
|
||||
.owner = THIS_MODULE,
|
||||
.en_core_tk_irqen = 1,
|
||||
.states = {
|
||||
ARM_CPUIDLE_WFI_STATE_PWR(600),
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
{
|
||||
.enter = tegra20_idle_lp2_coupled,
|
||||
.exit_latency = 5000,
|
||||
.target_residency = 10000,
|
||||
.power_usage = 0,
|
||||
.flags = CPUIDLE_FLAG_TIME_VALID |
|
||||
CPUIDLE_FLAG_COUPLED,
|
||||
.name = "powered-down",
|
||||
.desc = "CPU power gated",
|
||||
},
|
||||
#endif
|
||||
},
|
||||
.state_count = TEGRA20_MAX_STATES,
|
||||
.safe_state_index = 0,
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
#ifdef CONFIG_SMP
|
||||
static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
|
||||
|
@ -217,39 +218,8 @@ static int tegra20_idle_lp2_coupled(struct cpuidle_device *dev,
|
|||
|
||||
int __init tegra20_cpuidle_init(void)
|
||||
{
|
||||
int ret;
|
||||
unsigned int cpu;
|
||||
struct cpuidle_device *dev;
|
||||
struct cpuidle_driver *drv = &tegra_idle_driver;
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
tegra_tear_down_cpu = tegra20_tear_down_cpu;
|
||||
#endif
|
||||
|
||||
drv->state_count = ARRAY_SIZE(tegra_idle_states);
|
||||
memcpy(drv->states, tegra_idle_states,
|
||||
drv->state_count * sizeof(drv->states[0]));
|
||||
|
||||
ret = cpuidle_register_driver(&tegra_idle_driver);
|
||||
if (ret) {
|
||||
pr_err("CPUidle driver registration failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
dev = &per_cpu(tegra_idle_device, cpu);
|
||||
dev->cpu = cpu;
|
||||
#ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED
|
||||
dev->coupled_cpus = *cpu_possible_mask;
|
||||
#endif
|
||||
|
||||
dev->state_count = drv->state_count;
|
||||
ret = cpuidle_register_device(dev);
|
||||
if (ret) {
|
||||
pr_err("CPU%u: CPUidle device registration failed\n",
|
||||
cpu);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return cpuidle_register(&tegra_idle_driver, cpu_possible_mask);
|
||||
}
|
||||
|
|
|
@ -43,7 +43,6 @@ static int tegra30_idle_lp2(struct cpuidle_device *dev,
|
|||
static struct cpuidle_driver tegra_idle_driver = {
|
||||
.name = "tegra_idle",
|
||||
.owner = THIS_MODULE,
|
||||
.en_core_tk_irqen = 1,
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
.state_count = 2,
|
||||
#else
|
||||
|
@ -65,8 +64,6 @@ static struct cpuidle_driver tegra_idle_driver = {
|
|||
},
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static bool tegra30_cpu_cluster_power_down(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv,
|
||||
|
@ -157,32 +154,8 @@ static int tegra30_idle_lp2(struct cpuidle_device *dev,
|
|||
|
||||
int __init tegra30_cpuidle_init(void)
|
||||
{
|
||||
int ret;
|
||||
unsigned int cpu;
|
||||
struct cpuidle_device *dev;
|
||||
struct cpuidle_driver *drv = &tegra_idle_driver;
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
tegra_tear_down_cpu = tegra30_tear_down_cpu;
|
||||
#endif
|
||||
|
||||
ret = cpuidle_register_driver(&tegra_idle_driver);
|
||||
if (ret) {
|
||||
pr_err("CPUidle driver registration failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
dev = &per_cpu(tegra_idle_device, cpu);
|
||||
dev->cpu = cpu;
|
||||
|
||||
dev->state_count = drv->state_count;
|
||||
ret = cpuidle_register_device(dev);
|
||||
if (ret) {
|
||||
pr_err("CPU%u: CPUidle device registration failed\n",
|
||||
cpu);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return cpuidle_register(&tegra_idle_driver, NULL);
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/cpuidle.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/smp.h>
|
||||
|
@ -22,7 +21,6 @@
|
|||
|
||||
static atomic_t master = ATOMIC_INIT(0);
|
||||
static DEFINE_SPINLOCK(master_lock);
|
||||
static DEFINE_PER_CPU(struct cpuidle_device, ux500_cpuidle_device);
|
||||
|
||||
static inline int ux500_enter_idle(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int index)
|
||||
|
@ -30,8 +28,6 @@ static inline int ux500_enter_idle(struct cpuidle_device *dev,
|
|||
int this_cpu = smp_processor_id();
|
||||
bool recouple = false;
|
||||
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &this_cpu);
|
||||
|
||||
if (atomic_inc_return(&master) == num_online_cpus()) {
|
||||
|
||||
/* With this lock, we prevent the other cpu to exit and enter
|
||||
|
@ -91,22 +87,20 @@ static inline int ux500_enter_idle(struct cpuidle_device *dev,
|
|||
spin_unlock(&master_lock);
|
||||
}
|
||||
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &this_cpu);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static struct cpuidle_driver ux500_idle_driver = {
|
||||
.name = "ux500_idle",
|
||||
.owner = THIS_MODULE,
|
||||
.en_core_tk_irqen = 1,
|
||||
.states = {
|
||||
ARM_CPUIDLE_WFI_STATE,
|
||||
{
|
||||
.enter = ux500_enter_idle,
|
||||
.exit_latency = 70,
|
||||
.target_residency = 260,
|
||||
.flags = CPUIDLE_FLAG_TIME_VALID,
|
||||
.flags = CPUIDLE_FLAG_TIME_VALID |
|
||||
CPUIDLE_FLAG_TIMER_STOP,
|
||||
.name = "ApIdle",
|
||||
.desc = "ARM Retention",
|
||||
},
|
||||
|
@ -115,59 +109,13 @@ static struct cpuidle_driver ux500_idle_driver = {
|
|||
.state_count = 2,
|
||||
};
|
||||
|
||||
/*
|
||||
* For each cpu, setup the broadcast timer because we will
|
||||
* need to migrate the timers for the states >= ApIdle.
|
||||
*/
|
||||
static void ux500_setup_broadcast_timer(void *arg)
|
||||
{
|
||||
int cpu = smp_processor_id();
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ON, &cpu);
|
||||
}
|
||||
|
||||
int __init ux500_idle_init(void)
|
||||
{
|
||||
int ret, cpu;
|
||||
struct cpuidle_device *device;
|
||||
|
||||
/* Configure wake up reasons */
|
||||
prcmu_enable_wakeups(PRCMU_WAKEUP(ARM) | PRCMU_WAKEUP(RTC) |
|
||||
PRCMU_WAKEUP(ABB));
|
||||
|
||||
/*
|
||||
* Configure the timer broadcast for each cpu, that must
|
||||
* be done from the cpu context, so we use a smp cross
|
||||
* call with 'on_each_cpu'.
|
||||
*/
|
||||
on_each_cpu(ux500_setup_broadcast_timer, NULL, 1);
|
||||
|
||||
ret = cpuidle_register_driver(&ux500_idle_driver);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "failed to register ux500 idle driver\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
for_each_online_cpu(cpu) {
|
||||
device = &per_cpu(ux500_cpuidle_device, cpu);
|
||||
device->cpu = cpu;
|
||||
ret = cpuidle_register_device(device);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "Failed to register cpuidle "
|
||||
"device for cpu%d\n", cpu);
|
||||
goto out_unregister;
|
||||
}
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
|
||||
out_unregister:
|
||||
for_each_online_cpu(cpu) {
|
||||
device = &per_cpu(ux500_cpuidle_device, cpu);
|
||||
cpuidle_unregister_device(device);
|
||||
}
|
||||
|
||||
cpuidle_unregister_driver(&ux500_idle_driver);
|
||||
goto out;
|
||||
return cpuidle_register(&ux500_idle_driver, NULL);
|
||||
}
|
||||
|
||||
device_initcall(ux500_idle_init);
|
||||
|
|
|
@ -23,8 +23,8 @@
|
|||
#include "pseries.h"
|
||||
|
||||
struct cpuidle_driver pseries_idle_driver = {
|
||||
.name = "pseries_idle",
|
||||
.owner = THIS_MODULE,
|
||||
.name = "pseries_idle",
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
#define MAX_IDLE_STATE_COUNT 2
|
||||
|
@ -33,10 +33,8 @@ static int max_idle_state = MAX_IDLE_STATE_COUNT - 1;
|
|||
static struct cpuidle_device __percpu *pseries_cpuidle_devices;
|
||||
static struct cpuidle_state *cpuidle_state_table;
|
||||
|
||||
static inline void idle_loop_prolog(unsigned long *in_purr, ktime_t *kt_before)
|
||||
static inline void idle_loop_prolog(unsigned long *in_purr)
|
||||
{
|
||||
|
||||
*kt_before = ktime_get();
|
||||
*in_purr = mfspr(SPRN_PURR);
|
||||
/*
|
||||
* Indicate to the HV that we are idle. Now would be
|
||||
|
@ -45,12 +43,10 @@ static inline void idle_loop_prolog(unsigned long *in_purr, ktime_t *kt_before)
|
|||
get_lppaca()->idle = 1;
|
||||
}
|
||||
|
||||
static inline s64 idle_loop_epilog(unsigned long in_purr, ktime_t kt_before)
|
||||
static inline void idle_loop_epilog(unsigned long in_purr)
|
||||
{
|
||||
get_lppaca()->wait_state_cycles += mfspr(SPRN_PURR) - in_purr;
|
||||
get_lppaca()->idle = 0;
|
||||
|
||||
return ktime_to_us(ktime_sub(ktime_get(), kt_before));
|
||||
}
|
||||
|
||||
static int snooze_loop(struct cpuidle_device *dev,
|
||||
|
@ -58,10 +54,9 @@ static int snooze_loop(struct cpuidle_device *dev,
|
|||
int index)
|
||||
{
|
||||
unsigned long in_purr;
|
||||
ktime_t kt_before;
|
||||
int cpu = dev->cpu;
|
||||
|
||||
idle_loop_prolog(&in_purr, &kt_before);
|
||||
idle_loop_prolog(&in_purr);
|
||||
local_irq_enable();
|
||||
set_thread_flag(TIF_POLLING_NRFLAG);
|
||||
|
||||
|
@ -75,8 +70,8 @@ static int snooze_loop(struct cpuidle_device *dev,
|
|||
clear_thread_flag(TIF_POLLING_NRFLAG);
|
||||
smp_mb();
|
||||
|
||||
dev->last_residency =
|
||||
(int)idle_loop_epilog(in_purr, kt_before);
|
||||
idle_loop_epilog(in_purr);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
|
@ -102,9 +97,8 @@ static int dedicated_cede_loop(struct cpuidle_device *dev,
|
|||
int index)
|
||||
{
|
||||
unsigned long in_purr;
|
||||
ktime_t kt_before;
|
||||
|
||||
idle_loop_prolog(&in_purr, &kt_before);
|
||||
idle_loop_prolog(&in_purr);
|
||||
get_lppaca()->donate_dedicated_cpu = 1;
|
||||
|
||||
ppc64_runlatch_off();
|
||||
|
@ -112,8 +106,9 @@ static int dedicated_cede_loop(struct cpuidle_device *dev,
|
|||
check_and_cede_processor();
|
||||
|
||||
get_lppaca()->donate_dedicated_cpu = 0;
|
||||
dev->last_residency =
|
||||
(int)idle_loop_epilog(in_purr, kt_before);
|
||||
|
||||
idle_loop_epilog(in_purr);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
|
@ -122,9 +117,8 @@ static int shared_cede_loop(struct cpuidle_device *dev,
|
|||
int index)
|
||||
{
|
||||
unsigned long in_purr;
|
||||
ktime_t kt_before;
|
||||
|
||||
idle_loop_prolog(&in_purr, &kt_before);
|
||||
idle_loop_prolog(&in_purr);
|
||||
|
||||
/*
|
||||
* Yield the processor to the hypervisor. We return if
|
||||
|
@ -135,8 +129,8 @@ static int shared_cede_loop(struct cpuidle_device *dev,
|
|||
*/
|
||||
check_and_cede_processor();
|
||||
|
||||
dev->last_residency =
|
||||
(int)idle_loop_epilog(in_purr, kt_before);
|
||||
idle_loop_epilog(in_purr);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,9 +14,9 @@ struct swsusp_arch_regs {
|
|||
void sh_mobile_call_standby(unsigned long mode);
|
||||
|
||||
#ifdef CONFIG_CPU_IDLE
|
||||
void sh_mobile_setup_cpuidle(void);
|
||||
int sh_mobile_setup_cpuidle(void);
|
||||
#else
|
||||
static inline void sh_mobile_setup_cpuidle(void) {}
|
||||
static inline int sh_mobile_setup_cpuidle(void) { return 0; }
|
||||
#endif
|
||||
|
||||
/* notifier chains for pre/post sleep hooks */
|
||||
|
|
|
@ -51,70 +51,53 @@ static int cpuidle_sleep_enter(struct cpuidle_device *dev,
|
|||
return k;
|
||||
}
|
||||
|
||||
static struct cpuidle_device cpuidle_dev;
|
||||
static struct cpuidle_driver cpuidle_driver = {
|
||||
.name = "sh_idle",
|
||||
.owner = THIS_MODULE,
|
||||
.en_core_tk_irqen = 1,
|
||||
.name = "sh_idle",
|
||||
.owner = THIS_MODULE,
|
||||
.states = {
|
||||
{
|
||||
.exit_latency = 1,
|
||||
.target_residency = 1 * 2,
|
||||
.power_usage = 3,
|
||||
.flags = CPUIDLE_FLAG_TIME_VALID,
|
||||
.enter = cpuidle_sleep_enter,
|
||||
.name = "C1",
|
||||
.desc = "SuperH Sleep Mode",
|
||||
},
|
||||
{
|
||||
.exit_latency = 100,
|
||||
.target_residency = 1 * 2,
|
||||
.power_usage = 1,
|
||||
.flags = CPUIDLE_FLAG_TIME_VALID,
|
||||
.enter = cpuidle_sleep_enter,
|
||||
.name = "C2",
|
||||
.desc = "SuperH Sleep Mode [SF]",
|
||||
.disabled = true,
|
||||
},
|
||||
{
|
||||
.exit_latency = 2300,
|
||||
.target_residency = 1 * 2,
|
||||
.power_usage = 1,
|
||||
.flags = CPUIDLE_FLAG_TIME_VALID,
|
||||
.enter = cpuidle_sleep_enter,
|
||||
.name = "C3",
|
||||
.desc = "SuperH Mobile Standby Mode [SF]",
|
||||
.disabled = true,
|
||||
},
|
||||
},
|
||||
.safe_state_index = 0,
|
||||
.state_count = 3,
|
||||
};
|
||||
|
||||
void sh_mobile_setup_cpuidle(void)
|
||||
int __init sh_mobile_setup_cpuidle(void)
|
||||
{
|
||||
struct cpuidle_device *dev = &cpuidle_dev;
|
||||
struct cpuidle_driver *drv = &cpuidle_driver;
|
||||
struct cpuidle_state *state;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
if (sh_mobile_sleep_supported & SUSP_SH_SF)
|
||||
cpuidle_driver.states[1].disabled = false;
|
||||
|
||||
for (i = 0; i < CPUIDLE_STATE_MAX; i++) {
|
||||
drv->states[i].name[0] = '\0';
|
||||
drv->states[i].desc[0] = '\0';
|
||||
}
|
||||
if (sh_mobile_sleep_supported & SUSP_SH_STANDBY)
|
||||
cpuidle_driver.states[2].disabled = false;
|
||||
|
||||
i = CPUIDLE_DRIVER_STATE_START;
|
||||
|
||||
state = &drv->states[i++];
|
||||
snprintf(state->name, CPUIDLE_NAME_LEN, "C1");
|
||||
strncpy(state->desc, "SuperH Sleep Mode", CPUIDLE_DESC_LEN);
|
||||
state->exit_latency = 1;
|
||||
state->target_residency = 1 * 2;
|
||||
state->power_usage = 3;
|
||||
state->flags = 0;
|
||||
state->flags |= CPUIDLE_FLAG_TIME_VALID;
|
||||
state->enter = cpuidle_sleep_enter;
|
||||
|
||||
drv->safe_state_index = i-1;
|
||||
|
||||
if (sh_mobile_sleep_supported & SUSP_SH_SF) {
|
||||
state = &drv->states[i++];
|
||||
snprintf(state->name, CPUIDLE_NAME_LEN, "C2");
|
||||
strncpy(state->desc, "SuperH Sleep Mode [SF]",
|
||||
CPUIDLE_DESC_LEN);
|
||||
state->exit_latency = 100;
|
||||
state->target_residency = 1 * 2;
|
||||
state->power_usage = 1;
|
||||
state->flags = 0;
|
||||
state->flags |= CPUIDLE_FLAG_TIME_VALID;
|
||||
state->enter = cpuidle_sleep_enter;
|
||||
}
|
||||
|
||||
if (sh_mobile_sleep_supported & SUSP_SH_STANDBY) {
|
||||
state = &drv->states[i++];
|
||||
snprintf(state->name, CPUIDLE_NAME_LEN, "C3");
|
||||
strncpy(state->desc, "SuperH Mobile Standby Mode [SF]",
|
||||
CPUIDLE_DESC_LEN);
|
||||
state->exit_latency = 2300;
|
||||
state->target_residency = 1 * 2;
|
||||
state->power_usage = 1;
|
||||
state->flags = 0;
|
||||
state->flags |= CPUIDLE_FLAG_TIME_VALID;
|
||||
state->enter = cpuidle_sleep_enter;
|
||||
}
|
||||
|
||||
drv->state_count = i;
|
||||
dev->state_count = i;
|
||||
|
||||
cpuidle_register_driver(&cpuidle_driver);
|
||||
|
||||
cpuidle_register_device(dev);
|
||||
return cpuidle_register(&cpuidle_driver);
|
||||
}
|
||||
|
|
|
@ -150,8 +150,7 @@ static const struct platform_suspend_ops sh_pm_ops = {
|
|||
static int __init sh_pm_init(void)
|
||||
{
|
||||
suspend_set_ops(&sh_pm_ops);
|
||||
sh_mobile_setup_cpuidle();
|
||||
return 0;
|
||||
return sh_mobile_setup_cpuidle();
|
||||
}
|
||||
|
||||
late_initcall(sh_pm_init);
|
||||
|
|
|
@ -373,7 +373,6 @@ static int apm_cpu_idle(struct cpuidle_device *dev,
|
|||
static struct cpuidle_driver apm_idle_driver = {
|
||||
.name = "apm_idle",
|
||||
.owner = THIS_MODULE,
|
||||
.en_core_tk_irqen = 1,
|
||||
.states = {
|
||||
{ /* entry 0 is for polling */ },
|
||||
{ /* entry 1 is for APM idle */
|
||||
|
|
|
@ -918,7 +918,6 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
|
|||
struct cpuidle_driver acpi_idle_driver = {
|
||||
.name = "acpi_idle",
|
||||
.owner = THIS_MODULE,
|
||||
.en_core_tk_irqen = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -39,10 +39,4 @@ config CPU_IDLE_CALXEDA
|
|||
help
|
||||
Select this to enable cpuidle on Calxeda processors.
|
||||
|
||||
config CPU_IDLE_KIRKWOOD
|
||||
bool "CPU Idle Driver for Kirkwood processors"
|
||||
depends on ARCH_KIRKWOOD
|
||||
help
|
||||
Select this to enable cpuidle on Kirkwood processors.
|
||||
|
||||
endif
|
||||
|
|
|
@ -6,4 +6,4 @@ obj-y += cpuidle.o driver.o governor.o sysfs.o governors/
|
|||
obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o
|
||||
|
||||
obj-$(CONFIG_CPU_IDLE_CALXEDA) += cpuidle-calxeda.o
|
||||
obj-$(CONFIG_CPU_IDLE_KIRKWOOD) += cpuidle-kirkwood.o
|
||||
obj-$(CONFIG_ARCH_KIRKWOOD) += cpuidle-kirkwood.o
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Copyright 2012 Calxeda, Inc.
|
||||
*
|
||||
* Based on arch/arm/plat-mxc/cpuidle.c:
|
||||
* Based on arch/arm/plat-mxc/cpuidle.c: #v3.7
|
||||
* Copyright 2012 Freescale Semiconductor, Inc.
|
||||
* Copyright 2012 Linaro Ltd.
|
||||
*
|
||||
|
@ -16,6 +16,8 @@
|
|||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Maintainer: Rob Herring <rob.herring@calxeda.com>
|
||||
*/
|
||||
|
||||
#include <linux/cpuidle.h>
|
||||
|
@ -35,8 +37,6 @@
|
|||
extern void highbank_set_cpu_jump(int cpu, void *jump_addr);
|
||||
extern void *scu_base_addr;
|
||||
|
||||
static struct cpuidle_device __percpu *calxeda_idle_cpuidle_devices;
|
||||
|
||||
static inline unsigned int get_auxcr(void)
|
||||
{
|
||||
unsigned int val;
|
||||
|
@ -85,22 +85,8 @@ static int calxeda_pwrdown_idle(struct cpuidle_device *dev,
|
|||
return index;
|
||||
}
|
||||
|
||||
static void calxeda_idle_cpuidle_devices_uninit(void)
|
||||
{
|
||||
int i;
|
||||
struct cpuidle_device *dev;
|
||||
|
||||
for_each_possible_cpu(i) {
|
||||
dev = per_cpu_ptr(calxeda_idle_cpuidle_devices, i);
|
||||
cpuidle_unregister_device(dev);
|
||||
}
|
||||
|
||||
free_percpu(calxeda_idle_cpuidle_devices);
|
||||
}
|
||||
|
||||
static struct cpuidle_driver calxeda_idle_driver = {
|
||||
.name = "calxeda_idle",
|
||||
.en_core_tk_irqen = 1,
|
||||
.states = {
|
||||
ARM_CPUIDLE_WFI_STATE,
|
||||
{
|
||||
|
@ -118,44 +104,9 @@ static struct cpuidle_driver calxeda_idle_driver = {
|
|||
|
||||
static int __init calxeda_cpuidle_init(void)
|
||||
{
|
||||
int cpu_id;
|
||||
int ret;
|
||||
struct cpuidle_device *dev;
|
||||
struct cpuidle_driver *drv = &calxeda_idle_driver;
|
||||
|
||||
if (!of_machine_is_compatible("calxeda,highbank"))
|
||||
return -ENODEV;
|
||||
|
||||
ret = cpuidle_register_driver(drv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
calxeda_idle_cpuidle_devices = alloc_percpu(struct cpuidle_device);
|
||||
if (calxeda_idle_cpuidle_devices == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto unregister_drv;
|
||||
}
|
||||
|
||||
/* initialize state data for each cpuidle_device */
|
||||
for_each_possible_cpu(cpu_id) {
|
||||
dev = per_cpu_ptr(calxeda_idle_cpuidle_devices, cpu_id);
|
||||
dev->cpu = cpu_id;
|
||||
dev->state_count = drv->state_count;
|
||||
|
||||
ret = cpuidle_register_device(dev);
|
||||
if (ret) {
|
||||
pr_err("Failed to register cpu %u, error: %d\n",
|
||||
cpu_id, ret);
|
||||
goto uninit;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
uninit:
|
||||
calxeda_idle_cpuidle_devices_uninit();
|
||||
unregister_drv:
|
||||
cpuidle_unregister_driver(drv);
|
||||
return ret;
|
||||
return cpuidle_register(&calxeda_idle_driver, NULL);
|
||||
}
|
||||
module_init(calxeda_cpuidle_init);
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
/*
|
||||
* arch/arm/mach-kirkwood/cpuidle.c
|
||||
*
|
||||
* CPU idle Marvell Kirkwood SoCs
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
|
@ -11,6 +9,9 @@
|
|||
* to implement two idle states -
|
||||
* #1 wait-for-interrupt
|
||||
* #2 wait-for-interrupt and DDR self refresh
|
||||
*
|
||||
* Maintainer: Jason Cooper <jason@lakedaemon.net>
|
||||
* Maintainer: Andrew Lunn <andrew@lunn.ch>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
@ -41,7 +42,6 @@ static int kirkwood_enter_idle(struct cpuidle_device *dev,
|
|||
static struct cpuidle_driver kirkwood_idle_driver = {
|
||||
.name = "kirkwood_idle",
|
||||
.owner = THIS_MODULE,
|
||||
.en_core_tk_irqen = 1,
|
||||
.states[0] = ARM_CPUIDLE_WFI_STATE,
|
||||
.states[1] = {
|
||||
.enter = kirkwood_enter_idle,
|
||||
|
@ -53,9 +53,6 @@ static struct cpuidle_driver kirkwood_idle_driver = {
|
|||
},
|
||||
.state_count = KIRKWOOD_MAX_STATES,
|
||||
};
|
||||
static struct cpuidle_device *device;
|
||||
|
||||
static DEFINE_PER_CPU(struct cpuidle_device, kirkwood_cpuidle_device);
|
||||
|
||||
/* Initialize CPU idle by registering the idle states */
|
||||
static int kirkwood_cpuidle_probe(struct platform_device *pdev)
|
||||
|
@ -66,26 +63,16 @@ static int kirkwood_cpuidle_probe(struct platform_device *pdev)
|
|||
if (res == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
ddr_operation_base = devm_request_and_ioremap(&pdev->dev, res);
|
||||
if (!ddr_operation_base)
|
||||
return -EADDRNOTAVAIL;
|
||||
ddr_operation_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(ddr_operation_base))
|
||||
return PTR_ERR(ddr_operation_base);
|
||||
|
||||
device = &per_cpu(kirkwood_cpuidle_device, smp_processor_id());
|
||||
device->state_count = KIRKWOOD_MAX_STATES;
|
||||
|
||||
cpuidle_register_driver(&kirkwood_idle_driver);
|
||||
if (cpuidle_register_device(device)) {
|
||||
pr_err("kirkwood_init_cpuidle: Failed registering\n");
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
return cpuidle_register(&kirkwood_idle_driver, NULL);
|
||||
}
|
||||
|
||||
int kirkwood_cpuidle_remove(struct platform_device *pdev)
|
||||
{
|
||||
cpuidle_unregister_device(device);
|
||||
cpuidle_unregister_driver(&kirkwood_idle_driver);
|
||||
|
||||
cpuidle_unregister(&kirkwood_idle_driver);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
* This code is licenced under the GPL.
|
||||
*/
|
||||
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sched.h>
|
||||
|
@ -23,6 +24,7 @@
|
|||
#include "cpuidle.h"
|
||||
|
||||
DEFINE_PER_CPU(struct cpuidle_device *, cpuidle_devices);
|
||||
DEFINE_PER_CPU(struct cpuidle_device, cpuidle_dev);
|
||||
|
||||
DEFINE_MUTEX(cpuidle_lock);
|
||||
LIST_HEAD(cpuidle_detected_devices);
|
||||
|
@ -42,24 +44,6 @@ void disable_cpuidle(void)
|
|||
|
||||
static int __cpuidle_register_device(struct cpuidle_device *dev);
|
||||
|
||||
static inline int cpuidle_enter(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int index)
|
||||
{
|
||||
struct cpuidle_state *target_state = &drv->states[index];
|
||||
return target_state->enter(dev, drv, index);
|
||||
}
|
||||
|
||||
static inline int cpuidle_enter_tk(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int index)
|
||||
{
|
||||
return cpuidle_wrap_enter(dev, drv, index, cpuidle_enter);
|
||||
}
|
||||
|
||||
typedef int (*cpuidle_enter_t)(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int index);
|
||||
|
||||
static cpuidle_enter_t cpuidle_enter_ops;
|
||||
|
||||
/**
|
||||
* cpuidle_play_dead - cpu off-lining
|
||||
*
|
||||
|
@ -89,11 +73,27 @@ int cpuidle_play_dead(void)
|
|||
* @next_state: index into drv->states of the state to enter
|
||||
*/
|
||||
int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
|
||||
int next_state)
|
||||
int index)
|
||||
{
|
||||
int entered_state;
|
||||
|
||||
entered_state = cpuidle_enter_ops(dev, drv, next_state);
|
||||
struct cpuidle_state *target_state = &drv->states[index];
|
||||
ktime_t time_start, time_end;
|
||||
s64 diff;
|
||||
|
||||
time_start = ktime_get();
|
||||
|
||||
entered_state = target_state->enter(dev, drv, index);
|
||||
|
||||
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;
|
||||
|
||||
if (entered_state >= 0) {
|
||||
/* Update cpuidle counters */
|
||||
|
@ -146,12 +146,20 @@ int cpuidle_idle_call(void)
|
|||
|
||||
trace_cpu_idle_rcuidle(next_state, dev->cpu);
|
||||
|
||||
if (drv->states[next_state].flags & CPUIDLE_FLAG_TIMER_STOP)
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER,
|
||||
&dev->cpu);
|
||||
|
||||
if (cpuidle_state_is_coupled(dev, drv, next_state))
|
||||
entered_state = cpuidle_enter_state_coupled(dev, drv,
|
||||
next_state);
|
||||
else
|
||||
entered_state = cpuidle_enter_state(dev, drv, next_state);
|
||||
|
||||
if (drv->states[next_state].flags & CPUIDLE_FLAG_TIMER_STOP)
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT,
|
||||
&dev->cpu);
|
||||
|
||||
trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu);
|
||||
|
||||
/* give the governor an opportunity to reflect on the outcome */
|
||||
|
@ -222,37 +230,6 @@ void cpuidle_resume(void)
|
|||
mutex_unlock(&cpuidle_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* cpuidle_wrap_enter - performs timekeeping and irqen around enter function
|
||||
* @dev: pointer to a valid cpuidle_device object
|
||||
* @drv: pointer to a valid cpuidle_driver object
|
||||
* @index: index of the target cpuidle state.
|
||||
*/
|
||||
int cpuidle_wrap_enter(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int index,
|
||||
int (*enter)(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int index))
|
||||
{
|
||||
ktime_t time_start, time_end;
|
||||
s64 diff;
|
||||
|
||||
time_start = ktime_get();
|
||||
|
||||
index = enter(dev, drv, index);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARCH_HAS_CPU_RELAX
|
||||
static int poll_idle(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int index)
|
||||
|
@ -324,9 +301,6 @@ int cpuidle_enable_device(struct cpuidle_device *dev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
cpuidle_enter_ops = drv->en_core_tk_irqen ?
|
||||
cpuidle_enter_tk : cpuidle_enter;
|
||||
|
||||
poll_idle_init(drv);
|
||||
|
||||
ret = cpuidle_add_device_sysfs(dev);
|
||||
|
@ -480,6 +454,77 @@ void cpuidle_unregister_device(struct cpuidle_device *dev)
|
|||
|
||||
EXPORT_SYMBOL_GPL(cpuidle_unregister_device);
|
||||
|
||||
/**
|
||||
* cpuidle_unregister: unregister a driver and the devices. This function
|
||||
* can be used only if the driver has been previously registered through
|
||||
* the cpuidle_register function.
|
||||
*
|
||||
* @drv: a valid pointer to a struct cpuidle_driver
|
||||
*/
|
||||
void cpuidle_unregister(struct cpuidle_driver *drv)
|
||||
{
|
||||
int cpu;
|
||||
struct cpuidle_device *device;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
device = &per_cpu(cpuidle_dev, cpu);
|
||||
cpuidle_unregister_device(device);
|
||||
}
|
||||
|
||||
cpuidle_unregister_driver(drv);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cpuidle_unregister);
|
||||
|
||||
/**
|
||||
* cpuidle_register: registers the driver and the cpu devices with the
|
||||
* coupled_cpus passed as parameter. This function is used for all common
|
||||
* initialization pattern there are in the arch specific drivers. The
|
||||
* devices is globally defined in this file.
|
||||
*
|
||||
* @drv : a valid pointer to a struct cpuidle_driver
|
||||
* @coupled_cpus: a cpumask for the coupled states
|
||||
*
|
||||
* Returns 0 on success, < 0 otherwise
|
||||
*/
|
||||
int cpuidle_register(struct cpuidle_driver *drv,
|
||||
const struct cpumask *const coupled_cpus)
|
||||
{
|
||||
int ret, cpu;
|
||||
struct cpuidle_device *device;
|
||||
|
||||
ret = cpuidle_register_driver(drv);
|
||||
if (ret) {
|
||||
pr_err("failed to register cpuidle driver\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
device = &per_cpu(cpuidle_dev, cpu);
|
||||
device->cpu = cpu;
|
||||
|
||||
#ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED
|
||||
/*
|
||||
* On multiplatform for ARM, the coupled idle states could
|
||||
* enabled in the kernel even if the cpuidle driver does not
|
||||
* use it. Note, coupled_cpus is a struct copy.
|
||||
*/
|
||||
if (coupled_cpus)
|
||||
device->coupled_cpus = *coupled_cpus;
|
||||
#endif
|
||||
ret = cpuidle_register_device(device);
|
||||
if (!ret)
|
||||
continue;
|
||||
|
||||
pr_err("Failed to register cpuidle device for cpu%d\n", cpu);
|
||||
|
||||
cpuidle_unregister(drv);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cpuidle_register);
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
|
||||
static void smp_callback(void *v)
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/cpuidle.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/clockchips.h>
|
||||
|
||||
#include "cpuidle.h"
|
||||
|
||||
|
@ -19,9 +21,28 @@ DEFINE_SPINLOCK(cpuidle_driver_lock);
|
|||
static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu);
|
||||
static struct cpuidle_driver * __cpuidle_get_cpu_driver(int cpu);
|
||||
|
||||
static void __cpuidle_driver_init(struct cpuidle_driver *drv)
|
||||
static void cpuidle_setup_broadcast_timer(void *arg)
|
||||
{
|
||||
int cpu = smp_processor_id();
|
||||
clockevents_notify((long)(arg), &cpu);
|
||||
}
|
||||
|
||||
static void __cpuidle_driver_init(struct cpuidle_driver *drv, int cpu)
|
||||
{
|
||||
int i;
|
||||
|
||||
drv->refcnt = 0;
|
||||
|
||||
for (i = drv->state_count - 1; i >= 0 ; i--) {
|
||||
|
||||
if (!(drv->states[i].flags & CPUIDLE_FLAG_TIMER_STOP))
|
||||
continue;
|
||||
|
||||
drv->bctimer = 1;
|
||||
on_each_cpu_mask(get_cpu_mask(cpu), cpuidle_setup_broadcast_timer,
|
||||
(void *)CLOCK_EVT_NOTIFY_BROADCAST_ON, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int __cpuidle_register_driver(struct cpuidle_driver *drv, int cpu)
|
||||
|
@ -35,7 +56,7 @@ static int __cpuidle_register_driver(struct cpuidle_driver *drv, int cpu)
|
|||
if (__cpuidle_get_cpu_driver(cpu))
|
||||
return -EBUSY;
|
||||
|
||||
__cpuidle_driver_init(drv);
|
||||
__cpuidle_driver_init(drv, cpu);
|
||||
|
||||
__cpuidle_set_cpu_driver(drv, cpu);
|
||||
|
||||
|
@ -49,6 +70,12 @@ static void __cpuidle_unregister_driver(struct cpuidle_driver *drv, int cpu)
|
|||
|
||||
if (!WARN_ON(drv->refcnt > 0))
|
||||
__cpuidle_set_cpu_driver(NULL, cpu);
|
||||
|
||||
if (drv->bctimer) {
|
||||
drv->bctimer = 0;
|
||||
on_each_cpu_mask(get_cpu_mask(cpu), cpuidle_setup_broadcast_timer,
|
||||
(void *)CLOCK_EVT_NOTIFY_BROADCAST_OFF, 1);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS
|
||||
|
|
|
@ -71,7 +71,6 @@
|
|||
static struct cpuidle_driver intel_idle_driver = {
|
||||
.name = "intel_idle",
|
||||
.owner = THIS_MODULE,
|
||||
.en_core_tk_irqen = 1,
|
||||
};
|
||||
/* intel_idle.max_cstate=0 disables driver */
|
||||
static int max_cstate = CPUIDLE_STATE_MAX - 1;
|
||||
|
@ -339,7 +338,6 @@ static int intel_idle(struct cpuidle_device *dev,
|
|||
if (!(lapic_timer_reliable_states & (1 << (cstate))))
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu);
|
||||
|
||||
stop_critical_timings();
|
||||
if (!need_resched()) {
|
||||
|
||||
__monitor((void *)¤t_thread_info()->flags, 0, 0);
|
||||
|
@ -348,8 +346,6 @@ static int intel_idle(struct cpuidle_device *dev,
|
|||
__mwait(eax, ecx);
|
||||
}
|
||||
|
||||
start_critical_timings();
|
||||
|
||||
if (!(lapic_timer_reliable_states & (1 << (cstate))))
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu);
|
||||
|
||||
|
|
|
@ -8,6 +8,20 @@
|
|||
#ifndef _LINUX_CLOCKCHIPS_H
|
||||
#define _LINUX_CLOCKCHIPS_H
|
||||
|
||||
/* Clock event notification values */
|
||||
enum clock_event_nofitiers {
|
||||
CLOCK_EVT_NOTIFY_ADD,
|
||||
CLOCK_EVT_NOTIFY_BROADCAST_ON,
|
||||
CLOCK_EVT_NOTIFY_BROADCAST_OFF,
|
||||
CLOCK_EVT_NOTIFY_BROADCAST_FORCE,
|
||||
CLOCK_EVT_NOTIFY_BROADCAST_ENTER,
|
||||
CLOCK_EVT_NOTIFY_BROADCAST_EXIT,
|
||||
CLOCK_EVT_NOTIFY_SUSPEND,
|
||||
CLOCK_EVT_NOTIFY_RESUME,
|
||||
CLOCK_EVT_NOTIFY_CPU_DYING,
|
||||
CLOCK_EVT_NOTIFY_CPU_DEAD,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_GENERIC_CLOCKEVENTS_BUILD
|
||||
|
||||
#include <linux/clocksource.h>
|
||||
|
@ -26,20 +40,6 @@ enum clock_event_mode {
|
|||
CLOCK_EVT_MODE_RESUME,
|
||||
};
|
||||
|
||||
/* Clock event notification values */
|
||||
enum clock_event_nofitiers {
|
||||
CLOCK_EVT_NOTIFY_ADD,
|
||||
CLOCK_EVT_NOTIFY_BROADCAST_ON,
|
||||
CLOCK_EVT_NOTIFY_BROADCAST_OFF,
|
||||
CLOCK_EVT_NOTIFY_BROADCAST_FORCE,
|
||||
CLOCK_EVT_NOTIFY_BROADCAST_ENTER,
|
||||
CLOCK_EVT_NOTIFY_BROADCAST_EXIT,
|
||||
CLOCK_EVT_NOTIFY_SUSPEND,
|
||||
CLOCK_EVT_NOTIFY_RESUME,
|
||||
CLOCK_EVT_NOTIFY_CPU_DYING,
|
||||
CLOCK_EVT_NOTIFY_CPU_DEAD,
|
||||
};
|
||||
|
||||
/*
|
||||
* Clock event features
|
||||
*/
|
||||
|
@ -173,7 +173,7 @@ extern int tick_receive_broadcast(void);
|
|||
#ifdef CONFIG_GENERIC_CLOCKEVENTS
|
||||
extern void clockevents_notify(unsigned long reason, void *arg);
|
||||
#else
|
||||
# define clockevents_notify(reason, arg) do { } while (0)
|
||||
static inline void clockevents_notify(unsigned long reason, void *arg) {}
|
||||
#endif
|
||||
|
||||
#else /* CONFIG_GENERIC_CLOCKEVENTS_BUILD */
|
||||
|
@ -181,7 +181,7 @@ extern void clockevents_notify(unsigned long reason, void *arg);
|
|||
static inline void clockevents_suspend(void) {}
|
||||
static inline void clockevents_resume(void) {}
|
||||
|
||||
#define clockevents_notify(reason, arg) do { } while (0)
|
||||
static inline void clockevents_notify(unsigned long reason, void *arg) {}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -57,6 +57,7 @@ struct cpuidle_state {
|
|||
/* Idle State Flags */
|
||||
#define CPUIDLE_FLAG_TIME_VALID (0x01) /* is residency time measurable? */
|
||||
#define CPUIDLE_FLAG_COUPLED (0x02) /* state applies to multiple cpus */
|
||||
#define CPUIDLE_FLAG_TIMER_STOP (0x04) /* timer is stopped on this state */
|
||||
|
||||
#define CPUIDLE_DRIVER_FLAGS_MASK (0xFFFF0000)
|
||||
|
||||
|
@ -104,8 +105,8 @@ struct cpuidle_driver {
|
|||
struct module *owner;
|
||||
int refcnt;
|
||||
|
||||
/* set to 1 to use the core cpuidle time keeping (for all states). */
|
||||
unsigned int en_core_tk_irqen:1;
|
||||
/* used by the cpuidle framework to setup the broadcast timer */
|
||||
unsigned int bctimer:1;
|
||||
/* states array must be ordered in decreasing power consumption */
|
||||
struct cpuidle_state states[CPUIDLE_STATE_MAX];
|
||||
int state_count;
|
||||
|
@ -122,17 +123,15 @@ extern void cpuidle_driver_unref(void);
|
|||
extern void cpuidle_unregister_driver(struct cpuidle_driver *drv);
|
||||
extern int cpuidle_register_device(struct cpuidle_device *dev);
|
||||
extern void cpuidle_unregister_device(struct cpuidle_device *dev);
|
||||
|
||||
extern int cpuidle_register(struct cpuidle_driver *drv,
|
||||
const struct cpumask *const coupled_cpus);
|
||||
extern void cpuidle_unregister(struct cpuidle_driver *drv);
|
||||
extern void cpuidle_pause_and_lock(void);
|
||||
extern void cpuidle_resume_and_unlock(void);
|
||||
extern void cpuidle_pause(void);
|
||||
extern void cpuidle_resume(void);
|
||||
extern int cpuidle_enable_device(struct cpuidle_device *dev);
|
||||
extern void cpuidle_disable_device(struct cpuidle_device *dev);
|
||||
extern int cpuidle_wrap_enter(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int index,
|
||||
int (*enter)(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int index));
|
||||
extern int cpuidle_play_dead(void);
|
||||
|
||||
extern struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev);
|
||||
|
@ -151,7 +150,10 @@ static inline void cpuidle_unregister_driver(struct cpuidle_driver *drv) { }
|
|||
static inline int cpuidle_register_device(struct cpuidle_device *dev)
|
||||
{return -ENODEV; }
|
||||
static inline void cpuidle_unregister_device(struct cpuidle_device *dev) { }
|
||||
|
||||
static inline int cpuidle_register(struct cpuidle_driver *drv,
|
||||
const struct cpumask *const coupled_cpus)
|
||||
{return -ENODEV; }
|
||||
static inline void cpuidle_unregister(struct cpuidle_driver *drv) { }
|
||||
static inline void cpuidle_pause_and_lock(void) { }
|
||||
static inline void cpuidle_resume_and_unlock(void) { }
|
||||
static inline void cpuidle_pause(void) { }
|
||||
|
@ -159,11 +161,6 @@ static inline void cpuidle_resume(void) { }
|
|||
static inline int cpuidle_enable_device(struct cpuidle_device *dev)
|
||||
{return -ENODEV; }
|
||||
static inline void cpuidle_disable_device(struct cpuidle_device *dev) { }
|
||||
static inline int cpuidle_wrap_enter(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int index,
|
||||
int (*enter)(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int index))
|
||||
{ return -ENODEV; }
|
||||
static inline int cpuidle_play_dead(void) {return -ENODEV; }
|
||||
#endif
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue