
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;
+}