Commit 1bcda09d authored by Bich HEMON's avatar Bich HEMON Committed by Greg Kroah-Hartman

serial: stm32: add support for RS485 hardware control mode

Implement Driver Enable signal (DE) to activate the transmission mode
of the external transceiver.
Signed-off-by: default avatarYves Coppeaux <yves.coppeaux@st.com>
Signed-off-by: default avatarBich Hemon <bich.hemon@st.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 241672cb
...@@ -62,6 +62,113 @@ static void stm32_clr_bits(struct uart_port *port, u32 reg, u32 bits) ...@@ -62,6 +62,113 @@ static void stm32_clr_bits(struct uart_port *port, u32 reg, u32 bits)
writel_relaxed(val, port->membase + reg); 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 = &stm32_port->info->ofs;
struct stm32_usart_config *cfg = &stm32_port->info->cfg;
u32 usartdiv, baud, cr1, cr3;
bool over8;
unsigned long flags;
spin_lock_irqsave(&port->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(&cr1, &cr3,
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(&port->lock, flags);
return 0;
}
static int stm32_init_rs485(struct uart_port *port,
struct platform_device *pdev)
{
struct serial_rs485 *rs485conf = &port->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(&pdev->dev, rs485conf);
return 0;
}
static int stm32_pending_rx(struct uart_port *port, u32 *sr, int *last_res, static int stm32_pending_rx(struct uart_port *port, u32 *sr, int *last_res,
bool threaded) bool threaded)
{ {
...@@ -498,6 +605,7 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, ...@@ -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_port *stm32_port = to_stm32_port(port);
struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
struct stm32_usart_config *cfg = &stm32_port->info->cfg; struct stm32_usart_config *cfg = &stm32_port->info->cfg;
struct serial_rs485 *rs485conf = &port->rs485;
unsigned int baud; unsigned int baud;
u32 usartdiv, mantissa, fraction, oversampling; u32 usartdiv, mantissa, fraction, oversampling;
tcflag_t cflag = termios->c_cflag; tcflag_t cflag = termios->c_cflag;
...@@ -515,7 +623,7 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, ...@@ -515,7 +623,7 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
writel_relaxed(0, port->membase + ofs->cr1); writel_relaxed(0, port->membase + ofs->cr1);
cr1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE; cr1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE;
cr1 |= BIT(cfg->uart_enable_bit);
if (stm32_port->fifoen) if (stm32_port->fifoen)
cr1 |= USART_CR1_FIFOEN; cr1 |= USART_CR1_FIFOEN;
cr2 = 0; cr2 = 0;
...@@ -553,9 +661,11 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, ...@@ -553,9 +661,11 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
*/ */
if (usartdiv < 16) { if (usartdiv < 16) {
oversampling = 8; oversampling = 8;
cr1 |= USART_CR1_OVER8;
stm32_set_bits(port, ofs->cr1, USART_CR1_OVER8); stm32_set_bits(port, ofs->cr1, USART_CR1_OVER8);
} else { } else {
oversampling = 16; oversampling = 16;
cr1 &= ~USART_CR1_OVER8;
stm32_clr_bits(port, ofs->cr1, USART_CR1_OVER8); stm32_clr_bits(port, ofs->cr1, USART_CR1_OVER8);
} }
...@@ -592,10 +702,28 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, ...@@ -592,10 +702,28 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
if (stm32_port->rx_ch) if (stm32_port->rx_ch)
cr3 |= USART_CR3_DMAR; cr3 |= USART_CR3_DMAR;
if (rs485conf->flags & SER_RS485_ENABLED) {
stm32_config_reg_rs485(&cr1, &cr3,
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;
}
} else {
cr3 &= ~(USART_CR3_DEM | USART_CR3_DEP);
cr1 &= ~(USART_CR1_DEDT_MASK | USART_CR1_DEAT_MASK);
}
writel_relaxed(cr3, port->membase + ofs->cr3); writel_relaxed(cr3, port->membase + ofs->cr3);
writel_relaxed(cr2, port->membase + ofs->cr2); writel_relaxed(cr2, port->membase + ofs->cr2);
writel_relaxed(cr1, port->membase + ofs->cr1); writel_relaxed(cr1, port->membase + ofs->cr1);
stm32_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
spin_unlock_irqrestore(&port->lock, flags); spin_unlock_irqrestore(&port->lock, flags);
} }
......
...@@ -135,6 +135,7 @@ struct stm32_usart_info stm32h7_info = { ...@@ -135,6 +135,7 @@ struct stm32_usart_info stm32h7_info = {
#define USART_BRR_DIV_F_MASK GENMASK(3, 0) #define USART_BRR_DIV_F_MASK GENMASK(3, 0)
#define USART_BRR_DIV_M_MASK GENMASK(15, 4) #define USART_BRR_DIV_M_MASK GENMASK(15, 4)
#define USART_BRR_DIV_M_SHIFT 4 #define USART_BRR_DIV_M_SHIFT 4
#define USART_BRR_04_R_SHIFT 1
/* USART_CR1 */ /* USART_CR1 */
#define USART_CR1_SBK BIT(0) #define USART_CR1_SBK BIT(0)
...@@ -162,6 +163,8 @@ struct stm32_usart_info stm32h7_info = { ...@@ -162,6 +163,8 @@ struct stm32_usart_info stm32h7_info = {
#define USART_CR1_M1 BIT(28) /* F7 */ #define USART_CR1_M1 BIT(28) /* F7 */
#define USART_CR1_IE_MASK (GENMASK(8, 4) | BIT(14) | BIT(26) | BIT(27)) #define USART_CR1_IE_MASK (GENMASK(8, 4) | BIT(14) | BIT(26) | BIT(27))
#define USART_CR1_FIFOEN BIT(29) /* H7 */ #define USART_CR1_FIFOEN BIT(29) /* H7 */
#define USART_CR1_DEAT_SHIFT 21
#define USART_CR1_DEDT_SHIFT 16
/* USART_CR2 */ /* USART_CR2 */
#define USART_CR2_ADD_MASK GENMASK(3, 0) /* F4 */ #define USART_CR2_ADD_MASK GENMASK(3, 0) /* F4 */
......
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