airoha: spi: snfi driver fixes & improvements
authorMikhail Kshevetskiy <[email protected]>
Sat, 4 Oct 2025 06:22:16 +0000 (09:22 +0300)
committerRobert Marko <[email protected]>
Thu, 9 Oct 2025 14:37:25 +0000 (16:37 +0200)
This patch series greatly improve airoha snfi driver and fix a
number of serious bugs.

Fixed bugs:
 * Fix reading/writing of flashes with more than one plane per lun
 * Fill the buffer with 0xff before writing
 * Fix reading of flashes supporting continuous reading mode
 * Fix error paths

Improvements:
 * Add support of dual/quad wires spi modes in exec_op(). This also
   fix flash reading/writing if dirmap can't be created.
 * Support of dualio/quadio flash reading commands

Signed-off-by: Mikhail Kshevetskiy <[email protected]>
Link: https://github.com/openwrt/openwrt/pull/20295
Signed-off-by: Robert Marko <[email protected]>
target/linux/airoha/patches-6.12/029-01-spi-airoha-return-an-error-for-continuous-mode-di.patch [new file with mode: 0644]
target/linux/airoha/patches-6.12/029-02-spi-airoha-remove-unnecessary-restriction-length.patch [new file with mode: 0644]
target/linux/airoha/patches-6.12/029-03-spi-airoha-add-support-of-dual-quad-wires-spi-mod.patch [new file with mode: 0644]
target/linux/airoha/patches-6.12/029-04-spi-airoha-remove-unnecessary-switch-to-non-dma-m.patch [new file with mode: 0644]
target/linux/airoha/patches-6.12/029-05-spi-airoha-switch-back-to-non-dma-mode-in-the-cas.patch [new file with mode: 0644]
target/linux/airoha/patches-6.12/029-06-spi-airoha-fix-reading-writing-of-flashes-with-mo.patch [new file with mode: 0644]
target/linux/airoha/patches-6.12/029-07-spi-airoha-unify-dirmap-read-write-code.patch [new file with mode: 0644]
target/linux/airoha/patches-6.12/029-08-spi-airoha-support-of-dualio-quadio-flash-reading.patch [new file with mode: 0644]
target/linux/airoha/patches-6.12/029-09-spi-airoha-buffer-must-be-0xff-ed-before-writing.patch [new file with mode: 0644]

