[PATCH v4] serial: 8250_dw: preserve BUSY-safe fractional divisor programming
From: James Hilliard
Date: Tue Oct 27 2026 - 13:17:21 EST
dw8250_probe() installs a divisor setter that makes the UART idle and
checks DLAB before writing DLL and DLH. When dw8250_setup_port() detects
fractional-divisor support, it replaces that setter with the library's
callback. The replacement writes DLF and uses serial8250_do_set_divisor(),
bypassing the divisor setter's idle sequence and DLAB readback.
If the LCR update fails to set DLAB, the subsequent divisor writes can
reach TX and IER instead of DLL and DLH.
Expose the library's fractional-divisor calculation and DLF write as
shared helpers. Install the BUSY-safe setter after hardware discovery and
use the shared DLF helper to program the fractional field only after DLAB
has been confirmed, alongside DLL and DLH. Keep fractional-divisor
detection and calculator selection in the library, preserving the core
calculation for integer-only ports. Let the shared DLF setter check for
fractional-divisor support so callers can use it for either kind of port.
Fixes: a7b9ce39fbe4 ("serial: 8250_dw: Ensure BUSY is deasserted")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: James Hilliard <james.hilliard1@xxxxxxxxx>
---
Changes in v4:
- Leave fractional-divisor calculator selection in dwlib, removing the
platform driver's DLF capability check and getter assignment.
- Preserve dwlib's existing getter callback through a static wrapper
around the shared calculator, as requested by Andy.
- Link to v3: https://patch.msgid.link/20261026-submit-serial-8250-dw-divisor-v1-v3-1-7dbd194e1f38@xxxxxxxxx
Changes in v3:
- Move the fractional-divisor support check into the shared DLF setter,
allowing the BUSY-safe platform setter to call it directly, as requested
by Ilpo.
- Rebase onto current tty-next.
- Link to v2: https://lore.kernel.org/r/20261024-submit-serial-8250-dw-divisor-v1-v2-1-032ca5338359@xxxxxxxxx
Changes in v2:
- Share the fractional-divisor calculation and DLF write through exported
dwlib helpers, and use them in the platform driver as requested by Andy.
- Select the fractional calculator only when DLF is implemented, preserving
the existing core calculation for integer-only ports.
- Link to v1: https://lore.kernel.org/r/20261023-submit-serial-8250-dw-divisor-v1-v1-1-f452d4bb1b1f@xxxxxxxxx
Previously included in the DMA cancellation series v4:
https://lore.kernel.org/r/20261023-submit-serial-8250-dma-cancel-v1-v4-0-e759de1839de@xxxxxxxxx
Changes in DMA cancellation v4:
- Keep DesignWare's BUSY-safe divisor setter after fractional-divisor
discovery, writing DLF and DLL/DLH only after confirming DLAB.
- Link to v3: https://patch.msgid.link/20261023-submit-serial-8250-dma-cancel-v1-v3-0-d78f9e4c4932@xxxxxxxxx
---
drivers/tty/serial/8250/8250_dw.c | 5 ++++-
drivers/tty/serial/8250/8250_dwlib.c | 24 +++++++++++++++++++++---
drivers/tty/serial/8250/8250_dwlib.h | 2 ++
3 files changed, 27 insertions(+), 4 deletions(-)
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index eca02e8bd6d1..9cbe076463e0 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -201,6 +201,7 @@ static void dw8250_set_divisor(struct uart_port *p, unsigned int baud,
if (!(serial_port_in(p, UART_LCR) & UART_LCR_DLAB))
goto idle_failed;
+ dw8250_set_divisor_dlf(p, quot_frac);
serial_dl_write(up, quot);
serial_port_out(p, UART_LCR, up->lcr);
@@ -651,7 +652,6 @@ static int dw8250_probe(struct platform_device *pdev)
p->set_ldisc = dw8250_set_ldisc;
p->set_termios = dw8250_set_termios;
- p->set_divisor = dw8250_set_divisor;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
@@ -768,6 +768,9 @@ static int dw8250_probe(struct platform_device *pdev)
if (!data->skip_autocfg)
dw8250_setup_port(p);
+ /* Use the BUSY-safe sequence for both integer and fractional divisors. */
+ p->set_divisor = dw8250_set_divisor;
+
/* If we have a valid fifosize, try hooking up DMA */
if (p->fifosize) {
data->data.dma.rxconf.src_maxburst = p->fifosize / 4;
diff --git a/drivers/tty/serial/8250/8250_dwlib.c b/drivers/tty/serial/8250/8250_dwlib.c
index 9bb02a4ab11f..91b3607b64e6 100644
--- a/drivers/tty/serial/8250/8250_dwlib.c
+++ b/drivers/tty/serial/8250/8250_dwlib.c
@@ -23,8 +23,8 @@
* we have: div(F) * (16 * baud) = rem
* so frac = 2^dlf_size * rem / (16 * baud) = (rem << dlf_size) / (16 * baud)
*/
-static unsigned int dw8250_get_divisor(struct uart_port *p, unsigned int baud,
- unsigned int *frac)
+unsigned int dw8250_get_divisor_dlf(struct uart_port *p, unsigned int baud,
+ unsigned int *frac)
{
unsigned int quot, rem, base_baud = baud * 16;
struct dw8250_port_data *d = p->private_data;
@@ -35,11 +35,29 @@ static unsigned int dw8250_get_divisor(struct uart_port *p, unsigned int baud,
return quot;
}
+EXPORT_SYMBOL_GPL(dw8250_get_divisor_dlf);
+
+static unsigned int dw8250_get_divisor(struct uart_port *p, unsigned int baud,
+ unsigned int *frac)
+{
+ return dw8250_get_divisor_dlf(p, baud, frac);
+}
+
+void dw8250_set_divisor_dlf(struct uart_port *p, unsigned int quot_frac)
+{
+ struct dw8250_port_data *pd = p->private_data;
+
+ if (!pd->dlf_size)
+ return;
+
+ dw8250_writel_ext(p, DW_UART_DLF, quot_frac);
+}
+EXPORT_SYMBOL_GPL(dw8250_set_divisor_dlf);
static void dw8250_set_divisor(struct uart_port *p, unsigned int baud,
unsigned int quot, unsigned int quot_frac)
{
- dw8250_writel_ext(p, DW_UART_DLF, quot_frac);
+ dw8250_set_divisor_dlf(p, quot_frac);
serial8250_do_set_divisor(p, baud, quot);
}
diff --git a/drivers/tty/serial/8250/8250_dwlib.h b/drivers/tty/serial/8250/8250_dwlib.h
index 39fcb1b323ad..7191cabfb086 100644
--- a/drivers/tty/serial/8250/8250_dwlib.h
+++ b/drivers/tty/serial/8250/8250_dwlib.h
@@ -97,6 +97,8 @@ struct dw8250_port_data {
int srbr;
};
+unsigned int dw8250_get_divisor_dlf(struct uart_port *p, unsigned int baud, unsigned int *frac);
+void dw8250_set_divisor_dlf(struct uart_port *p, unsigned int quot_frac);
void dw8250_do_set_termios(struct uart_port *p, struct ktermios *termios, const struct ktermios *old);
void dw8250_setup_port(struct uart_port *p);
---
base-commit: 6b4b543fa18757297ac756a6fe6e64f506b7844a
change-id: 20261023-submit-serial-8250-dw-divisor-v1-5f1689ed05a8
Best regards,
--
James Hilliard <james.hilliard1@xxxxxxxxx>