Skip to content

Commit e42d6c3

Browse files
diandersandersson
authored andcommitted
serial: qcom_geni_serial: Make kgdb work even if UART isn't console
The geni serial driver had the rather sketchy hack in it where it would adjust the number of bytes per RX FIFO word from 4 down to 1 if it detected that CONFIG_CONSOLE_POLL was enabled (for kgdb) and this was a console port (defined by the kernel directing output to this port via the "console=" command line argument). The problem with that sketchy hack is that it's possible to run kgdb over a serial port even if it isn't used for console. Let's avoid the hack by simply handling the 4-bytes-per-FIFO word case for kdb. We'll have to have a (very small) cache but that should be fine. A nice side effect of this patch is that an agetty (or similar) running on this port is less likely to drop characters. We'll have roughly 4 times the RX FIFO depth than we used to now. NOTE: the character cache here isn't shared between the polling API and the non-polling API. That means that, technically, the polling API could eat a few extra bytes. This doesn't seem to pose a huge problem in reality because we'll only get several characters per FIFO word if those characters are all received at nearly the same time and we don't really expect non-kgdb characters to be sent to the same port as kgdb at the exact same time we're exiting kgdb. ALSO NOTE: we still have the sketchy hack for setting the number of bytes per TX FIFO word in place, but that one is less bad. kgdb doesn't have any problem with this because it always just sends 1 byte at a time and waits for it to finish. The TX FIFO hack is only really needed for console output. In any case, a future patch will remove that hack, too. Acked-by: Greg Kroah-Hartman <[email protected]> Reviewed-by: Evan Green <[email protected]> Signed-off-by: Douglas Anderson <[email protected]> Link: https://lore.kernel.org/r/20200626125844.1.I8546ecb6c5beb054f70c5302d1a7293484212cd1@changeid Signed-off-by: Bjorn Andersson <[email protected]>
1 parent da48dc8 commit e42d6c3

File tree

1 file changed

+55
-25
lines changed

1 file changed

+55
-25
lines changed

drivers/tty/serial/qcom_geni_serial.c

Lines changed: 55 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,13 @@
103103
#define DEFAULT_IO_MACRO_IO2_IO3_MASK GENMASK(15, 4)
104104
#define IO_MACRO_IO2_IO3_SWAP 0x4640
105105

106-
#ifdef CONFIG_CONSOLE_POLL
107-
#define CONSOLE_RX_BYTES_PW 1
108-
#else
109-
#define CONSOLE_RX_BYTES_PW 4
110-
#endif
106+
struct qcom_geni_private_data {
107+
/* NOTE: earlycon port will have NULL here */
108+
struct uart_driver *drv;
109+
110+
u32 poll_cached_bytes;
111+
unsigned int poll_cached_bytes_cnt;
112+
};
111113

112114
struct qcom_geni_serial_port {
113115
struct uart_port uport;
@@ -129,6 +131,8 @@ struct qcom_geni_serial_port {
129131
int wakeup_irq;
130132
bool rx_tx_swap;
131133
bool cts_rts_swap;
134+
135+
struct qcom_geni_private_data private_data;
132136
};
133137

134138
static const struct uart_ops qcom_geni_console_pops;
@@ -264,8 +268,9 @@ static bool qcom_geni_serial_poll_bit(struct uart_port *uport,
264268
unsigned int baud;
265269
unsigned int fifo_bits;
266270
unsigned long timeout_us = 20000;
271+
struct qcom_geni_private_data *private_data = uport->private_data;
267272

268-
if (uport->private_data) {
273+
if (private_data->drv) {
269274
port = to_dev_port(uport, uport);
270275
baud = port->baud;
271276
if (!baud)
@@ -331,23 +336,42 @@ static void qcom_geni_serial_abort_rx(struct uart_port *uport)
331336
}
332337

333338
#ifdef CONFIG_CONSOLE_POLL
339+
334340
static int qcom_geni_serial_get_char(struct uart_port *uport)
335341
{
336-
u32 rx_fifo;
342+
struct qcom_geni_private_data *private_data = uport->private_data;
337343
u32 status;
344+
u32 word_cnt;
345+
int ret;
346+
347+
if (!private_data->poll_cached_bytes_cnt) {
348+
status = readl(uport->membase + SE_GENI_M_IRQ_STATUS);
349+
writel(status, uport->membase + SE_GENI_M_IRQ_CLEAR);
338350

339-
status = readl(uport->membase + SE_GENI_M_IRQ_STATUS);
340-
writel(status, uport->membase + SE_GENI_M_IRQ_CLEAR);
351+
status = readl(uport->membase + SE_GENI_S_IRQ_STATUS);
352+
writel(status, uport->membase + SE_GENI_S_IRQ_CLEAR);
341353

342-
status = readl(uport->membase + SE_GENI_S_IRQ_STATUS);
343-
writel(status, uport->membase + SE_GENI_S_IRQ_CLEAR);
354+
status = readl(uport->membase + SE_GENI_RX_FIFO_STATUS);
355+
word_cnt = status & RX_FIFO_WC_MSK;
356+
if (!word_cnt)
357+
return NO_POLL_CHAR;
344358

345-
status = readl(uport->membase + SE_GENI_RX_FIFO_STATUS);
346-
if (!(status & RX_FIFO_WC_MSK))
347-
return NO_POLL_CHAR;
359+
if (word_cnt == 1 && (status & RX_LAST))
360+
private_data->poll_cached_bytes_cnt =
361+
(status & RX_LAST_BYTE_VALID_MSK) >>
362+
RX_LAST_BYTE_VALID_SHFT;
363+
else
364+
private_data->poll_cached_bytes_cnt = 4;
348365

349-
rx_fifo = readl(uport->membase + SE_GENI_RX_FIFOn);
350-
return rx_fifo & 0xff;
366+
private_data->poll_cached_bytes =
367+
readl(uport->membase + SE_GENI_RX_FIFOn);
368+
}
369+
370+
private_data->poll_cached_bytes_cnt--;
371+
ret = private_data->poll_cached_bytes & 0xff;
372+
private_data->poll_cached_bytes >>= 8;
373+
374+
return ret;
351375
}
352376

353377
static void qcom_geni_serial_poll_put_char(struct uart_port *uport,
@@ -837,13 +861,11 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport)
837861
u32 proto;
838862
u32 pin_swap;
839863

840-
if (uart_console(uport)) {
864+
if (uart_console(uport))
841865
port->tx_bytes_pw = 1;
842-
port->rx_bytes_pw = CONSOLE_RX_BYTES_PW;
843-
} else {
866+
else
844867
port->tx_bytes_pw = 4;
845-
port->rx_bytes_pw = 4;
846-
}
868+
port->rx_bytes_pw = 4;
847869

848870
proto = geni_se_read_proto(&port->se);
849871
if (proto != GENI_SE_UART) {
@@ -1139,6 +1161,8 @@ static int qcom_geni_serial_earlycon_exit(struct console *con)
11391161
return 0;
11401162
}
11411163

1164+
static struct qcom_geni_private_data earlycon_private_data;
1165+
11421166
static int __init qcom_geni_serial_earlycon_setup(struct earlycon_device *dev,
11431167
const char *opt)
11441168
{
@@ -1154,6 +1178,8 @@ static int __init qcom_geni_serial_earlycon_setup(struct earlycon_device *dev,
11541178
if (!uport->membase)
11551179
return -EINVAL;
11561180

1181+
uport->private_data = &earlycon_private_data;
1182+
11571183
memset(&se, 0, sizeof(se));
11581184
se.base = uport->membase;
11591185
if (geni_se_read_proto(&se) != GENI_SE_UART)
@@ -1172,6 +1198,7 @@ static int __init qcom_geni_serial_earlycon_setup(struct earlycon_device *dev,
11721198
qcom_geni_serial_poll_tx_done(uport);
11731199
qcom_geni_serial_abort_rx(uport);
11741200
geni_se_config_packing(&se, BITS_PER_BYTE, 1, false, true, false);
1201+
geni_se_config_packing(&se, BITS_PER_BYTE, 4, false, false, true);
11751202
geni_se_init(&se, DEF_FIFO_DEPTH_WORDS / 2, DEF_FIFO_DEPTH_WORDS - 2);
11761203
geni_se_select_mode(&se, GENI_SE_FIFO);
11771204

@@ -1396,7 +1423,8 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
13961423
return ret;
13971424
}
13981425

1399-
uport->private_data = drv;
1426+
port->private_data.drv = drv;
1427+
uport->private_data = &port->private_data;
14001428
platform_set_drvdata(pdev, port);
14011429
port->handle_rx = console ? handle_rx_console : handle_rx_uart;
14021430

@@ -1442,7 +1470,7 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
14421470
static int qcom_geni_serial_remove(struct platform_device *pdev)
14431471
{
14441472
struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
1445-
struct uart_driver *drv = port->uport.private_data;
1473+
struct uart_driver *drv = port->private_data.drv;
14461474

14471475
if (port->se.has_opp_table)
14481476
dev_pm_opp_of_remove_table(&pdev->dev);
@@ -1458,16 +1486,18 @@ static int __maybe_unused qcom_geni_serial_sys_suspend(struct device *dev)
14581486
{
14591487
struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
14601488
struct uart_port *uport = &port->uport;
1489+
struct qcom_geni_private_data *private_data = uport->private_data;
14611490

1462-
return uart_suspend_port(uport->private_data, uport);
1491+
return uart_suspend_port(private_data->drv, uport);
14631492
}
14641493

14651494
static int __maybe_unused qcom_geni_serial_sys_resume(struct device *dev)
14661495
{
14671496
struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
14681497
struct uart_port *uport = &port->uport;
1498+
struct qcom_geni_private_data *private_data = uport->private_data;
14691499

1470-
return uart_resume_port(uport->private_data, uport);
1500+
return uart_resume_port(private_data->drv, uport);
14711501
}
14721502

14731503
static const struct dev_pm_ops qcom_geni_serial_pm_ops = {

0 commit comments

Comments
 (0)