Permalink
Browse files

MTD: nand: jz4740: Experimental multi-chip support

  • Loading branch information...
1 parent 9df8a0f commit d6e96efcb6163fb63b01f9ee861ffdee1f666e2e @larsclausen larsclausen committed Jul 14, 2010
Showing with 108 additions and 28 deletions.
  1. +108 −28 drivers/mtd/nand/jz4740_nand.c
@@ -51,18 +51,21 @@
#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;
struct nand_chip chip;
void __iomem *base;
struct resource *mem;
- void __iomem *bank_base;
- struct resource *bank_mem;
+ void __iomem *bank_base[4];
+ struct resource *bank_mem[4];
+
+ int selected_chip;
struct jz_nand_platform_data *pdata;
bool is_reading;
@@ -73,26 +76,47 @@ static inline struct jz_nand *mtd_to_jz_nand(struct mtd_info *mtd)
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;
+
+ ctrl = readl(nand->base + JZ_REG_NAND_CTRL);
+ ctrl &= ~JZ_NAND_CTRL_ASSERT_CHIP_MASK;
+
+ if (chipnr != -1) {
+ chipnr = nand->pdata->banks[chipnr] - 1;
+ chip->IO_ADDR_R = nand->bank_base[chipnr];
+ chip->IO_ADDR_W = nand->bank_base[chipnr];
+ }
+ writel(ctrl, nand->base + JZ_REG_NAND_CTRL);
+
+ nand->selected_chip = chipnr;
+}
+
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_chip];
+
+ BUG_ON(nand->selected_chip < 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_chip);
else
- reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(0);
+ reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(nand->selected_chip);
writel(reg, nand->base + JZ_REG_NAND_CTRL);
}
if (dat != NAND_CMD_NONE)
@@ -299,7 +323,7 @@ static const char *part_probes[] = {"cmdline", NULL};
#endif
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;
@@ -335,6 +359,37 @@ static int jz_nand_ioremap_resource(struct platform_device *pdev,
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 jz_nand_ioremap_banks(struct platform_device *pdev,
+ struct jz_nand *nand, unsigned char *banks, size_t num_banks)
+{
+ int ret;
+ char name[6];
+ int i;
+
+ for (i = 0; i < num_banks; ++i) {
+ sprintf(name, "bank%d", banks[i]);
+
+ ret = jz_nand_ioremap_resource(pdev, name, &nand->bank_mem[i],
+ &nand->bank_base[i]);
+
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+err:
+ for (--i; i >= 0; --i)
+ jz_nand_iounmap_resource(nand->bank_mem[i], nand->bank_base[i]);
+
+ return ret;
+}
+
static int __devinit jz_nand_probe(struct platform_device *pdev)
{
int ret;
@@ -346,6 +401,9 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
struct mtd_partition *partition_info;
int num_partitions = 0;
#endif
+ size_t num_banks, i;
+ unsigned char bank;
+ uint32_t ctrl;
nand = kzalloc(sizeof(*nand), GFP_KERNEL);
if (!nand) {
@@ -356,18 +414,14 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
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");
if (ret) {
dev_err(&pdev->dev,
"Failed to request busy gpio %d: %d\n",
pdata->busy_gpio, ret);
- goto err_iounmap_mem;
+ goto err_iounmap_mmio;
}
}
@@ -392,22 +446,43 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
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);
+ if (pdata->banks[0] == 0)
+ pdata->banks[0] = 1;
+
+ ctrl = 0;
+ for (num_banks = 0; num_banks < 4 ; ++num_banks) {
+ bank = pdata->banks[num_banks];
+ if (bank == 0)
+ break;
+ if (bank > 4) {
+ ret = -EINVAL;
+ goto err_gpio_free;
+ }
+
+ ctrl |= JZ_NAND_CTRL_ENABLE_CHIP(bank - 1);
+ }
- ret = nand_scan_ident(mtd, 1);
+ ret = jz_nand_ioremap_banks(pdev, nand, pdata->banks, num_banks);
+ if (ret)
+ goto err_gpio_free;
+
+ chip->IO_ADDR_R = nand->bank_base[pdata->banks[0] - 1];
+ chip->IO_ADDR_W = nand->bank_base[pdata->banks[0] - 1];
+
+ writel(ctrl, nand->base + JZ_REG_NAND_CTRL);
+
+ ret = nand_scan_ident(mtd, num_banks);
if (ret) {
dev_err(&pdev->dev, "Failed to scan nand\n");
- goto err_gpio_free;
+ goto err_iounmap_banks;
}
if (pdata && pdata->ident_callback) {
@@ -418,7 +493,7 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
ret = nand_scan_tail(mtd);
if (ret) {
dev_err(&pdev->dev, "Failed to scan nand\n");
- goto err_gpio_free;
+ goto err_iounmap_banks;
}
#ifdef CONFIG_MTD_PARTITIONS
@@ -448,11 +523,12 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
err_nand_release:
nand_release(&nand->mtd);
+err_iounmap_banks:
+ for (i = 0; i < num_banks; ++i)
+ jz_nand_iounmap_resource(nand->bank_mem[i], nand->bank_base[i]);
err_gpio_free:
platform_set_drvdata(pdev, NULL);
gpio_free(pdata->busy_gpio);
-err_iounmap_mem:
- iounmap(nand->bank_base);
err_iounmap_mmio:
iounmap(nand->base);
err_free:
@@ -463,16 +539,20 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
static int __devexit jz_nand_remove(struct platform_device *pdev)
{
struct jz_nand *nand = platform_get_drvdata(pdev);
+ int 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));
+ i = 0;
+ do {
+ jz_nand_iounmap_resource(nand->bank_mem[i], nand->bank_base[i]);
+ ++i;
+ } while (nand->bank_base[i] != 0 && i < 4);
+
+ jz_nand_iounmap_resource(nand->mem, nand->base);
platform_set_drvdata(pdev, NULL);
kfree(nand);

0 comments on commit d6e96ef

Please sign in to comment.