Logo Search packages:      
Sourcecode: linux-fsl-imx51 version File versions  Download package

mxc_uart.c

Go to the documentation of this file.
/*
 * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
 */

/*
 * The code contained herein is licensed under the GNU General Public
 * License. You may obtain a copy of the GNU General Public License
 * Version 2 or later at the following locations:
 *
 * http://www.opensource.org/licenses/gpl-license.html
 * http://www.gnu.org/copyleft/gpl.html
 */

/*!
 * @file drivers/serial/mxc_uart.c
 *
 * @brief Driver for the Freescale Semiconductor MXC serial ports based on
 * drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
 *
 * @ingroup UART
 */

/*
 * Include Files
 */
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/serial.h>
#include <linux/console.h>
#include <linux/platform_device.h>
#include <linux/sysrq.h>
#include <linux/dma-mapping.h>
#include <linux/clk.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/dma.h>
#include <asm/div64.h>
#include <mach/hardware.h>
#include <mach/mxc_uart.h>

#if defined(CONFIG_SERIAL_MXC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
#define SERIAL_MXC_MAJOR        207
#define SERIAL_MXC_MINOR        16
#define MXC_ISR_PASS_LIMIT      256
#define UART_CREAD_BIT          256

#define MXC_UART_NR           8

/* IRDA minimum pulse duration in micro seconds */
#define MIN_PULSE_DUR           2
/*
 * Transmit DMA buffer size is set to 1024 bytes, this is limited
 * by UART_XMIT_SIZE.
 */
#define TXDMA_BUFF_SIZE         UART_XMIT_SIZE
/*
 * Receive DMA sub-buffer size
 */
#define RXDMA_BUFF_SIZE         128

/*!
 * This structure is used to store the information for DMA data transfer.
 */
00070 typedef struct {
      /*!
       * Holds the read channel number.
       */
00074       int rd_channel;
      /*!
       * Holds the write channel number.
       */
00078       int wr_channel;
      /*!
       * UART Transmit Event ID
       */
00082       int tx_event_id;
      /*!
       * UART Receive Event ID
       */
00086       int rx_event_id;
      /*!
       * DMA Transmit tasklet
       */
00090       struct tasklet_struct dma_tx_tasklet;
      /*!
       * Flag indicates if the channel is in use
       */
00094       int dma_txchnl_inuse;
} dma_info;

/*!
 * This is used to indicate if we want echo cancellation in the Irda mode.
 */
00100 static int echo_cancel;
extern void gpio_uart_active(int port, int no_irda);
extern void gpio_uart_inactive(int port, int no_irda);
extern void config_uartdma_event(int port);

static uart_mxc_port *mxc_ports[MXC_UART_NR];

/*!
 * This array holds the DMA channel information for each MXC UART
 */
00110 static dma_info dma_list[MXC_UART_NR];

/*!
 * This function is called by the core driver to stop UART transmission.
 * This might be due to the TTY layer indicating that the user wants to stop
 * transmission.
 *
 * @param   port       the port structure for the UART passed in by the core
 *                     driver
 */
00120 static void mxcuart_stop_tx(struct uart_port *port)
{
      uart_mxc_port *umxc = (uart_mxc_port *) port;
      volatile unsigned int cr1;

      cr1 = readl(port->membase + MXC_UARTUCR1);
      /* Disable Transmitter rdy interrupt */
      if (umxc->dma_enabled == 1) {
            cr1 &= ~MXC_UARTUCR1_TXDMAEN;
      } else {
            cr1 &= ~MXC_UARTUCR1_TRDYEN;
      }
      writel(cr1, port->membase + MXC_UARTUCR1);
}

/*!
 * DMA Transmit tasklet method is scheduled on completion of a DMA transmit
 * to send out any more data that is available in the UART xmit buffer.
 *
 * @param   arg  driver private data
 */
00141 static void dma_tx_do_tasklet(unsigned long arg)
{
      uart_mxc_port *umxc = (uart_mxc_port *) arg;
      struct circ_buf *xmit = &umxc->port.info->xmit;
      mxc_dma_requestbuf_t writechnl_request;
      int tx_num;
      unsigned long flags;

      spin_lock_irqsave(&umxc->port.lock, flags);
      tx_num = uart_circ_chars_pending(xmit);
      if (tx_num > 0) {
            if (xmit->tail > xmit->head) {
                  memcpy(umxc->tx_buf, xmit->buf + xmit->tail,
                         UART_XMIT_SIZE - xmit->tail);
                  memcpy(umxc->tx_buf + (UART_XMIT_SIZE - xmit->tail),
                         xmit->buf, xmit->head);
            } else {
                  memcpy(umxc->tx_buf, xmit->buf + xmit->tail, tx_num);
            }
            umxc->tx_handle = dma_map_single(umxc->port.dev, umxc->tx_buf,
                                     TXDMA_BUFF_SIZE,
                                     DMA_TO_DEVICE);

            writechnl_request.dst_addr = umxc->port.mapbase + MXC_UARTUTXD;
            writechnl_request.src_addr = umxc->tx_handle;
            writechnl_request.num_of_bytes = tx_num;

            if ((mxc_dma_config(dma_list[umxc->port.line].wr_channel,
                            &writechnl_request, 1,
                            MXC_DMA_MODE_WRITE)) == 0) {
                  mxc_dma_enable(dma_list[umxc->port.line].wr_channel);
            }
      } else {
            /* No more data available in the xmit queue, clear the flag */
            dma_list[umxc->port.line].dma_txchnl_inuse = 0;
      }
      spin_unlock_irqrestore(&umxc->port.lock, flags);
}

/*!
 * DMA Write callback is called by the SDMA controller after it has sent out all
 * the data from the user buffer. This function updates the xmit buffer pointers.
 *
 * @param   arg   driver private data
 * @param   error any DMA error
 * @param   count amount of data that was transferred
 */
00188 static void mxcuart_dma_writecallback(void *arg, int error, unsigned int count)
{
      uart_mxc_port *umxc = arg;
      struct circ_buf *xmit = &umxc->port.info->xmit;
      int tx_num;

      if (error != MXC_DMA_TRANSFER_ERROR) {
            tx_num = count;
            umxc->port.icount.tx += tx_num;
            xmit->tail = (xmit->tail + tx_num) & (UART_XMIT_SIZE - 1);
      }

      dma_unmap_single(umxc->port.dev, umxc->tx_handle, TXDMA_BUFF_SIZE,
                   DMA_TO_DEVICE);
      tx_num = uart_circ_chars_pending(xmit);
      /* Schedule a tasklet to send out the pending characters */
      if (tx_num > 0) {
            tasklet_schedule(&dma_list[umxc->port.line].dma_tx_tasklet);
      } else {
            dma_list[umxc->port.line].dma_txchnl_inuse = 0;
      }
      if (tx_num < WAKEUP_CHARS) {
            uart_write_wakeup(&umxc->port);
      }
}

/*!
 * This function is called by the core driver to start transmitting characters.
 * This function enables the transmit interrupts.
 *
 * @param   port       the port structure for the UART passed in by the core
 *                     driver
 */
00221 static void mxcuart_start_tx(struct uart_port *port)
{
      uart_mxc_port *umxc = (uart_mxc_port *) port;
      struct circ_buf *xmit = &umxc->port.info->xmit;
      volatile unsigned int cr1;
      mxc_dma_requestbuf_t writechnl_request;
      int tx_num;

      cr1 = readl(port->membase + MXC_UARTUCR1);
      /* Enable Transmitter rdy interrupt */
      if (umxc->dma_enabled == 1) {
            /*
             * If the channel is in use then return immediately and use
             * the dma_tx tasklet to transfer queued data when current DMA
             * transfer is complete
             */
            if (dma_list[umxc->port.line].dma_txchnl_inuse == 1) {
                  return;
            }
            tx_num = uart_circ_chars_pending(xmit);
            if (tx_num > 0) {
                  dma_list[umxc->port.line].dma_txchnl_inuse = 1;
                  if (xmit->tail > xmit->head) {
                        memcpy(umxc->tx_buf, xmit->buf + xmit->tail,
                               UART_XMIT_SIZE - xmit->tail);
                        memcpy(umxc->tx_buf +
                               (UART_XMIT_SIZE - xmit->tail), xmit->buf,
                               xmit->head);
                  } else {
                        memcpy(umxc->tx_buf, xmit->buf + xmit->tail,
                               tx_num);
                  }
                  umxc->tx_handle =
                      dma_map_single(umxc->port.dev, umxc->tx_buf,
                                 TXDMA_BUFF_SIZE, DMA_TO_DEVICE);

                  writechnl_request.dst_addr =
                      umxc->port.mapbase + MXC_UARTUTXD;
                  writechnl_request.src_addr = umxc->tx_handle;
                  writechnl_request.num_of_bytes = tx_num;
                  if ((mxc_dma_config
                       (dma_list[umxc->port.line].wr_channel,
                        &writechnl_request, 1,
                        MXC_DMA_MODE_WRITE)) == 0) {
                        mxc_dma_enable(dma_list[umxc->port.line].
                                     wr_channel);
                  }
                  cr1 |= MXC_UARTUCR1_TXDMAEN;
            }
      } else {
            cr1 |= MXC_UARTUCR1_TRDYEN;
      }
      writel(cr1, port->membase + MXC_UARTUCR1);
}

