diff mbox

[U-Boot,10/34] spi: Add zynq qspi controller driver

Message ID 72fa072b-a1a1-400e-9c62-a2966fe2c124@DB9EHSMHS010.ehs.local
State Superseded
Delegated to: Jagannadha Sutradharudu Teki
Headers show

Commit Message

Jagannadha Sutradharudu Teki Nov. 5, 2013, 5:46 p.m. UTC
Zynq qspi controller driver supports single bus
with singe chipselect.

Zynq qspi can be operated in below connection modes
- single qspi
- dual qspi, with dual stacked
- dual qspi, with dual parallel

Signed-off-by: Jagannadha Sutradharudu Teki <jaganna@xilinx.com>
---
 arch/arm/include/asm/arch-zynq/hardware.h |   1 +
 drivers/spi/Makefile                      |   1 +
 drivers/spi/zynq_qspi.c                   | 447 ++++++++++++++++++++++++++++++
 3 files changed, 449 insertions(+)
 create mode 100644 drivers/spi/zynq_qspi.c

Comments

thomas.langer@lantiq.com Nov. 5, 2013, 7:59 p.m. UTC | #1
Hello Jagan,

I have some comments and questions:

Am 05.11.2013 18:51, schrieb Jagannadha Sutradharudu Teki:
> +
> +/* Definitions of the flash commands - Flash insts in ascending order */
> +#define ZYNQ_QSPI_FLASH_INST_WRSR	0x01	/* Write status register */
> +#define ZYNQ_QSPI_FLASH_INST_PP		0x02	/* Page program */
> +#define ZYNQ_QSPI_FLASH_INST_WRDS	0x04	/* Write disable */
> +#define ZYNQ_QSPI_FLASH_INST_RDSR1	0x05	/* Read status register 1 */
> +#define ZYNQ_QSPI_FLASH_INST_WREN	0x06	/* Write enable */
> +#define ZYNQ_QSPI_FLASH_INST_AFR	0x0B	/* Fast read data bytes */
> +#define ZYNQ_QSPI_FLASH_INST_BE_4K	0x20	/* Erase 4KiB block */
> +#define ZYNQ_QSPI_FLASH_INST_RDSR2	0x35	/* Read status register 2 */
> +#define ZYNQ_QSPI_FLASH_INST_BE_32K	0x52	/* Erase 32KiB block */
> +#define ZYNQ_QSPI_FLASH_INST_RDID	0x9F	/* Read JEDEC ID */
> +#define ZYNQ_QSPI_FLASH_INST_SE		0xD8	/* Sector erase (usually 64KB)*/
> +
Why needs the spi controller this list of flash commands?
It is the job of the flash driver to handle this, the spi controller
only forwards this to the devices!

> +
> +/* List of all the QSPI instructions and its format */
> +static struct zynq_qspi_inst_format flash_inst[] = {
> +	{ZYNQ_QSPI_FLASH_INST_WRSR, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
> +	{ZYNQ_QSPI_FLASH_INST_PP, 4, ZYNQ_QSPI_TXD_00_00_OFFSET},
> +	{ZYNQ_QSPI_FLASH_INST_WRDS, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
> +	{ZYNQ_QSPI_FLASH_INST_RDSR1, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
> +	{ZYNQ_QSPI_FLASH_INST_WREN, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
> +	{ZYNQ_QSPI_FLASH_INST_AFR, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
> +	{ZYNQ_QSPI_FLASH_INST_BE_4K, 4, ZYNQ_QSPI_TXD_00_00_OFFSET},
> +	{ZYNQ_QSPI_FLASH_INST_RDSR2, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
> +	{ZYNQ_QSPI_FLASH_INST_BE_32K, 4, ZYNQ_QSPI_TXD_00_00_OFFSET},
> +	{ZYNQ_QSPI_FLASH_INST_RDID, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
> +	{ZYNQ_QSPI_FLASH_INST_SE, 4, ZYNQ_QSPI_TXD_00_00_OFFSET},
> +	/* Add all the instructions supported by the flash device */
> +};
Is this table used to encode which parts needs DUAL or QUAD transfers?
And are you sure the table is complete? The flash drivers don't use more
instructions?
What happens if some device is connected, which is no flash?
> +
> +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
> +		void *din, unsigned long flags)
> +{
> +	struct zynq_qspi_slave *zslave = to_zynq_qspi_slave(slave);
> +	u32 len = bitlen / 8, tx_tvl;
> +	u32 buf, status;
> +
> +	debug("spi_xfer: bus:%i cs:%i bitlen:%i len:%i flags:%lx\n",
> +	      slave->bus, slave->cs, bitlen, len, flags);
> +
> +	if (bitlen == 0)
> +		return -1;
> +
> +	if (bitlen % 8) {
> +		debug("spi_xfer: Non byte aligned SPI transfer\n");
> +		return -1;
> +	}
> +
> +	if (flags & SPI_XFER_BEGIN)
> +		spi_cs_activate(slave);
> +
> +	zslave->tx_len = len;
> +	zslave->rx_len = len;
> +	zslave->tx_buf = dout;
> +	zslave->rx_buf = din;
> +	while (zslave->rx_len > 0) {
> +		/* Write the data into TX FIFO - tx threshold is fifo_depth */
> +		tx_tvl = 0;
> +		while ((tx_tvl < zslave->fifo_depth) && zslave->tx_len) {
> +			if (zynq_qspi_process_tx(zslave) < 0) {
> +				flags |= SPI_XFER_END;
> +				goto out;
> +			}
> +			tx_tvl++;
> +		}
> +
> +		/* Check TX FIFO completion */
> +		if (zynq_qspi_check_txfifo(zslave) < 0) {
> +			flags |= SPI_XFER_END;
> +			goto out;
> +		}
> +
> +		/* Read the data from RX FIFO */
> +		status = readl(&zslave->base->isr);
> +		while (status & ZYNQ_QSPI_IXR_RXNEMPTY_MASK) {
> +			buf = readl(&zslave->base->rxdr);
> +			if (zslave->rx_len < 4)
> +				zynq_qspi_read(zslave, buf, zslave->rx_len);
> +			else
> +				zynq_qspi_read(zslave, buf, 4);
> +			status = readl(&zslave->base->isr);
> +		}
> +	}
> +
> +out:
> +	if (flags & SPI_XFER_END)
> +		spi_cs_deactivate(slave);
> +
> +	return 0;
> +}
In this function I miss the parts, where the caller (e.g. the flash
driver) tells the controller to transfer some parts in DUAL or QUAD mode!

Best Regards,
Thomas
Dinh Nguyen Nov. 6, 2013, 4:44 a.m. UTC | #2
On 11/5/13 11:46 AM, Jagannadha Sutradharudu Teki wrote:
> Zynq qspi controller driver supports single bus
> with singe chipselect.
>
> Zynq qspi can be operated in below connection modes
> - single qspi
> - dual qspi, with dual stacked
> - dual qspi, with dual parallel
>
> Signed-off-by: Jagannadha Sutradharudu Teki <jaganna@xilinx.com>
> ---
>  arch/arm/include/asm/arch-zynq/hardware.h |   1 +
>  drivers/spi/Makefile                      |   1 +
>  drivers/spi/zynq_qspi.c                   | 447 ++++++++++++++++++++++++++++++
>  3 files changed, 449 insertions(+)
>  create mode 100644 drivers/spi/zynq_qspi.c
>
> diff --git a/arch/arm/include/asm/arch-zynq/hardware.h b/arch/arm/include/asm/arch-zynq/hardware.h
> index cd69677..05870ae 100644
> --- a/arch/arm/include/asm/arch-zynq/hardware.h
> +++ b/arch/arm/include/asm/arch-zynq/hardware.h
> @@ -19,6 +19,7 @@
>  #define ZYNQ_I2C_BASEADDR1		0xE0005000
>  #define ZYNQ_SPI_BASEADDR0		0xE0006000
>  #define ZYNQ_SPI_BASEADDR1		0xE0007000
> +#define ZYNQ_QSPI_BASEADDR		0xE000D000
>  #define ZYNQ_DDRC_BASEADDR		0xF8006000
>  
>  /* Reflect slcr offsets */
> diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
> index 27902fe..5fafee0 100644
> --- a/drivers/spi/Makefile
> +++ b/drivers/spi/Makefile
> @@ -37,3 +37,4 @@ obj-$(CONFIG_TEGRA114_SPI) += tegra114_spi.o
>  obj-$(CONFIG_TI_QSPI) += ti_qspi.o
>  obj-$(CONFIG_XILINX_SPI) += xilinx_spi.o
>  obj-$(CONFIG_ZYNQ_SPI) += zynq_spi.o
> +obj-$(CONFIG_ZYNQ_QSPI) += zynq_qspi.o
> diff --git a/drivers/spi/zynq_qspi.c b/drivers/spi/zynq_qspi.c
> new file mode 100644
> index 0000000..f38ebca
> --- /dev/null
> +++ b/drivers/spi/zynq_qspi.c
> @@ -0,0 +1,447 @@
> +/*
> + * (C) Copyright 2013 Xilinx, Inc.
> + *
> + * Zynq PS Quad-SPI(QSPI) controller driver (master mode only)
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +
> +#include <config.h>
> +#include <common.h>
> +#include <malloc.h>
> +#include <spi.h>
> +#include <asm/io.h>
> +#include <asm/arch/hardware.h>
> +
> +/* zynq spi register bit masks ZYNQ_QSPI_<REG>_<BIT>_MASK */
> +#define ZYNQ_QSPI_CR_IFMODE_MASK	(1 << 31)	/* Flash intrface mode*/
> +#define ZYNQ_QSPI_CR_MSA_MASK		(1 << 15)	/* Manual start enb */
> +#define ZYNQ_QSPI_CR_MCS_MASK		(1 << 14)	/* Manual chip select */
> +#define ZYNQ_QSPI_CR_PCS_MASK		(1 << 10)	/* Peri chip select */
> +#define ZYNQ_QSPI_CR_FW_MASK		(0x3 << 6)	/* FIFO width */
> +#define ZYNQ_QSPI_CR_BRD_MASK		(0x7 << 3)	/* Baud rate div */
> +#define ZYNQ_QSPI_CR_CPHA_MASK		(1 << 2)	/* Clock phase */
> +#define ZYNQ_QSPI_CR_CPOL_MASK		(1 << 1)	/* Clock polarity */
> +#define ZYNQ_QSPI_CR_MSTREN_MASK	(1 << 0)	/* Mode select */
> +#define ZYNQ_QSPI_IXR_RXNEMPTY_MASK	(1 << 4)	/* RX_FIFO_not_empty */
> +#define ZYNQ_QSPI_IXR_TXOW_MASK		(1 << 2)	/* TX_FIFO_not_full */
> +#define ZYNQ_QSPI_IXR_ALL_MASK		0x7F		/* All IXR bits */
> +#define ZYNQ_QSPI_ENR_SPI_EN_MASK	(1 << 0)	/* SPI Enable */
> +
> +/* QSPI Transmit Data Register */
> +#define ZYNQ_QSPI_TXD_00_00_OFFSET	0x1C /* Transmit 4-byte inst */
> +#define ZYNQ_QSPI_TXD_00_01_OFFSET	0x80 /* Transmit 1-byte inst */
> +#define ZYNQ_QSPI_TXD_00_10_OFFSET	0x84 /* Transmit 2-byte inst */
> +#define ZYNQ_QSPI_TXD_00_11_OFFSET	0x88 /* Transmit 3-byte inst */
> +
> +/* Definitions of the flash commands - Flash insts in ascending order */
> +#define ZYNQ_QSPI_FLASH_INST_WRSR	0x01	/* Write status register */
> +#define ZYNQ_QSPI_FLASH_INST_PP		0x02	/* Page program */
> +#define ZYNQ_QSPI_FLASH_INST_WRDS	0x04	/* Write disable */
> +#define ZYNQ_QSPI_FLASH_INST_RDSR1	0x05	/* Read status register 1 */
> +#define ZYNQ_QSPI_FLASH_INST_WREN	0x06	/* Write enable */
> +#define ZYNQ_QSPI_FLASH_INST_AFR	0x0B	/* Fast read data bytes */
> +#define ZYNQ_QSPI_FLASH_INST_BE_4K	0x20	/* Erase 4KiB block */
> +#define ZYNQ_QSPI_FLASH_INST_RDSR2	0x35	/* Read status register 2 */
> +#define ZYNQ_QSPI_FLASH_INST_BE_32K	0x52	/* Erase 32KiB block */
> +#define ZYNQ_QSPI_FLASH_INST_RDID	0x9F	/* Read JEDEC ID */
> +#define ZYNQ_QSPI_FLASH_INST_SE		0xD8	/* Sector erase (usually 64KB)*/
> +
> +#define ZYNQ_QSPI_FIFO_DEPTH		63
> +#ifndef CONFIG_SYS_ZYNQ_QSPI_WAIT
> +#define CONFIG_SYS_ZYNQ_QSPI_WAIT	CONFIG_SYS_HZ/100	/* 10 ms */
> +#endif
> +
> +/* zynq qspi register set */
> +struct zynq_qspi_regs {
> +	u32 cr;		/* 0x00 */
> +	u32 isr;	/* 0x04 */
> +	u32 ier;	/* 0x08 */
> +	u32 idr;	/* 0x0C */
> +	u32 imr;	/* 0x10 */
> +	u32 enr;	/* 0x14 */
> +	u32 dr;		/* 0x18 */
> +	u32 txd0r;	/* 0x1C */
> +	u32 rxdr;	/* 0x20 */
> +	u32 sicr;	/* 0x24 */
> +	u32 txftr;	/* 0x28 */
> +	u32 rxftr;	/* 0x2C */
> +	u32 gpior;	/* 0x30 */
> +	u32 reserved0[19];
> +	u32 txd1r;	/* 0x80 */
> +	u32 txd2r;	/* 0x84 */
> +	u32 txd3r;	/* 0x88 */
> +};
> +
> +/*
> + * struct zynq_qspi_inst_format - Defines qspi flash instruction format
> + * @inst:	Instruction code
> + * @inst_size:	Size of the instruction including address bytes
> + * @inst_off:	Register address where instruction has to be written
> + */
> +struct zynq_qspi_inst_format {
> +	u8 inst;
> +	u8 inst_size;
> +	u8 inst_off;
> +};
> +
> +/* List of all the QSPI instructions and its format */
> +static struct zynq_qspi_inst_format flash_inst[] = {
> +	{ZYNQ_QSPI_FLASH_INST_WRSR, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
> +	{ZYNQ_QSPI_FLASH_INST_PP, 4, ZYNQ_QSPI_TXD_00_00_OFFSET},
> +	{ZYNQ_QSPI_FLASH_INST_WRDS, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
> +	{ZYNQ_QSPI_FLASH_INST_RDSR1, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
> +	{ZYNQ_QSPI_FLASH_INST_WREN, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
> +	{ZYNQ_QSPI_FLASH_INST_AFR, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
> +	{ZYNQ_QSPI_FLASH_INST_BE_4K, 4, ZYNQ_QSPI_TXD_00_00_OFFSET},
> +	{ZYNQ_QSPI_FLASH_INST_RDSR2, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
> +	{ZYNQ_QSPI_FLASH_INST_BE_32K, 4, ZYNQ_QSPI_TXD_00_00_OFFSET},
> +	{ZYNQ_QSPI_FLASH_INST_RDID, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
> +	{ZYNQ_QSPI_FLASH_INST_SE, 4, ZYNQ_QSPI_TXD_00_00_OFFSET},
> +	/* Add all the instructions supported by the flash device */
> +};
> +
> +/* zynq spi slave */
> +struct zynq_qspi_slave {
> +	struct spi_slave slave;
> +	struct zynq_qspi_regs *base;
> +	u8 mode;
> +	u8 is_inst;
> +	u8 fifo_depth;
> +	const void *tx_buf;
> +	void *rx_buf;
> +	u32 tx_len;
> +	u32 rx_len;
> +	u32 speed_hz;
> +	u32 input_hz;
> +	u32 req_hz;
> +};
> +
> +static inline struct zynq_qspi_slave *to_zynq_qspi_slave(
> +		struct spi_slave *slave)
> +{
> +	return container_of(slave, struct zynq_qspi_slave, slave);
> +}
> +
> +static void zynq_qspi_init_hw(struct zynq_qspi_slave *zslave)
> +{
> +	u32 confr;
> +
> +	/* Disable SPI */
> +	writel(~ZYNQ_QSPI_ENR_SPI_EN_MASK, &zslave->base->enr);
> +
> +	/* Disable Interrupts */
> +	writel(ZYNQ_QSPI_IXR_ALL_MASK, &zslave->base->idr);
> +
> +	/* Clear RX FIFO */
> +	while (readl(&zslave->base->isr) &
> +			ZYNQ_QSPI_IXR_RXNEMPTY_MASK)
> +		readl(&zslave->base->rxdr);
> +
> +	/* Clear Interrupts */
> +	writel(ZYNQ_QSPI_IXR_ALL_MASK, &zslave->base->isr);
> +
> +	/* Manual slave select and Auto start */
> +	confr = ZYNQ_QSPI_CR_IFMODE_MASK | ZYNQ_QSPI_CR_MCS_MASK |
> +		ZYNQ_QSPI_CR_PCS_MASK | ZYNQ_QSPI_CR_FW_MASK |
> +		ZYNQ_QSPI_CR_MSTREN_MASK;
> +	confr &= ~ZYNQ_QSPI_CR_MSA_MASK;
> +	confr &= ~ZYNQ_QSPI_CR_MSA_MASK;
Why &= ~ZYNQ_QSPI_CR_MSA_MASK twice?
> +	writel(confr, &zslave->base->cr);
> +
> +	/* Enable SPI */
> +	writel(ZYNQ_QSPI_ENR_SPI_EN_MASK, &zslave->base->enr);
> +}
> +
> +/*
> + * zynq_qspi_read - Copy data to RX buffer
> + * @zqspi:      Pointer to zynq_qspi_slave
> + * @data:       The 32 bit variable where data is stored
> + * @size:       Number of bytes to be copied from data to RX buffer
> + */
> +static void zynq_qspi_read(struct zynq_qspi_slave *zslave, u32 data, u8 size)
> +{
> +	if (zslave->rx_buf) {
> +		data >>= (4 - size) * 8;
> +		data = le32_to_cpu(data);
> +		memcpy((u8 *)zslave->rx_buf, &data, size);
> +		zslave->rx_buf += size;
> +	}
> +
> +	zslave->rx_len -= size;
> +}
> +
> +/*
> + * zynq_qspi_write - Copy data from TX buffer
> + * @zslave:	Pointer to zynq_qspi_slave
> + * @data:	Pointer to the 32 bit variable where data is to be copied
> + * @size:	Number of bytes to be copied from TX buffer to data
> + */
> +static void zynq_qspi_write(struct zynq_qspi_slave *zslave, u32 *data, u8 size)
> +{
> +	if (zslave->tx_buf) {
> +		switch (size) {
> +		case 1:
> +			*data = *((u8 *)zslave->tx_buf);
> +			zslave->tx_buf += 1;
> +			*data |= 0xFFFFFF00;
> +			break;
> +		case 2:
> +			*data = *((u16 *)zslave->tx_buf);
> +			zslave->tx_buf += 2;
> +			*data |= 0xFFFF0000;
> +			break;
> +		case 3:
> +			*data = *((u16 *)zslave->tx_buf);
> +			zslave->tx_buf += 2;
> +			*data |= (*((u8 *)zslave->tx_buf) << 16);
> +			zslave->tx_buf += 1;
> +			*data |= 0xFF000000;
> +			break;
> +		case 4:
> +			/* Can not assume word aligned buffer */
> +			memcpy(data, zslave->tx_buf, size);
> +			zslave->tx_buf += 4;
> +			break;
> +		default:
> +			/* This will never execute */
> +			break;
> +		}
> +	} else {
> +		*data = 0;
> +	}
> +
> +	zslave->tx_len -= size;
> +}
> +
> +static int zynq_qspi_check_txfifo(struct zynq_qspi_slave *zslave)
> +{
> +	u32 ts, status;
> +
> +	ts = get_timer(0);
> +	status = readl(&zslave->base->isr);
> +	while (!(status & ZYNQ_QSPI_IXR_TXOW_MASK)) {
> +		if (get_timer(ts) > CONFIG_SYS_ZYNQ_QSPI_WAIT) {
> +			printf("spi_xfer: Timeout! TX FIFO not full\n");
> +			return -1;
> +		}
> +		status = readl(&zslave->base->isr);
> +	}
> +
> +	return 0;
> +}
> +
> +static int zynq_qspi_process_tx(struct zynq_qspi_slave *zslave)
> +{
> +	struct zynq_qspi_inst_format *curr_inst;
> +	u8 inst, index;
> +	u32 buf;
> +
> +	inst = *(u8 *)zslave->tx_buf;
> +	/* instuction */
> +	if (inst && zslave->is_inst) {
> +		for (index = 0; index < ARRAY_SIZE(flash_inst); index++)
> +			if (inst == flash_inst[index].inst)
> +				break;
> +
> +		if (index == ARRAY_SIZE(flash_inst)) {
> +			printf("spi_xfer: Unsupported inst %02x\n", inst);
> +			return -1;
> +		}
> +
> +		curr_inst = &flash_inst[index];
> +		debug("spi_xfer: inst:%02x inst_size:%d inst_off:%02x\n",
> +		      curr_inst->inst, curr_inst->inst_size,
> +		      curr_inst->inst_off);
> +
> +		zynq_qspi_write(zslave, &buf, curr_inst->inst_size);
> +		writel(buf, &zslave->base->cr + (curr_inst->inst_off / 4));
> +		zslave->is_inst = 0;
> +	} else if (!zslave->is_inst) { /* addr + data */
> +		if (zslave->tx_len < 4) {
> +			/* Check TXOW for txd1, txd2 and txd3 */
> +			if (zynq_qspi_check_txfifo(zslave) < 0)
> +				return -1;
> +
> +			zynq_qspi_write(zslave, &buf, zslave->tx_len);
> +			writel(buf,
> +			       &zslave->base->txd1r + (zslave->tx_len - 1));
> +		} else {
> +			zynq_qspi_write(zslave, &buf, 4);
> +			writel(buf, &zslave->base->txd0r);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +int spi_cs_is_valid(unsigned int bus, unsigned int cs)
> +{
> +	/* 1 bus with 1 chipselect */
> +	return bus < 1 && cs < 1;
> +}
> +
> +void spi_cs_activate(struct spi_slave *slave)
> +{
> +	struct zynq_qspi_slave *zslave = to_zynq_qspi_slave(slave);
> +
> +	debug("spi_cs_activate: 0x%08x\n", (u32)slave);
> +	clrbits_le32(&zslave->base->cr, ZYNQ_QSPI_CR_PCS_MASK);
> +
> +	zslave->is_inst = 1;
> +}
> +
> +void spi_cs_deactivate(struct spi_slave *slave)
> +{
> +	struct zynq_qspi_slave *zslave = to_zynq_qspi_slave(slave);
> +
> +	debug("spi_cs_deactivate: 0x%08x\n", (u32)slave);
> +	setbits_le32(&zslave->base->cr, ZYNQ_QSPI_CR_PCS_MASK);
> +
> +	zslave->is_inst = 0;
> +}
> +
> +void spi_init()
> +{
> +	/* nothing to do */
> +}
> +
> +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
> +		unsigned int max_hz, unsigned int mode)
> +{
> +	struct zynq_qspi_slave *zslave;
> +
> +	if (!spi_cs_is_valid(bus, cs))
> +		return NULL;
> +
> +	zslave = spi_alloc_slave(struct zynq_qspi_slave, bus, cs);
> +	if (!zslave) {
> +		printf("SPI_error: Fail to allocate zynq_qspi_slave\n");
> +		return NULL;
> +	}
> +
> +	zslave->base = (struct zynq_qspi_regs *)ZYNQ_QSPI_BASEADDR;
> +	zslave->mode = mode;
> +	zslave->fifo_depth = ZYNQ_QSPI_FIFO_DEPTH;
> +	zslave->input_hz = 200000000;
Should this be a CONFIG define?

Dinh
> +	zslave->speed_hz = zslave->input_hz / 2;
> +	zslave->req_hz = max_hz;
> +
> +	/* init the zynq spi hw */
> +	zynq_qspi_init_hw(zslave);
> +
> +	return &zslave->slave;
> +}
> +
> +void spi_free_slave(struct spi_slave *slave)
> +{
> +	struct zynq_qspi_slave *zslave = to_zynq_qspi_slave(slave);
> +
> +	debug("spi_free_slave: 0x%08x\n", (u32)slave);
> +	free(zslave);
> +}
> +
> +int spi_claim_bus(struct spi_slave *slave)
> +{
> +	struct zynq_qspi_slave *zslave = to_zynq_qspi_slave(slave);
> +	u32 confr = 0;
> +	u8 baud_rate_val = 0;
> +
> +	writel(~ZYNQ_QSPI_ENR_SPI_EN_MASK, &zslave->base->enr);
> +
> +	/* Set the SPI Clock phase and polarities */
> +	confr = readl(&zslave->base->cr);
> +	confr &= ~(ZYNQ_QSPI_CR_CPHA_MASK | ZYNQ_QSPI_CR_CPOL_MASK);
> +	if (zslave->mode & SPI_CPHA)
> +		confr |= ZYNQ_QSPI_CR_CPHA_MASK;
> +	if (zslave->mode & SPI_CPOL)
> +		confr |= ZYNQ_QSPI_CR_CPOL_MASK;
> +
> +	/* Set the clock frequency */
> +	if (zslave->req_hz == 0) {
> +		/* Set baudrate x8, if the req_hz is 0 */
> +		baud_rate_val = 0x2;
> +	} else if (zslave->speed_hz != zslave->req_hz) {
> +		while ((baud_rate_val < 8) &&
> +				((zslave->input_hz /
> +				(2 << baud_rate_val)) > zslave->req_hz))
> +			baud_rate_val++;
> +		zslave->speed_hz = zslave->req_hz / (2 << baud_rate_val);
> +	}
> +	confr &= ~ZYNQ_QSPI_CR_BRD_MASK;
> +	confr |= (baud_rate_val << 3);
> +	writel(confr, &zslave->base->cr);
> +
> +	writel(ZYNQ_QSPI_ENR_SPI_EN_MASK, &zslave->base->enr);
> +
> +	return 0;
> +}
> +
> +void spi_release_bus(struct spi_slave *slave)
> +{
> +	struct zynq_qspi_slave *zslave = to_zynq_qspi_slave(slave);
> +
> +	debug("spi_release_bus: 0x%08x\n", (u32)slave);
> +	writel(~ZYNQ_QSPI_ENR_SPI_EN_MASK, &zslave->base->enr);
> +}
> +
> +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
> +		void *din, unsigned long flags)
> +{
> +	struct zynq_qspi_slave *zslave = to_zynq_qspi_slave(slave);
> +	u32 len = bitlen / 8, tx_tvl;
> +	u32 buf, status;
> +
> +	debug("spi_xfer: bus:%i cs:%i bitlen:%i len:%i flags:%lx\n",
> +	      slave->bus, slave->cs, bitlen, len, flags);
> +
> +	if (bitlen == 0)
> +		return -1;
> +
> +	if (bitlen % 8) {
> +		debug("spi_xfer: Non byte aligned SPI transfer\n");
> +		return -1;
> +	}
> +
> +	if (flags & SPI_XFER_BEGIN)
> +		spi_cs_activate(slave);
> +
> +	zslave->tx_len = len;
> +	zslave->rx_len = len;
> +	zslave->tx_buf = dout;
> +	zslave->rx_buf = din;
> +	while (zslave->rx_len > 0) {
> +		/* Write the data into TX FIFO - tx threshold is fifo_depth */
> +		tx_tvl = 0;
> +		while ((tx_tvl < zslave->fifo_depth) && zslave->tx_len) {
> +			if (zynq_qspi_process_tx(zslave) < 0) {
> +				flags |= SPI_XFER_END;
> +				goto out;
> +			}
> +			tx_tvl++;
> +		}
> +
> +		/* Check TX FIFO completion */
> +		if (zynq_qspi_check_txfifo(zslave) < 0) {
> +			flags |= SPI_XFER_END;
> +			goto out;
> +		}
> +
> +		/* Read the data from RX FIFO */
> +		status = readl(&zslave->base->isr);
> +		while (status & ZYNQ_QSPI_IXR_RXNEMPTY_MASK) {
> +			buf = readl(&zslave->base->rxdr);
> +			if (zslave->rx_len < 4)
> +				zynq_qspi_read(zslave, buf, zslave->rx_len);
> +			else
> +				zynq_qspi_read(zslave, buf, 4);
> +			status = readl(&zslave->base->isr);
> +		}
> +	}
> +
> +out:
> +	if (flags & SPI_XFER_END)
> +		spi_cs_deactivate(slave);
> +
> +	return 0;
> +}
Dinh Nguyen Nov. 6, 2013, 5:02 a.m. UTC | #3
On 11/5/13 11:46 AM, Jagannadha Sutradharudu Teki wrote:
> Zynq qspi controller driver supports single bus
> with singe chipselect.
>
> Zynq qspi can be operated in below connection modes
> - single qspi
> - dual qspi, with dual stacked
> - dual qspi, with dual parallel
>
> Signed-off-by: Jagannadha Sutradharudu Teki <jaganna@xilinx.com>
> ---
>  arch/arm/include/asm/arch-zynq/hardware.h |   1 +
>  drivers/spi/Makefile                      |   1 +
>  drivers/spi/zynq_qspi.c                   | 447 ++++++++++++++++++++++++++++++
>  3 files changed, 449 insertions(+)
>  create mode 100644 drivers/spi/zynq_qspi.c
>
> diff --git a/arch/arm/include/asm/arch-zynq/hardware.h b/arch/arm/include/asm/arch-zynq/hardware.h
> index cd69677..05870ae 100644
> --- a/arch/arm/include/asm/arch-zynq/hardware.h
> +++ b/arch/arm/include/asm/arch-zynq/hardware.h
> @@ -19,6 +19,7 @@
>  #define ZYNQ_I2C_BASEADDR1		0xE0005000
>  #define ZYNQ_SPI_BASEADDR0		0xE0006000
>  #define ZYNQ_SPI_BASEADDR1		0xE0007000
> +#define ZYNQ_QSPI_BASEADDR		0xE000D000
>  #define ZYNQ_DDRC_BASEADDR		0xF8006000
>  
>  /* Reflect slcr offsets */
> diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
> index 27902fe..5fafee0 100644
> --- a/drivers/spi/Makefile
> +++ b/drivers/spi/Makefile
> @@ -37,3 +37,4 @@ obj-$(CONFIG_TEGRA114_SPI) += tegra114_spi.o
>  obj-$(CONFIG_TI_QSPI) += ti_qspi.o
>  obj-$(CONFIG_XILINX_SPI) += xilinx_spi.o
>  obj-$(CONFIG_ZYNQ_SPI) += zynq_spi.o
> +obj-$(CONFIG_ZYNQ_QSPI) += zynq_qspi.o
> diff --git a/drivers/spi/zynq_qspi.c b/drivers/spi/zynq_qspi.c
> new file mode 100644
> index 0000000..f38ebca
> --- /dev/null
> +++ b/drivers/spi/zynq_qspi.c
> @@ -0,0 +1,447 @@
> +/*
> + * (C) Copyright 2013 Xilinx, Inc.
> + *
> + * Zynq PS Quad-SPI(QSPI) controller driver (master mode only)
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +
> +#include <config.h>
> +#include <common.h>
> +#include <malloc.h>
> +#include <spi.h>
> +#include <asm/io.h>
> +#include <asm/arch/hardware.h>
> +
> +/* zynq spi register bit masks ZYNQ_QSPI_<REG>_<BIT>_MASK */
> +#define ZYNQ_QSPI_CR_IFMODE_MASK	(1 << 31)	/* Flash intrface mode*/
> +#define ZYNQ_QSPI_CR_MSA_MASK		(1 << 15)	/* Manual start enb */
> +#define ZYNQ_QSPI_CR_MCS_MASK		(1 << 14)	/* Manual chip select */
> +#define ZYNQ_QSPI_CR_PCS_MASK		(1 << 10)	/* Peri chip select */
> +#define ZYNQ_QSPI_CR_FW_MASK		(0x3 << 6)	/* FIFO width */
> +#define ZYNQ_QSPI_CR_BRD_MASK		(0x7 << 3)	/* Baud rate div */
> +#define ZYNQ_QSPI_CR_CPHA_MASK		(1 << 2)	/* Clock phase */
> +#define ZYNQ_QSPI_CR_CPOL_MASK		(1 << 1)	/* Clock polarity */
> +#define ZYNQ_QSPI_CR_MSTREN_MASK	(1 << 0)	/* Mode select */
> +#define ZYNQ_QSPI_IXR_RXNEMPTY_MASK	(1 << 4)	/* RX_FIFO_not_empty */
> +#define ZYNQ_QSPI_IXR_TXOW_MASK		(1 << 2)	/* TX_FIFO_not_full */
> +#define ZYNQ_QSPI_IXR_ALL_MASK		0x7F		/* All IXR bits */
> +#define ZYNQ_QSPI_ENR_SPI_EN_MASK	(1 << 0)	/* SPI Enable */
> +
> +/* QSPI Transmit Data Register */
> +#define ZYNQ_QSPI_TXD_00_00_OFFSET	0x1C /* Transmit 4-byte inst */
> +#define ZYNQ_QSPI_TXD_00_01_OFFSET	0x80 /* Transmit 1-byte inst */
> +#define ZYNQ_QSPI_TXD_00_10_OFFSET	0x84 /* Transmit 2-byte inst */
> +#define ZYNQ_QSPI_TXD_00_11_OFFSET	0x88 /* Transmit 3-byte inst */
> +
> +/* Definitions of the flash commands - Flash insts in ascending order */
> +#define ZYNQ_QSPI_FLASH_INST_WRSR	0x01	/* Write status register */
> +#define ZYNQ_QSPI_FLASH_INST_PP		0x02	/* Page program */
> +#define ZYNQ_QSPI_FLASH_INST_WRDS	0x04	/* Write disable */
> +#define ZYNQ_QSPI_FLASH_INST_RDSR1	0x05	/* Read status register 1 */
> +#define ZYNQ_QSPI_FLASH_INST_WREN	0x06	/* Write enable */
> +#define ZYNQ_QSPI_FLASH_INST_AFR	0x0B	/* Fast read data bytes */
> +#define ZYNQ_QSPI_FLASH_INST_BE_4K	0x20	/* Erase 4KiB block */
> +#define ZYNQ_QSPI_FLASH_INST_RDSR2	0x35	/* Read status register 2 */
> +#define ZYNQ_QSPI_FLASH_INST_BE_32K	0x52	/* Erase 32KiB block */
> +#define ZYNQ_QSPI_FLASH_INST_RDID	0x9F	/* Read JEDEC ID */
> +#define ZYNQ_QSPI_FLASH_INST_SE		0xD8	/* Sector erase (usually 64KB)*/
> +
> +#define ZYNQ_QSPI_FIFO_DEPTH		63
> +#ifndef CONFIG_SYS_ZYNQ_QSPI_WAIT
> +#define CONFIG_SYS_ZYNQ_QSPI_WAIT	CONFIG_SYS_HZ/100	/* 10 ms */
> +#endif
> +
> +/* zynq qspi register set */
> +struct zynq_qspi_regs {
> +	u32 cr;		/* 0x00 */
> +	u32 isr;	/* 0x04 */
> +	u32 ier;	/* 0x08 */
> +	u32 idr;	/* 0x0C */
> +	u32 imr;	/* 0x10 */
> +	u32 enr;	/* 0x14 */
> +	u32 dr;		/* 0x18 */
> +	u32 txd0r;	/* 0x1C */
> +	u32 rxdr;	/* 0x20 */
> +	u32 sicr;	/* 0x24 */
> +	u32 txftr;	/* 0x28 */
> +	u32 rxftr;	/* 0x2C */
> +	u32 gpior;	/* 0x30 */
> +	u32 reserved0[19];
> +	u32 txd1r;	/* 0x80 */
> +	u32 txd2r;	/* 0x84 */
> +	u32 txd3r;	/* 0x88 */
> +};
> +
> +/*
> + * struct zynq_qspi_inst_format - Defines qspi flash instruction format
> + * @inst:	Instruction code
> + * @inst_size:	Size of the instruction including address bytes
> + * @inst_off:	Register address where instruction has to be written
> + */
> +struct zynq_qspi_inst_format {
> +	u8 inst;
> +	u8 inst_size;
> +	u8 inst_off;
> +};
> +
> +/* List of all the QSPI instructions and its format */
> +static struct zynq_qspi_inst_format flash_inst[] = {
> +	{ZYNQ_QSPI_FLASH_INST_WRSR, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
> +	{ZYNQ_QSPI_FLASH_INST_PP, 4, ZYNQ_QSPI_TXD_00_00_OFFSET},
> +	{ZYNQ_QSPI_FLASH_INST_WRDS, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
> +	{ZYNQ_QSPI_FLASH_INST_RDSR1, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
> +	{ZYNQ_QSPI_FLASH_INST_WREN, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
> +	{ZYNQ_QSPI_FLASH_INST_AFR, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
> +	{ZYNQ_QSPI_FLASH_INST_BE_4K, 4, ZYNQ_QSPI_TXD_00_00_OFFSET},
> +	{ZYNQ_QSPI_FLASH_INST_RDSR2, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
> +	{ZYNQ_QSPI_FLASH_INST_BE_32K, 4, ZYNQ_QSPI_TXD_00_00_OFFSET},
> +	{ZYNQ_QSPI_FLASH_INST_RDID, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
> +	{ZYNQ_QSPI_FLASH_INST_SE, 4, ZYNQ_QSPI_TXD_00_00_OFFSET},
> +	/* Add all the instructions supported by the flash device */
> +};
> +
> +/* zynq spi slave */
> +struct zynq_qspi_slave {
> +	struct spi_slave slave;
> +	struct zynq_qspi_regs *base;
> +	u8 mode;
> +	u8 is_inst;
> +	u8 fifo_depth;
> +	const void *tx_buf;
> +	void *rx_buf;
> +	u32 tx_len;
> +	u32 rx_len;
> +	u32 speed_hz;
> +	u32 input_hz;
> +	u32 req_hz;
> +};
> +
> +static inline struct zynq_qspi_slave *to_zynq_qspi_slave(
> +		struct spi_slave *slave)
> +{
> +	return container_of(slave, struct zynq_qspi_slave, slave);
> +}
> +
> +static void zynq_qspi_init_hw(struct zynq_qspi_slave *zslave)
> +{
> +	u32 confr;
> +
> +	/* Disable SPI */
> +	writel(~ZYNQ_QSPI_ENR_SPI_EN_MASK, &zslave->base->enr);
> +
> +	/* Disable Interrupts */
> +	writel(ZYNQ_QSPI_IXR_ALL_MASK, &zslave->base->idr);
> +
> +	/* Clear RX FIFO */
> +	while (readl(&zslave->base->isr) &
> +			ZYNQ_QSPI_IXR_RXNEMPTY_MASK)
> +		readl(&zslave->base->rxdr);
> +
> +	/* Clear Interrupts */
> +	writel(ZYNQ_QSPI_IXR_ALL_MASK, &zslave->base->isr);
> +
> +	/* Manual slave select and Auto start */
> +	confr = ZYNQ_QSPI_CR_IFMODE_MASK | ZYNQ_QSPI_CR_MCS_MASK |
> +		ZYNQ_QSPI_CR_PCS_MASK | ZYNQ_QSPI_CR_FW_MASK |
> +		ZYNQ_QSPI_CR_MSTREN_MASK;
> +	confr &= ~ZYNQ_QSPI_CR_MSA_MASK;
> +	confr &= ~ZYNQ_QSPI_CR_MSA_MASK;
Copy/paste error? Why "confr &= ~ZYNQ_QSPI_CR_MSA_MASK;" twice?
> +	writel(confr, &zslave->base->cr);
> +
> +	/* Enable SPI */
> +	writel(ZYNQ_QSPI_ENR_SPI_EN_MASK, &zslave->base->enr);
> +}
> +
> +/*
> + * zynq_qspi_read - Copy data to RX buffer
> + * @zqspi:      Pointer to zynq_qspi_slave
> + * @data:       The 32 bit variable where data is stored
> + * @size:       Number of bytes to be copied from data to RX buffer
> + */
> +static void zynq_qspi_read(struct zynq_qspi_slave *zslave, u32 data, u8 size)
> +{
> +	if (zslave->rx_buf) {
> +		data >>= (4 - size) * 8;
> +		data = le32_to_cpu(data);
> +		memcpy((u8 *)zslave->rx_buf, &data, size);
> +		zslave->rx_buf += size;
> +	}
> +
> +	zslave->rx_len -= size;
> +}
> +
> +/*
> + * zynq_qspi_write - Copy data from TX buffer
> + * @zslave:	Pointer to zynq_qspi_slave
> + * @data:	Pointer to the 32 bit variable where data is to be copied
> + * @size:	Number of bytes to be copied from TX buffer to data
> + */
> +static void zynq_qspi_write(struct zynq_qspi_slave *zslave, u32 *data, u8 size)
> +{
> +	if (zslave->tx_buf) {
> +		switch (size) {
> +		case 1:
> +			*data = *((u8 *)zslave->tx_buf);
> +			zslave->tx_buf += 1;
> +			*data |= 0xFFFFFF00;
> +			break;
> +		case 2:
> +			*data = *((u16 *)zslave->tx_buf);
> +			zslave->tx_buf += 2;
> +			*data |= 0xFFFF0000;
> +			break;
> +		case 3:
> +			*data = *((u16 *)zslave->tx_buf);
> +			zslave->tx_buf += 2;
> +			*data |= (*((u8 *)zslave->tx_buf) << 16);
> +			zslave->tx_buf += 1;
> +			*data |= 0xFF000000;
> +			break;
> +		case 4:
> +			/* Can not assume word aligned buffer */
> +			memcpy(data, zslave->tx_buf, size);
> +			zslave->tx_buf += 4;
> +			break;
> +		default:
> +			/* This will never execute */
> +			break;
> +		}
> +	} else {
> +		*data = 0;
> +	}
> +
> +	zslave->tx_len -= size;
> +}
> +
> +static int zynq_qspi_check_txfifo(struct zynq_qspi_slave *zslave)
> +{
> +	u32 ts, status;
> +
> +	ts = get_timer(0);
> +	status = readl(&zslave->base->isr);
> +	while (!(status & ZYNQ_QSPI_IXR_TXOW_MASK)) {
> +		if (get_timer(ts) > CONFIG_SYS_ZYNQ_QSPI_WAIT) {
> +			printf("spi_xfer: Timeout! TX FIFO not full\n");
> +			return -1;
> +		}
> +		status = readl(&zslave->base->isr);
> +	}
> +
> +	return 0;
> +}
> +
> +static int zynq_qspi_process_tx(struct zynq_qspi_slave *zslave)
> +{
> +	struct zynq_qspi_inst_format *curr_inst;
> +	u8 inst, index;
> +	u32 buf;
> +
> +	inst = *(u8 *)zslave->tx_buf;
> +	/* instuction */
> +	if (inst && zslave->is_inst) {
> +		for (index = 0; index < ARRAY_SIZE(flash_inst); index++)
> +			if (inst == flash_inst[index].inst)
> +				break;
> +
> +		if (index == ARRAY_SIZE(flash_inst)) {
> +			printf("spi_xfer: Unsupported inst %02x\n", inst);
> +			return -1;
> +		}
> +
> +		curr_inst = &flash_inst[index];
> +		debug("spi_xfer: inst:%02x inst_size:%d inst_off:%02x\n",
> +		      curr_inst->inst, curr_inst->inst_size,
> +		      curr_inst->inst_off);
> +
> +		zynq_qspi_write(zslave, &buf, curr_inst->inst_size);
> +		writel(buf, &zslave->base->cr + (curr_inst->inst_off / 4));
> +		zslave->is_inst = 0;
> +	} else if (!zslave->is_inst) { /* addr + data */
> +		if (zslave->tx_len < 4) {
> +			/* Check TXOW for txd1, txd2 and txd3 */
> +			if (zynq_qspi_check_txfifo(zslave) < 0)
> +				return -1;
> +
> +			zynq_qspi_write(zslave, &buf, zslave->tx_len);
> +			writel(buf,
> +			       &zslave->base->txd1r + (zslave->tx_len - 1));
> +		} else {
> +			zynq_qspi_write(zslave, &buf, 4);
> +			writel(buf, &zslave->base->txd0r);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +int spi_cs_is_valid(unsigned int bus, unsigned int cs)
> +{
> +	/* 1 bus with 1 chipselect */
> +	return bus < 1 && cs < 1;
> +}
> +
> +void spi_cs_activate(struct spi_slave *slave)
> +{
> +	struct zynq_qspi_slave *zslave = to_zynq_qspi_slave(slave);
> +
> +	debug("spi_cs_activate: 0x%08x\n", (u32)slave);
> +	clrbits_le32(&zslave->base->cr, ZYNQ_QSPI_CR_PCS_MASK);
> +
> +	zslave->is_inst = 1;
> +}
> +
> +void spi_cs_deactivate(struct spi_slave *slave)
> +{
> +	struct zynq_qspi_slave *zslave = to_zynq_qspi_slave(slave);
> +
> +	debug("spi_cs_deactivate: 0x%08x\n", (u32)slave);
> +	setbits_le32(&zslave->base->cr, ZYNQ_QSPI_CR_PCS_MASK);
> +
> +	zslave->is_inst = 0;
> +}
> +
> +void spi_init()
> +{
> +	/* nothing to do */
> +}
> +
> +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
> +		unsigned int max_hz, unsigned int mode)
> +{
> +	struct zynq_qspi_slave *zslave;
> +
> +	if (!spi_cs_is_valid(bus, cs))
> +		return NULL;
> +
> +	zslave = spi_alloc_slave(struct zynq_qspi_slave, bus, cs);
> +	if (!zslave) {
> +		printf("SPI_error: Fail to allocate zynq_qspi_slave\n");
> +		return NULL;
> +	}
> +
> +	zslave->base = (struct zynq_qspi_regs *)ZYNQ_QSPI_BASEADDR;
> +	zslave->mode = mode;
> +	zslave->fifo_depth = ZYNQ_QSPI_FIFO_DEPTH;
> +	zslave->input_hz = 200000000;
Should 200000000 be a CONFIG define?

Dinh
> +	zslave->speed_hz = zslave->input_hz / 2;
> +	zslave->req_hz = max_hz;
> +
> +	/* init the zynq spi hw */
> +	zynq_qspi_init_hw(zslave);
> +
> +	return &zslave->slave;
> +}
> +
> +void spi_free_slave(struct spi_slave *slave)
> +{
> +	struct zynq_qspi_slave *zslave = to_zynq_qspi_slave(slave);
> +
> +	debug("spi_free_slave: 0x%08x\n", (u32)slave);
> +	free(zslave);
> +}
> +
> +int spi_claim_bus(struct spi_slave *slave)
> +{
> +	struct zynq_qspi_slave *zslave = to_zynq_qspi_slave(slave);
> +	u32 confr = 0;
> +	u8 baud_rate_val = 0;
> +
> +	writel(~ZYNQ_QSPI_ENR_SPI_EN_MASK, &zslave->base->enr);
> +
> +	/* Set the SPI Clock phase and polarities */
> +	confr = readl(&zslave->base->cr);
> +	confr &= ~(ZYNQ_QSPI_CR_CPHA_MASK | ZYNQ_QSPI_CR_CPOL_MASK);
> +	if (zslave->mode & SPI_CPHA)
> +		confr |= ZYNQ_QSPI_CR_CPHA_MASK;
> +	if (zslave->mode & SPI_CPOL)
> +		confr |= ZYNQ_QSPI_CR_CPOL_MASK;
> +
> +	/* Set the clock frequency */
> +	if (zslave->req_hz == 0) {
> +		/* Set baudrate x8, if the req_hz is 0 */
> +		baud_rate_val = 0x2;
> +	} else if (zslave->speed_hz != zslave->req_hz) {
> +		while ((baud_rate_val < 8) &&
> +				((zslave->input_hz /
> +				(2 << baud_rate_val)) > zslave->req_hz))
> +			baud_rate_val++;
> +		zslave->speed_hz = zslave->req_hz / (2 << baud_rate_val);
> +	}
> +	confr &= ~ZYNQ_QSPI_CR_BRD_MASK;
> +	confr |= (baud_rate_val << 3);
> +	writel(confr, &zslave->base->cr);
> +
> +	writel(ZYNQ_QSPI_ENR_SPI_EN_MASK, &zslave->base->enr);
> +
> +	return 0;
> +}
> +
> +void spi_release_bus(struct spi_slave *slave)
> +{
> +	struct zynq_qspi_slave *zslave = to_zynq_qspi_slave(slave);
> +
> +	debug("spi_release_bus: 0x%08x\n", (u32)slave);
> +	writel(~ZYNQ_QSPI_ENR_SPI_EN_MASK, &zslave->base->enr);
> +}
> +
> +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
> +		void *din, unsigned long flags)
> +{
> +	struct zynq_qspi_slave *zslave = to_zynq_qspi_slave(slave);
> +	u32 len = bitlen / 8, tx_tvl;
> +	u32 buf, status;
> +
> +	debug("spi_xfer: bus:%i cs:%i bitlen:%i len:%i flags:%lx\n",
> +	      slave->bus, slave->cs, bitlen, len, flags);
> +
> +	if (bitlen == 0)
> +		return -1;
> +
> +	if (bitlen % 8) {
> +		debug("spi_xfer: Non byte aligned SPI transfer\n");
> +		return -1;
> +	}
> +
> +	if (flags & SPI_XFER_BEGIN)
> +		spi_cs_activate(slave);
> +
> +	zslave->tx_len = len;
> +	zslave->rx_len = len;
> +	zslave->tx_buf = dout;
> +	zslave->rx_buf = din;
> +	while (zslave->rx_len > 0) {
> +		/* Write the data into TX FIFO - tx threshold is fifo_depth */
> +		tx_tvl = 0;
> +		while ((tx_tvl < zslave->fifo_depth) && zslave->tx_len) {
> +			if (zynq_qspi_process_tx(zslave) < 0) {
> +				flags |= SPI_XFER_END;
> +				goto out;
> +			}
> +			tx_tvl++;
> +		}
> +
> +		/* Check TX FIFO completion */
> +		if (zynq_qspi_check_txfifo(zslave) < 0) {
> +			flags |= SPI_XFER_END;
> +			goto out;
> +		}
> +
> +		/* Read the data from RX FIFO */
> +		status = readl(&zslave->base->isr);
> +		while (status & ZYNQ_QSPI_IXR_RXNEMPTY_MASK) {
> +			buf = readl(&zslave->base->rxdr);
> +			if (zslave->rx_len < 4)
> +				zynq_qspi_read(zslave, buf, zslave->rx_len);
> +			else
> +				zynq_qspi_read(zslave, buf, 4);
> +			status = readl(&zslave->base->isr);
> +		}
> +	}
> +
> +out:
> +	if (flags & SPI_XFER_END)
> +		spi_cs_deactivate(slave);
> +
> +	return 0;
> +}
Jagannadha Sutradharudu Teki Nov. 6, 2013, 6:11 a.m. UTC | #4
Hi Thomas,

On Wednesday 06 November 2013 01:29 AM, thomas.langer@lantiq.com wrote:
> Hello Jagan,
>
> I have some comments and questions:
>
> Am 05.11.2013 18:51, schrieb Jagannadha Sutradharudu Teki:
>> +
>> +/* Definitions of the flash commands - Flash insts in ascending order */
>> +#define ZYNQ_QSPI_FLASH_INST_WRSR	0x01	/* Write status register */
>> +#define ZYNQ_QSPI_FLASH_INST_PP		0x02	/* Page program */
>> +#define ZYNQ_QSPI_FLASH_INST_WRDS	0x04	/* Write disable */
>> +#define ZYNQ_QSPI_FLASH_INST_RDSR1	0x05	/* Read status register 1 */
>> +#define ZYNQ_QSPI_FLASH_INST_WREN	0x06	/* Write enable */
>> +#define ZYNQ_QSPI_FLASH_INST_AFR	0x0B	/* Fast read data bytes */
>> +#define ZYNQ_QSPI_FLASH_INST_BE_4K	0x20	/* Erase 4KiB block */
>> +#define ZYNQ_QSPI_FLASH_INST_RDSR2	0x35	/* Read status register 2 */
>> +#define ZYNQ_QSPI_FLASH_INST_BE_32K	0x52	/* Erase 32KiB block */
>> +#define ZYNQ_QSPI_FLASH_INST_RDID	0x9F	/* Read JEDEC ID */
>> +#define ZYNQ_QSPI_FLASH_INST_SE		0xD8	/* Sector erase (usually 64KB)*/
>> +
> Why needs the spi controller this list of flash commands?
> It is the job of the flash driver to handle this, the spi controller
> only forwards this to the devices!
>
>> +
>> +/* List of all the QSPI instructions and its format */
>> +static struct zynq_qspi_inst_format flash_inst[] = {
>> +	{ZYNQ_QSPI_FLASH_INST_WRSR, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
>> +	{ZYNQ_QSPI_FLASH_INST_PP, 4, ZYNQ_QSPI_TXD_00_00_OFFSET},
>> +	{ZYNQ_QSPI_FLASH_INST_WRDS, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
>> +	{ZYNQ_QSPI_FLASH_INST_RDSR1, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
>> +	{ZYNQ_QSPI_FLASH_INST_WREN, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
>> +	{ZYNQ_QSPI_FLASH_INST_AFR, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
>> +	{ZYNQ_QSPI_FLASH_INST_BE_4K, 4, ZYNQ_QSPI_TXD_00_00_OFFSET},
>> +	{ZYNQ_QSPI_FLASH_INST_RDSR2, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
>> +	{ZYNQ_QSPI_FLASH_INST_BE_32K, 4, ZYNQ_QSPI_TXD_00_00_OFFSET},
>> +	{ZYNQ_QSPI_FLASH_INST_RDID, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
>> +	{ZYNQ_QSPI_FLASH_INST_SE, 4, ZYNQ_QSPI_TXD_00_00_OFFSET},
>> +	/* Add all the instructions supported by the flash device */
>> +};
> Is this table used to encode which parts needs DUAL or QUAD transfers?
> And are you sure the table is complete? The flash drivers don't use more
> instructions?
> What happens if some device is connected, which is no flash?
>> +
>> +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
>> +		void *din, unsigned long flags)
>> +{
>> +	struct zynq_qspi_slave *zslave = to_zynq_qspi_slave(slave);
>> +	u32 len = bitlen / 8, tx_tvl;
>> +	u32 buf, status;
>> +
>> +	debug("spi_xfer: bus:%i cs:%i bitlen:%i len:%i flags:%lx\n",
>> +	      slave->bus, slave->cs, bitlen, len, flags);
>> +
>> +	if (bitlen == 0)
>> +		return -1;
>> +
>> +	if (bitlen % 8) {
>> +		debug("spi_xfer: Non byte aligned SPI transfer\n");
>> +		return -1;
>> +	}
>> +
>> +	if (flags & SPI_XFER_BEGIN)
>> +		spi_cs_activate(slave);
>> +
>> +	zslave->tx_len = len;
>> +	zslave->rx_len = len;
>> +	zslave->tx_buf = dout;
>> +	zslave->rx_buf = din;
>> +	while (zslave->rx_len > 0) {
>> +		/* Write the data into TX FIFO - tx threshold is fifo_depth */
>> +		tx_tvl = 0;
>> +		while ((tx_tvl < zslave->fifo_depth) && zslave->tx_len) {
>> +			if (zynq_qspi_process_tx(zslave) < 0) {
>> +				flags |= SPI_XFER_END;
>> +				goto out;
>> +			}
>> +			tx_tvl++;
>> +		}
>> +
>> +		/* Check TX FIFO completion */
>> +		if (zynq_qspi_check_txfifo(zslave) < 0) {
>> +			flags |= SPI_XFER_END;
>> +			goto out;
>> +		}
>> +
>> +		/* Read the data from RX FIFO */
>> +		status = readl(&zslave->base->isr);
>> +		while (status & ZYNQ_QSPI_IXR_RXNEMPTY_MASK) {
>> +			buf = readl(&zslave->base->rxdr);
>> +			if (zslave->rx_len < 4)
>> +				zynq_qspi_read(zslave, buf, zslave->rx_len);
>> +			else
>> +				zynq_qspi_read(zslave, buf, 4);
>> +			status = readl(&zslave->base->isr);
>> +		}
>> +	}
>> +
>> +out:
>> +	if (flags & SPI_XFER_END)
>> +		spi_cs_deactivate(slave);
>> +
>> +	return 0;
>> +}
> In this function I miss the parts, where the caller (e.g. the flash
> driver) tells the controller to transfer some parts in DUAL or QUAD mode!

Yes- I sent this as part of zynq support series.
I will fix the details where controller won't need to aware of flash 
specific data in coming versions.
Jagannadha Sutradharudu Teki Nov. 6, 2013, 6:18 a.m. UTC | #5
On Wednesday 06 November 2013 10:32 AM, Dinh Nguyen wrote:
>
> On 11/5/13 11:46 AM, Jagannadha Sutradharudu Teki wrote:
>> Zynq qspi controller driver supports single bus
>> with singe chipselect.
>>
>> Zynq qspi can be operated in below connection modes
>> - single qspi
>> - dual qspi, with dual stacked
>> - dual qspi, with dual parallel
>>
>> Signed-off-by: Jagannadha Sutradharudu Teki <jaganna@xilinx.com>
>> ---
>>   arch/arm/include/asm/arch-zynq/hardware.h |   1 +
>>   drivers/spi/Makefile                      |   1 +
>>   drivers/spi/zynq_qspi.c                   | 447 ++++++++++++++++++++++++++++++
>>   3 files changed, 449 insertions(+)
>>   create mode 100644 drivers/spi/zynq_qspi.c
>>
>> diff --git a/arch/arm/include/asm/arch-zynq/hardware.h b/arch/arm/include/asm/arch-zynq/hardware.h
>> index cd69677..05870ae 100644
>> --- a/arch/arm/include/asm/arch-zynq/hardware.h
>> +++ b/arch/arm/include/asm/arch-zynq/hardware.h
>> @@ -19,6 +19,7 @@
>>   #define ZYNQ_I2C_BASEADDR1		0xE0005000
>>   #define ZYNQ_SPI_BASEADDR0		0xE0006000
>>   #define ZYNQ_SPI_BASEADDR1		0xE0007000
>> +#define ZYNQ_QSPI_BASEADDR		0xE000D000
>>   #define ZYNQ_DDRC_BASEADDR		0xF8006000
>>
>>   /* Reflect slcr offsets */
>> diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
>> index 27902fe..5fafee0 100644
>> --- a/drivers/spi/Makefile
>> +++ b/drivers/spi/Makefile
>> @@ -37,3 +37,4 @@ obj-$(CONFIG_TEGRA114_SPI) += tegra114_spi.o
>>   obj-$(CONFIG_TI_QSPI) += ti_qspi.o
>>   obj-$(CONFIG_XILINX_SPI) += xilinx_spi.o
>>   obj-$(CONFIG_ZYNQ_SPI) += zynq_spi.o
>> +obj-$(CONFIG_ZYNQ_QSPI) += zynq_qspi.o
>> diff --git a/drivers/spi/zynq_qspi.c b/drivers/spi/zynq_qspi.c
>> new file mode 100644
>> index 0000000..f38ebca
>> --- /dev/null
>> +++ b/drivers/spi/zynq_qspi.c
>> @@ -0,0 +1,447 @@
>> +/*
>> + * (C) Copyright 2013 Xilinx, Inc.
>> + *
>> + * Zynq PS Quad-SPI(QSPI) controller driver (master mode only)
>> + *
>> + * SPDX-License-Identifier:	GPL-2.0+
>> + */
>> +
>> +#include <config.h>
>> +#include <common.h>
>> +#include <malloc.h>
>> +#include <spi.h>
>> +#include <asm/io.h>
>> +#include <asm/arch/hardware.h>
>> +
>> +/* zynq spi register bit masks ZYNQ_QSPI_<REG>_<BIT>_MASK */
>> +#define ZYNQ_QSPI_CR_IFMODE_MASK	(1 << 31)	/* Flash intrface mode*/
>> +#define ZYNQ_QSPI_CR_MSA_MASK		(1 << 15)	/* Manual start enb */
>> +#define ZYNQ_QSPI_CR_MCS_MASK		(1 << 14)	/* Manual chip select */
>> +#define ZYNQ_QSPI_CR_PCS_MASK		(1 << 10)	/* Peri chip select */
>> +#define ZYNQ_QSPI_CR_FW_MASK		(0x3 << 6)	/* FIFO width */
>> +#define ZYNQ_QSPI_CR_BRD_MASK		(0x7 << 3)	/* Baud rate div */
>> +#define ZYNQ_QSPI_CR_CPHA_MASK		(1 << 2)	/* Clock phase */
>> +#define ZYNQ_QSPI_CR_CPOL_MASK		(1 << 1)	/* Clock polarity */
>> +#define ZYNQ_QSPI_CR_MSTREN_MASK	(1 << 0)	/* Mode select */
>> +#define ZYNQ_QSPI_IXR_RXNEMPTY_MASK	(1 << 4)	/* RX_FIFO_not_empty */
>> +#define ZYNQ_QSPI_IXR_TXOW_MASK		(1 << 2)	/* TX_FIFO_not_full */
>> +#define ZYNQ_QSPI_IXR_ALL_MASK		0x7F		/* All IXR bits */
>> +#define ZYNQ_QSPI_ENR_SPI_EN_MASK	(1 << 0)	/* SPI Enable */
>> +
>> +/* QSPI Transmit Data Register */
>> +#define ZYNQ_QSPI_TXD_00_00_OFFSET	0x1C /* Transmit 4-byte inst */
>> +#define ZYNQ_QSPI_TXD_00_01_OFFSET	0x80 /* Transmit 1-byte inst */
>> +#define ZYNQ_QSPI_TXD_00_10_OFFSET	0x84 /* Transmit 2-byte inst */
>> +#define ZYNQ_QSPI_TXD_00_11_OFFSET	0x88 /* Transmit 3-byte inst */
>> +
>> +/* Definitions of the flash commands - Flash insts in ascending order */
>> +#define ZYNQ_QSPI_FLASH_INST_WRSR	0x01	/* Write status register */
>> +#define ZYNQ_QSPI_FLASH_INST_PP		0x02	/* Page program */
>> +#define ZYNQ_QSPI_FLASH_INST_WRDS	0x04	/* Write disable */
>> +#define ZYNQ_QSPI_FLASH_INST_RDSR1	0x05	/* Read status register 1 */
>> +#define ZYNQ_QSPI_FLASH_INST_WREN	0x06	/* Write enable */
>> +#define ZYNQ_QSPI_FLASH_INST_AFR	0x0B	/* Fast read data bytes */
>> +#define ZYNQ_QSPI_FLASH_INST_BE_4K	0x20	/* Erase 4KiB block */
>> +#define ZYNQ_QSPI_FLASH_INST_RDSR2	0x35	/* Read status register 2 */
>> +#define ZYNQ_QSPI_FLASH_INST_BE_32K	0x52	/* Erase 32KiB block */
>> +#define ZYNQ_QSPI_FLASH_INST_RDID	0x9F	/* Read JEDEC ID */
>> +#define ZYNQ_QSPI_FLASH_INST_SE		0xD8	/* Sector erase (usually 64KB)*/
>> +
>> +#define ZYNQ_QSPI_FIFO_DEPTH		63
>> +#ifndef CONFIG_SYS_ZYNQ_QSPI_WAIT
>> +#define CONFIG_SYS_ZYNQ_QSPI_WAIT	CONFIG_SYS_HZ/100	/* 10 ms */
>> +#endif
>> +
>> +/* zynq qspi register set */
>> +struct zynq_qspi_regs {
>> +	u32 cr;		/* 0x00 */
>> +	u32 isr;	/* 0x04 */
>> +	u32 ier;	/* 0x08 */
>> +	u32 idr;	/* 0x0C */
>> +	u32 imr;	/* 0x10 */
>> +	u32 enr;	/* 0x14 */
>> +	u32 dr;		/* 0x18 */
>> +	u32 txd0r;	/* 0x1C */
>> +	u32 rxdr;	/* 0x20 */
>> +	u32 sicr;	/* 0x24 */
>> +	u32 txftr;	/* 0x28 */
>> +	u32 rxftr;	/* 0x2C */
>> +	u32 gpior;	/* 0x30 */
>> +	u32 reserved0[19];
>> +	u32 txd1r;	/* 0x80 */
>> +	u32 txd2r;	/* 0x84 */
>> +	u32 txd3r;	/* 0x88 */
>> +};
>> +
>> +/*
>> + * struct zynq_qspi_inst_format - Defines qspi flash instruction format
>> + * @inst:	Instruction code
>> + * @inst_size:	Size of the instruction including address bytes
>> + * @inst_off:	Register address where instruction has to be written
>> + */
>> +struct zynq_qspi_inst_format {
>> +	u8 inst;
>> +	u8 inst_size;
>> +	u8 inst_off;
>> +};
>> +
>> +/* List of all the QSPI instructions and its format */
>> +static struct zynq_qspi_inst_format flash_inst[] = {
>> +	{ZYNQ_QSPI_FLASH_INST_WRSR, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
>> +	{ZYNQ_QSPI_FLASH_INST_PP, 4, ZYNQ_QSPI_TXD_00_00_OFFSET},
>> +	{ZYNQ_QSPI_FLASH_INST_WRDS, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
>> +	{ZYNQ_QSPI_FLASH_INST_RDSR1, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
>> +	{ZYNQ_QSPI_FLASH_INST_WREN, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
>> +	{ZYNQ_QSPI_FLASH_INST_AFR, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
>> +	{ZYNQ_QSPI_FLASH_INST_BE_4K, 4, ZYNQ_QSPI_TXD_00_00_OFFSET},
>> +	{ZYNQ_QSPI_FLASH_INST_RDSR2, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
>> +	{ZYNQ_QSPI_FLASH_INST_BE_32K, 4, ZYNQ_QSPI_TXD_00_00_OFFSET},
>> +	{ZYNQ_QSPI_FLASH_INST_RDID, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
>> +	{ZYNQ_QSPI_FLASH_INST_SE, 4, ZYNQ_QSPI_TXD_00_00_OFFSET},
>> +	/* Add all the instructions supported by the flash device */
>> +};
>> +
>> +/* zynq spi slave */
>> +struct zynq_qspi_slave {
>> +	struct spi_slave slave;
>> +	struct zynq_qspi_regs *base;
>> +	u8 mode;
>> +	u8 is_inst;
>> +	u8 fifo_depth;
>> +	const void *tx_buf;
>> +	void *rx_buf;
>> +	u32 tx_len;
>> +	u32 rx_len;
>> +	u32 speed_hz;
>> +	u32 input_hz;
>> +	u32 req_hz;
>> +};
>> +
>> +static inline struct zynq_qspi_slave *to_zynq_qspi_slave(
>> +		struct spi_slave *slave)
>> +{
>> +	return container_of(slave, struct zynq_qspi_slave, slave);
>> +}
>> +
>> +static void zynq_qspi_init_hw(struct zynq_qspi_slave *zslave)
>> +{
>> +	u32 confr;
>> +
>> +	/* Disable SPI */
>> +	writel(~ZYNQ_QSPI_ENR_SPI_EN_MASK, &zslave->base->enr);
>> +
>> +	/* Disable Interrupts */
>> +	writel(ZYNQ_QSPI_IXR_ALL_MASK, &zslave->base->idr);
>> +
>> +	/* Clear RX FIFO */
>> +	while (readl(&zslave->base->isr) &
>> +			ZYNQ_QSPI_IXR_RXNEMPTY_MASK)
>> +		readl(&zslave->base->rxdr);
>> +
>> +	/* Clear Interrupts */
>> +	writel(ZYNQ_QSPI_IXR_ALL_MASK, &zslave->base->isr);
>> +
>> +	/* Manual slave select and Auto start */
>> +	confr = ZYNQ_QSPI_CR_IFMODE_MASK | ZYNQ_QSPI_CR_MCS_MASK |
>> +		ZYNQ_QSPI_CR_PCS_MASK | ZYNQ_QSPI_CR_FW_MASK |
>> +		ZYNQ_QSPI_CR_MSTREN_MASK;
>> +	confr &= ~ZYNQ_QSPI_CR_MSA_MASK;
>> +	confr &= ~ZYNQ_QSPI_CR_MSA_MASK;
> Copy/paste error? Why "confr &= ~ZYNQ_QSPI_CR_MSA_MASK;" twice?

Will fix in coming series.

>> +	writel(confr, &zslave->base->cr);
>> +
>> +	/* Enable SPI */
>> +	writel(ZYNQ_QSPI_ENR_SPI_EN_MASK, &zslave->base->enr);
>> +}
>> +
>> +/*
>> + * zynq_qspi_read - Copy data to RX buffer
>> + * @zqspi:      Pointer to zynq_qspi_slave
>> + * @data:       The 32 bit variable where data is stored
>> + * @size:       Number of bytes to be copied from data to RX buffer
>> + */
>> +static void zynq_qspi_read(struct zynq_qspi_slave *zslave, u32 data, u8 size)
>> +{
>> +	if (zslave->rx_buf) {
>> +		data >>= (4 - size) * 8;
>> +		data = le32_to_cpu(data);
>> +		memcpy((u8 *)zslave->rx_buf, &data, size);
>> +		zslave->rx_buf += size;
>> +	}
>> +
>> +	zslave->rx_len -= size;
>> +}
>> +
>> +/*
>> + * zynq_qspi_write - Copy data from TX buffer
>> + * @zslave:	Pointer to zynq_qspi_slave
>> + * @data:	Pointer to the 32 bit variable where data is to be copied
>> + * @size:	Number of bytes to be copied from TX buffer to data
>> + */
>> +static void zynq_qspi_write(struct zynq_qspi_slave *zslave, u32 *data, u8 size)
>> +{
>> +	if (zslave->tx_buf) {
>> +		switch (size) {
>> +		case 1:
>> +			*data = *((u8 *)zslave->tx_buf);
>> +			zslave->tx_buf += 1;
>> +			*data |= 0xFFFFFF00;
>> +			break;
>> +		case 2:
>> +			*data = *((u16 *)zslave->tx_buf);
>> +			zslave->tx_buf += 2;
>> +			*data |= 0xFFFF0000;
>> +			break;
>> +		case 3:
>> +			*data = *((u16 *)zslave->tx_buf);
>> +			zslave->tx_buf += 2;
>> +			*data |= (*((u8 *)zslave->tx_buf) << 16);
>> +			zslave->tx_buf += 1;
>> +			*data |= 0xFF000000;
>> +			break;
>> +		case 4:
>> +			/* Can not assume word aligned buffer */
>> +			memcpy(data, zslave->tx_buf, size);
>> +			zslave->tx_buf += 4;
>> +			break;
>> +		default:
>> +			/* This will never execute */
>> +			break;
>> +		}
>> +	} else {
>> +		*data = 0;
>> +	}
>> +
>> +	zslave->tx_len -= size;
>> +}
>> +
>> +static int zynq_qspi_check_txfifo(struct zynq_qspi_slave *zslave)
>> +{
>> +	u32 ts, status;
>> +
>> +	ts = get_timer(0);
>> +	status = readl(&zslave->base->isr);
>> +	while (!(status & ZYNQ_QSPI_IXR_TXOW_MASK)) {
>> +		if (get_timer(ts) > CONFIG_SYS_ZYNQ_QSPI_WAIT) {
>> +			printf("spi_xfer: Timeout! TX FIFO not full\n");
>> +			return -1;
>> +		}
>> +		status = readl(&zslave->base->isr);
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int zynq_qspi_process_tx(struct zynq_qspi_slave *zslave)
>> +{
>> +	struct zynq_qspi_inst_format *curr_inst;
>> +	u8 inst, index;
>> +	u32 buf;
>> +
>> +	inst = *(u8 *)zslave->tx_buf;
>> +	/* instuction */
>> +	if (inst && zslave->is_inst) {
>> +		for (index = 0; index < ARRAY_SIZE(flash_inst); index++)
>> +			if (inst == flash_inst[index].inst)
>> +				break;
>> +
>> +		if (index == ARRAY_SIZE(flash_inst)) {
>> +			printf("spi_xfer: Unsupported inst %02x\n", inst);
>> +			return -1;
>> +		}
>> +
>> +		curr_inst = &flash_inst[index];
>> +		debug("spi_xfer: inst:%02x inst_size:%d inst_off:%02x\n",
>> +		      curr_inst->inst, curr_inst->inst_size,
>> +		      curr_inst->inst_off);
>> +
>> +		zynq_qspi_write(zslave, &buf, curr_inst->inst_size);
>> +		writel(buf, &zslave->base->cr + (curr_inst->inst_off / 4));
>> +		zslave->is_inst = 0;
>> +	} else if (!zslave->is_inst) { /* addr + data */
>> +		if (zslave->tx_len < 4) {
>> +			/* Check TXOW for txd1, txd2 and txd3 */
>> +			if (zynq_qspi_check_txfifo(zslave) < 0)
>> +				return -1;
>> +
>> +			zynq_qspi_write(zslave, &buf, zslave->tx_len);
>> +			writel(buf,
>> +			       &zslave->base->txd1r + (zslave->tx_len - 1));
>> +		} else {
>> +			zynq_qspi_write(zslave, &buf, 4);
>> +			writel(buf, &zslave->base->txd0r);
>> +		}
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +int spi_cs_is_valid(unsigned int bus, unsigned int cs)
>> +{
>> +	/* 1 bus with 1 chipselect */
>> +	return bus < 1 && cs < 1;
>> +}
>> +
>> +void spi_cs_activate(struct spi_slave *slave)
>> +{
>> +	struct zynq_qspi_slave *zslave = to_zynq_qspi_slave(slave);
>> +
>> +	debug("spi_cs_activate: 0x%08x\n", (u32)slave);
>> +	clrbits_le32(&zslave->base->cr, ZYNQ_QSPI_CR_PCS_MASK);
>> +
>> +	zslave->is_inst = 1;
>> +}
>> +
>> +void spi_cs_deactivate(struct spi_slave *slave)
>> +{
>> +	struct zynq_qspi_slave *zslave = to_zynq_qspi_slave(slave);
>> +
>> +	debug("spi_cs_deactivate: 0x%08x\n", (u32)slave);
>> +	setbits_le32(&zslave->base->cr, ZYNQ_QSPI_CR_PCS_MASK);
>> +
>> +	zslave->is_inst = 0;
>> +}
>> +
>> +void spi_init()
>> +{
>> +	/* nothing to do */
>> +}
>> +
>> +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
>> +		unsigned int max_hz, unsigned int mode)
>> +{
>> +	struct zynq_qspi_slave *zslave;
>> +
>> +	if (!spi_cs_is_valid(bus, cs))
>> +		return NULL;
>> +
>> +	zslave = spi_alloc_slave(struct zynq_qspi_slave, bus, cs);
>> +	if (!zslave) {
>> +		printf("SPI_error: Fail to allocate zynq_qspi_slave\n");
>> +		return NULL;
>> +	}
>> +
>> +	zslave->base = (struct zynq_qspi_regs *)ZYNQ_QSPI_BASEADDR;
>> +	zslave->mode = mode;
>> +	zslave->fifo_depth = ZYNQ_QSPI_FIFO_DEPTH;
>> +	zslave->input_hz = 200000000;
> Should 200000000 be a CONFIG define?
Yes - I will mark this constant with macro.
diff mbox

Patch

diff --git a/arch/arm/include/asm/arch-zynq/hardware.h b/arch/arm/include/asm/arch-zynq/hardware.h
index cd69677..05870ae 100644
--- a/arch/arm/include/asm/arch-zynq/hardware.h
+++ b/arch/arm/include/asm/arch-zynq/hardware.h
@@ -19,6 +19,7 @@ 
 #define ZYNQ_I2C_BASEADDR1		0xE0005000
 #define ZYNQ_SPI_BASEADDR0		0xE0006000
 #define ZYNQ_SPI_BASEADDR1		0xE0007000
+#define ZYNQ_QSPI_BASEADDR		0xE000D000
 #define ZYNQ_DDRC_BASEADDR		0xF8006000
 
 /* Reflect slcr offsets */
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 27902fe..5fafee0 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -37,3 +37,4 @@  obj-$(CONFIG_TEGRA114_SPI) += tegra114_spi.o
 obj-$(CONFIG_TI_QSPI) += ti_qspi.o
 obj-$(CONFIG_XILINX_SPI) += xilinx_spi.o
 obj-$(CONFIG_ZYNQ_SPI) += zynq_spi.o
+obj-$(CONFIG_ZYNQ_QSPI) += zynq_qspi.o
diff --git a/drivers/spi/zynq_qspi.c b/drivers/spi/zynq_qspi.c
new file mode 100644
index 0000000..f38ebca
--- /dev/null
+++ b/drivers/spi/zynq_qspi.c
@@ -0,0 +1,447 @@ 
+/*
+ * (C) Copyright 2013 Xilinx, Inc.
+ *
+ * Zynq PS Quad-SPI(QSPI) controller driver (master mode only)
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <config.h>
+#include <common.h>
+#include <malloc.h>
+#include <spi.h>
+#include <asm/io.h>
+#include <asm/arch/hardware.h>
+
+/* zynq spi register bit masks ZYNQ_QSPI_<REG>_<BIT>_MASK */
+#define ZYNQ_QSPI_CR_IFMODE_MASK	(1 << 31)	/* Flash intrface mode*/
+#define ZYNQ_QSPI_CR_MSA_MASK		(1 << 15)	/* Manual start enb */
+#define ZYNQ_QSPI_CR_MCS_MASK		(1 << 14)	/* Manual chip select */
+#define ZYNQ_QSPI_CR_PCS_MASK		(1 << 10)	/* Peri chip select */
+#define ZYNQ_QSPI_CR_FW_MASK		(0x3 << 6)	/* FIFO width */
+#define ZYNQ_QSPI_CR_BRD_MASK		(0x7 << 3)	/* Baud rate div */
+#define ZYNQ_QSPI_CR_CPHA_MASK		(1 << 2)	/* Clock phase */
+#define ZYNQ_QSPI_CR_CPOL_MASK		(1 << 1)	/* Clock polarity */
+#define ZYNQ_QSPI_CR_MSTREN_MASK	(1 << 0)	/* Mode select */
+#define ZYNQ_QSPI_IXR_RXNEMPTY_MASK	(1 << 4)	/* RX_FIFO_not_empty */
+#define ZYNQ_QSPI_IXR_TXOW_MASK		(1 << 2)	/* TX_FIFO_not_full */
+#define ZYNQ_QSPI_IXR_ALL_MASK		0x7F		/* All IXR bits */
+#define ZYNQ_QSPI_ENR_SPI_EN_MASK	(1 << 0)	/* SPI Enable */
+
+/* QSPI Transmit Data Register */
+#define ZYNQ_QSPI_TXD_00_00_OFFSET	0x1C /* Transmit 4-byte inst */
+#define ZYNQ_QSPI_TXD_00_01_OFFSET	0x80 /* Transmit 1-byte inst */
+#define ZYNQ_QSPI_TXD_00_10_OFFSET	0x84 /* Transmit 2-byte inst */
+#define ZYNQ_QSPI_TXD_00_11_OFFSET	0x88 /* Transmit 3-byte inst */
+
+/* Definitions of the flash commands - Flash insts in ascending order */
+#define ZYNQ_QSPI_FLASH_INST_WRSR	0x01	/* Write status register */
+#define ZYNQ_QSPI_FLASH_INST_PP		0x02	/* Page program */
+#define ZYNQ_QSPI_FLASH_INST_WRDS	0x04	/* Write disable */
+#define ZYNQ_QSPI_FLASH_INST_RDSR1	0x05	/* Read status register 1 */
+#define ZYNQ_QSPI_FLASH_INST_WREN	0x06	/* Write enable */
+#define ZYNQ_QSPI_FLASH_INST_AFR	0x0B	/* Fast read data bytes */
+#define ZYNQ_QSPI_FLASH_INST_BE_4K	0x20	/* Erase 4KiB block */
+#define ZYNQ_QSPI_FLASH_INST_RDSR2	0x35	/* Read status register 2 */
+#define ZYNQ_QSPI_FLASH_INST_BE_32K	0x52	/* Erase 32KiB block */
+#define ZYNQ_QSPI_FLASH_INST_RDID	0x9F	/* Read JEDEC ID */
+#define ZYNQ_QSPI_FLASH_INST_SE		0xD8	/* Sector erase (usually 64KB)*/
+
+#define ZYNQ_QSPI_FIFO_DEPTH		63
+#ifndef CONFIG_SYS_ZYNQ_QSPI_WAIT
+#define CONFIG_SYS_ZYNQ_QSPI_WAIT	CONFIG_SYS_HZ/100	/* 10 ms */
+#endif
+
+/* zynq qspi register set */
+struct zynq_qspi_regs {
+	u32 cr;		/* 0x00 */
+	u32 isr;	/* 0x04 */
+	u32 ier;	/* 0x08 */
+	u32 idr;	/* 0x0C */
+	u32 imr;	/* 0x10 */
+	u32 enr;	/* 0x14 */
+	u32 dr;		/* 0x18 */
+	u32 txd0r;	/* 0x1C */
+	u32 rxdr;	/* 0x20 */
+	u32 sicr;	/* 0x24 */
+	u32 txftr;	/* 0x28 */
+	u32 rxftr;	/* 0x2C */
+	u32 gpior;	/* 0x30 */
+	u32 reserved0[19];
+	u32 txd1r;	/* 0x80 */
+	u32 txd2r;	/* 0x84 */
+	u32 txd3r;	/* 0x88 */
+};
+
+/*
+ * struct zynq_qspi_inst_format - Defines qspi flash instruction format
+ * @inst:	Instruction code
+ * @inst_size:	Size of the instruction including address bytes
+ * @inst_off:	Register address where instruction has to be written
+ */
+struct zynq_qspi_inst_format {
+	u8 inst;
+	u8 inst_size;
+	u8 inst_off;
+};
+
+/* List of all the QSPI instructions and its format */
+static struct zynq_qspi_inst_format flash_inst[] = {
+	{ZYNQ_QSPI_FLASH_INST_WRSR, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
+	{ZYNQ_QSPI_FLASH_INST_PP, 4, ZYNQ_QSPI_TXD_00_00_OFFSET},
+	{ZYNQ_QSPI_FLASH_INST_WRDS, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
+	{ZYNQ_QSPI_FLASH_INST_RDSR1, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
+	{ZYNQ_QSPI_FLASH_INST_WREN, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
+	{ZYNQ_QSPI_FLASH_INST_AFR, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
+	{ZYNQ_QSPI_FLASH_INST_BE_4K, 4, ZYNQ_QSPI_TXD_00_00_OFFSET},
+	{ZYNQ_QSPI_FLASH_INST_RDSR2, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
+	{ZYNQ_QSPI_FLASH_INST_BE_32K, 4, ZYNQ_QSPI_TXD_00_00_OFFSET},
+	{ZYNQ_QSPI_FLASH_INST_RDID, 1, ZYNQ_QSPI_TXD_00_01_OFFSET},
+	{ZYNQ_QSPI_FLASH_INST_SE, 4, ZYNQ_QSPI_TXD_00_00_OFFSET},
+	/* Add all the instructions supported by the flash device */
+};
+
+/* zynq spi slave */
+struct zynq_qspi_slave {
+	struct spi_slave slave;
+	struct zynq_qspi_regs *base;
+	u8 mode;
+	u8 is_inst;
+	u8 fifo_depth;
+	const void *tx_buf;
+	void *rx_buf;
+	u32 tx_len;
+	u32 rx_len;
+	u32 speed_hz;
+	u32 input_hz;
+	u32 req_hz;
+};
+
+static inline struct zynq_qspi_slave *to_zynq_qspi_slave(
+		struct spi_slave *slave)
+{
+	return container_of(slave, struct zynq_qspi_slave, slave);
+}
+
+static void zynq_qspi_init_hw(struct zynq_qspi_slave *zslave)
+{
+	u32 confr;
+
+	/* Disable SPI */
+	writel(~ZYNQ_QSPI_ENR_SPI_EN_MASK, &zslave->base->enr);
+
+	/* Disable Interrupts */
+	writel(ZYNQ_QSPI_IXR_ALL_MASK, &zslave->base->idr);
+
+	/* Clear RX FIFO */
+	while (readl(&zslave->base->isr) &
+			ZYNQ_QSPI_IXR_RXNEMPTY_MASK)
+		readl(&zslave->base->rxdr);
+
+	/* Clear Interrupts */
+	writel(ZYNQ_QSPI_IXR_ALL_MASK, &zslave->base->isr);
+
+	/* Manual slave select and Auto start */
+	confr = ZYNQ_QSPI_CR_IFMODE_MASK | ZYNQ_QSPI_CR_MCS_MASK |
+		ZYNQ_QSPI_CR_PCS_MASK | ZYNQ_QSPI_CR_FW_MASK |
+		ZYNQ_QSPI_CR_MSTREN_MASK;
+	confr &= ~ZYNQ_QSPI_CR_MSA_MASK;
+	confr &= ~ZYNQ_QSPI_CR_MSA_MASK;
+	writel(confr, &zslave->base->cr);
+
+	/* Enable SPI */
+	writel(ZYNQ_QSPI_ENR_SPI_EN_MASK, &zslave->base->enr);
+}
+
+/*
+ * zynq_qspi_read - Copy data to RX buffer
+ * @zqspi:      Pointer to zynq_qspi_slave
+ * @data:       The 32 bit variable where data is stored
+ * @size:       Number of bytes to be copied from data to RX buffer
+ */
+static void zynq_qspi_read(struct zynq_qspi_slave *zslave, u32 data, u8 size)
+{
+	if (zslave->rx_buf) {
+		data >>= (4 - size) * 8;
+		data = le32_to_cpu(data);
+		memcpy((u8 *)zslave->rx_buf, &data, size);
+		zslave->rx_buf += size;
+	}
+
+	zslave->rx_len -= size;
+}
+
+/*
+ * zynq_qspi_write - Copy data from TX buffer
+ * @zslave:	Pointer to zynq_qspi_slave
+ * @data:	Pointer to the 32 bit variable where data is to be copied
+ * @size:	Number of bytes to be copied from TX buffer to data
+ */
+static void zynq_qspi_write(struct zynq_qspi_slave *zslave, u32 *data, u8 size)
+{
+	if (zslave->tx_buf) {
+		switch (size) {
+		case 1:
+			*data = *((u8 *)zslave->tx_buf);
+			zslave->tx_buf += 1;
+			*data |= 0xFFFFFF00;
+			break;
+		case 2:
+			*data = *((u16 *)zslave->tx_buf);
+			zslave->tx_buf += 2;
+			*data |= 0xFFFF0000;
+			break;
+		case 3:
+			*data = *((u16 *)zslave->tx_buf);
+			zslave->tx_buf += 2;
+			*data |= (*((u8 *)zslave->tx_buf) << 16);
+			zslave->tx_buf += 1;
+			*data |= 0xFF000000;
+			break;
+		case 4:
+			/* Can not assume word aligned buffer */
+			memcpy(data, zslave->tx_buf, size);
+			zslave->tx_buf += 4;
+			break;
+		default:
+			/* This will never execute */
+			break;
+		}
+	} else {
+		*data = 0;
+	}
+
+	zslave->tx_len -= size;
+}
+
+static int zynq_qspi_check_txfifo(struct zynq_qspi_slave *zslave)
+{
+	u32 ts, status;
+
+	ts = get_timer(0);
+	status = readl(&zslave->base->isr);
+	while (!(status & ZYNQ_QSPI_IXR_TXOW_MASK)) {
+		if (get_timer(ts) > CONFIG_SYS_ZYNQ_QSPI_WAIT) {
+			printf("spi_xfer: Timeout! TX FIFO not full\n");
+			return -1;
+		}
+		status = readl(&zslave->base->isr);
+	}
+
+	return 0;
+}
+
+static int zynq_qspi_process_tx(struct zynq_qspi_slave *zslave)
+{
+	struct zynq_qspi_inst_format *curr_inst;
+	u8 inst, index;
+	u32 buf;
+
+	inst = *(u8 *)zslave->tx_buf;
+	/* instuction */
+	if (inst && zslave->is_inst) {
+		for (index = 0; index < ARRAY_SIZE(flash_inst); index++)
+			if (inst == flash_inst[index].inst)
+				break;
+
+		if (index == ARRAY_SIZE(flash_inst)) {
+			printf("spi_xfer: Unsupported inst %02x\n", inst);
+			return -1;
+		}
+
+		curr_inst = &flash_inst[index];
+		debug("spi_xfer: inst:%02x inst_size:%d inst_off:%02x\n",
+		      curr_inst->inst, curr_inst->inst_size,
+		      curr_inst->inst_off);
+
+		zynq_qspi_write(zslave, &buf, curr_inst->inst_size);
+		writel(buf, &zslave->base->cr + (curr_inst->inst_off / 4));
+		zslave->is_inst = 0;
+	} else if (!zslave->is_inst) { /* addr + data */
+		if (zslave->tx_len < 4) {
+			/* Check TXOW for txd1, txd2 and txd3 */
+			if (zynq_qspi_check_txfifo(zslave) < 0)
+				return -1;
+
+			zynq_qspi_write(zslave, &buf, zslave->tx_len);
+			writel(buf,
+			       &zslave->base->txd1r + (zslave->tx_len - 1));
+		} else {
+			zynq_qspi_write(zslave, &buf, 4);
+			writel(buf, &zslave->base->txd0r);
+		}
+	}
+
+	return 0;
+}
+
+int spi_cs_is_valid(unsigned int bus, unsigned int cs)
+{
+	/* 1 bus with 1 chipselect */
+	return bus < 1 && cs < 1;
+}
+
+void spi_cs_activate(struct spi_slave *slave)
+{
+	struct zynq_qspi_slave *zslave = to_zynq_qspi_slave(slave);
+
+	debug("spi_cs_activate: 0x%08x\n", (u32)slave);
+	clrbits_le32(&zslave->base->cr, ZYNQ_QSPI_CR_PCS_MASK);
+
+	zslave->is_inst = 1;
+}
+
+void spi_cs_deactivate(struct spi_slave *slave)
+{
+	struct zynq_qspi_slave *zslave = to_zynq_qspi_slave(slave);
+
+	debug("spi_cs_deactivate: 0x%08x\n", (u32)slave);
+	setbits_le32(&zslave->base->cr, ZYNQ_QSPI_CR_PCS_MASK);
+
+	zslave->is_inst = 0;
+}
+
+void spi_init()
+{
+	/* nothing to do */
+}
+
+struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
+		unsigned int max_hz, unsigned int mode)
+{
+	struct zynq_qspi_slave *zslave;
+
+	if (!spi_cs_is_valid(bus, cs))
+		return NULL;
+
+	zslave = spi_alloc_slave(struct zynq_qspi_slave, bus, cs);
+	if (!zslave) {
+		printf("SPI_error: Fail to allocate zynq_qspi_slave\n");
+		return NULL;
+	}
+
+	zslave->base = (struct zynq_qspi_regs *)ZYNQ_QSPI_BASEADDR;
+	zslave->mode = mode;
+	zslave->fifo_depth = ZYNQ_QSPI_FIFO_DEPTH;
+	zslave->input_hz = 200000000;
+	zslave->speed_hz = zslave->input_hz / 2;
+	zslave->req_hz = max_hz;
+
+	/* init the zynq spi hw */
+	zynq_qspi_init_hw(zslave);
+
+	return &zslave->slave;
+}
+
+void spi_free_slave(struct spi_slave *slave)
+{
+	struct zynq_qspi_slave *zslave = to_zynq_qspi_slave(slave);
+
+	debug("spi_free_slave: 0x%08x\n", (u32)slave);
+	free(zslave);
+}
+
+int spi_claim_bus(struct spi_slave *slave)
+{
+	struct zynq_qspi_slave *zslave = to_zynq_qspi_slave(slave);
+	u32 confr = 0;
+	u8 baud_rate_val = 0;
+
+	writel(~ZYNQ_QSPI_ENR_SPI_EN_MASK, &zslave->base->enr);
+
+	/* Set the SPI Clock phase and polarities */
+	confr = readl(&zslave->base->cr);
+	confr &= ~(ZYNQ_QSPI_CR_CPHA_MASK | ZYNQ_QSPI_CR_CPOL_MASK);
+	if (zslave->mode & SPI_CPHA)
+		confr |= ZYNQ_QSPI_CR_CPHA_MASK;
+	if (zslave->mode & SPI_CPOL)
+		confr |= ZYNQ_QSPI_CR_CPOL_MASK;
+
+	/* Set the clock frequency */
+	if (zslave->req_hz == 0) {
+		/* Set baudrate x8, if the req_hz is 0 */
+		baud_rate_val = 0x2;
+	} else if (zslave->speed_hz != zslave->req_hz) {
+		while ((baud_rate_val < 8) &&
+				((zslave->input_hz /
+				(2 << baud_rate_val)) > zslave->req_hz))
+			baud_rate_val++;
+		zslave->speed_hz = zslave->req_hz / (2 << baud_rate_val);
+	}
+	confr &= ~ZYNQ_QSPI_CR_BRD_MASK;
+	confr |= (baud_rate_val << 3);
+	writel(confr, &zslave->base->cr);
+
+	writel(ZYNQ_QSPI_ENR_SPI_EN_MASK, &zslave->base->enr);
+
+	return 0;
+}
+
+void spi_release_bus(struct spi_slave *slave)
+{
+	struct zynq_qspi_slave *zslave = to_zynq_qspi_slave(slave);
+
+	debug("spi_release_bus: 0x%08x\n", (u32)slave);
+	writel(~ZYNQ_QSPI_ENR_SPI_EN_MASK, &zslave->base->enr);
+}
+
+int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
+		void *din, unsigned long flags)
+{
+	struct zynq_qspi_slave *zslave = to_zynq_qspi_slave(slave);
+	u32 len = bitlen / 8, tx_tvl;
+	u32 buf, status;
+
+	debug("spi_xfer: bus:%i cs:%i bitlen:%i len:%i flags:%lx\n",
+	      slave->bus, slave->cs, bitlen, len, flags);
+
+	if (bitlen == 0)
+		return -1;
+
+	if (bitlen % 8) {
+		debug("spi_xfer: Non byte aligned SPI transfer\n");
+		return -1;
+	}
+
+	if (flags & SPI_XFER_BEGIN)
+		spi_cs_activate(slave);
+
+	zslave->tx_len = len;
+	zslave->rx_len = len;
+	zslave->tx_buf = dout;
+	zslave->rx_buf = din;
+	while (zslave->rx_len > 0) {
+		/* Write the data into TX FIFO - tx threshold is fifo_depth */
+		tx_tvl = 0;
+		while ((tx_tvl < zslave->fifo_depth) && zslave->tx_len) {
+			if (zynq_qspi_process_tx(zslave) < 0) {
+				flags |= SPI_XFER_END;
+				goto out;
+			}
+			tx_tvl++;
+		}
+
+		/* Check TX FIFO completion */
+		if (zynq_qspi_check_txfifo(zslave) < 0) {
+			flags |= SPI_XFER_END;
+			goto out;
+		}
+
+		/* Read the data from RX FIFO */
+		status = readl(&zslave->base->isr);
+		while (status & ZYNQ_QSPI_IXR_RXNEMPTY_MASK) {
+			buf = readl(&zslave->base->rxdr);
+			if (zslave->rx_len < 4)
+				zynq_qspi_read(zslave, buf, zslave->rx_len);
+			else
+				zynq_qspi_read(zslave, buf, 4);
+			status = readl(&zslave->base->isr);
+		}
+	}
+
+out:
+	if (flags & SPI_XFER_END)
+		spi_cs_deactivate(slave);
+
+	return 0;
+}