mirror of
				https://xff.cz/git/u-boot/
				synced 2025-10-30 18:05:48 +01:00 
			
		
		
		
	driver: spi: add bcm iproc qspi support
IPROC qspi driver supports both BSPI and MSPI modes. Signed-off-by: Rayagonda Kokatanur <rayagonda.kokatanur@broadcom.com> Signed-off-by: Bharat Gooty <bharat.gooty@broadcom.com> Acked-by: Rayagonda Kokatanur <rayagonda.kokatanur@broadcom.com> Signed-off-by: Roman Bacik <roman.bacik@broadcom.com> Reviewed-by: Jagan Teki <jagan@amarulasolutions.com>
This commit is contained in:
		
				
					committed by
					
						 Jagan Teki
						Jagan Teki
					
				
			
			
				
	
			
			
			
						parent
						
							d56dfc90c7
						
					
				
				
					commit
					2ba1bd1e11
				
			| @@ -185,6 +185,12 @@ config ICH_SPI | |||||||
| 	  access the SPI NOR flash on platforms embedding this Intel | 	  access the SPI NOR flash on platforms embedding this Intel | ||||||
| 	  ICH IP core. | 	  ICH IP core. | ||||||
|  |  | ||||||
|  | config IPROC_QSPI | ||||||
|  | 	bool "Broadcom iProc QSPI Flash Controller driver" | ||||||
|  | 	help | ||||||
|  | 	  Enable Broadcom iProc QSPI Flash Controller driver. | ||||||
|  | 	  This driver can be used to access the SPI NOR flash. | ||||||
|  |  | ||||||
| config KIRKWOOD_SPI | config KIRKWOOD_SPI | ||||||
| 	bool "Marvell Kirkwood SPI Driver" | 	bool "Marvell Kirkwood SPI Driver" | ||||||
| 	help | 	help | ||||||
|   | |||||||
| @@ -34,6 +34,7 @@ obj-$(CONFIG_FSL_DSPI) += fsl_dspi.o | |||||||
| obj-$(CONFIG_FSL_ESPI) += fsl_espi.o | obj-$(CONFIG_FSL_ESPI) += fsl_espi.o | ||||||
| obj-$(CONFIG_SYNQUACER_SPI) += spi-synquacer.o | obj-$(CONFIG_SYNQUACER_SPI) += spi-synquacer.o | ||||||
| obj-$(CONFIG_ICH_SPI) +=  ich.o | obj-$(CONFIG_ICH_SPI) +=  ich.o | ||||||
|  | obj-$(CONFIG_IPROC_QSPI) += iproc_qspi.o | ||||||
| obj-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o | obj-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o | ||||||
| obj-$(CONFIG_MESON_SPIFC) += meson_spifc.o | obj-$(CONFIG_MESON_SPIFC) += meson_spifc.o | ||||||
| obj-$(CONFIG_MPC8XX_SPI) += mpc8xx_spi.o | obj-$(CONFIG_MPC8XX_SPI) += mpc8xx_spi.o | ||||||
|   | |||||||
							
								
								
									
										576
									
								
								drivers/spi/iproc_qspi.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										576
									
								
								drivers/spi/iproc_qspi.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,576 @@ | |||||||