/*!
 * This function is called by the core driver to stop receiving characters; the
 * port is in the process of being closed.
 *
 * @param   port   the port structure for the UART passed in by the core driver
 */
00282 static void mxcuart_stop_rx(struct uart_port *port)
{
      uart_mxc_port *umxc = (uart_mxc_port *) port;
      volatile unsigned int cr1;

      cr1 = readl(port->membase + MXC_UARTUCR1);
      if (umxc->dma_enabled == 1) {
            cr1 &= ~MXC_UARTUCR1_RXDMAEN;
      } else {
            cr1 &= ~MXC_UARTUCR1_RRDYEN;
      }
      writel(cr1, port->membase + MXC_UARTUCR1);
}

/*!
 * This function is called by the core driver to enable the modem status
 * interrupts. If the port is configured to be in DTE mode then it enables the
 * DCDDELT and RIDELT interrupts in addition to the DTRDEN interrupt. The RTSDEN
 * interrupt is enabled only for interrupt-driven hardware flow control.
 *
 * @param   port   the port structure for the UART passed in by the core driver
 */
00304 static void mxcuart_enable_ms(struct uart_port *port)
{
      uart_mxc_port *umxc = (uart_mxc_port *) port;
      volatile unsigned int cr1, cr3;

      /*
       * RTS interrupt is enabled only if we are using interrupt-driven
       * software controlled hardware flow control
       */
      if (umxc->hardware_flow == 0) {
            cr1 = readl(umxc->port.membase + MXC_UARTUCR1);
            cr1 |= MXC_UARTUCR1_RTSDEN;
            writel(cr1, umxc->port.membase + MXC_UARTUCR1);
      }
      cr3 = readl(umxc->port.membase + MXC_UARTUCR3);
      cr3 |= MXC_UARTUCR3_DTRDEN;
      if (umxc->mode == MODE_DTE) {
            cr3 |= MXC_UARTUCR3_DCD | MXC_UARTUCR3_RI;
      }
      writel(cr3, umxc->port.membase + MXC_UARTUCR3);
}

/*!
 * This function is called from the interrupt service routine if the status bit
 * indicates that the receive fifo data level is above the set threshold. The
 * function reads the character and queues them into the TTY layers read
 * buffer. The function also looks for break characters, parity and framing
 * errors in the received character and sets the appropriate flag in the TTY
 * receive buffer.
 *
 * @param   umxc   the MXC UART port structure, this includes the \b uart_port
 *                 structure and other members that are specific to MXC UARTs
 */
00337 static void mxcuart_rx_chars(uart_mxc_port * umxc)
{
      volatile unsigned int ch, sr2;
      unsigned int status, flag, max_count = 256;

      sr2 = readl(umxc->port.membase + MXC_UARTUSR2);
      while (((sr2 & MXC_UARTUSR2_RDR) == 1) && (max_count-- > 0)) {
            ch = readl(umxc->port.membase + MXC_UARTURXD);

            flag = TTY_NORMAL;
            status = ch | UART_CREAD_BIT;
            ch &= 0xFF; /* Clear the upper bits */
            umxc->port.icount.rx++;

            /*
             * Check to see if there is an  error in the received
             * character. Perform the appropriate actions based on the
             * error bit that was set.
             */
            if (status & MXC_UARTURXD_ERR) {
                  if (status & MXC_UARTURXD_BRK) {
                        /*
                         * Clear the frame and parity error bits
                         * as these always get set on receiving a
                         * break character
                         */
                        status &= ~(MXC_UARTURXD_FRMERR |
                                  MXC_UARTURXD_PRERR);
                        umxc->port.icount.brk++;
                        if (uart_handle_break(&umxc->port)) {
                              goto ignore_char;
                        }
                  } else if (status & MXC_UARTURXD_FRMERR) {
                        umxc->port.icount.frame++;
                  } else if (status & MXC_UARTURXD_PRERR) {
                        umxc->port.icount.parity++;
                  }
                  if (status & MXC_UARTURXD_OVRRUN) {
                        umxc->port.icount.overrun++;
                  }

                  status &= umxc->port.read_status_mask;

                  if (status & MXC_UARTURXD_BRK) {
                        flag = TTY_BREAK;
                  } else if (status & MXC_UARTURXD_FRMERR) {
                        flag = TTY_FRAME;
                  } else if (status & MXC_UARTURXD_PRERR) {
                        flag = TTY_PARITY;
                  }
            }

            if (uart_handle_sysrq_char(&umxc->port, ch)) {
                  goto ignore_char;
            }

            uart_insert_char(&umxc->port, status, MXC_UARTURXD_OVRRUN, ch,
                         flag);
            ignore_char:
            sr2 = readl(umxc->port.membase + MXC_UARTUSR2);
      }
      tty_flip_buffer_push(umxc->port.info->port.tty);
}

/*!
 * This function is called from the interrupt service routine if the status bit
 * indicates that the transmit fifo is emptied below its set threshold and
 * requires data. The function pulls characters from the TTY layers write
 * buffer and writes it out to the UART transmit fifo.
 *
 * @param   umxc   the MXC UART port structure, this includes the \b uart_port
 *                 structure and other members that are specific to MXC UARTs
 */
00410 static void mxcuart_tx_chars(uart_mxc_port * umxc)
{
      struct circ_buf *xmit = &umxc->port.info->xmit;
      int count;

      /*
       * Transmit the XON/XOFF character if required
       */
      if (umxc->port.x_char) {
            writel(umxc->port.x_char, umxc->port.membase + MXC_UARTUTXD);
            umxc->port.icount.tx++;
            umxc->port.x_char = 0;
            return;
      }

      /*
       * Check to see if there is any data to be sent and that the
       * port has not been currently stopped by anything.
       */
      if (uart_circ_empty(xmit) || uart_tx_stopped(&umxc->port)) {
            mxcuart_stop_tx(&umxc->port);
            return;
      }

      count = umxc->port.fifosize - umxc->tx_threshold;
      do {
            writel(xmit->buf[xmit->tail],
                   umxc->port.membase + MXC_UARTUTXD);
            xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
            umxc->port.icount.tx++;
            if (uart_circ_empty(xmit)) {
                  break;
            }
      } while (--count > 0);

      /*
       * Check to see if we have flushed enough characters to ask for more
       * to be sent to us, if so, we notify the user space that we can
       * accept more data
       */
      if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) {
            uart_write_wakeup(&umxc->port);
      }

      if (uart_circ_empty(xmit)) {
            mxcuart_stop_tx(&umxc->port);
      }
}

/*!
 * This function is called from the interrupt service routine if there is a
 * change in the modem signals. This function handles these signal changes and
 * also clears the appropriate status register bits.
 *
 * @param   umxc   the MXC UART port structure, this includes the \b uart_port
 *                 structure and other members that are specific to MXC UARTs
 * @param   sr1    contents of status register 1
 * @param   sr2    contents of status register 2
 */
00469 static void mxcuart_modem_status(uart_mxc_port * umxc, unsigned int sr1,
                         unsigned int sr2)
{
      if (umxc->mode == MODE_DTE) {
            if (sr2 & MXC_UARTUSR2_DCDDELT) {
                  uart_handle_dcd_change(&umxc->port,
                                     !(sr2 & MXC_UARTUSR2_DCDIN));
            }
            if (sr2 & MXC_UARTUSR2_RIDELT) {
                  umxc->port.icount.rng++;
            }
      }
      if (sr1 & MXC_UARTUSR1_DTRD) {
            umxc->port.icount.dsr++;
      }
      if ((umxc->hardware_flow == 0) && (sr1 & MXC_UARTUSR1_RTSD)) {
            uart_handle_cts_change(&umxc->port, sr1 & MXC_UARTUSR1_RTSS);
      }

      wake_up_interruptible(&umxc->port.info->delta_msr_wait);
}

/*!
 * Interrupt service routine registered to handle the muxed ANDed interrupts.
 * This routine is registered only in the case where the UART interrupts are
 * muxed.
 *
 * @param   irq    the interrupt number
 * @param   dev_id driver private data
 *
 * @return  The function returns \b IRQ_RETVAL(1) if interrupt was handled,
 *          returns \b IRQ_RETVAL(0) if the interrupt was not handled.
 *          \b IRQ_RETVAL is defined in \b include/linux/interrupt.h.
 */
00503 static irqreturn_t mxcuart_int(int irq, void *dev_id)
{
      uart_mxc_port *umxc = dev_id;
      volatile unsigned int sr1, sr2, cr1, cr;
      unsigned int pass_counter = MXC_ISR_PASS_LIMIT;
      unsigned int term_cond = 0;
      int handled = 0;

      sr1 = readl(umxc->port.membase + MXC_UARTUSR1);
      sr2 = readl(umxc->port.membase + MXC_UARTUSR2);
      cr1 = readl(umxc->port.membase + MXC_UARTUCR1);

      do {
            /* Clear the bits that triggered the interrupt */
            writel(sr1, umxc->port.membase + MXC_UARTUSR1);
            writel(sr2, umxc->port.membase + MXC_UARTUSR2);
            /*
             * Read if there is data available
             */
            if (sr2 & MXC_UARTUSR2_RDR) {
                  mxcuart_rx_chars(umxc);
            }

            if ((sr1 & (MXC_UARTUSR1_RTSD | MXC_UARTUSR1_DTRD)) ||
                (sr2 & (MXC_UARTUSR2_DCDDELT | MXC_UARTUSR2_RIDELT))) {
                  mxcuart_modem_status(umxc, sr1, sr2);
            }

            /*
             * Send data if there is data to be sent
             */
            if ((cr1 & MXC_UARTUCR1_TRDYEN) && (sr1 & MXC_UARTUSR1_TRDY)) {
                  /* Echo cancellation for IRDA Transmit chars */
                  if (umxc->ir_mode == IRDA && echo_cancel) {
                        /* Disable the receiver */
                        cr = readl(umxc->port.membase + MXC_UARTUCR2);
                        cr &= ~MXC_UARTUCR2_RXEN;
                        writel(cr, umxc->port.membase + MXC_UARTUCR2);
                        /* Enable Transmit complete intr to reenable RX */
                        cr = readl(umxc->port.membase + MXC_UARTUCR4);
                        cr |= MXC_UARTUCR4_TCEN;
                        writel(cr, umxc->port.membase + MXC_UARTUCR4);
                  }
                  mxcuart_tx_chars(umxc);
            }

            if (pass_counter-- == 0) {
                  break;
            }

            sr1 = readl(umxc->port.membase + MXC_UARTUSR1);
            sr2 = readl(umxc->port.membase + MXC_UARTUSR2);

            /* Is the transmit complete to reenable the receiver? */
            if (umxc->ir_mode == IRDA && echo_cancel) {
                  if (sr2 & MXC_UARTUSR2_TXDC) {
                        cr = readl(umxc->port.membase + MXC_UARTUCR2);
                        cr |= MXC_UARTUCR2_RXEN;
                        writel(cr, umxc->port.membase + MXC_UARTUCR2);
                        /* Disable the Transmit complete interrupt bit */
                        cr = readl(umxc->port.membase + MXC_UARTUCR4);
                        cr &= ~MXC_UARTUCR4_TCEN;
                        writel(cr, umxc->port.membase + MXC_UARTUCR4);
                  }
            }

            /*
             * If there is no data to send or receive and if there is no
             * change in the modem status signals then quit the routine
             */
            term_cond = sr1 & (MXC_UARTUSR1_RTSD | MXC_UARTUSR1_DTRD);
            term_cond |= sr2 & (MXC_UARTUSR2_RDR | MXC_UARTUSR2_DCDDELT);
            term_cond |= !(sr2 & MXC_UARTUSR2_TXFE);
      } while (term_cond > 0);

      handled = 1;
      return IRQ_RETVAL(handled);
}

/*!
 * Interrupt service routine registered to handle the transmit interrupts. This
 * routine is registered only in the case where the UART interrupts are not
 * muxed.
 *
 * @param   irq    the interrupt number
 * @param   dev_id driver private data
 *
 * @return  The function returns \b IRQ_RETVAL(1) if interrupt was handled,
 *          returns \b IRQ_RETVAL(0) if the interrupt was not handled.
 *          \b IRQ_RETVAL is defined in include/linux/interrupt.h.
 */
00594 static irqreturn_t mxcuart_tx_int(int irq, void *dev_id)
{
      uart_mxc_port *umxc = dev_id;
      int handled = 0;
      volatile unsigned int sr2, cr;

      /* Echo cancellation for IRDA Transmit chars */
      if (umxc->ir_mode == IRDA && echo_cancel) {
            /* Disable the receiver */
            cr = readl(umxc->port.membase + MXC_UARTUCR2);
            cr &= ~MXC_UARTUCR2_RXEN;
            writel(cr, umxc->port.membase + MXC_UARTUCR2);
            /* Enable Transmit complete to reenable receiver */
            cr = readl(umxc->port.membase + MXC_UARTUCR4);
            cr |= MXC_UARTUCR4_TCEN;
            writel(cr, umxc->port.membase + MXC_UARTUCR4);
      }

      mxcuart_tx_chars(umxc);

      /* Is the transmit complete to reenable the receiver? */
      if (umxc->ir_mode == IRDA && echo_cancel) {
            sr2 = readl(umxc->port.membase + MXC_UARTUSR2);
            if (sr2 & MXC_UARTUSR2_TXDC) {
                  cr = readl(umxc->port.membase + MXC_UARTUCR2);
                  cr |= MXC_UARTUCR2_RXEN;
                  writel(cr, umxc->port.membase + MXC_UARTUCR2);
                  /* Disable the Transmit complete interrupt bit */
                  cr = readl(umxc->port.membase + MXC_UARTUCR4);
                  cr &= ~MXC_UARTUCR4_TCEN;
                  writel(cr, umxc->port.membase + MXC_UARTUCR4);
            }
      }

      handled = 1;

      return IRQ_RETVAL(handled);
}

/*!
 * Interrupt service routine registered to handle the receive interrupts. This
 * routine is registered only in the case where the UART interrupts are not
 * muxed.
 *
 * @param   irq    the interrupt number
 * @param   dev_id driver private data
 *
 * @return  The function returns \b IRQ_RETVAL(1) if interrupt was handled,
 *          returns \b IRQ_RETVAL(0) if the interrupt was not handled.
 *          \b IRQ_RETVAL is defined in include/linux/interrupt.h.
 */
00645 static irqreturn_t mxcuart_rx_int(int irq, void *dev_id)
{
      uart_mxc_port *umxc = dev_id;
      int handled = 0;

      /* Clear the aging timer bit */
      writel(MXC_UARTUSR1_AGTIM, umxc->port.membase + MXC_UARTUSR1);
      mxcuart_rx_chars(umxc);
      handled = 1;

      return IRQ_RETVAL(handled);
}

/*!
 * Interrupt service routine registered to handle the master interrupts. This
 * routine is registered only in the case where the UART interrupts are not
 * muxed.
 *
 * @param   irq    the interrupt number
 * @param   dev_id driver private data
 *
 * @return  The function returns \b IRQ_RETVAL(1) if interrupt was handled,
 *          returns \b IRQ_RETVAL(0) if the interrupt was not handled.
 *          \b IRQ_RETVAL is defined in include/linux/interrupt.h.
 */
00670 static irqreturn_t mxcuart_mint_int(int irq, void *dev_id)
{
      uart_mxc_port *umxc = dev_id;
      int handled = 0;
      volatile unsigned int sr1, sr2;

      sr1 = readl(umxc->port.membase + MXC_UARTUSR1);
      sr2 = readl(umxc->port.membase + MXC_UARTUSR2);
      /* Clear the modem status interrupt bits */
      writel(MXC_UARTUSR1_RTSD | MXC_UARTUSR1_DTRD,
             umxc->port.membase + MXC_UARTUSR1);
      writel(MXC_UARTUSR2_DCDDELT | MXC_UARTUSR2_RIDELT,
             umxc->port.membase + MXC_UARTUSR2);
      mxcuart_modem_status(umxc, sr1, sr2);
      handled = 1;

      return IRQ_RETVAL(handled);
}

/*!
 * This function is called by the core driver to test whether the transmitter
 * fifo and shift register for the UART port are empty.
 *
 * @param   port   the port structure for the UART passed in by the core driver
 *
 * @return  The function returns TIOCSER_TEMT if it is empty, else returns 0.
 */
00697 static unsigned int mxcuart_tx_empty(struct uart_port *port)
{
      volatile unsigned int sr2;
      unsigned long flags;

      spin_lock_irqsave(&port->lock, flags);
      sr2 = readl(port->membase + MXC_UARTUSR2);
      spin_unlock_irqrestore(&port->lock, flags);

      return sr2 & MXC_UARTUSR2_TXDC ? TIOCSER_TEMT : 0;
}

/*!
 * This function is called by the core driver to get the current status of the
 * modem input signals. The state of the output signals is not collected.
 *
 * @param   port   the port structure for the UART passed in by the core driver
 *
 * @return  The function returns an integer that contains the ORed value of the
 *          status of all the modem input signals or error.
 */
