The fractional divider is used to provide different frequencies from one source. The following formulas are used to show the relations between Fref, Fuart, prescaler, nominator, and denominator values: Fuart = Fref * m / n baud = Fuart / (prescaler * DLAB) where prescaler value is in range 1..16. The helper function returns prescaler, nominator, and denominator values. It also sets port UART clock to the corresponding value. Since serial core considers base Fuart = baud * 16, the given value of Fuart will be in proportion to that: Port UART clock = Fuart * 16 / prescaler Signed-off-by: Andy Shevchenko <andriy.shevchenko@xxxxxxxxxxxxxxx> --- drivers/tty/serial/8250/8250.h | 4 +++ drivers/tty/serial/8250/8250_core.c | 54 +++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 656ecc6..feed6a3 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -198,3 +198,7 @@ static inline int serial8250_request_dma(struct uart_8250_port *p) } static inline void serial8250_release_dma(struct uart_8250_port *p) { } #endif + +unsigned short serial8250_get_ps(struct uart_port *port, unsigned int baud, + unsigned int fref, unsigned short width, + unsigned int *m, unsigned int *n); diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 7b502c0..09803f5 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -27,6 +27,7 @@ #include <linux/console.h> #include <linux/sysrq.h> #include <linux/delay.h> +#include <linux/gcd.h> #include <linux/platform_device.h> #include <linux/tty.h> #include <linux/ratelimit.h> @@ -2412,6 +2413,59 @@ static void serial8250_shutdown(struct uart_port *port) serial8250_do_shutdown(port); } +/** + * serial8250_get_ps - calculates prescaler, nominator, and denominator values + * @port: pointer to struct uart_port object + * @baud: baud rate + * @fref: reference frequency in Hz + * @width: maximum width of @m and @n in bits + * @m: nominator of fractional divider + * @n: denominator of fractional divider + * + * The fractional divider is used to provide different frequencies from one + * source. The following formulas are used to show the relations between Fref, + * Fuart, prescaler, nominator, and denominator values: + * + * Fuart = Fref * m / n, where m <= n + * + * baud = Fuart / (prescaler * DLAB) + * + * Return: + * Prescaler value from 16 to 1, and sets port UART clock to the corresponding + * value. Since serial core considers base Fuart = baud * 16, the given value + * of Fuart will be in proportion to that: + * + * Port UART clock = Fuart * 16 / prescaler + */ +unsigned short serial8250_get_ps(struct uart_port *port, unsigned int baud, + unsigned int fref, unsigned short width, + unsigned int *m, unsigned int *n) +{ + unsigned int fuart; + unsigned int ps = 16, divisor, denominator; + + /* Find prescaler value that satisfies Fuart < Fref */ + do { + fuart = baud * ps; + } while ((fuart > fref) && (--ps > 1)); + + /* Get Fuart closer to Fref */ + fuart *= rounddown_pow_of_two(fref / fuart); + + /* Get a greatest common divisor */ + divisor = gcd(fref, fuart); + + denominator = fref / divisor; + while (denominator > BIT(width)) { + divisor <<= 1; + denominator >>= 1; + } + *n = denominator; + *m = fuart / divisor; + port->uartclk = fuart * 16 / ps; + return ps; +} + /* * XR17V35x UARTs have an extra fractional divisor register (DLD) * Calculate divisor with extra 4-bit fractional portion -- 2.1.4 -- 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