From 76047c0312414915035f4ebbaa533fe3817a21e0 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 3 Jun 2024 07:08:49 +0200 Subject: Adding upstream version 6.8.12. Signed-off-by: Daniel Baumann --- drivers/tty/serial/8250/8250_bcm7271.c | 101 ++++++++++++++++++++------------- drivers/tty/serial/8250/8250_mtk.c | 8 ++- drivers/tty/serial/sc16is7xx.c | 23 ++++++-- 3 files changed, 84 insertions(+), 48 deletions(-) (limited to 'drivers/tty/serial') diff --git a/drivers/tty/serial/8250/8250_bcm7271.c b/drivers/tty/serial/8250/8250_bcm7271.c index 504c4c0208..4f8b70f8ce 100644 --- a/drivers/tty/serial/8250/8250_bcm7271.c +++ b/drivers/tty/serial/8250/8250_bcm7271.c @@ -676,18 +676,46 @@ static void init_real_clk_rates(struct device *dev, struct brcmuart_priv *priv) clk_set_rate(priv->baud_mux_clk, priv->default_mux_rate); } +static u32 find_quot(struct device *dev, u32 freq, u32 baud, u32 *percent) +{ + u32 quot; + u32 rate; + u64 hires_rate; + u64 hires_baud; + u64 hires_err; + + rate = freq / 16; + quot = DIV_ROUND_CLOSEST(rate, baud); + if (!quot) + return 0; + + /* increase resolution to get xx.xx percent */ + hires_rate = div_u64((u64)rate * 10000, (u64)quot); + hires_baud = (u64)baud * 10000; + + /* get the delta */ + if (hires_rate > hires_baud) + hires_err = (hires_rate - hires_baud); + else + hires_err = (hires_baud - hires_rate); + + *percent = (unsigned long)DIV_ROUND_CLOSEST_ULL(hires_err, baud); + + dev_dbg(dev, "Baud rate: %u, MUX Clk: %u, Error: %u.%u%%\n", + baud, freq, *percent / 100, *percent % 100); + + return quot; +} + static void set_clock_mux(struct uart_port *up, struct brcmuart_priv *priv, u32 baud) { u32 percent; u32 best_percent = UINT_MAX; u32 quot; + u32 freq; u32 best_quot = 1; - u32 rate; - int best_index = -1; - u64 hires_rate; - u64 hires_baud; - u64 hires_err; + u32 best_freq = 0; int rc; int i; int real_baud; @@ -696,44 +724,35 @@ static void set_clock_mux(struct uart_port *up, struct brcmuart_priv *priv, if (priv->baud_mux_clk == NULL) return; - /* Find the closest match for specified baud */ - for (i = 0; i < ARRAY_SIZE(priv->real_rates); i++) { - if (priv->real_rates[i] == 0) - continue; - rate = priv->real_rates[i] / 16; - quot = DIV_ROUND_CLOSEST(rate, baud); - if (!quot) - continue; - - /* increase resolution to get xx.xx percent */ - hires_rate = (u64)rate * 10000; - hires_baud = (u64)baud * 10000; - - hires_err = div_u64(hires_rate, (u64)quot); - - /* get the delta */ - if (hires_err > hires_baud) - hires_err = (hires_err - hires_baud); - else - hires_err = (hires_baud - hires_err); - - percent = (unsigned long)DIV_ROUND_CLOSEST_ULL(hires_err, baud); - dev_dbg(up->dev, - "Baud rate: %u, MUX Clk: %u, Error: %u.%u%%\n", - baud, priv->real_rates[i], percent / 100, - percent % 100); - if (percent < best_percent) { - best_percent = percent; - best_index = i; - best_quot = quot; + /* Try default_mux_rate first */ + quot = find_quot(up->dev, priv->default_mux_rate, baud, &percent); + if (quot) { + best_percent = percent; + best_freq = priv->default_mux_rate; + best_quot = quot; + } + /* If more than 1% error, find the closest match for specified baud */ + if (best_percent > 100) { + for (i = 0; i < ARRAY_SIZE(priv->real_rates); i++) { + freq = priv->real_rates[i]; + if (freq == 0 || freq == priv->default_mux_rate) + continue; + quot = find_quot(up->dev, freq, baud, &percent); + if (!quot) + continue; + + if (percent < best_percent) { + best_percent = percent; + best_freq = freq; + best_quot = quot; + } } } - if (best_index == -1) { + if (!best_freq) { dev_err(up->dev, "Error, %d BAUD rate is too fast.\n", baud); return; } - rate = priv->real_rates[best_index]; - rc = clk_set_rate(priv->baud_mux_clk, rate); + rc = clk_set_rate(priv->baud_mux_clk, best_freq); if (rc) dev_err(up->dev, "Error selecting BAUD MUX clock\n"); @@ -742,8 +761,8 @@ static void set_clock_mux(struct uart_port *up, struct brcmuart_priv *priv, dev_err(up->dev, "Error, baud: %d has %u.%u%% error\n", baud, percent / 100, percent % 100); - real_baud = rate / 16 / best_quot; - dev_dbg(up->dev, "Selecting BAUD MUX rate: %u\n", rate); + real_baud = best_freq / 16 / best_quot; + dev_dbg(up->dev, "Selecting BAUD MUX rate: %u\n", best_freq); dev_dbg(up->dev, "Requested baud: %u, Actual baud: %u\n", baud, real_baud); @@ -752,7 +771,7 @@ static void set_clock_mux(struct uart_port *up, struct brcmuart_priv *priv, i += (i / 2); priv->char_wait = ns_to_ktime(i); - up->uartclk = rate; + up->uartclk = best_freq; } static void brcmstb_set_termios(struct uart_port *up, diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c index 9ff6bbe9c0..d14988d149 100644 --- a/drivers/tty/serial/8250/8250_mtk.c +++ b/drivers/tty/serial/8250/8250_mtk.c @@ -209,15 +209,19 @@ static int mtk8250_startup(struct uart_port *port) static void mtk8250_shutdown(struct uart_port *port) { -#ifdef CONFIG_SERIAL_8250_DMA struct uart_8250_port *up = up_to_u8250p(port); struct mtk8250_data *data = port->private_data; + int irq = data->rx_wakeup_irq; +#ifdef CONFIG_SERIAL_8250_DMA if (up->dma) data->rx_status = DMA_RX_SHUTDOWN; #endif - return serial8250_do_shutdown(port); + serial8250_do_shutdown(port); + + if (irq >= 0) + serial8250_do_set_mctrl(&up->port, TIOCM_RTS); } static void mtk8250_disable_intrs(struct uart_8250_port *up, int mask) diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 929206a9a6..12915fffac 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -554,16 +554,28 @@ static bool sc16is7xx_regmap_noinc(struct device *dev, unsigned int reg) return reg == SC16IS7XX_RHR_REG; } +/* + * Configure programmable baud rate generator (divisor) according to the + * desired baud rate. + * + * From the datasheet, the divisor is computed according to: + * + * XTAL1 input frequency + * ----------------------- + * prescaler + * divisor = --------------------------- + * baud-rate x sampling-rate + */ static int sc16is7xx_set_baud(struct uart_port *port, int baud) { struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); u8 lcr; - u8 prescaler = 0; + unsigned int prescaler = 1; unsigned long clk = port->uartclk, div = clk / 16 / baud; if (div >= BIT(16)) { - prescaler = SC16IS7XX_MCR_CLKSEL_BIT; - div /= 4; + prescaler = 4; + div /= prescaler; } /* Enable enhanced features */ @@ -573,9 +585,10 @@ static int sc16is7xx_set_baud(struct uart_port *port, int baud) SC16IS7XX_EFR_ENABLE_BIT); sc16is7xx_efr_unlock(port); + /* If bit MCR_CLKSEL is set, the divide by 4 prescaler is activated. */ sc16is7xx_port_update(port, SC16IS7XX_MCR_REG, SC16IS7XX_MCR_CLKSEL_BIT, - prescaler); + prescaler == 1 ? 0 : SC16IS7XX_MCR_CLKSEL_BIT); /* Backup LCR and access special register set (DLL/DLH) */ lcr = sc16is7xx_port_read(port, SC16IS7XX_LCR_REG); @@ -591,7 +604,7 @@ static int sc16is7xx_set_baud(struct uart_port *port, int baud) /* Restore LCR and access to general register set */ sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr); - return DIV_ROUND_CLOSEST(clk / 16, div); + return DIV_ROUND_CLOSEST((clk / prescaler) / 16, div); } static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen, -- cgit v1.2.3