xref: /openbmc/linux/drivers/tty/serial/mvebu-uart.c (revision 95f78768)
130530791SWilson Ding /*
230530791SWilson Ding * ***************************************************************************
389ebc274SPaul Gortmaker * Marvell Armada-3700 Serial Driver
489ebc274SPaul Gortmaker * Author: Wilson Ding <dingwei@marvell.com>
530530791SWilson Ding * Copyright (C) 2015 Marvell International Ltd.
630530791SWilson Ding * ***************************************************************************
730530791SWilson Ding * This program is free software: you can redistribute it and/or modify it
830530791SWilson Ding * under the terms of the GNU General Public License as published by the Free
930530791SWilson Ding * Software Foundation, either version 2 of the License, or any later version.
1030530791SWilson Ding *
1130530791SWilson Ding * This program is distributed in the hope that it will be useful,
1230530791SWilson Ding * but WITHOUT ANY WARRANTY; without even the implied warranty of
1330530791SWilson Ding * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1430530791SWilson Ding * GNU General Public License for more details.
1530530791SWilson Ding *
1630530791SWilson Ding * You should have received a copy of the GNU General Public License
1730530791SWilson Ding * along with this program.  If not, see <http://www.gnu.org/licenses/>.
1830530791SWilson Ding * ***************************************************************************
1930530791SWilson Ding */
2030530791SWilson Ding 
2130530791SWilson Ding #include <linux/clk.h>
2230530791SWilson Ding #include <linux/console.h>
2330530791SWilson Ding #include <linux/delay.h>
2430530791SWilson Ding #include <linux/device.h>
2530530791SWilson Ding #include <linux/init.h>
2630530791SWilson Ding #include <linux/io.h>
2730530791SWilson Ding #include <linux/iopoll.h>
2830530791SWilson Ding #include <linux/of.h>
2930530791SWilson Ding #include <linux/of_address.h>
3030530791SWilson Ding #include <linux/of_device.h>
3130530791SWilson Ding #include <linux/of_irq.h>
3230530791SWilson Ding #include <linux/of_platform.h>
3330530791SWilson Ding #include <linux/platform_device.h>
3430530791SWilson Ding #include <linux/serial.h>
3530530791SWilson Ding #include <linux/serial_core.h>
3630530791SWilson Ding #include <linux/slab.h>
3730530791SWilson Ding #include <linux/tty.h>
3830530791SWilson Ding #include <linux/tty_flip.h>
3930530791SWilson Ding 
4030530791SWilson Ding /* Register Map */
415218d769SMiquel Raynal #define UART_STD_RBR		0x00
4230530791SWilson Ding 
435218d769SMiquel Raynal #define UART_STD_TSH		0x04
4430530791SWilson Ding 
455218d769SMiquel Raynal #define UART_STD_CTRL1		0x08
4630530791SWilson Ding #define  CTRL_SOFT_RST		BIT(31)
4730530791SWilson Ding #define  CTRL_TXFIFO_RST	BIT(15)
4830530791SWilson Ding #define  CTRL_RXFIFO_RST	BIT(14)
4930530791SWilson Ding #define  CTRL_SND_BRK_SEQ	BIT(11)
5030530791SWilson Ding #define  CTRL_BRK_DET_INT	BIT(3)
5130530791SWilson Ding #define  CTRL_FRM_ERR_INT	BIT(2)
5230530791SWilson Ding #define  CTRL_PAR_ERR_INT	BIT(1)
5330530791SWilson Ding #define  CTRL_OVR_ERR_INT	BIT(0)
545218d769SMiquel Raynal #define  CTRL_BRK_INT		(CTRL_BRK_DET_INT | CTRL_FRM_ERR_INT | \
555218d769SMiquel Raynal 				CTRL_PAR_ERR_INT | CTRL_OVR_ERR_INT)
5630530791SWilson Ding 
575218d769SMiquel Raynal #define UART_STD_CTRL2		UART_STD_CTRL1
585218d769SMiquel Raynal #define  CTRL_STD_TX_RDY_INT	BIT(5)
595218d769SMiquel Raynal #define  CTRL_STD_RX_RDY_INT	BIT(4)
605218d769SMiquel Raynal 
615218d769SMiquel Raynal #define UART_STAT		0x0C
6230530791SWilson Ding #define  STAT_TX_FIFO_EMP	BIT(13)
6330530791SWilson Ding #define  STAT_TX_FIFO_FUL	BIT(11)
6430530791SWilson Ding #define  STAT_TX_EMP		BIT(6)
655218d769SMiquel Raynal #define  STAT_STD_TX_RDY	BIT(5)
665218d769SMiquel Raynal #define  STAT_STD_RX_RDY	BIT(4)
6730530791SWilson Ding #define  STAT_BRK_DET		BIT(3)
6830530791SWilson Ding #define  STAT_FRM_ERR		BIT(2)
6930530791SWilson Ding #define  STAT_PAR_ERR		BIT(1)
7030530791SWilson Ding #define  STAT_OVR_ERR		BIT(0)
7130530791SWilson Ding #define  STAT_BRK_ERR		(STAT_BRK_DET | STAT_FRM_ERR | STAT_FRM_ERR\
7230530791SWilson Ding 				 | STAT_PAR_ERR | STAT_OVR_ERR)
7330530791SWilson Ding 
7430530791SWilson Ding #define UART_BRDV		0x10
7568a0db1dSAllen Yan #define  BRDV_BAUD_MASK         0x3FF
7630530791SWilson Ding 
7730530791SWilson Ding #define MVEBU_NR_UARTS		1
7830530791SWilson Ding 
7930530791SWilson Ding #define MVEBU_UART_TYPE		"mvebu-uart"
8002c33330SYehuda Yitschak #define DRIVER_NAME		"mvebu_serial"
8130530791SWilson Ding 
8295f78768SMiquel Raynal enum {
8395f78768SMiquel Raynal 	/* Either there is only one summed IRQ... */
8495f78768SMiquel Raynal 	UART_IRQ_SUM = 0,
8595f78768SMiquel Raynal 	/* ...or there are two separate IRQ for RX and TX */
8695f78768SMiquel Raynal 	UART_RX_IRQ = 0,
8795f78768SMiquel Raynal 	UART_TX_IRQ,
8895f78768SMiquel Raynal 	UART_IRQ_COUNT
8995f78768SMiquel Raynal };
9095f78768SMiquel Raynal 
9195f78768SMiquel Raynal /* Diverging register offsets */
925218d769SMiquel Raynal struct uart_regs_layout {
935218d769SMiquel Raynal 	unsigned int rbr;
945218d769SMiquel Raynal 	unsigned int tsh;
955218d769SMiquel Raynal 	unsigned int ctrl;
965218d769SMiquel Raynal 	unsigned int intr;
975218d769SMiquel Raynal };
9830530791SWilson Ding 
995218d769SMiquel Raynal /* Diverging flags */
1005218d769SMiquel Raynal struct uart_flags {
1015218d769SMiquel Raynal 	unsigned int ctrl_tx_rdy_int;
1025218d769SMiquel Raynal 	unsigned int ctrl_rx_rdy_int;
1035218d769SMiquel Raynal 	unsigned int stat_tx_rdy;
1045218d769SMiquel Raynal 	unsigned int stat_rx_rdy;
1055218d769SMiquel Raynal };
1065218d769SMiquel Raynal 
1075218d769SMiquel Raynal /* Driver data, a structure for each UART port */
1085218d769SMiquel Raynal struct mvebu_uart_driver_data {
1095218d769SMiquel Raynal 	bool is_ext;
1105218d769SMiquel Raynal 	struct uart_regs_layout regs;
1115218d769SMiquel Raynal 	struct uart_flags flags;
1125218d769SMiquel Raynal };
1135218d769SMiquel Raynal 
1145218d769SMiquel Raynal /* MVEBU UART driver structure */
1155218d769SMiquel Raynal struct mvebu_uart {
11630530791SWilson Ding 	struct uart_port *port;
11730530791SWilson Ding 	struct clk *clk;
11895f78768SMiquel Raynal 	int irq[UART_IRQ_COUNT];
11995f78768SMiquel Raynal 	unsigned char __iomem *nb;
1205218d769SMiquel Raynal 	struct mvebu_uart_driver_data *data;
12130530791SWilson Ding };
12230530791SWilson Ding 
1235218d769SMiquel Raynal static struct mvebu_uart *to_mvuart(struct uart_port *port)
1245218d769SMiquel Raynal {
1255218d769SMiquel Raynal 	return (struct mvebu_uart *)port->private_data;
1265218d769SMiquel Raynal }
1275218d769SMiquel Raynal 
1285218d769SMiquel Raynal #define IS_EXTENDED(port) (to_mvuart(port)->data->is_ext)
1295218d769SMiquel Raynal 
1305218d769SMiquel Raynal #define UART_RBR(port) (to_mvuart(port)->data->regs.rbr)
1315218d769SMiquel Raynal #define UART_TSH(port) (to_mvuart(port)->data->regs.tsh)
1325218d769SMiquel Raynal #define UART_CTRL(port) (to_mvuart(port)->data->regs.ctrl)
1335218d769SMiquel Raynal #define UART_INTR(port) (to_mvuart(port)->data->regs.intr)
1345218d769SMiquel Raynal 
1355218d769SMiquel Raynal #define CTRL_TX_RDY_INT(port) (to_mvuart(port)->data->flags.ctrl_tx_rdy_int)
1365218d769SMiquel Raynal #define CTRL_RX_RDY_INT(port) (to_mvuart(port)->data->flags.ctrl_rx_rdy_int)
1375218d769SMiquel Raynal #define STAT_TX_RDY(port) (to_mvuart(port)->data->flags.stat_tx_rdy)
1385218d769SMiquel Raynal #define STAT_RX_RDY(port) (to_mvuart(port)->data->flags.stat_rx_rdy)
1395218d769SMiquel Raynal 
1405218d769SMiquel Raynal static struct uart_port mvebu_uart_ports[MVEBU_NR_UARTS];
1415218d769SMiquel Raynal 
14230530791SWilson Ding /* Core UART Driver Operations */
14330530791SWilson Ding static unsigned int mvebu_uart_tx_empty(struct uart_port *port)
14430530791SWilson Ding {
14530530791SWilson Ding 	unsigned long flags;
14630530791SWilson Ding 	unsigned int st;
14730530791SWilson Ding 
14830530791SWilson Ding 	spin_lock_irqsave(&port->lock, flags);
14930530791SWilson Ding 	st = readl(port->membase + UART_STAT);
15030530791SWilson Ding 	spin_unlock_irqrestore(&port->lock, flags);
15130530791SWilson Ding 
15230530791SWilson Ding 	return (st & STAT_TX_FIFO_EMP) ? TIOCSER_TEMT : 0;
15330530791SWilson Ding }
15430530791SWilson Ding 
15530530791SWilson Ding static unsigned int mvebu_uart_get_mctrl(struct uart_port *port)
15630530791SWilson Ding {
15730530791SWilson Ding 	return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
15830530791SWilson Ding }
15930530791SWilson Ding 
16030530791SWilson Ding static void mvebu_uart_set_mctrl(struct uart_port *port,
16130530791SWilson Ding 				 unsigned int mctrl)
16230530791SWilson Ding {
16330530791SWilson Ding /*
16430530791SWilson Ding  * Even if we do not support configuring the modem control lines, this
16530530791SWilson Ding  * function must be proided to the serial core
16630530791SWilson Ding  */
16730530791SWilson Ding }
16830530791SWilson Ding 
16930530791SWilson Ding static void mvebu_uart_stop_tx(struct uart_port *port)
17030530791SWilson Ding {
1715218d769SMiquel Raynal 	unsigned int ctl = readl(port->membase + UART_INTR(port));
17230530791SWilson Ding 
1735218d769SMiquel Raynal 	ctl &= ~CTRL_TX_RDY_INT(port);
1745218d769SMiquel Raynal 	writel(ctl, port->membase + UART_INTR(port));
17530530791SWilson Ding }
17630530791SWilson Ding 
17730530791SWilson Ding static void mvebu_uart_start_tx(struct uart_port *port)
17830530791SWilson Ding {
17930434b07SAllen Yan 	unsigned int ctl;
18030434b07SAllen Yan 	struct circ_buf *xmit = &port->state->xmit;
18130530791SWilson Ding 
18230434b07SAllen Yan 	if (IS_EXTENDED(port) && !uart_circ_empty(xmit)) {
18330434b07SAllen Yan 		writel(xmit->buf[xmit->tail], port->membase + UART_TSH(port));
18430434b07SAllen Yan 		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
18530434b07SAllen Yan 		port->icount.tx++;
18630434b07SAllen Yan 	}
18730434b07SAllen Yan 
18830434b07SAllen Yan 	ctl = readl(port->membase + UART_INTR(port));
1895218d769SMiquel Raynal 	ctl |= CTRL_TX_RDY_INT(port);
1905218d769SMiquel Raynal 	writel(ctl, port->membase + UART_INTR(port));
19130530791SWilson Ding }
19230530791SWilson Ding 
19330530791SWilson Ding static void mvebu_uart_stop_rx(struct uart_port *port)
19430530791SWilson Ding {
1955218d769SMiquel Raynal 	unsigned int ctl;
19630530791SWilson Ding 
1975218d769SMiquel Raynal 	ctl = readl(port->membase + UART_CTRL(port));
1985218d769SMiquel Raynal 	ctl &= ~CTRL_BRK_INT;
1995218d769SMiquel Raynal 	writel(ctl, port->membase + UART_CTRL(port));
2005218d769SMiquel Raynal 
2015218d769SMiquel Raynal 	ctl = readl(port->membase + UART_INTR(port));
2025218d769SMiquel Raynal 	ctl &= ~CTRL_RX_RDY_INT(port);
2035218d769SMiquel Raynal 	writel(ctl, port->membase + UART_INTR(port));
20430530791SWilson Ding }
20530530791SWilson Ding 
20630530791SWilson Ding static void mvebu_uart_break_ctl(struct uart_port *port, int brk)
20730530791SWilson Ding {
20830530791SWilson Ding 	unsigned int ctl;
20930530791SWilson Ding 	unsigned long flags;
21030530791SWilson Ding 
21130530791SWilson Ding 	spin_lock_irqsave(&port->lock, flags);
2125218d769SMiquel Raynal 	ctl = readl(port->membase + UART_CTRL(port));
21330530791SWilson Ding 	if (brk == -1)
21430530791SWilson Ding 		ctl |= CTRL_SND_BRK_SEQ;
21530530791SWilson Ding 	else
21630530791SWilson Ding 		ctl &= ~CTRL_SND_BRK_SEQ;
2175218d769SMiquel Raynal 	writel(ctl, port->membase + UART_CTRL(port));
21830530791SWilson Ding 	spin_unlock_irqrestore(&port->lock, flags);
21930530791SWilson Ding }
22030530791SWilson Ding 
22130530791SWilson Ding static void mvebu_uart_rx_chars(struct uart_port *port, unsigned int status)
22230530791SWilson Ding {
22330530791SWilson Ding 	struct tty_port *tport = &port->state->port;
22430530791SWilson Ding 	unsigned char ch = 0;
22530530791SWilson Ding 	char flag = 0;
22630530791SWilson Ding 
22730530791SWilson Ding 	do {
2285218d769SMiquel Raynal 		if (status & STAT_RX_RDY(port)) {
2295218d769SMiquel Raynal 			ch = readl(port->membase + UART_RBR(port));
23030530791SWilson Ding 			ch &= 0xff;
23130530791SWilson Ding 			flag = TTY_NORMAL;
23230530791SWilson Ding 			port->icount.rx++;
23330530791SWilson Ding 
23430530791SWilson Ding 			if (status & STAT_PAR_ERR)
23530530791SWilson Ding 				port->icount.parity++;
23630530791SWilson Ding 		}
23730530791SWilson Ding 
23830530791SWilson Ding 		if (status & STAT_BRK_DET) {
23930530791SWilson Ding 			port->icount.brk++;
24030530791SWilson Ding 			status &= ~(STAT_FRM_ERR | STAT_PAR_ERR);
24130530791SWilson Ding 			if (uart_handle_break(port))
24230530791SWilson Ding 				goto ignore_char;
24330530791SWilson Ding 		}
24430530791SWilson Ding 
24530530791SWilson Ding 		if (status & STAT_OVR_ERR)
24630530791SWilson Ding 			port->icount.overrun++;
24730530791SWilson Ding 
24830530791SWilson Ding 		if (status & STAT_FRM_ERR)
24930530791SWilson Ding 			port->icount.frame++;
25030530791SWilson Ding 
25130530791SWilson Ding 		if (uart_handle_sysrq_char(port, ch))
25230530791SWilson Ding 			goto ignore_char;
25330530791SWilson Ding 
25430530791SWilson Ding 		if (status & port->ignore_status_mask & STAT_PAR_ERR)
2555218d769SMiquel Raynal 			status &= ~STAT_RX_RDY(port);
25630530791SWilson Ding 
25730530791SWilson Ding 		status &= port->read_status_mask;
25830530791SWilson Ding 
25930530791SWilson Ding 		if (status & STAT_PAR_ERR)
26030530791SWilson Ding 			flag = TTY_PARITY;
26130530791SWilson Ding 
26230530791SWilson Ding 		status &= ~port->ignore_status_mask;
26330530791SWilson Ding 
2645218d769SMiquel Raynal 		if (status & STAT_RX_RDY(port))
26530530791SWilson Ding 			tty_insert_flip_char(tport, ch, flag);
26630530791SWilson Ding 
26730530791SWilson Ding 		if (status & STAT_BRK_DET)
26830530791SWilson Ding 			tty_insert_flip_char(tport, 0, TTY_BREAK);
26930530791SWilson Ding 
27030530791SWilson Ding 		if (status & STAT_FRM_ERR)
27130530791SWilson Ding 			tty_insert_flip_char(tport, 0, TTY_FRAME);
27230530791SWilson Ding 
27330530791SWilson Ding 		if (status & STAT_OVR_ERR)
27430530791SWilson Ding 			tty_insert_flip_char(tport, 0, TTY_OVERRUN);
27530530791SWilson Ding 
27630530791SWilson Ding ignore_char:
27730530791SWilson Ding 		status = readl(port->membase + UART_STAT);
2785218d769SMiquel Raynal 	} while (status & (STAT_RX_RDY(port) | STAT_BRK_DET));
27930530791SWilson Ding 
28030530791SWilson Ding 	tty_flip_buffer_push(tport);
28130530791SWilson Ding }
28230530791SWilson Ding 
28330530791SWilson Ding static void mvebu_uart_tx_chars(struct uart_port *port, unsigned int status)
28430530791SWilson Ding {
28530530791SWilson Ding 	struct circ_buf *xmit = &port->state->xmit;
28630530791SWilson Ding 	unsigned int count;
28730530791SWilson Ding 	unsigned int st;
28830530791SWilson Ding 
28930530791SWilson Ding 	if (port->x_char) {
2905218d769SMiquel Raynal 		writel(port->x_char, port->membase + UART_TSH(port));
29130530791SWilson Ding 		port->icount.tx++;
29230530791SWilson Ding 		port->x_char = 0;
29330530791SWilson Ding 		return;
29430530791SWilson Ding 	}
29530530791SWilson Ding 
29630530791SWilson Ding 	if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
29730530791SWilson Ding 		mvebu_uart_stop_tx(port);
29830530791SWilson Ding 		return;
29930530791SWilson Ding 	}
30030530791SWilson Ding 
30130530791SWilson Ding 	for (count = 0; count < port->fifosize; count++) {
3025218d769SMiquel Raynal 		writel(xmit->buf[xmit->tail], port->membase + UART_TSH(port));
30330530791SWilson Ding 		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
30430530791SWilson Ding 		port->icount.tx++;
30530530791SWilson Ding 
30630530791SWilson Ding 		if (uart_circ_empty(xmit))
30730530791SWilson Ding 			break;
30830530791SWilson Ding 
30930530791SWilson Ding 		st = readl(port->membase + UART_STAT);
31030530791SWilson Ding 		if (st & STAT_TX_FIFO_FUL)
31130530791SWilson Ding 			break;
31230530791SWilson Ding 	}
31330530791SWilson Ding 
31430530791SWilson Ding 	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
31530530791SWilson Ding 		uart_write_wakeup(port);
31630530791SWilson Ding 
31730530791SWilson Ding 	if (uart_circ_empty(xmit))
31830530791SWilson Ding 		mvebu_uart_stop_tx(port);
31930530791SWilson Ding }
32030530791SWilson Ding 
32130530791SWilson Ding static irqreturn_t mvebu_uart_isr(int irq, void *dev_id)
32230530791SWilson Ding {
32330530791SWilson Ding 	struct uart_port *port = (struct uart_port *)dev_id;
32430530791SWilson Ding 	unsigned int st = readl(port->membase + UART_STAT);
32530530791SWilson Ding 
3265218d769SMiquel Raynal 	if (st & (STAT_RX_RDY(port) | STAT_OVR_ERR | STAT_FRM_ERR |
3275218d769SMiquel Raynal 		  STAT_BRK_DET))
32830530791SWilson Ding 		mvebu_uart_rx_chars(port, st);
32930530791SWilson Ding 
3305218d769SMiquel Raynal 	if (st & STAT_TX_RDY(port))
33130530791SWilson Ding 		mvebu_uart_tx_chars(port, st);
33230530791SWilson Ding 
33330530791SWilson Ding 	return IRQ_HANDLED;
33430530791SWilson Ding }
33530530791SWilson Ding 
33695f78768SMiquel Raynal static irqreturn_t mvebu_uart_rx_isr(int irq, void *dev_id)
33795f78768SMiquel Raynal {
33895f78768SMiquel Raynal 	struct uart_port *port = (struct uart_port *)dev_id;
33995f78768SMiquel Raynal 	unsigned int st = readl(port->membase + UART_STAT);
34095f78768SMiquel Raynal 
34195f78768SMiquel Raynal 	if (st & (STAT_RX_RDY(port) | STAT_OVR_ERR | STAT_FRM_ERR |
34295f78768SMiquel Raynal 			STAT_BRK_DET))
34395f78768SMiquel Raynal 		mvebu_uart_rx_chars(port, st);
34495f78768SMiquel Raynal 
34595f78768SMiquel Raynal 	return IRQ_HANDLED;
34695f78768SMiquel Raynal }
34795f78768SMiquel Raynal 
34895f78768SMiquel Raynal static irqreturn_t mvebu_uart_tx_isr(int irq, void *dev_id)
34995f78768SMiquel Raynal {
35095f78768SMiquel Raynal 	struct uart_port *port = (struct uart_port *)dev_id;
35195f78768SMiquel Raynal 	unsigned int st = readl(port->membase + UART_STAT);
35295f78768SMiquel Raynal 
35395f78768SMiquel Raynal 	if (st & STAT_TX_RDY(port))
35495f78768SMiquel Raynal 		mvebu_uart_tx_chars(port, st);
35595f78768SMiquel Raynal 
35695f78768SMiquel Raynal 	return IRQ_HANDLED;
35795f78768SMiquel Raynal }
35895f78768SMiquel Raynal 
35930530791SWilson Ding static int mvebu_uart_startup(struct uart_port *port)
36030530791SWilson Ding {
36195f78768SMiquel Raynal 	struct mvebu_uart *mvuart = to_mvuart(port);
3625218d769SMiquel Raynal 	unsigned int ctl;
36330530791SWilson Ding 	int ret;
36430530791SWilson Ding 
36530530791SWilson Ding 	writel(CTRL_TXFIFO_RST | CTRL_RXFIFO_RST,
3665218d769SMiquel Raynal 	       port->membase + UART_CTRL(port));
36730530791SWilson Ding 	udelay(1);
3682ff23c48SAllen Yan 
3692ff23c48SAllen Yan 	/* Clear the error bits of state register before IRQ request */
3702ff23c48SAllen Yan 	ret = readl(port->membase + UART_STAT);
3712ff23c48SAllen Yan 	ret |= STAT_BRK_ERR;
3722ff23c48SAllen Yan 	writel(ret, port->membase + UART_STAT);
3732ff23c48SAllen Yan 
3745218d769SMiquel Raynal 	writel(CTRL_BRK_INT, port->membase + UART_CTRL(port));
3755218d769SMiquel Raynal 
3765218d769SMiquel Raynal 	ctl = readl(port->membase + UART_INTR(port));
3775218d769SMiquel Raynal 	ctl |= CTRL_RX_RDY_INT(port);
3785218d769SMiquel Raynal 	writel(ctl, port->membase + UART_INTR(port));
37930530791SWilson Ding 
38095f78768SMiquel Raynal 	if (!mvuart->irq[UART_TX_IRQ]) {
38195f78768SMiquel Raynal 		/* Old bindings with just one interrupt (UART0 only) */
38295f78768SMiquel Raynal 		ret = devm_request_irq(port->dev, mvuart->irq[UART_IRQ_SUM],
38395f78768SMiquel Raynal 				       mvebu_uart_isr, port->irqflags,
38495f78768SMiquel Raynal 				       dev_name(port->dev), port);
38530530791SWilson Ding 		if (ret) {
38695f78768SMiquel Raynal 			dev_err(port->dev, "unable to request IRQ %d\n",
38795f78768SMiquel Raynal 				mvuart->irq[UART_IRQ_SUM]);
38830530791SWilson Ding 			return ret;
38930530791SWilson Ding 		}
39095f78768SMiquel Raynal 	} else {
39195f78768SMiquel Raynal 		/* New bindings with an IRQ for RX and TX (both UART) */
39295f78768SMiquel Raynal 		ret = devm_request_irq(port->dev, mvuart->irq[UART_RX_IRQ],
39395f78768SMiquel Raynal 				       mvebu_uart_rx_isr, port->irqflags,
39495f78768SMiquel Raynal 				       dev_name(port->dev), port);
39595f78768SMiquel Raynal 		if (ret) {
39695f78768SMiquel Raynal 			dev_err(port->dev, "unable to request IRQ %d\n",
39795f78768SMiquel Raynal 				mvuart->irq[UART_RX_IRQ]);
39895f78768SMiquel Raynal 			return ret;
39995f78768SMiquel Raynal 		}
40095f78768SMiquel Raynal 
40195f78768SMiquel Raynal 		ret = devm_request_irq(port->dev, mvuart->irq[UART_TX_IRQ],
40295f78768SMiquel Raynal 				       mvebu_uart_tx_isr, port->irqflags,
40395f78768SMiquel Raynal 				       dev_name(port->dev),
40495f78768SMiquel Raynal 				       port);
40595f78768SMiquel Raynal 		if (ret) {
40695f78768SMiquel Raynal 			dev_err(port->dev, "unable to request IRQ %d\n",
40795f78768SMiquel Raynal 				mvuart->irq[UART_TX_IRQ]);
40895f78768SMiquel Raynal 			devm_free_irq(port->dev, mvuart->irq[UART_RX_IRQ],
40995f78768SMiquel Raynal 				      port);
41095f78768SMiquel Raynal 			return ret;
41195f78768SMiquel Raynal 		}
41295f78768SMiquel Raynal 	}
41330530791SWilson Ding 
41430530791SWilson Ding 	return 0;
41530530791SWilson Ding }
41630530791SWilson Ding 
41730530791SWilson Ding static void mvebu_uart_shutdown(struct uart_port *port)
41830530791SWilson Ding {
41995f78768SMiquel Raynal 	struct mvebu_uart *mvuart = to_mvuart(port);
42095f78768SMiquel Raynal 
4215218d769SMiquel Raynal 	writel(0, port->membase + UART_INTR(port));
422c2c1659bSThomas Petazzoni 
42395f78768SMiquel Raynal 	if (!mvuart->irq[UART_TX_IRQ]) {
42495f78768SMiquel Raynal 		devm_free_irq(port->dev, mvuart->irq[UART_IRQ_SUM], port);
42595f78768SMiquel Raynal 	} else {
42695f78768SMiquel Raynal 		devm_free_irq(port->dev, mvuart->irq[UART_RX_IRQ], port);
42795f78768SMiquel Raynal 		devm_free_irq(port->dev, mvuart->irq[UART_TX_IRQ], port);
42895f78768SMiquel Raynal 	}
42930530791SWilson Ding }
43030530791SWilson Ding 
43168a0db1dSAllen Yan static int mvebu_uart_baud_rate_set(struct uart_port *port, unsigned int baud)
43268a0db1dSAllen Yan {
43368a0db1dSAllen Yan 	struct mvebu_uart *mvuart = to_mvuart(port);
43468a0db1dSAllen Yan 	unsigned int baud_rate_div;
43568a0db1dSAllen Yan 	u32 brdv;
43668a0db1dSAllen Yan 
43768a0db1dSAllen Yan 	if (IS_ERR(mvuart->clk))
43868a0db1dSAllen Yan 		return -PTR_ERR(mvuart->clk);
43968a0db1dSAllen Yan 
44068a0db1dSAllen Yan 	/*
44168a0db1dSAllen Yan 	 * The UART clock is divided by the value of the divisor to generate
44268a0db1dSAllen Yan 	 * UCLK_OUT clock, which is 16 times faster than the baudrate.
44368a0db1dSAllen Yan 	 * This prescaler can achieve all standard baudrates until 230400.
44468a0db1dSAllen Yan 	 * Higher baudrates could be achieved for the extended UART by using the
44568a0db1dSAllen Yan 	 * programmable oversampling stack (also called fractional divisor).
44668a0db1dSAllen Yan 	 */
44768a0db1dSAllen Yan 	baud_rate_div = DIV_ROUND_UP(port->uartclk, baud * 16);
44868a0db1dSAllen Yan 	brdv = readl(port->membase + UART_BRDV);
44968a0db1dSAllen Yan 	brdv &= ~BRDV_BAUD_MASK;
45068a0db1dSAllen Yan 	brdv |= baud_rate_div;
45168a0db1dSAllen Yan 	writel(brdv, port->membase + UART_BRDV);
45268a0db1dSAllen Yan 
45368a0db1dSAllen Yan 	return 0;
45468a0db1dSAllen Yan }
45568a0db1dSAllen Yan 
45630530791SWilson Ding static void mvebu_uart_set_termios(struct uart_port *port,
45730530791SWilson Ding 				   struct ktermios *termios,
45830530791SWilson Ding 				   struct ktermios *old)
45930530791SWilson Ding {
46030530791SWilson Ding 	unsigned long flags;
46130530791SWilson Ding 	unsigned int baud;
46230530791SWilson Ding 
46330530791SWilson Ding 	spin_lock_irqsave(&port->lock, flags);
46430530791SWilson Ding 
4655218d769SMiquel Raynal 	port->read_status_mask = STAT_RX_RDY(port) | STAT_OVR_ERR |
4665218d769SMiquel Raynal 		STAT_TX_RDY(port) | STAT_TX_FIFO_FUL;
46730530791SWilson Ding 
46830530791SWilson Ding 	if (termios->c_iflag & INPCK)
46930530791SWilson Ding 		port->read_status_mask |= STAT_FRM_ERR | STAT_PAR_ERR;
47030530791SWilson Ding 
47130530791SWilson Ding 	port->ignore_status_mask = 0;
47230530791SWilson Ding 	if (termios->c_iflag & IGNPAR)
47330530791SWilson Ding 		port->ignore_status_mask |=
47430530791SWilson Ding 			STAT_FRM_ERR | STAT_PAR_ERR | STAT_OVR_ERR;
47530530791SWilson Ding 
47630530791SWilson Ding 	if ((termios->c_cflag & CREAD) == 0)
4775218d769SMiquel Raynal 		port->ignore_status_mask |= STAT_RX_RDY(port) | STAT_BRK_ERR;
47830530791SWilson Ding 
47968a0db1dSAllen Yan 	/*
48068a0db1dSAllen Yan 	 * Maximum achievable frequency with simple baudrate divisor is 230400.
48168a0db1dSAllen Yan 	 * Since the error per bit frame would be of more than 15%, achieving
48268a0db1dSAllen Yan 	 * higher frequencies would require to implement the fractional divisor
48368a0db1dSAllen Yan 	 * feature.
48468a0db1dSAllen Yan 	 */
48568a0db1dSAllen Yan 	baud = uart_get_baud_rate(port, termios, old, 0, 230400);
48668a0db1dSAllen Yan 	if (mvebu_uart_baud_rate_set(port, baud)) {
48768a0db1dSAllen Yan 		/* No clock available, baudrate cannot be changed */
48830530791SWilson Ding 		if (old)
48968a0db1dSAllen Yan 			baud = uart_get_baud_rate(port, old, NULL, 0, 230400);
49068a0db1dSAllen Yan 	} else {
49168a0db1dSAllen Yan 		tty_termios_encode_baud_rate(termios, baud, baud);
49230530791SWilson Ding 		uart_update_timeout(port, termios->c_cflag, baud);
49368a0db1dSAllen Yan 	}
49468a0db1dSAllen Yan 
49568a0db1dSAllen Yan 	/* Only the following flag changes are supported */
49668a0db1dSAllen Yan 	if (old) {
49768a0db1dSAllen Yan 		termios->c_iflag &= INPCK | IGNPAR;
49868a0db1dSAllen Yan 		termios->c_iflag |= old->c_iflag & ~(INPCK | IGNPAR);
49968a0db1dSAllen Yan 		termios->c_cflag &= CREAD | CBAUD;
50068a0db1dSAllen Yan 		termios->c_cflag |= old->c_cflag & ~(CREAD | CBAUD);
50168a0db1dSAllen Yan 		termios->c_lflag = old->c_lflag;
50268a0db1dSAllen Yan 	}
50330530791SWilson Ding 
50430530791SWilson Ding 	spin_unlock_irqrestore(&port->lock, flags);
50530530791SWilson Ding }
50630530791SWilson Ding 
50730530791SWilson Ding static const char *mvebu_uart_type(struct uart_port *port)
50830530791SWilson Ding {
50930530791SWilson Ding 	return MVEBU_UART_TYPE;
51030530791SWilson Ding }
51130530791SWilson Ding 
51230530791SWilson Ding static void mvebu_uart_release_port(struct uart_port *port)
51330530791SWilson Ding {
51430530791SWilson Ding 	/* Nothing to do here */
51530530791SWilson Ding }
51630530791SWilson Ding 
51730530791SWilson Ding static int mvebu_uart_request_port(struct uart_port *port)
51830530791SWilson Ding {
51930530791SWilson Ding 	return 0;
52030530791SWilson Ding }
52130530791SWilson Ding 
52230530791SWilson Ding #ifdef CONFIG_CONSOLE_POLL
52330530791SWilson Ding static int mvebu_uart_get_poll_char(struct uart_port *port)
52430530791SWilson Ding {
52530530791SWilson Ding 	unsigned int st = readl(port->membase + UART_STAT);
52630530791SWilson Ding 
5275218d769SMiquel Raynal 	if (!(st & STAT_RX_RDY(port)))
52830530791SWilson Ding 		return NO_POLL_CHAR;
52930530791SWilson Ding 
5305218d769SMiquel Raynal 	return readl(port->membase + UART_RBR(port));
53130530791SWilson Ding }
53230530791SWilson Ding 
53330530791SWilson Ding static void mvebu_uart_put_poll_char(struct uart_port *port, unsigned char c)
53430530791SWilson Ding {
53530530791SWilson Ding 	unsigned int st;
53630530791SWilson Ding 
53730530791SWilson Ding 	for (;;) {
53830530791SWilson Ding 		st = readl(port->membase + UART_STAT);
53930530791SWilson Ding 
54030530791SWilson Ding 		if (!(st & STAT_TX_FIFO_FUL))
54130530791SWilson Ding 			break;
54230530791SWilson Ding 
54330530791SWilson Ding 		udelay(1);
54430530791SWilson Ding 	}
54530530791SWilson Ding 
5465218d769SMiquel Raynal 	writel(c, port->membase + UART_TSH(port));
54730530791SWilson Ding }
54830530791SWilson Ding #endif
54930530791SWilson Ding 
55030530791SWilson Ding static const struct uart_ops mvebu_uart_ops = {
55130530791SWilson Ding 	.tx_empty	= mvebu_uart_tx_empty,
55230530791SWilson Ding 	.set_mctrl	= mvebu_uart_set_mctrl,
55330530791SWilson Ding 	.get_mctrl	= mvebu_uart_get_mctrl,
55430530791SWilson Ding 	.stop_tx	= mvebu_uart_stop_tx,
55530530791SWilson Ding 	.start_tx	= mvebu_uart_start_tx,
55630530791SWilson Ding 	.stop_rx	= mvebu_uart_stop_rx,
55730530791SWilson Ding 	.break_ctl	= mvebu_uart_break_ctl,
55830530791SWilson Ding 	.startup	= mvebu_uart_startup,
55930530791SWilson Ding 	.shutdown	= mvebu_uart_shutdown,
56030530791SWilson Ding 	.set_termios	= mvebu_uart_set_termios,
56130530791SWilson Ding 	.type		= mvebu_uart_type,
56230530791SWilson Ding 	.release_port	= mvebu_uart_release_port,
56330530791SWilson Ding 	.request_port	= mvebu_uart_request_port,
56430530791SWilson Ding #ifdef CONFIG_CONSOLE_POLL
56530530791SWilson Ding 	.poll_get_char	= mvebu_uart_get_poll_char,
56630530791SWilson Ding 	.poll_put_char	= mvebu_uart_put_poll_char,
56730530791SWilson Ding #endif
56830530791SWilson Ding };
56930530791SWilson Ding 
57030530791SWilson Ding /* Console Driver Operations  */
57130530791SWilson Ding 
57230530791SWilson Ding #ifdef CONFIG_SERIAL_MVEBU_CONSOLE
57330530791SWilson Ding /* Early Console */
57430530791SWilson Ding static void mvebu_uart_putc(struct uart_port *port, int c)
57530530791SWilson Ding {
57630530791SWilson Ding 	unsigned int st;
57730530791SWilson Ding 
57830530791SWilson Ding 	for (;;) {
57930530791SWilson Ding 		st = readl(port->membase + UART_STAT);
58030530791SWilson Ding 		if (!(st & STAT_TX_FIFO_FUL))
58130530791SWilson Ding 			break;
58230530791SWilson Ding 	}
58330530791SWilson Ding 
5845218d769SMiquel Raynal 	/* At early stage, DT is not parsed yet, only use UART0 */
5855218d769SMiquel Raynal 	writel(c, port->membase + UART_STD_TSH);
58630530791SWilson Ding 
58730530791SWilson Ding 	for (;;) {
58830530791SWilson Ding 		st = readl(port->membase + UART_STAT);
58930530791SWilson Ding 		if (st & STAT_TX_FIFO_EMP)
59030530791SWilson Ding 			break;
59130530791SWilson Ding 	}
59230530791SWilson Ding }
59330530791SWilson Ding 
59430530791SWilson Ding static void mvebu_uart_putc_early_write(struct console *con,
59530530791SWilson Ding 					const char *s,
59630530791SWilson Ding 					unsigned n)
59730530791SWilson Ding {
59830530791SWilson Ding 	struct earlycon_device *dev = con->data;
59930530791SWilson Ding 
60030530791SWilson Ding 	uart_console_write(&dev->port, s, n, mvebu_uart_putc);
60130530791SWilson Ding }
60230530791SWilson Ding 
60330530791SWilson Ding static int __init
60430530791SWilson Ding mvebu_uart_early_console_setup(struct earlycon_device *device,
60530530791SWilson Ding 			       const char *opt)
60630530791SWilson Ding {
60730530791SWilson Ding 	if (!device->port.membase)
60830530791SWilson Ding 		return -ENODEV;
60930530791SWilson Ding 
61030530791SWilson Ding 	device->con->write = mvebu_uart_putc_early_write;
61130530791SWilson Ding 
61230530791SWilson Ding 	return 0;
61330530791SWilson Ding }
61430530791SWilson Ding 
61530530791SWilson Ding EARLYCON_DECLARE(ar3700_uart, mvebu_uart_early_console_setup);
61630530791SWilson Ding OF_EARLYCON_DECLARE(ar3700_uart, "marvell,armada-3700-uart",
61730530791SWilson Ding 		    mvebu_uart_early_console_setup);
61830530791SWilson Ding 
61930530791SWilson Ding static void wait_for_xmitr(struct uart_port *port)
62030530791SWilson Ding {
62130530791SWilson Ding 	u32 val;
62230530791SWilson Ding 
62330530791SWilson Ding 	readl_poll_timeout_atomic(port->membase + UART_STAT, val,
62430530791SWilson Ding 				  (val & STAT_TX_EMP), 1, 10000);
62530530791SWilson Ding }
62630530791SWilson Ding 
62730530791SWilson Ding static void mvebu_uart_console_putchar(struct uart_port *port, int ch)
62830530791SWilson Ding {
62930530791SWilson Ding 	wait_for_xmitr(port);
6305218d769SMiquel Raynal 	writel(ch, port->membase + UART_TSH(port));
63130530791SWilson Ding }
63230530791SWilson Ding 
63330530791SWilson Ding static void mvebu_uart_console_write(struct console *co, const char *s,
63430530791SWilson Ding 				     unsigned int count)
63530530791SWilson Ding {
63630530791SWilson Ding 	struct uart_port *port = &mvebu_uart_ports[co->index];
63730530791SWilson Ding 	unsigned long flags;
6385218d769SMiquel Raynal 	unsigned int ier, intr, ctl;
63930530791SWilson Ding 	int locked = 1;
64030530791SWilson Ding 
64130530791SWilson Ding 	if (oops_in_progress)
64230530791SWilson Ding 		locked = spin_trylock_irqsave(&port->lock, flags);
64330530791SWilson Ding 	else
64430530791SWilson Ding 		spin_lock_irqsave(&port->lock, flags);
64530530791SWilson Ding 
6465218d769SMiquel Raynal 	ier = readl(port->membase + UART_CTRL(port)) & CTRL_BRK_INT;
6475218d769SMiquel Raynal 	intr = readl(port->membase + UART_INTR(port)) &
6485218d769SMiquel Raynal 		(CTRL_RX_RDY_INT(port) | CTRL_TX_RDY_INT(port));
6495218d769SMiquel Raynal 	writel(0, port->membase + UART_CTRL(port));
6505218d769SMiquel Raynal 	writel(0, port->membase + UART_INTR(port));
65130530791SWilson Ding 
65230530791SWilson Ding 	uart_console_write(port, s, count, mvebu_uart_console_putchar);
65330530791SWilson Ding 
65430530791SWilson Ding 	wait_for_xmitr(port);
65530530791SWilson Ding 
65630530791SWilson Ding 	if (ier)
6575218d769SMiquel Raynal 		writel(ier, port->membase + UART_CTRL(port));
6585218d769SMiquel Raynal 
6595218d769SMiquel Raynal 	if (intr) {
6605218d769SMiquel Raynal 		ctl = intr | readl(port->membase + UART_INTR(port));
6615218d769SMiquel Raynal 		writel(ctl, port->membase + UART_INTR(port));
6625218d769SMiquel Raynal 	}
66330530791SWilson Ding 
66430530791SWilson Ding 	if (locked)
66530530791SWilson Ding 		spin_unlock_irqrestore(&port->lock, flags);
66630530791SWilson Ding }
66730530791SWilson Ding 
66830530791SWilson Ding static int mvebu_uart_console_setup(struct console *co, char *options)
66930530791SWilson Ding {
67030530791SWilson Ding 	struct uart_port *port;
67130530791SWilson Ding 	int baud = 9600;
67230530791SWilson Ding 	int bits = 8;
67330530791SWilson Ding 	int parity = 'n';
67430530791SWilson Ding 	int flow = 'n';
67530530791SWilson Ding 
67630530791SWilson Ding 	if (co->index < 0 || co->index >= MVEBU_NR_UARTS)
67730530791SWilson Ding 		return -EINVAL;
67830530791SWilson Ding 
67930530791SWilson Ding 	port = &mvebu_uart_ports[co->index];
68030530791SWilson Ding 
68130530791SWilson Ding 	if (!port->mapbase || !port->membase) {
68230530791SWilson Ding 		pr_debug("console on ttyMV%i not present\n", co->index);
68330530791SWilson Ding 		return -ENODEV;
68430530791SWilson Ding 	}
68530530791SWilson Ding 
68630530791SWilson Ding 	if (options)
68730530791SWilson Ding 		uart_parse_options(options, &baud, &parity, &bits, &flow);
68830530791SWilson Ding 
68930530791SWilson Ding 	return uart_set_options(port, co, baud, parity, bits, flow);
69030530791SWilson Ding }
69130530791SWilson Ding 
69230530791SWilson Ding static struct uart_driver mvebu_uart_driver;
69330530791SWilson Ding 
69430530791SWilson Ding static struct console mvebu_uart_console = {
69530530791SWilson Ding 	.name	= "ttyMV",
69630530791SWilson Ding 	.write	= mvebu_uart_console_write,
69730530791SWilson Ding 	.device	= uart_console_device,
69830530791SWilson Ding 	.setup	= mvebu_uart_console_setup,
69930530791SWilson Ding 	.flags	= CON_PRINTBUFFER,
70030530791SWilson Ding 	.index	= -1,
70130530791SWilson Ding 	.data	= &mvebu_uart_driver,
70230530791SWilson Ding };
70330530791SWilson Ding 
70430530791SWilson Ding static int __init mvebu_uart_console_init(void)
70530530791SWilson Ding {
70630530791SWilson Ding 	register_console(&mvebu_uart_console);
70730530791SWilson Ding 	return 0;
70830530791SWilson Ding }
70930530791SWilson Ding 
71030530791SWilson Ding console_initcall(mvebu_uart_console_init);
71130530791SWilson Ding 
71230530791SWilson Ding 
71330530791SWilson Ding #endif /* CONFIG_SERIAL_MVEBU_CONSOLE */
71430530791SWilson Ding 
71530530791SWilson Ding static struct uart_driver mvebu_uart_driver = {
71630530791SWilson Ding 	.owner			= THIS_MODULE,
71702c33330SYehuda Yitschak 	.driver_name		= DRIVER_NAME,
71830530791SWilson Ding 	.dev_name		= "ttyMV",
71930530791SWilson Ding 	.nr			= MVEBU_NR_UARTS,
72030530791SWilson Ding #ifdef CONFIG_SERIAL_MVEBU_CONSOLE
72130530791SWilson Ding 	.cons			= &mvebu_uart_console,
72230530791SWilson Ding #endif
72330530791SWilson Ding };
72430530791SWilson Ding 
7255218d769SMiquel Raynal static const struct of_device_id mvebu_uart_of_match[];
7265218d769SMiquel Raynal 
72794228f95SAllen Yan /* Counter to keep track of each UART port id when not using CONFIG_OF */
72894228f95SAllen Yan static int uart_num_counter;
72994228f95SAllen Yan 
73030530791SWilson Ding static int mvebu_uart_probe(struct platform_device *pdev)
73130530791SWilson Ding {
73230530791SWilson Ding 	struct resource *reg = platform_get_resource(pdev, IORESOURCE_MEM, 0);
7335218d769SMiquel Raynal 	const struct of_device_id *match = of_match_device(mvebu_uart_of_match,
7345218d769SMiquel Raynal 							   &pdev->dev);
73530530791SWilson Ding 	struct uart_port *port;
7365218d769SMiquel Raynal 	struct mvebu_uart *mvuart;
73795f78768SMiquel Raynal 	int ret, id, irq;
73830530791SWilson Ding 
73995f78768SMiquel Raynal 	if (!reg) {
74095f78768SMiquel Raynal 		dev_err(&pdev->dev, "no registers defined\n");
74130530791SWilson Ding 		return -EINVAL;
74230530791SWilson Ding 	}
74330530791SWilson Ding 
74494228f95SAllen Yan 	/* Assume that all UART ports have a DT alias or none has */
74594228f95SAllen Yan 	id = of_alias_get_id(pdev->dev.of_node, "serial");
74694228f95SAllen Yan 	if (!pdev->dev.of_node || id < 0)
74794228f95SAllen Yan 		pdev->id = uart_num_counter++;
74894228f95SAllen Yan 	else
74994228f95SAllen Yan 		pdev->id = id;
75094228f95SAllen Yan 
75194228f95SAllen Yan 	if (pdev->id >= MVEBU_NR_UARTS) {
75294228f95SAllen Yan 		dev_err(&pdev->dev, "cannot have more than %d UART ports\n",
75394228f95SAllen Yan 			MVEBU_NR_UARTS);
75494228f95SAllen Yan 		return -EINVAL;
75594228f95SAllen Yan 	}
75694228f95SAllen Yan 
75794228f95SAllen Yan 	port = &mvebu_uart_ports[pdev->id];
75830530791SWilson Ding 
75930530791SWilson Ding 	spin_lock_init(&port->lock);
76030530791SWilson Ding 
76130530791SWilson Ding 	port->dev        = &pdev->dev;
76230530791SWilson Ding 	port->type       = PORT_MVEBU;
76330530791SWilson Ding 	port->ops        = &mvebu_uart_ops;
76430530791SWilson Ding 	port->regshift   = 0;
76530530791SWilson Ding 
76630530791SWilson Ding 	port->fifosize   = 32;
76730530791SWilson Ding 	port->iotype     = UPIO_MEM32;
76830530791SWilson Ding 	port->flags      = UPF_FIXED_PORT;
76994228f95SAllen Yan 	port->line       = pdev->id;
77030530791SWilson Ding 
77195f78768SMiquel Raynal 	/*
77295f78768SMiquel Raynal 	 * IRQ number is not stored in this structure because we may have two of
77395f78768SMiquel Raynal 	 * them per port (RX and TX). Instead, use the driver UART structure
77495f78768SMiquel Raynal 	 * array so called ->irq[].
77595f78768SMiquel Raynal 	 */
77695f78768SMiquel Raynal 	port->irq        = 0;
77730530791SWilson Ding 	port->irqflags   = 0;
77830530791SWilson Ding 	port->mapbase    = reg->start;
77930530791SWilson Ding 
78030530791SWilson Ding 	port->membase = devm_ioremap_resource(&pdev->dev, reg);
78130530791SWilson Ding 	if (IS_ERR(port->membase))
78230530791SWilson Ding 		return -PTR_ERR(port->membase);
78330530791SWilson Ding 
7845218d769SMiquel Raynal 	mvuart = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_uart),
78530530791SWilson Ding 			      GFP_KERNEL);
7865218d769SMiquel Raynal 	if (!mvuart)
78730530791SWilson Ding 		return -ENOMEM;
78830530791SWilson Ding 
78968a0db1dSAllen Yan 	/* Get controller data depending on the compatible string */
7905218d769SMiquel Raynal 	mvuart->data = (struct mvebu_uart_driver_data *)match->data;
7915218d769SMiquel Raynal 	mvuart->port = port;
79230530791SWilson Ding 
7935218d769SMiquel Raynal 	port->private_data = mvuart;
7945218d769SMiquel Raynal 	platform_set_drvdata(pdev, mvuart);
79530530791SWilson Ding 
79668a0db1dSAllen Yan 	/* Get fixed clock frequency */
79768a0db1dSAllen Yan 	mvuart->clk = devm_clk_get(&pdev->dev, NULL);
79868a0db1dSAllen Yan 	if (IS_ERR(mvuart->clk)) {
79968a0db1dSAllen Yan 		if (PTR_ERR(mvuart->clk) == -EPROBE_DEFER)
80068a0db1dSAllen Yan 			return PTR_ERR(mvuart->clk);
80168a0db1dSAllen Yan 
80268a0db1dSAllen Yan 		if (IS_EXTENDED(port)) {
80368a0db1dSAllen Yan 			dev_err(&pdev->dev, "unable to get UART clock\n");
80468a0db1dSAllen Yan 			return PTR_ERR(mvuart->clk);
80568a0db1dSAllen Yan 		}
80668a0db1dSAllen Yan 	} else {
80768a0db1dSAllen Yan 		if (!clk_prepare_enable(mvuart->clk))
80868a0db1dSAllen Yan 			port->uartclk = clk_get_rate(mvuart->clk);
80968a0db1dSAllen Yan 	}
81068a0db1dSAllen Yan 
81195f78768SMiquel Raynal 	/* Manage interrupts */
81295f78768SMiquel Raynal 	memset(mvuart->irq, 0, UART_IRQ_COUNT);
81395f78768SMiquel Raynal 	if (platform_irq_count(pdev) == 1) {
81495f78768SMiquel Raynal 		/* Old bindings: no name on the single unamed UART0 IRQ */
81595f78768SMiquel Raynal 		irq = platform_get_irq(pdev, 0);
81695f78768SMiquel Raynal 		if (irq < 0) {
81795f78768SMiquel Raynal 			dev_err(&pdev->dev, "unable to get UART IRQ\n");
81895f78768SMiquel Raynal 			return irq;
81995f78768SMiquel Raynal 		}
82095f78768SMiquel Raynal 
82195f78768SMiquel Raynal 		mvuart->irq[UART_IRQ_SUM] = irq;
82295f78768SMiquel Raynal 	} else {
82395f78768SMiquel Raynal 		/*
82495f78768SMiquel Raynal 		 * New bindings: named interrupts (RX, TX) for both UARTS,
82595f78768SMiquel Raynal 		 * only make use of uart-rx and uart-tx interrupts, do not use
82695f78768SMiquel Raynal 		 * uart-sum of UART0 port.
82795f78768SMiquel Raynal 		 */
82895f78768SMiquel Raynal 		irq = platform_get_irq_byname(pdev, "uart-rx");
82995f78768SMiquel Raynal 		if (irq < 0) {
83095f78768SMiquel Raynal 			dev_err(&pdev->dev, "unable to get 'uart-rx' IRQ\n");
83195f78768SMiquel Raynal 			return irq;
83295f78768SMiquel Raynal 		}
83395f78768SMiquel Raynal 
83495f78768SMiquel Raynal 		mvuart->irq[UART_RX_IRQ] = irq;
83595f78768SMiquel Raynal 
83695f78768SMiquel Raynal 		irq = platform_get_irq_byname(pdev, "uart-tx");
83795f78768SMiquel Raynal 		if (irq < 0) {
83895f78768SMiquel Raynal 			dev_err(&pdev->dev, "unable to get 'uart-tx' IRQ\n");
83995f78768SMiquel Raynal 			return irq;
84095f78768SMiquel Raynal 		}
84195f78768SMiquel Raynal 
84295f78768SMiquel Raynal 		mvuart->irq[UART_TX_IRQ] = irq;
84395f78768SMiquel Raynal 	}
84495f78768SMiquel Raynal 
8459c3d3ee1SAllen Yan 	/* UART Soft Reset*/
8469c3d3ee1SAllen Yan 	writel(CTRL_SOFT_RST, port->membase + UART_CTRL(port));
8479c3d3ee1SAllen Yan 	udelay(1);
8489c3d3ee1SAllen Yan 	writel(0, port->membase + UART_CTRL(port));
8499c3d3ee1SAllen Yan 
85030530791SWilson Ding 	ret = uart_add_one_port(&mvebu_uart_driver, port);
85130530791SWilson Ding 	if (ret)
85230530791SWilson Ding 		return ret;
85330530791SWilson Ding 	return 0;
85430530791SWilson Ding }
85530530791SWilson Ding 
8565218d769SMiquel Raynal static struct mvebu_uart_driver_data uart_std_driver_data = {
8575218d769SMiquel Raynal 	.is_ext = false,
8585218d769SMiquel Raynal 	.regs.rbr = UART_STD_RBR,
8595218d769SMiquel Raynal 	.regs.tsh = UART_STD_TSH,
8605218d769SMiquel Raynal 	.regs.ctrl = UART_STD_CTRL1,
8615218d769SMiquel Raynal 	.regs.intr = UART_STD_CTRL2,
8625218d769SMiquel Raynal 	.flags.ctrl_tx_rdy_int = CTRL_STD_TX_RDY_INT,
8635218d769SMiquel Raynal 	.flags.ctrl_rx_rdy_int = CTRL_STD_RX_RDY_INT,
8645218d769SMiquel Raynal 	.flags.stat_tx_rdy = STAT_STD_TX_RDY,
8655218d769SMiquel Raynal 	.flags.stat_rx_rdy = STAT_STD_RX_RDY,
8665218d769SMiquel Raynal };
8675218d769SMiquel Raynal 
86830530791SWilson Ding /* Match table for of_platform binding */
86930530791SWilson Ding static const struct of_device_id mvebu_uart_of_match[] = {
8705218d769SMiquel Raynal 	{
8715218d769SMiquel Raynal 		.compatible = "marvell,armada-3700-uart",
8725218d769SMiquel Raynal 		.data = (void *)&uart_std_driver_data,
8735218d769SMiquel Raynal 	},
87430530791SWilson Ding 	{}
87530530791SWilson Ding };
87630530791SWilson Ding 
87730530791SWilson Ding static struct platform_driver mvebu_uart_platform_driver = {
87830530791SWilson Ding 	.probe	= mvebu_uart_probe,
87930530791SWilson Ding 	.driver	= {
88030530791SWilson Ding 		.name  = "mvebu-uart",
88130530791SWilson Ding 		.of_match_table = of_match_ptr(mvebu_uart_of_match),
88289ebc274SPaul Gortmaker 		.suppress_bind_attrs = true,
88330530791SWilson Ding 	},
88430530791SWilson Ding };
88530530791SWilson Ding 
88630530791SWilson Ding static int __init mvebu_uart_init(void)
88730530791SWilson Ding {
88830530791SWilson Ding 	int ret;
88930530791SWilson Ding 
89030530791SWilson Ding 	ret = uart_register_driver(&mvebu_uart_driver);
89130530791SWilson Ding 	if (ret)
89230530791SWilson Ding 		return ret;
89330530791SWilson Ding 
89430530791SWilson Ding 	ret = platform_driver_register(&mvebu_uart_platform_driver);
89530530791SWilson Ding 	if (ret)
89630530791SWilson Ding 		uart_unregister_driver(&mvebu_uart_driver);
89730530791SWilson Ding 
89830530791SWilson Ding 	return ret;
89930530791SWilson Ding }
90030530791SWilson Ding arch_initcall(mvebu_uart_init);
901