diff --git a/drivers/power_domain/CMakeLists.txt b/drivers/power_domain/CMakeLists.txt index 0637e64f794..27b07c945f1 100644 --- a/drivers/power_domain/CMakeLists.txt +++ b/drivers/power_domain/CMakeLists.txt @@ -4,4 +4,5 @@ zephyr_library() zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_GPIO power_domain_gpio.c) +zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_GPIO_MONITOR power_domain_gpio_monitor.c) zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_INTEL_ADSP power_domain_intel_adsp.c) diff --git a/drivers/power_domain/Kconfig b/drivers/power_domain/Kconfig index 6c8563d4724..22ffc388503 100644 --- a/drivers/power_domain/Kconfig +++ b/drivers/power_domain/Kconfig @@ -12,6 +12,12 @@ module = POWER_DOMAIN module-str = power_domain source "subsys/logging/Kconfig.template.log_config" +config POWER_DOMAIN_INIT_PRIORITY + int "Power domain init priority" + default 75 + help + Power domain initialization priority. + config POWER_DOMAIN_GPIO bool "GPIO controlled power domain" default y @@ -28,4 +34,11 @@ config POWER_DOMAIN_INTEL_ADSP help Include Intel ADSP power domain control mechanisms +config POWER_DOMAIN_GPIO_MONITOR + bool "GPIO monitor for sensing power on rail" + default y + depends on DT_HAS_POWER_DOMAIN_GPIO_MONITOR_ENABLED + depends on GPIO + select DEVICE_DEPS + endif diff --git a/drivers/power_domain/power_domain_gpio.c b/drivers/power_domain/power_domain_gpio.c index b4702ad13eb..15c60a65d75 100644 --- a/drivers/power_domain/power_domain_gpio.c +++ b/drivers/power_domain/power_domain_gpio.c @@ -125,17 +125,17 @@ static int pd_gpio_init(const struct device *dev) return pm_device_driver_init(dev, pd_gpio_pm_action); } -#define POWER_DOMAIN_DEVICE(id) \ - static const struct pd_gpio_config pd_gpio_##id##_cfg = { \ - .enable = GPIO_DT_SPEC_INST_GET(id, enable_gpios), \ - .startup_delay_us = DT_INST_PROP(id, startup_delay_us), \ - .off_on_delay_us = DT_INST_PROP(id, off_on_delay_us), \ - }; \ - static struct pd_gpio_data pd_gpio_##id##_data; \ - PM_DEVICE_DT_INST_DEFINE(id, pd_gpio_pm_action); \ - DEVICE_DT_INST_DEFINE(id, pd_gpio_init, PM_DEVICE_DT_INST_GET(id), \ - &pd_gpio_##id##_data, &pd_gpio_##id##_cfg, \ - POST_KERNEL, 75, \ +#define POWER_DOMAIN_DEVICE(id) \ + static const struct pd_gpio_config pd_gpio_##id##_cfg = { \ + .enable = GPIO_DT_SPEC_INST_GET(id, enable_gpios), \ + .startup_delay_us = DT_INST_PROP(id, startup_delay_us), \ + .off_on_delay_us = DT_INST_PROP(id, off_on_delay_us), \ + }; \ + static struct pd_gpio_data pd_gpio_##id##_data; \ + PM_DEVICE_DT_INST_DEFINE(id, pd_gpio_pm_action); \ + DEVICE_DT_INST_DEFINE(id, pd_gpio_init, PM_DEVICE_DT_INST_GET(id), \ + &pd_gpio_##id##_data, &pd_gpio_##id##_cfg, \ + POST_KERNEL, CONFIG_POWER_DOMAIN_INIT_PRIORITY, \ NULL); DT_INST_FOREACH_STATUS_OKAY(POWER_DOMAIN_DEVICE) diff --git a/drivers/power_domain/power_domain_gpio_monitor.c b/drivers/power_domain/power_domain_gpio_monitor.c new file mode 100644 index 00000000000..55e44570553 --- /dev/null +++ b/drivers/power_domain/power_domain_gpio_monitor.c @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2023 Google LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT power_domain_gpio_monitor + +#include +#include +#include +#include + +#include + +LOG_MODULE_REGISTER(power_domain_gpio_monitor, CONFIG_POWER_DOMAIN_LOG_LEVEL); + +struct pd_gpio_monitor_config { + struct gpio_dt_spec power_good_gpio; +}; + +struct pd_gpio_monitor_data { + struct gpio_callback callback; + const struct device *dev; + bool is_powered; +}; + +struct pd_visitor_context { + const struct device *domain; + enum pm_device_action action; +}; + +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)) { + return 0; + } + + dev->pm->usage = 0; + (void)pm_device_action_run(dev, visitor_context->action); + return 0; +} + +static void pd_gpio_monitor_callback(const struct device *port, + struct gpio_callback *cb, gpio_port_pins_t pins) +{ + struct pd_gpio_monitor_data *data = CONTAINER_OF(cb, struct pd_gpio_monitor_data, callback); + const struct pd_gpio_monitor_config *config = data->dev->config; + const struct device *dev = data->dev; + struct pd_visitor_context context = {.domain = dev}; + int rc; + + rc = gpio_pin_get_dt(&config->power_good_gpio); + if (rc < 0) { + LOG_WRN("Failed to read gpio logic level"); + return; + } + + data->is_powered = rc; + if (rc == 0) { + context.action = PM_DEVICE_ACTION_SUSPEND; + (void)device_supported_foreach(dev, pd_on_domain_visitor, &context); + context.action = PM_DEVICE_ACTION_TURN_OFF; + (void)device_supported_foreach(dev, pd_on_domain_visitor, &context); + return; + } + + pm_device_children_action_run(data->dev, PM_DEVICE_ACTION_TURN_ON, NULL); +} + +static int pd_gpio_monitor_pm_action(const struct device *dev, enum pm_device_action action) +{ + struct pd_gpio_monitor_data *data = dev->data; + + switch (action) { + case PM_DEVICE_ACTION_TURN_ON: + case PM_DEVICE_ACTION_TURN_OFF: + return -ENOTSUP; + case PM_DEVICE_ACTION_RESUME: + if (!data->is_powered) { + return -EAGAIN; + } + break; + default: + break; + } + + return 0; +} + +static int pd_gpio_monitor_init(const struct device *dev) +{ + const struct pd_gpio_monitor_config *config = dev->config; + struct pd_gpio_monitor_data *data = dev->data; + int rc; + + data->dev = dev; + + if (!gpio_is_ready_dt(&config->power_good_gpio)) { + LOG_ERR("GPIO port %s is not ready", config->power_good_gpio.port->name); + return -ENODEV; + } + + rc = gpio_pin_configure_dt(&config->power_good_gpio, GPIO_INPUT); + if (rc) { + LOG_ERR("Failed to configure GPIO"); + goto unconfigure_pin; + } + + rc = gpio_pin_interrupt_configure_dt(&config->power_good_gpio, GPIO_INT_EDGE_BOTH); + if (rc) { + gpio_pin_configure_dt(&config->power_good_gpio, GPIO_DISCONNECTED); + LOG_ERR("Failed to configure GPIO interrupt"); + goto unconfigure_interrupt; + } + + gpio_init_callback(&data->callback, pd_gpio_monitor_callback, + BIT(config->power_good_gpio.pin)); + rc = gpio_add_callback_dt(&config->power_good_gpio, &data->callback); + if (rc) { + LOG_ERR("Failed to add GPIO callback"); + goto remove_callback; + } + + pm_device_init_suspended(dev); + return pm_device_runtime_enable(dev); +remove_callback: + gpio_remove_callback(config->power_good_gpio.port, &data->callback); +unconfigure_interrupt: + gpio_pin_interrupt_configure_dt(&config->power_good_gpio, GPIO_INT_DISABLE); +unconfigure_pin: + gpio_pin_configure_dt(&config->power_good_gpio, GPIO_DISCONNECTED); + return rc; +} + +#define POWER_DOMAIN_DEVICE(inst) \ + static const struct pd_gpio_monitor_config pd_gpio_monitor_config_##inst = { \ + .power_good_gpio = GPIO_DT_SPEC_INST_GET(inst, gpios), \ + }; \ + static struct pd_gpio_monitor_data pd_gpio_monitor_data_##inst; \ + PM_DEVICE_DT_INST_DEFINE(inst, pd_gpio_monitor_pm_action); \ + DEVICE_DT_INST_DEFINE(inst, pd_gpio_monitor_init, \ + PM_DEVICE_DT_INST_GET(inst), &pd_gpio_monitor_data_##inst, \ + &pd_gpio_monitor_config_##inst, POST_KERNEL, \ + CONFIG_POWER_DOMAIN_INIT_PRIORITY, NULL); + +DT_INST_FOREACH_STATUS_OKAY(POWER_DOMAIN_DEVICE) diff --git a/drivers/power_domain/power_domain_intel_adsp.c b/drivers/power_domain/power_domain_intel_adsp.c index c0ca87b652e..2fdd0ecf62e 100644 --- a/drivers/power_domain/power_domain_intel_adsp.c +++ b/drivers/power_domain/power_domain_intel_adsp.c @@ -76,14 +76,14 @@ static int pd_intel_adsp_init(const struct device *dev) #define DT_DRV_COMPAT intel_adsp_power_domain -#define POWER_DOMAIN_DEVICE(id) \ - static struct pg_bits pd_pg_reg##id = { \ - .SPA_bit = DT_INST_PROP(id, bit_position), \ - .CPA_bit = DT_INST_PROP(id, bit_position), \ - }; \ - PM_DEVICE_DT_INST_DEFINE(id, pd_intel_adsp_pm_action); \ - DEVICE_DT_INST_DEFINE(id, pd_intel_adsp_init, PM_DEVICE_DT_INST_GET(id),\ - &pd_pg_reg##id, NULL, POST_KERNEL, \ - CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, NULL); +#define POWER_DOMAIN_DEVICE(id) \ + static struct pg_bits pd_pg_reg##id = { \ + .SPA_bit = DT_INST_PROP(id, bit_position), \ + .CPA_bit = DT_INST_PROP(id, bit_position), \ + }; \ + PM_DEVICE_DT_INST_DEFINE(id, pd_intel_adsp_pm_action); \ + DEVICE_DT_INST_DEFINE(id, pd_intel_adsp_init, PM_DEVICE_DT_INST_GET(id), \ + &pd_pg_reg##id, NULL, POST_KERNEL, \ + CONFIG_POWER_DOMAIN_INIT_PRIORITY, NULL); DT_INST_FOREACH_STATUS_OKAY(POWER_DOMAIN_DEVICE) diff --git a/dts/bindings/power-domain/power-domain-gpio-monitor.yaml b/dts/bindings/power-domain/power-domain-gpio-monitor.yaml new file mode 100644 index 00000000000..d7dfd8779b7 --- /dev/null +++ b/dts/bindings/power-domain/power-domain-gpio-monitor.yaml @@ -0,0 +1,22 @@ +# Copyright (c) 2023 Google LLC +# SPDX-License-Identifier: Apache-2.0 + +description: | + Simple monitorig power domain + + This power domain monitors the state of a GPIO pin to detect whether a power + rail is on/off. Therefore, performing resume/suspend on power domain won't + change physical state of power rails and those action won't be triggerd on + child nodes. Additionally, due to the asynchronous nature of monitoring a + pending transaction won't be interrupted by power state change. + +compatible: "power-domain-gpio-monitor" + +include: power-domain.yaml + +properties: + gpios: + type: phandle-array + required: true + description: | + GPIO to use to sense if rail is powered on.