From 42ba050ce7e7bea7f7fe1ce497530d8c4b0b38e2 Mon Sep 17 00:00:00 2001 From: Satya Benson Date: Tue, 10 Nov 2026 17:18:19 -0500 Subject: [PATCH] spi: apple: restore thread-driven refill for transmit-only transfers The IRQ FIFO engine stalls the Mesa sensor firmware upload on J613: hundreds of TX words remain and there is no RX half to drive progress. Moving the FIFO acknowledgment before service also failed on hardware. The pre-IRQ-engine driver's wake/refill/rearm handshake restored sensor patch delivery and fingerprint match on the same kernel. Use that handshake for transmit-only transfers. Mask interrupts and wake the thread without acknowledging the FIFO threshold; the thread moves data and rearms the interrupt until TX completion. Synchronize the IRQ before servicing shared transfer state or returning after timeout. Keep in-handler FIFO service for receive and full-duplex transfers, where avoiding scheduler latency remains useful. Preserve the existing IRQ KUnit cases and add coverage of the TX-only wake/mask handshake. This contains the regression without reverting the whole IRQ engine. It does not establish the underlying hardware stall mechanism. Hardware results cover the old full-driver override, not this exact mixed-mode implementation; the latter still needs J613 fingerprint and load tests. Fixes: 6c564f2a0162 ("spi: apple: Service the FIFOs from the interrupt handler") Link: https://github.com/aurora-silicon/linux/issues/1388 Signed-off-by: Satya Benson Assisted-by: LLM --- drivers/spi/spi-apple-test.c | 24 ++++++++++++++++++++++ drivers/spi/spi-apple.c | 39 ++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/drivers/spi/spi-apple-test.c b/drivers/spi/spi-apple-test.c index 2b8a87d2a2bf9..407a46dd3a1e3 100644 --- a/drivers/spi/spi-apple-test.c +++ b/drivers/spi/spi-apple-test.c @@ -177,8 +177,32 @@ static void apple_spi_test_irq_after_timeout(struct kunit *test) KUNIT_EXPECT_EQ(test, reg_read(spi, APPLE_SPI_IE_XFER), 0); } +/* The TX-only fallback masks the interrupt and leaves refill to the thread. */ +static void apple_spi_test_irq_wakes_tx_thread(struct kunit *test) +{ + struct apple_spi *spi = apple_spi_test_init(test); + u8 buf[32] = {}; + + spi->active = true; + spi->threaded_tx = true; + spi->tx_ptr = buf; + spi->remaining_tx = sizeof(buf); + spi->xfer_flags = APPLE_SPI_XFER_TXCOMPLETE; + apple_spi_test_fifo_level(spi, 8, 0); + reg_write(spi, APPLE_SPI_IE_FIFO, APPLE_SPI_FIFO_TXTHRESH); + reg_write(spi, APPLE_SPI_IF_FIFO, APPLE_SPI_FIFO_TXTHRESH); + reg_write(spi, APPLE_SPI_IE_XFER, APPLE_SPI_XFER_TXCOMPLETE); + + KUNIT_EXPECT_EQ(test, apple_spi_irq(0, spi), IRQ_HANDLED); + KUNIT_EXPECT_EQ(test, spi->remaining_tx, sizeof(buf)); + KUNIT_EXPECT_TRUE(test, completion_done(&spi->done)); + KUNIT_EXPECT_EQ(test, reg_read(spi, APPLE_SPI_IE_FIFO), 0); + KUNIT_EXPECT_EQ(test, reg_read(spi, APPLE_SPI_IE_XFER), 0); +} + static struct kunit_case apple_spi_test_cases[] = { KUNIT_CASE(apple_spi_test_irq_refills_tx), + KUNIT_CASE(apple_spi_test_irq_wakes_tx_thread), KUNIT_CASE(apple_spi_test_irq_drains_rx), KUNIT_CASE(apple_spi_test_irq_waits_for_both_halves), KUNIT_CASE(apple_spi_test_irq_completes_transfer), diff --git a/drivers/spi/spi-apple.c b/drivers/spi/spi-apple.c index 09cab7ada11a1..fefc559c81d09 100644 --- a/drivers/spi/spi-apple.c +++ b/drivers/spi/spi-apple.c @@ -152,6 +152,7 @@ struct apple_spi { */ spinlock_t lock; /* protects the state below and IE */ bool active; + bool threaded_tx; /* transmit-only fallback */ const void *tx_ptr; void *rx_ptr; u32 remaining_tx; @@ -488,6 +489,15 @@ static irqreturn_t apple_spi_irq(int irq, void *dev_id) return IRQ_NONE; } + /* Keep the working thread-driven handshake for transmit-only uploads. */ + if (spi->active && spi->threaded_tx) { + reg_write(spi, APPLE_SPI_IE_XFER, 0); + reg_write(spi, APPLE_SPI_IE_FIFO, 0); + complete(&spi->done); + spin_unlock(&spi->lock); + return IRQ_HANDLED; + } + if (spi->active) apple_spi_service(spi); reg_write(spi, APPLE_SPI_IF_FIFO, @@ -533,6 +543,7 @@ static int apple_spi_transfer_one(struct spi_controller *ctlr, struct spi_device spi->remaining_tx = t->tx_buf ? words : 0; spi->remaining_rx = t->rx_buf ? words : 0; spi->bytes_per_word = bytes_per_word; + spi->threaded_tx = t->tx_buf && !t->rx_buf; /* Reset FIFOs */ reg_write(spi, APPLE_SPI_CTRL, APPLE_SPI_CTRL_RX_RESET | APPLE_SPI_CTRL_TX_RESET); @@ -572,6 +583,34 @@ static int apple_spi_transfer_one(struct spi_controller *ctlr, struct spi_device /* Transmit and receive everything we can */ apple_spi_service(spi); } + } else if (spi->threaded_tx) { + /* + * Transmit-only uploads can lose FIFO progress with the IRQ + * engine. Use the pre-IRQ-engine wake/refill/rearm sequence; + * retain in-handler service for receive and duplex transfers. + */ + while (spi->xfer_flags) { + reinit_completion(&spi->done); + spin_lock_irqsave(&spi->lock, flags); + spi->active = true; + reg_write(spi, APPLE_SPI_IE_XFER, spi->xfer_flags); + reg_write(spi, APPLE_SPI_IE_FIFO, apple_spi_fifo_flags(spi)); + spin_unlock_irqrestore(&spi->lock, flags); + + if (!wait_for_completion_timeout(&spi->done, + msecs_to_jiffies(APPLE_SPI_TIMEOUT_MS))) + ret = -ETIMEDOUT; + + spin_lock_irqsave(&spi->lock, flags); + spi->active = false; + reg_write(spi, APPLE_SPI_IE_XFER, 0); + reg_write(spi, APPLE_SPI_IE_FIFO, 0); + spin_unlock_irqrestore(&spi->lock, flags); + synchronize_irq(spi->irq); + if (ret) + break; + apple_spi_service(spi); + } } else { /* Allow for the transfer itself on top of the usual timeout. */ unsigned long ms = APPLE_SPI_TIMEOUT_MS +