diff mbox series

[5/5] cadence-xspi: Add xfer capabilities

Message ID 20240329194849.25554-6-wsadowski@marvell.com
State New
Headers show
Series Support for Cadence xSPI Marvell modifications | expand

Commit Message

Witold Sadowski March 29, 2024, 7:48 p.m. UTC
Add support for iMRVL xfer hw_overlay of Cadence xSPI
block.
MRVL Xfer overlay extend xSPI capabilities, to support
non-memory SPI operations.
With generic xSPI command it allows to create any
required SPI transaction

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

Comments

Krzysztof Kozlowski March 30, 2024, 11:37 a.m. UTC | #1
On 29/03/2024 20:48, Witold Sadowski wrote:
> Add support for iMRVL xfer hw_overlay of Cadence xSPI
> block.
> MRVL Xfer overlay extend xSPI capabilities, to support
> non-memory SPI operations.
> With generic xSPI command it allows to create any
> required SPI transaction

Please use subject prefixes matching the subsystem. You can get them for
example with `git log --oneline -- DIRECTORY_OR_FILE` on the directory
your patch is touching.

Please wrap commit message according to Linux coding style / submission
process (neither too early nor over the limit):
https://elixir.bootlin.com/linux/v6.4-rc1/source/Documentation/process/submitting-patches.rst#L597

... and build your code because this does not compile. :(

Best regards,
Krzysztof
kernel test robot March 31, 2024, 3:25 a.m. UTC | #2
Hi Witold,

kernel test robot noticed the following build warnings:

[auto build test WARNING on broonie-spi/for-next]
[also build test WARNING on linus/master v6.9-rc1 next-20240328]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Witold-Sadowski/spi-cadence-Add-new-bindings-documentation-for-Cadence-XSPI/20240330-035124
base:   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git for-next
patch link:    https://lore.kernel.org/r/20240329194849.25554-6-wsadowski%40marvell.com
patch subject: [PATCH 5/5] cadence-xspi: Add xfer capabilities
config: x86_64-randconfig-123-20240331 (https://download.01.org/0day-ci/archive/20240331/202403311133.jOI5kbg4-lkp@intel.com/config)
compiler: gcc-7 (Ubuntu 7.5.0-6ubuntu2) 7.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240331/202403311133.jOI5kbg4-lkp@intel.com/reproduce)

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>
| Closes: https://lore.kernel.org/oe-kbuild-all/202403311133.jOI5kbg4-lkp@intel.com/

All warnings (new ones prefixed by >>):

   drivers/spi/spi-cadence-xspi.c: In function 'cdns_xspi_setup':
   drivers/spi/spi-cadence-xspi.c:892:36: error: implicit declaration of function 'spi_master_get_devdata'; did you mean 'spi_mem_get_drvdata'? [-Werror=implicit-function-declaration]
     struct cdns_xspi_dev *cdns_xspi = spi_master_get_devdata(spi_dev->master);
                                       ^~~~~~~~~~~~~~~~~~~~~~
                                       spi_mem_get_drvdata
   drivers/spi/spi-cadence-xspi.c:892:66: error: 'struct spi_device' has no member named 'master'
     struct cdns_xspi_dev *cdns_xspi = spi_master_get_devdata(spi_dev->master);
                                                                     ^~
   drivers/spi/spi-cadence-xspi.c: In function 'cdns_xspi_transfer_one_message_b0':
>> drivers/spi/spi-cadence-xspi.c:1029:36: warning: initialization makes pointer from integer without a cast [-Wint-conversion]
     struct cdns_xspi_dev *cdns_xspi = spi_master_get_devdata(master);
                                       ^~~~~~~~~~~~~~~~~~~~~~
>> drivers/spi/spi-cadence-xspi.c:1035:11: warning: initialization makes integer from pointer without a cast [-Wint-conversion]
     int cs = spi->chip_select;
              ^~~
   drivers/spi/spi-cadence-xspi.c: At top level:
   drivers/spi/spi-cadence-xspi.c:1242:0: error: unterminated #ifdef
    #ifdef CONFIG_OF
    
   cc1: some warnings being treated as errors


vim +1029 drivers/spi/spi-cadence-xspi.c

  1025	
  1026	int cdns_xspi_transfer_one_message_b0(struct spi_controller *master,
  1027						   struct spi_message *m)
  1028	{
> 1029		struct cdns_xspi_dev *cdns_xspi = spi_master_get_devdata(master);
  1030		struct spi_device *spi = m->spi;
  1031		struct spi_transfer *t = NULL;
  1032	
  1033		const int max_len = XFER_QWORD_BYTECOUNT * XFER_QWORD_COUNT;
  1034		int current_cycle_count;
> 1035		int cs = spi->chip_select;
  1036		int cs_change = 0;
  1037	
  1038		/* Enable xfer state machine */
  1039		if (!cdns_xspi->xfer_in_progress) {
  1040			u32 xfer_control = readl(cdns_xspi->xferbase + SPIX_XFER_FUNC_CTRL);
  1041	
  1042			cdns_xspi->current_xfer_qword = 0;
  1043			cdns_xspi->xfer_in_progress = true;
  1044			xfer_control |= (XFER_RECEIVE_ENABLE |
  1045					 XFER_CLK_CAPTURE_POL |
  1046					 XFER_FUNC_START |
  1047					 XFER_SOFT_RESET |
  1048					 FIELD_PREP(XFER_CS_N_HOLD, (1 << cs)));
  1049			xfer_control &= ~(XFER_FUNC_ENABLE | XFER_CLK_DRIVE_POL);
  1050			writel(xfer_control, cdns_xspi->xferbase + SPIX_XFER_FUNC_CTRL);
  1051		}
  1052	
  1053		list_for_each_entry(t, &m->transfers, transfer_list) {
  1054			u8 *txd = (u8 *) t->tx_buf;
  1055			u8 *rxd = (u8 *) t->rx_buf;
  1056			u8 data[10];
  1057			u32 cmd_regs[6];
  1058	
  1059			if (!txd)
  1060				txd = data;
  1061	
  1062			cdns_xspi->in_buffer = txd + 1;
  1063			cdns_xspi->out_buffer = txd + 1;
  1064	
  1065			while (t->len) {
  1066	
  1067				current_cycle_count = t->len > max_len ? max_len : t->len;
  1068	
  1069				if (current_cycle_count < 10) {
  1070					cdns_xspi_prepare_generic(cs, txd, current_cycle_count,
  1071								  false, cmd_regs);
  1072					cdns_xspi_trigger_command(cdns_xspi, cmd_regs);
  1073					if (cdns_xspi_stig_ready(cdns_xspi, true))
  1074						return -EIO;
  1075				} else {
  1076					cdns_xspi_prepare_generic(cs, txd, 1, true, cmd_regs);
  1077					cdns_xspi_trigger_command(cdns_xspi, cmd_regs);
  1078					cdns_xspi_prepare_transfer(cs, 1, current_cycle_count - 1,
  1079								   cmd_regs);
  1080					cdns_xspi_trigger_command(cdns_xspi, cmd_regs);
  1081					if (cdns_xspi_sdma_ready(cdns_xspi, true))
  1082						return -EIO;
  1083					cdns_xspi_sdma_handle(cdns_xspi);
  1084					if (cdns_xspi_stig_ready(cdns_xspi, true))
  1085						return -EIO;
  1086	
  1087					cdns_xspi->in_buffer += current_cycle_count;
  1088					cdns_xspi->out_buffer += current_cycle_count;
  1089				}
  1090	
  1091				if (rxd) {
  1092					int j;
  1093	
  1094					for (j = 0; j < current_cycle_count / 8; j++)
  1095						cdns_xspi_read_single_qword(cdns_xspi, &rxd);
  1096					cdns_xspi_finish_read(cdns_xspi, &rxd, current_cycle_count);
  1097				} else {
  1098					cdns_xspi->current_xfer_qword += current_cycle_count /
  1099									 XFER_QWORD_BYTECOUNT;
  1100					if (current_cycle_count % XFER_QWORD_BYTECOUNT)
  1101						cdns_xspi->current_xfer_qword++;
  1102	
  1103					cdns_xspi->current_xfer_qword %= XFER_QWORD_COUNT;
  1104				}
  1105				cs_change = t->cs_change;
  1106				t->len -= current_cycle_count;
  1107			}
  1108		}
  1109	
  1110		if (!cs_change) {
  1111			u32 xfer_control = readl(cdns_xspi->xferbase + SPIX_XFER_FUNC_CTRL);
  1112	
  1113			xfer_control &= ~(XFER_RECEIVE_ENABLE |
  1114					  XFER_SOFT_RESET);
  1115			writel(xfer_control, cdns_xspi->xferbase + SPIX_XFER_FUNC_CTRL);
  1116			cdns_xspi->xfer_in_progress = false;
  1117		}
  1118	
  1119		m->status = 0;
  1120		spi_finalize_current_message(master);
  1121	
  1122		return 0;
  1123	}
  1124
diff mbox series

Patch

diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c
index 01e81d40f04c..451f83d53898 100644
--- a/drivers/spi/spi-cadence-xspi.c
+++ b/drivers/spi/spi-cadence-xspi.c
@@ -219,19 +219,38 @@ 
 #define CDNS_XSPI_DLL_RST_N BIT(24)
 #define CDNS_XSPI_DLL_LOCK  BIT(0)
 
+#define CDNS_XSPI_POLL_TIMEOUT_US	1000
+#define CDNS_XSPI_POLL_DELAY_US		10
+
 /* Marvell clock config register */
 #define CDNS_MRVL_XSPI_CLK_CTRL_AUX_REG	0x2020
 #define CDNS_MRVL_XSPI_CLK_ENABLE	BIT(0)
 #define CDNS_MRVL_XSPI_CLK_DIV		GENMASK(4, 1)
 
+
 /* Marvell MSI-X clear interrupt register */
 #define CDNS_MRVL_XSPI_SPIX_INTR_AUX	0x2000
 #define CDNS_MRVL_MSIX_CLEAR_IRQ	0x01
 
+#define SPIX_XFER_FUNC_CTRL 0x210
+#define SPIX_XFER_FUNC_CTRL_READ_DATA(i) (0x000 + 8 * (i))
+
 /* Marvell clock macros */
 #define CDNS_MRVL_XSPI_CLOCK_IO_HZ 800000000
 #define CDNS_MRVL_XSPI_CLOCK_DIVIDED(div) ((CDNS_MRVL_XSPI_CLOCK_IO_HZ) / (div))
 
+/* Marvell XFER registers */
+#define XFER_SOFT_RESET		BIT(11)
+#define XFER_CS_N_HOLD		GENMASK(9, 6)
+#define XFER_RECEIVE_ENABLE	BIT(4)
+#define XFER_FUNC_ENABLE	BIT(3)
+#define XFER_CLK_CAPTURE_POL	BIT(2)
+#define XFER_CLK_DRIVE_POL	BIT(1)
+#define XFER_FUNC_START		BIT(0)
+
+#define XFER_QWORD_COUNT 32
+#define XFER_QWORD_BYTECOUNT 8
+
 enum cdns_xspi_stig_instr_type {
 	CDNS_XSPI_STIG_INSTR_TYPE_0,
 	CDNS_XSPI_STIG_INSTR_TYPE_1,
@@ -256,6 +275,7 @@  struct cdns_xspi_dev {
 	void __iomem *iobase;
 	void __iomem *auxbase;
 	void __iomem *sdmabase;
+	void __iomem *xferbase;
 
 	int irq;
 	int cur_cs;
@@ -270,6 +290,9 @@  struct cdns_xspi_dev {
 	const void *out_buffer;
 
 	u8 hw_num_banks;
+
+	bool xfer_in_progress;
+	int current_xfer_qword;
 };
 
 #define MRVL_DEFAULT_CLK 25000000
@@ -884,6 +907,221 @@  static bool cdns_xspi_get_hw_overlay(struct platform_device *pdev)
 	return (err >= 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;
+}
+
+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 +
+		      SPIX_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 %= 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 +
+		      SPIX_XFER_FUNC_CTRL_READ_DATA(cdns_xspi->current_xfer_qword));
+	u8 *ptr = (u8 *)&d;
+	int k;
+
+	for (k = 0; k < data_count % 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 %= 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;
+}
+
+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 ? CDNS_XSPI_POLL_DELAY_US : 0,
+		sleep ? CDNS_XSPI_POLL_TIMEOUT_US : 0);
+}
+
+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 ? CDNS_XSPI_POLL_DELAY_US : 0,
+		sleep ? CDNS_XSPI_POLL_TIMEOUT_US : 0);
+}
+
+int cdns_xspi_transfer_one_message_b0(struct spi_controller *master,
+					   struct spi_message *m)
+{
+	struct cdns_xspi_dev *cdns_xspi = spi_master_get_devdata(master);
+	struct spi_device *spi = m->spi;
+	struct spi_transfer *t = NULL;
+
+	const int max_len = XFER_QWORD_BYTECOUNT * XFER_QWORD_COUNT;
+	int current_cycle_count;
+	int cs = spi->chip_select;
+	int cs_change = 0;
+
+	/* Enable xfer state machine */
+	if (!cdns_xspi->xfer_in_progress) {
+		u32 xfer_control = readl(cdns_xspi->xferbase + SPIX_XFER_FUNC_CTRL);
+
+		cdns_xspi->current_xfer_qword = 0;
+		cdns_xspi->xfer_in_progress = true;
+		xfer_control |= (XFER_RECEIVE_ENABLE |
+				 XFER_CLK_CAPTURE_POL |
+				 XFER_FUNC_START |
+				 XFER_SOFT_RESET |
+				 FIELD_PREP(XFER_CS_N_HOLD, (1 << cs)));
+		xfer_control &= ~(XFER_FUNC_ENABLE | XFER_CLK_DRIVE_POL);
+		writel(xfer_control, cdns_xspi->xferbase + SPIX_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 /
+								 XFER_QWORD_BYTECOUNT;
+				if (current_cycle_count % XFER_QWORD_BYTECOUNT)
+					cdns_xspi->current_xfer_qword++;
+
+				cdns_xspi->current_xfer_qword %= XFER_QWORD_COUNT;
+			}
+			cs_change = t->cs_change;
+			t->len -= current_cycle_count;
+		}
+	}
+
+	if (!cs_change) {
+		u32 xfer_control = readl(cdns_xspi->xferbase + SPIX_XFER_FUNC_CTRL);
+
+		xfer_control &= ~(XFER_RECEIVE_ENABLE |
+				  XFER_SOFT_RESET);
+		writel(xfer_control, cdns_xspi->xferbase + SPIX_XFER_FUNC_CTRL);
+		cdns_xspi->xfer_in_progress = false;
+	}
+
+	m->status = 0;
+	spi_finalize_current_message(master);
+
+	return 0;
+}
+
 static int cdns_xspi_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -948,6 +1186,16 @@  static int cdns_xspi_probe(struct platform_device *pdev)
 		return PTR_ERR(cdns_xspi->auxbase);
 	}
 
+	if (hw_overlay) {
+		res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
+		cdns_xspi->xferbase = devm_ioremap_resource(dev, res);
+		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;
@@ -962,6 +1210,7 @@  static int cdns_xspi_probe(struct platform_device *pdev)
 	if (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);