summaryrefslogtreecommitdiffstats
path: root/drivers/spi
diff options
context:
space:
mode:
authorWilliam Zhang <william.zhang@broadcom.com>2023-02-09 21:02:40 +0100
committerMark Brown <broonie@kernel.org>2023-02-10 22:36:40 +0100
commitc00d5e93ea018786d98670fc1d0dab4c36c2217c (patch)
tree836be138f9e24dcfec5ba35cbdea81232f37802e /drivers/spi
parentspi: export spi_transfer_cs_change_delay_exec function (diff)
downloadlinux-c00d5e93ea018786d98670fc1d0dab4c36c2217c.tar.xz
linux-c00d5e93ea018786d98670fc1d0dab4c36c2217c.zip
spi: bcm63xx-hsspi: Handle cs_change correctly
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> Link: https://lore.kernel.org/r/20230209200246.141520-10-william.zhang@broadcom.com Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'drivers/spi')
-rw-r--r--drivers/spi/spi-bcm63xx-hsspi.c33
1 files changed, 23 insertions, 10 deletions
diff --git a/drivers/spi/spi-bcm63xx-hsspi.c b/drivers/spi/spi-bcm63xx-hsspi.c
index 55cbe7deba08..af51488659b8 100644
--- a/drivers/spi/spi-bcm63xx-hsspi.c
+++ b/drivers/spi/spi-bcm63xx-hsspi.c
@@ -219,7 +219,8 @@ static int bcm63xx_hsspi_do_txrx(struct spi_device *spi, struct spi_transfer *t)
unsigned long limit;
bcm63xx_hsspi_set_clk(bs, spi, t->speed_hz);
- bcm63xx_hsspi_set_cs(bs, spi->chip_select, true);
+ if (!t->cs_off)
+ bcm63xx_hsspi_set_cs(bs, spi->chip_select, true);
if (tx && rx)
opcode = HSSPI_OP_READ_WRITE;
@@ -338,7 +339,7 @@ static int bcm63xx_hsspi_transfer_one(struct spi_master *master,
struct spi_device *spi = msg->spi;
int status = -EINVAL;
int dummy_cs;
- u32 reg;
+ bool keep_cs = false;
mutex_lock(&bs->msg_mutex);
/* This controller does not support keeping CS active during idle.
@@ -367,16 +368,28 @@ static int bcm63xx_hsspi_transfer_one(struct spi_master *master,
spi_transfer_delay_exec(t);
- if (t->cs_change)
- bcm63xx_hsspi_set_cs(bs, spi->chip_select, false);
+ /* use existing cs change logic from spi_transfer_one_message */
+ if (t->cs_change) {
+ if (list_is_last(&t->transfer_list, &msg->transfers)) {
+ keep_cs = true;
+ } else {
+ if (!t->cs_off)
+ bcm63xx_hsspi_set_cs(bs, spi->chip_select, false);
+
+ spi_transfer_cs_change_delay_exec(msg, t);
+
+ if (!list_next_entry(t, transfer_list)->cs_off)
+ bcm63xx_hsspi_set_cs(bs, spi->chip_select, true);
+ }
+ } else if (!list_is_last(&t->transfer_list, &msg->transfers) &&
+ t->cs_off != list_next_entry(t, transfer_list)->cs_off) {
+ bcm63xx_hsspi_set_cs(bs, spi->chip_select, t->cs_off);
+ }
}
- 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 (status || !keep_cs)
+ bcm63xx_hsspi_set_cs(bs, spi->chip_select, false);
mutex_unlock(&bs->msg_mutex);
msg->status = status;