|  | // SPDX-License-Identifier: GPL-2.0+ | ||||||
|  | /* | ||||||
|  |  * Copyright 2020-2021 Broadcom | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <common.h> | ||||||
|  | #include <dm.h> | ||||||
|  | #include <spi.h> | ||||||
|  | #include <spi-mem.h> | ||||||
|  | #include <asm/io.h> | ||||||
|  | #include <linux/delay.h> | ||||||
|  | #include <linux/err.h> | ||||||
|  | #include <linux/iopoll.h> | ||||||
|  | #include <linux/log2.h> | ||||||
|  |  | ||||||
|  | /* Delay required to change the mode of operation */ | ||||||
|  | #define BUSY_DELAY_US				1 | ||||||
|  | #define BUSY_TIMEOUT_US				200000 | ||||||
|  | #define DWORD_ALIGNED(a)			(!(((ulong)(a)) & 3)) | ||||||
|  |  | ||||||
|  | /* Chip attributes */ | ||||||
|  | #define QSPI_AXI_CLK				175000000 | ||||||
|  | #define SPBR_MIN				8U | ||||||
|  | #define SPBR_MAX				255U | ||||||
|  | #define NUM_CDRAM				16U | ||||||
|  |  | ||||||
|  | #define CDRAM_PCS0				2 | ||||||
|  | #define CDRAM_CONT				BIT(7) | ||||||
|  | #define CDRAM_BITS_EN				BIT(6) | ||||||
|  | #define CDRAM_QUAD_MODE				BIT(8) | ||||||
|  | #define CDRAM_RBIT_INPUT			BIT(10) | ||||||
|  | #define MSPI_SPE				BIT(6) | ||||||
|  | #define MSPI_CONT_AFTER_CMD			BIT(7) | ||||||
|  | #define MSPI_MSTR				BIT(7) | ||||||
|  |  | ||||||
|  | /* Register fields */ | ||||||
|  | #define MSPI_SPCR0_MSB_BITS_8			0x00000020 | ||||||
|  | #define BSPI_RAF_CONTROL_START_MASK		0x00000001 | ||||||
|  | #define BSPI_RAF_STATUS_SESSION_BUSY_MASK	0x00000001 | ||||||
|  | #define BSPI_RAF_STATUS_FIFO_EMPTY_MASK		0x00000002 | ||||||
|  | #define BSPI_STRAP_OVERRIDE_DATA_QUAD_SHIFT	3 | ||||||
|  | #define BSPI_STRAP_OVERRIDE_4BYTE_SHIFT	2 | ||||||
|  | #define BSPI_STRAP_OVERRIDE_DATA_DUAL_SHIFT	1 | ||||||
|  | #define BSPI_STRAP_OVERRIDE_SHIFT		0 | ||||||
|  | #define BSPI_BPC_DATA_SHIFT			0 | ||||||
|  | #define BSPI_BPC_MODE_SHIFT			8 | ||||||
|  | #define BSPI_BPC_ADDR_SHIFT			16 | ||||||
|  | #define BSPI_BPC_CMD_SHIFT			24 | ||||||
|  | #define BSPI_BPP_ADDR_SHIFT			16 | ||||||
|  |  | ||||||
|  | /* MSPI registers */ | ||||||
|  | #define MSPI_SPCR0_LSB_REG			0x000 | ||||||
|  | #define MSPI_SPCR0_MSB_REG			0x004 | ||||||
|  | #define MSPI_SPCR1_LSB_REG			0x008 | ||||||
|  | #define MSPI_SPCR1_MSB_REG			0x00c | ||||||
|  | #define MSPI_NEWQP_REG				0x010 | ||||||
|  | #define MSPI_ENDQP_REG				0x014 | ||||||
|  | #define MSPI_SPCR2_REG				0x018 | ||||||
|  | #define MSPI_STATUS_REG				0x020 | ||||||
|  | #define MSPI_CPTQP_REG				0x024 | ||||||
|  | #define MSPI_TX_REG				0x040 | ||||||
|  | #define MSPI_RX_REG				0x0c0 | ||||||
|  | #define MSPI_CDRAM_REG				0x140 | ||||||
|  | #define MSPI_WRITE_LOCK_REG			0x180 | ||||||
|  | #define MSPI_DISABLE_FLUSH_GEN_REG		0x184 | ||||||
|  |  | ||||||
|  | /* BSPI registers */ | ||||||
|  | #define BSPI_REVISION_ID_REG			0x000 | ||||||
|  | #define BSPI_SCRATCH_REG			0x004 | ||||||
|  | #define BSPI_MAST_N_BOOT_CTRL_REG		0x008 | ||||||
|  | #define BSPI_BUSY_STATUS_REG			0x00c | ||||||
|  | #define BSPI_INTR_STATUS_REG			0x010 | ||||||
|  | #define BSPI_B0_STATUS_REG			0x014 | ||||||
|  | #define BSPI_B0_CTRL_REG			0x018 | ||||||
|  | #define BSPI_B1_STATUS_REG			0x01c | ||||||
|  | #define BSPI_B1_CTRL_REG			0x020 | ||||||
|  | #define BSPI_STRAP_OVERRIDE_CTRL_REG		0x024 | ||||||
|  | #define BSPI_FLEX_MODE_ENABLE_REG		0x028 | ||||||
|  | #define BSPI_BITS_PER_CYCLE_REG			0x02C | ||||||
|  | #define BSPI_BITS_PER_PHASE_REG			0x030 | ||||||
|  | #define BSPI_CMD_AND_MODE_BYTE_REG		0x034 | ||||||
|  | #define BSPI_FLASH_UPPER_ADDR_BYTE_REG		0x038 | ||||||
|  | #define BSPI_XOR_VALUE_REG			0x03C | ||||||
|  | #define BSPI_XOR_ENABLE_REG			0x040 | ||||||
|  | #define BSPI_PIO_MODE_ENABLE_REG		0x044 | ||||||
|  | #define BSPI_PIO_IODIR_REG			0x048 | ||||||
|  | #define BSPI_PIO_DATA_REG			0x04C | ||||||
|  |  | ||||||
|  | /* RAF registers */ | ||||||
|  | #define BSPI_RAF_START_ADDRESS_REG		0x00 | ||||||
|  | #define BSPI_RAF_NUM_WORDS_REG			0x04 | ||||||
|  | #define BSPI_RAF_CTRL_REG			0x08 | ||||||
|  | #define BSPI_RAF_FULLNESS_REG			0x0C | ||||||
|  | #define BSPI_RAF_WATERMARK_REG			0x10 | ||||||
|  | #define BSPI_RAF_STATUS_REG			0x14 | ||||||
|  | #define BSPI_RAF_READ_DATA_REG			0x18 | ||||||
|  | #define BSPI_RAF_WORD_CNT_REG			0x1C | ||||||
|  | #define BSPI_RAF_CURR_ADDR_REG			0x20 | ||||||
|  |  | ||||||
|  | #define XFER_DUAL				BIT(30) | ||||||
|  | #define XFER_QUAD				BIT(31) | ||||||
|  |  | ||||||
|  | #define FLUSH_BIT				BIT(0) | ||||||
|  | #define MAST_N_BOOT_BIT				BIT(0) | ||||||
|  | #define WRITE_LOCK_BIT				BIT(0) | ||||||
|  |  | ||||||
|  | #define CEIL(m, n)				(((m) + (n) - 1) / (n)) | ||||||
|  | #define UPPER_BYTE_MASK				0xFF000000 | ||||||
|  | #define SIZE_16MB				0x001000000 | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * struct bcmspi_priv - qspi private structure | ||||||
|  |  * | ||||||
|  |  * @bspi_addr: bspi read address | ||||||
|  |  * @bspi_4byte_addr: bspi 4 byte address mode | ||||||
|  |  * @mspi: mspi registers block address | ||||||
|  |  * @bspi: bspi registers block address | ||||||
|  |  * @bspi_raf: bspi raf registers block address | ||||||
|  |  */ | ||||||
|  | struct bcmspi_priv { | ||||||
|  | 	u32 bspi_addr; | ||||||
|  | 	bool bspi_4byte_addr; | ||||||
|  | 	fdt_addr_t mspi; | ||||||
|  | 	fdt_addr_t bspi; | ||||||
|  | 	fdt_addr_t bspi_raf; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* BSPI mode */ | ||||||
|  |  | ||||||
|  | static void bspi_flush_prefetch_buffers(struct bcmspi_priv *priv) | ||||||
|  | { | ||||||
|  | 	writel(0, priv->bspi + BSPI_B0_CTRL_REG); | ||||||
|  | 	writel(0, priv->bspi + BSPI_B1_CTRL_REG); | ||||||
|  | 	writel(FLUSH_BIT, priv->bspi + BSPI_B0_CTRL_REG); | ||||||
|  | 	writel(FLUSH_BIT, priv->bspi + BSPI_B1_CTRL_REG); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int bspi_enable(struct bcmspi_priv *priv) | ||||||
|  | { | ||||||
|  | 	/* Disable write lock */ | ||||||
|  | 	writel(0, priv->mspi + MSPI_WRITE_LOCK_REG); | ||||||
|  | 	/* Flush prefetch buffers */ | ||||||
|  | 	bspi_flush_prefetch_buffers(priv); | ||||||
|  | 	/* Switch to BSPI */ | ||||||
|  | 	writel(0, priv->bspi + BSPI_MAST_N_BOOT_CTRL_REG); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int bspi_disable(struct bcmspi_priv *priv) | ||||||
|  | { | ||||||
|  | 	int ret; | ||||||
|  | 	uint val; | ||||||
|  |  | ||||||
|  | 	if ((readl(priv->bspi + BSPI_MAST_N_BOOT_CTRL_REG) & 1) == 0) { | ||||||
|  | 		ret = readl_poll_timeout(priv->bspi + BSPI_BUSY_STATUS_REG, val, !(val & 1), | ||||||
|  | 					 BUSY_TIMEOUT_US); | ||||||
|  | 		if (ret) { | ||||||
|  | 			printf("%s: Failed to disable bspi, device busy\n", __func__); | ||||||
|  | 			return ret; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		/* Switch to MSPI */ | ||||||
|  | 		writel(MAST_N_BOOT_BIT, priv->bspi + BSPI_MAST_N_BOOT_CTRL_REG); | ||||||
|  | 		udelay(BUSY_DELAY_US); | ||||||
|  |  | ||||||
|  | 		val = readl(priv->bspi + BSPI_MAST_N_BOOT_CTRL_REG); | ||||||
|  | 		if (!(val & 1)) { | ||||||
|  | 			printf("%s: Failed to enable mspi\n", __func__); | ||||||
|  | 			return -EBUSY; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Enable write lock */ | ||||||
|  | 	writel(WRITE_LOCK_BIT, priv->mspi + MSPI_WRITE_LOCK_REG); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int bspi_read_via_raf(struct bcmspi_priv *priv, u8 *rx, uint bytes) | ||||||
|  | { | ||||||
|  | 	u32 status; | ||||||
|  | 	uint words; | ||||||
|  | 	int aligned; | ||||||
|  | 	int ret; | ||||||
|  |  | ||||||
|  | 	/* | ||||||
|  | 	 * Flush data from the previous session (unlikely) | ||||||
|  | 	 * Read outstanding bits in the poll condition to empty FIFO | ||||||
|  | 	 */ | ||||||
|  | 	ret = readl_poll_timeout(priv->bspi_raf + BSPI_RAF_STATUS_REG, | ||||||
|  | 				 status, | ||||||
|  | 				 (!readl(priv->bspi_raf + BSPI_RAF_READ_DATA_REG) && | ||||||
|  | 				  status & BSPI_RAF_STATUS_FIFO_EMPTY_MASK) && | ||||||
|  | 				  !(status & BSPI_RAF_STATUS_SESSION_BUSY_MASK), | ||||||
|  | 				  BUSY_TIMEOUT_US); | ||||||
|  | 	if (ret) { | ||||||
|  | 		printf("%s: Failed to flush fifo\n", __func__); | ||||||
|  | 		return ret; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Transfer is in words */ | ||||||
|  | 	words = CEIL(bytes, 4); | ||||||
|  |  | ||||||
|  | 	/* Setup hardware */ | ||||||
|  | 	if (priv->bspi_4byte_addr) { | ||||||
|  | 		u32 val = priv->bspi_addr & UPPER_BYTE_MASK; | ||||||
|  |  | ||||||
|  | 		if (val != readl(priv->bspi + BSPI_FLASH_UPPER_ADDR_BYTE_REG)) { | ||||||
|  | 			writel(val, priv->bspi + BSPI_FLASH_UPPER_ADDR_BYTE_REG); | ||||||
|  | 			bspi_flush_prefetch_buffers(priv); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	writel(priv->bspi_addr & ~UPPER_BYTE_MASK, priv->bspi_raf + BSPI_RAF_START_ADDRESS_REG); | ||||||
|  | 	writel(words, priv->bspi_raf + BSPI_RAF_NUM_WORDS_REG); | ||||||
|  | 	writel(0, priv->bspi_raf + BSPI_RAF_WATERMARK_REG); | ||||||
|  |  | ||||||
|  | 	/* Start reading */ | ||||||
|  | 	writel(BSPI_RAF_CONTROL_START_MASK, priv->bspi_raf + BSPI_RAF_CTRL_REG); | ||||||
|  | 	aligned = DWORD_ALIGNED(rx); | ||||||
|  | 	while (bytes) { | ||||||
|  | 		status = readl(priv->bspi_raf + BSPI_RAF_STATUS_REG); | ||||||
|  | 		if (!(status & BSPI_RAF_STATUS_FIFO_EMPTY_MASK)) { | ||||||
|  | 			/* RAF is LE only, convert data to host endianness */ | ||||||
|  | 			u32 data = le32_to_cpu(readl(priv->bspi_raf + BSPI_RAF_READ_DATA_REG)); | ||||||
|  |  | ||||||
|  | 			/* Check if we can use the whole word */ | ||||||
|  | 			if (aligned && bytes >= 4) { | ||||||
|  | 				*(u32 *)rx = data; | ||||||
|  | 				rx += 4; | ||||||
|  | 				bytes -= 4; | ||||||
|  | 			} else { | ||||||
|  | 				uint chunk = min(bytes, 4U); | ||||||
|  |  | ||||||
|  | 				/* Read out bytes one by one */ | ||||||
|  | 				while (chunk) { | ||||||
|  | 					*rx++ = (u8)data; | ||||||
|  | 					data >>= 8; | ||||||
|  | 					chunk--; | ||||||
|  | 					bytes--; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 		if (!(status & BSPI_RAF_STATUS_SESSION_BUSY_MASK)) { | ||||||
|  | 			/* FIFO is empty and the session is done */ | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int bspi_read(struct bcmspi_priv *priv, u8 *rx, uint bytes) | ||||||
|  | { | ||||||
|  | 	int ret; | ||||||
|  |  | ||||||
|  | 	/* Transfer data */ | ||||||
|  | 	while (bytes > 0) { | ||||||
|  | 		/* Special handing since RAF cannot go across 16MB boundary */ | ||||||
|  | 		uint trans = bytes; | ||||||
|  | 		/* Divide into multiple transfers if it goes across the 16MB boundary */ | ||||||
|  | 		if (priv->bspi_4byte_addr && (priv->bspi_addr >> 24) != | ||||||
|  | 		    ((priv->bspi_addr + bytes) >> 24)) | ||||||
|  | 			trans = SIZE_16MB - (priv->bspi_addr & ~UPPER_BYTE_MASK); | ||||||
|  |  | ||||||
|  | 		ret = bspi_read_via_raf(priv, rx, trans); | ||||||
|  | 		if (ret) | ||||||
|  | 			return ret; | ||||||
|  |  | ||||||
|  | 		priv->bspi_addr += trans; | ||||||
|  | 		rx += trans; | ||||||
|  | 		bytes -= trans; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	bspi_flush_prefetch_buffers(priv); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void bspi_set_flex_mode(struct bcmspi_priv *priv, const struct spi_mem_op *op) | ||||||
|  | { | ||||||
|  | 	int bpp = (op->dummy.nbytes * 8) / op->dummy.buswidth; | ||||||
|  | 	int cmd = op->cmd.opcode; | ||||||
|  | 	int bpc = ilog2(op->data.buswidth) << BSPI_BPC_DATA_SHIFT | | ||||||
|  | 			  ilog2(op->addr.buswidth) << BSPI_BPC_ADDR_SHIFT | | ||||||
|  | 			  ilog2(op->cmd.buswidth) << BSPI_BPC_CMD_SHIFT; | ||||||
|  | 	int so =  BIT(BSPI_STRAP_OVERRIDE_SHIFT) | | ||||||
|  | 			  (op->data.buswidth > 1) << BSPI_STRAP_OVERRIDE_DATA_DUAL_SHIFT | | ||||||
|  | 			  (op->addr.nbytes > 3) << BSPI_STRAP_OVERRIDE_4BYTE_SHIFT | | ||||||
|  | 			  (op->data.buswidth > 3) << BSPI_STRAP_OVERRIDE_DATA_QUAD_SHIFT; | ||||||
|  |  | ||||||
|  | 	/* Disable flex mode first */ | ||||||
|  | 	writel(0, priv->bspi + BSPI_FLEX_MODE_ENABLE_REG); | ||||||
|  |  | ||||||
|  | 	/* Configure single, dual or quad mode */ | ||||||
|  | 	writel(bpc, priv->bspi + BSPI_BITS_PER_CYCLE_REG); | ||||||
|  |  | ||||||
|  | 	/* Opcode */ | ||||||
|  | 	writel(cmd, priv->bspi + BSPI_CMD_AND_MODE_BYTE_REG); | ||||||
|  |  | ||||||
|  | 	/* Count of dummy cycles */ | ||||||
|  | 	writel(bpp, priv->bspi + BSPI_BITS_PER_PHASE_REG); | ||||||
|  |  | ||||||
|  | 	/* Enable 4-byte address */ | ||||||
|  | 	if (priv->bspi_4byte_addr) { | ||||||
|  | 		setbits_le32(priv->bspi + BSPI_BITS_PER_PHASE_REG, BIT(BSPI_BPP_ADDR_SHIFT)); | ||||||
|  | 	} else { | ||||||
|  | 		clrbits_le32(priv->bspi + BSPI_BITS_PER_PHASE_REG, BIT(BSPI_BPP_ADDR_SHIFT)); | ||||||
|  | 		writel(0, priv->bspi + BSPI_FLASH_UPPER_ADDR_BYTE_REG); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Enable flex mode to take effect */ | ||||||
|  | 	writel(1, priv->bspi + BSPI_FLEX_MODE_ENABLE_REG); | ||||||
|  |  | ||||||
|  | 	/* Flush prefetch buffers since 32MB window BSPI could be used */ | ||||||
|  | 	bspi_flush_prefetch_buffers(priv); | ||||||
|  |  | ||||||
|  | 	/* Override the strap settings */ | ||||||
|  | 	writel(so, priv->bspi + BSPI_STRAP_OVERRIDE_CTRL_REG); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int bspi_exec_op(struct spi_slave *slave, const struct spi_mem_op *op) | ||||||
|  | { | ||||||
|  | 	struct udevice *bus = dev_get_parent(slave->dev); | ||||||
|  | 	struct bcmspi_priv *priv = dev_get_priv(bus); | ||||||
|  | 	int ret = -ENOTSUPP; | ||||||
|  |  | ||||||
|  | 	/* BSPI read */ | ||||||
|  | 	if (op->data.dir == SPI_MEM_DATA_IN && | ||||||
|  | 	    op->data.nbytes && op->addr.nbytes) { | ||||||
|  | 		priv->bspi_4byte_addr = (op->addr.nbytes > 3); | ||||||
|  | 		priv->bspi_addr = op->addr.val; | ||||||
|  | 		bspi_set_flex_mode(priv, op); | ||||||
|  | 		ret = bspi_read(priv, op->data.buf.in, op->data.nbytes); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static const struct spi_controller_mem_ops bspi_mem_ops = { | ||||||
|  | 	.exec_op = bspi_exec_op, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* MSPI mode */ | ||||||
|  |  | ||||||
|  | static int mspi_exec(struct bcmspi_priv *priv, uint bytes, const u8 *tx, u8 *rx, ulong flags) | ||||||
|  | { | ||||||
|  | 	u32 cdr = CDRAM_PCS0 | CDRAM_CONT; | ||||||
|  | 	bool use_16bits = !(bytes & 1); | ||||||
|  |  | ||||||
|  | 	if (flags & XFER_QUAD) { | ||||||
|  | 		cdr |= CDRAM_QUAD_MODE; | ||||||
|  |  | ||||||
|  | 		if (!tx) | ||||||
|  | 			cdr |= CDRAM_RBIT_INPUT; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	while (bytes) { | ||||||
|  | 		uint chunk; | ||||||
|  | 		uint queues; | ||||||
|  | 		uint i; | ||||||
|  | 		uint val; | ||||||
|  | 		int ret; | ||||||
|  |  | ||||||
|  | 		if (use_16bits) { | ||||||
|  | 			chunk = min(bytes, NUM_CDRAM * 2); | ||||||
|  | 			queues = (chunk + 1) / 2; | ||||||
|  | 			bytes -= chunk; | ||||||
|  |  | ||||||
|  | 			/* Fill CDRAMs */ | ||||||
|  | 			for (i = 0; i < queues; i++) | ||||||
|  | 				writel(cdr | CDRAM_BITS_EN, priv->mspi + MSPI_CDRAM_REG + 4 * i); | ||||||
|  |  | ||||||
|  | 			/* Fill TXRAMs */ | ||||||
|  | 			for (i = 0; i < chunk; i++) | ||||||
|  | 				writel(tx ? tx[i] : 0xff, priv->mspi + MSPI_TX_REG + 4 * i); | ||||||
|  | 		} else { | ||||||
|  | 			/* Determine how many bytes to process this time */ | ||||||
|  | 			chunk = min(bytes, NUM_CDRAM); | ||||||
|  | 			queues = chunk; | ||||||
|  | 			bytes -= chunk; | ||||||
|  |  | ||||||
|  | 			/* Fill CDRAMs and TXRAMS */ | ||||||
|  | 			for (i = 0; i < chunk; i++) { | ||||||
|  | 				writel(cdr, priv->mspi + MSPI_CDRAM_REG + 4 * i); | ||||||
|  | 				writel(tx ? tx[i] : 0xff, priv->mspi + MSPI_TX_REG + 8 * i); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		/* Setup queue pointers */ | ||||||
|  | 		writel(0, priv->mspi + MSPI_NEWQP_REG); | ||||||
|  | 		writel(queues - 1, priv->mspi + MSPI_ENDQP_REG); | ||||||
|  |  | ||||||
|  | 		/* Deassert CS if requested and it's the last transfer */ | ||||||
|  | 		if (bytes == 0 && (flags & SPI_XFER_END)) | ||||||
|  | 			clrbits_le32(priv->mspi + MSPI_CDRAM_REG + ((queues - 1) << 2), CDRAM_CONT); | ||||||
|  |  | ||||||
|  | 		/* Kick off */ | ||||||
|  | 		writel(0, priv->mspi + MSPI_STATUS_REG); | ||||||
|  | 		if (bytes == 0 && (flags & SPI_XFER_END)) | ||||||
|  | 			writel(MSPI_SPE, priv->mspi + MSPI_SPCR2_REG); | ||||||
|  | 		else | ||||||
|  | 			writel(MSPI_SPE | MSPI_CONT_AFTER_CMD, | ||||||
|  | 			       priv->mspi + MSPI_SPCR2_REG); | ||||||
|  |  | ||||||
|  | 		ret = readl_poll_timeout(priv->mspi + MSPI_STATUS_REG, val, (val & 1), | ||||||
|  | 					 BUSY_TIMEOUT_US); | ||||||
|  | 		if (ret) { | ||||||
|  | 			printf("%s: Failed to disable bspi, device busy\n", __func__); | ||||||
|  | 			return ret; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		/* Read data out */ | ||||||
|  | 		if (rx) { | ||||||
|  | 			if (use_16bits) { | ||||||
|  | 				for (i = 0; i < chunk; i++) | ||||||
|  | 					rx[i] = readl(priv->mspi + MSPI_RX_REG + 4 * i) & 0xff; | ||||||
|  | 			} else { | ||||||
|  | 				for (i = 0; i < chunk; i++) | ||||||
|  | 					rx[i] = readl(priv->mspi + MSPI_RX_REG + 8 * i + 4) & 0xff; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		/* Advance pointers */ | ||||||
|  | 		if (tx) | ||||||
|  | 			tx += chunk; | ||||||
|  | 		if (rx) | ||||||
|  | 			rx += chunk; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int mspi_xfer(struct udevice *dev, uint bitlen, const void *dout, void *din, ulong flags) | ||||||
|  | { | ||||||
|  | 	struct udevice *bus = dev_get_parent(dev); | ||||||
|  | 	struct bcmspi_priv *priv = dev_get_priv(bus); | ||||||
|  | 	uint bytes; | ||||||
|  | 	int ret = 0; | ||||||
|  |  | ||||||
|  | 	/* we can only transfer multiples of 8 bits */ | ||||||
|  | 	if (bitlen % 8) | ||||||
|  | 		return -EPROTONOSUPPORT; | ||||||
|  |  | ||||||
|  | 	bytes = bitlen / 8; | ||||||
|  |  | ||||||
|  | 	if (flags & SPI_XFER_BEGIN) { | ||||||
|  | 		/* Switch to MSPI */ | ||||||
|  | 		ret = bspi_disable(priv); | ||||||
|  | 		if (ret) | ||||||
|  | 			return ret; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* MSPI: Transfer */ | ||||||
|  | 	if (bytes) | ||||||
|  | 		ret = mspi_exec(priv, bytes, dout, din, flags); | ||||||
|  |  | ||||||
|  | 	if (flags & SPI_XFER_END) { | ||||||
|  | 		/* Switch back to BSPI */ | ||||||
|  | 		ret = bspi_enable(priv); | ||||||
|  | 		if (ret) | ||||||
|  | 			return ret; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* iProc interface */ | ||||||
|  |  | ||||||
|  | static int iproc_qspi_set_speed(struct udevice *bus, uint speed) | ||||||
|  | { | ||||||
|  | 	struct bcmspi_priv *priv = dev_get_priv(bus); | ||||||
|  | 	uint spbr; | ||||||
|  |  | ||||||
|  | 	/* MSPI: SCK configuration */ | ||||||
|  | 	spbr = (QSPI_AXI_CLK - 1) / (2 * speed) + 1; | ||||||
|  | 	writel(max(min(spbr, SPBR_MAX), SPBR_MIN), priv->mspi + MSPI_SPCR0_LSB_REG); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int iproc_qspi_set_mode(struct udevice *bus, uint mode) | ||||||
|  | { | ||||||
|  | 	struct bcmspi_priv *priv = dev_get_priv(bus); | ||||||
|  |  | ||||||
|  | 	/* MSPI: set master bit and mode */ | ||||||
|  | 	writel(MSPI_MSTR /* Master */ | (mode & 3), priv->mspi + MSPI_SPCR0_MSB_REG); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int iproc_qspi_claim_bus(struct udevice *dev) | ||||||
|  | { | ||||||
|  | 	/* Nothing to do */ | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int iproc_qspi_release_bus(struct udevice *dev) | ||||||
|  | { | ||||||
|  | 	struct udevice *bus = dev_get_parent(dev); | ||||||
|  | 	struct bcmspi_priv *priv = dev_get_priv(bus); | ||||||
|  |  | ||||||
|  | 	/* Make sure no operation is in progress */ | ||||||
|  | 	writel(0, priv->mspi + MSPI_SPCR2_REG); | ||||||
|  | 	udelay(BUSY_DELAY_US); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int iproc_qspi_of_to_plat(struct udevice *bus) | ||||||
|  | { | ||||||
|  | 	struct bcmspi_priv *priv = dev_get_priv(bus); | ||||||
|  |  | ||||||
|  | 	priv->bspi = dev_read_addr_name(bus, "bspi"); | ||||||
|  | 	if (IS_ERR((void *)priv->bspi)) { | ||||||
|  | 		printf("%s: Failed to get bspi base address\n", __func__); | ||||||
|  | 		return PTR_ERR((void *)priv->bspi); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	priv->bspi_raf = dev_read_addr_name(bus, "bspi_raf"); | ||||||
|  | 	if (IS_ERR((void *)priv->bspi_raf)) { | ||||||
|  | 		printf("%s: Failed to get bspi_raf base address\n", __func__); | ||||||
|  | 		return PTR_ERR((void *)priv->bspi_raf); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	priv->mspi = dev_read_addr_name(bus, "mspi"); | ||||||
|  | 	if (IS_ERR((void *)priv->mspi)) { | ||||||
|  | 		printf("%s: Failed to get mspi base address\n", __func__); | ||||||
|  | 		return PTR_ERR((void *)priv->mspi); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int iproc_qspi_probe(struct udevice *bus) | ||||||
|  | { | ||||||
|  | 	struct bcmspi_priv *priv = dev_get_priv(bus); | ||||||
|  |  | ||||||
|  | 	/* configure mspi */ | ||||||
|  | 	writel(0, priv->mspi + MSPI_SPCR1_LSB_REG); | ||||||
|  | 	writel(0, priv->mspi + MSPI_SPCR1_MSB_REG); | ||||||
|  | 	writel(0, priv->mspi + MSPI_NEWQP_REG); | ||||||
|  | 	writel(0, priv->mspi + MSPI_ENDQP_REG); | ||||||
|  | 	writel(0, priv->mspi + MSPI_SPCR2_REG); | ||||||
|  |  | ||||||
|  | 	/* configure bspi */ | ||||||
|  | 	bspi_enable(priv); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static const struct dm_spi_ops iproc_qspi_ops = { | ||||||
|  | 	.claim_bus	= iproc_qspi_claim_bus, | ||||||
|  | 	.release_bus	= iproc_qspi_release_bus, | ||||||
|  | 	.xfer		= mspi_xfer, | ||||||
|  | 	.set_speed	= iproc_qspi_set_speed, | ||||||
|  | 	.set_mode	= iproc_qspi_set_mode, | ||||||
|  | 	.mem_ops	= &bspi_mem_ops, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static const struct udevice_id iproc_qspi_ids[] = { | ||||||
|  | 	{ .compatible = "brcm,iproc-qspi" }, | ||||||
|  | 	{ } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | U_BOOT_DRIVER(iproc_qspi) = { | ||||||
|  | 	.name	= "iproc_qspi", | ||||||
|  | 	.id	= UCLASS_SPI, | ||||||
|  | 	.of_match = iproc_qspi_ids, | ||||||
|  | 	.ops	= &iproc_qspi_ops, | ||||||
|  | 	.of_to_plat = iproc_qspi_of_to_plat, | ||||||
|  | 	.priv_auto = sizeof(struct bcmspi_priv), | ||||||
|  | 	.probe	= iproc_qspi_probe, | ||||||
|  | }; | ||||||
		Reference in New Issue
	
	Block a user