1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /****************************************************************************/ |
3 | |
4 | /* |
5 | * mcf.c -- Freescale ColdFire UART driver |
6 | * |
7 | * (C) Copyright 2003-2007, Greg Ungerer <gerg@uclinux.org> |
8 | */ |
9 | |
10 | /****************************************************************************/ |
11 | |
12 | #include <linux/kernel.h> |
13 | #include <linux/init.h> |
14 | #include <linux/interrupt.h> |
15 | #include <linux/module.h> |
16 | #include <linux/console.h> |
17 | #include <linux/tty.h> |
18 | #include <linux/tty_flip.h> |
19 | #include <linux/serial.h> |
20 | #include <linux/serial_core.h> |
21 | #include <linux/io.h> |
22 | #include <linux/uaccess.h> |
23 | #include <linux/platform_device.h> |
24 | #include <asm/coldfire.h> |
25 | #include <asm/mcfsim.h> |
26 | #include <asm/mcfuart.h> |
27 | #include <asm/nettel.h> |
28 | |
29 | /****************************************************************************/ |
30 | |
31 | /* |
32 | * Some boards implement the DTR/DCD lines using GPIO lines, most |
33 | * don't. Dummy out the access macros for those that don't. Those |
34 | * that do should define these macros somewhere in there board |
35 | * specific inlude files. |
36 | */ |
37 | #if !defined(mcf_getppdcd) |
38 | #define mcf_getppdcd(p) (1) |
39 | #endif |
40 | #if !defined(mcf_getppdtr) |
41 | #define mcf_getppdtr(p) (1) |
42 | #endif |
43 | #if !defined(mcf_setppdtr) |
44 | #define mcf_setppdtr(p, v) do { } while (0) |
45 | #endif |
46 | |
47 | /****************************************************************************/ |
48 | |
49 | /* |
50 | * Local per-uart structure. |
51 | */ |
52 | struct mcf_uart { |
53 | struct uart_port port; |
54 | unsigned int sigs; /* Local copy of line sigs */ |
55 | unsigned char imr; /* Local IMR mirror */ |
56 | }; |
57 | |
58 | /****************************************************************************/ |
59 | |
60 | static unsigned int mcf_tx_empty(struct uart_port *port) |
61 | { |
62 | return (readb(port->membase + MCFUART_USR) & MCFUART_USR_TXEMPTY) ? |
63 | TIOCSER_TEMT : 0; |
64 | } |
65 | |
66 | /****************************************************************************/ |
67 | |
68 | static unsigned int mcf_get_mctrl(struct uart_port *port) |
69 | { |
70 | struct mcf_uart *pp = container_of(port, struct mcf_uart, port); |
71 | unsigned int sigs; |
72 | |
73 | sigs = (readb(port->membase + MCFUART_UIPR) & MCFUART_UIPR_CTS) ? |
74 | 0 : TIOCM_CTS; |
75 | sigs |= (pp->sigs & TIOCM_RTS); |
76 | sigs |= (mcf_getppdcd(port->line) ? TIOCM_CD : 0); |
77 | sigs |= (mcf_getppdtr(port->line) ? TIOCM_DTR : 0); |
78 | |
79 | return sigs; |
80 | } |
81 | |
82 | /****************************************************************************/ |
83 | |
84 | static void mcf_set_mctrl(struct uart_port *port, unsigned int sigs) |
85 | { |
86 | struct mcf_uart *pp = container_of(port, struct mcf_uart, port); |
87 | |
88 | pp->sigs = sigs; |
89 | mcf_setppdtr(port->line, (sigs & TIOCM_DTR)); |
90 | if (sigs & TIOCM_RTS) |
91 | writeb(val: MCFUART_UOP_RTS, addr: port->membase + MCFUART_UOP1); |
92 | else |
93 | writeb(val: MCFUART_UOP_RTS, addr: port->membase + MCFUART_UOP0); |
94 | } |
95 | |
96 | /****************************************************************************/ |
97 | |
98 | static void mcf_start_tx(struct uart_port *port) |
99 | { |
100 | struct mcf_uart *pp = container_of(port, struct mcf_uart, port); |
101 | |
102 | if (port->rs485.flags & SER_RS485_ENABLED) { |
103 | /* Enable Transmitter */ |
104 | writeb(val: MCFUART_UCR_TXENABLE, addr: port->membase + MCFUART_UCR); |
105 | /* Manually assert RTS */ |
106 | writeb(val: MCFUART_UOP_RTS, addr: port->membase + MCFUART_UOP1); |
107 | } |
108 | pp->imr |= MCFUART_UIR_TXREADY; |
109 | writeb(val: pp->imr, addr: port->membase + MCFUART_UIMR); |
110 | } |
111 | |
112 | /****************************************************************************/ |
113 | |
114 | static void mcf_stop_tx(struct uart_port *port) |
115 | { |
116 | struct mcf_uart *pp = container_of(port, struct mcf_uart, port); |
117 | |
118 | pp->imr &= ~MCFUART_UIR_TXREADY; |
119 | writeb(val: pp->imr, addr: port->membase + MCFUART_UIMR); |
120 | } |
121 | |
122 | /****************************************************************************/ |
123 | |
124 | static void mcf_stop_rx(struct uart_port *port) |
125 | { |
126 | struct mcf_uart *pp = container_of(port, struct mcf_uart, port); |
127 | |
128 | pp->imr &= ~MCFUART_UIR_RXREADY; |
129 | writeb(val: pp->imr, addr: port->membase + MCFUART_UIMR); |
130 | } |
131 | |
132 | /****************************************************************************/ |
133 | |
134 | static void mcf_break_ctl(struct uart_port *port, int break_state) |
135 | { |
136 | unsigned long flags; |
137 | |
138 | uart_port_lock_irqsave(up: port, flags: &flags); |
139 | if (break_state == -1) |
140 | writeb(val: MCFUART_UCR_CMDBREAKSTART, addr: port->membase + MCFUART_UCR); |
141 | else |
142 | writeb(val: MCFUART_UCR_CMDBREAKSTOP, addr: port->membase + MCFUART_UCR); |
143 | uart_port_unlock_irqrestore(up: port, flags); |
144 | } |
145 | |
146 | /****************************************************************************/ |
147 | |
148 | static int mcf_startup(struct uart_port *port) |
149 | { |
150 | struct mcf_uart *pp = container_of(port, struct mcf_uart, port); |
151 | unsigned long flags; |
152 | |
153 | uart_port_lock_irqsave(up: port, flags: &flags); |
154 | |
155 | /* Reset UART, get it into known state... */ |
156 | writeb(val: MCFUART_UCR_CMDRESETRX, addr: port->membase + MCFUART_UCR); |
157 | writeb(val: MCFUART_UCR_CMDRESETTX, addr: port->membase + MCFUART_UCR); |
158 | |
159 | /* Enable the UART transmitter and receiver */ |
160 | writeb(val: MCFUART_UCR_RXENABLE | MCFUART_UCR_TXENABLE, |
161 | addr: port->membase + MCFUART_UCR); |
162 | |
163 | /* Enable RX interrupts now */ |
164 | pp->imr = MCFUART_UIR_RXREADY; |
165 | writeb(val: pp->imr, addr: port->membase + MCFUART_UIMR); |
166 | |
167 | uart_port_unlock_irqrestore(up: port, flags); |
168 | |
169 | return 0; |
170 | } |
171 | |
172 | /****************************************************************************/ |
173 | |
174 | static void mcf_shutdown(struct uart_port *port) |
175 | { |
176 | struct mcf_uart *pp = container_of(port, struct mcf_uart, port); |
177 | unsigned long flags; |
178 | |
179 | uart_port_lock_irqsave(up: port, flags: &flags); |
180 | |
181 | /* Disable all interrupts now */ |
182 | pp->imr = 0; |
183 | writeb(val: pp->imr, addr: port->membase + MCFUART_UIMR); |
184 | |
185 | /* Disable UART transmitter and receiver */ |
186 | writeb(val: MCFUART_UCR_CMDRESETRX, addr: port->membase + MCFUART_UCR); |
187 | writeb(val: MCFUART_UCR_CMDRESETTX, addr: port->membase + MCFUART_UCR); |
188 | |
189 | uart_port_unlock_irqrestore(up: port, flags); |
190 | } |
191 | |
192 | /****************************************************************************/ |
193 | |
194 | static void mcf_set_termios(struct uart_port *port, struct ktermios *termios, |
195 | const struct ktermios *old) |
196 | { |
197 | unsigned long flags; |
198 | unsigned int baud, baudclk; |
199 | #if defined(CONFIG_M5272) |
200 | unsigned int baudfr; |
201 | #endif |
202 | unsigned char mr1, mr2; |
203 | |
204 | baud = uart_get_baud_rate(port, termios, old, min: 0, max: 230400); |
205 | #if defined(CONFIG_M5272) |
206 | baudclk = (MCF_BUSCLK / baud) / 32; |
207 | baudfr = (((MCF_BUSCLK / baud) + 1) / 2) % 16; |
208 | #else |
209 | baudclk = ((MCF_BUSCLK / baud) + 16) / 32; |
210 | #endif |
211 | |
212 | mr1 = MCFUART_MR1_RXIRQRDY | MCFUART_MR1_RXERRCHAR; |
213 | mr2 = 0; |
214 | |
215 | switch (termios->c_cflag & CSIZE) { |
216 | case CS5: mr1 |= MCFUART_MR1_CS5; break; |
217 | case CS6: mr1 |= MCFUART_MR1_CS6; break; |
218 | case CS7: mr1 |= MCFUART_MR1_CS7; break; |
219 | case CS8: |
220 | default: mr1 |= MCFUART_MR1_CS8; break; |
221 | } |
222 | |
223 | if (termios->c_cflag & PARENB) { |
224 | if (termios->c_cflag & CMSPAR) { |
225 | if (termios->c_cflag & PARODD) |
226 | mr1 |= MCFUART_MR1_PARITYMARK; |
227 | else |
228 | mr1 |= MCFUART_MR1_PARITYSPACE; |
229 | } else { |
230 | if (termios->c_cflag & PARODD) |
231 | mr1 |= MCFUART_MR1_PARITYODD; |
232 | else |
233 | mr1 |= MCFUART_MR1_PARITYEVEN; |
234 | } |
235 | } else { |
236 | mr1 |= MCFUART_MR1_PARITYNONE; |
237 | } |
238 | |
239 | /* |
240 | * FIXME: port->read_status_mask and port->ignore_status_mask |
241 | * need to be initialized based on termios settings for |
242 | * INPCK, IGNBRK, IGNPAR, PARMRK, BRKINT |
243 | */ |
244 | |
245 | if (termios->c_cflag & CSTOPB) |
246 | mr2 |= MCFUART_MR2_STOP2; |
247 | else |
248 | mr2 |= MCFUART_MR2_STOP1; |
249 | |
250 | if (termios->c_cflag & CRTSCTS) { |
251 | mr1 |= MCFUART_MR1_RXRTS; |
252 | mr2 |= MCFUART_MR2_TXCTS; |
253 | } |
254 | |
255 | uart_port_lock_irqsave(up: port, flags: &flags); |
256 | if (port->rs485.flags & SER_RS485_ENABLED) { |
257 | dev_dbg(port->dev, "Setting UART to RS485\n" ); |
258 | mr2 |= MCFUART_MR2_TXRTS; |
259 | } |
260 | |
261 | uart_update_timeout(port, cflag: termios->c_cflag, baud); |
262 | writeb(MCFUART_UCR_CMDRESETRX, port->membase + MCFUART_UCR); |
263 | writeb(MCFUART_UCR_CMDRESETTX, port->membase + MCFUART_UCR); |
264 | writeb(MCFUART_UCR_CMDRESETMRPTR, port->membase + MCFUART_UCR); |
265 | writeb(mr1, port->membase + MCFUART_UMR); |
266 | writeb(mr2, port->membase + MCFUART_UMR); |
267 | writeb((baudclk & 0xff00) >> 8, port->membase + MCFUART_UBG1); |
268 | writeb((baudclk & 0xff), port->membase + MCFUART_UBG2); |
269 | #if defined(CONFIG_M5272) |
270 | writeb((baudfr & 0x0f), port->membase + MCFUART_UFPD); |
271 | #endif |
272 | writeb(MCFUART_UCSR_RXCLKTIMER | MCFUART_UCSR_TXCLKTIMER, |
273 | port->membase + MCFUART_UCSR); |
274 | writeb(MCFUART_UCR_RXENABLE | MCFUART_UCR_TXENABLE, |
275 | port->membase + MCFUART_UCR); |
276 | uart_port_unlock_irqrestore(up: port, flags); |
277 | } |
278 | |
279 | /****************************************************************************/ |
280 | |
281 | static void mcf_rx_chars(struct mcf_uart *pp) |
282 | { |
283 | struct uart_port *port = &pp->port; |
284 | u8 status, ch, flag; |
285 | |
286 | while ((status = readb(port->membase + MCFUART_USR)) & MCFUART_USR_RXREADY) { |
287 | ch = readb(port->membase + MCFUART_URB); |
288 | flag = TTY_NORMAL; |
289 | port->icount.rx++; |
290 | |
291 | if (status & MCFUART_USR_RXERR) { |
292 | writeb(MCFUART_UCR_CMDRESETERR, |
293 | port->membase + MCFUART_UCR); |
294 | |
295 | if (status & MCFUART_USR_RXBREAK) { |
296 | port->icount.brk++; |
297 | if (uart_handle_break(port)) |
298 | continue; |
299 | } else if (status & MCFUART_USR_RXPARITY) { |
300 | port->icount.parity++; |
301 | } else if (status & MCFUART_USR_RXOVERRUN) { |
302 | port->icount.overrun++; |
303 | } else if (status & MCFUART_USR_RXFRAMING) { |
304 | port->icount.frame++; |
305 | } |
306 | |
307 | status &= port->read_status_mask; |
308 | |
309 | if (status & MCFUART_USR_RXBREAK) |
310 | flag = TTY_BREAK; |
311 | else if (status & MCFUART_USR_RXPARITY) |
312 | flag = TTY_PARITY; |
313 | else if (status & MCFUART_USR_RXFRAMING) |
314 | flag = TTY_FRAME; |
315 | } |
316 | |
317 | if (uart_handle_sysrq_char(port, ch)) |
318 | continue; |
319 | uart_insert_char(port, status, MCFUART_USR_RXOVERRUN, ch, flag); |
320 | } |
321 | |
322 | tty_flip_buffer_push(port: &port->state->port); |
323 | } |
324 | |
325 | /****************************************************************************/ |
326 | |
327 | static void mcf_tx_chars(struct mcf_uart *pp) |
328 | { |
329 | struct uart_port *port = &pp->port; |
330 | bool pending; |
331 | u8 ch; |
332 | |
333 | pending = uart_port_tx(port, ch, |
334 | readb(port->membase + MCFUART_USR) & MCFUART_USR_TXREADY, |
335 | writeb(ch, port->membase + MCFUART_UTB)); |
336 | |
337 | /* Disable TX to negate RTS automatically */ |
338 | if (!pending && (port->rs485.flags & SER_RS485_ENABLED)) |
339 | writeb(MCFUART_UCR_TXDISABLE, port->membase + MCFUART_UCR); |
340 | } |
341 | |
342 | /****************************************************************************/ |
343 | |
344 | static irqreturn_t mcf_interrupt(int irq, void *data) |
345 | { |
346 | struct uart_port *port = data; |
347 | struct mcf_uart *pp = container_of(port, struct mcf_uart, port); |
348 | unsigned int isr; |
349 | irqreturn_t ret = IRQ_NONE; |
350 | |
351 | isr = readb(port->membase + MCFUART_UISR) & pp->imr; |
352 | |
353 | uart_port_lock(up: port); |
354 | if (isr & MCFUART_UIR_RXREADY) { |
355 | mcf_rx_chars(pp); |
356 | ret = IRQ_HANDLED; |
357 | } |
358 | if (isr & MCFUART_UIR_TXREADY) { |
359 | mcf_tx_chars(pp); |
360 | ret = IRQ_HANDLED; |
361 | } |
362 | uart_port_unlock(up: port); |
363 | |
364 | return ret; |
365 | } |
366 | |
367 | /****************************************************************************/ |
368 | |
369 | static void mcf_config_port(struct uart_port *port, int flags) |
370 | { |
371 | port->type = PORT_MCF; |
372 | port->fifosize = MCFUART_TXFIFOSIZE; |
373 | |
374 | /* Clear mask, so no surprise interrupts. */ |
375 | writeb(0, port->membase + MCFUART_UIMR); |
376 | |
377 | if (request_irq(irq: port->irq, handler: mcf_interrupt, flags: 0, name: "UART" , dev: port)) |
378 | printk(KERN_ERR "MCF: unable to attach ColdFire UART %d " |
379 | "interrupt vector=%d\n" , port->line, port->irq); |
380 | } |
381 | |
382 | /****************************************************************************/ |
383 | |
384 | static const char *mcf_type(struct uart_port *port) |
385 | { |
386 | return (port->type == PORT_MCF) ? "ColdFire UART" : NULL; |
387 | } |
388 | |
389 | /****************************************************************************/ |
390 | |
391 | static int mcf_request_port(struct uart_port *port) |
392 | { |
393 | /* UARTs always present */ |
394 | return 0; |
395 | } |
396 | |
397 | /****************************************************************************/ |
398 | |
399 | static void mcf_release_port(struct uart_port *port) |
400 | { |
401 | /* Nothing to release... */ |
402 | } |
403 | |
404 | /****************************************************************************/ |
405 | |
406 | static int mcf_verify_port(struct uart_port *port, struct serial_struct *ser) |
407 | { |
408 | if ((ser->type != PORT_UNKNOWN) && (ser->type != PORT_MCF)) |
409 | return -EINVAL; |
410 | return 0; |
411 | } |
412 | |
413 | /****************************************************************************/ |
414 | |
415 | /* Enable or disable the RS485 support */ |
416 | static int mcf_config_rs485(struct uart_port *port, struct ktermios *termios, |
417 | struct serial_rs485 *rs485) |
418 | { |
419 | unsigned char mr1, mr2; |
420 | |
421 | /* Get mode registers */ |
422 | mr1 = readb(port->membase + MCFUART_UMR); |
423 | mr2 = readb(port->membase + MCFUART_UMR); |
424 | if (rs485->flags & SER_RS485_ENABLED) { |
425 | dev_dbg(port->dev, "Setting UART to RS485\n" ); |
426 | /* Automatically negate RTS after TX completes */ |
427 | mr2 |= MCFUART_MR2_TXRTS; |
428 | } else { |
429 | dev_dbg(port->dev, "Setting UART to RS232\n" ); |
430 | mr2 &= ~MCFUART_MR2_TXRTS; |
431 | } |
432 | writeb(mr1, port->membase + MCFUART_UMR); |
433 | writeb(mr2, port->membase + MCFUART_UMR); |
434 | |
435 | return 0; |
436 | } |
437 | |
438 | static const struct serial_rs485 mcf_rs485_supported = { |
439 | .flags = SER_RS485_ENABLED | SER_RS485_RTS_AFTER_SEND, |
440 | }; |
441 | |
442 | /****************************************************************************/ |
443 | |
444 | /* |
445 | * Define the basic serial functions we support. |
446 | */ |
447 | static const struct uart_ops mcf_uart_ops = { |
448 | .tx_empty = mcf_tx_empty, |
449 | .get_mctrl = mcf_get_mctrl, |
450 | .set_mctrl = mcf_set_mctrl, |
451 | .start_tx = mcf_start_tx, |
452 | .stop_tx = mcf_stop_tx, |
453 | .stop_rx = mcf_stop_rx, |
454 | .break_ctl = mcf_break_ctl, |
455 | .startup = mcf_startup, |
456 | .shutdown = mcf_shutdown, |
457 | .set_termios = mcf_set_termios, |
458 | .type = mcf_type, |
459 | .request_port = mcf_request_port, |
460 | .release_port = mcf_release_port, |
461 | .config_port = mcf_config_port, |
462 | .verify_port = mcf_verify_port, |
463 | }; |
464 | |
465 | static struct mcf_uart mcf_ports[4]; |
466 | |
467 | #define MCF_MAXPORTS ARRAY_SIZE(mcf_ports) |
468 | |
469 | /****************************************************************************/ |
470 | #if defined(CONFIG_SERIAL_MCF_CONSOLE) |
471 | /****************************************************************************/ |
472 | |
473 | int __init early_mcf_setup(struct mcf_platform_uart *platp) |
474 | { |
475 | struct uart_port *port; |
476 | int i; |
477 | |
478 | for (i = 0; ((i < MCF_MAXPORTS) && (platp[i].mapbase)); i++) { |
479 | port = &mcf_ports[i].port; |
480 | |
481 | port->line = i; |
482 | port->type = PORT_MCF; |
483 | port->mapbase = platp[i].mapbase; |
484 | port->membase = (platp[i].membase) ? platp[i].membase : |
485 | (unsigned char __iomem *) port->mapbase; |
486 | port->iotype = SERIAL_IO_MEM; |
487 | port->irq = platp[i].irq; |
488 | port->uartclk = MCF_BUSCLK; |
489 | port->flags = UPF_BOOT_AUTOCONF; |
490 | port->rs485_config = mcf_config_rs485; |
491 | port->rs485_supported = mcf_rs485_supported; |
492 | port->ops = &mcf_uart_ops; |
493 | } |
494 | |
495 | return 0; |
496 | } |
497 | |
498 | /****************************************************************************/ |
499 | |
500 | static void mcf_console_putc(struct console *co, const char c) |
501 | { |
502 | struct uart_port *port = &(mcf_ports + co->index)->port; |
503 | int i; |
504 | |
505 | for (i = 0; (i < 0x10000); i++) { |
506 | if (readb(port->membase + MCFUART_USR) & MCFUART_USR_TXREADY) |
507 | break; |
508 | } |
509 | writeb(c, port->membase + MCFUART_UTB); |
510 | for (i = 0; (i < 0x10000); i++) { |
511 | if (readb(port->membase + MCFUART_USR) & MCFUART_USR_TXREADY) |
512 | break; |
513 | } |
514 | } |
515 | |
516 | /****************************************************************************/ |
517 | |
518 | static void mcf_console_write(struct console *co, const char *s, unsigned int count) |
519 | { |
520 | for (; (count); count--, s++) { |
521 | mcf_console_putc(co, *s); |
522 | if (*s == '\n') |
523 | mcf_console_putc(co, '\r'); |
524 | } |
525 | } |
526 | |
527 | /****************************************************************************/ |
528 | |
529 | static int __init mcf_console_setup(struct console *co, char *options) |
530 | { |
531 | struct uart_port *port; |
532 | int baud = CONFIG_SERIAL_MCF_BAUDRATE; |
533 | int bits = 8; |
534 | int parity = 'n'; |
535 | int flow = 'n'; |
536 | |
537 | if ((co->index < 0) || (co->index >= MCF_MAXPORTS)) |
538 | co->index = 0; |
539 | port = &mcf_ports[co->index].port; |
540 | if (port->membase == 0) |
541 | return -ENODEV; |
542 | |
543 | if (options) |
544 | uart_parse_options(options, &baud, &parity, &bits, &flow); |
545 | |
546 | return uart_set_options(port, co, baud, parity, bits, flow); |
547 | } |
548 | |
549 | /****************************************************************************/ |
550 | |
551 | static struct uart_driver mcf_driver; |
552 | |
553 | static struct console mcf_console = { |
554 | .name = "ttyS" , |
555 | .write = mcf_console_write, |
556 | .device = uart_console_device, |
557 | .setup = mcf_console_setup, |
558 | .flags = CON_PRINTBUFFER, |
559 | .index = -1, |
560 | .data = &mcf_driver, |
561 | }; |
562 | |
563 | static int __init mcf_console_init(void) |
564 | { |
565 | register_console(&mcf_console); |
566 | return 0; |
567 | } |
568 | |
569 | console_initcall(mcf_console_init); |
570 | |
571 | #define MCF_CONSOLE &mcf_console |
572 | |
573 | /****************************************************************************/ |
574 | #else |
575 | /****************************************************************************/ |
576 | |
577 | #define MCF_CONSOLE NULL |
578 | |
579 | /****************************************************************************/ |
580 | #endif /* CONFIG_SERIAL_MCF_CONSOLE */ |
581 | /****************************************************************************/ |
582 | |
583 | /* |
584 | * Define the mcf UART driver structure. |
585 | */ |
586 | static struct uart_driver mcf_driver = { |
587 | .owner = THIS_MODULE, |
588 | .driver_name = "mcf" , |
589 | .dev_name = "ttyS" , |
590 | .major = TTY_MAJOR, |
591 | .minor = 64, |
592 | .nr = MCF_MAXPORTS, |
593 | .cons = MCF_CONSOLE, |
594 | }; |
595 | |
596 | /****************************************************************************/ |
597 | |
598 | static int mcf_probe(struct platform_device *pdev) |
599 | { |
600 | struct mcf_platform_uart *platp = dev_get_platdata(dev: &pdev->dev); |
601 | struct uart_port *port; |
602 | int i; |
603 | |
604 | for (i = 0; ((i < MCF_MAXPORTS) && (platp[i].mapbase)); i++) { |
605 | port = &mcf_ports[i].port; |
606 | |
607 | port->line = i; |
608 | port->type = PORT_MCF; |
609 | port->mapbase = platp[i].mapbase; |
610 | port->membase = (platp[i].membase) ? platp[i].membase : |
611 | (unsigned char __iomem *) platp[i].mapbase; |
612 | port->dev = &pdev->dev; |
613 | port->iotype = SERIAL_IO_MEM; |
614 | port->irq = platp[i].irq; |
615 | port->uartclk = MCF_BUSCLK; |
616 | port->ops = &mcf_uart_ops; |
617 | port->flags = UPF_BOOT_AUTOCONF; |
618 | port->rs485_config = mcf_config_rs485; |
619 | port->rs485_supported = mcf_rs485_supported; |
620 | port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_MCF_CONSOLE); |
621 | |
622 | uart_add_one_port(&mcf_driver, port); |
623 | } |
624 | |
625 | return 0; |
626 | } |
627 | |
628 | /****************************************************************************/ |
629 | |
630 | static int mcf_remove(struct platform_device *pdev) |
631 | { |
632 | struct uart_port *port; |
633 | int i; |
634 | |
635 | for (i = 0; (i < MCF_MAXPORTS); i++) { |
636 | port = &mcf_ports[i].port; |
637 | if (port) |
638 | uart_remove_one_port(reg: &mcf_driver, port); |
639 | } |
640 | |
641 | return 0; |
642 | } |
643 | |
644 | /****************************************************************************/ |
645 | |
646 | static struct platform_driver mcf_platform_driver = { |
647 | .probe = mcf_probe, |
648 | .remove = mcf_remove, |
649 | .driver = { |
650 | .name = "mcfuart" , |
651 | }, |
652 | }; |
653 | |
654 | /****************************************************************************/ |
655 | |
656 | static int __init mcf_init(void) |
657 | { |
658 | int rc; |
659 | |
660 | printk("ColdFire internal UART serial driver\n" ); |
661 | |
662 | rc = uart_register_driver(uart: &mcf_driver); |
663 | if (rc) |
664 | return rc; |
665 | rc = platform_driver_register(&mcf_platform_driver); |
666 | if (rc) { |
667 | uart_unregister_driver(uart: &mcf_driver); |
668 | return rc; |
669 | } |
670 | return 0; |
671 | } |
672 | |
673 | /****************************************************************************/ |
674 | |
675 | static void __exit mcf_exit(void) |
676 | { |
677 | platform_driver_unregister(&mcf_platform_driver); |
678 | uart_unregister_driver(uart: &mcf_driver); |
679 | } |
680 | |
681 | /****************************************************************************/ |
682 | |
683 | module_init(mcf_init); |
684 | module_exit(mcf_exit); |
685 | |
686 | MODULE_AUTHOR("Greg Ungerer <gerg@uclinux.org>" ); |
687 | MODULE_DESCRIPTION("Freescale ColdFire UART driver" ); |
688 | MODULE_LICENSE("GPL" ); |
689 | MODULE_ALIAS("platform:mcfuart" ); |
690 | |
691 | /****************************************************************************/ |
692 | |