diff mbox

[1/2] P4080/eLBC: Make Freescale elbc interrupt common to elbc devices

Message ID 1287386552-10647-1-git-send-email-tie-fei.zang@freescale.com (mailing list archive)
State Accepted, archived
Delegated to: Kumar Gala
Headers show

Commit Message

Zang Roy-R61911 Oct. 18, 2010, 7:22 a.m. UTC
Move Freescale elbc interrupt from nand dirver to elbc driver.
Then all elbc devices can use the interrupt instead of ONLY nand.

For former nand driver, it had the two functions:

1. detecting nand flash partitions;
2. registering elbc interrupt.

Now, second function is removed to fsl_lbc.c.

Signed-off-by: Lan Chunhe-B25806 <b25806@freescale.com>
Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
Reviewed-by: Anton Vorontsov <cbouatmailru@gmail.com>
Cc: Wood Scott-B07421 <B07421@freescale.com>
---

These two patches are based on the following commits:
1.	http://lists.infradead.org/pipermail/linux-mtd/2010-September/032112.html
2.	http://lists.infradead.org/pipermail/linux-mtd/2010-September/032110.html
3.	http://lists.infradead.org/pipermail/linux-mtd/2010-September/032111.html
According to Anton's comment, I merge 1 & 2 together and start a new thread.
Comparing the provided link:
1.	Merge 1 & 2 together.
2.	Some code style updates
3.	Add counter protect for elbc driver remove 
4.	Rebase to 2.6.36-rc7

Other histories from the links:
V2: Comparing with v1, according to the feedback, add some decorations.

V3: Comparing with v2:
1.	according to the feedback, add some decorations.
2.	change of_platform_driver to platform_driver
3.	rebase to 2.6.36-rc4

V4: Comparing with v3
1.	minor fix from type unsigned int to u32
2.	fix platform_driver issue.
3.	add mutex for nand probe

 arch/powerpc/Kconfig               |    7 +-
 arch/powerpc/include/asm/fsl_lbc.h |   33 +++-
 arch/powerpc/sysdev/fsl_lbc.c      |  229 ++++++++++++++---
 drivers/mtd/nand/Kconfig           |    1 +
 drivers/mtd/nand/fsl_elbc_nand.c   |  482 +++++++++++++++---------------------
 5 files changed, 425 insertions(+), 327 deletions(-)

Comments

