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 | ||||
| 	  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 | ||||
| 	bool "Marvell Kirkwood SPI Driver" | ||||
| 	help | ||||
|   | ||||
| @@ -34,6 +34,7 @@ obj-$(CONFIG_FSL_DSPI) += fsl_dspi.o | ||||
| obj-$(CONFIG_FSL_ESPI) += fsl_espi.o | ||||
| obj-$(CONFIG_SYNQUACER_SPI) += spi-synquacer.o | ||||
| obj-$(CONFIG_ICH_SPI) +=  ich.o | ||||
| obj-$(CONFIG_IPROC_QSPI) += iproc_qspi.o | ||||
| obj-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o | ||||
| obj-$(CONFIG_MESON_SPIFC) += meson_spifc.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