Felipe,
Another minor comment below, in addition to Dave's comments:
Felipe Balbi <[EMAIL PROTECTED]> writes:
> From: Felipe Balbi <[EMAIL PROTECTED]>
>
> Introduce a structure to hold device data. Already
> changes read/write functions to use __raw_read/write.
>
> Signed-off-by: Felipe Balbi <[EMAIL PROTECTED]>
> ---
> drivers/mtd/nand/davinci_nand.c | 311
> ++++++++++++++++++++++++---------------
> 1 files changed, 190 insertions(+), 121 deletions(-)
>
> diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c
> index b6e1384..e296ce3 100644
> --- a/drivers/mtd/nand/davinci_nand.c
> +++ b/drivers/mtd/nand/davinci_nand.c
> @@ -45,6 +45,7 @@
> #include <linux/mtd/mtd.h>
> #include <linux/mtd/nand.h>
> #include <linux/mtd/partitions.h>
> +#include <linux/spinlock.h>
>
> #include <mach/hardware.h>
> #include <mach/nand.h>
> @@ -60,14 +61,20 @@
>
> #define DRIVER_NAME "davinci_nand"
>
> -static struct clk *nand_clock;
> -static void __iomem *nand_vaddr;
> -static void __iomem *emif_base;
> +struct davinci_nand_info {
> + /* device lock */
> + spinlock_t lock;
> + struct nand_chip chip;
> + struct mtd_info mtd;
>
> -/*
> - * MTD structure for DaVinici board
> - */
> -static struct mtd_info *nand_davinci_mtd;
> + struct device *dev;
> + struct clk *clk;
> +
> + void __iomem *base;
> + void __iomem *vaddr;
> +};
> +
> +#define to_davinci_nand(m) container_of(m, struct davinci_nand_info, mtd)
>
> #ifdef CONFIG_MTD_PARTITIONS
> const char *part_probes[] = { "cmdlinepart", NULL };
> @@ -91,18 +98,24 @@ static struct nand_bbt_descr davinci_memorybased_large = {
> .pattern = scan_ff_pattern
> };
>
> -inline unsigned int davinci_nand_readl(int offset)
> +/* Caller should take care of locking */
> +static inline unsigned int davinci_nand_readl(struct davinci_nand_info *info,
> + int offset)
> {
> - return davinci_readl(emif_base + offset);
> + return __raw_readl(info->base + offset);
> }
>
> -inline void davinci_nand_writel(unsigned long value, int offset)
> +/* Caller should take care of locking */
> +static inline void davinci_nand_writel(struct davinci_nand_info *info,
> + int offset, unsigned long value)
> {
> - davinci_writel(value, emif_base + offset);
> + __raw_writel(value, info->base + offset);
> }
>
> /*
> * Hardware specific access to control-lines
> + *
> + * REVISIT avoid casting addresses
> */
> static void nand_davinci_hwcontrol(struct mtd_info *mtd, int cmd,
> unsigned int ctrl)
> @@ -134,15 +147,23 @@ static void nand_davinci_select_chip(struct mtd_info
> *mtd, int chip)
> #ifdef CONFIG_NAND_FLASH_HW_ECC
> static void nand_davinci_enable_hwecc(struct mtd_info *mtd, int mode)
> {
> + struct davinci_nand_info *info;
> + unsigned long flags;
> u32 retval;
>
> + info = to_davinci_nand(mtd);
> +
> + spin_lock_irqsave(&info->lock, flags);
> +
> /* Reset ECC hardware */
> - retval = davinci_nand_readl(NANDF1ECC_OFFSET);
> + retval = davinci_nand_readl(info, NANDF1ECC_OFFSET);
>
> /* Restart ECC hardware */
> - retval = davinci_nand_readl(NANDFCR_OFFSET);
> + retval = davinci_nand_readl(info, NANDFCR_OFFSET);
> retval |= (1 << 8);
> - davinci_nand_writel(retval, NANDFCR_OFFSET);
> + davinci_nand_writel(info, NANDFCR_OFFSET, retval);
> +
> + spin_unlock_irqrestore(&info->lock, flags);
> }
>
> /*
> @@ -150,8 +171,18 @@ static void nand_davinci_enable_hwecc(struct mtd_info
> *mtd, int mode)
> */
> static u32 nand_davinci_readecc(struct mtd_info *mtd)
> {
> + struct davinci_nand_info *info;
> + unsigned long flags;
> + unsigned int ecc;
> +
> + info = to_davinci_nand(mtd);
> +
> /* Read register ECC and clear it */
> - return davinci_nand_readl(NANDF1ECC_OFFSET);
> + spin_lock_irqsave(&info->lock, flags);
> + ecc = davinci_nand_readl(info, NANDF1ECC_OFFSET);
> + spin_unlock_irqrestore(&info->lock, flags);
> +
> + return ecc;
> }
>
> /*
> @@ -248,6 +279,7 @@ static int nand_davinci_memory_bbt(struct mtd_info *mtd,
> int blocksize = 1 << chip->bbt_erase_shift;
> uint8_t *buf = chip->buffers->databuf;
> int len = bd->options & NAND_BBT_SCAN2NDPAGE ? 2 : 1;
> + struct davinci_nand_info *info = to_davinci_nand(mtd);
>
> /* -numblocks- is 2 times the actual number of eraseblocks */
> numblocks = mtd->size >> (chip->bbt_erase_shift - 1);
> @@ -273,7 +305,7 @@ static int nand_davinci_memory_bbt(struct mtd_info *mtd,
> table */
> chip->bbt[i >> 3] |= 0x03 << (i & 0x6);
>
> - printk(KERN_WARNING "Bad eraseblock %d at " \
> + dev_dbg(info->dev, "Bad eraseblock %d at " \
> "0x%08x\n", i >> 1,
> (unsigned int)from);
>
> @@ -287,7 +319,7 @@ static int nand_davinci_memory_bbt(struct mtd_info *mtd,
> from += blocksize;
> }
>
> - printk(KERN_NOTICE "Bad block scan: %d out of %d blocks are bad.\n",
> + dev_dbg(info->dev, "Bad block scan: %d out of %d blocks are bad.\n",
> mtd->ecc_stats.badblocks, numblocks>>1);
>
> return 0;
> @@ -303,6 +335,7 @@ static int nand_davinci_scan_bbt(struct mtd_info *mtd)
> struct nand_chip *chip = mtd->priv;
> struct nand_bbt_descr *bd;
> int len, ret = 0;
> + struct davinci_nand_info *info = to_davinci_nand(mtd);
>
> chip->bbt_td = NULL;
> chip->bbt_md = NULL;
> @@ -322,14 +355,14 @@ static int nand_davinci_scan_bbt(struct mtd_info *mtd)
> table */
> chip->bbt = kzalloc(len, GFP_KERNEL);
> if (!chip->bbt) {
> - printk(KERN_ERR "nand_davinci_scan_bbt: Out of memory\n");
> + dev_err(info->dev, "nand_davinci_scan_bbt: Out of memory\n");
> return -ENOMEM;
> }
>
> /* Now try to fill in the BBT */
> ret = nand_davinci_memory_bbt(mtd, bd);
> if (ret) {
> - printk(KERN_ERR "nand_davinci_scan_bbt: "
> + dev_err(info->dev, "nand_davinci_scan_bbt: "
> "Can't scan flash and build the RAM-based BBT\n");
>
> kfree(chip->bbt);
> @@ -360,7 +393,17 @@ static void nand_davinci_read_buf(struct mtd_info *mtd,
> uint8_t *buf, int len)
> */
> static int nand_davinci_dev_ready(struct mtd_info *mtd)
> {
> - return davinci_nand_readl(NANDFSR_OFFSET) & NAND_BUSY_FLAG;
> + struct davinci_nand_info *info;
> + unsigned long flags;
> + unsigned int ready;
> +
> + info = to_davinci_nand(mtd);
> +
> + spin_lock_irqsave(&info->lock, flags);
> + ready = davinci_nand_readl(info, NANDFSR_OFFSET) & NAND_BUSY_FLAG;
> + spin_unlock_irqrestore(&info->lock, flags);
> +
> + return ready;
> }
>
> static void nand_davinci_set_eccsize(struct nand_chip *chip)
> @@ -408,10 +451,13 @@ static void nand_davinci_set_eccbytes(struct nand_chip
> *chip)
> #endif
> }
>
> -static void __devinit nand_davinci_flash_init(void)
> +static void __devinit nand_davinci_flash_init(struct davinci_nand_info *info)
> {
> + unsigned long flags;
> u32 regval, tmp;
>
> + spin_lock_irqsave(&info->lock, flags);
> +
> /* Check for correct pin mux, reconfigure if necessary */
> tmp = davinci_readl(DAVINCI_SYSTEM_MODULE_BASE + PINMUX0);
>
> @@ -431,14 +477,14 @@ static void __devinit nand_davinci_flash_init(void)
>
> regval = davinci_readl(DAVINCI_SYSTEM_MODULE_BASE + PINMUX0);
>
> - printk(KERN_WARNING "Warning: MUX config for NAND: Set " \
> + dev_dbg(info->dev, "Warning: MUX config for NAND: Set " \
> "PINMUX0 reg to 0x%08x, was 0x%08x, should be done " \
> "by bootloader.\n", regval, tmp);
> }
>
> - regval = davinci_nand_readl(AWCCR_OFFSET);
> + regval = davinci_nand_readl(info, AWCCR_OFFSET);
> regval |= 0x10000000;
> - davinci_nand_writel(regval, AWCCR_OFFSET);
> + davinci_nand_writel(info, AWCCR_OFFSET, regval);
>
> /*------------------------------------------------------------------*
> * NAND FLASH CHIP TIMEOUT @ 459 MHz *
> @@ -459,163 +505,185 @@ static void __devinit nand_davinci_flash_init(void)
> | (3 << 2) /* turnAround ?? ns */
> | (0 << 0) /* asyncSize 8-bit bus */
> ;
> - tmp = davinci_nand_readl(A1CR_OFFSET);
> + tmp = davinci_nand_readl(info, A1CR_OFFSET);
> if (tmp != regval) {
> - printk(KERN_WARNING "Warning: NAND config: Set A1CR " \
> + dev_dbg(info->dev, "Warning: NAND config: Set A1CR " \
> "reg to 0x%08x, was 0x%08x, should be done by " \
> "bootloader.\n", regval, tmp);
> - davinci_nand_writel(regval, A1CR_OFFSET); /* 0x0434018C */
> + davinci_nand_writel(info, A1CR_OFFSET, regval); /* 0x0434018C */
> }
>
> - davinci_nand_writel(0x00000101, NANDFCR_OFFSET);
> + davinci_nand_writel(info, NANDFCR_OFFSET, 0x00000101);
> +
> + spin_unlock_irqrestore(&info->lock, flags);
> }
>
> -/*
> - * Main initialization routine
> - */
> -int __devinit nand_davinci_probe(struct platform_device *pdev)
> +static int __init nand_davinci_probe(struct platform_device *pdev)
> {
> struct flash_platform_data *pdata = pdev->dev.platform_data;
> - struct resource *res = pdev->resource;
> - struct resource *res2 = platform_get_resource(pdev,
> - IORESOURCE_MEM, 1);
> - struct nand_chip *chip;
> - struct device *dev = NULL;
> - u32 nand_rev_code;
> + struct davinci_nand_info *info;
> + struct resource *res1;
> + struct resource *res2;
> #ifdef CONFIG_MTD_CMDLINE_PARTS
> + struct mtd_partition *mtd_parts = 0;
> char *master_name;
> int mtd_parts_nb = 0;
> - struct mtd_partition *mtd_parts = 0;
> #endif
> + void __iomem *vaddr;
> + void __iomem *base;
>
> - nand_clock = clk_get(dev, "AEMIFCLK");
> - if (IS_ERR(nand_clock)) {
> - printk(KERN_ERR "Error %ld getting AEMIFCLK clock?\n",
> - PTR_ERR(nand_clock));
> - return -1;
> + int ret;
> + u32 rev;
> +
> + if (!pdata) {
> + dev_err(&pdev->dev, "platform_data missing\n");
> + ret = -ENODEV;
> + goto err_pdata;
> }
>
> - clk_enable(nand_clock);
> + info = kzalloc(sizeof(*info), GFP_KERNEL);
> + if (!info) {
> + dev_err(&pdev->dev, "unable to allocate memory\n");
> + ret = -ENOMEM;
> + goto err_nomem;
> + }
>
> - /* Allocate memory for MTD device structure and private data */
> - nand_davinci_mtd = kmalloc(sizeof(struct mtd_info) +
> - sizeof(struct nand_chip), GFP_KERNEL);
> + spin_lock_init(&info->lock);
> + platform_set_drvdata(pdev, info);
>
> - if (!nand_davinci_mtd) {
> - printk(KERN_ERR "Unable to allocate davinci NAND MTD device " \
> - "structure.\n");
> - clk_disable(nand_clock);
> - return -ENOMEM;
> + res1 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> + if (!res1 || !res2) {
> + dev_err(&pdev->dev, "resource missing\n");
> + ret = -EINVAL;
> + goto err_res;
> }
>
> - /* Get pointer to private data */
> - chip = (struct nand_chip *) (&nand_davinci_mtd[1]);
> + vaddr = ioremap(res1->start, res1->end - res1->start);
> + base = ioremap(res2->start, res2->end - res2->start);
> + if (!vaddr || !base) {
> + dev_err(&pdev->dev, "ioremap failed\n");
> + ret = -EINVAL;
> + goto err_ioremap;
>
> - /* Initialize structures */
> - memset((char *)nand_davinci_mtd, 0, sizeof(struct mtd_info));
> - memset((char *)chip, 0, sizeof(struct nand_chip));
> -
> - /* Link the private data with the MTD structure */
> - nand_davinci_mtd->priv = chip;
> + }
>
> - nand_rev_code = davinci_nand_readl(NRCSR_OFFSET);
> + info->dev = &pdev->dev;
> + info->base = base;
> + info->vaddr = vaddr;
>
> - printk("DaVinci NAND Controller rev. %d.%d\n",
> - (nand_rev_code >> 8) & 0xff, nand_rev_code & 0xff);
> + info->mtd.priv = &info->chip;
> + info->mtd.name = dev_name(&pdev->dev);
> + info->mtd.owner = THIS_MODULE;
>
> - nand_vaddr = ioremap(res->start, res->end - res->start);
> - emif_base = ioremap(res2->start, res2->end - res2->start);
> - if (nand_vaddr == NULL || emif_base == NULL) {
> - printk(KERN_ERR "DaVinci NAND: ioremap failed.\n");
> - clk_disable(nand_clock);
> - kfree(nand_davinci_mtd);
> - return -ENOMEM;
> - }
> + info->chip.IO_ADDR_R = vaddr;
> + info->chip.IO_ADDR_W = vaddr;
> + info->chip.chip_delay = 0;
> + info->chip.select_chip = nand_davinci_select_chip;
> + info->chip.options = 0;
> + info->chip.ecc.mode = DAVINCI_NAND_ECC_MODE;
>
> - chip->IO_ADDR_R = nand_vaddr;
> - chip->IO_ADDR_W = nand_vaddr;
> - chip->chip_delay = 0;
> - chip->select_chip = nand_davinci_select_chip;
> - chip->options = 0;
> - chip->ecc.mode = DAVINCI_NAND_ECC_MODE;
> + /* Set address of hardware control function */
> + info->chip.cmd_ctrl = nand_davinci_hwcontrol;
> + info->chip.dev_ready = nand_davinci_dev_ready;
>
> - /* Set ECC size and bytes */
> - nand_davinci_set_eccsize(chip);
> - nand_davinci_set_eccbytes(chip);
> + /* Speed up the read buffer */
> + info->chip.read_buf = nand_davinci_read_buf;
>
> - /* Set address of hardware control function */
> - chip->cmd_ctrl = nand_davinci_hwcontrol;
> - chip->dev_ready = nand_davinci_dev_ready;
> + /* Speed up the creation of the bad block table */
> + info->chip.scan_bbt = nand_davinci_scan_bbt;
>
> #ifdef CONFIG_NAND_FLASH_HW_ECC
> - chip->ecc.calculate = nand_davinci_calculate_ecc;
> - chip->ecc.correct = nand_davinci_correct_data;
> - chip->ecc.hwctl = nand_davinci_enable_hwecc;
> + /* REVISIT should be using runtime check */
> + info->chip.ecc.calculate = nand_davinci_calculate_ecc;
> + info->chip.ecc.correct = nand_davinci_correct_data;
> + info->chip.ecc.hwctl = nand_davinci_enable_hwecc;
> #endif
>
> - /* Speed up the read buffer */
> - chip->read_buf = nand_davinci_read_buf;
> + info->clk = clk_get(&pdev->dev, "AEMIFCLK");
> + if (IS_ERR(info->clk)) {
> + ret = PTR_ERR(info->clk);
> + dev_dbg(&pdev->dev, "unable to get AEMIFCLK, err %d\n", ret);
> + goto err_clk;
> + }
>
> - /* Speed up the creation of the bad block table */
> - chip->scan_bbt = nand_davinci_scan_bbt;
> + ret = clk_enable(info->clk);
> + if (ret < 0) {
> + dev_dbg(&pdev->dev, "unable to enable AEMIFCLK, err %d\n", ret);
> + goto err_clk_enable;
> + }
>
> - nand_davinci_flash_init();
> + /* Set ECC size and bytes */
> + nand_davinci_set_eccsize(&info->chip);
> + nand_davinci_set_eccbytes(&info->chip);
>
> - nand_davinci_mtd->owner = THIS_MODULE;
> + nand_davinci_flash_init(info);
>
> /* Scan to find existence of the device */
> - if (nand_scan(nand_davinci_mtd, 1)) {
> - printk(KERN_ERR "Chip Select is not set for NAND\n");
> - clk_disable(nand_clock);
> - kfree(nand_davinci_mtd);
> - return -ENXIO;
> + if (nand_scan(&info->mtd, 1)) {
> + dev_err(&pdev->dev, "chip select is not set for 'NAND'\n");
> + ret = -ENXIO;
> + goto err_scan;
> }
>
> /* Register the partitions */
> - add_mtd_partitions(nand_davinci_mtd, pdata->parts, pdata->nr_parts);
> + add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts);
>
> #ifdef CONFIG_MTD_CMDLINE_PARTS
> - /* Set nand_davinci_mtd->name = 0 temporarily */
> - master_name = nand_davinci_mtd->name;
> - nand_davinci_mtd->name = (char *)0;
> + /* Set info->mtd.name = 0 temporarily */
> + master_name = info->mtd.name;
> + info->mtd.name = (char *)0;
>
> - /* nand_davinci_mtd->name == 0, means: don't bother checking
> + /* info->mtd.name == 0, means: don't bother checking
> <mtd-id> */
> - mtd_parts_nb = parse_mtd_partitions(nand_davinci_mtd, part_probes,
> + mtd_parts_nb = parse_mtd_partitions(&info->mtd, part_probes,
> &mtd_parts, 0);
>
> - /* Restore nand_davinci_mtd->name */
> - nand_davinci_mtd->name = master_name;
> + /* Restore info->mtd.name */
> + info->mtd.name = master_name;
>
> - add_mtd_partitions(nand_davinci_mtd, mtd_parts, mtd_parts_nb);
> + add_mtd_partitions(&info->mtd, mtd_parts, mtd_parts_nb);
> #endif
>
> + rev = davinci_nand_readl(info, NRCSR_OFFSET);
> + dev_info(&pdev->dev, "controller rev. %d.%d\n",
> + (rev >> 8) & 0xff, rev & 0xff);
> +
> return 0;
> +
> +err_scan:
> + clk_disable(info->clk);
> +
> +err_clk_enable:
> + clk_put(info->clk);
> +
> +err_clk:
> +err_ioremap:
> + kfree(info);
> +
> +err_nomem:
> +err_res:
> +err_pdata:
> + return ret;
> }
>
> -/*
> - * Clean up routine
> - */
> -static int nand_davinci_remove(struct platform_device *pdev)
> +static int __exit nand_davinci_remove(struct platform_device *pdev)
I believe this should be __devexit, or there are linker problems when
the driver is built in.
Kevin
> {
> - clk_disable(nand_clock);
> + struct davinci_nand_info *info = platform_get_drvdata(pdev);
>
> - if (nand_vaddr)
> - iounmap(nand_vaddr);
> + iounmap(info->base);
> + iounmap(info->vaddr);
>
> - if (emif_base)
> - iounmap(emif_base);
> + nand_release(&info->mtd);
>
> - /* Release resources, unregister device */
> - nand_release(nand_davinci_mtd);
> + clk_disable(info->clk);
> + clk_put(info->clk);
>
> - /* Free the MTD device structure */
> - kfree(nand_davinci_mtd);
> + kfree(info);
>
> return 0;
> }
>
> -
> static struct platform_driver nand_davinci_driver = {
> .probe = nand_davinci_probe,
> .remove = nand_davinci_remove,
> @@ -640,3 +708,4 @@ MODULE_ALIAS(DRIVER_NAME);
> MODULE_LICENSE("GPL");
> MODULE_AUTHOR("Texas Instruments");
> MODULE_DESCRIPTION("Davinci NAND flash driver");
> +
> --
> 1.6.0.4.617.g2baf1
>
>
> _______________________________________________
> Davinci-linux-open-source mailing list
> [email protected]
> http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source
_______________________________________________
Davinci-linux-open-source mailing list
[email protected]
http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source