[openwrt/openwrt] bcm47xx: Add support for brcmnand controller on BCMA bus

LEDE Commits lede-commits at lists.infradead.org
Mon Jun 20 15:16:52 PDT 2022


florian pushed a commit to openwrt/openwrt.git, branch master:
https://git.openwrt.org/acff8aec0c629859fec4ebac2073c4fe37c8035a

commit acff8aec0c629859fec4ebac2073c4fe37c8035a
Author: Florian Fainelli <f.fainelli at gmail.com>
AuthorDate: Wed Dec 22 19:25:14 2021 -0800

    bcm47xx: Add support for brcmnand controller on BCMA bus
    
    Back port the patches being submitted upstream in order to make the NAND
    controller work on BCM47187/5358. This is a prerequisite for supporting
    devices like the Netgear WNR3500L V2.
    
    Signed-off-by: Florian Fainelli <f.fainelli at gmail.com>
---
 target/linux/bcm47xx/config-5.10                   |   5 +-
 ...-brcmnand-Assign-soc-as-early-as-possible.patch |  33 ++++
 ...mnand-Allow-SoC-to-provide-I-O-operations.patch | 150 +++++++++++++++
 ...d-brcmnand-Avoid-pdev-in-brcmnand_init_cs.patch |  52 ++++++
 ...ove-OF-operations-out-of-brcmnand_init_cs.patch |  63 +++++++
 ...brcmnand-Allow-working-without-interrupts.patch |  91 ++++++++++
 ...nand-Add-platform-data-structure-for-BCMA.patch | 115 ++++++++++++
 ...brcmnand-Allow-platform-data-instantation.patch | 124 +++++++++++++
 ...d-BCMA-controller-uses-command-shift-of-0.patch |  29 +++
 .../108-mtd-rawnand-brcmnand-Add-BCMA-shim.patch   | 201 +++++++++++++++++++++
 10 files changed, 862 insertions(+), 1 deletion(-)

diff --git a/target/linux/bcm47xx/config-5.10 b/target/linux/bcm47xx/config-5.10
index 297d6fce6d..377e77d157 100644
--- a/target/linux/bcm47xx/config-5.10
+++ b/target/linux/bcm47xx/config-5.10
@@ -147,8 +147,11 @@ CONFIG_MIPS_L1_CACHE_SHIFT=5
 CONFIG_MODULES_USE_ELF_REL=y
 CONFIG_MTD_BCM47XXSFLASH=y
 CONFIG_MTD_BCM47XX_PARTS=y
-CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_CORE=y
+CONFIG_MTD_RAW_NAND=y
 CONFIG_MTD_NAND_BCM47XXNFLASH=y
+CONFIG_MTD_NAND_BRCMNAND=y
+CONFIG_MTD_NAND_BRCMNAND_BCMA=y
 CONFIG_MTD_NAND_ECC=y
 CONFIG_MTD_PARSER_TRX=y
 CONFIG_MTD_PHYSMAP=y
