Skip to content

Commit 06cfbd4

Browse files
drivers: power_domain: Introduce a gpio monitor driver
Power rails of some peripherals are controlled externally. This is a case in embedded controllers, where the power of some I2C devices are managed by the main application processor. To ensure that zephyr drivers access the devices where is powered on, introduce a "monitoring" power domain. It works by registering interrupt handler with gpio a pin, so that when power state changes, it will notify relevant drivers. Additionaly add CONFIG_POWER_DOMAIN_INIT_PRIORITY to replace harcoded init priority. Fixes: #51349 Signed-off-by: Albert Jakieła <[email protected]>
1 parent a17d495 commit 06cfbd4

File tree

6 files changed

+206
-20
lines changed

6 files changed

+206
-20
lines changed

drivers/power_domain/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@
44
zephyr_library()
55

66
zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_GPIO power_domain_gpio.c)
7+
zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_GPIO_MONITOR power_domain_gpio_monitor.c)
78
zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_INTEL_ADSP power_domain_intel_adsp.c)

drivers/power_domain/Kconfig

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ module = POWER_DOMAIN
1212
module-str = power_domain
1313
source "subsys/logging/Kconfig.template.log_config"
1414

15+
config POWER_DOMAIN_INIT_PRIORITY
16+
int "Power domain init priority"
17+
default 75
18+
help
19+
Power domain initialization priority.
20+
1521
config POWER_DOMAIN_GPIO
1622
bool "GPIO controlled power domain"
1723
default y
@@ -28,4 +34,11 @@ config POWER_DOMAIN_INTEL_ADSP
2834
help
2935
Include Intel ADSP power domain control mechanisms
3036

37+
config POWER_DOMAIN_GPIO_MONITOR
38+
bool "GPIO monitor for sensing power on rail"
39+
default y
40+
depends on DT_HAS_POWER_DOMAIN_GPIO_MONITOR_ENABLED
41+
depends on GPIO
42+
select DEVICE_DEPS
43+
3144
endif

drivers/power_domain/power_domain_gpio.c

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -125,17 +125,17 @@ static int pd_gpio_init(const struct device *dev)
125125
return pm_device_driver_init(dev, pd_gpio_pm_action);
126126
}
127127

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, 75, \
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_INIT_PRIORITY, \
139139
NULL);
140140

141141
DT_INST_FOREACH_STATUS_OKAY(POWER_DOMAIN_DEVICE)
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
/*
2+
* Copyright (c) 2023 Google LLC
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT power_domain_gpio_monitor
8+
9+
#include <zephyr/kernel.h>
10+
#include <zephyr/drivers/gpio.h>
11+
#include <zephyr/pm/device.h>
12+
#include <zephyr/pm/device_runtime.h>
13+
14+
#include <zephyr/logging/log.h>
15+
16+
LOG_MODULE_REGISTER(power_domain_gpio_monitor, CONFIG_POWER_DOMAIN_LOG_LEVEL);
17+
18+
struct pd_gpio_monitor_config {
19+
struct gpio_dt_spec power_good_gpio;
20+
};
21+
22+
struct pd_gpio_monitor_data {
23+
struct gpio_callback callback;
24+
const struct device *dev;
25+
bool is_powered;
26+
};
27+
28+
struct pd_visitor_context {
29+
const struct device *domain;
30+
enum pm_device_action action;
31+
};
32+
33+
static int pd_on_domain_visitor(const struct device *dev, void *context)
34+
{
35+
struct pd_visitor_context *visitor_context = context;
36+
37+
/* Only run action if the device is on the specified domain */
38+
if (!dev->pm || (dev->pm->domain != visitor_context->domain)) {
39+
return 0;
40+
}
41+
42+
dev->pm->usage = 0;
43+
(void)pm_device_action_run(dev, visitor_context->action);
44+
return 0;
45+
}
46+
47+
static void pd_gpio_monitor_callback(const struct device *port,
48+
struct gpio_callback *cb, gpio_port_pins_t pins)
49+
{
50+
struct pd_gpio_monitor_data *data = CONTAINER_OF(cb, struct pd_gpio_monitor_data, callback);
51+
const struct pd_gpio_monitor_config *config = data->dev->config;
52+
const struct device *dev = data->dev;
53+
struct pd_visitor_context context = {.domain = dev};
54+
int rc;
55+
56+
rc = gpio_pin_get_dt(&config->power_good_gpio);
57+
if (rc < 0) {
58+
LOG_WRN("Failed to read gpio logic level");
59+
return;
60+
}
61+
62+
data->is_powered = rc;
63+
if (rc == 0) {
64+
context.action = PM_DEVICE_ACTION_SUSPEND;
65+
(void)device_supported_foreach(dev, pd_on_domain_visitor, &context);
66+
context.action = PM_DEVICE_ACTION_TURN_OFF;
67+
(void)device_supported_foreach(dev, pd_on_domain_visitor, &context);
68+
return;
69+
}
70+
71+
pm_device_children_action_run(data->dev, PM_DEVICE_ACTION_TURN_ON, NULL);
72+
}
73+
74+
static int pd_gpio_monitor_pm_action(const struct device *dev, enum pm_device_action action)
75+
{
76+
struct pd_gpio_monitor_data *data = dev->data;
77+
78+
switch (action) {
79+
case PM_DEVICE_ACTION_TURN_ON:
80+
case PM_DEVICE_ACTION_TURN_OFF:
81+
return -ENOTSUP;
82+
case PM_DEVICE_ACTION_RESUME:
83+
if (!data->is_powered) {
84+
return -EAGAIN;
85+
}
86+
break;
87+
default:
88+
break;
89+
}
90+
91+
return 0;
92+
}
93+
94+
static int pd_gpio_monitor_init(const struct device *dev)
95+
{
96+
const struct pd_gpio_monitor_config *config = dev->config;
97+
struct pd_gpio_monitor_data *data = dev->data;
98+
int rc;
99+
100+
data->dev = dev;
101+
102+
if (!gpio_is_ready_dt(&config->power_good_gpio)) {
103+
LOG_ERR("GPIO port %s is not ready", config->power_good_gpio.port->name);
104+
return -ENODEV;
105+
}
106+
107+
rc = gpio_pin_configure_dt(&config->power_good_gpio, GPIO_INPUT);
108+
if (rc) {
109+
LOG_ERR("Failed to configure GPIO");
110+
goto unconfigure_pin;
111+
}
112+
113+
rc = gpio_pin_interrupt_configure_dt(&config->power_good_gpio, GPIO_INT_EDGE_BOTH);
114+
if (rc) {
115+
gpio_pin_configure_dt(&config->power_good_gpio, GPIO_DISCONNECTED);
116+
LOG_ERR("Failed to configure GPIO interrupt");
117+
goto unconfigure_interrupt;
118+
}
119+
120+
gpio_init_callback(&data->callback, pd_gpio_monitor_callback,
121+
BIT(config->power_good_gpio.pin));
122+
rc = gpio_add_callback_dt(&config->power_good_gpio, &data->callback);
123+
if (rc) {
124+
LOG_ERR("Failed to add GPIO callback");
125+
goto remove_callback;
126+
}
127+
128+
pm_device_init_suspended(dev);
129+
return pm_device_runtime_enable(dev);
130+
remove_callback:
131+
gpio_remove_callback(config->power_good_gpio.port, &data->callback);
132+
unconfigure_interrupt:
133+
gpio_pin_interrupt_configure_dt(&config->power_good_gpio, GPIO_INT_DISABLE);
134+
unconfigure_pin:
135+
gpio_pin_configure_dt(&config->power_good_gpio, GPIO_DISCONNECTED);
136+
return rc;
137+
}
138+
139+
#define POWER_DOMAIN_DEVICE(inst) \
140+
static const struct pd_gpio_monitor_config pd_gpio_monitor_config_##inst = { \
141+
.power_good_gpio = GPIO_DT_SPEC_INST_GET(inst, gpios), \
142+
}; \
143+
static struct pd_gpio_monitor_data pd_gpio_monitor_data_##inst; \
144+
PM_DEVICE_DT_INST_DEFINE(inst, pd_gpio_monitor_pm_action); \
145+
DEVICE_DT_INST_DEFINE(inst, pd_gpio_monitor_init, \
146+
PM_DEVICE_DT_INST_GET(inst), &pd_gpio_monitor_data_##inst, \
147+
&pd_gpio_monitor_config_##inst, POST_KERNEL, \
148+
CONFIG_POWER_DOMAIN_INIT_PRIORITY, NULL);
149+
150+
DT_INST_FOREACH_STATUS_OKAY(POWER_DOMAIN_DEVICE)

