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;
|
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,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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,
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
161
include/device.h
161
include/device.h
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue