#define ZYNQMP_QSPI_IXR_TXFULL_MASK 0x00000008 /* QSPI TX FIFO is full */
#define ZYNQMP_QSPI_IXR_RXNEMTY_MASK 0x00000010 /* QSPI RX FIFO Not Empty */
#define ZYNQMP_QSPI_IXR_GFEMTY_MASK 0x00000080 /* QSPI Generic FIFO Empty */
+#define ZYNQMP_QSPI_IXR_GFNFULL_MASK 0x00000200 /* QSPI GENFIFO not full */
#define ZYNQMP_QSPI_IXR_ALL_MASK (ZYNQMP_QSPI_IXR_TXNFULL_MASK | \
ZYNQMP_QSPI_IXR_RXNEMTY_MASK)
#define ZYNQMP_QSPI_GFIFO_SELECT (1 << 0)
#define ZYNQMP_QSPI_FIFO_THRESHOLD 1
+#define ZYNQMP_QSPI_GENFIFO_THRESHOLD 31
#define SPI_XFER_ON_BOTH 0
#define SPI_XFER_ON_LOWER 1
u32 speed_hz;
unsigned int is_dual;
unsigned int tx_rx_mode;
+ unsigned int io_mode;
};
struct zynqmp_qspi_priv {
unsigned cs_change:1;
unsigned int dummy_bytes;
unsigned int tx_rx_mode;
+ unsigned int io_mode;
};
static u8 last_cmd;
else
plat->is_dual = SF_DUAL_STACKED_FLASH;
+ plat->io_mode = fdtdec_get_bool(gd->fdt_blob, dev_of_offset(bus),
+ "has-io-mode");
+
offset = fdt_first_subnode(gd->fdt_blob, dev_of_offset(bus));
value = fdtdec_get_uint(gd->fdt_blob, offset, "spi-rx-bus-width", 1);
writel(ZYNQMP_QSPI_GFIFO_ALL_INT_MASK, ®s->idisr);
writel(ZYNQMP_QSPI_FIFO_THRESHOLD, ®s->txftr);
writel(ZYNQMP_QSPI_FIFO_THRESHOLD, ®s->rxftr);
+ writel(ZYNQMP_QSPI_GENFIFO_THRESHOLD, ®s->gqfthr);
writel(ZYNQMP_QSPI_GFIFO_ALL_INT_MASK, ®s->isr);
+ writel(0x0, ®s->enbr);
config_reg = readl(®s->confr);
- config_reg &= ~(ZYNQMP_QSPI_GFIFO_STRT_MODE_MASK |
- ZYNQMP_QSPI_CONFIG_MODE_EN_MASK);
- config_reg |= ZYNQMP_QSPI_CONFIG_DMA_MODE |
- ZYNQMP_QSPI_GFIFO_WP_HOLD |
+ config_reg &= ~(ZYNQMP_QSPI_CONFIG_MODE_EN_MASK);
+ config_reg |= ZYNQMP_QSPI_GFIFO_WP_HOLD |
ZYNQMP_QSPI_DFLT_BAUD_RATE_DIV;
+ if (priv->io_mode) {
+ config_reg |= ZYNQMP_QSPI_GFIFO_STRT_MODE_MASK;
+ } else {
+ config_reg &= ~(ZYNQMP_QSPI_GFIFO_STRT_MODE_MASK);
+ config_reg |= ZYNQMP_QSPI_CONFIG_DMA_MODE;
+ }
+
writel(config_reg, ®s->confr);
writel(ZYNQMP_QSPI_ENABLE_ENABLE_MASK, ®s->enbr);
u32 gqspi_fifo_reg)
{
struct zynqmp_qspi_regs *regs = priv->regs;
- u32 reg;
+ u32 reg, config_reg, ier;
+ config_reg = readl(®s->confr);
+ /* Manual start if needed */
+ if (config_reg & ZYNQMP_QSPI_GEN_FIFO_STRT_MOD) {
+ config_reg |= ZYNQMP_QSPI_STRT_GEN_FIFO;
+ writel(config_reg, ®s->confr);
+
+ /* Enable interrupts */
+ ier = readl(®s->ier);
+ ier |= ZYNQMP_QSPI_IXR_ALL_MASK;
+ writel(ier, ®s->ier);
+ }
+
+ /* Wait until the fifo is not full to write the new command */
do {
reg = readl(®s->isr);
- } while (!(reg & ZYNQMP_QSPI_IXR_GFEMTY_MASK));
+ } while (!(reg & ZYNQMP_QSPI_IXR_GFNFULL_MASK));
writel(gqspi_fifo_reg, ®s->genfifo);
}
priv->dma_regs = plat->dma_regs;
priv->is_dual = plat->is_dual;
priv->tx_rx_mode = plat->tx_rx_mode;
+ priv->io_mode = plat->io_mode;
if (priv->is_dual == -1) {
debug("%s: No QSPI device detected based on MIO settings\n",
static int zynqmp_qspi_fill_tx_fifo(struct zynqmp_qspi_priv *priv, u32 size)
{
- u32 data;
+ u32 data, config_reg, ier;
u32 timeout = ZYNQMP_QSPI_TIMEOUT;
struct zynqmp_qspi_regs *regs = priv->regs;
u32 *buf = (u32 *)priv->tx_buf;
debug("TxFIFO: 0x%x, size: 0x%x\n", readl(®s->isr),
size);
+ config_reg = readl(®s->confr);
+ /* Manual start if needed */
+ if (config_reg & ZYNQMP_QSPI_GEN_FIFO_STRT_MOD) {
+ config_reg |= ZYNQMP_QSPI_STRT_GEN_FIFO;
+ writel(config_reg, ®s->confr);
+ /* Enable interrupts */
+ ier = readl(®s->ier);
+ ier |= ZYNQMP_QSPI_IXR_ALL_MASK;
+ writel(ier, ®s->ier);
+ }
+
while (size && timeout) {
if (readl(®s->isr) &
ZYNQMP_QSPI_IXR_TXNFULL_MASK) {
return ret;
}
+static int zynqmp_qspi_start_io(struct zynqmp_qspi_priv *priv,
+ u32 gen_fifo_cmd, u32 *buf)
+{
+ u32 len;
+ u32 actuallen = priv->len;
+ u32 config_reg, ier, isr;
+ u32 timeout = ZYNQMP_QSPI_TIMEOUT;
+ struct zynqmp_qspi_regs *regs = priv->regs;
+ u32 last_bits;
+ u32 *traverse = buf;
+
+ while (priv->len) {
+ len = zynqmp_qspi_calc_exp(priv, &gen_fifo_cmd);
+ /* If exponent bit is set, reset immediate to be 2^len */
+ if (gen_fifo_cmd & ZYNQMP_QSPI_GFIFO_EXP_MASK)
+ priv->bytes_to_receive = (1 << len);
+ else
+ priv->bytes_to_receive = len;
+ zynqmp_qspi_fill_gen_fifo(priv, gen_fifo_cmd);
+ debug("GFIFO_CMD_RX:0x%x\n", gen_fifo_cmd);
+ /* Manual start */
+ config_reg = readl(®s->confr);
+ config_reg |= ZYNQMP_QSPI_STRT_GEN_FIFO;
+ writel(config_reg, ®s->confr);
+ /* Enable RX interrupts for IO mode */
+ ier = readl(®s->ier);
+ ier |= ZYNQMP_QSPI_IXR_ALL_MASK;
+ writel(ier, ®s->ier);
+ while (priv->bytes_to_receive && timeout) {
+ isr = readl(®s->isr);
+ if (isr & ZYNQMP_QSPI_IXR_RXNEMTY_MASK) {
+ if (priv->bytes_to_receive >= 4) {
+ *traverse = readl(®s->drxr);
+ traverse++;
+ priv->bytes_to_receive -= 4;
+ } else {
+ last_bits = readl(®s->drxr);
+ memcpy(traverse, &last_bits,
+ priv->bytes_to_receive);
+ priv->bytes_to_receive = 0;
+ }
+ timeout = ZYNQMP_QSPI_TIMEOUT;
+ } else {
+ udelay(1);
+ timeout--;
+ }
+ }
+
+ debug("buf:0x%lx, rxbuf:0x%lx, *buf:0x%x len: 0x%x\n",
+ (unsigned long)buf, (unsigned long)priv->rx_buf,
+ *buf, actuallen);
+ if (!timeout) {
+ printf("IO timeout: %d\n", readl(®s->isr));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
static int zynqmp_qspi_start_dma(struct zynqmp_qspi_priv *priv,
u32 gen_fifo_cmd, u32 *buf)
{
* Check if receive buffer is aligned to 4 byte and length
* is multiples of four byte as we are using dma to receive.
*/
- if (!((unsigned long)priv->rx_buf & (ZYNQMP_QSPI_DMA_ALIGN - 1)) &&
- !(actuallen % ZYNQMP_QSPI_DMA_ALIGN)) {
+ if ((!((unsigned long)priv->rx_buf & (ZYNQMP_QSPI_DMA_ALIGN - 1)) &&
+ !(actuallen % ZYNQMP_QSPI_DMA_ALIGN)) || priv->io_mode) {
buf = (u32 *)priv->rx_buf;
- return zynqmp_qspi_start_dma(priv, gen_fifo_cmd, buf);
+ if (priv->io_mode)
+ return zynqmp_qspi_start_io(priv, gen_fifo_cmd, buf);
+ else
+ return zynqmp_qspi_start_dma(priv, gen_fifo_cmd, buf);
}
ALLOC_CACHE_ALIGN_BUFFER(u8, tmp, roundup(priv->len,