source: rtems/c/src/lib/libcpu/bfin/serial/uart.c @ 1c6926c1

Last change on this file since 1c6926c1 was 1c6926c1, checked in by Kevin Kirspel <kevin-kirspel@…>, on Mar 21, 2017 at 7:39:48 PM

termios: Synchronize with latest FreeBSD headers

Adding modified FreeBSD headers to synchronize RTEMS termios with
FreeBSD. Modify termios to support dedicated input and output baud for
termios structure. Updated BSPs to use dedicated input and output baud
in termios structure. Updated tools to use dedicated input and output
baud in termios structure. Updated termios testsuites to use dedicated
input and output baud in termios structure.

Close #2897.

  • Property mode set to 100644
File size: 14.0 KB
Line 
1/*  UART driver for Blackfin
2 */
3
4/*
5 *  Copyright (c) 2008 Kallisti Labs, Los Gatos, CA, USA
6 *             written by Allan Hessenflow <allanh@kallisti.com>
7 *
8 *  The license and distribution terms for this file may be
9 *  found in the file LICENSE in this distribution or at
10 *  http://www.rtems.org/license/LICENSE.
11 */
12
13#include <rtems.h>
14#include <rtems/libio.h>
15#include <rtems/termiostypes.h>
16#include <termios.h>
17#include <stdlib.h>
18
19#include <libcpu/uartRegs.h>
20#include <libcpu/dmaRegs.h>
21#include "uart.h"
22
23/* flags */
24#define BFIN_UART_XMIT_BUSY 0x01
25
26static bfin_uart_config_t *uartsConfig;
27
28static int pollRead(int minor)
29{
30  int c;
31  uint32_t base;
32
33  base = uartsConfig->channels[minor].uart_baseAddress;
34
35  /* check to see if driver is using interrupts so this call will be
36     harmless (though non-functional) in case some debug code tries to
37     use it */
38  if (!uartsConfig->channels[minor].uart_useInterrupts &&
39      *((uint16_t volatile *) (base + UART_LSR_OFFSET)) & UART_LSR_DR)
40    c = *((uint16_t volatile *) (base + UART_RBR_OFFSET));
41  else
42    c = -1;
43
44  return c;
45}
46
47char bfin_uart_poll_read(rtems_device_minor_number minor)
48{
49  int c;
50
51  do {
52    c = pollRead(minor);
53  } while (c == -1);
54
55  return c;
56}
57
58void bfin_uart_poll_write(int minor, char c)
59{
60  uint32_t base;
61
62  base = uartsConfig->channels[minor].uart_baseAddress;
63
64  while (!(*((uint16_t volatile *) (base + UART_LSR_OFFSET)) & UART_LSR_THRE))
65    ;
66  *(uint16_t volatile *) (base + UART_THR_OFFSET) = c;
67}
68
69/*
70 *  Console Termios Support Entry Points
71 *
72 */
73
74static ssize_t pollWrite(int minor, const char *buf, size_t len)
75{
76  size_t count;
77  for ( count = 0; count < len; count++ )
78    bfin_uart_poll_write(minor, *buf++);
79
80  return count;
81}
82
83/**
84 * Routine to initialize the hardware. It initialize the DMA,
85 * interrupt if required.
86 * @param channel channel information
87 */
88static void initializeHardware(bfin_uart_channel_t *channel)
89{
90  uint16_t divisor        = 0;
91  uint32_t base           = 0;
92  uint32_t tx_dma_base    = 0;
93
94  if ( NULL == channel ) {
95    return;
96  }
97
98  base        = channel->uart_baseAddress;
99  tx_dma_base = channel->uart_txDmaBaseAddress;
100  /**
101   * RX based DMA and interrupt is not supported yet
102   * uint32_t tx_dma_base    = 0;
103   *
104   * rx_dma_base = channel->uart_rxDmaBaseAddress;
105   */
106
107
108  *(uint16_t volatile *) (base + UART_IER_OFFSET) = 0;
109
110  if ( 0 != channel->uart_baud) {
111    divisor = (uint16_t) (uartsConfig->freq /
112        (channel->uart_baud * 16));
113  } else {
114    divisor = (uint16_t) (uartsConfig->freq / (9600 * 16));
115  }
116
117  *(uint16_t volatile *) (base + UART_LCR_OFFSET) = UART_LCR_DLAB;
118  *(uint16_t volatile *) (base + UART_DLL_OFFSET) = (divisor & 0xff);
119  *(uint16_t volatile *) (base + UART_DLH_OFFSET) = ((divisor >> 8) & 0xff);
120
121  *(uint16_t volatile *) (base + UART_LCR_OFFSET) = UART_LCR_WLS_8;
122
123  *(uint16_t volatile *) (base + UART_GCTL_OFFSET) = UART_GCTL_UCEN;
124
125  /**
126   * To clear previous status
127   * divisor is a temp variable here
128   */
129  divisor = *(uint16_t volatile *) (base + UART_LSR_OFFSET);
130  divisor = *(uint16_t volatile *) (base + UART_RBR_OFFSET);
131  divisor = *(uint16_t volatile *) (base + UART_IIR_OFFSET);
132
133  if ( channel->uart_useDma ) {
134    *(uint16_t  volatile *)(tx_dma_base + DMA_CONFIG_OFFSET) = 0;
135    *(uint16_t  volatile *)(tx_dma_base + DMA_CONFIG_OFFSET) = DMA_CONFIG_DI_EN
136        | DMA_CONFIG_SYNC ;
137    *(uint16_t  volatile *)(tx_dma_base + DMA_IRQ_STATUS_OFFSET) |=
138        DMA_IRQ_STATUS_DMA_DONE | DMA_IRQ_STATUS_DMA_ERR;
139
140  } else {
141    /**
142    * We use polling or interrupts only sending one char at a time :(
143    */
144  }
145}
146
147
148/**
149 * Set the UART attributes.
150 * @param minor
151 * @param termios
152 * @return
153 */
154static int setAttributes(int minor, const struct termios *termios)
155{
156  uint32_t base;
157  int baud;
158  uint16_t divisor;
159  uint16_t lcr;
160
161  base = uartsConfig->channels[minor].uart_baseAddress;
162  switch (termios->c_ospeed) {
163    case B0:      baud = 0;      break;
164    case B50:     baud = 50;     break;
165    case B75:     baud = 75;     break;
166    case B110:    baud = 110;    break;
167    case B134:    baud = 134;    break;
168    case B150:    baud = 150;    break;
169    case B200:    baud = 200;    break;
170    case B300:    baud = 300;    break;
171    case B600:    baud = 600;    break;
172    case B1200:   baud = 1200;   break;
173    case B1800:   baud = 1800;   break;
174    case B2400:   baud = 2400;   break;
175    case B4800:   baud = 4800;   break;
176    case B9600:   baud = 9600;   break;
177    case B19200:  baud = 19200;  break;
178    case B38400:  baud = 38400;  break;
179    case B57600:  baud = 57600;  break;
180    case B115200: baud = 115200; break;
181    case B230400: baud = 230400; break;
182    case B460800: baud = 460800; break;
183    default: baud = -1; break;
184  }
185  if (baud > 0 && uartsConfig->channels[minor].uart_baud)
186    baud = uartsConfig->channels[minor].uart_baud;
187  switch (termios->c_cflag & CSIZE) {
188  case CS5: lcr = UART_LCR_WLS_5; break;
189  case CS6: lcr = UART_LCR_WLS_6; break;
190  case CS7: lcr = UART_LCR_WLS_7; break;
191  default:
192  case CS8: lcr = UART_LCR_WLS_8; break;
193  }
194  switch (termios->c_cflag & (PARENB | PARODD)) {
195    case PARENB:
196      lcr |= UART_LCR_PEN | UART_LCR_EPS;
197      break;
198    case PARENB | PARODD:
199      lcr |= UART_LCR_PEN;
200      break;
201    default:
202      break;
203  }
204  if (termios->c_cflag & CSTOPB)
205    lcr |= UART_LCR_STB;
206
207  if (baud > 0) {
208    divisor = (uint16_t) (uartsConfig->freq / (baud * 16));
209    *(uint16_t volatile *) (base + UART_LCR_OFFSET) = lcr | UART_LCR_DLAB;
210    *(uint16_t volatile *) (base + UART_DLL_OFFSET) = (divisor & 0xff);
211    *(uint16_t volatile *) (base + UART_DLH_OFFSET) = ((divisor >> 8) & 0xff);
212  }
213  *(uint16_t volatile *) (base + UART_LCR_OFFSET) = lcr;
214
215  return 0;
216}
217
218/**
219 * Interrupt based uart tx routine. The routine writes one character at a time.
220 *
221 * @param minor Minor number to indicate uart number
222 * @param buf Character buffer which stores characters to be transmitted.
223 * @param len Length of buffer to be transmitted.
224 * @return
225 */
226static ssize_t uart_interruptWrite(int minor, const char *buf, size_t len)
227{
228  uint32_t              base      = 0;
229  bfin_uart_channel_t*  channel   = NULL;
230
231  /**
232   * Sanity Check
233   */
234  if (
235    NULL == buf || NULL == channel || NULL == uartsConfig
236      || minor < 0 || 0 == len
237  ) {
238    return 0;
239  }
240
241  channel = &(uartsConfig->channels[minor]);
242
243  if ( NULL == channel || channel->flags &  BFIN_UART_XMIT_BUSY ) {
244    return 0;
245  }
246
247  base = channel->uart_baseAddress;
248
249  channel->flags |= BFIN_UART_XMIT_BUSY;
250  channel->length = 1;
251  *(uint16_t volatile *) (base + UART_THR_OFFSET) = *buf;
252  *(uint16_t volatile *) (base + UART_IER_OFFSET) = UART_IER_ETBEI;
253
254  return 0;
255}
256
257/**
258 * This function implements RX ISR
259 */
260void bfinUart_rxIsr(void *_arg)
261{
262  /**
263   * TODO: UART RX ISR implementation.
264   */
265}
266
267/**
268 * This function implements TX ISR. The function gets called when the TX FIFO is
269 * empty. It clears the interrupt and dequeues the character. It only tx one
270 * character at a time.
271 *
272 * TODO: error handling.
273 * @param _arg gets the channel information.
274 */
275void bfinUart_txIsr(void *_arg)
276{
277  bfin_uart_channel_t*  channel = NULL;
278  uint32_t              base    = 0;
279
280  /**
281   * Sanity check
282   */
283  if (NULL == _arg) {
284    /** It should never be NULL */
285    return;
286  }
287
288  channel = (bfin_uart_channel_t *) _arg;
289
290  base = channel->uart_baseAddress;
291
292  *(uint16_t volatile *) (base + UART_IER_OFFSET) &= ~UART_IER_ETBEI;
293  channel->flags &= ~BFIN_UART_XMIT_BUSY;
294
295  rtems_termios_dequeue_characters(channel->termios, channel->length);
296}
297
298/**
299 * interrupt based DMA write Routine. It configure the DMA to write len bytes.
300 * The DMA supports 64K data only.
301 *
302 * @param minor Identification number of the UART.
303 * @param buf Character buffer pointer
304 * @param len length of data items to be written
305 * @return data already written
306 */
307static ssize_t uart_DmaWrite(int minor, const char *buf, size_t len)
308{
309  uint32_t              base        = 0;
310  bfin_uart_channel_t*  channel     = NULL;
311  uint32_t              tx_dma_base = 0;
312
313  /**
314   * Sanity Check
315   */
316  if ( NULL == buf || 0 > minor || NULL == uartsConfig || 0 == len ) {
317    return 0;
318  }
319
320  channel = &(uartsConfig->channels[minor]);
321
322  /**
323   * Sanity Check and check for transmit busy.
324   */
325  if ( NULL == channel || BFIN_UART_XMIT_BUSY & channel->flags ) {
326    return 0;
327  }
328
329  base        = channel->uart_baseAddress;
330  tx_dma_base = channel->uart_txDmaBaseAddress;
331
332  channel->flags |= BFIN_UART_XMIT_BUSY;
333  channel->length = len;
334
335  *(uint16_t volatile *) (tx_dma_base + DMA_CONFIG_OFFSET) &= ~DMA_CONFIG_DMAEN;
336  *(uint32_t volatile *) (tx_dma_base + DMA_START_ADDR_OFFSET) = (uint32_t)buf;
337  *(uint16_t volatile *) (tx_dma_base + DMA_X_COUNT_OFFSET) = channel->length;
338  *(uint16_t volatile *) (tx_dma_base + DMA_X_MODIFY_OFFSET) = 1;
339  *(uint16_t volatile *) (tx_dma_base + DMA_CONFIG_OFFSET) |= DMA_CONFIG_DMAEN;
340  *(uint16_t volatile *) (base + UART_IER_OFFSET) = UART_IER_ETBEI;
341
342  return 0;
343}
344
345/**
346 * RX DMA ISR.
347 * The polling route is used for receiving the characters. This is a place
348 * holder for future implementation.
349 * @param _arg
350 */
351void bfinUart_rxDmaIsr(void *_arg)
352{
353/**
354 * TODO: Implementation of RX DMA
355 */
356}
357
358/**
359 * This function implements TX dma ISR. It clears the IRQ and dequeues a char
360 * The channel argument will have the base address. Since there are two uart
361 * and both the uarts can use the same tx dma isr.
362 *
363 * TODO: 1. Error checking 2. sending correct length ie after looking at the
364 * number of elements the uart transmitted.
365 *
366 * @param _arg argument passed to the interrupt handler. It contains the
367 * channel argument.
368 */
369void bfinUart_txDmaIsr(void *_arg)
370{
371  bfin_uart_channel_t*  channel     = NULL;
372  uint32_t              tx_dma_base = 0;
373
374  /**
375   * Sanity check
376   */
377  if (NULL == _arg) {
378    /** It should never be NULL */
379    return;
380  }
381
382  channel = (bfin_uart_channel_t *) _arg;
383
384  tx_dma_base = channel->uart_txDmaBaseAddress;
385
386  if ((*(uint16_t volatile *) (tx_dma_base + DMA_IRQ_STATUS_OFFSET)
387      & DMA_IRQ_STATUS_DMA_DONE)) {
388
389    *(uint16_t volatile *) (tx_dma_base + DMA_IRQ_STATUS_OFFSET)
390                            |= DMA_IRQ_STATUS_DMA_DONE | DMA_IRQ_STATUS_DMA_ERR;
391    channel->flags &= ~BFIN_UART_XMIT_BUSY;
392    rtems_termios_dequeue_characters(channel->termios, channel->length);
393  } else {
394    /* UART DMA did not generate interrupt.
395     * This routine must not be called.
396     */
397  }
398}
399
400/**
401 * Function called during exit
402 */
403static void uart_exit(void)
404{
405  /**
406   * TODO: Flushing of quques
407   */
408}
409
410/**
411 * Opens the device in different modes. The supported modes are
412 * 1. Polling
413 * 2. Interrupt
414 * 3. DMA
415 * At exit the uart_Exit function will be called to flush the device.
416 *
417 * @param major Major number of the device
418 * @param minor Minor number of the device
419 * @param arg
420 * @return
421 */
422rtems_device_driver bfin_uart_open(rtems_device_major_number major,
423    rtems_device_minor_number minor, void *arg) {
424  rtems_status_code             sc    = RTEMS_NOT_DEFINED;
425  rtems_libio_open_close_args_t *args = NULL;
426
427  /**
428   * Callback function for polling
429   */
430  static const rtems_termios_callbacks pollCallbacks = {
431      NULL,                        /* firstOpen */
432      NULL,                        /* lastClose */
433      pollRead,                    /* pollRead */
434      pollWrite,                   /* write */
435      setAttributes,               /* setAttributes */
436      NULL,                        /* stopRemoteTx */
437      NULL,                        /* startRemoteTx */
438      TERMIOS_POLLED               /* outputUsesInterrupts */
439  };
440
441  /**
442   * Callback function for interrupt based transfers without DMA.
443   * We use interrupts for writing only. For reading we use polling.
444   */
445  static const rtems_termios_callbacks interruptCallbacks = {
446      NULL,                        /* firstOpen */
447      NULL,                        /* lastClose */
448      pollRead,                    /* pollRead */
449      uart_interruptWrite,              /* write */
450      setAttributes,               /* setAttributes */
451      NULL,                        /* stopRemoteTx */
452      NULL,                        /* startRemoteTx */
453      TERMIOS_IRQ_DRIVEN           /* outputUsesInterrupts */
454  };
455
456  /**
457   * Callback function for interrupt based DMA transfers.
458   * We use interrupts for writing only. For reading we use polling.
459   */
460  static const rtems_termios_callbacks interruptDmaCallbacks = {
461      NULL,                        /* firstOpen */
462      NULL,                        /* lastClose */
463      NULL,                        /* pollRead */
464      uart_DmaWrite,              /* write */
465      setAttributes,               /* setAttributes */
466      NULL,                        /* stopRemoteTx */
467      NULL,                        /* startRemoteTx */
468      TERMIOS_IRQ_DRIVEN           /* outputUsesInterrupts */
469  };
470
471  if ( NULL == uartsConfig || 0 > minor || minor >= uartsConfig->num_channels) {
472    return RTEMS_INVALID_NUMBER;
473  }
474
475  /**
476   * Opens device for handling uart send request either by
477   * 1. interrupt with DMA
478   * 2. interrupt based
479   * 3. Polling
480   */
481  if ( uartsConfig->channels[minor].uart_useDma ) {
482    sc = rtems_termios_open(major, minor, arg, &interruptDmaCallbacks);
483  } else {
484    sc = rtems_termios_open(major, minor, arg,
485        uartsConfig->channels[minor].uart_useInterrupts ?
486            &interruptCallbacks : &pollCallbacks);
487  }
488
489  args = arg;
490  uartsConfig->channels[minor].termios = args->iop->data1;
491
492  atexit(uart_exit);
493
494  return sc;
495}
496
497/**
498* Uart initialization function.
499* @param major major number of the device
500* @param config configuration parameters
501* @return rtems status code
502*/
503rtems_status_code bfin_uart_initialize(
504  rtems_device_major_number major,
505  bfin_uart_config_t *config
506)
507{
508  rtems_status_code sc = RTEMS_NOT_DEFINED;
509  int               i  = 0;
510
511  rtems_termios_initialize();
512
513  /*
514   *  Register Device Names
515   */
516  uartsConfig = config;
517  for (i = 0; i < config->num_channels; i++) {
518    config->channels[i].termios = NULL;
519    config->channels[i].flags = 0;
520    initializeHardware(&(config->channels[i]));
521    sc = rtems_io_register_name(config->channels[i].name, major, i);
522    if (RTEMS_SUCCESSFUL != sc) {
523      return sc;
524    }
525  }
526
527  return sc;
528}
Note: See TracBrowser for help on using the repository browser.