Tiejun Chen Oct. 18, 2010, 8:55 a.m. UTC | #1
Roy Zang wrote:
> Move Freescale elbc interrupt from nand dirver to elbc driver.
> Then all elbc devices can use the interrupt instead of ONLY nand.
> 
> For former nand driver, it had the two functions:
> 
> 1. detecting nand flash partitions;
> 2. registering elbc interrupt.
> 
> Now, second function is removed to fsl_lbc.c.
> 
> Signed-off-by: Lan Chunhe-B25806 <b25806@freescale.com>
> Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
> Reviewed-by: Anton Vorontsov <cbouatmailru@gmail.com>
> Cc: Wood Scott-B07421 <B07421@freescale.com>
> ---
> 
> These two patches are based on the following commits:
> 1.	http://lists.infradead.org/pipermail/linux-mtd/2010-September/032112.html
> 2.	http://lists.infradead.org/pipermail/linux-mtd/2010-September/032110.html
> 3.	http://lists.infradead.org/pipermail/linux-mtd/2010-September/032111.html
> According to Anton's comment, I merge 1 & 2 together and start a new thread.
> Comparing the provided link:
> 1.	Merge 1 & 2 together.
> 2.	Some code style updates
> 3.	Add counter protect for elbc driver remove 
> 4.	Rebase to 2.6.36-rc7
> 
> Other histories from the links:
> V2: Comparing with v1, according to the feedback, add some decorations.
> 
> V3: Comparing with v2:
> 1.	according to the feedback, add some decorations.
> 2.	change of_platform_driver to platform_driver
> 3.	rebase to 2.6.36-rc4
> 
> V4: Comparing with v3
> 1.	minor fix from type unsigned int to u32
> 2.	fix platform_driver issue.
> 3.	add mutex for nand probe
> 
>  arch/powerpc/Kconfig               |    7 +-
>  arch/powerpc/include/asm/fsl_lbc.h |   33 +++-
>  arch/powerpc/sysdev/fsl_lbc.c      |  229 ++++++++++++++---
>  drivers/mtd/nand/Kconfig           |    1 +
>  drivers/mtd/nand/fsl_elbc_nand.c   |  482 +++++++++++++++---------------------
>  5 files changed, 425 insertions(+), 327 deletions(-)
> 
> diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
> index 631e5a0..44df1ba 100644
> --- a/arch/powerpc/Kconfig
> +++ b/arch/powerpc/Kconfig
> @@ -687,9 +687,12 @@ config 4xx_SOC
>  	bool
>  
>  config FSL_LBC
> -	bool
> +	bool "Freescale Local Bus support"
> +	depends on FSL_SOC
>  	help
> -	  Freescale Localbus support
> +	  Enables reporting of errors from the Freescale local bus
> +	  controller.  Also contains some common code used by
> +	  drivers for specific local bus peripherals.
>  
>  config FSL_GTM
>  	bool
> diff --git a/arch/powerpc/include/asm/fsl_lbc.h b/arch/powerpc/include/asm/fsl_lbc.h
> index 1b5a210..0c40c05 100644
> --- a/arch/powerpc/include/asm/fsl_lbc.h
> +++ b/arch/powerpc/include/asm/fsl_lbc.h
> @@ -1,9 +1,10 @@
>  /* Freescale Local Bus Controller
>   *
> - * Copyright (c) 2006-2007 Freescale Semiconductor
> + * Copyright (c) 2006-2007, 2010 Freescale Semiconductor
>   *
>   * Authors: Nick Spence <nick.spence@freescale.com>,
>   *          Scott Wood <scottwood@freescale.com>
> + *          Jack Lan <jack.lan@freescale.com>
>   *
>   * This program is free software; you can redistribute it and/or modify
>   * it under the terms of the GNU General Public License as published by
> @@ -26,6 +27,8 @@
>  #include <linux/compiler.h>
>  #include <linux/types.h>
>  #include <linux/io.h>
> +#include <linux/device.h>
> +#include <linux/spinlock.h>
>  
>  struct fsl_lbc_bank {
>  	__be32 br;             /**< Base Register  */
> @@ -125,13 +128,23 @@ struct fsl_lbc_regs {
>  #define LTESR_ATMW 0x00800000
>  #define LTESR_ATMR 0x00400000
>  #define LTESR_CS   0x00080000
> +#define LTESR_UPM  0x00000002
>  #define LTESR_CC   0x00000001
>  #define LTESR_NAND_MASK (LTESR_FCT | LTESR_PAR | LTESR_CC)
> +#define LTESR_MASK      (LTESR_BM | LTESR_FCT | LTESR_PAR | LTESR_WP \
> +			 | LTESR_ATMW | LTESR_ATMR | LTESR_CS | LTESR_UPM \
> +			 | LTESR_CC)
> +#define LTESR_CLEAR	0xFFFFFFFF
> +#define LTECCR_CLEAR	0xFFFFFFFF
> +#define LTESR_STATUS	LTESR_MASK
> +#define LTEIR_ENABLE	LTESR_MASK
> +#define LTEDR_ENABLE	0x00000000
>  	__be32 ltedr;           /**< Transfer Error Disable Register */
>  	__be32 lteir;           /**< Transfer Error Interrupt Register */
>  	__be32 lteatr;          /**< Transfer Error Attributes Register */
>  	__be32 ltear;           /**< Transfer Error Address Register */
> -	u8 res6[0xC];
> +	__be32 lteccr;          /**< Transfer Error ECC Register */
> +	u8 res6[0x8];
>  	__be32 lbcr;            /**< Configuration Register */
>  #define LBCR_LDIS  0x80000000
>  #define LBCR_LDIS_SHIFT    31
> @@ -265,7 +278,23 @@ static inline void fsl_upm_end_pattern(struct fsl_upm *upm)
>  		cpu_relax();
>  }
>  
> +/* overview of the fsl lbc controller */
> +
> +struct fsl_lbc_ctrl {
> +	/* device info */
> +	struct device			*dev;
> +	struct fsl_lbc_regs __iomem	*regs;
> +	int				irq;
> +	wait_queue_head_t		irq_wait;
> +	spinlock_t			lock;
> +	void				*nand;
> +
> +	/* status read from LTESR by irq handler */
> +	unsigned int			irq_status;
> +};
> +
>  extern int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base,
>  			       u32 mar);
> +extern struct fsl_lbc_ctrl *fsl_lbc_ctrl_dev;
>  
>  #endif /* __ASM_FSL_LBC_H */
> diff --git a/arch/powerpc/sysdev/fsl_lbc.c b/arch/powerpc/sysdev/fsl_lbc.c
> index dceb8d1..4bb0336 100644
> --- a/arch/powerpc/sysdev/fsl_lbc.c
> +++ b/arch/powerpc/sysdev/fsl_lbc.c
> @@ -2,8 +2,11 @@
>   * Freescale LBC and UPM routines.
>   *
>   * Copyright (c) 2007-2008  MontaVista Software, Inc.
> + * Copyright (c) 2010 Freescale Semiconductor
>   *
>   * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
> + * Author: Jack Lan <Jack.Lan@freescale.com>
> + * Author: Roy Zang <tie-fei.zang@freescale.com>
>   *
>   * This program is free software; you can redistribute it and/or modify
>   * it under the terms of the GNU General Public License as published by
> @@ -19,39 +22,16 @@
>  #include <linux/types.h>
>  #include <linux/io.h>
>  #include <linux/of.h>
> +#include <linux/slab.h>
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +#include <linux/mod_devicetable.h>
>  #include <asm/prom.h>
>  #include <asm/fsl_lbc.h>
>  
>  static spinlock_t fsl_lbc_lock = __SPIN_LOCK_UNLOCKED(fsl_lbc_lock);
> -static struct fsl_lbc_regs __iomem *fsl_lbc_regs;
> -
> -static char __initdata *compat_lbc[] = {
> -	"fsl,pq2-localbus",
> -	"fsl,pq2pro-localbus",
> -	"fsl,pq3-localbus",
> -	"fsl,elbc",
> -};
> -
> -static int __init fsl_lbc_init(void)
> -{
> -	struct device_node *lbus;
> -	int i;
> -
> -	for (i = 0; i < ARRAY_SIZE(compat_lbc); i++) {
> -		lbus = of_find_compatible_node(NULL, NULL, compat_lbc[i]);
> -		if (lbus)
> -			goto found;
> -	}
> -	return -ENODEV;
> -
> -found:
> -	fsl_lbc_regs = of_iomap(lbus, 0);
> -	of_node_put(lbus);
> -	if (!fsl_lbc_regs)
> -		return -ENOMEM;
> -	return 0;
> -}
> -arch_initcall(fsl_lbc_init);
> +struct fsl_lbc_ctrl *fsl_lbc_ctrl_dev;
> +EXPORT_SYMBOL(fsl_lbc_ctrl_dev);
>  
>  /**
>   * fsl_lbc_find - find Localbus bank
> @@ -65,13 +45,15 @@ arch_initcall(fsl_lbc_init);
>  int fsl_lbc_find(phys_addr_t addr_base)
>  {
>  	int i;
> +	struct fsl_lbc_regs __iomem *lbc;
>  
> -	if (!fsl_lbc_regs)
> +	if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
>  		return -ENODEV;
>  
> -	for (i = 0; i < ARRAY_SIZE(fsl_lbc_regs->bank); i++) {
> -		__be32 br = in_be32(&fsl_lbc_regs->bank[i].br);
> -		__be32 or = in_be32(&fsl_lbc_regs->bank[i].or);
> +	lbc = fsl_lbc_ctrl_dev->regs;
> +	for (i = 0; i < ARRAY_SIZE(lbc->bank); i++) {
> +		__be32 br = in_be32(&lbc->bank[i].br);
> +		__be32 or = in_be32(&lbc->bank[i].or);
>  
>  		if (br & BR_V && (br & or & BR_BA) == addr_base)
>  			return i;
> @@ -94,22 +76,27 @@ int fsl_upm_find(phys_addr_t addr_base, struct fsl_upm *upm)
>  {
>  	int bank;
>  	__be32 br;
> +	struct fsl_lbc_regs __iomem *lbc;
>  
>  	bank = fsl_lbc_find(addr_base);
>  	if (bank < 0)
>  		return bank;
>  
> -	br = in_be32(&fsl_lbc_regs->bank[bank].br);
> +	if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
> +		return -ENODEV;
> +
> +	lbc = fsl_lbc_ctrl_dev->regs;
> +	br = in_be32(&lbc->bank[bank].br);
>  
>  	switch (br & BR_MSEL) {
>  	case BR_MS_UPMA:
> -		upm->mxmr = &fsl_lbc_regs->mamr;
> +		upm->mxmr = &lbc->mamr;
>  		break;
>  	case BR_MS_UPMB:
> -		upm->mxmr = &fsl_lbc_regs->mbmr;
> +		upm->mxmr = &lbc->mbmr;
>  		break;
>  	case BR_MS_UPMC:
> -		upm->mxmr = &fsl_lbc_regs->mcmr;
> +		upm->mxmr = &lbc->mcmr;
>  		break;
>  	default:
>  		return -EINVAL;
> @@ -148,9 +135,12 @@ int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, u32 mar)
>  	int ret = 0;
>  	unsigned long flags;
>  
> +	if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
> +		return -ENODEV;
> +
>  	spin_lock_irqsave(&fsl_lbc_lock, flags);
>  
> -	out_be32(&fsl_lbc_regs->mar, mar);
> +	out_be32(&fsl_lbc_ctrl_dev->regs->mar, mar);
>  
>  	switch (upm->width) {
>  	case 8:
> @@ -172,3 +162,166 @@ int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, u32 mar)
>  	return ret;
>  }
>  EXPORT_SYMBOL(fsl_upm_run_pattern);
> +
> +static int __devinit fsl_lbc_ctrl_init(struct fsl_lbc_ctrl *ctrl)
> +{
> +	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
> +
> +	/* clear event registers */
> +	setbits32(&lbc->ltesr, LTESR_CLEAR);
> +	out_be32(&lbc->lteatr, 0);
> +	out_be32(&lbc->ltear, 0);
> +	out_be32(&lbc->lteccr, LTECCR_CLEAR);
> +	out_be32(&lbc->ltedr, LTEDR_ENABLE);
> +
> +	/* Enable interrupts for any detected events */
> +	out_be32(&lbc->lteir, LTEIR_ENABLE);
> +
> +	return 0;
> +}
> +
> +/*
> + * NOTE: This interrupt is used to report localbus events of various kinds,
> + * such as transaction errors on the chipselects.
> + */
> +
> +static irqreturn_t fsl_lbc_ctrl_irq(int irqno, void *data)
> +{
> +	struct fsl_lbc_ctrl *ctrl = data;
> +	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
> +	u32 status;
> +
> +	status = in_be32(&lbc->ltesr);
> +	if (!status)
> +		return IRQ_NONE;
> +
> +	out_be32(&lbc->ltesr, LTESR_CLEAR);
> +	out_be32(&lbc->lteatr, 0);
> +	out_be32(&lbc->ltear, 0);
> +	ctrl->irq_status = status;
> +
> +	if (status & LTESR_BM)
> +		dev_err(ctrl->dev, "Local bus monitor time-out: "
> +			"LTESR 0x%08X\n", status);
> +	if (status & LTESR_WP)
> +		dev_err(ctrl->dev, "Write protect error: "
> +			"LTESR 0x%08X\n", status);
> +	if (status & LTESR_ATMW)
> +		dev_err(ctrl->dev, "Atomic write error: "
> +			"LTESR 0x%08X\n", status);
> +	if (status & LTESR_ATMR)
> +		dev_err(ctrl->dev, "Atomic read error: "
> +			"LTESR 0x%08X\n", status);
> +	if (status & LTESR_CS)
> +		dev_err(ctrl->dev, "Chip select error: "
> +			"LTESR 0x%08X\n", status);
> +	if (status & LTESR_UPM)
> +		;
> +	if (status & LTESR_FCT) {
> +		dev_err(ctrl->dev, "FCM command time-out: "
> +			"LTESR 0x%08X\n", status);
> +		smp_wmb();
> +		wake_up(&ctrl->irq_wait);
> +	}
> +	if (status & LTESR_PAR) {
> +		dev_err(ctrl->dev, "Parity or Uncorrectable ECC error: "
> +			"LTESR 0x%08X\n", status);
> +		smp_wmb();
> +		wake_up(&ctrl->irq_wait);
> +	}
> +	if (status & LTESR_CC) {
> +		smp_wmb();
> +		wake_up(&ctrl->irq_wait);
> +	}
> +	if (status & ~LTESR_MASK)
> +		dev_err(ctrl->dev, "Unknown error: "
> +			"LTESR 0x%08X\n", status);
> +	return IRQ_HANDLED;
> +}
> +
> +/*
> + * fsl_lbc_ctrl_probe
> + *
> + * called by device layer when it finds a device matching
> + * one our driver can handled. This code allocates all of
> + * the resources needed for the controller only.  The
> + * resources for the NAND banks themselves are allocated
> + * in the chip probe function.
> +*/
> +
> +static int __devinit fsl_lbc_ctrl_probe(struct platform_device *dev)
> +{
> +	int ret;
> +
> +	if (!dev->dev.of_node) {
> +		dev_err(&dev->dev, "Device OF-Node is NULL");
> +		return -EFAULT;
> +	}
> +
> +	fsl_lbc_ctrl_dev = kzalloc(sizeof(*fsl_lbc_ctrl_dev), GFP_KERNEL);
> +	if (!fsl_lbc_ctrl_dev)
> +		return -ENOMEM;
> +
> +	dev_set_drvdata(&dev->dev, fsl_lbc_ctrl_dev);
> +
> +	spin_lock_init(&fsl_lbc_ctrl_dev->lock);
> +	init_waitqueue_head(&fsl_lbc_ctrl_dev->irq_wait);
> +
> +	fsl_lbc_ctrl_dev->regs = of_iomap(dev->dev.of_node, 0);
> +	if (!fsl_lbc_ctrl_dev->regs) {
> +		dev_err(&dev->dev, "failed to get memory region\n");
> +		ret = -ENODEV;
> +		goto err;

Looks you always iounmap(fsl_lbc_ctrl_dev->regs) on position 'err' but here
of_iomap() is already failed you should skip iounmap() fsl_lbc_ctrl_dev->regs
again. So you should improve that as the following on 'err', or layout 'err' in
gain.
------
	if(fsl_lbc_ctrl_dev->regs)
		iounmap(fsl_lbc_ctrl_dev->regs);

Tiejun

> +	}
> +
> +	fsl_lbc_ctrl_dev->irq = irq_of_parse_and_map(dev->dev.of_node, 0);
> +	if (fsl_lbc_ctrl_dev->irq == NO_IRQ) {
> +		dev_err(&dev->dev, "failed to get irq resource\n");
> +		ret = -ENODEV;
> +		goto err;
> +	}
> +
> +	fsl_lbc_ctrl_dev->dev = &dev->dev;
> +
> +	ret = fsl_lbc_ctrl_init(fsl_lbc_ctrl_dev);
> +	if (ret < 0)
> +		goto err;
> +
> +	ret = request_irq(fsl_lbc_ctrl_dev->irq, fsl_lbc_ctrl_irq, 0,
> +				"fsl-lbc", fsl_lbc_ctrl_dev);
> +	if (ret != 0) {
> +		dev_err(&dev->dev, "failed to install irq (%d)\n",
> +			fsl_lbc_ctrl_dev->irq);
> +		ret = fsl_lbc_ctrl_dev->irq;
> +		goto err;
> +	}
> +
> +	return 0;
> +
> +err:
> +	iounmap(fsl_lbc_ctrl_dev->regs);
> +	kfree(fsl_lbc_ctrl_dev);
> +	return ret;
> +}
> +
> +static const struct of_device_id fsl_lbc_match[] = {
> +	{ .compatible = "fsl,elbc", },
> +	{ .compatible = "fsl,pq3-localbus", },
> +	{ .compatible = "fsl,pq2-localbus", },
> +	{ .compatible = "fsl,pq2pro-localbus", },
> +	{},
> +};
> +
> +static struct platform_driver fsl_lbc_ctrl_driver = {
> +	.driver = {
> +		.name = "fsl-lbc",
> +		.of_match_table = fsl_lbc_match,
> +	},
> +	.probe = fsl_lbc_ctrl_probe,
> +};
> +
> +static int __init fsl_lbc_init(void)
> +{
> +	return platform_driver_register(&fsl_lbc_ctrl_driver);
> +}
> +module_init(fsl_lbc_init);
> diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
> index 8b4b67c..4132c46 100644
> --- a/drivers/mtd/nand/Kconfig
> +++ b/drivers/mtd/nand/Kconfig
> @@ -458,6 +458,7 @@ config MTD_NAND_ORION
>  config MTD_NAND_FSL_ELBC
>  	tristate "NAND support for Freescale eLBC controllers"
>  	depends on PPC_OF
> +	select FSL_LBC
>  	help
>  	  Various Freescale chips, including the 8313, include a NAND Flash
>  	  Controller Module with built-in hardware ECC capabilities.
> diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c
> index 80de0bf..400f01f 100644
> --- a/drivers/mtd/nand/fsl_elbc_nand.c
> +++ b/drivers/mtd/nand/fsl_elbc_nand.c
> @@ -1,9 +1,11 @@
>  /* Freescale Enhanced Local Bus Controller NAND driver
>   *
> - * Copyright (c) 2006-2007 Freescale Semiconductor
> + * Copyright (c) 2006-2007, 2010 Freescale Semiconductor
>   *
>   * Authors: Nick Spence <nick.spence@freescale.com>,
>   *          Scott Wood <scottwood@freescale.com>
> + *          Jack Lan <jack.lan@freescale.com>
> + *          Roy Zang <tie-fei.zang@freescale.com>
>   *
>   * This program is free software; you can redistribute it and/or modify
>   * it under the terms of the GNU General Public License as published by
> @@ -27,6 +29,7 @@
>  #include <linux/string.h>
>  #include <linux/ioport.h>
>  #include <linux/of_platform.h>
> +#include <linux/platform_device.h>
>  #include <linux/slab.h>
>  #include <linux/interrupt.h>
>  
> @@ -42,14 +45,12 @@
>  #define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */
>  #define FCM_TIMEOUT_MSECS 500 /* Maximum number of mSecs to wait for FCM */
>  
> -struct fsl_elbc_ctrl;
> -
>  /* mtd information per set */
>  
>  struct fsl_elbc_mtd {
>  	struct mtd_info mtd;
>  	struct nand_chip chip;
> -	struct fsl_elbc_ctrl *ctrl;
> +	struct fsl_lbc_ctrl *ctrl;
>  
>  	struct device *dev;
>  	int bank;               /* Chip select bank number           */
> @@ -58,18 +59,12 @@ struct fsl_elbc_mtd {
>  	unsigned int fmr;       /* FCM Flash Mode Register value     */
>  };
>  
> -/* overview of the fsl elbc controller */
> +/* Freescale eLBC FCM controller infomation */
>  
> -struct fsl_elbc_ctrl {
> +struct fsl_elbc_fcm_ctrl {
>  	struct nand_hw_control controller;
>  	struct fsl_elbc_mtd *chips[MAX_BANKS];
>  
> -	/* device info */
> -	struct device *dev;
> -	struct fsl_lbc_regs __iomem *regs;
> -	int irq;
> -	wait_queue_head_t irq_wait;
> -	unsigned int irq_status; /* status read from LTESR by irq handler */
>  	u8 __iomem *addr;        /* Address of assigned FCM buffer        */
>  	unsigned int page;       /* Last page written to / read from      */
>  	unsigned int read_bytes; /* Number of bytes read during command   */
> @@ -79,6 +74,7 @@ struct fsl_elbc_ctrl {
>  	unsigned int mdr;        /* UPM/FCM Data Register value           */
>  	unsigned int use_mdr;    /* Non zero if the MDR is to be set      */
>  	unsigned int oob;        /* Non zero if operating on OOB data     */
> +	unsigned int counter;	 /* counter for the initializations	  */
>  	char *oob_poi;           /* Place to write ECC after read back    */
>  };
>  
> @@ -164,11 +160,12 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
>  {
>  	struct nand_chip *chip = mtd->priv;
>  	struct fsl_elbc_mtd *priv = chip->priv;
> -	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
> +	struct fsl_lbc_ctrl *ctrl = priv->ctrl;
>  	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
> +	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
>  	int buf_num;
>  
> -	ctrl->page = page_addr;
> +	elbc_fcm_ctrl->page = page_addr;
>  
>  	out_be32(&lbc->fbar,
>  	         page_addr >> (chip->phys_erase_shift - chip->page_shift));
> @@ -185,16 +182,18 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
>  		buf_num = page_addr & 7;
>  	}
>  
> -	ctrl->addr = priv->vbase + buf_num * 1024;
> -	ctrl->index = column;
> +	elbc_fcm_ctrl->addr = priv->vbase + buf_num * 1024;
> +	elbc_fcm_ctrl->index = column;
>  
>  	/* for OOB data point to the second half of the buffer */
>  	if (oob)
> -		ctrl->index += priv->page_size ? 2048 : 512;
> +		elbc_fcm_ctrl->index += priv->page_size ? 2048 : 512;
>  
> -	dev_vdbg(ctrl->dev, "set_addr: bank=%d, ctrl->addr=0x%p (0x%p), "
> +	dev_vdbg(priv->dev, "set_addr: bank=%d, "
> +			    "elbc_fcm_ctrl->addr=0x%p (0x%p), "
>  	                    "index %x, pes %d ps %d\n",
> -	         buf_num, ctrl->addr, priv->vbase, ctrl->index,
> +		 buf_num, elbc_fcm_ctrl->addr, priv->vbase,
> +		 elbc_fcm_ctrl->index,
>  	         chip->phys_erase_shift, chip->page_shift);
>  }
>  
> @@ -205,18 +204,19 @@ static int fsl_elbc_run_command(struct mtd_info *mtd)
>  {
>  	struct nand_chip *chip = mtd->priv;
>  	struct fsl_elbc_mtd *priv = chip->priv;
> -	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
> +	struct fsl_lbc_ctrl *ctrl = priv->ctrl;
> +	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
>  	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
>  
>  	/* Setup the FMR[OP] to execute without write protection */
>  	out_be32(&lbc->fmr, priv->fmr | 3);
> -	if (ctrl->use_mdr)
> -		out_be32(&lbc->mdr, ctrl->mdr);
> +	if (elbc_fcm_ctrl->use_mdr)
> +		out_be32(&lbc->mdr, elbc_fcm_ctrl->mdr);
>  
> -	dev_vdbg(ctrl->dev,
> +	dev_vdbg(priv->dev,
>  	         "fsl_elbc_run_command: fmr=%08x fir=%08x fcr=%08x\n",
>  	         in_be32(&lbc->fmr), in_be32(&lbc->fir), in_be32(&lbc->fcr));
> -	dev_vdbg(ctrl->dev,
> +	dev_vdbg(priv->dev,
>  	         "fsl_elbc_run_command: fbar=%08x fpar=%08x "
>  	         "fbcr=%08x bank=%d\n",
>  	         in_be32(&lbc->fbar), in_be32(&lbc->fpar),
> @@ -229,19 +229,18 @@ static int fsl_elbc_run_command(struct mtd_info *mtd)
>  	/* wait for FCM complete flag or timeout */
>  	wait_event_timeout(ctrl->irq_wait, ctrl->irq_status,
>  	                   FCM_TIMEOUT_MSECS * HZ/1000);
> -	ctrl->status = ctrl->irq_status;
> -
> +	elbc_fcm_ctrl->status = ctrl->irq_status;
>  	/* store mdr value in case it was needed */
> -	if (ctrl->use_mdr)
> -		ctrl->mdr = in_be32(&lbc->mdr);
> +	if (elbc_fcm_ctrl->use_mdr)
> +		elbc_fcm_ctrl->mdr = in_be32(&lbc->mdr);
>  
> -	ctrl->use_mdr = 0;
> +	elbc_fcm_ctrl->use_mdr = 0;
>  
> -	if (ctrl->status != LTESR_CC) {
> -		dev_info(ctrl->dev,
> +	if (elbc_fcm_ctrl->status != LTESR_CC) {
> +		dev_info(priv->dev,
>  		         "command failed: fir %x fcr %x status %x mdr %x\n",
>  		         in_be32(&lbc->fir), in_be32(&lbc->fcr),
> -		         ctrl->status, ctrl->mdr);
> +			 elbc_fcm_ctrl->status, elbc_fcm_ctrl->mdr);
>  		return -EIO;
>  	}
>  
> @@ -251,7 +250,7 @@ static int fsl_elbc_run_command(struct mtd_info *mtd)
>  static void fsl_elbc_do_read(struct nand_chip *chip, int oob)
>  {
>  	struct fsl_elbc_mtd *priv = chip->priv;
> -	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
> +	struct fsl_lbc_ctrl *ctrl = priv->ctrl;
>  	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
>  
>  	if (priv->page_size) {
> @@ -284,15 +283,16 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
>  {
>  	struct nand_chip *chip = mtd->priv;
>  	struct fsl_elbc_mtd *priv = chip->priv;
> -	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
> +	struct fsl_lbc_ctrl *ctrl = priv->ctrl;
> +	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
>  	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
>  
> -	ctrl->use_mdr = 0;
> +	elbc_fcm_ctrl->use_mdr = 0;
>  
>  	/* clear the read buffer */
> -	ctrl->read_bytes = 0;
> +	elbc_fcm_ctrl->read_bytes = 0;
>  	if (command != NAND_CMD_PAGEPROG)
> -		ctrl->index = 0;
> +		elbc_fcm_ctrl->index = 0;
>  
>  	switch (command) {
>  	/* READ0 and READ1 read the entire buffer to use hardware ECC. */
> @@ -301,7 +301,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
>  
>  	/* fall-through */
>  	case NAND_CMD_READ0:
> -		dev_dbg(ctrl->dev,
> +		dev_dbg(priv->dev,
>  		        "fsl_elbc_cmdfunc: NAND_CMD_READ0, page_addr:"
>  		        " 0x%x, column: 0x%x.\n", page_addr, column);
>  
> @@ -309,8 +309,8 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
>  		out_be32(&lbc->fbcr, 0); /* read entire page to enable ECC */
>  		set_addr(mtd, 0, page_addr, 0);
>  
> -		ctrl->read_bytes = mtd->writesize + mtd->oobsize;
> -		ctrl->index += column;
> +		elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize;
> +		elbc_fcm_ctrl->index += column;
>  
>  		fsl_elbc_do_read(chip, 0);
>  		fsl_elbc_run_command(mtd);
> @@ -318,14 +318,14 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
>  
>  	/* READOOB reads only the OOB because no ECC is performed. */
>  	case NAND_CMD_READOOB:
> -		dev_vdbg(ctrl->dev,
> +		dev_vdbg(priv->dev,
>  		         "fsl_elbc_cmdfunc: NAND_CMD_READOOB, page_addr:"
>  			 " 0x%x, column: 0x%x.\n", page_addr, column);
>  
>  		out_be32(&lbc->fbcr, mtd->oobsize - column);
>  		set_addr(mtd, column, page_addr, 1);
>  
> -		ctrl->read_bytes = mtd->writesize + mtd->oobsize;
> +		elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize;
>  
>  		fsl_elbc_do_read(chip, 1);
>  		fsl_elbc_run_command(mtd);
> @@ -333,7 +333,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
>  
>  	/* READID must read all 5 possible bytes while CEB is active */
>  	case NAND_CMD_READID:
> -		dev_vdbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_READID.\n");
> +		dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_READID.\n");
>  
>  		out_be32(&lbc->fir, (FIR_OP_CM0 << FIR_OP0_SHIFT) |
>  		                    (FIR_OP_UA  << FIR_OP1_SHIFT) |
> @@ -341,9 +341,9 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
>  		out_be32(&lbc->fcr, NAND_CMD_READID << FCR_CMD0_SHIFT);
>  		/* 5 bytes for manuf, device and exts */
>  		out_be32(&lbc->fbcr, 5);
> -		ctrl->read_bytes = 5;
> -		ctrl->use_mdr = 1;
> -		ctrl->mdr = 0;
> +		elbc_fcm_ctrl->read_bytes = 5;
> +		elbc_fcm_ctrl->use_mdr = 1;
> +		elbc_fcm_ctrl->mdr = 0;
>  
>  		set_addr(mtd, 0, 0, 0);
>  		fsl_elbc_run_command(mtd);
> @@ -351,7 +351,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
>  
>  	/* ERASE1 stores the block and page address */
>  	case NAND_CMD_ERASE1:
> -		dev_vdbg(ctrl->dev,
> +		dev_vdbg(priv->dev,
>  		         "fsl_elbc_cmdfunc: NAND_CMD_ERASE1, "
>  		         "page_addr: 0x%x.\n", page_addr);
>  		set_addr(mtd, 0, page_addr, 0);
> @@ -359,7 +359,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
>  
>  	/* ERASE2 uses the block and page address from ERASE1 */
>  	case NAND_CMD_ERASE2:
> -		dev_vdbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n");
> +		dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n");
>  
>  		out_be32(&lbc->fir,
>  		         (FIR_OP_CM0 << FIR_OP0_SHIFT) |
> @@ -374,8 +374,8 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
>  		         (NAND_CMD_ERASE2 << FCR_CMD2_SHIFT));
>  
>  		out_be32(&lbc->fbcr, 0);
> -		ctrl->read_bytes = 0;
> -		ctrl->use_mdr = 1;
> +		elbc_fcm_ctrl->read_bytes = 0;
> +		elbc_fcm_ctrl->use_mdr = 1;
>  
>  		fsl_elbc_run_command(mtd);
>  		return;
> @@ -383,14 +383,12 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
>  	/* SEQIN sets up the addr buffer and all registers except the length */
>  	case NAND_CMD_SEQIN: {
>  		__be32 fcr;
> -		dev_vdbg(ctrl->dev,
> -		         "fsl_elbc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, "
> +		dev_vdbg(priv->dev,
> +			 "fsl_elbc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, "
>  		         "page_addr: 0x%x, column: 0x%x.\n",
>  		         page_addr, column);
>  
> -		ctrl->column = column;
> -		ctrl->oob = 0;
> -		ctrl->use_mdr = 1;
> +		elbc_fcm_ctrl->use_mdr = 1;
>  
>  		fcr = (NAND_CMD_STATUS   << FCR_CMD1_SHIFT) |
>  		      (NAND_CMD_SEQIN    << FCR_CMD2_SHIFT) |
> @@ -420,7 +418,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
>  				/* OOB area --> READOOB */
>  				column -= mtd->writesize;
>  				fcr |= NAND_CMD_READOOB << FCR_CMD0_SHIFT;
> -				ctrl->oob = 1;
> +				elbc_fcm_ctrl->oob = 1;
>  			} else {
>  				WARN_ON(column != 0);
>  				/* First 256 bytes --> READ0 */
> @@ -429,24 +427,24 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
>  		}
>  
>  		out_be32(&lbc->fcr, fcr);
> -		set_addr(mtd, column, page_addr, ctrl->oob);
> +		set_addr(mtd, column, page_addr, elbc_fcm_ctrl->oob);
>  		return;
>  	}
>  
>  	/* PAGEPROG reuses all of the setup from SEQIN and adds the length */
>  	case NAND_CMD_PAGEPROG: {
>  		int full_page;
> -		dev_vdbg(ctrl->dev,
> +		dev_vdbg(priv->dev,
>  		         "fsl_elbc_cmdfunc: NAND_CMD_PAGEPROG "
> -		         "writing %d bytes.\n", ctrl->index);
> +			 "writing %d bytes.\n", elbc_fcm_ctrl->index);
>  
>  		/* if the write did not start at 0 or is not a full page
>  		 * then set the exact length, otherwise use a full page
>  		 * write so the HW generates the ECC.
>  		 */
> -		if (ctrl->oob || ctrl->column != 0 ||
> -		    ctrl->index != mtd->writesize + mtd->oobsize) {
> -			out_be32(&lbc->fbcr, ctrl->index);
> +		if (elbc_fcm_ctrl->oob || elbc_fcm_ctrl->column != 0 ||
> +		    elbc_fcm_ctrl->index != mtd->writesize + mtd->oobsize) {
> +			out_be32(&lbc->fbcr, elbc_fcm_ctrl->index);
>  			full_page = 0;
>  		} else {
>  			out_be32(&lbc->fbcr, 0);
> @@ -458,21 +456,21 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
>  		/* Read back the page in order to fill in the ECC for the
>  		 * caller.  Is this really needed?
>  		 */
> -		if (full_page && ctrl->oob_poi) {
> +		if (full_page && elbc_fcm_ctrl->oob_poi) {
>  			out_be32(&lbc->fbcr, 3);
>  			set_addr(mtd, 6, page_addr, 1);
>  
> -			ctrl->read_bytes = mtd->writesize + 9;
> +			elbc_fcm_ctrl->read_bytes = mtd->writesize + 9;
>  
>  			fsl_elbc_do_read(chip, 1);
>  			fsl_elbc_run_command(mtd);
>  
> -			memcpy_fromio(ctrl->oob_poi + 6,
> -			              &ctrl->addr[ctrl->index], 3);
> -			ctrl->index += 3;
> +			memcpy_fromio(elbc_fcm_ctrl->oob_poi + 6,
> +				&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], 3);
> +			elbc_fcm_ctrl->index += 3;
>  		}
>  
> -		ctrl->oob_poi = NULL;
> +		elbc_fcm_ctrl->oob_poi = NULL;
>  		return;
>  	}
>  
> @@ -485,26 +483,26 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
>  		out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT);
>  		out_be32(&lbc->fbcr, 1);
>  		set_addr(mtd, 0, 0, 0);
> -		ctrl->read_bytes = 1;
> +		elbc_fcm_ctrl->read_bytes = 1;
>  
>  		fsl_elbc_run_command(mtd);
>  
>  		/* The chip always seems to report that it is
>  		 * write-protected, even when it is not.
>  		 */
> -		setbits8(ctrl->addr, NAND_STATUS_WP);
> +		setbits8(elbc_fcm_ctrl->addr, NAND_STATUS_WP);
>  		return;
>  
>  	/* RESET without waiting for the ready line */
>  	case NAND_CMD_RESET:
> -		dev_dbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_RESET.\n");
> +		dev_dbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_RESET.\n");
>  		out_be32(&lbc->fir, FIR_OP_CM0 << FIR_OP0_SHIFT);
>  		out_be32(&lbc->fcr, NAND_CMD_RESET << FCR_CMD0_SHIFT);
>  		fsl_elbc_run_command(mtd);
>  		return;
>  
>  	default:
> -		dev_err(ctrl->dev,
> +		dev_err(priv->dev,
>  		        "fsl_elbc_cmdfunc: error, unsupported command 0x%x.\n",
>  		        command);
>  	}
> @@ -524,24 +522,24 @@ static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
>  {
>  	struct nand_chip *chip = mtd->priv;
>  	struct fsl_elbc_mtd *priv = chip->priv;
> -	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
> +	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
>  	unsigned int bufsize = mtd->writesize + mtd->oobsize;
>  
>  	if (len <= 0) {
> -		dev_err(ctrl->dev, "write_buf of %d bytes", len);
> -		ctrl->status = 0;
> +		dev_err(priv->dev, "write_buf of %d bytes", len);
> +		elbc_fcm_ctrl->status = 0;
>  		return;
>  	}
>  
> -	if ((unsigned int)len > bufsize - ctrl->index) {
> -		dev_err(ctrl->dev,
> +	if ((unsigned int)len > bufsize - elbc_fcm_ctrl->index) {
> +		dev_err(priv->dev,
>  		        "write_buf beyond end of buffer "
>  		        "(%d requested, %u available)\n",
> -		        len, bufsize - ctrl->index);
> -		len = bufsize - ctrl->index;
> +			len, bufsize - elbc_fcm_ctrl->index);
> +		len = bufsize - elbc_fcm_ctrl->index;
>  	}
>  
> -	memcpy_toio(&ctrl->addr[ctrl->index], buf, len);
> +	memcpy_toio(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], buf, len);
>  	/*
>  	 * This is workaround for the weird elbc hangs during nand write,
>  	 * Scott Wood says: "...perhaps difference in how long it takes a
> @@ -549,9 +547,9 @@ static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
>  	 * is causing problems, and sync isn't helping for some reason."
>  	 * Reading back the last byte helps though.
>  	 */
> -	in_8(&ctrl->addr[ctrl->index] + len - 1);
> +	in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index] + len - 1);
>  
> -	ctrl->index += len;
> +	elbc_fcm_ctrl->index += len;
>  }
>  
>  /*
> @@ -562,13 +560,13 @@ static u8 fsl_elbc_read_byte(struct mtd_info *mtd)
>  {
>  	struct nand_chip *chip = mtd->priv;
>  	struct fsl_elbc_mtd *priv = chip->priv;
> -	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
> +	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
>  
>  	/* If there are still bytes in the FCM, then use the next byte. */
> -	if (ctrl->index < ctrl->read_bytes)
> -		return in_8(&ctrl->addr[ctrl->index++]);
> +	if (elbc_fcm_ctrl->index < elbc_fcm_ctrl->read_bytes)
> +		return in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index++]);
>  
> -	dev_err(ctrl->dev, "read_byte beyond end of buffer\n");
> +	dev_err(priv->dev, "read_byte beyond end of buffer\n");
>  	return ERR_BYTE;
>  }
>  
> @@ -579,18 +577,19 @@ static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
>  {
>  	struct nand_chip *chip = mtd->priv;
>  	struct fsl_elbc_mtd *priv = chip->priv;
> -	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
> +	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
>  	int avail;
>  
>  	if (len < 0)
>  		return;
>  
> -	avail = min((unsigned int)len, ctrl->read_bytes - ctrl->index);
> -	memcpy_fromio(buf, &ctrl->addr[ctrl->index], avail);
> -	ctrl->index += avail;
> +	avail = min((unsigned int)len,
> +			elbc_fcm_ctrl->read_bytes - elbc_fcm_ctrl->index);
> +	memcpy_fromio(buf, &elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], avail);
> +	elbc_fcm_ctrl->index += avail;
>  
>  	if (len > avail)
> -		dev_err(ctrl->dev,
> +		dev_err(priv->dev,
>  		        "read_buf beyond end of buffer "
>  		        "(%d requested, %d available)\n",
>  		        len, avail);
> @@ -603,30 +602,32 @@ static int fsl_elbc_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
>  {
>  	struct nand_chip *chip = mtd->priv;
>  	struct fsl_elbc_mtd *priv = chip->priv;
> -	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
> +	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
>  	int i;
>  
>  	if (len < 0) {
> -		dev_err(ctrl->dev, "write_buf of %d bytes", len);
> +		dev_err(priv->dev, "write_buf of %d bytes", len);
>  		return -EINVAL;
>  	}
>  
> -	if ((unsigned int)len > ctrl->read_bytes - ctrl->index) {
> -		dev_err(ctrl->dev,
> -		        "verify_buf beyond end of buffer "
> -		        "(%d requested, %u available)\n",
> -		        len, ctrl->read_bytes - ctrl->index);
> +	if ((unsigned int)len >
> +			elbc_fcm_ctrl->read_bytes - elbc_fcm_ctrl->index) {
> +		dev_err(priv->dev,
> +			"verify_buf beyond end of buffer "
> +			"(%d requested, %u available)\n",
> +			len, elbc_fcm_ctrl->read_bytes - elbc_fcm_ctrl->index);
>  
> -		ctrl->index = ctrl->read_bytes;
> +		elbc_fcm_ctrl->index = elbc_fcm_ctrl->read_bytes;
>  		return -EINVAL;
>  	}
>  
>  	for (i = 0; i < len; i++)
> -		if (in_8(&ctrl->addr[ctrl->index + i]) != buf[i])
> +		if (in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index + i])
> +				!= buf[i])
>  			break;
>  
> -	ctrl->index += len;
> -	return i == len && ctrl->status == LTESR_CC ? 0 : -EIO;
> +	elbc_fcm_ctrl->index += len;
> +	return i == len && elbc_fcm_ctrl->status == LTESR_CC ? 0 : -EIO;
>  }
>  
>  /* This function is called after Program and Erase Operations to
> @@ -635,22 +636,22 @@ static int fsl_elbc_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
>  static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip)
>  {
>  	struct fsl_elbc_mtd *priv = chip->priv;
> -	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
> +	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
>  
> -	if (ctrl->status != LTESR_CC)
> +	if (elbc_fcm_ctrl->status != LTESR_CC)
>  		return NAND_STATUS_FAIL;
>  
>  	/* The chip always seems to report that it is
>  	 * write-protected, even when it is not.
>  	 */
> -	return (ctrl->mdr & 0xff) | NAND_STATUS_WP;
> +	return (elbc_fcm_ctrl->mdr & 0xff) | NAND_STATUS_WP;
>  }
>  
>  static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
>  {
>  	struct nand_chip *chip = mtd->priv;
>  	struct fsl_elbc_mtd *priv = chip->priv;
> -	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
> +	struct fsl_lbc_ctrl *ctrl = priv->ctrl;
>  	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
>  	unsigned int al;
>  
> @@ -665,41 +666,41 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
>  	priv->fmr |= (12 << FMR_CWTO_SHIFT) |  /* Timeout > 12 ms */
>  	             (al << FMR_AL_SHIFT);
>  
> -	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->numchips = %d\n",
> +	dev_dbg(priv->dev, "fsl_elbc_init: nand->numchips = %d\n",
>  	        chip->numchips);
> -	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->chipsize = %lld\n",
> +	dev_dbg(priv->dev, "fsl_elbc_init: nand->chipsize = %lld\n",
>  	        chip->chipsize);
> -	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->pagemask = %8x\n",
> +	dev_dbg(priv->dev, "fsl_elbc_init: nand->pagemask = %8x\n",
>  	        chip->pagemask);
> -	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->chip_delay = %d\n",
> +	dev_dbg(priv->dev, "fsl_elbc_init: nand->chip_delay = %d\n",
>  	        chip->chip_delay);
> -	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->badblockpos = %d\n",
> +	dev_dbg(priv->dev, "fsl_elbc_init: nand->badblockpos = %d\n",
>  	        chip->badblockpos);
> -	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->chip_shift = %d\n",
> +	dev_dbg(priv->dev, "fsl_elbc_init: nand->chip_shift = %d\n",
>  	        chip->chip_shift);
> -	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->page_shift = %d\n",
> +	dev_dbg(priv->dev, "fsl_elbc_init: nand->page_shift = %d\n",
>  	        chip->page_shift);
> -	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->phys_erase_shift = %d\n",
> +	dev_dbg(priv->dev, "fsl_elbc_init: nand->phys_erase_shift = %d\n",
>  	        chip->phys_erase_shift);
> -	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecclayout = %p\n",
> +	dev_dbg(priv->dev, "fsl_elbc_init: nand->ecclayout = %p\n",
>  	        chip->ecclayout);
> -	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.mode = %d\n",
> +	dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.mode = %d\n",
>  	        chip->ecc.mode);
> -	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.steps = %d\n",
> +	dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.steps = %d\n",
>  	        chip->ecc.steps);
> -	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.bytes = %d\n",
> +	dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.bytes = %d\n",
>  	        chip->ecc.bytes);
> -	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.total = %d\n",
> +	dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.total = %d\n",
>  	        chip->ecc.total);
> -	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.layout = %p\n",
> +	dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.layout = %p\n",
>  	        chip->ecc.layout);
> -	dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->flags = %08x\n", mtd->flags);
> -	dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->size = %lld\n", mtd->size);
> -	dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->erasesize = %d\n",
> +	dev_dbg(priv->dev, "fsl_elbc_init: mtd->flags = %08x\n", mtd->flags);
> +	dev_dbg(priv->dev, "fsl_elbc_init: mtd->size = %lld\n", mtd->size);
> +	dev_dbg(priv->dev, "fsl_elbc_init: mtd->erasesize = %d\n",
>  	        mtd->erasesize);
> -	dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->writesize = %d\n",
> +	dev_dbg(priv->dev, "fsl_elbc_init: mtd->writesize = %d\n",
>  	        mtd->writesize);
> -	dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->oobsize = %d\n",
> +	dev_dbg(priv->dev, "fsl_elbc_init: mtd->oobsize = %d\n",
>  	        mtd->oobsize);
>  
>  	/* adjust Option Register and ECC to match Flash page size */
> @@ -719,7 +720,7 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
>  			chip->badblock_pattern = &largepage_memorybased;
>  		}
>  	} else {
> -		dev_err(ctrl->dev,
> +		dev_err(priv->dev,
>  		        "fsl_elbc_init: page size %d is not supported\n",
>  		        mtd->writesize);
>  		return -1;
> @@ -750,18 +751,19 @@ static void fsl_elbc_write_page(struct mtd_info *mtd,
>                                  const uint8_t *buf)
>  {
>  	struct fsl_elbc_mtd *priv = chip->priv;
> -	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
> +	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
>  
>  	fsl_elbc_write_buf(mtd, buf, mtd->writesize);
>  	fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
>  
> -	ctrl->oob_poi = chip->oob_poi;
> +	elbc_fcm_ctrl->oob_poi = chip->oob_poi;
>  }
>  
>  static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
>  {
> -	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
> +	struct fsl_lbc_ctrl *ctrl = priv->ctrl;
>  	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
> +	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
>  	struct nand_chip *chip = &priv->chip;
>  
>  	dev_dbg(priv->dev, "eLBC Set Information for bank %d\n", priv->bank);
> @@ -790,7 +792,7 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
>  	chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR |
>  			NAND_USE_FLASH_BBT;
>  
> -	chip->controller = &ctrl->controller;
> +	chip->controller = &elbc_fcm_ctrl->controller;
>  	chip->priv = priv;
>  
>  	chip->ecc.read_page = fsl_elbc_read_page;
> @@ -815,8 +817,7 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
>  
>  static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv)
>  {
> -	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
> -
> +	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
>  	nand_release(&priv->mtd);
>  
>  	kfree(priv->mtd.name);
> @@ -824,18 +825,21 @@ static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv)
>  	if (priv->vbase)
>  		iounmap(priv->vbase);
>  
> -	ctrl->chips[priv->bank] = NULL;
> +	elbc_fcm_ctrl->chips[priv->bank] = NULL;
>  	kfree(priv);
> -
> +	kfree(elbc_fcm_ctrl);
>  	return 0;
>  }
>  
> -static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl,
> -					 struct device_node *node)
> +static DEFINE_MUTEX(fsl_elbc_nand_mutex);
> +
> +static int __devinit fsl_elbc_nand_probe(struct platform_device *pdev)
>  {
> -	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
> +	struct fsl_lbc_regs __iomem *lbc;
>  	struct fsl_elbc_mtd *priv;
>  	struct resource res;
> +	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl;
> +
>  #ifdef CONFIG_MTD_PARTITIONS
>  	static const char *part_probe_types[]
>  		= { "cmdlinepart", "RedBoot", NULL };
> @@ -843,11 +847,18 @@ static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl,
>  #endif
>  	int ret;
>  	int bank;
> +	struct device *dev;
> +	struct device_node *node = pdev->dev.of_node;
> +
> +	if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
> +		return -ENODEV;
> +	lbc = fsl_lbc_ctrl_dev->regs;
> +	dev = fsl_lbc_ctrl_dev->dev;
>  
>  	/* get, allocate and map the memory resource */
>  	ret = of_address_to_resource(node, 0, &res);
>  	if (ret) {
> -		dev_err(ctrl->dev, "failed to get resource\n");
> +		dev_err(dev, "failed to get resource\n");
>  		return ret;
>  	}
>  
> @@ -861,7 +872,7 @@ static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl,
>  			break;
>  
>  	if (bank >= MAX_BANKS) {
> -		dev_err(ctrl->dev, "address did not match any chip selects\n");
> +		dev_err(dev, "address did not match any chip selects\n");
>  		return -ENODEV;
>  	}
>  
> @@ -869,14 +880,33 @@ static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl,
>  	if (!priv)
>  		return -ENOMEM;
>  
> -	ctrl->chips[bank] = priv;
> +	mutex_lock(&fsl_elbc_nand_mutex);
> +	if (!fsl_lbc_ctrl_dev->nand) {
> +		elbc_fcm_ctrl = kzalloc(sizeof(*elbc_fcm_ctrl), GFP_KERNEL);
> +		if (!elbc_fcm_ctrl) {
> +			dev_err(dev, "failed to allocate memory\n");
> +			mutex_unlock(&fsl_elbc_nand_mutex);
> +			ret = -ENOMEM;
> +			goto err;
> +		}
> +		elbc_fcm_ctrl->counter++;
> +
> +		spin_lock_init(&elbc_fcm_ctrl->controller.lock);
> +		init_waitqueue_head(&elbc_fcm_ctrl->controller.wq);
> +		fsl_lbc_ctrl_dev->nand = elbc_fcm_ctrl;
> +	} else {
> +		elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand;
> +	}
> +	mutex_unlock(&fsl_elbc_nand_mutex);
> +
> +	elbc_fcm_ctrl->chips[bank] = priv;
>  	priv->bank = bank;
> -	priv->ctrl = ctrl;
> -	priv->dev = ctrl->dev;
> +	priv->ctrl = fsl_lbc_ctrl_dev;
> +	priv->dev = dev;
>  
>  	priv->vbase = ioremap(res.start, resource_size(&res));
>  	if (!priv->vbase) {
> -		dev_err(ctrl->dev, "failed to map chip region\n");
> +		dev_err(dev, "failed to map chip region\n");
>  		ret = -ENOMEM;
>  		goto err;
>  	}
> @@ -933,171 +963,53 @@ err:
>  	return ret;
>  }
>  
> -static int __devinit fsl_elbc_ctrl_init(struct fsl_elbc_ctrl *ctrl)
> +static int fsl_elbc_nand_remove(struct platform_device *pdev)
>  {
> -	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
> -
> -	/*
> -	 * NAND transactions can tie up the bus for a long time, so set the
> -	 * bus timeout to max by clearing LBCR[BMT] (highest base counter
> -	 * value) and setting LBCR[BMTPS] to the highest prescaler value.
> -	 */
> -	clrsetbits_be32(&lbc->lbcr, LBCR_BMT, 15);
> -
> -	/* clear event registers */
> -	setbits32(&lbc->ltesr, LTESR_NAND_MASK);
> -	out_be32(&lbc->lteatr, 0);
> -
> -	/* Enable interrupts for any detected events */
> -	out_be32(&lbc->lteir, LTESR_NAND_MASK);
> -
> -	ctrl->read_bytes = 0;
> -	ctrl->index = 0;
> -	ctrl->addr = NULL;
> -
> -	return 0;
> -}
> -
> -static int fsl_elbc_ctrl_remove(struct platform_device *ofdev)
> -{
> -	struct fsl_elbc_ctrl *ctrl = dev_get_drvdata(&ofdev->dev);
>  	int i;
> -
> +	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand;
>  	for (i = 0; i < MAX_BANKS; i++)
> -		if (ctrl->chips[i])
> -			fsl_elbc_chip_remove(ctrl->chips[i]);
> -
> -	if (ctrl->irq)
> -		free_irq(ctrl->irq, ctrl);
> -
> -	if (ctrl->regs)
> -		iounmap(ctrl->regs);
> -
> -	dev_set_drvdata(&ofdev->dev, NULL);
> -	kfree(ctrl);
> -	return 0;
> -}
> -
> -/* NOTE: This interrupt is also used to report other localbus events,
> - * such as transaction errors on other chipselects.  If we want to
> - * capture those, we'll need to move the IRQ code into a shared
> - * LBC driver.
> - */
> -
> -static irqreturn_t fsl_elbc_ctrl_irq(int irqno, void *data)
> -{
> -	struct fsl_elbc_ctrl *ctrl = data;
> -	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
> -	__be32 status = in_be32(&lbc->ltesr) & LTESR_NAND_MASK;
> -
> -	if (status) {
> -		out_be32(&lbc->ltesr, status);
> -		out_be32(&lbc->lteatr, 0);
> -
> -		ctrl->irq_status = status;
> -		smp_wmb();
> -		wake_up(&ctrl->irq_wait);
> -
> -		return IRQ_HANDLED;
> +		if (elbc_fcm_ctrl->chips[i])
> +			fsl_elbc_chip_remove(elbc_fcm_ctrl->chips[i]);
> +
> +	mutex_lock(&fsl_elbc_nand_mutex);
> +	elbc_fcm_ctrl->counter--;
> +	if (!elbc_fcm_ctrl->counter) {
> +		fsl_lbc_ctrl_dev->nand = NULL;
> +		kfree(elbc_fcm_ctrl);
>  	}
> -
> -	return IRQ_NONE;
> -}
> -
> -/* fsl_elbc_ctrl_probe
> - *
> - * called by device layer when it finds a device matching
> - * one our driver can handled. This code allocates all of
> - * the resources needed for the controller only.  The
> - * resources for the NAND banks themselves are allocated
> - * in the chip probe function.
> -*/
> -
> -static int __devinit fsl_elbc_ctrl_probe(struct platform_device *ofdev,
> -                                         const struct of_device_id *match)
> -{
> -	struct device_node *child;
> -	struct fsl_elbc_ctrl *ctrl;
> -	int ret;
> -
> -	ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
> -	if (!ctrl)
> -		return -ENOMEM;
> -
> -	dev_set_drvdata(&ofdev->dev, ctrl);
> -
> -	spin_lock_init(&ctrl->controller.lock);
> -	init_waitqueue_head(&ctrl->controller.wq);
> -	init_waitqueue_head(&ctrl->irq_wait);
> -
> -	ctrl->regs = of_iomap(ofdev->dev.of_node, 0);
> -	if (!ctrl->regs) {
> -		dev_err(&ofdev->dev, "failed to get memory region\n");
> -		ret = -ENODEV;
> -		goto err;
> -	}
> -
> -	ctrl->irq = of_irq_to_resource(ofdev->dev.of_node, 0, NULL);
> -	if (ctrl->irq == NO_IRQ) {
> -		dev_err(&ofdev->dev, "failed to get irq resource\n");
> -		ret = -ENODEV;
> -		goto err;
> -	}
> -
> -	ctrl->dev = &ofdev->dev;
> -
> -	ret = fsl_elbc_ctrl_init(ctrl);
> -	if (ret < 0)
> -		goto err;
> -
> -	ret = request_irq(ctrl->irq, fsl_elbc_ctrl_irq, 0, "fsl-elbc", ctrl);
> -	if (ret != 0) {
> -		dev_err(&ofdev->dev, "failed to install irq (%d)\n",
> -		        ctrl->irq);
> -		ret = ctrl->irq;
> -		goto err;
> -	}
> -
> -	for_each_child_of_node(ofdev->dev.of_node, child)
> -		if (of_device_is_compatible(child, "fsl,elbc-fcm-nand"))
> -			fsl_elbc_chip_probe(ctrl, child);
> +	mutex_unlock(&fsl_elbc_nand_mutex);
>  
>  	return 0;
>  
> -err:
> -	fsl_elbc_ctrl_remove(ofdev);
> -	return ret;
>  }
>  
> -static const struct of_device_id fsl_elbc_match[] = {
> -	{
> -		.compatible = "fsl,elbc",
> -	},
> +static const struct of_device_id fsl_elbc_nand_match[] = {
> +	{ .compatible = "fsl,elbc-fcm-nand", },
>  	{}
>  };
>  
> -static struct of_platform_driver fsl_elbc_ctrl_driver = {
> +static struct platform_driver fsl_elbc_nand_driver = {
>  	.driver = {
> -		.name = "fsl-elbc",
> +		.name = "fsl,elbc-fcm-nand",
>  		.owner = THIS_MODULE,
> -		.of_match_table = fsl_elbc_match,
> +		.of_match_table = fsl_elbc_nand_match,
>  	},
> -	.probe = fsl_elbc_ctrl_probe,
> -	.remove = fsl_elbc_ctrl_remove,
> +	.probe = fsl_elbc_nand_probe,
> +	.remove = fsl_elbc_nand_remove,
>  };
>  
> -static int __init fsl_elbc_init(void)
> +static int __init fsl_elbc_nand_init(void)
>  {
> -	return of_register_platform_driver(&fsl_elbc_ctrl_driver);
> +	return platform_driver_register(&fsl_elbc_nand_driver);
>  }
>  
> -static void __exit fsl_elbc_exit(void)
> +static void __exit fsl_elbc_nand_exit(void)
>  {
> -	of_unregister_platform_driver(&fsl_elbc_ctrl_driver);
> +	platform_driver_unregister(&fsl_elbc_nand_driver);
>  }
>  
> -module_init(fsl_elbc_init);
> -module_exit(fsl_elbc_exit);
> +module_init(fsl_elbc_nand_init);
> +module_exit(fsl_elbc_nand_exit);
>  
>  MODULE_LICENSE("GPL");
>  MODULE_AUTHOR("Freescale");
Zang Roy-R61911 Oct. 18, 2010, 9:30 a.m. UTC | #2
> -----Original Message-----
> From: tiejun.chen [mailto:tiejun.chen@windriver.com]
> Sent: Monday, October 18, 2010 16:56 PM
> To: Zang Roy-R61911
> Cc: linux-mtd@lists.infradead.org; Wood Scott-B07421; dedekind1@gmail.com; Lan
> Chunhe-B25806; linuxppc-dev@ozlabs.org; akpm@linux-foundation.org;
> dwmw2@infradead.org; Gala Kumar-B11780
> Subject: Re: [PATCH 1/2] P4080/eLBC: Make Freescale elbc interrupt common to
> elbc devices
> 
> Roy Zang wrote:
> > Move Freescale elbc interrupt from nand dirver to elbc driver.
> > Then all elbc devices can use the interrupt instead of ONLY nand.
> >
> > For former nand driver, it had the two functions:
> >
> > 1. detecting nand flash partitions;
> > 2. registering elbc interrupt.
> >
> > Now, second function is removed to fsl_lbc.c.
> >
> > Signed-off-by: Lan Chunhe-B25806 <b25806@freescale.com>
> > Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
> > Reviewed-by: Anton Vorontsov <cbouatmailru@gmail.com>
> > Cc: Wood Scott-B07421 <B07421@freescale.com>
> > ---
> >
> > These two patches are based on the following commits:
> > 1.	http://lists.infradead.org/pipermail/linux-mtd/2010-
> September/032112.html
> > 2.	http://lists.infradead.org/pipermail/linux-mtd/2010-
> September/032110.html
> > 3.	http://lists.infradead.org/pipermail/linux-mtd/2010-
> September/032111.html
> > According to Anton's comment, I merge 1 & 2 together and start a new thread.
> > Comparing the provided link:
> > 1.	Merge 1 & 2 together.
> > 2.	Some code style updates
> > 3.	Add counter protect for elbc driver remove
> > 4.	Rebase to 2.6.36-rc7
> >
> > Other histories from the links:
> > V2: Comparing with v1, according to the feedback, add some decorations.
> >
> > V3: Comparing with v2:
> > 1.	according to the feedback, add some decorations.
> > 2.	change of_platform_driver to platform_driver
> > 3.	rebase to 2.6.36-rc4
> >
> > V4: Comparing with v3
> > 1.	minor fix from type unsigned int to u32
> > 2.	fix platform_driver issue.
> > 3.	add mutex for nand probe
> >
> >  arch/powerpc/Kconfig               |    7 +-
> >  arch/powerpc/include/asm/fsl_lbc.h |   33 +++-
> >  arch/powerpc/sysdev/fsl_lbc.c      |  229 ++++++++++++++---
> >  drivers/mtd/nand/Kconfig           |    1 +
> >  drivers/mtd/nand/fsl_elbc_nand.c   |  482 +++++++++++++++------------------
> ---
> >  5 files changed, 425 insertions(+), 327 deletions(-)
> >
> > diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
> > index 631e5a0..44df1ba 100644
> > --- a/arch/powerpc/Kconfig
> > +++ b/arch/powerpc/Kconfig
> > @@ -687,9 +687,12 @@ config 4xx_SOC
> >  	bool
> >
> >  config FSL_LBC
> > -	bool
> > +	bool "Freescale Local Bus support"
> > +	depends on FSL_SOC
> >  	help
> > -	  Freescale Localbus support
> > +	  Enables reporting of errors from the Freescale local bus
> > +	  controller.  Also contains some common code used by
> > +	  drivers for specific local bus peripherals.
> >
> >  config FSL_GTM
> >  	bool
> > diff --git a/arch/powerpc/include/asm/fsl_lbc.h
> b/arch/powerpc/include/asm/fsl_lbc.h
> > index 1b5a210..0c40c05 100644
> > --- a/arch/powerpc/include/asm/fsl_lbc.h
> > +++ b/arch/powerpc/include/asm/fsl_lbc.h
> > @@ -1,9 +1,10 @@
> >  /* Freescale Local Bus Controller
> >   *
> > - * Copyright (c) 2006-2007 Freescale Semiconductor
> > + * Copyright (c) 2006-2007, 2010 Freescale Semiconductor
> >   *
> >   * Authors: Nick Spence <nick.spence@freescale.com>,
> >   *          Scott Wood <scottwood@freescale.com>
> > + *          Jack Lan <jack.lan@freescale.com>
> >   *
> >   * This program is free software; you can redistribute it and/or modify
> >   * it under the terms of the GNU General Public License as published by
> > @@ -26,6 +27,8 @@
> >  #include <linux/compiler.h>
> >  #include <linux/types.h>
> >  #include <linux/io.h>
> > +#include <linux/device.h>
> > +#include <linux/spinlock.h>
> >
> >  struct fsl_lbc_bank {
> >  	__be32 br;             /**< Base Register  */
> > @@ -125,13 +128,23 @@ struct fsl_lbc_regs {
> >  #define LTESR_ATMW 0x00800000
> >  #define LTESR_ATMR 0x00400000
> >  #define LTESR_CS   0x00080000
> > +#define LTESR_UPM  0x00000002
> >  #define LTESR_CC   0x00000001
> >  #define LTESR_NAND_MASK (LTESR_FCT | LTESR_PAR | LTESR_CC)
> > +#define LTESR_MASK      (LTESR_BM | LTESR_FCT | LTESR_PAR | LTESR_WP \
> > +			 | LTESR_ATMW | LTESR_ATMR | LTESR_CS | LTESR_UPM \
> > +			 | LTESR_CC)
> > +#define LTESR_CLEAR	0xFFFFFFFF
> > +#define LTECCR_CLEAR	0xFFFFFFFF
> > +#define LTESR_STATUS	LTESR_MASK
> > +#define LTEIR_ENABLE	LTESR_MASK
> > +#define LTEDR_ENABLE	0x00000000
> >  	__be32 ltedr;           /**< Transfer Error Disable Register */
> >  	__be32 lteir;           /**< Transfer Error Interrupt Register */
> >  	__be32 lteatr;          /**< Transfer Error Attributes Register */
> >  	__be32 ltear;           /**< Transfer Error Address Register */
> > -	u8 res6[0xC];
> > +	__be32 lteccr;          /**< Transfer Error ECC Register */
> > +	u8 res6[0x8];
> >  	__be32 lbcr;            /**< Configuration Register */
> >  #define LBCR_LDIS  0x80000000
> >  #define LBCR_LDIS_SHIFT    31
> > @@ -265,7 +278,23 @@ static inline void fsl_upm_end_pattern(struct fsl_upm
> *upm)
> >  		cpu_relax();
> >  }
> >
> > +/* overview of the fsl lbc controller */
> > +
> > +struct fsl_lbc_ctrl {
> > +	/* device info */
> > +	struct device			*dev;
> > +	struct fsl_lbc_regs __iomem	*regs;
> > +	int				irq;
> > +	wait_queue_head_t		irq_wait;
> > +	spinlock_t			lock;
> > +	void				*nand;
> > +
> > +	/* status read from LTESR by irq handler */
> > +	unsigned int			irq_status;
> > +};
> > +
> >  extern int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base,
> >  			       u32 mar);
> > +extern struct fsl_lbc_ctrl *fsl_lbc_ctrl_dev;
> >
> >  #endif /* __ASM_FSL_LBC_H */
> > diff --git a/arch/powerpc/sysdev/fsl_lbc.c b/arch/powerpc/sysdev/fsl_lbc.c
> > index dceb8d1..4bb0336 100644
> > --- a/arch/powerpc/sysdev/fsl_lbc.c
> > +++ b/arch/powerpc/sysdev/fsl_lbc.c
> > @@ -2,8 +2,11 @@
> >   * Freescale LBC and UPM routines.
> >   *
> >   * Copyright (c) 2007-2008  MontaVista Software, Inc.
> > + * Copyright (c) 2010 Freescale Semiconductor
> >   *
> >   * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
> > + * Author: Jack Lan <Jack.Lan@freescale.com>
> > + * Author: Roy Zang <tie-fei.zang@freescale.com>
> >   *
> >   * This program is free software; you can redistribute it and/or modify
> >   * it under the terms of the GNU General Public License as published by
> > @@ -19,39 +22,16 @@
> >  #include <linux/types.h>
> >  #include <linux/io.h>
> >  #include <linux/of.h>
> > +#include <linux/slab.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/mod_devicetable.h>
> >  #include <asm/prom.h>
> >  #include <asm/fsl_lbc.h>
> >
> >  static spinlock_t fsl_lbc_lock = __SPIN_LOCK_UNLOCKED(fsl_lbc_lock);
> > -static struct fsl_lbc_regs __iomem *fsl_lbc_regs;
> > -
> > -static char __initdata *compat_lbc[] = {
> > -	"fsl,pq2-localbus",
> > -	"fsl,pq2pro-localbus",
> > -	"fsl,pq3-localbus",
> > -	"fsl,elbc",
> > -};
> > -
> > -static int __init fsl_lbc_init(void)
> > -{
> > -	struct device_node *lbus;
> > -	int i;
> > -
> > -	for (i = 0; i < ARRAY_SIZE(compat_lbc); i++) {
> > -		lbus = of_find_compatible_node(NULL, NULL, compat_lbc[i]);
> > -		if (lbus)
> > -			goto found;
> > -	}
> > -	return -ENODEV;
> > -
> > -found:
> > -	fsl_lbc_regs = of_iomap(lbus, 0);
> > -	of_node_put(lbus);
> > -	if (!fsl_lbc_regs)
> > -		return -ENOMEM;
> > -	return 0;
> > -}
> > -arch_initcall(fsl_lbc_init);
> > +struct fsl_lbc_ctrl *fsl_lbc_ctrl_dev;
> > +EXPORT_SYMBOL(fsl_lbc_ctrl_dev);
> >
> >  /**
> >   * fsl_lbc_find - find Localbus bank
> > @@ -65,13 +45,15 @@ arch_initcall(fsl_lbc_init);
> >  int fsl_lbc_find(phys_addr_t addr_base)
> >  {
> >  	int i;
> > +	struct fsl_lbc_regs __iomem *lbc;
> >
> > -	if (!fsl_lbc_regs)
> > +	if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
> >  		return -ENODEV;
> >
> > -	for (i = 0; i < ARRAY_SIZE(fsl_lbc_regs->bank); i++) {
> > -		__be32 br = in_be32(&fsl_lbc_regs->bank[i].br);
> > -		__be32 or = in_be32(&fsl_lbc_regs->bank[i].or);
> > +	lbc = fsl_lbc_ctrl_dev->regs;
> > +	for (i = 0; i < ARRAY_SIZE(lbc->bank); i++) {
> > +		__be32 br = in_be32(&lbc->bank[i].br);
> > +		__be32 or = in_be32(&lbc->bank[i].or);
> >
> >  		if (br & BR_V && (br & or & BR_BA) == addr_base)
> >  			return i;
> > @@ -94,22 +76,27 @@ int fsl_upm_find(phys_addr_t addr_base, struct fsl_upm
> *upm)
> >  {
> >  	int bank;
> >  	__be32 br;
> > +	struct fsl_lbc_regs __iomem *lbc;
> >
> >  	bank = fsl_lbc_find(addr_base);
> >  	if (bank < 0)
> >  		return bank;
> >
> > -	br = in_be32(&fsl_lbc_regs->bank[bank].br);
> > +	if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
> > +		return -ENODEV;
> > +
> > +	lbc = fsl_lbc_ctrl_dev->regs;
> > +	br = in_be32(&lbc->bank[bank].br);
> >
> >  	switch (br & BR_MSEL) {
> >  	case BR_MS_UPMA:
> > -		upm->mxmr = &fsl_lbc_regs->mamr;
> > +		upm->mxmr = &lbc->mamr;
> >  		break;
> >  	case BR_MS_UPMB:
> > -		upm->mxmr = &fsl_lbc_regs->mbmr;
> > +		upm->mxmr = &lbc->mbmr;
> >  		break;
> >  	case BR_MS_UPMC:
> > -		upm->mxmr = &fsl_lbc_regs->mcmr;
> > +		upm->mxmr = &lbc->mcmr;
> >  		break;
> >  	default:
> >  		return -EINVAL;
> > @@ -148,9 +135,12 @@ int fsl_upm_run_pattern(struct fsl_upm *upm, void
> __iomem *io_base, u32 mar)
> >  	int ret = 0;
> >  	unsigned long flags;
> >
> > +	if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
> > +		return -ENODEV;
> > +
> >  	spin_lock_irqsave(&fsl_lbc_lock, flags);
> >
> > -	out_be32(&fsl_lbc_regs->mar, mar);
> > +	out_be32(&fsl_lbc_ctrl_dev->regs->mar, mar);
> >
> >  	switch (upm->width) {
> >  	case 8:
> > @@ -172,3 +162,166 @@ int fsl_upm_run_pattern(struct fsl_upm *upm, void
> __iomem *io_base, u32 mar)
> >  	return ret;
> >  }
> >  EXPORT_SYMBOL(fsl_upm_run_pattern);
> > +
> > +static int __devinit fsl_lbc_ctrl_init(struct fsl_lbc_ctrl *ctrl)
> > +{
> > +	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
> > +
> > +	/* clear event registers */
> > +	setbits32(&lbc->ltesr, LTESR_CLEAR);
> > +	out_be32(&lbc->lteatr, 0);
> > +	out_be32(&lbc->ltear, 0);
> > +	out_be32(&lbc->lteccr, LTECCR_CLEAR);
> > +	out_be32(&lbc->ltedr, LTEDR_ENABLE);
> > +
> > +	/* Enable interrupts for any detected events */
> > +	out_be32(&lbc->lteir, LTEIR_ENABLE);
> > +
> > +	return 0;
> > +}
> > +
> > +/*
> > + * NOTE: This interrupt is used to report localbus events of various kinds,
> > + * such as transaction errors on the chipselects.
> > + */
> > +
> > +static irqreturn_t fsl_lbc_ctrl_irq(int irqno, void *data)
> > +{
> > +	struct fsl_lbc_ctrl *ctrl = data;
> > +	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
> > +	u32 status;
> > +
> > +	status = in_be32(&lbc->ltesr);
> > +	if (!status)
> > +		return IRQ_NONE;
> > +
> > +	out_be32(&lbc->ltesr, LTESR_CLEAR);
> > +	out_be32(&lbc->lteatr, 0);
> > +	out_be32(&lbc->ltear, 0);
> > +	ctrl->irq_status = status;
> > +
> > +	if (status & LTESR_BM)
> > +		dev_err(ctrl->dev, "Local bus monitor time-out: "
> > +			"LTESR 0x%08X\n", status);
> > +	if (status & LTESR_WP)
> > +		dev_err(ctrl->dev, "Write protect error: "
> > +			"LTESR 0x%08X\n", status);
> > +	if (status & LTESR_ATMW)
> > +		dev_err(ctrl->dev, "Atomic write error: "
> > +			"LTESR 0x%08X\n", status);
> > +	if (status & LTESR_ATMR)
> > +		dev_err(ctrl->dev, "Atomic read error: "
> > +			"LTESR 0x%08X\n", status);
> > +	if (status & LTESR_CS)
> > +		dev_err(ctrl->dev, "Chip select error: "
> > +			"LTESR 0x%08X\n", status);
> > +	if (status & LTESR_UPM)
> > +		;
> > +	if (status & LTESR_FCT) {
> > +		dev_err(ctrl->dev, "FCM command time-out: "
> > +			"LTESR 0x%08X\n", status);
> > +		smp_wmb();
> > +		wake_up(&ctrl->irq_wait);
> > +	}
> > +	if (status & LTESR_PAR) {
> > +		dev_err(ctrl->dev, "Parity or Uncorrectable ECC error: "
> > +			"LTESR 0x%08X\n", status);
> > +		smp_wmb();
> > +		wake_up(&ctrl->irq_wait);
> > +	}
> > +	if (status & LTESR_CC) {
> > +		smp_wmb();
> > +		wake_up(&ctrl->irq_wait);
> > +	}
> > +	if (status & ~LTESR_MASK)
> > +		dev_err(ctrl->dev, "Unknown error: "
> > +			"LTESR 0x%08X\n", status);
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +/*
> > + * fsl_lbc_ctrl_probe
> > + *
> > + * called by device layer when it finds a device matching
> > + * one our driver can handled. This code allocates all of
> > + * the resources needed for the controller only.  The
> > + * resources for the NAND banks themselves are allocated
> > + * in the chip probe function.
> > +*/
> > +
> > +static int __devinit fsl_lbc_ctrl_probe(struct platform_device *dev)
> > +{
> > +	int ret;
> > +
> > +	if (!dev->dev.of_node) {
> > +		dev_err(&dev->dev, "Device OF-Node is NULL");
> > +		return -EFAULT;
> > +	}
> > +
> > +	fsl_lbc_ctrl_dev = kzalloc(sizeof(*fsl_lbc_ctrl_dev), GFP_KERNEL);
> > +	if (!fsl_lbc_ctrl_dev)
> > +		return -ENOMEM;
> > +
> > +	dev_set_drvdata(&dev->dev, fsl_lbc_ctrl_dev);
> > +
> > +	spin_lock_init(&fsl_lbc_ctrl_dev->lock);
> > +	init_waitqueue_head(&fsl_lbc_ctrl_dev->irq_wait);
> > +
> > +	fsl_lbc_ctrl_dev->regs = of_iomap(dev->dev.of_node, 0);
> > +	if (!fsl_lbc_ctrl_dev->regs) {
> > +		dev_err(&dev->dev, "failed to get memory region\n");
> > +		ret = -ENODEV;
> > +		goto err;
> 
> Looks you always iounmap(fsl_lbc_ctrl_dev->regs) on position 'err' but here
> of_iomap() is already failed you should skip iounmap() fsl_lbc_ctrl_dev->regs
> again. So you should improve that as the following on 'err', or layout 'err'
> in
> gain.
> ------
> 	if(fsl_lbc_ctrl_dev->regs)
> 		iounmap(fsl_lbc_ctrl_dev->regs);
> 
> Tiejun

You are right!
How about
 
        if (!fsl_lbc_ctrl_dev->regs) {
                dev_err(&dev->dev, "failed to get memory region\n");
                kfree(fsl_lbc_ctrl_dev);
                return -ENOMEM;
        }

...

Thanks.
Roy
Tiejun Chen Oct. 18, 2010, 9:44 a.m. UTC | #3
Zang Roy-R61911 wrote:
> 
>> -----Original Message-----
>> From: tiejun.chen [mailto:tiejun.chen@windriver.com]
>> Sent: Monday, October 18, 2010 16:56 PM
>> To: Zang Roy-R61911
>> Cc: linux-mtd@lists.infradead.org; Wood Scott-B07421; dedekind1@gmail.com; Lan
>> Chunhe-B25806; linuxppc-dev@ozlabs.org; akpm@linux-foundation.org;
>> dwmw2@infradead.org; Gala Kumar-B11780
>> Subject: Re: [PATCH 1/2] P4080/eLBC: Make Freescale elbc interrupt common to
>> elbc devices
>>
>> Roy Zang wrote:
>>> Move Freescale elbc interrupt from nand dirver to elbc driver.
>>> Then all elbc devices can use the interrupt instead of ONLY nand.
>>>
>>> For former nand driver, it had the two functions:
>>>
>>> 1. detecting nand flash partitions;
>>> 2. registering elbc interrupt.
>>>
>>> Now, second function is removed to fsl_lbc.c.
>>>
>>> Signed-off-by: Lan Chunhe-B25806 <b25806@freescale.com>
>>> Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
>>> Reviewed-by: Anton Vorontsov <cbouatmailru@gmail.com>
>>> Cc: Wood Scott-B07421 <B07421@freescale.com>
>>> ---
>>>
>>> These two patches are based on the following commits:
>>> 1.	http://lists.infradead.org/pipermail/linux-mtd/2010-
>> September/032112.html
>>> 2.	http://lists.infradead.org/pipermail/linux-mtd/2010-
>> September/032110.html
>>> 3.	http://lists.infradead.org/pipermail/linux-mtd/2010-
>> September/032111.html
>>> According to Anton's comment, I merge 1 & 2 together and start a new thread.
>>> Comparing the provided link:
>>> 1.	Merge 1 & 2 together.
>>> 2.	Some code style updates
>>> 3.	Add counter protect for elbc driver remove
>>> 4.	Rebase to 2.6.36-rc7
>>>
>>> Other histories from the links:
>>> V2: Comparing with v1, according to the feedback, add some decorations.
>>>
>>> V3: Comparing with v2:
>>> 1.	according to the feedback, add some decorations.
>>> 2.	change of_platform_driver to platform_driver
>>> 3.	rebase to 2.6.36-rc4
>>>
>>> V4: Comparing with v3
>>> 1.	minor fix from type unsigned int to u32
>>> 2.	fix platform_driver issue.
>>> 3.	add mutex for nand probe
>>>
>>>  arch/powerpc/Kconfig               |    7 +-
>>>  arch/powerpc/include/asm/fsl_lbc.h |   33 +++-
>>>  arch/powerpc/sysdev/fsl_lbc.c      |  229 ++++++++++++++---
>>>  drivers/mtd/nand/Kconfig           |    1 +
>>>  drivers/mtd/nand/fsl_elbc_nand.c   |  482 +++++++++++++++------------------
>> ---
>>>  5 files changed, 425 insertions(+), 327 deletions(-)
>>>
>>> diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
>>> index 631e5a0..44df1ba 100644
>>> --- a/arch/powerpc/Kconfig
>>> +++ b/arch/powerpc/Kconfig
>>> @@ -687,9 +687,12 @@ config 4xx_SOC
>>>  	bool
>>>
>>>  config FSL_LBC
>>> -	bool
>>> +	bool "Freescale Local Bus support"
>>> +	depends on FSL_SOC
>>>  	help
>>> -	  Freescale Localbus support
>>> +	  Enables reporting of errors from the Freescale local bus
>>> +	  controller.  Also contains some common code used by
>>> +	  drivers for specific local bus peripherals.
>>>
>>>  config FSL_GTM
>>>  	bool
>>> diff --git a/arch/powerpc/include/asm/fsl_lbc.h
>> b/arch/powerpc/include/asm/fsl_lbc.h
>>> index 1b5a210..0c40c05 100644
>>> --- a/arch/powerpc/include/asm/fsl_lbc.h
>>> +++ b/arch/powerpc/include/asm/fsl_lbc.h
>>> @@ -1,9 +1,10 @@
>>>  /* Freescale Local Bus Controller
>>>   *
>>> - * Copyright (c) 2006-2007 Freescale Semiconductor
>>> + * Copyright (c) 2006-2007, 2010 Freescale Semiconductor
>>>   *
>>>   * Authors: Nick Spence <nick.spence@freescale.com>,
>>>   *          Scott Wood <scottwood@freescale.com>
>>> + *          Jack Lan <jack.lan@freescale.com>
>>>   *
>>>   * This program is free software; you can redistribute it and/or modify
>>>   * it under the terms of the GNU General Public License as published by
>>> @@ -26,6 +27,8 @@
>>>  #include <linux/compiler.h>
>>>  #include <linux/types.h>
>>>  #include <linux/io.h>
>>> +#include <linux/device.h>
>>> +#include <linux/spinlock.h>
>>>
>>>  struct fsl_lbc_bank {
>>>  	__be32 br;             /**< Base Register  */
>>> @@ -125,13 +128,23 @@ struct fsl_lbc_regs {
>>>  #define LTESR_ATMW 0x00800000
>>>  #define LTESR_ATMR 0x00400000
>>>  #define LTESR_CS   0x00080000
>>> +#define LTESR_UPM  0x00000002
>>>  #define LTESR_CC   0x00000001
>>>  #define LTESR_NAND_MASK (LTESR_FCT | LTESR_PAR | LTESR_CC)
>>> +#define LTESR_MASK      (LTESR_BM | LTESR_FCT | LTESR_PAR | LTESR_WP \
>>> +			 | LTESR_ATMW | LTESR_ATMR | LTESR_CS | LTESR_UPM \
>>> +			 | LTESR_CC)
>>> +#define LTESR_CLEAR	0xFFFFFFFF
>>> +#define LTECCR_CLEAR	0xFFFFFFFF
>>> +#define LTESR_STATUS	LTESR_MASK
>>> +#define LTEIR_ENABLE	LTESR_MASK
>>> +#define LTEDR_ENABLE	0x00000000
>>>  	__be32 ltedr;           /**< Transfer Error Disable Register */
>>>  	__be32 lteir;           /**< Transfer Error Interrupt Register */
>>>  	__be32 lteatr;          /**< Transfer Error Attributes Register */
>>>  	__be32 ltear;           /**< Transfer Error Address Register */
>>> -	u8 res6[0xC];
>>> +	__be32 lteccr;          /**< Transfer Error ECC Register */
>>> +	u8 res6[0x8];
>>>  	__be32 lbcr;            /**< Configuration Register */
>>>  #define LBCR_LDIS  0x80000000
>>>  #define LBCR_LDIS_SHIFT    31
>>> @@ -265,7 +278,23 @@ static inline void fsl_upm_end_pattern(struct fsl_upm
>> *upm)
>>>  		cpu_relax();
>>>  }
>>>
>>> +/* overview of the fsl lbc controller */
>>> +
>>> +struct fsl_lbc_ctrl {
>>> +	/* device info */
>>> +	struct device			*dev;
>>> +	struct fsl_lbc_regs __iomem	*regs;
>>> +	int				irq;
>>> +	wait_queue_head_t		irq_wait;
>>> +	spinlock_t			lock;
>>> +	void				*nand;
>>> +
>>> +	/* status read from LTESR by irq handler */
>>> +	unsigned int			irq_status;
>>> +};
>>> +
>>>  extern int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base,
>>>  			       u32 mar);
>>> +extern struct fsl_lbc_ctrl *fsl_lbc_ctrl_dev;
>>>
>>>  #endif /* __ASM_FSL_LBC_H */
>>> diff --git a/arch/powerpc/sysdev/fsl_lbc.c b/arch/powerpc/sysdev/fsl_lbc.c
>>> index dceb8d1..4bb0336 100644
>>> --- a/arch/powerpc/sysdev/fsl_lbc.c
>>> +++ b/arch/powerpc/sysdev/fsl_lbc.c
>>> @@ -2,8 +2,11 @@
>>>   * Freescale LBC and UPM routines.
>>>   *
>>>   * Copyright (c) 2007-2008  MontaVista Software, Inc.
>>> + * Copyright (c) 2010 Freescale Semiconductor
>>>   *
>>>   * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
>>> + * Author: Jack Lan <Jack.Lan@freescale.com>
>>> + * Author: Roy Zang <tie-fei.zang@freescale.com>
>>>   *
>>>   * This program is free software; you can redistribute it and/or modify
>>>   * it under the terms of the GNU General Public License as published by
>>> @@ -19,39 +22,16 @@
>>>  #include <linux/types.h>
>>>  #include <linux/io.h>
>>>  #include <linux/of.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/interrupt.h>
>>> +#include <linux/mod_devicetable.h>
>>>  #include <asm/prom.h>
>>>  #include <asm/fsl_lbc.h>
>>>
>>>  static spinlock_t fsl_lbc_lock = __SPIN_LOCK_UNLOCKED(fsl_lbc_lock);
>>> -static struct fsl_lbc_regs __iomem *fsl_lbc_regs;
>>> -
>>> -static char __initdata *compat_lbc[] = {
>>> -	"fsl,pq2-localbus",
>>> -	"fsl,pq2pro-localbus",
>>> -	"fsl,pq3-localbus",
>>> -	"fsl,elbc",
>>> -};
>>> -
>>> -static int __init fsl_lbc_init(void)
>>> -{
>>> -	struct device_node *lbus;
>>> -	int i;
>>> -
>>> -	for (i = 0; i < ARRAY_SIZE(compat_lbc); i++) {
>>> -		lbus = of_find_compatible_node(NULL, NULL, compat_lbc[i]);
>>> -		if (lbus)
>>> -			goto found;
>>> -	}
>>> -	return -ENODEV;
>>> -
>>> -found:
>>> -	fsl_lbc_regs = of_iomap(lbus, 0);
>>> -	of_node_put(lbus);
>>> -	if (!fsl_lbc_regs)
>>> -		return -ENOMEM;
>>> -	return 0;
>>> -}
>>> -arch_initcall(fsl_lbc_init);
>>> +struct fsl_lbc_ctrl *fsl_lbc_ctrl_dev;
>>> +EXPORT_SYMBOL(fsl_lbc_ctrl_dev);
>>>
>>>  /**
>>>   * fsl_lbc_find - find Localbus bank
>>> @@ -65,13 +45,15 @@ arch_initcall(fsl_lbc_init);
>>>  int fsl_lbc_find(phys_addr_t addr_base)
>>>  {
>>>  	int i;
>>> +	struct fsl_lbc_regs __iomem *lbc;
>>>
>>> -	if (!fsl_lbc_regs)
>>> +	if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
>>>  		return -ENODEV;
>>>
>>> -	for (i = 0; i < ARRAY_SIZE(fsl_lbc_regs->bank); i++) {
>>> -		__be32 br = in_be32(&fsl_lbc_regs->bank[i].br);
>>> -		__be32 or = in_be32(&fsl_lbc_regs->bank[i].or);
>>> +	lbc = fsl_lbc_ctrl_dev->regs;
>>> +	for (i = 0; i < ARRAY_SIZE(lbc->bank); i++) {
>>> +		__be32 br = in_be32(&lbc->bank[i].br);
>>> +		__be32 or = in_be32(&lbc->bank[i].or);
>>>
>>>  		if (br & BR_V && (br & or & BR_BA) == addr_base)
>>>  			return i;
>>> @@ -94,22 +76,27 @@ int fsl_upm_find(phys_addr_t addr_base, struct fsl_upm
>> *upm)
>>>  {
>>>  	int bank;
>>>  	__be32 br;
>>> +	struct fsl_lbc_regs __iomem *lbc;
>>>
>>>  	bank = fsl_lbc_find(addr_base);
>>>  	if (bank < 0)
>>>  		return bank;
>>>
>>> -	br = in_be32(&fsl_lbc_regs->bank[bank].br);
>>> +	if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
>>> +		return -ENODEV;
>>> +
>>> +	lbc = fsl_lbc_ctrl_dev->regs;
>>> +	br = in_be32(&lbc->bank[bank].br);
>>>
>>>  	switch (br & BR_MSEL) {
>>>  	case BR_MS_UPMA:
>>> -		upm->mxmr = &fsl_lbc_regs->mamr;
>>> +		upm->mxmr = &lbc->mamr;
>>>  		break;
>>>  	case BR_MS_UPMB:
>>> -		upm->mxmr = &fsl_lbc_regs->mbmr;
>>> +		upm->mxmr = &lbc->mbmr;
>>>  		break;
>>>  	case BR_MS_UPMC:
>>> -		upm->mxmr = &fsl_lbc_regs->mcmr;
>>> +		upm->mxmr = &lbc->mcmr;
>>>  		break;
>>>  	default:
>>>  		return -EINVAL;
>>> @@ -148,9 +135,12 @@ int fsl_upm_run_pattern(struct fsl_upm *upm, void
>> __iomem *io_base, u32 mar)
>>>  	int ret = 0;
>>>  	unsigned long flags;
>>>
>>> +	if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
>>> +		return -ENODEV;
>>> +
>>>  	spin_lock_irqsave(&fsl_lbc_lock, flags);
>>>
>>> -	out_be32(&fsl_lbc_regs->mar, mar);
>>> +	out_be32(&fsl_lbc_ctrl_dev->regs->mar, mar);
>>>
>>>  	switch (upm->width) {
>>>  	case 8:
>>> @@ -172,3 +162,166 @@ int fsl_upm_run_pattern(struct fsl_upm *upm, void
>> __iomem *io_base, u32 mar)
>>>  	return ret;
>>>  }
>>>  EXPORT_SYMBOL(fsl_upm_run_pattern);
>>> +
>>> +static int __devinit fsl_lbc_ctrl_init(struct fsl_lbc_ctrl *ctrl)
>>> +{
>>> +	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
>>> +
>>> +	/* clear event registers */
>>> +	setbits32(&lbc->ltesr, LTESR_CLEAR);
>>> +	out_be32(&lbc->lteatr, 0);
>>> +	out_be32(&lbc->ltear, 0);
>>> +	out_be32(&lbc->lteccr, LTECCR_CLEAR);
>>> +	out_be32(&lbc->ltedr, LTEDR_ENABLE);
>>> +
>>> +	/* Enable interrupts for any detected events */
>>> +	out_be32(&lbc->lteir, LTEIR_ENABLE);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +/*
>>> + * NOTE: This interrupt is used to report localbus events of various kinds,
>>> + * such as transaction errors on the chipselects.
>>> + */
>>> +
>>> +static irqreturn_t fsl_lbc_ctrl_irq(int irqno, void *data)
>>> +{
>>> +	struct fsl_lbc_ctrl *ctrl = data;
>>> +	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
>>> +	u32 status;
>>> +
>>> +	status = in_be32(&lbc->ltesr);
>>> +	if (!status)
>>> +		return IRQ_NONE;
>>> +
>>> +	out_be32(&lbc->ltesr, LTESR_CLEAR);
>>> +	out_be32(&lbc->lteatr, 0);
>>> +	out_be32(&lbc->ltear, 0);
>>> +	ctrl->irq_status = status;
>>> +
>>> +	if (status & LTESR_BM)
>>> +		dev_err(ctrl->dev, "Local bus monitor time-out: "
>>> +			"LTESR 0x%08X\n", status);
>>> +	if (status & LTESR_WP)
>>> +		dev_err(ctrl->dev, "Write protect error: "
>>> +			"LTESR 0x%08X\n", status);
>>> +	if (status & LTESR_ATMW)
>>> +		dev_err(ctrl->dev, "Atomic write error: "
>>> +			"LTESR 0x%08X\n", status);
>>> +	if (status & LTESR_ATMR)
>>> +		dev_err(ctrl->dev, "Atomic read error: "
>>> +			"LTESR 0x%08X\n", status);
>>> +	if (status & LTESR_CS)
>>> +		dev_err(ctrl->dev, "Chip select error: "
>>> +			"LTESR 0x%08X\n", status);
>>> +	if (status & LTESR_UPM)
>>> +		;
>>> +	if (status & LTESR_FCT) {
>>> +		dev_err(ctrl->dev, "FCM command time-out: "
>>> +			"LTESR 0x%08X\n", status);
>>> +		smp_wmb();
>>> +		wake_up(&ctrl->irq_wait);
>>> +	}
>>> +	if (status & LTESR_PAR) {
>>> +		dev_err(ctrl->dev, "Parity or Uncorrectable ECC error: "
>>> +			"LTESR 0x%08X\n", status);
>>> +		smp_wmb();
>>> +		wake_up(&ctrl->irq_wait);
>>> +	}
>>> +	if (status & LTESR_CC) {
>>> +		smp_wmb();
>>> +		wake_up(&ctrl->irq_wait);
>>> +	}
>>> +	if (status & ~LTESR_MASK)
>>> +		dev_err(ctrl->dev, "Unknown error: "
>>> +			"LTESR 0x%08X\n", status);
>>> +	return IRQ_HANDLED;
>>> +}
>>> +
>>> +/*
>>> + * fsl_lbc_ctrl_probe
>>> + *
>>> + * called by device layer when it finds a device matching
>>> + * one our driver can handled. This code allocates all of
>>> + * the resources needed for the controller only.  The
>>> + * resources for the NAND banks themselves are allocated
>>> + * in the chip probe function.
>>> +*/
>>> +
>>> +static int __devinit fsl_lbc_ctrl_probe(struct platform_device *dev)
>>> +{
>>> +	int ret;
>>> +
>>> +	if (!dev->dev.of_node) {
>>> +		dev_err(&dev->dev, "Device OF-Node is NULL");
>>> +		return -EFAULT;
>>> +	}
>>> +
>>> +	fsl_lbc_ctrl_dev = kzalloc(sizeof(*fsl_lbc_ctrl_dev), GFP_KERNEL);
>>> +	if (!fsl_lbc_ctrl_dev)
>>> +		return -ENOMEM;
>>> +
>>> +	dev_set_drvdata(&dev->dev, fsl_lbc_ctrl_dev);
>>> +
>>> +	spin_lock_init(&fsl_lbc_ctrl_dev->lock);
>>> +	init_waitqueue_head(&fsl_lbc_ctrl_dev->irq_wait);
>>> +
>>> +	fsl_lbc_ctrl_dev->regs = of_iomap(dev->dev.of_node, 0);
>>> +	if (!fsl_lbc_ctrl_dev->regs) {
>>> +		dev_err(&dev->dev, "failed to get memory region\n");
>>> +		ret = -ENODEV;
>>> +		goto err;
>> Looks you always iounmap(fsl_lbc_ctrl_dev->regs) on position 'err' but here
>> of_iomap() is already failed you should skip iounmap() fsl_lbc_ctrl_dev->regs
>> again. So you should improve that as the following on 'err', or layout 'err'
>> in
>> gain.
>> ------
>> 	if(fsl_lbc_ctrl_dev->regs)
>> 		iounmap(fsl_lbc_ctrl_dev->regs);
>>
>> Tiejun
> 
> You are right!
> How about
>  
>         if (!fsl_lbc_ctrl_dev->regs) {
>                 dev_err(&dev->dev, "failed to get memory region\n");
>                 kfree(fsl_lbc_ctrl_dev);
>                 return -ENOMEM;
>         }

Although this is a big problem, I prefer to return 'ENXIO' :)

Others are fine to me.

Tiejun

> 
> ...
> 
> Thanks.
> Roy
Tiejun Chen Oct. 18, 2010, 9:46 a.m. UTC | #4
tiejun.chen wrote:
> Zang Roy-R61911 wrote:
>>> -----Original Message-----
>>> From: tiejun.chen [mailto:tiejun.chen@windriver.com]
>>> Sent: Monday, October 18, 2010 16:56 PM
>>> To: Zang Roy-R61911
>>> Cc: linux-mtd@lists.infradead.org; Wood Scott-B07421; dedekind1@gmail.com; Lan
>>> Chunhe-B25806; linuxppc-dev@ozlabs.org; akpm@linux-foundation.org;
>>> dwmw2@infradead.org; Gala Kumar-B11780
>>> Subject: Re: [PATCH 1/2] P4080/eLBC: Make Freescale elbc interrupt common to
>>> elbc devices
>>>
>>> Roy Zang wrote:
>>>> Move Freescale elbc interrupt from nand dirver to elbc driver.
>>>> Then all elbc devices can use the interrupt instead of ONLY nand.
>>>>
>>>> For former nand driver, it had the two functions:
>>>>
>>>> 1. detecting nand flash partitions;
>>>> 2. registering elbc interrupt.
>>>>
>>>> Now, second function is removed to fsl_lbc.c.
>>>>
>>>> Signed-off-by: Lan Chunhe-B25806 <b25806@freescale.com>
>>>> Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
>>>> Reviewed-by: Anton Vorontsov <cbouatmailru@gmail.com>
>>>> Cc: Wood Scott-B07421 <B07421@freescale.com>
>>>> ---
>>>>
>>>> These two patches are based on the following commits:
>>>> 1.	http://lists.infradead.org/pipermail/linux-mtd/2010-
>>> September/032112.html
>>>> 2.	http://lists.infradead.org/pipermail/linux-mtd/2010-
>>> September/032110.html
>>>> 3.	http://lists.infradead.org/pipermail/linux-mtd/2010-
>>> September/032111.html
>>>> According to Anton's comment, I merge 1 & 2 together and start a new thread.
>>>> Comparing the provided link:
>>>> 1.	Merge 1 & 2 together.
>>>> 2.	Some code style updates
>>>> 3.	Add counter protect for elbc driver remove
>>>> 4.	Rebase to 2.6.36-rc7
>>>>
>>>> Other histories from the links:
>>>> V2: Comparing with v1, according to the feedback, add some decorations.
>>>>
>>>> V3: Comparing with v2:
>>>> 1.	according to the feedback, add some decorations.
>>>> 2.	change of_platform_driver to platform_driver
>>>> 3.	rebase to 2.6.36-rc4
>>>>
>>>> V4: Comparing with v3
>>>> 1.	minor fix from type unsigned int to u32
>>>> 2.	fix platform_driver issue.
>>>> 3.	add mutex for nand probe
>>>>
>>>>  arch/powerpc/Kconfig               |    7 +-
>>>>  arch/powerpc/include/asm/fsl_lbc.h |   33 +++-
>>>>  arch/powerpc/sysdev/fsl_lbc.c      |  229 ++++++++++++++---
>>>>  drivers/mtd/nand/Kconfig           |    1 +
>>>>  drivers/mtd/nand/fsl_elbc_nand.c   |  482 +++++++++++++++------------------
>>> ---
>>>>  5 files changed, 425 insertions(+), 327 deletions(-)
>>>>
>>>> diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
>>>> index 631e5a0..44df1ba 100644
>>>> --- a/arch/powerpc/Kconfig
>>>> +++ b/arch/powerpc/Kconfig
>>>> @@ -687,9 +687,12 @@ config 4xx_SOC
>>>>  	bool
>>>>
>>>>  config FSL_LBC
>>>> -	bool
>>>> +	bool "Freescale Local Bus support"
>>>> +	depends on FSL_SOC
>>>>  	help
>>>> -	  Freescale Localbus support
>>>> +	  Enables reporting of errors from the Freescale local bus
>>>> +	  controller.  Also contains some common code used by
>>>> +	  drivers for specific local bus peripherals.
>>>>
>>>>  config FSL_GTM
>>>>  	bool
>>>> diff --git a/arch/powerpc/include/asm/fsl_lbc.h
>>> b/arch/powerpc/include/asm/fsl_lbc.h
>>>> index 1b5a210..0c40c05 100644
>>>> --- a/arch/powerpc/include/asm/fsl_lbc.h
>>>> +++ b/arch/powerpc/include/asm/fsl_lbc.h
>>>> @@ -1,9 +1,10 @@
>>>>  /* Freescale Local Bus Controller
>>>>   *
>>>> - * Copyright (c) 2006-2007 Freescale Semiconductor
>>>> + * Copyright (c) 2006-2007, 2010 Freescale Semiconductor
>>>>   *
>>>>   * Authors: Nick Spence <nick.spence@freescale.com>,
>>>>   *          Scott Wood <scottwood@freescale.com>
>>>> + *          Jack Lan <jack.lan@freescale.com>
>>>>   *
>>>>   * This program is free software; you can redistribute it and/or modify
>>>>   * it under the terms of the GNU General Public License as published by
>>>> @@ -26,6 +27,8 @@
>>>>  #include <linux/compiler.h>
>>>>  #include <linux/types.h>
>>>>  #include <linux/io.h>
>>>> +#include <linux/device.h>
>>>> +#include <linux/spinlock.h>
>>>>
>>>>  struct fsl_lbc_bank {
>>>>  	__be32 br;             /**< Base Register  */
>>>> @@ -125,13 +128,23 @@ struct fsl_lbc_regs {
>>>>  #define LTESR_ATMW 0x00800000
>>>>  #define LTESR_ATMR 0x00400000
>>>>  #define LTESR_CS   0x00080000
>>>> +#define LTESR_UPM  0x00000002
>>>>  #define LTESR_CC   0x00000001
>>>>  #define LTESR_NAND_MASK (LTESR_FCT | LTESR_PAR | LTESR_CC)
>>>> +#define LTESR_MASK      (LTESR_BM | LTESR_FCT | LTESR_PAR | LTESR_WP \
>>>> +			 | LTESR_ATMW | LTESR_ATMR | LTESR_CS | LTESR_UPM \
>>>> +			 | LTESR_CC)
>>>> +#define LTESR_CLEAR	0xFFFFFFFF
>>>> +#define LTECCR_CLEAR	0xFFFFFFFF
>>>> +#define LTESR_STATUS	LTESR_MASK
>>>> +#define LTEIR_ENABLE	LTESR_MASK
>>>> +#define LTEDR_ENABLE	0x00000000
>>>>  	__be32 ltedr;           /**< Transfer Error Disable Register */
>>>>  	__be32 lteir;           /**< Transfer Error Interrupt Register */
>>>>  	__be32 lteatr;          /**< Transfer Error Attributes Register */
>>>>  	__be32 ltear;           /**< Transfer Error Address Register */
>>>> -	u8 res6[0xC];
>>>> +	__be32 lteccr;          /**< Transfer Error ECC Register */
>>>> +	u8 res6[0x8];
>>>>  	__be32 lbcr;            /**< Configuration Register */
>>>>  #define LBCR_LDIS  0x80000000
>>>>  #define LBCR_LDIS_SHIFT    31
>>>> @@ -265,7 +278,23 @@ static inline void fsl_upm_end_pattern(struct fsl_upm
>>> *upm)
>>>>  		cpu_relax();
>>>>  }
>>>>
>>>> +/* overview of the fsl lbc controller */
>>>> +
>>>> +struct fsl_lbc_ctrl {
>>>> +	/* device info */
>>>> +	struct device			*dev;
>>>> +	struct fsl_lbc_regs __iomem	*regs;
>>>> +	int				irq;
>>>> +	wait_queue_head_t		irq_wait;
>>>> +	spinlock_t			lock;
>>>> +	void				*nand;
>>>> +
>>>> +	/* status read from LTESR by irq handler */
>>>> +	unsigned int			irq_status;
>>>> +};
>>>> +
>>>>  extern int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base,
>>>>  			       u32 mar);
>>>> +extern struct fsl_lbc_ctrl *fsl_lbc_ctrl_dev;
>>>>
>>>>  #endif /* __ASM_FSL_LBC_H */
>>>> diff --git a/arch/powerpc/sysdev/fsl_lbc.c b/arch/powerpc/sysdev/fsl_lbc.c
>>>> index dceb8d1..4bb0336 100644
>>>> --- a/arch/powerpc/sysdev/fsl_lbc.c
>>>> +++ b/arch/powerpc/sysdev/fsl_lbc.c
>>>> @@ -2,8 +2,11 @@
>>>>   * Freescale LBC and UPM routines.
>>>>   *
>>>>   * Copyright (c) 2007-2008  MontaVista Software, Inc.
>>>> + * Copyright (c) 2010 Freescale Semiconductor
>>>>   *
>>>>   * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
>>>> + * Author: Jack Lan <Jack.Lan@freescale.com>
>>>> + * Author: Roy Zang <tie-fei.zang@freescale.com>
>>>>   *
>>>>   * This program is free software; you can redistribute it and/or modify
>>>>   * it under the terms of the GNU General Public License as published by
>>>> @@ -19,39 +22,16 @@
>>>>  #include <linux/types.h>
>>>>  #include <linux/io.h>
>>>>  #include <linux/of.h>
>>>> +#include <linux/slab.h>
>>>> +#include <linux/platform_device.h>
>>>> +#include <linux/interrupt.h>
>>>> +#include <linux/mod_devicetable.h>
>>>>  #include <asm/prom.h>
>>>>  #include <asm/fsl_lbc.h>
>>>>
>>>>  static spinlock_t fsl_lbc_lock = __SPIN_LOCK_UNLOCKED(fsl_lbc_lock);
>>>> -static struct fsl_lbc_regs __iomem *fsl_lbc_regs;
>>>> -
>>>> -static char __initdata *compat_lbc[] = {
>>>> -	"fsl,pq2-localbus",
>>>> -	"fsl,pq2pro-localbus",
>>>> -	"fsl,pq3-localbus",
>>>> -	"fsl,elbc",
>>>> -};
>>>> -
>>>> -static int __init fsl_lbc_init(void)
>>>> -{
>>>> -	struct device_node *lbus;
>>>> -	int i;
>>>> -
>>>> -	for (i = 0; i < ARRAY_SIZE(compat_lbc); i++) {
>>>> -		lbus = of_find_compatible_node(NULL, NULL, compat_lbc[i]);
>>>> -		if (lbus)
>>>> -			goto found;
>>>> -	}
>>>> -	return -ENODEV;
>>>> -
>>>> -found:
>>>> -	fsl_lbc_regs = of_iomap(lbus, 0);
>>>> -	of_node_put(lbus);
>>>> -	if (!fsl_lbc_regs)
>>>> -		return -ENOMEM;
>>>> -	return 0;
>>>> -}
>>>> -arch_initcall(fsl_lbc_init);
>>>> +struct fsl_lbc_ctrl *fsl_lbc_ctrl_dev;
>>>> +EXPORT_SYMBOL(fsl_lbc_ctrl_dev);
>>>>
>>>>  /**
>>>>   * fsl_lbc_find - find Localbus bank
>>>> @@ -65,13 +45,15 @@ arch_initcall(fsl_lbc_init);
>>>>  int fsl_lbc_find(phys_addr_t addr_base)
>>>>  {
>>>>  	int i;
>>>> +	struct fsl_lbc_regs __iomem *lbc;
>>>>
>>>> -	if (!fsl_lbc_regs)
>>>> +	if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
>>>>  		return -ENODEV;
>>>>
>>>> -	for (i = 0; i < ARRAY_SIZE(fsl_lbc_regs->bank); i++) {
>>>> -		__be32 br = in_be32(&fsl_lbc_regs->bank[i].br);
>>>> -		__be32 or = in_be32(&fsl_lbc_regs->bank[i].or);
>>>> +	lbc = fsl_lbc_ctrl_dev->regs;
>>>> +	for (i = 0; i < ARRAY_SIZE(lbc->bank); i++) {
>>>> +		__be32 br = in_be32(&lbc->bank[i].br);
>>>> +		__be32 or = in_be32(&lbc->bank[i].or);
>>>>
>>>>  		if (br & BR_V && (br & or & BR_BA) == addr_base)
>>>>  			return i;
>>>> @@ -94,22 +76,27 @@ int fsl_upm_find(phys_addr_t addr_base, struct fsl_upm
>>> *upm)
>>>>  {
>>>>  	int bank;
>>>>  	__be32 br;
>>>> +	struct fsl_lbc_regs __iomem *lbc;
>>>>
>>>>  	bank = fsl_lbc_find(addr_base);
>>>>  	if (bank < 0)
>>>>  		return bank;
>>>>
>>>> -	br = in_be32(&fsl_lbc_regs->bank[bank].br);
>>>> +	if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
>>>> +		return -ENODEV;
>>>> +
>>>> +	lbc = fsl_lbc_ctrl_dev->regs;
>>>> +	br = in_be32(&lbc->bank[bank].br);
>>>>
>>>>  	switch (br & BR_MSEL) {
>>>>  	case BR_MS_UPMA:
>>>> -		upm->mxmr = &fsl_lbc_regs->mamr;
>>>> +		upm->mxmr = &lbc->mamr;
>>>>  		break;
>>>>  	case BR_MS_UPMB:
>>>> -		upm->mxmr = &fsl_lbc_regs->mbmr;
>>>> +		upm->mxmr = &lbc->mbmr;
>>>>  		break;
>>>>  	case BR_MS_UPMC:
>>>> -		upm->mxmr = &fsl_lbc_regs->mcmr;
>>>> +		upm->mxmr = &lbc->mcmr;
>>>>  		break;
>>>>  	default:
>>>>  		return -EINVAL;
>>>> @@ -148,9 +135,12 @@ int fsl_upm_run_pattern(struct fsl_upm *upm, void
>>> __iomem *io_base, u32 mar)
>>>>  	int ret = 0;
>>>>  	unsigned long flags;
>>>>
>>>> +	if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
>>>> +		return -ENODEV;
>>>> +
>>>>  	spin_lock_irqsave(&fsl_lbc_lock, flags);
>>>>
>>>> -	out_be32(&fsl_lbc_regs->mar, mar);
>>>> +	out_be32(&fsl_lbc_ctrl_dev->regs->mar, mar);
>>>>
>>>>  	switch (upm->width) {
>>>>  	case 8:
>>>> @@ -172,3 +162,166 @@ int fsl_upm_run_pattern(struct fsl_upm *upm, void
>>> __iomem *io_base, u32 mar)
>>>>  	return ret;
>>>>  }
>>>>  EXPORT_SYMBOL(fsl_upm_run_pattern);
>>>> +
>>>> +static int __devinit fsl_lbc_ctrl_init(struct fsl_lbc_ctrl *ctrl)
>>>> +{
>>>> +	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
>>>> +
>>>> +	/* clear event registers */
>>>> +	setbits32(&lbc->ltesr, LTESR_CLEAR);
>>>> +	out_be32(&lbc->lteatr, 0);
>>>> +	out_be32(&lbc->ltear, 0);
>>>> +	out_be32(&lbc->lteccr, LTECCR_CLEAR);
>>>> +	out_be32(&lbc->ltedr, LTEDR_ENABLE);
>>>> +
>>>> +	/* Enable interrupts for any detected events */
>>>> +	out_be32(&lbc->lteir, LTEIR_ENABLE);
>>>> +
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +/*
>>>> + * NOTE: This interrupt is used to report localbus events of various kinds,
>>>> + * such as transaction errors on the chipselects.
>>>> + */
>>>> +
>>>> +static irqreturn_t fsl_lbc_ctrl_irq(int irqno, void *data)
>>>> +{
>>>> +	struct fsl_lbc_ctrl *ctrl = data;
>>>> +	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
>>>> +	u32 status;
>>>> +
>>>> +	status = in_be32(&lbc->ltesr);
>>>> +	if (!status)
>>>> +		return IRQ_NONE;
>>>> +
>>>> +	out_be32(&lbc->ltesr, LTESR_CLEAR);
>>>> +	out_be32(&lbc->lteatr, 0);
>>>> +	out_be32(&lbc->ltear, 0);
>>>> +	ctrl->irq_status = status;
>>>> +
>>>> +	if (status & LTESR_BM)
>>>> +		dev_err(ctrl->dev, "Local bus monitor time-out: "
>>>> +			"LTESR 0x%08X\n", status);
>>>> +	if (status & LTESR_WP)
>>>> +		dev_err(ctrl->dev, "Write protect error: "
>>>> +			"LTESR 0x%08X\n", status);
>>>> +	if (status & LTESR_ATMW)
>>>> +		dev_err(ctrl->dev, "Atomic write error: "
>>>> +			"LTESR 0x%08X\n", status);
>>>> +	if (status & LTESR_ATMR)
>>>> +		dev_err(ctrl->dev, "Atomic read error: "
>>>> +			"LTESR 0x%08X\n", status);
>>>> +	if (status & LTESR_CS)
>>>> +		dev_err(ctrl->dev, "Chip select error: "
>>>> +			"LTESR 0x%08X\n", status);
>>>> +	if (status & LTESR_UPM)
>>>> +		;
>>>> +	if (status & LTESR_FCT) {
>>>> +		dev_err(ctrl->dev, "FCM command time-out: "
>>>> +			"LTESR 0x%08X\n", status);
>>>> +		smp_wmb();
>>>> +		wake_up(&ctrl->irq_wait);
>>>> +	}
>>>> +	if (status & LTESR_PAR) {
>>>> +		dev_err(ctrl->dev, "Parity or Uncorrectable ECC error: "
>>>> +			"LTESR 0x%08X\n", status);
>>>> +		smp_wmb();
>>>> +		wake_up(&ctrl->irq_wait);
>>>> +	}
>>>> +	if (status & LTESR_CC) {
>>>> +		smp_wmb();
>>>> +		wake_up(&ctrl->irq_wait);
>>>> +	}
>>>> +	if (status & ~LTESR_MASK)
>>>> +		dev_err(ctrl->dev, "Unknown error: "
>>>> +			"LTESR 0x%08X\n", status);
>>>> +	return IRQ_HANDLED;
>>>> +}
>>>> +
>>>> +/*
>>>> + * fsl_lbc_ctrl_probe
>>>> + *
>>>> + * called by device layer when it finds a device matching
>>>> + * one our driver can handled. This code allocates all of
>>>> + * the resources needed for the controller only.  The
>>>> + * resources for the NAND banks themselves are allocated
>>>> + * in the chip probe function.
>>>> +*/
>>>> +
>>>> +static int __devinit fsl_lbc_ctrl_probe(struct platform_device *dev)
>>>> +{
>>>> +	int ret;
>>>> +
>>>> +	if (!dev->dev.of_node) {
>>>> +		dev_err(&dev->dev, "Device OF-Node is NULL");
>>>> +		return -EFAULT;
>>>> +	}
>>>> +
>>>> +	fsl_lbc_ctrl_dev = kzalloc(sizeof(*fsl_lbc_ctrl_dev), GFP_KERNEL);
>>>> +	if (!fsl_lbc_ctrl_dev)
>>>> +		return -ENOMEM;
>>>> +
>>>> +	dev_set_drvdata(&dev->dev, fsl_lbc_ctrl_dev);
>>>> +
>>>> +	spin_lock_init(&fsl_lbc_ctrl_dev->lock);
>>>> +	init_waitqueue_head(&fsl_lbc_ctrl_dev->irq_wait);
>>>> +
>>>> +	fsl_lbc_ctrl_dev->regs = of_iomap(dev->dev.of_node, 0);
>>>> +	if (!fsl_lbc_ctrl_dev->regs) {
>>>> +		dev_err(&dev->dev, "failed to get memory region\n");
>>>> +		ret = -ENODEV;
>>>> +		goto err;
>>> Looks you always iounmap(fsl_lbc_ctrl_dev->regs) on position 'err' but here
>>> of_iomap() is already failed you should skip iounmap() fsl_lbc_ctrl_dev->regs
>>> again. So you should improve that as the following on 'err', or layout 'err'
>>> in
>>> gain.
>>> ------
>>> 	if(fsl_lbc_ctrl_dev->regs)
>>> 		iounmap(fsl_lbc_ctrl_dev->regs);
>>>
>>> Tiejun
>> You are right!
>> How about
>>  
>>         if (!fsl_lbc_ctrl_dev->regs) {
>>                 dev_err(&dev->dev, "failed to get memory region\n");
>>                 kfree(fsl_lbc_ctrl_dev);
>>                 return -ENOMEM;
>>         }
> 
> Although this is a big problem, I prefer to return 'ENXIO' :)
		^
		Typo: is not a ......

Tiejun

> 
> Others are fine to me.
> 
> Tiejun
> 
>> ...
>>
>> Thanks.
>> Roy
> 
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
>
Scott Wood Oct. 18, 2010, 4:06 p.m. UTC | #5
On Mon, 18 Oct 2010 16:55:49 +0800
"tiejun.chen" <tiejun.chen@windriver.com> wrote:

> Looks you always iounmap(fsl_lbc_ctrl_dev->regs) on position 'err' but here
> of_iomap() is already failed you should skip iounmap() fsl_lbc_ctrl_dev->regs
> again. So you should improve that as the following on 'err', or layout 'err' in
> gain.
> ------
> 	if(fsl_lbc_ctrl_dev->regs)
> 		iounmap(fsl_lbc_ctrl_dev->regs);
> 

It looks like iounmap(NULL) is a no-op, just like kfree(NULL).

-Scott
Tiejun Chen Oct. 19, 2010, 1:35 a.m. UTC | #6
Scott Wood wrote:
> On Mon, 18 Oct 2010 16:55:49 +0800
> "tiejun.chen" <tiejun.chen@windriver.com> wrote:
> 
>> Looks you always iounmap(fsl_lbc_ctrl_dev->regs) on position 'err' but here
>> of_iomap() is already failed you should skip iounmap() fsl_lbc_ctrl_dev->regs
>> again. So you should improve that as the following on 'err', or layout 'err' in
>> gain.
>> ------
>> 	if(fsl_lbc_ctrl_dev->regs)
>> 		iounmap(fsl_lbc_ctrl_dev->regs);
>>
> 
> It looks like iounmap(NULL) is a no-op, just like kfree(NULL).
> 

Absolutely, I know what you mean :)

But I think we should take care of every line as one normal progress/rule. That
will make us understand easily, and maybe we also would benefit a little good
performance from those codes.

Tiejun

> -Scott
> 
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
>
Zang Roy-R61911 Oct. 19, 2010, 6:37 a.m. UTC | #7
> -----Original Message-----
> From: Wood Scott-B07421
> Sent: Tuesday, October 19, 2010 0:07 AM
> To: tiejun.chen
> Cc: Zang Roy-R61911; linux-mtd@lists.infradead.org; Wood Scott-B07421;
> dedekind1@gmail.com; Lan Chunhe-B25806; linuxppc-dev@ozlabs.org;
akpm@linux-
> foundation.org; dwmw2@infradead.org; Gala Kumar-B11780
> Subject: Re: [PATCH 1/2] P4080/eLBC: Make Freescale elbc interrupt
common to
> elbc devices
> 
> On Mon, 18 Oct 2010 16:55:49 +0800
> "tiejun.chen" <tiejun.chen@windriver.com> wrote:
> 
> > Looks you always iounmap(fsl_lbc_ctrl_dev->regs) on position 'err'
but here
> > of_iomap() is already failed you should skip iounmap()
fsl_lbc_ctrl_dev-
> >regs
> > again. So you should improve that as the following on 'err', or
layout 'err'
> in
> > gain.
> > ------
> > 	if(fsl_lbc_ctrl_dev->regs)
> > 		iounmap(fsl_lbc_ctrl_dev->regs);
> >
> 
> It looks like iounmap(NULL) is a no-op, just like kfree(NULL).
Thanks for the reminder. I will keep original post, if there is no more
comment.
Roy
Kumar Gala Oct. 19, 2010, 1:18 p.m. UTC | #8
On Oct 18, 2010, at 2:22 AM, Roy Zang wrote:

> Move Freescale elbc interrupt from nand dirver to elbc driver.
> Then all elbc devices can use the interrupt instead of ONLY nand.
> 
> For former nand driver, it had the two functions:
> 
> 1. detecting nand flash partitions;
> 2. registering elbc interrupt.
> 
> Now, second function is removed to fsl_lbc.c.
> 
> Signed-off-by: Lan Chunhe-B25806 <b25806@freescale.com>
> Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
> Reviewed-by: Anton Vorontsov <cbouatmailru@gmail.com>
> Cc: Wood Scott-B07421 <B07421@freescale.com>
> ---

Roy, this is a nit, but are these really p4080 specific?  just wondering why the subject is P4080/eLBC:...

- k
Zang Roy-R61911 Oct. 20, 2010, 5:12 a.m. UTC | #9
> -----Original Message-----
> From: Kumar Gala [mailto:galak@kernel.crashing.org]
> Sent: Tuesday, October 19, 2010 21:19 PM
> To: Zang Roy-R61911
> Cc: linux-mtd@lists.infradead.org; Wood Scott-B07421;
dedekind1@gmail.com; Lan
> Chunhe-B25806; linuxppc-dev@ozlabs.org; akpm@linux-foundation.org;
> dwmw2@infradead.org; Gala Kumar-B11780
> Subject: Re: [PATCH 1/2] P4080/eLBC: Make Freescale elbc interrupt
common to
> elbc devices
> 
> 
> On Oct 18, 2010, at 2:22 AM, Roy Zang wrote:
> 
> > Move Freescale elbc interrupt from nand dirver to elbc driver.
> > Then all elbc devices can use the interrupt instead of ONLY nand.
> >
> > For former nand driver, it had the two functions:
> >
> > 1. detecting nand flash partitions;
> > 2. registering elbc interrupt.
> >
> > Now, second function is removed to fsl_lbc.c.
> >
> > Signed-off-by: Lan Chunhe-B25806 <b25806@freescale.com>
> > Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
> > Reviewed-by: Anton Vorontsov <cbouatmailru@gmail.com>
> > Cc: Wood Scott-B07421 <B07421@freescale.com>
> > ---
> 
> Roy, this is a nit, but are these really p4080 specific?  just
wondering why
> the subject is P4080/eLBC:...
We start these code in P4080 project. Some customer want to track eLBC
error on P4080, but some of the code is limited in nand driver only ...
That is why P4080/eLBC ...
Roy
Kumar Gala Oct. 20, 2010, 6:54 a.m. UTC | #10
On Oct 20, 2010, at 12:12 AM, Zang Roy-R61911 wrote:

> 
> 
>> -----Original Message-----
>> From: Kumar Gala [mailto:galak@kernel.crashing.org]
>> Sent: Tuesday, October 19, 2010 21:19 PM
>> To: Zang Roy-R61911
>> Cc: linux-mtd@lists.infradead.org; Wood Scott-B07421;
> dedekind1@gmail.com; Lan
>> Chunhe-B25806; linuxppc-dev@ozlabs.org; akpm@linux-foundation.org;
>> dwmw2@infradead.org; Gala Kumar-B11780
>> Subject: Re: [PATCH 1/2] P4080/eLBC: Make Freescale elbc interrupt
> common to
>> elbc devices
>> 
>> 
>> On Oct 18, 2010, at 2:22 AM, Roy Zang wrote:
>> 
>>> Move Freescale elbc interrupt from nand dirver to elbc driver.
>>> Then all elbc devices can use the interrupt instead of ONLY nand.
>>> 
>>> For former nand driver, it had the two functions:
>>> 
>>> 1. detecting nand flash partitions;
>>> 2. registering elbc interrupt.
>>> 
>>> Now, second function is removed to fsl_lbc.c.
>>> 
>>> Signed-off-by: Lan Chunhe-B25806 <b25806@freescale.com>
>>> Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
>>> Reviewed-by: Anton Vorontsov <cbouatmailru@gmail.com>
>>> Cc: Wood Scott-B07421 <B07421@freescale.com>
>>> ---
>> 
>> Roy, this is a nit, but are these really p4080 specific?  just
> wondering why
>> the subject is P4080/eLBC:...
> We start these code in P4080 project. Some customer want to track eLBC
> error on P4080, but some of the code is limited in nand driver only ...
> That is why P4080/eLBC ...
> Roy

sure, but is anything about these patches p4080 specific?

- k
Zang Roy-R61911 Oct. 20, 2010, 8:33 a.m. UTC | #11
> -----Original Message-----
> From: Kumar Gala [mailto:galak@kernel.crashing.org]
> Sent: Wednesday, October 20, 2010 14:55 PM
> To: Zang Roy-R61911
> Cc: linux-mtd@lists.infradead.org; Wood Scott-B07421;
dedekind1@gmail.com; Lan
> Chunhe-B25806; linuxppc-dev@ozlabs.org; akpm@linux-foundation.org;
> dwmw2@infradead.org; Gala Kumar-B11780
> Subject: Re: [PATCH 1/2] P4080/eLBC: Make Freescale elbc interrupt
common to
> elbc devices
> 
> 
> On Oct 20, 2010, at 12:12 AM, Zang Roy-R61911 wrote:
> 
> >
> >
> >> -----Original Message-----
> >> From: Kumar Gala [mailto:galak@kernel.crashing.org]
> >> Sent: Tuesday, October 19, 2010 21:19 PM
> >> To: Zang Roy-R61911
> >> Cc: linux-mtd@lists.infradead.org; Wood Scott-B07421;
> > dedekind1@gmail.com; Lan
> >> Chunhe-B25806; linuxppc-dev@ozlabs.org; akpm@linux-foundation.org;
> >> dwmw2@infradead.org; Gala Kumar-B11780
> >> Subject: Re: [PATCH 1/2] P4080/eLBC: Make Freescale elbc interrupt
> > common to
> >> elbc devices
> >>
> >>
> >> On Oct 18, 2010, at 2:22 AM, Roy Zang wrote:
> >>
> >>> Move Freescale elbc interrupt from nand dirver to elbc driver.
> >>> Then all elbc devices can use the interrupt instead of ONLY nand.
> >>>
> >>> For former nand driver, it had the two functions:
> >>>
> >>> 1. detecting nand flash partitions;
> >>> 2. registering elbc interrupt.
> >>>
> >>> Now, second function is removed to fsl_lbc.c.
> >>>
> >>> Signed-off-by: Lan Chunhe-B25806 <b25806@freescale.com>
> >>> Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
> >>> Reviewed-by: Anton Vorontsov <cbouatmailru@gmail.com>
> >>> Cc: Wood Scott-B07421 <B07421@freescale.com>
> >>> ---
> >>
> >> Roy, this is a nit, but are these really p4080 specific?  just
> > wondering why
> >> the subject is P4080/eLBC:...
> > We start these code in P4080 project. Some customer want to track
eLBC
> > error on P4080, but some of the code is limited in nand driver only
...
> > That is why P4080/eLBC ...
> > Roy
> 
> sure, but is anything about these patches p4080 specific?
No.
Should I update the subject by a new version.
Thanks.
Roy
David Woodhouse Oct. 25, 2010, 2:15 p.m. UTC | #12
On Mon, 2010-10-18 at 17:46 +0800, tiejun.chen wrote:
> 
> >>> Looks you always iounmap(fsl_lbc_ctrl_dev->regs) on position 'err' but here
> >>> of_iomap() is already failed you should skip iounmap() fsl_lbc_ctrl_dev->regs
> >>> again. So you should improve that as the following on 'err', or layout 'err'
> >>> in
> >>> gain.
> >>> ------
> >>>     if(fsl_lbc_ctrl_dev->regs)
> >>>             iounmap(fsl_lbc_ctrl_dev->regs);
> >>>
> >>> Tiejun
> >> You are right!
> >> How about
> >>  
> >>         if (!fsl_lbc_ctrl_dev->regs) {
> >>                 dev_err(&dev->dev, "failed to get memory region\n");
> >>                 kfree(fsl_lbc_ctrl_dev);
> >>                 return -ENOMEM;
> >>         }
> > 
> > Although this is a big problem, I prefer to return 'ENXIO' :)
>                 ^
>                 Typo: is not a ...... 

My God! Why did you *both* repeat over 400 lines of patch, over and over
again, just for this?

Please, make sure you quote *only* what you actually need to quote for
context. Don't make me implement a filter for the list which rejects
messages that are more than 90% quotation.
diff mbox

Patch

diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 631e5a0..44df1ba 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -687,9 +687,12 @@  config 4xx_SOC
 	bool
 
 config FSL_LBC
-	bool
+	bool "Freescale Local Bus support"
+	depends on FSL_SOC
 	help
-	  Freescale Localbus support
+	  Enables reporting of errors from the Freescale local bus
+	  controller.  Also contains some common code used by
+	  drivers for specific local bus peripherals.
 
 config FSL_GTM
 	bool
diff --git a/arch/powerpc/include/asm/fsl_lbc.h b/arch/powerpc/include/asm/fsl_lbc.h
index 1b5a210..0c40c05 100644
--- a/arch/powerpc/include/asm/fsl_lbc.h
+++ b/arch/powerpc/include/asm/fsl_lbc.h
@@ -1,9 +1,10 @@ 
 /* Freescale Local Bus Controller
  *
- * Copyright (c) 2006-2007 Freescale Semiconductor
+ * Copyright (c) 2006-2007, 2010 Freescale Semiconductor
  *
  * Authors: Nick Spence <nick.spence@freescale.com>,
  *          Scott Wood <scottwood@freescale.com>
+ *          Jack Lan <jack.lan@freescale.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -26,6 +27,8 @@ 
 #include <linux/compiler.h>
 #include <linux/types.h>
 #include <linux/io.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
 
 struct fsl_lbc_bank {
 	__be32 br;             /**< Base Register  */
@@ -125,13 +128,23 @@  struct fsl_lbc_regs {
 #define LTESR_ATMW 0x00800000
 #define LTESR_ATMR 0x00400000
 #define LTESR_CS   0x00080000
+#define LTESR_UPM  0x00000002
 #define LTESR_CC   0x00000001
 #define LTESR_NAND_MASK (LTESR_FCT | LTESR_PAR | LTESR_CC)
+#define LTESR_MASK      (LTESR_BM | LTESR_FCT | LTESR_PAR | LTESR_WP \
+			 | LTESR_ATMW | LTESR_ATMR | LTESR_CS | LTESR_UPM \
+			 | LTESR_CC)
+#define LTESR_CLEAR	0xFFFFFFFF
+#define LTECCR_CLEAR	0xFFFFFFFF
+#define LTESR_STATUS	LTESR_MASK
+#define LTEIR_ENABLE	LTESR_MASK
+#define LTEDR_ENABLE	0x00000000
 	__be32 ltedr;           /**< Transfer Error Disable Register */
 	__be32 lteir;           /**< Transfer Error Interrupt Register */
 	__be32 lteatr;          /**< Transfer Error Attributes Register */
 	__be32 ltear;           /**< Transfer Error Address Register */
-	u8 res6[0xC];
+	__be32 lteccr;          /**< Transfer Error ECC Register */
+	u8 res6[0x8];
 	__be32 lbcr;            /**< Configuration Register */
 #define LBCR_LDIS  0x80000000
 #define LBCR_LDIS_SHIFT    31
@@ -265,7 +278,23 @@  static inline void fsl_upm_end_pattern(struct fsl_upm *upm)
 		cpu_relax();
 }
 
+/* overview of the fsl lbc controller */
+
+struct fsl_lbc_ctrl {
+	/* device info */
+	struct device			*dev;
+	struct fsl_lbc_regs __iomem	*regs;
+	int				irq;
+	wait_queue_head_t		irq_wait;
+	spinlock_t			lock;
+	void				*nand;
+
+	/* status read from LTESR by irq handler */
+	unsigned int			irq_status;
+};
+
 extern int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base,
 			       u32 mar);
+extern struct fsl_lbc_ctrl *fsl_lbc_ctrl_dev;
 
 #endif /* __ASM_FSL_LBC_H */
diff --git a/arch/powerpc/sysdev/fsl_lbc.c b/arch/powerpc/sysdev/fsl_lbc.c
index dceb8d1..4bb0336 100644
--- a/arch/powerpc/sysdev/fsl_lbc.c
+++ b/arch/powerpc/sysdev/fsl_lbc.c
@@ -2,8 +2,11 @@ 
  * Freescale LBC and UPM routines.
  *
  * Copyright (c) 2007-2008  MontaVista Software, Inc.
+ * Copyright (c) 2010 Freescale Semiconductor
  *
  * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
+ * Author: Jack Lan <Jack.Lan@freescale.com>
+ * Author: Roy Zang <tie-fei.zang@freescale.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -19,39 +22,16 @@ 
 #include <linux/types.h>
 #include <linux/io.h>
 #include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/mod_devicetable.h>
 #include <asm/prom.h>
 #include <asm/fsl_lbc.h>
 
 static spinlock_t fsl_lbc_lock = __SPIN_LOCK_UNLOCKED(fsl_lbc_lock);
-static struct fsl_lbc_regs __iomem *fsl_lbc_regs;
-
-static char __initdata *compat_lbc[] = {
-	"fsl,pq2-localbus",
-	"fsl,pq2pro-localbus",
-	"fsl,pq3-localbus",
-	"fsl,elbc",
-};
-
-static int __init fsl_lbc_init(void)
-{
-	struct device_node *lbus;
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(compat_lbc); i++) {
-		lbus = of_find_compatible_node(NULL, NULL, compat_lbc[i]);
-		if (lbus)
-			goto found;
-	}
-	return -ENODEV;
-
-found:
-	fsl_lbc_regs = of_iomap(lbus, 0);
-	of_node_put(lbus);
-	if (!fsl_lbc_regs)
-		return -ENOMEM;
-	return 0;
-}
-arch_initcall(fsl_lbc_init);
+struct fsl_lbc_ctrl *fsl_lbc_ctrl_dev;
+EXPORT_SYMBOL(fsl_lbc_ctrl_dev);
 
 /**
  * fsl_lbc_find - find Localbus bank
@@ -65,13 +45,15 @@  arch_initcall(fsl_lbc_init);
 int fsl_lbc_find(phys_addr_t addr_base)
 {
 	int i;
+	struct fsl_lbc_regs __iomem *lbc;
 
-	if (!fsl_lbc_regs)
+	if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
 		return -ENODEV;
 
-	for (i = 0; i < ARRAY_SIZE(fsl_lbc_regs->bank); i++) {
-		__be32 br = in_be32(&fsl_lbc_regs->bank[i].br);
-		__be32 or = in_be32(&fsl_lbc_regs->bank[i].or);
+	lbc = fsl_lbc_ctrl_dev->regs;
+	for (i = 0; i < ARRAY_SIZE(lbc->bank); i++) {
+		__be32 br = in_be32(&lbc->bank[i].br);
+		__be32 or = in_be32(&lbc->bank[i].or);
 
 		if (br & BR_V && (br & or & BR_BA) == addr_base)
 			return i;
@@ -94,22 +76,27 @@  int fsl_upm_find(phys_addr_t addr_base, struct fsl_upm *upm)
 {
 	int bank;
 	__be32 br;
+	struct fsl_lbc_regs __iomem *lbc;
 
 	bank = fsl_lbc_find(addr_base);
 	if (bank < 0)
 		return bank;
 
-	br = in_be32(&fsl_lbc_regs->bank[bank].br);
+	if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
+		return -ENODEV;
+
+	lbc = fsl_lbc_ctrl_dev->regs;
+	br = in_be32(&lbc->bank[bank].br);
 
 	switch (br & BR_MSEL) {
 	case BR_MS_UPMA:
-		upm->mxmr = &fsl_lbc_regs->mamr;
+		upm->mxmr = &lbc->mamr;
 		break;
 	case BR_MS_UPMB:
-		upm->mxmr = &fsl_lbc_regs->mbmr;
+		upm->mxmr = &lbc->mbmr;
 		break;
 	case BR_MS_UPMC:
-		upm->mxmr = &fsl_lbc_regs->mcmr;
+		upm->mxmr = &lbc->mcmr;
 		break;
 	default:
 		return -EINVAL;
@@ -148,9 +135,12 @@  int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, u32 mar)
 	int ret = 0;
 	unsigned long flags;
 
+	if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
+		return -ENODEV;
+
 	spin_lock_irqsave(&fsl_lbc_lock, flags);
 
-	out_be32(&fsl_lbc_regs->mar, mar);
+	out_be32(&fsl_lbc_ctrl_dev->regs->mar, mar);
 
 	switch (upm->width) {
 	case 8:
@@ -172,3 +162,166 @@  int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, u32 mar)
 	return ret;
 }
 EXPORT_SYMBOL(fsl_upm_run_pattern);
+
+static int __devinit fsl_lbc_ctrl_init(struct fsl_lbc_ctrl *ctrl)
+{
+	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
+
+	/* clear event registers */
+	setbits32(&lbc->ltesr, LTESR_CLEAR);
+	out_be32(&lbc->lteatr, 0);
+	out_be32(&lbc->ltear, 0);
+	out_be32(&lbc->lteccr, LTECCR_CLEAR);
+	out_be32(&lbc->ltedr, LTEDR_ENABLE);
+
+	/* Enable interrupts for any detected events */
+	out_be32(&lbc->lteir, LTEIR_ENABLE);
+
+	return 0;
+}
+
+/*
+ * NOTE: This interrupt is used to report localbus events of various kinds,
+ * such as transaction errors on the chipselects.
+ */
+
+static irqreturn_t fsl_lbc_ctrl_irq(int irqno, void *data)
+{
+	struct fsl_lbc_ctrl *ctrl = data;
+	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
+	u32 status;
+
+	status = in_be32(&lbc->ltesr);
+	if (!status)
+		return IRQ_NONE;
+
+	out_be32(&lbc->ltesr, LTESR_CLEAR);
+	out_be32(&lbc->lteatr, 0);
+	out_be32(&lbc->ltear, 0);
+	ctrl->irq_status = status;
+
+	if (status & LTESR_BM)
+		dev_err(ctrl->dev, "Local bus monitor time-out: "
+			"LTESR 0x%08X\n", status);
+	if (status & LTESR_WP)
+		dev_err(ctrl->dev, "Write protect error: "
+			"LTESR 0x%08X\n", status);
+	if (status & LTESR_ATMW)
+		dev_err(ctrl->dev, "Atomic write error: "
+			"LTESR 0x%08X\n", status);
+	if (status & LTESR_ATMR)
+		dev_err(ctrl->dev, "Atomic read error: "
+			"LTESR 0x%08X\n", status);
+	if (status & LTESR_CS)
+		dev_err(ctrl->dev, "Chip select error: "
+			"LTESR 0x%08X\n", status);
+	if (status & LTESR_UPM)
+		;
+	if (status & LTESR_FCT) {
+		dev_err(ctrl->dev, "FCM command time-out: "
+			"LTESR 0x%08X\n", status);
+		smp_wmb();
+		wake_up(&ctrl->irq_wait);
+	}
+	if (status & LTESR_PAR) {
+		dev_err(ctrl->dev, "Parity or Uncorrectable ECC error: "
+			"LTESR 0x%08X\n", status);
+		smp_wmb();
+		wake_up(&ctrl->irq_wait);
+	}
+	if (status & LTESR_CC) {
+		smp_wmb();
+		wake_up(&ctrl->irq_wait);
+	}
+	if (status & ~LTESR_MASK)
+		dev_err(ctrl->dev, "Unknown error: "
+			"LTESR 0x%08X\n", status);
+	return IRQ_HANDLED;
+}
+
+/*
+ * fsl_lbc_ctrl_probe
+ *
+ * called by device layer when it finds a device matching
+ * one our driver can handled. This code allocates all of
+ * the resources needed for the controller only.  The
+ * resources for the NAND banks themselves are allocated
+ * in the chip probe function.
+*/
+
+static int __devinit fsl_lbc_ctrl_probe(struct platform_device *dev)
+{
+	int ret;
+
+	if (!dev->dev.of_node) {
+		dev_err(&dev->dev, "Device OF-Node is NULL");
+		return -EFAULT;
+	}
+
+	fsl_lbc_ctrl_dev = kzalloc(sizeof(*fsl_lbc_ctrl_dev), GFP_KERNEL);
+	if (!fsl_lbc_ctrl_dev)
+		return -ENOMEM;
+
+	dev_set_drvdata(&dev->dev, fsl_lbc_ctrl_dev);
+
+	spin_lock_init(&fsl_lbc_ctrl_dev->lock);
+	init_waitqueue_head(&fsl_lbc_ctrl_dev->irq_wait);
+
+	fsl_lbc_ctrl_dev->regs = of_iomap(dev->dev.of_node, 0);
+	if (!fsl_lbc_ctrl_dev->regs) {
+		dev_err(&dev->dev, "failed to get memory region\n");
+		ret = -ENODEV;
+		goto err;
+	}
+
+	fsl_lbc_ctrl_dev->irq = irq_of_parse_and_map(dev->dev.of_node, 0);
+	if (fsl_lbc_ctrl_dev->irq == NO_IRQ) {
+		dev_err(&dev->dev, "failed to get irq resource\n");
+		ret = -ENODEV;
+		goto err;
+	}
+
+	fsl_lbc_ctrl_dev->dev = &dev->dev;
+
+	ret = fsl_lbc_ctrl_init(fsl_lbc_ctrl_dev);
+	if (ret < 0)
+		goto err;
+
+	ret = request_irq(fsl_lbc_ctrl_dev->irq, fsl_lbc_ctrl_irq, 0,
+				"fsl-lbc", fsl_lbc_ctrl_dev);
+	if (ret != 0) {
+		dev_err(&dev->dev, "failed to install irq (%d)\n",
+			fsl_lbc_ctrl_dev->irq);
+		ret = fsl_lbc_ctrl_dev->irq;
+		goto err;
+	}
+
+	return 0;
+
+err:
+	iounmap(fsl_lbc_ctrl_dev->regs);
+	kfree(fsl_lbc_ctrl_dev);
+	return ret;
+}
+
+static const struct of_device_id fsl_lbc_match[] = {
+	{ .compatible = "fsl,elbc", },
+	{ .compatible = "fsl,pq3-localbus", },
+	{ .compatible = "fsl,pq2-localbus", },
+	{ .compatible = "fsl,pq2pro-localbus", },
+	{},
+};
+
+static struct platform_driver fsl_lbc_ctrl_driver = {
+	.driver = {
+		.name = "fsl-lbc",
+		.of_match_table = fsl_lbc_match,
+	},
+	.probe = fsl_lbc_ctrl_probe,
+};
+
+static int __init fsl_lbc_init(void)
+{
+	return platform_driver_register(&fsl_lbc_ctrl_driver);
+}
+module_init(fsl_lbc_init);
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 8b4b67c..4132c46 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -458,6 +458,7 @@  config MTD_NAND_ORION
 config MTD_NAND_FSL_ELBC
 	tristate "NAND support for Freescale eLBC controllers"
 	depends on PPC_OF
