diff options
Diffstat (limited to 'drivers/serial')
-rw-r--r-- | drivers/serial/Kconfig | 104 | ||||
-rw-r--r-- | drivers/serial/Makefile | 4 | ||||
-rw-r--r-- | drivers/serial/mxc_uart.c | 1957 | ||||
-rw-r--r-- | drivers/serial/mxc_uart_early.c | 184 | ||||
-rw-r--r-- | drivers/serial/mxc_uart_reg.h | 128 | ||||
-rw-r--r-- | drivers/serial/mxs-auart.c | 1108 | ||||
-rw-r--r-- | drivers/serial/mxs-duart.c | 803 | ||||
-rw-r--r-- | drivers/serial/regs-duart.h | 301 | ||||
-rw-r--r-- | drivers/serial/regs-uartapp.h | 307 | ||||
-rw-r--r-- | drivers/serial/serial_core.c | 2 |
10 files changed, 4897 insertions, 1 deletions
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 8b23165bc5dc..17cbbd3e9408 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -304,6 +304,110 @@ config SERIAL_AMBA_PL010_CONSOLE your boot loader (lilo or loadlin) about how to pass options to the kernel at boot time.) +config SERIAL_MXC + tristate "MXC Internal serial port support" + depends on ARCH_MXC + select SERIAL_CORE + help + This selects the Freescale Semiconductor MXC Internal UART driver. + If unsure, say N. + +config SERIAL_MXC_CONSOLE + bool "Support for console on a MXC/MX27/MX21 Internal serial port" + depends on SERIAL_MXC=y + select SERIAL_CORE_CONSOLE + help + Say Y here if you wish to use an MXC Internal UART as the system + console (the system console is the device which receives all kernel + messages and warnings and which allows logins in single user mode). + + Even if you say Y here, the currently visible framebuffer console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttymxc". (Try "man bootparam" or see the documentation of + your boot loader (lilo or loadlin) about how to pass options to the + kernel at boot time.) + +config SERIAL_STMP_DBG + tristate "STMP debug serial port support" + depends on ARCH_STMP3XXX + select SERIAL_CORE + help + Driver for Sigmatel 36XX/37XX internal debug serial port + +config SERIAL_STMP_DBG_CONSOLE + bool "Support for console on STMP37XX DBG serial port" + depends on SERIAL_STMP_DBG=y + select SERIAL_CORE_CONSOLE + ---help--- + Say Y here if you wish to use the STMP36XX/37XX debug serial port as the + system console (the system console is the device which receives all + kernel messages and warnings and which allows logins in single user + mode). + + Even if you say Y here, the currently visible framebuffer console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttyAM0". (Try "man bootparam" or see the documentation of + your boot loader (lilo or loadlin) about how to pass options to the + kernel at boot time.) + +config SERIAL_STMP_APP + tristate "STMP app serial port support" + depends on ARCH_STMP3XXX + select SERIAL_CORE + help + Driver for Sigmatel 36XX/37XX internal application serial port + +config SERIAL_MXS_DUART + tristate "i.MXS debug serial port support" + depends on ARCH_MXS + select SERIAL_CORE + help + Driver for Freescale i.MXS internal debug serial port + +config SERIAL_MXS_AUART + tristate "i.MXS Application serial port support" + depends on ARCH_MXS + select SERIAL_CORE + help + Driver for Freescale i.MXS internal application serial port + +config SERIAL_MXS_AUART_CONSOLE + bool "Support for console on i.MXS application serial port" + depends on SERIAL_MXS_AUART=y + select SERIAL_CORE_CONSOLE + ---help--- + Say Y here if you wish to use the i.MXS app serial port as the + system console (the system console is the device which receives all + kernel messages and warnings and which allows logins in single user + mode). + + Even if you say Y here, the currently visible framebuffer console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttySP1". (Try "man bootparam" or see the documentation of + your boot loader (lilo or loadlin) about how to pass options to the + kernel at boot time.) + + +config SERIAL_MXS_DUART_CONSOLE + bool "Support for console on i.MXS debug serial port" + depends on SERIAL_MXS_DUART=y + select SERIAL_CORE_CONSOLE + ---help--- + Say Y here if you wish to use the i.MXS debug serial port as the + system console (the system console is the device which receives all + kernel messages and warnings and which allows logins in single user + mode). + + Even if you say Y here, the currently visible framebuffer console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttyAM0". (Try "man bootparam" or see the documentation of + your boot loader (lilo or loadlin) about how to pass options to the + kernel at boot time.) + config SERIAL_AMBA_PL011 tristate "ARM AMBA PL011 serial port support" depends on ARM_AMBA diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 208a85572c32..2ed31d54c6c6 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -75,6 +75,10 @@ obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o obj-$(CONFIG_SERIAL_MSM) += msm_serial.o obj-$(CONFIG_SERIAL_NETX) += netx-serial.o +obj-$(CONFIG_SERIAL_MXC) += mxc_uart.o +obj-$(CONFIG_SERIAL_MXC_CONSOLE) += mxc_uart_early.o +obj-$(CONFIG_SERIAL_MXS_DUART) += mxs-duart.o +obj-$(CONFIG_SERIAL_MXS_AUART) += mxs-auart.o obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL) += nwpserial.o obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o diff --git a/drivers/serial/mxc_uart.c b/drivers/serial/mxc_uart.c new file mode 100644 index 000000000000..347a7462c396 --- /dev/null +++ b/drivers/serial/mxc_uart.c @@ -0,0 +1,1957 @@ +/* + * Copyright 2004-2010 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 <linux/io.h> +#include <linux/slab.h> +#include <asm/irq.h> +#include <asm/dma.h> +#include <asm/div64.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. + */ +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.state->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.state->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.state->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.state->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.state->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.state->port.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.state->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; + + umxc->dma_rxbuf_id += 1; + if (umxc->dma_rxbuf_id >= 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.state->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 */ + umxc->tx_buf = kmalloc(TXDMA_BUFF_SIZE, GFP_KERNEL); + if (umxc->tx_buf == NULL) { + ret = -1; + goto err_dma_tx_buff; + } + rxbufs = umxc->dma_rxbuf_size / RXDMA_BUFF_SIZE; + /* Allocate the DMA Virtual Receive Buffer */ + umxc->rx_dmamap = kmalloc(rxbufs * sizeof(mxc_uart_rxdmamap), GFP_KERNEL); + if (umxc->rx_dmamap == NULL) { + ret = -1; + goto err_dma_rx_buff; + } + + /* Allocate the DMA Receive Request structures */ + readchnl_reqelem = kmalloc(rxbufs * sizeof(mxc_dma_requestbuf_t), + GFP_KERNEL); + if (readchnl_reqelem == 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 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; + } + + /* Hardware flow control should controled by userspace */ + umxc->hardware_flow = (termios->c_cflag & CRTSCTS) ? 1 : 0; + + 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.state->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) +{ + struct platform_device *pdev = to_platform_device(port->dev); + struct resource *mmres; + void *ret; + + mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mmres) + return -ENODEV; + + ret = request_mem_region(mmres->start, mmres->end - mmres->start + 1, + "serial_mxc"); + + return ret ? 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->state->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 && umxc->port.flags & ASYNC_INITIALIZED) + uart_suspend_port(&mxc_reg, &umxc->port); + + if (umxc && umxc->port.flags & ASYNC_SUSPENDED) + umxc->port.state->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 && umxc->port.flags & ASYNC_SUSPENDED) { + umxc->port.state->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; + struct resource *res; + void __iomem *base; + + 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) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + base = ioremap(res->start, res->end - res->start + 1); + if (!base) + return -ENOMEM; + + mxc_ports[id]->port.membase = base; + mxc_ports[id]->port.mapbase = res->start; + mxc_ports[id]->port.dev = &pdev->dev; + mxc_ports[id]->port.irq = platform_get_irq(pdev, 0); + mxc_ports[id]->irqs[0] = platform_get_irq(pdev, 1); + mxc_ports[id]->irqs[1] = platform_get_irq(pdev, 2); + 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 |= ASYNC_LOW_LATENCY; + } + + mxc_ports[id]->clk = clk_get(&pdev->dev, NULL); + 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); + iounmap(umxc->port.membase); + } + 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"); diff --git a/drivers/serial/mxc_uart_early.c b/drivers/serial/mxc_uart_early.c new file mode 100644 index 000000000000..f4b6493a9a64 --- /dev/null +++ b/drivers/serial/mxc_uart_early.c @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2004-2010 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_early.c + * + * @brief Driver for the Freescale Semiconductor MXC serial ports based on + * drivers/char/8250_early.c, Copyright 2004 Hewlett-Packard Development Company, + * L.P. by Bjorn Helgaasby. + * + * Early serial console for MXC UARTS. + * + * This is for use before the serial driver has initialized, in + * particular, before the UARTs have been discovered and named. + * Instead of specifying the console device as, e.g., "ttymxc0", + * we locate the device directly by its MMIO or I/O port address. + * + * The user can specify the device directly, e.g., + * console=mxcuart,0x43f90000,115200n8 + * or platform code can call early_uart_console_init() to set + * the early UART device. + * + * After the normal serial driver starts, we try to locate the + * matching ttymxc device and start a console there. + */ + +/* + * Include Files + */ + +#include <linux/tty.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/console.h> +#include <linux/serial_core.h> +#include <linux/serial_reg.h> +#include <linux/clk.h> +#include <mach/mxc_uart.h> + +struct mxc_early_uart_device { + struct uart_port port; + char options[16]; /* e.g., 115200n8 */ + unsigned int baud; + struct clk *clk; +}; +static struct mxc_early_uart_device mxc_early_device __initdata; + +/* + * Write out a character once the UART is ready + */ +static void __init mxcuart_console_write_char(struct uart_port *port, int ch) +{ + unsigned int status; + + do { + status = readl(port->membase + MXC_UARTUSR2); + } while ((status & MXC_UARTUSR2_TXFE) == 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 + */ +void __init early_mxcuart_console_write(struct console *co, const char *s, + u_int count) +{ + struct uart_port *port = &mxc_early_device.port; + volatile unsigned int status, oldcr1, oldcr2, oldcr3, cr2, cr3; + + /* + * 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); + + /* Transmit string */ + uart_console_write(port, s, count, mxcuart_console_write_char); + + /* + * 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); +} + +static unsigned int __init probe_baud(struct uart_port *port) +{ + /* FIXME Return Default Baud Rate */ + return 115200; +} + +static int __init mxc_early_uart_setup(struct console *console, char *options) +{ + struct mxc_early_uart_device *device = &mxc_early_device; + struct uart_port *port = &device->port; + int length; + + if (device->port.membase || device->port.iobase) + return -ENODEV; + + /* Enable Early MXC UART Clock */ + clk_enable(device->clk); + + port->uartclk = 5600000; + port->iotype = UPIO_MEM; + port->membase = ioremap(port->mapbase, SZ_4K); + + if (options) { + device->baud = simple_strtoul(options, NULL, 0); + length = min(strlen(options), sizeof(device->options)); + strncpy(device->options, options, length); + } else { + device->baud = probe_baud(port); + snprintf(device->options, sizeof(device->options), "%u", + device->baud); + } + printk(KERN_INFO + "MXC_Early serial console at MMIO 0x%x (options '%s')\n", + port->mapbase, device->options); + return 0; +} + +static struct console mxc_early_uart_console __initdata = { + .name = "ttymxc", + .write = early_mxcuart_console_write, + .setup = mxc_early_uart_setup, + .flags = CON_PRINTBUFFER | CON_BOOT, + .index = -1, +}; + +int __init mxc_early_serial_console_init(unsigned long base, struct clk *clk) +{ + mxc_early_device.clk = clk; + mxc_early_device.port.mapbase = base; + + register_console(&mxc_early_uart_console); + return 0; +} + +int __init mxc_early_uart_console_disable(void) +{ + struct mxc_early_uart_device *device = &mxc_early_device; + struct uart_port *port = &device->port; + + if (mxc_early_uart_console.index >= 0) { + iounmap(port->membase); + clk_disable(device->clk); + clk_put(device->clk); + } + return 0; +} +late_initcall(mxc_early_uart_console_disable); diff --git a/drivers/serial/mxc_uart_reg.h b/drivers/serial/mxc_uart_reg.h new file mode 100644 index 000000000000..dcdcbdb476c1 --- /dev/null +++ b/drivers/serial/mxc_uart_reg.h @@ -0,0 +1,128 @@ +/* + * Copyright 2004-2010 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 + */ +#ifndef __MXC_UART_REG_H__ +#define __MXC_UART_REG_H__ + +/* Address offsets of the UART registers */ +#define MXC_UARTURXD 0x000 /* Receive reg */ +#define MXC_UARTUTXD 0x040 /* Transmitter reg */ +#define MXC_UARTUCR1 0x080 /* Control reg 1 */ +#define MXC_UARTUCR2 0x084 /* Control reg 2 */ +#define MXC_UARTUCR3 0x088 /* Control reg 3 */ +#define MXC_UARTUCR4 0x08C /* Control reg 4 */ +#define MXC_UARTUFCR 0x090 /* FIFO control reg */ +#define MXC_UARTUSR1 0x094 /* Status reg 1 */ +#define MXC_UARTUSR2 0x098 /* Status reg 2 */ +#define MXC_UARTUESC 0x09C /* Escape character reg */ +#define MXC_UARTUTIM 0x0A0 /* Escape timer reg */ +#define MXC_UARTUBIR 0x0A4 /* BRM incremental reg */ +#define MXC_UARTUBMR 0x0A8 /* BRM modulator reg */ +#define MXC_UARTUBRC 0x0AC /* Baud rate count reg */ +#define MXC_UARTONEMS 0x0B0 /* One millisecond reg */ +#define MXC_UARTUTS 0x0B4 /* Test reg */ + +/* Bit definations of UCR1 */ +#define MXC_UARTUCR1_ADEN 0x8000 +#define MXC_UARTUCR1_ADBR 0x4000 +#define MXC_UARTUCR1_TRDYEN 0x2000 +#define MXC_UARTUCR1_IDEN 0x1000 +#define MXC_UARTUCR1_RRDYEN 0x0200 +#define MXC_UARTUCR1_RXDMAEN 0x0100 +#define MXC_UARTUCR1_IREN 0x0080 +#define MXC_UARTUCR1_TXMPTYEN 0x0040 +#define MXC_UARTUCR1_RTSDEN 0x0020 +#define MXC_UARTUCR1_SNDBRK 0x0010 +#define MXC_UARTUCR1_TXDMAEN 0x0008 +#define MXC_UARTUCR1_ATDMAEN 0x0004 +#define MXC_UARTUCR1_DOZE 0x0002 +#define MXC_UARTUCR1_UARTEN 0x0001 + +/* Bit definations of UCR2 */ +#define MXC_UARTUCR2_ESCI 0x8000 +#define MXC_UARTUCR2_IRTS 0x4000 +#define MXC_UARTUCR2_CTSC 0x2000 +#define MXC_UARTUCR2_CTS 0x1000 +#define MXC_UARTUCR2_PREN 0x0100 +#define MXC_UARTUCR2_PROE 0x0080 +#define MXC_UARTUCR2_STPB 0x0040 +#define MXC_UARTUCR2_WS 0x0020 +#define MXC_UARTUCR2_RTSEN 0x0010 +#define MXC_UARTUCR2_ATEN 0x0008 +#define MXC_UARTUCR2_TXEN 0x0004 +#define MXC_UARTUCR2_RXEN 0x0002 +#define MXC_UARTUCR2_SRST 0x0001 + +/* Bit definations of UCR3 */ +#define MXC_UARTUCR3_DTREN 0x2000 +#define MXC_UARTUCR3_PARERREN 0x1000 +#define MXC_UARTUCR3_FRAERREN 0x0800 +#define MXC_UARTUCR3_DSR 0x0400 +#define MXC_UARTUCR3_DCD 0x0200 +#define MXC_UARTUCR3_RI 0x0100 +#define MXC_UARTUCR3_RXDSEN 0x0040 +#define MXC_UARTUCR3_AWAKEN 0x0010 +#define MXC_UARTUCR3_DTRDEN 0x0008 +#define MXC_UARTUCR3_RXDMUXSEL 0x0004 +#define MXC_UARTUCR3_INVT 0x0002 + +/* Bit definations of UCR4 */ +#define MXC_UARTUCR4_CTSTL_OFFSET 10 +#define MXC_UARTUCR4_CTSTL_MASK (0x3F << 10) +#define MXC_UARTUCR4_INVR 0x0200 +#define MXC_UARTUCR4_ENIRI 0x0100 +#define MXC_UARTUCR4_REF16 0x0040 +#define MXC_UARTUCR4_IRSC 0x0020 +#define MXC_UARTUCR4_TCEN 0x0008 +#define MXC_UARTUCR4_OREN 0x0002 +#define MXC_UARTUCR4_DREN 0x0001 + +/* Bit definations of UFCR */ +#define MXC_UARTUFCR_RFDIV 0x0200 /* Ref freq div is set to 2 */ +#define MXC_UARTUFCR_RFDIV_OFFSET 7 +#define MXC_UARTUFCR_RFDIV_MASK (0x7 << 7) +#define MXC_UARTUFCR_TXTL_OFFSET 10 +#define MXC_UARTUFCR_DCEDTE 0x0040 + +/* Bit definations of URXD */ +#define MXC_UARTURXD_ERR 0x4000 +#define MXC_UARTURXD_OVRRUN 0x2000 +#define MXC_UARTURXD_FRMERR 0x1000 +#define MXC_UARTURXD_BRK 0x0800 +#define MXC_UARTURXD_PRERR 0x0400 + +/* Bit definations of USR1 */ +#define MXC_UARTUSR1_PARITYERR 0x8000 +#define MXC_UARTUSR1_RTSS 0x4000 +#define MXC_UARTUSR1_TRDY 0x2000 +#define MXC_UARTUSR1_RTSD 0x1000 +#define MXC_UARTUSR1_FRAMERR 0x0400 +#define MXC_UARTUSR1_RRDY 0x0200 +#define MXC_UARTUSR1_AGTIM 0x0100 +#define MXC_UARTUSR1_DTRD 0x0080 +#define MXC_UARTUSR1_AWAKE 0x0010 + +/* Bit definations of USR2 */ +#define MXC_UARTUSR2_TXFE 0x4000 +#define MXC_UARTUSR2_IDLE 0x1000 +#define MXC_UARTUSR2_RIDELT 0x0400 +#define MXC_UARTUSR2_RIIN 0x0200 +#define MXC_UARTUSR2_DCDDELT 0x0040 +#define MXC_UARTUSR2_DCDIN 0x0020 +#define MXC_UARTUSR2_TXDC 0x0008 +#define MXC_UARTUSR2_ORE 0x0002 +#define MXC_UARTUSR2_RDR 0x0001 + +/* Bit definations of UTS */ +#define MXC_UARTUTS_LOOP 0x1000 + +#endif /* __MXC_UART_REG_H__ */ diff --git a/drivers/serial/mxs-auart.c b/drivers/serial/mxs-auart.c new file mode 100644 index 000000000000..be79f0e8bf11 --- /dev/null +++ b/drivers/serial/mxs-auart.c @@ -0,0 +1,1108 @@ +/* + * Freescale STMP37XX/STMP378X Application UART driver + * + * Author: dmitry pervushin <dimka@embeddedalley.com> + * + * Copyright 2008-2010 Freescale Semiconductor, Inc. + * Copyright 2008 Embedded Alley Solutions, 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 + */ +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/console.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/wait.h> +#include <linux/tty.h> +#include <linux/tty_driver.h> +#include <linux/tty_flip.h> +#include <linux/serial.h> +#include <linux/serial_core.h> +#include <linux/platform_device.h> +#include <linux/device.h> +#include <linux/clk.h> +#include <linux/cpufreq.h> +#include <linux/dma-mapping.h> +#include <linux/delay.h> +#include <linux/uaccess.h> + +#include <asm/cacheflush.h> + +#include <mach/hardware.h> +#include <mach/device.h> +#include <mach/dmaengine.h> + +#include "regs-uartapp.h" + +#define MXS_AUART_MAJOR 242 +#define MXS_AUART_RX_THRESHOLD 16 + +static struct uart_driver auart_driver; + +struct mxs_auart_port { + struct uart_port port; + + unsigned int flags; +#define MXS_AUART_PORT_OPEN 0x80000000 +#define MXS_AUART_PORT_DMA_MODE 0x80000000 + unsigned int ctrl; + + unsigned int irq[3]; + + struct clk *clk; + struct device *dev; + unsigned int dma_rx_chan; + unsigned int dma_tx_chan; + unsigned int dma_rx_buffer_size; + struct list_head rx_done; + struct list_head free; + struct mxs_dma_desc *tx; + struct tasklet_struct rx_task; +}; + +static void mxs_auart_stop_tx(struct uart_port *u); +static void mxs_auart_submit_tx(struct mxs_auart_port *s, int size); +static void mxs_auart_submit_rx(struct mxs_auart_port *s); + +static inline struct mxs_auart_port *to_auart_port(struct uart_port *u) +{ + return container_of(u, struct mxs_auart_port, port); +} + +static inline void mxs_auart_tx_chars(struct mxs_auart_port *s) +{ + struct circ_buf *xmit = &s->port.state->xmit; + + if (s->flags & MXS_AUART_PORT_DMA_MODE) { + int i = 0, size; + char *buffer = s->tx->buffer; + + if (mxs_dma_desc_pending(s->tx)) + return; + while (!uart_circ_empty(xmit) && !uart_tx_stopped(&s->port)) { + if (i >= PAGE_SIZE) + break; + if (s->port.x_char) { + buffer[i++] = s->port.x_char; + s->port.x_char = 0; + continue; + } + size = min_t(u32, PAGE_SIZE - i, + CIRC_CNT_TO_END(xmit->head, + xmit->tail, + UART_XMIT_SIZE)); + memcpy(buffer + i, xmit->buf + xmit->tail, size); + xmit->tail = (xmit->tail + size) & (UART_XMIT_SIZE - 1); + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&s->port); + i += size; + } + if (i) + mxs_auart_submit_tx(s, i); + else { + if (uart_tx_stopped(&s->port)) + mxs_auart_stop_tx(&s->port); + } + return; + } + + while (!(__raw_readl(s->port.membase + HW_UARTAPP_STAT) & + BM_UARTAPP_STAT_TXFF)) { + if (s->port.x_char) { + __raw_writel(s->port.x_char, + s->port.membase + HW_UARTAPP_DATA); + s->port.x_char = 0; + continue; + } + if (!uart_circ_empty(xmit) && !uart_tx_stopped(&s->port)) { + __raw_writel(xmit->buf[xmit->tail], + s->port.membase + HW_UARTAPP_DATA); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&s->port); + } else + break; + } + if (uart_circ_empty(&(s->port.state->xmit))) + __raw_writel(BM_UARTAPP_INTR_TXIEN, + s->port.membase + HW_UARTAPP_INTR_CLR); + else + __raw_writel(BM_UARTAPP_INTR_TXIEN, + s->port.membase + HW_UARTAPP_INTR_SET); + + if (uart_tx_stopped(&s->port)) + mxs_auart_stop_tx(&s->port); +} + +static inline unsigned int +mxs_auart_rx_char(struct mxs_auart_port *s, unsigned int stat, u8 c) +{ + int flag; + + flag = TTY_NORMAL; + if (stat & BM_UARTAPP_STAT_BERR) { + stat &= ~BM_UARTAPP_STAT_BERR; + s->port.icount.brk++; + if (uart_handle_break(&s->port)) + return stat; + flag = TTY_BREAK; + } else if (stat & BM_UARTAPP_STAT_PERR) { + stat &= ~BM_UARTAPP_STAT_PERR; + s->port.icount.parity++; + flag = TTY_PARITY; + } else if (stat & BM_UARTAPP_STAT_FERR) { + stat &= ~BM_UARTAPP_STAT_FERR; + s->port.icount.frame++; + flag = TTY_FRAME; + } + + if (stat & BM_UARTAPP_STAT_OERR) + s->port.icount.overrun++; + + if (uart_handle_sysrq_char(&s->port, c)) + return stat; + + uart_insert_char(&s->port, stat, BM_UARTAPP_STAT_OERR, c, flag); + return stat; +} + +static void mxs_auart_rx_chars(struct mxs_auart_port *s) +{ + u8 c; + struct tty_struct *tty = s->port.state->port.tty; + u32 stat = 0; + + if (s->flags & MXS_AUART_PORT_DMA_MODE) { + int i, count; + struct list_head *p, *q; + LIST_HEAD(list); + struct mxs_dma_desc *pdesc; + mxs_dma_cooked(s->dma_rx_chan, &list); + stat = __raw_readl(s->port.membase + HW_UARTAPP_STAT); + list_for_each_safe(p, q, &list) { + u8 *buffer; + list_del(p); + pdesc = list_entry(p, struct mxs_dma_desc, node); + count = stat & BM_UARTAPP_STAT_RXCOUNT; + buffer = pdesc->buffer; + for (i = 0; i < count; i++) + stat = mxs_auart_rx_char(s, stat, buffer[i]); + list_add(p, &s->free); + stat = __raw_readl(s->port.membase + HW_UARTAPP_STAT); + } + mxs_auart_submit_rx(s); + goto out; + } + for (;;) { + stat = __raw_readl(s->port.membase + HW_UARTAPP_STAT); + if (stat & BM_UARTAPP_STAT_RXFE) + break; + c = __raw_readl(s->port.membase + HW_UARTAPP_DATA); + stat = mxs_auart_rx_char(s, stat, c); + __raw_writel(stat, s->port.membase + HW_UARTAPP_STAT); + } +out: + __raw_writel(stat, s->port.membase + HW_UARTAPP_STAT); + tty_flip_buffer_push(tty); +} + +/* Allocate and initialize rx and tx DMA chains */ +static int mxs_auart_dma_init(struct mxs_auart_port *s) +{ + int ret, i; + struct list_head *p, *n; + struct mxs_dma_desc *pdesc; + + ret = mxs_dma_request(s->dma_rx_chan, s->dev, dev_name(s->dev)); + if (ret) + goto fail_get_dma_rx; + ret = mxs_dma_request(s->dma_tx_chan, s->dev, dev_name(s->dev)); + if (ret) + goto fail_get_dma_tx; + ret = -ENOMEM; + INIT_LIST_HEAD(&s->rx_done); + INIT_LIST_HEAD(&s->free); + s->tx = NULL; + + + for (i = 0; i < 5; i++) { + pdesc = mxs_dma_alloc_desc(); + if (pdesc == NULL || IS_ERR(pdesc)) + goto fail_alloc_desc; + + if (s->tx == NULL) { + pdesc->buffer = dma_alloc_coherent(s->dev, PAGE_SIZE, + &pdesc->cmd.address, + GFP_DMA); + if (pdesc->buffer == NULL) + goto fail_alloc_desc; + s->tx = pdesc; + } else { + pdesc->buffer = dma_alloc_coherent(s->dev, + s->dma_rx_buffer_size, + &pdesc->cmd.address, + GFP_DMA); + if (pdesc->buffer == NULL) + goto fail_alloc_desc; + list_add_tail(&pdesc->node, &s->free); + } + } + /* + Tell DMA to select UART. + Both DMA channels are shared between app UART and IrDA. + Target id of 0 means UART, 1 means IrDA + */ + mxs_dma_set_target(s->dma_rx_chan, 0); + mxs_dma_set_target(s->dma_tx_chan, 0); + + mxs_dma_enable_irq(s->dma_rx_chan, 1); + mxs_dma_enable_irq(s->dma_tx_chan, 1); + + return 0; +fail_alloc_desc: + if (s->tx) { + if (s->tx->buffer) + dma_free_coherent(s->dev, + PAGE_SIZE, + s->tx->buffer, + s->tx->cmd.address); + s->tx->buffer = NULL; + mxs_dma_free_desc(s->tx); + s->tx = NULL; + } + list_for_each_safe(p, n, &s->free) { + list_del(p); + pdesc = list_entry(p, struct mxs_dma_desc, node); + if (pdesc->buffer) + dma_free_coherent(s->dev, + s->dma_rx_buffer_size, + pdesc->buffer, + pdesc->cmd.address); + pdesc->buffer = NULL; + mxs_dma_free_desc(pdesc); + } + mxs_dma_release(s->dma_tx_chan, s->dev); +fail_get_dma_tx: + mxs_dma_release(s->dma_rx_chan, s->dev); +fail_get_dma_rx: + WARN_ON(ret); + return ret; +} + +static void mxs_auart_dma_exit(struct mxs_auart_port *s) +{ + struct list_head *p, *n; + LIST_HEAD(list); + struct mxs_dma_desc *pdesc; + + mxs_dma_enable_irq(s->dma_rx_chan, 0); + mxs_dma_enable_irq(s->dma_tx_chan, 0); + + mxs_dma_disable(s->dma_tx_chan); + mxs_dma_disable(s->dma_rx_chan); + + mxs_dma_get_cooked(s->dma_tx_chan, &list); + mxs_dma_get_cooked(s->dma_rx_chan, &s->free); + + mxs_dma_release(s->dma_tx_chan, s->dev); + mxs_dma_release(s->dma_rx_chan, s->dev); + + if (s->tx) { + if (s->tx->buffer) + dma_free_coherent(s->dev, + PAGE_SIZE, + s->tx->buffer, + s->tx->cmd.address); + s->tx->buffer = NULL; + mxs_dma_free_desc(s->tx); + s->tx = NULL; + } + list_for_each_safe(p, n, &s->free) { + list_del(p); + pdesc = list_entry(p, struct mxs_dma_desc, node); + if (pdesc->buffer) + dma_free_coherent(s->dev, + s->dma_rx_buffer_size, + pdesc->buffer, + pdesc->cmd.address); + pdesc->buffer = NULL; + mxs_dma_free_desc(pdesc); + } +} + +static void mxs_auart_submit_rx(struct mxs_auart_port *s) +{ + int ret; + unsigned int pio_value; + struct list_head *p, *n; + struct mxs_dma_desc *pdesc; + + pio_value = BM_UARTAPP_CTRL0_RXTO_ENABLE | + BF_UARTAPP_CTRL0_RXTIMEOUT(0x80) | + BF_UARTAPP_CTRL0_XFER_COUNT(s->dma_rx_buffer_size); + + list_for_each_safe(p, n, &s->free) { + list_del(p); + pdesc = list_entry(p, struct mxs_dma_desc, node); + pdesc->cmd.cmd.bits.bytes = s->dma_rx_buffer_size; + pdesc->cmd.cmd.bits.terminate_flush = 1; + pdesc->cmd.cmd.bits.pio_words = 1; + pdesc->cmd.cmd.bits.wait4end = 1; + pdesc->cmd.cmd.bits.dec_sem = 1; + pdesc->cmd.cmd.bits.irq = 1; + pdesc->cmd.cmd.bits.chain = 1; + pdesc->cmd.cmd.bits.command = DMA_WRITE; + pdesc->cmd.pio_words[0] = pio_value; + ret = mxs_dma_desc_append(s->dma_rx_chan, pdesc); + if (ret) + pr_info("%s append dma desc, %d\n", __func__, ret); + } + ret = mxs_dma_enable(s->dma_rx_chan); + if (ret) + pr_info("%s enable dma desc, %d\n", __func__, ret); +} + +static irqreturn_t mxs_auart_irq_dma_rx(int irq, void *context) +{ + struct mxs_auart_port *s = context; + + mxs_dma_ack_irq(s->dma_rx_chan); + mxs_auart_rx_chars(s); + return IRQ_HANDLED; +} + +static void mxs_auart_submit_tx(struct mxs_auart_port *s, int size) +{ + int ret; + struct mxs_dma_desc *d = s->tx; + + d->cmd.pio_words[0] = BF_UARTAPP_CTRL1_XFER_COUNT(size); + d->cmd.cmd.bits.bytes = size; + d->cmd.cmd.bits.pio_words = 1; + d->cmd.cmd.bits.wait4end = 1; + d->cmd.cmd.bits.dec_sem = 1; + d->cmd.cmd.bits.irq = 1; + d->cmd.cmd.bits.command = DMA_READ; + ret = mxs_dma_desc_append(s->dma_tx_chan, s->tx); + if (ret) + pr_info("append dma desc, %d\n", ret); + + ret = mxs_dma_enable(s->dma_tx_chan); + if (ret) + pr_info("enable dma desc, %d\n", ret); +} + +static irqreturn_t mxs_auart_irq_dma_tx(int irq, void *context) +{ + struct mxs_auart_port *s = context; + + LIST_HEAD(list); + mxs_dma_ack_irq(s->dma_tx_chan); + mxs_dma_cooked(s->dma_tx_chan, &list); + mxs_auart_tx_chars(s); + return IRQ_HANDLED; +} + +static int mxs_auart_request_port(struct uart_port *u) +{ + struct mxs_auart_port *s = to_auart_port(u); + + if (!request_mem_region((u32)u->mapbase, SZ_4K, dev_name(s->dev))) + return -EBUSY; + return 0; + +} + +static int mxs_auart_verify_port(struct uart_port *u, + struct serial_struct *ser) +{ + if (u->type != PORT_UNKNOWN && u->type != PORT_IMX) + return -EINVAL; + return 0; +} + +static void mxs_auart_config_port(struct uart_port *u, int flags) +{ +} + +static const char *mxs_auart_type(struct uart_port *u) +{ + struct mxs_auart_port *s = to_auart_port(u); + + return dev_name(s->dev); +} + +static void mxs_auart_release_port(struct uart_port *u) +{ + release_mem_region(u->mapbase, SZ_4K); +} + +static void mxs_auart_set_mctrl(struct uart_port *u, unsigned mctrl) +{ + struct mxs_auart_port *s = to_auart_port(u); + + u32 ctrl = __raw_readl(u->membase + HW_UARTAPP_CTRL2); + + ctrl &= ~BM_UARTAPP_CTRL2_RTS; + if (mctrl & TIOCM_RTS) + ctrl |= BM_UARTAPP_CTRL2_RTS; + s->ctrl = mctrl; + __raw_writel(ctrl, u->membase + HW_UARTAPP_CTRL2); +} + +static u32 mxs_auart_get_mctrl(struct uart_port *u) +{ + struct mxs_auart_port *s = to_auart_port(u); + u32 stat = __raw_readl(u->membase + HW_UARTAPP_STAT); + int ctrl2 = __raw_readl(u->membase + HW_UARTAPP_CTRL2); + u32 mctrl = s->ctrl; + + mctrl &= ~TIOCM_CTS; + if (stat & BM_UARTAPP_STAT_CTS) + mctrl |= TIOCM_CTS; + + if (ctrl2 & BM_UARTAPP_CTRL2_RTS) + mctrl |= TIOCM_RTS; + + return mctrl; +} + +static void mxs_auart_settermios(struct uart_port *u, + struct ktermios *termios, + struct ktermios *old) +{ + u32 bm, ctrl, ctrl2, div; + unsigned int cflag, baud; + + if (termios == NULL) { + printk(KERN_ERR "Empty ktermios setting:!\n"); + return; + } + + cflag = termios->c_cflag; + + ctrl = BM_UARTAPP_LINECTRL_FEN; + ctrl2 = __raw_readl(u->membase + HW_UARTAPP_CTRL2); + + /* byte size */ + switch (cflag & CSIZE) { + case CS5: + bm = 0; + break; + case CS6: + bm = 1; + break; + case CS7: + bm = 2; + break; + case CS8: + bm = 3; + break; + default: + return; + } + + ctrl |= BF_UARTAPP_LINECTRL_WLEN(bm); + + /* parity */ + if (cflag & PARENB) { + ctrl |= BM_UARTAPP_LINECTRL_PEN; + if ((cflag & PARODD) == 0) + ctrl |= BM_UARTAPP_LINECTRL_EPS; + } + + /* figure out the stop bits requested */ + if (cflag & CSTOPB) + ctrl |= BM_UARTAPP_LINECTRL_STP2; + + /* figure out the hardware flow control settings */ + if (cflag & CRTSCTS) + ctrl2 |= BM_UARTAPP_CTRL2_CTSEN /* | BM_UARTAPP_CTRL2_RTSEN */ ; + else + ctrl2 &= ~BM_UARTAPP_CTRL2_CTSEN; + + /* set baud rate */ + baud = uart_get_baud_rate(u, termios, old, 0, u->uartclk); + div = u->uartclk * 32 / baud; + ctrl |= BF_UARTAPP_LINECTRL_BAUD_DIVFRAC(div & 0x3F); + ctrl |= BF_UARTAPP_LINECTRL_BAUD_DIVINT(div >> 6); + + if ((cflag & CREAD) != 0) + ctrl2 |= BM_UARTAPP_CTRL2_RXE; + + __raw_writel(ctrl, u->membase + HW_UARTAPP_LINECTRL); + __raw_writel(ctrl2, u->membase + HW_UARTAPP_CTRL2); +} + +static irqreturn_t mxs_auart_irq_handle(int irq, void *context) +{ + u32 istatus, istat; + struct mxs_auart_port *s = context; + u32 stat = __raw_readl(s->port.membase + HW_UARTAPP_STAT); + + istatus = istat = __raw_readl(s->port.membase + HW_UARTAPP_INTR); + + if (istat & BM_UARTAPP_INTR_CTSMIS) { + uart_handle_cts_change(&s->port, stat & BM_UARTAPP_STAT_CTS); + __raw_writel(BM_UARTAPP_INTR_CTSMIS, + s->port.membase + HW_UARTAPP_INTR_CLR); + istat &= ~BM_UARTAPP_INTR_CTSMIS; + } + if (istat & (BM_UARTAPP_INTR_RTIS | BM_UARTAPP_INTR_RXIS)) { + mxs_auart_rx_chars(s); + istat &= ~(BM_UARTAPP_INTR_RTIS | BM_UARTAPP_INTR_RXIS); + } + + if (istat & BM_UARTAPP_INTR_TXIS) { + mxs_auart_tx_chars(s); + istat &= ~BM_UARTAPP_INTR_TXIS; + } + /* modem status interrupt bits are undefined + after reset,and the hardware do not support + DSRMIS,DCDMIS and RIMIS bit,so we should ingore + them when they are pending. */ + if (istat & (BM_UARTAPP_INTR_ABDIS + | BM_UARTAPP_INTR_OEIS + | BM_UARTAPP_INTR_BEIS + | BM_UARTAPP_INTR_PEIS + | BM_UARTAPP_INTR_FEIS + | BM_UARTAPP_INTR_RTIS + | BM_UARTAPP_INTR_TXIS + | BM_UARTAPP_INTR_RXIS + | BM_UARTAPP_INTR_CTSMIS)) { + dev_info(s->dev, "Unhandled status %x\n", istat); + } + __raw_writel(istatus & (BM_UARTAPP_INTR_ABDIS + | BM_UARTAPP_INTR_OEIS + | BM_UARTAPP_INTR_BEIS + | BM_UARTAPP_INTR_PEIS + | BM_UARTAPP_INTR_FEIS + | BM_UARTAPP_INTR_RTIS + | BM_UARTAPP_INTR_TXIS + | BM_UARTAPP_INTR_RXIS + | BM_UARTAPP_INTR_DSRMIS + | BM_UARTAPP_INTR_DCDMIS + | BM_UARTAPP_INTR_CTSMIS + | BM_UARTAPP_INTR_RIMIS), + s->port.membase + HW_UARTAPP_INTR_CLR); + + return IRQ_HANDLED; +} + +static int mxs_auart_free_irqs(struct mxs_auart_port *s) +{ + int irqn = 0; + + for (irqn = 0; irqn < ARRAY_SIZE(s->irq); irqn++) + free_irq(s->irq[irqn], s); + return 0; +} + +static int mxs_auart_request_irqs(struct mxs_auart_port *s) +{ + int err = 0; + + /* + * order counts. resources should be listed in the same order + */ + irq_handler_t handlers[] = { + mxs_auart_irq_handle, + mxs_auart_irq_dma_rx, + mxs_auart_irq_dma_tx, + }; + char *handlers_names[] = { + "auart internal", + "auart dma rx", + "auart dma tx", + }; + int irqn; + + for (irqn = 0; irqn < ARRAY_SIZE(handlers); irqn++) { + err = request_irq(s->irq[irqn], handlers[irqn], + 0, handlers_names[irqn], s); + if (err) + goto out; + } + return 0; +out: + mxs_auart_free_irqs(s); + return err; +} + +static inline void mxs_auart_reset(struct uart_port *u) +{ + int i; + unsigned int reg; + + __raw_writel(BM_UARTAPP_CTRL0_SFTRST, + u->membase + HW_UARTAPP_CTRL0_CLR); + + for (i = 0; i < 10000; i++) { + reg = __raw_readl(u->membase + HW_UARTAPP_CTRL0); + if (!(reg & BM_UARTAPP_CTRL0_SFTRST)) + break; + udelay(3); + } + + __raw_writel(BM_UARTAPP_CTRL0_CLKGATE, + u->membase + HW_UARTAPP_CTRL0_CLR); +} + +static int mxs_auart_startup(struct uart_port *u) +{ + struct mxs_auart_port *s = to_auart_port(u); + + mxs_auart_reset(u); + + __raw_writel(BM_UARTAPP_CTRL2_UARTEN, + s->port.membase + HW_UARTAPP_CTRL2_SET); + + /* Enable the Application UART DMA bits. */ + if (s->flags & MXS_AUART_PORT_DMA_MODE) { + int ret; + ret = mxs_auart_dma_init(s); + if (ret) { + __raw_writel(BM_UARTAPP_CTRL2_UARTEN, + s->port.membase + HW_UARTAPP_CTRL2_CLR); + return ret; + } + __raw_writel(BM_UARTAPP_CTRL2_TXDMAE | BM_UARTAPP_CTRL2_RXDMAE + | BM_UARTAPP_CTRL2_DMAONERR, + s->port.membase + HW_UARTAPP_CTRL2_SET); + /* clear any pending interrupts */ + __raw_writel(0, s->port.membase + HW_UARTAPP_INTR); + + /* reset all dma channels */ + mxs_dma_reset(s->dma_tx_chan); + mxs_dma_reset(s->dma_rx_chan); + } else + __raw_writel(BM_UARTAPP_INTR_RXIEN | BM_UARTAPP_INTR_RTIEN, + s->port.membase + HW_UARTAPP_INTR); + + __raw_writel(BM_UARTAPP_INTR_CTSMIEN, + s->port.membase + HW_UARTAPP_INTR_SET); + + /* + * Enable fifo so all four bytes of a DMA word are written to + * output (otherwise, only the LSB is written, ie. 1 in 4 bytes) + */ + __raw_writel(BM_UARTAPP_LINECTRL_FEN, + s->port.membase + HW_UARTAPP_LINECTRL_SET); + + if (s->flags & MXS_AUART_PORT_DMA_MODE) + mxs_auart_submit_rx(s); + return mxs_auart_request_irqs(s); +} + +static void mxs_auart_shutdown(struct uart_port *u) +{ + struct mxs_auart_port *s = to_auart_port(u); + + __raw_writel(BM_UARTAPP_CTRL0_SFTRST, + s->port.membase + HW_UARTAPP_CTRL0_SET); + + if (s->flags & MXS_AUART_PORT_DMA_MODE) + mxs_auart_dma_exit(s); + else + __raw_writel(BM_UARTAPP_INTR_RXIEN | BM_UARTAPP_INTR_RTIEN | + BM_UARTAPP_INTR_CTSMIEN, + s->port.membase + HW_UARTAPP_INTR_CLR); + mxs_auart_free_irqs(s); +} + +static unsigned int mxs_auart_tx_empty(struct uart_port *u) +{ + struct mxs_auart_port *s = to_auart_port(u); + + if (s->flags & MXS_AUART_PORT_DMA_MODE) + return mxs_dma_desc_pending(s->tx) ? 0 : TIOCSER_TEMT; + + if (__raw_readl(u->membase + HW_UARTAPP_STAT) & + BM_UARTAPP_STAT_TXFE) + return TIOCSER_TEMT; + else + return 0; +} + +static void mxs_auart_start_tx(struct uart_port *u) +{ + struct mxs_auart_port *s = to_auart_port(u); + + /* enable transmitter */ + __raw_writel(BM_UARTAPP_CTRL2_TXE, u->membase + HW_UARTAPP_CTRL2_SET); + + mxs_auart_tx_chars(s); +} + +static void mxs_auart_stop_tx(struct uart_port *u) +{ + __raw_writel(BM_UARTAPP_CTRL2_TXE, u->membase + HW_UARTAPP_CTRL2_CLR); +} + +static void mxs_auart_stop_rx(struct uart_port *u) +{ + __raw_writel(BM_UARTAPP_CTRL2_RXE, u->membase + HW_UARTAPP_CTRL2_CLR); +} + +static void mxs_auart_break_ctl(struct uart_port *u, int ctl) +{ + if (ctl) + __raw_writel(BM_UARTAPP_LINECTRL_BRK, + u->membase + HW_UARTAPP_LINECTRL_SET); + else + __raw_writel(BM_UARTAPP_LINECTRL_BRK, + u->membase + HW_UARTAPP_LINECTRL_CLR); +} + +static void mxs_auart_enable_ms(struct uart_port *port) +{ + /* just empty */ +} + +static struct uart_ops mxs_auart_ops = { + .tx_empty = mxs_auart_tx_empty, + .start_tx = mxs_auart_start_tx, + .stop_tx = mxs_auart_stop_tx, + .stop_rx = mxs_auart_stop_rx, + .enable_ms = mxs_auart_enable_ms, + .break_ctl = mxs_auart_break_ctl, + .set_mctrl = mxs_auart_set_mctrl, + .get_mctrl = mxs_auart_get_mctrl, + .startup = mxs_auart_startup, + .shutdown = mxs_auart_shutdown, + .set_termios = mxs_auart_settermios, + .type = mxs_auart_type, + .release_port = mxs_auart_release_port, + .request_port = mxs_auart_request_port, + .config_port = mxs_auart_config_port, + .verify_port = mxs_auart_verify_port, +}; +#ifdef CONFIG_SERIAL_MXS_AUART_CONSOLE +static struct mxs_auart_port auart_port[CONFIG_MXS_AUART_PORTS] = {}; + +static void +auart_console_write(struct console *co, const char *s, unsigned int count) +{ + struct uart_port *port; + unsigned int status, old_cr; + int i; + + if (co->index > CONFIG_MXS_AUART_PORTS || co->index < 0) + return; + + port = &auart_port[co->index].port; + + /* First save the CR then disable the interrupts */ + old_cr = __raw_readl(port->membase + HW_UARTAPP_CTRL2); + __raw_writel(BM_UARTAPP_CTRL2_UARTEN | BM_UARTAPP_CTRL2_TXE, + port->membase + HW_UARTAPP_CTRL2_SET); + + /* Now, do each character */ + for (i = 0; i < count; i++) { + do { + status = __raw_readl(port->membase + HW_UARTAPP_STAT); + } while (status & BM_UARTAPP_STAT_TXFF); + + __raw_writel(s[i], port->membase + HW_UARTAPP_DATA); + if (s[i] == '\n') { + do { + status = __raw_readl(port->membase + + HW_UARTAPP_STAT); + } while (status & BM_UARTAPP_STAT_TXFF); + __raw_writel('\r', port->membase + HW_UARTAPP_DATA); + } + } + + /* + * Finally, wait for transmitter to become empty + * and restore the TCR + */ + do { + status = __raw_readl(port->membase + HW_UARTAPP_STAT); + } while (status & BM_UARTAPP_STAT_BUSY); + __raw_writel(old_cr, port->membase + HW_UARTAPP_CTRL2); +} + +static void __init +auart_console_get_options(struct uart_port *port, int *baud, + int *parity, int *bits) +{ + if (__raw_readl(port->membase + HW_UARTAPP_CTRL2) + & BM_UARTAPP_CTRL2_UARTEN) { + unsigned int lcr_h, quot; + lcr_h = __raw_readl(port->membase + HW_UARTAPP_LINECTRL); + + *parity = 'n'; + if (lcr_h & BM_UARTAPP_LINECTRL_PEN) { + if (lcr_h & BM_UARTAPP_LINECTRL_EPS) + *parity = 'e'; + else + *parity = 'o'; + } + + if ((lcr_h & BM_UARTAPP_LINECTRL_WLEN) + == BF_UARTAPP_LINECTRL_WLEN(2)) + *bits = 7; + else + *bits = 8; + + quot = (((__raw_readl(port->membase + HW_UARTAPP_LINECTRL) + & BM_UARTAPP_LINECTRL_BAUD_DIVINT)) + >> (BP_UARTAPP_LINECTRL_BAUD_DIVINT - 6)) + | (((__raw_readl(port->membase + HW_UARTAPP_LINECTRL) + & BM_UARTAPP_LINECTRL_BAUD_DIVFRAC)) + >> BP_UARTAPP_LINECTRL_BAUD_DIVFRAC); + if (quot == 0) + quot = 1; + *baud = (port->uartclk << 2) / quot; + } +} + +static int __init auart_console_setup(struct console *co, char *options) +{ + struct mxs_auart_port *port; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index > CONFIG_MXS_AUART_PORTS || co->index < 0) + return -EINVAL; + + port = &auart_port[co->index].port; + + if (port->port.membase == 0) { + if (cpu_is_mx23()) { + if (co->index == 1) { + port->port.membase = IO_ADDRESS(0x8006C000); + port->port.mapbase = 0x8006C000; + } else { + port->port.membase = IO_ADDRESS(0x8006E000); + port->port.mapbase = 0x8006E000; + } + } + + port->port.fifosize = 16; + port->port.ops = &mxs_auart_ops; + port->port.flags = ASYNC_BOOT_AUTOCONF; + port->port.line = 0; + } + mxs_auart_reset(port); + + __raw_writel(BM_UARTAPP_CTRL2_UARTEN, + port->port.membase + HW_UARTAPP_CTRL2_SET); + + if (port->clk == NULL || IS_ERR(port->clk)) { + port->clk = clk_get(NULL, "uart"); + if (port->clk == NULL || IS_ERR(port->clk)) + return -ENODEV; + port->port.uartclk = clk_get_rate(port->clk); + } + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + auart_console_get_options(port, &baud, &parity, &bits); + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct console auart_console = { + .name = "ttySP", + .write = auart_console_write, + .device = uart_console_device, + .setup = auart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &auart_driver, +}; + +#ifdef CONFIG_MXS_EARLY_CONSOLE +static int __init auart_console_init(void) +{ + register_console(&auart_console); + return 0; +} + +console_initcall(auart_console_init); +#endif + +#endif +static struct uart_driver auart_driver = { + .owner = THIS_MODULE, + .driver_name = "auart", + .dev_name = "ttySP", + .major = MXS_AUART_MAJOR, + .minor = 0, + .nr = CONFIG_MXS_AUART_PORTS, +#ifdef CONFIG_SERIAL_MXS_AUART_CONSOLE + .cons = &auart_console, +#endif +}; + +static int __devinit mxs_auart_probe(struct platform_device *pdev) +{ + struct mxs_auart_plat_data *plat; + struct mxs_auart_port *s; + u32 version; + int i, ret = 0; + struct resource *r; + + s = kzalloc(sizeof(struct mxs_auart_port), GFP_KERNEL); + if (!s) { + ret = -ENOMEM; + goto out; + } + + plat = pdev->dev.platform_data; + if (plat == NULL) { + ret = -ENOMEM; + goto out_free; + } + + if (plat && plat->clk) + s->clk = clk_get(NULL, plat->clk); + else + s->clk = clk_get(NULL, "uart"); + if (IS_ERR(s->clk)) { + ret = PTR_ERR(s->clk); + goto out_free; + } + + clk_enable(s->clk); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + ret = -ENXIO; + goto out_free_clk; + } + s->port.mapbase = r->start; + s->port.membase = (void __iomem *)IO_ADDRESS(r->start); + s->port.ops = &mxs_auart_ops; + s->port.iotype = UPIO_MEM; + s->port.line = pdev->id < 0 ? 0 : pdev->id; + s->port.fifosize = plat->fifo_size; + s->port.timeout = plat->timeout ? plat->timeout : (HZ / 10); + s->port.uartclk = clk_get_rate(s->clk); + s->port.type = PORT_IMX; + s->port.dev = s->dev = get_device(&pdev->dev); + + s->flags = plat->dma_mode ? MXS_AUART_PORT_DMA_MODE : 0; + s->ctrl = 0; + s->dma_rx_buffer_size = plat->dma_rx_buffer_size; + + for (i = 0; i < ARRAY_SIZE(s->irq); i++) { + s->irq[i] = platform_get_irq(pdev, i); + if (s->irq[i] < 0) { + ret = s->irq[i]; + goto out_free_clk; + } + } + s->port.irq = s->irq[0]; + + r = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!r) { + ret = -ENXIO; + goto out_free_clk; + } + s->dma_rx_chan = r->start; + + r = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (!r) { + ret = -ENXIO; + goto out_free_clk; + } + s->dma_tx_chan = r->start; + + platform_set_drvdata(pdev, s); + + device_init_wakeup(&pdev->dev, 1); + +#ifdef CONFIG_SERIAL_MXS_AUART_CONSOLE + memcpy(&auart_port[pdev->id], s, sizeof(struct mxs_auart_port)); +#endif + + ret = uart_add_one_port(&auart_driver, &s->port); + if (ret) + goto out_free_clk; + + version = __raw_readl(s->port.membase + HW_UARTAPP_VERSION); + printk(KERN_INFO "Found APPUART %d.%d.%d\n", + (version >> 24) & 0xFF, + (version >> 16) & 0xFF, version & 0xFFFF); + return 0; + +out_free_clk: + if (!IS_ERR(s->clk)) + clk_put(s->clk); +out_free: + kfree(s); +out: + return ret; +} + +static int __devexit mxs_auart_remove(struct platform_device *pdev) +{ + struct mxs_auart_port *s; + + s = platform_get_drvdata(pdev); + if (s) { + put_device(s->dev); + clk_disable(s->clk); + clk_put(s->clk); + uart_remove_one_port(&auart_driver, &s->port); + kfree(s); + } + return 0; +} + +static struct platform_driver mxs_auart_driver = { + .probe = mxs_auart_probe, + .remove = __devexit_p(mxs_auart_remove), + .driver = { + .name = "mxs-auart", + .owner = THIS_MODULE, + }, +}; + +static int __init mxs_auart_init(void) +{ + int r; + + r = uart_register_driver(&auart_driver); + if (r) + goto out; + r = platform_driver_register(&mxs_auart_driver); + if (r) + goto out_err; + return 0; +out_err: + uart_unregister_driver(&auart_driver); +out: + return r; +} + +static void __exit mxs_auart_exit(void) +{ + platform_driver_unregister(&mxs_auart_driver); + uart_unregister_driver(&auart_driver); +} + +module_init(mxs_auart_init) +module_exit(mxs_auart_exit) +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Freescale MXS application uart driver"); diff --git a/drivers/serial/mxs-duart.c b/drivers/serial/mxs-duart.c new file mode 100644 index 000000000000..64e5057344e1 --- /dev/null +++ b/drivers/serial/mxs-duart.c @@ -0,0 +1,803 @@ +/* + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if defined(CONFIG_SERIAL_MXS_DUART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include <linux/module.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/console.h> +#include <linux/sysrq.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial_core.h> +#include <linux/serial.h> +#include <linux/io.h> +#include <linux/irq.h> + +#include <mach/hardware.h> +#include <mach/device.h> +#include "regs-duart.h" + +/* treated as variable unless submitted to open-source */ +#define PORT_DUART 100 +#define SERIAL_DUART_MAJOR 204 +#define SERIAL_DUART_MINOR 16 +#define SERIAL_RX_LIMIT 256 +#define ISR_PASS_LIMIT 256 + +#define DUART_DEVID "DebugUART" + +static int force_cd = 1; +static struct uart_driver duart_drv; + +/* + * We wrap our port structure around the generic uart_port. + */ +struct duart_port { + struct uart_port port; + struct clk *clk; + unsigned int im; /* interrupt mask */ + unsigned int old_status; + int suspended; +}; + +static void duart_stop_tx(struct uart_port *port) +{ + struct duart_port *dp = container_of(port, struct duart_port, port); + + dp->im &= ~BM_UARTDBGIMSC_TXIM; + __raw_writel(dp->im, dp->port.membase + HW_UARTDBGIMSC); +} + +static void duart_start_tx(struct uart_port *port) +{ + struct duart_port *dp = container_of(port, struct duart_port, port); + + dp->im |= BM_UARTDBGIMSC_TXIM; + __raw_writel(dp->im, dp->port.membase + HW_UARTDBGIMSC); +} + +static void duart_stop_rx(struct uart_port *port) +{ + struct duart_port *dp = container_of(port, struct duart_port, port); + + dp->im &= ~(BM_UARTDBGIMSC_OEIM | BM_UARTDBGIMSC_BEIM | + BM_UARTDBGIMSC_PEIM | BM_UARTDBGIMSC_FEIM | + BM_UARTDBGIMSC_RTIM | BM_UARTDBGIMSC_RXIM); + __raw_writel(dp->im, dp->port.membase + HW_UARTDBGIMSC); +} + +static void duart_enable_ms(struct uart_port *port) +{ + struct duart_port *dp = container_of(port, struct duart_port, port); + + dp->im |= BM_UARTDBGIMSC_RIMIM | BM_UARTDBGIMSC_CTSMIM | + BM_UARTDBGIMSC_DCDMIM | BM_UARTDBGIMSC_DSRMIM; + __raw_writel(dp->im, dp->port.membase + HW_UARTDBGIMSC); +} + +static void duart_rx_chars(struct duart_port *dp) +{ + struct tty_struct *tty = dp->port.state->port.tty; + unsigned int status, ch, flag, rsr, max_count = SERIAL_RX_LIMIT; + + status = __raw_readl(dp->port.membase + HW_UARTDBGFR); + while ((status & BM_UARTDBGFR_RXFE) == 0 && max_count--) { + ch = __raw_readl(dp->port.membase + HW_UARTDBGDR); + flag = TTY_NORMAL; + dp->port.icount.rx++; + + /* + * Note that the error handling code is + * out of the main execution path + */ + rsr = __raw_readl(dp->port.membase + HW_UARTDBGRSR_ECR); + if (unlikely(rsr & (BM_UARTDBGRSR_ECR_OE | + BM_UARTDBGRSR_ECR_BE | + BM_UARTDBGRSR_ECR_PE | + BM_UARTDBGRSR_ECR_FE))) { + if (rsr & BM_UARTDBGRSR_ECR_BE) { + rsr &= ~(BM_UARTDBGRSR_ECR_FE | + BM_UARTDBGRSR_ECR_PE); + dp->port.icount.brk++; + if (uart_handle_break(&dp->port)) + goto ignore_char; + } else if (rsr & BM_UARTDBGRSR_ECR_PE) + dp->port.icount.parity++; + else if (rsr & BM_UARTDBGRSR_ECR_FE) + dp->port.icount.frame++; + if (rsr & BM_UARTDBGRSR_ECR_OE) + dp->port.icount.overrun++; + + rsr &= dp->port.read_status_mask; + + if (rsr & BM_UARTDBGRSR_ECR_BE) + flag = TTY_BREAK; + else if (rsr & BM_UARTDBGRSR_ECR_PE) + flag = TTY_PARITY; + else if (rsr & BM_UARTDBGRSR_ECR_FE) + flag = TTY_FRAME; + } + + if (uart_handle_sysrq_char(&dp->port, ch)) + goto ignore_char; + + uart_insert_char(&dp->port, rsr, BM_UARTDBGRSR_ECR_OE, ch, + flag); + +ignore_char: + status = __raw_readl(dp->port.membase + HW_UARTDBGFR); + } + tty_flip_buffer_push(tty); + return; +} + +static void duart_tx_chars(struct duart_port *dp) +{ + int count; + struct circ_buf *xmit = &dp->port.state->xmit; + + if (dp->port.x_char) { + __raw_writel(dp->port.x_char, dp->port.membase + HW_UARTDBGDR); + dp->port.icount.tx++; + dp->port.x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(&dp->port)) { + duart_stop_tx(&dp->port); + return; + } + + count = dp->port.fifosize >> 1; + do { + __raw_writel(xmit->buf[xmit->tail], + dp->port.membase + HW_UARTDBGDR); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + dp->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&dp->port); + + if (uart_circ_empty(xmit)) + duart_stop_tx(&dp->port); +} + +static void duart_modem_status(struct duart_port *dp) +{ + unsigned int status, delta; + status = __raw_readl(dp->port.membase + HW_UARTDBGFR) & + (BM_UARTDBGFR_DCD | BM_UARTDBGFR_DSR | BM_UARTDBGFR_CTS); + + delta = status ^ dp->old_status; + dp->old_status = status; + + if (!delta) + return; + + if (delta & BM_UARTDBGFR_DCD) + uart_handle_dcd_change(&dp->port, status & BM_UARTDBGFR_DCD); + + if (delta & BM_UARTDBGFR_DSR) + dp->port.icount.dsr++; + + if (delta & BM_UARTDBGFR_CTS) + uart_handle_cts_change(&dp->port, status & BM_UARTDBGFR_CTS); + + wake_up_interruptible(&dp->port.state->port.delta_msr_wait); +} + +static irqreturn_t duart_int(int irq, void *dev_id) +{ + int handled = 0; + struct duart_port *dp = dev_id; + unsigned int status, pass_counter = ISR_PASS_LIMIT; + + spin_lock(&dp->port.lock); + + status = __raw_readl(dp->port.membase + HW_UARTDBGMIS); + while (status) { + handled = 1; + + __raw_writel(status & ~(BM_UARTDBGMIS_TXMIS | + BM_UARTDBGMIS_RTMIS | + BM_UARTDBGMIS_RXMIS), + dp->port.membase + HW_UARTDBGICR); + + if (status & (BM_UARTDBGMIS_RTMIS | BM_UARTDBGMIS_RXMIS)) + duart_rx_chars(dp); + if (status & (BM_UARTDBGMIS_DSRMMIS | + BM_UARTDBGMIS_DCDMMIS | + BM_UARTDBGMIS_CTSMMIS | BM_UARTDBGMIS_RIMMIS)) + duart_modem_status(dp); + if (status & BM_UARTDBGMIS_TXMIS) + duart_tx_chars(dp); + + if (pass_counter-- == 0) + break; + + status = __raw_readl(dp->port.membase + HW_UARTDBGMIS); + }; + + spin_unlock(&dp->port.lock); + + return IRQ_RETVAL(handled); +} + +static unsigned int duart_tx_empty(struct uart_port *port) +{ + struct duart_port *dp = (struct duart_port *)port; + unsigned int status = __raw_readl(dp->port.membase + HW_UARTDBGFR); + return status & (BM_UARTDBGFR_BUSY | BM_UARTDBGFR_TXFF) ? + 0 : TIOCSER_TEMT; +} + +static unsigned int duart_get_mctrl(struct uart_port *port) +{ + unsigned int result = 0; + struct duart_port *dp = (struct duart_port *)port; + unsigned int status = __raw_readl(dp->port.membase + HW_UARTDBGFR); + +#define TEST_AND_SET_BIT(uartbit, tiocmbit) do { \ + if (status & uartbit) \ + result |= tiocmbit; \ + } while (0) + + TEST_AND_SET_BIT(BM_UARTDBGFR_DCD, TIOCM_CAR); + TEST_AND_SET_BIT(BM_UARTDBGFR_DSR, TIOCM_DSR); + TEST_AND_SET_BIT(BM_UARTDBGFR_CTS, TIOCM_CTS); + TEST_AND_SET_BIT(BM_UARTDBGFR_RI, TIOCM_RNG); +#undef TEST_AND_SET_BIT + if (force_cd) + result |= TIOCM_CAR; + return result; +} + +static void duart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + unsigned int cr; + struct duart_port *dp = (struct duart_port *)port; + + cr = __raw_readl(dp->port.membase + HW_UARTDBGCR); + +#define TEST_AND_SET_BIT(tiocmbit, uartbit) do { \ + if (mctrl & tiocmbit) \ + cr |= uartbit; \ + else \ + cr &= ~uartbit; \ + } while (0) + + TEST_AND_SET_BIT(TIOCM_RTS, BM_UARTDBGCR_RTS); + TEST_AND_SET_BIT(TIOCM_DTR, BM_UARTDBGCR_DTR); + TEST_AND_SET_BIT(TIOCM_OUT1, BM_UARTDBGCR_OUT1); + TEST_AND_SET_BIT(TIOCM_OUT2, BM_UARTDBGCR_OUT2); + TEST_AND_SET_BIT(TIOCM_LOOP, BM_UARTDBGCR_LBE); +#undef TEST_AND_SET_BIT + + __raw_writel(cr, dp->port.membase + HW_UARTDBGCR); +} + +static void duart_break_ctl(struct uart_port *port, int break_state) +{ + unsigned long flags; + unsigned int lcr_h; + struct duart_port *dp = (struct duart_port *)port; + + spin_lock_irqsave(&dp->port.lock, flags); + lcr_h = __raw_readl(dp->port.membase + HW_UARTDBGLCR_H); + if (break_state == -1) + lcr_h |= BM_UARTDBGLCR_H_BRK; + else + lcr_h &= ~BM_UARTDBGLCR_H_BRK; + __raw_writel(lcr_h, dp->port.membase + HW_UARTDBGLCR_H); + spin_unlock_irqrestore(&dp->port.lock, flags); +} + +static int duart_startup(struct uart_port *port) +{ + u32 cr, lcr; + int retval; + struct duart_port *dp = (struct duart_port *)port; + + /* + * Allocate the IRQ + */ + retval = request_irq(dp->port.irq, duart_int, 0, DUART_DEVID, dp); + if (retval) + return retval; + + /* wake up the UART */ + __raw_writel(0, dp->port.membase + HW_UARTDBGDR); + + __raw_writel(BF_UARTDBGIFLS_TXIFLSEL(BV_UARTDBGIFLS_TXIFLSEL__ONE_HALF) + | + BF_UARTDBGIFLS_RXIFLSEL(BV_UARTDBGIFLS_RXIFLSEL__ONE_HALF), + dp->port.membase + HW_UARTDBGIFLS); + + /* + * Provoke TX FIFO interrupt into asserting. + */ + cr = BM_UARTDBGCR_UARTEN | BM_UARTDBGCR_RXE | BM_UARTDBGCR_TXE; + __raw_writel(cr, dp->port.membase + HW_UARTDBGCR); + + lcr = __raw_readl(dp->port.membase + HW_UARTDBGLCR_H); + lcr |= BM_UARTDBGLCR_H_FEN; + __raw_writel(lcr, dp->port.membase + HW_UARTDBGLCR_H); + + /* + * initialise the old status of the modem signals + */ + dp->old_status = __raw_readl(dp->port.membase + HW_UARTDBGFR) & + (BM_UARTDBGFR_DCD | BM_UARTDBGFR_DSR | BM_UARTDBGFR_CTS); + /* + * Finally, enable interrupts + */ + dp->im = BM_UARTDBGIMSC_RXIM | BM_UARTDBGIMSC_RTIM; + __raw_writel(dp->im, dp->port.membase + HW_UARTDBGIMSC); + + return 0; +} + +static void duart_shutdown(struct uart_port *port) +{ + unsigned long flags; + unsigned int val; + struct duart_port *dp = (struct duart_port *)port; + + /* + * disable all interrupts + */ + spin_lock_irqsave(&dp->port.lock, flags); + dp->im = 0; + __raw_writel(dp->im, dp->port.membase + HW_UARTDBGIMSC); + __raw_writel(0xffff, dp->port.membase + HW_UARTDBGICR); + spin_unlock_irqrestore(&dp->port.lock, flags); + + free_irq(dp->port.irq, dp); + + /* + * disable the port + */ + __raw_writel(BM_UARTDBGCR_UARTEN | BM_UARTDBGCR_TXE, + dp->port.membase + HW_UARTDBGCR); + /* + * disable break condition and fifos + */ + val = __raw_readl(dp->port.membase + HW_UARTDBGLCR_H); + val &= ~(BM_UARTDBGLCR_H_BRK | BM_UARTDBGLCR_H_FEN); + __raw_writel(val, dp->port.membase + HW_UARTDBGLCR_H); +} + +static void +duart_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + unsigned int lcr_h, old_cr; + unsigned long flags; + unsigned int baud, quot; + + /* + * Ask the core to calculate the divisor for us. + */ + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); + quot = (port->uartclk << 2) / baud; + + switch (termios->c_cflag & CSIZE) { + case CS5: + lcr_h = BF_UARTDBGLCR_H_WLEN(0); + break; + case CS6: + lcr_h = BF_UARTDBGLCR_H_WLEN(1); + break; + case CS7: + lcr_h = BF_UARTDBGLCR_H_WLEN(2); + break; + default: /* CS8 */ + lcr_h = BF_UARTDBGLCR_H_WLEN(3); + break; + } + if (termios->c_cflag & CSTOPB) + lcr_h |= BM_UARTDBGLCR_H_STP2; + if (termios->c_cflag & PARENB) { + lcr_h |= BM_UARTDBGLCR_H_PEN; + if (!(termios->c_cflag & PARODD)) + lcr_h |= BM_UARTDBGLCR_H_EPS; + } + lcr_h |= BM_UARTDBGLCR_H_FEN; + + spin_lock_irqsave(&port->lock, flags); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + port->read_status_mask = BM_UARTDBGRSR_ECR_OE; + if (termios->c_iflag & INPCK) + port->read_status_mask |= BM_UARTDBGRSR_ECR_FE | + BM_UARTDBGRSR_ECR_PE; + if (termios->c_iflag & (BRKINT | PARMRK)) + port->read_status_mask |= BM_UARTDBGRSR_ECR_BE; + + /* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= BM_UARTDBGRSR_ECR_FE | + BM_UARTDBGRSR_ECR_PE; + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |= BM_UARTDBGRSR_ECR_BE; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= BM_UARTDBGRSR_ECR_OE; + } + + if (UART_ENABLE_MS(port, termios->c_cflag)) + duart_enable_ms(port); + + /* first, disable everything */ + old_cr = __raw_readl(port->membase + HW_UARTDBGCR); + __raw_writel(0, port->membase + HW_UARTDBGCR); + + /* Set baud rate */ + __raw_writel(quot & 0x3f, port->membase + HW_UARTDBGFBRD); + __raw_writel(quot >> 6, port->membase + HW_UARTDBGIBRD); + /* + * ----------v----------v----------v----------v----- + * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L + * ----------^----------^----------^----------^----- + */ + __raw_writel(lcr_h, port->membase + HW_UARTDBGLCR_H); + __raw_writel(old_cr, port->membase + HW_UARTDBGCR); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *duart_type(struct uart_port *port) +{ + return port->type == PORT_DUART ? DUART_DEVID : NULL; +} + +/* + * Release the memory region(s) being used by 'port' + */ +static void duart_release_port(struct uart_port *port) +{ + release_mem_region(port->mapbase, PAGE_SIZE); +} + +/* + * Request the memory region(s) being used by 'port' + */ +static int duart_request_port(struct uart_port *port) +{ + return request_mem_region(port->mapbase, PAGE_SIZE, DUART_DEVID) + != NULL ? 0 : -EBUSY; +} + +/* + * Configure/autoconfigure the port. + */ +static void duart_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) { + port->type = PORT_DUART; + duart_request_port(port); + } +} + +/* + * verify the new serial_struct (for TIOCSSERIAL). + */ +static int duart_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + int ret = 0; + if (ser->type != PORT_UNKNOWN && ser->type != PORT_DUART) + ret = -EINVAL; + if (ser->irq < 0 || ser->irq >= NR_IRQS) + ret = -EINVAL; + if (ser->baud_base < 9600) + ret = -EINVAL; + return ret; +} + +static struct uart_ops duart_pops = { + .tx_empty = duart_tx_empty, + .set_mctrl = duart_set_mctrl, + .get_mctrl = duart_get_mctrl, + .stop_tx = duart_stop_tx, + .start_tx = duart_start_tx, + .stop_rx = duart_stop_rx, + .enable_ms = duart_enable_ms, + .break_ctl = duart_break_ctl, + .startup = duart_startup, + .shutdown = duart_shutdown, + .set_termios = duart_set_termios, + .type = duart_type, + .release_port = duart_release_port, + .request_port = duart_request_port, + .config_port = duart_config_port, + .verify_port = duart_verify_port, +}; + +static struct duart_port duart_port = { + .port = { + .iotype = SERIAL_IO_MEM, +#ifdef CONFIG_MXS_EARLY_CONSOLE + .membase = MXS_DEBUG_CONSOLE_VIRT, + .mapbase = MXS_DEBUG_CONSOLE_PHYS, +#endif + .fifosize = 16, + .ops = &duart_pops, + .flags = ASYNC_BOOT_AUTOCONF, + .line = 0, + }, +}; + +#ifdef CONFIG_SERIAL_MXS_DUART_CONSOLE + +static void +duart_console_write(struct console *co, const char *s, unsigned int count) +{ + struct uart_port *port = &duart_port.port; + unsigned int status, old_cr; + int i; + /* + * First save the CR then disable the interrupts + */ + old_cr = __raw_readl(port->membase + HW_UARTDBGCR); + __raw_writel(BM_UARTDBGCR_UARTEN | BM_UARTDBGCR_TXE, + port->membase + HW_UARTDBGCR); + /* + * Now, do each character + */ + for (i = 0; i < count; i++) { + do { + status = __raw_readl(port->membase + HW_UARTDBGFR); + } while (status & BM_UARTDBGFR_TXFF); + + __raw_writel(s[i], port->membase + HW_UARTDBGDR); + if (s[i] == '\n') { + do { + status = __raw_readl(port->membase + + HW_UARTDBGFR); + } while (status & BM_UARTDBGFR_TXFF); + __raw_writel('\r', port->membase + HW_UARTDBGDR); + } + } + + /* + * Finally, wait for transmitter to become empty + * and restore the TCR + */ + do { + status = __raw_readl(port->membase + HW_UARTDBGFR); + } while (status & BM_UARTDBGFR_BUSY); + __raw_writel(old_cr, port->membase + HW_UARTDBGCR); +} + +static void __init +duart_console_get_options(struct uart_port *port, int *baud, + int *parity, int *bits) +{ + if (__raw_readl(port->membase + HW_UARTDBGCR) & BM_UARTDBGCR_UARTEN) { + unsigned int lcr_h, quot; + lcr_h = __raw_readl(port->membase + HW_UARTDBGLCR_H); + + *parity = 'n'; + if (lcr_h & BM_UARTDBGLCR_H_PEN) { + if (lcr_h & BM_UARTDBGLCR_H_EPS) + *parity = 'e'; + else + *parity = 'o'; + } + + if ((lcr_h & BM_UARTDBGLCR_H_WLEN) == BF_UARTDBGLCR_H_WLEN(2)) + *bits = 7; + else + *bits = 8; + + quot = (__raw_readl(port->membase + HW_UARTDBGFBRD) & 0x3F) | + __raw_readl(port->membase + HW_UARTDBGIBRD) << 6; + if (quot == 0) + quot = 1; + *baud = (port->uartclk << 2) / quot; + } +} + +static int __init duart_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index) + return -EINVAL; + + port = &duart_port.port; + + if (duart_port.clk == NULL || IS_ERR(duart_port.clk)) { + duart_port.clk = clk_get(NULL, "uart"); + if (duart_port.clk == NULL || IS_ERR(duart_port.clk)) + return -ENODEV; + duart_port.port.uartclk = clk_get_rate(duart_port.clk); + } + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + duart_console_get_options(port, &baud, &parity, &bits); + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct console duart_console = { + .name = "ttyAM", + .write = duart_console_write, + .device = uart_console_device, + .setup = duart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &duart_drv, +}; + +#ifdef CONFIG_MXS_EARLY_CONSOLE +static int __init duart_console_init(void) +{ + register_console(&duart_console); + return 0; +} + +console_initcall(duart_console_init); +#endif + +#endif + +static struct uart_driver duart_drv = { + .owner = THIS_MODULE, + .driver_name = "ttyAM", + .dev_name = "ttyAM", + .major = SERIAL_DUART_MAJOR, + .minor = SERIAL_DUART_MINOR, + .nr = 1, +#ifdef CONFIG_SERIAL_MXS_DUART_CONSOLE + .cons = &duart_console, +#endif +}; + +static int __devinit duart_probe(struct platform_device *pdev) +{ + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOMEM; + /* + * Will use mapbase and membase here if !CONFIG_MXS_EARLY_CONSOLE, + * or use the overridden values later if CONFIG_MXS_EARLY_CONSOLE + */ + duart_port.port.mapbase = res->start; + duart_port.port.membase = + (unsigned char __iomem *)IO_ADDRESS(res->start); + + duart_port.port.irq = platform_get_irq(pdev, 0); + if (duart_port.port.irq < 0) + return -EINVAL; + device_init_wakeup(&pdev->dev, 1); + + duart_port.clk = clk_get(NULL, "uart"); + if (duart_port.clk == NULL || IS_ERR(duart_port.clk)) + return -ENODEV; + duart_port.suspended = 0; + duart_port.port.dev = &pdev->dev; + duart_port.port.uartclk = clk_get_rate(duart_port.clk); + uart_add_one_port(&duart_drv, &duart_port.port); + return 0; +} + +static int __devexit duart_remove(struct platform_device *pdev) +{ + clk_put(duart_port.clk); + uart_remove_one_port(&duart_drv, &duart_port.port); + return 0; +} + +#ifdef CONFIG_PM +static int duart_suspend(struct platform_device *pdev, + pm_message_t state) +{ + int ret = 0; + if (!duart_port.suspended) { + ret = uart_suspend_port(&duart_drv, &duart_port.port); + if (!ret) + duart_port.suspended = 1; + } + return ret; +} + +static int duart_resume(struct platform_device *pdev, + pm_message_t state) +{ + int ret = 0; + if (duart_port.suspended) { + ret = uart_resume_port(&duart_drv, &duart_port.port); + if (!ret) + duart_port.suspended = 0; + } + return ret; +} +#else +#define duart_suspend NULL +#define duart_resume NULL +#endif + +static struct platform_driver duart_driver = { + .probe = duart_probe, + .remove = __devexit_p(duart_remove), + .suspend = duart_suspend, + .resume = duart_resume, + .driver = { + .name = "mxs-duart", + .owner = THIS_MODULE, + }, +}; + +static int __init duart_init(void) +{ + int ret; + ret = uart_register_driver(&duart_drv); + if (ret) + return ret; + + ret = platform_driver_register(&duart_driver); + if (ret) + uart_unregister_driver(&duart_drv); + + return ret; +} + +static void __exit duart_exit(void) +{ + platform_driver_unregister(&duart_driver); + uart_unregister_driver(&duart_drv); +} + +module_init(duart_init); +module_exit(duart_exit); +module_param(force_cd, int, 0644); +MODULE_AUTHOR("ARM Ltd/Deep Blue Solutions Ltd/Freescale Inc"); +MODULE_DESCRIPTION("i.MXS debug uart"); +MODULE_LICENSE("GPL"); diff --git a/drivers/serial/regs-duart.h b/drivers/serial/regs-duart.h new file mode 100644 index 000000000000..0b5932c79a55 --- /dev/null +++ b/drivers/serial/regs-duart.h @@ -0,0 +1,301 @@ +/* + * Freescale UARTDBG Register Definitions + * + * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This file is created by xml file. Don't Edit it. + * + * Xml Revision: 1.21 + * Template revision: 26195 + */ + +#ifndef __ARCH_ARM___UARTDBG_H +#define __ARCH_ARM___UARTDBG_H + + +#define HW_UARTDBGDR (0x00000000) + +#define BP_UARTDBGDR_UNAVAILABLE 16 +#define BM_UARTDBGDR_UNAVAILABLE 0xFFFF0000 +#define BF_UARTDBGDR_UNAVAILABLE(v) \ + (((v) << 16) & BM_UARTDBGDR_UNAVAILABLE) +#define BP_UARTDBGDR_RESERVED 12 +#define BM_UARTDBGDR_RESERVED 0x0000F000 +#define BF_UARTDBGDR_RESERVED(v) \ + (((v) << 12) & BM_UARTDBGDR_RESERVED) +#define BM_UARTDBGDR_OE 0x00000800 +#define BM_UARTDBGDR_BE 0x00000400 +#define BM_UARTDBGDR_PE 0x00000200 +#define BM_UARTDBGDR_FE 0x00000100 +#define BP_UARTDBGDR_DATA 0 +#define BM_UARTDBGDR_DATA 0x000000FF +#define BF_UARTDBGDR_DATA(v) \ + (((v) << 0) & BM_UARTDBGDR_DATA) + +#define HW_UARTDBGRSR_ECR (0x00000004) + +#define BP_UARTDBGRSR_ECR_UNAVAILABLE 8 +#define BM_UARTDBGRSR_ECR_UNAVAILABLE 0xFFFFFF00 +#define BF_UARTDBGRSR_ECR_UNAVAILABLE(v) \ + (((v) << 8) & BM_UARTDBGRSR_ECR_UNAVAILABLE) +#define BP_UARTDBGRSR_ECR_EC 4 +#define BM_UARTDBGRSR_ECR_EC 0x000000F0 +#define BF_UARTDBGRSR_ECR_EC(v) \ + (((v) << 4) & BM_UARTDBGRSR_ECR_EC) +#define BM_UARTDBGRSR_ECR_OE 0x00000008 +#define BM_UARTDBGRSR_ECR_BE 0x00000004 +#define BM_UARTDBGRSR_ECR_PE 0x00000002 +#define BM_UARTDBGRSR_ECR_FE 0x00000001 + +#define HW_UARTDBGFR (0x00000018) + +#define BP_UARTDBGFR_UNAVAILABLE 16 +#define BM_UARTDBGFR_UNAVAILABLE 0xFFFF0000 +#define BF_UARTDBGFR_UNAVAILABLE(v) \ + (((v) << 16) & BM_UARTDBGFR_UNAVAILABLE) +#define BP_UARTDBGFR_RESERVED 9 +#define BM_UARTDBGFR_RESERVED 0x0000FE00 +#define BF_UARTDBGFR_RESERVED(v) \ + (((v) << 9) & BM_UARTDBGFR_RESERVED) +#define BM_UARTDBGFR_RI 0x00000100 +#define BM_UARTDBGFR_TXFE 0x00000080 +#define BM_UARTDBGFR_RXFF 0x00000040 +#define BM_UARTDBGFR_TXFF 0x00000020 +#define BM_UARTDBGFR_RXFE 0x00000010 +#define BM_UARTDBGFR_BUSY 0x00000008 +#define BM_UARTDBGFR_DCD 0x00000004 +#define BM_UARTDBGFR_DSR 0x00000002 +#define BM_UARTDBGFR_CTS 0x00000001 + +#define HW_UARTDBGILPR (0x00000020) + +#define BP_UARTDBGILPR_UNAVAILABLE 8 +#define BM_UARTDBGILPR_UNAVAILABLE 0xFFFFFF00 +#define BF_UARTDBGILPR_UNAVAILABLE(v) \ + (((v) << 8) & BM_UARTDBGILPR_UNAVAILABLE) +#define BP_UARTDBGILPR_ILPDVSR 0 +#define BM_UARTDBGILPR_ILPDVSR 0x000000FF +#define BF_UARTDBGILPR_ILPDVSR(v) \ + (((v) << 0) & BM_UARTDBGILPR_ILPDVSR) + +#define HW_UARTDBGIBRD (0x00000024) + +#define BP_UARTDBGIBRD_UNAVAILABLE 16 +#define BM_UARTDBGIBRD_UNAVAILABLE 0xFFFF0000 +#define BF_UARTDBGIBRD_UNAVAILABLE(v) \ + (((v) << 16) & BM_UARTDBGIBRD_UNAVAILABLE) +#define BP_UARTDBGIBRD_BAUD_DIVINT 0 +#define BM_UARTDBGIBRD_BAUD_DIVINT 0x0000FFFF +#define BF_UARTDBGIBRD_BAUD_DIVINT(v) \ + (((v) << 0) & BM_UARTDBGIBRD_BAUD_DIVINT) + +#define HW_UARTDBGFBRD (0x00000028) + +#define BP_UARTDBGFBRD_UNAVAILABLE 8 +#define BM_UARTDBGFBRD_UNAVAILABLE 0xFFFFFF00 +#define BF_UARTDBGFBRD_UNAVAILABLE(v) \ + (((v) << 8) & BM_UARTDBGFBRD_UNAVAILABLE) +#define BP_UARTDBGFBRD_RESERVED 6 +#define BM_UARTDBGFBRD_RESERVED 0x000000C0 +#define BF_UARTDBGFBRD_RESERVED(v) \ + (((v) << 6) & BM_UARTDBGFBRD_RESERVED) +#define BP_UARTDBGFBRD_BAUD_DIVFRAC 0 +#define BM_UARTDBGFBRD_BAUD_DIVFRAC 0x0000003F +#define BF_UARTDBGFBRD_BAUD_DIVFRAC(v) \ + (((v) << 0) & BM_UARTDBGFBRD_BAUD_DIVFRAC) + +#define HW_UARTDBGLCR_H (0x0000002c) + +#define BP_UARTDBGLCR_H_UNAVAILABLE 16 +#define BM_UARTDBGLCR_H_UNAVAILABLE 0xFFFF0000 +#define BF_UARTDBGLCR_H_UNAVAILABLE(v) \ + (((v) << 16) & BM_UARTDBGLCR_H_UNAVAILABLE) +#define BP_UARTDBGLCR_H_RESERVED 8 +#define BM_UARTDBGLCR_H_RESERVED 0x0000FF00 +#define BF_UARTDBGLCR_H_RESERVED(v) \ + (((v) << 8) & BM_UARTDBGLCR_H_RESERVED) +#define BM_UARTDBGLCR_H_SPS 0x00000080 +#define BP_UARTDBGLCR_H_WLEN 5 +#define BM_UARTDBGLCR_H_WLEN 0x00000060 +#define BF_UARTDBGLCR_H_WLEN(v) \ + (((v) << 5) & BM_UARTDBGLCR_H_WLEN) +#define BM_UARTDBGLCR_H_FEN 0x00000010 +#define BM_UARTDBGLCR_H_STP2 0x00000008 +#define BM_UARTDBGLCR_H_EPS 0x00000004 +#define BM_UARTDBGLCR_H_PEN 0x00000002 +#define BM_UARTDBGLCR_H_BRK 0x00000001 + +#define HW_UARTDBGCR (0x00000030) + +#define BP_UARTDBGCR_UNAVAILABLE 16 +#define BM_UARTDBGCR_UNAVAILABLE 0xFFFF0000 +#define BF_UARTDBGCR_UNAVAILABLE(v) \ + (((v) << 16) & BM_UARTDBGCR_UNAVAILABLE) +#define BM_UARTDBGCR_CTSEN 0x00008000 +#define BM_UARTDBGCR_RTSEN 0x00004000 +#define BM_UARTDBGCR_OUT2 0x00002000 +#define BM_UARTDBGCR_OUT1 0x00001000 +#define BM_UARTDBGCR_RTS 0x00000800 +#define BM_UARTDBGCR_DTR 0x00000400 +#define BM_UARTDBGCR_RXE 0x00000200 +#define BM_UARTDBGCR_TXE 0x00000100 +#define BM_UARTDBGCR_LBE 0x00000080 +#define BP_UARTDBGCR_RESERVED 3 +#define BM_UARTDBGCR_RESERVED 0x00000078 +#define BF_UARTDBGCR_RESERVED(v) \ + (((v) << 3) & BM_UARTDBGCR_RESERVED) +#define BM_UARTDBGCR_SIRLP 0x00000004 +#define BM_UARTDBGCR_SIREN 0x00000002 +#define BM_UARTDBGCR_UARTEN 0x00000001 + +#define HW_UARTDBGIFLS (0x00000034) + +#define BP_UARTDBGIFLS_UNAVAILABLE 16 +#define BM_UARTDBGIFLS_UNAVAILABLE 0xFFFF0000 +#define BF_UARTDBGIFLS_UNAVAILABLE(v) \ + (((v) << 16) & BM_UARTDBGIFLS_UNAVAILABLE) +#define BP_UARTDBGIFLS_RESERVED 6 +#define BM_UARTDBGIFLS_RESERVED 0x0000FFC0 +#define BF_UARTDBGIFLS_RESERVED(v) \ + (((v) << 6) & BM_UARTDBGIFLS_RESERVED) +#define BP_UARTDBGIFLS_RXIFLSEL 3 +#define BM_UARTDBGIFLS_RXIFLSEL 0x00000038 +#define BF_UARTDBGIFLS_RXIFLSEL(v) \ + (((v) << 3) & BM_UARTDBGIFLS_RXIFLSEL) +#define BV_UARTDBGIFLS_RXIFLSEL__ONE_EIGHT 0x0 +#define BV_UARTDBGIFLS_RXIFLSEL__ONE_QUARTER 0x1 +#define BV_UARTDBGIFLS_RXIFLSEL__ONE_HALF 0x2 +#define BV_UARTDBGIFLS_RXIFLSEL__THREE_QUARTERS 0x3 +#define BV_UARTDBGIFLS_RXIFLSEL__SEVEN_EIGHTHS 0x4 +#define BV_UARTDBGIFLS_RXIFLSEL__INVALID5 0x5 +#define BV_UARTDBGIFLS_RXIFLSEL__INVALID6 0x6 +#define BV_UARTDBGIFLS_RXIFLSEL__INVALID7 0x7 +#define BP_UARTDBGIFLS_TXIFLSEL 0 +#define BM_UARTDBGIFLS_TXIFLSEL 0x00000007 +#define BF_UARTDBGIFLS_TXIFLSEL(v) \ + (((v) << 0) & BM_UARTDBGIFLS_TXIFLSEL) +#define BV_UARTDBGIFLS_TXIFLSEL__ONE_EIGHT 0x0 +#define BV_UARTDBGIFLS_TXIFLSEL__ONE_QUARTER 0x1 +#define BV_UARTDBGIFLS_TXIFLSEL__ONE_HALF 0x2 +#define BV_UARTDBGIFLS_TXIFLSEL__THREE_QUARTERS 0x3 +#define BV_UARTDBGIFLS_TXIFLSEL__SEVEN_EIGHTHS 0x4 +#define BV_UARTDBGIFLS_TXIFLSEL__INVALID5 0x5 +#define BV_UARTDBGIFLS_TXIFLSEL__INVALID6 0x6 +#define BV_UARTDBGIFLS_TXIFLSEL__INVALID7 0x7 + +#define HW_UARTDBGIMSC (0x00000038) + +#define BP_UARTDBGIMSC_UNAVAILABLE 16 +#define BM_UARTDBGIMSC_UNAVAILABLE 0xFFFF0000 +#define BF_UARTDBGIMSC_UNAVAILABLE(v) \ + (((v) << 16) & BM_UARTDBGIMSC_UNAVAILABLE) +#define BP_UARTDBGIMSC_RESERVED 11 +#define BM_UARTDBGIMSC_RESERVED 0x0000F800 +#define BF_UARTDBGIMSC_RESERVED(v) \ + (((v) << 11) & BM_UARTDBGIMSC_RESERVED) +#define BM_UARTDBGIMSC_OEIM 0x00000400 +#define BM_UARTDBGIMSC_BEIM 0x00000200 +#define BM_UARTDBGIMSC_PEIM 0x00000100 +#define BM_UARTDBGIMSC_FEIM 0x00000080 +#define BM_UARTDBGIMSC_RTIM 0x00000040 +#define BM_UARTDBGIMSC_TXIM 0x00000020 +#define BM_UARTDBGIMSC_RXIM 0x00000010 +#define BM_UARTDBGIMSC_DSRMIM 0x00000008 +#define BM_UARTDBGIMSC_DCDMIM 0x00000004 +#define BM_UARTDBGIMSC_CTSMIM 0x00000002 +#define BM_UARTDBGIMSC_RIMIM 0x00000001 + +#define HW_UARTDBGRIS (0x0000003c) + +#define BP_UARTDBGRIS_UNAVAILABLE 16 +#define BM_UARTDBGRIS_UNAVAILABLE 0xFFFF0000 +#define BF_UARTDBGRIS_UNAVAILABLE(v) \ + (((v) << 16) & BM_UARTDBGRIS_UNAVAILABLE) +#define BP_UARTDBGRIS_RESERVED 11 +#define BM_UARTDBGRIS_RESERVED 0x0000F800 +#define BF_UARTDBGRIS_RESERVED(v) \ + (((v) << 11) & BM_UARTDBGRIS_RESERVED) +#define BM_UARTDBGRIS_OERIS 0x00000400 +#define BM_UARTDBGRIS_BERIS 0x00000200 +#define BM_UARTDBGRIS_PERIS 0x00000100 +#define BM_UARTDBGRIS_FERIS 0x00000080 +#define BM_UARTDBGRIS_RTRIS 0x00000040 +#define BM_UARTDBGRIS_TXRIS 0x00000020 +#define BM_UARTDBGRIS_RXRIS 0x00000010 +#define BM_UARTDBGRIS_DSRRMIS 0x00000008 +#define BM_UARTDBGRIS_DCDRMIS 0x00000004 +#define BM_UARTDBGRIS_CTSRMIS 0x00000002 +#define BM_UARTDBGRIS_RIRMIS 0x00000001 + +#define HW_UARTDBGMIS (0x00000040) + +#define BP_UARTDBGMIS_UNAVAILABLE 16 +#define BM_UARTDBGMIS_UNAVAILABLE 0xFFFF0000 +#define BF_UARTDBGMIS_UNAVAILABLE(v) \ + (((v) << 16) & BM_UARTDBGMIS_UNAVAILABLE) +#define BP_UARTDBGMIS_RESERVED 11 +#define BM_UARTDBGMIS_RESERVED 0x0000F800 +#define BF_UARTDBGMIS_RESERVED(v) \ + (((v) << 11) & BM_UARTDBGMIS_RESERVED) +#define BM_UARTDBGMIS_OEMIS 0x00000400 +#define BM_UARTDBGMIS_BEMIS 0x00000200 +#define BM_UARTDBGMIS_PEMIS 0x00000100 +#define BM_UARTDBGMIS_FEMIS 0x00000080 +#define BM_UARTDBGMIS_RTMIS 0x00000040 +#define BM_UARTDBGMIS_TXMIS 0x00000020 +#define BM_UARTDBGMIS_RXMIS 0x00000010 +#define BM_UARTDBGMIS_DSRMMIS 0x00000008 +#define BM_UARTDBGMIS_DCDMMIS 0x00000004 +#define BM_UARTDBGMIS_CTSMMIS 0x00000002 +#define BM_UARTDBGMIS_RIMMIS 0x00000001 + +#define HW_UARTDBGICR (0x00000044) + +#define BP_UARTDBGICR_UNAVAILABLE 16 +#define BM_UARTDBGICR_UNAVAILABLE 0xFFFF0000 +#define BF_UARTDBGICR_UNAVAILABLE(v) \ + (((v) << 16) & BM_UARTDBGICR_UNAVAILABLE) +#define BP_UARTDBGICR_RESERVED 11 +#define BM_UARTDBGICR_RESERVED 0x0000F800 +#define BF_UARTDBGICR_RESERVED(v) \ + (((v) << 11) & BM_UARTDBGICR_RESERVED) +#define BM_UARTDBGICR_OEIC 0x00000400 +#define BM_UARTDBGICR_BEIC 0x00000200 +#define BM_UARTDBGICR_PEIC 0x00000100 +#define BM_UARTDBGICR_FEIC 0x00000080 +#define BM_UARTDBGICR_RTIC 0x00000040 +#define BM_UARTDBGICR_TXIC 0x00000020 +#define BM_UARTDBGICR_RXIC 0x00000010 +#define BM_UARTDBGICR_DSRMIC 0x00000008 +#define BM_UARTDBGICR_DCDMIC 0x00000004 +#define BM_UARTDBGICR_CTSMIC 0x00000002 +#define BM_UARTDBGICR_RIMIC 0x00000001 + +#define HW_UARTDBGDMACR (0x00000048) + +#define BP_UARTDBGDMACR_UNAVAILABLE 16 +#define BM_UARTDBGDMACR_UNAVAILABLE 0xFFFF0000 +#define BF_UARTDBGDMACR_UNAVAILABLE(v) \ + (((v) << 16) & BM_UARTDBGDMACR_UNAVAILABLE) +#define BP_UARTDBGDMACR_RESERVED 3 +#define BM_UARTDBGDMACR_RESERVED 0x0000FFF8 +#define BF_UARTDBGDMACR_RESERVED(v) \ + (((v) << 3) & BM_UARTDBGDMACR_RESERVED) +#define BM_UARTDBGDMACR_DMAONERR 0x00000004 +#define BM_UARTDBGDMACR_TXDMAE 0x00000002 +#define BM_UARTDBGDMACR_RXDMAE 0x00000001 +#endif /* __ARCH_ARM___UARTDBG_H */ diff --git a/drivers/serial/regs-uartapp.h b/drivers/serial/regs-uartapp.h new file mode 100644 index 000000000000..aad9a7866556 --- /dev/null +++ b/drivers/serial/regs-uartapp.h @@ -0,0 +1,307 @@ +/* + * Freescale UARTAPP Register Definitions + * + * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This file is created by xml file. Don't Edit it. + * + * Xml Revision: 1.42 + * Template revision: 26195 + */ + +#ifndef __ARCH_ARM___UARTAPP_H +#define __ARCH_ARM___UARTAPP_H + + +#define HW_UARTAPP_CTRL0 (0x00000000) +#define HW_UARTAPP_CTRL0_SET (0x00000004) +#define HW_UARTAPP_CTRL0_CLR (0x00000008) +#define HW_UARTAPP_CTRL0_TOG (0x0000000c) + +#define BM_UARTAPP_CTRL0_SFTRST 0x80000000 +#define BM_UARTAPP_CTRL0_CLKGATE 0x40000000 +#define BM_UARTAPP_CTRL0_RUN 0x20000000 +#define BM_UARTAPP_CTRL0_RX_SOURCE 0x10000000 +#define BM_UARTAPP_CTRL0_RXTO_ENABLE 0x08000000 +#define BP_UARTAPP_CTRL0_RXTIMEOUT 16 +#define BM_UARTAPP_CTRL0_RXTIMEOUT 0x07FF0000 +#define BF_UARTAPP_CTRL0_RXTIMEOUT(v) \ + (((v) << 16) & BM_UARTAPP_CTRL0_RXTIMEOUT) +#define BP_UARTAPP_CTRL0_XFER_COUNT 0 +#define BM_UARTAPP_CTRL0_XFER_COUNT 0x0000FFFF +#define BF_UARTAPP_CTRL0_XFER_COUNT(v) \ + (((v) << 0) & BM_UARTAPP_CTRL0_XFER_COUNT) + +#define HW_UARTAPP_CTRL1 (0x00000010) +#define HW_UARTAPP_CTRL1_SET (0x00000014) +#define HW_UARTAPP_CTRL1_CLR (0x00000018) +#define HW_UARTAPP_CTRL1_TOG (0x0000001c) + +#define BP_UARTAPP_CTRL1_RSVD2 29 +#define BM_UARTAPP_CTRL1_RSVD2 0xE0000000 +#define BF_UARTAPP_CTRL1_RSVD2(v) \ + (((v) << 29) & BM_UARTAPP_CTRL1_RSVD2) +#define BM_UARTAPP_CTRL1_RUN 0x10000000 +#define BP_UARTAPP_CTRL1_RSVD1 16 +#define BM_UARTAPP_CTRL1_RSVD1 0x0FFF0000 +#define BF_UARTAPP_CTRL1_RSVD1(v) \ + (((v) << 16) & BM_UARTAPP_CTRL1_RSVD1) +#define BP_UARTAPP_CTRL1_XFER_COUNT 0 +#define BM_UARTAPP_CTRL1_XFER_COUNT 0x0000FFFF +#define BF_UARTAPP_CTRL1_XFER_COUNT(v) \ + (((v) << 0) & BM_UARTAPP_CTRL1_XFER_COUNT) + +#define HW_UARTAPP_CTRL2 (0x00000020) +#define HW_UARTAPP_CTRL2_SET (0x00000024) +#define HW_UARTAPP_CTRL2_CLR (0x00000028) +#define HW_UARTAPP_CTRL2_TOG (0x0000002c) + +#define BM_UARTAPP_CTRL2_INVERT_RTS 0x80000000 +#define BM_UARTAPP_CTRL2_INVERT_CTS 0x40000000 +#define BM_UARTAPP_CTRL2_INVERT_TX 0x20000000 +#define BM_UARTAPP_CTRL2_INVERT_RX 0x10000000 +#define BM_UARTAPP_CTRL2_RTS_SEMAPHORE 0x08000000 +#define BM_UARTAPP_CTRL2_DMAONERR 0x04000000 +#define BM_UARTAPP_CTRL2_TXDMAE 0x02000000 +#define BM_UARTAPP_CTRL2_RXDMAE 0x01000000 +#define BM_UARTAPP_CTRL2_RSVD2 0x00800000 +#define BP_UARTAPP_CTRL2_RXIFLSEL 20 +#define BM_UARTAPP_CTRL2_RXIFLSEL 0x00700000 +#define BF_UARTAPP_CTRL2_RXIFLSEL(v) \ + (((v) << 20) & BM_UARTAPP_CTRL2_RXIFLSEL) +#define BV_UARTAPP_CTRL2_RXIFLSEL__NOT_EMPTY 0x0 +#define BV_UARTAPP_CTRL2_RXIFLSEL__ONE_QUARTER 0x1 +#define BV_UARTAPP_CTRL2_RXIFLSEL__ONE_HALF 0x2 +#define BV_UARTAPP_CTRL2_RXIFLSEL__THREE_QUARTERS 0x3 +#define BV_UARTAPP_CTRL2_RXIFLSEL__SEVEN_EIGHTHS 0x4 +#define BV_UARTAPP_CTRL2_RXIFLSEL__INVALID5 0x5 +#define BV_UARTAPP_CTRL2_RXIFLSEL__INVALID6 0x6 +#define BV_UARTAPP_CTRL2_RXIFLSEL__INVALID7 0x7 +#define BM_UARTAPP_CTRL2_RSVD3 0x00080000 +#define BP_UARTAPP_CTRL2_TXIFLSEL 16 +#define BM_UARTAPP_CTRL2_TXIFLSEL 0x00070000 +#define BF_UARTAPP_CTRL2_TXIFLSEL(v) \ + (((v) << 16) & BM_UARTAPP_CTRL2_TXIFLSEL) +#define BV_UARTAPP_CTRL2_TXIFLSEL__EMPTY 0x0 +#define BV_UARTAPP_CTRL2_TXIFLSEL__ONE_QUARTER 0x1 +#define BV_UARTAPP_CTRL2_TXIFLSEL__ONE_HALF 0x2 +#define BV_UARTAPP_CTRL2_TXIFLSEL__THREE_QUARTERS 0x3 +#define BV_UARTAPP_CTRL2_TXIFLSEL__SEVEN_EIGHTHS 0x4 +#define BV_UARTAPP_CTRL2_TXIFLSEL__INVALID5 0x5 +#define BV_UARTAPP_CTRL2_TXIFLSEL__INVALID6 0x6 +#define BV_UARTAPP_CTRL2_TXIFLSEL__INVALID7 0x7 +#define BM_UARTAPP_CTRL2_CTSEN 0x00008000 +#define BM_UARTAPP_CTRL2_RTSEN 0x00004000 +#define BM_UARTAPP_CTRL2_OUT2 0x00002000 +#define BM_UARTAPP_CTRL2_OUT1 0x00001000 +#define BM_UARTAPP_CTRL2_RTS 0x00000800 +#define BM_UARTAPP_CTRL2_DTR 0x00000400 +#define BM_UARTAPP_CTRL2_RXE 0x00000200 +#define BM_UARTAPP_CTRL2_TXE 0x00000100 +#define BM_UARTAPP_CTRL2_LBE 0x00000080 +#define BM_UARTAPP_CTRL2_USE_LCR2 0x00000040 +#define BP_UARTAPP_CTRL2_RSVD4 3 +#define BM_UARTAPP_CTRL2_RSVD4 0x00000038 +#define BF_UARTAPP_CTRL2_RSVD4(v) \ + (((v) << 3) & BM_UARTAPP_CTRL2_RSVD4) +#define BM_UARTAPP_CTRL2_SIRLP 0x00000004 +#define BM_UARTAPP_CTRL2_SIREN 0x00000002 +#define BM_UARTAPP_CTRL2_UARTEN 0x00000001 + +#define HW_UARTAPP_LINECTRL (0x00000030) +#define HW_UARTAPP_LINECTRL_SET (0x00000034) +#define HW_UARTAPP_LINECTRL_CLR (0x00000038) +#define HW_UARTAPP_LINECTRL_TOG (0x0000003c) + +#define BP_UARTAPP_LINECTRL_BAUD_DIVINT 16 +#define BM_UARTAPP_LINECTRL_BAUD_DIVINT 0xFFFF0000 +#define BF_UARTAPP_LINECTRL_BAUD_DIVINT(v) \ + (((v) << 16) & BM_UARTAPP_LINECTRL_BAUD_DIVINT) +#define BP_UARTAPP_LINECTRL_RSVD 14 +#define BM_UARTAPP_LINECTRL_RSVD 0x0000C000 +#define BF_UARTAPP_LINECTRL_RSVD(v) \ + (((v) << 14) & BM_UARTAPP_LINECTRL_RSVD) +#define BP_UARTAPP_LINECTRL_BAUD_DIVFRAC 8 +#define BM_UARTAPP_LINECTRL_BAUD_DIVFRAC 0x00003F00 +#define BF_UARTAPP_LINECTRL_BAUD_DIVFRAC(v) \ + (((v) << 8) & BM_UARTAPP_LINECTRL_BAUD_DIVFRAC) +#define BM_UARTAPP_LINECTRL_SPS 0x00000080 +#define BP_UARTAPP_LINECTRL_WLEN 5 +#define BM_UARTAPP_LINECTRL_WLEN 0x00000060 +#define BF_UARTAPP_LINECTRL_WLEN(v) \ + (((v) << 5) & BM_UARTAPP_LINECTRL_WLEN) +#define BM_UARTAPP_LINECTRL_FEN 0x00000010 +#define BM_UARTAPP_LINECTRL_STP2 0x00000008 +#define BM_UARTAPP_LINECTRL_EPS 0x00000004 +#define BM_UARTAPP_LINECTRL_PEN 0x00000002 +#define BM_UARTAPP_LINECTRL_BRK 0x00000001 + +#define HW_UARTAPP_LINECTRL2 (0x00000040) +#define HW_UARTAPP_LINECTRL2_SET (0x00000044) +#define HW_UARTAPP_LINECTRL2_CLR (0x00000048) +#define HW_UARTAPP_LINECTRL2_TOG (0x0000004c) + +#define BP_UARTAPP_LINECTRL2_BAUD_DIVINT 16 +#define BM_UARTAPP_LINECTRL2_BAUD_DIVINT 0xFFFF0000 +#define BF_UARTAPP_LINECTRL2_BAUD_DIVINT(v) \ + (((v) << 16) & BM_UARTAPP_LINECTRL2_BAUD_DIVINT) +#define BP_UARTAPP_LINECTRL2_RSVD 14 +#define BM_UARTAPP_LINECTRL2_RSVD 0x0000C000 +#define BF_UARTAPP_LINECTRL2_RSVD(v) \ + (((v) << 14) & BM_UARTAPP_LINECTRL2_RSVD) +#define BP_UARTAPP_LINECTRL2_BAUD_DIVFRAC 8 +#define BM_UARTAPP_LINECTRL2_BAUD_DIVFRAC 0x00003F00 +#define BF_UARTAPP_LINECTRL2_BAUD_DIVFRAC(v) \ + (((v) << 8) & BM_UARTAPP_LINECTRL2_BAUD_DIVFRAC) +#define BM_UARTAPP_LINECTRL2_SPS 0x00000080 +#define BP_UARTAPP_LINECTRL2_WLEN 5 +#define BM_UARTAPP_LINECTRL2_WLEN 0x00000060 +#define BF_UARTAPP_LINECTRL2_WLEN(v) \ + (((v) << 5) & BM_UARTAPP_LINECTRL2_WLEN) +#define BM_UARTAPP_LINECTRL2_FEN 0x00000010 +#define BM_UARTAPP_LINECTRL2_STP2 0x00000008 +#define BM_UARTAPP_LINECTRL2_EPS 0x00000004 +#define BM_UARTAPP_LINECTRL2_PEN 0x00000002 +#define BM_UARTAPP_LINECTRL2_RSVD1 0x00000001 + +#define HW_UARTAPP_INTR (0x00000050) +#define HW_UARTAPP_INTR_SET (0x00000054) +#define HW_UARTAPP_INTR_CLR (0x00000058) +#define HW_UARTAPP_INTR_TOG (0x0000005c) + +#define BP_UARTAPP_INTR_RSVD1 28 +#define BM_UARTAPP_INTR_RSVD1 0xF0000000 +#define BF_UARTAPP_INTR_RSVD1(v) \ + (((v) << 28) & BM_UARTAPP_INTR_RSVD1) +#define BM_UARTAPP_INTR_ABDIEN 0x08000000 +#define BM_UARTAPP_INTR_OEIEN 0x04000000 +#define BM_UARTAPP_INTR_BEIEN 0x02000000 +#define BM_UARTAPP_INTR_PEIEN 0x01000000 +#define BM_UARTAPP_INTR_FEIEN 0x00800000 +#define BM_UARTAPP_INTR_RTIEN 0x00400000 +#define BM_UARTAPP_INTR_TXIEN 0x00200000 +#define BM_UARTAPP_INTR_RXIEN 0x00100000 +#define BM_UARTAPP_INTR_DSRMIEN 0x00080000 +#define BM_UARTAPP_INTR_DCDMIEN 0x00040000 +#define BM_UARTAPP_INTR_CTSMIEN 0x00020000 +#define BM_UARTAPP_INTR_RIMIEN 0x00010000 +#define BP_UARTAPP_INTR_RSVD2 12 +#define BM_UARTAPP_INTR_RSVD2 0x0000F000 +#define BF_UARTAPP_INTR_RSVD2(v) \ + (((v) << 12) & BM_UARTAPP_INTR_RSVD2) +#define BM_UARTAPP_INTR_ABDIS 0x00000800 +#define BM_UARTAPP_INTR_OEIS 0x00000400 +#define BM_UARTAPP_INTR_BEIS 0x00000200 +#define BM_UARTAPP_INTR_PEIS 0x00000100 +#define BM_UARTAPP_INTR_FEIS 0x00000080 +#define BM_UARTAPP_INTR_RTIS 0x00000040 +#define BM_UARTAPP_INTR_TXIS 0x00000020 +#define BM_UARTAPP_INTR_RXIS 0x00000010 +#define BM_UARTAPP_INTR_DSRMIS 0x00000008 +#define BM_UARTAPP_INTR_DCDMIS 0x00000004 +#define BM_UARTAPP_INTR_CTSMIS 0x00000002 +#define BM_UARTAPP_INTR_RIMIS 0x00000001 + +#define HW_UARTAPP_DATA (0x00000060) + +#define BP_UARTAPP_DATA_DATA 0 +#define BM_UARTAPP_DATA_DATA 0xFFFFFFFF +#define BF_UARTAPP_DATA_DATA(v) (v) + +#define HW_UARTAPP_STAT (0x00000070) + +#define BM_UARTAPP_STAT_PRESENT 0x80000000 +#define BV_UARTAPP_STAT_PRESENT__UNAVAILABLE 0x0 +#define BV_UARTAPP_STAT_PRESENT__AVAILABLE 0x1 +#define BM_UARTAPP_STAT_HISPEED 0x40000000 +#define BV_UARTAPP_STAT_HISPEED__UNAVAILABLE 0x0 +#define BV_UARTAPP_STAT_HISPEED__AVAILABLE 0x1 +#define BM_UARTAPP_STAT_BUSY 0x20000000 +#define BM_UARTAPP_STAT_CTS 0x10000000 +#define BM_UARTAPP_STAT_TXFE 0x08000000 +#define BM_UARTAPP_STAT_RXFF 0x04000000 +#define BM_UARTAPP_STAT_TXFF 0x02000000 +#define BM_UARTAPP_STAT_RXFE 0x01000000 +#define BP_UARTAPP_STAT_RXBYTE_INVALID 20 +#define BM_UARTAPP_STAT_RXBYTE_INVALID 0x00F00000 +#define BF_UARTAPP_STAT_RXBYTE_INVALID(v) \ + (((v) << 20) & BM_UARTAPP_STAT_RXBYTE_INVALID) +#define BM_UARTAPP_STAT_OERR 0x00080000 +#define BM_UARTAPP_STAT_BERR 0x00040000 +#define BM_UARTAPP_STAT_PERR 0x00020000 +#define BM_UARTAPP_STAT_FERR 0x00010000 +#define BP_UARTAPP_STAT_RXCOUNT 0 +#define BM_UARTAPP_STAT_RXCOUNT 0x0000FFFF +#define BF_UARTAPP_STAT_RXCOUNT(v) \ + (((v) << 0) & BM_UARTAPP_STAT_RXCOUNT) + +#define HW_UARTAPP_DEBUG (0x00000080) + +#define BP_UARTAPP_DEBUG_RXIBAUD_DIV 16 +#define BM_UARTAPP_DEBUG_RXIBAUD_DIV 0xFFFF0000 +#define BF_UARTAPP_DEBUG_RXIBAUD_DIV(v) \ + (((v) << 16) & BM_UARTAPP_DEBUG_RXIBAUD_DIV) +#define BP_UARTAPP_DEBUG_RXFBAUD_DIV 10 +#define BM_UARTAPP_DEBUG_RXFBAUD_DIV 0x0000FC00 +#define BF_UARTAPP_DEBUG_RXFBAUD_DIV(v) \ + (((v) << 10) & BM_UARTAPP_DEBUG_RXFBAUD_DIV) +#define BP_UARTAPP_DEBUG_RSVD1 6 +#define BM_UARTAPP_DEBUG_RSVD1 0x000003C0 +#define BF_UARTAPP_DEBUG_RSVD1(v) \ + (((v) << 6) & BM_UARTAPP_DEBUG_RSVD1) +#define BM_UARTAPP_DEBUG_TXDMARUN 0x00000020 +#define BM_UARTAPP_DEBUG_RXDMARUN 0x00000010 +#define BM_UARTAPP_DEBUG_TXCMDEND 0x00000008 +#define BM_UARTAPP_DEBUG_RXCMDEND 0x00000004 +#define BM_UARTAPP_DEBUG_TXDMARQ 0x00000002 +#define BM_UARTAPP_DEBUG_RXDMARQ 0x00000001 + +#define HW_UARTAPP_VERSION (0x00000090) + +#define BP_UARTAPP_VERSION_MAJOR 24 +#define BM_UARTAPP_VERSION_MAJOR 0xFF000000 +#define BF_UARTAPP_VERSION_MAJOR(v) \ + (((v) << 24) & BM_UARTAPP_VERSION_MAJOR) +#define BP_UARTAPP_VERSION_MINOR 16 +#define BM_UARTAPP_VERSION_MINOR 0x00FF0000 +#define BF_UARTAPP_VERSION_MINOR(v) \ + (((v) << 16) & BM_UARTAPP_VERSION_MINOR) +#define BP_UARTAPP_VERSION_STEP 0 +#define BM_UARTAPP_VERSION_STEP 0x0000FFFF +#define BF_UARTAPP_VERSION_STEP(v) \ + (((v) << 0) & BM_UARTAPP_VERSION_STEP) + +#define HW_UARTAPP_AUTOBAUD (0x000000a0) + +#define BP_UARTAPP_AUTOBAUD_REFCHAR1 24 +#define BM_UARTAPP_AUTOBAUD_REFCHAR1 0xFF000000 +#define BF_UARTAPP_AUTOBAUD_REFCHAR1(v) \ + (((v) << 24) & BM_UARTAPP_AUTOBAUD_REFCHAR1) +#define BP_UARTAPP_AUTOBAUD_REFCHAR0 16 +#define BM_UARTAPP_AUTOBAUD_REFCHAR0 0x00FF0000 +#define BF_UARTAPP_AUTOBAUD_REFCHAR0(v) \ + (((v) << 16) & BM_UARTAPP_AUTOBAUD_REFCHAR0) +#define BP_UARTAPP_AUTOBAUD_RSVD1 5 +#define BM_UARTAPP_AUTOBAUD_RSVD1 0x0000FFE0 +#define BF_UARTAPP_AUTOBAUD_RSVD1(v) \ + (((v) << 5) & BM_UARTAPP_AUTOBAUD_RSVD1) +#define BM_UARTAPP_AUTOBAUD_UPDATE_TX 0x00000010 +#define BM_UARTAPP_AUTOBAUD_TWO_REF_CHARS 0x00000008 +#define BM_UARTAPP_AUTOBAUD_START_WITH_RUNBIT 0x00000004 +#define BM_UARTAPP_AUTOBAUD_START_BAUD_DETECT 0x00000002 +#define BM_UARTAPP_AUTOBAUD_BAUD_DETECT_ENABLE 0x00000001 +#endif /* __ARCH_ARM___UARTAPP_H */ diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index 7f2830709512..4087aff757bb 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -2092,7 +2092,7 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) */ if (uart_console(uport)) { uart_change_pm(state, 0); - uport->ops->set_termios(uport, &termios, NULL); + uport->ops->set_termios(uport, port->tty->termios, NULL); console_start(uport->cons); } |