All of lore.kernel.org
 help / color / mirror / Atom feed
From: William Zhang <william.zhang@broadcom.com>
To: Linux SPI List <linux-spi@vger.kernel.org>,
	Broadcom Kernel List <bcm-kernel-feedback-list@broadcom.com>
Cc: anand.gore@broadcom.com, tomer.yacoby@broadcom.com,
	dan.beygelman@broadcom.com, joel.peshkin@broadcom.com,
	f.fainelli@gmail.com, jonas.gorski@gmail.com,
	kursad.oney@broadcom.com, dregan@mail.com,
	William Zhang <william.zhang@broadcom.com>,
	Mark Brown <broonie@kernel.org>,
	linux-kernel@vger.kernel.org
Subject: [PATCH 11/16] spi: bcm63xx-hsspi: Add prepend feature support
Date: Fri,  6 Jan 2023 12:08:03 -0800	[thread overview]
Message-ID: <20230106200809.330769-12-william.zhang@broadcom.com> (raw)
In-Reply-To: <20230106200809.330769-1-william.zhang@broadcom.com>

Multiple transfers within a SPI message may be combined into one
transfer to the controller using its prepend feature. A SPI message is
prependable only if the following are all true:
  * One or more half duplex write transfer
  * Optional full duplex read/write at the end
  * No delay and cs_change between transfers

Most of the SPI device meets this requirements such as SPI NOR,
SPI NAND flash, Broadcom SPI voice card and etc. So this patch
makes use of the prepend feature as the default mode as it has no board
design requirement as the dummy cs workaround needs.

For any SPI device that does not meet the above requirement, dummy cs
mode can be used as long as the board design meets its cs pin usage
requirement or runs below 30MHz clock speed.

Signed-off-by: William Zhang <william.zhang@broadcom.com>
---

 drivers/spi/spi-bcm63xx-hsspi.c | 246 ++++++++++++++++++++++++++++----
 1 file changed, 221 insertions(+), 25 deletions(-)

diff --git a/drivers/spi/spi-bcm63xx-hsspi.c b/drivers/spi/spi-bcm63xx-hsspi.c
index b5043251edec..58f2b495c13c 100644
--- a/drivers/spi/spi-bcm63xx-hsspi.c
+++ b/drivers/spi/spi-bcm63xx-hsspi.c
@@ -113,8 +113,208 @@ struct bcm63xx_hsspi {
 	u8 cs_polarity;
 	bool use_cs_workaround;
 	int irq;
+	u32 prepend_cnt;
+	u8 *prepend_buf;
 };
 