00718 static unsigned int mxcuart_get_mctrl(struct uart_port *port)
{
      uart_mxc_port *umxc = (uart_mxc_port *) port;
      unsigned int result = 0;
      volatile unsigned int sr1, sr2;

      sr1 = readl(umxc->port.membase + MXC_UARTUSR1);
      sr2 = readl(umxc->port.membase + MXC_UARTUSR2);

      if (sr1 & MXC_UARTUSR1_RTSS) {
            result |= TIOCM_CTS;
      }
      if (umxc->mode == MODE_DTE) {
            if (!(sr2 & MXC_UARTUSR2_DCDIN)) {
                  result |= TIOCM_CAR;
            }
            if (!(sr2 & MXC_UARTUSR2_RIIN)) {
                  result |= TIOCM_RI;
            }
      }
      return result;
}

/*!
 * This function is called by the core driver to set the state of the modem
 * control lines.
 *
 * @param   port   the port structure for the UART passed in by the core driver
 * @param   mctrl  the state that the modem control lines should be changed to
 */
00748 static void mxcuart_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
      uart_mxc_port *umxc = (uart_mxc_port *) port;
      volatile unsigned int cr2 = 0, cr3 = 0, uts = 0;

      cr2 = readl(port->membase + MXC_UARTUCR2);
      cr3 = readl(port->membase + MXC_UARTUCR3);
      uts = readl(port->membase + MXC_UARTUTS);

      if (mctrl & TIOCM_RTS) {
            /*
             * Return to hardware-driven hardware flow control if the
             * option is enabled
             */
            if (umxc->hardware_flow == 1) {
                  cr2 |= MXC_UARTUCR2_CTSC;
            } else {
                  cr2 |= MXC_UARTUCR2_CTS;
                  cr2 &= ~MXC_UARTUCR2_CTSC;
            }
      } else {
            cr2 &= ~(MXC_UARTUCR2_CTS | MXC_UARTUCR2_CTSC);
      }
      writel(cr2, port->membase + MXC_UARTUCR2);

      if (mctrl & TIOCM_DTR) {
            cr3 |= MXC_UARTUCR3_DSR;
      } else {
            cr3 &= ~MXC_UARTUCR3_DSR;
      }
      writel(cr3, port->membase + MXC_UARTUCR3);

      if (mctrl & TIOCM_LOOP) {
            if (umxc->ir_mode == IRDA) {
                  echo_cancel = 0;
            } else {
                  uts |= MXC_UARTUTS_LOOP;
            }
      } else {
            if (umxc->ir_mode == IRDA) {
                  echo_cancel = 1;
            } else {
                  uts &= ~MXC_UARTUTS_LOOP;
            }
      }
      writel(uts, port->membase + MXC_UARTUTS);
}

/*!
 * This function is called by the core driver to control the transmission of
 * the break signal. If break_state is non-zero, the break signal is
 * transmitted, the signal is terminated when another call is made with
 * break_state set to 0.
 *
 * @param   port          the port structure for the UART passed in by the core
 *                        driver
 * @param   break_state   the requested state of the break signal
 */
00806 static void mxcuart_break_ctl(struct uart_port *port, int break_state)
{
      volatile unsigned int cr1;
      unsigned long flags;

      spin_lock_irqsave(&port->lock, flags);
      cr1 = readl(port->membase + MXC_UARTUCR1);
      if (break_state == -1) {
            cr1 |= MXC_UARTUCR1_SNDBRK;
      } else {
            cr1 &= ~MXC_UARTUCR1_SNDBRK;
      }
      writel(cr1, port->membase + MXC_UARTUCR1);
      spin_unlock_irqrestore(&port->lock, flags);
}

/*!
 * The read DMA callback, this method is called when the DMA buffer has received its
 * data. This functions copies the data to the tty buffer and updates the tty buffer
 * pointers. It also queues the DMA buffer back to the DMA system.
 *
 * @param   arg   driver private data
 * @param   error any DMA error
 * @param   cnt   amount of data that was transferred
 */
00831 static void mxcuart_dmaread_callback(void *arg, int error, unsigned int cnt)
{
      uart_mxc_port *umxc = arg;
      struct tty_struct *tty = umxc->port.info->port.tty;
      int buff_id, flip_cnt, num_bufs;
      mxc_dma_requestbuf_t readchnl_request;
      mxc_uart_rxdmamap *rx_buf_elem = NULL;
      unsigned int sr1, sr2;
      char flag;

      num_bufs = umxc->dma_rxbuf_size / RXDMA_BUFF_SIZE;
      /* Clear the aging timer bit */
      writel(MXC_UARTUSR1_AGTIM, umxc->port.membase + MXC_UARTUSR1);

      buff_id = umxc->dma_rxbuf_id;
      flag = TTY_NORMAL;

      if ((umxc->dma_rxbuf_id += 1) >= num_bufs) {
            umxc->dma_rxbuf_id = 0;
      }

      rx_buf_elem = (mxc_uart_rxdmamap *) (umxc->rx_dmamap + buff_id);

      if (error == MXC_DMA_TRANSFER_ERROR) {

            sr1 = __raw_readl(umxc->port.membase + MXC_UARTUSR1);
            sr2 = __raw_readl(umxc->port.membase + MXC_UARTUSR2);

            if (sr2 & MXC_UARTUSR2_BRCD) {
                  umxc->port.icount.brk++;
                  if (uart_handle_break(&umxc->port)) {
                        goto drop_data;
                  }
            } else if (sr1 & MXC_UARTUSR1_PARITYERR) {
                  umxc->port.icount.parity++;
            } else if (sr1 & MXC_UARTUSR1_FRAMERR) {
                  umxc->port.icount.frame++;
            } else if (sr2 & MXC_UARTUSR2_ORE) {
                  umxc->port.icount.overrun++;

            }

            if (umxc->port.read_status_mask & MXC_UARTURXD_BRK) {
                  if (sr2 & MXC_UARTUSR2_BRCD)
                        flag = TTY_BREAK;
            } else if (umxc->port.read_status_mask & MXC_UARTURXD_PRERR) {
                  if (sr1 & MXC_UARTUSR1_PARITYERR)
                        flag = TTY_PARITY;
            } else if (umxc->port.read_status_mask & MXC_UARTURXD_FRMERR) {
                  if (sr1 & MXC_UARTUSR1_FRAMERR)
                        flag = TTY_FRAME;
            } else if (umxc->port.read_status_mask & MXC_UARTURXD_OVRRUN) {
                  if (sr2 & MXC_UARTUSR2_ORE)
                        flag = TTY_OVERRUN;
            }
/* By default clearing all error bits in status reg */
            __raw_writel((MXC_UARTUSR2_BRCD | MXC_UARTUSR2_ORE),
                       umxc->port.membase + MXC_UARTUSR2);
            __raw_writel((MXC_UARTUSR1_PARITYERR | MXC_UARTUSR1_FRAMERR),
                       umxc->port.membase + MXC_UARTUSR1);
      }

      flip_cnt = tty_buffer_request_room(tty, cnt);

      /* Check for space availability in the TTY Flip buffer */
      if (flip_cnt <= 0) {
            goto drop_data;
      }
      umxc->port.icount.rx += flip_cnt;

      tty_insert_flip_string(tty, rx_buf_elem->rx_buf, flip_cnt);

      if (flag != TTY_NORMAL) {
            tty_insert_flip_char(tty, 0, flag);
      }

      tty_flip_buffer_push(tty);
      umxc->port.info->port.tty->real_raw = 1;

      drop_data:
      readchnl_request.src_addr = umxc->port.mapbase;
      readchnl_request.dst_addr = rx_buf_elem->rx_handle;
      readchnl_request.num_of_bytes = RXDMA_BUFF_SIZE;
      mxc_dma_config(dma_list[umxc->port.line].rd_channel, &readchnl_request,
                   1, MXC_DMA_MODE_READ);
      mxc_dma_enable(dma_list[umxc->port.line].rd_channel);
}

/*!
 * Allocates DMA read and write channels, creates DMA read and write buffers and
 * sets the channel specific parameters.
 *
 * @param   d_info the structure that holds all the DMA information for a
 *                 particular MXC UART
 * @param   umxc   the MXC UART port structure, this includes the \b uart_port
 *                 structure and other members that are specific to MXC UARTs
 *
 * @return  The function returns 0 on success and a non-zero value on failure.
 */
