* [PATCH] tty/serial: add MVF uart driver support
@ 2013-04-12 7:10 Jingchang Lu
2013-04-12 12:30 ` Sascha Hauer
2013-04-13 20:41 ` Fabio Estevam
0 siblings, 2 replies; 4+ messages in thread
From: Jingchang Lu @ 2013-04-12 7:10 UTC (permalink / raw)
To: linux-arm-kernel
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
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 */
--
1.8.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH] tty/serial: add MVF uart driver support
2013-04-12 7:10 [PATCH] tty/serial: add MVF uart driver support Jingchang Lu
@ 2013-04-12 12:30 ` Sascha Hauer
2013-04-13 20:41 ` Fabio Estevam
1 sibling, 0 replies; 4+ messages in thread
From: Sascha Hauer @ 2013-04-12 12:30 UTC (permalink / raw)
To: linux-arm-kernel
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 at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH] tty/serial: add MVF uart driver support
2013-04-12 7:10 [PATCH] tty/serial: add MVF uart driver support Jingchang Lu
2013-04-12 12:30 ` Sascha Hauer
@ 2013-04-13 20:41 ` Fabio Estevam
2013-04-14 8:42 ` Sascha Hauer
1 sibling, 1 reply; 4+ messages in thread
From: Fabio Estevam @ 2013-04-13 20:41 UTC (permalink / raw)
To: 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?
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH] tty/serial: add MVF uart driver support
2013-04-13 20:41 ` Fabio Estevam
@ 2013-04-14 8:42 ` Sascha Hauer
0 siblings, 0 replies; 4+ messages in thread
From: Sascha Hauer @ 2013-04-14 8:42 UTC (permalink / raw)
To: linux-arm-kernel
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
--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2013-04-14 8:42 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-04-12 7:10 [PATCH] tty/serial: add MVF uart driver support Jingchang Lu
2013-04-12 12:30 ` Sascha Hauer
2013-04-13 20:41 ` Fabio Estevam
2013-04-14 8:42 ` Sascha Hauer
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).