* [PATCH v7 0/2] Add UART driver for Suplus SP7021 SoC @ 2022-02-07 5:57 Hammer Hsieh 2022-02-07 5:58 ` [PATCH v7 1/2] dt-bindings:serial:Add bindings doc for Sunplus SoC UART Driver Hammer Hsieh 2022-02-07 5:58 ` [PATCH v7 2/2] serial:sunplus-uart:Add " Hammer Hsieh 0 siblings, 2 replies; 11+ messages in thread From: Hammer Hsieh @ 2022-02-07 5:57 UTC (permalink / raw) To: gregkh, robh+dt, linux-serial, devicetree, linux-kernel, jirislaby, p.zabel Cc: wells.lu, hammer.hsieh, Hammer Hsieh This is a patch series for UART driver for Suplus SP7021 SoC. Sunplus SP7021 is an ARM Cortex A7 (4 cores) based SoC. It integrates many peripherals (ex: UART. I2C, SPI, SDIO, eMMC, USB, SD card and etc.) into a single chip. It is designed for industrial control. Refer to: https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview https://tibbo.com/store/plus1.html Refer to (UART): https://sunplus.atlassian.net/wiki/spaces/doc/pages/1873412290/13.+Universal+Asynchronous+Receiver+Transmitter+UART Hammer Hsieh (2): dt-bindings:serial:Add bindings doc for Sunplus SoC UART Driver serial:sunplus-uart:Add Sunplus SoC UART Driver .../bindings/serial/sunplus,sp7021-uart.yaml | 56 ++ MAINTAINERS | 6 + drivers/tty/serial/Kconfig | 25 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/sunplus-uart.c | 762 +++++++++++++++++++++ include/uapi/linux/serial_core.h | 3 + 6 files changed, 853 insertions(+) create mode 100644 Documentation/devicetree/bindings/serial/sunplus,sp7021-uart.yaml create mode 100644 drivers/tty/serial/sunplus-uart.c -- 2.7.4 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v7 1/2] dt-bindings:serial:Add bindings doc for Sunplus SoC UART Driver 2022-02-07 5:57 [PATCH v7 0/2] Add UART driver for Suplus SP7021 SoC Hammer Hsieh @ 2022-02-07 5:58 ` Hammer Hsieh 2022-02-07 5:58 ` [PATCH v7 2/2] serial:sunplus-uart:Add " Hammer Hsieh 1 sibling, 0 replies; 11+ messages in thread From: Hammer Hsieh @ 2022-02-07 5:58 UTC (permalink / raw) To: gregkh, robh+dt, linux-serial, devicetree, linux-kernel, jirislaby, p.zabel Cc: wells.lu, hammer.hsieh, Hammer Hsieh Add bindings doc for Sunplus SoC UART Driver Reviewed-by: Rob Herring <robh@kernel.org> Signed-off-by: Hammer Hsieh <hammerh0314@gmail.com> --- Changes in v7: - no change. .../bindings/serial/sunplus,sp7021-uart.yaml | 56 ++++++++++++++++++++++ MAINTAINERS | 5 ++ 2 files changed, 61 insertions(+) create mode 100644 Documentation/devicetree/bindings/serial/sunplus,sp7021-uart.yaml diff --git a/Documentation/devicetree/bindings/serial/sunplus,sp7021-uart.yaml b/Documentation/devicetree/bindings/serial/sunplus,sp7021-uart.yaml new file mode 100644 index 0000000..894324c --- /dev/null +++ b/Documentation/devicetree/bindings/serial/sunplus,sp7021-uart.yaml @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) Sunplus Co., Ltd. 2021 +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/serial/sunplus,sp7021-uart.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Sunplus SoC SP7021 UART Controller Device Tree Bindings + +maintainers: + - Hammer Hsieh <hammerh0314@gmail.com> + +allOf: + - $ref: serial.yaml# + +properties: + compatible: + const: sunplus,sp7021-uart + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + resets: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - clocks + - resets + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + aliases { + serial0 = &uart0; + }; + + uart0: serial@9c000900 { + compatible = "sunplus,sp7021-uart"; + reg = <0x9c000900 0x80>; + interrupt-parent = <&intc>; + interrupts = <53 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clkc 0x28>; + resets = <&rstc 0x18>; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index 3b79fd4..3c1362e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17945,6 +17945,11 @@ L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/dlink/sundance.c +SUNPLUS UART DRIVER +M: Hammer Hsieh <hammerh0314@gmail.com> +S: Maintained +F: Documentation/devicetree/bindings/serial/sunplus,sp7021-uart.yaml + SUPERH M: Yoshinori Sato <ysato@users.sourceforge.jp> M: Rich Felker <dalias@libc.org> -- 2.7.4 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v7 2/2] serial:sunplus-uart:Add Sunplus SoC UART Driver 2022-02-07 5:57 [PATCH v7 0/2] Add UART driver for Suplus SP7021 SoC Hammer Hsieh 2022-02-07 5:58 ` [PATCH v7 1/2] dt-bindings:serial:Add bindings doc for Sunplus SoC UART Driver Hammer Hsieh @ 2022-02-07 5:58 ` Hammer Hsieh 2022-02-07 7:18 ` Greg KH ` (3 more replies) 1 sibling, 4 replies; 11+ messages in thread From: Hammer Hsieh @ 2022-02-07 5:58 UTC (permalink / raw) To: gregkh, robh+dt, linux-serial, devicetree, linux-kernel, jirislaby, p.zabel Cc: wells.lu, hammer.hsieh, Hammer Hsieh Add Sunplus SoC UART Driver Signed-off-by: Hammer Hsieh <hammerh0314@gmail.com> --- Changes in v7: - Addressed all comments from Jiri Slaby and Greg KH. MAINTAINERS | 1 + drivers/tty/serial/Kconfig | 25 ++ drivers/tty/serial/Makefile | 1 + drivers/tty/serial/sunplus-uart.c | 762 ++++++++++++++++++++++++++++++++++++++ include/uapi/linux/serial_core.h | 3 + 5 files changed, 792 insertions(+) create mode 100644 drivers/tty/serial/sunplus-uart.c diff --git a/MAINTAINERS b/MAINTAINERS index 3c1362e..2dc2fe6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17949,6 +17949,7 @@ SUNPLUS UART DRIVER M: Hammer Hsieh <hammerh0314@gmail.com> S: Maintained F: Documentation/devicetree/bindings/serial/sunplus,sp7021-uart.yaml +F: drivers/tty/serial/sunplus-uart.c SUPERH M: Yoshinori Sato <ysato@users.sourceforge.jp> diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 131a6a5..0865da3 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1561,6 +1561,31 @@ config SERIAL_LITEUART_CONSOLE and warnings and which allows logins in single user mode). Otherwise, say 'N'. +config SERIAL_SUNPLUS + tristate "Sunplus UART support" + depends on OF || COMPILE_TEST + select SERIAL_CORE + help + Select this option if you would like to use Sunplus serial port on + Sunplus SoC SP7021. + If you enable this option, Sunplus serial ports in the system will + be registered as ttySUPx. + This driver can also be built as a module. If so, the module will be + called sunplus-uart. + +config SERIAL_SUNPLUS_CONSOLE + bool "Console on Sunplus UART" + depends on SERIAL_SUNPLUS + select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON + help + Select this option if you would like to use a Sunplus UART as the + system console. + Even if you say Y here, the currently visible virtual console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttySUPx". + endmenu config SERIAL_MCTRL_GPIO diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 7da0856..61cc8de 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -87,6 +87,7 @@ obj-$(CONFIG_SERIAL_RDA) += rda-uart.o obj-$(CONFIG_SERIAL_MILBEAUT_USIO) += milbeaut_usio.o obj-$(CONFIG_SERIAL_SIFIVE) += sifive.o obj-$(CONFIG_SERIAL_LITEUART) += liteuart.o +obj-$(CONFIG_SERIAL_SUNPLUS) += sunplus-uart.o # GPIOLIB helpers for modem control lines obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o diff --git a/drivers/tty/serial/sunplus-uart.c b/drivers/tty/serial/sunplus-uart.c new file mode 100644 index 0000000..753a18a --- /dev/null +++ b/drivers/tty/serial/sunplus-uart.c @@ -0,0 +1,762 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Sunplus SoC UART driver + * + * Author: Hammer Hsieh <hammerh0314@gmail.com> + */ +#include <linux/clk.h> +#include <linux/console.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/reset.h> +#include <linux/serial_core.h> +#include <linux/serial_reg.h> +#include <linux/sysrq.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <asm/irq.h> + +/* Register offsets */ +#define SUP_UART_DATA 0x00 +#define SUP_UART_LSR 0x04 +#define SUP_UART_MSR 0x08 +#define SUP_UART_LCR 0x0C +#define SUP_UART_MCR 0x10 +#define SUP_UART_DIV_L 0x14 +#define SUP_UART_DIV_H 0x18 +#define SUP_UART_ISC 0x1C +#define SUP_UART_TX_RESIDUE 0x20 +#define SUP_UART_RX_RESIDUE 0x24 + +/* Line Status Register bits */ +#define SUP_UART_LSR_BC BIT(5) /* break condition status */ +#define SUP_UART_LSR_FE BIT(4) /* frame error status */ +#define SUP_UART_LSR_OE BIT(3) /* overrun error status */ +#define SUP_UART_LSR_PE BIT(2) /* parity error status */ +#define SUP_UART_LSR_RX BIT(1) /* 1: receive fifo not empty */ +#define SUP_UART_LSR_TX BIT(0) /* 1: transmit fifo is not full */ +#define SUP_UART_LSR_TX_NOT_FULL 1 +#define SUP_UART_LSR_BRK_ERROR_BITS GENMASK(5, 2) + +/* Line Control Register bits */ +#define SUP_UART_LCR_SBC BIT(5) /* select break condition */ + +/* Modem Control Register bits */ +#define SUP_UART_MCR_RI BIT(3) /* ring indicator */ +#define SUP_UART_MCR_DCD BIT(2) /* data carrier detect */ + +/* Interrupt Status/Control Register bits */ +#define SUP_UART_ISC_RXM BIT(5) /* RX interrupt enable */ +#define SUP_UART_ISC_TXM BIT(4) /* TX interrupt enable */ +#define SUP_UART_ISC_RX BIT(1) /* RX interrupt status */ +#define SUP_UART_ISC_TX BIT(0) /* TX interrupt status */ + +#define SUP_DUMMY_READ BIT(16) /* drop bytes received on a !CREAD port */ +#define SUP_UART_NR 5 + +struct sunplus_uart_port { + struct uart_port port; + struct clk *clk; + struct reset_control *rstc; +}; + +static void sp_uart_put_char(struct uart_port *port, unsigned int ch) +{ + writel(ch, port->membase + SUP_UART_DATA); +} + +static u32 sunplus_tx_buf_not_full(struct uart_port *port) +{ + unsigned int lsr = readl(port->membase + SUP_UART_LSR); + + return (lsr & SUP_UART_LSR_TX) ? SUP_UART_LSR_TX_NOT_FULL : 0; +} + +static unsigned int sunplus_tx_empty(struct uart_port *port) +{ + unsigned int lsr = readl(port->membase + SUP_UART_LSR); + + return (lsr & UART_LSR_TEMT) ? TIOCSER_TEMT : 0; +} + +static void sunplus_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + unsigned int mcr = readl(port->membase + SUP_UART_MCR); + + if (mctrl & TIOCM_DTR) + mcr |= UART_MCR_DTR; + else + mcr &= ~UART_MCR_DTR; + + if (mctrl & TIOCM_RTS) + mcr |= UART_MCR_RTS; + else + mcr &= ~UART_MCR_RTS; + + if (mctrl & TIOCM_CAR) + mcr |= SUP_UART_MCR_DCD; + else + mcr &= ~SUP_UART_MCR_DCD; + + if (mctrl & TIOCM_RI) + mcr |= SUP_UART_MCR_RI; + else + mcr &= ~SUP_UART_MCR_RI; + + if (mctrl & TIOCM_LOOP) + mcr |= UART_MCR_LOOP; + else + mcr &= ~UART_MCR_LOOP; + + writel(mcr, port->membase + SUP_UART_MCR); +} + +static unsigned int sunplus_get_mctrl(struct uart_port *port) +{ + unsigned int ret, mcr; + + mcr = readl(port->membase + SUP_UART_MCR); + + if (mcr & UART_MCR_DTR) + ret |= TIOCM_DTR; + + if (mcr & UART_MCR_RTS) + ret |= TIOCM_RTS; + + if (mcr & SUP_UART_MCR_DCD) + ret |= TIOCM_CAR; + + if (mcr & SUP_UART_MCR_RI) + ret |= TIOCM_RI; + + if (mcr & UART_MCR_LOOP) + ret |= TIOCM_LOOP; + + return ret; +} + +static void sunplus_stop_tx(struct uart_port *port) +{ + unsigned int isc; + + isc = readl(port->membase + SUP_UART_ISC); + isc &= ~SUP_UART_ISC_TXM; + writel(isc, port->membase + SUP_UART_ISC); +} + +static void sunplus_start_tx(struct uart_port *port) +{ + unsigned int isc; + + isc = readl(port->membase + SUP_UART_ISC); + isc |= SUP_UART_ISC_TXM; + writel(isc, port->membase + SUP_UART_ISC); +} + +static void sunplus_stop_rx(struct uart_port *port) +{ + unsigned int isc; + + isc = readl(port->membase + SUP_UART_ISC); + isc &= ~SUP_UART_ISC_RXM; + writel(isc, port->membase + SUP_UART_ISC); +} + +static void sunplus_break_ctl(struct uart_port *port, int ctl) +{ + unsigned long flags; + unsigned int lcr; + + spin_lock_irqsave(&port->lock, flags); + + lcr = readl(port->membase + SUP_UART_LCR); + + if (ctl) + lcr |= SUP_UART_LCR_SBC; /* start break */ + else + lcr &= ~SUP_UART_LCR_SBC; /* stop break */ + + writel(lcr, port->membase + SUP_UART_LCR); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static void transmit_chars(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + + if (port->x_char) { + sp_uart_put_char(port, port->x_char); + port->icount.tx++; + port->x_char = 0; + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + sunplus_stop_tx(port); + return; + } + + do { + sp_uart_put_char(port, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) % UART_XMIT_SIZE; + port->icount.tx++; + + if (uart_circ_empty(xmit)) + break; + } while (sunplus_tx_buf_not_full(port)); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + sunplus_stop_tx(port); +} + +static void receive_chars(struct uart_port *port) +{ + unsigned int lsr = readl(port->membase + SUP_UART_LSR); + unsigned int ch, flag; + + do { + ch = readl(port->membase + SUP_UART_DATA); + flag = TTY_NORMAL; + port->icount.rx++; + + if (unlikely(lsr & SUP_UART_LSR_BRK_ERROR_BITS)) { + if (lsr & SUP_UART_LSR_BC) { + lsr &= ~(SUP_UART_LSR_FE | SUP_UART_LSR_PE); + port->icount.brk++; + flag = TTY_BREAK; + if (uart_handle_break(port)) + goto ignore_char; + } else if (lsr & SUP_UART_LSR_PE) { + port->icount.parity++; + flag = TTY_PARITY; + } else if (lsr & SUP_UART_LSR_FE) { + port->icount.frame++; + flag = TTY_FRAME; + } + + if (lsr & SUP_UART_LSR_OE) + port->icount.overrun++; + } + + if (port->ignore_status_mask & SUP_DUMMY_READ) + goto ignore_char; + + if (uart_handle_sysrq_char(port, ch)) + goto ignore_char; + + uart_insert_char(port, lsr, SUP_UART_LSR_OE, ch, flag); + +ignore_char: + lsr = readl(port->membase + SUP_UART_LSR); + } while (lsr & SUP_UART_LSR_RX); + + tty_flip_buffer_push(&port->state->port); +} + +static irqreturn_t sunplus_uart_irq(int irq, void *args) +{ + struct uart_port *port = args; + unsigned int isc; + + spin_lock(&port->lock); + + isc = readl(port->membase + SUP_UART_ISC); + + if (isc & SUP_UART_ISC_RX) + receive_chars(port); + + if (isc & SUP_UART_ISC_TX) + transmit_chars(port); + + spin_unlock(&port->lock); + + return IRQ_HANDLED; +} + +static int sunplus_startup(struct uart_port *port) +{ + unsigned long flags; + unsigned int isc; + int ret; + + ret = request_irq(port->irq, sunplus_uart_irq, 0, "sunplus_uart", port); + if (ret) + return ret; + + spin_lock_irqsave(&port->lock, flags); + + isc = readl(port->membase + SUP_UART_ISC); + isc |= SUP_UART_ISC_RXM; + writel(isc, port->membase + SUP_UART_ISC); + + spin_unlock_irqrestore(&port->lock, flags); + + return 0; +} + +static void sunplus_shutdown(struct uart_port *port) +{ + unsigned long flags; + unsigned int isc; + + spin_lock_irqsave(&port->lock, flags); + + isc = readl(port->membase + SUP_UART_ISC); + isc &= ~(SUP_UART_ISC_RXM | SUP_UART_ISC_TXM); + writel(isc, port->membase + SUP_UART_ISC); + + spin_unlock_irqrestore(&port->lock, flags); + + free_irq(port->irq, port); +} + +static void sunplus_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *oldtermios) +{ + u32 ext, div, div_l, div_h, baud, lcr; + u32 clk = port->uartclk; + unsigned long flags; + + baud = uart_get_baud_rate(port, termios, oldtermios, 0, port->uartclk / 16); + + /* baud rate = uartclk / ((16 * divisor + 1) + divisor_ext) */ + clk += baud >> 1; + div = clk / baud; + ext = div & 0x0F; + div = (div >> 4) - 1; + div_l = (div & 0xFF) | (ext << 12); + div_h = div >> 8; + + switch (termios->c_cflag & CSIZE) { + case CS5: + lcr = UART_LCR_WLEN5; + break; + case CS6: + lcr = UART_LCR_WLEN6; + break; + case CS7: + lcr = UART_LCR_WLEN7; + break; + default: + lcr = UART_LCR_WLEN8; + break; + } + + if (termios->c_cflag & CSTOPB) + lcr |= UART_LCR_STOP; + + if (termios->c_cflag & PARENB) { + lcr |= UART_LCR_PARITY; + + if (!(termios->c_cflag & PARODD)) + lcr |= UART_LCR_EPAR; + } + + spin_lock_irqsave(&port->lock, flags); + + uart_update_timeout(port, termios->c_cflag, baud); + + port->read_status_mask = 0; + if (termios->c_iflag & INPCK) + port->read_status_mask |= SUP_UART_LSR_PE | SUP_UART_LSR_FE; + + if (termios->c_iflag & (BRKINT | PARMRK)) + port->read_status_mask |= SUP_UART_LSR_BC; + + /* Characters to ignore */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= SUP_UART_LSR_FE | SUP_UART_LSR_PE; + + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |= SUP_UART_LSR_BC; + + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= SUP_UART_LSR_OE; + } + + /* Ignore all characters if CREAD is not set */ + if ((termios->c_cflag & CREAD) == 0) { + port->ignore_status_mask |= SUP_DUMMY_READ; + /* flush rx data FIFO */ + writel(0, port->membase + SUP_UART_RX_RESIDUE); + } + + /* Settings for baud rate divisor and lcr */ + writel(div_h, port->membase + SUP_UART_DIV_H); + writel(div_l, port->membase + SUP_UART_DIV_L); + writel(lcr, port->membase + SUP_UART_LCR); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static void sunplus_set_ldisc(struct uart_port *port, struct ktermios *termios) +{ + int new = termios->c_line; + + if (new == N_PPS) + port->flags |= UPF_HARDPPS_CD; + else + port->flags &= ~UPF_HARDPPS_CD; +} + +static const char *sunplus_type(struct uart_port *port) +{ + return port->type == PORT_SUNPLUS ? "sunplus_uart" : NULL; +} + +static void sunplus_config_port(struct uart_port *port, int type) +{ + if (type & UART_CONFIG_TYPE) + port->type = PORT_SUNPLUS; +} + +static int sunplus_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + if (ser->type != PORT_UNKNOWN && ser->type != PORT_SUNPLUS) + return -EINVAL; + + return 0; +} + +#ifdef CONFIG_CONSOLE_POLL +static void sunplus_poll_put_char(struct uart_port *port, unsigned char data) +{ + wait_for_xmitr(port); + sp_uart_put_char(port, data); +} + +static int sunplus_poll_get_char(struct uart_port *port) +{ + unsigned int lsr = readl(port->membase + SUP_UART_LSR); + + if (!(lsr & SUP_UART_LSR_RX)) + return NO_POLL_CHAR; + + return readl(port->membase + SUP_UART_DATA); +} +#endif + +static const struct uart_ops sunplus_uart_ops = { + .tx_empty = sunplus_tx_empty, + .set_mctrl = sunplus_set_mctrl, + .get_mctrl = sunplus_get_mctrl, + .stop_tx = sunplus_stop_tx, + .start_tx = sunplus_start_tx, + .stop_rx = sunplus_stop_rx, + .break_ctl = sunplus_break_ctl, + .startup = sunplus_startup, + .shutdown = sunplus_shutdown, + .set_termios = sunplus_set_termios, + .set_ldisc = sunplus_set_ldisc, + .type = sunplus_type, + .config_port = sunplus_config_port, + .verify_port = sunplus_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_put_char = sunplus_poll_put_char, + .poll_get_char = sunplus_poll_get_char, +#endif +}; + +#ifdef CONFIG_SERIAL_SUNPLUS_CONSOLE +struct sunplus_uart_port *sunplus_console_ports[SUP_UART_NR]; + +static void wait_for_xmitr(struct uart_port *port) +{ + unsigned int val; + int ret; + + /* Wait while FIFO is full or timeout */ + ret = readl_poll_timeout_atomic(port->membase + SUP_UART_LSR, val, + (val & SUP_UART_LSR_TX), 1, 10000); + + if (ret == -ETIMEDOUT) { + dev_err(port->dev, "Timeout waiting while UART TX FULL\n"); + return; + } +} + +static void sunplus_uart_console_putchar(struct uart_port *port, int ch) +{ + wait_for_xmitr(port); + sp_uart_put_char(port, ch); +} + +static void sunplus_console_write(struct console *co, + const char *s, + unsigned int count) +{ + unsigned long flags; + int locked = 1; + + local_irq_save(flags); + + if (sunplus_console_ports[co->index]->port.sysrq) + locked = 0; + else if (oops_in_progress) + locked = spin_trylock(&sunplus_console_ports[co->index]->port.lock); + else + spin_lock(&sunplus_console_ports[co->index]->port.lock); + + uart_console_write(&sunplus_console_ports[co->index]->port, s, count, + sunplus_uart_console_putchar); + + if (locked) + spin_unlock(&sunplus_console_ports[co->index]->port.lock); + + local_irq_restore(flags); +} + +static int __init sunplus_console_setup(struct console *co, char *options) +{ + struct sunplus_uart_port *sup; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index < 0 || co->index >= SUP_UART_NR) + return -EINVAL; + + sup = sunplus_console_ports[co->index]; + if (!sup) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&sup->port, co, baud, parity, bits, flow); +} + +static struct uart_driver sunplus_uart_driver; +static struct console sunplus_uart_console = { + .name = "ttySUP", + .write = sunplus_console_write, + .device = uart_console_device, + .setup = sunplus_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &sunplus_uart_driver +}; + +static int __init sunplus_console_init(void) +{ + register_console(&sunplus_uart_console); + return 0; +} +console_initcall(sunplus_console_init); +#endif + +static struct uart_driver sunplus_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "sunplus_uart", + .dev_name = "ttySUP", + .major = TTY_MAJOR, + .minor = 64, + .nr = SUP_UART_NR, + .cons = NULL, +}; + +static int sunplus_uart_probe(struct platform_device *pdev) +{ + struct sunplus_uart_port *sup; + struct uart_port *port; + struct resource *res; + int ret, irq; + + pdev->id = of_alias_get_id(pdev->dev.of_node, "serial"); + + if (pdev->id < 0 || pdev->id >= SUP_UART_NR) + return -EINVAL; + + sup = devm_kzalloc(&pdev->dev, sizeof(*sup), GFP_KERNEL); + if (!sup) + return -ENOMEM; + + sup->clk = devm_clk_get_optional(&pdev->dev, NULL); + if (IS_ERR(sup->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(sup->clk), "clk not found\n"); + + ret = clk_prepare_enable(sup->clk); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&pdev->dev, + (void(*)(void *))clk_disable_unprepare, + sup->clk); + if (ret) + return ret; + + sup->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); + if (IS_ERR(sup->rstc)) + return dev_err_probe(&pdev->dev, PTR_ERR(sup->rstc), "rstc not found\n"); + + port = &sup->port; + + port->membase = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(port->membase)) + return dev_err_probe(&pdev->dev, PTR_ERR(port->membase), "membase not found\n"); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + port->mapbase = res->start; + port->uartclk = clk_get_rate(sup->clk); + port->line = pdev->id; + port->irq = irq; + port->dev = &pdev->dev; + port->iotype = UPIO_MEM; + port->ops = &sunplus_uart_ops; + port->flags = UPF_BOOT_AUTOCONF; + port->fifosize = 128; + + ret = reset_control_deassert(sup->rstc); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&pdev->dev, + (void(*)(void *))reset_control_assert, + sup->rstc); + if (ret) + return ret; + +#ifdef CONFIG_SERIAL_SUNPLUS_CONSOLE + sunplus_console_ports[sup->port.line] = sup; +#endif + + platform_set_drvdata(pdev, &sup->port); + + ret = uart_add_one_port(&sunplus_uart_driver, &sup->port); +#ifdef CONFIG_SERIAL_SUNPLUS_CONSOLE + if (ret) + sunplus_console_ports[sup->port.line] = NULL; +#endif + + return ret; +} + +static int sunplus_uart_remove(struct platform_device *pdev) +{ + struct sunplus_uart_port *sup = platform_get_drvdata(pdev); + + uart_remove_one_port(&sunplus_uart_driver, &sup->port); + + return 0; +} + +static int sunplus_uart_suspend(struct device *dev) +{ + struct sunplus_uart_port *sup = dev_get_drvdata(dev); + + if (!uart_console(&sup->port)) + uart_suspend_port(&sunplus_uart_driver, &sup->port); + + return 0; +} + +static int sunplus_uart_resume(struct device *dev) +{ + struct sunplus_uart_port *sup = dev_get_drvdata(dev); + + if (!uart_console(&sup->port)) + uart_resume_port(&sunplus_uart_driver, &sup->port); + + return 0; +} + +static const struct dev_pm_ops sunplus_uart_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(sunplus_uart_suspend, sunplus_uart_resume) +}; + +static const struct of_device_id sp_uart_of_match[] = { + { .compatible = "sunplus,sp7021-uart" }, + {} +}; +MODULE_DEVICE_TABLE(of, sp_uart_of_match); + +static struct platform_driver sunplus_uart_platform_driver = { + .probe = sunplus_uart_probe, + .remove = sunplus_uart_remove, + .driver = { + .name = "sunplus_uart", + .of_match_table = sp_uart_of_match, + .pm = &sunplus_uart_pm_ops, + } +}; + +static int __init sunplus_uart_init(void) +{ + int ret; + +#ifdef CONFIG_SERIAL_SUNPLUS_CONSOLE + sunplus_uart_driver.cons = &sunplus_uart_console; +#endif + + ret = uart_register_driver(&sunplus_uart_driver); + if (ret) + return ret; + + ret = platform_driver_register(&sunplus_uart_platform_driver); + if (ret) + uart_unregister_driver(&sunplus_uart_driver); + + return ret; +} +module_init(sunplus_uart_init); + +static void __exit sunplus_uart_exit(void) +{ + platform_driver_unregister(&sunplus_uart_platform_driver); + uart_unregister_driver(&sunplus_uart_driver); +} +module_exit(sunplus_uart_exit); + +#ifdef CONFIG_SERIAL_EARLYCON +static void sunplus_uart_putc(struct uart_port *port, int c) +{ + unsigned int val; + int ret; + + ret = readl_poll_timeout_atomic(port->membase + SUP_UART_LSR, val, + (val & UART_LSR_TEMT), 1, 10000); + if (ret) + return; + + writel(c, port->membase + SUP_UART_DATA); +} + +static void sunplus_uart_early_write(struct console *con, const char *s, unsigned int n) +{ + struct earlycon_device *dev = con->data; + + uart_console_write(&dev->port, s, n, sunplus_uart_putc); +} + +static int __init +sunplus_uart_early_setup(struct earlycon_device *dev, const char *opt) +{ + if (!(dev->port.membase || dev->port.iobase)) + return -ENODEV; + + dev->con->write = sunplus_uart_early_write; + + return 0; +} +OF_EARLYCON_DECLARE(sunplus_uart, "sunplus,sp7021-uart", sunplus_uart_early_setup); +#endif + +MODULE_DESCRIPTION("Sunplus UART driver"); +MODULE_AUTHOR("Hammer Hsieh <hammerh0314@gmail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index c4042dc..2dfe443 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -274,4 +274,7 @@ /* Freescale LINFlexD UART */ #define PORT_LINFLEXUART 122 +/* Sunplus UART */ +#define PORT_SUNPLUS 123 + #endif /* _UAPILINUX_SERIAL_CORE_H */ -- 2.7.4 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v7 2/2] serial:sunplus-uart:Add Sunplus SoC UART Driver 2022-02-07 5:58 ` [PATCH v7 2/2] serial:sunplus-uart:Add " Hammer Hsieh @ 2022-02-07 7:18 ` Greg KH 2022-02-07 11:28 ` hammer hsieh 2022-02-07 11:43 ` kernel test robot ` (2 subsequent siblings) 3 siblings, 1 reply; 11+ messages in thread From: Greg KH @ 2022-02-07 7:18 UTC (permalink / raw) To: Hammer Hsieh Cc: robh+dt, linux-serial, devicetree, linux-kernel, jirislaby, p.zabel, wells.lu, hammer.hsieh On Mon, Feb 07, 2022 at 01:58:01PM +0800, Hammer Hsieh wrote: > Add Sunplus SoC UART Driver > We need more of a changelog comment here please. Describe the hardware, what the new tty name you are using, and other stuff. Be descriptive. > --- a/include/uapi/linux/serial_core.h > +++ b/include/uapi/linux/serial_core.h > @@ -274,4 +274,7 @@ > /* Freescale LINFlexD UART */ > #define PORT_LINFLEXUART 122 > > +/* Sunplus UART */ > +#define PORT_SUNPLUS 123 Why is this needed? Are you going to require this in some userspace code? If not, please do not add it. thanks, greg k-h ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v7 2/2] serial:sunplus-uart:Add Sunplus SoC UART Driver 2022-02-07 7:18 ` Greg KH @ 2022-02-07 11:28 ` hammer hsieh 0 siblings, 0 replies; 11+ messages in thread From: hammer hsieh @ 2022-02-07 11:28 UTC (permalink / raw) To: Greg KH Cc: robh+dt, linux-serial, devicetree, linux-kernel, Jiri Slaby, p.zabel, wells.lu, hammer.hsieh Greg KH <gregkh@linuxfoundation.org> 於 2022年2月7日 週一 下午3:18寫道: > > On Mon, Feb 07, 2022 at 01:58:01PM +0800, Hammer Hsieh wrote: > > Add Sunplus SoC UART Driver > > > > We need more of a changelog comment here please. Describe the hardware, > what the new tty name you are using, and other stuff. Be descriptive. > I will describe as below. Add Sunplus SoC UART Driver. SP7021 UART block contains 5 UARTs. There are UART0~4 that supported in SP7021, the features list as below. Support Full-duplex communication. Support data packet length configurable. Support stop bit number configurable. Support force break condition. Support baud rate configurable. Support error detection and report. Support RXD Noise Rejection Vote configurable. UART0 pinout only support TX/RX two pins. UART1 to UART4 pinout support TX/RX/CTS/RTS four pins. Normally UART0 used for kernel console, also can be used for normal uart. Command line set "console=ttySUP0,115200", SUP means Sunplus Uart Port. UART driver probe will create path named "/dev/ttySUPx". https://sunplus.atlassian.net/wiki/spaces/doc/pages/1873412290/13.+Universal+Asynchronous+Receiver+Transmitter+UART > > --- a/include/uapi/linux/serial_core.h > > +++ b/include/uapi/linux/serial_core.h > > @@ -274,4 +274,7 @@ > > /* Freescale LINFlexD UART */ > > #define PORT_LINFLEXUART 122 > > > > +/* Sunplus UART */ > > +#define PORT_SUNPLUS 123 > > Why is this needed? Are you going to require this in some userspace > code? If not, please do not add it. > In serial_core.c (line 3014) uart_add_one_port( ) It will call uart_configure_port( ). In my uart driver probe set port->flags=UPF_BOOT_AUTOCONF. I try to remove port define and remove sunplus_config_port( ) and just add a "sunplus_uart" for in sunplus_type( ). And my uart console not work with PuTTY tool. I try to do another test as below. static const char *sunplus_type(struct uart_port *port) { return "sunplus_uart"; } static void sunplus_config_port(struct uart_port *port, int type) { if (type & UART_CONFIG_TYPE) port->type = "sunplus_uart"; //type define is unsigned int } Uart console works, but port->type should be unsigned int , not string. So compile will warning " assignment makes integer from pointer without a cast [-Wint-conversion]". I think I should keep the current submit code. > thanks, > > greg k-h ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v7 2/2] serial:sunplus-uart:Add Sunplus SoC UART Driver 2022-02-07 5:58 ` [PATCH v7 2/2] serial:sunplus-uart:Add " Hammer Hsieh 2022-02-07 7:18 ` Greg KH @ 2022-02-07 11:43 ` kernel test robot 2022-02-07 12:14 ` kernel test robot 2022-02-08 6:27 ` Jiri Slaby 3 siblings, 0 replies; 11+ messages in thread From: kernel test robot @ 2022-02-07 11:43 UTC (permalink / raw) To: kbuild-all [-- Attachment #1: Type: text/plain, Size: 6917 bytes --] Hi Hammer, I love your patch! Perhaps something to improve: [auto build test WARNING on linux/master] [cannot apply to tty/tty-testing robh/for-next linus/master v5.17-rc3 next-20220207] [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] url: https://github.com/0day-ci/linux/commits/Hammer-Hsieh/Add-UART-driver-for-Suplus-SP7021-SoC/20220207-144451 base: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 2c271fe77d52a0555161926c232cd5bc07178b39 config: ia64-allmodconfig (https://download.01.org/0day-ci/archive/20220207/202202071924.YLKjNyyr-lkp(a)intel.com/config) compiler: ia64-linux-gcc (GCC) 11.2.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/0day-ci/linux/commit/423c8dea29f94c6fe20e3864a2424e7bc4b79b27 git remote add linux-review https://github.com/0day-ci/linux git fetch --no-tags linux-review Hammer-Hsieh/Add-UART-driver-for-Suplus-SP7021-SoC/20220207-144451 git checkout 423c8dea29f94c6fe20e3864a2424e7bc4b79b27 # save the config file to linux build tree mkdir build_dir COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=ia64 SHELL=/bin/bash drivers/tty/serial/ If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot <lkp@intel.com> All warnings (new ones prefixed by >>): drivers/tty/serial/sunplus-uart.c: In function 'sunplus_uart_probe': >> drivers/tty/serial/sunplus-uart.c:630:40: warning: cast between incompatible function types from 'int (*)(struct reset_control *)' to 'void (*)(void *)' [-Wcast-function-type] 630 | (void(*)(void *))reset_control_assert, | ^ In file included from drivers/tty/serial/sunplus-uart.c:13: drivers/tty/serial/sunplus-uart.c: At top level: include/linux/module.h:131:49: error: redefinition of '__inittest' 131 | static inline initcall_t __maybe_unused __inittest(void) \ | ^~~~~~~~~~ drivers/tty/serial/sunplus-uart.c:717:1: note: in expansion of macro 'module_init' 717 | module_init(sunplus_uart_init); | ^~~~~~~~~~~ include/linux/module.h:131:49: note: previous definition of '__inittest' with type 'int (*(void))(void)' 131 | static inline initcall_t __maybe_unused __inittest(void) \ | ^~~~~~~~~~ include/linux/module.h:127:41: note: in expansion of macro 'module_init' 127 | #define console_initcall(fn) module_init(fn) | ^~~~~~~~~~~ drivers/tty/serial/sunplus-uart.c:558:1: note: in expansion of macro 'console_initcall' 558 | console_initcall(sunplus_console_init); | ^~~~~~~~~~~~~~~~ include/linux/module.h:133:13: error: redefinition of 'init_module' 133 | int init_module(void) __copy(initfn) \ | ^~~~~~~~~~~ drivers/tty/serial/sunplus-uart.c:717:1: note: in expansion of macro 'module_init' 717 | module_init(sunplus_uart_init); | ^~~~~~~~~~~ include/linux/module.h:133:13: note: previous definition of 'init_module' with type 'int(void)' 133 | int init_module(void) __copy(initfn) \ | ^~~~~~~~~~~ include/linux/module.h:127:41: note: in expansion of macro 'module_init' 127 | #define console_initcall(fn) module_init(fn) | ^~~~~~~~~~~ drivers/tty/serial/sunplus-uart.c:558:1: note: in expansion of macro 'console_initcall' 558 | console_initcall(sunplus_console_init); | ^~~~~~~~~~~~~~~~ drivers/tty/serial/sunplus-uart.c:669:12: warning: 'sunplus_uart_resume' defined but not used [-Wunused-function] 669 | static int sunplus_uart_resume(struct device *dev) | ^~~~~~~~~~~~~~~~~~~ drivers/tty/serial/sunplus-uart.c:659:12: warning: 'sunplus_uart_suspend' defined but not used [-Wunused-function] 659 | static int sunplus_uart_suspend(struct device *dev) | ^~~~~~~~~~~~~~~~~~~~ vim +630 drivers/tty/serial/sunplus-uart.c 570 571 static int sunplus_uart_probe(struct platform_device *pdev) 572 { 573 struct sunplus_uart_port *sup; 574 struct uart_port *port; 575 struct resource *res; 576 int ret, irq; 577 578 pdev->id = of_alias_get_id(pdev->dev.of_node, "serial"); 579 580 if (pdev->id < 0 || pdev->id >= SUP_UART_NR) 581 return -EINVAL; 582 583 sup = devm_kzalloc(&pdev->dev, sizeof(*sup), GFP_KERNEL); 584 if (!sup) 585 return -ENOMEM; 586 587 sup->clk = devm_clk_get_optional(&pdev->dev, NULL); 588 if (IS_ERR(sup->clk)) 589 return dev_err_probe(&pdev->dev, PTR_ERR(sup->clk), "clk not found\n"); 590 591 ret = clk_prepare_enable(sup->clk); 592 if (ret) 593 return ret; 594 595 ret = devm_add_action_or_reset(&pdev->dev, 596 (void(*)(void *))clk_disable_unprepare, 597 sup->clk); 598 if (ret) 599 return ret; 600 601 sup->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); 602 if (IS_ERR(sup->rstc)) 603 return dev_err_probe(&pdev->dev, PTR_ERR(sup->rstc), "rstc not found\n"); 604 605 port = &sup->port; 606 607 port->membase = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 608 if (IS_ERR(port->membase)) 609 return dev_err_probe(&pdev->dev, PTR_ERR(port->membase), "membase not found\n"); 610 611 irq = platform_get_irq(pdev, 0); 612 if (irq < 0) 613 return irq; 614 615 port->mapbase = res->start; 616 port->uartclk = clk_get_rate(sup->clk); 617 port->line = pdev->id; 618 port->irq = irq; 619 port->dev = &pdev->dev; 620 port->iotype = UPIO_MEM; 621 port->ops = &sunplus_uart_ops; 622 port->flags = UPF_BOOT_AUTOCONF; 623 port->fifosize = 128; 624 625 ret = reset_control_deassert(sup->rstc); 626 if (ret) 627 return ret; 628 629 ret = devm_add_action_or_reset(&pdev->dev, > 630 (void(*)(void *))reset_control_assert, 631 sup->rstc); 632 if (ret) 633 return ret; 634 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v7 2/2] serial:sunplus-uart:Add Sunplus SoC UART Driver 2022-02-07 5:58 ` [PATCH v7 2/2] serial:sunplus-uart:Add " Hammer Hsieh 2022-02-07 7:18 ` Greg KH 2022-02-07 11:43 ` kernel test robot @ 2022-02-07 12:14 ` kernel test robot 2022-02-08 6:27 ` Jiri Slaby 3 siblings, 0 replies; 11+ messages in thread From: kernel test robot @ 2022-02-07 12:14 UTC (permalink / raw) To: kbuild-all [-- Attachment #1: Type: text/plain, Size: 7714 bytes --] Hi Hammer, I love your patch! Yet something to improve: [auto build test ERROR on linux/master] [cannot apply to tty/tty-testing robh/for-next linus/master v5.17-rc3 next-20220207] [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] url: https://github.com/0day-ci/linux/commits/Hammer-Hsieh/Add-UART-driver-for-Suplus-SP7021-SoC/20220207-144451 base: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 2c271fe77d52a0555161926c232cd5bc07178b39 config: powerpc-allmodconfig (https://download.01.org/0day-ci/archive/20220207/202202072009.s4knQBpy-lkp(a)intel.com/config) compiler: powerpc-linux-gcc (GCC) 11.2.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/0day-ci/linux/commit/423c8dea29f94c6fe20e3864a2424e7bc4b79b27 git remote add linux-review https://github.com/0day-ci/linux git fetch --no-tags linux-review Hammer-Hsieh/Add-UART-driver-for-Suplus-SP7021-SoC/20220207-144451 git checkout 423c8dea29f94c6fe20e3864a2424e7bc4b79b27 # save the config file to linux build tree mkdir build_dir COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=powerpc SHELL=/bin/bash drivers/tty/serial/ If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot <lkp@intel.com> All error/warnings (new ones prefixed by >>): drivers/tty/serial/sunplus-uart.c: In function 'sunplus_poll_put_char': >> drivers/tty/serial/sunplus-uart.c:436:9: error: implicit declaration of function 'wait_for_xmitr'; did you mean 'wait_on_bit'? [-Werror=implicit-function-declaration] 436 | wait_for_xmitr(port); | ^~~~~~~~~~~~~~ | wait_on_bit drivers/tty/serial/sunplus-uart.c: At top level: >> drivers/tty/serial/sunplus-uart.c:475:13: warning: conflicting types for 'wait_for_xmitr'; have 'void(struct uart_port *)' 475 | static void wait_for_xmitr(struct uart_port *port) | ^~~~~~~~~~~~~~ >> drivers/tty/serial/sunplus-uart.c:475:13: error: static declaration of 'wait_for_xmitr' follows non-static declaration drivers/tty/serial/sunplus-uart.c:436:9: note: previous implicit declaration of 'wait_for_xmitr' with type 'void(struct uart_port *)' 436 | wait_for_xmitr(port); | ^~~~~~~~~~~~~~ drivers/tty/serial/sunplus-uart.c: In function 'sunplus_uart_probe': drivers/tty/serial/sunplus-uart.c:630:40: warning: cast between incompatible function types from 'int (*)(struct reset_control *)' to 'void (*)(void *)' [-Wcast-function-type] 630 | (void(*)(void *))reset_control_assert, | ^ In file included from include/linux/device/driver.h:21, from include/linux/device.h:32, from arch/powerpc/include/asm/io.h:27, from include/linux/io.h:13, from include/linux/irq.h:20, from arch/powerpc/include/asm/hardirq.h:6, from include/linux/hardirq.h:11, from include/linux/interrupt.h:11, from drivers/tty/serial/sunplus-uart.c:10: drivers/tty/serial/sunplus-uart.c: At top level: include/linux/module.h:131:49: error: redefinition of '__inittest' 131 | static inline initcall_t __maybe_unused __inittest(void) \ | ^~~~~~~~~~ drivers/tty/serial/sunplus-uart.c:717:1: note: in expansion of macro 'module_init' 717 | module_init(sunplus_uart_init); | ^~~~~~~~~~~ include/linux/module.h:131:49: note: previous definition of '__inittest' with type 'int (*(void))(void)' 131 | static inline initcall_t __maybe_unused __inittest(void) \ | ^~~~~~~~~~ include/linux/module.h:127:41: note: in expansion of macro 'module_init' 127 | #define console_initcall(fn) module_init(fn) | ^~~~~~~~~~~ drivers/tty/serial/sunplus-uart.c:558:1: note: in expansion of macro 'console_initcall' 558 | console_initcall(sunplus_console_init); | ^~~~~~~~~~~~~~~~ include/linux/module.h:133:13: error: redefinition of 'init_module' 133 | int init_module(void) __copy(initfn) \ | ^~~~~~~~~~~ drivers/tty/serial/sunplus-uart.c:717:1: note: in expansion of macro 'module_init' 717 | module_init(sunplus_uart_init); | ^~~~~~~~~~~ include/linux/module.h:133:13: note: previous definition of 'init_module' with type 'int(void)' 133 | int init_module(void) __copy(initfn) \ | ^~~~~~~~~~~ include/linux/module.h:127:41: note: in expansion of macro 'module_init' 127 | #define console_initcall(fn) module_init(fn) | ^~~~~~~~~~~ drivers/tty/serial/sunplus-uart.c:558:1: note: in expansion of macro 'console_initcall' 558 | console_initcall(sunplus_console_init); | ^~~~~~~~~~~~~~~~ cc1: some warnings being treated as errors vim +436 drivers/tty/serial/sunplus-uart.c 432 433 #ifdef CONFIG_CONSOLE_POLL 434 static void sunplus_poll_put_char(struct uart_port *port, unsigned char data) 435 { > 436 wait_for_xmitr(port); 437 sp_uart_put_char(port, data); 438 } 439 440 static int sunplus_poll_get_char(struct uart_port *port) 441 { 442 unsigned int lsr = readl(port->membase + SUP_UART_LSR); 443 444 if (!(lsr & SUP_UART_LSR_RX)) 445 return NO_POLL_CHAR; 446 447 return readl(port->membase + SUP_UART_DATA); 448 } 449 #endif 450 451 static const struct uart_ops sunplus_uart_ops = { 452 .tx_empty = sunplus_tx_empty, 453 .set_mctrl = sunplus_set_mctrl, 454 .get_mctrl = sunplus_get_mctrl, 455 .stop_tx = sunplus_stop_tx, 456 .start_tx = sunplus_start_tx, 457 .stop_rx = sunplus_stop_rx, 458 .break_ctl = sunplus_break_ctl, 459 .startup = sunplus_startup, 460 .shutdown = sunplus_shutdown, 461 .set_termios = sunplus_set_termios, 462 .set_ldisc = sunplus_set_ldisc, 463 .type = sunplus_type, 464 .config_port = sunplus_config_port, 465 .verify_port = sunplus_verify_port, 466 #ifdef CONFIG_CONSOLE_POLL 467 .poll_put_char = sunplus_poll_put_char, 468 .poll_get_char = sunplus_poll_get_char, 469 #endif 470 }; 471 472 #ifdef CONFIG_SERIAL_SUNPLUS_CONSOLE 473 struct sunplus_uart_port *sunplus_console_ports[SUP_UART_NR]; 474 > 475 static void wait_for_xmitr(struct uart_port *port) 476 { 477 unsigned int val; 478 int ret; 479 480 /* Wait while FIFO is full or timeout */ 481 ret = readl_poll_timeout_atomic(port->membase + SUP_UART_LSR, val, 482 (val & SUP_UART_LSR_TX), 1, 10000); 483 484 if (ret == -ETIMEDOUT) { 485 dev_err(port->dev, "Timeout waiting while UART TX FULL\n"); 486 return; 487 } 488 } 489 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v7 2/2] serial:sunplus-uart:Add Sunplus SoC UART Driver 2022-02-07 5:58 ` [PATCH v7 2/2] serial:sunplus-uart:Add " Hammer Hsieh ` (2 preceding siblings ...) 2022-02-07 12:14 ` kernel test robot @ 2022-02-08 6:27 ` Jiri Slaby 2022-02-08 11:16 ` hammer hsieh 3 siblings, 1 reply; 11+ messages in thread From: Jiri Slaby @ 2022-02-08 6:27 UTC (permalink / raw) To: Hammer Hsieh, gregkh, robh+dt, linux-serial, devicetree, linux-kernel, p.zabel Cc: wells.lu, hammer.hsieh Hi, On 07. 02. 22, 6:58, Hammer Hsieh wrote: > +static void sunplus_shutdown(struct uart_port *port) > +{ > + unsigned long flags; > + unsigned int isc; > + > + spin_lock_irqsave(&port->lock, flags); > + > + isc = readl(port->membase + SUP_UART_ISC); > + isc &= ~(SUP_UART_ISC_RXM | SUP_UART_ISC_TXM); Is this correct? I mean: will the SUP_UART_ISC read contain the control bits, not only status bits? > + writel(isc, port->membase + SUP_UART_ISC); > + > + spin_unlock_irqrestore(&port->lock, flags); > + > + free_irq(port->irq, port); I am still waiting for explanation why this is safe with respect to posted writes. regards, -- js suse labs ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v7 2/2] serial:sunplus-uart:Add Sunplus SoC UART Driver 2022-02-08 6:27 ` Jiri Slaby @ 2022-02-08 11:16 ` hammer hsieh 2022-02-08 11:31 ` Greg KH 0 siblings, 1 reply; 11+ messages in thread From: hammer hsieh @ 2022-02-08 11:16 UTC (permalink / raw) To: Jiri Slaby Cc: Greg KH, robh+dt, linux-serial, devicetree, linux-kernel, p.zabel, wells.lu, hammer.hsieh Jiri Slaby <jirislaby@kernel.org> 於 2022年2月8日 週二 下午2:27寫道: > > Hi, > > On 07. 02. 22, 6:58, Hammer Hsieh wrote: > > +static void sunplus_shutdown(struct uart_port *port) > > +{ > > + unsigned long flags; > > + unsigned int isc; > > + > > + spin_lock_irqsave(&port->lock, flags); > > + > > + isc = readl(port->membase + SUP_UART_ISC); > > + isc &= ~(SUP_UART_ISC_RXM | SUP_UART_ISC_TXM); > > Is this correct? I mean: will the SUP_UART_ISC read contain the control > bits, not only status bits? > I assume reviewers don't like writel(0,xxx). So I use definition to let the code easy to read. The purpose is to clear all interrupt. Bit[3:0] status bit only for read, write 1 or 0 no effect. > > + writel(isc, port->membase + SUP_UART_ISC); > > + > > + spin_unlock_irqrestore(&port->lock, flags); > > + > > + free_irq(port->irq, port); > > I am still waiting for explanation why this is safe with respect to > posted writes. > Actually I'm not IC designer, not expert for bus design. About data incoherence issue between memory bus and peripheral bus. In case of AXI bus, use non-posted write can avoid data incoherence issue. What if in case of posted write: Send a specific command after last write command. SDCTRL identify specific command, means previous write command done. Then send interrupt signal to interrupt controller. And then interrupt controller send done signal to Master. Master receive done signal, means write command done. Then issue a interrupt or proceed next write command. IC designer told us no problem, and as a uart driver maintainer. It is not my concern, but glad you ask. Let me have chance to know it from IC designer. > regards, > -- > js > suse labs ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v7 2/2] serial:sunplus-uart:Add Sunplus SoC UART Driver 2022-02-08 11:16 ` hammer hsieh @ 2022-02-08 11:31 ` Greg KH 2022-02-09 10:53 ` hammer hsieh 0 siblings, 1 reply; 11+ messages in thread From: Greg KH @ 2022-02-08 11:31 UTC (permalink / raw) To: hammer hsieh Cc: Jiri Slaby, robh+dt, linux-serial, devicetree, linux-kernel, p.zabel, wells.lu, hammer.hsieh On Tue, Feb 08, 2022 at 07:16:52PM +0800, hammer hsieh wrote: > Jiri Slaby <jirislaby@kernel.org> 於 2022年2月8日 週二 下午2:27寫道: > > > > Hi, > > > > On 07. 02. 22, 6:58, Hammer Hsieh wrote: > > > +static void sunplus_shutdown(struct uart_port *port) > > > +{ > > > + unsigned long flags; > > > + unsigned int isc; > > > + > > > + spin_lock_irqsave(&port->lock, flags); > > > + > > > + isc = readl(port->membase + SUP_UART_ISC); > > > + isc &= ~(SUP_UART_ISC_RXM | SUP_UART_ISC_TXM); > > > > Is this correct? I mean: will the SUP_UART_ISC read contain the control > > bits, not only status bits? > > > > I assume reviewers don't like writel(0,xxx). > So I use definition to let the code easy to read. > The purpose is to clear all interrupt. > Bit[3:0] status bit only for read, write 1 or 0 no effect. > > > > + writel(isc, port->membase + SUP_UART_ISC); > > > + > > > + spin_unlock_irqrestore(&port->lock, flags); > > > + > > > + free_irq(port->irq, port); > > > > I am still waiting for explanation why this is safe with respect to > > posted writes. > > > > Actually I'm not IC designer, not expert for bus design. > About data incoherence issue between memory bus and peripheral bus. > In case of AXI bus, use non-posted write can avoid data incoherence issue. > What if in case of posted write: > Send a specific command after last write command. > SDCTRL identify specific command, means previous write command done. > Then send interrupt signal to interrupt controller. > And then interrupt controller send done signal to Master. > Master receive done signal, means write command done. > Then issue a interrupt or proceed next write command. But how does the kernel know when the write is completed? The kernel seems to ignore that here entirely, so the write could actually complete seconds later, which would not be a good thing, right? Traditionally, we want to ensure that a write() completes, so on some busses, we have to do a read to ensure that the write made it to the hardware before we can continue on. That is not happening here which is why Jiri keeps bringing it up. It looks broken to us, and you need to document it somewhere (in the changelog? In the top of the file?) as to why this is not needed. thanks, greg k-h ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v7 2/2] serial:sunplus-uart:Add Sunplus SoC UART Driver 2022-02-08 11:31 ` Greg KH @ 2022-02-09 10:53 ` hammer hsieh 0 siblings, 0 replies; 11+ messages in thread From: hammer hsieh @ 2022-02-09 10:53 UTC (permalink / raw) To: Greg KH Cc: Jiri Slaby, robh+dt, linux-serial, devicetree, linux-kernel, p.zabel, wells.lu, hammer.hsieh Ok, thanks for your explaination, I got it now. I will document "posted write" info in the top of the file or top of startup/shutdown function. And kernel test robot report me build error and warning with gcc 11.2.0 ia64 / powerpc. I will fix it and send next patch. Greg KH <gregkh@linuxfoundation.org> 於 2022年2月8日 週二 下午7:31寫道: > > On Tue, Feb 08, 2022 at 07:16:52PM +0800, hammer hsieh wrote: > > Jiri Slaby <jirislaby@kernel.org> 於 2022年2月8日 週二 下午2:27寫道: > > > > > > Hi, > > > > > > On 07. 02. 22, 6:58, Hammer Hsieh wrote: > > > > +static void sunplus_shutdown(struct uart_port *port) > > > > +{ > > > > + unsigned long flags; > > > > + unsigned int isc; > > > > + > > > > + spin_lock_irqsave(&port->lock, flags); > > > > + > > > > + isc = readl(port->membase + SUP_UART_ISC); > > > > + isc &= ~(SUP_UART_ISC_RXM | SUP_UART_ISC_TXM); > > > > > > Is this correct? I mean: will the SUP_UART_ISC read contain the control > > > bits, not only status bits? > > > > > > > I assume reviewers don't like writel(0,xxx). > > So I use definition to let the code easy to read. > > The purpose is to clear all interrupt. > > Bit[3:0] status bit only for read, write 1 or 0 no effect. > > > > > > + writel(isc, port->membase + SUP_UART_ISC); > > > > + > > > > + spin_unlock_irqrestore(&port->lock, flags); > > > > + > > > > + free_irq(port->irq, port); > > > > > > I am still waiting for explanation why this is safe with respect to > > > posted writes. > > > > > > > Actually I'm not IC designer, not expert for bus design. > > About data incoherence issue between memory bus and peripheral bus. > > In case of AXI bus, use non-posted write can avoid data incoherence issue. > > What if in case of posted write: > > Send a specific command after last write command. > > SDCTRL identify specific command, means previous write command done. > > Then send interrupt signal to interrupt controller. > > And then interrupt controller send done signal to Master. > > Master receive done signal, means write command done. > > Then issue a interrupt or proceed next write command. > > But how does the kernel know when the write is completed? The kernel > seems to ignore that here entirely, so the write could actually complete > seconds later, which would not be a good thing, right? > > Traditionally, we want to ensure that a write() completes, so on some > busses, we have to do a read to ensure that the write made it to the > hardware before we can continue on. That is not happening here which is > why Jiri keeps bringing it up. It looks broken to us, and you need to > document it somewhere (in the changelog? In the top of the file?) as to > why this is not needed. > > thanks, > > greg k-h ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2022-02-09 11:55 UTC | newest] Thread overview: 11+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2022-02-07 5:57 [PATCH v7 0/2] Add UART driver for Suplus SP7021 SoC Hammer Hsieh 2022-02-07 5:58 ` [PATCH v7 1/2] dt-bindings:serial:Add bindings doc for Sunplus SoC UART Driver Hammer Hsieh 2022-02-07 5:58 ` [PATCH v7 2/2] serial:sunplus-uart:Add " Hammer Hsieh 2022-02-07 7:18 ` Greg KH 2022-02-07 11:28 ` hammer hsieh 2022-02-07 11:43 ` kernel test robot 2022-02-07 12:14 ` kernel test robot 2022-02-08 6:27 ` Jiri Slaby 2022-02-08 11:16 ` hammer hsieh 2022-02-08 11:31 ` Greg KH 2022-02-09 10:53 ` hammer hsieh
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.