>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, a specific 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.c | 93 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 93 insertions(+)

diff --git a/drivers/spi/spi-aspeed.c b/drivers/spi/spi-aspeed.c
index 9574aff793..e5e348eb7b 100644
--- a/drivers/spi/spi-aspeed.c
+++ b/drivers/spi/spi-aspeed.c
@@ -85,6 +85,8 @@ struct aspeed_spi_info {
 
 static int aspeed_spi_trim_decoded_size(struct udevice *bus,
                                        u32 decoded_sz_arr[]);
+static int aspeed_spi_decoded_range_config(struct udevice *bus,
+                                          u32 decoded_sz_arr[]);
 
 static u32 aspeed_spi_get_io_mode(u32 bus_width)
 {
@@ -509,6 +511,95 @@ 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_plat *plat = dev_get_plat(bus);
+       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 decoded_sz_arr[ASPEED_SPI_MAX_CS];
+       u32 reg_val;
+
+       if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_IN) {
+               for (i = 0; i < priv->num_cs; i++) {
+                       reg_val = readl(plat->ctrl_base +
+                                       REG_CE0_DECODED_ADDR_REG + i * 4);
+                       decoded_sz_arr[i] =
+                               info->segment_end(bus, reg_val) -
+                               info->segment_start(bus, reg_val);
+               }
+
+               decoded_sz_arr[cs] = desc->info.length;
+
+               if (info->adjust_decoded_sz)
+                       info->adjust_decoded_sz(bus, decoded_sz_arr);
+
+               for (i = 0; i < priv->num_cs; i++) {
+                       dev_dbg(dev, "cs: %d, sz: 0x%x\n", i,
+                               decoded_sz_arr[i]);
+               }
+
+               ret = aspeed_spi_decoded_range_config(bus, decoded_sz_arr);
+               if (ret)
+                       return ret;
+
+               reg_val = readl(plat->ctrl_base + REG_CE0_CTRL_REG + cs * 4) &
+                         (~info->cmd_io_ctrl_mask);
+               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,
+                      plat->ctrl_base + REG_CE0_CTRL_REG + cs * 4);
+               priv->flashes[cs].ce_ctrl_read = reg_val;
+
+               dev_dbg(dev, "read bus width: %d [0x%08x]\n",
+                       op_tmpl.data.buswidth, priv->flashes[cs].ce_ctrl_read);
+       } else {
+               /*
+                * 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;
+       }
+
+       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_win_sz < offs + len ||
+           (offs + len) % 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;
@@ -792,6 +883,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

Reply via email to