xref: /openbmc/linux/drivers/tty/serial/mvebu-uart.c (revision 30434b07)
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 
825218d769SMiquel Raynal /* Register offsets, different depending on the UART */
835218d769SMiquel Raynal struct uart_regs_layout {
845218d769SMiquel Raynal 	unsigned int rbr;
855218d769SMiquel Raynal 	unsigned int tsh;
865218d769SMiquel Raynal 	unsigned int ctrl;
875218d769SMiquel Raynal 	unsigned int intr;
885218d769SMiquel Raynal };
8930530791SWilson Ding 
905218d769SMiquel Raynal /* Diverging flags */
915218d769SMiquel Raynal struct uart_flags {
925218d769SMiquel Raynal 	unsigned int ctrl_tx_rdy_int;
935218d769SMiquel Raynal 	unsigned int ctrl_rx_rdy_int;
945218d769SMiquel Raynal 	unsigned int stat_tx_rdy;
955218d769SMiquel Raynal 	unsigned int stat_rx_rdy;
965218d769SMiquel Raynal };
975218d769SMiquel Raynal 
985218d769SMiquel Raynal /* Driver data, a structure for each UART port */
995218d769SMiquel Raynal struct mvebu_uart_driver_data {
1005218d769SMiquel Raynal 	bool is_ext;
1015218d769SMiquel Raynal 	struct uart_regs_layout regs;
1025218d769SMiquel Raynal 	struct uart_flags flags;
1035218d769SMiquel Raynal };
1045218d769SMiquel Raynal 
1055218d769SMiquel Raynal /* MVEBU UART driver structure */
1065218d769SMiquel Raynal struct mvebu_uart {
10730530791SWilson Ding 	struct uart_port *port;
10830530791SWilson Ding 	struct clk *clk;
1095218d769SMiquel Raynal 	struct mvebu_uart_driver_data *data;
11030530791SWilson Ding };
11130530791SWilson Ding 
1125218d769SMiquel Raynal static struct mvebu_uart *to_mvuart(struct uart_port *port)
1135218d769SMiquel Raynal {
1145218d769SMiquel Raynal 	return (struct mvebu_uart *)port->private_data;
1155218d769SMiquel Raynal }
1165218d769SMiquel Raynal 
1175218d769SMiquel Raynal #define IS_EXTENDED(port) (to_mvuart(port)->data->is_ext)
1185218d769SMiquel Raynal 
1195218d769SMiquel Raynal #define UART_RBR(port) (to_mvuart(port)->data->regs.rbr)
1205218d769SMiquel Raynal #define UART_TSH(port) (to_mvuart(port)->data->regs.tsh)
1215218d769SMiquel Raynal #define UART_CTRL(port) (to_mvuart(port)->data->regs.ctrl)
1225218d769SMiquel Raynal #define UART_INTR(port) (to_mvuart(port)->data->regs.intr)
1235218d769SMiquel Raynal 
1245218d769SMiquel Raynal #define CTRL_TX_RDY_INT(port) (to_mvuart(port)->data->flags.ctrl_tx_rdy_int)
1255218d769SMiquel Raynal #define CTRL_RX_RDY_INT(port) (to_mvuart(port)->data->flags.ctrl_rx_rdy_int)
1265218d769SMiquel Raynal #define STAT_TX_RDY(port) (to_mvuart(port)->data->flags.stat_tx_rdy)
1275218d769SMiquel Raynal #define STAT_RX_RDY(port) (to_mvuart(port)->data->flags.stat_rx_rdy)
1285218d769SMiquel Raynal 
1295218d769SMiquel Raynal static struct uart_port mvebu_uart_ports[MVEBU_NR_UARTS];
1305218d769SMiquel Raynal 
13130530791SWilson Ding /* Core UART Driver Operations */
13230530791SWilson Ding static unsigned int mvebu_uart_tx_empty(struct uart_port *port)
13330530791SWilson Ding {
13430530791SWilson Ding 	unsigned long flags;
13530530791SWilson Ding 	unsigned int st;
13630530791SWilson Ding 
13730530791SWilson Ding 	spin_lock_irqsave(&port->lock, flags);
13830530791SWilson Ding 	st = readl(port->membase + UART_STAT);
13930530791SWilson Ding 	spin_unlock_irqrestore(&port->lock, flags);
14030530791SWilson Ding 
14130530791SWilson Ding 	return (st & STAT_TX_FIFO_EMP) ? TIOCSER_TEMT : 0;
14230530791SWilson Ding }
14330530791SWilson Ding 
14430530791SWilson Ding static unsigned int mvebu_uart_get_mctrl(struct uart_port *port)
14530530791SWilson Ding {
14630530791SWilson Ding 	return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
14730530791SWilson Ding }
14830530791SWilson Ding 
14930530791SWilson Ding static void mvebu_uart_set_mctrl(struct uart_port *port,
15030530791SWilson Ding 				 unsigned int mctrl)
15130530791SWilson Ding {
15230530791SWilson Ding /*
15330530791SWilson Ding  * Even if we do not support configuring the modem control lines, this
15430530791SWilson Ding  * function must be proided to the serial core
15530530791SWilson Ding  */
15630530791SWilson Ding }
15730530791SWilson Ding 
15830530791SWilson Ding static void mvebu_uart_stop_tx(struct uart_port *port)
15930530791SWilson Ding {
1605218d769SMiquel Raynal 	unsigned int ctl = readl(port->membase + UART_INTR(port));
16130530791SWilson Ding 
1625218d769SMiquel Raynal 	ctl &= ~CTRL_TX_RDY_INT(port);
1635218d769SMiquel Raynal 	writel(ctl, port->membase + UART_INTR(port));
16430530791SWilson Ding }
16530530791SWilson Ding 
16630530791SWilson Ding static void mvebu_uart_start_tx(struct uart_port *port)
16730530791SWilson Ding {
16830434b07SAllen Yan 	unsigned int ctl;
16930434b07SAllen Yan 	struct circ_buf *xmit = &port->state->xmit;
17030530791SWilson Ding 
17130434b07SAllen Yan 	if (IS_EXTENDED(port) && !uart_circ_empty(xmit)) {
17230434b07SAllen Yan 		writel(xmit->buf[xmit->tail], port->membase + UART_TSH(port));
17330434b07SAllen Yan 		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
17430434b07SAllen Yan 		port->icount.tx++;
17530434b07SAllen Yan 	}
17630434b07SAllen Yan 
17730434b07SAllen Yan 	ctl = readl(port->membase + UART_INTR(port));
1785218d769SMiquel Raynal 	ctl |= CTRL_TX_RDY_INT(port);
1795218d769SMiquel Raynal 	writel(ctl, port->membase + UART_INTR(port));
18030530791SWilson Ding }
18130530791SWilson Ding 
18230530791SWilson Ding static void mvebu_uart_stop_rx(struct uart_port *port)
18330530791SWilson Ding {
1845218d769SMiquel Raynal 	unsigned int ctl;
18530530791SWilson Ding 
1865218d769SMiquel Raynal 	ctl = readl(port->membase + UART_CTRL(port));
1875218d769SMiquel Raynal 	ctl &= ~CTRL_BRK_INT;
1885218d769SMiquel Raynal 	writel(ctl, port->membase + UART_CTRL(port));
1895218d769SMiquel Raynal 
1905218d769SMiquel Raynal 	ctl = readl(port->membase + UART_INTR(port));
1915218d769SMiquel Raynal 	ctl &= ~CTRL_RX_RDY_INT(port);
1925218d769SMiquel Raynal 	writel(ctl, port->membase + UART_INTR(port));
19330530791SWilson Ding }
19430530791SWilson Ding 
19530530791SWilson Ding static void mvebu_uart_break_ctl(struct uart_port *port, int brk)
19630530791SWilson Ding {
19730530791SWilson Ding 	unsigned int ctl;
19830530791SWilson Ding 	unsigned long flags;
19930530791SWilson Ding 
20030530791SWilson Ding 	spin_lock_irqsave(&port->lock, flags);
2015218d769SMiquel Raynal 	ctl = readl(port->membase + UART_CTRL(port));
20230530791SWilson Ding 	if (brk == -1)
20330530791SWilson Ding 		ctl |= CTRL_SND_BRK_SEQ;
20430530791SWilson Ding 	else
20530530791SWilson Ding 		ctl &= ~CTRL_SND_BRK_SEQ;
2065218d769SMiquel Raynal 	writel(ctl, port->membase + UART_CTRL(port));
20730530791SWilson Ding 	spin_unlock_irqrestore(&port->lock, flags);
20830530791SWilson Ding }
20930530791SWilson Ding 
21030530791SWilson Ding static void mvebu_uart_rx_chars(struct uart_port *port, unsigned int status)
21130530791SWilson Ding {
21230530791SWilson Ding 	struct tty_port *tport = &port->state->port;
21330530791SWilson Ding 	unsigned char ch = 0;
21430530791SWilson Ding 	char flag = 0;
21530530791SWilson Ding 
21630530791SWilson Ding 	do {
2175218d769SMiquel Raynal 		if (status & STAT_RX_RDY(port)) {
2185218d769SMiquel Raynal 			ch = readl(port->membase + UART_RBR(port));
21930530791SWilson Ding 			ch &= 0xff;
22030530791SWilson Ding 			flag = TTY_NORMAL;
22130530791SWilson Ding 			port->icount.rx++;
22230530791SWilson Ding 
22330530791SWilson Ding 			if (status & STAT_PAR_ERR)
22430530791SWilson Ding 				port->icount.parity++;
22530530791SWilson Ding 		}
22630530791SWilson Ding 
22730530791SWilson Ding 		if (status & STAT_BRK_DET) {
22830530791SWilson Ding 			port->icount.brk++;
22930530791SWilson Ding 			status &= ~(STAT_FRM_ERR | STAT_PAR_ERR);
23030530791SWilson Ding 			if (uart_handle_break(port))
23130530791SWilson Ding 				goto ignore_char;
23230530791SWilson Ding 		}
23330530791SWilson Ding 
23430530791SWilson Ding 		if (status & STAT_OVR_ERR)
23530530791SWilson Ding 			port->icount.overrun++;
23630530791SWilson Ding 
23730530791SWilson Ding 		if (status & STAT_FRM_ERR)
23830530791SWilson Ding 			port->icount.frame++;
23930530791SWilson Ding 
24030530791SWilson Ding 		if (uart_handle_sysrq_char(port, ch))
24130530791SWilson Ding 			goto ignore_char;
24230530791SWilson Ding 
24330530791SWilson Ding 		if (status & port->ignore_status_mask & STAT_PAR_ERR)
2445218d769SMiquel Raynal 			status &= ~STAT_RX_RDY(port);
24530530791SWilson Ding 
24630530791SWilson Ding 		status &= port->read_status_mask;
24730530791SWilson Ding 
24830530791SWilson Ding 		if (status & STAT_PAR_ERR)
24930530791SWilson Ding 			flag = TTY_PARITY;
25030530791SWilson Ding 
25130530791SWilson Ding 		status &= ~port->ignore_status_mask;
25230530791SWilson Ding 
2535218d769SMiquel Raynal 		if (status & STAT_RX_RDY(port))
25430530791SWilson Ding 			tty_insert_flip_char(tport, ch, flag);
25530530791SWilson Ding 
25630530791SWilson Ding 		if (status & STAT_BRK_DET)
25730530791SWilson Ding 			tty_insert_flip_char(tport, 0, TTY_BREAK);
25830530791SWilson Ding 
25930530791SWilson Ding 		if (status & STAT_FRM_ERR)
26030530791SWilson Ding 			tty_insert_flip_char(tport, 0, TTY_FRAME);
26130530791SWilson Ding 
26230530791SWilson Ding 		if (status & STAT_OVR_ERR)
26330530791SWilson Ding 			tty_insert_flip_char(tport, 0, TTY_OVERRUN);
26430530791SWilson Ding 
26530530791SWilson Ding ignore_char:
26630530791SWilson Ding 		status = readl(port->membase + UART_STAT);
2675218d769SMiquel Raynal 	} while (status & (STAT_RX_RDY(port) | STAT_BRK_DET));
26830530791SWilson Ding 
26930530791SWilson Ding 	tty_flip_buffer_push(tport);
27030530791SWilson Ding }
27130530791SWilson Ding 
27230530791SWilson Ding static void mvebu_uart_tx_chars(struct uart_port *port, unsigned int status)
27330530791SWilson Ding {
27430530791SWilson Ding 	struct circ_buf *xmit = &port->state->xmit;
27530530791SWilson Ding 	unsigned int count;
27630530791SWilson Ding 	unsigned int st;
27730530791SWilson Ding 
27830530791SWilson Ding 	if (port->x_char) {
2795218d769SMiquel Raynal 		writel(port->x_char, port->membase + UART_TSH(port));
28030530791SWilson Ding 		port->icount.tx++;
28130530791SWilson Ding 		port->x_char = 0;
28230530791SWilson Ding 		return;
28330530791SWilson Ding 	}
28430530791SWilson Ding 
28530530791SWilson Ding 	if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
28630530791SWilson Ding 		mvebu_uart_stop_tx(port);
28730530791SWilson Ding 		return;
28830530791SWilson Ding 	}
28930530791SWilson Ding 
29030530791SWilson Ding 	for (count = 0; count < port->fifosize; count++) {
2915218d769SMiquel Raynal 		writel(xmit->buf[xmit->tail], port->membase + UART_TSH(port));
29230530791SWilson Ding 		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
29330530791SWilson Ding 		port->icount.tx++;
29430530791SWilson Ding 
29530530791SWilson Ding 		if (uart_circ_empty(xmit))
29630530791SWilson Ding 			break;
29730530791SWilson Ding 
29830530791SWilson Ding 		st = readl(port->membase + UART_STAT);
29930530791SWilson Ding 		if (st & STAT_TX_FIFO_FUL)
30030530791SWilson Ding 			break;
30130530791SWilson Ding 	}
30230530791SWilson Ding 
30330530791SWilson Ding 	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
30430530791SWilson Ding 		uart_write_wakeup(port);
30530530791SWilson Ding 
30630530791SWilson Ding 	if (uart_circ_empty(xmit))
30730530791SWilson Ding 		mvebu_uart_stop_tx(port);
30830530791SWilson Ding }
30930530791SWilson Ding 
31030530791SWilson Ding static irqreturn_t mvebu_uart_isr(int irq, void *dev_id)
31130530791SWilson Ding {
31230530791SWilson Ding 	struct uart_port *port = (struct uart_port *)dev_id;
31330530791SWilson Ding 	unsigned int st = readl(port->membase + UART_STAT);
31430530791SWilson Ding 
3155218d769SMiquel Raynal 	if (st & (STAT_RX_RDY(port) | STAT_OVR_ERR | STAT_FRM_ERR |
3165218d769SMiquel Raynal 			STAT_BRK_DET))
31730530791SWilson Ding 		mvebu_uart_rx_chars(port, st);
31830530791SWilson Ding 
3195218d769SMiquel Raynal 	if (st & STAT_TX_RDY(port))
32030530791SWilson Ding 		mvebu_uart_tx_chars(port, st);
32130530791SWilson Ding 
32230530791SWilson Ding 	return IRQ_HANDLED;
32330530791SWilson Ding }
32430530791SWilson Ding 
32530530791SWilson Ding static int mvebu_uart_startup(struct uart_port *port)
32630530791SWilson Ding {
3275218d769SMiquel Raynal 	unsigned int ctl;
32830530791SWilson Ding 	int ret;
32930530791SWilson Ding 
33030530791SWilson Ding 	writel(CTRL_TXFIFO_RST | CTRL_RXFIFO_RST,
3315218d769SMiquel Raynal 	       port->membase + UART_CTRL(port));
33230530791SWilson Ding 	udelay(1);
3332ff23c48SAllen Yan 
3342ff23c48SAllen Yan 	/* Clear the error bits of state register before IRQ request */
3352ff23c48SAllen Yan 	ret = readl(port->membase + UART_STAT);
3362ff23c48SAllen Yan 	ret |= STAT_BRK_ERR;
3372ff23c48SAllen Yan 	writel(ret, port->membase + UART_STAT);
3382ff23c48SAllen Yan 
3395218d769SMiquel Raynal 	writel(CTRL_BRK_INT, port->membase + UART_CTRL(port));
3405218d769SMiquel Raynal 
3415218d769SMiquel Raynal 	ctl = readl(port->membase + UART_INTR(port));
3425218d769SMiquel Raynal 	ctl |= CTRL_RX_RDY_INT(port);
3435218d769SMiquel Raynal 	writel(ctl, port->membase + UART_INTR(port));
34430530791SWilson Ding 
34502c33330SYehuda Yitschak 	ret = request_irq(port->irq, mvebu_uart_isr, port->irqflags,
34602c33330SYehuda Yitschak 			  DRIVER_NAME, port);
34730530791SWilson Ding 	if (ret) {
34830530791SWilson Ding 		dev_err(port->dev, "failed to request irq\n");
34930530791SWilson Ding 		return ret;
35030530791SWilson Ding 	}
35130530791SWilson Ding 
35230530791SWilson Ding 	return 0;
35330530791SWilson Ding }
35430530791SWilson Ding 
35530530791SWilson Ding static void mvebu_uart_shutdown(struct uart_port *port)
35630530791SWilson Ding {
3575218d769SMiquel Raynal 	writel(0, port->membase + UART_INTR(port));
358c2c1659bSThomas Petazzoni 
359c2c1659bSThomas Petazzoni 	free_irq(port->irq, port);
36030530791SWilson Ding }
36130530791SWilson Ding 
36268a0db1dSAllen Yan static int mvebu_uart_baud_rate_set(struct uart_port *port, unsigned int baud)
36368a0db1dSAllen Yan {
36468a0db1dSAllen Yan 	struct mvebu_uart *mvuart = to_mvuart(port);
36568a0db1dSAllen Yan 	unsigned int baud_rate_div;
36668a0db1dSAllen Yan 	u32 brdv;
36768a0db1dSAllen Yan 
36868a0db1dSAllen Yan 	if (IS_ERR(mvuart->clk))
36968a0db1dSAllen Yan 		return -PTR_ERR(mvuart->clk);
37068a0db1dSAllen Yan 
37168a0db1dSAllen Yan 	/*
37268a0db1dSAllen Yan 	 * The UART clock is divided by the value of the divisor to generate
37368a0db1dSAllen Yan 	 * UCLK_OUT clock, which is 16 times faster than the baudrate.
37468a0db1dSAllen Yan 	 * This prescaler can achieve all standard baudrates until 230400.
37568a0db1dSAllen Yan 	 * Higher baudrates could be achieved for the extended UART by using the
37668a0db1dSAllen Yan 	 * programmable oversampling stack (also called fractional divisor).
37768a0db1dSAllen Yan 	 */
37868a0db1dSAllen Yan 	baud_rate_div = DIV_ROUND_UP(port->uartclk, baud * 16);
37968a0db1dSAllen Yan 	brdv = readl(port->membase + UART_BRDV);
38068a0db1dSAllen Yan 	brdv &= ~BRDV_BAUD_MASK;
38168a0db1dSAllen Yan 	brdv |= baud_rate_div;
38268a0db1dSAllen Yan 	writel(brdv, port->membase + UART_BRDV);
38368a0db1dSAllen Yan 
38468a0db1dSAllen Yan 	return 0;
38568a0db1dSAllen Yan }
38668a0db1dSAllen Yan 
38730530791SWilson Ding static void mvebu_uart_set_termios(struct uart_port *port,
38830530791SWilson Ding 				   struct ktermios *termios,
38930530791SWilson Ding 				   struct ktermios *old)
39030530791SWilson Ding {
39130530791SWilson Ding 	unsigned long flags;
39230530791SWilson Ding 	unsigned int baud;
39330530791SWilson Ding 
39430530791SWilson Ding 	spin_lock_irqsave(&port->lock, flags);
39530530791SWilson Ding 
3965218d769SMiquel Raynal 	port->read_status_mask = STAT_RX_RDY(port) | STAT_OVR_ERR |
3975218d769SMiquel Raynal 		STAT_TX_RDY(port) | STAT_TX_FIFO_FUL;
39830530791SWilson Ding 
39930530791SWilson Ding 	if (termios->c_iflag & INPCK)
40030530791SWilson Ding 		port->read_status_mask |= STAT_FRM_ERR | STAT_PAR_ERR;
40130530791SWilson Ding 
40230530791SWilson Ding 	port->ignore_status_mask = 0;
40330530791SWilson Ding 	if (termios->c_iflag & IGNPAR)
40430530791SWilson Ding 		port->ignore_status_mask |=
40530530791SWilson Ding 			STAT_FRM_ERR | STAT_PAR_ERR | STAT_OVR_ERR;
40630530791SWilson Ding 
40730530791SWilson Ding 	if ((termios->c_cflag & CREAD) == 0)
4085218d769SMiquel Raynal 		port->ignore_status_mask |= STAT_RX_RDY(port) | STAT_BRK_ERR;
40930530791SWilson Ding 
41068a0db1dSAllen Yan 	/*
41168a0db1dSAllen Yan 	 * Maximum achievable frequency with simple baudrate divisor is 230400.
41268a0db1dSAllen Yan 	 * Since the error per bit frame would be of more than 15%, achieving
41368a0db1dSAllen Yan 	 * higher frequencies would require to implement the fractional divisor
41468a0db1dSAllen Yan 	 * feature.
41568a0db1dSAllen Yan 	 */
41668a0db1dSAllen Yan 	baud = uart_get_baud_rate(port, termios, old, 0, 230400);
41768a0db1dSAllen Yan 	if (mvebu_uart_baud_rate_set(port, baud)) {
41868a0db1dSAllen Yan 		/* No clock available, baudrate cannot be changed */
41930530791SWilson Ding 		if (old)
42068a0db1dSAllen Yan 			baud = uart_get_baud_rate(port, old, NULL, 0, 230400);
42168a0db1dSAllen Yan 	} else {
42268a0db1dSAllen Yan 		tty_termios_encode_baud_rate(termios, baud, baud);
42330530791SWilson Ding 		uart_update_timeout(port, termios->c_cflag, baud);
42468a0db1dSAllen Yan 	}
42568a0db1dSAllen Yan 
42668a0db1dSAllen Yan 	/* Only the following flag changes are supported */
42768a0db1dSAllen Yan 	if (old) {
42868a0db1dSAllen Yan 		termios->c_iflag &= INPCK | IGNPAR;
42968a0db1dSAllen Yan 		termios->c_iflag |= old->c_iflag & ~(INPCK | IGNPAR);
43068a0db1dSAllen Yan 		termios->c_cflag &= CREAD | CBAUD;
43168a0db1dSAllen Yan 		termios->c_cflag |= old->c_cflag & ~(CREAD | CBAUD);
43268a0db1dSAllen Yan 		termios->c_lflag = old->c_lflag;
43368a0db1dSAllen Yan 	}
43430530791SWilson Ding 
43530530791SWilson Ding 	spin_unlock_irqrestore(&port->lock, flags);
43630530791SWilson Ding }
43730530791SWilson Ding 
43830530791SWilson Ding static const char *mvebu_uart_type(struct uart_port *port)
43930530791SWilson Ding {
44030530791SWilson Ding 	return MVEBU_UART_TYPE;
44130530791SWilson Ding }
44230530791SWilson Ding 
44330530791SWilson Ding static void mvebu_uart_release_port(struct uart_port *port)
44430530791SWilson Ding {
44530530791SWilson Ding 	/* Nothing to do here */
44630530791SWilson Ding }
44730530791SWilson Ding 
44830530791SWilson Ding static int mvebu_uart_request_port(struct uart_port *port)
44930530791SWilson Ding {
45030530791SWilson Ding 	return 0;
45130530791SWilson Ding }
45230530791SWilson Ding 
45330530791SWilson Ding #ifdef CONFIG_CONSOLE_POLL
45430530791SWilson Ding static int mvebu_uart_get_poll_char(struct uart_port *port)
45530530791SWilson Ding {
45630530791SWilson Ding 	unsigned int st = readl(port->membase + UART_STAT);
45730530791SWilson Ding 
4585218d769SMiquel Raynal 	if (!(st & STAT_RX_RDY(port)))
45930530791SWilson Ding 		return NO_POLL_CHAR;
46030530791SWilson Ding 
4615218d769SMiquel Raynal 	return readl(port->membase + UART_RBR(port));
46230530791SWilson Ding }
46330530791SWilson Ding 
46430530791SWilson Ding static void mvebu_uart_put_poll_char(struct uart_port *port, unsigned char c)
46530530791SWilson Ding {
46630530791SWilson Ding 	unsigned int st;
46730530791SWilson Ding 
46830530791SWilson Ding 	for (;;) {
46930530791SWilson Ding 		st = readl(port->membase + UART_STAT);
47030530791SWilson Ding 
47130530791SWilson Ding 		if (!(st & STAT_TX_FIFO_FUL))
47230530791SWilson Ding 			break;
47330530791SWilson Ding 
47430530791SWilson Ding 		udelay(1);
47530530791SWilson Ding 	}
47630530791SWilson Ding 
4775218d769SMiquel Raynal 	writel(c, port->membase + UART_TSH(port));
47830530791SWilson Ding }
47930530791SWilson Ding #endif
48030530791SWilson Ding 
48130530791SWilson Ding static const struct uart_ops mvebu_uart_ops = {
48230530791SWilson Ding 	.tx_empty	= mvebu_uart_tx_empty,
48330530791SWilson Ding 	.set_mctrl	= mvebu_uart_set_mctrl,
48430530791SWilson Ding 	.get_mctrl	= mvebu_uart_get_mctrl,
48530530791SWilson Ding 	.stop_tx	= mvebu_uart_stop_tx,
48630530791SWilson Ding 	.start_tx	= mvebu_uart_start_tx,
48730530791SWilson Ding 	.stop_rx	= mvebu_uart_stop_rx,
48830530791SWilson Ding 	.break_ctl	= mvebu_uart_break_ctl,
48930530791SWilson Ding 	.startup	= mvebu_uart_startup,
49030530791SWilson Ding 	.shutdown	= mvebu_uart_shutdown,
49130530791SWilson Ding 	.set_termios	= mvebu_uart_set_termios,
49230530791SWilson Ding 	.type		= mvebu_uart_type,
49330530791SWilson Ding 	.release_port	= mvebu_uart_release_port,
49430530791SWilson Ding 	.request_port	= mvebu_uart_request_port,
49530530791SWilson Ding #ifdef CONFIG_CONSOLE_POLL
49630530791SWilson Ding 	.poll_get_char	= mvebu_uart_get_poll_char,
49730530791SWilson Ding 	.poll_put_char	= mvebu_uart_put_poll_char,
49830530791SWilson Ding #endif
49930530791SWilson Ding };
50030530791SWilson Ding 
50130530791SWilson Ding /* Console Driver Operations  */
50230530791SWilson Ding 
50330530791SWilson Ding #ifdef CONFIG_SERIAL_MVEBU_CONSOLE
50430530791SWilson Ding /* Early Console */
50530530791SWilson Ding static void mvebu_uart_putc(struct uart_port *port, int c)
50630530791SWilson Ding {
50730530791SWilson Ding 	unsigned int st;
50830530791SWilson Ding 
50930530791SWilson Ding 	for (;;) {
51030530791SWilson Ding 		st = readl(port->membase + UART_STAT);
51130530791SWilson Ding 		if (!(st & STAT_TX_FIFO_FUL))
51230530791SWilson Ding 			break;
51330530791SWilson Ding 	}
51430530791SWilson Ding 
5155218d769SMiquel Raynal 	/* At early stage, DT is not parsed yet, only use UART0 */
5165218d769SMiquel Raynal 	writel(c, port->membase + UART_STD_TSH);
51730530791SWilson Ding 
51830530791SWilson Ding 	for (;;) {
51930530791SWilson Ding 		st = readl(port->membase + UART_STAT);
52030530791SWilson Ding 		if (st & STAT_TX_FIFO_EMP)
52130530791SWilson Ding 			break;
52230530791SWilson Ding 	}
52330530791SWilson Ding }
52430530791SWilson Ding 
52530530791SWilson Ding static void mvebu_uart_putc_early_write(struct console *con,
52630530791SWilson Ding 					const char *s,
52730530791SWilson Ding 					unsigned n)
52830530791SWilson Ding {
52930530791SWilson Ding 	struct earlycon_device *dev = con->data;
53030530791SWilson Ding 
53130530791SWilson Ding 	uart_console_write(&dev->port, s, n, mvebu_uart_putc);
53230530791SWilson Ding }
53330530791SWilson Ding 
53430530791SWilson Ding static int __init
53530530791SWilson Ding mvebu_uart_early_console_setup(struct earlycon_device *device,
53630530791SWilson Ding 			       const char *opt)
53730530791SWilson Ding {
53830530791SWilson Ding 	if (!device->port.membase)
53930530791SWilson Ding 		return -ENODEV;
54030530791SWilson Ding 
54130530791SWilson Ding 	device->con->write = mvebu_uart_putc_early_write;
54230530791SWilson Ding 
54330530791SWilson Ding 	return 0;
54430530791SWilson Ding }
54530530791SWilson Ding 
54630530791SWilson Ding EARLYCON_DECLARE(ar3700_uart, mvebu_uart_early_console_setup);
54730530791SWilson Ding OF_EARLYCON_DECLARE(ar3700_uart, "marvell,armada-3700-uart",
54830530791SWilson Ding 		    mvebu_uart_early_console_setup);
54930530791SWilson Ding 
55030530791SWilson Ding static void wait_for_xmitr(struct uart_port *port)
55130530791SWilson Ding {
55230530791SWilson Ding 	u32 val;
55330530791SWilson Ding 
55430530791SWilson Ding 	readl_poll_timeout_atomic(port->membase + UART_STAT, val,
55530530791SWilson Ding 				  (val & STAT_TX_EMP), 1, 10000);
55630530791SWilson Ding }
55730530791SWilson Ding 
55830530791SWilson Ding static void mvebu_uart_console_putchar(struct uart_port *port, int ch)
55930530791SWilson Ding {
56030530791SWilson Ding 	wait_for_xmitr(port);
5615218d769SMiquel Raynal 	writel(ch, port->membase + UART_TSH(port));
56230530791SWilson Ding }
56330530791SWilson Ding 
56430530791SWilson Ding static void mvebu_uart_console_write(struct console *co, const char *s,
56530530791SWilson Ding 				     unsigned int count)
56630530791SWilson Ding {
56730530791SWilson Ding 	struct uart_port *port = &mvebu_uart_ports[co->index];
56830530791SWilson Ding 	unsigned long flags;
5695218d769SMiquel Raynal 	unsigned int ier, intr, ctl;
57030530791SWilson Ding 	int locked = 1;
57130530791SWilson Ding 
57230530791SWilson Ding 	if (oops_in_progress)
57330530791SWilson Ding 		locked = spin_trylock_irqsave(&port->lock, flags);
57430530791SWilson Ding 	else
57530530791SWilson Ding 		spin_lock_irqsave(&port->lock, flags);
57630530791SWilson Ding 
5775218d769SMiquel Raynal 	ier = readl(port->membase + UART_CTRL(port)) & CTRL_BRK_INT;
5785218d769SMiquel Raynal 	intr = readl(port->membase + UART_INTR(port)) &
5795218d769SMiquel Raynal 		(CTRL_RX_RDY_INT(port) | CTRL_TX_RDY_INT(port));
5805218d769SMiquel Raynal 	writel(0, port->membase + UART_CTRL(port));
5815218d769SMiquel Raynal 	writel(0, port->membase + UART_INTR(port));
58230530791SWilson Ding 
58330530791SWilson Ding 	uart_console_write(port, s, count, mvebu_uart_console_putchar);
58430530791SWilson Ding 
58530530791SWilson Ding 	wait_for_xmitr(port);
58630530791SWilson Ding 
58730530791SWilson Ding 	if (ier)
5885218d769SMiquel Raynal 		writel(ier, port->membase + UART_CTRL(port));
5895218d769SMiquel Raynal 
5905218d769SMiquel Raynal 	if (intr) {
5915218d769SMiquel Raynal 		ctl = intr | readl(port->membase + UART_INTR(port));
5925218d769SMiquel Raynal 		writel(ctl, port->membase + UART_INTR(port));
5935218d769SMiquel Raynal 	}
59430530791SWilson Ding 
59530530791SWilson Ding 	if (locked)
59630530791SWilson Ding 		spin_unlock_irqrestore(&port->lock, flags);
59730530791SWilson Ding }
59830530791SWilson Ding 
59930530791SWilson Ding static int mvebu_uart_console_setup(struct console *co, char *options)
60030530791SWilson Ding {
60130530791SWilson Ding 	struct uart_port *port;
60230530791SWilson Ding 	int baud = 9600;
60330530791SWilson Ding 	int bits = 8;
60430530791SWilson Ding 	int parity = 'n';
60530530791SWilson Ding 	int flow = 'n';
60630530791SWilson Ding 
60730530791SWilson Ding 	if (co->index < 0 || co->index >= MVEBU_NR_UARTS)
60830530791SWilson Ding 		return -EINVAL;
60930530791SWilson Ding 
61030530791SWilson Ding 	port = &mvebu_uart_ports[co->index];
61130530791SWilson Ding 
61230530791SWilson Ding 	if (!port->mapbase || !port->membase) {
61330530791SWilson Ding 		pr_debug("console on ttyMV%i not present\n", co->index);
61430530791SWilson Ding 		return -ENODEV;
61530530791SWilson Ding 	}
61630530791SWilson Ding 
61730530791SWilson Ding 	if (options)
61830530791SWilson Ding 		uart_parse_options(options, &baud, &parity, &bits, &flow);
61930530791SWilson Ding 
62030530791SWilson Ding 	return uart_set_options(port, co, baud, parity, bits, flow);
62130530791SWilson Ding }
62230530791SWilson Ding 
62330530791SWilson Ding static struct uart_driver mvebu_uart_driver;
62430530791SWilson Ding 
62530530791SWilson Ding static struct console mvebu_uart_console = {
62630530791SWilson Ding 	.name	= "ttyMV",
62730530791SWilson Ding 	.write	= mvebu_uart_console_write,
62830530791SWilson Ding 	.device	= uart_console_device,
62930530791SWilson Ding 	.setup	= mvebu_uart_console_setup,
63030530791SWilson Ding 	.flags	= CON_PRINTBUFFER,
63130530791SWilson Ding 	.index	= -1,
63230530791SWilson Ding 	.data	= &mvebu_uart_driver,
63330530791SWilson Ding };
63430530791SWilson Ding 
63530530791SWilson Ding static int __init mvebu_uart_console_init(void)
63630530791SWilson Ding {
63730530791SWilson Ding 	register_console(&mvebu_uart_console);
63830530791SWilson Ding 	return 0;
63930530791SWilson Ding }
64030530791SWilson Ding 
64130530791SWilson Ding console_initcall(mvebu_uart_console_init);
64230530791SWilson Ding 
64330530791SWilson Ding 
64430530791SWilson Ding #endif /* CONFIG_SERIAL_MVEBU_CONSOLE */
64530530791SWilson Ding 
64630530791SWilson Ding static struct uart_driver mvebu_uart_driver = {
64730530791SWilson Ding 	.owner			= THIS_MODULE,
64802c33330SYehuda Yitschak 	.driver_name		= DRIVER_NAME,
64930530791SWilson Ding 	.dev_name		= "ttyMV",
65030530791SWilson Ding 	.nr			= MVEBU_NR_UARTS,
65130530791SWilson Ding #ifdef CONFIG_SERIAL_MVEBU_CONSOLE
65230530791SWilson Ding 	.cons			= &mvebu_uart_console,
65330530791SWilson Ding #endif
65430530791SWilson Ding };
65530530791SWilson Ding 
6565218d769SMiquel Raynal static const struct of_device_id mvebu_uart_of_match[];
6575218d769SMiquel Raynal 
65894228f95SAllen Yan /* Counter to keep track of each UART port id when not using CONFIG_OF */
65994228f95SAllen Yan static int uart_num_counter;
66094228f95SAllen Yan 
66130530791SWilson Ding static int mvebu_uart_probe(struct platform_device *pdev)
66230530791SWilson Ding {
66330530791SWilson Ding 	struct resource *reg = platform_get_resource(pdev, IORESOURCE_MEM, 0);
66430530791SWilson Ding 	struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
6655218d769SMiquel Raynal 	const struct of_device_id *match = of_match_device(mvebu_uart_of_match,
6665218d769SMiquel Raynal 							   &pdev->dev);
66730530791SWilson Ding 	struct uart_port *port;
6685218d769SMiquel Raynal 	struct mvebu_uart *mvuart;
66994228f95SAllen Yan 	int ret, id;
67030530791SWilson Ding 
67130530791SWilson Ding 	if (!reg || !irq) {
67230530791SWilson Ding 		dev_err(&pdev->dev, "no registers/irq defined\n");
67330530791SWilson Ding 		return -EINVAL;
67430530791SWilson Ding 	}
67530530791SWilson Ding 
67694228f95SAllen Yan 	/* Assume that all UART ports have a DT alias or none has */
67794228f95SAllen Yan 	id = of_alias_get_id(pdev->dev.of_node, "serial");
67894228f95SAllen Yan 	if (!pdev->dev.of_node || id < 0)
67994228f95SAllen Yan 		pdev->id = uart_num_counter++;
68094228f95SAllen Yan 	else
68194228f95SAllen Yan 		pdev->id = id;
68294228f95SAllen Yan 
68394228f95SAllen Yan 	if (pdev->id >= MVEBU_NR_UARTS) {
68494228f95SAllen Yan 		dev_err(&pdev->dev, "cannot have more than %d UART ports\n",
68594228f95SAllen Yan 			MVEBU_NR_UARTS);
68694228f95SAllen Yan 		return -EINVAL;
68794228f95SAllen Yan 	}
68894228f95SAllen Yan 
68994228f95SAllen Yan 	port = &mvebu_uart_ports[pdev->id];
69030530791SWilson Ding 
69130530791SWilson Ding 	spin_lock_init(&port->lock);
69230530791SWilson Ding 
69330530791SWilson Ding 	port->dev        = &pdev->dev;
69430530791SWilson Ding 	port->type       = PORT_MVEBU;
69530530791SWilson Ding 	port->ops        = &mvebu_uart_ops;
69630530791SWilson Ding 	port->regshift   = 0;
69730530791SWilson Ding 
69830530791SWilson Ding 	port->fifosize   = 32;
69930530791SWilson Ding 	port->iotype     = UPIO_MEM32;
70030530791SWilson Ding 	port->flags      = UPF_FIXED_PORT;
70194228f95SAllen Yan 	port->line       = pdev->id;
70230530791SWilson Ding 
70330530791SWilson Ding 	port->irq        = irq->start;
70430530791SWilson Ding 	port->irqflags   = 0;
70530530791SWilson Ding 	port->mapbase    = reg->start;
70630530791SWilson Ding 
70730530791SWilson Ding 	port->membase = devm_ioremap_resource(&pdev->dev, reg);
70830530791SWilson Ding 	if (IS_ERR(port->membase))
70930530791SWilson Ding 		return -PTR_ERR(port->membase);
71030530791SWilson Ding 
7115218d769SMiquel Raynal 	mvuart = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_uart),
71230530791SWilson Ding 			      GFP_KERNEL);
7135218d769SMiquel Raynal 	if (!mvuart)
71430530791SWilson Ding 		return -ENOMEM;
71530530791SWilson Ding 
71668a0db1dSAllen Yan 	/* Get controller data depending on the compatible string */
7175218d769SMiquel Raynal 	mvuart->data = (struct mvebu_uart_driver_data *)match->data;
7185218d769SMiquel Raynal 	mvuart->port = port;
71930530791SWilson Ding 
7205218d769SMiquel Raynal 	port->private_data = mvuart;
7215218d769SMiquel Raynal 	platform_set_drvdata(pdev, mvuart);
72230530791SWilson Ding 
72368a0db1dSAllen Yan 	/* Get fixed clock frequency */
72468a0db1dSAllen Yan 	mvuart->clk = devm_clk_get(&pdev->dev, NULL);
72568a0db1dSAllen Yan 	if (IS_ERR(mvuart->clk)) {
72668a0db1dSAllen Yan 		if (PTR_ERR(mvuart->clk) == -EPROBE_DEFER)
72768a0db1dSAllen Yan 			return PTR_ERR(mvuart->clk);
72868a0db1dSAllen Yan 
72968a0db1dSAllen Yan 		if (IS_EXTENDED(port)) {
73068a0db1dSAllen Yan 			dev_err(&pdev->dev, "unable to get UART clock\n");
73168a0db1dSAllen Yan 			return PTR_ERR(mvuart->clk);
73268a0db1dSAllen Yan 		}
73368a0db1dSAllen Yan 	} else {
73468a0db1dSAllen Yan 		if (!clk_prepare_enable(mvuart->clk))
73568a0db1dSAllen Yan 			port->uartclk = clk_get_rate(mvuart->clk);
73668a0db1dSAllen Yan 	}
73768a0db1dSAllen Yan 
7389c3d3ee1SAllen Yan 	/* UART Soft Reset*/
7399c3d3ee1SAllen Yan 	writel(CTRL_SOFT_RST, port->membase + UART_CTRL(port));
7409c3d3ee1SAllen Yan 	udelay(1);
7419c3d3ee1SAllen Yan 	writel(0, port->membase + UART_CTRL(port));
7429c3d3ee1SAllen Yan 
74330530791SWilson Ding 	ret = uart_add_one_port(&mvebu_uart_driver, port);
74430530791SWilson Ding 	if (ret)
74530530791SWilson Ding 		return ret;
74630530791SWilson Ding 	return 0;
74730530791SWilson Ding }
74830530791SWilson Ding 
7495218d769SMiquel Raynal static struct mvebu_uart_driver_data uart_std_driver_data = {
7505218d769SMiquel Raynal 	.is_ext = false,
7515218d769SMiquel Raynal 	.regs.rbr = UART_STD_RBR,
7525218d769SMiquel Raynal 	.regs.tsh = UART_STD_TSH,
7535218d769SMiquel Raynal 	.regs.ctrl = UART_STD_CTRL1,
7545218d769SMiquel Raynal 	.regs.intr = UART_STD_CTRL2,
7555218d769SMiquel Raynal 	.flags.ctrl_tx_rdy_int = CTRL_STD_TX_RDY_INT,
7565218d769SMiquel Raynal 	.flags.ctrl_rx_rdy_int = CTRL_STD_RX_RDY_INT,
7575218d769SMiquel Raynal 	.flags.stat_tx_rdy = STAT_STD_TX_RDY,
7585218d769SMiquel Raynal 	.flags.stat_rx_rdy = STAT_STD_RX_RDY,
7595218d769SMiquel Raynal };
7605218d769SMiquel Raynal 
76130530791SWilson Ding /* Match table for of_platform binding */
76230530791SWilson Ding static const struct of_device_id mvebu_uart_of_match[] = {
7635218d769SMiquel Raynal 	{
7645218d769SMiquel Raynal 		.compatible = "marvell,armada-3700-uart",
7655218d769SMiquel Raynal 		.data = (void *)&uart_std_driver_data,
7665218d769SMiquel Raynal 	},
76730530791SWilson Ding 	{}
76830530791SWilson Ding };
76930530791SWilson Ding 
77030530791SWilson Ding static struct platform_driver mvebu_uart_platform_driver = {
77130530791SWilson Ding 	.probe	= mvebu_uart_probe,
77230530791SWilson Ding 	.driver	= {
77330530791SWilson Ding 		.name  = "mvebu-uart",
77430530791SWilson Ding 		.of_match_table = of_match_ptr(mvebu_uart_of_match),
77589ebc274SPaul Gortmaker 		.suppress_bind_attrs = true,
77630530791SWilson Ding 	},
77730530791SWilson Ding };
77830530791SWilson Ding 
77930530791SWilson Ding static int __init mvebu_uart_init(void)
78030530791SWilson Ding {
78130530791SWilson Ding 	int ret;
78230530791SWilson Ding 
78330530791SWilson Ding 	ret = uart_register_driver(&mvebu_uart_driver);
78430530791SWilson Ding 	if (ret)
78530530791SWilson Ding 		return ret;
78630530791SWilson Ding 
78730530791SWilson Ding 	ret = platform_driver_register(&mvebu_uart_platform_driver);
78830530791SWilson Ding 	if (ret)
78930530791SWilson Ding 		uart_unregister_driver(&mvebu_uart_driver);
79030530791SWilson Ding 
79130530791SWilson Ding 	return ret;
79230530791SWilson Ding }
79330530791SWilson Ding arch_initcall(mvebu_uart_init);
794