diff mbox series

[v7,4/4] spi: cadence: Add MRVL overlay xfer operation support

Message ID 20240529220026.1644986-5-wsadowski@marvell.com
State Superseded
Headers show
Series Marvell HW overlay support for Cadence xSPI | expand

Commit Message

Witold Sadowski May 29, 2024, 10 p.m. UTC
MRVL Xfer overlay extends xSPI capabilities to support non-memory SPI
operations. The Marvell overlay, combined with a generic command, allows
for full-duplex SPI transactions. It also enables transactions with
undetermined lengths using the cs_hold parameter and the ability to
extend CS signal assertion, even if the xSPI block requests CS signal
de-assertion.

Signed-off-by: Witold Sadowski <wsadowski@marvell.com>
---
 drivers/spi/spi-cadence-xspi.c | 245 +++++++++++++++++++++++++++++++++
 1 file changed, 245 insertions(+)

Comments

Witold Sadowski June 7, 2024, 3:15 p.m. UTC | #1
> -----Original Message-----
> From: Dan Carpenter <dan.carpenter@linaro.org>
> Sent: Tuesday, June 4, 2024 11:22 AM
> To: oe-kbuild@lists.linux.dev; Witold Sadowski <wsadowski@marvell.com>;
> linux-kernel@vger.kernel.org; linux-spi@vger.kernel.org;
> devicetree@vger.kernel.org
> Cc: lkp@intel.com; oe-kbuild-all@lists.linux.dev; broonie@kernel.org;
> robh@kernel.org; krzysztof.kozlowski+dt@linaro.org; conor+dt@kernel.org;
> pthombar@cadence.com; Witold Sadowski <wsadowski@marvell.com>
> Subject: [EXTERNAL] Re: [PATCH v7 4/4] spi: cadence: Add MRVL overlay xfer
> operation support
> 
> Prioritize security for external emails: Confirm sender and content safety
> before clicking links or opening attachments
> 
> ----------------------------------------------------------------------
> Hi Witold,
> 
> kernel test robot noticed the following build warnings:
> 
> https://urldefense.proofpoint.com/v2/url?u=https-3A__git-
> 2Dscm.com_docs_git-2Dformat-2Dpatch-23-5Fbase-5Ftree-
> 5Finformation&d=DwIBAg&c=nKjWec2b6R0mOyPaz7xtfQ&r=GKgcn-g6ZX-
> JmCL3S2qKgVQhvhv7hu2n8En-dZbLTa8&m=3T1y56pUB9HOjzo3-
> EL5gF5YmRfjS2So3PliL081jziAvO4ArfnIo9RLWQtkHevL&s=07hYvagESNc9n4j7O0lrzZcF
> lmVTYeR4d95yszqqbbA&e= ]
> 
> url:    https://urldefense.proofpoint.com/v2/url?u=https-
> 3A__github.com_intel-2Dlab-2Dlkp_linux_commits_Witold-2DSadowski_spi-2Ddt-
> 2Dbindings-2Dcadence-2DAdd-2DMarvell-2Doverlay-2Dbindings-2Ddocumentation-
> 2Dfor-2DCadence-2DXSPI_20240530-
> 2D060250&d=DwIBAg&c=nKjWec2b6R0mOyPaz7xtfQ&r=GKgcn-g6ZX-
> JmCL3S2qKgVQhvhv7hu2n8En-dZbLTa8&m=3T1y56pUB9HOjzo3-
> EL5gF5YmRfjS2So3PliL081jziAvO4ArfnIo9RLWQtkHevL&s=L5rHOjcT7_LtRsrf-
> 8usYbyZ1nygU8VSbseBArBD4Gw&e=
> base:   https://urldefense.proofpoint.com/v2/url?u=https-
> 3A__git.kernel.org_pub_scm_linux_kernel_git_broonie_spi.git&d=DwIBAg&c=nKj
> Wec2b6R0mOyPaz7xtfQ&r=GKgcn-g6ZX-JmCL3S2qKgVQhvhv7hu2n8En-
> dZbLTa8&m=3T1y56pUB9HOjzo3-
> EL5gF5YmRfjS2So3PliL081jziAvO4ArfnIo9RLWQtkHevL&s=Z49n1j1P9sWiOEwpdOUyPisI
> LleXaHHWL3gqPnZMnjY&e=  for-next
> patch link:    https://urldefense.proofpoint.com/v2/url?u=https-
> 3A__lore.kernel.org_r_20240529220026.1644986-2D5-2Dwsadowski-
> 2540marvell.com&d=DwIBAg&c=nKjWec2b6R0mOyPaz7xtfQ&r=GKgcn-g6ZX-
> JmCL3S2qKgVQhvhv7hu2n8En-dZbLTa8&m=3T1y56pUB9HOjzo3-
> EL5gF5YmRfjS2So3PliL081jziAvO4ArfnIo9RLWQtkHevL&s=enzvWq1OJNu52elgRFFsdNYY
> M66--SRJXq13KKmZycI&e=
> patch subject: [PATCH v7 4/4] spi: cadence: Add MRVL overlay xfer
> operation support
> config: powerpc64-randconfig-r071-20240531
> (https://urldefense.proofpoint.com/v2/url?u=https-
> 3A__download.01.org_0day-2Dci_archive_20240602_202406020007.yDo5EI4r-
> 2Dlkp-40intel.com_config&d=DwIBAg&c=nKjWec2b6R0mOyPaz7xtfQ&r=GKgcn-g6ZX-
> JmCL3S2qKgVQhvhv7hu2n8En-dZbLTa8&m=3T1y56pUB9HOjzo3-
> EL5gF5YmRfjS2So3PliL081jziAvO4ArfnIo9RLWQtkHevL&s=XlRP4onlzTi33VFScF3JKa8D
> gPsSALAwrYapdtVzgy8&e= )
> compiler: clang version 19.0.0git
> (https://urldefense.proofpoint.com/v2/url?u=https-
> 3A__github.com_llvm_llvm-
> 2Dproject&d=DwIBAg&c=nKjWec2b6R0mOyPaz7xtfQ&r=GKgcn-g6ZX-
> JmCL3S2qKgVQhvhv7hu2n8En-dZbLTa8&m=3T1y56pUB9HOjzo3-
> EL5gF5YmRfjS2So3PliL081jziAvO4ArfnIo9RLWQtkHevL&s=ndehxuXJ9Np2ZrPiPcWAZV3K
> CIWkfsgKavjDFYmr5s8&e=  bafda89a0944d947fc4b3b5663185e07a397ac30)
> 
> If you fix the issue in a separate patch/commit (i.e. not just a new
> version of the same patch/commit), kindly add following tags
> | Reported-by: kernel test robot <lkp@intel.com>
> | Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
> | Closes:
> | https://urldefense.proofpoint.com/v2/url?u=https-3A__lore.kernel.org_r
> | _202406020007.yDo5EI4r-2Dlkp-40intel.com_&d=DwIBAg&c=nKjWec2b6R0mOyPaz
> | 7xtfQ&r=GKgcn-g6ZX-JmCL3S2qKgVQhvhv7hu2n8En-dZbLTa8&m=3T1y56pUB9HOjzo3
> | -EL5gF5YmRfjS2So3PliL081jziAvO4ArfnIo9RLWQtkHevL&s=oMyRM-yKxK3MXd83MBK
> | fR__LIwxmob86rs5qIz1iV6Q&e=
> 
> New smatch warnings:
> drivers/spi/spi-cadence-xspi.c:955 cdns_xspi_stig_ready() warn: signedness
> bug returning '(-110)'
> drivers/spi/spi-cadence-xspi.c:967 cdns_xspi_sdma_ready() warn: signedness
> bug returning '(-110)'
> 
> vim +955 drivers/spi/spi-cadence-xspi.c
> 
> 66e19aa5a2e022b Witold Sadowski 2024-05-29  951  static bool
> cdns_xspi_stig_ready(struct cdns_xspi_dev *cdns_xspi, bool sleep)
> 66e19aa5a2e022b Witold Sadowski 2024-05-29  952  {
> 66e19aa5a2e022b Witold Sadowski 2024-05-29  953  	u32 ctrl_stat;
> 66e19aa5a2e022b Witold Sadowski 2024-05-29  954
> 66e19aa5a2e022b Witold Sadowski 2024-05-29 @955  	return
> readl_relaxed_poll_timeout
> 66e19aa5a2e022b Witold Sadowski 2024-05-29  956  		(cdns_xspi-
> >iobase + CDNS_XSPI_CTRL_STATUS_REG,
> 66e19aa5a2e022b Witold Sadowski 2024-05-29  957  		ctrl_stat,
> 66e19aa5a2e022b Witold Sadowski 2024-05-29  958  		((ctrl_stat &
> BIT(3)) == 0),
> 66e19aa5a2e022b Witold Sadowski 2024-05-29  959  		sleep ?
> MRVL_XSPI_POLL_DELAY_US : 0,
> 66e19aa5a2e022b Witold Sadowski 2024-05-29  960  		sleep ?
> MRVL_XSPI_POLL_TIMEOUT_US : 0);
> 
> This works but from the name you would expect it to return true when it's
> ready and false when it's not.
> 
> 66e19aa5a2e022b Witold Sadowski 2024-05-29  961  } 66e19aa5a2e022b Witold
> Sadowski 2024-05-29  962 66e19aa5a2e022b Witold Sadowski 2024-05-29  963
> static bool cdns_xspi_sdma_ready(struct cdns_xspi_dev *cdns_xspi, bool
> sleep) 66e19aa5a2e022b Witold Sadowski 2024-05-29  964  {
> 66e19aa5a2e022b Witold Sadowski 2024-05-29  965  	u32 ctrl_stat;
> 66e19aa5a2e022b Witold Sadowski 2024-05-29  966
> 66e19aa5a2e022b Witold Sadowski 2024-05-29 @967  	return
> readl_relaxed_poll_timeout
> 66e19aa5a2e022b Witold Sadowski 2024-05-29  968  		(cdns_xspi-
> >iobase + CDNS_XSPI_INTR_STATUS_REG,
> 66e19aa5a2e022b Witold Sadowski 2024-05-29  969  		ctrl_stat,
> 66e19aa5a2e022b Witold Sadowski 2024-05-29  970  		(ctrl_stat &
> CDNS_XSPI_SDMA_TRIGGER),
> 66e19aa5a2e022b Witold Sadowski 2024-05-29  971  		sleep ?
> MRVL_XSPI_POLL_DELAY_US : 0,
> 66e19aa5a2e022b Witold Sadowski 2024-05-29  972  		sleep ?
> MRVL_XSPI_POLL_TIMEOUT_US : 0);
> 66e19aa5a2e022b Witold Sadowski 2024-05-29  973  }
> 

