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