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