source: rtems/c/src/lib/libcpu/bfin/serial/uart.c @ 0c5ea9b

4.10
Last change on this file since 0c5ea9b was 0c5ea9b, checked in by Joel Sherrill <joel.sherrill@…>, on 04/20/11 at 20:19:52

2011-04-20 Rohan Kangralkar <rkangral@…>

PR 1781/bsps

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