|
@@ -0,0 +1,345 @@
|
|
|
+/*
|
|
|
+ * StarFive JH7100 Serial Port driver
|
|
|
+ * This driver ported from NS16550 Serial Port driver:
|
|
|
+ * drivers/serial/ns16550.c
|
|
|
+ * (which is originally from linux source (arch/powerpc/boot/ns16550.c))
|
|
|
+ *
|
|
|
+ * Modified for StarFive JH7100 SoC by
|
|
|
+ * TekkamanV <tekkamanv@starfivetech.com>
|
|
|
+ *
|
|
|
+ * SPDX-License-Identifier: GPL-2.0+
|
|
|
+ */
|
|
|
+#include <common.h>
|
|
|
+#include <clk.h>
|
|
|
+#include <debug_uart.h>
|
|
|
+#include <dm.h>
|
|
|
+#include <errno.h>
|
|
|
+#include <fdtdec.h>
|
|
|
+#include <log.h>
|
|
|
+#include <watchdog.h>
|
|
|
+#include <asm/io.h>
|
|
|
+#include <linux/compiler.h>
|
|
|
+#include <serial.h>
|
|
|
+#include <linux/err.h>
|
|
|
+
|
|
|
+#define UART_REG(x) u32 x
|
|
|
+typedef struct uart_starfive {
|
|
|
+ UART_REG(rbr); /* 0 */
|
|
|
+ UART_REG(ier); /* 1 */
|
|
|
+ UART_REG(fcr); /* 2 */
|
|
|
+ UART_REG(lcr); /* 3 */
|
|
|
+ UART_REG(mcr); /* 4 */
|
|
|
+ UART_REG(lsr); /* 5 */
|
|
|
+ UART_REG(msr); /* 6 */
|
|
|
+ UART_REG(spr); /* 7 */
|
|
|
+} uart_starfive_t;
|
|
|
+
|
|
|
+struct starfive_uart_platdata {
|
|
|
+ unsigned long clock;
|
|
|
+ struct uart_starfive *regs;
|
|
|
+};
|
|
|
+
|
|
|
+#define thr rbr
|
|
|
+#define iir fcr
|
|
|
+#define dll rbr
|
|
|
+#define dlh ier
|
|
|
+
|
|
|
+DECLARE_GLOBAL_DATA_PTR;
|
|
|
+
|
|
|
+/* TXCTRL register */
|
|
|
+#define UART_TXEN 0x1
|
|
|
+#define UART_TXWM(x) (((x) & 0xffff) << 16)
|
|
|
+
|
|
|
+/* RXCTRL register */
|
|
|
+#define UART_RXEN 0x1
|
|
|
+#define UART_RXWM(x) (((x) & 0xffff) << 16)
|
|
|
+
|
|
|
+/* IP register */
|
|
|
+#define UART_IP_TXWM 0x1
|
|
|
+#define UART_IP_RXWM 0x2
|
|
|
+
|
|
|
+#define UART_TXFIFO_FULL 0x80000000
|
|
|
+#define UART_RXFIFO_EMPTY 0x80000000
|
|
|
+
|
|
|
+#define SER_RBR 0x00
|
|
|
+#define SER_THR 0x00
|
|
|
+#define SER_DLL 0x00
|
|
|
+#define SER_DLH 0x04
|
|
|
+#define SER_IER 0x04
|
|
|
+#define SER_IIR 0x08
|
|
|
+#define SER_FCR 0x08
|
|
|
+#define SER_LCR 0x0C
|
|
|
+#define SER_MCR 0x10
|
|
|
+#define SER_LSR 0x14
|
|
|
+#define SER_MSR 0x18
|
|
|
+#define SER_SCR 0x1C
|
|
|
+
|
|
|
+//UART_LCR bit define
|
|
|
+#define LCR_WORD_LEN5 (0 << 0)
|
|
|
+#define LCR_WORD_LEN6 (1 << 0)
|
|
|
+#define LCR_WORD_LEN7 (2 << 0)
|
|
|
+#define LCR_WORD_LEN8 (3 << 0)
|
|
|
+#define LCR_STOP_1BIT (0 << 2)
|
|
|
+#define LCR_STOP_2BIT (1 << 2)
|
|
|
+#define LCR_PE (1 << 3)
|
|
|
+#define LCR_EPS (1 << 4)
|
|
|
+#define LCR_SP (1 << 5)
|
|
|
+#define LCR_BC (1 << 6)
|
|
|
+#define LCR_DLAB (1 << 7)
|
|
|
+
|
|
|
+//IER, interrupt enable reg
|
|
|
+#define IER_ERBFI (1 << 0) //rx interrupt enable
|
|
|
+#define IER_ETBEI (1 << 1) //tx interrupt enable
|
|
|
+#define IER_ELSI (1 << 2)
|
|
|
+#define IER_EDSSI (1 << 3)
|
|
|
+#define IER_PTIME (1 << 7)
|
|
|
+
|
|
|
+//IIR, interrupt identify reg
|
|
|
+#define INTR_ID_RLSI 0x06
|
|
|
+#define INTR_ID_DATAVL 0x04
|
|
|
+#define INTR_ID_TIMEOUT 0x0C
|
|
|
+#define INTR_ID_THR 0x02
|
|
|
+
|
|
|
+//FCR
|
|
|
+#define RCVR_TRIG_1C (0x00 << 0x06) //1 char
|
|
|
+#define RCVR_TRIG_QFL (0x01 << 0x06) //1/4 FIFO
|
|
|
+#define RCVR_TRIG_HFL (0x02 << 0x06) //1/2 FIFO
|
|
|
+#define RCVR_TRIG_LTF (0x03 << 0x06) //2 less than full
|
|
|
+
|
|
|
+#define TXEM_TRIG_EMT (0x00 << 0x04) //FIFO empty
|
|
|
+#define TXEM_TRIG_2C (0x00 << 0x04) //2 char
|
|
|
+#define TXEM_TRIG_QFL (0x02 << 0x04) //1/4 FIFO
|
|
|
+#define TXEM_TRIG_HFL (0x03 << 0x04) //1/2 FIFO
|
|
|
+
|
|
|
+#define DMA_MOD1 (0x01 << 0x03)
|
|
|
+#define DMA_MOD0 (0x00 << 0x03)
|
|
|
+#define TXFIFO_RST (0x01 << 0x02)
|
|
|
+#define RXFIFO_RST (0x01 << 0x01)
|
|
|
+#define FIFO_ENA (0x01 << 0x00)
|
|
|
+
|
|
|
+//LSR
|
|
|
+#define DATA_RDY (0x01 << 0x00) //at least one byte available
|
|
|
+#define RCV_OVERUN (0x01 << 0x01)
|
|
|
+#define ERR_PARITY (0x01 << 0x02)
|
|
|
+#define ERR_FRAMING (0x01 << 0x03)
|
|
|
+#define ID_BREAK (0x01 << 0x04) //break interrupt bit
|
|
|
+#define ID_THRE (0x01 << 0x05) //in FIFO THRE mode, it is the xmit fifo full flag !!!
|
|
|
+#define ID_TEMPT (0x01 << 0x06)
|
|
|
+#define ERR_RPE (0x01 << 0x07)
|
|
|
+
|
|
|
+/* MCR */
|
|
|
+#define MCR_DTR (0x01 << 0x00) /* dtr output */
|
|
|
+#define MCR_RTS (0x01 << 0x01) /* rts output */
|
|
|
+#define MCR_OUT1 (0x01 << 0x02) /* output #1 */
|
|
|
+#define MCR_OUT2 (0x01 << 0x03) /* output #2 */
|
|
|
+#define MCR_LOOP (0x01 << 0x04) /* loop back */
|
|
|
+#define MCR_AFCE (0x01 << 0x05) /* auto flow control enable */
|
|
|
+
|
|
|
+static void _ser_clrrxtmo(struct uart_starfive *regs)
|
|
|
+{
|
|
|
+ u32 reg32_val;
|
|
|
+
|
|
|
+ while(1) {
|
|
|
+ reg32_val = readl(®s->iir);
|
|
|
+ if((reg32_val & INTR_ID_TIMEOUT) != INTR_ID_TIMEOUT)
|
|
|
+ break;
|
|
|
+ reg32_val = readl(®s->rbr);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* Set up the baud rate in gd struct */
|
|
|
+static void _starfive_serial_setbrg(struct uart_starfive *regs,
|
|
|
+ unsigned long clock, unsigned long baud)
|
|
|
+{
|
|
|
+ u64 baud_value = ((clock / baud) >> 4);
|
|
|
+
|
|
|
+ writel((baud_value & 0xFF), ®s->dll);
|
|
|
+ writel(((baud_value >> 8) & 0xFF), ®s->dlh);
|
|
|
+}
|
|
|
+
|
|
|
+static void _starfive_serial_init(struct uart_starfive *regs)
|
|
|
+{
|
|
|
+ u32 datab, stopb, par, mcr;
|
|
|
+
|
|
|
+ u8 databits = 8;
|
|
|
+ u8 stopbits = 1;
|
|
|
+ u8 parity = 0;
|
|
|
+ u8 flow_ctl = 0;
|
|
|
+
|
|
|
+ if (databits >= 5 && databits <= 8)
|
|
|
+ datab = (databits - 5);
|
|
|
+ else
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (stopbits == 1)
|
|
|
+ stopb = LCR_STOP_1BIT;
|
|
|
+ else if (stopbits == 2)
|
|
|
+ stopb = LCR_STOP_2BIT;
|
|
|
+ else
|
|
|
+ return;
|
|
|
+
|
|
|
+ par = (parity == 0) ? 0 : (parity == 1) ? LCR_PE : (LCR_PE | LCR_EPS);
|
|
|
+
|
|
|
+ writel(LCR_DLAB, ®s->lcr);
|
|
|
+ writel(datab | stopb | par, ®s->lcr);
|
|
|
+
|
|
|
+ //enable fifo and reset fifo, 1-byte int trig!
|
|
|
+ writel((RCVR_TRIG_1C | TXEM_TRIG_EMT | TXFIFO_RST | RXFIFO_RST |
|
|
|
+ FIFO_ENA), ®s->fcr);
|
|
|
+
|
|
|
+ mcr = (flow_ctl == 0) ? 0 : MCR_RTS | MCR_AFCE;
|
|
|
+ writel(mcr, ®s->mcr);
|
|
|
+
|
|
|
+ //dis the ser interrupt
|
|
|
+ writel(0, ®s->ier);
|
|
|
+
|
|
|
+ _ser_clrrxtmo(regs);
|
|
|
+}
|
|
|
+
|
|
|
+static int _starfive_serial_putc(struct uart_starfive *regs, const char c)
|
|
|
+{
|
|
|
+ if (readl(®s->lsr) & ID_TEMPT) {
|
|
|
+ writel(c, ®s->thr);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ return -EAGAIN;
|
|
|
+}
|
|
|
+
|
|
|
+static int _starfive_serial_getc(struct uart_starfive *regs)
|
|
|
+{
|
|
|
+ /* Wait here until the the FIFO is not full */
|
|
|
+ if (readl(®s->lsr) & DATA_RDY)
|
|
|
+ return readl(®s->rbr);
|
|
|
+
|
|
|
+ return -EAGAIN;
|
|
|
+}
|
|
|
+
|
|
|
+static int starfive_serial_setbrg(struct udevice *dev, int baudrate)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+ u32 clock = 0;
|
|
|
+ struct clk clk;
|
|
|
+ struct starfive_uart_platdata *platdata = dev_get_plat(dev);
|
|
|
+
|
|
|
+ ret = clk_get_by_index(dev, 0, &clk);
|
|
|
+ if (IS_ERR_VALUE(ret)) {
|
|
|
+ debug("StarFive UART failed to get clock\n");
|
|
|
+ ret = dev_read_u32(dev, "clock-frequency", &clock);
|
|
|
+ if (IS_ERR_VALUE(ret)) {
|
|
|
+ debug("StarFive UART clock not defined\n");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ clock = clk_get_rate(&clk);
|
|
|
+ if (IS_ERR_VALUE(clock)) {
|
|
|
+ debug("StarFive UART clock get rate failed\n");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ platdata->clock = clock;
|
|
|
+ _starfive_serial_setbrg(platdata->regs, platdata->clock, baudrate);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int starfive_serial_probe(struct udevice *dev)
|
|
|
+{
|
|
|
+ struct starfive_uart_platdata *platdata = dev_get_plat(dev);
|
|
|
+
|
|
|
+ /* No need to reinitialize the UART after relocation */
|
|
|
+ if (gd->flags & GD_FLG_RELOC)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ _starfive_serial_init(platdata->regs);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int starfive_serial_getc(struct udevice *dev)
|
|
|
+{
|
|
|
+ int c;
|
|
|
+ struct starfive_uart_platdata *platdata = dev_get_plat(dev);
|
|
|
+ struct uart_starfive *regs = platdata->regs;
|
|
|
+
|
|
|
+ while ((c = _starfive_serial_getc(regs)) == -EAGAIN) ;
|
|
|
+
|
|
|
+ return c;
|
|
|
+}
|
|
|
+
|
|
|
+static int starfive_serial_putc(struct udevice *dev, const char ch)
|
|
|
+{
|
|
|
+ int rc;
|
|
|
+ struct starfive_uart_platdata *platdata = dev_get_plat(dev);
|
|
|
+
|
|
|
+ while ((rc = _starfive_serial_putc(platdata->regs, ch)) == -EAGAIN) ;
|
|
|
+
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static int starfive_serial_pending(struct udevice *dev, bool input)
|
|
|
+{
|
|
|
+ struct starfive_uart_platdata *platdata = dev_get_plat(dev);
|
|
|
+ struct uart_starfive *regs = platdata->regs;
|
|
|
+
|
|
|
+ if (input)
|
|
|
+ return (readl(®s->lsr) & DATA_RDY) ? 1 : 0;
|
|
|
+ else
|
|
|
+ return (readl(®s->lsr) & ID_THRE) ? 0 : 1;
|
|
|
+}
|
|
|
+
|
|
|
+static int starfive_serial_of_to_plat(struct udevice *dev)
|
|
|
+{
|
|
|
+ struct starfive_uart_platdata *platdata = dev_get_plat(dev);
|
|
|
+
|
|
|
+ platdata->regs = (struct uart_starfive *)dev_read_addr(dev);
|
|
|
+ if (IS_ERR(platdata->regs))
|
|
|
+ return PTR_ERR(platdata->regs);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static const struct dm_serial_ops starfive_serial_ops = {
|
|
|
+ .putc = starfive_serial_putc,
|
|
|
+ .getc = starfive_serial_getc,
|
|
|
+ .pending = starfive_serial_pending,
|
|
|
+ .setbrg = starfive_serial_setbrg,
|
|
|
+};
|
|
|
+
|
|
|
+static const struct udevice_id starfive_serial_ids[] = {
|
|
|
+ { .compatible = "starfive,uart0" },
|
|
|
+ { }
|
|
|
+};
|
|
|
+
|
|
|
+U_BOOT_DRIVER(serial_starfive) = {
|
|
|
+ .name = "serial_starfive",
|
|
|
+ .id = UCLASS_SERIAL,
|
|
|
+ .of_match = starfive_serial_ids,
|
|
|
+ .of_to_plat = starfive_serial_of_to_plat,
|
|
|
+ .plat_auto = sizeof(struct starfive_uart_platdata),
|
|
|
+ .probe = starfive_serial_probe,
|
|
|
+ .ops = &starfive_serial_ops,
|
|
|
+};
|
|
|
+
|
|
|
+#ifdef CONFIG_DEBUG_UART_STARFIVE
|
|
|
+static inline void _debug_uart_init(void)
|
|
|
+{
|
|
|
+ struct uart_starfive *regs =
|
|
|
+ (struct uart_starfive *)CONFIG_DEBUG_UART_BASE;
|
|
|
+
|
|
|
+ _starfive_serial_setbrg(regs, CONFIG_DEBUG_UART_CLOCK,
|
|
|
+ CONFIG_BAUDRATE);
|
|
|
+ _starfive_serial_init(regs);
|
|
|
+}
|
|
|
+
|
|
|
+static inline void _debug_uart_putc(int ch)
|
|
|
+{
|
|
|
+ struct uart_starfive *regs =
|
|
|
+ (struct uart_starfive *)CONFIG_DEBUG_UART_BASE;
|
|
|
+
|
|
|
+ while (_starfive_serial_putc(regs, ch) == -EAGAIN)
|
|
|
+ WATCHDOG_RESET();
|
|
|
+}
|
|
|
+
|
|
|
+DEBUG_UART_FUNCS
|
|
|
+#endif
|