|
31 | 31 |
|
32 | 32 | /* Offsets for the DesignWare specific registers */
|
33 | 33 | #define DW_UART_USR 0x1f /* UART Status Register */
|
| 34 | +#define DW_UART_DLF 0xc0 /* Divisor Latch Fraction Register */ |
34 | 35 | #define DW_UART_CPR 0xf4 /* Component Parameter Register */
|
35 | 36 | #define DW_UART_UCV 0xf8 /* UART Component Version */
|
36 | 37 |
|
|
55 | 56 |
|
56 | 57 | struct dw8250_data {
|
57 | 58 | u8 usr_reg;
|
| 59 | + u8 dlf_size; |
58 | 60 | int line;
|
59 | 61 | int msr_mask_on;
|
60 | 62 | int msr_mask_off;
|
@@ -366,6 +368,37 @@ static bool dw8250_idma_filter(struct dma_chan *chan, void *param)
|
366 | 368 | return param == chan->device->dev->parent;
|
367 | 369 | }
|
368 | 370 |
|
| 371 | +/* |
| 372 | + * divisor = div(I) + div(F) |
| 373 | + * "I" means integer, "F" means fractional |
| 374 | + * quot = div(I) = clk / (16 * baud) |
| 375 | + * frac = div(F) * 2^dlf_size |
| 376 | + * |
| 377 | + * let rem = clk % (16 * baud) |
| 378 | + * we have: div(F) * (16 * baud) = rem |
| 379 | + * so frac = 2^dlf_size * rem / (16 * baud) = (rem << dlf_size) / (16 * baud) |
| 380 | + */ |
| 381 | +static unsigned int dw8250_get_divisor(struct uart_port *p, |
| 382 | + unsigned int baud, |
| 383 | + unsigned int *frac) |
| 384 | +{ |
| 385 | + unsigned int quot, rem, base_baud = baud * 16; |
| 386 | + struct dw8250_data *d = p->private_data; |
| 387 | + |
| 388 | + quot = p->uartclk / base_baud; |
| 389 | + rem = p->uartclk % base_baud; |
| 390 | + *frac = DIV_ROUND_CLOSEST(rem << d->dlf_size, base_baud); |
| 391 | + |
| 392 | + return quot; |
| 393 | +} |
| 394 | + |
| 395 | +static void dw8250_set_divisor(struct uart_port *p, unsigned int baud, |
| 396 | + unsigned int quot, unsigned int quot_frac) |
| 397 | +{ |
| 398 | + dw8250_writel_ext(p, DW_UART_DLF, quot_frac); |
| 399 | + serial8250_do_set_divisor(p, baud, quot, quot_frac); |
| 400 | +} |
| 401 | + |
369 | 402 | static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data)
|
370 | 403 | {
|
371 | 404 | if (p->dev->of_node) {
|
@@ -426,6 +459,18 @@ static void dw8250_setup_port(struct uart_port *p)
|
426 | 459 | dev_dbg(p->dev, "Designware UART version %c.%c%c\n",
|
427 | 460 | (reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff);
|
428 | 461 |
|
| 462 | + dw8250_writel_ext(p, DW_UART_DLF, ~0U); |
| 463 | + reg = dw8250_readl_ext(p, DW_UART_DLF); |
| 464 | + dw8250_writel_ext(p, DW_UART_DLF, 0); |
| 465 | + |
| 466 | + if (reg) { |
| 467 | + struct dw8250_data *d = p->private_data; |
| 468 | + |
| 469 | + d->dlf_size = fls(reg); |
| 470 | + p->get_divisor = dw8250_get_divisor; |
| 471 | + p->set_divisor = dw8250_set_divisor; |
| 472 | + } |
| 473 | + |
429 | 474 | reg = dw8250_readl_ext(p, DW_UART_CPR);
|
430 | 475 | if (!reg)
|
431 | 476 | return;
|
|
0 commit comments