From d1ab0f6b6ebb071f2d2012e8f69a42f4f03251e7 Mon Sep 17 00:00:00 2001 From: Jordan Yates Date: Sat, 12 Mar 2022 15:40:35 +1000 Subject: [PATCH] power_domain: gpio: use on-off and startup time Respect the configured values for how long the domain takes to turn on, and how long the domain needs to be off for before it can be repowered. As these actions can block, guard the transition function with pm_device_action_can_block. To avoid system PM being able to turn the domain off but not back on again, guard the entire implementation. Signed-off-by: Jordan Yates --- drivers/power_domain/Kconfig | 1 + drivers/power_domain/power_domain_gpio.c | 20 +++++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/power_domain/Kconfig b/drivers/power_domain/Kconfig index da8118d854b..3e759a41a89 100644 --- a/drivers/power_domain/Kconfig +++ b/drivers/power_domain/Kconfig @@ -11,5 +11,6 @@ if POWER_DOMAIN config POWER_DOMAIN_GPIO bool "GPIO controlled power domain" depends on GPIO + depends on TIMEOUT_64BIT endif diff --git a/drivers/power_domain/power_domain_gpio.c b/drivers/power_domain/power_domain_gpio.c index 978f01c4458..44c6ea2af9f 100644 --- a/drivers/power_domain/power_domain_gpio.c +++ b/drivers/power_domain/power_domain_gpio.c @@ -22,20 +22,32 @@ struct pd_gpio_config { }; struct pd_gpio_data { - k_timeout_t last_boot; + k_timeout_t next_boot; }; static int pd_gpio_pm_action(const struct device *dev, enum pm_device_action action) { const struct pd_gpio_config *cfg = dev->config; + struct pd_gpio_data *data = dev->data; + int64_t next_boot_ticks; int rc = 0; + /* Validate that blocking API's can be used */ + if (!k_can_yield()) { + LOG_ERR("Blocking actions cannot run in this context"); + return -ENOTSUP; + } + switch (action) { case PM_DEVICE_ACTION_RESUME: + /* Wait until we can boot again */ + k_sleep(data->next_boot); /* Switch power on */ gpio_pin_set_dt(&cfg->enable, 1); LOG_DBG("%s is now ON", dev->name); + /* Wait for domain to come up */ + k_sleep(K_USEC(cfg->startup_delay_us)); /* Notify supported devices they are now powered */ pm_device_children_action_run(dev, PM_DEVICE_ACTION_TURN_ON, NULL); break; @@ -45,6 +57,9 @@ static int pd_gpio_pm_action(const struct device *dev, /* Switch power off */ gpio_pin_set_dt(&cfg->enable, 0); LOG_DBG("%s is now OFF and powered", dev->name); + /* Store next time we can boot */ + next_boot_ticks = k_uptime_ticks() + k_us_to_ticks_ceil32(cfg->off_on_delay_us); + data->next_boot = K_TIMEOUT_ABS_TICKS(next_boot_ticks); break; case PM_DEVICE_ACTION_TURN_ON: /* Actively control the enable pin now that the device is powered */ @@ -66,12 +81,15 @@ static int pd_gpio_pm_action(const struct device *dev, static int pd_gpio_init(const struct device *dev) { const struct pd_gpio_config *cfg = dev->config; + struct pd_gpio_data *data = dev->data; int rc; if (!device_is_ready(cfg->enable.port)) { LOG_ERR("GPIO port %s is not ready", cfg->enable.port->name); return -ENODEV; } + /* We can't know how long the domain has been off for before boot */ + data->next_boot = K_TIMEOUT_ABS_US(cfg->off_on_delay_us); if (pm_device_on_power_domain(dev)) { /* Device is unpowered */