00930 static int mxcuart_initdma(dma_info * d_info, uart_mxc_port * umxc)
{
      int ret = 0, rxbufs, i, j;
      mxc_dma_requestbuf_t *readchnl_reqelem;
      mxc_uart_rxdmamap *rx_buf_elem;

      /* Request for the read and write channels */
      d_info->rd_channel = mxc_dma_request(umxc->dma_rx_id, "MXC UART Read");
      if (d_info->rd_channel < 0) {
            printk(KERN_ERR "MXC UART: Cannot allocate DMA read channel\n");
            return -1;
      } else {
            d_info->wr_channel =
                mxc_dma_request(umxc->dma_tx_id, "MXC UART Write");
            if (d_info->wr_channel < 0) {
                  mxc_dma_free(d_info->rd_channel);
                  printk(KERN_ERR
                         "MXC UART: Cannot allocate DMA write channel\n");
                  return -1;
            }
      }

      /* Allocate the DMA Transmit Buffer */
      if ((umxc->tx_buf = kmalloc(TXDMA_BUFF_SIZE, GFP_KERNEL)) == NULL) {
            ret = -1;
            goto err_dma_tx_buff;
      }
      rxbufs = umxc->dma_rxbuf_size / RXDMA_BUFF_SIZE;
      /* Allocate the DMA Virtual Receive Buffer */
      if ((umxc->rx_dmamap = kmalloc(rxbufs * sizeof(mxc_uart_rxdmamap),
                               GFP_KERNEL)) == NULL) {
            ret = -1;
            goto err_dma_rx_buff;
      }

      /* Allocate the DMA Receive Request structures */
      if ((readchnl_reqelem =
           kmalloc(rxbufs * sizeof(mxc_dma_requestbuf_t),
                 GFP_KERNEL)) == NULL) {
            ret = -1;
            goto err_request;
      }

      for (i = 0; i < rxbufs; i++) {
            rx_buf_elem = (mxc_uart_rxdmamap *) (umxc->rx_dmamap + i);
            rx_buf_elem->rx_buf =
                dma_alloc_coherent(NULL, RXDMA_BUFF_SIZE,
                               &rx_buf_elem->rx_handle, GFP_DMA);
            if (rx_buf_elem->rx_buf == NULL) {
                  for (j = 0; j < i; j++) {
                        rx_buf_elem =
                            (mxc_uart_rxdmamap *) (umxc->rx_dmamap + j);
                        dma_free_coherent(NULL, RXDMA_BUFF_SIZE,
                                      rx_buf_elem->rx_buf,
                                      rx_buf_elem->rx_handle);
                  }
                  ret = -1;
                  goto cleanup;
            }
      }

      umxc->dma_rxbuf_id = 0;
      /* Setup the DMA read request structures */
      for (i = 0; i < rxbufs; i++) {
            rx_buf_elem = (mxc_uart_rxdmamap *) (umxc->rx_dmamap + i);
            (readchnl_reqelem + i)->src_addr = umxc->port.mapbase;
            (readchnl_reqelem + i)->dst_addr = rx_buf_elem->rx_handle;
            (readchnl_reqelem + i)->num_of_bytes = RXDMA_BUFF_SIZE;
      }
      mxc_dma_config(d_info->rd_channel, readchnl_reqelem, rxbufs,
                   MXC_DMA_MODE_READ);
      mxc_dma_callback_set(d_info->rd_channel, mxcuart_dmaread_callback,
                       umxc);
      mxc_dma_callback_set(d_info->wr_channel, mxcuart_dma_writecallback,
                       umxc);

      /* Start the read channel */
      mxc_dma_enable(d_info->rd_channel);
      kfree(readchnl_reqelem);
      tasklet_init(&d_info->dma_tx_tasklet, dma_tx_do_tasklet,
                 (unsigned long)umxc);
      d_info->dma_txchnl_inuse = 0;
      return ret;
      cleanup:
      kfree(readchnl_reqelem);
      err_request:
      kfree(umxc->rx_dmamap);
      err_dma_rx_buff:
      kfree(umxc->tx_buf);
      err_dma_tx_buff:
      mxc_dma_free(d_info->rd_channel);
      mxc_dma_free(d_info->wr_channel);

      return ret;
}

/*!
 * Stops DMA and frees the DMA resources
 *
 * @param   d_info the structure that holds all the DMA information for a
 *                 particular MXC UART
 * @param   umxc   the MXC UART port structure, this includes the \b uart_port
 *                 structure and other members that are specific to MXC UARTs
 */
01034 static void mxcuart_freedma(dma_info * d_info, uart_mxc_port * umxc)
{
      int i, rxbufs;
      mxc_uart_rxdmamap *rx_buf_elem;

      rxbufs = umxc->dma_rxbuf_size / RXDMA_BUFF_SIZE;

      for (i = 0; i < rxbufs; i++) {
            rx_buf_elem = (mxc_uart_rxdmamap *) (umxc->rx_dmamap + i);
            dma_free_coherent(NULL, RXDMA_BUFF_SIZE,
                          rx_buf_elem->rx_buf, rx_buf_elem->rx_handle);
      }
      kfree(umxc->rx_dmamap);
      kfree(umxc->tx_buf);
      mxc_dma_free(d_info->rd_channel);
      mxc_dma_free(d_info->wr_channel);
}

/*!
 * This function is called to free the interrupts.
 *
 * @param   umxc   the MXC UART port structure, this includes the \b uart_port
 *                 structure and other members that are specific to MXC UARTs
 */
01058 static void mxcuart_free_interrupts(uart_mxc_port * umxc)
{
      free_irq(umxc->port.irq, umxc);
      if (umxc->ints_muxed == 0) {
            free_irq(umxc->irqs[0], umxc);
            free_irq(umxc->irqs[1], umxc);
      }
}

/*!
 * Calculate and set the UART port clock value
 *
 * @param   umxc     the MXC UART port structure, this includes the \b uart_port
 *                   structure and other members that are specific to MXC UARTs
 * @param   per_clk  peripheral clock coming into the MXC UART module
 * @param   req_baud current baudrate requested
 * @param   div      returns the reference frequency divider value
 */
01076 static void mxcuart_set_ref_freq(uart_mxc_port * umxc, unsigned long per_clk,
                         unsigned int req_baud, int *div)
{
      unsigned int d = 1;

      /*
       * Choose the smallest possible prescaler to maximize
       * the chance of using integer scaling.  Ensure that
       * the calculation won't overflow.  Limit the denom
       * to 15 bits since a 16-bit denom doesn't work.
       */
      if (req_baud < (1 << (31 - (4 + 15))))
            d = per_clk / (req_baud << (4 + 15)) + 1;

      umxc->port.uartclk = per_clk / d;

      /*
       * Set the ONEMS register that is used by IR special case bit and
       * the Escape character detect logic
       */
      writel(umxc->port.uartclk / 1000, umxc->port.membase + MXC_UARTONEMS);
      *div = d;
}

/*!
 * This function is called by the core driver to initialize the low-level
 * driver. The function grabs the interrupt resources and registers its
 * interrupt service routines. It then initializes the IOMUX registers to
 * configure the pins for UART signals and finally initializes the various
 * UART registers and enables the port for reception.
 *
 * @param   port   the port structure for the UART passed in by the core driver
 *
 * @return  The function returns 0 on success and a non-zero value on failure.
 */
