* 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>
#define UART_TXCTRL_TXEN 0x1
#define UART_RXCTRL_RXEN 0x1
+/* IP register */
+#define UART_IP_RXWM 0x2
+
struct uart_sifive {
u32 txfifo;
u32 rxfifo;
};
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), ®s->div);
+ writel((uart_min_clk_divisor(clock, baud)), ®s->div);
}
static void _sifive_serial_init(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;
if (gd->flags & GD_FLG_RELOC)
return 0;
- platdata->saved_input_char = 0;
_sifive_serial_init(platdata->regs);
return 0;
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;
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(®s->ip) & UART_IP_RXWM);
+ else
return !!(readl(®s->txfifo) & UART_TXFIFO_FULL);
- }
}
static int sifive_serial_ofdata_to_platdata(struct udevice *dev)