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

Jagan Teki jagannadha.sutradharudu-teki at xilinx.com
Wed Nov 6 07:18:07 CET 2013


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 at 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.

-- 
Thanks,
Jagan.




More information about the U-Boot mailing list