diff --git a/target/linux/airoha/patches-6.12/029-01-spi-airoha-return-an-error-for-continuous-mode-di.patch b/target/linux/airoha/patches-6.12/029-01-spi-airoha-return-an-error-for-continuous-mode-di.patch
new file mode 100644 (file)
index 0000000..9ee47fc
--- /dev/null
@@ -0,0 +1,33 @@
+From 4aac08add11979d838335ebff0dc42c532f05c98 Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <[email protected]>
+Date: Mon, 4 Aug 2025 21:45:46 +0300
+Subject: [PATCH v6 01/13] spi: airoha: return an error for continuous mode
+ dirmap creation cases
+
+This driver can accelerate single page operations only, thus
+continuous reading mode should not be used.
+
+Continuous reading will use sizes up to the size of one erase block.
+This size is much larger than the size of single flash page. Use this
+difference to identify continuous reading and return an error.
+
+Signed-off-by: Mikhail Kshevetskiy <[email protected]>
+Reviewed-by: Frieder Schrempf <[email protected]>
+Reviewed-by: AngeloGioacchino Del Regno <[email protected]>
+---
+ drivers/spi/spi-airoha-snfi.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/spi/spi-airoha-snfi.c
++++ b/drivers/spi/spi-airoha-snfi.c
+@@ -618,6 +618,10 @@ static int airoha_snand_dirmap_create(st
+       if (desc->info.offset + desc->info.length > U32_MAX)
+               return -EINVAL;
++      /* continuous reading is not supported */
++      if (desc->info.length > SPI_NAND_CACHE_SIZE)
++              return -E2BIG;
++
+       if (!airoha_snand_supports_op(desc->mem, &desc->info.op_tmpl))
+               return -EOPNOTSUPP;
diff --git a/target/linux/airoha/patches-6.12/029-02-spi-airoha-remove-unnecessary-restriction-length.patch b/target/linux/airoha/patches-6.12/029-02-spi-airoha-remove-unnecessary-restriction-length.patch
new file mode 100644 (file)
index 0000000..bbb4121
--- /dev/null
@@ -0,0 +1,31 @@
+From 4658f57ba7f60c3bd8e14c1ca7acf2090aee8436 Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <[email protected]>
+Date: Tue, 12 Aug 2025 06:21:35 +0300
+Subject: [PATCH v6 02/13] spi: airoha: remove unnecessary restriction length
+
+The "length < 160" restriction is not needed because airoha_snand_write_data()
+and airoha_snand_read_data() will properly handle data transfers above
+SPI_MAX_TRANSFER_SIZE.
+
+Signed-off-by: Mikhail Kshevetskiy <[email protected]>
+Reviewed-by: AngeloGioacchino Del Regno <[email protected]>
+---
+ drivers/spi/spi-airoha-snfi.c | 7 -------
+ 1 file changed, 7 deletions(-)
+
+--- a/drivers/spi/spi-airoha-snfi.c
++++ b/drivers/spi/spi-airoha-snfi.c
+@@ -579,13 +579,6 @@ static int airoha_snand_adjust_op_size(s
+               if (op->data.nbytes > max_len)
+                       op->data.nbytes = max_len;
+-      } else {
+-              max_len = 1 + op->addr.nbytes + op->dummy.nbytes;
+-              if (max_len >= 160)
+-                      return -EOPNOTSUPP;
+-
+-              if (op->data.nbytes > 160 - max_len)
+-                      op->data.nbytes = 160 - max_len;
+       }
+       return 0;
diff --git a/target/linux/airoha/patches-6.12/029-03-spi-airoha-add-support-of-dual-quad-wires-spi-mod.patch b/target/linux/airoha/patches-6.12/029-03-spi-airoha-add-support-of-dual-quad-wires-spi-mod.patch
new file mode 100644 (file)
index 0000000..ff54fea
--- /dev/null
@@ -0,0 +1,209 @@
+From 703b10241666b468484a6ec5eb5c7c71fb2463ef Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <[email protected]>
+Date: Sat, 7 Jun 2025 09:09:38 +0300
+Subject: [PATCH v6 03/13] spi: airoha: add support of dual/quad wires spi
+ modes to exec_op() handler
+
+Booting without this patch and disabled dirmap support results in
+
+[    2.980719] spi-nand spi0.0: Micron SPI NAND was found.
+[    2.986040] spi-nand spi0.0: 256 MiB, block size: 128 KiB, page size: 2048, OOB size: 128
+[    2.994709] 2 fixed-partitions partitions found on MTD device spi0.0
+[    3.001075] Creating 2 MTD partitions on "spi0.0":
+[    3.005862] 0x000000000000-0x000000020000 : "bl2"
+[    3.011272] 0x000000020000-0x000010000000 : "ubi"
+...
+[    6.195594] ubi0: attaching mtd1
+[   13.338398] ubi0: scanning is finished
+[   13.342188] ubi0 error: ubi_read_volume_table: the layout volume was not found
+[   13.349784] ubi0 error: ubi_attach_mtd_dev: failed to attach mtd1, error -22
+[   13.356897] UBI error: cannot attach mtd1
+
+If dirmap is disabled or not supported in the spi driver, the dirmap requests
+will be executed via exec_op() handler. Thus, if the hardware supports
+dual/quad spi modes, then corresponding requests will be sent to exec_op()
+handler. Current driver does not support such requests, so error is arrised.
+As result the flash can't be read/write.
+
+This patch adds support of dual and quad wires spi modes to exec_op() handler.
+
+Fixes: a403997c12019 ("spi: airoha: add SPI-NAND Flash controller driver")
+Signed-off-by: Mikhail Kshevetskiy <[email protected]>
+Reviewed-by: AngeloGioacchino Del Regno <[email protected]>
+---
+ drivers/spi/spi-airoha-snfi.c | 108 ++++++++++++++++++++++++++--------
+ 1 file changed, 82 insertions(+), 26 deletions(-)
+
+--- a/drivers/spi/spi-airoha-snfi.c
++++ b/drivers/spi/spi-airoha-snfi.c
+@@ -192,6 +192,14 @@
+ #define SPI_NAND_OP_RESET                     0xff
+ #define SPI_NAND_OP_DIE_SELECT                        0xc2
++/* SNAND FIFO commands */
++#define SNAND_FIFO_TX_BUSWIDTH_SINGLE         0x08
++#define SNAND_FIFO_TX_BUSWIDTH_DUAL           0x09
++#define SNAND_FIFO_TX_BUSWIDTH_QUAD           0x0a
++#define SNAND_FIFO_RX_BUSWIDTH_SINGLE         0x0c
++#define SNAND_FIFO_RX_BUSWIDTH_DUAL           0x0e
++#define SNAND_FIFO_RX_BUSWIDTH_QUAD           0x0f
++
+ #define SPI_NAND_CACHE_SIZE                   (SZ_4K + SZ_256)
+ #define SPI_MAX_TRANSFER_SIZE                 511
+@@ -387,10 +395,26 @@ static int airoha_snand_set_mode(struct
+       return regmap_write(as_ctrl->regmap_ctrl, REG_SPI_CTRL_DUMMY, 0);
+ }
+-static int airoha_snand_write_data(struct airoha_snand_ctrl *as_ctrl, u8 cmd,
+-                                 const u8 *data, int len)
++static int airoha_snand_write_data(struct airoha_snand_ctrl *as_ctrl,
++                                 const u8 *data, int len, int buswidth)
+ {
+       int i, data_len;
++      u8 cmd;
++
++      switch (buswidth) {
++      case 0:
++      case 1:
++              cmd = SNAND_FIFO_TX_BUSWIDTH_SINGLE;
++              break;
++      case 2:
++              cmd = SNAND_FIFO_TX_BUSWIDTH_DUAL;
++              break;
++      case 4:
++              cmd = SNAND_FIFO_TX_BUSWIDTH_QUAD;
++              break;
++      default:
++              return -EINVAL;
++      }
+       for (i = 0; i < len; i += data_len) {
+               int err;
+@@ -409,16 +433,32 @@ static int airoha_snand_write_data(struc
+       return 0;
+ }
+-static int airoha_snand_read_data(struct airoha_snand_ctrl *as_ctrl, u8 *data,
+-                                int len)
++static int airoha_snand_read_data(struct airoha_snand_ctrl *as_ctrl,
++                                u8 *data, int len, int buswidth)
+ {
+       int i, data_len;
++      u8 cmd;
++
++      switch (buswidth) {
++      case 0:
++      case 1:
++              cmd = SNAND_FIFO_RX_BUSWIDTH_SINGLE;
++              break;
++      case 2:
++              cmd = SNAND_FIFO_RX_BUSWIDTH_DUAL;
++              break;
++      case 4:
++              cmd = SNAND_FIFO_RX_BUSWIDTH_QUAD;
++              break;
++      default:
++              return -EINVAL;
++      }
+       for (i = 0; i < len; i += data_len) {
+               int err;
+               data_len = min(len - i, SPI_MAX_TRANSFER_SIZE);
+-              err = airoha_snand_set_fifo_op(as_ctrl, 0xc, data_len);
++              err = airoha_snand_set_fifo_op(as_ctrl, cmd, data_len);
+               if (err)
+                       return err;
+@@ -895,12 +935,28 @@ error_dma_unmap:
+ static int airoha_snand_exec_op(struct spi_mem *mem,
+                               const struct spi_mem_op *op)
+ {
+-      u8 data[8], cmd, opcode = op->cmd.opcode;
+       struct airoha_snand_ctrl *as_ctrl;
++      int op_len, addr_len, dummy_len;
++      u8 buf[20], *data;
+       int i, err;
+       as_ctrl = spi_controller_get_devdata(mem->spi->controller);
++      op_len = op->cmd.nbytes;
++      addr_len = op->addr.nbytes;
++      dummy_len = op->dummy.nbytes;
++
++      if (op_len + dummy_len + addr_len > sizeof(buf))
++              return -EIO;
++
++      data = buf;
++      for (i = 0; i < op_len; i++)
++              *data++ = op->cmd.opcode >> (8 * (op_len - i - 1));
++      for (i = 0; i < addr_len; i++)
++              *data++ = op->addr.val >> (8 * (addr_len - i - 1));
++      for (i = 0; i < dummy_len; i++)
++              *data++ = 0xff;
++
+       /* switch to manual mode */
+       err = airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL);
+       if (err < 0)
+@@ -911,40 +967,40 @@ static int airoha_snand_exec_op(struct s
+               return err;
+       /* opcode */
+-      err = airoha_snand_write_data(as_ctrl, 0x8, &opcode, sizeof(opcode));
++      data = buf;
++      err = airoha_snand_write_data(as_ctrl, data, op_len,
++                                    op->cmd.buswidth);
+       if (err)
+               return err;
+       /* addr part */
+-      cmd = opcode == SPI_NAND_OP_GET_FEATURE ? 0x11 : 0x8;
+-      put_unaligned_be64(op->addr.val, data);
+-
+-      for (i = ARRAY_SIZE(data) - op->addr.nbytes;
+-           i < ARRAY_SIZE(data); i++) {
+-              err = airoha_snand_write_data(as_ctrl, cmd, &data[i],
+-                                            sizeof(data[0]));
++      data += op_len;
++      if (addr_len) {
++              err = airoha_snand_write_data(as_ctrl, data, addr_len,
++                                            op->addr.buswidth);
+               if (err)
+                       return err;
+       }
+       /* dummy */
+-      data[0] = 0xff;
+-      for (i = 0; i < op->dummy.nbytes; i++) {
+-              err = airoha_snand_write_data(as_ctrl, 0x8, &data[0],
+-                                            sizeof(data[0]));
++      data += addr_len;
++      if (dummy_len) {
++              err = airoha_snand_write_data(as_ctrl, data, dummy_len,
++                                            op->dummy.buswidth);
+               if (err)
+                       return err;
+       }
+       /* data */
+-      if (op->data.dir == SPI_MEM_DATA_IN) {
+-              err = airoha_snand_read_data(as_ctrl, op->data.buf.in,
+-                                           op->data.nbytes);
+-              if (err)
+-                      return err;
+-      } else {
+-              err = airoha_snand_write_data(as_ctrl, 0x8, op->data.buf.out,
+-                                            op->data.nbytes);
++      if (op->data.nbytes) {
++              if (op->data.dir == SPI_MEM_DATA_IN)
++                      err = airoha_snand_read_data(as_ctrl, op->data.buf.in,
++                                                   op->data.nbytes,
++                                                   op->data.buswidth);
++              else
++                      err = airoha_snand_write_data(as_ctrl, op->data.buf.out,
++                                                    op->data.nbytes,
++                                                    op->data.buswidth);
+               if (err)
+                       return err;
+       }
diff --git a/target/linux/airoha/patches-6.12/029-04-spi-airoha-remove-unnecessary-switch-to-non-dma-m.patch b/target/linux/airoha/patches-6.12/029-04-spi-airoha-remove-unnecessary-switch-to-non-dma-m.patch
new file mode 100644 (file)
index 0000000..7735d22
--- /dev/null
@@ -0,0 +1,29 @@
+From fb41a3e3bc357592b28a8abb504df99dad642588 Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <[email protected]>
+Date: Mon, 11 Aug 2025 13:09:51 +0300
+Subject: [PATCH v6 04/13] spi: airoha: remove unnecessary switch to non-dma
+ mode
+
+The code switches to dma at the start of dirmap operation and returns
+to non-dma at the end of dirmap operation, so an additional switch to
+non-dma at the start of dirmap write is not required.
+
+Signed-off-by: Mikhail Kshevetskiy <[email protected]>
+Acked-by: Lorenzo Bianconi <[email protected]>
+Reviewed-by: AngeloGioacchino Del Regno <[email protected]>
+---
+ drivers/spi/spi-airoha-snfi.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+--- a/drivers/spi/spi-airoha-snfi.c
++++ b/drivers/spi/spi-airoha-snfi.c
+@@ -812,9 +812,6 @@ static ssize_t airoha_snand_dirmap_write
+       int err;
+       as_ctrl = spi_controller_get_devdata(spi->controller);
+-      err = airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL);
+-      if (err < 0)
+-              return err;
+       memcpy(txrx_buf + offs, buf, len);
+       dma_addr = dma_map_single(as_ctrl->dev, txrx_buf, SPI_NAND_CACHE_SIZE,
diff --git a/target/linux/airoha/patches-6.12/029-05-spi-airoha-switch-back-to-non-dma-mode-in-the-cas.patch b/target/linux/airoha/patches-6.12/029-05-spi-airoha-switch-back-to-non-dma-mode-in-the-cas.patch
new file mode 100644 (file)
index 0000000..39e759f
--- /dev/null
@@ -0,0 +1,54 @@
+From 711584484d76448763959ed4e103895d9dcc7438 Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <[email protected]>
+Date: Mon, 11 Aug 2025 20:24:42 +0300
+Subject: [PATCH v6 05/13] spi: airoha: switch back to non-dma mode in the case
+ of error
+
+Current dirmap code does not switch back to non-dma mode in the case of
+error. This is wrong.
+
+This patch fixes dirmap read/write error path.
+
+Fixes: a403997c12019 ("spi: airoha: add SPI-NAND Flash controller driver")
+Signed-off-by: Mikhail Kshevetskiy <[email protected]>
+Acked-by: Lorenzo Bianconi <[email protected]>
+Reviewed-by: AngeloGioacchino Del Regno <[email protected]>
+---
+ drivers/spi/spi-airoha-snfi.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+--- a/drivers/spi/spi-airoha-snfi.c
++++ b/drivers/spi/spi-airoha-snfi.c
+@@ -691,13 +691,13 @@ static ssize_t airoha_snand_dirmap_read(
+       err = airoha_snand_nfi_config(as_ctrl);
+       if (err)
+-              return err;
++              goto error_dma_mode_off;
+       dma_addr = dma_map_single(as_ctrl->dev, txrx_buf, SPI_NAND_CACHE_SIZE,
+                                 DMA_FROM_DEVICE);
+       err = dma_mapping_error(as_ctrl->dev, dma_addr);
+       if (err)
+-              return err;
++              goto error_dma_mode_off;
+       /* set dma addr */
+       err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_STRADDR,
+@@ -797,6 +797,8 @@ static ssize_t airoha_snand_dirmap_read(
+ error_dma_unmap:
+       dma_unmap_single(as_ctrl->dev, dma_addr, SPI_NAND_CACHE_SIZE,
+                        DMA_FROM_DEVICE);
++error_dma_mode_off:
++      airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL);
+       return err;
+ }
+@@ -926,6 +928,7 @@ static ssize_t airoha_snand_dirmap_write
+ error_dma_unmap:
+       dma_unmap_single(as_ctrl->dev, dma_addr, SPI_NAND_CACHE_SIZE,
+                        DMA_TO_DEVICE);
++      airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL);
+       return err;
+ }
diff --git a/target/linux/airoha/patches-6.12/029-06-spi-airoha-fix-reading-writing-of-flashes-with-mo.patch b/target/linux/airoha/patches-6.12/029-06-spi-airoha-fix-reading-writing-of-flashes-with-mo.patch
new file mode 100644 (file)
index 0000000..24515c8
--- /dev/null
@@ -0,0 +1,102 @@
+From d8a0a67bf75c4cf2a760b6fa0002b0baff6e8b20 Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <[email protected]>
+Date: Mon, 11 Aug 2025 20:32:40 +0300
+Subject: [PATCH v6 06/13] spi: airoha: fix reading/writing of flashes with
+ more than one plane per lun
+
+Attaching UBI on the flash with more than one plane per lun will lead to
+the following error:
+
+[    2.980989] spi-nand spi0.0: Micron SPI NAND was found.
+[    2.986309] spi-nand spi0.0: 256 MiB, block size: 128 KiB, page size: 2048, OOB size: 128
+[    2.994978] 2 fixed-partitions partitions found on MTD device spi0.0
+[    3.001350] Creating 2 MTD partitions on "spi0.0":
+[    3.006159] 0x000000000000-0x000000020000 : "bl2"
+[    3.011663] 0x000000020000-0x000010000000 : "ubi"
+...
+[    6.391748] ubi0: attaching mtd1
+[    6.412545] ubi0 error: ubi_attach: PEB 0 contains corrupted VID header, and the data does not contain all 0xFF
+[    6.422677] ubi0 error: ubi_attach: this may be a non-UBI PEB or a severe VID header corruption which requires manual inspection
+[    6.434249] Volume identifier header dump:
+[    6.438349]     magic     55424923
+[    6.441482]     version   1
+[    6.444007]     vol_type  0
+[    6.446539]     copy_flag 0
+[    6.449068]     compat    0
+[    6.451594]     vol_id    0
+[    6.454120]     lnum      1
+[    6.456651]     data_size 4096
+[    6.459442]     used_ebs  1061644134
+[    6.462748]     data_pad  0
+[    6.465274]     sqnum     0
+[    6.467805]     hdr_crc   61169820
+[    6.470943] Volume identifier header hexdump:
+[    6.475308] hexdump of PEB 0 offset 4096, length 126976
+[    6.507391] ubi0 warning: ubi_attach: valid VID header but corrupted EC header at PEB 4
+[    6.515415] ubi0 error: ubi_compare_lebs: unsupported on-flash UBI format
+[    6.522222] ubi0 error: ubi_attach_mtd_dev: failed to attach mtd1, error -22
+[    6.529294] UBI error: cannot attach mtd1
+
+Non dirmap reading works good. Looking to spi_mem_no_dirmap_read() code we'll see:
+
+       static ssize_t spi_mem_no_dirmap_read(struct spi_mem_dirmap_desc *desc,
+                                             u64 offs, size_t len, void *buf)
+       {
+               struct spi_mem_op op = desc->info.op_tmpl;
+               int ret;
+
+// --- see here ---
+               op.addr.val = desc->info.offset + offs;
+//-----------------
+               op.data.buf.in = buf;
+               op.data.nbytes = len;
+               ret = spi_mem_adjust_op_size(desc->mem, &op);
+               if (ret)
+               return ret;
+
+               ret = spi_mem_exec_op(desc->mem, &op);
+               if (ret)
+                       return ret;
+
+               return op.data.nbytes;
+       }
+
+The similar happens for spi_mem_no_dirmap_write(). Thus the address
+passed to the flash should take in the account the value of
+desc->info.offset.
+
+This patch fix dirmap reading/writing of flashes with more than one
+plane per lun.
+
+Fixes: a403997c12019 ("spi: airoha: add SPI-NAND Flash controller driver")
+Signed-off-by: Mikhail Kshevetskiy <[email protected]>
+Reviewed-by: AngeloGioacchino Del Regno <[email protected]>
+---
+ drivers/spi/spi-airoha-snfi.c | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+--- a/drivers/spi/spi-airoha-snfi.c
++++ b/drivers/spi/spi-airoha-snfi.c
+@@ -726,8 +726,9 @@ static ssize_t airoha_snand_dirmap_read(
+       if (err)
+               goto error_dma_unmap;
+-      /* set read addr */
+-      err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_RD_CTL3, 0x0);
++      /* set read addr: zero page offset + descriptor read offset */
++      err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_RD_CTL3,
++                         desc->info.offset);
+       if (err)
+               goto error_dma_unmap;
+@@ -860,7 +861,9 @@ static ssize_t airoha_snand_dirmap_write
+       if (err)
+               goto error_dma_unmap;
+-      err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_PG_CTL2, 0x0);
++      /* set write addr: zero page offset + descriptor write offset */
++      err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_PG_CTL2,
++                         desc->info.offset);
+       if (err)
+               goto error_dma_unmap;
diff --git a/target/linux/airoha/patches-6.12/029-07-spi-airoha-unify-dirmap-read-write-code.patch b/target/linux/airoha/patches-6.12/029-07-spi-airoha-unify-dirmap-read-write-code.patch
new file mode 100644 (file)
index 0000000..4be20c5
--- /dev/null
@@ -0,0 +1,135 @@
+From 995b1a65206ee28d5403db0518cb230f2ce429ef Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <[email protected]>
+Date: Mon, 11 Aug 2025 19:57:43 +0300
+Subject: [PATCH v6 07/13] spi: airoha: unify dirmap read/write code
+
+Makes dirmap writing looks similar to dirmap reading. Just a minor
+refactoring, no behavior change is expected.
+
+Signed-off-by: Mikhail Kshevetskiy <[email protected]>
+---
+ drivers/spi/spi-airoha-snfi.c | 50 ++++++++++++++++++++++-------------
+ 1 file changed, 32 insertions(+), 18 deletions(-)
+
+--- a/drivers/spi/spi-airoha-snfi.c
++++ b/drivers/spi/spi-airoha-snfi.c
+@@ -672,6 +672,8 @@ static ssize_t airoha_snand_dirmap_read(
+       u32 val, rd_mode;
+       int err;
++      as_ctrl = spi_controller_get_devdata(spi->controller);
++
+       switch (op->cmd.opcode) {
+       case SPI_NAND_OP_READ_FROM_CACHE_DUAL:
+               rd_mode = 1;
+@@ -684,7 +686,6 @@ static ssize_t airoha_snand_dirmap_read(
+               break;
+       }
+-      as_ctrl = spi_controller_get_devdata(spi->controller);
+       err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA);
+       if (err < 0)
+               return err;
+@@ -748,7 +749,7 @@ static ssize_t airoha_snand_dirmap_read(
+       if (err)
+               goto error_dma_unmap;
+-      /* trigger dma start read */
++      /* trigger dma reading */
+       err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
+                               SPI_NFI_RD_TRIG);
+       if (err)
+@@ -806,37 +807,47 @@ error_dma_mode_off:
+ static ssize_t airoha_snand_dirmap_write(struct spi_mem_dirmap_desc *desc,
+                                        u64 offs, size_t len, const void *buf)
+ {
+-      struct spi_mem_op *op = &desc->info.op_tmpl;
+       struct spi_device *spi = desc->mem->spi;
+       u8 *txrx_buf = spi_get_ctldata(spi);
+       struct airoha_snand_ctrl *as_ctrl;
+       dma_addr_t dma_addr;
+-      u32 wr_mode, val;
++      u32 wr_mode, val, opcode;
+       int err;
+       as_ctrl = spi_controller_get_devdata(spi->controller);
++      opcode = desc->info.op_tmpl.cmd.opcode;
++      switch (opcode) {
++      case SPI_NAND_OP_PROGRAM_LOAD_SINGLE:
++      case SPI_NAND_OP_PROGRAM_LOAD_RAMDOM_SINGLE:
++              wr_mode = 0;
++              break;
++      case SPI_NAND_OP_PROGRAM_LOAD_QUAD:
++      case SPI_NAND_OP_PROGRAM_LOAD_RAMDON_QUAD:
++              wr_mode = 2;
++              break;
++      default:
++              /* unknown opcode */
++              return -EOPNOTSUPP;
++      }
++
+       memcpy(txrx_buf + offs, buf, len);
+-      dma_addr = dma_map_single(as_ctrl->dev, txrx_buf, SPI_NAND_CACHE_SIZE,
+-                                DMA_TO_DEVICE);
+-      err = dma_mapping_error(as_ctrl->dev, dma_addr);
+-      if (err)
+-              return err;
+       err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA);
+       if (err < 0)
+-              goto error_dma_unmap;
++              return err;
+       err = airoha_snand_nfi_config(as_ctrl);
+       if (err)
+-              goto error_dma_unmap;
++              goto error_dma_mode_off;
+-      if (op->cmd.opcode == SPI_NAND_OP_PROGRAM_LOAD_QUAD ||
+-          op->cmd.opcode == SPI_NAND_OP_PROGRAM_LOAD_RAMDON_QUAD)
+-              wr_mode = BIT(1);
+-      else
+-              wr_mode = 0;
++      dma_addr = dma_map_single(as_ctrl->dev, txrx_buf, SPI_NAND_CACHE_SIZE,
++                                DMA_TO_DEVICE);
++      err = dma_mapping_error(as_ctrl->dev, dma_addr);
++      if (err)
++              goto error_dma_mode_off;
++      /* set dma addr */
+       err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_STRADDR,
+                          dma_addr);
+       if (err)
+@@ -850,12 +861,13 @@ static ssize_t airoha_snand_dirmap_write
+       if (err)
+               goto error_dma_unmap;
++      /* set write command */
+       err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_PG_CTL1,
+-                         FIELD_PREP(SPI_NFI_PG_LOAD_CMD,
+-                                    op->cmd.opcode));
++                         FIELD_PREP(SPI_NFI_PG_LOAD_CMD, opcode));
+       if (err)
+               goto error_dma_unmap;
++      /* set write mode */
+       err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_MISC_CTL,
+                          FIELD_PREP(SPI_NFI_DATA_READ_WR_MODE, wr_mode));
+       if (err)
+@@ -887,6 +899,7 @@ static ssize_t airoha_snand_dirmap_write
+       if (err)
+               goto error_dma_unmap;
++      /* trigger dma writing */
+       err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
+                               SPI_NFI_WR_TRIG);
+       if (err)
+@@ -931,6 +944,7 @@ static ssize_t airoha_snand_dirmap_write
+ error_dma_unmap:
+       dma_unmap_single(as_ctrl->dev, dma_addr, SPI_NAND_CACHE_SIZE,
+                        DMA_TO_DEVICE);
++error_dma_mode_off:
+       airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL);
+       return err;
+ }
diff --git a/target/linux/airoha/patches-6.12/029-08-spi-airoha-support-of-dualio-quadio-flash-reading.patch b/target/linux/airoha/patches-6.12/029-08-spi-airoha-support-of-dualio-quadio-flash-reading.patch
new file mode 100644 (file)
index 0000000..4e00e7c
--- /dev/null
@@ -0,0 +1,92 @@
+From baaba9b8d3d907575323cbb7fabeae23db2a542b Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <[email protected]>
+Date: Mon, 11 Aug 2025 20:52:34 +0300
+Subject: [PATCH v6 08/13] spi: airoha: support of dualio/quadio flash reading
+ commands
+
+Airoha snfi spi controller supports acceleration of DUAL/QUAD
+operations, but does not supports DUAL_IO/QUAD_IO operations.
+Luckily DUAL/QUAD operations do the same as DUAL_IO/QUAD_IO ones,
+so we can issue corresponding DUAL/QUAD operation instead of
+DUAL_IO/QUAD_IO one.
+
+Signed-off-by: Mikhail Kshevetskiy <[email protected]>
+Reviewed-by: AngeloGioacchino Del Regno <[email protected]>
+---
+ drivers/spi/spi-airoha-snfi.c | 28 ++++++++++++++++++++++------
+ 1 file changed, 22 insertions(+), 6 deletions(-)
+
+--- a/drivers/spi/spi-airoha-snfi.c
++++ b/drivers/spi/spi-airoha-snfi.c
+@@ -147,6 +147,8 @@
+ #define SPI_NFI_CUS_SEC_SIZE_EN                       BIT(16)
+ #define REG_SPI_NFI_RD_CTL2                   0x0510
++#define SPI_NFI_DATA_READ_CMD                 GENMASK(7, 0)
++
+ #define REG_SPI_NFI_RD_CTL3                   0x0514
+ #define REG_SPI_NFI_PG_CTL1                   0x0524
+@@ -179,7 +181,9 @@
+ #define SPI_NAND_OP_READ_FROM_CACHE_SINGLE    0x03
+ #define SPI_NAND_OP_READ_FROM_CACHE_SINGLE_FAST       0x0b
+ #define SPI_NAND_OP_READ_FROM_CACHE_DUAL      0x3b
++#define SPI_NAND_OP_READ_FROM_CACHE_DUALIO    0xbb
+ #define SPI_NAND_OP_READ_FROM_CACHE_QUAD      0x6b
++#define SPI_NAND_OP_READ_FROM_CACHE_QUADIO    0xeb
+ #define SPI_NAND_OP_WRITE_ENABLE              0x06
+ #define SPI_NAND_OP_WRITE_DISABLE             0x04
+ #define SPI_NAND_OP_PROGRAM_LOAD_SINGLE               0x02
+@@ -664,26 +668,38 @@ static int airoha_snand_dirmap_create(st
+ static ssize_t airoha_snand_dirmap_read(struct spi_mem_dirmap_desc *desc,
+                                       u64 offs, size_t len, void *buf)
+ {
+-      struct spi_mem_op *op = &desc->info.op_tmpl;
+       struct spi_device *spi = desc->mem->spi;
+       struct airoha_snand_ctrl *as_ctrl;
+       u8 *txrx_buf = spi_get_ctldata(spi);
+       dma_addr_t dma_addr;
+-      u32 val, rd_mode;
++      u32 val, rd_mode, opcode;
+       int err;
+       as_ctrl = spi_controller_get_devdata(spi->controller);
+-      switch (op->cmd.opcode) {
++      /*
++       * DUALIO and QUADIO opcodes are not supported by the spi controller,
++       * replace them with supported opcodes.
++       */
++      opcode = desc->info.op_tmpl.cmd.opcode;
++      switch (opcode) {
++      case SPI_NAND_OP_READ_FROM_CACHE_SINGLE:
++      case SPI_NAND_OP_READ_FROM_CACHE_SINGLE_FAST:
++              rd_mode = 0;
++              break;
+       case SPI_NAND_OP_READ_FROM_CACHE_DUAL:
++      case SPI_NAND_OP_READ_FROM_CACHE_DUALIO:
++              opcode = SPI_NAND_OP_READ_FROM_CACHE_DUAL;
+               rd_mode = 1;
+               break;
+       case SPI_NAND_OP_READ_FROM_CACHE_QUAD:
++      case SPI_NAND_OP_READ_FROM_CACHE_QUADIO:
++              opcode = SPI_NAND_OP_READ_FROM_CACHE_QUAD;
+               rd_mode = 2;
+               break;
+       default:
+-              rd_mode = 0;
+-              break;
++              /* unknown opcode */
++              return -EOPNOTSUPP;
+       }
+       err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA);
+@@ -717,7 +733,7 @@ static ssize_t airoha_snand_dirmap_read(
+       /* set read command */
+       err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_RD_CTL2,
+-                         op->cmd.opcode);
++                         FIELD_PREP(SPI_NFI_DATA_READ_CMD, opcode));
+       if (err)
+               goto error_dma_unmap;
diff --git a/target/linux/airoha/patches-6.12/029-09-spi-airoha-buffer-must-be-0xff-ed-before-writing.patch b/target/linux/airoha/patches-6.12/029-09-spi-airoha-buffer-must-be-0xff-ed-before-writing.patch
new file mode 100644 (file)
index 0000000..7eef83d
--- /dev/null
@@ -0,0 +1,29 @@
+From 6ca9cd453cb5d8a6411791295771b4dbd1c623de Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <[email protected]>
+Date: Mon, 11 Aug 2025 21:18:04 +0300
+Subject: [PATCH v6 09/13] spi: airoha: buffer must be 0xff-ed before writing
+
+During writing, the entire flash page (including OOB) will be updated
+with the values from the temporary buffer, so we need to fill the
+untouched areas of the buffer with 0xff value to prevent accidental
+data overwriting.
+
+Signed-off-by: Mikhail Kshevetskiy <[email protected]>
+---
+ drivers/spi/spi-airoha-snfi.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/spi/spi-airoha-snfi.c
++++ b/drivers/spi/spi-airoha-snfi.c
+@@ -847,7 +847,11 @@ static ssize_t airoha_snand_dirmap_write
+               return -EOPNOTSUPP;
+       }
++      if (offs > 0)
++              memset(txrx_buf, 0xff, offs);
+       memcpy(txrx_buf + offs, buf, len);
++      if (bytes > offs + len)
++              memset(txrx_buf + offs + len, 0xff, bytes - offs - len);
+       err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA);
+       if (err < 0)