Commit 592882e5 authored by Jithu Joseph's avatar Jithu Joseph Committed by Anas Nashif
Browse files

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: default avatarJithu Joseph <jithu.joseph@intel.com>
parent 35360df9
Loading
Loading
Loading
Loading
+43 −0
Original line number Diff line number Diff line
@@ -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

/**
+20 −1
Original line number Diff line number Diff line
@@ -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);
@@ -70,6 +88,7 @@
		DEVICE_INIT_LEVEL(MICROKERNEL)	\
		DEVICE_INIT_LEVEL(APPLICATION)	\
		__device_init_end = .;		\
		DEVICE_BUSY_BITFIELD()		\


/* define a section for undefined device initialization levels */
+43 −0
Original line number Diff line number Diff line
@@ -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
}
+4 −0
Original line number Diff line number Diff line
@@ -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_ */