Ok, I will rework that to return non-inverted value.

> --
> 0-DAY CI Kernel Test Service
> https://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_intel_lkp-
> 2Dtests_wiki&d=DwIBAg&c=nKjWec2b6R0mOyPaz7xtfQ&r=GKgcn-g6ZX-
> JmCL3S2qKgVQhvhv7hu2n8En-dZbLTa8&m=3T1y56pUB9HOjzo3-
> EL5gF5YmRfjS2So3PliL081jziAvO4ArfnIo9RLWQtkHevL&s=7_g-
> spW_lOA62MlydcNUrv_58Ua2UFcfpe6GmRUHskQ&e=
diff mbox series

Patch

diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c
index 490bba7a0fc8..7279a73162ce 100644
--- a/drivers/spi/spi-cadence-xspi.c
+++ b/drivers/spi/spi-cadence-xspi.c
@@ -219,6 +219,7 @@ 
 #define CDNS_XSPI_DLL_RST_N BIT(24)
 #define CDNS_XSPI_DLL_LOCK  BIT(0)
 
+
 /* Marvell overlay registers - clock */
 #define MRVL_XSPI_CLK_CTRL_AUX_REG   0x2020
 #define MRVL_XSPI_CLK_ENABLE	     BIT(0)
@@ -232,6 +233,22 @@ 
 #define MRVL_XSPI_SPIX_INTR_AUX	0x2000
 #define MRVL_MSIX_CLEAR_IRQ	0x01
 
