From: Lothar Waßmann Date: Fri, 28 Jul 2017 11:07:57 +0000 (+0200) Subject: mtd: nand: kirkwood: enable BCH ECC and add support for dynamically switching ECC... X-Git-Tag: KARO-TK71-2017-07-28 X-Git-Url: https://git.kernelconcepts.de/?p=karo-tx-uboot.git;a=commitdiff_plain;h=0c384211f0a1623dc3494f44f9f16c26dde941c0;hp=6bddff903bb875c02831cc312a66f99195a2cdf2 mtd: nand: kirkwood: enable BCH ECC and add support for dynamically switching ECC mode For certain NAND chips it is favourable or required to support 4-bit ECC schemes. The CPU uses an RS-ECC scheme for which no decoder could be found and is not worthwile being developed only for this purpose. Thus enable support for SOFT_BCH ECC which is fully supported by U-Boot. Add a command for switching between ECC modes to be able to write a bootable image and for compatibility with existing Linux implementations. --- diff --git a/drivers/mtd/nand/kirkwood_nand.c b/drivers/mtd/nand/kirkwood_nand.c index 1cdf286f60..9774fc4497 100644 --- a/drivers/mtd/nand/kirkwood_nand.c +++ b/drivers/mtd/nand/kirkwood_nand.c @@ -27,6 +27,7 @@ #include #include #include +#include #ifndef CONFIG_NAND_ECC_ALGO #define CONFIG_NAND_ECC_ALGO NAND_ECC_SOFT @@ -89,6 +90,106 @@ static struct nand_ecclayout kw_nand_oob_rs = { { .offset = 2, .length = 22, }, }, }; + +static void kw_nand_switch_eccmode(struct mtd_info *mtd, + nand_ecc_modes_t eccmode) +{ + struct nand_chip *nand = mtd->priv; + + if (nand->ecc.mode == eccmode) + return; + + switch (eccmode) { + case NAND_ECC_SOFT_RS: + nand->ecc.mode = NAND_ECC_SOFT_RS; + nand->ecc.layout = &kw_nand_oob_rs; + nand->ecc.size = 512; + nand->ecc.bytes = 10; + break; + case NAND_ECC_SOFT_BCH: + nand->ecc.layout = NULL; + nand->ecc.bytes = 0; + nand->ecc.size = 0; + break; + case NAND_ECC_SOFT: + nand->ecc.layout = NULL; + nand->ecc.size = 0; + break; + default: + printf("Unsupported ecc mode %u\n", eccmode); + return; + } + nand->ecc.mode = eccmode; + nand->options |= NAND_OWN_BUFFERS; + nand_scan_tail(mtd); + nand->options &= ~NAND_OWN_BUFFERS; +} + +static int do_kw_nand_switch_eccmode(cmd_tbl_t *cmdtp, int flag, int argc, + char *const argv[]) +{ + nand_ecc_modes_t mode; + struct mtd_info *mtd; + + if (argc > 2) + return CMD_RET_USAGE; + if (nand_curr_device < 0 || + nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE || + !nand_info[nand_curr_device].name) { + printf("Error: Can't switch ecc, no devices available\n"); + return CMD_RET_FAILURE; + } + mtd = &nand_info[nand_curr_device]; + + if (argc < 2) { + struct nand_chip *nand = mtd->priv; + char *modestr; + + switch (nand->ecc.mode) { + case NAND_ECC_SOFT: + modestr = "HAMMING"; + break; + case NAND_ECC_SOFT_RS: + modestr = "RS"; + break; + case NAND_ECC_SOFT_BCH: + if (mtd_nand_has_bch()) { + modestr = "BCH"; + break; + } + // fallthru + default: + printf("Unsupported ECC mode %d in use\n", nand->ecc.mode); + return CMD_RET_FAILURE; + } + printf("NAND ECC mode: %s\n", modestr); + return CMD_RET_SUCCESS; + } + if (strcmp(argv[1], "hamming") == 0 || + strcmp(argv[1], "soft") == 0) { + mode = NAND_ECC_SOFT; + } else if (strcmp(argv[1], "rs") == 0) { + mode = NAND_ECC_SOFT_RS; + } else if (strcmp(argv[1], "bch") == 0) { + mode = NAND_ECC_SOFT_BCH; + } else { + printf("Unsupported ECC mode '%s'; supported modes are 'hamming', 'RS'%s\n", + argv[1], mtd_nand_has_bch() ? ", 'BCH'" : ""); + return CMD_RET_USAGE; + } + + kw_nand_switch_eccmode(mtd, mode); + printf("ECC mode switched to %s\n", argv[1]); + return CMD_RET_SUCCESS; +} +U_BOOT_CMD(nandecc, 2, 0, do_kw_nand_switch_eccmode, + "nandecc", + "switch ecc mode\n" + "\tvalid modes are:" + "HAMMING (1 bit ECC; not suitable for Micron NAND)" + "BCH 4 bit ECC; (default)" + "RS 4 bit ECC; (write only! for compatibility with ROM code)" + ); #endif int board_nand_init(struct nand_chip *nand) @@ -97,10 +198,7 @@ int board_nand_init(struct nand_chip *nand) #ifndef CONFIG_NAND_ECC_SOFT_RS nand->ecc.mode = NAND_ECC_SOFT; #else - nand->ecc.mode = NAND_ECC_SOFT_RS; - nand->ecc.layout = &kw_nand_oob_rs; - nand->ecc.size = 512; - nand->ecc.bytes = 10; + nand->ecc.mode = NAND_ECC_SOFT_BCH; #endif nand->cmd_ctrl = kw_nand_hwcontrol; nand->chip_delay = 40; diff --git a/include/linux/mtd/nand_ecc.h b/include/linux/mtd/nand_ecc.h index dc1ddeda9a..b07e7431f3 100644 --- a/include/linux/mtd/nand_ecc.h +++ b/include/linux/mtd/nand_ecc.h @@ -30,6 +30,13 @@ int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_cha * Calculate 10 byte RS ECC syndrome for 512 byte block */ int nand_rs_calculate_ecc(struct mtd_info *mtd, const uint8_t *data, uint8_t *ecc); + +/* + * stub routine for unsupported RS-ECC decoding + */ +int nand_rs_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc); + +void kw_nand_ecc_switch(struct mtd_info *mtd, nand_ecc_modes_t eccmode); #endif #endif /* __MTD_NAND_ECC_H__ */