1 /*
2 * Copyright (c) 2018, Christian Taedcke, Diego Sueiro
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT silabs_gecko_leuart
8
9 #include <errno.h>
10 #include <zephyr/drivers/uart.h>
11 #include <zephyr/irq.h>
12 #include <em_leuart.h>
13 #include <em_gpio.h>
14 #include <em_cmu.h>
15 #include <soc.h>
16
17 #define LEUART_PREFIX cmuClock_LEUART
18 #define CLOCK_ID_PRFX2(prefix, suffix) prefix##suffix
19 #define CLOCK_ID_PRFX(prefix, suffix) CLOCK_ID_PRFX2(prefix, suffix)
20 #define CLOCK_LEUART(id) CLOCK_ID_PRFX(LEUART_PREFIX, id)
21
22 #define DEV_BASE(dev) \
23 ((LEUART_TypeDef *) \
24 ((const struct leuart_gecko_config * const)(dev)->config)->base)
25
26 struct leuart_gecko_config {
27 LEUART_TypeDef *base;
28 CMU_Clock_TypeDef clock;
29 uint32_t baud_rate;
30 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
31 void (*irq_config_func)(const struct device *dev);
32 #endif
33 struct soc_gpio_pin pin_rx;
34 struct soc_gpio_pin pin_tx;
35 #ifdef CONFIG_SOC_GECKO_HAS_INDIVIDUAL_PIN_LOCATION
36 uint8_t loc_rx;
37 uint8_t loc_tx;
38 #else
39 uint8_t loc;
40 #endif
41 };
42
43 struct leuart_gecko_data {
44 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
45 uart_irq_callback_user_data_t callback;
46 void *cb_data;
47 #endif
48 };
49
leuart_gecko_poll_in(const struct device * dev,unsigned char * c)50 static int leuart_gecko_poll_in(const struct device *dev, unsigned char *c)
51 {
52 LEUART_TypeDef *base = DEV_BASE(dev);
53 uint32_t flags = LEUART_StatusGet(base);
54
55 if (flags & LEUART_STATUS_RXDATAV) {
56 *c = LEUART_Rx(base);
57 return 0;
58 }
59
60 return -1;
61 }
62
leuart_gecko_poll_out(const struct device * dev,unsigned char c)63 static void leuart_gecko_poll_out(const struct device *dev, unsigned char c)
64 {
65 LEUART_TypeDef *base = DEV_BASE(dev);
66
67 /* LEUART_Tx function already waits for the transmit buffer being empty
68 * and waits for the bus to be free to transmit.
69 */
70 LEUART_Tx(base, c);
71 }
72
leuart_gecko_err_check(const struct device * dev)73 static int leuart_gecko_err_check(const struct device *dev)
74 {
75 LEUART_TypeDef *base = DEV_BASE(dev);
76 uint32_t flags = LEUART_IntGet(base);
77 int err = 0;
78
79 if (flags & LEUART_IF_RXOF) {
80 err |= UART_ERROR_OVERRUN;
81 }
82
83 if (flags & LEUART_IF_PERR) {
84 err |= UART_ERROR_PARITY;
85 }
86
87 if (flags & LEUART_IF_FERR) {
88 err |= UART_ERROR_FRAMING;
89 }
90
91 LEUART_IntClear(base, LEUART_IF_RXOF |
92 LEUART_IF_PERR |
93 LEUART_IF_FERR);
94
95 return err;
96 }
97
98 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
leuart_gecko_fifo_fill(const struct device * dev,const uint8_t * tx_data,int len)99 static int leuart_gecko_fifo_fill(const struct device *dev,
100 const uint8_t *tx_data,
101 int len)
102 {
103 LEUART_TypeDef *base = DEV_BASE(dev);
104 int num_tx = 0U;
105
106 while ((len - num_tx > 0) &&
107 (base->STATUS & LEUART_STATUS_TXBL)) {
108
109 base->TXDATA = (uint32_t)tx_data[num_tx++];
110 }
111
112 return num_tx;
113 }
114
leuart_gecko_fifo_read(const struct device * dev,uint8_t * rx_data,const int len)115 static int leuart_gecko_fifo_read(const struct device *dev, uint8_t *rx_data,
116 const int len)
117 {
118 LEUART_TypeDef *base = DEV_BASE(dev);
119 int num_rx = 0U;
120
121 while ((len - num_rx > 0) &&
122 (base->STATUS & LEUART_STATUS_RXDATAV)) {
123
124 rx_data[num_rx++] = (uint8_t)base->RXDATA;
125 }
126
127 return num_rx;
128 }
129
leuart_gecko_irq_tx_enable(const struct device * dev)130 static void leuart_gecko_irq_tx_enable(const struct device *dev)
131 {
132 LEUART_TypeDef *base = DEV_BASE(dev);
133 uint32_t mask = LEUART_IEN_TXBL | LEUART_IEN_TXC;
134
135 LEUART_IntEnable(base, mask);
136 }
137
leuart_gecko_irq_tx_disable(const struct device * dev)138 static void leuart_gecko_irq_tx_disable(const struct device *dev)
139 {
140 LEUART_TypeDef *base = DEV_BASE(dev);
141 uint32_t mask = LEUART_IEN_TXBL | LEUART_IEN_TXC;
142
143 LEUART_IntDisable(base, mask);
144 }
145
leuart_gecko_irq_tx_complete(const struct device * dev)146 static int leuart_gecko_irq_tx_complete(const struct device *dev)
147 {
148 LEUART_TypeDef *base = DEV_BASE(dev);
149 uint32_t flags = LEUART_IntGet(base);
150
151 return (flags & LEUART_IF_TXC) != 0U;
152 }
153
leuart_gecko_irq_tx_ready(const struct device * dev)154 static int leuart_gecko_irq_tx_ready(const struct device *dev)
155 {
156 LEUART_TypeDef *base = DEV_BASE(dev);
157 uint32_t flags = LEUART_IntGet(base);
158
159 return (flags & LEUART_IF_TXBL) != 0U;
160 }
161
leuart_gecko_irq_rx_enable(const struct device * dev)162 static void leuart_gecko_irq_rx_enable(const struct device *dev)
163 {
164 LEUART_TypeDef *base = DEV_BASE(dev);
165 uint32_t mask = LEUART_IEN_RXDATAV;
166
167 LEUART_IntEnable(base, mask);
168 }
169
leuart_gecko_irq_rx_disable(const struct device * dev)170 static void leuart_gecko_irq_rx_disable(const struct device *dev)
171 {
172 LEUART_TypeDef *base = DEV_BASE(dev);
173 uint32_t mask = LEUART_IEN_RXDATAV;
174
175 LEUART_IntDisable(base, mask);
176 }
177
leuart_gecko_irq_rx_full(const struct device * dev)178 static int leuart_gecko_irq_rx_full(const struct device *dev)
179 {
180 LEUART_TypeDef *base = DEV_BASE(dev);
181 uint32_t flags = LEUART_IntGet(base);
182
183 return (flags & LEUART_IF_RXDATAV) != 0U;
184 }
185
leuart_gecko_irq_rx_ready(const struct device * dev)186 static int leuart_gecko_irq_rx_ready(const struct device *dev)
187 {
188 LEUART_TypeDef *base = DEV_BASE(dev);
189 uint32_t mask = LEUART_IEN_RXDATAV;
190
191 return (base->IEN & mask)
192 && leuart_gecko_irq_rx_full(dev);
193 }
194
leuart_gecko_irq_err_enable(const struct device * dev)195 static void leuart_gecko_irq_err_enable(const struct device *dev)
196 {
197 LEUART_TypeDef *base = DEV_BASE(dev);
198
199 LEUART_IntEnable(base, LEUART_IF_RXOF |
200 LEUART_IF_PERR |
201 LEUART_IF_FERR);
202 }
203
leuart_gecko_irq_err_disable(const struct device * dev)204 static void leuart_gecko_irq_err_disable(const struct device *dev)
205 {
206 LEUART_TypeDef *base = DEV_BASE(dev);
207
208 LEUART_IntDisable(base, LEUART_IF_RXOF |
209 LEUART_IF_PERR |
210 LEUART_IF_FERR);
211 }
212
leuart_gecko_irq_is_pending(const struct device * dev)213 static int leuart_gecko_irq_is_pending(const struct device *dev)
214 {
215 return leuart_gecko_irq_tx_ready(dev) || leuart_gecko_irq_rx_ready(dev);
216 }
217
leuart_gecko_irq_update(const struct device * dev)218 static int leuart_gecko_irq_update(const struct device *dev)
219 {
220 return 1;
221 }
222
leuart_gecko_irq_callback_set(const struct device * dev,uart_irq_callback_user_data_t cb,void * cb_data)223 static void leuart_gecko_irq_callback_set(const struct device *dev,
224 uart_irq_callback_user_data_t cb,
225 void *cb_data)
226 {
227 struct leuart_gecko_data *data = dev->data;
228
229 data->callback = cb;
230 data->cb_data = cb_data;
231 }
232
leuart_gecko_isr(const struct device * dev)233 static void leuart_gecko_isr(const struct device *dev)
234 {
235 struct leuart_gecko_data *data = dev->data;
236
237 if (data->callback) {
238 data->callback(dev, data->cb_data);
239 }
240 }
241 #endif /* CONFIG_UART_INTERRUPT_DRIVEN */
242
leuart_gecko_init_pins(const struct device * dev)243 static void leuart_gecko_init_pins(const struct device *dev)
244 {
245 const struct leuart_gecko_config *config = dev->config;
246 LEUART_TypeDef *base = DEV_BASE(dev);
247
248 GPIO_PinModeSet(config->pin_rx.port, config->pin_rx.pin,
249 config->pin_rx.mode, config->pin_rx.out);
250 GPIO_PinModeSet(config->pin_tx.port, config->pin_tx.pin,
251 config->pin_tx.mode, config->pin_tx.out);
252
253 #ifdef CONFIG_SOC_GECKO_HAS_INDIVIDUAL_PIN_LOCATION
254 base->ROUTEPEN = LEUART_ROUTEPEN_RXPEN | LEUART_ROUTEPEN_TXPEN;
255 base->ROUTELOC0 =
256 (config->loc_tx << _LEUART_ROUTELOC0_TXLOC_SHIFT) |
257 (config->loc_rx << _LEUART_ROUTELOC0_RXLOC_SHIFT);
258 #else
259 base->ROUTE = LEUART_ROUTE_RXPEN | LEUART_ROUTE_TXPEN
260 | (config->loc << 8);
261 #endif
262 }
263
leuart_gecko_init(const struct device * dev)264 static int leuart_gecko_init(const struct device *dev)
265 {
266 const struct leuart_gecko_config *config = dev->config;
267 LEUART_TypeDef *base = DEV_BASE(dev);
268 LEUART_Init_TypeDef leuartInit = LEUART_INIT_DEFAULT;
269
270 /* The peripheral and gpio clock are already enabled from soc and gpio
271 * driver
272 */
273
274 leuartInit.baudrate = config->baud_rate;
275
276 /* Enable CORE LE clock in order to access LE modules */
277 CMU_ClockEnable(cmuClock_CORELE, true);
278
279 /* Select LFXO for LEUARTs (and wait for it to stabilize) */
280 CMU_ClockSelectSet(cmuClock_LFB, cmuSelect_LFXO);
281
282 /* Enable LEUART clock */
283 CMU_ClockEnable(config->clock, true);
284
285 /* Init LEUART */
286 LEUART_Init(base, &leuartInit);
287
288 /* Initialize LEUART pins */
289 leuart_gecko_init_pins(dev);
290
291 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
292 config->irq_config_func(dev);
293 #endif
294
295 return 0;
296 }
297
298 static DEVICE_API(uart, leuart_gecko_driver_api) = {
299 .poll_in = leuart_gecko_poll_in,
300 .poll_out = leuart_gecko_poll_out,
301 .err_check = leuart_gecko_err_check,
302 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
303 .fifo_fill = leuart_gecko_fifo_fill,
304 .fifo_read = leuart_gecko_fifo_read,
305 .irq_tx_enable = leuart_gecko_irq_tx_enable,
306 .irq_tx_disable = leuart_gecko_irq_tx_disable,
307 .irq_tx_complete = leuart_gecko_irq_tx_complete,
308 .irq_tx_ready = leuart_gecko_irq_tx_ready,
309 .irq_rx_enable = leuart_gecko_irq_rx_enable,
310 .irq_rx_disable = leuart_gecko_irq_rx_disable,
311 .irq_rx_ready = leuart_gecko_irq_rx_ready,
312 .irq_err_enable = leuart_gecko_irq_err_enable,
313 .irq_err_disable = leuart_gecko_irq_err_disable,
314 .irq_is_pending = leuart_gecko_irq_is_pending,
315 .irq_update = leuart_gecko_irq_update,
316 .irq_callback_set = leuart_gecko_irq_callback_set,
317 #endif
318 };
319
320 #if DT_NODE_HAS_STATUS_OKAY(DT_DRV_INST(0))
321
322 #define PIN_LEUART_0_RXD {DT_INST_PROP_BY_IDX(0, location_rx, 1), \
323 DT_INST_PROP_BY_IDX(0, location_rx, 2), gpioModeInput, 1}
324 #define PIN_LEUART_0_TXD {DT_INST_PROP_BY_IDX(0, location_tx, 1), \
325 DT_INST_PROP_BY_IDX(0, location_tx, 2), gpioModePushPull, 1}
326
327 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
328 static void leuart_gecko_config_func_0(const struct device *dev);
329 #endif
330
331 static const struct leuart_gecko_config leuart_gecko_0_config = {
332 .base = (LEUART_TypeDef *)DT_INST_REG_ADDR(0),
333 .clock = CLOCK_LEUART(DT_INST_PROP(0, peripheral_id)),
334 .baud_rate = DT_INST_PROP(0, current_speed),
335 .pin_rx = PIN_LEUART_0_RXD,
336 .pin_tx = PIN_LEUART_0_TXD,
337 #ifdef CONFIG_SOC_GECKO_HAS_INDIVIDUAL_PIN_LOCATION
338 .loc_rx = DT_INST_PROP_BY_IDX(0, location_rx, 0),
339 .loc_tx = DT_INST_PROP_BY_IDX(0, location_tx, 0),
340 #else
341 #if DT_INST_PROP_BY_IDX(0, location_rx, 0) \
342 != DT_INST_PROP_BY_IDX(0, location_tx, 0)
343 #error LEUART_0 DTS location-* properties must have identical value
344 #endif
345 .loc = DT_INST_PROP_BY_IDX(0, location_rx, 0),
346 #endif
347 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
348 .irq_config_func = leuart_gecko_config_func_0,
349 #endif
350 };
351
352 static struct leuart_gecko_data leuart_gecko_0_data;
353
354 DEVICE_DT_INST_DEFINE(0, &leuart_gecko_init,
355 NULL, &leuart_gecko_0_data,
356 &leuart_gecko_0_config, PRE_KERNEL_1,
357 CONFIG_SERIAL_INIT_PRIORITY,
358 &leuart_gecko_driver_api);
359
360 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
leuart_gecko_config_func_0(const struct device * dev)361 static void leuart_gecko_config_func_0(const struct device *dev)
362 {
363 IRQ_CONNECT(DT_INST_IRQN(0),
364 DT_INST_IRQ(0, priority),
365 leuart_gecko_isr, DEVICE_DT_INST_GET(0), 0);
366
367 irq_enable(DT_INST_IRQN(0));
368 }
369 #endif
370
371 #endif /* DT_NODE_HAS_STATUS_OKAY(DT_DRV_INST(0)) */
372
373 #if DT_NODE_HAS_STATUS_OKAY(DT_DRV_INST(1))
374
375 #define PIN_LEUART_1_RXD {DT_INST_PROP_BY_IDX(1, location_rx, 1), \
376 DT_INST_PROP_BY_IDX(1, location_rx, 2), gpioModeInput, 1}
377 #define PIN_LEUART_1_TXD {DT_INST_PROP_BY_IDX(1, location_tx, 1), \
378 DT_INST_PROP_BY_IDX(1, location_tx, 2), gpioModePushPull, 1}
379
380 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
381 static void leuart_gecko_config_func_1(const struct device *dev);
382 #endif
383
384 static const struct leuart_gecko_config leuart_gecko_1_config = {
385 .base = (LEUART_TypeDef *)DT_INST_REG_ADDR(1),
386 .clock = CLOCK_LEUART(DT_INST_PROP(1, peripheral_id)),
387 .baud_rate = DT_INST_PROP(1, current_speed),
388 .pin_rx = PIN_LEUART_1_RXD,
389 .pin_tx = PIN_LEUART_1_TXD,
390 #ifdef CONFIG_SOC_GECKO_HAS_INDIVIDUAL_PIN_LOCATION
391 .loc_rx = DT_INST_PROP_BY_IDX(1, location_rx, 0),
392 .loc_tx = DT_INST_PROP_BY_IDX(1, location_tx, 0),
393 #else
394 #if DT_INST_PROP_BY_IDX(1, location_rx, 0) \
395 != DT_INST_PROP_BY_IDX(1, location_tx, 0)
396 #error LEUART_1 DTS location-* properties must have identical value
397 #endif
398 .loc = DT_INST_PROP_BY_IDX(1, location_rx, 0),
399 #endif
400 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
401 .irq_config_func = leuart_gecko_config_func_1,
402 #endif
403 };
404
405 static struct leuart_gecko_data leuart_gecko_1_data;
406
407 DEVICE_DT_INST_DEFINE(1, &leuart_gecko_init,
408 NULL, &leuart_gecko_1_data,
409 &leuart_gecko_1_config, PRE_KERNEL_1,
410 CONFIG_SERIAL_INIT_PRIORITY,
411 &leuart_gecko_driver_api);
412
413 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
leuart_gecko_config_func_1(const struct device * dev)414 static void leuart_gecko_config_func_1(const struct device *dev)
415 {
416 IRQ_CONNECT(DT_INST_IRQN(1),
417 DT_INST_IRQ(1, priority),
418 leuart_gecko_isr, DEVICE_DT_INST_GET(1), 0);
419
420 irq_enable(DT_INST_IRQN(1));
421 }
422 #endif
423
424 #endif /* DT_NODE_HAS_STATUS_OKAY(DT_DRV_INST(1)) */
425