Commit 502eab97 authored by Felipe Balbi's avatar Felipe Balbi Committed by Kevin Hilman

mtd: nand: introduce davinci_nand_info

On Fri, Dec 12, 2008 at 07:52:28PM +0200, Felipe Balbi wrote:
> From: Felipe Balbi <felipe.balbi@nokia.com>
>
> introduce a structure to hold davinci_nand device
> data.
>
> Signed-off-by: Felipe Balbi <felipe.balbi@nokia.com>

New version below:

====== cut here ======

>From 1d2f9999a56bd0a71510ce09e0e863d15d98013d Mon Sep 17 00:00:00 2001
From: Felipe Balbi <felipe.balbi@nokia.com>
Date: Fri, 12 Dec 2008 16:15:07 +0200
Subject: [patch-v2.6.28 04/10] mtd: nand: introduce davinci_nand_info

introduce a structure to hold davinci_nand device
data.
Signed-off-by: default avatarFelipe Balbi <felipe.balbi@nokia.com>
parent 14f80ce1
...@@ -60,14 +60,18 @@ ...@@ -60,14 +60,18 @@
#define DRIVER_NAME "davinci_nand" #define DRIVER_NAME "davinci_nand"
static struct clk *nand_clock; struct davinci_nand_info {
static void __iomem *nand_vaddr; struct mtd_info mtd;
static void __iomem *emif_base; struct nand_chip chip;
/* struct device *dev;
* MTD structure for DaVinici board struct clk *clk;
*/
static struct mtd_info *nand_davinci_mtd; void __iomem *base;
void __iomem *vaddr;
};
#define to_davinci_nand(m) container_of(m, struct davinci_nand_info, mtd)
#ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_PARTITIONS
const char *part_probes[] = { "cmdlinepart", NULL }; const char *part_probes[] = { "cmdlinepart", NULL };
...@@ -91,18 +95,22 @@ static struct nand_bbt_descr davinci_memorybased_large = { ...@@ -91,18 +95,22 @@ static struct nand_bbt_descr davinci_memorybased_large = {
.pattern = scan_ff_pattern .pattern = scan_ff_pattern
}; };
inline unsigned int davinci_nand_readl(int offset) static inline unsigned int davinci_nand_readl(struct davinci_nand_info *info,
int offset)
{ {
return __raw_readl(emif_base + offset); return __raw_readl(info->base + offset);
} }
inline void davinci_nand_writel(unsigned long value, int offset) static inline void davinci_nand_writel(struct davinci_nand_info *info,
int offset, unsigned long value)
{ {
__raw_writel(value, emif_base + offset); __raw_writel(value, info->base + offset);
} }
/* /*
* Hardware specific access to control-lines * Hardware specific access to control-lines
*
* REVISIT avoid casting addresses
*/ */
static void nand_davinci_hwcontrol(struct mtd_info *mtd, int cmd, static void nand_davinci_hwcontrol(struct mtd_info *mtd, int cmd,
unsigned int ctrl) unsigned int ctrl)
...@@ -134,15 +142,18 @@ static void nand_davinci_select_chip(struct mtd_info *mtd, int chip) ...@@ -134,15 +142,18 @@ static void nand_davinci_select_chip(struct mtd_info *mtd, int chip)
#ifdef CONFIG_NAND_FLASH_HW_ECC #ifdef CONFIG_NAND_FLASH_HW_ECC
static void nand_davinci_enable_hwecc(struct mtd_info *mtd, int mode) static void nand_davinci_enable_hwecc(struct mtd_info *mtd, int mode)
{ {
struct davinci_nand_info *info;
u32 retval; u32 retval;
info = to_davinci_nand(mtd);
/* Reset ECC hardware */ /* Reset ECC hardware */
retval = davinci_nand_readl(NANDF1ECC_OFFSET); retval = davinci_nand_readl(info, NANDF1ECC_OFFSET);
/* Restart ECC hardware */ /* Restart ECC hardware */
retval = davinci_nand_readl(NANDFCR_OFFSET); retval = davinci_nand_readl(info, NANDFCR_OFFSET);
retval |= (1 << 8); retval |= (1 << 8);
davinci_nand_writel(retval, NANDFCR_OFFSET); davinci_nand_writel(info, NANDFCR_OFFSET, retval);
} }
/* /*
...@@ -150,8 +161,10 @@ static void nand_davinci_enable_hwecc(struct mtd_info *mtd, int mode) ...@@ -150,8 +161,10 @@ static void nand_davinci_enable_hwecc(struct mtd_info *mtd, int mode)
*/ */
static u32 nand_davinci_readecc(struct mtd_info *mtd) static u32 nand_davinci_readecc(struct mtd_info *mtd)
{ {
struct davinci_nand_info *info = to_davinci_nand(mtd);
/* Read register ECC and clear it */ /* Read register ECC and clear it */
return davinci_nand_readl(NANDF1ECC_OFFSET); return davinci_nand_readl(info, NANDF1ECC_OFFSET);
} }
/* /*
...@@ -360,7 +373,9 @@ static void nand_davinci_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) ...@@ -360,7 +373,9 @@ 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) static int nand_davinci_dev_ready(struct mtd_info *mtd)
{ {
return davinci_nand_readl(NANDFSR_OFFSET) & NAND_BUSY_FLAG; struct davinci_nand_info *info = to_davinci_nand(mtd);
return davinci_nand_readl(info, NANDFSR_OFFSET) & NAND_BUSY_FLAG;
} }
static void nand_davinci_set_eccsize(struct nand_chip *chip) static void nand_davinci_set_eccsize(struct nand_chip *chip)
...@@ -408,7 +423,7 @@ static void nand_davinci_set_eccbytes(struct nand_chip *chip) ...@@ -408,7 +423,7 @@ static void nand_davinci_set_eccbytes(struct nand_chip *chip)
#endif #endif
} }
static void __devinit nand_davinci_flash_init(void) static void __devinit nand_davinci_flash_init(struct davinci_nand_info *info)
{ {
u32 regval, tmp; u32 regval, tmp;
...@@ -436,9 +451,9 @@ static void __devinit nand_davinci_flash_init(void) ...@@ -436,9 +451,9 @@ static void __devinit nand_davinci_flash_init(void)
"by bootloader.\n", regval, tmp); "by bootloader.\n", regval, tmp);
} }
regval = davinci_nand_readl(AWCCR_OFFSET); regval = davinci_nand_readl(info, AWCCR_OFFSET);
regval |= 0x10000000; regval |= 0x10000000;
davinci_nand_writel(regval, AWCCR_OFFSET); davinci_nand_writel(info, AWCCR_OFFSET, regval);
/*------------------------------------------------------------------* /*------------------------------------------------------------------*
* NAND FLASH CHIP TIMEOUT @ 459 MHz * * NAND FLASH CHIP TIMEOUT @ 459 MHz *
...@@ -459,163 +474,182 @@ static void __devinit nand_davinci_flash_init(void) ...@@ -459,163 +474,182 @@ static void __devinit nand_davinci_flash_init(void)
| (3 << 2) /* turnAround ?? ns */ | (3 << 2) /* turnAround ?? ns */
| (0 << 0) /* asyncSize 8-bit bus */ | (0 << 0) /* asyncSize 8-bit bus */
; ;
tmp = davinci_nand_readl(A1CR_OFFSET); tmp = davinci_nand_readl(info, A1CR_OFFSET);
if (tmp != regval) { 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 " \ "reg to 0x%08x, was 0x%08x, should be done by " \
"bootloader.\n", regval, tmp); "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);
} }
/* static int __devinit nand_davinci_probe(struct platform_device *pdev)
* Main initialization routine
*/
int __devinit nand_davinci_probe(struct platform_device *pdev)
{ {
struct flash_platform_data *pdata = pdev->dev.platform_data; struct flash_platform_data *pdata = pdev->dev.platform_data;
struct resource *res = pdev->resource; struct davinci_nand_info *info;
struct resource *res2 = platform_get_resource(pdev, struct resource *res1;
IORESOURCE_MEM, 1); struct resource *res2;
struct nand_chip *chip;
struct device *dev = NULL;
u32 nand_rev_code;
#ifdef CONFIG_MTD_CMDLINE_PARTS #ifdef CONFIG_MTD_CMDLINE_PARTS
struct mtd_partition *mtd_parts = 0;
char *master_name; char *master_name;
int mtd_parts_nb = 0; int mtd_parts_nb = 0;
struct mtd_partition *mtd_parts = 0;
#endif #endif
void __iomem *vaddr;
void __iomem *base;
nand_clock = clk_get(dev, "AEMIFCLK"); int ret;
if (IS_ERR(nand_clock)) { u32 rev;
printk(KERN_ERR "Error %ld getting AEMIFCLK clock?\n",
PTR_ERR(nand_clock)); if (!pdata) {
return -1; 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 */ platform_set_drvdata(pdev, info);
nand_davinci_mtd = kmalloc(sizeof(struct mtd_info) +
sizeof(struct nand_chip), GFP_KERNEL);
if (!nand_davinci_mtd) { res1 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
printk(KERN_ERR "Unable to allocate davinci NAND MTD device " \ res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
"structure.\n"); if (!res1 || !res2) {
clk_disable(nand_clock); dev_err(&pdev->dev, "resource missing\n");
return -ENOMEM; ret = -EINVAL;
goto err_res;
} }
/* Get pointer to private data */ vaddr = ioremap(res1->start, res1->end - res1->start);
chip = (struct nand_chip *) (&nand_davinci_mtd[1]); base = ioremap(res2->start, res2->end - res2->start);
if (!vaddr || !base) {
/* Initialize structures */ dev_err(&pdev->dev, "ioremap failed\n");
memset((char *)nand_davinci_mtd, 0, sizeof(struct mtd_info)); ret = -EINVAL;
memset((char *)chip, 0, sizeof(struct nand_chip)); goto err_ioremap;
/* 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", info->mtd.priv = &info->chip;
(nand_rev_code >> 8) & 0xff, nand_rev_code & 0xff); info->mtd.name = dev_name(&pdev->dev);
info->mtd.owner = THIS_MODULE;
nand_vaddr = ioremap(res->start, res->end - res->start); info->chip.IO_ADDR_R = vaddr;
emif_base = ioremap(res2->start, res2->end - res2->start); info->chip.IO_ADDR_W = vaddr;
if (nand_vaddr == NULL || emif_base == NULL) { info->chip.chip_delay = 0;
printk(KERN_ERR "DaVinci NAND: ioremap failed.\n"); info->chip.select_chip = nand_davinci_select_chip;
clk_disable(nand_clock); info->chip.options = 0;
kfree(nand_davinci_mtd); info->chip.ecc.mode = DAVINCI_NAND_ECC_MODE;
return -ENOMEM;
}
chip->IO_ADDR_R = nand_vaddr; /* Set address of hardware control function */
chip->IO_ADDR_W = nand_vaddr; info->chip.cmd_ctrl = nand_davinci_hwcontrol;
chip->chip_delay = 0; info->chip.dev_ready = nand_davinci_dev_ready;
chip->select_chip = nand_davinci_select_chip;
chip->options = 0;
chip->ecc.mode = DAVINCI_NAND_ECC_MODE;
/* Set ECC size and bytes */ /* Speed up the read buffer */
nand_davinci_set_eccsize(chip); info->chip.read_buf = nand_davinci_read_buf;
nand_davinci_set_eccbytes(chip);
/* Set address of hardware control function */ /* Speed up the creation of the bad block table */
chip->cmd_ctrl = nand_davinci_hwcontrol; info->chip.scan_bbt = nand_davinci_scan_bbt;
chip->dev_ready = nand_davinci_dev_ready;
#ifdef CONFIG_NAND_FLASH_HW_ECC #ifdef CONFIG_NAND_FLASH_HW_ECC
chip->ecc.calculate = nand_davinci_calculate_ecc; /* REVISIT should be using runtime check */
chip->ecc.correct = nand_davinci_correct_data; info->chip.ecc.calculate = nand_davinci_calculate_ecc;
chip->ecc.hwctl = nand_davinci_enable_hwecc; info->chip.ecc.correct = nand_davinci_correct_data;
info->chip.ecc.hwctl = nand_davinci_enable_hwecc;
#endif #endif
/* Speed up the read buffer */ info->clk = clk_get(&pdev->dev, "AEMIFCLK");
chip->read_buf = nand_davinci_read_buf; 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 */ ret = clk_enable(info->clk);
chip->scan_bbt = nand_davinci_scan_bbt; 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 */ /* Scan to find existence of the device */
if (nand_scan(nand_davinci_mtd, 1)) { if (nand_scan(&info->mtd, 1)) {
printk(KERN_ERR "Chip Select is not set for NAND\n"); dev_err(&pdev->dev, "chip select is not set for 'NAND'\n");
clk_disable(nand_clock); ret = -ENXIO;
kfree(nand_davinci_mtd); goto err_scan;
return -ENXIO;
} }
/* Register the partitions */ /* 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 #ifdef CONFIG_MTD_CMDLINE_PARTS
/* Set nand_davinci_mtd->name = 0 temporarily */ /* Set info->mtd.name = 0 temporarily */
master_name = nand_davinci_mtd->name; master_name = info->mtd.name;
nand_davinci_mtd->name = (char *)0; 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-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); &mtd_parts, 0);
/* Restore nand_davinci_mtd->name */ /* Restore info->mtd.name */
nand_davinci_mtd->name = master_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 #endif
rev = davinci_nand_readl(info, NRCSR_OFFSET);
dev_info(&pdev->dev, "controller rev. %d.%d\n",
(rev >> 8) & 0xff, rev & 0xff);
return 0; 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 nand_davinci_remove(struct platform_device *pdev)
{ {
clk_disable(nand_clock); struct davinci_nand_info *info = platform_get_drvdata(pdev);
if (nand_vaddr) iounmap(info->base);
iounmap(nand_vaddr); iounmap(info->vaddr);
if (emif_base) nand_release(&info->mtd);
iounmap(emif_base);
/* Release resources, unregister device */ clk_disable(info->clk);
nand_release(nand_davinci_mtd); clk_put(info->clk);
/* Free the MTD device structure */ kfree(info);
kfree(nand_davinci_mtd);
return 0; return 0;
} }
static struct platform_driver nand_davinci_driver = { static struct platform_driver nand_davinci_driver = {
.probe = nand_davinci_probe, .probe = nand_davinci_probe,
.remove = nand_davinci_remove, .remove = nand_davinci_remove,
...@@ -640,3 +674,4 @@ MODULE_ALIAS(DRIVER_NAME); ...@@ -640,3 +674,4 @@ MODULE_ALIAS(DRIVER_NAME);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Texas Instruments"); MODULE_AUTHOR("Texas Instruments");
MODULE_DESCRIPTION("Davinci NAND flash driver"); MODULE_DESCRIPTION("Davinci NAND flash driver");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment