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