diff options
Diffstat (limited to 'drivers/tty/serial/arc_uart.c')
| -rw-r--r-- | drivers/tty/serial/arc_uart.c | 407 |
1 files changed, 150 insertions, 257 deletions
diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c index cbf1d155b7b2..5c4895d154c0 100644 --- a/drivers/tty/serial/arc_uart.c +++ b/drivers/tty/serial/arc_uart.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * ARC On-Chip(fpga) UART Driver * * Copyright (C) 2010-2012 Synopsys, Inc. (www.synopsys.com) * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * * vineetg: July 10th 2012 * -Decoupled the driver from arch/arc * +Using platform_get_resource() for irq/membase (thx to bfin_uart.c) @@ -24,10 +21,6 @@ * -check if sysreq works */ -#if defined(CONFIG_SERIAL_ARC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - #include <linux/module.h> #include <linux/serial.h> #include <linux/console.h> @@ -37,8 +30,8 @@ #include <linux/tty_flip.h> #include <linux/serial_core.h> #include <linux/io.h> -#include <linux/of.h> -#include <linux/of_platform.h> +#include <linux/of_irq.h> +#include <linux/of_address.h> /************************************* * ARC UART Hardware Specs @@ -72,7 +65,7 @@ #define RXOERR 0x02 /* OverFlow Err: Char recv but RXFULL still set */ /* Uart bit fiddling helpers: lowest level */ -#define RBASE(uart, reg) (uart->port.membase + reg) +#define RBASE(port, reg) (port->membase + reg) #define UART_REG_SET(u, r, v) writeb((v), RBASE(u, r)) #define UART_REG_GET(u, r) readb(RBASE(u, r)) @@ -102,7 +95,6 @@ struct arc_uart_port { struct uart_port port; unsigned long baud; - int is_emulated; /* H/w vs. Instruction Set Simulator */ }; #define to_arc_port(uport) container_of(uport, struct arc_uart_port, port) @@ -129,19 +121,15 @@ static struct uart_driver arc_uart_driver = { static void arc_serial_stop_rx(struct uart_port *port) { - struct arc_uart_port *uart = to_arc_port(port); - - UART_RX_IRQ_DISABLE(uart); + UART_RX_IRQ_DISABLE(port); } static void arc_serial_stop_tx(struct uart_port *port) { - struct arc_uart_port *uart = to_arc_port(port); - - while (!(UART_GET_STATUS(uart) & TXEMPTY)) + while (!(UART_GET_STATUS(port) & TXEMPTY)) cpu_relax(); - UART_TX_IRQ_DISABLE(uart); + UART_TX_IRQ_DISABLE(port); } /* @@ -149,10 +137,9 @@ static void arc_serial_stop_tx(struct uart_port *port) */ static unsigned int arc_serial_tx_empty(struct uart_port *port) { - struct arc_uart_port *uart = to_arc_port(port); unsigned int stat; - stat = UART_GET_STATUS(uart); + stat = UART_GET_STATUS(port); if (stat & TXEMPTY) return TIOCSER_TEMT; @@ -162,28 +149,25 @@ static unsigned int arc_serial_tx_empty(struct uart_port *port) /* * Driver internal routine, used by both tty(serial core) as well as tx-isr * -Called under spinlock in either cases - * -also tty->stopped has already been checked + * -also tty->flow.stopped has already been checked * = by uart_start( ) before calling us * = tx_ist checks that too before calling */ -static void arc_serial_tx_chars(struct arc_uart_port *uart) +static void arc_serial_tx_chars(struct uart_port *port) { - struct circ_buf *xmit = &uart->port.state->xmit; + struct tty_port *tport = &port->state->port; int sent = 0; unsigned char ch; - if (unlikely(uart->port.x_char)) { - UART_SET_DATA(uart, uart->port.x_char); - uart->port.icount.tx++; - uart->port.x_char = 0; + if (unlikely(port->x_char)) { + UART_SET_DATA(port, port->x_char); + port->icount.tx++; + port->x_char = 0; sent = 1; - } else if (xmit->tail != xmit->head) { /* TODO: uart_circ_empty */ - ch = xmit->buf[xmit->tail]; - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - uart->port.icount.tx++; - while (!(UART_GET_STATUS(uart) & TXEMPTY)) + } else if (uart_fifo_get(port, &ch)) { + while (!(UART_GET_STATUS(port) & TXEMPTY)) cpu_relax(); - UART_SET_DATA(uart, ch); + UART_SET_DATA(port, ch); sent = 1; } @@ -191,11 +175,11 @@ static void arc_serial_tx_chars(struct arc_uart_port *uart) * If num chars in xmit buffer are too few, ask tty layer for more. * By Hard ISR to schedule processing in software interrupt part */ - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&uart->port); + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) + uart_write_wakeup(port); if (sent) - UART_TX_IRQ_ENABLE(uart); + UART_TX_IRQ_ENABLE(port); } /* @@ -204,15 +188,11 @@ static void arc_serial_tx_chars(struct arc_uart_port *uart) */ static void arc_serial_start_tx(struct uart_port *port) { - struct arc_uart_port *uart = to_arc_port(port); - - arc_serial_tx_chars(uart); + arc_serial_tx_chars(port); } -static void arc_serial_rx_chars(struct arc_uart_port *uart) +static void arc_serial_rx_chars(struct uart_port *port, unsigned int status) { - unsigned int status, ch, flg = 0; - /* * UART has 4 deep RX-FIFO. Driver's recongnition of this fact * is very subtle. Here's how ... @@ -222,34 +202,36 @@ static void arc_serial_rx_chars(struct arc_uart_port *uart) * before RX-EMPTY=0, implies some sort of buffering going on in the * controller, which is indeed the Rx-FIFO. */ - while (!((status = UART_GET_STATUS(uart)) & RXEMPTY)) { + do { + u8 ch, flg = TTY_NORMAL; - ch = UART_GET_DATA(uart); - uart->port.icount.rx++; + /* + * This could be an Rx Intr for err (no data), + * so check err and clear that Intr first + */ + if (status & RXOERR) { + port->icount.overrun++; + flg = TTY_OVERRUN; + UART_CLR_STATUS(port, RXOERR); + } - if (unlikely(status & (RXOERR | RXFERR))) { - if (status & RXOERR) { - uart->port.icount.overrun++; - flg = TTY_OVERRUN; - UART_CLR_STATUS(uart, RXOERR); - } + if (status & RXFERR) { + port->icount.frame++; + flg = TTY_FRAME; + UART_CLR_STATUS(port, RXFERR); + } - if (status & RXFERR) { - uart->port.icount.frame++; - flg = TTY_FRAME; - UART_CLR_STATUS(uart, RXFERR); - } - } else - flg = TTY_NORMAL; + if (status & RXEMPTY) + continue; - if (unlikely(uart_handle_sysrq_char(&uart->port, ch))) - goto done; + ch = UART_GET_DATA(port); + port->icount.rx++; - uart_insert_char(&uart->port, status, RXOERR, ch, flg); + if (!(uart_handle_sysrq_char(port, ch))) + uart_insert_char(port, status, RXOERR, ch, flg); -done: - tty_flip_buffer_push(&uart->port.state->port); - } + tty_flip_buffer_push(&port->state->port); + } while (!((status = UART_GET_STATUS(port)) & RXEMPTY)); } /* @@ -282,22 +264,22 @@ done: static irqreturn_t arc_serial_isr(int irq, void *dev_id) { - struct arc_uart_port *uart = dev_id; + struct uart_port *port = dev_id; unsigned int status; - status = UART_GET_STATUS(uart); + status = UART_GET_STATUS(port); /* * Single IRQ for both Rx (data available) Tx (room available) Interrupt * notifications from the UART Controller. * To demultiplex between the two, we check the relevant bits */ - if ((status & RXIENB) && !(status & RXEMPTY)) { + if (status & RXIENB) { /* already in ISR, no need of xx_irqsave */ - spin_lock(&uart->port.lock); - arc_serial_rx_chars(uart); - spin_unlock(&uart->port.lock); + uart_port_lock(port); + arc_serial_rx_chars(port, status); + uart_port_unlock(port); } if ((status & TXIENB) && (status & TXEMPTY)) { @@ -305,14 +287,14 @@ static irqreturn_t arc_serial_isr(int irq, void *dev_id) /* Unconditionally disable further Tx-Interrupts. * will be enabled by tx_chars() if needed. */ - UART_TX_IRQ_DISABLE(uart); + UART_TX_IRQ_DISABLE(port); - spin_lock(&uart->port.lock); + uart_port_lock(port); - if (!uart_tx_stopped(&uart->port)) - arc_serial_tx_chars(uart); + if (!uart_tx_stopped(port)) + arc_serial_tx_chars(port); - spin_unlock(&uart->port.lock); + uart_port_unlock(port); } return IRQ_HANDLED; @@ -335,13 +317,6 @@ static void arc_serial_set_mctrl(struct uart_port *port, unsigned int mctrl) /* MCR not present */ } -/* Enable Modem Status Interrupts */ - -static void arc_serial_enable_ms(struct uart_port *port) -{ - /* MSR not present */ -} - static void arc_serial_break_ctl(struct uart_port *port, int break_state) { /* ARC UART doesn't support sending Break signal */ @@ -349,18 +324,15 @@ static void arc_serial_break_ctl(struct uart_port *port, int break_state) static int arc_serial_startup(struct uart_port *port) { - struct arc_uart_port *uart = to_arc_port(port); - /* Before we hook up the ISR, Disable all UART Interrupts */ - UART_ALL_IRQ_DISABLE(uart); + UART_ALL_IRQ_DISABLE(port); - if (request_irq(uart->port.irq, arc_serial_isr, 0, "arc uart rx-tx", - uart)) { - dev_warn(uart->port.dev, "Unable to attach ARC UART intr\n"); + if (request_irq(port->irq, arc_serial_isr, 0, "arc uart rx-tx", port)) { + dev_warn(port->dev, "Unable to attach ARC UART intr\n"); return -EBUSY; } - UART_RX_IRQ_ENABLE(uart); /* Only Rx IRQ enabled to begin with */ + UART_RX_IRQ_ENABLE(port); /* Only Rx IRQ enabled to begin with */ return 0; } @@ -368,13 +340,12 @@ static int arc_serial_startup(struct uart_port *port) /* This is not really needed */ static void arc_serial_shutdown(struct uart_port *port) { - struct arc_uart_port *uart = to_arc_port(port); - free_irq(uart->port.irq, uart); + free_irq(port->irq, port); } static void arc_serial_set_termios(struct uart_port *port, struct ktermios *new, - struct ktermios *old) + const struct ktermios *old) { struct arc_uart_port *uart = to_arc_port(port); unsigned int baud, uartl, uarth, hw_val; @@ -393,25 +364,14 @@ arc_serial_set_termios(struct uart_port *port, struct ktermios *new, uartl = hw_val & 0xFF; uarth = (hw_val >> 8) & 0xFF; - /* - * UART ISS(Instruction Set simulator) emulation has a subtle bug: - * A existing value of Baudh = 0 is used as a indication to startup - * it's internal state machine. - * Thus if baudh is set to 0, 2 times, it chokes. - * This happens with BAUD=115200 and the formaula above - * Until that is fixed, when running on ISS, we will set baudh to !0 - */ - if (uart->is_emulated) - uarth = 1; - - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); - UART_ALL_IRQ_DISABLE(uart); + UART_ALL_IRQ_DISABLE(port); - UART_SET_BAUDL(uart, uartl); - UART_SET_BAUDH(uart, uarth); + UART_SET_BAUDL(port, uartl); + UART_SET_BAUDH(port, uarth); - UART_RX_IRQ_ENABLE(uart); + UART_RX_IRQ_ENABLE(port); /* * UART doesn't support Parity/Hardware Flow Control; @@ -429,14 +389,12 @@ arc_serial_set_termios(struct uart_port *port, struct ktermios *new, uart_update_timeout(port, new->c_cflag, baud); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static const char *arc_serial_type(struct uart_port *port) { - struct arc_uart_port *uart = to_arc_port(port); - - return uart->port.type == PORT_ARC ? DRIVER_NAME : NULL; + return port->type == PORT_ARC ? DRIVER_NAME : NULL; } static void arc_serial_release_port(struct uart_port *port) @@ -465,47 +423,39 @@ arc_serial_verify_port(struct uart_port *port, struct serial_struct *ser) */ static void arc_serial_config_port(struct uart_port *port, int flags) { - struct arc_uart_port *uart = to_arc_port(port); - if (flags & UART_CONFIG_TYPE) - uart->port.type = PORT_ARC; + port->type = PORT_ARC; } -#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_ARC_CONSOLE) +#ifdef CONFIG_CONSOLE_POLL static void arc_serial_poll_putchar(struct uart_port *port, unsigned char chr) { - struct arc_uart_port *uart = to_arc_port(port); - - while (!(UART_GET_STATUS(uart) & TXEMPTY)) + while (!(UART_GET_STATUS(port) & TXEMPTY)) cpu_relax(); - UART_SET_DATA(uart, chr); + UART_SET_DATA(port, chr); } -#endif -#ifdef CONFIG_CONSOLE_POLL static int arc_serial_poll_getchar(struct uart_port *port) { - struct arc_uart_port *uart = to_arc_port(port); unsigned char chr; - while (!(UART_GET_STATUS(uart) & RXEMPTY)) + while (!(UART_GET_STATUS(port) & RXEMPTY)) cpu_relax(); - chr = UART_GET_DATA(uart); + chr = UART_GET_DATA(port); return chr; } #endif -static struct uart_ops arc_serial_pops = { +static const struct uart_ops arc_serial_pops = { .tx_empty = arc_serial_tx_empty, .set_mctrl = arc_serial_set_mctrl, .get_mctrl = arc_serial_get_mctrl, .stop_tx = arc_serial_stop_tx, .start_tx = arc_serial_start_tx, .stop_rx = arc_serial_stop_rx, - .enable_ms = arc_serial_enable_ms, .break_ctl = arc_serial_break_ctl, .startup = arc_serial_startup, .shutdown = arc_serial_shutdown, @@ -521,71 +471,6 @@ static struct uart_ops arc_serial_pops = { #endif }; -static int -arc_uart_init_one(struct platform_device *pdev, int dev_id) -{ - struct resource *res, *res2; - unsigned long *plat_data; - struct arc_uart_port *uart = &arc_uart_ports[dev_id]; - - plat_data = ((unsigned long *)(pdev->dev.platform_data)); - if (!plat_data) - return -ENODEV; - - uart->is_emulated = !!plat_data[0]; /* workaround ISS bug */ - - if (is_early_platform_device(pdev)) { - uart->port.uartclk = plat_data[1]; - uart->baud = plat_data[2]; - } else { - struct device_node *np = pdev->dev.of_node; - u32 val; - - if (of_property_read_u32(np, "clock-frequency", &val)) { - dev_err(&pdev->dev, "clock-frequency property NOTset\n"); - return -EINVAL; - } - uart->port.uartclk = val; - - if (of_property_read_u32(np, "current-speed", &val)) { - dev_err(&pdev->dev, "current-speed property NOT set\n"); - return -EINVAL; - } - uart->baud = val; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; - - res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res2) - return -ENODEV; - - uart->port.mapbase = res->start; - uart->port.membase = ioremap_nocache(res->start, resource_size(res)); - if (!uart->port.membase) - /* No point of dev_err since UART itself is hosed here */ - return -ENXIO; - - uart->port.irq = res2->start; - uart->port.dev = &pdev->dev; - uart->port.iotype = UPIO_MEM; - uart->port.flags = UPF_BOOT_AUTOCONF; - uart->port.line = dev_id; - uart->port.ops = &arc_serial_pops; - - uart->port.fifosize = ARC_UART_TX_FIFO_SIZE; - - /* - * uart_insert_char( ) uses it in decideding whether to ignore a - * char or not. Explicitly setting it here, removes the subtelty - */ - uart->port.ignore_status_mask = 0; - - return 0; -} - #ifdef CONFIG_SERIAL_ARC_CONSOLE static int arc_serial_console_setup(struct console *co, char *options) @@ -617,9 +502,12 @@ static int arc_serial_console_setup(struct console *co, char *options) return uart_set_options(port, co, baud, parity, bits, flow); } -static void arc_serial_console_putchar(struct uart_port *port, int ch) +static void arc_serial_console_putchar(struct uart_port *port, unsigned char ch) { - arc_serial_poll_putchar(port, (unsigned char)ch); + while (!(UART_GET_STATUS(port) & TXEMPTY)) + cpu_relax(); + + UART_SET_DATA(port, (unsigned char)ch); } /* @@ -631,9 +519,9 @@ static void arc_serial_console_write(struct console *co, const char *s, struct uart_port *port = &arc_uart_ports[co->index].port; unsigned long flags; - spin_lock_irqsave(&port->lock, flags); + uart_port_lock_irqsave(port, &flags); uart_console_write(port, s, count, arc_serial_console_putchar); - spin_unlock_irqrestore(&port->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static struct console arc_console = { @@ -646,48 +534,44 @@ static struct console arc_console = { .data = &arc_uart_driver }; -static __init void early_serial_write(struct console *con, const char *s, - unsigned int n) +static void arc_early_serial_write(struct console *con, const char *s, + unsigned int n) { - struct uart_port *port = &arc_uart_ports[con->index].port; - unsigned int i; + struct earlycon_device *dev = con->data; - for (i = 0; i < n; i++, s++) { - if (*s == '\n') - arc_serial_poll_putchar(port, '\r'); - arc_serial_poll_putchar(port, *s); - } + uart_console_write(&dev->port, s, n, arc_serial_console_putchar); } -static struct console arc_early_serial_console __initdata = { - .name = "early_ARCuart", - .write = early_serial_write, - .flags = CON_PRINTBUFFER | CON_BOOT, - .index = -1 -}; - -static int __init arc_serial_probe_earlyprintk(struct platform_device *pdev) +static int __init arc_early_console_setup(struct earlycon_device *dev, + const char *opt) { - int dev_id = pdev->id < 0 ? 0 : pdev->id; - int rc; + struct uart_port *port = &dev->port; + unsigned int l, h, hw_val; - arc_early_serial_console.index = dev_id; + if (!dev->port.membase) + return -ENODEV; - rc = arc_uart_init_one(pdev, dev_id); - if (rc) - panic("early console init failed\n"); + hw_val = port->uartclk / (dev->baud * 4) - 1; + l = hw_val & 0xFF; + h = (hw_val >> 8) & 0xFF; - arc_serial_console_setup(&arc_early_serial_console, NULL); + UART_SET_BAUDL(port, l); + UART_SET_BAUDH(port, h); - register_console(&arc_early_serial_console); + dev->con->write = arc_early_serial_write; return 0; } +OF_EARLYCON_DECLARE(arc_uart, "snps,arc-uart", arc_early_console_setup); + #endif /* CONFIG_SERIAL_ARC_CONSOLE */ static int arc_serial_probe(struct platform_device *pdev) { - int rc, dev_id; struct device_node *np = pdev->dev.of_node; + struct arc_uart_port *uart; + struct uart_port *port; + int dev_id; + u32 val; /* no device tree device */ if (!np) @@ -697,18 +581,50 @@ static int arc_serial_probe(struct platform_device *pdev) if (dev_id < 0) dev_id = 0; - rc = arc_uart_init_one(pdev, dev_id); - if (rc) - return rc; + if (dev_id >= ARRAY_SIZE(arc_uart_ports)) { + dev_err(&pdev->dev, "serial%d out of range\n", dev_id); + return -EINVAL; + } - rc = uart_add_one_port(&arc_uart_driver, &arc_uart_ports[dev_id].port); - return rc; -} + uart = &arc_uart_ports[dev_id]; + port = &uart->port; -static int arc_serial_remove(struct platform_device *pdev) -{ - /* This will never be called */ - return 0; + if (of_property_read_u32(np, "clock-frequency", &val)) { + dev_err(&pdev->dev, "clock-frequency property NOTset\n"); + return -EINVAL; + } + port->uartclk = val; + + if (of_property_read_u32(np, "current-speed", &val)) { + dev_err(&pdev->dev, "current-speed property NOT set\n"); + return -EINVAL; + } + uart->baud = val; + + port->membase = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(port->membase)) { + /* No point of dev_err since UART itself is hosed here */ + return PTR_ERR(port->membase); + } + + port->irq = irq_of_parse_and_map(np, 0); + + port->dev = &pdev->dev; + port->iotype = UPIO_MEM; + port->flags = UPF_BOOT_AUTOCONF; + port->line = dev_id; + port->ops = &arc_serial_pops; + port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_ARC_CONSOLE); + + port->fifosize = ARC_UART_TX_FIFO_SIZE; + + /* + * uart_insert_char( ) uses it in decideding whether to ignore a + * char or not. Explicitly setting it here, removes the subtelty + */ + port->ignore_status_mask = 0; + + return uart_add_one_port(&arc_uart_driver, &arc_uart_ports[dev_id].port); } static const struct of_device_id arc_uart_dt_ids[] = { @@ -719,35 +635,12 @@ MODULE_DEVICE_TABLE(of, arc_uart_dt_ids); static struct platform_driver arc_platform_driver = { .probe = arc_serial_probe, - .remove = arc_serial_remove, .driver = { .name = DRIVER_NAME, - .owner = THIS_MODULE, .of_match_table = arc_uart_dt_ids, }, }; -#ifdef CONFIG_SERIAL_ARC_CONSOLE - -static struct platform_driver early_arc_platform_driver __initdata = { - .probe = arc_serial_probe_earlyprintk, - .remove = arc_serial_remove, - .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE, - }, -}; -/* - * Register an early platform driver of "earlyprintk" class. - * ARCH platform code installs the driver and probes the early devices - * The installation could rely on user specifying earlyprintk=xyx in cmd line - * or it could be done independently, for all "earlyprintk" class drivers. - * [see arch/arc/plat-arcfpga/platform.c] - */ -early_platform_init("earlyprintk", &early_arc_platform_driver); - -#endif /* CONFIG_SERIAL_ARC_CONSOLE */ - static int __init arc_serial_init(void) { int ret; @@ -773,6 +666,6 @@ module_init(arc_serial_init); module_exit(arc_serial_exit); MODULE_LICENSE("GPL"); -MODULE_ALIAS("plat-arcfpga/uart"); +MODULE_ALIAS("platform:" DRIVER_NAME); MODULE_AUTHOR("Vineet Gupta"); MODULE_DESCRIPTION("ARC(Synopsys) On-Chip(fpga) serial driver"); |
