power_mgmt: Add device power management support
Added device power management hook infrastructure. Added DEVICE_INIT_PM and SYS_INIT_PM macros that creates device structures with the supplied device_ops structure containing the hooks. Added example support in gpio_dw driver. Updated the sample app and tested using LPS and Device Suspend Only policies. Change-Id: I2fe347f8d8fd1041d8318e02738990deb8c5d68e Signed-off-by: Ramesh Thomas <ramesh.thomas@intel.com> Signed-off-by: Anas Nashif <anas.nashif@intel.com>
This commit is contained in:
parent
bb19e6f82f
commit
4104bbfb08
15 changed files with 276 additions and 144 deletions
|
@ -284,20 +284,6 @@ static int gpio_sam3_disable_callback(struct device *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_sam3_suspend_port(struct device *dev)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static int gpio_sam3_resume_port(struct device *dev)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static struct gpio_driver_api gpio_sam3_drv_api_funcs = {
|
||||
.config = gpio_sam3_config,
|
||||
.write = gpio_sam3_write,
|
||||
|
@ -305,8 +291,6 @@ static struct gpio_driver_api gpio_sam3_drv_api_funcs = {
|
|||
.set_callback = gpio_sam3_set_callback,
|
||||
.enable_callback = gpio_sam3_enable_callback,
|
||||
.disable_callback = gpio_sam3_disable_callback,
|
||||
.suspend = gpio_sam3_suspend_port,
|
||||
.resume = gpio_sam3_resume_port,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -34,6 +34,10 @@
|
|||
#include <drivers/ioapic.h>
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
||||
#include <power.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* ARC architecture configure IP through IO auxiliary registers.
|
||||
* Other architectures as ARM and x86 configure IP through MMIO registers
|
||||
|
@ -286,19 +290,21 @@ static inline int gpio_dw_disable_callback(struct device *port, int access_op,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline int gpio_dw_suspend_port(struct device *port)
|
||||
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
||||
static inline int gpio_dw_suspend_port(struct device *port, int pm_policy)
|
||||
{
|
||||
_gpio_dw_clock_off(port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int gpio_dw_resume_port(struct device *port)
|
||||
static inline int gpio_dw_resume_port(struct device *port, int pm_policy)
|
||||
{
|
||||
_gpio_dw_clock_on(port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SOC_QUARK_SE
|
||||
static inline void gpio_dw_unmask_int(uint32_t mask_addr)
|
||||
|
@ -362,8 +368,6 @@ static struct gpio_driver_api api_funcs = {
|
|||
.set_callback = gpio_dw_set_callback,
|
||||
.enable_callback = gpio_dw_enable_callback,
|
||||
.disable_callback = gpio_dw_disable_callback,
|
||||
.suspend = gpio_dw_suspend_port,
|
||||
.resume = gpio_dw_resume_port
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
|
@ -456,9 +460,20 @@ struct gpio_dw_config gpio_config_0 = {
|
|||
|
||||
struct gpio_dw_runtime gpio_0_runtime;
|
||||
|
||||
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
||||
struct device_pm_ops gpio_dev_pm_ops = {
|
||||
.suspend = gpio_dw_suspend_port,
|
||||
.resume = gpio_dw_resume_port
|
||||
};
|
||||
|
||||
DEVICE_INIT_PM(gpio_dw_0, CONFIG_GPIO_DW_0_NAME, gpio_dw_initialize,
|
||||
&gpio_dev_pm_ops, &gpio_0_runtime, &gpio_config_0,
|
||||
SECONDARY, CONFIG_GPIO_DW_INIT_PRIORITY);
|
||||
#else
|
||||
DEVICE_INIT(gpio_dw_0, CONFIG_GPIO_DW_0_NAME, gpio_dw_initialize,
|
||||
&gpio_0_runtime, &gpio_config_0,
|
||||
SECONDARY, CONFIG_GPIO_DW_INIT_PRIORITY);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_GPIO_DW_0_IRQ_DIRECT
|
||||
#ifdef CONFIG_IOAPIC
|
||||
|
@ -531,9 +546,15 @@ struct gpio_dw_config gpio_dw_config_1 = {
|
|||
|
||||
struct gpio_dw_runtime gpio_1_runtime;
|
||||
|
||||
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
||||
DEVICE_INIT_PM(gpio_dw_1, CONFIG_GPIO_DW_1_NAME, gpio_dw_initialize,
|
||||
&gpio_dev_pm_ops, &gpio_1_runtime, &gpio_dw_config_1,
|
||||
SECONDARY, CONFIG_GPIO_DW_INIT_PRIORITY);
|
||||
#else
|
||||
DEVICE_INIT(gpio_dw_1, CONFIG_GPIO_DW_1_NAME, gpio_dw_initialize,
|
||||
&gpio_1_runtime, &gpio_dw_config_1,
|
||||
SECONDARY, CONFIG_GPIO_DW_INIT_PRIORITY);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_GPIO_DW_1_IRQ_DIRECT
|
||||
#ifdef CONFIG_IOAPIC
|
||||
|
|
|
@ -221,22 +221,6 @@ static int gpio_k64_disable_callback(struct device *dev,
|
|||
}
|
||||
|
||||
|
||||
static int gpio_k64_suspend_port(struct device *dev)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
|
||||
static int gpio_k64_resume_port(struct device *dev)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Handler for port interrupts
|
||||
* @param dev Pointer to device structure for driver instance
|
||||
|
@ -290,8 +274,6 @@ static struct gpio_driver_api gpio_k64_drv_api_funcs = {
|
|||
.set_callback = gpio_k64_set_callback,
|
||||
.enable_callback = gpio_k64_enable_callback,
|
||||
.disable_callback = gpio_k64_disable_callback,
|
||||
.suspend = gpio_k64_suspend_port,
|
||||
.resume = gpio_k64_resume_port,
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -277,20 +277,6 @@ static int gpio_mmio_disable_callback(struct device *dev,
|
|||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static int gpio_mmio_suspend_port(struct device *dev)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static int gpio_mmio_resume_port(struct device *dev)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static struct gpio_driver_api gpio_mmio_drv_api_funcs = {
|
||||
.config = gpio_mmio_config,
|
||||
.write = gpio_mmio_write,
|
||||
|
@ -298,8 +284,6 @@ static struct gpio_driver_api gpio_mmio_drv_api_funcs = {
|
|||
.set_callback = gpio_mmio_set_callback,
|
||||
.enable_callback = gpio_mmio_enable_callback,
|
||||
.disable_callback = gpio_mmio_disable_callback,
|
||||
.suspend = gpio_mmio_suspend_port,
|
||||
.resume = gpio_mmio_resume_port,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -544,24 +544,6 @@ static int gpio_pcal9535a_disable_callback(struct device *dev,
|
|||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static int gpio_pcal9535a_suspend_port(struct device *dev)
|
||||
{
|
||||
if (!_has_i2c_master(dev)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static int gpio_pcal9535a_resume_port(struct device *dev)
|
||||
{
|
||||
if (!_has_i2c_master(dev)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static struct gpio_driver_api gpio_pcal9535a_drv_api_funcs = {
|
||||
.config = gpio_pcal9535a_config,
|
||||
.write = gpio_pcal9535a_write,
|
||||
|
@ -569,8 +551,6 @@ static struct gpio_driver_api gpio_pcal9535a_drv_api_funcs = {
|
|||
.set_callback = gpio_pcal9535a_set_callback,
|
||||
.enable_callback = gpio_pcal9535a_enable_callback,
|
||||
.disable_callback = gpio_pcal9535a_disable_callback,
|
||||
.suspend = gpio_pcal9535a_suspend_port,
|
||||
.resume = gpio_pcal9535a_resume_port,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -271,16 +271,6 @@ static inline int gpio_qmsi_disable_callback(struct device *port,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline int gpio_qmsi_suspend_port(struct device *port)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int gpio_qmsi_resume_port(struct device *port)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static struct gpio_driver_api api_funcs = {
|
||||
.config = gpio_qmsi_config,
|
||||
.write = gpio_qmsi_write,
|
||||
|
@ -288,8 +278,6 @@ static struct gpio_driver_api api_funcs = {
|
|||
.set_callback = gpio_qmsi_set_callback,
|
||||
.enable_callback = gpio_qmsi_enable_callback,
|
||||
.disable_callback = gpio_qmsi_disable_callback,
|
||||
.suspend = gpio_qmsi_suspend_port,
|
||||
.resume = gpio_qmsi_resume_port
|
||||
};
|
||||
|
||||
int gpio_qmsi_init(struct device *port)
|
||||
|
|
|
@ -316,16 +316,6 @@ static int gpio_sch_disable_callback(struct device *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_sch_suspend(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_sch_resume(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct gpio_driver_api gpio_sch_api = {
|
||||
.config = gpio_sch_config,
|
||||
.write = gpio_sch_write,
|
||||
|
@ -333,8 +323,6 @@ static struct gpio_driver_api gpio_sch_api = {
|
|||
.set_callback = gpio_sch_set_callback,
|
||||
.enable_callback = gpio_sch_enable_callback,
|
||||
.disable_callback = gpio_sch_disable_callback,
|
||||
.suspend = gpio_sch_suspend,
|
||||
.resume = gpio_sch_resume
|
||||
};
|
||||
|
||||
int gpio_sch_init(struct device *dev)
|
||||
|
|
|
@ -178,20 +178,6 @@ static int gpio_stm32_disable_callback(struct device *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_stm32_suspend_port(struct device *dev)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static int gpio_stm32_resume_port(struct device *dev)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static struct gpio_driver_api gpio_stm32_driver = {
|
||||
.config = gpio_stm32_config,
|
||||
.write = gpio_stm32_write,
|
||||
|
@ -199,8 +185,6 @@ static struct gpio_driver_api gpio_stm32_driver = {
|
|||
.set_callback = gpio_stm32_set_callback,
|
||||
.enable_callback = gpio_stm32_enable_callback,
|
||||
.disable_callback = gpio_stm32_disable_callback,
|
||||
.suspend = gpio_stm32_suspend_port,
|
||||
.resume = gpio_stm32_resume_port,
|
||||
|
||||
};
|
||||
|
||||
|
|
161
include/device.h
161
include/device.h
|
@ -87,6 +87,7 @@ extern "C" {
|
|||
* (e.g. CONFIG_KERNEL_INIT_PRIORITY_DEFAULT + 5).
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_DEVICE_POWER_MANAGEMENT
|
||||
#define DEVICE_INIT(dev_name, drv_name, init_fn, data, cfg_info, level, prio) \
|
||||
\
|
||||
static struct device_config __config_##dev_name __used \
|
||||
|
@ -100,6 +101,88 @@ extern "C" {
|
|||
.config = &(__config_##dev_name), \
|
||||
.driver_data = data \
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* @def DEVICE_INIT_PM
|
||||
*
|
||||
* @brief create device object and set it up for boot time initialization
|
||||
*
|
||||
* @details This macro defines a device object that is automatically
|
||||
* configured by the kernel during system initialization.
|
||||
*
|
||||
* @param dev_name Device name.
|
||||
*
|
||||
* @param drv_name The name this instance of the driver exposes to
|
||||
* the system.
|
||||
*
|
||||
* @param init_fn Address to the init function of the driver.
|
||||
*
|
||||
* @param device_pm_ops Address to the device_pm_ops structure of the driver.
|
||||
*
|
||||
* @param data Pointer to the device's configuration data.
|
||||
*
|
||||
* @param cfg_info The address to the structure containing the
|
||||
* configuration information for this instance of the driver.
|
||||
*
|
||||
* @param level The initialization level at which configuration occurs.
|
||||
* Must be one of the following symbols, which are listed in the order
|
||||
* they are performed by the kernel:
|
||||
*
|
||||
* PRIMARY: Used for devices that have no dependencies, such as those
|
||||
* that rely solely on hardware present in the processor/SOC. These devices
|
||||
* cannot use any kernel services during configuration, since they are not
|
||||
* yet available.
|
||||
*
|
||||
* SECONDARY: Used for devices that rely on the initialization of devices
|
||||
* initialized as part of the PRIMARY level. These devices cannot use any
|
||||
* kernel services during configuration, since they are not yet available.
|
||||
*
|
||||
* NANOKERNEL: Used for devices that require nanokernel services during
|
||||
* configuration.
|
||||
*
|
||||
* MICROKERNEL: Used for devices that require microkernel services during
|
||||
* configuration.
|
||||
*
|
||||
* APPLICATION: Used for application components (i.e. non-kernel components)
|
||||
* that need automatic configuration. These devices can use all services
|
||||
* provided by the kernel during configuration.
|
||||
*
|
||||
* @param prio The initialization priority of the device, relative to
|
||||
* other devices of the same initialization level. Specified as an integer
|
||||
* value in the range 0 to 99; lower values indicate earlier initialization.
|
||||
* Must be a decimal integer literal without leading zeroes or sign (e.g. 32),
|
||||
* or an equivalent symbolic name (e.g. \#define MY_INIT_PRIO 32); symbolic
|
||||
* expressions are *not* permitted
|
||||
* (e.g. CONFIG_KERNEL_INIT_PRIORITY_DEFAULT + 5).
|
||||
*/
|
||||
|
||||
#define DEVICE_INIT_PM(dev_name, drv_name, init_fn, device_pm_ops, \
|
||||
data, cfg_info, level, prio) \
|
||||
\
|
||||
static struct device_config __config_##dev_name __used \
|
||||
__attribute__((__section__(".devconfig.init"))) = { \
|
||||
.name = drv_name, .init = (init_fn), \
|
||||
.dev_pm_ops = (device_pm_ops), \
|
||||
.config_info = (cfg_info) \
|
||||
}; \
|
||||
\
|
||||
static struct device (__device_##dev_name) __used \
|
||||
__attribute__((__section__(".init_" #level STRINGIFY(prio)))) = { \
|
||||
.config = &(__config_##dev_name), \
|
||||
.driver_data = data \
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a default device_pm_ops for devices that do not call the
|
||||
* DEVICE_INIT_PM macro so that caller of hook functions
|
||||
* need not check dev_pm_ops != NULL.
|
||||
*/
|
||||
extern struct device_pm_ops device_pm_ops_nop;
|
||||
#define DEVICE_INIT(dev_name, drv_name, init_fn, data, cfg_info, level, prio) \
|
||||
DEVICE_INIT_PM(dev_name, drv_name, init_fn, \
|
||||
&device_pm_ops_nop, data, cfg_info, \
|
||||
level, prio)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def DEVICE_NAME_GET
|
||||
|
@ -165,6 +248,13 @@ extern "C" {
|
|||
|
||||
struct device;
|
||||
|
||||
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
||||
struct device_pm_ops {
|
||||
int (*suspend)(struct device *device, int pm_policy);
|
||||
int (*resume)(struct device *device, int pm_policy);
|
||||
};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Static device information (In ROM) Per driver instance
|
||||
* @param name name of the device
|
||||
|
@ -174,6 +264,9 @@ struct device;
|
|||
struct device_config {
|
||||
char *name;
|
||||
int (*init)(struct device *device);
|
||||
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
||||
struct device_pm_ops *dev_pm_ops;
|
||||
#endif
|
||||
void *config_info;
|
||||
};
|
||||
|
||||
|
@ -193,6 +286,74 @@ struct device {
|
|||
void _sys_device_do_config_level(int level);
|
||||
struct device* device_get_binding(char *name);
|
||||
|
||||
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
||||
/**
|
||||
* Device PM functions
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief No-op function to initialize unimplemented pm hooks
|
||||
*
|
||||
* This function should be used to initialize device pm hooks
|
||||
* for which a device has no operation.
|
||||
*
|
||||
* @param unused_device
|
||||
* @param unused_policy
|
||||
*
|
||||
* @retval Always returns 0
|
||||
*/
|
||||
int device_pm_nop(struct device *unused_device, int unused_policy);
|
||||
|
||||
/**
|
||||
* @brief Call the suspend function of a device
|
||||
*
|
||||
* Called by the Power Manager application to let the device do
|
||||
* any policy based PM suspend operations.
|
||||
*
|
||||
* @param device Pointer to device structure of the driver instance.
|
||||
* @param pm_policy PM policy for which this call is made.
|
||||
*
|
||||
* @retval 0 If successful.
|
||||
* @retval -EBUSY If device is busy
|
||||
* @retval Other negative errno code if failure.
|
||||
*/
|
||||
static inline int device_suspend(struct device *device, int pm_policy)
|
||||
{
|
||||
return device->config->dev_pm_ops->suspend(device, pm_policy);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Call the resume function of a device
|
||||
*
|
||||
* Called by the Power Manager application to let the device do
|
||||
* any policy based PM resume operations.
|
||||
*
|
||||
* @param device Pointer to device structure of the driver instance.
|
||||
* @param pm_policy PM policy for which this call is made.
|
||||
*
|
||||
* @retval 0 If successful.
|
||||
* @retval Negative errno code if failure.
|
||||
*/
|
||||
static inline int device_resume(struct device *device, int pm_policy)
|
||||
{
|
||||
return device->config->dev_pm_ops->resume(device, pm_policy);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the device structure list array and device count
|
||||
*
|
||||
* Called by the Power Manager application to get the list of
|
||||
* device structures associated with the devices in the system.
|
||||
* The PM app would use this list to create its own sorted list
|
||||
* based on the order it wishes to suspend or resume the devices.
|
||||
*
|
||||
* @param device_list Pointer to receive the device list array
|
||||
* @param device_count Pointer to receive the device count
|
||||
*/
|
||||
void device_list_get(struct device **device_list, int *device_count);
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Synchronous calls API
|
||||
*/
|
||||
|
|
|
@ -150,8 +150,6 @@ typedef int (*gpio_enable_callback_t)(struct device *port,
|
|||
typedef int (*gpio_disable_callback_t)(struct device *port,
|
||||
int access_op,
|
||||
uint32_t pin);
|
||||
typedef int (*gpio_suspend_port_t)(struct device *port);
|
||||
typedef int (*gpio_resume_port_t)(struct device *port);
|
||||
|
||||
struct gpio_driver_api {
|
||||
gpio_config_t config;
|
||||
|
@ -160,8 +158,6 @@ struct gpio_driver_api {
|
|||
gpio_set_callback_t set_callback;
|
||||
gpio_enable_callback_t enable_callback;
|
||||
gpio_disable_callback_t disable_callback;
|
||||
gpio_suspend_port_t suspend;
|
||||
gpio_resume_port_t resume;
|
||||
};
|
||||
/**
|
||||
* @endcond
|
||||
|
@ -325,31 +321,6 @@ static inline int gpio_port_disable_callback(struct device *port)
|
|||
return api->disable_callback(port, GPIO_ACCESS_BY_PORT, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Save the state of the device and make it go to the
|
||||
* low power state.
|
||||
* @param port Pointer to the device structure for the driver instance.
|
||||
*/
|
||||
static inline int gpio_suspend(struct device *port)
|
||||
{
|
||||
struct gpio_driver_api *api;
|
||||
|
||||
api = (struct gpio_driver_api *) port->driver_api;
|
||||
return api->suspend(port);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Restore the state stored during suspend and resume operation.
|
||||
* @param port Pointer to the device structure for the driver instance.
|
||||
*/
|
||||
static inline int gpio_resume(struct device *port)
|
||||
{
|
||||
struct gpio_driver_api *api;
|
||||
|
||||
api = (struct gpio_driver_api *) port->driver_api;
|
||||
return api->resume(port);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -41,6 +41,12 @@ extern "C" {
|
|||
#define SYS_INIT(init_fn, level, prio) \
|
||||
DEVICE_INIT(sys_init_##init_fn, "", init_fn, NULL, NULL, level, prio)
|
||||
|
||||
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
||||
#define SYS_INIT_PM(drv_name, init_fn, device_pm_ops, level, prio) \
|
||||
DEVICE_INIT_PM(sys_init_##init_fn, drv_name, init_fn, device_pm_ops, \
|
||||
NULL, NULL, level, prio)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -35,6 +35,10 @@ static struct device *config_levels[] = {
|
|||
__device_init_end,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
||||
struct device_pm_ops device_pm_ops_nop = {device_pm_nop, device_pm_nop};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Execute all the device initialization functions at a given level
|
||||
*
|
||||
|
@ -81,3 +85,17 @@ struct device *device_get_binding(char *name)
|
|||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
||||
int device_pm_nop(struct device *unused_device, int unused_policy)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void device_list_get(struct device **device_list, int *device_count)
|
||||
{
|
||||
|
||||
*device_list = __device_init_start;
|
||||
*device_count = __device_init_end - __device_init_start;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
CONFIG_STDOUT_CONSOLE=y
|
||||
CONFIG_SYS_POWER_MANAGEMENT=y
|
||||
CONFIG_SYS_POWER_LOW_POWER_STATE=y
|
||||
CONFIG_DEVICE_POWER_MANAGEMENT=y
|
||||
CONFIG_TICKLESS_IDLE=y
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
CONFIG_SYS_POWER_MANAGEMENT=y
|
||||
CONFIG_SYS_POWER_LOW_POWER_STATE=y
|
||||
CONFIG_DEVICE_POWER_MANAGEMENT=y
|
||||
CONFIG_TICKLESS_IDLE=y
|
||||
CONFIG_RTC=y
|
||||
CONFIG_GPIO=y
|
||||
CONFIG_STDOUT_CONSOLE=y
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <misc/printk.h>
|
||||
#define PRINT printk
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <rtc.h>
|
||||
|
||||
#define SLEEPTICKS SECONDS(5)
|
||||
|
@ -33,6 +34,15 @@ static int pm_state; /* 1 = LPS; 2 = Device suspend only */
|
|||
static void quark_low_power(void);
|
||||
static struct device *rtc_dev;
|
||||
static uint32_t start_time, end_time;
|
||||
static void create_device_list(void);
|
||||
static void suspend_devices(int pm_policy);
|
||||
static void resume_devices(int pm_policy);
|
||||
|
||||
#define DEVICE_POLICY_MAX 10
|
||||
static struct device *device_list;
|
||||
static int device_count;
|
||||
static char device_policy_list[DEVICE_POLICY_MAX];
|
||||
static char device_retval[DEVICE_POLICY_MAX];
|
||||
|
||||
void main(void)
|
||||
{
|
||||
|
@ -49,6 +59,8 @@ void main(void)
|
|||
rtc_enable(rtc_dev);
|
||||
rtc_set_config(rtc_dev, &config);
|
||||
|
||||
create_device_list();
|
||||
|
||||
while (1) {
|
||||
task_sleep(SLEEPTICKS);
|
||||
}
|
||||
|
@ -79,6 +91,7 @@ static int low_power_state_entry(int32_t ticks)
|
|||
PRINT("\n\nLow power state policy entry!\n");
|
||||
|
||||
/* Turn off peripherals/clocks here */
|
||||
suspend_devices(SYS_PM_LOW_POWER_STATE);
|
||||
|
||||
quark_low_power();
|
||||
|
||||
|
@ -90,6 +103,7 @@ static int device_suspend_only_entry(int32_t ticks)
|
|||
PRINT("Device suspend only policy entry!\n");
|
||||
|
||||
/* Turn off peripherals/clocks here */
|
||||
suspend_devices(SYS_PM_DEVICE_SUSPEND_ONLY);
|
||||
|
||||
return SYS_PM_DEVICE_SUSPEND_ONLY;
|
||||
}
|
||||
|
@ -120,6 +134,8 @@ int _sys_soc_suspend(int32_t ticks)
|
|||
|
||||
static void low_power_state_exit(void)
|
||||
{
|
||||
resume_devices(SYS_PM_LOW_POWER_STATE);
|
||||
|
||||
end_time = rtc_read(rtc_dev);
|
||||
PRINT("\nLow power state policy exit!\n");
|
||||
PRINT("Total Elapsed From Suspend To Resume = %d RTC Cycles\n",
|
||||
|
@ -128,6 +144,8 @@ static void low_power_state_exit(void)
|
|||
|
||||
static void device_suspend_only_exit(void)
|
||||
{
|
||||
resume_devices(SYS_PM_DEVICE_SUSPEND_ONLY);
|
||||
|
||||
end_time = rtc_read(rtc_dev);
|
||||
PRINT("\nDevice suspend only policy exit!\n");
|
||||
PRINT("Total Elapsed From Suspend To Resume = %d RTC Cycles\n",
|
||||
|
@ -164,3 +182,48 @@ static void quark_low_power(void)
|
|||
::"a"(P_LVL2));
|
||||
|
||||
}
|
||||
|
||||
static void suspend_devices(int pm_policy)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < device_count; i++) {
|
||||
int idx = device_policy_list[i];
|
||||
|
||||
device_retval[i] = device_suspend(&device_list[idx], pm_policy);
|
||||
}
|
||||
}
|
||||
|
||||
static void resume_devices(int pm_policy)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = device_count - 1; i >= 0; i--) {
|
||||
if (!device_retval[i]) {
|
||||
int idx = device_policy_list[i];
|
||||
|
||||
device_resume(&device_list[idx], pm_policy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void create_device_list(void)
|
||||
{
|
||||
int count;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Create a list of devices that will be suspended.
|
||||
* For the demo we will create a simple list containing
|
||||
* gpio devices.
|
||||
*/
|
||||
device_list_get(&device_list, &count);
|
||||
|
||||
for (i = 0; (i < count) && (device_count < DEVICE_POLICY_MAX); i++) {
|
||||
if (!strcmp(device_list[i].config->name, CONFIG_GPIO_DW_0_NAME) ||
|
||||
!strcmp(device_list[i].config->name,
|
||||
CONFIG_GPIO_DW_1_NAME)) {
|
||||
device_policy_list[device_count++] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue