From 8eceeee798743d25ac882cf0374cb062a435cefe Mon Sep 17 00:00:00 2001 From: Flavio Ceolin Date: Wed, 30 Jun 2021 14:32:56 -0700 Subject: [PATCH] pm: device: Add wakeup source API Introduce a new API to allow devices capable of wake up the system register themselves was wake up sources. This permits applications to select the most appropriate way to wake up the system when it is suspended. Signed-off-by: Flavio Ceolin --- doc/_scripts/gen_devicetree_rest.py | 4 ++- dts/bindings/base/base.yaml | 2 ++ dts/bindings/base/pm.yaml | 18 ++++++++++++ include/device.h | 18 ++++++++++-- include/pm/device.h | 43 +++++++++++++++++++++++++++++ kernel/device.c | 1 + subsys/pm/device.c | 32 +++++++++++++++++++++ 7 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 dts/bindings/base/pm.yaml diff --git a/doc/_scripts/gen_devicetree_rest.py b/doc/_scripts/gen_devicetree_rest.py index c2c0cd66fb0..746275f5d5c 100644 --- a/doc/_scripts/gen_devicetree_rest.py +++ b/doc/_scripts/gen_devicetree_rest.py @@ -226,9 +226,11 @@ def load_base_binding(): # nodes from node-specific properties. base_yaml = ZEPHYR_BASE / 'dts' / 'bindings' / 'base' / 'base.yaml' + base_includes = {"pm.yaml": os.fspath(ZEPHYR_BASE / 'dts' / 'bindings' / 'base'/ 'pm.yaml')} + if not base_yaml.is_file(): sys.exit(f'Expected to find base.yaml at {base_yaml}') - return edtlib.Binding(os.fspath(base_yaml), {}, require_compatible=False, + return edtlib.Binding(os.fspath(base_yaml), base_includes, require_compatible=False, require_description=False) def dump_content(bindings, base_binding, vnd_lookup, out_dir): diff --git a/dts/bindings/base/base.yaml b/dts/bindings/base/base.yaml index 4d9dafe0f2a..1e489401e83 100644 --- a/dts/bindings/base/base.yaml +++ b/dts/bindings/base/base.yaml @@ -1,5 +1,7 @@ # Common fields for all devices +include: [pm.yaml] + properties: status: type: string diff --git a/dts/bindings/base/pm.yaml b/dts/bindings/base/pm.yaml new file mode 100644 index 00000000000..a0d3bae2033 --- /dev/null +++ b/dts/bindings/base/pm.yaml @@ -0,0 +1,18 @@ +# Copyright (c) 2021 Intel Corporation. +# SPDX-License-Identifier: Apache-2.0 + +# Properties for Power Management (PM) + +properties: + wakeup-source: + required: false + type: boolean + description: | + Property to identify that a device can be used as wake up source. + + When this property is provided a specific flag is set into the + device that tells the system that the device is capable of + wake up the system. + + Wake up capable devices are disabled (interruptions will not wake up + the system) by default but they can be enabled at runtime if necessary. diff --git a/include/device.h b/include/device.h index 53a245a1182..afc6855d1e7 100644 --- a/include/device.h +++ b/include/device.h @@ -663,6 +663,7 @@ static inline bool device_is_ready(const struct device *dev) */ #define Z_DEVICE_DEFINE_PRE(node_id, dev_name, ...) \ Z_DEVICE_DEFINE_HANDLES(node_id, dev_name, __VA_ARGS__) \ + Z_DEVICE_STATE_DEFINE(node_id, dev_name) \ Z_DEVICE_DEFINE_PM_SLOT(dev_name) @@ -713,7 +714,6 @@ BUILD_ASSERT(sizeof(device_handle_t) == 2, "fix the linker scripts"); */ #define Z_DEVICE_DEFINE(node_id, dev_name, drv_name, init_fn, pm_control_fn, \ data_ptr, cfg_ptr, level, prio, api_ptr, ...) \ - static struct device_state Z_DEVICE_STATE_NAME(dev_name); \ Z_DEVICE_DEFINE_PRE(node_id, dev_name, __VA_ARGS__) \ COND_CODE_1(DT_NODE_EXISTS(node_id), (), (static)) \ const Z_DECL_ALIGN(struct device) \ @@ -732,10 +732,24 @@ BUILD_ASSERT(sizeof(device_handle_t) == 2, "fix the linker scripts"); (&DEVICE_NAME_GET(dev_name)), level, prio) #ifdef CONFIG_PM_DEVICE -#define Z_DEVICE_DEFINE_PM_INIT(dev_name, pm_control_fn) \ +#define Z_DEVICE_STATE_DEFINE(node_id, dev_name) \ + static struct device_state Z_DEVICE_STATE_NAME(dev_name) = { \ + .pm = { \ + .flags = ATOMIC_INIT(COND_CODE_1( \ + DT_NODE_EXISTS(node_id), \ + (DT_PROP( \ + node_id, wakeup_source)), \ + (0)) << \ + PM_DEVICE_FLAGS_WS_CAPABLE), \ + }, \ + }; + +#define Z_DEVICE_DEFINE_PM_INIT(dev_name, pm_control_fn) \ .pm_control = (pm_control_fn), \ .pm = &Z_DEVICE_STATE_NAME(dev_name).pm, #else +#define Z_DEVICE_STATE_DEFINE(node_id, dev_name) \ + static struct device_state Z_DEVICE_STATE_NAME(dev_name); #define Z_DEVICE_DEFINE_PM_INIT(dev_name, pm_control_fn) #endif diff --git a/include/pm/device.h b/include/pm/device.h index 0299b3cce7d..7c65ea36ade 100644 --- a/include/pm/device.h +++ b/include/pm/device.h @@ -66,6 +66,13 @@ enum pm_device_state { enum pm_device_flag { /** Indicate if the device is busy or not. */ PM_DEVICE_FLAG_BUSY, + /** + * Indicates whether or not the device is capable of waking the system + * up. + */ + PM_DEVICE_FLAGS_WS_CAPABLE, + /** Indicates if the device is being used as wakeup source. */ + PM_DEVICE_FLAGS_WS_ENABLED, /** Number of flags (internal use only). */ PM_DEVICE_FLAG_COUNT }; @@ -231,6 +238,42 @@ __deprecated static inline int device_busy_check(const struct device *dev) /** Alias for legacy use of device_pm_control_nop */ #define device_pm_control_nop __DEPRECATED_MACRO NULL +/** + * @brief Enable a power management wakeup source + * + * Enable a wakeup source. This will keep the current device active when the + * system is suspended, allowing it to be used to wake up the system. + * + * @param dev device object to enable. + * @param enable @c true to enable or @c false to disable + * + * @retval true if the wakeup source was successfully enabled. + * @retval false if the wakeup source was not successfully enabled. + */ +bool pm_device_wakeup_enable(struct device *dev, bool enable); + +/** + * @brief Check if a power management wakeup source is enabled + * + * Checks if a wake up source is enabled. + * + * @param dev device object to check. + * + * @retval true if the wakeup source is enabled. + * @retval false if the wakeup source is not enabled. + */ +bool pm_device_wakeup_is_enabled(const struct device *dev); + +/** + * @brief Check if a device is wake up capable + * + * @param dev device object to check. + * + * @retval true if the device is wake up capable. + * @retval false if the device is not wake up capable. + */ +bool pm_device_wakeup_is_capable(const struct device *dev); + /** @} */ #ifdef __cplusplus diff --git a/kernel/device.c b/kernel/device.c index a204d86122e..f09dcf55484 100644 --- a/kernel/device.c +++ b/kernel/device.c @@ -33,6 +33,7 @@ static inline void device_pm_state_init(const struct device *dev) .lock = Z_MUTEX_INITIALIZER(dev->pm->lock), .condvar = Z_CONDVAR_INITIALIZER(dev->pm->condvar), .state = PM_DEVICE_STATE_ACTIVE, + .flags = ATOMIC_INIT(dev->pm->flags), }; #endif /* CONFIG_PM_DEVICE */ } diff --git a/subsys/pm/device.c b/subsys/pm/device.c index 013e6c2805d..1db54d8b97b 100644 --- a/subsys/pm/device.c +++ b/subsys/pm/device.c @@ -206,3 +206,35 @@ void pm_device_busy_clear(const struct device *dev) { atomic_clear_bit(&dev->pm->flags, PM_DEVICE_FLAG_BUSY); } + +bool pm_device_wakeup_enable(struct device *dev, bool enable) +{ + atomic_val_t flags, new_flags; + + flags = atomic_get(&dev->pm->flags); + + if ((flags & BIT(PM_DEVICE_FLAGS_WS_CAPABLE)) == 0U) { + return false; + } + + if (enable) { + new_flags = flags | + BIT(PM_DEVICE_FLAGS_WS_ENABLED); + } else { + new_flags = flags & ~BIT(PM_DEVICE_FLAGS_WS_ENABLED); + } + + return atomic_cas(&dev->pm->flags, flags, new_flags); +} + +bool pm_device_wakeup_is_enabled(const struct device *dev) +{ + return atomic_test_bit(&dev->pm->flags, + PM_DEVICE_FLAGS_WS_ENABLED); +} + +bool pm_device_wakeup_is_capable(const struct device *dev) +{ + return atomic_test_bit(&dev->pm->flags, + PM_DEVICE_FLAGS_WS_CAPABLE); +}