summaryrefslogtreecommitdiff
path: root/drivers/serial
diff options
context:
space:
mode:
authorAtish Patra <atish.patra@wdc.com>2019-02-25 08:15:02 +0000
committerAndes <uboot@andestech.com>2019-02-27 09:12:33 +0800
commita3682008a03c0d4a58bcf62ca1e4a37187c9a8df (patch)
treeb640f60a2ae5a37e643cdf8ea9f6b31db86df1fb /drivers/serial
parentb630d57d0ab45639eea02f2671c2aa0d023c89ac (diff)
drivers: serial_sifive: Fix baud rate calculation
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>
Diffstat (limited to 'drivers/serial')
-rw-r--r--drivers/serial/serial_sifive.c28
1 files changed, 26 insertions, 2 deletions
diff --git a/drivers/serial/serial_sifive.c b/drivers/serial/serial_sifive.c
index 341728a690..ea4d35d48c 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), &regs->div);
+ writel((uart_min_clk_divisor(clock, baud)), &regs->div);
}
static void _sifive_serial_init(struct uart_sifive *regs)