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: Girish Mahadevan <girishm@codeaurora.org> Signed-off-by: Mohammed Khajapasha <mkhaja@codeaurora.org> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Родитель
c58caaab3b
Коммит
8a8a66a1a1
|
@ -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 @@
|
||||||
#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 @@
|
||||||
#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 @@
|
||||||
#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 @@
|
||||||
#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 {
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
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,
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
#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)
|
||||||
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)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
* 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)
|
||||||
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,
|
||||||
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)
|
||||||
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)
|
||||||
}
|
}
|
||||||
#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 = {
|
||||||
.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)
|
||||||
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)
|
||||||
}
|
}
|
||||||
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)
|
||||||
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)
|
||||||
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 = {
|
||||||
|
|
||||||
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)
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = platform_driver_register(&qcom_geni_serial_platform_driver);
|
ret = uart_register_driver(&qcom_geni_uart_driver);
|
||||||
if (ret)
|
if (ret) {
|
||||||
console_unregister(&qcom_geni_console_driver);
|
console_unregister(&qcom_geni_console_driver);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = platform_driver_register(&qcom_geni_serial_platform_driver);
|
||||||
|
if (ret) {
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
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);
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче