git: 6aa2c651df0b - main - rk_spi: Do not change the clock with the driver lock held
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
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);