2022-11-27 14:05:23

by Jisheng Zhang

[permalink] [raw]
Subject: [PATCH v2 2/9] serial: bflb_uart: add Bouffalolab UART Driver

Add the driver for Bouffalolab UART IP which is found in Bouffalolab
SoCs such as bl808.

UART driver probe will create path named "/dev/ttySx".

Signed-off-by: Jisheng Zhang <[email protected]>
---
drivers/tty/serial/Kconfig | 18 +
drivers/tty/serial/Makefile | 1 +
drivers/tty/serial/bflb_uart.c | 610 +++++++++++++++++++++++++++++++
include/uapi/linux/serial_core.h | 3 +
4 files changed, 632 insertions(+)
create mode 100644 drivers/tty/serial/bflb_uart.c

diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 434f83168546..056a8144e2cc 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -179,6 +179,24 @@ config SERIAL_ATMEL_TTYAT

Say Y if you have an external 8250/16C550 UART. If unsure, say N.

+config SERIAL_BFLB
+ tristate "Bouffalolab serial port support"
+ select SERIAL_CORE
+ depends on COMMON_CLK
+ help
+ This enables the driver for the Bouffalolab's serial.
+
+config SERIAL_BFLB_CONSOLE
+ bool "Support for console on Bouffalolab serial port"
+ depends on SERIAL_BFLB
+ select SERIAL_CORE_CONSOLE
+ select SERIAL_EARLYCON
+ help
+ Say Y here if you wish to use a Bouffalolab UART as the
+ system console (the system console is the device which
+ receives all kernel messages and warnings and which allows
+ logins in single user mode) as /dev/ttySn.
+
config SERIAL_KGDB_NMI
bool "Serial console over KGDB NMI debugger port"
depends on KGDB_SERIAL_CONSOLE
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 238a9557b487..8509cdc11d87 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_SERIAL_8250) += 8250/

obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o
obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o
+obj-$(CONFIG_SERIAL_BFLB) += bflb_uart.o
obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o
obj-$(CONFIG_SERIAL_PXA_NON8250) += pxa.o
obj-$(CONFIG_SERIAL_SA1100) += sa1100.o
diff --git a/drivers/tty/serial/bflb_uart.c b/drivers/tty/serial/bflb_uart.c
new file mode 100644
index 000000000000..1816c5c71cae
--- /dev/null
+++ b/drivers/tty/serial/bflb_uart.c
@@ -0,0 +1,610 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Based on bflb_uart.c, by Bouffalolab team
+ *
+ * Copyright (C) 2022 Jisheng Zhang <[email protected]>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+#define UART_UTX_CONFIG 0x00
+#define UART_CR_UTX_EN BIT(0)
+#define UART_CR_UTX_CTS_EN BIT(1)
+#define UART_CR_UTX_FRM_EN BIT(2)
+#define UART_CR_UTX_PRT_EN BIT(4)
+#define UART_CR_UTX_PRT_SEL BIT(5)
+#define UART_CR_UTX_BIT_CNT_D_MSK GENMASK(10, 8)
+#define UART_CR_UTX_BIT_CNT_P_MSK GENMASK(12, 11)
+#define UART_URX_CONFIG 0x04
+#define UART_CR_URX_EN BIT(0)
+#define UART_CR_URX_PRT_EN BIT(4)
+#define UART_CR_URX_PRT_SEL BIT(5)
+#define UART_CR_URX_BIT_CNT_D_MSK GENMASK(10, 8)
+#define UART_BIT_PRD 0x08
+#define UART_CR_UTX_BIT_PRD_MSK GENMASK(15, 0)
+#define UART_CR_URX_BIT_PRD_MSK GENMASK(31, 16)
+#define UART_DATA_CONFIG 0x0c
+#define UART_CR_UART_BIT_INV BIT(0)
+#define UART_URX_RTO_TIMER 0x18
+#define UART_CR_URX_RTO_VALUE_MSK GENMASK(7, 0)
+#define UART_SW_MODE 0x1c
+#define UART_INT_STS 0x20
+#define UART_UTX_END_INT BIT(0)
+#define UART_URX_END_INT BIT(1)
+#define UART_UTX_FIFO_INT BIT(2)
+#define UART_URX_FIFO_INT BIT(3)
+#define UART_URX_RTO_INT BIT(4)
+#define UART_URX_PCE_INT BIT(5)
+#define UART_UTX_FER_INT BIT(6)
+#define UART_URX_FER_INT BIT(7)
+#define UART_URX_LSE_INT BIT(8)
+#define UART_INT_MASK 0x24
+#define UART_INT_CLEAR 0x28
+#define UART_INT_EN 0x2c
+#define UART_STATUS 0x30
+#define UART_STS_UTX_BUS_BUSY BIT(0)
+#define UART_FIFO_CONFIG_0 0x80
+#define UART_DMA_TX_EN BIT(0)
+#define UART_DMA_RX_EN BIT(1)
+#define UART_TX_FIFO_CLR BIT(2)
+#define UART_RX_FIFO_CLR BIT(3)
+#define UART_TX_FIFO_OVERFLOW BIT(4)
+#define UART_TX_FIFO_UNDERFLOW BIT(5)
+#define UART_RX_FIFO_OVERFLOW BIT(6)
+#define UART_RX_FIFO_UNDERFLOW BIT(7)
+#define UART_FIFO_CONFIG_1 0x84
+#define UART_TX_FIFO_CNT_MSK GENMASK(5, 0)
+#define UART_RX_FIFO_CNT_MSK GENMASK(13, 8)
+#define UART_TX_FIFO_TH_MSK GENMASK(20, 16)
+#define UART_RX_FIFO_TH_MSK GENMASK(28, 24)
+#define UART_FIFO_WDATA 0x88
+#define UART_FIFO_RDATA 0x8c
+#define UART_FIFO_RDATA_MSK GENMASK(7, 0)
+
+#define BFLB_UART_MAXPORTS 8
+#define BFLB_UART_BAUD 2000000
+#define BFLB_UART_RX_FIFO_TH 7
+#define BFLB_UART_TX_FIFO_TH 15
+#define BFLB_UART_URX_RTO_TIME 0x4f
+
+struct bflb_uart_port {
+ struct uart_port port;
+ struct clk *clk;
+};
+
+static struct bflb_uart_port *bflb_uart_ports[BFLB_UART_MAXPORTS];
+
+static inline u32 rdl(struct uart_port *port, u32 reg)
+{
+ return readl_relaxed(port->membase + reg);
+}
+
+static inline void wrl(struct uart_port *port, u32 reg, u32 value)
+{
+ writel_relaxed(value, port->membase + reg);
+}
+
+static inline void wrb(struct uart_port *port, u32 reg, u8 value)
+{
+ writeb_relaxed(value, port->membase + reg);
+}
+
+static unsigned int bflb_uart_tx_empty(struct uart_port *port)
+{
+ return (rdl(port, UART_FIFO_CONFIG_1) & UART_TX_FIFO_CNT_MSK) ? TIOCSER_TEMT : 0;
+}
+
+static unsigned int bflb_uart_get_mctrl(struct uart_port *port)
+{
+ return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
+}
+
+static void bflb_uart_set_mctrl(struct uart_port *port, unsigned int sigs)
+{
+}
+
+static void bflb_uart_start_tx(struct uart_port *port)
+{
+ u32 val;
+
+ val = rdl(port, UART_UTX_CONFIG);
+ val |= UART_CR_UTX_EN;
+ wrl(port, UART_UTX_CONFIG, val);
+
+ val = rdl(port, UART_FIFO_CONFIG_1);
+ val &= ~UART_TX_FIFO_TH_MSK;
+ val |= FIELD_PREP(UART_TX_FIFO_TH_MSK, BFLB_UART_TX_FIFO_TH);
+ wrl(port, UART_FIFO_CONFIG_1, val);
+
+ val = rdl(port, UART_INT_MASK);
+ val &= ~UART_UTX_FIFO_INT;
+ wrl(port, UART_INT_MASK, val);
+}
+
+static void bflb_uart_stop_tx(struct uart_port *port)
+{
+ u32 val;
+
+ val = rdl(port, UART_INT_MASK);
+ val |= UART_UTX_FIFO_INT;
+ wrl(port, UART_INT_MASK, val);
+}
+
+static void bflb_uart_stop_rx(struct uart_port *port)
+{
+ u32 val;
+
+ val = rdl(port, UART_URX_CONFIG);
+ val &= ~UART_CR_URX_EN;
+ wrl(port, UART_URX_CONFIG, val);
+
+ val = rdl(port, UART_INT_MASK);
+ val |= UART_URX_FIFO_INT | UART_URX_RTO_INT | UART_URX_FER_INT;
+ wrl(port, UART_INT_MASK, val);
+}
+
+static void bflb_uart_set_termios(struct uart_port *port,
+ struct ktermios *termios,
+ const struct ktermios *old)
+{
+ unsigned long flags;
+ u32 valt, valr, val;
+ unsigned int baud, min;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ /* set data length */
+ val = tty_get_char_size(termios->c_cflag) - 1;
+ valt = FIELD_PREP(UART_CR_UTX_BIT_CNT_D_MSK, val);
+
+ /* calculate parity */
+ termios->c_cflag &= ~CMSPAR; /* no support mark/space */
+ if (termios->c_cflag & PARENB) {
+ valt |= UART_CR_UTX_PRT_EN;
+ if (termios->c_cflag & PARODD)
+ valt |= UART_CR_UTX_PRT_SEL;
+ }
+
+ valr = valt;
+
+ /* calculate stop bits */
+ if (termios->c_cflag & CSTOPB)
+ val = 2;
+ else
+ val = 1;
+ valt |= FIELD_PREP(UART_CR_UTX_BIT_CNT_P_MSK, val);
+
+ /* flow control */
+ if (termios->c_cflag & CRTSCTS)
+ valt |= UART_CR_UTX_CTS_EN;
+
+ /* enable TX freerunning mode */
+ valt |= UART_CR_UTX_FRM_EN;
+
+ valt |= UART_CR_UTX_EN;
+ valr |= UART_CR_URX_EN;
+
+ wrl(port, UART_UTX_CONFIG, valt);
+ wrl(port, UART_URX_CONFIG, valr);
+
+ min = port->uartclk / (UART_CR_UTX_BIT_PRD_MSK + 1);
+ baud = uart_get_baud_rate(port, termios, old, min, 4000000);
+
+ val = DIV_ROUND_CLOSEST(port->uartclk, baud) - 1;
+ val = FIELD_PREP(UART_CR_UTX_BIT_PRD_MSK, val);
+ val |= FIELD_PREP(UART_CR_URX_BIT_PRD_MSK, val);
+ wrl(port, UART_BIT_PRD, val);
+
+ uart_update_timeout(port, termios->c_cflag, baud);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void bflb_uart_rx_chars(struct uart_port *port)
+{
+ u8 ch;
+
+ while (rdl(port, UART_FIFO_CONFIG_1) & UART_RX_FIFO_CNT_MSK) {
+ ch = FIELD_GET(UART_FIFO_RDATA_MSK, rdl(port, UART_FIFO_RDATA));
+ port->icount.rx++;
+
+ if (uart_handle_sysrq_char(port, ch))
+ continue;
+ uart_insert_char(port, 0, 0, ch, TTY_NORMAL);
+ }
+
+ spin_unlock(&port->lock);
+ tty_flip_buffer_push(&port->state->port);
+ spin_lock(&port->lock);
+}
+
+static void bflb_uart_tx_chars(struct uart_port *port)
+{
+ struct circ_buf *xmit = &port->state->xmit;
+ unsigned int count;
+
+ if (port->x_char) {
+ /* Send special char - probably flow control */
+ wrl(port, UART_FIFO_WDATA, port->x_char);
+ port->x_char = 0;
+ port->icount.tx++;
+ return;
+ }
+
+ if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+ bflb_uart_stop_tx(port);
+ return;
+ }
+
+ count = BFLB_UART_TX_FIFO_TH;
+ do {
+ wrl(port, UART_FIFO_WDATA, xmit->buf[xmit->tail]);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ port->icount.tx++;
+ if (uart_circ_empty(xmit))
+ break;
+ } while (--count > 0);
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+
+ if (uart_circ_empty(xmit))
+ bflb_uart_stop_tx(port);
+}
+
+static irqreturn_t bflb_uart_interrupt(int irq, void *data)
+{
+ struct uart_port *port = data;
+ u32 isr, val;
+
+ isr = rdl(port, UART_INT_STS);
+ wrl(port, UART_INT_CLEAR, isr);
+
+ spin_lock(&port->lock);
+
+ if (isr & UART_URX_FER_INT) {
+ /* RX FIFO error interrupt */
+ val = rdl(port, UART_FIFO_CONFIG_0);
+ if (val & UART_RX_FIFO_OVERFLOW)
+ port->icount.overrun++;
+
+ val |= UART_RX_FIFO_CLR;
+ wrl(port, UART_FIFO_CONFIG_0, val);
+ }
+
+ if (isr & (UART_URX_FIFO_INT | UART_URX_RTO_INT))
+ bflb_uart_rx_chars(port);
+
+ if (isr & UART_UTX_FIFO_INT)
+ bflb_uart_tx_chars(port);
+
+ spin_unlock(&port->lock);
+
+ return IRQ_RETVAL(isr);
+}
+
+static void bflb_uart_config_port(struct uart_port *port, int flags)
+{
+ port->type = PORT_BFLB;
+}
+
+static int bflb_uart_startup(struct uart_port *port)
+{
+ unsigned long flags;
+ int ret;
+ u32 val;
+
+ ret = devm_request_irq(port->dev, port->irq, bflb_uart_interrupt,
+ IRQF_SHARED, port->name, port);
+ if (ret) {
+ dev_err(port->dev, "fail to request serial irq %d, ret=%d\n",
+ port->irq, ret);
+ return ret;
+ }
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ wrl(port, UART_INT_MASK, ~0);
+
+ wrl(port, UART_DATA_CONFIG, 0);
+ wrl(port, UART_SW_MODE, 0);
+ wrl(port, UART_URX_RTO_TIMER, FIELD_PREP(UART_CR_URX_RTO_VALUE_MSK, BFLB_UART_URX_RTO_TIME));
+
+ val = rdl(port, UART_FIFO_CONFIG_1);
+ val &= ~UART_RX_FIFO_TH_MSK;
+ val |= FIELD_PREP(UART_RX_FIFO_TH_MSK, BFLB_UART_RX_FIFO_TH);
+ wrl(port, UART_FIFO_CONFIG_1, val);
+
+ /* Unmask RX interrupts now */
+ val = rdl(port, UART_INT_MASK);
+ val &= ~(UART_URX_FIFO_INT | UART_URX_RTO_INT | UART_URX_FER_INT);
+ wrl(port, UART_INT_MASK, val);
+
+ val = rdl(port, UART_UTX_CONFIG);
+ val |= UART_CR_UTX_EN;
+ wrl(port, UART_UTX_CONFIG, val);
+ val = rdl(port, UART_URX_CONFIG);
+ val |= UART_CR_URX_EN;
+ wrl(port, UART_URX_CONFIG, val);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ return 0;
+}
+
+static void bflb_uart_shutdown(struct uart_port *port)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+ /* mask all interrupts now */
+ wrl(port, UART_INT_MASK, ~0);
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *bflb_uart_type(struct uart_port *port)
+{
+ return (port->type == PORT_BFLB) ? "BFLB UART" : NULL;
+}
+
+static int bflb_uart_verify_port(struct uart_port *port,
+ struct serial_struct *ser)
+{
+ if (ser->type != PORT_UNKNOWN && ser->type != PORT_BFLB)
+ return -EINVAL;
+ return 0;
+}
+
+static const struct uart_ops bflb_uart_ops = {
+ .tx_empty = bflb_uart_tx_empty,
+ .get_mctrl = bflb_uart_get_mctrl,
+ .set_mctrl = bflb_uart_set_mctrl,
+ .start_tx = bflb_uart_start_tx,
+ .stop_tx = bflb_uart_stop_tx,
+ .stop_rx = bflb_uart_stop_rx,
+ .startup = bflb_uart_startup,
+ .shutdown = bflb_uart_shutdown,
+ .set_termios = bflb_uart_set_termios,
+ .type = bflb_uart_type,
+ .config_port = bflb_uart_config_port,
+ .verify_port = bflb_uart_verify_port,
+};
+
+#ifdef CONFIG_SERIAL_BFLB_CONSOLE
+static void bflb_console_putchar(struct uart_port *port, unsigned char ch)
+{
+ while (!(rdl(port, UART_FIFO_CONFIG_1) & UART_TX_FIFO_CNT_MSK))
+ cpu_relax();
+ wrb(port, UART_FIFO_WDATA, ch);
+}
+
+/*
+ * Interrupts are disabled on entering
+ */
+static void bflb_uart_console_write(struct console *co, const char *s,
+ u_int count)
+{
+ struct uart_port *port = &bflb_uart_ports[co->index]->port;
+ u32 status, reg, mask;
+
+ /* save then disable interrupts */
+ mask = rdl(port, UART_INT_MASK);
+ reg = ~0;
+ wrl(port, UART_INT_MASK, reg);
+
+ /* Make sure that tx is enabled */
+ reg = rdl(port, UART_UTX_CONFIG);
+ reg |= UART_CR_UTX_EN;
+ wrl(port, UART_UTX_CONFIG, reg);
+
+ uart_console_write(port, s, count, bflb_console_putchar);
+
+ /* wait for TX done */
+ do {
+ status = rdl(port, UART_STATUS);
+ } while ((status & UART_STS_UTX_BUS_BUSY));
+
+ /* restore IRQ mask */
+ wrl(port, UART_INT_MASK, mask);
+}
+
+static int bflb_uart_console_setup(struct console *co, char *options)
+{
+ struct uart_port *port;
+ struct bflb_uart_port *bp;
+ int baud = BFLB_UART_BAUD;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+ u32 val;
+
+ if (co->index >= BFLB_UART_MAXPORTS || co->index < 0)
+ return -EINVAL;
+
+ bp = bflb_uart_ports[co->index];
+ if (!bp)
+ /* Port not initialized yet - delay setup */
+ return -ENODEV;
+
+ port = &bp->port;
+
+ val = rdl(port, UART_UTX_CONFIG);
+ val |= UART_CR_UTX_EN;
+ wrl(port, UART_UTX_CONFIG, val);
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+ return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver bflb_uart_driver;
+static struct console bflb_uart_console = {
+ .name = "ttyS",
+ .write = bflb_uart_console_write,
+ .device = uart_console_device,
+ .setup = bflb_uart_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &bflb_uart_driver,
+};
+
+static int __init bflb_uart_console_init(void)
+{
+ register_console(&bflb_uart_console);
+ return 0;
+}
+console_initcall(bflb_uart_console_init);
+
+#define BFLB_UART_CONSOLE (&bflb_uart_console)
+
+static void bflb_uart_earlycon_write(struct console *co, const char *s,
+ unsigned int count)
+{
+ struct earlycon_device *dev = co->data;
+
+ uart_console_write(&dev->port, s, count, bflb_console_putchar);
+}
+
+static int __init bflb_uart_earlycon_setup(struct earlycon_device *dev,
+ const char *options)
+{
+ if (!dev->port.membase)
+ return -ENODEV;
+
+ dev->con->write = bflb_uart_earlycon_write;
+
+ return 0;
+}
+OF_EARLYCON_DECLARE(bflb_uart, "bouffalolab,uart", bflb_uart_earlycon_setup);
+
+#else
+
+#define BFLB_UART_CONSOLE NULL
+
+#endif /* CONFIG_SERIAL_BFLB_CONSOLE */
+
+static struct uart_driver bflb_uart_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = "bflb_uart",
+ .dev_name = "ttyS",
+ .nr = BFLB_UART_MAXPORTS,
+ .cons = BFLB_UART_CONSOLE,
+};
+
+static int bflb_uart_probe(struct platform_device *pdev)
+{
+ struct uart_port *port;
+ struct bflb_uart_port *bp;
+ struct resource *res;
+ int index, irq;
+
+ index = of_alias_get_id(pdev->dev.of_node, "serial");
+ if (unlikely(index < 0 || index >= BFLB_UART_MAXPORTS)) {
+ dev_err(&pdev->dev, "got a wrong serial alias id %d\n", index);
+ return -EINVAL;
+ }
+
+ bp = devm_kzalloc(&pdev->dev, sizeof(*bp), GFP_KERNEL);
+ if (!bp)
+ return -ENOMEM;
+
+ bflb_uart_ports[index] = bp;
+ platform_set_drvdata(pdev, bp);
+ port = &bp->port;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ port->membase = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(port->membase))
+ return PTR_ERR(port->membase);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ port->mapbase = res->start;
+ port->irq = irq;
+ port->line = index;
+ port->type = PORT_BFLB;
+ port->iotype = UPIO_MEM;
+ port->fifosize = 32;
+ port->ops = &bflb_uart_ops;
+ port->flags = UPF_BOOT_AUTOCONF;
+ port->dev = &pdev->dev;
+ port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_BFLB_CONSOLE);
+
+ bp->clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ if (IS_ERR(bp->clk))
+ return PTR_ERR(bp->clk);
+ port->uartclk = clk_get_rate(bp->clk);
+
+ return uart_add_one_port(&bflb_uart_driver, port);
+}
+
+static int bflb_uart_remove(struct platform_device *pdev)
+{
+ struct bflb_uart_port *bp = platform_get_drvdata(pdev);
+
+ uart_remove_one_port(&bflb_uart_driver, &bp->port);
+ bflb_uart_ports[bp->port.line] = NULL;
+
+ return 0;
+}
+
+static const struct of_device_id bflb_uart_match[] = {
+ {
+ .compatible = "bouffalolab,bl808-uart",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, bflb_uart_match);
+
+static struct platform_driver bflb_uart_platform_driver = {
+ .probe = bflb_uart_probe,
+ .remove = bflb_uart_remove,
+ .driver = {
+ .name = "bflb_uart",
+ .of_match_table = of_match_ptr(bflb_uart_match),
+ },
+};
+
+static int __init bflb_uart_init(void)
+{
+ int ret;
+
+ ret = uart_register_driver(&bflb_uart_driver);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&bflb_uart_platform_driver);
+ if (ret)
+ uart_unregister_driver(&bflb_uart_driver);
+
+ return ret;
+}
+
+static void __exit bflb_uart_exit(void)
+{
+ platform_driver_unregister(&bflb_uart_platform_driver);
+ uart_unregister_driver(&bflb_uart_driver);
+}
+
+module_init(bflb_uart_init);
+module_exit(bflb_uart_exit);
+
+MODULE_DESCRIPTION("Bouffalolab UART driver");
+MODULE_AUTHOR("Jisheng Zhang <[email protected]>");
+MODULE_LICENSE("GPL");
diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
index 3ba34d8378bd..dabbb5ea2857 100644
--- a/include/uapi/linux/serial_core.h
+++ b/include/uapi/linux/serial_core.h
@@ -276,4 +276,7 @@
/* Sunplus UART */
#define PORT_SUNPLUS 123

