diff options
Diffstat (limited to 'target/linux/xburst/patches-3.3/0006-MTD-NAND-JZ4740-Multi-bank-support-with-autodetectio.patch')
-rw-r--r-- | target/linux/xburst/patches-3.3/0006-MTD-NAND-JZ4740-Multi-bank-support-with-autodetectio.patch | 417 |
1 files changed, 417 insertions, 0 deletions
diff --git a/target/linux/xburst/patches-3.3/0006-MTD-NAND-JZ4740-Multi-bank-support-with-autodetectio.patch b/target/linux/xburst/patches-3.3/0006-MTD-NAND-JZ4740-Multi-bank-support-with-autodetectio.patch new file mode 100644 index 0000000000..4110147974 --- /dev/null +++ b/target/linux/xburst/patches-3.3/0006-MTD-NAND-JZ4740-Multi-bank-support-with-autodetectio.patch @@ -0,0 +1,417 @@ +From d5814bdb661d3dac61422f8f69e459be884c9a9d Mon Sep 17 00:00:00 2001 +From: Maarten ter Huurne <maarten@treewalker.org> +Date: Tue, 2 Aug 2011 10:49:28 +0200 +Subject: [PATCH 06/21] MTD: NAND: JZ4740: Multi-bank support with + autodetection + +The platform data can now specify which external memory banks to probe +for NAND chips, and in which order. Banks that contain a NAND are used +and the other banks are freed. + +Squashed version of development done in jz-2.6.38 branch. +Original patch by Lars-Peter Clausen with some bug fixes from me. +Thanks to Paul Cercueil for the initial autodetection patch. +--- + arch/mips/include/asm/mach-jz4740/jz4740_nand.h | 4 + + arch/mips/jz4740/platform.c | 20 ++- + drivers/mtd/nand/jz4740_nand.c | 228 +++++++++++++++++++---- + 3 files changed, 215 insertions(+), 37 deletions(-) + +--- a/arch/mips/include/asm/mach-jz4740/jz4740_nand.h ++++ b/arch/mips/include/asm/mach-jz4740/jz4740_nand.h +@@ -19,6 +19,8 @@ + #include <linux/mtd/nand.h> + #include <linux/mtd/partitions.h> + ++#define JZ_NAND_NUM_BANKS 4 ++ + struct jz_nand_platform_data { + int num_partitions; + struct mtd_partition *partitions; +@@ -27,6 +29,8 @@ struct jz_nand_platform_data { + + unsigned int busy_gpio; + ++ unsigned char banks[JZ_NAND_NUM_BANKS]; ++ + void (*ident_callback)(struct platform_device *, struct nand_chip *, + struct mtd_partition **, int *num_partitions); + }; +--- a/arch/mips/jz4740/platform.c ++++ b/arch/mips/jz4740/platform.c +@@ -157,11 +157,29 @@ static struct resource jz4740_nand_resou + .flags = IORESOURCE_MEM, + }, + { +- .name = "bank", ++ .name = "bank1", + .start = 0x18000000, + .end = 0x180C0000 - 1, + .flags = IORESOURCE_MEM, + }, ++ { ++ .name = "bank2", ++ .start = 0x14000000, ++ .end = 0x140C0000 - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .name = "bank3", ++ .start = 0x0C000000, ++ .end = 0x0C0C0000 - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .name = "bank4", ++ .start = 0x08000000, ++ .end = 0x080C0000 - 1, ++ .flags = IORESOURCE_MEM, ++ }, + }; + + struct platform_device jz4740_nand_device = { +--- a/drivers/mtd/nand/jz4740_nand.c ++++ b/drivers/mtd/nand/jz4740_nand.c +@@ -52,9 +52,10 @@ + + #define JZ_NAND_CTRL_ENABLE_CHIP(x) BIT((x) << 1) + #define JZ_NAND_CTRL_ASSERT_CHIP(x) BIT(((x) << 1) + 1) ++#define JZ_NAND_CTRL_ASSERT_CHIP_MASK 0xaa + +-#define JZ_NAND_MEM_ADDR_OFFSET 0x10000 + #define JZ_NAND_MEM_CMD_OFFSET 0x08000 ++#define JZ_NAND_MEM_ADDR_OFFSET 0x10000 + + struct jz_nand { + struct mtd_info mtd; +@@ -62,8 +63,11 @@ struct jz_nand { + void __iomem *base; + struct resource *mem; + +- void __iomem *bank_base; +- struct resource *bank_mem; ++ unsigned char banks[JZ_NAND_NUM_BANKS]; ++ void __iomem *bank_base[JZ_NAND_NUM_BANKS]; ++ struct resource *bank_mem[JZ_NAND_NUM_BANKS]; ++ ++ int selected_bank; + + struct jz_nand_platform_data *pdata; + bool is_reading; +@@ -74,26 +78,50 @@ static inline struct jz_nand *mtd_to_jz_ + return container_of(mtd, struct jz_nand, mtd); + } + ++static void jz_nand_select_chip(struct mtd_info *mtd, int chipnr) ++{ ++ struct jz_nand *nand = mtd_to_jz_nand(mtd); ++ struct nand_chip *chip = mtd->priv; ++ uint32_t ctrl; ++ int banknr; ++ ++ ctrl = readl(nand->base + JZ_REG_NAND_CTRL); ++ ctrl &= ~JZ_NAND_CTRL_ASSERT_CHIP_MASK; ++ ++ if (chipnr == -1) { ++ banknr = -1; ++ } else { ++ banknr = nand->banks[chipnr] - 1; ++ chip->IO_ADDR_R = nand->bank_base[banknr]; ++ chip->IO_ADDR_W = nand->bank_base[banknr]; ++ } ++ writel(ctrl, nand->base + JZ_REG_NAND_CTRL); ++ ++ nand->selected_bank = banknr; ++} ++ + static void jz_nand_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) + { + struct jz_nand *nand = mtd_to_jz_nand(mtd); + struct nand_chip *chip = mtd->priv; + uint32_t reg; ++ void __iomem *bank_base = nand->bank_base[nand->selected_bank]; ++ ++ BUG_ON(nand->selected_bank < 0); + + if (ctrl & NAND_CTRL_CHANGE) { + BUG_ON((ctrl & NAND_ALE) && (ctrl & NAND_CLE)); + if (ctrl & NAND_ALE) +- chip->IO_ADDR_W = nand->bank_base + JZ_NAND_MEM_ADDR_OFFSET; ++ bank_base += JZ_NAND_MEM_ADDR_OFFSET; + else if (ctrl & NAND_CLE) +- chip->IO_ADDR_W = nand->bank_base + JZ_NAND_MEM_CMD_OFFSET; +- else +- chip->IO_ADDR_W = nand->bank_base; ++ bank_base += JZ_NAND_MEM_CMD_OFFSET; ++ chip->IO_ADDR_W = bank_base; + + reg = readl(nand->base + JZ_REG_NAND_CTRL); + if (ctrl & NAND_NCE) +- reg |= JZ_NAND_CTRL_ASSERT_CHIP(0); ++ reg |= JZ_NAND_CTRL_ASSERT_CHIP(nand->selected_bank); + else +- reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(0); ++ reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(nand->selected_bank); + writel(reg, nand->base + JZ_REG_NAND_CTRL); + } + if (dat != NAND_CMD_NONE) +@@ -252,7 +280,7 @@ static int jz_nand_correct_ecc_rs(struct + } + + static int jz_nand_ioremap_resource(struct platform_device *pdev, +- const char *name, struct resource **res, void __iomem **base) ++ const char *name, struct resource **res, void *__iomem *base) + { + int ret; + +@@ -288,6 +316,90 @@ err: + return ret; + } + ++static inline void jz_nand_iounmap_resource(struct resource *res, void __iomem *base) ++{ ++ iounmap(base); ++ release_mem_region(res->start, resource_size(res)); ++} ++ ++static int __devinit jz_nand_detect_bank(struct platform_device *pdev, struct jz_nand *nand, unsigned char bank, size_t chipnr, uint8_t *nand_maf_id, uint8_t *nand_dev_id) { ++ int ret; ++ int gpio; ++ char gpio_name[9]; ++ char res_name[6]; ++ uint32_t ctrl; ++ struct mtd_info *mtd = &nand->mtd; ++ struct nand_chip *chip = &nand->chip; ++ ++ /* Request GPIO port. */ ++ gpio = JZ_GPIO_MEM_CS0 + bank - 1; ++ sprintf(gpio_name, "NAND CS%d", bank); ++ ret = gpio_request(gpio, gpio_name); ++ if (ret) { ++ dev_warn(&pdev->dev, ++ "Failed to request %s gpio %d: %d\n", ++ gpio_name, gpio, ret); ++ goto notfound_gpio; ++ } ++ ++ /* Request I/O resource. */ ++ sprintf(res_name, "bank%d", bank); ++ ret = jz_nand_ioremap_resource(pdev, res_name, ++ &nand->bank_mem[bank - 1], ++ &nand->bank_base[bank - 1]); ++ if (ret) ++ goto notfound_resource; ++ ++ /* Enable chip in bank. */ ++ jz_gpio_set_function(gpio, JZ_GPIO_FUNC_MEM_CS0); ++ ctrl = readl(nand->base + JZ_REG_NAND_CTRL); ++ ctrl |= JZ_NAND_CTRL_ENABLE_CHIP(bank - 1); ++ writel(ctrl, nand->base + JZ_REG_NAND_CTRL); ++ ++ if (chipnr == 0) { ++ /* Detect first chip. */ ++ ret = nand_scan_ident(mtd, 1, NULL); ++ if (ret) ++ goto notfound_id; ++ ++ /* Retrieve the IDs from the first chip. */ ++ chip->select_chip(mtd, 0); ++ chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); ++ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); ++ *nand_maf_id = chip->read_byte(mtd); ++ *nand_dev_id = chip->read_byte(mtd); ++ } else { ++ /* Detect additional chip. */ ++ chip->select_chip(mtd, chipnr); ++ chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); ++ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); ++ if (*nand_maf_id != chip->read_byte(mtd) ++ || *nand_dev_id != chip->read_byte(mtd)) { ++ ret = -ENODEV; ++ goto notfound_id; ++ } ++ ++ /* Update size of the MTD. */ ++ chip->numchips++; ++ mtd->size += chip->chipsize; ++ } ++ ++ dev_info(&pdev->dev, "Found chip %i on bank %i\n", chipnr, bank); ++ return 0; ++ ++notfound_id: ++ dev_info(&pdev->dev, "No chip found on bank %i\n", bank); ++ ctrl &= ~(JZ_NAND_CTRL_ENABLE_CHIP(bank - 1)); ++ writel(ctrl, nand->base + JZ_REG_NAND_CTRL); ++ jz_gpio_set_function(gpio, JZ_GPIO_FUNC_NONE); ++ jz_nand_iounmap_resource(nand->bank_mem[bank - 1], ++ nand->bank_base[bank - 1]); ++notfound_resource: ++ gpio_free(gpio); ++notfound_gpio: ++ return ret; ++} ++ + static int __devinit jz_nand_probe(struct platform_device *pdev) + { + int ret; +@@ -295,6 +407,8 @@ static int __devinit jz_nand_probe(struc + struct nand_chip *chip; + struct mtd_info *mtd; + struct jz_nand_platform_data *pdata = pdev->dev.platform_data; ++ size_t chipnr, bank_idx; ++ uint8_t nand_maf_id = 0, nand_dev_id = 0; + + nand = kzalloc(sizeof(*nand), GFP_KERNEL); + if (!nand) { +@@ -305,10 +419,6 @@ static int __devinit jz_nand_probe(struc + ret = jz_nand_ioremap_resource(pdev, "mmio", &nand->mem, &nand->base); + if (ret) + goto err_free; +- ret = jz_nand_ioremap_resource(pdev, "bank", &nand->bank_mem, +- &nand->bank_base); +- if (ret) +- goto err_iounmap_mmio; + + if (pdata && gpio_is_valid(pdata->busy_gpio)) { + ret = gpio_request(pdata->busy_gpio, "NAND busy pin"); +@@ -316,7 +426,7 @@ static int __devinit jz_nand_probe(struc + dev_err(&pdev->dev, + "Failed to request busy gpio %d: %d\n", + pdata->busy_gpio, ret); +- goto err_iounmap_mem; ++ goto err_iounmap_mmio; + } + } + +@@ -338,22 +448,51 @@ static int __devinit jz_nand_probe(struc + + chip->chip_delay = 50; + chip->cmd_ctrl = jz_nand_cmd_ctrl; ++ chip->select_chip = jz_nand_select_chip; + + if (pdata && gpio_is_valid(pdata->busy_gpio)) + chip->dev_ready = jz_nand_dev_ready; + +- chip->IO_ADDR_R = nand->bank_base; +- chip->IO_ADDR_W = nand->bank_base; +- + nand->pdata = pdata; + platform_set_drvdata(pdev, nand); + +- writel(JZ_NAND_CTRL_ENABLE_CHIP(0), nand->base + JZ_REG_NAND_CTRL); +- +- ret = nand_scan_ident(mtd, 1, NULL); +- if (ret) { +- dev_err(&pdev->dev, "Failed to scan nand\n"); +- goto err_gpio_free; ++ /* We are going to autodetect NAND chips in the banks specified in the ++ * platform data. Although nand_scan_ident() can detect multiple chips, ++ * it requires those chips to be numbered consecuitively, which is not ++ * always the case for external memory banks. And a fixed chip-to-bank ++ * mapping is not practical either, since for example Dingoo units ++ * produced at different times have NAND chips in different banks. ++ */ ++ chipnr = 0; ++ for (bank_idx = 0; bank_idx < JZ_NAND_NUM_BANKS; bank_idx++) { ++ unsigned char bank; ++ ++ /* If there is no platform data, look for NAND in bank 1, ++ * which is the most likely bank since it is the only one ++ * that can be booted from. ++ */ ++ bank = pdata ? pdata->banks[bank_idx] : bank_idx ^ 1; ++ if (bank == 0) ++ break; ++ if (bank > JZ_NAND_NUM_BANKS) { ++ dev_warn(&pdev->dev, ++ "Skipping non-existing bank: %d\n", bank); ++ continue; ++ } ++ /* The detection routine will directly or indirectly call ++ * jz_nand_select_chip(), so nand->banks has to contain the ++ * bank we're checking. ++ */ ++ nand->banks[chipnr] = bank; ++ if (jz_nand_detect_bank(pdev, nand, bank, chipnr, ++ &nand_maf_id, &nand_dev_id) == 0) ++ chipnr++; ++ else ++ nand->banks[chipnr] = 0; ++ } ++ if (chipnr == 0) { ++ dev_err(&pdev->dev, "No NAND chips found\n"); ++ goto err_gpio_busy; + } + + if (pdata && pdata->ident_callback) { +@@ -363,8 +502,8 @@ static int __devinit jz_nand_probe(struc + + ret = nand_scan_tail(mtd); + if (ret) { +- dev_err(&pdev->dev, "Failed to scan nand\n"); +- goto err_gpio_free; ++ dev_err(&pdev->dev, "Failed to scan NAND\n"); ++ goto err_unclaim_banks; + } + + ret = mtd_device_parse_register(mtd, NULL, 0, +@@ -381,14 +520,21 @@ static int __devinit jz_nand_probe(struc + return 0; + + err_nand_release: +- nand_release(&nand->mtd); +-err_gpio_free: ++ nand_release(mtd); ++err_unclaim_banks: ++ while (chipnr--) { ++ unsigned char bank = nand->banks[chipnr]; ++ gpio_free(JZ_GPIO_MEM_CS0 + bank - 1); ++ jz_nand_iounmap_resource(nand->bank_mem[bank - 1], ++ nand->bank_base[bank - 1]); ++ } ++ writel(0, nand->base + JZ_REG_NAND_CTRL); ++err_gpio_busy: ++ if (pdata && gpio_is_valid(pdata->busy_gpio)) ++ gpio_free(pdata->busy_gpio); + platform_set_drvdata(pdev, NULL); +- gpio_free(pdata->busy_gpio); +-err_iounmap_mem: +- iounmap(nand->bank_base); + err_iounmap_mmio: +- iounmap(nand->base); ++ jz_nand_iounmap_resource(nand->mem, nand->base); + err_free: + kfree(nand); + return ret; +@@ -397,16 +543,26 @@ err_free: + static int __devexit jz_nand_remove(struct platform_device *pdev) + { + struct jz_nand *nand = platform_get_drvdata(pdev); ++ struct jz_nand_platform_data *pdata = pdev->dev.platform_data; ++ size_t i; + + nand_release(&nand->mtd); + + /* Deassert and disable all chips */ + writel(0, nand->base + JZ_REG_NAND_CTRL); + +- iounmap(nand->bank_base); +- release_mem_region(nand->bank_mem->start, resource_size(nand->bank_mem)); +- iounmap(nand->base); +- release_mem_region(nand->mem->start, resource_size(nand->mem)); ++ for (i = 0; i < JZ_NAND_NUM_BANKS; ++i) { ++ unsigned char bank = nand->banks[i]; ++ if (bank != 0) { ++ jz_nand_iounmap_resource(nand->bank_mem[bank - 1], ++ nand->bank_base[bank - 1]); ++ gpio_free(JZ_GPIO_MEM_CS0 + bank - 1); ++ } ++ } ++ if (pdata && gpio_is_valid(pdata->busy_gpio)) ++ gpio_free(pdata->busy_gpio); ++ ++ jz_nand_iounmap_resource(nand->mem, nand->base); + + platform_set_drvdata(pdev, NULL); + kfree(nand); |