Skip to content

Commit

Permalink
bcm47xx: Add support for brcmnand controller on BCMA bus
Browse files Browse the repository at this point in the history
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@gmail.com>
  • Loading branch information
ffainelli committed Jun 20, 2022
1 parent cd3de51 commit acff8ae
Show file tree
Hide file tree
Showing 10 changed files with 862 additions and 1 deletion.
5 changes: 4 additions & 1 deletion target/linux/bcm47xx/config-5.10
Expand Up @@ -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
Expand Down
@@ -0,0 +1,33 @@
From: Florian Fainelli <f.fainelli@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@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);

@@ -0,0 +1,150 @@
From: Florian Fainelli <f.fainelli@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@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);

@@ -0,0 +1,52 @@
From: Florian Fainelli <f.fainelli@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@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;
@@ -0,0 +1,63 @@
From: Florian Fainelli <f.fainelli@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@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 */

0 comments on commit acff8ae

Please sign in to comment.