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>
38 #include <cfg/macros.h> /* for BV() */
39 #include <cpu/power.h> /* cpu_relax() */
40 #include <drv/ser_p.h>
42 #include <drv/vic_lpc2.h> /* vic_handler_t */
43 #include <io/lpc23xx.h>
44 #include "cfg/cfg_ser.h"
52 /* From the high-level serial driver */
53 extern struct Serial *ser_handles[SER_CNT];
57 struct SerialHardware hw;
62 /* Forward declaration */
63 static struct LPC2Serial UARTDesc[SER_CNT];
70 uint32_t pclksel0_mask;
72 uint32_t pclksel1_mask;
74 uint32_t pinsel0_mask;
76 uint32_t pinsel4_mask;
79 /* UART registers configuration */
80 static const struct uart_config uart_param[] =
84 .base = UART0_BASE_ADDR,
88 .pclksel0_mask = BV(7) | BV(6),
93 .pinsel0 = BV(6) | BV(4),
94 .pinsel0_mask = BV(7) | BV(6) | BV(5) | BV(4),
101 .base = UART1_BASE_ADDR,
105 .pclksel0_mask = BV(9) | BV(8),
113 .pinsel4 = BV(3) | BV(1),
114 .pinsel4_mask = BV(3) | BV(2) | BV(1) | BV(0),
118 .base = UART2_BASE_ADDR,
125 .pclksel1_mask = BV(17) | BV(16),
130 .pinsel4 = BV(19) | BV(17),
131 .pinsel4_mask = BV(19) | BV(18) | BV(17) | BV(16),
135 .base = UART3_BASE_ADDR,
142 .pclksel1_mask = BV(19) | BV(18),
144 .pinsel0 = BV(3) | BV(1),
145 .pinsel0_mask = BV(3) | BV(2) | BV(1) | BV(0),
152 static void lpc2_uartSetBaudRate(int port, unsigned long baud)
156 IRQ_SAVE_DISABLE(flags);
158 /* LCR: DLAB = 1 (enable divisor modify) */
159 *(reg8_t *)(uart_param[port].base + 0x0c) |= 0x80;
161 *(reg8_t *)(uart_param[port].base + 0x00) =
162 DIV_ROUND(CPU_FREQ, 16 * baud) & 0xFF;
164 *(reg8_t *)(uart_param[port].base + 0x04) =
165 (DIV_ROUND(CPU_FREQ, 16 * baud) >> 8) & 0xFF;
166 *(reg32_t *)(uart_param[port].base + 0x0c) &= ~0x80;
167 /* LCR: DLAB = 0 (disable divisor modify) */
168 *(reg8_t *)(uart_param[port].base + 0x0c) &= ~0x80;
173 static void lpc2_uartSetParity(int port, int parity)
175 /* Set 8-bit word, one stop bit by default */
176 uint32_t config = BV(1) | BV(0);
180 IRQ_SAVE_DISABLE(flags);
184 case SER_PARITY_NONE:
189 case SER_PARITY_EVEN:
190 config |= BV(4) | BV(3);
198 *(reg8_t *)(uart_param[port].base + 0x0c) = config;
203 static void lpc2_uartPutChar(uint32_t base, uint8_t c)
205 reg8_t *lsr = (reg8_t *)base + 0x14;
206 reg8_t *thr = (reg8_t *)base + 0x00;
208 while (!(*lsr & BV(6)))
213 void lpc2_uartInit(int port)
217 IRQ_SAVE_DISABLE(flags);
219 /* Power-on the device */
220 PCONP |= uart_param[port].pconp;
221 /* Set UART clk to CPU_FREQ */
222 PCLKSEL0 &= ~uart_param[port].pclksel0_mask;
223 PCLKSEL0 |= uart_param[port].pclksel0;
224 PCLKSEL1 &= ~uart_param[port].pclksel1_mask;
225 PCLKSEL1 |= uart_param[port].pclksel1;
227 /* LCR: 8bit, 1 stop bit, no parity, DLAB = 1 (enable divisor modify) */
228 *(reg8_t *)(uart_param[port].base + 0x0c) = 0x83;
230 *(reg8_t *)(uart_param[port].base + 0x00) =
231 DIV_ROUND(CPU_FREQ, 16 * CONFIG_KDEBUG_BAUDRATE) & 0xFF;
233 *(reg8_t *)(uart_param[port].base + 0x04) =
234 (DIV_ROUND(CPU_FREQ, 16 * CONFIG_KDEBUG_BAUDRATE) >> 8) & 0xFF;
236 *(reg32_t *)(uart_param[port].base + 0x28) = 0x10;
238 /* Assign TX pin to UART0*/
239 PINSEL0 &= ~uart_param[port].pinsel0_mask;
240 PINSEL0 |= uart_param[port].pinsel0;
241 PINSEL4 &= ~uart_param[port].pinsel4_mask;
242 PINSEL4 |= uart_param[port].pinsel4;
243 /* LCR: set 8bit, 1 stop bit, no parity, DLAB = 0 (disable divisor modify) */
244 *(reg8_t *)(uart_param[port].base + 0x0c) = 0x03;
246 /* TER: Enable transmitter */
247 *(reg8_t *)(uart_param[port].base + 0x30) = BV(7);
248 /* IER: Enable RBR interrupt */
249 *(reg8_t *)(uart_param[port].base + 0x04) = BV(0);
254 static bool tx_sending(struct SerialHardware *_hw)
256 struct LPC2Serial *hw = (struct LPC2Serial *)_hw;
260 INLINE bool lpc2_uartRxReady(int port)
262 /* LSR: check Receiver Data Ready (RDR) bit */
263 return *(reg8_t *)(uart_param[port].base + 0x14) & BV(0) ? true : false;
266 static void uart_irq_rx(int port)
268 struct FIFOBuffer *rxfifo = &ser_handles[port]->rxfifo;
271 while (lpc2_uartRxReady(port))
273 c = *(reg8_t *)(uart_param[port].base + 0x00);
274 if (fifo_isfull(rxfifo))
275 ser_handles[port]->status |= SERRF_RXFIFOOVERRUN;
277 fifo_push(rxfifo, c);
281 INLINE bool lpc2_uartTxReady(int port)
283 /* LSR: check Transmitter Holding Register Empty (THRE) bit */
284 return *(reg8_t *)(uart_param[port].base + 0x14) & BV(5) ? true : false;
287 static void uart_irq_tx(int port)
289 struct FIFOBuffer *txfifo = &ser_handles[port]->txfifo;
291 while (lpc2_uartTxReady(port))
294 * Disable TX empty interrupts if there're no more
295 * characters to transmit.
297 if (fifo_isempty(txfifo))
299 /* IER: Disable THRE interrupt */
300 *(reg8_t *)(uart_param[port].base + 0x04) &= ~BV(1);
301 UARTDesc[port].sending = false;
304 /* THR: put a character to the Transmit Holding Register */
305 *(reg8_t *)(uart_param[port].base + 0x00) = fifo_pop(txfifo);
309 static void uart_common_irq_handler(int port)
311 /* IIR: identify the interrupt source */
312 uint32_t status = *(reg32_t *)(uart_param[port].base + 0x08) >> 1 & 0x7;
314 /* Receiver Data Ready (RDR) */
317 /* Transmit Holding Register Empty (THRE) */
318 else if (status == 0x01)
320 /* Signal the VIC we have completed the ISR */
324 static void lpc2_uartIRQEnable(int port, vic_handler_t handler)
326 vic_setVector(UARTDesc[port].irq, handler);
327 vic_enable(UARTDesc[port].irq);
330 static void lpc2_uartIRQDisable(int port)
332 vic_disable(UARTDesc[port].irq);
335 /* UART class definition */
336 #define UART_PORT(port) \
337 /* UART TX and RX buffers */ \
338 static unsigned char \
339 uart ## port ## _txbuffer[CONFIG_UART ## port ## _TXBUFSIZE]; \
340 static unsigned char \
341 uart ## port ## _rxbuffer[CONFIG_UART ## port ## _RXBUFSIZE]; \
343 /* UART interrupt handler */ \
344 static DECLARE_ISR(uart ## port ## _irq_handler) \
346 uart_common_irq_handler(port); \
349 /* UART public methods */ \
351 uart ## port ## _txStart(struct SerialHardware *_hw) \
353 struct FIFOBuffer *txfifo = &ser_handles[port]->txfifo; \
354 struct LPC2Serial *hw = (struct LPC2Serial *)_hw; \
358 lpc2_uartPutChar(UART ## port ## _BASE_ADDR, fifo_pop(txfifo)); \
359 if (!fifo_isempty(txfifo)) \
361 hw->sending = true; \
362 /* IER: Enable THRE interrupt */ \
363 *(reg8_t *)(uart_param[port].base + 0x04) |= BV(1); \
368 uart ## port ## _setbaudrate(UNUSED_ARG(struct SerialHardware *, hw), \
369 unsigned long baud) \
371 lpc2_uartSetBaudRate(port, baud); \
375 uart ## port ## _setparity(UNUSED_ARG(struct SerialHardware *, hw), \
378 lpc2_uartSetParity(port, parity); \
382 uart ## port ## _cleanup(struct SerialHardware *_hw) \
384 struct LPC2Serial *hw = (struct LPC2Serial *)_hw; \
386 hw->sending = false; \
387 lpc2_uartIRQDisable(port); \
391 uart ## port ## _init(UNUSED_ARG(struct SerialHardware *, hw), \
392 UNUSED_ARG(struct Serial *, ser)) \
394 lpc2_uartInit(port); \
395 lpc2_uartIRQEnable(port, uart ## port ## _irq_handler); \
398 /* UART operations */ \
399 static const struct SerialHardwareVT UART ## port ## _VT = \
401 .init = uart ## port ## _init, \
402 .cleanup = uart ## port ## _cleanup, \
403 .setBaudrate = uart ## port ## _setbaudrate, \
404 .setParity = uart ## port ## _setparity, \
405 .txStart = uart ## port ## _txStart, \
406 .txSending = tx_sending, \
409 /* UART port instances */
415 static struct LPC2Serial UARTDesc[SER_CNT] =
420 .txbuffer = uart0_txbuffer,
421 .rxbuffer = uart0_rxbuffer,
422 .txbuffer_size = sizeof(uart0_txbuffer),
423 .rxbuffer_size = sizeof(uart0_rxbuffer),
431 .txbuffer = uart1_txbuffer,
432 .rxbuffer = uart1_rxbuffer,
433 .txbuffer_size = sizeof(uart1_txbuffer),
434 .rxbuffer_size = sizeof(uart1_rxbuffer),
442 .txbuffer = uart2_txbuffer,
443 .rxbuffer = uart2_rxbuffer,
444 .txbuffer_size = sizeof(uart2_txbuffer),
445 .rxbuffer_size = sizeof(uart2_rxbuffer),
453 .txbuffer = uart3_txbuffer,
454 .rxbuffer = uart3_rxbuffer,
455 .txbuffer_size = sizeof(uart3_txbuffer),
456 .rxbuffer_size = sizeof(uart3_rxbuffer),
463 struct SerialHardware *ser_hw_getdesc(int port)
465 ASSERT(port >= 0 && port < SER_CNT);
466 return &UARTDesc[port].hw;