@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * (C) Copyright 2018 Xilinx
+ * (C) Copyright 2013 - 2022, Xilinx, Inc.
+ * (C) Copyright 2023, Advanced Micro Devices, Inc.
*
* Xilinx ZynqMP Generic Quad-SPI(QSPI) controller driver(master mode only)
*/
@@ -23,6 +24,8 @@
#include <linux/bitops.h>
#include <linux/err.h>
#include <linux/sizes.h>
+#include <linux/mtd/spi-nor.h>
+#include "../mtd/spi/sf_internal.h"
#include <zynqmp_firmware.h>
#define GQSPI_GFIFO_STRT_MODE_MASK BIT(29)
@@ -86,6 +89,9 @@
#define SPI_XFER_ON_LOWER 1
#define SPI_XFER_ON_UPPER 2
+#define GQSPI_SELECT_LOWER_CS BIT(0)
+#define GQSPI_SELECT_UPPER_CS BIT(1)
+
#define GQSPI_DMA_ALIGN 0x4
#define GQSPI_MAX_BAUD_RATE_VAL 7
#define GQSPI_DFLT_BAUD_RATE_VAL 2
@@ -181,13 +187,14 @@ struct zynqmp_qspi_priv {
int bytes_to_transfer;
int bytes_to_receive;
const struct spi_mem_op *op;
+ unsigned int is_parallel;
+ unsigned int u_page;
+ unsigned int bus;
+ unsigned int stripe;
+ unsigned int flags;
+ u32 max_hz;
};
-__weak int zynqmp_mmio_write(const u32 address, const u32 mask, const u32 value)
-{
- return 0;
-}
-
static int zynqmp_qspi_of_to_plat(struct udevice *bus)
{
struct zynqmp_qspi_plat *plat = dev_get_plat(bus);
@@ -234,9 +241,30 @@ static u32 zynqmp_qspi_bus_select(struct zynqmp_qspi_priv *priv)
{
u32 gqspi_fifo_reg = 0;
- gqspi_fifo_reg = GQSPI_GFIFO_LOW_BUS |
- GQSPI_GFIFO_CS_LOWER;
-
+ if (priv->is_parallel) {
+ if (priv->bus == SPI_XFER_ON_BOTH)
+ gqspi_fifo_reg = GQSPI_GFIFO_LOW_BUS |
+ GQSPI_GFIFO_UP_BUS |
+ GQSPI_GFIFO_CS_UPPER |
+ GQSPI_GFIFO_CS_LOWER;
+ else if (priv->bus == SPI_XFER_ON_LOWER)
+ gqspi_fifo_reg = GQSPI_GFIFO_LOW_BUS |
+ GQSPI_GFIFO_CS_UPPER |
+ GQSPI_GFIFO_CS_LOWER;
+ else if (priv->bus == SPI_XFER_ON_UPPER)
+ gqspi_fifo_reg = GQSPI_GFIFO_UP_BUS |
+ GQSPI_GFIFO_CS_LOWER |
+ GQSPI_GFIFO_CS_UPPER;
+ else
+ debug("Wrong Bus selection:0x%x\n", priv->bus);
+ } else {
+ if (priv->u_page)
+ gqspi_fifo_reg = GQSPI_GFIFO_LOW_BUS |
+ GQSPI_GFIFO_CS_UPPER;
+ else
+ gqspi_fifo_reg = GQSPI_GFIFO_LOW_BUS |
+ GQSPI_GFIFO_CS_LOWER;
+ }
return gqspi_fifo_reg;
}
@@ -279,7 +307,6 @@ static void zynqmp_qspi_fill_gen_fifo(struct zynqmp_qspi_priv *priv,
GQSPI_TIMEOUT, 1);
if (ret)
printf("%s Timeout\n", __func__);
-
}
static void zynqmp_qspi_chipselect(struct zynqmp_qspi_priv *priv, int is_on)
@@ -291,7 +318,13 @@ static void zynqmp_qspi_chipselect(struct zynqmp_qspi_priv *priv, int is_on)
gqspi_fifo_reg |= GQSPI_SPI_MODE_SPI |
GQSPI_IMD_DATA_CS_ASSERT;
} else {
- gqspi_fifo_reg = GQSPI_GFIFO_LOW_BUS;
+ if (priv->is_parallel)
+ gqspi_fifo_reg = GQSPI_GFIFO_UP_BUS |
+ GQSPI_GFIFO_LOW_BUS;
+ else if (priv->u_page)
+ gqspi_fifo_reg = GQSPI_GFIFO_UP_BUS;
+ else
+ gqspi_fifo_reg = GQSPI_GFIFO_LOW_BUS;
gqspi_fifo_reg |= GQSPI_IMD_DATA_CS_DEASSERT;
}
@@ -362,13 +395,15 @@ static int zynqmp_qspi_set_speed(struct udevice *bus, uint speed)
u32 confr;
u8 baud_rate_val = 0;
- debug("%s\n", __func__);
- if (speed > plat->frequency)
- speed = plat->frequency;
+ /*
+ * If speed == 0 or speed > max freq, then set speed to highest
+ */
+ if (!speed || speed > priv->max_hz)
+ speed = priv->max_hz;
+
+ debug("%s %d\n", __func__, speed);
if (plat->speed_hz != speed) {
- /* Set the clock frequency */
- /* If speed == 0, default to lowest speed */
while ((baud_rate_val < 8) &&
((plat->frequency /
(2 << baud_rate_val)) > speed))
@@ -391,6 +426,18 @@ static int zynqmp_qspi_set_speed(struct udevice *bus, uint speed)
return 0;
}
+static int zynqmp_qspi_child_pre_probe(struct udevice *bus)
+{
+ struct spi_slave *slave = dev_get_parent_priv(bus);
+ struct zynqmp_qspi_priv *priv = dev_get_priv(bus->parent);
+
+ slave->multi_cs_cap = true;
+ slave->bytemode = SPI_4BYTE_MODE;
+ priv->max_hz = slave->max_hz;
+
+ return 0;
+}
+
static int zynqmp_qspi_probe(struct udevice *bus)
{
struct zynqmp_qspi_plat *plat = dev_get_plat(bus);
@@ -455,7 +502,7 @@ static int zynqmp_qspi_set_mode(struct udevice *bus, uint mode)
static int zynqmp_qspi_fill_tx_fifo(struct zynqmp_qspi_priv *priv, u32 size)
{
- u32 data;
+ u32 data, ier;
int ret = 0;
struct zynqmp_qspi_regs *regs = priv->regs;
u32 *buf = (u32 *)priv->tx_buf;
@@ -464,6 +511,11 @@ static int zynqmp_qspi_fill_tx_fifo(struct zynqmp_qspi_priv *priv, u32 size)
debug("TxFIFO: 0x%x, size: 0x%x\n", readl(®s->isr),
size);
+ /* Enable interrupts */
+ ier = readl(®s->ier);
+ ier |= GQSPI_IXR_ALL_MASK | GQSPI_IXR_TXFIFOEMPTY_MASK;
+ writel(ier, ®s->ier);
+
while (size) {
ret = wait_for_bit_le32(®s->isr, GQSPI_IXR_TXNFULL_MASK, 1,
GQSPI_TIMEOUT, 1);
@@ -587,6 +639,9 @@ static int zynqmp_qspi_genfifo_fill_tx(struct zynqmp_qspi_priv *priv)
gen_fifo_cmd |= zynqmp_qspi_genfifo_mode(priv->op->data.buswidth);
gen_fifo_cmd |= GQSPI_GFIFO_TX | GQSPI_GFIFO_DATA_XFR_MASK;
+ if (priv->stripe)
+ gen_fifo_cmd |= GQSPI_GFIFO_STRIPE_MASK;
+
while (priv->len) {
len = zynqmp_qspi_calc_exp(priv, &gen_fifo_cmd);
zynqmp_qspi_fill_gen_fifo(priv, gen_fifo_cmd);
@@ -690,7 +745,7 @@ static int zynqmp_qspi_start_dma(struct zynqmp_qspi_priv *priv,
writel(GQSPI_DMA_DST_I_STS_MASK, &dma_regs->dmaier);
addr = (unsigned long)buf;
size = roundup(priv->len, GQSPI_DMA_ALIGN);
- flush_dcache_range(addr, addr + size);
+ invalidate_dcache_range(addr, addr + size);
while (priv->len) {
zynqmp_qspi_calc_exp(priv, &gen_fifo_cmd);
@@ -707,6 +762,8 @@ static int zynqmp_qspi_start_dma(struct zynqmp_qspi_priv *priv,
return -ETIMEDOUT;
}
+ invalidate_dcache_range(addr, addr + size);
+
writel(GQSPI_DMA_DST_I_STS_DONE, &dma_regs->dmaisr);
debug("buf:0x%lx, rxbuf:0x%lx, *buf:0x%x len: 0x%x\n",
@@ -733,6 +790,9 @@ static int zynqmp_qspi_genfifo_fill_rx(struct zynqmp_qspi_priv *priv)
gen_fifo_cmd |= zynqmp_qspi_genfifo_mode(priv->op->data.buswidth);
gen_fifo_cmd |= GQSPI_GFIFO_RX | GQSPI_GFIFO_DATA_XFR_MASK;
+ if (priv->stripe)
+ gen_fifo_cmd |= GQSPI_GFIFO_STRIPE_MASK;
+
/*
* Check if receive buffer is aligned to 4 byte and length
* is multiples of four byte as we are using dma to receive.
@@ -773,6 +833,33 @@ static int zynqmp_qspi_release_bus(struct udevice *dev)
return 0;
}
+static bool zynqmp_qspi_update_stripe(const struct spi_mem_op *op)
+{
+ /*
+ * This is a list of opcodes for which we must not use striped access
+ * even in dual parallel mode, but instead broadcast the same data to
+ * both chips. This is primarily erase commands and writing some
+ * registers.
+ */
+ switch (op->cmd.opcode) {
+ case SPINOR_OP_BE_4K:
+ case SPINOR_OP_BE_32K:
+ case SPINOR_OP_CHIP_ERASE:
+ case SPINOR_OP_SE:
+ case SPINOR_OP_BE_32K_4B:
+ case SPINOR_OP_SE_4B:
+ case SPINOR_OP_BE_4K_4B:
+ case SPINOR_OP_WRSR:
+ case SPINOR_OP_WREAR:
+ case SPINOR_OP_BRWR:
+ return false;
+ case SPINOR_OP_WRSR2:
+ return op->addr.nbytes != 0;
+ default:
+ return true;
+ }
+}
+
static int zynqmp_qspi_exec_op(struct spi_slave *slave,
const struct spi_mem_op *op)
{
@@ -784,6 +871,25 @@ static int zynqmp_qspi_exec_op(struct spi_slave *slave,
priv->rx_buf = op->data.buf.in;
priv->len = op->data.nbytes;
+ if (slave->flags & SPI_XFER_U_PAGE)
+ priv->u_page = 1;
+ else
+ priv->u_page = 0;
+
+ if ((slave->flags & GQSPI_SELECT_LOWER_CS) &&
+ (slave->flags & GQSPI_SELECT_UPPER_CS))
+ priv->is_parallel = true;
+
+ priv->stripe = 0;
+ priv->bus = 0;
+
+ if (priv->is_parallel) {
+ if (slave->flags & SPI_XFER_MASK)
+ priv->bus = (slave->flags & SPI_XFER_MASK) >> 8;
+ if (zynqmp_qspi_update_stripe(op))
+ priv->stripe = 1;
+ }
+
zynqmp_qspi_chipselect(priv, 1);
/* Send opcode, addr, dummy */
@@ -797,6 +903,9 @@ static int zynqmp_qspi_exec_op(struct spi_slave *slave,
zynqmp_qspi_chipselect(priv, 0);
+ priv->is_parallel = false;
+ slave->flags &= ~SPI_XFER_MASK;
+
return ret;
}
@@ -827,4 +936,5 @@ U_BOOT_DRIVER(zynqmp_qspi) = {
.plat_auto = sizeof(struct zynqmp_qspi_plat),
.priv_auto = sizeof(struct zynqmp_qspi_priv),
.probe = zynqmp_qspi_probe,
+ .child_pre_probe = zynqmp_qspi_child_pre_probe,
};
@@ -39,6 +39,9 @@
#define SPI_DEFAULT_WORDLEN 8
+#define SPI_3BYTE_MODE 0x0
+#define SPI_4BYTE_MODE 0x1
+
/* SPI transfer flags */
#define SPI_XFER_STRIPE (1 << 6)
#define SPI_XFER_MASK (3 << 8)
@@ -168,6 +171,15 @@ struct spi_slave {
#define SPI_XFER_ONCE (SPI_XFER_BEGIN | SPI_XFER_END)
#define SPI_XFER_U_PAGE BIT(4)
#define SPI_XFER_STACKED BIT(5)
+
+ u32 bytemode;
+
+ /*
+ * Flag indicating that the spi-controller has multi chip select
+ * capability and can assert/de-assert more than one chip select
+ * at once.
+ */
+ bool multi_cs_cap;
};
/**