1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
25930cb35SBaruch Siach /*
35930cb35SBaruch Siach * Driver for Conexant Digicolor serial ports (USART)
45930cb35SBaruch Siach *
55930cb35SBaruch Siach * Author: Baruch Siach <baruch@tkos.co.il>
65930cb35SBaruch Siach *
75930cb35SBaruch Siach * Copyright (C) 2014 Paradox Innovation Ltd.
85930cb35SBaruch Siach */
95930cb35SBaruch Siach
105930cb35SBaruch Siach #include <linux/module.h>
115930cb35SBaruch Siach #include <linux/console.h>
125930cb35SBaruch Siach #include <linux/serial_core.h>
135930cb35SBaruch Siach #include <linux/serial.h>
145930cb35SBaruch Siach #include <linux/clk.h>
155930cb35SBaruch Siach #include <linux/io.h>
165930cb35SBaruch Siach #include <linux/tty.h>
175930cb35SBaruch Siach #include <linux/tty_flip.h>
185930cb35SBaruch Siach #include <linux/of.h>
195930cb35SBaruch Siach #include <linux/platform_device.h>
205930cb35SBaruch Siach #include <linux/workqueue.h>
215930cb35SBaruch Siach
225930cb35SBaruch Siach #define UA_ENABLE 0x00
235930cb35SBaruch Siach #define UA_ENABLE_ENABLE BIT(0)
245930cb35SBaruch Siach
255930cb35SBaruch Siach #define UA_CONTROL 0x01
265930cb35SBaruch Siach #define UA_CONTROL_RX_ENABLE BIT(0)
275930cb35SBaruch Siach #define UA_CONTROL_TX_ENABLE BIT(1)
285930cb35SBaruch Siach #define UA_CONTROL_SOFT_RESET BIT(2)
295930cb35SBaruch Siach
305930cb35SBaruch Siach #define UA_STATUS 0x02
315930cb35SBaruch Siach #define UA_STATUS_PARITY_ERR BIT(0)
325930cb35SBaruch Siach #define UA_STATUS_FRAME_ERR BIT(1)
335930cb35SBaruch Siach #define UA_STATUS_OVERRUN_ERR BIT(2)
345930cb35SBaruch Siach #define UA_STATUS_TX_READY BIT(6)
355930cb35SBaruch Siach
365930cb35SBaruch Siach #define UA_CONFIG 0x03
375930cb35SBaruch Siach #define UA_CONFIG_CHAR_LEN BIT(0)
385930cb35SBaruch Siach #define UA_CONFIG_STOP_BITS BIT(1)
395930cb35SBaruch Siach #define UA_CONFIG_PARITY BIT(2)
405930cb35SBaruch Siach #define UA_CONFIG_ODD_PARITY BIT(4)
415930cb35SBaruch Siach
425930cb35SBaruch Siach #define UA_EMI_REC 0x04
435930cb35SBaruch Siach
445930cb35SBaruch Siach #define UA_HBAUD_LO 0x08
455930cb35SBaruch Siach #define UA_HBAUD_HI 0x09
465930cb35SBaruch Siach
475930cb35SBaruch Siach #define UA_STATUS_FIFO 0x0a
485930cb35SBaruch Siach #define UA_STATUS_FIFO_RX_EMPTY BIT(2)
495930cb35SBaruch Siach #define UA_STATUS_FIFO_RX_INT_ALMOST BIT(3)
505930cb35SBaruch Siach #define UA_STATUS_FIFO_TX_FULL BIT(4)
515930cb35SBaruch Siach #define UA_STATUS_FIFO_TX_INT_ALMOST BIT(7)
525930cb35SBaruch Siach
535930cb35SBaruch Siach #define UA_CONFIG_FIFO 0x0b
545930cb35SBaruch Siach #define UA_CONFIG_FIFO_RX_THRESH 7
555930cb35SBaruch Siach #define UA_CONFIG_FIFO_RX_FIFO_MODE BIT(3)
565930cb35SBaruch Siach #define UA_CONFIG_FIFO_TX_FIFO_MODE BIT(7)
575930cb35SBaruch Siach
585930cb35SBaruch Siach #define UA_INTFLAG_CLEAR 0x1c
595930cb35SBaruch Siach #define UA_INTFLAG_SET 0x1d
605930cb35SBaruch Siach #define UA_INT_ENABLE 0x1e
615930cb35SBaruch Siach #define UA_INT_STATUS 0x1f
625930cb35SBaruch Siach
635930cb35SBaruch Siach #define UA_INT_TX BIT(0)
645930cb35SBaruch Siach #define UA_INT_RX BIT(1)
655930cb35SBaruch Siach
665930cb35SBaruch Siach #define DIGICOLOR_USART_NR 3
675930cb35SBaruch Siach
685930cb35SBaruch Siach /*
695930cb35SBaruch Siach * We use the 16 bytes hardware FIFO to buffer Rx traffic. Rx interrupt is
705930cb35SBaruch Siach * only produced when the FIFO is filled more than a certain configurable
715930cb35SBaruch Siach * threshold. Unfortunately, there is no way to set this threshold below half
725930cb35SBaruch Siach * FIFO. This means that we must periodically poll the FIFO status register to
735930cb35SBaruch Siach * see whether there are waiting Rx bytes.
745930cb35SBaruch Siach */
755930cb35SBaruch Siach
765930cb35SBaruch Siach struct digicolor_port {
775930cb35SBaruch Siach struct uart_port port;
785930cb35SBaruch Siach struct delayed_work rx_poll_work;
795930cb35SBaruch Siach };
805930cb35SBaruch Siach
815930cb35SBaruch Siach static struct uart_port *digicolor_ports[DIGICOLOR_USART_NR];
825930cb35SBaruch Siach
digicolor_uart_tx_full(struct uart_port * port)835930cb35SBaruch Siach static bool digicolor_uart_tx_full(struct uart_port *port)
845930cb35SBaruch Siach {
855930cb35SBaruch Siach return !!(readb_relaxed(port->membase + UA_STATUS_FIFO) &
865930cb35SBaruch Siach UA_STATUS_FIFO_TX_FULL);
875930cb35SBaruch Siach }
885930cb35SBaruch Siach
digicolor_uart_rx_empty(struct uart_port * port)895930cb35SBaruch Siach static bool digicolor_uart_rx_empty(struct uart_port *port)
905930cb35SBaruch Siach {
915930cb35SBaruch Siach return !!(readb_relaxed(port->membase + UA_STATUS_FIFO) &
925930cb35SBaruch Siach UA_STATUS_FIFO_RX_EMPTY);
935930cb35SBaruch Siach }
945930cb35SBaruch Siach
digicolor_uart_stop_tx(struct uart_port * port)955930cb35SBaruch Siach static void digicolor_uart_stop_tx(struct uart_port *port)
965930cb35SBaruch Siach {
975930cb35SBaruch Siach u8 int_enable = readb_relaxed(port->membase + UA_INT_ENABLE);
985930cb35SBaruch Siach
995930cb35SBaruch Siach int_enable &= ~UA_INT_TX;
1005930cb35SBaruch Siach writeb_relaxed(int_enable, port->membase + UA_INT_ENABLE);
1015930cb35SBaruch Siach }
1025930cb35SBaruch Siach
digicolor_uart_start_tx(struct uart_port * port)1035930cb35SBaruch Siach static void digicolor_uart_start_tx(struct uart_port *port)
1045930cb35SBaruch Siach {
1055930cb35SBaruch Siach u8 int_enable = readb_relaxed(port->membase + UA_INT_ENABLE);
1065930cb35SBaruch Siach
1075930cb35SBaruch Siach int_enable |= UA_INT_TX;
1085930cb35SBaruch Siach writeb_relaxed(int_enable, port->membase + UA_INT_ENABLE);
1095930cb35SBaruch Siach }
1105930cb35SBaruch Siach
digicolor_uart_stop_rx(struct uart_port * port)1115930cb35SBaruch Siach static void digicolor_uart_stop_rx(struct uart_port *port)
1125930cb35SBaruch Siach {
1135930cb35SBaruch Siach u8 int_enable = readb_relaxed(port->membase + UA_INT_ENABLE);
1145930cb35SBaruch Siach
1155930cb35SBaruch Siach int_enable &= ~UA_INT_RX;
1165930cb35SBaruch Siach writeb_relaxed(int_enable, port->membase + UA_INT_ENABLE);
1175930cb35SBaruch Siach }
1185930cb35SBaruch Siach
digicolor_rx_poll(struct work_struct * work)1195930cb35SBaruch Siach static void digicolor_rx_poll(struct work_struct *work)
1205930cb35SBaruch Siach {
1215930cb35SBaruch Siach struct digicolor_port *dp =
1225930cb35SBaruch Siach container_of(to_delayed_work(work),
1235930cb35SBaruch Siach struct digicolor_port, rx_poll_work);
1245930cb35SBaruch Siach
1255930cb35SBaruch Siach if (!digicolor_uart_rx_empty(&dp->port))
1265930cb35SBaruch Siach /* force RX interrupt */
1275930cb35SBaruch Siach writeb_relaxed(UA_INT_RX, dp->port.membase + UA_INTFLAG_SET);
1285930cb35SBaruch Siach
1295930cb35SBaruch Siach schedule_delayed_work(&dp->rx_poll_work, msecs_to_jiffies(100));
1305930cb35SBaruch Siach }
1315930cb35SBaruch Siach
digicolor_uart_rx(struct uart_port * port)1325930cb35SBaruch Siach static void digicolor_uart_rx(struct uart_port *port)
1335930cb35SBaruch Siach {
1345930cb35SBaruch Siach unsigned long flags;
1355930cb35SBaruch Siach
1362d8a3178SThomas Gleixner uart_port_lock_irqsave(port, &flags);
1375930cb35SBaruch Siach
1385930cb35SBaruch Siach while (1) {
139fd2b55f8SJiri Slaby u8 status, ch, ch_flag;
1405930cb35SBaruch Siach
1415930cb35SBaruch Siach if (digicolor_uart_rx_empty(port))
1425930cb35SBaruch Siach break;
1435930cb35SBaruch Siach
1445930cb35SBaruch Siach ch = readb_relaxed(port->membase + UA_EMI_REC);
1455930cb35SBaruch Siach status = readb_relaxed(port->membase + UA_STATUS);
1465930cb35SBaruch Siach
1475930cb35SBaruch Siach port->icount.rx++;
1485930cb35SBaruch Siach ch_flag = TTY_NORMAL;
1495930cb35SBaruch Siach
1505930cb35SBaruch Siach if (status) {
1515930cb35SBaruch Siach if (status & UA_STATUS_PARITY_ERR)
1525930cb35SBaruch Siach port->icount.parity++;
1535930cb35SBaruch Siach else if (status & UA_STATUS_FRAME_ERR)
1545930cb35SBaruch Siach port->icount.frame++;
1555930cb35SBaruch Siach else if (status & UA_STATUS_OVERRUN_ERR)
1565930cb35SBaruch Siach port->icount.overrun++;
1575930cb35SBaruch Siach
1585930cb35SBaruch Siach status &= port->read_status_mask;
1595930cb35SBaruch Siach
1605930cb35SBaruch Siach if (status & UA_STATUS_PARITY_ERR)
1615930cb35SBaruch Siach ch_flag = TTY_PARITY;
1625930cb35SBaruch Siach else if (status & UA_STATUS_FRAME_ERR)
1635930cb35SBaruch Siach ch_flag = TTY_FRAME;
1645930cb35SBaruch Siach else if (status & UA_STATUS_OVERRUN_ERR)
1655930cb35SBaruch Siach ch_flag = TTY_OVERRUN;
1665930cb35SBaruch Siach }
1675930cb35SBaruch Siach
1685930cb35SBaruch Siach if (status & port->ignore_status_mask)
1695930cb35SBaruch Siach continue;
1705930cb35SBaruch Siach
1715930cb35SBaruch Siach uart_insert_char(port, status, UA_STATUS_OVERRUN_ERR, ch,
1725930cb35SBaruch Siach ch_flag);
1735930cb35SBaruch Siach }
1745930cb35SBaruch Siach
1752d8a3178SThomas Gleixner uart_port_unlock_irqrestore(port, flags);
1765930cb35SBaruch Siach
1775930cb35SBaruch Siach tty_flip_buffer_push(&port->state->port);
1785930cb35SBaruch Siach }
1795930cb35SBaruch Siach
digicolor_uart_tx(struct uart_port * port)1805930cb35SBaruch Siach static void digicolor_uart_tx(struct uart_port *port)
1815930cb35SBaruch Siach {
1821788cf6aSJiri Slaby (SUSE) struct tty_port *tport = &port->state->port;
1835930cb35SBaruch Siach unsigned long flags;
1841788cf6aSJiri Slaby (SUSE) unsigned char c;
1855930cb35SBaruch Siach
1865930cb35SBaruch Siach if (digicolor_uart_tx_full(port))
1875930cb35SBaruch Siach return;
1885930cb35SBaruch Siach
1892d8a3178SThomas Gleixner uart_port_lock_irqsave(port, &flags);
1905930cb35SBaruch Siach
1915930cb35SBaruch Siach if (port->x_char) {
1925930cb35SBaruch Siach writeb_relaxed(port->x_char, port->membase + UA_EMI_REC);
1935930cb35SBaruch Siach port->icount.tx++;
1945930cb35SBaruch Siach port->x_char = 0;
1955930cb35SBaruch Siach goto out;
1965930cb35SBaruch Siach }
1975930cb35SBaruch Siach
1981788cf6aSJiri Slaby (SUSE) if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
1995930cb35SBaruch Siach digicolor_uart_stop_tx(port);
2005930cb35SBaruch Siach goto out;
2015930cb35SBaruch Siach }
2025930cb35SBaruch Siach
2031788cf6aSJiri Slaby (SUSE) while (uart_fifo_get(port, &c)) {
2041788cf6aSJiri Slaby (SUSE) writeb(c, port->membase + UA_EMI_REC);
2055930cb35SBaruch Siach
2065930cb35SBaruch Siach if (digicolor_uart_tx_full(port))
2075930cb35SBaruch Siach break;
2085930cb35SBaruch Siach }
2095930cb35SBaruch Siach
2101788cf6aSJiri Slaby (SUSE) if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
2115930cb35SBaruch Siach uart_write_wakeup(port);
2125930cb35SBaruch Siach
2135930cb35SBaruch Siach out:
2142d8a3178SThomas Gleixner uart_port_unlock_irqrestore(port, flags);
2155930cb35SBaruch Siach }
2165930cb35SBaruch Siach
digicolor_uart_int(int irq,void * dev_id)2175930cb35SBaruch Siach static irqreturn_t digicolor_uart_int(int irq, void *dev_id)
2185930cb35SBaruch Siach {
2195930cb35SBaruch Siach struct uart_port *port = dev_id;
2205930cb35SBaruch Siach u8 int_status = readb_relaxed(port->membase + UA_INT_STATUS);
2215930cb35SBaruch Siach
2225930cb35SBaruch Siach writeb_relaxed(UA_INT_RX | UA_INT_TX,
2235930cb35SBaruch Siach port->membase + UA_INTFLAG_CLEAR);
2245930cb35SBaruch Siach
2255930cb35SBaruch Siach if (int_status & UA_INT_RX)
2265930cb35SBaruch Siach digicolor_uart_rx(port);
2275930cb35SBaruch Siach if (int_status & UA_INT_TX)
2285930cb35SBaruch Siach digicolor_uart_tx(port);
2295930cb35SBaruch Siach
2305930cb35SBaruch Siach return IRQ_HANDLED;
2315930cb35SBaruch Siach }
2325930cb35SBaruch Siach
digicolor_uart_tx_empty(struct uart_port * port)2335930cb35SBaruch Siach static unsigned int digicolor_uart_tx_empty(struct uart_port *port)
2345930cb35SBaruch Siach {
2355930cb35SBaruch Siach u8 status = readb_relaxed(port->membase + UA_STATUS);
2365930cb35SBaruch Siach
2375930cb35SBaruch Siach return (status & UA_STATUS_TX_READY) ? TIOCSER_TEMT : 0;
2385930cb35SBaruch Siach }
2395930cb35SBaruch Siach
digicolor_uart_get_mctrl(struct uart_port * port)2405930cb35SBaruch Siach static unsigned int digicolor_uart_get_mctrl(struct uart_port *port)
2415930cb35SBaruch Siach {
2425930cb35SBaruch Siach return TIOCM_CTS;
2435930cb35SBaruch Siach }
2445930cb35SBaruch Siach
digicolor_uart_set_mctrl(struct uart_port * port,unsigned int mctrl)2455930cb35SBaruch Siach static void digicolor_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
2465930cb35SBaruch Siach {
2475930cb35SBaruch Siach }
2485930cb35SBaruch Siach
digicolor_uart_break_ctl(struct uart_port * port,int state)2495930cb35SBaruch Siach static void digicolor_uart_break_ctl(struct uart_port *port, int state)
2505930cb35SBaruch Siach {
2515930cb35SBaruch Siach }
2525930cb35SBaruch Siach
digicolor_uart_startup(struct uart_port * port)2535930cb35SBaruch Siach static int digicolor_uart_startup(struct uart_port *port)
2545930cb35SBaruch Siach {
2555930cb35SBaruch Siach struct digicolor_port *dp =
2565930cb35SBaruch Siach container_of(port, struct digicolor_port, port);
2575930cb35SBaruch Siach
2585930cb35SBaruch Siach writeb_relaxed(UA_ENABLE_ENABLE, port->membase + UA_ENABLE);
2595930cb35SBaruch Siach writeb_relaxed(UA_CONTROL_SOFT_RESET, port->membase + UA_CONTROL);
2605930cb35SBaruch Siach writeb_relaxed(0, port->membase + UA_CONTROL);
2615930cb35SBaruch Siach
2625930cb35SBaruch Siach writeb_relaxed(UA_CONFIG_FIFO_RX_FIFO_MODE
2635930cb35SBaruch Siach | UA_CONFIG_FIFO_TX_FIFO_MODE | UA_CONFIG_FIFO_RX_THRESH,
2645930cb35SBaruch Siach port->membase + UA_CONFIG_FIFO);
2655930cb35SBaruch Siach writeb_relaxed(UA_STATUS_FIFO_RX_INT_ALMOST,
2665930cb35SBaruch Siach port->membase + UA_STATUS_FIFO);
2675930cb35SBaruch Siach writeb_relaxed(UA_CONTROL_RX_ENABLE | UA_CONTROL_TX_ENABLE,
2685930cb35SBaruch Siach port->membase + UA_CONTROL);
2695930cb35SBaruch Siach writeb_relaxed(UA_INT_TX | UA_INT_RX,
2705930cb35SBaruch Siach port->membase + UA_INT_ENABLE);
2715930cb35SBaruch Siach
2725930cb35SBaruch Siach schedule_delayed_work(&dp->rx_poll_work, msecs_to_jiffies(100));
2735930cb35SBaruch Siach
2745930cb35SBaruch Siach return 0;
2755930cb35SBaruch Siach }
2765930cb35SBaruch Siach
digicolor_uart_shutdown(struct uart_port * port)2775930cb35SBaruch Siach static void digicolor_uart_shutdown(struct uart_port *port)
2785930cb35SBaruch Siach {
2795930cb35SBaruch Siach struct digicolor_port *dp =
2805930cb35SBaruch Siach container_of(port, struct digicolor_port, port);
2815930cb35SBaruch Siach
2825930cb35SBaruch Siach writeb_relaxed(0, port->membase + UA_ENABLE);
2835930cb35SBaruch Siach cancel_delayed_work_sync(&dp->rx_poll_work);
2845930cb35SBaruch Siach }
2855930cb35SBaruch Siach
digicolor_uart_set_termios(struct uart_port * port,struct ktermios * termios,const struct ktermios * old)2865930cb35SBaruch Siach static void digicolor_uart_set_termios(struct uart_port *port,
2875930cb35SBaruch Siach struct ktermios *termios,
288bec5b814SIlpo Järvinen const struct ktermios *old)
2895930cb35SBaruch Siach {
2905930cb35SBaruch Siach unsigned int baud, divisor;
2915930cb35SBaruch Siach u8 config = 0;
2925930cb35SBaruch Siach unsigned long flags;
2935930cb35SBaruch Siach
2945930cb35SBaruch Siach /* Mask termios capabilities we don't support */
2955930cb35SBaruch Siach termios->c_cflag &= ~CMSPAR;
2965930cb35SBaruch Siach termios->c_iflag &= ~(BRKINT | IGNBRK);
2975930cb35SBaruch Siach
2985930cb35SBaruch Siach /* Limit baud rates so that we don't need the fractional divider */
2995930cb35SBaruch Siach baud = uart_get_baud_rate(port, termios, old,
3005930cb35SBaruch Siach port->uartclk / (0x10000*16),
3015930cb35SBaruch Siach port->uartclk / 256);
3025930cb35SBaruch Siach divisor = uart_get_divisor(port, baud) - 1;
3035930cb35SBaruch Siach
3045930cb35SBaruch Siach switch (termios->c_cflag & CSIZE) {
3055930cb35SBaruch Siach case CS7:
3065930cb35SBaruch Siach break;
3075930cb35SBaruch Siach case CS8:
3085930cb35SBaruch Siach default:
3095930cb35SBaruch Siach config |= UA_CONFIG_CHAR_LEN;
310fd63031bSIlpo Järvinen termios->c_cflag &= ~CSIZE;
311fd63031bSIlpo Järvinen termios->c_cflag |= CS8;
3125930cb35SBaruch Siach break;
3135930cb35SBaruch Siach }
3145930cb35SBaruch Siach
3155930cb35SBaruch Siach if (termios->c_cflag & CSTOPB)
3165930cb35SBaruch Siach config |= UA_CONFIG_STOP_BITS;
3175930cb35SBaruch Siach
3185930cb35SBaruch Siach if (termios->c_cflag & PARENB) {
3195930cb35SBaruch Siach config |= UA_CONFIG_PARITY;
3205930cb35SBaruch Siach if (termios->c_cflag & PARODD)
3215930cb35SBaruch Siach config |= UA_CONFIG_ODD_PARITY;
3225930cb35SBaruch Siach }
3235930cb35SBaruch Siach
3245930cb35SBaruch Siach /* Set read status mask */
3255930cb35SBaruch Siach port->read_status_mask = UA_STATUS_OVERRUN_ERR;
3265930cb35SBaruch Siach if (termios->c_iflag & INPCK)
3275930cb35SBaruch Siach port->read_status_mask |= UA_STATUS_PARITY_ERR
3285930cb35SBaruch Siach | UA_STATUS_FRAME_ERR;
3295930cb35SBaruch Siach
3305930cb35SBaruch Siach /* Set status ignore mask */
3315930cb35SBaruch Siach port->ignore_status_mask = 0;
3325930cb35SBaruch Siach if (!(termios->c_cflag & CREAD))
3335930cb35SBaruch Siach port->ignore_status_mask |= UA_STATUS_OVERRUN_ERR
3345930cb35SBaruch Siach | UA_STATUS_PARITY_ERR | UA_STATUS_FRAME_ERR;
3355930cb35SBaruch Siach
3362d8a3178SThomas Gleixner uart_port_lock_irqsave(port, &flags);
3375930cb35SBaruch Siach
3385930cb35SBaruch Siach uart_update_timeout(port, termios->c_cflag, baud);
3395930cb35SBaruch Siach
3405930cb35SBaruch Siach writeb_relaxed(config, port->membase + UA_CONFIG);
3415930cb35SBaruch Siach writeb_relaxed(divisor & 0xff, port->membase + UA_HBAUD_LO);
3425930cb35SBaruch Siach writeb_relaxed(divisor >> 8, port->membase + UA_HBAUD_HI);
3435930cb35SBaruch Siach
3442d8a3178SThomas Gleixner uart_port_unlock_irqrestore(port, flags);
3455930cb35SBaruch Siach }
3465930cb35SBaruch Siach
digicolor_uart_type(struct uart_port * port)3475930cb35SBaruch Siach static const char *digicolor_uart_type(struct uart_port *port)
3485930cb35SBaruch Siach {
3495930cb35SBaruch Siach return (port->type == PORT_DIGICOLOR) ? "DIGICOLOR USART" : NULL;
3505930cb35SBaruch Siach }
3515930cb35SBaruch Siach
digicolor_uart_config_port(struct uart_port * port,int flags)3525930cb35SBaruch Siach static void digicolor_uart_config_port(struct uart_port *port, int flags)
3535930cb35SBaruch Siach {
3545930cb35SBaruch Siach if (flags & UART_CONFIG_TYPE)
3555930cb35SBaruch Siach port->type = PORT_DIGICOLOR;
3565930cb35SBaruch Siach }
3575930cb35SBaruch Siach
digicolor_uart_release_port(struct uart_port * port)3585930cb35SBaruch Siach static void digicolor_uart_release_port(struct uart_port *port)
3595930cb35SBaruch Siach {
3605930cb35SBaruch Siach }
3615930cb35SBaruch Siach
digicolor_uart_request_port(struct uart_port * port)3625930cb35SBaruch Siach static int digicolor_uart_request_port(struct uart_port *port)
3635930cb35SBaruch Siach {
3645930cb35SBaruch Siach return 0;
3655930cb35SBaruch Siach }
3665930cb35SBaruch Siach
3675930cb35SBaruch Siach static const struct uart_ops digicolor_uart_ops = {
3685930cb35SBaruch Siach .tx_empty = digicolor_uart_tx_empty,
3695930cb35SBaruch Siach .set_mctrl = digicolor_uart_set_mctrl,
3705930cb35SBaruch Siach .get_mctrl = digicolor_uart_get_mctrl,
3715930cb35SBaruch Siach .stop_tx = digicolor_uart_stop_tx,
3725930cb35SBaruch Siach .start_tx = digicolor_uart_start_tx,
3735930cb35SBaruch Siach .stop_rx = digicolor_uart_stop_rx,
3745930cb35SBaruch Siach .break_ctl = digicolor_uart_break_ctl,
3755930cb35SBaruch Siach .startup = digicolor_uart_startup,
3765930cb35SBaruch Siach .shutdown = digicolor_uart_shutdown,
3775930cb35SBaruch Siach .set_termios = digicolor_uart_set_termios,
3785930cb35SBaruch Siach .type = digicolor_uart_type,
3795930cb35SBaruch Siach .config_port = digicolor_uart_config_port,
3805930cb35SBaruch Siach .release_port = digicolor_uart_release_port,
3815930cb35SBaruch Siach .request_port = digicolor_uart_request_port,
3825930cb35SBaruch Siach };
3835930cb35SBaruch Siach
digicolor_uart_console_putchar(struct uart_port * port,unsigned char ch)3843f8bab17SJiri Slaby static void digicolor_uart_console_putchar(struct uart_port *port, unsigned char ch)
3855930cb35SBaruch Siach {
3865930cb35SBaruch Siach while (digicolor_uart_tx_full(port))
3875930cb35SBaruch Siach cpu_relax();
3885930cb35SBaruch Siach
3895930cb35SBaruch Siach writeb_relaxed(ch, port->membase + UA_EMI_REC);
3905930cb35SBaruch Siach }
3915930cb35SBaruch Siach
digicolor_uart_console_write(struct console * co,const char * c,unsigned n)3925930cb35SBaruch Siach static void digicolor_uart_console_write(struct console *co, const char *c,
3935930cb35SBaruch Siach unsigned n)
3945930cb35SBaruch Siach {
3955930cb35SBaruch Siach struct uart_port *port = digicolor_ports[co->index];
3965930cb35SBaruch Siach u8 status;
3975930cb35SBaruch Siach unsigned long flags;
3985930cb35SBaruch Siach int locked = 1;
3995930cb35SBaruch Siach
400c0b18db0SBaruch Siach if (oops_in_progress)
4012d8a3178SThomas Gleixner locked = uart_port_trylock_irqsave(port, &flags);
4025930cb35SBaruch Siach else
4032d8a3178SThomas Gleixner uart_port_lock_irqsave(port, &flags);
4045930cb35SBaruch Siach
4055930cb35SBaruch Siach uart_console_write(port, c, n, digicolor_uart_console_putchar);
4065930cb35SBaruch Siach
4075930cb35SBaruch Siach if (locked)
4082d8a3178SThomas Gleixner uart_port_unlock_irqrestore(port, flags);
4095930cb35SBaruch Siach
4105930cb35SBaruch Siach /* Wait for transmitter to become empty */
4115930cb35SBaruch Siach do {
4125930cb35SBaruch Siach status = readb_relaxed(port->membase + UA_STATUS);
4135930cb35SBaruch Siach } while ((status & UA_STATUS_TX_READY) == 0);
4145930cb35SBaruch Siach }
4155930cb35SBaruch Siach
digicolor_uart_console_setup(struct console * co,char * options)4165930cb35SBaruch Siach static int digicolor_uart_console_setup(struct console *co, char *options)
4175930cb35SBaruch Siach {
4185930cb35SBaruch Siach int baud = 115200, bits = 8, parity = 'n', flow = 'n';
4195930cb35SBaruch Siach struct uart_port *port;
4205930cb35SBaruch Siach
4215930cb35SBaruch Siach if (co->index < 0 || co->index >= DIGICOLOR_USART_NR)
4225930cb35SBaruch Siach return -EINVAL;
4235930cb35SBaruch Siach
4245930cb35SBaruch Siach port = digicolor_ports[co->index];
4255930cb35SBaruch Siach if (!port)
4265930cb35SBaruch Siach return -ENODEV;
4275930cb35SBaruch Siach
4285930cb35SBaruch Siach if (options)
4295930cb35SBaruch Siach uart_parse_options(options, &baud, &parity, &bits, &flow);
4305930cb35SBaruch Siach
4315930cb35SBaruch Siach return uart_set_options(port, co, baud, parity, bits, flow);
4325930cb35SBaruch Siach }
4335930cb35SBaruch Siach
4345930cb35SBaruch Siach static struct console digicolor_console = {
4355930cb35SBaruch Siach .name = "ttyS",
4365930cb35SBaruch Siach .device = uart_console_device,
4375930cb35SBaruch Siach .write = digicolor_uart_console_write,
4385930cb35SBaruch Siach .setup = digicolor_uart_console_setup,
4395930cb35SBaruch Siach .flags = CON_PRINTBUFFER,
4405930cb35SBaruch Siach .index = -1,
4415930cb35SBaruch Siach };
4425930cb35SBaruch Siach
4435930cb35SBaruch Siach static struct uart_driver digicolor_uart = {
4445930cb35SBaruch Siach .driver_name = "digicolor-usart",
4455930cb35SBaruch Siach .dev_name = "ttyS",
4465930cb35SBaruch Siach .nr = DIGICOLOR_USART_NR,
4475930cb35SBaruch Siach };
4485930cb35SBaruch Siach
digicolor_uart_probe(struct platform_device * pdev)4495930cb35SBaruch Siach static int digicolor_uart_probe(struct platform_device *pdev)
4505930cb35SBaruch Siach {
4515930cb35SBaruch Siach struct device_node *np = pdev->dev.of_node;
452dd9b5588SGuenter Roeck int irq, ret, index;
4535930cb35SBaruch Siach struct digicolor_port *dp;
4545930cb35SBaruch Siach struct resource *res;
4555930cb35SBaruch Siach struct clk *uart_clk;
4565930cb35SBaruch Siach
4575930cb35SBaruch Siach if (!np) {
4585930cb35SBaruch Siach dev_err(&pdev->dev, "Missing device tree node\n");
4595930cb35SBaruch Siach return -ENXIO;
4605930cb35SBaruch Siach }
4615930cb35SBaruch Siach
4625930cb35SBaruch Siach index = of_alias_get_id(np, "serial");
4635930cb35SBaruch Siach if (index < 0 || index >= DIGICOLOR_USART_NR)
4645930cb35SBaruch Siach return -EINVAL;
4655930cb35SBaruch Siach
4665930cb35SBaruch Siach dp = devm_kzalloc(&pdev->dev, sizeof(*dp), GFP_KERNEL);
4675930cb35SBaruch Siach if (!dp)
4685930cb35SBaruch Siach return -ENOMEM;
4695930cb35SBaruch Siach
4705930cb35SBaruch Siach uart_clk = devm_clk_get(&pdev->dev, NULL);
4715930cb35SBaruch Siach if (IS_ERR(uart_clk))
4725930cb35SBaruch Siach return PTR_ERR(uart_clk);
4735930cb35SBaruch Siach
474447ee151SYang Yingliang dp->port.membase = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
4755930cb35SBaruch Siach if (IS_ERR(dp->port.membase))
4765930cb35SBaruch Siach return PTR_ERR(dp->port.membase);
477447ee151SYang Yingliang dp->port.mapbase = res->start;
4785930cb35SBaruch Siach
479dd9b5588SGuenter Roeck irq = platform_get_irq(pdev, 0);
480dd9b5588SGuenter Roeck if (irq < 0)
481dd9b5588SGuenter Roeck return irq;
482dd9b5588SGuenter Roeck dp->port.irq = irq;
4835930cb35SBaruch Siach
4845930cb35SBaruch Siach dp->port.iotype = UPIO_MEM;
4855930cb35SBaruch Siach dp->port.uartclk = clk_get_rate(uart_clk);
4865930cb35SBaruch Siach dp->port.fifosize = 16;
4875930cb35SBaruch Siach dp->port.dev = &pdev->dev;
4885930cb35SBaruch Siach dp->port.ops = &digicolor_uart_ops;
4895930cb35SBaruch Siach dp->port.line = index;
4905930cb35SBaruch Siach dp->port.type = PORT_DIGICOLOR;
4915930cb35SBaruch Siach spin_lock_init(&dp->port.lock);
4925930cb35SBaruch Siach
4935930cb35SBaruch Siach digicolor_ports[index] = &dp->port;
4945930cb35SBaruch Siach platform_set_drvdata(pdev, &dp->port);
4955930cb35SBaruch Siach
4965930cb35SBaruch Siach INIT_DELAYED_WORK(&dp->rx_poll_work, digicolor_rx_poll);
4975930cb35SBaruch Siach
4985930cb35SBaruch Siach ret = devm_request_irq(&pdev->dev, dp->port.irq, digicolor_uart_int, 0,
4995930cb35SBaruch Siach dev_name(&pdev->dev), &dp->port);
5005930cb35SBaruch Siach if (ret)
5015930cb35SBaruch Siach return ret;
5025930cb35SBaruch Siach
5035930cb35SBaruch Siach return uart_add_one_port(&digicolor_uart, &dp->port);
5045930cb35SBaruch Siach }
5055930cb35SBaruch Siach
digicolor_uart_remove(struct platform_device * pdev)506e9b09d9cSUwe Kleine-König static void digicolor_uart_remove(struct platform_device *pdev)
5075930cb35SBaruch Siach {
5085930cb35SBaruch Siach struct uart_port *port = platform_get_drvdata(pdev);
5095930cb35SBaruch Siach
5105930cb35SBaruch Siach uart_remove_one_port(&digicolor_uart, port);
5115930cb35SBaruch Siach }
5125930cb35SBaruch Siach
5135930cb35SBaruch Siach static const struct of_device_id digicolor_uart_dt_ids[] = {
5145930cb35SBaruch Siach { .compatible = "cnxt,cx92755-usart", },
5155930cb35SBaruch Siach { }
5165930cb35SBaruch Siach };
5175930cb35SBaruch Siach MODULE_DEVICE_TABLE(of, digicolor_uart_dt_ids);
5185930cb35SBaruch Siach
5195930cb35SBaruch Siach static struct platform_driver digicolor_uart_platform = {
5205930cb35SBaruch Siach .driver = {
5215930cb35SBaruch Siach .name = "digicolor-usart",
5225930cb35SBaruch Siach .of_match_table = of_match_ptr(digicolor_uart_dt_ids),
5235930cb35SBaruch Siach },
5245930cb35SBaruch Siach .probe = digicolor_uart_probe,
525*5cbb9b17SUwe Kleine-König .remove = digicolor_uart_remove,
5265930cb35SBaruch Siach };
5275930cb35SBaruch Siach
digicolor_uart_init(void)5285930cb35SBaruch Siach static int __init digicolor_uart_init(void)
5295930cb35SBaruch Siach {
5305930cb35SBaruch Siach int ret;
5315930cb35SBaruch Siach
5325930cb35SBaruch Siach if (IS_ENABLED(CONFIG_SERIAL_CONEXANT_DIGICOLOR_CONSOLE)) {
5335930cb35SBaruch Siach digicolor_uart.cons = &digicolor_console;
5345930cb35SBaruch Siach digicolor_console.data = &digicolor_uart;
5355930cb35SBaruch Siach }
5365930cb35SBaruch Siach
5375930cb35SBaruch Siach ret = uart_register_driver(&digicolor_uart);
5385930cb35SBaruch Siach if (ret)
5395930cb35SBaruch Siach return ret;
5405930cb35SBaruch Siach
541c7ad9ba0SKefeng Wang ret = platform_driver_register(&digicolor_uart_platform);
542c7ad9ba0SKefeng Wang if (ret)
543c7ad9ba0SKefeng Wang uart_unregister_driver(&digicolor_uart);
544c7ad9ba0SKefeng Wang
545c7ad9ba0SKefeng Wang return ret;
5465930cb35SBaruch Siach }
5475930cb35SBaruch Siach module_init(digicolor_uart_init);
5485930cb35SBaruch Siach
digicolor_uart_exit(void)5495930cb35SBaruch Siach static void __exit digicolor_uart_exit(void)
5505930cb35SBaruch Siach {
5515930cb35SBaruch Siach platform_driver_unregister(&digicolor_uart_platform);
5525930cb35SBaruch Siach uart_unregister_driver(&digicolor_uart);
5535930cb35SBaruch Siach }
5545930cb35SBaruch Siach module_exit(digicolor_uart_exit);
5555930cb35SBaruch Siach
5565930cb35SBaruch Siach MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
5575930cb35SBaruch Siach MODULE_DESCRIPTION("Conexant Digicolor USART serial driver");
5585930cb35SBaruch Siach MODULE_LICENSE("GPL");
559