[08/16] spi: bcm63xx-hsspi: Handle cs_change correctly

Message ID 20230106200809.330769-9-william.zhang@broadcom.com
State New
Headers
Series spi: bcm63xx-hsspi: driver and doc updates |

Commit Message

William Zhang Jan. 6, 2023, 8:08 p.m. UTC
  The kernel SPI interface includes the cs_change flag that alters how
the CS behaves.

If we're in the middle of transfers, it tells us to unselect the
CS momentarily since the target device requires that.

If we're at the end of a transfer, it tells us to keep the CS
selected, perhaps because the next transfer is likely targeted
to the same device.

We implement this scheme in the HSSPI driver in this change.

Prior to this change, the CS would toggle momentarily if cs_change
was set for the last transfer. This can be ignored by some or
most devices, but the Microchip TPM2 device does not ignore it.

With the change, the behavior is corrected and the 'glitch' is
eliminated.

Signed-off-by: Kursad Oney <kursad.oney@broadcom.com>
Signed-off-by: William Zhang <william.zhang@broadcom.com>
---

 drivers/spi/spi-bcm63xx-hsspi.c | 28 +++++++++++++++++++++-------
 1 file changed, 21 insertions(+), 7 deletions(-)
  

Patch

diff --git a/drivers/spi/spi-bcm63xx-hsspi.c b/drivers/spi/spi-bcm63xx-hsspi.c
index 2b4cdf7e7002..f2b1b83bd6d4 100644
--- a/drivers/spi/spi-bcm63xx-hsspi.c
+++ b/drivers/spi/spi-bcm63xx-hsspi.c
@@ -290,6 +290,7 @@  static int bcm63xx_hsspi_transfer_one(struct spi_master *master,
 	int status = -EINVAL;
 	int dummy_cs;
 	u32 reg;
+	bool restore_polarity = true;
 
 	/* This controller does not support keeping CS active during idle.
 	 * To work around this, we use the following ugly hack:
@@ -317,16 +318,29 @@  static int bcm63xx_hsspi_transfer_one(struct spi_master *master,
 
 		spi_transfer_delay_exec(t);
 
-		if (t->cs_change)
+		/*
+		 * cs_change rules:
+		 * (1) cs_change = 0 && last_xfer = 0:
+		 *     Do not touch the CS. On to the next xfer.
+		 * (2) cs_change = 1 && last_xfer = 0:
+		 *     Set cs = false before the next xfer.
+		 * (3) cs_change = 0 && last_xfer = 1:
+		 *     We want CS to be deactivated. So do NOT set cs = false,
+		 *     instead just restore the original polarity. This has the
+		 *     same effect of deactivating the CS.
+		 * (4) cs_change = 1 && last_xfer = 1:
+		 *     We want to keep CS active. So do NOT set cs = false, and
+		 *     make sure we do NOT reverse polarity.
+		 */
+		if (t->cs_change && !list_is_last(&t->transfer_list, &msg->transfers))
 			bcm63xx_hsspi_set_cs(bs, spi->chip_select, false);
+
+		restore_polarity = !t->cs_change;
 	}
 
-	mutex_lock(&bs->bus_mutex);
-	reg = __raw_readl(bs->regs + HSSPI_GLOBAL_CTRL_REG);
-	reg &= ~GLOBAL_CTRL_CS_POLARITY_MASK;
-	reg |= bs->cs_polarity;
-	__raw_writel(reg, bs->regs + HSSPI_GLOBAL_CTRL_REG);
-	mutex_unlock(&bs->bus_mutex);
+	bcm63xx_hsspi_set_cs(bs, dummy_cs, false);
+	if (restore_polarity)
+		bcm63xx_hsspi_set_cs(bs, spi->chip_select, false);
 
 	msg->status = status;
 	spi_finalize_current_message(master);