1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2018 Socionext Inc. |
4 | */ |
5 | |
6 | #include <linux/clk.h> |
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> |
14 | |
15 | #define USIO_NAME "mlb-usio-uart" |
16 | #define USIO_UART_DEV_NAME "ttyUSI" |
17 | |
18 | static struct uart_port mlb_usio_ports[CONFIG_SERIAL_MILBEAUT_USIO_PORTS]; |
19 | |
20 | #define RX 0 |
21 | #define TX 1 |
22 | static int mlb_usio_irq[CONFIG_SERIAL_MILBEAUT_USIO_PORTS][2]; |
23 | |
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 |
32 | |
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) |
64 | |
65 | static void mlb_usio_stop_tx(struct uart_port *port) |
66 | { |
67 | writew(readw(addr: port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FTIE, |
68 | addr: port->membase + MLB_USIO_REG_FCR); |
69 | writeb(readb(addr: port->membase + MLB_USIO_REG_SCR) & ~MLB_USIO_SCR_TBIE, |
70 | addr: port->membase + MLB_USIO_REG_SCR); |
71 | } |
72 | |
73 | static void mlb_usio_tx_chars(struct uart_port *port) |
74 | { |
75 | struct circ_buf *xmit = &port->state->xmit; |
76 | int count; |
77 | |
78 | writew(readw(addr: port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FTIE, |
79 | addr: port->membase + MLB_USIO_REG_FCR); |
80 | writeb(readb(addr: port->membase + MLB_USIO_REG_SCR) & |
81 | ~(MLB_USIO_SCR_TIE | MLB_USIO_SCR_TBIE), |
82 | addr: port->membase + MLB_USIO_REG_SCR); |
83 | |
84 | if (port->x_char) { |
85 | writew(val: port->x_char, addr: port->membase + MLB_USIO_REG_DR); |
86 | port->icount.tx++; |
87 | port->x_char = 0; |
88 | return; |
89 | } |
90 | if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { |
91 | mlb_usio_stop_tx(port); |
92 | return; |
93 | } |
94 | |
95 | count = port->fifosize - |
96 | (readw(addr: port->membase + MLB_USIO_REG_FBYTE) & 0xff); |
97 | |
98 | do { |
99 | writew(val: xmit->buf[xmit->tail], addr: port->membase + MLB_USIO_REG_DR); |
100 | |
101 | uart_xmit_advance(up: port, chars: 1); |
102 | if (uart_circ_empty(xmit)) |
103 | break; |
104 | |
105 | } while (--count > 0); |
106 | |
107 | writew(readw(addr: port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FDRQ, |
108 | addr: port->membase + MLB_USIO_REG_FCR); |
109 | |
110 | writeb(readb(addr: port->membase + MLB_USIO_REG_SCR) | MLB_USIO_SCR_TBIE, |
111 | addr: port->membase + MLB_USIO_REG_SCR); |
112 | |
113 | if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) |
114 | uart_write_wakeup(port); |
115 | |
116 | if (uart_circ_empty(xmit)) |
117 | mlb_usio_stop_tx(port); |
118 | } |
119 | |
120 | static void mlb_usio_start_tx(struct uart_port *port) |
121 | { |
122 | u16 fcr = readw(addr: port->membase + MLB_USIO_REG_FCR); |
123 | |
124 | writew(val: fcr | MLB_USIO_FCR_FTIE, addr: port->membase + MLB_USIO_REG_FCR); |
125 | if (!(fcr & MLB_USIO_FCR_FDRQ)) |
126 | return; |
127 | |
128 | writeb(readb(addr: port->membase + MLB_USIO_REG_SCR) | MLB_USIO_SCR_TBIE, |
129 | addr: port->membase + MLB_USIO_REG_SCR); |
130 | |
131 | if (readb(addr: port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TBI) |
132 | mlb_usio_tx_chars(port); |
133 | } |
134 | |
135 | static void mlb_usio_stop_rx(struct uart_port *port) |
136 | { |
137 | writeb(readb(addr: port->membase + MLB_USIO_REG_SCR) & ~MLB_USIO_SCR_RIE, |
138 | addr: port->membase + MLB_USIO_REG_SCR); |
139 | } |
140 | |
141 | static void mlb_usio_enable_ms(struct uart_port *port) |
142 | { |
143 | writeb(readb(addr: port->membase + MLB_USIO_REG_SCR) | |
144 | MLB_USIO_SCR_RIE | MLB_USIO_SCR_RXE, |
145 | addr: port->membase + MLB_USIO_REG_SCR); |
146 | } |
147 | |
148 | static void mlb_usio_rx_chars(struct uart_port *port) |
149 | { |
150 | struct tty_port *ttyport = &port->state->port; |
151 | u8 flag = 0, ch = 0; |
152 | u8 status; |
153 | int max_count = 2; |
154 | |
155 | while (max_count--) { |
156 | status = readb(addr: port->membase + MLB_USIO_REG_SSR); |
157 | |
158 | if (!(status & MLB_USIO_SSR_RDRF)) |
159 | break; |
160 | |
161 | if (!(status & (MLB_USIO_SSR_ORE | MLB_USIO_SSR_FRE | |
162 | MLB_USIO_SSR_PE))) { |
163 | ch = readw(addr: port->membase + MLB_USIO_REG_DR); |
164 | flag = TTY_NORMAL; |
165 | port->icount.rx++; |
166 | if (uart_handle_sysrq_char(port, ch)) |
167 | continue; |
168 | uart_insert_char(port, status, MLB_USIO_SSR_ORE, |
169 | ch, flag); |
170 | continue; |
171 | } |
172 | if (status & MLB_USIO_SSR_PE) |
173 | port->icount.parity++; |
174 | if (status & MLB_USIO_SSR_ORE) |
175 | port->icount.overrun++; |
176 | status &= port->read_status_mask; |
177 | if (status & MLB_USIO_SSR_BRK) { |
178 | flag = TTY_BREAK; |
179 | ch = 0; |
180 | } else |
181 | if (status & MLB_USIO_SSR_PE) { |
182 | flag = TTY_PARITY; |
183 | ch = 0; |
184 | } else |
185 | if (status & MLB_USIO_SSR_FRE) { |
186 | flag = TTY_FRAME; |
187 | ch = 0; |
188 | } |
189 | if (flag) |
190 | uart_insert_char(port, status, MLB_USIO_SSR_ORE, |
191 | ch, flag); |
192 | |
193 | writeb(readb(addr: port->membase + MLB_USIO_REG_SSR) | |
194 | MLB_USIO_SSR_REC, |
195 | addr: port->membase + MLB_USIO_REG_SSR); |
196 | |
197 | max_count = readw(addr: port->membase + MLB_USIO_REG_FBYTE) >> 8; |
198 | writew(readw(addr: port->membase + MLB_USIO_REG_FCR) | |
199 | MLB_USIO_FCR_FE2 | MLB_USIO_FCR_FRIIE, |
200 | addr: port->membase + MLB_USIO_REG_FCR); |
201 | } |
202 | |
203 | tty_flip_buffer_push(port: ttyport); |
204 | } |
205 | |
206 | static irqreturn_t mlb_usio_rx_irq(int irq, void *dev_id) |
207 | { |
208 | struct uart_port *port = dev_id; |
209 | |
210 | uart_port_lock(up: port); |
211 | mlb_usio_rx_chars(port); |
212 | uart_port_unlock(up: port); |
213 | |
214 | return IRQ_HANDLED; |
215 | } |
216 | |
217 | static irqreturn_t mlb_usio_tx_irq(int irq, void *dev_id) |
218 | { |
219 | struct uart_port *port = dev_id; |
220 | |
221 | uart_port_lock(up: port); |
222 | if (readb(addr: port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TBI) |
223 | mlb_usio_tx_chars(port); |
224 | uart_port_unlock(up: port); |
225 | |
226 | return IRQ_HANDLED; |
227 | } |
228 | |
229 | static unsigned int mlb_usio_tx_empty(struct uart_port *port) |
230 | { |
231 | return (readb(addr: port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TBI) ? |
232 | TIOCSER_TEMT : 0; |
233 | } |
234 | |
235 | static void mlb_usio_set_mctrl(struct uart_port *port, unsigned int mctrl) |
236 | { |
237 | } |
238 | |
239 | static unsigned int mlb_usio_get_mctrl(struct uart_port *port) |
240 | { |
241 | return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; |
242 | |
243 | } |
244 | |
245 | static void mlb_usio_break_ctl(struct uart_port *port, int break_state) |
246 | { |
247 | } |
248 | |
249 | static int mlb_usio_startup(struct uart_port *port) |
250 | { |
251 | const char *portname = to_platform_device(port->dev)->name; |
252 | unsigned long flags; |
253 | int ret, index = port->line; |
254 | unsigned char escr; |
255 | |
256 | ret = request_irq(irq: mlb_usio_irq[index][RX], handler: mlb_usio_rx_irq, |
257 | flags: 0, name: portname, dev: port); |
258 | if (ret) |
259 | return ret; |
260 | ret = request_irq(irq: mlb_usio_irq[index][TX], handler: mlb_usio_tx_irq, |
261 | flags: 0, name: portname, dev: port); |
262 | if (ret) { |
263 | free_irq(mlb_usio_irq[index][RX], port); |
264 | return ret; |
265 | } |
266 | |
267 | escr = readb(addr: port->membase + MLB_USIO_REG_ESCR); |
268 | if (of_property_read_bool(np: port->dev->of_node, propname: "auto-flow-control" )) |
269 | escr |= MLB_USIO_ESCR_FLWEN; |
270 | uart_port_lock_irqsave(up: port, flags: &flags); |
271 | writeb(val: 0, addr: port->membase + MLB_USIO_REG_SCR); |
272 | writeb(val: escr, addr: port->membase + MLB_USIO_REG_ESCR); |
273 | writeb(MLB_USIO_SCR_UPCL, addr: port->membase + MLB_USIO_REG_SCR); |
274 | writeb(MLB_USIO_SSR_REC, addr: port->membase + MLB_USIO_REG_SSR); |
275 | writew(val: 0, addr: port->membase + MLB_USIO_REG_FCR); |
276 | writew(MLB_USIO_FCR_FCL1 | MLB_USIO_FCR_FCL2, |
277 | addr: port->membase + MLB_USIO_REG_FCR); |
278 | writew(MLB_USIO_FCR_FE1 | MLB_USIO_FCR_FE2 | MLB_USIO_FCR_FRIIE, |
279 | addr: port->membase + MLB_USIO_REG_FCR); |
280 | writew(val: 0, addr: port->membase + MLB_USIO_REG_FBYTE); |
281 | writew(BIT(12), addr: port->membase + MLB_USIO_REG_FBYTE); |
282 | |
283 | writeb(MLB_USIO_SCR_TXE | MLB_USIO_SCR_RIE | MLB_USIO_SCR_TBIE | |
284 | MLB_USIO_SCR_RXE, addr: port->membase + MLB_USIO_REG_SCR); |
285 | uart_port_unlock_irqrestore(up: port, flags); |
286 | |
287 | return 0; |
288 | } |
289 | |
290 | static void mlb_usio_shutdown(struct uart_port *port) |
291 | { |
292 | int index = port->line; |
293 | |
294 | free_irq(mlb_usio_irq[index][RX], port); |
295 | free_irq(mlb_usio_irq[index][TX], port); |
296 | } |
297 | |
298 | static void mlb_usio_set_termios(struct uart_port *port, |
299 | struct ktermios *termios, |
300 | const struct ktermios *old) |
301 | { |
302 | unsigned int escr, smr = MLB_USIO_SMR_SOE; |
303 | unsigned long flags, baud, quot; |
304 | |
305 | switch (termios->c_cflag & CSIZE) { |
306 | case CS5: |
307 | escr = MLB_USIO_ESCR_L_5BIT; |
308 | break; |
309 | case CS6: |
310 | escr = MLB_USIO_ESCR_L_6BIT; |
311 | break; |
312 | case CS7: |
313 | escr = MLB_USIO_ESCR_L_7BIT; |
314 | break; |
315 | case CS8: |
316 | default: |
317 | escr = MLB_USIO_ESCR_L_8BIT; |
318 | break; |
319 | } |
320 | |
321 | if (termios->c_cflag & CSTOPB) |
322 | smr |= MLB_USIO_SMR_SBL; |
323 | |
324 | if (termios->c_cflag & PARENB) { |
325 | escr |= MLB_USIO_ESCR_PEN; |
326 | if (termios->c_cflag & PARODD) |
327 | escr |= MLB_USIO_ESCR_P; |
328 | } |
329 | /* Set hard flow control */ |
330 | if (of_property_read_bool(np: port->dev->of_node, propname: "auto-flow-control" ) || |
331 | (termios->c_cflag & CRTSCTS)) |
332 | escr |= MLB_USIO_ESCR_FLWEN; |
333 | |
334 | baud = uart_get_baud_rate(port, termios, old, min: 0, max: port->uartclk); |
335 | if (baud > 1) |
336 | quot = port->uartclk / baud - 1; |
337 | else |
338 | quot = 0; |
339 | |
340 | uart_port_lock_irqsave(up: port, flags: &flags); |
341 | uart_update_timeout(port, cflag: termios->c_cflag, baud); |
342 | port->read_status_mask = MLB_USIO_SSR_ORE | MLB_USIO_SSR_RDRF | |
343 | MLB_USIO_SSR_TDRE; |
344 | if (termios->c_iflag & INPCK) |
345 | port->read_status_mask |= MLB_USIO_SSR_FRE | MLB_USIO_SSR_PE; |
346 | |
347 | port->ignore_status_mask = 0; |
348 | if (termios->c_iflag & IGNPAR) |
349 | port->ignore_status_mask |= MLB_USIO_SSR_FRE | MLB_USIO_SSR_PE; |
350 | if ((termios->c_iflag & IGNBRK) && (termios->c_iflag & IGNPAR)) |
351 | port->ignore_status_mask |= MLB_USIO_SSR_ORE; |
352 | if ((termios->c_cflag & CREAD) == 0) |
353 | port->ignore_status_mask |= MLB_USIO_SSR_RDRF; |
354 | |
355 | writeb(val: 0, addr: port->membase + MLB_USIO_REG_SCR); |
356 | writeb(MLB_USIO_SCR_UPCL, addr: port->membase + MLB_USIO_REG_SCR); |
357 | writeb(MLB_USIO_SSR_REC, addr: port->membase + MLB_USIO_REG_SSR); |
358 | writew(val: 0, addr: port->membase + MLB_USIO_REG_FCR); |
359 | writeb(val: smr, addr: port->membase + MLB_USIO_REG_SMR); |
360 | writeb(val: escr, addr: port->membase + MLB_USIO_REG_ESCR); |
361 | writew(val: quot, addr: port->membase + MLB_USIO_REG_BGR); |
362 | writew(val: 0, addr: port->membase + MLB_USIO_REG_FCR); |
363 | writew(MLB_USIO_FCR_FCL1 | MLB_USIO_FCR_FCL2 | MLB_USIO_FCR_FE1 | |
364 | MLB_USIO_FCR_FE2 | MLB_USIO_FCR_FRIIE, |
365 | addr: port->membase + MLB_USIO_REG_FCR); |
366 | writew(val: 0, addr: port->membase + MLB_USIO_REG_FBYTE); |
367 | writew(BIT(12), addr: port->membase + MLB_USIO_REG_FBYTE); |
368 | writeb(MLB_USIO_SCR_RIE | MLB_USIO_SCR_RXE | MLB_USIO_SCR_TBIE | |
369 | MLB_USIO_SCR_TXE, addr: port->membase + MLB_USIO_REG_SCR); |
370 | uart_port_unlock_irqrestore(up: port, flags); |
371 | } |
372 | |
373 | static const char *mlb_usio_type(struct uart_port *port) |
374 | { |
375 | return ((port->type == PORT_MLB_USIO) ? USIO_NAME : NULL); |
376 | } |
377 | |
378 | static void mlb_usio_config_port(struct uart_port *port, int flags) |
379 | { |
380 | if (flags & UART_CONFIG_TYPE) |
381 | port->type = PORT_MLB_USIO; |
382 | } |
383 | |
384 | static const struct uart_ops mlb_usio_ops = { |
385 | .tx_empty = mlb_usio_tx_empty, |
386 | .set_mctrl = mlb_usio_set_mctrl, |
387 | .get_mctrl = mlb_usio_get_mctrl, |
388 | .stop_tx = mlb_usio_stop_tx, |
389 | .start_tx = mlb_usio_start_tx, |
390 | .stop_rx = mlb_usio_stop_rx, |
391 | .enable_ms = mlb_usio_enable_ms, |
392 | .break_ctl = mlb_usio_break_ctl, |
393 | .startup = mlb_usio_startup, |
394 | .shutdown = mlb_usio_shutdown, |
395 | .set_termios = mlb_usio_set_termios, |
396 | .type = mlb_usio_type, |
397 | .config_port = mlb_usio_config_port, |
398 | }; |
399 | |
400 | #ifdef CONFIG_SERIAL_MILBEAUT_USIO_CONSOLE |
401 | |
402 | static void mlb_usio_console_putchar(struct uart_port *port, unsigned char c) |
403 | { |
404 | while (!(readb(addr: port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TDRE)) |
405 | cpu_relax(); |
406 | |
407 | writew(val: c, addr: port->membase + MLB_USIO_REG_DR); |
408 | } |
409 | |
410 | static void mlb_usio_console_write(struct console *co, const char *s, |
411 | unsigned int count) |
412 | { |
413 | struct uart_port *port = &mlb_usio_ports[co->index]; |
414 | |
415 | uart_console_write(port, s, count, putchar: mlb_usio_console_putchar); |
416 | } |
417 | |
418 | static int __init mlb_usio_console_setup(struct console *co, char *options) |
419 | { |
420 | struct uart_port *port; |
421 | int baud = 115200; |
422 | int parity = 'n'; |
423 | int flow = 'n'; |
424 | int bits = 8; |
425 | |
426 | if (co->index >= CONFIG_SERIAL_MILBEAUT_USIO_PORTS) |
427 | return -ENODEV; |
428 | |
429 | port = &mlb_usio_ports[co->index]; |
430 | if (!port->membase) |
431 | return -ENODEV; |
432 | |
433 | |
434 | if (options) |
435 | uart_parse_options(options, baud: &baud, parity: &parity, bits: &bits, flow: &flow); |
436 | |
437 | if (of_property_read_bool(np: port->dev->of_node, propname: "auto-flow-control" )) |
438 | flow = 'r'; |
439 | |
440 | return uart_set_options(port, co, baud, parity, bits, flow); |
441 | } |
442 | |
443 | |
444 | static struct uart_driver mlb_usio_uart_driver; |
445 | static struct console mlb_usio_console = { |
446 | .name = USIO_UART_DEV_NAME, |
447 | .write = mlb_usio_console_write, |
448 | .device = uart_console_device, |
449 | .setup = mlb_usio_console_setup, |
450 | .flags = CON_PRINTBUFFER, |
451 | .index = -1, |
452 | .data = &mlb_usio_uart_driver, |
453 | }; |
454 | |
455 | static int __init mlb_usio_console_init(void) |
456 | { |
457 | register_console(&mlb_usio_console); |
458 | return 0; |
459 | } |
460 | console_initcall(mlb_usio_console_init); |
461 | |
462 | |
463 | static void mlb_usio_early_console_write(struct console *co, const char *s, |
464 | u_int count) |
465 | { |
466 | struct earlycon_device *dev = co->data; |
467 | |
468 | uart_console_write(port: &dev->port, s, count, putchar: mlb_usio_console_putchar); |
469 | } |
470 | |
471 | static int __init mlb_usio_early_console_setup(struct earlycon_device *device, |
472 | const char *opt) |
473 | { |
474 | if (!device->port.membase) |
475 | return -ENODEV; |
476 | device->con->write = mlb_usio_early_console_write; |
477 | return 0; |
478 | } |
479 | |
480 | OF_EARLYCON_DECLARE(mlb_usio, "socionext,milbeaut-usio-uart" , |
481 | mlb_usio_early_console_setup); |
482 | |
483 | #define USIO_CONSOLE (&mlb_usio_console) |
484 | #else |
485 | #define USIO_CONSOLE NULL |
486 | #endif |
487 | |
488 | static struct uart_driver mlb_usio_uart_driver = { |
489 | .owner = THIS_MODULE, |
490 | .driver_name = USIO_NAME, |
491 | .dev_name = USIO_UART_DEV_NAME, |
492 | .cons = USIO_CONSOLE, |
493 | .nr = CONFIG_SERIAL_MILBEAUT_USIO_PORTS, |
494 | }; |
495 | |
496 | static int mlb_usio_probe(struct platform_device *pdev) |
497 | { |
498 | struct clk *clk = devm_clk_get(dev: &pdev->dev, NULL); |
499 | struct uart_port *port; |
500 | struct resource *res; |
501 | int index = 0; |
502 | int ret; |
503 | |
504 | if (IS_ERR(ptr: clk)) { |
505 | dev_err(&pdev->dev, "Missing clock\n" ); |
506 | return PTR_ERR(ptr: clk); |
507 | } |
508 | ret = clk_prepare_enable(clk); |
509 | if (ret) { |
510 | dev_err(&pdev->dev, "Clock enable failed: %d\n" , ret); |
511 | return ret; |
512 | } |
513 | of_property_read_u32(np: pdev->dev.of_node, propname: "index" , out_value: &index); |
514 | port = &mlb_usio_ports[index]; |
515 | |
516 | port->private_data = (void *)clk; |
517 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
518 | if (res == NULL) { |
519 | dev_err(&pdev->dev, "Missing regs\n" ); |
520 | ret = -ENODEV; |
521 | goto failed; |
522 | } |
523 | port->membase = devm_ioremap(dev: &pdev->dev, offset: res->start, |
524 | size: resource_size(res)); |
525 | |
526 | ret = platform_get_irq_byname(pdev, "rx" ); |
527 | mlb_usio_irq[index][RX] = ret; |
528 | |
529 | ret = platform_get_irq_byname(pdev, "tx" ); |
530 | mlb_usio_irq[index][TX] = ret; |
531 | |
532 | port->irq = mlb_usio_irq[index][RX]; |
533 | port->uartclk = clk_get_rate(clk); |
534 | port->fifosize = 128; |
535 | port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_MILBEAUT_USIO_CONSOLE); |
536 | port->iotype = UPIO_MEM32; |
537 | port->flags = UPF_BOOT_AUTOCONF | UPF_SPD_VHI; |
538 | port->line = index; |
539 | port->ops = &mlb_usio_ops; |
540 | port->dev = &pdev->dev; |
541 | |
542 | ret = uart_add_one_port(reg: &mlb_usio_uart_driver, port); |
543 | if (ret) { |
544 | dev_err(&pdev->dev, "Adding port failed: %d\n" , ret); |
545 | goto failed; |
546 | } |
547 | return 0; |
548 | |
549 | failed: |
550 | clk_disable_unprepare(clk); |
551 | |
552 | return ret; |
553 | } |
554 | |
555 | static void mlb_usio_remove(struct platform_device *pdev) |
556 | { |
557 | struct uart_port *port = &mlb_usio_ports[pdev->id]; |
558 | struct clk *clk = port->private_data; |
559 | |
560 | uart_remove_one_port(reg: &mlb_usio_uart_driver, port); |
561 | clk_disable_unprepare(clk); |
562 | } |
563 | |
564 | static const struct of_device_id mlb_usio_dt_ids[] = { |
565 | { .compatible = "socionext,milbeaut-usio-uart" }, |
566 | { /* sentinel */ } |
567 | }; |
568 | MODULE_DEVICE_TABLE(of, mlb_usio_dt_ids); |
569 | |
570 | static struct platform_driver mlb_usio_driver = { |
571 | .probe = mlb_usio_probe, |
572 | .remove_new = mlb_usio_remove, |
573 | .driver = { |
574 | .name = USIO_NAME, |
575 | .of_match_table = mlb_usio_dt_ids, |
576 | }, |
577 | }; |
578 | |
579 | static int __init mlb_usio_init(void) |
580 | { |
581 | int ret = uart_register_driver(uart: &mlb_usio_uart_driver); |
582 | |
583 | if (ret) { |
584 | pr_err("%s: uart registration failed: %d\n" , __func__, ret); |
585 | return ret; |
586 | } |
587 | ret = platform_driver_register(&mlb_usio_driver); |
588 | if (ret) { |
589 | uart_unregister_driver(uart: &mlb_usio_uart_driver); |
590 | pr_err("%s: drv registration failed: %d\n" , __func__, ret); |
591 | return ret; |
592 | } |
593 | |
594 | return 0; |
595 | } |
596 | |
597 | static void __exit mlb_usio_exit(void) |
598 | { |
599 | platform_driver_unregister(&mlb_usio_driver); |
600 | uart_unregister_driver(uart: &mlb_usio_uart_driver); |
601 | } |
602 | |
603 | module_init(mlb_usio_init); |
604 | module_exit(mlb_usio_exit); |
605 | |
606 | MODULE_AUTHOR("SOCIONEXT" ); |
607 | MODULE_DESCRIPTION("MILBEAUT_USIO/UART Driver" ); |
608 | MODULE_LICENSE("GPL" ); |
609 | |