// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2018 Anup Patel */ #include #include #include #include #include #include #include #include #include #include #include #include DECLARE_GLOBAL_DATA_PTR; #define UART_TXFIFO_FULL 0x80000000 #define UART_RXFIFO_EMPTY 0x80000000 #define UART_RXFIFO_DATA 0x000000ff #define UART_TXCTRL_TXEN 0x1 #define UART_RXCTRL_RXEN 0x1 /* IP register */ #define UART_IP_RXWM 0x2 struct uart_sifive { u32 txfifo; u32 rxfifo; u32 txctrl; u32 rxctrl; u32 ie; u32 ip; u32 div; }; struct sifive_uart_plat { 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((uart_min_clk_divisor(clock, baud)), ®s->div); } static void _sifive_serial_init(struct uart_sifive *regs) { writel(UART_TXCTRL_TXEN, ®s->txctrl); writel(UART_RXCTRL_RXEN, ®s->rxctrl); writel(0, ®s->ie); } static int _sifive_serial_putc(struct uart_sifive *regs, const char c) { if (readl(®s->txfifo) & UART_TXFIFO_FULL) return -EAGAIN; writel(c, ®s->txfifo); return 0; } static int _sifive_serial_getc(struct uart_sifive *regs) { int ch = readl(®s->rxfifo); if (ch & UART_RXFIFO_EMPTY) return -EAGAIN; ch &= UART_RXFIFO_DATA; return ch; } static int sifive_serial_setbrg(struct udevice *dev, int baudrate) { int ret; struct clk clk; struct sifive_uart_plat *plat = dev_get_plat(dev); u32 clock = 0; ret = clk_get_by_index(dev, 0, &clk); if (IS_ERR_VALUE(ret)) { debug("SiFive UART failed to get clock\n"); 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; } } plat->clock = clock; _sifive_serial_setbrg(plat->regs, plat->clock, baudrate); return 0; } static int sifive_serial_probe(struct udevice *dev) { struct sifive_uart_plat *plat = dev_get_plat(dev); /* No need to reinitialize the UART after relocation */ if (gd->flags & GD_FLG_RELOC) return 0; _sifive_serial_init(plat->regs); return 0; } static int sifive_serial_getc(struct udevice *dev) { int c; struct sifive_uart_plat *plat = dev_get_plat(dev); struct uart_sifive *regs = plat->regs; while ((c = _sifive_serial_getc(regs)) == -EAGAIN) ; return c; } static int sifive_serial_putc(struct udevice *dev, const char ch) { int rc; struct sifive_uart_plat *plat = dev_get_plat(dev); while ((rc = _sifive_serial_putc(plat->regs, ch)) == -EAGAIN) ; return rc; } static int sifive_serial_pending(struct udevice *dev, bool input) { struct sifive_uart_plat *plat = dev_get_plat(dev); struct uart_sifive *regs = plat->regs; if (input) return (readl(®s->ip) & UART_IP_RXWM); else return !!(readl(®s->txfifo) & UART_TXFIFO_FULL); } static int sifive_serial_of_to_plat(struct udevice *dev) { struct sifive_uart_plat *plat = dev_get_plat(dev); plat->regs = (struct uart_sifive *)(uintptr_t)dev_read_addr(dev); if (IS_ERR(plat->regs)) return PTR_ERR(plat->regs); return 0; } static const struct dm_serial_ops sifive_serial_ops = { .putc = sifive_serial_putc, .getc = sifive_serial_getc, .pending = sifive_serial_pending, .setbrg = sifive_serial_setbrg, }; static const struct udevice_id sifive_serial_ids[] = { { .compatible = "sifive,uart0" }, { } }; U_BOOT_DRIVER(serial_sifive) = { .name = "serial_sifive", .id = UCLASS_SERIAL, .of_match = sifive_serial_ids, .of_to_plat = sifive_serial_of_to_plat, .plat_auto = sizeof(struct sifive_uart_plat), .probe = sifive_serial_probe, .ops = &sifive_serial_ops, }; #ifdef CONFIG_DEBUG_UART_SIFIVE static inline void _debug_uart_init(void) { struct uart_sifive *regs = (struct uart_sifive *)CONFIG_VAL(DEBUG_UART_BASE); _sifive_serial_setbrg(regs, CONFIG_DEBUG_UART_CLOCK, CONFIG_BAUDRATE); _sifive_serial_init(regs); } static inline void _debug_uart_putc(int ch) { struct uart_sifive *regs = (struct uart_sifive *)CONFIG_VAL(DEBUG_UART_BASE); while (_sifive_serial_putc(regs, ch) == -EAGAIN) schedule(); } DEBUG_UART_FUNCS #endif