b326a058bc9af98b0960275311f497788a0fffa5
[bertos.git] / bertos / cpu / cortex-m3 / drv / ser_lm3s.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 LM3S1968 UART interface driver.
34  *
35  * \author Andrea Righi <arighi@develer.com>
36  */
37
38 #include <cfg/macros.h> /* for BV() */
39 #include <drv/gpio_lm3s.h>
40 #include <drv/ser_p.h>
41 #include <drv/ser.h>
42 #include <drv/irq_cm3.h>
43 #include "cfg/cfg_ser.h"
44 #include "ser_lm3s.h"
45
46 /* From the high-level serial driver */
47 extern struct Serial *ser_handles[SER_CNT];
48
49 struct CM3Serial
50 {
51         struct SerialHardware hw;
52         bool sending;
53         uint32_t base;
54         sysirq_t irq;
55 };
56
57 /* Forward declaration */
58 static struct CM3Serial UARTDesc[SER_CNT];
59
60 /* GPIO descriptor for UART pins */
61 struct gpio_uart_info
62 {
63         /* Sysctl */
64         uint32_t sysctl;
65         /* GPIO base address register */
66         uint32_t base;
67         /* Pin(s) bitmask */
68         uint8_t pins;
69 };
70
71 /* Table to retrieve GPIO pins configuration to work as UART pins */
72 static const struct gpio_uart_info gpio_uart[SER_CNT] =
73 {
74         /* UART0 */
75         {
76                 .base = GPIO_PORTA_BASE,
77                 .pins = BV(1) | BV(0),
78                 .sysctl = SYSCTL_RCGC2_GPIOA,
79         },
80         /* UART1 */
81         {
82                 .base = GPIO_PORTD_BASE,
83                 .pins = BV(3) | BV(2),
84                 .sysctl = SYSCTL_RCGC2_GPIOD,
85         },
86         /* UART2 */
87         {
88                 .base = GPIO_PORTG_BASE,
89                 .pins = BV(1) | BV(0),
90                 .sysctl = SYSCTL_RCGC2_GPIOG,
91         },
92 };
93
94 /* Clear the flags register */
95 INLINE void lm3s_uartClear(uint32_t base)
96 {
97         HWREG(base + UART_O_FR) = 0;
98 }
99
100 void lm3s_uartSetBaudRate(uint32_t base, unsigned long baud)
101 {
102         unsigned long div;
103         bool hi_speed;
104
105         if (baud * 16 > CPU_FREQ)
106         {
107                 hi_speed = true;
108                 baud /= 2;
109         }
110         div = (CPU_FREQ * 8 / baud + 1) / 2;
111
112         lm3s_uartDisable(base);
113         if (hi_speed)
114                 HWREG(base + UART_O_CTL) |= UART_CTL_HSE;
115         else
116                 HWREG(base + UART_O_CTL) &= ~UART_CTL_HSE;
117         /* Set the baud rate */
118         HWREG(base + UART_O_IBRD) = div / 64;
119         HWREG(base + UART_O_FBRD) = div % 64;
120         lm3s_uartClear(base);
121         lm3s_uartEnable(base);
122 }
123
124 void lm3s_uartSetParity(uint32_t base, int parity)
125 {
126         /* Set 8-bit word, one stop bit by default */
127         uint32_t config = UART_LCRH_WLEN_8;
128
129         switch(parity)
130         {
131         case SER_PARITY_NONE:
132                 break;
133         case SER_PARITY_ODD:
134                 config |= UART_LCRH_PEN;
135                 break;
136         case SER_PARITY_EVEN:
137                 config |= UART_LCRH_EPS | UART_LCRH_PEN;
138                 break;
139         default:
140                 ASSERT(0);
141                 return;
142         }
143         lm3s_uartDisable(base);
144         HWREG(base + UART_O_LCRH) = config;
145         lm3s_uartClear(base);
146         lm3s_uartEnable(base);
147 }
148
149 void lm3s_uartInit(int port)
150 {
151         uint32_t reg_clock, base;
152
153         ASSERT(port >= 0 && port < SER_CNT);
154
155         base = UARTDesc[port].base;
156         reg_clock = 1 << port;
157
158         /* Enable the peripheral clock */
159         SYSCTL_RCGC1_R |= reg_clock;
160         SYSCTL_RCGC2_R |= gpio_uart[port].sysctl;
161         lm3s_busyWait(512);
162
163         /* Configure GPIO pins to work as UART pins */
164         lm3s_gpioPinConfig(gpio_uart[port].base, gpio_uart[port].pins,
165                         GPIO_DIR_MODE_HW, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD);
166
167         /* Set serial param: 115.200 bps, no parity */
168         lm3s_uartSetBaudRate(base, 115200);
169         lm3s_uartSetParity(base, SER_PARITY_NONE);
170 }
171
172 static bool tx_sending(struct SerialHardware *_hw)
173 {
174         struct CM3Serial *hw = (struct CM3Serial *)_hw;
175         return hw->sending;
176 }
177
178 static void uart_irq_rx(int port)
179 {
180         struct FIFOBuffer *rxfifo = &ser_handles[port]->rxfifo;
181         uint32_t base = UARTDesc[port].base;
182         char c;
183
184         while (lm3s_uartRxReady(base))
185         {
186                 c = HWREG(base + UART_O_DR);
187                 if (fifo_isfull(rxfifo))
188                         ser_handles[port]->status |= SERRF_RXFIFOOVERRUN;
189                 else
190                         fifo_push(rxfifo, c);
191         }
192 }
193
194 static void uart_irq_tx(int port)
195 {
196         struct FIFOBuffer *txfifo = &ser_handles[port]->txfifo;
197         uint32_t base = UARTDesc[port].base;
198
199         while (lm3s_uartTxReady(base))
200         {
201                 if (fifo_isempty(txfifo)) {
202                         /*
203                          * Disable TX empty interrupts if there're no more
204                          * characters to transmit.
205                          */
206                         HWREG(base + UART_O_IM) &= ~UART_IM_TXIM;
207                         UARTDesc[port].sending = false;
208                         break;
209                 }
210                 HWREG(base + UART_O_DR) = fifo_pop(txfifo);
211         }
212 }
213
214 static void uart_common_irq_handler(int port)
215 {
216         uint32_t base = UARTDesc[port].base;
217         uint32_t status;
218
219         /* Read and clear the IRQ status */
220         status = HWREG(base + UART_O_RIS);
221
222         /* Process the IRQ */
223         if (status & (UART_RIS_RXRIS | UART_RIS_RTRIS))
224                 uart_irq_rx(port);
225         if (status & UART_RIS_TXRIS)
226                 uart_irq_tx(port);
227 }
228
229 static void
230 lm3s_uartIRQEnable(int port, sysirq_handler_t handler)
231 {
232         uint32_t base = UARTDesc[port].base;
233         sysirq_t irq = UARTDesc[port].irq;
234
235         /* Register the IRQ handler */
236         sysirq_setHandler(irq, handler);
237         /* Enable RX interrupt in the UART interrupt mask register */
238         HWREG(base + UART_O_IM) |= UART_IM_RXIM | UART_IM_RTIM;
239 }
240
241 static void lm3s_uartIRQDisable(int port)
242 {
243         uint32_t base = UARTDesc[port].base;
244
245         HWREG(base + UART_O_IM) &=
246                         ~(UART_IM_TXIM | UART_IM_RXIM | UART_IM_RTIM);
247 }
248
249 /* UART class definition */
250 #define UART_PORT(port)                                                         \
251         /* UART TX and RX buffers */                                            \
252         static unsigned char                                                    \
253                 uart ## port ## _txbuffer[CONFIG_UART ## port ## _TXBUFSIZE];   \
254         static unsigned char                                                    \
255                 uart ## port ## _rxbuffer[CONFIG_UART ## port ## _RXBUFSIZE];   \
256                                                                                 \
257         /* UART interrupt handler */                                            \
258         static DECLARE_ISR(uart ## port ## _irq_handler)                        \
259         {                                                                       \
260                 uart_common_irq_handler(port);                                  \
261         }                                                                       \
262                                                                                 \
263         /* UART public methods */                                               \
264         static void                                                             \
265         uart ## port ## _txStart(struct SerialHardware *_hw)                    \
266         {                                                                       \
267                 struct FIFOBuffer *txfifo = &ser_handles[port]->txfifo;         \
268                 struct CM3Serial *hw = (struct CM3Serial *)_hw;                 \
269                                                                                 \
270                 if (hw->sending)                                                \
271                         return;                                                 \
272                 lm3s_uartPutChar(UART ## port ## _BASE, fifo_pop(txfifo));      \
273                 if (!fifo_isempty(txfifo))                                      \
274                 {                                                               \
275                         HWREG(UART ## port ## _BASE + UART_O_IM) |=             \
276                                                  UART_IM_TXIM;                  \
277                         hw->sending = true;                                     \
278                 }                                                               \
279         }                                                                       \
280                                                                                 \
281         static void                                                             \
282         uart ## port ## _setbaudrate(UNUSED_ARG(struct SerialHardware *, hw),   \
283                                                 unsigned long baud)             \
284         {                                                                       \
285                 lm3s_uartSetBaudRate(UART ## port ## _BASE, baud);              \
286         }                                                                       \
287                                                                                 \
288         static void                                                             \
289         uart ## port ## _setparity(UNUSED_ARG(struct SerialHardware *, hw),     \
290                                                 int parity)                     \
291         {                                                                       \
292                 lm3s_uartSetParity(UART ## port ## _BASE, parity);              \
293         }                                                                       \
294                                                                                 \
295         static void                                                             \
296         uart ## port ## _cleanup(struct SerialHardware *_hw)                    \
297         {                                                                       \
298                 struct CM3Serial *hw = (struct CM3Serial *)_hw;                 \
299                                                                                 \
300                 hw->sending = false;                                            \
301                 lm3s_uartIRQDisable(port);                                      \
302                 lm3s_uartClear(UART ## port ## _BASE);                          \
303                 lm3s_uartDisable(UART ## port ## _BASE);                        \
304         }                                                                       \
305                                                                                 \
306         static void                                                             \
307         uart ## port ## _init(UNUSED_ARG(struct SerialHardware *, hw),          \
308                                 UNUSED_ARG(struct Serial *, ser))               \
309         {                                                                       \
310                 lm3s_uartInit(port);                                            \
311                 lm3s_uartEnable(UART ## port ## _BASE);                         \
312                 lm3s_uartIRQEnable(port, uart ## port ## _irq_handler);         \
313         }                                                                       \
314                                                                                 \
315         /* UART operations */                                                   \
316         static const struct SerialHardwareVT UART ## port ## _VT =              \
317         {                                                                       \
318                 .init = uart ## port ## _init,                                  \
319                 .cleanup = uart ## port ## _cleanup,                            \
320                 .setBaudrate = uart ## port ## _setbaudrate,                    \
321                 .setParity = uart ## port ## _setparity,                        \
322                 .txStart = uart ## port ## _txStart,                            \
323                 .txSending = tx_sending,                                        \
324         };
325
326 /* UART port instances */
327 UART_PORT(0)
328 UART_PORT(1)
329 UART_PORT(2)
330
331 static struct CM3Serial UARTDesc[SER_CNT] =
332 {
333         {
334                 .hw = {
335                         .table = &UART0_VT,
336                         .txbuffer = uart0_txbuffer,
337                         .rxbuffer = uart0_rxbuffer,
338                         .txbuffer_size = sizeof(uart0_txbuffer),
339                         .rxbuffer_size = sizeof(uart0_rxbuffer),
340                 },
341                 .sending = false,
342                 .base = UART0_BASE,
343                 .irq = INT_UART0,
344         },
345         {
346                 .hw = {
347                         .table = &UART1_VT,
348                         .txbuffer = uart1_txbuffer,
349                         .rxbuffer = uart1_rxbuffer,
350                         .txbuffer_size = sizeof(uart1_txbuffer),
351                         .rxbuffer_size = sizeof(uart1_rxbuffer),
352                 },
353                 .sending = false,
354                 .base = UART1_BASE,
355                 .irq = INT_UART1,
356         },
357         {
358                 .hw = {
359                         .table = &UART2_VT,
360                         .txbuffer = uart2_txbuffer,
361                         .rxbuffer = uart2_rxbuffer,
362                         .txbuffer_size = sizeof(uart2_txbuffer),
363                         .rxbuffer_size = sizeof(uart2_rxbuffer),
364                 },
365                 .sending = false,
366                 .base = UART2_BASE,
367                 .irq = INT_UART2,
368         },
369 };
370
371 struct SerialHardware *ser_hw_getdesc(int port)
372 {
373         ASSERT(port >= 0 && port < SER_CNT);
374         return &UARTDesc[port].hw;
375 }