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