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

Last change on this file since c4b058ca was ed6365a, checked in by Joel Sherrill <joel.sherrill@…>, on Oct 14, 2014 at 4:03:38 PM

bfin libcpu and libbsp: Fix warnings

  • 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_cflag & CBAUD) {
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.