[openwrt/openwrt] airoha: spi: snfi driver fixes & improvements
LEDE Commits
lede-commits at lists.infradead.org
Thu Oct 9 07:37:35 PDT 2025
robimarko pushed a commit to openwrt/openwrt.git, branch main:
https://git.openwrt.org/5ff0e7093081dcb255b94f1f7714ca5f6c08be1a
commit 5ff0e7093081dcb255b94f1f7714ca5f6c08be1a
Author: Mikhail Kshevetskiy <mikhail.kshevetskiy at iopsys.eu>
AuthorDate: Sat Oct 4 09:22:16 2025 +0300
airoha: spi: snfi driver fixes & improvements
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 <mikhail.kshevetskiy at iopsys.eu>
Link: https://github.com/openwrt/openwrt/pull/20295
Signed-off-by: Robert Marko <robimarko at gmail.com>
---
...ha-return-an-error-for-continuous-mode-di.patch | 33 ++++
...oha-remove-unnecessary-restriction-length.patch | 31 +++
...ha-add-support-of-dual-quad-wires-spi-mod.patch | 209 +++++++++++++++++++++
...ha-remove-unnecessary-switch-to-non-dma-m.patch | 29 +++
...ha-switch-back-to-non-dma-mode-in-the-cas.patch | 54 ++++++
...ha-fix-reading-writing-of-flashes-with-mo.patch | 102 ++++++++++
...7-spi-airoha-unify-dirmap-read-write-code.patch | 135 +++++++++++++
...ha-support-of-dualio-quadio-flash-reading.patch | 92 +++++++++
...oha-buffer-must-be-0xff-ed-before-writing.patch | 29 +++
9 files changed, 714 insertions(+)
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
index 0000000000..9ee47fcbe5
--- /dev/null
+++ b/target/linux/airoha/patches-6.12/029-01-spi-airoha-return-an-error-for-continuous-mode-di.patch
@@ -0,0 +1,33 @@
+From 4aac08add11979d838335ebff0dc42c532f05c98 Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <mikhail.kshevetskiy at iopsys.eu>
+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 <mikhail.kshevetskiy at iopsys.eu>
+Reviewed-by: Frieder Schrempf <frieder.schrempf at kontron.de>
+Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno at collabora.com>
+---
+ 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
index 0000000000..bbb4121282
--- /dev/null
+++ b/target/linux/airoha/patches-6.12/029-02-spi-airoha-remove-unnecessary-restriction-length.patch
@@ -0,0 +1,31 @@
+From 4658f57ba7f60c3bd8e14c1ca7acf2090aee8436 Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <mikhail.kshevetskiy at iopsys.eu>
+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 <mikhail.kshevetskiy at iopsys.eu>
+Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno at collabora.com>
+---
+ 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
index 0000000000..ff54feab79
--- /dev/null
+++ b/target/linux/airoha/patches-6.12/029-03-spi-airoha-add-support-of-dual-quad-wires-spi-mod.patch
@@ -0,0 +1,209 @@
+From 703b10241666b468484a6ec5eb5c7c71fb2463ef Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <mikhail.kshevetskiy at iopsys.eu>
+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 <mikhail.kshevetskiy at iopsys.eu>
+Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno at collabora.com>
+---
+ 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
index 0000000000..7735d22d00
--- /dev/null
+++ b/target/linux/airoha/patches-6.12/029-04-spi-airoha-remove-unnecessary-switch-to-non-dma-m.patch
@@ -0,0 +1,29 @@
+From fb41a3e3bc357592b28a8abb504df99dad642588 Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <mikhail.kshevetskiy at iopsys.eu>
+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 <mikhail.kshevetskiy at iopsys.eu>
+Acked-by: Lorenzo Bianconi <lorenzo at kernel.org>
+Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno at collabora.com>
+---
+ 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
index 0000000000..39e759fec7
--- /dev/null
+++ b/target/linux/airoha/patches-6.12/029-05-spi-airoha-switch-back-to-non-dma-mode-in-the-cas.patch
@@ -0,0 +1,54 @@
+From 711584484d76448763959ed4e103895d9dcc7438 Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <mikhail.kshevetskiy at iopsys.eu>
+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 <mikhail.kshevetskiy at iopsys.eu>
+Acked-by: Lorenzo Bianconi <lorenzo at kernel.org>
+Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno at collabora.com>
+---
+ 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
index 0000000000..24515c8c03
--- /dev/null
+++ b/target/linux/airoha/patches-6.12/029-06-spi-airoha-fix-reading-writing-of-flashes-with-mo.patch
@@ -0,0 +1,102 @@
+From d8a0a67bf75c4cf2a760b6fa0002b0baff6e8b20 Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <mikhail.kshevetskiy at iopsys.eu>
+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 <mikhail.kshevetskiy at iopsys.eu>
+Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno at collabora.com>
+---
+ 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
index 0000000000..4be20c5b55
--- /dev/null
+++ b/target/linux/airoha/patches-6.12/029-07-spi-airoha-unify-dirmap-read-write-code.patch
@@ -0,0 +1,135 @@
+From 995b1a65206ee28d5403db0518cb230f2ce429ef Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <mikhail.kshevetskiy at iopsys.eu>
+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 <mikhail.kshevetskiy at iopsys.eu>
+---
+ 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
index 0000000000..4e00e7c178
--- /dev/null
+++ b/target/linux/airoha/patches-6.12/029-08-spi-airoha-support-of-dualio-quadio-flash-reading.patch
@@ -0,0 +1,92 @@
+From baaba9b8d3d907575323cbb7fabeae23db2a542b Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <mikhail.kshevetskiy at iopsys.eu>
+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 <mikhail.kshevetskiy at iopsys.eu>
+Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno at collabora.com>
+---
+ 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
index 0000000000..7eef83d562
--- /dev/null
+++ b/target/linux/airoha/patches-6.12/029-09-spi-airoha-buffer-must-be-0xff-ed-before-writing.patch
@@ -0,0 +1,29 @@
+From 6ca9cd453cb5d8a6411791295771b4dbd1c623de Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <mikhail.kshevetskiy at iopsys.eu>
+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 <mikhail.kshevetskiy at iopsys.eu>
+---
+ 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)
More information about the lede-commits
mailing list