[openwrt/openwrt] generic: 6.12: backport upstream v6.16 brcmnand patches

LEDE Commits lede-commits at lists.infradead.org
Tue May 27 02:30:08 PDT 2025


noltari pushed a commit to openwrt/openwrt.git, branch main:
https://git.openwrt.org/53c723246a153801264b3284c4488ced3fe57abd

commit 53c723246a153801264b3284c4488ced3fe57abd
Author: Álvaro Fernández Rojas <noltari at gmail.com>
AuthorDate: Fri May 23 08:15:42 2025 +0200

    generic: 6.12: backport upstream v6.16 brcmnand patches
    
    This is needed to restore compatibility with legacy brcmnand controllers.
    
    3bfb22cecfe6 mtd: rawnand: brcmnand: legacy exec_op implementation
    528b541b71cf mtd: nand: brcmnand: fix NAND timeout when accessing eMMC
    56fce7547004 mtd: rawnand: brcmnand: remove unused parameters
    
    Signed-off-by: Álvaro Fernández Rojas <noltari at gmail.com>
---
 ...rawnand-brcmnand-remove-unused-parameters.patch |  88 ++++++
 ...cmnand-fix-NAND-timeout-when-accessing-eM.patch |  30 +++
 ...nd-brcmnand-legacy-exec_op-implementation.patch | 299 +++++++++++++++++++++
 3 files changed, 417 insertions(+)

