]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/mtd/nand/pxa3xx_nand.c
Merge git://git.infradead.org/mtd-2.6
[mv-sheeva.git] / drivers / mtd / nand / pxa3xx_nand.c
index 61b69cc4000956cee468f116653bc5766fa1037f..30a8ce6d3e69bcdc1f0429daafacd06115baf06e 100644 (file)
@@ -170,7 +170,13 @@ static int use_dma = 1;
 module_param(use_dma, bool, 0444);
 MODULE_PARM_DESC(use_dma, "enable DMA for data transfering to/from NAND HW");
 
-#ifdef CONFIG_MTD_NAND_PXA3xx_BUILTIN
+/*
+ * Default NAND flash controller configuration setup by the
+ * bootloader. This configuration is used only when pdata->keep_config is set
+ */
+static struct pxa3xx_nand_timing default_timing;
+static struct pxa3xx_nand_flash default_flash;
+
 static struct pxa3xx_nand_cmdset smallpage_cmdset = {
        .read1          = 0x0000,
        .read2          = 0x0050,
@@ -197,6 +203,7 @@ static struct pxa3xx_nand_cmdset largepage_cmdset = {
        .lock_status    = 0x007A,
 };
 
+#ifdef CONFIG_MTD_NAND_PXA3xx_BUILTIN
 static struct pxa3xx_nand_timing samsung512MbX16_timing = {
        .tCH    = 10,
        .tCS    = 0,
@@ -296,9 +303,23 @@ static struct pxa3xx_nand_flash *builtin_flash_types[] = {
 #define NDTR1_tWHR(c)  (min((c), 15) << 4)
 #define NDTR1_tAR(c)   (min((c), 15) << 0)
 
+#define tCH_NDTR0(r)   (((r) >> 19) & 0x7)
+#define tCS_NDTR0(r)   (((r) >> 16) & 0x7)
+#define tWH_NDTR0(r)   (((r) >> 11) & 0x7)
+#define tWP_NDTR0(r)   (((r) >> 8) & 0x7)
+#define tRH_NDTR0(r)   (((r) >> 3) & 0x7)
+#define tRP_NDTR0(r)   (((r) >> 0) & 0x7)
+
+#define tR_NDTR1(r)    (((r) >> 16) & 0xffff)
+#define tWHR_NDTR1(r)  (((r) >> 4) & 0xf)
+#define tAR_NDTR1(r)   (((r) >> 0) & 0xf)
+
 /* convert nano-seconds to nand flash controller clock cycles */
 #define ns2cycle(ns, clk)      (int)(((ns) * (clk / 1000000) / 1000) - 1)
 
+/* convert nand flash controller clock cycles to nano-seconds */
+#define cycle2ns(c, clk)       ((((c) + 1) * 1000000 + clk / 500) / (clk / 1000))
+
 static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
                                   const struct pxa3xx_nand_timing *t)
 {
@@ -920,6 +941,82 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
        return 0;
 }
 
+static void pxa3xx_nand_detect_timing(struct pxa3xx_nand_info *info,
+                                     struct pxa3xx_nand_timing *t)
+{
+       unsigned long nand_clk = clk_get_rate(info->clk);
+       uint32_t ndtr0 = nand_readl(info, NDTR0CS0);
+       uint32_t ndtr1 = nand_readl(info, NDTR1CS0);
+
+       t->tCH = cycle2ns(tCH_NDTR0(ndtr0), nand_clk);
+       t->tCS = cycle2ns(tCS_NDTR0(ndtr0), nand_clk);
+       t->tWH = cycle2ns(tWH_NDTR0(ndtr0), nand_clk);
+       t->tWP = cycle2ns(tWP_NDTR0(ndtr0), nand_clk);
+       t->tRH = cycle2ns(tRH_NDTR0(ndtr0), nand_clk);
+       t->tRP = cycle2ns(tRP_NDTR0(ndtr0), nand_clk);
+
+       t->tR = cycle2ns(tR_NDTR1(ndtr1), nand_clk);
+       t->tWHR = cycle2ns(tWHR_NDTR1(ndtr1), nand_clk);
+       t->tAR = cycle2ns(tAR_NDTR1(ndtr1), nand_clk);
+}
+
+static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
+{
+       uint32_t ndcr = nand_readl(info, NDCR);
+       struct nand_flash_dev *type = NULL;
+       uint32_t id = -1;
+       int i;
+
+       default_flash.page_per_block = ndcr & NDCR_PG_PER_BLK ? 64 : 32;
+       default_flash.page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
+       default_flash.flash_width = ndcr & NDCR_DWIDTH_M ? 16 : 8;
+       default_flash.dfc_width = ndcr & NDCR_DWIDTH_C ? 16 : 8;
+
+       if (default_flash.page_size == 2048)
+               default_flash.cmdset = &largepage_cmdset;
+       else
+               default_flash.cmdset = &smallpage_cmdset;
+
+       /* set info fields needed to __readid */
+       info->flash_info = &default_flash;
+       info->read_id_bytes = (default_flash.page_size == 2048) ? 4 : 2;
+       info->reg_ndcr = ndcr;
+
+       if (__readid(info, &id))
+               return -ENODEV;
+
+       /* Lookup the flash id */
+       id = (id >> 8) & 0xff;          /* device id is byte 2 */
+       for (i = 0; nand_flash_ids[i].name != NULL; i++) {
+               if (id == nand_flash_ids[i].id) {
+                       type =  &nand_flash_ids[i];
+                       break;
+               }
+       }
+
+       if (!type)
+               return -ENODEV;
+
+       /* fill the missing flash information */
+       i = __ffs(default_flash.page_per_block * default_flash.page_size);
+       default_flash.num_blocks = type->chipsize << (20 - i);
+
+       info->oob_size = (default_flash.page_size == 2048) ? 64 : 16;
+
+       /* calculate addressing information */
+       info->col_addr_cycles = (default_flash.page_size == 2048) ? 2 : 1;
+
+       if (default_flash.num_blocks * default_flash.page_per_block > 65536)
+               info->row_addr_cycles = 3;
+       else
+               info->row_addr_cycles = 2;
+
+       pxa3xx_nand_detect_timing(info, &default_timing);
+       default_flash.timing = &default_timing;
+
+       return 0;
+}
+
 static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info,
                                    const struct pxa3xx_nand_platform_data *pdata)
 {
@@ -927,6 +1024,10 @@ static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info,
        uint32_t id = -1;
        int i;
 
+       if (pdata->keep_config)
+               if (pxa3xx_nand_detect_config(info) == 0)
+                       return 0;
+
        for (i = 0; i<pdata->num_flash; ++i) {
                f = pdata->flash + i;
 
@@ -1078,6 +1179,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 
        this = &info->nand_chip;
        mtd->priv = info;
+       mtd->owner = THIS_MODULE;
 
        info->clk = clk_get(&pdev->dev, NULL);
        if (IS_ERR(info->clk)) {
@@ -1117,14 +1219,14 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
                goto fail_put_clk;
        }
 
-       r = request_mem_region(r->start, r->end - r->start + 1, pdev->name);
+       r = request_mem_region(r->start, resource_size(r), pdev->name);
        if (r == NULL) {
                dev_err(&pdev->dev, "failed to request memory resource\n");
                ret = -EBUSY;
                goto fail_put_clk;
        }
 
-       info->mmio_base = ioremap(r->start, r->end - r->start + 1);
+       info->mmio_base = ioremap(r->start, resource_size(r));
        if (info->mmio_base == NULL) {
                dev_err(&pdev->dev, "ioremap() failed\n");
                ret = -ENODEV;
@@ -1173,7 +1275,7 @@ fail_free_buf:
 fail_free_io:
        iounmap(info->mmio_base);
 fail_free_res:
-       release_mem_region(r->start, r->end - r->start + 1);
+       release_mem_region(r->start, resource_size(r));
 fail_put_clk:
        clk_disable(info->clk);
        clk_put(info->clk);
@@ -1186,6 +1288,7 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 {
        struct mtd_info *mtd = platform_get_drvdata(pdev);
        struct pxa3xx_nand_info *info = mtd->priv;
+       struct resource *r;
 
        platform_set_drvdata(pdev, NULL);
 
@@ -1198,6 +1301,14 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
                                info->data_buff, info->data_buff_phys);
        } else
                kfree(info->data_buff);
+
+       iounmap(info->mmio_base);
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       release_mem_region(r->start, resource_size(r));
+
+       clk_disable(info->clk);
+       clk_put(info->clk);
+
        kfree(mtd);
        return 0;
 }