01111 static int mxcuart_startup(struct uart_port *port)
{
      uart_mxc_port *umxc = (uart_mxc_port *) port;
      int retval;
      volatile unsigned int cr, cr1 = 0, cr2 = 0, ufcr = 0;

      /*
       * Some UARTs need separate registrations for the interrupts as
       * they do not take the muxed interrupt output to the ARM core
       */
      if (umxc->ints_muxed == 1) {
            retval = request_irq(umxc->port.irq, mxcuart_int, 0,
                             "mxcintuart", umxc);
            if (retval != 0) {
                  return retval;
            }
      } else {
            retval = request_irq(umxc->port.irq, mxcuart_tx_int,
                             0, "mxcintuart", umxc);
            if (retval != 0) {
                  return retval;
            } else {
                  retval = request_irq(umxc->irqs[0], mxcuart_rx_int,
                                   0, "mxcintuart", umxc);
                  if (retval != 0) {
                        free_irq(umxc->port.irq, umxc);
                        return retval;
                  } else {
                        retval =
                            request_irq(umxc->irqs[1], mxcuart_mint_int,
                                    0, "mxcintuart", umxc);
                        if (retval != 0) {
                              free_irq(umxc->port.irq, umxc);
                              free_irq(umxc->irqs[0], umxc);
                              return retval;
                        }
                  }
            }
      }

      /* Initialize the DMA if we need SDMA data transfer */
      if (umxc->dma_enabled == 1) {
            retval = mxcuart_initdma(dma_list + umxc->port.line, umxc);
            if (retval != 0) {
                  printk
                      (KERN_ERR
                       "MXC UART: Failed to initialize DMA for UART %d\n",
                       umxc->port.line);
                  mxcuart_free_interrupts(umxc);
                  return retval;
            }
            /* Configure the GPR register to receive SDMA events */
            config_uartdma_event(umxc->port.line);
      }

      /*
       * Clear Status Registers 1 and 2
       */
      writel(0xFFFF, umxc->port.membase + MXC_UARTUSR1);
      writel(0xFFFF, umxc->port.membase + MXC_UARTUSR2);

      /* Configure the IOMUX for the UART */
      gpio_uart_active(umxc->port.line, umxc->ir_mode);

      /*
       * Set the transceiver invert bits if required
       */
      if (umxc->ir_mode == IRDA) {
            echo_cancel = 1;
            writel(umxc->ir_rx_inv | MXC_UARTUCR4_IRSC, umxc->port.membase
                   + MXC_UARTUCR4);
            writel(umxc->rxd_mux | umxc->ir_tx_inv,
                   umxc->port.membase + MXC_UARTUCR3);
      } else {
            writel(umxc->rxd_mux, umxc->port.membase + MXC_UARTUCR3);
      }

      /*
       * Initialize UCR1,2 and UFCR registers
       */
      if (umxc->dma_enabled == 1) {
            cr2 = (MXC_UARTUCR2_TXEN | MXC_UARTUCR2_RXEN);
      } else {
            cr2 =
                (MXC_UARTUCR2_ATEN | MXC_UARTUCR2_TXEN | MXC_UARTUCR2_RXEN);
      }

      writel(cr2, umxc->port.membase + MXC_UARTUCR2);
      /* Wait till we are out of software reset */
      do {
            cr = readl(umxc->port.membase + MXC_UARTUCR2);
      } while (!(cr & MXC_UARTUCR2_SRST));

      if (umxc->mode == MODE_DTE) {
            ufcr |= ((umxc->tx_threshold << MXC_UARTUFCR_TXTL_OFFSET) |
                   MXC_UARTUFCR_DCEDTE | MXC_UARTUFCR_RFDIV | umxc->
                   rx_threshold);
      } else {
            ufcr |= ((umxc->tx_threshold << MXC_UARTUFCR_TXTL_OFFSET) |
                   MXC_UARTUFCR_RFDIV | umxc->rx_threshold);
      }
      writel(ufcr, umxc->port.membase + MXC_UARTUFCR);

      /*
       * Finally enable the UART and the Receive interrupts
       */
      if (umxc->ir_mode == IRDA) {
            cr1 |= MXC_UARTUCR1_IREN;
      }
      if (umxc->dma_enabled == 1) {
            cr1 |= (MXC_UARTUCR1_RXDMAEN | MXC_UARTUCR1_ATDMAEN |
                  MXC_UARTUCR1_UARTEN);
      } else {
            cr1 |= (MXC_UARTUCR1_RRDYEN | MXC_UARTUCR1_UARTEN);
      }
      writel(cr1, umxc->port.membase + MXC_UARTUCR1);

      return 0;
}

/*!
 * This function is called by the core driver for the low-level driver to free
 * its resources. The function frees all its interrupts and disables the UART.
 *
 * @param   port   the port structure for the UART passed in by the core driver
 */
01237 static void mxcuart_shutdown(struct uart_port *port)
{
      uart_mxc_port *umxc = (uart_mxc_port *) port;

      /* Disable the IOMUX for the UART */
      gpio_uart_inactive(umxc->port.line, umxc->ir_mode);
      mxcuart_free_interrupts(umxc);
      /* Disable all interrupts, port and break condition */
      writel(0, umxc->port.membase + MXC_UARTUCR1);
      writel(0, umxc->port.membase + MXC_UARTUCR3);
      if (umxc->dma_enabled == 1) {
            mxcuart_freedma(dma_list + umxc->port.line, umxc);
      }
}

/*!
 * This function is called while changing the UART parameters. It is called to
 * check if the Infrared special case bit (IRSC) in control register 4 should
 * be set.
 *
 * @param   baudrate   the desired baudrate
 *
 * @return  The functions returns 0 if the IRSC bit does not have to be set,
 *          else it returns a 1.
 */
/*
static int mxcuart_setir_special(u_int baudrate)
{
        u_int thresh_val;

        thresh_val = 1000000 / (8 * MIN_PULSE_DUR);
        if (baudrate > thresh_val) {
                return 0;
        }

        return 1;
}
*/

/*!
 * This function is called by the core driver to change the UART parameters,
 * including baudrate, word length, parity, stop bits. The function also updates
 * the port structures mask registers to indicate the types of events the user is
 * interested in receiving.
 *
 * @param   port    the port structure for the UART passed in by the core driver
 * @param   termios the desired termios settings
 * @param   old     old termios
 */
01286 static void mxcuart_set_termios(struct uart_port *port,
                        struct ktermios *termios, struct ktermios *old)
{
      uart_mxc_port *umxc = (uart_mxc_port *) port;
      volatile unsigned int cr4 = 0, cr2 = 0, ufcr;
      u_int num, denom, baud;
      u_int cr2_mask;         /* Used to add the changes to CR2 */
      unsigned long flags, per_clk;
      int div;

      cr2_mask = ~(MXC_UARTUCR2_IRTS | MXC_UARTUCR2_CTSC | MXC_UARTUCR2_PREN |
                 MXC_UARTUCR2_PROE | MXC_UARTUCR2_STPB | MXC_UARTUCR2_WS);

      per_clk = clk_get_rate(umxc->clk);

      /*
       * Ask the core to get the baudrate, if requested baudrate is not
       * between max and min, then either use the baudrate in old termios
       * setting. If it's still invalid, we try 9600 baud.
       */
      baud = uart_get_baud_rate(&umxc->port, termios, old, 0, per_clk / 16);
      /* Set the Reference frequency divider */
      mxcuart_set_ref_freq(umxc, per_clk, baud, &div);

      /* Byte size, default is 8-bit mode */
      switch (termios->c_cflag & CSIZE) {
      case CS7:
            cr2 = 0;
            break;
      default:
            cr2 = MXC_UARTUCR2_WS;
            break;
      }
      /* Check to see if we need 2 Stop bits */
      if (termios->c_cflag & CSTOPB) {
            cr2 |= MXC_UARTUCR2_STPB;
      }

      /* Check to see if we need Parity checking */
      if (termios->c_cflag & PARENB) {
            cr2 |= MXC_UARTUCR2_PREN;
            if (termios->c_cflag & PARODD) {
                  cr2 |= MXC_UARTUCR2_PROE;
            }
      }
      spin_lock_irqsave(&umxc->port.lock, flags);

      ufcr = readl(umxc->port.membase + MXC_UARTUFCR);
      ufcr = (ufcr & (~MXC_UARTUFCR_RFDIV_MASK)) |
          ((6 - div) << MXC_UARTUFCR_RFDIV_OFFSET);
      writel(ufcr, umxc->port.membase + MXC_UARTUFCR);

      /*
       * Update the per-port timeout
       */
      uart_update_timeout(&umxc->port, termios->c_cflag, baud);

      umxc->port.read_status_mask = MXC_UARTURXD_OVRRUN;
      /*
       * Enable appropriate events to be passed to the TTY layer
       */
      if (termios->c_iflag & INPCK) {
            umxc->port.read_status_mask |= MXC_UARTURXD_FRMERR |
                MXC_UARTURXD_PRERR;
      }
      if (termios->c_iflag & (BRKINT | PARMRK)) {
            umxc->port.read_status_mask |= MXC_UARTURXD_BRK;
      }

      /*
       * Characters to ignore
       */
      umxc->port.ignore_status_mask = 0;
      if (termios->c_iflag & IGNPAR) {
            umxc->port.ignore_status_mask |= MXC_UARTURXD_FRMERR |
                MXC_UARTURXD_PRERR;
      }
      if (termios->c_iflag & IGNBRK) {
            umxc->port.ignore_status_mask |= MXC_UARTURXD_BRK;
            /*
             * If we are ignoring parity and break indicators,
             * ignore overruns too (for real raw support)
             */
            if (termios->c_iflag & IGNPAR) {
                  umxc->port.ignore_status_mask |= MXC_UARTURXD_OVRRUN;
            }
      }

      /*
       * Ignore all characters if CREAD is not set, still receive characters
       * from the port, but throw them away.
       */
      if ((termios->c_cflag & CREAD) == 0) {
            umxc->port.ignore_status_mask |= UART_CREAD_BIT;
      }

      cr4 = readl(umxc->port.membase + MXC_UARTUCR4);
      if (UART_ENABLE_MS(port, termios->c_cflag)) {
            mxcuart_enable_ms(port);
            if (umxc->hardware_flow == 1) {
                  cr4 = (cr4 & (~MXC_UARTUCR4_CTSTL_MASK)) |
                      (umxc->cts_threshold << MXC_UARTUCR4_CTSTL_OFFSET);
                  cr2 |= MXC_UARTUCR2_CTSC;
                  umxc->port.info->port.tty->hw_stopped = 0;
            } else {
                  cr2 |= MXC_UARTUCR2_IRTS;
            }
      } else {
            cr2 |= MXC_UARTUCR2_IRTS;
      }

