19e8d5470SHammer Hsieh // SPDX-License-Identifier: GPL-2.0 29e8d5470SHammer Hsieh /* 39e8d5470SHammer Hsieh * Sunplus SoC UART driver 49e8d5470SHammer Hsieh * 59e8d5470SHammer Hsieh * Author: Hammer Hsieh <[email protected]> 69e8d5470SHammer Hsieh * 79e8d5470SHammer Hsieh * Note1: This driver is 8250-like uart, but are not register compatible. 89e8d5470SHammer Hsieh * 99e8d5470SHammer Hsieh * Note2: On some buses, for preventing data incoherence, must do a read 109e8d5470SHammer Hsieh * for ensure write made it to hardware. In this driver, function startup 119e8d5470SHammer Hsieh * and shutdown did not do a read but only do a write directly. For what? 129e8d5470SHammer Hsieh * In Sunplus bus communication between memory bus and peripheral bus with 139e8d5470SHammer Hsieh * posted write, it will send a specific command after last write command 149e8d5470SHammer Hsieh * to make sure write done. Then memory bus identify the specific command 159e8d5470SHammer Hsieh * and send done signal back to master device. After master device received 169e8d5470SHammer Hsieh * done signal, then proceed next write command. It is no need to do a read 179e8d5470SHammer Hsieh * before write. 189e8d5470SHammer Hsieh */ 199e8d5470SHammer Hsieh #include <linux/clk.h> 209e8d5470SHammer Hsieh #include <linux/console.h> 219e8d5470SHammer Hsieh #include <linux/interrupt.h> 229e8d5470SHammer Hsieh #include <linux/io.h> 239e8d5470SHammer Hsieh #include <linux/iopoll.h> 249e8d5470SHammer Hsieh #include <linux/module.h> 259e8d5470SHammer Hsieh #include <linux/of.h> 269e8d5470SHammer Hsieh #include <linux/of_platform.h> 279e8d5470SHammer Hsieh #include <linux/platform_device.h> 289e8d5470SHammer Hsieh #include <linux/reset.h> 299e8d5470SHammer Hsieh #include <linux/serial_core.h> 309e8d5470SHammer Hsieh #include <linux/serial_reg.h> 319e8d5470SHammer Hsieh #include <linux/sysrq.h> 329e8d5470SHammer Hsieh #include <linux/tty.h> 339e8d5470SHammer Hsieh #include <linux/tty_flip.h> 349e8d5470SHammer Hsieh #include <asm/irq.h> 359e8d5470SHammer Hsieh 369e8d5470SHammer Hsieh /* Register offsets */ 379e8d5470SHammer Hsieh #define SUP_UART_DATA 0x00 389e8d5470SHammer Hsieh #define SUP_UART_LSR 0x04 399e8d5470SHammer Hsieh #define SUP_UART_MSR 0x08 409e8d5470SHammer Hsieh #define SUP_UART_LCR 0x0C 419e8d5470SHammer Hsieh #define SUP_UART_MCR 0x10 429e8d5470SHammer Hsieh #define SUP_UART_DIV_L 0x14 439e8d5470SHammer Hsieh #define SUP_UART_DIV_H 0x18 449e8d5470SHammer Hsieh #define SUP_UART_ISC 0x1C 459e8d5470SHammer Hsieh #define SUP_UART_TX_RESIDUE 0x20 469e8d5470SHammer Hsieh #define SUP_UART_RX_RESIDUE 0x24 479e8d5470SHammer Hsieh 489e8d5470SHammer Hsieh /* Line Status Register bits */ 499e8d5470SHammer Hsieh #define SUP_UART_LSR_BC BIT(5) /* break condition status */ 509e8d5470SHammer Hsieh #define SUP_UART_LSR_FE BIT(4) /* frame error status */ 519e8d5470SHammer Hsieh #define SUP_UART_LSR_OE BIT(3) /* overrun error status */ 529e8d5470SHammer Hsieh #define SUP_UART_LSR_PE BIT(2) /* parity error status */ 539e8d5470SHammer Hsieh #define SUP_UART_LSR_RX BIT(1) /* 1: receive fifo not empty */ 549e8d5470SHammer Hsieh #define SUP_UART_LSR_TX BIT(0) /* 1: transmit fifo is not full */ 559e8d5470SHammer Hsieh #define SUP_UART_LSR_TX_NOT_FULL 1 569e8d5470SHammer Hsieh #define SUP_UART_LSR_BRK_ERROR_BITS GENMASK(5, 2) 579e8d5470SHammer Hsieh 589e8d5470SHammer Hsieh /* Line Control Register bits */ 599e8d5470SHammer Hsieh #define SUP_UART_LCR_SBC BIT(5) /* select break condition */ 609e8d5470SHammer Hsieh 619e8d5470SHammer Hsieh /* Modem Control Register bits */ 629e8d5470SHammer Hsieh #define SUP_UART_MCR_RI BIT(3) /* ring indicator */ 639e8d5470SHammer Hsieh #define SUP_UART_MCR_DCD BIT(2) /* data carrier detect */ 649e8d5470SHammer Hsieh 659e8d5470SHammer Hsieh /* Interrupt Status/Control Register bits */ 669e8d5470SHammer Hsieh #define SUP_UART_ISC_RXM BIT(5) /* RX interrupt enable */ 679e8d5470SHammer Hsieh #define SUP_UART_ISC_TXM BIT(4) /* TX interrupt enable */ 689e8d5470SHammer Hsieh #define SUP_UART_ISC_RX BIT(1) /* RX interrupt status */ 699e8d5470SHammer Hsieh #define SUP_UART_ISC_TX BIT(0) /* TX interrupt status */ 709e8d5470SHammer Hsieh 719e8d5470SHammer Hsieh #define SUP_DUMMY_READ BIT(16) /* drop bytes received on a !CREAD port */ 729e8d5470SHammer Hsieh #define SUP_UART_NR 5 739e8d5470SHammer Hsieh 749e8d5470SHammer Hsieh struct sunplus_uart_port { 759e8d5470SHammer Hsieh struct uart_port port; 769e8d5470SHammer Hsieh struct clk *clk; 779e8d5470SHammer Hsieh struct reset_control *rstc; 789e8d5470SHammer Hsieh }; 799e8d5470SHammer Hsieh 809e8d5470SHammer Hsieh static void sp_uart_put_char(struct uart_port *port, unsigned int ch) 819e8d5470SHammer Hsieh { 829e8d5470SHammer Hsieh writel(ch, port->membase + SUP_UART_DATA); 839e8d5470SHammer Hsieh } 849e8d5470SHammer Hsieh 859e8d5470SHammer Hsieh static u32 sunplus_tx_buf_not_full(struct uart_port *port) 869e8d5470SHammer Hsieh { 879e8d5470SHammer Hsieh unsigned int lsr = readl(port->membase + SUP_UART_LSR); 889e8d5470SHammer Hsieh 899e8d5470SHammer Hsieh return (lsr & SUP_UART_LSR_TX) ? SUP_UART_LSR_TX_NOT_FULL : 0; 909e8d5470SHammer Hsieh } 919e8d5470SHammer Hsieh 929e8d5470SHammer Hsieh static unsigned int sunplus_tx_empty(struct uart_port *port) 939e8d5470SHammer Hsieh { 949e8d5470SHammer Hsieh unsigned int lsr = readl(port->membase + SUP_UART_LSR); 959e8d5470SHammer Hsieh 969e8d5470SHammer Hsieh return (lsr & UART_LSR_TEMT) ? TIOCSER_TEMT : 0; 979e8d5470SHammer Hsieh } 989e8d5470SHammer Hsieh 999e8d5470SHammer Hsieh static void sunplus_set_mctrl(struct uart_port *port, unsigned int mctrl) 1009e8d5470SHammer Hsieh { 1019e8d5470SHammer Hsieh unsigned int mcr = readl(port->membase + SUP_UART_MCR); 1029e8d5470SHammer Hsieh 1039e8d5470SHammer Hsieh if (mctrl & TIOCM_DTR) 1049e8d5470SHammer Hsieh mcr |= UART_MCR_DTR; 1059e8d5470SHammer Hsieh else 1069e8d5470SHammer Hsieh mcr &= ~UART_MCR_DTR; 1079e8d5470SHammer Hsieh 1089e8d5470SHammer Hsieh if (mctrl & TIOCM_RTS) 1099e8d5470SHammer Hsieh mcr |= UART_MCR_RTS; 1109e8d5470SHammer Hsieh else 1119e8d5470SHammer Hsieh mcr &= ~UART_MCR_RTS; 1129e8d5470SHammer Hsieh 1139e8d5470SHammer Hsieh if (mctrl & TIOCM_CAR) 1149e8d5470SHammer Hsieh mcr |= SUP_UART_MCR_DCD; 1159e8d5470SHammer Hsieh else 1169e8d5470SHammer Hsieh mcr &= ~SUP_UART_MCR_DCD; 1179e8d5470SHammer Hsieh 1189e8d5470SHammer Hsieh if (mctrl & TIOCM_RI) 1199e8d5470SHammer Hsieh mcr |= SUP_UART_MCR_RI; 1209e8d5470SHammer Hsieh else 1219e8d5470SHammer Hsieh mcr &= ~SUP_UART_MCR_RI; 1229e8d5470SHammer Hsieh 1239e8d5470SHammer Hsieh if (mctrl & TIOCM_LOOP) 1249e8d5470SHammer Hsieh mcr |= UART_MCR_LOOP; 1259e8d5470SHammer Hsieh else 1269e8d5470SHammer Hsieh mcr &= ~UART_MCR_LOOP; 1279e8d5470SHammer Hsieh 1289e8d5470SHammer Hsieh writel(mcr, port->membase + SUP_UART_MCR); 1299e8d5470SHammer Hsieh } 1309e8d5470SHammer Hsieh 1319e8d5470SHammer Hsieh static unsigned int sunplus_get_mctrl(struct uart_port *port) 1329e8d5470SHammer Hsieh { 1339e8d5470SHammer Hsieh unsigned int mcr, ret = 0; 1349e8d5470SHammer Hsieh 1359e8d5470SHammer Hsieh mcr = readl(port->membase + SUP_UART_MCR); 1369e8d5470SHammer Hsieh 1379e8d5470SHammer Hsieh if (mcr & UART_MCR_DTR) 1389e8d5470SHammer Hsieh ret |= TIOCM_DTR; 1399e8d5470SHammer Hsieh 1409e8d5470SHammer Hsieh if (mcr & UART_MCR_RTS) 1419e8d5470SHammer Hsieh ret |= TIOCM_RTS; 1429e8d5470SHammer Hsieh 1439e8d5470SHammer Hsieh if (mcr & SUP_UART_MCR_DCD) 1449e8d5470SHammer Hsieh ret |= TIOCM_CAR; 1459e8d5470SHammer Hsieh 1469e8d5470SHammer Hsieh if (mcr & SUP_UART_MCR_RI) 1479e8d5470SHammer Hsieh ret |= TIOCM_RI; 1489e8d5470SHammer Hsieh 1499e8d5470SHammer Hsieh if (mcr & UART_MCR_LOOP) 1509e8d5470SHammer Hsieh ret |= TIOCM_LOOP; 1519e8d5470SHammer Hsieh 1529e8d5470SHammer Hsieh return ret; 1539e8d5470SHammer Hsieh } 1549e8d5470SHammer Hsieh 1559e8d5470SHammer Hsieh static void sunplus_stop_tx(struct uart_port *port) 1569e8d5470SHammer Hsieh { 1579e8d5470SHammer Hsieh unsigned int isc; 1589e8d5470SHammer Hsieh 1599e8d5470SHammer Hsieh isc = readl(port->membase + SUP_UART_ISC); 1609e8d5470SHammer Hsieh isc &= ~SUP_UART_ISC_TXM; 1619e8d5470SHammer Hsieh writel(isc, port->membase + SUP_UART_ISC); 1629e8d5470SHammer Hsieh } 1639e8d5470SHammer Hsieh 1649e8d5470SHammer Hsieh static void sunplus_start_tx(struct uart_port *port) 1659e8d5470SHammer Hsieh { 1669e8d5470SHammer Hsieh unsigned int isc; 1679e8d5470SHammer Hsieh 1689e8d5470SHammer Hsieh isc = readl(port->membase + SUP_UART_ISC); 1699e8d5470SHammer Hsieh isc |= SUP_UART_ISC_TXM; 1709e8d5470SHammer Hsieh writel(isc, port->membase + SUP_UART_ISC); 1719e8d5470SHammer Hsieh } 1729e8d5470SHammer Hsieh 1739e8d5470SHammer Hsieh static void sunplus_stop_rx(struct uart_port *port) 1749e8d5470SHammer Hsieh { 1759e8d5470SHammer Hsieh unsigned int isc; 1769e8d5470SHammer Hsieh 1779e8d5470SHammer Hsieh isc = readl(port->membase + SUP_UART_ISC); 1789e8d5470SHammer Hsieh isc &= ~SUP_UART_ISC_RXM; 1799e8d5470SHammer Hsieh writel(isc, port->membase + SUP_UART_ISC); 1809e8d5470SHammer Hsieh } 1819e8d5470SHammer Hsieh 1829e8d5470SHammer Hsieh static void sunplus_break_ctl(struct uart_port *port, int ctl) 1839e8d5470SHammer Hsieh { 1849e8d5470SHammer Hsieh unsigned long flags; 1859e8d5470SHammer Hsieh unsigned int lcr; 1869e8d5470SHammer Hsieh 1879e8d5470SHammer Hsieh spin_lock_irqsave(&port->lock, flags); 1889e8d5470SHammer Hsieh 1899e8d5470SHammer Hsieh lcr = readl(port->membase + SUP_UART_LCR); 1909e8d5470SHammer Hsieh 1919e8d5470SHammer Hsieh if (ctl) 1929e8d5470SHammer Hsieh lcr |= SUP_UART_LCR_SBC; /* start break */ 1939e8d5470SHammer Hsieh else 1949e8d5470SHammer Hsieh lcr &= ~SUP_UART_LCR_SBC; /* stop break */ 1959e8d5470SHammer Hsieh 1969e8d5470SHammer Hsieh writel(lcr, port->membase + SUP_UART_LCR); 1979e8d5470SHammer Hsieh 1989e8d5470SHammer Hsieh spin_unlock_irqrestore(&port->lock, flags); 1999e8d5470SHammer Hsieh } 2009e8d5470SHammer Hsieh 2019e8d5470SHammer Hsieh static void transmit_chars(struct uart_port *port) 2029e8d5470SHammer Hsieh { 2039e8d5470SHammer Hsieh struct circ_buf *xmit = &port->state->xmit; 2049e8d5470SHammer Hsieh 2059e8d5470SHammer Hsieh if (port->x_char) { 2069e8d5470SHammer Hsieh sp_uart_put_char(port, port->x_char); 2079e8d5470SHammer Hsieh port->icount.tx++; 2089e8d5470SHammer Hsieh port->x_char = 0; 2099e8d5470SHammer Hsieh return; 2109e8d5470SHammer Hsieh } 2119e8d5470SHammer Hsieh 2129e8d5470SHammer Hsieh if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { 2139e8d5470SHammer Hsieh sunplus_stop_tx(port); 2149e8d5470SHammer Hsieh return; 2159e8d5470SHammer Hsieh } 2169e8d5470SHammer Hsieh 2179e8d5470SHammer Hsieh do { 2189e8d5470SHammer Hsieh sp_uart_put_char(port, xmit->buf[xmit->tail]); 219*54ffabbeSIlpo Järvinen uart_xmit_advance(port, 1); 2209e8d5470SHammer Hsieh if (uart_circ_empty(xmit)) 2219e8d5470SHammer Hsieh break; 2229e8d5470SHammer Hsieh } while (sunplus_tx_buf_not_full(port)); 2239e8d5470SHammer Hsieh 2249e8d5470SHammer Hsieh if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) 2259e8d5470SHammer Hsieh uart_write_wakeup(port); 2269e8d5470SHammer Hsieh 2279e8d5470SHammer Hsieh if (uart_circ_empty(xmit)) 2289e8d5470SHammer Hsieh sunplus_stop_tx(port); 2299e8d5470SHammer Hsieh } 2309e8d5470SHammer Hsieh 2319e8d5470SHammer Hsieh static void receive_chars(struct uart_port *port) 2329e8d5470SHammer Hsieh { 2339e8d5470SHammer Hsieh unsigned int lsr = readl(port->membase + SUP_UART_LSR); 2349e8d5470SHammer Hsieh unsigned int ch, flag; 2359e8d5470SHammer Hsieh 2369e8d5470SHammer Hsieh do { 2379e8d5470SHammer Hsieh ch = readl(port->membase + SUP_UART_DATA); 2389e8d5470SHammer Hsieh flag = TTY_NORMAL; 2399e8d5470SHammer Hsieh port->icount.rx++; 2409e8d5470SHammer Hsieh 2419e8d5470SHammer Hsieh if (unlikely(lsr & SUP_UART_LSR_BRK_ERROR_BITS)) { 2429e8d5470SHammer Hsieh if (lsr & SUP_UART_LSR_BC) { 2439e8d5470SHammer Hsieh lsr &= ~(SUP_UART_LSR_FE | SUP_UART_LSR_PE); 2449e8d5470SHammer Hsieh port->icount.brk++; 2459e8d5470SHammer Hsieh flag = TTY_BREAK; 2469e8d5470SHammer Hsieh if (uart_handle_break(port)) 2479e8d5470SHammer Hsieh goto ignore_char; 2489e8d5470SHammer Hsieh } else if (lsr & SUP_UART_LSR_PE) { 2499e8d5470SHammer Hsieh port->icount.parity++; 2509e8d5470SHammer Hsieh flag = TTY_PARITY; 2519e8d5470SHammer Hsieh } else if (lsr & SUP_UART_LSR_FE) { 2529e8d5470SHammer Hsieh port->icount.frame++; 2539e8d5470SHammer Hsieh flag = TTY_FRAME; 2549e8d5470SHammer Hsieh } 2559e8d5470SHammer Hsieh 2569e8d5470SHammer Hsieh if (lsr & SUP_UART_LSR_OE) 2579e8d5470SHammer Hsieh port->icount.overrun++; 2589e8d5470SHammer Hsieh } 2599e8d5470SHammer Hsieh 2609e8d5470SHammer Hsieh if (port->ignore_status_mask & SUP_DUMMY_READ) 2619e8d5470SHammer Hsieh goto ignore_char; 2629e8d5470SHammer Hsieh 2639e8d5470SHammer Hsieh if (uart_handle_sysrq_char(port, ch)) 2649e8d5470SHammer Hsieh goto ignore_char; 2659e8d5470SHammer Hsieh 2669e8d5470SHammer Hsieh uart_insert_char(port, lsr, SUP_UART_LSR_OE, ch, flag); 2679e8d5470SHammer Hsieh 2689e8d5470SHammer Hsieh ignore_char: 2699e8d5470SHammer Hsieh lsr = readl(port->membase + SUP_UART_LSR); 2709e8d5470SHammer Hsieh } while (lsr & SUP_UART_LSR_RX); 2719e8d5470SHammer Hsieh 2729e8d5470SHammer Hsieh tty_flip_buffer_push(&port->state->port); 2739e8d5470SHammer Hsieh } 2749e8d5470SHammer Hsieh 2759e8d5470SHammer Hsieh static irqreturn_t sunplus_uart_irq(int irq, void *args) 2769e8d5470SHammer Hsieh { 2779e8d5470SHammer Hsieh struct uart_port *port = args; 2789e8d5470SHammer Hsieh unsigned int isc; 2799e8d5470SHammer Hsieh 2809e8d5470SHammer Hsieh spin_lock(&port->lock); 2819e8d5470SHammer Hsieh 2829e8d5470SHammer Hsieh isc = readl(port->membase + SUP_UART_ISC); 2839e8d5470SHammer Hsieh 2849e8d5470SHammer Hsieh if (isc & SUP_UART_ISC_RX) 2859e8d5470SHammer Hsieh receive_chars(port); 2869e8d5470SHammer Hsieh 2879e8d5470SHammer Hsieh if (isc & SUP_UART_ISC_TX) 2889e8d5470SHammer Hsieh transmit_chars(port); 2899e8d5470SHammer Hsieh 2909e8d5470SHammer Hsieh spin_unlock(&port->lock); 2919e8d5470SHammer Hsieh 2929e8d5470SHammer Hsieh return IRQ_HANDLED; 2939e8d5470SHammer Hsieh } 2949e8d5470SHammer Hsieh 2959e8d5470SHammer Hsieh static int sunplus_startup(struct uart_port *port) 2969e8d5470SHammer Hsieh { 2979e8d5470SHammer Hsieh unsigned long flags; 2989e8d5470SHammer Hsieh unsigned int isc = 0; 2999e8d5470SHammer Hsieh int ret; 3009e8d5470SHammer Hsieh 3019e8d5470SHammer Hsieh ret = request_irq(port->irq, sunplus_uart_irq, 0, "sunplus_uart", port); 3029e8d5470SHammer Hsieh if (ret) 3039e8d5470SHammer Hsieh return ret; 3049e8d5470SHammer Hsieh 3059e8d5470SHammer Hsieh spin_lock_irqsave(&port->lock, flags); 3069e8d5470SHammer Hsieh /* isc define Bit[7:4] int setting, Bit[3:0] int status 3079e8d5470SHammer Hsieh * isc register will clean Bit[3:0] int status after read 3089e8d5470SHammer Hsieh * only do a write to Bit[7:4] int setting 3099e8d5470SHammer Hsieh */ 3109e8d5470SHammer Hsieh isc |= SUP_UART_ISC_RXM; 3119e8d5470SHammer Hsieh writel(isc, port->membase + SUP_UART_ISC); 3129e8d5470SHammer Hsieh spin_unlock_irqrestore(&port->lock, flags); 3139e8d5470SHammer Hsieh 3149e8d5470SHammer Hsieh return 0; 3159e8d5470SHammer Hsieh } 3169e8d5470SHammer Hsieh 3179e8d5470SHammer Hsieh static void sunplus_shutdown(struct uart_port *port) 3189e8d5470SHammer Hsieh { 3199e8d5470SHammer Hsieh unsigned long flags; 3209e8d5470SHammer Hsieh 3219e8d5470SHammer Hsieh spin_lock_irqsave(&port->lock, flags); 3229e8d5470SHammer Hsieh /* isc define Bit[7:4] int setting, Bit[3:0] int status 3239e8d5470SHammer Hsieh * isc register will clean Bit[3:0] int status after read 3249e8d5470SHammer Hsieh * only do a write to Bit[7:4] int setting 3259e8d5470SHammer Hsieh */ 3269e8d5470SHammer Hsieh writel(0, port->membase + SUP_UART_ISC); /* disable all interrupt */ 3279e8d5470SHammer Hsieh spin_unlock_irqrestore(&port->lock, flags); 3289e8d5470SHammer Hsieh 3299e8d5470SHammer Hsieh free_irq(port->irq, port); 3309e8d5470SHammer Hsieh } 3319e8d5470SHammer Hsieh 3329e8d5470SHammer Hsieh static void sunplus_set_termios(struct uart_port *port, 3339e8d5470SHammer Hsieh struct ktermios *termios, 334bec5b814SIlpo Järvinen const struct ktermios *oldtermios) 3359e8d5470SHammer Hsieh { 3369e8d5470SHammer Hsieh u32 ext, div, div_l, div_h, baud, lcr; 3379e8d5470SHammer Hsieh u32 clk = port->uartclk; 3389e8d5470SHammer Hsieh unsigned long flags; 3399e8d5470SHammer Hsieh 3409e8d5470SHammer Hsieh baud = uart_get_baud_rate(port, termios, oldtermios, 0, port->uartclk / 16); 3419e8d5470SHammer Hsieh 3429e8d5470SHammer Hsieh /* baud rate = uartclk / ((16 * divisor + 1) + divisor_ext) */ 3439e8d5470SHammer Hsieh clk += baud >> 1; 3449e8d5470SHammer Hsieh div = clk / baud; 3459e8d5470SHammer Hsieh ext = div & 0x0F; 3469e8d5470SHammer Hsieh div = (div >> 4) - 1; 3479e8d5470SHammer Hsieh div_l = (div & 0xFF) | (ext << 12); 3489e8d5470SHammer Hsieh div_h = div >> 8; 3499e8d5470SHammer Hsieh 3509e8d5470SHammer Hsieh switch (termios->c_cflag & CSIZE) { 3519e8d5470SHammer Hsieh case CS5: 3529e8d5470SHammer Hsieh lcr = UART_LCR_WLEN5; 3539e8d5470SHammer Hsieh break; 3549e8d5470SHammer Hsieh case CS6: 3559e8d5470SHammer Hsieh lcr = UART_LCR_WLEN6; 3569e8d5470SHammer Hsieh break; 3579e8d5470SHammer Hsieh case CS7: 3589e8d5470SHammer Hsieh lcr = UART_LCR_WLEN7; 3599e8d5470SHammer Hsieh break; 3609e8d5470SHammer Hsieh default: 3619e8d5470SHammer Hsieh lcr = UART_LCR_WLEN8; 3629e8d5470SHammer Hsieh break; 3639e8d5470SHammer Hsieh } 3649e8d5470SHammer Hsieh 3659e8d5470SHammer Hsieh if (termios->c_cflag & CSTOPB) 3669e8d5470SHammer Hsieh lcr |= UART_LCR_STOP; 3679e8d5470SHammer Hsieh 3689e8d5470SHammer Hsieh if (termios->c_cflag & PARENB) { 3699e8d5470SHammer Hsieh lcr |= UART_LCR_PARITY; 3709e8d5470SHammer Hsieh 3719e8d5470SHammer Hsieh if (!(termios->c_cflag & PARODD)) 3729e8d5470SHammer Hsieh lcr |= UART_LCR_EPAR; 3739e8d5470SHammer Hsieh } 3749e8d5470SHammer Hsieh 3759e8d5470SHammer Hsieh spin_lock_irqsave(&port->lock, flags); 3769e8d5470SHammer Hsieh 3779e8d5470SHammer Hsieh uart_update_timeout(port, termios->c_cflag, baud); 3789e8d5470SHammer Hsieh 3799e8d5470SHammer Hsieh port->read_status_mask = 0; 3809e8d5470SHammer Hsieh if (termios->c_iflag & INPCK) 3819e8d5470SHammer Hsieh port->read_status_mask |= SUP_UART_LSR_PE | SUP_UART_LSR_FE; 3829e8d5470SHammer Hsieh 3839e8d5470SHammer Hsieh if (termios->c_iflag & (BRKINT | PARMRK)) 3849e8d5470SHammer Hsieh port->read_status_mask |= SUP_UART_LSR_BC; 3859e8d5470SHammer Hsieh 3869e8d5470SHammer Hsieh /* Characters to ignore */ 3879e8d5470SHammer Hsieh port->ignore_status_mask = 0; 3889e8d5470SHammer Hsieh if (termios->c_iflag & IGNPAR) 3899e8d5470SHammer Hsieh port->ignore_status_mask |= SUP_UART_LSR_FE | SUP_UART_LSR_PE; 3909e8d5470SHammer Hsieh 3919e8d5470SHammer Hsieh if (termios->c_iflag & IGNBRK) { 3929e8d5470SHammer Hsieh port->ignore_status_mask |= SUP_UART_LSR_BC; 3939e8d5470SHammer Hsieh 3949e8d5470SHammer Hsieh if (termios->c_iflag & IGNPAR) 3959e8d5470SHammer Hsieh port->ignore_status_mask |= SUP_UART_LSR_OE; 3969e8d5470SHammer Hsieh } 3979e8d5470SHammer Hsieh 3989e8d5470SHammer Hsieh /* Ignore all characters if CREAD is not set */ 3999e8d5470SHammer Hsieh if ((termios->c_cflag & CREAD) == 0) { 4009e8d5470SHammer Hsieh port->ignore_status_mask |= SUP_DUMMY_READ; 4019e8d5470SHammer Hsieh /* flush rx data FIFO */ 4029e8d5470SHammer Hsieh writel(0, port->membase + SUP_UART_RX_RESIDUE); 4039e8d5470SHammer Hsieh } 4049e8d5470SHammer Hsieh 4059e8d5470SHammer Hsieh /* Settings for baud rate divisor and lcr */ 4069e8d5470SHammer Hsieh writel(div_h, port->membase + SUP_UART_DIV_H); 4079e8d5470SHammer Hsieh writel(div_l, port->membase + SUP_UART_DIV_L); 4089e8d5470SHammer Hsieh writel(lcr, port->membase + SUP_UART_LCR); 4099e8d5470SHammer Hsieh 4109e8d5470SHammer Hsieh spin_unlock_irqrestore(&port->lock, flags); 4119e8d5470SHammer Hsieh } 4129e8d5470SHammer Hsieh 4139e8d5470SHammer Hsieh static void sunplus_set_ldisc(struct uart_port *port, struct ktermios *termios) 4149e8d5470SHammer Hsieh { 4159e8d5470SHammer Hsieh int new = termios->c_line; 4169e8d5470SHammer Hsieh 4179e8d5470SHammer Hsieh if (new == N_PPS) 4189e8d5470SHammer Hsieh port->flags |= UPF_HARDPPS_CD; 4199e8d5470SHammer Hsieh else 4209e8d5470SHammer Hsieh port->flags &= ~UPF_HARDPPS_CD; 4219e8d5470SHammer Hsieh } 4229e8d5470SHammer Hsieh 4239e8d5470SHammer Hsieh static const char *sunplus_type(struct uart_port *port) 4249e8d5470SHammer Hsieh { 4259e8d5470SHammer Hsieh return port->type == PORT_SUNPLUS ? "sunplus_uart" : NULL; 4269e8d5470SHammer Hsieh } 4279e8d5470SHammer Hsieh 4289e8d5470SHammer Hsieh static void sunplus_config_port(struct uart_port *port, int type) 4299e8d5470SHammer Hsieh { 4309e8d5470SHammer Hsieh if (type & UART_CONFIG_TYPE) 4319e8d5470SHammer Hsieh port->type = PORT_SUNPLUS; 4329e8d5470SHammer Hsieh } 4339e8d5470SHammer Hsieh 4349e8d5470SHammer Hsieh static int sunplus_verify_port(struct uart_port *port, struct serial_struct *ser) 4359e8d5470SHammer Hsieh { 4369e8d5470SHammer Hsieh if (ser->type != PORT_UNKNOWN && ser->type != PORT_SUNPLUS) 4379e8d5470SHammer Hsieh return -EINVAL; 4389e8d5470SHammer Hsieh 4399e8d5470SHammer Hsieh return 0; 4409e8d5470SHammer Hsieh } 4419e8d5470SHammer Hsieh 442a106848cSHammer Hsieh #if defined(CONFIG_SERIAL_SUNPLUS_CONSOLE) || defined(CONFIG_CONSOLE_POLL) 4439e8d5470SHammer Hsieh static void wait_for_xmitr(struct uart_port *port) 4449e8d5470SHammer Hsieh { 4459e8d5470SHammer Hsieh unsigned int val; 4469e8d5470SHammer Hsieh int ret; 4479e8d5470SHammer Hsieh 4489e8d5470SHammer Hsieh /* Wait while FIFO is full or timeout */ 4499e8d5470SHammer Hsieh ret = readl_poll_timeout_atomic(port->membase + SUP_UART_LSR, val, 4509e8d5470SHammer Hsieh (val & SUP_UART_LSR_TX), 1, 10000); 4519e8d5470SHammer Hsieh 4529e8d5470SHammer Hsieh if (ret == -ETIMEDOUT) { 4539e8d5470SHammer Hsieh dev_err(port->dev, "Timeout waiting while UART TX FULL\n"); 4549e8d5470SHammer Hsieh return; 4559e8d5470SHammer Hsieh } 4569e8d5470SHammer Hsieh } 4579e8d5470SHammer Hsieh #endif 4589e8d5470SHammer Hsieh 4599e8d5470SHammer Hsieh #ifdef CONFIG_CONSOLE_POLL 4609e8d5470SHammer Hsieh static void sunplus_poll_put_char(struct uart_port *port, unsigned char data) 4619e8d5470SHammer Hsieh { 4629e8d5470SHammer Hsieh wait_for_xmitr(port); 4639e8d5470SHammer Hsieh sp_uart_put_char(port, data); 4649e8d5470SHammer Hsieh } 4659e8d5470SHammer Hsieh 4669e8d5470SHammer Hsieh static int sunplus_poll_get_char(struct uart_port *port) 4679e8d5470SHammer Hsieh { 4689e8d5470SHammer Hsieh unsigned int lsr = readl(port->membase + SUP_UART_LSR); 4699e8d5470SHammer Hsieh 4709e8d5470SHammer Hsieh if (!(lsr & SUP_UART_LSR_RX)) 4719e8d5470SHammer Hsieh return NO_POLL_CHAR; 4729e8d5470SHammer Hsieh 4739e8d5470SHammer Hsieh return readl(port->membase + SUP_UART_DATA); 4749e8d5470SHammer Hsieh } 4759e8d5470SHammer Hsieh #endif 4769e8d5470SHammer Hsieh 4779e8d5470SHammer Hsieh static const struct uart_ops sunplus_uart_ops = { 4789e8d5470SHammer Hsieh .tx_empty = sunplus_tx_empty, 4799e8d5470SHammer Hsieh .set_mctrl = sunplus_set_mctrl, 4809e8d5470SHammer Hsieh .get_mctrl = sunplus_get_mctrl, 4819e8d5470SHammer Hsieh .stop_tx = sunplus_stop_tx, 4829e8d5470SHammer Hsieh .start_tx = sunplus_start_tx, 4839e8d5470SHammer Hsieh .stop_rx = sunplus_stop_rx, 4849e8d5470SHammer Hsieh .break_ctl = sunplus_break_ctl, 4859e8d5470SHammer Hsieh .startup = sunplus_startup, 4869e8d5470SHammer Hsieh .shutdown = sunplus_shutdown, 4879e8d5470SHammer Hsieh .set_termios = sunplus_set_termios, 4889e8d5470SHammer Hsieh .set_ldisc = sunplus_set_ldisc, 4899e8d5470SHammer Hsieh .type = sunplus_type, 4909e8d5470SHammer Hsieh .config_port = sunplus_config_port, 4919e8d5470SHammer Hsieh .verify_port = sunplus_verify_port, 4929e8d5470SHammer Hsieh #ifdef CONFIG_CONSOLE_POLL 4939e8d5470SHammer Hsieh .poll_put_char = sunplus_poll_put_char, 4949e8d5470SHammer Hsieh .poll_get_char = sunplus_poll_get_char, 4959e8d5470SHammer Hsieh #endif 4969e8d5470SHammer Hsieh }; 4979e8d5470SHammer Hsieh 4989e8d5470SHammer Hsieh #ifdef CONFIG_SERIAL_SUNPLUS_CONSOLE 499b68f42d4STom Rix static struct sunplus_uart_port *sunplus_console_ports[SUP_UART_NR]; 5009e8d5470SHammer Hsieh 5013f8bab17SJiri Slaby static void sunplus_uart_console_putchar(struct uart_port *port, 5023f8bab17SJiri Slaby unsigned char ch) 5039e8d5470SHammer Hsieh { 5049e8d5470SHammer Hsieh wait_for_xmitr(port); 5059e8d5470SHammer Hsieh sp_uart_put_char(port, ch); 5069e8d5470SHammer Hsieh } 5079e8d5470SHammer Hsieh 5089e8d5470SHammer Hsieh static void sunplus_console_write(struct console *co, 5099e8d5470SHammer Hsieh const char *s, 5109e8d5470SHammer Hsieh unsigned int count) 5119e8d5470SHammer Hsieh { 5129e8d5470SHammer Hsieh unsigned long flags; 5139e8d5470SHammer Hsieh int locked = 1; 5149e8d5470SHammer Hsieh 5159e8d5470SHammer Hsieh local_irq_save(flags); 5169e8d5470SHammer Hsieh 5179e8d5470SHammer Hsieh if (sunplus_console_ports[co->index]->port.sysrq) 5189e8d5470SHammer Hsieh locked = 0; 5199e8d5470SHammer Hsieh else if (oops_in_progress) 5209e8d5470SHammer Hsieh locked = spin_trylock(&sunplus_console_ports[co->index]->port.lock); 5219e8d5470SHammer Hsieh else 5229e8d5470SHammer Hsieh spin_lock(&sunplus_console_ports[co->index]->port.lock); 5239e8d5470SHammer Hsieh 5249e8d5470SHammer Hsieh uart_console_write(&sunplus_console_ports[co->index]->port, s, count, 5259e8d5470SHammer Hsieh sunplus_uart_console_putchar); 5269e8d5470SHammer Hsieh 5279e8d5470SHammer Hsieh if (locked) 5289e8d5470SHammer Hsieh spin_unlock(&sunplus_console_ports[co->index]->port.lock); 5299e8d5470SHammer Hsieh 5309e8d5470SHammer Hsieh local_irq_restore(flags); 5319e8d5470SHammer Hsieh } 5329e8d5470SHammer Hsieh 5339e8d5470SHammer Hsieh static int __init sunplus_console_setup(struct console *co, char *options) 5349e8d5470SHammer Hsieh { 5359e8d5470SHammer Hsieh struct sunplus_uart_port *sup; 5369e8d5470SHammer Hsieh int baud = 115200; 5379e8d5470SHammer Hsieh int bits = 8; 5389e8d5470SHammer Hsieh int parity = 'n'; 5399e8d5470SHammer Hsieh int flow = 'n'; 5409e8d5470SHammer Hsieh 5419e8d5470SHammer Hsieh if (co->index < 0 || co->index >= SUP_UART_NR) 5429e8d5470SHammer Hsieh return -EINVAL; 5439e8d5470SHammer Hsieh 5449e8d5470SHammer Hsieh sup = sunplus_console_ports[co->index]; 5459e8d5470SHammer Hsieh if (!sup) 5469e8d5470SHammer Hsieh return -ENODEV; 5479e8d5470SHammer Hsieh 5489e8d5470SHammer Hsieh if (options) 5499e8d5470SHammer Hsieh uart_parse_options(options, &baud, &parity, &bits, &flow); 5509e8d5470SHammer Hsieh 5519e8d5470SHammer Hsieh return uart_set_options(&sup->port, co, baud, parity, bits, flow); 5529e8d5470SHammer Hsieh } 5539e8d5470SHammer Hsieh 5549e8d5470SHammer Hsieh static struct uart_driver sunplus_uart_driver; 5559e8d5470SHammer Hsieh static struct console sunplus_uart_console = { 5569e8d5470SHammer Hsieh .name = "ttySUP", 5579e8d5470SHammer Hsieh .write = sunplus_console_write, 5589e8d5470SHammer Hsieh .device = uart_console_device, 5599e8d5470SHammer Hsieh .setup = sunplus_console_setup, 5609e8d5470SHammer Hsieh .flags = CON_PRINTBUFFER, 5619e8d5470SHammer Hsieh .index = -1, 5629e8d5470SHammer Hsieh .data = &sunplus_uart_driver 5639e8d5470SHammer Hsieh }; 564a106848cSHammer Hsieh 565a106848cSHammer Hsieh #define SERIAL_SUNPLUS_CONSOLE (&sunplus_uart_console) 566a106848cSHammer Hsieh #else 567a106848cSHammer Hsieh #define SERIAL_SUNPLUS_CONSOLE NULL 5689e8d5470SHammer Hsieh #endif 5699e8d5470SHammer Hsieh 5709e8d5470SHammer Hsieh static struct uart_driver sunplus_uart_driver = { 5719e8d5470SHammer Hsieh .owner = THIS_MODULE, 5729e8d5470SHammer Hsieh .driver_name = "sunplus_uart", 5739e8d5470SHammer Hsieh .dev_name = "ttySUP", 5749e8d5470SHammer Hsieh .major = TTY_MAJOR, 5759e8d5470SHammer Hsieh .minor = 64, 5769e8d5470SHammer Hsieh .nr = SUP_UART_NR, 577a106848cSHammer Hsieh .cons = SERIAL_SUNPLUS_CONSOLE, 5789e8d5470SHammer Hsieh }; 5799e8d5470SHammer Hsieh 5809e8d5470SHammer Hsieh static void sunplus_uart_disable_unprepare(void *data) 5819e8d5470SHammer Hsieh { 5829e8d5470SHammer Hsieh clk_disable_unprepare(data); 5839e8d5470SHammer Hsieh } 5849e8d5470SHammer Hsieh 5859e8d5470SHammer Hsieh static void sunplus_uart_reset_control_assert(void *data) 5869e8d5470SHammer Hsieh { 5879e8d5470SHammer Hsieh reset_control_assert(data); 5889e8d5470SHammer Hsieh } 5899e8d5470SHammer Hsieh 5909e8d5470SHammer Hsieh static int sunplus_uart_probe(struct platform_device *pdev) 5919e8d5470SHammer Hsieh { 5929e8d5470SHammer Hsieh struct sunplus_uart_port *sup; 5939e8d5470SHammer Hsieh struct uart_port *port; 5949e8d5470SHammer Hsieh struct resource *res; 5959e8d5470SHammer Hsieh int ret, irq; 5969e8d5470SHammer Hsieh 5979e8d5470SHammer Hsieh pdev->id = of_alias_get_id(pdev->dev.of_node, "serial"); 5989e8d5470SHammer Hsieh 5999e8d5470SHammer Hsieh if (pdev->id < 0 || pdev->id >= SUP_UART_NR) 6009e8d5470SHammer Hsieh return -EINVAL; 6019e8d5470SHammer Hsieh 6029e8d5470SHammer Hsieh sup = devm_kzalloc(&pdev->dev, sizeof(*sup), GFP_KERNEL); 6039e8d5470SHammer Hsieh if (!sup) 6049e8d5470SHammer Hsieh return -ENOMEM; 6059e8d5470SHammer Hsieh 6069e8d5470SHammer Hsieh sup->clk = devm_clk_get_optional(&pdev->dev, NULL); 6079e8d5470SHammer Hsieh if (IS_ERR(sup->clk)) 6089e8d5470SHammer Hsieh return dev_err_probe(&pdev->dev, PTR_ERR(sup->clk), "clk not found\n"); 6099e8d5470SHammer Hsieh 6109e8d5470SHammer Hsieh ret = clk_prepare_enable(sup->clk); 6119e8d5470SHammer Hsieh if (ret) 6129e8d5470SHammer Hsieh return ret; 6139e8d5470SHammer Hsieh 6149e8d5470SHammer Hsieh ret = devm_add_action_or_reset(&pdev->dev, sunplus_uart_disable_unprepare, sup->clk); 6159e8d5470SHammer Hsieh if (ret) 6169e8d5470SHammer Hsieh return ret; 6179e8d5470SHammer Hsieh 6189e8d5470SHammer Hsieh sup->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); 6199e8d5470SHammer Hsieh if (IS_ERR(sup->rstc)) 6209e8d5470SHammer Hsieh return dev_err_probe(&pdev->dev, PTR_ERR(sup->rstc), "rstc not found\n"); 6219e8d5470SHammer Hsieh 6229e8d5470SHammer Hsieh port = &sup->port; 6239e8d5470SHammer Hsieh 6249e8d5470SHammer Hsieh port->membase = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 6259e8d5470SHammer Hsieh if (IS_ERR(port->membase)) 6269e8d5470SHammer Hsieh return dev_err_probe(&pdev->dev, PTR_ERR(port->membase), "membase not found\n"); 6279e8d5470SHammer Hsieh 6289e8d5470SHammer Hsieh irq = platform_get_irq(pdev, 0); 6299e8d5470SHammer Hsieh if (irq < 0) 6309e8d5470SHammer Hsieh return irq; 6319e8d5470SHammer Hsieh 6329e8d5470SHammer Hsieh port->mapbase = res->start; 6339e8d5470SHammer Hsieh port->uartclk = clk_get_rate(sup->clk); 6349e8d5470SHammer Hsieh port->line = pdev->id; 6359e8d5470SHammer Hsieh port->irq = irq; 6369e8d5470SHammer Hsieh port->dev = &pdev->dev; 6379e8d5470SHammer Hsieh port->iotype = UPIO_MEM; 6389e8d5470SHammer Hsieh port->ops = &sunplus_uart_ops; 6399e8d5470SHammer Hsieh port->flags = UPF_BOOT_AUTOCONF; 6409e8d5470SHammer Hsieh port->fifosize = 128; 6419e8d5470SHammer Hsieh 6429e8d5470SHammer Hsieh ret = reset_control_deassert(sup->rstc); 6439e8d5470SHammer Hsieh if (ret) 6449e8d5470SHammer Hsieh return ret; 6459e8d5470SHammer Hsieh 6469e8d5470SHammer Hsieh ret = devm_add_action_or_reset(&pdev->dev, sunplus_uart_reset_control_assert, sup->rstc); 6479e8d5470SHammer Hsieh if (ret) 6489e8d5470SHammer Hsieh return ret; 6499e8d5470SHammer Hsieh 6509e8d5470SHammer Hsieh #ifdef CONFIG_SERIAL_SUNPLUS_CONSOLE 6519e8d5470SHammer Hsieh sunplus_console_ports[sup->port.line] = sup; 6529e8d5470SHammer Hsieh #endif 6539e8d5470SHammer Hsieh 6549e8d5470SHammer Hsieh platform_set_drvdata(pdev, &sup->port); 6559e8d5470SHammer Hsieh 6569e8d5470SHammer Hsieh ret = uart_add_one_port(&sunplus_uart_driver, &sup->port); 6579e8d5470SHammer Hsieh #ifdef CONFIG_SERIAL_SUNPLUS_CONSOLE 6589e8d5470SHammer Hsieh if (ret) 6599e8d5470SHammer Hsieh sunplus_console_ports[sup->port.line] = NULL; 6609e8d5470SHammer Hsieh #endif 6619e8d5470SHammer Hsieh 6629e8d5470SHammer Hsieh return ret; 6639e8d5470SHammer Hsieh } 6649e8d5470SHammer Hsieh 6659e8d5470SHammer Hsieh static int sunplus_uart_remove(struct platform_device *pdev) 6669e8d5470SHammer Hsieh { 6679e8d5470SHammer Hsieh struct sunplus_uart_port *sup = platform_get_drvdata(pdev); 6689e8d5470SHammer Hsieh 6699e8d5470SHammer Hsieh uart_remove_one_port(&sunplus_uart_driver, &sup->port); 6709e8d5470SHammer Hsieh 6719e8d5470SHammer Hsieh return 0; 6729e8d5470SHammer Hsieh } 6739e8d5470SHammer Hsieh 6749e8d5470SHammer Hsieh static int __maybe_unused sunplus_uart_suspend(struct device *dev) 6759e8d5470SHammer Hsieh { 6769e8d5470SHammer Hsieh struct sunplus_uart_port *sup = dev_get_drvdata(dev); 6779e8d5470SHammer Hsieh 6789e8d5470SHammer Hsieh if (!uart_console(&sup->port)) 6799e8d5470SHammer Hsieh uart_suspend_port(&sunplus_uart_driver, &sup->port); 6809e8d5470SHammer Hsieh 6819e8d5470SHammer Hsieh return 0; 6829e8d5470SHammer Hsieh } 6839e8d5470SHammer Hsieh 6849e8d5470SHammer Hsieh static int __maybe_unused sunplus_uart_resume(struct device *dev) 6859e8d5470SHammer Hsieh { 6869e8d5470SHammer Hsieh struct sunplus_uart_port *sup = dev_get_drvdata(dev); 6879e8d5470SHammer Hsieh 6889e8d5470SHammer Hsieh if (!uart_console(&sup->port)) 6899e8d5470SHammer Hsieh uart_resume_port(&sunplus_uart_driver, &sup->port); 6909e8d5470SHammer Hsieh 6919e8d5470SHammer Hsieh return 0; 6929e8d5470SHammer Hsieh } 6939e8d5470SHammer Hsieh 6949e8d5470SHammer Hsieh static const struct dev_pm_ops sunplus_uart_pm_ops = { 6959e8d5470SHammer Hsieh SET_SYSTEM_SLEEP_PM_OPS(sunplus_uart_suspend, sunplus_uart_resume) 6969e8d5470SHammer Hsieh }; 6979e8d5470SHammer Hsieh 6989e8d5470SHammer Hsieh static const struct of_device_id sp_uart_of_match[] = { 6999e8d5470SHammer Hsieh { .compatible = "sunplus,sp7021-uart" }, 7009e8d5470SHammer Hsieh {} 7019e8d5470SHammer Hsieh }; 7029e8d5470SHammer Hsieh MODULE_DEVICE_TABLE(of, sp_uart_of_match); 7039e8d5470SHammer Hsieh 7049e8d5470SHammer Hsieh static struct platform_driver sunplus_uart_platform_driver = { 7059e8d5470SHammer Hsieh .probe = sunplus_uart_probe, 7069e8d5470SHammer Hsieh .remove = sunplus_uart_remove, 7079e8d5470SHammer Hsieh .driver = { 7089e8d5470SHammer Hsieh .name = "sunplus_uart", 7099e8d5470SHammer Hsieh .of_match_table = sp_uart_of_match, 7109e8d5470SHammer Hsieh .pm = &sunplus_uart_pm_ops, 7119e8d5470SHammer Hsieh } 7129e8d5470SHammer Hsieh }; 7139e8d5470SHammer Hsieh 7149e8d5470SHammer Hsieh static int __init sunplus_uart_init(void) 7159e8d5470SHammer Hsieh { 7169e8d5470SHammer Hsieh int ret; 7179e8d5470SHammer Hsieh 7189e8d5470SHammer Hsieh ret = uart_register_driver(&sunplus_uart_driver); 7199e8d5470SHammer Hsieh if (ret) 7209e8d5470SHammer Hsieh return ret; 7219e8d5470SHammer Hsieh 7229e8d5470SHammer Hsieh ret = platform_driver_register(&sunplus_uart_platform_driver); 7239e8d5470SHammer Hsieh if (ret) 7249e8d5470SHammer Hsieh uart_unregister_driver(&sunplus_uart_driver); 7259e8d5470SHammer Hsieh 7269e8d5470SHammer Hsieh return ret; 7279e8d5470SHammer Hsieh } 7289e8d5470SHammer Hsieh module_init(sunplus_uart_init); 7299e8d5470SHammer Hsieh 7309e8d5470SHammer Hsieh static void __exit sunplus_uart_exit(void) 7319e8d5470SHammer Hsieh { 7329e8d5470SHammer Hsieh platform_driver_unregister(&sunplus_uart_platform_driver); 7339e8d5470SHammer Hsieh uart_unregister_driver(&sunplus_uart_driver); 7349e8d5470SHammer Hsieh } 7359e8d5470SHammer Hsieh module_exit(sunplus_uart_exit); 7369e8d5470SHammer Hsieh 7379e8d5470SHammer Hsieh #ifdef CONFIG_SERIAL_EARLYCON 7383f8bab17SJiri Slaby static void sunplus_uart_putc(struct uart_port *port, unsigned char c) 7399e8d5470SHammer Hsieh { 7409e8d5470SHammer Hsieh unsigned int val; 7419e8d5470SHammer Hsieh int ret; 7429e8d5470SHammer Hsieh 7439e8d5470SHammer Hsieh ret = readl_poll_timeout_atomic(port->membase + SUP_UART_LSR, val, 7449e8d5470SHammer Hsieh (val & UART_LSR_TEMT), 1, 10000); 7459e8d5470SHammer Hsieh if (ret) 7469e8d5470SHammer Hsieh return; 7479e8d5470SHammer Hsieh 7489e8d5470SHammer Hsieh writel(c, port->membase + SUP_UART_DATA); 7499e8d5470SHammer Hsieh } 7509e8d5470SHammer Hsieh 7519e8d5470SHammer Hsieh static void sunplus_uart_early_write(struct console *con, const char *s, unsigned int n) 7529e8d5470SHammer Hsieh { 7539e8d5470SHammer Hsieh struct earlycon_device *dev = con->data; 7549e8d5470SHammer Hsieh 7559e8d5470SHammer Hsieh uart_console_write(&dev->port, s, n, sunplus_uart_putc); 7569e8d5470SHammer Hsieh } 7579e8d5470SHammer Hsieh 7589e8d5470SHammer Hsieh static int __init 7599e8d5470SHammer Hsieh sunplus_uart_early_setup(struct earlycon_device *dev, const char *opt) 7609e8d5470SHammer Hsieh { 7619e8d5470SHammer Hsieh if (!(dev->port.membase || dev->port.iobase)) 7629e8d5470SHammer Hsieh return -ENODEV; 7639e8d5470SHammer Hsieh 7649e8d5470SHammer Hsieh dev->con->write = sunplus_uart_early_write; 7659e8d5470SHammer Hsieh 7669e8d5470SHammer Hsieh return 0; 7679e8d5470SHammer Hsieh } 7689e8d5470SHammer Hsieh OF_EARLYCON_DECLARE(sunplus_uart, "sunplus,sp7021-uart", sunplus_uart_early_setup); 7699e8d5470SHammer Hsieh #endif 7709e8d5470SHammer Hsieh 7719e8d5470SHammer Hsieh MODULE_DESCRIPTION("Sunplus UART driver"); 7729e8d5470SHammer Hsieh MODULE_AUTHOR("Hammer Hsieh <[email protected]>"); 7739e8d5470SHammer Hsieh MODULE_LICENSE("GPL v2"); 774