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@gmail.com>
This commit is contained in:
		| @@ -147,8 +147,11 @@ CONFIG_MIPS_L1_CACHE_SHIFT=5 | |||||||
| CONFIG_MODULES_USE_ELF_REL=y | CONFIG_MODULES_USE_ELF_REL=y | ||||||
| CONFIG_MTD_BCM47XXSFLASH=y | CONFIG_MTD_BCM47XXSFLASH=y | ||||||
| CONFIG_MTD_BCM47XX_PARTS=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_BCM47XXNFLASH=y | ||||||
|  | CONFIG_MTD_NAND_BRCMNAND=y | ||||||
|  | CONFIG_MTD_NAND_BRCMNAND_BCMA=y | ||||||
| CONFIG_MTD_NAND_ECC=y | CONFIG_MTD_NAND_ECC=y | ||||||
| CONFIG_MTD_PARSER_TRX=y | CONFIG_MTD_PARSER_TRX=y | ||||||
| CONFIG_MTD_PHYSMAP=y | CONFIG_MTD_PHYSMAP=y | ||||||
|   | |||||||
| @@ -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,0 +1,91 @@ | |||||||
|  | From: Florian Fainelli <f.fainelli@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@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) { | ||||||
| @@ -0,0 +1,115 @@ | |||||||
|  | From: Florian Fainelli <f.fainelli@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@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@lists.infradead.org | ||||||
|  |  L:	bcm-kernel-feedback-list@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@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 */ | ||||||
| @@ -0,0 +1,124 @@ | |||||||
|  | From: Florian Fainelli <f.fainelli@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@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; | ||||||
| @@ -0,0 +1,29 @@ | |||||||
|  | From: Florian Fainelli <f.fainelli@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@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; | ||||||
| @@ -0,0 +1,201 @@ | |||||||
|  | From: Florian Fainelli <f.fainelli@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@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) | ||||||
		Reference in New Issue
	
	Block a user
	 Florian Fainelli
					Florian Fainelli