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:
Ramesh Thomas 2016-03-23 23:23:10 -07:00 committed by Anas Nashif
commit 4104bbfb08
15 changed files with 276 additions and 144 deletions

View file

@ -284,20 +284,6 @@ static int gpio_sam3_disable_callback(struct device *dev,
return 0; 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 = { static struct gpio_driver_api gpio_sam3_drv_api_funcs = {
.config = gpio_sam3_config, .config = gpio_sam3_config,
.write = gpio_sam3_write, .write = gpio_sam3_write,
@ -305,8 +291,6 @@ static struct gpio_driver_api gpio_sam3_drv_api_funcs = {
.set_callback = gpio_sam3_set_callback, .set_callback = gpio_sam3_set_callback,
.enable_callback = gpio_sam3_enable_callback, .enable_callback = gpio_sam3_enable_callback,
.disable_callback = gpio_sam3_disable_callback, .disable_callback = gpio_sam3_disable_callback,
.suspend = gpio_sam3_suspend_port,
.resume = gpio_sam3_resume_port,
}; };
/** /**

View file

@ -34,6 +34,10 @@
#include <drivers/ioapic.h> #include <drivers/ioapic.h>
#endif #endif
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
#include <power.h>
#endif
/* /*
* ARC architecture configure IP through IO auxiliary registers. * ARC architecture configure IP through IO auxiliary registers.
* Other architectures as ARM and x86 configure IP through MMIO 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; 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); _gpio_dw_clock_off(port);
return 0; 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); _gpio_dw_clock_on(port);
return 0; return 0;
} }
#endif
#ifdef CONFIG_SOC_QUARK_SE #ifdef CONFIG_SOC_QUARK_SE
static inline void gpio_dw_unmask_int(uint32_t mask_addr) 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, .set_callback = gpio_dw_set_callback,
.enable_callback = gpio_dw_enable_callback, .enable_callback = gpio_dw_enable_callback,
.disable_callback = gpio_dw_disable_callback, .disable_callback = gpio_dw_disable_callback,
.suspend = gpio_dw_suspend_port,
.resume = gpio_dw_resume_port
}; };
#ifdef CONFIG_PCI #ifdef CONFIG_PCI
@ -456,9 +460,20 @@ struct gpio_dw_config gpio_config_0 = {
struct gpio_dw_runtime gpio_0_runtime; 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, DEVICE_INIT(gpio_dw_0, CONFIG_GPIO_DW_0_NAME, gpio_dw_initialize,
&gpio_0_runtime, &gpio_config_0, &gpio_0_runtime, &gpio_config_0,
SECONDARY, CONFIG_GPIO_DW_INIT_PRIORITY); SECONDARY, CONFIG_GPIO_DW_INIT_PRIORITY);
#endif
#ifdef CONFIG_GPIO_DW_0_IRQ_DIRECT #ifdef CONFIG_GPIO_DW_0_IRQ_DIRECT
#ifdef CONFIG_IOAPIC #ifdef CONFIG_IOAPIC
@ -531,9 +546,15 @@ struct gpio_dw_config gpio_dw_config_1 = {
struct gpio_dw_runtime gpio_1_runtime; 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, DEVICE_INIT(gpio_dw_1, CONFIG_GPIO_DW_1_NAME, gpio_dw_initialize,
&gpio_1_runtime, &gpio_dw_config_1, &gpio_1_runtime, &gpio_dw_config_1,
SECONDARY, CONFIG_GPIO_DW_INIT_PRIORITY); SECONDARY, CONFIG_GPIO_DW_INIT_PRIORITY);
#endif
#ifdef CONFIG_GPIO_DW_1_IRQ_DIRECT #ifdef CONFIG_GPIO_DW_1_IRQ_DIRECT
#ifdef CONFIG_IOAPIC #ifdef CONFIG_IOAPIC

View file

@ -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 * @brief Handler for port interrupts
* @param dev Pointer to device structure for driver instance * @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, .set_callback = gpio_k64_set_callback,
.enable_callback = gpio_k64_enable_callback, .enable_callback = gpio_k64_enable_callback,
.disable_callback = gpio_k64_disable_callback, .disable_callback = gpio_k64_disable_callback,
.suspend = gpio_k64_suspend_port,
.resume = gpio_k64_resume_port,
}; };

View file

@ -277,20 +277,6 @@ static int gpio_mmio_disable_callback(struct device *dev,
return -ENOTSUP; 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 = { static struct gpio_driver_api gpio_mmio_drv_api_funcs = {
.config = gpio_mmio_config, .config = gpio_mmio_config,
.write = gpio_mmio_write, .write = gpio_mmio_write,
@ -298,8 +284,6 @@ static struct gpio_driver_api gpio_mmio_drv_api_funcs = {
.set_callback = gpio_mmio_set_callback, .set_callback = gpio_mmio_set_callback,
.enable_callback = gpio_mmio_enable_callback, .enable_callback = gpio_mmio_enable_callback,
.disable_callback = gpio_mmio_disable_callback, .disable_callback = gpio_mmio_disable_callback,
.suspend = gpio_mmio_suspend_port,
.resume = gpio_mmio_resume_port,
}; };
/** /**

View file

@ -544,24 +544,6 @@ static int gpio_pcal9535a_disable_callback(struct device *dev,
return -ENOTSUP; 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 = { static struct gpio_driver_api gpio_pcal9535a_drv_api_funcs = {
.config = gpio_pcal9535a_config, .config = gpio_pcal9535a_config,
.write = gpio_pcal9535a_write, .write = gpio_pcal9535a_write,
@ -569,8 +551,6 @@ static struct gpio_driver_api gpio_pcal9535a_drv_api_funcs = {
.set_callback = gpio_pcal9535a_set_callback, .set_callback = gpio_pcal9535a_set_callback,
.enable_callback = gpio_pcal9535a_enable_callback, .enable_callback = gpio_pcal9535a_enable_callback,
.disable_callback = gpio_pcal9535a_disable_callback, .disable_callback = gpio_pcal9535a_disable_callback,
.suspend = gpio_pcal9535a_suspend_port,
.resume = gpio_pcal9535a_resume_port,
}; };
/** /**

View file

@ -271,16 +271,6 @@ static inline int gpio_qmsi_disable_callback(struct device *port,
return 0; 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 = { static struct gpio_driver_api api_funcs = {
.config = gpio_qmsi_config, .config = gpio_qmsi_config,
.write = gpio_qmsi_write, .write = gpio_qmsi_write,
@ -288,8 +278,6 @@ static struct gpio_driver_api api_funcs = {
.set_callback = gpio_qmsi_set_callback, .set_callback = gpio_qmsi_set_callback,
.enable_callback = gpio_qmsi_enable_callback, .enable_callback = gpio_qmsi_enable_callback,
.disable_callback = gpio_qmsi_disable_callback, .disable_callback = gpio_qmsi_disable_callback,
.suspend = gpio_qmsi_suspend_port,
.resume = gpio_qmsi_resume_port
}; };
int gpio_qmsi_init(struct device *port) int gpio_qmsi_init(struct device *port)

View file

@ -316,16 +316,6 @@ static int gpio_sch_disable_callback(struct device *dev,
return 0; 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 = { static struct gpio_driver_api gpio_sch_api = {
.config = gpio_sch_config, .config = gpio_sch_config,
.write = gpio_sch_write, .write = gpio_sch_write,
@ -333,8 +323,6 @@ static struct gpio_driver_api gpio_sch_api = {
.set_callback = gpio_sch_set_callback, .set_callback = gpio_sch_set_callback,
.enable_callback = gpio_sch_enable_callback, .enable_callback = gpio_sch_enable_callback,
.disable_callback = gpio_sch_disable_callback, .disable_callback = gpio_sch_disable_callback,
.suspend = gpio_sch_suspend,
.resume = gpio_sch_resume
}; };
int gpio_sch_init(struct device *dev) int gpio_sch_init(struct device *dev)

View file

@ -178,20 +178,6 @@ static int gpio_stm32_disable_callback(struct device *dev,
return 0; 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 = { static struct gpio_driver_api gpio_stm32_driver = {
.config = gpio_stm32_config, .config = gpio_stm32_config,
.write = gpio_stm32_write, .write = gpio_stm32_write,
@ -199,8 +185,6 @@ static struct gpio_driver_api gpio_stm32_driver = {
.set_callback = gpio_stm32_set_callback, .set_callback = gpio_stm32_set_callback,
.enable_callback = gpio_stm32_enable_callback, .enable_callback = gpio_stm32_enable_callback,
.disable_callback = gpio_stm32_disable_callback, .disable_callback = gpio_stm32_disable_callback,
.suspend = gpio_stm32_suspend_port,
.resume = gpio_stm32_resume_port,
}; };

View file

@ -87,6 +87,7 @@ extern "C" {
* (e.g. CONFIG_KERNEL_INIT_PRIORITY_DEFAULT + 5). * (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) \ #define DEVICE_INIT(dev_name, drv_name, init_fn, data, cfg_info, level, prio) \
\ \
static struct device_config __config_##dev_name __used \ static struct device_config __config_##dev_name __used \
@ -100,6 +101,88 @@ extern "C" {
.config = &(__config_##dev_name), \ .config = &(__config_##dev_name), \
.driver_data = data \ .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 * @def DEVICE_NAME_GET
@ -165,6 +248,13 @@ extern "C" {
struct device; 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 * @brief Static device information (In ROM) Per driver instance
* @param name name of the device * @param name name of the device
@ -174,6 +264,9 @@ struct device;
struct device_config { struct device_config {
char *name; char *name;
int (*init)(struct device *device); int (*init)(struct device *device);
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
struct device_pm_ops *dev_pm_ops;
#endif
void *config_info; void *config_info;
}; };
@ -193,6 +286,74 @@ struct device {
void _sys_device_do_config_level(int level); void _sys_device_do_config_level(int level);
struct device* device_get_binding(char *name); 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 * Synchronous calls API
*/ */

View file

@ -150,8 +150,6 @@ typedef int (*gpio_enable_callback_t)(struct device *port,
typedef int (*gpio_disable_callback_t)(struct device *port, typedef int (*gpio_disable_callback_t)(struct device *port,
int access_op, int access_op,
uint32_t pin); 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 { struct gpio_driver_api {
gpio_config_t config; gpio_config_t config;
@ -160,8 +158,6 @@ struct gpio_driver_api {
gpio_set_callback_t set_callback; gpio_set_callback_t set_callback;
gpio_enable_callback_t enable_callback; gpio_enable_callback_t enable_callback;
gpio_disable_callback_t disable_callback; gpio_disable_callback_t disable_callback;
gpio_suspend_port_t suspend;
gpio_resume_port_t resume;
}; };
/** /**
* @endcond * @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); 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 #ifdef __cplusplus
} }
#endif #endif

View file

@ -41,6 +41,12 @@ extern "C" {
#define SYS_INIT(init_fn, level, prio) \ #define SYS_INIT(init_fn, level, prio) \
DEVICE_INIT(sys_init_##init_fn, "", init_fn, NULL, NULL, 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 #ifdef __cplusplus
} }
#endif #endif

View file

@ -35,6 +35,10 @@ static struct device *config_levels[] = {
__device_init_end, __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 * @brief Execute all the device initialization functions at a given level
* *
@ -81,3 +85,17 @@ struct device *device_get_binding(char *name)
return NULL; 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

View file

@ -1,4 +1,4 @@
CONFIG_STDOUT_CONSOLE=y CONFIG_STDOUT_CONSOLE=y
CONFIG_SYS_POWER_MANAGEMENT=y CONFIG_SYS_POWER_MANAGEMENT=y
CONFIG_SYS_POWER_LOW_POWER_STATE=y CONFIG_DEVICE_POWER_MANAGEMENT=y
CONFIG_TICKLESS_IDLE=y CONFIG_TICKLESS_IDLE=y

View file

@ -1,5 +1,7 @@
CONFIG_SYS_POWER_MANAGEMENT=y CONFIG_SYS_POWER_MANAGEMENT=y
CONFIG_SYS_POWER_LOW_POWER_STATE=y CONFIG_SYS_POWER_LOW_POWER_STATE=y
CONFIG_DEVICE_POWER_MANAGEMENT=y
CONFIG_TICKLESS_IDLE=y CONFIG_TICKLESS_IDLE=y
CONFIG_RTC=y CONFIG_RTC=y
CONFIG_GPIO=y
CONFIG_STDOUT_CONSOLE=y CONFIG_STDOUT_CONSOLE=y

View file

@ -24,6 +24,7 @@
#include <misc/printk.h> #include <misc/printk.h>
#define PRINT printk #define PRINT printk
#endif #endif
#include <string.h>
#include <rtc.h> #include <rtc.h>
#define SLEEPTICKS SECONDS(5) #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 void quark_low_power(void);
static struct device *rtc_dev; static struct device *rtc_dev;
static uint32_t start_time, end_time; 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) void main(void)
{ {
@ -49,6 +59,8 @@ void main(void)
rtc_enable(rtc_dev); rtc_enable(rtc_dev);
rtc_set_config(rtc_dev, &config); rtc_set_config(rtc_dev, &config);
create_device_list();
while (1) { while (1) {
task_sleep(SLEEPTICKS); task_sleep(SLEEPTICKS);
} }
@ -79,6 +91,7 @@ static int low_power_state_entry(int32_t ticks)
PRINT("\n\nLow power state policy entry!\n"); PRINT("\n\nLow power state policy entry!\n");
/* Turn off peripherals/clocks here */ /* Turn off peripherals/clocks here */
suspend_devices(SYS_PM_LOW_POWER_STATE);
quark_low_power(); quark_low_power();
@ -90,6 +103,7 @@ static int device_suspend_only_entry(int32_t ticks)
PRINT("Device suspend only policy entry!\n"); PRINT("Device suspend only policy entry!\n");
/* Turn off peripherals/clocks here */ /* Turn off peripherals/clocks here */
suspend_devices(SYS_PM_DEVICE_SUSPEND_ONLY);
return 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) static void low_power_state_exit(void)
{ {
resume_devices(SYS_PM_LOW_POWER_STATE);
end_time = rtc_read(rtc_dev); end_time = rtc_read(rtc_dev);
PRINT("\nLow power state policy exit!\n"); PRINT("\nLow power state policy exit!\n");
PRINT("Total Elapsed From Suspend To Resume = %d RTC Cycles\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) static void device_suspend_only_exit(void)
{ {
resume_devices(SYS_PM_DEVICE_SUSPEND_ONLY);
end_time = rtc_read(rtc_dev); end_time = rtc_read(rtc_dev);
PRINT("\nDevice suspend only policy exit!\n"); PRINT("\nDevice suspend only policy exit!\n");
PRINT("Total Elapsed From Suspend To Resume = %d RTC Cycles\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)); ::"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;
}
}
}