diff --git a/arch/arm/boot/dts/overlays/README b/arch/arm/boot/dts/overlays/README
index 0fba9016138b9..3fb9e0c0f03d7 100644
--- a/arch/arm/boot/dts/overlays/README
+++ b/arch/arm/boot/dts/overlays/README
@@ -3316,6 +3316,8 @@ Load: dtoverlay=jedec-spi-nor,=
Params: spi- Enable flash device on SPI, CS#
fastr Add fast read capability to the flash device
speed Maximum SPI frequency (Hz)
+ quad Enable quad (4-wire) I/O mode, which also
+ uses GPIOs 0 and 1
flash-spi- Same as spi- (deprecated)
flash-fastr-spi- Same as spi->m>,fastr (deprecated)
diff --git a/arch/arm/boot/dts/overlays/jedec-spi-nor-overlay.dts b/arch/arm/boot/dts/overlays/jedec-spi-nor-overlay.dts
index fb6d4bc91bf3c..4b3bf153adf9c 100644
--- a/arch/arm/boot/dts/overlays/jedec-spi-nor-overlay.dts
+++ b/arch/arm/boot/dts/overlays/jedec-spi-nor-overlay.dts
@@ -4,6 +4,7 @@
// flash-spi- - Enables flash device on SPI, CS#.
// flash-fastr-spi- - Enables flash device with fast read capability on SPI, CS#.
// speed - Set the SPI clock speed in Hz
+// quad - Enable quad (4-wire) I/O mode
//
// If devices are present on SPI1 or SPI2, those interfaces must be enabled with one of the spi1-1/2/3cs and/or spi2-1/2/3cs overlays.
//
@@ -89,6 +90,13 @@
};
};
+ fragment@9 {
+ target = <&spi0_pins>;
+ __dormant__ {
+ pins = "gpio9", "gpio10", "gpio11", "gpio0", "gpio1";
+ };
+ };
+
payload: fragment@100 {
target = <&spi0>;
__overlay__ {
@@ -100,6 +108,9 @@
compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <500000>;
+ /* The defaults. The quad parameter changes them. */
+ spi-rx-bus-width = <1>;
+ spi-tx-bus-width = <1>;
};
};
};
@@ -131,6 +142,9 @@
flash-fastr-spi2-2 = <0>,"+7+8", <&payload>,"target:0=",<&spi2>, <&spi_nor>,"reg:0=2";
fastr = <0>,"+8";
speed = <&spi_nor>, "spi-max-frequency:0";
+ quad = <0>,"+9",
+ <&spi_nor>,"spi-rx-bus-width:0=4",
+ <&spi_nor>,"spi-tx-bus-width:0=4";
};
};
diff --git a/drivers/mtd/spi-nor/issi.c b/drivers/mtd/spi-nor/issi.c
index 18d9a00aa22eb..6c473006f948f 100644
--- a/drivers/mtd/spi-nor/issi.c
+++ b/drivers/mtd/spi-nor/issi.c
@@ -99,6 +99,7 @@ static const struct flash_info issi_nor_parts[] = {
.id = SNOR_ID(0x9d, 0x60, 0x18),
.name = "is25lp128",
.size = SZ_16M,
+ .flags = SPI_NOR_QUAD_PP,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ,
}, {
.id = SNOR_ID(0x9d, 0x60, 0x19),
diff --git a/drivers/spi/spi-dw-bt1.c b/drivers/spi/spi-dw-bt1.c
index 4a5be813efa75..90caf790ebe75 100644
--- a/drivers/spi/spi-dw-bt1.c
+++ b/drivers/spi/spi-dw-bt1.c
@@ -135,7 +135,7 @@ static ssize_t dw_spi_bt1_dirmap_read(struct spi_mem_dirmap_desc *desc,
dw_spi_enable_chip(dws, 0);
- dw_spi_update_config(dws, mem->spi, &cfg);
+ dw_spi_update_config(dws, mem->spi, &cfg, NULL);
dw_spi_umask_intr(dws, DW_SPI_INT_RXFI);
diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index bb6989b4b6185..90692396b12d1 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -375,7 +375,7 @@ static u32 dw_spi_prepare_cr0(struct dw_spi *dws, struct spi_device *spi)
}
void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
- struct dw_spi_cfg *cfg)
+ struct dw_spi_cfg *cfg, struct dw_spi_enh_cfg *ecfg)
{
struct dw_spi_chip_data *chip = spi_get_ctldata(spi);
u32 cr0 = chip->cr0;
@@ -385,15 +385,27 @@ void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
/* CTRLR0[ 4/3: 0] or CTRLR0[ 20: 16] Data Frame Size */
cr0 |= (cfg->dfs - 1) << dws->dfs_offset;
- if (dw_spi_ip_is(dws, PSSI))
+ if (dw_spi_ip_is(dws, PSSI)) {
/* CTRLR0[ 9:8] Transfer Mode */
cr0 |= FIELD_PREP(DW_PSSI_CTRLR0_TMOD_MASK, cfg->tmode);
- else
+ /* CTRLR0[22:21] Enhanced SPI frame format (quad) */
+ if (dws->caps & DW_SPI_CAP_QUAD)
+ cr0 |= FIELD_PREP(DW_PSSI_CTRLR0_ENH_FRF_MASK, cfg->enh_frf);
+ } else {
/* CTRLR0[11:10] Transfer Mode */
cr0 |= FIELD_PREP(DW_HSSI_CTRLR0_TMOD_MASK, cfg->tmode);
+ }
dw_writel(dws, DW_SPI_CTRLR0, cr0);
+ if (ecfg) {
+ cr0 = FIELD_PREP(DW_SPI_ENH_CTRLR0_TRANS_TYPE_MASK, ecfg->trans_type) |
+ FIELD_PREP(DW_SPI_ENH_CTRLR0_ADDR_L_MASK, ecfg->addr_l) |
+ FIELD_PREP(DW_SPI_ENH_CTRLR0_INST_L_MASK, ecfg->inst_l) |
+ FIELD_PREP(DW_SPI_ENH_CTRLR0_WAIT_CYCLE_MASK, ecfg->wait_l);
+ dw_writel(dws, DW_SPI_ENH_CTRLR0, cr0);
+ }
+
if (cfg->tmode == DW_SPI_CTRLR0_TMOD_EPROMREAD ||
cfg->tmode == DW_SPI_CTRLR0_TMOD_RO)
dw_writel(dws, DW_SPI_CTRLR1, cfg->ndf ? cfg->ndf - 1 : 0);
@@ -415,11 +427,144 @@ void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
}
EXPORT_SYMBOL_NS_GPL(dw_spi_update_config, "SPI_DW_CORE");
+/*
+ * The controller transmits only while the Tx FIFO is non-empty, so a frame has
+ * to be initiated by a write to it. A standard receive-only frame needs a
+ * single dummy word. An enhanced frame is initiated by its control words. For
+ * a write the data must follow at once, since the frame ends as soon as the
+ * Tx FIFO drains.
+ */
+void dw_spi_start_frame(struct dw_spi *dws)
+{
+ if (dws->enh.ctrl_len) {
+ dw_write_io_reg(dws, DW_SPI_DR, dws->enh.inst);
+ if (dws->enh.ctrl_len > 1)
+ dw_write_io_reg(dws, DW_SPI_DR, dws->enh.addr);
+
+ /* The control words are consumed and must not be repeated */
+ memset(&dws->enh, 0, sizeof(dws->enh));
+
+ if (dws->tx_len)
+ dw_writer(dws);
+ } else if (!dws->tx_len) {
+ dw_writel(dws, DW_SPI_DR, 0);
+ }
+}
+
+/* True if the multi-lane data of an enhanced frame is still ahead */
+static bool dw_spi_enh_data_pending(struct spi_controller *host,
+ struct spi_transfer *xfer)
+{
+ struct spi_transfer *next = xfer;
+
+ list_for_each_entry_continue(next, &host->cur_msg->transfers, transfer_list)
+ if (next->tx_nbits > 1 || next->rx_nbits > 1)
+ return true;
+
+ return false;
+}
+
+/*
+ * Collect one transfer into the enhanced frame in dws->enh to be written to
+ * the FIFO later. The instruction comes first then the address.
+ */
+static int dw_spi_collect_enh_xfer(struct dw_spi *dws,
+ struct spi_transfer *xfer)
+{
+ unsigned int cycles, i;
+
+ if (!xfer->tx_buf)
+ return -EINVAL;
+
+ if (xfer->dummy_data) {
+ /* Dummy bytes set WAIT_CYCLES, which counts sclk periods */
+ cycles = xfer->len * BITS_PER_BYTE / xfer->tx_nbits;
+ if (cycles > FIELD_MAX(DW_SPI_ENH_CTRLR0_WAIT_CYCLE_MASK))
+ return -EINVAL;
+
+ dws->enh.cfg.wait_l = cycles;
+ } else if (!dws->enh.ctrl_len) {
+ /* The instruction comes first, INST_L is a width */
+ if (xfer->len == 1)
+ dws->enh.cfg.inst_l = DW_SPI_ENH_CTRLR0_INST_L_8BIT;
+ else if (xfer->len == 2)
+ dws->enh.cfg.inst_l = DW_SPI_ENH_CTRLR0_INST_L_16BIT;
+ else
+ return -EINVAL;
+
+ for (i = 0; i < xfer->len; i++)
+ dws->enh.inst = dws->enh.inst << BITS_PER_BYTE |
+ ((const u8 *)xfer->tx_buf)[i];
+
+ dws->enh.ctrl_len = 1;
+ } else if (dws->enh.ctrl_len == 1) {
+ /* Then the address, one FIFO entry, ADDR_L counts nibbles */
+ if (xfer->len > sizeof(dws->enh.addr))
+ return -EINVAL;
+
+ dws->enh.cfg.addr_l = xfer->len * 2;
+ if (xfer->tx_nbits > 1)
+ dws->enh.cfg.trans_type = DW_SPI_ENH_CTRLR0_TRANS_TYPE_TT1;
+
+ for (i = 0; i < xfer->len; i++)
+ dws->enh.addr = dws->enh.addr << BITS_PER_BYTE |
+ ((const u8 *)xfer->tx_buf)[i];
+
+ dws->enh.ctrl_len = 2;
+ } else {
+ /* Nothing else can precede the data */
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Check the collected frame against the transfer and select its format. Only
+ * spi-mem ops are filtered earlier by dw_spi_supports_enh_mem_op().
+ */
+static int dw_spi_check_enh_frame(struct dw_spi *dws, struct dw_spi_cfg *cfg,
+ int buswidth)
+{
+ /* Quad is the only frame format the driver implements */
+ if (buswidth != 4 || !(dws->caps & DW_SPI_CAP_QUAD))
+ return -EOPNOTSUPP;
+
+ /* An enhanced frame always begins with an instruction */
+ if (!dws->enh.ctrl_len)
+ return -EINVAL;
+
+ /* The control words are byte sized, so DFS has to be 8 */
+ if (dws->n_bytes != 1)
+ return -EINVAL;
+
+ /* There is no full duplex enhanced frame */
+ if (cfg->tmode != DW_SPI_CTRLR0_TMOD_RO &&
+ cfg->tmode != DW_SPI_CTRLR0_TMOD_TO)
+ return -EINVAL;
+
+ /* A write ends when the Tx FIFO drains, so all of it has to fit */
+ if (cfg->tmode == DW_SPI_CTRLR0_TMOD_TO &&
+ dws->tx_len + dws->enh.ctrl_len > dws->fifo_len)
+ return -EINVAL;
+
+ cfg->enh_frf = DW_SPI_CTRLR0_ENH_FRF_QUAD;
+
+ return 0;
+}
+
static void dw_spi_irq_setup(struct dw_spi *dws)
{
u16 level;
u8 imask;
+ /*
+ * An enhanced write must be loaded before the interrupts are unmasked,
+ * or TXEI would feed its data ahead of the control words.
+ */
+ if (dws->tx_len && dws->enh.ctrl_len)
+ dw_spi_start_frame(dws);
+
/*
* Originally Tx and Rx data lengths match. Rx FIFO Threshold level
* will be adjusted at the final stage of the IRQ-based SPI transfer
@@ -427,7 +572,7 @@ static void dw_spi_irq_setup(struct dw_spi *dws)
*/
level = min_t(unsigned int, dws->fifo_len / 2, dws->tx_len ? dws->tx_len : dws->rx_len);
dw_writel(dws, DW_SPI_TXFTLR, level);
- dw_writel(dws, DW_SPI_RXFTLR, level - 1);
+ dw_writel(dws, DW_SPI_RXFTLR, level ? level - 1 : 0);
dws->transfer_handler = dw_spi_transfer_handler;
@@ -435,8 +580,9 @@ static void dw_spi_irq_setup(struct dw_spi *dws)
if (dws->rx_len)
imask |= DW_SPI_INT_RXUI | DW_SPI_INT_RXOI | DW_SPI_INT_RXFI;
dw_spi_umask_intr(dws, imask);
- if (!dws->tx_len)
- dw_writel(dws, DW_SPI_DR, 0);
+
+ if (!dws->tx_len && dws->rx_len)
+ dw_spi_start_frame(dws);
}
/*
@@ -459,8 +605,7 @@ static int dw_spi_poll_transfer(struct dw_spi *dws,
delay.unit = SPI_DELAY_UNIT_SCK;
nbits = dws->n_bytes * BITS_PER_BYTE;
- if (!dws->tx_len)
- dw_writel(dws, DW_SPI_DR, 0);
+ dw_spi_start_frame(dws);
do {
if (dws->tx_len)
@@ -493,6 +638,14 @@ static int dw_spi_transfer_one(struct spi_controller *host,
int buswidth;
int ret;
+ /* Start the message with no frame collected */
+ if (list_is_first(&transfer->transfer_list, &host->cur_msg->transfers))
+ memset(&dws->enh, 0, sizeof(dws->enh));
+
+ /* Not transmitted here, the data transfer issues the whole frame */
+ if (dw_spi_enh_data_pending(host, transfer))
+ return dw_spi_collect_enh_xfer(dws, transfer);
+
dws->dma_mapped = 0;
dws->n_bytes = spi_bpw_to_bytes(transfer->bits_per_word);
dws->tx = (void *)transfer->tx_buf;
@@ -508,15 +661,22 @@ static int dw_spi_transfer_one(struct spi_controller *host,
cfg.tmode = DW_SPI_CTRLR0_TMOD_RO;
cfg.ndf = dws->rx_len;
}
- buswidth = transfer->rx_buf ? transfer->rx_nbits :
- (transfer->tx_buf ? transfer->tx_nbits : 1);
+
+ /* A multi-lane transfer here is the data of an enhanced frame */
+ buswidth = transfer->rx_buf ? transfer->rx_nbits : transfer->tx_nbits;
+ if (buswidth > 1) {
+ ret = dw_spi_check_enh_frame(dws, &cfg, buswidth);
+ if (ret)
+ return ret;
+ }
/* Ensure the data above is visible for all CPUs */
smp_mb();
dw_spi_enable_chip(dws, 0);
- dw_spi_update_config(dws, spi, &cfg);
+ dw_spi_update_config(dws, spi, &cfg,
+ buswidth > 1 ? &dws->enh.cfg : NULL);
transfer->effective_speed_hz = dws->current_freq;
@@ -552,13 +712,20 @@ static void dw_spi_handle_err(struct spi_controller *host,
if (dws->dma_mapped)
dws->dma_ops->dma_stop(dws);
+ memset(&dws->enh, 0, sizeof(dws->enh));
dw_spi_reset_chip(dws);
}
static int dw_spi_adjust_mem_op_size(struct spi_mem *mem, struct spi_mem_op *op)
{
+ struct dw_spi *dws = spi_controller_get_devdata(mem->spi->controller);
+
if (op->data.dir == SPI_MEM_DATA_IN)
op->data.nbytes = clamp_val(op->data.nbytes, 0, DW_SPI_NDF_MASK + 1);
+ else if (op->data.buswidth > 1)
+ /* An enhanced write is limited to what the Tx FIFO can hold */
+ op->data.nbytes = min(op->data.nbytes,
+ dws->fifo_len - DW_SPI_ENH_CTRL_ENTRIES);
return 0;
}
@@ -573,6 +740,29 @@ static bool dw_spi_supports_mem_op(struct spi_mem *mem,
return spi_mem_default_supports_op(mem, op);
}
+static bool dw_spi_supports_enh_mem_op(struct spi_mem *mem,
+ const struct spi_mem_op *op)
+{
+ struct dw_spi *dws = spi_controller_get_devdata(mem->spi->controller);
+
+ /* The instruction is always sent single-lane */
+ if (op->cmd.buswidth > 1)
+ return false;
+
+ /* Quad is the only multi-lane data width implemented */
+ if (op->data.buswidth > 1 && op->data.buswidth != 4)
+ return false;
+ if (op->data.buswidth == 4 && !(dws->caps & DW_SPI_CAP_QUAD))
+ return false;
+
+ /* A multi-lane write requires Tx FIFO space after the control words */
+ if (op->data.dir == SPI_MEM_DATA_OUT && op->data.buswidth > 1 &&
+ dws->fifo_len <= DW_SPI_ENH_CTRL_ENTRIES)
+ return false;
+
+ return spi_mem_default_supports_op(mem, op);
+}
+
static int dw_spi_init_mem_buf(struct dw_spi *dws, const struct spi_mem_op *op)
{
unsigned int i, j, len;
@@ -737,10 +927,15 @@ static void dw_spi_stop_mem_op(struct dw_spi *dws, struct spi_device *spi)
static int dw_spi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
{
struct dw_spi *dws = spi_controller_get_devdata(mem->spi->controller);
- struct dw_spi_cfg cfg;
+ struct dw_spi_cfg cfg = {};
unsigned long flags;
int ret;
+ // This path implements single-lane frames only
+ if (op->data.buswidth > 1 || op->addr.buswidth > 1 ||
+ op->dummy.buswidth > 1 || op->cmd.buswidth > 1)
+ return -EOPNOTSUPP;
+
/*
* Collect the outbound data into a single buffer to speed the
* transmission up at least on the initial stage.
@@ -764,7 +959,7 @@ static int dw_spi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
dw_spi_enable_chip(dws, 0);
- dw_spi_update_config(dws, mem->spi, &cfg);
+ dw_spi_update_config(dws, mem->spi, &cfg, NULL);
dw_spi_mask_intr(dws, 0xff);
@@ -840,7 +1035,10 @@ static void dw_spi_init_mem_ops(struct dw_spi *dws)
if (!dws->mem_ops.exec_op && !(dws->caps & DW_SPI_CAP_CS_OVERRIDE) &&
!dws->set_cs) {
dws->mem_ops.adjust_op_size = dw_spi_adjust_mem_op_size;
- dws->mem_ops.supports_op = dw_spi_supports_mem_op;
+ if (dws->caps & DW_SPI_CAP_QUAD)
+ dws->mem_ops.supports_op = dw_spi_supports_enh_mem_op;
+ else
+ dws->mem_ops.supports_op = dw_spi_supports_mem_op;
dws->mem_ops.exec_op = dw_spi_exec_mem_op;
if (!dws->max_mem_freq)
dws->max_mem_freq = dws->max_freq;
@@ -946,14 +1144,22 @@ static void dw_spi_hw_init(struct device *dev, struct dw_spi *dws)
/*
* Detect CTRLR0.DFS field size and offset by testing the lowest bits
* writability. Note DWC SSI controller also has the extended DFS, but
- * with zero offset.
+ * with zero offset. The ENH_FRF field is probed the same way.
*/
if (dw_spi_ip_is(dws, PSSI)) {
u32 cr0, tmp = dw_readl(dws, DW_SPI_CTRLR0);
+ u32 enh_frf = 0;
dw_spi_enable_chip(dws, 0);
+
dw_writel(dws, DW_SPI_CTRLR0, 0xffffffff);
cr0 = dw_readl(dws, DW_SPI_CTRLR0);
+
+ /* ENH_FRF: quad is the only format the driver uses */
+ dw_writel(dws, DW_SPI_CTRLR0, (tmp & ~DW_PSSI_CTRLR0_ENH_FRF_MASK) |
+ FIELD_PREP(DW_PSSI_CTRLR0_ENH_FRF_MASK, DW_SPI_CTRLR0_ENH_FRF_QUAD));
+ enh_frf = FIELD_GET(DW_PSSI_CTRLR0_ENH_FRF_MASK, dw_readl(dws, DW_SPI_CTRLR0));
+
dw_writel(dws, DW_SPI_CTRLR0, tmp);
dw_spi_enable_chip(dws, 1);
@@ -962,6 +1168,11 @@ static void dw_spi_hw_init(struct device *dev, struct dw_spi *dws)
dws->dfs_offset = __bf_shf(DW_PSSI_CTRLR0_DFS32_MASK);
dev_dbg(dev, "Detected 32-bits max data frame size\n");
}
+
+ if (enh_frf == DW_SPI_CTRLR0_ENH_FRF_QUAD) {
+ dws->caps |= DW_SPI_CAP_QUAD;
+ dev_dbg(dev, "Detected quad enhanced SPI mode\n");
+ }
} else {
dws->caps |= DW_SPI_CAP_DFS32;
}
@@ -1008,6 +1219,8 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
host->use_gpio_descriptors = true;
host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP;
+ if (dws->caps & DW_SPI_CAP_QUAD)
+ host->mode_bits |= SPI_TX_QUAD | SPI_RX_QUAD;
if (dws->caps & DW_SPI_CAP_DFS32)
host->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
else
diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c
index e52fa329c2476..819cf470bd8ff 100644
--- a/drivers/spi/spi-dw-dma.c
+++ b/drivers/spi/spi-dw-dma.c
@@ -540,7 +540,7 @@ static int dw_spi_dma_transfer_all(struct dw_spi *dws,
usleep_range(5, 10);
/* Write something to the TX FIFO to start the transfer */
- dw_writel(dws, DW_SPI_DR, 0);
+ dw_spi_start_frame(dws);
}
ret = dw_spi_dma_wait(dws, xfer->len, xfer->effective_speed_hz);
diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h
index 2e7d8527d0a1c..c839ca15d1167 100644
--- a/drivers/spi/spi-dw.h
+++ b/drivers/spi/spi-dw.h
@@ -32,6 +32,7 @@
/* DW SPI controller capabilities */
#define DW_SPI_CAP_CS_OVERRIDE BIT(0)
#define DW_SPI_CAP_DFS32 BIT(1)
+#define DW_SPI_CAP_QUAD BIT(2)
/* Register offsets (Generic for both DWC APB SSI and DWC SSI IP-cores) */
#define DW_SPI_CTRLR0 0x00
@@ -62,6 +63,9 @@
#define DW_SPI_RX_SAMPLE_DLY 0xf0
#define DW_SPI_CS_OVERRIDE 0xf4
+/* SPI_CTRLR0, same address as CS_OVERRIDE (controller has only one of them) */
+#define DW_SPI_ENH_CTRLR0 0xf4
+
/* Bit fields in CTRLR0 (DWC APB SSI) */
#define DW_PSSI_CTRLR0_DFS_MASK GENMASK(3, 0)
#define DW_PSSI_CTRLR0_DFS32_MASK GENMASK(20, 16)
@@ -86,6 +90,11 @@
#define DW_PSSI_CTRLR0_SRL BIT(11)
#define DW_PSSI_CTRLR0_CFS BIT(12)
+#define DW_PSSI_CTRLR0_ENH_FRF_MASK GENMASK(22, 21)
+#define DW_SPI_CTRLR0_ENH_FRF_STD 0x0
+#define DW_SPI_CTRLR0_ENH_FRF_DUAL 0x1
+#define DW_SPI_CTRLR0_ENH_FRF_QUAD 0x2
+
/* Bit fields in CTRLR0 (DWC SSI with AHB interface) */
#define DW_HSSI_CTRLR0_DFS_MASK GENMASK(4, 0)
#define DW_HSSI_CTRLR0_FRF_MASK GENMASK(7, 6)
@@ -121,6 +130,18 @@
#define DW_SPI_DMACR_RDMAE BIT(0)
#define DW_SPI_DMACR_TDMAE BIT(1)
+/* Bit fields in ENH_CTRLR0 */
+#define DW_SPI_ENH_CTRLR0_WAIT_CYCLE_MASK GENMASK(15, 11)
+#define DW_SPI_ENH_CTRLR0_INST_L_MASK GENMASK(9, 8)
+#define DW_SPI_ENH_CTRLR0_INST_L_0BIT 0x0
+#define DW_SPI_ENH_CTRLR0_INST_L_8BIT 0x2
+#define DW_SPI_ENH_CTRLR0_INST_L_16BIT 0x3
+#define DW_SPI_ENH_CTRLR0_ADDR_L_MASK GENMASK(5, 2)
+#define DW_SPI_ENH_CTRLR0_TRANS_TYPE_MASK GENMASK(1, 0)
+#define DW_SPI_ENH_CTRLR0_TRANS_TYPE_TT0 0x0 /* inst std, addr std */
+#define DW_SPI_ENH_CTRLR0_TRANS_TYPE_TT1 0x1 /* inst std, addr enh */
+#define DW_SPI_ENH_CTRLR0_TRANS_TYPE_TT2 0x2 /* inst enh, addr enh */
+
/* Mem/DMA operations helpers */
#define DW_SPI_WAIT_RETRIES 5
#define DW_SPI_BUF_SIZE \
@@ -128,15 +149,40 @@
sizeof_field(struct spi_mem_op, addr.val) + 256)
#define DW_SPI_GET_BYTE(_val, _idx) \
((_val) >> (BITS_PER_BYTE * (_idx)) & 0xff)
+/*
+ * Tx FIFO entries used by the control words. The controller reads INST_L
+ * bits from one entry and ADDR_L bits from the next. DFS does not apply.
+ */
+#define DW_SPI_ENH_CTRL_ENTRIES 2
/* Slave spi_transfer/spi_mem_op related */
struct dw_spi_cfg {
u8 tmode;
u8 dfs;
+ u8 enh_frf; /* CTRLR0 [22:21] */
u32 ndf;
u32 freq;
};
+/* The SPI_CTRLR0 layout of an enhanced frame */
+struct dw_spi_enh_cfg {
+ u8 trans_type; /* SPI_CTRLR0 [1:0] */
+ u8 addr_l; /* SPI_CTRLR0 [5:2] */
+ u8 inst_l; /* SPI_CTRLR0 [9:8] */
+ u8 wait_l; /* SPI_CTRLR0 [15:11] */
+};
+
+/*
+ * Enhanced frame being collected. cfg holds the SPI_CTRLR0 phase lengths.
+ * inst and addr are the two control words for the Tx FIFO.
+ */
+struct dw_spi_enh_frame {
+ struct dw_spi_enh_cfg cfg;
+ u32 inst;
+ u32 addr;
+ u8 ctrl_len; /* control Tx FIFO entries */
+};
+
struct dw_spi;
struct dw_spi_dma_ops {
int (*dma_init)(struct device *dev, struct dw_spi *dws);
@@ -176,6 +222,7 @@ struct dw_spi {
u8 buf[DW_SPI_BUF_SIZE];
int dma_mapped;
u8 n_bytes; /* current is a 1/2 bytes op */
+ struct dw_spi_enh_frame enh; /* Enhanced frame */
irqreturn_t (*transfer_handler)(struct dw_spi *dws);
u32 current_freq; /* frequency in hz */
u32 cur_rx_sample_dly;
@@ -290,8 +337,10 @@ static inline void dw_spi_shutdown_chip(struct dw_spi *dws)
extern void dw_spi_set_cs(struct spi_device *spi, bool enable);
extern void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
- struct dw_spi_cfg *cfg);
+ struct dw_spi_cfg *cfg,
+ struct dw_spi_enh_cfg *ecfg);
extern int dw_spi_check_status(struct dw_spi *dws, bool raw);
+extern void dw_spi_start_frame(struct dw_spi *dws);
extern int dw_spi_add_host(struct device *dev, struct dw_spi *dws);
extern void dw_spi_remove_host(struct dw_spi *dws);
extern int dw_spi_suspend_host(struct dw_spi *dws);