diff --git a/target/linux/bcm47xx/patches-5.10/100-mtd-rawnand-brcmnand-Assign-soc-as-early-as-possible.patch b/target/linux/bcm47xx/patches-5.10/100-mtd-rawnand-brcmnand-Assign-soc-as-early-as-possible.patch
new file mode 100644
index 0000000000..8efda10809
--- /dev/null
+++ b/target/linux/bcm47xx/patches-5.10/100-mtd-rawnand-brcmnand-Assign-soc-as-early-as-possible.patch
@@ -0,0 +1,33 @@
+From: Florian Fainelli <f.fainelli at gmail.com>
+Subject: [PATCH v3 1/9] mtd: rawnand: brcmnand: Assign soc as early as possible
+Date: Fri, 07 Jan 2022 10:46:06 -0800
+Content-Type: text/plain; charset="utf-8"
+
+In order to key off the brcmnand_probe() code in subsequent changes
+depending upon ctrl->soc, assign that variable as early as possible,
+instead of much later when we have checked that it is non-NULL.
+
+Signed-off-by: Florian Fainelli <f.fainelli at gmail.com>
+---
+ drivers/mtd/nand/raw/brcmnand/brcmnand.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+--- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c
++++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c
+@@ -2949,6 +2949,7 @@ int brcmnand_probe(struct platform_devic
+ 
+ 	dev_set_drvdata(dev, ctrl);
+ 	ctrl->dev = dev;
++	ctrl->soc = soc;
+ 
+ 	init_completion(&ctrl->done);
+ 	init_completion(&ctrl->dma_done);
+@@ -3089,8 +3090,6 @@ int brcmnand_probe(struct platform_devic
+ 	 * interesting ways
+ 	 */
+ 	if (soc) {
+-		ctrl->soc = soc;
+-
+ 		ret = devm_request_irq(dev, ctrl->irq, brcmnand_irq, 0,
+ 				       DRV_NAME, ctrl);
+ 
diff --git a/target/linux/bcm47xx/patches-5.10/101-mtd-rawnand-brcmnand-Allow-SoC-to-provide-I-O-operations.patch b/target/linux/bcm47xx/patches-5.10/101-mtd-rawnand-brcmnand-Allow-SoC-to-provide-I-O-operations.patch
new file mode 100644
index 0000000000..23f5df3015
--- /dev/null
+++ b/target/linux/bcm47xx/patches-5.10/101-mtd-rawnand-brcmnand-Allow-SoC-to-provide-I-O-operations.patch
@@ -0,0 +1,150 @@
+From: Florian Fainelli <f.fainelli at gmail.com>
+Subject: [PATCH v3 2/9] mtd: rawnand: brcmnand: Allow SoC to provide I/O operations
+Date: Fri, 07 Jan 2022 10:46:07 -0800
+Content-Type: text/plain; charset="utf-8"
+
+Allow a brcmnand_soc instance to provide a custom set of I/O operations
+which we will require when using this driver on a BCMA bus which is not
+directly memory mapped I/O. Update the nand_{read,write}_reg accordingly
+to use the SoC operations if provided.
+
+To minimize the penalty on other SoCs which do support standard MMIO
+accesses, we use a static key which is disabled by default and gets
+enabled if a soc implementation does provide I/O operations.
+
+Signed-off-by: Florian Fainelli <f.fainelli at gmail.com>
+---
+ drivers/mtd/nand/raw/brcmnand/brcmnand.c | 28 +++++++++++++++++++++--
+ drivers/mtd/nand/raw/brcmnand/brcmnand.h | 29 ++++++++++++++++++++++++
+ 2 files changed, 55 insertions(+), 2 deletions(-)
+
+--- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c
++++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c
+@@ -25,6 +25,7 @@
+ #include <linux/of.h>
+ #include <linux/of_platform.h>
+ #include <linux/slab.h>
++#include <linux/static_key.h>
+ #include <linux/list.h>
+ #include <linux/log2.h>
+ 
+@@ -207,6 +208,8 @@ enum {
+ 
+ struct brcmnand_host;
+ 
++static DEFINE_STATIC_KEY_FALSE(brcmnand_soc_has_ops_key);
++
+ struct brcmnand_controller {
+ 	struct device		*dev;
+ 	struct nand_controller	controller;
+@@ -589,15 +592,25 @@ enum {
+ 	INTFC_CTLR_READY		= BIT(31),
+ };
+ 
++static inline bool brcmnand_non_mmio_ops(struct brcmnand_controller *ctrl)
++{
++	return static_branch_unlikely(&brcmnand_soc_has_ops_key);
++}
++
+ static inline u32 nand_readreg(struct brcmnand_controller *ctrl, u32 offs)
+ {
++	if (brcmnand_non_mmio_ops(ctrl))
++		return brcmnand_soc_read(ctrl->soc, offs);
+ 	return brcmnand_readl(ctrl->nand_base + offs);
+ }
+ 
+ static inline void nand_writereg(struct brcmnand_controller *ctrl, u32 offs,
+ 				 u32 val)
+ {
+-	brcmnand_writel(val, ctrl->nand_base + offs);
++	if (brcmnand_non_mmio_ops(ctrl))
++		brcmnand_soc_write(ctrl->soc, val, offs);
++	else
++		brcmnand_writel(val, ctrl->nand_base + offs);
+ }
+ 
+ static int brcmnand_revision_init(struct brcmnand_controller *ctrl)
+@@ -763,13 +776,18 @@ static inline void brcmnand_rmw_reg(stru
+ 
+ static inline u32 brcmnand_read_fc(struct brcmnand_controller *ctrl, int word)
+ {
++	if (brcmnand_non_mmio_ops(ctrl))
++		return brcmnand_soc_read(ctrl->soc, BRCMNAND_NON_MMIO_FC_ADDR);
+ 	return __raw_readl(ctrl->nand_fc + word * 4);
+ }
+ 
+ static inline void brcmnand_write_fc(struct brcmnand_controller *ctrl,
+ 				     int word, u32 val)
+ {
+-	__raw_writel(val, ctrl->nand_fc + word * 4);
++	if (brcmnand_non_mmio_ops(ctrl))
++		brcmnand_soc_write(ctrl->soc, val, BRCMNAND_NON_MMIO_FC_ADDR);
++	else
++		__raw_writel(val, ctrl->nand_fc + word * 4);
+ }
+ 
+ static inline void edu_writel(struct brcmnand_controller *ctrl,
+@@ -2951,6 +2969,12 @@ int brcmnand_probe(struct platform_devic
+ 	ctrl->dev = dev;
+ 	ctrl->soc = soc;
+ 
++	/* Enable the static key if the soc provides I/O operations indicating
++	 * that a non-memory mapped IO access path must be used
++	 */
++	if (brcmnand_soc_has_ops(ctrl->soc))
++		static_branch_enable(&brcmnand_soc_has_ops_key);
++
+ 	init_completion(&ctrl->done);
+ 	init_completion(&ctrl->dma_done);
+ 	init_completion(&ctrl->edu_done);
+--- a/drivers/mtd/nand/raw/brcmnand/brcmnand.h
++++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.h
+@@ -11,12 +11,25 @@
+ 
+ struct platform_device;
+ struct dev_pm_ops;
++struct brcmnand_io_ops;
++
++/* Special register offset constant to intercept a non-MMIO access
++ * to the flash cache register space. This is intentionally large
++ * not to overlap with an existing offset.
++ */
++#define BRCMNAND_NON_MMIO_FC_ADDR	0xffffffff
+ 
+ struct brcmnand_soc {
+ 	bool (*ctlrdy_ack)(struct brcmnand_soc *soc);
+ 	void (*ctlrdy_set_enabled)(struct brcmnand_soc *soc, bool en);
+ 	void (*prepare_data_bus)(struct brcmnand_soc *soc, bool prepare,
+ 				 bool is_param);
++	const struct brcmnand_io_ops *ops;
++};
++
++struct brcmnand_io_ops {
++	u32 (*read_reg)(struct brcmnand_soc *soc, u32 offset);
++	void (*write_reg)(struct brcmnand_soc *soc, u32 val, u32 offset);
+ };
+ 
+ static inline void brcmnand_soc_data_bus_prepare(struct brcmnand_soc *soc,
+@@ -58,6 +71,22 @@ static inline void brcmnand_writel(u32 v
+ 		writel_relaxed(val, addr);
+ }
+ 
++static inline bool brcmnand_soc_has_ops(struct brcmnand_soc *soc)
++{
++	return soc && soc->ops && soc->ops->read_reg && soc->ops->write_reg;
++}
++
++static inline u32 brcmnand_soc_read(struct brcmnand_soc *soc, u32 offset)
++{
++	return soc->ops->read_reg(soc, offset);
++}
++
++static inline void brcmnand_soc_write(struct brcmnand_soc *soc, u32 val,
++				      u32 offset)
++{
++	soc->ops->write_reg(soc, val, offset);
++}
++
+ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc);
+ int brcmnand_remove(struct platform_device *pdev);
+ 
diff --git a/target/linux/bcm47xx/patches-5.10/102-mtd-rawnand-brcmnand-Avoid-pdev-in-brcmnand_init_cs.patch b/target/linux/bcm47xx/patches-5.10/102-mtd-rawnand-brcmnand-Avoid-pdev-in-brcmnand_init_cs.patch
new file mode 100644
index 0000000000..d9324c2595
--- /dev/null
+++ b/target/linux/bcm47xx/patches-5.10/102-mtd-rawnand-brcmnand-Avoid-pdev-in-brcmnand_init_cs.patch
@@ -0,0 +1,52 @@
+From: Florian Fainelli <f.fainelli at gmail.com>
+Subject: [PATCH v3 3/9] mtd: rawnand: brcmnand: Avoid pdev in brcmnand_init_cs()
+Date: Fri, 07 Jan 2022 10:46:08 -0800
+Content-Type: text/plain; charset="utf-8"
+
+In preparation for encapsulating more of what the loop calling
+brcmnand_init_cs() does, avoid using platform_device when it is the
+device behind platform_device that we are using for printing errors.
+
+No functional changes introduced.
+
+Signed-off-by: Florian Fainelli <f.fainelli at gmail.com>
+---
+ drivers/mtd/nand/raw/brcmnand/brcmnand.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c
++++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c
+@@ -2722,7 +2722,7 @@ static const struct nand_controller_ops
+ static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn)
+ {
+ 	struct brcmnand_controller *ctrl = host->ctrl;
+-	struct platform_device *pdev = host->pdev;
++	struct device *dev = ctrl->dev;
+ 	struct mtd_info *mtd;
+ 	struct nand_chip *chip;
+ 	int ret;
+@@ -2730,7 +2730,7 @@ static int brcmnand_init_cs(struct brcmn
+ 
+ 	ret = of_property_read_u32(dn, "reg", &host->cs);
+ 	if (ret) {
+-		dev_err(&pdev->dev, "can't get chip-select\n");
++		dev_err(dev, "can't get chip-select\n");
+ 		return -ENXIO;
+ 	}
+ 
+@@ -2739,13 +2739,13 @@ static int brcmnand_init_cs(struct brcmn
+ 
+ 	nand_set_flash_node(chip, dn);
+ 	nand_set_controller_data(chip, host);
+-	mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "brcmnand.%d",
++	mtd->name = devm_kasprintf(dev, GFP_KERNEL, "brcmnand.%d",
+ 				   host->cs);
+ 	if (!mtd->name)
+ 		return -ENOMEM;
+ 
+ 	mtd->owner = THIS_MODULE;
+-	mtd->dev.parent = &pdev->dev;
++	mtd->dev.parent = dev;
+ 
+ 	chip->legacy.cmd_ctrl = brcmnand_cmd_ctrl;
+ 	chip->legacy.cmdfunc = brcmnand_cmdfunc;
diff --git a/target/linux/bcm47xx/patches-5.10/103-mtd-rawnand-brcmnand-Move-OF-operations-out-of-brcmnand_init_cs.patch b/target/linux/bcm47xx/patches-5.10/103-mtd-rawnand-brcmnand-Move-OF-operations-out-of-brcmnand_init_cs.patch
new file mode 100644
index 0000000000..34fd1b47ea
--- /dev/null
+++ b/target/linux/bcm47xx/patches-5.10/103-mtd-rawnand-brcmnand-Move-OF-operations-out-of-brcmnand_init_cs.patch
@@ -0,0 +1,63 @@
+From: Florian Fainelli <f.fainelli at gmail.com>
+Subject: [PATCH v3 4/9] mtd: rawnand: brcmnand: Move OF operations out of brcmnand_init_cs()
+Date: Fri, 07 Jan 2022 10:46:09 -0800
+Content-Type: text/plain; charset="utf-8"
+
+In order to initialize a given chip select object for use by the
+brcmnand driver, move all of the Device Tree specific routines outside
+of brcmnand_init_cs() in order to make it usable in a platform data
+configuration which will be necessary for supporting BCMA chips.
+
+No functional changes introduced.
+
+Signed-off-by: Florian Fainelli <f.fainelli at gmail.com>
+---
+ drivers/mtd/nand/raw/brcmnand/brcmnand.c | 20 +++++++++++---------
+ 1 file changed, 11 insertions(+), 9 deletions(-)
+
+--- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c
++++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c
+@@ -2719,7 +2719,7 @@ static const struct nand_controller_ops
+ 	.attach_chip = brcmnand_attach_chip,
+ };
+ 
+-static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn)
++static int brcmnand_init_cs(struct brcmnand_host *host)
+ {
+ 	struct brcmnand_controller *ctrl = host->ctrl;
+ 	struct device *dev = ctrl->dev;
+@@ -2728,16 +2728,9 @@ static int brcmnand_init_cs(struct brcmn
+ 	int ret;
+ 	u16 cfg_offs;
+ 
+-	ret = of_property_read_u32(dn, "reg", &host->cs);
+-	if (ret) {
+-		dev_err(dev, "can't get chip-select\n");
+-		return -ENXIO;
+-	}
+-
+ 	mtd = nand_to_mtd(&host->chip);
+ 	chip = &host->chip;
+ 
+-	nand_set_flash_node(chip, dn);
+ 	nand_set_controller_data(chip, host);
+ 	mtd->name = devm_kasprintf(dev, GFP_KERNEL, "brcmnand.%d",
+ 				   host->cs);
+@@ -3144,7 +3137,16 @@ int brcmnand_probe(struct platform_devic
+ 			host->pdev = pdev;
+ 			host->ctrl = ctrl;
+ 
+-			ret = brcmnand_init_cs(host, child);
++			ret = of_property_read_u32(child, "reg", &host->cs);
++			if (ret) {
++				dev_err(dev, "can't get chip-select\n");
++				devm_kfree(dev, host);
++				continue;
++			}
++
++			nand_set_flash_node(&host->chip, child);
++
++			ret = brcmnand_init_cs(host);
+ 			if (ret) {
+ 				devm_kfree(dev, host);
+ 				continue; /* Try all chip-selects */
diff --git a/target/linux/bcm47xx/patches-5.10/104-mtd-rawnand-brcmnand-Allow-working-without-interrupts.patch b/target/linux/bcm47xx/patches-5.10/104-mtd-rawnand-brcmnand-Allow-working-without-interrupts.patch
new file mode 100644
index 0000000000..3a9d18fa21
--- /dev/null
+++ b/target/linux/bcm47xx/patches-5.10/104-mtd-rawnand-brcmnand-Allow-working-without-interrupts.patch
@@ -0,0 +1,91 @@
+From: Florian Fainelli <f.fainelli at gmail.com>
+Subject: [PATCH v3 5/9] mtd: rawnand: brcmnand: Allow working without interrupts
+Date: Fri, 07 Jan 2022 10:46:10 -0800
+Content-Type: text/plain; charset="utf-8"
+
+The BCMA devices include the brcmnand controller but they do not wire up
+any interrupt line, allow the main interrupt to be optional and update
+the completion path to also check for the lack of an interrupt line.
+
+Signed-off-by: Florian Fainelli <f.fainelli at gmail.com>
+---
+ drivers/mtd/nand/raw/brcmnand/brcmnand.c | 52 +++++++++++-------------
+ 1 file changed, 24 insertions(+), 28 deletions(-)
+
+--- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c
++++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c
+@@ -216,7 +216,7 @@ struct brcmnand_controller {
+ 	void __iomem		*nand_base;
+ 	void __iomem		*nand_fc; /* flash cache */
+ 	void __iomem		*flash_dma_base;
+-	unsigned int		irq;
++	int			irq;
+ 	unsigned int		dma_irq;
+ 	int			nand_version;
+ 
+@@ -1590,7 +1590,7 @@ static bool brcmstb_nand_wait_for_comple
+ 	bool err = false;
+ 	int sts;
+ 
+-	if (mtd->oops_panic_write) {
++	if (mtd->oops_panic_write || ctrl->irq < 0) {
+ 		/* switch to interrupt polling and PIO mode */
+ 		disable_ctrl_irqs(ctrl);
+ 		sts = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY,
+@@ -3095,33 +3095,29 @@ int brcmnand_probe(struct platform_devic
+ 	}
+ 
+ 	/* IRQ */
+-	ctrl->irq = platform_get_irq(pdev, 0);
+-	if ((int)ctrl->irq < 0) {
+-		dev_err(dev, "no IRQ defined\n");
+-		ret = -ENODEV;
+-		goto err;
+-	}
+-
+-	/*
+-	 * Some SoCs integrate this controller (e.g., its interrupt bits) in
+-	 * interesting ways
+-	 */
+-	if (soc) {
+-		ret = devm_request_irq(dev, ctrl->irq, brcmnand_irq, 0,
+-				       DRV_NAME, ctrl);
+-
+-		/* Enable interrupt */
+-		ctrl->soc->ctlrdy_ack(ctrl->soc);
+-		ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true);
+-	} else {
+-		/* Use standard interrupt infrastructure */
+-		ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0,
+-				       DRV_NAME, ctrl);
+-	}
+-	if (ret < 0) {
+-		dev_err(dev, "can't allocate IRQ %d: error %d\n",
+-			ctrl->irq, ret);
+-		goto err;
++	ctrl->irq = platform_get_irq_optional(pdev, 0);
++	if (ctrl->irq > 0) {
++		/*
++		 * Some SoCs integrate this controller (e.g., its interrupt bits) in
++		 * interesting ways
++		 */
++		if (soc) {
++			ret = devm_request_irq(dev, ctrl->irq, brcmnand_irq, 0,
++					       DRV_NAME, ctrl);
++
++			/* Enable interrupt */
++			ctrl->soc->ctlrdy_ack(ctrl->soc);
++			ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true);
++		} else {
++			/* Use standard interrupt infrastructure */
++			ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0,
++					       DRV_NAME, ctrl);
++		}
++		if (ret < 0) {
++			dev_err(dev, "can't allocate IRQ %d: error %d\n",
++				ctrl->irq, ret);
++			goto err;
++		}
+ 	}
+ 
+ 	for_each_available_child_of_node(dn, child) {
diff --git a/target/linux/bcm47xx/patches-5.10/105-mtd-rawnand-brcmnand-Add-platform-data-structure-for-BCMA.patch b/target/linux/bcm47xx/patches-5.10/105-mtd-rawnand-brcmnand-Add-platform-data-structure-for-BCMA.patch
new file mode 100644
index 0000000000..973a3e95ee
--- /dev/null
+++ b/target/linux/bcm47xx/patches-5.10/105-mtd-rawnand-brcmnand-Add-platform-data-structure-for-BCMA.patch
@@ -0,0 +1,115 @@
+From: Florian Fainelli <f.fainelli at gmail.com>
+Subject: [PATCH v3 6/9] mtd: rawnand: brcmnand: Add platform data structure for BCMA
+Date: Fri, 07 Jan 2022 10:46:11 -0800
+Content-Type: text/plain; charset="utf-8"
+
+Update the BCMA's chipcommon nand flash driver to detect which
+chip-select is used and pass that information via platform data to the
+brcmnand driver. Make sure that the brcmnand platform data structure is
+always at the beginning of the platform data of the "nflash" device
+created by BCMA to allow brcmnand to safely de-reference it.
+
+Signed-off-by: Florian Fainelli <f.fainelli at gmail.com>
+---
+ MAINTAINERS                                 |  1 +
+ drivers/bcma/driver_chipcommon_nflash.c     | 20 +++++++++++++++++++-
+ include/linux/bcma/bcma_driver_chipcommon.h |  5 +++++
+ include/linux/platform_data/brcmnand.h      | 12 ++++++++++++
+ 4 files changed, 37 insertions(+), 1 deletion(-)
+ create mode 100644 include/linux/platform_data/brcmnand.h
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -3712,6 +3712,7 @@ L:	linux-mtd at lists.infradead.org
+ L:	bcm-kernel-feedback-list at broadcom.com
+ S:	Maintained
+ F:	drivers/mtd/nand/raw/brcmnand/
++F:	include/linux/platform_data/brcmnand.h
+ 
+ BROADCOM SYSTEMPORT ETHERNET DRIVER
+ M:	Florian Fainelli <f.fainelli at gmail.com>
+--- a/drivers/bcma/driver_chipcommon_nflash.c
++++ b/drivers/bcma/driver_chipcommon_nflash.c
+@@ -7,18 +7,28 @@
+ 
+ #include "bcma_private.h"
+ 
++#include <linux/bitops.h>
+ #include <linux/platform_device.h>
++#include <linux/platform_data/brcmnand.h>
+ #include <linux/bcma/bcma.h>
+ 
++/* Alternate NAND controller driver name in order to allow both bcm47xxnflash
++ * and bcma_brcmnand to be built into the same kernel image.
++ */
++static const char *bcma_nflash_alt_name = "bcma_brcmnand";
++
+ struct platform_device bcma_nflash_dev = {
+ 	.name		= "bcma_nflash",
+ 	.num_resources	= 0,
+ };
+ 
++static const char *probes[] = { "bcm47xxpart", NULL };
++
+ /* Initialize NAND flash access */
+ int bcma_nflash_init(struct bcma_drv_cc *cc)
+ {
+ 	struct bcma_bus *bus = cc->core->bus;
++	u32 reg;
+ 
+ 	if (bus->chipinfo.id != BCMA_CHIP_ID_BCM4706 &&
+ 	    cc->core->id.rev != 38) {
+@@ -33,8 +43,16 @@ int bcma_nflash_init(struct bcma_drv_cc
+ 
+ 	cc->nflash.present = true;
+ 	if (cc->core->id.rev == 38 &&
+-	    (cc->status & BCMA_CC_CHIPST_5357_NAND_BOOT))
++	    (cc->status & BCMA_CC_CHIPST_5357_NAND_BOOT)) {
+ 		cc->nflash.boot = true;
++		/* Determine the chip select that is being used */
++		reg = bcma_cc_read32(cc, BCMA_CC_NAND_CS_NAND_SELECT) & 0xff;
++		cc->nflash.brcmnand_info.chip_select = ffs(reg) - 1;
++		cc->nflash.brcmnand_info.part_probe_types = probes;
++		cc->nflash.brcmnand_info.ecc_stepsize = 512;
++		cc->nflash.brcmnand_info.ecc_strength = 1;
++		bcma_nflash_dev.name = bcma_nflash_alt_name;
++	}
+ 
+ 	/* Prepare platform device, but don't register it yet. It's too early,
+ 	 * malloc (required by device_private_init) is not available yet. */
+--- a/include/linux/bcma/bcma_driver_chipcommon.h
++++ b/include/linux/bcma/bcma_driver_chipcommon.h
+@@ -3,6 +3,7 @@
+ #define LINUX_BCMA_DRIVER_CC_H_
+ 
+ #include <linux/platform_device.h>
++#include <linux/platform_data/brcmnand.h>
+ #include <linux/gpio.h>
+ 
+ /** ChipCommon core registers. **/
+@@ -599,6 +600,10 @@ struct bcma_sflash {
+ 
+ #ifdef CONFIG_BCMA_NFLASH
+ struct bcma_nflash {
++	/* Must be the fist member for the brcmnand driver to
++	 * de-reference that structure.
++	 */
++	struct brcmnand_platform_data brcmnand_info;
+ 	bool present;
+ 	bool boot;		/* This is the flash the SoC boots from */
+ };
+--- /dev/null
++++ b/include/linux/platform_data/brcmnand.h
+@@ -0,0 +1,12 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++#ifndef BRCMNAND_PLAT_DATA_H
++#define BRCMNAND_PLAT_DATA_H
++
++struct brcmnand_platform_data {
++	int	chip_select;
++	const char * const *part_probe_types;
++	unsigned int ecc_stepsize;
++	unsigned int ecc_strength;
++};
++
++#endif /* BRCMNAND_PLAT_DATA_H */
diff --git a/target/linux/bcm47xx/patches-5.10/106-mtd-rawnand-brcmnand-Allow-platform-data-instantation.patch b/target/linux/bcm47xx/patches-5.10/106-mtd-rawnand-brcmnand-Allow-platform-data-instantation.patch
new file mode 100644
index 0000000000..fb9ee07d04
--- /dev/null
+++ b/target/linux/bcm47xx/patches-5.10/106-mtd-rawnand-brcmnand-Allow-platform-data-instantation.patch
@@ -0,0 +1,124 @@
+From: Florian Fainelli <f.fainelli at gmail.com>
+Subject: [PATCH v3 7/9] mtd: rawnand: brcmnand: Allow platform data instantation
+Date: Fri, 07 Jan 2022 10:46:12 -0800
+Content-Type: text/plain; charset="utf-8"
+
+Make use of the recently refactored code in brcmnand_init_cs() and
+derive the chip-select from the platform data that is supplied. Update
+the various code paths to avoid relying on possibly non-existent
+resources, too.
+
+Signed-off-by: Florian Fainelli <f.fainelli at gmail.com>
+---
+ drivers/mtd/nand/raw/brcmnand/brcmnand.c | 45 ++++++++++++++++++------
+ 1 file changed, 35 insertions(+), 10 deletions(-)
+
+--- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c
++++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c
+@@ -9,6 +9,7 @@
+ #include <linux/delay.h>
+ #include <linux/device.h>
+ #include <linux/platform_device.h>
++#include <linux/platform_data/brcmnand.h>
+ #include <linux/err.h>
+ #include <linux/completion.h>
+ #include <linux/interrupt.h>
+@@ -2719,7 +2720,8 @@ static const struct nand_controller_ops
+ 	.attach_chip = brcmnand_attach_chip,
+ };
+ 
+-static int brcmnand_init_cs(struct brcmnand_host *host)
++static int brcmnand_init_cs(struct brcmnand_host *host,
++			    const char * const *part_probe_types)
+ {
+ 	struct brcmnand_controller *ctrl = host->ctrl;
+ 	struct device *dev = ctrl->dev;
+@@ -2772,7 +2774,7 @@ static int brcmnand_init_cs(struct brcmn
+ 	if (ret)
+ 		return ret;
+ 
+-	ret = mtd_device_register(mtd, NULL, 0);
++	ret = mtd_device_parse_register(mtd, part_probe_types, NULL, NULL, 0);
+ 	if (ret)
+ 		nand_cleanup(chip);
+ 
+@@ -2941,17 +2943,15 @@ static int brcmnand_edu_setup(struct pla
+ 
+ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
+ {
++	struct brcmnand_platform_data *pd = dev_get_platdata(&pdev->dev);
+ 	struct device *dev = &pdev->dev;
+ 	struct device_node *dn = dev->of_node, *child;
+ 	struct brcmnand_controller *ctrl;
++	struct brcmnand_host *host;
+ 	struct resource *res;
+ 	int ret;
+ 
+-	/* We only support device-tree instantiation */
+-	if (!dn)
+-		return -ENODEV;
+-
+-	if (!of_match_node(brcmnand_of_match, dn))
++	if (dn && !of_match_node(brcmnand_of_match, dn))
+ 		return -ENODEV;
+ 
+ 	ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+@@ -2978,7 +2978,7 @@ int brcmnand_probe(struct platform_devic
+ 	/* NAND register range */
+ 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ 	ctrl->nand_base = devm_ioremap_resource(dev, res);
+-	if (IS_ERR(ctrl->nand_base))
++	if (IS_ERR(ctrl->nand_base) && !brcmnand_soc_has_ops(soc))
+ 		return PTR_ERR(ctrl->nand_base);
+ 
+ 	/* Enable clock before using NAND registers */
+@@ -3122,7 +3122,6 @@ int brcmnand_probe(struct platform_devic
+ 
+ 	for_each_available_child_of_node(dn, child) {
+ 		if (of_device_is_compatible(child, "brcm,nandcs")) {
+-			struct brcmnand_host *host;
+ 
+ 			host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
+ 			if (!host) {
+@@ -3142,7 +3141,7 @@ int brcmnand_probe(struct platform_devic
+ 
+ 			nand_set_flash_node(&host->chip, child);
+ 
+-			ret = brcmnand_init_cs(host);
++			ret = brcmnand_init_cs(host, NULL);
+ 			if (ret) {
+ 				devm_kfree(dev, host);
+ 				continue; /* Try all chip-selects */
+@@ -3152,6 +3151,32 @@ int brcmnand_probe(struct platform_devic
+ 		}
+ 	}
+ 
++	if (!list_empty(&ctrl->host_list))
++		return 0;
++
++	if (!pd) {
++		ret = -ENODEV;
++		goto err;
++	}
++
++	/* If we got there we must have been probing via platform data */
++	host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
++	if (!host) {
++		ret = -ENOMEM;
++		goto err;
++	}
++	host->pdev = pdev;
++	host->ctrl = ctrl;
++	host->cs = pd->chip_select;
++	host->chip.ecc.size = pd->ecc_stepsize;
++	host->chip.ecc.strength = pd->ecc_strength;
++
++	ret = brcmnand_init_cs(host, pd->part_probe_types);
++	if (ret)
++		goto err;
++
++	list_add_tail(&host->node, &ctrl->host_list);
++
+ 	/* No chip-selects could initialize properly */
+ 	if (list_empty(&ctrl->host_list)) {
+ 		ret = -ENODEV;
diff --git a/target/linux/bcm47xx/patches-5.10/107-mtd-rawnand-brcmnand-BCMA-controller-uses-command-shift-of-0.patch b/target/linux/bcm47xx/patches-5.10/107-mtd-rawnand-brcmnand-BCMA-controller-uses-command-shift-of-0.patch
new file mode 100644
index 0000000000..39f34aab29
--- /dev/null
+++ b/target/linux/bcm47xx/patches-5.10/107-mtd-rawnand-brcmnand-BCMA-controller-uses-command-shift-of-0.patch
@@ -0,0 +1,29 @@
+From: Florian Fainelli <f.fainelli at gmail.com>
+Subject: [PATCH v3 8/9] mtd: rawnand: brcmnand: BCMA controller uses command shift of 0
+Date: Fri, 07 Jan 2022 10:46:13 -0800
+Content-Type: text/plain; charset="utf-8"
+
+For some odd and unexplained reason the BCMA NAND controller, albeit
+revision 3.4 uses a command shift of 0 instead of 24 as it should be,
+quirk that.
+
+Signed-off-by: Florian Fainelli <f.fainelli at gmail.com>
+---
+ drivers/mtd/nand/raw/brcmnand/brcmnand.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c
++++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c
+@@ -913,6 +913,12 @@ static void brcmnand_wr_corr_thresh(stru
+ 
+ static inline int brcmnand_cmd_shift(struct brcmnand_controller *ctrl)
+ {
++	/* Kludge for the BCMA-based NAND controller which does not actually
++	 * shift the command
++	 */
++	if (ctrl->nand_version == 0x0304 && brcmnand_non_mmio_ops(ctrl))
++		return 0;
++
+ 	if (ctrl->nand_version < 0x0602)
+ 		return 24;
+ 	return 0;
diff --git a/target/linux/bcm47xx/patches-5.10/108-mtd-rawnand-brcmnand-Add-BCMA-shim.patch b/target/linux/bcm47xx/patches-5.10/108-mtd-rawnand-brcmnand-Add-BCMA-shim.patch
new file mode 100644
index 0000000000..eabb4c2475
--- /dev/null
+++ b/target/linux/bcm47xx/patches-5.10/108-mtd-rawnand-brcmnand-Add-BCMA-shim.patch
@@ -0,0 +1,201 @@
+From: Florian Fainelli <f.fainelli at gmail.com>
+Subject: [PATCH v3 9/9] mtd: rawnand: brcmnand: Add BCMA shim
+Date: Fri, 07 Jan 2022 10:46:14 -0800
+Content-Type: text/plain; charset="utf-8"
+
+Add a BCMA shim to allow us to register the brcmnand driver using the
+BCMA bus which provides indirect memory mapped access to SoC registers.
+
+There are a number of registers that need to be byte swapped because
+they are natively big endian, coming directly from the NAND chip, and
+there is no bus interface unlike the iProc or STB platforms that
+performs the byte swapping for us.
+
+Signed-off-by: Florian Fainelli <f.fainelli at gmail.com>
+---
+ drivers/mtd/nand/raw/Kconfig              |  13 +++
+ drivers/mtd/nand/raw/brcmnand/Makefile    |   2 +
+ drivers/mtd/nand/raw/brcmnand/bcma_nand.c | 132 ++++++++++++++++++++++
+ drivers/mtd/nand/raw/brcmnand/brcmnand.c  |   4 +
+ 4 files changed, 151 insertions(+)
+ create mode 100644 drivers/mtd/nand/raw/brcmnand/bcma_nand.c
+
+--- a/drivers/mtd/nand/raw/Kconfig
++++ b/drivers/mtd/nand/raw/Kconfig
+@@ -236,6 +236,19 @@ config MTD_NAND_BRCMNAND
+ 	  originally designed for Set-Top Box but is used on various BCM7xxx,
+ 	  BCM3xxx, BCM63xxx, iProc/Cygnus and more.
+ 
++if MTD_NAND_BRCMNAND
++
++config MTD_NAND_BRCMNAND_BCMA
++	tristate "Broadcom BCMA NAND controller"
++	depends on BCMA_NFLASH
++	depends on BCMA
++	help
++	  Enables the BRCMNAND controller over BCMA on BCM47186/BCM5358 SoCs.
++	  The glue driver will take care of performing the low-level I/O
++	  operations to interface the BRCMNAND controller over the BCMA bus.
++
++endif # MTD_NAND_BRCMNAND
++
+ config MTD_NAND_BCM47XXNFLASH
+ 	tristate "BCM4706 BCMA NAND controller"
+ 	depends on BCMA_NFLASH
+--- a/drivers/mtd/nand/raw/brcmnand/Makefile
++++ b/drivers/mtd/nand/raw/brcmnand/Makefile
+@@ -6,3 +6,5 @@ obj-$(CONFIG_MTD_NAND_BRCMNAND)		+= bcm6
+ obj-$(CONFIG_MTD_NAND_BRCMNAND)		+= bcm6368_nand.o
+ obj-$(CONFIG_MTD_NAND_BRCMNAND)		+= brcmstb_nand.o
+ obj-$(CONFIG_MTD_NAND_BRCMNAND)		+= brcmnand.o
++
++obj-$(CONFIG_MTD_NAND_BRCMNAND_BCMA)	+= bcma_nand.o
+--- /dev/null
++++ b/drivers/mtd/nand/raw/brcmnand/bcma_nand.c
+@@ -0,0 +1,132 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright © 2021 Broadcom
++ */
++#include <linux/bcma/bcma.h>
++#include <linux/bcma/bcma_driver_chipcommon.h>
++#include <linux/device.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++
++#include "brcmnand.h"
++
++struct brcmnand_bcma_soc {
++	struct brcmnand_soc soc;
++	struct bcma_drv_cc *cc;
++};
++
++static inline bool brcmnand_bcma_needs_swapping(u32 offset)
++{
++	switch (offset) {
++	case BCMA_CC_NAND_SPARE_RD0:
++	case BCMA_CC_NAND_SPARE_RD4:
++	case BCMA_CC_NAND_SPARE_RD8:
++	case BCMA_CC_NAND_SPARE_RD12:
++	case BCMA_CC_NAND_SPARE_WR0:
++	case BCMA_CC_NAND_SPARE_WR4:
++	case BCMA_CC_NAND_SPARE_WR8:
++	case BCMA_CC_NAND_SPARE_WR12:
++	case BCMA_CC_NAND_DEVID:
++	case BCMA_CC_NAND_DEVID_X:
++	case BCMA_CC_NAND_SPARE_RD16:
++	case BCMA_CC_NAND_SPARE_RD20:
++	case BCMA_CC_NAND_SPARE_RD24:
++	case BCMA_CC_NAND_SPARE_RD28:
++		return true;
++	}
++
++	return false;
++}
++
++static inline struct brcmnand_bcma_soc *to_bcma_soc(struct brcmnand_soc *soc)
++{
++	return container_of(soc, struct brcmnand_bcma_soc, soc);
++}
++
++static u32 brcmnand_bcma_read_reg(struct brcmnand_soc *soc, u32 offset)
++{
++	struct brcmnand_bcma_soc *sc = to_bcma_soc(soc);
++	u32 val;
++
++	/* Offset into the NAND block and deal with the flash cache separately */
++	if (offset == BRCMNAND_NON_MMIO_FC_ADDR)
++		offset = BCMA_CC_NAND_CACHE_DATA;
++	else
++		offset += BCMA_CC_NAND_REVISION;
++
++	val = bcma_cc_read32(sc->cc, offset);
++
++	/* Swap if necessary */
++	if (brcmnand_bcma_needs_swapping(offset))
++		val = be32_to_cpu(val);
++	return val;
++}
++
++static void brcmnand_bcma_write_reg(struct brcmnand_soc *soc, u32 val,
++				    u32 offset)
++{
++	struct brcmnand_bcma_soc *sc = to_bcma_soc(soc);
++
++	/* Offset into the NAND block */
++	if (offset == BRCMNAND_NON_MMIO_FC_ADDR)
++		offset = BCMA_CC_NAND_CACHE_DATA;
++	else
++		offset += BCMA_CC_NAND_REVISION;
++
++	/* Swap if necessary */
++	if (brcmnand_bcma_needs_swapping(offset))
++		val = cpu_to_be32(val);
++
++	bcma_cc_write32(sc->cc, offset, val);
++}
++
++static struct brcmnand_io_ops brcmnand_bcma_io_ops = {
++	.read_reg	= brcmnand_bcma_read_reg,
++	.write_reg	= brcmnand_bcma_write_reg,
++};
++
++static void brcmnand_bcma_prepare_data_bus(struct brcmnand_soc *soc, bool prepare,
++					   bool is_param)
++{
++	struct brcmnand_bcma_soc *sc = to_bcma_soc(soc);
++
++	/* Reset the cache address to ensure we are already accessing the
++	 * beginning of a sub-page.
++	 */
++	bcma_cc_write32(sc->cc, BCMA_CC_NAND_CACHE_ADDR, 0);
++}
++
++static int brcmnand_bcma_nand_probe(struct platform_device *pdev)
++{
++	struct bcma_nflash *nflash = dev_get_platdata(&pdev->dev);
++	struct brcmnand_bcma_soc *soc;
++
++	soc = devm_kzalloc(&pdev->dev, sizeof(*soc), GFP_KERNEL);
++	if (!soc)
++		return -ENOMEM;
++
++	soc->cc = container_of(nflash, struct bcma_drv_cc, nflash);
++	soc->soc.prepare_data_bus = brcmnand_bcma_prepare_data_bus;
++	soc->soc.ops = &brcmnand_bcma_io_ops;
++
++	if (soc->cc->core->bus->chipinfo.id == BCMA_CHIP_ID_BCM4706) {
++		dev_err(&pdev->dev, "Use bcm47xxnflash for 4706!\n");
++		return -ENODEV;
++	}
++
++	return brcmnand_probe(pdev, &soc->soc);
++}
++
++static struct platform_driver brcmnand_bcma_nand_driver = {
++	.probe			= brcmnand_bcma_nand_probe,
++	.remove			= brcmnand_remove,
++	.driver = {
++		.name		= "bcma_brcmnand",
++		.pm		= &brcmnand_pm_ops,
++	}
++};
++module_platform_driver(brcmnand_bcma_nand_driver);
++
++MODULE_LICENSE("GPL v2");
++MODULE_AUTHOR("Broadcom");
++MODULE_DESCRIPTION("NAND controller driver glue for BCMA chips");
+--- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c
++++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c
+@@ -595,7 +595,11 @@ enum {
+ 
+ static inline bool brcmnand_non_mmio_ops(struct brcmnand_controller *ctrl)
+ {
++#if IS_ENABLED(CONFIG_MTD_NAND_BRCMNAND_BCMA)
+ 	return static_branch_unlikely(&brcmnand_soc_has_ops_key);
++#else
++	return false;
++#endif
+ }
+ 
+ static inline u32 nand_readreg(struct brcmnand_controller *ctrl, u32 offs)




More information about the lede-commits mailing list