Commit 8a8a66a1 authored by Girish Mahadevan's avatar Girish Mahadevan Committed by Greg Kroah-Hartman

tty: serial: qcom_geni_serial: Add support for flow control

Add support for flow control functionality in the GENI serial driver
and also support for non-console higher baud rate(upto 4Mbps) usecases.
Signed-off-by: default avatarGirish Mahadevan <girishm@codeaurora.org>
Signed-off-by: default avatarMohammed Khajapasha <mkhaja@codeaurora.org>
Signed-off-by: default avatarKarthikeyan Ramasubramanian <kramasub@codeaurora.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent c58caaab
...@@ -46,7 +46,7 @@ Child nodes should conform to I2C bus binding as described in i2c.txt. ...@@ -46,7 +46,7 @@ Child nodes should conform to I2C bus binding as described in i2c.txt.
Qualcomm Technologies Inc. GENI Serial Engine based UART Controller Qualcomm Technologies Inc. GENI Serial Engine based UART Controller
Required properties: Required properties:
- compatible: Must be "qcom,geni-debug-uart". - compatible: Must be "qcom,geni-debug-uart" or "qcom,geni-uart".
- reg: Must contain UART register location and length. - reg: Must contain UART register location and length.
- interrupts: Must contain UART core interrupts. - interrupts: Must contain UART core interrupts.
- clock-names: Must contain "se". - clock-names: Must contain "se".
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/tty_flip.h> #include <linux/tty_flip.h>
/* UART specific GENI registers */ /* UART specific GENI registers */
#define SE_UART_LOOPBACK_CFG 0x22c
#define SE_UART_TX_TRANS_CFG 0x25c #define SE_UART_TX_TRANS_CFG 0x25c
#define SE_UART_TX_WORD_LEN 0x268 #define SE_UART_TX_WORD_LEN 0x268
#define SE_UART_TX_STOP_BIT_LEN 0x26c #define SE_UART_TX_STOP_BIT_LEN 0x26c
...@@ -26,6 +27,7 @@ ...@@ -26,6 +27,7 @@
#define SE_UART_RX_STALE_CNT 0x294 #define SE_UART_RX_STALE_CNT 0x294
#define SE_UART_TX_PARITY_CFG 0x2a4 #define SE_UART_TX_PARITY_CFG 0x2a4
#define SE_UART_RX_PARITY_CFG 0x2a8 #define SE_UART_RX_PARITY_CFG 0x2a8
#define SE_UART_MANUAL_RFR 0x2ac
/* SE_UART_TRANS_CFG */ /* SE_UART_TRANS_CFG */
#define UART_TX_PAR_EN BIT(0) #define UART_TX_PAR_EN BIT(0)
...@@ -62,6 +64,11 @@ ...@@ -62,6 +64,11 @@
#define PAR_SPACE 0x10 #define PAR_SPACE 0x10
#define PAR_MARK 0x11 #define PAR_MARK 0x11
/* SE_UART_MANUAL_RFR register fields */
#define UART_MANUAL_RFR_EN BIT(31)
#define UART_RFR_NOT_READY BIT(1)
#define UART_RFR_READY BIT(0)
/* UART M_CMD OP codes */ /* UART M_CMD OP codes */
#define UART_START_TX 0x1 #define UART_START_TX 0x1
#define UART_START_BREAK 0x4 #define UART_START_BREAK 0x4
...@@ -74,10 +81,12 @@ ...@@ -74,10 +81,12 @@
#define STALE_TIMEOUT 16 #define STALE_TIMEOUT 16
#define DEFAULT_BITS_PER_CHAR 10 #define DEFAULT_BITS_PER_CHAR 10
#define GENI_UART_CONS_PORTS 1 #define GENI_UART_CONS_PORTS 1
#define GENI_UART_PORTS 3
#define DEF_FIFO_DEPTH_WORDS 16 #define DEF_FIFO_DEPTH_WORDS 16
#define DEF_TX_WM 2 #define DEF_TX_WM 2
#define DEF_FIFO_WIDTH_BITS 32 #define DEF_FIFO_WIDTH_BITS 32
#define UART_CONSOLE_RX_WM 2 #define UART_CONSOLE_RX_WM 2
#define MAX_LOOPBACK_CFG 3
#ifdef CONFIG_CONSOLE_POLL #ifdef CONFIG_CONSOLE_POLL
#define RX_BYTES_PW 1 #define RX_BYTES_PW 1
...@@ -101,22 +110,81 @@ struct qcom_geni_serial_port { ...@@ -101,22 +110,81 @@ struct qcom_geni_serial_port {
unsigned int baud; unsigned int baud;
unsigned int tx_bytes_pw; unsigned int tx_bytes_pw;
unsigned int rx_bytes_pw; unsigned int rx_bytes_pw;
u32 *rx_fifo;
u32 loopback;
bool brk; bool brk;
}; };
static const struct uart_ops qcom_geni_console_pops; static const struct uart_ops qcom_geni_console_pops;
static const struct uart_ops qcom_geni_uart_pops;
static struct uart_driver qcom_geni_console_driver; static struct uart_driver qcom_geni_console_driver;
static struct uart_driver qcom_geni_uart_driver;
static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop); static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop);
static int handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop);
static unsigned int qcom_geni_serial_tx_empty(struct uart_port *port); static unsigned int qcom_geni_serial_tx_empty(struct uart_port *port);
static void qcom_geni_serial_stop_rx(struct uart_port *uport); static void qcom_geni_serial_stop_rx(struct uart_port *uport);
static const unsigned long root_freq[] = {7372800, 14745600, 19200000, 29491200, static const unsigned long root_freq[] = {7372800, 14745600, 19200000, 29491200,
32000000, 48000000, 64000000, 80000000, 32000000, 48000000, 64000000, 80000000,
96000000, 100000000}; 96000000, 100000000, 102400000,
112000000, 120000000, 128000000};
#define to_dev_port(ptr, member) \ #define to_dev_port(ptr, member) \
container_of(ptr, struct qcom_geni_serial_port, member) container_of(ptr, struct qcom_geni_serial_port, member)
static struct qcom_geni_serial_port qcom_geni_uart_ports[GENI_UART_PORTS] = {
[0] = {
.uport = {
.iotype = UPIO_MEM,
.ops = &qcom_geni_uart_pops,
.flags = UPF_BOOT_AUTOCONF,
.line = 0,
},
},
[1] = {
.uport = {
.iotype = UPIO_MEM,
.ops = &qcom_geni_uart_pops,
.flags = UPF_BOOT_AUTOCONF,
.line = 1,
},
},
[2] = {
.uport = {
.iotype = UPIO_MEM,
.ops = &qcom_geni_uart_pops,
.flags = UPF_BOOT_AUTOCONF,
.line = 2,
},
},
};
static ssize_t loopback_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
return snprintf(buf, sizeof(u32), "%d\n", port->loopback);
}
static ssize_t loopback_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t size)
{
struct platform_device *pdev = to_platform_device(dev);
struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
u32 loopback;
if (kstrtoint(buf, 0, &loopback) || loopback > MAX_LOOPBACK_CFG) {
dev_err(dev, "Invalid input\n");
return -EINVAL;
}
port->loopback = loopback;
return size;
}
static DEVICE_ATTR_RW(loopback);
static struct qcom_geni_serial_port qcom_geni_console_port = { static struct qcom_geni_serial_port qcom_geni_console_port = {
.uport = { .uport = {
.iotype = UPIO_MEM, .iotype = UPIO_MEM,
...@@ -148,14 +216,33 @@ static void qcom_geni_serial_config_port(struct uart_port *uport, int cfg_flags) ...@@ -148,14 +216,33 @@ static void qcom_geni_serial_config_port(struct uart_port *uport, int cfg_flags)
} }
} }
static unsigned int qcom_geni_cons_get_mctrl(struct uart_port *uport) static unsigned int qcom_geni_serial_get_mctrl(struct uart_port *uport)
{ {
return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS; unsigned int mctrl = TIOCM_DSR | TIOCM_CAR;
u32 geni_ios;
if (uart_console(uport) || !uart_cts_enabled(uport)) {
mctrl |= TIOCM_CTS;
} else {
geni_ios = readl_relaxed(uport->membase + SE_GENI_IOS);
if (!(geni_ios & IO2_DATA_IN))
mctrl |= TIOCM_CTS;
}
return mctrl;
} }
static void qcom_geni_cons_set_mctrl(struct uart_port *uport, static void qcom_geni_serial_set_mctrl(struct uart_port *uport,
unsigned int mctrl) unsigned int mctrl)
{ {
u32 uart_manual_rfr = 0;
if (uart_console(uport) || !uart_cts_enabled(uport))
return;
if (!(mctrl & TIOCM_RTS))
uart_manual_rfr = UART_MANUAL_RFR_EN | UART_RFR_NOT_READY;
writel_relaxed(uart_manual_rfr, uport->membase + SE_UART_MANUAL_RFR);
} }
static const char *qcom_geni_serial_get_type(struct uart_port *uport) static const char *qcom_geni_serial_get_type(struct uart_port *uport)
...@@ -163,11 +250,16 @@ static const char *qcom_geni_serial_get_type(struct uart_port *uport) ...@@ -163,11 +250,16 @@ static const char *qcom_geni_serial_get_type(struct uart_port *uport)
return "MSM"; return "MSM";
} }
static struct qcom_geni_serial_port *get_port_from_line(int line) static struct qcom_geni_serial_port *get_port_from_line(int line, bool console)
{ {
if (line < 0 || line >= GENI_UART_CONS_PORTS) struct qcom_geni_serial_port *port;
int nr_ports = console ? GENI_UART_CONS_PORTS : GENI_UART_PORTS;
if (line < 0 || line >= nr_ports)
return ERR_PTR(-ENXIO); return ERR_PTR(-ENXIO);
return &qcom_geni_console_port;
port = console ? &qcom_geni_console_port : &qcom_geni_uart_ports[line];
return port;
} }
static bool qcom_geni_serial_poll_bit(struct uart_port *uport, static bool qcom_geni_serial_poll_bit(struct uart_port *uport,
...@@ -346,7 +438,7 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s, ...@@ -346,7 +438,7 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s,
WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS); WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS);
port = get_port_from_line(co->index); port = get_port_from_line(co->index, true);
if (IS_ERR(port)) if (IS_ERR(port))
return; return;
...@@ -420,6 +512,32 @@ static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop) ...@@ -420,6 +512,32 @@ static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop)
#endif /* CONFIG_SERIAL_QCOM_GENI_CONSOLE */ #endif /* CONFIG_SERIAL_QCOM_GENI_CONSOLE */
static int handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop)
{
unsigned char *buf;
struct tty_port *tport;
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
u32 num_bytes_pw = port->tx_fifo_width / BITS_PER_BYTE;
u32 words = ALIGN(bytes, num_bytes_pw) / num_bytes_pw;
int ret;
tport = &uport->state->port;
ioread32_rep(uport->membase + SE_GENI_RX_FIFOn, port->rx_fifo, words);
if (drop)
return 0;
buf = (unsigned char *)port->rx_fifo;
ret = tty_insert_flip_string(tport, buf, bytes);
if (ret != bytes) {
dev_err(uport->dev, "%s:Unable to push data ret %d_bytes %d\n",
__func__, ret, bytes);
WARN_ON_ONCE(1);
}
uport->icount.rx += ret;
tty_flip_buffer_push(tport);
return ret;
}
static void qcom_geni_serial_start_tx(struct uart_port *uport) static void qcom_geni_serial_start_tx(struct uart_port *uport)
{ {
u32 irq_en; u32 irq_en;
...@@ -586,6 +704,7 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport) ...@@ -586,6 +704,7 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport)
u32 status; u32 status;
unsigned int chunk; unsigned int chunk;
int tail; int tail;
u32 irq_en;
chunk = uart_circ_chars_pending(xmit); chunk = uart_circ_chars_pending(xmit);
status = readl_relaxed(uport->membase + SE_GENI_TX_FIFO_STATUS); status = readl_relaxed(uport->membase + SE_GENI_TX_FIFO_STATUS);
...@@ -595,6 +714,13 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport) ...@@ -595,6 +714,13 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport)
goto out_write_wakeup; goto out_write_wakeup;
} }
if (!uart_console(uport)) {
irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
irq_en &= ~(M_TX_FIFO_WATERMARK_EN);
writel_relaxed(0, uport->membase + SE_GENI_TX_WATERMARK_REG);
writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
}
avail = (port->tx_fifo_depth - port->tx_wm) * port->tx_bytes_pw; avail = (port->tx_fifo_depth - port->tx_wm) * port->tx_bytes_pw;
tail = xmit->tail; tail = xmit->tail;
chunk = min3((size_t)chunk, (size_t)(UART_XMIT_SIZE - tail), avail); chunk = min3((size_t)chunk, (size_t)(UART_XMIT_SIZE - tail), avail);
...@@ -623,7 +749,8 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport) ...@@ -623,7 +749,8 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport)
} }
xmit->tail = tail & (UART_XMIT_SIZE - 1); xmit->tail = tail & (UART_XMIT_SIZE - 1);
qcom_geni_serial_poll_tx_done(uport); if (uart_console(uport))
qcom_geni_serial_poll_tx_done(uport);
out_write_wakeup: out_write_wakeup:
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(uport); uart_write_wakeup(uport);
...@@ -710,7 +837,8 @@ static void qcom_geni_serial_shutdown(struct uart_port *uport) ...@@ -710,7 +837,8 @@ static void qcom_geni_serial_shutdown(struct uart_port *uport)
unsigned long flags; unsigned long flags;
/* Stop the console before stopping the current tx */ /* Stop the console before stopping the current tx */
console_stop(uport->cons); if (uart_console(uport))
console_stop(uport->cons);
free_irq(uport->irq, uport); free_irq(uport->irq, uport);
spin_lock_irqsave(&uport->lock, flags); spin_lock_irqsave(&uport->lock, flags);
...@@ -731,13 +859,20 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport) ...@@ -731,13 +859,20 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport)
* it else we could end up in data loss scenarios. * it else we could end up in data loss scenarios.
*/ */
port->xfer_mode = GENI_SE_FIFO; port->xfer_mode = GENI_SE_FIFO;
qcom_geni_serial_poll_tx_done(uport); if (uart_console(uport))
qcom_geni_serial_poll_tx_done(uport);
geni_se_config_packing(&port->se, BITS_PER_BYTE, port->tx_bytes_pw, geni_se_config_packing(&port->se, BITS_PER_BYTE, port->tx_bytes_pw,
false, true, false); false, true, false);
geni_se_config_packing(&port->se, BITS_PER_BYTE, port->rx_bytes_pw, geni_se_config_packing(&port->se, BITS_PER_BYTE, port->rx_bytes_pw,
false, false, true); false, false, true);
geni_se_init(&port->se, port->rx_wm, port->rx_rfr); geni_se_init(&port->se, port->rx_wm, port->rx_rfr);
geni_se_select_mode(&port->se, port->xfer_mode); geni_se_select_mode(&port->se, port->xfer_mode);
if (!uart_console(uport)) {
port->rx_fifo = devm_kzalloc(uport->dev,
port->rx_fifo_depth * sizeof(u32), GFP_KERNEL);
if (!port->rx_fifo)
return -ENOMEM;
}
port->setup = true; port->setup = true;
return 0; return 0;
} }
...@@ -749,8 +884,13 @@ static int qcom_geni_serial_startup(struct uart_port *uport) ...@@ -749,8 +884,13 @@ static int qcom_geni_serial_startup(struct uart_port *uport)
struct qcom_geni_serial_port *port = to_dev_port(uport, uport); struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
scnprintf(port->name, sizeof(port->name), scnprintf(port->name, sizeof(port->name),
"qcom_serial_geni%d", uport->line); "qcom_serial_%s%d",
(uart_console(uport) ? "console" : "uart"), uport->line);
if (!uart_console(uport)) {
port->tx_bytes_pw = 4;
port->rx_bytes_pw = RX_BYTES_PW;
}
proto = geni_se_read_proto(&port->se); proto = geni_se_read_proto(&port->se);
if (proto != GENI_SE_UART) { if (proto != GENI_SE_UART) {
dev_err(uport->dev, "Invalid FW loaded, proto: %d\n", proto); dev_err(uport->dev, "Invalid FW loaded, proto: %d\n", proto);
...@@ -886,6 +1026,9 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport, ...@@ -886,6 +1026,9 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport,
if (baud) if (baud)
uart_update_timeout(uport, termios->c_cflag, baud); uart_update_timeout(uport, termios->c_cflag, baud);
if (!uart_console(uport))
writel_relaxed(port->loopback,
uport->membase + SE_UART_LOOPBACK_CFG);
writel_relaxed(tx_trans_cfg, uport->membase + SE_UART_TX_TRANS_CFG); writel_relaxed(tx_trans_cfg, uport->membase + SE_UART_TX_TRANS_CFG);
writel_relaxed(tx_parity_cfg, uport->membase + SE_UART_TX_PARITY_CFG); writel_relaxed(tx_parity_cfg, uport->membase + SE_UART_TX_PARITY_CFG);
writel_relaxed(rx_trans_cfg, uport->membase + SE_UART_RX_TRANS_CFG); writel_relaxed(rx_trans_cfg, uport->membase + SE_UART_RX_TRANS_CFG);
...@@ -917,7 +1060,7 @@ static int __init qcom_geni_console_setup(struct console *co, char *options) ...@@ -917,7 +1060,7 @@ static int __init qcom_geni_console_setup(struct console *co, char *options)
if (co->index >= GENI_UART_CONS_PORTS || co->index < 0) if (co->index >= GENI_UART_CONS_PORTS || co->index < 0)
return -ENXIO; return -ENXIO;
port = get_port_from_line(co->index); port = get_port_from_line(co->index, true);
if (IS_ERR(port)) { if (IS_ERR(port)) {
pr_err("Invalid line %d\n", co->index); pr_err("Invalid line %d\n", co->index);
return PTR_ERR(port); return PTR_ERR(port);
...@@ -1048,16 +1191,23 @@ static void console_unregister(struct uart_driver *drv) ...@@ -1048,16 +1191,23 @@ static void console_unregister(struct uart_driver *drv)
} }
#endif /* CONFIG_SERIAL_QCOM_GENI_CONSOLE */ #endif /* CONFIG_SERIAL_QCOM_GENI_CONSOLE */
static void qcom_geni_serial_cons_pm(struct uart_port *uport, static struct uart_driver qcom_geni_uart_driver = {
.owner = THIS_MODULE,
.driver_name = "qcom_geni_uart",
.dev_name = "ttyHS",
.nr = GENI_UART_PORTS,
};
static void qcom_geni_serial_pm(struct uart_port *uport,
unsigned int new_state, unsigned int old_state) unsigned int new_state, unsigned int old_state)
{ {
struct qcom_geni_serial_port *port = to_dev_port(uport, uport); struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
if (unlikely(!uart_console(uport)))
return;
if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF) if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF)
geni_se_resources_on(&port->se); geni_se_resources_on(&port->se);
else if (!uart_console(uport) && (new_state == UART_PM_STATE_ON &&
old_state == UART_PM_STATE_UNDEFINED))
geni_se_resources_on(&port->se);
else if (new_state == UART_PM_STATE_OFF && else if (new_state == UART_PM_STATE_OFF &&
old_state == UART_PM_STATE_ON) old_state == UART_PM_STATE_ON)
geni_se_resources_off(&port->se); geni_se_resources_off(&port->se);
...@@ -1074,13 +1224,29 @@ static const struct uart_ops qcom_geni_console_pops = { ...@@ -1074,13 +1224,29 @@ static const struct uart_ops qcom_geni_console_pops = {
.config_port = qcom_geni_serial_config_port, .config_port = qcom_geni_serial_config_port,
.shutdown = qcom_geni_serial_shutdown, .shutdown = qcom_geni_serial_shutdown,
.type = qcom_geni_serial_get_type, .type = qcom_geni_serial_get_type,
.set_mctrl = qcom_geni_cons_set_mctrl, .set_mctrl = qcom_geni_serial_set_mctrl,
.get_mctrl = qcom_geni_cons_get_mctrl, .get_mctrl = qcom_geni_serial_get_mctrl,
#ifdef CONFIG_CONSOLE_POLL #ifdef CONFIG_CONSOLE_POLL
.poll_get_char = qcom_geni_serial_get_char, .poll_get_char = qcom_geni_serial_get_char,
.poll_put_char = qcom_geni_serial_poll_put_char, .poll_put_char = qcom_geni_serial_poll_put_char,
#endif #endif
.pm = qcom_geni_serial_cons_pm, .pm = qcom_geni_serial_pm,
};
static const struct uart_ops qcom_geni_uart_pops = {
.tx_empty = qcom_geni_serial_tx_empty,
.stop_tx = qcom_geni_serial_stop_tx,
.start_tx = qcom_geni_serial_start_tx,
.stop_rx = qcom_geni_serial_stop_rx,
.set_termios = qcom_geni_serial_set_termios,
.startup = qcom_geni_serial_startup,
.request_port = qcom_geni_serial_request_port,
.config_port = qcom_geni_serial_config_port,
.shutdown = qcom_geni_serial_shutdown,
.type = qcom_geni_serial_get_type,
.set_mctrl = qcom_geni_serial_set_mctrl,
.get_mctrl = qcom_geni_serial_get_mctrl,
.pm = qcom_geni_serial_pm,
}; };
static int qcom_geni_serial_probe(struct platform_device *pdev) static int qcom_geni_serial_probe(struct platform_device *pdev)
...@@ -1091,13 +1257,23 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) ...@@ -1091,13 +1257,23 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
struct uart_port *uport; struct uart_port *uport;
struct resource *res; struct resource *res;
int irq; int irq;
bool console = false;
struct uart_driver *drv;
if (pdev->dev.of_node) if (of_device_is_compatible(pdev->dev.of_node, "qcom,geni-debug-uart"))
line = of_alias_get_id(pdev->dev.of_node, "serial"); console = true;
if (line < 0 || line >= GENI_UART_CONS_PORTS) if (pdev->dev.of_node) {
return -ENXIO; if (console) {
port = get_port_from_line(line); drv = &qcom_geni_console_driver;
line = of_alias_get_id(pdev->dev.of_node, "serial");
} else {
drv = &qcom_geni_uart_driver;
line = of_alias_get_id(pdev->dev.of_node, "hsuart");
}
}
port = get_port_from_line(line, console);
if (IS_ERR(port)) { if (IS_ERR(port)) {
dev_err(&pdev->dev, "Invalid line %d\n", line); dev_err(&pdev->dev, "Invalid line %d\n", line);
return PTR_ERR(port); return PTR_ERR(port);
...@@ -1134,10 +1310,12 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) ...@@ -1134,10 +1310,12 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
} }
uport->irq = irq; uport->irq = irq;
uport->private_data = &qcom_geni_console_driver; uport->private_data = drv;
platform_set_drvdata(pdev, port); platform_set_drvdata(pdev, port);
port->handle_rx = handle_rx_console; port->handle_rx = console ? handle_rx_console : handle_rx_uart;
return uart_add_one_port(&qcom_geni_console_driver, uport); if (!console)
device_create_file(uport->dev, &dev_attr_loopback);
return uart_add_one_port(drv, uport);
} }
static int qcom_geni_serial_remove(struct platform_device *pdev) static int qcom_geni_serial_remove(struct platform_device *pdev)
...@@ -1154,7 +1332,17 @@ static int __maybe_unused qcom_geni_serial_sys_suspend_noirq(struct device *dev) ...@@ -1154,7 +1332,17 @@ static int __maybe_unused qcom_geni_serial_sys_suspend_noirq(struct device *dev)
struct qcom_geni_serial_port *port = dev_get_drvdata(dev); struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
struct uart_port *uport = &port->uport; struct uart_port *uport = &port->uport;
uart_suspend_port(uport->private_data, uport); if (uart_console(uport)) {
uart_suspend_port(uport->private_data, uport);
} else {
struct uart_state *state = uport->state;
/*
* If the port is open, deny system suspend.
*/
if (state->pm_state == UART_PM_STATE_ON)
return -EBUSY;
}
return 0; return 0;
} }
...@@ -1163,7 +1351,8 @@ static int __maybe_unused qcom_geni_serial_sys_resume_noirq(struct device *dev) ...@@ -1163,7 +1351,8 @@ static int __maybe_unused qcom_geni_serial_sys_resume_noirq(struct device *dev)
struct qcom_geni_serial_port *port = dev_get_drvdata(dev); struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
struct uart_port *uport = &port->uport; struct uart_port *uport = &port->uport;
if (console_suspend_enabled && uport->suspended) { if (uart_console(uport) &&
console_suspend_enabled && uport->suspended) {
uart_resume_port(uport->private_data, uport); uart_resume_port(uport->private_data, uport);
/* /*
* uart_suspend_port() invokes port shutdown which in turn * uart_suspend_port() invokes port shutdown which in turn
...@@ -1185,6 +1374,7 @@ static const struct dev_pm_ops qcom_geni_serial_pm_ops = { ...@@ -1185,6 +1374,7 @@ static const struct dev_pm_ops qcom_geni_serial_pm_ops = {
static const struct of_device_id qcom_geni_serial_match_table[] = { static const struct of_device_id qcom_geni_serial_match_table[] = {
{ .compatible = "qcom,geni-debug-uart", }, { .compatible = "qcom,geni-debug-uart", },
{ .compatible = "qcom,geni-uart", },
{} {}
}; };
MODULE_DEVICE_TABLE(of, qcom_geni_serial_match_table); MODULE_DEVICE_TABLE(of, qcom_geni_serial_match_table);
...@@ -1207,9 +1397,17 @@ static int __init qcom_geni_serial_init(void) ...@@ -1207,9 +1397,17 @@ static int __init qcom_geni_serial_init(void)
if (ret) if (ret)
return ret; return ret;
ret = uart_register_driver(&qcom_geni_uart_driver);
if (ret) {
console_unregister(&qcom_geni_console_driver);
return ret;
}
ret = platform_driver_register(&qcom_geni_serial_platform_driver); ret = platform_driver_register(&qcom_geni_serial_platform_driver);
if (ret) if (ret) {
console_unregister(&qcom_geni_console_driver); console_unregister(&qcom_geni_console_driver);
uart_unregister_driver(&qcom_geni_uart_driver);
}
return ret; return ret;
} }
module_init(qcom_geni_serial_init); module_init(qcom_geni_serial_init);
...@@ -1218,6 +1416,7 @@ static void __exit qcom_geni_serial_exit(void) ...@@ -1218,6 +1416,7 @@ static void __exit qcom_geni_serial_exit(void)
{ {
platform_driver_unregister(&qcom_geni_serial_platform_driver); platform_driver_unregister(&qcom_geni_serial_platform_driver);
console_unregister(&qcom_geni_console_driver); console_unregister(&qcom_geni_console_driver);
uart_unregister_driver(&qcom_geni_uart_driver);
} }
module_exit(qcom_geni_serial_exit); module_exit(qcom_geni_serial_exit);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment