mirror of https://gitee.com/openkylin/linux.git
serial: sh-sci: Fix support for hardware-assisted RTS/CTS
The existing support for hardware-assisted RTS/CTS is rudimentary and doesn't work. Add support for hardware-assisted RTS/CTS hardware flow control for the (H)SCIF, SCIFA, and SCIFB variants. Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
e9d7a45a03
commit
33f50ffc25
|
@ -141,6 +141,8 @@ struct sci_port {
|
||||||
struct timer_list rx_timer;
|
struct timer_list rx_timer;
|
||||||
unsigned int rx_timeout;
|
unsigned int rx_timeout;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
bool autorts;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define SCI_NPORTS CONFIG_SERIAL_SH_SCI_NR_UARTS
|
#define SCI_NPORTS CONFIG_SERIAL_SH_SCI_NR_UARTS
|
||||||
|
@ -1811,6 +1813,46 @@ static unsigned int sci_tx_empty(struct uart_port *port)
|
||||||
return (status & SCxSR_TEND(port)) && !in_tx_fifo ? TIOCSER_TEMT : 0;
|
return (status & SCxSR_TEND(port)) && !in_tx_fifo ? TIOCSER_TEMT : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sci_set_rts(struct uart_port *port, bool state)
|
||||||
|
{
|
||||||
|
if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
|
||||||
|
u16 data = serial_port_in(port, SCPDR);
|
||||||
|
|
||||||
|
/* Active low */
|
||||||
|
if (state)
|
||||||
|
data &= ~SCPDR_RTSD;
|
||||||
|
else
|
||||||
|
data |= SCPDR_RTSD;
|
||||||
|
serial_port_out(port, SCPDR, data);
|
||||||
|
|
||||||
|
/* RTS# is output */
|
||||||
|
serial_port_out(port, SCPCR,
|
||||||
|
serial_port_in(port, SCPCR) | SCPCR_RTSC);
|
||||||
|
} else if (sci_getreg(port, SCSPTR)->size) {
|
||||||
|
u16 ctrl = serial_port_in(port, SCSPTR);
|
||||||
|
|
||||||
|
/* Active low */
|
||||||
|
if (state)
|
||||||
|
ctrl &= ~SCSPTR_RTSDT;
|
||||||
|
else
|
||||||
|
ctrl |= SCSPTR_RTSDT;
|
||||||
|
serial_port_out(port, SCSPTR, ctrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool sci_get_cts(struct uart_port *port)
|
||||||
|
{
|
||||||
|
if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
|
||||||
|
/* Active low */
|
||||||
|
return !(serial_port_in(port, SCPDR) & SCPDR_CTSD);
|
||||||
|
} else if (sci_getreg(port, SCSPTR)->size) {
|
||||||
|
/* Active low */
|
||||||
|
return !(serial_port_in(port, SCSPTR) & SCSPTR_CTSDT);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Modem control is a bit of a mixed bag for SCI(F) ports. Generally
|
* Modem control is a bit of a mixed bag for SCI(F) ports. Generally
|
||||||
* CTS/RTS is supported in hardware by at least one port and controlled
|
* CTS/RTS is supported in hardware by at least one port and controlled
|
||||||
|
@ -1841,6 +1883,31 @@ static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
mctrl_gpio_set(s->gpios, mctrl);
|
mctrl_gpio_set(s->gpios, mctrl);
|
||||||
|
|
||||||
|
if (!(s->cfg->capabilities & SCIx_HAVE_RTSCTS))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!(mctrl & TIOCM_RTS)) {
|
||||||
|
/* Disable Auto RTS */
|
||||||
|
serial_port_out(port, SCFCR,
|
||||||
|
serial_port_in(port, SCFCR) & ~SCFCR_MCE);
|
||||||
|
|
||||||
|
/* Clear RTS */
|
||||||
|
sci_set_rts(port, 0);
|
||||||
|
} else if (s->autorts) {
|
||||||
|
if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
|
||||||
|
/* Enable RTS# pin function */
|
||||||
|
serial_port_out(port, SCPCR,
|
||||||
|
serial_port_in(port, SCPCR) & ~SCPCR_RTSC);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable Auto RTS */
|
||||||
|
serial_port_out(port, SCFCR,
|
||||||
|
serial_port_in(port, SCFCR) | SCFCR_MCE);
|
||||||
|
} else {
|
||||||
|
/* Set RTS */
|
||||||
|
sci_set_rts(port, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int sci_get_mctrl(struct uart_port *port)
|
static unsigned int sci_get_mctrl(struct uart_port *port)
|
||||||
|
@ -1853,10 +1920,14 @@ static unsigned int sci_get_mctrl(struct uart_port *port)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CTS/RTS is handled in hardware when supported, while nothing
|
* CTS/RTS is handled in hardware when supported, while nothing
|
||||||
* else is wired up. Keep it simple and simply assert CTS/DSR/CAR.
|
* else is wired up.
|
||||||
*/
|
*/
|
||||||
if (IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(gpios, UART_GPIO_CTS)))
|
if (s->autorts) {
|
||||||
|
if (sci_get_cts(port))
|
||||||
|
mctrl |= TIOCM_CTS;
|
||||||
|
} else if (IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(gpios, UART_GPIO_CTS))) {
|
||||||
mctrl |= TIOCM_CTS;
|
mctrl |= TIOCM_CTS;
|
||||||
|
}
|
||||||
if (IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(gpios, UART_GPIO_DSR)))
|
if (IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(gpios, UART_GPIO_DSR)))
|
||||||
mctrl |= TIOCM_DSR;
|
mctrl |= TIOCM_DSR;
|
||||||
if (IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(gpios, UART_GPIO_DCD)))
|
if (IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(gpios, UART_GPIO_DCD)))
|
||||||
|
@ -1927,6 +1998,7 @@ static void sci_shutdown(struct uart_port *port)
|
||||||
|
|
||||||
dev_dbg(port->dev, "%s(%d)\n", __func__, port->line);
|
dev_dbg(port->dev, "%s(%d)\n", __func__, port->line);
|
||||||
|
|
||||||
|
s->autorts = false;
|
||||||
mctrl_gpio_disable_ms(to_sci_port(port)->gpios);
|
mctrl_gpio_disable_ms(to_sci_port(port)->gpios);
|
||||||
|
|
||||||
spin_lock_irqsave(&port->lock, flags);
|
spin_lock_irqsave(&port->lock, flags);
|
||||||
|
@ -2248,15 +2320,18 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||||
|
|
||||||
sci_init_pins(port, termios->c_cflag);
|
sci_init_pins(port, termios->c_cflag);
|
||||||
|
|
||||||
|
port->status &= ~UPSTAT_AUTOCTS;
|
||||||
|
s->autorts = false;
|
||||||
reg = sci_getreg(port, SCFCR);
|
reg = sci_getreg(port, SCFCR);
|
||||||
if (reg->size) {
|
if (reg->size) {
|
||||||
unsigned short ctrl = serial_port_in(port, SCFCR);
|
unsigned short ctrl = serial_port_in(port, SCFCR);
|
||||||
|
|
||||||
if (s->cfg->capabilities & SCIx_HAVE_RTSCTS) {
|
if ((port->flags & UPF_HARD_FLOW) &&
|
||||||
if (termios->c_cflag & CRTSCTS)
|
(termios->c_cflag & CRTSCTS)) {
|
||||||
ctrl |= SCFCR_MCE;
|
/* There is no CTS interrupt to restart the hardware */
|
||||||
else
|
port->status |= UPSTAT_AUTOCTS;
|
||||||
ctrl &= ~SCFCR_MCE;
|
/* MCE is enabled when RTS is raised */
|
||||||
|
s->autorts = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2958,6 +3033,7 @@ static int sci_probe_single(struct platform_device *dev,
|
||||||
dev_err(&dev->dev, "Conflicting RTS/CTS config\n");
|
dev_err(&dev->dev, "Conflicting RTS/CTS config\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
sciport->port.flags |= UPF_HARD_FLOW;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = uart_add_one_port(&sci_uart_driver, &sciport->port);
|
ret = uart_add_one_port(&sci_uart_driver, &sciport->port);
|
||||||
|
|
Loading…
Reference in New Issue