+/* Bouffalolab UART */
+#define PORT_BFLB 124
+
#endif /* _UAPILINUX_SERIAL_CORE_H */
--
2.38.1


2022-11-28 06:42:33

by Jiri Slaby

[permalink] [raw]
Subject: Re: [PATCH v2 2/9] serial: bflb_uart: add Bouffalolab UART Driver

On 27. 11. 22, 14:24, Jisheng Zhang wrote:
> +static void bflb_uart_tx_chars(struct uart_port *port)

Again:

Are you unable to use the TX helper? If so:
* why?
* use uart_advance_xmit() at least.

> +{
> + struct circ_buf *xmit = &port->state->xmit;
> + unsigned int count;
> +
> + if (port->x_char) {
> + /* Send special char - probably flow control */
> + wrl(port, UART_FIFO_WDATA, port->x_char);
> + port->x_char = 0;
> + port->icount.tx++;
> + return;
> + }
> +
> + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
> + bflb_uart_stop_tx(port);
> + return;
> + }
> +
> + count = BFLB_UART_TX_FIFO_TH;
> + do {
> + wrl(port, UART_FIFO_WDATA, xmit->buf[xmit->tail]);
> + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
> + port->icount.tx++;
> + if (uart_circ_empty(xmit))
> + break;
> + } while (--count > 0);
> +
> + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + uart_write_wakeup(port);
> +
> + if (uart_circ_empty(xmit))
> + bflb_uart_stop_tx(port);
> +}

