Date: | 2011-05-29 17:06:05 (12 years 9 months ago) |
---|---|
Author: | Maarten ter Huurne |
Commit: | 3ed7d0461571cd54ed3450d910370614d0db13be |
Message: | MTD: NAND: JZ4740: Finished multi-bank support and added
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. Thanks to Paul Cercueil <paul@crapouillou.net> for the initial patch that performed autodetection. Additionally, the following problems were tackled: - add_mtd_device returns 0/1 instead of an error code - the mmio resource should be released if probe fails - avoid freeing non-existing busy gpio |
Files: |
drivers/mtd/nand/jz4740_nand.c (9 diffs) |
Change Details
drivers/mtd/nand/jz4740_nand.c | ||
---|---|---|
57 | 57 | #define JZ_NAND_MEM_CMD_OFFSET 0x08000 |
58 | 58 | #define JZ_NAND_MEM_ADDR_OFFSET 0x10000 |
59 | 59 | |
60 | #define JZ_NAND_NUM_BANKS 4 | |
61 | ||
60 | 62 | struct jz_nand { |
61 | 63 | struct mtd_info mtd; |
62 | 64 | struct nand_chip chip; |
63 | 65 | void __iomem *base; |
64 | 66 | struct resource *mem; |
65 | 67 | |
66 | void __iomem *bank_base[4]; | |
67 | struct resource *bank_mem[4]; | |
68 | unsigned char banks[JZ_NAND_NUM_BANKS]; | |
69 | void __iomem *bank_base[JZ_NAND_NUM_BANKS]; | |
70 | struct resource *bank_mem[JZ_NAND_NUM_BANKS]; | |
68 | 71 | |
69 | 72 | int selected_bank; |
70 | 73 | |
... | ... | |
90 | 93 | if (chipnr == -1) { |
91 | 94 | banknr = -1; |
92 | 95 | } else { |
93 | banknr = nand->pdata->banks[chipnr] - 1; | |
96 | banknr = nand->banks[chipnr] - 1; | |
94 | 97 | chip->IO_ADDR_R = nand->bank_base[banknr]; |
95 | 98 | chip->IO_ADDR_W = nand->bank_base[banknr]; |
96 | 99 | } |
... | ... | |
327 | 330 | release_mem_region(res->start, resource_size(res)); |
328 | 331 | } |
329 | 332 | |
330 | static void jz_nand_iounmap_banks( | |
331 | struct jz_nand *nand, unsigned char *banks, size_t num_banks) | |
332 | { | |
333 | int i; | |
333 | 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) { | |
334 | int ret; | |
335 | int gpio; | |
336 | char gpio_name[9]; | |
337 | char res_name[6]; | |
338 | uint32_t ctrl; | |
339 | struct mtd_info *mtd = &nand->mtd; | |
340 | struct nand_chip *chip = &nand->chip; | |
334 | 341 | |
335 | for (i = 0; i < num_banks; ++i) { | |
336 | unsigned char bank = banks[i]; | |
337 | jz_nand_iounmap_resource(nand->bank_mem[bank - 1], | |
338 | nand->bank_base[bank - 1]); | |
342 | /* Request GPIO port. */ | |
343 | gpio = JZ_GPIO_MEM_CS0 + bank - 1; | |
344 | sprintf(gpio_name, "NAND CS%d", bank); | |
345 | ret = gpio_request(gpio, gpio_name); | |
346 | if (ret) { | |
347 | dev_warn(&pdev->dev, | |
348 | "Failed to request %s gpio %d: %d\n", | |
349 | gpio_name, gpio, ret); | |
350 | goto notfound_gpio; | |
339 | 351 | } |
340 | } | |
341 | 352 | |
342 | static int jz_nand_ioremap_banks(struct platform_device *pdev, | |
343 | struct jz_nand *nand, unsigned char *banks, size_t num_banks) | |
344 | { | |
345 | int ret; | |
346 | char name[6]; | |
347 | int i; | |
353 | /* Request I/O resource. */ | |
354 | sprintf(res_name, "bank%d", bank); | |
355 | ret = jz_nand_ioremap_resource(pdev, res_name, | |
356 | &nand->bank_mem[bank - 1], | |
357 | &nand->bank_base[bank - 1]); | |
358 | if (ret) | |
359 | goto notfound_resource; | |
348 | 360 | |
349 | for (i = 0; i < num_banks; ++i) { | |
350 | unsigned char bank = banks[i]; | |
351 | sprintf(name, "bank%d", bank); | |
361 | /* Enable chip in bank. */ | |
362 | jz_gpio_set_function(gpio, JZ_GPIO_FUNC_MEM_CS0); | |
363 | ctrl = readl(nand->base + JZ_REG_NAND_CTRL); | |
364 | ctrl |= JZ_NAND_CTRL_ENABLE_CHIP(bank - 1); | |
365 | writel(ctrl, nand->base + JZ_REG_NAND_CTRL); | |
352 | 366 | |
353 | ret = jz_nand_ioremap_resource(pdev, name, | |
354 | &nand->bank_mem[bank - 1], | |
355 | &nand->bank_base[bank - 1]); | |
356 | if (ret) { | |
357 | jz_nand_iounmap_banks(nand, banks, i); | |
358 | return ret; | |
367 | if (chipnr == 0) { | |
368 | /* Detect first chip. */ | |
369 | ret = nand_scan_ident(mtd, 1, NULL); | |
370 | if (ret) | |
371 | goto notfound_id; | |
372 | ||
373 | /* Retrieve the IDs from the first chip. */ | |
374 | chip->select_chip(mtd, 0); | |
375 | chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); | |
376 | chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); | |
377 | *nand_maf_id = chip->read_byte(mtd); | |
378 | *nand_dev_id = chip->read_byte(mtd); | |
379 | } else { | |
380 | /* Detect additional chip. */ | |
381 | chip->select_chip(mtd, chipnr); | |
382 | chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); | |
383 | chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); | |
384 | if (*nand_maf_id != chip->read_byte(mtd) | |
385 | || *nand_dev_id != chip->read_byte(mtd)) { | |
386 | ret = -ENODEV; | |
387 | goto notfound_id; | |
359 | 388 | } |
389 | ||
390 | /* Update size of the MTD. */ | |
391 | chip->numchips++; | |
392 | mtd->size += chip->chipsize; | |
360 | 393 | } |
361 | 394 | |
395 | dev_info(&pdev->dev, "Found chip %i on bank %i\n", chipnr, bank); | |
362 | 396 | return 0; |
397 | ||
398 | notfound_id: | |
399 | dev_info(&pdev->dev, "No chip found on bank %i\n", bank); | |
400 | ctrl &= ~(JZ_NAND_CTRL_ENABLE_CHIP(bank - 1)); | |
401 | writel(ctrl, nand->base + JZ_REG_NAND_CTRL); | |
402 | jz_gpio_set_function(gpio, JZ_GPIO_FUNC_NONE); | |
403 | jz_nand_iounmap_resource(nand->bank_mem[bank - 1], | |
404 | nand->bank_base[bank - 1]); | |
405 | notfound_resource: | |
406 | gpio_free(gpio); | |
407 | notfound_gpio: | |
408 | return ret; | |
363 | 409 | } |
364 | 410 | |
365 | 411 | static int __devinit jz_nand_probe(struct platform_device *pdev) |
... | ... | |
373 | 419 | struct mtd_partition *partition_info; |
374 | 420 | int num_partitions = 0; |
375 | 421 | #endif |
376 | size_t num_banks; | |
377 | unsigned char bank; | |
378 | uint32_t ctrl; | |
422 | size_t chipnr, bank_idx; | |
423 | uint8_t nand_maf_id = 0, nand_dev_id = 0; | |
379 | 424 | |
380 | 425 | nand = kzalloc(sizeof(*nand), GFP_KERNEL); |
381 | 426 | if (!nand) { |
... | ... | |
423 | 468 | nand->pdata = pdata; |
424 | 469 | platform_set_drvdata(pdev, nand); |
425 | 470 | |
426 | if (pdata->banks[0] == 0) | |
427 | pdata->banks[0] = 1; | |
428 | ||
429 | ctrl = 0; | |
430 | for (num_banks = 0; num_banks < 4 ; ++num_banks) { | |
431 | int gpio; | |
432 | char name[9]; | |
433 | ||
434 | bank = pdata->banks[num_banks]; | |
471 | /* We are going to autodetect NAND chips in the banks specified in the | |
472 | * platform data. Although nand_scan_ident() can detect multiple chips, | |
473 | * it requires those chips to be numbered consecuitively, which is not | |
474 | * always the case for external memory banks. And a fixed chip-to-bank | |
475 | * mapping is not practical either, since for example Dingoo units | |
476 | * produced at different times have NAND chips in different banks. | |
477 | */ | |
478 | chipnr = 0; | |
479 | for (bank_idx = 0; bank_idx < JZ_NAND_NUM_BANKS; bank_idx++) { | |
480 | unsigned char bank; | |
481 | ||
482 | /* If there is no platform data, look for NAND in bank 1, | |
483 | * which is the most likely bank since it is the only one | |
484 | * that can be booted from. | |
485 | */ | |
486 | bank = pdata ? pdata->banks[bank_idx] : bank_idx ^ 1; | |
435 | 487 | if (bank == 0) |
436 | 488 | break; |
437 | if (bank > 4) { | |
438 | dev_err(&pdev->dev, "Non-existing bank: %d\n", bank); | |
439 | ret = -EINVAL; | |
440 | goto err_gpio_free; | |
489 | if (bank > JZ_NAND_NUM_BANKS) { | |
490 | dev_warn(&pdev->dev, | |
491 | "Skipping non-existing bank: %d\n", bank); | |
492 | continue; | |
441 | 493 | } |
442 | ||
443 | sprintf(name, "NAND CS%d", bank); | |
444 | gpio = JZ_GPIO_MEM_CS0 + bank - 1; | |
445 | ret = gpio_request(gpio, name); | |
446 | if (ret) { | |
447 | dev_err(&pdev->dev, | |
448 | "Failed to request %s gpio %d: %d\n", | |
449 | name, gpio, ret); | |
450 | goto err_gpio_free; | |
451 | } | |
452 | jz_gpio_set_function(gpio, JZ_GPIO_FUNC_MEM_CS0); | |
453 | ||
454 | ctrl |= JZ_NAND_CTRL_ENABLE_CHIP(bank - 1); | |
494 | /* The detection routine will directly or indirectly call | |
495 | * jz_nand_select_chip(), so nand->banks has to contain the | |
496 | * bank we're checking. | |
497 | */ | |
498 | nand->banks[chipnr] = bank; | |
499 | if (jz_nand_detect_bank(pdev, nand, bank, chipnr, | |
500 | &nand_maf_id, &nand_dev_id) == 0) | |
501 | chipnr++; | |
502 | else | |
503 | nand->banks[chipnr] = 0; | |
455 | 504 | } |
456 | ||
457 | ret = jz_nand_ioremap_banks(pdev, nand, pdata->banks, num_banks); | |
458 | if (ret) | |
459 | goto err_gpio_free; | |
460 | ||
461 | chip->IO_ADDR_R = nand->bank_base[pdata->banks[0] - 1]; | |
462 | chip->IO_ADDR_W = nand->bank_base[pdata->banks[0] - 1]; | |
463 | ||
464 | writel(ctrl, nand->base + JZ_REG_NAND_CTRL); | |
465 | ||
466 | ret = nand_scan_ident(mtd, num_banks, NULL); | |
467 | if (ret) { | |
468 | dev_err(&pdev->dev, "Failed to scan nand\n"); | |
469 | goto err_iounmap_banks; | |
505 | if (chipnr == 0) { | |
506 | dev_err(&pdev->dev, "No NAND chips found\n"); | |
507 | goto err_gpio_busy; | |
470 | 508 | } |
471 | 509 | |
472 | 510 | if (pdata && pdata->ident_callback) { |
... | ... | |
476 | 514 | |
477 | 515 | ret = nand_scan_tail(mtd); |
478 | 516 | if (ret) { |
479 | dev_err(&pdev->dev, "Failed to scan nand\n"); | |
480 | goto err_iounmap_banks; | |
517 | dev_err(&pdev->dev, "Failed to scan NAND\n"); | |
518 | goto err_unclaim_banks; | |
481 | 519 | } |
482 | 520 | |
483 | 521 | #ifdef CONFIG_MTD_PARTITIONS |
... | ... | |
494 | 532 | ret = add_mtd_partitions(mtd, partition_info, num_partitions); |
495 | 533 | else |
496 | 534 | #endif |
497 | ret = add_mtd_device(mtd); | |
535 | ret = add_mtd_device(mtd) ? -ENODEV : 0; | |
498 | 536 | |
499 | 537 | if (ret) { |
500 | 538 | dev_err(&pdev->dev, "Failed to add mtd device\n"); |
... | ... | |
506 | 544 | return 0; |
507 | 545 | |
508 | 546 | err_nand_release: |
509 | nand_release(&nand->mtd); | |
510 | err_iounmap_banks: | |
511 | jz_nand_iounmap_banks(nand, pdata->banks, num_banks); | |
512 | err_gpio_free: | |
513 | platform_set_drvdata(pdev, NULL); | |
514 | gpio_free(pdata->busy_gpio); | |
515 | while (num_banks--) { | |
516 | bank = pdata->banks[num_banks]; | |
547 | nand_release(mtd); | |
548 | err_unclaim_banks: | |
549 | while (chipnr--) { | |
550 | unsigned char bank = nand->banks[chipnr]; | |
517 | 551 | gpio_free(JZ_GPIO_MEM_CS0 + bank - 1); |
552 | jz_nand_iounmap_resource(nand->bank_mem[bank - 1], | |
553 | nand->bank_base[bank - 1]); | |
518 | 554 | } |
555 | writel(0, nand->base + JZ_REG_NAND_CTRL); | |
556 | err_gpio_busy: | |
557 | if (pdata && gpio_is_valid(pdata->busy_gpio)) | |
558 | gpio_free(pdata->busy_gpio); | |
559 | platform_set_drvdata(pdev, NULL); | |
519 | 560 | err_iounmap_mmio: |
520 | iounmap(nand->base); | |
561 | jz_nand_iounmap_resource(nand->mem, nand->base); | |
521 | 562 | err_free: |
522 | 563 | kfree(nand); |
523 | 564 | return ret; |
... | ... | |
527 | 568 | { |
528 | 569 | struct jz_nand *nand = platform_get_drvdata(pdev); |
529 | 570 | struct jz_nand_platform_data *pdata = pdev->dev.platform_data; |
530 | int i; | |
571 | size_t i; | |
531 | 572 | |
532 | 573 | nand_release(&nand->mtd); |
533 | 574 | |
534 | 575 | /* Deassert and disable all chips */ |
535 | 576 | writel(0, nand->base + JZ_REG_NAND_CTRL); |
536 | 577 | |
537 | for (i = 0; i < 4; ++i) | |
538 | if (nand->bank_base[i] != 0) { | |
539 | jz_nand_iounmap_resource(nand->bank_mem[i], | |
540 | nand->bank_base[i]); | |
541 | gpio_free(JZ_GPIO_MEM_CS0 + i); | |
578 | for (i = 0; i < JZ_NAND_NUM_BANKS; ++i) { | |
579 | unsigned char bank = nand->banks[i]; | |
580 | if (bank != 0) { | |
581 | jz_nand_iounmap_resource(nand->bank_mem[bank - 1], | |
582 | nand->bank_base[bank - 1]); | |
583 | gpio_free(JZ_GPIO_MEM_CS0 + bank - 1); | |
542 | 584 | } |
543 | gpio_free(pdata->busy_gpio); | |
585 | } | |
586 | if (pdata && gpio_is_valid(pdata->busy_gpio)) | |
587 | gpio_free(pdata->busy_gpio); | |
544 | 588 | |
545 | 589 | jz_nand_iounmap_resource(nand->mem, nand->base); |
546 | 590 |
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