* [PATCH v3] serial: 8250_dw: preserve BUSY-safe fractional divisor programming
@ 2026-10-26 15:59 James Hilliard
2026-10-26 19:16 ` Andy Shevchenko
0 siblings, 1 reply; 2+ messages in thread
From: James Hilliard @ 2026-10-26 15:59 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby, Ilpo Järvinen
Cc: Andy Shevchenko, Raul E Rangel, Heikki Krogerus, linux-serial,
linux-kernel, stable, James Hilliard
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. Select the shared calculator
when fractional divisors are supported, preserving the core calculation
for integer-only ports. Keep the check for fractional-divisor support in
the shared DLF setter so callers can use it for either kind of port.
Fixes: a7b9ce39fbe4 ("serial: 8250_dw: Ensure BUSY is deasserted")
Cc: stable@vger.kernel.org
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
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@gmail.com
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@gmail.com
Previously included in the DMA cancellation series v4:
https://lore.kernel.org/r/20261023-submit-serial-8250-dma-cancel-v1-v4-0-e759de1839de@gmail.com
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@gmail.com
---
drivers/tty/serial/8250/8250_dw.c | 7 ++++++-
drivers/tty/serial/8250/8250_dwlib.c | 20 ++++++++++++++++----
drivers/tty/serial/8250/8250_dwlib.h | 2 ++
3 files changed, 24 insertions(+), 5 deletions(-)
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index eca02e8bd6d1..e823c38d381e 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,11 @@ 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. */
+ if (data->data.dlf_size)
+ p->get_divisor = dw8250_get_divisor_dlf;
+ 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..9125ef130a97 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,23 @@ static unsigned int dw8250_get_divisor(struct uart_port *p, unsigned int baud,
return quot;
}
+EXPORT_SYMBOL_GPL(dw8250_get_divisor_dlf);
+
+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);
}
@@ -217,7 +229,7 @@ void dw8250_setup_port(struct uart_port *p)
if (reg) {
pd->dlf_size = fls(reg);
- p->get_divisor = dw8250_get_divisor;
+ p->get_divisor = dw8250_get_divisor_dlf;
p->set_divisor = dw8250_set_divisor;
}
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@gmail.com>
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [PATCH v3] serial: 8250_dw: preserve BUSY-safe fractional divisor programming
2026-10-26 15:59 [PATCH v3] serial: 8250_dw: preserve BUSY-safe fractional divisor programming James Hilliard
@ 2026-10-26 19:16 ` Andy Shevchenko
0 siblings, 0 replies; 2+ messages in thread
From: Andy Shevchenko @ 2026-10-26 19:16 UTC (permalink / raw)
To: James Hilliard
Cc: Greg Kroah-Hartman, Jiri Slaby, Ilpo Järvinen,
Raul E Rangel, Heikki Krogerus, linux-serial, linux-kernel,
stable
On Mon, Oct 26, 2026 at 09:59:10AM -0600, James Hilliard wrote:
> 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. Select the shared calculator
> when fractional divisors are supported, preserving the core calculation
> for integer-only ports. Keep the check for fractional-divisor support in
> the shared DLF setter so callers can use it for either kind of port.
...
> + /* Use the BUSY-safe sequence for both integer and fractional divisors. */
> + if (data->data.dlf_size)
> + p->get_divisor = dw8250_get_divisor_dlf;
Yeah, this will be the similar issue with layering violation that Ilpo pointed
out elsewhere. So, perhaps easiest way for now is to let it be as is here, id
est not assigning .get_divisor() at all. Otherwise we would need the same check
in there and the branch on what to do if it's not a DLF case.
> + p->set_divisor = dw8250_set_divisor;
...
> if (reg) {
> pd->dlf_size = fls(reg);
> - p->get_divisor = dw8250_get_divisor;
> + p->get_divisor = dw8250_get_divisor_dlf;
Just add a static function wrapper that simply calls this, so we won't
change it here at all.
static unsigned int dw8250_get_divisor(struct uart_port *p, unsigned int baud,
unsigned int *frac)
{
return dw8250_get_divisor_dlf(p, baud, frac);
}
> p->set_divisor = dw8250_set_divisor;
> }
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-10-26 19:16 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-26 15:59 [PATCH v3] serial: 8250_dw: preserve BUSY-safe fractional divisor programming James Hilliard
2026-10-26 19:16 ` Andy Shevchenko
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®