Contiki-NG
uart-driver.c
1/*
2 * Copyright (c) 2015 NXP B.V.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of NXP B.V. nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY NXP B.V. AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL NXP B.V. OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * This file is part of the Contiki operating system.
30 *
31 * Author: Lee Mitchell
32 * Integrated into Contiki by Beshr Al Nahas
33 *
34 */
35
36#include <jendefs.h>
37
38#ifdef DEBUG
39#include <dbg.h>
40#else
41#define DBG_vPrintf(...)
42#endif
43
44#include "contiki.h"
45#include "uart-driver.h"
46#include "sys/rtimer.h"
47#include "watchdog.h"
48#include <math.h>
49#include <AppHardwareApi.h>
50
51#if UART_XONXOFF_FLOW_CTRL
52
53#include "sys/process.h"
54
55#define TX_FIFO_SW_FLOW_LIMIT 8 /* Maximum allowed fill level for tx fifo */
56#if TX_FIFO_SW_FLOW_LIMIT > 16
57#undef TX_FIFO_SW_FLOW_LIMIT
58#define TX_FIFO_SW_FLOW_LIMIT 16
59#warning "TX_FIFO_SW_FLOW_LIMIT too big. Forced to 16."
60#endif /* TX_FIFO_SW_FLOW_LIMIT > 16 */
61
62#define XON 17
63#define XOFF 19
64
65extern volatile unsigned char xonxoff_state;
66
67#endif /* UART_XONXOFF_FLOW_CTRL */
68
69#define DEBUG_UART_BUFFERED FALSE
70
71#define CHAR_DEADLINE (uart_char_delay * 100)
72
73/*** Local Function Prototypes ***/
74static void uart_driver_isr(uint32_t device_id, uint32_t item_bitmap);
75#if !UART_XONXOFF_FLOW_CTRL
76static int16_t uart_driver_get_tx_fifo_available_space(uint8_t uart_dev);
77#endif /* !UART_XONXOFF_FLOW_CTRL */
78static void uart_driver_set_baudrate(uint8_t uart_dev, uint8_t br);
79static void uart_driver_set_high_baudrate(uint8_t uart_dev, uint32_t baud_rate);
80
81/*** Local Variables ***/
82#define UART_NUM_UARTS 2
83static uint16_t tx_fifo_size[UART_NUM_UARTS] = { 0 };
84static uint8_t active_uarts[UART_NUM_UARTS] = { 0 };
85/** slip input function pointer */
86static int(*uart_input[UART_NUM_UARTS]) (unsigned char) = { 0 };
87/* time in uSec for transmitting 1 char */
88static uint16_t uart_char_delay = 0;
89static volatile int8_t interrupt_enabled[UART_NUM_UARTS] = { 0 };
90static volatile int8_t interrupt_enabled_saved[UART_NUM_UARTS] = { 0 };
91
92/****************************************************************************
93 *
94 * NAME: uart_driver_init
95 *
96 * DESCRIPTION:
97 * Initializes the specified UART device.
98 *
99 * PARAMETERS: Name RW Usage
100 * uart_dev R UART to initialise, eg, E_AHI_UART_0
101 * br R Baudrate to use (e.g. UART_RATE_115200)
102 * if br > UART_RATE_115200
103 * then uart_driver_set_baud_rate is called
104 * else vAHI_UartSetClockDivisor
105 * txbuf_data R Pointer to a memory block to use
106 * and rxbuf_data as uart tx/rx fifo
107 * txbuf_size R size of tx fifo (valid range: 16-2047)
108 * txbuf_size R size of rx fifo (valid range: 16-2047)
109 * uart_input_function a function pointer to input uart rx bytes
110 * RETURNS:
111 * void
112 *
113 ****************************************************************************/
114void
115uart_driver_init(uint8_t uart_dev, uint8_t br, uint8_t *txbuf_data,
116 uint16_t txbuf_size, uint8_t *rxbuf_data, uint16_t rxbuf_size,
117 int (*uart_input_function)(unsigned char c))
118{
119#if !UART_HW_FLOW_CTRL
120 /* Disable RTS/CTS */
121 vAHI_UartSetRTSCTS(uart_dev, FALSE);
122#endif
123
124 tx_fifo_size[uart_dev] = txbuf_size;
125
126 /* Configure the selected Uart */
127 uint8_t uart_enabled = bAHI_UartEnable(uart_dev, txbuf_data, txbuf_size,
128 rxbuf_data, rxbuf_size);
129 /* fallback to internal buffers */
130 if(!uart_enabled) {
131 vAHI_UartEnable(uart_dev);
132 tx_fifo_size[uart_dev] = 16; /* Fixed size */
133 }
134 /* Reset tx/rx fifos */
135 vAHI_UartReset(uart_dev, TRUE, TRUE);
136 vAHI_UartReset(uart_dev, FALSE, FALSE);
137
138 uart_driver_set_baudrate(uart_dev, br);
139
140 /* install interrupt service callback */
141 if(uart_dev == E_AHI_UART_0) {
142 vAHI_Uart0RegisterCallback((void *)uart_driver_isr);
143 } else {
144 vAHI_Uart1RegisterCallback((void *)uart_driver_isr);
145 /* Enable RX interrupt */
146 }
147 uart_driver_enable_interrupts(uart_dev);
148 uart_input[uart_dev] = uart_input_function;
149 active_uarts[uart_dev] = 1;
150
151#if UART_HW_FLOW_CTRL
152 /* Configure HW flow control */
153 vAHI_UartSetAutoFlowCtrl(uart_dev, E_AHI_UART_FIFO_ARTS_LEVEL_13, /* uint8 const u8RxFifoLevel,*/
154 FALSE, /* bool_t const bFlowCtrlPolarity,*/
155 TRUE, /* bool_t const bAutoRts, */
156 TRUE /* bool_t const bAutoCts */);
157#endif
158
159 DBG_vPrintf("UART %d init: using %s buffers %d\n", uart_dev,
160 uart_enabled ? "external" : "internal", tx_fifo_size[uart_dev]);
161}
162void
163uart_driver_enable_interrupts(uint8_t uart_dev)
164{
165 /* wait while char being tx is done */
166 while((u8AHI_UartReadLineStatus(uart_dev) & E_AHI_UART_LS_THRE) == 0) ;
167
168 vAHI_UartSetInterrupt(uart_dev, FALSE /*bEnableModemStatus*/,
169 FALSE /*bEnableRxLineStatus == Break condition */,
170 FALSE /*bEnableTxFifoEmpty*/,
171 TRUE /* bEnableRxData */, E_AHI_UART_FIFO_LEVEL_14);
172 interrupt_enabled[uart_dev] = 1;
173}
174void
175uart_driver_disable_interrupts(uint8_t uart_dev)
176{
177 /* wait while char being tx is done */
178 while((u8AHI_UartReadLineStatus(uart_dev) & E_AHI_UART_LS_THRE) == 0) ;
179
180 vAHI_UartSetInterrupt(uart_dev, FALSE /*bEnableModemStatus*/,
181 FALSE /*bEnableRxLineStatus == Break condition */,
182 FALSE /*bEnableTxFifoEmpty*/,
183 FALSE /* bEnableRxData */, E_AHI_UART_FIFO_LEVEL_14);
184 interrupt_enabled[uart_dev] = 0;
185}
186void
187uart_driver_store_interrupts(uint8_t uart_dev)
188{
189 interrupt_enabled_saved[uart_dev] = interrupt_enabled[uart_dev];
190}
191void
192uart_driver_restore_interrupts(uint8_t uart_dev)
193{
194 if(interrupt_enabled_saved[uart_dev]) {
195 uart_driver_enable_interrupts(uart_dev);
196 } else {
197 uart_driver_disable_interrupts(uart_dev);
198 }
199}
200int8_t
201uart_driver_interrupt_is_enabled(uint8_t uart_dev)
202{
203 return interrupt_enabled[uart_dev];
204}
205void
206uart_driver_set_input(uint8_t uart_dev, int
207 (*uart_input_function)(unsigned char c))
208{
209 uart_input[uart_dev] = uart_input_function;
210}
211/****************************************************************************
212 *
213 * NAME: uart_driver_read
214 *
215 * DESCRIPTION:
216 * Reads 1 byte from the RX buffer. If there is no data in the
217 * buffer, then return FALSE
218 *
219 * PARAMETERS: Name RW Usage
220 * uart_dev R UART to use, eg, E_AHI_UART_0
221 *
222 * RETURNS:
223 * TRUE if a byte has been read from the queue
224 *
225 ****************************************************************************/
226uint8_t
227uart_driver_read(uint8_t uart_dev, uint8_t *data)
228{
229 if(data && u16AHI_UartReadRxFifoLevel(uart_dev) > 0) {
230 *data = u8AHI_UartReadData(uart_dev);
231 return TRUE;
232 }
233 return FALSE;
234}
235void
236uart_driver_write_buffered(uint8_t uart_dev, uint8_t ch)
237{
238 uart_driver_write_with_deadline(uart_dev, ch);
239}
240/****************************************************************************
241 *
242 * NAME: uart_driver_write_with_deadline
243 *
244 * DESCRIPTION:
245 * Writes one byte to the specified uart for transmission
246 *
247 * PARAMETERS: Name RW Usage
248 * uart_dev R UART to use, eg, E_AHI_UART_0
249 * ch R data to transmit
250 *
251 * RETURNS:
252 * void
253 *
254 ****************************************************************************/
255void
256uart_driver_write_with_deadline(uint8_t uart_dev, uint8_t ch)
257{
258#if UART_XONXOFF_FLOW_CTRL
259 /* Block until host can receive data */
260 /* Wait until there are less than N characters in TX FIFO */
261 while(xonxoff_state != XON
262 || u16AHI_UartReadTxFifoLevel(uart_dev) > TX_FIFO_SW_FLOW_LIMIT) {
264 }
265 /* write to TX FIFO and return immediately */
266 vAHI_UartWriteData(uart_dev, ch);
267#else /* UART_XONXOFF_FLOW_CTRL */
268 volatile int16_t write = 0;
270 /* wait until there is space in tx fifo */
271 RTIMER_BUSYWAIT_UNTIL(write = (uart_driver_get_tx_fifo_available_space(uart_dev) > 0),
272 CHAR_DEADLINE);
273 /* write only if there is space so we do not get stuck */
274 if(write) {
275 /* write to TX FIFO and return immediately */
276 vAHI_UartWriteData(uart_dev, ch);
277 }
278#endif /* UART_XONXOFF_FLOW_CTRL */
279}
280void
281uart_driver_write_direct(uint8_t uart_dev, uint8_t ch)
282{
283 /* Write character */
284 vAHI_UartWriteData(uart_dev, ch);
285 /* Wait for buffers to empty */
286 while((u8AHI_UartReadLineStatus(uart_dev) & E_AHI_UART_LS_THRE) == 0) ;
287 while((u8AHI_UartReadLineStatus(uart_dev) & E_AHI_UART_LS_TEMT) == 0) ;
288}
289/****************************************************************************
290 *
291 * NAME: uart_driver_rx_handler
292 *
293 * DESCRIPTION:
294 * Interrupt service callback for UART data reception. Reads a received
295 * byte from the UART and writes it to the reception buffer if it is not
296 * full.
297 *
298 * PARAMETERS: Name RW Usage
299 * uart_dev R Uart to read from
300 *
301 * RETURNS:
302 * void
303 *
304 ****************************************************************************/
305void
306uart_driver_rx_handler(uint8_t uart_dev)
307{
308 /* optimization for high throughput: Read upto 32 bytes from RX fifo.
309 * Disabled because it does not work with current slip_input_byte */
310
311 /* Status from uart_input:
312 * 0 means do not exit power saving mode
313 * -1 means RX buffer overflow ==> stop reading
314 * 1 means end of slip packet
315 */
316#if UART_XONXOFF_FLOW_CTRL
317 /* save old status */
318 int xonxoff_state_old = xonxoff_state;
319#endif /* UART_XONXOFF_FLOW_CTRL */
320 int status = 0;
321 int c = 0;
322 while(u16AHI_UartReadRxFifoLevel(uart_dev) > 0 && c++ < 32 && status == 0) {
323 if(uart_input[uart_dev] != NULL) { /* read one char at a time */
324
325 /* process received character */
326 status = (uart_input[uart_dev])(u8AHI_UartReadData(uart_dev));
327
328#if UART_XONXOFF_FLOW_CTRL
329 /* Process XON-XOFF*/
330 if(xonxoff_state == XOFF) {
331 /* XXX do not set break condition as it corrupts one character, instead we block on TX */
332 /* Instruct uart to stop TX */
333 /* vAHI_UartSetBreak(uart_dev, TRUE); */
334 break;
335 } else if(xonxoff_state_old == XOFF && xonxoff_state == XON) {
336 /* Instruct uart to resume TX if it was stopped */
337 /* vAHI_UartSetBreak(uart_dev, FALSE); */
338 }
339#endif /* UART_XONXOFF_FLOW_CTRL */
340 } else {
341 /* no input handler, or no bytes to read: Discard byte. */
342 u8AHI_UartReadData(uart_dev);
343 }
344 }
345}
346/****************************************************************************/
347/*** Local Functions ***/
348/****************************************************************************/
349
350#if !UART_XONXOFF_FLOW_CTRL
351/* Returns the free space in tx fifo, i.e., how many characters we can put */
352static int16_t
353uart_driver_get_tx_fifo_available_space(uint8_t uart_dev)
354{
355 return tx_fifo_size[uart_dev] - u16AHI_UartReadTxFifoLevel(uart_dev);
356}
357#endif /* !UART_XONXOFF_FLOW_CTRL */
358/* Initializes the specified UART with auto-selection of
359 baudrate tuning method */
360static void
361uart_driver_set_baudrate(uint8_t uart_dev, uint8_t br)
362{
363 uint32_t high_br = 0;
364 uint8_t low_br = 0;
365
366 switch(br) {
367 case UART_RATE_4800:
368 low_br = E_AHI_UART_RATE_4800;
369 uart_char_delay = 1667;
370 break;
371 case UART_RATE_9600:
372 low_br = E_AHI_UART_RATE_9600;
373 uart_char_delay = 834;
374 break;
375 case UART_RATE_19200:
376 low_br = E_AHI_UART_RATE_19200;
377 uart_char_delay = 417;
378 break;
379 case UART_RATE_38400:
380 low_br = E_AHI_UART_RATE_38400;
381 uart_char_delay = 209;
382 break;
383 case UART_RATE_76800:
384 low_br = E_AHI_UART_RATE_76800;
385 uart_char_delay = 105;
386 break;
387 case UART_RATE_115200:
388 low_br = E_AHI_UART_RATE_115200;
389 uart_char_delay = 69;
390 break;
391 case UART_RATE_230400:
392 high_br = 230400UL;
393 uart_char_delay = 35;
394 break;
395 case UART_RATE_460800:
396 high_br = 460800UL;
397 uart_char_delay = 18;
398 break;
399 case UART_RATE_500000:
400 high_br = 500000UL;
401 uart_char_delay = 16;
402 break;
403 case UART_RATE_576000:
404 high_br = 576000UL;
405 uart_char_delay = 14;
406 break;
407 case UART_RATE_921600:
408 high_br = 921600UL;
409 uart_char_delay = 9;
410 break;
411 case UART_RATE_1000000:
412 high_br = 1000000UL;
413 uart_char_delay = 8;
414 break;
415 default:
416 high_br = 1000000UL;
417 uart_char_delay = 8;
418 break;
419 }
420 if(high_br == 0) {
421 vAHI_UartSetClockDivisor(uart_dev, low_br);
422 } else {
423 uart_driver_set_high_baudrate(uart_dev, high_br);
424 }
425}
426/****************************************************************************
427 *
428 * NAME: uart_driver_set_high_baudrate
429 *
430 * DESCRIPTION:
431 * Sets the baud rate for the specified uart
432 *
433 * PARAMETERS: Name RW Usage
434 * uart_dev R UART to initialise, eg, E_AHI_UART_0
435 * baud_rate R Baudrate to use (bps eg 921600)
436 *
437 * RETURNS:
438 * void
439 *
440 ****************************************************************************/
441static void
442uart_driver_set_high_baudrate(uint8_t uart_dev, uint32_t baud_rate)
443{
444 uint16 u16Divisor = 1;
445 uint32_t u32Remainder;
446 uint8_t u8ClocksPerBit = 16;
447
448#if (ENABLE_ADVANCED_BAUD_SELECTION)
449 /* Defining ENABLE_ADVANCED_BAUD_SELECTION in the Makefile
450 * enables this code which searches for a clocks per bit setting
451 * that gets closest to the configured rate.
452 */
453 uint32_t u32CalcBaudRate = 0;
454 int32 i32BaudError = 0x7FFFFFFF;
455
456 DBG_vPrintf(DEBUG_UART_BUFFERED, "Config uart=%d, baud=%d\n", uart_dev,
457 baud_rate);
458
459 while(ABS(i32BaudError) > (int32)(baud_rate >> 4)) { /* 6.25% (100/16) error */
460 if(--u8ClocksPerBit < 3) {
461 DBG_vPrintf(DEBUG_UART_BUFFERED,
462 "Could not calculate UART settings for target baud!");
463 return;
464 }
465#endif /* ENABLE_ADVANCED_BAUD_SELECTION */
466
467 /* Calculate Divisor register = 16MHz / (16 x baud rate) */
468 u16Divisor = (uint16)(16000000UL / ((u8ClocksPerBit + 1) * baud_rate));
469
470 /* Correct for rounding errors */
471 u32Remainder =
472 (uint32_t)(16000000UL % ((u8ClocksPerBit + 1) * baud_rate));
473
474 if(u32Remainder >= (((u8ClocksPerBit + 1) * baud_rate) / 2)) {
475 u16Divisor += 1;
476 }
477#if (ENABLE_ADVANCED_BAUD_SELECTION)
478 DBG_vPrintf(DEBUG_UART_BUFFERED, "Divisor=%d, cpb=%d\n", u16Divisor,
479 u8ClocksPerBit);
480
481 u32CalcBaudRate = (16000000UL / ((u8ClocksPerBit + 1) * u16Divisor));
482
483 DBG_vPrintf(DEBUG_UART_BUFFERED, "Calculated baud=%d\n", u32CalcBaudRate);
484
485 i32BaudError = (int32)u32CalcBaudRate - (int32)baud_rate;
486
487 DBG_vPrintf(DEBUG_UART_BUFFERED, "Error baud=%d\n", i32BaudError);
488}
489DBG_vPrintf(DEBUG_UART_BUFFERED, "Config uart=%d: Divisor=%d, cpb=%d\n",
490 uart_dev, u16Divisor, u8ClocksPerBit);
491
492/* Set the calculated clocks per bit */
493vAHI_UartSetClocksPerBit(uart_dev, u8ClocksPerBit);
494#endif /* ENABLE_ADVANCED_BAUD_SELECTION */
495
496 /* Set the calculated divisor */
497 vAHI_UartSetBaudDivisor(uart_dev, u16Divisor);
498}
499
500/****************************************************************************
501 *
502 * NAME: uart_driver_isr
503 *
504 * DESCRIPTION:
505 * Interrupt service callback for UART's
506 *
507 * PARAMETERS: Name RW Usage
508 * device_id R Device ID of whatever generated the
509 * interrupt
510 * item_bitmap R Which part of the device generated
511 * the interrupt
512 *
513 * RETURNS:
514 * void
515 *
516 ****************************************************************************/
517static void
518uart_driver_isr(uint32_t device_id, uint32_t item_bitmap)
519{
520 uint8_t uart_dev;
521 switch(device_id) {
522 case E_AHI_DEVICE_UART0:
523 uart_dev = E_AHI_UART_0;
524 break;
525 case E_AHI_DEVICE_UART1:
526 uart_dev = E_AHI_UART_1;
527 break;
528 default:
529 return;
530 }
531 switch(item_bitmap) {
532 /* byte available since a long time but RX-fifo not full: */
533 case E_AHI_UART_INT_TIMEOUT:
534 /* RX-fifo full: */
535 case E_AHI_UART_INT_RXDATA:
536 uart_driver_rx_handler(uart_dev);
537 break;
538 case E_AHI_UART_INT_TX:
539 break;
540 case E_AHI_UART_INT_RXLINE:
541 /* rx-line interrupt is disabled. Should not get here */
542 /* An error condition has occurred on the RxD line, such as
543 a break indication, framing error, parity error or over-run. */
544 break;
545 }
546}
547/****************************************************************************
548 *
549 * NAME: uart_driver_tx_in_progress
550 *
551 * DESCRIPTION:
552 * Returns the state of data transmission
553 *
554 * PARAMETERS: Name RW Usage
555 * uart_dev R UART to use, eg, E_AHI_UART_0
556 *
557 * RETURNS:
558 * uint8_t: TRUE if data in buffer is being transmitted
559 * FALSE if all data in buffer has been transmitted by the UART
560 *
561 ****************************************************************************/
562uint8_t
563uart_driver_tx_in_progress(uint8_t uart_dev)
564{
565
566 if(u16AHI_UartReadTxFifoLevel(uart_dev) == 0) {
567 if((u8AHI_UartReadLineStatus(uart_dev) & E_AHI_UART_LS_TEMT) != 0) {
568 return FALSE;
569 }
570 }
571 return TRUE;
572}
573#ifdef UART_EXTRAS
574
575/****************************************************************************
576 *
577 * NAME: uart_driver_flush
578 *
579 * DESCRIPTION:
580 * Flushes the buffers of the specified UART
581 *
582 * PARAMETERS: Name RW Usage
583 * uart_dev R UART to disable, eg, E_AHI_UART_0
584 * reset_tx R to reset the transmit FIFO
585 * reset_rx R to reset the receive FIFO
586 *
587 * RETURNS:
588 * void
589 *
590 ****************************************************************************/
591void
592uart_driver_flush(uint8_t uart_dev, bool_t reset_tx, bool_t reset_rx)
593{
594 /* Disable TX Fifo empty and Rx data interrupts */
595 uart_driver_disable_interrupts(uart_dev);
596
597 /* flush hardware buffer */
598 vAHI_UartReset(uart_dev, reset_tx, reset_rx);
599 vAHI_UartReset(uart_dev, FALSE, FALSE);
600
601 /* Re-enable TX Fifo empty and Rx data interrupts */
602 uart_driver_enable_interrupts(uart_dev);
603}
604#endif /* UART_EXTRAS */
Header file for the dbg-io module.
void watchdog_periodic(void)
Writes the WDT clear sequence.
Definition: watchdog.c:85
#define RTIMER_BUSYWAIT_UNTIL(cond, max_time)
Busy-wait until a condition for at most max_time.
Definition: rtimer.h:211
Header file for the Contiki process interface.
Header file for the real-time timer module.