Classification: Confidential This driver patch adds the UART TTY serial support for the RISC-V based InCore Semiconductor's Chromite SoC family like Chromite M, Chromite H. Signed-off-by: Sathish Kumar Balasubramaniam <b-sathishkumar@xxxxxxx> --- drivers/tty/serial/Kconfig | 25 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/chromite_uart.c | 764 +++++++++++++++++++++++++++++ include/uapi/linux/serial_core.h | 3 + 4 files changed, 793 insertions(+) create mode 100644 drivers/tty/serial/chromite_uart.c diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 24282ad99d85..f238bdb04ba6 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1009,6 +1009,31 @@ config SERIAL_SIFIVE_CONSOLE your boot loader about how to pass options to the kernel at boot time.) +config SERIAL_CHROMITE + tristate "Chromite UART support" + depends on OF + select SERIAL_CORE + help + Select this option if you are building a kernel for a device that + contains a Chromite UART IP block. This type of UART is present on + Chromite-H SoC, among others. + +config SERIAL_CHROMITE_CONSOLE + bool "Console on Chromite UART" + depends on SERIAL_CHROMITE=y + select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON + help + Select this option if you would like to use a Chromite 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=ttyCRMTx". (Try "man bootparam" or see the documentation of + your boot loader about how to pass options to the kernel at + boot time.) + config SERIAL_LANTIQ tristate "Lantiq serial driver" depends on (LANTIQ || X86) || COMPILE_TEST diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 7da0856cd198..dd0acc512714 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -86,6 +86,7 @@ obj-$(CONFIG_SERIAL_OWL) += owl-uart.o obj-$(CONFIG_SERIAL_RDA) += rda-uart.o obj-$(CONFIG_SERIAL_MILBEAUT_USIO) += milbeaut_usio.o obj-$(CONFIG_SERIAL_SIFIVE) += sifive.o +obj-$(CONFIG_SERIAL_CHROMITE) += chromite_uart.o obj-$(CONFIG_SERIAL_LITEUART) += liteuart.o # GPIOLIB helpers for modem control lines diff --git a/drivers/tty/serial/chromite_uart.c b/drivers/tty/serial/chromite_uart.c new file mode 100644 index 000000000000..0e9da1d0d725 --- /dev/null +++ b/drivers/tty/serial/chromite_uart.c @@ -0,0 +1,764 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Chromite UART driver + * Copyright (C) 2021 Sathish Kumar Balasubramaniam <b-sathishkumar@xxxxxxx> + * Copyright (C) 2021 InCore Semiconductors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Based on: + * - drivers/tty/serial/sifive.c + * + * See the following sources for documentation + * - https://chromiteh-soc.readthedocs.io/en/latest/uart.html + * - https://chromitem-soc.readthedocs.io/en/latest/uart.html + * + * Presently the Chromite UART supports only the following configuration + * 8-N-1: 8 data bits, no parity and 1 stop-bit + * + * Presently the Chromite UART does not have support for hardware flow control or other modem control signals + * + * + */ + + +#include <linux/clk.h> +#include <linux/console.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h> +#include <linux/serial_core.h> +#include <linux/serial_reg.h> +#include <linux/slab.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> + + +#define CHROMITE_SERIAL_MAX_PORTS 1 +#define CHROMITE_DEFAULT_BAUD_RATE 115200 +#define CHROMITE_TTY_PREFIX "ttyCRMT" +#define CHROMITE_CLOCK 50000000 +#define TX_FIFO_DEPTH 16 +#define RX_FIFO_DEPTH 16 + +#define BAUD_REG_OFF 0x00 +#define TX_DATA_REG_OFF 0x04 +#define RX_DATA_REG_OFF 0x08 +#define STATUS_REG_OFF 0x0C +#define CONTROL_REG_OFF 0x10 +#define STATUS_CLEAR_REG_OFF 0x14 +#define INT_EN_REG_OFF 0x18 + +#define STATUS_REG_TX_NOTFULL_MASK 0x02 +#define STATUS_REG_RX_NOTEMPTY_MASK 0x08 +#define STATUS_REG_TX_THLD_HIT_MASK 0x10 +#define STATUS_REG_RX_THLD_HIT_MASK 0x20 + +#define CONTROL_REG_TX_THLD_AUTO_RST_SHIFT 10 +#define CONTROL_REG_RX_THLD_AUTO_RST_SHIFT 11 +#define CONTROL_REG_TX_THLD_MASK 0xF0000 +#define CONTROL_REG_RX_THLD_MASK 0xF00000 +#define CONTROL_REG_STOP_BITS_MASK 0x3 +#define CONTROL_REG_STOP_BITS_SHIFT 0 + +#define STATUS_CLEAR_TX_THLD_HIT 0x10 +#define STATUS_CLEAR_RX_THLD_HIT 0x20 + +#define INT_EN_TX_THLD_HIT_MASK 0x10 +#define INT_EN_RX_THLD_HIT_MASK 0x20 + + +struct chromite_serial_port { + struct uart_port port; + unsigned long clkin_rate; + unsigned long baud_rate; + unsigned char cread_flag; + struct device *dev; +}; + +#define port_to_chromite_serial_port(p) (container_of((p), \ + struct chromite_serial_port, \ + port)) + +static void chromite_serial_stop_tx(struct uart_port *port); + +static int chromite_serial_is_tx_fifo_full(struct chromite_serial_port *csp) +{ + u32 ret = 0; + + ret = readl_relaxed(csp->port.membase + STATUS_REG_OFF); + if ((ret & STATUS_REG_TX_NOTFULL_MASK) == 0) + return 1; + + return 0; +} + +static void chromite_serial_wait_for_xmitr(struct chromite_serial_port *csp) +{ + while (chromite_serial_is_tx_fifo_full(csp)) + udelay(1); +} + +static void chromite_serial_transmit_char(struct chromite_serial_port *csp, + unsigned char ch) +{ + writeb_relaxed(ch, csp->port.membase + TX_DATA_REG_OFF); +} + +static void chromite_serial_transmit_chars(struct chromite_serial_port *csp) +{ + struct circ_buf *xmit = &csp->port.state->xmit; + int count = 0; + + if (csp->port.x_char) { + chromite_serial_transmit_char(csp, csp->port.x_char); + csp->port.icount.tx++; + csp->port.x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(&csp->port)) { + chromite_serial_stop_tx(&csp->port); + return; + } + count = TX_FIFO_DEPTH; + do { + chromite_serial_transmit_char(csp, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + csp->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&csp->port); + + if (uart_circ_empty(xmit)) + chromite_serial_stop_tx(&csp->port); + +} + +static void chromite_serial_enable_tx_thld_hit( + struct chromite_serial_port *csp) +{ + u16 val = 0; + + val = readw_relaxed(csp->port.membase + INT_EN_REG_OFF); + if (val & INT_EN_TX_THLD_HIT_MASK) + return; + + val |= INT_EN_TX_THLD_HIT_MASK; + writew_relaxed(val, csp->port.membase + INT_EN_REG_OFF); +} + +static void chromite_serial_disable_tx_thld_hit( + struct chromite_serial_port *csp) +{ + u16 val = 0; + + val = readw_relaxed(csp->port.membase + INT_EN_REG_OFF); + if ((val & INT_EN_TX_THLD_HIT_MASK) == 0) + return; + + val &= ~INT_EN_TX_THLD_HIT_MASK; + writew_relaxed(val, csp->port.membase + INT_EN_REG_OFF); +} + +static void chromite_serial_enable_rx_thld_hit( + struct chromite_serial_port *csp) +{ + u16 val = 0; + + val = readw_relaxed(csp->port.membase + INT_EN_REG_OFF); + if (val & INT_EN_RX_THLD_HIT_MASK) + return; + + val |= INT_EN_RX_THLD_HIT_MASK; + writew_relaxed(val, csp->port.membase + INT_EN_REG_OFF); +} + +static void chromite_serial_disable_rx_thld_hit( + struct chromite_serial_port *csp) +{ + u16 val = 0; + + val = readw_relaxed(csp->port.membase + INT_EN_REG_OFF); + if ((val & INT_EN_RX_THLD_HIT_MASK) == 0) + return; + + val &= ~INT_EN_RX_THLD_HIT_MASK; + writew_relaxed(val, csp->port.membase + INT_EN_REG_OFF); +} + +static int chromite_serial_receive_char(struct chromite_serial_port *csp, + unsigned char *ch) +{ + u32 val = 0; + + if (!ch) { + BUG_ON(1); + return (-1); + } + + val = readl_relaxed(csp->port.membase + STATUS_REG_OFF); + + // check if rx FIFO is empty + if ((val & STATUS_REG_RX_NOTEMPTY_MASK) == 0) + return 1; + + // if CREAD is not set in TERMIOS, then ignore the receive characters + if (csp->cread_flag == 0) + return 1; + + *ch = readb_relaxed(csp->port.membase + RX_DATA_REG_OFF); + + return 0; +} + +static void chromite_serial_receive_chars(struct chromite_serial_port *csp) +{ + int i = 0; + unsigned char ch = 0; + + for (i = 0; i < RX_FIFO_DEPTH; i++) { + if (chromite_serial_receive_char(csp, &ch) == 0) { + csp->port.icount.rx++; + uart_insert_char(&csp->port, 0, 0, ch, TTY_NORMAL); + } else { + break; + } + } + + spin_unlock(&csp->port.lock); + tty_flip_buffer_push(&csp->port.state->port); + spin_lock(&csp->port.lock); +} + +static void chromite_serial_update_div(struct chromite_serial_port *csp) +{ + u16 div; + + div = csp->clkin_rate / (16 * csp->baud_rate); + writew_relaxed(div, csp->port.membase + BAUD_REG_OFF); +} + +static void chromite_serial_update_baud_rate(struct chromite_serial_port *csp, + unsigned int rate) +{ + if (csp->baud_rate == rate) + return; + + csp->baud_rate = rate; + chromite_serial_update_div(csp); +} + +static void chromite_serial_stop_tx(struct uart_port *port) +{ + struct chromite_serial_port *csp = port_to_chromite_serial_port(port); + + chromite_serial_disable_tx_thld_hit(csp); +} + +static void chromite_serial_stop_rx(struct uart_port *port) +{ + struct chromite_serial_port *csp = port_to_chromite_serial_port(port); + + chromite_serial_disable_rx_thld_hit(csp); +} + +static void chromite_serial_start_tx(struct uart_port *port) +{ + struct chromite_serial_port *csp = port_to_chromite_serial_port(port); + + chromite_serial_enable_tx_thld_hit(csp); +} + +static irqreturn_t chromite_serial_irq(int irq, void *dev_id) +{ + struct chromite_serial_port *csp = dev_id; + u32 status_reg; + + spin_lock(&csp->port.lock); + + status_reg = readl_relaxed(csp->port.membase + STATUS_REG_OFF); + + if ((status_reg & (STATUS_REG_RX_THLD_HIT_MASK | + STATUS_REG_TX_THLD_HIT_MASK)) == 0) { + spin_unlock(&csp->port.lock); + return IRQ_NONE; + } + if (status_reg & STATUS_REG_RX_THLD_HIT_MASK) { + chromite_serial_receive_chars(csp); + writeb_relaxed(STATUS_CLEAR_RX_THLD_HIT, csp->port.membase + + STATUS_CLEAR_REG_OFF); + } + if (status_reg & STATUS_REG_TX_THLD_HIT_MASK) { + chromite_serial_transmit_chars(csp); + writeb_relaxed(STATUS_CLEAR_TX_THLD_HIT, csp->port.membase + + STATUS_CLEAR_REG_OFF); + } + + spin_unlock(&csp->port.lock); + + return IRQ_HANDLED; +} + + +static unsigned int chromite_serial_tx_empty(struct uart_port *port) +{ + return TIOCSER_TEMT; +} + +static unsigned int chromite_serial_get_mctrl(struct uart_port *port) +{ + return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR; +} + +static void chromite_serial_set_mctrl(struct uart_port *port, + unsigned int mctrl) +{ + /* No support */ +} + +static void chromite_serial_break_ctl(struct uart_port *port, int break_state) +{ + /* No support */ +} + +static int chromite_serial_startup(struct uart_port *port) +{ + struct chromite_serial_port *csp = port_to_chromite_serial_port(port); + + chromite_serial_enable_rx_thld_hit(csp); + + return 0; +} + +static void chromite_serial_shutdown(struct uart_port *port) +{ + struct chromite_serial_port *csp = port_to_chromite_serial_port(port); + + chromite_serial_disable_rx_thld_hit(csp); + chromite_serial_disable_tx_thld_hit(csp); +} + +static void chromite_serial_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + struct chromite_serial_port *csp = port_to_chromite_serial_port(port); + unsigned long flags; + int rate; + + if ((termios->c_cflag & CSIZE) != CS8) + dev_err_once(csp->port.dev, "only 8-bit words supported\n"); + if (termios->c_cflag & CSTOPB) + dev_err_once(csp->port.dev, "only 1 stop-bit is supported\n"); + if (termios->c_iflag & (INPCK | PARMRK)) + dev_err_once(csp->port.dev, "parity checking not supported\n"); + if (termios->c_iflag & BRKINT) + dev_err_once(csp->port.dev, "BREAK detection not supported\n"); + + /* Set line rate */ + rate = uart_get_baud_rate(port, termios, old, 0, csp->clkin_rate / 16); + chromite_serial_update_baud_rate(csp, rate); + + spin_lock_irqsave(&csp->port.lock, flags); + + /* Update the per-port timeout */ + uart_update_timeout(port, termios->c_cflag, rate); + + csp->port.read_status_mask = 0; + + /* Ignore all characters if CREAD is not set */ + if ((termios->c_cflag & CREAD) == 0) + csp->cread_flag = 0; + else + csp->cread_flag = 1; + + spin_unlock_irqrestore(&csp->port.lock, flags); +} + +static void chromite_serial_release_port(struct uart_port *port) +{ +} + +static int chromite_serial_request_port(struct uart_port *port) +{ + return 0; +} + +static void chromite_serial_config_port(struct uart_port *port, int flags) +{ + struct chromite_serial_port *csp = port_to_chromite_serial_port(port); + + csp->port.type = PORT_CHROMITE; +} + +static int chromite_serial_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + return -EINVAL; +} + +static const char *chromite_serial_type(struct uart_port *port) +{ + return port->type == PORT_CHROMITE ? "Chromite UART" : NULL; +} + +#ifdef CONFIG_CONSOLE_POLL +static int chromite_serial_poll_get_char(struct uart_port *port) +{ + struct chromite_serial_port *csp = port_to_chromite_serial_port(port); + char ch; + + if (chromite_serial_receive_char(csp, &ch) != 0) + return NO_POLL_CHAR; + + return ch; +} + +static void chromite_serial_poll_put_char(struct uart_port *port, + unsigned char c) +{ + struct chromite_serial_port *csp = port_to_chromite_serial_port(port); + + chromite_serial_wait_for_xmitr(csp); + chromite_serial_transmit_char(csp, c); +} +#endif /* CONFIG_CONSOLE_POLL */ + +/* + * Early console support + */ + +#ifdef CONFIG_SERIAL_EARLYCON +static void early_chromite_serial_putc(struct uart_port *port, int c) +{ + while ((readl_relaxed(port->membase + STATUS_REG_OFF) & + STATUS_REG_TX_NOTFULL_MASK) == 0) + cpu_relax(); + + writeb_relaxed(c, port->membase + TX_DATA_REG_OFF); +} + +static void early_chromite_serial_write(struct console *con, const char *s, + unsigned int n) +{ + struct earlycon_device *dev = con->data; + struct uart_port *port = &dev->port; + + uart_console_write(port, s, n, early_chromite_serial_putc); +} + +static int __init early_chromite_serial_setup(struct earlycon_device *dev, + const char *options) +{ + struct uart_port *port = &dev->port; + + if (!port->membase) + return -ENODEV; + + dev->con->write = early_chromite_serial_write; + + return 0; +} + +OF_EARLYCON_DECLARE(chromite, "chromite,uart0", early_chromite_serial_setup); +#endif /* CONFIG_SERIAL_EARLYCON */ + +/* + * Linux console interface + */ + +#ifdef CONFIG_SERIAL_CHROMITE_CONSOLE + +static struct chromite_serial_port *chromite_serial_console_ports[CHROMITE_SERIAL_MAX_PORTS]; + +static void chromite_serial_console_putchar(struct uart_port *port, int ch) +{ + struct chromite_serial_port *csp = port_to_chromite_serial_port(port); + + chromite_serial_wait_for_xmitr(csp); + chromite_serial_transmit_char(csp, ch); +} + +static void chromite_serial_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct chromite_serial_port *csp = chromite_serial_console_ports[co->index]; + unsigned long flags; + u16 ier; + int locked = 1; + + if (!csp) + return; + + local_irq_save(flags); + if (csp->port.sysrq) + locked = 0; + else if (oops_in_progress) + locked = spin_trylock(&csp->port.lock); + else + spin_lock(&csp->port.lock); + + ier = readw_relaxed(csp->port.membase + INT_EN_REG_OFF); + writew_relaxed(0, csp->port.membase + INT_EN_REG_OFF); + + uart_console_write(&csp->port, s, count, chromite_serial_console_putchar); + + writew_relaxed(ier, csp->port.membase + INT_EN_REG_OFF); + + if (locked) + spin_unlock(&csp->port.lock); + local_irq_restore(flags); +} + +static int __init chromite_serial_console_setup(struct console *co, char *options) +{ + struct chromite_serial_port *csp; + int baud = CHROMITE_DEFAULT_BAUD_RATE; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index < 0 || co->index >= CHROMITE_SERIAL_MAX_PORTS) + return -ENODEV; + + csp = chromite_serial_console_ports[co->index]; + if (!csp) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&csp->port, co, baud, parity, bits, flow); +} + +static struct uart_driver chromite_serial_uart_driver; + +static struct console chromite_serial_console = { + .name = CHROMITE_TTY_PREFIX, + .write = chromite_serial_console_write, + .device = uart_console_device, + .setup = chromite_serial_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &chromite_serial_uart_driver, +}; + +static int __init chromite_console_init(void) +{ + register_console(&chromite_serial_console); + return 0; +} + +console_initcall(chromite_console_init); + +static void chromite_add_console_port(struct chromite_serial_port *csp) +{ + chromite_serial_console_ports[csp->port.line] = csp; +} + +static void chromite_remove_console_port(struct chromite_serial_port *csp) +{ + chromite_serial_console_ports[csp->port.line] = 0; +} + +#define CHROMITE_SERIAL_CONSOLE (&chromite_serial_console) + +#else + +#define CHROMITE_SERIAL_CONSOLE NULL + +static void chromite_add_console_port(struct chromite_serial_port *csp) +{} +static void chromite_remove_console_port(struct chromite_serial_port *csp) +{} + +#endif /* CONFIG_SERIAL_CHROMITE_CONSOLE */ + +static const struct uart_ops chromite_serial_uart_ops = { + .tx_empty = chromite_serial_tx_empty, + .set_mctrl = chromite_serial_set_mctrl, + .get_mctrl = chromite_serial_get_mctrl, + .stop_tx = chromite_serial_stop_tx, + .start_tx = chromite_serial_start_tx, + .stop_rx = chromite_serial_stop_rx, + .break_ctl = chromite_serial_break_ctl, + .startup = chromite_serial_startup, + .shutdown = chromite_serial_shutdown, + .set_termios = chromite_serial_set_termios, + .type = chromite_serial_type, + .release_port = chromite_serial_release_port, + .request_port = chromite_serial_request_port, + .config_port = chromite_serial_config_port, + .verify_port = chromite_serial_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = chromite_serial_poll_get_char, + .poll_put_char = chromite_serial_poll_put_char, +#endif +}; + + +static struct uart_driver chromite_serial_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "chromite-serial", + .dev_name = CHROMITE_TTY_PREFIX, + .nr = 1, + .cons = CHROMITE_SERIAL_CONSOLE, +}; + +static int chromite_serial_probe(struct platform_device *pdev) +{ + struct chromite_serial_port *csp; + struct resource *mem; + void __iomem *base; + int irq, id, r; + u32 cr; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return -EPROBE_DEFER; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(base)) { + dev_err(&pdev->dev, "could not acquire device memory\n"); + return PTR_ERR(base); + } + id = of_alias_get_id(pdev->dev.of_node, "serial"); + if (id < 0) { + dev_err(&pdev->dev, "missing aliases entry\n"); + return id; + } +#ifdef CONFIG_SERIAL_CHROMITE_CONSOLE + if (id > CHROMITE_SERIAL_MAX_PORTS) { + dev_err(&pdev->dev, "too many UARTs (%d)\n", id); + return -EINVAL; + } +#endif /* CONFIG_SERIAL_CHROMITE_CONSOLE */ + + csp = devm_kzalloc(&pdev->dev, sizeof(*csp), GFP_KERNEL); + if (!csp) + return -ENOMEM; + + csp->port.dev = &pdev->dev; + csp->port.type = PORT_CHROMITE; + csp->port.iotype = UPIO_MEM; + csp->port.irq = irq; + csp->port.fifosize = TX_FIFO_DEPTH; + csp->port.ops = &chromite_serial_uart_ops; + csp->port.line = id; + csp->port.mapbase = mem->start; + csp->port.membase = base; + csp->dev = &pdev->dev; + + /* Set up clock divider */ + csp->clkin_rate = CHROMITE_CLOCK; + csp->baud_rate = CHROMITE_DEFAULT_BAUD_RATE; + csp->port.uartclk = csp->baud_rate * 16; + chromite_serial_update_div(csp); + + csp->cread_flag = 1; + + platform_set_drvdata(pdev, csp); + + cr = readl_relaxed(csp->port.membase + CONTROL_REG_OFF); + /* Set TX_THLD_AUTO_RST and RX_THLD_AUTO_RST in control register to 1 */ + cr |= (1 << CONTROL_REG_TX_THLD_AUTO_RST_SHIFT) | + (1 << CONTROL_REG_RX_THLD_AUTO_RST_SHIFT); + /* Set TX_THLD and RX_THLD in control register to 0 */ + cr &= ~(CONTROL_REG_TX_THLD_MASK | CONTROL_REG_RX_THLD_MASK); + writel_relaxed(cr, csp->port.membase + CONTROL_REG_OFF); + + r = request_irq(csp->port.irq, chromite_serial_irq, csp->port.irqflags, + dev_name(&pdev->dev), csp); + if (r) { + dev_err(&pdev->dev, "could not attach interrupt: %d\n", r); + goto probe_out1; + } + + chromite_add_console_port(csp); + + r = uart_add_one_port(&chromite_serial_uart_driver, &csp->port); + if (r != 0) { + dev_err(&pdev->dev, "could not add uart: %d\n", r); + goto probe_out2; + } + + return 0; + +probe_out2: + chromite_remove_console_port(csp); + free_irq(csp->port.irq, csp); +probe_out1: + return r; +} + +static int chromite_serial_remove(struct platform_device *dev) +{ + struct chromite_serial_port *csp = platform_get_drvdata(dev); + + chromite_remove_console_port(csp); + uart_remove_one_port(&chromite_serial_uart_driver, &csp->port); + free_irq(csp->port.irq, csp); + + return 0; +} + + +static const struct of_device_id chromite_serial_of_match[] = { + { .compatible = "chromite,uart0" }, + {}, +}; +MODULE_DEVICE_TABLE(of, chromite_serial_of_match); + +static struct platform_driver chromite_serial_platform_driver = { + .probe = chromite_serial_probe, + .remove = chromite_serial_remove, + .driver = { + .name = "chromite-serial", + .of_match_table = of_match_ptr(chromite_serial_of_match), + }, +}; + +static int __init chromite_serial_init(void) +{ + int ret = uart_register_driver(&chromite_serial_uart_driver); + + if (ret) + return ret; + + ret = platform_driver_register(&chromite_serial_platform_driver); + if (ret) + uart_unregister_driver(&chromite_serial_uart_driver); + + return ret; +} + +static void __exit chromite_serial_exit(void) +{ + platform_driver_unregister(&chromite_serial_platform_driver); + uart_unregister_driver(&chromite_serial_uart_driver); +} + +module_init(chromite_serial_init); +module_exit(chromite_serial_exit); + +MODULE_DESCRIPTION("Chromite UART serial driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Sathish Kumar Balasubrananiam <b-sathishkumar@xxxxxxx>"); + diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index c4042dcfdc0c..91592471e762 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 +/* Chromite UART */ +#define PORT_CHROMITE 123 + #endif /* _UAPILINUX_SERIAL_CORE_H */ -- 2.17.1 ::DISCLAIMER:: ________________________________ The contents of this e-mail and any attachment(s) are confidential and intended for the named recipient(s) only. E-mail transmission is not guaranteed to be secure or error-free as information could be intercepted, corrupted, lost, destroyed, arrive late or incomplete, or may contain viruses in transmission. The e mail and its contents (with or without referred errors) shall therefore not attach any liability on the originator or HCL or its affiliates. Views or opinions, if any, presented in this email are solely those of the author and may not necessarily reflect the views or opinions of HCL or its affiliates. Any form of reproduction, dissemination, copying, disclosure, modification, distribution and / or publication of this message without the prior written consent of authorized representative of HCL is strictly prohibited. If you have received this email in error please delete it and notify the sender immediately. Before opening any email and/or attachments, please check them for viruses and other defects. ________________________________