From 270244937facd49a30ff0af586f0c83b1a8e57a9 Mon Sep 17 00:00:00 2001 From: Mohamad Noor Alim Hussin Date: Mon, 10 Jan 2022 18:09:49 +0800 Subject: [PATCH] drivers: add serial spinal driver Signed-off-by: Dolu1990 Signed-off-by: Mohamad Noor Alim Hussin --- drivers/tty/serial/Kconfig | 23 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/serial_spinal_lib.c | 729 +++++++++++++++++++++++++ include/uapi/linux/serial_core.h | 3 + 4 files changed, 756 insertions(+) create mode 100644 drivers/tty/serial/serial_spinal_lib.c diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 28f22e586..da24e92c3 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1583,6 +1583,29 @@ config SERIAL_MILBEAUT_USIO_CONSOLE receives all kernel messages and warnings and which allows logins in single user mode). +config SERIAL_SPINAL_LIB_UART + tristate "Spinal lib UART support" + select SERIAL_CORE + help + This driver supports the spinal lib UART port. + +config SERIAL_SPINAL_LIB_UART_BAUDRATE + int "Default baudrate for spinal lib UART ports" + depends on SERIAL_SPINAL_LIB_UART + default 115200 + help + This setting lets you define what the default baudrate is for the + spinal lib UART ports. The usual default varies from board to board, + and this setting is a way of catering for that. + +config SERIAL_SPINAL_LIB_UART_CONSOLE + bool "Spinal lib UART console support" + depends on SERIAL_SPINAL_LIB_UART=y + select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON + help + Enable a spinal lib UART port to be the system console. + endmenu config SERIAL_MCTRL_GPIO diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index caf167f0c..bf3979e22 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -90,6 +90,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_SPINAL_LIB_UART) += serial_spinal_lib.o # GPIOLIB helpers for modem control lines obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o diff --git a/drivers/tty/serial/serial_spinal_lib.c b/drivers/tty/serial/serial_spinal_lib.c new file mode 100644 index 000000000..fc289c78c --- /dev/null +++ b/drivers/tty/serial/serial_spinal_lib.c @@ -0,0 +1,729 @@ +/* + * Copyright (C) 2023 Efinix Inc. All rights reserved. + * + * SPDX-License-Identifier: GPL-3.0 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "spinal_lib_uart" +#define SERIAL_SPINAL_LIB_UART_MAXPORTS 4 + +/* Register Offset */ +#define SPINAL_LIB_UART_DATA 0x00 +#define SPINAL_LIB_UART_STATUS 0x04 +#define SPINAL_LIB_UART_CLOCK_DIVIDER 0x08 +#define SPINAL_LIB_UART_FRAME_CONFIG 0x0C +#define SPINAL_LIB_UART_FRAME_FLAGS 0x10 + +#define SPINAL_LIB_UART_STATUS_TX_BUSY 0x8000 +#define SPINAL_LIB_UART_FRAME_CONFIG_STOP1 (0 << 16) +#define SPINAL_LIB_UART_FRAME_CONFIG_STOP2 (1 << 16) +#define SPINAL_LIB_UART_FRAME_CONFIG_ODD (1 << 9) +#define SPINAL_LIB_UART_FRAME_CONFIG_EVEN (1 << 8) +#define SPINAL_LIB_UART_MISC_READ_OVERFLOW (1 << 1) +#define SPINAL_LIB_UART_MISC_BREAK_ACTIVE (1 << 8) +#define SPINAL_LIB_UART_MISC_BREAK_DETECTED (1 << 9) +#define SPINAL_LIB_UART_MISC_BREAK_ENABLE (1 << 10) +#define SPINAL_LIB_UART_MISC_BREAK_DISABLE (1 << 11) + +#define SPINAL_LIB_UART_CONTROL_TRDY_MSK (1 << 0) +#define SPINAL_LIB_UART_CONTROL_RRDY_MSK (1 << 1) +#define SPINAL_LIB_UART_STATUS_TRDY_MSK (1 << 8) +#define SPINAL_LIB_UART_STATUS_RRDY_MSK (1 << 9) + +/* Depth of TX/RX FIFO (in bytes) */ +#define SPINAL_LIB_UART_FIFO_DEPTH 128 + +#undef pr_fmt +#define pr_fmt(fmt) "%s : " fmt, __func__ + +/* + * Local per-uart structure. + */ +struct spinal_lib_uart { + struct device *dev; + struct uart_port port; + struct timer_list tmr; + u32 break_on_rx; + u32 imr; +}; + +static struct spinal_lib_uart *spinal_lib_uart_ports[SERIAL_SPINAL_LIB_UART_MAXPORTS]; + +static u32 spinal_lib_uart_tx_availability(struct uart_port *port) +{ + return (readl(port->membase + SPINAL_LIB_UART_STATUS) >> 16) & 0xFF; +} +static u32 spinal_lib_uart_rx_occupancy(struct uart_port *port) +{ + return (readl(port->membase + SPINAL_LIB_UART_STATUS) >> 24) & 0xFF; +} +static void spinal_lib_uart_tx(struct uart_port *port, char ch) +{ + writel(ch, port->membase + SPINAL_LIB_UART_DATA); +} + +static inline void spinal_lib_uart_writel(struct uart_port *port, u32 data, u32 reg) +{ + writel(data, port->membase + reg); +} + +static inline u32 spinal_lib_uart_readl(struct uart_port *port, u32 reg) +{ + return readl(port->membase + reg); +} + +static void spinal_lib_uart_update_ctrl_reg(struct spinal_lib_uart *pp) +{ + unsigned short imr = pp->imr; + + /* + * If the device doesn't have an irq, ensure that the irq bits are + * masked out to keep the irq line inactive. + */ + + spinal_lib_uart_writel(&pp->port, imr, SPINAL_LIB_UART_STATUS); +} + +static inline void spinal_lib_uart_disable_tx_interrupt(struct spinal_lib_uart *pp) +{ + pp->imr &= ~SPINAL_LIB_UART_CONTROL_TRDY_MSK; + spinal_lib_uart_update_ctrl_reg(pp); +} + +static inline void spinal_lib_uart_disable_rx_interrupt(struct spinal_lib_uart *pp) +{ + pp->imr &= ~SPINAL_LIB_UART_CONTROL_RRDY_MSK; + spinal_lib_uart_update_ctrl_reg(pp); +} + +static inline void spinal_lib_uart_enable_tx_interrupt(struct spinal_lib_uart *pp) +{ + pp->imr |= SPINAL_LIB_UART_CONTROL_TRDY_MSK; + spinal_lib_uart_update_ctrl_reg(pp); +} + +static inline void spinal_lib_uart_enable_rx_interrupt(struct spinal_lib_uart *pp) +{ + pp->imr |= SPINAL_LIB_UART_CONTROL_RRDY_MSK; + spinal_lib_uart_update_ctrl_reg(pp); +} + +static unsigned int spinal_lib_uart_tx_empty(struct uart_port *port) +{ + return (spinal_lib_uart_tx_availability(port) & SPINAL_LIB_UART_STATUS_TX_BUSY) ? 0 : TIOCSER_TEMT; +} + +static unsigned int spinal_lib_uart_get_mctrl(struct uart_port *port) +{ + return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; +} + +static void spinal_lib_uart_set_mctrl(struct uart_port *port, unsigned int sigs) +{ +} + +static void spinal_lib_uart_stop_tx(struct uart_port *port) +{ + struct spinal_lib_uart *pp = container_of(port, struct spinal_lib_uart, port); + + spinal_lib_uart_disable_tx_interrupt(pp); +} + +static void spinal_lib_uart_stop_rx(struct uart_port *port) +{ + struct spinal_lib_uart *pp = container_of(port, struct spinal_lib_uart, port); + + spinal_lib_uart_disable_rx_interrupt(pp); +} + +static void spinal_lib_uart_break_ctl(struct uart_port *port, int break_state) +{ + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + writel(break_state != 0 ? SPINAL_LIB_UART_MISC_BREAK_ENABLE : SPINAL_LIB_UART_MISC_BREAK_DISABLE, port->membase + SPINAL_LIB_UART_FRAME_FLAGS); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void spinal_lib_uart_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + unsigned long flags; + unsigned int baud, baudclk; + unsigned int config = 0; + + baud = uart_get_baud_rate(port, termios, old, 0, 4000000); + + switch(termios->c_cflag & CSIZE) { + case CS5: config |= 4; break; + case CS6: config |= 5; break; + case CS7: config |= 6; break; + case CS8: config |= 7; break; + } + + baudclk = port->uartclk / (baud * (config + 1)) - 1; + + if (termios->c_cflag & CSTOPB) + config |= SPINAL_LIB_UART_FRAME_CONFIG_STOP2; + else + config |= SPINAL_LIB_UART_FRAME_CONFIG_STOP1; + + if (termios->c_cflag & PARODD) + config |= SPINAL_LIB_UART_FRAME_CONFIG_ODD; + + if (termios->c_cflag & PARENB) + config |= SPINAL_LIB_UART_FRAME_CONFIG_EVEN; + + spinal_lib_uart_writel(port, baudclk, SPINAL_LIB_UART_CLOCK_DIVIDER); + spinal_lib_uart_writel(port, config, SPINAL_LIB_UART_FRAME_CONFIG); + + spin_lock_irqsave(&port->lock, flags); + uart_update_timeout(port, termios->c_cflag, baud); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void spinal_lib_uart_rx_chars(struct spinal_lib_uart *pp) +{ + struct uart_port *port = &pp->port; + unsigned char ch, flag; + u32 count; + u32 flags; + + flags = spinal_lib_uart_readl(port, SPINAL_LIB_UART_FRAME_FLAGS); + if(!pp->break_on_rx){ + if(flags & SPINAL_LIB_UART_MISC_BREAK_DETECTED){ + spinal_lib_uart_writel(port, + SPINAL_LIB_UART_MISC_BREAK_DETECTED, + SPINAL_LIB_UART_FRAME_FLAGS); + pp->break_on_rx = 1; + port->icount.brk++; + uart_handle_break(port); + } + } else { + if((flags & SPINAL_LIB_UART_MISC_BREAK_ACTIVE) == 0){ + pp->break_on_rx = 0; + } + } + + if(!pp->break_on_rx) while((count = spinal_lib_uart_rx_occupancy(port))){ + while(count--){ + ch = spinal_lib_uart_readl(port, SPINAL_LIB_UART_DATA); + flag = TTY_NORMAL; + port->icount.rx++; + if (uart_handle_sysrq_char(port, ch)) + continue; + uart_insert_char(port, 0, 0, ch, flag); + } + } + + spin_unlock(&port->lock); + tty_flip_buffer_push(&port->state->port); + spin_lock(&port->lock); +} + +static void spinal_lib_uart_tx_chars(struct spinal_lib_uart *pp) +{ + struct uart_port *port = &pp->port; + struct circ_buf *xmit = &port->state->xmit; + unsigned int numbytes; + + numbytes = SPINAL_LIB_UART_FIFO_DEPTH; + + if (port->x_char && spinal_lib_uart_tx_availability(port)) { + /* Send special char - probably flow control */ + spinal_lib_uart_tx(port, port->x_char); + port->x_char = 0; + port->icount.tx++; + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + spinal_lib_uart_stop_tx(port); + return; + } + + while (numbytes && !uart_circ_empty(xmit) && spinal_lib_uart_tx_availability(port)) { + spinal_lib_uart_tx(port, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + numbytes--; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + spinal_lib_uart_stop_tx(port); + else + spinal_lib_uart_enable_tx_interrupt(pp); +} + +static void spinal_lib_uart_start_tx(struct uart_port *port) +{ + struct spinal_lib_uart *pp = container_of(port, struct spinal_lib_uart, port); + + spinal_lib_uart_enable_tx_interrupt(pp); +} + +static irqreturn_t spinal_lib_uart_interrupt(int irq, void *data) +{ + struct uart_port *port = data; + struct spinal_lib_uart *pp = container_of(port, struct spinal_lib_uart, port); + unsigned int isr; + + isr = spinal_lib_uart_readl(port, SPINAL_LIB_UART_STATUS); + + /* Disable Tx/Rx interrupt */ + spinal_lib_uart_disable_tx_interrupt(pp); + spinal_lib_uart_disable_rx_interrupt(pp); + + spin_lock(&port->lock); + if (isr & SPINAL_LIB_UART_STATUS_RRDY_MSK) + spinal_lib_uart_rx_chars(pp); + + if (isr & SPINAL_LIB_UART_STATUS_TRDY_MSK) + spinal_lib_uart_tx_chars(pp); + + spin_unlock(&port->lock); + + /* Enable Rx interrupt */ + spinal_lib_uart_enable_rx_interrupt(pp); + + return IRQ_RETVAL(isr); +} + +static void spinal_lib_uart_timer(struct timer_list *t) +{ + struct spinal_lib_uart *pp = from_timer(pp, t, tmr); + struct uart_port *port = &pp->port; + + spinal_lib_uart_interrupt(0, port); + mod_timer(&pp->tmr, jiffies + uart_poll_timeout(port)); +} + +static void spinal_lib_uart_config_port(struct uart_port *port, int flags) +{ + port->type = PORT_SPINAL_LIB; + + if (flags & UART_CONFIG_TYPE) + port->type = PORT_SPINAL_LIB; +} + +static int spinal_lib_uart_startup(struct uart_port *port) +{ + struct spinal_lib_uart *pp = container_of(port, struct spinal_lib_uart, port); + unsigned long flags; + + if (!port->irq) { + timer_setup(&pp->tmr, spinal_lib_uart_timer, 0); + mod_timer(&pp->tmr, jiffies + uart_poll_timeout(port)); + } + + spin_lock_irqsave(&port->lock, flags); + + /* Enable RX interrupts now */ + spinal_lib_uart_enable_rx_interrupt(pp); + + spin_unlock_irqrestore(&port->lock, flags); + + return 0; +} + +static void spinal_lib_uart_shutdown(struct uart_port *port) +{ + struct spinal_lib_uart *pp = container_of(port, struct spinal_lib_uart, port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + /* Disable all interrupts now */ + spinal_lib_uart_disable_tx_interrupt(pp); + spinal_lib_uart_disable_rx_interrupt(pp); + + spin_unlock_irqrestore(&port->lock, flags); + + if (!port->irq) + del_timer_sync(&pp->tmr); +} + +static const char *spinal_lib_uart_type(struct uart_port *port) +{ + return (port->type == PORT_SPINAL_LIB) ? "Spinal lib UART" : NULL; +} + +static int spinal_lib_uart_request_port(struct uart_port *port) +{ + /* UARTs always present */ + return 0; +} + +static void spinal_lib_uart_release_port(struct uart_port *port) +{ + /* Nothing to release... */ +} + +static int spinal_lib_uart_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + if ((ser->type != PORT_UNKNOWN) && (ser->type != PORT_SPINAL_LIB)) + return -EINVAL; + return 0; +} + +#ifdef CONFIG_CONSOLE_POLL +static int spinal_lib_uart_poll_get_char(struct uart_port *port) +{ + while (!spinal_lib_uart_rx_occupancy(port)) + return NO_POLL_CHAR; + + return spinal_lib_uart_readl(port, SPINAL_LIB_UART_DATA); +} + +static void spinal_lib_uart_poll_put_char(struct uart_port *port, unsigned char c) +{ + while (!spinal_lib_uart_tx_availability(port)) + cpu_relax(); + + spinal_lib_uart_writel(port, c, SPINAL_LIB_UART_DATA); +} +#endif + +/* + * Define the basic serial functions we support. + */ +static const struct uart_ops spinal_lib_uart_ops = { + .tx_empty = spinal_lib_uart_tx_empty, + .get_mctrl = spinal_lib_uart_get_mctrl, + .set_mctrl = spinal_lib_uart_set_mctrl, + .start_tx = spinal_lib_uart_start_tx, + .stop_tx = spinal_lib_uart_stop_tx, + .stop_rx = spinal_lib_uart_stop_rx, + .break_ctl = spinal_lib_uart_break_ctl, + .startup = spinal_lib_uart_startup, + .shutdown = spinal_lib_uart_shutdown, + .set_termios = spinal_lib_uart_set_termios, + .type = spinal_lib_uart_type, + .request_port = spinal_lib_uart_request_port, + .release_port = spinal_lib_uart_release_port, + .config_port = spinal_lib_uart_config_port, + .verify_port = spinal_lib_uart_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = spinal_lib_uart_poll_get_char, + .poll_put_char = spinal_lib_uart_poll_put_char, +#endif +}; + +#ifdef CONFIG_SERIAL_SPINAL_LIB_UART_CONSOLE + +static void spinal_lib_uart_console_putc(struct uart_port *port, int c) +{ + while (!spinal_lib_uart_tx_availability(port)) + cpu_relax(); + + spinal_lib_uart_writel(port, c, SPINAL_LIB_UART_DATA); +} + +static void spinal_lib_uart_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct spinal_lib_uart *pp = spinal_lib_uart_ports[co->index]; + unsigned long flags; + int locked = 1; + + if (!pp) + return; + + local_irq_save(flags); + if (pp->port.sysrq) + locked = 0; + else if (oops_in_progress) + locked = spin_trylock(&pp->port.lock); + else + spin_lock(&pp->port.lock); + + uart_console_write(&pp->port, s, count, spinal_lib_uart_console_putc); + + if (locked) + spin_unlock(&pp->port.lock); + local_irq_restore(flags); +} + +static int __init spinal_lib_uart_console_setup(struct console *co, char *options) +{ + struct spinal_lib_uart *pp; + int baud = CONFIG_SERIAL_SPINAL_LIB_UART_BAUDRATE; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index < 0 || co->index >= SERIAL_SPINAL_LIB_UART_MAXPORTS) + return -ENODEV; + + pp = spinal_lib_uart_ports[co->index]; + if (!pp) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&pp->port, co, baud, parity, bits, flow); +} + +static struct uart_driver spinal_lib_uart_driver; + +static struct console spinal_lib_uart_console = { + .name = "ttySL", + .write = spinal_lib_uart_console_write, + .device = uart_console_device, + .setup = spinal_lib_uart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &spinal_lib_uart_driver, +}; + +static int __init spinal_lib_uart_console_init(void) +{ + register_console(&spinal_lib_uart_console); + return 0; +} + +console_initcall(spinal_lib_uart_console_init); + +#define SPINAL_LIB_UART_CONSOLE (&spinal_lib_uart_console) + +static void spinal_lib_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, spinal_lib_uart_console_putc); +} + +static int __init spinal_lib_uart_earlycon_setup(struct earlycon_device *dev, + const char *options) +{ + struct uart_port *port = &dev->port; + unsigned int baudclk; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + unsigned int config = 0; + + if (!port->membase) + return -ENODEV; + + if (options) + uart_parse_options(options, &dev->baud, &parity, &bits, &flow); + + switch (parity) { + case 'e': config |= SPINAL_LIB_UART_FRAME_CONFIG_EVEN; break; + case 'o': config |= SPINAL_LIB_UART_FRAME_CONFIG_ODD; break; + default: break; + } + + config |= (bits - 1); + + if (dev->baud) { + baudclk = port->uartclk / (dev->baud * bits) - 1; + + spinal_lib_uart_writel(port, baudclk, SPINAL_LIB_UART_CLOCK_DIVIDER); + spinal_lib_uart_writel(port, config, SPINAL_LIB_UART_FRAME_CONFIG); + } + + dev->con->write = spinal_lib_uart_earlycon_write; + + return 0; +} + +OF_EARLYCON_DECLARE(sluart, "spinal-lib,uart-2.0", spinal_lib_uart_earlycon_setup); + +#else + +#define SPINAL_LIB_UART_CONSOLE NULL + +#endif /* CONFIG_SERIAL_SPINAL_LIB_UART_CONSOLE */ + +/* + * Define the spinal_lib_uart UART driver structure. + */ +static struct uart_driver spinal_lib_uart_driver = { + .owner = THIS_MODULE, + .driver_name = DRV_NAME, + .dev_name = "ttySL", + .nr = SERIAL_SPINAL_LIB_UART_MAXPORTS, + .cons = SPINAL_LIB_UART_CONSOLE, +}; + +static int spinal_lib_uart_probe(struct platform_device *pdev) +{ + struct resource *res_mem; + struct uart_port *port; + struct device *dev = &pdev->dev; + int ret; + struct spinal_lib_uart *pp; + struct clk *clk; + int irq; + u32 hz; + + if (pdev->dev.of_node) { + pdev->id = of_alias_get_id(pdev->dev.of_node, "serial"); + if (pdev->id < 0) + pdev->id = of_alias_get_id(pdev->dev.of_node, "uart"); + } + + if (pdev->id < 0 || pdev->id >= SERIAL_SPINAL_LIB_UART_MAXPORTS) { + return -ENODEV; + } + + pp = devm_kzalloc(&pdev->dev, sizeof(*pp), GFP_KERNEL); + + if (!pp) { + dev_err(dev, "out of memory\n"); + return -ENOMEM; + } + + spinal_lib_uart_ports[pdev->id] = pp; + pp->dev = &pdev->dev; + pp->break_on_rx = 0; + + port = &pp->port; + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (res_mem) + port->mapbase = res_mem->start; + else + return -ENODEV; + + port->membase = devm_ioremap_resource(&pdev->dev, res_mem); + if (IS_ERR(port->membase)) + return PTR_ERR(port->membase); + + irq = platform_get_irq(pdev, 0); + port->irq = irq; + + ret = devm_request_irq(pp->dev, port->irq, spinal_lib_uart_interrupt, + port->irqflags, DRV_NAME, port); + if (ret) { + port->irq = 0; + pr_err(DRV_NAME ": unable to attach Spinal Lib UART %d " + "interrupt vector=%d\n", port->line, port->irq); + } + + clk = devm_clk_get(&pdev->dev, NULL); + + if (IS_ERR(clk)) { + dev_info(&pdev->dev, "No peripheral clock\n"); + goto exit; + } + + hz = clk_get_rate(clk); + if(!hz){ + dev_info(&pdev->dev, "Bad frequancy\n"); + goto exit; + } + + port->uartclk = hz; + port->fifosize = SPINAL_LIB_UART_FIFO_DEPTH; + port->regshift = 0; + port->line = pdev->id; + port->type = PORT_SPINAL_LIB; + port->iotype = SERIAL_IO_MEM; + port->ops = &spinal_lib_uart_ops; + port->flags = UPF_BOOT_AUTOCONF; + port->dev = &pdev->dev; + port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_SPINAL_LIB_UART_CONSOLE); + + platform_set_drvdata(pdev, port); + + ret = uart_add_one_port(&spinal_lib_uart_driver, port); + if (ret) { + spinal_lib_uart_ports[pdev->id]->port.membase = NULL; + return ret; + } + + return 0; + +exit: + return -EINVAL; + +} + +static int spinal_lib_uart_remove(struct platform_device *pdev) +{ + struct uart_port *port = platform_get_drvdata(pdev); + + if (port) { + uart_remove_one_port(&spinal_lib_uart_driver, port); + spinal_lib_uart_ports[pdev->id]->port.membase = NULL; + } + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id spinal_lib_uart_match[] = { + { .compatible = "spinal-lib,uart-2.0", }, + {}, +}; +MODULE_DEVICE_TABLE(of, spinal_lib_uart_match); +#endif /* CONFIG_OF */ + +static struct platform_driver spinal_lib_uart_platform_driver = { + .probe = spinal_lib_uart_probe, + .remove = spinal_lib_uart_remove, + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(spinal_lib_uart_match), + }, +}; + +static int __init spinal_lib_uart_init(void) +{ + int ret; + ret = uart_register_driver(&spinal_lib_uart_driver); + if (ret < 0) { + pr_err("Could not register %s driver\n", + spinal_lib_uart_driver.driver_name); + return ret; + } + + ret = platform_driver_register(&spinal_lib_uart_platform_driver); + if (ret < 0) { + pr_err("Uart platform driver register failed, e = %d\n", ret); + uart_unregister_driver(&spinal_lib_uart_driver); + return ret; + } + return 0; +} + +static void __exit spinal_lib_uart_exit(void) +{ + platform_driver_unregister(&spinal_lib_uart_platform_driver); + uart_unregister_driver(&spinal_lib_uart_driver); +} + +module_init(spinal_lib_uart_init); +module_exit(spinal_lib_uart_exit); + +MODULE_DESCRIPTION("Spinal lib UART driver"); +MODULE_AUTHOR("Charles Papon "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index 851b982f8..58db9c2c2 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -279,4 +279,7 @@ /* Freescale LINFlexD UART */ #define PORT_LINFLEXUART 122 +/* Spinal lib UART */ +#define PORT_SPINAL_LIB 123 + #endif /* _UAPILINUX_SERIAL_CORE_H */