>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_...@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