]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
spi: aspeed: SPI dirmap read support
authorChin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
Fri, 19 Aug 2022 09:01:10 +0000 (17:01 +0800)
committerTom Rini <trini@konsulko.com>
Tue, 13 Sep 2022 16:08:41 +0000 (12:08 -0400)
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

index f858d360232e6ced4987d14577a71dd517ef2891..6099b8525517a3a527c79f8c0564f808b56d3887 100644 (file)
@@ -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 = {