xref: /openbmc/linux/drivers/tty/serial/mvebu-uart.c (revision 9078204c)
1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
230530791SWilson Ding /*
330530791SWilson Ding * ***************************************************************************
489ebc274SPaul Gortmaker * Marvell Armada-3700 Serial Driver
589ebc274SPaul Gortmaker * Author: Wilson Ding <dingwei@marvell.com>
630530791SWilson Ding * Copyright (C) 2015 Marvell International Ltd.
730530791SWilson Ding * ***************************************************************************
830530791SWilson Ding */
930530791SWilson Ding 
1030530791SWilson Ding #include <linux/clk.h>
1130530791SWilson Ding #include <linux/console.h>
1230530791SWilson Ding #include <linux/delay.h>
1330530791SWilson Ding #include <linux/device.h>
1430530791SWilson Ding #include <linux/init.h>
1530530791SWilson Ding #include <linux/io.h>
1630530791SWilson Ding #include <linux/iopoll.h>
1730530791SWilson Ding #include <linux/of.h>
1830530791SWilson Ding #include <linux/of_address.h>
1930530791SWilson Ding #include <linux/of_device.h>
2030530791SWilson Ding #include <linux/of_irq.h>
2130530791SWilson Ding #include <linux/of_platform.h>
2230530791SWilson Ding #include <linux/platform_device.h>
2330530791SWilson Ding #include <linux/serial.h>
2430530791SWilson Ding #include <linux/serial_core.h>
2530530791SWilson Ding #include <linux/slab.h>
2630530791SWilson Ding #include <linux/tty.h>
2730530791SWilson Ding #include <linux/tty_flip.h>
2830530791SWilson Ding 
2930530791SWilson Ding /* Register Map */
305218d769SMiquel Raynal #define UART_STD_RBR		0x00
3153501e02SMiquel Raynal #define UART_EXT_RBR		0x18
3230530791SWilson Ding 
335218d769SMiquel Raynal #define UART_STD_TSH		0x04
3453501e02SMiquel Raynal #define UART_EXT_TSH		0x1C
3530530791SWilson Ding 
365218d769SMiquel Raynal #define UART_STD_CTRL1		0x08
3753501e02SMiquel Raynal #define UART_EXT_CTRL1		0x04
3830530791SWilson Ding #define  CTRL_SOFT_RST		BIT(31)
3930530791SWilson Ding #define  CTRL_TXFIFO_RST	BIT(15)
4030530791SWilson Ding #define  CTRL_RXFIFO_RST	BIT(14)
4130530791SWilson Ding #define  CTRL_SND_BRK_SEQ	BIT(11)
4230530791SWilson Ding #define  CTRL_BRK_DET_INT	BIT(3)
4330530791SWilson Ding #define  CTRL_FRM_ERR_INT	BIT(2)
4430530791SWilson Ding #define  CTRL_PAR_ERR_INT	BIT(1)
4530530791SWilson Ding #define  CTRL_OVR_ERR_INT	BIT(0)
465218d769SMiquel Raynal #define  CTRL_BRK_INT		(CTRL_BRK_DET_INT | CTRL_FRM_ERR_INT | \
475218d769SMiquel Raynal 				CTRL_PAR_ERR_INT | CTRL_OVR_ERR_INT)
4830530791SWilson Ding 
495218d769SMiquel Raynal #define UART_STD_CTRL2		UART_STD_CTRL1
5053501e02SMiquel Raynal #define UART_EXT_CTRL2		0x20
515218d769SMiquel Raynal #define  CTRL_STD_TX_RDY_INT	BIT(5)
5253501e02SMiquel Raynal #define  CTRL_EXT_TX_RDY_INT	BIT(6)
535218d769SMiquel Raynal #define  CTRL_STD_RX_RDY_INT	BIT(4)
5453501e02SMiquel Raynal #define  CTRL_EXT_RX_RDY_INT	BIT(5)
555218d769SMiquel Raynal 
565218d769SMiquel Raynal #define UART_STAT		0x0C
5730530791SWilson Ding #define  STAT_TX_FIFO_EMP	BIT(13)
5830530791SWilson Ding #define  STAT_TX_FIFO_FUL	BIT(11)
5930530791SWilson Ding #define  STAT_TX_EMP		BIT(6)
605218d769SMiquel Raynal #define  STAT_STD_TX_RDY	BIT(5)
6153501e02SMiquel Raynal #define  STAT_EXT_TX_RDY	BIT(15)
625218d769SMiquel Raynal #define  STAT_STD_RX_RDY	BIT(4)
6353501e02SMiquel Raynal #define  STAT_EXT_RX_RDY	BIT(14)
6430530791SWilson Ding #define  STAT_BRK_DET		BIT(3)
6530530791SWilson Ding #define  STAT_FRM_ERR		BIT(2)
6630530791SWilson Ding #define  STAT_PAR_ERR		BIT(1)
6730530791SWilson Ding #define  STAT_OVR_ERR		BIT(0)
680ef5a6e0SColin Ian King #define  STAT_BRK_ERR		(STAT_BRK_DET | STAT_FRM_ERR \
6930530791SWilson Ding 				 | STAT_PAR_ERR | STAT_OVR_ERR)
7030530791SWilson Ding 
7130530791SWilson Ding #define UART_BRDV		0x10
7268a0db1dSAllen Yan #define  BRDV_BAUD_MASK         0x3FF
7330530791SWilson Ding 
74394e8351SMiquel Raynal #define UART_OSAMP		0x14
750e4cf69eSMiquel Raynal #define  OSAMP_DEFAULT_DIVISOR	16
7635d7a58aSMiquel Raynal #define  OSAMP_DIVISORS_MASK	0x3F3F3F3F
77394e8351SMiquel Raynal 
783a75e91bSMiquel Raynal #define MVEBU_NR_UARTS		2
7930530791SWilson Ding 
8030530791SWilson Ding #define MVEBU_UART_TYPE		"mvebu-uart"
8102c33330SYehuda Yitschak #define DRIVER_NAME		"mvebu_serial"
8230530791SWilson Ding 
8395f78768SMiquel Raynal enum {
8495f78768SMiquel Raynal 	/* Either there is only one summed IRQ... */
8595f78768SMiquel Raynal 	UART_IRQ_SUM = 0,
8695f78768SMiquel Raynal 	/* ...or there are two separate IRQ for RX and TX */
8795f78768SMiquel Raynal 	UART_RX_IRQ = 0,
8895f78768SMiquel Raynal 	UART_TX_IRQ,
8995f78768SMiquel Raynal 	UART_IRQ_COUNT
9095f78768SMiquel Raynal };
9195f78768SMiquel Raynal 
9295f78768SMiquel Raynal /* Diverging register offsets */
935218d769SMiquel Raynal struct uart_regs_layout {
945218d769SMiquel Raynal 	unsigned int rbr;
955218d769SMiquel Raynal 	unsigned int tsh;
965218d769SMiquel Raynal 	unsigned int ctrl;
975218d769SMiquel Raynal 	unsigned int intr;
985218d769SMiquel Raynal };
9930530791SWilson Ding 
1005218d769SMiquel Raynal /* Diverging flags */
1015218d769SMiquel Raynal struct uart_flags {
1025218d769SMiquel Raynal 	unsigned int ctrl_tx_rdy_int;
1035218d769SMiquel Raynal 	unsigned int ctrl_rx_rdy_int;
1045218d769SMiquel Raynal 	unsigned int stat_tx_rdy;
1055218d769SMiquel Raynal 	unsigned int stat_rx_rdy;
1065218d769SMiquel Raynal };
1075218d769SMiquel Raynal 
1085218d769SMiquel Raynal /* Driver data, a structure for each UART port */
1095218d769SMiquel Raynal struct mvebu_uart_driver_data {
1105218d769SMiquel Raynal 	bool is_ext;
1115218d769SMiquel Raynal 	struct uart_regs_layout regs;
1125218d769SMiquel Raynal 	struct uart_flags flags;
1135218d769SMiquel Raynal };
1145218d769SMiquel Raynal 
115394e8351SMiquel Raynal /* Saved registers during suspend */
116394e8351SMiquel Raynal struct mvebu_uart_pm_regs {
117394e8351SMiquel Raynal 	unsigned int rbr;
118394e8351SMiquel Raynal 	unsigned int tsh;
119394e8351SMiquel Raynal 	unsigned int ctrl;
120394e8351SMiquel Raynal 	unsigned int intr;
121394e8351SMiquel Raynal 	unsigned int stat;
122394e8351SMiquel Raynal 	unsigned int brdv;
123394e8351SMiquel Raynal 	unsigned int osamp;
124394e8351SMiquel Raynal };
125394e8351SMiquel Raynal 
1265218d769SMiquel Raynal /* MVEBU UART driver structure */
1275218d769SMiquel Raynal struct mvebu_uart {
12830530791SWilson Ding 	struct uart_port *port;
12930530791SWilson Ding 	struct clk *clk;
13095f78768SMiquel Raynal 	int irq[UART_IRQ_COUNT];
13195f78768SMiquel Raynal 	unsigned char __iomem *nb;
1325218d769SMiquel Raynal 	struct mvebu_uart_driver_data *data;
133394e8351SMiquel Raynal #if defined(CONFIG_PM)
134394e8351SMiquel Raynal 	struct mvebu_uart_pm_regs pm_regs;
135394e8351SMiquel Raynal #endif /* CONFIG_PM */
13630530791SWilson Ding };
13730530791SWilson Ding 
1385218d769SMiquel Raynal static struct mvebu_uart *to_mvuart(struct uart_port *port)
1395218d769SMiquel Raynal {
1405218d769SMiquel Raynal 	return (struct mvebu_uart *)port->private_data;
1415218d769SMiquel Raynal }
1425218d769SMiquel Raynal 
1435218d769SMiquel Raynal #define IS_EXTENDED(port) (to_mvuart(port)->data->is_ext)
1445218d769SMiquel Raynal 
1455218d769SMiquel Raynal #define UART_RBR(port) (to_mvuart(port)->data->regs.rbr)
1465218d769SMiquel Raynal #define UART_TSH(port) (to_mvuart(port)->data->regs.tsh)
1475218d769SMiquel Raynal #define UART_CTRL(port) (to_mvuart(port)->data->regs.ctrl)
1485218d769SMiquel Raynal #define UART_INTR(port) (to_mvuart(port)->data->regs.intr)
1495218d769SMiquel Raynal 
1505218d769SMiquel Raynal #define CTRL_TX_RDY_INT(port) (to_mvuart(port)->data->flags.ctrl_tx_rdy_int)
1515218d769SMiquel Raynal #define CTRL_RX_RDY_INT(port) (to_mvuart(port)->data->flags.ctrl_rx_rdy_int)
1525218d769SMiquel Raynal #define STAT_TX_RDY(port) (to_mvuart(port)->data->flags.stat_tx_rdy)
1535218d769SMiquel Raynal #define STAT_RX_RDY(port) (to_mvuart(port)->data->flags.stat_rx_rdy)
1545218d769SMiquel Raynal 
1555218d769SMiquel Raynal static struct uart_port mvebu_uart_ports[MVEBU_NR_UARTS];
1565218d769SMiquel Raynal 
15730530791SWilson Ding /* Core UART Driver Operations */
15830530791SWilson Ding static unsigned int mvebu_uart_tx_empty(struct uart_port *port)
15930530791SWilson Ding {
16030530791SWilson Ding 	unsigned long flags;
16130530791SWilson Ding 	unsigned int st;
16230530791SWilson Ding 
16330530791SWilson Ding 	spin_lock_irqsave(&port->lock, flags);
16430530791SWilson Ding 	st = readl(port->membase + UART_STAT);
16530530791SWilson Ding 	spin_unlock_irqrestore(&port->lock, flags);
16630530791SWilson Ding 
16730530791SWilson Ding 	return (st & STAT_TX_FIFO_EMP) ? TIOCSER_TEMT : 0;
16830530791SWilson Ding }
16930530791SWilson Ding 
17030530791SWilson Ding static unsigned int mvebu_uart_get_mctrl(struct uart_port *port)
17130530791SWilson Ding {
17230530791SWilson Ding 	return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
17330530791SWilson Ding }
17430530791SWilson Ding 
17530530791SWilson Ding static void mvebu_uart_set_mctrl(struct uart_port *port,
17630530791SWilson Ding 				 unsigned int mctrl)
17730530791SWilson Ding {
17830530791SWilson Ding /*
17930530791SWilson Ding  * Even if we do not support configuring the modem control lines, this
18030530791SWilson Ding  * function must be proided to the serial core
18130530791SWilson Ding  */
18230530791SWilson Ding }
18330530791SWilson Ding 
18430530791SWilson Ding static void mvebu_uart_stop_tx(struct uart_port *port)
18530530791SWilson Ding {
1865218d769SMiquel Raynal 	unsigned int ctl = readl(port->membase + UART_INTR(port));
18730530791SWilson Ding 
1885218d769SMiquel Raynal 	ctl &= ~CTRL_TX_RDY_INT(port);
1895218d769SMiquel Raynal 	writel(ctl, port->membase + UART_INTR(port));
19030530791SWilson Ding }
19130530791SWilson Ding 
19230530791SWilson Ding static void mvebu_uart_start_tx(struct uart_port *port)
19330530791SWilson Ding {
19430434b07SAllen Yan 	unsigned int ctl;
19530434b07SAllen Yan 	struct circ_buf *xmit = &port->state->xmit;
19630530791SWilson Ding 
19730434b07SAllen Yan 	if (IS_EXTENDED(port) && !uart_circ_empty(xmit)) {
19830434b07SAllen Yan 		writel(xmit->buf[xmit->tail], port->membase + UART_TSH(port));
19930434b07SAllen Yan 		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
20030434b07SAllen Yan 		port->icount.tx++;
20130434b07SAllen Yan 	}
20230434b07SAllen Yan 
20330434b07SAllen Yan 	ctl = readl(port->membase + UART_INTR(port));
2045218d769SMiquel Raynal 	ctl |= CTRL_TX_RDY_INT(port);
2055218d769SMiquel Raynal 	writel(ctl, port->membase + UART_INTR(port));
20630530791SWilson Ding }
20730530791SWilson Ding 
20830530791SWilson Ding static void mvebu_uart_stop_rx(struct uart_port *port)
20930530791SWilson Ding {
2105218d769SMiquel Raynal 	unsigned int ctl;
21130530791SWilson Ding 
2125218d769SMiquel Raynal 	ctl = readl(port->membase + UART_CTRL(port));
2135218d769SMiquel Raynal 	ctl &= ~CTRL_BRK_INT;
2145218d769SMiquel Raynal 	writel(ctl, port->membase + UART_CTRL(port));
2155218d769SMiquel Raynal 
2165218d769SMiquel Raynal 	ctl = readl(port->membase + UART_INTR(port));
2175218d769SMiquel Raynal 	ctl &= ~CTRL_RX_RDY_INT(port);
2185218d769SMiquel Raynal 	writel(ctl, port->membase + UART_INTR(port));
21930530791SWilson Ding }
22030530791SWilson Ding 
22130530791SWilson Ding static void mvebu_uart_break_ctl(struct uart_port *port, int brk)
22230530791SWilson Ding {
22330530791SWilson Ding 	unsigned int ctl;
22430530791SWilson Ding 	unsigned long flags;
22530530791SWilson Ding 
22630530791SWilson Ding 	spin_lock_irqsave(&port->lock, flags);
2275218d769SMiquel Raynal 	ctl = readl(port->membase + UART_CTRL(port));
22830530791SWilson Ding 	if (brk == -1)
22930530791SWilson Ding 		ctl |= CTRL_SND_BRK_SEQ;
23030530791SWilson Ding 	else
23130530791SWilson Ding 		ctl &= ~CTRL_SND_BRK_SEQ;
2325218d769SMiquel Raynal 	writel(ctl, port->membase + UART_CTRL(port));
23330530791SWilson Ding 	spin_unlock_irqrestore(&port->lock, flags);
23430530791SWilson Ding }
23530530791SWilson Ding 
23630530791SWilson Ding static void mvebu_uart_rx_chars(struct uart_port *port, unsigned int status)
23730530791SWilson Ding {
23830530791SWilson Ding 	struct tty_port *tport = &port->state->port;
23930530791SWilson Ding 	unsigned char ch = 0;
24030530791SWilson Ding 	char flag = 0;
24130530791SWilson Ding 
24230530791SWilson Ding 	do {
2435218d769SMiquel Raynal 		if (status & STAT_RX_RDY(port)) {
2445218d769SMiquel Raynal 			ch = readl(port->membase + UART_RBR(port));
24530530791SWilson Ding 			ch &= 0xff;
24630530791SWilson Ding 			flag = TTY_NORMAL;
24730530791SWilson Ding 			port->icount.rx++;
24830530791SWilson Ding 
24930530791SWilson Ding 			if (status & STAT_PAR_ERR)
25030530791SWilson Ding 				port->icount.parity++;
25130530791SWilson Ding 		}
25230530791SWilson Ding 
25330530791SWilson Ding 		if (status & STAT_BRK_DET) {
25430530791SWilson Ding 			port->icount.brk++;
25530530791SWilson Ding 			status &= ~(STAT_FRM_ERR | STAT_PAR_ERR);
25630530791SWilson Ding 			if (uart_handle_break(port))
25730530791SWilson Ding 				goto ignore_char;
25830530791SWilson Ding 		}
25930530791SWilson Ding 
26030530791SWilson Ding 		if (status & STAT_OVR_ERR)
26130530791SWilson Ding 			port->icount.overrun++;
26230530791SWilson Ding 
26330530791SWilson Ding 		if (status & STAT_FRM_ERR)
26430530791SWilson Ding 			port->icount.frame++;
26530530791SWilson Ding 
26630530791SWilson Ding 		if (uart_handle_sysrq_char(port, ch))
26730530791SWilson Ding 			goto ignore_char;
26830530791SWilson Ding 
26930530791SWilson Ding 		if (status & port->ignore_status_mask & STAT_PAR_ERR)
2705218d769SMiquel Raynal 			status &= ~STAT_RX_RDY(port);
27130530791SWilson Ding 
27230530791SWilson Ding 		status &= port->read_status_mask;
27330530791SWilson Ding 
27430530791SWilson Ding 		if (status & STAT_PAR_ERR)
27530530791SWilson Ding 			flag = TTY_PARITY;
27630530791SWilson Ding 
27730530791SWilson Ding 		status &= ~port->ignore_status_mask;
27830530791SWilson Ding 
2795218d769SMiquel Raynal 		if (status & STAT_RX_RDY(port))
28030530791SWilson Ding 			tty_insert_flip_char(tport, ch, flag);
28130530791SWilson Ding 
28230530791SWilson Ding 		if (status & STAT_BRK_DET)
28330530791SWilson Ding 			tty_insert_flip_char(tport, 0, TTY_BREAK);
28430530791SWilson Ding 
28530530791SWilson Ding 		if (status & STAT_FRM_ERR)
28630530791SWilson Ding 			tty_insert_flip_char(tport, 0, TTY_FRAME);
28730530791SWilson Ding 
28830530791SWilson Ding 		if (status & STAT_OVR_ERR)
28930530791SWilson Ding 			tty_insert_flip_char(tport, 0, TTY_OVERRUN);
29030530791SWilson Ding 
29130530791SWilson Ding ignore_char:
29230530791SWilson Ding 		status = readl(port->membase + UART_STAT);
2935218d769SMiquel Raynal 	} while (status & (STAT_RX_RDY(port) | STAT_BRK_DET));
29430530791SWilson Ding 
29530530791SWilson Ding 	tty_flip_buffer_push(tport);
29630530791SWilson Ding }
29730530791SWilson Ding 
29830530791SWilson Ding static void mvebu_uart_tx_chars(struct uart_port *port, unsigned int status)
29930530791SWilson Ding {
30030530791SWilson Ding 	struct circ_buf *xmit = &port->state->xmit;
30130530791SWilson Ding 	unsigned int count;
30230530791SWilson Ding 	unsigned int st;
30330530791SWilson Ding 
30430530791SWilson Ding 	if (port->x_char) {
3055218d769SMiquel Raynal 		writel(port->x_char, port->membase + UART_TSH(port));
30630530791SWilson Ding 		port->icount.tx++;
30730530791SWilson Ding 		port->x_char = 0;
30830530791SWilson Ding 		return;
30930530791SWilson Ding 	}
31030530791SWilson Ding 
31130530791SWilson Ding 	if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
31230530791SWilson Ding 		mvebu_uart_stop_tx(port);
31330530791SWilson Ding 		return;
31430530791SWilson Ding 	}
31530530791SWilson Ding 
31630530791SWilson Ding 	for (count = 0; count < port->fifosize; count++) {
3175218d769SMiquel Raynal 		writel(xmit->buf[xmit->tail], port->membase + UART_TSH(port));
31830530791SWilson Ding 		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
31930530791SWilson Ding 		port->icount.tx++;
32030530791SWilson Ding 
32130530791SWilson Ding 		if (uart_circ_empty(xmit))
32230530791SWilson Ding 			break;
32330530791SWilson Ding 
32430530791SWilson Ding 		st = readl(port->membase + UART_STAT);
32530530791SWilson Ding 		if (st & STAT_TX_FIFO_FUL)
32630530791SWilson Ding 			break;
32730530791SWilson Ding 	}
32830530791SWilson Ding 
32930530791SWilson Ding 	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
33030530791SWilson Ding 		uart_write_wakeup(port);
33130530791SWilson Ding 
33230530791SWilson Ding 	if (uart_circ_empty(xmit))
33330530791SWilson Ding 		mvebu_uart_stop_tx(port);
33430530791SWilson Ding }
33530530791SWilson Ding 
33630530791SWilson Ding static irqreturn_t mvebu_uart_isr(int irq, void *dev_id)
33730530791SWilson Ding {
33830530791SWilson Ding 	struct uart_port *port = (struct uart_port *)dev_id;
33930530791SWilson Ding 	unsigned int st = readl(port->membase + UART_STAT);
34030530791SWilson Ding 
3415218d769SMiquel Raynal 	if (st & (STAT_RX_RDY(port) | STAT_OVR_ERR | STAT_FRM_ERR |
3425218d769SMiquel Raynal 		  STAT_BRK_DET))
34330530791SWilson Ding 		mvebu_uart_rx_chars(port, st);
34430530791SWilson Ding 
3455218d769SMiquel Raynal 	if (st & STAT_TX_RDY(port))
34630530791SWilson Ding 		mvebu_uart_tx_chars(port, st);
34730530791SWilson Ding 
34830530791SWilson Ding 	return IRQ_HANDLED;
34930530791SWilson Ding }
35030530791SWilson Ding 
35195f78768SMiquel Raynal static irqreturn_t mvebu_uart_rx_isr(int irq, void *dev_id)
35295f78768SMiquel Raynal {
35395f78768SMiquel Raynal 	struct uart_port *port = (struct uart_port *)dev_id;
35495f78768SMiquel Raynal 	unsigned int st = readl(port->membase + UART_STAT);
35595f78768SMiquel Raynal 
35695f78768SMiquel Raynal 	if (st & (STAT_RX_RDY(port) | STAT_OVR_ERR | STAT_FRM_ERR |
35795f78768SMiquel Raynal 			STAT_BRK_DET))
35895f78768SMiquel Raynal 		mvebu_uart_rx_chars(port, st);
35995f78768SMiquel Raynal 
36095f78768SMiquel Raynal 	return IRQ_HANDLED;
36195f78768SMiquel Raynal }
36295f78768SMiquel Raynal 
36395f78768SMiquel Raynal static irqreturn_t mvebu_uart_tx_isr(int irq, void *dev_id)
36495f78768SMiquel Raynal {
36595f78768SMiquel Raynal 	struct uart_port *port = (struct uart_port *)dev_id;
36695f78768SMiquel Raynal 	unsigned int st = readl(port->membase + UART_STAT);
36795f78768SMiquel Raynal 
36895f78768SMiquel Raynal 	if (st & STAT_TX_RDY(port))
36995f78768SMiquel Raynal 		mvebu_uart_tx_chars(port, st);
37095f78768SMiquel Raynal 
37195f78768SMiquel Raynal 	return IRQ_HANDLED;
37295f78768SMiquel Raynal }
37395f78768SMiquel Raynal 
37430530791SWilson Ding static int mvebu_uart_startup(struct uart_port *port)
37530530791SWilson Ding {
37695f78768SMiquel Raynal 	struct mvebu_uart *mvuart = to_mvuart(port);
3775218d769SMiquel Raynal 	unsigned int ctl;
37830530791SWilson Ding 	int ret;
37930530791SWilson Ding 
38030530791SWilson Ding 	writel(CTRL_TXFIFO_RST | CTRL_RXFIFO_RST,
3815218d769SMiquel Raynal 	       port->membase + UART_CTRL(port));
38230530791SWilson Ding 	udelay(1);
3832ff23c48SAllen Yan 
3842ff23c48SAllen Yan 	/* Clear the error bits of state register before IRQ request */
3852ff23c48SAllen Yan 	ret = readl(port->membase + UART_STAT);
3862ff23c48SAllen Yan 	ret |= STAT_BRK_ERR;
3872ff23c48SAllen Yan 	writel(ret, port->membase + UART_STAT);
3882ff23c48SAllen Yan 
3895218d769SMiquel Raynal 	writel(CTRL_BRK_INT, port->membase + UART_CTRL(port));
3905218d769SMiquel Raynal 
3915218d769SMiquel Raynal 	ctl = readl(port->membase + UART_INTR(port));
3925218d769SMiquel Raynal 	ctl |= CTRL_RX_RDY_INT(port);
3935218d769SMiquel Raynal 	writel(ctl, port->membase + UART_INTR(port));
39430530791SWilson Ding 
39595f78768SMiquel Raynal 	if (!mvuart->irq[UART_TX_IRQ]) {
39695f78768SMiquel Raynal 		/* Old bindings with just one interrupt (UART0 only) */
39795f78768SMiquel Raynal 		ret = devm_request_irq(port->dev, mvuart->irq[UART_IRQ_SUM],
39895f78768SMiquel Raynal 				       mvebu_uart_isr, port->irqflags,
39995f78768SMiquel Raynal 				       dev_name(port->dev), port);
40030530791SWilson Ding 		if (ret) {
40195f78768SMiquel Raynal 			dev_err(port->dev, "unable to request IRQ %d\n",
40295f78768SMiquel Raynal 				mvuart->irq[UART_IRQ_SUM]);
40330530791SWilson Ding 			return ret;
40430530791SWilson Ding 		}
40595f78768SMiquel Raynal 	} else {
40695f78768SMiquel Raynal 		/* New bindings with an IRQ for RX and TX (both UART) */
40795f78768SMiquel Raynal 		ret = devm_request_irq(port->dev, mvuart->irq[UART_RX_IRQ],
40895f78768SMiquel Raynal 				       mvebu_uart_rx_isr, port->irqflags,
40995f78768SMiquel Raynal 				       dev_name(port->dev), port);
41095f78768SMiquel Raynal 		if (ret) {
41195f78768SMiquel Raynal 			dev_err(port->dev, "unable to request IRQ %d\n",
41295f78768SMiquel Raynal 				mvuart->irq[UART_RX_IRQ]);
41395f78768SMiquel Raynal 			return ret;
41495f78768SMiquel Raynal 		}
41595f78768SMiquel Raynal 
41695f78768SMiquel Raynal 		ret = devm_request_irq(port->dev, mvuart->irq[UART_TX_IRQ],
41795f78768SMiquel Raynal 				       mvebu_uart_tx_isr, port->irqflags,
41895f78768SMiquel Raynal 				       dev_name(port->dev),
41995f78768SMiquel Raynal 				       port);
42095f78768SMiquel Raynal 		if (ret) {
42195f78768SMiquel Raynal 			dev_err(port->dev, "unable to request IRQ %d\n",
42295f78768SMiquel Raynal 				mvuart->irq[UART_TX_IRQ]);
42395f78768SMiquel Raynal 			devm_free_irq(port->dev, mvuart->irq[UART_RX_IRQ],
42495f78768SMiquel Raynal 				      port);
42595f78768SMiquel Raynal 			return ret;
42695f78768SMiquel Raynal 		}
42795f78768SMiquel Raynal 	}
42830530791SWilson Ding 
42930530791SWilson Ding 	return 0;
43030530791SWilson Ding }
43130530791SWilson Ding 
43230530791SWilson Ding static void mvebu_uart_shutdown(struct uart_port *port)
43330530791SWilson Ding {
43495f78768SMiquel Raynal 	struct mvebu_uart *mvuart = to_mvuart(port);
43595f78768SMiquel Raynal 
4365218d769SMiquel Raynal 	writel(0, port->membase + UART_INTR(port));
437c2c1659bSThomas Petazzoni 
43895f78768SMiquel Raynal 	if (!mvuart->irq[UART_TX_IRQ]) {
43995f78768SMiquel Raynal 		devm_free_irq(port->dev, mvuart->irq[UART_IRQ_SUM], port);
44095f78768SMiquel Raynal 	} else {
44195f78768SMiquel Raynal 		devm_free_irq(port->dev, mvuart->irq[UART_RX_IRQ], port);
44295f78768SMiquel Raynal 		devm_free_irq(port->dev, mvuart->irq[UART_TX_IRQ], port);
44395f78768SMiquel Raynal 	}
44430530791SWilson Ding }
44530530791SWilson Ding 
44668a0db1dSAllen Yan static int mvebu_uart_baud_rate_set(struct uart_port *port, unsigned int baud)
44768a0db1dSAllen Yan {
44868a0db1dSAllen Yan 	struct mvebu_uart *mvuart = to_mvuart(port);
4490e4cf69eSMiquel Raynal 	unsigned int d_divisor, m_divisor;
45035d7a58aSMiquel Raynal 	u32 brdv, osamp;
45168a0db1dSAllen Yan 
45268a0db1dSAllen Yan 	if (IS_ERR(mvuart->clk))
45368a0db1dSAllen Yan 		return -PTR_ERR(mvuart->clk);
45468a0db1dSAllen Yan 
45568a0db1dSAllen Yan 	/*
4560e4cf69eSMiquel Raynal 	 * The baudrate is derived from the UART clock thanks to two divisors:
4570e4cf69eSMiquel Raynal 	 *   > D ("baud generator"): can divide the clock from 2 to 2^10 - 1.
4580e4cf69eSMiquel Raynal 	 *   > M ("fractional divisor"): allows a better accuracy for
4590e4cf69eSMiquel Raynal 	 *     baudrates higher than 230400.
4600e4cf69eSMiquel Raynal 	 *
4610e4cf69eSMiquel Raynal 	 * As the derivation of M is rather complicated, the code sticks to its
4620e4cf69eSMiquel Raynal 	 * default value (x16) when all the prescalers are zeroed, and only
4630e4cf69eSMiquel Raynal 	 * makes use of D to configure the desired baudrate.
46468a0db1dSAllen Yan 	 */
4650e4cf69eSMiquel Raynal 	m_divisor = OSAMP_DEFAULT_DIVISOR;
466*9078204cSPali Rohár 	d_divisor = DIV_ROUND_CLOSEST(port->uartclk, baud * m_divisor);
4670e4cf69eSMiquel Raynal 
46868a0db1dSAllen Yan 	brdv = readl(port->membase + UART_BRDV);
46968a0db1dSAllen Yan 	brdv &= ~BRDV_BAUD_MASK;
4700e4cf69eSMiquel Raynal 	brdv |= d_divisor;
47168a0db1dSAllen Yan 	writel(brdv, port->membase + UART_BRDV);
47268a0db1dSAllen Yan 
47335d7a58aSMiquel Raynal 	osamp = readl(port->membase + UART_OSAMP);
47435d7a58aSMiquel Raynal 	osamp &= ~OSAMP_DIVISORS_MASK;
47535d7a58aSMiquel Raynal 	writel(osamp, port->membase + UART_OSAMP);
47635d7a58aSMiquel Raynal 
47768a0db1dSAllen Yan 	return 0;
47868a0db1dSAllen Yan }
47968a0db1dSAllen Yan 
48030530791SWilson Ding static void mvebu_uart_set_termios(struct uart_port *port,
48130530791SWilson Ding 				   struct ktermios *termios,
48230530791SWilson Ding 				   struct ktermios *old)
48330530791SWilson Ding {
48430530791SWilson Ding 	unsigned long flags;
48530530791SWilson Ding 	unsigned int baud;
48630530791SWilson Ding 
48730530791SWilson Ding 	spin_lock_irqsave(&port->lock, flags);
48830530791SWilson Ding 
4895218d769SMiquel Raynal 	port->read_status_mask = STAT_RX_RDY(port) | STAT_OVR_ERR |
4905218d769SMiquel Raynal 		STAT_TX_RDY(port) | STAT_TX_FIFO_FUL;
49130530791SWilson Ding 
49230530791SWilson Ding 	if (termios->c_iflag & INPCK)
49330530791SWilson Ding 		port->read_status_mask |= STAT_FRM_ERR | STAT_PAR_ERR;
49430530791SWilson Ding 
49530530791SWilson Ding 	port->ignore_status_mask = 0;
49630530791SWilson Ding 	if (termios->c_iflag & IGNPAR)
49730530791SWilson Ding 		port->ignore_status_mask |=
49830530791SWilson Ding 			STAT_FRM_ERR | STAT_PAR_ERR | STAT_OVR_ERR;
49930530791SWilson Ding 
50030530791SWilson Ding 	if ((termios->c_cflag & CREAD) == 0)
5015218d769SMiquel Raynal 		port->ignore_status_mask |= STAT_RX_RDY(port) | STAT_BRK_ERR;
50230530791SWilson Ding 
50368a0db1dSAllen Yan 	/*
50468a0db1dSAllen Yan 	 * Maximum achievable frequency with simple baudrate divisor is 230400.
50568a0db1dSAllen Yan 	 * Since the error per bit frame would be of more than 15%, achieving
50668a0db1dSAllen Yan 	 * higher frequencies would require to implement the fractional divisor
50768a0db1dSAllen Yan 	 * feature.
50868a0db1dSAllen Yan 	 */
50968a0db1dSAllen Yan 	baud = uart_get_baud_rate(port, termios, old, 0, 230400);
51068a0db1dSAllen Yan 	if (mvebu_uart_baud_rate_set(port, baud)) {
51168a0db1dSAllen Yan 		/* No clock available, baudrate cannot be changed */
51230530791SWilson Ding 		if (old)
51368a0db1dSAllen Yan 			baud = uart_get_baud_rate(port, old, NULL, 0, 230400);
51468a0db1dSAllen Yan 	} else {
51568a0db1dSAllen Yan 		tty_termios_encode_baud_rate(termios, baud, baud);
51630530791SWilson Ding 		uart_update_timeout(port, termios->c_cflag, baud);
51768a0db1dSAllen Yan 	}
51868a0db1dSAllen Yan 
51968a0db1dSAllen Yan 	/* Only the following flag changes are supported */
52068a0db1dSAllen Yan 	if (old) {
52168a0db1dSAllen Yan 		termios->c_iflag &= INPCK | IGNPAR;
52268a0db1dSAllen Yan 		termios->c_iflag |= old->c_iflag & ~(INPCK | IGNPAR);
52368a0db1dSAllen Yan 		termios->c_cflag &= CREAD | CBAUD;
52468a0db1dSAllen Yan 		termios->c_cflag |= old->c_cflag & ~(CREAD | CBAUD);
525e0bf2d49SJan Kiszka 		termios->c_cflag |= CS8;
52668a0db1dSAllen Yan 	}
52730530791SWilson Ding 
52830530791SWilson Ding 	spin_unlock_irqrestore(&port->lock, flags);
52930530791SWilson Ding }
53030530791SWilson Ding 
53130530791SWilson Ding static const char *mvebu_uart_type(struct uart_port *port)
53230530791SWilson Ding {
53330530791SWilson Ding 	return MVEBU_UART_TYPE;
53430530791SWilson Ding }
53530530791SWilson Ding 
53630530791SWilson Ding static void mvebu_uart_release_port(struct uart_port *port)
53730530791SWilson Ding {
53830530791SWilson Ding 	/* Nothing to do here */
53930530791SWilson Ding }
54030530791SWilson Ding 
54130530791SWilson Ding static int mvebu_uart_request_port(struct uart_port *port)
54230530791SWilson Ding {
54330530791SWilson Ding 	return 0;
54430530791SWilson Ding }
54530530791SWilson Ding 
54630530791SWilson Ding #ifdef CONFIG_CONSOLE_POLL
54730530791SWilson Ding static int mvebu_uart_get_poll_char(struct uart_port *port)
54830530791SWilson Ding {
54930530791SWilson Ding 	unsigned int st = readl(port->membase + UART_STAT);
55030530791SWilson Ding 
5515218d769SMiquel Raynal 	if (!(st & STAT_RX_RDY(port)))
55230530791SWilson Ding 		return NO_POLL_CHAR;
55330530791SWilson Ding 
5545218d769SMiquel Raynal 	return readl(port->membase + UART_RBR(port));
55530530791SWilson Ding }
55630530791SWilson Ding 
55730530791SWilson Ding static void mvebu_uart_put_poll_char(struct uart_port *port, unsigned char c)
55830530791SWilson Ding {
55930530791SWilson Ding 	unsigned int st;
56030530791SWilson Ding 
56130530791SWilson Ding 	for (;;) {
56230530791SWilson Ding 		st = readl(port->membase + UART_STAT);
56330530791SWilson Ding 
56430530791SWilson Ding 		if (!(st & STAT_TX_FIFO_FUL))
56530530791SWilson Ding 			break;
56630530791SWilson Ding 
56730530791SWilson Ding 		udelay(1);
56830530791SWilson Ding 	}
56930530791SWilson Ding 
5705218d769SMiquel Raynal 	writel(c, port->membase + UART_TSH(port));
57130530791SWilson Ding }
57230530791SWilson Ding #endif
57330530791SWilson Ding 
57430530791SWilson Ding static const struct uart_ops mvebu_uart_ops = {
57530530791SWilson Ding 	.tx_empty	= mvebu_uart_tx_empty,
57630530791SWilson Ding 	.set_mctrl	= mvebu_uart_set_mctrl,
57730530791SWilson Ding 	.get_mctrl	= mvebu_uart_get_mctrl,
57830530791SWilson Ding 	.stop_tx	= mvebu_uart_stop_tx,
57930530791SWilson Ding 	.start_tx	= mvebu_uart_start_tx,
58030530791SWilson Ding 	.stop_rx	= mvebu_uart_stop_rx,
58130530791SWilson Ding 	.break_ctl	= mvebu_uart_break_ctl,
58230530791SWilson Ding 	.startup	= mvebu_uart_startup,
58330530791SWilson Ding 	.shutdown	= mvebu_uart_shutdown,
58430530791SWilson Ding 	.set_termios	= mvebu_uart_set_termios,
58530530791SWilson Ding 	.type		= mvebu_uart_type,
58630530791SWilson Ding 	.release_port	= mvebu_uart_release_port,
58730530791SWilson Ding 	.request_port	= mvebu_uart_request_port,
58830530791SWilson Ding #ifdef CONFIG_CONSOLE_POLL
58930530791SWilson Ding 	.poll_get_char	= mvebu_uart_get_poll_char,
59030530791SWilson Ding 	.poll_put_char	= mvebu_uart_put_poll_char,
59130530791SWilson Ding #endif
59230530791SWilson Ding };
59330530791SWilson Ding 
59430530791SWilson Ding /* Console Driver Operations  */
59530530791SWilson Ding 
59630530791SWilson Ding #ifdef CONFIG_SERIAL_MVEBU_CONSOLE
59730530791SWilson Ding /* Early Console */
59830530791SWilson Ding static void mvebu_uart_putc(struct uart_port *port, int c)
59930530791SWilson Ding {
60030530791SWilson Ding 	unsigned int st;
60130530791SWilson Ding 
60230530791SWilson Ding 	for (;;) {
60330530791SWilson Ding 		st = readl(port->membase + UART_STAT);
60430530791SWilson Ding 		if (!(st & STAT_TX_FIFO_FUL))
60530530791SWilson Ding 			break;
60630530791SWilson Ding 	}
60730530791SWilson Ding 
6085218d769SMiquel Raynal 	/* At early stage, DT is not parsed yet, only use UART0 */
6095218d769SMiquel Raynal 	writel(c, port->membase + UART_STD_TSH);
61030530791SWilson Ding 
61130530791SWilson Ding 	for (;;) {
61230530791SWilson Ding 		st = readl(port->membase + UART_STAT);
61330530791SWilson Ding 		if (st & STAT_TX_FIFO_EMP)
61430530791SWilson Ding 			break;
61530530791SWilson Ding 	}
61630530791SWilson Ding }
61730530791SWilson Ding 
61830530791SWilson Ding static void mvebu_uart_putc_early_write(struct console *con,
61930530791SWilson Ding 					const char *s,
6205607fa6cSJinchao Wang 					unsigned int n)
62130530791SWilson Ding {
62230530791SWilson Ding 	struct earlycon_device *dev = con->data;
62330530791SWilson Ding 
62430530791SWilson Ding 	uart_console_write(&dev->port, s, n, mvebu_uart_putc);
62530530791SWilson Ding }
62630530791SWilson Ding 
62730530791SWilson Ding static int __init
62830530791SWilson Ding mvebu_uart_early_console_setup(struct earlycon_device *device,
62930530791SWilson Ding 			       const char *opt)
63030530791SWilson Ding {
63130530791SWilson Ding 	if (!device->port.membase)
63230530791SWilson Ding 		return -ENODEV;
63330530791SWilson Ding 
63430530791SWilson Ding 	device->con->write = mvebu_uart_putc_early_write;
63530530791SWilson Ding 
63630530791SWilson Ding 	return 0;
63730530791SWilson Ding }
63830530791SWilson Ding 
63930530791SWilson Ding EARLYCON_DECLARE(ar3700_uart, mvebu_uart_early_console_setup);
64030530791SWilson Ding OF_EARLYCON_DECLARE(ar3700_uart, "marvell,armada-3700-uart",
64130530791SWilson Ding 		    mvebu_uart_early_console_setup);
64230530791SWilson Ding 
64330530791SWilson Ding static void wait_for_xmitr(struct uart_port *port)
64430530791SWilson Ding {
64530530791SWilson Ding 	u32 val;
64630530791SWilson Ding 
64730530791SWilson Ding 	readl_poll_timeout_atomic(port->membase + UART_STAT, val,
648c685af11SGabriel Matni 				  (val & STAT_TX_RDY(port)), 1, 10000);
64930530791SWilson Ding }
65030530791SWilson Ding 
65154ca955bSPali Rohár static void wait_for_xmite(struct uart_port *port)
65254ca955bSPali Rohár {
65354ca955bSPali Rohár 	u32 val;
65454ca955bSPali Rohár 
65554ca955bSPali Rohár 	readl_poll_timeout_atomic(port->membase + UART_STAT, val,
65654ca955bSPali Rohár 				  (val & STAT_TX_EMP), 1, 10000);
65754ca955bSPali Rohár }
65854ca955bSPali Rohár 
65930530791SWilson Ding static void mvebu_uart_console_putchar(struct uart_port *port, int ch)
66030530791SWilson Ding {
66130530791SWilson Ding 	wait_for_xmitr(port);
6625218d769SMiquel Raynal 	writel(ch, port->membase + UART_TSH(port));
66330530791SWilson Ding }
66430530791SWilson Ding 
66530530791SWilson Ding static void mvebu_uart_console_write(struct console *co, const char *s,
66630530791SWilson Ding 				     unsigned int count)
66730530791SWilson Ding {
66830530791SWilson Ding 	struct uart_port *port = &mvebu_uart_ports[co->index];
66930530791SWilson Ding 	unsigned long flags;
6705218d769SMiquel Raynal 	unsigned int ier, intr, ctl;
67130530791SWilson Ding 	int locked = 1;
67230530791SWilson Ding 
67330530791SWilson Ding 	if (oops_in_progress)
67430530791SWilson Ding 		locked = spin_trylock_irqsave(&port->lock, flags);
67530530791SWilson Ding 	else
67630530791SWilson Ding 		spin_lock_irqsave(&port->lock, flags);
67730530791SWilson Ding 
6785218d769SMiquel Raynal 	ier = readl(port->membase + UART_CTRL(port)) & CTRL_BRK_INT;
6795218d769SMiquel Raynal 	intr = readl(port->membase + UART_INTR(port)) &
6805218d769SMiquel Raynal 		(CTRL_RX_RDY_INT(port) | CTRL_TX_RDY_INT(port));
6815218d769SMiquel Raynal 	writel(0, port->membase + UART_CTRL(port));
6825218d769SMiquel Raynal 	writel(0, port->membase + UART_INTR(port));
68330530791SWilson Ding 
68430530791SWilson Ding 	uart_console_write(port, s, count, mvebu_uart_console_putchar);
68530530791SWilson Ding 
68654ca955bSPali Rohár 	wait_for_xmite(port);
68730530791SWilson Ding 
68830530791SWilson Ding 	if (ier)
6895218d769SMiquel Raynal 		writel(ier, port->membase + UART_CTRL(port));
6905218d769SMiquel Raynal 
6915218d769SMiquel Raynal 	if (intr) {
6925218d769SMiquel Raynal 		ctl = intr | readl(port->membase + UART_INTR(port));
6935218d769SMiquel Raynal 		writel(ctl, port->membase + UART_INTR(port));
6945218d769SMiquel Raynal 	}
69530530791SWilson Ding 
69630530791SWilson Ding 	if (locked)
69730530791SWilson Ding 		spin_unlock_irqrestore(&port->lock, flags);
69830530791SWilson Ding }
69930530791SWilson Ding 
70030530791SWilson Ding static int mvebu_uart_console_setup(struct console *co, char *options)
70130530791SWilson Ding {
70230530791SWilson Ding 	struct uart_port *port;
70330530791SWilson Ding 	int baud = 9600;
70430530791SWilson Ding 	int bits = 8;
70530530791SWilson Ding 	int parity = 'n';
70630530791SWilson Ding 	int flow = 'n';
70730530791SWilson Ding 
70830530791SWilson Ding 	if (co->index < 0 || co->index >= MVEBU_NR_UARTS)
70930530791SWilson Ding 		return -EINVAL;
71030530791SWilson Ding 
71130530791SWilson Ding 	port = &mvebu_uart_ports[co->index];
71230530791SWilson Ding 
71330530791SWilson Ding 	if (!port->mapbase || !port->membase) {
71430530791SWilson Ding 		pr_debug("console on ttyMV%i not present\n", co->index);
71530530791SWilson Ding 		return -ENODEV;
71630530791SWilson Ding 	}
71730530791SWilson Ding 
71830530791SWilson Ding 	if (options)
71930530791SWilson Ding 		uart_parse_options(options, &baud, &parity, &bits, &flow);
72030530791SWilson Ding 
72130530791SWilson Ding 	return uart_set_options(port, co, baud, parity, bits, flow);
72230530791SWilson Ding }
72330530791SWilson Ding 
72430530791SWilson Ding static struct uart_driver mvebu_uart_driver;
72530530791SWilson Ding 
72630530791SWilson Ding static struct console mvebu_uart_console = {
72730530791SWilson Ding 	.name	= "ttyMV",
72830530791SWilson Ding 	.write	= mvebu_uart_console_write,
72930530791SWilson Ding 	.device	= uart_console_device,
73030530791SWilson Ding 	.setup	= mvebu_uart_console_setup,
73130530791SWilson Ding 	.flags	= CON_PRINTBUFFER,
73230530791SWilson Ding 	.index	= -1,
73330530791SWilson Ding 	.data	= &mvebu_uart_driver,
73430530791SWilson Ding };
73530530791SWilson Ding 
73630530791SWilson Ding static int __init mvebu_uart_console_init(void)
73730530791SWilson Ding {
73830530791SWilson Ding 	register_console(&mvebu_uart_console);
73930530791SWilson Ding 	return 0;
74030530791SWilson Ding }
74130530791SWilson Ding 
74230530791SWilson Ding console_initcall(mvebu_uart_console_init);
74330530791SWilson Ding 
74430530791SWilson Ding 
74530530791SWilson Ding #endif /* CONFIG_SERIAL_MVEBU_CONSOLE */
74630530791SWilson Ding 
74730530791SWilson Ding static struct uart_driver mvebu_uart_driver = {
74830530791SWilson Ding 	.owner			= THIS_MODULE,
74902c33330SYehuda Yitschak 	.driver_name		= DRIVER_NAME,
75030530791SWilson Ding 	.dev_name		= "ttyMV",
75130530791SWilson Ding 	.nr			= MVEBU_NR_UARTS,
75230530791SWilson Ding #ifdef CONFIG_SERIAL_MVEBU_CONSOLE
75330530791SWilson Ding 	.cons			= &mvebu_uart_console,
75430530791SWilson Ding #endif
75530530791SWilson Ding };
75630530791SWilson Ding 
757394e8351SMiquel Raynal #if defined(CONFIG_PM)
758394e8351SMiquel Raynal static int mvebu_uart_suspend(struct device *dev)
759394e8351SMiquel Raynal {
760394e8351SMiquel Raynal 	struct mvebu_uart *mvuart = dev_get_drvdata(dev);
761394e8351SMiquel Raynal 	struct uart_port *port = mvuart->port;
762394e8351SMiquel Raynal 
763394e8351SMiquel Raynal 	uart_suspend_port(&mvebu_uart_driver, port);
764394e8351SMiquel Raynal 
765394e8351SMiquel Raynal 	mvuart->pm_regs.rbr = readl(port->membase + UART_RBR(port));
766394e8351SMiquel Raynal 	mvuart->pm_regs.tsh = readl(port->membase + UART_TSH(port));
767394e8351SMiquel Raynal 	mvuart->pm_regs.ctrl = readl(port->membase + UART_CTRL(port));
768394e8351SMiquel Raynal 	mvuart->pm_regs.intr = readl(port->membase + UART_INTR(port));
769394e8351SMiquel Raynal 	mvuart->pm_regs.stat = readl(port->membase + UART_STAT);
770394e8351SMiquel Raynal 	mvuart->pm_regs.brdv = readl(port->membase + UART_BRDV);
771394e8351SMiquel Raynal 	mvuart->pm_regs.osamp = readl(port->membase + UART_OSAMP);
772394e8351SMiquel Raynal 
773394e8351SMiquel Raynal 	device_set_wakeup_enable(dev, true);
774394e8351SMiquel Raynal 
775394e8351SMiquel Raynal 	return 0;
776394e8351SMiquel Raynal }
777394e8351SMiquel Raynal 
778394e8351SMiquel Raynal static int mvebu_uart_resume(struct device *dev)
779394e8351SMiquel Raynal {
780394e8351SMiquel Raynal 	struct mvebu_uart *mvuart = dev_get_drvdata(dev);
781394e8351SMiquel Raynal 	struct uart_port *port = mvuart->port;
782394e8351SMiquel Raynal 
783394e8351SMiquel Raynal 	writel(mvuart->pm_regs.rbr, port->membase + UART_RBR(port));
784394e8351SMiquel Raynal 	writel(mvuart->pm_regs.tsh, port->membase + UART_TSH(port));
785394e8351SMiquel Raynal 	writel(mvuart->pm_regs.ctrl, port->membase + UART_CTRL(port));
786394e8351SMiquel Raynal 	writel(mvuart->pm_regs.intr, port->membase + UART_INTR(port));
787394e8351SMiquel Raynal 	writel(mvuart->pm_regs.stat, port->membase + UART_STAT);
788394e8351SMiquel Raynal 	writel(mvuart->pm_regs.brdv, port->membase + UART_BRDV);
789394e8351SMiquel Raynal 	writel(mvuart->pm_regs.osamp, port->membase + UART_OSAMP);
790394e8351SMiquel Raynal 
791394e8351SMiquel Raynal 	uart_resume_port(&mvebu_uart_driver, port);
792394e8351SMiquel Raynal 
793394e8351SMiquel Raynal 	return 0;
794394e8351SMiquel Raynal }
795394e8351SMiquel Raynal 
796394e8351SMiquel Raynal static const struct dev_pm_ops mvebu_uart_pm_ops = {
797394e8351SMiquel Raynal 	.suspend        = mvebu_uart_suspend,
798394e8351SMiquel Raynal 	.resume         = mvebu_uart_resume,
799394e8351SMiquel Raynal };
800394e8351SMiquel Raynal #endif /* CONFIG_PM */
801394e8351SMiquel Raynal 
8025218d769SMiquel Raynal static const struct of_device_id mvebu_uart_of_match[];
8035218d769SMiquel Raynal 
80494228f95SAllen Yan /* Counter to keep track of each UART port id when not using CONFIG_OF */
80594228f95SAllen Yan static int uart_num_counter;
80694228f95SAllen Yan 
80730530791SWilson Ding static int mvebu_uart_probe(struct platform_device *pdev)
80830530791SWilson Ding {
80930530791SWilson Ding 	struct resource *reg = platform_get_resource(pdev, IORESOURCE_MEM, 0);
8105218d769SMiquel Raynal 	const struct of_device_id *match = of_match_device(mvebu_uart_of_match,
8115218d769SMiquel Raynal 							   &pdev->dev);
81230530791SWilson Ding 	struct uart_port *port;
8135218d769SMiquel Raynal 	struct mvebu_uart *mvuart;
81458e49346SQinglang Miao 	int id, irq;
81530530791SWilson Ding 
81695f78768SMiquel Raynal 	if (!reg) {
81795f78768SMiquel Raynal 		dev_err(&pdev->dev, "no registers defined\n");
81830530791SWilson Ding 		return -EINVAL;
81930530791SWilson Ding 	}
82030530791SWilson Ding 
82194228f95SAllen Yan 	/* Assume that all UART ports have a DT alias or none has */
82294228f95SAllen Yan 	id = of_alias_get_id(pdev->dev.of_node, "serial");
82394228f95SAllen Yan 	if (!pdev->dev.of_node || id < 0)
82494228f95SAllen Yan 		pdev->id = uart_num_counter++;
82594228f95SAllen Yan 	else
82694228f95SAllen Yan 		pdev->id = id;
82794228f95SAllen Yan 
82894228f95SAllen Yan 	if (pdev->id >= MVEBU_NR_UARTS) {
82994228f95SAllen Yan 		dev_err(&pdev->dev, "cannot have more than %d UART ports\n",
83094228f95SAllen Yan 			MVEBU_NR_UARTS);
83194228f95SAllen Yan 		return -EINVAL;
83294228f95SAllen Yan 	}
83394228f95SAllen Yan 
83494228f95SAllen Yan 	port = &mvebu_uart_ports[pdev->id];
83530530791SWilson Ding 
83630530791SWilson Ding 	spin_lock_init(&port->lock);
83730530791SWilson Ding 
83830530791SWilson Ding 	port->dev        = &pdev->dev;
83930530791SWilson Ding 	port->type       = PORT_MVEBU;
84030530791SWilson Ding 	port->ops        = &mvebu_uart_ops;
84130530791SWilson Ding 	port->regshift   = 0;
84230530791SWilson Ding 
84330530791SWilson Ding 	port->fifosize   = 32;
84430530791SWilson Ding 	port->iotype     = UPIO_MEM32;
84530530791SWilson Ding 	port->flags      = UPF_FIXED_PORT;
84694228f95SAllen Yan 	port->line       = pdev->id;
84730530791SWilson Ding 
84895f78768SMiquel Raynal 	/*
84995f78768SMiquel Raynal 	 * IRQ number is not stored in this structure because we may have two of
85095f78768SMiquel Raynal 	 * them per port (RX and TX). Instead, use the driver UART structure
85195f78768SMiquel Raynal 	 * array so called ->irq[].
85295f78768SMiquel Raynal 	 */
85395f78768SMiquel Raynal 	port->irq        = 0;
85430530791SWilson Ding 	port->irqflags   = 0;
85530530791SWilson Ding 	port->mapbase    = reg->start;
85630530791SWilson Ding 
85730530791SWilson Ding 	port->membase = devm_ioremap_resource(&pdev->dev, reg);
85830530791SWilson Ding 	if (IS_ERR(port->membase))
8594a3e2084Stangbin 		return PTR_ERR(port->membase);
86030530791SWilson Ding 
8615218d769SMiquel Raynal 	mvuart = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_uart),
86230530791SWilson Ding 			      GFP_KERNEL);
8635218d769SMiquel Raynal 	if (!mvuart)
86430530791SWilson Ding 		return -ENOMEM;
86530530791SWilson Ding 
86668a0db1dSAllen Yan 	/* Get controller data depending on the compatible string */
8675218d769SMiquel Raynal 	mvuart->data = (struct mvebu_uart_driver_data *)match->data;
8685218d769SMiquel Raynal 	mvuart->port = port;
86930530791SWilson Ding 
8705218d769SMiquel Raynal 	port->private_data = mvuart;
8715218d769SMiquel Raynal 	platform_set_drvdata(pdev, mvuart);
87230530791SWilson Ding 
87368a0db1dSAllen Yan 	/* Get fixed clock frequency */
87468a0db1dSAllen Yan 	mvuart->clk = devm_clk_get(&pdev->dev, NULL);
87568a0db1dSAllen Yan 	if (IS_ERR(mvuart->clk)) {
87668a0db1dSAllen Yan 		if (PTR_ERR(mvuart->clk) == -EPROBE_DEFER)
87768a0db1dSAllen Yan 			return PTR_ERR(mvuart->clk);
87868a0db1dSAllen Yan 
87968a0db1dSAllen Yan 		if (IS_EXTENDED(port)) {
88068a0db1dSAllen Yan 			dev_err(&pdev->dev, "unable to get UART clock\n");
88168a0db1dSAllen Yan 			return PTR_ERR(mvuart->clk);
88268a0db1dSAllen Yan 		}
88368a0db1dSAllen Yan 	} else {
88468a0db1dSAllen Yan 		if (!clk_prepare_enable(mvuart->clk))
88568a0db1dSAllen Yan 			port->uartclk = clk_get_rate(mvuart->clk);
88668a0db1dSAllen Yan 	}
88768a0db1dSAllen Yan 
88895f78768SMiquel Raynal 	/* Manage interrupts */
88995f78768SMiquel Raynal 	if (platform_irq_count(pdev) == 1) {
89095f78768SMiquel Raynal 		/* Old bindings: no name on the single unamed UART0 IRQ */
89195f78768SMiquel Raynal 		irq = platform_get_irq(pdev, 0);
8921df21786SStephen Boyd 		if (irq < 0)
89395f78768SMiquel Raynal 			return irq;
89495f78768SMiquel Raynal 
89595f78768SMiquel Raynal 		mvuart->irq[UART_IRQ_SUM] = irq;
89695f78768SMiquel Raynal 	} else {
89795f78768SMiquel Raynal 		/*
89895f78768SMiquel Raynal 		 * New bindings: named interrupts (RX, TX) for both UARTS,
89995f78768SMiquel Raynal 		 * only make use of uart-rx and uart-tx interrupts, do not use
90095f78768SMiquel Raynal 		 * uart-sum of UART0 port.
90195f78768SMiquel Raynal 		 */
90295f78768SMiquel Raynal 		irq = platform_get_irq_byname(pdev, "uart-rx");
9031df21786SStephen Boyd 		if (irq < 0)
90495f78768SMiquel Raynal 			return irq;
90595f78768SMiquel Raynal 
90695f78768SMiquel Raynal 		mvuart->irq[UART_RX_IRQ] = irq;
90795f78768SMiquel Raynal 
90895f78768SMiquel Raynal 		irq = platform_get_irq_byname(pdev, "uart-tx");
9091df21786SStephen Boyd 		if (irq < 0)
91095f78768SMiquel Raynal 			return irq;
91195f78768SMiquel Raynal 
91295f78768SMiquel Raynal 		mvuart->irq[UART_TX_IRQ] = irq;
91395f78768SMiquel Raynal 	}
91495f78768SMiquel Raynal 
9159c3d3ee1SAllen Yan 	/* UART Soft Reset*/
9169c3d3ee1SAllen Yan 	writel(CTRL_SOFT_RST, port->membase + UART_CTRL(port));
9179c3d3ee1SAllen Yan 	udelay(1);
9189c3d3ee1SAllen Yan 	writel(0, port->membase + UART_CTRL(port));
9199c3d3ee1SAllen Yan 
920b6353702SQinglang Miao 	return uart_add_one_port(&mvebu_uart_driver, port);
92130530791SWilson Ding }
92230530791SWilson Ding 
9235218d769SMiquel Raynal static struct mvebu_uart_driver_data uart_std_driver_data = {
9245218d769SMiquel Raynal 	.is_ext = false,
9255218d769SMiquel Raynal 	.regs.rbr = UART_STD_RBR,
9265218d769SMiquel Raynal 	.regs.tsh = UART_STD_TSH,
9275218d769SMiquel Raynal 	.regs.ctrl = UART_STD_CTRL1,
9285218d769SMiquel Raynal 	.regs.intr = UART_STD_CTRL2,
9295218d769SMiquel Raynal 	.flags.ctrl_tx_rdy_int = CTRL_STD_TX_RDY_INT,
9305218d769SMiquel Raynal 	.flags.ctrl_rx_rdy_int = CTRL_STD_RX_RDY_INT,
9315218d769SMiquel Raynal 	.flags.stat_tx_rdy = STAT_STD_TX_RDY,
9325218d769SMiquel Raynal 	.flags.stat_rx_rdy = STAT_STD_RX_RDY,
9335218d769SMiquel Raynal };
9345218d769SMiquel Raynal 
93553501e02SMiquel Raynal static struct mvebu_uart_driver_data uart_ext_driver_data = {
93653501e02SMiquel Raynal 	.is_ext = true,
93753501e02SMiquel Raynal 	.regs.rbr = UART_EXT_RBR,
93853501e02SMiquel Raynal 	.regs.tsh = UART_EXT_TSH,
93953501e02SMiquel Raynal 	.regs.ctrl = UART_EXT_CTRL1,
94053501e02SMiquel Raynal 	.regs.intr = UART_EXT_CTRL2,
94153501e02SMiquel Raynal 	.flags.ctrl_tx_rdy_int = CTRL_EXT_TX_RDY_INT,
94253501e02SMiquel Raynal 	.flags.ctrl_rx_rdy_int = CTRL_EXT_RX_RDY_INT,
94353501e02SMiquel Raynal 	.flags.stat_tx_rdy = STAT_EXT_TX_RDY,
94453501e02SMiquel Raynal 	.flags.stat_rx_rdy = STAT_EXT_RX_RDY,
94553501e02SMiquel Raynal };
94653501e02SMiquel Raynal 
94730530791SWilson Ding /* Match table for of_platform binding */
94830530791SWilson Ding static const struct of_device_id mvebu_uart_of_match[] = {
9495218d769SMiquel Raynal 	{
9505218d769SMiquel Raynal 		.compatible = "marvell,armada-3700-uart",
9515218d769SMiquel Raynal 		.data = (void *)&uart_std_driver_data,
9525218d769SMiquel Raynal 	},
95353501e02SMiquel Raynal 	{
95453501e02SMiquel Raynal 		.compatible = "marvell,armada-3700-uart-ext",
95553501e02SMiquel Raynal 		.data = (void *)&uart_ext_driver_data,
95653501e02SMiquel Raynal 	},
95730530791SWilson Ding 	{}
95830530791SWilson Ding };
95930530791SWilson Ding 
96030530791SWilson Ding static struct platform_driver mvebu_uart_platform_driver = {
96130530791SWilson Ding 	.probe	= mvebu_uart_probe,
96230530791SWilson Ding 	.driver	= {
96330530791SWilson Ding 		.name  = "mvebu-uart",
96430530791SWilson Ding 		.of_match_table = of_match_ptr(mvebu_uart_of_match),
96589ebc274SPaul Gortmaker 		.suppress_bind_attrs = true,
966394e8351SMiquel Raynal #if defined(CONFIG_PM)
967394e8351SMiquel Raynal 		.pm	= &mvebu_uart_pm_ops,
968394e8351SMiquel Raynal #endif /* CONFIG_PM */
96930530791SWilson Ding 	},
97030530791SWilson Ding };
97130530791SWilson Ding 
97230530791SWilson Ding static int __init mvebu_uart_init(void)
97330530791SWilson Ding {
97430530791SWilson Ding 	int ret;
97530530791SWilson Ding 
97630530791SWilson Ding 	ret = uart_register_driver(&mvebu_uart_driver);
97730530791SWilson Ding 	if (ret)
97830530791SWilson Ding 		return ret;
97930530791SWilson Ding 
98030530791SWilson Ding 	ret = platform_driver_register(&mvebu_uart_platform_driver);
98130530791SWilson Ding 	if (ret)
98230530791SWilson Ding 		uart_unregister_driver(&mvebu_uart_driver);
98330530791SWilson Ding 
98430530791SWilson Ding 	return ret;
98530530791SWilson Ding }
98630530791SWilson Ding arch_initcall(mvebu_uart_init);
987