1 /*
2  * Copyright (c) 2024 Texas Instruments Incorporated
3  * Copyright (c) 2024 BayLibre, SAS
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #define DT_DRV_COMPAT ti_cc23x0_gpio
9 
10 #include <zephyr/types.h>
11 #include <zephyr/device.h>
12 #include <zephyr/irq.h>
13 #include <zephyr/drivers/gpio.h>
14 #include <zephyr/drivers/gpio/gpio_utils.h>
15 
16 #include <driverlib/clkctl.h>
17 #include <driverlib/gpio.h>
18 #include <inc/hw_ioc.h>
19 
20 #define IOC_ADDR(index)       (IOC_BASE + IOC_O_IOC0 + (sizeof(uint32_t) * (index)))
21 
22 struct gpio_cc23x0_config {
23 	/* gpio_driver_config needs to be first */
24 	struct gpio_driver_config common;
25 };
26 
27 struct gpio_cc23x0_data {
28 	/* gpio_driver_data needs to be first */
29 	struct gpio_driver_data common;
30 	sys_slist_t callbacks;
31 };
32 
set_pin_mask_non_atomic(uint8_t index,uint32_t registerBaseAddress)33 static void set_pin_mask_non_atomic(uint8_t index, uint32_t registerBaseAddress)
34 {
35 	GPIOSetConfigDio(GPIO_BASE + registerBaseAddress, BIT(index));
36 }
37 
gpio_cc23x0_config(const struct device * port,gpio_pin_t pin,gpio_flags_t flags)38 static int gpio_cc23x0_config(const struct device *port, gpio_pin_t pin, gpio_flags_t flags)
39 {
40 	uint32_t config = 0;
41 	uint32_t iocfg_reg = IOC_ADDR(pin);
42 	gpio_flags_t direction = flags & GPIO_DIR_MASK;
43 
44 	if (flags & GPIO_PULL_UP) {
45 		config |= IOC_IOC0_PULLCTL_PULL_UP;
46 	} else if (flags & GPIO_PULL_DOWN) {
47 		config |= IOC_IOC0_PULLCTL_PULL_DOWN;
48 	} else {
49 		config |= IOC_IOC0_PULLCTL_PULL_DIS;
50 	}
51 
52 	if (!(flags & GPIO_SINGLE_ENDED)) {
53 		config |= IOC_IOC0_IOMODE_NORMAL;
54 	} else {
55 		if (flags & GPIO_LINE_OPEN_DRAIN) {
56 			config |= IOC_IOC0_IOMODE_OPEND;
57 		} else {
58 			config |= IOC_IOC0_IOMODE_OPENS;
59 		}
60 	}
61 	if (direction & GPIO_INPUT) {
62 		config |= IOC_IOC0_INPEN_EN | IOC_IOC0_HYSTEN_EN;
63 	}
64 
65 	GPIOSetConfigDio(iocfg_reg, config);
66 
67 	if (flags & GPIO_OUTPUT) {
68 		if (flags & GPIO_OUTPUT_INIT_HIGH) {
69 			GPIOSetDio(pin);
70 		} else if (flags & GPIO_OUTPUT_INIT_LOW) {
71 			GPIOClearDio(pin);
72 		}
73 		GPIOSetOutputEnableDio(pin, GPIO_OUTPUT_ENABLE);
74 	} else {
75 		GPIOSetOutputEnableDio(pin, GPIO_OUTPUT_DISABLE);
76 	}
77 	return 0;
78 }
79 
80 #ifdef CONFIG_GPIO_GET_CONFIG
gpio_cc23x0_get_config(const struct device * port,gpio_pin_t pin,gpio_flags_t * flags)81 static int gpio_cc23x0_get_config(const struct device *port, gpio_pin_t pin, gpio_flags_t *flags)
82 {
83 	uint32_t out_flag = 0;
84 	uint32_t iocfg_reg = IOC_ADDR(pin);
85 	uint32_t config = GPIOGetConfigDio(iocfg_reg);
86 
87 	/* GPIO input/output configuration flags */
88 	if (config & IOC_IOC0_INPEN_EN) {
89 		out_flag |= GPIO_INPUT;
90 	}
91 
92 	if (GPIOGetOutputEnableDio(pin)) {
93 		out_flag |= GPIO_OUTPUT;
94 
95 		if (GPIOReadDio(pin)) {
96 			out_flag |= GPIO_OUTPUT_INIT_HIGH;
97 		} else {
98 			/* This is the default value. If not explicitly set,
99 			 * the returned config will not be symmetric
100 			 */
101 			out_flag |= GPIO_OUTPUT_INIT_LOW;
102 		}
103 	}
104 
105 	/* GPIO interrupt configuration flags */
106 	if ((config & IOC_IOC0_EDGEDET_M) != IOC_IOC0_EDGEDET_EDGE_DIS) {
107 		if (config & IOC_IOC0_EDGEDET_EDGE_POS) {
108 			out_flag |= GPIO_INT_EDGE_RISING;
109 		}
110 
111 		if (config & IOC_IOC0_EDGEDET_EDGE_NEG) {
112 			out_flag |= GPIO_INT_EDGE_FALLING;
113 		}
114 	} else {
115 		/* This is the default value. If not explicitly set,
116 		 * the returned config will not be symmetric
117 		 */
118 		out_flag |= GPIO_INT_DISABLE;
119 	}
120 
121 	/* GPIO pin drive flags */
122 	if (config & IOC_IOC0_IOMODE_OPENS) {
123 		out_flag |= GPIO_OPEN_SOURCE;
124 	}
125 
126 	if (config & IOC_IOC0_IOMODE_OPEND) {
127 		out_flag |= IOC_IOC0_IOMODE_OPEND;
128 	}
129 
130 	if (config & IOC_IOC0_PULLCTL_PULL_UP) {
131 		out_flag |= GPIO_PULL_UP;
132 	}
133 
134 	if (config & IOC_IOC0_PULLCTL_PULL_DOWN) {
135 		out_flag |= GPIO_PULL_DOWN;
136 	}
137 
138 	*flags = out_flag;
139 
140 	return 0;
141 }
142 #endif
143 
gpio_cc23x0_port_get_raw(const struct device * port,uint32_t * value)144 static int gpio_cc23x0_port_get_raw(const struct device *port, uint32_t *value)
145 {
146 	*value = GPIOReadMultiDio(GPIO_DIO_ALL_MASK);
147 
148 	return 0;
149 }
150 
gpio_cc23x0_port_set_masked_raw(const struct device * port,uint32_t mask,uint32_t value)151 static int gpio_cc23x0_port_set_masked_raw(const struct device *port, uint32_t mask, uint32_t value)
152 {
153 	GPIOWriteMultiDio(mask, value);
154 
155 	return 0;
156 }
157 
gpio_cc23x0_port_set_bits_raw(const struct device * port,uint32_t mask)158 static int gpio_cc23x0_port_set_bits_raw(const struct device *port, uint32_t mask)
159 {
160 	GPIOSetMultiDio(mask);
161 
162 	return 0;
163 }
164 
gpio_cc23x0_port_clear_bits_raw(const struct device * port,uint32_t mask)165 static int gpio_cc23x0_port_clear_bits_raw(const struct device *port, uint32_t mask)
166 {
167 	GPIOClearMultiDio(mask);
168 
169 	return 0;
170 }
171 
gpio_cc23x0_port_toggle_bits(const struct device * port,uint32_t mask)172 static int gpio_cc23x0_port_toggle_bits(const struct device *port, uint32_t mask)
173 {
174 	GPIOToggleMultiDio(mask);
175 
176 	return 0;
177 }
178 
gpio_cc23x0xx_pin_interrupt_configure(const struct device * port,gpio_pin_t pin,enum gpio_int_mode mode,enum gpio_int_trig trig)179 static int gpio_cc23x0xx_pin_interrupt_configure(const struct device *port, gpio_pin_t pin,
180 						 enum gpio_int_mode mode, enum gpio_int_trig trig)
181 {
182 	if (mode == GPIO_INT_MODE_LEVEL) {
183 		return ENOTSUP;
184 	}
185 
186 	uint32_t config = GPIOGetConfigDio(IOC_ADDR(pin)) & ~IOC_IOC0_EDGEDET_M;
187 
188 	if (mode == GPIO_INT_MODE_DISABLED) {
189 		config |= IOC_IOC1_EDGEDET_EDGE_DIS;
190 
191 		GPIOSetConfigDio(IOC_ADDR(pin), config);
192 
193 		/* Disable interrupt mask */
194 		set_pin_mask_non_atomic(pin, GPIO_O_IMCLR);
195 
196 	} else if (mode == GPIO_INT_MODE_EDGE) {
197 		switch (trig) {
198 		case GPIO_INT_TRIG_LOW:
199 			config |= IOC_IOC1_EDGEDET_EDGE_NEG;
200 			break;
201 		case GPIO_INT_TRIG_HIGH:
202 			config |= IOC_IOC1_EDGEDET_EDGE_POS;
203 			break;
204 		case GPIO_INT_TRIG_BOTH:
205 			config |= IOC_IOC1_EDGEDET_EDGE_BOTH;
206 			break;
207 		default:
208 			return ENOTSUP;
209 		}
210 
211 		GPIOSetConfigDio(IOC_ADDR(pin), config);
212 
213 		/* Enable interrupt mask */
214 		set_pin_mask_non_atomic(pin, GPIO_O_ICLR);
215 		set_pin_mask_non_atomic(pin, GPIO_O_IMSET);
216 	}
217 
218 	return 0;
219 }
220 
gpio_cc23x0_manage_callback(const struct device * port,struct gpio_callback * callback,bool set)221 static int gpio_cc23x0_manage_callback(const struct device *port, struct gpio_callback *callback,
222 				       bool set)
223 {
224 	struct gpio_cc23x0_data *data = port->data;
225 
226 	return gpio_manage_callback(&data->callbacks, callback, set);
227 }
228 
gpio_cc23x0_get_pending_int(const struct device * dev)229 static uint32_t gpio_cc23x0_get_pending_int(const struct device *dev)
230 {
231 	return GPIOGetEventMultiDio(GPIO_DIO_ALL_MASK);
232 }
233 
gpio_cc23x0_isr(const struct device * dev)234 static void gpio_cc23x0_isr(const struct device *dev)
235 {
236 	struct gpio_cc23x0_data *data = dev->data;
237 
238 	uint32_t status = GPIOGetEventMultiDio(GPIO_DIO_ALL_MASK);
239 
240 	GPIOClearEventMultiDio(status);
241 
242 	gpio_fire_callbacks(&data->callbacks, dev, status);
243 }
244 
gpio_cc23x0_init(const struct device * dev)245 static int gpio_cc23x0_init(const struct device *dev)
246 {
247 	/* Enable GPIO domain clock */
248 	CLKCTLEnable(CLKCTL_BASE, CLKCTL_GPIO);
249 
250 	/* Enable IRQ */
251 	IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), gpio_cc23x0_isr,
252 		    DEVICE_DT_INST_GET(0), 0);
253 
254 	irq_enable(DT_INST_IRQN(0));
255 
256 	return 0;
257 }
258 
259 static DEVICE_API(gpio, gpio_cc23x0_driver_api) = {
260 	.pin_configure = gpio_cc23x0_config,
261 #ifdef CONFIG_GPIO_GET_CONFIG
262 	.pin_get_config = gpio_cc23x0_get_config,
263 #endif
264 	.port_get_raw = gpio_cc23x0_port_get_raw,
265 	.port_set_masked_raw = gpio_cc23x0_port_set_masked_raw,
266 	.port_set_bits_raw = gpio_cc23x0_port_set_bits_raw,
267 	.port_clear_bits_raw = gpio_cc23x0_port_clear_bits_raw,
268 	.port_toggle_bits = gpio_cc23x0_port_toggle_bits,
269 	.pin_interrupt_configure = gpio_cc23x0xx_pin_interrupt_configure,
270 	.manage_callback = gpio_cc23x0_manage_callback,
271 	.get_pending_int = gpio_cc23x0_get_pending_int,
272 };
273 
274 static const struct gpio_cc23x0_config gpio_cc23x0_config_0 = {
275 	.common = {/* Read ngpios from DT */
276 		   .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(0)}};
277 
278 static struct gpio_cc23x0_data gpio_cc23x0_data_0;
279 
280 DEVICE_DT_INST_DEFINE(0, gpio_cc23x0_init, NULL, &gpio_cc23x0_data_0, &gpio_cc23x0_config_0,
281 		      PRE_KERNEL_1, CONFIG_GPIO_INIT_PRIORITY, &gpio_cc23x0_driver_api);
282