[PATCH 2/6] spi: keep track of number of chipselects in spi_device

From: Jonas Gorski
Date: Sat Oct 12 2024 - 05:54:51 EST


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@xxxxxxxxx>
---
drivers/spi/spi.c | 29 +++++++++++++++++++++--------
include/linux/spi/spi.h | 4 +++-
2 files changed, 24 insertions(+), 9 deletions(-)

diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index ab38978e9c58517bb671d7bda017c8550cc82d58..15b2f4e02a685b2c778b27473289197ab08987d8 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -587,6 +587,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;
@@ -636,7 +637,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);
@@ -653,7 +654,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;
@@ -675,7 +676,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) {
@@ -690,7 +697,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;
@@ -718,7 +725,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]);
@@ -1025,7 +1032,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)
@@ -1083,8 +1090,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->mode & SPI_CS_HIGH)
@@ -2459,6 +2470,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]);

diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 8497f4747e24d4ecd85b74f49609ac1c82c73535..77511c7d40df7085644cecaae325c982fb306afa 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -136,6 +136,7 @@ extern void spi_transfer_cs_change_delay_exec(struct spi_message *msg,
* The spi_transfer.speed_hz can override this for each transfer.
* @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.
* @mode: The spi mode defines how data is clocked out and in.
* This may be changed by the device's driver.
* The "active low" default for chipselect mode can be overridden
@@ -186,6 +187,7 @@ struct spi_device {
struct spi_controller *controller;
u32 max_speed_hz;
u8 chip_select[SPI_CS_CNT_MAX];
+ u8 num_chipselect;
u8 bits_per_word;
bool rt;
#define SPI_NO_TX BIT(31) /* No transmit wire */
@@ -311,7 +313,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;
}

--
2.43.0