Commit 6cf61b9b authored by Manivannan Sadhasivam's avatar Manivannan Sadhasivam Committed by Greg Kroah-Hartman

tty: serial: Add modem control gpio support for STM32 UART

STM32 UART controllers have the built in modem control support using
dedicated gpios which can be enabled using 'st,hw-flow-ctrl' flag in DT.
But there might be cases where the board design need to use different
gpios for modem control.

For supporting such cases, this commit adds modem control gpio support
to STM32 UART controller using mctrl_gpio driver.
Reviewed-by: default avatarAndy Shevchenko <andy.shevchenko@gmail.com>
Signed-off-by: default avatarManivannan Sadhasivam <mani@kernel.org>
Acked-by: default avatarFabrice Gasnier <fabrice.gasnier@st.com>
Link: https://lore.kernel.org/r/20200420170204.24541-3-mani@kernel.orgSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 888ae871
...@@ -1471,6 +1471,7 @@ config SERIAL_STM32 ...@@ -1471,6 +1471,7 @@ config SERIAL_STM32
tristate "STMicroelectronics STM32 serial port support" tristate "STMicroelectronics STM32 serial port support"
select SERIAL_CORE select SERIAL_CORE
depends on ARCH_STM32 || COMPILE_TEST depends on ARCH_STM32 || COMPILE_TEST
select SERIAL_MCTRL_GPIO if GPIOLIB
help help
This driver is for the on-chip Serial Controller on This driver is for the on-chip Serial Controller on
STMicroelectronics STM32 MCUs. STMicroelectronics STM32 MCUs.
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <linux/tty_flip.h> #include <linux/tty_flip.h>
#include <linux/tty.h> #include <linux/tty.h>
#include "serial_mctrl_gpio.h"
#include "stm32-usart.h" #include "stm32-usart.h"
static void stm32_stop_tx(struct uart_port *port); static void stm32_stop_tx(struct uart_port *port);
...@@ -510,12 +511,29 @@ static void stm32_set_mctrl(struct uart_port *port, unsigned int mctrl) ...@@ -510,12 +511,29 @@ static void stm32_set_mctrl(struct uart_port *port, unsigned int mctrl)
stm32_set_bits(port, ofs->cr3, USART_CR3_RTSE); stm32_set_bits(port, ofs->cr3, USART_CR3_RTSE);
else else
stm32_clr_bits(port, ofs->cr3, USART_CR3_RTSE); stm32_clr_bits(port, ofs->cr3, USART_CR3_RTSE);
mctrl_gpio_set(stm32_port->gpios, mctrl);
} }
static unsigned int stm32_get_mctrl(struct uart_port *port) static unsigned int stm32_get_mctrl(struct uart_port *port)
{ {
struct stm32_port *stm32_port = to_stm32_port(port);
unsigned int ret;
/* This routine is used to get signals of: DCD, DSR, RI, and CTS */ /* This routine is used to get signals of: DCD, DSR, RI, and CTS */
return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; ret = TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
return mctrl_gpio_get(stm32_port->gpios, &ret);
}
static void stm32_enable_ms(struct uart_port *port)
{
mctrl_gpio_enable_ms(to_stm32_port(port)->gpios);
}
static void stm32_disable_ms(struct uart_port *port)
{
mctrl_gpio_disable_ms(to_stm32_port(port)->gpios);
} }
/* Transmit stop */ /* Transmit stop */
...@@ -626,6 +644,9 @@ static void stm32_shutdown(struct uart_port *port) ...@@ -626,6 +644,9 @@ static void stm32_shutdown(struct uart_port *port)
u32 val, isr; u32 val, isr;
int ret; int ret;
/* Disable modem control interrupts */
stm32_disable_ms(port);
val = USART_CR1_TXEIE | USART_CR1_TE; val = USART_CR1_TXEIE | USART_CR1_TE;
val |= stm32_port->cr1_irq | USART_CR1_RE; val |= stm32_port->cr1_irq | USART_CR1_RE;
val |= BIT(cfg->uart_enable_bit); val |= BIT(cfg->uart_enable_bit);
...@@ -764,6 +785,12 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, ...@@ -764,6 +785,12 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
cr3 |= USART_CR3_CTSE | USART_CR3_RTSE; cr3 |= USART_CR3_CTSE | USART_CR3_RTSE;
} }
/* Handle modem control interrupts */
if (UART_ENABLE_MS(port, termios->c_cflag))
stm32_enable_ms(port);
else
stm32_disable_ms(port);
usartdiv = DIV_ROUND_CLOSEST(port->uartclk, baud); usartdiv = DIV_ROUND_CLOSEST(port->uartclk, baud);
/* /*
...@@ -898,6 +925,7 @@ static const struct uart_ops stm32_uart_ops = { ...@@ -898,6 +925,7 @@ static const struct uart_ops stm32_uart_ops = {
.throttle = stm32_throttle, .throttle = stm32_throttle,
.unthrottle = stm32_unthrottle, .unthrottle = stm32_unthrottle,
.stop_rx = stm32_stop_rx, .stop_rx = stm32_stop_rx,
.enable_ms = stm32_enable_ms,
.break_ctl = stm32_break_ctl, .break_ctl = stm32_break_ctl,
.startup = stm32_startup, .startup = stm32_startup,
.shutdown = stm32_shutdown, .shutdown = stm32_shutdown,
...@@ -960,10 +988,31 @@ static int stm32_init_port(struct stm32_port *stm32port, ...@@ -960,10 +988,31 @@ static int stm32_init_port(struct stm32_port *stm32port,
stm32port->port.uartclk = clk_get_rate(stm32port->clk); stm32port->port.uartclk = clk_get_rate(stm32port->clk);
if (!stm32port->port.uartclk) { if (!stm32port->port.uartclk) {
clk_disable_unprepare(stm32port->clk);
ret = -EINVAL; ret = -EINVAL;
goto err_clk;
}
stm32port->gpios = mctrl_gpio_init(&stm32port->port, 0);
if (IS_ERR(stm32port->gpios)) {
ret = PTR_ERR(stm32port->gpios);
goto err_clk;
} }
/* Both CTS/RTS gpios and "st,hw-flow-ctrl" should not be specified */
if (stm32port->hw_flow_control) {
if (mctrl_gpio_to_gpiod(stm32port->gpios, UART_GPIO_CTS) ||
mctrl_gpio_to_gpiod(stm32port->gpios, UART_GPIO_RTS)) {
dev_err(&pdev->dev, "Conflicting RTS/CTS config\n");
ret = -EINVAL;
goto err_clk;
}
}
return ret;
err_clk:
clk_disable_unprepare(stm32port->clk);
return ret; return ret;
} }
......
...@@ -274,6 +274,7 @@ struct stm32_port { ...@@ -274,6 +274,7 @@ struct stm32_port {
bool fifoen; bool fifoen;
int wakeirq; int wakeirq;
int rdr_mask; /* receive data register mask */ int rdr_mask; /* receive data register mask */
struct mctrl_gpios *gpios; /* modem control gpios */
}; };
static struct stm32_port stm32_ports[STM32_MAX_PORTS]; static struct stm32_port stm32_ports[STM32_MAX_PORTS];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment