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_uart
9 
10 #include <zephyr/device.h>
11 #include <zephyr/drivers/uart.h>
12 #include <zephyr/drivers/pinctrl.h>
13 #include <zephyr/irq.h>
14 
15 #include <errno.h>
16 
17 #include <driverlib/uart.h>
18 #include <driverlib/clkctl.h>
19 
20 struct uart_cc23x0_config {
21 	uint32_t reg;
22 	uint32_t sys_clk_freq;
23 	const struct pinctrl_dev_config *pcfg;
24 };
25 
26 struct uart_cc23x0_data {
27 	struct uart_config uart_config;
28 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
29 	uart_irq_callback_user_data_t callback;
30 	void *user_data;
31 #endif /* CONFIG_UART_INTERRUPT_DRIVEN */
32 };
33 
uart_cc23x0_poll_in(const struct device * dev,unsigned char * c)34 static int uart_cc23x0_poll_in(const struct device *dev, unsigned char *c)
35 {
36 	const struct uart_cc23x0_config *config = dev->config;
37 
38 	if (!UARTCharAvailable(config->reg)) {
39 		return -1;
40 	}
41 
42 	*c = UARTGetCharNonBlocking(config->reg);
43 
44 	return 0;
45 }
46 
uart_cc23x0_poll_out(const struct device * dev,unsigned char c)47 static void uart_cc23x0_poll_out(const struct device *dev, unsigned char c)
48 {
49 	const struct uart_cc23x0_config *config = dev->config;
50 
51 	UARTPutChar(config->reg, c);
52 }
53 
uart_cc23x0_err_check(const struct device * dev)54 static int uart_cc23x0_err_check(const struct device *dev)
55 {
56 	const struct uart_cc23x0_config *config = dev->config;
57 	uint32_t flags = UARTGetRxError(config->reg);
58 	int error = 0;
59 
60 	error |= (flags & UART_RXERROR_FRAMING) ? UART_ERROR_FRAMING : 0;
61 	error |= (flags & UART_RXERROR_PARITY) ? UART_ERROR_PARITY : 0;
62 	error |= (flags & UART_RXERROR_BREAK) ? UART_BREAK : 0;
63 	error |= (flags & UART_RXERROR_OVERRUN) ? UART_ERROR_OVERRUN : 0;
64 
65 	UARTClearRxError(config->reg);
66 
67 	return error;
68 }
69 
uart_cc23x0_configure(const struct device * dev,const struct uart_config * cfg)70 static int uart_cc23x0_configure(const struct device *dev, const struct uart_config *cfg)
71 {
72 	const struct uart_cc23x0_config *config = dev->config;
73 	struct uart_cc23x0_data *data = dev->data;
74 	uint32_t line_ctrl = 0;
75 	bool flow_ctrl;
76 
77 	switch (cfg->parity) {
78 	case UART_CFG_PARITY_NONE:
79 		line_ctrl |= UART_CONFIG_PAR_NONE;
80 		break;
81 	case UART_CFG_PARITY_ODD:
82 		line_ctrl |= UART_CONFIG_PAR_ODD;
83 		break;
84 	case UART_CFG_PARITY_EVEN:
85 		line_ctrl |= UART_CONFIG_PAR_EVEN;
86 		break;
87 	case UART_CFG_PARITY_MARK:
88 		line_ctrl |= UART_CONFIG_PAR_ONE;
89 		break;
90 	case UART_CFG_PARITY_SPACE:
91 		line_ctrl |= UART_CONFIG_PAR_ZERO;
92 		break;
93 	default:
94 		return -EINVAL;
95 	}
96 
97 	switch (cfg->stop_bits) {
98 	case UART_CFG_STOP_BITS_1:
99 		line_ctrl |= UART_CONFIG_STOP_ONE;
100 		break;
101 	case UART_CFG_STOP_BITS_2:
102 		line_ctrl |= UART_CONFIG_STOP_TWO;
103 		break;
104 	case UART_CFG_STOP_BITS_0_5:
105 	case UART_CFG_STOP_BITS_1_5:
106 		return -ENOTSUP;
107 	default:
108 		return -EINVAL;
109 	}
110 
111 	switch (cfg->data_bits) {
112 	case UART_CFG_DATA_BITS_5:
113 		line_ctrl |= UART_CONFIG_WLEN_5;
114 		break;
115 	case UART_CFG_DATA_BITS_6:
116 		line_ctrl |= UART_CONFIG_WLEN_6;
117 		break;
118 	case UART_CFG_DATA_BITS_7:
119 		line_ctrl |= UART_CONFIG_WLEN_7;
120 		break;
121 	case UART_CFG_DATA_BITS_8:
122 		line_ctrl |= UART_CONFIG_WLEN_8;
123 		break;
124 	default:
125 		return -EINVAL;
126 	}
127 
128 	switch (cfg->flow_ctrl) {
129 	case UART_CFG_FLOW_CTRL_NONE:
130 		flow_ctrl = false;
131 		break;
132 	case UART_CFG_FLOW_CTRL_RTS_CTS:
133 		flow_ctrl = true;
134 		break;
135 	case UART_CFG_FLOW_CTRL_DTR_DSR:
136 		return -ENOTSUP;
137 	default:
138 		return -EINVAL;
139 	}
140 
141 	/* Disables UART before setting control registers */
142 	UARTConfigSetExpClk(config->reg, config->sys_clk_freq, cfg->baudrate, line_ctrl);
143 
144 	if (flow_ctrl) {
145 		UARTEnableCTS(config->reg);
146 		UARTEnableRTS(config->reg);
147 	} else {
148 		UARTDisableCTS(config->reg);
149 		UARTDisableRTS(config->reg);
150 	}
151 
152 	/* Re-enable UART */
153 	UARTEnable(config->reg);
154 
155 	/* Make use of the FIFO to reduce chances of data being lost */
156 	UARTEnableFifo(config->reg);
157 
158 	data->uart_config = *cfg;
159 
160 	return 0;
161 }
162 
163 #ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
uart_cc23x0_config_get(const struct device * dev,struct uart_config * cfg)164 static int uart_cc23x0_config_get(const struct device *dev, struct uart_config *cfg)
165 {
166 	const struct uart_cc23x0_data *data = dev->data;
167 
168 	*cfg = data->uart_config;
169 	return 0;
170 }
171 #endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */
172 
173 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
174 
uart_cc23x0_fifo_fill(const struct device * dev,const uint8_t * buf,int len)175 static int uart_cc23x0_fifo_fill(const struct device *dev, const uint8_t *buf, int len)
176 {
177 	const struct uart_cc23x0_config *config = dev->config;
178 	int n = 0;
179 
180 	while (n < len) {
181 		if (!UARTSpaceAvailable(config->reg)) {
182 			break;
183 		}
184 		UARTPutCharNonBlocking(config->reg, buf[n]);
185 		n++;
186 	}
187 
188 	return n;
189 }
190 
uart_cc23x0_fifo_read(const struct device * dev,uint8_t * buf,const int len)191 static int uart_cc23x0_fifo_read(const struct device *dev, uint8_t *buf, const int len)
192 {
193 	const struct uart_cc23x0_config *config = dev->config;
194 	int c, n;
195 
196 	n = 0;
197 	while (n < len) {
198 		if (!UARTCharAvailable(config->reg)) {
199 			break;
200 		}
201 		c = UARTGetCharNonBlocking(config->reg);
202 		buf[n++] = c;
203 	}
204 
205 	return n;
206 }
207 
uart_cc23x0_irq_tx_enable(const struct device * dev)208 static void uart_cc23x0_irq_tx_enable(const struct device *dev)
209 {
210 	const struct uart_cc23x0_config *config = dev->config;
211 
212 	UARTEnableInt(config->reg, UART_INT_TX);
213 }
214 
uart_cc23x0_irq_tx_disable(const struct device * dev)215 static void uart_cc23x0_irq_tx_disable(const struct device *dev)
216 {
217 	const struct uart_cc23x0_config *config = dev->config;
218 
219 	UARTDisableInt(config->reg, UART_INT_TX);
220 }
221 
uart_cc23x0_irq_tx_ready(const struct device * dev)222 static int uart_cc23x0_irq_tx_ready(const struct device *dev)
223 {
224 	const struct uart_cc23x0_config *config = dev->config;
225 
226 	return UARTSpaceAvailable(config->reg) ? 1 : 0;
227 }
228 
uart_cc23x0_irq_rx_enable(const struct device * dev)229 static void uart_cc23x0_irq_rx_enable(const struct device *dev)
230 {
231 	const struct uart_cc23x0_config *config = dev->config;
232 
233 	/* Trigger the ISR on both RX and Receive Timeout. This is to allow
234 	 * the use of the hardware FIFOs for more efficient operation
235 	 */
236 	UARTEnableInt(config->reg, UART_INT_RX | UART_INT_RT);
237 }
238 
uart_cc23x0_irq_rx_disable(const struct device * dev)239 static void uart_cc23x0_irq_rx_disable(const struct device *dev)
240 {
241 	const struct uart_cc23x0_config *config = dev->config;
242 
243 	UARTDisableInt(config->reg, UART_INT_RX | UART_INT_RT);
244 }
245 
uart_cc23x0_irq_tx_complete(const struct device * dev)246 static int uart_cc23x0_irq_tx_complete(const struct device *dev)
247 {
248 	const struct uart_cc23x0_config *config = dev->config;
249 
250 	return UARTBusy(config->reg) ? 0 : 1;
251 }
252 
uart_cc23x0_irq_rx_ready(const struct device * dev)253 static int uart_cc23x0_irq_rx_ready(const struct device *dev)
254 {
255 	const struct uart_cc23x0_config *config = dev->config;
256 
257 	return UARTCharAvailable(config->reg) ? 1 : 0;
258 }
259 
uart_cc23x0_irq_err_enable(const struct device * dev)260 static void uart_cc23x0_irq_err_enable(const struct device *dev)
261 {
262 	const struct uart_cc23x0_config *config = dev->config;
263 
264 	return UARTEnableInt(config->reg, UART_INT_OE | UART_INT_BE | UART_INT_PE | UART_INT_FE);
265 }
266 
uart_cc23x0_irq_err_disable(const struct device * dev)267 static void uart_cc23x0_irq_err_disable(const struct device *dev)
268 {
269 	const struct uart_cc23x0_config *config = dev->config;
270 
271 	return UARTDisableInt(config->reg, UART_INT_OE | UART_INT_BE | UART_INT_PE | UART_INT_FE);
272 }
273 
uart_cc23x0_irq_is_pending(const struct device * dev)274 static int uart_cc23x0_irq_is_pending(const struct device *dev)
275 {
276 	const struct uart_cc23x0_config *config = dev->config;
277 
278 	/* Read masked interrupt status */
279 	uint32_t status = UARTIntStatus(config->reg, true);
280 
281 	return status ? 1 : 0;
282 }
283 
uart_cc23x0_irq_update(const struct device * dev)284 static int uart_cc23x0_irq_update(const struct device *dev)
285 {
286 	ARG_UNUSED(dev);
287 	return 1;
288 }
289 
uart_cc23x0_irq_callback_set(const struct device * dev,uart_irq_callback_user_data_t cb,void * user_data)290 static void uart_cc23x0_irq_callback_set(const struct device *dev, uart_irq_callback_user_data_t cb,
291 					 void *user_data)
292 {
293 	struct uart_cc23x0_data *data = dev->data;
294 
295 	data->callback = cb;
296 	data->user_data = user_data;
297 }
298 
uart_cc23x0_isr(const struct device * dev)299 static void uart_cc23x0_isr(const struct device *dev)
300 {
301 	struct uart_cc23x0_data *data = dev->data;
302 
303 	if (data->callback) {
304 		data->callback(dev, data->user_data);
305 	}
306 }
307 
308 #endif /* CONFIG_UART_INTERRUPT_DRIVEN */
309 
310 static DEVICE_API(uart, uart_cc23x0_driver_api) = {
311 	.poll_in = uart_cc23x0_poll_in,
312 	.poll_out = uart_cc23x0_poll_out,
313 	.err_check = uart_cc23x0_err_check,
314 #ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
315 	.configure = uart_cc23x0_configure,
316 	.config_get = uart_cc23x0_config_get,
317 #endif
318 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
319 	.fifo_fill = uart_cc23x0_fifo_fill,
320 	.fifo_read = uart_cc23x0_fifo_read,
321 	.irq_tx_enable = uart_cc23x0_irq_tx_enable,
322 	.irq_tx_disable = uart_cc23x0_irq_tx_disable,
323 	.irq_tx_ready = uart_cc23x0_irq_tx_ready,
324 	.irq_rx_enable = uart_cc23x0_irq_rx_enable,
325 	.irq_rx_disable = uart_cc23x0_irq_rx_disable,
326 	.irq_tx_complete = uart_cc23x0_irq_tx_complete,
327 	.irq_rx_ready = uart_cc23x0_irq_rx_ready,
328 	.irq_err_enable = uart_cc23x0_irq_err_enable,
329 	.irq_err_disable = uart_cc23x0_irq_err_disable,
330 	.irq_is_pending = uart_cc23x0_irq_is_pending,
331 	.irq_update = uart_cc23x0_irq_update,
332 	.irq_callback_set = uart_cc23x0_irq_callback_set,
333 #endif /* CONFIG_UART_INTERRUPT_DRIVEN */
334 };
335 
336 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
337 #define UART_CC23X0_IRQ_CFG(n)                                                                     \
338 	do {                                                                                       \
339 		UARTClearInt(config->reg, UART_INT_RX);                                            \
340 		UARTClearInt(config->reg, UART_INT_RT);                                            \
341                                                                                                    \
342 		IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), uart_cc23x0_isr,            \
343 			    DEVICE_DT_INST_GET(n), 0);                                             \
344 		irq_enable(DT_INST_IRQN(n));                                                       \
345 	} while (false)
346 
347 #define UART_CC23X0_INT_FIELDS .callback = NULL, .user_data = NULL,
348 #else
349 #define UART_CC23X0_IRQ_CFG(n)
350 #define UART_CC23X0_INT_FIELDS
351 #endif /* CONFIG_UART_INTERRUPT_DRIVEN */
352 
353 #define UART_CC23X0_DEVICE_DEFINE(n)                                                               \
354                                                                                                    \
355 	DEVICE_DT_INST_DEFINE(n, uart_cc23x0_init_##n, NULL, &uart_cc23x0_data_##n,                \
356 			      &uart_cc23x0_config_##n, PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY,  \
357 			      &uart_cc23x0_driver_api)
358 
359 #define UART_CC23X0_INIT_FUNC(n)                                                                   \
360 	static int uart_cc23x0_init_##n(const struct device *dev)                                  \
361 	{                                                                                          \
362 		const struct uart_cc23x0_config *config = dev->config;                             \
363 		struct uart_cc23x0_data *data = dev->data;                                         \
364 		int ret;                                                                           \
365                                                                                                    \
366 		CLKCTLEnable(CLKCTL_BASE, CLKCTL_UART0);                                           \
367                                                                                                    \
368 		ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);                    \
369 		if (ret < 0) {                                                                     \
370 			return ret;                                                                \
371 		}                                                                                  \
372                                                                                                    \
373 		/* Configure and enable UART */                                                    \
374 		ret = uart_cc23x0_configure(dev, &data->uart_config);                              \
375                                                                                                    \
376 		/* Enable interrupts */                                                            \
377 		UART_CC23X0_IRQ_CFG(n);                                                            \
378                                                                                                    \
379 		return ret;                                                                        \
380 	}
381 
382 #define UART_CC23X0_INIT(n)                                                                        \
383 	PINCTRL_DT_INST_DEFINE(n);                                                                 \
384 	UART_CC23X0_INIT_FUNC(n);                                                                  \
385                                                                                                    \
386 	static struct uart_cc23x0_config uart_cc23x0_config_##n = {                                \
387 		.reg = DT_INST_REG_ADDR(n),                                                        \
388 		.sys_clk_freq = DT_INST_PROP_BY_PHANDLE(n, clocks, clock_frequency),               \
389 		.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n),                                         \
390 		};                                                                                 \
391                                                                                                    \
392 	static struct uart_cc23x0_data uart_cc23x0_data_##n = {                                    \
393 		.uart_config =                                                                     \
394 			{                                                                          \
395 				.baudrate = DT_INST_PROP(n, current_speed),                        \
396 				.parity = DT_INST_ENUM_IDX(n, parity),                             \
397 				.stop_bits = DT_INST_ENUM_IDX(n, stop_bits),                       \
398 				.data_bits = DT_INST_ENUM_IDX(n, data_bits),                       \
399 				.flow_ctrl = DT_INST_PROP(n, hw_flow_control),                     \
400 			},                                                                         \
401 		UART_CC23X0_INT_FIELDS                                                             \
402 		};                                                                                 \
403 	UART_CC23X0_DEVICE_DEFINE(n);
404 
405 DT_INST_FOREACH_STATUS_OKAY(UART_CC23X0_INIT)
406