mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 02:39:48 +00:00
![Ai Li](/assets/img/avatar_default.png)
On some SoC chips, HW resources may be in use during any particular idle period. As a consequence, the cpuidle states that the SoC is safe to enter can change from idle period to idle period. In addition, the latency and threshold of each cpuidle state can vary, depending on the operating condition when the CPU becomes idle, e.g. the current cpu frequency, the current state of the HW blocks, etc. cpuidle core and the menu governor, in the current form, are geared towards cpuidle states that are static, i.e. the availabiltiy of the states, their latencies, their thresholds are non-changing during run time. cpuidle does not provide any hook that cpuidle drivers can use to adjust those values on the fly for the current idle period before the menu governor selects the target cpuidle state. This patch extends cpuidle core and the menu governor to handle states that are dynamic. There are three additions in the patch and the patch maintains backwards-compatibility with existing cpuidle drivers. 1) add prepare() to struct cpuidle_device. A cpuidle driver can hook into the callback and cpuidle will call prepare() before calling the governor's select function. The callback gives the cpuidle driver a chance to update the dynamic information of the cpuidle states for the current idle period, e.g. state availability, latencies, thresholds, power values, etc. 2) add CPUIDLE_FLAG_IGNORE as one of the state flags. In the prepare() function, a cpuidle driver can set/clear the flag to indicate to the menu governor whether a cpuidle state should be ignored, i.e. not available, during the current idle period. 3) add power_specified bit to struct cpuidle_device. The menu governor currently assumes that the cpuidle states are arranged in the order of increasing latency, threshold, and power savings. This is true or can be made true for static states. Once the state parameters are dynamic, the latencies, thresholds, and power savings for the cpuidle states can increase or decrease by different amounts from idle period to idle period. So the assumption of increasing latency, threshold, and power savings from Cn to C(n+1) can no longer be guaranteed. It can be straightforward to calculate the power consumption of each available state and to specify it in power_usage for the idle period. Using the power_usage fields, the menu governor then selects the state that has the lowest power consumption and that still satisfies all other critieria. The power_specified bit defaults to 0. For existing cpuidle drivers, cpuidle detects that power_specified is 0 and fills in a dummy set of power_usage values. Signed-off-by: Ai Li <aili@codeaurora.org> Cc: Len Brown <len.brown@intel.com> Acked-by: Arjan van de Ven <arjan@linux.intel.com> Cc: Ingo Molnar <mingo@elte.hu> Cc: Venkatesh Pallipadi <venki@google.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
197 lines
5.3 KiB
C
197 lines
5.3 KiB
C
/*
|
|
* cpuidle.h - a generic framework for CPU idle power management
|
|
*
|
|
* (C) 2007 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
|
|
* Shaohua Li <shaohua.li@intel.com>
|
|
* Adam Belay <abelay@novell.com>
|
|
*
|
|
* This code is licenced under the GPL.
|
|
*/
|
|
|
|
#ifndef _LINUX_CPUIDLE_H
|
|
#define _LINUX_CPUIDLE_H
|
|
|
|
#include <linux/percpu.h>
|
|
#include <linux/list.h>
|
|
#include <linux/module.h>
|
|
#include <linux/kobject.h>
|
|
#include <linux/completion.h>
|
|
|
|
#define CPUIDLE_STATE_MAX 8
|
|
#define CPUIDLE_NAME_LEN 16
|
|
#define CPUIDLE_DESC_LEN 32
|
|
|
|
struct cpuidle_device;
|
|
|
|
|
|
/****************************
|
|
* CPUIDLE DEVICE INTERFACE *
|
|
****************************/
|
|
|
|
struct cpuidle_state {
|
|
char name[CPUIDLE_NAME_LEN];
|
|
char desc[CPUIDLE_DESC_LEN];
|
|
void *driver_data;
|
|
|
|
unsigned int flags;
|
|
unsigned int exit_latency; /* in US */
|
|
unsigned int power_usage; /* in mW */
|
|
unsigned int target_residency; /* in US */
|
|
|
|
unsigned long long usage;
|
|
unsigned long long time; /* in US */
|
|
|
|
int (*enter) (struct cpuidle_device *dev,
|
|
struct cpuidle_state *state);
|
|
};
|
|
|
|
/* Idle State Flags */
|
|
#define CPUIDLE_FLAG_TIME_VALID (0x01) /* is residency time measurable? */
|
|
#define CPUIDLE_FLAG_CHECK_BM (0x02) /* BM activity will exit state */
|
|
#define CPUIDLE_FLAG_POLL (0x10) /* no latency, no savings */
|
|
#define CPUIDLE_FLAG_SHALLOW (0x20) /* low latency, minimal savings */
|
|
#define CPUIDLE_FLAG_BALANCED (0x40) /* medium latency, moderate savings */
|
|
#define CPUIDLE_FLAG_DEEP (0x80) /* high latency, large savings */
|
|
#define CPUIDLE_FLAG_IGNORE (0x100) /* ignore during this idle period */
|
|
|
|
#define CPUIDLE_DRIVER_FLAGS_MASK (0xFFFF0000)
|
|
|
|
/**
|
|
* cpuidle_get_statedata - retrieves private driver state data
|
|
* @state: the state
|
|
*/
|
|
static inline void * cpuidle_get_statedata(struct cpuidle_state *state)
|
|
{
|
|
return state->driver_data;
|
|
}
|
|
|
|
/**
|
|
* cpuidle_set_statedata - stores private driver state data
|
|
* @state: the state
|
|
* @data: the private data
|
|
*/
|
|
static inline void
|
|
cpuidle_set_statedata(struct cpuidle_state *state, void *data)
|
|
{
|
|
state->driver_data = data;
|
|
}
|
|
|
|
struct cpuidle_state_kobj {
|
|
struct cpuidle_state *state;
|
|
struct completion kobj_unregister;
|
|
struct kobject kobj;
|
|
};
|
|
|
|
struct cpuidle_device {
|
|
unsigned int registered:1;
|
|
unsigned int enabled:1;
|
|
unsigned int power_specified:1;
|
|
unsigned int cpu;
|
|
|
|
int last_residency;
|
|
int state_count;
|
|
struct cpuidle_state states[CPUIDLE_STATE_MAX];
|
|
struct cpuidle_state_kobj *kobjs[CPUIDLE_STATE_MAX];
|
|
struct cpuidle_state *last_state;
|
|
|
|
struct list_head device_list;
|
|
struct kobject kobj;
|
|
struct completion kobj_unregister;
|
|
void *governor_data;
|
|
struct cpuidle_state *safe_state;
|
|
|
|
int (*prepare) (struct cpuidle_device *dev);
|
|
};
|
|
|
|
DECLARE_PER_CPU(struct cpuidle_device *, cpuidle_devices);
|
|
|
|
/**
|
|
* cpuidle_get_last_residency - retrieves the last state's residency time
|
|
* @dev: the target CPU
|
|
*
|
|
* NOTE: this value is invalid if CPUIDLE_FLAG_TIME_VALID isn't set
|
|
*/
|
|
static inline int cpuidle_get_last_residency(struct cpuidle_device *dev)
|
|
{
|
|
return dev->last_residency;
|
|
}
|
|
|
|
|
|
/****************************
|
|
* CPUIDLE DRIVER INTERFACE *
|
|
****************************/
|
|
|
|
struct cpuidle_driver {
|
|
char name[CPUIDLE_NAME_LEN];
|
|
struct module *owner;
|
|
};
|
|
|
|
#ifdef CONFIG_CPU_IDLE
|
|
|
|
extern int cpuidle_register_driver(struct cpuidle_driver *drv);
|
|
struct cpuidle_driver *cpuidle_get_driver(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 void cpuidle_pause_and_lock(void);
|
|
extern void cpuidle_resume_and_unlock(void);
|
|
extern int cpuidle_enable_device(struct cpuidle_device *dev);
|
|
extern void cpuidle_disable_device(struct cpuidle_device *dev);
|
|
|
|
#else
|
|
|
|
static inline int cpuidle_register_driver(struct cpuidle_driver *drv)
|
|
{return -ENODEV; }
|
|
static inline struct cpuidle_driver *cpuidle_get_driver(void) {return NULL; }
|
|
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 void cpuidle_pause_and_lock(void) { }
|
|
static inline void cpuidle_resume_and_unlock(void) { }
|
|
static inline int cpuidle_enable_device(struct cpuidle_device *dev)
|
|
{return -ENODEV; }
|
|
static inline void cpuidle_disable_device(struct cpuidle_device *dev) { }
|
|
|
|
#endif
|
|
|
|
/******************************
|
|
* CPUIDLE GOVERNOR INTERFACE *
|
|
******************************/
|
|
|
|
struct cpuidle_governor {
|
|
char name[CPUIDLE_NAME_LEN];
|
|
struct list_head governor_list;
|
|
unsigned int rating;
|
|
|
|
int (*enable) (struct cpuidle_device *dev);
|
|
void (*disable) (struct cpuidle_device *dev);
|
|
|
|
int (*select) (struct cpuidle_device *dev);
|
|
void (*reflect) (struct cpuidle_device *dev);
|
|
|
|
struct module *owner;
|
|
};
|
|
|
|
#ifdef CONFIG_CPU_IDLE
|
|
|
|
extern int cpuidle_register_governor(struct cpuidle_governor *gov);
|
|
extern void cpuidle_unregister_governor(struct cpuidle_governor *gov);
|
|
|
|
#else
|
|
|
|
static inline int cpuidle_register_governor(struct cpuidle_governor *gov)
|
|
{return 0;}
|
|
static inline void cpuidle_unregister_governor(struct cpuidle_governor *gov) { }
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_ARCH_HAS_CPU_RELAX
|
|
#define CPUIDLE_DRIVER_STATE_START 1
|
|
#else
|
|
#define CPUIDLE_DRIVER_STATE_START 0
|
|
#endif
|
|
|
|
#endif /* _LINUX_CPUIDLE_H */
|