--
js
suse labs

2022-11-28 14:57:19

by Jisheng Zhang

[permalink] [raw]
Subject: Re: [PATCH v2 2/9] serial: bflb_uart: add Bouffalolab UART Driver

On Mon, Nov 28, 2022 at 07:10:41AM +0100, Jiri Slaby wrote:
> On 27. 11. 22, 14:24, Jisheng Zhang wrote:
> > +static void bflb_uart_tx_chars(struct uart_port *port)
>
> Again:
>
> Are you unable to use the TX helper? If so:

You know serial subsystem better than me, may I ask for more
details? For example,
Besides uart_xmit_advance(), do you expect other TX helpers? If yes,
can you please list them?

> * why?
> * use uart_advance_xmit() at least.

Do you mean uart_xmit_advance()? in the do while loop below?

I'm not sure I understand the meaning, correct me If I misunderstand
something.

thanks
>
> > +{
> > + struct circ_buf *xmit = &port->state->xmit;
> > + unsigned int count;
> > +
> > + if (port->x_char) {
> > + /* Send special char - probably flow control */
> > + wrl(port, UART_FIFO_WDATA, port->x_char);
> > + port->x_char = 0;
> > + port->icount.tx++;
> > + return;
> > + }
> > +
> > + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
> > + bflb_uart_stop_tx(port);
> > + return;
> > + }
> > +
> > + count = BFLB_UART_TX_FIFO_TH;
> > + do {
> > + wrl(port, UART_FIFO_WDATA, xmit->buf[xmit->tail]);
> > + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
> > + port->icount.tx++;
> > + if (uart_circ_empty(xmit))
> > + break;
> > + } while (--count > 0);
> > +
> > + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> > + uart_write_wakeup(port);
> > +
> > + if (uart_circ_empty(xmit))
> > + bflb_uart_stop_tx(port);
> > +}
>
> --
> js
> suse labs
>

