git: 6aa2c651df0b - main - rk_spi: Do not change the clock with the driver lock held

From: Emmanuel Vadot <manu_at_FreeBSD.org>
Date: Tue, 20 Oct 2026 11:47:40 UTC
The branch main has been updated by manu:

URL: https://cgit.FreeBSD.org/src/commit/?id=6aa2c651df0b0d9e0f8693dbab09d11ecea8e1f8

commit 6aa2c651df0b0d9e0f8693dbab09d11ecea8e1f8
Author:     Emmanuel Vadot <manu@FreeBSD.org>
AuthorDate: 2026-10-20 11:35:59 +0000
Commit:     Emmanuel Vadot <manu@FreeBSD.org>
CommitDate: 2026-10-20 11:47:20 +0000

    rk_spi: Do not change the clock with the driver lock held
    
    The clock framework can sleep, it takes the clock topology sx lock.  Calling
    clk_set_freq() and clk_get_freq() from rk_spi_hw_setup() with the driver
    mutex held triggers a witness warning about a sleepable lock acquired after
    a non-sleepable one, on the first transfer that raises the clock.
    
    Set the clock in a new function that is called before taking the lock, and
    only keep the register setup under the lock.
---
 sys/dev/spibus/controller/rockchip/rk_spi.c | 33 +++++++++++++++++++++++------
 1 file changed, 27 insertions(+), 6 deletions(-)

diff --git a/sys/dev/spibus/controller/rockchip/rk_spi.c b/sys/dev/spibus/controller/rockchip/rk_spi.c
index db650763f6e1..788615cc40b2 100644
--- a/sys/dev/spibus/controller/rockchip/rk_spi.c
+++ b/sys/dev/spibus/controller/rockchip/rk_spi.c
@@ -158,6 +158,31 @@ rk_spi_set_cs(struct rk_spi_softc *sc, uint32_t cs, bool active)
 	return (0);
 }
 
+/*
+ * Raise the SPI clock if needed.  The clock framework can sleep, so this must
+ * be called without the driver lock held.
+ */
+static void
+rk_spi_set_clock(struct rk_spi_softc *sc, uint32_t freq)
+{
+	uint64_t max_freq;
+
+	RK_SPI_LOCK(sc);
+	max_freq = sc->max_freq;
+	RK_SPI_UNLOCK(sc);
+
+	/* minimum divider is 2 */
+	if (max_freq >= freq * 2)
+		return;
+
+	clk_set_freq(sc->clk_spi, 2 * freq, CLK_SET_ROUND_DOWN);
+	clk_get_freq(sc->clk_spi, &max_freq);
+
+	RK_SPI_LOCK(sc);
+	sc->max_freq = max_freq;
+	RK_SPI_UNLOCK(sc);
+}
+
 static void
 rk_spi_hw_setup(struct rk_spi_softc *sc, uint32_t mode, uint32_t freq)
 {
@@ -173,12 +198,6 @@ rk_spi_hw_setup(struct rk_spi_softc *sc, uint32_t mode, uint32_t freq)
 	if (mode & SPIBUS_MODE_CPOL)
 		cr0 |= CTRLR0_SCPOL;
 
-	/* minimum divider is 2 */
-	if (sc->max_freq < freq*2) {
-		clk_set_freq(sc->clk_spi, 2 * freq, CLK_SET_ROUND_DOWN);
-		clk_get_freq(sc->clk_spi, &sc->max_freq);
-	}
-
 	div = ((sc->max_freq + freq - 1) / freq);
 	div = (div + 1) & 0xfffe;
 	RK_SPI_WRITE_4(sc, RK_SPI_BAUDR, div);
@@ -422,6 +441,8 @@ rk_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
 	spibus_get_clock(child, &clock);
 	spibus_get_mode(child, &mode);
 
+	rk_spi_set_clock(sc, clock);
+
 	RK_SPI_LOCK(sc);
 	rk_spi_hw_setup(sc, mode, clock);
 	rk_spi_enable_chip(sc, 1);