diff --git a/drivers/tty/serial/mcf.c b/drivers/tty/serial/mcf.c index 7ed99274572f39b0427b904d1ae5423e75a7c3d2..e956377a38fe53e299766724c278a05a35d802c4 100644 --- a/drivers/tty/serial/mcf.c +++ b/drivers/tty/serial/mcf.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,7 @@ struct mcf_uart { struct uart_port port; unsigned int sigs; /* Local copy of line sigs */ unsigned char imr; /* Local IMR mirror */ + struct serial_rs485 rs485; /* RS485 settings */ }; /****************************************************************************/ @@ -101,6 +103,12 @@ static void mcf_start_tx(struct uart_port *port) { struct mcf_uart *pp = container_of(port, struct mcf_uart, port); + if (pp->rs485.flags & SER_RS485_ENABLED) { + /* Enable Transmitter */ + writeb(MCFUART_UCR_TXENABLE, port->membase + MCFUART_UCR); + /* Manually assert RTS */ + writeb(MCFUART_UOP_RTS, port->membase + MCFUART_UOP1); + } pp->imr |= MCFUART_UIR_TXREADY; writeb(pp->imr, port->membase + MCFUART_UIMR); } @@ -196,6 +204,7 @@ static void mcf_shutdown(struct uart_port *port) static void mcf_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { + struct mcf_uart *pp = container_of(port, struct mcf_uart, port); unsigned long flags; unsigned int baud, baudclk; #if defined(CONFIG_M5272) @@ -248,6 +257,11 @@ static void mcf_set_termios(struct uart_port *port, struct ktermios *termios, mr2 |= MCFUART_MR2_TXCTS; } + if (pp->rs485.flags & SER_RS485_ENABLED) { + dev_dbg(port->dev, "Setting UART to RS485\n"); + mr2 |= MCFUART_MR2_TXRTS; + } + spin_lock_irqsave(&port->lock, flags); uart_update_timeout(port, termios->c_cflag, baud); writeb(MCFUART_UCR_CMDRESETRX, port->membase + MCFUART_UCR); @@ -342,6 +356,10 @@ static void mcf_tx_chars(struct mcf_uart *pp) if (xmit->head == xmit->tail) { pp->imr &= ~MCFUART_UIR_TXREADY; writeb(pp->imr, port->membase + MCFUART_UIMR); + /* Disable TX to negate RTS automatically */ + if (pp->rs485.flags & SER_RS485_ENABLED) + writeb(MCFUART_UCR_TXDISABLE, + port->membase + MCFUART_UCR); } } @@ -418,6 +436,58 @@ static int mcf_verify_port(struct uart_port *port, struct serial_struct *ser) /****************************************************************************/ +/* Enable or disable the RS485 support */ +static void mcf_config_rs485(struct uart_port *port, struct serial_rs485 *rs485) +{ + struct mcf_uart *pp = container_of(port, struct mcf_uart, port); + unsigned long flags; + unsigned char mr1, mr2; + + spin_lock_irqsave(&port->lock, flags); + /* Get mode registers */ + mr1 = readb(port->membase + MCFUART_UMR); + mr2 = readb(port->membase + MCFUART_UMR); + if (rs485->flags & SER_RS485_ENABLED) { + dev_dbg(port->dev, "Setting UART to RS485\n"); + /* Automatically negate RTS after TX completes */ + mr2 |= MCFUART_MR2_TXRTS; + } else { + dev_dbg(port->dev, "Setting UART to RS232\n"); + mr2 &= ~MCFUART_MR2_TXRTS; + } + writeb(mr1, port->membase + MCFUART_UMR); + writeb(mr2, port->membase + MCFUART_UMR); + pp->rs485 = *rs485; + spin_unlock_irqrestore(&port->lock, flags); +} + +static int mcf_ioctl(struct uart_port *port, unsigned int cmd, + unsigned long arg) +{ + switch (cmd) { + case TIOCSRS485: { + struct serial_rs485 rs485; + if (copy_from_user(&rs485, (struct serial_rs485 *)arg, + sizeof(struct serial_rs485))) + return -EFAULT; + mcf_config_rs485(port, &rs485); + break; + } + case TIOCGRS485: { + struct mcf_uart *pp = container_of(port, struct mcf_uart, port); + if (copy_to_user((struct serial_rs485 *)arg, &pp->rs485, + sizeof(struct serial_rs485))) + return -EFAULT; + break; + } + default: + return -ENOIOCTLCMD; + } + return 0; +} + +/****************************************************************************/ + /* * Define the basic serial functions we support. */ @@ -438,6 +508,7 @@ static const struct uart_ops mcf_uart_ops = { .release_port = mcf_release_port, .config_port = mcf_config_port, .verify_port = mcf_verify_port, + .ioctl = mcf_ioctl, }; static struct mcf_uart mcf_ports[4];