4 * This file is part of BeRTOS.
6 * Bertos is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 * As a special exception, you may use this file as part of a free software
21 * library without restriction. Specifically, if other files instantiate
22 * templates or use macros or inline functions from this file, or you compile
23 * this file and link it with other files to produce an executable, this
24 * file does not by itself cause the resulting executable to be covered by
25 * the GNU General Public License. This exception does not however
26 * invalidate any other reasons why the executable file might be covered by
27 * the GNU General Public License.
29 * Copyright 2010 Develer S.r.l. (http://www.develer.com/)
33 * \brief LPC23xx UART driver.
35 * \author Andrea Righi <arighi@develer.com>
42 #include "cfg/cfg_ser.h"
44 #include <cfg/macros.h> /* for BV() */
46 #include <cpu/power.h> /* cpu_relax() */
48 #include <drv/ser_p.h>
50 #include <drv/vic_lpc2.h> /* vic_handler_t */
52 #include <io/lpc23xx.h>
55 /* Register offsets */
71 /* From the high-level serial driver */
72 extern struct Serial *ser_handles[SER_CNT];
76 struct SerialHardware hw;
81 /* Forward declaration */
82 static struct LPC2Serial UARTDesc[SER_CNT];
89 uint32_t pclksel0_mask;
91 uint32_t pclksel1_mask;
93 uint32_t pinsel0_mask;
95 uint32_t pinsel4_mask;
98 /* UART registers configuration */
99 static const struct uart_config uart_param[] =
103 .base = UART0_BASE_ADDR,
107 .pclksel0_mask = BV(7) | BV(6),
112 .pinsel0 = BV(6) | BV(4),
113 .pinsel0_mask = BV(7) | BV(6) | BV(5) | BV(4),
120 .base = UART1_BASE_ADDR,
124 .pclksel0_mask = BV(9) | BV(8),
132 .pinsel4 = BV(3) | BV(1),
133 .pinsel4_mask = BV(3) | BV(2) | BV(1) | BV(0),
137 .base = UART2_BASE_ADDR,
144 .pclksel1_mask = BV(17) | BV(16),
149 .pinsel4 = BV(19) | BV(17),
150 .pinsel4_mask = BV(19) | BV(18) | BV(17) | BV(16),
154 .base = UART3_BASE_ADDR,
161 .pclksel1_mask = BV(19) | BV(18),
163 .pinsel0 = BV(3) | BV(1),
164 .pinsel0_mask = BV(3) | BV(2) | BV(1) | BV(0),
171 static void lpc2_uartSetBaudRate(int port, unsigned long baud)
175 IRQ_SAVE_DISABLE(flags);
177 /* LCR: DLAB = 1 (enable divisor modify) */
178 *(reg8_t *)(uart_param[port].base + LCR) |= 0x80;
180 *(reg8_t *)(uart_param[port].base + DLL) =
181 DIV_ROUND(CPU_FREQ, 16 * baud) & 0xFF;
183 *(reg8_t *)(uart_param[port].base + DLM) =
184 (DIV_ROUND(CPU_FREQ, 16 * baud) >> 8) & 0xFF;
185 *(reg32_t *)(uart_param[port].base + LCR) &= ~0x80;
186 /* LCR: DLAB = 0 (disable divisor modify) */
187 *(reg8_t *)(uart_param[port].base + LCR) &= ~0x80;
192 static void lpc2_uartSetParity(int port, int parity)
194 /* Set 8-bit word, one stop bit by default */
195 uint32_t config = BV(1) | BV(0);
199 IRQ_SAVE_DISABLE(flags);
203 case SER_PARITY_NONE:
208 case SER_PARITY_EVEN:
209 config |= BV(4) | BV(3);
217 *(reg8_t *)(uart_param[port].base + LCR) = config;
222 static void lpc2_uartPutChar(uint32_t base, uint8_t c)
224 reg8_t *lsr = (reg8_t *)base + LSR;
225 reg8_t *thr = (reg8_t *)base + THR;
227 while (!(*lsr & BV(6)))
232 void lpc2_uartInit(int port)
236 IRQ_SAVE_DISABLE(flags);
238 /* Power-on the device */
239 PCONP |= uart_param[port].pconp;
240 /* Set UART clk to CPU_FREQ */
241 PCLKSEL0 &= ~uart_param[port].pclksel0_mask;
242 PCLKSEL0 |= uart_param[port].pclksel0;
243 PCLKSEL1 &= ~uart_param[port].pclksel1_mask;
244 PCLKSEL1 |= uart_param[port].pclksel1;
246 /* LCR: 8bit, 1 stop bit, no parity, DLAB = 1 (enable divisor modify) */
247 *(reg8_t *)(uart_param[port].base + LCR) = 0x83;
249 *(reg8_t *)(uart_param[port].base + DLL) =
250 DIV_ROUND(CPU_FREQ, 16 * CONFIG_KDEBUG_BAUDRATE) & 0xFF;
252 *(reg8_t *)(uart_param[port].base + DLM) =
253 (DIV_ROUND(CPU_FREQ, 16 * CONFIG_KDEBUG_BAUDRATE) >> 8) & 0xFF;
255 *(reg32_t *)(uart_param[port].base + FDR) = 0x10;
257 /* Assign TX pin to UART0*/
258 PINSEL0 &= ~uart_param[port].pinsel0_mask;
259 PINSEL0 |= uart_param[port].pinsel0;
260 PINSEL4 &= ~uart_param[port].pinsel4_mask;
261 PINSEL4 |= uart_param[port].pinsel4;
262 /* LCR: set 8bit, 1 stop bit, no parity, DLAB = 0 (disable divisor modify) */
263 *(reg8_t *)(uart_param[port].base + LCR) = 0x03;
265 /* TER: Enable transmitter */
266 *(reg8_t *)(uart_param[port].base + TER) = BV(7);
267 /* IER: Enable RBR interrupt */
268 *(reg8_t *)(uart_param[port].base + IER) = BV(0);
273 static bool tx_sending(struct SerialHardware *_hw)
275 struct LPC2Serial *hw = (struct LPC2Serial *)_hw;
279 INLINE bool lpc2_uartRxReady(int port)
281 /* LSR: check Receiver Data Ready (RDR) bit */
282 return *(reg8_t *)(uart_param[port].base + LSR) & BV(0) ? true : false;
285 static void uart_irq_rx(int port)
287 struct FIFOBuffer *rxfifo = &ser_handles[port]->rxfifo;
290 while (lpc2_uartRxReady(port))
292 /* RBR: read a character from the Receiver Buffer Register */
293 c = *(reg8_t *)(uart_param[port].base + RBR);
294 if (fifo_isfull(rxfifo))
295 ser_handles[port]->status |= SERRF_RXFIFOOVERRUN;
297 fifo_push(rxfifo, c);
301 INLINE bool lpc2_uartTxReady(int port)
303 /* LSR: check Transmitter Holding Register Empty (THRE) bit */
304 return *(reg8_t *)(uart_param[port].base + LSR) & BV(5) ? true : false;
307 static void uart_irq_tx(int port)
309 struct FIFOBuffer *txfifo = &ser_handles[port]->txfifo;
311 while (lpc2_uartTxReady(port))
314 * Disable TX empty interrupts if there're no more
315 * characters to transmit.
317 if (fifo_isempty(txfifo))
319 /* IER: Disable THRE interrupt */
320 *(reg8_t *)(uart_param[port].base + IER) &= ~BV(1);
321 UARTDesc[port].sending = false;
324 /* THR: put a character to the Transmit Holding Register */
325 *(reg8_t *)(uart_param[port].base + THR) = fifo_pop(txfifo);
329 static void uart_common_irq_handler(int port)
331 /* IIR: identify the interrupt source */
332 uint32_t status = *(reg32_t *)(uart_param[port].base + IIR) >> 1 & 0x7;
334 /* Receiver Data Ready (RDR) */
337 /* Transmit Holding Register Empty (THRE) */
338 else if (status == 0x01)
340 /* Signal the VIC we have completed the ISR */
344 static void lpc2_uartIRQEnable(int port, vic_handler_t handler)
346 vic_setVector(UARTDesc[port].irq, handler);
347 vic_enable(UARTDesc[port].irq);
350 static void lpc2_uartIRQDisable(int port)
352 vic_disable(UARTDesc[port].irq);
355 /* UART class definition */
356 #define UART_PORT(port) \
357 /* UART TX and RX buffers */ \
358 static unsigned char \
359 uart ## port ## _txbuffer[CONFIG_UART ## port ## _TXBUFSIZE]; \
360 static unsigned char \
361 uart ## port ## _rxbuffer[CONFIG_UART ## port ## _RXBUFSIZE]; \
363 /* UART interrupt handler */ \
364 static DECLARE_ISR(uart ## port ## _irq_handler) \
366 uart_common_irq_handler(port); \
369 /* UART public methods */ \
371 uart ## port ## _txStart(struct SerialHardware *_hw) \
373 struct FIFOBuffer *txfifo = &ser_handles[port]->txfifo; \
374 struct LPC2Serial *hw = (struct LPC2Serial *)_hw; \
378 lpc2_uartPutChar(UART ## port ## _BASE_ADDR, fifo_pop(txfifo)); \
379 if (!fifo_isempty(txfifo)) \
381 hw->sending = true; \
382 /* IER: Enable THRE interrupt */ \
383 *(reg8_t *)(uart_param[port].base + IER) |= BV(1); \
388 uart ## port ## _setbaudrate(UNUSED_ARG(struct SerialHardware *, hw), \
389 unsigned long baud) \
391 lpc2_uartSetBaudRate(port, baud); \
395 uart ## port ## _setparity(UNUSED_ARG(struct SerialHardware *, hw), \
398 lpc2_uartSetParity(port, parity); \
402 uart ## port ## _cleanup(struct SerialHardware *_hw) \
404 struct LPC2Serial *hw = (struct LPC2Serial *)_hw; \
406 hw->sending = false; \
407 lpc2_uartIRQDisable(port); \
411 uart ## port ## _init(UNUSED_ARG(struct SerialHardware *, hw), \
412 UNUSED_ARG(struct Serial *, ser)) \
414 lpc2_uartInit(port); \
415 lpc2_uartIRQEnable(port, uart ## port ## _irq_handler); \
418 /* UART operations */ \
419 static const struct SerialHardwareVT UART ## port ## _VT = \
421 .init = uart ## port ## _init, \
422 .cleanup = uart ## port ## _cleanup, \
423 .setBaudrate = uart ## port ## _setbaudrate, \
424 .setParity = uart ## port ## _setparity, \
425 .txStart = uart ## port ## _txStart, \
426 .txSending = tx_sending, \
429 /* UART port instances */
435 static struct LPC2Serial UARTDesc[SER_CNT] =
440 .txbuffer = uart0_txbuffer,
441 .rxbuffer = uart0_rxbuffer,
442 .txbuffer_size = sizeof(uart0_txbuffer),
443 .rxbuffer_size = sizeof(uart0_rxbuffer),
451 .txbuffer = uart1_txbuffer,
452 .rxbuffer = uart1_rxbuffer,
453 .txbuffer_size = sizeof(uart1_txbuffer),
454 .rxbuffer_size = sizeof(uart1_rxbuffer),
462 .txbuffer = uart2_txbuffer,
463 .rxbuffer = uart2_rxbuffer,
464 .txbuffer_size = sizeof(uart2_txbuffer),
465 .rxbuffer_size = sizeof(uart2_rxbuffer),
473 .txbuffer = uart3_txbuffer,
474 .rxbuffer = uart3_rxbuffer,
475 .txbuffer_size = sizeof(uart3_txbuffer),
476 .rxbuffer_size = sizeof(uart3_rxbuffer),
483 struct SerialHardware *ser_hw_getdesc(int port)
485 ASSERT(port >= 0 && port < SER_CNT);
486 return &UARTDesc[port].hw;