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); +}