+/* Marvell overlay registers - xfer */
+#define MRVL_XFER_FUNC_CTRL		 0x210
+#define MRVL_XFER_FUNC_CTRL_READ_DATA(i) (0x000 + 8 * (i))
+#define MRVL_XFER_SOFT_RESET		 BIT(11)
+#define MRVL_XFER_CS_N_HOLD		 GENMASK(9, 6)
+#define MRVL_XFER_RECEIVE_ENABLE	 BIT(4)
+#define MRVL_XFER_FUNC_ENABLE		 BIT(3)
+#define MRVL_XFER_CLK_CAPTURE_POL	 BIT(2)
+#define MRVL_XFER_CLK_DRIVE_POL		 BIT(1)
+#define MRVL_XFER_FUNC_START		 BIT(0)
+#define MRVL_XFER_QWORD_COUNT		 32
+#define MRVL_XFER_QWORD_BYTECOUNT	 8
+
+#define MRVL_XSPI_POLL_TIMEOUT_US	1000
+#define MRVL_XSPI_POLL_DELAY_US		10
+
 enum cdns_xspi_stig_instr_type {
 	CDNS_XSPI_STIG_INSTR_TYPE_0,
 	CDNS_XSPI_STIG_INSTR_TYPE_1,
@@ -256,6 +273,7 @@  struct cdns_xspi_dev {
 	void __iomem *iobase;
 	void __iomem *auxbase;
 	void __iomem *sdmabase;
+	void __iomem *xferbase;
 
 	int irq;
 	int cur_cs;
@@ -270,6 +288,9 @@  struct cdns_xspi_dev {
 	const void *out_buffer;
 
 	u8 hw_num_banks;
+
+	bool xfer_in_progress;
+	int current_xfer_qword;
 };
 
 struct cdns_xspi_driver_data {
@@ -836,6 +857,220 @@  static int cdns_xspi_setup(struct spi_device *spi_dev)
 	return 0;
 }
 
+static int cdns_xspi_prepare_generic(int cs, const void *dout, int len, int glue, u32 *cmd_regs)
+{
+	u8 *data = (u8 *)dout;
+	int i;
+	int data_counter = 0;
+
+	memset(cmd_regs, 0x00, 6*4);
+
+	if (len > 7) {
+		for (i = (len >= 10 ? 2 : len - 8); i >= 0 ; i--)
+			cmd_regs[3] |= data[data_counter++] << (8*i);
+	}
+	if (len > 3) {
+		for (i = (len >= 7 ? 3 : len - 4); i >= 0; i--)
+			cmd_regs[2] |= data[data_counter++] << (8*i);
+	}
+	for (i = (len >= 3 ? 2 : len - 1); i >= 0 ; i--)
+		cmd_regs[1] |= data[data_counter++] << (8 + 8*i);
+
+	cmd_regs[1] |= 96;
+	cmd_regs[3] |= len << 24;
+	cmd_regs[4] |= cs << 12;
+
+	if (glue == 1)
+		cmd_regs[4] |= 1 << 28;
+
+	return 0;
+}
+
+static unsigned char reverse_bits(unsigned char num)
+{
+	unsigned int count = sizeof(num) * 8 - 1;
+	unsigned int reverse_num = num;
+
+	num >>= 1;
+	while (num) {
+		reverse_num <<= 1;
+		reverse_num |= num & 1;
+		num >>= 1;
+		count--;
+	}
+	reverse_num <<= count;
+	return reverse_num;
+}
+
+static void cdns_xspi_read_single_qword(struct cdns_xspi_dev *cdns_xspi, u8 **buffer)
+{
+	u64 d = readq(cdns_xspi->xferbase +
+		      MRVL_XFER_FUNC_CTRL_READ_DATA(cdns_xspi->current_xfer_qword));
+	u8 *ptr = (u8 *)&d;
+	int k;
+
+	for (k = 0; k < 8; k++) {
+		u8 val = reverse_bits((ptr[k]));
+		**buffer = val;
+		*buffer = *buffer + 1;
+	}
+
+	cdns_xspi->current_xfer_qword++;
+	cdns_xspi->current_xfer_qword %= MRVL_XFER_QWORD_COUNT;
+}
+
+static void cdns_xspi_finish_read(struct cdns_xspi_dev *cdns_xspi, u8 **buffer, u32 data_count)
+{
+	u64 d = readq(cdns_xspi->xferbase +
+		      MRVL_XFER_FUNC_CTRL_READ_DATA(cdns_xspi->current_xfer_qword));
+	u8 *ptr = (u8 *)&d;
+	int k;
+
+	for (k = 0; k < data_count % MRVL_XFER_QWORD_BYTECOUNT; k++) {
+		u8 val = reverse_bits((ptr[k]));
+		**buffer = val;
+		*buffer = *buffer + 1;
+	}
+
+	cdns_xspi->current_xfer_qword++;
+	cdns_xspi->current_xfer_qword %= MRVL_XFER_QWORD_COUNT;
+}
+
+static int cdns_xspi_prepare_transfer(int cs, int dir, int len, u32 *cmd_regs)
+{
+	memset(cmd_regs, 0x00, 6*4);
+
+	cmd_regs[1] |= 127;
+	cmd_regs[2] |= len << 16;
+	cmd_regs[4] |= dir << 4; //dir = 0 read, dir =1 write
+	cmd_regs[4] |= cs << 12;
+
+	return 0;
+}
+
+static bool cdns_xspi_stig_ready(struct cdns_xspi_dev *cdns_xspi, bool sleep)
+{
+	u32 ctrl_stat;
+
+	return readl_relaxed_poll_timeout
+		(cdns_xspi->iobase + CDNS_XSPI_CTRL_STATUS_REG,
+		ctrl_stat,
+		((ctrl_stat & BIT(3)) == 0),
+		sleep ? MRVL_XSPI_POLL_DELAY_US : 0,
+		sleep ? MRVL_XSPI_POLL_TIMEOUT_US : 0);
+}
+
+static bool cdns_xspi_sdma_ready(struct cdns_xspi_dev *cdns_xspi, bool sleep)
+{
+	u32 ctrl_stat;
+
+	return readl_relaxed_poll_timeout
+		(cdns_xspi->iobase + CDNS_XSPI_INTR_STATUS_REG,
+		ctrl_stat,
+		(ctrl_stat & CDNS_XSPI_SDMA_TRIGGER),
+		sleep ? MRVL_XSPI_POLL_DELAY_US : 0,
+		sleep ? MRVL_XSPI_POLL_TIMEOUT_US : 0);
+}
+
+static int cdns_xspi_transfer_one_message_b0(struct spi_controller *controller,
+					   struct spi_message *m)
+{
+	struct cdns_xspi_dev *cdns_xspi = spi_controller_get_devdata(controller);
+	struct spi_device *spi = m->spi;
+	struct spi_transfer *t = NULL;
+
+	const int max_len = MRVL_XFER_QWORD_BYTECOUNT * MRVL_XFER_QWORD_COUNT;
+	int current_cycle_count;
+	int cs = spi_get_chipselect(spi, 0);
+	int cs_change = 0;
+
+	/* Enable xfer state machine */
+	if (!cdns_xspi->xfer_in_progress) {
+		u32 xfer_control = readl(cdns_xspi->xferbase + MRVL_XFER_FUNC_CTRL);
+
+		cdns_xspi->current_xfer_qword = 0;
+		cdns_xspi->xfer_in_progress = true;
+		xfer_control |= (MRVL_XFER_RECEIVE_ENABLE |
+				 MRVL_XFER_CLK_CAPTURE_POL |
+				 MRVL_XFER_FUNC_START |
+				 MRVL_XFER_SOFT_RESET |
+				 FIELD_PREP(MRVL_XFER_CS_N_HOLD, (1 << cs)));
+		xfer_control &= ~(MRVL_XFER_FUNC_ENABLE | MRVL_XFER_CLK_DRIVE_POL);
+		writel(xfer_control, cdns_xspi->xferbase + MRVL_XFER_FUNC_CTRL);
+	}
+
+	list_for_each_entry(t, &m->transfers, transfer_list) {
+		u8 *txd = (u8 *) t->tx_buf;
+		u8 *rxd = (u8 *) t->rx_buf;
+		u8 data[10];
+		u32 cmd_regs[6];
+
+		if (!txd)
+			txd = data;
+
+		cdns_xspi->in_buffer = txd + 1;
+		cdns_xspi->out_buffer = txd + 1;
+
+		while (t->len) {
+
+			current_cycle_count = t->len > max_len ? max_len : t->len;
+
+			if (current_cycle_count < 10) {
+				cdns_xspi_prepare_generic(cs, txd, current_cycle_count,
+							  false, cmd_regs);
+				cdns_xspi_trigger_command(cdns_xspi, cmd_regs);
+				if (cdns_xspi_stig_ready(cdns_xspi, true))
+					return -EIO;
+			} else {
+				cdns_xspi_prepare_generic(cs, txd, 1, true, cmd_regs);
+				cdns_xspi_trigger_command(cdns_xspi, cmd_regs);
+				cdns_xspi_prepare_transfer(cs, 1, current_cycle_count - 1,
+							   cmd_regs);
+				cdns_xspi_trigger_command(cdns_xspi, cmd_regs);
+				if (cdns_xspi_sdma_ready(cdns_xspi, true))
+					return -EIO;
+				cdns_xspi_sdma_handle(cdns_xspi);
+				if (cdns_xspi_stig_ready(cdns_xspi, true))
+					return -EIO;
+
+				cdns_xspi->in_buffer += current_cycle_count;
+				cdns_xspi->out_buffer += current_cycle_count;
+			}
+
+			if (rxd) {
+				int j;
+
+				for (j = 0; j < current_cycle_count / 8; j++)
+					cdns_xspi_read_single_qword(cdns_xspi, &rxd);
+				cdns_xspi_finish_read(cdns_xspi, &rxd, current_cycle_count);
+			} else {
+				cdns_xspi->current_xfer_qword += current_cycle_count /
+								 MRVL_XFER_QWORD_BYTECOUNT;
+				if (current_cycle_count % MRVL_XFER_QWORD_BYTECOUNT)
+					cdns_xspi->current_xfer_qword++;
+
+				cdns_xspi->current_xfer_qword %= MRVL_XFER_QWORD_COUNT;
+			}
+			cs_change = t->cs_change;
+			t->len -= current_cycle_count;
+		}
+	}
+
+	if (!cs_change) {
+		u32 xfer_control = readl(cdns_xspi->xferbase + MRVL_XFER_FUNC_CTRL);
+
+		xfer_control &= ~(MRVL_XFER_RECEIVE_ENABLE |
+				  MRVL_XFER_SOFT_RESET);
+		writel(xfer_control, cdns_xspi->xferbase + MRVL_XFER_FUNC_CTRL);
+		cdns_xspi->xfer_in_progress = false;
+	}
+
+	m->status = 0;
+	spi_finalize_current_message(controller);
+
+	return 0;
+}
+
 static int cdns_xspi_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -903,6 +1138,15 @@  static int cdns_xspi_probe(struct platform_device *pdev)
 		return PTR_ERR(cdns_xspi->auxbase);
 	}
 
+	if (cdns_xspi->mrvl_hw_overlay) {
+		cdns_xspi->xferbase = devm_platform_ioremap_resource(pdev, 3);
+		if (IS_ERR(cdns_xspi->xferbase)) {
+			dev_info(dev, "XFER register base not found, set it\n");
+			// For compatibility with older firmware
+			cdns_xspi->xferbase = cdns_xspi->iobase + 0x8000;
+		}
+	}
+
 	cdns_xspi->irq = platform_get_irq(pdev, 0);
 	if (cdns_xspi->irq < 0)
 		return -ENXIO;
@@ -917,6 +1161,7 @@  static int cdns_xspi_probe(struct platform_device *pdev)
 	if (drv_data->mrvl_hw_overlay) {
 		cdns_mrvl_xspi_setup_clock(cdns_xspi, MRVL_DEFAULT_CLK);
 		cdns_xspi_configure_phy(cdns_xspi);
+		host->transfer_one_message = cdns_xspi_transfer_one_message_b0;
 	}
 
 	cdns_xspi_print_phy_config(cdns_xspi);