diff options
Diffstat (limited to 'drivers/serial/serial_sifive.c')
-rw-r--r-- | drivers/serial/serial_sifive.c | 60 |
1 files changed, 42 insertions, 18 deletions
diff --git a/drivers/serial/serial_sifive.c b/drivers/serial/serial_sifive.c index 341728a690..537bc7a975 100644 --- a/drivers/serial/serial_sifive.c +++ b/drivers/serial/serial_sifive.c @@ -33,16 +33,40 @@ struct uart_sifive { }; struct sifive_uart_platdata { - unsigned int clock; + unsigned long clock; int saved_input_char; struct uart_sifive *regs; }; +/** + * Find minimum divisor divides in_freq to max_target_hz; + * Based on uart driver n SiFive FSBL. + * + * f_baud = f_in / (div + 1) => div = (f_in / f_baud) - 1 + * The nearest integer solution requires rounding up as to not exceed + * max_target_hz. + * div = ceil(f_in / f_baud) - 1 + * = floor((f_in - 1 + f_baud) / f_baud) - 1 + * This should not overflow as long as (f_in - 1 + f_baud) does not exceed + * 2^32 - 1, which is unlikely since we represent frequencies in kHz. + */ +static inline unsigned int uart_min_clk_divisor(unsigned long in_freq, + unsigned long max_target_hz) +{ + unsigned long quotient = + (in_freq + max_target_hz - 1) / (max_target_hz); + /* Avoid underflow */ + if (quotient == 0) + return 0; + else + return quotient - 1; +} + /* Set up the baud rate in gd struct */ static void _sifive_serial_setbrg(struct uart_sifive *regs, unsigned long clock, unsigned long baud) { - writel((u32)((clock / baud) - 1), ®s->div); + writel((uart_min_clk_divisor(clock, baud)), ®s->div); } static void _sifive_serial_init(struct uart_sifive *regs) @@ -75,27 +99,27 @@ static int _sifive_serial_getc(struct uart_sifive *regs) static int sifive_serial_setbrg(struct udevice *dev, int baudrate) { - int err; + int ret; struct clk clk; struct sifive_uart_platdata *platdata = dev_get_platdata(dev); + u32 clock = 0; - err = clk_get_by_index(dev, 0, &clk); - if (!err) { - err = clk_get_rate(&clk); - if (!IS_ERR_VALUE(err)) - platdata->clock = err; - } else if (err != -ENOENT && err != -ENODEV && err != -ENOSYS) { + ret = clk_get_by_index(dev, 0, &clk); + if (IS_ERR_VALUE(ret)) { debug("SiFive UART failed to get clock\n"); - return err; - } - - if (!platdata->clock) - platdata->clock = dev_read_u32_default(dev, "clock-frequency", 0); - if (!platdata->clock) { - debug("SiFive UART clock not defined\n"); - return -EINVAL; + ret = dev_read_u32(dev, "clock-frequency", &clock); + if (IS_ERR_VALUE(ret)) { + debug("SiFive UART clock not defined\n"); + return 0; + } + } else { + clock = clk_get_rate(&clk); + if (IS_ERR_VALUE(clock)) { + debug("SiFive UART clock get rate failed\n"); + return 0; + } } - + platdata->clock = clock; _sifive_serial_setbrg(platdata->regs, platdata->clock, baudrate); return 0; |