// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2018 Anup Patel */ #include #include #include #include #include #include #include #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 sifive_serial_regs { u32 txfifo; u32 rxfifo; u32 txctrl; u32 rxctrl; u32 ie; u32 ip; u32 div; }; struct sifive_serial_priv { unsigned long freq; struct sifive_serial_regs __iomem *regs; struct console_device cdev; }; #define to_priv(cdev) container_of(cdev, struct sifive_serial_priv, cdev) /** * 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; } static void sifive_serial_init(struct sifive_serial_regs __iomem *regs) { writel(UART_TXCTRL_TXEN, ®s->txctrl); writel(UART_RXCTRL_RXEN, ®s->rxctrl); writel(0, ®s->ie); } static int sifive_serial_setbrg(struct console_device *cdev, int baudrate) { struct sifive_serial_priv *priv = to_priv(cdev); writel((uart_min_clk_divisor(priv->freq, baudrate)), &priv->regs->div); return 0; } static int sifive_serial_getc(struct console_device *cdev) { struct sifive_serial_regs __iomem *regs = to_priv(cdev)->regs; u32 ch; do { ch = readl(®s->rxfifo); } while (ch & UART_RXFIFO_EMPTY); return ch & UART_RXFIFO_DATA; } static void sifive_serial_putc(struct console_device *cdev, const char ch) { struct sifive_serial_regs __iomem *regs = to_priv(cdev)->regs; // TODO: how to check for !empty to utilize fifo? while (readl(®s->txfifo) & UART_TXFIFO_FULL) ; writel(ch, ®s->txfifo); } static int sifive_serial_tstc(struct console_device *cdev) { struct sifive_serial_regs __iomem *regs = to_priv(cdev)->regs; return readl(®s->ip) & UART_IP_RXWM; } static void sifive_serial_flush(struct console_device *cdev) { struct sifive_serial_regs __iomem *regs = to_priv(cdev)->regs; while (readl(®s->txfifo) & UART_TXFIFO_FULL) ; } static int sifive_serial_probe(struct device_d *dev) { struct sifive_serial_priv *priv; struct resource *iores; struct clk *clk; u32 freq; int ret; clk = clk_get(dev, NULL); if (!IS_ERR(clk)) { freq = clk_get_rate(clk); } else { dev_dbg(dev, "failed to get clock. Fallback to device tree.\n"); ret = of_property_read_u32(dev->device_node, "clock-frequency", &freq); if (ret) { dev_warn(dev, "unknown clock frequency\n"); return ret; } } iores = dev_request_mem_resource(dev, 0); if (IS_ERR(iores)) return PTR_ERR(iores); priv = xzalloc(sizeof(*priv)); priv->freq = freq; priv->regs = IOMEM(iores->start); priv->cdev.dev = dev; priv->cdev.putc = sifive_serial_putc; priv->cdev.getc = sifive_serial_getc; priv->cdev.tstc = sifive_serial_tstc; priv->cdev.flush = sifive_serial_flush; priv->cdev.setbrg = sifive_serial_setbrg, sifive_serial_init(priv->regs); return console_register(&priv->cdev); } static __maybe_unused struct of_device_id sifive_serial_dt_ids[] = { { .compatible = "sifive,uart0" }, { /* sentinel */ } }; static struct driver_d serial_sifive_driver = { .name = "serial_sifive", .probe = sifive_serial_probe, .of_compatible = sifive_serial_dt_ids, }; console_platform_driver(serial_sifive_driver);