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