diff --git a/target/linux/generic/backport-6.12/420-01-v6.16-mtd-rawnand-brcmnand-remove-unused-parameters.patch b/target/linux/generic/backport-6.12/420-01-v6.16-mtd-rawnand-brcmnand-remove-unused-parameters.patch
new file mode 100644
index 0000000000..3bc6038711
--- /dev/null
+++ b/target/linux/generic/backport-6.12/420-01-v6.16-mtd-rawnand-brcmnand-remove-unused-parameters.patch
@@ -0,0 +1,88 @@
+From 56fce75470041b5b0d92ae10637416e1a4cceb1b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= <noltari at gmail.com>
+Date: Wed, 14 May 2025 08:14:54 +0200
+Subject: [PATCH] mtd: rawnand: brcmnand: remove unused parameters
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+last_cmd and last_byte are now unused brcmnand_host members.
+last_addr is only written and never read so we can remove it too.
+
+Signed-off-by: Álvaro Fernández Rojas <noltari at gmail.com>
+Reviewed-by: Florian Fainelli <florian.fainelli at broadcom.com>
+Reviewed-by: William Zhang <william.zhang at broadcom.com>
+Signed-off-by: Miquel Raynal <miquel.raynal at bootlin.com>
+---
+ drivers/mtd/nand/raw/brcmnand/brcmnand.c | 24 ++++++------------------
+ 1 file changed, 6 insertions(+), 18 deletions(-)
+
+--- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c
++++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c
+@@ -310,9 +310,6 @@ struct brcmnand_host {
+ 	struct platform_device	*pdev;
+ 	int			cs;
+ 
+-	unsigned int		last_cmd;
+-	unsigned int		last_byte;
+-	u64			last_addr;
+ 	struct brcmnand_cfg	hwcfg;
+ 	struct brcmnand_controller *ctrl;
+ };
+@@ -2233,14 +2230,11 @@ static int brcmnand_read_page(struct nan
+ 			      int oob_required, int page)
+ {
+ 	struct mtd_info *mtd = nand_to_mtd(chip);
+-	struct brcmnand_host *host = nand_get_controller_data(chip);
+ 	u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL;
+ 	u64 addr = (u64)page << chip->page_shift;
+ 
+-	host->last_addr = addr;
+-
+-	return brcmnand_read(mtd, chip, host->last_addr,
+-			mtd->writesize >> FC_SHIFT, (u32 *)buf, oob);
++	return brcmnand_read(mtd, chip, addr, mtd->writesize >> FC_SHIFT,
++			     (u32 *)buf, oob);
+ }
+ 
+ static int brcmnand_read_page_raw(struct nand_chip *chip, uint8_t *buf,
+@@ -2252,11 +2246,9 @@ static int brcmnand_read_page_raw(struct
+ 	int ret;
+ 	u64 addr = (u64)page << chip->page_shift;
+ 
+-	host->last_addr = addr;
+-
+ 	brcmnand_set_ecc_enabled(host, 0);
+-	ret = brcmnand_read(mtd, chip, host->last_addr,
+-			mtd->writesize >> FC_SHIFT, (u32 *)buf, oob);
++	ret = brcmnand_read(mtd, chip, addr, mtd->writesize >> FC_SHIFT,
++			    (u32 *)buf, oob);
+ 	brcmnand_set_ecc_enabled(host, 1);
+ 	return ret;
+ }
+@@ -2363,13 +2355,10 @@ static int brcmnand_write_page(struct na
+ 			       int oob_required, int page)
+ {
+ 	struct mtd_info *mtd = nand_to_mtd(chip);
+-	struct brcmnand_host *host = nand_get_controller_data(chip);
+ 	void *oob = oob_required ? chip->oob_poi : NULL;
+ 	u64 addr = (u64)page << chip->page_shift;
+ 
+-	host->last_addr = addr;
+-
+-	return brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob);
++	return brcmnand_write(mtd, chip, addr, (const u32 *)buf, oob);
+ }
+ 
+ static int brcmnand_write_page_raw(struct nand_chip *chip, const uint8_t *buf,
+@@ -2381,9 +2370,8 @@ static int brcmnand_write_page_raw(struc
+ 	u64 addr = (u64)page << chip->page_shift;
+ 	int ret = 0;
+ 
+-	host->last_addr = addr;
+ 	brcmnand_set_ecc_enabled(host, 0);
+-	ret = brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob);
++	ret = brcmnand_write(mtd, chip, addr, (const u32 *)buf, oob);
+ 	brcmnand_set_ecc_enabled(host, 1);
+ 
+ 	return ret;
diff --git a/target/linux/generic/backport-6.12/420-02-v6.16-mtd-nand-brcmnand-fix-NAND-timeout-when-accessing-eM.patch b/target/linux/generic/backport-6.12/420-02-v6.16-mtd-nand-brcmnand-fix-NAND-timeout-when-accessing-eM.patch
new file mode 100644
index 0000000000..d3dbde8b9e
--- /dev/null
+++ b/target/linux/generic/backport-6.12/420-02-v6.16-mtd-nand-brcmnand-fix-NAND-timeout-when-accessing-eM.patch
@@ -0,0 +1,30 @@
+From 528b541b71cf03e263272b051b70696f92258e9d Mon Sep 17 00:00:00 2001
+From: David Regan <dregan at broadcom.com>
+Date: Thu, 22 May 2025 10:25:17 -0700
+Subject: [PATCH] mtd: nand: brcmnand: fix NAND timeout when accessing eMMC
+
+When booting a board to NAND and accessing NAND while eMMC
+transactions are occurring the NAND will sometimes timeout. This
+is due to both NAND and eMMC controller sharing the same data bus
+on BCMBCA chips. Fix is to extend NAND timeout to allow eMMC
+transactions time to complete.
+
+Signed-off-by: David Regan <dregan at broadcom.com>
+Reviewed-by: William Zhang <william.zhang at broadcom.com>
+Reviewed-by: Florian Fainelli <florian.fainelli at broadcom.com>
+Signed-off-by: Miquel Raynal <miquel.raynal at bootlin.com>
+---
+ drivers/mtd/nand/raw/brcmnand/brcmnand.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c
++++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c
+@@ -101,7 +101,7 @@ struct brcm_nand_dma_desc {
+ #define BRCMNAND_MIN_DEVSIZE	(4ULL * 1024 * 1024)
+ 
+ #define NAND_CTRL_RDY			(INTFC_CTLR_READY | INTFC_FLASH_READY)
+-#define NAND_POLL_STATUS_TIMEOUT_MS	100
++#define NAND_POLL_STATUS_TIMEOUT_MS	500
+ 
+ #define EDU_CMD_WRITE          0x00
+ #define EDU_CMD_READ           0x01
diff --git a/target/linux/generic/backport-6.12/420-03-v6.16-mtd-rawnand-brcmnand-legacy-exec_op-implementation.patch b/target/linux/generic/backport-6.12/420-03-v6.16-mtd-rawnand-brcmnand-legacy-exec_op-implementation.patch
new file mode 100644
index 0000000000..6c75c2bbf2
--- /dev/null
+++ b/target/linux/generic/backport-6.12/420-03-v6.16-mtd-rawnand-brcmnand-legacy-exec_op-implementation.patch
@@ -0,0 +1,299 @@
+From 3bfb22cecfe6b6f0d8ee56ef4b533cf68599c5d9 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= <noltari at gmail.com>
+Date: Wed, 21 May 2025 10:03:25 +0200
+Subject: [PATCH] mtd: rawnand: brcmnand: legacy exec_op implementation
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Commit 3c8260ce7663 ("mtd: rawnand: brcmnand: exec_op implementation")
+removed legacy interface functions, breaking < v5.0 controllers support.
+In order to fix older controllers we need to add an alternative exec_op
+implementation which doesn't rely on low level registers.
+
+Fixes: 3c8260ce7663 ("mtd: rawnand: brcmnand: exec_op implementation")
+Signed-off-by: Álvaro Fernández Rojas <noltari at gmail.com>
+Reviewed-by: David Regan <dregan at broadcom.com>
+Reviewed-by: Florian Fainelli <florian.fainelli at broadcom.com>
+Reviewed-by: William Zhang <william.zhang at broadcom.com>
+Signed-off-by: Miquel Raynal <miquel.raynal at bootlin.com>
+---
+ drivers/mtd/nand/raw/brcmnand/brcmnand.c | 222 ++++++++++++++++++++++-
+ 1 file changed, 215 insertions(+), 7 deletions(-)
+
+--- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c
++++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c
+@@ -65,6 +65,7 @@ module_param(wp_on, int, 0444);
+ #define CMD_PARAMETER_READ		0x0e
+ #define CMD_PARAMETER_CHANGE_COL	0x0f
+ #define CMD_LOW_LEVEL_OP		0x10
++#define CMD_NOT_SUPPORTED		0xff
+ 
+ struct brcm_nand_dma_desc {
+ 	u32 next_desc;
+@@ -199,6 +200,30 @@ static const u16 flash_dma_regs_v4[] = {
+ 	[FLASH_DMA_CURRENT_DESC_EXT]	= 0x34,
+ };
+ 
++/* Native command conversion for legacy controllers (< v5.0) */
++static const u8 native_cmd_conv[] = {
++	[NAND_CMD_READ0]	= CMD_NOT_SUPPORTED,
++	[NAND_CMD_READ1]	= CMD_NOT_SUPPORTED,
++	[NAND_CMD_RNDOUT]	= CMD_PARAMETER_CHANGE_COL,
++	[NAND_CMD_PAGEPROG]	= CMD_NOT_SUPPORTED,
++	[NAND_CMD_READOOB]	= CMD_NOT_SUPPORTED,
++	[NAND_CMD_ERASE1]	= CMD_BLOCK_ERASE,
++	[NAND_CMD_STATUS]	= CMD_NOT_SUPPORTED,
++	[NAND_CMD_SEQIN]	= CMD_NOT_SUPPORTED,
++	[NAND_CMD_RNDIN]	= CMD_NOT_SUPPORTED,
++	[NAND_CMD_READID]	= CMD_DEVICE_ID_READ,
++	[NAND_CMD_ERASE2]	= CMD_NULL,
++	[NAND_CMD_PARAM]	= CMD_PARAMETER_READ,
++	[NAND_CMD_GET_FEATURES]	= CMD_NOT_SUPPORTED,
++	[NAND_CMD_SET_FEATURES]	= CMD_NOT_SUPPORTED,
++	[NAND_CMD_RESET]	= CMD_NOT_SUPPORTED,
++	[NAND_CMD_READSTART]	= CMD_NOT_SUPPORTED,
++	[NAND_CMD_READCACHESEQ]	= CMD_NOT_SUPPORTED,
++	[NAND_CMD_READCACHEEND]	= CMD_NOT_SUPPORTED,
++	[NAND_CMD_RNDOUTSTART]	= CMD_NULL,
++	[NAND_CMD_CACHEDPROG]	= CMD_NOT_SUPPORTED,
++};
++
+ /* Controller feature flags */
+ enum {
+ 	BRCMNAND_HAS_1K_SECTORS			= BIT(0),
+@@ -237,6 +262,12 @@ struct brcmnand_controller {
+ 	/* List of NAND hosts (one for each chip-select) */
+ 	struct list_head host_list;
+ 
++	/* Functions to be called from exec_op */
++	int (*check_instr)(struct nand_chip *chip,
++			   const struct nand_operation *op);
++	int (*exec_instr)(struct nand_chip *chip,
++			  const struct nand_operation *op);
++
+ 	/* EDU info, per-transaction */
+ 	const u16               *edu_offsets;
+ 	void __iomem            *edu_base;
+@@ -2478,18 +2509,190 @@ static int brcmnand_op_is_reset(const st
+ 	return 0;
+ }
+ 
++static int brcmnand_check_instructions(struct nand_chip *chip,
++				       const struct nand_operation *op)
++{
++	return 0;
++}
++
++static int brcmnand_exec_instructions(struct nand_chip *chip,
++				      const struct nand_operation *op)
++{
++	struct brcmnand_host *host = nand_get_controller_data(chip);
++	unsigned int i;
++	int ret = 0;
++
++	for (i = 0; i < op->ninstrs; i++) {
++		ret = brcmnand_exec_instr(host, i, op);
++		if (ret)
++			break;
++	}
++
++	return ret;
++}
++
++static int brcmnand_check_instructions_legacy(struct nand_chip *chip,
++					      const struct nand_operation *op)
++{
++	const struct nand_op_instr *instr;
++	unsigned int i;
++	u8 cmd;
++
++	for (i = 0; i < op->ninstrs; i++) {
++		instr = &op->instrs[i];
++
++		switch (instr->type) {
++		case NAND_OP_CMD_INSTR:
++			cmd = native_cmd_conv[instr->ctx.cmd.opcode];
++			if (cmd == CMD_NOT_SUPPORTED)
++				return -EOPNOTSUPP;
++			break;
++		case NAND_OP_ADDR_INSTR:
++		case NAND_OP_DATA_IN_INSTR:
++		case NAND_OP_WAITRDY_INSTR:
++			break;
++		default:
++			return -EOPNOTSUPP;
++		}
++	}
++
++	return 0;
++}
++
++static int brcmnand_exec_instructions_legacy(struct nand_chip *chip,
++					     const struct nand_operation *op)
++{
++	struct mtd_info *mtd = nand_to_mtd(chip);
++	struct brcmnand_host *host = nand_get_controller_data(chip);
++	struct brcmnand_controller *ctrl = host->ctrl;
++	const struct nand_op_instr *instr;
++	unsigned int i, j;
++	u8 cmd = CMD_NULL, last_cmd = CMD_NULL;
++	int ret = 0;
++	u64 last_addr;
++
++	for (i = 0; i < op->ninstrs; i++) {
++		instr = &op->instrs[i];
++
++		if (instr->type == NAND_OP_CMD_INSTR) {
++			cmd = native_cmd_conv[instr->ctx.cmd.opcode];
++			if (cmd == CMD_NOT_SUPPORTED) {
++				dev_err(ctrl->dev, "unsupported cmd=%d\n",
++					instr->ctx.cmd.opcode);
++				ret = -EOPNOTSUPP;
++				break;
++			}
++		} else if (instr->type == NAND_OP_ADDR_INSTR) {
++			u64 addr = 0;
++
++			if (cmd == CMD_NULL)
++				continue;
++
++			if (instr->ctx.addr.naddrs > 8) {
++				dev_err(ctrl->dev, "unsupported naddrs=%u\n",
++					instr->ctx.addr.naddrs);
++				ret = -EOPNOTSUPP;
++				break;
++			}
++
++			for (j = 0; j < instr->ctx.addr.naddrs; j++)
++				addr |= (instr->ctx.addr.addrs[j]) << (j << 3);
++
++			if (cmd == CMD_BLOCK_ERASE)
++				addr <<= chip->page_shift;
++			else if (cmd == CMD_PARAMETER_CHANGE_COL)
++				addr &= ~((u64)(FC_BYTES - 1));
++
++			brcmnand_set_cmd_addr(mtd, addr);
++			brcmnand_send_cmd(host, cmd);
++			last_addr = addr;
++			last_cmd = cmd;
++			cmd = CMD_NULL;
++			brcmnand_waitfunc(chip);
++
++			if (last_cmd == CMD_PARAMETER_READ ||
++			    last_cmd == CMD_PARAMETER_CHANGE_COL) {
++				/* Copy flash cache word-wise */
++				u32 *flash_cache = (u32 *)ctrl->flash_cache;
++
++				brcmnand_soc_data_bus_prepare(ctrl->soc, true);
++
++				/*
++				 * Must cache the FLASH_CACHE now, since changes in
++				 * SECTOR_SIZE_1K may invalidate it
++				 */
++				for (j = 0; j < FC_WORDS; j++)
++					/*
++					 * Flash cache is big endian for parameter pages, at
++					 * least on STB SoCs
++					 */
++					flash_cache[j] = be32_to_cpu(brcmnand_read_fc(ctrl, j));
++
++				brcmnand_soc_data_bus_unprepare(ctrl->soc, true);
++			}
++		} else if (instr->type == NAND_OP_DATA_IN_INSTR) {
++			u8 *in = instr->ctx.data.buf.in;
++
++			if (last_cmd == CMD_DEVICE_ID_READ) {
++				u32 val;
++
++				if (instr->ctx.data.len > 8) {
++					dev_err(ctrl->dev, "unsupported len=%u\n",
++						instr->ctx.data.len);
++					ret = -EOPNOTSUPP;
++					break;
++				}
++
++				for (j = 0; j < instr->ctx.data.len; j++) {
++					if (j == 0)
++						val = brcmnand_read_reg(ctrl, BRCMNAND_ID);
++					else if (j == 4)
++						val = brcmnand_read_reg(ctrl, BRCMNAND_ID_EXT);
++
++					in[j] = (val >> (24 - ((j % 4) << 3))) & 0xff;
++				}
++			} else if (last_cmd == CMD_PARAMETER_READ ||
++				   last_cmd == CMD_PARAMETER_CHANGE_COL) {
++				u64 addr;
++				u32 offs;
++
++				for (j = 0; j < instr->ctx.data.len; j++) {
++					addr = last_addr + j;
++					offs = addr & (FC_BYTES - 1);
++
++					if (j > 0 && offs == 0)
++						nand_change_read_column_op(chip, addr, NULL, 0,
++									   false);
++
++					in[j] = ctrl->flash_cache[offs];
++				}
++			}
++		} else if (instr->type == NAND_OP_WAITRDY_INSTR) {
++			ret = bcmnand_ctrl_poll_status(host, NAND_CTRL_RDY, NAND_CTRL_RDY, 0);
++			if (ret)
++				break;
++		} else {
++			dev_err(ctrl->dev, "unsupported instruction type: %d\n", instr->type);
++			ret = -EOPNOTSUPP;
++			break;
++		}
++	}
++
++	return ret;
++}
++
+ static int brcmnand_exec_op(struct nand_chip *chip,
+ 			    const struct nand_operation *op,
+ 			    bool check_only)
+ {
+ 	struct brcmnand_host *host = nand_get_controller_data(chip);
++	struct brcmnand_controller *ctrl = host->ctrl;
+ 	struct mtd_info *mtd = nand_to_mtd(chip);
+ 	u8 *status;
+-	unsigned int i;
+ 	int ret = 0;
+ 
+ 	if (check_only)
+-		return 0;
++		return ctrl->check_instr(chip, op);
+ 
+ 	if (brcmnand_op_is_status(op)) {
+ 		status = op->instrs[1].ctx.data.buf.in;
+@@ -2513,11 +2716,7 @@ static int brcmnand_exec_op(struct nand_
+ 	if (op->deassert_wp)
+ 		brcmnand_wp(mtd, 0);
+ 
+-	for (i = 0; i < op->ninstrs; i++) {
+-		ret = brcmnand_exec_instr(host, i, op);
+-		if (ret)
+-			break;
+-	}
++	ret = ctrl->exec_instr(chip, op);
+ 
+ 	if (op->deassert_wp)
+ 		brcmnand_wp(mtd, 1);
+@@ -3130,6 +3329,15 @@ int brcmnand_probe(struct platform_devic
+ 	if (ret)
+ 		goto err;
+ 
++	/* Only v5.0+ controllers have low level ops support */
++	if (ctrl->nand_version >= 0x0500) {
++		ctrl->check_instr = brcmnand_check_instructions;
++		ctrl->exec_instr = brcmnand_exec_instructions;
++	} else {
++		ctrl->check_instr = brcmnand_check_instructions_legacy;
++		ctrl->exec_instr = brcmnand_exec_instructions_legacy;
++	}
++
+ 	/*
+ 	 * Most chips have this cache at a fixed offset within 'nand' block.
+ 	 * Some must specify this region separately.




More information about the lede-commits mailing list