[PATCH 00/14] Backport BRCMNAND changes from Linux

Hunting down a bug on my system I took to back-porting all reasonable changes from the Linux brcmnand driver that were not yet in the U-Boot derivative.
I noticed that a simple diff -ur between brcmnand.c between the file in Linux and U-Boot was possible to see what differs. Combining this with some git log --oneline manual comparison, fuzzing and manual intervention I backported a set of relevant patches from Linux that compiles and WorksForMe(TM).
The diff between Linux and U-Boot is much smaller after this, the main missing part are subsystem cosmetics changes and the EDU DMA mode support.
This was as much as I could bite off in one go.
All patches countersigned-off and marked as [backported].
It's nice if someone else tests this I think.
Boris Brezillon (1): mtd: nand: brcm: switch to mtd_ooblayout_ops
Claire Lin (1): mtd: rawnand: brcmnand: Fix ecc chunk calculation for erased page bitfips
Kamal Dasu (4): mtd: rawnand: brcmnand: Fix BCH ECC layout for large page NAND parts mtd: rawnand: brcmnand: Refactored code to introduce helper functions mtd: rawnand: brcmnand: Add support for v7.3 controller mtd: nand: brcmnand: Add support for flash-dma v0
Álvaro Fernández Rojas (8): mtd: rawnand: brcmnand: fix hamming oob layout mtd: rawnand: brcmnand: improve hamming oob layout mtd: rawnand: brcmnand: correctly verify erased pages mtd: rawnand: brcmnand: rename v4 registers mtd: rawnand: brcmnand: fix CS0 layout mtd: rawnand: brcmnand: rename page sizes mtd: rawnand: brcmnand: support v2.1-v2.2 controllers mtd: rawnand: brcmnand: fix OOB R/W with Hamming ECC
drivers/mtd/nand/raw/brcmnand/brcmnand.c | 615 ++++++++++++++++------- 1 file changed, 425 insertions(+), 190 deletions(-)

From: Boris Brezillon boris.brezillon@free-electrons.com
Implementing the mtd_ooblayout_ops interface is the new way of exposing ECC/OOB layout to MTD users.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com [Ported to U-Boot from the Linux kernel] Signed-off-by: Linus Walleij linus.walleij@linaro.org --- drivers/mtd/nand/raw/brcmnand/brcmnand.c | 260 ++++++++++++++--------- 1 file changed, 156 insertions(+), 104 deletions(-)
diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index 74c9348f7fc4..8ea33e861354 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -894,131 +894,183 @@ static inline bool is_hamming_ecc(struct brcmnand_controller *ctrl, }
/* - * Returns a nand_ecclayout strucutre for the given layout/configuration. - * Returns NULL on failure. + * Set mtd->ooblayout to the appropriate mtd_ooblayout_ops given + * the layout/configuration. + * Returns -ERRCODE on failure. */ -static struct nand_ecclayout *brcmnand_create_layout(int ecc_level, - struct brcmnand_host *host) +static int brcmnand_hamming_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) { + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); struct brcmnand_cfg *cfg = &host->hwcfg; - int i, j; - struct nand_ecclayout *layout; - int req; - int sectors; - int sas; - int idx1, idx2;
-#ifndef __UBOOT__ - layout = devm_kzalloc(&host->pdev->dev, sizeof(*layout), GFP_KERNEL); -#else - layout = devm_kzalloc(host->pdev, sizeof(*layout), GFP_KERNEL); -#endif - if (!layout) - return NULL; - - sectors = cfg->page_size / (512 << cfg->sector_size_1k); - sas = cfg->spare_area_size << cfg->sector_size_1k; - - /* Hamming */ - if (is_hamming_ecc(host->ctrl, cfg)) { - for (i = 0, idx1 = 0, idx2 = 0; i < sectors; i++) { - /* First sector of each page may have BBI */ - if (i == 0) { - layout->oobfree[idx2].offset = i * sas + 1; - /* Small-page NAND use byte 6 for BBI */ - if (cfg->page_size == 512) - layout->oobfree[idx2].offset--; - layout->oobfree[idx2].length = 5; - } else { - layout->oobfree[idx2].offset = i * sas; - layout->oobfree[idx2].length = 6; - } - idx2++; - layout->eccpos[idx1++] = i * sas + 6; - layout->eccpos[idx1++] = i * sas + 7; - layout->eccpos[idx1++] = i * sas + 8; - layout->oobfree[idx2].offset = i * sas + 9; - layout->oobfree[idx2].length = 7; - idx2++; - /* Leave zero-terminated entry for OOBFREE */ - if (idx1 >= MTD_MAX_ECCPOS_ENTRIES_LARGE || - idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1) - break; - } + int sas = cfg->spare_area_size << cfg->sector_size_1k; + int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
- return layout; - } + if (section >= sectors) + return -ERANGE; + oobregion->offset = (section * sas) + 6; + oobregion->length = 3;
- /* - * CONTROLLER_VERSION: - * < v5.0: ECC_REQ = ceil(BCH_T * 13/8) - * >= v5.0: ECC_REQ = ceil(BCH_T * 14/8) - * But we will just be conservative. - */ - req = DIV_ROUND_UP(ecc_level * 14, 8); - if (req >= sas) { - dev_err(host->pdev, - "error: ECC too large for OOB (ECC bytes %d, spare sector %d)\n", - req, sas); - return NULL; - } + return 0; +} + +static int brcmnand_hamming_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_cfg *cfg = &host->hwcfg; + int sas = cfg->spare_area_size << cfg->sector_size_1k; + int sectors = cfg->page_size / (512 << cfg->sector_size_1k); + + if (section >= sectors * 2) + return -ERANGE; + + oobregion->offset = (section / 2) * sas;
- layout->eccbytes = req * sectors; - for (i = 0, idx1 = 0, idx2 = 0; i < sectors; i++) { - for (j = sas - req; j < sas && idx1 < - MTD_MAX_ECCPOS_ENTRIES_LARGE; j++, idx1++) - layout->eccpos[idx1] = i * sas + j; + if (section & 1) { + oobregion->offset += 9; + oobregion->length = 7; + } else { + oobregion->length = 6;
/* First sector of each page may have BBI */ - if (i == 0) { - if (cfg->page_size == 512 && (sas - req >= 6)) { - /* Small-page NAND use byte 6 for BBI */ - layout->oobfree[idx2].offset = 0; - layout->oobfree[idx2].length = 5; - idx2++; - if (sas - req > 6) { - layout->oobfree[idx2].offset = 6; - layout->oobfree[idx2].length = - sas - req - 6; - idx2++; - } - } else if (sas > req + 1) { - layout->oobfree[idx2].offset = i * sas + 1; - layout->oobfree[idx2].length = sas - req - 1; - idx2++; - } - } else if (sas > req) { - layout->oobfree[idx2].offset = i * sas; - layout->oobfree[idx2].length = sas - req; - idx2++; + if (!section) { + /* + * Small-page NAND use byte 6 for BBI while large-page + * NAND use byte 0. + */ + if (cfg->page_size > 512) + oobregion->offset++; + oobregion->length--; } - /* Leave zero-terminated entry for OOBFREE */ - if (idx1 >= MTD_MAX_ECCPOS_ENTRIES_LARGE || - idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1) - break; }
- return layout; + return 0; }
-static struct nand_ecclayout *brcmstb_choose_ecc_layout( - struct brcmnand_host *host) +static const struct mtd_ooblayout_ops brcmnand_hamming_ooblayout_ops = { + .ecc = brcmnand_hamming_ooblayout_ecc, + .rfree = brcmnand_hamming_ooblayout_free, +}; + +static int brcmnand_bch_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_cfg *cfg = &host->hwcfg; + int sas = cfg->spare_area_size << cfg->sector_size_1k; + int sectors = cfg->page_size / (512 << cfg->sector_size_1k); + + if (section >= sectors) + return -ERANGE; + + oobregion->offset = (section * (sas + 1)) - chip->ecc.bytes; + oobregion->length = chip->ecc.bytes; + + return 0; +} + +static int brcmnand_bch_ooblayout_free_lp(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_cfg *cfg = &host->hwcfg; + int sas = cfg->spare_area_size << cfg->sector_size_1k; + int sectors = cfg->page_size / (512 << cfg->sector_size_1k); + + if (section >= sectors) + return -ERANGE; + + if (sas <= chip->ecc.bytes) + return 0; + + oobregion->offset = section * sas; + oobregion->length = sas - chip->ecc.bytes; + + if (!section) { + oobregion->offset++; + oobregion->length--; + } + + return 0; +} + +static int brcmnand_bch_ooblayout_free_sp(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_cfg *cfg = &host->hwcfg; + int sas = cfg->spare_area_size << cfg->sector_size_1k; + + if (section > 1 || sas - chip->ecc.bytes < 6 || + (section && sas - chip->ecc.bytes == 6)) + return -ERANGE; + + if (!section) { + oobregion->offset = 0; + oobregion->length = 5; + } else { + oobregion->offset = 6; + oobregion->length = sas - chip->ecc.bytes - 6; + } + + return 0; +} + +static const struct mtd_ooblayout_ops brcmnand_bch_lp_ooblayout_ops = { + .ecc = brcmnand_bch_ooblayout_ecc, + .rfree = brcmnand_bch_ooblayout_free_lp, +}; + +static const struct mtd_ooblayout_ops brcmnand_bch_sp_ooblayout_ops = { + .ecc = brcmnand_bch_ooblayout_ecc, + .rfree = brcmnand_bch_ooblayout_free_sp, +}; + +static int brcmstb_choose_ecc_layout(struct brcmnand_host *host) { - struct nand_ecclayout *layout; struct brcmnand_cfg *p = &host->hwcfg; + struct mtd_info *mtd = nand_to_mtd(&host->chip); + struct nand_ecc_ctrl *ecc = &host->chip.ecc; unsigned int ecc_level = p->ecc_level; + int sas = p->spare_area_size << p->sector_size_1k; + int sectors = p->page_size / (512 << p->sector_size_1k);
if (p->sector_size_1k) ecc_level <<= 1;
- layout = brcmnand_create_layout(ecc_level, host); - if (!layout) { + if (is_hamming_ecc(host->ctrl, p)) { + ecc->bytes = 3 * sectors; + mtd_set_ooblayout(mtd, &brcmnand_hamming_ooblayout_ops); + return 0; + } + + /* + * CONTROLLER_VERSION: + * < v5.0: ECC_REQ = ceil(BCH_T * 13/8) + * >= v5.0: ECC_REQ = ceil(BCH_T * 14/8) + * But we will just be conservative. + */ + ecc->bytes = DIV_ROUND_UP(ecc_level * 14, 8); + if (p->page_size == 512) + mtd_set_ooblayout(mtd, &brcmnand_bch_sp_ooblayout_ops); + else + mtd_set_ooblayout(mtd, &brcmnand_bch_lp_ooblayout_ops); + + if (ecc->bytes >= sas) { dev_err(host->pdev, - "no proper ecc_layout for this NAND cfg\n"); - return NULL; + "error: ECC too large for OOB (ECC bytes %d, spare sector %d)\n", + ecc->bytes, sas); + return -EINVAL; }
- return layout; + return 0; }
static void brcmnand_wp(struct mtd_info *mtd, int wp) @@ -2329,9 +2381,9 @@ static int brcmnand_init_cs(struct brcmnand_host *host, ofnode dn) /* only use our internal HW threshold */ mtd->bitflip_threshold = 1;
- chip->ecc.layout = brcmstb_choose_ecc_layout(host); - if (!chip->ecc.layout) - return -ENXIO; + ret = brcmstb_choose_ecc_layout(host); + if (ret) + return ret;
ret = nand_scan_tail(mtd); if (ret)

Hi Linus,
Unfortunately the u-boot nand base code still uses nand_ecclayout structure because it was based on old kernel nand driver. Your change cause bugcheck in the nand_scan_tail at line 4978 when mtd->oobsize is not one of the default size (i.e. some large nand with BCH-8 ecc requirement and has 224 bytes oobsize per 4K page) because ecc->layout is never set. Also certainly any data built based on nand_cclayout like mtd->oobavail will not be correct.
I actually converted back to nand_ecclayout structure from mtd_ooblayout with this fix to solve the above issues. Fixes: e365de90517b ("drivers: nand: brcmnand: fix nand_chip ecc layout structure")
I can see your point to get the latest from the brcmnand linux kernel driver but this requires updating the u-boot nand base driver to use mtd_ooblayout as well and all others nand controller drivers too. I am not sure if this is something you want to tackle right now.
As far as I can see, all other oob/ecc layout setting patches you back ported in this series are not in the original brcmstb_choose_ecc_layout code you replaced. So I am not worried about that we must switch to mtd_ooblayout_ops at this point. If indeed there is bug in brcmstb_choose_ecc_layout, we can port and convert the fix to nand_ecclayout structure from kernel code.
Was the bug you were hunting down in the code related to this patch?
Thanks, William
On 01/15/2023 11:52 AM, Linus Walleij wrote:
From: Boris Brezillon boris.brezillon@free-electrons.com
Implementing the mtd_ooblayout_ops interface is the new way of exposing ECC/OOB layout to MTD users.
Signed-off-by: Boris Brezillon boris.brezillon@free-electrons.com [Ported to U-Boot from the Linux kernel] Signed-off-by: Linus Walleij linus.walleij@linaro.org
drivers/mtd/nand/raw/brcmnand/brcmnand.c | 260 ++++++++++++++--------- 1 file changed, 156 insertions(+), 104 deletions(-)
diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index 74c9348f7fc4..8ea33e861354 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -894,131 +894,183 @@ static inline bool is_hamming_ecc(struct brcmnand_controller *ctrl, }
/*
- Returns a nand_ecclayout strucutre for the given layout/configuration.
- Returns NULL on failure.
- Set mtd->ooblayout to the appropriate mtd_ooblayout_ops given
- the layout/configuration.
*/
- Returns -ERRCODE on failure.
-static struct nand_ecclayout *brcmnand_create_layout(int ecc_level,
struct brcmnand_host *host)
+static int brcmnand_hamming_ooblayout_ecc(struct mtd_info *mtd, int section,
{struct mtd_oob_region *oobregion)
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct brcmnand_host *host = nand_get_controller_data(chip); struct brcmnand_cfg *cfg = &host->hwcfg;
- int i, j;
- struct nand_ecclayout *layout;
- int req;
- int sectors;
- int sas;
- int idx1, idx2;
-#ifndef __UBOOT__
- layout = devm_kzalloc(&host->pdev->dev, sizeof(*layout), GFP_KERNEL);
-#else
- layout = devm_kzalloc(host->pdev, sizeof(*layout), GFP_KERNEL);
-#endif
- if (!layout)
return NULL;
- sectors = cfg->page_size / (512 << cfg->sector_size_1k);
- sas = cfg->spare_area_size << cfg->sector_size_1k;
- /* Hamming */
- if (is_hamming_ecc(host->ctrl, cfg)) {
for (i = 0, idx1 = 0, idx2 = 0; i < sectors; i++) {
/* First sector of each page may have BBI */
if (i == 0) {
layout->oobfree[idx2].offset = i * sas + 1;
/* Small-page NAND use byte 6 for BBI */
if (cfg->page_size == 512)
layout->oobfree[idx2].offset--;
layout->oobfree[idx2].length = 5;
} else {
layout->oobfree[idx2].offset = i * sas;
layout->oobfree[idx2].length = 6;
}
idx2++;
layout->eccpos[idx1++] = i * sas + 6;
layout->eccpos[idx1++] = i * sas + 7;
layout->eccpos[idx1++] = i * sas + 8;
layout->oobfree[idx2].offset = i * sas + 9;
layout->oobfree[idx2].length = 7;
idx2++;
/* Leave zero-terminated entry for OOBFREE */
if (idx1 >= MTD_MAX_ECCPOS_ENTRIES_LARGE ||
idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1)
break;
}
- int sas = cfg->spare_area_size << cfg->sector_size_1k;
- int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
return layout;
- }
- if (section >= sectors)
return -ERANGE;
- oobregion->offset = (section * sas) + 6;
- oobregion->length = 3;
- /*
* CONTROLLER_VERSION:
* < v5.0: ECC_REQ = ceil(BCH_T * 13/8)
* >= v5.0: ECC_REQ = ceil(BCH_T * 14/8)
* But we will just be conservative.
*/
- req = DIV_ROUND_UP(ecc_level * 14, 8);
- if (req >= sas) {
dev_err(host->pdev,
"error: ECC too large for OOB (ECC bytes %d, spare sector %d)\n",
req, sas);
return NULL;
- }
- return 0;
+}
+static int brcmnand_hamming_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
+{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct brcmnand_host *host = nand_get_controller_data(chip);
- struct brcmnand_cfg *cfg = &host->hwcfg;
- int sas = cfg->spare_area_size << cfg->sector_size_1k;
- int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
- if (section >= sectors * 2)
return -ERANGE;
- oobregion->offset = (section / 2) * sas;
- layout->eccbytes = req * sectors;
- for (i = 0, idx1 = 0, idx2 = 0; i < sectors; i++) {
for (j = sas - req; j < sas && idx1 <
MTD_MAX_ECCPOS_ENTRIES_LARGE; j++, idx1++)
layout->eccpos[idx1] = i * sas + j;
if (section & 1) {
oobregion->offset += 9;
oobregion->length = 7;
} else {
oobregion->length = 6;
/* First sector of each page may have BBI */
if (i == 0) {
if (cfg->page_size == 512 && (sas - req >= 6)) {
/* Small-page NAND use byte 6 for BBI */
layout->oobfree[idx2].offset = 0;
layout->oobfree[idx2].length = 5;
idx2++;
if (sas - req > 6) {
layout->oobfree[idx2].offset = 6;
layout->oobfree[idx2].length =
sas - req - 6;
idx2++;
}
} else if (sas > req + 1) {
layout->oobfree[idx2].offset = i * sas + 1;
layout->oobfree[idx2].length = sas - req - 1;
idx2++;
}
} else if (sas > req) {
layout->oobfree[idx2].offset = i * sas;
layout->oobfree[idx2].length = sas - req;
idx2++;
if (!section) {
/*
* Small-page NAND use byte 6 for BBI while large-page
* NAND use byte 0.
*/
if (cfg->page_size > 512)
oobregion->offset++;
}oobregion->length--;
/* Leave zero-terminated entry for OOBFREE */
if (idx1 >= MTD_MAX_ECCPOS_ENTRIES_LARGE ||
idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1)
break;
}
return layout;
- return 0; }
-static struct nand_ecclayout *brcmstb_choose_ecc_layout(
struct brcmnand_host *host)
+static const struct mtd_ooblayout_ops brcmnand_hamming_ooblayout_ops = {
- .ecc = brcmnand_hamming_ooblayout_ecc,
- .rfree = brcmnand_hamming_ooblayout_free,
+};
+static int brcmnand_bch_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
+{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct brcmnand_host *host = nand_get_controller_data(chip);
- struct brcmnand_cfg *cfg = &host->hwcfg;
- int sas = cfg->spare_area_size << cfg->sector_size_1k;
- int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
- if (section >= sectors)
return -ERANGE;
- oobregion->offset = (section * (sas + 1)) - chip->ecc.bytes;
- oobregion->length = chip->ecc.bytes;
- return 0;
+}
+static int brcmnand_bch_ooblayout_free_lp(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
+{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct brcmnand_host *host = nand_get_controller_data(chip);
- struct brcmnand_cfg *cfg = &host->hwcfg;
- int sas = cfg->spare_area_size << cfg->sector_size_1k;
- int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
- if (section >= sectors)
return -ERANGE;
- if (sas <= chip->ecc.bytes)
return 0;
- oobregion->offset = section * sas;
- oobregion->length = sas - chip->ecc.bytes;
- if (!section) {
oobregion->offset++;
oobregion->length--;
- }
- return 0;
+}
+static int brcmnand_bch_ooblayout_free_sp(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
+{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct brcmnand_host *host = nand_get_controller_data(chip);
- struct brcmnand_cfg *cfg = &host->hwcfg;
- int sas = cfg->spare_area_size << cfg->sector_size_1k;
- if (section > 1 || sas - chip->ecc.bytes < 6 ||
(section && sas - chip->ecc.bytes == 6))
return -ERANGE;
- if (!section) {
oobregion->offset = 0;
oobregion->length = 5;
- } else {
oobregion->offset = 6;
oobregion->length = sas - chip->ecc.bytes - 6;
- }
- return 0;
+}
+static const struct mtd_ooblayout_ops brcmnand_bch_lp_ooblayout_ops = {
- .ecc = brcmnand_bch_ooblayout_ecc,
- .rfree = brcmnand_bch_ooblayout_free_lp,
+};
+static const struct mtd_ooblayout_ops brcmnand_bch_sp_ooblayout_ops = {
- .ecc = brcmnand_bch_ooblayout_ecc,
- .rfree = brcmnand_bch_ooblayout_free_sp,
+};
+static int brcmstb_choose_ecc_layout(struct brcmnand_host *host) {
- struct nand_ecclayout *layout; struct brcmnand_cfg *p = &host->hwcfg;
struct mtd_info *mtd = nand_to_mtd(&host->chip);
struct nand_ecc_ctrl *ecc = &host->chip.ecc; unsigned int ecc_level = p->ecc_level;
int sas = p->spare_area_size << p->sector_size_1k;
int sectors = p->page_size / (512 << p->sector_size_1k);
if (p->sector_size_1k) ecc_level <<= 1;
- layout = brcmnand_create_layout(ecc_level, host);
- if (!layout) {
- if (is_hamming_ecc(host->ctrl, p)) {
ecc->bytes = 3 * sectors;
mtd_set_ooblayout(mtd, &brcmnand_hamming_ooblayout_ops);
return 0;
- }
- /*
* CONTROLLER_VERSION:
* < v5.0: ECC_REQ = ceil(BCH_T * 13/8)
* >= v5.0: ECC_REQ = ceil(BCH_T * 14/8)
* But we will just be conservative.
*/
- ecc->bytes = DIV_ROUND_UP(ecc_level * 14, 8);
- if (p->page_size == 512)
mtd_set_ooblayout(mtd, &brcmnand_bch_sp_ooblayout_ops);
- else
mtd_set_ooblayout(mtd, &brcmnand_bch_lp_ooblayout_ops);
- if (ecc->bytes >= sas) { dev_err(host->pdev,
"no proper ecc_layout for this NAND cfg\n");
return NULL;
"error: ECC too large for OOB (ECC bytes %d, spare sector %d)\n",
ecc->bytes, sas);
}return -EINVAL;
- return layout;
return 0; }
static void brcmnand_wp(struct mtd_info *mtd, int wp)
@@ -2329,9 +2381,9 @@ static int brcmnand_init_cs(struct brcmnand_host *host, ofnode dn) /* only use our internal HW threshold */ mtd->bitflip_threshold = 1;
- chip->ecc.layout = brcmstb_choose_ecc_layout(host);
- if (!chip->ecc.layout)
return -ENXIO;
ret = brcmstb_choose_ecc_layout(host);
if (ret)
return ret;
ret = nand_scan_tail(mtd); if (ret)

On Thu, Jan 26, 2023 at 2:02 AM William Zhang william.zhang@broadcom.com wrote:
Unfortunately the u-boot nand base code still uses nand_ecclayout structure because it was based on old kernel nand driver. Your change cause bugcheck in the nand_scan_tail at line 4978 when mtd->oobsize is not one of the default size (i.e. some large nand with BCH-8 ecc requirement and has 224 bytes oobsize per 4K page) because ecc->layout is never set. Also certainly any data built based on nand_cclayout like mtd->oobavail will not be correct.
I actually converted back to nand_ecclayout structure from mtd_ooblayout with this fix to solve the above issues. Fixes: e365de90517b ("drivers: nand: brcmnand: fix nand_chip ecc layout structure")
Argh yeah I see. Let's hold this series off then. It was worth a try!
I can see your point to get the latest from the brcmnand linux kernel driver but this requires updating the u-boot nand base driver to use mtd_ooblayout as well and all others nand controller drivers too. I am not sure if this is something you want to tackle right now.
No I can't do that I do not have a big enough experience with NAND flash and no testbed for it either.
As far as I can see, all other oob/ecc layout setting patches you back ported in this series are not in the original brcmstb_choose_ecc_layout code you replaced. So I am not worried about that we must switch to mtd_ooblayout_ops at this point. If indeed there is bug in brcmstb_choose_ecc_layout, we can port and convert the fix to nand_ecclayout structure from kernel code.
OK no they seemed to be mostly improvements of the algorithm so we can certainly live without.
Was the bug you were hunting down in the code related to this patch?
Actually not, I think, it's one of the other patches I sent, the one enabling BCH-1 by reading the proper ECC properties from the device tree. That made it finally work.
The iproc NAND driver I sent should also work pretty much as-is, nothing depends on these backports.
Yours, Linus Walleij

On 01/26/2023 12:43 AM, Linus Walleij wrote:
On Thu, Jan 26, 2023 at 2:02 AM William Zhang william.zhang@broadcom.com wrote:
Unfortunately the u-boot nand base code still uses nand_ecclayout structure because it was based on old kernel nand driver. Your change cause bugcheck in the nand_scan_tail at line 4978 when mtd->oobsize is not one of the default size (i.e. some large nand with BCH-8 ecc requirement and has 224 bytes oobsize per 4K page) because ecc->layout is never set. Also certainly any data built based on nand_cclayout like mtd->oobavail will not be correct.
I actually converted back to nand_ecclayout structure from mtd_ooblayout with this fix to solve the above issues. Fixes: e365de90517b ("drivers: nand: brcmnand: fix nand_chip ecc layout structure")
Argh yeah I see. Let's hold this series off then. It was worth a try!
I can see your point to get the latest from the brcmnand linux kernel driver but this requires updating the u-boot nand base driver to use mtd_ooblayout as well and all others nand controller drivers too. I am not sure if this is something you want to tackle right now.
No I can't do that I do not have a big enough experience with NAND flash and no testbed for it either.
As far as I can see, all other oob/ecc layout setting patches you back ported in this series are not in the original brcmstb_choose_ecc_layout code you replaced. So I am not worried about that we must switch to mtd_ooblayout_ops at this point. If indeed there is bug in brcmstb_choose_ecc_layout, we can port and convert the fix to nand_ecclayout structure from kernel code.
OK no they seemed to be mostly improvements of the algorithm so we can certainly live without.
Was the bug you were hunting down in the code related to this patch?
Actually not, I think, it's one of the other patches I sent, the one enabling BCH-1 by reading the proper ECC properties from the device tree. That made it finally work.
The iproc NAND driver I sent should also work pretty much as-is, nothing depends on these backports.
Yes the patches that are not relate to the ooblayout should still be good to have.
Yours, Linus Walleij

Hi William
On Thu, Jan 26, 2023 at 6:39 PM William Zhang william.zhang@broadcom.com wrote:
On 01/26/2023 12:43 AM, Linus Walleij wrote:
On Thu, Jan 26, 2023 at 2:02 AM William Zhang william.zhang@broadcom.com wrote:
Can you add your review-by?
Michael
Unfortunately the u-boot nand base code still uses nand_ecclayout structure because it was based on old kernel nand driver. Your change cause bugcheck in the nand_scan_tail at line 4978 when mtd->oobsize is not one of the default size (i.e. some large nand with BCH-8 ecc requirement and has 224 bytes oobsize per 4K page) because ecc->layout is never set. Also certainly any data built based on nand_cclayout like mtd->oobavail will not be correct.
I actually converted back to nand_ecclayout structure from mtd_ooblayout with this fix to solve the above issues. Fixes: e365de90517b ("drivers: nand: brcmnand: fix nand_chip ecc layout structure")
Argh yeah I see. Let's hold this series off then. It was worth a try!
I can see your point to get the latest from the brcmnand linux kernel driver but this requires updating the u-boot nand base driver to use mtd_ooblayout as well and all others nand controller drivers too. I am not sure if this is something you want to tackle right now.
No I can't do that I do not have a big enough experience with NAND flash and no testbed for it either.
As far as I can see, all other oob/ecc layout setting patches you back ported in this series are not in the original brcmstb_choose_ecc_layout code you replaced. So I am not worried about that we must switch to mtd_ooblayout_ops at this point. If indeed there is bug in brcmstb_choose_ecc_layout, we can port and convert the fix to nand_ecclayout structure from kernel code.
OK no they seemed to be mostly improvements of the algorithm so we can certainly live without.
Was the bug you were hunting down in the code related to this patch?
Actually not, I think, it's one of the other patches I sent, the one enabling BCH-1 by reading the proper ECC properties from the device tree. That made it finally work.
The iproc NAND driver I sent should also work pretty much as-is, nothing depends on these backports.
Yes the patches that are not relate to the ooblayout should still be good to have.
Yours, Linus Walleij

On Fri, Feb 3, 2023 at 9:48 AM Michael Nazzareno Trimarchi michael@amarulasolutions.com wrote:
On Thu, Jan 26, 2023 at 6:39 PM William Zhang william.zhang@broadcom.com wrote:
On 01/26/2023 12:43 AM, Linus Walleij wrote:
On Thu, Jan 26, 2023 at 2:02 AM William Zhang william.zhang@broadcom.com wrote:
Can you add your review-by?
I think maybe I need to rebase the series and take out all the changes that does not relate to patch 1 that changes the way we handle the OOB layout so William can test the result?
Yours, Linus Walleij

Hi Linus and Michael,
On 02/03/2023 03:10 AM, Linus Walleij wrote:
On Fri, Feb 3, 2023 at 9:48 AM Michael Nazzareno Trimarchi michael@amarulasolutions.com wrote:
On Thu, Jan 26, 2023 at 6:39 PM William Zhang william.zhang@broadcom.com wrote:
On 01/26/2023 12:43 AM, Linus Walleij wrote:
On Thu, Jan 26, 2023 at 2:02 AM William Zhang william.zhang@broadcom.com wrote:
Can you add your review-by?
I think maybe I need to rebase the series and take out all the changes that does not relate to patch 1 that changes the way we handle the OOB layout so William can test the result?
Yeah I think that is a good idea and I can add my review-by and test-by tag if everything is good.
Yours, Linus Walleij

Hi Linus
On Fri, Feb 3, 2023 at 6:23 PM William Zhang william.zhang@broadcom.com wrote:
Hi Linus and Michael,
On 02/03/2023 03:10 AM, Linus Walleij wrote:
On Fri, Feb 3, 2023 at 9:48 AM Michael Nazzareno Trimarchi michael@amarulasolutions.com wrote:
On Thu, Jan 26, 2023 at 6:39 PM William Zhang william.zhang@broadcom.com wrote:
On 01/26/2023 12:43 AM, Linus Walleij wrote:
On Thu, Jan 26, 2023 at 2:02 AM William Zhang william.zhang@broadcom.com wrote:
Can you add your review-by?
I think maybe I need to rebase the series and take out all the changes that does not relate to patch 1 that changes the way we handle the OOB layout so William can test the result?
Yeah I think that is a good idea and I can add my review-by and test-by tag if everything is good.
Yours, Linus Walleij
Can you repost your patchset?
Thanks Michael

From: Kamal Dasu kdasu.kdev@gmail.com
The way oobregion->offset is derived for large page NAND parts is wrong, fixes it.
Fixes: ef5eeea6e911 ("mtd: nand: brcm: switch to mtd_ooblayout_ops") Signed-off-by: Kamal Dasu kdasu.kdev@gmail.com Reviewed-by: Florian Fainelli f.fainelli@gmail.com Signed-off-by: Miquel Raynal miquel.raynal@bootlin.com [Ported to U-Boot from the Linux kernel] Signed-off-by: Linus Walleij linus.walleij@linaro.org --- drivers/mtd/nand/raw/brcmnand/brcmnand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index 8ea33e861354..65aab4be87b9 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -968,7 +968,7 @@ static int brcmnand_bch_ooblayout_ecc(struct mtd_info *mtd, int section, if (section >= sectors) return -ERANGE;
- oobregion->offset = (section * (sas + 1)) - chip->ecc.bytes; + oobregion->offset = ((section + 1) * sas) - chip->ecc.bytes; oobregion->length = chip->ecc.bytes;
return 0;

From: Kamal Dasu kdasu.kdev@gmail.com
Refactored NAND ECC and CMD address configuration code to use helper functions.
Signed-off-by: Kamal Dasu kdasu.kdev@gmail.com Signed-off-by: Miquel Raynal miquel.raynal@bootlin.com [Ported to U-Boot from the Linux kernel] Signed-off-by: Linus Walleij linus.walleij@linaro.org --- drivers/mtd/nand/raw/brcmnand/brcmnand.c | 100 ++++++++++++++--------- 1 file changed, 62 insertions(+), 38 deletions(-)
diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index 65aab4be87b9..ede51def5063 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -595,6 +595,54 @@ static inline void brcmnand_write_fc(struct brcmnand_controller *ctrl, __raw_writel(val, ctrl->nand_fc + word * 4); }
+static void brcmnand_clear_ecc_addr(struct brcmnand_controller *ctrl) +{ + + /* Clear error addresses */ + brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_ADDR, 0); + brcmnand_write_reg(ctrl, BRCMNAND_CORR_ADDR, 0); + brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_EXT_ADDR, 0); + brcmnand_write_reg(ctrl, BRCMNAND_CORR_EXT_ADDR, 0); +} + +static u64 brcmnand_get_uncorrecc_addr(struct brcmnand_controller *ctrl) +{ + u64 err_addr; + + err_addr = brcmnand_read_reg(ctrl, BRCMNAND_UNCORR_ADDR); + err_addr |= ((u64)(brcmnand_read_reg(ctrl, + BRCMNAND_UNCORR_EXT_ADDR) + & 0xffff) << 32); + + return err_addr; +} + +static u64 brcmnand_get_correcc_addr(struct brcmnand_controller *ctrl) +{ + u64 err_addr; + + err_addr = brcmnand_read_reg(ctrl, BRCMNAND_CORR_ADDR); + err_addr |= ((u64)(brcmnand_read_reg(ctrl, + BRCMNAND_CORR_EXT_ADDR) + & 0xffff) << 32); + + return err_addr; +} + +static void brcmnand_set_cmd_addr(struct mtd_info *mtd, u64 addr) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_controller *ctrl = host->ctrl; + + brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS, + (host->cs << 16) | ((addr >> 32) & 0xffff)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS); + brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, + lower_32_bits(addr)); + (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); +} + static inline u16 brcmnand_cs_offset(struct brcmnand_controller *ctrl, int cs, enum brcmnand_cs_reg reg) { @@ -1242,9 +1290,12 @@ static void brcmnand_send_cmd(struct brcmnand_host *host, int cmd) { struct brcmnand_controller *ctrl = host->ctrl; int ret; + u64 cmd_addr; + + cmd_addr = brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); + + dev_dbg(ctrl->dev, "send native cmd %d addr 0x%llx\n", cmd, cmd_addr);
- dev_dbg(ctrl->dev, "send native cmd %d addr_lo 0x%x\n", cmd, - brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS)); BUG_ON(ctrl->cmd_pending != 0); ctrl->cmd_pending = cmd;
@@ -1417,12 +1468,7 @@ static void brcmnand_cmdfunc(struct mtd_info *mtd, unsigned command, if (!native_cmd) return;
- brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS, - (host->cs << 16) | ((addr >> 32) & 0xffff)); - (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS); - brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, lower_32_bits(addr)); - (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); - + brcmnand_set_cmd_addr(mtd, addr); brcmnand_send_cmd(host, native_cmd); brcmnand_waitfunc(mtd, chip);
@@ -1652,20 +1698,10 @@ static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip, struct brcmnand_controller *ctrl = host->ctrl; int i, j, ret = 0;
- /* Clear error addresses */ - brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_ADDR, 0); - brcmnand_write_reg(ctrl, BRCMNAND_CORR_ADDR, 0); - brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_EXT_ADDR, 0); - brcmnand_write_reg(ctrl, BRCMNAND_CORR_EXT_ADDR, 0); - - brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS, - (host->cs << 16) | ((addr >> 32) & 0xffff)); - (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS); + brcmnand_clear_ecc_addr(ctrl);
for (i = 0; i < trans; i++, addr += FC_BYTES) { - brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, - lower_32_bits(addr)); - (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); + brcmnand_set_cmd_addr(mtd, addr); /* SPARE_AREA_READ does not use ECC, so just use PAGE_READ */ brcmnand_send_cmd(host, CMD_PAGE_READ); brcmnand_waitfunc(mtd, chip); @@ -1685,21 +1721,15 @@ static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip, host->hwcfg.sector_size_1k);
if (ret != -EBADMSG) { - *err_addr = brcmnand_read_reg(ctrl, - BRCMNAND_UNCORR_ADDR) | - ((u64)(brcmnand_read_reg(ctrl, - BRCMNAND_UNCORR_EXT_ADDR) - & 0xffff) << 32); + *err_addr = brcmnand_get_uncorrecc_addr(ctrl); + if (*err_addr) ret = -EBADMSG; }
if (!ret) { - *err_addr = brcmnand_read_reg(ctrl, - BRCMNAND_CORR_ADDR) | - ((u64)(brcmnand_read_reg(ctrl, - BRCMNAND_CORR_EXT_ADDR) - & 0xffff) << 32); + *err_addr = brcmnand_get_correcc_addr(ctrl); + if (*err_addr) ret = -EUCLEAN; } @@ -1773,7 +1803,7 @@ static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip, dev_dbg(ctrl->dev, "read %llx -> %p\n", (unsigned long long)addr, buf);
try_dmaread: - brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_COUNT, 0); + brcmnand_clear_ecc_addr(ctrl);
#ifndef __UBOOT__ if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) { @@ -1927,15 +1957,9 @@ static int brcmnand_write(struct mtd_info *mtd, struct nand_chip *chip, } #endif /* __UBOOT__ */
- brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS, - (host->cs << 16) | ((addr >> 32) & 0xffff)); - (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS); - for (i = 0; i < trans; i++, addr += FC_BYTES) { /* full address MUST be set before populating FC */ - brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, - lower_32_bits(addr)); - (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); + brcmnand_set_cmd_addr(mtd, addr);
if (buf) { brcmnand_soc_data_bus_prepare(ctrl->soc, false);

From: Kamal Dasu kdasu.kdev@gmail.com
This change adds support for brcm NAND v7.3 controller. This controller uses a newer version of flash_dma engine and change mostly implements these differences.
Signed-off-by: Kamal Dasu kdasu.kdev@gmail.com Signed-off-by: Miquel Raynal miquel.raynal@bootlin.com [Ported to U-Boot from the Linux kernel] Signed-off-by: Linus Walleij linus.walleij@linaro.org --- drivers/mtd/nand/raw/brcmnand/brcmnand.c | 106 ++++++++++++++++++----- 1 file changed, 84 insertions(+), 22 deletions(-)
diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index ede51def5063..ef73205d8c18 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -86,6 +86,12 @@ struct brcm_nand_dma_desc { #define FLASH_DMA_ECC_ERROR (1 << 8) #define FLASH_DMA_CORR_ERROR (1 << 9)
+/* Bitfields for DMA_MODE */ +#define FLASH_DMA_MODE_STOP_ON_ERROR BIT(1) /* stop in Uncorr ECC error */ +#define FLASH_DMA_MODE_MODE BIT(0) /* link list */ +#define FLASH_DMA_MODE_MASK (FLASH_DMA_MODE_STOP_ON_ERROR | \ + FLASH_DMA_MODE_MODE) + /* 512B flash cache in the NAND controller HW */ #define FC_SHIFT 9U #define FC_BYTES 512U @@ -98,6 +104,53 @@ struct brcm_nand_dma_desc { #define NAND_CTRL_RDY (INTFC_CTLR_READY | INTFC_FLASH_READY) #define NAND_POLL_STATUS_TIMEOUT_MS 100
+/* flash_dma registers */ +enum flash_dma_reg { + FLASH_DMA_REVISION = 0, + FLASH_DMA_FIRST_DESC, + FLASH_DMA_FIRST_DESC_EXT, + FLASH_DMA_CTRL, + FLASH_DMA_MODE, + FLASH_DMA_STATUS, + FLASH_DMA_INTERRUPT_DESC, + FLASH_DMA_INTERRUPT_DESC_EXT, + FLASH_DMA_ERROR_STATUS, + FLASH_DMA_CURRENT_DESC, + FLASH_DMA_CURRENT_DESC_EXT, +}; + +#ifndef __UBOOT__ +/* flash_dma registers v1*/ +static const u16 flash_dma_regs_v1[] = { + [FLASH_DMA_REVISION] = 0x00, + [FLASH_DMA_FIRST_DESC] = 0x04, + [FLASH_DMA_FIRST_DESC_EXT] = 0x08, + [FLASH_DMA_CTRL] = 0x0c, + [FLASH_DMA_MODE] = 0x10, + [FLASH_DMA_STATUS] = 0x14, + [FLASH_DMA_INTERRUPT_DESC] = 0x18, + [FLASH_DMA_INTERRUPT_DESC_EXT] = 0x1c, + [FLASH_DMA_ERROR_STATUS] = 0x20, + [FLASH_DMA_CURRENT_DESC] = 0x24, + [FLASH_DMA_CURRENT_DESC_EXT] = 0x28, +}; + +/* flash_dma registers v4 */ +static const u16 flash_dma_regs_v4[] = { + [FLASH_DMA_REVISION] = 0x00, + [FLASH_DMA_FIRST_DESC] = 0x08, + [FLASH_DMA_FIRST_DESC_EXT] = 0x0c, + [FLASH_DMA_CTRL] = 0x10, + [FLASH_DMA_MODE] = 0x14, + [FLASH_DMA_STATUS] = 0x18, + [FLASH_DMA_INTERRUPT_DESC] = 0x20, + [FLASH_DMA_INTERRUPT_DESC_EXT] = 0x24, + [FLASH_DMA_ERROR_STATUS] = 0x28, + [FLASH_DMA_CURRENT_DESC] = 0x30, + [FLASH_DMA_CURRENT_DESC_EXT] = 0x34, +}; +#endif /* __UBOOT__ */ + /* Controller feature flags */ enum { BRCMNAND_HAS_1K_SECTORS = BIT(0), @@ -135,6 +188,8 @@ struct brcmnand_controller { /* List of NAND hosts (one for each chip-select) */ struct list_head host_list;
+ /* flash_dma reg */ + const u16 *flash_dma_offsets; struct brcm_nand_dma_desc *dma_desc; dma_addr_t dma_pa;
@@ -473,7 +528,7 @@ static int brcmnand_revision_init(struct brcmnand_controller *ctrl) /* Register offsets */ if (ctrl->nand_version >= 0x0702) ctrl->reg_offsets = brcmnand_regs_v72; - else if (ctrl->nand_version >= 0x0701) + else if (ctrl->nand_version == 0x0701) ctrl->reg_offsets = brcmnand_regs_v71; else if (ctrl->nand_version >= 0x0600) ctrl->reg_offsets = brcmnand_regs_v60; @@ -518,7 +573,7 @@ static int brcmnand_revision_init(struct brcmnand_controller *ctrl) }
/* Maximum spare area sector size (per 512B) */ - if (ctrl->nand_version >= 0x0702) + if (ctrl->nand_version == 0x0702) ctrl->max_oob = 128; else if (ctrl->nand_version >= 0x0600) ctrl->max_oob = 64; @@ -553,6 +608,17 @@ static int brcmnand_revision_init(struct brcmnand_controller *ctrl) return 0; }
+#ifndef __UBOOT__ +static void brcmnand_flash_dma_revision_init(struct brcmnand_controller *ctrl) +{ + /* flash_dma register offsets */ + if (ctrl->nand_version >= 0x0703) + ctrl->flash_dma_offsets = flash_dma_regs_v4; + else + ctrl->flash_dma_offsets = flash_dma_regs_v1; +} +#endif /* __UBOOT__ */ + static inline u32 brcmnand_read_reg(struct brcmnand_controller *ctrl, enum brcmnand_reg reg) { @@ -675,7 +741,7 @@ static void brcmnand_wr_corr_thresh(struct brcmnand_host *host, u8 val) enum brcmnand_reg reg = BRCMNAND_CORR_THRESHOLD; int cs = host->cs;
- if (ctrl->nand_version >= 0x0702) + if (ctrl->nand_version == 0x0702) bits = 7; else if (ctrl->nand_version >= 0x0600) bits = 6; @@ -729,7 +795,7 @@ enum {
static inline u32 brcmnand_spare_area_mask(struct brcmnand_controller *ctrl) { - if (ctrl->nand_version >= 0x0702) + if (ctrl->nand_version == 0x0702) return GENMASK(7, 0); else if (ctrl->nand_version >= 0x0600) return GENMASK(6, 0); @@ -877,20 +943,6 @@ static inline void brcmnand_set_wp(struct brcmnand_controller *ctrl, bool en) * Flash DMA ***********************************************************************/
-enum flash_dma_reg { - FLASH_DMA_REVISION = 0x00, - FLASH_DMA_FIRST_DESC = 0x04, - FLASH_DMA_FIRST_DESC_EXT = 0x08, - FLASH_DMA_CTRL = 0x0c, - FLASH_DMA_MODE = 0x10, - FLASH_DMA_STATUS = 0x14, - FLASH_DMA_INTERRUPT_DESC = 0x18, - FLASH_DMA_INTERRUPT_DESC_EXT = 0x1c, - FLASH_DMA_ERROR_STATUS = 0x20, - FLASH_DMA_CURRENT_DESC = 0x24, - FLASH_DMA_CURRENT_DESC_EXT = 0x28, -}; - static inline bool has_flash_dma(struct brcmnand_controller *ctrl) { return ctrl->flash_dma_base; @@ -906,14 +958,19 @@ static inline bool flash_dma_buf_ok(const void *buf) #endif /* __UBOOT__ */ }
-static inline void flash_dma_writel(struct brcmnand_controller *ctrl, u8 offs, - u32 val) +static inline void flash_dma_writel(struct brcmnand_controller *ctrl, + enum flash_dma_reg dma_reg, u32 val) { + u16 offs = ctrl->flash_dma_offsets[dma_reg]; + brcmnand_writel(val, ctrl->flash_dma_base + offs); }
-static inline u32 flash_dma_readl(struct brcmnand_controller *ctrl, u8 offs) +static inline u32 flash_dma_readl(struct brcmnand_controller *ctrl, + enum flash_dma_reg dma_reg) { + u16 offs = ctrl->flash_dma_offsets[dma_reg]; + return brcmnand_readl(ctrl->flash_dma_base + offs); }
@@ -2522,6 +2579,7 @@ static const struct of_device_id brcmnand_of_match[] = { { .compatible = "brcm,brcmnand-v7.0" }, { .compatible = "brcm,brcmnand-v7.1" }, { .compatible = "brcm,brcmnand-v7.2" }, + { .compatible = "brcm,brcmnand-v7.3" }, {}, }; MODULE_DEVICE_TABLE(of, brcmnand_of_match); @@ -2652,7 +2710,11 @@ int brcmnand_probe(struct udevice *dev, struct brcmnand_soc *soc) goto err; }
- flash_dma_writel(ctrl, FLASH_DMA_MODE, 1); /* linked-list */ + /* initialize the dma version */ + brcmnand_flash_dma_revision_init(ctrl); + + /* linked-list and stop on error */ + flash_dma_writel(ctrl, FLASH_DMA_MODE, FLASH_DMA_MODE_MASK); flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0);
/* Allocate descriptor(s) */

From: Claire Lin claire.lin@broadcom.com
In brcmstb_nand_verify_erased_page(), the ECC chunk pointer calculation while correcting erased page bitflips is wrong, fix it.
Fixes: 02b88eea9f9c ("mtd: brcmnand: Add check for erased page bitflips") Signed-off-by: Claire Lin claire.lin@broadcom.com Reviewed-by: Ray Jui ray.jui@broadcom.com Signed-off-by: Kamal Dasu kdasu.kdev@gmail.com Signed-off-by: Miquel Raynal miquel.raynal@bootlin.com [Ported to U-Boot from the Linux kernel] Signed-off-by: Linus Walleij linus.walleij@linaro.org --- drivers/mtd/nand/raw/brcmnand/brcmnand.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index ef73205d8c18..bbaf403d2a03 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -1817,6 +1817,7 @@ static int brcmstb_nand_verify_erased_page(struct mtd_info *mtd, int bitflips = 0; int page = addr >> chip->page_shift; int ret; + void *ecc_chunk;
if (!buf) { #ifndef __UBOOT__ @@ -1836,7 +1837,9 @@ static int brcmstb_nand_verify_erased_page(struct mtd_info *mtd, return ret;
for (i = 0; i < chip->ecc.steps; i++, oob += sas) { - ret = nand_check_erased_ecc_chunk(buf, chip->ecc.size, + ecc_chunk = buf + chip->ecc.size * i; + ret = nand_check_erased_ecc_chunk(ecc_chunk, + chip->ecc.size, oob, sas, NULL, 0, chip->ecc.strength); if (ret < 0)

From: Kamal Dasu kdasu.kdev@gmail.com
This change adds support for flash dma v0.0.
Signed-off-by: Kamal Dasu kdasu.kdev@gmail.com Signed-off-by: Miquel Raynal miquel.raynal@bootlin.com [Ported to U-Boot from the Linux kernel] Signed-off-by: Linus Walleij linus.walleij@linaro.org --- drivers/mtd/nand/raw/brcmnand/brcmnand.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index bbaf403d2a03..fdc1fc6c1043 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -120,6 +120,18 @@ enum flash_dma_reg { };
#ifndef __UBOOT__ +/* flash_dma registers v0*/ +static const u16 flash_dma_regs_v0[] = { + [FLASH_DMA_REVISION] = 0x00, + [FLASH_DMA_FIRST_DESC] = 0x04, + [FLASH_DMA_CTRL] = 0x08, + [FLASH_DMA_MODE] = 0x0c, + [FLASH_DMA_STATUS] = 0x10, + [FLASH_DMA_INTERRUPT_DESC] = 0x14, + [FLASH_DMA_ERROR_STATUS] = 0x18, + [FLASH_DMA_CURRENT_DESC] = 0x1c, +}; + /* flash_dma registers v1*/ static const u16 flash_dma_regs_v1[] = { [FLASH_DMA_REVISION] = 0x00, @@ -614,6 +626,8 @@ static void brcmnand_flash_dma_revision_init(struct brcmnand_controller *ctrl) /* flash_dma register offsets */ if (ctrl->nand_version >= 0x0703) ctrl->flash_dma_offsets = flash_dma_regs_v4; + else if (ctrl->nand_version == 0x0602) + ctrl->flash_dma_offsets = flash_dma_regs_v0; else ctrl->flash_dma_offsets = flash_dma_regs_v1; } @@ -1697,8 +1711,11 @@ static void brcmnand_dma_run(struct brcmnand_host *host, dma_addr_t desc)
flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC, lower_32_bits(desc)); (void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC); - flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC_EXT, upper_32_bits(desc)); - (void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC_EXT); + if (ctrl->nand_version > 0x0602) { + flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC_EXT, + upper_32_bits(desc)); + (void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC_EXT); + }
/* Start FLASH_DMA engine */ ctrl->dma_pending = true;

From: Álvaro Fernández Rojas noltari@gmail.com
First 2 bytes are used in large-page nand.
Fixes: ef5eeea6e911 ("mtd: nand: brcm: switch to mtd_ooblayout_ops") Cc: stable@vger.kernel.org Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Signed-off-by: Miquel Raynal miquel.raynal@bootlin.com Link: https://lore.kernel.org/linux-mtd/20200512075733.745374-2-noltari@gmail.com [Ported to U-Boot from the Linux kernel] Signed-off-by: Linus Walleij linus.walleij@linaro.org --- drivers/mtd/nand/raw/brcmnand/brcmnand.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index fdc1fc6c1043..7cb6f2651250 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -1059,11 +1059,14 @@ static int brcmnand_hamming_ooblayout_free(struct mtd_info *mtd, int section, if (!section) { /* * Small-page NAND use byte 6 for BBI while large-page - * NAND use byte 0. + * NAND use bytes 0 and 1. */ - if (cfg->page_size > 512) - oobregion->offset++; - oobregion->length--; + if (cfg->page_size > 512) { + oobregion->offset += 2; + oobregion->length -= 2; + } else { + oobregion->length--; + } } }

From: Álvaro Fernández Rojas noltari@gmail.com
The current code generates 8 oob sections: S1 1-5 ECC 6-8 S2 9-15 S3 16-21 ECC 22-24 S4 25-31 S5 32-37 ECC 38-40 S6 41-47 S7 48-53 ECC 54-56 S8 57-63
Change it by merging continuous sections: S1 1-5 ECC 6-8 S2 9-21 ECC 22-24 S3 25-37 ECC 38-40 S4 41-53 ECC 54-56 S5 57-63
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Signed-off-by: Miquel Raynal miquel.raynal@bootlin.com Link: https://lore.kernel.org/linux-mtd/20200512075733.745374-3-noltari@gmail.com [Ported to U-Boot from the Linux kernel] Signed-off-by: Linus Walleij linus.walleij@linaro.org --- drivers/mtd/nand/raw/brcmnand/brcmnand.c | 35 +++++++++++------------- 1 file changed, 16 insertions(+), 19 deletions(-)
diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index 7cb6f2651250..d082801b8306 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -1043,33 +1043,30 @@ static int brcmnand_hamming_ooblayout_free(struct mtd_info *mtd, int section, struct brcmnand_cfg *cfg = &host->hwcfg; int sas = cfg->spare_area_size << cfg->sector_size_1k; int sectors = cfg->page_size / (512 << cfg->sector_size_1k); + u32 next;
- if (section >= sectors * 2) + if (section > sectors) return -ERANGE;
- oobregion->offset = (section / 2) * sas; + next = (section * sas); + if (section < sectors) + next += 6;
- if (section & 1) { - oobregion->offset += 9; - oobregion->length = 7; + if (section) { + oobregion->offset = ((section - 1) * sas) + 9; } else { - oobregion->length = 6; - - /* First sector of each page may have BBI */ - if (!section) { - /* - * Small-page NAND use byte 6 for BBI while large-page - * NAND use bytes 0 and 1. - */ - if (cfg->page_size > 512) { - oobregion->offset += 2; - oobregion->length -= 2; - } else { - oobregion->length--; - } + if (cfg->page_size > 512) { + /* Large page NAND uses first 2 bytes for BBI */ + oobregion->offset = 2; + } else { + /* Small page NAND uses last byte before ECC for BBI */ + oobregion->offset = 0; + next--; } }
+ oobregion->length = next - oobregion->offset; + return 0; }

From: Álvaro Fernández Rojas noltari@gmail.com
The current code checks that the whole OOB area is erased. This is a problem when JFFS2 cleanmarkers are added to the OOB, since it will fail due to the usable OOB bytes not being 0xff. Correct this by only checking that data and ECC bytes aren't 0xff.
Fixes: 02b88eea9f9c ("mtd: brcmnand: Add check for erased page bitflips") Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Signed-off-by: Miquel Raynal miquel.raynal@bootlin.com Link: https://lore.kernel.org/linux-mtd/20200512082451.771212-1-noltari@gmail.com [Ported to U-Boot from the Linux kernel] Signed-off-by: Linus Walleij linus.walleij@linaro.org --- drivers/mtd/nand/raw/brcmnand/brcmnand.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-)
diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index d082801b8306..b87e3e0656df 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -1829,11 +1829,12 @@ static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip, static int brcmstb_nand_verify_erased_page(struct mtd_info *mtd, struct nand_chip *chip, void *buf, u64 addr) { - int i, sas; - void *oob = chip->oob_poi; + struct mtd_oob_region ecc; + int i; int bitflips = 0; int page = addr >> chip->page_shift; int ret; + void *ecc_bytes; void *ecc_chunk;
if (!buf) { @@ -1846,18 +1847,20 @@ static int brcmstb_nand_verify_erased_page(struct mtd_info *mtd, chip->pagebuf = -1; }
- sas = mtd->oobsize / chip->ecc.steps; - /* read without ecc for verification */ ret = chip->ecc.read_page_raw(mtd, chip, buf, true, page); if (ret) return ret;
- for (i = 0; i < chip->ecc.steps; i++, oob += sas) { + for (i = 0; i < chip->ecc.steps; i++) { ecc_chunk = buf + chip->ecc.size * i; - ret = nand_check_erased_ecc_chunk(ecc_chunk, - chip->ecc.size, - oob, sas, NULL, 0, + + mtd_ooblayout_ecc(mtd, i, &ecc); + ecc_bytes = chip->oob_poi + ecc.offset; + + ret = nand_check_erased_ecc_chunk(ecc_chunk, chip->ecc.size, + ecc_bytes, ecc.length, + NULL, 0, chip->ecc.strength); if (ret < 0) return ret;

From: Álvaro Fernández Rojas noltari@gmail.com
These registers are also used on v3.3.
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Reviewed-by: Miquel Raynal miquel.raynal@bootlin.com Acked-by: Florian Fainelli f.fainelli@gmail.com Signed-off-by: Miquel Raynal miquel.raynal@bootlin.com Link: https://lore.kernel.org/linux-mtd/20200522121524.4161539-2-noltari@gmail.com [Ported to U-Boot from the Linux kernel] Signed-off-by: Linus Walleij linus.walleij@linaro.org --- drivers/mtd/nand/raw/brcmnand/brcmnand.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index b87e3e0656df..effc7f1ea802 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -293,8 +293,8 @@ enum brcmnand_reg { BRCMNAND_FC_BASE, };
-/* BRCMNAND v4.0 */ -static const u16 brcmnand_regs_v40[] = { +/* BRCMNAND v3.3-v4.0 */ +static const u16 brcmnand_regs_v33[] = { [BRCMNAND_CMD_START] = 0x04, [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, [BRCMNAND_CMD_ADDRESS] = 0x0c, @@ -546,8 +546,8 @@ static int brcmnand_revision_init(struct brcmnand_controller *ctrl) ctrl->reg_offsets = brcmnand_regs_v60; else if (ctrl->nand_version >= 0x0500) ctrl->reg_offsets = brcmnand_regs_v50; - else if (ctrl->nand_version >= 0x0400) - ctrl->reg_offsets = brcmnand_regs_v40; + else if (ctrl->nand_version >= 0x0303) + ctrl->reg_offsets = brcmnand_regs_v33;
/* Chip-select stride */ if (ctrl->nand_version >= 0x0701)

From: Álvaro Fernández Rojas noltari@gmail.com
Only v3.3-v5.0 have a different CS0 layout. Controllers before v3.3 use the same layout for every CS.
Fixes: 27c5b17cd1b1 ("mtd: nand: add NAND driver "library" for Broadcom STB NAND controller") Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Acked-by: Florian Fainelli f.fainelli@gmail.com Signed-off-by: Miquel Raynal miquel.raynal@bootlin.com Link: https://lore.kernel.org/linux-mtd/20200522121524.4161539-3-noltari@gmail.com [Ported to U-Boot from the Linux kernel] Signed-off-by: Linus Walleij linus.walleij@linaro.org --- drivers/mtd/nand/raw/brcmnand/brcmnand.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index effc7f1ea802..c02d34ffbc95 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -561,8 +561,9 @@ static int brcmnand_revision_init(struct brcmnand_controller *ctrl) } else { ctrl->cs_offsets = brcmnand_cs_offsets;
- /* v5.0 and earlier has a different CS0 offset layout */ - if (ctrl->nand_version <= 0x0500) + /* v3.3-5.0 have a different CS0 offset layout */ + if (ctrl->nand_version >= 0x0303 && + ctrl->nand_version <= 0x0500) ctrl->cs0_offsets = brcmnand_cs_offsets_cs0; }

From: Álvaro Fernández Rojas noltari@gmail.com
Current pages sizes apply to controllers after v3.4
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Acked-by: Florian Fainelli f.fainelli@gmail.com Signed-off-by: Miquel Raynal miquel.raynal@bootlin.com Link: https://lore.kernel.org/linux-mtd/20200522121524.4161539-4-noltari@gmail.com [Ported to U-Boot from the Linux kernel] Signed-off-by: Linus Walleij linus.walleij@linaro.org --- drivers/mtd/nand/raw/brcmnand/brcmnand.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index c02d34ffbc95..934981e2f3fe 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -526,7 +526,7 @@ static int brcmnand_revision_init(struct brcmnand_controller *ctrl) { static const unsigned int block_sizes_v6[] = { 8, 16, 128, 256, 512, 1024, 2048, 0 }; static const unsigned int block_sizes_v4[] = { 16, 128, 8, 512, 256, 1024, 2048, 0 }; - static const unsigned int page_sizes[] = { 512, 2048, 4096, 8192, 0 }; + static const unsigned int page_sizes_v3_4[] = { 512, 2048, 4096, 8192, 0 };
ctrl->nand_version = nand_readreg(ctrl, 0) & 0xffff;
@@ -573,7 +573,7 @@ static int brcmnand_revision_init(struct brcmnand_controller *ctrl) ctrl->max_page_size = 16 * 1024; ctrl->max_block_size = 2 * 1024 * 1024; } else { - ctrl->page_sizes = page_sizes; + ctrl->page_sizes = page_sizes_v3_4; if (ctrl->nand_version >= 0x0600) ctrl->block_sizes = block_sizes_v6; else

From: Álvaro Fernández Rojas noltari@gmail.com
v2.1: tested on Netgear DGND3700v1 (BCM6368) v2.2: tested on Netgear DGND3700v2 (BCM6362)
Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Acked-by: Florian Fainelli f.fainelli@gmail.com Signed-off-by: Miquel Raynal miquel.raynal@bootlin.com Link: https://lore.kernel.org/linux-mtd/20200522121524.4161539-6-noltari@gmail.com [Ported to U-Boot from the Linux kernel] Signed-off-by: Linus Walleij linus.walleij@linaro.org --- drivers/mtd/nand/raw/brcmnand/brcmnand.c | 85 +++++++++++++++++++++--- 1 file changed, 76 insertions(+), 9 deletions(-)
diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index 934981e2f3fe..2cca897ab3c9 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -217,6 +217,7 @@ struct brcmnand_controller { const unsigned int *block_sizes; unsigned int max_page_size; const unsigned int *page_sizes; + unsigned int page_size_shift; unsigned int max_oob; u32 features;
@@ -293,6 +294,36 @@ enum brcmnand_reg { BRCMNAND_FC_BASE, };
+/* BRCMNAND v2.1-v2.2 */ +static const u16 brcmnand_regs_v21[] = { + [BRCMNAND_CMD_START] = 0x04, + [BRCMNAND_CMD_EXT_ADDRESS] = 0x08, + [BRCMNAND_CMD_ADDRESS] = 0x0c, + [BRCMNAND_INTFC_STATUS] = 0x5c, + [BRCMNAND_CS_SELECT] = 0x14, + [BRCMNAND_CS_XOR] = 0x18, + [BRCMNAND_LL_OP] = 0, + [BRCMNAND_CS0_BASE] = 0x40, + [BRCMNAND_CS1_BASE] = 0, + [BRCMNAND_CORR_THRESHOLD] = 0, + [BRCMNAND_CORR_THRESHOLD_EXT] = 0, + [BRCMNAND_UNCORR_COUNT] = 0, + [BRCMNAND_CORR_COUNT] = 0, + [BRCMNAND_CORR_EXT_ADDR] = 0x60, + [BRCMNAND_CORR_ADDR] = 0x64, + [BRCMNAND_UNCORR_EXT_ADDR] = 0x68, + [BRCMNAND_UNCORR_ADDR] = 0x6c, + [BRCMNAND_SEMAPHORE] = 0x50, + [BRCMNAND_ID] = 0x54, + [BRCMNAND_ID_EXT] = 0, + [BRCMNAND_LL_RDATA] = 0, + [BRCMNAND_OOB_READ_BASE] = 0x20, + [BRCMNAND_OOB_READ_10_BASE] = 0, + [BRCMNAND_OOB_WRITE_BASE] = 0x30, + [BRCMNAND_OOB_WRITE_10_BASE] = 0, + [BRCMNAND_FC_BASE] = 0x200, +}; + /* BRCMNAND v3.3-v4.0 */ static const u16 brcmnand_regs_v33[] = { [BRCMNAND_CMD_START] = 0x04, @@ -491,6 +522,9 @@ enum { CFG_BUS_WIDTH = BIT(CFG_BUS_WIDTH_SHIFT), CFG_DEVICE_SIZE_SHIFT = 24,
+ /* Only for v2.1 */ + CFG_PAGE_SIZE_SHIFT_v2_1 = 30, + /* Only for pre-v7.1 (with no CFG_EXT register) */ CFG_PAGE_SIZE_SHIFT = 20, CFG_BLK_SIZE_SHIFT = 28, @@ -526,12 +560,16 @@ static int brcmnand_revision_init(struct brcmnand_controller *ctrl) { static const unsigned int block_sizes_v6[] = { 8, 16, 128, 256, 512, 1024, 2048, 0 }; static const unsigned int block_sizes_v4[] = { 16, 128, 8, 512, 256, 1024, 2048, 0 }; + static const unsigned int block_sizes_v2_2[] = { 16, 128, 8, 512, 256, 0 }; + static const unsigned int block_sizes_v2_1[] = { 16, 128, 8, 512, 0 }; static const unsigned int page_sizes_v3_4[] = { 512, 2048, 4096, 8192, 0 }; + static const unsigned int page_sizes_v2_2[] = { 512, 2048, 4096, 0 }; + static const unsigned int page_sizes_v2_1[] = { 512, 2048, 0 };
ctrl->nand_version = nand_readreg(ctrl, 0) & 0xffff;
- /* Only support v4.0+? */ - if (ctrl->nand_version < 0x0400) { + /* Only support v2.1+ */ + if (ctrl->nand_version < 0x0201) { dev_err(ctrl->dev, "version %#x not supported\n", ctrl->nand_version); return -ENODEV; @@ -548,6 +586,8 @@ static int brcmnand_revision_init(struct brcmnand_controller *ctrl) ctrl->reg_offsets = brcmnand_regs_v50; else if (ctrl->nand_version >= 0x0303) ctrl->reg_offsets = brcmnand_regs_v33; + else if (ctrl->nand_version >= 0x0201) + ctrl->reg_offsets = brcmnand_regs_v21;
/* Chip-select stride */ if (ctrl->nand_version >= 0x0701) @@ -573,14 +613,32 @@ static int brcmnand_revision_init(struct brcmnand_controller *ctrl) ctrl->max_page_size = 16 * 1024; ctrl->max_block_size = 2 * 1024 * 1024; } else { - ctrl->page_sizes = page_sizes_v3_4; + if (ctrl->nand_version >= 0x0304) + ctrl->page_sizes = page_sizes_v3_4; + else if (ctrl->nand_version >= 0x0202) + ctrl->page_sizes = page_sizes_v2_2; + else + ctrl->page_sizes = page_sizes_v2_1; + + if (ctrl->nand_version >= 0x0202) + ctrl->page_size_shift = CFG_PAGE_SIZE_SHIFT; + else + ctrl->page_size_shift = CFG_PAGE_SIZE_SHIFT_v2_1; + if (ctrl->nand_version >= 0x0600) ctrl->block_sizes = block_sizes_v6; - else + else if (ctrl->nand_version >= 0x0400) ctrl->block_sizes = block_sizes_v4; + else if (ctrl->nand_version >= 0x0202) + ctrl->block_sizes = block_sizes_v2_2; + else + ctrl->block_sizes = block_sizes_v2_1;
if (ctrl->nand_version < 0x0400) { - ctrl->max_page_size = 4096; + if (ctrl->nand_version < 0x0202) + ctrl->max_page_size = 2048; + else + ctrl->max_page_size = 4096; ctrl->max_block_size = 512 * 1024; } } @@ -756,6 +814,9 @@ static void brcmnand_wr_corr_thresh(struct brcmnand_host *host, u8 val) enum brcmnand_reg reg = BRCMNAND_CORR_THRESHOLD; int cs = host->cs;
+ if (!ctrl->reg_offsets[reg]) + return; + if (ctrl->nand_version == 0x0702) bits = 7; else if (ctrl->nand_version >= 0x0600) @@ -814,8 +875,10 @@ static inline u32 brcmnand_spare_area_mask(struct brcmnand_controller *ctrl) return GENMASK(7, 0); else if (ctrl->nand_version >= 0x0600) return GENMASK(6, 0); - else + else if (ctrl->nand_version >= 0x0303) return GENMASK(5, 0); + else + return GENMASK(4, 0); }
#define NAND_ACC_CONTROL_ECC_SHIFT 16 @@ -2201,7 +2264,7 @@ static int brcmnand_set_cfg(struct brcmnand_host *host, (!!(cfg->device_width == 16) << CFG_BUS_WIDTH_SHIFT) | (device_size << CFG_DEVICE_SIZE_SHIFT); if (cfg_offs == cfg_ext_offs) { - tmp |= (page_size << CFG_PAGE_SIZE_SHIFT) | + tmp |= (page_size << ctrl->page_size_shift) | (block_size << CFG_BLK_SIZE_SHIFT); nand_writereg(ctrl, cfg_offs, tmp); } else { @@ -2213,9 +2276,11 @@ static int brcmnand_set_cfg(struct brcmnand_host *host,
tmp = nand_readreg(ctrl, acc_control_offs); tmp &= ~brcmnand_ecc_level_mask(ctrl); - tmp |= cfg->ecc_level << NAND_ACC_CONTROL_ECC_SHIFT; tmp &= ~brcmnand_spare_area_mask(ctrl); - tmp |= cfg->spare_area_size; + if (ctrl->nand_version >= 0x0302) { + tmp |= cfg->ecc_level << NAND_ACC_CONTROL_ECC_SHIFT; + tmp |= cfg->spare_area_size; + } nand_writereg(ctrl, acc_control_offs, tmp);
brcmnand_set_sector_size_1k(host, cfg->sector_size_1k); @@ -2595,6 +2660,8 @@ const struct dev_pm_ops brcmnand_pm_ops = { EXPORT_SYMBOL_GPL(brcmnand_pm_ops);
static const struct of_device_id brcmnand_of_match[] = { + { .compatible = "brcm,brcmnand-v2.1" }, + { .compatible = "brcm,brcmnand-v2.2" }, { .compatible = "brcm,brcmnand-v4.0" }, { .compatible = "brcm,brcmnand-v5.0" }, { .compatible = "brcm,brcmnand-v6.0" },

From: Álvaro Fernández Rojas noltari@gmail.com
Hamming ECC doesn't cover the OOB data, so reading or writing OOB shall always be done without ECC enabled. This is a problem when adding JFFS2 cleanmarkers to erased blocks. If JFFS2 clenmarkers are added to the OOB with ECC enabled, OOB bytes will be changed from ff ff ff to 00 00 00, reporting incorrect ECC errors.
Fixes: 27c5b17cd1b1 ("mtd: nand: add NAND driver "library" for Broadcom STB NAND controller") Signed-off-by: Álvaro Fernández Rojas noltari@gmail.com Acked-by: Brian Norris computersforpeace@gmail.com Signed-off-by: Miquel Raynal miquel.raynal@bootlin.com Link: https://lore.kernel.org/linux-mtd/20210224080210.23686-1-noltari@gmail.com [Ported to U-Boot from the Linux kernel] Signed-off-by: Linus Walleij linus.walleij@linaro.org --- drivers/mtd/nand/raw/brcmnand/brcmnand.c | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index 2cca897ab3c9..ee3ff372b863 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -2567,6 +2567,12 @@ static int brcmnand_init_cs(struct brcmnand_host *host, ofnode dn) ret = nand_register(0, mtd); #endif /* __UBOOT__ */
+ /* If OOB is written with ECC enabled it will cause ECC errors */ + if (is_hamming_ecc(host->ctrl, &host->hwcfg)) { + chip->ecc.write_oob = brcmnand_write_oob_raw; + chip->ecc.read_oob = brcmnand_read_oob_raw; + } + return ret; }

Hi
On Sun, Jan 15, 2023 at 8:53 PM Linus Walleij linus.walleij@linaro.org wrote:
Hunting down a bug on my system I took to back-porting all reasonable changes from the Linux brcmnand driver that were not yet in the U-Boot derivative.
I noticed that a simple diff -ur between brcmnand.c between the file in Linux and U-Boot was possible to see what differs. Combining this with some git log --oneline manual comparison, fuzzing and manual intervention I backported a set of relevant patches from Linux that compiles and WorksForMe(TM).
The diff between Linux and U-Boot is much smaller after this, the main missing part are subsystem cosmetics changes and the EDU DMA mode support.
This was as much as I could bite off in one go.
All patches countersigned-off and marked as [backported].
Thank you for your time
It's nice if someone else tests this I think.
I don't have any board to test those changes
Boris Brezillon (1): mtd: nand: brcm: switch to mtd_ooblayout_ops
Claire Lin (1): mtd: rawnand: brcmnand: Fix ecc chunk calculation for erased page bitfips
Kamal Dasu (4): mtd: rawnand: brcmnand: Fix BCH ECC layout for large page NAND parts mtd: rawnand: brcmnand: Refactored code to introduce helper functions mtd: rawnand: brcmnand: Add support for v7.3 controller mtd: nand: brcmnand: Add support for flash-dma v0
Álvaro Fernández Rojas (8): mtd: rawnand: brcmnand: fix hamming oob layout mtd: rawnand: brcmnand: improve hamming oob layout mtd: rawnand: brcmnand: correctly verify erased pages mtd: rawnand: brcmnand: rename v4 registers mtd: rawnand: brcmnand: fix CS0 layout mtd: rawnand: brcmnand: rename page sizes mtd: rawnand: brcmnand: support v2.1-v2.2 controllers mtd: rawnand: brcmnand: fix OOB R/W with Hamming ECC
drivers/mtd/nand/raw/brcmnand/brcmnand.c | 615 ++++++++++++++++------- 1 file changed, 425 insertions(+), 190 deletions(-)
Plan to review this week and run ci-pipeline to include in nand-next
MIchael
-- 2.39.0

Hi Linus,
I will try to review and test your patches late this week or early next week. I don't have those old chips (v2.1, v2.2, v4) but will test on some of recent Broadcom broadband(BCMBCA) SoCs with v7.0 and 7.1 controller.
Thanks, William
On 01/16/2023 11:11 AM, Michael Nazzareno Trimarchi wrote:
Hi
On Sun, Jan 15, 2023 at 8:53 PM Linus Walleij linus.walleij@linaro.org wrote:
Hunting down a bug on my system I took to back-porting all reasonable changes from the Linux brcmnand driver that were not yet in the U-Boot derivative.
I noticed that a simple diff -ur between brcmnand.c between the file in Linux and U-Boot was possible to see what differs. Combining this with some git log --oneline manual comparison, fuzzing and manual intervention I backported a set of relevant patches from Linux that compiles and WorksForMe(TM).
The diff between Linux and U-Boot is much smaller after this, the main missing part are subsystem cosmetics changes and the EDU DMA mode support.
This was as much as I could bite off in one go.
All patches countersigned-off and marked as [backported].
Thank you for your time
It's nice if someone else tests this I think.
I don't have any board to test those changes
Boris Brezillon (1): mtd: nand: brcm: switch to mtd_ooblayout_ops
Claire Lin (1): mtd: rawnand: brcmnand: Fix ecc chunk calculation for erased page bitfips
Kamal Dasu (4): mtd: rawnand: brcmnand: Fix BCH ECC layout for large page NAND parts mtd: rawnand: brcmnand: Refactored code to introduce helper functions mtd: rawnand: brcmnand: Add support for v7.3 controller mtd: nand: brcmnand: Add support for flash-dma v0
Álvaro Fernández Rojas (8): mtd: rawnand: brcmnand: fix hamming oob layout mtd: rawnand: brcmnand: improve hamming oob layout mtd: rawnand: brcmnand: correctly verify erased pages mtd: rawnand: brcmnand: rename v4 registers mtd: rawnand: brcmnand: fix CS0 layout mtd: rawnand: brcmnand: rename page sizes mtd: rawnand: brcmnand: support v2.1-v2.2 controllers mtd: rawnand: brcmnand: fix OOB R/W with Hamming ECC
drivers/mtd/nand/raw/brcmnand/brcmnand.c | 615 ++++++++++++++++------- 1 file changed, 425 insertions(+), 190 deletions(-)
Plan to review this week and run ci-pipeline to include in nand-next
MIchael
-- 2.39.0

Hi all,
On Tue, Jan 17, 2023 at 7:25 AM William Zhang william.zhang@broadcom.com wrote:
Hi Linus,
I will try to review and test your patches late this week or early next week. I don't have those old chips (v2.1, v2.2, v4) but will test on some of recent Broadcom broadband(BCMBCA) SoCs with v7.0 and 7.1 controller.
Thanks, William
On 01/16/2023 11:11 AM, Michael Nazzareno Trimarchi wrote:
Hi
On Sun, Jan 15, 2023 at 8:53 PM Linus Walleij linus.walleij@linaro.org wrote:
Hunting down a bug on my system I took to back-porting all reasonable changes from the Linux brcmnand driver that were not yet in the U-Boot derivative.
I noticed that a simple diff -ur between brcmnand.c between the file in Linux and U-Boot was possible to see what differs. Combining this with some git log --oneline manual comparison, fuzzing and manual intervention I backported a set of relevant patches from Linux that compiles and WorksForMe(TM).
The diff between Linux and U-Boot is much smaller after this, the main missing part are subsystem cosmetics changes and the EDU DMA mode support.
This was as much as I could bite off in one go.
All patches countersigned-off and marked as [backported].
Thank you for your time
It's nice if someone else tests this I think.
I don't have any board to test those changes
Boris Brezillon (1): mtd: nand: brcm: switch to mtd_ooblayout_ops
Claire Lin (1): mtd: rawnand: brcmnand: Fix ecc chunk calculation for erased page bitfips
Kamal Dasu (4): mtd: rawnand: brcmnand: Fix BCH ECC layout for large page NAND parts mtd: rawnand: brcmnand: Refactored code to introduce helper functions mtd: rawnand: brcmnand: Add support for v7.3 controller mtd: nand: brcmnand: Add support for flash-dma v0
Álvaro Fernández Rojas (8): mtd: rawnand: brcmnand: fix hamming oob layout mtd: rawnand: brcmnand: improve hamming oob layout mtd: rawnand: brcmnand: correctly verify erased pages mtd: rawnand: brcmnand: rename v4 registers mtd: rawnand: brcmnand: fix CS0 layout mtd: rawnand: brcmnand: rename page sizes mtd: rawnand: brcmnand: support v2.1-v2.2 controllers mtd: rawnand: brcmnand: fix OOB R/W with Hamming ECC
drivers/mtd/nand/raw/brcmnand/brcmnand.c | 615 ++++++++++++++++------- 1 file changed, 425 insertions(+), 190 deletions(-)
Plan to review this week and run ci-pipeline to include in nand-next
MIchael
-- 2.39.0
I applied v2 of the series to nand-next.
Thanks and regards Dario
participants (4)
-
Dario Binacchi
-
Linus Walleij
-
Michael Nazzareno Trimarchi
-
William Zhang