1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * RT288x/Au1xxx driver |
4 | */ |
5 | |
6 | #include <linux/module.h> |
7 | #include <linux/io.h> |
8 | #include <linux/init.h> |
9 | #include <linux/console.h> |
10 | #include <linux/serial.h> |
11 | #include <linux/serial_8250.h> |
12 | |
13 | #include "8250.h" |
14 | |
15 | #define RT288X_DL 0x28 |
16 | |
17 | /* Au1x00/RT288x UART hardware has a weird register layout */ |
18 | static const u8 au_io_in_map[7] = { |
19 | [UART_RX] = 0, |
20 | [UART_IER] = 2, |
21 | [UART_IIR] = 3, |
22 | [UART_LCR] = 5, |
23 | [UART_MCR] = 6, |
24 | [UART_LSR] = 7, |
25 | [UART_MSR] = 8, |
26 | }; |
27 | |
28 | static const u8 au_io_out_map[5] = { |
29 | [UART_TX] = 1, |
30 | [UART_IER] = 2, |
31 | [UART_FCR] = 4, |
32 | [UART_LCR] = 5, |
33 | [UART_MCR] = 6, |
34 | }; |
35 | |
36 | static unsigned int au_serial_in(struct uart_port *p, int offset) |
37 | { |
38 | if (offset >= ARRAY_SIZE(au_io_in_map)) |
39 | return UINT_MAX; |
40 | offset = au_io_in_map[offset]; |
41 | |
42 | return __raw_readl(addr: p->membase + (offset << p->regshift)); |
43 | } |
44 | |
45 | static void au_serial_out(struct uart_port *p, int offset, int value) |
46 | { |
47 | if (offset >= ARRAY_SIZE(au_io_out_map)) |
48 | return; |
49 | offset = au_io_out_map[offset]; |
50 | |
51 | __raw_writel(val: value, addr: p->membase + (offset << p->regshift)); |
52 | } |
53 | |
54 | /* Au1x00 haven't got a standard divisor latch */ |
55 | static u32 au_serial_dl_read(struct uart_8250_port *up) |
56 | { |
57 | return __raw_readl(addr: up->port.membase + RT288X_DL); |
58 | } |
59 | |
60 | static void au_serial_dl_write(struct uart_8250_port *up, u32 value) |
61 | { |
62 | __raw_writel(val: value, addr: up->port.membase + RT288X_DL); |
63 | } |
64 | |
65 | int au_platform_setup(struct plat_serial8250_port *p) |
66 | { |
67 | p->iotype = UPIO_AU; |
68 | |
69 | p->serial_in = au_serial_in; |
70 | p->serial_out = au_serial_out; |
71 | p->dl_read = au_serial_dl_read; |
72 | p->dl_write = au_serial_dl_write; |
73 | |
74 | p->mapsize = 0x1000; |
75 | |
76 | p->bugs |= UART_BUG_NOMSR; |
77 | |
78 | return 0; |
79 | } |
80 | EXPORT_SYMBOL_GPL(au_platform_setup); |
81 | |
82 | int rt288x_setup(struct uart_port *p) |
83 | { |
84 | struct uart_8250_port *up = up_to_u8250p(up: p); |
85 | |
86 | p->iotype = UPIO_AU; |
87 | |
88 | p->serial_in = au_serial_in; |
89 | p->serial_out = au_serial_out; |
90 | up->dl_read = au_serial_dl_read; |
91 | up->dl_write = au_serial_dl_write; |
92 | |
93 | p->mapsize = 0x100; |
94 | |
95 | up->bugs |= UART_BUG_NOMSR; |
96 | |
97 | return 0; |
98 | } |
99 | EXPORT_SYMBOL_GPL(rt288x_setup); |
100 | |
101 | #ifdef CONFIG_SERIAL_8250_CONSOLE |
102 | static void au_putc(struct uart_port *port, unsigned char c) |
103 | { |
104 | unsigned int status; |
105 | |
106 | au_serial_out(p: port, UART_TX, value: c); |
107 | |
108 | for (;;) { |
109 | status = au_serial_in(p: port, UART_LSR); |
110 | if (uart_lsr_tx_empty(lsr: status)) |
111 | break; |
112 | cpu_relax(); |
113 | } |
114 | } |
115 | |
116 | static void au_early_serial8250_write(struct console *console, |
117 | const char *s, unsigned int count) |
118 | { |
119 | struct earlycon_device *device = console->data; |
120 | struct uart_port *port = &device->port; |
121 | |
122 | uart_console_write(port, s, count, putchar: au_putc); |
123 | } |
124 | |
125 | static int __init early_au_setup(struct earlycon_device *dev, const char *opt) |
126 | { |
127 | rt288x_setup(&dev->port); |
128 | dev->con->write = au_early_serial8250_write; |
129 | |
130 | return 0; |
131 | } |
132 | OF_EARLYCON_DECLARE(palmchip, "ralink,rt2880-uart" , early_au_setup); |
133 | #endif |
134 | |
135 | MODULE_DESCRIPTION("RT288x/Au1xxx UART driver" ); |
136 | MODULE_LICENSE("GPL" ); |
137 | |