      /* Add Parity, character length and stop bits information */
      cr2 |= (readl(umxc->port.membase + MXC_UARTUCR2) & cr2_mask);
      writel(cr2, umxc->port.membase + MXC_UARTUCR2);
      /*
         if (umxc->ir_mode == IRDA) {
         ret = mxcuart_setir_special(baud);
         if (ret == 0) {
         cr4 &= ~MXC_UARTUCR4_IRSC;
         } else {
         cr4 |= MXC_UARTUCR4_IRSC;
         }
         } */
      writel(cr4, umxc->port.membase + MXC_UARTUCR4);

      /*
       * Set baud rate
       */

      /* Use integer scaling, if possible. Limit the denom to 15 bits. */
      num = 0;
      denom = (umxc->port.uartclk + 8 * baud) / (16 * baud) - 1;

      /* Use fractional scaling if needed to limit the max error to 0.5% */
      if (denom < 100) {
            u64 n64 = (u64) 16 * 0x8000 * baud + (umxc->port.uartclk / 2);
            do_div(n64, umxc->port.uartclk);
            num = (u_int) n64 - 1;
            denom = 0x7fff;
      }
      writel(num, umxc->port.membase + MXC_UARTUBIR);
      writel(denom, umxc->port.membase + MXC_UARTUBMR);

      spin_unlock_irqrestore(&umxc->port.lock, flags);
}

/*!
 * This function is called by the core driver to know the UART type.
 *
 * @param   port   the port structure for the UART passed in by the core driver
 *
 * @return  The function returns a pointer to a string describing the UART port.
 */
01439 static const char *mxcuart_type(struct uart_port *port)
{
      return port->type == PORT_IMX ? "Freescale i.MX" : NULL;
}

/*!
 * This function is called by the core driver to release the memory resources
 * currently in use by the UART port.
 *
 * @param   port   the port structure for the UART passed in by the core driver
 */
01450 static void mxcuart_release_port(struct uart_port *port)
{
      release_mem_region(port->mapbase, SZ_4K);
}

/*!
 * This function is called by the core driver to request memory resources for
 * the UART port.
 *
 * @param   port   the port structure for the UART passed in by the core driver
 *
 * @return  The function returns \b -EBUSY on failure, else it returns 0.
 */
01463 static int mxcuart_request_port(struct uart_port *port)
{
      return request_mem_region(port->mapbase, SZ_4K, "serial_mxc")
          != NULL ? 0 : -EBUSY;
}

/*!
 * This function is called by the core driver to perform any autoconfiguration
 * steps required for the UART port. This function sets the port->type field.
 *
 * @param   port   the port structure for the UART passed in by the core driver
 * @param   flags  bit mask of the required configuration
 */
01476 static void mxcuart_config_port(struct uart_port *port, int flags)
{
      if ((flags & UART_CONFIG_TYPE) && (mxcuart_request_port(port) == 0)) {
            port->type = PORT_IMX;
      }
}

/*!
 * This function is called by the core driver to verify that the new serial
 * port information contained within \a ser is suitable for this UART port type.
 * The function checks to see if the UART port type specified by the user
 * application while setting the UART port information matches what is stored
 * in the define \b PORT_MXC found in the header file include/linux/serial_core.h
 *
 * @param   port   the port structure for the UART passed in by the core driver
 * @param   ser    the new serial port information
 *
 * @return  The function returns 0 on success or \b -EINVAL if the port type
 *          specified is not equal to \b PORT_MXC.
 */
01496 static int mxcuart_verify_port(struct uart_port *port,
                         struct serial_struct *ser)
{
      int ret = 0;
      if (ser->type != PORT_UNKNOWN && ser->type != PORT_IMX) {
            ret = -EINVAL;
      }
      return ret;
}

/*!
 * This function is used to send a high priority XON/XOFF character
 *
 * @param   port   the port structure for the UART passed in by the core driver
 * @param   ch     the character to send
 */
01512 static void mxcuart_send_xchar(struct uart_port *port, char ch)
{
      unsigned long flags;

      port->x_char = ch;
      if (port->info->port.tty->hw_stopped) {
            return;
      }

      if (ch) {
            spin_lock_irqsave(&port->lock, flags);
            port->ops->start_tx(port);
            spin_unlock_irqrestore(&port->lock, flags);
      }
}

/*!
 * This function is used enable/disable the MXC UART clocks
 *
 * @param   port      the port structure for the UART passed in by the core driver
 * @param   state     New PM state
 * @param   oldstate  Current PM state
 */
static void
01536 mxcuart_pm(struct uart_port *port, unsigned int state, unsigned int oldstate)
{
      uart_mxc_port *umxc = (uart_mxc_port *) port;

      if (state)
            clk_disable(umxc->clk);
      else
            clk_enable(umxc->clk);
}

/*!
 * This structure contains the pointers to the control functions that are
 * invoked by the core serial driver to access the UART hardware. The
 * structure is passed to serial_core.c file during registration.
 */
01551 static struct uart_ops mxc_ops = {
      .tx_empty = mxcuart_tx_empty,
      .set_mctrl = mxcuart_set_mctrl,
      .get_mctrl = mxcuart_get_mctrl,
      .stop_tx = mxcuart_stop_tx,
      .start_tx = mxcuart_start_tx,
      .stop_rx = mxcuart_stop_rx,
      .enable_ms = mxcuart_enable_ms,
      .break_ctl = mxcuart_break_ctl,
      .startup = mxcuart_startup,
      .shutdown = mxcuart_shutdown,
      .set_termios = mxcuart_set_termios,
      .type = mxcuart_type,
      .pm = mxcuart_pm,
      .release_port = mxcuart_release_port,
      .request_port = mxcuart_request_port,
      .config_port = mxcuart_config_port,
      .verify_port = mxcuart_verify_port,
      .send_xchar = mxcuart_send_xchar,
};

#ifdef CONFIG_SERIAL_MXC_CONSOLE

/*
 * Write out a character once the UART is ready
 */
static inline void mxcuart_console_write_char(struct uart_port *port, char ch)
{
      volatile unsigned int status;

      do {
            status = readl(port->membase + MXC_UARTUSR1);
      } while ((status & MXC_UARTUSR1_TRDY) == 0);
      writel(ch, port->membase + MXC_UARTUTXD);
}

/*!
 * This function is called to write the console messages through the UART port.
 *
 * @param   co    the console structure
 * @param   s     the log message to be written to the UART
 * @param   count length of the message
 */
static void mxcuart_console_write(struct console *co, const char *s,
                          u_int count)
{
      struct uart_port *port = &mxc_ports[co->index]->port;
      volatile unsigned int status, oldcr1, oldcr2, oldcr3, cr2, cr3;
      int i;

      /*
       * First save the control registers and then disable the interrupts
       */
      oldcr1 = readl(port->membase + MXC_UARTUCR1);
      oldcr2 = readl(port->membase + MXC_UARTUCR2);
      oldcr3 = readl(port->membase + MXC_UARTUCR3);
      cr2 =
          oldcr2 & ~(MXC_UARTUCR2_ATEN | MXC_UARTUCR2_RTSEN |
                   MXC_UARTUCR2_ESCI);
      cr3 =
          oldcr3 & ~(MXC_UARTUCR3_DCD | MXC_UARTUCR3_RI |
                   MXC_UARTUCR3_DTRDEN);
      writel(MXC_UARTUCR1_UARTEN, port->membase + MXC_UARTUCR1);
      writel(cr2, port->membase + MXC_UARTUCR2);
      writel(cr3, port->membase + MXC_UARTUCR3);
      /*
       * Do each character
       */
      for (i = 0; i < count; i++) {
            mxcuart_console_write_char(port, s[i]);
            if (s[i] == '\n') {
                  mxcuart_console_write_char(port, '\r');
            }
      }
      /*
       * Finally, wait for the transmitter to become empty
       */
      do {
            status = readl(port->membase + MXC_UARTUSR2);
      } while (!(status & MXC_UARTUSR2_TXDC));

      /*
       * Restore the control registers
       */
      writel(oldcr1, port->membase + MXC_UARTUCR1);
      writel(oldcr2, port->membase + MXC_UARTUCR2);
      writel(oldcr3, port->membase + MXC_UARTUCR3);
}

/*!
 * Initializes the UART port to be used to print console message with the
 * options specified. If no options are specified, then the function
 * initializes the UART with the default options of baudrate=115200, 8 bit
 * word size, no parity, no flow control.
 *
 * @param   co      The console structure
 * @param   options Any console options passed in from the command line
 *
 * @return  The function returns 0 on success or error.
 */
