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)
{
- 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