2022-11-28 16:17:12

by Ilpo Järvinen

[permalink] [raw]
Subject: Re: [PATCH v2 2/9] serial: bflb_uart: add Bouffalolab UART Driver

On Mon, 28 Nov 2022, Jisheng Zhang wrote:

> On Mon, Nov 28, 2022 at 07:10:41AM +0100, Jiri Slaby wrote:
> > On 27. 11. 22, 14:24, Jisheng Zhang wrote:
> > > +static void bflb_uart_tx_chars(struct uart_port *port)
> >
> > Again:
> >
> > Are you unable to use the TX helper? If so:
>
> You know serial subsystem better than me, may I ask for more
> details? For example,
> Besides uart_xmit_advance(), do you expect other TX helpers? If yes,
> can you please list them?

Please take on look on commit 8275b48b278096edc1e3ea5aa9cf946a10022f79.
The changes following that commit convert some drivers to use the tx
helper so you can look into them to see examples.

> > * why?
> > * use uart_advance_xmit() at least.
>
> Do you mean uart_xmit_advance()? in the do while loop below?

Yes, Jiri had the name wrong. But your code looked like it could use
the tx helper instead.

--
i.

2022-11-29 00:07:00

by Jisheng Zhang

[permalink] [raw]
Subject: Re: [PATCH v2 2/9] serial: bflb_uart: add Bouffalolab UART Driver

