source: rtems/c/src/lib/libcpu/bfin/serial/uart.c @ 9b4422a2

4.115
Last change on this file since 9b4422a2 was 9b4422a2, checked in by Joel Sherrill <joel.sherrill@…>, on 05/03/12 at 15:09:24

Remove All CVS Id Strings Possible Using a Script

Script does what is expected and tries to do it as
smartly as possible.

+ remove occurrences of two blank comment lines

next to each other after Id string line removed.

+ remove entire comment blocks which only exited to

contain CVS Ids

+ If the processing left a blank line at the top of

a file, it was removed.

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