From 758dfe3ddcae59b337d4109331f9be39da53b0ed Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Tue, 18 May 2021 07:21:17 +0200 Subject: MAINTAINERS: TTY LAYER: add some ./include/linux/ header files An early prototypical automated code analysis of headers and the existing MAINTAINERS sections identified some header files in ./include/linux/ to be probably included into the TTY LAYER section. I further checked those suggestions by this analysis and identified a subset of files that I am rather certain to belong to the TTY LAYER. Add these ./include/linux/ header files to TTY LAYER in MAINTAINERS. The patterns include/linux/tty*.h and include/linux/vt_*.h currently cover: include/linux/tty.h include/linux/tty_driver.h include/linux/tty_flip.h include/linux/tty_ldisc.h include/linux/vt_buffer.h include/linux/vt_kern.h Signed-off-by: Lukas Bulwahn Link: https://lore.kernel.org/r/20210518052117.14819-2-lukas.bulwahn@gmail.com Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index bd7aff0c120f..bf9e97cf71bc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18546,9 +18546,13 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git F: Documentation/driver-api/serial/ F: drivers/tty/ F: drivers/tty/serial/serial_core.c +F: include/linux/selection.h F: include/linux/serial.h F: include/linux/serial_core.h -F: include/linux/tty.h +F: include/linux/sysrq.h +F: include/linux/tty*.h +F: include/linux/vt.h +F: include/linux/vt_*.h F: include/uapi/linux/serial.h F: include/uapi/linux/serial_core.h F: include/uapi/linux/tty.h -- cgit v1.2.3 From 18aaa97eee7ddb770963d84aeaf5e95a8f8af088 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Fri, 18 Jun 2021 08:15:16 +0200 Subject: MAINTAINERS: add me back as mxser maintainer I was given a few cards from MOXA (thanks a lot). Provided I can now test changes, I cleaned up the driver a bit and can continue maintaining it. Signed-off-by: Jiri Slaby Link: https://lore.kernel.org/r/20210618061516.662-71-jslaby@suse.cz Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 54053c71785c..25d7d864cfbd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12389,7 +12389,8 @@ F: drivers/media/pci/meye/ F: include/uapi/linux/meye.h MOXA SMARTIO/INDUSTIO/INTELLIO SERIAL CARD -S: Orphan +M: Jiri Slaby +S: Maintained F: Documentation/driver-api/serial/moxa-smartio.rst F: drivers/tty/mxser.* -- cgit v1.2.3 From b61c8bf4694b5115766849378dcb8787ff54e65e Mon Sep 17 00:00:00 2001 From: Jason Li Date: Tue, 15 Jun 2021 09:57:48 -0700 Subject: tty: serial: Add UART driver for Cortina-Access platform This driver supports Cortina Access UART IP integrated in most all CAXXXX line of SoCs. Earlycom is also supported Signed-off-by: Jason Li Link: https://lore.kernel.org/r/20210615165750.31261-1-alex.nemirovsky@cortina-access.com Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 5 + drivers/tty/serial/Kconfig | 19 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/serial_cortina-access.c | 798 +++++++++++++++++++++++++++++ 4 files changed, 823 insertions(+) create mode 100644 drivers/tty/serial/serial_cortina-access.c (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 25d7d864cfbd..f20502df69b8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4680,6 +4680,11 @@ S: Maintained W: http://www.fi.muni.cz/~kas/cosa/ F: drivers/net/wan/cosa* +CORTINA-ACCESS SERIAL CONSOLE DRIVER +M: Jason Li +S: Supported +F: drivers/tty/serial/serial_cortina-access.c + COUNTER SUBSYSTEM M: William Breathitt Gray L: linux-iio@vger.kernel.org diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 24282ad99d85..2fa39cd78c56 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1561,6 +1561,25 @@ config SERIAL_LITEUART_CONSOLE and warnings and which allows logins in single user mode). Otherwise, say 'N'. +config SERIAL_CORTINA_ACCESS + tristate "Cortina-Access serial port support" + select SERIAL_CORE + help + This driver is for Cortina-Access SoC's UART. If you have a machine + based on the Cortina-Access SoC and wish to use the serial port, + say 'Y' here. Otherwise, say 'N'. + +config SERIAL_CORTINA_ACCESS_CONSOLE + bool "Console on Cortina-ACCESS serial port" + depends on SERIAL_CORTINA_ACCESS=y + select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON + help + Say 'Y' here if you wish to use Cortina-Access UART as the system + console. (the system console is the device which receives all kernel + messages and warnings and which allows logins in single user mode) + /dev/ttyS* is default device node. + endmenu config SERIAL_MCTRL_GPIO diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 7da0856cd198..f3f531a48854 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_CORTINA_ACCESS) += serial_cortina-access.o # GPIOLIB helpers for modem control lines obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o diff --git a/drivers/tty/serial/serial_cortina-access.c b/drivers/tty/serial/serial_cortina-access.c new file mode 100644 index 000000000000..b4b9362bb4e6 --- /dev/null +++ b/drivers/tty/serial/serial_cortina-access.c @@ -0,0 +1,798 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * UART driver for Cortina-Access Soc platform + * Copyright (C) 2021 Cortina-Access Inc. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*************************************** + * UART Related registers + ****************************************/ +/* register definitions */ +#define CFG 0x00 +#define FC 0x04 +#define RX_SAMPLE 0x08 +#define RT_TUNE 0x0C +#define TX_DAT 0x10 +#define RX_DAT 0x14 +#define INFO 0x18 +#define IE 0x1C +#define INT 0x24 +#define STATUS 0x2C + +/* CFG */ +#define CFG_STOP_2BIT BIT(2) +#define CFG_PARITY_EVEN BIT(3) +#define CFG_PARITY_EN BIT(4) +#define CFG_TX_EN BIT(5) +#define CFG_RX_EN BIT(6) +#define CFG_UART_EN BIT(7) +#define CFG_BAUD_SART_SHIFT 8 + +/* INFO */ +#define INFO_TX_EMPTY BIT(3) +#define INFO_TX_FULL BIT(2) +#define INFO_RX_EMPTY BIT(1) +#define INFO_RX_FULL BIT(0) + +/* Interrupt */ +#define RX_BREAK BIT(7) +#define RX_FIFO_NONEMPTYE BIT(6) +#define TX_FIFO_EMPTYE BIT(5) +#define RX_FIFO_UNDERRUNE BIT(4) +#define RX_FIFO_OVERRUNE BIT(3) +#define RX_PARITY_ERRE BIT(2) +#define RX_STOP_ERRE BIT(1) +#define TX_FIFO_OVERRUNE BIT(0) + +#define TX_TIMEOUT 5000 +#define UART_NR 4 +#define CA_UART_NAME_LEN 32 +struct cortina_uart_port { + struct uart_port uart; + char name[CA_UART_NAME_LEN]; + char has_bi; + unsigned int may_wakeup; +}; + +static struct cortina_uart_port *cortina_uart_ports; + +static irqreturn_t cortina_uart_interrupt(int irq, void *dev_id); + +/* Return uart_port pointer base on index */ +struct cortina_uart_port *cortina_uart_get_port(unsigned int index) +{ + struct cortina_uart_port *pca_port = cortina_uart_ports; + + if (index >= UART_NR) { + /* return 1st element if invalid index */ + index = 0; + } + + pca_port += index; + + return pca_port; +} + +/* uart_ops functions */ +static unsigned int cortina_uart_tx_empty(struct uart_port *port) +{ + /* Return 0 on FIXO condition, TIOCSER_TEMT otherwise */ + return (readl(port->membase + INFO) & INFO_TX_EMPTY) ? TIOCSER_TEMT : 0; +} + +static void cortina_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +/* + * Even if we do not support configuring the modem control lines, this + * function must be proided to the serial core. + * port->ops->set_mctrl() be called in uart_configure_port() + */ +} + +static unsigned int cortina_uart_get_mctrl(struct uart_port *port) +{ + /* Unimplemented signals asserted, per Documentation/serial/driver */ + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; +} + +static void cortina_uart_stop_tx(struct uart_port *port) +{ + /* Turn off Tx interrupts. The port lock is held at this point */ + unsigned int reg_v; + + reg_v = readl(port->membase + IE); + writel(reg_v & ~TX_FIFO_EMPTYE, port->membase + IE); +} + +static inline void cortina_transmit_buffer(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + cortina_uart_stop_tx(port); + return; + } + + do { + /* send xmit->buf[xmit->tail] out the port here */ + writel(xmit->buf[xmit->tail], port->membase + TX_DAT); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if ((readl(port->membase + INFO) & INFO_TX_FULL)) + break; + } while (!uart_circ_empty(xmit)); + + if (uart_circ_empty(xmit)) + cortina_uart_stop_tx(port); +} + +static void cortina_uart_start_tx(struct uart_port *port) +{ + /* Turn on Tx interrupts. The port lock is held at this point */ + unsigned int reg_v; + + reg_v = readl(port->membase + IE); + writel((reg_v | TX_FIFO_EMPTYE), port->membase + IE); + + reg_v = readl(port->membase + CFG); + writel(reg_v | CFG_TX_EN, port->membase + CFG); + + if (readl(port->membase + INFO) & INFO_TX_EMPTY) + cortina_transmit_buffer(port); +} + +static void cortina_uart_stop_rx(struct uart_port *port) +{ + /* Turn off Rx interrupts. The port lock is held at this point */ + unsigned int reg_v; + + reg_v = readl(port->membase + IE); + writel(reg_v & ~RX_FIFO_NONEMPTYE, port->membase + IE); +} + +static void cortina_uart_enable_ms(struct uart_port *port) +{ + /* Nope, you really can't hope to attach a modem to this */ +} + +static int cortina_uart_startup(struct uart_port *port) +{ + unsigned int reg_v; + int retval; + unsigned long flags; + + /* Disable interrupt */ + writel(0, port->membase + IE); + + retval = + request_irq(port->irq, cortina_uart_interrupt, 0, "cortina_uart", + port); + if (retval) + return retval; + + spin_lock_irqsave(&port->lock, flags); + + reg_v = readl(port->membase + CFG); + reg_v |= (CFG_UART_EN | CFG_TX_EN | CFG_RX_EN | 0x3 /* 8-bits data */); + writel(reg_v, port->membase + CFG); + reg_v = readl(port->membase + IE); + writel(reg_v | RX_FIFO_NONEMPTYE | TX_FIFO_EMPTYE, port->membase + IE); + + spin_unlock_irqrestore(&port->lock, flags); + return 0; +} + +static void cortina_uart_shutdown(struct uart_port *port) +{ + cortina_uart_stop_tx(port); + cortina_uart_stop_rx(port); + free_irq(port->irq, port); +} + +static void cortina_uart_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + unsigned long flags; + int baud; + unsigned int reg_v, sample_freq = 0; + + baud = uart_get_baud_rate(port, termios, old, 0, 230400); + reg_v = readl(port->membase + CFG); + /* mask off the baud settings */ + reg_v &= 0xff; + reg_v |= (port->uartclk / baud) << CFG_BAUD_SART_SHIFT; + + /* Sampling rate should be half of baud count */ + sample_freq = (reg_v >> CFG_BAUD_SART_SHIFT) / 2; + + /* See include/uapi/asm-generic/termbits.h for CSIZE definition */ + /* mask off the data width */ + reg_v &= 0xfffffffc; + switch (termios->c_cflag & CSIZE) { + case CS5: + reg_v |= 0x0; + break; + case CS6: + reg_v |= 0x1; + break; + case CS7: + reg_v |= 0x2; + break; + case CS8: + default: + reg_v |= 0x3; + break; + } + + /* mask off Stop bits */ + reg_v &= ~(CFG_STOP_2BIT); + if (termios->c_cflag & CSTOPB) + reg_v |= CFG_STOP_2BIT; + + /* Parity */ + reg_v &= ~(CFG_PARITY_EN); + reg_v |= CFG_PARITY_EVEN; + if (termios->c_cflag & PARENB) { + reg_v |= CFG_PARITY_EN; + if (termios->c_cflag & PARODD) + reg_v &= ~(CFG_PARITY_EVEN); + } + + spin_lock_irqsave(&port->lock, flags); + writel(reg_v, port->membase + CFG); + writel(sample_freq, port->membase + RX_SAMPLE); + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *cortina_uart_type(struct uart_port *port) +{ + return container_of(port, struct cortina_uart_port, uart)->name; +} + +static void cortina_uart_config_port(struct uart_port *port, int flags) +{ + /* + * Driver core for serial ports forces a non-zero value for port type. + * Write an arbitrary value here to accommodate the serial core driver, + * as ID part of UAPI is redundant. + */ + port->type = 1; +} + +static int cortina_uart_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + if (ser->type != PORT_UNKNOWN && ser->type != 1) + return -EINVAL; + return 0; +} + +static void cortina_access_power(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + unsigned int reg_v; + + /* Read Config register */ + reg_v = readl(port->membase + CFG); + switch (state) { + case UART_PM_STATE_ON: + reg_v |= CFG_UART_EN; + break; + case UART_PM_STATE_OFF: + reg_v &= ~CFG_UART_EN; + break; + default: + pr_err("cortina-access serial: Unknown PM state %d\n", state); + } + writel(reg_v, port->membase + CFG); +} + +#ifdef CONFIG_CONSOLE_POLL +static int cortina_poll_get_char(struct uart_port *port) +{ + unsigned int rx; + + if (readl(port->membase + INFO) & INFO_RX_EMPTY) + return NO_POLL_CHAR; + + rx = readl(port->membase + RX_DAT); + + return rx; +} + +static void cortina_poll_put_char(struct uart_port *port, unsigned char c) +{ + unsigned long time_out; + + time_out = jiffies + usecs_to_jiffies(TX_TIMEOUT); + + while (time_before(jiffies, time_out) && + (readl(port->membase + INFO) & INFO_TX_FULL)) + cpu_relax(); + + /* Give up if FIFO stuck! */ + if ((readl(port->membase + INFO) & INFO_TX_FULL)) + return; + + writel(c, port->membase + TX_DAT); +} + +#endif + +static const struct uart_ops cortina_uart_ops = { + .tx_empty = cortina_uart_tx_empty, + .set_mctrl = cortina_uart_set_mctrl, + .get_mctrl = cortina_uart_get_mctrl, + .stop_tx = cortina_uart_stop_tx, + .start_tx = cortina_uart_start_tx, + .stop_rx = cortina_uart_stop_rx, + .enable_ms = cortina_uart_enable_ms, + .startup = cortina_uart_startup, + .shutdown = cortina_uart_shutdown, + .set_termios = cortina_uart_set_termios, + .type = cortina_uart_type, + .config_port = cortina_uart_config_port, + .verify_port = cortina_uart_verify_port, + .pm = cortina_access_power, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = cortina_poll_get_char, + .poll_put_char = cortina_poll_put_char, +#endif +}; + +static inline void cortina_uart_interrupt_rx_chars(struct uart_port *port, + unsigned long status) +{ + struct tty_port *ttyport = &port->state->port; + unsigned int ch; + unsigned int rx, flg; + struct cortina_uart_port *pca_port; + + rx = readl(port->membase + INFO); + if (INFO_RX_EMPTY & rx) + return; + + if (status & RX_FIFO_OVERRUNE) + port->icount.overrun++; + + pca_port = cortina_uart_get_port(port->line); + + /* Read the character while FIFO is not empty */ + do { + flg = TTY_NORMAL; + port->icount.rx++; + ch = readl(port->membase + RX_DAT); + if (status & RX_PARITY_ERRE) { + port->icount.parity++; + flg = TTY_PARITY; + } + + if (pca_port->has_bi) { + /* If BI supported ? */ + if (status & RX_BREAK) { + port->icount.brk++; + if (uart_handle_break(port)) + goto ignore; + } + } else { + /* Treat stop err as BI */ + if (status & RX_STOP_ERRE) { + port->icount.brk++; + if (uart_handle_break(port)) + goto ignore; + } + } + if (!(ch & 0x100)) /* RX char is not valid */ + goto ignore; + + if (uart_handle_sysrq_char(port, (unsigned char)ch)) + goto ignore; + + tty_insert_flip_char(ttyport, ch, flg); + ignore: + rx = readl(port->membase + INFO); + } while (!(INFO_RX_EMPTY & rx)); + + spin_unlock(&port->lock); + tty_flip_buffer_push(ttyport); + spin_lock(&port->lock); +} + +static inline void cortina_uart_interrupt_tx_chars(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + + /* Process out of band chars */ + if (port->x_char) { + /* Send next char */ + writel(port->x_char, port->membase + TX_DAT); + goto done; + } + + /* Nothing to do ? */ + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + cortina_uart_stop_tx(port); + goto done; + } + + cortina_transmit_buffer(port); + + /* Wake up */ + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + /* Maybe we're done after all */ + if (uart_circ_empty(xmit)) + cortina_uart_stop_tx(port); + + done: + return; +} + +irqreturn_t cortina_uart_interrupt(int irq, void *dev_id) +{ + struct uart_port *port = (struct uart_port *)dev_id; + unsigned int irq_status; + + spin_lock(&port->lock); + + /* Clear interrupt! */ + irq_status = readl(port->membase + INT); + writel(irq_status, port->membase + INT); + + /* Process any Rx chars first */ + cortina_uart_interrupt_rx_chars(port, irq_status); + /* Then use any Tx space */ + cortina_uart_interrupt_tx_chars(port); + + spin_unlock(&port->lock); + + return IRQ_HANDLED; +} + +#ifdef CONFIG_SERIAL_CORTINA_ACCESS_CONSOLE +void cortina_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_port *port; + struct cortina_uart_port *pca_port; + unsigned int i, previous; + unsigned long flags; + int locked; + + pca_port = cortina_uart_get_port(co->index); + port = &pca_port->uart; + + local_irq_save(flags); + if (port->sysrq) { + locked = 0; + } else if (oops_in_progress) { + locked = spin_trylock(&port->lock); + } else { + spin_lock(&port->lock); + locked = 1; + } + + /* Save current state */ + previous = readl(port->membase + IE); + /* Disable Tx interrupts so this all goes out in one go */ + cortina_uart_stop_tx(port); + + /* Write all the chars */ + for (i = 0; i < count; i++) { + /* Wait the TX buffer to be empty, which can't take forever */ + while (!(readl(port->membase + INFO) & INFO_TX_EMPTY)) + cpu_relax(); + + /* Send the char */ + writel(*s, port->membase + TX_DAT); + + /* CR/LF stuff */ + if (*s++ == '\n') { + /* Wait the TX buffer to be empty */ + while (!(readl(port->membase + INFO) & INFO_TX_EMPTY)) + cpu_relax(); + writel('\r', port->membase + TX_DAT); + } + } + + writel(previous, port->membase + IE); /* Put it all back */ + + if (locked) + spin_unlock(&port->lock); + local_irq_restore(flags); +} + +static int __init cortina_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + struct cortina_uart_port *pca_port; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index < 0 || co->index >= UART_NR) + return -ENODEV; + + pca_port = cortina_uart_get_port(co->index); + port = &pca_port->uart; + + /* This isn't going to do much, but it might change the baud rate. */ + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver cortina_uart_driver; + +static struct console cortina_console = { + .name = "ttyS", + .write = cortina_console_write, + .device = uart_console_device, + .setup = cortina_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, /* Only possible option. */ + .data = &cortina_uart_driver, +}; +#define CORTINA_CONSOLE (&cortina_console) + +/* Support EARLYCON */ +static void cortina_putc(struct uart_port *port, int c) +{ + unsigned int tmout; + + /* No jiffie at early boot stage! + * Wait up to 5ms for the character to be sent. + */ + tmout = TX_TIMEOUT; + while (--tmout) { + if (!(readl(port->membase + INFO) & INFO_TX_FULL)) + break; + udelay(1); + } + + /* Give up if FIFO stuck! */ + while ((readl(port->membase + INFO) & INFO_TX_FULL)) + return; + + /* Send the char */ + writel(c, port->membase + TX_DAT); +} + +static void cortina_early_write(struct console *con, const char *s, + unsigned int n) +{ + struct earlycon_device *dev = con->data; + + uart_console_write(&dev->port, s, n, cortina_putc); +} + +static int __init cortina_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + device->con->write = cortina_early_write; + return 0; +} + +EARLYCON_DECLARE(serial, cortina_early_console_setup); +OF_EARLYCON_DECLARE(serial, "cortina-access,serial", cortina_early_console_setup); +#else +#define CORTINA_CONSOLE NULL +#endif + +static struct uart_driver cortina_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "cortina-access_uart", + .dev_name = "ttyS", + .major = TTY_MAJOR, + .minor = 64, + .nr = UART_NR, + .cons = CORTINA_CONSOLE, +}; + +/* Match table for of_platform binding */ +static const struct of_device_id cortina_uart_of_match[] = { + {.compatible = "cortina-access,serial",}, + {} +}; +MODULE_DEVICE_TABLE(of, cortina_uart_of_match); + +static int serial_cortina_probe(struct platform_device *pdev) +{ + int ret; + void __iomem *base; + struct cortina_uart_port *port; + const struct of_device_id *match; + + /* assign DT node pointer */ + struct device_node *np = pdev->dev.of_node; + struct resource mem_resource; + u32 of_clock_frequency; + struct clk *pclk_info; + int uart_idx; + + /* search DT for a match */ + match = of_match_device(cortina_uart_of_match, &pdev->dev); + if (!match) + return -EINVAL; + + if (cortina_uart_ports == NULL) + cortina_uart_ports = kzalloc(UART_NR * sizeof(struct cortina_uart_port), + GFP_KERNEL); + + port = cortina_uart_ports; + for (uart_idx = 0; uart_idx < UART_NR; ++uart_idx) { + /* Find first empty slot */ + if (strlen(port->name) == 0) + break; + port++; + } + + if (uart_idx >= UART_NR) + return -EINVAL; + + snprintf(port->name, sizeof(port->name), "Cortina-Access UART%d", uart_idx); + + /* Retrieve HW base address */ + ret = of_address_to_resource(np, 0, &mem_resource); + if (ret) { + dev_warn(&pdev->dev, "invalid address %d\n", ret); + return ret; + } + + base = devm_ioremap(&pdev->dev, mem_resource.start, + resource_size(&mem_resource)); + if (!base) { + devm_kfree(&pdev->dev, port); + return -ENOMEM; + } + + /* assign reg base and irq from DT */ + port->uart.irq = irq_of_parse_and_map(np, 0); + port->uart.membase = base; + port->uart.mapbase = mem_resource.start; + port->uart.ops = &cortina_uart_ops; + port->uart.dev = &pdev->dev; + port->uart.line = uart_idx; + port->uart.has_sysrq = IS_ENABLED(CONFIG_SERIAL_CORTINA_ACCESS_CONSOLE); + + /* get clock-freqency tuple from DT and store value */ + if (of_property_read_u32(np, "clock-frequency", &of_clock_frequency)) { + /* If we are here, it means DT node did not contain + * clock-frequency tuple. Therefore, instead try to get + * clk rate through the clk driver that DT has stated + * we are consuming. + */ + pclk_info = clk_get(&pdev->dev, NULL); + if (IS_ERR(pclk_info)) { + dev_warn(&pdev->dev, + "clk or clock-frequency not defined\n"); + return PTR_ERR(pclk_info); + } + + clk_prepare_enable(pclk_info); + of_clock_frequency = clk_get_rate(pclk_info); + } + port->uart.uartclk = of_clock_frequency; + + if (of_property_read_bool(np, "wakeup-source")) + port->may_wakeup = true; + if (of_property_read_bool(np, "break-indicator")) + port->has_bi = true; + + port->uart.type = PORT_UNKNOWN; + + if (port->may_wakeup) + device_init_wakeup(&pdev->dev, true); + + ret = uart_add_one_port(&cortina_uart_driver, &port->uart); + if (ret) + return ret; + + platform_set_drvdata(pdev, port); + + return 0; +} + +static int serial_cortina_remove(struct platform_device *pdev) +{ + struct uart_port *port = platform_get_drvdata(pdev); + struct cortina_uart_port *pca_port; + + if (port) { + pca_port = cortina_uart_get_port(port->line); + memset(pca_port->name, 0, CA_UART_NAME_LEN); + uart_remove_one_port(&cortina_uart_driver, port); + } + + platform_set_drvdata(pdev, NULL); + return 0; +} + +#ifdef CONFIG_PM +static int serial_cortina_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct cortina_uart_port *p = + (struct cortina_uart_port *)pdev->dev.driver_data; + + uart_suspend_port(&cortina_uart_driver, &p->uart); + + return 0; +} + +static int serial_cortina_resume(struct platform_device *pdev) +{ + struct cortina_uart_port *p = + (struct cortina_uart_port *)pdev->dev.driver_data; + + uart_resume_port(&cortina_uart_driver, &p->uart); + + return 0; +} +#else +#define serial_cortina_suspend NULL +#define serial_cortina_resume NULL +#endif + +static struct platform_driver serial_cortina_driver = { + .probe = serial_cortina_probe, + .remove = serial_cortina_remove, +#ifdef CONFIG_PM + .suspend = serial_cortina_suspend, + .resume = serial_cortina_resume, +#endif + .driver = { + .name = "cortina-access_serial", + .owner = THIS_MODULE, + .of_match_table = cortina_uart_of_match, + }, +}; + +static int __init cortina_uart_init(void) +{ + int ret; + + ret = uart_register_driver(&cortina_uart_driver); + if (ret) + return ret; + + ret = platform_driver_register(&serial_cortina_driver); + if (ret) + uart_unregister_driver(&cortina_uart_driver); + + return ret; +} + +static void __exit cortina_uart_exit(void) +{ + platform_driver_unregister(&serial_cortina_driver); + uart_unregister_driver(&cortina_uart_driver); +} + +module_init(cortina_uart_init); +module_exit(cortina_uart_exit); + +MODULE_AUTHOR("Cortina-Access Inc."); +MODULE_DESCRIPTION(" Cortina-Access UART driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From cddd53e8aa4fc442e26a7a7be183593ce31453ca Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 24 Jun 2021 14:46:32 +0200 Subject: Revert "tty: serial: Add UART driver for Cortina-Access platform" This reverts commit b61c8bf4694b5115766849378dcb8787ff54e65e. It never made it to a public mailing list and still needs some work based on the review comments. So revert it for now. Reported-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/CAMuHMdXA9-ajoAza2JAW5879ECieMm1dbBbKHgJhDa7=3kWu3w@mail.gmail.com Cc: Jason Li Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 5 - drivers/tty/serial/Kconfig | 19 - drivers/tty/serial/Makefile | 1 - drivers/tty/serial/serial_cortina-access.c | 798 ----------------------------- 4 files changed, 823 deletions(-) delete mode 100644 drivers/tty/serial/serial_cortina-access.c (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index f20502df69b8..25d7d864cfbd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4680,11 +4680,6 @@ S: Maintained W: http://www.fi.muni.cz/~kas/cosa/ F: drivers/net/wan/cosa* -CORTINA-ACCESS SERIAL CONSOLE DRIVER -M: Jason Li -S: Supported -F: drivers/tty/serial/serial_cortina-access.c - COUNTER SUBSYSTEM M: William Breathitt Gray L: linux-iio@vger.kernel.org diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 2fa39cd78c56..24282ad99d85 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1561,25 +1561,6 @@ config SERIAL_LITEUART_CONSOLE and warnings and which allows logins in single user mode). Otherwise, say 'N'. -config SERIAL_CORTINA_ACCESS - tristate "Cortina-Access serial port support" - select SERIAL_CORE - help - This driver is for Cortina-Access SoC's UART. If you have a machine - based on the Cortina-Access SoC and wish to use the serial port, - say 'Y' here. Otherwise, say 'N'. - -config SERIAL_CORTINA_ACCESS_CONSOLE - bool "Console on Cortina-ACCESS serial port" - depends on SERIAL_CORTINA_ACCESS=y - select SERIAL_CORE_CONSOLE - select SERIAL_EARLYCON - help - Say 'Y' here if you wish to use Cortina-Access UART as the system - console. (the system console is the device which receives all kernel - messages and warnings and which allows logins in single user mode) - /dev/ttyS* is default device node. - endmenu config SERIAL_MCTRL_GPIO diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index f3f531a48854..7da0856cd198 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -87,7 +87,6 @@ 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_CORTINA_ACCESS) += serial_cortina-access.o # GPIOLIB helpers for modem control lines obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o diff --git a/drivers/tty/serial/serial_cortina-access.c b/drivers/tty/serial/serial_cortina-access.c deleted file mode 100644 index b4b9362bb4e6..000000000000 --- a/drivers/tty/serial/serial_cortina-access.c +++ /dev/null @@ -1,798 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * UART driver for Cortina-Access Soc platform - * Copyright (C) 2021 Cortina-Access Inc. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/*************************************** - * UART Related registers - ****************************************/ -/* register definitions */ -#define CFG 0x00 -#define FC 0x04 -#define RX_SAMPLE 0x08 -#define RT_TUNE 0x0C -#define TX_DAT 0x10 -#define RX_DAT 0x14 -#define INFO 0x18 -#define IE 0x1C -#define INT 0x24 -#define STATUS 0x2C - -/* CFG */ -#define CFG_STOP_2BIT BIT(2) -#define CFG_PARITY_EVEN BIT(3) -#define CFG_PARITY_EN BIT(4) -#define CFG_TX_EN BIT(5) -#define CFG_RX_EN BIT(6) -#define CFG_UART_EN BIT(7) -#define CFG_BAUD_SART_SHIFT 8 - -/* INFO */ -#define INFO_TX_EMPTY BIT(3) -#define INFO_TX_FULL BIT(2) -#define INFO_RX_EMPTY BIT(1) -#define INFO_RX_FULL BIT(0) - -/* Interrupt */ -#define RX_BREAK BIT(7) -#define RX_FIFO_NONEMPTYE BIT(6) -#define TX_FIFO_EMPTYE BIT(5) -#define RX_FIFO_UNDERRUNE BIT(4) -#define RX_FIFO_OVERRUNE BIT(3) -#define RX_PARITY_ERRE BIT(2) -#define RX_STOP_ERRE BIT(1) -#define TX_FIFO_OVERRUNE BIT(0) - -#define TX_TIMEOUT 5000 -#define UART_NR 4 -#define CA_UART_NAME_LEN 32 -struct cortina_uart_port { - struct uart_port uart; - char name[CA_UART_NAME_LEN]; - char has_bi; - unsigned int may_wakeup; -}; - -static struct cortina_uart_port *cortina_uart_ports; - -static irqreturn_t cortina_uart_interrupt(int irq, void *dev_id); - -/* Return uart_port pointer base on index */ -struct cortina_uart_port *cortina_uart_get_port(unsigned int index) -{ - struct cortina_uart_port *pca_port = cortina_uart_ports; - - if (index >= UART_NR) { - /* return 1st element if invalid index */ - index = 0; - } - - pca_port += index; - - return pca_port; -} - -/* uart_ops functions */ -static unsigned int cortina_uart_tx_empty(struct uart_port *port) -{ - /* Return 0 on FIXO condition, TIOCSER_TEMT otherwise */ - return (readl(port->membase + INFO) & INFO_TX_EMPTY) ? TIOCSER_TEMT : 0; -} - -static void cortina_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ -/* - * Even if we do not support configuring the modem control lines, this - * function must be proided to the serial core. - * port->ops->set_mctrl() be called in uart_configure_port() - */ -} - -static unsigned int cortina_uart_get_mctrl(struct uart_port *port) -{ - /* Unimplemented signals asserted, per Documentation/serial/driver */ - return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; -} - -static void cortina_uart_stop_tx(struct uart_port *port) -{ - /* Turn off Tx interrupts. The port lock is held at this point */ - unsigned int reg_v; - - reg_v = readl(port->membase + IE); - writel(reg_v & ~TX_FIFO_EMPTYE, port->membase + IE); -} - -static inline void cortina_transmit_buffer(struct uart_port *port) -{ - struct circ_buf *xmit = &port->state->xmit; - - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { - cortina_uart_stop_tx(port); - return; - } - - do { - /* send xmit->buf[xmit->tail] out the port here */ - writel(xmit->buf[xmit->tail], port->membase + TX_DAT); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - if ((readl(port->membase + INFO) & INFO_TX_FULL)) - break; - } while (!uart_circ_empty(xmit)); - - if (uart_circ_empty(xmit)) - cortina_uart_stop_tx(port); -} - -static void cortina_uart_start_tx(struct uart_port *port) -{ - /* Turn on Tx interrupts. The port lock is held at this point */ - unsigned int reg_v; - - reg_v = readl(port->membase + IE); - writel((reg_v | TX_FIFO_EMPTYE), port->membase + IE); - - reg_v = readl(port->membase + CFG); - writel(reg_v | CFG_TX_EN, port->membase + CFG); - - if (readl(port->membase + INFO) & INFO_TX_EMPTY) - cortina_transmit_buffer(port); -} - -static void cortina_uart_stop_rx(struct uart_port *port) -{ - /* Turn off Rx interrupts. The port lock is held at this point */ - unsigned int reg_v; - - reg_v = readl(port->membase + IE); - writel(reg_v & ~RX_FIFO_NONEMPTYE, port->membase + IE); -} - -static void cortina_uart_enable_ms(struct uart_port *port) -{ - /* Nope, you really can't hope to attach a modem to this */ -} - -static int cortina_uart_startup(struct uart_port *port) -{ - unsigned int reg_v; - int retval; - unsigned long flags; - - /* Disable interrupt */ - writel(0, port->membase + IE); - - retval = - request_irq(port->irq, cortina_uart_interrupt, 0, "cortina_uart", - port); - if (retval) - return retval; - - spin_lock_irqsave(&port->lock, flags); - - reg_v = readl(port->membase + CFG); - reg_v |= (CFG_UART_EN | CFG_TX_EN | CFG_RX_EN | 0x3 /* 8-bits data */); - writel(reg_v, port->membase + CFG); - reg_v = readl(port->membase + IE); - writel(reg_v | RX_FIFO_NONEMPTYE | TX_FIFO_EMPTYE, port->membase + IE); - - spin_unlock_irqrestore(&port->lock, flags); - return 0; -} - -static void cortina_uart_shutdown(struct uart_port *port) -{ - cortina_uart_stop_tx(port); - cortina_uart_stop_rx(port); - free_irq(port->irq, port); -} - -static void cortina_uart_set_termios(struct uart_port *port, - struct ktermios *termios, - struct ktermios *old) -{ - unsigned long flags; - int baud; - unsigned int reg_v, sample_freq = 0; - - baud = uart_get_baud_rate(port, termios, old, 0, 230400); - reg_v = readl(port->membase + CFG); - /* mask off the baud settings */ - reg_v &= 0xff; - reg_v |= (port->uartclk / baud) << CFG_BAUD_SART_SHIFT; - - /* Sampling rate should be half of baud count */ - sample_freq = (reg_v >> CFG_BAUD_SART_SHIFT) / 2; - - /* See include/uapi/asm-generic/termbits.h for CSIZE definition */ - /* mask off the data width */ - reg_v &= 0xfffffffc; - switch (termios->c_cflag & CSIZE) { - case CS5: - reg_v |= 0x0; - break; - case CS6: - reg_v |= 0x1; - break; - case CS7: - reg_v |= 0x2; - break; - case CS8: - default: - reg_v |= 0x3; - break; - } - - /* mask off Stop bits */ - reg_v &= ~(CFG_STOP_2BIT); - if (termios->c_cflag & CSTOPB) - reg_v |= CFG_STOP_2BIT; - - /* Parity */ - reg_v &= ~(CFG_PARITY_EN); - reg_v |= CFG_PARITY_EVEN; - if (termios->c_cflag & PARENB) { - reg_v |= CFG_PARITY_EN; - if (termios->c_cflag & PARODD) - reg_v &= ~(CFG_PARITY_EVEN); - } - - spin_lock_irqsave(&port->lock, flags); - writel(reg_v, port->membase + CFG); - writel(sample_freq, port->membase + RX_SAMPLE); - spin_unlock_irqrestore(&port->lock, flags); -} - -static const char *cortina_uart_type(struct uart_port *port) -{ - return container_of(port, struct cortina_uart_port, uart)->name; -} - -static void cortina_uart_config_port(struct uart_port *port, int flags) -{ - /* - * Driver core for serial ports forces a non-zero value for port type. - * Write an arbitrary value here to accommodate the serial core driver, - * as ID part of UAPI is redundant. - */ - port->type = 1; -} - -static int cortina_uart_verify_port(struct uart_port *port, - struct serial_struct *ser) -{ - if (ser->type != PORT_UNKNOWN && ser->type != 1) - return -EINVAL; - return 0; -} - -static void cortina_access_power(struct uart_port *port, unsigned int state, - unsigned int oldstate) -{ - unsigned int reg_v; - - /* Read Config register */ - reg_v = readl(port->membase + CFG); - switch (state) { - case UART_PM_STATE_ON: - reg_v |= CFG_UART_EN; - break; - case UART_PM_STATE_OFF: - reg_v &= ~CFG_UART_EN; - break; - default: - pr_err("cortina-access serial: Unknown PM state %d\n", state); - } - writel(reg_v, port->membase + CFG); -} - -#ifdef CONFIG_CONSOLE_POLL -static int cortina_poll_get_char(struct uart_port *port) -{ - unsigned int rx; - - if (readl(port->membase + INFO) & INFO_RX_EMPTY) - return NO_POLL_CHAR; - - rx = readl(port->membase + RX_DAT); - - return rx; -} - -static void cortina_poll_put_char(struct uart_port *port, unsigned char c) -{ - unsigned long time_out; - - time_out = jiffies + usecs_to_jiffies(TX_TIMEOUT); - - while (time_before(jiffies, time_out) && - (readl(port->membase + INFO) & INFO_TX_FULL)) - cpu_relax(); - - /* Give up if FIFO stuck! */ - if ((readl(port->membase + INFO) & INFO_TX_FULL)) - return; - - writel(c, port->membase + TX_DAT); -} - -#endif - -static const struct uart_ops cortina_uart_ops = { - .tx_empty = cortina_uart_tx_empty, - .set_mctrl = cortina_uart_set_mctrl, - .get_mctrl = cortina_uart_get_mctrl, - .stop_tx = cortina_uart_stop_tx, - .start_tx = cortina_uart_start_tx, - .stop_rx = cortina_uart_stop_rx, - .enable_ms = cortina_uart_enable_ms, - .startup = cortina_uart_startup, - .shutdown = cortina_uart_shutdown, - .set_termios = cortina_uart_set_termios, - .type = cortina_uart_type, - .config_port = cortina_uart_config_port, - .verify_port = cortina_uart_verify_port, - .pm = cortina_access_power, -#ifdef CONFIG_CONSOLE_POLL - .poll_get_char = cortina_poll_get_char, - .poll_put_char = cortina_poll_put_char, -#endif -}; - -static inline void cortina_uart_interrupt_rx_chars(struct uart_port *port, - unsigned long status) -{ - struct tty_port *ttyport = &port->state->port; - unsigned int ch; - unsigned int rx, flg; - struct cortina_uart_port *pca_port; - - rx = readl(port->membase + INFO); - if (INFO_RX_EMPTY & rx) - return; - - if (status & RX_FIFO_OVERRUNE) - port->icount.overrun++; - - pca_port = cortina_uart_get_port(port->line); - - /* Read the character while FIFO is not empty */ - do { - flg = TTY_NORMAL; - port->icount.rx++; - ch = readl(port->membase + RX_DAT); - if (status & RX_PARITY_ERRE) { - port->icount.parity++; - flg = TTY_PARITY; - } - - if (pca_port->has_bi) { - /* If BI supported ? */ - if (status & RX_BREAK) { - port->icount.brk++; - if (uart_handle_break(port)) - goto ignore; - } - } else { - /* Treat stop err as BI */ - if (status & RX_STOP_ERRE) { - port->icount.brk++; - if (uart_handle_break(port)) - goto ignore; - } - } - if (!(ch & 0x100)) /* RX char is not valid */ - goto ignore; - - if (uart_handle_sysrq_char(port, (unsigned char)ch)) - goto ignore; - - tty_insert_flip_char(ttyport, ch, flg); - ignore: - rx = readl(port->membase + INFO); - } while (!(INFO_RX_EMPTY & rx)); - - spin_unlock(&port->lock); - tty_flip_buffer_push(ttyport); - spin_lock(&port->lock); -} - -static inline void cortina_uart_interrupt_tx_chars(struct uart_port *port) -{ - struct circ_buf *xmit = &port->state->xmit; - - /* Process out of band chars */ - if (port->x_char) { - /* Send next char */ - writel(port->x_char, port->membase + TX_DAT); - goto done; - } - - /* Nothing to do ? */ - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { - cortina_uart_stop_tx(port); - goto done; - } - - cortina_transmit_buffer(port); - - /* Wake up */ - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); - - /* Maybe we're done after all */ - if (uart_circ_empty(xmit)) - cortina_uart_stop_tx(port); - - done: - return; -} - -irqreturn_t cortina_uart_interrupt(int irq, void *dev_id) -{ - struct uart_port *port = (struct uart_port *)dev_id; - unsigned int irq_status; - - spin_lock(&port->lock); - - /* Clear interrupt! */ - irq_status = readl(port->membase + INT); - writel(irq_status, port->membase + INT); - - /* Process any Rx chars first */ - cortina_uart_interrupt_rx_chars(port, irq_status); - /* Then use any Tx space */ - cortina_uart_interrupt_tx_chars(port); - - spin_unlock(&port->lock); - - return IRQ_HANDLED; -} - -#ifdef CONFIG_SERIAL_CORTINA_ACCESS_CONSOLE -void cortina_console_write(struct console *co, const char *s, - unsigned int count) -{ - struct uart_port *port; - struct cortina_uart_port *pca_port; - unsigned int i, previous; - unsigned long flags; - int locked; - - pca_port = cortina_uart_get_port(co->index); - port = &pca_port->uart; - - local_irq_save(flags); - if (port->sysrq) { - locked = 0; - } else if (oops_in_progress) { - locked = spin_trylock(&port->lock); - } else { - spin_lock(&port->lock); - locked = 1; - } - - /* Save current state */ - previous = readl(port->membase + IE); - /* Disable Tx interrupts so this all goes out in one go */ - cortina_uart_stop_tx(port); - - /* Write all the chars */ - for (i = 0; i < count; i++) { - /* Wait the TX buffer to be empty, which can't take forever */ - while (!(readl(port->membase + INFO) & INFO_TX_EMPTY)) - cpu_relax(); - - /* Send the char */ - writel(*s, port->membase + TX_DAT); - - /* CR/LF stuff */ - if (*s++ == '\n') { - /* Wait the TX buffer to be empty */ - while (!(readl(port->membase + INFO) & INFO_TX_EMPTY)) - cpu_relax(); - writel('\r', port->membase + TX_DAT); - } - } - - writel(previous, port->membase + IE); /* Put it all back */ - - if (locked) - spin_unlock(&port->lock); - local_irq_restore(flags); -} - -static int __init cortina_console_setup(struct console *co, char *options) -{ - struct uart_port *port; - struct cortina_uart_port *pca_port; - int baud = 115200; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - - if (co->index < 0 || co->index >= UART_NR) - return -ENODEV; - - pca_port = cortina_uart_get_port(co->index); - port = &pca_port->uart; - - /* This isn't going to do much, but it might change the baud rate. */ - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(port, co, baud, parity, bits, flow); -} - -static struct uart_driver cortina_uart_driver; - -static struct console cortina_console = { - .name = "ttyS", - .write = cortina_console_write, - .device = uart_console_device, - .setup = cortina_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, /* Only possible option. */ - .data = &cortina_uart_driver, -}; -#define CORTINA_CONSOLE (&cortina_console) - -/* Support EARLYCON */ -static void cortina_putc(struct uart_port *port, int c) -{ - unsigned int tmout; - - /* No jiffie at early boot stage! - * Wait up to 5ms for the character to be sent. - */ - tmout = TX_TIMEOUT; - while (--tmout) { - if (!(readl(port->membase + INFO) & INFO_TX_FULL)) - break; - udelay(1); - } - - /* Give up if FIFO stuck! */ - while ((readl(port->membase + INFO) & INFO_TX_FULL)) - return; - - /* Send the char */ - writel(c, port->membase + TX_DAT); -} - -static void cortina_early_write(struct console *con, const char *s, - unsigned int n) -{ - struct earlycon_device *dev = con->data; - - uart_console_write(&dev->port, s, n, cortina_putc); -} - -static int __init cortina_early_console_setup(struct earlycon_device *device, - const char *opt) -{ - if (!device->port.membase) - return -ENODEV; - - device->con->write = cortina_early_write; - return 0; -} - -EARLYCON_DECLARE(serial, cortina_early_console_setup); -OF_EARLYCON_DECLARE(serial, "cortina-access,serial", cortina_early_console_setup); -#else -#define CORTINA_CONSOLE NULL -#endif - -static struct uart_driver cortina_uart_driver = { - .owner = THIS_MODULE, - .driver_name = "cortina-access_uart", - .dev_name = "ttyS", - .major = TTY_MAJOR, - .minor = 64, - .nr = UART_NR, - .cons = CORTINA_CONSOLE, -}; - -/* Match table for of_platform binding */ -static const struct of_device_id cortina_uart_of_match[] = { - {.compatible = "cortina-access,serial",}, - {} -}; -MODULE_DEVICE_TABLE(of, cortina_uart_of_match); - -static int serial_cortina_probe(struct platform_device *pdev) -{ - int ret; - void __iomem *base; - struct cortina_uart_port *port; - const struct of_device_id *match; - - /* assign DT node pointer */ - struct device_node *np = pdev->dev.of_node; - struct resource mem_resource; - u32 of_clock_frequency; - struct clk *pclk_info; - int uart_idx; - - /* search DT for a match */ - match = of_match_device(cortina_uart_of_match, &pdev->dev); - if (!match) - return -EINVAL; - - if (cortina_uart_ports == NULL) - cortina_uart_ports = kzalloc(UART_NR * sizeof(struct cortina_uart_port), - GFP_KERNEL); - - port = cortina_uart_ports; - for (uart_idx = 0; uart_idx < UART_NR; ++uart_idx) { - /* Find first empty slot */ - if (strlen(port->name) == 0) - break; - port++; - } - - if (uart_idx >= UART_NR) - return -EINVAL; - - snprintf(port->name, sizeof(port->name), "Cortina-Access UART%d", uart_idx); - - /* Retrieve HW base address */ - ret = of_address_to_resource(np, 0, &mem_resource); - if (ret) { - dev_warn(&pdev->dev, "invalid address %d\n", ret); - return ret; - } - - base = devm_ioremap(&pdev->dev, mem_resource.start, - resource_size(&mem_resource)); - if (!base) { - devm_kfree(&pdev->dev, port); - return -ENOMEM; - } - - /* assign reg base and irq from DT */ - port->uart.irq = irq_of_parse_and_map(np, 0); - port->uart.membase = base; - port->uart.mapbase = mem_resource.start; - port->uart.ops = &cortina_uart_ops; - port->uart.dev = &pdev->dev; - port->uart.line = uart_idx; - port->uart.has_sysrq = IS_ENABLED(CONFIG_SERIAL_CORTINA_ACCESS_CONSOLE); - - /* get clock-freqency tuple from DT and store value */ - if (of_property_read_u32(np, "clock-frequency", &of_clock_frequency)) { - /* If we are here, it means DT node did not contain - * clock-frequency tuple. Therefore, instead try to get - * clk rate through the clk driver that DT has stated - * we are consuming. - */ - pclk_info = clk_get(&pdev->dev, NULL); - if (IS_ERR(pclk_info)) { - dev_warn(&pdev->dev, - "clk or clock-frequency not defined\n"); - return PTR_ERR(pclk_info); - } - - clk_prepare_enable(pclk_info); - of_clock_frequency = clk_get_rate(pclk_info); - } - port->uart.uartclk = of_clock_frequency; - - if (of_property_read_bool(np, "wakeup-source")) - port->may_wakeup = true; - if (of_property_read_bool(np, "break-indicator")) - port->has_bi = true; - - port->uart.type = PORT_UNKNOWN; - - if (port->may_wakeup) - device_init_wakeup(&pdev->dev, true); - - ret = uart_add_one_port(&cortina_uart_driver, &port->uart); - if (ret) - return ret; - - platform_set_drvdata(pdev, port); - - return 0; -} - -static int serial_cortina_remove(struct platform_device *pdev) -{ - struct uart_port *port = platform_get_drvdata(pdev); - struct cortina_uart_port *pca_port; - - if (port) { - pca_port = cortina_uart_get_port(port->line); - memset(pca_port->name, 0, CA_UART_NAME_LEN); - uart_remove_one_port(&cortina_uart_driver, port); - } - - platform_set_drvdata(pdev, NULL); - return 0; -} - -#ifdef CONFIG_PM -static int serial_cortina_suspend(struct platform_device *pdev, - pm_message_t state) -{ - struct cortina_uart_port *p = - (struct cortina_uart_port *)pdev->dev.driver_data; - - uart_suspend_port(&cortina_uart_driver, &p->uart); - - return 0; -} - -static int serial_cortina_resume(struct platform_device *pdev) -{ - struct cortina_uart_port *p = - (struct cortina_uart_port *)pdev->dev.driver_data; - - uart_resume_port(&cortina_uart_driver, &p->uart); - - return 0; -} -#else -#define serial_cortina_suspend NULL -#define serial_cortina_resume NULL -#endif - -static struct platform_driver serial_cortina_driver = { - .probe = serial_cortina_probe, - .remove = serial_cortina_remove, -#ifdef CONFIG_PM - .suspend = serial_cortina_suspend, - .resume = serial_cortina_resume, -#endif - .driver = { - .name = "cortina-access_serial", - .owner = THIS_MODULE, - .of_match_table = cortina_uart_of_match, - }, -}; - -static int __init cortina_uart_init(void) -{ - int ret; - - ret = uart_register_driver(&cortina_uart_driver); - if (ret) - return ret; - - ret = platform_driver_register(&serial_cortina_driver); - if (ret) - uart_unregister_driver(&cortina_uart_driver); - - return ret; -} - -static void __exit cortina_uart_exit(void) -{ - platform_driver_unregister(&serial_cortina_driver); - uart_unregister_driver(&cortina_uart_driver); -} - -module_init(cortina_uart_init); -module_exit(cortina_uart_exit); - -MODULE_AUTHOR("Cortina-Access Inc."); -MODULE_DESCRIPTION(" Cortina-Access UART driver"); -MODULE_LICENSE("GPL"); -- cgit v1.2.3