diff --git a/Documentation/devicetree/bindings/serial/apple,dockchannel-uart.yaml b/Documentation/devicetree/bindings/serial/apple,dockchannel-uart.yaml index 07a7f1fc8c71b..b52769189908c 100644 --- a/Documentation/devicetree/bindings/serial/apple,dockchannel-uart.yaml +++ b/Documentation/devicetree/bindings/serial/apple,dockchannel-uart.yaml @@ -54,6 +54,16 @@ properties: property of the channel's Apple device tree node. The interrupt, if described, is not used. + apple,tx-never-wait: + type: boolean + description: + Output never waits for the host to read the channel. The channel drains + only while a debug USB host reads it, so with none attached its TX FIFO + stays full. Console writes send what fits and drop the rest, a closing + port does not wait for its output, and output queued with no room for + two drain ticks is dropped. For boards that boot unattended with nothing + reading the channel. + required: - compatible - reg diff --git a/drivers/tty/serial/apple_dockchannel.h b/drivers/tty/serial/apple_dockchannel.h new file mode 100644 index 0000000000000..97fcccc6a5a57 --- /dev/null +++ b/drivers/tty/serial/apple_dockchannel.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* + * Apple DockChannel UART: console output shared by the earlycon and the driver. + */ +#ifndef _APPLE_DOCKCHANNEL_H +#define _APPLE_DOCKCHANNEL_H + +#include +#include +#include + +#define DOCKCHANNEL_TX8 0x04 +#define DOCKCHANNEL_TX_FREE 0x14 +#define DOCKCHANNEL_TX_RETRIES 10000 + +/* + * Whether console and tty output never wait for a reader: the DT property + * apple,tx-never-wait on the UART node, which only J775D (t6050-j775d.dts) + * sets, where unattended boots run with nothing reading the channel. Every + * other DockChannel Mac keeps waiting for room as before. Before widening + * it (or making it the default), check on one other DockChannel Mac with a + * slow attached reader (m1n1 proxy, USB debug) that long printk lines and tty + * output still arrive whole. + */ +#define DOCKCHANNEL_NEVER_WAIT "apple,tx-never-wait" + +/* Waiting for room, at most DOCKCHANNEL_TX_RETRIES reads per character. */ +static inline void dockchannel_wait_putc(void __iomem *data, unsigned char c) +{ + unsigned int retries = DOCKCHANNEL_TX_RETRIES; + + while (readl(data + DOCKCHANNEL_TX_FREE) == 0 && --retries) + cpu_relax(); + if (!retries) + return; + + writel(c, data + DOCKCHANNEL_TX8); +} + +/* + * With DOCKCHANNEL_NEVER_WAIT: the channel drains only while a debug USB host + * reads it; with none, the TX FIFO stays full for good. So console output + * never waits for room: each + * write sends what TX_FREE says fits and drops the rest. One write reads + * TX_FREE again when the room it read runs out, and stops reading once it + * reads 0. Callers serialize writes, so the room read stays true. + */ +struct dockchannel_tx { + u32 room; + bool full; +}; + +static inline void dockchannel_tx_begin(struct dockchannel_tx *tx) +{ + tx->room = 0; + tx->full = false; +} + +static inline void dockchannel_console_putc(void __iomem *data, + struct dockchannel_tx *tx, + unsigned char c) +{ + if (!tx->room && !tx->full) { + tx->room = readl(data + DOCKCHANNEL_TX_FREE); + tx->full = !tx->room; + } + if (!tx->room) + return; + tx->room--; + writel(c, data + DOCKCHANNEL_TX8); +} + +#endif diff --git a/drivers/tty/serial/apple_dockchannel_early_uart.c b/drivers/tty/serial/apple_dockchannel_early_uart.c index 1048dde41d794..6c427189d34be 100644 --- a/drivers/tty/serial/apple_dockchannel_early_uart.c +++ b/drivers/tty/serial/apple_dockchannel_early_uart.c @@ -8,22 +8,33 @@ #include #include +#include +#include #include -#define DOCKCHANNEL_TX8 0x04 -#define DOCKCHANNEL_TX_FREE 0x14 -#define DOCKCHANNEL_TX_RETRIES 10000 +#include "apple_dockchannel.h" + +static struct dockchannel_tx dockchannel_early_tx; +static bool dockchannel_early_never_wait; static void dockchannel_uart_putc(struct uart_port *port, unsigned char c) { - unsigned int retries = DOCKCHANNEL_TX_RETRIES; + if (dockchannel_early_never_wait) + dockchannel_console_putc(port->membase, &dockchannel_early_tx, c); + else + dockchannel_wait_putc(port->membase, c); +} - while (readl(port->membase + DOCKCHANNEL_TX_FREE) == 0 && --retries) - cpu_relax(); - if (!retries) - return; +/* The DT's DockChannel UART node asks for DOCKCHANNEL_NEVER_WAIT (flat DT). */ +static bool __init dockchannel_uart_early_never_wait(void) +{ + const void *fdt = initial_boot_params; + int node; - writel(c, port->membase + DOCKCHANNEL_TX8); + if (!fdt) + return false; + node = fdt_node_offset_by_compatible(fdt, -1, "apple,dockchannel-uart"); + return node >= 0 && fdt_getprop(fdt, node, DOCKCHANNEL_NEVER_WAIT, NULL); } static void dockchannel_uart_early_write(struct console *con, const char *s, @@ -31,6 +42,7 @@ static void dockchannel_uart_early_write(struct console *con, const char *s, { struct earlycon_device *dev = con->data; + dockchannel_tx_begin(&dockchannel_early_tx); uart_console_write(&dev->port, s, n, dockchannel_uart_putc); } @@ -42,6 +54,7 @@ static int __init dockchannel_uart_early_setup(struct earlycon_device *device, if (!port->mapbase) return -ENODEV; + dockchannel_early_never_wait = dockchannel_uart_early_never_wait(); device->con->write = dockchannel_uart_early_write; return 0; } diff --git a/drivers/tty/serial/apple_dockchannel_uart.c b/drivers/tty/serial/apple_dockchannel_uart.c index c015d916ac432..d4255cb949848 100644 --- a/drivers/tty/serial/apple_dockchannel_uart.c +++ b/drivers/tty/serial/apple_dockchannel_uart.c @@ -16,6 +16,8 @@ #include #include +#include "apple_dockchannel.h" + #define FIFO_SIZE 0x800 #define IRQ_MASK 0x0 @@ -44,7 +46,13 @@ * that is about 115 bytes per poll, far below the FIFO size. */ #define SW_DRAIN_MS 10 -#define TX_RETRIES 10000 +/* + * TX never waits for FIFO room: what does not fit stays in the xmit buffer + * and the timer sends it as room appears. After this many ticks with no room + * at all, nothing is reading the channel: drop what is queued, so writers do + * not block on a reader that may never come. + */ +#define TX_STUCK_TICKS 2 struct dcuart_port { struct uart_port port; @@ -57,6 +65,12 @@ struct dcuart_port { struct timer_list drain_timer; /* Serializes IRQ mask updates and RX FIFO draining. */ spinlock_t lock; + /* DOCKCHANNEL_NEVER_WAIT: TX never waits for room (J775D only). */ + bool never_wait; + /* One console write's view of the TX FIFO (dockchannel_console_putc). */ + struct dockchannel_tx console_tx; + /* Timer ticks in a row that found TX output queued and no FIFO room. */ + unsigned int tx_stuck_ticks; }; static struct uart_driver dcuart_drv; @@ -97,22 +111,38 @@ static void dcuart_irq_disable(struct dcuart_port *dcuart, u32 bits) static void dcuart_putc(struct uart_port *port, unsigned char c) { - unsigned int retries = TX_RETRIES; + struct dcuart_port *dcuart = container_of(port, struct dcuart_port, port); - while (readl(port->membase + DATA_TX_FREE) == 0 && --retries) - cpu_relax(); - if (!retries) - return; - - writel(c, port->membase + DATA_TX8); + if (dcuart->never_wait) + dockchannel_console_putc(port->membase, &dcuart->console_tx, c); + else + dockchannel_wait_putc(port->membase, c); } static void dcuart_write(struct console *co, const char *s, unsigned int n) { struct dcuart_port *dcuart = dcuart_ports[co->index]; + struct uart_port *port = &dcuart->port; + unsigned long flags; + bool locked = true; + + if (!dcuart->never_wait) { + uart_console_write(port, s, n, dcuart_putc); + return; + } + + /* The TX path writes the same FIFO, and the room read must stay true. */ + if (oops_in_progress) + locked = uart_port_trylock_irqsave(port, &flags); + else + uart_port_lock_irqsave(port, &flags); + + dockchannel_tx_begin(&dcuart->console_tx); + uart_console_write(port, s, n, dcuart_putc); - uart_console_write(&dcuart->port, s, n, dcuart_putc); + if (locked) + uart_port_unlock_irqrestore(port, flags); } static int dcuart_console_setup(struct console *co, char *options) @@ -198,39 +228,106 @@ static irqreturn_t dcuart_interrupt(int irq, void *data) return IRQ_HANDLED; } +/* + * Send what the TX FIFO has room for, without waiting. From the timer (tick), + * also count ticks without room and drop the queue after TX_STUCK_TICKS. + * Called with the port lock held. Returns the bytes left queued. + */ +static unsigned int dcuart_tx_chars(struct dcuart_port *dcuart, bool tick) +{ + struct uart_port *port = &dcuart->port; + struct tty_port *tport = &port->state->port; + unsigned int queued = kfifo_len(&tport->xmit_fifo), pending; + u8 ch; + + if (!queued && !port->x_char) + return 0; + + pending = uart_port_tx_limited(port, ch, readl(port->membase + DATA_TX_FREE), + true, writel(ch, port->membase + DATA_TX8), ({})); + if (!pending || pending < queued) { + dcuart->tx_stuck_ticks = 0; + return pending; + } + if (!tick || ++dcuart->tx_stuck_ticks < TX_STUCK_TICKS) + return pending; + + kfifo_reset_out(&tport->xmit_fifo); + dcuart->tx_stuck_ticks = 0; + uart_write_wakeup(port); + return 0; +} + static void dcuart_drain_timer(struct timer_list *t) { struct dcuart_port *dcuart = timer_container_of(dcuart, t, drain_timer); + struct uart_port *port = &dcuart->port; unsigned long flags; - bool got; + unsigned int pending; - spin_lock_irqsave(&dcuart->lock, flags); - got = dcuart_drain_rx(dcuart); - spin_unlock_irqrestore(&dcuart->lock, flags); + if (!dcuart->never_wait) { + bool got; - if (got) - tty_flip_buffer_push(&dcuart->port.state->port); - mod_timer(&dcuart->drain_timer, jiffies + msecs_to_jiffies(SW_DRAIN_MS)); + spin_lock_irqsave(&dcuart->lock, flags); + got = dcuart_drain_rx(dcuart); + spin_unlock_irqrestore(&dcuart->lock, flags); + + if (got) + tty_flip_buffer_push(&port->state->port); + mod_timer(&dcuart->drain_timer, jiffies + msecs_to_jiffies(SW_DRAIN_MS)); + return; + } + + if (dcuart->sw_drain) { + bool got; + + spin_lock_irqsave(&dcuart->lock, flags); + got = dcuart_drain_rx(dcuart); + spin_unlock_irqrestore(&dcuart->lock, flags); + + if (got) + tty_flip_buffer_push(&port->state->port); + } + + uart_port_lock_irqsave(port, &flags); + pending = dcuart_tx_chars(dcuart, true); + uart_port_unlock_irqrestore(port, flags); + + if (dcuart->sw_drain || pending) + mod_timer(&dcuart->drain_timer, jiffies + msecs_to_jiffies(SW_DRAIN_MS)); } +/* + * Bytes in the FIFO stay there for a host to read whenever it comes; a full + * FIFO means none is reading, not that a transmission is in progress. So + * report empty: a close or tcdrain must not wait for a reader (it waited + * twice the port timeout, 0.4 s, on every close of an unread channel). + */ static unsigned int dcuart_tx_empty(struct uart_port *port) { - return (readl(port->membase + DATA_TX_FREE) != 0) ? - TIOCSER_TEMT : - 0; + if (!port_to_dc(port)->never_wait) + return (readl(port->membase + DATA_TX_FREE) != 0) ? TIOCSER_TEMT : 0; + return TIOCSER_TEMT; } static void dcuart_start_tx(struct uart_port *port) { - char ch; - struct tty_port *tport = &port->state->port; + struct dcuart_port *dcuart = port_to_dc(port); + + if (!dcuart->never_wait) { + struct tty_port *tport = &port->state->port; + char ch; - if (!kfifo_is_empty(&tport->xmit_fifo)) { while (kfifo_get(&tport->xmit_fifo, &ch)) { dcuart_putc(port, ch); port->icount.tx++; } + return; } + + /* The rest goes from the timer, which the RX poll keeps running anyway. */ + if (dcuart_tx_chars(dcuart, false) && !timer_pending(&dcuart->drain_timer)) + mod_timer(&dcuart->drain_timer, jiffies + msecs_to_jiffies(SW_DRAIN_MS)); } static void dcuart_stop_tx(struct uart_port *port) @@ -258,6 +355,14 @@ static int dcuart_startup(struct uart_port *port) struct dcuart_port *dcuart = port_to_dc(port); unsigned long flags; + /* + * A close waits for the xmit buffer to empty, which on an unread + * channel takes until the timer drops it (systemd closes the console + * after every status line: 30 ms each). Never wait for a reader. + */ + if (dcuart->never_wait) + port->state->port.closing_wait = ASYNC_CLOSING_WAIT_NONE; + spin_lock_irqsave(&dcuart->lock, flags); dcuart_irq_disable(dcuart, dcuart->irq_tx | dcuart->irq_rx); writel_relaxed(1, dcuart->config_base + CONFIG_RX_THRESH); @@ -278,9 +383,10 @@ static void dcuart_shutdown(struct uart_port *port) struct dcuart_port *dcuart = port_to_dc(port); unsigned long flags; - if (dcuart->sw_drain) + /* With never_wait the timer also sends TX in IRQ mode. */ + if (dcuart->sw_drain || dcuart->never_wait) timer_delete_sync(&dcuart->drain_timer); - else + if (!dcuart->sw_drain) disable_irq(port->irq); spin_lock_irqsave(&dcuart->lock, flags); @@ -376,6 +482,7 @@ static int dcuart_probe(struct platform_device *pdev) dcuart->irq_tx = IRQ_TX(channel); dcuart->irq_rx = IRQ_RX(channel); dcuart->sw_drain = of_property_read_bool(pdev->dev.of_node, "apple,sw-drain"); + dcuart->never_wait = of_property_read_bool(pdev->dev.of_node, DOCKCHANNEL_NEVER_WAIT); timer_setup(&dcuart->drain_timer, dcuart_drain_timer, 0); dcuart->irq_base = devm_platform_ioremap_resource_byname(pdev, "irq");