+static void bcm63xx_hsspi_set_clk(struct bcm63xx_hsspi *bs,
+				  struct spi_device *spi, int hz);
+
+static size_t bcm63xx_hsspi_max_message_size(struct spi_device *spi)
+{
+	return HSSPI_BUFFER_LEN - HSSPI_OPCODE_LEN;
+}
+
+static int bcm63xx_hsspi_wait_cmd(struct bcm63xx_hsspi *bs)
+{
+	unsigned long limit;
+	u32 reg = 0;
+	int rc = 0;
+
+	if (bs->irq > 0) {
+		if (wait_for_completion_timeout(&bs->done, HZ) == 0)
+			rc = 1;
+	} else {
+		/* polling mode checks for status busy bit */
+		limit = jiffies + msecs_to_jiffies(HSSPI_POLL_STATUS_TIMEOUT_MS);
+
+		while (!time_after(jiffies, limit)) {
+			reg = __raw_readl(bs->regs + HSSPI_PINGPONG_STATUS_REG(0));
+			if (reg & HSSPI_PINGPONG_STATUS_SRC_BUSY)
+				cpu_relax();
+			else
+				break;
+		}
+		if (reg & HSSPI_PINGPONG_STATUS_SRC_BUSY)
+			rc = 1;
+	}
+
+	if (rc)
+		dev_err(&bs->pdev->dev, "transfer timed out!\n");
+
+	return rc;
+}
+
+static bool bcm63xx_check_msg_prependable(struct spi_master *master,
+					  struct spi_message *msg,
+					  struct spi_transfer *t_prepend)
+{
+
+	struct bcm63xx_hsspi *bs = spi_master_get_devdata(master);
+	bool prepend = false, tx_only = false;
+	struct spi_transfer *t;
+
+	/* If cs dummy workaround used, no need to prepend message */
+	if (bs->use_cs_workaround)
+		goto check_done;
+
+	/*
+	 * Multiple transfers within a message may be combined into one transfer
+	 * to the controller using its prepend feature. A SPI message is prependable
+	 * only if the following are all true:
+	 *   1. One or more half duplex write transfer
+	 *   2. Optional full duplex read/write at the end
+	 *   3. No delay and cs_change between transfers
+	 */
+	bs->prepend_cnt = 0;
+	list_for_each_entry(t, &msg->transfers, transfer_list) {
+		if ((spi_delay_to_ns(&t->delay, t) > 0) || t->cs_change) {
+			dev_warn(&bs->pdev->dev,
+				 "Delay or cs change not supported in prepend mode!\n");
+			break;
+		}
+
+		tx_only = false;
+		if (t->tx_buf && !t->rx_buf) {
+			tx_only = true;
+			if (bs->prepend_cnt + t->len >
+			    (HSSPI_BUFFER_LEN - HSSPI_OPCODE_LEN)) {
+				dev_warn(&bs->pdev->dev,
+					 "exceed max buf len, abort prepending transfers!\n");
+				break;
+			}
+			memcpy(bs->prepend_buf + bs->prepend_cnt, t->tx_buf,
+			       t->len);
+			bs->prepend_cnt += t->len;
+		} else {
+			if (!list_is_last(&t->transfer_list, &msg->transfers)) {
+				dev_warn(&bs->pdev->dev,
+					 "rx/tx_rx transfer not supported when it is not last one!\n");
+				break;
+			}
+		}
+
+		if (list_is_last(&t->transfer_list, &msg->transfers)) {
+			memcpy(t_prepend, t, sizeof(struct spi_transfer));
+
+			if (tx_only) {
+				/*
+				 * if the last one is also a tx only transfer, merge all
+				 * them into one single tx transfer
+				 */
+				t_prepend->len = bs->prepend_cnt;
+				t_prepend->tx_buf = bs->prepend_buf;
+				bs->prepend_cnt = 0;
+			} else {
+				/*
+				 * if the last one is not a tx only transfer, all the previous
+				 * transfers are sent through prepend bytes and make sure it does
+				 * not exceed the max prepend len
+				 */
+				if (bs->prepend_cnt > HSSPI_MAX_PREPEND_LEN) {
+					dev_warn(&bs->pdev->dev,
+						"exceed max prepend len, abort prepending transfers!\n");
+					break;
+				}
+			}
+			prepend = true;
+		}
+	}
+
+check_done:
+	if (!bs->use_cs_workaround && !prepend)
+		dev_warn(&bs->pdev->dev,
+		    "Msg not prependable and cs war not used. Transfer may fail!\n");
+
+	return prepend;
+}
+
+static int bcm63xx_hsspi_do_prepend_txrx(struct spi_device *spi,
+					 struct spi_transfer *t)
+{
+	struct bcm63xx_hsspi *bs = spi_master_get_devdata(spi->master);
+	unsigned int chip_select = spi->chip_select;
+	u16 opcode = 0;
+	const u8 *tx = t->tx_buf;
+	u8 *rx = t->rx_buf;
+	u32 reg = 0;
+
+	/*
+	 * shouldn't happen as we set the max_message_size in the probe.
+	 * but check it again in case some driver does not honor the max size
+	 */
+	if (t->len + bs->prepend_cnt > (HSSPI_BUFFER_LEN - HSSPI_OPCODE_LEN)) {
+		dev_warn(&bs->pdev->dev,
+			 "Prepend message large than fifo size len %d prepend %d\n",
+			 t->len, bs->prepend_cnt);
+		return -EINVAL;
+	}
+
+	bcm63xx_hsspi_set_clk(bs, spi, t->speed_hz);
+
+	if (tx && rx)
+		opcode = HSSPI_OP_READ_WRITE;
+	else if (tx)
+		opcode = HSSPI_OP_WRITE;
+	else if (rx)
+		opcode = HSSPI_OP_READ;
+
+	if ((opcode == HSSPI_OP_READ && t->rx_nbits == SPI_NBITS_DUAL) ||
+	    (opcode == HSSPI_OP_WRITE && t->tx_nbits == SPI_NBITS_DUAL)) {
+		opcode |= HSSPI_OP_MULTIBIT;
+
+		if (t->rx_nbits == SPI_NBITS_DUAL) {
+			reg |= 1 << MODE_CTRL_MULTIDATA_RD_SIZE_SHIFT;
+			reg |= bs->prepend_cnt << MODE_CTRL_MULTIDATA_RD_STRT_SHIFT;
+		}
+		if (t->tx_nbits == SPI_NBITS_DUAL) {
+			reg |= 1 << MODE_CTRL_MULTIDATA_WR_SIZE_SHIFT;
+			reg |= bs->prepend_cnt << MODE_CTRL_MULTIDATA_WR_STRT_SHIFT;
+		}
+	}
+
+	reg |= bs->prepend_cnt << MODE_CTRL_PREPENDBYTE_CNT_SHIFT;
+	__raw_writel(reg | 0xff,
+		     bs->regs + HSSPI_PROFILE_MODE_CTRL_REG(chip_select));
+
+	reinit_completion(&bs->done);
+	if (bs->prepend_cnt)
+		memcpy_toio(bs->fifo + HSSPI_OPCODE_LEN, bs->prepend_buf,
+			    bs->prepend_cnt);
+	if (tx)
+		memcpy_toio(bs->fifo + HSSPI_OPCODE_LEN + bs->prepend_cnt, tx,
+			    t->len);
+
+	__raw_writew(cpu_to_be16(opcode | t->len), bs->fifo);
+	/* enable interrupt */
+	if (bs->irq > 0)
+		__raw_writel(HSSPI_PINGx_CMD_DONE(0), bs->regs + HSSPI_INT_MASK_REG);
+
+	/* start the transfer */
+	reg = chip_select << PINGPONG_CMD_SS_SHIFT |
+	    chip_select << PINGPONG_CMD_PROFILE_SHIFT |
+	    PINGPONG_COMMAND_START_NOW;
+	__raw_writel(reg, bs->regs + HSSPI_PINGPONG_COMMAND_REG(0));
+
+	if (bcm63xx_hsspi_wait_cmd(bs))
+		return -ETIMEDOUT;
+
+	if (rx)
+		memcpy_fromio(rx, bs->fifo, t->len);
+
+	return 0;
+}
+
 static void bcm63xx_hsspi_set_cs(struct bcm63xx_hsspi *bs, unsigned int cs,
 				 bool active)
 {
@@ -168,7 +368,6 @@ static int bcm63xx_hsspi_do_txrx(struct spi_device *spi, struct spi_transfer *t)
 	int step_size = HSSPI_BUFFER_LEN;
 	const u8 *tx = t->tx_buf;
 	u8 *rx = t->rx_buf;
-	unsigned long limit;
 	u32 reg = 0;
 
 	bcm63xx_hsspi_set_clk(bs, spi, t->speed_hz);
@@ -220,23 +419,8 @@ static int bcm63xx_hsspi_do_txrx(struct spi_device *spi, struct spi_transfer *t)
 			    PINGPONG_COMMAND_START_NOW;
 		__raw_writel(reg, bs->regs + HSSPI_PINGPONG_COMMAND_REG(0));
 
-		if (bs->irq > 0) {
-			if (wait_for_completion_timeout(&bs->done, HZ) == 0)
-				goto err_timeout;
-		} else {
-			/* polling mode checks for status busy bit */
-			limit = jiffies + msecs_to_jiffies(HSSPI_POLL_STATUS_TIMEOUT_MS);
-
-			while (!time_after(jiffies, limit)) {
-				reg = __raw_readl(bs->regs + HSSPI_PINGPONG_STATUS_REG(0));
-				if (reg & HSSPI_PINGPONG_STATUS_SRC_BUSY)
-					cpu_relax();
-				else
-					break;
-			}
-			if (reg & HSSPI_PINGPONG_STATUS_SRC_BUSY)
-				goto err_timeout;
-		}
+		if (bcm63xx_hsspi_wait_cmd(bs))
+			return -ETIMEDOUT;
 
 		if (rx) {
 			memcpy_fromio(rx, bs->fifo, curr_step);
@@ -247,10 +431,6 @@ static int bcm63xx_hsspi_do_txrx(struct spi_device *spi, struct spi_transfer *t)
 	}
 
 	return 0;
-
-err_timeout:
-	dev_err(&bs->pdev->dev, "transfer timed out!\n");
-	return -ETIMEDOUT;
 }
 
 static int bcm63xx_hsspi_setup(struct spi_device *spi)
