Hi Andy, On Mon, 9 Jul 2018 16:23:15 +0800 Jisheng Zhang wrote: > For Synopsys DesignWare 8250 uart which version >= 4.00a, there's a > valid divisor latch fraction register. The fractional divisor width is > 4bits ~ 6bits. > > Now the preparation is done, it's easy to add the feature support. > This patch firstly tries to get the fractional divisor width during > probe, then setups dw specific get_divisor() and set_divisor() hook. > > Signed-off-by: Jisheng Zhang <Jisheng.Zhang@xxxxxxxxxxxxx> > --- > drivers/tty/serial/8250/8250_dw.c | 45 +++++++++++++++++++++++++++++++ > 1 file changed, 45 insertions(+) > > diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c > index fa8a00e8c9c6..e90c3d229f00 100644 > --- a/drivers/tty/serial/8250/8250_dw.c > +++ b/drivers/tty/serial/8250/8250_dw.c > @@ -31,6 +31,7 @@ > > /* Offsets for the DesignWare specific registers */ > #define DW_UART_USR 0x1f /* UART Status Register */ > +#define DW_UART_DLF 0xc0 /* Divisor Latch Fraction Register */ > #define DW_UART_CPR 0xf4 /* Component Parameter Register */ > #define DW_UART_UCV 0xf8 /* UART Component Version */ > > @@ -55,6 +56,7 @@ > > struct dw8250_data { > u8 usr_reg; > + u8 dlf_size; > int line; > int msr_mask_on; > int msr_mask_off; > @@ -366,6 +368,37 @@ static bool dw8250_idma_filter(struct dma_chan *chan, void *param) > return param == chan->device->dev->parent; > } > > +/* > + * divisor = clk / (16 * baud) = div(I) + div(F) > + * "I" means integer, "F" means fractional > + * > + * 2^dlf_size * clk / (16 * baud) = 2^dlf_size * (div(I) + div(F)) > + * so, (clk << (dlf_siz - 4)) / baud = (div(I) + div(F)) << dlf_size > + * > + * let quot = DIV_ROUND_CLOSEST(clk << (dlf_size - 4), baud), we get > + * div(I) = quot >> dlf_size > + * div(F) = quot & dlf_mask, where dlf_mask = GENMASK(dlf_size - 1, 0) > + */ > +static unsigned int dw8250_get_divisor(struct uart_port *p, > + unsigned int baud, > + unsigned int *frac) > +{ > + unsigned int quot; > + struct dw8250_data *d = p->private_data; > + > + quot = DIV_ROUND_CLOSEST(p->uartclk << (d->dlf_size - 4), baud); > + *frac = quot & GENMASK(d->dlf_size - 1, 0); > + > + return quot >> d->dlf_size; After more consideration, I sent out this version for the following two points: 1. the max frac divisor width is 6bits now, but we dunno whether future IP extends it or not. This patch version can still support > 6bits width except the overflow concern. But fixing overflow(if there is) is as simple as using\ the DIV_ROUND_CLOSEST_ULL macro. 2. this version makes use of well implemented GENMASK to get the dlf_mask, the micro is well understood, I think. so the code is simplified as well. 3. the magic "4" is explained in the comments, I hope it could help the code. what do you think? Thanks in advance, Jisheng > +} > + > +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); > + serial8250_do_set_divisor(p, baud, quot, quot_frac); > +} > + > static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data) > { > if (p->dev->of_node) { > @@ -426,6 +459,18 @@ static void dw8250_setup_port(struct uart_port *p) > dev_dbg(p->dev, "Designware UART version %c.%c%c\n", > (reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff); > > + dw8250_writel_ext(p, DW_UART_DLF, ~0U); > + reg = dw8250_readl_ext(p, DW_UART_DLF); > + dw8250_writel_ext(p, DW_UART_DLF, 0); > + > + if (reg) { > + struct dw8250_data *d = p->private_data; > + > + d->dlf_size = fls(reg); > + p->get_divisor = dw8250_get_divisor; > + p->set_divisor = dw8250_set_divisor; > + } > + > reg = dw8250_readl_ext(p, DW_UART_CPR); > if (!reg) > return; -- To unsubscribe from this list: send the line "unsubscribe linux-serial" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html