+	select FSL_LBC
 	help
 	  Various Freescale chips, including the 8313, include a NAND Flash
 	  Controller Module with built-in hardware ECC capabilities.
diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c
index 80de0bf..400f01f 100644
--- a/drivers/mtd/nand/fsl_elbc_nand.c
+++ b/drivers/mtd/nand/fsl_elbc_nand.c
@@ -1,9 +1,11 @@ 
 /* Freescale Enhanced Local Bus Controller NAND driver
  *
- * Copyright (c) 2006-2007 Freescale Semiconductor
+ * Copyright (c) 2006-2007, 2010 Freescale Semiconductor
  *
  * Authors: Nick Spence <nick.spence@freescale.com>,
  *          Scott Wood <scottwood@freescale.com>
+ *          Jack Lan <jack.lan@freescale.com>
+ *          Roy Zang <tie-fei.zang@freescale.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -27,6 +29,7 @@ 
 #include <linux/string.h>
 #include <linux/ioport.h>
 #include <linux/of_platform.h>
+#include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/interrupt.h>
 
@@ -42,14 +45,12 @@ 
 #define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */
 #define FCM_TIMEOUT_MSECS 500 /* Maximum number of mSecs to wait for FCM */
 
-struct fsl_elbc_ctrl;
-
 /* mtd information per set */
 
 struct fsl_elbc_mtd {
 	struct mtd_info mtd;
 	struct nand_chip chip;
-	struct fsl_elbc_ctrl *ctrl;
+	struct fsl_lbc_ctrl *ctrl;
 
 	struct device *dev;
 	int bank;               /* Chip select bank number           */
@@ -58,18 +59,12 @@  struct fsl_elbc_mtd {
 	unsigned int fmr;       /* FCM Flash Mode Register value     */
 };
 
-/* overview of the fsl elbc controller */
+/* Freescale eLBC FCM controller infomation */
 
-struct fsl_elbc_ctrl {
+struct fsl_elbc_fcm_ctrl {
 	struct nand_hw_control controller;
 	struct fsl_elbc_mtd *chips[MAX_BANKS];
 
-	/* device info */
-	struct device *dev;
-	struct fsl_lbc_regs __iomem *regs;
-	int irq;
-	wait_queue_head_t irq_wait;
-	unsigned int irq_status; /* status read from LTESR by irq handler */
 	u8 __iomem *addr;        /* Address of assigned FCM buffer        */
 	unsigned int page;       /* Last page written to / read from      */
 	unsigned int read_bytes; /* Number of bytes read during command   */
@@ -79,6 +74,7 @@  struct fsl_elbc_ctrl {
 	unsigned int mdr;        /* UPM/FCM Data Register value           */
 	unsigned int use_mdr;    /* Non zero if the MDR is to be set      */
 	unsigned int oob;        /* Non zero if operating on OOB data     */
+	unsigned int counter;	 /* counter for the initializations	  */
 	char *oob_poi;           /* Place to write ECC after read back    */
 };
 
@@ -164,11 +160,12 @@  static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
 {
 	struct nand_chip *chip = mtd->priv;
 	struct fsl_elbc_mtd *priv = chip->priv;
-	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+	struct fsl_lbc_ctrl *ctrl = priv->ctrl;
 	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
+	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
 	int buf_num;
 
-	ctrl->page = page_addr;
+	elbc_fcm_ctrl->page = page_addr;
 
 	out_be32(&lbc->fbar,
 	         page_addr >> (chip->phys_erase_shift - chip->page_shift));
@@ -185,16 +182,18 @@  static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
 		buf_num = page_addr & 7;
 	}
 
-	ctrl->addr = priv->vbase + buf_num * 1024;
-	ctrl->index = column;
+	elbc_fcm_ctrl->addr = priv->vbase + buf_num * 1024;
+	elbc_fcm_ctrl->index = column;
 
 	/* for OOB data point to the second half of the buffer */
 	if (oob)
-		ctrl->index += priv->page_size ? 2048 : 512;
+		elbc_fcm_ctrl->index += priv->page_size ? 2048 : 512;
 
-	dev_vdbg(ctrl->dev, "set_addr: bank=%d, ctrl->addr=0x%p (0x%p), "
+	dev_vdbg(priv->dev, "set_addr: bank=%d, "
+			    "elbc_fcm_ctrl->addr=0x%p (0x%p), "
 	                    "index %x, pes %d ps %d\n",
-	         buf_num, ctrl->addr, priv->vbase, ctrl->index,
+		 buf_num, elbc_fcm_ctrl->addr, priv->vbase,
+		 elbc_fcm_ctrl->index,
 	         chip->phys_erase_shift, chip->page_shift);
 }
 
@@ -205,18 +204,19 @@  static int fsl_elbc_run_command(struct mtd_info *mtd)
 {
 	struct nand_chip *chip = mtd->priv;
 	struct fsl_elbc_mtd *priv = chip->priv;
-	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+	struct fsl_lbc_ctrl *ctrl = priv->ctrl;
+	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
 	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
 
 	/* Setup the FMR[OP] to execute without write protection */
 	out_be32(&lbc->fmr, priv->fmr | 3);
-	if (ctrl->use_mdr)
-		out_be32(&lbc->mdr, ctrl->mdr);
+	if (elbc_fcm_ctrl->use_mdr)
+		out_be32(&lbc->mdr, elbc_fcm_ctrl->mdr);
 
-	dev_vdbg(ctrl->dev,
+	dev_vdbg(priv->dev,
 	         "fsl_elbc_run_command: fmr=%08x fir=%08x fcr=%08x\n",
 	         in_be32(&lbc->fmr), in_be32(&lbc->fir), in_be32(&lbc->fcr));
-	dev_vdbg(ctrl->dev,
+	dev_vdbg(priv->dev,
 	         "fsl_elbc_run_command: fbar=%08x fpar=%08x "
 	         "fbcr=%08x bank=%d\n",
 	         in_be32(&lbc->fbar), in_be32(&lbc->fpar),
@@ -229,19 +229,18 @@  static int fsl_elbc_run_command(struct mtd_info *mtd)
 	/* wait for FCM complete flag or timeout */
 	wait_event_timeout(ctrl->irq_wait, ctrl->irq_status,
 	                   FCM_TIMEOUT_MSECS * HZ/1000);
-	ctrl->status = ctrl->irq_status;
-
+	elbc_fcm_ctrl->status = ctrl->irq_status;
 	/* store mdr value in case it was needed */
-	if (ctrl->use_mdr)
-		ctrl->mdr = in_be32(&lbc->mdr);
+	if (elbc_fcm_ctrl->use_mdr)
+		elbc_fcm_ctrl->mdr = in_be32(&lbc->mdr);
 
-	ctrl->use_mdr = 0;
+	elbc_fcm_ctrl->use_mdr = 0;
 
-	if (ctrl->status != LTESR_CC) {
-		dev_info(ctrl->dev,
+	if (elbc_fcm_ctrl->status != LTESR_CC) {
+		dev_info(priv->dev,
 		         "command failed: fir %x fcr %x status %x mdr %x\n",
 		         in_be32(&lbc->fir), in_be32(&lbc->fcr),
-		         ctrl->status, ctrl->mdr);
+			 elbc_fcm_ctrl->status, elbc_fcm_ctrl->mdr);
 		return -EIO;
 	}
 
@@ -251,7 +250,7 @@  static int fsl_elbc_run_command(struct mtd_info *mtd)
 static void fsl_elbc_do_read(struct nand_chip *chip, int oob)
 {
 	struct fsl_elbc_mtd *priv = chip->priv;
-	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+	struct fsl_lbc_ctrl *ctrl = priv->ctrl;
 	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
 
 	if (priv->page_size) {
@@ -284,15 +283,16 @@  static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
 {
 	struct nand_chip *chip = mtd->priv;
 	struct fsl_elbc_mtd *priv = chip->priv;
-	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+	struct fsl_lbc_ctrl *ctrl = priv->ctrl;
+	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
 	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
 
-	ctrl->use_mdr = 0;
+	elbc_fcm_ctrl->use_mdr = 0;
 
 	/* clear the read buffer */
-	ctrl->read_bytes = 0;
+	elbc_fcm_ctrl->read_bytes = 0;
 	if (command != NAND_CMD_PAGEPROG)
-		ctrl->index = 0;
+		elbc_fcm_ctrl->index = 0;
 
 	switch (command) {
 	/* READ0 and READ1 read the entire buffer to use hardware ECC. */
@@ -301,7 +301,7 @@  static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
 
 	/* fall-through */
 	case NAND_CMD_READ0:
-		dev_dbg(ctrl->dev,
+		dev_dbg(priv->dev,
 		        "fsl_elbc_cmdfunc: NAND_CMD_READ0, page_addr:"
 		        " 0x%x, column: 0x%x.\n", page_addr, column);
 
@@ -309,8 +309,8 @@  static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
 		out_be32(&lbc->fbcr, 0); /* read entire page to enable ECC */
 		set_addr(mtd, 0, page_addr, 0);
 
-		ctrl->read_bytes = mtd->writesize + mtd->oobsize;
-		ctrl->index += column;
+		elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize;
+		elbc_fcm_ctrl->index += column;
 
 		fsl_elbc_do_read(chip, 0);
 		fsl_elbc_run_command(mtd);
@@ -318,14 +318,14 @@  static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
 
 	/* READOOB reads only the OOB because no ECC is performed. */
 	case NAND_CMD_READOOB:
-		dev_vdbg(ctrl->dev,
+		dev_vdbg(priv->dev,
 		         "fsl_elbc_cmdfunc: NAND_CMD_READOOB, page_addr:"
 			 " 0x%x, column: 0x%x.\n", page_addr, column);
 
 		out_be32(&lbc->fbcr, mtd->oobsize - column);
 		set_addr(mtd, column, page_addr, 1);
 
-		ctrl->read_bytes = mtd->writesize + mtd->oobsize;
+		elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize;
 
 		fsl_elbc_do_read(chip, 1);
 		fsl_elbc_run_command(mtd);
@@ -333,7 +333,7 @@  static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
 
 	/* READID must read all 5 possible bytes while CEB is active */
 	case NAND_CMD_READID:
-		dev_vdbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_READID.\n");
+		dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_READID.\n");
 
 		out_be32(&lbc->fir, (FIR_OP_CM0 << FIR_OP0_SHIFT) |
 		                    (FIR_OP_UA  << FIR_OP1_SHIFT) |
@@ -341,9 +341,9 @@  static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
 		out_be32(&lbc->fcr, NAND_CMD_READID << FCR_CMD0_SHIFT);
 		/* 5 bytes for manuf, device and exts */
 		out_be32(&lbc->fbcr, 5);
-		ctrl->read_bytes = 5;
-		ctrl->use_mdr = 1;
-		ctrl->mdr = 0;
+		elbc_fcm_ctrl->read_bytes = 5;
+		elbc_fcm_ctrl->use_mdr = 1;
+		elbc_fcm_ctrl->mdr = 0;
 
 		set_addr(mtd, 0, 0, 0);
 		fsl_elbc_run_command(mtd);
@@ -351,7 +351,7 @@  static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
 
 	/* ERASE1 stores the block and page address */
 	case NAND_CMD_ERASE1:
-		dev_vdbg(ctrl->dev,
+		dev_vdbg(priv->dev,
 		         "fsl_elbc_cmdfunc: NAND_CMD_ERASE1, "
 		         "page_addr: 0x%x.\n", page_addr);
 		set_addr(mtd, 0, page_addr, 0);
@@ -359,7 +359,7 @@  static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
 
 	/* ERASE2 uses the block and page address from ERASE1 */
 	case NAND_CMD_ERASE2:
-		dev_vdbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n");
+		dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n");
 
 		out_be32(&lbc->fir,
 		         (FIR_OP_CM0 << FIR_OP0_SHIFT) |
@@ -374,8 +374,8 @@  static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
 		         (NAND_CMD_ERASE2 << FCR_CMD2_SHIFT));
 
 		out_be32(&lbc->fbcr, 0);
-		ctrl->read_bytes = 0;
-		ctrl->use_mdr = 1;
+		elbc_fcm_ctrl->read_bytes = 0;
+		elbc_fcm_ctrl->use_mdr = 1;
 
 		fsl_elbc_run_command(mtd);
 		return;
@@ -383,14 +383,12 @@  static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
 	/* SEQIN sets up the addr buffer and all registers except the length */
 	case NAND_CMD_SEQIN: {
 		__be32 fcr;
-		dev_vdbg(ctrl->dev,
-		         "fsl_elbc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, "
+		dev_vdbg(priv->dev,
+			 "fsl_elbc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, "
 		         "page_addr: 0x%x, column: 0x%x.\n",
 		         page_addr, column);
 
-		ctrl->column = column;
-		ctrl->oob = 0;
-		ctrl->use_mdr = 1;
+		elbc_fcm_ctrl->use_mdr = 1;
 
 		fcr = (NAND_CMD_STATUS   << FCR_CMD1_SHIFT) |
 		      (NAND_CMD_SEQIN    << FCR_CMD2_SHIFT) |
@@ -420,7 +418,7 @@  static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
 				/* OOB area --> READOOB */
 				column -= mtd->writesize;
 				fcr |= NAND_CMD_READOOB << FCR_CMD0_SHIFT;
-				ctrl->oob = 1;
+				elbc_fcm_ctrl->oob = 1;
 			} else {
 				WARN_ON(column != 0);
 				/* First 256 bytes --> READ0 */
@@ -429,24 +427,24 @@  static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
 		}
 
 		out_be32(&lbc->fcr, fcr);
-		set_addr(mtd, column, page_addr, ctrl->oob);
+		set_addr(mtd, column, page_addr, elbc_fcm_ctrl->oob);
 		return;
 	}
 
 	/* PAGEPROG reuses all of the setup from SEQIN and adds the length */
 	case NAND_CMD_PAGEPROG: {
 		int full_page;
-		dev_vdbg(ctrl->dev,
+		dev_vdbg(priv->dev,
 		         "fsl_elbc_cmdfunc: NAND_CMD_PAGEPROG "
-		         "writing %d bytes.\n", ctrl->index);
+			 "writing %d bytes.\n", elbc_fcm_ctrl->index);
 
 		/* if the write did not start at 0 or is not a full page
 		 * then set the exact length, otherwise use a full page
 		 * write so the HW generates the ECC.
 		 */
-		if (ctrl->oob || ctrl->column != 0 ||
-		    ctrl->index != mtd->writesize + mtd->oobsize) {
-			out_be32(&lbc->fbcr, ctrl->index);
+		if (elbc_fcm_ctrl->oob || elbc_fcm_ctrl->column != 0 ||
+		    elbc_fcm_ctrl->index != mtd->writesize + mtd->oobsize) {
+			out_be32(&lbc->fbcr, elbc_fcm_ctrl->index);
 			full_page = 0;
 		} else {
 			out_be32(&lbc->fbcr, 0);
@@ -458,21 +456,21 @@  static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
 		/* Read back the page in order to fill in the ECC for the
 		 * caller.  Is this really needed?
 		 */
-		if (full_page && ctrl->oob_poi) {
+		if (full_page && elbc_fcm_ctrl->oob_poi) {
 			out_be32(&lbc->fbcr, 3);
 			set_addr(mtd, 6, page_addr, 1);
 
-			ctrl->read_bytes = mtd->writesize + 9;
+			elbc_fcm_ctrl->read_bytes = mtd->writesize + 9;
 
 			fsl_elbc_do_read(chip, 1);
 			fsl_elbc_run_command(mtd);
 
-			memcpy_fromio(ctrl->oob_poi + 6,
-			              &ctrl->addr[ctrl->index], 3);
-			ctrl->index += 3;
+			memcpy_fromio(elbc_fcm_ctrl->oob_poi + 6,
+				&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], 3);
+			elbc_fcm_ctrl->index += 3;
 		}
 
-		ctrl->oob_poi = NULL;
+		elbc_fcm_ctrl->oob_poi = NULL;
 		return;
 	}
 
@@ -485,26 +483,26 @@  static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
 		out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT);
 		out_be32(&lbc->fbcr, 1);
 		set_addr(mtd, 0, 0, 0);
-		ctrl->read_bytes = 1;
+		elbc_fcm_ctrl->read_bytes = 1;
 
 		fsl_elbc_run_command(mtd);
 
 		/* The chip always seems to report that it is
 		 * write-protected, even when it is not.
 		 */
-		setbits8(ctrl->addr, NAND_STATUS_WP);
+		setbits8(elbc_fcm_ctrl->addr, NAND_STATUS_WP);
 		return;
 
 	/* RESET without waiting for the ready line */
 	case NAND_CMD_RESET:
-		dev_dbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_RESET.\n");
+		dev_dbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_RESET.\n");
 		out_be32(&lbc->fir, FIR_OP_CM0 << FIR_OP0_SHIFT);
 		out_be32(&lbc->fcr, NAND_CMD_RESET << FCR_CMD0_SHIFT);
 		fsl_elbc_run_command(mtd);
 		return;
 
 	default:
-		dev_err(ctrl->dev,
+		dev_err(priv->dev,
 		        "fsl_elbc_cmdfunc: error, unsupported command 0x%x.\n",
 		        command);
 	}
@@ -524,24 +522,24 @@  static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
 {
 	struct nand_chip *chip = mtd->priv;
 	struct fsl_elbc_mtd *priv = chip->priv;
-	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
 	unsigned int bufsize = mtd->writesize + mtd->oobsize;
 
 	if (len <= 0) {
-		dev_err(ctrl->dev, "write_buf of %d bytes", len);
-		ctrl->status = 0;
+		dev_err(priv->dev, "write_buf of %d bytes", len);
+		elbc_fcm_ctrl->status = 0;
 		return;
 	}
 
-	if ((unsigned int)len > bufsize - ctrl->index) {
-		dev_err(ctrl->dev,
+	if ((unsigned int)len > bufsize - elbc_fcm_ctrl->index) {
+		dev_err(priv->dev,
 		        "write_buf beyond end of buffer "
 		        "(%d requested, %u available)\n",
-		        len, bufsize - ctrl->index);
-		len = bufsize - ctrl->index;
+			len, bufsize - elbc_fcm_ctrl->index);
+		len = bufsize - elbc_fcm_ctrl->index;
 	}
 
-	memcpy_toio(&ctrl->addr[ctrl->index], buf, len);
+	memcpy_toio(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], buf, len);
 	/*
 	 * This is workaround for the weird elbc hangs during nand write,
 	 * Scott Wood says: "...perhaps difference in how long it takes a
@@ -549,9 +547,9 @@  static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
 	 * is causing problems, and sync isn't helping for some reason."
 	 * Reading back the last byte helps though.
 	 */
-	in_8(&ctrl->addr[ctrl->index] + len - 1);
+	in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index] + len - 1);
 
-	ctrl->index += len;
+	elbc_fcm_ctrl->index += len;
 }
 
 /*
@@ -562,13 +560,13 @@  static u8 fsl_elbc_read_byte(struct mtd_info *mtd)
 {
 	struct nand_chip *chip = mtd->priv;
 	struct fsl_elbc_mtd *priv = chip->priv;
-	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
 
 	/* If there are still bytes in the FCM, then use the next byte. */
-	if (ctrl->index < ctrl->read_bytes)
-		return in_8(&ctrl->addr[ctrl->index++]);
+	if (elbc_fcm_ctrl->index < elbc_fcm_ctrl->read_bytes)
+		return in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index++]);
 
-	dev_err(ctrl->dev, "read_byte beyond end of buffer\n");
+	dev_err(priv->dev, "read_byte beyond end of buffer\n");
 	return ERR_BYTE;
 }
 
@@ -579,18 +577,19 @@  static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
 {
 	struct nand_chip *chip = mtd->priv;
 	struct fsl_elbc_mtd *priv = chip->priv;
-	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
 	int avail;
 
 	if (len < 0)
 		return;
 
-	avail = min((unsigned int)len, ctrl->read_bytes - ctrl->index);
-	memcpy_fromio(buf, &ctrl->addr[ctrl->index], avail);
-	ctrl->index += avail;
+	avail = min((unsigned int)len,
+			elbc_fcm_ctrl->read_bytes - elbc_fcm_ctrl->index);
+	memcpy_fromio(buf, &elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], avail);
+	elbc_fcm_ctrl->index += avail;
 
 	if (len > avail)
-		dev_err(ctrl->dev,
+		dev_err(priv->dev,
 		        "read_buf beyond end of buffer "
 		        "(%d requested, %d available)\n",
 		        len, avail);
@@ -603,30 +602,32 @@  static int fsl_elbc_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
 {
 	struct nand_chip *chip = mtd->priv;
 	struct fsl_elbc_mtd *priv = chip->priv;
-	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
 	int i;
 
 	if (len < 0) {
-		dev_err(ctrl->dev, "write_buf of %d bytes", len);
+		dev_err(priv->dev, "write_buf of %d bytes", len);
 		return -EINVAL;
 	}
 
-	if ((unsigned int)len > ctrl->read_bytes - ctrl->index) {
-		dev_err(ctrl->dev,
-		        "verify_buf beyond end of buffer "
-		        "(%d requested, %u available)\n",
-		        len, ctrl->read_bytes - ctrl->index);
+	if ((unsigned int)len >
+			elbc_fcm_ctrl->read_bytes - elbc_fcm_ctrl->index) {
+		dev_err(priv->dev,
+			"verify_buf beyond end of buffer "
+			"(%d requested, %u available)\n",
+			len, elbc_fcm_ctrl->read_bytes - elbc_fcm_ctrl->index);
 
-		ctrl->index = ctrl->read_bytes;
+		elbc_fcm_ctrl->index = elbc_fcm_ctrl->read_bytes;
 		return -EINVAL;
 	}
 
 	for (i = 0; i < len; i++)
-		if (in_8(&ctrl->addr[ctrl->index + i]) != buf[i])
+		if (in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index + i])
+				!= buf[i])
 			break;
 
-	ctrl->index += len;
-	return i == len && ctrl->status == LTESR_CC ? 0 : -EIO;
+	elbc_fcm_ctrl->index += len;
+	return i == len && elbc_fcm_ctrl->status == LTESR_CC ? 0 : -EIO;
 }
 
 /* This function is called after Program and Erase Operations to
@@ -635,22 +636,22 @@  static int fsl_elbc_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
 static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip)
 {
 	struct fsl_elbc_mtd *priv = chip->priv;
-	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
 
-	if (ctrl->status != LTESR_CC)
+	if (elbc_fcm_ctrl->status != LTESR_CC)
 		return NAND_STATUS_FAIL;
 
 	/* The chip always seems to report that it is
 	 * write-protected, even when it is not.
 	 */
-	return (ctrl->mdr & 0xff) | NAND_STATUS_WP;
+	return (elbc_fcm_ctrl->mdr & 0xff) | NAND_STATUS_WP;
 }
 
 static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
 {
 	struct nand_chip *chip = mtd->priv;
 	struct fsl_elbc_mtd *priv = chip->priv;
-	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+	struct fsl_lbc_ctrl *ctrl = priv->ctrl;
 	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
 	unsigned int al;
 
@@ -665,41 +666,41 @@  static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
 	priv->fmr |= (12 << FMR_CWTO_SHIFT) |  /* Timeout > 12 ms */
 	             (al << FMR_AL_SHIFT);
 
-	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->numchips = %d\n",
+	dev_dbg(priv->dev, "fsl_elbc_init: nand->numchips = %d\n",
 	        chip->numchips);
-	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->chipsize = %lld\n",
+	dev_dbg(priv->dev, "fsl_elbc_init: nand->chipsize = %lld\n",
 	        chip->chipsize);
-	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->pagemask = %8x\n",
+	dev_dbg(priv->dev, "fsl_elbc_init: nand->pagemask = %8x\n",
 	        chip->pagemask);
-	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->chip_delay = %d\n",
+	dev_dbg(priv->dev, "fsl_elbc_init: nand->chip_delay = %d\n",
 	        chip->chip_delay);
-	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->badblockpos = %d\n",
+	dev_dbg(priv->dev, "fsl_elbc_init: nand->badblockpos = %d\n",
 	        chip->badblockpos);
-	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->chip_shift = %d\n",
+	dev_dbg(priv->dev, "fsl_elbc_init: nand->chip_shift = %d\n",
 	        chip->chip_shift);
-	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->page_shift = %d\n",
+	dev_dbg(priv->dev, "fsl_elbc_init: nand->page_shift = %d\n",
 	        chip->page_shift);
-	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->phys_erase_shift = %d\n",
+	dev_dbg(priv->dev, "fsl_elbc_init: nand->phys_erase_shift = %d\n",
 	        chip->phys_erase_shift);
-	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecclayout = %p\n",
+	dev_dbg(priv->dev, "fsl_elbc_init: nand->ecclayout = %p\n",
 	        chip->ecclayout);
-	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.mode = %d\n",
+	dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.mode = %d\n",
 	        chip->ecc.mode);
-	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.steps = %d\n",
+	dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.steps = %d\n",
 	        chip->ecc.steps);
-	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.bytes = %d\n",
+	dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.bytes = %d\n",
 	        chip->ecc.bytes);
-	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.total = %d\n",
+	dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.total = %d\n",
 	        chip->ecc.total);
-	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.layout = %p\n",
+	dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.layout = %p\n",
 	        chip->ecc.layout);
-	dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->flags = %08x\n", mtd->flags);
-	dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->size = %lld\n", mtd->size);
-	dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->erasesize = %d\n",
+	dev_dbg(priv->dev, "fsl_elbc_init: mtd->flags = %08x\n", mtd->flags);
+	dev_dbg(priv->dev, "fsl_elbc_init: mtd->size = %lld\n", mtd->size);
+	dev_dbg(priv->dev, "fsl_elbc_init: mtd->erasesize = %d\n",
 	        mtd->erasesize);
-	dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->writesize = %d\n",
+	dev_dbg(priv->dev, "fsl_elbc_init: mtd->writesize = %d\n",
 	        mtd->writesize);
-	dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->oobsize = %d\n",
+	dev_dbg(priv->dev, "fsl_elbc_init: mtd->oobsize = %d\n",
 	        mtd->oobsize);
 
 	/* adjust Option Register and ECC to match Flash page size */
@@ -719,7 +720,7 @@  static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
 			chip->badblock_pattern = &largepage_memorybased;
 		}
 	} else {
-		dev_err(ctrl->dev,
+		dev_err(priv->dev,
 		        "fsl_elbc_init: page size %d is not supported\n",
 		        mtd->writesize);
 		return -1;
@@ -750,18 +751,19 @@  static void fsl_elbc_write_page(struct mtd_info *mtd,
                                 const uint8_t *buf)
 {
 	struct fsl_elbc_mtd *priv = chip->priv;
-	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
 
 	fsl_elbc_write_buf(mtd, buf, mtd->writesize);
 	fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
 
-	ctrl->oob_poi = chip->oob_poi;
+	elbc_fcm_ctrl->oob_poi = chip->oob_poi;
 }
 
 static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
 {
-	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+	struct fsl_lbc_ctrl *ctrl = priv->ctrl;
 	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
+	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
 	struct nand_chip *chip = &priv->chip;
 
 	dev_dbg(priv->dev, "eLBC Set Information for bank %d\n", priv->bank);
@@ -790,7 +792,7 @@  static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
 	chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR |
 			NAND_USE_FLASH_BBT;
 
-	chip->controller = &ctrl->controller;
+	chip->controller = &elbc_fcm_ctrl->controller;
 	chip->priv = priv;
 
 	chip->ecc.read_page = fsl_elbc_read_page;
@@ -815,8 +817,7 @@  static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
 
 static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv)
 {
-	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
-
+	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
 	nand_release(&priv->mtd);
 
 	kfree(priv->mtd.name);
@@ -824,18 +825,21 @@  static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv)
 	if (priv->vbase)
 		iounmap(priv->vbase);
 
-	ctrl->chips[priv->bank] = NULL;
+	elbc_fcm_ctrl->chips[priv->bank] = NULL;
 	kfree(priv);
-
+	kfree(elbc_fcm_ctrl);
 	return 0;
 }
 
-static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl,
-					 struct device_node *node)
+static DEFINE_MUTEX(fsl_elbc_nand_mutex);
+
+static int __devinit fsl_elbc_nand_probe(struct platform_device *pdev)
 {
-	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
+	struct fsl_lbc_regs __iomem *lbc;
 	struct fsl_elbc_mtd *priv;
 	struct resource res;
+	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl;
+
 #ifdef CONFIG_MTD_PARTITIONS
 	static const char *part_probe_types[]
 		= { "cmdlinepart", "RedBoot", NULL };
@@ -843,11 +847,18 @@  static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl,
 #endif
 	int ret;
 	int bank;
+	struct device *dev;
+	struct device_node *node = pdev->dev.of_node;
+
+	if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
+		return -ENODEV;
+	lbc = fsl_lbc_ctrl_dev->regs;
+	dev = fsl_lbc_ctrl_dev->dev;
 
 	/* get, allocate and map the memory resource */
 	ret = of_address_to_resource(node, 0, &res);
 	if (ret) {
-		dev_err(ctrl->dev, "failed to get resource\n");
+		dev_err(dev, "failed to get resource\n");
 		return ret;
 	}
 
@@ -861,7 +872,7 @@  static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl,
 			break;
 
 	if (bank >= MAX_BANKS) {
-		dev_err(ctrl->dev, "address did not match any chip selects\n");
+		dev_err(dev, "address did not match any chip selects\n");
 		return -ENODEV;
 	}
 
@@ -869,14 +880,33 @@  static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl,
 	if (!priv)
 		return -ENOMEM;
 
-	ctrl->chips[bank] = priv;
+	mutex_lock(&fsl_elbc_nand_mutex);
+	if (!fsl_lbc_ctrl_dev->nand) {
+		elbc_fcm_ctrl = kzalloc(sizeof(*elbc_fcm_ctrl), GFP_KERNEL);
+		if (!elbc_fcm_ctrl) {
+			dev_err(dev, "failed to allocate memory\n");
+			mutex_unlock(&fsl_elbc_nand_mutex);
+			ret = -ENOMEM;
+			goto err;
+		}
+		elbc_fcm_ctrl->counter++;
+
+		spin_lock_init(&elbc_fcm_ctrl->controller.lock);
+		init_waitqueue_head(&elbc_fcm_ctrl->controller.wq);
+		fsl_lbc_ctrl_dev->nand = elbc_fcm_ctrl;
+	} else {
+		elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand;
+	}
+	mutex_unlock(&fsl_elbc_nand_mutex);
+
+	elbc_fcm_ctrl->chips[bank] = priv;
 	priv->bank = bank;
-	priv->ctrl = ctrl;
-	priv->dev = ctrl->dev;
+	priv->ctrl = fsl_lbc_ctrl_dev;
+	priv->dev = dev;
 
 	priv->vbase = ioremap(res.start, resource_size(&res));
 	if (!priv->vbase) {
-		dev_err(ctrl->dev, "failed to map chip region\n");
+		dev_err(dev, "failed to map chip region\n");
 		ret = -ENOMEM;
 		goto err;
 	}
@@ -933,171 +963,53 @@  err:
 	return ret;
 }
 
-static int __devinit fsl_elbc_ctrl_init(struct fsl_elbc_ctrl *ctrl)
+static int fsl_elbc_nand_remove(struct platform_device *pdev)
 {
-	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
-
-	/*
-	 * NAND transactions can tie up the bus for a long time, so set the
-	 * bus timeout to max by clearing LBCR[BMT] (highest base counter
-	 * value) and setting LBCR[BMTPS] to the highest prescaler value.
-	 */
-	clrsetbits_be32(&lbc->lbcr, LBCR_BMT, 15);
-
-	/* clear event registers */
-	setbits32(&lbc->ltesr, LTESR_NAND_MASK);
-	out_be32(&lbc->lteatr, 0);
-
-	/* Enable interrupts for any detected events */
-	out_be32(&lbc->lteir, LTESR_NAND_MASK);
-
-	ctrl->read_bytes = 0;
-	ctrl->index = 0;
-	ctrl->addr = NULL;
-
-	return 0;
-}
-
-static int fsl_elbc_ctrl_remove(struct platform_device *ofdev)
-{
-	struct fsl_elbc_ctrl *ctrl = dev_get_drvdata(&ofdev->dev);
 	int i;
-
+	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand;
 	for (i = 0; i < MAX_BANKS; i++)
-		if (ctrl->chips[i])
-			fsl_elbc_chip_remove(ctrl->chips[i]);
-
-	if (ctrl->irq)
-		free_irq(ctrl->irq, ctrl);
-
-	if (ctrl->regs)
-		iounmap(ctrl->regs);
-
-	dev_set_drvdata(&ofdev->dev, NULL);
-	kfree(ctrl);
-	return 0;
-}
-
-/* NOTE: This interrupt is also used to report other localbus events,
- * such as transaction errors on other chipselects.  If we want to
- * capture those, we'll need to move the IRQ code into a shared
- * LBC driver.
- */
-
-static irqreturn_t fsl_elbc_ctrl_irq(int irqno, void *data)
-{
-	struct fsl_elbc_ctrl *ctrl = data;
-	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
-	__be32 status = in_be32(&lbc->ltesr) & LTESR_NAND_MASK;
-
-	if (status) {
-		out_be32(&lbc->ltesr, status);
-		out_be32(&lbc->lteatr, 0);
-
-		ctrl->irq_status = status;
-		smp_wmb();
-		wake_up(&ctrl->irq_wait);
-
-		return IRQ_HANDLED;
+		if (elbc_fcm_ctrl->chips[i])
+			fsl_elbc_chip_remove(elbc_fcm_ctrl->chips[i]);
+
+	mutex_lock(&fsl_elbc_nand_mutex);
+	elbc_fcm_ctrl->counter--;
+	if (!elbc_fcm_ctrl->counter) {
+		fsl_lbc_ctrl_dev->nand = NULL;
+		kfree(elbc_fcm_ctrl);
 	}
-
-	return IRQ_NONE;
-}
-
-/* fsl_elbc_ctrl_probe
- *
- * called by device layer when it finds a device matching
- * one our driver can handled. This code allocates all of
- * the resources needed for the controller only.  The
- * resources for the NAND banks themselves are allocated
- * in the chip probe function.
-*/
-
-static int __devinit fsl_elbc_ctrl_probe(struct platform_device *ofdev,
-                                         const struct of_device_id *match)
-{
-	struct device_node *child;
-	struct fsl_elbc_ctrl *ctrl;
-	int ret;
-
-	ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
-	if (!ctrl)
-		return -ENOMEM;
-
-	dev_set_drvdata(&ofdev->dev, ctrl);
-
-	spin_lock_init(&ctrl->controller.lock);
-	init_waitqueue_head(&ctrl->controller.wq);
-	init_waitqueue_head(&ctrl->irq_wait);
-
-	ctrl->regs = of_iomap(ofdev->dev.of_node, 0);
-	if (!ctrl->regs) {
-		dev_err(&ofdev->dev, "failed to get memory region\n");
-		ret = -ENODEV;
-		goto err;
-	}
-
-	ctrl->irq = of_irq_to_resource(ofdev->dev.of_node, 0, NULL);
-	if (ctrl->irq == NO_IRQ) {
-		dev_err(&ofdev->dev, "failed to get irq resource\n");
-		ret = -ENODEV;
-		goto err;
-	}
-
-	ctrl->dev = &ofdev->dev;
-
-	ret = fsl_elbc_ctrl_init(ctrl);
-	if (ret < 0)
-		goto err;
-
-	ret = request_irq(ctrl->irq, fsl_elbc_ctrl_irq, 0, "fsl-elbc", ctrl);
-	if (ret != 0) {
-		dev_err(&ofdev->dev, "failed to install irq (%d)\n",
-		        ctrl->irq);
-		ret = ctrl->irq;
-		goto err;
-	}
-
-	for_each_child_of_node(ofdev->dev.of_node, child)
-		if (of_device_is_compatible(child, "fsl,elbc-fcm-nand"))
-			fsl_elbc_chip_probe(ctrl, child);
+	mutex_unlock(&fsl_elbc_nand_mutex);
 
 	return 0;
 
-err:
-	fsl_elbc_ctrl_remove(ofdev);
-	return ret;
 }
 
-static const struct of_device_id fsl_elbc_match[] = {
-	{
-		.compatible = "fsl,elbc",
-	},
+static const struct of_device_id fsl_elbc_nand_match[] = {
+	{ .compatible = "fsl,elbc-fcm-nand", },
 	{}
 };
 
-static struct of_platform_driver fsl_elbc_ctrl_driver = {
+static struct platform_driver fsl_elbc_nand_driver = {
 	.driver = {
-		.name = "fsl-elbc",
+		.name = "fsl,elbc-fcm-nand",
 		.owner = THIS_MODULE,
-		.of_match_table = fsl_elbc_match,
+		.of_match_table = fsl_elbc_nand_match,
 	},
-	.probe = fsl_elbc_ctrl_probe,
-	.remove = fsl_elbc_ctrl_remove,
+	.probe = fsl_elbc_nand_probe,
+	.remove = fsl_elbc_nand_remove,
 };
 
-static int __init fsl_elbc_init(void)
+static int __init fsl_elbc_nand_init(void)
 {
-	return of_register_platform_driver(&fsl_elbc_ctrl_driver);
+	return platform_driver_register(&fsl_elbc_nand_driver);
 }
 
-static void __exit fsl_elbc_exit(void)
+static void __exit fsl_elbc_nand_exit(void)
 {
-	of_unregister_platform_driver(&fsl_elbc_ctrl_driver);
+	platform_driver_unregister(&fsl_elbc_nand_driver);
 }
 
-module_init(fsl_elbc_init);
-module_exit(fsl_elbc_exit);
+module_init(fsl_elbc_nand_init);
+module_exit(fsl_elbc_nand_exit);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Freescale");