Date: | 2010-07-14 22:58:40 (13 years 4 months ago) |
---|---|
Author: | Lars C. |
Commit: | d6e96efcb6163fb63b01f9ee861ffdee1f666e2e |
Message: | MTD: nand: jz4740: Experimental multi-chip support |
Files: |
drivers/mtd/nand/jz4740_nand.c (12 diffs) |
Change Details
drivers/mtd/nand/jz4740_nand.c | ||
---|---|---|
51 | 51 | |
52 | 52 | #define JZ_NAND_CTRL_ENABLE_CHIP(x) BIT((x) << 1) |
53 | 53 | #define JZ_NAND_CTRL_ASSERT_CHIP(x) BIT(((x) << 1) + 1) |
54 | #define JZ_NAND_CTRL_ASSERT_CHIP_MASK 0xaa | |
54 | 55 | |
55 | #define JZ_NAND_MEM_ADDR_OFFSET 0x10000 | |
56 | 56 | #define JZ_NAND_MEM_CMD_OFFSET 0x08000 |
57 | #define JZ_NAND_MEM_ADDR_OFFSET 0x10000 | |
57 | 58 | |
58 | 59 | struct jz_nand { |
59 | 60 | struct mtd_info mtd; |
... | ... | |
61 | 62 | void __iomem *base; |
62 | 63 | struct resource *mem; |
63 | 64 | |
64 | void __iomem *bank_base; | |
65 | struct resource *bank_mem; | |
65 | void __iomem *bank_base[4]; | |
66 | struct resource *bank_mem[4]; | |
67 | ||
68 | int selected_chip; | |
66 | 69 | |
67 | 70 | struct jz_nand_platform_data *pdata; |
68 | 71 | bool is_reading; |
... | ... | |
73 | 76 | return container_of(mtd, struct jz_nand, mtd); |
74 | 77 | } |
75 | 78 | |
79 | static void jz_nand_select_chip(struct mtd_info *mtd, int chipnr) | |
80 | { | |
81 | struct jz_nand *nand = mtd_to_jz_nand(mtd); | |
82 | struct nand_chip *chip = mtd->priv; | |
83 | uint32_t ctrl; | |
84 | ||
85 | ctrl = readl(nand->base + JZ_REG_NAND_CTRL); | |
86 | ctrl &= ~JZ_NAND_CTRL_ASSERT_CHIP_MASK; | |
87 | ||
88 | if (chipnr != -1) { | |
89 | chipnr = nand->pdata->banks[chipnr] - 1; | |
90 | chip->IO_ADDR_R = nand->bank_base[chipnr]; | |
91 | chip->IO_ADDR_W = nand->bank_base[chipnr]; | |
92 | } | |
93 | writel(ctrl, nand->base + JZ_REG_NAND_CTRL); | |
94 | ||
95 | nand->selected_chip = chipnr; | |
96 | } | |
97 | ||
76 | 98 | static void jz_nand_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) |
77 | 99 | { |
78 | 100 | struct jz_nand *nand = mtd_to_jz_nand(mtd); |
79 | 101 | struct nand_chip *chip = mtd->priv; |
80 | 102 | uint32_t reg; |
103 | void __iomem *bank_base = nand->bank_base[nand->selected_chip]; | |
104 | ||
105 | BUG_ON(nand->selected_chip < 0); | |
81 | 106 | |
82 | 107 | if (ctrl & NAND_CTRL_CHANGE) { |
83 | 108 | BUG_ON((ctrl & NAND_ALE) && (ctrl & NAND_CLE)); |
84 | 109 | if (ctrl & NAND_ALE) |
85 | chip->IO_ADDR_W = nand->bank_base + JZ_NAND_MEM_ADDR_OFFSET; | |
110 | bank_base += JZ_NAND_MEM_ADDR_OFFSET; | |
86 | 111 | else if (ctrl & NAND_CLE) |
87 | chip->IO_ADDR_W = nand->bank_base + JZ_NAND_MEM_CMD_OFFSET; | |
88 | else | |
89 | chip->IO_ADDR_W = nand->bank_base; | |
112 | bank_base += JZ_NAND_MEM_CMD_OFFSET; | |
113 | chip->IO_ADDR_W = bank_base; | |
90 | 114 | |
91 | 115 | reg = readl(nand->base + JZ_REG_NAND_CTRL); |
92 | 116 | if (ctrl & NAND_NCE) |
93 | reg |= JZ_NAND_CTRL_ASSERT_CHIP(0); | |
117 | reg |= JZ_NAND_CTRL_ASSERT_CHIP(nand->selected_chip); | |
94 | 118 | else |
95 | reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(0); | |
119 | reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(nand->selected_chip); | |
96 | 120 | writel(reg, nand->base + JZ_REG_NAND_CTRL); |
97 | 121 | } |
98 | 122 | if (dat != NAND_CMD_NONE) |
... | ... | |
299 | 323 | #endif |
300 | 324 | |
301 | 325 | static int jz_nand_ioremap_resource(struct platform_device *pdev, |
302 | const char *name, struct resource **res, void __iomem **base) | |
326 | const char *name, struct resource **res, void *__iomem *base) | |
303 | 327 | { |
304 | 328 | int ret; |
305 | 329 | |
... | ... | |
335 | 359 | return ret; |
336 | 360 | } |
337 | 361 | |
362 | static inline void jz_nand_iounmap_resource(struct resource *res, void __iomem *base) | |
363 | { | |
364 | iounmap(base); | |
365 | release_mem_region(res->start, resource_size(res)); | |
366 | } | |
367 | ||
368 | static int jz_nand_ioremap_banks(struct platform_device *pdev, | |
369 | struct jz_nand *nand, unsigned char *banks, size_t num_banks) | |
370 | { | |
371 | int ret; | |
372 | char name[6]; | |
373 | int i; | |
374 | ||
375 | for (i = 0; i < num_banks; ++i) { | |
376 | sprintf(name, "bank%d", banks[i]); | |
377 | ||
378 | ret = jz_nand_ioremap_resource(pdev, name, &nand->bank_mem[i], | |
379 | &nand->bank_base[i]); | |
380 | ||
381 | if (ret) | |
382 | goto err; | |
383 | } | |
384 | ||
385 | return 0; | |
386 | err: | |
387 | for (--i; i >= 0; --i) | |
388 | jz_nand_iounmap_resource(nand->bank_mem[i], nand->bank_base[i]); | |
389 | ||
390 | return ret; | |
391 | } | |
392 | ||
338 | 393 | static int __devinit jz_nand_probe(struct platform_device *pdev) |
339 | 394 | { |
340 | 395 | int ret; |
... | ... | |
346 | 401 | struct mtd_partition *partition_info; |
347 | 402 | int num_partitions = 0; |
348 | 403 | #endif |
404 | size_t num_banks, i; | |
405 | unsigned char bank; | |
406 | uint32_t ctrl; | |
349 | 407 | |
350 | 408 | nand = kzalloc(sizeof(*nand), GFP_KERNEL); |
351 | 409 | if (!nand) { |
... | ... | |
356 | 414 | ret = jz_nand_ioremap_resource(pdev, "mmio", &nand->mem, &nand->base); |
357 | 415 | if (ret) |
358 | 416 | goto err_free; |
359 | ret = jz_nand_ioremap_resource(pdev, "bank", &nand->bank_mem, | |
360 | &nand->bank_base); | |
361 | if (ret) | |
362 | goto err_iounmap_mmio; | |
363 | 417 | |
364 | 418 | if (pdata && gpio_is_valid(pdata->busy_gpio)) { |
365 | 419 | ret = gpio_request(pdata->busy_gpio, "NAND busy pin"); |
... | ... | |
367 | 421 | dev_err(&pdev->dev, |
368 | 422 | "Failed to request busy gpio %d: %d\n", |
369 | 423 | pdata->busy_gpio, ret); |
370 | goto err_iounmap_mem; | |
424 | goto err_iounmap_mmio; | |
371 | 425 | } |
372 | 426 | } |
373 | 427 | |
... | ... | |
392 | 446 | |
393 | 447 | chip->chip_delay = 50; |
394 | 448 | chip->cmd_ctrl = jz_nand_cmd_ctrl; |
449 | chip->select_chip = jz_nand_select_chip; | |
395 | 450 | |
396 | 451 | if (pdata && gpio_is_valid(pdata->busy_gpio)) |
397 | 452 | chip->dev_ready = jz_nand_dev_ready; |
398 | 453 | |
399 | chip->IO_ADDR_R = nand->bank_base; | |
400 | chip->IO_ADDR_W = nand->bank_base; | |
401 | ||
402 | 454 | nand->pdata = pdata; |
403 | 455 | platform_set_drvdata(pdev, nand); |
404 | 456 | |
405 | writel(JZ_NAND_CTRL_ENABLE_CHIP(0), nand->base + JZ_REG_NAND_CTRL); | |
457 | if (pdata->banks[0] == 0) | |
458 | pdata->banks[0] = 1; | |
459 | ||
460 | ctrl = 0; | |
461 | for (num_banks = 0; num_banks < 4 ; ++num_banks) { | |
462 | bank = pdata->banks[num_banks]; | |
463 | if (bank == 0) | |
464 | break; | |
465 | if (bank > 4) { | |
466 | ret = -EINVAL; | |
467 | goto err_gpio_free; | |
468 | } | |
469 | ||
470 | ctrl |= JZ_NAND_CTRL_ENABLE_CHIP(bank - 1); | |
471 | } | |
406 | 472 | |
407 | ret = nand_scan_ident(mtd, 1); | |
473 | ret = jz_nand_ioremap_banks(pdev, nand, pdata->banks, num_banks); | |
474 | if (ret) | |
475 | goto err_gpio_free; | |
476 | ||
477 | chip->IO_ADDR_R = nand->bank_base[pdata->banks[0] - 1]; | |
478 | chip->IO_ADDR_W = nand->bank_base[pdata->banks[0] - 1]; | |
479 | ||
480 | writel(ctrl, nand->base + JZ_REG_NAND_CTRL); | |
481 | ||
482 | ret = nand_scan_ident(mtd, num_banks); | |
408 | 483 | if (ret) { |
409 | 484 | dev_err(&pdev->dev, "Failed to scan nand\n"); |
410 | goto err_gpio_free; | |
485 | goto err_iounmap_banks; | |
411 | 486 | } |
412 | 487 | |
413 | 488 | if (pdata && pdata->ident_callback) { |
... | ... | |
418 | 493 | ret = nand_scan_tail(mtd); |
419 | 494 | if (ret) { |
420 | 495 | dev_err(&pdev->dev, "Failed to scan nand\n"); |
421 | goto err_gpio_free; | |
496 | goto err_iounmap_banks; | |
422 | 497 | } |
423 | 498 | |
424 | 499 | #ifdef CONFIG_MTD_PARTITIONS |
... | ... | |
448 | 523 | |
449 | 524 | err_nand_release: |
450 | 525 | nand_release(&nand->mtd); |
526 | err_iounmap_banks: | |
527 | for (i = 0; i < num_banks; ++i) | |
528 | jz_nand_iounmap_resource(nand->bank_mem[i], nand->bank_base[i]); | |
451 | 529 | err_gpio_free: |
452 | 530 | platform_set_drvdata(pdev, NULL); |
453 | 531 | gpio_free(pdata->busy_gpio); |
454 | err_iounmap_mem: | |
455 | iounmap(nand->bank_base); | |
456 | 532 | err_iounmap_mmio: |
457 | 533 | iounmap(nand->base); |
458 | 534 | err_free: |
... | ... | |
463 | 539 | static int __devexit jz_nand_remove(struct platform_device *pdev) |
464 | 540 | { |
465 | 541 | struct jz_nand *nand = platform_get_drvdata(pdev); |
542 | int i; | |
466 | 543 | |
467 | 544 | nand_release(&nand->mtd); |
468 | 545 | |
469 | 546 | /* Deassert and disable all chips */ |
470 | 547 | writel(0, nand->base + JZ_REG_NAND_CTRL); |
471 | 548 | |
472 | iounmap(nand->bank_base); | |
473 | release_mem_region(nand->bank_mem->start, resource_size(nand->bank_mem)); | |
474 | iounmap(nand->base); | |
475 | release_mem_region(nand->mem->start, resource_size(nand->mem)); | |
549 | i = 0; | |
550 | do { | |
551 | jz_nand_iounmap_resource(nand->bank_mem[i], nand->bank_base[i]); | |
552 | ++i; | |
553 | } while (nand->bank_base[i] != 0 && i < 4); | |
554 | ||
555 | jz_nand_iounmap_resource(nand->mem, nand->base); | |
476 | 556 | |
477 | 557 | platform_set_drvdata(pdev, NULL); |
478 | 558 | kfree(nand); |
Branches:
ben-wpan
ben-wpan-stefan
5396a9238205f20f811ea57898980d3ca82df0b6
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9