Re: [PATCH] serial: 8250_dw: preserve BUSY-safe fractional divisor programming

From: Andy Shevchenko

Date: Sat Oct 24 2026 - 10:14:49 EST


On Fri, Oct 23, 2026 at 11:50:17PM -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.
>
> Install the BUSY-safe setter after hardware discovery and extend it to
> write DLF when fractional divisors are supported. Program both divisor
> fields only after DLAB has been confirmed, retaining the fractional
> divisor calculation selected by dw8250_setup_port().

...

> 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;
>
> + if (pd->dlf_size)
> + dw8250_writel_ext(p, DW_UART_DLF, quot_frac);

I still think it's better to export dw8250_?et_divisor_dfl() from dwlib and use
both here.

In dwlib:

unsigned int dw8250_get_divisor_dfl(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;

quot = p->uartclk / base_baud;
rem = p->uartclk % base_baud;
*frac = DIV_ROUND_CLOSEST(rem << d->dlf_size, base_baud);

return quot;
}

void dw8250_set_divisor_dfl(struct uart_port *p, unsigned int baud,
unsigned int quot, unsigned int quot_frac)
{
dw8250_writel_ext(p, DW_UART_DLF, quot_frac);
}


static unsigned int dw8250_get_divisor(struct uart_port *p, unsigned int baud,
unsigned int *frac)
{
return dw8250_get_divisor_dfl(p, baud, frac);
}

static void dw8250_set_divisor(struct uart_port *p, unsigned int baud,
unsigned int quot, unsigned int quot_frac)
{
dw8250_set_divisor_dfl(p, baud, quot, quot_frac);

serial8250_do_set_divisor(p, baud, quot);
}

In dw:

static unsigned int dw8250_get_divisor(struct uart_port *p, unsigned int baud,
unsigned int *frac)
{
return dw8250_get_divisor_dfl(p, baud, frac);
}

static void dw8250_set_divisor(struct uart_port *p, unsigned int baud,
unsigned int quot, unsigned int quot_frac)
{
...
if (pd->dfl_size)
dw8250_set_divisor_dfl(p, baud, quot, quot_frac);
...
}

...

> + /* Use the BUSY-safe sequence for both integer and fractional divisors. */

...and here

p->get_divisor = dw8250_get_divisor;

> + p->set_divisor = dw8250_set_divisor;


--
With Best Regards,
Andy Shevchenko