power_mgmt: APIs for devices to signal busy
Certain Low power SOC states (e.g. deep sleep) will result in device IP blocks losing state. In such a scenario it can be useful to have a mechanism for devices (driver code) to signal the power manager / policy that they are in the middle of a transaction. We expect the device driver code to make a call to device_busy_set(device *) before initiating a transaction and device_busy_clear(device *) on completion. It is expected that device driver developers will add this as necessary in their drivers. Further an API is provided for power manager application / policy to check this. Based on this the power manager / policy can decide whether or not to go into a particular power state. Change-Id: I0fedd90b98e182cd41b53c7f9e08655532822faa Signed-off-by: Jithu Joseph <jithu.joseph@intel.com>
This commit is contained in:
parent
35360df9be
commit
592882e5f4
4 changed files with 110 additions and 1 deletions
|
@ -304,6 +304,25 @@ struct device {
|
|||
void _sys_device_do_config_level(int level);
|
||||
struct device* device_get_binding(char *name);
|
||||
|
||||
/**
|
||||
* @brief Indicate that the device is in the middle of a transaction
|
||||
*
|
||||
* Called by a device driver to indicate that it is in the middle of a
|
||||
* transaction.
|
||||
*
|
||||
* @param busy_dev Pointer to device structure of the driver instance.
|
||||
*/
|
||||
void device_busy_set(struct device *busy_dev);
|
||||
|
||||
/**
|
||||
* @brief Indicate that the device has completed its transaction
|
||||
*
|
||||
* Called by a device driver to indicate the end of a transaction.
|
||||
*
|
||||
* @param busy_dev Pointer to device structure of the driver instance.
|
||||
*/
|
||||
void device_busy_clear(struct device *busy_dev);
|
||||
|
||||
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
||||
/**
|
||||
* Device PM functions
|
||||
|
@ -370,6 +389,30 @@ static inline int device_resume(struct device *device, int pm_policy)
|
|||
*/
|
||||
void device_list_get(struct device **device_list, int *device_count);
|
||||
|
||||
/**
|
||||
* @brief Check if any device is in the middle of a transaction
|
||||
*
|
||||
* Called by an application to see if any device is in the middle
|
||||
* of a critical transaction that cannot be interrupted.
|
||||
*
|
||||
* @retval 0 if no device is busy
|
||||
* @retval -EBUSY if any device is busy
|
||||
*/
|
||||
int device_any_busy_check(void);
|
||||
|
||||
/**
|
||||
* @brief Check if a specific device is in the middle of a transaction
|
||||
*
|
||||
* Called by an application to see if a particular device is in the
|
||||
* middle of a critical transaction that cannot be interrupted.
|
||||
*
|
||||
* @param chk_dev Pointer to device structure of the specific device driver
|
||||
* the caller is interested in.
|
||||
* @retval 0 if the device is not busy
|
||||
* @retval -EBUSY if the device is busy
|
||||
*/
|
||||
int device_busy_check(struct device *chk_dev);
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
|
|
@ -45,6 +45,24 @@
|
|||
|
||||
#ifdef _LINKER
|
||||
|
||||
|
||||
/*
|
||||
* Space for storing per device busy bitmap. Since we do not know beforehand
|
||||
* the number of devices, we go through the below mechanism to allocate the
|
||||
* required space.
|
||||
*/
|
||||
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
||||
#define DEVICE_COUNT ((__device_init_end - __device_init_start) / __DEVICE_STR_SIZEOF)
|
||||
#define DEV_BUSY_SZ (((DEVICE_COUNT + 31) / 32) * 4)
|
||||
#define DEVICE_BUSY_BITFIELD() \
|
||||
FILL(0x00) ; \
|
||||
__device_busy_start = .; \
|
||||
. = . + DEV_BUSY_SZ; \
|
||||
__device_busy_end = .;
|
||||
#else
|
||||
#define DEVICE_BUSY_BITFIELD()
|
||||
#endif
|
||||
|
||||
/*
|
||||
* generate a symbol to mark the start of the device initialization objects for
|
||||
* the specified level, then link all of those objects (sorted by priority);
|
||||
|
@ -69,7 +87,8 @@
|
|||
DEVICE_INIT_LEVEL(NANOKERNEL) \
|
||||
DEVICE_INIT_LEVEL(MICROKERNEL) \
|
||||
DEVICE_INIT_LEVEL(APPLICATION) \
|
||||
__device_init_end = .; \
|
||||
__device_init_end = .; \
|
||||
DEVICE_BUSY_BITFIELD() \
|
||||
|
||||
|
||||
/* define a section for undefined device initialization levels */
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <string.h>
|
||||
#include <device.h>
|
||||
#include <misc/util.h>
|
||||
#include <atomic.h>
|
||||
|
||||
extern struct device __device_init_start[];
|
||||
extern struct device __device_PRIMARY_start[];
|
||||
|
@ -37,6 +38,9 @@ static struct device *config_levels[] = {
|
|||
|
||||
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
||||
struct device_pm_ops device_pm_ops_nop = {device_pm_nop, device_pm_nop};
|
||||
extern uint32_t __device_busy_start[];
|
||||
extern uint32_t __device_busy_end[];
|
||||
#define DEVICE_BUSY_SIZE (__device_busy_end - __device_busy_start)
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
@ -98,4 +102,43 @@ void device_list_get(struct device **device_list, int *device_count)
|
|||
*device_list = __device_init_start;
|
||||
*device_count = __device_init_end - __device_init_start;
|
||||
}
|
||||
|
||||
|
||||
int device_any_busy_check(void)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < DEVICE_BUSY_SIZE; i++) {
|
||||
if (__device_busy_start[i] != 0) {
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int device_busy_check(struct device *chk_dev)
|
||||
{
|
||||
if (atomic_test_bit((const atomic_t *)__device_busy_start,
|
||||
(chk_dev - __device_init_start))) {
|
||||
return -EBUSY;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void device_busy_set(struct device *busy_dev)
|
||||
{
|
||||
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
||||
atomic_set_bit((atomic_t *) __device_busy_start,
|
||||
(busy_dev - __device_init_start));
|
||||
#endif
|
||||
}
|
||||
|
||||
void device_busy_clear(struct device *busy_dev)
|
||||
{
|
||||
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
||||
atomic_clear_bit((atomic_t *) __device_busy_start,
|
||||
(busy_dev - __device_init_start));
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include <device.h>
|
||||
|
||||
#ifndef _NANO_OFFSETS__H_
|
||||
#define _NANO_OFFSETS__H_
|
||||
|
@ -64,4 +65,7 @@ GEN_OFFSET_SYM(tTCS, errno_var);
|
|||
|
||||
GEN_ABSOLUTE_SYM(__tTCS_SIZEOF, sizeof(tTCS));
|
||||
|
||||
/* size of the device structure. Used by linker scripts */
|
||||
GEN_ABSOLUTE_SYM(__DEVICE_STR_SIZEOF, sizeof(struct device));
|
||||
|
||||
#endif /* _NANO_OFFSETS__H_ */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue