LPC2: UART driver.
[bertos.git] / bertos / cpu / arm / drv / ser_lpc2.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
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.
10  *
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.
15  *
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
19  *
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.
28  *
29  * Copyright 2010 Develer S.r.l. (http://www.develer.com/)
30  *
31  * -->
32  *
33  * \brief LPC23xx UART driver.
34  *
35  * \author Andrea Righi <arighi@develer.com>
36  */
37
38 #include <cfg/macros.h> /* for BV() */
39 #include <cpu/power.h> /* cpu_relax() */
40 #include <drv/ser_p.h>
41 #include <drv/ser.h>
42 #include <drv/vic_lpc2.h> /* vic_handler_t */
43 #include <io/lpc23xx.h>
44 #include "cfg/cfg_ser.h"
45 #include "ser_lpc2.h"
46
47 #define INT_UART0       6
48 #define INT_UART1       7
49 #define INT_UART2       28
50 #define INT_UART3       29
51
52 /* From the high-level serial driver */
53 extern struct Serial *ser_handles[SER_CNT];
54
55 struct LPC2Serial
56 {
57         struct SerialHardware hw;
58         bool sending;
59         int irq;
60 };
61
62 /* Forward declaration */
63 static struct LPC2Serial UARTDesc[SER_CNT];
64
65 struct uart_config
66 {
67         uint32_t base;
68         uint32_t pconp;
69         uint32_t pclksel0;
70         uint32_t pclksel0_mask;
71         uint32_t pclksel1;
72         uint32_t pclksel1_mask;
73         uint32_t pinsel0;
74         uint32_t pinsel0_mask;
75         uint32_t pinsel4;
76         uint32_t pinsel4_mask;
77 };
78
79 /* UART registers configuration */
80 static const struct uart_config uart_param[] =
81 {
82         /* UART0 */
83         {
84                 .base = UART0_BASE_ADDR,
85                 .pconp = BV(3),
86
87                 .pclksel0 = BV(6),
88                 .pclksel0_mask = BV(7) | BV(6),
89
90                 .pclksel1 = 0,
91                 .pclksel1_mask = 0,
92
93                 .pinsel0 = BV(6) | BV(4),
94                 .pinsel0_mask = BV(7) | BV(6) | BV(5) | BV(4),
95
96                 .pinsel4 = 0,
97                 .pinsel4_mask = 0,
98         },
99         /* UART1 */
100         {
101                 .base = UART1_BASE_ADDR,
102                 .pconp = BV(4),
103
104                 .pclksel0 = BV(8),
105                 .pclksel0_mask = BV(9) | BV(8),
106
107                 .pclksel1 = 0,
108                 .pclksel1_mask = 0,
109
110                 .pinsel0 = 0,
111                 .pinsel0_mask = 0,
112
113                 .pinsel4 = BV(3) | BV(1),
114                 .pinsel4_mask = BV(3) | BV(2) | BV(1) | BV(0),
115         },
116         /* UART2 */
117         {
118                 .base = UART2_BASE_ADDR,
119                 .pconp = BV(24),
120
121                 .pclksel0 = 0,
122                 .pclksel0_mask = 0,
123
124                 .pclksel1 = BV(16),
125                 .pclksel1_mask = BV(17) | BV(16),
126
127                 .pinsel0 = 0,
128                 .pinsel0_mask = 0,
129
130                 .pinsel4 = BV(19) | BV(17),
131                 .pinsel4_mask = BV(19) | BV(18) | BV(17) | BV(16),
132         },
133         /* UART3 */
134         {
135                 .base = UART3_BASE_ADDR,
136                 .pconp = BV(25),
137
138                 .pclksel0 = 0,
139                 .pclksel0_mask = 0,
140
141                 .pclksel1 = BV(18),
142                 .pclksel1_mask = BV(19) | BV(18),
143
144                 .pinsel0 = BV(3) | BV(1),
145                 .pinsel0_mask = BV(3) | BV(2) | BV(1) | BV(0),
146
147                 .pinsel4 = 0,
148                 .pinsel4_mask = 0,
149         },
150 };
151
152 static void lpc2_uartSetBaudRate(int port, unsigned long baud)
153 {
154         cpu_flags_t flags;
155
156         IRQ_SAVE_DISABLE(flags);
157
158         /* LCR: DLAB = 1 (enable divisor modify) */
159         *(reg8_t *)(uart_param[port].base + 0x0c) |= 0x80;
160         /* DLL */
161         *(reg8_t *)(uart_param[port].base + 0x00) =
162                 DIV_ROUND(CPU_FREQ, 16 * baud) & 0xFF;
163         /* DLM */
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;
169
170         IRQ_RESTORE(flags);
171 }
172
173 static void lpc2_uartSetParity(int port, int parity)
174 {
175         /* Set 8-bit word, one stop bit by default */
176         uint32_t config = BV(1) | BV(0);
177
178         cpu_flags_t flags;
179
180         IRQ_SAVE_DISABLE(flags);
181
182         switch(parity)
183         {
184         case SER_PARITY_NONE:
185                 break;
186         case SER_PARITY_ODD:
187                 config |= BV(3);
188                 break;
189         case SER_PARITY_EVEN:
190                 config |= BV(4) | BV(3);
191                 break;
192         default:
193                 ASSERT(0);
194                 IRQ_RESTORE(flags);
195                 return;
196         }
197         /* LCR */
198         *(reg8_t *)(uart_param[port].base + 0x0c) = config;
199
200         IRQ_RESTORE(flags);
201 }
202
203 static void lpc2_uartPutChar(uint32_t base, uint8_t c)
204 {
205         reg8_t *lsr = (reg8_t *)base + 0x14;
206         reg8_t *thr = (reg8_t *)base + 0x00;
207
208         while (!(*lsr & BV(6)))
209                 cpu_relax();
210         *thr = c;
211 }
212
213 void lpc2_uartInit(int port)
214 {
215         cpu_flags_t flags;
216
217         IRQ_SAVE_DISABLE(flags);
218
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;
226
227         /* LCR: 8bit, 1 stop bit, no parity, DLAB = 1 (enable divisor modify) */
228         *(reg8_t *)(uart_param[port].base + 0x0c) = 0x83;
229         /* DLL */
230         *(reg8_t *)(uart_param[port].base + 0x00) =
231                 DIV_ROUND(CPU_FREQ, 16 * CONFIG_KDEBUG_BAUDRATE) & 0xFF;
232         /* DLM */
233         *(reg8_t *)(uart_param[port].base + 0x04) =
234                 (DIV_ROUND(CPU_FREQ, 16 * CONFIG_KDEBUG_BAUDRATE) >> 8) & 0xFF;
235         /* FDR */
236         *(reg32_t *)(uart_param[port].base + 0x28) = 0x10;
237
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;
245
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);
250
251         IRQ_RESTORE(flags);
252 }
253
254 static bool tx_sending(struct SerialHardware *_hw)
255 {
256         struct LPC2Serial *hw = (struct LPC2Serial *)_hw;
257         return hw->sending;
258 }
259
260 INLINE bool lpc2_uartRxReady(int port)
261 {
262         /* LSR: check Receiver Data Ready (RDR) bit */
263         return *(reg8_t *)(uart_param[port].base + 0x14) & BV(0) ? true : false;
264 }
265
266 static void uart_irq_rx(int port)
267 {
268         struct FIFOBuffer *rxfifo = &ser_handles[port]->rxfifo;
269         char c;
270
271         while (lpc2_uartRxReady(port))
272         {
273                 c = *(reg8_t *)(uart_param[port].base + 0x00);
274                 if (fifo_isfull(rxfifo))
275                         ser_handles[port]->status |= SERRF_RXFIFOOVERRUN;
276                 else
277                         fifo_push(rxfifo, c);
278         }
279 }
280
281 INLINE bool lpc2_uartTxReady(int port)
282 {
283         /* LSR: check Transmitter Holding Register Empty (THRE) bit */
284         return *(reg8_t *)(uart_param[port].base + 0x14) & BV(5) ? true : false;
285 }
286
287 static void uart_irq_tx(int port)
288 {
289         struct FIFOBuffer *txfifo = &ser_handles[port]->txfifo;
290
291         while (lpc2_uartTxReady(port))
292         {
293                 /*
294                  * Disable TX empty interrupts if there're no more
295                  * characters to transmit.
296                  */
297                 if (fifo_isempty(txfifo))
298                 {
299                         /* IER: Disable THRE interrupt */
300                         *(reg8_t *)(uart_param[port].base + 0x04) &= ~BV(1);
301                         UARTDesc[port].sending = false;
302                         break;
303                 }
304                 /* THR: put a character to the Transmit Holding Register */
305                 *(reg8_t *)(uart_param[port].base + 0x00) = fifo_pop(txfifo);
306         }
307 }
308
309 static void uart_common_irq_handler(int port)
310 {
311         /* IIR: identify the interrupt source */
312         uint32_t status = *(reg32_t *)(uart_param[port].base + 0x08) >> 1 & 0x7;
313
314         /* Receiver Data Ready (RDR) */
315         if (status == 0x02)
316                 uart_irq_rx(port);
317         /* Transmit Holding Register Empty (THRE) */
318         else if (status == 0x01)
319                 uart_irq_tx(port);
320         /* Signal the VIC we have completed the ISR */
321         VICVectAddr = 0;
322 }
323
324 static void lpc2_uartIRQEnable(int port, vic_handler_t handler)
325 {
326         vic_setVector(UARTDesc[port].irq, handler);
327         vic_enable(UARTDesc[port].irq);
328 }
329
330 static void lpc2_uartIRQDisable(int port)
331 {
332         vic_disable(UARTDesc[port].irq);
333 }
334
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];   \
342                                                                                 \
343         /* UART interrupt handler */                                            \
344         static DECLARE_ISR(uart ## port ## _irq_handler)                        \
345         {                                                                       \
346                 uart_common_irq_handler(port);                                  \
347         }                                                                       \
348                                                                                 \
349         /* UART public methods */                                               \
350         static void                                                             \
351         uart ## port ## _txStart(struct SerialHardware *_hw)                    \
352         {                                                                       \
353                 struct FIFOBuffer *txfifo = &ser_handles[port]->txfifo;         \
354                 struct LPC2Serial *hw = (struct LPC2Serial *)_hw;               \
355                                                                                 \
356                 if (hw->sending)                                                \
357                         return;                                                 \
358                 lpc2_uartPutChar(UART ## port ## _BASE_ADDR, fifo_pop(txfifo)); \
359                 if (!fifo_isempty(txfifo))                                      \
360                 {                                                               \
361                         hw->sending = true;                                     \
362                         /* IER: Enable THRE interrupt */                        \
363                         *(reg8_t *)(uart_param[port].base + 0x04) |= BV(1);     \
364                 }                                                               \
365         }                                                                       \
366                                                                                 \
367         static void                                                             \
368         uart ## port ## _setbaudrate(UNUSED_ARG(struct SerialHardware *, hw),   \
369                                                 unsigned long baud)             \
370         {                                                                       \
371                 lpc2_uartSetBaudRate(port, baud);                               \
372         }                                                                       \
373                                                                                 \
374         static void                                                             \
375         uart ## port ## _setparity(UNUSED_ARG(struct SerialHardware *, hw),     \
376                                                 int parity)                     \
377         {                                                                       \
378                 lpc2_uartSetParity(port, parity);                               \
379         }                                                                       \
380                                                                                 \
381         static void                                                             \
382         uart ## port ## _cleanup(struct SerialHardware *_hw)                    \
383         {                                                                       \
384                 struct LPC2Serial *hw = (struct LPC2Serial *)_hw;               \
385                                                                                 \
386                 hw->sending = false;                                            \
387                 lpc2_uartIRQDisable(port);                                      \
388         }                                                                       \
389                                                                                 \
390         static void                                                             \
391         uart ## port ## _init(UNUSED_ARG(struct SerialHardware *, hw),          \
392                                 UNUSED_ARG(struct Serial *, ser))               \
393         {                                                                       \
394                 lpc2_uartInit(port);                                            \
395                 lpc2_uartIRQEnable(port, uart ## port ## _irq_handler);         \
396         }                                                                       \
397                                                                                 \
398         /* UART operations */                                                   \
399         static const struct SerialHardwareVT UART ## port ## _VT =              \
400         {                                                                       \
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,                                        \
407         };
408
409 /* UART port instances */
410 UART_PORT(0)
411 UART_PORT(1)
412 UART_PORT(2)
413 UART_PORT(3)
414
415 static struct LPC2Serial UARTDesc[SER_CNT] =
416 {
417         {
418                 .hw = {
419                         .table = &UART0_VT,
420                         .txbuffer = uart0_txbuffer,
421                         .rxbuffer = uart0_rxbuffer,
422                         .txbuffer_size = sizeof(uart0_txbuffer),
423                         .rxbuffer_size = sizeof(uart0_rxbuffer),
424                 },
425                 .sending = false,
426                 .irq = INT_UART0,
427         },
428         {
429                 .hw = {
430                         .table = &UART1_VT,
431                         .txbuffer = uart1_txbuffer,
432                         .rxbuffer = uart1_rxbuffer,
433                         .txbuffer_size = sizeof(uart1_txbuffer),
434                         .rxbuffer_size = sizeof(uart1_rxbuffer),
435                 },
436                 .sending = false,
437                 .irq = INT_UART1,
438         },
439         {
440                 .hw = {
441                         .table = &UART2_VT,
442                         .txbuffer = uart2_txbuffer,
443                         .rxbuffer = uart2_rxbuffer,
444                         .txbuffer_size = sizeof(uart2_txbuffer),
445                         .rxbuffer_size = sizeof(uart2_rxbuffer),
446                 },
447                 .sending = false,
448                 .irq = INT_UART2,
449         },
450         {
451                 .hw = {
452                         .table = &UART3_VT,
453                         .txbuffer = uart3_txbuffer,
454                         .rxbuffer = uart3_rxbuffer,
455                         .txbuffer_size = sizeof(uart3_txbuffer),
456                         .rxbuffer_size = sizeof(uart3_rxbuffer),
457                 },
458                 .sending = false,
459                 .irq = INT_UART3,
460         },
461 };
462
463 struct SerialHardware *ser_hw_getdesc(int port)
464 {
465         ASSERT(port >= 0 && port < SER_CNT);
466         return &UARTDesc[port].hw;
467 }