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>
40 #include <cfg/macros.h> /* for BV() */
41 #include <cpu/power.h> /* cpu_relax() */
42 #include <drv/ser_p.h>
44 #include <drv/vic_lpc2.h> /* vic_handler_t */
45 #include <io/lpc23xx.h>
46 #include "cfg/cfg_ser.h"
54 /* From the high-level serial driver */
55 extern struct Serial *ser_handles[SER_CNT];
59 struct SerialHardware hw;
64 /* Forward declaration */
65 static struct LPC2Serial UARTDesc[SER_CNT];
72 uint32_t pclksel0_mask;
74 uint32_t pclksel1_mask;
76 uint32_t pinsel0_mask;
78 uint32_t pinsel4_mask;
81 /* UART registers configuration */
82 static const struct uart_config uart_param[] =
86 .base = UART0_BASE_ADDR,
90 .pclksel0_mask = BV(7) | BV(6),
95 .pinsel0 = BV(6) | BV(4),
96 .pinsel0_mask = BV(7) | BV(6) | BV(5) | BV(4),
103 .base = UART1_BASE_ADDR,
107 .pclksel0_mask = BV(9) | BV(8),
115 .pinsel4 = BV(3) | BV(1),
116 .pinsel4_mask = BV(3) | BV(2) | BV(1) | BV(0),
120 .base = UART2_BASE_ADDR,
127 .pclksel1_mask = BV(17) | BV(16),
132 .pinsel4 = BV(19) | BV(17),
133 .pinsel4_mask = BV(19) | BV(18) | BV(17) | BV(16),
137 .base = UART3_BASE_ADDR,
144 .pclksel1_mask = BV(19) | BV(18),
146 .pinsel0 = BV(3) | BV(1),
147 .pinsel0_mask = BV(3) | BV(2) | BV(1) | BV(0),
154 static void lpc2_uartSetBaudRate(int port, unsigned long baud)
158 IRQ_SAVE_DISABLE(flags);
160 /* LCR: DLAB = 1 (enable divisor modify) */
161 *(reg8_t *)(uart_param[port].base + 0x0c) |= 0x80;
163 *(reg8_t *)(uart_param[port].base + 0x00) =
164 DIV_ROUND(CPU_FREQ, 16 * baud) & 0xFF;
166 *(reg8_t *)(uart_param[port].base + 0x04) =
167 (DIV_ROUND(CPU_FREQ, 16 * baud) >> 8) & 0xFF;
168 *(reg32_t *)(uart_param[port].base + 0x0c) &= ~0x80;
169 /* LCR: DLAB = 0 (disable divisor modify) */
170 *(reg8_t *)(uart_param[port].base + 0x0c) &= ~0x80;
175 static void lpc2_uartSetParity(int port, int parity)
177 /* Set 8-bit word, one stop bit by default */
178 uint32_t config = BV(1) | BV(0);
182 IRQ_SAVE_DISABLE(flags);
186 case SER_PARITY_NONE:
191 case SER_PARITY_EVEN:
192 config |= BV(4) | BV(3);
200 *(reg8_t *)(uart_param[port].base + 0x0c) = config;
205 static void lpc2_uartPutChar(uint32_t base, uint8_t c)
207 reg8_t *lsr = (reg8_t *)base + 0x14;
208 reg8_t *thr = (reg8_t *)base + 0x00;
210 while (!(*lsr & BV(6)))
215 void lpc2_uartInit(int port)
219 IRQ_SAVE_DISABLE(flags);
221 /* Power-on the device */
222 PCONP |= uart_param[port].pconp;
223 /* Set UART clk to CPU_FREQ */
224 PCLKSEL0 &= ~uart_param[port].pclksel0_mask;
225 PCLKSEL0 |= uart_param[port].pclksel0;
226 PCLKSEL1 &= ~uart_param[port].pclksel1_mask;
227 PCLKSEL1 |= uart_param[port].pclksel1;
229 /* LCR: 8bit, 1 stop bit, no parity, DLAB = 1 (enable divisor modify) */
230 *(reg8_t *)(uart_param[port].base + 0x0c) = 0x83;
232 *(reg8_t *)(uart_param[port].base + 0x00) =
233 DIV_ROUND(CPU_FREQ, 16 * CONFIG_KDEBUG_BAUDRATE) & 0xFF;
235 *(reg8_t *)(uart_param[port].base + 0x04) =
236 (DIV_ROUND(CPU_FREQ, 16 * CONFIG_KDEBUG_BAUDRATE) >> 8) & 0xFF;
238 *(reg32_t *)(uart_param[port].base + 0x28) = 0x10;
240 /* Assign TX pin to UART0*/
241 PINSEL0 &= ~uart_param[port].pinsel0_mask;
242 PINSEL0 |= uart_param[port].pinsel0;
243 PINSEL4 &= ~uart_param[port].pinsel4_mask;
244 PINSEL4 |= uart_param[port].pinsel4;
245 /* LCR: set 8bit, 1 stop bit, no parity, DLAB = 0 (disable divisor modify) */
246 *(reg8_t *)(uart_param[port].base + 0x0c) = 0x03;
248 /* TER: Enable transmitter */
249 *(reg8_t *)(uart_param[port].base + 0x30) = BV(7);
250 /* IER: Enable RBR interrupt */
251 *(reg8_t *)(uart_param[port].base + 0x04) = BV(0);
256 static bool tx_sending(struct SerialHardware *_hw)
258 struct LPC2Serial *hw = (struct LPC2Serial *)_hw;
262 INLINE bool lpc2_uartRxReady(int port)
264 /* LSR: check Receiver Data Ready (RDR) bit */
265 return *(reg8_t *)(uart_param[port].base + 0x14) & BV(0) ? true : false;
268 static void uart_irq_rx(int port)
270 struct FIFOBuffer *rxfifo = &ser_handles[port]->rxfifo;
273 while (lpc2_uartRxReady(port))
275 c = *(reg8_t *)(uart_param[port].base + 0x00);
276 if (fifo_isfull(rxfifo))
277 ser_handles[port]->status |= SERRF_RXFIFOOVERRUN;
279 fifo_push(rxfifo, c);
283 INLINE bool lpc2_uartTxReady(int port)
285 /* LSR: check Transmitter Holding Register Empty (THRE) bit */
286 return *(reg8_t *)(uart_param[port].base + 0x14) & BV(5) ? true : false;
289 static void uart_irq_tx(int port)
291 struct FIFOBuffer *txfifo = &ser_handles[port]->txfifo;
293 while (lpc2_uartTxReady(port))
296 * Disable TX empty interrupts if there're no more
297 * characters to transmit.
299 if (fifo_isempty(txfifo))
301 /* IER: Disable THRE interrupt */
302 *(reg8_t *)(uart_param[port].base + 0x04) &= ~BV(1);
303 UARTDesc[port].sending = false;
306 /* THR: put a character to the Transmit Holding Register */
307 *(reg8_t *)(uart_param[port].base + 0x00) = fifo_pop(txfifo);
311 static void uart_common_irq_handler(int port)
313 /* IIR: identify the interrupt source */
314 uint32_t status = *(reg32_t *)(uart_param[port].base + 0x08) >> 1 & 0x7;
316 /* Receiver Data Ready (RDR) */
319 /* Transmit Holding Register Empty (THRE) */
320 else if (status == 0x01)
322 /* Signal the VIC we have completed the ISR */
326 static void lpc2_uartIRQEnable(int port, vic_handler_t handler)
328 vic_setVector(UARTDesc[port].irq, handler);
329 vic_enable(UARTDesc[port].irq);
332 static void lpc2_uartIRQDisable(int port)
334 vic_disable(UARTDesc[port].irq);
337 /* UART class definition */
338 #define UART_PORT(port) \
339 /* UART TX and RX buffers */ \
340 static unsigned char \
341 uart ## port ## _txbuffer[CONFIG_UART ## port ## _TXBUFSIZE]; \
342 static unsigned char \
343 uart ## port ## _rxbuffer[CONFIG_UART ## port ## _RXBUFSIZE]; \
345 /* UART interrupt handler */ \
346 static DECLARE_ISR(uart ## port ## _irq_handler) \
348 uart_common_irq_handler(port); \
351 /* UART public methods */ \
353 uart ## port ## _txStart(struct SerialHardware *_hw) \
355 struct FIFOBuffer *txfifo = &ser_handles[port]->txfifo; \
356 struct LPC2Serial *hw = (struct LPC2Serial *)_hw; \
360 lpc2_uartPutChar(UART ## port ## _BASE_ADDR, fifo_pop(txfifo)); \
361 if (!fifo_isempty(txfifo)) \
363 hw->sending = true; \
364 /* IER: Enable THRE interrupt */ \
365 *(reg8_t *)(uart_param[port].base + 0x04) |= BV(1); \
370 uart ## port ## _setbaudrate(UNUSED_ARG(struct SerialHardware *, hw), \
371 unsigned long baud) \
373 lpc2_uartSetBaudRate(port, baud); \
377 uart ## port ## _setparity(UNUSED_ARG(struct SerialHardware *, hw), \
380 lpc2_uartSetParity(port, parity); \
384 uart ## port ## _cleanup(struct SerialHardware *_hw) \
386 struct LPC2Serial *hw = (struct LPC2Serial *)_hw; \
388 hw->sending = false; \
389 lpc2_uartIRQDisable(port); \
393 uart ## port ## _init(UNUSED_ARG(struct SerialHardware *, hw), \
394 UNUSED_ARG(struct Serial *, ser)) \
396 lpc2_uartInit(port); \
397 lpc2_uartIRQEnable(port, uart ## port ## _irq_handler); \
400 /* UART operations */ \
401 static const struct SerialHardwareVT UART ## port ## _VT = \
403 .init = uart ## port ## _init, \
404 .cleanup = uart ## port ## _cleanup, \
405 .setBaudrate = uart ## port ## _setbaudrate, \
406 .setParity = uart ## port ## _setparity, \
407 .txStart = uart ## port ## _txStart, \
408 .txSending = tx_sending, \
411 /* UART port instances */
417 static struct LPC2Serial UARTDesc[SER_CNT] =
422 .txbuffer = uart0_txbuffer,
423 .rxbuffer = uart0_rxbuffer,
424 .txbuffer_size = sizeof(uart0_txbuffer),
425 .rxbuffer_size = sizeof(uart0_rxbuffer),
433 .txbuffer = uart1_txbuffer,
434 .rxbuffer = uart1_rxbuffer,
435 .txbuffer_size = sizeof(uart1_txbuffer),
436 .rxbuffer_size = sizeof(uart1_rxbuffer),
444 .txbuffer = uart2_txbuffer,
445 .rxbuffer = uart2_rxbuffer,
446 .txbuffer_size = sizeof(uart2_txbuffer),
447 .rxbuffer_size = sizeof(uart2_rxbuffer),
455 .txbuffer = uart3_txbuffer,
456 .rxbuffer = uart3_rxbuffer,
457 .txbuffer_size = sizeof(uart3_txbuffer),
458 .rxbuffer_size = sizeof(uart3_rxbuffer),
465 struct SerialHardware *ser_hw_getdesc(int port)
467 ASSERT(port >= 0 && port < SER_CNT);
468 return &UARTDesc[port].hw;