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 <jordan.yates@data61.csiro.au>
This commit is contained in:
Jordan Yates 2022-03-12 15:40:35 +10:00 committed by Carles Cufí
commit d1ab0f6b6e
2 changed files with 20 additions and 1 deletions

View file

@ -11,5 +11,6 @@ if POWER_DOMAIN
config POWER_DOMAIN_GPIO
bool "GPIO controlled power domain"
depends on GPIO
depends on TIMEOUT_64BIT
endif

View file

@ -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 */