From: Mikhail Zhilkin Date: Sun, 7 Jun 2026 10:04:39 +0000 (+0300) Subject: kernel: add support for FudanMicro FM25G01B and FM25G02B spi-nand X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F23864%2Fhead;p=thirdparty%2Fopenwrt.git kernel: add support for FudanMicro FM25G01B and FM25G02B spi-nand This commit adds support for FudanMicro FM25G01B and FM25G02B SPI NAND chips. This is required to: 1. Fix bootloop on new revision of Keenetic KN-1812 and Netcraze NC-1812 (with FudanMicro FM25G02B SPI NAND) 2. Add Nokia XG-040G-MD support (device has either SkyHigh or FudanMicro SPI NAND). Fixes: https://github.com/openwrt/openwrt/issues/23855 Signed-off-by: Mikhail Zhilkin Link: https://github.com/openwrt/openwrt/pull/23864 Signed-off-by: Hauke Mehrtens --- diff --git a/target/linux/generic/pending-6.18/401-mtd-spinand-fmsh-add-support-for-FM25G0102B.patch b/target/linux/generic/pending-6.18/401-mtd-spinand-fmsh-add-support-for-FM25G0102B.patch new file mode 100644 index 00000000000..e3a5a10e5bc --- /dev/null +++ b/target/linux/generic/pending-6.18/401-mtd-spinand-fmsh-add-support-for-FM25G0102B.patch @@ -0,0 +1,152 @@ +From: Ziyang Huang +To: miquel.raynal@bootlin.com +Subject: [PATCH v3] mtd: spinand: fmsh: add support for FM25G{01,02}B +Date: Sun, 31 May 2026 21:05:17 +0800 + +Add support for FudanMicro FM25G01B SPI NAND and FudanMicro FM25G02B SPI +NAND. + +FM25G01B datasheet: https://www.fmsh.com/nvm/FM25G01B_ds_eng.pdf +FM25G02B datasheet: https://www.fmsh.com/nvm/FM25G02B_ds_eng.pdf + +Signed-off-by: Ziyang Huang +Link: https://lore.kernel.org/all/SEYPR01MB58821E380C5DD8F7B3FFDA23C9142@SEYPR01MB5882.apcprd01.prod.exchangelabs.com/ +[Fix chip names in the commit message, mask macro in the switch operator] +Signed-off-by: Mikhail Zhilkin +--- +Changes since v2: + More verbose commit message. + Use only one section in fm25g01b_ooblayout_free(). + +Changes since v1: + Fix copy-paste issue. (Correct FM25G01B size.) + + drivers/mtd/nand/spi/fmsh.c | 96 +++++++++++++++++++++++++++++++++++++ + 1 file changed, 96 insertions(+) + +--- a/drivers/mtd/nand/spi/fmsh.c ++++ b/drivers/mtd/nand/spi/fmsh.c +@@ -9,6 +9,16 @@ + #include + #include + ++#define FM25G01B_STATUS_ECC_MASK (7 << 4) ++ #define FM25G01B_STATUS_ECC_NO_BITFLIPS (0 << 4) ++ #define FM25G01B_STATUS_ECC_1_3_BITFLIPS (1 << 4) ++ #define FM25G01B_STATUS_ECC_4_BITFLIPS (2 << 4) ++ #define FM25G01B_STATUS_ECC_5_BITFLIPS (3 << 4) ++ #define FM25G01B_STATUS_ECC_6_BITFLIPS (4 << 4) ++ #define FM25G01B_STATUS_ECC_7_BITFLIPS (5 << 4) ++ #define FM25G01B_STATUS_ECC_8_BITFLIPS (6 << 4) ++ #define FM25G01B_STATUS_ECC_UNCOR_ERROR (7 << 4) ++ + #define FM25S01BI3_STATUS_ECC_MASK (7 << 4) + #define FM25S01BI3_STATUS_ECC_NO_BITFLIPS (0 << 4) + #define FM25S01BI3_STATUS_ECC_1_3_BITFLIPS (1 << 4) +@@ -34,6 +44,67 @@ static SPINAND_OP_VARIANTS(update_cache_ + SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0)); + ++ ++static int fm25g01b_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) ++{ ++ if (section) ++ return -ERANGE; ++ ++ region->offset = 64; ++ region->length = 64; ++ ++ return 0; ++} ++ ++static int fm25g01b_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) ++{ ++ if (section) ++ return -ERANGE; ++ ++ /* reserve 2 bytes for the BBM */ ++ region->offset = 2; ++ region->length = 62; ++ ++ return 0; ++} ++ ++static int fm25g01b_ecc_get_status(struct spinand_device *spinand, ++ u8 status) ++{ ++ switch (status & FM25G01B_STATUS_ECC_MASK) { ++ case FM25G01B_STATUS_ECC_NO_BITFLIPS: ++ return 0; ++ ++ case FM25G01B_STATUS_ECC_1_3_BITFLIPS: ++ return 3; ++ ++ case FM25G01B_STATUS_ECC_4_BITFLIPS: ++ return 4; ++ ++ case FM25G01B_STATUS_ECC_5_BITFLIPS: ++ return 5; ++ ++ case FM25G01B_STATUS_ECC_6_BITFLIPS: ++ return 6; ++ ++ case FM25G01B_STATUS_ECC_7_BITFLIPS: ++ return 7; ++ ++ case FM25G01B_STATUS_ECC_8_BITFLIPS: ++ return 8; ++ ++ case FM25G01B_STATUS_ECC_UNCOR_ERROR: ++ return -EBADMSG; ++ ++ default: ++ break; ++ } ++ ++ return -EINVAL; ++} ++ + static int fm25s01a_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) + { +@@ -102,6 +173,11 @@ static int fm25s01bi3_ooblayout_free(str + return 0; + } + ++static const struct mtd_ooblayout_ops fm25g01b_ooblayout = { ++ .ecc = fm25g01b_ooblayout_ecc, ++ .free = fm25g01b_ooblayout_free, ++}; ++ + static const struct mtd_ooblayout_ops fm25s01a_ooblayout = { + .ecc = fm25s01a_ooblayout_ecc, + .free = fm25s01a_ooblayout_free, +@@ -113,6 +189,26 @@ static const struct mtd_ooblayout_ops fm + }; + + static const struct spinand_info fmsh_spinand_table[] = { ++ SPINAND_INFO("FM25G01B", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xd1), ++ NAND_MEMORG(1, 2048, 128, 64, 1024, 21, 1, 1, 1), ++ NAND_ECCREQ(8, 528), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&fm25g01b_ooblayout, ++ fm25g01b_ecc_get_status)), ++ SPINAND_INFO("FM25G02B", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xd2), ++ NAND_MEMORG(1, 2048, 128, 64, 2048, 41, 1, 1, 1), ++ NAND_ECCREQ(8, 528), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&fm25g01b_ooblayout, ++ fm25g01b_ecc_get_status)), + SPINAND_INFO("FM25S01A", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE4), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),