[RFC PATCH 1/2] spi: Add multiple CS support for a single SPI device

Amit Kumar Mahapatra amit.kumar-mahapatra at xilinx.com
Mon Jun 6 04:26:06 PDT 2022


For supporting multiple CS the SPI device need to be aware of all the CS
values. So the "chip_select" member in the spi_device structure is now an
array that holds all the CS values.

spi_device structure now has a "cs_index_mask" member. This acts as an
index to the chip_select array. If nth bit of spi->cs_index_mask is set then
the driver would assert spi->chip_slect[n].

When flashes are connected in stacked mode SPI-NOR will enable the required
chip select by setting the appropriate bit in spi->cs_index_mask.

In parallel connection both the flashes need to be asserted simultaneously,
this can be achieved by enabling 0th(CS0) & 1st(CS1) bit in spi->cs_index_mask.

Signed-off-by: Amit Kumar Mahapatra <amit.kumar-mahapatra at xilinx.com>
---
 drivers/spi/spi-zynqmp-gqspi.c | 30 ++++++++++++++++++++++++++----
 drivers/spi/spi.c              | 10 +++++++---
 include/linux/spi/spi.h        | 10 +++++++++-
 3 files changed, 42 insertions(+), 8 deletions(-)

diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c
index c760aac070e5..2535a8bca4da 100644
--- a/drivers/spi/spi-zynqmp-gqspi.c
+++ b/drivers/spi/spi-zynqmp-gqspi.c
@@ -136,6 +136,11 @@
 
 #define GQSPI_MAX_NUM_CS	2	/* Maximum number of chip selects */
 
+#define GQSPI_SELECT_LOWER_CS	BIT(0)
+#define GQSPI_SELECT_UPPER_CS	BIT(1)
+#define GQSPI_SELECT_BOTH_CS	(GQSPI_SELECT_LOWER_CS | \
+				 GQSPI_SELECT_UPPER_CS)
+				 
 #define SPI_AUTOSUSPEND_TIMEOUT		3000
 enum mode_type {GQSPI_MODE_IO, GQSPI_MODE_DMA};
 
@@ -361,16 +366,33 @@ static void zynqmp_qspi_chipselect(struct spi_device *qspi, bool is_high)
 	struct zynqmp_qspi *xqspi = spi_master_get_devdata(qspi->master);
 	ulong timeout;
 	u32 genfifoentry = 0, statusreg;
+	u8 cs_index_mask = qspi->cs_index_mask;
 
 	genfifoentry |= GQSPI_GENFIFO_MODE_SPI;
 
 	if (!is_high) {
-		if (!qspi->chip_select) {
-			xqspi->genfifobus = GQSPI_GENFIFO_BUS_LOWER;
-			xqspi->genfifocs = GQSPI_GENFIFO_CS_LOWER;
-		} else {
+		/*
+		 * GQSPI controller only supports two chip selects,
+		 * CS0 and CS1
+		 */
+		if (cs_index_mask & GQSPI_SELECT_BOTH_CS) {
+
+			xqspi->genfifobus = GQSPI_GENFIFO_BUS_LOWER |
+				GQSPI_GENFIFO_BUS_UPPER;
+			xqspi->genfifocs = GQSPI_GENFIFO_CS_LOWER |
+				GQSPI_GENFIFO_CS_UPPER;
+
+		} else if ((cs_index_mask & GQSPI_SELECT_UPPER_CS) &&
+			   (qspi->chip_select[GQSPI_SELECT_UPPER_CS - 1])) {
+
 			xqspi->genfifobus = GQSPI_GENFIFO_BUS_UPPER;
 			xqspi->genfifocs = GQSPI_GENFIFO_CS_UPPER;
+
+		} else if ((cs_index_mask & GQSPI_SELECT_LOWER_CS) &&
+			   (!qspi->chip_select[GQSPI_SELECT_LOWER_CS - 1])) {
+
+			xqspi->genfifobus = GQSPI_GENFIFO_BUS_LOWER;
+			xqspi->genfifocs = GQSPI_GENFIFO_CS_LOWER;
 		}
 		genfifoentry |= xqspi->genfifobus;
 		genfifoentry |= xqspi->genfifocs;
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 2e6d6bbeb784..d3077874e6e8 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -2082,6 +2082,8 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
 {
 	u32 value;
 	int rc;
+	u32 cs[SPI_CS_CNT_MAX];
+	u8 idx;
 
 	/* Mode (clock phase/polarity/etc.) */
 	if (of_property_read_bool(nc, "spi-cpha"))
@@ -2154,13 +2156,15 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
 	}
 
 	/* Device address */
-	rc = of_property_read_u32(nc, "reg", &value);
-	if (rc) {
+	rc = of_property_read_variable_u32_array(nc, "reg", &cs[0],
+						 1, SPI_CS_CNT_MAX);
+	if (rc < 0) {
 		dev_err(&ctlr->dev, "%pOF has no valid 'reg' property (%d)\n",
 			nc, rc);
 		return rc;
 	}
-	spi->chip_select = value;
+	for(idx = 0; idx < rc; idx++)
+		spi->chip_select[idx] = cs[idx];
 
 	/* Device speed */
 	if (!of_property_read_u32(nc, "spi-max-frequency", &value))
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 5f8c063ddff4..e930d987f3c2 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -18,6 +18,9 @@
 #include <uapi/linux/spi/spi.h>
 #include <linux/acpi.h>
 
+/* Max no. of CS supported per spi device */
+#define SPI_CS_CNT_MAX	2
+
 struct dma_chan;
 struct software_node;
 struct ptp_system_timestamp;
@@ -148,6 +151,7 @@ extern int spi_delay_exec(struct spi_delay *_delay, struct spi_transfer *xfer);
  *	deasserted. If @cs_change_delay is used from @spi_transfer, then the
  *	two delays will be added up.
  * @statistics: statistics for the spi_device
+ * @cs_index_mask: Bit mask of the active chipselect(s) in the chipselect array
  *
  * A @spi_device is used to interchange data between an SPI slave
  * (usually a discrete chip) and CPU memory.
@@ -163,7 +167,7 @@ struct spi_device {
 	struct spi_controller	*controller;
 	struct spi_controller	*master;	/* compatibility layer */
 	u32			max_speed_hz;
-	u8			chip_select;
+	u8			chip_select[SPI_CS_CNT_MAX];
 	u8			bits_per_word;
 	bool			rt;
 #define SPI_NO_TX	BIT(31)		/* no transmit wire */
@@ -194,6 +198,10 @@ struct spi_device {
 	/* the statistics */
 	struct spi_statistics	statistics;
 
+	/* Bit mask of the chipselect(s) that the driver
+	 * need to use form the chipselect array.
+	 */
+	u8			cs_index_mask : 2;
 	/*
 	 * likely need more hooks for more protocol options affecting how
 	 * the controller talks to each chip, like:
-- 
2.17.1




More information about the linux-arm-kernel mailing list