/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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. */ typedef struct { /*! * Holds the read channel number. */ int rd_channel; /*! * Holds the write channel number. */ int wr_channel; /*! * UART Transmit Event ID */ int tx_event_id; /*! * UART Receive Event ID */ int rx_event_id; /*! * DMA Transmit tasklet */ struct tasklet_struct dma_tx_tasklet; /*! * Flag indicates if the channel is in use */ int dma_txchnl_inuse; } dma_info; /*! * This is used to indicate if we want echo cancellation in the Irda mode. */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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. */ 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. */ 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. */ 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. */ 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. */ 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. */ 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 */ 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 */ 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 */ 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. */ 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 */ 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 */ 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 */ 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. */ 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 */ 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 */ 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. */ 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 */ 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. */ 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 */ 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. */ 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 */ 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 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. */ 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. */ 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 */ 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 */ 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. */ 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. */ 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. */ 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. */ 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. */ 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");