[RFC PATCH 11/13] spi: dw: Add support for DIRMAP

Sean Anderson seanga2 at gmail.com
Fri Feb 5 05:39:21 CET 2021


This adds support for XIP mode. It is not actually any faster than QPI
(yet), but it serves as a good starting point for using XIP mode for other
purposes (such as actual eXecuting In Place).

Signed-off-by: Sean Anderson <seanga2 at gmail.com>
---

 drivers/spi/designware_spi.c | 131 +++++++++++++++++++++++++++++++++++
 1 file changed, 131 insertions(+)

diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c
index 6f74a471e3..cb7a28c3bf 100644
--- a/drivers/spi/designware_spi.c
+++ b/drivers/spi/designware_spi.c
@@ -1056,10 +1056,141 @@ static int dw_spi_adjust_op_size(struct spi_slave *slave, struct spi_mem_op *op)
 	return 0;
 }
 
+#if CONFIG_IS_ENABLED(SPI_DIRMAP)
+static int dw_spi_dirmap_create(struct spi_mem_dirmap_desc *desc)
+{
+	struct dw_spi_priv *priv = dev_get_priv(desc->slave->dev->parent);
+
+	/*
+	 * Currently only DWC XIP is supported. DW APB SSI XIP exists, but
+	 * cannot send an instruction before the address, so it is left for when
+	 * U-Boot supports 0-X-X instructions. In addition, we only support
+	 * concurrent XIP (since I have no non-condcurrent XIP hardware to test
+	 * with)
+	 */
+	if (!(priv->caps & (DW_SPI_CAP_XIP)) ||
+	    !(priv->caps & (DW_SPI_CAP_DWC_SSI)) ||
+	    !(priv->caps & (DW_SPI_CAP_XIP_CONCURRENT)))
+		return -ENOTSUPP;
+
+	if (!spi_mem_supports_op(desc->slave, &desc->info.op_tmpl))
+		return -ENOTSUPP;
+
+	/*
+	 * Make sure the requested region doesn't go out of the physically
+	 * mapped flash memory bounds and the operation is read-only.
+	 */
+	if (desc->info.offset + desc->info.length > priv->regs_size ||
+	    desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN)
+		return -ENOTSUPP;
+
+	/* XIP only supports enhanced SPI modes */
+	if (desc->info.op_tmpl.data.buswidth == 1)
+		return -ENOTSUPP;
+
+	return 0;
+}
+
+static u32 dw_spi_update_xip_cr(const struct spi_mem_op *op, uint frf)
+{
+	uint trans_type, wait_cycles;
+
+	/* This assumes support_op has filtered invalid types */
+	if (op->addr.buswidth == 1)
+		trans_type = TRANS_TYPE_1_1_X;
+	else if (op->cmd.buswidth == 1)
+		trans_type = TRANS_TYPE_1_X_X;
+	else
+		trans_type = TRANS_TYPE_X_X_X;
+
+	if (op->dummy.buswidth)
+		wait_cycles = op->dummy.nbytes * 8 / op->dummy.buswidth;
+	else
+		wait_cycles = 0;
+
+	return FIELD_PREP(XIP_CTRL_FRF, frf)
+	       | FIELD_PREP(XIP_CTRL_TRANS_TYPE_MASK, trans_type)
+	       | FIELD_PREP(XIP_CTRL_ADDR_L_MASK, op->addr.nbytes * 2)
+	       | FIELD_PREP(XIP_CTRL_INST_L_MASK, INST_L_8)
+	       | FIELD_PREP(XIP_CTRL_WAIT_CYCLES_MASK, wait_cycles)
+	       //| XIP_CTRL_DFS_HC
+	       | XIP_CTRL_INST_EN
+	       | XIP_CTRL_CONT_XFER_EN
+	       | XIP_CTRL_PREFETCH_EN;
+}
+
+static ssize_t dw_spi_dirmap_read(struct spi_mem_dirmap_desc *desc, u64 offs,
+				  size_t len, void *buf)
+{
+	int ret;
+	size_t count = len;
+	struct spi_slave *slave = desc->slave;
+	struct udevice *bus = slave->dev->parent;
+	struct dw_spi_priv *priv = dev_get_priv(bus);
+	struct spi_mem_op *op = &desc->info.op_tmpl;
+	u8 *from, *to;
+
+	switch (op->data.buswidth) {
+	case 2:
+		priv->spi_frf = CTRLR0_SPI_FRF_DUAL;
+		break;
+	case 4:
+		priv->spi_frf = CTRLR0_SPI_FRF_QUAD;
+		break;
+	case 8:
+		priv->spi_frf = CTRLR0_SPI_FRF_OCTAL;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = dw_spi_mux_ctrl(bus);
+	if (ret)
+		return ret;
+
+	dw_write(priv, DW_SPI_SSIENR, 0);
+	//dw_write(priv, DW_SPI_CTRLR0, dw_spi_update_cr0(priv));
+	dw_write(priv, DW_SPI_XIP_CTRL, dw_spi_update_xip_cr(op, priv->spi_frf));
+	dw_write(priv, DW_SPI_XIP_INCR_INST, op->cmd.opcode);
+	/*
+	 * FIXME: U-Boot doesn't currently support wrap instructions, but we
+	 * can't control what the AHB master does. Just write 0 to get something
+	 * obviously bogus.
+	 */
+	dw_write(priv, DW_SPI_XIP_WRAP_INST, 0);
+	dw_write(priv, DW_SPI_XIP_SER, 1 << spi_chip_select(slave->dev));
+	dw_write(priv, DW_SPI_SSIENR, 1);
+
+	dw_spi_mux_deselect(bus);
+
+	external_cs_manage(slave->dev, true);
+
+	ret = dw_spi_mux_xip(bus);
+	if (ret)
+		return ret;
+
+	//memcpy(buf, priv->regs + offs, len);
+	from = priv->regs + offs;
+	to = buf;
+	while (count--)
+		*to++ = *from++;
+
+	dw_spi_mux_deselect(bus);
+
+	external_cs_manage(slave->dev, false);
+
+	return len;
+}
+#endif /* CONFIG_SPI_DIRMAP */
+
 static const struct spi_controller_mem_ops dw_spi_mem_ops = {
 	.exec_op = dw_spi_exec_op,
 	.supports_op = dw_spi_supports_op,
 	.adjust_op_size = dw_spi_adjust_op_size,
+#if CONFIG_IS_ENABLED(SPI_DIRMAP)
+	.dirmap_create = dw_spi_dirmap_create,
+	.dirmap_read = dw_spi_dirmap_read,
+#endif
 };
 
 static int dw_spi_set_speed(struct udevice *bus, uint speed)
-- 
2.29.2



More information about the U-Boot mailing list