1 /*
2 * Copyright (c) 2022-2023 Vestas Wind Systems A/S
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/devicetree.h>
8 #include <zephyr/drivers/gpio.h>
9 #include <zephyr/init.h>
10 #include <zephyr/logging/log.h>
11
12 LOG_MODULE_REGISTER(gpio_hogs, CONFIG_GPIO_LOG_LEVEL);
13
14 struct gpio_hog_dt_spec {
15 gpio_pin_t pin;
16 gpio_flags_t flags;
17 };
18
19 struct gpio_hogs {
20 const struct device *port;
21 const struct gpio_hog_dt_spec *specs;
22 size_t num_specs;
23 };
24
25 /* Static initializer for a struct gpio_hog_dt_spec */
26 #define GPIO_HOG_DT_SPEC_GET_BY_IDX(node_id, idx) \
27 { \
28 .pin = DT_GPIO_HOG_PIN_BY_IDX(node_id, idx), \
29 .flags = DT_GPIO_HOG_FLAGS_BY_IDX(node_id, idx) | \
30 COND_CODE_1(DT_PROP(node_id, input), (GPIO_INPUT), \
31 (COND_CODE_1(DT_PROP(node_id, output_low), \
32 (GPIO_OUTPUT_INACTIVE), \
33 (COND_CODE_1(DT_PROP(node_id, output_high), \
34 (GPIO_OUTPUT_ACTIVE), (0)))))), \
35 }
36
37 /* Expands to 1 if node_id is a GPIO controller, 0 otherwise */
38 #define GPIO_HOGS_NODE_IS_GPIO_CTLR(node_id) \
39 DT_PROP_OR(node_id, gpio_controller, 0)
40
41 /* Expands to 1 if node_id is a GPIO hog, empty otherwise */
42 #define GPIO_HOGS_NODE_IS_GPIO_HOG(node_id) \
43 IF_ENABLED(DT_PROP_OR(node_id, gpio_hog, 0), 1)
44
45 /* Expands to 1 if GPIO controller node_id has GPIO hog children, 0 otherwise */
46 #define GPIO_HOGS_GPIO_CTLR_HAS_HOGS(node_id) \
47 COND_CODE_0( \
48 IS_EMPTY(DT_FOREACH_CHILD_STATUS_OKAY(node_id, \
49 GPIO_HOGS_NODE_IS_GPIO_HOG)), \
50 (1), (0))
51
52 /* Called for GPIO hog indexes */
53 #define GPIO_HOGS_INIT_GPIO_HOG_BY_IDX(idx, node_id) \
54 GPIO_HOG_DT_SPEC_GET_BY_IDX(node_id, idx)
55
56 /* Called for GPIO hog dts nodes */
57 #define GPIO_HOGS_INIT_GPIO_HOGS(node_id) \
58 LISTIFY(DT_NUM_GPIO_HOGS(node_id), \
59 GPIO_HOGS_INIT_GPIO_HOG_BY_IDX, (,), node_id),
60
61 /* Called for GPIO controller dts node children */
62 #define GPIO_HOGS_COND_INIT_GPIO_HOGS(node_id) \
63 COND_CODE_0(IS_EMPTY(GPIO_HOGS_NODE_IS_GPIO_HOG(node_id)), \
64 (GPIO_HOGS_INIT_GPIO_HOGS(node_id)), ())
65
66 /* Called for each GPIO controller dts node which has GPIO hog children */
67 #define GPIO_HOGS_INIT_GPIO_CTLR(node_id) \
68 { \
69 .port = DEVICE_DT_GET(node_id), \
70 .specs = (const struct gpio_hog_dt_spec []) { \
71 DT_FOREACH_CHILD_STATUS_OKAY(node_id, \
72 GPIO_HOGS_COND_INIT_GPIO_HOGS) \
73 }, \
74 .num_specs = \
75 DT_FOREACH_CHILD_STATUS_OKAY_SEP(node_id, \
76 DT_NUM_GPIO_HOGS, (+)), \
77 },
78
79 /* Called for each GPIO controller dts node */
80 #define GPIO_HOGS_COND_INIT_GPIO_CTLR(node_id) \
81 IF_ENABLED(GPIO_HOGS_GPIO_CTLR_HAS_HOGS(node_id), \
82 (GPIO_HOGS_INIT_GPIO_CTLR(node_id)))
83
84 /* Called for each dts node */
85 #define GPIO_HOGS_COND_INIT(node_id) \
86 IF_ENABLED(GPIO_HOGS_NODE_IS_GPIO_CTLR(node_id), \
87 (GPIO_HOGS_COND_INIT_GPIO_CTLR(node_id)))
88
89 static const struct gpio_hogs gpio_hogs[] = {
90 DT_FOREACH_STATUS_OKAY_NODE(GPIO_HOGS_COND_INIT)
91 };
92
gpio_hogs_init(void)93 static int gpio_hogs_init(void)
94 {
95 const struct gpio_hogs *hogs;
96 const struct gpio_hog_dt_spec *spec;
97 int err;
98 int i;
99 int j;
100
101
102 for (i = 0; i < ARRAY_SIZE(gpio_hogs); i++) {
103 hogs = &gpio_hogs[i];
104
105 if (!device_is_ready(hogs->port)) {
106 LOG_ERR("GPIO port %s not ready", hogs->port->name);
107 return -ENODEV;
108 }
109
110 for (j = 0; j < hogs->num_specs; j++) {
111 spec = &hogs->specs[j];
112
113 err = gpio_pin_configure(hogs->port, spec->pin, spec->flags);
114 if (err < 0) {
115 LOG_ERR("failed to configure GPIO hog for port %s pin %u (err %d)",
116 hogs->port->name, spec->pin, err);
117 return err;
118 }
119 }
120 }
121
122 return 0;
123 }
124
125 SYS_INIT(gpio_hogs_init, POST_KERNEL, CONFIG_GPIO_HOGS_INIT_PRIORITY);
126