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 <jaga...@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 081624e..3b86195 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
 
 /* Reflect slcr offsets */
 struct slcr_regs {
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 91d24ce..5b09eac 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -40,6 +40,7 @@ COBJS-$(CONFIG_TEGRA20_SLINK) += tegra20_slink.o
 COBJS-$(CONFIG_TEGRA114_SPI) += tegra114_spi.o
 COBJS-$(CONFIG_XILINX_SPI) += xilinx_spi.o
 COBJS-$(CONFIG_ZYNQ_SPI) += zynq_spi.o
+COBJS-$(CONFIG_ZYNQ_QSPI) += zynq_qspi.o
 
 COBJS  := $(COBJS-y)
 SRCS   := $(COBJS:.o=.c)
diff --git a/drivers/spi/zynq_qspi.c b/drivers/spi/zynq_qspi.c
new file mode 100644
index 0000000..df090d7
--- /dev/null
+++ b/drivers/spi/zynq_qspi.c
@@ -0,0 +1,447 @@
+/*
+ * (C) Copyright 2013 Inc.
+ *
+ * Xilinx 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;
+}
-- 
1.8.3


_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to