summaryrefslogtreecommitdiffstats
path: root/drivers/tty/serial/sprd_serial.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/serial/sprd_serial.c')
-rw-r--r--drivers/tty/serial/sprd_serial.c99
1 files changed, 82 insertions, 17 deletions
diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c
index 73d71a4e6c0c..3d3c70634589 100644
--- a/drivers/tty/serial/sprd_serial.c
+++ b/drivers/tty/serial/sprd_serial.c
@@ -3,10 +3,6 @@
* Copyright (C) 2012-2015 Spreadtrum Communications Inc.
*/
-#if defined(CONFIG_SERIAL_SPRD_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
-#define SUPPORT_SYSRQ
-#endif
-
#include <linux/clk.h>
#include <linux/console.h>
#include <linux/delay.h>
@@ -79,6 +75,7 @@
/* control register 1 */
#define SPRD_CTL1 0x001C
#define SPRD_DMA_EN BIT(15)
+#define SPRD_LOOPBACK_EN BIT(14)
#define RX_HW_FLOW_CTL_THLD BIT(6)
#define RX_HW_FLOW_CTL_EN BIT(7)
#define TX_HW_FLOW_CTL_EN BIT(8)
@@ -164,7 +161,14 @@ static unsigned int sprd_get_mctrl(struct uart_port *port)
static void sprd_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
- /* nothing to do */
+ u32 val = serial_in(port, SPRD_CTL1);
+
+ if (mctrl & TIOCM_LOOP)
+ val |= SPRD_LOOPBACK_EN;
+ else
+ val &= ~SPRD_LOOPBACK_EN;
+
+ serial_out(port, SPRD_CTL1, val);
}
static void sprd_stop_rx(struct uart_port *port)
@@ -609,7 +613,7 @@ static inline void sprd_rx(struct uart_port *port)
if (lsr & (SPRD_LSR_BI | SPRD_LSR_PE |
SPRD_LSR_FE | SPRD_LSR_OE))
- if (handle_lsr_errors(port, &lsr, &flag))
+ if (handle_lsr_errors(port, &flag, &lsr))
continue;
if (uart_handle_sysrq_char(port, ch))
continue;
@@ -671,6 +675,9 @@ static irqreturn_t sprd_handle_irq(int irq, void *dev_id)
if (ims & SPRD_IMSR_TIMEOUT)
serial_out(port, SPRD_ICLR, SPRD_ICLR_TIMEOUT);
+ if (ims & SPRD_IMSR_BREAK_DETECT)
+ serial_out(port, SPRD_ICLR, SPRD_IMSR_BREAK_DETECT);
+
if (ims & (SPRD_IMSR_RX_FIFO_FULL | SPRD_IMSR_BREAK_DETECT |
SPRD_IMSR_TIMEOUT))
sprd_rx(port);
@@ -911,6 +918,34 @@ static void sprd_pm(struct uart_port *port, unsigned int state,
}
}
+#ifdef CONFIG_CONSOLE_POLL
+static int sprd_poll_init(struct uart_port *port)
+{
+ if (port->state->pm_state != UART_PM_STATE_ON) {
+ sprd_pm(port, UART_PM_STATE_ON, 0);
+ port->state->pm_state = UART_PM_STATE_ON;
+ }
+
+ return 0;
+}
+
+static int sprd_poll_get_char(struct uart_port *port)
+{
+ while (!(serial_in(port, SPRD_STS1) & SPRD_RX_FIFO_CNT_MASK))
+ cpu_relax();
+
+ return serial_in(port, SPRD_RXD);
+}
+
+static void sprd_poll_put_char(struct uart_port *port, unsigned char ch)
+{
+ while (serial_in(port, SPRD_STS1) & SPRD_TX_FIFO_CNT_MASK)
+ cpu_relax();
+
+ serial_out(port, SPRD_TXD, ch);
+}
+#endif
+
static const struct uart_ops serial_sprd_ops = {
.tx_empty = sprd_tx_empty,
.get_mctrl = sprd_get_mctrl,
@@ -928,6 +963,11 @@ static const struct uart_ops serial_sprd_ops = {
.config_port = sprd_config_port,
.verify_port = sprd_verify_port,
.pm = sprd_pm,
+#ifdef CONFIG_CONSOLE_POLL
+ .poll_init = sprd_poll_init,
+ .poll_get_char = sprd_poll_get_char,
+ .poll_put_char = sprd_poll_put_char,
+#endif
};
#ifdef CONFIG_SERIAL_SPRD_CONSOLE
@@ -975,7 +1015,7 @@ static void sprd_console_write(struct console *co, const char *s,
static int __init sprd_console_setup(struct console *co, char *options)
{
- struct uart_port *port;
+ struct sprd_uart_port *sprd_uart_port;
int baud = 115200;
int bits = 8;
int parity = 'n';
@@ -984,15 +1024,17 @@ static int __init sprd_console_setup(struct console *co, char *options)
if (co->index >= UART_NR_MAX || co->index < 0)
co->index = 0;
- port = &sprd_port[co->index]->port;
- if (port == NULL) {
+ sprd_uart_port = sprd_port[co->index];
+ if (!sprd_uart_port || !sprd_uart_port->port.membase) {
pr_info("serial port %d not yet initialized\n", co->index);
return -ENODEV;
}
+
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
- return uart_set_options(port, co, baud, parity, bits, flow);
+ return uart_set_options(&sprd_uart_port->port, co, baud,
+ parity, bits, flow);
}
static struct uart_driver sprd_uart_driver;
@@ -1006,6 +1048,13 @@ static struct console sprd_console = {
.data = &sprd_uart_driver,
};
+static int __init sprd_serial_console_init(void)
+{
+ register_console(&sprd_console);
+ return 0;
+}
+console_initcall(sprd_serial_console_init);
+
#define SPRD_CONSOLE (&sprd_console)
/* Support for earlycon */
@@ -1094,6 +1143,16 @@ static int sprd_remove(struct platform_device *dev)
return 0;
}
+static bool sprd_uart_is_console(struct uart_port *uport)
+{
+ struct console *cons = sprd_uart_driver.cons;
+
+ if (cons && cons->index >= 0 && cons->index == uport->line)
+ return true;
+
+ return false;
+}
+
static int sprd_clk_init(struct uart_port *uport)
{
struct clk *clk_uart, *clk_parent;
@@ -1120,10 +1179,17 @@ static int sprd_clk_init(struct uart_port *uport)
u->clk = devm_clk_get(uport->dev, "enable");
if (IS_ERR(u->clk)) {
- if (PTR_ERR(u->clk) != -EPROBE_DEFER)
- dev_err(uport->dev, "uart%d can't get enable clock\n",
- uport->line);
- return PTR_ERR(u->clk);
+ if (PTR_ERR(u->clk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ dev_warn(uport->dev, "uart%d can't get enable clock\n",
+ uport->line);
+
+ /* To keep console alive even if the error occurred */
+ if (!sprd_uart_is_console(uport))
+ return PTR_ERR(u->clk);
+
+ u->clk = NULL;
}
return 0;
@@ -1160,6 +1226,7 @@ static int sprd_probe(struct platform_device *pdev)
up->fifosize = SPRD_FIFO_SIZE;
up->ops = &serial_sprd_ops;
up->flags = UPF_BOOT_AUTOCONF;
+ up->has_sysrq = IS_ENABLED(CONFIG_SERIAL_SPRD_CONSOLE);
ret = sprd_clk_init(up);
if (ret)
@@ -1173,10 +1240,8 @@ static int sprd_probe(struct platform_device *pdev)
up->mapbase = res->start;
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "not provide irq resource: %d\n", irq);
+ if (irq < 0)
return irq;
- }
up->irq = irq;
/*
OpenPOWER on IntegriCloud