2024-01-11 15:28:02

by Valentin Caron

[permalink] [raw]
Subject: [PATCH 0/4] serial: stm32: improve driver for stm32mp25

STM32MP25 got the same serial hardware block as STM32MP1x but with two improvements:
- TX and RX FIFO have been extended to 64 bytes.
- one instance more than in STM32MP1x series.

Valentin Caron (4):
serial: stm32: implement prescaler tuning to compute low baudrate
serial: stm32: extend max number of U(S)ART to 9
serial: stm32: change register's offset type from u8 to u16
serial: stm32: get FIFO size from hwcfg register

drivers/tty/serial/stm32-usart.c | 222 +++++++++++++++++++------------
drivers/tty/serial/stm32-usart.h | 38 ++++--
2 files changed, 159 insertions(+), 101 deletions(-)

--
2.25.1



2024-01-11 15:28:09

by Valentin Caron

[permalink] [raw]
Subject: [PATCH 2/4] serial: stm32: extend max number of U(S)ART to 9

STM32MP25x got 9 instances of U(S)ART. So extend STM32_MAX_PORTS to 9, in
order to handle all instances.

Signed-off-by: Valentin Caron <[email protected]>
---
drivers/tty/serial/stm32-usart.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/tty/serial/stm32-usart.h b/drivers/tty/serial/stm32-usart.h
index 8cecfdce9386..5ce1d3aae07e 100644
--- a/drivers/tty/serial/stm32-usart.h
+++ b/drivers/tty/serial/stm32-usart.h
@@ -183,7 +183,7 @@ struct stm32_usart_info {
#define USART_PRESC_MAX 0b1011

#define STM32_SERIAL_NAME "ttySTM"
-#define STM32_MAX_PORTS 8
+#define STM32_MAX_PORTS 9

#define RX_BUF_L 4096 /* dma rx buffer length */
#define RX_BUF_P (RX_BUF_L / 2) /* dma rx buffer period */
--
2.25.1


2024-01-11 15:28:24

by Valentin Caron

[permalink] [raw]
Subject: [PATCH 3/4] serial: stm32: change register's offset type from u8 to u16

USART has registers above 0xff offset, so extend variable type to u16.
And change UNDEF_REG to 0xffff.

Signed-off-by: Valentin Caron <[email protected]>
---
drivers/tty/serial/stm32-usart.h | 26 +++++++++++++-------------
1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/drivers/tty/serial/stm32-usart.h b/drivers/tty/serial/stm32-usart.h
index 5ce1d3aae07e..bf3720948704 100644
--- a/drivers/tty/serial/stm32-usart.h
+++ b/drivers/tty/serial/stm32-usart.h
@@ -9,18 +9,18 @@
#define DRIVER_NAME "stm32-usart"

struct stm32_usart_offsets {
- u8 cr1;
- u8 cr2;
- u8 cr3;
- u8 brr;
- u8 gtpr;
- u8 rtor;
- u8 rqr;
- u8 isr;
- u8 icr;
- u8 rdr;
- u8 tdr;
- u8 presc;
+ u16 cr1;
+ u16 cr2;
+ u16 cr3;
+ u16 brr;
+ u16 gtpr;
+ u16 rtor;
+ u16 rqr;
+ u16 isr;
+ u16 icr;
+ u16 rdr;
+ u16 tdr;
+ u16 presc;
};

struct stm32_usart_config {
@@ -37,7 +37,7 @@ struct stm32_usart_info {
struct stm32_usart_config cfg;
};

-#define UNDEF_REG 0xff
+#define UNDEF_REG 0xffff

/* USART_SR (F4) / USART_ISR (F7) */
#define USART_SR_PE BIT(0)
--
2.25.1


2024-01-11 15:28:36

by Valentin Caron

[permalink] [raw]
Subject: [PATCH 1/4] serial: stm32: implement prescaler tuning to compute low baudrate

In the case of high USART input clock and low baud rate, BRR value
is not enough to get correct baud rate. So here we use USART prescaler to
divide USART input clock to get the correct baud rate.

PRESC register is only available since stm32h7.

Signed-off-by: Valentin Caron <[email protected]>
---
drivers/tty/serial/stm32-usart.c | 70 ++++++++++++++++++++++----------
drivers/tty/serial/stm32-usart.h | 6 +++
2 files changed, 55 insertions(+), 21 deletions(-)

diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c
index 794b77512740..e8ab5efad945 100644
--- a/drivers/tty/serial/stm32-usart.c
+++ b/drivers/tty/serial/stm32-usart.c
@@ -50,6 +50,7 @@ static struct stm32_usart_info __maybe_unused stm32f4_info = {
.rtor = UNDEF_REG,
.rqr = UNDEF_REG,
.icr = UNDEF_REG,
+ .presc = UNDEF_REG,
},
.cfg = {
.uart_enable_bit = 13,
@@ -71,6 +72,7 @@ static struct stm32_usart_info __maybe_unused stm32f7_info = {
.icr = 0x20,
.rdr = 0x24,
.tdr = 0x28,
+ .presc = UNDEF_REG,
},
.cfg = {
.uart_enable_bit = 0,
@@ -93,6 +95,7 @@ static struct stm32_usart_info __maybe_unused stm32h7_info = {
.icr = 0x20,
.rdr = 0x24,
.tdr = 0x28,
+ .presc = 0x2c,
},
.cfg = {
.uart_enable_bit = 0,
@@ -1145,6 +1148,8 @@ static void stm32_usart_shutdown(struct uart_port *port)
free_irq(port->irq, port);
}

+static const unsigned int stm32_usart_presc_val[] = {1, 2, 4, 6, 8, 10, 12, 16, 32, 64, 128, 256};
+
static void stm32_usart_set_termios(struct uart_port *port,
struct ktermios *termios,
const struct ktermios *old)
@@ -1153,17 +1158,19 @@ static void stm32_usart_set_termios(struct uart_port *port,
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
const struct stm32_usart_config *cfg = &stm32_port->info->cfg;
struct serial_rs485 *rs485conf = &port->rs485;
- unsigned int baud, bits;
+ unsigned int baud, bits, uart_clk, uart_clk_pres;
u32 usartdiv, mantissa, fraction, oversampling;
tcflag_t cflag = termios->c_cflag;
- u32 cr1, cr2, cr3, isr;
+ u32 cr1, cr2, cr3, isr, brr, presc;
unsigned long flags;
int ret;

if (!stm32_port->hw_flow_control)
cflag &= ~CRTSCTS;

- baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 8);
+ uart_clk = clk_get_rate(stm32_port->clk);
+
+ baud = uart_get_baud_rate(port, termios, old, 0, uart_clk / 8);

uart_port_lock_irqsave(port, &flags);

@@ -1265,27 +1272,48 @@ static void stm32_usart_set_termios(struct uart_port *port,
cr3 |= USART_CR3_CTSE | USART_CR3_RTSE;
}

- usartdiv = DIV_ROUND_CLOSEST(port->uartclk, baud);
+ for (presc = 0; presc <= USART_PRESC_MAX; presc++) {
+ uart_clk_pres = DIV_ROUND_CLOSEST(uart_clk, stm32_usart_presc_val[presc]);
+ usartdiv = DIV_ROUND_CLOSEST(uart_clk_pres, baud);

- /*
- * The USART supports 16 or 8 times oversampling.
- * By default we prefer 16 times oversampling, so that the receiver
- * has a better tolerance to clock deviations.
- * 8 times oversampling is only used to achieve higher speeds.
- */
- if (usartdiv < 16) {
- oversampling = 8;
- cr1 |= USART_CR1_OVER8;
- stm32_usart_set_bits(port, ofs->cr1, USART_CR1_OVER8);
- } else {
- oversampling = 16;
- cr1 &= ~USART_CR1_OVER8;
- stm32_usart_clr_bits(port, ofs->cr1, USART_CR1_OVER8);
+ /*
+ * The USART supports 16 or 8 times oversampling.
+ * By default we prefer 16 times oversampling, so that the receiver
+ * has a better tolerance to clock deviations.
+ * 8 times oversampling is only used to achieve higher speeds.
+ */
+ if (usartdiv < 16) {
+ oversampling = 8;
+ cr1 |= USART_CR1_OVER8;
+ stm32_usart_set_bits(port, ofs->cr1, USART_CR1_OVER8);
+ } else {
+ oversampling = 16;
+ cr1 &= ~USART_CR1_OVER8;
+ stm32_usart_clr_bits(port, ofs->cr1, USART_CR1_OVER8);
+ }
+
+ mantissa = (usartdiv / oversampling) << USART_BRR_DIV_M_SHIFT;
+ fraction = usartdiv % oversampling;
+ brr = mantissa | fraction;
+
+ if (FIELD_FIT(USART_BRR_MASK, brr)) {
+ if (ofs->presc != UNDEF_REG) {
+ port->uartclk = uart_clk_pres;
+ writel_relaxed(presc, port->membase + ofs->presc);
+ } else if (presc) {
+ /* We need a prescaler but we don't have it (STM32F4, STM32F7) */
+ dev_err(port->dev,
+ "unable to set baudrate, input clock is too high");
+ }
+ break;
+ } else if (presc == USART_PRESC_MAX) {
+ /* Even with prescaler and brr at max value we can't set baudrate */
+ dev_err(port->dev, "unable to set baudrate, input clock is too high");
+ break;
+ }
}

- mantissa = (usartdiv / oversampling) << USART_BRR_DIV_M_SHIFT;
- fraction = usartdiv % oversampling;
- writel_relaxed(mantissa | fraction, port->membase + ofs->brr);
+ writel_relaxed(brr, port->membase + ofs->brr);

uart_update_timeout(port, cflag, baud);

diff --git a/drivers/tty/serial/stm32-usart.h b/drivers/tty/serial/stm32-usart.h
index f59f831b2a10..8cecfdce9386 100644
--- a/drivers/tty/serial/stm32-usart.h
+++ b/drivers/tty/serial/stm32-usart.h
@@ -20,6 +20,7 @@ struct stm32_usart_offsets {
u8 icr;
u8 rdr;
u8 tdr;
+ u8 presc;
};

struct stm32_usart_config {
@@ -71,6 +72,7 @@ struct stm32_usart_info {
#define USART_BRR_DIV_M_MASK GENMASK(15, 4)
#define USART_BRR_DIV_M_SHIFT 4
#define USART_BRR_04_R_SHIFT 1
+#define USART_BRR_MASK (USART_BRR_DIV_M_MASK | USART_BRR_DIV_F_MASK)

/* USART_CR1 */
#define USART_CR1_SBK BIT(0)
@@ -176,6 +178,10 @@ struct stm32_usart_info {
#define USART_ICR_CMCF BIT(17) /* F7 */
#define USART_ICR_WUCF BIT(20) /* H7 */

+/* USART_PRESC */
+#define USART_PRESC GENMASK(3, 0) /* H7 */
+#define USART_PRESC_MAX 0b1011
+
#define STM32_SERIAL_NAME "ttySTM"
#define STM32_MAX_PORTS 8

--
2.25.1


2024-01-11 15:29:20

by Valentin Caron

[permalink] [raw]
Subject: [PATCH 4/4] serial: stm32: get FIFO size from hwcfg register

Since STM32MP25, FIFO size could vary regarding the STM32MPxx version.
So we get this size from "hwcfgr1" register and compute threshold values
corresponding to the ratio given by reference manual.

As STM32MP1x, STM32MP25 and STM32H7 share the same compatible and STM32H7
doesn't have a register to get FIFO size, we force FIFO size to 16 in case
of zero read from hwcfgr1 register.

Signed-off-by: Valentin Caron <[email protected]>
---
drivers/tty/serial/stm32-usart.c | 158 +++++++++++++++++--------------
drivers/tty/serial/stm32-usart.h | 6 +-
2 files changed, 94 insertions(+), 70 deletions(-)

diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c
index e8ab5efad945..ffb2d4248107 100644
--- a/drivers/tty/serial/stm32-usart.c
+++ b/drivers/tty/serial/stm32-usart.c
@@ -39,63 +39,64 @@
/* Register offsets */
static struct stm32_usart_info __maybe_unused stm32f4_info = {
.ofs = {
- .isr = 0x00,
- .rdr = 0x04,
- .tdr = 0x04,
- .brr = 0x08,
- .cr1 = 0x0c,
- .cr2 = 0x10,
- .cr3 = 0x14,
- .gtpr = 0x18,
- .rtor = UNDEF_REG,
- .rqr = UNDEF_REG,
- .icr = UNDEF_REG,
- .presc = UNDEF_REG,
+ .isr = 0x00,
+ .rdr = 0x04,
+ .tdr = 0x04,
+ .brr = 0x08,
+ .cr1 = 0x0c,
+ .cr2 = 0x10,
+ .cr3 = 0x14,
+ .gtpr = 0x18,
+ .rtor = UNDEF_REG,
+ .rqr = UNDEF_REG,
+ .icr = UNDEF_REG,
+ .presc = UNDEF_REG,
+ .hwcfgr1 = UNDEF_REG,
},
.cfg = {
.uart_enable_bit = 13,
.has_7bits_data = false,
- .fifosize = 1,
}
};

static struct stm32_usart_info __maybe_unused stm32f7_info = {
.ofs = {
- .cr1 = 0x00,
- .cr2 = 0x04,
- .cr3 = 0x08,
- .brr = 0x0c,
- .gtpr = 0x10,
- .rtor = 0x14,
- .rqr = 0x18,
- .isr = 0x1c,
- .icr = 0x20,
- .rdr = 0x24,
- .tdr = 0x28,
- .presc = UNDEF_REG,
+ .cr1 = 0x00,
+ .cr2 = 0x04,
+ .cr3 = 0x08,
+ .brr = 0x0c,
+ .gtpr = 0x10,
+ .rtor = 0x14,
+ .rqr = 0x18,
+ .isr = 0x1c,
+ .icr = 0x20,
+ .rdr = 0x24,
+ .tdr = 0x28,
+ .presc = UNDEF_REG,
+ .hwcfgr1 = UNDEF_REG,
},
.cfg = {
.uart_enable_bit = 0,
.has_7bits_data = true,
.has_swap = true,
- .fifosize = 1,
}
};

static struct stm32_usart_info __maybe_unused stm32h7_info = {
.ofs = {
- .cr1 = 0x00,
- .cr2 = 0x04,
- .cr3 = 0x08,
- .brr = 0x0c,
- .gtpr = 0x10,
- .rtor = 0x14,
- .rqr = 0x18,
- .isr = 0x1c,
- .icr = 0x20,
- .rdr = 0x24,
- .tdr = 0x28,
- .presc = 0x2c,
+ .cr1 = 0x00,
+ .cr2 = 0x04,
+ .cr3 = 0x08,
+ .brr = 0x0c,
+ .gtpr = 0x10,
+ .rtor = 0x14,
+ .rqr = 0x18,
+ .isr = 0x1c,
+ .icr = 0x20,
+ .rdr = 0x24,
+ .tdr = 0x28,
+ .presc = 0x2c,
+ .hwcfgr1 = 0x3f0,
},
.cfg = {
.uart_enable_bit = 0,
@@ -103,7 +104,6 @@ static struct stm32_usart_info __maybe_unused stm32h7_info = {
.has_swap = true,
.has_wakeup = true,
.has_fifo = true,
- .fifosize = 16,
}
};

@@ -1497,37 +1497,57 @@ static const struct uart_ops stm32_uart_ops = {
#endif /* CONFIG_CONSOLE_POLL */
};

-/*
- * STM32H7 RX & TX FIFO threshold configuration (CR3 RXFTCFG / TXFTCFG)
- * Note: 1 isn't a valid value in RXFTCFG / TXFTCFG. In this case,
- * RXNEIE / TXEIE can be used instead of threshold irqs: RXFTIE / TXFTIE.
- * So, RXFTCFG / TXFTCFG bitfields values are encoded as array index + 1.
- */
-static const u32 stm32h7_usart_fifo_thresh_cfg[] = { 1, 2, 4, 8, 12, 14, 16 };
+struct stm32_usart_thresh_ratio {
+ int mul;
+ int div;
+};
+
+static const struct stm32_usart_thresh_ratio stm32h7_usart_fifo_thresh_cfg[] = {
+ {1, 8}, {1, 4}, {1, 2}, {3, 4}, {7, 8}, {1, 1} };

-static void stm32_usart_get_ftcfg(struct platform_device *pdev, const char *p,
- int *ftcfg)
+static int stm32_usart_get_thresh_value(u32 fifo_size, int index)
{
- u32 bytes, i;
+ return fifo_size * stm32h7_usart_fifo_thresh_cfg[index].mul /
+ stm32h7_usart_fifo_thresh_cfg[index].div;
+}

- /* DT option to get RX & TX FIFO threshold (default to 8 bytes) */
+static int stm32_usart_get_ftcfg(struct platform_device *pdev, struct stm32_port *stm32port,
+ const char *p, int *ftcfg)
+{
+ const struct stm32_usart_offsets *ofs = &stm32port->info->ofs;
+ u32 bytes, i, cfg8;
+ int fifo_size;
+
+ if (WARN_ON(ofs->hwcfgr1 == UNDEF_REG))
+ return 1;
+
+ cfg8 = FIELD_GET(USART_HWCFGR1_CFG8,
+ readl_relaxed(stm32port->port.membase + ofs->hwcfgr1));
+
+ /* On STM32H7, hwcfgr is not present, so returned value will be 0 */
+ fifo_size = cfg8 ? 1 << cfg8 : STM32H7_USART_FIFO_SIZE;
+
+ /* DT option to get RX & TX FIFO threshold (default to half fifo size) */
if (of_property_read_u32(pdev->dev.of_node, p, &bytes))
- bytes = 8;
+ bytes = fifo_size / 2;
+
+ if (bytes < stm32_usart_get_thresh_value(fifo_size, 0)) {
+ *ftcfg = -EINVAL;
+ return fifo_size;
+ }

- for (i = 0; i < ARRAY_SIZE(stm32h7_usart_fifo_thresh_cfg); i++)
- if (stm32h7_usart_fifo_thresh_cfg[i] >= bytes)
+ for (i = 0; i < ARRAY_SIZE(stm32h7_usart_fifo_thresh_cfg); i++) {
+ if (stm32_usart_get_thresh_value(fifo_size, i) >= bytes)
break;
+ }
if (i >= ARRAY_SIZE(stm32h7_usart_fifo_thresh_cfg))
i = ARRAY_SIZE(stm32h7_usart_fifo_thresh_cfg) - 1;

- dev_dbg(&pdev->dev, "%s set to %d bytes\n", p,
- stm32h7_usart_fifo_thresh_cfg[i]);
+ dev_dbg(&pdev->dev, "%s set to %d/%d bytes\n", p,
+ stm32_usart_get_thresh_value(fifo_size, i), fifo_size);

- /* Provide FIFO threshold ftcfg (1 is invalid: threshold irq unused) */
- if (i)
- *ftcfg = i - 1;
- else
- *ftcfg = -EINVAL;
+ *ftcfg = i;
+ return fifo_size;
}

static void stm32_usart_deinit_port(struct stm32_port *stm32port)
@@ -1557,7 +1577,6 @@ static int stm32_usart_init_port(struct stm32_port *stm32port,
port->flags = UPF_BOOT_AUTOCONF;
port->ops = &stm32_uart_ops;
port->dev = &pdev->dev;
- port->fifosize = stm32port->info->cfg.fifosize;
port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_STM32_CONSOLE);
port->irq = irq;
port->rs485_config = stm32_usart_config_rs485;
@@ -1573,14 +1592,6 @@ static int stm32_usart_init_port(struct stm32_port *stm32port,
stm32port->swap = stm32port->info->cfg.has_swap &&
of_property_read_bool(pdev->dev.of_node, "rx-tx-swap");

- stm32port->fifoen = stm32port->info->cfg.has_fifo;
- if (stm32port->fifoen) {
- stm32_usart_get_ftcfg(pdev, "rx-threshold",
- &stm32port->rxftcfg);
- stm32_usart_get_ftcfg(pdev, "tx-threshold",
- &stm32port->txftcfg);
- }
-
port->membase = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(port->membase))
return PTR_ERR(port->membase);
@@ -1603,6 +1614,15 @@ static int stm32_usart_init_port(struct stm32_port *stm32port,
goto err_clk;
}

+ stm32port->fifoen = stm32port->info->cfg.has_fifo;
+ if (stm32port->fifoen) {
+ stm32_usart_get_ftcfg(pdev, stm32port, "rx-threshold", &stm32port->rxftcfg);
+ port->fifosize = stm32_usart_get_ftcfg(pdev, stm32port, "tx-threshold",
+ &stm32port->txftcfg);
+ } else {
+ port->fifosize = 1;
+ }
+
stm32port->gpios = mctrl_gpio_init(&stm32port->port, 0);
if (IS_ERR(stm32port->gpios)) {
ret = PTR_ERR(stm32port->gpios);
diff --git a/drivers/tty/serial/stm32-usart.h b/drivers/tty/serial/stm32-usart.h
index bf3720948704..af20258ccc7a 100644
--- a/drivers/tty/serial/stm32-usart.h
+++ b/drivers/tty/serial/stm32-usart.h
@@ -21,6 +21,7 @@ struct stm32_usart_offsets {
u16 rdr;
u16 tdr;
u16 presc;
+ u16 hwcfgr1;
};

struct stm32_usart_config {
@@ -29,7 +30,6 @@ struct stm32_usart_config {
bool has_swap;
bool has_wakeup;
bool has_fifo;
- int fifosize;
};

struct stm32_usart_info {
@@ -182,8 +182,12 @@ struct stm32_usart_info {
#define USART_PRESC GENMASK(3, 0) /* H7 */
#define USART_PRESC_MAX 0b1011

+/* USART_HWCFCR1 */
+#define USART_HWCFGR1_CFG8 GENMASK(31, 28) /* MP1 */
+
#define STM32_SERIAL_NAME "ttySTM"
#define STM32_MAX_PORTS 9
+#define STM32H7_USART_FIFO_SIZE 16

#define RX_BUF_L 4096 /* dma rx buffer length */
#define RX_BUF_P (RX_BUF_L / 2) /* dma rx buffer period */
--
2.25.1


2024-01-12 06:59:26

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH 1/4] serial: stm32: implement prescaler tuning to compute low baudrate

Hi Valentin,

kernel test robot noticed the following build errors:

[auto build test ERROR on atorgue-stm32/stm32-next]
[also build test ERROR on tty/tty-testing tty/tty-next tty/tty-linus usb/usb-testing usb/usb-next usb/usb-linus linus/master v6.7 next-20240111]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url: https://github.com/intel-lab-lkp/linux/commits/Valentin-Caron/serial-stm32-implement-prescaler-tuning-to-compute-low-baudrate/20240111-233406
base: https://git.kernel.org/pub/scm/linux/kernel/git/atorgue/stm32.git stm32-next
patch link: https://lore.kernel.org/r/20240111152712.1842790-2-valentin.caron%40foss.st.com
patch subject: [PATCH 1/4] serial: stm32: implement prescaler tuning to compute low baudrate
config: i386-buildonly-randconfig-003-20240112 (https://download.01.org/0day-ci/archive/20240112/[email protected]/config)
compiler: ClangBuiltLinux clang version 17.0.6 (https://github.com/llvm/llvm-project 6009708b4367171ccdbf4b5905cb6a803753fe18)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240112/[email protected]/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/

All errors (new ones prefixed by >>):

>> drivers/tty/serial/stm32-usart.c:1303:7: error: call to undeclared function 'FIELD_FIT'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
1303 | if (FIELD_FIT(USART_BRR_MASK, brr)) {
| ^
1 error generated.


vim +/FIELD_FIT +1303 drivers/tty/serial/stm32-usart.c

1156
1157 static void stm32_usart_set_termios(struct uart_port *port,
1158 struct ktermios *termios,
1159 const struct ktermios *old)
1160 {
1161 struct stm32_port *stm32_port = to_stm32_port(port);
1162 const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
1163 const struct stm32_usart_config *cfg = &stm32_port->info->cfg;
1164 struct serial_rs485 *rs485conf = &port->rs485;
1165 unsigned int baud, bits, uart_clk, uart_clk_pres;
1166 u32 usartdiv, mantissa, fraction, oversampling;
1167 tcflag_t cflag = termios->c_cflag;
1168 u32 cr1, cr2, cr3, isr, brr, presc;
1169 unsigned long flags;
1170 int ret;
1171
1172 if (!stm32_port->hw_flow_control)
1173 cflag &= ~CRTSCTS;
1174
1175 uart_clk = clk_get_rate(stm32_port->clk);
1176
1177 baud = uart_get_baud_rate(port, termios, old, 0, uart_clk / 8);
1178
1179 uart_port_lock_irqsave(port, &flags);
1180
1181 ret = readl_relaxed_poll_timeout_atomic(port->membase + ofs->isr,
1182 isr,
1183 (isr & USART_SR_TC),
1184 10, 100000);
1185
1186 /* Send the TC error message only when ISR_TC is not set. */
1187 if (ret)
1188 dev_err(port->dev, "Transmission is not complete\n");
1189
1190 /* Stop serial port and reset value */
1191 writel_relaxed(0, port->membase + ofs->cr1);
1192
1193 /* flush RX & TX FIFO */
1194 if (ofs->rqr != UNDEF_REG)
1195 writel_relaxed(USART_RQR_TXFRQ | USART_RQR_RXFRQ,
1196 port->membase + ofs->rqr);
1197
1198 cr1 = USART_CR1_TE | USART_CR1_RE;
1199 if (stm32_port->fifoen)
1200 cr1 |= USART_CR1_FIFOEN;
1201 cr2 = stm32_port->swap ? USART_CR2_SWAP : 0;
1202
1203 /* Tx and RX FIFO configuration */
1204 cr3 = readl_relaxed(port->membase + ofs->cr3);
1205 cr3 &= USART_CR3_TXFTIE | USART_CR3_RXFTIE;
1206 if (stm32_port->fifoen) {
1207 if (stm32_port->txftcfg >= 0)
1208 cr3 |= stm32_port->txftcfg << USART_CR3_TXFTCFG_SHIFT;
1209 if (stm32_port->rxftcfg >= 0)
1210 cr3 |= stm32_port->rxftcfg << USART_CR3_RXFTCFG_SHIFT;
1211 }
1212
1213 if (cflag & CSTOPB)
1214 cr2 |= USART_CR2_STOP_2B;
1215
1216 bits = tty_get_char_size(cflag);
1217 stm32_port->rdr_mask = (BIT(bits) - 1);
1218
1219 if (cflag & PARENB) {
1220 bits++;
1221 cr1 |= USART_CR1_PCE;
1222 }
1223
1224 /*
1225 * Word length configuration:
1226 * CS8 + parity, 9 bits word aka [M1:M0] = 0b01
1227 * CS7 or (CS6 + parity), 7 bits word aka [M1:M0] = 0b10
1228 * CS8 or (CS7 + parity), 8 bits word aka [M1:M0] = 0b00
1229 * M0 and M1 already cleared by cr1 initialization.
1230 */
1231 if (bits == 9) {
1232 cr1 |= USART_CR1_M0;
1233 } else if ((bits == 7) && cfg->has_7bits_data) {
1234 cr1 |= USART_CR1_M1;
1235 } else if (bits != 8) {
1236 dev_dbg(port->dev, "Unsupported data bits config: %u bits\n"
1237 , bits);
1238 cflag &= ~CSIZE;
1239 cflag |= CS8;
1240 termios->c_cflag = cflag;
1241 bits = 8;
1242 if (cflag & PARENB) {
1243 bits++;
1244 cr1 |= USART_CR1_M0;
1245 }
1246 }
1247
1248 if (ofs->rtor != UNDEF_REG && (stm32_port->rx_ch ||
1249 (stm32_port->fifoen &&
1250 stm32_port->rxftcfg >= 0))) {
1251 if (cflag & CSTOPB)
1252 bits = bits + 3; /* 1 start bit + 2 stop bits */
1253 else
1254 bits = bits + 2; /* 1 start bit + 1 stop bit */
1255
1256 /* RX timeout irq to occur after last stop bit + bits */
1257 stm32_port->cr1_irq = USART_CR1_RTOIE;
1258 writel_relaxed(bits, port->membase + ofs->rtor);
1259 cr2 |= USART_CR2_RTOEN;
1260 /*
1261 * Enable fifo threshold irq in two cases, either when there is no DMA, or when
1262 * wake up over usart, from low power until the DMA gets re-enabled by resume.
1263 */
1264 stm32_port->cr3_irq = USART_CR3_RXFTIE;
1265 }
1266
1267 cr1 |= stm32_port->cr1_irq;
1268 cr3 |= stm32_port->cr3_irq;
1269
1270 if (cflag & PARODD)
1271 cr1 |= USART_CR1_PS;
1272
1273 port->status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS);
1274 if (cflag & CRTSCTS) {
1275 port->status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS;
1276 cr3 |= USART_CR3_CTSE | USART_CR3_RTSE;
1277 }
1278
1279 for (presc = 0; presc <= USART_PRESC_MAX; presc++) {
1280 uart_clk_pres = DIV_ROUND_CLOSEST(uart_clk, stm32_usart_presc_val[presc]);
1281 usartdiv = DIV_ROUND_CLOSEST(uart_clk_pres, baud);
1282
1283 /*
1284 * The USART supports 16 or 8 times oversampling.
1285 * By default we prefer 16 times oversampling, so that the receiver
1286 * has a better tolerance to clock deviations.
1287 * 8 times oversampling is only used to achieve higher speeds.
1288 */
1289 if (usartdiv < 16) {
1290 oversampling = 8;
1291 cr1 |= USART_CR1_OVER8;
1292 stm32_usart_set_bits(port, ofs->cr1, USART_CR1_OVER8);
1293 } else {
1294 oversampling = 16;
1295 cr1 &= ~USART_CR1_OVER8;
1296 stm32_usart_clr_bits(port, ofs->cr1, USART_CR1_OVER8);
1297 }
1298
1299 mantissa = (usartdiv / oversampling) << USART_BRR_DIV_M_SHIFT;
1300 fraction = usartdiv % oversampling;
1301 brr = mantissa | fraction;
1302
> 1303 if (FIELD_FIT(USART_BRR_MASK, brr)) {
1304 if (ofs->presc != UNDEF_REG) {
1305 port->uartclk = uart_clk_pres;
1306 writel_relaxed(presc, port->membase + ofs->presc);
1307 } else if (presc) {
1308 /* We need a prescaler but we don't have it (STM32F4, STM32F7) */
1309 dev_err(port->dev,
1310 "unable to set baudrate, input clock is too high");
1311 }
1312 break;
1313 } else if (presc == USART_PRESC_MAX) {
1314 /* Even with prescaler and brr at max value we can't set baudrate */
1315 dev_err(port->dev, "unable to set baudrate, input clock is too high");
1316 break;
1317 }
1318 }
1319
1320 writel_relaxed(brr, port->membase + ofs->brr);
1321
1322 uart_update_timeout(port, cflag, baud);
1323
1324 port->read_status_mask = USART_SR_ORE;
1325 if (termios->c_iflag & INPCK)
1326 port->read_status_mask |= USART_SR_PE | USART_SR_FE;
1327 if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
1328 port->read_status_mask |= USART_SR_FE;
1329
1330 /* Characters to ignore */
1331 port->ignore_status_mask = 0;
1332 if (termios->c_iflag & IGNPAR)
1333 port->ignore_status_mask = USART_SR_PE | USART_SR_FE;
1334 if (termios->c_iflag & IGNBRK) {
1335 port->ignore_status_mask |= USART_SR_FE;
1336 /*
1337 * If we're ignoring parity and break indicators,
1338 * ignore overruns too (for real raw support).
1339 */
1340 if (termios->c_iflag & IGNPAR)
1341 port->ignore_status_mask |= USART_SR_ORE;
1342 }
1343
1344 /* Ignore all characters if CREAD is not set */
1345 if ((termios->c_cflag & CREAD) == 0)
1346 port->ignore_status_mask |= USART_SR_DUMMY_RX;
1347
1348 if (stm32_port->rx_ch) {
1349 /*
1350 * Setup DMA to collect only valid data and enable error irqs.
1351 * This also enables break reception when using DMA.
1352 */
1353 cr1 |= USART_CR1_PEIE;
1354 cr3 |= USART_CR3_EIE;
1355 cr3 |= USART_CR3_DMAR;
1356 cr3 |= USART_CR3_DDRE;
1357 }
1358
1359 if (stm32_port->tx_ch)
1360 cr3 |= USART_CR3_DMAT;
1361
1362 if (rs485conf->flags & SER_RS485_ENABLED) {
1363 stm32_usart_config_reg_rs485(&cr1, &cr3,
1364 rs485conf->delay_rts_before_send,
1365 rs485conf->delay_rts_after_send,
1366 baud);
1367 if (rs485conf->flags & SER_RS485_RTS_ON_SEND) {
1368 cr3 &= ~USART_CR3_DEP;
1369 rs485conf->flags &= ~SER_RS485_RTS_AFTER_SEND;
1370 } else {
1371 cr3 |= USART_CR3_DEP;
1372 rs485conf->flags |= SER_RS485_RTS_AFTER_SEND;
1373 }
1374
1375 } else {
1376 cr3 &= ~(USART_CR3_DEM | USART_CR3_DEP);
1377 cr1 &= ~(USART_CR1_DEDT_MASK | USART_CR1_DEAT_MASK);
1378 }
1379
1380 /* Configure wake up from low power on start bit detection */
1381 if (stm32_port->wakeup_src) {
1382 cr3 &= ~USART_CR3_WUS_MASK;
1383 cr3 |= USART_CR3_WUS_START_BIT;
1384 }
1385
1386 writel_relaxed(cr3, port->membase + ofs->cr3);
1387 writel_relaxed(cr2, port->membase + ofs->cr2);
1388 writel_relaxed(cr1, port->membase + ofs->cr1);
1389
1390 stm32_usart_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
1391 uart_port_unlock_irqrestore(port, flags);
1392
1393 /* Handle modem control interrupts */
1394 if (UART_ENABLE_MS(port, termios->c_cflag))
1395 stm32_usart_enable_ms(port);
1396 else
1397 stm32_usart_disable_ms(port);
1398 }
1399

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki