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