spi: keep track of number of chipselects in spi_device
authorJonas Gorski <jonas.gorski@gmail.com>
Mon, 15 Sep 2025 18:37:20 +0000 (20:37 +0200)
committerMark Brown <broonie@kernel.org>
Mon, 22 Sep 2025 08:29:41 +0000 (09:29 +0100)
There are several places where we need to iterate over a device's
chipselect. To be able to do it efficiently, store the number of
chipselects in spi_device, like we do for controllers.

Since we now use a device supplied value, add a check to make sure it
isn't more than we can support.

Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
Link: https://patch.msgid.link/20250915183725.219473-3-jonas.gorski@gmail.com
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/spi/spi.c
include/linux/spi/spi.h

index b07d6cd..6598fb8 100644 (file)
@@ -586,6 +586,7 @@ struct spi_device *spi_alloc_device(struct spi_controller *ctlr)
        spi->dev.bus = &spi_bus_type;
        spi->dev.release = spidev_release;
        spi->mode = ctlr->buswidth_override_bits;
+       spi->num_chipselect = 1;
 
        device_initialize(&spi->dev);
        return spi;
@@ -635,7 +636,7 @@ static inline int spi_dev_check_cs(struct device *dev,
        u8 idx_new;
 
        cs = spi_get_chipselect(spi, idx);
-       for (idx_new = new_idx; idx_new < SPI_CS_CNT_MAX; idx_new++) {
+       for (idx_new = new_idx; idx_new < new_spi->num_chipselect; idx_new++) {
                cs_new = spi_get_chipselect(new_spi, idx_new);
                if (is_valid_cs(cs) && is_valid_cs(cs_new) && cs == cs_new) {
                        dev_err(dev, "chipselect %u already in use\n", cs_new);
@@ -652,7 +653,7 @@ static int spi_dev_check(struct device *dev, void *data)
        int status, idx;
 
        if (spi->controller == new_spi->controller) {
-               for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
+               for (idx = 0; idx < spi->num_chipselect; idx++) {
                        status = spi_dev_check_cs(dev, spi, idx, new_spi, 0);
                        if (status)
                                return status;
@@ -674,7 +675,13 @@ static int __spi_add_device(struct spi_device *spi)
        int status, idx;
        u8 cs;
 
-       for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
+       if (spi->num_chipselect > SPI_CS_CNT_MAX) {
+               dev_err(dev, "num_cs %d > max %d\n", spi->num_chipselect,
+                       SPI_CS_CNT_MAX);
+               return -EOVERFLOW;
+       }
+
+       for (idx = 0; idx < spi->num_chipselect; idx++) {
                /* Chipselects are numbered 0..max; validate. */
                cs = spi_get_chipselect(spi, idx);
                if (is_valid_cs(cs) && cs >= ctlr->num_chipselect) {
@@ -689,7 +696,7 @@ static int __spi_add_device(struct spi_device *spi)
         * For example, spi->chip_select[0] != spi->chip_select[1] and so on.
         */
        if (!spi_controller_is_target(ctlr)) {
-               for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
+               for (idx = 0; idx < spi->num_chipselect; idx++) {
                        status = spi_dev_check_cs(dev, spi, idx, spi, idx + 1);
                        if (status)
                                return status;
@@ -717,7 +724,7 @@ static int __spi_add_device(struct spi_device *spi)
        if (ctlr->cs_gpiods) {
                u8 cs;
 
-               for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
+               for (idx = 0; idx < spi->num_chipselect; idx++) {
                        cs = spi_get_chipselect(spi, idx);
                        if (is_valid_cs(cs))
                                spi_set_csgpiod(spi, idx, ctlr->cs_gpiods[cs]);
@@ -1024,7 +1031,7 @@ static void spi_res_release(struct spi_controller *ctlr, struct spi_message *mes
 
 /*-------------------------------------------------------------------------*/
 #define spi_for_each_valid_cs(spi, idx)                                \
-       for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)              \
+       for (idx = 0; idx < spi->num_chipselect; idx++)         \
                if (!(spi->cs_index_mask & BIT(idx))) {} else
 
 static inline bool spi_is_last_cs(struct spi_device *spi)
@@ -1080,8 +1087,12 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
        trace_spi_set_cs(spi, activate);
 
        spi->controller->last_cs_index_mask = spi->cs_index_mask;
-       for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
-               spi->controller->last_cs[idx] = enable ? spi_get_chipselect(spi, 0) : SPI_INVALID_CS;
+       for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
+               if (enable && idx < spi->num_chipselect)
+                       spi->controller->last_cs[idx] = spi_get_chipselect(spi, 0);
+               else
+                       spi->controller->last_cs[idx] = SPI_INVALID_CS;
+       }
 
        spi->controller->last_cs_mode_high = spi->mode & SPI_CS_HIGH;
        if (spi->controller->last_cs_mode_high)
@@ -2452,6 +2463,8 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
                dev_err(&ctlr->dev, "SPI controller doesn't support multi CS\n");
                return -EINVAL;
        }
+
+       spi->num_chipselect = rc;
        for (idx = 0; idx < rc; idx++)
                spi_set_chipselect(spi, idx, cs[idx]);
 
index e9ea432..49c0482 100644 (file)
@@ -170,6 +170,7 @@ extern void spi_transfer_cs_change_delay_exec(struct spi_message *msg,
  *     two delays will be added up.
  * @chip_select: Array of physical chipselect, spi->chipselect[i] gives
  *     the corresponding physical CS for logical CS i.
+ * @num_chipselect: Number of physical chipselects used.
  * @cs_index_mask: Bit mask of the active chipselect(s) in the chipselect array
  * @cs_gpiod: Array of GPIO descriptors of the corresponding chipselect lines
  *     (optional, NULL when not using a GPIO line)
@@ -229,6 +230,7 @@ struct spi_device {
        struct spi_delay        cs_inactive;
 
        u8                      chip_select[SPI_CS_CNT_MAX];
+       u8                      num_chipselect;
 
        /*
         * Bit mask of the chipselect(s) that the driver need to use from
@@ -315,7 +317,7 @@ static inline bool spi_is_csgpiod(struct spi_device *spi)
 {
        u8 idx;
 
-       for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
+       for (idx = 0; idx < spi->num_chipselect; idx++) {
                if (spi_get_csgpiod(spi, idx))
                        return true;
        }