aboutsummaryrefslogtreecommitdiff
path: root/lib/chibios/os/hal/ports/LPC/LPC214x/hal_serial_lld.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chibios/os/hal/ports/LPC/LPC214x/hal_serial_lld.c')
-rw-r--r--lib/chibios/os/hal/ports/LPC/LPC214x/hal_serial_lld.c343
1 files changed, 343 insertions, 0 deletions
diff --git a/lib/chibios/os/hal/ports/LPC/LPC214x/hal_serial_lld.c b/lib/chibios/os/hal/ports/LPC/LPC214x/hal_serial_lld.c
new file mode 100644
index 000000000..b8d3e592e
--- /dev/null
+++ b/lib/chibios/os/hal/ports/LPC/LPC214x/hal_serial_lld.c
@@ -0,0 +1,343 @@
1/*
2 ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15*/
16
17/**
18 * @file LPC214x/hal_serial_lld.c
19 * @brief LPC214x low level serial driver code.
20 *
21 * @addtogroup SERIAL
22 * @{
23 */
24
25#include "hal.h"
26
27#if HAL_USE_SERIAL || defined(__DOXYGEN__)
28
29/*===========================================================================*/
30/* Driver exported variables. */
31/*===========================================================================*/
32
33#if USE_LPC214x_UART0 || defined(__DOXYGEN__)
34/** @brief UART0 serial driver identifier.*/
35SerialDriver SD1;
36#endif
37
38#if USE_LPC214x_UART1 || defined(__DOXYGEN__)
39/** @brief UART1 serial driver identifier.*/
40SerialDriver SD2;
41#endif
42
43/*===========================================================================*/
44/* Driver local variables and types. */
45/*===========================================================================*/
46
47/** @brief Driver default configuration.*/
48static const SerialConfig default_config = {
49 SERIAL_DEFAULT_BITRATE,
50 LCR_WL8 | LCR_STOP1 | LCR_NOPARITY,
51 FCR_TRIGGER0
52};
53
54/*===========================================================================*/
55/* Driver local functions. */
56/*===========================================================================*/
57
58/**
59 * @brief UART initialization.
60 *
61 * @param[in] sdp communication channel associated to the UART
62 * @param[in] config the architecture-dependent serial driver configuration
63 */
64static void uart_init(SerialDriver *sdp, const SerialConfig *config) {
65 UART *u = sdp->uart;
66
67 uint32_t div = PCLK / (config->sc_speed << 4);
68 u->UART_LCR = config->sc_lcr | LCR_DLAB;
69 u->UART_DLL = div;
70 u->UART_DLM = div >> 8;
71 u->UART_LCR = config->sc_lcr;
72 u->UART_FCR = FCR_ENABLE | FCR_RXRESET | FCR_TXRESET | config->sc_fcr;
73 u->UART_ACR = 0;
74 u->UART_FDR = 0x10;
75 u->UART_TER = TER_ENABLE;
76 u->UART_IER = IER_RBR | IER_STATUS;
77}
78
79/**
80 * @brief UART de-initialization.
81 *
82 * @param[in] u pointer to an UART I/O block
83 */
84static void uart_deinit(UART *u) {
85
86 u->UART_LCR = LCR_DLAB;
87 u->UART_DLL = 1;
88 u->UART_DLM = 0;
89 u->UART_LCR = 0;
90 u->UART_FDR = 0x10;
91 u->UART_IER = 0;
92 u->UART_FCR = FCR_RXRESET | FCR_TXRESET;
93 u->UART_ACR = 0;
94 u->UART_TER = TER_ENABLE;
95}
96
97/**
98 * @brief Error handling routine.
99 *
100 * @param[in] sdp communication channel associated to the UART
101 * @param[in] err UART LSR register value
102 */
103static void set_error(SerialDriver *sdp, IOREG32 err) {
104 eventflags_t sts = 0;
105
106 if (err & LSR_OVERRUN)
107 sts |= SD_OVERRUN_ERROR;
108 if (err & LSR_PARITY)
109 sts |= SD_PARITY_ERROR;
110 if (err & LSR_FRAMING)
111 sts |= SD_FRAMING_ERROR;
112 if (err & LSR_BREAK)
113 sts |= SD_BREAK_DETECTED;
114 osalSysLockFromISR();
115 chnAddFlagsI(sdp, sts);
116 osalSysUnlockFromISR();
117}
118
119/**
120 * @brief Common IRQ handler.
121 * @note Tries hard to clear all the pending interrupt sources, we dont want
122 * to go through the whole ISR and have another interrupt soon after.
123 *
124 * @param[in] sdp communication channel associated to the UART
125 */
126static void serve_interrupt(SerialDriver *sdp) {
127 UART *u = sdp->uart;
128
129 while (TRUE) {
130 switch (u->UART_IIR & IIR_SRC_MASK) {
131 case IIR_SRC_NONE:
132 return;
133 case IIR_SRC_ERROR:
134 set_error(sdp, u->UART_LSR);
135 break;
136 case IIR_SRC_TIMEOUT:
137 case IIR_SRC_RX:
138 osalSysLockFromISR();
139 if (iqIsEmptyI(&sdp->iqueue))
140 chnAddFlagsI(sdp, CHN_INPUT_AVAILABLE);
141 osalSysUnlockFromISR();
142 while (u->UART_LSR & LSR_RBR_FULL) {
143 osalSysLockFromISR();
144 if (iqPutI(&sdp->iqueue, u->UART_RBR) < MSG_OK)
145 chnAddFlagsI(sdp, SD_OVERRUN_ERROR);
146 osalSysUnlockFromISR();
147 }
148 break;
149 case IIR_SRC_TX:
150 {
151 int i = LPC214x_UART_FIFO_PRELOAD;
152 do {
153 msg_t b;
154
155 osalSysLockFromISR();
156 b = oqGetI(&sdp->oqueue);
157 osalSysUnlockFromISR();
158 if (b < MSG_OK) {
159 u->UART_IER &= ~IER_THRE;
160 osalSysLockFromISR();
161 chnAddFlagsI(sdp, CHN_OUTPUT_EMPTY);
162 osalSysUnlockFromISR();
163 break;
164 }
165 u->UART_THR = b;
166 } while (--i);
167 }
168 break;
169 default:
170 (void) u->UART_THR;
171 (void) u->UART_RBR;
172 }
173 }
174}
175
176/**
177 * @brief Attempts a TX FIFO preload.
178 */
179static void preload(SerialDriver *sdp) {
180 UART *u = sdp->uart;
181
182 if (u->UART_LSR & LSR_THRE) {
183 int i = LPC214x_UART_FIFO_PRELOAD;
184 do {
185 msg_t b = oqGetI(&sdp->oqueue);
186 if (b < MSG_OK) {
187 chnAddFlagsI(sdp, CHN_OUTPUT_EMPTY);
188 return;
189 }
190 u->UART_THR = b;
191 } while (--i);
192 }
193 u->UART_IER |= IER_THRE;
194}
195
196/**
197 * @brief Driver SD1 output notification.
198 */
199#if USE_LPC214x_UART0 || defined(__DOXYGEN__)
200static void notify1(io_queue_t *qp) {
201
202 (void)qp;
203 preload(&SD1);
204}
205#endif
206
207/**
208 * @brief Driver SD2 output notification.
209 */
210#if USE_LPC214x_UART1 || defined(__DOXYGEN__)
211static void notify2(io_queue_t *qp) {
212
213 (void)qp;
214 preload(&SD2);
215}
216#endif
217
218/*===========================================================================*/
219/* Driver interrupt handlers. */
220/*===========================================================================*/
221
222/**
223 * @brief UART0 IRQ handler.
224 *
225 * @isr
226 */
227#if USE_LPC214x_UART0 || defined(__DOXYGEN__)
228CH_IRQ_HANDLER(UART0IrqHandler) {
229
230 CH_IRQ_PROLOGUE();
231
232 serve_interrupt(&SD1);
233 VICVectAddr = 0;
234
235 CH_IRQ_EPILOGUE();
236}
237#endif
238
239/**
240 * @brief UART1 IRQ handler.
241 *
242 * @isr
243 */
244#if USE_LPC214x_UART1 || defined(__DOXYGEN__)
245CH_IRQ_HANDLER(UART1IrqHandler) {
246
247 CH_IRQ_PROLOGUE();
248
249 serve_interrupt(&SD2);
250 VICVectAddr = 0;
251
252 CH_IRQ_EPILOGUE();
253}
254#endif
255
256/*===========================================================================*/
257/* Driver exported functions. */
258/*===========================================================================*/
259
260/**
261 * @brief Low level serial driver initialization.
262 *
263 * @notapi
264 */
265void sd_lld_init(void) {
266
267#if USE_LPC214x_UART0
268 sdObjectInit(&SD1, NULL, notify1);
269 SD1.uart = U0Base;
270 SetVICVector(UART0IrqHandler, LPC214x_UART0_PRIORITY, SOURCE_UART0);
271#endif
272#if USE_LPC214x_UART1
273 sdObjectInit(&SD2, NULL, notify2);
274 SD2.uart = U1Base;
275 SetVICVector(UART1IrqHandler, LPC214x_UART1_PRIORITY, SOURCE_UART1);
276#endif
277}
278
279/**
280 * @brief Low level serial driver configuration and (re)start.
281 *
282 * @param[in] sdp pointer to a @p SerialDriver object
283 * @param[in] config the architecture-dependent serial driver configuration.
284 * If this parameter is set to @p NULL then a default
285 * configuration is used.
286 *
287 * @notapi
288 */
289void sd_lld_start(SerialDriver *sdp, const SerialConfig *config) {
290
291 if (config == NULL)
292 config = &default_config;
293
294 if (sdp->state == SD_STOP) {
295#if USE_LPC214x_UART0
296 if (&SD1 == sdp) {
297 PCONP = (PCONP & PCALL) | PCUART0;
298 VICIntEnable = INTMASK(SOURCE_UART0);
299 }
300#endif
301#if USE_LPC214x_UART1
302 if (&SD2 == sdp) {
303 PCONP = (PCONP & PCALL) | PCUART1;
304 VICIntEnable = INTMASK(SOURCE_UART1);
305 }
306#endif
307 }
308 uart_init(sdp, config);
309}
310
311/**
312 * @brief Low level serial driver stop.
313 * @details De-initializes the UART, stops the associated clock, resets the
314 * interrupt vector.
315 *
316 * @param[in] sdp pointer to a @p SerialDriver object
317 *
318 * @notapi
319 */
320void sd_lld_stop(SerialDriver *sdp) {
321
322 if (sdp->state == SD_READY) {
323 uart_deinit(sdp->uart);
324#if USE_LPC214x_UART0
325 if (&SD1 == sdp) {
326 PCONP = (PCONP & PCALL) & ~PCUART0;
327 VICIntEnClear = INTMASK(SOURCE_UART0);
328 return;
329 }
330#endif
331#if USE_LPC214x_UART1
332 if (&SD2 == sdp) {
333 PCONP = (PCONP & PCALL) & ~PCUART1;
334 VICIntEnClear = INTMASK(SOURCE_UART1);
335 return;
336 }
337#endif
338 }
339}
340
341#endif /* HAL_USE_SERIAL */
342
343/** @} */