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:
Jithu Joseph 2016-04-08 12:38:57 -07:00 committed by Anas Nashif
commit 592882e5f4
4 changed files with 110 additions and 1 deletions

View file

@ -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
/**

View file

@ -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 */

View file

@ -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
}

View file

@ -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_ */