aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/serial/arc_uart.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--drivers/tty/serial/arc_uart.c337
1 files changed, 121 insertions, 216 deletions
diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c
index 008c223eaf26..a59d1d77e750 100644
--- a/drivers/tty/serial/arc_uart.c
+++ b/drivers/tty/serial/arc_uart.c
@@ -37,8 +37,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 +72,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 +102,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 +128,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 +144,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;
@@ -166,24 +160,24 @@ static unsigned int arc_serial_tx_empty(struct uart_port *port)
* = 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 circ_buf *xmit = &port->state->xmit;
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 (!uart_circ_empty(xmit)) {
ch = xmit->buf[xmit->tail];
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- uart->port.icount.tx++;
- while (!(UART_GET_STATUS(uart) & TXEMPTY))
+ port->icount.tx++;
+ while (!(UART_GET_STATUS(port) & TXEMPTY))
cpu_relax();
- UART_SET_DATA(uart, ch);
+ UART_SET_DATA(port, ch);
sent = 1;
}
@@ -192,10 +186,10 @@ static void arc_serial_tx_chars(struct arc_uart_port *uart)
* By Hard ISR to schedule processing in software interrupt part
*/
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(&uart->port);
+ uart_write_wakeup(port);
if (sent)
- UART_TX_IRQ_ENABLE(uart);
+ UART_TX_IRQ_ENABLE(port);
}
/*
@@ -204,12 +198,10 @@ 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, unsigned int status)
+static void arc_serial_rx_chars(struct uart_port *port, unsigned int status)
{
unsigned int ch, flg = 0;
@@ -229,15 +221,15 @@ static void arc_serial_rx_chars(struct arc_uart_port *uart, unsigned int status)
*/
if (unlikely(status & (RXOERR | RXFERR))) {
if (status & RXOERR) {
- uart->port.icount.overrun++;
+ port->icount.overrun++;
flg = TTY_OVERRUN;
- UART_CLR_STATUS(uart, RXOERR);
+ UART_CLR_STATUS(port, RXOERR);
}
if (status & RXFERR) {
- uart->port.icount.frame++;
+ port->icount.frame++;
flg = TTY_FRAME;
- UART_CLR_STATUS(uart, RXFERR);
+ UART_CLR_STATUS(port, RXFERR);
}
} else
flg = TTY_NORMAL;
@@ -245,16 +237,16 @@ static void arc_serial_rx_chars(struct arc_uart_port *uart, unsigned int status)
if (status & RXEMPTY)
continue;
- ch = UART_GET_DATA(uart);
- uart->port.icount.rx++;
+ ch = UART_GET_DATA(port);
+ port->icount.rx++;
- if (!(uart_handle_sysrq_char(&uart->port, ch)))
- uart_insert_char(&uart->port, status, RXOERR, ch, flg);
+ if (!(uart_handle_sysrq_char(port, ch)))
+ uart_insert_char(port, status, RXOERR, ch, flg);
- spin_unlock(&uart->port.lock);
- tty_flip_buffer_push(&uart->port.state->port);
- spin_lock(&uart->port.lock);
- } while (!((status = UART_GET_STATUS(uart)) & RXEMPTY));
+ spin_unlock(&port->lock);
+ tty_flip_buffer_push(&port->state->port);
+ spin_lock(&port->lock);
+ } while (!((status = UART_GET_STATUS(port)) & RXEMPTY));
}
/*
@@ -287,10 +279,10 @@ static void arc_serial_rx_chars(struct arc_uart_port *uart, unsigned int status)
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
@@ -300,9 +292,9 @@ static irqreturn_t arc_serial_isr(int irq, void *dev_id)
if (status & RXIENB) {
/* already in ISR, no need of xx_irqsave */
- spin_lock(&uart->port.lock);
- arc_serial_rx_chars(uart, status);
- spin_unlock(&uart->port.lock);
+ spin_lock(&port->lock);
+ arc_serial_rx_chars(port, status);
+ spin_unlock(&port->lock);
}
if ((status & TXIENB) && (status & TXEMPTY)) {
@@ -310,14 +302,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);
+ spin_lock(&port->lock);
- 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);
+ spin_unlock(&port->lock);
}
return IRQ_HANDLED;
@@ -340,13 +332,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 */
@@ -354,18 +339,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;
}
@@ -373,8 +355,7 @@ 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
@@ -398,25 +379,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_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;
@@ -439,9 +409,7 @@ arc_serial_set_termios(struct uart_port *port, struct ktermios *new,
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)
@@ -470,35 +438,28 @@ 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
@@ -510,7 +471,6 @@ static struct uart_ops arc_serial_pops = {
.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,
@@ -526,71 +486,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 = dev_get_platdata(&pdev->dev);
- 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)
@@ -624,7 +519,10 @@ static int arc_serial_console_setup(struct console *co, char *options)
static void arc_serial_console_putchar(struct uart_port *port, int ch)
{
- arc_serial_poll_putchar(port, (unsigned char)ch);
+ while (!(UART_GET_STATUS(port) & TXEMPTY))
+ cpu_relax();
+
+ UART_SET_DATA(port, (unsigned char)ch);
}
/*
@@ -651,48 +549,45 @@ 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 __init 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;
}
+EARLYCON_DECLARE(arc_uart, arc_early_console_setup);
+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)
@@ -702,12 +597,43 @@ 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;
+ uart = &arc_uart_ports[dev_id];
+ port = &uart->port;
+
+ 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 = of_iomap(np, 0);
+ if (!port->membase)
+ /* No point of dev_err since UART itself is hosed here */
+ return -ENXIO;
+
+ port->irq = irq_of_parse_and_map(np, 0);
- rc = uart_add_one_port(&arc_uart_driver, &arc_uart_ports[dev_id].port);
- return rc;
+ port->dev = &pdev->dev;
+ port->iotype = UPIO_MEM;
+ port->flags = UPF_BOOT_AUTOCONF;
+ port->line = dev_id;
+ port->ops = &arc_serial_pops;
+
+ 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 int arc_serial_remove(struct platform_device *pdev)
@@ -732,27 +658,6 @@ static struct platform_driver arc_platform_driver = {
},
};
-#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;