pm: device: move device busy APIs to pm subsystem

The following device busy APIs:

- device_busy_set()
- device_busy_clear()
- device_busy_check()
- device_any_busy_check()

were used for device PM, so they have been moved to the pm subsystem.
This means they are now prefixed with `pm_` and are defined in
`pm/device.h`.

If device PM is not enabled dummy functions are now provided that do
nothing or return `-ENOSYS`, meaning that the functionality is not
available.

Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
This commit is contained in:
Gerard Marull-Paretas 2021-05-31 15:24:34 +02:00 committed by Anas Nashif
commit 70322853a8
11 changed files with 112 additions and 119 deletions

View file

@ -425,15 +425,15 @@ static int i2c_dw_transfer(const struct device *dev,
* While waiting at device_sync_sem, kernel can switch to idle
* task which in turn can call pm_system_suspend() hook of Power
* Management App (PMA).
* device_busy_set() call here, would indicate to PMA that it should not
* execute PM policies that would turn off this ip block, causing an
* pm_device_busy_set() call here, would indicate to PMA that it should
* not execute PM policies that would turn off this ip block, causing an
* ongoing hw transaction to be left in an inconsistent state.
* Note : This is just a sample to show a possible use of the API, it is
* upto the driver expert to see, if he actually needs it here, or
* somewhere else, or not needed as the driver's suspend()/resume()
* can handle everything
*/
device_busy_set(dev);
pm_device_busy_set(dev);
/* Process all the messages */
while (msg_left > 0) {
@ -493,7 +493,7 @@ static int i2c_dw_transfer(const struct device *dev,
msg_left--;
}
device_busy_clear(dev);
pm_device_busy_clear(dev);
dw->state = I2C_DW_STATE_READY;

View file

@ -353,7 +353,7 @@ static int bmp388_sample_fetch(const struct device *dev,
}
#endif
device_busy_set(dev);
pm_device_busy_set(dev);
/* Wait for status to indicate that data is ready. */
raw[0] = 0U;
@ -382,7 +382,7 @@ static int bmp388_sample_fetch(const struct device *dev,
bmp388->sample.comp_temp = 0;
error:
device_busy_clear(dev);
pm_device_busy_clear(dev);
return ret;
}

View file

@ -241,7 +241,7 @@ static void uart_cc13xx_cc26xx_irq_tx_enable(const struct device *dev)
* to transmit using the uart, hence we should no longer go
* into standby.
*
* Instead of using device_busy_set(), which currently does
* Instead of using pm_device_busy_set(), which currently does
* not impact the PM policy, we specifically disable the
* standby mode instead, since it is the power state that
* would interfere with a transfer.

View file

@ -833,7 +833,7 @@ static void uart_nrfx_irq_tx_enable(const struct device *dev)
/* Indicate that this device started a transaction that should not be
* interrupted by putting the SoC into the deep sleep mode.
*/
device_busy_set(dev);
pm_device_busy_set(dev);
/* Activate the transmitter. */
nrf_uart_task_trigger(uart0_addr, NRF_UART_TASK_STARTTX);
@ -956,7 +956,7 @@ static void uart_nrfx_isr(const struct device *dev)
/* The transaction is over. It is okay to enter the deep sleep
* mode if needed.
*/
device_busy_clear(dev);
pm_device_busy_clear(dev);
disable_tx_irq = false;

View file

@ -344,8 +344,8 @@ static int transceive(const struct device *dev,
spi_context_lock(&spi->ctx, asynchronous, signal, config);
#ifdef CONFIG_PM_DEVICE
if (device_busy_check(dev) != (-EBUSY)) {
device_busy_set(dev);
if (pm_device_busy_check(dev) != (-EBUSY)) {
pm_device_busy_set(dev);
}
#endif /* CONFIG_PM_DEVICE */
@ -434,7 +434,7 @@ static int transceive(const struct device *dev,
out:
spi_context_release(&spi->ctx, ret);
device_busy_clear(dev);
pm_device_busy_clear(dev);
return ret;
}

View file

@ -612,52 +612,6 @@ static inline bool device_is_ready(const struct device *dev)
return device_usable_check(dev) == 0;
}
/**
* @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 dev Pointer to device structure of the driver instance.
*/
void device_busy_set(const struct device *dev);
/**
* @brief Indicate that the device has completed its transaction
*
* Called by a device driver to indicate the end of a transaction.
*
* @param dev Pointer to device structure of the driver instance.
*/
void device_busy_clear(const struct device *dev);
#ifdef CONFIG_PM_DEVICE
/**
* @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(const struct device *chk_dev);
#endif
/**
* @}
*/

View file

@ -133,6 +133,56 @@ int pm_device_state_set(const struct device *dev,
int pm_device_state_get(const struct device *dev,
enum pm_device_state *device_power_state);
#ifdef CONFIG_PM_DEVICE
/**
* @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 dev Pointer to device structure of the driver instance.
*/
void pm_device_busy_set(const struct device *dev);
/**
* @brief Indicate that the device has completed its transaction
*
* Called by a device driver to indicate the end of a transaction.
*
* @param dev Pointer to device structure of the driver instance.
*/
void pm_device_busy_clear(const struct device *dev);
/**
* @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 pm_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 pm_device_busy_check(const struct device *chk_dev);
#else
static inline void pm_device_busy_set(const struct device *dev) {}
static inline void pm_device_busy_clear(const struct device *dev) {}
static inline int pm_device_any_busy_check(void) { return -ENOSYS; }
static inline int pm_device_busy_check(const struct device *chk_dev) { return -ENOSYS; }
#endif
/** Alias for legacy use of device_pm_control_nop */
#define device_pm_control_nop __DEPRECATED_MACRO NULL

View file

@ -195,50 +195,3 @@ int device_required_foreach(const struct device *dev,
return handle_count;
}
#ifdef CONFIG_PM_DEVICE
int device_any_busy_check(void)
{
const struct device *dev = __device_start;
while (dev < __device_end) {
if (atomic_test_bit(&dev->pm->atomic_flags,
PM_DEVICE_ATOMIC_FLAGS_BUSY_BIT)) {
return -EBUSY;
}
++dev;
}
return 0;
}
int device_busy_check(const struct device *dev)
{
if (atomic_test_bit(&dev->pm->atomic_flags,
PM_DEVICE_ATOMIC_FLAGS_BUSY_BIT)) {
return -EBUSY;
}
return 0;
}
#endif
void device_busy_set(const struct device *dev)
{
#ifdef CONFIG_PM_DEVICE
atomic_set_bit(&dev->pm->atomic_flags,
PM_DEVICE_ATOMIC_FLAGS_BUSY_BIT);
#else
ARG_UNUSED(dev);
#endif
}
void device_busy_clear(const struct device *dev)
{
#ifdef CONFIG_PM_DEVICE
atomic_clear_bit(&dev->pm->atomic_flags,
PM_DEVICE_ATOMIC_FLAGS_BUSY_BIT);
#else
ARG_UNUSED(dev);
#endif
}

View file

@ -24,7 +24,7 @@ void main(void)
printk("Device ready\n");
/* Don't let the system power off / low power this device */
device_busy_set(led.port);
pm_device_busy_set(led.port);
while (true) {
gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);

View file

@ -10,7 +10,6 @@
#include <device.h>
#include <pm/policy.h>
#if defined(CONFIG_PM)
#define LOG_LEVEL CONFIG_PM_LOG_LEVEL /* From power module Kconfig */
#include <logging/log.h>
LOG_MODULE_DECLARE(power);
@ -18,6 +17,7 @@ LOG_MODULE_DECLARE(power);
extern const struct device __device_start[];
extern const struct device __device_end[];
#if defined(CONFIG_PM)
extern const struct device *__pm_device_slots_start[];
/* Number of devices successfully suspended. */
@ -28,7 +28,7 @@ static bool should_suspend(const struct device *dev, enum pm_device_state state)
int rc;
enum pm_device_state current_state;
if (device_busy_check(dev) != 0) {
if (pm_device_busy_check(dev) != 0) {
return false;
}
@ -149,3 +149,39 @@ int pm_device_state_get(const struct device *dev,
return dev->pm_control(dev, PM_DEVICE_STATE_GET,
device_power_state);
}
int pm_device_any_busy_check(void)
{
const struct device *dev = __device_start;
while (dev < __device_end) {
if (atomic_test_bit(&dev->pm->atomic_flags,
PM_DEVICE_ATOMIC_FLAGS_BUSY_BIT)) {
return -EBUSY;
}
++dev;
}
return 0;
}
int pm_device_busy_check(const struct device *dev)
{
if (atomic_test_bit(&dev->pm->atomic_flags,
PM_DEVICE_ATOMIC_FLAGS_BUSY_BIT)) {
return -EBUSY;
}
return 0;
}
void pm_device_busy_set(const struct device *dev)
{
atomic_set_bit(&dev->pm->atomic_flags,
PM_DEVICE_ATOMIC_FLAGS_BUSY_BIT);
}
void pm_device_busy_clear(const struct device *dev)
{
atomic_clear_bit(&dev->pm->atomic_flags,
PM_DEVICE_ATOMIC_FLAGS_BUSY_BIT);
}

View file

@ -51,7 +51,7 @@ extern void test_mmio_device_map(void);
*
* @ingroup kernel_device_tests
*
* @see device_get_binding(), device_busy_set(), device_busy_clear(),
* @see device_get_binding(), pm_device_busy_set(), pm_device_busy_clear(),
* DEVICE_DEFINE()
*/
void test_dummy_device(void)
@ -66,8 +66,8 @@ void test_dummy_device(void)
dev = device_get_binding(DUMMY_PORT_2);
zassert_false((dev == NULL), NULL);
device_busy_set(dev);
device_busy_clear(dev);
pm_device_busy_set(dev);
pm_device_busy_clear(dev);
/* device_get_binding() returns false for device object
* with failed init.
@ -283,8 +283,8 @@ static void test_enable_and_disable_automatic_runtime_pm(void)
* enabled. It also checks if the device is in the middle of a transaction,
* sets/clears busy status and validates status again.
*
* @see device_get_binding(), device_busy_set(), device_busy_clear(),
* device_busy_check(), device_any_busy_check(),
* @see device_get_binding(), pm_device_busy_set(), pm_device_busy_clear(),
* pm_device_busy_check(), pm_device_any_busy_check(),
* pm_device_state_set()
*/
void test_dummy_device_pm(void)
@ -296,22 +296,22 @@ void test_dummy_device_pm(void)
dev = device_get_binding(DUMMY_PORT_2);
zassert_false((dev == NULL), NULL);
busy = device_any_busy_check();
busy = pm_device_any_busy_check();
zassert_true((busy == 0), NULL);
/* Set device state to BUSY*/
device_busy_set(dev);
pm_device_busy_set(dev);
busy = device_any_busy_check();
busy = pm_device_any_busy_check();
zassert_false((busy == 0), NULL);
busy = device_busy_check(dev);
busy = pm_device_busy_check(dev);
zassert_false((busy == 0), NULL);
/* Clear device BUSY state*/
device_busy_clear(dev);
pm_device_busy_clear(dev);
busy = device_busy_check(dev);
busy = pm_device_busy_check(dev);
zassert_true((busy == 0), NULL);
test_build_suspend_device_list();