diff options
Diffstat (limited to 'drivers/tty/serial/mcf.c')
-rw-r--r-- | drivers/tty/serial/mcf.c | 73 |
1 files changed, 72 insertions, 1 deletions
diff --git a/drivers/tty/serial/mcf.c b/drivers/tty/serial/mcf.c index fcd56ab6053f..e956377a38fe 100644 --- a/drivers/tty/serial/mcf.c +++ b/drivers/tty/serial/mcf.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/serial.h> | 23 | #include <linux/serial.h> |
24 | #include <linux/serial_core.h> | 24 | #include <linux/serial_core.h> |
25 | #include <linux/io.h> | 25 | #include <linux/io.h> |
26 | #include <linux/uaccess.h> | ||
26 | #include <asm/coldfire.h> | 27 | #include <asm/coldfire.h> |
27 | #include <asm/mcfsim.h> | 28 | #include <asm/mcfsim.h> |
28 | #include <asm/mcfuart.h> | 29 | #include <asm/mcfuart.h> |
@@ -55,6 +56,7 @@ struct mcf_uart { | |||
55 | struct uart_port port; | 56 | struct uart_port port; |
56 | unsigned int sigs; /* Local copy of line sigs */ | 57 | unsigned int sigs; /* Local copy of line sigs */ |
57 | unsigned char imr; /* Local IMR mirror */ | 58 | unsigned char imr; /* Local IMR mirror */ |
59 | struct serial_rs485 rs485; /* RS485 settings */ | ||
58 | }; | 60 | }; |
59 | 61 | ||
60 | /****************************************************************************/ | 62 | /****************************************************************************/ |
@@ -101,6 +103,12 @@ static void mcf_start_tx(struct uart_port *port) | |||
101 | { | 103 | { |
102 | struct mcf_uart *pp = container_of(port, struct mcf_uart, port); | 104 | struct mcf_uart *pp = container_of(port, struct mcf_uart, port); |
103 | 105 | ||
106 | if (pp->rs485.flags & SER_RS485_ENABLED) { | ||
107 | /* Enable Transmitter */ | ||
108 | writeb(MCFUART_UCR_TXENABLE, port->membase + MCFUART_UCR); | ||
109 | /* Manually assert RTS */ | ||
110 | writeb(MCFUART_UOP_RTS, port->membase + MCFUART_UOP1); | ||
111 | } | ||
104 | pp->imr |= MCFUART_UIR_TXREADY; | 112 | pp->imr |= MCFUART_UIR_TXREADY; |
105 | writeb(pp->imr, port->membase + MCFUART_UIMR); | 113 | writeb(pp->imr, port->membase + MCFUART_UIMR); |
106 | } | 114 | } |
@@ -196,6 +204,7 @@ static void mcf_shutdown(struct uart_port *port) | |||
196 | static void mcf_set_termios(struct uart_port *port, struct ktermios *termios, | 204 | static void mcf_set_termios(struct uart_port *port, struct ktermios *termios, |
197 | struct ktermios *old) | 205 | struct ktermios *old) |
198 | { | 206 | { |
207 | struct mcf_uart *pp = container_of(port, struct mcf_uart, port); | ||
199 | unsigned long flags; | 208 | unsigned long flags; |
200 | unsigned int baud, baudclk; | 209 | unsigned int baud, baudclk; |
201 | #if defined(CONFIG_M5272) | 210 | #if defined(CONFIG_M5272) |
@@ -248,6 +257,11 @@ static void mcf_set_termios(struct uart_port *port, struct ktermios *termios, | |||
248 | mr2 |= MCFUART_MR2_TXCTS; | 257 | mr2 |= MCFUART_MR2_TXCTS; |
249 | } | 258 | } |
250 | 259 | ||
260 | if (pp->rs485.flags & SER_RS485_ENABLED) { | ||
261 | dev_dbg(port->dev, "Setting UART to RS485\n"); | ||
262 | mr2 |= MCFUART_MR2_TXRTS; | ||
263 | } | ||
264 | |||
251 | spin_lock_irqsave(&port->lock, flags); | 265 | spin_lock_irqsave(&port->lock, flags); |
252 | uart_update_timeout(port, termios->c_cflag, baud); | 266 | uart_update_timeout(port, termios->c_cflag, baud); |
253 | writeb(MCFUART_UCR_CMDRESETRX, port->membase + MCFUART_UCR); | 267 | writeb(MCFUART_UCR_CMDRESETRX, port->membase + MCFUART_UCR); |
@@ -310,7 +324,7 @@ static void mcf_rx_chars(struct mcf_uart *pp) | |||
310 | uart_insert_char(port, status, MCFUART_USR_RXOVERRUN, ch, flag); | 324 | uart_insert_char(port, status, MCFUART_USR_RXOVERRUN, ch, flag); |
311 | } | 325 | } |
312 | 326 | ||
313 | tty_flip_buffer_push(port->state->port.tty); | 327 | tty_flip_buffer_push(&port->state->port); |
314 | } | 328 | } |
315 | 329 | ||
316 | /****************************************************************************/ | 330 | /****************************************************************************/ |
@@ -342,6 +356,10 @@ static void mcf_tx_chars(struct mcf_uart *pp) | |||
342 | if (xmit->head == xmit->tail) { | 356 | if (xmit->head == xmit->tail) { |
343 | pp->imr &= ~MCFUART_UIR_TXREADY; | 357 | pp->imr &= ~MCFUART_UIR_TXREADY; |
344 | writeb(pp->imr, port->membase + MCFUART_UIMR); | 358 | writeb(pp->imr, port->membase + MCFUART_UIMR); |
359 | /* Disable TX to negate RTS automatically */ | ||
360 | if (pp->rs485.flags & SER_RS485_ENABLED) | ||
361 | writeb(MCFUART_UCR_TXDISABLE, | ||
362 | port->membase + MCFUART_UCR); | ||
345 | } | 363 | } |
346 | } | 364 | } |
347 | 365 | ||
@@ -418,6 +436,58 @@ static int mcf_verify_port(struct uart_port *port, struct serial_struct *ser) | |||
418 | 436 | ||
419 | /****************************************************************************/ | 437 | /****************************************************************************/ |
420 | 438 | ||
439 | /* Enable or disable the RS485 support */ | ||
440 | static void mcf_config_rs485(struct uart_port *port, struct serial_rs485 *rs485) | ||
441 | { | ||
442 | struct mcf_uart *pp = container_of(port, struct mcf_uart, port); | ||
443 | unsigned long flags; | ||
444 | unsigned char mr1, mr2; | ||
445 | |||
446 | spin_lock_irqsave(&port->lock, flags); | ||
447 | /* Get mode registers */ | ||
448 | mr1 = readb(port->membase + MCFUART_UMR); | ||
449 | mr2 = readb(port->membase + MCFUART_UMR); | ||
450 | if (rs485->flags & SER_RS485_ENABLED) { | ||
451 | dev_dbg(port->dev, "Setting UART to RS485\n"); | ||
452 | /* Automatically negate RTS after TX completes */ | ||
453 | mr2 |= MCFUART_MR2_TXRTS; | ||
454 | } else { | ||
455 | dev_dbg(port->dev, "Setting UART to RS232\n"); | ||
456 | mr2 &= ~MCFUART_MR2_TXRTS; | ||
457 | } | ||
458 | writeb(mr1, port->membase + MCFUART_UMR); | ||
459 | writeb(mr2, port->membase + MCFUART_UMR); | ||
460 | pp->rs485 = *rs485; | ||
461 | spin_unlock_irqrestore(&port->lock, flags); | ||
462 | } | ||
463 | |||
464 | static int mcf_ioctl(struct uart_port *port, unsigned int cmd, | ||
465 | unsigned long arg) | ||
466 | { | ||
467 | switch (cmd) { | ||
468 | case TIOCSRS485: { | ||
469 | struct serial_rs485 rs485; | ||
470 | if (copy_from_user(&rs485, (struct serial_rs485 *)arg, | ||
471 | sizeof(struct serial_rs485))) | ||
472 | return -EFAULT; | ||
473 | mcf_config_rs485(port, &rs485); | ||
474 | break; | ||
475 | } | ||
476 | case TIOCGRS485: { | ||
477 | struct mcf_uart *pp = container_of(port, struct mcf_uart, port); | ||
478 | if (copy_to_user((struct serial_rs485 *)arg, &pp->rs485, | ||
479 | sizeof(struct serial_rs485))) | ||
480 | return -EFAULT; | ||
481 | break; | ||
482 | } | ||
483 | default: | ||
484 | return -ENOIOCTLCMD; | ||
485 | } | ||
486 | return 0; | ||
487 | } | ||
488 | |||
489 | /****************************************************************************/ | ||
490 | |||
421 | /* | 491 | /* |
422 | * Define the basic serial functions we support. | 492 | * Define the basic serial functions we support. |
423 | */ | 493 | */ |
@@ -438,6 +508,7 @@ static const struct uart_ops mcf_uart_ops = { | |||
438 | .release_port = mcf_release_port, | 508 | .release_port = mcf_release_port, |
439 | .config_port = mcf_config_port, | 509 | .config_port = mcf_config_port, |
440 | .verify_port = mcf_verify_port, | 510 | .verify_port = mcf_verify_port, |
511 | .ioctl = mcf_ioctl, | ||
441 | }; | 512 | }; |
442 | 513 | ||
443 | static struct mcf_uart mcf_ports[4]; | 514 | static struct mcf_uart mcf_ports[4]; |