@@ -300,8 +480,16 @@ static int bcm63xx_hsspi_transfer_one(struct spi_master *master,
 	int dummy_cs;
 	u32 reg;
 	bool restore_polarity = true;
+	struct spi_transfer t_prepend;
+
+	if (bcm63xx_check_msg_prependable(master, msg, &t_prepend)) {
+		status = bcm63xx_hsspi_do_prepend_txrx(spi, &t_prepend);
+		msg->actual_length += (t_prepend.len + bs->prepend_cnt);
+		goto msg_done;
+	}
 
-	/* This controller does not support keeping CS active during idle.
+	/*
+	 * This controller does not support keeping CS active during idle.
 	 * To work around this, we use the following ugly hack:
 	 *
 	 * a. Invert the target chip select's polarity so it will be active.
@@ -355,6 +543,7 @@ static int bcm63xx_hsspi_transfer_one(struct spi_master *master,
 			bcm63xx_hsspi_set_cs(bs, spi->chip_select, false);
 	}
 
+msg_done:
 	msg->status = status;
 	spi_finalize_current_message(master);
 
@@ -448,6 +637,11 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev)
 	bs->speed_hz = rate;
 	bs->fifo = (u8 __iomem *)(bs->regs + HSSPI_FIFO_REG(0));
 	bs->irq = irq;
+	bs->prepend_buf = devm_kzalloc(dev, HSSPI_BUFFER_LEN, GFP_KERNEL);
+	if (!bs->prepend_buf) {
+		ret = -ENOMEM;
+		goto out_put_master;
+	}
 
 	mutex_init(&bs->bus_mutex);
 	init_completion(&bs->done);
@@ -462,8 +656,6 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev)
 		bs->use_cs_workaround = of_property_read_bool(
 				    dev->of_node, "brcm,use-cs-workaround");
 	}
-	/* tmp hack. hard code to use cs workaround before prepend mode is added */
-	bs->use_cs_workaround = true;
 
 	of_property_read_u32(dev->of_node, "num-cs", &num_cs);
 	if (num_cs > 8) {
@@ -474,6 +666,10 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev)
 	master->num_chipselect = num_cs;
 	master->setup = bcm63xx_hsspi_setup;
 	master->transfer_one_message = bcm63xx_hsspi_transfer_one;
+	if (!bs->use_cs_workaround) {
+		master->max_transfer_size = bcm63xx_hsspi_max_message_size;
+		master->max_message_size = bcm63xx_hsspi_max_message_size;
+	}
 	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH |
 			    SPI_RX_DUAL | SPI_TX_DUAL;
 	master->bits_per_word_mask = SPI_BPW_MASK(8);
-- 
2.37.3


  parent reply	other threads:[~2023-01-06 20:19 UTC|newest]

Thread overview: 81+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-01-06 20:07 [PATCH 00/16] spi: bcm63xx-hsspi: driver and doc updates William Zhang
2023-01-06 20:07 ` William Zhang
2023-01-06 20:07 ` [PATCH 01/16] dt-bindings: spi: Convert bcm63xx-hsspi bindings to json-schema William Zhang
2023-01-07 15:18   ` Rob Herring
2023-01-07 15:32   ` Krzysztof Kozlowski
2023-01-09  7:52     ` William Zhang
2023-01-09  8:48       ` Krzysztof Kozlowski
2023-01-06 20:07 ` [PATCH 02/16] dt-bindings: spi: Add bcmbca-hsspi controller support William Zhang
2023-01-08 14:51   ` Krzysztof Kozlowski
2023-01-09  8:27     ` William Zhang
2023-01-09  8:56       ` Krzysztof Kozlowski
2023-01-09 19:13         ` William Zhang
2023-01-10  8:40           ` Krzysztof Kozlowski
2023-01-10 22:18             ` Florian Fainelli
2023-01-11  1:08               ` William Zhang
2023-01-11  9:02               ` Krzysztof Kozlowski
2023-01-11 18:04                 ` William Zhang
2023-01-11 18:12                   ` Krzysztof Kozlowski
2023-01-11 18:44                     ` William Zhang
2023-01-12  8:21                       ` Krzysztof Kozlowski
2023-01-12 19:50                         ` William Zhang
2023-01-13  7:41                           ` Krzysztof Kozlowski
2023-01-14  3:17                             ` William Zhang
2023-01-14  3:17                               ` William Zhang
2023-01-15 14:31                               ` Krzysztof Kozlowski
2023-01-11  0:59             ` William Zhang
2023-01-11  9:01               ` Krzysztof Kozlowski
2023-01-06 20:07 ` [PATCH 03/16] dt-bindings: spi: Add spi peripheral specific property William Zhang
2023-01-06 21:14   ` Mark Brown
2023-01-07  3:27     ` William Zhang
2023-01-07 15:38       ` Rob Herring
2023-01-09  8:06         ` William Zhang
2023-01-09 19:19           ` Mark Brown
2023-01-09 20:18             ` William Zhang
2023-01-10 22:01               ` Mark Brown
2023-01-11 19:48                 ` William Zhang
2023-01-08 14:52   ` Krzysztof Kozlowski
2023-01-09  8:27     ` William Zhang
2023-01-06 20:07 ` [PATCH 04/16] ARM: dts: broadcom: bcmbca: Add spi controller node William Zhang
2023-01-06 20:07   ` William Zhang
2023-01-06 20:07 ` [PATCH 05/16] arm64: " William Zhang
2023-01-06 20:07   ` William Zhang
2023-01-06 20:07 ` [PATCH 06/16] spi: bcm63xx-hsspi: Endianness fix for ARM based SoC William Zhang
2023-01-07  7:44   ` kernel test robot
2023-01-07 21:21   ` kernel test robot
2023-01-07 21:52   ` kernel test robot
2023-01-07 23:23   ` kernel test robot
2023-01-11  6:33   ` kernel test robot
2023-01-06 20:07 ` [PATCH 07/16] spi: bcm63xx-hsspi: Add polling mode support William Zhang
2023-01-06 21:47   ` Mark Brown
2023-01-07  3:35     ` William Zhang
2023-01-09 19:06       ` Mark Brown
2023-01-09 20:10         ` William Zhang
2023-01-10 22:49           ` Mark Brown
2023-01-11 20:13             ` William Zhang
2023-01-11 22:41               ` Mark Brown
2023-01-11 22:57                 ` William Zhang
2023-01-06 20:08 ` [PATCH 08/16] spi: bcm63xx-hsspi: Handle cs_change correctly William Zhang
2023-01-06 20:08 ` [PATCH 09/16] spi: bcm63xx-hsspi: Fix multi-bit mode setting William Zhang
2023-01-06 20:08 ` [PATCH 10/16] spi: bcm63xx-hsspi: Make dummy cs workaround as an option William Zhang
2023-01-12 18:08   ` Mark Brown
2023-01-18 23:09     ` William Zhang
2023-01-19 13:09       ` Mark Brown
2023-01-06 20:08 ` William Zhang [this message]
2023-01-06 22:00   ` [PATCH 11/16] spi: bcm63xx-hsspi: Add prepend feature support Mark Brown
2023-01-07  3:52     ` William Zhang
2023-01-09 19:31       ` Mark Brown
2023-01-09 20:43         ` William Zhang
2023-01-10 21:18           ` Mark Brown
2023-01-11 19:42             ` William Zhang
2023-01-12 16:57               ` Mark Brown
2023-01-06 20:08 ` [PATCH 12/16] spi: bcm63xx-hsspi: Add clock gate disable option support William Zhang
2023-01-06 20:08 ` [PATCH 13/16] spi: spi-mem: Allow controller supporting mem_ops without exec_op William Zhang
2023-01-06 20:08 ` [PATCH 14/16] spi: bcm63xx-hsspi: prepend: Disable spi mem dual io read op support William Zhang
2023-01-06 22:07   ` Mark Brown
2023-01-07  3:57     ` William Zhang
2023-01-06 20:08 ` [PATCH 15/16] spi: bcmbca-hsspi: Add driver for newer HSSPI controller William Zhang
2023-01-06 20:08   ` William Zhang
2023-01-07 22:02   ` kernel test robot
2023-01-08  2:25   ` kernel test robot
2023-01-06 20:08 ` [PATCH 16/16] MAINTAINERS: Add entry for Broadcom Broadband SoC HS SPI drivers William Zhang

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230106200809.330769-12-william.zhang@broadcom.com \
    --to=william.zhang@broadcom.com \
    --cc=anand.gore@broadcom.com \
    --cc=bcm-kernel-feedback-list@broadcom.com \
    --cc=broonie@kernel.org \
    --cc=dan.beygelman@broadcom.com \
    --cc=dregan@mail.com \
    --cc=f.fainelli@gmail.com \
    --cc=joel.peshkin@broadcom.com \
    --cc=jonas.gorski@gmail.com \
    --cc=kursad.oney@broadcom.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-spi@vger.kernel.org \
    --cc=tomer.yacoby@broadcom.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.