drivers/power_domain/power_domain_intel_adsp.c

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,14 @@ static int pd_intel_adsp_init(const struct device *dev)
7676

7777
#define DT_DRV_COMPAT intel_adsp_power_domain
7878

79-
#define POWER_DOMAIN_DEVICE(id) \
80-
static struct pg_bits pd_pg_reg##id = { \
81-
.SPA_bit = DT_INST_PROP(id, bit_position), \
82-
.CPA_bit = DT_INST_PROP(id, bit_position), \
83-
}; \
84-
PM_DEVICE_DT_INST_DEFINE(id, pd_intel_adsp_pm_action); \
85-
DEVICE_DT_INST_DEFINE(id, pd_intel_adsp_init, PM_DEVICE_DT_INST_GET(id),\
86-
&pd_pg_reg##id, NULL, POST_KERNEL, \
87-
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, NULL);
79+
#define POWER_DOMAIN_DEVICE(id) \
80+
static struct pg_bits pd_pg_reg##id = { \
81+
.SPA_bit = DT_INST_PROP(id, bit_position), \
82+
.CPA_bit = DT_INST_PROP(id, bit_position), \
83+
}; \
84+
PM_DEVICE_DT_INST_DEFINE(id, pd_intel_adsp_pm_action); \
85+
DEVICE_DT_INST_DEFINE(id, pd_intel_adsp_init, PM_DEVICE_DT_INST_GET(id), \
86+
&pd_pg_reg##id, NULL, POST_KERNEL, \
87+
CONFIG_POWER_DOMAIN_INIT_PRIORITY, NULL);
8888

8989
DT_INST_FOREACH_STATUS_OKAY(POWER_DOMAIN_DEVICE)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Copyright (c) 2023 Google LLC
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: |
5+
Simple monitorig power domain
6+
7+
This power domain monitors the state of a GPIO pin to detect whether a power
8+
rail is on/off. Therefore, performing resume/suspend on power domain won't
9+
change physical state of power rails and those action won't be triggerd on
10+
child nodes. Additionally, due to the asynchronous nature of monitoring a
11+
pending transaction won't be interrupted by power state change.
12+
13+
compatible: "power-domain-gpio-monitor"
14+
15+
include: power-domain.yaml
16+
17+
properties:
18+
gpios:
19+
type: phandle-array
20+
required: true
21+
description: |
22+
GPIO to use to sense if rail is powered on.

0 commit comments

Comments
 (0)