diff mbox series

[v5,09/14] spi: aspeed: SPI dirmap read support

Message ID 20220819090115.1854805-10-chin-ting_kuo@aspeedtech.com
State Accepted
Commit 992d02ea737895dc67246bbf7065084bb2721d6b
Delegated to: Tom Rini
Headers show
Series Add ASPEED SPI controller driver | expand

Commit Message

Chin-Ting Kuo Aug. 19, 2022, 9:01 a.m. UTC
From the HW point of view, the performance of
command read mode is greater than user mode slightly.
Thus, dirmap read framework is introduced to achieve
this goal. In dirmap_create, command read mode is
configured. Usually, the decoded address area with flash
size is assigned to each CS. CPU can thus access the
SPI flash as normal memory in dirmap_read function.

Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
---
 drivers/spi/spi-aspeed-smc.c | 88 ++++++++++++++++++++++++++++++++++++
 1 file changed, 88 insertions(+)
diff mbox series

Patch

diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c
index f858d36023..6099b85255 100644
--- a/drivers/spi/spi-aspeed-smc.c
+++ b/drivers/spi/spi-aspeed-smc.c
@@ -87,6 +87,7 @@  struct aspeed_spi_info {
 };
 
 static const struct aspeed_spi_info ast2400_spi_info;
+static int aspeed_spi_decoded_range_config(struct udevice *bus);
 
 static u32 aspeed_spi_get_io_mode(u32 bus_width)
 {
@@ -381,6 +382,91 @@  static int aspeed_spi_exec_op_user_mode(struct spi_slave *slave,
 	return 0;
 }
 
+static int aspeed_spi_dirmap_create(struct spi_mem_dirmap_desc *desc)
+{
+	int ret = 0;
+	struct udevice *dev = desc->slave->dev;
+	struct udevice *bus = dev->parent;
+	struct aspeed_spi_priv *priv = dev_get_priv(bus);
+	struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
+	const struct aspeed_spi_info *info = priv->info;
+	struct spi_mem_op op_tmpl = desc->info.op_tmpl;
+	u32 i;
+	u32 cs = slave_plat->cs;
+	u32 reg_val;
+	u32 ce_ctrl_reg;
+
+	if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT) {
+		/*
+		 * dirmap_write is not supported currently due to a HW
+		 * limitation for command write mode: The written data
+		 * length should be multiple of 4-byte.
+		 */
+		return -EOPNOTSUPP;
+	}
+
+	ce_ctrl_reg = (u32)&priv->regs->ce_ctrl[cs];
+	if (info == &ast2400_spi_info)
+		ce_ctrl_reg = (u32)&priv->regs->ctrl;
+
+	if (desc->info.length > 0x1000000)
+		priv->info->set_4byte(bus, cs);
+
+	/* AST2400 SPI1 doesn't have decoded address segment register. */
+	if (info != &ast2400_spi_info) {
+		priv->flashes[cs].ahb_decoded_sz = desc->info.length;
+
+		for (i = 0; i < priv->num_cs; i++) {
+			dev_dbg(dev, "cs: %d, sz: 0x%x\n", i,
+				priv->flashes[cs].ahb_decoded_sz);
+		}
+
+		ret = aspeed_spi_decoded_range_config(bus);
+		if (ret)
+			return ret;
+	}
+
+	reg_val = aspeed_spi_get_io_mode(op_tmpl.data.buswidth) |
+		  op_tmpl.cmd.opcode << 16 |
+		  ((op_tmpl.dummy.nbytes) & 0x3) << 6 |
+		  ((op_tmpl.dummy.nbytes) & 0x4) << 14 |
+		  CTRL_IO_MODE_CMD_READ;
+
+	writel(reg_val, ce_ctrl_reg);
+
+	priv->flashes[cs].ce_ctrl_read = reg_val;
+
+	dev_dbg(dev, "read bus width: %d ce_ctrl_val: 0x%08x\n",
+		op_tmpl.data.buswidth, priv->flashes[cs].ce_ctrl_read);
+
+	return ret;
+}
+
+static ssize_t aspeed_spi_dirmap_read(struct spi_mem_dirmap_desc *desc,
+				      u64 offs, size_t len, void *buf)
+{
+	struct udevice *dev = desc->slave->dev;
+	struct aspeed_spi_priv *priv = dev_get_priv(dev->parent);
+	struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
+	u32 cs = slave_plat->cs;
+	int ret;
+
+	dev_dbg(dev, "read op:0x%x, addr:0x%llx, len:0x%x\n",
+		desc->info.op_tmpl.cmd.opcode, offs, len);
+
+	if (priv->flashes[cs].ahb_decoded_sz < offs + len ||
+	    (offs % 4) != 0) {
+		ret = aspeed_spi_exec_op_user_mode(desc->slave,
+						   &desc->info.op_tmpl);
+		if (ret != 0)
+			return 0;
+	} else {
+		memcpy_fromio(buf, priv->flashes[cs].ahb_base + offs, len);
+	}
+
+	return len;
+}
+
 static struct aspeed_spi_flash *aspeed_spi_get_flash(struct udevice *dev)
 {
 	struct udevice *bus = dev->parent;
@@ -662,6 +748,8 @@  static int aspeed_spi_probe(struct udevice *bus)
 static const struct spi_controller_mem_ops aspeed_spi_mem_ops = {
 	.supports_op = aspeed_spi_supports_op,
 	.exec_op = aspeed_spi_exec_op_user_mode,
+	.dirmap_create = aspeed_spi_dirmap_create,
+	.dirmap_read = aspeed_spi_dirmap_read,
 };
 
 static const struct dm_spi_ops aspeed_spi_ops = {