Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pm: Add option to have synchronous runtime power management #67424

Merged
merged 3 commits into from
Feb 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion drivers/power_domain/power_domain_gpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ static int pd_on_domain_visitor(const struct device *dev, void *context)
struct pd_visitor_context *visitor_context = context;

/* Only run action if the device is on the specified domain */
if (!dev->pm || (dev->pm->domain != visitor_context->domain)) {
if (!dev->pm || (dev->pm_base->domain != visitor_context->domain)) {
return 0;
}

Expand Down
2 changes: 1 addition & 1 deletion drivers/power_domain/power_domain_gpio_monitor.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ static int pd_on_domain_visitor(const struct device *dev, void *context)
struct pd_visitor_context *visitor_context = context;

/* Only run action if the device is on the specified domain */
if (!dev->pm || (dev->pm->domain != visitor_context->domain)) {
if (!dev->pm || (dev->pm_base->domain != visitor_context->domain)) {
return 0;
}

Expand Down
16 changes: 11 additions & 5 deletions include/zephyr/device.h
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,9 @@ struct device_state {
bool initialized : 1;
};

struct pm_device_base;
struct pm_device;
struct pm_device_isr;

#ifdef CONFIG_DEVICE_DEPS_DYNAMIC
#define Z_DEVICE_DEPS_CONST
Expand Down Expand Up @@ -409,7 +411,11 @@ struct device {
* Reference to the device PM resources (only available if
* @kconfig{CONFIG_PM_DEVICE} is enabled).
*/
struct pm_device *pm;
union {
struct pm_device_base *pm_base;
struct pm_device *pm;
struct pm_device_isr *pm_isr;
};
#endif
};

Expand Down Expand Up @@ -885,7 +891,7 @@ static inline bool z_impl_device_is_ready(const struct device *dev)
* @brief Initializer for @ref device.
*
* @param name_ Name of the device.
* @param pm_ Reference to @ref pm_device (optional).
* @param pm_ Reference to @ref pm_device_base (optional).
* @param data_ Reference to device data.
* @param config_ Reference to device config.
* @param api_ Reference to device API ops.
Expand All @@ -900,7 +906,7 @@ static inline bool z_impl_device_is_ready(const struct device *dev)
.state = (state_), \
.data = (data_), \
IF_ENABLED(CONFIG_DEVICE_DEPS, (.deps = (deps_),)) /**/ \
IF_ENABLED(CONFIG_PM_DEVICE, (.pm = (pm_),)) /**/ \
IF_ENABLED(CONFIG_PM_DEVICE, (.pm_base = (pm_),)) /**/ \
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this broke gcc 4.2.0 again.

Seems very similar to #68118

 -c zephyr/soc/xtensa/intel_adsp/common/mem_window.c
zephyr/soc/xtensa/intel_adsp/common/mem_window.c:62: error: unknown field ‘pm_base’ specified in initializer
cc1: warnings being treated as errors
zephyr/soc/xtensa/intel_adsp/common/mem_window.c:62: warning: missing braces around initializer
zephyr/soc/xtensa/intel_adsp/common/mem_window.c:62: warning: (near initialization for ‘__device_dts_ord_66.<anonymous>’)
zephyr/soc/xtensa/intel_adsp/common/mem_window.c:65: error: unknown field ‘pm_base’ specified in initializer
zephyr/soc/xtensa/intel_adsp/common/mem_window.c:65: warning: missing braces around initializer
zephyr/soc/xtensa/intel_adsp/common/mem_window.c:65: warning: (near initialization for ‘__device_dts_ord_67.<anonymous>’)
zephyr/soc/xtensa/intel_adsp/common/mem_window.c:68: error: unknown field ‘pm_base’ specified in initializer
zephyr/soc/xtensa/intel_adsp/common/mem_window.c:68: warning: missing braces around initializer
zephyr/soc/xtensa/intel_adsp/common/mem_window.c:68: warning: (near initialization for ‘__device_dts_ord_68.<anonymous>’)
zephyr/soc/xtensa/intel_adsp/common/mem_window.c:71: error: unknown field ‘pm_base’ specified in initializer
zephyr/soc/xtensa/intel_adsp/common/mem_window.c:71: warning: missing braces around initializer
zephyr/soc/xtensa/intel_adsp/common/mem_window.c:71: warning: (near initialization for ‘__device_dts_ord_69.<anonymous>’)


Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

/**
Expand All @@ -919,7 +925,7 @@ static inline bool z_impl_device_is_ready(const struct device *dev)
* software device).
* @param dev_id Device identifier (used to name the defined @ref device).
* @param name Name of the device.
* @param pm Reference to @ref pm_device associated with the device.
* @param pm Reference to @ref pm_device_base associated with the device.
* (optional).
* @param data Reference to device data.
* @param config Reference to device config.
Expand Down Expand Up @@ -991,7 +997,7 @@ static inline bool z_impl_device_is_ready(const struct device *dev)
* @param dev_id Device identifier (used to name the defined @ref device).
* @param name Name of the device.
* @param init_fn Device init function.
* @param pm Reference to @ref pm_device associated with the device.
* @param pm Reference to @ref pm_device_base associated with the device.
* (optional).
* @param data Reference to device data.
* @param config Reference to device config.
Expand Down
146 changes: 109 additions & 37 deletions include/zephyr/pm/device.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,22 @@ enum pm_device_flag {
PM_DEVICE_FLAG_PD,
/** Indicates if device runtime PM should be automatically enabled */
PM_DEVICE_FLAG_RUNTIME_AUTO,
/** Indicates that device runtime PM supports suspending and resuming from any context. */
PM_DEVICE_FLAG_ISR_SAFE,
};

/** @endcond */

/** @brief Flag indicating that runtime PM API for the device can be called from any context.
*
* If @ref PM_DEVICE_ISR_SAFE flag is used for device definition, it indicates that PM actions
* are synchronous and can be executed from any context. This approach can be used for cases where
* suspending and resuming is short as it is executed in the critical section. This mode requires
* less resources (~80 byte less RAM) and allows to use device runtime PM from any context
* (including interrupts).
*/
#define PM_DEVICE_ISR_SAFE 1

/** @brief Device power states. */
enum pm_device_state {
/** Device is in active or regular state. */
Expand Down Expand Up @@ -122,32 +134,69 @@ typedef bool (*pm_device_action_failed_cb_t)(const struct device *dev,

/**
* @brief Device PM info
*
* Structure holds fields which are common for two PM devices: generic and
* synchronous.
*/
struct pm_device_base {
/** Device PM status flags. */
atomic_t flags;
/** Device power state */
enum pm_device_state state;
/** Device PM action callback */
pm_device_action_cb_t action_cb;
#if defined(CONFIG_PM_DEVICE_RUNTIME) || defined(__DOXYGEN__)
/** Device usage count */
uint32_t usage;
#endif /* CONFIG_PM_DEVICE_RUNTIME */
#ifdef CONFIG_PM_DEVICE_POWER_DOMAIN
/** Power Domain it belongs */
const struct device *domain;
#endif /* CONFIG_PM_DEVICE_POWER_DOMAIN */
};

/**
* @brief Runtime PM info for device with generic PM.
*
* Generic PM involves suspending and resuming operations which can be blocking,
* long lasting or asynchronous. Runtime PM API is limited when used from
* interrupt context.
*/
struct pm_device {
/** Base info. */
struct pm_device_base base;
#if defined(CONFIG_PM_DEVICE_RUNTIME) || defined(__DOXYGEN__)
/** Pointer to the device */
const struct device *dev;
/** Lock to synchronize the get/put operations */
struct k_sem lock;
/** Event var to listen to the sync request events */
struct k_event event;
/** Device usage count */
uint32_t usage;
/** Work object for asynchronous calls */
struct k_work_delayable work;
#endif /* CONFIG_PM_DEVICE_RUNTIME */
#ifdef CONFIG_PM_DEVICE_POWER_DOMAIN
/** Power Domain it belongs */
const struct device *domain;
#endif /* CONFIG_PM_DEVICE_POWER_DOMAIN */
/* Device PM status flags. */
atomic_t flags;
/** Device power state */
enum pm_device_state state;
/** Device PM action callback */
pm_device_action_cb_t action_cb;
};

/**
* @brief Runtime PM info for device with synchronous PM.
*
* Synchronous PM can be used with devices which suspend and resume operations can
* be performed in the critical section as they are short and non-blocking.
* Runtime PM API can be used from any context in that case.
*/
struct pm_device_isr {
/** Base info. */
struct pm_device_base base;
#if defined(CONFIG_PM_DEVICE_RUNTIME) || defined(__DOXYGEN__)
/** Lock to synchronize the synchronous get/put operations */
struct k_spinlock lock;
#endif
};

/* Base part must be the first element. */
BUILD_ASSERT(offsetof(struct pm_device, base) == 0);
BUILD_ASSERT(offsetof(struct pm_device_isr, base) == 0);

/** @cond INTERNAL_HIDDEN */

#ifdef CONFIG_PM_DEVICE_RUNTIME
Expand All @@ -167,7 +216,7 @@ struct pm_device {
#endif /* CONFIG_PM_DEVICE_POWER_DOMAIN */

/**
* @brief Utility macro to initialize #pm_device flags
* @brief Utility macro to initialize #pm_device_base flags
*
* @param node_id Devicetree node for the initialized device (can be invalid).
*/
Expand All @@ -188,17 +237,34 @@ struct pm_device {
* @note #DT_PROP_OR is used to retrieve the wakeup_source property because
* it may not be defined on all devices.
*
* @param obj Name of the #pm_device structure being initialized.
* @param obj Name of the #pm_device_base structure being initialized.
* @param node_id Devicetree node for the initialized device (can be invalid).
* @param pm_action_cb Device PM control callback function.
* @param _flags Additional flags passed to the structure.
*/
#define Z_PM_DEVICE_BASE_INIT(obj, node_id, pm_action_cb, _flags) \
{ \
.action_cb = pm_action_cb, \
.state = PM_DEVICE_STATE_ACTIVE, \
.flags = ATOMIC_INIT(Z_PM_DEVICE_FLAGS(node_id) | (_flags)), \
Z_PM_DEVICE_POWER_DOMAIN_INIT(node_id) \
}

/**
* @brief Utility macro to initialize #pm_device_rt.
*
* @note #DT_PROP_OR is used to retrieve the wakeup_source property because
* it may not be defined on all devices.
*
* @param obj Name of the #pm_device_base structure being initialized.
* @param node_id Devicetree node for the initialized device (can be invalid).
* @param pm_action_cb Device PM control callback function.
*/
#define Z_PM_DEVICE_INIT(obj, node_id, pm_action_cb) \
{ \
Z_PM_DEVICE_RUNTIME_INIT(obj) \
.action_cb = pm_action_cb, \
.state = PM_DEVICE_STATE_ACTIVE, \
.flags = ATOMIC_INIT(Z_PM_DEVICE_FLAGS(node_id)), \
Z_PM_DEVICE_POWER_DOMAIN_INIT(node_id) \
#define Z_PM_DEVICE_INIT(obj, node_id, pm_action_cb, isr_safe) \
{ \
.base = Z_PM_DEVICE_BASE_INIT(obj, node_id, pm_action_cb, \
isr_safe ? BIT(PM_DEVICE_FLAG_ISR_SAFE) : 0), \
COND_CODE_1(isr_safe, (), (Z_PM_DEVICE_RUNTIME_INIT(obj))) \
}

/**
Expand Down Expand Up @@ -231,21 +297,22 @@ struct pm_device {
* @param dev_id Device id.
* @param pm_action_cb PM control callback.
*/
#define Z_PM_DEVICE_DEFINE(node_id, dev_id, pm_action_cb) \
Z_PM_DEVICE_DEFINE_SLOT(dev_id); \
static struct pm_device Z_PM_DEVICE_NAME(dev_id) = \
Z_PM_DEVICE_INIT(Z_PM_DEVICE_NAME(dev_id), node_id, \
pm_action_cb)
#define Z_PM_DEVICE_DEFINE(node_id, dev_id, pm_action_cb, isr_safe) \
Z_PM_DEVICE_DEFINE_SLOT(dev_id); \
static struct COND_CODE_1(isr_safe, (pm_device_isr), (pm_device)) \
Z_PM_DEVICE_NAME(dev_id) = \
Z_PM_DEVICE_INIT(Z_PM_DEVICE_NAME(dev_id), node_id, \
pm_action_cb, isr_safe)

/**
* Get a reference to the device PM resources.
*
* @param dev_id Device id.
*/
#define Z_PM_DEVICE_GET(dev_id) (&Z_PM_DEVICE_NAME(dev_id))
#define Z_PM_DEVICE_GET(dev_id) ((struct pm_device_base *)&Z_PM_DEVICE_NAME(dev_id))

#else
#define Z_PM_DEVICE_DEFINE(node_id, dev_id, pm_action_cb)
#define Z_PM_DEVICE_DEFINE(node_id, dev_id, pm_action_cb, isr_safe)
#define Z_PM_DEVICE_GET(dev_id) NULL
#endif /* CONFIG_PM_DEVICE */

Expand All @@ -258,11 +325,13 @@ struct pm_device {
*
* @param dev_id Device id.
* @param pm_action_cb PM control callback.
* @param ... Optional flag to indicate that ISR safe. Use @ref PM_DEVICE_ISR_SAFE or 0.
*
* @see #PM_DEVICE_DT_DEFINE, #PM_DEVICE_DT_INST_DEFINE
*/
#define PM_DEVICE_DEFINE(dev_id, pm_action_cb) \
Z_PM_DEVICE_DEFINE(DT_INVALID_NODE, dev_id, pm_action_cb)
#define PM_DEVICE_DEFINE(dev_id, pm_action_cb, ...) \
Z_PM_DEVICE_DEFINE(DT_INVALID_NODE, dev_id, pm_action_cb, \
COND_CODE_1(IS_EMPTY(__VA_ARGS__), (0), (__VA_ARGS__)))

/**
* Define device PM resources for the given node identifier.
Expand All @@ -271,12 +340,13 @@ struct pm_device {
*
* @param node_id Node identifier.
* @param pm_action_cb PM control callback.
* @param ... Optional flag to indicate that device is isr_ok. Use @ref PM_DEVICE_ISR_SAFE or 0.
*
* @see #PM_DEVICE_DT_INST_DEFINE, #PM_DEVICE_DEFINE
*/
#define PM_DEVICE_DT_DEFINE(node_id, pm_action_cb) \
Z_PM_DEVICE_DEFINE(node_id, Z_DEVICE_DT_DEV_ID(node_id), \
pm_action_cb)
#define PM_DEVICE_DT_DEFINE(node_id, pm_action_cb, ...) \
Z_PM_DEVICE_DEFINE(node_id, Z_DEVICE_DT_DEV_ID(node_id), pm_action_cb, \
COND_CODE_1(IS_EMPTY(__VA_ARGS__), (0), (__VA_ARGS__)))

/**
* Define device PM resources for the given instance.
Expand All @@ -285,13 +355,15 @@ struct pm_device {
*
* @param idx Instance index.
* @param pm_action_cb PM control callback.
* @param ... Optional flag to indicate that device is isr_ok. Use @ref PM_DEVICE_ISR_SAFE or 0.
*
* @see #PM_DEVICE_DT_DEFINE, #PM_DEVICE_DEFINE
*/
#define PM_DEVICE_DT_INST_DEFINE(idx, pm_action_cb) \
#define PM_DEVICE_DT_INST_DEFINE(idx, pm_action_cb, ...) \
Z_PM_DEVICE_DEFINE(DT_DRV_INST(idx), \
Z_DEVICE_DT_DEV_ID(DT_DRV_INST(idx)), \
pm_action_cb)
pm_action_cb, \
COND_CODE_1(IS_EMPTY(__VA_ARGS__), (0), (__VA_ARGS__)))

/**
* @brief Obtain a reference to the device PM resources for the given device.
Expand Down Expand Up @@ -393,7 +465,7 @@ int pm_device_state_get(const struct device *dev,
*/
static inline void pm_device_init_suspended(const struct device *dev)
{
struct pm_device *pm = dev->pm;
struct pm_device_base *pm = dev->pm_base;

pm->state = PM_DEVICE_STATE_SUSPENDED;
}
Expand All @@ -413,7 +485,7 @@ static inline void pm_device_init_suspended(const struct device *dev)
*/
static inline void pm_device_init_off(const struct device *dev)
{
struct pm_device *pm = dev->pm;
struct pm_device_base *pm = dev->pm_base;

pm->state = PM_DEVICE_STATE_OFF;
}
Expand Down
2 changes: 1 addition & 1 deletion kernel/include/kernel_offsets.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ GEN_ABSOLUTE_SYM(_DEVICE_STRUCT_PM_OFFSET,
/* member offsets in the pm_device structure. Used in image post-processing */

GEN_ABSOLUTE_SYM(_PM_DEVICE_STRUCT_FLAGS_OFFSET,
offsetof(struct pm_device, flags));
offsetof(struct pm_device_base, flags));

GEN_ABSOLUTE_SYM(_PM_DEVICE_FLAG_PD, PM_DEVICE_FLAG_PD);

Expand Down
Loading
Loading