]> Git Repo - linux.git/blobdiff - drivers/tty/serial/stm32-usart.c
Merge 6.8-rc6 into tty-next
[linux.git] / drivers / tty / serial / stm32-usart.c
index 693e932d6feb5842467d1408e04c8d574342cb1f..58d169e5c1db03b910686ac2a229116644f0d6e9 100644 (file)
@@ -9,6 +9,7 @@
  * Inspired by st-asc.c from STMicroelectronics (c)
  */
 
+#include <linux/bitfield.h>
 #include <linux/clk.h>
 #include <linux/console.h>
 #include <linux/delay.h>
 /* 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,
+               .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,
+               .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,
+               .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,
@@ -100,7 +105,6 @@ static struct stm32_usart_info __maybe_unused stm32h7_info = {
                .has_swap = true,
                .has_wakeup = true,
                .has_fifo = true,
-               .fifosize = 16,
        }
 };
 
@@ -1147,6 +1151,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)
@@ -1155,17 +1161,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);
 
@@ -1267,27 +1275,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);
 
@@ -1471,37 +1500,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 int stm32_usart_get_thresh_value(u32 fifo_size, int index)
+{
+       return fifo_size * stm32h7_usart_fifo_thresh_cfg[index].mul /
+               stm32h7_usart_fifo_thresh_cfg[index].div;
+}
 
-static void stm32_usart_get_ftcfg(struct platform_device *pdev, const char *p,
-                                 int *ftcfg)
+static int stm32_usart_get_ftcfg(struct platform_device *pdev, struct stm32_port *stm32port,
+                                const char *p, int *ftcfg)
 {
-       u32 bytes, i;
+       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 8 bytes) */
+       /* 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;
 
-       for (i = 0; i < ARRAY_SIZE(stm32h7_usart_fifo_thresh_cfg); i++)
-               if (stm32h7_usart_fifo_thresh_cfg[i] >= bytes)
+       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 (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)
@@ -1531,7 +1580,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;
@@ -1547,14 +1595,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);
@@ -1577,6 +1617,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);
This page took 0.042203 seconds and 4 git commands to generate.