Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:40

0001 /*  UART driver for Blackfin
0002  */
0003 
0004 /*
0005  *  Copyright (c) 2008 Kallisti Labs, Los Gatos, CA, USA
0006  *             written by Allan Hessenflow <allanh@kallisti.com>
0007  *
0008  *  The license and distribution terms for this file may be
0009  *  found in the file LICENSE in this distribution or at
0010  *  http://www.rtems.org/license/LICENSE.
0011  */
0012 
0013 #include <rtems.h>
0014 #include <rtems/libio.h>
0015 #include <rtems/termiostypes.h>
0016 #include <termios.h>
0017 #include <stdlib.h>
0018 
0019 #include <libcpu/uartRegs.h>
0020 #include <libcpu/dmaRegs.h>
0021 #include <libcpu/uart.h>
0022 
0023 /* flags */
0024 #define BFIN_UART_XMIT_BUSY 0x01
0025 
0026 static bfin_uart_config_t *uartsConfig;
0027 
0028 static int pollRead(int minor)
0029 {
0030   int c;
0031   uint32_t base;
0032 
0033   base = uartsConfig->channels[minor].uart_baseAddress;
0034 
0035   /* check to see if driver is using interrupts so this call will be
0036      harmless (though non-functional) in case some debug code tries to
0037      use it */
0038   if (!uartsConfig->channels[minor].uart_useInterrupts &&
0039       *((uint16_t volatile *) (base + UART_LSR_OFFSET)) & UART_LSR_DR)
0040     c = *((uint16_t volatile *) (base + UART_RBR_OFFSET));
0041   else
0042     c = -1;
0043 
0044   return c;
0045 }
0046 
0047 char bfin_uart_poll_read(rtems_device_minor_number minor)
0048 {
0049   int c;
0050 
0051   do {
0052     c = pollRead(minor);
0053   } while (c == -1);
0054 
0055   return c;
0056 }
0057 
0058 void bfin_uart_poll_write(int minor, char c)
0059 {
0060   uint32_t base;
0061 
0062   base = uartsConfig->channels[minor].uart_baseAddress;
0063 
0064   while (!(*((uint16_t volatile *) (base + UART_LSR_OFFSET)) & UART_LSR_THRE))
0065     ;
0066   *(uint16_t volatile *) (base + UART_THR_OFFSET) = c;
0067 }
0068 
0069 /*
0070  *  Console Termios Support Entry Points
0071  *
0072  */
0073 
0074 static ssize_t pollWrite(int minor, const char *buf, size_t len)
0075 {
0076   size_t count;
0077   for ( count = 0; count < len; count++ )
0078     bfin_uart_poll_write(minor, *buf++);
0079 
0080   return count;
0081 }
0082 
0083 /**
0084  * Routine to initialize the hardware. It initialize the DMA,
0085  * interrupt if required.
0086  * @param channel channel information
0087  */
0088 static void initializeHardware(bfin_uart_channel_t *channel)
0089 {
0090   uint16_t divisor        = 0;
0091   uint32_t base           = 0;
0092   uint32_t tx_dma_base    = 0;
0093 
0094   if ( NULL == channel ) {
0095     return;
0096   }
0097 
0098   base        = channel->uart_baseAddress;
0099   tx_dma_base = channel->uart_txDmaBaseAddress;
0100   /**
0101    * RX based DMA and interrupt is not supported yet
0102    * uint32_t tx_dma_base    = 0;
0103    *
0104    * rx_dma_base = channel->uart_rxDmaBaseAddress;
0105    */
0106 
0107 
0108   *(uint16_t volatile *) (base + UART_IER_OFFSET) = 0;
0109 
0110   if ( 0 != channel->uart_baud) {
0111     divisor = (uint16_t) (uartsConfig->freq /
0112         (channel->uart_baud * 16));
0113   } else {
0114     divisor = (uint16_t) (uartsConfig->freq / (9600 * 16));
0115   }
0116 
0117   *(uint16_t volatile *) (base + UART_LCR_OFFSET) = UART_LCR_DLAB;
0118   *(uint16_t volatile *) (base + UART_DLL_OFFSET) = (divisor & 0xff);
0119   *(uint16_t volatile *) (base + UART_DLH_OFFSET) = ((divisor >> 8) & 0xff);
0120 
0121   *(uint16_t volatile *) (base + UART_LCR_OFFSET) = UART_LCR_WLS_8;
0122 
0123   *(uint16_t volatile *) (base + UART_GCTL_OFFSET) = UART_GCTL_UCEN;
0124 
0125   /**
0126    * To clear previous status
0127    * divisor is a temp variable here
0128    */
0129   divisor = *(uint16_t volatile *) (base + UART_LSR_OFFSET);
0130   divisor = *(uint16_t volatile *) (base + UART_RBR_OFFSET);
0131   divisor = *(uint16_t volatile *) (base + UART_IIR_OFFSET);
0132 
0133   if ( channel->uart_useDma ) {
0134     *(uint16_t  volatile *)(tx_dma_base + DMA_CONFIG_OFFSET) = 0;
0135     *(uint16_t  volatile *)(tx_dma_base + DMA_CONFIG_OFFSET) = DMA_CONFIG_DI_EN
0136         | DMA_CONFIG_SYNC ;
0137     *(uint16_t  volatile *)(tx_dma_base + DMA_IRQ_STATUS_OFFSET) |=
0138         DMA_IRQ_STATUS_DMA_DONE | DMA_IRQ_STATUS_DMA_ERR;
0139 
0140   } else {
0141     /**
0142     * We use polling or interrupts only sending one char at a time :(
0143     */
0144   }
0145 }
0146 
0147 
0148 /**
0149  * Set the UART attributes.
0150  * @param minor
0151  * @param termios
0152  * @return
0153  */
0154 static int setAttributes(int minor, const struct termios *termios)
0155 {
0156   uint32_t base;
0157   int baud;
0158   uint16_t divisor;
0159   uint16_t lcr;
0160 
0161   base = uartsConfig->channels[minor].uart_baseAddress;
0162   switch (termios->c_ospeed) {
0163     case B0:      baud = 0;      break;
0164     case B50:     baud = 50;     break;
0165     case B75:     baud = 75;     break;
0166     case B110:    baud = 110;    break;
0167     case B134:    baud = 134;    break;
0168     case B150:    baud = 150;    break;
0169     case B200:    baud = 200;    break;
0170     case B300:    baud = 300;    break;
0171     case B600:    baud = 600;    break;
0172     case B1200:   baud = 1200;   break;
0173     case B1800:   baud = 1800;   break;
0174     case B2400:   baud = 2400;   break;
0175     case B4800:   baud = 4800;   break;
0176     case B9600:   baud = 9600;   break;
0177     case B19200:  baud = 19200;  break;
0178     case B38400:  baud = 38400;  break;
0179     case B57600:  baud = 57600;  break;
0180     case B115200: baud = 115200; break;
0181     case B230400: baud = 230400; break;
0182     case B460800: baud = 460800; break;
0183     default: baud = -1; break;
0184   }
0185   if (baud > 0 && uartsConfig->channels[minor].uart_baud)
0186     baud = uartsConfig->channels[minor].uart_baud;
0187   switch (termios->c_cflag & CSIZE) {
0188   case CS5: lcr = UART_LCR_WLS_5; break;
0189   case CS6: lcr = UART_LCR_WLS_6; break;
0190   case CS7: lcr = UART_LCR_WLS_7; break;
0191   default:
0192   case CS8: lcr = UART_LCR_WLS_8; break;
0193   }
0194   switch (termios->c_cflag & (PARENB | PARODD)) {
0195     case PARENB:
0196       lcr |= UART_LCR_PEN | UART_LCR_EPS;
0197       break;
0198     case PARENB | PARODD:
0199       lcr |= UART_LCR_PEN;
0200       break;
0201     default:
0202       break;
0203   }
0204   if (termios->c_cflag & CSTOPB)
0205     lcr |= UART_LCR_STB;
0206 
0207   if (baud > 0) {
0208     divisor = (uint16_t) (uartsConfig->freq / (baud * 16));
0209     *(uint16_t volatile *) (base + UART_LCR_OFFSET) = lcr | UART_LCR_DLAB;
0210     *(uint16_t volatile *) (base + UART_DLL_OFFSET) = (divisor & 0xff);
0211     *(uint16_t volatile *) (base + UART_DLH_OFFSET) = ((divisor >> 8) & 0xff);
0212   }
0213   *(uint16_t volatile *) (base + UART_LCR_OFFSET) = lcr;
0214 
0215   return 0;
0216 }
0217 
0218 /**
0219  * Interrupt based uart tx routine. The routine writes one character at a time.
0220  *
0221  * @param minor Minor number to indicate uart number
0222  * @param buf Character buffer which stores characters to be transmitted.
0223  * @param len Length of buffer to be transmitted.
0224  * @return
0225  */
0226 static ssize_t uart_interruptWrite(int minor, const char *buf, size_t len)
0227 {
0228   uint32_t              base      = 0;
0229   bfin_uart_channel_t*  channel   = NULL;
0230 
0231   /**
0232    * Sanity Check
0233    */
0234   if (
0235     NULL == buf || NULL == channel || NULL == uartsConfig
0236       || minor < 0 || 0 == len
0237   ) {
0238     return 0;
0239   }
0240 
0241   channel = &(uartsConfig->channels[minor]);
0242 
0243   if ( NULL == channel || channel->flags &  BFIN_UART_XMIT_BUSY ) {
0244     return 0;
0245   }
0246 
0247   base = channel->uart_baseAddress;
0248 
0249   channel->flags |= BFIN_UART_XMIT_BUSY;
0250   channel->length = 1;
0251   *(uint16_t volatile *) (base + UART_THR_OFFSET) = *buf;
0252   *(uint16_t volatile *) (base + UART_IER_OFFSET) = UART_IER_ETBEI;
0253 
0254   return 0;
0255 }
0256 
0257 /**
0258  * This function implements RX ISR
0259  */
0260 void bfinUart_rxIsr(void *_arg)
0261 {
0262   /**
0263    * TODO: UART RX ISR implementation.
0264    */
0265 }
0266 
0267 /**
0268  * This function implements TX ISR. The function gets called when the TX FIFO is
0269  * empty. It clears the interrupt and dequeues the character. It only tx one
0270  * character at a time.
0271  *
0272  * TODO: error handling.
0273  * @param _arg gets the channel information.
0274  */
0275 void bfinUart_txIsr(void *_arg)
0276 {
0277   bfin_uart_channel_t*  channel = NULL;
0278   uint32_t              base    = 0;
0279 
0280   /**
0281    * Sanity check
0282    */
0283   if (NULL == _arg) {
0284     /** It should never be NULL */
0285     return;
0286   }
0287 
0288   channel = (bfin_uart_channel_t *) _arg;
0289 
0290   base = channel->uart_baseAddress;
0291 
0292   *(uint16_t volatile *) (base + UART_IER_OFFSET) &= ~UART_IER_ETBEI;
0293   channel->flags &= ~BFIN_UART_XMIT_BUSY;
0294 
0295   rtems_termios_dequeue_characters(channel->termios, channel->length);
0296 }
0297 
0298 /**
0299  * interrupt based DMA write Routine. It configure the DMA to write len bytes.
0300  * The DMA supports 64K data only.
0301  *
0302  * @param minor Identification number of the UART.
0303  * @param buf Character buffer pointer
0304  * @param len length of data items to be written
0305  * @return data already written
0306  */
0307 static ssize_t uart_DmaWrite(int minor, const char *buf, size_t len)
0308 {
0309   uint32_t              base        = 0;
0310   bfin_uart_channel_t*  channel     = NULL;
0311   uint32_t              tx_dma_base = 0;
0312 
0313   /**
0314    * Sanity Check
0315    */
0316   if ( NULL == buf || 0 > minor || NULL == uartsConfig || 0 == len ) {
0317     return 0;
0318   }
0319 
0320   channel = &(uartsConfig->channels[minor]);
0321 
0322   /**
0323    * Sanity Check and check for transmit busy.
0324    */
0325   if ( NULL == channel || BFIN_UART_XMIT_BUSY & channel->flags ) {
0326     return 0;
0327   }
0328 
0329   base        = channel->uart_baseAddress;
0330   tx_dma_base = channel->uart_txDmaBaseAddress;
0331 
0332   channel->flags |= BFIN_UART_XMIT_BUSY;
0333   channel->length = len;
0334 
0335   *(uint16_t volatile *) (tx_dma_base + DMA_CONFIG_OFFSET) &= ~DMA_CONFIG_DMAEN;
0336   *(uint32_t volatile *) (tx_dma_base + DMA_START_ADDR_OFFSET) = (uint32_t)buf;
0337   *(uint16_t volatile *) (tx_dma_base + DMA_X_COUNT_OFFSET) = channel->length;
0338   *(uint16_t volatile *) (tx_dma_base + DMA_X_MODIFY_OFFSET) = 1;
0339   *(uint16_t volatile *) (tx_dma_base + DMA_CONFIG_OFFSET) |= DMA_CONFIG_DMAEN;
0340   *(uint16_t volatile *) (base + UART_IER_OFFSET) = UART_IER_ETBEI;
0341 
0342   return 0;
0343 }
0344 
0345 /**
0346  * RX DMA ISR.
0347  * The polling route is used for receiving the characters. This is a place
0348  * holder for future implementation.
0349  * @param _arg
0350  */
0351 void bfinUart_rxDmaIsr(void *_arg)
0352 {
0353 /**
0354  * TODO: Implementation of RX DMA
0355  */
0356 }
0357 
0358 /**
0359  * This function implements TX dma ISR. It clears the IRQ and dequeues a char
0360  * The channel argument will have the base address. Since there are two uart
0361  * and both the uarts can use the same tx dma isr.
0362  *
0363  * TODO: 1. Error checking 2. sending correct length ie after looking at the
0364  * number of elements the uart transmitted.
0365  *
0366  * @param _arg argument passed to the interrupt handler. It contains the
0367  * channel argument.
0368  */
0369 void bfinUart_txDmaIsr(void *_arg)
0370 {
0371   bfin_uart_channel_t*  channel     = NULL;
0372   uint32_t              tx_dma_base = 0;
0373 
0374   /**
0375    * Sanity check
0376    */
0377   if (NULL == _arg) {
0378     /** It should never be NULL */
0379     return;
0380   }
0381 
0382   channel = (bfin_uart_channel_t *) _arg;
0383 
0384   tx_dma_base = channel->uart_txDmaBaseAddress;
0385 
0386   if ((*(uint16_t volatile *) (tx_dma_base + DMA_IRQ_STATUS_OFFSET)
0387       & DMA_IRQ_STATUS_DMA_DONE)) {
0388 
0389     *(uint16_t volatile *) (tx_dma_base + DMA_IRQ_STATUS_OFFSET)
0390                     |= DMA_IRQ_STATUS_DMA_DONE | DMA_IRQ_STATUS_DMA_ERR;
0391     channel->flags &= ~BFIN_UART_XMIT_BUSY;
0392     rtems_termios_dequeue_characters(channel->termios, channel->length);
0393   } else {
0394     /* UART DMA did not generate interrupt.
0395      * This routine must not be called.
0396      */
0397   }
0398 }
0399 
0400 /**
0401  * Function called during exit
0402  */
0403 static void uart_exit(void)
0404 {
0405   /**
0406    * TODO: Flushing of quques
0407    */
0408 }
0409 
0410 /**
0411  * Opens the device in different modes. The supported modes are
0412  * 1. Polling
0413  * 2. Interrupt
0414  * 3. DMA
0415  * At exit the uart_Exit function will be called to flush the device.
0416  *
0417  * @param major Major number of the device
0418  * @param minor Minor number of the device
0419  * @param arg
0420  * @return
0421  */
0422 rtems_device_driver bfin_uart_open(rtems_device_major_number major,
0423     rtems_device_minor_number minor, void *arg) {
0424   rtems_status_code             sc    = RTEMS_NOT_DEFINED;
0425   rtems_libio_open_close_args_t *args = NULL;
0426 
0427   /**
0428    * Callback function for polling
0429    */
0430   static const rtems_termios_callbacks pollCallbacks = {
0431       NULL,                        /* firstOpen */
0432       NULL,                        /* lastClose */
0433       pollRead,                    /* pollRead */
0434       pollWrite,                   /* write */
0435       setAttributes,               /* setAttributes */
0436       NULL,                        /* stopRemoteTx */
0437       NULL,                        /* startRemoteTx */
0438       TERMIOS_POLLED               /* outputUsesInterrupts */
0439   };
0440 
0441   /**
0442    * Callback function for interrupt based transfers without DMA.
0443    * We use interrupts for writing only. For reading we use polling.
0444    */
0445   static const rtems_termios_callbacks interruptCallbacks = {
0446       NULL,                        /* firstOpen */
0447       NULL,                        /* lastClose */
0448       pollRead,                    /* pollRead */
0449       uart_interruptWrite,              /* write */
0450       setAttributes,               /* setAttributes */
0451       NULL,                        /* stopRemoteTx */
0452       NULL,                        /* startRemoteTx */
0453       TERMIOS_IRQ_DRIVEN           /* outputUsesInterrupts */
0454   };
0455 
0456   /**
0457    * Callback function for interrupt based DMA transfers.
0458    * We use interrupts for writing only. For reading we use polling.
0459    */
0460   static const rtems_termios_callbacks interruptDmaCallbacks = {
0461       NULL,                        /* firstOpen */
0462       NULL,                        /* lastClose */
0463       NULL,                        /* pollRead */
0464       uart_DmaWrite,              /* write */
0465       setAttributes,               /* setAttributes */
0466       NULL,                        /* stopRemoteTx */
0467       NULL,                        /* startRemoteTx */
0468       TERMIOS_IRQ_DRIVEN           /* outputUsesInterrupts */
0469   };
0470 
0471   if ( NULL == uartsConfig || 0 > minor || minor >= uartsConfig->num_channels) {
0472     return RTEMS_INVALID_NUMBER;
0473   }
0474 
0475   /**
0476    * Opens device for handling uart send request either by
0477    * 1. interrupt with DMA
0478    * 2. interrupt based
0479    * 3. Polling
0480    */
0481   if ( uartsConfig->channels[minor].uart_useDma ) {
0482     sc = rtems_termios_open(major, minor, arg, &interruptDmaCallbacks);
0483   } else {
0484     sc = rtems_termios_open(major, minor, arg,
0485         uartsConfig->channels[minor].uart_useInterrupts ?
0486             &interruptCallbacks : &pollCallbacks);
0487   }
0488 
0489   args = arg;
0490   uartsConfig->channels[minor].termios = args->iop->data1;
0491 
0492   atexit(uart_exit);
0493 
0494   return sc;
0495 }
0496 
0497 /**
0498 * Uart initialization function.
0499 * @param major major number of the device
0500 * @param config configuration parameters
0501 * @return rtems status code
0502 */
0503 rtems_status_code bfin_uart_initialize(
0504   rtems_device_major_number major,
0505   bfin_uart_config_t *config
0506 )
0507 {
0508   rtems_status_code sc = RTEMS_NOT_DEFINED;
0509   int               i  = 0;
0510 
0511   rtems_termios_initialize();
0512 
0513   /*
0514    *  Register Device Names
0515    */
0516   uartsConfig = config;
0517   for (i = 0; i < config->num_channels; i++) {
0518     config->channels[i].termios = NULL;
0519     config->channels[i].flags = 0;
0520     initializeHardware(&(config->channels[i]));
0521     sc = rtems_io_register_name(config->channels[i].name, major, i);
0522     if (RTEMS_SUCCESSFUL != sc) {
0523       return sc;
0524     }
0525   }
0526 
0527   return sc;
0528 }