From 21a236d49e442a32b45d640d65bc0266ef7febb9 Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Wed, 10 Jun 2026 13:38:10 +0300 Subject: [PATCH] kernel: add support for HeYangTek HYF1GQ4UDACAE SPI-NAND The HeYangTek HYF1GQ4UDACAE is a 1 Gbit (128 MiB) SLC SPI-NAND with 2048 + 64 byte pages and on-die 4-bit / 512-byte ECC (JEDEC ID 0xc9), found among others on some Keenetic KN-3411 (Buddy 6) units. The driver is accepted upstream and sits in the MTD tree heading for Linux 7.3, so it goes to backport-6.18 as the upstream commit a837468386 ("mtd: spinand: add support for HeYangTek HYF1GQ4UDACAE"). The Etron and Dosilicon spinand patches are refreshed because the backport applies before them and touches the same spinand-objs line. Other SPI-NAND patches in the MediaTek, Airoha, EcoNet and Qualcommax targets are refreshed for the shifted core offsets. Signed-off-by: Aleksei Sviridkin Link: https://github.com/openwrt/openwrt/pull/23742 Signed-off-by: Jonas Jelonek --- .../901-snand-mtk-bmt-support.patch | 6 +- .../902-snand-mtk-bmt-support.patch | 6 +- ...-support-for-HeYangTek-HYF1GQ4UDACAE.patch | 191 ++++++++++++++++++ ...nd-Add-support-for-Etron-EM73D044VCx.patch | 4 +- ...support-for-Dosilicon-DS35Q1GA-DS35M.patch | 10 +- .../330-snand-mtk-bmt-support.patch | 6 +- ...and-Add-calibration-support-for-spin.patch | 4 +- ...nor-Add-calibration-support-for-spi-.patch | 2 +- ...960-asus-hack-u-boot-ignore-mtdparts.patch | 4 +- ...and-qpic-only-support-max-4-bytes-ID.patch | 2 +- 10 files changed, 212 insertions(+), 23 deletions(-) create mode 100644 target/linux/generic/backport-6.18/436-v7.3-mtd-spinand-add-support-for-HeYangTek-HYF1GQ4UDACAE.patch diff --git a/target/linux/airoha/patches-6.18/901-snand-mtk-bmt-support.patch b/target/linux/airoha/patches-6.18/901-snand-mtk-bmt-support.patch index 804542b9f5e..51be92ca2ce 100644 --- a/target/linux/airoha/patches-6.18/901-snand-mtk-bmt-support.patch +++ b/target/linux/airoha/patches-6.18/901-snand-mtk-bmt-support.patch @@ -8,7 +8,7 @@ int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val) { -@@ -1718,6 +1719,7 @@ static int spinand_probe(struct spi_mem +@@ -1719,6 +1720,7 @@ static int spinand_probe(struct spi_mem if (ret) return ret; @@ -16,7 +16,7 @@ ret = mtd_device_register(mtd, NULL, 0); if (ret) goto err_spinand_cleanup; -@@ -1725,6 +1727,7 @@ static int spinand_probe(struct spi_mem +@@ -1726,6 +1728,7 @@ static int spinand_probe(struct spi_mem return 0; err_spinand_cleanup: @@ -24,7 +24,7 @@ spinand_cleanup(spinand); return ret; -@@ -1743,6 +1746,7 @@ static int spinand_remove(struct spi_mem +@@ -1744,6 +1747,7 @@ static int spinand_remove(struct spi_mem if (ret) return ret; diff --git a/target/linux/econet/patches-6.18/902-snand-mtk-bmt-support.patch b/target/linux/econet/patches-6.18/902-snand-mtk-bmt-support.patch index 804542b9f5e..51be92ca2ce 100644 --- a/target/linux/econet/patches-6.18/902-snand-mtk-bmt-support.patch +++ b/target/linux/econet/patches-6.18/902-snand-mtk-bmt-support.patch @@ -8,7 +8,7 @@ int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val) { -@@ -1718,6 +1719,7 @@ static int spinand_probe(struct spi_mem +@@ -1719,6 +1720,7 @@ static int spinand_probe(struct spi_mem if (ret) return ret; @@ -16,7 +16,7 @@ ret = mtd_device_register(mtd, NULL, 0); if (ret) goto err_spinand_cleanup; -@@ -1725,6 +1727,7 @@ static int spinand_probe(struct spi_mem +@@ -1726,6 +1728,7 @@ static int spinand_probe(struct spi_mem return 0; err_spinand_cleanup: @@ -24,7 +24,7 @@ spinand_cleanup(spinand); return ret; -@@ -1743,6 +1746,7 @@ static int spinand_remove(struct spi_mem +@@ -1744,6 +1747,7 @@ static int spinand_remove(struct spi_mem if (ret) return ret; diff --git a/target/linux/generic/backport-6.18/436-v7.3-mtd-spinand-add-support-for-HeYangTek-HYF1GQ4UDACAE.patch b/target/linux/generic/backport-6.18/436-v7.3-mtd-spinand-add-support-for-HeYangTek-HYF1GQ4UDACAE.patch new file mode 100644 index 00000000000..6771819ca4c --- /dev/null +++ b/target/linux/generic/backport-6.18/436-v7.3-mtd-spinand-add-support-for-HeYangTek-HYF1GQ4UDACAE.patch @@ -0,0 +1,191 @@ +From a8374683868634012ac873d628fa581fcc452e9c Mon Sep 17 00:00:00 2001 +From: Aleksei Sviridkin +Date: Tue, 16 Jun 2026 16:28:44 +0300 +Subject: mtd: spinand: add support for HeYangTek HYF1GQ4UDACAE + +The HeYangTek HYF1GQ4UDACAE is a 1 Gbit (128 MiB) SLC SPI-NAND with +2048 + 64 byte pages and on-die 4-bit / 512-byte ECC; its JEDEC +manufacturer ID is 0xc9. The die is GD5F1GQ4-compatible, so the OOB +layout is taken from the in-tree gd5fxgq4xa. The die exposes only a +coarse 2-bit ECC status with no fine-grained bitflip-count register, so +the status is decoded into a representative number of corrected bitflips. + +It is found, among others, on some Keenetic KN-3411 (Buddy 6) units. + +Datasheet: +https://www.heyangtek.cn/previewfile.jsp?file=ABUIABA9GAAgwsvRnwYo-eDpsgc + +Signed-off-by: Aleksei Sviridkin +Signed-off-by: Miquel Raynal +--- + drivers/mtd/nand/spi/Makefile | 2 +- + drivers/mtd/nand/spi/core.c | 1 + + drivers/mtd/nand/spi/heyangtek.c | 132 +++++++++++++++++++++++++++++++++++++++ + include/linux/mtd/spinand.h | 1 + + 4 files changed, 135 insertions(+), 1 deletion(-) + create mode 100644 drivers/mtd/nand/spi/heyangtek.c + +--- a/drivers/mtd/nand/spi/Makefile ++++ b/drivers/mtd/nand/spi/Makefile +@@ -1,5 +1,5 @@ + # SPDX-License-Identifier: GPL-2.0 + spinand-objs := core.o otp.o +-spinand-objs += alliancememory.o ato.o esmt.o fmsh.o foresee.o gigadevice.o macronix.o ++spinand-objs += alliancememory.o ato.o esmt.o fmsh.o foresee.o gigadevice.o heyangtek.o macronix.o + spinand-objs += micron.o paragon.o skyhigh.o toshiba.o winbond.o xtx.o + obj-$(CONFIG_MTD_SPI_NAND) += spinand.o +--- a/drivers/mtd/nand/spi/core.c ++++ b/drivers/mtd/nand/spi/core.c +@@ -1231,6 +1231,7 @@ static const struct spinand_manufacturer + &fmsh_spinand_manufacturer, + &foresee_spinand_manufacturer, + &gigadevice_spinand_manufacturer, ++ &heyangtek_spinand_manufacturer, + ¯onix_spinand_manufacturer, + µn_spinand_manufacturer, + ¶gon_spinand_manufacturer, +--- /dev/null ++++ b/drivers/mtd/nand/spi/heyangtek.c +@@ -0,0 +1,132 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Authors: ++ * Andrey Zolotarev - the main driver logic ++ * Aleksei Sviridkin - adaptation to the mainline Linux kernel ++ * ++ * Based on: ++ * https://github.com/keenetic/kernel-49/commit/bacade569fb12bc0ad31ba09bca9b890118fbca7 ++ */ ++ ++#include ++#include ++#include ++ ++#define SPINAND_MFR_HEYANGTEK 0xc9 ++ ++#define HYF1GQ4_STATUS_ECC_LIMIT_BITFLIPS (3 << 4) ++ ++static SPINAND_OP_VARIANTS(read_cache_variants, ++ SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 1, NULL, 0, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_1S_2S_2S_OP(0, 1, NULL, 0, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0)); ++ ++static SPINAND_OP_VARIANTS(write_cache_variants, ++ SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0), ++ SPINAND_PROG_LOAD_1S_1S_1S_OP(true, 0, NULL, 0)); ++ ++static SPINAND_OP_VARIANTS(update_cache_variants, ++ SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0), ++ SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0)); ++ ++/* ++ * HYF1GQ4UDACAE is a GD5F1GQ4-compatible die, so the OOB layout is taken ++ * from gd5fxgq4xa: the on-die ECC parity occupies bytes 8..15 of each ++ * 16-byte section, the bad block marker sits in byte 0 and the remaining ++ * bytes are exposed as free. ++ */ ++static int hyf1gq4_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) ++{ ++ if (section > 3) ++ return -ERANGE; ++ ++ region->offset = (16 * section) + 8; ++ region->length = 8; ++ ++ return 0; ++} ++ ++static int hyf1gq4_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) ++{ ++ if (section > 3) ++ return -ERANGE; ++ ++ if (section) { ++ region->offset = 16 * section; ++ region->length = 8; ++ } else { ++ /* section 0 has one byte reserved for the bad block marker */ ++ region->offset = 1; ++ region->length = 7; ++ } ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops hyf1gq4_ooblayout = { ++ .ecc = hyf1gq4_ooblayout_ecc, ++ .free = hyf1gq4_ooblayout_free, ++}; ++ ++static int hyf1gq4_ecc_get_status(struct spinand_device *spinand, u8 status) ++{ ++ struct nand_device *nand = spinand_to_nand(spinand); ++ ++ switch (status & STATUS_ECC_MASK) { ++ case STATUS_ECC_NO_BITFLIPS: ++ return 0; ++ ++ case STATUS_ECC_UNCOR_ERROR: ++ return -EBADMSG; ++ ++ case STATUS_ECC_HAS_BITFLIPS: ++ /* ++ * The die exposes only a coarse 2-bit ECC status and has no ++ * register for the exact bitflip count. This code means ++ * "corrected, below the refresh threshold", so report half of ++ * the ECC strength as a representative value. ++ */ ++ return nanddev_get_ecc_conf(nand)->strength / 2; ++ ++ case HYF1GQ4_STATUS_ECC_LIMIT_BITFLIPS: ++ /* ++ * "Corrected, refresh recommended": report the full ECC ++ * strength so the upper layers relocate the data. ++ */ ++ return nanddev_get_ecc_conf(nand)->strength; ++ ++ default: ++ break; ++ } ++ ++ return -EINVAL; ++} ++ ++static const struct spinand_info heyangtek_spinand_table[] = { ++ SPINAND_INFO("HYF1GQ4UDACAE", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x21), ++ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(4, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&hyf1gq4_ooblayout, ++ hyf1gq4_ecc_get_status)), ++}; ++ ++static const struct spinand_manufacturer_ops heyangtek_spinand_manuf_ops = { ++}; ++ ++const struct spinand_manufacturer heyangtek_spinand_manufacturer = { ++ .id = SPINAND_MFR_HEYANGTEK, ++ .name = "HeYangTek", ++ .chips = heyangtek_spinand_table, ++ .nchips = ARRAY_SIZE(heyangtek_spinand_table), ++ .ops = &heyangtek_spinand_manuf_ops, ++}; +--- a/include/linux/mtd/spinand.h ++++ b/include/linux/mtd/spinand.h +@@ -365,6 +365,7 @@ extern const struct spinand_manufacturer + extern const struct spinand_manufacturer fmsh_spinand_manufacturer; + extern const struct spinand_manufacturer foresee_spinand_manufacturer; + extern const struct spinand_manufacturer gigadevice_spinand_manufacturer; ++extern const struct spinand_manufacturer heyangtek_spinand_manufacturer; + extern const struct spinand_manufacturer macronix_spinand_manufacturer; + extern const struct spinand_manufacturer micron_spinand_manufacturer; + extern const struct spinand_manufacturer paragon_spinand_manufacturer; diff --git a/target/linux/generic/pending-6.18/487-mtd-spinand-Add-support-for-Etron-EM73D044VCx.patch b/target/linux/generic/pending-6.18/487-mtd-spinand-Add-support-for-Etron-EM73D044VCx.patch index 7a322868443..1988e85570f 100644 --- a/target/linux/generic/pending-6.18/487-mtd-spinand-Add-support-for-Etron-EM73D044VCx.patch +++ b/target/linux/generic/pending-6.18/487-mtd-spinand-Add-support-for-Etron-EM73D044VCx.patch @@ -43,9 +43,9 @@ Submitted-by: Daniel Danzberger @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 spinand-objs := core.o otp.o --spinand-objs += alliancememory.o ato.o esmt.o fmsh.o foresee.o gigadevice.o macronix.o +-spinand-objs += alliancememory.o ato.o esmt.o fmsh.o foresee.o gigadevice.o heyangtek.o macronix.o -spinand-objs += micron.o paragon.o skyhigh.o toshiba.o winbond.o xtx.o -+spinand-objs += alliancememory.o ato.o esmt.o etron.o fmsh.o foresee.o gigadevice.o ++spinand-objs += alliancememory.o ato.o esmt.o etron.o fmsh.o foresee.o gigadevice.o heyangtek.o +spinand-objs += macronix.o micron.o paragon.o skyhigh.o toshiba.o winbond.o xtx.o obj-$(CONFIG_MTD_SPI_NAND) += spinand.o --- a/drivers/mtd/nand/spi/core.c diff --git a/target/linux/mediatek/patches-6.18/010-v7.0-mtd-spinand-add-support-for-Dosilicon-DS35Q1GA-DS35M.patch b/target/linux/mediatek/patches-6.18/010-v7.0-mtd-spinand-add-support-for-Dosilicon-DS35Q1GA-DS35M.patch index 5dc74180679..22a307de8c1 100644 --- a/target/linux/mediatek/patches-6.18/010-v7.0-mtd-spinand-add-support-for-Dosilicon-DS35Q1GA-DS35M.patch +++ b/target/linux/mediatek/patches-6.18/010-v7.0-mtd-spinand-add-support-for-Dosilicon-DS35Q1GA-DS35M.patch @@ -33,14 +33,12 @@ Signed-off-by: Miquel Raynal --- a/drivers/mtd/nand/spi/Makefile +++ b/drivers/mtd/nand/spi/Makefile -@@ -1,5 +1,6 @@ +@@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 spinand-objs := core.o otp.o --spinand-objs += alliancememory.o ato.o esmt.o etron.o fmsh.o foresee.o gigadevice.o --spinand-objs += macronix.o micron.o paragon.o skyhigh.o toshiba.o winbond.o xtx.o -+spinand-objs += alliancememory.o ato.o dosilicon.o esmt.o etron.o fmsh.o foresee.o -+spinand-objs += gigadevice.o macronix.o micron.o paragon.o skyhigh.o toshiba.o -+spinand-objs += winbond.o xtx.o +-spinand-objs += alliancememory.o ato.o esmt.o etron.o fmsh.o foresee.o gigadevice.o heyangtek.o ++spinand-objs += alliancememory.o ato.o dosilicon.o esmt.o etron.o fmsh.o foresee.o gigadevice.o heyangtek.o + spinand-objs += macronix.o micron.o paragon.o skyhigh.o toshiba.o winbond.o xtx.o obj-$(CONFIG_MTD_SPI_NAND) += spinand.o --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c diff --git a/target/linux/mediatek/patches-6.18/330-snand-mtk-bmt-support.patch b/target/linux/mediatek/patches-6.18/330-snand-mtk-bmt-support.patch index 182872bf0c4..ee760e2c497 100644 --- a/target/linux/mediatek/patches-6.18/330-snand-mtk-bmt-support.patch +++ b/target/linux/mediatek/patches-6.18/330-snand-mtk-bmt-support.patch @@ -20,7 +20,7 @@ Signed-off-by: Chuanhong Guo int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val) { -@@ -1719,6 +1720,7 @@ static int spinand_probe(struct spi_mem +@@ -1720,6 +1721,7 @@ static int spinand_probe(struct spi_mem if (ret) return ret; @@ -28,7 +28,7 @@ Signed-off-by: Chuanhong Guo ret = mtd_device_register(mtd, NULL, 0); if (ret) goto err_spinand_cleanup; -@@ -1726,6 +1728,7 @@ static int spinand_probe(struct spi_mem +@@ -1727,6 +1729,7 @@ static int spinand_probe(struct spi_mem return 0; err_spinand_cleanup: @@ -36,7 +36,7 @@ Signed-off-by: Chuanhong Guo spinand_cleanup(spinand); return ret; -@@ -1744,6 +1747,7 @@ static int spinand_remove(struct spi_mem +@@ -1745,6 +1748,7 @@ static int spinand_remove(struct spi_mem if (ret) return ret; diff --git a/target/linux/mediatek/patches-6.18/435-drivers-mtd-spinand-Add-calibration-support-for-spin.patch b/target/linux/mediatek/patches-6.18/435-drivers-mtd-spinand-Add-calibration-support-for-spin.patch index 14efbc19d35..1d39ef66f25 100644 --- a/target/linux/mediatek/patches-6.18/435-drivers-mtd-spinand-Add-calibration-support-for-spin.patch +++ b/target/linux/mediatek/patches-6.18/435-drivers-mtd-spinand-Add-calibration-support-for-spin.patch @@ -11,7 +11,7 @@ Signed-off-by: SkyLake.Huang --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c -@@ -1270,6 +1270,56 @@ static int spinand_manufacturer_match(st +@@ -1271,6 +1271,56 @@ static int spinand_manufacturer_match(st return -EOPNOTSUPP; } @@ -68,7 +68,7 @@ Signed-off-by: SkyLake.Huang static int spinand_id_detect(struct spinand_device *spinand) { u8 *id = spinand->id.data; -@@ -1589,6 +1639,10 @@ static int spinand_init(struct spinand_d +@@ -1590,6 +1640,10 @@ static int spinand_init(struct spinand_d spinand_init_ssdr_templates(spinand); diff --git a/target/linux/mediatek/patches-6.18/436-drivers-mtd-spi-nor-Add-calibration-support-for-spi-.patch b/target/linux/mediatek/patches-6.18/436-drivers-mtd-spi-nor-Add-calibration-support-for-spi-.patch index 29092f4aeb5..0ec651a14e4 100644 --- a/target/linux/mediatek/patches-6.18/436-drivers-mtd-spi-nor-Add-calibration-support-for-spi-.patch +++ b/target/linux/mediatek/patches-6.18/436-drivers-mtd-spi-nor-Add-calibration-support-for-spi-.patch @@ -12,7 +12,7 @@ Signed-off-by: SkyLake.Huang --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c -@@ -1311,7 +1311,10 @@ static int spinand_cal_read(void *priv, +@@ -1312,7 +1312,10 @@ static int spinand_cal_read(void *priv, if (ret) return ret; diff --git a/target/linux/mediatek/patches-6.18/960-asus-hack-u-boot-ignore-mtdparts.patch b/target/linux/mediatek/patches-6.18/960-asus-hack-u-boot-ignore-mtdparts.patch index 1d079331858..f2d05682aba 100644 --- a/target/linux/mediatek/patches-6.18/960-asus-hack-u-boot-ignore-mtdparts.patch +++ b/target/linux/mediatek/patches-6.18/960-asus-hack-u-boot-ignore-mtdparts.patch @@ -29,7 +29,7 @@ Signed-off-by: Daniel Golle --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c -@@ -1812,6 +1812,7 @@ static int spinand_remove(struct spi_mem +@@ -1813,6 +1813,7 @@ static int spinand_remove(struct spi_mem static const struct spi_device_id spinand_ids[] = { { .name = "spi-nand" }, @@ -37,7 +37,7 @@ Signed-off-by: Daniel Golle { /* sentinel */ }, }; MODULE_DEVICE_TABLE(spi, spinand_ids); -@@ -1819,6 +1820,7 @@ MODULE_DEVICE_TABLE(spi, spinand_ids); +@@ -1820,6 +1821,7 @@ MODULE_DEVICE_TABLE(spi, spinand_ids); #ifdef CONFIG_OF static const struct of_device_id spinand_of_ids[] = { { .compatible = "spi-nand" }, diff --git a/target/linux/qualcommax/patches-6.18/0412-mtd-spinand-qpic-only-support-max-4-bytes-ID.patch b/target/linux/qualcommax/patches-6.18/0412-mtd-spinand-qpic-only-support-max-4-bytes-ID.patch index 7fe59a025cd..f7fe0206e34 100644 --- a/target/linux/qualcommax/patches-6.18/0412-mtd-spinand-qpic-only-support-max-4-bytes-ID.patch +++ b/target/linux/qualcommax/patches-6.18/0412-mtd-spinand-qpic-only-support-max-4-bytes-ID.patch @@ -14,7 +14,7 @@ Signed-off-by: Ziyang Huang --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c -@@ -1408,7 +1408,7 @@ int spinand_match_and_init(struct spinan +@@ -1409,7 +1409,7 @@ int spinand_match_and_init(struct spinan if (rdid_method != info->devid.method) continue; -- 2.47.3