On Mon, Nov 28, 2022 at 06:01:28PM +0200, Ilpo Järvinen wrote:
> On Mon, 28 Nov 2022, Jisheng Zhang wrote:
>
> > On Mon, Nov 28, 2022 at 07:10:41AM +0100, Jiri Slaby wrote:
> > > On 27. 11. 22, 14:24, Jisheng Zhang wrote:
> > > > +static void bflb_uart_tx_chars(struct uart_port *port)
> > >
> > > Again:
> > >
> > > Are you unable to use the TX helper? If so:
> >
> > You know serial subsystem better than me, may I ask for more
> > details? For example,
> > Besides uart_xmit_advance(), do you expect other TX helpers? If yes,
> > can you please list them?
>
> Please take on look on commit 8275b48b278096edc1e3ea5aa9cf946a10022f79.
> The changes following that commit convert some drivers to use the tx
> helper so you can look into them to see examples.

Thanks a lot for the hint. Will those tx helpers(uart_port_tx,
uart_port_tx_limited etc.) be in v6.2-rc1? Or I need to patch
based on Jiri's devel branch? Aha, Jiri says "at least uart_xmit_advance

>
> > > * why?
> > > * use uart_advance_xmit() at least.
> >
> > Do you mean uart_xmit_advance()? in the do while loop below?
>
> Yes, Jiri had the name wrong. But your code looked like it could use
> the tx helper instead.

