[PATCH 2/2] serial: stm32: add support for RS485 hardware control mode

2018-02-28 Thread Bich HEMON
Implement Driver Enable signal (DE) to activate the transmission mode
of the external transceiver.

Signed-off-by: Yves Coppeaux 
Signed-off-by: Bich Hemon 
---
 drivers/tty/serial/stm32-usart.c | 134 ++-
 drivers/tty/serial/stm32-usart.h |   3 +
 2 files changed, 136 insertions(+), 1 deletion(-)

diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c
index 5c85cbc..6d34472 100644
--- a/drivers/tty/serial/stm32-usart.c
+++ b/drivers/tty/serial/stm32-usart.c
@@ -62,6 +62,113 @@ static void stm32_clr_bits(struct uart_port *port, u32 reg, 
u32 bits)
writel_relaxed(val, port->membase + reg);
 }
 
+static void stm32_config_reg_rs485(u32 *cr1, u32 *cr3, u32 delay_ADE,
+  u32 delay_DDE, u32 baud)
+{
+   u32 rs485_deat_dedt;
+   u32 rs485_deat_dedt_max = (USART_CR1_DEAT_MASK >> USART_CR1_DEAT_SHIFT);
+   bool over8;
+
+   *cr3 |= USART_CR3_DEM;
+   over8 = *cr1 & USART_CR1_OVER8;
+
+   if (over8)
+   rs485_deat_dedt = delay_ADE * baud * 8;
+   else
+   rs485_deat_dedt = delay_ADE * baud * 16;
+
+   rs485_deat_dedt = DIV_ROUND_CLOSEST(rs485_deat_dedt, 1000);
+   rs485_deat_dedt = rs485_deat_dedt > rs485_deat_dedt_max ?
+ rs485_deat_dedt_max : rs485_deat_dedt;
+   rs485_deat_dedt = (rs485_deat_dedt << USART_CR1_DEAT_SHIFT) &
+  USART_CR1_DEAT_MASK;
+   *cr1 |= rs485_deat_dedt;
+
+   if (over8)
+   rs485_deat_dedt = delay_DDE * baud * 8;
+   else
+   rs485_deat_dedt = delay_DDE * baud * 16;
+
+   rs485_deat_dedt = DIV_ROUND_CLOSEST(rs485_deat_dedt, 1000);
+   rs485_deat_dedt = rs485_deat_dedt > rs485_deat_dedt_max ?
+ rs485_deat_dedt_max : rs485_deat_dedt;
+   rs485_deat_dedt = (rs485_deat_dedt << USART_CR1_DEDT_SHIFT) &
+  USART_CR1_DEDT_MASK;
+   *cr1 |= rs485_deat_dedt;
+}
+
+static int stm32_config_rs485(struct uart_port *port,
+ struct serial_rs485 *rs485conf)
+{
+   struct stm32_port *stm32_port = to_stm32_port(port);
+   struct stm32_usart_offsets *ofs = _port->info->ofs;
+   struct stm32_usart_config *cfg = _port->info->cfg;
+   u32 usartdiv, baud, cr1, cr3;
+   bool over8;
+   unsigned long flags;
+
+   spin_lock_irqsave(>lock, flags);
+   stm32_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
+
+   port->rs485 = *rs485conf;
+
+   rs485conf->flags |= SER_RS485_RX_DURING_TX;
+
+   if (rs485conf->flags & SER_RS485_ENABLED) {
+   cr1 = readl_relaxed(port->membase + ofs->cr1);
+   cr3 = readl_relaxed(port->membase + ofs->cr3);
+   usartdiv = readl_relaxed(port->membase + ofs->brr);
+   usartdiv = usartdiv & GENMASK(15, 0);
+   over8 = cr1 & USART_CR1_OVER8;
+
+   if (over8)
+   usartdiv = usartdiv | (usartdiv & GENMASK(4, 0))
+  << USART_BRR_04_R_SHIFT;
+
+   baud = DIV_ROUND_CLOSEST(port->uartclk, usartdiv);
+   stm32_config_reg_rs485(, ,
+  rs485conf->delay_rts_before_send,
+  rs485conf->delay_rts_after_send, baud);
+
+   if (rs485conf->flags & SER_RS485_RTS_ON_SEND) {
+   cr3 &= ~USART_CR3_DEP;
+   rs485conf->flags &= ~SER_RS485_RTS_AFTER_SEND;
+   } else {
+   cr3 |= USART_CR3_DEP;
+   rs485conf->flags |= SER_RS485_RTS_AFTER_SEND;
+   }
+
+   writel_relaxed(cr3, port->membase + ofs->cr3);
+   writel_relaxed(cr1, port->membase + ofs->cr1);
+   } else {
+   stm32_clr_bits(port, ofs->cr3, USART_CR3_DEM | USART_CR3_DEP);
+   stm32_clr_bits(port, ofs->cr1,
+  USART_CR1_DEDT_MASK | USART_CR1_DEAT_MASK);
+   }
+
+   stm32_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
+   spin_unlock_irqrestore(>lock, flags);
+
+   return 0;
+}
+
+static int stm32_init_rs485(struct uart_port *port,
+   struct platform_device *pdev)
+{
+   struct serial_rs485 *rs485conf = >rs485;
+
+   rs485conf->flags = 0;
+   rs485conf->delay_rts_before_send = 0;
+   rs485conf->delay_rts_after_send = 0;
+
+   if (!pdev->dev.of_node)
+   return -ENODEV;
+
+   uart_get_rs485_mode(>dev, rs485conf);
+
+   return 0;
+}
+
 static int stm32_pending_rx(struct uart_port *port, u32 *sr, int *last_res,
bool threaded)
 {
@@ -498,6 +605,7 @@ static void stm32_set_termios(struct uart_port *port, 
struct ktermios *termios,
struct stm32_port *stm32_port = to_stm32_port(port);
struct stm32_usart_offsets 

[PATCH 2/2] serial: stm32: add support for RS485 hardware control mode

2018-02-28 Thread Bich HEMON
Implement Driver Enable signal (DE) to activate the transmission mode
of the external transceiver.

Signed-off-by: Yves Coppeaux 
Signed-off-by: Bich Hemon 
---
 drivers/tty/serial/stm32-usart.c | 134 ++-
 drivers/tty/serial/stm32-usart.h |   3 +
 2 files changed, 136 insertions(+), 1 deletion(-)

diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c
index 5c85cbc..6d34472 100644
--- a/drivers/tty/serial/stm32-usart.c
+++ b/drivers/tty/serial/stm32-usart.c
@@ -62,6 +62,113 @@ static void stm32_clr_bits(struct uart_port *port, u32 reg, 
u32 bits)
writel_relaxed(val, port->membase + reg);
 }
 
+static void stm32_config_reg_rs485(u32 *cr1, u32 *cr3, u32 delay_ADE,
+  u32 delay_DDE, u32 baud)
+{
+   u32 rs485_deat_dedt;
+   u32 rs485_deat_dedt_max = (USART_CR1_DEAT_MASK >> USART_CR1_DEAT_SHIFT);
+   bool over8;
+
+   *cr3 |= USART_CR3_DEM;
+   over8 = *cr1 & USART_CR1_OVER8;
+
+   if (over8)
+   rs485_deat_dedt = delay_ADE * baud * 8;
+   else
+   rs485_deat_dedt = delay_ADE * baud * 16;
+
+   rs485_deat_dedt = DIV_ROUND_CLOSEST(rs485_deat_dedt, 1000);
+   rs485_deat_dedt = rs485_deat_dedt > rs485_deat_dedt_max ?
+ rs485_deat_dedt_max : rs485_deat_dedt;
+   rs485_deat_dedt = (rs485_deat_dedt << USART_CR1_DEAT_SHIFT) &
+  USART_CR1_DEAT_MASK;
+   *cr1 |= rs485_deat_dedt;
+
+   if (over8)
+   rs485_deat_dedt = delay_DDE * baud * 8;
+   else
+   rs485_deat_dedt = delay_DDE * baud * 16;
+
+   rs485_deat_dedt = DIV_ROUND_CLOSEST(rs485_deat_dedt, 1000);
+   rs485_deat_dedt = rs485_deat_dedt > rs485_deat_dedt_max ?
+ rs485_deat_dedt_max : rs485_deat_dedt;
+   rs485_deat_dedt = (rs485_deat_dedt << USART_CR1_DEDT_SHIFT) &
+  USART_CR1_DEDT_MASK;
+   *cr1 |= rs485_deat_dedt;
+}
+
+static int stm32_config_rs485(struct uart_port *port,
+ struct serial_rs485 *rs485conf)
+{
+   struct stm32_port *stm32_port = to_stm32_port(port);
+   struct stm32_usart_offsets *ofs = _port->info->ofs;
+   struct stm32_usart_config *cfg = _port->info->cfg;
+   u32 usartdiv, baud, cr1, cr3;
+   bool over8;
+   unsigned long flags;
+
+   spin_lock_irqsave(>lock, flags);
+   stm32_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
+
+   port->rs485 = *rs485conf;
+
+   rs485conf->flags |= SER_RS485_RX_DURING_TX;
+
+   if (rs485conf->flags & SER_RS485_ENABLED) {
+   cr1 = readl_relaxed(port->membase + ofs->cr1);
+   cr3 = readl_relaxed(port->membase + ofs->cr3);
+   usartdiv = readl_relaxed(port->membase + ofs->brr);
+   usartdiv = usartdiv & GENMASK(15, 0);
+   over8 = cr1 & USART_CR1_OVER8;
+
+   if (over8)
+   usartdiv = usartdiv | (usartdiv & GENMASK(4, 0))
+  << USART_BRR_04_R_SHIFT;
+
+   baud = DIV_ROUND_CLOSEST(port->uartclk, usartdiv);
+   stm32_config_reg_rs485(, ,
+  rs485conf->delay_rts_before_send,
+  rs485conf->delay_rts_after_send, baud);
+
+   if (rs485conf->flags & SER_RS485_RTS_ON_SEND) {
+   cr3 &= ~USART_CR3_DEP;
+   rs485conf->flags &= ~SER_RS485_RTS_AFTER_SEND;
+   } else {
+   cr3 |= USART_CR3_DEP;
+   rs485conf->flags |= SER_RS485_RTS_AFTER_SEND;
+   }
+
+   writel_relaxed(cr3, port->membase + ofs->cr3);
+   writel_relaxed(cr1, port->membase + ofs->cr1);
+   } else {
+   stm32_clr_bits(port, ofs->cr3, USART_CR3_DEM | USART_CR3_DEP);
+   stm32_clr_bits(port, ofs->cr1,
+  USART_CR1_DEDT_MASK | USART_CR1_DEAT_MASK);
+   }
+
+   stm32_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
+   spin_unlock_irqrestore(>lock, flags);
+
+   return 0;
+}
+
+static int stm32_init_rs485(struct uart_port *port,
+   struct platform_device *pdev)
+{
+   struct serial_rs485 *rs485conf = >rs485;
+
+   rs485conf->flags = 0;
+   rs485conf->delay_rts_before_send = 0;
+   rs485conf->delay_rts_after_send = 0;
+
+   if (!pdev->dev.of_node)
+   return -ENODEV;
+
+   uart_get_rs485_mode(>dev, rs485conf);
+
+   return 0;
+}
+
 static int stm32_pending_rx(struct uart_port *port, u32 *sr, int *last_res,
bool threaded)
 {
@@ -498,6 +605,7 @@ static void stm32_set_termios(struct uart_port *port, 
struct ktermios *termios,
struct stm32_port *stm32_port = to_stm32_port(port);
struct stm32_usart_offsets *ofs = _port->info->ofs;
struct