Skip to content

Commit cc4a0e5

Browse files
jhovoldgregkh
authored andcommitted
serial: qcom-geni: fix console corruption
The Qualcomm serial console implementation is broken and can lose characters when the serial port is also used for tty output. Specifically, the console code only waits for the current tx command to complete when all data has already been written to the fifo. When there are on-going longer transfers this often means that console output is lost when the console code inadvertently "hijacks" the current tx command instead of starting a new one. This can, for example, be observed during boot when console output that should have been interspersed with init output is truncated: [ 9.462317] qcom-snps-eusb2-hsphy fde000.phy: Registered Qcom-eUSB2 phy [ OK ] Found device KBG50ZNS256G KIOXIA Wi[ 9.471743ndows. [ 9.539915] xhci-hcd xhci-hcd.0.auto: xHCI Host Controller Add a new state variable to track how much data has been written to the fifo and use it to determine when the fifo and shift register are both empty. This is needed since there is currently no other known way to determine when the shift register is empty. This in turn allows the console code to interrupt long transfers without losing data. Note that the oops-in-progress case is similarly broken as it does not cancel any active command and also waits for the wrong status flag when attempting to drain the fifo (TX_FIFO_NOT_EMPTY_EN is only set when cancelling a command leaves data in the fifo). Fixes: c4f5287 ("tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP") Fixes: a1fee89 ("tty: serial: qcom_geni_serial: Fix softlock") Fixes: 9e957a1 ("serial: qcom-geni: Don't cancel/abort if we can't get the port lock") Cc: [email protected] # 4.17 Reviewed-by: Douglas Anderson <[email protected]> Tested-by: Nícolas F. R. A. Prado <[email protected]> Signed-off-by: Johan Hovold <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent b26d1ad commit cc4a0e5

File tree

1 file changed

+22
-23
lines changed

1 file changed

+22
-23
lines changed

drivers/tty/serial/qcom_geni_serial.c

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ struct qcom_geni_serial_port {
131131
bool brk;
132132

133133
unsigned int tx_remaining;
134+
unsigned int tx_queued;
134135
int wakeup_irq;
135136
bool rx_tx_swap;
136137
bool cts_rts_swap;
@@ -144,6 +145,8 @@ static const struct uart_ops qcom_geni_uart_pops;
144145
static struct uart_driver qcom_geni_console_driver;
145146
static struct uart_driver qcom_geni_uart_driver;
146147

148+
static void qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport);
149+
147150
static inline struct qcom_geni_serial_port *to_dev_port(struct uart_port *uport)
148151
{
149152
return container_of(uport, struct qcom_geni_serial_port, uport);
@@ -393,6 +396,14 @@ static void qcom_geni_serial_poll_put_char(struct uart_port *uport,
393396
#endif
394397

395398
#ifdef CONFIG_SERIAL_QCOM_GENI_CONSOLE
399+
static void qcom_geni_serial_drain_fifo(struct uart_port *uport)
400+
{
401+
struct qcom_geni_serial_port *port = to_dev_port(uport);
402+
403+
qcom_geni_serial_poll_bitfield(uport, SE_GENI_M_GP_LENGTH, GP_LENGTH,
404+
port->tx_queued);
405+
}
406+
396407
static void qcom_geni_serial_wr_char(struct uart_port *uport, unsigned char ch)
397408
{
398409
struct qcom_geni_private_data *private_data = uport->private_data;
@@ -468,7 +479,6 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s,
468479
struct qcom_geni_serial_port *port;
469480
bool locked = true;
470481
unsigned long flags;
471-
u32 geni_status;
472482

473483
WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS);
474484

@@ -482,34 +492,20 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s,
482492
else
483493
uart_port_lock_irqsave(uport, &flags);
484494

485-
geni_status = readl(uport->membase + SE_GENI_STATUS);
495+
if (qcom_geni_serial_main_active(uport)) {
496+
/* Wait for completion or drain FIFO */
497+
if (!locked || port->tx_remaining == 0)
498+
qcom_geni_serial_poll_tx_done(uport);
499+
else
500+
qcom_geni_serial_drain_fifo(uport);
486501

487-
if (!locked) {
488-
/*
489-
* We can only get here if an oops is in progress then we were
490-
* unable to get the lock. This means we can't safely access
491-
* our state variables like tx_remaining. About the best we
492-
* can do is wait for the FIFO to be empty before we start our
493-
* transfer, so we'll do that.
494-
*/
495-
qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
496-
M_TX_FIFO_NOT_EMPTY_EN, false);
497-
} else if ((geni_status & M_GENI_CMD_ACTIVE) && !port->tx_remaining) {
498-
/*
499-
* It seems we can't interrupt existing transfers if all data
500-
* has been sent, in which case we need to look for done first.
501-
*/
502-
qcom_geni_serial_poll_tx_done(uport);
502+
qcom_geni_serial_cancel_tx_cmd(uport);
503503
}
504504

505505
__qcom_geni_serial_console_write(uport, s, count);
506506

507-
508-
if (locked) {
509-
if (port->tx_remaining)
510-
qcom_geni_serial_setup_tx(uport, port->tx_remaining);
507+
if (locked)
511508
uart_port_unlock_irqrestore(uport, flags);
512-
}
513509
}
514510

515511
static void handle_rx_console(struct uart_port *uport, u32 bytes, bool drop)
@@ -690,6 +686,7 @@ static void qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport)
690686
writel(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR);
691687

692688
port->tx_remaining = 0;
689+
port->tx_queued = 0;
693690
}
694691

695692
static void qcom_geni_serial_handle_rx_fifo(struct uart_port *uport, bool drop)
@@ -916,6 +913,7 @@ static void qcom_geni_serial_handle_tx_fifo(struct uart_port *uport,
916913
if (!port->tx_remaining) {
917914
qcom_geni_serial_setup_tx(uport, pending);
918915
port->tx_remaining = pending;
916+
port->tx_queued = 0;
919917

920918
irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN);
921919
if (!(irq_en & M_TX_FIFO_WATERMARK_EN))
@@ -924,6 +922,7 @@ static void qcom_geni_serial_handle_tx_fifo(struct uart_port *uport,
924922
}
925923

926924
qcom_geni_serial_send_chunk_fifo(uport, chunk);
925+
port->tx_queued += chunk;
927926

928927
/*
929928
* The tx fifo watermark is level triggered and latched. Though we had

0 commit comments

Comments
 (0)