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