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