dm: core: Require users of devres to include the header
[oweals/u-boot.git] / drivers / serial / serial_sifive.c
index 341728a690fd292f71b7da0dfe859599bce2d7f7..5a02f0c8feb5b5e46b010cee702791716d8055ff 100644 (file)
@@ -3,8 +3,8 @@
  * Copyright (C) 2018 Anup Patel <anup@brainfault.org>
  */
 
-#include <clk.h>
 #include <common.h>
+#include <clk.h>
 #include <debug_uart.h>
 #include <dm.h>
 #include <errno.h>
@@ -13,6 +13,7 @@
 #include <asm/io.h>
 #include <linux/compiler.h>
 #include <serial.h>
+#include <linux/err.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -22,6 +23,9 @@ DECLARE_GLOBAL_DATA_PTR;
 #define UART_TXCTRL_TXEN       0x1
 #define UART_RXCTRL_RXEN       0x1
 
+/* IP register */
+#define UART_IP_RXWM            0x2
+
 struct uart_sifive {
        u32 txfifo;
        u32 rxfifo;
@@ -33,16 +37,39 @@ struct uart_sifive {
 };
 
 struct sifive_uart_platdata {
-       unsigned int clock;
-       int saved_input_char;
+       unsigned long clock;
        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)
@@ -70,32 +97,32 @@ static int _sifive_serial_getc(struct uart_sifive *regs)
                return -EAGAIN;
        ch &= UART_RXFIFO_DATA;
 
-       return (!ch) ? -EAGAIN : ch;
+       return ch;
 }
 
 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;
@@ -109,7 +136,6 @@ static int sifive_serial_probe(struct udevice *dev)
        if (gd->flags & GD_FLG_RELOC)
                return 0;
 
-       platdata->saved_input_char = 0;
        _sifive_serial_init(platdata->regs);
 
        return 0;
@@ -121,12 +147,6 @@ static int sifive_serial_getc(struct udevice *dev)
        struct sifive_uart_platdata *platdata = dev_get_platdata(dev);
        struct uart_sifive *regs = platdata->regs;
 
-       if (platdata->saved_input_char > 0) {
-               c = platdata->saved_input_char;
-               platdata->saved_input_char = 0;
-               return c;
-       }
-
        while ((c = _sifive_serial_getc(regs)) == -EAGAIN) ;
 
        return c;
@@ -147,14 +167,10 @@ static int sifive_serial_pending(struct udevice *dev, bool input)
        struct sifive_uart_platdata *platdata = dev_get_platdata(dev);
        struct uart_sifive *regs = platdata->regs;
 
-       if (input) {
-               if (platdata->saved_input_char > 0)
-                       return 1;
-               platdata->saved_input_char = _sifive_serial_getc(regs);
-               return (platdata->saved_input_char > 0) ? 1 : 0;
-       } else {
+       if (input)
+               return (readl(&regs->ip) & UART_IP_RXWM);
+       else
                return !!(readl(&regs->txfifo) & UART_TXFIFO_FULL);
-       }
 }
 
 static int sifive_serial_ofdata_to_platdata(struct udevice *dev)