static int __init mxcuart_console_setup(struct console *co, char *options)
{
      uart_mxc_port *umxc;
      int baud = 115200;
      int bits = 8;
      int parity = 'n';
      int flow = 'n';
      volatile unsigned int cr = 0;

      /*
       * Check whether an invalid uart number had been specified, and if
       * so, search for the first available port that does have console
       * support
       */
      if (co->index >= MXC_UART_NR) {
            co->index = 0;
      }
      umxc = mxc_ports[co->index];

      if (umxc == NULL) {
            return -ENODEV;
      }

      clk_enable(umxc->clk);

      /* initialize port.lock else oops */
      spin_lock_init(&umxc->port.lock);

      /*
       * Initialize the UART registers
       */
      writel(MXC_UARTUCR1_UARTEN, umxc->port.membase + MXC_UARTUCR1);
      /* Enable the transmitter and do a software reset */
      writel(MXC_UARTUCR2_TXEN, umxc->port.membase + MXC_UARTUCR2);
      /* Wait till we are out of software reset */
      do {
            cr = readl(umxc->port.membase + MXC_UARTUCR2);
      } while (!(cr & MXC_UARTUCR2_SRST));

      writel(0x0, umxc->port.membase + MXC_UARTUCR3);
      writel(0x0, umxc->port.membase + MXC_UARTUCR4);
      /* Set TXTL to 2, RXTL to 1 and RFDIV to 2 */
      cr = 0x0800 | MXC_UARTUFCR_RFDIV | 0x1;
      if (umxc->mode == MODE_DTE) {
            cr |= MXC_UARTUFCR_DCEDTE;
      }
      writel(cr, umxc->port.membase + MXC_UARTUFCR);
      writel(0xFFFF, umxc->port.membase + MXC_UARTUSR1);
      writel(0xFFFF, umxc->port.membase + MXC_UARTUSR2);

      if (options != NULL) {
            uart_parse_options(options, &baud, &parity, &bits, &flow);
      }
      gpio_uart_active(umxc->port.line, umxc->ir_mode);
      return uart_set_options(&umxc->port, co, baud, parity, bits, flow);
}

static struct uart_driver mxc_reg;

/*!
 * This structure contains the pointers to the UART console functions. It is
 * passed as an argument when registering the console.
 */
static struct console mxc_console = {
      .name = "ttymxc",
      .write = mxcuart_console_write,
      .device = uart_console_device,
      .setup = mxcuart_console_setup,
      .flags = CON_PRINTBUFFER,
      .index = -1,
      .data = &mxc_reg,
};

/*!
 * This function registers the console callback functions with the kernel.
 */
static int __init mxcuart_console_init(void)
{
      register_console(&mxc_console);
      return 0;
}

console_initcall(mxcuart_console_init);

static int __init find_port(struct uart_port *p)
{
      int line;
      struct uart_port *port;
      for (line = 0; line < MXC_UART_NR; line++) {
            if (!mxc_ports[line])
                  continue;
            port = &mxc_ports[line]->port;
            if (uart_match_port(p, port))
                  return line;
      }
      return -ENODEV;
}

int __init mxc_uart_start_console(struct uart_port *port, char *options)
{
      int line;
      line = find_port(port);
      if (line < 0)
            return -ENODEV;

      add_preferred_console("ttymxc", line, options);
      printk("Switching Console to ttymxc%d at %s 0x%lx (options '%s')\n",
             line, port->iotype == UPIO_MEM ? "MMIO" : "I/O port",
             port->iotype ==
             UPIO_MEM ? (unsigned long)port->mapbase : (unsigned long)port->
             iobase, options);

      if (!(mxc_console.flags & CON_ENABLED)) {
            mxc_console.flags &= ~CON_PRINTBUFFER;
            register_console(&mxc_console);
      }
      return 0;
}

#define MXC_CONSOLE     &mxc_console
#else
#define MXC_CONSOLE     NULL
#endif                        /* CONFIG_SERIAL_MXC_CONSOLE */

/*!
 * This structure contains the information such as the name of the UART driver
 * that appears in the /dev folder, major and minor numbers etc. This structure
 * is passed to the serial_core.c file.
 */
01780 static struct uart_driver mxc_reg = {
      .owner = THIS_MODULE,
      .driver_name = "ttymxc",
      .dev_name = "ttymxc",
      .major = SERIAL_MXC_MAJOR,
      .minor = SERIAL_MXC_MINOR,
      .nr = MXC_UART_NR,
      .cons = MXC_CONSOLE,
};

/*!
 * This function is called to put the UART in a low power state. Refer to the
 * document driver-model/driver.txt in the kernel source tree for more
 * information.
 *
 * @param   pdev  the device structure used to give information on which UART
 *                to suspend
 * @param   state the power state the device is entering
 *
 * @return  The function returns 0 on success and -1 on failure
 */
01801 static int mxcuart_suspend(struct platform_device *pdev, pm_message_t state)
{
      uart_mxc_port *umxc = platform_get_drvdata(pdev);

      if (umxc == NULL)
            return 0;   /* skip disabled ports */

      if (umxc->port.info && umxc->port.info->flags & UIF_INITIALIZED)
            uart_suspend_port(&mxc_reg, &umxc->port);

      if (umxc->port.info && umxc->port.info->flags & UIF_SUSPENDED)
            umxc->port.info->port.tty->hw_stopped = 1;

      return 0;
}

/*!
 * This function is called to bring the UART back from a low power state. Refer
 * to the document driver-model/driver.txt in the kernel source tree for more
 * information.
 *
 * @param   pdev  the device structure used to give information on which UART
 *                to resume
 *
 * @return  The function returns 0 on success and -1 on failure
 */
01827 static int mxcuart_resume(struct platform_device *pdev)
{
      uart_mxc_port *umxc = platform_get_drvdata(pdev);

      if (umxc == NULL)
            return 0;   /* skip disabled ports */

      if (umxc->port.info && umxc->port.info->flags & UIF_SUSPENDED) {
            umxc->port.info->port.tty->hw_stopped = 0;
            uart_resume_port(&mxc_reg, &umxc->port);
      }

      return 0;
}

/*!
 * This function is called during the driver binding process. Based on the UART
 * that is being probed this function adds the appropriate UART port structure
 * in the core driver.
 *
 * @param   pdev  the device structure used to store device specific
 *                information that is used by the suspend, resume and remove
 *                functions
 *
 * @return  The function returns 0 if successful; -1 otherwise.
 */
01853 static int mxcuart_probe(struct platform_device *pdev)
{
      int id = pdev->id;

      mxc_ports[id] = pdev->dev.platform_data;
      mxc_ports[id]->port.ops = &mxc_ops;

      /* Do not use UARTs that are disabled during integration */
      if (mxc_ports[id]->enabled == 1) {
            mxc_ports[id]->port.dev = &pdev->dev;
            spin_lock_init(&mxc_ports[id]->port.lock);
            /* Enable the low latency flag for DMA UART ports */
            if (mxc_ports[id]->dma_enabled == 1) {
                  mxc_ports[id]->port.flags |= UPF_LOW_LATENCY;
            }

            mxc_ports[id]->clk = clk_get(&pdev->dev, "uart_clk");
            if (mxc_ports[id]->clk == NULL)
                  return -1;

            uart_add_one_port(&mxc_reg, &mxc_ports[id]->port);
            platform_set_drvdata(pdev, mxc_ports[id]);
      }
      return 0;
}

/*!
 * Dissociates the driver from the UART device. Removes the appropriate UART
 * port structure from the core driver.
 *
 * @param   pdev  the device structure used to give information on which UART
 *                to remove
 *
 * @return  The function always returns 0.
 */
01888 static int mxcuart_remove(struct platform_device *pdev)
{
      uart_mxc_port *umxc = platform_get_drvdata(pdev);

      platform_set_drvdata(pdev, NULL);

      if (umxc) {
            uart_remove_one_port(&mxc_reg, &umxc->port);
      }
      return 0;
}

/*!
 * This structure contains pointers to the power management callback functions.
 */
01903 static struct platform_driver mxcuart_driver = {
      .driver = {
               .name = "mxcintuart",
               },
      .probe = mxcuart_probe,
      .remove = mxcuart_remove,
      .suspend = mxcuart_suspend,
      .resume = mxcuart_resume,
};

/*!
 * This function is used to initialize the UART driver module. The function
 * registers the power management callback functions with the kernel and also
 * registers the UART callback functions with the core serial driver.
 *
 * @return  The function returns 0 on success and a non-zero value on failure.
 */
01920 static int __init mxcuart_init(void)
{
      int ret = 0;

      printk(KERN_INFO "Serial: MXC Internal UART driver\n");
      ret = uart_register_driver(&mxc_reg);
      if (ret == 0) {
            /* Register the device driver structure. */
            ret = platform_driver_register(&mxcuart_driver);
            if (ret != 0) {
                  uart_unregister_driver(&mxc_reg);
            }
      }
      return ret;
}

/*!
 * This function is used to cleanup all resources before the driver exits.
 */
01939 static void __exit mxcuart_exit(void)
{
      platform_driver_unregister(&mxcuart_driver);
      uart_unregister_driver(&mxc_reg);
}

module_init(mxcuart_init);
module_exit(mxcuart_exit);

MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("MXC serial port driver");
MODULE_LICENSE("GPL");

Generated by  Doxygen 1.6.0   Back to index