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