// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2018 Anup Patel */ #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 struct uart_sifive { u32 txfifo; u32 rxfifo; u32 txctrl; u32 rxctrl; u32 ie; u32 ip; u32 div; }; struct sifive_uart_platdata { unsigned int clock; int saved_input_char; struct uart_sifive *regs; }; /* 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); } 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) ? -EAGAIN : ch; } static int sifive_serial_setbrg(struct udevice *dev, int baudrate) { int err; struct clk clk; struct sifive_uart_platdata *platdata = dev_get_platdata(dev); 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) { 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; } _sifive_serial_setbrg(platdata->regs, platdata->clock, baudrate); return 0; } static int sifive_serial_probe(struct udevice *dev) { struct sifive_uart_platdata *platdata = dev_get_platdata(dev); /* No need to reinitialize the UART after relocation */ if (gd->flags & GD_FLG_RELOC) return 0; platdata->saved_input_char = 0; _sifive_serial_init(platdata->regs); return 0; } static int sifive_serial_getc(struct udevice *dev) { int c; 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; } static int sifive_serial_putc(struct udevice *dev, const char ch) { int rc; struct sifive_uart_platdata *platdata = dev_get_platdata(dev); while ((rc = _sifive_serial_putc(platdata->regs, ch)) == -EAGAIN) ; return rc; } 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 { return !!(readl(®s->txfifo) & UART_TXFIFO_FULL); } } static int sifive_serial_ofdata_to_platdata(struct udevice *dev) { struct sifive_uart_platdata *platdata = dev_get_platdata(dev); platdata->regs = (struct uart_sifive *)dev_read_addr(dev); if (IS_ERR(platdata->regs)) return PTR_ERR(platdata->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, .ofdata_to_platdata = sifive_serial_ofdata_to_platdata, .platdata_auto_alloc_size = sizeof(struct sifive_uart_platdata), .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_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_DEBUG_UART_BASE; while (_sifive_serial_putc(regs, ch) == -EAGAIN) WATCHDOG_RESET(); } DEBUG_UART_FUNCS #endif