2022-11-29 06:46:33

by Jiri Slaby

[permalink] [raw]
Subject: Re: [PATCH v2 2/9] serial: bflb_uart: add Bouffalolab UART Driver

On 29. 11. 22, 0:20, Jisheng Zhang wrote:
> On Mon, Nov 28, 2022 at 06:01:28PM +0200, Ilpo Järvinen wrote:
>> On Mon, 28 Nov 2022, Jisheng Zhang wrote:
>>
>>> On Mon, Nov 28, 2022 at 07:10:41AM +0100, Jiri Slaby wrote:
>>>> On 27. 11. 22, 14:24, Jisheng Zhang wrote:
>>>>> +static void bflb_uart_tx_chars(struct uart_port *port)
>>>>
>>>> Again:
>>>>
>>>> Are you unable to use the TX helper? If so:
>>>
>>> You know serial subsystem better than me, may I ask for more
>>> details? For example,
>>> Besides uart_xmit_advance(), do you expect other TX helpers? If yes,
>>> can you please list them?
>>
>> Please take on look on commit 8275b48b278096edc1e3ea5aa9cf946a10022f79.
>> The changes following that commit convert some drivers to use the tx
>> helper so you can look into them to see examples.
>
> Thanks a lot for the hint. Will those tx helpers(uart_port_tx,
> uart_port_tx_limited etc.) be in v6.2-rc1? Or I need to patch
> based on Jiri's devel branch?

It's in tty-next and so in -next. You should base on either of those
anyway ;). And yes, the plan is to have them in v6.2-rc1.

thanks,
--
js

2022-12-05 21:42:31

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v2 2/9] serial: bflb_uart: add Bouffalolab UART Driver

