drivers: serial_sifive: Fix baud rate calculation
authorAtish Patra <atish.patra@wdc.com>
Mon, 25 Feb 2019 08:15:02 +0000 (08:15 +0000)
committerAndes <uboot@andestech.com>
Wed, 27 Feb 2019 01:12:33 +0000 (09:12 +0800)
Compute the baud rate multipler with more precision.

Signed-off-by: Atish Patra <atish.patra@wdc.com>
Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Alexander Graf <agraf@suse.de>
Reviewed-by: Lukas Auer <lukas.auer@aisec.fraunhofer.de>
drivers/serial/serial_sifive.c

index 341728a690fd292f71b7da0dfe859599bce2d7f7..ea4d35d48c0681ce3a98bd7bb6e7f1624b302cd3 100644 (file)
@@ -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), &regs->div);
+       writel((uart_min_clk_divisor(clock, baud)), &regs->div);
 }
 
 static void _sifive_serial_init(struct uart_sifive *regs)