1 /*
2  * Copyright (c) 2022, Commonwealth Scientific and Industrial Research
3  * Organisation (CSIRO) ABN 41 687 119 230.
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #define DT_DRV_COMPAT power_domain_gpio
9 
10 #include <zephyr/kernel.h>
11 #include <zephyr/drivers/gpio.h>
12 #include <zephyr/pm/device.h>
13 #include <zephyr/pm/device_runtime.h>
14 
15 #include <zephyr/logging/log.h>
16 LOG_MODULE_REGISTER(power_domain_gpio, CONFIG_POWER_DOMAIN_LOG_LEVEL);
17 
18 struct pd_gpio_config {
19 	struct gpio_dt_spec enable;
20 	uint32_t startup_delay_us;
21 	uint32_t off_on_delay_us;
22 };
23 
24 struct pd_gpio_data {
25 	k_timeout_t next_boot;
26 };
27 
28 struct pd_visitor_context {
29 	const struct device *domain;
30 	enum pm_device_action action;
31 };
32 
33 #ifdef CONFIG_PM_DEVICE_POWER_DOMAIN
34 
pd_on_domain_visitor(const struct device * dev,void * context)35 static int pd_on_domain_visitor(const struct device *dev, void *context)
36 {
37 	struct pd_visitor_context *visitor_context = context;
38 
39 	/* Only run action if the device is on the specified domain */
40 	if (!dev->pm || (dev->pm_base->domain != visitor_context->domain)) {
41 		return 0;
42 	}
43 
44 	(void)pm_device_action_run(dev, visitor_context->action);
45 	return 0;
46 }
47 
48 #endif
49 
pd_gpio_pm_action(const struct device * dev,enum pm_device_action action)50 static int pd_gpio_pm_action(const struct device *dev,
51 			     enum pm_device_action action)
52 {
53 #ifdef CONFIG_PM_DEVICE_POWER_DOMAIN
54 	struct pd_visitor_context context = {.domain = dev};
55 #endif
56 	const struct pd_gpio_config *cfg = dev->config;
57 	struct pd_gpio_data *data = dev->data;
58 	int64_t next_boot_ticks;
59 	int rc = 0;
60 
61 	/* Validate that blocking API's can be used */
62 	if (!k_can_yield()) {
63 		LOG_ERR("Blocking actions cannot run in this context");
64 		return -ENOTSUP;
65 	}
66 
67 	switch (action) {
68 	case PM_DEVICE_ACTION_RESUME:
69 		/* Wait until we can boot again */
70 		k_sleep(data->next_boot);
71 		/* Switch power on */
72 		gpio_pin_set_dt(&cfg->enable, 1);
73 		LOG_INF("%s is now ON", dev->name);
74 		/* Wait for domain to come up */
75 		k_sleep(K_USEC(cfg->startup_delay_us));
76 #ifdef CONFIG_PM_DEVICE_POWER_DOMAIN
77 		/* Notify devices on the domain they are now powered */
78 		context.action = PM_DEVICE_ACTION_TURN_ON;
79 		(void)device_supported_foreach(dev, pd_on_domain_visitor, &context);
80 #endif
81 		break;
82 	case PM_DEVICE_ACTION_SUSPEND:
83 #ifdef CONFIG_PM_DEVICE_POWER_DOMAIN
84 		/* Notify devices on the domain that power is going down */
85 		context.action = PM_DEVICE_ACTION_TURN_OFF;
86 		(void)device_supported_foreach(dev, pd_on_domain_visitor, &context);
87 #endif
88 		/* Switch power off */
89 		gpio_pin_set_dt(&cfg->enable, 0);
90 		LOG_INF("%s is now OFF", dev->name);
91 		/* Store next time we can boot */
92 		next_boot_ticks = k_uptime_ticks() + k_us_to_ticks_ceil32(cfg->off_on_delay_us);
93 		data->next_boot = K_TIMEOUT_ABS_TICKS(next_boot_ticks);
94 		break;
95 	case PM_DEVICE_ACTION_TURN_ON:
96 		/* Actively control the enable pin now that the device is powered */
97 		gpio_pin_configure_dt(&cfg->enable, GPIO_OUTPUT_INACTIVE);
98 		LOG_DBG("%s is OFF and powered", dev->name);
99 		break;
100 	case PM_DEVICE_ACTION_TURN_OFF:
101 		/* Let the enable pin float while device is not powered */
102 		gpio_pin_configure_dt(&cfg->enable, GPIO_DISCONNECTED);
103 		LOG_DBG("%s is OFF and not powered", dev->name);
104 		break;
105 	default:
106 		rc = -ENOTSUP;
107 	}
108 
109 	return rc;
110 }
111 
pd_gpio_init(const struct device * dev)112 static int pd_gpio_init(const struct device *dev)
113 {
114 	const struct pd_gpio_config *cfg = dev->config;
115 	struct pd_gpio_data *data = dev->data;
116 
117 	if (!gpio_is_ready_dt(&cfg->enable)) {
118 		LOG_ERR("GPIO port %s is not ready", cfg->enable.port->name);
119 		return -ENODEV;
120 	}
121 	/* We can't know how long the domain has been off for before boot */
122 	data->next_boot = K_TIMEOUT_ABS_US(cfg->off_on_delay_us);
123 
124 	/* Boot according to state */
125 	return pm_device_driver_init(dev, pd_gpio_pm_action);
126 }
127 
128 #define POWER_DOMAIN_DEVICE(id)							\
129 	static const struct pd_gpio_config pd_gpio_##id##_cfg = {		\
130 		.enable = GPIO_DT_SPEC_INST_GET(id, enable_gpios),		\
131 		.startup_delay_us = DT_INST_PROP(id, startup_delay_us),		\
132 		.off_on_delay_us = DT_INST_PROP(id, off_on_delay_us),		\
133 	};									\
134 	static struct pd_gpio_data pd_gpio_##id##_data;				\
135 	PM_DEVICE_DT_INST_DEFINE(id, pd_gpio_pm_action);			\
136 	DEVICE_DT_INST_DEFINE(id, pd_gpio_init, PM_DEVICE_DT_INST_GET(id),	\
137 			      &pd_gpio_##id##_data, &pd_gpio_##id##_cfg,	\
138 			      POST_KERNEL, CONFIG_POWER_DOMAIN_GPIO_INIT_PRIORITY,	\
139 			      NULL);
140 
141 DT_INST_FOREACH_STATUS_OKAY(POWER_DOMAIN_DEVICE)
142