[PATCH 1/2] gpiolib: of: Only apply the SPI CS quirk to SPI peripherals
From: Maciej Andrzejewski ICEYE
Date: Mon Aug 10 2026 - 11:20:37 EST
The legacy SPI chip-select polarity quirk in of_gpio_flags_quirks() is
keyed on nothing but the property name "cs-gpios". That name is not
exclusive to SPI: nand-controller.yaml documents the very same property
for NAND controllers, and rawnand_dt_parse_gpio_cs() requests those
lines with gpiod_count(dev, "cs"), which gpiolib expands to "cs-gpios".
A NAND controller therefore has SPI chip-select semantics forced onto
its chip selects, and any chip node whose first reg cell matches a GPIO
index is silently flipped to active low. The NAND core requests the
descriptors GPIOD_OUT_HIGH and drivers assert with a logical 0, so the
inversion leaves the die permanently deselected.
The example in nand-controller.yaml is itself affected: it pairs a
native chip select with a GPIO one and gives the second chip a reg of 1,
which trips the quirk whenever CONFIG_SPI_MASTER is enabled. In-tree the
collision is real but latent. 36 board trees, all Atmel/Microchip at91,
give their nand@3 controller a cs-gpios line, and they escape only
because the sole child of those nodes is a partitions container with no
reg for the quirk to match against.
Device tree carries no bus type marker, so identify the bus from what
the peripheral advertises about itself. Properties of an SPI peripheral
are namespaced with "spi-" (spi-max-frequency, spi-cpol, spi-cs-high and
the rest of spi-peripheral-props.yaml), whereas a NAND chip node carries
only reg, nand-* and its partition table. Skip the quirk when the
matched child has no "spi-" prefixed property.
This is a convention rather than a guarantee: compatible and reg are the
only properties spi-controller.yaml makes mandatory for a peripheral, so
a minimal one may carry no "spi-" property at all. The next patch covers
those by looking at the controller instead.
Signed-off-by: Maciej Andrzejewski ICEYE <maciej.andrzejewski@xxxxxxxxxxx>
---
drivers/gpio/gpiolib-of.c | 28 ++++++++++++++++++++++++++--
1 file changed, 26 insertions(+), 2 deletions(-)
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index 940b566946ce..bd9a623f2ae1 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -340,6 +340,25 @@ static void of_gpio_set_polarity_by_property(const struct device_node *np,
}
}
+/*
+ * The legacy SPI chip select binding below is keyed on a property name that
+ * other subsystems reuse for the same purpose, notably NAND controllers (see
+ * Documentation/devicetree/bindings/mtd/nand-controller.yaml), whose chip
+ * selects carry no SPI polarity semantics. Device tree has no bus type
+ * marker, so go by what the peripheral advertises about itself: properties
+ * of an SPI peripheral are namespaced with "spi-".
+ */
+static bool of_gpio_child_is_spi_peripheral(const struct device_node *child)
+{
+ struct property *pp;
+
+ for_each_property_of_node(child, pp)
+ if (str_has_prefix(pp->name, "spi-"))
+ return true;
+
+ return false;
+}
+
static void of_gpio_flags_quirks(const struct device_node *np,
const char *propname,
enum of_gpio_flags *flags,
@@ -374,6 +393,11 @@ static void of_gpio_flags_quirks(const struct device_node *np,
if (ret)
continue;
if (cs == index) {
+ bool active_high;
+
+ if (!of_gpio_child_is_spi_peripheral(child))
+ break;
+
/*
* SPI children have active low chip selects
* by default. This can be specified negatively
@@ -386,8 +410,8 @@ static void of_gpio_flags_quirks(const struct device_node *np,
* conflict and the "spi-cs-high" flag will
* take precedence.
*/
- bool active_high = of_property_read_bool(child,
- "spi-cs-high");
+ active_high = of_property_read_bool(child,
+ "spi-cs-high");
of_gpio_quirk_polarity(child, active_high,
flags);
break;
--
2.50.1