1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
2ab4382d2SGreg Kroah-Hartman /*
3ab4382d2SGreg Kroah-Hartman * Driver for CLPS711x serial ports
4ab4382d2SGreg Kroah-Hartman *
5ab4382d2SGreg Kroah-Hartman * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
6ab4382d2SGreg Kroah-Hartman *
7ab4382d2SGreg Kroah-Hartman * Copyright 1999 ARM Limited
8ab4382d2SGreg Kroah-Hartman * Copyright (C) 2000 Deep Blue Solutions Ltd.
9ab4382d2SGreg Kroah-Hartman */
10ab4382d2SGreg Kroah-Hartman
11ab4382d2SGreg Kroah-Hartman #include <linux/module.h>
12ab4382d2SGreg Kroah-Hartman #include <linux/device.h>
13a1c25f2bSAlexander Shiyan #include <linux/console.h>
14ab4382d2SGreg Kroah-Hartman #include <linux/serial_core.h>
15ab4382d2SGreg Kroah-Hartman #include <linux/serial.h>
16c08f0153SAlexander Shiyan #include <linux/clk.h>
17bc000245SAlexander Shiyan #include <linux/io.h>
18a1c25f2bSAlexander Shiyan #include <linux/tty.h>
19a1c25f2bSAlexander Shiyan #include <linux/tty_flip.h>
20a1c25f2bSAlexander Shiyan #include <linux/ioport.h>
21bc000245SAlexander Shiyan #include <linux/of.h>
2295113728SAlexander Shiyan #include <linux/platform_device.h>
23bc000245SAlexander Shiyan #include <linux/regmap.h>
24ab4382d2SGreg Kroah-Hartman
25bc000245SAlexander Shiyan #include <linux/mfd/syscon.h>
26bc000245SAlexander Shiyan #include <linux/mfd/syscon/clps711x.h>
27ab4382d2SGreg Kroah-Hartman
2862b0a1b3SAlexander Shiyan #include "serial_mctrl_gpio.h"
2962b0a1b3SAlexander Shiyan
30bc000245SAlexander Shiyan #define UART_CLPS711X_DEVNAME "ttyCL"
31117d5d42SAlexander Shiyan #define UART_CLPS711X_NR 2
32117d5d42SAlexander Shiyan #define UART_CLPS711X_MAJOR 204
33117d5d42SAlexander Shiyan #define UART_CLPS711X_MINOR 40
3495113728SAlexander Shiyan
35bc000245SAlexander Shiyan #define UARTDR_OFFSET (0x00)
36bc000245SAlexander Shiyan #define UBRLCR_OFFSET (0x40)
37bc000245SAlexander Shiyan
38bc000245SAlexander Shiyan #define UARTDR_FRMERR (1 << 8)
39bc000245SAlexander Shiyan #define UARTDR_PARERR (1 << 9)
40bc000245SAlexander Shiyan #define UARTDR_OVERR (1 << 10)
41bc000245SAlexander Shiyan
42bc000245SAlexander Shiyan #define UBRLCR_BAUD_MASK ((1 << 12) - 1)
43bc000245SAlexander Shiyan #define UBRLCR_BREAK (1 << 12)
44bc000245SAlexander Shiyan #define UBRLCR_PRTEN (1 << 13)
45bc000245SAlexander Shiyan #define UBRLCR_EVENPRT (1 << 14)
46bc000245SAlexander Shiyan #define UBRLCR_XSTOP (1 << 15)
47bc000245SAlexander Shiyan #define UBRLCR_FIFOEN (1 << 16)
48bc000245SAlexander Shiyan #define UBRLCR_WRDLEN5 (0 << 17)
49bc000245SAlexander Shiyan #define UBRLCR_WRDLEN6 (1 << 17)
50bc000245SAlexander Shiyan #define UBRLCR_WRDLEN7 (2 << 17)
51bc000245SAlexander Shiyan #define UBRLCR_WRDLEN8 (3 << 17)
52bc000245SAlexander Shiyan #define UBRLCR_WRDLEN_MASK (3 << 17)
53ab4382d2SGreg Kroah-Hartman
54117d5d42SAlexander Shiyan struct clps711x_port {
55bc000245SAlexander Shiyan struct uart_port port;
56bc000245SAlexander Shiyan unsigned int tx_enabled;
57bc000245SAlexander Shiyan int rx_irq;
58bc000245SAlexander Shiyan struct regmap *syscon;
5962b0a1b3SAlexander Shiyan struct mctrl_gpios *gpios;
60bc000245SAlexander Shiyan };
61bc000245SAlexander Shiyan
62bc000245SAlexander Shiyan static struct uart_driver clps711x_uart = {
63bc000245SAlexander Shiyan .owner = THIS_MODULE,
64bc000245SAlexander Shiyan .driver_name = UART_CLPS711X_DEVNAME,
65bc000245SAlexander Shiyan .dev_name = UART_CLPS711X_DEVNAME,
66bc000245SAlexander Shiyan .major = UART_CLPS711X_MAJOR,
67bc000245SAlexander Shiyan .minor = UART_CLPS711X_MINOR,
68bc000245SAlexander Shiyan .nr = UART_CLPS711X_NR,
69117d5d42SAlexander Shiyan };
70117d5d42SAlexander Shiyan
uart_clps711x_stop_tx(struct uart_port * port)71a1c25f2bSAlexander Shiyan static void uart_clps711x_stop_tx(struct uart_port *port)
72ab4382d2SGreg Kroah-Hartman {
733c7e9eb1SAlexander Shiyan struct clps711x_port *s = dev_get_drvdata(port->dev);
743c7e9eb1SAlexander Shiyan
75bc000245SAlexander Shiyan if (s->tx_enabled) {
76bc000245SAlexander Shiyan disable_irq(port->irq);
77bc000245SAlexander Shiyan s->tx_enabled = 0;
78ab4382d2SGreg Kroah-Hartman }
79ab4382d2SGreg Kroah-Hartman }
80ab4382d2SGreg Kroah-Hartman
uart_clps711x_start_tx(struct uart_port * port)81a1c25f2bSAlexander Shiyan static void uart_clps711x_start_tx(struct uart_port *port)
82ab4382d2SGreg Kroah-Hartman {
833c7e9eb1SAlexander Shiyan struct clps711x_port *s = dev_get_drvdata(port->dev);
843c7e9eb1SAlexander Shiyan
85bc000245SAlexander Shiyan if (!s->tx_enabled) {
86bc000245SAlexander Shiyan s->tx_enabled = 1;
87bc000245SAlexander Shiyan enable_irq(port->irq);
88ab4382d2SGreg Kroah-Hartman }
89ab4382d2SGreg Kroah-Hartman }
90ab4382d2SGreg Kroah-Hartman
uart_clps711x_int_rx(int irq,void * dev_id)91135cc790SAlexander Shiyan static irqreturn_t uart_clps711x_int_rx(int irq, void *dev_id)
92ab4382d2SGreg Kroah-Hartman {
93ab4382d2SGreg Kroah-Hartman struct uart_port *port = dev_id;
94bc000245SAlexander Shiyan struct clps711x_port *s = dev_get_drvdata(port->dev);
95fd2b55f8SJiri Slaby unsigned int status;
96bc000245SAlexander Shiyan u16 ch;
97fd2b55f8SJiri Slaby u8 flg;
98ab4382d2SGreg Kroah-Hartman
99f27de95cSAlexander Shiyan for (;;) {
100093a9e2aSAlexander Shiyan u32 sysflg = 0;
101093a9e2aSAlexander Shiyan
102bc000245SAlexander Shiyan regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg);
103bc000245SAlexander Shiyan if (sysflg & SYSFLG_URXFE)
104f27de95cSAlexander Shiyan break;
105f27de95cSAlexander Shiyan
106093a9e2aSAlexander Shiyan ch = readw(port->membase + UARTDR_OFFSET);
107f27de95cSAlexander Shiyan status = ch & (UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR);
108f27de95cSAlexander Shiyan ch &= 0xff;
109ab4382d2SGreg Kroah-Hartman
110ab4382d2SGreg Kroah-Hartman port->icount.rx++;
111ab4382d2SGreg Kroah-Hartman flg = TTY_NORMAL;
112ab4382d2SGreg Kroah-Hartman
113f27de95cSAlexander Shiyan if (unlikely(status)) {
114f27de95cSAlexander Shiyan if (status & UARTDR_PARERR)
115ab4382d2SGreg Kroah-Hartman port->icount.parity++;
116f27de95cSAlexander Shiyan else if (status & UARTDR_FRMERR)
117ab4382d2SGreg Kroah-Hartman port->icount.frame++;
118f27de95cSAlexander Shiyan else if (status & UARTDR_OVERR)
119ab4382d2SGreg Kroah-Hartman port->icount.overrun++;
120ab4382d2SGreg Kroah-Hartman
121f27de95cSAlexander Shiyan status &= port->read_status_mask;
122ab4382d2SGreg Kroah-Hartman
123f27de95cSAlexander Shiyan if (status & UARTDR_PARERR)
124ab4382d2SGreg Kroah-Hartman flg = TTY_PARITY;
125f27de95cSAlexander Shiyan else if (status & UARTDR_FRMERR)
126ab4382d2SGreg Kroah-Hartman flg = TTY_FRAME;
127f27de95cSAlexander Shiyan else if (status & UARTDR_OVERR)
128f27de95cSAlexander Shiyan flg = TTY_OVERRUN;
129ab4382d2SGreg Kroah-Hartman }
130ab4382d2SGreg Kroah-Hartman
131ab4382d2SGreg Kroah-Hartman if (uart_handle_sysrq_char(port, ch))
132f27de95cSAlexander Shiyan continue;
133ab4382d2SGreg Kroah-Hartman
134f27de95cSAlexander Shiyan if (status & port->ignore_status_mask)
135f27de95cSAlexander Shiyan continue;
136ab4382d2SGreg Kroah-Hartman
137f27de95cSAlexander Shiyan uart_insert_char(port, status, UARTDR_OVERR, ch, flg);
138ab4382d2SGreg Kroah-Hartman }
139f27de95cSAlexander Shiyan
1402e124b4aSJiri Slaby tty_flip_buffer_push(&port->state->port);
141f27de95cSAlexander Shiyan
142ab4382d2SGreg Kroah-Hartman return IRQ_HANDLED;
143ab4382d2SGreg Kroah-Hartman }
144ab4382d2SGreg Kroah-Hartman
uart_clps711x_int_tx(int irq,void * dev_id)145135cc790SAlexander Shiyan static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id)
146ab4382d2SGreg Kroah-Hartman {
147ab4382d2SGreg Kroah-Hartman struct uart_port *port = dev_id;
1483c7e9eb1SAlexander Shiyan struct clps711x_port *s = dev_get_drvdata(port->dev);
149ab4382d2SGreg Kroah-Hartman struct circ_buf *xmit = &port->state->xmit;
150ab4382d2SGreg Kroah-Hartman
151ab4382d2SGreg Kroah-Hartman if (port->x_char) {
152093a9e2aSAlexander Shiyan writew(port->x_char, port->membase + UARTDR_OFFSET);
153ab4382d2SGreg Kroah-Hartman port->icount.tx++;
154ab4382d2SGreg Kroah-Hartman port->x_char = 0;
155ab4382d2SGreg Kroah-Hartman return IRQ_HANDLED;
156ab4382d2SGreg Kroah-Hartman }
1577a6fbc9aSAlexander Shiyan
1583c7e9eb1SAlexander Shiyan if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
159bc000245SAlexander Shiyan if (s->tx_enabled) {
160bc000245SAlexander Shiyan disable_irq_nosync(port->irq);
161bc000245SAlexander Shiyan s->tx_enabled = 0;
162bc000245SAlexander Shiyan }
1633c7e9eb1SAlexander Shiyan return IRQ_HANDLED;
1643c7e9eb1SAlexander Shiyan }
165ab4382d2SGreg Kroah-Hartman
166cf03a884SAlexander Shiyan while (!uart_circ_empty(xmit)) {
167093a9e2aSAlexander Shiyan u32 sysflg = 0;
168093a9e2aSAlexander Shiyan
169093a9e2aSAlexander Shiyan writew(xmit->buf[xmit->tail], port->membase + UARTDR_OFFSET);
1704146765cSIlpo Järvinen uart_xmit_advance(port, 1);
171bc000245SAlexander Shiyan
172bc000245SAlexander Shiyan regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg);
173bc000245SAlexander Shiyan if (sysflg & SYSFLG_UTXFF)
174ab4382d2SGreg Kroah-Hartman break;
175cf03a884SAlexander Shiyan }
176ab4382d2SGreg Kroah-Hartman
177ab4382d2SGreg Kroah-Hartman if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
178ab4382d2SGreg Kroah-Hartman uart_write_wakeup(port);
179ab4382d2SGreg Kroah-Hartman
180ab4382d2SGreg Kroah-Hartman return IRQ_HANDLED;
181ab4382d2SGreg Kroah-Hartman }
182ab4382d2SGreg Kroah-Hartman
uart_clps711x_tx_empty(struct uart_port * port)183a1c25f2bSAlexander Shiyan static unsigned int uart_clps711x_tx_empty(struct uart_port *port)
184ab4382d2SGreg Kroah-Hartman {
185bc000245SAlexander Shiyan struct clps711x_port *s = dev_get_drvdata(port->dev);
186093a9e2aSAlexander Shiyan u32 sysflg = 0;
187bc000245SAlexander Shiyan
188bc000245SAlexander Shiyan regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg);
189bc000245SAlexander Shiyan
190bc000245SAlexander Shiyan return (sysflg & SYSFLG_UBUSY) ? 0 : TIOCSER_TEMT;
191ab4382d2SGreg Kroah-Hartman }
192ab4382d2SGreg Kroah-Hartman
uart_clps711x_get_mctrl(struct uart_port * port)193a1c25f2bSAlexander Shiyan static unsigned int uart_clps711x_get_mctrl(struct uart_port *port)
194ab4382d2SGreg Kroah-Hartman {
19562b0a1b3SAlexander Shiyan unsigned int result = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR;
196bc000245SAlexander Shiyan struct clps711x_port *s = dev_get_drvdata(port->dev);
197ab4382d2SGreg Kroah-Hartman
19862b0a1b3SAlexander Shiyan return mctrl_gpio_get(s->gpios, &result);
199ab4382d2SGreg Kroah-Hartman }
200ab4382d2SGreg Kroah-Hartman
uart_clps711x_set_mctrl(struct uart_port * port,unsigned int mctrl)201a1c25f2bSAlexander Shiyan static void uart_clps711x_set_mctrl(struct uart_port *port, unsigned int mctrl)
202ab4382d2SGreg Kroah-Hartman {
20362b0a1b3SAlexander Shiyan struct clps711x_port *s = dev_get_drvdata(port->dev);
20462b0a1b3SAlexander Shiyan
20562b0a1b3SAlexander Shiyan mctrl_gpio_set(s->gpios, mctrl);
206ab4382d2SGreg Kroah-Hartman }
207ab4382d2SGreg Kroah-Hartman
uart_clps711x_break_ctl(struct uart_port * port,int break_state)208a1c25f2bSAlexander Shiyan static void uart_clps711x_break_ctl(struct uart_port *port, int break_state)
209ab4382d2SGreg Kroah-Hartman {
210ab4382d2SGreg Kroah-Hartman unsigned int ubrlcr;
211ab4382d2SGreg Kroah-Hartman
212093a9e2aSAlexander Shiyan ubrlcr = readl(port->membase + UBRLCR_OFFSET);
213ec335526SAlexander Shiyan if (break_state)
214ab4382d2SGreg Kroah-Hartman ubrlcr |= UBRLCR_BREAK;
215ab4382d2SGreg Kroah-Hartman else
216ab4382d2SGreg Kroah-Hartman ubrlcr &= ~UBRLCR_BREAK;
217093a9e2aSAlexander Shiyan writel(ubrlcr, port->membase + UBRLCR_OFFSET);
218ab4382d2SGreg Kroah-Hartman }
219ab4382d2SGreg Kroah-Hartman
uart_clps711x_set_ldisc(struct uart_port * port,struct ktermios * termios)220732a84a0SPeter Hurley static void uart_clps711x_set_ldisc(struct uart_port *port,
221732a84a0SPeter Hurley struct ktermios *termios)
22271b9e8c6SAlexander Shiyan {
22371b9e8c6SAlexander Shiyan if (!port->line) {
22471b9e8c6SAlexander Shiyan struct clps711x_port *s = dev_get_drvdata(port->dev);
22571b9e8c6SAlexander Shiyan
22671b9e8c6SAlexander Shiyan regmap_update_bits(s->syscon, SYSCON_OFFSET, SYSCON1_SIREN,
227732a84a0SPeter Hurley (termios->c_line == N_IRDA) ? SYSCON1_SIREN : 0);
22871b9e8c6SAlexander Shiyan }
22971b9e8c6SAlexander Shiyan }
23071b9e8c6SAlexander Shiyan
uart_clps711x_startup(struct uart_port * port)231a1c25f2bSAlexander Shiyan static int uart_clps711x_startup(struct uart_port *port)
232ab4382d2SGreg Kroah-Hartman {
2333c7e9eb1SAlexander Shiyan struct clps711x_port *s = dev_get_drvdata(port->dev);
234ab4382d2SGreg Kroah-Hartman
235f52ede2aSAlexander Shiyan /* Disable break */
236093a9e2aSAlexander Shiyan writel(readl(port->membase + UBRLCR_OFFSET) & ~UBRLCR_BREAK,
237093a9e2aSAlexander Shiyan port->membase + UBRLCR_OFFSET);
238f52ede2aSAlexander Shiyan
239f52ede2aSAlexander Shiyan /* Enable the port */
240bc000245SAlexander Shiyan return regmap_update_bits(s->syscon, SYSCON_OFFSET,
241bc000245SAlexander Shiyan SYSCON_UARTEN, SYSCON_UARTEN);
242ab4382d2SGreg Kroah-Hartman }
243ab4382d2SGreg Kroah-Hartman
uart_clps711x_shutdown(struct uart_port * port)244a1c25f2bSAlexander Shiyan static void uart_clps711x_shutdown(struct uart_port *port)
245ab4382d2SGreg Kroah-Hartman {
246bc000245SAlexander Shiyan struct clps711x_port *s = dev_get_drvdata(port->dev);
247ab4382d2SGreg Kroah-Hartman
248f52ede2aSAlexander Shiyan /* Disable the port */
249bc000245SAlexander Shiyan regmap_update_bits(s->syscon, SYSCON_OFFSET, SYSCON_UARTEN, 0);
250ab4382d2SGreg Kroah-Hartman }
251ab4382d2SGreg Kroah-Hartman
uart_clps711x_set_termios(struct uart_port * port,struct ktermios * termios,const struct ktermios * old)252a1c25f2bSAlexander Shiyan static void uart_clps711x_set_termios(struct uart_port *port,
253a1c25f2bSAlexander Shiyan struct ktermios *termios,
254bec5b814SIlpo Järvinen const struct ktermios *old)
255ab4382d2SGreg Kroah-Hartman {
256bc000245SAlexander Shiyan u32 ubrlcr;
257bc000245SAlexander Shiyan unsigned int baud, quot;
258ab4382d2SGreg Kroah-Hartman
2597ae75e94SAlexander Shiyan /* Mask termios capabilities we don't support */
2607ae75e94SAlexander Shiyan termios->c_cflag &= ~CMSPAR;
2617ae75e94SAlexander Shiyan termios->c_iflag &= ~(BRKINT | IGNBRK);
262ab4382d2SGreg Kroah-Hartman
263c08f0153SAlexander Shiyan /* Ask the core to calculate the divisor for us */
264c08f0153SAlexander Shiyan baud = uart_get_baud_rate(port, termios, old, port->uartclk / 4096,
265c08f0153SAlexander Shiyan port->uartclk / 16);
266ab4382d2SGreg Kroah-Hartman quot = uart_get_divisor(port, baud);
267ab4382d2SGreg Kroah-Hartman
268ab4382d2SGreg Kroah-Hartman switch (termios->c_cflag & CSIZE) {
269ab4382d2SGreg Kroah-Hartman case CS5:
270ab4382d2SGreg Kroah-Hartman ubrlcr = UBRLCR_WRDLEN5;
271ab4382d2SGreg Kroah-Hartman break;
272ab4382d2SGreg Kroah-Hartman case CS6:
273ab4382d2SGreg Kroah-Hartman ubrlcr = UBRLCR_WRDLEN6;
274ab4382d2SGreg Kroah-Hartman break;
275ab4382d2SGreg Kroah-Hartman case CS7:
276ab4382d2SGreg Kroah-Hartman ubrlcr = UBRLCR_WRDLEN7;
277ab4382d2SGreg Kroah-Hartman break;
278a1c25f2bSAlexander Shiyan case CS8:
279a1c25f2bSAlexander Shiyan default:
280ab4382d2SGreg Kroah-Hartman ubrlcr = UBRLCR_WRDLEN8;
281ab4382d2SGreg Kroah-Hartman break;
282ab4382d2SGreg Kroah-Hartman }
2837ae75e94SAlexander Shiyan
284ab4382d2SGreg Kroah-Hartman if (termios->c_cflag & CSTOPB)
285ab4382d2SGreg Kroah-Hartman ubrlcr |= UBRLCR_XSTOP;
2867ae75e94SAlexander Shiyan
287ab4382d2SGreg Kroah-Hartman if (termios->c_cflag & PARENB) {
288ab4382d2SGreg Kroah-Hartman ubrlcr |= UBRLCR_PRTEN;
289ab4382d2SGreg Kroah-Hartman if (!(termios->c_cflag & PARODD))
290ab4382d2SGreg Kroah-Hartman ubrlcr |= UBRLCR_EVENPRT;
291ab4382d2SGreg Kroah-Hartman }
292cf03a884SAlexander Shiyan
293cf03a884SAlexander Shiyan /* Enable FIFO */
294ab4382d2SGreg Kroah-Hartman ubrlcr |= UBRLCR_FIFOEN;
295ab4382d2SGreg Kroah-Hartman
2967ae75e94SAlexander Shiyan /* Set read status mask */
297ab4382d2SGreg Kroah-Hartman port->read_status_mask = UARTDR_OVERR;
298ab4382d2SGreg Kroah-Hartman if (termios->c_iflag & INPCK)
299ab4382d2SGreg Kroah-Hartman port->read_status_mask |= UARTDR_PARERR | UARTDR_FRMERR;
300ab4382d2SGreg Kroah-Hartman
3017ae75e94SAlexander Shiyan /* Set status ignore mask */
302ab4382d2SGreg Kroah-Hartman port->ignore_status_mask = 0;
3037ae75e94SAlexander Shiyan if (!(termios->c_cflag & CREAD))
3047ae75e94SAlexander Shiyan port->ignore_status_mask |= UARTDR_OVERR | UARTDR_PARERR |
3057ae75e94SAlexander Shiyan UARTDR_FRMERR;
306ab4382d2SGreg Kroah-Hartman
3077ae75e94SAlexander Shiyan uart_update_timeout(port, termios->c_cflag, baud);
308ab4382d2SGreg Kroah-Hartman
309093a9e2aSAlexander Shiyan writel(ubrlcr | (quot - 1), port->membase + UBRLCR_OFFSET);
310ab4382d2SGreg Kroah-Hartman }
311ab4382d2SGreg Kroah-Hartman
uart_clps711x_type(struct uart_port * port)312a1c25f2bSAlexander Shiyan static const char *uart_clps711x_type(struct uart_port *port)
313ab4382d2SGreg Kroah-Hartman {
314a1c25f2bSAlexander Shiyan return (port->type == PORT_CLPS711X) ? "CLPS711X" : NULL;
315ab4382d2SGreg Kroah-Hartman }
316ab4382d2SGreg Kroah-Hartman
uart_clps711x_config_port(struct uart_port * port,int flags)317a1c25f2bSAlexander Shiyan static void uart_clps711x_config_port(struct uart_port *port, int flags)
318ab4382d2SGreg Kroah-Hartman {
319ab4382d2SGreg Kroah-Hartman if (flags & UART_CONFIG_TYPE)
320ab4382d2SGreg Kroah-Hartman port->type = PORT_CLPS711X;
321ab4382d2SGreg Kroah-Hartman }
322ab4382d2SGreg Kroah-Hartman
uart_clps711x_nop_void(struct uart_port * port)323bc000245SAlexander Shiyan static void uart_clps711x_nop_void(struct uart_port *port)
324ab4382d2SGreg Kroah-Hartman {
325ab4382d2SGreg Kroah-Hartman }
326ab4382d2SGreg Kroah-Hartman
uart_clps711x_nop_int(struct uart_port * port)327bc000245SAlexander Shiyan static int uart_clps711x_nop_int(struct uart_port *port)
328ab4382d2SGreg Kroah-Hartman {
329ab4382d2SGreg Kroah-Hartman return 0;
330ab4382d2SGreg Kroah-Hartman }
331ab4382d2SGreg Kroah-Hartman
332a1c25f2bSAlexander Shiyan static const struct uart_ops uart_clps711x_ops = {
333a1c25f2bSAlexander Shiyan .tx_empty = uart_clps711x_tx_empty,
334a1c25f2bSAlexander Shiyan .set_mctrl = uart_clps711x_set_mctrl,
335a1c25f2bSAlexander Shiyan .get_mctrl = uart_clps711x_get_mctrl,
336a1c25f2bSAlexander Shiyan .stop_tx = uart_clps711x_stop_tx,
337a1c25f2bSAlexander Shiyan .start_tx = uart_clps711x_start_tx,
338bc000245SAlexander Shiyan .stop_rx = uart_clps711x_nop_void,
339a1c25f2bSAlexander Shiyan .break_ctl = uart_clps711x_break_ctl,
34071b9e8c6SAlexander Shiyan .set_ldisc = uart_clps711x_set_ldisc,
341a1c25f2bSAlexander Shiyan .startup = uart_clps711x_startup,
342a1c25f2bSAlexander Shiyan .shutdown = uart_clps711x_shutdown,
343a1c25f2bSAlexander Shiyan .set_termios = uart_clps711x_set_termios,
344a1c25f2bSAlexander Shiyan .type = uart_clps711x_type,
345a1c25f2bSAlexander Shiyan .config_port = uart_clps711x_config_port,
346bc000245SAlexander Shiyan .release_port = uart_clps711x_nop_void,
347bc000245SAlexander Shiyan .request_port = uart_clps711x_nop_int,
348ab4382d2SGreg Kroah-Hartman };
349ab4382d2SGreg Kroah-Hartman
350ab4382d2SGreg Kroah-Hartman #ifdef CONFIG_SERIAL_CLPS711X_CONSOLE
uart_clps711x_console_putchar(struct uart_port * port,unsigned char ch)3513f8bab17SJiri Slaby static void uart_clps711x_console_putchar(struct uart_port *port, unsigned char ch)
352ab4382d2SGreg Kroah-Hartman {
353bc000245SAlexander Shiyan struct clps711x_port *s = dev_get_drvdata(port->dev);
354093a9e2aSAlexander Shiyan u32 sysflg = 0;
355117d5d42SAlexander Shiyan
3562f310b8eSAlexander Shiyan /* Wait for FIFO is not full */
3572f310b8eSAlexander Shiyan do {
358bc000245SAlexander Shiyan regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg);
3592f310b8eSAlexander Shiyan } while (sysflg & SYSFLG_UTXFF);
360bc000245SAlexander Shiyan
361093a9e2aSAlexander Shiyan writew(ch, port->membase + UARTDR_OFFSET);
362ab4382d2SGreg Kroah-Hartman }
363ab4382d2SGreg Kroah-Hartman
uart_clps711x_console_write(struct console * co,const char * c,unsigned n)364117d5d42SAlexander Shiyan static void uart_clps711x_console_write(struct console *co, const char *c,
365117d5d42SAlexander Shiyan unsigned n)
366ab4382d2SGreg Kroah-Hartman {
367bc000245SAlexander Shiyan struct uart_port *port = clps711x_uart.state[co->index].uart_port;
368bc000245SAlexander Shiyan struct clps711x_port *s = dev_get_drvdata(port->dev);
3692f310b8eSAlexander Shiyan u32 sysflg = 0;
370ab4382d2SGreg Kroah-Hartman
371117d5d42SAlexander Shiyan uart_console_write(port, c, n, uart_clps711x_console_putchar);
372ab4382d2SGreg Kroah-Hartman
373117d5d42SAlexander Shiyan /* Wait for transmitter to become empty */
3742f310b8eSAlexander Shiyan do {
375bc000245SAlexander Shiyan regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg);
3762f310b8eSAlexander Shiyan } while (sysflg & SYSFLG_UBUSY);
377ab4382d2SGreg Kroah-Hartman }
378ab4382d2SGreg Kroah-Hartman
uart_clps711x_console_setup(struct console * co,char * options)379117d5d42SAlexander Shiyan static int uart_clps711x_console_setup(struct console *co, char *options)
380ab4382d2SGreg Kroah-Hartman {
381117d5d42SAlexander Shiyan int baud = 38400, bits = 8, parity = 'n', flow = 'n';
382bc000245SAlexander Shiyan int ret, index = co->index;
383bc000245SAlexander Shiyan struct clps711x_port *s;
384bc000245SAlexander Shiyan struct uart_port *port;
385bc000245SAlexander Shiyan unsigned int quot;
386093a9e2aSAlexander Shiyan u32 ubrlcr;
387ab4382d2SGreg Kroah-Hartman
388bc000245SAlexander Shiyan if (index < 0 || index >= UART_CLPS711X_NR)
389bc000245SAlexander Shiyan return -EINVAL;
390bc000245SAlexander Shiyan
391bc000245SAlexander Shiyan port = clps711x_uart.state[index].uart_port;
392bc000245SAlexander Shiyan if (!port)
393bc000245SAlexander Shiyan return -ENODEV;
394bc000245SAlexander Shiyan
395bc000245SAlexander Shiyan s = dev_get_drvdata(port->dev);
396bc000245SAlexander Shiyan
397bc000245SAlexander Shiyan if (!options) {
398093a9e2aSAlexander Shiyan u32 syscon = 0;
399093a9e2aSAlexander Shiyan
400bc000245SAlexander Shiyan regmap_read(s->syscon, SYSCON_OFFSET, &syscon);
401bc000245SAlexander Shiyan if (syscon & SYSCON_UARTEN) {
402093a9e2aSAlexander Shiyan ubrlcr = readl(port->membase + UBRLCR_OFFSET);
403bc000245SAlexander Shiyan
404bc000245SAlexander Shiyan if (ubrlcr & UBRLCR_PRTEN) {
405bc000245SAlexander Shiyan if (ubrlcr & UBRLCR_EVENPRT)
406bc000245SAlexander Shiyan parity = 'e';
407ab4382d2SGreg Kroah-Hartman else
408bc000245SAlexander Shiyan parity = 'o';
409ab4382d2SGreg Kroah-Hartman }
410bc000245SAlexander Shiyan
411bc000245SAlexander Shiyan if ((ubrlcr & UBRLCR_WRDLEN_MASK) == UBRLCR_WRDLEN7)
412bc000245SAlexander Shiyan bits = 7;
413bc000245SAlexander Shiyan
414bc000245SAlexander Shiyan quot = ubrlcr & UBRLCR_BAUD_MASK;
415bc000245SAlexander Shiyan baud = port->uartclk / (16 * (quot + 1));
416bc000245SAlexander Shiyan }
417bc000245SAlexander Shiyan } else
418bc000245SAlexander Shiyan uart_parse_options(options, &baud, &parity, &bits, &flow);
419bc000245SAlexander Shiyan
420bc000245SAlexander Shiyan ret = uart_set_options(port, co, baud, parity, bits, flow);
421bc000245SAlexander Shiyan if (ret)
422bc000245SAlexander Shiyan return ret;
423bc000245SAlexander Shiyan
424bc000245SAlexander Shiyan return regmap_update_bits(s->syscon, SYSCON_OFFSET,
425bc000245SAlexander Shiyan SYSCON_UARTEN, SYSCON_UARTEN);
426bc000245SAlexander Shiyan }
427bc000245SAlexander Shiyan
428bc000245SAlexander Shiyan static struct console clps711x_console = {
429bc000245SAlexander Shiyan .name = UART_CLPS711X_DEVNAME,
430bc000245SAlexander Shiyan .device = uart_console_device,
431bc000245SAlexander Shiyan .write = uart_clps711x_console_write,
432bc000245SAlexander Shiyan .setup = uart_clps711x_console_setup,
433bc000245SAlexander Shiyan .flags = CON_PRINTBUFFER,
434bc000245SAlexander Shiyan .index = -1,
435bc000245SAlexander Shiyan };
436ab4382d2SGreg Kroah-Hartman #endif
437ab4382d2SGreg Kroah-Hartman
uart_clps711x_probe(struct platform_device * pdev)4389671f099SBill Pemberton static int uart_clps711x_probe(struct platform_device *pdev)
439ab4382d2SGreg Kroah-Hartman {
440bc000245SAlexander Shiyan struct device_node *np = pdev->dev.of_node;
441117d5d42SAlexander Shiyan struct clps711x_port *s;
442bc000245SAlexander Shiyan struct resource *res;
443bc000245SAlexander Shiyan struct clk *uart_clk;
444db4a6cbfSAlexander Shiyan int irq, ret;
445bc000245SAlexander Shiyan
446bc000245SAlexander Shiyan s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL);
447bc000245SAlexander Shiyan if (!s)
448117d5d42SAlexander Shiyan return -ENOMEM;
449bc000245SAlexander Shiyan
450bc000245SAlexander Shiyan uart_clk = devm_clk_get(&pdev->dev, NULL);
451bc000245SAlexander Shiyan if (IS_ERR(uart_clk))
452bc000245SAlexander Shiyan return PTR_ERR(uart_clk);
453bc000245SAlexander Shiyan
454*0bb60bdaSYangtao Li s->port.membase = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
455bc000245SAlexander Shiyan if (IS_ERR(s->port.membase))
456bc000245SAlexander Shiyan return PTR_ERR(s->port.membase);
457bc000245SAlexander Shiyan
4588f5405c9SGuenter Roeck irq = platform_get_irq(pdev, 0);
4598f5405c9SGuenter Roeck if (irq < 0)
4608f5405c9SGuenter Roeck return irq;
4618f5405c9SGuenter Roeck s->port.irq = irq;
462bc000245SAlexander Shiyan
463bc000245SAlexander Shiyan s->rx_irq = platform_get_irq(pdev, 1);
4648f5405c9SGuenter Roeck if (s->rx_irq < 0)
465bc000245SAlexander Shiyan return s->rx_irq;
466bc000245SAlexander Shiyan
467bc000245SAlexander Shiyan s->syscon = syscon_regmap_lookup_by_phandle(np, "syscon");
468bc000245SAlexander Shiyan if (IS_ERR(s->syscon))
469bc000245SAlexander Shiyan return PTR_ERR(s->syscon);
470bc000245SAlexander Shiyan
471db4a6cbfSAlexander Shiyan s->port.line = of_alias_get_id(np, "serial");
472bc000245SAlexander Shiyan s->port.dev = &pdev->dev;
473bc000245SAlexander Shiyan s->port.iotype = UPIO_MEM32;
474bc000245SAlexander Shiyan s->port.mapbase = res->start;
475bc000245SAlexander Shiyan s->port.type = PORT_CLPS711X;
476bc000245SAlexander Shiyan s->port.fifosize = 16;
47776f82db9SDmitry Safonov s->port.has_sysrq = IS_ENABLED(CONFIG_SERIAL_CLPS711X_CONSOLE);
478bc000245SAlexander Shiyan s->port.flags = UPF_SKIP_TEST | UPF_FIXED_TYPE;
479bc000245SAlexander Shiyan s->port.uartclk = clk_get_rate(uart_clk);
480bc000245SAlexander Shiyan s->port.ops = &uart_clps711x_ops;
481bc000245SAlexander Shiyan
482117d5d42SAlexander Shiyan platform_set_drvdata(pdev, s);
483ab4382d2SGreg Kroah-Hartman
4847d8c70d8SUwe Kleine-König s->gpios = mctrl_gpio_init_noauto(&pdev->dev, 0);
485f059a455SUwe Kleine-König if (IS_ERR(s->gpios))
486f059a455SUwe Kleine-König return PTR_ERR(s->gpios);
48762b0a1b3SAlexander Shiyan
488bc000245SAlexander Shiyan ret = uart_add_one_port(&clps711x_uart, &s->port);
489bc000245SAlexander Shiyan if (ret)
490bc000245SAlexander Shiyan return ret;
491c08f0153SAlexander Shiyan
492bc000245SAlexander Shiyan /* Disable port */
493bc000245SAlexander Shiyan if (!uart_console(&s->port))
494bc000245SAlexander Shiyan regmap_update_bits(s->syscon, SYSCON_OFFSET, SYSCON_UARTEN, 0);
495bc000245SAlexander Shiyan
496bc000245SAlexander Shiyan s->tx_enabled = 1;
497bc000245SAlexander Shiyan
498bc000245SAlexander Shiyan ret = devm_request_irq(&pdev->dev, s->port.irq, uart_clps711x_int_tx, 0,
499bc000245SAlexander Shiyan dev_name(&pdev->dev), &s->port);
500117d5d42SAlexander Shiyan if (ret) {
501bc000245SAlexander Shiyan uart_remove_one_port(&clps711x_uart, &s->port);
50243b829b3SJingoo Han return ret;
503117d5d42SAlexander Shiyan }
504ab4382d2SGreg Kroah-Hartman
505bc000245SAlexander Shiyan ret = devm_request_irq(&pdev->dev, s->rx_irq, uart_clps711x_int_rx, 0,
506bc000245SAlexander Shiyan dev_name(&pdev->dev), &s->port);
507bc000245SAlexander Shiyan if (ret)
508bc000245SAlexander Shiyan uart_remove_one_port(&clps711x_uart, &s->port);
509ab4382d2SGreg Kroah-Hartman
510bc000245SAlexander Shiyan return ret;
511ab4382d2SGreg Kroah-Hartman }
512ab4382d2SGreg Kroah-Hartman
uart_clps711x_remove(struct platform_device * pdev)513ae8d8a14SBill Pemberton static int uart_clps711x_remove(struct platform_device *pdev)
514ab4382d2SGreg Kroah-Hartman {
515117d5d42SAlexander Shiyan struct clps711x_port *s = platform_get_drvdata(pdev);
516ab4382d2SGreg Kroah-Hartman
517d5b3d02dSUwe Kleine-König uart_remove_one_port(&clps711x_uart, &s->port);
518d5b3d02dSUwe Kleine-König
519d5b3d02dSUwe Kleine-König return 0;
520ab4382d2SGreg Kroah-Hartman }
521ab4382d2SGreg Kroah-Hartman
522bc000245SAlexander Shiyan static const struct of_device_id __maybe_unused clps711x_uart_dt_ids[] = {
523d305345cSAlexander Shiyan { .compatible = "cirrus,ep7209-uart", },
524bc000245SAlexander Shiyan { }
525bc000245SAlexander Shiyan };
526bc000245SAlexander Shiyan MODULE_DEVICE_TABLE(of, clps711x_uart_dt_ids);
527bc000245SAlexander Shiyan
528bc000245SAlexander Shiyan static struct platform_driver clps711x_uart_platform = {
52995113728SAlexander Shiyan .driver = {
530bc000245SAlexander Shiyan .name = "clps711x-uart",
531bc000245SAlexander Shiyan .of_match_table = of_match_ptr(clps711x_uart_dt_ids),
53295113728SAlexander Shiyan },
53395113728SAlexander Shiyan .probe = uart_clps711x_probe,
5342d47b716SBill Pemberton .remove = uart_clps711x_remove,
53595113728SAlexander Shiyan };
53695113728SAlexander Shiyan
uart_clps711x_init(void)53795113728SAlexander Shiyan static int __init uart_clps711x_init(void)
53895113728SAlexander Shiyan {
539bc000245SAlexander Shiyan int ret;
540bc000245SAlexander Shiyan
541bc000245SAlexander Shiyan #ifdef CONFIG_SERIAL_CLPS711X_CONSOLE
542bc000245SAlexander Shiyan clps711x_uart.cons = &clps711x_console;
543bc000245SAlexander Shiyan clps711x_console.data = &clps711x_uart;
544bc000245SAlexander Shiyan #endif
545bc000245SAlexander Shiyan
546bc000245SAlexander Shiyan ret = uart_register_driver(&clps711x_uart);
547bc000245SAlexander Shiyan if (ret)
548bc000245SAlexander Shiyan return ret;
549bc000245SAlexander Shiyan
550bc000245SAlexander Shiyan return platform_driver_register(&clps711x_uart_platform);
55195113728SAlexander Shiyan }
55295113728SAlexander Shiyan module_init(uart_clps711x_init);
55395113728SAlexander Shiyan
uart_clps711x_exit(void)55495113728SAlexander Shiyan static void __exit uart_clps711x_exit(void)
55595113728SAlexander Shiyan {
556bc000245SAlexander Shiyan platform_driver_unregister(&clps711x_uart_platform);
557bc000245SAlexander Shiyan uart_unregister_driver(&clps711x_uart);
55895113728SAlexander Shiyan }
55995113728SAlexander Shiyan module_exit(uart_clps711x_exit);
560ab4382d2SGreg Kroah-Hartman
561ab4382d2SGreg Kroah-Hartman MODULE_AUTHOR("Deep Blue Solutions Ltd");
56295113728SAlexander Shiyan MODULE_DESCRIPTION("CLPS711X serial driver");
563ab4382d2SGreg Kroah-Hartman MODULE_LICENSE("GPL");
564