diff options
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.c | 343 |
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.*/ | ||
35 | SerialDriver SD1; | ||
36 | #endif | ||
37 | |||
38 | #if USE_LPC214x_UART1 || defined(__DOXYGEN__) | ||
39 | /** @brief UART1 serial driver identifier.*/ | ||
40 | SerialDriver SD2; | ||
41 | #endif | ||
42 | |||
43 | /*===========================================================================*/ | ||
44 | /* Driver local variables and types. */ | ||
45 | /*===========================================================================*/ | ||
46 | |||
47 | /** @brief Driver default configuration.*/ | ||
48 | static 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 | */ | ||
64 | static 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 | */ | ||
84 | static 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 | */ | ||
103 | static 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 | */ | ||
126 | static 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 | */ | ||
179 | static 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__) | ||
200 | static 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__) | ||
211 | static 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__) | ||
228 | CH_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__) | ||
245 | CH_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 | */ | ||
265 | void 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 | */ | ||
289 | void 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 | */ | ||
320 | void 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 | /** @} */ | ||