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