[v5 09/14] spi: aspeed: SPI dirmap read support
Chin-Ting Kuo
chin-ting_kuo at aspeedtech.com
Fri Aug 19 11:01:10 CEST 2022
>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 at aspeedtech.com>
---
drivers/spi/spi-aspeed-smc.c | 88 ++++++++++++++++++++++++++++++++++++
1 file changed, 88 insertions(+)
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 = {
--
2.25.1
More information about the U-Boot
mailing list