Hi Jisheng,

I love your patch! Yet something to improve:

[auto build test ERROR on tty/tty-testing]
[also build test ERROR on tty/tty-next tty/tty-linus krzk/for-next krzk-dt/for-next linus/master v6.1-rc8 next-20221205]
[cannot apply to robh/for-next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url: https://github.com/intel-lab-lkp/linux/commits/Jisheng-Zhang/riscv-add-Bouffalolab-bl808-support/20221127-213728
base: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git tty-testing
patch link: https://lore.kernel.org/r/20221127132448.4034-3-jszhang%40kernel.org
patch subject: [PATCH v2 2/9] serial: bflb_uart: add Bouffalolab UART Driver
config: openrisc-randconfig-c032-20221205
compiler: or1k-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/0bb4f3a3f1d685809b24d1b93aadd7b67e04eb5a
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Jisheng-Zhang/riscv-add-Bouffalolab-bl808-support/20221127-213728
git checkout 0bb4f3a3f1d685809b24d1b93aadd7b67e04eb5a
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=openrisc SHELL=/bin/bash drivers/tty/serial/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <[email protected]>

All errors (new ones prefixed by >>):

In file included from drivers/tty/serial/bflb_uart.c:15:
>> include/linux/module.h:130:49: error: redefinition of '__inittest'
130 | static inline initcall_t __maybe_unused __inittest(void) \
| ^~~~~~~~~~
drivers/tty/serial/bflb_uart.c:605:1: note: in expansion of macro 'module_init'
605 | module_init(bflb_uart_init);
| ^~~~~~~~~~~
include/linux/module.h:130:49: note: previous definition of '__inittest' with type 'int (*(void))(void)'
130 | static inline initcall_t __maybe_unused __inittest(void) \
| ^~~~~~~~~~
include/linux/module.h:126:41: note: in expansion of macro 'module_init'
126 | #define console_initcall(fn) module_init(fn)
| ^~~~~~~~~~~
drivers/tty/serial/bflb_uart.c:470:1: note: in expansion of macro 'console_initcall'
470 | console_initcall(bflb_uart_console_init);
| ^~~~~~~~~~~~~~~~
>> include/linux/module.h:132:13: error: redefinition of 'init_module'
132 | int init_module(void) __copy(initfn) \
| ^~~~~~~~~~~
drivers/tty/serial/bflb_uart.c:605:1: note: in expansion of macro 'module_init'
605 | module_init(bflb_uart_init);
| ^~~~~~~~~~~
include/linux/module.h:132:13: note: previous definition of 'init_module' with type 'int(void)'
132 | int init_module(void) __copy(initfn) \
| ^~~~~~~~~~~
include/linux/module.h:126:41: note: in expansion of macro 'module_init'
126 | #define console_initcall(fn) module_init(fn)
| ^~~~~~~~~~~
drivers/tty/serial/bflb_uart.c:470:1: note: in expansion of macro 'console_initcall'
470 | console_initcall(bflb_uart_console_init);
| ^~~~~~~~~~~~~~~~

Kconfig warnings: (for reference only)
WARNING: unmet direct dependencies detected for DEBUG_MAPLE_TREE
Depends on [n]: DEBUG_KERNEL [=n]
Selected by [m]:
- TEST_MAPLE_TREE [=m] && RUNTIME_TESTING_MENU [=y]


vim +/__inittest +130 include/linux/module.h

0fd972a7d91d6e Paul Gortmaker 2015-05-01 127
0fd972a7d91d6e Paul Gortmaker 2015-05-01 128 /* Each module must use one module_init(). */
0fd972a7d91d6e Paul Gortmaker 2015-05-01 129 #define module_init(initfn) \
1f318a8bafcfba Arnd Bergmann 2017-02-01 @130 static inline initcall_t __maybe_unused __inittest(void) \
0fd972a7d91d6e Paul Gortmaker 2015-05-01 131 { return initfn; } \
cf68fffb66d60d Sami Tolvanen 2021-04-08 @132 int init_module(void) __copy(initfn) \
cf68fffb66d60d Sami Tolvanen 2021-04-08 133 __attribute__((alias(#initfn))); \
92efda8eb15295 Sami Tolvanen 2022-09-08 134 ___ADDRESSABLE(init_module, __initdata);
0fd972a7d91d6e Paul Gortmaker 2015-05-01 135

--
0-DAY CI Kernel Test Service
https://01.org/lkp


Attachments:
(No filename) (4.81 kB)
config (151.07 kB)
Download all attachments