1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2018 Socionext Inc.
7 #include <linux/console.h>
8 #include <linux/module.h>
9 #include <linux/of_irq.h>
10 #include <linux/platform_device.h>
11 #include <linux/serial_core.h>
12 #include <linux/tty.h>
13 #include <linux/tty_flip.h>
15 #define USIO_NAME "mlb-usio-uart"
16 #define USIO_UART_DEV_NAME "ttyUSI"
18 static struct uart_port mlb_usio_ports
[CONFIG_SERIAL_MILBEAUT_USIO_PORTS
];
22 static int mlb_usio_irq
[CONFIG_SERIAL_MILBEAUT_USIO_PORTS
][2];
24 #define MLB_USIO_REG_SMR 0
25 #define MLB_USIO_REG_SCR 1
26 #define MLB_USIO_REG_ESCR 2
27 #define MLB_USIO_REG_SSR 3
28 #define MLB_USIO_REG_DR 4
29 #define MLB_USIO_REG_BGR 6
30 #define MLB_USIO_REG_FCR 12
31 #define MLB_USIO_REG_FBYTE 14
33 #define MLB_USIO_SMR_SOE BIT(0)
34 #define MLB_USIO_SMR_SBL BIT(3)
35 #define MLB_USIO_SCR_TXE BIT(0)
36 #define MLB_USIO_SCR_RXE BIT(1)
37 #define MLB_USIO_SCR_TBIE BIT(2)
38 #define MLB_USIO_SCR_TIE BIT(3)
39 #define MLB_USIO_SCR_RIE BIT(4)
40 #define MLB_USIO_SCR_UPCL BIT(7)
41 #define MLB_USIO_ESCR_L_8BIT 0
42 #define MLB_USIO_ESCR_L_5BIT 1
43 #define MLB_USIO_ESCR_L_6BIT 2
44 #define MLB_USIO_ESCR_L_7BIT 3
45 #define MLB_USIO_ESCR_P BIT(3)
46 #define MLB_USIO_ESCR_PEN BIT(4)
47 #define MLB_USIO_ESCR_FLWEN BIT(7)
48 #define MLB_USIO_SSR_TBI BIT(0)
49 #define MLB_USIO_SSR_TDRE BIT(1)
50 #define MLB_USIO_SSR_RDRF BIT(2)
51 #define MLB_USIO_SSR_ORE BIT(3)
52 #define MLB_USIO_SSR_FRE BIT(4)
53 #define MLB_USIO_SSR_PE BIT(5)
54 #define MLB_USIO_SSR_REC BIT(7)
55 #define MLB_USIO_SSR_BRK BIT(8)
56 #define MLB_USIO_FCR_FE1 BIT(0)
57 #define MLB_USIO_FCR_FE2 BIT(1)
58 #define MLB_USIO_FCR_FCL1 BIT(2)
59 #define MLB_USIO_FCR_FCL2 BIT(3)
60 #define MLB_USIO_FCR_FSET BIT(4)
61 #define MLB_USIO_FCR_FTIE BIT(9)
62 #define MLB_USIO_FCR_FDRQ BIT(10)
63 #define MLB_USIO_FCR_FRIIE BIT(11)
65 static void mlb_usio_stop_tx(struct uart_port
*port
)
67 writew(readw(port
->membase
+ MLB_USIO_REG_FCR
) & ~MLB_USIO_FCR_FTIE
,
68 port
->membase
+ MLB_USIO_REG_FCR
);
69 writeb(readb(port
->membase
+ MLB_USIO_REG_SCR
) & ~MLB_USIO_SCR_TBIE
,
70 port
->membase
+ MLB_USIO_REG_SCR
);
73 static void mlb_usio_tx_chars(struct uart_port
*port
)
75 struct circ_buf
*xmit
= &port
->state
->xmit
;
78 writew(readw(port
->membase
+ MLB_USIO_REG_FCR
) & ~MLB_USIO_FCR_FTIE
,
79 port
->membase
+ MLB_USIO_REG_FCR
);
80 writeb(readb(port
->membase
+ MLB_USIO_REG_SCR
) &
81 ~(MLB_USIO_SCR_TIE
| MLB_USIO_SCR_TBIE
),
82 port
->membase
+ MLB_USIO_REG_SCR
);
85 writew(port
->x_char
, port
->membase
+ MLB_USIO_REG_DR
);
90 if (uart_circ_empty(xmit
) || uart_tx_stopped(port
)) {
91 mlb_usio_stop_tx(port
);
95 count
= port
->fifosize
-
96 (readw(port
->membase
+ MLB_USIO_REG_FBYTE
) & 0xff);
99 writew(xmit
->buf
[xmit
->tail
], port
->membase
+ MLB_USIO_REG_DR
);
101 xmit
->tail
= (xmit
->tail
+ 1) & (UART_XMIT_SIZE
- 1);
103 if (uart_circ_empty(xmit
))
106 } while (--count
> 0);
108 writew(readw(port
->membase
+ MLB_USIO_REG_FCR
) & ~MLB_USIO_FCR_FDRQ
,
109 port
->membase
+ MLB_USIO_REG_FCR
);
111 writeb(readb(port
->membase
+ MLB_USIO_REG_SCR
) | MLB_USIO_SCR_TBIE
,
112 port
->membase
+ MLB_USIO_REG_SCR
);
114 if (uart_circ_chars_pending(xmit
) < WAKEUP_CHARS
)
115 uart_write_wakeup(port
);
117 if (uart_circ_empty(xmit
))
118 mlb_usio_stop_tx(port
);
121 static void mlb_usio_start_tx(struct uart_port
*port
)
123 u16 fcr
= readw(port
->membase
+ MLB_USIO_REG_FCR
);
125 writew(fcr
| MLB_USIO_FCR_FTIE
, port
->membase
+ MLB_USIO_REG_FCR
);
126 if (!(fcr
& MLB_USIO_FCR_FDRQ
))
129 writeb(readb(port
->membase
+ MLB_USIO_REG_SCR
) | MLB_USIO_SCR_TBIE
,
130 port
->membase
+ MLB_USIO_REG_SCR
);
132 if (readb(port
->membase
+ MLB_USIO_REG_SSR
) & MLB_USIO_SSR_TBI
)
133 mlb_usio_tx_chars(port
);
136 static void mlb_usio_stop_rx(struct uart_port
*port
)
138 writeb(readb(port
->membase
+ MLB_USIO_REG_SCR
) & ~MLB_USIO_SCR_RIE
,
139 port
->membase
+ MLB_USIO_REG_SCR
);
142 static void mlb_usio_enable_ms(struct uart_port
*port
)
144 writeb(readb(port
->membase
+ MLB_USIO_REG_SCR
) |
145 MLB_USIO_SCR_RIE
| MLB_USIO_SCR_RXE
,
146 port
->membase
+ MLB_USIO_REG_SCR
);
149 static void mlb_usio_rx_chars(struct uart_port
*port
)
151 struct tty_port
*ttyport
= &port
->state
->port
;
152 unsigned long flag
= 0;
157 while (max_count
--) {
158 status
= readb(port
->membase
+ MLB_USIO_REG_SSR
);
160 if (!(status
& MLB_USIO_SSR_RDRF
))
163 if (!(status
& (MLB_USIO_SSR_ORE
| MLB_USIO_SSR_FRE
|
165 ch
= readw(port
->membase
+ MLB_USIO_REG_DR
);
168 if (uart_handle_sysrq_char(port
, ch
))
170 uart_insert_char(port
, status
, MLB_USIO_SSR_ORE
,
174 if (status
& MLB_USIO_SSR_PE
)
175 port
->icount
.parity
++;
176 if (status
& MLB_USIO_SSR_ORE
)
177 port
->icount
.overrun
++;
178 status
&= port
->read_status_mask
;
179 if (status
& MLB_USIO_SSR_BRK
) {
183 if (status
& MLB_USIO_SSR_PE
) {
187 if (status
& MLB_USIO_SSR_FRE
) {
192 uart_insert_char(port
, status
, MLB_USIO_SSR_ORE
,
195 writeb(readb(port
->membase
+ MLB_USIO_REG_SSR
) |
197 port
->membase
+ MLB_USIO_REG_SSR
);
199 max_count
= readw(port
->membase
+ MLB_USIO_REG_FBYTE
) >> 8;
200 writew(readw(port
->membase
+ MLB_USIO_REG_FCR
) |
201 MLB_USIO_FCR_FE2
| MLB_USIO_FCR_FRIIE
,
202 port
->membase
+ MLB_USIO_REG_FCR
);
205 tty_flip_buffer_push(ttyport
);
208 static irqreturn_t
mlb_usio_rx_irq(int irq
, void *dev_id
)
210 struct uart_port
*port
= dev_id
;
212 spin_lock(&port
->lock
);
213 mlb_usio_rx_chars(port
);
214 spin_unlock(&port
->lock
);
219 static irqreturn_t
mlb_usio_tx_irq(int irq
, void *dev_id
)
221 struct uart_port
*port
= dev_id
;
223 spin_lock(&port
->lock
);
224 if (readb(port
->membase
+ MLB_USIO_REG_SSR
) & MLB_USIO_SSR_TBI
)
225 mlb_usio_tx_chars(port
);
226 spin_unlock(&port
->lock
);
231 static unsigned int mlb_usio_tx_empty(struct uart_port
*port
)
233 return (readb(port
->membase
+ MLB_USIO_REG_SSR
) & MLB_USIO_SSR_TBI
) ?
237 static void mlb_usio_set_mctrl(struct uart_port
*port
, unsigned int mctrl
)
241 static unsigned int mlb_usio_get_mctrl(struct uart_port
*port
)
243 return TIOCM_CAR
| TIOCM_DSR
| TIOCM_CTS
;
247 static void mlb_usio_break_ctl(struct uart_port
*port
, int break_state
)
251 static int mlb_usio_startup(struct uart_port
*port
)
253 const char *portname
= to_platform_device(port
->dev
)->name
;
255 int ret
, index
= port
->line
;
258 ret
= request_irq(mlb_usio_irq
[index
][RX
], mlb_usio_rx_irq
,
262 ret
= request_irq(mlb_usio_irq
[index
][TX
], mlb_usio_tx_irq
,
265 free_irq(mlb_usio_irq
[index
][RX
], port
);
269 escr
= readb(port
->membase
+ MLB_USIO_REG_ESCR
);
270 if (of_property_read_bool(port
->dev
->of_node
, "auto-flow-control"))
271 escr
|= MLB_USIO_ESCR_FLWEN
;
272 spin_lock_irqsave(&port
->lock
, flags
);
273 writeb(0, port
->membase
+ MLB_USIO_REG_SCR
);
274 writeb(escr
, port
->membase
+ MLB_USIO_REG_ESCR
);
275 writeb(MLB_USIO_SCR_UPCL
, port
->membase
+ MLB_USIO_REG_SCR
);
276 writeb(MLB_USIO_SSR_REC
, port
->membase
+ MLB_USIO_REG_SSR
);
277 writew(0, port
->membase
+ MLB_USIO_REG_FCR
);
278 writew(MLB_USIO_FCR_FCL1
| MLB_USIO_FCR_FCL2
,
279 port
->membase
+ MLB_USIO_REG_FCR
);
280 writew(MLB_USIO_FCR_FE1
| MLB_USIO_FCR_FE2
| MLB_USIO_FCR_FRIIE
,
281 port
->membase
+ MLB_USIO_REG_FCR
);
282 writew(0, port
->membase
+ MLB_USIO_REG_FBYTE
);
283 writew(BIT(12), port
->membase
+ MLB_USIO_REG_FBYTE
);
285 writeb(MLB_USIO_SCR_TXE
| MLB_USIO_SCR_RIE
| MLB_USIO_SCR_TBIE
|
286 MLB_USIO_SCR_RXE
, port
->membase
+ MLB_USIO_REG_SCR
);
287 spin_unlock_irqrestore(&port
->lock
, flags
);
292 static void mlb_usio_shutdown(struct uart_port
*port
)
294 int index
= port
->line
;
296 free_irq(mlb_usio_irq
[index
][RX
], port
);
297 free_irq(mlb_usio_irq
[index
][TX
], port
);
300 static void mlb_usio_set_termios(struct uart_port
*port
,
301 struct ktermios
*termios
, struct ktermios
*old
)
303 unsigned int escr
, smr
= MLB_USIO_SMR_SOE
;
304 unsigned long flags
, baud
, quot
;
306 switch (termios
->c_cflag
& CSIZE
) {
308 escr
= MLB_USIO_ESCR_L_5BIT
;
311 escr
= MLB_USIO_ESCR_L_6BIT
;
314 escr
= MLB_USIO_ESCR_L_7BIT
;
318 escr
= MLB_USIO_ESCR_L_8BIT
;
322 if (termios
->c_cflag
& CSTOPB
)
323 smr
|= MLB_USIO_SMR_SBL
;
325 if (termios
->c_cflag
& PARENB
) {
326 escr
|= MLB_USIO_ESCR_PEN
;
327 if (termios
->c_cflag
& PARODD
)
328 escr
|= MLB_USIO_ESCR_P
;
330 /* Set hard flow control */
331 if (of_property_read_bool(port
->dev
->of_node
, "auto-flow-control") ||
332 (termios
->c_cflag
& CRTSCTS
))
333 escr
|= MLB_USIO_ESCR_FLWEN
;
335 baud
= uart_get_baud_rate(port
, termios
, old
, 0, port
->uartclk
);
337 quot
= port
->uartclk
/ baud
- 1;
341 spin_lock_irqsave(&port
->lock
, flags
);
342 uart_update_timeout(port
, termios
->c_cflag
, baud
);
343 port
->read_status_mask
= MLB_USIO_SSR_ORE
| MLB_USIO_SSR_RDRF
|
345 if (termios
->c_iflag
& INPCK
)
346 port
->read_status_mask
|= MLB_USIO_SSR_FRE
| MLB_USIO_SSR_PE
;
348 port
->ignore_status_mask
= 0;
349 if (termios
->c_iflag
& IGNPAR
)
350 port
->ignore_status_mask
|= MLB_USIO_SSR_FRE
| MLB_USIO_SSR_PE
;
351 if ((termios
->c_iflag
& IGNBRK
) && (termios
->c_iflag
& IGNPAR
))
352 port
->ignore_status_mask
|= MLB_USIO_SSR_ORE
;
353 if ((termios
->c_cflag
& CREAD
) == 0)
354 port
->ignore_status_mask
|= MLB_USIO_SSR_RDRF
;
356 writeb(0, port
->membase
+ MLB_USIO_REG_SCR
);
357 writeb(MLB_USIO_SCR_UPCL
, port
->membase
+ MLB_USIO_REG_SCR
);
358 writeb(MLB_USIO_SSR_REC
, port
->membase
+ MLB_USIO_REG_SSR
);
359 writew(0, port
->membase
+ MLB_USIO_REG_FCR
);
360 writeb(smr
, port
->membase
+ MLB_USIO_REG_SMR
);
361 writeb(escr
, port
->membase
+ MLB_USIO_REG_ESCR
);
362 writew(quot
, port
->membase
+ MLB_USIO_REG_BGR
);
363 writew(0, port
->membase
+ MLB_USIO_REG_FCR
);
364 writew(MLB_USIO_FCR_FCL1
| MLB_USIO_FCR_FCL2
| MLB_USIO_FCR_FE1
|
365 MLB_USIO_FCR_FE2
| MLB_USIO_FCR_FRIIE
,
366 port
->membase
+ MLB_USIO_REG_FCR
);
367 writew(0, port
->membase
+ MLB_USIO_REG_FBYTE
);
368 writew(BIT(12), port
->membase
+ MLB_USIO_REG_FBYTE
);
369 writeb(MLB_USIO_SCR_RIE
| MLB_USIO_SCR_RXE
| MLB_USIO_SCR_TBIE
|
370 MLB_USIO_SCR_TXE
, port
->membase
+ MLB_USIO_REG_SCR
);
371 spin_unlock_irqrestore(&port
->lock
, flags
);
374 static const char *mlb_usio_type(struct uart_port
*port
)
376 return ((port
->type
== PORT_MLB_USIO
) ? USIO_NAME
: NULL
);
379 static void mlb_usio_config_port(struct uart_port
*port
, int flags
)
381 if (flags
& UART_CONFIG_TYPE
)
382 port
->type
= PORT_MLB_USIO
;
385 static const struct uart_ops mlb_usio_ops
= {
386 .tx_empty
= mlb_usio_tx_empty
,
387 .set_mctrl
= mlb_usio_set_mctrl
,
388 .get_mctrl
= mlb_usio_get_mctrl
,
389 .stop_tx
= mlb_usio_stop_tx
,
390 .start_tx
= mlb_usio_start_tx
,
391 .stop_rx
= mlb_usio_stop_rx
,
392 .enable_ms
= mlb_usio_enable_ms
,
393 .break_ctl
= mlb_usio_break_ctl
,
394 .startup
= mlb_usio_startup
,
395 .shutdown
= mlb_usio_shutdown
,
396 .set_termios
= mlb_usio_set_termios
,
397 .type
= mlb_usio_type
,
398 .config_port
= mlb_usio_config_port
,
401 #ifdef CONFIG_SERIAL_MILBEAUT_USIO_CONSOLE
403 static void mlb_usio_console_putchar(struct uart_port
*port
, int c
)
405 while (!(readb(port
->membase
+ MLB_USIO_REG_SSR
) & MLB_USIO_SSR_TDRE
))
408 writew(c
, port
->membase
+ MLB_USIO_REG_DR
);
411 static void mlb_usio_console_write(struct console
*co
, const char *s
,
414 struct uart_port
*port
= &mlb_usio_ports
[co
->index
];
416 uart_console_write(port
, s
, count
, mlb_usio_console_putchar
);
419 static int __init
mlb_usio_console_setup(struct console
*co
, char *options
)
421 struct uart_port
*port
;
427 if (co
->index
>= CONFIG_SERIAL_MILBEAUT_USIO_PORTS
)
430 port
= &mlb_usio_ports
[co
->index
];
436 uart_parse_options(options
, &baud
, &parity
, &bits
, &flow
);
438 if (of_property_read_bool(port
->dev
->of_node
, "auto-flow-control"))
441 return uart_set_options(port
, co
, baud
, parity
, bits
, flow
);
445 static struct uart_driver mlb_usio_uart_driver
;
446 static struct console mlb_usio_console
= {
447 .name
= USIO_UART_DEV_NAME
,
448 .write
= mlb_usio_console_write
,
449 .device
= uart_console_device
,
450 .setup
= mlb_usio_console_setup
,
451 .flags
= CON_PRINTBUFFER
,
453 .data
= &mlb_usio_uart_driver
,
456 static int __init
mlb_usio_console_init(void)
458 register_console(&mlb_usio_console
);
461 console_initcall(mlb_usio_console_init
);
464 static void mlb_usio_early_console_write(struct console
*co
, const char *s
,
467 struct earlycon_device
*dev
= co
->data
;
469 uart_console_write(&dev
->port
, s
, count
, mlb_usio_console_putchar
);
472 static int __init
mlb_usio_early_console_setup(struct earlycon_device
*device
,
475 if (!device
->port
.membase
)
477 device
->con
->write
= mlb_usio_early_console_write
;
481 OF_EARLYCON_DECLARE(mlb_usio
, "socionext,milbeaut-usio-uart",
482 mlb_usio_early_console_setup
);
484 #define USIO_CONSOLE (&mlb_usio_console)
486 #define USIO_CONSOLE NULL
489 static struct uart_driver mlb_usio_uart_driver
= {
490 .owner
= THIS_MODULE
,
491 .driver_name
= USIO_NAME
,
492 .dev_name
= USIO_UART_DEV_NAME
,
493 .cons
= USIO_CONSOLE
,
494 .nr
= CONFIG_SERIAL_MILBEAUT_USIO_PORTS
,
497 static int mlb_usio_probe(struct platform_device
*pdev
)
499 struct clk
*clk
= devm_clk_get(&pdev
->dev
, NULL
);
500 struct uart_port
*port
;
501 struct resource
*res
;
506 dev_err(&pdev
->dev
, "Missing clock\n");
509 ret
= clk_prepare_enable(clk
);
511 dev_err(&pdev
->dev
, "Clock enable failed: %d\n", ret
);
514 of_property_read_u32(pdev
->dev
.of_node
, "index", &index
);
515 port
= &mlb_usio_ports
[index
];
517 port
->private_data
= (void *)clk
;
518 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
520 dev_err(&pdev
->dev
, "Missing regs\n");
524 port
->membase
= devm_ioremap(&pdev
->dev
, res
->start
,
527 ret
= platform_get_irq_byname(pdev
, "rx");
528 mlb_usio_irq
[index
][RX
] = ret
;
530 ret
= platform_get_irq_byname(pdev
, "tx");
531 mlb_usio_irq
[index
][TX
] = ret
;
533 port
->irq
= mlb_usio_irq
[index
][RX
];
534 port
->uartclk
= clk_get_rate(clk
);
535 port
->fifosize
= 128;
536 port
->has_sysrq
= IS_ENABLED(CONFIG_SERIAL_MILBEAUT_USIO_CONSOLE
);
537 port
->iotype
= UPIO_MEM32
;
538 port
->flags
= UPF_BOOT_AUTOCONF
| UPF_SPD_VHI
;
540 port
->ops
= &mlb_usio_ops
;
541 port
->dev
= &pdev
->dev
;
543 ret
= uart_add_one_port(&mlb_usio_uart_driver
, port
);
545 dev_err(&pdev
->dev
, "Adding port failed: %d\n", ret
);
551 clk_disable_unprepare(clk
);
556 static int mlb_usio_remove(struct platform_device
*pdev
)
558 struct uart_port
*port
= &mlb_usio_ports
[pdev
->id
];
559 struct clk
*clk
= port
->private_data
;
561 uart_remove_one_port(&mlb_usio_uart_driver
, port
);
562 clk_disable_unprepare(clk
);
567 static const struct of_device_id mlb_usio_dt_ids
[] = {
568 { .compatible
= "socionext,milbeaut-usio-uart" },
571 MODULE_DEVICE_TABLE(of
, mlb_usio_dt_ids
);
573 static struct platform_driver mlb_usio_driver
= {
574 .probe
= mlb_usio_probe
,
575 .remove
= mlb_usio_remove
,
578 .of_match_table
= mlb_usio_dt_ids
,
582 static int __init
mlb_usio_init(void)
584 int ret
= uart_register_driver(&mlb_usio_uart_driver
);
587 pr_err("%s: uart registration failed: %d\n", __func__
, ret
);
590 ret
= platform_driver_register(&mlb_usio_driver
);
592 uart_unregister_driver(&mlb_usio_uart_driver
);
593 pr_err("%s: drv registration failed: %d\n", __func__
, ret
);
600 static void __exit
mlb_usio_exit(void)
602 platform_driver_unregister(&mlb_usio_driver
);
603 uart_unregister_driver(&mlb_usio_uart_driver
);
606 module_init(mlb_usio_init
);
607 module_exit(mlb_usio_exit
);
609 MODULE_AUTHOR("SOCIONEXT");
610 MODULE_DESCRIPTION("MILBEAUT_USIO/UART Driver");
611 MODULE_LICENSE("GPL");