Message ID | 1365750655-3530-1-git-send-email-b35083@freescale.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Fri, Apr 12, 2013 at 03:10:55PM +0800, Jingchang Lu wrote: > It adds Freescale Vybrid Family uart driver support > > Signed-off-by: Jingchang Lu <b35083@freescale.com> > --- > +++ b/drivers/tty/serial/mvf.c > @@ -0,0 +1,1246 @@ > +/* > + * Driver for Freescale Vybrid Family serial ports > + * > + * Based on drivers/tty/serial/imx.c > + * > + * Copyright 2012-2013 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Another FSF address. Please remove. > + * > + */ > + > +#if defined(CONFIG_SERIAL_MVF_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/platform_device.h> > +#include <linux/tty.h> > +#include <linux/tty_flip.h> > +#include <linux/serial_core.h> > +#include <linux/serial.h> > +#include <linux/clk.h> > +#include <linux/delay.h> > +#include <linux/rational.h> > +#include <linux/slab.h> > +#include <linux/dma-mapping.h> > +#include <linux/of.h> > +#include <linux/of_device.h> > +#include <linux/pinctrl/consumer.h> > +#include <linux/io.h> > +#include <asm/irq.h> > +#include <linux/platform_data/serial-imx.h> > +#include <linux/platform_data/dma-imx.h> > + > +/* All uart module registers for MVF is 8-bit width */ > +#define MXC_UARTBDH 0x00 /* Baud rate reg: high */ > +#define MXC_UARTBDL 0x01 /* Baud rate reg: low */ > +#define MXC_UARTCR1 0x02 /* Control reg 1 */ > +#define MXC_UARTCR2 0x03 /* Control reg 2 */ > +#define MXC_UARTSR1 0x04 /* Status reg 1 */ > +#define MXC_UARTSR2 0x05 /* Status reg 2 */ > +#define MXC_UARTCR3 0x06 /* Control reg 3 */ > +#define MXC_UARTDR 0x07 /* Data reg */ > +#define MXC_UARTMAR1 0x08 /* Match address reg 1 */ > +#define MXC_UARTMAR2 0x09 /* Match address reg 2 */ > +#define MXC_UARTCR4 0x0A /* Control reg 4 */ > +#define MXC_UARTCR5 0x0B /* Control reg 5 */ > +#define MXC_UARTEDR 0x0C /* Extended data reg */ > +#define MXC_UARTMODEM 0x0D /* Modem reg */ > +#define MXC_UARTIR 0x0E /* Infrared reg */ > +#define MXC_UARTPFIFO 0x10 /* FIFO parameter reg */ > +#define MXC_UARTCFIFO 0x11 /* FIFO control reg */ > +#define MXC_UARTSFIFO 0x12 /* FIFO status reg */ > +#define MXC_UARTTWFIFO 0x13 /* FIFO transmit watermark reg */ > +#define MXC_UARTTCFIFO 0x14 /* FIFO transmit count reg */ > +#define MXC_UARTRWFIFO 0x15 /* FIFO receive watermark reg */ > +#define MXC_UARTRCFIFO 0x16 /* FIFO receive count reg */ > +#define MXC_UARTC7816 0x18 /* 7816 control reg */ > +#define MXC_UARTIE7816 0x19 /* 7816 interrupt enable reg */ > +#define MXC_UARTIS7816 0x1A /* 7816 interrupt status reg */ > +#define MXC_UARTWP7816T0 0x1B /* 7816 wait parameter reg */ > +#define MXC_UARTWP7816T1 0x1B /* 7816 wait parameter reg */ > +#define MXC_UARTWN7816 0x1C /* 7816 wait N reg */ > +#define MXC_UARTWF7816 0x1D /* 7816 wait FD reg */ > +#define MXC_UARTET7816 0x1E /* 7816 error threshold reg */ > +#define MXC_UARTTL7816 0x1F /* 7816 transmit length reg */ > +#define MXC_UARTCR6 0x21 /* CEA709.1-B contrl reg */ > +#define MXC_UARTPCTH 0x22 /* CEA709.1-B packet cycle counter high */ > +#define MXC_UARTPCTL 0x23 /* CEA709.1-B packet cycle counter low */ > +#define MXC_UARTB1T 0x24 /* CEA709.1-B beta 1 time */ > +#define MXC_UARTSDTH 0x25 /* CEA709.1-B secondary delay timer high */ > +#define MXC_UARTSDTL 0x26 /* CEA709.1-B secondary delay timer low */ > +#define MXC_UARTPRE 0x27 /* CEA709.1-B preamble */ > +#define MXC_UARTTPL 0x28 /* CEA709.1-B transmit packet length */ > +#define MXC_UARTIE 0x29 /* CEA709.1-B transmit interrupt enable */ > +#define MXC_UARTSR3 0x2B /* CEA709.1-B status reg */ > +#define MXC_UARTSR4 0x2C /* CEA709.1-B status reg */ > +#define MXC_UARTRPL 0x2D /* CEA709.1-B received packet length */ > +#define MXC_UARTRPREL 0x2E /* CEA709.1-B received preamble length */ > +#define MXC_UARTCPW 0x2F /* CEA709.1-B collision pulse width */ > +#define MXC_UARTRIDT 0x30 /* CEA709.1-B receive indeterminate time */ > +#define MXC_UARTTIDT 0x31 /* CEA709.1-B transmit indeterminate time*/ > + > +/* Bit definations of BDH */ > +#define MXC_UARTBDH_LBKDIE 0x80 /* LIN break detect interrupt enable */ > +#define MXC_UARTBDH_RXEDGIE 0x40 /* RxD input Active edge interrupt enable*/ > +#define MXC_UARTBDH_SBR_MASK 0x1f /* Uart baud rate high 5-bits */ > +/* Bit definations of CR1 */ > +#define MXC_UARTCR1_LOOPS 0x80 /* Loop mode select */ > +#define MXC_UARTCR1_RSRC 0x20 /* Receiver source select */ > +#define MXC_UARTCR1_M 0x10 /* 9-bit 8-bit mode select */ > +#define MXC_UARTCR1_WAKE 0x08 /* Receiver wakeup method */ > +#define MXC_UARTCR1_ILT 0x04 /* Idle line type */ > +#define MXC_UARTCR1_PE 0x02 /* Parity enable */ > +#define MXC_UARTCR1_PT 0x01 /* Parity type */ > +/* Bit definations of CR2 */ > +#define MXC_UARTCR2_TIE 0x80 /* Tx interrupt or DMA request enable */ > +#define MXC_UARTCR2_TCIE 0x40 /* Transmission complete int enable */ > +#define MXC_UARTCR2_RIE 0x20 /* Rx full int or DMA request enable */ > +#define MXC_UARTCR2_ILIE 0x10 /* Idle line interrupt enable */ > +#define MXC_UARTCR2_TE 0x08 /* Transmitter enable */ > +#define MXC_UARTCR2_RE 0x04 /* Receiver enable */ > +#define MXC_UARTCR2_RWU 0x02 /* Receiver wakeup control */ > +#define MXC_UARTCR2_SBK 0x01 /* Send break */ > +/* Bit definations of SR1 */ > +#define MXC_UARTSR1_TDRE 0x80 /* Tx data reg empty */ > +#define MXC_UARTSR1_TC 0x40 /* Transmit complete */ > +#define MXC_UARTSR1_RDRF 0x20 /* Rx data reg full */ > +#define MXC_UARTSR1_IDLE 0x10 /* Idle line flag */ > +#define MXC_UARTSR1_OR 0x08 /* Receiver overrun */ > +#define MXC_UARTSR1_NF 0x04 /* Noise flag */ > +#define MXC_UARTSR1_FE 0x02 /* Frame error */ > +#define MXC_UARTSR1_PE 0x01 /* Parity error */ > +/* Bit definations of SR2 */ > +#define MXC_UARTSR2_LBKDIF 0x80 /* LIN brk detect interrupt flag */ > +#define MXC_UARTSR2_RXEDGIF 0x40 /* RxD pin active edge interrupt flag */ > +#define MXC_UARTSR2_MSBF 0x20 /* MSB first */ > +#define MXC_UARTSR2_RXINV 0x10 /* Receive data inverted */ > +#define MXC_UARTSR2_RWUID 0x08 /* Receive wakeup idle detect */ > +#define MXC_UARTSR2_BRK13 0x04 /* Break transmit character length */ > +#define MXC_UARTSR2_LBKDE 0x02 /* LIN break detection enable */ > +#define MXC_UARTSR2_RAF 0x01 /* Receiver active flag */ > +/* Bit definations of CR3 */ > +#define MXC_UARTCR3_R8 0x80 /* Received bit8, for 9-bit data format */ > +#define MXC_UARTCR3_T8 0x40 /* transmit bit8, for 9-bit data format */ > +#define MXC_UARTCR3_TXDIR 0x20 /* Tx pin direction in single-wire mode */ > +#define MXC_UARTCR3_TXINV 0x10 /* Transmit data inversion */ > +#define MXC_UARTCR3_ORIE 0x08 /* Overrun error interrupt enable */ > +#define MXC_UARTCR3_NEIE 0x04 /* Noise error interrupt enable */ > +#define MXC_UARTCR3_FEIE 0x02 /* Framing error interrupt enable */ > +#define MXC_UARTCR3_PEIE 0x01 /* Parity errror interrupt enable */ > +/* Bit definations of CR4 */ > +#define MXC_UARTCR4_MAEN1 0x80 /* Match address mode enable 1 */ > +#define MXC_UARTCR4_MAEN2 0x40 /* Match address mode enable 2 */ > +#define MXC_UARTCR4_M10 0x20 /* 10-bit mode select */ > +#define MXC_UARTCR4_BRFA_MASK 0x1F /* Baud rate fine adjust */ > +#define MXC_UARTCR4_BRFA_OFF 0 > +/* Bit definations of CR5 */ > +#define MXC_UARTCR5_TDMAS 0x80 /* Transmitter DMA select */ > +#define MXC_UARTCR5_RDMAS 0x20 /* Receiver DMA select */ > +/* Bit definations of Modem */ > +#define MXC_UARTMODEM_RXRTSE 0x08 /* Enable receiver request-to-send */ > +#define MXC_UARTMODEM_TXRTSPOL 0x04 /* Select transmitter RTS polarity */ > +#define MXC_UARTMODEM_TXRTSE 0x02 /* Enable transmitter request-to-send */ > +#define MXC_UARTMODEM_TXCTSE 0x01 /* Enable transmitter CTS clear-to-send */ > +/* Bit definations of EDR */ > +#define MXC_UARTEDR_NOISY 0x80 /* Current dataword received with noise */ > +#define MXC_UARTEDR_PARITYE 0x40 /* Dataword received with parity error */ > +/* Bit definations of Infrared reg(IR) */ > +#define MXC_UARTIR_IREN 0x04 /* Infrared enable */ > +#define MXC_UARTIR_TNP_MASK 0x03 /* Transmitter narrow pluse */ > +#define MXC_UARTIR_TNP_OFF 0 > +/* Bit definations of FIFO parameter reg */ > +#define MXC_UARTPFIFO_TXFE 0x80 /* Transmit fifo enable */ > +#define MXC_UARTPFIFO_TXFIFOSIZE_MASK 0x7 > +#define MXC_UARTPFIFO_TXFIFOSIZE_OFF 4 > +#define MXC_UARTPFIFO_RXFE 0x08 /* Receiver fifo enable */ > +#define MXC_UARTPFIFO_RXFIFOSIZE_MASK 0x7 > +#define MXC_UARTPFIFO_RXFIFOSIZE_OFF 0 > +/* Bit definations of FIFO control reg */ > +#define MXC_UARTCFIFO_TXFLUSH 0x80 /* Transmit FIFO/buffer flush */ > +#define MXC_UARTCFIFO_RXFLUSH 0x40 /* Receive FIFO/buffer flush */ > +#define MXC_UARTCFIFO_RXOFE 0x04 /* Receive fifo overflow INT enable */ > +#define MXC_UARTCFIFO_TXOFE 0x02 /* Transmit fifo overflow INT enable */ > +#define MXC_UARTCFIFO_RXUFE 0x01 /* Receive fifo underflow INT enable */ > +/* Bit definations of FIFO status reg */ > +#define MXC_UARTSFIFO_TXEMPT 0x80 /* Transmit fifo/buffer empty */ > +#define MXC_UARTSFIFO_RXEMPT 0x40 /* Receive fifo/buffer empty */ > +#define MXC_UARTSFIFO_RXOF 0x04 /* Rx buffer overflow flag */ > +#define MXC_UARTSFIFO_TXOF 0x02 /* Tx buffer overflow flag */ > +#define MXC_UARTSFIFO_RXUF 0x01 /* Rx buffer underflow flag */ > + > + > + > +#undef CONFIG_MVF_SERIAL_DMA Please remove. > +/* follow IMX dev node number */ > +#define SERIAL_IMX_MAJOR 207 > +#define MINOR_START 24 > +#define DEV_NAME "ttymxc" > +#define MAX_INTERNAL_IRQ MXC_INTERNAL_IRQS > + > +/* > + * This determines how often we check the modem status signals > + * for any change. They generally aren't connected to an IRQ > + * so we have to poll them. We also check immediately before > + * filling the TX fifo incase CTS has been dropped. > + */ > +#define MCTRL_TIMEOUT (250*HZ/1000) Add whitespaces left and right to operators: (250 * HZ / 1000) > + > +#define DRIVER_NAME "MVF-uart" > + > +#define UART_NR 6 > + > +struct mvf_port { > + struct uart_port port; > + unsigned int old_status; > + int txirq, rxirq, rtsirq; > + unsigned int have_rtscts:1; > + unsigned int use_dcedte:1; > + unsigned int use_irda:1; > + unsigned int irda_inv_rx:1; > + unsigned int irda_inv_tx:1; > + unsigned int fifo_en:1; /* enable FIFO mode */ > + unsigned int mark_en:1; /* enable Mark address match */ > + unsigned int format_9bits:1; /* 9bits data format */ > + unsigned short trcv_delay; /* transceiver delay */ > + unsigned char ma_addr; /* Match address */ > + struct clk *clk; > + > + unsigned int tx_fifo_size, rx_fifo_size; > + > + /* DMA fields */ > + int enable_dma; enable_dma is unused. Either implement it or remove it. > + unsigned long dma_tx_ch; /* configured eDMA channel */ > + struct imx_dma_data dma_data; > + struct dma_chan *dma_chan_rx, *dma_chan_tx; > + struct scatterlist rx_sgl, tx_sgl; > + void *rx_buf; > + unsigned char *tx_buf; > + unsigned int rx_bytes, tx_bytes; > + struct work_struct tsk_dma_rx, tsk_dma_tx; > + unsigned int dma_tx_nents; > + bool dma_is_rxing, dma_is_txing; > + wait_queue_head_t dma_wait; > +}; > + > +#ifdef CONFIG_IRDA > +#define USE_IRDA(sport) ((sport)->use_irda) > +#else > +#define USE_IRDA(sport) (0) > +#endif This is unused. Remove. also rtsirq and use_irda > +enum mvf_uart_type { > + MVF600_UART, > +}; > + > +struct mvf_uart_data { > + unsigned uts_reg; > + enum mvf_uart_type devtype; > +}; > + > +static struct mvf_uart_data mvf_uart_devdata[] = { > + [MVF600_UART] = { > + .devtype = MVF600_UART, > + }, > +}; > + > +static struct platform_device_id mvf_uart_devtype[] = { > + { > + .name = "mvf600-uart", > + .driver_data = (kernel_ulong_t) &mvf_uart_devdata[MVF600_UART], > + }, { > + /* sentinel */ > + } > +}; > +MODULE_DEVICE_TABLE(platform, mvf_uart_devtype); > + > +static struct of_device_id mvf_uart_dt_ids[] = { > + { > + .compatible = "fsl,mvf-uart", > + .data = &mvf_uart_devdata[MVF600_UART], > + }, > + { /* sentinel */ } > +}; > +MODULE_DEVICE_TABLE(of, mvf_uart_dt_ids); > + > +/* > + * interrupts disabled on entry > + */ > +static void mvf_stop_tx(struct uart_port *port) > +{ > + struct mvf_port *sport = (struct mvf_port *)port; > + unsigned char temp; > + > + temp = readb(sport->port.membase + MXC_UARTCR2); > + writeb(temp & ~(MXC_UARTCR2_TIE | MXC_UARTCR2_TCIE), > + sport->port.membase + MXC_UARTCR2); I find it more readable to do a: temp = readl(adr); temp &= ~(bla); writel(temp, adr); But that's just my 2 cents. > +} > + > +/* > + * interrupts disabled on entry > + */ > +static void mvf_stop_rx(struct uart_port *port) > +{ > + struct mvf_port *sport = (struct mvf_port *)port; > + unsigned char temp; > + > + /* if the DMA RX thread is running, wait for it to finish. */ > + if (sport->enable_dma && sport->dma_is_rxing) > + return; > + > + temp = readb(sport->port.membase + MXC_UARTCR2); > + writeb(temp & ~MXC_UARTCR2_RE, sport->port.membase + MXC_UARTCR2); > +} > + > + /* modem status update function */ > +static void mvf_enable_ms(struct uart_port *port) > +{ > +} > + > +static inline void mvf_transmit_buffer(struct mvf_port *sport) > +{ > + struct circ_buf *xmit = &sport->port.state->xmit; > + > + while (!uart_circ_empty(xmit) && (sport->fifo_en == 1 ? > + (readb(sport->port.membase + MXC_UARTTCFIFO) < sport->tx_fifo_size) : > + (readb(sport->port.membase + MXC_UARTSR1) & MXC_UARTSR1_TDRE) > + )) { Can you make this more readable? > + /* send out xmit->buf[xmit->tail] */ > + writeb(xmit->buf[xmit->tail], sport->port.membase + MXC_UARTDR); > + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); > + sport->port.icount.tx++; > + } > + > + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) > + uart_write_wakeup(&sport->port); > + > + if (uart_circ_empty(xmit)) > + mvf_stop_tx(&sport->port); > +} > + > +/* > + * interrupts disabled on entry > + */ > +static void mvf_start_tx(struct uart_port *port) > +{ > + struct mvf_port *sport = (struct mvf_port *)port; > + unsigned char temp; > + > + if (!sport->enable_dma) { > + temp = readb(sport->port.membase + MXC_UARTCR2); > + writeb(temp | MXC_UARTCR2_TIE, > + sport->port.membase + MXC_UARTCR2); > + } > + > + if (sport->enable_dma) { > + schedule_work(&sport->tsk_dma_tx); > + return; > + } > + > + if (readb(sport->port.membase + MXC_UARTSR1) & MXC_UARTSR1_TDRE) > + mvf_transmit_buffer(sport); > +} > + > + > +static irqreturn_t mvf_txint(int irq, void *dev_id) > +{ > + struct mvf_port *sport = dev_id; > + struct circ_buf *xmit = &sport->port.state->xmit; > + unsigned long flags; > + > + spin_lock_irqsave(&sport->port.lock, flags); > + if (sport->port.x_char) { > + /* Send next char */ > + writeb(sport->port.x_char, sport->port.membase + MXC_UARTDR); > + goto out; > + } > + > + if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { > + mvf_stop_tx(&sport->port); > + goto out; > + } > + > + mvf_transmit_buffer(sport); > + > + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) > + uart_write_wakeup(&sport->port); > + > +out: > + spin_unlock_irqrestore(&sport->port.lock, flags); > + return IRQ_HANDLED; > +} > + > +static irqreturn_t mvf_rxint(int irq, void *dev_id) > +{ > + struct mvf_port *sport = dev_id; > + unsigned int flg, ignored = 0; > + struct tty_port *port = &sport->port.state->port; > + unsigned long flags; > + unsigned char r8, rx, sr; > + > + spin_lock_irqsave(&sport->port.lock, flags); > + > + while (!(readb(sport->port.membase + MXC_UARTSFIFO) & > + MXC_UARTSFIFO_RXEMPT)) { > + flg = TTY_NORMAL; > + sport->port.icount.rx++; > + > + /* To clear the FE, OR, NF, FE, PE flags when set, > + * read SR1 then read DR > + */ > + sr = readb(sport->port.membase + MXC_UARTSR1); > + > + r8 = readb(sport->port.membase + MXC_UARTCR3) & MXC_UARTCR3_R8; > + rx = readb(sport->port.membase + MXC_UARTDR); > + > + if (sport->mark_en && > + (sport->format_9bits ? r8 : (rx == sport->ma_addr))) > + continue; > + > + if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx)) > + continue; > + if (sr & (MXC_UARTSR1_PE | MXC_UARTSR1_OR | MXC_UARTSR1_FE)) { > + if (sr & MXC_UARTSR1_PE) > + sport->port.icount.parity++; > + else if (sr & MXC_UARTSR1_FE) > + sport->port.icount.frame++; > + if (sr & MXC_UARTSR1_OR) > + sport->port.icount.overrun++; > + > + if (sr & sport->port.ignore_status_mask) { > + if (++ignored > 100) > + goto out; > + continue; > + } > + > + sr &= sport->port.read_status_mask; > + > + if (sr & MXC_UARTSR1_PE) > + flg = TTY_PARITY; > + else if (sr & MXC_UARTSR1_FE) > + flg = TTY_FRAME; > + if (sr & MXC_UARTSR1_OR) > + flg = TTY_OVERRUN; > + > +#ifdef SUPPORT_SYSRQ > + sport->port.sysrq = 0; > +#endif > + } > + > + tty_insert_flip_char(port, rx, flg); > + } > + > +out: > + spin_unlock_irqrestore(&sport->port.lock, flags); > + > + tty_flip_buffer_push(port); > + return IRQ_HANDLED; > +} > + > +static irqreturn_t mvf_int(int irq, void *dev_id) > +{ > + struct mvf_port *sport = dev_id; > + unsigned int sts; > + > + sts = readb(sport->port.membase + MXC_UARTSR1); > + > + if (sts & MXC_UARTSR1_RDRF) > + mvf_rxint(irq, dev_id); Wrong indention. > + > + if (sts & MXC_UARTSR1_TDRE && > + !(readb(sport->port.membase + MXC_UARTCR5) & > + MXC_UARTCR5_TDMAS)) > + mvf_txint(irq, dev_id); > + > + return IRQ_HANDLED; > +} > + > +/* > + * Return TIOCSER_TEMT when transmitter is not busy. > + */ > +static unsigned int mvf_tx_empty(struct uart_port *port) > +{ > + struct mvf_port *sport = (struct mvf_port *)port; > + > + return (readb(sport->port.membase + MXC_UARTSR1) & MXC_UARTSR1_TC) ? > + TIOCSER_TEMT : 0; > +} > + > +static unsigned int mvf_get_mctrl(struct uart_port *port) > +{ > + struct mvf_port *sport = (struct mvf_port *)port; > + unsigned int tmp = 0; > + unsigned char reg; > + > + reg = readb(sport->port.membase + MXC_UARTMODEM); > + if (reg & MXC_UARTMODEM_TXCTSE) > + tmp |= TIOCM_CTS; > + > + if (reg & MXC_UARTMODEM_RXRTSE) > + tmp |= TIOCM_RTS; > + > + return tmp; > +} > + > +static void mvf_set_mctrl(struct uart_port *port, unsigned int mctrl) > +{ > + struct mvf_port *sport = (struct mvf_port *)port; Do not cast to different struct typed. Use container_of instead. > + unsigned long temp; > + > + temp = readb(sport->port.membase + MXC_UARTMODEM) & > + ~(MXC_UARTMODEM_RXRTSE | MXC_UARTMODEM_TXCTSE); > + > + if (mctrl & TIOCM_RTS) > + temp |= MXC_UARTMODEM_RXRTSE; > + if (mctrl & TIOCM_CTS) > + temp |= MXC_UARTMODEM_TXCTSE; > + > + writeb(temp, sport->port.membase + MXC_UARTMODEM); > +} > + > +/* > + * Interrupts always disabled. > + */ > +static void mvf_break_ctl(struct uart_port *port, int break_state) > +{ > + struct mvf_port *sport = (struct mvf_port *)port; > + unsigned long flags; > + unsigned char temp; > + > + spin_lock_irqsave(&sport->port.lock, flags); > + > + temp = readb(sport->port.membase + MXC_UARTCR2) & ~MXC_UARTCR2_SBK; > + > + if (break_state != 0) > + temp |= MXC_UARTCR2_SBK; > + > + writeb(temp, sport->port.membase + MXC_UARTCR2); > + > + spin_unlock_irqrestore(&sport->port.lock, flags); > +} > + > +#define TXTL 2 > +#define RXTL 1 > + > +static int mvf_setup_watermark(struct mvf_port *sport, unsigned int mode) The mode argument is unused. > +{ > + unsigned char val, old_cr2, cr2; > + > + /* set receiver/transmitter trigger level. */ > + old_cr2 = cr2 = readb(sport->port.membase + MXC_UARTCR2); > + cr2 &= ~(MXC_UARTCR2_TIE | MXC_UARTCR2_TCIE | MXC_UARTCR2_TE | > + MXC_UARTCR2_RIE | MXC_UARTCR2_RE); > + writeb(cr2, sport->port.membase + MXC_UARTCR2); > + > + val = TXTL; > + writeb(val, sport->port.membase + MXC_UARTTWFIFO); > + val = RXTL; > + writeb(val, sport->port.membase + MXC_UARTRWFIFO); Please drop this additional 'val =' hoop. > + > + /* Enable Tx and Rx FIFO */ > + val = readb(sport->port.membase + MXC_UARTPFIFO); > + sport->tx_fifo_size = 0x1 << (((val >> MXC_UARTPFIFO_TXFIFOSIZE_OFF) & > + MXC_UARTPFIFO_TXFIFOSIZE_MASK) + 1); > + sport->rx_fifo_size = 0x1 << (((val >> MXC_UARTPFIFO_RXFIFOSIZE_OFF) & > + MXC_UARTPFIFO_RXFIFOSIZE_MASK) + 1); During init you hardcode the FIFO size to 32 byte. Can't you use these values instead? > + writeb(val | MXC_UARTPFIFO_TXFE | MXC_UARTPFIFO_RXFE, > + sport->port.membase + MXC_UARTPFIFO); > + > + /* Flush the Tx and Rx FIFO to a known state */ > + writeb(MXC_UARTCFIFO_TXFLUSH | MXC_UARTCFIFO_RXFLUSH, > + sport->port.membase + MXC_UARTCFIFO); > + > + /* restore CR2 */ > + writeb(old_cr2, sport->port.membase + MXC_UARTCR2); > + > + return 0; > +} The function always returns 0 and the return value is never checked, so make it return void. > + > + > +static int mvf_startup(struct uart_port *port) > +{ > + struct mvf_port *sport = (struct mvf_port *)port; > + int retval; > + unsigned long flags, temp; > + > + if (sport->fifo_en) > + mvf_setup_watermark(sport, 0); > + > + temp = readb(sport->port.membase + MXC_UARTCR2); > + > + writeb(temp & ~MXC_UARTCR2_RIE, sport->port.membase + MXC_UARTCR2); > + > + /* > + * Allocate the IRQ(s) > + * Vybrid chips only have one interrupt. > + */ > + retval = request_irq(sport->port.irq, mvf_int, 0, > + DRIVER_NAME, sport); Request you interrupt in the probe function so that you can use a devm_* function. > + if (retval) > + goto error_out1; > + > + /* Enable the DMA ops for uart. */ > + if (sport->enable_dma) { > + sport->dma_is_txing = 0; > + > + /* enable DMA request generation */ > + temp = readb(sport->port.membase + MXC_UARTCR5); > + temp |= MXC_UARTCR5_TDMAS; > + writeb(temp, sport->port.membase + MXC_UARTCR5); > + > + init_waitqueue_head(&sport->dma_wait); > + } > + > + spin_lock_irqsave(&sport->port.lock, flags); > + > + /* Finally, clear and enable interrupts */ > + temp = readb(sport->port.membase + MXC_UARTCR2); > + temp |= MXC_UARTCR2_RIE | MXC_UARTCR2_TIE; > + writeb(temp, sport->port.membase + MXC_UARTCR2); > + > + spin_unlock_irqrestore(&sport->port.lock, flags); > + > + return 0; > + > +error_out1: > + return retval; > +} > + > +static void mvf_shutdown(struct uart_port *port) > +{ > + struct mvf_port *sport = (struct mvf_port *)port; > + unsigned char temp; > + unsigned long flags; > + > + spin_lock_irqsave(&sport->port.lock, flags); > + temp = readb(sport->port.membase + MXC_UARTCR2); > + temp &= ~(MXC_UARTCR2_TE | MXC_UARTCR2_RE); > + writeb(temp, sport->port.membase + MXC_UARTCR2); > + spin_unlock_irqrestore(&sport->port.lock, flags); > + > + /* > + * Free the interrupts > + */ > + if (sport->txirq > 0) { > + if (!USE_IRDA(sport)) > + free_irq(sport->rtsirq, sport); > + free_irq(sport->txirq, sport); > + free_irq(sport->rxirq, sport); > + } else > + free_irq(sport->port.irq, sport); If one branch of a conditional statement needs braces add them to the other aswell. > + > + /* > + * Disable all interrupts, port and break condition. > + */ > + > + spin_lock_irqsave(&sport->port.lock, flags); > + temp = readb(sport->port.membase + MXC_UARTCR2); > + temp &= ~(MXC_UARTCR2_TIE | MXC_UARTCR2_TCIE | MXC_UARTCR2_RIE); > + writeb(temp, sport->port.membase + MXC_UARTCR2); > + > + spin_unlock_irqrestore(&sport->port.lock, flags); > +} > + > +static void > +mvf_set_termios(struct uart_port *port, struct ktermios *termios, > + struct ktermios *old) > +{ > + struct mvf_port *sport = (struct mvf_port *)port; > + unsigned long flags; > + unsigned char cr1, old_cr1, old_cr2, cr4, bdh, modem; > + unsigned int baud; > + unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; > + unsigned int sbr, brfa; > + > + cr1 = old_cr1 = readb(sport->port.membase + MXC_UARTCR1); > + old_cr2 = readb(sport->port.membase + MXC_UARTCR2); > + cr4 = readb(sport->port.membase + MXC_UARTCR4); > + bdh = readb(sport->port.membase + MXC_UARTBDH); > + modem = readb(sport->port.membase + MXC_UARTMODEM); > + /* > + * If we don't support modem control lines, don't allow > + * these to be set. > + */ > + if (0) { > + termios->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR); > + termios->c_cflag |= CLOCAL; > + } Dead code. Please remove. > + > + /* > + * We only support CS8 and CS7,but CS7 must enable PE. > + * supported mode: > + * - (7,e/o,1) > + * - (8,n,1) > + * - (8,m/s,1) > + * - (8,e/o,1) > + */ > + while ((termios->c_cflag & CSIZE) != CS8 && > + (termios->c_cflag & CSIZE) != CS7) { > + termios->c_cflag &= ~CSIZE; > + termios->c_cflag |= old_csize; > + old_csize = CS8; > + } > + > + if ((termios->c_cflag & CSIZE) == CS8 || > + (termios->c_cflag & CSIZE) == CS7) > + cr1 = old_cr1 & ~MXC_UARTCR1_M; > + > + if (termios->c_cflag & CMSPAR) { > + if ((termios->c_cflag & CSIZE) != CS8) { > + termios->c_cflag &= ~CSIZE; > + termios->c_cflag |= CS8; > + } > + cr1 |= MXC_UARTCR1_M; > + } > + > + if (termios->c_cflag & CRTSCTS) { > + if (sport->have_rtscts) > + modem |= (MXC_UARTMODEM_RXRTSE | MXC_UARTMODEM_TXCTSE); > + > + } else { > + termios->c_cflag &= ~CRTSCTS; > + modem &= ~(MXC_UARTMODEM_RXRTSE | MXC_UARTMODEM_TXCTSE); > + } > + > + if (termios->c_cflag & CSTOPB) > + termios->c_cflag &= ~CSTOPB; > + > + /* parity must enable when CS7 to match 8-bits format */ > + if ((termios->c_cflag & CSIZE) == CS7) > + termios->c_cflag |= PARENB; > + > + if ((termios->c_cflag & PARENB)) { > + if (termios->c_cflag & CMSPAR) { > + cr1 &= ~MXC_UARTCR1_PE; > + cr1 |= MXC_UARTCR1_M; > + sport->format_9bits = 1; > + } else { > + cr1 |= MXC_UARTCR1_PE; > + if ((termios->c_cflag & CSIZE) == CS8) > + cr1 |= MXC_UARTCR1_M; > + if (termios->c_cflag & PARODD) > + cr1 |= MXC_UARTCR1_PT; > + else > + cr1 &= ~MXC_UARTCR1_PT; > + } > + } > + > + /* > + * Ask the core to calculate the divisor for us. > + */ > + baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16); > + > + spin_lock_irqsave(&sport->port.lock, flags); > + > + sport->port.read_status_mask = 0; > + if (termios->c_iflag & INPCK) > + sport->port.read_status_mask |= > + (MXC_UARTSR1_FE | MXC_UARTSR1_PE); > + if (termios->c_iflag & (BRKINT | PARMRK)) > + sport->port.read_status_mask |= MXC_UARTSR1_FE; > + > + /* > + * Characters to ignore > + */ > + sport->port.ignore_status_mask = 0; > + if (termios->c_iflag & IGNPAR) > + sport->port.ignore_status_mask |= MXC_UARTSR1_PE; > + if (termios->c_iflag & IGNBRK) { > + sport->port.ignore_status_mask |= MXC_UARTSR1_FE; > + /* > + * If we're ignoring parity and break indicators, > + * ignore overruns too (for real raw support). > + */ > + if (termios->c_iflag & IGNPAR) > + sport->port.ignore_status_mask |= MXC_UARTSR1_OR; > + } > + > + /* > + * Update the per-port timeout. > + */ > + uart_update_timeout(port, termios->c_cflag, baud); > + > + /* wait transmit engin complete */ > + while (!(readb(sport->port.membase + MXC_UARTSR1) & MXC_UARTSR1_TC)) > + barrier(); > + > + writeb(old_cr2 & ~(MXC_UARTCR2_TE | MXC_UARTCR2_RE), > + sport->port.membase + MXC_UARTCR2); > + > + /* disable transmit and receive */ > + sbr = sport->port.uartclk / (16 * baud); > + brfa = ((sport->port.uartclk - (16 * sbr * baud)) * 2)/baud; > + > + bdh &= ~MXC_UARTBDH_SBR_MASK; > + bdh |= (sbr >> 8) & 0x1F; > + > + cr4 &= ~MXC_UARTCR4_BRFA_MASK; > + brfa &= MXC_UARTCR4_BRFA_MASK; > + writeb(cr4 | brfa, sport->port.membase + MXC_UARTCR4); > + writeb(bdh, sport->port.membase + MXC_UARTBDH); > + writeb(sbr & 0xFF, sport->port.membase + MXC_UARTBDL); > + writeb(cr1, sport->port.membase + MXC_UARTCR1); > + writeb(modem, sport->port.membase + MXC_UARTMODEM); > + > + /* restore control register */ > + writeb(old_cr2, sport->port.membase + MXC_UARTCR2); > + > + spin_unlock_irqrestore(&sport->port.lock, flags); > +} > + > +static const char *mvf_type(struct uart_port *port) > +{ > + struct mvf_port *sport = (struct mvf_port *)port; > + > + return sport->port.type == PORT_MVF ? "MVF" : NULL; > +} > + > +/* > + * Release the memory region(s) being used by 'port'. > + */ > +static void mvf_release_port(struct uart_port *port) > +{ > + struct platform_device *pdev = to_platform_device(port->dev); > + struct resource *mmres; > + > + mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + release_mem_region(mmres->start, mmres->end - mmres->start + 1); > +} > + > +/* > + * Request the memory region(s) being used by 'port'. > + */ > +static int mvf_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, > + "imx-uart"); Use resource_size() > + > + return ret ? 0 : -EBUSY; > +} > + > +/* > + * Configure/autoconfigure the port. > + */ > +static void mvf_config_port(struct uart_port *port, int flags) > +{ > + struct mvf_port *sport = (struct mvf_port *)port; > + > + if (flags & UART_CONFIG_TYPE && > + mvf_request_port(&sport->port) == 0) > + sport->port.type = PORT_MVF; > +} > + > +/* > + * Verify the new serial_struct (for TIOCSSERIAL). > + * The only change we allow are to the flags and type, and > + * even then only between PORT_MVF and PORT_UNKNOWN > + */ > +static int > +mvf_verify_port(struct uart_port *port, struct serial_struct *ser) > +{ > + struct mvf_port *sport = (struct mvf_port *)port; > + int ret = 0; > + > + if (ser->type != PORT_UNKNOWN && ser->type != PORT_MVF) > + ret = -EINVAL; > + if (sport->port.irq != ser->irq) > + ret = -EINVAL; > + if (ser->io_type != UPIO_MEM) > + ret = -EINVAL; > + if (sport->port.uartclk / 16 != ser->baud_base) > + ret = -EINVAL; > + if (sport->port.iobase != ser->port) > + ret = -EINVAL; > + if (ser->hub6 != 0) > + ret = -EINVAL; > + return ret; > +} > + > + > +static struct uart_ops mvf_pops = { > + .tx_empty = mvf_tx_empty, > + .set_mctrl = mvf_set_mctrl, > + .get_mctrl = mvf_get_mctrl, > + .stop_tx = mvf_stop_tx, > + .start_tx = mvf_start_tx, > + .stop_rx = mvf_stop_rx, > + .enable_ms = mvf_enable_ms, > + .break_ctl = mvf_break_ctl, > + .startup = mvf_startup, > + .shutdown = mvf_shutdown, > + .set_termios = mvf_set_termios, > + .type = mvf_type, > + .release_port = mvf_release_port, > + .request_port = mvf_request_port, > + .config_port = mvf_config_port, > + .verify_port = mvf_verify_port, > +}; > + > +static struct mvf_port *mvf_ports[UART_NR]; > + > +#ifdef CONFIG_SERIAL_MVF_CONSOLE > +static void mvf_console_putchar(struct uart_port *port, int ch) > +{ > + struct mvf_port *sport = (struct mvf_port *)port; > + > + while (!(readb(sport->port.membase + MXC_UARTSR1) & MXC_UARTSR1_TDRE)) > + barrier(); > + > + writeb(ch, sport->port.membase + MXC_UARTDR); > +} > + > +/* > + * Interrupts are disabled on entering > + */ You don't care since you are locking the device anyway. > +static void > +mvf_console_write(struct console *co, const char *s, unsigned int count) > +{ > + struct mvf_port *sport = mvf_ports[co->index]; > + unsigned int old_cr2, cr2; > + unsigned long flags; > + > + spin_lock_irqsave(&sport->port.lock, flags); > + /* > + * First, save UCR1/2 and then disable interrupts > + */ The comment seems wrong. > + cr2 = old_cr2 = readb(sport->port.membase + MXC_UARTCR2); > + > + cr2 |= (MXC_UARTCR2_TE | MXC_UARTCR2_RE); > + cr2 &= ~(MXC_UARTCR2_TIE | MXC_UARTCR2_TCIE | MXC_UARTCR2_RIE); > + > + writeb(cr2, sport->port.membase + MXC_UARTCR2); > + > + uart_console_write(&sport->port, s, count, mvf_console_putchar); > + > + /* > + * wait for transmitter finish complete and restore CR2 > + */ > + while (!(readb(sport->port.membase + MXC_UARTSR1) & MXC_UARTSR1_TC)) > + ; > + > + writeb(old_cr2, sport->port.membase + MXC_UARTCR2); > + spin_unlock_irqrestore(&sport->port.lock, flags); > +} > + > +/* > + * If the port was already initialised (eg, by a boot loader), > + * try to determine the current setup. > + */ > +static void __init > +mvf_console_get_options(struct mvf_port *sport, int *baud, > + int *parity, int *bits) > +{ > + > + if (readb(sport->port.membase + MXC_UARTCR2) & > + (MXC_UARTCR2_TE | MXC_UARTCR2)) { Bail out early here by testing for the opposite. You'll save an indention level below. > + /* ok, the port was enabled */ > + unsigned char cr1, bdh, bdl, brfa; > + unsigned int sbr, uartclk; > + unsigned int baud_raw; > + > + cr1 = readb(sport->port.membase + MXC_UARTCR1); > + > + *parity = 'n'; > + if (cr1 & MXC_UARTCR1_PE) { > + if (cr1 & MXC_UARTCR1_PT) > + *parity = 'o'; > + else > + *parity = 'e'; > + } > + > + if (cr1 & MXC_UARTCR1_M) > + *bits = 9; > + else > + *bits = 8; > + > + bdh = readb(sport->port.membase + MXC_UARTBDH) & > + MXC_UARTBDH_SBR_MASK; > + bdl = readb(sport->port.membase + MXC_UARTBDL); > + sbr = bdh; > + sbr <<= 8; > + sbr |= bdl; > + brfa = readb(sport->port.membase + MXC_UARTCR4) & > + MXC_UARTCR4_BRFA_MASK; > + uartclk = clk_get_rate(sport->clk); > + /* > + * Baud = mod_clk/(16*(sbr[13]+(brfa)/32) > + */ > + baud_raw = uartclk/(16 * (sbr + brfa/32)); Add whitespaces left and right to operators. > + > + if (*baud != baud_raw) > + printk(KERN_INFO "Serial: Console IMX " > + "rounded baud rate from %d to %d\n", IMX? > + baud_raw, *baud); > + } > +} > + > +static int __init > +mvf_console_setup(struct console *co, char *options) > +{ > + struct mvf_port *sport; > + 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 == -1 || co->index >= ARRAY_SIZE(mvf_ports)) > + co->index = 0; > + sport = mvf_ports[co->index]; > + > + if (sport == NULL) > + return -ENODEV; > + > + if (options) > + uart_parse_options(options, &baud, &parity, &bits, &flow); > + else > + mvf_console_get_options(sport, &baud, &parity, &bits); > + > + if (sport->fifo_en == 1) > + mvf_setup_watermark(sport, 0); > + > + return uart_set_options(&sport->port, co, baud, parity, bits, flow); > +} > + > +static struct uart_driver mvf_reg; > +static struct console mvf_console = { > + .name = DEV_NAME, > + .write = mvf_console_write, > + .device = uart_console_device, > + .setup = mvf_console_setup, > + .flags = CON_PRINTBUFFER, > + .index = -1, > + .data = &mvf_reg, > +}; > + > +#define MVF_CONSOLE (&mvf_console) > +#else > +#define MVF_CONSOLE NULL > +#endif > + > +static struct uart_driver mvf_reg = { > + .owner = THIS_MODULE, > + .driver_name = DRIVER_NAME, > + .dev_name = DEV_NAME, > + .major = SERIAL_IMX_MAJOR, > + .minor = MINOR_START, > + .nr = ARRAY_SIZE(mvf_ports), > + .cons = MVF_CONSOLE, > +}; > + > +static int serial_mvf_suspend(struct platform_device *dev, pm_message_t state) > +{ > + struct mvf_port *sport = platform_get_drvdata(dev); > + > + /* Enable UART wakeup */ > + > + if (sport) > + uart_suspend_port(&mvf_reg, &sport->port); unnecessary test. > + > + return 0; > +} > + > +static int serial_mvf_resume(struct platform_device *dev) > +{ > + struct mvf_port *sport = platform_get_drvdata(dev); > + > + if (sport) > + uart_resume_port(&mvf_reg, &sport->port); ditto. > + > + /* Disable UART wakeup */ > + > + return 0; > +} > + > +static int serial_mvf_probe_dt(struct mvf_port *sport, > + struct platform_device *pdev) > +{ > + struct device_node *np = pdev->dev.of_node; > + int ret; > + > + if (!np) > + /* no device tree device */ > + return 1; > + > + ret = of_alias_get_id(np, "serial"); > + if (ret < 0) { > + dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret); > + return ret; > + } > + sport->port.line = ret; > + > + if (of_get_property(np, "fsl,uart-fifo-mode", NULL)) > + sport->fifo_en = 1; Why is this configurable? Everybody wants FIFO support, right? > + > + return 0; > +} > + > +static int serial_mvf_probe(struct platform_device *pdev) > +{ > + struct mvf_port *sport; > + struct imxuart_platform_data *pdata; > + void __iomem *base; > + int ret = 0; > + struct resource *res; > + > + sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL); > + if (!sport) > + return -ENOMEM; > + > + pdev->dev.coherent_dma_mask = 0; > + > + ret = serial_mvf_probe_dt(sport, pdev); > + if (ret < 0) > + return ret ; > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!res) > + return -ENODEV; > + > + base = devm_ioremap(&pdev->dev, res->start, PAGE_SIZE); > + if (!base) > + return -ENOMEM; devm_request_and_ioremap? > + > + sport->port.dev = &pdev->dev; > + sport->port.mapbase = res->start; > + sport->port.membase = base; > + sport->port.type = PORT_MVF, > + sport->port.iotype = UPIO_MEM; > + sport->port.irq = platform_get_irq(pdev, 0); > + sport->port.fifosize = 32; > + sport->port.ops = &mvf_pops; > + sport->port.flags = UPF_BOOT_AUTOCONF; > + if (pdev->id >= 0) > + sport->port.line = pdev->id; > + > + sport->clk = devm_clk_get(&pdev->dev, "ipg"); > + if (IS_ERR(sport->clk)) { > + ret = PTR_ERR(sport->clk); > + dev_err(&pdev->dev, "failed to uart clk: %d\n", ret); > + return ret; > + } > + > + clk_prepare_enable(sport->clk); > + > + sport->port.uartclk = clk_get_rate(sport->clk); > + > + mvf_ports[sport->port.line] = sport; > + > + pdata = pdev->dev.platform_data; > + > + /* all uarts support hardware RTS/CTS */ > + sport->have_rtscts = 1; This should either be configurable from the devicetree since not all UARTs on all boards have routed the rts/cts pins out, or you should not even have this variable. > + > + if (pdata && pdata->init) { > + ret = pdata->init(pdev); > + if (ret) > + goto clkput; > + } No. Drop this. > + > + ret = uart_add_one_port(&mvf_reg, &sport->port); > + > + if (ret) > + goto deinit; > + platform_set_drvdata(pdev, &sport->port); This should be done *before* registering the device. > + > + return 0; > +deinit: > + if (pdata && pdata->exit) > + pdata->exit(pdev); > +clkput: > + clk_disable_unprepare(sport->clk); > + > + return ret; > +} > + > +static int serial_mvf_remove(struct platform_device *pdev) > +{ > + struct imxuart_platform_data *pdata; > + struct mvf_port *sport = platform_get_drvdata(pdev); > + > + pdata = pdev->dev.platform_data; > + > + platform_set_drvdata(pdev, NULL); > + > + if (sport) > + uart_remove_one_port(&mvf_reg, &sport->port); > + > + clk_disable_unprepare(sport->clk); > + > + if (pdata && pdata->exit) > + pdata->exit(pdev); > + > + return 0; > +} > + > +static struct platform_driver serial_mvf_driver = { > + .probe = serial_mvf_probe, > + .remove = serial_mvf_remove, > + > + .suspend = serial_mvf_suspend, > + .resume = serial_mvf_resume, > + .id_table = mvf_uart_devtype, > + .driver = { > + .name = "mvf-uart", > + .owner = THIS_MODULE, > + .of_match_table = mvf_uart_dt_ids, > + }, > +}; > + > +static int __init mvf_serial_init(void) > +{ > + int ret; > + > + pr_info("Serial: Vybrid Family driver\n"); > + > + ret = uart_register_driver(&mvf_reg); > + if (ret) > + return ret; > + > + ret = platform_driver_register(&serial_mvf_driver); > + if (ret != 0) > + uart_unregister_driver(&mvf_reg); > + > + return 0; > +} > + > +static void __exit mvf_serial_exit(void) > +{ > + platform_driver_unregister(&serial_mvf_driver); > + uart_unregister_driver(&mvf_reg); > +} > + > +module_init(mvf_serial_init); > +module_exit(mvf_serial_exit); > + > +MODULE_DESCRIPTION("Vybrid Family serial port driver"); > +MODULE_LICENSE("GPL"); > diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h > index 74c2bf7..ffb7192 100644 > --- a/include/uapi/linux/serial_core.h > +++ b/include/uapi/linux/serial_core.h > @@ -226,4 +226,7 @@ > /* Rocketport EXPRESS/INFINITY */ > #define PORT_RP2 102 > > +/* Freescale Vybrid uart */ > +#define PORT_MVF 103 > + > #endif /* _UAPILINUX_SERIAL_CORE_H */ > -- > 1.8.0 > > > > _______________________________________________ > linux-arm-kernel mailing list > linux-arm-kernel@lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel >
On Fri, Apr 12, 2013 at 4:10 AM, Jingchang Lu <b35083@freescale.com> wrote: > It adds Freescale Vybrid Family uart driver support > > Signed-off-by: Jingchang Lu <b35083@freescale.com> > --- > drivers/tty/serial/Kconfig | 17 + > drivers/tty/serial/Makefile | 1 + > drivers/tty/serial/mvf.c | 1246 ++++++++++++++++++++++++++++++++++++++ > include/uapi/linux/serial_core.h | 3 + > 4 files changed, 1267 insertions(+) > create mode 100644 drivers/tty/serial/mvf.c Would it be possible to add Vybrid support into the existing imx serial port?
On Sat, Apr 13, 2013 at 05:41:26PM -0300, Fabio Estevam wrote: > On Fri, Apr 12, 2013 at 4:10 AM, Jingchang Lu <b35083@freescale.com> wrote: > > It adds Freescale Vybrid Family uart driver support > > > > Signed-off-by: Jingchang Lu <b35083@freescale.com> > > --- > > drivers/tty/serial/Kconfig | 17 + > > drivers/tty/serial/Makefile | 1 + > > drivers/tty/serial/mvf.c | 1246 ++++++++++++++++++++++++++++++++++++++ > > include/uapi/linux/serial_core.h | 3 + > > 4 files changed, 1267 insertions(+) > > create mode 100644 drivers/tty/serial/mvf.c > > Would it be possible to add Vybrid support into the existing imx serial port? That's completely different hardware. Sascha
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 7e7006f..ab6dc27 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -574,6 +574,23 @@ config SERIAL_IMX_CONSOLE your boot loader (lilo or loadlin) about how to pass options to the kernel at boot time.) +config SERIAL_MVF + bool "Freescale Vybrid serial port support" + depends on ARCH_MVF + select SERIAL_CORE + select RATIONAL + help + If you have a machine based on a Freescale Vybrid CPU you + can enable its onboard serial port by enabling this option. + +config SERIAL_MVF_CONSOLE + bool "Console on Vybrid serial port" + depends on SERIAL_MVF + select SERIAL_CORE_CONSOLE + help + If you have enabled the serial port on the Freescale Vybrid family + CPU you can make it the console by answering Y to this option. + config SERIAL_UARTLITE tristate "Xilinx uartlite serial port support" depends on PPC32 || MICROBLAZE || MFD_TIMBERDALE diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index eedfec4..be2ed68 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_SERIAL_SH_SCI) += sh-sci.o obj-$(CONFIG_SERIAL_SGI_L1_CONSOLE) += sn_console.o obj-$(CONFIG_SERIAL_CPM) += cpm_uart/ obj-$(CONFIG_SERIAL_IMX) += imx.o +obj-$(CONFIG_SERIAL_MVF) += mvf.o obj-$(CONFIG_SERIAL_MPC52xx) += mpc52xx_uart.o obj-$(CONFIG_SERIAL_ICOM) += icom.o obj-$(CONFIG_SERIAL_M32R_SIO) += m32r_sio.o diff --git a/drivers/tty/serial/mvf.c b/drivers/tty/serial/mvf.c new file mode 100644 index 0000000..b3b7ffd --- /dev/null +++ b/drivers/tty/serial/mvf.c @@ -0,0 +1,1246 @@ +/* + * Driver for Freescale Vybrid Family serial ports + * + * Based on drivers/tty/serial/imx.c + * + * Copyright 2012-2013 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#if defined(CONFIG_SERIAL_MVF_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/platform_device.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial_core.h> +#include <linux/serial.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/rational.h> +#include <linux/slab.h> +#include <linux/dma-mapping.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/pinctrl/consumer.h> +#include <linux/io.h> +#include <asm/irq.h> +#include <linux/platform_data/serial-imx.h> +#include <linux/platform_data/dma-imx.h> + +/* All uart module registers for MVF is 8-bit width */ +#define MXC_UARTBDH 0x00 /* Baud rate reg: high */ +#define MXC_UARTBDL 0x01 /* Baud rate reg: low */ +#define MXC_UARTCR1 0x02 /* Control reg 1 */ +#define MXC_UARTCR2 0x03 /* Control reg 2 */ +#define MXC_UARTSR1 0x04 /* Status reg 1 */ +#define MXC_UARTSR2 0x05 /* Status reg 2 */ +#define MXC_UARTCR3 0x06 /* Control reg 3 */ +#define MXC_UARTDR 0x07 /* Data reg */ +#define MXC_UARTMAR1 0x08 /* Match address reg 1 */ +#define MXC_UARTMAR2 0x09 /* Match address reg 2 */ +#define MXC_UARTCR4 0x0A /* Control reg 4 */ +#define MXC_UARTCR5 0x0B /* Control reg 5 */ +#define MXC_UARTEDR 0x0C /* Extended data reg */ +#define MXC_UARTMODEM 0x0D /* Modem reg */ +#define MXC_UARTIR 0x0E /* Infrared reg */ +#define MXC_UARTPFIFO 0x10 /* FIFO parameter reg */ +#define MXC_UARTCFIFO 0x11 /* FIFO control reg */ +#define MXC_UARTSFIFO 0x12 /* FIFO status reg */ +#define MXC_UARTTWFIFO 0x13 /* FIFO transmit watermark reg */ +#define MXC_UARTTCFIFO 0x14 /* FIFO transmit count reg */ +#define MXC_UARTRWFIFO 0x15 /* FIFO receive watermark reg */ +#define MXC_UARTRCFIFO 0x16 /* FIFO receive count reg */ +#define MXC_UARTC7816 0x18 /* 7816 control reg */ +#define MXC_UARTIE7816 0x19 /* 7816 interrupt enable reg */ +#define MXC_UARTIS7816 0x1A /* 7816 interrupt status reg */ +#define MXC_UARTWP7816T0 0x1B /* 7816 wait parameter reg */ +#define MXC_UARTWP7816T1 0x1B /* 7816 wait parameter reg */ +#define MXC_UARTWN7816 0x1C /* 7816 wait N reg */ +#define MXC_UARTWF7816 0x1D /* 7816 wait FD reg */ +#define MXC_UARTET7816 0x1E /* 7816 error threshold reg */ +#define MXC_UARTTL7816 0x1F /* 7816 transmit length reg */ +#define MXC_UARTCR6 0x21 /* CEA709.1-B contrl reg */ +#define MXC_UARTPCTH 0x22 /* CEA709.1-B packet cycle counter high */ +#define MXC_UARTPCTL 0x23 /* CEA709.1-B packet cycle counter low */ +#define MXC_UARTB1T 0x24 /* CEA709.1-B beta 1 time */ +#define MXC_UARTSDTH 0x25 /* CEA709.1-B secondary delay timer high */ +#define MXC_UARTSDTL 0x26 /* CEA709.1-B secondary delay timer low */ +#define MXC_UARTPRE 0x27 /* CEA709.1-B preamble */ +#define MXC_UARTTPL 0x28 /* CEA709.1-B transmit packet length */ +#define MXC_UARTIE 0x29 /* CEA709.1-B transmit interrupt enable */ +#define MXC_UARTSR3 0x2B /* CEA709.1-B status reg */ +#define MXC_UARTSR4 0x2C /* CEA709.1-B status reg */ +#define MXC_UARTRPL 0x2D /* CEA709.1-B received packet length */ +#define MXC_UARTRPREL 0x2E /* CEA709.1-B received preamble length */ +#define MXC_UARTCPW 0x2F /* CEA709.1-B collision pulse width */ +#define MXC_UARTRIDT 0x30 /* CEA709.1-B receive indeterminate time */ +#define MXC_UARTTIDT 0x31 /* CEA709.1-B transmit indeterminate time*/ + +/* Bit definations of BDH */ +#define MXC_UARTBDH_LBKDIE 0x80 /* LIN break detect interrupt enable */ +#define MXC_UARTBDH_RXEDGIE 0x40 /* RxD input Active edge interrupt enable*/ +#define MXC_UARTBDH_SBR_MASK 0x1f /* Uart baud rate high 5-bits */ +/* Bit definations of CR1 */ +#define MXC_UARTCR1_LOOPS 0x80 /* Loop mode select */ +#define MXC_UARTCR1_RSRC 0x20 /* Receiver source select */ +#define MXC_UARTCR1_M 0x10 /* 9-bit 8-bit mode select */ +#define MXC_UARTCR1_WAKE 0x08 /* Receiver wakeup method */ +#define MXC_UARTCR1_ILT 0x04 /* Idle line type */ +#define MXC_UARTCR1_PE 0x02 /* Parity enable */ +#define MXC_UARTCR1_PT 0x01 /* Parity type */ +/* Bit definations of CR2 */ +#define MXC_UARTCR2_TIE 0x80 /* Tx interrupt or DMA request enable */ +#define MXC_UARTCR2_TCIE 0x40 /* Transmission complete int enable */ +#define MXC_UARTCR2_RIE 0x20 /* Rx full int or DMA request enable */ +#define MXC_UARTCR2_ILIE 0x10 /* Idle line interrupt enable */ +#define MXC_UARTCR2_TE 0x08 /* Transmitter enable */ +#define MXC_UARTCR2_RE 0x04 /* Receiver enable */ +#define MXC_UARTCR2_RWU 0x02 /* Receiver wakeup control */ +#define MXC_UARTCR2_SBK 0x01 /* Send break */ +/* Bit definations of SR1 */ +#define MXC_UARTSR1_TDRE 0x80 /* Tx data reg empty */ +#define MXC_UARTSR1_TC 0x40 /* Transmit complete */ +#define MXC_UARTSR1_RDRF 0x20 /* Rx data reg full */ +#define MXC_UARTSR1_IDLE 0x10 /* Idle line flag */ +#define MXC_UARTSR1_OR 0x08 /* Receiver overrun */ +#define MXC_UARTSR1_NF 0x04 /* Noise flag */ +#define MXC_UARTSR1_FE 0x02 /* Frame error */ +#define MXC_UARTSR1_PE 0x01 /* Parity error */ +/* Bit definations of SR2 */ +#define MXC_UARTSR2_LBKDIF 0x80 /* LIN brk detect interrupt flag */ +#define MXC_UARTSR2_RXEDGIF 0x40 /* RxD pin active edge interrupt flag */ +#define MXC_UARTSR2_MSBF 0x20 /* MSB first */ +#define MXC_UARTSR2_RXINV 0x10 /* Receive data inverted */ +#define MXC_UARTSR2_RWUID 0x08 /* Receive wakeup idle detect */ +#define MXC_UARTSR2_BRK13 0x04 /* Break transmit character length */ +#define MXC_UARTSR2_LBKDE 0x02 /* LIN break detection enable */ +#define MXC_UARTSR2_RAF 0x01 /* Receiver active flag */ +/* Bit definations of CR3 */ +#define MXC_UARTCR3_R8 0x80 /* Received bit8, for 9-bit data format */ +#define MXC_UARTCR3_T8 0x40 /* transmit bit8, for 9-bit data format */ +#define MXC_UARTCR3_TXDIR 0x20 /* Tx pin direction in single-wire mode */ +#define MXC_UARTCR3_TXINV 0x10 /* Transmit data inversion */ +#define MXC_UARTCR3_ORIE 0x08 /* Overrun error interrupt enable */ +#define MXC_UARTCR3_NEIE 0x04 /* Noise error interrupt enable */ +#define MXC_UARTCR3_FEIE 0x02 /* Framing error interrupt enable */ +#define MXC_UARTCR3_PEIE 0x01 /* Parity errror interrupt enable */ +/* Bit definations of CR4 */ +#define MXC_UARTCR4_MAEN1 0x80 /* Match address mode enable 1 */ +#define MXC_UARTCR4_MAEN2 0x40 /* Match address mode enable 2 */ +#define MXC_UARTCR4_M10 0x20 /* 10-bit mode select */ +#define MXC_UARTCR4_BRFA_MASK 0x1F /* Baud rate fine adjust */ +#define MXC_UARTCR4_BRFA_OFF 0 +/* Bit definations of CR5 */ +#define MXC_UARTCR5_TDMAS 0x80 /* Transmitter DMA select */ +#define MXC_UARTCR5_RDMAS 0x20 /* Receiver DMA select */ +/* Bit definations of Modem */ +#define MXC_UARTMODEM_RXRTSE 0x08 /* Enable receiver request-to-send */ +#define MXC_UARTMODEM_TXRTSPOL 0x04 /* Select transmitter RTS polarity */ +#define MXC_UARTMODEM_TXRTSE 0x02 /* Enable transmitter request-to-send */ +#define MXC_UARTMODEM_TXCTSE 0x01 /* Enable transmitter CTS clear-to-send */ +/* Bit definations of EDR */ +#define MXC_UARTEDR_NOISY 0x80 /* Current dataword received with noise */ +#define MXC_UARTEDR_PARITYE 0x40 /* Dataword received with parity error */ +/* Bit definations of Infrared reg(IR) */ +#define MXC_UARTIR_IREN 0x04 /* Infrared enable */ +#define MXC_UARTIR_TNP_MASK 0x03 /* Transmitter narrow pluse */ +#define MXC_UARTIR_TNP_OFF 0 +/* Bit definations of FIFO parameter reg */ +#define MXC_UARTPFIFO_TXFE 0x80 /* Transmit fifo enable */ +#define MXC_UARTPFIFO_TXFIFOSIZE_MASK 0x7 +#define MXC_UARTPFIFO_TXFIFOSIZE_OFF 4 +#define MXC_UARTPFIFO_RXFE 0x08 /* Receiver fifo enable */ +#define MXC_UARTPFIFO_RXFIFOSIZE_MASK 0x7 +#define MXC_UARTPFIFO_RXFIFOSIZE_OFF 0 +/* Bit definations of FIFO control reg */ +#define MXC_UARTCFIFO_TXFLUSH 0x80 /* Transmit FIFO/buffer flush */ +#define MXC_UARTCFIFO_RXFLUSH 0x40 /* Receive FIFO/buffer flush */ +#define MXC_UARTCFIFO_RXOFE 0x04 /* Receive fifo overflow INT enable */ +#define MXC_UARTCFIFO_TXOFE 0x02 /* Transmit fifo overflow INT enable */ +#define MXC_UARTCFIFO_RXUFE 0x01 /* Receive fifo underflow INT enable */ +/* Bit definations of FIFO status reg */ +#define MXC_UARTSFIFO_TXEMPT 0x80 /* Transmit fifo/buffer empty */ +#define MXC_UARTSFIFO_RXEMPT 0x40 /* Receive fifo/buffer empty */ +#define MXC_UARTSFIFO_RXOF 0x04 /* Rx buffer overflow flag */ +#define MXC_UARTSFIFO_TXOF 0x02 /* Tx buffer overflow flag */ +#define MXC_UARTSFIFO_RXUF 0x01 /* Rx buffer underflow flag */ + + + +#undef CONFIG_MVF_SERIAL_DMA +/* follow IMX dev node number */ +#define SERIAL_IMX_MAJOR 207 +#define MINOR_START 24 +#define DEV_NAME "ttymxc" +#define MAX_INTERNAL_IRQ MXC_INTERNAL_IRQS + +/* + * This determines how often we check the modem status signals + * for any change. They generally aren't connected to an IRQ + * so we have to poll them. We also check immediately before + * filling the TX fifo incase CTS has been dropped. + */ +#define MCTRL_TIMEOUT (250*HZ/1000) + +#define DRIVER_NAME "MVF-uart" + +#define UART_NR 6 + +struct mvf_port { + struct uart_port port; + unsigned int old_status; + int txirq, rxirq, rtsirq; + unsigned int have_rtscts:1; + unsigned int use_dcedte:1; + unsigned int use_irda:1; + unsigned int irda_inv_rx:1; + unsigned int irda_inv_tx:1; + unsigned int fifo_en:1; /* enable FIFO mode */ + unsigned int mark_en:1; /* enable Mark address match */ + unsigned int format_9bits:1; /* 9bits data format */ + unsigned short trcv_delay; /* transceiver delay */ + unsigned char ma_addr; /* Match address */ + struct clk *clk; + + unsigned int tx_fifo_size, rx_fifo_size; + + /* DMA fields */ + int enable_dma; + unsigned long dma_tx_ch; /* configured eDMA channel */ + struct imx_dma_data dma_data; + struct dma_chan *dma_chan_rx, *dma_chan_tx; + struct scatterlist rx_sgl, tx_sgl; + void *rx_buf; + unsigned char *tx_buf; + unsigned int rx_bytes, tx_bytes; + struct work_struct tsk_dma_rx, tsk_dma_tx; + unsigned int dma_tx_nents; + bool dma_is_rxing, dma_is_txing; + wait_queue_head_t dma_wait; +}; + +#ifdef CONFIG_IRDA +#define USE_IRDA(sport) ((sport)->use_irda) +#else +#define USE_IRDA(sport) (0) +#endif +enum mvf_uart_type { + MVF600_UART, +}; + +struct mvf_uart_data { + unsigned uts_reg; + enum mvf_uart_type devtype; +}; + +static struct mvf_uart_data mvf_uart_devdata[] = { + [MVF600_UART] = { + .devtype = MVF600_UART, + }, +}; + +static struct platform_device_id mvf_uart_devtype[] = { + { + .name = "mvf600-uart", + .driver_data = (kernel_ulong_t) &mvf_uart_devdata[MVF600_UART], + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(platform, mvf_uart_devtype); + +static struct of_device_id mvf_uart_dt_ids[] = { + { + .compatible = "fsl,mvf-uart", + .data = &mvf_uart_devdata[MVF600_UART], + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mvf_uart_dt_ids); + +/* + * interrupts disabled on entry + */ +static void mvf_stop_tx(struct uart_port *port) +{ + struct mvf_port *sport = (struct mvf_port *)port; + unsigned char temp; + + temp = readb(sport->port.membase + MXC_UARTCR2); + writeb(temp & ~(MXC_UARTCR2_TIE | MXC_UARTCR2_TCIE), + sport->port.membase + MXC_UARTCR2); +} + +/* + * interrupts disabled on entry + */ +static void mvf_stop_rx(struct uart_port *port) +{ + struct mvf_port *sport = (struct mvf_port *)port; + unsigned char temp; + + /* if the DMA RX thread is running, wait for it to finish. */ + if (sport->enable_dma && sport->dma_is_rxing) + return; + + temp = readb(sport->port.membase + MXC_UARTCR2); + writeb(temp & ~MXC_UARTCR2_RE, sport->port.membase + MXC_UARTCR2); +} + + /* modem status update function */ +static void mvf_enable_ms(struct uart_port *port) +{ +} + +static inline void mvf_transmit_buffer(struct mvf_port *sport) +{ + struct circ_buf *xmit = &sport->port.state->xmit; + + while (!uart_circ_empty(xmit) && (sport->fifo_en == 1 ? + (readb(sport->port.membase + MXC_UARTTCFIFO) < sport->tx_fifo_size) : + (readb(sport->port.membase + MXC_UARTSR1) & MXC_UARTSR1_TDRE) + )) { + /* send out xmit->buf[xmit->tail] */ + writeb(xmit->buf[xmit->tail], sport->port.membase + MXC_UARTDR); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + sport->port.icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&sport->port); + + if (uart_circ_empty(xmit)) + mvf_stop_tx(&sport->port); +} + +/* + * interrupts disabled on entry + */ +static void mvf_start_tx(struct uart_port *port) +{ + struct mvf_port *sport = (struct mvf_port *)port; + unsigned char temp; + + if (!sport->enable_dma) { + temp = readb(sport->port.membase + MXC_UARTCR2); + writeb(temp | MXC_UARTCR2_TIE, + sport->port.membase + MXC_UARTCR2); + } + + if (sport->enable_dma) { + schedule_work(&sport->tsk_dma_tx); + return; + } + + if (readb(sport->port.membase + MXC_UARTSR1) & MXC_UARTSR1_TDRE) + mvf_transmit_buffer(sport); +} + + +static irqreturn_t mvf_txint(int irq, void *dev_id) +{ + struct mvf_port *sport = dev_id; + struct circ_buf *xmit = &sport->port.state->xmit; + unsigned long flags; + + spin_lock_irqsave(&sport->port.lock, flags); + if (sport->port.x_char) { + /* Send next char */ + writeb(sport->port.x_char, sport->port.membase + MXC_UARTDR); + goto out; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { + mvf_stop_tx(&sport->port); + goto out; + } + + mvf_transmit_buffer(sport); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&sport->port); + +out: + spin_unlock_irqrestore(&sport->port.lock, flags); + return IRQ_HANDLED; +} + +static irqreturn_t mvf_rxint(int irq, void *dev_id) +{ + struct mvf_port *sport = dev_id; + unsigned int flg, ignored = 0; + struct tty_port *port = &sport->port.state->port; + unsigned long flags; + unsigned char r8, rx, sr; + + spin_lock_irqsave(&sport->port.lock, flags); + + while (!(readb(sport->port.membase + MXC_UARTSFIFO) & + MXC_UARTSFIFO_RXEMPT)) { + flg = TTY_NORMAL; + sport->port.icount.rx++; + + /* To clear the FE, OR, NF, FE, PE flags when set, + * read SR1 then read DR + */ + sr = readb(sport->port.membase + MXC_UARTSR1); + + r8 = readb(sport->port.membase + MXC_UARTCR3) & MXC_UARTCR3_R8; + rx = readb(sport->port.membase + MXC_UARTDR); + + if (sport->mark_en && + (sport->format_9bits ? r8 : (rx == sport->ma_addr))) + continue; + + if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx)) + continue; + if (sr & (MXC_UARTSR1_PE | MXC_UARTSR1_OR | MXC_UARTSR1_FE)) { + if (sr & MXC_UARTSR1_PE) + sport->port.icount.parity++; + else if (sr & MXC_UARTSR1_FE) + sport->port.icount.frame++; + if (sr & MXC_UARTSR1_OR) + sport->port.icount.overrun++; + + if (sr & sport->port.ignore_status_mask) { + if (++ignored > 100) + goto out; + continue; + } + + sr &= sport->port.read_status_mask; + + if (sr & MXC_UARTSR1_PE) + flg = TTY_PARITY; + else if (sr & MXC_UARTSR1_FE) + flg = TTY_FRAME; + if (sr & MXC_UARTSR1_OR) + flg = TTY_OVERRUN; + +#ifdef SUPPORT_SYSRQ + sport->port.sysrq = 0; +#endif + } + + tty_insert_flip_char(port, rx, flg); + } + +out: + spin_unlock_irqrestore(&sport->port.lock, flags); + + tty_flip_buffer_push(port); + return IRQ_HANDLED; +} + +static irqreturn_t mvf_int(int irq, void *dev_id) +{ + struct mvf_port *sport = dev_id; + unsigned int sts; + + sts = readb(sport->port.membase + MXC_UARTSR1); + + if (sts & MXC_UARTSR1_RDRF) + mvf_rxint(irq, dev_id); + + if (sts & MXC_UARTSR1_TDRE && + !(readb(sport->port.membase + MXC_UARTCR5) & + MXC_UARTCR5_TDMAS)) + mvf_txint(irq, dev_id); + + return IRQ_HANDLED; +} + +/* + * Return TIOCSER_TEMT when transmitter is not busy. + */ +static unsigned int mvf_tx_empty(struct uart_port *port) +{ + struct mvf_port *sport = (struct mvf_port *)port; + + return (readb(sport->port.membase + MXC_UARTSR1) & MXC_UARTSR1_TC) ? + TIOCSER_TEMT : 0; +} + +static unsigned int mvf_get_mctrl(struct uart_port *port) +{ + struct mvf_port *sport = (struct mvf_port *)port; + unsigned int tmp = 0; + unsigned char reg; + + reg = readb(sport->port.membase + MXC_UARTMODEM); + if (reg & MXC_UARTMODEM_TXCTSE) + tmp |= TIOCM_CTS; + + if (reg & MXC_UARTMODEM_RXRTSE) + tmp |= TIOCM_RTS; + + return tmp; +} + +static void mvf_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct mvf_port *sport = (struct mvf_port *)port; + unsigned long temp; + + temp = readb(sport->port.membase + MXC_UARTMODEM) & + ~(MXC_UARTMODEM_RXRTSE | MXC_UARTMODEM_TXCTSE); + + if (mctrl & TIOCM_RTS) + temp |= MXC_UARTMODEM_RXRTSE; + if (mctrl & TIOCM_CTS) + temp |= MXC_UARTMODEM_TXCTSE; + + writeb(temp, sport->port.membase + MXC_UARTMODEM); +} + +/* + * Interrupts always disabled. + */ +static void mvf_break_ctl(struct uart_port *port, int break_state) +{ + struct mvf_port *sport = (struct mvf_port *)port; + unsigned long flags; + unsigned char temp; + + spin_lock_irqsave(&sport->port.lock, flags); + + temp = readb(sport->port.membase + MXC_UARTCR2) & ~MXC_UARTCR2_SBK; + + if (break_state != 0) + temp |= MXC_UARTCR2_SBK; + + writeb(temp, sport->port.membase + MXC_UARTCR2); + + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +#define TXTL 2 +#define RXTL 1 + +static int mvf_setup_watermark(struct mvf_port *sport, unsigned int mode) +{ + unsigned char val, old_cr2, cr2; + + /* set receiver/transmitter trigger level. */ + old_cr2 = cr2 = readb(sport->port.membase + MXC_UARTCR2); + cr2 &= ~(MXC_UARTCR2_TIE | MXC_UARTCR2_TCIE | MXC_UARTCR2_TE | + MXC_UARTCR2_RIE | MXC_UARTCR2_RE); + writeb(cr2, sport->port.membase + MXC_UARTCR2); + + val = TXTL; + writeb(val, sport->port.membase + MXC_UARTTWFIFO); + val = RXTL; + writeb(val, sport->port.membase + MXC_UARTRWFIFO); + + /* Enable Tx and Rx FIFO */ + val = readb(sport->port.membase + MXC_UARTPFIFO); + sport->tx_fifo_size = 0x1 << (((val >> MXC_UARTPFIFO_TXFIFOSIZE_OFF) & + MXC_UARTPFIFO_TXFIFOSIZE_MASK) + 1); + sport->rx_fifo_size = 0x1 << (((val >> MXC_UARTPFIFO_RXFIFOSIZE_OFF) & + MXC_UARTPFIFO_RXFIFOSIZE_MASK) + 1); + writeb(val | MXC_UARTPFIFO_TXFE | MXC_UARTPFIFO_RXFE, + sport->port.membase + MXC_UARTPFIFO); + + /* Flush the Tx and Rx FIFO to a known state */ + writeb(MXC_UARTCFIFO_TXFLUSH | MXC_UARTCFIFO_RXFLUSH, + sport->port.membase + MXC_UARTCFIFO); + + /* restore CR2 */ + writeb(old_cr2, sport->port.membase + MXC_UARTCR2); + + return 0; +} + + +static int mvf_startup(struct uart_port *port) +{ + struct mvf_port *sport = (struct mvf_port *)port; + int retval; + unsigned long flags, temp; + + if (sport->fifo_en) + mvf_setup_watermark(sport, 0); + + temp = readb(sport->port.membase + MXC_UARTCR2); + + writeb(temp & ~MXC_UARTCR2_RIE, sport->port.membase + MXC_UARTCR2); + + /* + * Allocate the IRQ(s) + * Vybrid chips only have one interrupt. + */ + retval = request_irq(sport->port.irq, mvf_int, 0, + DRIVER_NAME, sport); + if (retval) + goto error_out1; + + /* Enable the DMA ops for uart. */ + if (sport->enable_dma) { + sport->dma_is_txing = 0; + + /* enable DMA request generation */ + temp = readb(sport->port.membase + MXC_UARTCR5); + temp |= MXC_UARTCR5_TDMAS; + writeb(temp, sport->port.membase + MXC_UARTCR5); + + init_waitqueue_head(&sport->dma_wait); + } + + spin_lock_irqsave(&sport->port.lock, flags); + + /* Finally, clear and enable interrupts */ + temp = readb(sport->port.membase + MXC_UARTCR2); + temp |= MXC_UARTCR2_RIE | MXC_UARTCR2_TIE; + writeb(temp, sport->port.membase + MXC_UARTCR2); + + spin_unlock_irqrestore(&sport->port.lock, flags); + + return 0; + +error_out1: + return retval; +} + +static void mvf_shutdown(struct uart_port *port) +{ + struct mvf_port *sport = (struct mvf_port *)port; + unsigned char temp; + unsigned long flags; + + spin_lock_irqsave(&sport->port.lock, flags); + temp = readb(sport->port.membase + MXC_UARTCR2); + temp &= ~(MXC_UARTCR2_TE | MXC_UARTCR2_RE); + writeb(temp, sport->port.membase + MXC_UARTCR2); + spin_unlock_irqrestore(&sport->port.lock, flags); + + /* + * Free the interrupts + */ + if (sport->txirq > 0) { + if (!USE_IRDA(sport)) + free_irq(sport->rtsirq, sport); + free_irq(sport->txirq, sport); + free_irq(sport->rxirq, sport); + } else + free_irq(sport->port.irq, sport); + + /* + * Disable all interrupts, port and break condition. + */ + + spin_lock_irqsave(&sport->port.lock, flags); + temp = readb(sport->port.membase + MXC_UARTCR2); + temp &= ~(MXC_UARTCR2_TIE | MXC_UARTCR2_TCIE | MXC_UARTCR2_RIE); + writeb(temp, sport->port.membase + MXC_UARTCR2); + + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +static void +mvf_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct mvf_port *sport = (struct mvf_port *)port; + unsigned long flags; + unsigned char cr1, old_cr1, old_cr2, cr4, bdh, modem; + unsigned int baud; + unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; + unsigned int sbr, brfa; + + cr1 = old_cr1 = readb(sport->port.membase + MXC_UARTCR1); + old_cr2 = readb(sport->port.membase + MXC_UARTCR2); + cr4 = readb(sport->port.membase + MXC_UARTCR4); + bdh = readb(sport->port.membase + MXC_UARTBDH); + modem = readb(sport->port.membase + MXC_UARTMODEM); + /* + * If we don't support modem control lines, don't allow + * these to be set. + */ + if (0) { + termios->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR); + termios->c_cflag |= CLOCAL; + } + + /* + * We only support CS8 and CS7,but CS7 must enable PE. + * supported mode: + * - (7,e/o,1) + * - (8,n,1) + * - (8,m/s,1) + * - (8,e/o,1) + */ + while ((termios->c_cflag & CSIZE) != CS8 && + (termios->c_cflag & CSIZE) != CS7) { + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= old_csize; + old_csize = CS8; + } + + if ((termios->c_cflag & CSIZE) == CS8 || + (termios->c_cflag & CSIZE) == CS7) + cr1 = old_cr1 & ~MXC_UARTCR1_M; + + if (termios->c_cflag & CMSPAR) { + if ((termios->c_cflag & CSIZE) != CS8) { + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= CS8; + } + cr1 |= MXC_UARTCR1_M; + } + + if (termios->c_cflag & CRTSCTS) { + if (sport->have_rtscts) + modem |= (MXC_UARTMODEM_RXRTSE | MXC_UARTMODEM_TXCTSE); + + } else { + termios->c_cflag &= ~CRTSCTS; + modem &= ~(MXC_UARTMODEM_RXRTSE | MXC_UARTMODEM_TXCTSE); + } + + if (termios->c_cflag & CSTOPB) + termios->c_cflag &= ~CSTOPB; + + /* parity must enable when CS7 to match 8-bits format */ + if ((termios->c_cflag & CSIZE) == CS7) + termios->c_cflag |= PARENB; + + if ((termios->c_cflag & PARENB)) { + if (termios->c_cflag & CMSPAR) { + cr1 &= ~MXC_UARTCR1_PE; + cr1 |= MXC_UARTCR1_M; + sport->format_9bits = 1; + } else { + cr1 |= MXC_UARTCR1_PE; + if ((termios->c_cflag & CSIZE) == CS8) + cr1 |= MXC_UARTCR1_M; + if (termios->c_cflag & PARODD) + cr1 |= MXC_UARTCR1_PT; + else + cr1 &= ~MXC_UARTCR1_PT; + } + } + + /* + * Ask the core to calculate the divisor for us. + */ + baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16); + + spin_lock_irqsave(&sport->port.lock, flags); + + sport->port.read_status_mask = 0; + if (termios->c_iflag & INPCK) + sport->port.read_status_mask |= + (MXC_UARTSR1_FE | MXC_UARTSR1_PE); + if (termios->c_iflag & (BRKINT | PARMRK)) + sport->port.read_status_mask |= MXC_UARTSR1_FE; + + /* + * Characters to ignore + */ + sport->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + sport->port.ignore_status_mask |= MXC_UARTSR1_PE; + if (termios->c_iflag & IGNBRK) { + sport->port.ignore_status_mask |= MXC_UARTSR1_FE; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + sport->port.ignore_status_mask |= MXC_UARTSR1_OR; + } + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + /* wait transmit engin complete */ + while (!(readb(sport->port.membase + MXC_UARTSR1) & MXC_UARTSR1_TC)) + barrier(); + + writeb(old_cr2 & ~(MXC_UARTCR2_TE | MXC_UARTCR2_RE), + sport->port.membase + MXC_UARTCR2); + + /* disable transmit and receive */ + sbr = sport->port.uartclk / (16 * baud); + brfa = ((sport->port.uartclk - (16 * sbr * baud)) * 2)/baud; + + bdh &= ~MXC_UARTBDH_SBR_MASK; + bdh |= (sbr >> 8) & 0x1F; + + cr4 &= ~MXC_UARTCR4_BRFA_MASK; + brfa &= MXC_UARTCR4_BRFA_MASK; + writeb(cr4 | brfa, sport->port.membase + MXC_UARTCR4); + writeb(bdh, sport->port.membase + MXC_UARTBDH); + writeb(sbr & 0xFF, sport->port.membase + MXC_UARTBDL); + writeb(cr1, sport->port.membase + MXC_UARTCR1); + writeb(modem, sport->port.membase + MXC_UARTMODEM); + + /* restore control register */ + writeb(old_cr2, sport->port.membase + MXC_UARTCR2); + + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +static const char *mvf_type(struct uart_port *port) +{ + struct mvf_port *sport = (struct mvf_port *)port; + + return sport->port.type == PORT_MVF ? "MVF" : NULL; +} + +/* + * Release the memory region(s) being used by 'port'. + */ +static void mvf_release_port(struct uart_port *port) +{ + struct platform_device *pdev = to_platform_device(port->dev); + struct resource *mmres; + + mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(mmres->start, mmres->end - mmres->start + 1); +} + +/* + * Request the memory region(s) being used by 'port'. + */ +static int mvf_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, + "imx-uart"); + + return ret ? 0 : -EBUSY; +} + +/* + * Configure/autoconfigure the port. + */ +static void mvf_config_port(struct uart_port *port, int flags) +{ + struct mvf_port *sport = (struct mvf_port *)port; + + if (flags & UART_CONFIG_TYPE && + mvf_request_port(&sport->port) == 0) + sport->port.type = PORT_MVF; +} + +/* + * Verify the new serial_struct (for TIOCSSERIAL). + * The only change we allow are to the flags and type, and + * even then only between PORT_MVF and PORT_UNKNOWN + */ +static int +mvf_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + struct mvf_port *sport = (struct mvf_port *)port; + int ret = 0; + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_MVF) + ret = -EINVAL; + if (sport->port.irq != ser->irq) + ret = -EINVAL; + if (ser->io_type != UPIO_MEM) + ret = -EINVAL; + if (sport->port.uartclk / 16 != ser->baud_base) + ret = -EINVAL; + if (sport->port.iobase != ser->port) + ret = -EINVAL; + if (ser->hub6 != 0) + ret = -EINVAL; + return ret; +} + + +static struct uart_ops mvf_pops = { + .tx_empty = mvf_tx_empty, + .set_mctrl = mvf_set_mctrl, + .get_mctrl = mvf_get_mctrl, + .stop_tx = mvf_stop_tx, + .start_tx = mvf_start_tx, + .stop_rx = mvf_stop_rx, + .enable_ms = mvf_enable_ms, + .break_ctl = mvf_break_ctl, + .startup = mvf_startup, + .shutdown = mvf_shutdown, + .set_termios = mvf_set_termios, + .type = mvf_type, + .release_port = mvf_release_port, + .request_port = mvf_request_port, + .config_port = mvf_config_port, + .verify_port = mvf_verify_port, +}; + +static struct mvf_port *mvf_ports[UART_NR]; + +#ifdef CONFIG_SERIAL_MVF_CONSOLE +static void mvf_console_putchar(struct uart_port *port, int ch) +{ + struct mvf_port *sport = (struct mvf_port *)port; + + while (!(readb(sport->port.membase + MXC_UARTSR1) & MXC_UARTSR1_TDRE)) + barrier(); + + writeb(ch, sport->port.membase + MXC_UARTDR); +} + +/* + * Interrupts are disabled on entering + */ +static void +mvf_console_write(struct console *co, const char *s, unsigned int count) +{ + struct mvf_port *sport = mvf_ports[co->index]; + unsigned int old_cr2, cr2; + unsigned long flags; + + spin_lock_irqsave(&sport->port.lock, flags); + /* + * First, save UCR1/2 and then disable interrupts + */ + cr2 = old_cr2 = readb(sport->port.membase + MXC_UARTCR2); + + cr2 |= (MXC_UARTCR2_TE | MXC_UARTCR2_RE); + cr2 &= ~(MXC_UARTCR2_TIE | MXC_UARTCR2_TCIE | MXC_UARTCR2_RIE); + + writeb(cr2, sport->port.membase + MXC_UARTCR2); + + uart_console_write(&sport->port, s, count, mvf_console_putchar); + + /* + * wait for transmitter finish complete and restore CR2 + */ + while (!(readb(sport->port.membase + MXC_UARTSR1) & MXC_UARTSR1_TC)) + ; + + writeb(old_cr2, sport->port.membase + MXC_UARTCR2); + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +/* + * If the port was already initialised (eg, by a boot loader), + * try to determine the current setup. + */ +static void __init +mvf_console_get_options(struct mvf_port *sport, int *baud, + int *parity, int *bits) +{ + + if (readb(sport->port.membase + MXC_UARTCR2) & + (MXC_UARTCR2_TE | MXC_UARTCR2)) { + /* ok, the port was enabled */ + unsigned char cr1, bdh, bdl, brfa; + unsigned int sbr, uartclk; + unsigned int baud_raw; + + cr1 = readb(sport->port.membase + MXC_UARTCR1); + + *parity = 'n'; + if (cr1 & MXC_UARTCR1_PE) { + if (cr1 & MXC_UARTCR1_PT) + *parity = 'o'; + else + *parity = 'e'; + } + + if (cr1 & MXC_UARTCR1_M) + *bits = 9; + else + *bits = 8; + + bdh = readb(sport->port.membase + MXC_UARTBDH) & + MXC_UARTBDH_SBR_MASK; + bdl = readb(sport->port.membase + MXC_UARTBDL); + sbr = bdh; + sbr <<= 8; + sbr |= bdl; + brfa = readb(sport->port.membase + MXC_UARTCR4) & + MXC_UARTCR4_BRFA_MASK; + uartclk = clk_get_rate(sport->clk); + /* + * Baud = mod_clk/(16*(sbr[13]+(brfa)/32) + */ + baud_raw = uartclk/(16 * (sbr + brfa/32)); + + if (*baud != baud_raw) + printk(KERN_INFO "Serial: Console IMX " + "rounded baud rate from %d to %d\n", + baud_raw, *baud); + } +} + +static int __init +mvf_console_setup(struct console *co, char *options) +{ + struct mvf_port *sport; + 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 == -1 || co->index >= ARRAY_SIZE(mvf_ports)) + co->index = 0; + sport = mvf_ports[co->index]; + + if (sport == NULL) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + mvf_console_get_options(sport, &baud, &parity, &bits); + + if (sport->fifo_en == 1) + mvf_setup_watermark(sport, 0); + + return uart_set_options(&sport->port, co, baud, parity, bits, flow); +} + +static struct uart_driver mvf_reg; +static struct console mvf_console = { + .name = DEV_NAME, + .write = mvf_console_write, + .device = uart_console_device, + .setup = mvf_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &mvf_reg, +}; + +#define MVF_CONSOLE (&mvf_console) +#else +#define MVF_CONSOLE NULL +#endif + +static struct uart_driver mvf_reg = { + .owner = THIS_MODULE, + .driver_name = DRIVER_NAME, + .dev_name = DEV_NAME, + .major = SERIAL_IMX_MAJOR, + .minor = MINOR_START, + .nr = ARRAY_SIZE(mvf_ports), + .cons = MVF_CONSOLE, +}; + +static int serial_mvf_suspend(struct platform_device *dev, pm_message_t state) +{ + struct mvf_port *sport = platform_get_drvdata(dev); + + /* Enable UART wakeup */ + + if (sport) + uart_suspend_port(&mvf_reg, &sport->port); + + return 0; +} + +static int serial_mvf_resume(struct platform_device *dev) +{ + struct mvf_port *sport = platform_get_drvdata(dev); + + if (sport) + uart_resume_port(&mvf_reg, &sport->port); + + /* Disable UART wakeup */ + + return 0; +} + +static int serial_mvf_probe_dt(struct mvf_port *sport, + struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + int ret; + + if (!np) + /* no device tree device */ + return 1; + + ret = of_alias_get_id(np, "serial"); + if (ret < 0) { + dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret); + return ret; + } + sport->port.line = ret; + + if (of_get_property(np, "fsl,uart-fifo-mode", NULL)) + sport->fifo_en = 1; + + return 0; +} + +static int serial_mvf_probe(struct platform_device *pdev) +{ + struct mvf_port *sport; + struct imxuart_platform_data *pdata; + void __iomem *base; + int ret = 0; + struct resource *res; + + sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL); + if (!sport) + return -ENOMEM; + + pdev->dev.coherent_dma_mask = 0; + + ret = serial_mvf_probe_dt(sport, pdev); + if (ret < 0) + return ret ; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + base = devm_ioremap(&pdev->dev, res->start, PAGE_SIZE); + if (!base) + return -ENOMEM; + + sport->port.dev = &pdev->dev; + sport->port.mapbase = res->start; + sport->port.membase = base; + sport->port.type = PORT_MVF, + sport->port.iotype = UPIO_MEM; + sport->port.irq = platform_get_irq(pdev, 0); + sport->port.fifosize = 32; + sport->port.ops = &mvf_pops; + sport->port.flags = UPF_BOOT_AUTOCONF; + if (pdev->id >= 0) + sport->port.line = pdev->id; + + sport->clk = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(sport->clk)) { + ret = PTR_ERR(sport->clk); + dev_err(&pdev->dev, "failed to uart clk: %d\n", ret); + return ret; + } + + clk_prepare_enable(sport->clk); + + sport->port.uartclk = clk_get_rate(sport->clk); + + mvf_ports[sport->port.line] = sport; + + pdata = pdev->dev.platform_data; + + /* all uarts support hardware RTS/CTS */ + sport->have_rtscts = 1; + + if (pdata && pdata->init) { + ret = pdata->init(pdev); + if (ret) + goto clkput; + } + + ret = uart_add_one_port(&mvf_reg, &sport->port); + + if (ret) + goto deinit; + platform_set_drvdata(pdev, &sport->port); + + return 0; +deinit: + if (pdata && pdata->exit) + pdata->exit(pdev); +clkput: + clk_disable_unprepare(sport->clk); + + return ret; +} + +static int serial_mvf_remove(struct platform_device *pdev) +{ + struct imxuart_platform_data *pdata; + struct mvf_port *sport = platform_get_drvdata(pdev); + + pdata = pdev->dev.platform_data; + + platform_set_drvdata(pdev, NULL); + + if (sport) + uart_remove_one_port(&mvf_reg, &sport->port); + + clk_disable_unprepare(sport->clk); + + if (pdata && pdata->exit) + pdata->exit(pdev); + + return 0; +} + +static struct platform_driver serial_mvf_driver = { + .probe = serial_mvf_probe, + .remove = serial_mvf_remove, + + .suspend = serial_mvf_suspend, + .resume = serial_mvf_resume, + .id_table = mvf_uart_devtype, + .driver = { + .name = "mvf-uart", + .owner = THIS_MODULE, + .of_match_table = mvf_uart_dt_ids, + }, +}; + +static int __init mvf_serial_init(void) +{ + int ret; + + pr_info("Serial: Vybrid Family driver\n"); + + ret = uart_register_driver(&mvf_reg); + if (ret) + return ret; + + ret = platform_driver_register(&serial_mvf_driver); + if (ret != 0) + uart_unregister_driver(&mvf_reg); + + return 0; +} + +static void __exit mvf_serial_exit(void) +{ + platform_driver_unregister(&serial_mvf_driver); + uart_unregister_driver(&mvf_reg); +} + +module_init(mvf_serial_init); +module_exit(mvf_serial_exit); + +MODULE_DESCRIPTION("Vybrid Family serial port driver"); +MODULE_LICENSE("GPL"); diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index 74c2bf7..ffb7192 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -226,4 +226,7 @@ /* Rocketport EXPRESS/INFINITY */ #define PORT_RP2 102 +/* Freescale Vybrid uart */ +#define PORT_MVF 103 + #endif /* _UAPILINUX_SERIAL_CORE_H */
It adds Freescale Vybrid Family uart driver support Signed-off-by: Jingchang Lu <b35083@freescale.com> --- drivers/tty/serial/Kconfig | 17 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/mvf.c | 1246 ++++++++++++++++++++++++++++++++++++++ include/uapi/linux/serial_core.h | 3 + 4 files changed, 1267 insertions(+) create mode 100644 drivers/tty/serial/mvf.c