diff options
89 files changed, 6145 insertions, 682 deletions
diff --git a/Documentation/powerpc/dts-bindings/4xx/ndfc.txt b/Documentation/powerpc/dts-bindings/4xx/ndfc.txt new file mode 100644 index 000000000000..869f0b5f16e8 --- /dev/null +++ b/Documentation/powerpc/dts-bindings/4xx/ndfc.txt | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | AMCC NDFC (NanD Flash Controller) | ||
| 2 | |||
| 3 | Required properties: | ||
| 4 | - compatible : "ibm,ndfc". | ||
| 5 | - reg : should specify chip select and size used for the chip (0x2000). | ||
| 6 | |||
| 7 | Optional properties: | ||
| 8 | - ccr : NDFC config and control register value (default 0). | ||
| 9 | - bank-settings : NDFC bank configuration register value (default 0). | ||
| 10 | |||
| 11 | Notes: | ||
| 12 | - partition(s) - follows the OF MTD standard for partitions | ||
| 13 | |||
| 14 | Example: | ||
| 15 | |||
| 16 | ndfc@1,0 { | ||
| 17 | compatible = "ibm,ndfc"; | ||
| 18 | reg = <0x00000001 0x00000000 0x00002000>; | ||
| 19 | ccr = <0x00001000>; | ||
| 20 | bank-settings = <0x80002222>; | ||
| 21 | #address-cells = <1>; | ||
| 22 | #size-cells = <1>; | ||
| 23 | |||
| 24 | nand { | ||
| 25 | #address-cells = <1>; | ||
| 26 | #size-cells = <1>; | ||
| 27 | |||
| 28 | partition@0 { | ||
| 29 | label = "kernel"; | ||
| 30 | reg = <0x00000000 0x00200000>; | ||
| 31 | }; | ||
| 32 | partition@200000 { | ||
| 33 | label = "root"; | ||
| 34 | reg = <0x00200000 0x03E00000>; | ||
| 35 | }; | ||
| 36 | }; | ||
| 37 | }; | ||
| 38 | |||
| 39 | |||
diff --git a/arch/arm/mach-pxa/corgi.c b/arch/arm/mach-pxa/corgi.c index c5e28a46b292..a8d91b6c136b 100644 --- a/arch/arm/mach-pxa/corgi.c +++ b/arch/arm/mach-pxa/corgi.c | |||
| @@ -27,6 +27,7 @@ | |||
| 27 | #include <linux/spi/spi.h> | 27 | #include <linux/spi/spi.h> |
| 28 | #include <linux/spi/ads7846.h> | 28 | #include <linux/spi/ads7846.h> |
| 29 | #include <linux/spi/corgi_lcd.h> | 29 | #include <linux/spi/corgi_lcd.h> |
| 30 | #include <linux/mtd/sharpsl.h> | ||
| 30 | #include <video/w100fb.h> | 31 | #include <video/w100fb.h> |
| 31 | 32 | ||
| 32 | #include <asm/setup.h> | 33 | #include <asm/setup.h> |
| @@ -542,6 +543,55 @@ err_free_1: | |||
| 542 | static inline void corgi_init_spi(void) {} | 543 | static inline void corgi_init_spi(void) {} |
| 543 | #endif | 544 | #endif |
| 544 | 545 | ||
| 546 | static struct mtd_partition sharpsl_nand_partitions[] = { | ||
| 547 | { | ||
| 548 | .name = "System Area", | ||
| 549 | .offset = 0, | ||
| 550 | .size = 7 * 1024 * 1024, | ||
| 551 | }, | ||
| 552 | { | ||
| 553 | .name = "Root Filesystem", | ||
| 554 | .offset = 7 * 1024 * 1024, | ||
| 555 | .size = 25 * 1024 * 1024, | ||
| 556 | }, | ||
| 557 | { | ||
| 558 | .name = "Home Filesystem", | ||
| 559 | .offset = MTDPART_OFS_APPEND, | ||
| 560 | .size = MTDPART_SIZ_FULL, | ||
| 561 | }, | ||
| 562 | }; | ||
| 563 | |||
| 564 | static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; | ||
| 565 | |||
| 566 | static struct nand_bbt_descr sharpsl_bbt = { | ||
| 567 | .options = 0, | ||
| 568 | .offs = 4, | ||
| 569 | .len = 2, | ||
| 570 | .pattern = scan_ff_pattern | ||
| 571 | }; | ||
| 572 | |||
| 573 | static struct sharpsl_nand_platform_data sharpsl_nand_platform_data = { | ||
| 574 | .badblock_pattern = &sharpsl_bbt, | ||
| 575 | .partitions = sharpsl_nand_partitions, | ||
| 576 | .nr_partitions = ARRAY_SIZE(sharpsl_nand_partitions), | ||
| 577 | }; | ||
| 578 | |||
| 579 | static struct resource sharpsl_nand_resources[] = { | ||
| 580 | { | ||
| 581 | .start = 0x0C000000, | ||
| 582 | .end = 0x0C000FFF, | ||
| 583 | .flags = IORESOURCE_MEM, | ||
| 584 | }, | ||
| 585 | }; | ||
| 586 | |||
| 587 | static struct platform_device sharpsl_nand_device = { | ||
| 588 | .name = "sharpsl-nand", | ||
| 589 | .id = -1, | ||
| 590 | .resource = sharpsl_nand_resources, | ||
| 591 | .num_resources = ARRAY_SIZE(sharpsl_nand_resources), | ||
| 592 | .dev.platform_data = &sharpsl_nand_platform_data, | ||
| 593 | }; | ||
| 594 | |||
| 545 | static struct mtd_partition sharpsl_rom_parts[] = { | 595 | static struct mtd_partition sharpsl_rom_parts[] = { |
| 546 | { | 596 | { |
| 547 | .name ="Boot PROM Filesystem", | 597 | .name ="Boot PROM Filesystem", |
| @@ -577,6 +627,7 @@ static struct platform_device *devices[] __initdata = { | |||
| 577 | &corgifb_device, | 627 | &corgifb_device, |
| 578 | &corgikbd_device, | 628 | &corgikbd_device, |
| 579 | &corgiled_device, | 629 | &corgiled_device, |
| 630 | &sharpsl_nand_device, | ||
| 580 | &sharpsl_rom_device, | 631 | &sharpsl_rom_device, |
| 581 | }; | 632 | }; |
| 582 | 633 | ||
| @@ -617,6 +668,9 @@ static void __init corgi_init(void) | |||
| 617 | 668 | ||
| 618 | platform_scoop_config = &corgi_pcmcia_config; | 669 | platform_scoop_config = &corgi_pcmcia_config; |
| 619 | 670 | ||
| 671 | if (machine_is_husky()) | ||
| 672 | sharpsl_nand_partitions[1].size = 53 * 1024 * 1024; | ||
| 673 | |||
| 620 | platform_add_devices(devices, ARRAY_SIZE(devices)); | 674 | platform_add_devices(devices, ARRAY_SIZE(devices)); |
| 621 | } | 675 | } |
| 622 | 676 | ||
diff --git a/arch/arm/mach-pxa/poodle.c b/arch/arm/mach-pxa/poodle.c index ae88855bf974..f9093beba752 100644 --- a/arch/arm/mach-pxa/poodle.c +++ b/arch/arm/mach-pxa/poodle.c | |||
| @@ -24,6 +24,7 @@ | |||
| 24 | #include <linux/gpio.h> | 24 | #include <linux/gpio.h> |
| 25 | #include <linux/spi/spi.h> | 25 | #include <linux/spi/spi.h> |
| 26 | #include <linux/spi/ads7846.h> | 26 | #include <linux/spi/ads7846.h> |
| 27 | #include <linux/mtd/sharpsl.h> | ||
| 27 | 28 | ||
| 28 | #include <mach/hardware.h> | 29 | #include <mach/hardware.h> |
| 29 | #include <asm/mach-types.h> | 30 | #include <asm/mach-types.h> |
| @@ -414,6 +415,55 @@ static struct pxafb_mach_info poodle_fb_info = { | |||
| 414 | .lcd_conn = LCD_COLOR_TFT_16BPP, | 415 | .lcd_conn = LCD_COLOR_TFT_16BPP, |
| 415 | }; | 416 | }; |
| 416 | 417 | ||
| 418 | static struct mtd_partition sharpsl_nand_partitions[] = { | ||
| 419 | { | ||
| 420 | .name = "System Area", | ||
| 421 | .offset = 0, | ||
| 422 | .size = 7 * 1024 * 1024, | ||
| 423 | }, | ||
| 424 | { | ||
| 425 | .name = "Root Filesystem", | ||
| 426 | .offset = 7 * 1024 * 1024, | ||
| 427 | .size = 22 * 1024 * 1024, | ||
| 428 | }, | ||
| 429 | { | ||
| 430 | .name = "Home Filesystem", | ||
| 431 | .offset = MTDPART_OFS_APPEND, | ||
| 432 | .size = MTDPART_SIZ_FULL, | ||
| 433 | }, | ||
| 434 | }; | ||
| 435 | |||
| 436 | static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; | ||
| 437 | |||
| 438 | static struct nand_bbt_descr sharpsl_bbt = { | ||
| 439 | .options = 0, | ||
| 440 | .offs = 4, | ||
| 441 | .len = 2, | ||
| 442 | .pattern = scan_ff_pattern | ||
| 443 | }; | ||
| 444 | |||
| 445 | static struct sharpsl_nand_platform_data sharpsl_nand_platform_data = { | ||
| 446 | .badblock_pattern = &sharpsl_bbt, | ||
| 447 | .partitions = sharpsl_nand_partitions, | ||
| 448 | .nr_partitions = ARRAY_SIZE(sharpsl_nand_partitions), | ||
| 449 | }; | ||
| 450 | |||
| 451 | static struct resource sharpsl_nand_resources[] = { | ||
| 452 | { | ||
| 453 | .start = 0x0C000000, | ||
| 454 | .end = 0x0C000FFF, | ||
| 455 | .flags = IORESOURCE_MEM, | ||
| 456 | }, | ||
| 457 | }; | ||
| 458 | |||
| 459 | static struct platform_device sharpsl_nand_device = { | ||
| 460 | .name = "sharpsl-nand", | ||
| 461 | .id = -1, | ||
| 462 | .resource = sharpsl_nand_resources, | ||
| 463 | .num_resources = ARRAY_SIZE(sharpsl_nand_resources), | ||
| 464 | .dev.platform_data = &sharpsl_nand_platform_data, | ||
| 465 | }; | ||
| 466 | |||
| 417 | static struct mtd_partition sharpsl_rom_parts[] = { | 467 | static struct mtd_partition sharpsl_rom_parts[] = { |
| 418 | { | 468 | { |
| 419 | .name ="Boot PROM Filesystem", | 469 | .name ="Boot PROM Filesystem", |
| @@ -447,6 +497,7 @@ static struct platform_device sharpsl_rom_device = { | |||
| 447 | static struct platform_device *devices[] __initdata = { | 497 | static struct platform_device *devices[] __initdata = { |
| 448 | &poodle_locomo_device, | 498 | &poodle_locomo_device, |
| 449 | &poodle_scoop_device, | 499 | &poodle_scoop_device, |
| 500 | &sharpsl_nand_device, | ||
| 450 | &sharpsl_rom_device, | 501 | &sharpsl_rom_device, |
| 451 | }; | 502 | }; |
| 452 | 503 | ||
diff --git a/arch/arm/mach-pxa/spitz.c b/arch/arm/mach-pxa/spitz.c index 7299d87a1cb3..6d447c9ce8ab 100644 --- a/arch/arm/mach-pxa/spitz.c +++ b/arch/arm/mach-pxa/spitz.c | |||
| @@ -31,6 +31,7 @@ | |||
| 31 | #include <linux/spi/spi.h> | 31 | #include <linux/spi/spi.h> |
| 32 | #include <linux/spi/ads7846.h> | 32 | #include <linux/spi/ads7846.h> |
| 33 | #include <linux/spi/corgi_lcd.h> | 33 | #include <linux/spi/corgi_lcd.h> |
| 34 | #include <linux/mtd/sharpsl.h> | ||
| 34 | 35 | ||
| 35 | #include <asm/setup.h> | 36 | #include <asm/setup.h> |
| 36 | #include <asm/memory.h> | 37 | #include <asm/memory.h> |
| @@ -613,6 +614,54 @@ static struct pxafb_mach_info spitz_pxafb_info = { | |||
| 613 | .lcd_conn = LCD_COLOR_TFT_16BPP | LCD_ALTERNATE_MAPPING, | 614 | .lcd_conn = LCD_COLOR_TFT_16BPP | LCD_ALTERNATE_MAPPING, |
| 614 | }; | 615 | }; |
| 615 | 616 | ||
| 617 | static struct mtd_partition sharpsl_nand_partitions[] = { | ||
| 618 | { | ||
| 619 | .name = "System Area", | ||
| 620 | .offset = 0, | ||
| 621 | .size = 7 * 1024 * 1024, | ||
| 622 | }, | ||
| 623 | { | ||
| 624 | .name = "Root Filesystem", | ||
| 625 | .offset = 7 * 1024 * 1024, | ||
| 626 | }, | ||
| 627 | { | ||
| 628 | .name = "Home Filesystem", | ||
| 629 | .offset = MTDPART_OFS_APPEND, | ||
| 630 | .size = MTDPART_SIZ_FULL, | ||
| 631 | }, | ||
| 632 | }; | ||
| 633 | |||
| 634 | static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; | ||
| 635 | |||
| 636 | static struct nand_bbt_descr sharpsl_bbt = { | ||
| 637 | .options = 0, | ||
| 638 | .offs = 4, | ||
| 639 | .len = 2, | ||
| 640 | .pattern = scan_ff_pattern | ||
| 641 | }; | ||
| 642 | |||
| 643 | static struct sharpsl_nand_platform_data sharpsl_nand_platform_data = { | ||
| 644 | .badblock_pattern = &sharpsl_bbt, | ||
| 645 | .partitions = sharpsl_nand_partitions, | ||
| 646 | .nr_partitions = ARRAY_SIZE(sharpsl_nand_partitions), | ||
| 647 | }; | ||
| 648 | |||
| 649 | static struct resource sharpsl_nand_resources[] = { | ||
| 650 | { | ||
| 651 | .start = 0x0C000000, | ||
| 652 | .end = 0x0C000FFF, | ||
| 653 | .flags = IORESOURCE_MEM, | ||
| 654 | }, | ||
| 655 | }; | ||
| 656 | |||
| 657 | static struct platform_device sharpsl_nand_device = { | ||
| 658 | .name = "sharpsl-nand", | ||
| 659 | .id = -1, | ||
| 660 | .resource = sharpsl_nand_resources, | ||
| 661 | .num_resources = ARRAY_SIZE(sharpsl_nand_resources), | ||
| 662 | .dev.platform_data = &sharpsl_nand_platform_data, | ||
| 663 | }; | ||
| 664 | |||
| 616 | 665 | ||
| 617 | static struct mtd_partition sharpsl_rom_parts[] = { | 666 | static struct mtd_partition sharpsl_rom_parts[] = { |
| 618 | { | 667 | { |
| @@ -648,6 +697,7 @@ static struct platform_device *devices[] __initdata = { | |||
| 648 | &spitzscoop_device, | 697 | &spitzscoop_device, |
| 649 | &spitzkbd_device, | 698 | &spitzkbd_device, |
| 650 | &spitzled_device, | 699 | &spitzled_device, |
| 700 | &sharpsl_nand_device, | ||
| 651 | &sharpsl_rom_device, | 701 | &sharpsl_rom_device, |
| 652 | }; | 702 | }; |
| 653 | 703 | ||
| @@ -671,6 +721,14 @@ static void __init common_init(void) | |||
| 671 | pm_power_off = spitz_poweroff; | 721 | pm_power_off = spitz_poweroff; |
| 672 | arm_pm_restart = spitz_restart; | 722 | arm_pm_restart = spitz_restart; |
| 673 | 723 | ||
| 724 | if (machine_is_spitz()) { | ||
| 725 | sharpsl_nand_partitions[1].size = 5 * 1024 * 1024; | ||
| 726 | } else if (machine_is_akita()) { | ||
| 727 | sharpsl_nand_partitions[1].size = 58 * 1024 * 1024; | ||
| 728 | } else if (machine_is_borzoi()) { | ||
| 729 | sharpsl_nand_partitions[1].size = 32 * 1024 * 1024; | ||
| 730 | } | ||
| 731 | |||
| 674 | PMCR = 0x00; | 732 | PMCR = 0x00; |
| 675 | 733 | ||
| 676 | /* Stop 3.6MHz and drive HIGH to PCMCIA and CS */ | 734 | /* Stop 3.6MHz and drive HIGH to PCMCIA and CS */ |
| @@ -715,10 +773,29 @@ static struct i2c_board_info akita_i2c_board_info[] = { | |||
| 715 | }, | 773 | }, |
| 716 | }; | 774 | }; |
| 717 | 775 | ||
| 776 | static struct nand_bbt_descr sharpsl_akita_bbt = { | ||
| 777 | .options = 0, | ||
| 778 | .offs = 4, | ||
| 779 | .len = 1, | ||
| 780 | .pattern = scan_ff_pattern | ||
| 781 | }; | ||
| 782 | |||
| 783 | static struct nand_ecclayout akita_oobinfo = { | ||
| 784 | .eccbytes = 24, | ||
| 785 | .eccpos = { | ||
| 786 | 0x5, 0x1, 0x2, 0x3, 0x6, 0x7, 0x15, 0x11, | ||
| 787 | 0x12, 0x13, 0x16, 0x17, 0x25, 0x21, 0x22, 0x23, | ||
| 788 | 0x26, 0x27, 0x35, 0x31, 0x32, 0x33, 0x36, 0x37}, | ||
| 789 | .oobfree = {{0x08, 0x09}} | ||
| 790 | }; | ||
| 791 | |||
| 718 | static void __init akita_init(void) | 792 | static void __init akita_init(void) |
| 719 | { | 793 | { |
| 720 | spitz_ficp_platform_data.transceiver_mode = akita_irda_transceiver_mode; | 794 | spitz_ficp_platform_data.transceiver_mode = akita_irda_transceiver_mode; |
| 721 | 795 | ||
| 796 | sharpsl_nand_platform_data.badblock_pattern = &sharpsl_akita_bbt; | ||
| 797 | sharpsl_nand_platform_data.ecc_layout = &akita_oobinfo; | ||
| 798 | |||
| 722 | /* We just pretend the second element of the array doesn't exist */ | 799 | /* We just pretend the second element of the array doesn't exist */ |
| 723 | spitz_pcmcia_config.num_devs = 1; | 800 | spitz_pcmcia_config.num_devs = 1; |
| 724 | platform_scoop_config = &spitz_pcmcia_config; | 801 | platform_scoop_config = &spitz_pcmcia_config; |
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index a90d50c2c3e5..7d04fb9ddcaa 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig | |||
| @@ -45,6 +45,14 @@ config MTD_PARTITIONS | |||
| 45 | devices. Partitioning on NFTL 'devices' is a different - that's the | 45 | devices. Partitioning on NFTL 'devices' is a different - that's the |
| 46 | 'normal' form of partitioning used on a block device. | 46 | 'normal' form of partitioning used on a block device. |
| 47 | 47 | ||
| 48 | config MTD_TESTS | ||
| 49 | tristate "MTD tests support" | ||
| 50 | depends on m | ||
| 51 | help | ||
| 52 | This option includes various MTD tests into compilation. The tests | ||
| 53 | should normally be compiled as kernel modules. The modules perform | ||
| 54 | various checks and verifications when loaded. | ||
| 55 | |||
| 48 | config MTD_REDBOOT_PARTS | 56 | config MTD_REDBOOT_PARTS |
| 49 | tristate "RedBoot partition table parsing" | 57 | tristate "RedBoot partition table parsing" |
| 50 | depends on MTD_PARTITIONS | 58 | depends on MTD_PARTITIONS |
| @@ -316,6 +324,8 @@ source "drivers/mtd/nand/Kconfig" | |||
| 316 | 324 | ||
| 317 | source "drivers/mtd/onenand/Kconfig" | 325 | source "drivers/mtd/onenand/Kconfig" |
| 318 | 326 | ||
| 327 | source "drivers/mtd/lpddr/Kconfig" | ||
| 328 | |||
| 319 | source "drivers/mtd/ubi/Kconfig" | 329 | source "drivers/mtd/ubi/Kconfig" |
| 320 | 330 | ||
| 321 | endif # MTD | 331 | endif # MTD |
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 4b77335715f0..4521b1ecce45 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile | |||
| @@ -29,6 +29,6 @@ obj-$(CONFIG_MTD_OOPS) += mtdoops.o | |||
| 29 | nftl-objs := nftlcore.o nftlmount.o | 29 | nftl-objs := nftlcore.o nftlmount.o |
| 30 | inftl-objs := inftlcore.o inftlmount.o | 30 | inftl-objs := inftlcore.o inftlmount.o |
| 31 | 31 | ||
| 32 | obj-y += chips/ maps/ devices/ nand/ onenand/ | 32 | obj-y += chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/ |
| 33 | 33 | ||
| 34 | obj-$(CONFIG_MTD_UBI) += ubi/ | 34 | obj-$(CONFIG_MTD_UBI) += ubi/ |
diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index c93a8be5d5f1..f5ab6fa1057b 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c | |||
| @@ -58,8 +58,8 @@ static int cfi_intelext_write_buffers(struct mtd_info *, loff_t, size_t, size_t | |||
| 58 | static int cfi_intelext_writev(struct mtd_info *, const struct kvec *, unsigned long, loff_t, size_t *); | 58 | static int cfi_intelext_writev(struct mtd_info *, const struct kvec *, unsigned long, loff_t, size_t *); |
| 59 | static int cfi_intelext_erase_varsize(struct mtd_info *, struct erase_info *); | 59 | static int cfi_intelext_erase_varsize(struct mtd_info *, struct erase_info *); |
| 60 | static void cfi_intelext_sync (struct mtd_info *); | 60 | static void cfi_intelext_sync (struct mtd_info *); |
| 61 | static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len); | 61 | static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); |
| 62 | static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len); | 62 | static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); |
| 63 | #ifdef CONFIG_MTD_OTP | 63 | #ifdef CONFIG_MTD_OTP |
| 64 | static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); | 64 | static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); |
| 65 | static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); | 65 | static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); |
| @@ -558,8 +558,8 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd) | |||
| 558 | } | 558 | } |
| 559 | 559 | ||
| 560 | for (i=0; i<mtd->numeraseregions;i++){ | 560 | for (i=0; i<mtd->numeraseregions;i++){ |
| 561 | printk(KERN_DEBUG "erase region %d: offset=0x%x,size=0x%x,blocks=%d\n", | 561 | printk(KERN_DEBUG "erase region %d: offset=0x%llx,size=0x%x,blocks=%d\n", |
| 562 | i,mtd->eraseregions[i].offset, | 562 | i,(unsigned long long)mtd->eraseregions[i].offset, |
| 563 | mtd->eraseregions[i].erasesize, | 563 | mtd->eraseregions[i].erasesize, |
| 564 | mtd->eraseregions[i].numblocks); | 564 | mtd->eraseregions[i].numblocks); |
| 565 | } | 565 | } |
| @@ -2058,7 +2058,7 @@ out: put_chip(map, chip, adr); | |||
| 2058 | return ret; | 2058 | return ret; |
| 2059 | } | 2059 | } |
| 2060 | 2060 | ||
| 2061 | static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len) | 2061 | static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) |
| 2062 | { | 2062 | { |
| 2063 | int ret; | 2063 | int ret; |
| 2064 | 2064 | ||
| @@ -2082,7 +2082,7 @@ static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len) | |||
| 2082 | return ret; | 2082 | return ret; |
| 2083 | } | 2083 | } |
| 2084 | 2084 | ||
| 2085 | static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) | 2085 | static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) |
| 2086 | { | 2086 | { |
| 2087 | int ret; | 2087 | int ret; |
| 2088 | 2088 | ||
diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index d74ec46aa032..94bb61e19047 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c | |||
| @@ -71,8 +71,8 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr | |||
| 71 | static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr); | 71 | static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr); |
| 72 | #include "fwh_lock.h" | 72 | #include "fwh_lock.h" |
| 73 | 73 | ||
| 74 | static int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, size_t len); | 74 | static int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); |
| 75 | static int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, size_t len); | 75 | static int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); |
| 76 | 76 | ||
| 77 | static struct mtd_chip_driver cfi_amdstd_chipdrv = { | 77 | static struct mtd_chip_driver cfi_amdstd_chipdrv = { |
| 78 | .probe = NULL, /* Not usable directly */ | 78 | .probe = NULL, /* Not usable directly */ |
| @@ -322,6 +322,14 @@ static struct cfi_fixup fixup_table[] = { | |||
| 322 | }; | 322 | }; |
| 323 | 323 | ||
| 324 | 324 | ||
| 325 | static void cfi_fixup_major_minor(struct cfi_private *cfi, | ||
| 326 | struct cfi_pri_amdstd *extp) | ||
| 327 | { | ||
| 328 | if (cfi->mfr == CFI_MFR_SAMSUNG && cfi->id == 0x257e && | ||
| 329 | extp->MajorVersion == '0') | ||
| 330 | extp->MajorVersion = '1'; | ||
| 331 | } | ||
| 332 | |||
| 325 | struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) | 333 | struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) |
| 326 | { | 334 | { |
| 327 | struct cfi_private *cfi = map->fldrv_priv; | 335 | struct cfi_private *cfi = map->fldrv_priv; |
| @@ -363,6 +371,8 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) | |||
| 363 | return NULL; | 371 | return NULL; |
| 364 | } | 372 | } |
| 365 | 373 | ||
| 374 | cfi_fixup_major_minor(cfi, extp); | ||
| 375 | |||
| 366 | if (extp->MajorVersion != '1' || | 376 | if (extp->MajorVersion != '1' || |
| 367 | (extp->MinorVersion < '0' || extp->MinorVersion > '4')) { | 377 | (extp->MinorVersion < '0' || extp->MinorVersion > '4')) { |
| 368 | printk(KERN_ERR " Unknown Amd/Fujitsu Extended Query " | 378 | printk(KERN_ERR " Unknown Amd/Fujitsu Extended Query " |
| @@ -1774,12 +1784,12 @@ out_unlock: | |||
| 1774 | return ret; | 1784 | return ret; |
| 1775 | } | 1785 | } |
| 1776 | 1786 | ||
| 1777 | static int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, size_t len) | 1787 | static int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) |
| 1778 | { | 1788 | { |
| 1779 | return cfi_varsize_frob(mtd, do_atmel_lock, ofs, len, NULL); | 1789 | return cfi_varsize_frob(mtd, do_atmel_lock, ofs, len, NULL); |
| 1780 | } | 1790 | } |
| 1781 | 1791 | ||
| 1782 | static int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) | 1792 | static int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) |
| 1783 | { | 1793 | { |
| 1784 | return cfi_varsize_frob(mtd, do_atmel_unlock, ofs, len, NULL); | 1794 | return cfi_varsize_frob(mtd, do_atmel_unlock, ofs, len, NULL); |
| 1785 | } | 1795 | } |
diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index d4714dd9f7ab..6c740f346f91 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c | |||
| @@ -42,8 +42,8 @@ static int cfi_staa_writev(struct mtd_info *mtd, const struct kvec *vecs, | |||
| 42 | unsigned long count, loff_t to, size_t *retlen); | 42 | unsigned long count, loff_t to, size_t *retlen); |
| 43 | static int cfi_staa_erase_varsize(struct mtd_info *, struct erase_info *); | 43 | static int cfi_staa_erase_varsize(struct mtd_info *, struct erase_info *); |
| 44 | static void cfi_staa_sync (struct mtd_info *); | 44 | static void cfi_staa_sync (struct mtd_info *); |
| 45 | static int cfi_staa_lock(struct mtd_info *mtd, loff_t ofs, size_t len); | 45 | static int cfi_staa_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); |
| 46 | static int cfi_staa_unlock(struct mtd_info *mtd, loff_t ofs, size_t len); | 46 | static int cfi_staa_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); |
| 47 | static int cfi_staa_suspend (struct mtd_info *); | 47 | static int cfi_staa_suspend (struct mtd_info *); |
| 48 | static void cfi_staa_resume (struct mtd_info *); | 48 | static void cfi_staa_resume (struct mtd_info *); |
| 49 | 49 | ||
| @@ -221,8 +221,8 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map) | |||
| 221 | } | 221 | } |
| 222 | 222 | ||
| 223 | for (i=0; i<mtd->numeraseregions;i++){ | 223 | for (i=0; i<mtd->numeraseregions;i++){ |
| 224 | printk(KERN_DEBUG "%d: offset=0x%x,size=0x%x,blocks=%d\n", | 224 | printk(KERN_DEBUG "%d: offset=0x%llx,size=0x%x,blocks=%d\n", |
| 225 | i,mtd->eraseregions[i].offset, | 225 | i, (unsigned long long)mtd->eraseregions[i].offset, |
| 226 | mtd->eraseregions[i].erasesize, | 226 | mtd->eraseregions[i].erasesize, |
| 227 | mtd->eraseregions[i].numblocks); | 227 | mtd->eraseregions[i].numblocks); |
| 228 | } | 228 | } |
| @@ -964,7 +964,7 @@ static int cfi_staa_erase_varsize(struct mtd_info *mtd, | |||
| 964 | adr += regions[i].erasesize; | 964 | adr += regions[i].erasesize; |
| 965 | len -= regions[i].erasesize; | 965 | len -= regions[i].erasesize; |
| 966 | 966 | ||
| 967 | if (adr % (1<< cfi->chipshift) == ((regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift))) | 967 | if (adr % (1<< cfi->chipshift) == (((unsigned long)regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift))) |
| 968 | i++; | 968 | i++; |
| 969 | 969 | ||
| 970 | if (adr >> cfi->chipshift) { | 970 | if (adr >> cfi->chipshift) { |
| @@ -1135,7 +1135,7 @@ retry: | |||
| 1135 | spin_unlock_bh(chip->mutex); | 1135 | spin_unlock_bh(chip->mutex); |
| 1136 | return 0; | 1136 | return 0; |
| 1137 | } | 1137 | } |
| 1138 | static int cfi_staa_lock(struct mtd_info *mtd, loff_t ofs, size_t len) | 1138 | static int cfi_staa_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) |
| 1139 | { | 1139 | { |
| 1140 | struct map_info *map = mtd->priv; | 1140 | struct map_info *map = mtd->priv; |
| 1141 | struct cfi_private *cfi = map->fldrv_priv; | 1141 | struct cfi_private *cfi = map->fldrv_priv; |
| @@ -1284,7 +1284,7 @@ retry: | |||
| 1284 | spin_unlock_bh(chip->mutex); | 1284 | spin_unlock_bh(chip->mutex); |
| 1285 | return 0; | 1285 | return 0; |
| 1286 | } | 1286 | } |
| 1287 | static int cfi_staa_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) | 1287 | static int cfi_staa_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) |
| 1288 | { | 1288 | { |
| 1289 | struct map_info *map = mtd->priv; | 1289 | struct map_info *map = mtd->priv; |
| 1290 | struct cfi_private *cfi = map->fldrv_priv; | 1290 | struct cfi_private *cfi = map->fldrv_priv; |
diff --git a/drivers/mtd/chips/fwh_lock.h b/drivers/mtd/chips/fwh_lock.h index ab44f2b996f8..57e0e4e921f9 100644 --- a/drivers/mtd/chips/fwh_lock.h +++ b/drivers/mtd/chips/fwh_lock.h | |||
| @@ -77,7 +77,7 @@ static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip, | |||
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | 79 | ||
| 80 | static int fwh_lock_varsize(struct mtd_info *mtd, loff_t ofs, size_t len) | 80 | static int fwh_lock_varsize(struct mtd_info *mtd, loff_t ofs, uint64_t len) |
| 81 | { | 81 | { |
| 82 | int ret; | 82 | int ret; |
| 83 | 83 | ||
| @@ -88,7 +88,7 @@ static int fwh_lock_varsize(struct mtd_info *mtd, loff_t ofs, size_t len) | |||
| 88 | } | 88 | } |
| 89 | 89 | ||
| 90 | 90 | ||
| 91 | static int fwh_unlock_varsize(struct mtd_info *mtd, loff_t ofs, size_t len) | 91 | static int fwh_unlock_varsize(struct mtd_info *mtd, loff_t ofs, uint64_t len) |
| 92 | { | 92 | { |
| 93 | int ret; | 93 | int ret; |
| 94 | 94 | ||
diff --git a/drivers/mtd/devices/lart.c b/drivers/mtd/devices/lart.c index f4bda4cee495..578de1c67bfe 100644 --- a/drivers/mtd/devices/lart.c +++ b/drivers/mtd/devices/lart.c | |||
| @@ -619,7 +619,7 @@ static struct mtd_partition lart_partitions[] = { | |||
| 619 | }; | 619 | }; |
| 620 | #endif | 620 | #endif |
| 621 | 621 | ||
| 622 | int __init lart_flash_init (void) | 622 | static int __init lart_flash_init (void) |
| 623 | { | 623 | { |
| 624 | int result; | 624 | int result; |
| 625 | memset (&mtd,0,sizeof (mtd)); | 625 | memset (&mtd,0,sizeof (mtd)); |
| @@ -690,7 +690,7 @@ int __init lart_flash_init (void) | |||
| 690 | return (result); | 690 | return (result); |
| 691 | } | 691 | } |
| 692 | 692 | ||
| 693 | void __exit lart_flash_exit (void) | 693 | static void __exit lart_flash_exit (void) |
| 694 | { | 694 | { |
| 695 | #ifndef HAVE_PARTITIONS | 695 | #ifndef HAVE_PARTITIONS |
| 696 | del_mtd_device (&mtd); | 696 | del_mtd_device (&mtd); |
| @@ -705,5 +705,3 @@ module_exit (lart_flash_exit); | |||
| 705 | MODULE_LICENSE("GPL"); | 705 | MODULE_LICENSE("GPL"); |
| 706 | MODULE_AUTHOR("Abraham vd Merwe <abraham@2d3d.co.za>"); | 706 | MODULE_AUTHOR("Abraham vd Merwe <abraham@2d3d.co.za>"); |
| 707 | MODULE_DESCRIPTION("MTD driver for Intel 28F160F3 on LART board"); | 707 | MODULE_DESCRIPTION("MTD driver for Intel 28F160F3 on LART board"); |
| 708 | |||
| 709 | |||
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 5733f0643843..7c3fc766dcf1 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c | |||
| @@ -20,6 +20,7 @@ | |||
| 20 | #include <linux/device.h> | 20 | #include <linux/device.h> |
| 21 | #include <linux/interrupt.h> | 21 | #include <linux/interrupt.h> |
| 22 | #include <linux/mutex.h> | 22 | #include <linux/mutex.h> |
| 23 | #include <linux/math64.h> | ||
| 23 | 24 | ||
| 24 | #include <linux/mtd/mtd.h> | 25 | #include <linux/mtd/mtd.h> |
| 25 | #include <linux/mtd/partitions.h> | 26 | #include <linux/mtd/partitions.h> |
| @@ -169,9 +170,9 @@ static int wait_till_ready(struct m25p *flash) | |||
| 169 | */ | 170 | */ |
| 170 | static int erase_chip(struct m25p *flash) | 171 | static int erase_chip(struct m25p *flash) |
| 171 | { | 172 | { |
| 172 | DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %dKiB\n", | 173 | DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %lldKiB\n", |
| 173 | dev_name(&flash->spi->dev), __func__, | 174 | dev_name(&flash->spi->dev), __func__, |
| 174 | flash->mtd.size / 1024); | 175 | (long long)(flash->mtd.size >> 10)); |
| 175 | 176 | ||
| 176 | /* Wait until finished previous write command. */ | 177 | /* Wait until finished previous write command. */ |
| 177 | if (wait_till_ready(flash)) | 178 | if (wait_till_ready(flash)) |
| @@ -232,18 +233,18 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
| 232 | { | 233 | { |
| 233 | struct m25p *flash = mtd_to_m25p(mtd); | 234 | struct m25p *flash = mtd_to_m25p(mtd); |
| 234 | u32 addr,len; | 235 | u32 addr,len; |
| 236 | uint32_t rem; | ||
| 235 | 237 | ||
| 236 | DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %d\n", | 238 | DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%llx, len %lld\n", |
| 237 | dev_name(&flash->spi->dev), __func__, "at", | 239 | dev_name(&flash->spi->dev), __func__, "at", |
| 238 | (u32)instr->addr, instr->len); | 240 | (long long)instr->addr, (long long)instr->len); |
| 239 | 241 | ||
| 240 | /* sanity checks */ | 242 | /* sanity checks */ |
| 241 | if (instr->addr + instr->len > flash->mtd.size) | 243 | if (instr->addr + instr->len > flash->mtd.size) |
| 242 | return -EINVAL; | 244 | return -EINVAL; |
| 243 | if ((instr->addr % mtd->erasesize) != 0 | 245 | div_u64_rem(instr->len, mtd->erasesize, &rem); |
| 244 | || (instr->len % mtd->erasesize) != 0) { | 246 | if (rem) |
| 245 | return -EINVAL; | 247 | return -EINVAL; |
| 246 | } | ||
| 247 | 248 | ||
| 248 | addr = instr->addr; | 249 | addr = instr->addr; |
| 249 | len = instr->len; | 250 | len = instr->len; |
| @@ -677,24 +678,24 @@ static int __devinit m25p_probe(struct spi_device *spi) | |||
| 677 | flash->mtd.erasesize = info->sector_size; | 678 | flash->mtd.erasesize = info->sector_size; |
| 678 | } | 679 | } |
| 679 | 680 | ||
| 680 | dev_info(&spi->dev, "%s (%d Kbytes)\n", info->name, | 681 | dev_info(&spi->dev, "%s (%lld Kbytes)\n", info->name, |
| 681 | flash->mtd.size / 1024); | 682 | (long long)flash->mtd.size >> 10); |
| 682 | 683 | ||
| 683 | DEBUG(MTD_DEBUG_LEVEL2, | 684 | DEBUG(MTD_DEBUG_LEVEL2, |
| 684 | "mtd .name = %s, .size = 0x%.8x (%uMiB) " | 685 | "mtd .name = %s, .size = 0x%llx (%lldMiB) " |
| 685 | ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n", | 686 | ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n", |
| 686 | flash->mtd.name, | 687 | flash->mtd.name, |
| 687 | flash->mtd.size, flash->mtd.size / (1024*1024), | 688 | (long long)flash->mtd.size, (long long)(flash->mtd.size >> 20), |
| 688 | flash->mtd.erasesize, flash->mtd.erasesize / 1024, | 689 | flash->mtd.erasesize, flash->mtd.erasesize / 1024, |
| 689 | flash->mtd.numeraseregions); | 690 | flash->mtd.numeraseregions); |
| 690 | 691 | ||
| 691 | if (flash->mtd.numeraseregions) | 692 | if (flash->mtd.numeraseregions) |
| 692 | for (i = 0; i < flash->mtd.numeraseregions; i++) | 693 | for (i = 0; i < flash->mtd.numeraseregions; i++) |
| 693 | DEBUG(MTD_DEBUG_LEVEL2, | 694 | DEBUG(MTD_DEBUG_LEVEL2, |
| 694 | "mtd.eraseregions[%d] = { .offset = 0x%.8x, " | 695 | "mtd.eraseregions[%d] = { .offset = 0x%llx, " |
| 695 | ".erasesize = 0x%.8x (%uKiB), " | 696 | ".erasesize = 0x%.8x (%uKiB), " |
| 696 | ".numblocks = %d }\n", | 697 | ".numblocks = %d }\n", |
| 697 | i, flash->mtd.eraseregions[i].offset, | 698 | i, (long long)flash->mtd.eraseregions[i].offset, |
| 698 | flash->mtd.eraseregions[i].erasesize, | 699 | flash->mtd.eraseregions[i].erasesize, |
| 699 | flash->mtd.eraseregions[i].erasesize / 1024, | 700 | flash->mtd.eraseregions[i].erasesize / 1024, |
| 700 | flash->mtd.eraseregions[i].numblocks); | 701 | flash->mtd.eraseregions[i].numblocks); |
| @@ -722,12 +723,12 @@ static int __devinit m25p_probe(struct spi_device *spi) | |||
| 722 | if (nr_parts > 0) { | 723 | if (nr_parts > 0) { |
| 723 | for (i = 0; i < nr_parts; i++) { | 724 | for (i = 0; i < nr_parts; i++) { |
| 724 | DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = " | 725 | DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = " |
| 725 | "{.name = %s, .offset = 0x%.8x, " | 726 | "{.name = %s, .offset = 0x%llx, " |
| 726 | ".size = 0x%.8x (%uKiB) }\n", | 727 | ".size = 0x%llx (%lldKiB) }\n", |
| 727 | i, parts[i].name, | 728 | i, parts[i].name, |
| 728 | parts[i].offset, | 729 | (long long)parts[i].offset, |
| 729 | parts[i].size, | 730 | (long long)parts[i].size, |
| 730 | parts[i].size / 1024); | 731 | (long long)(parts[i].size >> 10)); |
| 731 | } | 732 | } |
| 732 | flash->partitioned = 1; | 733 | flash->partitioned = 1; |
| 733 | return add_mtd_partitions(&flash->mtd, parts, nr_parts); | 734 | return add_mtd_partitions(&flash->mtd, parts, nr_parts); |
diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index 65126cd668ff..d44f741ae229 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c | |||
| @@ -16,6 +16,7 @@ | |||
| 16 | #include <linux/device.h> | 16 | #include <linux/device.h> |
| 17 | #include <linux/mutex.h> | 17 | #include <linux/mutex.h> |
| 18 | #include <linux/err.h> | 18 | #include <linux/err.h> |
| 19 | #include <linux/math64.h> | ||
| 19 | 20 | ||
| 20 | #include <linux/spi/spi.h> | 21 | #include <linux/spi/spi.h> |
| 21 | #include <linux/spi/flash.h> | 22 | #include <linux/spi/flash.h> |
| @@ -152,15 +153,20 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
| 152 | struct spi_message msg; | 153 | struct spi_message msg; |
| 153 | unsigned blocksize = priv->page_size << 3; | 154 | unsigned blocksize = priv->page_size << 3; |
| 154 | uint8_t *command; | 155 | uint8_t *command; |
| 156 | uint32_t rem; | ||
| 155 | 157 | ||
| 156 | DEBUG(MTD_DEBUG_LEVEL2, "%s: erase addr=0x%x len 0x%x\n", | 158 | DEBUG(MTD_DEBUG_LEVEL2, "%s: erase addr=0x%llx len 0x%llx\n", |
| 157 | dev_name(&spi->dev), | 159 | dev_name(&spi->dev), (long long)instr->addr, |
| 158 | instr->addr, instr->len); | 160 | (long long)instr->len); |
| 159 | 161 | ||
| 160 | /* Sanity checks */ | 162 | /* Sanity checks */ |
| 161 | if ((instr->addr + instr->len) > mtd->size | 163 | if (instr->addr + instr->len > mtd->size) |
| 162 | || (instr->len % priv->page_size) != 0 | 164 | return -EINVAL; |
| 163 | || (instr->addr % priv->page_size) != 0) | 165 | div_u64_rem(instr->len, priv->page_size, &rem); |
| 166 | if (rem) | ||
| 167 | return -EINVAL; | ||
| 168 | div_u64_rem(instr->addr, priv->page_size, &rem); | ||
| 169 | if (rem) | ||
| 164 | return -EINVAL; | 170 | return -EINVAL; |
| 165 | 171 | ||
| 166 | spi_message_init(&msg); | 172 | spi_message_init(&msg); |
| @@ -178,7 +184,7 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
| 178 | /* Calculate flash page address; use block erase (for speed) if | 184 | /* Calculate flash page address; use block erase (for speed) if |
| 179 | * we're at a block boundary and need to erase the whole block. | 185 | * we're at a block boundary and need to erase the whole block. |
| 180 | */ | 186 | */ |
| 181 | pageaddr = instr->addr / priv->page_size; | 187 | pageaddr = div_u64(instr->len, priv->page_size); |
| 182 | do_block = (pageaddr & 0x7) == 0 && instr->len >= blocksize; | 188 | do_block = (pageaddr & 0x7) == 0 && instr->len >= blocksize; |
| 183 | pageaddr = pageaddr << priv->page_offset; | 189 | pageaddr = pageaddr << priv->page_offset; |
| 184 | 190 | ||
| @@ -667,8 +673,8 @@ add_dataflash_otp(struct spi_device *spi, char *name, | |||
| 667 | if (revision >= 'c') | 673 | if (revision >= 'c') |
| 668 | otp_tag = otp_setup(device, revision); | 674 | otp_tag = otp_setup(device, revision); |
| 669 | 675 | ||
| 670 | dev_info(&spi->dev, "%s (%d KBytes) pagesize %d bytes%s\n", | 676 | dev_info(&spi->dev, "%s (%lld KBytes) pagesize %d bytes%s\n", |
| 671 | name, DIV_ROUND_UP(device->size, 1024), | 677 | name, (long long)((device->size + 1023) >> 10), |
| 672 | pagesize, otp_tag); | 678 | pagesize, otp_tag); |
| 673 | dev_set_drvdata(&spi->dev, priv); | 679 | dev_set_drvdata(&spi->dev, priv); |
| 674 | 680 | ||
diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c index 9bf581c4f740..a790c062af1f 100644 --- a/drivers/mtd/ftl.c +++ b/drivers/mtd/ftl.c | |||
| @@ -109,25 +109,25 @@ module_param(shuffle_freq, int, 0); | |||
| 109 | /* Each memory region corresponds to a minor device */ | 109 | /* Each memory region corresponds to a minor device */ |
| 110 | typedef struct partition_t { | 110 | typedef struct partition_t { |
| 111 | struct mtd_blktrans_dev mbd; | 111 | struct mtd_blktrans_dev mbd; |
| 112 | u_int32_t state; | 112 | uint32_t state; |
| 113 | u_int32_t *VirtualBlockMap; | 113 | uint32_t *VirtualBlockMap; |
| 114 | u_int32_t *VirtualPageMap; | 114 | uint32_t *VirtualPageMap; |
| 115 | u_int32_t FreeTotal; | 115 | uint32_t FreeTotal; |
| 116 | struct eun_info_t { | 116 | struct eun_info_t { |
| 117 | u_int32_t Offset; | 117 | uint32_t Offset; |
| 118 | u_int32_t EraseCount; | 118 | uint32_t EraseCount; |
| 119 | u_int32_t Free; | 119 | uint32_t Free; |
| 120 | u_int32_t Deleted; | 120 | uint32_t Deleted; |
| 121 | } *EUNInfo; | 121 | } *EUNInfo; |
| 122 | struct xfer_info_t { | 122 | struct xfer_info_t { |
| 123 | u_int32_t Offset; | 123 | uint32_t Offset; |
| 124 | u_int32_t EraseCount; | 124 | uint32_t EraseCount; |
| 125 | u_int16_t state; | 125 | uint16_t state; |
| 126 | } *XferInfo; | 126 | } *XferInfo; |
| 127 | u_int16_t bam_index; | 127 | uint16_t bam_index; |
| 128 | u_int32_t *bam_cache; | 128 | uint32_t *bam_cache; |
| 129 | u_int16_t DataUnits; | 129 | uint16_t DataUnits; |
| 130 | u_int32_t BlocksPerUnit; | 130 | uint32_t BlocksPerUnit; |
| 131 | erase_unit_header_t header; | 131 | erase_unit_header_t header; |
| 132 | } partition_t; | 132 | } partition_t; |
| 133 | 133 | ||
| @@ -199,8 +199,8 @@ static int scan_header(partition_t *part) | |||
| 199 | static int build_maps(partition_t *part) | 199 | static int build_maps(partition_t *part) |
| 200 | { | 200 | { |
| 201 | erase_unit_header_t header; | 201 | erase_unit_header_t header; |
| 202 | u_int16_t xvalid, xtrans, i; | 202 | uint16_t xvalid, xtrans, i; |
| 203 | u_int blocks, j; | 203 | unsigned blocks, j; |
| 204 | int hdr_ok, ret = -1; | 204 | int hdr_ok, ret = -1; |
| 205 | ssize_t retval; | 205 | ssize_t retval; |
| 206 | loff_t offset; | 206 | loff_t offset; |
| @@ -269,14 +269,14 @@ static int build_maps(partition_t *part) | |||
| 269 | 269 | ||
| 270 | /* Set up virtual page map */ | 270 | /* Set up virtual page map */ |
| 271 | blocks = le32_to_cpu(header.FormattedSize) >> header.BlockSize; | 271 | blocks = le32_to_cpu(header.FormattedSize) >> header.BlockSize; |
| 272 | part->VirtualBlockMap = vmalloc(blocks * sizeof(u_int32_t)); | 272 | part->VirtualBlockMap = vmalloc(blocks * sizeof(uint32_t)); |
| 273 | if (!part->VirtualBlockMap) | 273 | if (!part->VirtualBlockMap) |
| 274 | goto out_XferInfo; | 274 | goto out_XferInfo; |
| 275 | 275 | ||
| 276 | memset(part->VirtualBlockMap, 0xff, blocks * sizeof(u_int32_t)); | 276 | memset(part->VirtualBlockMap, 0xff, blocks * sizeof(uint32_t)); |
| 277 | part->BlocksPerUnit = (1 << header.EraseUnitSize) >> header.BlockSize; | 277 | part->BlocksPerUnit = (1 << header.EraseUnitSize) >> header.BlockSize; |
| 278 | 278 | ||
| 279 | part->bam_cache = kmalloc(part->BlocksPerUnit * sizeof(u_int32_t), | 279 | part->bam_cache = kmalloc(part->BlocksPerUnit * sizeof(uint32_t), |
| 280 | GFP_KERNEL); | 280 | GFP_KERNEL); |
| 281 | if (!part->bam_cache) | 281 | if (!part->bam_cache) |
| 282 | goto out_VirtualBlockMap; | 282 | goto out_VirtualBlockMap; |
| @@ -290,7 +290,7 @@ static int build_maps(partition_t *part) | |||
| 290 | offset = part->EUNInfo[i].Offset + le32_to_cpu(header.BAMOffset); | 290 | offset = part->EUNInfo[i].Offset + le32_to_cpu(header.BAMOffset); |
| 291 | 291 | ||
| 292 | ret = part->mbd.mtd->read(part->mbd.mtd, offset, | 292 | ret = part->mbd.mtd->read(part->mbd.mtd, offset, |
| 293 | part->BlocksPerUnit * sizeof(u_int32_t), &retval, | 293 | part->BlocksPerUnit * sizeof(uint32_t), &retval, |
| 294 | (unsigned char *)part->bam_cache); | 294 | (unsigned char *)part->bam_cache); |
| 295 | 295 | ||
| 296 | if (ret) | 296 | if (ret) |
| @@ -332,7 +332,7 @@ out: | |||
| 332 | ======================================================================*/ | 332 | ======================================================================*/ |
| 333 | 333 | ||
| 334 | static int erase_xfer(partition_t *part, | 334 | static int erase_xfer(partition_t *part, |
| 335 | u_int16_t xfernum) | 335 | uint16_t xfernum) |
| 336 | { | 336 | { |
| 337 | int ret; | 337 | int ret; |
| 338 | struct xfer_info_t *xfer; | 338 | struct xfer_info_t *xfer; |
| @@ -408,7 +408,7 @@ static int prepare_xfer(partition_t *part, int i) | |||
| 408 | erase_unit_header_t header; | 408 | erase_unit_header_t header; |
| 409 | struct xfer_info_t *xfer; | 409 | struct xfer_info_t *xfer; |
| 410 | int nbam, ret; | 410 | int nbam, ret; |
| 411 | u_int32_t ctl; | 411 | uint32_t ctl; |
| 412 | ssize_t retlen; | 412 | ssize_t retlen; |
| 413 | loff_t offset; | 413 | loff_t offset; |
| 414 | 414 | ||
| @@ -430,15 +430,15 @@ static int prepare_xfer(partition_t *part, int i) | |||
| 430 | } | 430 | } |
| 431 | 431 | ||
| 432 | /* Write the BAM stub */ | 432 | /* Write the BAM stub */ |
| 433 | nbam = (part->BlocksPerUnit * sizeof(u_int32_t) + | 433 | nbam = (part->BlocksPerUnit * sizeof(uint32_t) + |
| 434 | le32_to_cpu(part->header.BAMOffset) + SECTOR_SIZE - 1) / SECTOR_SIZE; | 434 | le32_to_cpu(part->header.BAMOffset) + SECTOR_SIZE - 1) / SECTOR_SIZE; |
| 435 | 435 | ||
| 436 | offset = xfer->Offset + le32_to_cpu(part->header.BAMOffset); | 436 | offset = xfer->Offset + le32_to_cpu(part->header.BAMOffset); |
| 437 | ctl = cpu_to_le32(BLOCK_CONTROL); | 437 | ctl = cpu_to_le32(BLOCK_CONTROL); |
| 438 | 438 | ||
| 439 | for (i = 0; i < nbam; i++, offset += sizeof(u_int32_t)) { | 439 | for (i = 0; i < nbam; i++, offset += sizeof(uint32_t)) { |
| 440 | 440 | ||
| 441 | ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int32_t), | 441 | ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(uint32_t), |
| 442 | &retlen, (u_char *)&ctl); | 442 | &retlen, (u_char *)&ctl); |
| 443 | 443 | ||
| 444 | if (ret) | 444 | if (ret) |
| @@ -461,18 +461,18 @@ static int prepare_xfer(partition_t *part, int i) | |||
| 461 | 461 | ||
| 462 | ======================================================================*/ | 462 | ======================================================================*/ |
| 463 | 463 | ||
| 464 | static int copy_erase_unit(partition_t *part, u_int16_t srcunit, | 464 | static int copy_erase_unit(partition_t *part, uint16_t srcunit, |
| 465 | u_int16_t xferunit) | 465 | uint16_t xferunit) |
| 466 | { | 466 | { |
| 467 | u_char buf[SECTOR_SIZE]; | 467 | u_char buf[SECTOR_SIZE]; |
| 468 | struct eun_info_t *eun; | 468 | struct eun_info_t *eun; |
| 469 | struct xfer_info_t *xfer; | 469 | struct xfer_info_t *xfer; |
| 470 | u_int32_t src, dest, free, i; | 470 | uint32_t src, dest, free, i; |
| 471 | u_int16_t unit; | 471 | uint16_t unit; |
| 472 | int ret; | 472 | int ret; |
| 473 | ssize_t retlen; | 473 | ssize_t retlen; |
| 474 | loff_t offset; | 474 | loff_t offset; |
| 475 | u_int16_t srcunitswap = cpu_to_le16(srcunit); | 475 | uint16_t srcunitswap = cpu_to_le16(srcunit); |
| 476 | 476 | ||
| 477 | eun = &part->EUNInfo[srcunit]; | 477 | eun = &part->EUNInfo[srcunit]; |
| 478 | xfer = &part->XferInfo[xferunit]; | 478 | xfer = &part->XferInfo[xferunit]; |
| @@ -486,7 +486,7 @@ static int copy_erase_unit(partition_t *part, u_int16_t srcunit, | |||
| 486 | offset = eun->Offset + le32_to_cpu(part->header.BAMOffset); | 486 | offset = eun->Offset + le32_to_cpu(part->header.BAMOffset); |
| 487 | 487 | ||
| 488 | ret = part->mbd.mtd->read(part->mbd.mtd, offset, | 488 | ret = part->mbd.mtd->read(part->mbd.mtd, offset, |
| 489 | part->BlocksPerUnit * sizeof(u_int32_t), | 489 | part->BlocksPerUnit * sizeof(uint32_t), |
| 490 | &retlen, (u_char *) (part->bam_cache)); | 490 | &retlen, (u_char *) (part->bam_cache)); |
| 491 | 491 | ||
| 492 | /* mark the cache bad, in case we get an error later */ | 492 | /* mark the cache bad, in case we get an error later */ |
| @@ -503,7 +503,7 @@ static int copy_erase_unit(partition_t *part, u_int16_t srcunit, | |||
| 503 | offset = xfer->Offset + 20; /* Bad! */ | 503 | offset = xfer->Offset + 20; /* Bad! */ |
| 504 | unit = cpu_to_le16(0x7fff); | 504 | unit = cpu_to_le16(0x7fff); |
| 505 | 505 | ||
| 506 | ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int16_t), | 506 | ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(uint16_t), |
| 507 | &retlen, (u_char *) &unit); | 507 | &retlen, (u_char *) &unit); |
| 508 | 508 | ||
| 509 | if (ret) { | 509 | if (ret) { |
| @@ -560,7 +560,7 @@ static int copy_erase_unit(partition_t *part, u_int16_t srcunit, | |||
| 560 | 560 | ||
| 561 | 561 | ||
| 562 | /* All clear? Then update the LogicalEUN again */ | 562 | /* All clear? Then update the LogicalEUN again */ |
| 563 | ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset + 20, sizeof(u_int16_t), | 563 | ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset + 20, sizeof(uint16_t), |
| 564 | &retlen, (u_char *)&srcunitswap); | 564 | &retlen, (u_char *)&srcunitswap); |
| 565 | 565 | ||
| 566 | if (ret) { | 566 | if (ret) { |
| @@ -605,8 +605,8 @@ static int copy_erase_unit(partition_t *part, u_int16_t srcunit, | |||
| 605 | 605 | ||
| 606 | static int reclaim_block(partition_t *part) | 606 | static int reclaim_block(partition_t *part) |
| 607 | { | 607 | { |
| 608 | u_int16_t i, eun, xfer; | 608 | uint16_t i, eun, xfer; |
| 609 | u_int32_t best; | 609 | uint32_t best; |
| 610 | int queued, ret; | 610 | int queued, ret; |
| 611 | 611 | ||
| 612 | DEBUG(0, "ftl_cs: reclaiming space...\n"); | 612 | DEBUG(0, "ftl_cs: reclaiming space...\n"); |
| @@ -723,10 +723,10 @@ static void dump_lists(partition_t *part) | |||
| 723 | } | 723 | } |
| 724 | #endif | 724 | #endif |
| 725 | 725 | ||
| 726 | static u_int32_t find_free(partition_t *part) | 726 | static uint32_t find_free(partition_t *part) |
| 727 | { | 727 | { |
| 728 | u_int16_t stop, eun; | 728 | uint16_t stop, eun; |
| 729 | u_int32_t blk; | 729 | uint32_t blk; |
| 730 | size_t retlen; | 730 | size_t retlen; |
| 731 | int ret; | 731 | int ret; |
| 732 | 732 | ||
| @@ -749,7 +749,7 @@ static u_int32_t find_free(partition_t *part) | |||
| 749 | 749 | ||
| 750 | ret = part->mbd.mtd->read(part->mbd.mtd, | 750 | ret = part->mbd.mtd->read(part->mbd.mtd, |
| 751 | part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset), | 751 | part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset), |
| 752 | part->BlocksPerUnit * sizeof(u_int32_t), | 752 | part->BlocksPerUnit * sizeof(uint32_t), |
| 753 | &retlen, (u_char *) (part->bam_cache)); | 753 | &retlen, (u_char *) (part->bam_cache)); |
| 754 | 754 | ||
| 755 | if (ret) { | 755 | if (ret) { |
| @@ -786,7 +786,7 @@ static u_int32_t find_free(partition_t *part) | |||
| 786 | static int ftl_read(partition_t *part, caddr_t buffer, | 786 | static int ftl_read(partition_t *part, caddr_t buffer, |
| 787 | u_long sector, u_long nblocks) | 787 | u_long sector, u_long nblocks) |
| 788 | { | 788 | { |
| 789 | u_int32_t log_addr, bsize; | 789 | uint32_t log_addr, bsize; |
| 790 | u_long i; | 790 | u_long i; |
| 791 | int ret; | 791 | int ret; |
| 792 | size_t offset, retlen; | 792 | size_t offset, retlen; |
| @@ -829,14 +829,14 @@ static int ftl_read(partition_t *part, caddr_t buffer, | |||
| 829 | 829 | ||
| 830 | ======================================================================*/ | 830 | ======================================================================*/ |
| 831 | 831 | ||
| 832 | static int set_bam_entry(partition_t *part, u_int32_t log_addr, | 832 | static int set_bam_entry(partition_t *part, uint32_t log_addr, |
| 833 | u_int32_t virt_addr) | 833 | uint32_t virt_addr) |
| 834 | { | 834 | { |
| 835 | u_int32_t bsize, blk, le_virt_addr; | 835 | uint32_t bsize, blk, le_virt_addr; |
| 836 | #ifdef PSYCHO_DEBUG | 836 | #ifdef PSYCHO_DEBUG |
| 837 | u_int32_t old_addr; | 837 | uint32_t old_addr; |
| 838 | #endif | 838 | #endif |
| 839 | u_int16_t eun; | 839 | uint16_t eun; |
| 840 | int ret; | 840 | int ret; |
| 841 | size_t retlen, offset; | 841 | size_t retlen, offset; |
| 842 | 842 | ||
| @@ -845,11 +845,11 @@ static int set_bam_entry(partition_t *part, u_int32_t log_addr, | |||
| 845 | bsize = 1 << part->header.EraseUnitSize; | 845 | bsize = 1 << part->header.EraseUnitSize; |
| 846 | eun = log_addr / bsize; | 846 | eun = log_addr / bsize; |
| 847 | blk = (log_addr % bsize) / SECTOR_SIZE; | 847 | blk = (log_addr % bsize) / SECTOR_SIZE; |
| 848 | offset = (part->EUNInfo[eun].Offset + blk * sizeof(u_int32_t) + | 848 | offset = (part->EUNInfo[eun].Offset + blk * sizeof(uint32_t) + |
| 849 | le32_to_cpu(part->header.BAMOffset)); | 849 | le32_to_cpu(part->header.BAMOffset)); |
| 850 | 850 | ||
| 851 | #ifdef PSYCHO_DEBUG | 851 | #ifdef PSYCHO_DEBUG |
| 852 | ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(u_int32_t), | 852 | ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(uint32_t), |
| 853 | &retlen, (u_char *)&old_addr); | 853 | &retlen, (u_char *)&old_addr); |
| 854 | if (ret) { | 854 | if (ret) { |
| 855 | printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d\n",ret); | 855 | printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d\n",ret); |
| @@ -886,7 +886,7 @@ static int set_bam_entry(partition_t *part, u_int32_t log_addr, | |||
| 886 | #endif | 886 | #endif |
| 887 | part->bam_cache[blk] = le_virt_addr; | 887 | part->bam_cache[blk] = le_virt_addr; |
| 888 | } | 888 | } |
| 889 | ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int32_t), | 889 | ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(uint32_t), |
| 890 | &retlen, (u_char *)&le_virt_addr); | 890 | &retlen, (u_char *)&le_virt_addr); |
| 891 | 891 | ||
| 892 | if (ret) { | 892 | if (ret) { |
| @@ -900,7 +900,7 @@ static int set_bam_entry(partition_t *part, u_int32_t log_addr, | |||
| 900 | static int ftl_write(partition_t *part, caddr_t buffer, | 900 | static int ftl_write(partition_t *part, caddr_t buffer, |
| 901 | u_long sector, u_long nblocks) | 901 | u_long sector, u_long nblocks) |
| 902 | { | 902 | { |
| 903 | u_int32_t bsize, log_addr, virt_addr, old_addr, blk; | 903 | uint32_t bsize, log_addr, virt_addr, old_addr, blk; |
| 904 | u_long i; | 904 | u_long i; |
| 905 | int ret; | 905 | int ret; |
| 906 | size_t retlen, offset; | 906 | size_t retlen, offset; |
diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c index 50ce13887f63..73f05227dc8c 100644 --- a/drivers/mtd/inftlcore.c +++ b/drivers/mtd/inftlcore.c | |||
| @@ -50,7 +50,7 @@ static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) | |||
| 50 | struct INFTLrecord *inftl; | 50 | struct INFTLrecord *inftl; |
| 51 | unsigned long temp; | 51 | unsigned long temp; |
| 52 | 52 | ||
| 53 | if (mtd->type != MTD_NANDFLASH) | 53 | if (mtd->type != MTD_NANDFLASH || mtd->size > UINT_MAX) |
| 54 | return; | 54 | return; |
| 55 | /* OK, this is moderately ugly. But probably safe. Alternatives? */ | 55 | /* OK, this is moderately ugly. But probably safe. Alternatives? */ |
| 56 | if (memcmp(mtd->name, "DiskOnChip", 10)) | 56 | if (memcmp(mtd->name, "DiskOnChip", 10)) |
diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index 9113628ed1ef..f751dd97c549 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c | |||
| @@ -63,7 +63,7 @@ static int find_boot_record(struct INFTLrecord *inftl) | |||
| 63 | * otherwise. | 63 | * otherwise. |
| 64 | */ | 64 | */ |
| 65 | inftl->EraseSize = inftl->mbd.mtd->erasesize; | 65 | inftl->EraseSize = inftl->mbd.mtd->erasesize; |
| 66 | inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize; | 66 | inftl->nb_blocks = (u32)inftl->mbd.mtd->size / inftl->EraseSize; |
| 67 | 67 | ||
| 68 | inftl->MediaUnit = BLOCK_NIL; | 68 | inftl->MediaUnit = BLOCK_NIL; |
| 69 | 69 | ||
| @@ -187,7 +187,7 @@ static int find_boot_record(struct INFTLrecord *inftl) | |||
| 187 | mh->BlockMultiplierBits); | 187 | mh->BlockMultiplierBits); |
| 188 | inftl->EraseSize = inftl->mbd.mtd->erasesize << | 188 | inftl->EraseSize = inftl->mbd.mtd->erasesize << |
| 189 | mh->BlockMultiplierBits; | 189 | mh->BlockMultiplierBits; |
| 190 | inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize; | 190 | inftl->nb_blocks = (u32)inftl->mbd.mtd->size / inftl->EraseSize; |
| 191 | block >>= mh->BlockMultiplierBits; | 191 | block >>= mh->BlockMultiplierBits; |
| 192 | } | 192 | } |
| 193 | 193 | ||
diff --git a/drivers/mtd/lpddr/Kconfig b/drivers/mtd/lpddr/Kconfig new file mode 100644 index 000000000000..acd4ea9b2278 --- /dev/null +++ b/drivers/mtd/lpddr/Kconfig | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | # drivers/mtd/chips/Kconfig | ||
| 2 | |||
| 3 | menu "LPDDR flash memory drivers" | ||
| 4 | depends on MTD!=n | ||
| 5 | |||
| 6 | config MTD_LPDDR | ||
| 7 | tristate "Support for LPDDR flash chips" | ||
| 8 | select MTD_QINFO_PROBE | ||
| 9 | help | ||
| 10 | This option enables support of LPDDR (Low power double data rate) | ||
| 11 | flash chips. Synonymous with Mobile-DDR. It is a new standard for | ||
| 12 | DDR memories, intended for battery-operated systems. | ||
| 13 | |||
| 14 | config MTD_QINFO_PROBE | ||
| 15 | tristate "Detect flash chips by QINFO probe" | ||
| 16 | help | ||
| 17 | Device Information for LPDDR chips is offered through the Overlay | ||
| 18 | Window QINFO interface, permits software to be used for entire | ||
| 19 | families of devices. This serves similar purpose of CFI on legacy | ||
| 20 | Flash products | ||
| 21 | endmenu | ||
| 22 | |||
diff --git a/drivers/mtd/lpddr/Makefile b/drivers/mtd/lpddr/Makefile new file mode 100644 index 000000000000..da48e46b5812 --- /dev/null +++ b/drivers/mtd/lpddr/Makefile | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | # | ||
| 2 | # linux/drivers/mtd/lpddr/Makefile | ||
| 3 | # | ||
| 4 | |||
| 5 | obj-$(CONFIG_MTD_QINFO_PROBE) += qinfo_probe.o | ||
| 6 | obj-$(CONFIG_MTD_LPDDR) += lpddr_cmds.o | ||
diff --git a/drivers/mtd/lpddr/lpddr_cmds.c b/drivers/mtd/lpddr/lpddr_cmds.c new file mode 100644 index 000000000000..e22ca49583e7 --- /dev/null +++ b/drivers/mtd/lpddr/lpddr_cmds.c | |||
| @@ -0,0 +1,796 @@ | |||
| 1 | /* | ||
| 2 | * LPDDR flash memory device operations. This module provides read, write, | ||
| 3 | * erase, lock/unlock support for LPDDR flash memories | ||
| 4 | * (C) 2008 Korolev Alexey <akorolev@infradead.org> | ||
| 5 | * (C) 2008 Vasiliy Leonenko <vasiliy.leonenko@gmail.com> | ||
| 6 | * Many thanks to Roman Borisov for intial enabling | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or | ||
| 9 | * modify it under the terms of the GNU General Public License | ||
| 10 | * as published by the Free Software Foundation; either version 2 | ||
| 11 | * of the License, or (at your option) any later version. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, | ||
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | * GNU General Public License for more details. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the GNU General Public License | ||
| 19 | * along with this program; if not, write to the Free Software | ||
| 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
| 21 | * 02110-1301, USA. | ||
| 22 | * TODO: | ||
| 23 | * Implement VPP management | ||
| 24 | * Implement XIP support | ||
| 25 | * Implement OTP support | ||
| 26 | */ | ||
| 27 | #include <linux/mtd/pfow.h> | ||
| 28 | #include <linux/mtd/qinfo.h> | ||
| 29 | |||
| 30 | static int lpddr_read(struct mtd_info *mtd, loff_t adr, size_t len, | ||
| 31 | size_t *retlen, u_char *buf); | ||
| 32 | static int lpddr_write_buffers(struct mtd_info *mtd, loff_t to, | ||
| 33 | size_t len, size_t *retlen, const u_char *buf); | ||
| 34 | static int lpddr_writev(struct mtd_info *mtd, const struct kvec *vecs, | ||
| 35 | unsigned long count, loff_t to, size_t *retlen); | ||
| 36 | static int lpddr_erase(struct mtd_info *mtd, struct erase_info *instr); | ||
| 37 | static int lpddr_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); | ||
| 38 | static int lpddr_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); | ||
| 39 | static int lpddr_point(struct mtd_info *mtd, loff_t adr, size_t len, | ||
| 40 | size_t *retlen, void **mtdbuf, resource_size_t *phys); | ||
| 41 | static void lpddr_unpoint(struct mtd_info *mtd, loff_t adr, size_t len); | ||
| 42 | static int get_chip(struct map_info *map, struct flchip *chip, int mode); | ||
| 43 | static int chip_ready(struct map_info *map, struct flchip *chip, int mode); | ||
| 44 | static void put_chip(struct map_info *map, struct flchip *chip); | ||
| 45 | |||
| 46 | struct mtd_info *lpddr_cmdset(struct map_info *map) | ||
| 47 | { | ||
| 48 | struct lpddr_private *lpddr = map->fldrv_priv; | ||
| 49 | struct flchip_shared *shared; | ||
| 50 | struct flchip *chip; | ||
| 51 | struct mtd_info *mtd; | ||
| 52 | int numchips; | ||
| 53 | int i, j; | ||
| 54 | |||
| 55 | mtd = kzalloc(sizeof(*mtd), GFP_KERNEL); | ||
| 56 | if (!mtd) { | ||
| 57 | printk(KERN_ERR "Failed to allocate memory for MTD device\n"); | ||
| 58 | return NULL; | ||
| 59 | } | ||
| 60 | mtd->priv = map; | ||
| 61 | mtd->type = MTD_NORFLASH; | ||
| 62 | |||
| 63 | /* Fill in the default mtd operations */ | ||
| 64 | mtd->read = lpddr_read; | ||
| 65 | mtd->type = MTD_NORFLASH; | ||
| 66 | mtd->flags = MTD_CAP_NORFLASH; | ||
| 67 | mtd->flags &= ~MTD_BIT_WRITEABLE; | ||
| 68 | mtd->erase = lpddr_erase; | ||
| 69 | mtd->write = lpddr_write_buffers; | ||
| 70 | mtd->writev = lpddr_writev; | ||
| 71 | mtd->read_oob = NULL; | ||
| 72 | mtd->write_oob = NULL; | ||
| 73 | mtd->sync = NULL; | ||
| 74 | mtd->lock = lpddr_lock; | ||
| 75 | mtd->unlock = lpddr_unlock; | ||
| 76 | mtd->suspend = NULL; | ||
| 77 | mtd->resume = NULL; | ||
| 78 | if (map_is_linear(map)) { | ||
| 79 | mtd->point = lpddr_point; | ||
| 80 | mtd->unpoint = lpddr_unpoint; | ||
| 81 | } | ||
| 82 | mtd->block_isbad = NULL; | ||
| 83 | mtd->block_markbad = NULL; | ||
| 84 | mtd->size = 1 << lpddr->qinfo->DevSizeShift; | ||
| 85 | mtd->erasesize = 1 << lpddr->qinfo->UniformBlockSizeShift; | ||
| 86 | mtd->writesize = 1 << lpddr->qinfo->BufSizeShift; | ||
| 87 | |||
| 88 | shared = kmalloc(sizeof(struct flchip_shared) * lpddr->numchips, | ||
| 89 | GFP_KERNEL); | ||
| 90 | if (!shared) { | ||
| 91 | kfree(lpddr); | ||
| 92 | kfree(mtd); | ||
| 93 | return NULL; | ||
| 94 | } | ||
| 95 | |||
| 96 | chip = &lpddr->chips[0]; | ||
| 97 | numchips = lpddr->numchips / lpddr->qinfo->HWPartsNum; | ||
| 98 | for (i = 0; i < numchips; i++) { | ||
| 99 | shared[i].writing = shared[i].erasing = NULL; | ||
| 100 | spin_lock_init(&shared[i].lock); | ||
| 101 | for (j = 0; j < lpddr->qinfo->HWPartsNum; j++) { | ||
| 102 | *chip = lpddr->chips[i]; | ||
| 103 | chip->start += j << lpddr->chipshift; | ||
| 104 | chip->oldstate = chip->state = FL_READY; | ||
| 105 | chip->priv = &shared[i]; | ||
| 106 | /* those should be reset too since | ||
| 107 | they create memory references. */ | ||
| 108 | init_waitqueue_head(&chip->wq); | ||
| 109 | spin_lock_init(&chip->_spinlock); | ||
| 110 | chip->mutex = &chip->_spinlock; | ||
| 111 | chip++; | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | return mtd; | ||
| 116 | } | ||
| 117 | EXPORT_SYMBOL(lpddr_cmdset); | ||
| 118 | |||
| 119 | static int wait_for_ready(struct map_info *map, struct flchip *chip, | ||
| 120 | unsigned int chip_op_time) | ||
| 121 | { | ||
| 122 | unsigned int timeo, reset_timeo, sleep_time; | ||
| 123 | unsigned int dsr; | ||
| 124 | flstate_t chip_state = chip->state; | ||
| 125 | int ret = 0; | ||
| 126 | |||
| 127 | /* set our timeout to 8 times the expected delay */ | ||
| 128 | timeo = chip_op_time * 8; | ||
| 129 | if (!timeo) | ||
| 130 | timeo = 500000; | ||
| 131 | reset_timeo = timeo; | ||
| 132 | sleep_time = chip_op_time / 2; | ||
| 133 | |||
| 134 | for (;;) { | ||
| 135 | dsr = CMDVAL(map_read(map, map->pfow_base + PFOW_DSR)); | ||
| 136 | if (dsr & DSR_READY_STATUS) | ||
| 137 | break; | ||
| 138 | if (!timeo) { | ||
| 139 | printk(KERN_ERR "%s: Flash timeout error state %d \n", | ||
| 140 | map->name, chip_state); | ||
| 141 | ret = -ETIME; | ||
| 142 | break; | ||
| 143 | } | ||
| 144 | |||
| 145 | /* OK Still waiting. Drop the lock, wait a while and retry. */ | ||
| 146 | spin_unlock(chip->mutex); | ||
| 147 | if (sleep_time >= 1000000/HZ) { | ||
| 148 | /* | ||
| 149 | * Half of the normal delay still remaining | ||
| 150 | * can be performed with a sleeping delay instead | ||
| 151 | * of busy waiting. | ||
| 152 | */ | ||
| 153 | msleep(sleep_time/1000); | ||
| 154 | timeo -= sleep_time; | ||
| 155 | sleep_time = 1000000/HZ; | ||
| 156 | } else { | ||
| 157 | udelay(1); | ||
| 158 | cond_resched(); | ||
| 159 | timeo--; | ||
| 160 | } | ||
| 161 | spin_lock(chip->mutex); | ||
| 162 | |||
| 163 | while (chip->state != chip_state) { | ||
| 164 | /* Someone's suspended the operation: sleep */ | ||
| 165 | DECLARE_WAITQUEUE(wait, current); | ||
| 166 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
| 167 | add_wait_queue(&chip->wq, &wait); | ||
| 168 | spin_unlock(chip->mutex); | ||
| 169 | schedule(); | ||
| 170 | remove_wait_queue(&chip->wq, &wait); | ||
| 171 | spin_lock(chip->mutex); | ||
| 172 | } | ||
| 173 | if (chip->erase_suspended || chip->write_suspended) { | ||
| 174 | /* Suspend has occured while sleep: reset timeout */ | ||
| 175 | timeo = reset_timeo; | ||
| 176 | chip->erase_suspended = chip->write_suspended = 0; | ||
| 177 | } | ||
| 178 | } | ||
| 179 | /* check status for errors */ | ||
| 180 | if (dsr & DSR_ERR) { | ||
| 181 | /* Clear DSR*/ | ||
| 182 | map_write(map, CMD(~(DSR_ERR)), map->pfow_base + PFOW_DSR); | ||
| 183 | printk(KERN_WARNING"%s: Bad status on wait: 0x%x \n", | ||
| 184 | map->name, dsr); | ||
| 185 | print_drs_error(dsr); | ||
| 186 | ret = -EIO; | ||
| 187 | } | ||
| 188 | chip->state = FL_READY; | ||
| 189 | return ret; | ||
| 190 | } | ||
| 191 | |||
| 192 | static int get_chip(struct map_info *map, struct flchip *chip, int mode) | ||
| 193 | { | ||
| 194 | int ret; | ||
| 195 | DECLARE_WAITQUEUE(wait, current); | ||
| 196 | |||
| 197 | retry: | ||
| 198 | if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING) | ||
| 199 | && chip->state != FL_SYNCING) { | ||
| 200 | /* | ||
| 201 | * OK. We have possibility for contension on the write/erase | ||
| 202 | * operations which are global to the real chip and not per | ||
| 203 | * partition. So let's fight it over in the partition which | ||
| 204 | * currently has authority on the operation. | ||
| 205 | * | ||
| 206 | * The rules are as follows: | ||
| 207 | * | ||
| 208 | * - any write operation must own shared->writing. | ||
| 209 | * | ||
| 210 | * - any erase operation must own _both_ shared->writing and | ||
| 211 | * shared->erasing. | ||
| 212 | * | ||
| 213 | * - contension arbitration is handled in the owner's context. | ||
| 214 | * | ||
| 215 | * The 'shared' struct can be read and/or written only when | ||
| 216 | * its lock is taken. | ||
| 217 | */ | ||
| 218 | struct flchip_shared *shared = chip->priv; | ||
| 219 | struct flchip *contender; | ||
| 220 | spin_lock(&shared->lock); | ||
| 221 | contender = shared->writing; | ||
| 222 | if (contender && contender != chip) { | ||
| 223 | /* | ||
| 224 | * The engine to perform desired operation on this | ||
| 225 | * partition is already in use by someone else. | ||
| 226 | * Let's fight over it in the context of the chip | ||
| 227 | * currently using it. If it is possible to suspend, | ||
| 228 | * that other partition will do just that, otherwise | ||
| 229 | * it'll happily send us to sleep. In any case, when | ||
| 230 | * get_chip returns success we're clear to go ahead. | ||
| 231 | */ | ||
| 232 | ret = spin_trylock(contender->mutex); | ||
| 233 | spin_unlock(&shared->lock); | ||
| 234 | if (!ret) | ||
| 235 | goto retry; | ||
| 236 | spin_unlock(chip->mutex); | ||
| 237 | ret = chip_ready(map, contender, mode); | ||
| 238 | spin_lock(chip->mutex); | ||
| 239 | |||
| 240 | if (ret == -EAGAIN) { | ||
| 241 | spin_unlock(contender->mutex); | ||
| 242 | goto retry; | ||
| 243 | } | ||
| 244 | if (ret) { | ||
| 245 | spin_unlock(contender->mutex); | ||
| 246 | return ret; | ||
| 247 | } | ||
| 248 | spin_lock(&shared->lock); | ||
| 249 | |||
| 250 | /* We should not own chip if it is already in FL_SYNCING | ||
| 251 | * state. Put contender and retry. */ | ||
| 252 | if (chip->state == FL_SYNCING) { | ||
| 253 | put_chip(map, contender); | ||
| 254 | spin_unlock(contender->mutex); | ||
| 255 | goto retry; | ||
| 256 | } | ||
| 257 | spin_unlock(contender->mutex); | ||
| 258 | } | ||
| 259 | |||
| 260 | /* Check if we have suspended erase on this chip. | ||
| 261 | Must sleep in such a case. */ | ||
| 262 | if (mode == FL_ERASING && shared->erasing | ||
| 263 | && shared->erasing->oldstate == FL_ERASING) { | ||
| 264 | spin_unlock(&shared->lock); | ||
| 265 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
| 266 | add_wait_queue(&chip->wq, &wait); | ||
| 267 | spin_unlock(chip->mutex); | ||
| 268 | schedule(); | ||
| 269 | remove_wait_queue(&chip->wq, &wait); | ||
| 270 | spin_lock(chip->mutex); | ||
| 271 | goto retry; | ||
| 272 | } | ||
| 273 | |||
| 274 | /* We now own it */ | ||
| 275 | shared->writing = chip; | ||
| 276 | if (mode == FL_ERASING) | ||
| 277 | shared->erasing = chip; | ||
| 278 | spin_unlock(&shared->lock); | ||
| 279 | } | ||
| 280 | |||
| 281 | ret = chip_ready(map, chip, mode); | ||
| 282 | if (ret == -EAGAIN) | ||
| 283 | goto retry; | ||
| 284 | |||
| 285 | return ret; | ||
| 286 | } | ||
| 287 | |||
| 288 | static int chip_ready(struct map_info *map, struct flchip *chip, int mode) | ||
| 289 | { | ||
| 290 | struct lpddr_private *lpddr = map->fldrv_priv; | ||
| 291 | int ret = 0; | ||
| 292 | DECLARE_WAITQUEUE(wait, current); | ||
| 293 | |||
| 294 | /* Prevent setting state FL_SYNCING for chip in suspended state. */ | ||
| 295 | if (FL_SYNCING == mode && FL_READY != chip->oldstate) | ||
| 296 | goto sleep; | ||
| 297 | |||
| 298 | switch (chip->state) { | ||
| 299 | case FL_READY: | ||
| 300 | case FL_JEDEC_QUERY: | ||
| 301 | return 0; | ||
| 302 | |||
| 303 | case FL_ERASING: | ||
| 304 | if (!lpddr->qinfo->SuspEraseSupp || | ||
| 305 | !(mode == FL_READY || mode == FL_POINT)) | ||
| 306 | goto sleep; | ||
| 307 | |||
| 308 | map_write(map, CMD(LPDDR_SUSPEND), | ||
| 309 | map->pfow_base + PFOW_PROGRAM_ERASE_SUSPEND); | ||
| 310 | chip->oldstate = FL_ERASING; | ||
| 311 | chip->state = FL_ERASE_SUSPENDING; | ||
| 312 | ret = wait_for_ready(map, chip, 0); | ||
| 313 | if (ret) { | ||
| 314 | /* Oops. something got wrong. */ | ||
| 315 | /* Resume and pretend we weren't here. */ | ||
| 316 | map_write(map, CMD(LPDDR_RESUME), | ||
| 317 | map->pfow_base + PFOW_COMMAND_CODE); | ||
| 318 | map_write(map, CMD(LPDDR_START_EXECUTION), | ||
| 319 | map->pfow_base + PFOW_COMMAND_EXECUTE); | ||
| 320 | chip->state = FL_ERASING; | ||
| 321 | chip->oldstate = FL_READY; | ||
| 322 | printk(KERN_ERR "%s: suspend operation failed." | ||
| 323 | "State may be wrong \n", map->name); | ||
| 324 | return -EIO; | ||
| 325 | } | ||
| 326 | chip->erase_suspended = 1; | ||
| 327 | chip->state = FL_READY; | ||
| 328 | return 0; | ||
| 329 | /* Erase suspend */ | ||
| 330 | case FL_POINT: | ||
| 331 | /* Only if there's no operation suspended... */ | ||
| 332 | if (mode == FL_READY && chip->oldstate == FL_READY) | ||
| 333 | return 0; | ||
| 334 | |||
| 335 | default: | ||
| 336 | sleep: | ||
| 337 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
| 338 | add_wait_queue(&chip->wq, &wait); | ||
| 339 | spin_unlock(chip->mutex); | ||
| 340 | schedule(); | ||
| 341 | remove_wait_queue(&chip->wq, &wait); | ||
| 342 | spin_lock(chip->mutex); | ||
| 343 | return -EAGAIN; | ||
| 344 | } | ||
| 345 | } | ||
| 346 | |||
| 347 | static void put_chip(struct map_info *map, struct flchip *chip) | ||
| 348 | { | ||
| 349 | if (chip->priv) { | ||
| 350 | struct flchip_shared *shared = chip->priv; | ||
| 351 | spin_lock(&shared->lock); | ||
| 352 | if (shared->writing == chip && chip->oldstate == FL_READY) { | ||
| 353 | /* We own the ability to write, but we're done */ | ||
| 354 | shared->writing = shared->erasing; | ||
| 355 | if (shared->writing && shared->writing != chip) { | ||
| 356 | /* give back the ownership */ | ||
| 357 | struct flchip *loaner = shared->writing; | ||
| 358 | spin_lock(loaner->mutex); | ||
| 359 | spin_unlock(&shared->lock); | ||
| 360 | spin_unlock(chip->mutex); | ||
| 361 | put_chip(map, loaner); | ||
| 362 | spin_lock(chip->mutex); | ||
| 363 | spin_unlock(loaner->mutex); | ||
| 364 | wake_up(&chip->wq); | ||
| 365 | return; | ||
| 366 | } | ||
| 367 | shared->erasing = NULL; | ||
| 368 | shared->writing = NULL; | ||
| 369 | } else if (shared->erasing == chip && shared->writing != chip) { | ||
| 370 | /* | ||
| 371 | * We own the ability to erase without the ability | ||
| 372 | * to write, which means the erase was suspended | ||
| 373 | * and some other partition is currently writing. | ||
| 374 | * Don't let the switch below mess things up since | ||
| 375 | * we don't have ownership to resume anything. | ||
| 376 | */ | ||
| 377 | spin_unlock(&shared->lock); | ||
| 378 | wake_up(&chip->wq); | ||
| 379 | return; | ||
| 380 | } | ||
| 381 | spin_unlock(&shared->lock); | ||
| 382 | } | ||
| 383 | |||
| 384 | switch (chip->oldstate) { | ||
| 385 | case FL_ERASING: | ||
| 386 | chip->state = chip->oldstate; | ||
| 387 | map_write(map, CMD(LPDDR_RESUME), | ||
| 388 | map->pfow_base + PFOW_COMMAND_CODE); | ||
| 389 | map_write(map, CMD(LPDDR_START_EXECUTION), | ||
| 390 | map->pfow_base + PFOW_COMMAND_EXECUTE); | ||
| 391 | chip->oldstate = FL_READY; | ||
| 392 | chip->state = FL_ERASING; | ||
| 393 | break; | ||
| 394 | case FL_READY: | ||
| 395 | break; | ||
| 396 | default: | ||
| 397 | printk(KERN_ERR "%s: put_chip() called with oldstate %d!\n", | ||
| 398 | map->name, chip->oldstate); | ||
| 399 | } | ||
| 400 | wake_up(&chip->wq); | ||
| 401 | } | ||
| 402 | |||
| 403 | int do_write_buffer(struct map_info *map, struct flchip *chip, | ||
| 404 | unsigned long adr, const struct kvec **pvec, | ||
| 405 | unsigned long *pvec_seek, int len) | ||
| 406 | { | ||
| 407 | struct lpddr_private *lpddr = map->fldrv_priv; | ||
| 408 | map_word datum; | ||
| 409 | int ret, wbufsize, word_gap, words; | ||
| 410 | const struct kvec *vec; | ||
| 411 | unsigned long vec_seek; | ||
| 412 | unsigned long prog_buf_ofs; | ||
| 413 | |||
| 414 | wbufsize = 1 << lpddr->qinfo->BufSizeShift; | ||
| 415 | |||
| 416 | spin_lock(chip->mutex); | ||
| 417 | ret = get_chip(map, chip, FL_WRITING); | ||
| 418 | if (ret) { | ||
| 419 | spin_unlock(chip->mutex); | ||
| 420 | return ret; | ||
| 421 | } | ||
| 422 | /* Figure out the number of words to write */ | ||
| 423 | word_gap = (-adr & (map_bankwidth(map)-1)); | ||
| 424 | words = (len - word_gap + map_bankwidth(map) - 1) / map_bankwidth(map); | ||
| 425 | if (!word_gap) { | ||
| 426 | words--; | ||
| 427 | } else { | ||
| 428 | word_gap = map_bankwidth(map) - word_gap; | ||
| 429 | adr -= word_gap; | ||
| 430 | datum = map_word_ff(map); | ||
| 431 | } | ||
| 432 | /* Write data */ | ||
| 433 | /* Get the program buffer offset from PFOW register data first*/ | ||
| 434 | prog_buf_ofs = map->pfow_base + CMDVAL(map_read(map, | ||
| 435 | map->pfow_base + PFOW_PROGRAM_BUFFER_OFFSET)); | ||
| 436 | vec = *pvec; | ||
| 437 | vec_seek = *pvec_seek; | ||
| 438 | do { | ||
| 439 | int n = map_bankwidth(map) - word_gap; | ||
| 440 | |||
| 441 | if (n > vec->iov_len - vec_seek) | ||
| 442 | n = vec->iov_len - vec_seek; | ||
| 443 | if (n > len) | ||
| 444 | n = len; | ||
| 445 | |||
| 446 | if (!word_gap && (len < map_bankwidth(map))) | ||
| 447 | datum = map_word_ff(map); | ||
| 448 | |||
| 449 | datum = map_word_load_partial(map, datum, | ||
| 450 | vec->iov_base + vec_seek, word_gap, n); | ||
| 451 | |||
| 452 | len -= n; | ||
| 453 | word_gap += n; | ||
| 454 | if (!len || word_gap == map_bankwidth(map)) { | ||
| 455 | map_write(map, datum, prog_buf_ofs); | ||
| 456 | prog_buf_ofs += map_bankwidth(map); | ||
| 457 | word_gap = 0; | ||
| 458 | } | ||
| 459 | |||
| 460 | vec_seek += n; | ||
| 461 | if (vec_seek == vec->iov_len) { | ||
| 462 | vec++; | ||
| 463 | vec_seek = 0; | ||
| 464 | } | ||
| 465 | } while (len); | ||
| 466 | *pvec = vec; | ||
| 467 | *pvec_seek = vec_seek; | ||
| 468 | |||
| 469 | /* GO GO GO */ | ||
| 470 | send_pfow_command(map, LPDDR_BUFF_PROGRAM, adr, wbufsize, NULL); | ||
| 471 | chip->state = FL_WRITING; | ||
| 472 | ret = wait_for_ready(map, chip, (1<<lpddr->qinfo->ProgBufferTime)); | ||
| 473 | if (ret) { | ||
| 474 | printk(KERN_WARNING"%s Buffer program error: %d at %lx; \n", | ||
| 475 | map->name, ret, adr); | ||
| 476 | goto out; | ||
| 477 | } | ||
| 478 | |||
| 479 | out: put_chip(map, chip); | ||
| 480 | spin_unlock(chip->mutex); | ||
| 481 | return ret; | ||
| 482 | } | ||
| 483 | |||
| 484 | int do_erase_oneblock(struct mtd_info *mtd, loff_t adr) | ||
| 485 | { | ||
| 486 | struct map_info *map = mtd->priv; | ||
| 487 | struct lpddr_private *lpddr = map->fldrv_priv; | ||
| 488 | int chipnum = adr >> lpddr->chipshift; | ||
| 489 | struct flchip *chip = &lpddr->chips[chipnum]; | ||
| 490 | int ret; | ||
| 491 | |||
| 492 | spin_lock(chip->mutex); | ||
| 493 | ret = get_chip(map, chip, FL_ERASING); | ||
| 494 | if (ret) { | ||
| 495 | spin_unlock(chip->mutex); | ||
| 496 | return ret; | ||
| 497 | } | ||
| 498 | send_pfow_command(map, LPDDR_BLOCK_ERASE, adr, 0, NULL); | ||
| 499 | chip->state = FL_ERASING; | ||
| 500 | ret = wait_for_ready(map, chip, (1<<lpddr->qinfo->BlockEraseTime)*1000); | ||
| 501 | if (ret) { | ||
| 502 | printk(KERN_WARNING"%s Erase block error %d at : %llx\n", | ||
| 503 | map->name, ret, adr); | ||
| 504 | goto out; | ||
| 505 | } | ||
| 506 | out: put_chip(map, chip); | ||
| 507 | spin_unlock(chip->mutex); | ||
| 508 | return ret; | ||
| 509 | } | ||
| 510 | |||
| 511 | static int lpddr_read(struct mtd_info *mtd, loff_t adr, size_t len, | ||
| 512 | size_t *retlen, u_char *buf) | ||
| 513 | { | ||
| 514 | struct map_info *map = mtd->priv; | ||
| 515 | struct lpddr_private *lpddr = map->fldrv_priv; | ||
| 516 | int chipnum = adr >> lpddr->chipshift; | ||
| 517 | struct flchip *chip = &lpddr->chips[chipnum]; | ||
| 518 | int ret = 0; | ||
| 519 | |||
| 520 | spin_lock(chip->mutex); | ||
| 521 | ret = get_chip(map, chip, FL_READY); | ||
| 522 | if (ret) { | ||
| 523 | spin_unlock(chip->mutex); | ||
| 524 | return ret; | ||
| 525 | } | ||
| 526 | |||
| 527 | map_copy_from(map, buf, adr, len); | ||
| 528 | *retlen = len; | ||
| 529 | |||
| 530 | put_chip(map, chip); | ||
| 531 | spin_unlock(chip->mutex); | ||
| 532 | return ret; | ||
| 533 | } | ||
| 534 | |||
| 535 | static int lpddr_point(struct mtd_info *mtd, loff_t adr, size_t len, | ||
| 536 | size_t *retlen, void **mtdbuf, resource_size_t *phys) | ||
| 537 | { | ||
| 538 | struct map_info *map = mtd->priv; | ||
| 539 | struct lpddr_private *lpddr = map->fldrv_priv; | ||
| 540 | int chipnum = adr >> lpddr->chipshift; | ||
| 541 | unsigned long ofs, last_end = 0; | ||
| 542 | struct flchip *chip = &lpddr->chips[chipnum]; | ||
| 543 | int ret = 0; | ||
| 544 | |||
| 545 | if (!map->virt || (adr + len > mtd->size)) | ||
| 546 | return -EINVAL; | ||
| 547 | |||
| 548 | /* ofs: offset within the first chip that the first read should start */ | ||
| 549 | ofs = adr - (chipnum << lpddr->chipshift); | ||
| 550 | |||
| 551 | *mtdbuf = (void *)map->virt + chip->start + ofs; | ||
| 552 | *retlen = 0; | ||
| 553 | |||
| 554 | while (len) { | ||
| 555 | unsigned long thislen; | ||
| 556 | |||
| 557 | if (chipnum >= lpddr->numchips) | ||
| 558 | break; | ||
| 559 | |||
| 560 | /* We cannot point across chips that are virtually disjoint */ | ||
| 561 | if (!last_end) | ||
| 562 | last_end = chip->start; | ||
| 563 | else if (chip->start != last_end) | ||
| 564 | break; | ||
| 565 | |||
| 566 | if ((len + ofs - 1) >> lpddr->chipshift) | ||
| 567 | thislen = (1<<lpddr->chipshift) - ofs; | ||
| 568 | else | ||
| 569 | thislen = len; | ||
| 570 | /* get the chip */ | ||
| 571 | spin_lock(chip->mutex); | ||
| 572 | ret = get_chip(map, chip, FL_POINT); | ||
| 573 | spin_unlock(chip->mutex); | ||
| 574 | if (ret) | ||
| 575 | break; | ||
| 576 | |||
| 577 | chip->state = FL_POINT; | ||
| 578 | chip->ref_point_counter++; | ||
| 579 | *retlen += thislen; | ||
| 580 | len -= thislen; | ||
| 581 | |||
| 582 | ofs = 0; | ||
| 583 | last_end += 1 << lpddr->chipshift; | ||
| 584 | chipnum++; | ||
| 585 | chip = &lpddr->chips[chipnum]; | ||
| 586 | } | ||
| 587 | return 0; | ||
| 588 | } | ||
| 589 | |||
| 590 | static void lpddr_unpoint (struct mtd_info *mtd, loff_t adr, size_t len) | ||
| 591 | { | ||
| 592 | struct map_info *map = mtd->priv; | ||
| 593 | struct lpddr_private *lpddr = map->fldrv_priv; | ||
| 594 | int chipnum = adr >> lpddr->chipshift; | ||
| 595 | unsigned long ofs; | ||
| 596 | |||
| 597 | /* ofs: offset within the first chip that the first read should start */ | ||
| 598 | ofs = adr - (chipnum << lpddr->chipshift); | ||
| 599 | |||
| 600 | while (len) { | ||
| 601 | unsigned long thislen; | ||
| 602 | struct flchip *chip; | ||
| 603 | |||
| 604 | chip = &lpddr->chips[chipnum]; | ||
| 605 | if (chipnum >= lpddr->numchips) | ||
| 606 | break; | ||
| 607 | |||
| 608 | if ((len + ofs - 1) >> lpddr->chipshift) | ||
| 609 | thislen = (1<<lpddr->chipshift) - ofs; | ||
| 610 | else | ||
| 611 | thislen = len; | ||
| 612 | |||
| 613 | spin_lock(chip->mutex); | ||
| 614 | if (chip->state == FL_POINT) { | ||
| 615 | chip->ref_point_counter--; | ||
| 616 | if (chip->ref_point_counter == 0) | ||
| 617 | chip->state = FL_READY; | ||
| 618 | } else | ||
| 619 | printk(KERN_WARNING "%s: Warning: unpoint called on non" | ||
| 620 | "pointed region\n", map->name); | ||
| 621 | |||
| 622 | put_chip(map, chip); | ||
| 623 | spin_unlock(chip->mutex); | ||
| 624 | |||
| 625 | len -= thislen; | ||
| 626 | ofs = 0; | ||
| 627 | chipnum++; | ||
| 628 | } | ||
| 629 | } | ||
| 630 | |||
| 631 | static int lpddr_write_buffers(struct mtd_info *mtd, loff_t to, size_t len, | ||
| 632 | size_t *retlen, const u_char *buf) | ||
| 633 | { | ||
| 634 | struct kvec vec; | ||
| 635 | |||
| 636 | vec.iov_base = (void *) buf; | ||
| 637 | vec.iov_len = len; | ||
| 638 | |||
| 639 | return lpddr_writev(mtd, &vec, 1, to, retlen); | ||
| 640 | } | ||
| 641 | |||
| 642 | |||
| 643 | static int lpddr_writev(struct mtd_info *mtd, const struct kvec *vecs, | ||
| 644 | unsigned long count, loff_t to, size_t *retlen) | ||
| 645 | { | ||
| 646 | struct map_info *map = mtd->priv; | ||
| 647 | struct lpddr_private *lpddr = map->fldrv_priv; | ||
| 648 | int ret = 0; | ||
| 649 | int chipnum; | ||
| 650 | unsigned long ofs, vec_seek, i; | ||
| 651 | int wbufsize = 1 << lpddr->qinfo->BufSizeShift; | ||
| 652 | |||
| 653 | size_t len = 0; | ||
| 654 | |||
| 655 | for (i = 0; i < count; i++) | ||
| 656 | len += vecs[i].iov_len; | ||
| 657 | |||
| 658 | *retlen = 0; | ||
| 659 | if (!len) | ||
| 660 | return 0; | ||
| 661 | |||
| 662 | chipnum = to >> lpddr->chipshift; | ||
| 663 | |||
| 664 | ofs = to; | ||
| 665 | vec_seek = 0; | ||
| 666 | |||
| 667 | do { | ||
| 668 | /* We must not cross write block boundaries */ | ||
| 669 | int size = wbufsize - (ofs & (wbufsize-1)); | ||
| 670 | |||
| 671 | if (size > len) | ||
| 672 | size = len; | ||
| 673 | |||
| 674 | ret = do_write_buffer(map, &lpddr->chips[chipnum], | ||
| 675 | ofs, &vecs, &vec_seek, size); | ||
| 676 | if (ret) | ||
| 677 | return ret; | ||
| 678 | |||
| 679 | ofs += size; | ||
| 680 | (*retlen) += size; | ||
| 681 | len -= size; | ||
| 682 | |||
| 683 | /* Be nice and reschedule with the chip in a usable | ||
| 684 | * state for other processes */ | ||
| 685 | cond_resched(); | ||
| 686 | |||
| 687 | } while (len); | ||
| 688 | |||
| 689 | return 0; | ||
| 690 | } | ||
| 691 | |||
| 692 | static int lpddr_erase(struct mtd_info *mtd, struct erase_info *instr) | ||
| 693 | { | ||
| 694 | unsigned long ofs, len; | ||
| 695 | int ret; | ||
| 696 | struct map_info *map = mtd->priv; | ||
| 697 | struct lpddr_private *lpddr = map->fldrv_priv; | ||
| 698 | int size = 1 << lpddr->qinfo->UniformBlockSizeShift; | ||
| 699 | |||
| 700 | ofs = instr->addr; | ||
| 701 | len = instr->len; | ||
| 702 | |||
| 703 | if (ofs > mtd->size || (len + ofs) > mtd->size) | ||
| 704 | return -EINVAL; | ||
| 705 | |||
| 706 | while (len > 0) { | ||
| 707 | ret = do_erase_oneblock(mtd, ofs); | ||
| 708 | if (ret) | ||
| 709 | return ret; | ||
| 710 | ofs += size; | ||
| 711 | len -= size; | ||
| 712 | } | ||
| 713 | instr->state = MTD_ERASE_DONE; | ||
| 714 | mtd_erase_callback(instr); | ||
| 715 | |||
| 716 | return 0; | ||
| 717 | } | ||
| 718 | |||
| 719 | #define DO_XXLOCK_LOCK 1 | ||
| 720 | #define DO_XXLOCK_UNLOCK 2 | ||
| 721 | int do_xxlock(struct mtd_info *mtd, loff_t adr, uint32_t len, int thunk) | ||
| 722 | { | ||
| 723 | int ret = 0; | ||
| 724 | struct map_info *map = mtd->priv; | ||
| 725 | struct lpddr_private *lpddr = map->fldrv_priv; | ||
| 726 | int chipnum = adr >> lpddr->chipshift; | ||
| 727 | struct flchip *chip = &lpddr->chips[chipnum]; | ||
| 728 | |||
| 729 | spin_lock(chip->mutex); | ||
| 730 | ret = get_chip(map, chip, FL_LOCKING); | ||
| 731 | if (ret) { | ||
| 732 | spin_unlock(chip->mutex); | ||
| 733 | return ret; | ||
| 734 | } | ||
| 735 | |||
| 736 | if (thunk == DO_XXLOCK_LOCK) { | ||
| 737 | send_pfow_command(map, LPDDR_LOCK_BLOCK, adr, adr + len, NULL); | ||
| 738 | chip->state = FL_LOCKING; | ||
| 739 | } else if (thunk == DO_XXLOCK_UNLOCK) { | ||
| 740 | send_pfow_command(map, LPDDR_UNLOCK_BLOCK, adr, adr + len, NULL); | ||
| 741 | chip->state = FL_UNLOCKING; | ||
| 742 | } else | ||
| 743 | BUG(); | ||
| 744 | |||
| 745 | ret = wait_for_ready(map, chip, 1); | ||
| 746 | if (ret) { | ||
| 747 | printk(KERN_ERR "%s: block unlock error status %d \n", | ||
| 748 | map->name, ret); | ||
| 749 | goto out; | ||
| 750 | } | ||
| 751 | out: put_chip(map, chip); | ||
| 752 | spin_unlock(chip->mutex); | ||
| 753 | return ret; | ||
| 754 | } | ||
| 755 | |||
| 756 | static int lpddr_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) | ||
| 757 | { | ||
| 758 | return do_xxlock(mtd, ofs, len, DO_XXLOCK_LOCK); | ||
| 759 | } | ||
| 760 | |||
| 761 | static int lpddr_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) | ||
| 762 | { | ||
| 763 | return do_xxlock(mtd, ofs, len, DO_XXLOCK_UNLOCK); | ||
| 764 | } | ||
| 765 | |||
| 766 | int word_program(struct map_info *map, loff_t adr, uint32_t curval) | ||
| 767 | { | ||
| 768 | int ret; | ||
| 769 | struct lpddr_private *lpddr = map->fldrv_priv; | ||
| 770 | int chipnum = adr >> lpddr->chipshift; | ||
| 771 | struct flchip *chip = &lpddr->chips[chipnum]; | ||
| 772 | |||
| 773 | spin_lock(chip->mutex); | ||
| 774 | ret = get_chip(map, chip, FL_WRITING); | ||
| 775 | if (ret) { | ||
| 776 | spin_unlock(chip->mutex); | ||
| 777 | return ret; | ||
| 778 | } | ||
| 779 | |||
| 780 | send_pfow_command(map, LPDDR_WORD_PROGRAM, adr, 0x00, (map_word *)&curval); | ||
| 781 | |||
| 782 | ret = wait_for_ready(map, chip, (1<<lpddr->qinfo->SingleWordProgTime)); | ||
| 783 | if (ret) { | ||
| 784 | printk(KERN_WARNING"%s word_program error at: %llx; val: %x\n", | ||
| 785 | map->name, adr, curval); | ||
| 786 | goto out; | ||
| 787 | } | ||
| 788 | |||
| 789 | out: put_chip(map, chip); | ||
| 790 | spin_unlock(chip->mutex); | ||
| 791 | return ret; | ||
| 792 | } | ||
| 793 | |||
| 794 | MODULE_LICENSE("GPL"); | ||
| 795 | MODULE_AUTHOR("Alexey Korolev <akorolev@infradead.org>"); | ||
| 796 | MODULE_DESCRIPTION("MTD driver for LPDDR flash chips"); | ||
diff --git a/drivers/mtd/lpddr/qinfo_probe.c b/drivers/mtd/lpddr/qinfo_probe.c new file mode 100644 index 000000000000..79bf40f48b75 --- /dev/null +++ b/drivers/mtd/lpddr/qinfo_probe.c | |||
| @@ -0,0 +1,255 @@ | |||
| 1 | /* | ||
| 2 | * Probing flash chips with QINFO records. | ||
| 3 | * (C) 2008 Korolev Alexey <akorolev@infradead.org> | ||
| 4 | * (C) 2008 Vasiliy Leonenko <vasiliy.leonenko@gmail.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU General Public License | ||
| 8 | * as published by the Free Software Foundation; either version 2 | ||
| 9 | * of the License, or (at your option) any later version. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
| 19 | * 02110-1301, USA. | ||
| 20 | */ | ||
| 21 | #include <linux/module.h> | ||
| 22 | #include <linux/types.h> | ||
| 23 | #include <linux/kernel.h> | ||
| 24 | #include <linux/init.h> | ||
| 25 | #include <linux/errno.h> | ||
| 26 | #include <linux/slab.h> | ||
| 27 | #include <linux/interrupt.h> | ||
| 28 | |||
| 29 | #include <linux/mtd/xip.h> | ||
| 30 | #include <linux/mtd/map.h> | ||
| 31 | #include <linux/mtd/pfow.h> | ||
| 32 | #include <linux/mtd/qinfo.h> | ||
| 33 | |||
| 34 | static int lpddr_chip_setup(struct map_info *map, struct lpddr_private *lpddr); | ||
| 35 | struct mtd_info *lpddr_probe(struct map_info *map); | ||
| 36 | static struct lpddr_private *lpddr_probe_chip(struct map_info *map); | ||
| 37 | static int lpddr_pfow_present(struct map_info *map, | ||
| 38 | struct lpddr_private *lpddr); | ||
| 39 | |||
| 40 | static struct qinfo_query_info qinfo_array[] = { | ||
| 41 | /* General device info */ | ||
| 42 | {0, 0, "DevSizeShift", "Device size 2^n bytes"}, | ||
| 43 | {0, 3, "BufSizeShift", "Program buffer size 2^n bytes"}, | ||
| 44 | /* Erase block information */ | ||
| 45 | {1, 1, "TotalBlocksNum", "Total number of blocks"}, | ||
| 46 | {1, 2, "UniformBlockSizeShift", "Uniform block size 2^n bytes"}, | ||
| 47 | /* Partition information */ | ||
| 48 | {2, 1, "HWPartsNum", "Number of hardware partitions"}, | ||
| 49 | /* Optional features */ | ||
| 50 | {5, 1, "SuspEraseSupp", "Suspend erase supported"}, | ||
| 51 | /* Operation typical time */ | ||
| 52 | {10, 0, "SingleWordProgTime", "Single word program 2^n u-sec"}, | ||
| 53 | {10, 1, "ProgBufferTime", "Program buffer write 2^n u-sec"}, | ||
| 54 | {10, 2, "BlockEraseTime", "Block erase 2^n m-sec"}, | ||
| 55 | {10, 3, "FullChipEraseTime", "Full chip erase 2^n m-sec"}, | ||
| 56 | }; | ||
| 57 | |||
| 58 | static long lpddr_get_qinforec_pos(struct map_info *map, char *id_str) | ||
| 59 | { | ||
| 60 | int qinfo_lines = sizeof(qinfo_array)/sizeof(struct qinfo_query_info); | ||
| 61 | int i; | ||
| 62 | int bankwidth = map_bankwidth(map) * 8; | ||
| 63 | int major, minor; | ||
| 64 | |||
| 65 | for (i = 0; i < qinfo_lines; i++) { | ||
| 66 | if (strcmp(id_str, qinfo_array[i].id_str) == 0) { | ||
| 67 | major = qinfo_array[i].major & ((1 << bankwidth) - 1); | ||
| 68 | minor = qinfo_array[i].minor & ((1 << bankwidth) - 1); | ||
| 69 | return minor | (major << bankwidth); | ||
| 70 | } | ||
| 71 | } | ||
| 72 | printk(KERN_ERR"%s qinfo id string is wrong! \n", map->name); | ||
| 73 | BUG(); | ||
| 74 | return -1; | ||
| 75 | } | ||
| 76 | |||
| 77 | static uint16_t lpddr_info_query(struct map_info *map, char *id_str) | ||
| 78 | { | ||
| 79 | unsigned int dsr, val; | ||
| 80 | int bits_per_chip = map_bankwidth(map) * 8; | ||
| 81 | unsigned long adr = lpddr_get_qinforec_pos(map, id_str); | ||
| 82 | int attempts = 20; | ||
| 83 | |||
| 84 | /* Write a request for the PFOW record */ | ||
| 85 | map_write(map, CMD(LPDDR_INFO_QUERY), | ||
| 86 | map->pfow_base + PFOW_COMMAND_CODE); | ||
| 87 | map_write(map, CMD(adr & ((1 << bits_per_chip) - 1)), | ||
| 88 | map->pfow_base + PFOW_COMMAND_ADDRESS_L); | ||
| 89 | map_write(map, CMD(adr >> bits_per_chip), | ||
| 90 | map->pfow_base + PFOW_COMMAND_ADDRESS_H); | ||
| 91 | map_write(map, CMD(LPDDR_START_EXECUTION), | ||
| 92 | map->pfow_base + PFOW_COMMAND_EXECUTE); | ||
| 93 | |||
| 94 | while ((attempts--) > 0) { | ||
| 95 | dsr = CMDVAL(map_read(map, map->pfow_base + PFOW_DSR)); | ||
| 96 | if (dsr & DSR_READY_STATUS) | ||
| 97 | break; | ||
| 98 | udelay(10); | ||
| 99 | } | ||
| 100 | |||
| 101 | val = CMDVAL(map_read(map, map->pfow_base + PFOW_COMMAND_DATA)); | ||
| 102 | return val; | ||
| 103 | } | ||
| 104 | |||
| 105 | static int lpddr_pfow_present(struct map_info *map, struct lpddr_private *lpddr) | ||
| 106 | { | ||
| 107 | map_word pfow_val[4]; | ||
| 108 | |||
| 109 | /* Check identification string */ | ||
| 110 | pfow_val[0] = map_read(map, map->pfow_base + PFOW_QUERY_STRING_P); | ||
| 111 | pfow_val[1] = map_read(map, map->pfow_base + PFOW_QUERY_STRING_F); | ||
| 112 | pfow_val[2] = map_read(map, map->pfow_base + PFOW_QUERY_STRING_O); | ||
| 113 | pfow_val[3] = map_read(map, map->pfow_base + PFOW_QUERY_STRING_W); | ||
| 114 | |||
| 115 | if (!map_word_equal(map, CMD('P'), pfow_val[0])) | ||
| 116 | goto out; | ||
| 117 | |||
| 118 | if (!map_word_equal(map, CMD('F'), pfow_val[1])) | ||
| 119 | goto out; | ||
| 120 | |||
| 121 | if (!map_word_equal(map, CMD('O'), pfow_val[2])) | ||
| 122 | goto out; | ||
| 123 | |||
| 124 | if (!map_word_equal(map, CMD('W'), pfow_val[3])) | ||
| 125 | goto out; | ||
| 126 | |||
| 127 | return 1; /* "PFOW" is found */ | ||
| 128 | out: | ||
| 129 | printk(KERN_WARNING"%s: PFOW string at 0x%lx is not found \n", | ||
| 130 | map->name, map->pfow_base); | ||
| 131 | return 0; | ||
| 132 | } | ||
| 133 | |||
| 134 | static int lpddr_chip_setup(struct map_info *map, struct lpddr_private *lpddr) | ||
| 135 | { | ||
| 136 | |||
| 137 | lpddr->qinfo = kmalloc(sizeof(struct qinfo_chip), GFP_KERNEL); | ||
| 138 | if (!lpddr->qinfo) { | ||
| 139 | printk(KERN_WARNING "%s: no memory for LPDDR qinfo structure\n", | ||
| 140 | map->name); | ||
| 141 | return 0; | ||
| 142 | } | ||
| 143 | memset(lpddr->qinfo, 0, sizeof(struct qinfo_chip)); | ||
| 144 | |||
| 145 | /* Get the ManuID */ | ||
| 146 | lpddr->ManufactId = CMDVAL(map_read(map, map->pfow_base + PFOW_MANUFACTURER_ID)); | ||
| 147 | /* Get the DeviceID */ | ||
| 148 | lpddr->DevId = CMDVAL(map_read(map, map->pfow_base + PFOW_DEVICE_ID)); | ||
| 149 | /* read parameters from chip qinfo table */ | ||
| 150 | lpddr->qinfo->DevSizeShift = lpddr_info_query(map, "DevSizeShift"); | ||
| 151 | lpddr->qinfo->TotalBlocksNum = lpddr_info_query(map, "TotalBlocksNum"); | ||
| 152 | lpddr->qinfo->BufSizeShift = lpddr_info_query(map, "BufSizeShift"); | ||
| 153 | lpddr->qinfo->HWPartsNum = lpddr_info_query(map, "HWPartsNum"); | ||
| 154 | lpddr->qinfo->UniformBlockSizeShift = | ||
| 155 | lpddr_info_query(map, "UniformBlockSizeShift"); | ||
| 156 | lpddr->qinfo->SuspEraseSupp = lpddr_info_query(map, "SuspEraseSupp"); | ||
| 157 | lpddr->qinfo->SingleWordProgTime = | ||
| 158 | lpddr_info_query(map, "SingleWordProgTime"); | ||
| 159 | lpddr->qinfo->ProgBufferTime = lpddr_info_query(map, "ProgBufferTime"); | ||
| 160 | lpddr->qinfo->BlockEraseTime = lpddr_info_query(map, "BlockEraseTime"); | ||
| 161 | return 1; | ||
| 162 | } | ||
| 163 | static struct lpddr_private *lpddr_probe_chip(struct map_info *map) | ||
| 164 | { | ||
| 165 | struct lpddr_private lpddr; | ||
| 166 | struct lpddr_private *retlpddr; | ||
| 167 | int numvirtchips; | ||
| 168 | |||
| 169 | |||
| 170 | if ((map->pfow_base + 0x1000) >= map->size) { | ||
| 171 | printk(KERN_NOTICE"%s Probe at base (0x%08lx) past the end of" | ||
| 172 | "the map(0x%08lx)\n", map->name, | ||
| 173 | (unsigned long)map->pfow_base, map->size - 1); | ||
| 174 | return NULL; | ||
| 175 | } | ||
| 176 | memset(&lpddr, 0, sizeof(struct lpddr_private)); | ||
| 177 | if (!lpddr_pfow_present(map, &lpddr)) | ||
| 178 | return NULL; | ||
| 179 | |||
| 180 | if (!lpddr_chip_setup(map, &lpddr)) | ||
| 181 | return NULL; | ||
| 182 | |||
| 183 | /* Ok so we found a chip */ | ||
| 184 | lpddr.chipshift = lpddr.qinfo->DevSizeShift; | ||
| 185 | lpddr.numchips = 1; | ||
| 186 | |||
| 187 | numvirtchips = lpddr.numchips * lpddr.qinfo->HWPartsNum; | ||
| 188 | retlpddr = kmalloc(sizeof(struct lpddr_private) + | ||
| 189 | numvirtchips * sizeof(struct flchip), GFP_KERNEL); | ||
| 190 | if (!retlpddr) | ||
| 191 | return NULL; | ||
| 192 | |||
| 193 | memset(retlpddr, 0, sizeof(struct lpddr_private) + | ||
| 194 | numvirtchips * sizeof(struct flchip)); | ||
| 195 | memcpy(retlpddr, &lpddr, sizeof(struct lpddr_private)); | ||
| 196 | |||
| 197 | retlpddr->numchips = numvirtchips; | ||
| 198 | retlpddr->chipshift = retlpddr->qinfo->DevSizeShift - | ||
| 199 | __ffs(retlpddr->qinfo->HWPartsNum); | ||
| 200 | |||
| 201 | return retlpddr; | ||
| 202 | } | ||
| 203 | |||
| 204 | struct mtd_info *lpddr_probe(struct map_info *map) | ||
| 205 | { | ||
| 206 | struct mtd_info *mtd = NULL; | ||
| 207 | struct lpddr_private *lpddr; | ||
| 208 | |||
| 209 | /* First probe the map to see if we havecan open PFOW here */ | ||
| 210 | lpddr = lpddr_probe_chip(map); | ||
| 211 | if (!lpddr) | ||
| 212 | return NULL; | ||
| 213 | |||
| 214 | map->fldrv_priv = lpddr; | ||
| 215 | mtd = lpddr_cmdset(map); | ||
| 216 | if (mtd) { | ||
| 217 | if (mtd->size > map->size) { | ||
| 218 | printk(KERN_WARNING "Reducing visibility of %ldKiB chip" | ||
| 219 | "to %ldKiB\n", (unsigned long)mtd->size >> 10, | ||
| 220 | (unsigned long)map->size >> 10); | ||
| 221 | mtd->size = map->size; | ||
| 222 | } | ||
| 223 | return mtd; | ||
| 224 | } | ||
| 225 | |||
| 226 | kfree(lpddr->qinfo); | ||
| 227 | kfree(lpddr); | ||
| 228 | map->fldrv_priv = NULL; | ||
| 229 | return NULL; | ||
| 230 | } | ||
| 231 | |||
| 232 | static struct mtd_chip_driver lpddr_chipdrv = { | ||
| 233 | .probe = lpddr_probe, | ||
| 234 | .name = "qinfo_probe", | ||
| 235 | .module = THIS_MODULE | ||
| 236 | }; | ||
| 237 | |||
| 238 | static int __init lpddr_probe_init(void) | ||
| 239 | { | ||
| 240 | register_mtd_chip_driver(&lpddr_chipdrv); | ||
| 241 | return 0; | ||
| 242 | } | ||
| 243 | |||
| 244 | static void __exit lpddr_probe_exit(void) | ||
| 245 | { | ||
| 246 | unregister_mtd_chip_driver(&lpddr_chipdrv); | ||
| 247 | } | ||
| 248 | |||
| 249 | module_init(lpddr_probe_init); | ||
| 250 | module_exit(lpddr_probe_exit); | ||
| 251 | |||
| 252 | MODULE_LICENSE("GPL"); | ||
| 253 | MODULE_AUTHOR("Vasiliy Leonenko <vasiliy.leonenko@gmail.com>"); | ||
| 254 | MODULE_DESCRIPTION("Driver to probe qinfo flash chips"); | ||
| 255 | |||
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 5ea169362164..0225cbbf22de 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig | |||
| @@ -10,8 +10,8 @@ config MTD_COMPLEX_MAPPINGS | |||
| 10 | paged mappings of flash chips. | 10 | paged mappings of flash chips. |
| 11 | 11 | ||
| 12 | config MTD_PHYSMAP | 12 | config MTD_PHYSMAP |
| 13 | tristate "CFI Flash device in physical memory map" | 13 | tristate "Flash device in physical memory map" |
| 14 | depends on MTD_CFI || MTD_JEDECPROBE || MTD_ROM | 14 | depends on MTD_CFI || MTD_JEDECPROBE || MTD_ROM || MTD_LPDDR |
| 15 | help | 15 | help |
| 16 | This provides a 'mapping' driver which allows the NOR Flash and | 16 | This provides a 'mapping' driver which allows the NOR Flash and |
| 17 | ROM driver code to communicate with chips which are mapped | 17 | ROM driver code to communicate with chips which are mapped |
| @@ -23,9 +23,20 @@ config MTD_PHYSMAP | |||
| 23 | To compile this driver as a module, choose M here: the | 23 | To compile this driver as a module, choose M here: the |
| 24 | module will be called physmap. | 24 | module will be called physmap. |
| 25 | 25 | ||
| 26 | config MTD_PHYSMAP_COMPAT | ||
| 27 | bool "Physmap compat support" | ||
| 28 | depends on MTD_PHYSMAP | ||
| 29 | default n | ||
| 30 | help | ||
| 31 | Setup a simple mapping via the Kconfig options. Normally the | ||
| 32 | physmap configuration options are done via your board's | ||
| 33 | resource file. | ||
| 34 | |||
| 35 | If unsure, say N here. | ||
| 36 | |||
| 26 | config MTD_PHYSMAP_START | 37 | config MTD_PHYSMAP_START |
| 27 | hex "Physical start address of flash mapping" | 38 | hex "Physical start address of flash mapping" |
| 28 | depends on MTD_PHYSMAP | 39 | depends on MTD_PHYSMAP_COMPAT |
| 29 | default "0x8000000" | 40 | default "0x8000000" |
| 30 | help | 41 | help |
| 31 | This is the physical memory location at which the flash chips | 42 | This is the physical memory location at which the flash chips |
| @@ -37,7 +48,7 @@ config MTD_PHYSMAP_START | |||
| 37 | 48 | ||
| 38 | config MTD_PHYSMAP_LEN | 49 | config MTD_PHYSMAP_LEN |
| 39 | hex "Physical length of flash mapping" | 50 | hex "Physical length of flash mapping" |
| 40 | depends on MTD_PHYSMAP | 51 | depends on MTD_PHYSMAP_COMPAT |
| 41 | default "0" | 52 | default "0" |
| 42 | help | 53 | help |
| 43 | This is the total length of the mapping of the flash chips on | 54 | This is the total length of the mapping of the flash chips on |
| @@ -51,7 +62,7 @@ config MTD_PHYSMAP_LEN | |||
| 51 | 62 | ||
| 52 | config MTD_PHYSMAP_BANKWIDTH | 63 | config MTD_PHYSMAP_BANKWIDTH |
| 53 | int "Bank width in octets" | 64 | int "Bank width in octets" |
| 54 | depends on MTD_PHYSMAP | 65 | depends on MTD_PHYSMAP_COMPAT |
| 55 | default "2" | 66 | default "2" |
| 56 | help | 67 | help |
| 57 | This is the total width of the data bus of the flash devices | 68 | This is the total width of the data bus of the flash devices |
diff --git a/drivers/mtd/maps/alchemy-flash.c b/drivers/mtd/maps/alchemy-flash.c index 82811bcb0436..845ad4f2a542 100644 --- a/drivers/mtd/maps/alchemy-flash.c +++ b/drivers/mtd/maps/alchemy-flash.c | |||
| @@ -111,7 +111,7 @@ static struct mtd_partition alchemy_partitions[] = { | |||
| 111 | 111 | ||
| 112 | static struct mtd_info *mymtd; | 112 | static struct mtd_info *mymtd; |
| 113 | 113 | ||
| 114 | int __init alchemy_mtd_init(void) | 114 | static int __init alchemy_mtd_init(void) |
| 115 | { | 115 | { |
| 116 | struct mtd_partition *parts; | 116 | struct mtd_partition *parts; |
| 117 | int nb_parts = 0; | 117 | int nb_parts = 0; |
diff --git a/drivers/mtd/maps/amd76xrom.c b/drivers/mtd/maps/amd76xrom.c index d1eec7d3243f..237733d094c4 100644 --- a/drivers/mtd/maps/amd76xrom.c +++ b/drivers/mtd/maps/amd76xrom.c | |||
| @@ -232,8 +232,8 @@ static int __devinit amd76xrom_init_one (struct pci_dev *pdev, | |||
| 232 | /* Trim the size if we are larger than the map */ | 232 | /* Trim the size if we are larger than the map */ |
| 233 | if (map->mtd->size > map->map.size) { | 233 | if (map->mtd->size > map->map.size) { |
| 234 | printk(KERN_WARNING MOD_NAME | 234 | printk(KERN_WARNING MOD_NAME |
| 235 | " rom(%u) larger than window(%lu). fixing...\n", | 235 | " rom(%llu) larger than window(%lu). fixing...\n", |
| 236 | map->mtd->size, map->map.size); | 236 | (unsigned long long)map->mtd->size, map->map.size); |
| 237 | map->mtd->size = map->map.size; | 237 | map->mtd->size = map->map.size; |
| 238 | } | 238 | } |
| 239 | if (window->rsrc.parent) { | 239 | if (window->rsrc.parent) { |
diff --git a/drivers/mtd/maps/cfi_flagadm.c b/drivers/mtd/maps/cfi_flagadm.c index 0ecc3f6d735b..b4ed81611918 100644 --- a/drivers/mtd/maps/cfi_flagadm.c +++ b/drivers/mtd/maps/cfi_flagadm.c | |||
| @@ -88,7 +88,7 @@ struct mtd_partition flagadm_parts[] = { | |||
| 88 | 88 | ||
| 89 | static struct mtd_info *mymtd; | 89 | static struct mtd_info *mymtd; |
| 90 | 90 | ||
| 91 | int __init init_flagadm(void) | 91 | static int __init init_flagadm(void) |
| 92 | { | 92 | { |
| 93 | printk(KERN_NOTICE "FlagaDM flash device: %x at %x\n", | 93 | printk(KERN_NOTICE "FlagaDM flash device: %x at %x\n", |
| 94 | FLASH_SIZE, FLASH_PHYS_ADDR); | 94 | FLASH_SIZE, FLASH_PHYS_ADDR); |
diff --git a/drivers/mtd/maps/ck804xrom.c b/drivers/mtd/maps/ck804xrom.c index 1a6feb4474de..5f7a245ed132 100644 --- a/drivers/mtd/maps/ck804xrom.c +++ b/drivers/mtd/maps/ck804xrom.c | |||
| @@ -263,8 +263,8 @@ static int __devinit ck804xrom_init_one (struct pci_dev *pdev, | |||
| 263 | /* Trim the size if we are larger than the map */ | 263 | /* Trim the size if we are larger than the map */ |
| 264 | if (map->mtd->size > map->map.size) { | 264 | if (map->mtd->size > map->map.size) { |
| 265 | printk(KERN_WARNING MOD_NAME | 265 | printk(KERN_WARNING MOD_NAME |
| 266 | " rom(%u) larger than window(%lu). fixing...\n", | 266 | " rom(%llu) larger than window(%lu). fixing...\n", |
| 267 | map->mtd->size, map->map.size); | 267 | (unsigned long long)map->mtd->size, map->map.size); |
| 268 | map->mtd->size = map->map.size; | 268 | map->mtd->size = map->map.size; |
| 269 | } | 269 | } |
| 270 | if (window->rsrc.parent) { | 270 | if (window->rsrc.parent) { |
diff --git a/drivers/mtd/maps/dbox2-flash.c b/drivers/mtd/maps/dbox2-flash.c index e115667bf1d0..cfacfa6f45dd 100644 --- a/drivers/mtd/maps/dbox2-flash.c +++ b/drivers/mtd/maps/dbox2-flash.c | |||
| @@ -69,7 +69,7 @@ struct map_info dbox2_flash_map = { | |||
| 69 | .phys = WINDOW_ADDR, | 69 | .phys = WINDOW_ADDR, |
| 70 | }; | 70 | }; |
| 71 | 71 | ||
| 72 | int __init init_dbox2_flash(void) | 72 | static int __init init_dbox2_flash(void) |
| 73 | { | 73 | { |
| 74 | printk(KERN_NOTICE "D-Box 2 flash driver (size->0x%X mem->0x%X)\n", WINDOW_SIZE, WINDOW_ADDR); | 74 | printk(KERN_NOTICE "D-Box 2 flash driver (size->0x%X mem->0x%X)\n", WINDOW_SIZE, WINDOW_ADDR); |
| 75 | dbox2_flash_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE); | 75 | dbox2_flash_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE); |
diff --git a/drivers/mtd/maps/edb7312.c b/drivers/mtd/maps/edb7312.c index 9433738c1664..be9e90b44587 100644 --- a/drivers/mtd/maps/edb7312.c +++ b/drivers/mtd/maps/edb7312.c | |||
| @@ -71,7 +71,7 @@ static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; | |||
| 71 | static int mtd_parts_nb = 0; | 71 | static int mtd_parts_nb = 0; |
| 72 | static struct mtd_partition *mtd_parts = 0; | 72 | static struct mtd_partition *mtd_parts = 0; |
| 73 | 73 | ||
| 74 | int __init init_edb7312nor(void) | 74 | static int __init init_edb7312nor(void) |
| 75 | { | 75 | { |
| 76 | static const char *rom_probe_types[] = PROBETYPES; | 76 | static const char *rom_probe_types[] = PROBETYPES; |
| 77 | const char **type; | 77 | const char **type; |
diff --git a/drivers/mtd/maps/esb2rom.c b/drivers/mtd/maps/esb2rom.c index bbbcdd4c8d13..11a2f57df9cf 100644 --- a/drivers/mtd/maps/esb2rom.c +++ b/drivers/mtd/maps/esb2rom.c | |||
| @@ -324,8 +324,8 @@ static int __devinit esb2rom_init_one(struct pci_dev *pdev, | |||
| 324 | /* Trim the size if we are larger than the map */ | 324 | /* Trim the size if we are larger than the map */ |
| 325 | if (map->mtd->size > map->map.size) { | 325 | if (map->mtd->size > map->map.size) { |
| 326 | printk(KERN_WARNING MOD_NAME | 326 | printk(KERN_WARNING MOD_NAME |
| 327 | " rom(%u) larger than window(%lu). fixing...\n", | 327 | " rom(%llu) larger than window(%lu). fixing...\n", |
| 328 | map->mtd->size, map->map.size); | 328 | (unsigned long long)map->mtd->size, map->map.size); |
| 329 | map->mtd->size = map->map.size; | 329 | map->mtd->size = map->map.size; |
| 330 | } | 330 | } |
| 331 | if (window->rsrc.parent) { | 331 | if (window->rsrc.parent) { |
diff --git a/drivers/mtd/maps/fortunet.c b/drivers/mtd/maps/fortunet.c index a8e3fde4cbd5..1e43124d498b 100644 --- a/drivers/mtd/maps/fortunet.c +++ b/drivers/mtd/maps/fortunet.c | |||
| @@ -181,7 +181,7 @@ __setup("MTD_Partition=", MTD_New_Partition); | |||
| 181 | /* Backwards-spelling-compatibility */ | 181 | /* Backwards-spelling-compatibility */ |
| 182 | __setup("MTD_Partion=", MTD_New_Partition); | 182 | __setup("MTD_Partion=", MTD_New_Partition); |
| 183 | 183 | ||
| 184 | int __init init_fortunet(void) | 184 | static int __init init_fortunet(void) |
| 185 | { | 185 | { |
| 186 | int ix,iy; | 186 | int ix,iy; |
| 187 | for(iy=ix=0;ix<MAX_NUM_REGIONS;ix++) | 187 | for(iy=ix=0;ix<MAX_NUM_REGIONS;ix++) |
diff --git a/drivers/mtd/maps/h720x-flash.c b/drivers/mtd/maps/h720x-flash.c index 3b959fad1c4e..72c724fa8c27 100644 --- a/drivers/mtd/maps/h720x-flash.c +++ b/drivers/mtd/maps/h720x-flash.c | |||
| @@ -65,7 +65,7 @@ static const char *probes[] = { "cmdlinepart", NULL }; | |||
| 65 | /* | 65 | /* |
| 66 | * Initialize FLASH support | 66 | * Initialize FLASH support |
| 67 | */ | 67 | */ |
| 68 | int __init h720x_mtd_init(void) | 68 | static int __init h720x_mtd_init(void) |
| 69 | { | 69 | { |
| 70 | 70 | ||
| 71 | char *part_type = NULL; | 71 | char *part_type = NULL; |
diff --git a/drivers/mtd/maps/ichxrom.c b/drivers/mtd/maps/ichxrom.c index aeb6c916e23f..c32bc28920b3 100644 --- a/drivers/mtd/maps/ichxrom.c +++ b/drivers/mtd/maps/ichxrom.c | |||
| @@ -258,8 +258,8 @@ static int __devinit ichxrom_init_one (struct pci_dev *pdev, | |||
| 258 | /* Trim the size if we are larger than the map */ | 258 | /* Trim the size if we are larger than the map */ |
| 259 | if (map->mtd->size > map->map.size) { | 259 | if (map->mtd->size > map->map.size) { |
| 260 | printk(KERN_WARNING MOD_NAME | 260 | printk(KERN_WARNING MOD_NAME |
| 261 | " rom(%u) larger than window(%lu). fixing...\n", | 261 | " rom(%llu) larger than window(%lu). fixing...\n", |
| 262 | map->mtd->size, map->map.size); | 262 | (unsigned long long)map->mtd->size, map->map.size); |
| 263 | map->mtd->size = map->map.size; | 263 | map->mtd->size = map->map.size; |
| 264 | } | 264 | } |
| 265 | if (window->rsrc.parent) { | 265 | if (window->rsrc.parent) { |
diff --git a/drivers/mtd/maps/impa7.c b/drivers/mtd/maps/impa7.c index 2682ab51a367..998a27da97f3 100644 --- a/drivers/mtd/maps/impa7.c +++ b/drivers/mtd/maps/impa7.c | |||
| @@ -70,7 +70,7 @@ static struct mtd_partition *mtd_parts[NUM_FLASHBANKS]; | |||
| 70 | 70 | ||
| 71 | static const char *probes[] = { "cmdlinepart", NULL }; | 71 | static const char *probes[] = { "cmdlinepart", NULL }; |
| 72 | 72 | ||
| 73 | int __init init_impa7(void) | 73 | static int __init init_impa7(void) |
| 74 | { | 74 | { |
| 75 | static const char *rom_probe_types[] = PROBETYPES; | 75 | static const char *rom_probe_types[] = PROBETYPES; |
| 76 | const char **type; | 76 | const char **type; |
diff --git a/drivers/mtd/maps/ipaq-flash.c b/drivers/mtd/maps/ipaq-flash.c index ed58f6a77bd9..748c85f635f1 100644 --- a/drivers/mtd/maps/ipaq-flash.c +++ b/drivers/mtd/maps/ipaq-flash.c | |||
| @@ -202,7 +202,7 @@ static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL }; | |||
| 202 | 202 | ||
| 203 | static int __init h1900_special_case(void); | 203 | static int __init h1900_special_case(void); |
| 204 | 204 | ||
| 205 | int __init ipaq_mtd_init(void) | 205 | static int __init ipaq_mtd_init(void) |
| 206 | { | 206 | { |
| 207 | struct mtd_partition *parts = NULL; | 207 | struct mtd_partition *parts = NULL; |
| 208 | int nb_parts = 0; | 208 | int nb_parts = 0; |
diff --git a/drivers/mtd/maps/mbx860.c b/drivers/mtd/maps/mbx860.c index 706f67394b07..0eb5a7c85380 100644 --- a/drivers/mtd/maps/mbx860.c +++ b/drivers/mtd/maps/mbx860.c | |||
| @@ -55,7 +55,7 @@ struct map_info mbx_map = { | |||
| 55 | .bankwidth = 4, | 55 | .bankwidth = 4, |
| 56 | }; | 56 | }; |
| 57 | 57 | ||
| 58 | int __init init_mbx(void) | 58 | static int __init init_mbx(void) |
| 59 | { | 59 | { |
| 60 | printk(KERN_NOTICE "Motorola MBX flash device: 0x%x at 0x%x\n", WINDOW_SIZE*4, WINDOW_ADDR); | 60 | printk(KERN_NOTICE "Motorola MBX flash device: 0x%x at 0x%x\n", WINDOW_SIZE*4, WINDOW_ADDR); |
| 61 | mbx_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE * 4); | 61 | mbx_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE * 4); |
diff --git a/drivers/mtd/maps/nettel.c b/drivers/mtd/maps/nettel.c index 965e6c6d6ab0..a97133eb9d70 100644 --- a/drivers/mtd/maps/nettel.c +++ b/drivers/mtd/maps/nettel.c | |||
| @@ -226,7 +226,7 @@ static int __init nettel_init(void) | |||
| 226 | 226 | ||
| 227 | if ((amd_mtd = do_map_probe("jedec_probe", &nettel_amd_map))) { | 227 | if ((amd_mtd = do_map_probe("jedec_probe", &nettel_amd_map))) { |
| 228 | printk(KERN_NOTICE "SNAPGEAR: AMD flash device size = %dK\n", | 228 | printk(KERN_NOTICE "SNAPGEAR: AMD flash device size = %dK\n", |
| 229 | amd_mtd->size>>10); | 229 | (int)(amd_mtd->size>>10)); |
| 230 | 230 | ||
| 231 | amd_mtd->owner = THIS_MODULE; | 231 | amd_mtd->owner = THIS_MODULE; |
| 232 | 232 | ||
| @@ -357,13 +357,12 @@ static int __init nettel_init(void) | |||
| 357 | *intel1par = 0; | 357 | *intel1par = 0; |
| 358 | } | 358 | } |
| 359 | 359 | ||
| 360 | printk(KERN_NOTICE "SNAPGEAR: Intel flash device size = %dK\n", | 360 | printk(KERN_NOTICE "SNAPGEAR: Intel flash device size = %lldKiB\n", |
| 361 | (intel_mtd->size >> 10)); | 361 | (unsigned long long)(intel_mtd->size >> 10)); |
| 362 | 362 | ||
| 363 | intel_mtd->owner = THIS_MODULE; | 363 | intel_mtd->owner = THIS_MODULE; |
| 364 | 364 | ||
| 365 | num_intel_partitions = sizeof(nettel_intel_partitions) / | 365 | num_intel_partitions = ARRAY_SIZE(nettel_intel_partitions); |
| 366 | sizeof(nettel_intel_partitions[0]); | ||
| 367 | 366 | ||
| 368 | if (intelboot) { | 367 | if (intelboot) { |
| 369 | /* | 368 | /* |
diff --git a/drivers/mtd/maps/octagon-5066.c b/drivers/mtd/maps/octagon-5066.c index 43e04c1d22a9..2b2e45093218 100644 --- a/drivers/mtd/maps/octagon-5066.c +++ b/drivers/mtd/maps/octagon-5066.c | |||
| @@ -184,7 +184,7 @@ void cleanup_oct5066(void) | |||
| 184 | release_region(PAGE_IO, 1); | 184 | release_region(PAGE_IO, 1); |
| 185 | } | 185 | } |
| 186 | 186 | ||
| 187 | int __init init_oct5066(void) | 187 | static int __init init_oct5066(void) |
| 188 | { | 188 | { |
| 189 | int i; | 189 | int i; |
| 190 | int ret = 0; | 190 | int ret = 0; |
diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index 1db16e549e38..87743661d48e 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c | |||
| @@ -29,7 +29,6 @@ struct physmap_flash_info { | |||
| 29 | struct map_info map[MAX_RESOURCES]; | 29 | struct map_info map[MAX_RESOURCES]; |
| 30 | #ifdef CONFIG_MTD_PARTITIONS | 30 | #ifdef CONFIG_MTD_PARTITIONS |
| 31 | int nr_parts; | 31 | int nr_parts; |
| 32 | struct mtd_partition *parts; | ||
| 33 | #endif | 32 | #endif |
| 34 | }; | 33 | }; |
| 35 | 34 | ||
| @@ -56,14 +55,10 @@ static int physmap_flash_remove(struct platform_device *dev) | |||
| 56 | for (i = 0; i < MAX_RESOURCES; i++) { | 55 | for (i = 0; i < MAX_RESOURCES; i++) { |
| 57 | if (info->mtd[i] != NULL) { | 56 | if (info->mtd[i] != NULL) { |
| 58 | #ifdef CONFIG_MTD_PARTITIONS | 57 | #ifdef CONFIG_MTD_PARTITIONS |
| 59 | if (info->nr_parts) { | 58 | if (info->nr_parts || physmap_data->nr_parts) |
| 60 | del_mtd_partitions(info->mtd[i]); | 59 | del_mtd_partitions(info->mtd[i]); |
| 61 | kfree(info->parts); | 60 | else |
| 62 | } else if (physmap_data->nr_parts) { | ||
| 63 | del_mtd_partitions(info->mtd[i]); | ||
| 64 | } else { | ||
| 65 | del_mtd_device(info->mtd[i]); | 61 | del_mtd_device(info->mtd[i]); |
| 66 | } | ||
| 67 | #else | 62 | #else |
| 68 | del_mtd_device(info->mtd[i]); | 63 | del_mtd_device(info->mtd[i]); |
| 69 | #endif | 64 | #endif |
| @@ -73,7 +68,12 @@ static int physmap_flash_remove(struct platform_device *dev) | |||
| 73 | return 0; | 68 | return 0; |
| 74 | } | 69 | } |
| 75 | 70 | ||
| 76 | static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", NULL }; | 71 | static const char *rom_probe_types[] = { |
| 72 | "cfi_probe", | ||
| 73 | "jedec_probe", | ||
| 74 | "qinfo_probe", | ||
| 75 | "map_rom", | ||
| 76 | NULL }; | ||
| 77 | #ifdef CONFIG_MTD_PARTITIONS | 77 | #ifdef CONFIG_MTD_PARTITIONS |
| 78 | static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL }; | 78 | static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL }; |
| 79 | #endif | 79 | #endif |
| @@ -86,6 +86,9 @@ static int physmap_flash_probe(struct platform_device *dev) | |||
| 86 | int err = 0; | 86 | int err = 0; |
| 87 | int i; | 87 | int i; |
| 88 | int devices_found = 0; | 88 | int devices_found = 0; |
| 89 | #ifdef CONFIG_MTD_PARTITIONS | ||
| 90 | struct mtd_partition *parts; | ||
| 91 | #endif | ||
| 89 | 92 | ||
| 90 | physmap_data = dev->dev.platform_data; | 93 | physmap_data = dev->dev.platform_data; |
| 91 | if (physmap_data == NULL) | 94 | if (physmap_data == NULL) |
| @@ -119,6 +122,7 @@ static int physmap_flash_probe(struct platform_device *dev) | |||
| 119 | info->map[i].size = dev->resource[i].end - dev->resource[i].start + 1; | 122 | info->map[i].size = dev->resource[i].end - dev->resource[i].start + 1; |
| 120 | info->map[i].bankwidth = physmap_data->width; | 123 | info->map[i].bankwidth = physmap_data->width; |
| 121 | info->map[i].set_vpp = physmap_data->set_vpp; | 124 | info->map[i].set_vpp = physmap_data->set_vpp; |
| 125 | info->map[i].pfow_base = physmap_data->pfow_base; | ||
| 122 | 126 | ||
| 123 | info->map[i].virt = devm_ioremap(&dev->dev, info->map[i].phys, | 127 | info->map[i].virt = devm_ioremap(&dev->dev, info->map[i].phys, |
| 124 | info->map[i].size); | 128 | info->map[i].size); |
| @@ -163,9 +167,10 @@ static int physmap_flash_probe(struct platform_device *dev) | |||
| 163 | goto err_out; | 167 | goto err_out; |
| 164 | 168 | ||
| 165 | #ifdef CONFIG_MTD_PARTITIONS | 169 | #ifdef CONFIG_MTD_PARTITIONS |
| 166 | err = parse_mtd_partitions(info->cmtd, part_probe_types, &info->parts, 0); | 170 | err = parse_mtd_partitions(info->cmtd, part_probe_types, &parts, 0); |
| 167 | if (err > 0) { | 171 | if (err > 0) { |
| 168 | add_mtd_partitions(info->cmtd, info->parts, err); | 172 | add_mtd_partitions(info->cmtd, parts, err); |
| 173 | kfree(parts); | ||
| 169 | return 0; | 174 | return 0; |
| 170 | } | 175 | } |
| 171 | 176 | ||
| @@ -251,14 +256,7 @@ static struct platform_driver physmap_flash_driver = { | |||
| 251 | }; | 256 | }; |
| 252 | 257 | ||
| 253 | 258 | ||
| 254 | #ifdef CONFIG_MTD_PHYSMAP_LEN | 259 | #ifdef CONFIG_MTD_PHYSMAP_COMPAT |
| 255 | #if CONFIG_MTD_PHYSMAP_LEN != 0 | ||
| 256 | #warning using PHYSMAP compat code | ||
| 257 | #define PHYSMAP_COMPAT | ||
| 258 | #endif | ||
| 259 | #endif | ||
| 260 | |||
| 261 | #ifdef PHYSMAP_COMPAT | ||
| 262 | static struct physmap_flash_data physmap_flash_data = { | 260 | static struct physmap_flash_data physmap_flash_data = { |
| 263 | .width = CONFIG_MTD_PHYSMAP_BANKWIDTH, | 261 | .width = CONFIG_MTD_PHYSMAP_BANKWIDTH, |
| 264 | }; | 262 | }; |
| @@ -302,7 +300,7 @@ static int __init physmap_init(void) | |||
| 302 | int err; | 300 | int err; |
| 303 | 301 | ||
| 304 | err = platform_driver_register(&physmap_flash_driver); | 302 | err = platform_driver_register(&physmap_flash_driver); |
| 305 | #ifdef PHYSMAP_COMPAT | 303 | #ifdef CONFIG_MTD_PHYSMAP_COMPAT |
| 306 | if (err == 0) | 304 | if (err == 0) |
| 307 | platform_device_register(&physmap_flash); | 305 | platform_device_register(&physmap_flash); |
| 308 | #endif | 306 | #endif |
| @@ -312,7 +310,7 @@ static int __init physmap_init(void) | |||
| 312 | 310 | ||
| 313 | static void __exit physmap_exit(void) | 311 | static void __exit physmap_exit(void) |
| 314 | { | 312 | { |
| 315 | #ifdef PHYSMAP_COMPAT | 313 | #ifdef CONFIG_MTD_PHYSMAP_COMPAT |
| 316 | platform_device_unregister(&physmap_flash); | 314 | platform_device_unregister(&physmap_flash); |
| 317 | #endif | 315 | #endif |
| 318 | platform_driver_unregister(&physmap_flash_driver); | 316 | platform_driver_unregister(&physmap_flash_driver); |
| @@ -326,8 +324,7 @@ MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); | |||
| 326 | MODULE_DESCRIPTION("Generic configurable MTD map driver"); | 324 | MODULE_DESCRIPTION("Generic configurable MTD map driver"); |
| 327 | 325 | ||
| 328 | /* legacy platform drivers can't hotplug or coldplg */ | 326 | /* legacy platform drivers can't hotplug or coldplg */ |
| 329 | #ifndef PHYSMAP_COMPAT | 327 | #ifndef CONFIG_MTD_PHYSMAP_COMPAT |
| 330 | /* work with hotplug and coldplug */ | 328 | /* work with hotplug and coldplug */ |
| 331 | MODULE_ALIAS("platform:physmap-flash"); | 329 | MODULE_ALIAS("platform:physmap-flash"); |
| 332 | #endif | 330 | #endif |
| 333 | |||
diff --git a/drivers/mtd/maps/pmcmsp-flash.c b/drivers/mtd/maps/pmcmsp-flash.c index f43ba2815cbb..4768bd5459d6 100644 --- a/drivers/mtd/maps/pmcmsp-flash.c +++ b/drivers/mtd/maps/pmcmsp-flash.c | |||
| @@ -48,7 +48,7 @@ static int fcnt; | |||
| 48 | 48 | ||
| 49 | #define DEBUG_MARKER printk(KERN_NOTICE "%s[%d]\n", __func__, __LINE__) | 49 | #define DEBUG_MARKER printk(KERN_NOTICE "%s[%d]\n", __func__, __LINE__) |
| 50 | 50 | ||
| 51 | int __init init_msp_flash(void) | 51 | static int __init init_msp_flash(void) |
| 52 | { | 52 | { |
| 53 | int i, j; | 53 | int i, j; |
| 54 | int offset, coff; | 54 | int offset, coff; |
diff --git a/drivers/mtd/maps/redwood.c b/drivers/mtd/maps/redwood.c index de002eb1a7fe..933c0b63b016 100644 --- a/drivers/mtd/maps/redwood.c +++ b/drivers/mtd/maps/redwood.c | |||
| @@ -122,7 +122,7 @@ struct map_info redwood_flash_map = { | |||
| 122 | 122 | ||
| 123 | static struct mtd_info *redwood_mtd; | 123 | static struct mtd_info *redwood_mtd; |
| 124 | 124 | ||
| 125 | int __init init_redwood_flash(void) | 125 | static int __init init_redwood_flash(void) |
| 126 | { | 126 | { |
| 127 | int err; | 127 | int err; |
| 128 | 128 | ||
diff --git a/drivers/mtd/maps/rpxlite.c b/drivers/mtd/maps/rpxlite.c index 14d90edb4430..3e3ef53d4fd4 100644 --- a/drivers/mtd/maps/rpxlite.c +++ b/drivers/mtd/maps/rpxlite.c | |||
| @@ -23,7 +23,7 @@ static struct map_info rpxlite_map = { | |||
| 23 | .phys = WINDOW_ADDR, | 23 | .phys = WINDOW_ADDR, |
| 24 | }; | 24 | }; |
| 25 | 25 | ||
| 26 | int __init init_rpxlite(void) | 26 | static int __init init_rpxlite(void) |
| 27 | { | 27 | { |
| 28 | printk(KERN_NOTICE "RPX Lite or CLLF flash device: %x at %x\n", WINDOW_SIZE*4, WINDOW_ADDR); | 28 | printk(KERN_NOTICE "RPX Lite or CLLF flash device: %x at %x\n", WINDOW_SIZE*4, WINDOW_ADDR); |
| 29 | rpxlite_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE * 4); | 29 | rpxlite_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE * 4); |
diff --git a/drivers/mtd/maps/sbc8240.c b/drivers/mtd/maps/sbc8240.c index 6e1e99cd2b59..d5374cdcb163 100644 --- a/drivers/mtd/maps/sbc8240.c +++ b/drivers/mtd/maps/sbc8240.c | |||
| @@ -136,7 +136,7 @@ static struct mtd_part_def sbc8240_part_banks[NUM_FLASH_BANKS]; | |||
| 136 | #endif /* CONFIG_MTD_PARTITIONS */ | 136 | #endif /* CONFIG_MTD_PARTITIONS */ |
| 137 | 137 | ||
| 138 | 138 | ||
| 139 | int __init init_sbc8240_mtd (void) | 139 | static int __init init_sbc8240_mtd (void) |
| 140 | { | 140 | { |
| 141 | static struct _cjs { | 141 | static struct _cjs { |
| 142 | u_long addr; | 142 | u_long addr; |
diff --git a/drivers/mtd/maps/scb2_flash.c b/drivers/mtd/maps/scb2_flash.c index 21169e6d646c..7e329f09a548 100644 --- a/drivers/mtd/maps/scb2_flash.c +++ b/drivers/mtd/maps/scb2_flash.c | |||
| @@ -118,7 +118,8 @@ scb2_fixup_mtd(struct mtd_info *mtd) | |||
| 118 | struct mtd_erase_region_info *region = &mtd->eraseregions[i]; | 118 | struct mtd_erase_region_info *region = &mtd->eraseregions[i]; |
| 119 | 119 | ||
| 120 | if (region->numblocks * region->erasesize > mtd->size) { | 120 | if (region->numblocks * region->erasesize > mtd->size) { |
| 121 | region->numblocks = (mtd->size / region->erasesize); | 121 | region->numblocks = ((unsigned long)mtd->size / |
| 122 | region->erasesize); | ||
| 122 | done = 1; | 123 | done = 1; |
| 123 | } else { | 124 | } else { |
| 124 | region->numblocks = 0; | 125 | region->numblocks = 0; |
| @@ -187,8 +188,9 @@ scb2_flash_probe(struct pci_dev *dev, const struct pci_device_id *ent) | |||
| 187 | return -ENODEV; | 188 | return -ENODEV; |
| 188 | } | 189 | } |
| 189 | 190 | ||
| 190 | printk(KERN_NOTICE MODNAME ": chip size 0x%x at offset 0x%x\n", | 191 | printk(KERN_NOTICE MODNAME ": chip size 0x%llx at offset 0x%llx\n", |
| 191 | scb2_mtd->size, SCB2_WINDOW - scb2_mtd->size); | 192 | (unsigned long long)scb2_mtd->size, |
| 193 | (unsigned long long)(SCB2_WINDOW - scb2_mtd->size)); | ||
| 192 | 194 | ||
| 193 | add_mtd_device(scb2_mtd); | 195 | add_mtd_device(scb2_mtd); |
| 194 | 196 | ||
diff --git a/drivers/mtd/maps/sharpsl-flash.c b/drivers/mtd/maps/sharpsl-flash.c index 026eab028189..b392f096c706 100644 --- a/drivers/mtd/maps/sharpsl-flash.c +++ b/drivers/mtd/maps/sharpsl-flash.c | |||
| @@ -47,7 +47,7 @@ static struct mtd_partition sharpsl_partitions[1] = { | |||
| 47 | } | 47 | } |
| 48 | }; | 48 | }; |
| 49 | 49 | ||
| 50 | int __init init_sharpsl(void) | 50 | static int __init init_sharpsl(void) |
| 51 | { | 51 | { |
| 52 | struct mtd_partition *parts; | 52 | struct mtd_partition *parts; |
| 53 | int nb_parts = 0; | 53 | int nb_parts = 0; |
diff --git a/drivers/mtd/maps/tqm8xxl.c b/drivers/mtd/maps/tqm8xxl.c index a5d3d8531faa..60146984f4be 100644 --- a/drivers/mtd/maps/tqm8xxl.c +++ b/drivers/mtd/maps/tqm8xxl.c | |||
| @@ -109,7 +109,7 @@ static struct mtd_partition tqm8xxl_fs_partitions[] = { | |||
| 109 | }; | 109 | }; |
| 110 | #endif | 110 | #endif |
| 111 | 111 | ||
| 112 | int __init init_tqm_mtd(void) | 112 | static int __init init_tqm_mtd(void) |
| 113 | { | 113 | { |
| 114 | int idx = 0, ret = 0; | 114 | int idx = 0, ret = 0; |
| 115 | unsigned long flash_addr, flash_size, mtd_size = 0; | 115 | unsigned long flash_addr, flash_size, mtd_size = 0; |
diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c index 0dc645f8152f..81756e397711 100644 --- a/drivers/mtd/maps/uclinux.c +++ b/drivers/mtd/maps/uclinux.c | |||
| @@ -51,7 +51,7 @@ int uclinux_point(struct mtd_info *mtd, loff_t from, size_t len, | |||
| 51 | 51 | ||
| 52 | /****************************************************************************/ | 52 | /****************************************************************************/ |
| 53 | 53 | ||
| 54 | int __init uclinux_mtd_init(void) | 54 | static int __init uclinux_mtd_init(void) |
| 55 | { | 55 | { |
| 56 | struct mtd_info *mtd; | 56 | struct mtd_info *mtd; |
| 57 | struct map_info *mapp; | 57 | struct map_info *mapp; |
| @@ -94,7 +94,7 @@ int __init uclinux_mtd_init(void) | |||
| 94 | 94 | ||
| 95 | /****************************************************************************/ | 95 | /****************************************************************************/ |
| 96 | 96 | ||
| 97 | void __exit uclinux_mtd_cleanup(void) | 97 | static void __exit uclinux_mtd_cleanup(void) |
| 98 | { | 98 | { |
| 99 | if (uclinux_ram_mtdinfo) { | 99 | if (uclinux_ram_mtdinfo) { |
| 100 | del_mtd_partitions(uclinux_ram_mtdinfo); | 100 | del_mtd_partitions(uclinux_ram_mtdinfo); |
diff --git a/drivers/mtd/maps/vmax301.c b/drivers/mtd/maps/vmax301.c index 5a0c9a353b0f..6d452dcdfe34 100644 --- a/drivers/mtd/maps/vmax301.c +++ b/drivers/mtd/maps/vmax301.c | |||
| @@ -146,7 +146,7 @@ static void __exit cleanup_vmax301(void) | |||
| 146 | iounmap((void *)vmax_map[0].map_priv_1 - WINDOW_START); | 146 | iounmap((void *)vmax_map[0].map_priv_1 - WINDOW_START); |
| 147 | } | 147 | } |
| 148 | 148 | ||
| 149 | int __init init_vmax301(void) | 149 | static int __init init_vmax301(void) |
| 150 | { | 150 | { |
| 151 | int i; | 151 | int i; |
| 152 | unsigned long iomapadr; | 152 | unsigned long iomapadr; |
diff --git a/drivers/mtd/maps/wr_sbc82xx_flash.c b/drivers/mtd/maps/wr_sbc82xx_flash.c index 413b0cf9bbd2..933a2b6598b4 100644 --- a/drivers/mtd/maps/wr_sbc82xx_flash.c +++ b/drivers/mtd/maps/wr_sbc82xx_flash.c | |||
| @@ -74,7 +74,7 @@ do { \ | |||
| 74 | } \ | 74 | } \ |
| 75 | } while (0); | 75 | } while (0); |
| 76 | 76 | ||
| 77 | int __init init_sbc82xx_flash(void) | 77 | static int __init init_sbc82xx_flash(void) |
| 78 | { | 78 | { |
| 79 | volatile memctl_cpm2_t *mc = &cpm2_immr->im_memctl; | 79 | volatile memctl_cpm2_t *mc = &cpm2_immr->im_memctl; |
| 80 | int bigflash; | 80 | int bigflash; |
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index bcffeda2df3d..e9ec59e9a566 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c | |||
| @@ -450,16 +450,20 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
| 450 | if (!erase) | 450 | if (!erase) |
| 451 | ret = -ENOMEM; | 451 | ret = -ENOMEM; |
| 452 | else { | 452 | else { |
| 453 | struct erase_info_user einfo; | ||
| 454 | |||
| 453 | wait_queue_head_t waitq; | 455 | wait_queue_head_t waitq; |
| 454 | DECLARE_WAITQUEUE(wait, current); | 456 | DECLARE_WAITQUEUE(wait, current); |
| 455 | 457 | ||
| 456 | init_waitqueue_head(&waitq); | 458 | init_waitqueue_head(&waitq); |
| 457 | 459 | ||
| 458 | if (copy_from_user(&erase->addr, argp, | 460 | if (copy_from_user(&einfo, argp, |
| 459 | sizeof(struct erase_info_user))) { | 461 | sizeof(struct erase_info_user))) { |
| 460 | kfree(erase); | 462 | kfree(erase); |
| 461 | return -EFAULT; | 463 | return -EFAULT; |
| 462 | } | 464 | } |
| 465 | erase->addr = einfo.start; | ||
| 466 | erase->len = einfo.length; | ||
| 463 | erase->mtd = mtd; | 467 | erase->mtd = mtd; |
| 464 | erase->callback = mtdchar_erase_callback; | 468 | erase->callback = mtdchar_erase_callback; |
| 465 | erase->priv = (unsigned long)&waitq; | 469 | erase->priv = (unsigned long)&waitq; |
diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 1a05cf37851e..3dbb1b38db66 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c | |||
| @@ -197,7 +197,7 @@ concat_writev(struct mtd_info *mtd, const struct kvec *vecs, | |||
| 197 | continue; | 197 | continue; |
| 198 | } | 198 | } |
| 199 | 199 | ||
| 200 | size = min(total_len, (size_t)(subdev->size - to)); | 200 | size = min_t(uint64_t, total_len, subdev->size - to); |
| 201 | wsize = size; /* store for future use */ | 201 | wsize = size; /* store for future use */ |
| 202 | 202 | ||
| 203 | entry_high = entry_low; | 203 | entry_high = entry_low; |
| @@ -385,7 +385,7 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
| 385 | struct mtd_concat *concat = CONCAT(mtd); | 385 | struct mtd_concat *concat = CONCAT(mtd); |
| 386 | struct mtd_info *subdev; | 386 | struct mtd_info *subdev; |
| 387 | int i, err; | 387 | int i, err; |
| 388 | u_int32_t length, offset = 0; | 388 | uint64_t length, offset = 0; |
| 389 | struct erase_info *erase; | 389 | struct erase_info *erase; |
| 390 | 390 | ||
| 391 | if (!(mtd->flags & MTD_WRITEABLE)) | 391 | if (!(mtd->flags & MTD_WRITEABLE)) |
| @@ -518,7 +518,7 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
| 518 | return 0; | 518 | return 0; |
| 519 | } | 519 | } |
| 520 | 520 | ||
| 521 | static int concat_lock(struct mtd_info *mtd, loff_t ofs, size_t len) | 521 | static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) |
| 522 | { | 522 | { |
| 523 | struct mtd_concat *concat = CONCAT(mtd); | 523 | struct mtd_concat *concat = CONCAT(mtd); |
| 524 | int i, err = -EINVAL; | 524 | int i, err = -EINVAL; |
| @@ -528,7 +528,7 @@ static int concat_lock(struct mtd_info *mtd, loff_t ofs, size_t len) | |||
| 528 | 528 | ||
| 529 | for (i = 0; i < concat->num_subdev; i++) { | 529 | for (i = 0; i < concat->num_subdev; i++) { |
| 530 | struct mtd_info *subdev = concat->subdev[i]; | 530 | struct mtd_info *subdev = concat->subdev[i]; |
| 531 | size_t size; | 531 | uint64_t size; |
| 532 | 532 | ||
| 533 | if (ofs >= subdev->size) { | 533 | if (ofs >= subdev->size) { |
| 534 | size = 0; | 534 | size = 0; |
| @@ -556,7 +556,7 @@ static int concat_lock(struct mtd_info *mtd, loff_t ofs, size_t len) | |||
| 556 | return err; | 556 | return err; |
| 557 | } | 557 | } |
| 558 | 558 | ||
| 559 | static int concat_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) | 559 | static int concat_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) |
| 560 | { | 560 | { |
| 561 | struct mtd_concat *concat = CONCAT(mtd); | 561 | struct mtd_concat *concat = CONCAT(mtd); |
| 562 | int i, err = 0; | 562 | int i, err = 0; |
| @@ -566,7 +566,7 @@ static int concat_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) | |||
| 566 | 566 | ||
| 567 | for (i = 0; i < concat->num_subdev; i++) { | 567 | for (i = 0; i < concat->num_subdev; i++) { |
| 568 | struct mtd_info *subdev = concat->subdev[i]; | 568 | struct mtd_info *subdev = concat->subdev[i]; |
| 569 | size_t size; | 569 | uint64_t size; |
| 570 | 570 | ||
| 571 | if (ofs >= subdev->size) { | 571 | if (ofs >= subdev->size) { |
| 572 | size = 0; | 572 | size = 0; |
| @@ -696,7 +696,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c | |||
| 696 | int i; | 696 | int i; |
| 697 | size_t size; | 697 | size_t size; |
| 698 | struct mtd_concat *concat; | 698 | struct mtd_concat *concat; |
| 699 | u_int32_t max_erasesize, curr_erasesize; | 699 | uint32_t max_erasesize, curr_erasesize; |
| 700 | int num_erase_region; | 700 | int num_erase_region; |
| 701 | 701 | ||
| 702 | printk(KERN_NOTICE "Concatenating MTD devices:\n"); | 702 | printk(KERN_NOTICE "Concatenating MTD devices:\n"); |
| @@ -842,12 +842,14 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c | |||
| 842 | concat->mtd.erasesize = curr_erasesize; | 842 | concat->mtd.erasesize = curr_erasesize; |
| 843 | concat->mtd.numeraseregions = 0; | 843 | concat->mtd.numeraseregions = 0; |
| 844 | } else { | 844 | } else { |
| 845 | uint64_t tmp64; | ||
| 846 | |||
| 845 | /* | 847 | /* |
| 846 | * erase block size varies across the subdevices: allocate | 848 | * erase block size varies across the subdevices: allocate |
| 847 | * space to store the data describing the variable erase regions | 849 | * space to store the data describing the variable erase regions |
| 848 | */ | 850 | */ |
| 849 | struct mtd_erase_region_info *erase_region_p; | 851 | struct mtd_erase_region_info *erase_region_p; |
| 850 | u_int32_t begin, position; | 852 | uint64_t begin, position; |
| 851 | 853 | ||
| 852 | concat->mtd.erasesize = max_erasesize; | 854 | concat->mtd.erasesize = max_erasesize; |
| 853 | concat->mtd.numeraseregions = num_erase_region; | 855 | concat->mtd.numeraseregions = num_erase_region; |
| @@ -879,8 +881,9 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c | |||
| 879 | erase_region_p->offset = begin; | 881 | erase_region_p->offset = begin; |
| 880 | erase_region_p->erasesize = | 882 | erase_region_p->erasesize = |
| 881 | curr_erasesize; | 883 | curr_erasesize; |
| 882 | erase_region_p->numblocks = | 884 | tmp64 = position - begin; |
| 883 | (position - begin) / curr_erasesize; | 885 | do_div(tmp64, curr_erasesize); |
| 886 | erase_region_p->numblocks = tmp64; | ||
| 884 | begin = position; | 887 | begin = position; |
| 885 | 888 | ||
| 886 | curr_erasesize = subdev[i]->erasesize; | 889 | curr_erasesize = subdev[i]->erasesize; |
| @@ -897,9 +900,9 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c | |||
| 897 | erase_region_p->offset = begin; | 900 | erase_region_p->offset = begin; |
| 898 | erase_region_p->erasesize = | 901 | erase_region_p->erasesize = |
| 899 | curr_erasesize; | 902 | curr_erasesize; |
| 900 | erase_region_p->numblocks = | 903 | tmp64 = position - begin; |
| 901 | (position - | 904 | do_div(tmp64, curr_erasesize); |
| 902 | begin) / curr_erasesize; | 905 | erase_region_p->numblocks = tmp64; |
| 903 | begin = position; | 906 | begin = position; |
| 904 | 907 | ||
| 905 | curr_erasesize = | 908 | curr_erasesize = |
| @@ -909,14 +912,16 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c | |||
| 909 | } | 912 | } |
| 910 | position += | 913 | position += |
| 911 | subdev[i]->eraseregions[j]. | 914 | subdev[i]->eraseregions[j]. |
| 912 | numblocks * curr_erasesize; | 915 | numblocks * (uint64_t)curr_erasesize; |
| 913 | } | 916 | } |
| 914 | } | 917 | } |
| 915 | } | 918 | } |
| 916 | /* Now write the final entry */ | 919 | /* Now write the final entry */ |
| 917 | erase_region_p->offset = begin; | 920 | erase_region_p->offset = begin; |
| 918 | erase_region_p->erasesize = curr_erasesize; | 921 | erase_region_p->erasesize = curr_erasesize; |
| 919 | erase_region_p->numblocks = (position - begin) / curr_erasesize; | 922 | tmp64 = position - begin; |
| 923 | do_div(tmp64, curr_erasesize); | ||
| 924 | erase_region_p->numblocks = tmp64; | ||
| 920 | } | 925 | } |
| 921 | 926 | ||
| 922 | return &concat->mtd; | 927 | return &concat->mtd; |
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index a9d246949820..76fe0a1e7a5e 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c | |||
| @@ -57,6 +57,19 @@ int add_mtd_device(struct mtd_info *mtd) | |||
| 57 | mtd->index = i; | 57 | mtd->index = i; |
| 58 | mtd->usecount = 0; | 58 | mtd->usecount = 0; |
| 59 | 59 | ||
| 60 | if (is_power_of_2(mtd->erasesize)) | ||
| 61 | mtd->erasesize_shift = ffs(mtd->erasesize) - 1; | ||
| 62 | else | ||
| 63 | mtd->erasesize_shift = 0; | ||
| 64 | |||
| 65 | if (is_power_of_2(mtd->writesize)) | ||
| 66 | mtd->writesize_shift = ffs(mtd->writesize) - 1; | ||
| 67 | else | ||
| 68 | mtd->writesize_shift = 0; | ||
| 69 | |||
| 70 | mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1; | ||
| 71 | mtd->writesize_mask = (1 << mtd->writesize_shift) - 1; | ||
| 72 | |||
| 60 | /* Some chips always power up locked. Unlock them now */ | 73 | /* Some chips always power up locked. Unlock them now */ |
| 61 | if ((mtd->flags & MTD_WRITEABLE) | 74 | if ((mtd->flags & MTD_WRITEABLE) |
| 62 | && (mtd->flags & MTD_POWERUP_LOCK) && mtd->unlock) { | 75 | && (mtd->flags & MTD_POWERUP_LOCK) && mtd->unlock) { |
| @@ -344,7 +357,8 @@ static inline int mtd_proc_info (char *buf, int i) | |||
| 344 | if (!this) | 357 | if (!this) |
| 345 | return 0; | 358 | return 0; |
| 346 | 359 | ||
| 347 | return sprintf(buf, "mtd%d: %8.8x %8.8x \"%s\"\n", i, this->size, | 360 | return sprintf(buf, "mtd%d: %8.8llx %8.8x \"%s\"\n", i, |
| 361 | (unsigned long long)this->size, | ||
| 348 | this->erasesize, this->name); | 362 | this->erasesize, this->name); |
| 349 | } | 363 | } |
| 350 | 364 | ||
diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index aebb3b27edbd..1a6b3beabe8d 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c | |||
| @@ -80,9 +80,9 @@ static int mtdoops_erase_block(struct mtd_info *mtd, int offset) | |||
| 80 | if (ret) { | 80 | if (ret) { |
| 81 | set_current_state(TASK_RUNNING); | 81 | set_current_state(TASK_RUNNING); |
| 82 | remove_wait_queue(&wait_q, &wait); | 82 | remove_wait_queue(&wait_q, &wait); |
| 83 | printk (KERN_WARNING "mtdoops: erase of region [0x%x, 0x%x] " | 83 | printk (KERN_WARNING "mtdoops: erase of region [0x%llx, 0x%llx] " |
| 84 | "on \"%s\" failed\n", | 84 | "on \"%s\" failed\n", |
| 85 | erase.addr, erase.len, mtd->name); | 85 | (unsigned long long)erase.addr, (unsigned long long)erase.len, mtd->name); |
| 86 | return ret; | 86 | return ret; |
| 87 | } | 87 | } |
| 88 | 88 | ||
| @@ -289,7 +289,10 @@ static void mtdoops_notify_add(struct mtd_info *mtd) | |||
| 289 | } | 289 | } |
| 290 | 290 | ||
| 291 | cxt->mtd = mtd; | 291 | cxt->mtd = mtd; |
| 292 | cxt->oops_pages = mtd->size / OOPS_PAGE_SIZE; | 292 | if (mtd->size > INT_MAX) |
| 293 | cxt->oops_pages = INT_MAX / OOPS_PAGE_SIZE; | ||
| 294 | else | ||
| 295 | cxt->oops_pages = (int)mtd->size / OOPS_PAGE_SIZE; | ||
| 293 | 296 | ||
| 294 | find_next_position(cxt); | 297 | find_next_position(cxt); |
| 295 | 298 | ||
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 3728913fa5fa..144e6b613a77 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c | |||
| @@ -26,7 +26,7 @@ static LIST_HEAD(mtd_partitions); | |||
| 26 | struct mtd_part { | 26 | struct mtd_part { |
| 27 | struct mtd_info mtd; | 27 | struct mtd_info mtd; |
| 28 | struct mtd_info *master; | 28 | struct mtd_info *master; |
| 29 | u_int32_t offset; | 29 | uint64_t offset; |
| 30 | int index; | 30 | int index; |
| 31 | struct list_head list; | 31 | struct list_head list; |
| 32 | int registered; | 32 | int registered; |
| @@ -235,7 +235,7 @@ void mtd_erase_callback(struct erase_info *instr) | |||
| 235 | } | 235 | } |
| 236 | EXPORT_SYMBOL_GPL(mtd_erase_callback); | 236 | EXPORT_SYMBOL_GPL(mtd_erase_callback); |
| 237 | 237 | ||
| 238 | static int part_lock(struct mtd_info *mtd, loff_t ofs, size_t len) | 238 | static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) |
| 239 | { | 239 | { |
| 240 | struct mtd_part *part = PART(mtd); | 240 | struct mtd_part *part = PART(mtd); |
| 241 | if ((len + ofs) > mtd->size) | 241 | if ((len + ofs) > mtd->size) |
| @@ -243,7 +243,7 @@ static int part_lock(struct mtd_info *mtd, loff_t ofs, size_t len) | |||
| 243 | return part->master->lock(part->master, ofs + part->offset, len); | 243 | return part->master->lock(part->master, ofs + part->offset, len); |
| 244 | } | 244 | } |
| 245 | 245 | ||
| 246 | static int part_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) | 246 | static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) |
| 247 | { | 247 | { |
| 248 | struct mtd_part *part = PART(mtd); | 248 | struct mtd_part *part = PART(mtd); |
| 249 | if ((len + ofs) > mtd->size) | 249 | if ((len + ofs) > mtd->size) |
| @@ -317,7 +317,7 @@ EXPORT_SYMBOL(del_mtd_partitions); | |||
| 317 | 317 | ||
| 318 | static struct mtd_part *add_one_partition(struct mtd_info *master, | 318 | static struct mtd_part *add_one_partition(struct mtd_info *master, |
| 319 | const struct mtd_partition *part, int partno, | 319 | const struct mtd_partition *part, int partno, |
| 320 | u_int32_t cur_offset) | 320 | uint64_t cur_offset) |
| 321 | { | 321 | { |
| 322 | struct mtd_part *slave; | 322 | struct mtd_part *slave; |
| 323 | 323 | ||
| @@ -395,19 +395,19 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, | |||
| 395 | slave->offset = cur_offset; | 395 | slave->offset = cur_offset; |
| 396 | if (slave->offset == MTDPART_OFS_NXTBLK) { | 396 | if (slave->offset == MTDPART_OFS_NXTBLK) { |
| 397 | slave->offset = cur_offset; | 397 | slave->offset = cur_offset; |
| 398 | if ((cur_offset % master->erasesize) != 0) { | 398 | if (mtd_mod_by_eb(cur_offset, master) != 0) { |
| 399 | /* Round up to next erasesize */ | 399 | /* Round up to next erasesize */ |
| 400 | slave->offset = ((cur_offset / master->erasesize) + 1) * master->erasesize; | 400 | slave->offset = (mtd_div_by_eb(cur_offset, master) + 1) * master->erasesize; |
| 401 | printk(KERN_NOTICE "Moving partition %d: " | 401 | printk(KERN_NOTICE "Moving partition %d: " |
| 402 | "0x%08x -> 0x%08x\n", partno, | 402 | "0x%012llx -> 0x%012llx\n", partno, |
| 403 | cur_offset, slave->offset); | 403 | (unsigned long long)cur_offset, (unsigned long long)slave->offset); |
| 404 | } | 404 | } |
| 405 | } | 405 | } |
| 406 | if (slave->mtd.size == MTDPART_SIZ_FULL) | 406 | if (slave->mtd.size == MTDPART_SIZ_FULL) |
| 407 | slave->mtd.size = master->size - slave->offset; | 407 | slave->mtd.size = master->size - slave->offset; |
| 408 | 408 | ||
| 409 | printk(KERN_NOTICE "0x%08x-0x%08x : \"%s\"\n", slave->offset, | 409 | printk(KERN_NOTICE "0x%012llx-0x%012llx : \"%s\"\n", (unsigned long long)slave->offset, |
| 410 | slave->offset + slave->mtd.size, slave->mtd.name); | 410 | (unsigned long long)(slave->offset + slave->mtd.size), slave->mtd.name); |
| 411 | 411 | ||
| 412 | /* let's do some sanity checks */ | 412 | /* let's do some sanity checks */ |
| 413 | if (slave->offset >= master->size) { | 413 | if (slave->offset >= master->size) { |
| @@ -420,13 +420,13 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, | |||
| 420 | } | 420 | } |
| 421 | if (slave->offset + slave->mtd.size > master->size) { | 421 | if (slave->offset + slave->mtd.size > master->size) { |
| 422 | slave->mtd.size = master->size - slave->offset; | 422 | slave->mtd.size = master->size - slave->offset; |
| 423 | printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#x\n", | 423 | printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#llx\n", |
| 424 | part->name, master->name, slave->mtd.size); | 424 | part->name, master->name, (unsigned long long)slave->mtd.size); |
| 425 | } | 425 | } |
| 426 | if (master->numeraseregions > 1) { | 426 | if (master->numeraseregions > 1) { |
| 427 | /* Deal with variable erase size stuff */ | 427 | /* Deal with variable erase size stuff */ |
| 428 | int i, max = master->numeraseregions; | 428 | int i, max = master->numeraseregions; |
| 429 | u32 end = slave->offset + slave->mtd.size; | 429 | u64 end = slave->offset + slave->mtd.size; |
| 430 | struct mtd_erase_region_info *regions = master->eraseregions; | 430 | struct mtd_erase_region_info *regions = master->eraseregions; |
| 431 | 431 | ||
| 432 | /* Find the first erase regions which is part of this | 432 | /* Find the first erase regions which is part of this |
| @@ -449,7 +449,7 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, | |||
| 449 | } | 449 | } |
| 450 | 450 | ||
| 451 | if ((slave->mtd.flags & MTD_WRITEABLE) && | 451 | if ((slave->mtd.flags & MTD_WRITEABLE) && |
| 452 | (slave->offset % slave->mtd.erasesize)) { | 452 | mtd_mod_by_eb(slave->offset, &slave->mtd)) { |
| 453 | /* Doesn't start on a boundary of major erase size */ | 453 | /* Doesn't start on a boundary of major erase size */ |
| 454 | /* FIXME: Let it be writable if it is on a boundary of | 454 | /* FIXME: Let it be writable if it is on a boundary of |
| 455 | * _minor_ erase size though */ | 455 | * _minor_ erase size though */ |
| @@ -458,7 +458,7 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, | |||
| 458 | part->name); | 458 | part->name); |
| 459 | } | 459 | } |
| 460 | if ((slave->mtd.flags & MTD_WRITEABLE) && | 460 | if ((slave->mtd.flags & MTD_WRITEABLE) && |
| 461 | (slave->mtd.size % slave->mtd.erasesize)) { | 461 | mtd_mod_by_eb(slave->mtd.size, &slave->mtd)) { |
| 462 | slave->mtd.flags &= ~MTD_WRITEABLE; | 462 | slave->mtd.flags &= ~MTD_WRITEABLE; |
| 463 | printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n", | 463 | printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n", |
| 464 | part->name); | 464 | part->name); |
| @@ -466,7 +466,7 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, | |||
| 466 | 466 | ||
| 467 | slave->mtd.ecclayout = master->ecclayout; | 467 | slave->mtd.ecclayout = master->ecclayout; |
| 468 | if (master->block_isbad) { | 468 | if (master->block_isbad) { |
| 469 | uint32_t offs = 0; | 469 | uint64_t offs = 0; |
| 470 | 470 | ||
| 471 | while (offs < slave->mtd.size) { | 471 | while (offs < slave->mtd.size) { |
| 472 | if (master->block_isbad(master, | 472 | if (master->block_isbad(master, |
| @@ -501,7 +501,7 @@ int add_mtd_partitions(struct mtd_info *master, | |||
| 501 | int nbparts) | 501 | int nbparts) |
| 502 | { | 502 | { |
| 503 | struct mtd_part *slave; | 503 | struct mtd_part *slave; |
| 504 | u_int32_t cur_offset = 0; | 504 | uint64_t cur_offset = 0; |
| 505 | int i; | 505 | int i; |
| 506 | 506 | ||
| 507 | printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); | 507 | printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); |
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index f8ae0400c49c..8b12e6e109d3 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig | |||
| @@ -163,6 +163,13 @@ config MTD_NAND_S3C2410_HWECC | |||
| 163 | incorrect ECC generation, and if using these, the default of | 163 | incorrect ECC generation, and if using these, the default of |
| 164 | software ECC is preferable. | 164 | software ECC is preferable. |
| 165 | 165 | ||
| 166 | config MTD_NAND_NDFC | ||
| 167 | tristate "NDFC NanD Flash Controller" | ||
| 168 | depends on 4xx | ||
| 169 | select MTD_NAND_ECC_SMC | ||
| 170 | help | ||
| 171 | NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs | ||
| 172 | |||
| 166 | config MTD_NAND_S3C2410_CLKSTOP | 173 | config MTD_NAND_S3C2410_CLKSTOP |
| 167 | bool "S3C2410 NAND IDLE clock stop" | 174 | bool "S3C2410 NAND IDLE clock stop" |
| 168 | depends on MTD_NAND_S3C2410 | 175 | depends on MTD_NAND_S3C2410 |
diff --git a/drivers/mtd/nand/alauda.c b/drivers/mtd/nand/alauda.c index 962380394855..6d9649159a18 100644 --- a/drivers/mtd/nand/alauda.c +++ b/drivers/mtd/nand/alauda.c | |||
| @@ -676,11 +676,11 @@ static int alauda_probe(struct usb_interface *interface, | |||
| 676 | goto error; | 676 | goto error; |
| 677 | 677 | ||
| 678 | al->write_out = usb_sndbulkpipe(al->dev, | 678 | al->write_out = usb_sndbulkpipe(al->dev, |
| 679 | ep_wr->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); | 679 | usb_endpoint_num(ep_wr)); |
| 680 | al->bulk_in = usb_rcvbulkpipe(al->dev, | 680 | al->bulk_in = usb_rcvbulkpipe(al->dev, |
| 681 | ep_in->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); | 681 | usb_endpoint_num(ep_in)); |
| 682 | al->bulk_out = usb_sndbulkpipe(al->dev, | 682 | al->bulk_out = usb_sndbulkpipe(al->dev, |
| 683 | ep_out->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); | 683 | usb_endpoint_num(ep_out)); |
| 684 | 684 | ||
| 685 | /* second device is identical up to now */ | 685 | /* second device is identical up to now */ |
| 686 | memcpy(al+1, al, sizeof(*al)); | 686 | memcpy(al+1, al, sizeof(*al)); |
diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index b8064bf3aee4..22a6b2e50e91 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c | |||
| @@ -90,7 +90,7 @@ static int timing[3]; | |||
| 90 | module_param_array(timing, int, &numtimings, 0644); | 90 | module_param_array(timing, int, &numtimings, 0644); |
| 91 | 91 | ||
| 92 | #ifdef CONFIG_MTD_PARTITIONS | 92 | #ifdef CONFIG_MTD_PARTITIONS |
| 93 | static const char *part_probes[] = { "RedBoot", NULL }; | 93 | static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL }; |
| 94 | #endif | 94 | #endif |
| 95 | 95 | ||
| 96 | /* Hrm. Why isn't this already conditional on something in the struct device? */ | 96 | /* Hrm. Why isn't this already conditional on something in the struct device? */ |
| @@ -805,10 +805,13 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, | |||
| 805 | add_mtd_device(mtd); | 805 | add_mtd_device(mtd); |
| 806 | 806 | ||
| 807 | #ifdef CONFIG_MTD_PARTITIONS | 807 | #ifdef CONFIG_MTD_PARTITIONS |
| 808 | #ifdef CONFIG_MTD_CMDLINE_PARTS | ||
| 809 | mtd->name = "cafe_nand"; | ||
| 810 | #endif | ||
| 808 | nr_parts = parse_mtd_partitions(mtd, part_probes, &parts, 0); | 811 | nr_parts = parse_mtd_partitions(mtd, part_probes, &parts, 0); |
| 809 | if (nr_parts > 0) { | 812 | if (nr_parts > 0) { |
| 810 | cafe->parts = parts; | 813 | cafe->parts = parts; |
| 811 | dev_info(&cafe->pdev->dev, "%d RedBoot partitions found\n", nr_parts); | 814 | dev_info(&cafe->pdev->dev, "%d partitions found\n", nr_parts); |
| 812 | add_mtd_partitions(mtd, parts, nr_parts); | 815 | add_mtd_partitions(mtd, parts, nr_parts); |
| 813 | } | 816 | } |
| 814 | #endif | 817 | #endif |
diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 4aa5bd6158da..65929db29446 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c | |||
| @@ -777,7 +777,9 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) | |||
| 777 | /* Fill in fsl_elbc_mtd structure */ | 777 | /* Fill in fsl_elbc_mtd structure */ |
| 778 | priv->mtd.priv = chip; | 778 | priv->mtd.priv = chip; |
| 779 | priv->mtd.owner = THIS_MODULE; | 779 | priv->mtd.owner = THIS_MODULE; |
| 780 | priv->fmr = 0; /* rest filled in later */ | 780 | |
| 781 | /* Set the ECCM according to the settings in bootloader.*/ | ||
| 782 | priv->fmr = in_be32(&lbc->fmr) & FMR_ECCM; | ||
| 781 | 783 | ||
| 782 | /* fill in nand_chip structure */ | 784 | /* fill in nand_chip structure */ |
| 783 | /* set up function call table */ | 785 | /* set up function call table */ |
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 0a9c9cd33f96..0c3afccde8a2 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c | |||
| @@ -2014,13 +2014,14 @@ static int nand_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
| 2014 | int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, | 2014 | int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, |
| 2015 | int allowbbt) | 2015 | int allowbbt) |
| 2016 | { | 2016 | { |
| 2017 | int page, len, status, pages_per_block, ret, chipnr; | 2017 | int page, status, pages_per_block, ret, chipnr; |
| 2018 | struct nand_chip *chip = mtd->priv; | 2018 | struct nand_chip *chip = mtd->priv; |
| 2019 | int rewrite_bbt[NAND_MAX_CHIPS]={0}; | 2019 | loff_t rewrite_bbt[NAND_MAX_CHIPS]={0}; |
| 2020 | unsigned int bbt_masked_page = 0xffffffff; | 2020 | unsigned int bbt_masked_page = 0xffffffff; |
| 2021 | loff_t len; | ||
| 2021 | 2022 | ||
| 2022 | DEBUG(MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%08x, len = %i\n", | 2023 | DEBUG(MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%012llx, len = %llu\n", |
| 2023 | (unsigned int)instr->addr, (unsigned int)instr->len); | 2024 | (unsigned long long)instr->addr, (unsigned long long)instr->len); |
| 2024 | 2025 | ||
| 2025 | /* Start address must align on block boundary */ | 2026 | /* Start address must align on block boundary */ |
| 2026 | if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) { | 2027 | if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) { |
| @@ -2116,7 +2117,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, | |||
| 2116 | DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " | 2117 | DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " |
| 2117 | "Failed erase, page 0x%08x\n", page); | 2118 | "Failed erase, page 0x%08x\n", page); |
| 2118 | instr->state = MTD_ERASE_FAILED; | 2119 | instr->state = MTD_ERASE_FAILED; |
| 2119 | instr->fail_addr = (page << chip->page_shift); | 2120 | instr->fail_addr = |
| 2121 | ((loff_t)page << chip->page_shift); | ||
| 2120 | goto erase_exit; | 2122 | goto erase_exit; |
| 2121 | } | 2123 | } |
| 2122 | 2124 | ||
| @@ -2126,7 +2128,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, | |||
| 2126 | */ | 2128 | */ |
| 2127 | if (bbt_masked_page != 0xffffffff && | 2129 | if (bbt_masked_page != 0xffffffff && |
| 2128 | (page & BBT_PAGE_MASK) == bbt_masked_page) | 2130 | (page & BBT_PAGE_MASK) == bbt_masked_page) |
| 2129 | rewrite_bbt[chipnr] = (page << chip->page_shift); | 2131 | rewrite_bbt[chipnr] = |
| 2132 | ((loff_t)page << chip->page_shift); | ||
| 2130 | 2133 | ||
| 2131 | /* Increment page address and decrement length */ | 2134 | /* Increment page address and decrement length */ |
| 2132 | len -= (1 << chip->phys_erase_shift); | 2135 | len -= (1 << chip->phys_erase_shift); |
| @@ -2173,7 +2176,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, | |||
| 2173 | continue; | 2176 | continue; |
| 2174 | /* update the BBT for chip */ | 2177 | /* update the BBT for chip */ |
| 2175 | DEBUG(MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt " | 2178 | DEBUG(MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt " |
| 2176 | "(%d:0x%0x 0x%0x)\n", chipnr, rewrite_bbt[chipnr], | 2179 | "(%d:0x%0llx 0x%0x)\n", chipnr, rewrite_bbt[chipnr], |
| 2177 | chip->bbt_td->pages[chipnr]); | 2180 | chip->bbt_td->pages[chipnr]); |
| 2178 | nand_update_bbt(mtd, rewrite_bbt[chipnr]); | 2181 | nand_update_bbt(mtd, rewrite_bbt[chipnr]); |
| 2179 | } | 2182 | } |
| @@ -2365,7 +2368,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, | |||
| 2365 | if (!mtd->name) | 2368 | if (!mtd->name) |
| 2366 | mtd->name = type->name; | 2369 | mtd->name = type->name; |
| 2367 | 2370 | ||
| 2368 | chip->chipsize = type->chipsize << 20; | 2371 | chip->chipsize = (uint64_t)type->chipsize << 20; |
| 2369 | 2372 | ||
| 2370 | /* Newer devices have all the information in additional id bytes */ | 2373 | /* Newer devices have all the information in additional id bytes */ |
| 2371 | if (!type->pagesize) { | 2374 | if (!type->pagesize) { |
| @@ -2423,7 +2426,10 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, | |||
| 2423 | 2426 | ||
| 2424 | chip->bbt_erase_shift = chip->phys_erase_shift = | 2427 | chip->bbt_erase_shift = chip->phys_erase_shift = |
| 2425 | ffs(mtd->erasesize) - 1; | 2428 | ffs(mtd->erasesize) - 1; |
| 2426 | chip->chip_shift = ffs(chip->chipsize) - 1; | 2429 | if (chip->chipsize & 0xffffffff) |
| 2430 | chip->chip_shift = ffs((unsigned)chip->chipsize) - 1; | ||
| 2431 | else | ||
| 2432 | chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32)) + 32 - 1; | ||
| 2427 | 2433 | ||
| 2428 | /* Set the bad block position */ | 2434 | /* Set the bad block position */ |
| 2429 | chip->badblockpos = mtd->writesize > 512 ? | 2435 | chip->badblockpos = mtd->writesize > 512 ? |
| @@ -2517,7 +2523,6 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips) | |||
| 2517 | /** | 2523 | /** |
| 2518 | * nand_scan_tail - [NAND Interface] Scan for the NAND device | 2524 | * nand_scan_tail - [NAND Interface] Scan for the NAND device |
| 2519 | * @mtd: MTD device structure | 2525 | * @mtd: MTD device structure |
| 2520 | * @maxchips: Number of chips to scan for | ||
| 2521 | * | 2526 | * |
| 2522 | * This is the second phase of the normal nand_scan() function. It | 2527 | * This is the second phase of the normal nand_scan() function. It |
| 2523 | * fills out all the uninitialized function pointers with the defaults | 2528 | * fills out all the uninitialized function pointers with the defaults |
diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 0b1c48595f12..55c23e5cd210 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c | |||
| @@ -171,16 +171,16 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, | |||
| 171 | if (tmp == msk) | 171 | if (tmp == msk) |
| 172 | continue; | 172 | continue; |
| 173 | if (reserved_block_code && (tmp == reserved_block_code)) { | 173 | if (reserved_block_code && (tmp == reserved_block_code)) { |
| 174 | printk(KERN_DEBUG "nand_read_bbt: Reserved block at 0x%08x\n", | 174 | printk(KERN_DEBUG "nand_read_bbt: Reserved block at 0x%012llx\n", |
| 175 | ((offs << 2) + (act >> 1)) << this->bbt_erase_shift); | 175 | (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift); |
| 176 | this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06); | 176 | this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06); |
| 177 | mtd->ecc_stats.bbtblocks++; | 177 | mtd->ecc_stats.bbtblocks++; |
| 178 | continue; | 178 | continue; |
| 179 | } | 179 | } |
| 180 | /* Leave it for now, if its matured we can move this | 180 | /* Leave it for now, if its matured we can move this |
| 181 | * message to MTD_DEBUG_LEVEL0 */ | 181 | * message to MTD_DEBUG_LEVEL0 */ |
| 182 | printk(KERN_DEBUG "nand_read_bbt: Bad block at 0x%08x\n", | 182 | printk(KERN_DEBUG "nand_read_bbt: Bad block at 0x%012llx\n", |
| 183 | ((offs << 2) + (act >> 1)) << this->bbt_erase_shift); | 183 | (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift); |
| 184 | /* Factory marked bad or worn out ? */ | 184 | /* Factory marked bad or worn out ? */ |
| 185 | if (tmp == 0) | 185 | if (tmp == 0) |
| 186 | this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06); | 186 | this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06); |
| @@ -284,7 +284,7 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, | |||
| 284 | 284 | ||
| 285 | /* Read the primary version, if available */ | 285 | /* Read the primary version, if available */ |
| 286 | if (td->options & NAND_BBT_VERSION) { | 286 | if (td->options & NAND_BBT_VERSION) { |
| 287 | scan_read_raw(mtd, buf, td->pages[0] << this->page_shift, | 287 | scan_read_raw(mtd, buf, (loff_t)td->pages[0] << this->page_shift, |
| 288 | mtd->writesize); | 288 | mtd->writesize); |
| 289 | td->version[0] = buf[mtd->writesize + td->veroffs]; | 289 | td->version[0] = buf[mtd->writesize + td->veroffs]; |
| 290 | printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", | 290 | printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", |
| @@ -293,7 +293,7 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, | |||
| 293 | 293 | ||
| 294 | /* Read the mirror version, if available */ | 294 | /* Read the mirror version, if available */ |
| 295 | if (md && (md->options & NAND_BBT_VERSION)) { | 295 | if (md && (md->options & NAND_BBT_VERSION)) { |
| 296 | scan_read_raw(mtd, buf, md->pages[0] << this->page_shift, | 296 | scan_read_raw(mtd, buf, (loff_t)md->pages[0] << this->page_shift, |
| 297 | mtd->writesize); | 297 | mtd->writesize); |
| 298 | md->version[0] = buf[mtd->writesize + md->veroffs]; | 298 | md->version[0] = buf[mtd->writesize + md->veroffs]; |
| 299 | printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", | 299 | printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", |
| @@ -411,7 +411,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, | |||
| 411 | numblocks = this->chipsize >> (this->bbt_erase_shift - 1); | 411 | numblocks = this->chipsize >> (this->bbt_erase_shift - 1); |
| 412 | startblock = chip * numblocks; | 412 | startblock = chip * numblocks; |
| 413 | numblocks += startblock; | 413 | numblocks += startblock; |
| 414 | from = startblock << (this->bbt_erase_shift - 1); | 414 | from = (loff_t)startblock << (this->bbt_erase_shift - 1); |
| 415 | } | 415 | } |
| 416 | 416 | ||
| 417 | for (i = startblock; i < numblocks;) { | 417 | for (i = startblock; i < numblocks;) { |
| @@ -428,8 +428,8 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, | |||
| 428 | 428 | ||
| 429 | if (ret) { | 429 | if (ret) { |
| 430 | this->bbt[i >> 3] |= 0x03 << (i & 0x6); | 430 | this->bbt[i >> 3] |= 0x03 << (i & 0x6); |
| 431 | printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", | 431 | printk(KERN_WARNING "Bad eraseblock %d at 0x%012llx\n", |
| 432 | i >> 1, (unsigned int)from); | 432 | i >> 1, (unsigned long long)from); |
| 433 | mtd->ecc_stats.badblocks++; | 433 | mtd->ecc_stats.badblocks++; |
| 434 | } | 434 | } |
| 435 | 435 | ||
| @@ -495,7 +495,7 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr | |||
| 495 | for (block = 0; block < td->maxblocks; block++) { | 495 | for (block = 0; block < td->maxblocks; block++) { |
| 496 | 496 | ||
| 497 | int actblock = startblock + dir * block; | 497 | int actblock = startblock + dir * block; |
| 498 | loff_t offs = actblock << this->bbt_erase_shift; | 498 | loff_t offs = (loff_t)actblock << this->bbt_erase_shift; |
| 499 | 499 | ||
| 500 | /* Read first page */ | 500 | /* Read first page */ |
| 501 | scan_read_raw(mtd, buf, offs, mtd->writesize); | 501 | scan_read_raw(mtd, buf, offs, mtd->writesize); |
| @@ -719,7 +719,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, | |||
| 719 | 719 | ||
| 720 | memset(&einfo, 0, sizeof(einfo)); | 720 | memset(&einfo, 0, sizeof(einfo)); |
| 721 | einfo.mtd = mtd; | 721 | einfo.mtd = mtd; |
| 722 | einfo.addr = (unsigned long)to; | 722 | einfo.addr = to; |
| 723 | einfo.len = 1 << this->bbt_erase_shift; | 723 | einfo.len = 1 << this->bbt_erase_shift; |
| 724 | res = nand_erase_nand(mtd, &einfo, 1); | 724 | res = nand_erase_nand(mtd, &einfo, 1); |
| 725 | if (res < 0) | 725 | if (res < 0) |
| @@ -729,8 +729,8 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, | |||
| 729 | if (res < 0) | 729 | if (res < 0) |
| 730 | goto outerr; | 730 | goto outerr; |
| 731 | 731 | ||
| 732 | printk(KERN_DEBUG "Bad block table written to 0x%08x, version " | 732 | printk(KERN_DEBUG "Bad block table written to 0x%012llx, version " |
| 733 | "0x%02X\n", (unsigned int)to, td->version[chip]); | 733 | "0x%02X\n", (unsigned long long)to, td->version[chip]); |
| 734 | 734 | ||
| 735 | /* Mark it as used */ | 735 | /* Mark it as used */ |
| 736 | td->pages[chip] = page; | 736 | td->pages[chip] = page; |
| @@ -910,7 +910,7 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) | |||
| 910 | newval = oldval | (0x2 << (block & 0x06)); | 910 | newval = oldval | (0x2 << (block & 0x06)); |
| 911 | this->bbt[(block >> 3)] = newval; | 911 | this->bbt[(block >> 3)] = newval; |
| 912 | if ((oldval != newval) && td->reserved_block_code) | 912 | if ((oldval != newval) && td->reserved_block_code) |
| 913 | nand_update_bbt(mtd, block << (this->bbt_erase_shift - 1)); | 913 | nand_update_bbt(mtd, (loff_t)block << (this->bbt_erase_shift - 1)); |
| 914 | continue; | 914 | continue; |
| 915 | } | 915 | } |
| 916 | update = 0; | 916 | update = 0; |
| @@ -931,7 +931,7 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) | |||
| 931 | new ones have been marked, then we need to update the stored | 931 | new ones have been marked, then we need to update the stored |
| 932 | bbts. This should only happen once. */ | 932 | bbts. This should only happen once. */ |
| 933 | if (update && td->reserved_block_code) | 933 | if (update && td->reserved_block_code) |
| 934 | nand_update_bbt(mtd, (block - 2) << (this->bbt_erase_shift - 1)); | 934 | nand_update_bbt(mtd, (loff_t)(block - 2) << (this->bbt_erase_shift - 1)); |
| 935 | } | 935 | } |
| 936 | } | 936 | } |
| 937 | 937 | ||
| @@ -1027,7 +1027,6 @@ int nand_update_bbt(struct mtd_info *mtd, loff_t offs) | |||
| 1027 | if (!this->bbt || !td) | 1027 | if (!this->bbt || !td) |
| 1028 | return -EINVAL; | 1028 | return -EINVAL; |
| 1029 | 1029 | ||
| 1030 | len = mtd->size >> (this->bbt_erase_shift + 2); | ||
| 1031 | /* Allocate a temporary buffer for one eraseblock incl. oob */ | 1030 | /* Allocate a temporary buffer for one eraseblock incl. oob */ |
| 1032 | len = (1 << this->bbt_erase_shift); | 1031 | len = (1 << this->bbt_erase_shift); |
| 1033 | len += (len >> this->page_shift) * mtd->oobsize; | 1032 | len += (len >> this->page_shift) * mtd->oobsize; |
diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index ae7c57781a68..cd0711b83ac4 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c | |||
| @@ -38,6 +38,9 @@ | |||
| 38 | #include <linux/delay.h> | 38 | #include <linux/delay.h> |
| 39 | #include <linux/list.h> | 39 | #include <linux/list.h> |
| 40 | #include <linux/random.h> | 40 | #include <linux/random.h> |
| 41 | #include <linux/sched.h> | ||
| 42 | #include <linux/fs.h> | ||
| 43 | #include <linux/pagemap.h> | ||
| 41 | 44 | ||
| 42 | /* Default simulator parameters values */ | 45 | /* Default simulator parameters values */ |
| 43 | #if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE) || \ | 46 | #if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE) || \ |
| @@ -100,6 +103,7 @@ static unsigned int bitflips = 0; | |||
| 100 | static char *gravepages = NULL; | 103 | static char *gravepages = NULL; |
| 101 | static unsigned int rptwear = 0; | 104 | static unsigned int rptwear = 0; |
| 102 | static unsigned int overridesize = 0; | 105 | static unsigned int overridesize = 0; |
| 106 | static char *cache_file = NULL; | ||
| 103 | 107 | ||
| 104 | module_param(first_id_byte, uint, 0400); | 108 | module_param(first_id_byte, uint, 0400); |
| 105 | module_param(second_id_byte, uint, 0400); | 109 | module_param(second_id_byte, uint, 0400); |
| @@ -122,12 +126,13 @@ module_param(bitflips, uint, 0400); | |||
| 122 | module_param(gravepages, charp, 0400); | 126 | module_param(gravepages, charp, 0400); |
| 123 | module_param(rptwear, uint, 0400); | 127 | module_param(rptwear, uint, 0400); |
| 124 | module_param(overridesize, uint, 0400); | 128 | module_param(overridesize, uint, 0400); |
| 129 | module_param(cache_file, charp, 0400); | ||
| 125 | 130 | ||
| 126 | MODULE_PARM_DESC(first_id_byte, "The first byte returned by NAND Flash 'read ID' command (manufacturer ID)"); | 131 | MODULE_PARM_DESC(first_id_byte, "The first byte returned by NAND Flash 'read ID' command (manufacturer ID)"); |
| 127 | MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)"); | 132 | MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)"); |
| 128 | MODULE_PARM_DESC(third_id_byte, "The third byte returned by NAND Flash 'read ID' command"); | 133 | MODULE_PARM_DESC(third_id_byte, "The third byte returned by NAND Flash 'read ID' command"); |
| 129 | MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read ID' command"); | 134 | MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read ID' command"); |
| 130 | MODULE_PARM_DESC(access_delay, "Initial page access delay (microiseconds)"); | 135 | MODULE_PARM_DESC(access_delay, "Initial page access delay (microseconds)"); |
| 131 | MODULE_PARM_DESC(programm_delay, "Page programm delay (microseconds"); | 136 | MODULE_PARM_DESC(programm_delay, "Page programm delay (microseconds"); |
| 132 | MODULE_PARM_DESC(erase_delay, "Sector erase delay (milliseconds)"); | 137 | MODULE_PARM_DESC(erase_delay, "Sector erase delay (milliseconds)"); |
| 133 | MODULE_PARM_DESC(output_cycle, "Word output (from flash) time (nanodeconds)"); | 138 | MODULE_PARM_DESC(output_cycle, "Word output (from flash) time (nanodeconds)"); |
| @@ -153,6 +158,7 @@ MODULE_PARM_DESC(rptwear, "Number of erases inbetween reporting wear, if | |||
| 153 | MODULE_PARM_DESC(overridesize, "Specifies the NAND Flash size overriding the ID bytes. " | 158 | MODULE_PARM_DESC(overridesize, "Specifies the NAND Flash size overriding the ID bytes. " |
| 154 | "The size is specified in erase blocks and as the exponent of a power of two" | 159 | "The size is specified in erase blocks and as the exponent of a power of two" |
| 155 | " e.g. 5 means a size of 32 erase blocks"); | 160 | " e.g. 5 means a size of 32 erase blocks"); |
| 161 | MODULE_PARM_DESC(cache_file, "File to use to cache nand pages instead of memory"); | ||
| 156 | 162 | ||
| 157 | /* The largest possible page size */ | 163 | /* The largest possible page size */ |
| 158 | #define NS_LARGEST_PAGE_SIZE 2048 | 164 | #define NS_LARGEST_PAGE_SIZE 2048 |
| @@ -266,6 +272,9 @@ MODULE_PARM_DESC(overridesize, "Specifies the NAND Flash size overriding the I | |||
| 266 | */ | 272 | */ |
| 267 | #define NS_MAX_PREVSTATES 1 | 273 | #define NS_MAX_PREVSTATES 1 |
| 268 | 274 | ||
| 275 | /* Maximum page cache pages needed to read or write a NAND page to the cache_file */ | ||
| 276 | #define NS_MAX_HELD_PAGES 16 | ||
| 277 | |||
| 269 | /* | 278 | /* |
| 270 | * A union to represent flash memory contents and flash buffer. | 279 | * A union to represent flash memory contents and flash buffer. |
| 271 | */ | 280 | */ |
| @@ -295,6 +304,9 @@ struct nandsim { | |||
| 295 | /* The simulated NAND flash pages array */ | 304 | /* The simulated NAND flash pages array */ |
| 296 | union ns_mem *pages; | 305 | union ns_mem *pages; |
| 297 | 306 | ||
| 307 | /* Slab allocator for nand pages */ | ||
| 308 | struct kmem_cache *nand_pages_slab; | ||
| 309 | |||
| 298 | /* Internal buffer of page + OOB size bytes */ | 310 | /* Internal buffer of page + OOB size bytes */ |
| 299 | union ns_mem buf; | 311 | union ns_mem buf; |
| 300 | 312 | ||
| @@ -335,6 +347,13 @@ struct nandsim { | |||
| 335 | int ale; /* address Latch Enable */ | 347 | int ale; /* address Latch Enable */ |
| 336 | int wp; /* write Protect */ | 348 | int wp; /* write Protect */ |
| 337 | } lines; | 349 | } lines; |
| 350 | |||
| 351 | /* Fields needed when using a cache file */ | ||
| 352 | struct file *cfile; /* Open file */ | ||
| 353 | unsigned char *pages_written; /* Which pages have been written */ | ||
| 354 | void *file_buf; | ||
| 355 | struct page *held_pages[NS_MAX_HELD_PAGES]; | ||
| 356 | int held_cnt; | ||
| 338 | }; | 357 | }; |
| 339 | 358 | ||
| 340 | /* | 359 | /* |
| @@ -420,25 +439,69 @@ static struct mtd_info *nsmtd; | |||
| 420 | static u_char ns_verify_buf[NS_LARGEST_PAGE_SIZE]; | 439 | static u_char ns_verify_buf[NS_LARGEST_PAGE_SIZE]; |
| 421 | 440 | ||
| 422 | /* | 441 | /* |
| 423 | * Allocate array of page pointers and initialize the array to NULL | 442 | * Allocate array of page pointers, create slab allocation for an array |
| 424 | * pointers. | 443 | * and initialize the array by NULL pointers. |
| 425 | * | 444 | * |
| 426 | * RETURNS: 0 if success, -ENOMEM if memory alloc fails. | 445 | * RETURNS: 0 if success, -ENOMEM if memory alloc fails. |
| 427 | */ | 446 | */ |
| 428 | static int alloc_device(struct nandsim *ns) | 447 | static int alloc_device(struct nandsim *ns) |
| 429 | { | 448 | { |
| 430 | int i; | 449 | struct file *cfile; |
| 450 | int i, err; | ||
| 451 | |||
| 452 | if (cache_file) { | ||
| 453 | cfile = filp_open(cache_file, O_CREAT | O_RDWR | O_LARGEFILE, 0600); | ||
| 454 | if (IS_ERR(cfile)) | ||
| 455 | return PTR_ERR(cfile); | ||
| 456 | if (!cfile->f_op || (!cfile->f_op->read && !cfile->f_op->aio_read)) { | ||
| 457 | NS_ERR("alloc_device: cache file not readable\n"); | ||
| 458 | err = -EINVAL; | ||
| 459 | goto err_close; | ||
| 460 | } | ||
| 461 | if (!cfile->f_op->write && !cfile->f_op->aio_write) { | ||
| 462 | NS_ERR("alloc_device: cache file not writeable\n"); | ||
| 463 | err = -EINVAL; | ||
| 464 | goto err_close; | ||
| 465 | } | ||
| 466 | ns->pages_written = vmalloc(ns->geom.pgnum); | ||
| 467 | if (!ns->pages_written) { | ||
| 468 | NS_ERR("alloc_device: unable to allocate pages written array\n"); | ||
| 469 | err = -ENOMEM; | ||
| 470 | goto err_close; | ||
| 471 | } | ||
| 472 | ns->file_buf = kmalloc(ns->geom.pgszoob, GFP_KERNEL); | ||
| 473 | if (!ns->file_buf) { | ||
| 474 | NS_ERR("alloc_device: unable to allocate file buf\n"); | ||
| 475 | err = -ENOMEM; | ||
| 476 | goto err_free; | ||
| 477 | } | ||
| 478 | ns->cfile = cfile; | ||
| 479 | memset(ns->pages_written, 0, ns->geom.pgnum); | ||
| 480 | return 0; | ||
| 481 | } | ||
| 431 | 482 | ||
| 432 | ns->pages = vmalloc(ns->geom.pgnum * sizeof(union ns_mem)); | 483 | ns->pages = vmalloc(ns->geom.pgnum * sizeof(union ns_mem)); |
| 433 | if (!ns->pages) { | 484 | if (!ns->pages) { |
| 434 | NS_ERR("alloc_map: unable to allocate page array\n"); | 485 | NS_ERR("alloc_device: unable to allocate page array\n"); |
| 435 | return -ENOMEM; | 486 | return -ENOMEM; |
| 436 | } | 487 | } |
| 437 | for (i = 0; i < ns->geom.pgnum; i++) { | 488 | for (i = 0; i < ns->geom.pgnum; i++) { |
| 438 | ns->pages[i].byte = NULL; | 489 | ns->pages[i].byte = NULL; |
| 439 | } | 490 | } |
| 491 | ns->nand_pages_slab = kmem_cache_create("nandsim", | ||
| 492 | ns->geom.pgszoob, 0, 0, NULL); | ||
| 493 | if (!ns->nand_pages_slab) { | ||
| 494 | NS_ERR("cache_create: unable to create kmem_cache\n"); | ||
| 495 | return -ENOMEM; | ||
| 496 | } | ||
| 440 | 497 | ||
| 441 | return 0; | 498 | return 0; |
| 499 | |||
| 500 | err_free: | ||
| 501 | vfree(ns->pages_written); | ||
| 502 | err_close: | ||
| 503 | filp_close(cfile, NULL); | ||
| 504 | return err; | ||
| 442 | } | 505 | } |
| 443 | 506 | ||
| 444 | /* | 507 | /* |
| @@ -448,11 +511,20 @@ static void free_device(struct nandsim *ns) | |||
| 448 | { | 511 | { |
| 449 | int i; | 512 | int i; |
| 450 | 513 | ||
| 514 | if (ns->cfile) { | ||
| 515 | kfree(ns->file_buf); | ||
| 516 | vfree(ns->pages_written); | ||
| 517 | filp_close(ns->cfile, NULL); | ||
| 518 | return; | ||
| 519 | } | ||
| 520 | |||
| 451 | if (ns->pages) { | 521 | if (ns->pages) { |
| 452 | for (i = 0; i < ns->geom.pgnum; i++) { | 522 | for (i = 0; i < ns->geom.pgnum; i++) { |
| 453 | if (ns->pages[i].byte) | 523 | if (ns->pages[i].byte) |
| 454 | kfree(ns->pages[i].byte); | 524 | kmem_cache_free(ns->nand_pages_slab, |
| 525 | ns->pages[i].byte); | ||
| 455 | } | 526 | } |
| 527 | kmem_cache_destroy(ns->nand_pages_slab); | ||
| 456 | vfree(ns->pages); | 528 | vfree(ns->pages); |
| 457 | } | 529 | } |
| 458 | } | 530 | } |
| @@ -464,7 +536,7 @@ static char *get_partition_name(int i) | |||
| 464 | return kstrdup(buf, GFP_KERNEL); | 536 | return kstrdup(buf, GFP_KERNEL); |
| 465 | } | 537 | } |
| 466 | 538 | ||
| 467 | static u_int64_t divide(u_int64_t n, u_int32_t d) | 539 | static uint64_t divide(uint64_t n, uint32_t d) |
| 468 | { | 540 | { |
| 469 | do_div(n, d); | 541 | do_div(n, d); |
| 470 | return n; | 542 | return n; |
| @@ -480,8 +552,8 @@ static int init_nandsim(struct mtd_info *mtd) | |||
| 480 | struct nand_chip *chip = (struct nand_chip *)mtd->priv; | 552 | struct nand_chip *chip = (struct nand_chip *)mtd->priv; |
| 481 | struct nandsim *ns = (struct nandsim *)(chip->priv); | 553 | struct nandsim *ns = (struct nandsim *)(chip->priv); |
| 482 | int i, ret = 0; | 554 | int i, ret = 0; |
| 483 | u_int64_t remains; | 555 | uint64_t remains; |
| 484 | u_int64_t next_offset; | 556 | uint64_t next_offset; |
| 485 | 557 | ||
| 486 | if (NS_IS_INITIALIZED(ns)) { | 558 | if (NS_IS_INITIALIZED(ns)) { |
| 487 | NS_ERR("init_nandsim: nandsim is already initialized\n"); | 559 | NS_ERR("init_nandsim: nandsim is already initialized\n"); |
| @@ -548,7 +620,7 @@ static int init_nandsim(struct mtd_info *mtd) | |||
| 548 | remains = ns->geom.totsz; | 620 | remains = ns->geom.totsz; |
| 549 | next_offset = 0; | 621 | next_offset = 0; |
| 550 | for (i = 0; i < parts_num; ++i) { | 622 | for (i = 0; i < parts_num; ++i) { |
| 551 | u_int64_t part_sz = (u_int64_t)parts[i] * ns->geom.secsz; | 623 | uint64_t part_sz = (uint64_t)parts[i] * ns->geom.secsz; |
| 552 | 624 | ||
| 553 | if (!part_sz || part_sz > remains) { | 625 | if (!part_sz || part_sz > remains) { |
| 554 | NS_ERR("bad partition size.\n"); | 626 | NS_ERR("bad partition size.\n"); |
| @@ -1211,6 +1283,97 @@ static int find_operation(struct nandsim *ns, uint32_t flag) | |||
| 1211 | return -1; | 1283 | return -1; |
| 1212 | } | 1284 | } |
| 1213 | 1285 | ||
| 1286 | static void put_pages(struct nandsim *ns) | ||
| 1287 | { | ||
| 1288 | int i; | ||
| 1289 | |||
| 1290 | for (i = 0; i < ns->held_cnt; i++) | ||
| 1291 | page_cache_release(ns->held_pages[i]); | ||
| 1292 | } | ||
| 1293 | |||
| 1294 | /* Get page cache pages in advance to provide NOFS memory allocation */ | ||
| 1295 | static int get_pages(struct nandsim *ns, struct file *file, size_t count, loff_t pos) | ||
| 1296 | { | ||
| 1297 | pgoff_t index, start_index, end_index; | ||
| 1298 | struct page *page; | ||
| 1299 | struct address_space *mapping = file->f_mapping; | ||
| 1300 | |||
| 1301 | start_index = pos >> PAGE_CACHE_SHIFT; | ||
| 1302 | end_index = (pos + count - 1) >> PAGE_CACHE_SHIFT; | ||
| 1303 | if (end_index - start_index + 1 > NS_MAX_HELD_PAGES) | ||
| 1304 | return -EINVAL; | ||
| 1305 | ns->held_cnt = 0; | ||
| 1306 | for (index = start_index; index <= end_index; index++) { | ||
| 1307 | page = find_get_page(mapping, index); | ||
| 1308 | if (page == NULL) { | ||
| 1309 | page = find_or_create_page(mapping, index, GFP_NOFS); | ||
| 1310 | if (page == NULL) { | ||
| 1311 | write_inode_now(mapping->host, 1); | ||
| 1312 | page = find_or_create_page(mapping, index, GFP_NOFS); | ||
| 1313 | } | ||
| 1314 | if (page == NULL) { | ||
| 1315 | put_pages(ns); | ||
| 1316 | return -ENOMEM; | ||
| 1317 | } | ||
| 1318 | unlock_page(page); | ||
| 1319 | } | ||
| 1320 | ns->held_pages[ns->held_cnt++] = page; | ||
| 1321 | } | ||
| 1322 | return 0; | ||
| 1323 | } | ||
| 1324 | |||
| 1325 | static int set_memalloc(void) | ||
| 1326 | { | ||
| 1327 | if (current->flags & PF_MEMALLOC) | ||
| 1328 | return 0; | ||
| 1329 | current->flags |= PF_MEMALLOC; | ||
| 1330 | return 1; | ||
| 1331 | } | ||
| 1332 | |||
| 1333 | static void clear_memalloc(int memalloc) | ||
| 1334 | { | ||
| 1335 | if (memalloc) | ||
| 1336 | current->flags &= ~PF_MEMALLOC; | ||
| 1337 | } | ||
| 1338 | |||
| 1339 | static ssize_t read_file(struct nandsim *ns, struct file *file, void *buf, size_t count, loff_t *pos) | ||
| 1340 | { | ||
| 1341 | mm_segment_t old_fs; | ||
| 1342 | ssize_t tx; | ||
| 1343 | int err, memalloc; | ||
| 1344 | |||
| 1345 | err = get_pages(ns, file, count, *pos); | ||
| 1346 | if (err) | ||
| 1347 | return err; | ||
| 1348 | old_fs = get_fs(); | ||
| 1349 | set_fs(get_ds()); | ||
| 1350 | memalloc = set_memalloc(); | ||
| 1351 | tx = vfs_read(file, (char __user *)buf, count, pos); | ||
| 1352 | clear_memalloc(memalloc); | ||
| 1353 | set_fs(old_fs); | ||
| 1354 | put_pages(ns); | ||
| 1355 | return tx; | ||
| 1356 | } | ||
| 1357 | |||
| 1358 | static ssize_t write_file(struct nandsim *ns, struct file *file, void *buf, size_t count, loff_t *pos) | ||
| 1359 | { | ||
| 1360 | mm_segment_t old_fs; | ||
| 1361 | ssize_t tx; | ||
| 1362 | int err, memalloc; | ||
| 1363 | |||
| 1364 | err = get_pages(ns, file, count, *pos); | ||
| 1365 | if (err) | ||
| 1366 | return err; | ||
| 1367 | old_fs = get_fs(); | ||
| 1368 | set_fs(get_ds()); | ||
| 1369 | memalloc = set_memalloc(); | ||
| 1370 | tx = vfs_write(file, (char __user *)buf, count, pos); | ||
| 1371 | clear_memalloc(memalloc); | ||
| 1372 | set_fs(old_fs); | ||
| 1373 | put_pages(ns); | ||
| 1374 | return tx; | ||
| 1375 | } | ||
| 1376 | |||
| 1214 | /* | 1377 | /* |
| 1215 | * Returns a pointer to the current page. | 1378 | * Returns a pointer to the current page. |
| 1216 | */ | 1379 | */ |
| @@ -1227,6 +1390,38 @@ static inline u_char *NS_PAGE_BYTE_OFF(struct nandsim *ns) | |||
| 1227 | return NS_GET_PAGE(ns)->byte + ns->regs.column + ns->regs.off; | 1390 | return NS_GET_PAGE(ns)->byte + ns->regs.column + ns->regs.off; |
| 1228 | } | 1391 | } |
| 1229 | 1392 | ||
| 1393 | int do_read_error(struct nandsim *ns, int num) | ||
| 1394 | { | ||
| 1395 | unsigned int page_no = ns->regs.row; | ||
| 1396 | |||
| 1397 | if (read_error(page_no)) { | ||
| 1398 | int i; | ||
| 1399 | memset(ns->buf.byte, 0xFF, num); | ||
| 1400 | for (i = 0; i < num; ++i) | ||
| 1401 | ns->buf.byte[i] = random32(); | ||
| 1402 | NS_WARN("simulating read error in page %u\n", page_no); | ||
| 1403 | return 1; | ||
| 1404 | } | ||
| 1405 | return 0; | ||
| 1406 | } | ||
| 1407 | |||
| 1408 | void do_bit_flips(struct nandsim *ns, int num) | ||
| 1409 | { | ||
| 1410 | if (bitflips && random32() < (1 << 22)) { | ||
| 1411 | int flips = 1; | ||
| 1412 | if (bitflips > 1) | ||
| 1413 | flips = (random32() % (int) bitflips) + 1; | ||
| 1414 | while (flips--) { | ||
| 1415 | int pos = random32() % (num * 8); | ||
| 1416 | ns->buf.byte[pos / 8] ^= (1 << (pos % 8)); | ||
| 1417 | NS_WARN("read_page: flipping bit %d in page %d " | ||
| 1418 | "reading from %d ecc: corrected=%u failed=%u\n", | ||
| 1419 | pos, ns->regs.row, ns->regs.column + ns->regs.off, | ||
| 1420 | nsmtd->ecc_stats.corrected, nsmtd->ecc_stats.failed); | ||
| 1421 | } | ||
| 1422 | } | ||
| 1423 | } | ||
| 1424 | |||
| 1230 | /* | 1425 | /* |
| 1231 | * Fill the NAND buffer with data read from the specified page. | 1426 | * Fill the NAND buffer with data read from the specified page. |
| 1232 | */ | 1427 | */ |
| @@ -1234,36 +1429,40 @@ static void read_page(struct nandsim *ns, int num) | |||
| 1234 | { | 1429 | { |
| 1235 | union ns_mem *mypage; | 1430 | union ns_mem *mypage; |
| 1236 | 1431 | ||
| 1432 | if (ns->cfile) { | ||
| 1433 | if (!ns->pages_written[ns->regs.row]) { | ||
| 1434 | NS_DBG("read_page: page %d not written\n", ns->regs.row); | ||
| 1435 | memset(ns->buf.byte, 0xFF, num); | ||
| 1436 | } else { | ||
| 1437 | loff_t pos; | ||
| 1438 | ssize_t tx; | ||
| 1439 | |||
| 1440 | NS_DBG("read_page: page %d written, reading from %d\n", | ||
| 1441 | ns->regs.row, ns->regs.column + ns->regs.off); | ||
| 1442 | if (do_read_error(ns, num)) | ||
| 1443 | return; | ||
| 1444 | pos = (loff_t)ns->regs.row * ns->geom.pgszoob + ns->regs.column + ns->regs.off; | ||
| 1445 | tx = read_file(ns, ns->cfile, ns->buf.byte, num, &pos); | ||
| 1446 | if (tx != num) { | ||
| 1447 | NS_ERR("read_page: read error for page %d ret %ld\n", ns->regs.row, (long)tx); | ||
| 1448 | return; | ||
| 1449 | } | ||
| 1450 | do_bit_flips(ns, num); | ||
| 1451 | } | ||
| 1452 | return; | ||
| 1453 | } | ||
| 1454 | |||
| 1237 | mypage = NS_GET_PAGE(ns); | 1455 | mypage = NS_GET_PAGE(ns); |
| 1238 | if (mypage->byte == NULL) { | 1456 | if (mypage->byte == NULL) { |
| 1239 | NS_DBG("read_page: page %d not allocated\n", ns->regs.row); | 1457 | NS_DBG("read_page: page %d not allocated\n", ns->regs.row); |
| 1240 | memset(ns->buf.byte, 0xFF, num); | 1458 | memset(ns->buf.byte, 0xFF, num); |
| 1241 | } else { | 1459 | } else { |
| 1242 | unsigned int page_no = ns->regs.row; | ||
| 1243 | NS_DBG("read_page: page %d allocated, reading from %d\n", | 1460 | NS_DBG("read_page: page %d allocated, reading from %d\n", |
| 1244 | ns->regs.row, ns->regs.column + ns->regs.off); | 1461 | ns->regs.row, ns->regs.column + ns->regs.off); |
| 1245 | if (read_error(page_no)) { | 1462 | if (do_read_error(ns, num)) |
| 1246 | int i; | ||
| 1247 | memset(ns->buf.byte, 0xFF, num); | ||
| 1248 | for (i = 0; i < num; ++i) | ||
| 1249 | ns->buf.byte[i] = random32(); | ||
| 1250 | NS_WARN("simulating read error in page %u\n", page_no); | ||
| 1251 | return; | 1463 | return; |
| 1252 | } | ||
| 1253 | memcpy(ns->buf.byte, NS_PAGE_BYTE_OFF(ns), num); | 1464 | memcpy(ns->buf.byte, NS_PAGE_BYTE_OFF(ns), num); |
| 1254 | if (bitflips && random32() < (1 << 22)) { | 1465 | do_bit_flips(ns, num); |
| 1255 | int flips = 1; | ||
| 1256 | if (bitflips > 1) | ||
| 1257 | flips = (random32() % (int) bitflips) + 1; | ||
| 1258 | while (flips--) { | ||
| 1259 | int pos = random32() % (num * 8); | ||
| 1260 | ns->buf.byte[pos / 8] ^= (1 << (pos % 8)); | ||
| 1261 | NS_WARN("read_page: flipping bit %d in page %d " | ||
| 1262 | "reading from %d ecc: corrected=%u failed=%u\n", | ||
| 1263 | pos, ns->regs.row, ns->regs.column + ns->regs.off, | ||
| 1264 | nsmtd->ecc_stats.corrected, nsmtd->ecc_stats.failed); | ||
| 1265 | } | ||
| 1266 | } | ||
| 1267 | } | 1466 | } |
| 1268 | } | 1467 | } |
| 1269 | 1468 | ||
| @@ -1275,11 +1474,20 @@ static void erase_sector(struct nandsim *ns) | |||
| 1275 | union ns_mem *mypage; | 1474 | union ns_mem *mypage; |
| 1276 | int i; | 1475 | int i; |
| 1277 | 1476 | ||
| 1477 | if (ns->cfile) { | ||
| 1478 | for (i = 0; i < ns->geom.pgsec; i++) | ||
| 1479 | if (ns->pages_written[ns->regs.row + i]) { | ||
| 1480 | NS_DBG("erase_sector: freeing page %d\n", ns->regs.row + i); | ||
| 1481 | ns->pages_written[ns->regs.row + i] = 0; | ||
| 1482 | } | ||
| 1483 | return; | ||
| 1484 | } | ||
| 1485 | |||
| 1278 | mypage = NS_GET_PAGE(ns); | 1486 | mypage = NS_GET_PAGE(ns); |
| 1279 | for (i = 0; i < ns->geom.pgsec; i++) { | 1487 | for (i = 0; i < ns->geom.pgsec; i++) { |
| 1280 | if (mypage->byte != NULL) { | 1488 | if (mypage->byte != NULL) { |
| 1281 | NS_DBG("erase_sector: freeing page %d\n", ns->regs.row+i); | 1489 | NS_DBG("erase_sector: freeing page %d\n", ns->regs.row+i); |
| 1282 | kfree(mypage->byte); | 1490 | kmem_cache_free(ns->nand_pages_slab, mypage->byte); |
| 1283 | mypage->byte = NULL; | 1491 | mypage->byte = NULL; |
| 1284 | } | 1492 | } |
| 1285 | mypage++; | 1493 | mypage++; |
| @@ -1295,16 +1503,57 @@ static int prog_page(struct nandsim *ns, int num) | |||
| 1295 | union ns_mem *mypage; | 1503 | union ns_mem *mypage; |
| 1296 | u_char *pg_off; | 1504 | u_char *pg_off; |
| 1297 | 1505 | ||
| 1506 | if (ns->cfile) { | ||
| 1507 | loff_t off, pos; | ||
| 1508 | ssize_t tx; | ||
| 1509 | int all; | ||
| 1510 | |||
| 1511 | NS_DBG("prog_page: writing page %d\n", ns->regs.row); | ||
| 1512 | pg_off = ns->file_buf + ns->regs.column + ns->regs.off; | ||
| 1513 | off = (loff_t)ns->regs.row * ns->geom.pgszoob + ns->regs.column + ns->regs.off; | ||
| 1514 | if (!ns->pages_written[ns->regs.row]) { | ||
| 1515 | all = 1; | ||
| 1516 | memset(ns->file_buf, 0xff, ns->geom.pgszoob); | ||
| 1517 | } else { | ||
| 1518 | all = 0; | ||
| 1519 | pos = off; | ||
| 1520 | tx = read_file(ns, ns->cfile, pg_off, num, &pos); | ||
| 1521 | if (tx != num) { | ||
| 1522 | NS_ERR("prog_page: read error for page %d ret %ld\n", ns->regs.row, (long)tx); | ||
| 1523 | return -1; | ||
| 1524 | } | ||
| 1525 | } | ||
| 1526 | for (i = 0; i < num; i++) | ||
| 1527 | pg_off[i] &= ns->buf.byte[i]; | ||
| 1528 | if (all) { | ||
| 1529 | pos = (loff_t)ns->regs.row * ns->geom.pgszoob; | ||
| 1530 | tx = write_file(ns, ns->cfile, ns->file_buf, ns->geom.pgszoob, &pos); | ||
| 1531 | if (tx != ns->geom.pgszoob) { | ||
| 1532 | NS_ERR("prog_page: write error for page %d ret %ld\n", ns->regs.row, (long)tx); | ||
| 1533 | return -1; | ||
| 1534 | } | ||
| 1535 | ns->pages_written[ns->regs.row] = 1; | ||
| 1536 | } else { | ||
| 1537 | pos = off; | ||
| 1538 | tx = write_file(ns, ns->cfile, pg_off, num, &pos); | ||
| 1539 | if (tx != num) { | ||
| 1540 | NS_ERR("prog_page: write error for page %d ret %ld\n", ns->regs.row, (long)tx); | ||
| 1541 | return -1; | ||
| 1542 | } | ||
| 1543 | } | ||
| 1544 | return 0; | ||
| 1545 | } | ||
| 1546 | |||
| 1298 | mypage = NS_GET_PAGE(ns); | 1547 | mypage = NS_GET_PAGE(ns); |
| 1299 | if (mypage->byte == NULL) { | 1548 | if (mypage->byte == NULL) { |
| 1300 | NS_DBG("prog_page: allocating page %d\n", ns->regs.row); | 1549 | NS_DBG("prog_page: allocating page %d\n", ns->regs.row); |
| 1301 | /* | 1550 | /* |
| 1302 | * We allocate memory with GFP_NOFS because a flash FS may | 1551 | * We allocate memory with GFP_NOFS because a flash FS may |
| 1303 | * utilize this. If it is holding an FS lock, then gets here, | 1552 | * utilize this. If it is holding an FS lock, then gets here, |
| 1304 | * then kmalloc runs writeback which goes to the FS again | 1553 | * then kernel memory alloc runs writeback which goes to the FS |
| 1305 | * and deadlocks. This was seen in practice. | 1554 | * again and deadlocks. This was seen in practice. |
| 1306 | */ | 1555 | */ |
| 1307 | mypage->byte = kmalloc(ns->geom.pgszoob, GFP_NOFS); | 1556 | mypage->byte = kmem_cache_alloc(ns->nand_pages_slab, GFP_NOFS); |
| 1308 | if (mypage->byte == NULL) { | 1557 | if (mypage->byte == NULL) { |
| 1309 | NS_ERR("prog_page: error allocating memory for page %d\n", ns->regs.row); | 1558 | NS_ERR("prog_page: error allocating memory for page %d\n", ns->regs.row); |
| 1310 | return -1; | 1559 | return -1; |
| @@ -1736,13 +1985,17 @@ static void ns_nand_write_byte(struct mtd_info *mtd, u_char byte) | |||
| 1736 | 1985 | ||
| 1737 | /* Check if chip is expecting command */ | 1986 | /* Check if chip is expecting command */ |
| 1738 | if (NS_STATE(ns->nxstate) != STATE_UNKNOWN && !(ns->nxstate & STATE_CMD_MASK)) { | 1987 | if (NS_STATE(ns->nxstate) != STATE_UNKNOWN && !(ns->nxstate & STATE_CMD_MASK)) { |
| 1739 | /* | 1988 | /* Do not warn if only 2 id bytes are read */ |
| 1740 | * We are in situation when something else (not command) | 1989 | if (!(ns->regs.command == NAND_CMD_READID && |
| 1741 | * was expected but command was input. In this case ignore | 1990 | NS_STATE(ns->state) == STATE_DATAOUT_ID && ns->regs.count == 2)) { |
| 1742 | * previous command(s)/state(s) and accept the last one. | 1991 | /* |
| 1743 | */ | 1992 | * We are in situation when something else (not command) |
| 1744 | NS_WARN("write_byte: command (%#x) wasn't expected, expected state is %s, " | 1993 | * was expected but command was input. In this case ignore |
| 1745 | "ignore previous states\n", (uint)byte, get_state_name(ns->nxstate)); | 1994 | * previous command(s)/state(s) and accept the last one. |
| 1995 | */ | ||
| 1996 | NS_WARN("write_byte: command (%#x) wasn't expected, expected state is %s, " | ||
| 1997 | "ignore previous states\n", (uint)byte, get_state_name(ns->nxstate)); | ||
| 1998 | } | ||
| 1746 | switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); | 1999 | switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); |
| 1747 | } | 2000 | } |
| 1748 | 2001 | ||
| @@ -2044,7 +2297,7 @@ static int __init ns_init_module(void) | |||
| 2044 | } | 2297 | } |
| 2045 | 2298 | ||
| 2046 | if (overridesize) { | 2299 | if (overridesize) { |
| 2047 | u_int64_t new_size = (u_int64_t)nsmtd->erasesize << overridesize; | 2300 | uint64_t new_size = (uint64_t)nsmtd->erasesize << overridesize; |
| 2048 | if (new_size >> overridesize != nsmtd->erasesize) { | 2301 | if (new_size >> overridesize != nsmtd->erasesize) { |
| 2049 | NS_ERR("overridesize is too big\n"); | 2302 | NS_ERR("overridesize is too big\n"); |
| 2050 | goto err_exit; | 2303 | goto err_exit; |
diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index 955959eb02d4..582cf80f555a 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c | |||
| @@ -2,12 +2,20 @@ | |||
| 2 | * drivers/mtd/ndfc.c | 2 | * drivers/mtd/ndfc.c |
| 3 | * | 3 | * |
| 4 | * Overview: | 4 | * Overview: |
| 5 | * Platform independend driver for NDFC (NanD Flash Controller) | 5 | * Platform independent driver for NDFC (NanD Flash Controller) |
| 6 | * integrated into EP440 cores | 6 | * integrated into EP440 cores |
| 7 | * | 7 | * |
| 8 | * Ported to an OF platform driver by Sean MacLennan | ||
| 9 | * | ||
| 10 | * The NDFC supports multiple chips, but this driver only supports a | ||
| 11 | * single chip since I do not have access to any boards with | ||
| 12 | * multiple chips. | ||
| 13 | * | ||
| 8 | * Author: Thomas Gleixner | 14 | * Author: Thomas Gleixner |
| 9 | * | 15 | * |
| 10 | * Copyright 2006 IBM | 16 | * Copyright 2006 IBM |
| 17 | * Copyright 2008 PIKA Technologies | ||
| 18 | * Sean MacLennan <smaclennan@pikatech.com> | ||
| 11 | * | 19 | * |
| 12 | * This program is free software; you can redistribute it and/or modify it | 20 | * This program is free software; you can redistribute it and/or modify it |
| 13 | * under the terms of the GNU General Public License as published by the | 21 | * under the terms of the GNU General Public License as published by the |
| @@ -21,27 +29,20 @@ | |||
| 21 | #include <linux/mtd/partitions.h> | 29 | #include <linux/mtd/partitions.h> |
| 22 | #include <linux/mtd/ndfc.h> | 30 | #include <linux/mtd/ndfc.h> |
| 23 | #include <linux/mtd/mtd.h> | 31 | #include <linux/mtd/mtd.h> |
| 24 | #include <linux/platform_device.h> | 32 | #include <linux/of_platform.h> |
| 25 | |||
| 26 | #include <asm/io.h> | 33 | #include <asm/io.h> |
| 27 | #ifdef CONFIG_40x | ||
| 28 | #include <asm/ibm405.h> | ||
| 29 | #else | ||
| 30 | #include <asm/ibm44x.h> | ||
| 31 | #endif | ||
| 32 | |||
| 33 | struct ndfc_nand_mtd { | ||
| 34 | struct mtd_info mtd; | ||
| 35 | struct nand_chip chip; | ||
| 36 | struct platform_nand_chip *pl_chip; | ||
| 37 | }; | ||
| 38 | 34 | ||
| 39 | static struct ndfc_nand_mtd ndfc_mtd[NDFC_MAX_BANKS]; | ||
| 40 | 35 | ||
| 41 | struct ndfc_controller { | 36 | struct ndfc_controller { |
| 42 | void __iomem *ndfcbase; | 37 | struct of_device *ofdev; |
| 43 | struct nand_hw_control ndfc_control; | 38 | void __iomem *ndfcbase; |
| 44 | atomic_t childs_active; | 39 | struct mtd_info mtd; |
| 40 | struct nand_chip chip; | ||
| 41 | int chip_select; | ||
| 42 | struct nand_hw_control ndfc_control; | ||
| 43 | #ifdef CONFIG_MTD_PARTITIONS | ||
| 44 | struct mtd_partition *parts; | ||
| 45 | #endif | ||
| 45 | }; | 46 | }; |
| 46 | 47 | ||
| 47 | static struct ndfc_controller ndfc_ctrl; | 48 | static struct ndfc_controller ndfc_ctrl; |
| @@ -50,17 +51,14 @@ static void ndfc_select_chip(struct mtd_info *mtd, int chip) | |||
| 50 | { | 51 | { |
| 51 | uint32_t ccr; | 52 | uint32_t ccr; |
| 52 | struct ndfc_controller *ndfc = &ndfc_ctrl; | 53 | struct ndfc_controller *ndfc = &ndfc_ctrl; |
| 53 | struct nand_chip *nandchip = mtd->priv; | ||
| 54 | struct ndfc_nand_mtd *nandmtd = nandchip->priv; | ||
| 55 | struct platform_nand_chip *pchip = nandmtd->pl_chip; | ||
| 56 | 54 | ||
| 57 | ccr = __raw_readl(ndfc->ndfcbase + NDFC_CCR); | 55 | ccr = in_be32(ndfc->ndfcbase + NDFC_CCR); |
| 58 | if (chip >= 0) { | 56 | if (chip >= 0) { |
| 59 | ccr &= ~NDFC_CCR_BS_MASK; | 57 | ccr &= ~NDFC_CCR_BS_MASK; |
| 60 | ccr |= NDFC_CCR_BS(chip + pchip->chip_offset); | 58 | ccr |= NDFC_CCR_BS(chip + ndfc->chip_select); |
| 61 | } else | 59 | } else |
| 62 | ccr |= NDFC_CCR_RESET_CE; | 60 | ccr |= NDFC_CCR_RESET_CE; |
| 63 | __raw_writel(ccr, ndfc->ndfcbase + NDFC_CCR); | 61 | out_be32(ndfc->ndfcbase + NDFC_CCR, ccr); |
| 64 | } | 62 | } |
| 65 | 63 | ||
| 66 | static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) | 64 | static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) |
| @@ -80,7 +78,7 @@ static int ndfc_ready(struct mtd_info *mtd) | |||
| 80 | { | 78 | { |
| 81 | struct ndfc_controller *ndfc = &ndfc_ctrl; | 79 | struct ndfc_controller *ndfc = &ndfc_ctrl; |
| 82 | 80 | ||
| 83 | return __raw_readl(ndfc->ndfcbase + NDFC_STAT) & NDFC_STAT_IS_READY; | 81 | return in_be32(ndfc->ndfcbase + NDFC_STAT) & NDFC_STAT_IS_READY; |
| 84 | } | 82 | } |
| 85 | 83 | ||
| 86 | static void ndfc_enable_hwecc(struct mtd_info *mtd, int mode) | 84 | static void ndfc_enable_hwecc(struct mtd_info *mtd, int mode) |
| @@ -88,9 +86,9 @@ static void ndfc_enable_hwecc(struct mtd_info *mtd, int mode) | |||
| 88 | uint32_t ccr; | 86 | uint32_t ccr; |
| 89 | struct ndfc_controller *ndfc = &ndfc_ctrl; | 87 | struct ndfc_controller *ndfc = &ndfc_ctrl; |
| 90 | 88 | ||
| 91 | ccr = __raw_readl(ndfc->ndfcbase + NDFC_CCR); | 89 | ccr = in_be32(ndfc->ndfcbase + NDFC_CCR); |
| 92 | ccr |= NDFC_CCR_RESET_ECC; | 90 | ccr |= NDFC_CCR_RESET_ECC; |
| 93 | __raw_writel(ccr, ndfc->ndfcbase + NDFC_CCR); | 91 | out_be32(ndfc->ndfcbase + NDFC_CCR, ccr); |
| 94 | wmb(); | 92 | wmb(); |
| 95 | } | 93 | } |
| 96 | 94 | ||
| @@ -102,9 +100,10 @@ static int ndfc_calculate_ecc(struct mtd_info *mtd, | |||
| 102 | uint8_t *p = (uint8_t *)&ecc; | 100 | uint8_t *p = (uint8_t *)&ecc; |
| 103 | 101 | ||
| 104 | wmb(); | 102 | wmb(); |
| 105 | ecc = __raw_readl(ndfc->ndfcbase + NDFC_ECC); | 103 | ecc = in_be32(ndfc->ndfcbase + NDFC_ECC); |
| 106 | ecc_code[0] = p[1]; | 104 | /* The NDFC uses Smart Media (SMC) bytes order */ |
| 107 | ecc_code[1] = p[2]; | 105 | ecc_code[0] = p[2]; |
| 106 | ecc_code[1] = p[1]; | ||
| 108 | ecc_code[2] = p[3]; | 107 | ecc_code[2] = p[3]; |
| 109 | 108 | ||
| 110 | return 0; | 109 | return 0; |
| @@ -123,7 +122,7 @@ static void ndfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) | |||
| 123 | uint32_t *p = (uint32_t *) buf; | 122 | uint32_t *p = (uint32_t *) buf; |
| 124 | 123 | ||
| 125 | for(;len > 0; len -= 4) | 124 | for(;len > 0; len -= 4) |
| 126 | *p++ = __raw_readl(ndfc->ndfcbase + NDFC_DATA); | 125 | *p++ = in_be32(ndfc->ndfcbase + NDFC_DATA); |
| 127 | } | 126 | } |
| 128 | 127 | ||
| 129 | static void ndfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) | 128 | static void ndfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) |
| @@ -132,7 +131,7 @@ static void ndfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) | |||
| 132 | uint32_t *p = (uint32_t *) buf; | 131 | uint32_t *p = (uint32_t *) buf; |
| 133 | 132 | ||
| 134 | for(;len > 0; len -= 4) | 133 | for(;len > 0; len -= 4) |
| 135 | __raw_writel(*p++, ndfc->ndfcbase + NDFC_DATA); | 134 | out_be32(ndfc->ndfcbase + NDFC_DATA, *p++); |
| 136 | } | 135 | } |
| 137 | 136 | ||
| 138 | static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) | 137 | static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) |
| @@ -141,7 +140,7 @@ static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) | |||
| 141 | uint32_t *p = (uint32_t *) buf; | 140 | uint32_t *p = (uint32_t *) buf; |
| 142 | 141 | ||
| 143 | for(;len > 0; len -= 4) | 142 | for(;len > 0; len -= 4) |
| 144 | if (*p++ != __raw_readl(ndfc->ndfcbase + NDFC_DATA)) | 143 | if (*p++ != in_be32(ndfc->ndfcbase + NDFC_DATA)) |
| 145 | return -EFAULT; | 144 | return -EFAULT; |
| 146 | return 0; | 145 | return 0; |
| 147 | } | 146 | } |
| @@ -149,10 +148,19 @@ static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) | |||
| 149 | /* | 148 | /* |
| 150 | * Initialize chip structure | 149 | * Initialize chip structure |
| 151 | */ | 150 | */ |
| 152 | static void ndfc_chip_init(struct ndfc_nand_mtd *mtd) | 151 | static int ndfc_chip_init(struct ndfc_controller *ndfc, |
| 152 | struct device_node *node) | ||
| 153 | { | 153 | { |
| 154 | struct ndfc_controller *ndfc = &ndfc_ctrl; | 154 | #ifdef CONFIG_MTD_PARTITIONS |
| 155 | struct nand_chip *chip = &mtd->chip; | 155 | #ifdef CONFIG_MTD_CMDLINE_PARTS |
| 156 | static const char *part_types[] = { "cmdlinepart", NULL }; | ||
| 157 | #else | ||
| 158 | static const char *part_types[] = { NULL }; | ||
| 159 | #endif | ||
| 160 | #endif | ||
| 161 | struct device_node *flash_np; | ||
| 162 | struct nand_chip *chip = &ndfc->chip; | ||
| 163 | int ret; | ||
| 156 | 164 | ||
| 157 | chip->IO_ADDR_R = ndfc->ndfcbase + NDFC_DATA; | 165 | chip->IO_ADDR_R = ndfc->ndfcbase + NDFC_DATA; |
| 158 | chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_DATA; | 166 | chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_DATA; |
| @@ -160,8 +168,6 @@ static void ndfc_chip_init(struct ndfc_nand_mtd *mtd) | |||
| 160 | chip->dev_ready = ndfc_ready; | 168 | chip->dev_ready = ndfc_ready; |
| 161 | chip->select_chip = ndfc_select_chip; | 169 | chip->select_chip = ndfc_select_chip; |
| 162 | chip->chip_delay = 50; | 170 | chip->chip_delay = 50; |
| 163 | chip->priv = mtd; | ||
| 164 | chip->options = mtd->pl_chip->options; | ||
| 165 | chip->controller = &ndfc->ndfc_control; | 171 | chip->controller = &ndfc->ndfc_control; |
| 166 | chip->read_buf = ndfc_read_buf; | 172 | chip->read_buf = ndfc_read_buf; |
| 167 | chip->write_buf = ndfc_write_buf; | 173 | chip->write_buf = ndfc_write_buf; |
| @@ -172,143 +178,136 @@ static void ndfc_chip_init(struct ndfc_nand_mtd *mtd) | |||
| 172 | chip->ecc.mode = NAND_ECC_HW; | 178 | chip->ecc.mode = NAND_ECC_HW; |
| 173 | chip->ecc.size = 256; | 179 | chip->ecc.size = 256; |
| 174 | chip->ecc.bytes = 3; | 180 | chip->ecc.bytes = 3; |
| 175 | chip->ecclayout = chip->ecc.layout = mtd->pl_chip->ecclayout; | ||
| 176 | mtd->mtd.priv = chip; | ||
| 177 | mtd->mtd.owner = THIS_MODULE; | ||
| 178 | } | ||
| 179 | |||
| 180 | static int ndfc_chip_probe(struct platform_device *pdev) | ||
| 181 | { | ||
| 182 | struct platform_nand_chip *nc = pdev->dev.platform_data; | ||
| 183 | struct ndfc_chip_settings *settings = nc->priv; | ||
| 184 | struct ndfc_controller *ndfc = &ndfc_ctrl; | ||
| 185 | struct ndfc_nand_mtd *nandmtd; | ||
| 186 | |||
| 187 | if (nc->chip_offset >= NDFC_MAX_BANKS || nc->nr_chips > NDFC_MAX_BANKS) | ||
| 188 | return -EINVAL; | ||
| 189 | |||
| 190 | /* Set the bank settings */ | ||
| 191 | __raw_writel(settings->bank_settings, | ||
| 192 | ndfc->ndfcbase + NDFC_BCFG0 + (nc->chip_offset << 2)); | ||
| 193 | 181 | ||
| 194 | nandmtd = &ndfc_mtd[pdev->id]; | 182 | ndfc->mtd.priv = chip; |
| 195 | if (nandmtd->pl_chip) | 183 | ndfc->mtd.owner = THIS_MODULE; |
| 196 | return -EBUSY; | ||
| 197 | 184 | ||
| 198 | nandmtd->pl_chip = nc; | 185 | flash_np = of_get_next_child(node, NULL); |
| 199 | ndfc_chip_init(nandmtd); | 186 | if (!flash_np) |
| 200 | |||
| 201 | /* Scan for chips */ | ||
| 202 | if (nand_scan(&nandmtd->mtd, nc->nr_chips)) { | ||
| 203 | nandmtd->pl_chip = NULL; | ||
| 204 | return -ENODEV; | 187 | return -ENODEV; |
| 188 | |||
| 189 | ndfc->mtd.name = kasprintf(GFP_KERNEL, "%s.%s", | ||
| 190 | ndfc->ofdev->dev.bus_id, flash_np->name); | ||
| 191 | if (!ndfc->mtd.name) { | ||
| 192 | ret = -ENOMEM; | ||
| 193 | goto err; | ||
| 205 | } | 194 | } |
| 206 | 195 | ||
| 207 | #ifdef CONFIG_MTD_PARTITIONS | 196 | ret = nand_scan(&ndfc->mtd, 1); |
| 208 | printk("Number of partitions %d\n", nc->nr_partitions); | 197 | if (ret) |
| 209 | if (nc->nr_partitions) { | 198 | goto err; |
| 210 | /* Add the full device, so complete dumps can be made */ | ||
| 211 | add_mtd_device(&nandmtd->mtd); | ||
| 212 | add_mtd_partitions(&nandmtd->mtd, nc->partitions, | ||
| 213 | nc->nr_partitions); | ||
| 214 | 199 | ||
| 215 | } else | 200 | #ifdef CONFIG_MTD_PARTITIONS |
| 216 | #else | 201 | ret = parse_mtd_partitions(&ndfc->mtd, part_types, &ndfc->parts, 0); |
| 217 | add_mtd_device(&nandmtd->mtd); | 202 | if (ret < 0) |
| 203 | goto err; | ||
| 204 | |||
| 205 | #ifdef CONFIG_MTD_OF_PARTS | ||
| 206 | if (ret == 0) { | ||
| 207 | ret = of_mtd_parse_partitions(&ndfc->ofdev->dev, flash_np, | ||
| 208 | &ndfc->parts); | ||
| 209 | if (ret < 0) | ||
| 210 | goto err; | ||
| 211 | } | ||
| 218 | #endif | 212 | #endif |
| 219 | 213 | ||
| 220 | atomic_inc(&ndfc->childs_active); | 214 | if (ret > 0) |
| 221 | return 0; | 215 | ret = add_mtd_partitions(&ndfc->mtd, ndfc->parts, ret); |
| 222 | } | 216 | else |
| 217 | #endif | ||
| 218 | ret = add_mtd_device(&ndfc->mtd); | ||
| 223 | 219 | ||
| 224 | static int ndfc_chip_remove(struct platform_device *pdev) | 220 | err: |
| 225 | { | 221 | of_node_put(flash_np); |
| 226 | return 0; | 222 | if (ret) |
| 223 | kfree(ndfc->mtd.name); | ||
| 224 | return ret; | ||
| 227 | } | 225 | } |
| 228 | 226 | ||
| 229 | static int ndfc_nand_probe(struct platform_device *pdev) | 227 | static int __devinit ndfc_probe(struct of_device *ofdev, |
| 228 | const struct of_device_id *match) | ||
| 230 | { | 229 | { |
| 231 | struct platform_nand_ctrl *nc = pdev->dev.platform_data; | ||
| 232 | struct ndfc_controller_settings *settings = nc->priv; | ||
| 233 | struct resource *res = pdev->resource; | ||
| 234 | struct ndfc_controller *ndfc = &ndfc_ctrl; | 230 | struct ndfc_controller *ndfc = &ndfc_ctrl; |
| 235 | unsigned long long phys = settings->ndfc_erpn | res->start; | 231 | const u32 *reg; |
| 232 | u32 ccr; | ||
| 233 | int err, len; | ||
| 236 | 234 | ||
| 237 | #ifndef CONFIG_PHYS_64BIT | 235 | spin_lock_init(&ndfc->ndfc_control.lock); |
| 238 | ndfc->ndfcbase = ioremap((phys_addr_t)phys, res->end - res->start + 1); | 236 | init_waitqueue_head(&ndfc->ndfc_control.wq); |
| 239 | #else | 237 | ndfc->ofdev = ofdev; |
| 240 | ndfc->ndfcbase = ioremap64(phys, res->end - res->start + 1); | 238 | dev_set_drvdata(&ofdev->dev, ndfc); |
| 241 | #endif | 239 | |
| 240 | /* Read the reg property to get the chip select */ | ||
| 241 | reg = of_get_property(ofdev->node, "reg", &len); | ||
| 242 | if (reg == NULL || len != 12) { | ||
| 243 | dev_err(&ofdev->dev, "unable read reg property (%d)\n", len); | ||
| 244 | return -ENOENT; | ||
| 245 | } | ||
| 246 | ndfc->chip_select = reg[0]; | ||
| 247 | |||
| 248 | ndfc->ndfcbase = of_iomap(ofdev->node, 0); | ||
| 242 | if (!ndfc->ndfcbase) { | 249 | if (!ndfc->ndfcbase) { |
| 243 | printk(KERN_ERR "NDFC: ioremap failed\n"); | 250 | dev_err(&ofdev->dev, "failed to get memory\n"); |
| 244 | return -EIO; | 251 | return -EIO; |
| 245 | } | 252 | } |
| 246 | 253 | ||
| 247 | __raw_writel(settings->ccr_settings, ndfc->ndfcbase + NDFC_CCR); | 254 | ccr = NDFC_CCR_BS(ndfc->chip_select); |
| 248 | 255 | ||
| 249 | spin_lock_init(&ndfc->ndfc_control.lock); | 256 | /* It is ok if ccr does not exist - just default to 0 */ |
| 250 | init_waitqueue_head(&ndfc->ndfc_control.wq); | 257 | reg = of_get_property(ofdev->node, "ccr", NULL); |
| 258 | if (reg) | ||
| 259 | ccr |= *reg; | ||
| 251 | 260 | ||
| 252 | platform_set_drvdata(pdev, ndfc); | 261 | out_be32(ndfc->ndfcbase + NDFC_CCR, ccr); |
| 253 | 262 | ||
| 254 | printk("NDFC NAND Driver initialized. Chip-Rev: 0x%08x\n", | 263 | /* Set the bank settings if given */ |
| 255 | __raw_readl(ndfc->ndfcbase + NDFC_REVID)); | 264 | reg = of_get_property(ofdev->node, "bank-settings", NULL); |
| 265 | if (reg) { | ||
| 266 | int offset = NDFC_BCFG0 + (ndfc->chip_select << 2); | ||
| 267 | out_be32(ndfc->ndfcbase + offset, *reg); | ||
| 268 | } | ||
| 269 | |||
| 270 | err = ndfc_chip_init(ndfc, ofdev->node); | ||
| 271 | if (err) { | ||
| 272 | iounmap(ndfc->ndfcbase); | ||
| 273 | return err; | ||
| 274 | } | ||
| 256 | 275 | ||
| 257 | return 0; | 276 | return 0; |
| 258 | } | 277 | } |
| 259 | 278 | ||
| 260 | static int ndfc_nand_remove(struct platform_device *pdev) | 279 | static int __devexit ndfc_remove(struct of_device *ofdev) |
| 261 | { | 280 | { |
| 262 | struct ndfc_controller *ndfc = platform_get_drvdata(pdev); | 281 | struct ndfc_controller *ndfc = dev_get_drvdata(&ofdev->dev); |
| 263 | 282 | ||
| 264 | if (atomic_read(&ndfc->childs_active)) | 283 | nand_release(&ndfc->mtd); |
| 265 | return -EBUSY; | ||
| 266 | 284 | ||
| 267 | if (ndfc) { | ||
| 268 | platform_set_drvdata(pdev, NULL); | ||
| 269 | iounmap(ndfc_ctrl.ndfcbase); | ||
| 270 | ndfc_ctrl.ndfcbase = NULL; | ||
| 271 | } | ||
| 272 | return 0; | 285 | return 0; |
| 273 | } | 286 | } |
| 274 | 287 | ||
| 275 | /* driver device registration */ | 288 | static const struct of_device_id ndfc_match[] = { |
| 276 | 289 | { .compatible = "ibm,ndfc", }, | |
| 277 | static struct platform_driver ndfc_chip_driver = { | 290 | {} |
| 278 | .probe = ndfc_chip_probe, | ||
| 279 | .remove = ndfc_chip_remove, | ||
| 280 | .driver = { | ||
| 281 | .name = "ndfc-chip", | ||
| 282 | .owner = THIS_MODULE, | ||
| 283 | }, | ||
| 284 | }; | 291 | }; |
| 292 | MODULE_DEVICE_TABLE(of, ndfc_match); | ||
| 285 | 293 | ||
| 286 | static struct platform_driver ndfc_nand_driver = { | 294 | static struct of_platform_driver ndfc_driver = { |
| 287 | .probe = ndfc_nand_probe, | 295 | .driver = { |
| 288 | .remove = ndfc_nand_remove, | 296 | .name = "ndfc", |
| 289 | .driver = { | ||
| 290 | .name = "ndfc-nand", | ||
| 291 | .owner = THIS_MODULE, | ||
| 292 | }, | 297 | }, |
| 298 | .match_table = ndfc_match, | ||
| 299 | .probe = ndfc_probe, | ||
| 300 | .remove = __devexit_p(ndfc_remove), | ||
| 293 | }; | 301 | }; |
| 294 | 302 | ||
| 295 | static int __init ndfc_nand_init(void) | 303 | static int __init ndfc_nand_init(void) |
| 296 | { | 304 | { |
| 297 | int ret; | 305 | return of_register_platform_driver(&ndfc_driver); |
| 298 | |||
| 299 | spin_lock_init(&ndfc_ctrl.ndfc_control.lock); | ||
| 300 | init_waitqueue_head(&ndfc_ctrl.ndfc_control.wq); | ||
| 301 | |||
| 302 | ret = platform_driver_register(&ndfc_nand_driver); | ||
| 303 | if (!ret) | ||
| 304 | ret = platform_driver_register(&ndfc_chip_driver); | ||
| 305 | return ret; | ||
| 306 | } | 306 | } |
| 307 | 307 | ||
| 308 | static void __exit ndfc_nand_exit(void) | 308 | static void __exit ndfc_nand_exit(void) |
| 309 | { | 309 | { |
| 310 | platform_driver_unregister(&ndfc_chip_driver); | 310 | of_unregister_platform_driver(&ndfc_driver); |
| 311 | platform_driver_unregister(&ndfc_nand_driver); | ||
| 312 | } | 311 | } |
| 313 | 312 | ||
| 314 | module_init(ndfc_nand_init); | 313 | module_init(ndfc_nand_init); |
| @@ -316,6 +315,4 @@ module_exit(ndfc_nand_exit); | |||
| 316 | 315 | ||
| 317 | MODULE_LICENSE("GPL"); | 316 | MODULE_LICENSE("GPL"); |
| 318 | MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>"); | 317 | MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>"); |
| 319 | MODULE_DESCRIPTION("Platform driver for NDFC"); | 318 | MODULE_DESCRIPTION("OF Platform driver for NDFC"); |
| 320 | MODULE_ALIAS("platform:ndfc-chip"); | ||
| 321 | MODULE_ALIAS("platform:ndfc-nand"); | ||
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index fc4144495610..cc55cbc2b308 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c | |||
| @@ -298,7 +298,7 @@ static struct pxa3xx_nand_flash *builtin_flash_types[] = { | |||
| 298 | #define NDTR1_tAR(c) (min((c), 15) << 0) | 298 | #define NDTR1_tAR(c) (min((c), 15) << 0) |
| 299 | 299 | ||
| 300 | /* convert nano-seconds to nand flash controller clock cycles */ | 300 | /* convert nano-seconds to nand flash controller clock cycles */ |
| 301 | #define ns2cycle(ns, clk) (int)(((ns) * (clk / 1000000) / 1000) + 1) | 301 | #define ns2cycle(ns, clk) (int)(((ns) * (clk / 1000000) / 1000) - 1) |
| 302 | 302 | ||
| 303 | static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info, | 303 | static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info, |
| 304 | const struct pxa3xx_nand_timing *t) | 304 | const struct pxa3xx_nand_timing *t) |
| @@ -368,14 +368,14 @@ static int prepare_read_prog_cmd(struct pxa3xx_nand_info *info, | |||
| 368 | /* large block, 2 cycles for column address | 368 | /* large block, 2 cycles for column address |
| 369 | * row address starts from 3rd cycle | 369 | * row address starts from 3rd cycle |
| 370 | */ | 370 | */ |
| 371 | info->ndcb1 |= (page_addr << 16) | (column & 0xffff); | 371 | info->ndcb1 |= page_addr << 16; |
| 372 | if (info->row_addr_cycles == 3) | 372 | if (info->row_addr_cycles == 3) |
| 373 | info->ndcb2 = (page_addr >> 16) & 0xff; | 373 | info->ndcb2 = (page_addr >> 16) & 0xff; |
| 374 | } else | 374 | } else |
| 375 | /* small block, 1 cycles for column address | 375 | /* small block, 1 cycles for column address |
| 376 | * row address starts from 2nd cycle | 376 | * row address starts from 2nd cycle |
| 377 | */ | 377 | */ |
| 378 | info->ndcb1 = (page_addr << 8) | (column & 0xff); | 378 | info->ndcb1 = page_addr << 8; |
| 379 | 379 | ||
| 380 | if (cmd == cmdset->program) | 380 | if (cmd == cmdset->program) |
| 381 | info->ndcb0 |= NDCB0_CMD_TYPE(1) | NDCB0_AUTO_RS; | 381 | info->ndcb0 |= NDCB0_CMD_TYPE(1) | NDCB0_AUTO_RS; |
diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c index 30a518e211bd..54ec7542a7b7 100644 --- a/drivers/mtd/nand/sharpsl.c +++ b/drivers/mtd/nand/sharpsl.c | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | * drivers/mtd/nand/sharpsl.c | 2 | * drivers/mtd/nand/sharpsl.c |
| 3 | * | 3 | * |
| 4 | * Copyright (C) 2004 Richard Purdie | 4 | * Copyright (C) 2004 Richard Purdie |
| 5 | * Copyright (C) 2008 Dmitry Baryshkov | ||
| 5 | * | 6 | * |
| 6 | * Based on Sharp's NAND driver sharp_sl.c | 7 | * Based on Sharp's NAND driver sharp_sl.c |
| 7 | * | 8 | * |
| @@ -19,22 +20,31 @@ | |||
| 19 | #include <linux/mtd/nand.h> | 20 | #include <linux/mtd/nand.h> |
| 20 | #include <linux/mtd/nand_ecc.h> | 21 | #include <linux/mtd/nand_ecc.h> |
| 21 | #include <linux/mtd/partitions.h> | 22 | #include <linux/mtd/partitions.h> |
| 23 | #include <linux/mtd/sharpsl.h> | ||
| 22 | #include <linux/interrupt.h> | 24 | #include <linux/interrupt.h> |
| 25 | #include <linux/platform_device.h> | ||
| 26 | |||
| 23 | #include <asm/io.h> | 27 | #include <asm/io.h> |
| 24 | #include <mach/hardware.h> | 28 | #include <mach/hardware.h> |
| 25 | #include <asm/mach-types.h> | 29 | #include <asm/mach-types.h> |
| 26 | 30 | ||
| 27 | static void __iomem *sharpsl_io_base; | 31 | struct sharpsl_nand { |
| 28 | static int sharpsl_phys_base = 0x0C000000; | 32 | struct mtd_info mtd; |
| 33 | struct nand_chip chip; | ||
| 34 | |||
| 35 | void __iomem *io; | ||
| 36 | }; | ||
| 37 | |||
| 38 | #define mtd_to_sharpsl(_mtd) container_of(_mtd, struct sharpsl_nand, mtd) | ||
| 29 | 39 | ||
| 30 | /* register offset */ | 40 | /* register offset */ |
| 31 | #define ECCLPLB sharpsl_io_base+0x00 /* line parity 7 - 0 bit */ | 41 | #define ECCLPLB 0x00 /* line parity 7 - 0 bit */ |
| 32 | #define ECCLPUB sharpsl_io_base+0x04 /* line parity 15 - 8 bit */ | 42 | #define ECCLPUB 0x04 /* line parity 15 - 8 bit */ |
| 33 | #define ECCCP sharpsl_io_base+0x08 /* column parity 5 - 0 bit */ | 43 | #define ECCCP 0x08 /* column parity 5 - 0 bit */ |
| 34 | #define ECCCNTR sharpsl_io_base+0x0C /* ECC byte counter */ | 44 | #define ECCCNTR 0x0C /* ECC byte counter */ |
| 35 | #define ECCCLRR sharpsl_io_base+0x10 /* cleare ECC */ | 45 | #define ECCCLRR 0x10 /* cleare ECC */ |
| 36 | #define FLASHIO sharpsl_io_base+0x14 /* Flash I/O */ | 46 | #define FLASHIO 0x14 /* Flash I/O */ |
| 37 | #define FLASHCTL sharpsl_io_base+0x18 /* Flash Control */ | 47 | #define FLASHCTL 0x18 /* Flash Control */ |
| 38 | 48 | ||
| 39 | /* Flash control bit */ | 49 | /* Flash control bit */ |
| 40 | #define FLRYBY (1 << 5) | 50 | #define FLRYBY (1 << 5) |
| @@ -45,35 +55,6 @@ static int sharpsl_phys_base = 0x0C000000; | |||
| 45 | #define FLCE0 (1 << 0) | 55 | #define FLCE0 (1 << 0) |
| 46 | 56 | ||
| 47 | /* | 57 | /* |
| 48 | * MTD structure for SharpSL | ||
| 49 | */ | ||
| 50 | static struct mtd_info *sharpsl_mtd = NULL; | ||
| 51 | |||
| 52 | /* | ||
| 53 | * Define partitions for flash device | ||
| 54 | */ | ||
| 55 | #define DEFAULT_NUM_PARTITIONS 3 | ||
| 56 | |||
| 57 | static int nr_partitions; | ||
| 58 | static struct mtd_partition sharpsl_nand_default_partition_info[] = { | ||
| 59 | { | ||
| 60 | .name = "System Area", | ||
| 61 | .offset = 0, | ||
| 62 | .size = 7 * 1024 * 1024, | ||
| 63 | }, | ||
| 64 | { | ||
| 65 | .name = "Root Filesystem", | ||
| 66 | .offset = 7 * 1024 * 1024, | ||
| 67 | .size = 30 * 1024 * 1024, | ||
| 68 | }, | ||
| 69 | { | ||
| 70 | .name = "Home Filesystem", | ||
| 71 | .offset = MTDPART_OFS_APPEND, | ||
| 72 | .size = MTDPART_SIZ_FULL, | ||
| 73 | }, | ||
| 74 | }; | ||
| 75 | |||
| 76 | /* | ||
| 77 | * hardware specific access to control-lines | 58 | * hardware specific access to control-lines |
| 78 | * ctrl: | 59 | * ctrl: |
| 79 | * NAND_CNE: bit 0 -> ! bit 0 & 4 | 60 | * NAND_CNE: bit 0 -> ! bit 0 & 4 |
| @@ -84,6 +65,7 @@ static struct mtd_partition sharpsl_nand_default_partition_info[] = { | |||
| 84 | static void sharpsl_nand_hwcontrol(struct mtd_info *mtd, int cmd, | 65 | static void sharpsl_nand_hwcontrol(struct mtd_info *mtd, int cmd, |
| 85 | unsigned int ctrl) | 66 | unsigned int ctrl) |
| 86 | { | 67 | { |
| 68 | struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd); | ||
| 87 | struct nand_chip *chip = mtd->priv; | 69 | struct nand_chip *chip = mtd->priv; |
| 88 | 70 | ||
| 89 | if (ctrl & NAND_CTRL_CHANGE) { | 71 | if (ctrl & NAND_CTRL_CHANGE) { |
| @@ -93,103 +75,97 @@ static void sharpsl_nand_hwcontrol(struct mtd_info *mtd, int cmd, | |||
| 93 | 75 | ||
| 94 | bits ^= 0x11; | 76 | bits ^= 0x11; |
| 95 | 77 | ||
| 96 | writeb((readb(FLASHCTL) & ~0x17) | bits, FLASHCTL); | 78 | writeb((readb(sharpsl->io + FLASHCTL) & ~0x17) | bits, sharpsl->io + FLASHCTL); |
| 97 | } | 79 | } |
| 98 | 80 | ||
| 99 | if (cmd != NAND_CMD_NONE) | 81 | if (cmd != NAND_CMD_NONE) |
| 100 | writeb(cmd, chip->IO_ADDR_W); | 82 | writeb(cmd, chip->IO_ADDR_W); |
| 101 | } | 83 | } |
| 102 | 84 | ||
| 103 | static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; | ||
| 104 | |||
| 105 | static struct nand_bbt_descr sharpsl_bbt = { | ||
| 106 | .options = 0, | ||
| 107 | .offs = 4, | ||
| 108 | .len = 2, | ||
| 109 | .pattern = scan_ff_pattern | ||
| 110 | }; | ||
| 111 | |||
| 112 | static struct nand_bbt_descr sharpsl_akita_bbt = { | ||
| 113 | .options = 0, | ||
| 114 | .offs = 4, | ||
| 115 | .len = 1, | ||
| 116 | .pattern = scan_ff_pattern | ||
| 117 | }; | ||
| 118 | |||
| 119 | static struct nand_ecclayout akita_oobinfo = { | ||
| 120 | .eccbytes = 24, | ||
| 121 | .eccpos = { | ||
| 122 | 0x5, 0x1, 0x2, 0x3, 0x6, 0x7, 0x15, 0x11, | ||
| 123 | 0x12, 0x13, 0x16, 0x17, 0x25, 0x21, 0x22, 0x23, | ||
| 124 | 0x26, 0x27, 0x35, 0x31, 0x32, 0x33, 0x36, 0x37}, | ||
| 125 | .oobfree = {{0x08, 0x09}} | ||
| 126 | }; | ||
| 127 | |||
| 128 | static int sharpsl_nand_dev_ready(struct mtd_info *mtd) | 85 | static int sharpsl_nand_dev_ready(struct mtd_info *mtd) |
| 129 | { | 86 | { |
| 130 | return !((readb(FLASHCTL) & FLRYBY) == 0); | 87 | struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd); |
| 88 | return !((readb(sharpsl->io + FLASHCTL) & FLRYBY) == 0); | ||
| 131 | } | 89 | } |
| 132 | 90 | ||
| 133 | static void sharpsl_nand_enable_hwecc(struct mtd_info *mtd, int mode) | 91 | static void sharpsl_nand_enable_hwecc(struct mtd_info *mtd, int mode) |
| 134 | { | 92 | { |
| 135 | writeb(0, ECCCLRR); | 93 | struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd); |
| 94 | writeb(0, sharpsl->io + ECCCLRR); | ||
| 136 | } | 95 | } |
| 137 | 96 | ||
| 138 | static int sharpsl_nand_calculate_ecc(struct mtd_info *mtd, const u_char * dat, u_char * ecc_code) | 97 | static int sharpsl_nand_calculate_ecc(struct mtd_info *mtd, const u_char * dat, u_char * ecc_code) |
| 139 | { | 98 | { |
| 140 | ecc_code[0] = ~readb(ECCLPUB); | 99 | struct sharpsl_nand *sharpsl = mtd_to_sharpsl(mtd); |
| 141 | ecc_code[1] = ~readb(ECCLPLB); | 100 | ecc_code[0] = ~readb(sharpsl->io + ECCLPUB); |
| 142 | ecc_code[2] = (~readb(ECCCP) << 2) | 0x03; | 101 | ecc_code[1] = ~readb(sharpsl->io + ECCLPLB); |
| 143 | return readb(ECCCNTR) != 0; | 102 | ecc_code[2] = (~readb(sharpsl->io + ECCCP) << 2) | 0x03; |
| 103 | return readb(sharpsl->io + ECCCNTR) != 0; | ||
| 144 | } | 104 | } |
| 145 | 105 | ||
| 146 | #ifdef CONFIG_MTD_PARTITIONS | 106 | #ifdef CONFIG_MTD_PARTITIONS |
| 147 | const char *part_probes[] = { "cmdlinepart", NULL }; | 107 | static const char *part_probes[] = { "cmdlinepart", NULL }; |
| 148 | #endif | 108 | #endif |
| 149 | 109 | ||
| 150 | /* | 110 | /* |
| 151 | * Main initialization routine | 111 | * Main initialization routine |
| 152 | */ | 112 | */ |
| 153 | static int __init sharpsl_nand_init(void) | 113 | static int __devinit sharpsl_nand_probe(struct platform_device *pdev) |
| 154 | { | 114 | { |
| 155 | struct nand_chip *this; | 115 | struct nand_chip *this; |
| 116 | #ifdef CONFIG_MTD_PARTITIONS | ||
| 156 | struct mtd_partition *sharpsl_partition_info; | 117 | struct mtd_partition *sharpsl_partition_info; |
| 118 | int nr_partitions; | ||
| 119 | #endif | ||
| 120 | struct resource *r; | ||
| 157 | int err = 0; | 121 | int err = 0; |
| 122 | struct sharpsl_nand *sharpsl; | ||
| 123 | struct sharpsl_nand_platform_data *data = pdev->dev.platform_data; | ||
| 124 | |||
| 125 | if (!data) { | ||
| 126 | dev_err(&pdev->dev, "no platform data!\n"); | ||
| 127 | return -EINVAL; | ||
| 128 | } | ||
| 158 | 129 | ||
| 159 | /* Allocate memory for MTD device structure and private data */ | 130 | /* Allocate memory for MTD device structure and private data */ |
| 160 | sharpsl_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); | 131 | sharpsl = kzalloc(sizeof(struct sharpsl_nand), GFP_KERNEL); |
| 161 | if (!sharpsl_mtd) { | 132 | if (!sharpsl) { |
| 162 | printk("Unable to allocate SharpSL NAND MTD device structure.\n"); | 133 | printk("Unable to allocate SharpSL NAND MTD device structure.\n"); |
| 163 | return -ENOMEM; | 134 | return -ENOMEM; |
| 164 | } | 135 | } |
| 165 | 136 | ||
| 137 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 138 | if (!r) { | ||
| 139 | dev_err(&pdev->dev, "no io memory resource defined!\n"); | ||
| 140 | err = -ENODEV; | ||
| 141 | goto err_get_res; | ||
| 142 | } | ||
| 143 | |||
| 166 | /* map physical address */ | 144 | /* map physical address */ |
| 167 | sharpsl_io_base = ioremap(sharpsl_phys_base, 0x1000); | 145 | sharpsl->io = ioremap(r->start, resource_size(r)); |
| 168 | if (!sharpsl_io_base) { | 146 | if (!sharpsl->io) { |
| 169 | printk("ioremap to access Sharp SL NAND chip failed\n"); | 147 | printk("ioremap to access Sharp SL NAND chip failed\n"); |
| 170 | kfree(sharpsl_mtd); | 148 | err = -EIO; |
| 171 | return -EIO; | 149 | goto err_ioremap; |
| 172 | } | 150 | } |
| 173 | 151 | ||
| 174 | /* Get pointer to private data */ | 152 | /* Get pointer to private data */ |
| 175 | this = (struct nand_chip *)(&sharpsl_mtd[1]); | 153 | this = (struct nand_chip *)(&sharpsl->chip); |
| 176 | |||
| 177 | /* Initialize structures */ | ||
| 178 | memset(sharpsl_mtd, 0, sizeof(struct mtd_info)); | ||
| 179 | memset(this, 0, sizeof(struct nand_chip)); | ||
| 180 | 154 | ||
| 181 | /* Link the private data with the MTD structure */ | 155 | /* Link the private data with the MTD structure */ |
| 182 | sharpsl_mtd->priv = this; | 156 | sharpsl->mtd.priv = this; |
| 183 | sharpsl_mtd->owner = THIS_MODULE; | 157 | sharpsl->mtd.owner = THIS_MODULE; |
| 158 | |||
| 159 | platform_set_drvdata(pdev, sharpsl); | ||
| 184 | 160 | ||
| 185 | /* | 161 | /* |
| 186 | * PXA initialize | 162 | * PXA initialize |
| 187 | */ | 163 | */ |
| 188 | writeb(readb(FLASHCTL) | FLWP, FLASHCTL); | 164 | writeb(readb(sharpsl->io + FLASHCTL) | FLWP, sharpsl->io + FLASHCTL); |
| 189 | 165 | ||
| 190 | /* Set address of NAND IO lines */ | 166 | /* Set address of NAND IO lines */ |
| 191 | this->IO_ADDR_R = FLASHIO; | 167 | this->IO_ADDR_R = sharpsl->io + FLASHIO; |
| 192 | this->IO_ADDR_W = FLASHIO; | 168 | this->IO_ADDR_W = sharpsl->io + FLASHIO; |
| 193 | /* Set address of hardware control function */ | 169 | /* Set address of hardware control function */ |
| 194 | this->cmd_ctrl = sharpsl_nand_hwcontrol; | 170 | this->cmd_ctrl = sharpsl_nand_hwcontrol; |
| 195 | this->dev_ready = sharpsl_nand_dev_ready; | 171 | this->dev_ready = sharpsl_nand_dev_ready; |
| @@ -199,68 +175,89 @@ static int __init sharpsl_nand_init(void) | |||
| 199 | this->ecc.mode = NAND_ECC_HW; | 175 | this->ecc.mode = NAND_ECC_HW; |
| 200 | this->ecc.size = 256; | 176 | this->ecc.size = 256; |
| 201 | this->ecc.bytes = 3; | 177 | this->ecc.bytes = 3; |
| 202 | this->badblock_pattern = &sharpsl_bbt; | 178 | this->badblock_pattern = data->badblock_pattern; |
| 203 | if (machine_is_akita() || machine_is_borzoi()) { | 179 | this->ecc.layout = data->ecc_layout; |
| 204 | this->badblock_pattern = &sharpsl_akita_bbt; | ||
| 205 | this->ecc.layout = &akita_oobinfo; | ||
| 206 | } | ||
| 207 | this->ecc.hwctl = sharpsl_nand_enable_hwecc; | 180 | this->ecc.hwctl = sharpsl_nand_enable_hwecc; |
| 208 | this->ecc.calculate = sharpsl_nand_calculate_ecc; | 181 | this->ecc.calculate = sharpsl_nand_calculate_ecc; |
| 209 | this->ecc.correct = nand_correct_data; | 182 | this->ecc.correct = nand_correct_data; |
| 210 | 183 | ||
| 211 | /* Scan to find existence of the device */ | 184 | /* Scan to find existence of the device */ |
| 212 | err = nand_scan(sharpsl_mtd, 1); | 185 | err = nand_scan(&sharpsl->mtd, 1); |
| 213 | if (err) { | 186 | if (err) |
| 214 | iounmap(sharpsl_io_base); | 187 | goto err_scan; |
| 215 | kfree(sharpsl_mtd); | ||
| 216 | return err; | ||
| 217 | } | ||
| 218 | 188 | ||
| 219 | /* Register the partitions */ | 189 | /* Register the partitions */ |
| 220 | sharpsl_mtd->name = "sharpsl-nand"; | 190 | sharpsl->mtd.name = "sharpsl-nand"; |
| 221 | nr_partitions = parse_mtd_partitions(sharpsl_mtd, part_probes, &sharpsl_partition_info, 0); | 191 | #ifdef CONFIG_MTD_PARTITIONS |
| 222 | 192 | nr_partitions = parse_mtd_partitions(&sharpsl->mtd, part_probes, &sharpsl_partition_info, 0); | |
| 223 | if (nr_partitions <= 0) { | 193 | if (nr_partitions <= 0) { |
| 224 | nr_partitions = DEFAULT_NUM_PARTITIONS; | 194 | nr_partitions = data->nr_partitions; |
| 225 | sharpsl_partition_info = sharpsl_nand_default_partition_info; | 195 | sharpsl_partition_info = data->partitions; |
| 226 | if (machine_is_poodle()) { | ||
| 227 | sharpsl_partition_info[1].size = 22 * 1024 * 1024; | ||
| 228 | } else if (machine_is_corgi() || machine_is_shepherd()) { | ||
| 229 | sharpsl_partition_info[1].size = 25 * 1024 * 1024; | ||
| 230 | } else if (machine_is_husky()) { | ||
| 231 | sharpsl_partition_info[1].size = 53 * 1024 * 1024; | ||
| 232 | } else if (machine_is_spitz()) { | ||
| 233 | sharpsl_partition_info[1].size = 5 * 1024 * 1024; | ||
| 234 | } else if (machine_is_akita()) { | ||
| 235 | sharpsl_partition_info[1].size = 58 * 1024 * 1024; | ||
| 236 | } else if (machine_is_borzoi()) { | ||
| 237 | sharpsl_partition_info[1].size = 32 * 1024 * 1024; | ||
| 238 | } | ||
| 239 | } | 196 | } |
| 240 | 197 | ||
| 241 | add_mtd_partitions(sharpsl_mtd, sharpsl_partition_info, nr_partitions); | 198 | if (nr_partitions > 0) |
| 199 | err = add_mtd_partitions(&sharpsl->mtd, sharpsl_partition_info, nr_partitions); | ||
| 200 | else | ||
| 201 | #endif | ||
| 202 | err = add_mtd_device(&sharpsl->mtd); | ||
| 203 | if (err) | ||
| 204 | goto err_add; | ||
| 242 | 205 | ||
| 243 | /* Return happy */ | 206 | /* Return happy */ |
| 244 | return 0; | 207 | return 0; |
| 245 | } | ||
| 246 | 208 | ||
| 247 | module_init(sharpsl_nand_init); | 209 | err_add: |
| 210 | nand_release(&sharpsl->mtd); | ||
| 211 | |||
| 212 | err_scan: | ||
| 213 | platform_set_drvdata(pdev, NULL); | ||
| 214 | iounmap(sharpsl->io); | ||
| 215 | err_ioremap: | ||
| 216 | err_get_res: | ||
| 217 | kfree(sharpsl); | ||
| 218 | return err; | ||
| 219 | } | ||
| 248 | 220 | ||
| 249 | /* | 221 | /* |
| 250 | * Clean up routine | 222 | * Clean up routine |
| 251 | */ | 223 | */ |
| 252 | static void __exit sharpsl_nand_cleanup(void) | 224 | static int __devexit sharpsl_nand_remove(struct platform_device *pdev) |
| 253 | { | 225 | { |
| 226 | struct sharpsl_nand *sharpsl = platform_get_drvdata(pdev); | ||
| 227 | |||
| 254 | /* Release resources, unregister device */ | 228 | /* Release resources, unregister device */ |
| 255 | nand_release(sharpsl_mtd); | 229 | nand_release(&sharpsl->mtd); |
| 256 | 230 | ||
| 257 | iounmap(sharpsl_io_base); | 231 | platform_set_drvdata(pdev, NULL); |
| 232 | |||
| 233 | iounmap(sharpsl->io); | ||
| 258 | 234 | ||
| 259 | /* Free the MTD device structure */ | 235 | /* Free the MTD device structure */ |
| 260 | kfree(sharpsl_mtd); | 236 | kfree(sharpsl); |
| 237 | |||
| 238 | return 0; | ||
| 239 | } | ||
| 240 | |||
| 241 | static struct platform_driver sharpsl_nand_driver = { | ||
| 242 | .driver = { | ||
| 243 | .name = "sharpsl-nand", | ||
| 244 | .owner = THIS_MODULE, | ||
| 245 | }, | ||
| 246 | .probe = sharpsl_nand_probe, | ||
| 247 | .remove = __devexit_p(sharpsl_nand_remove), | ||
| 248 | }; | ||
| 249 | |||
| 250 | static int __init sharpsl_nand_init(void) | ||
| 251 | { | ||
| 252 | return platform_driver_register(&sharpsl_nand_driver); | ||
| 261 | } | 253 | } |
| 254 | module_init(sharpsl_nand_init); | ||
| 262 | 255 | ||
| 263 | module_exit(sharpsl_nand_cleanup); | 256 | static void __exit sharpsl_nand_exit(void) |
| 257 | { | ||
| 258 | platform_driver_unregister(&sharpsl_nand_driver); | ||
| 259 | } | ||
| 260 | module_exit(sharpsl_nand_exit); | ||
| 264 | 261 | ||
| 265 | MODULE_LICENSE("GPL"); | 262 | MODULE_LICENSE("GPL"); |
| 266 | MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>"); | 263 | MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>"); |
diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index 320b929abe79..d1c4546513f7 100644 --- a/drivers/mtd/nftlcore.c +++ b/drivers/mtd/nftlcore.c | |||
| @@ -39,7 +39,7 @@ static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) | |||
| 39 | struct NFTLrecord *nftl; | 39 | struct NFTLrecord *nftl; |
| 40 | unsigned long temp; | 40 | unsigned long temp; |
| 41 | 41 | ||
| 42 | if (mtd->type != MTD_NANDFLASH) | 42 | if (mtd->type != MTD_NANDFLASH || mtd->size > UINT_MAX) |
| 43 | return; | 43 | return; |
| 44 | /* OK, this is moderately ugly. But probably safe. Alternatives? */ | 44 | /* OK, this is moderately ugly. But probably safe. Alternatives? */ |
| 45 | if (memcmp(mtd->name, "DiskOnChip", 10)) | 45 | if (memcmp(mtd->name, "DiskOnChip", 10)) |
diff --git a/drivers/mtd/nftlmount.c b/drivers/mtd/nftlmount.c index ccc4f209fbb5..8b22b1836e9f 100644 --- a/drivers/mtd/nftlmount.c +++ b/drivers/mtd/nftlmount.c | |||
| @@ -51,7 +51,7 @@ static int find_boot_record(struct NFTLrecord *nftl) | |||
| 51 | the mtd device accordingly. We could even get rid of | 51 | the mtd device accordingly. We could even get rid of |
| 52 | nftl->EraseSize if there were any point in doing so. */ | 52 | nftl->EraseSize if there were any point in doing so. */ |
| 53 | nftl->EraseSize = nftl->mbd.mtd->erasesize; | 53 | nftl->EraseSize = nftl->mbd.mtd->erasesize; |
| 54 | nftl->nb_blocks = nftl->mbd.mtd->size / nftl->EraseSize; | 54 | nftl->nb_blocks = (u32)nftl->mbd.mtd->size / nftl->EraseSize; |
| 55 | 55 | ||
| 56 | nftl->MediaUnit = BLOCK_NIL; | 56 | nftl->MediaUnit = BLOCK_NIL; |
| 57 | nftl->SpareMediaUnit = BLOCK_NIL; | 57 | nftl->SpareMediaUnit = BLOCK_NIL; |
| @@ -168,7 +168,7 @@ device is already correct. | |||
| 168 | printk(KERN_NOTICE "WARNING: Support for NFTL with UnitSizeFactor 0x%02x is experimental\n", | 168 | printk(KERN_NOTICE "WARNING: Support for NFTL with UnitSizeFactor 0x%02x is experimental\n", |
| 169 | mh->UnitSizeFactor); | 169 | mh->UnitSizeFactor); |
| 170 | nftl->EraseSize = nftl->mbd.mtd->erasesize << (0xff - mh->UnitSizeFactor); | 170 | nftl->EraseSize = nftl->mbd.mtd->erasesize << (0xff - mh->UnitSizeFactor); |
| 171 | nftl->nb_blocks = nftl->mbd.mtd->size / nftl->EraseSize; | 171 | nftl->nb_blocks = (u32)nftl->mbd.mtd->size / nftl->EraseSize; |
| 172 | } | 172 | } |
| 173 | #endif | 173 | #endif |
| 174 | nftl->nb_boot_blocks = le16_to_cpu(mh->FirstPhysicalEUN); | 174 | nftl->nb_boot_blocks = le16_to_cpu(mh->FirstPhysicalEUN); |
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 90ed319f26e6..529af271db17 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c | |||
| @@ -1772,7 +1772,7 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
| 1772 | int len; | 1772 | int len; |
| 1773 | int ret = 0; | 1773 | int ret = 0; |
| 1774 | 1774 | ||
| 1775 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len); | 1775 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%012llx, len = %llu\n", (unsigned long long) instr->addr, (unsigned long long) instr->len); |
| 1776 | 1776 | ||
| 1777 | block_size = (1 << this->erase_shift); | 1777 | block_size = (1 << this->erase_shift); |
| 1778 | 1778 | ||
| @@ -1810,7 +1810,7 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
| 1810 | 1810 | ||
| 1811 | /* Check if we have a bad block, we do not erase bad blocks */ | 1811 | /* Check if we have a bad block, we do not erase bad blocks */ |
| 1812 | if (onenand_block_isbad_nolock(mtd, addr, 0)) { | 1812 | if (onenand_block_isbad_nolock(mtd, addr, 0)) { |
| 1813 | printk (KERN_WARNING "onenand_erase: attempt to erase a bad block at addr 0x%08x\n", (unsigned int) addr); | 1813 | printk (KERN_WARNING "onenand_erase: attempt to erase a bad block at addr 0x%012llx\n", (unsigned long long) addr); |
| 1814 | instr->state = MTD_ERASE_FAILED; | 1814 | instr->state = MTD_ERASE_FAILED; |
| 1815 | goto erase_exit; | 1815 | goto erase_exit; |
| 1816 | } | 1816 | } |
| @@ -2029,7 +2029,7 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int | |||
| 2029 | * | 2029 | * |
| 2030 | * Lock one or more blocks | 2030 | * Lock one or more blocks |
| 2031 | */ | 2031 | */ |
| 2032 | static int onenand_lock(struct mtd_info *mtd, loff_t ofs, size_t len) | 2032 | static int onenand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) |
| 2033 | { | 2033 | { |
| 2034 | int ret; | 2034 | int ret; |
| 2035 | 2035 | ||
| @@ -2047,7 +2047,7 @@ static int onenand_lock(struct mtd_info *mtd, loff_t ofs, size_t len) | |||
| 2047 | * | 2047 | * |
| 2048 | * Unlock one or more blocks | 2048 | * Unlock one or more blocks |
| 2049 | */ | 2049 | */ |
| 2050 | static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) | 2050 | static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) |
| 2051 | { | 2051 | { |
| 2052 | int ret; | 2052 | int ret; |
| 2053 | 2053 | ||
diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c index e538c0a72abb..d2aa9c46530f 100644 --- a/drivers/mtd/rfd_ftl.c +++ b/drivers/mtd/rfd_ftl.c | |||
| @@ -21,8 +21,6 @@ | |||
| 21 | 21 | ||
| 22 | #include <asm/types.h> | 22 | #include <asm/types.h> |
| 23 | 23 | ||
| 24 | #define const_cpu_to_le16 __constant_cpu_to_le16 | ||
| 25 | |||
| 26 | static int block_size = 0; | 24 | static int block_size = 0; |
| 27 | module_param(block_size, int, 0); | 25 | module_param(block_size, int, 0); |
| 28 | MODULE_PARM_DESC(block_size, "Block size to use by RFD, defaults to erase unit size"); | 26 | MODULE_PARM_DESC(block_size, "Block size to use by RFD, defaults to erase unit size"); |
| @@ -156,7 +154,7 @@ static int scan_header(struct partition *part) | |||
| 156 | size_t retlen; | 154 | size_t retlen; |
| 157 | 155 | ||
| 158 | sectors_per_block = part->block_size / SECTOR_SIZE; | 156 | sectors_per_block = part->block_size / SECTOR_SIZE; |
| 159 | part->total_blocks = part->mbd.mtd->size / part->block_size; | 157 | part->total_blocks = (u32)part->mbd.mtd->size / part->block_size; |
| 160 | 158 | ||
| 161 | if (part->total_blocks < 2) | 159 | if (part->total_blocks < 2) |
| 162 | return -ENOENT; | 160 | return -ENOENT; |
| @@ -276,16 +274,17 @@ static void erase_callback(struct erase_info *erase) | |||
| 276 | 274 | ||
| 277 | part = (struct partition*)erase->priv; | 275 | part = (struct partition*)erase->priv; |
| 278 | 276 | ||
| 279 | i = erase->addr / part->block_size; | 277 | i = (u32)erase->addr / part->block_size; |
| 280 | if (i >= part->total_blocks || part->blocks[i].offset != erase->addr) { | 278 | if (i >= part->total_blocks || part->blocks[i].offset != erase->addr || |
| 281 | printk(KERN_ERR PREFIX "erase callback for unknown offset %x " | 279 | erase->addr > UINT_MAX) { |
| 282 | "on '%s'\n", erase->addr, part->mbd.mtd->name); | 280 | printk(KERN_ERR PREFIX "erase callback for unknown offset %llx " |
| 281 | "on '%s'\n", (unsigned long long)erase->addr, part->mbd.mtd->name); | ||
| 283 | return; | 282 | return; |
| 284 | } | 283 | } |
| 285 | 284 | ||
| 286 | if (erase->state != MTD_ERASE_DONE) { | 285 | if (erase->state != MTD_ERASE_DONE) { |
| 287 | printk(KERN_WARNING PREFIX "erase failed at 0x%x on '%s', " | 286 | printk(KERN_WARNING PREFIX "erase failed at 0x%llx on '%s', " |
| 288 | "state %d\n", erase->addr, | 287 | "state %d\n", (unsigned long long)erase->addr, |
| 289 | part->mbd.mtd->name, erase->state); | 288 | part->mbd.mtd->name, erase->state); |
| 290 | 289 | ||
| 291 | part->blocks[i].state = BLOCK_FAILED; | 290 | part->blocks[i].state = BLOCK_FAILED; |
| @@ -297,7 +296,7 @@ static void erase_callback(struct erase_info *erase) | |||
| 297 | return; | 296 | return; |
| 298 | } | 297 | } |
| 299 | 298 | ||
| 300 | magic = const_cpu_to_le16(RFD_MAGIC); | 299 | magic = cpu_to_le16(RFD_MAGIC); |
| 301 | 300 | ||
| 302 | part->blocks[i].state = BLOCK_ERASED; | 301 | part->blocks[i].state = BLOCK_ERASED; |
| 303 | part->blocks[i].free_sectors = part->data_sectors_per_block; | 302 | part->blocks[i].free_sectors = part->data_sectors_per_block; |
| @@ -345,9 +344,9 @@ static int erase_block(struct partition *part, int block) | |||
| 345 | rc = part->mbd.mtd->erase(part->mbd.mtd, erase); | 344 | rc = part->mbd.mtd->erase(part->mbd.mtd, erase); |
| 346 | 345 | ||
| 347 | if (rc) { | 346 | if (rc) { |
| 348 | printk(KERN_ERR PREFIX "erase of region %x,%x on '%s' " | 347 | printk(KERN_ERR PREFIX "erase of region %llx,%llx on '%s' " |
| 349 | "failed\n", erase->addr, erase->len, | 348 | "failed\n", (unsigned long long)erase->addr, |
| 350 | part->mbd.mtd->name); | 349 | (unsigned long long)erase->len, part->mbd.mtd->name); |
| 351 | kfree(erase); | 350 | kfree(erase); |
| 352 | } | 351 | } |
| 353 | 352 | ||
| @@ -587,7 +586,7 @@ static int mark_sector_deleted(struct partition *part, u_long old_addr) | |||
| 587 | int block, offset, rc; | 586 | int block, offset, rc; |
| 588 | u_long addr; | 587 | u_long addr; |
| 589 | size_t retlen; | 588 | size_t retlen; |
| 590 | u16 del = const_cpu_to_le16(SECTOR_DELETED); | 589 | u16 del = cpu_to_le16(SECTOR_DELETED); |
| 591 | 590 | ||
| 592 | block = old_addr / part->block_size; | 591 | block = old_addr / part->block_size; |
| 593 | offset = (old_addr % part->block_size) / SECTOR_SIZE - | 592 | offset = (old_addr % part->block_size) / SECTOR_SIZE - |
| @@ -763,7 +762,7 @@ static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) | |||
| 763 | { | 762 | { |
| 764 | struct partition *part; | 763 | struct partition *part; |
| 765 | 764 | ||
| 766 | if (mtd->type != MTD_NORFLASH) | 765 | if (mtd->type != MTD_NORFLASH || mtd->size > UINT_MAX) |
| 767 | return; | 766 | return; |
| 768 | 767 | ||
| 769 | part = kzalloc(sizeof(struct partition), GFP_KERNEL); | 768 | part = kzalloc(sizeof(struct partition), GFP_KERNEL); |
diff --git a/drivers/mtd/ssfdc.c b/drivers/mtd/ssfdc.c index 33a5d6ed6f18..3f67e00d98e0 100644 --- a/drivers/mtd/ssfdc.c +++ b/drivers/mtd/ssfdc.c | |||
| @@ -294,7 +294,8 @@ static void ssfdcr_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) | |||
| 294 | int cis_sector; | 294 | int cis_sector; |
| 295 | 295 | ||
| 296 | /* Check for small page NAND flash */ | 296 | /* Check for small page NAND flash */ |
| 297 | if (mtd->type != MTD_NANDFLASH || mtd->oobsize != OOB_SIZE) | 297 | if (mtd->type != MTD_NANDFLASH || mtd->oobsize != OOB_SIZE || |
| 298 | mtd->size > UINT_MAX) | ||
| 298 | return; | 299 | return; |
| 299 | 300 | ||
| 300 | /* Check for SSDFC format by reading CIS/IDI sector */ | 301 | /* Check for SSDFC format by reading CIS/IDI sector */ |
| @@ -316,7 +317,7 @@ static void ssfdcr_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) | |||
| 316 | 317 | ||
| 317 | ssfdc->cis_block = cis_sector / (mtd->erasesize >> SECTOR_SHIFT); | 318 | ssfdc->cis_block = cis_sector / (mtd->erasesize >> SECTOR_SHIFT); |
| 318 | ssfdc->erase_size = mtd->erasesize; | 319 | ssfdc->erase_size = mtd->erasesize; |
| 319 | ssfdc->map_len = mtd->size / mtd->erasesize; | 320 | ssfdc->map_len = (u32)mtd->size / mtd->erasesize; |
| 320 | 321 | ||
| 321 | DEBUG(MTD_DEBUG_LEVEL1, | 322 | DEBUG(MTD_DEBUG_LEVEL1, |
| 322 | "SSFDC_RO: cis_block=%d,erase_size=%d,map_len=%d,n_zones=%d\n", | 323 | "SSFDC_RO: cis_block=%d,erase_size=%d,map_len=%d,n_zones=%d\n", |
| @@ -327,7 +328,7 @@ static void ssfdcr_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) | |||
| 327 | ssfdc->heads = 16; | 328 | ssfdc->heads = 16; |
| 328 | ssfdc->sectors = 32; | 329 | ssfdc->sectors = 32; |
| 329 | get_chs(mtd->size, NULL, &ssfdc->heads, &ssfdc->sectors); | 330 | get_chs(mtd->size, NULL, &ssfdc->heads, &ssfdc->sectors); |
| 330 | ssfdc->cylinders = (unsigned short)((mtd->size >> SECTOR_SHIFT) / | 331 | ssfdc->cylinders = (unsigned short)(((u32)mtd->size >> SECTOR_SHIFT) / |
| 331 | ((long)ssfdc->sectors * (long)ssfdc->heads)); | 332 | ((long)ssfdc->sectors * (long)ssfdc->heads)); |
| 332 | 333 | ||
| 333 | DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: using C:%d H:%d S:%d == %ld sects\n", | 334 | DEBUG(MTD_DEBUG_LEVEL1, "SSFDC_RO: using C:%d H:%d S:%d == %ld sects\n", |
diff --git a/drivers/mtd/tests/Makefile b/drivers/mtd/tests/Makefile new file mode 100644 index 000000000000..c1d501335006 --- /dev/null +++ b/drivers/mtd/tests/Makefile | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | obj-$(CONFIG_MTD_TESTS) += mtd_oobtest.o | ||
| 2 | obj-$(CONFIG_MTD_TESTS) += mtd_pagetest.o | ||
| 3 | obj-$(CONFIG_MTD_TESTS) += mtd_readtest.o | ||
| 4 | obj-$(CONFIG_MTD_TESTS) += mtd_speedtest.o | ||
| 5 | obj-$(CONFIG_MTD_TESTS) += mtd_stresstest.o | ||
| 6 | obj-$(CONFIG_MTD_TESTS) += mtd_subpagetest.o | ||
| 7 | obj-$(CONFIG_MTD_TESTS) += mtd_torturetest.o | ||
diff --git a/drivers/mtd/tests/mtd_oobtest.c b/drivers/mtd/tests/mtd_oobtest.c new file mode 100644 index 000000000000..afbc3f8126db --- /dev/null +++ b/drivers/mtd/tests/mtd_oobtest.c | |||
| @@ -0,0 +1,742 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2006-2008 Nokia Corporation | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify it | ||
| 5 | * under the terms of the GNU General Public License version 2 as published by | ||
| 6 | * the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | * | ||
| 13 | * You should have received a copy of the GNU General Public License along with | ||
| 14 | * this program; see the file COPYING. If not, write to the Free Software | ||
| 15 | * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
| 16 | * | ||
| 17 | * Test OOB read and write on MTD device. | ||
| 18 | * | ||
| 19 | * Author: Adrian Hunter <ext-adrian.hunter@nokia.com> | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <asm/div64.h> | ||
| 23 | #include <linux/init.h> | ||
| 24 | #include <linux/module.h> | ||
| 25 | #include <linux/moduleparam.h> | ||
| 26 | #include <linux/err.h> | ||
| 27 | #include <linux/mtd/mtd.h> | ||
| 28 | #include <linux/sched.h> | ||
| 29 | |||
| 30 | #define PRINT_PREF KERN_INFO "mtd_oobtest: " | ||
| 31 | |||
| 32 | static int dev; | ||
| 33 | module_param(dev, int, S_IRUGO); | ||
| 34 | MODULE_PARM_DESC(dev, "MTD device number to use"); | ||
| 35 | |||
| 36 | static struct mtd_info *mtd; | ||
| 37 | static unsigned char *readbuf; | ||
| 38 | static unsigned char *writebuf; | ||
| 39 | static unsigned char *bbt; | ||
| 40 | |||
| 41 | static int ebcnt; | ||
| 42 | static int pgcnt; | ||
| 43 | static int errcnt; | ||
| 44 | static int use_offset; | ||
| 45 | static int use_len; | ||
| 46 | static int use_len_max; | ||
| 47 | static int vary_offset; | ||
| 48 | static unsigned long next = 1; | ||
| 49 | |||
| 50 | static inline unsigned int simple_rand(void) | ||
| 51 | { | ||
| 52 | next = next * 1103515245 + 12345; | ||
| 53 | return (unsigned int)((next / 65536) % 32768); | ||
| 54 | } | ||
| 55 | |||
| 56 | static inline void simple_srand(unsigned long seed) | ||
| 57 | { | ||
| 58 | next = seed; | ||
| 59 | } | ||
| 60 | |||
| 61 | static void set_random_data(unsigned char *buf, size_t len) | ||
| 62 | { | ||
| 63 | size_t i; | ||
| 64 | |||
| 65 | for (i = 0; i < len; ++i) | ||
| 66 | buf[i] = simple_rand(); | ||
| 67 | } | ||
| 68 | |||
| 69 | static int erase_eraseblock(int ebnum) | ||
| 70 | { | ||
| 71 | int err; | ||
| 72 | struct erase_info ei; | ||
| 73 | loff_t addr = ebnum * mtd->erasesize; | ||
| 74 | |||
| 75 | memset(&ei, 0, sizeof(struct erase_info)); | ||
| 76 | ei.mtd = mtd; | ||
| 77 | ei.addr = addr; | ||
| 78 | ei.len = mtd->erasesize; | ||
| 79 | |||
| 80 | err = mtd->erase(mtd, &ei); | ||
| 81 | if (err) { | ||
| 82 | printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum); | ||
| 83 | return err; | ||
| 84 | } | ||
| 85 | |||
| 86 | if (ei.state == MTD_ERASE_FAILED) { | ||
| 87 | printk(PRINT_PREF "some erase error occurred at EB %d\n", | ||
| 88 | ebnum); | ||
| 89 | return -EIO; | ||
| 90 | } | ||
| 91 | |||
| 92 | return 0; | ||
| 93 | } | ||
| 94 | |||
| 95 | static int erase_whole_device(void) | ||
| 96 | { | ||
| 97 | int err; | ||
| 98 | unsigned int i; | ||
| 99 | |||
| 100 | printk(PRINT_PREF "erasing whole device\n"); | ||
| 101 | for (i = 0; i < ebcnt; ++i) { | ||
| 102 | if (bbt[i]) | ||
| 103 | continue; | ||
| 104 | err = erase_eraseblock(i); | ||
| 105 | if (err) | ||
| 106 | return err; | ||
| 107 | cond_resched(); | ||
| 108 | } | ||
| 109 | printk(PRINT_PREF "erased %u eraseblocks\n", i); | ||
| 110 | return 0; | ||
| 111 | } | ||
| 112 | |||
| 113 | static void do_vary_offset(void) | ||
| 114 | { | ||
| 115 | use_len -= 1; | ||
| 116 | if (use_len < 1) { | ||
| 117 | use_offset += 1; | ||
| 118 | if (use_offset >= use_len_max) | ||
| 119 | use_offset = 0; | ||
| 120 | use_len = use_len_max - use_offset; | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | static int write_eraseblock(int ebnum) | ||
| 125 | { | ||
| 126 | int i; | ||
| 127 | struct mtd_oob_ops ops; | ||
| 128 | int err = 0; | ||
| 129 | loff_t addr = ebnum * mtd->erasesize; | ||
| 130 | |||
| 131 | for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) { | ||
| 132 | set_random_data(writebuf, use_len); | ||
| 133 | ops.mode = MTD_OOB_AUTO; | ||
| 134 | ops.len = 0; | ||
| 135 | ops.retlen = 0; | ||
| 136 | ops.ooblen = use_len; | ||
| 137 | ops.oobretlen = 0; | ||
| 138 | ops.ooboffs = use_offset; | ||
| 139 | ops.datbuf = 0; | ||
| 140 | ops.oobbuf = writebuf; | ||
| 141 | err = mtd->write_oob(mtd, addr, &ops); | ||
| 142 | if (err || ops.oobretlen != use_len) { | ||
| 143 | printk(PRINT_PREF "error: writeoob failed at %#llx\n", | ||
| 144 | (long long)addr); | ||
| 145 | printk(PRINT_PREF "error: use_len %d, use_offset %d\n", | ||
| 146 | use_len, use_offset); | ||
| 147 | errcnt += 1; | ||
| 148 | return err ? err : -1; | ||
| 149 | } | ||
| 150 | if (vary_offset) | ||
| 151 | do_vary_offset(); | ||
| 152 | } | ||
| 153 | |||
| 154 | return err; | ||
| 155 | } | ||
| 156 | |||
| 157 | static int write_whole_device(void) | ||
| 158 | { | ||
| 159 | int err; | ||
| 160 | unsigned int i; | ||
| 161 | |||
| 162 | printk(PRINT_PREF "writing OOBs of whole device\n"); | ||
| 163 | for (i = 0; i < ebcnt; ++i) { | ||
| 164 | if (bbt[i]) | ||
| 165 | continue; | ||
| 166 | err = write_eraseblock(i); | ||
| 167 | if (err) | ||
| 168 | return err; | ||
| 169 | if (i % 256 == 0) | ||
| 170 | printk(PRINT_PREF "written up to eraseblock %u\n", i); | ||
| 171 | cond_resched(); | ||
| 172 | } | ||
| 173 | printk(PRINT_PREF "written %u eraseblocks\n", i); | ||
| 174 | return 0; | ||
| 175 | } | ||
| 176 | |||
| 177 | static int verify_eraseblock(int ebnum) | ||
| 178 | { | ||
| 179 | int i; | ||
| 180 | struct mtd_oob_ops ops; | ||
| 181 | int err = 0; | ||
| 182 | loff_t addr = ebnum * mtd->erasesize; | ||
| 183 | |||
| 184 | for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) { | ||
| 185 | set_random_data(writebuf, use_len); | ||
| 186 | ops.mode = MTD_OOB_AUTO; | ||
| 187 | ops.len = 0; | ||
| 188 | ops.retlen = 0; | ||
| 189 | ops.ooblen = use_len; | ||
| 190 | ops.oobretlen = 0; | ||
| 191 | ops.ooboffs = use_offset; | ||
| 192 | ops.datbuf = 0; | ||
| 193 | ops.oobbuf = readbuf; | ||
| 194 | err = mtd->read_oob(mtd, addr, &ops); | ||
| 195 | if (err || ops.oobretlen != use_len) { | ||
| 196 | printk(PRINT_PREF "error: readoob failed at %#llx\n", | ||
| 197 | (long long)addr); | ||
| 198 | errcnt += 1; | ||
| 199 | return err ? err : -1; | ||
| 200 | } | ||
| 201 | if (memcmp(readbuf, writebuf, use_len)) { | ||
| 202 | printk(PRINT_PREF "error: verify failed at %#llx\n", | ||
| 203 | (long long)addr); | ||
| 204 | errcnt += 1; | ||
| 205 | if (errcnt > 1000) { | ||
| 206 | printk(PRINT_PREF "error: too many errors\n"); | ||
| 207 | return -1; | ||
| 208 | } | ||
| 209 | } | ||
| 210 | if (use_offset != 0 || use_len < mtd->ecclayout->oobavail) { | ||
| 211 | int k; | ||
| 212 | |||
| 213 | ops.mode = MTD_OOB_AUTO; | ||
| 214 | ops.len = 0; | ||
| 215 | ops.retlen = 0; | ||
| 216 | ops.ooblen = mtd->ecclayout->oobavail; | ||
| 217 | ops.oobretlen = 0; | ||
| 218 | ops.ooboffs = 0; | ||
| 219 | ops.datbuf = 0; | ||
| 220 | ops.oobbuf = readbuf; | ||
| 221 | err = mtd->read_oob(mtd, addr, &ops); | ||
| 222 | if (err || ops.oobretlen != mtd->ecclayout->oobavail) { | ||
| 223 | printk(PRINT_PREF "error: readoob failed at " | ||
| 224 | "%#llx\n", (long long)addr); | ||
| 225 | errcnt += 1; | ||
| 226 | return err ? err : -1; | ||
| 227 | } | ||
| 228 | if (memcmp(readbuf + use_offset, writebuf, use_len)) { | ||
| 229 | printk(PRINT_PREF "error: verify failed at " | ||
| 230 | "%#llx\n", (long long)addr); | ||
| 231 | errcnt += 1; | ||
| 232 | if (errcnt > 1000) { | ||
| 233 | printk(PRINT_PREF "error: too many " | ||
| 234 | "errors\n"); | ||
| 235 | return -1; | ||
| 236 | } | ||
| 237 | } | ||
| 238 | for (k = 0; k < use_offset; ++k) | ||
| 239 | if (readbuf[k] != 0xff) { | ||
| 240 | printk(PRINT_PREF "error: verify 0xff " | ||
| 241 | "failed at %#llx\n", | ||
| 242 | (long long)addr); | ||
| 243 | errcnt += 1; | ||
| 244 | if (errcnt > 1000) { | ||
| 245 | printk(PRINT_PREF "error: too " | ||
| 246 | "many errors\n"); | ||
| 247 | return -1; | ||
| 248 | } | ||
| 249 | } | ||
| 250 | for (k = use_offset + use_len; | ||
| 251 | k < mtd->ecclayout->oobavail; ++k) | ||
| 252 | if (readbuf[k] != 0xff) { | ||
| 253 | printk(PRINT_PREF "error: verify 0xff " | ||
| 254 | "failed at %#llx\n", | ||
| 255 | (long long)addr); | ||
| 256 | errcnt += 1; | ||
| 257 | if (errcnt > 1000) { | ||
| 258 | printk(PRINT_PREF "error: too " | ||
| 259 | "many errors\n"); | ||
| 260 | return -1; | ||
| 261 | } | ||
| 262 | } | ||
| 263 | } | ||
| 264 | if (vary_offset) | ||
| 265 | do_vary_offset(); | ||
| 266 | } | ||
| 267 | return err; | ||
| 268 | } | ||
| 269 | |||
| 270 | static int verify_eraseblock_in_one_go(int ebnum) | ||
| 271 | { | ||
| 272 | struct mtd_oob_ops ops; | ||
| 273 | int err = 0; | ||
| 274 | loff_t addr = ebnum * mtd->erasesize; | ||
| 275 | size_t len = mtd->ecclayout->oobavail * pgcnt; | ||
| 276 | |||
| 277 | set_random_data(writebuf, len); | ||
| 278 | ops.mode = MTD_OOB_AUTO; | ||
| 279 | ops.len = 0; | ||
| 280 | ops.retlen = 0; | ||
| 281 | ops.ooblen = len; | ||
| 282 | ops.oobretlen = 0; | ||
| 283 | ops.ooboffs = 0; | ||
| 284 | ops.datbuf = 0; | ||
| 285 | ops.oobbuf = readbuf; | ||
| 286 | err = mtd->read_oob(mtd, addr, &ops); | ||
| 287 | if (err || ops.oobretlen != len) { | ||
| 288 | printk(PRINT_PREF "error: readoob failed at %#llx\n", | ||
| 289 | (long long)addr); | ||
| 290 | errcnt += 1; | ||
| 291 | return err ? err : -1; | ||
| 292 | } | ||
| 293 | if (memcmp(readbuf, writebuf, len)) { | ||
| 294 | printk(PRINT_PREF "error: verify failed at %#llx\n", | ||
| 295 | (long long)addr); | ||
| 296 | errcnt += 1; | ||
| 297 | if (errcnt > 1000) { | ||
| 298 | printk(PRINT_PREF "error: too many errors\n"); | ||
| 299 | return -1; | ||
| 300 | } | ||
| 301 | } | ||
| 302 | |||
| 303 | return err; | ||
| 304 | } | ||
| 305 | |||
| 306 | static int verify_all_eraseblocks(void) | ||
| 307 | { | ||
| 308 | int err; | ||
| 309 | unsigned int i; | ||
| 310 | |||
| 311 | printk(PRINT_PREF "verifying all eraseblocks\n"); | ||
| 312 | for (i = 0; i < ebcnt; ++i) { | ||
| 313 | if (bbt[i]) | ||
| 314 | continue; | ||
| 315 | err = verify_eraseblock(i); | ||
| 316 | if (err) | ||
| 317 | return err; | ||
| 318 | if (i % 256 == 0) | ||
| 319 | printk(PRINT_PREF "verified up to eraseblock %u\n", i); | ||
| 320 | cond_resched(); | ||
| 321 | } | ||
| 322 | printk(PRINT_PREF "verified %u eraseblocks\n", i); | ||
| 323 | return 0; | ||
| 324 | } | ||
| 325 | |||
| 326 | static int is_block_bad(int ebnum) | ||
| 327 | { | ||
| 328 | int ret; | ||
| 329 | loff_t addr = ebnum * mtd->erasesize; | ||
| 330 | |||
| 331 | ret = mtd->block_isbad(mtd, addr); | ||
| 332 | if (ret) | ||
| 333 | printk(PRINT_PREF "block %d is bad\n", ebnum); | ||
| 334 | return ret; | ||
| 335 | } | ||
| 336 | |||
| 337 | static int scan_for_bad_eraseblocks(void) | ||
| 338 | { | ||
| 339 | int i, bad = 0; | ||
| 340 | |||
| 341 | bbt = kmalloc(ebcnt, GFP_KERNEL); | ||
| 342 | if (!bbt) { | ||
| 343 | printk(PRINT_PREF "error: cannot allocate memory\n"); | ||
| 344 | return -ENOMEM; | ||
| 345 | } | ||
| 346 | memset(bbt, 0 , ebcnt); | ||
| 347 | |||
| 348 | printk(PRINT_PREF "scanning for bad eraseblocks\n"); | ||
| 349 | for (i = 0; i < ebcnt; ++i) { | ||
| 350 | bbt[i] = is_block_bad(i) ? 1 : 0; | ||
| 351 | if (bbt[i]) | ||
| 352 | bad += 1; | ||
| 353 | cond_resched(); | ||
| 354 | } | ||
| 355 | printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad); | ||
| 356 | return 0; | ||
| 357 | } | ||
| 358 | |||
| 359 | static int __init mtd_oobtest_init(void) | ||
| 360 | { | ||
| 361 | int err = 0; | ||
| 362 | unsigned int i; | ||
| 363 | uint64_t tmp; | ||
| 364 | struct mtd_oob_ops ops; | ||
| 365 | loff_t addr = 0, addr0; | ||
| 366 | |||
| 367 | printk(KERN_INFO "\n"); | ||
| 368 | printk(KERN_INFO "=================================================\n"); | ||
| 369 | printk(PRINT_PREF "MTD device: %d\n", dev); | ||
| 370 | |||
| 371 | mtd = get_mtd_device(NULL, dev); | ||
| 372 | if (IS_ERR(mtd)) { | ||
| 373 | err = PTR_ERR(mtd); | ||
| 374 | printk(PRINT_PREF "error: cannot get MTD device\n"); | ||
| 375 | return err; | ||
| 376 | } | ||
| 377 | |||
| 378 | if (mtd->type != MTD_NANDFLASH) { | ||
| 379 | printk(PRINT_PREF "this test requires NAND flash\n"); | ||
| 380 | goto out; | ||
| 381 | } | ||
| 382 | |||
| 383 | tmp = mtd->size; | ||
| 384 | do_div(tmp, mtd->erasesize); | ||
| 385 | ebcnt = tmp; | ||
| 386 | pgcnt = mtd->erasesize / mtd->writesize; | ||
| 387 | |||
| 388 | printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, " | ||
| 389 | "page size %u, count of eraseblocks %u, pages per " | ||
| 390 | "eraseblock %u, OOB size %u\n", | ||
| 391 | (unsigned long long)mtd->size, mtd->erasesize, | ||
| 392 | mtd->writesize, ebcnt, pgcnt, mtd->oobsize); | ||
| 393 | |||
| 394 | err = -ENOMEM; | ||
| 395 | mtd->erasesize = mtd->erasesize; | ||
| 396 | readbuf = kmalloc(mtd->erasesize, GFP_KERNEL); | ||
| 397 | if (!readbuf) { | ||
| 398 | printk(PRINT_PREF "error: cannot allocate memory\n"); | ||
| 399 | goto out; | ||
| 400 | } | ||
| 401 | writebuf = kmalloc(mtd->erasesize, GFP_KERNEL); | ||
| 402 | if (!writebuf) { | ||
| 403 | printk(PRINT_PREF "error: cannot allocate memory\n"); | ||
| 404 | goto out; | ||
| 405 | } | ||
| 406 | |||
| 407 | err = scan_for_bad_eraseblocks(); | ||
| 408 | if (err) | ||
| 409 | goto out; | ||
| 410 | |||
| 411 | use_offset = 0; | ||
| 412 | use_len = mtd->ecclayout->oobavail; | ||
| 413 | use_len_max = mtd->ecclayout->oobavail; | ||
| 414 | vary_offset = 0; | ||
| 415 | |||
| 416 | /* First test: write all OOB, read it back and verify */ | ||
| 417 | printk(PRINT_PREF "test 1 of 5\n"); | ||
| 418 | |||
| 419 | err = erase_whole_device(); | ||
| 420 | if (err) | ||
| 421 | goto out; | ||
| 422 | |||
| 423 | simple_srand(1); | ||
| 424 | err = write_whole_device(); | ||
| 425 | if (err) | ||
| 426 | goto out; | ||
| 427 | |||
| 428 | simple_srand(1); | ||
| 429 | err = verify_all_eraseblocks(); | ||
| 430 | if (err) | ||
| 431 | goto out; | ||
| 432 | |||
| 433 | /* | ||
| 434 | * Second test: write all OOB, a block at a time, read it back and | ||
| 435 | * verify. | ||
| 436 | */ | ||
| 437 | printk(PRINT_PREF "test 2 of 5\n"); | ||
| 438 | |||
| 439 | err = erase_whole_device(); | ||
| 440 | if (err) | ||
| 441 | goto out; | ||
| 442 | |||
| 443 | simple_srand(3); | ||
| 444 | err = write_whole_device(); | ||
| 445 | if (err) | ||
| 446 | goto out; | ||
| 447 | |||
| 448 | /* Check all eraseblocks */ | ||
| 449 | simple_srand(3); | ||
| 450 | printk(PRINT_PREF "verifying all eraseblocks\n"); | ||
| 451 | for (i = 0; i < ebcnt; ++i) { | ||
| 452 | if (bbt[i]) | ||
| 453 | continue; | ||
| 454 | err = verify_eraseblock_in_one_go(i); | ||
| 455 | if (err) | ||
| 456 | goto out; | ||
| 457 | if (i % 256 == 0) | ||
| 458 | printk(PRINT_PREF "verified up to eraseblock %u\n", i); | ||
| 459 | cond_resched(); | ||
| 460 | } | ||
| 461 | printk(PRINT_PREF "verified %u eraseblocks\n", i); | ||
| 462 | |||
| 463 | /* | ||
| 464 | * Third test: write OOB at varying offsets and lengths, read it back | ||
| 465 | * and verify. | ||
| 466 | */ | ||
| 467 | printk(PRINT_PREF "test 3 of 5\n"); | ||
| 468 | |||
| 469 | err = erase_whole_device(); | ||
| 470 | if (err) | ||
| 471 | goto out; | ||
| 472 | |||
| 473 | /* Write all eraseblocks */ | ||
| 474 | use_offset = 0; | ||
| 475 | use_len = mtd->ecclayout->oobavail; | ||
| 476 | use_len_max = mtd->ecclayout->oobavail; | ||
| 477 | vary_offset = 1; | ||
| 478 | simple_srand(5); | ||
| 479 | printk(PRINT_PREF "writing OOBs of whole device\n"); | ||
| 480 | for (i = 0; i < ebcnt; ++i) { | ||
| 481 | if (bbt[i]) | ||
| 482 | continue; | ||
| 483 | err = write_eraseblock(i); | ||
| 484 | if (err) | ||
| 485 | goto out; | ||
| 486 | if (i % 256 == 0) | ||
| 487 | printk(PRINT_PREF "written up to eraseblock %u\n", i); | ||
| 488 | cond_resched(); | ||
| 489 | } | ||
| 490 | printk(PRINT_PREF "written %u eraseblocks\n", i); | ||
| 491 | |||
| 492 | /* Check all eraseblocks */ | ||
| 493 | use_offset = 0; | ||
| 494 | use_len = mtd->ecclayout->oobavail; | ||
| 495 | use_len_max = mtd->ecclayout->oobavail; | ||
| 496 | vary_offset = 1; | ||
| 497 | simple_srand(5); | ||
| 498 | err = verify_all_eraseblocks(); | ||
| 499 | if (err) | ||
| 500 | goto out; | ||
| 501 | |||
| 502 | use_offset = 0; | ||
| 503 | use_len = mtd->ecclayout->oobavail; | ||
| 504 | use_len_max = mtd->ecclayout->oobavail; | ||
| 505 | vary_offset = 0; | ||
| 506 | |||
| 507 | /* Fourth test: try to write off end of device */ | ||
| 508 | printk(PRINT_PREF "test 4 of 5\n"); | ||
| 509 | |||
| 510 | err = erase_whole_device(); | ||
| 511 | if (err) | ||
| 512 | goto out; | ||
| 513 | |||
| 514 | addr0 = 0; | ||
| 515 | for (i = 0; bbt[i] && i < ebcnt; ++i) | ||
| 516 | addr0 += mtd->erasesize; | ||
| 517 | |||
| 518 | /* Attempt to write off end of OOB */ | ||
| 519 | ops.mode = MTD_OOB_AUTO; | ||
| 520 | ops.len = 0; | ||
| 521 | ops.retlen = 0; | ||
| 522 | ops.ooblen = 1; | ||
| 523 | ops.oobretlen = 0; | ||
| 524 | ops.ooboffs = mtd->ecclayout->oobavail; | ||
| 525 | ops.datbuf = 0; | ||
| 526 | ops.oobbuf = writebuf; | ||
| 527 | printk(PRINT_PREF "attempting to start write past end of OOB\n"); | ||
| 528 | printk(PRINT_PREF "an error is expected...\n"); | ||
| 529 | err = mtd->write_oob(mtd, addr0, &ops); | ||
| 530 | if (err) { | ||
| 531 | printk(PRINT_PREF "error occurred as expected\n"); | ||
| 532 | err = 0; | ||
| 533 | } else { | ||
| 534 | printk(PRINT_PREF "error: can write past end of OOB\n"); | ||
| 535 | errcnt += 1; | ||
| 536 | } | ||
| 537 | |||
| 538 | /* Attempt to read off end of OOB */ | ||
| 539 | ops.mode = MTD_OOB_AUTO; | ||
| 540 | ops.len = 0; | ||
| 541 | ops.retlen = 0; | ||
| 542 | ops.ooblen = 1; | ||
| 543 | ops.oobretlen = 0; | ||
| 544 | ops.ooboffs = mtd->ecclayout->oobavail; | ||
| 545 | ops.datbuf = 0; | ||
| 546 | ops.oobbuf = readbuf; | ||
| 547 | printk(PRINT_PREF "attempting to start read past end of OOB\n"); | ||
| 548 | printk(PRINT_PREF "an error is expected...\n"); | ||
| 549 | err = mtd->read_oob(mtd, addr0, &ops); | ||
| 550 | if (err) { | ||
| 551 | printk(PRINT_PREF "error occurred as expected\n"); | ||
| 552 | err = 0; | ||
| 553 | } else { | ||
| 554 | printk(PRINT_PREF "error: can read past end of OOB\n"); | ||
| 555 | errcnt += 1; | ||
| 556 | } | ||
| 557 | |||
| 558 | if (bbt[ebcnt - 1]) | ||
| 559 | printk(PRINT_PREF "skipping end of device tests because last " | ||
| 560 | "block is bad\n"); | ||
| 561 | else { | ||
| 562 | /* Attempt to write off end of device */ | ||
| 563 | ops.mode = MTD_OOB_AUTO; | ||
| 564 | ops.len = 0; | ||
| 565 | ops.retlen = 0; | ||
| 566 | ops.ooblen = mtd->ecclayout->oobavail + 1; | ||
| 567 | ops.oobretlen = 0; | ||
| 568 | ops.ooboffs = 0; | ||
| 569 | ops.datbuf = 0; | ||
| 570 | ops.oobbuf = writebuf; | ||
| 571 | printk(PRINT_PREF "attempting to write past end of device\n"); | ||
| 572 | printk(PRINT_PREF "an error is expected...\n"); | ||
| 573 | err = mtd->write_oob(mtd, mtd->size - mtd->writesize, &ops); | ||
| 574 | if (err) { | ||
| 575 | printk(PRINT_PREF "error occurred as expected\n"); | ||
| 576 | err = 0; | ||
| 577 | } else { | ||
| 578 | printk(PRINT_PREF "error: wrote past end of device\n"); | ||
| 579 | errcnt += 1; | ||
| 580 | } | ||
| 581 | |||
| 582 | /* Attempt to read off end of device */ | ||
| 583 | ops.mode = MTD_OOB_AUTO; | ||
| 584 | ops.len = 0; | ||
| 585 | ops.retlen = 0; | ||
| 586 | ops.ooblen = mtd->ecclayout->oobavail + 1; | ||
| 587 | ops.oobretlen = 0; | ||
| 588 | ops.ooboffs = 0; | ||
| 589 | ops.datbuf = 0; | ||
| 590 | ops.oobbuf = readbuf; | ||
| 591 | printk(PRINT_PREF "attempting to read past end of device\n"); | ||
| 592 | printk(PRINT_PREF "an error is expected...\n"); | ||
| 593 | err = mtd->read_oob(mtd, mtd->size - mtd->writesize, &ops); | ||
| 594 | if (err) { | ||
| 595 | printk(PRINT_PREF "error occurred as expected\n"); | ||
| 596 | err = 0; | ||
| 597 | } else { | ||
| 598 | printk(PRINT_PREF "error: read past end of device\n"); | ||
| 599 | errcnt += 1; | ||
| 600 | } | ||
| 601 | |||
| 602 | err = erase_eraseblock(ebcnt - 1); | ||
| 603 | if (err) | ||
| 604 | goto out; | ||
| 605 | |||
| 606 | /* Attempt to write off end of device */ | ||
| 607 | ops.mode = MTD_OOB_AUTO; | ||
| 608 | ops.len = 0; | ||
| 609 | ops.retlen = 0; | ||
| 610 | ops.ooblen = mtd->ecclayout->oobavail; | ||
| 611 | ops.oobretlen = 0; | ||
| 612 | ops.ooboffs = 1; | ||
| 613 | ops.datbuf = 0; | ||
| 614 | ops.oobbuf = writebuf; | ||
| 615 | printk(PRINT_PREF "attempting to write past end of device\n"); | ||
| 616 | printk(PRINT_PREF "an error is expected...\n"); | ||
| 617 | err = mtd->write_oob(mtd, mtd->size - mtd->writesize, &ops); | ||
| 618 | if (err) { | ||
| 619 | printk(PRINT_PREF "error occurred as expected\n"); | ||
| 620 | err = 0; | ||
| 621 | } else { | ||
| 622 | printk(PRINT_PREF "error: wrote past end of device\n"); | ||
| 623 | errcnt += 1; | ||
| 624 | } | ||
| 625 | |||
| 626 | /* Attempt to read off end of device */ | ||
| 627 | ops.mode = MTD_OOB_AUTO; | ||
| 628 | ops.len = 0; | ||
| 629 | ops.retlen = 0; | ||
| 630 | ops.ooblen = mtd->ecclayout->oobavail; | ||
| 631 | ops.oobretlen = 0; | ||
| 632 | ops.ooboffs = 1; | ||
| 633 | ops.datbuf = 0; | ||
| 634 | ops.oobbuf = readbuf; | ||
| 635 | printk(PRINT_PREF "attempting to read past end of device\n"); | ||
| 636 | printk(PRINT_PREF "an error is expected...\n"); | ||
| 637 | err = mtd->read_oob(mtd, mtd->size - mtd->writesize, &ops); | ||
| 638 | if (err) { | ||
| 639 | printk(PRINT_PREF "error occurred as expected\n"); | ||
| 640 | err = 0; | ||
| 641 | } else { | ||
| 642 | printk(PRINT_PREF "error: read past end of device\n"); | ||
| 643 | errcnt += 1; | ||
| 644 | } | ||
| 645 | } | ||
| 646 | |||
| 647 | /* Fifth test: write / read across block boundaries */ | ||
| 648 | printk(PRINT_PREF "test 5 of 5\n"); | ||
| 649 | |||
| 650 | /* Erase all eraseblocks */ | ||
| 651 | err = erase_whole_device(); | ||
| 652 | if (err) | ||
| 653 | goto out; | ||
| 654 | |||
| 655 | /* Write all eraseblocks */ | ||
| 656 | simple_srand(11); | ||
| 657 | printk(PRINT_PREF "writing OOBs of whole device\n"); | ||
| 658 | for (i = 0; i < ebcnt - 1; ++i) { | ||
| 659 | int cnt = 2; | ||
| 660 | int pg; | ||
| 661 | size_t sz = mtd->ecclayout->oobavail; | ||
| 662 | if (bbt[i] || bbt[i + 1]) | ||
| 663 | continue; | ||
| 664 | addr = (i + 1) * mtd->erasesize - mtd->writesize; | ||
| 665 | for (pg = 0; pg < cnt; ++pg) { | ||
| 666 | set_random_data(writebuf, sz); | ||
| 667 | ops.mode = MTD_OOB_AUTO; | ||
| 668 | ops.len = 0; | ||
| 669 | ops.retlen = 0; | ||
| 670 | ops.ooblen = sz; | ||
| 671 | ops.oobretlen = 0; | ||
| 672 | ops.ooboffs = 0; | ||
| 673 | ops.datbuf = 0; | ||
| 674 | ops.oobbuf = writebuf; | ||
| 675 | err = mtd->write_oob(mtd, addr, &ops); | ||
| 676 | if (err) | ||
| 677 | goto out; | ||
| 678 | if (i % 256 == 0) | ||
| 679 | printk(PRINT_PREF "written up to eraseblock " | ||
| 680 | "%u\n", i); | ||
| 681 | cond_resched(); | ||
| 682 | addr += mtd->writesize; | ||
| 683 | } | ||
| 684 | } | ||
| 685 | printk(PRINT_PREF "written %u eraseblocks\n", i); | ||
| 686 | |||
| 687 | /* Check all eraseblocks */ | ||
| 688 | simple_srand(11); | ||
| 689 | printk(PRINT_PREF "verifying all eraseblocks\n"); | ||
| 690 | for (i = 0; i < ebcnt - 1; ++i) { | ||
| 691 | if (bbt[i] || bbt[i + 1]) | ||
| 692 | continue; | ||
| 693 | set_random_data(writebuf, mtd->ecclayout->oobavail * 2); | ||
| 694 | addr = (i + 1) * mtd->erasesize - mtd->writesize; | ||
| 695 | ops.mode = MTD_OOB_AUTO; | ||
| 696 | ops.len = 0; | ||
| 697 | ops.retlen = 0; | ||
| 698 | ops.ooblen = mtd->ecclayout->oobavail * 2; | ||
| 699 | ops.oobretlen = 0; | ||
| 700 | ops.ooboffs = 0; | ||
| 701 | ops.datbuf = 0; | ||
| 702 | ops.oobbuf = readbuf; | ||
| 703 | err = mtd->read_oob(mtd, addr, &ops); | ||
| 704 | if (err) | ||
| 705 | goto out; | ||
| 706 | if (memcmp(readbuf, writebuf, mtd->ecclayout->oobavail * 2)) { | ||
| 707 | printk(PRINT_PREF "error: verify failed at %#llx\n", | ||
| 708 | (long long)addr); | ||
| 709 | errcnt += 1; | ||
| 710 | if (errcnt > 1000) { | ||
| 711 | printk(PRINT_PREF "error: too many errors\n"); | ||
| 712 | goto out; | ||
| 713 | } | ||
| 714 | } | ||
| 715 | if (i % 256 == 0) | ||
| 716 | printk(PRINT_PREF "verified up to eraseblock %u\n", i); | ||
| 717 | cond_resched(); | ||
| 718 | } | ||
| 719 | printk(PRINT_PREF "verified %u eraseblocks\n", i); | ||
| 720 | |||
| 721 | printk(PRINT_PREF "finished with %d errors\n", errcnt); | ||
| 722 | out: | ||
| 723 | kfree(bbt); | ||
| 724 | kfree(writebuf); | ||
| 725 | kfree(readbuf); | ||
| 726 | put_mtd_device(mtd); | ||
| 727 | if (err) | ||
| 728 | printk(PRINT_PREF "error %d occurred\n", err); | ||
| 729 | printk(KERN_INFO "=================================================\n"); | ||
| 730 | return err; | ||
| 731 | } | ||
| 732 | module_init(mtd_oobtest_init); | ||
| 733 | |||
| 734 | static void __exit mtd_oobtest_exit(void) | ||
| 735 | { | ||
| 736 | return; | ||
| 737 | } | ||
| 738 | module_exit(mtd_oobtest_exit); | ||
| 739 | |||
| 740 | MODULE_DESCRIPTION("Out-of-band test module"); | ||
| 741 | MODULE_AUTHOR("Adrian Hunter"); | ||
| 742 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/mtd/tests/mtd_pagetest.c b/drivers/mtd/tests/mtd_pagetest.c new file mode 100644 index 000000000000..9648818b9e2c --- /dev/null +++ b/drivers/mtd/tests/mtd_pagetest.c | |||
| @@ -0,0 +1,632 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2006-2008 Nokia Corporation | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify it | ||
| 5 | * under the terms of the GNU General Public License version 2 as published by | ||
| 6 | * the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | * | ||
| 13 | * You should have received a copy of the GNU General Public License along with | ||
| 14 | * this program; see the file COPYING. If not, write to the Free Software | ||
| 15 | * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
| 16 | * | ||
| 17 | * Test page read and write on MTD device. | ||
| 18 | * | ||
| 19 | * Author: Adrian Hunter <ext-adrian.hunter@nokia.com> | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <asm/div64.h> | ||
| 23 | #include <linux/init.h> | ||
| 24 | #include <linux/module.h> | ||
| 25 | #include <linux/moduleparam.h> | ||
| 26 | #include <linux/err.h> | ||
| 27 | #include <linux/mtd/mtd.h> | ||
| 28 | #include <linux/sched.h> | ||
| 29 | |||
| 30 | #define PRINT_PREF KERN_INFO "mtd_pagetest: " | ||
| 31 | |||
| 32 | static int dev; | ||
| 33 | module_param(dev, int, S_IRUGO); | ||
| 34 | MODULE_PARM_DESC(dev, "MTD device number to use"); | ||
| 35 | |||
| 36 | static struct mtd_info *mtd; | ||
| 37 | static unsigned char *twopages; | ||
| 38 | static unsigned char *writebuf; | ||
| 39 | static unsigned char *boundary; | ||
| 40 | static unsigned char *bbt; | ||
| 41 | |||
| 42 | static int pgsize; | ||
| 43 | static int bufsize; | ||
| 44 | static int ebcnt; | ||
| 45 | static int pgcnt; | ||
| 46 | static int errcnt; | ||
| 47 | static unsigned long next = 1; | ||
| 48 | |||
| 49 | static inline unsigned int simple_rand(void) | ||
| 50 | { | ||
| 51 | next = next * 1103515245 + 12345; | ||
| 52 | return (unsigned int)((next / 65536) % 32768); | ||
| 53 | } | ||
| 54 | |||
| 55 | static inline void simple_srand(unsigned long seed) | ||
| 56 | { | ||
| 57 | next = seed; | ||
| 58 | } | ||
| 59 | |||
| 60 | static void set_random_data(unsigned char *buf, size_t len) | ||
| 61 | { | ||
| 62 | size_t i; | ||
| 63 | |||
| 64 | for (i = 0; i < len; ++i) | ||
| 65 | buf[i] = simple_rand(); | ||
| 66 | } | ||
| 67 | |||
| 68 | static int erase_eraseblock(int ebnum) | ||
| 69 | { | ||
| 70 | int err; | ||
| 71 | struct erase_info ei; | ||
| 72 | loff_t addr = ebnum * mtd->erasesize; | ||
| 73 | |||
| 74 | memset(&ei, 0, sizeof(struct erase_info)); | ||
| 75 | ei.mtd = mtd; | ||
| 76 | ei.addr = addr; | ||
| 77 | ei.len = mtd->erasesize; | ||
| 78 | |||
| 79 | err = mtd->erase(mtd, &ei); | ||
| 80 | if (err) { | ||
| 81 | printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum); | ||
| 82 | return err; | ||
| 83 | } | ||
| 84 | |||
| 85 | if (ei.state == MTD_ERASE_FAILED) { | ||
| 86 | printk(PRINT_PREF "some erase error occurred at EB %d\n", | ||
| 87 | ebnum); | ||
| 88 | return -EIO; | ||
| 89 | } | ||
| 90 | |||
| 91 | return 0; | ||
| 92 | } | ||
| 93 | |||
| 94 | static int write_eraseblock(int ebnum) | ||
| 95 | { | ||
| 96 | int err = 0; | ||
| 97 | size_t written = 0; | ||
| 98 | loff_t addr = ebnum * mtd->erasesize; | ||
| 99 | |||
| 100 | set_random_data(writebuf, mtd->erasesize); | ||
| 101 | cond_resched(); | ||
| 102 | err = mtd->write(mtd, addr, mtd->erasesize, &written, writebuf); | ||
| 103 | if (err || written != mtd->erasesize) | ||
| 104 | printk(PRINT_PREF "error: write failed at %#llx\n", | ||
| 105 | (long long)addr); | ||
| 106 | |||
| 107 | return err; | ||
| 108 | } | ||
| 109 | |||
| 110 | static int verify_eraseblock(int ebnum) | ||
| 111 | { | ||
| 112 | uint32_t j; | ||
| 113 | size_t read = 0; | ||
| 114 | int err = 0, i; | ||
| 115 | loff_t addr0, addrn; | ||
| 116 | loff_t addr = ebnum * mtd->erasesize; | ||
| 117 | |||
| 118 | addr0 = 0; | ||
| 119 | for (i = 0; bbt[i] && i < ebcnt; ++i) | ||
| 120 | addr0 += mtd->erasesize; | ||
| 121 | |||
| 122 | addrn = mtd->size; | ||
| 123 | for (i = 0; bbt[ebcnt - i - 1] && i < ebcnt; ++i) | ||
| 124 | addrn -= mtd->erasesize; | ||
| 125 | |||
| 126 | set_random_data(writebuf, mtd->erasesize); | ||
| 127 | for (j = 0; j < pgcnt - 1; ++j, addr += pgsize) { | ||
| 128 | /* Do a read to set the internal dataRAMs to different data */ | ||
| 129 | err = mtd->read(mtd, addr0, bufsize, &read, twopages); | ||
| 130 | if (err == -EUCLEAN) | ||
| 131 | err = 0; | ||
| 132 | if (err || read != bufsize) { | ||
| 133 | printk(PRINT_PREF "error: read failed at %#llx\n", | ||
| 134 | (long long)addr0); | ||
| 135 | return err; | ||
| 136 | } | ||
| 137 | err = mtd->read(mtd, addrn - bufsize, bufsize, &read, twopages); | ||
| 138 | if (err == -EUCLEAN) | ||
| 139 | err = 0; | ||
| 140 | if (err || read != bufsize) { | ||
| 141 | printk(PRINT_PREF "error: read failed at %#llx\n", | ||
| 142 | (long long)(addrn - bufsize)); | ||
| 143 | return err; | ||
| 144 | } | ||
| 145 | memset(twopages, 0, bufsize); | ||
| 146 | read = 0; | ||
| 147 | err = mtd->read(mtd, addr, bufsize, &read, twopages); | ||
| 148 | if (err == -EUCLEAN) | ||
| 149 | err = 0; | ||
| 150 | if (err || read != bufsize) { | ||
| 151 | printk(PRINT_PREF "error: read failed at %#llx\n", | ||
| 152 | (long long)addr); | ||
| 153 | break; | ||
| 154 | } | ||
| 155 | if (memcmp(twopages, writebuf + (j * pgsize), bufsize)) { | ||
| 156 | printk(PRINT_PREF "error: verify failed at %#llx\n", | ||
| 157 | (long long)addr); | ||
| 158 | errcnt += 1; | ||
| 159 | } | ||
| 160 | } | ||
| 161 | /* Check boundary between eraseblocks */ | ||
| 162 | if (addr <= addrn - pgsize - pgsize && !bbt[ebnum + 1]) { | ||
| 163 | unsigned long oldnext = next; | ||
| 164 | /* Do a read to set the internal dataRAMs to different data */ | ||
| 165 | err = mtd->read(mtd, addr0, bufsize, &read, twopages); | ||
| 166 | if (err == -EUCLEAN) | ||
| 167 | err = 0; | ||
| 168 | if (err || read != bufsize) { | ||
| 169 | printk(PRINT_PREF "error: read failed at %#llx\n", | ||
| 170 | (long long)addr0); | ||
| 171 | return err; | ||
| 172 | } | ||
| 173 | err = mtd->read(mtd, addrn - bufsize, bufsize, &read, twopages); | ||
| 174 | if (err == -EUCLEAN) | ||
| 175 | err = 0; | ||
| 176 | if (err || read != bufsize) { | ||
| 177 | printk(PRINT_PREF "error: read failed at %#llx\n", | ||
| 178 | (long long)(addrn - bufsize)); | ||
| 179 | return err; | ||
| 180 | } | ||
| 181 | memset(twopages, 0, bufsize); | ||
| 182 | read = 0; | ||
| 183 | err = mtd->read(mtd, addr, bufsize, &read, twopages); | ||
| 184 | if (err == -EUCLEAN) | ||
| 185 | err = 0; | ||
| 186 | if (err || read != bufsize) { | ||
| 187 | printk(PRINT_PREF "error: read failed at %#llx\n", | ||
| 188 | (long long)addr); | ||
| 189 | return err; | ||
| 190 | } | ||
| 191 | memcpy(boundary, writebuf + mtd->erasesize - pgsize, pgsize); | ||
| 192 | set_random_data(boundary + pgsize, pgsize); | ||
| 193 | if (memcmp(twopages, boundary, bufsize)) { | ||
| 194 | printk(PRINT_PREF "error: verify failed at %#llx\n", | ||
| 195 | (long long)addr); | ||
| 196 | errcnt += 1; | ||
| 197 | } | ||
| 198 | next = oldnext; | ||
| 199 | } | ||
| 200 | return err; | ||
| 201 | } | ||
| 202 | |||
| 203 | static int crosstest(void) | ||
| 204 | { | ||
| 205 | size_t read = 0; | ||
| 206 | int err = 0, i; | ||
| 207 | loff_t addr, addr0, addrn; | ||
| 208 | unsigned char *pp1, *pp2, *pp3, *pp4; | ||
| 209 | |||
| 210 | printk(PRINT_PREF "crosstest\n"); | ||
| 211 | pp1 = kmalloc(pgsize * 4, GFP_KERNEL); | ||
| 212 | if (!pp1) { | ||
| 213 | printk(PRINT_PREF "error: cannot allocate memory\n"); | ||
| 214 | return -ENOMEM; | ||
| 215 | } | ||
| 216 | pp2 = pp1 + pgsize; | ||
| 217 | pp3 = pp2 + pgsize; | ||
| 218 | pp4 = pp3 + pgsize; | ||
| 219 | memset(pp1, 0, pgsize * 4); | ||
| 220 | |||
| 221 | addr0 = 0; | ||
| 222 | for (i = 0; bbt[i] && i < ebcnt; ++i) | ||
| 223 | addr0 += mtd->erasesize; | ||
| 224 | |||
| 225 | addrn = mtd->size; | ||
| 226 | for (i = 0; bbt[ebcnt - i - 1] && i < ebcnt; ++i) | ||
| 227 | addrn -= mtd->erasesize; | ||
| 228 | |||
| 229 | /* Read 2nd-to-last page to pp1 */ | ||
| 230 | read = 0; | ||
| 231 | addr = addrn - pgsize - pgsize; | ||
| 232 | err = mtd->read(mtd, addr, pgsize, &read, pp1); | ||
| 233 | if (err == -EUCLEAN) | ||
| 234 | err = 0; | ||
| 235 | if (err || read != pgsize) { | ||
| 236 | printk(PRINT_PREF "error: read failed at %#llx\n", | ||
| 237 | (long long)addr); | ||
| 238 | kfree(pp1); | ||
| 239 | return err; | ||
| 240 | } | ||
| 241 | |||
| 242 | /* Read 3rd-to-last page to pp1 */ | ||
| 243 | read = 0; | ||
| 244 | addr = addrn - pgsize - pgsize - pgsize; | ||
| 245 | err = mtd->read(mtd, addr, pgsize, &read, pp1); | ||
| 246 | if (err == -EUCLEAN) | ||
| 247 | err = 0; | ||
| 248 | if (err || read != pgsize) { | ||
| 249 | printk(PRINT_PREF "error: read failed at %#llx\n", | ||
| 250 | (long long)addr); | ||
| 251 | kfree(pp1); | ||
| 252 | return err; | ||
| 253 | } | ||
| 254 | |||
| 255 | /* Read first page to pp2 */ | ||
| 256 | read = 0; | ||
| 257 | addr = addr0; | ||
| 258 | printk(PRINT_PREF "reading page at %#llx\n", (long long)addr); | ||
| 259 | err = mtd->read(mtd, addr, pgsize, &read, pp2); | ||
| 260 | if (err == -EUCLEAN) | ||
| 261 | err = 0; | ||
| 262 | if (err || read != pgsize) { | ||
| 263 | printk(PRINT_PREF "error: read failed at %#llx\n", | ||
| 264 | (long long)addr); | ||
| 265 | kfree(pp1); | ||
| 266 | return err; | ||
| 267 | } | ||
| 268 | |||
| 269 | /* Read last page to pp3 */ | ||
| 270 | read = 0; | ||
| 271 | addr = addrn - pgsize; | ||
| 272 | printk(PRINT_PREF "reading page at %#llx\n", (long long)addr); | ||
| 273 | err = mtd->read(mtd, addr, pgsize, &read, pp3); | ||
| 274 | if (err == -EUCLEAN) | ||
| 275 | err = 0; | ||
| 276 | if (err || read != pgsize) { | ||
| 277 | printk(PRINT_PREF "error: read failed at %#llx\n", | ||
| 278 | (long long)addr); | ||
| 279 | kfree(pp1); | ||
| 280 | return err; | ||
| 281 | } | ||
| 282 | |||
| 283 | /* Read first page again to pp4 */ | ||
| 284 | read = 0; | ||
| 285 | addr = addr0; | ||
| 286 | printk(PRINT_PREF "reading page at %#llx\n", (long long)addr); | ||
| 287 | err = mtd->read(mtd, addr, pgsize, &read, pp4); | ||
| 288 | if (err == -EUCLEAN) | ||
| 289 | err = 0; | ||
| 290 | if (err || read != pgsize) { | ||
| 291 | printk(PRINT_PREF "error: read failed at %#llx\n", | ||
| 292 | (long long)addr); | ||
| 293 | kfree(pp1); | ||
| 294 | return err; | ||
| 295 | } | ||
| 296 | |||
| 297 | /* pp2 and pp4 should be the same */ | ||
| 298 | printk(PRINT_PREF "verifying pages read at %#llx match\n", | ||
| 299 | (long long)addr0); | ||
| 300 | if (memcmp(pp2, pp4, pgsize)) { | ||
| 301 | printk(PRINT_PREF "verify failed!\n"); | ||
| 302 | errcnt += 1; | ||
| 303 | } else if (!err) | ||
| 304 | printk(PRINT_PREF "crosstest ok\n"); | ||
| 305 | kfree(pp1); | ||
| 306 | return err; | ||
| 307 | } | ||
| 308 | |||
| 309 | static int erasecrosstest(void) | ||
| 310 | { | ||
| 311 | size_t read = 0, written = 0; | ||
| 312 | int err = 0, i, ebnum, ok = 1, ebnum2; | ||
| 313 | loff_t addr0; | ||
| 314 | char *readbuf = twopages; | ||
| 315 | |||
| 316 | printk(PRINT_PREF "erasecrosstest\n"); | ||
| 317 | |||
| 318 | ebnum = 0; | ||
| 319 | addr0 = 0; | ||
| 320 | for (i = 0; bbt[i] && i < ebcnt; ++i) { | ||
| 321 | addr0 += mtd->erasesize; | ||
| 322 | ebnum += 1; | ||
| 323 | } | ||
| 324 | |||
| 325 | ebnum2 = ebcnt - 1; | ||
| 326 | while (ebnum2 && bbt[ebnum2]) | ||
| 327 | ebnum2 -= 1; | ||
| 328 | |||
| 329 | printk(PRINT_PREF "erasing block %d\n", ebnum); | ||
| 330 | err = erase_eraseblock(ebnum); | ||
| 331 | if (err) | ||
| 332 | return err; | ||
| 333 | |||
| 334 | printk(PRINT_PREF "writing 1st page of block %d\n", ebnum); | ||
| 335 | set_random_data(writebuf, pgsize); | ||
| 336 | strcpy(writebuf, "There is no data like this!"); | ||
| 337 | err = mtd->write(mtd, addr0, pgsize, &written, writebuf); | ||
| 338 | if (err || written != pgsize) { | ||
| 339 | printk(PRINT_PREF "error: write failed at %#llx\n", | ||
| 340 | (long long)addr0); | ||
| 341 | return err ? err : -1; | ||
| 342 | } | ||
| 343 | |||
| 344 | printk(PRINT_PREF "reading 1st page of block %d\n", ebnum); | ||
| 345 | memset(readbuf, 0, pgsize); | ||
| 346 | err = mtd->read(mtd, addr0, pgsize, &read, readbuf); | ||
| 347 | if (err == -EUCLEAN) | ||
| 348 | err = 0; | ||
| 349 | if (err || read != pgsize) { | ||
| 350 | printk(PRINT_PREF "error: read failed at %#llx\n", | ||
| 351 | (long long)addr0); | ||
| 352 | return err ? err : -1; | ||
| 353 | } | ||
| 354 | |||
| 355 | printk(PRINT_PREF "verifying 1st page of block %d\n", ebnum); | ||
| 356 | if (memcmp(writebuf, readbuf, pgsize)) { | ||
| 357 | printk(PRINT_PREF "verify failed!\n"); | ||
| 358 | errcnt += 1; | ||
| 359 | ok = 0; | ||
| 360 | return err; | ||
| 361 | } | ||
| 362 | |||
| 363 | printk(PRINT_PREF "erasing block %d\n", ebnum); | ||
| 364 | err = erase_eraseblock(ebnum); | ||
| 365 | if (err) | ||
| 366 | return err; | ||
| 367 | |||
| 368 | printk(PRINT_PREF "writing 1st page of block %d\n", ebnum); | ||
| 369 | set_random_data(writebuf, pgsize); | ||
| 370 | strcpy(writebuf, "There is no data like this!"); | ||
| 371 | err = mtd->write(mtd, addr0, pgsize, &written, writebuf); | ||
| 372 | if (err || written != pgsize) { | ||
| 373 | printk(PRINT_PREF "error: write failed at %#llx\n", | ||
| 374 | (long long)addr0); | ||
| 375 | return err ? err : -1; | ||
| 376 | } | ||
| 377 | |||
| 378 | printk(PRINT_PREF "erasing block %d\n", ebnum2); | ||
| 379 | err = erase_eraseblock(ebnum2); | ||
| 380 | if (err) | ||
| 381 | return err; | ||
| 382 | |||
| 383 | printk(PRINT_PREF "reading 1st page of block %d\n", ebnum); | ||
| 384 | memset(readbuf, 0, pgsize); | ||
| 385 | err = mtd->read(mtd, addr0, pgsize, &read, readbuf); | ||
| 386 | if (err == -EUCLEAN) | ||
| 387 | err = 0; | ||
| 388 | if (err || read != pgsize) { | ||
| 389 | printk(PRINT_PREF "error: read failed at %#llx\n", | ||
| 390 | (long long)addr0); | ||
| 391 | return err ? err : -1; | ||
| 392 | } | ||
| 393 | |||
| 394 | printk(PRINT_PREF "verifying 1st page of block %d\n", ebnum); | ||
| 395 | if (memcmp(writebuf, readbuf, pgsize)) { | ||
| 396 | printk(PRINT_PREF "verify failed!\n"); | ||
| 397 | errcnt += 1; | ||
| 398 | ok = 0; | ||
| 399 | } | ||
| 400 | |||
| 401 | if (ok && !err) | ||
| 402 | printk(PRINT_PREF "erasecrosstest ok\n"); | ||
| 403 | return err; | ||
| 404 | } | ||
| 405 | |||
| 406 | static int erasetest(void) | ||
| 407 | { | ||
| 408 | size_t read = 0, written = 0; | ||
| 409 | int err = 0, i, ebnum, ok = 1; | ||
| 410 | loff_t addr0; | ||
| 411 | |||
| 412 | printk(PRINT_PREF "erasetest\n"); | ||
| 413 | |||
| 414 | ebnum = 0; | ||
| 415 | addr0 = 0; | ||
| 416 | for (i = 0; bbt[i] && i < ebcnt; ++i) { | ||
| 417 | addr0 += mtd->erasesize; | ||
| 418 | ebnum += 1; | ||
| 419 | } | ||
| 420 | |||
| 421 | printk(PRINT_PREF "erasing block %d\n", ebnum); | ||
| 422 | err = erase_eraseblock(ebnum); | ||
| 423 | if (err) | ||
| 424 | return err; | ||
| 425 | |||
| 426 | printk(PRINT_PREF "writing 1st page of block %d\n", ebnum); | ||
| 427 | set_random_data(writebuf, pgsize); | ||
| 428 | err = mtd->write(mtd, addr0, pgsize, &written, writebuf); | ||
| 429 | if (err || written != pgsize) { | ||
| 430 | printk(PRINT_PREF "error: write failed at %#llx\n", | ||
| 431 | (long long)addr0); | ||
| 432 | return err ? err : -1; | ||
| 433 | } | ||
| 434 | |||
| 435 | printk(PRINT_PREF "erasing block %d\n", ebnum); | ||
| 436 | err = erase_eraseblock(ebnum); | ||
| 437 | if (err) | ||
| 438 | return err; | ||
| 439 | |||
| 440 | printk(PRINT_PREF "reading 1st page of block %d\n", ebnum); | ||
| 441 | err = mtd->read(mtd, addr0, pgsize, &read, twopages); | ||
| 442 | if (err == -EUCLEAN) | ||
| 443 | err = 0; | ||
| 444 | if (err || read != pgsize) { | ||
| 445 | printk(PRINT_PREF "error: read failed at %#llx\n", | ||
| 446 | (long long)addr0); | ||
| 447 | return err ? err : -1; | ||
| 448 | } | ||
| 449 | |||
| 450 | printk(PRINT_PREF "verifying 1st page of block %d is all 0xff\n", | ||
| 451 | ebnum); | ||
| 452 | for (i = 0; i < pgsize; ++i) | ||
| 453 | if (twopages[i] != 0xff) { | ||
| 454 | printk(PRINT_PREF "verifying all 0xff failed at %d\n", | ||
| 455 | i); | ||
| 456 | errcnt += 1; | ||
| 457 | ok = 0; | ||
| 458 | break; | ||
| 459 | } | ||
| 460 | |||
| 461 | if (ok && !err) | ||
| 462 | printk(PRINT_PREF "erasetest ok\n"); | ||
| 463 | |||
| 464 | return err; | ||
| 465 | } | ||
| 466 | |||
| 467 | static int is_block_bad(int ebnum) | ||
| 468 | { | ||
| 469 | loff_t addr = ebnum * mtd->erasesize; | ||
| 470 | int ret; | ||
| 471 | |||
| 472 | ret = mtd->block_isbad(mtd, addr); | ||
| 473 | if (ret) | ||
| 474 | printk(PRINT_PREF "block %d is bad\n", ebnum); | ||
| 475 | return ret; | ||
| 476 | } | ||
| 477 | |||
| 478 | static int scan_for_bad_eraseblocks(void) | ||
| 479 | { | ||
| 480 | int i, bad = 0; | ||
| 481 | |||
| 482 | bbt = kmalloc(ebcnt, GFP_KERNEL); | ||
| 483 | if (!bbt) { | ||
| 484 | printk(PRINT_PREF "error: cannot allocate memory\n"); | ||
| 485 | return -ENOMEM; | ||
| 486 | } | ||
| 487 | memset(bbt, 0 , ebcnt); | ||
| 488 | |||
| 489 | printk(PRINT_PREF "scanning for bad eraseblocks\n"); | ||
| 490 | for (i = 0; i < ebcnt; ++i) { | ||
| 491 | bbt[i] = is_block_bad(i) ? 1 : 0; | ||
| 492 | if (bbt[i]) | ||
| 493 | bad += 1; | ||
| 494 | cond_resched(); | ||
| 495 | } | ||
| 496 | printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad); | ||
| 497 | return 0; | ||
| 498 | } | ||
| 499 | |||
| 500 | static int __init mtd_pagetest_init(void) | ||
| 501 | { | ||
| 502 | int err = 0; | ||
| 503 | uint64_t tmp; | ||
| 504 | uint32_t i; | ||
| 505 | |||
| 506 | printk(KERN_INFO "\n"); | ||
| 507 | printk(KERN_INFO "=================================================\n"); | ||
| 508 | printk(PRINT_PREF "MTD device: %d\n", dev); | ||
| 509 | |||
| 510 | mtd = get_mtd_device(NULL, dev); | ||
| 511 | if (IS_ERR(mtd)) { | ||
| 512 | err = PTR_ERR(mtd); | ||
| 513 | printk(PRINT_PREF "error: cannot get MTD device\n"); | ||
| 514 | return err; | ||
| 515 | } | ||
| 516 | |||
| 517 | if (mtd->type != MTD_NANDFLASH) { | ||
| 518 | printk(PRINT_PREF "this test requires NAND flash\n"); | ||
| 519 | goto out; | ||
| 520 | } | ||
| 521 | |||
| 522 | tmp = mtd->size; | ||
| 523 | do_div(tmp, mtd->erasesize); | ||
| 524 | ebcnt = tmp; | ||
| 525 | pgcnt = mtd->erasesize / mtd->writesize; | ||
| 526 | |||
| 527 | printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, " | ||
| 528 | "page size %u, count of eraseblocks %u, pages per " | ||
| 529 | "eraseblock %u, OOB size %u\n", | ||
| 530 | (unsigned long long)mtd->size, mtd->erasesize, | ||
| 531 | pgsize, ebcnt, pgcnt, mtd->oobsize); | ||
| 532 | |||
| 533 | err = -ENOMEM; | ||
| 534 | bufsize = pgsize * 2; | ||
| 535 | writebuf = kmalloc(mtd->erasesize, GFP_KERNEL); | ||
| 536 | if (!writebuf) { | ||
| 537 | printk(PRINT_PREF "error: cannot allocate memory\n"); | ||
| 538 | goto out; | ||
| 539 | } | ||
| 540 | twopages = kmalloc(bufsize, GFP_KERNEL); | ||
| 541 | if (!twopages) { | ||
| 542 | printk(PRINT_PREF "error: cannot allocate memory\n"); | ||
| 543 | goto out; | ||
| 544 | } | ||
| 545 | boundary = kmalloc(bufsize, GFP_KERNEL); | ||
| 546 | if (!boundary) { | ||
| 547 | printk(PRINT_PREF "error: cannot allocate memory\n"); | ||
| 548 | goto out; | ||
| 549 | } | ||
| 550 | |||
| 551 | err = scan_for_bad_eraseblocks(); | ||
| 552 | if (err) | ||
| 553 | goto out; | ||
| 554 | |||
| 555 | /* Erase all eraseblocks */ | ||
| 556 | printk(PRINT_PREF "erasing whole device\n"); | ||
| 557 | for (i = 0; i < ebcnt; ++i) { | ||
| 558 | if (bbt[i]) | ||
| 559 | continue; | ||
| 560 | err = erase_eraseblock(i); | ||
| 561 | if (err) | ||
| 562 | goto out; | ||
| 563 | cond_resched(); | ||
| 564 | } | ||
| 565 | printk(PRINT_PREF "erased %u eraseblocks\n", i); | ||
| 566 | |||
| 567 | /* Write all eraseblocks */ | ||
| 568 | simple_srand(1); | ||
| 569 | printk(PRINT_PREF "writing whole device\n"); | ||
| 570 | for (i = 0; i < ebcnt; ++i) { | ||
| 571 | if (bbt[i]) | ||
| 572 | continue; | ||
| 573 | err = write_eraseblock(i); | ||
| 574 | if (err) | ||
| 575 | goto out; | ||
| 576 | if (i % 256 == 0) | ||
| 577 | printk(PRINT_PREF "written up to eraseblock %u\n", i); | ||
| 578 | cond_resched(); | ||
| 579 | } | ||
| 580 | printk(PRINT_PREF "written %u eraseblocks\n", i); | ||
| 581 | |||
| 582 | /* Check all eraseblocks */ | ||
| 583 | simple_srand(1); | ||
| 584 | printk(PRINT_PREF "verifying all eraseblocks\n"); | ||
| 585 | for (i = 0; i < ebcnt; ++i) { | ||
| 586 | if (bbt[i]) | ||
| 587 | continue; | ||
| 588 | err = verify_eraseblock(i); | ||
| 589 | if (err) | ||
| 590 | goto out; | ||
| 591 | if (i % 256 == 0) | ||
| 592 | printk(PRINT_PREF "verified up to eraseblock %u\n", i); | ||
| 593 | cond_resched(); | ||
| 594 | } | ||
| 595 | printk(PRINT_PREF "verified %u eraseblocks\n", i); | ||
| 596 | |||
| 597 | err = crosstest(); | ||
| 598 | if (err) | ||
| 599 | goto out; | ||
| 600 | |||
| 601 | err = erasecrosstest(); | ||
| 602 | if (err) | ||
| 603 | goto out; | ||
| 604 | |||
| 605 | err = erasetest(); | ||
| 606 | if (err) | ||
| 607 | goto out; | ||
| 608 | |||
| 609 | printk(PRINT_PREF "finished with %d errors\n", errcnt); | ||
| 610 | out: | ||
| 611 | |||
| 612 | kfree(bbt); | ||
| 613 | kfree(boundary); | ||
| 614 | kfree(twopages); | ||
| 615 | kfree(writebuf); | ||
| 616 | put_mtd_device(mtd); | ||
| 617 | if (err) | ||
| 618 | printk(PRINT_PREF "error %d occurred\n", err); | ||
| 619 | printk(KERN_INFO "=================================================\n"); | ||
| 620 | return err; | ||
| 621 | } | ||
| 622 | module_init(mtd_pagetest_init); | ||
| 623 | |||
| 624 | static void __exit mtd_pagetest_exit(void) | ||
| 625 | { | ||
| 626 | return; | ||
| 627 | } | ||
| 628 | module_exit(mtd_pagetest_exit); | ||
| 629 | |||
| 630 | MODULE_DESCRIPTION("NAND page test"); | ||
| 631 | MODULE_AUTHOR("Adrian Hunter"); | ||
| 632 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/mtd/tests/mtd_readtest.c b/drivers/mtd/tests/mtd_readtest.c new file mode 100644 index 000000000000..645e77fdc63d --- /dev/null +++ b/drivers/mtd/tests/mtd_readtest.c | |||
| @@ -0,0 +1,253 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2006-2008 Nokia Corporation | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify it | ||
| 5 | * under the terms of the GNU General Public License version 2 as published by | ||
| 6 | * the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | * | ||
| 13 | * You should have received a copy of the GNU General Public License along with | ||
| 14 | * this program; see the file COPYING. If not, write to the Free Software | ||
| 15 | * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
| 16 | * | ||
| 17 | * Check MTD device read. | ||
| 18 | * | ||
| 19 | * Author: Adrian Hunter <ext-adrian.hunter@nokia.com> | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <linux/init.h> | ||
| 23 | #include <linux/module.h> | ||
| 24 | #include <linux/moduleparam.h> | ||
| 25 | #include <linux/err.h> | ||
| 26 | #include <linux/mtd/mtd.h> | ||
| 27 | #include <linux/sched.h> | ||
| 28 | |||
| 29 | #define PRINT_PREF KERN_INFO "mtd_readtest: " | ||
| 30 | |||
| 31 | static int dev; | ||
| 32 | module_param(dev, int, S_IRUGO); | ||
| 33 | MODULE_PARM_DESC(dev, "MTD device number to use"); | ||
| 34 | |||
| 35 | static struct mtd_info *mtd; | ||
| 36 | static unsigned char *iobuf; | ||
| 37 | static unsigned char *iobuf1; | ||
| 38 | static unsigned char *bbt; | ||
| 39 | |||
| 40 | static int pgsize; | ||
| 41 | static int ebcnt; | ||
| 42 | static int pgcnt; | ||
| 43 | |||
| 44 | static int read_eraseblock_by_page(int ebnum) | ||
| 45 | { | ||
| 46 | size_t read = 0; | ||
| 47 | int i, ret, err = 0; | ||
| 48 | loff_t addr = ebnum * mtd->erasesize; | ||
| 49 | void *buf = iobuf; | ||
| 50 | void *oobbuf = iobuf1; | ||
| 51 | |||
| 52 | for (i = 0; i < pgcnt; i++) { | ||
| 53 | memset(buf, 0 , pgcnt); | ||
| 54 | ret = mtd->read(mtd, addr, pgsize, &read, buf); | ||
| 55 | if (ret == -EUCLEAN) | ||
| 56 | ret = 0; | ||
| 57 | if (ret || read != pgsize) { | ||
| 58 | printk(PRINT_PREF "error: read failed at %#llx\n", | ||
| 59 | (long long)addr); | ||
| 60 | if (!err) | ||
| 61 | err = ret; | ||
| 62 | if (!err) | ||
| 63 | err = -EINVAL; | ||
| 64 | } | ||
| 65 | if (mtd->oobsize) { | ||
| 66 | struct mtd_oob_ops ops; | ||
| 67 | |||
| 68 | ops.mode = MTD_OOB_PLACE; | ||
| 69 | ops.len = 0; | ||
| 70 | ops.retlen = 0; | ||
| 71 | ops.ooblen = mtd->oobsize; | ||
| 72 | ops.oobretlen = 0; | ||
| 73 | ops.ooboffs = 0; | ||
| 74 | ops.datbuf = 0; | ||
| 75 | ops.oobbuf = oobbuf; | ||
| 76 | ret = mtd->read_oob(mtd, addr, &ops); | ||
| 77 | if (ret || ops.oobretlen != mtd->oobsize) { | ||
| 78 | printk(PRINT_PREF "error: read oob failed at " | ||
| 79 | "%#llx\n", (long long)addr); | ||
| 80 | if (!err) | ||
| 81 | err = ret; | ||
| 82 | if (!err) | ||
| 83 | err = -EINVAL; | ||
| 84 | } | ||
| 85 | oobbuf += mtd->oobsize; | ||
| 86 | } | ||
| 87 | addr += pgsize; | ||
| 88 | buf += pgsize; | ||
| 89 | } | ||
| 90 | |||
| 91 | return err; | ||
| 92 | } | ||
| 93 | |||
| 94 | static void dump_eraseblock(int ebnum) | ||
| 95 | { | ||
| 96 | int i, j, n; | ||
| 97 | char line[128]; | ||
| 98 | int pg, oob; | ||
| 99 | |||
| 100 | printk(PRINT_PREF "dumping eraseblock %d\n", ebnum); | ||
| 101 | n = mtd->erasesize; | ||
| 102 | for (i = 0; i < n;) { | ||
| 103 | char *p = line; | ||
| 104 | |||
| 105 | p += sprintf(p, "%05x: ", i); | ||
| 106 | for (j = 0; j < 32 && i < n; j++, i++) | ||
| 107 | p += sprintf(p, "%02x", (unsigned int)iobuf[i]); | ||
| 108 | printk(KERN_CRIT "%s\n", line); | ||
| 109 | cond_resched(); | ||
| 110 | } | ||
| 111 | if (!mtd->oobsize) | ||
| 112 | return; | ||
| 113 | printk(PRINT_PREF "dumping oob from eraseblock %d\n", ebnum); | ||
| 114 | n = mtd->oobsize; | ||
| 115 | for (pg = 0, i = 0; pg < pgcnt; pg++) | ||
| 116 | for (oob = 0; oob < n;) { | ||
| 117 | char *p = line; | ||
| 118 | |||
| 119 | p += sprintf(p, "%05x: ", i); | ||
| 120 | for (j = 0; j < 32 && oob < n; j++, oob++, i++) | ||
| 121 | p += sprintf(p, "%02x", | ||
| 122 | (unsigned int)iobuf1[i]); | ||
| 123 | printk(KERN_CRIT "%s\n", line); | ||
| 124 | cond_resched(); | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | static int is_block_bad(int ebnum) | ||
| 129 | { | ||
| 130 | loff_t addr = ebnum * mtd->erasesize; | ||
| 131 | int ret; | ||
| 132 | |||
| 133 | ret = mtd->block_isbad(mtd, addr); | ||
| 134 | if (ret) | ||
| 135 | printk(PRINT_PREF "block %d is bad\n", ebnum); | ||
| 136 | return ret; | ||
| 137 | } | ||
| 138 | |||
| 139 | static int scan_for_bad_eraseblocks(void) | ||
| 140 | { | ||
| 141 | int i, bad = 0; | ||
| 142 | |||
| 143 | bbt = kmalloc(ebcnt, GFP_KERNEL); | ||
| 144 | if (!bbt) { | ||
| 145 | printk(PRINT_PREF "error: cannot allocate memory\n"); | ||
| 146 | return -ENOMEM; | ||
| 147 | } | ||
| 148 | memset(bbt, 0 , ebcnt); | ||
| 149 | |||
| 150 | printk(PRINT_PREF "scanning for bad eraseblocks\n"); | ||
| 151 | for (i = 0; i < ebcnt; ++i) { | ||
| 152 | bbt[i] = is_block_bad(i) ? 1 : 0; | ||
| 153 | if (bbt[i]) | ||
| 154 | bad += 1; | ||
| 155 | cond_resched(); | ||
| 156 | } | ||
| 157 | printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad); | ||
| 158 | return 0; | ||
| 159 | } | ||
| 160 | |||
| 161 | static int __init mtd_readtest_init(void) | ||
| 162 | { | ||
| 163 | uint64_t tmp; | ||
| 164 | int err, i; | ||
| 165 | |||
| 166 | printk(KERN_INFO "\n"); | ||
| 167 | printk(KERN_INFO "=================================================\n"); | ||
| 168 | printk(PRINT_PREF "MTD device: %d\n", dev); | ||
| 169 | |||
| 170 | mtd = get_mtd_device(NULL, dev); | ||
| 171 | if (IS_ERR(mtd)) { | ||
| 172 | err = PTR_ERR(mtd); | ||
| 173 | printk(PRINT_PREF "error: Cannot get MTD device\n"); | ||
| 174 | return err; | ||
| 175 | } | ||
| 176 | |||
| 177 | if (mtd->writesize == 1) { | ||
| 178 | printk(PRINT_PREF "not NAND flash, assume page size is 512 " | ||
| 179 | "bytes.\n"); | ||
| 180 | pgsize = 512; | ||
| 181 | } else | ||
| 182 | pgsize = mtd->writesize; | ||
| 183 | |||
| 184 | tmp = mtd->size; | ||
| 185 | do_div(tmp, mtd->erasesize); | ||
| 186 | ebcnt = tmp; | ||
| 187 | pgcnt = mtd->erasesize / mtd->writesize; | ||
| 188 | |||
| 189 | printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, " | ||
| 190 | "page size %u, count of eraseblocks %u, pages per " | ||
| 191 | "eraseblock %u, OOB size %u\n", | ||
| 192 | (unsigned long long)mtd->size, mtd->erasesize, | ||
| 193 | pgsize, ebcnt, pgcnt, mtd->oobsize); | ||
| 194 | |||
| 195 | err = -ENOMEM; | ||
| 196 | iobuf = kmalloc(mtd->erasesize, GFP_KERNEL); | ||
| 197 | if (!iobuf) { | ||
| 198 | printk(PRINT_PREF "error: cannot allocate memory\n"); | ||
| 199 | goto out; | ||
| 200 | } | ||
| 201 | iobuf1 = kmalloc(mtd->erasesize, GFP_KERNEL); | ||
| 202 | if (!iobuf1) { | ||
| 203 | printk(PRINT_PREF "error: cannot allocate memory\n"); | ||
| 204 | goto out; | ||
| 205 | } | ||
| 206 | |||
| 207 | err = scan_for_bad_eraseblocks(); | ||
| 208 | if (err) | ||
| 209 | goto out; | ||
| 210 | |||
| 211 | /* Read all eraseblocks 1 page at a time */ | ||
| 212 | printk(PRINT_PREF "testing page read\n"); | ||
| 213 | for (i = 0; i < ebcnt; ++i) { | ||
| 214 | int ret; | ||
| 215 | |||
| 216 | if (bbt[i]) | ||
| 217 | continue; | ||
| 218 | ret = read_eraseblock_by_page(i); | ||
| 219 | if (ret) { | ||
| 220 | dump_eraseblock(i); | ||
| 221 | if (!err) | ||
| 222 | err = ret; | ||
| 223 | } | ||
| 224 | cond_resched(); | ||
| 225 | } | ||
| 226 | |||
| 227 | if (err) | ||
| 228 | printk(PRINT_PREF "finished with errors\n"); | ||
| 229 | else | ||
| 230 | printk(PRINT_PREF "finished\n"); | ||
| 231 | |||
| 232 | out: | ||
| 233 | |||
| 234 | kfree(iobuf); | ||
| 235 | kfree(iobuf1); | ||
| 236 | kfree(bbt); | ||
| 237 | put_mtd_device(mtd); | ||
| 238 | if (err) | ||
| 239 | printk(PRINT_PREF "error %d occurred\n", err); | ||
| 240 | printk(KERN_INFO "=================================================\n"); | ||
| 241 | return err; | ||
| 242 | } | ||
| 243 | module_init(mtd_readtest_init); | ||
| 244 | |||
| 245 | static void __exit mtd_readtest_exit(void) | ||
| 246 | { | ||
| 247 | return; | ||
| 248 | } | ||
| 249 | module_exit(mtd_readtest_exit); | ||
| 250 | |||
| 251 | MODULE_DESCRIPTION("Read test module"); | ||
| 252 | MODULE_AUTHOR("Adrian Hunter"); | ||
| 253 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/mtd/tests/mtd_speedtest.c b/drivers/mtd/tests/mtd_speedtest.c new file mode 100644 index 000000000000..141363a7e805 --- /dev/null +++ b/drivers/mtd/tests/mtd_speedtest.c | |||
| @@ -0,0 +1,502 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007 Nokia Corporation | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify it | ||
| 5 | * under the terms of the GNU General Public License version 2 as published by | ||
| 6 | * the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | * | ||
| 13 | * You should have received a copy of the GNU General Public License along with | ||
| 14 | * this program; see the file COPYING. If not, write to the Free Software | ||
| 15 | * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
| 16 | * | ||
| 17 | * Test read and write speed of a MTD device. | ||
| 18 | * | ||
| 19 | * Author: Adrian Hunter <ext-adrian.hunter@nokia.com> | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <linux/init.h> | ||
| 23 | #include <linux/module.h> | ||
| 24 | #include <linux/moduleparam.h> | ||
| 25 | #include <linux/err.h> | ||
| 26 | #include <linux/mtd/mtd.h> | ||
| 27 | #include <linux/sched.h> | ||
| 28 | |||
| 29 | #define PRINT_PREF KERN_INFO "mtd_speedtest: " | ||
| 30 | |||
| 31 | static int dev; | ||
| 32 | module_param(dev, int, S_IRUGO); | ||
| 33 | MODULE_PARM_DESC(dev, "MTD device number to use"); | ||
| 34 | |||
| 35 | static struct mtd_info *mtd; | ||
| 36 | static unsigned char *iobuf; | ||
| 37 | static unsigned char *bbt; | ||
| 38 | |||
| 39 | static int pgsize; | ||
| 40 | static int ebcnt; | ||
| 41 | static int pgcnt; | ||
| 42 | static int goodebcnt; | ||
| 43 | static struct timeval start, finish; | ||
| 44 | static unsigned long next = 1; | ||
| 45 | |||
| 46 | static inline unsigned int simple_rand(void) | ||
| 47 | { | ||
| 48 | next = next * 1103515245 + 12345; | ||
| 49 | return (unsigned int)((next / 65536) % 32768); | ||
| 50 | } | ||
| 51 | |||
| 52 | static inline void simple_srand(unsigned long seed) | ||
| 53 | { | ||
| 54 | next = seed; | ||
| 55 | } | ||
| 56 | |||
| 57 | static void set_random_data(unsigned char *buf, size_t len) | ||
| 58 | { | ||
| 59 | size_t i; | ||
| 60 | |||
| 61 | for (i = 0; i < len; ++i) | ||
| 62 | buf[i] = simple_rand(); | ||
| 63 | } | ||
| 64 | |||
| 65 | static int erase_eraseblock(int ebnum) | ||
| 66 | { | ||
| 67 | int err; | ||
| 68 | struct erase_info ei; | ||
| 69 | loff_t addr = ebnum * mtd->erasesize; | ||
| 70 | |||
| 71 | memset(&ei, 0, sizeof(struct erase_info)); | ||
| 72 | ei.mtd = mtd; | ||
| 73 | ei.addr = addr; | ||
| 74 | ei.len = mtd->erasesize; | ||
| 75 | |||
| 76 | err = mtd->erase(mtd, &ei); | ||
| 77 | if (err) { | ||
| 78 | printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum); | ||
| 79 | return err; | ||
| 80 | } | ||
| 81 | |||
| 82 | if (ei.state == MTD_ERASE_FAILED) { | ||
| 83 | printk(PRINT_PREF "some erase error occurred at EB %d\n", | ||
| 84 | ebnum); | ||
| 85 | return -EIO; | ||
| 86 | } | ||
| 87 | |||
| 88 | return 0; | ||
| 89 | } | ||
| 90 | |||
| 91 | static int erase_whole_device(void) | ||
| 92 | { | ||
| 93 | int err; | ||
| 94 | unsigned int i; | ||
| 95 | |||
| 96 | for (i = 0; i < ebcnt; ++i) { | ||
| 97 | if (bbt[i]) | ||
| 98 | continue; | ||
| 99 | err = erase_eraseblock(i); | ||
| 100 | if (err) | ||
| 101 | return err; | ||
| 102 | cond_resched(); | ||
| 103 | } | ||
| 104 | return 0; | ||
| 105 | } | ||
| 106 | |||
| 107 | static int write_eraseblock(int ebnum) | ||
| 108 | { | ||
| 109 | size_t written = 0; | ||
| 110 | int err = 0; | ||
| 111 | loff_t addr = ebnum * mtd->erasesize; | ||
| 112 | |||
| 113 | err = mtd->write(mtd, addr, mtd->erasesize, &written, iobuf); | ||
| 114 | if (err || written != mtd->erasesize) { | ||
| 115 | printk(PRINT_PREF "error: write failed at %#llx\n", addr); | ||
| 116 | if (!err) | ||
| 117 | err = -EINVAL; | ||
| 118 | } | ||
| 119 | |||
| 120 | return err; | ||
| 121 | } | ||
| 122 | |||
| 123 | static int write_eraseblock_by_page(int ebnum) | ||
| 124 | { | ||
| 125 | size_t written = 0; | ||
| 126 | int i, err = 0; | ||
| 127 | loff_t addr = ebnum * mtd->erasesize; | ||
| 128 | void *buf = iobuf; | ||
| 129 | |||
| 130 | for (i = 0; i < pgcnt; i++) { | ||
| 131 | err = mtd->write(mtd, addr, pgsize, &written, buf); | ||
| 132 | if (err || written != pgsize) { | ||
| 133 | printk(PRINT_PREF "error: write failed at %#llx\n", | ||
| 134 | addr); | ||
| 135 | if (!err) | ||
| 136 | err = -EINVAL; | ||
| 137 | break; | ||
| 138 | } | ||
| 139 | addr += pgsize; | ||
| 140 | buf += pgsize; | ||
| 141 | } | ||
| 142 | |||
| 143 | return err; | ||
| 144 | } | ||
| 145 | |||
| 146 | static int write_eraseblock_by_2pages(int ebnum) | ||
| 147 | { | ||
| 148 | size_t written = 0, sz = pgsize * 2; | ||
| 149 | int i, n = pgcnt / 2, err = 0; | ||
| 150 | loff_t addr = ebnum * mtd->erasesize; | ||
| 151 | void *buf = iobuf; | ||
| 152 | |||
| 153 | for (i = 0; i < n; i++) { | ||
| 154 | err = mtd->write(mtd, addr, sz, &written, buf); | ||
| 155 | if (err || written != sz) { | ||
| 156 | printk(PRINT_PREF "error: write failed at %#llx\n", | ||
| 157 | addr); | ||
| 158 | if (!err) | ||
| 159 | err = -EINVAL; | ||
| 160 | return err; | ||
| 161 | } | ||
| 162 | addr += sz; | ||
| 163 | buf += sz; | ||
| 164 | } | ||
| 165 | if (pgcnt % 2) { | ||
| 166 | err = mtd->write(mtd, addr, pgsize, &written, buf); | ||
| 167 | if (err || written != pgsize) { | ||
| 168 | printk(PRINT_PREF "error: write failed at %#llx\n", | ||
| 169 | addr); | ||
| 170 | if (!err) | ||
| 171 | err = -EINVAL; | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 175 | return err; | ||
| 176 | } | ||
| 177 | |||
| 178 | static int read_eraseblock(int ebnum) | ||
| 179 | { | ||
| 180 | size_t read = 0; | ||
| 181 | int err = 0; | ||
| 182 | loff_t addr = ebnum * mtd->erasesize; | ||
| 183 | |||
| 184 | err = mtd->read(mtd, addr, mtd->erasesize, &read, iobuf); | ||
| 185 | /* Ignore corrected ECC errors */ | ||
| 186 | if (err == -EUCLEAN) | ||
| 187 | err = 0; | ||
| 188 | if (err || read != mtd->erasesize) { | ||
| 189 | printk(PRINT_PREF "error: read failed at %#llx\n", addr); | ||
| 190 | if (!err) | ||
| 191 | err = -EINVAL; | ||
| 192 | } | ||
| 193 | |||
| 194 | return err; | ||
| 195 | } | ||
| 196 | |||
| 197 | static int read_eraseblock_by_page(int ebnum) | ||
| 198 | { | ||
| 199 | size_t read = 0; | ||
| 200 | int i, err = 0; | ||
| 201 | loff_t addr = ebnum * mtd->erasesize; | ||
| 202 | void *buf = iobuf; | ||
| 203 | |||
| 204 | for (i = 0; i < pgcnt; i++) { | ||
| 205 | err = mtd->read(mtd, addr, pgsize, &read, buf); | ||
| 206 | /* Ignore corrected ECC errors */ | ||
| 207 | if (err == -EUCLEAN) | ||
| 208 | err = 0; | ||
| 209 | if (err || read != pgsize) { | ||
| 210 | printk(PRINT_PREF "error: read failed at %#llx\n", | ||
| 211 | addr); | ||
| 212 | if (!err) | ||
| 213 | err = -EINVAL; | ||
| 214 | break; | ||
| 215 | } | ||
| 216 | addr += pgsize; | ||
| 217 | buf += pgsize; | ||
| 218 | } | ||
| 219 | |||
| 220 | return err; | ||
| 221 | } | ||
| 222 | |||
| 223 | static int read_eraseblock_by_2pages(int ebnum) | ||
| 224 | { | ||
| 225 | size_t read = 0, sz = pgsize * 2; | ||
| 226 | int i, n = pgcnt / 2, err = 0; | ||
| 227 | loff_t addr = ebnum * mtd->erasesize; | ||
| 228 | void *buf = iobuf; | ||
| 229 | |||
| 230 | for (i = 0; i < n; i++) { | ||
| 231 | err = mtd->read(mtd, addr, sz, &read, buf); | ||
| 232 | /* Ignore corrected ECC errors */ | ||
| 233 | if (err == -EUCLEAN) | ||
| 234 | err = 0; | ||
| 235 | if (err || read != sz) { | ||
| 236 | printk(PRINT_PREF "error: read failed at %#llx\n", | ||
| 237 | addr); | ||
| 238 | if (!err) | ||
| 239 | err = -EINVAL; | ||
| 240 | return err; | ||
| 241 | } | ||
| 242 | addr += sz; | ||
| 243 | buf += sz; | ||
| 244 | } | ||
| 245 | if (pgcnt % 2) { | ||
| 246 | err = mtd->read(mtd, addr, pgsize, &read, buf); | ||
| 247 | /* Ignore corrected ECC errors */ | ||
| 248 | if (err == -EUCLEAN) | ||
| 249 | err = 0; | ||
| 250 | if (err || read != pgsize) { | ||
| 251 | printk(PRINT_PREF "error: read failed at %#llx\n", | ||
| 252 | addr); | ||
| 253 | if (!err) | ||
| 254 | err = -EINVAL; | ||
| 255 | } | ||
| 256 | } | ||
| 257 | |||
| 258 | return err; | ||
| 259 | } | ||
| 260 | |||
| 261 | static int is_block_bad(int ebnum) | ||
| 262 | { | ||
| 263 | loff_t addr = ebnum * mtd->erasesize; | ||
| 264 | int ret; | ||
| 265 | |||
| 266 | ret = mtd->block_isbad(mtd, addr); | ||
| 267 | if (ret) | ||
| 268 | printk(PRINT_PREF "block %d is bad\n", ebnum); | ||
| 269 | return ret; | ||
| 270 | } | ||
| 271 | |||
| 272 | static inline void start_timing(void) | ||
| 273 | { | ||
| 274 | do_gettimeofday(&start); | ||
| 275 | } | ||
| 276 | |||
| 277 | static inline void stop_timing(void) | ||
| 278 | { | ||
| 279 | do_gettimeofday(&finish); | ||
| 280 | } | ||
| 281 | |||
| 282 | static long calc_speed(void) | ||
| 283 | { | ||
| 284 | long ms, k, speed; | ||
| 285 | |||
| 286 | ms = (finish.tv_sec - start.tv_sec) * 1000 + | ||
| 287 | (finish.tv_usec - start.tv_usec) / 1000; | ||
| 288 | k = goodebcnt * mtd->erasesize / 1024; | ||
| 289 | speed = (k * 1000) / ms; | ||
| 290 | return speed; | ||
| 291 | } | ||
| 292 | |||
| 293 | static int scan_for_bad_eraseblocks(void) | ||
| 294 | { | ||
| 295 | int i, bad = 0; | ||
| 296 | |||
| 297 | bbt = kmalloc(ebcnt, GFP_KERNEL); | ||
| 298 | if (!bbt) { | ||
| 299 | printk(PRINT_PREF "error: cannot allocate memory\n"); | ||
| 300 | return -ENOMEM; | ||
| 301 | } | ||
| 302 | memset(bbt, 0 , ebcnt); | ||
| 303 | |||
| 304 | printk(PRINT_PREF "scanning for bad eraseblocks\n"); | ||
| 305 | for (i = 0; i < ebcnt; ++i) { | ||
| 306 | bbt[i] = is_block_bad(i) ? 1 : 0; | ||
| 307 | if (bbt[i]) | ||
| 308 | bad += 1; | ||
| 309 | cond_resched(); | ||
| 310 | } | ||
| 311 | printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad); | ||
| 312 | goodebcnt = ebcnt - bad; | ||
| 313 | return 0; | ||
| 314 | } | ||
| 315 | |||
| 316 | static int __init mtd_speedtest_init(void) | ||
| 317 | { | ||
| 318 | int err, i; | ||
| 319 | long speed; | ||
| 320 | uint64_t tmp; | ||
| 321 | |||
| 322 | printk(KERN_INFO "\n"); | ||
| 323 | printk(KERN_INFO "=================================================\n"); | ||
| 324 | printk(PRINT_PREF "MTD device: %d\n", dev); | ||
| 325 | |||
| 326 | mtd = get_mtd_device(NULL, dev); | ||
| 327 | if (IS_ERR(mtd)) { | ||
| 328 | err = PTR_ERR(mtd); | ||
| 329 | printk(PRINT_PREF "error: cannot get MTD device\n"); | ||
| 330 | return err; | ||
| 331 | } | ||
| 332 | |||
| 333 | if (mtd->writesize == 1) { | ||
| 334 | printk(PRINT_PREF "not NAND flash, assume page size is 512 " | ||
| 335 | "bytes.\n"); | ||
| 336 | pgsize = 512; | ||
| 337 | } else | ||
| 338 | pgsize = mtd->writesize; | ||
| 339 | |||
| 340 | tmp = mtd->size; | ||
| 341 | do_div(tmp, mtd->erasesize); | ||
| 342 | ebcnt = tmp; | ||
| 343 | pgcnt = mtd->erasesize / mtd->writesize; | ||
| 344 | |||
| 345 | printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, " | ||
| 346 | "page size %u, count of eraseblocks %u, pages per " | ||
| 347 | "eraseblock %u, OOB size %u\n", | ||
| 348 | (unsigned long long)mtd->size, mtd->erasesize, | ||
| 349 | pgsize, ebcnt, pgcnt, mtd->oobsize); | ||
| 350 | |||
| 351 | err = -ENOMEM; | ||
| 352 | iobuf = kmalloc(mtd->erasesize, GFP_KERNEL); | ||
| 353 | if (!iobuf) { | ||
| 354 | printk(PRINT_PREF "error: cannot allocate memory\n"); | ||
| 355 | goto out; | ||
| 356 | } | ||
| 357 | |||
| 358 | simple_srand(1); | ||
| 359 | set_random_data(iobuf, mtd->erasesize); | ||
| 360 | |||
| 361 | err = scan_for_bad_eraseblocks(); | ||
| 362 | if (err) | ||
| 363 | goto out; | ||
| 364 | |||
| 365 | err = erase_whole_device(); | ||
| 366 | if (err) | ||
| 367 | goto out; | ||
| 368 | |||
| 369 | /* Write all eraseblocks, 1 eraseblock at a time */ | ||
| 370 | printk(PRINT_PREF "testing eraseblock write speed\n"); | ||
| 371 | start_timing(); | ||
| 372 | for (i = 0; i < ebcnt; ++i) { | ||
| 373 | if (bbt[i]) | ||
| 374 | continue; | ||
| 375 | err = write_eraseblock(i); | ||
| 376 | if (err) | ||
| 377 | goto out; | ||
| 378 | cond_resched(); | ||
| 379 | } | ||
| 380 | stop_timing(); | ||
| 381 | speed = calc_speed(); | ||
| 382 | printk(PRINT_PREF "eraseblock write speed is %ld KiB/s\n", speed); | ||
| 383 | |||
| 384 | /* Read all eraseblocks, 1 eraseblock at a time */ | ||
| 385 | printk(PRINT_PREF "testing eraseblock read speed\n"); | ||
| 386 | start_timing(); | ||
| 387 | for (i = 0; i < ebcnt; ++i) { | ||
| 388 | if (bbt[i]) | ||
| 389 | continue; | ||
| 390 | err = read_eraseblock(i); | ||
| 391 | if (err) | ||
| 392 | goto out; | ||
| 393 | cond_resched(); | ||
| 394 | } | ||
| 395 | stop_timing(); | ||
| 396 | speed = calc_speed(); | ||
| 397 | printk(PRINT_PREF "eraseblock read speed is %ld KiB/s\n", speed); | ||
| 398 | |||
| 399 | err = erase_whole_device(); | ||
| 400 | if (err) | ||
| 401 | goto out; | ||
| 402 | |||
| 403 | /* Write all eraseblocks, 1 page at a time */ | ||
| 404 | printk(PRINT_PREF "testing page write speed\n"); | ||
| 405 | start_timing(); | ||
| 406 | for (i = 0; i < ebcnt; ++i) { | ||
| 407 | if (bbt[i]) | ||
| 408 | continue; | ||
| 409 | err = write_eraseblock_by_page(i); | ||
| 410 | if (err) | ||
| 411 | goto out; | ||
| 412 | cond_resched(); | ||
| 413 | } | ||
| 414 | stop_timing(); | ||
| 415 | speed = calc_speed(); | ||
| 416 | printk(PRINT_PREF "page write speed is %ld KiB/s\n", speed); | ||
| 417 | |||
| 418 | /* Read all eraseblocks, 1 page at a time */ | ||
| 419 | printk(PRINT_PREF "testing page read speed\n"); | ||
| 420 | start_timing(); | ||
| 421 | for (i = 0; i < ebcnt; ++i) { | ||
| 422 | if (bbt[i]) | ||
| 423 | continue; | ||
| 424 | err = read_eraseblock_by_page(i); | ||
| 425 | if (err) | ||
| 426 | goto out; | ||
| 427 | cond_resched(); | ||
| 428 | } | ||
| 429 | stop_timing(); | ||
| 430 | speed = calc_speed(); | ||
| 431 | printk(PRINT_PREF "page read speed is %ld KiB/s\n", speed); | ||
| 432 | |||
| 433 | err = erase_whole_device(); | ||
| 434 | if (err) | ||
| 435 | goto out; | ||
| 436 | |||
| 437 | /* Write all eraseblocks, 2 pages at a time */ | ||
| 438 | printk(PRINT_PREF "testing 2 page write speed\n"); | ||
| 439 | start_timing(); | ||
| 440 | for (i = 0; i < ebcnt; ++i) { | ||
| 441 | if (bbt[i]) | ||
| 442 | continue; | ||
| 443 | err = write_eraseblock_by_2pages(i); | ||
| 444 | if (err) | ||
| 445 | goto out; | ||
| 446 | cond_resched(); | ||
| 447 | } | ||
| 448 | stop_timing(); | ||
| 449 | speed = calc_speed(); | ||
| 450 | printk(PRINT_PREF "2 page write speed is %ld KiB/s\n", speed); | ||
| 451 | |||
| 452 | /* Read all eraseblocks, 2 pages at a time */ | ||
| 453 | printk(PRINT_PREF "testing 2 page read speed\n"); | ||
| 454 | start_timing(); | ||
| 455 | for (i = 0; i < ebcnt; ++i) { | ||
| 456 | if (bbt[i]) | ||
| 457 | continue; | ||
| 458 | err = read_eraseblock_by_2pages(i); | ||
| 459 | if (err) | ||
| 460 | goto out; | ||
| 461 | cond_resched(); | ||
| 462 | } | ||
| 463 | stop_timing(); | ||
| 464 | speed = calc_speed(); | ||
| 465 | printk(PRINT_PREF "2 page read speed is %ld KiB/s\n", speed); | ||
| 466 | |||
| 467 | /* Erase all eraseblocks */ | ||
| 468 | printk(PRINT_PREF "Testing erase speed\n"); | ||
| 469 | start_timing(); | ||
| 470 | for (i = 0; i < ebcnt; ++i) { | ||
| 471 | if (bbt[i]) | ||
| 472 | continue; | ||
| 473 | err = erase_eraseblock(i); | ||
| 474 | if (err) | ||
| 475 | goto out; | ||
| 476 | cond_resched(); | ||
| 477 | } | ||
| 478 | stop_timing(); | ||
| 479 | speed = calc_speed(); | ||
| 480 | printk(PRINT_PREF "erase speed is %ld KiB/s\n", speed); | ||
| 481 | |||
| 482 | printk(PRINT_PREF "finished\n"); | ||
| 483 | out: | ||
| 484 | kfree(iobuf); | ||
| 485 | kfree(bbt); | ||
| 486 | put_mtd_device(mtd); | ||
| 487 | if (err) | ||
| 488 | printk(PRINT_PREF "error %d occurred\n", err); | ||
| 489 | printk(KERN_INFO "=================================================\n"); | ||
| 490 | return err; | ||
| 491 | } | ||
| 492 | module_init(mtd_speedtest_init); | ||
| 493 | |||
| 494 | static void __exit mtd_speedtest_exit(void) | ||
| 495 | { | ||
| 496 | return; | ||
| 497 | } | ||
| 498 | module_exit(mtd_speedtest_exit); | ||
| 499 | |||
| 500 | MODULE_DESCRIPTION("Speed test module"); | ||
| 501 | MODULE_AUTHOR("Adrian Hunter"); | ||
| 502 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/mtd/tests/mtd_stresstest.c b/drivers/mtd/tests/mtd_stresstest.c new file mode 100644 index 000000000000..63920476b57a --- /dev/null +++ b/drivers/mtd/tests/mtd_stresstest.c | |||
| @@ -0,0 +1,330 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2006-2008 Nokia Corporation | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify it | ||
| 5 | * under the terms of the GNU General Public License version 2 as published by | ||
| 6 | * the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | * | ||
| 13 | * You should have received a copy of the GNU General Public License along with | ||
| 14 | * this program; see the file COPYING. If not, write to the Free Software | ||
| 15 | * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
| 16 | * | ||
| 17 | * Test random reads, writes and erases on MTD device. | ||
| 18 | * | ||
| 19 | * Author: Adrian Hunter <ext-adrian.hunter@nokia.com> | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <linux/init.h> | ||
| 23 | #include <linux/module.h> | ||
| 24 | #include <linux/moduleparam.h> | ||
| 25 | #include <linux/err.h> | ||
| 26 | #include <linux/mtd/mtd.h> | ||
| 27 | #include <linux/sched.h> | ||
| 28 | #include <linux/vmalloc.h> | ||
| 29 | |||
| 30 | #define PRINT_PREF KERN_INFO "mtd_stresstest: " | ||
| 31 | |||
| 32 | static int dev; | ||
| 33 | module_param(dev, int, S_IRUGO); | ||
| 34 | MODULE_PARM_DESC(dev, "MTD device number to use"); | ||
| 35 | |||
| 36 | static int count = 10000; | ||
| 37 | module_param(count, int, S_IRUGO); | ||
| 38 | MODULE_PARM_DESC(count, "Number of operations to do (default is 10000)"); | ||
| 39 | |||
| 40 | static struct mtd_info *mtd; | ||
| 41 | static unsigned char *writebuf; | ||
| 42 | static unsigned char *readbuf; | ||
| 43 | static unsigned char *bbt; | ||
| 44 | static int *offsets; | ||
| 45 | |||
| 46 | static int pgsize; | ||
| 47 | static int bufsize; | ||
| 48 | static int ebcnt; | ||
| 49 | static int pgcnt; | ||
| 50 | static unsigned long next = 1; | ||
| 51 | |||
| 52 | static inline unsigned int simple_rand(void) | ||
| 53 | { | ||
| 54 | next = next * 1103515245 + 12345; | ||
| 55 | return (unsigned int)((next / 65536) % 32768); | ||
| 56 | } | ||
| 57 | |||
| 58 | static inline void simple_srand(unsigned long seed) | ||
| 59 | { | ||
| 60 | next = seed; | ||
| 61 | } | ||
| 62 | |||
| 63 | static int rand_eb(void) | ||
| 64 | { | ||
| 65 | int eb; | ||
| 66 | |||
| 67 | again: | ||
| 68 | if (ebcnt < 32768) | ||
| 69 | eb = simple_rand(); | ||
| 70 | else | ||
| 71 | eb = (simple_rand() << 15) | simple_rand(); | ||
| 72 | /* Read or write up 2 eraseblocks at a time - hence 'ebcnt - 1' */ | ||
| 73 | eb %= (ebcnt - 1); | ||
| 74 | if (bbt[eb]) | ||
| 75 | goto again; | ||
| 76 | return eb; | ||
| 77 | } | ||
| 78 | |||
| 79 | static int rand_offs(void) | ||
| 80 | { | ||
| 81 | int offs; | ||
| 82 | |||
| 83 | if (bufsize < 32768) | ||
| 84 | offs = simple_rand(); | ||
| 85 | else | ||
| 86 | offs = (simple_rand() << 15) | simple_rand(); | ||
| 87 | offs %= bufsize; | ||
| 88 | return offs; | ||
| 89 | } | ||
| 90 | |||
| 91 | static int rand_len(int offs) | ||
| 92 | { | ||
| 93 | int len; | ||
| 94 | |||
| 95 | if (bufsize < 32768) | ||
| 96 | len = simple_rand(); | ||
| 97 | else | ||
| 98 | len = (simple_rand() << 15) | simple_rand(); | ||
| 99 | len %= (bufsize - offs); | ||
| 100 | return len; | ||
| 101 | } | ||
| 102 | |||
| 103 | static int erase_eraseblock(int ebnum) | ||
| 104 | { | ||
| 105 | int err; | ||
| 106 | struct erase_info ei; | ||
| 107 | loff_t addr = ebnum * mtd->erasesize; | ||
| 108 | |||
| 109 | memset(&ei, 0, sizeof(struct erase_info)); | ||
| 110 | ei.mtd = mtd; | ||
| 111 | ei.addr = addr; | ||
| 112 | ei.len = mtd->erasesize; | ||
| 113 | |||
| 114 | err = mtd->erase(mtd, &ei); | ||
| 115 | if (unlikely(err)) { | ||
| 116 | printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum); | ||
| 117 | return err; | ||
| 118 | } | ||
| 119 | |||
| 120 | if (unlikely(ei.state == MTD_ERASE_FAILED)) { | ||
| 121 | printk(PRINT_PREF "some erase error occurred at EB %d\n", | ||
| 122 | ebnum); | ||
| 123 | return -EIO; | ||
| 124 | } | ||
| 125 | |||
| 126 | return 0; | ||
| 127 | } | ||
| 128 | |||
| 129 | static int is_block_bad(int ebnum) | ||
| 130 | { | ||
| 131 | loff_t addr = ebnum * mtd->erasesize; | ||
| 132 | int ret; | ||
| 133 | |||
| 134 | ret = mtd->block_isbad(mtd, addr); | ||
| 135 | if (ret) | ||
| 136 | printk(PRINT_PREF "block %d is bad\n", ebnum); | ||
| 137 | return ret; | ||
| 138 | } | ||
| 139 | |||
| 140 | static int do_read(void) | ||
| 141 | { | ||
| 142 | size_t read = 0; | ||
| 143 | int eb = rand_eb(); | ||
| 144 | int offs = rand_offs(); | ||
| 145 | int len = rand_len(offs), err; | ||
| 146 | loff_t addr; | ||
| 147 | |||
| 148 | if (bbt[eb + 1]) { | ||
| 149 | if (offs >= mtd->erasesize) | ||
| 150 | offs -= mtd->erasesize; | ||
| 151 | if (offs + len > mtd->erasesize) | ||
| 152 | len = mtd->erasesize - offs; | ||
| 153 | } | ||
| 154 | addr = eb * mtd->erasesize + offs; | ||
| 155 | err = mtd->read(mtd, addr, len, &read, readbuf); | ||
| 156 | if (err == -EUCLEAN) | ||
| 157 | err = 0; | ||
| 158 | if (unlikely(err || read != len)) { | ||
| 159 | printk(PRINT_PREF "error: read failed at 0x%llx\n", | ||
| 160 | (long long)addr); | ||
| 161 | if (!err) | ||
| 162 | err = -EINVAL; | ||
| 163 | return err; | ||
| 164 | } | ||
| 165 | return 0; | ||
| 166 | } | ||
| 167 | |||
| 168 | static int do_write(void) | ||
| 169 | { | ||
| 170 | int eb = rand_eb(), offs, err, len; | ||
| 171 | size_t written = 0; | ||
| 172 | loff_t addr; | ||
| 173 | |||
| 174 | offs = offsets[eb]; | ||
| 175 | if (offs >= mtd->erasesize) { | ||
| 176 | err = erase_eraseblock(eb); | ||
| 177 | if (err) | ||
| 178 | return err; | ||
| 179 | offs = offsets[eb] = 0; | ||
| 180 | } | ||
| 181 | len = rand_len(offs); | ||
| 182 | len = ((len + pgsize - 1) / pgsize) * pgsize; | ||
| 183 | if (offs + len > mtd->erasesize) { | ||
| 184 | if (bbt[eb + 1]) | ||
| 185 | len = mtd->erasesize - offs; | ||
| 186 | else { | ||
| 187 | err = erase_eraseblock(eb + 1); | ||
| 188 | if (err) | ||
| 189 | return err; | ||
| 190 | offsets[eb + 1] = 0; | ||
| 191 | } | ||
| 192 | } | ||
| 193 | addr = eb * mtd->erasesize + offs; | ||
| 194 | err = mtd->write(mtd, addr, len, &written, writebuf); | ||
| 195 | if (unlikely(err || written != len)) { | ||
| 196 | printk(PRINT_PREF "error: write failed at 0x%llx\n", | ||
| 197 | (long long)addr); | ||
| 198 | if (!err) | ||
| 199 | err = -EINVAL; | ||
| 200 | return err; | ||
| 201 | } | ||
| 202 | offs += len; | ||
| 203 | while (offs > mtd->erasesize) { | ||
| 204 | offsets[eb++] = mtd->erasesize; | ||
| 205 | offs -= mtd->erasesize; | ||
| 206 | } | ||
| 207 | offsets[eb] = offs; | ||
| 208 | return 0; | ||
| 209 | } | ||
| 210 | |||
| 211 | static int do_operation(void) | ||
| 212 | { | ||
| 213 | if (simple_rand() & 1) | ||
| 214 | return do_read(); | ||
| 215 | else | ||
| 216 | return do_write(); | ||
| 217 | } | ||
| 218 | |||
| 219 | static int scan_for_bad_eraseblocks(void) | ||
| 220 | { | ||
| 221 | int i, bad = 0; | ||
| 222 | |||
| 223 | bbt = kmalloc(ebcnt, GFP_KERNEL); | ||
| 224 | if (!bbt) { | ||
| 225 | printk(PRINT_PREF "error: cannot allocate memory\n"); | ||
| 226 | return -ENOMEM; | ||
| 227 | } | ||
| 228 | memset(bbt, 0 , ebcnt); | ||
| 229 | |||
| 230 | printk(PRINT_PREF "scanning for bad eraseblocks\n"); | ||
| 231 | for (i = 0; i < ebcnt; ++i) { | ||
| 232 | bbt[i] = is_block_bad(i) ? 1 : 0; | ||
| 233 | if (bbt[i]) | ||
| 234 | bad += 1; | ||
| 235 | cond_resched(); | ||
| 236 | } | ||
| 237 | printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad); | ||
| 238 | return 0; | ||
| 239 | } | ||
| 240 | |||
| 241 | static int __init mtd_stresstest_init(void) | ||
| 242 | { | ||
| 243 | int err; | ||
| 244 | int i, op; | ||
| 245 | uint64_t tmp; | ||
| 246 | |||
| 247 | printk(KERN_INFO "\n"); | ||
| 248 | printk(KERN_INFO "=================================================\n"); | ||
| 249 | printk(PRINT_PREF "MTD device: %d\n", dev); | ||
| 250 | |||
| 251 | mtd = get_mtd_device(NULL, dev); | ||
| 252 | if (IS_ERR(mtd)) { | ||
| 253 | err = PTR_ERR(mtd); | ||
| 254 | printk(PRINT_PREF "error: cannot get MTD device\n"); | ||
| 255 | return err; | ||
| 256 | } | ||
| 257 | |||
| 258 | if (mtd->writesize == 1) { | ||
| 259 | printk(PRINT_PREF "not NAND flash, assume page size is 512 " | ||
| 260 | "bytes.\n"); | ||
| 261 | pgsize = 512; | ||
| 262 | } else | ||
| 263 | pgsize = mtd->writesize; | ||
| 264 | |||
| 265 | tmp = mtd->size; | ||
| 266 | do_div(tmp, mtd->erasesize); | ||
| 267 | ebcnt = tmp; | ||
| 268 | pgcnt = mtd->erasesize / mtd->writesize; | ||
| 269 | |||
| 270 | printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, " | ||
| 271 | "page size %u, count of eraseblocks %u, pages per " | ||
| 272 | "eraseblock %u, OOB size %u\n", | ||
| 273 | (unsigned long long)mtd->size, mtd->erasesize, | ||
| 274 | pgsize, ebcnt, pgcnt, mtd->oobsize); | ||
| 275 | |||
| 276 | /* Read or write up 2 eraseblocks at a time */ | ||
| 277 | bufsize = mtd->erasesize * 2; | ||
| 278 | |||
| 279 | err = -ENOMEM; | ||
| 280 | readbuf = vmalloc(bufsize); | ||
| 281 | writebuf = vmalloc(bufsize); | ||
| 282 | offsets = kmalloc(ebcnt * sizeof(int), GFP_KERNEL); | ||
| 283 | if (!readbuf || !writebuf || !offsets) { | ||
| 284 | printk(PRINT_PREF "error: cannot allocate memory\n"); | ||
| 285 | goto out; | ||
| 286 | } | ||
| 287 | for (i = 0; i < ebcnt; i++) | ||
| 288 | offsets[i] = mtd->erasesize; | ||
| 289 | simple_srand(current->pid); | ||
| 290 | for (i = 0; i < bufsize; i++) | ||
| 291 | writebuf[i] = simple_rand(); | ||
| 292 | |||
| 293 | err = scan_for_bad_eraseblocks(); | ||
| 294 | if (err) | ||
| 295 | goto out; | ||
| 296 | |||
| 297 | /* Do operations */ | ||
| 298 | printk(PRINT_PREF "doing operations\n"); | ||
| 299 | for (op = 0; op < count; op++) { | ||
| 300 | if ((op & 1023) == 0) | ||
| 301 | printk(PRINT_PREF "%d operations done\n", op); | ||
| 302 | err = do_operation(); | ||
| 303 | if (err) | ||
| 304 | goto out; | ||
| 305 | cond_resched(); | ||
| 306 | } | ||
| 307 | printk(PRINT_PREF "finished, %d operations done\n", op); | ||
| 308 | |||
| 309 | out: | ||
| 310 | kfree(offsets); | ||
| 311 | kfree(bbt); | ||
| 312 | vfree(writebuf); | ||
| 313 | vfree(readbuf); | ||
| 314 | put_mtd_device(mtd); | ||
| 315 | if (err) | ||
| 316 | printk(PRINT_PREF "error %d occurred\n", err); | ||
| 317 | printk(KERN_INFO "=================================================\n"); | ||
| 318 | return err; | ||
| 319 | } | ||
| 320 | module_init(mtd_stresstest_init); | ||
| 321 | |||
| 322 | static void __exit mtd_stresstest_exit(void) | ||
| 323 | { | ||
| 324 | return; | ||
| 325 | } | ||
| 326 | module_exit(mtd_stresstest_exit); | ||
| 327 | |||
| 328 | MODULE_DESCRIPTION("Stress test module"); | ||
| 329 | MODULE_AUTHOR("Adrian Hunter"); | ||
| 330 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/mtd/tests/mtd_subpagetest.c b/drivers/mtd/tests/mtd_subpagetest.c new file mode 100644 index 000000000000..5b889724268e --- /dev/null +++ b/drivers/mtd/tests/mtd_subpagetest.c | |||
| @@ -0,0 +1,525 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2006-2007 Nokia Corporation | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify it | ||
| 5 | * under the terms of the GNU General Public License version 2 as published by | ||
| 6 | * the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | * | ||
| 13 | * You should have received a copy of the GNU General Public License along with | ||
| 14 | * this program; see the file COPYING. If not, write to the Free Software | ||
| 15 | * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
| 16 | * | ||
| 17 | * Test sub-page read and write on MTD device. | ||
| 18 | * Author: Adrian Hunter <ext-adrian.hunter@nokia.com> | ||
| 19 | * | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <linux/init.h> | ||
| 23 | #include <linux/module.h> | ||
| 24 | #include <linux/moduleparam.h> | ||
| 25 | #include <linux/err.h> | ||
| 26 | #include <linux/mtd/mtd.h> | ||
| 27 | #include <linux/sched.h> | ||
| 28 | |||
| 29 | #define PRINT_PREF KERN_INFO "mtd_subpagetest: " | ||
| 30 | |||
| 31 | static int dev; | ||
| 32 | module_param(dev, int, S_IRUGO); | ||
| 33 | MODULE_PARM_DESC(dev, "MTD device number to use"); | ||
| 34 | |||
| 35 | static struct mtd_info *mtd; | ||
| 36 | static unsigned char *writebuf; | ||
| 37 | static unsigned char *readbuf; | ||
| 38 | static unsigned char *bbt; | ||
| 39 | |||
| 40 | static int subpgsize; | ||
| 41 | static int bufsize; | ||
| 42 | static int ebcnt; | ||
| 43 | static int pgcnt; | ||
| 44 | static int errcnt; | ||
| 45 | static unsigned long next = 1; | ||
| 46 | |||
| 47 | static inline unsigned int simple_rand(void) | ||
| 48 | { | ||
| 49 | next = next * 1103515245 + 12345; | ||
| 50 | return (unsigned int)((next / 65536) % 32768); | ||
| 51 | } | ||
| 52 | |||
| 53 | static inline void simple_srand(unsigned long seed) | ||
| 54 | { | ||
| 55 | next = seed; | ||
| 56 | } | ||
| 57 | |||
| 58 | static void set_random_data(unsigned char *buf, size_t len) | ||
| 59 | { | ||
| 60 | size_t i; | ||
| 61 | |||
| 62 | for (i = 0; i < len; ++i) | ||
| 63 | buf[i] = simple_rand(); | ||
| 64 | } | ||
| 65 | |||
| 66 | static inline void clear_data(unsigned char *buf, size_t len) | ||
| 67 | { | ||
| 68 | memset(buf, 0, len); | ||
| 69 | } | ||
| 70 | |||
| 71 | static int erase_eraseblock(int ebnum) | ||
| 72 | { | ||
| 73 | int err; | ||
| 74 | struct erase_info ei; | ||
| 75 | loff_t addr = ebnum * mtd->erasesize; | ||
| 76 | |||
| 77 | memset(&ei, 0, sizeof(struct erase_info)); | ||
| 78 | ei.mtd = mtd; | ||
| 79 | ei.addr = addr; | ||
| 80 | ei.len = mtd->erasesize; | ||
| 81 | |||
| 82 | err = mtd->erase(mtd, &ei); | ||
| 83 | if (err) { | ||
| 84 | printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum); | ||
| 85 | return err; | ||
| 86 | } | ||
| 87 | |||
| 88 | if (ei.state == MTD_ERASE_FAILED) { | ||
| 89 | printk(PRINT_PREF "some erase error occurred at EB %d\n", | ||
| 90 | ebnum); | ||
| 91 | return -EIO; | ||
| 92 | } | ||
| 93 | |||
| 94 | return 0; | ||
| 95 | } | ||
| 96 | |||
| 97 | static int erase_whole_device(void) | ||
| 98 | { | ||
| 99 | int err; | ||
| 100 | unsigned int i; | ||
| 101 | |||
| 102 | printk(PRINT_PREF "erasing whole device\n"); | ||
| 103 | for (i = 0; i < ebcnt; ++i) { | ||
| 104 | if (bbt[i]) | ||
| 105 | continue; | ||
| 106 | err = erase_eraseblock(i); | ||
| 107 | if (err) | ||
| 108 | return err; | ||
| 109 | cond_resched(); | ||
| 110 | } | ||
| 111 | printk(PRINT_PREF "erased %u eraseblocks\n", i); | ||
| 112 | return 0; | ||
| 113 | } | ||
| 114 | |||
| 115 | static int write_eraseblock(int ebnum) | ||
| 116 | { | ||
| 117 | size_t written = 0; | ||
| 118 | int err = 0; | ||
| 119 | loff_t addr = ebnum * mtd->erasesize; | ||
| 120 | |||
| 121 | set_random_data(writebuf, subpgsize); | ||
| 122 | err = mtd->write(mtd, addr, subpgsize, &written, writebuf); | ||
| 123 | if (unlikely(err || written != subpgsize)) { | ||
| 124 | printk(PRINT_PREF "error: write failed at %#llx\n", | ||
| 125 | (long long)addr); | ||
| 126 | if (written != subpgsize) { | ||
| 127 | printk(PRINT_PREF " write size: %#x\n", subpgsize); | ||
| 128 | printk(PRINT_PREF " written: %#zx\n", written); | ||
| 129 | } | ||
| 130 | return err ? err : -1; | ||
| 131 | } | ||
| 132 | |||
| 133 | addr += subpgsize; | ||
| 134 | |||
| 135 | set_random_data(writebuf, subpgsize); | ||
| 136 | err = mtd->write(mtd, addr, subpgsize, &written, writebuf); | ||
| 137 | if (unlikely(err || written != subpgsize)) { | ||
| 138 | printk(PRINT_PREF "error: write failed at %#llx\n", | ||
| 139 | (long long)addr); | ||
| 140 | if (written != subpgsize) { | ||
| 141 | printk(PRINT_PREF " write size: %#x\n", subpgsize); | ||
| 142 | printk(PRINT_PREF " written: %#zx\n", written); | ||
| 143 | } | ||
| 144 | return err ? err : -1; | ||
| 145 | } | ||
| 146 | |||
| 147 | return err; | ||
| 148 | } | ||
| 149 | |||
| 150 | static int write_eraseblock2(int ebnum) | ||
| 151 | { | ||
| 152 | size_t written = 0; | ||
| 153 | int err = 0, k; | ||
| 154 | loff_t addr = ebnum * mtd->erasesize; | ||
| 155 | |||
| 156 | for (k = 1; k < 33; ++k) { | ||
| 157 | if (addr + (subpgsize * k) > (ebnum + 1) * mtd->erasesize) | ||
| 158 | break; | ||
| 159 | set_random_data(writebuf, subpgsize * k); | ||
| 160 | err = mtd->write(mtd, addr, subpgsize * k, &written, writebuf); | ||
| 161 | if (unlikely(err || written != subpgsize * k)) { | ||
| 162 | printk(PRINT_PREF "error: write failed at %#llx\n", | ||
| 163 | (long long)addr); | ||
| 164 | if (written != subpgsize) { | ||
| 165 | printk(PRINT_PREF " write size: %#x\n", | ||
| 166 | subpgsize * k); | ||
| 167 | printk(PRINT_PREF " written: %#08zx\n", | ||
| 168 | written); | ||
| 169 | } | ||
| 170 | return err ? err : -1; | ||
| 171 | } | ||
| 172 | addr += subpgsize * k; | ||
| 173 | } | ||
| 174 | |||
| 175 | return err; | ||
| 176 | } | ||
| 177 | |||
| 178 | static void print_subpage(unsigned char *p) | ||
| 179 | { | ||
| 180 | int i, j; | ||
| 181 | |||
| 182 | for (i = 0; i < subpgsize; ) { | ||
| 183 | for (j = 0; i < subpgsize && j < 32; ++i, ++j) | ||
| 184 | printk("%02x", *p++); | ||
| 185 | printk("\n"); | ||
| 186 | } | ||
| 187 | } | ||
| 188 | |||
| 189 | static int verify_eraseblock(int ebnum) | ||
| 190 | { | ||
| 191 | size_t read = 0; | ||
| 192 | int err = 0; | ||
| 193 | loff_t addr = ebnum * mtd->erasesize; | ||
| 194 | |||
| 195 | set_random_data(writebuf, subpgsize); | ||
| 196 | clear_data(readbuf, subpgsize); | ||
| 197 | read = 0; | ||
| 198 | err = mtd->read(mtd, addr, subpgsize, &read, readbuf); | ||
| 199 | if (unlikely(err || read != subpgsize)) { | ||
| 200 | if (err == -EUCLEAN && read == subpgsize) { | ||
| 201 | printk(PRINT_PREF "ECC correction at %#llx\n", | ||
| 202 | (long long)addr); | ||
| 203 | err = 0; | ||
| 204 | } else { | ||
| 205 | printk(PRINT_PREF "error: read failed at %#llx\n", | ||
| 206 | (long long)addr); | ||
| 207 | return err ? err : -1; | ||
| 208 | } | ||
| 209 | } | ||
| 210 | if (unlikely(memcmp(readbuf, writebuf, subpgsize))) { | ||
| 211 | printk(PRINT_PREF "error: verify failed at %#llx\n", | ||
| 212 | (long long)addr); | ||
| 213 | printk(PRINT_PREF "------------- written----------------\n"); | ||
| 214 | print_subpage(writebuf); | ||
| 215 | printk(PRINT_PREF "------------- read ------------------\n"); | ||
| 216 | print_subpage(readbuf); | ||
| 217 | printk(PRINT_PREF "-------------------------------------\n"); | ||
| 218 | errcnt += 1; | ||
| 219 | } | ||
| 220 | |||
| 221 | addr += subpgsize; | ||
| 222 | |||
| 223 | set_random_data(writebuf, subpgsize); | ||
| 224 | clear_data(readbuf, subpgsize); | ||
| 225 | read = 0; | ||
| 226 | err = mtd->read(mtd, addr, subpgsize, &read, readbuf); | ||
| 227 | if (unlikely(err || read != subpgsize)) { | ||
| 228 | if (err == -EUCLEAN && read == subpgsize) { | ||
| 229 | printk(PRINT_PREF "ECC correction at %#llx\n", | ||
| 230 | (long long)addr); | ||
| 231 | err = 0; | ||
| 232 | } else { | ||
| 233 | printk(PRINT_PREF "error: read failed at %#llx\n", | ||
| 234 | (long long)addr); | ||
| 235 | return err ? err : -1; | ||
| 236 | } | ||
| 237 | } | ||
| 238 | if (unlikely(memcmp(readbuf, writebuf, subpgsize))) { | ||
| 239 | printk(PRINT_PREF "error: verify failed at %#llx\n", | ||
| 240 | (long long)addr); | ||
| 241 | printk(PRINT_PREF "------------- written----------------\n"); | ||
| 242 | print_subpage(writebuf); | ||
| 243 | printk(PRINT_PREF "------------- read ------------------\n"); | ||
| 244 | print_subpage(readbuf); | ||
| 245 | printk(PRINT_PREF "-------------------------------------\n"); | ||
| 246 | errcnt += 1; | ||
| 247 | } | ||
| 248 | |||
| 249 | return err; | ||
| 250 | } | ||
| 251 | |||
| 252 | static int verify_eraseblock2(int ebnum) | ||
| 253 | { | ||
| 254 | size_t read = 0; | ||
| 255 | int err = 0, k; | ||
| 256 | loff_t addr = ebnum * mtd->erasesize; | ||
| 257 | |||
| 258 | for (k = 1; k < 33; ++k) { | ||
| 259 | if (addr + (subpgsize * k) > (ebnum + 1) * mtd->erasesize) | ||
| 260 | break; | ||
| 261 | set_random_data(writebuf, subpgsize * k); | ||
| 262 | clear_data(readbuf, subpgsize * k); | ||
| 263 | read = 0; | ||
| 264 | err = mtd->read(mtd, addr, subpgsize * k, &read, readbuf); | ||
| 265 | if (unlikely(err || read != subpgsize * k)) { | ||
| 266 | if (err == -EUCLEAN && read == subpgsize * k) { | ||
| 267 | printk(PRINT_PREF "ECC correction at %#llx\n", | ||
| 268 | (long long)addr); | ||
| 269 | err = 0; | ||
| 270 | } else { | ||
| 271 | printk(PRINT_PREF "error: read failed at " | ||
| 272 | "%#llx\n", (long long)addr); | ||
| 273 | return err ? err : -1; | ||
| 274 | } | ||
| 275 | } | ||
| 276 | if (unlikely(memcmp(readbuf, writebuf, subpgsize * k))) { | ||
| 277 | printk(PRINT_PREF "error: verify failed at %#llx\n", | ||
| 278 | (long long)addr); | ||
| 279 | errcnt += 1; | ||
| 280 | } | ||
| 281 | addr += subpgsize * k; | ||
| 282 | } | ||
| 283 | |||
| 284 | return err; | ||
| 285 | } | ||
| 286 | |||
| 287 | static int verify_eraseblock_ff(int ebnum) | ||
| 288 | { | ||
| 289 | uint32_t j; | ||
| 290 | size_t read = 0; | ||
| 291 | int err = 0; | ||
| 292 | loff_t addr = ebnum * mtd->erasesize; | ||
| 293 | |||
| 294 | memset(writebuf, 0xff, subpgsize); | ||
| 295 | for (j = 0; j < mtd->erasesize / subpgsize; ++j) { | ||
| 296 | clear_data(readbuf, subpgsize); | ||
| 297 | read = 0; | ||
| 298 | err = mtd->read(mtd, addr, subpgsize, &read, readbuf); | ||
| 299 | if (unlikely(err || read != subpgsize)) { | ||
| 300 | if (err == -EUCLEAN && read == subpgsize) { | ||
| 301 | printk(PRINT_PREF "ECC correction at %#llx\n", | ||
| 302 | (long long)addr); | ||
| 303 | err = 0; | ||
| 304 | } else { | ||
| 305 | printk(PRINT_PREF "error: read failed at " | ||
| 306 | "%#llx\n", (long long)addr); | ||
| 307 | return err ? err : -1; | ||
| 308 | } | ||
| 309 | } | ||
| 310 | if (unlikely(memcmp(readbuf, writebuf, subpgsize))) { | ||
| 311 | printk(PRINT_PREF "error: verify 0xff failed at " | ||
| 312 | "%#llx\n", (long long)addr); | ||
| 313 | errcnt += 1; | ||
| 314 | } | ||
| 315 | addr += subpgsize; | ||
| 316 | } | ||
| 317 | |||
| 318 | return err; | ||
| 319 | } | ||
| 320 | |||
| 321 | static int verify_all_eraseblocks_ff(void) | ||
| 322 | { | ||
| 323 | int err; | ||
| 324 | unsigned int i; | ||
| 325 | |||
| 326 | printk(PRINT_PREF "verifying all eraseblocks for 0xff\n"); | ||
| 327 | for (i = 0; i < ebcnt; ++i) { | ||
| 328 | if (bbt[i]) | ||
| 329 | continue; | ||
| 330 | err = verify_eraseblock_ff(i); | ||
| 331 | if (err) | ||
| 332 | return err; | ||
| 333 | if (i % 256 == 0) | ||
| 334 | printk(PRINT_PREF "verified up to eraseblock %u\n", i); | ||
| 335 | cond_resched(); | ||
| 336 | } | ||
| 337 | printk(PRINT_PREF "verified %u eraseblocks\n", i); | ||
| 338 | return 0; | ||
| 339 | } | ||
| 340 | |||
| 341 | static int is_block_bad(int ebnum) | ||
| 342 | { | ||
| 343 | loff_t addr = ebnum * mtd->erasesize; | ||
| 344 | int ret; | ||
| 345 | |||
| 346 | ret = mtd->block_isbad(mtd, addr); | ||
| 347 | if (ret) | ||
| 348 | printk(PRINT_PREF "block %d is bad\n", ebnum); | ||
| 349 | return ret; | ||
| 350 | } | ||
| 351 | |||
| 352 | static int scan_for_bad_eraseblocks(void) | ||
| 353 | { | ||
| 354 | int i, bad = 0; | ||
| 355 | |||
| 356 | bbt = kmalloc(ebcnt, GFP_KERNEL); | ||
| 357 | if (!bbt) { | ||
| 358 | printk(PRINT_PREF "error: cannot allocate memory\n"); | ||
| 359 | return -ENOMEM; | ||
| 360 | } | ||
| 361 | memset(bbt, 0 , ebcnt); | ||
| 362 | |||
| 363 | printk(PRINT_PREF "scanning for bad eraseblocks\n"); | ||
| 364 | for (i = 0; i < ebcnt; ++i) { | ||
| 365 | bbt[i] = is_block_bad(i) ? 1 : 0; | ||
| 366 | if (bbt[i]) | ||
| 367 | bad += 1; | ||
| 368 | cond_resched(); | ||
| 369 | } | ||
| 370 | printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad); | ||
| 371 | return 0; | ||
| 372 | } | ||
| 373 | |||
| 374 | static int __init mtd_subpagetest_init(void) | ||
| 375 | { | ||
| 376 | int err = 0; | ||
| 377 | uint32_t i; | ||
| 378 | uint64_t tmp; | ||
| 379 | |||
| 380 | printk(KERN_INFO "\n"); | ||
| 381 | printk(KERN_INFO "=================================================\n"); | ||
| 382 | printk(PRINT_PREF "MTD device: %d\n", dev); | ||
| 383 | |||
| 384 | mtd = get_mtd_device(NULL, dev); | ||
| 385 | if (IS_ERR(mtd)) { | ||
| 386 | err = PTR_ERR(mtd); | ||
| 387 | printk(PRINT_PREF "error: cannot get MTD device\n"); | ||
| 388 | return err; | ||
| 389 | } | ||
| 390 | |||
| 391 | if (mtd->type != MTD_NANDFLASH) { | ||
| 392 | printk(PRINT_PREF "this test requires NAND flash\n"); | ||
| 393 | goto out; | ||
| 394 | } | ||
| 395 | |||
| 396 | subpgsize = mtd->writesize >> mtd->subpage_sft; | ||
| 397 | printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, " | ||
| 398 | "page size %u, subpage size %u, count of eraseblocks %u, " | ||
| 399 | "pages per eraseblock %u, OOB size %u\n", | ||
| 400 | (unsigned long long)mtd->size, mtd->erasesize, | ||
| 401 | mtd->writesize, subpgsize, ebcnt, pgcnt, mtd->oobsize); | ||
| 402 | |||
| 403 | err = -ENOMEM; | ||
| 404 | bufsize = subpgsize * 32; | ||
| 405 | writebuf = kmalloc(bufsize, GFP_KERNEL); | ||
| 406 | if (!writebuf) { | ||
| 407 | printk(PRINT_PREF "error: cannot allocate memory\n"); | ||
| 408 | goto out; | ||
| 409 | } | ||
| 410 | readbuf = kmalloc(bufsize, GFP_KERNEL); | ||
| 411 | if (!readbuf) { | ||
| 412 | printk(PRINT_PREF "error: cannot allocate memory\n"); | ||
| 413 | goto out; | ||
| 414 | } | ||
| 415 | |||
| 416 | tmp = mtd->size; | ||
| 417 | do_div(tmp, mtd->erasesize); | ||
| 418 | ebcnt = tmp; | ||
| 419 | pgcnt = mtd->erasesize / mtd->writesize; | ||
| 420 | |||
| 421 | err = scan_for_bad_eraseblocks(); | ||
| 422 | if (err) | ||
| 423 | goto out; | ||
| 424 | |||
| 425 | err = erase_whole_device(); | ||
| 426 | if (err) | ||
| 427 | goto out; | ||
| 428 | |||
| 429 | printk(PRINT_PREF "writing whole device\n"); | ||
| 430 | simple_srand(1); | ||
| 431 | for (i = 0; i < ebcnt; ++i) { | ||
| 432 | if (bbt[i]) | ||
| 433 | continue; | ||
| 434 | err = write_eraseblock(i); | ||
| 435 | if (unlikely(err)) | ||
| 436 | goto out; | ||
| 437 | if (i % 256 == 0) | ||
| 438 | printk(PRINT_PREF "written up to eraseblock %u\n", i); | ||
| 439 | cond_resched(); | ||
| 440 | } | ||
| 441 | printk(PRINT_PREF "written %u eraseblocks\n", i); | ||
| 442 | |||
| 443 | simple_srand(1); | ||
| 444 | printk(PRINT_PREF "verifying all eraseblocks\n"); | ||
| 445 | for (i = 0; i < ebcnt; ++i) { | ||
| 446 | if (bbt[i]) | ||
| 447 | continue; | ||
| 448 | err = verify_eraseblock(i); | ||
| 449 | if (unlikely(err)) | ||
| 450 | goto out; | ||
| 451 | if (i % 256 == 0) | ||
| 452 | printk(PRINT_PREF "verified up to eraseblock %u\n", i); | ||
| 453 | cond_resched(); | ||
| 454 | } | ||
| 455 | printk(PRINT_PREF "verified %u eraseblocks\n", i); | ||
| 456 | |||
| 457 | err = erase_whole_device(); | ||
| 458 | if (err) | ||
| 459 | goto out; | ||
| 460 | |||
| 461 | err = verify_all_eraseblocks_ff(); | ||
| 462 | if (err) | ||
| 463 | goto out; | ||
| 464 | |||
| 465 | /* Write all eraseblocks */ | ||
| 466 | simple_srand(3); | ||
| 467 | printk(PRINT_PREF "writing whole device\n"); | ||
| 468 | for (i = 0; i < ebcnt; ++i) { | ||
| 469 | if (bbt[i]) | ||
| 470 | continue; | ||
| 471 | err = write_eraseblock2(i); | ||
| 472 | if (unlikely(err)) | ||
| 473 | goto out; | ||
| 474 | if (i % 256 == 0) | ||
| 475 | printk(PRINT_PREF "written up to eraseblock %u\n", i); | ||
| 476 | cond_resched(); | ||
| 477 | } | ||
| 478 | printk(PRINT_PREF "written %u eraseblocks\n", i); | ||
| 479 | |||
| 480 | /* Check all eraseblocks */ | ||
| 481 | simple_srand(3); | ||
| 482 | printk(PRINT_PREF "verifying all eraseblocks\n"); | ||
| 483 | for (i = 0; i < ebcnt; ++i) { | ||
| 484 | if (bbt[i]) | ||
| 485 | continue; | ||
| 486 | err = verify_eraseblock2(i); | ||
| 487 | if (unlikely(err)) | ||
| 488 | goto out; | ||
| 489 | if (i % 256 == 0) | ||
| 490 | printk(PRINT_PREF "verified up to eraseblock %u\n", i); | ||
| 491 | cond_resched(); | ||
| 492 | } | ||
| 493 | printk(PRINT_PREF "verified %u eraseblocks\n", i); | ||
| 494 | |||
| 495 | err = erase_whole_device(); | ||
| 496 | if (err) | ||
| 497 | goto out; | ||
| 498 | |||
| 499 | err = verify_all_eraseblocks_ff(); | ||
| 500 | if (err) | ||
| 501 | goto out; | ||
| 502 | |||
| 503 | printk(PRINT_PREF "finished with %d errors\n", errcnt); | ||
| 504 | |||
| 505 | out: | ||
| 506 | kfree(bbt); | ||
| 507 | kfree(readbuf); | ||
| 508 | kfree(writebuf); | ||
| 509 | put_mtd_device(mtd); | ||
| 510 | if (err) | ||
| 511 | printk(PRINT_PREF "error %d occurred\n", err); | ||
| 512 | printk(KERN_INFO "=================================================\n"); | ||
| 513 | return err; | ||
| 514 | } | ||
| 515 | module_init(mtd_subpagetest_init); | ||
| 516 | |||
| 517 | static void __exit mtd_subpagetest_exit(void) | ||
| 518 | { | ||
| 519 | return; | ||
| 520 | } | ||
| 521 | module_exit(mtd_subpagetest_exit); | ||
| 522 | |||
| 523 | MODULE_DESCRIPTION("Subpage test module"); | ||
| 524 | MODULE_AUTHOR("Adrian Hunter"); | ||
| 525 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/mtd/tests/mtd_torturetest.c b/drivers/mtd/tests/mtd_torturetest.c new file mode 100644 index 000000000000..631a0ab3a33c --- /dev/null +++ b/drivers/mtd/tests/mtd_torturetest.c | |||
| @@ -0,0 +1,530 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2006-2008 Artem Bityutskiy | ||
| 3 | * Copyright (C) 2006-2008 Jarkko Lavinen | ||
| 4 | * Copyright (C) 2006-2008 Adrian Hunter | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms of the GNU General Public License version 2 as published by | ||
| 8 | * the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 13 | * more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License along with | ||
| 16 | * this program; see the file COPYING. If not, write to the Free Software | ||
| 17 | * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
| 18 | * | ||
| 19 | * Authors: Artem Bityutskiy, Jarkko Lavinen, Adria Hunter | ||
| 20 | * | ||
| 21 | * WARNING: this test program may kill your flash and your device. Do not | ||
| 22 | * use it unless you know what you do. Authors are not responsible for any | ||
| 23 | * damage caused by this program. | ||
| 24 | */ | ||
| 25 | |||
| 26 | #include <linux/init.h> | ||
| 27 | #include <linux/module.h> | ||
| 28 | #include <linux/moduleparam.h> | ||
| 29 | #include <linux/err.h> | ||
| 30 | #include <linux/mtd/mtd.h> | ||
| 31 | #include <linux/sched.h> | ||
| 32 | |||
| 33 | #define PRINT_PREF KERN_INFO "mtd_torturetest: " | ||
| 34 | #define RETRIES 3 | ||
| 35 | |||
| 36 | static int eb = 8; | ||
| 37 | module_param(eb, int, S_IRUGO); | ||
| 38 | MODULE_PARM_DESC(eb, "eraseblock number within the selected MTD device"); | ||
| 39 | |||
| 40 | static int ebcnt = 32; | ||
| 41 | module_param(ebcnt, int, S_IRUGO); | ||
| 42 | MODULE_PARM_DESC(ebcnt, "number of consecutive eraseblocks to torture"); | ||
| 43 | |||
| 44 | static int pgcnt; | ||
| 45 | module_param(pgcnt, int, S_IRUGO); | ||
| 46 | MODULE_PARM_DESC(pgcnt, "number of pages per eraseblock to torture (0 => all)"); | ||
| 47 | |||
| 48 | static int dev; | ||
| 49 | module_param(dev, int, S_IRUGO); | ||
| 50 | MODULE_PARM_DESC(dev, "MTD device number to use"); | ||
| 51 | |||
| 52 | static int gran = 512; | ||
| 53 | module_param(gran, int, S_IRUGO); | ||
| 54 | MODULE_PARM_DESC(gran, "how often the status information should be printed"); | ||
| 55 | |||
| 56 | static int check = 1; | ||
| 57 | module_param(check, int, S_IRUGO); | ||
| 58 | MODULE_PARM_DESC(check, "if the written data should be checked"); | ||
| 59 | |||
| 60 | static unsigned int cycles_count; | ||
| 61 | module_param(cycles_count, uint, S_IRUGO); | ||
| 62 | MODULE_PARM_DESC(cycles_count, "how many erase cycles to do " | ||
| 63 | "(infinite by default)"); | ||
| 64 | |||
| 65 | static struct mtd_info *mtd; | ||
| 66 | |||
| 67 | /* This buffer contains 0x555555...0xAAAAAA... pattern */ | ||
| 68 | static unsigned char *patt_5A5; | ||
| 69 | /* This buffer contains 0xAAAAAA...0x555555... pattern */ | ||
| 70 | static unsigned char *patt_A5A; | ||
| 71 | /* This buffer contains all 0xFF bytes */ | ||
| 72 | static unsigned char *patt_FF; | ||
| 73 | /* This a temporary buffer is use when checking data */ | ||
| 74 | static unsigned char *check_buf; | ||
| 75 | /* How many erase cycles were done */ | ||
| 76 | static unsigned int erase_cycles; | ||
| 77 | |||
| 78 | static int pgsize; | ||
| 79 | static struct timeval start, finish; | ||
| 80 | |||
| 81 | static void report_corrupt(unsigned char *read, unsigned char *written); | ||
| 82 | |||
| 83 | static inline void start_timing(void) | ||
| 84 | { | ||
| 85 | do_gettimeofday(&start); | ||
| 86 | } | ||
| 87 | |||
| 88 | static inline void stop_timing(void) | ||
| 89 | { | ||
| 90 | do_gettimeofday(&finish); | ||
| 91 | } | ||
| 92 | |||
| 93 | /* | ||
| 94 | * Erase eraseblock number @ebnum. | ||
| 95 | */ | ||
| 96 | static inline int erase_eraseblock(int ebnum) | ||
| 97 | { | ||
| 98 | int err; | ||
| 99 | struct erase_info ei; | ||
| 100 | loff_t addr = ebnum * mtd->erasesize; | ||
| 101 | |||
| 102 | memset(&ei, 0, sizeof(struct erase_info)); | ||
| 103 | ei.mtd = mtd; | ||
| 104 | ei.addr = addr; | ||
| 105 | ei.len = mtd->erasesize; | ||
| 106 | |||
| 107 | err = mtd->erase(mtd, &ei); | ||
| 108 | if (err) { | ||
| 109 | printk(PRINT_PREF "error %d while erasing EB %d\n", err, ebnum); | ||
| 110 | return err; | ||
| 111 | } | ||
| 112 | |||
| 113 | if (ei.state == MTD_ERASE_FAILED) { | ||
| 114 | printk(PRINT_PREF "some erase error occurred at EB %d\n", | ||
| 115 | ebnum); | ||
| 116 | return -EIO; | ||
| 117 | } | ||
| 118 | |||
| 119 | return 0; | ||
| 120 | } | ||
| 121 | |||
| 122 | /* | ||
| 123 | * Check that the contents of eraseblock number @enbum is equivalent to the | ||
| 124 | * @buf buffer. | ||
| 125 | */ | ||
| 126 | static inline int check_eraseblock(int ebnum, unsigned char *buf) | ||
| 127 | { | ||
| 128 | int err, retries = 0; | ||
| 129 | size_t read = 0; | ||
| 130 | loff_t addr = ebnum * mtd->erasesize; | ||
| 131 | size_t len = mtd->erasesize; | ||
| 132 | |||
| 133 | if (pgcnt) { | ||
| 134 | addr = (ebnum + 1) * mtd->erasesize - pgcnt * pgsize; | ||
| 135 | len = pgcnt * pgsize; | ||
| 136 | } | ||
| 137 | |||
| 138 | retry: | ||
| 139 | err = mtd->read(mtd, addr, len, &read, check_buf); | ||
| 140 | if (err == -EUCLEAN) | ||
| 141 | printk(PRINT_PREF "single bit flip occurred at EB %d " | ||
| 142 | "MTD reported that it was fixed.\n", ebnum); | ||
| 143 | else if (err) { | ||
| 144 | printk(PRINT_PREF "error %d while reading EB %d, " | ||
| 145 | "read %zd\n", err, ebnum, read); | ||
| 146 | return err; | ||
| 147 | } | ||
| 148 | |||
| 149 | if (read != len) { | ||
| 150 | printk(PRINT_PREF "failed to read %zd bytes from EB %d, " | ||
| 151 | "read only %zd, but no error reported\n", | ||
| 152 | len, ebnum, read); | ||
| 153 | return -EIO; | ||
| 154 | } | ||
| 155 | |||
| 156 | if (memcmp(buf, check_buf, len)) { | ||
| 157 | printk(PRINT_PREF "read wrong data from EB %d\n", ebnum); | ||
| 158 | report_corrupt(check_buf, buf); | ||
| 159 | |||
| 160 | if (retries++ < RETRIES) { | ||
| 161 | /* Try read again */ | ||
| 162 | yield(); | ||
| 163 | printk(PRINT_PREF "re-try reading data from EB %d\n", | ||
| 164 | ebnum); | ||
| 165 | goto retry; | ||
| 166 | } else { | ||
| 167 | printk(PRINT_PREF "retried %d times, still errors, " | ||
| 168 | "give-up\n", RETRIES); | ||
| 169 | return -EINVAL; | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | if (retries != 0) | ||
| 174 | printk(PRINT_PREF "only attempt number %d was OK (!!!)\n", | ||
| 175 | retries); | ||
| 176 | |||
| 177 | return 0; | ||
| 178 | } | ||
| 179 | |||
| 180 | static inline int write_pattern(int ebnum, void *buf) | ||
| 181 | { | ||
| 182 | int err; | ||
| 183 | size_t written = 0; | ||
| 184 | loff_t addr = ebnum * mtd->erasesize; | ||
| 185 | size_t len = mtd->erasesize; | ||
| 186 | |||
| 187 | if (pgcnt) { | ||
| 188 | addr = (ebnum + 1) * mtd->erasesize - pgcnt * pgsize; | ||
| 189 | len = pgcnt * pgsize; | ||
| 190 | } | ||
| 191 | err = mtd->write(mtd, addr, len, &written, buf); | ||
| 192 | if (err) { | ||
| 193 | printk(PRINT_PREF "error %d while writing EB %d, written %zd" | ||
| 194 | " bytes\n", err, ebnum, written); | ||
| 195 | return err; | ||
| 196 | } | ||
| 197 | if (written != len) { | ||
| 198 | printk(PRINT_PREF "written only %zd bytes of %zd, but no error" | ||
| 199 | " reported\n", written, len); | ||
| 200 | return -EIO; | ||
| 201 | } | ||
| 202 | |||
| 203 | return 0; | ||
| 204 | } | ||
| 205 | |||
| 206 | static int __init tort_init(void) | ||
| 207 | { | ||
| 208 | int err = 0, i, infinite = !cycles_count; | ||
| 209 | int bad_ebs[ebcnt]; | ||
| 210 | |||
| 211 | printk(KERN_INFO "\n"); | ||
| 212 | printk(KERN_INFO "=================================================\n"); | ||
| 213 | printk(PRINT_PREF "Warning: this program is trying to wear out your " | ||
| 214 | "flash, stop it if this is not wanted.\n"); | ||
| 215 | printk(PRINT_PREF "MTD device: %d\n", dev); | ||
| 216 | printk(PRINT_PREF "torture %d eraseblocks (%d-%d) of mtd%d\n", | ||
| 217 | ebcnt, eb, eb + ebcnt - 1, dev); | ||
| 218 | if (pgcnt) | ||
| 219 | printk(PRINT_PREF "torturing just %d pages per eraseblock\n", | ||
| 220 | pgcnt); | ||
| 221 | printk(PRINT_PREF "write verify %s\n", check ? "enabled" : "disabled"); | ||
| 222 | |||
| 223 | mtd = get_mtd_device(NULL, dev); | ||
| 224 | if (IS_ERR(mtd)) { | ||
| 225 | err = PTR_ERR(mtd); | ||
| 226 | printk(PRINT_PREF "error: cannot get MTD device\n"); | ||
| 227 | return err; | ||
| 228 | } | ||
| 229 | |||
| 230 | if (mtd->writesize == 1) { | ||
| 231 | printk(PRINT_PREF "not NAND flash, assume page size is 512 " | ||
| 232 | "bytes.\n"); | ||
| 233 | pgsize = 512; | ||
| 234 | } else | ||
| 235 | pgsize = mtd->writesize; | ||
| 236 | |||
| 237 | if (pgcnt && (pgcnt > mtd->erasesize / pgsize || pgcnt < 0)) { | ||
| 238 | printk(PRINT_PREF "error: invalid pgcnt value %d\n", pgcnt); | ||
| 239 | goto out_mtd; | ||
| 240 | } | ||
| 241 | |||
| 242 | err = -ENOMEM; | ||
| 243 | patt_5A5 = kmalloc(mtd->erasesize, GFP_KERNEL); | ||
| 244 | if (!patt_5A5) { | ||
| 245 | printk(PRINT_PREF "error: cannot allocate memory\n"); | ||
| 246 | goto out_mtd; | ||
| 247 | } | ||
| 248 | |||
| 249 | patt_A5A = kmalloc(mtd->erasesize, GFP_KERNEL); | ||
| 250 | if (!patt_A5A) { | ||
| 251 | printk(PRINT_PREF "error: cannot allocate memory\n"); | ||
| 252 | goto out_patt_5A5; | ||
| 253 | } | ||
| 254 | |||
| 255 | patt_FF = kmalloc(mtd->erasesize, GFP_KERNEL); | ||
| 256 | if (!patt_FF) { | ||
| 257 | printk(PRINT_PREF "error: cannot allocate memory\n"); | ||
| 258 | goto out_patt_A5A; | ||
| 259 | } | ||
| 260 | |||
| 261 | check_buf = kmalloc(mtd->erasesize, GFP_KERNEL); | ||
| 262 | if (!check_buf) { | ||
| 263 | printk(PRINT_PREF "error: cannot allocate memory\n"); | ||
| 264 | goto out_patt_FF; | ||
| 265 | } | ||
| 266 | |||
| 267 | err = 0; | ||
| 268 | |||
| 269 | /* Initialize patterns */ | ||
| 270 | memset(patt_FF, 0xFF, mtd->erasesize); | ||
| 271 | for (i = 0; i < mtd->erasesize / pgsize; i++) { | ||
| 272 | if (!(i & 1)) { | ||
| 273 | memset(patt_5A5 + i * pgsize, 0x55, pgsize); | ||
| 274 | memset(patt_A5A + i * pgsize, 0xAA, pgsize); | ||
| 275 | } else { | ||
| 276 | memset(patt_5A5 + i * pgsize, 0xAA, pgsize); | ||
| 277 | memset(patt_A5A + i * pgsize, 0x55, pgsize); | ||
| 278 | } | ||
| 279 | } | ||
| 280 | |||
| 281 | /* | ||
| 282 | * Check if there is a bad eraseblock among those we are going to test. | ||
| 283 | */ | ||
| 284 | memset(&bad_ebs[0], 0, sizeof(int) * ebcnt); | ||
| 285 | if (mtd->block_isbad) { | ||
| 286 | for (i = eb; i < eb + ebcnt; i++) { | ||
| 287 | err = mtd->block_isbad(mtd, | ||
| 288 | (loff_t)i * mtd->erasesize); | ||
| 289 | |||
| 290 | if (err < 0) { | ||
| 291 | printk(PRINT_PREF "block_isbad() returned %d " | ||
| 292 | "for EB %d\n", err, i); | ||
| 293 | goto out; | ||
| 294 | } | ||
| 295 | |||
| 296 | if (err) { | ||
| 297 | printk("EB %d is bad. Skip it.\n", i); | ||
| 298 | bad_ebs[i - eb] = 1; | ||
| 299 | } | ||
| 300 | } | ||
| 301 | } | ||
| 302 | |||
| 303 | start_timing(); | ||
| 304 | while (1) { | ||
| 305 | int i; | ||
| 306 | void *patt; | ||
| 307 | |||
| 308 | /* Erase all eraseblocks */ | ||
| 309 | for (i = eb; i < eb + ebcnt; i++) { | ||
| 310 | if (bad_ebs[i - eb]) | ||
| 311 | continue; | ||
| 312 | err = erase_eraseblock(i); | ||
| 313 | if (err) | ||
| 314 | goto out; | ||
| 315 | cond_resched(); | ||
| 316 | } | ||
| 317 | |||
| 318 | /* Check if the eraseblocks contain only 0xFF bytes */ | ||
| 319 | if (check) { | ||
| 320 | for (i = eb; i < eb + ebcnt; i++) { | ||
| 321 | if (bad_ebs[i - eb]) | ||
| 322 | continue; | ||
| 323 | err = check_eraseblock(i, patt_FF); | ||
| 324 | if (err) { | ||
| 325 | printk(PRINT_PREF "verify failed" | ||
| 326 | " for 0xFF... pattern\n"); | ||
| 327 | goto out; | ||
| 328 | } | ||
| 329 | cond_resched(); | ||
| 330 | } | ||
| 331 | } | ||
| 332 | |||
| 333 | /* Write the pattern */ | ||
| 334 | for (i = eb; i < eb + ebcnt; i++) { | ||
| 335 | if (bad_ebs[i - eb]) | ||
| 336 | continue; | ||
| 337 | if ((eb + erase_cycles) & 1) | ||
| 338 | patt = patt_5A5; | ||
| 339 | else | ||
| 340 | patt = patt_A5A; | ||
| 341 | err = write_pattern(i, patt); | ||
| 342 | if (err) | ||
| 343 | goto out; | ||
| 344 | cond_resched(); | ||
| 345 | } | ||
| 346 | |||
| 347 | /* Verify what we wrote */ | ||
| 348 | if (check) { | ||
| 349 | for (i = eb; i < eb + ebcnt; i++) { | ||
| 350 | if (bad_ebs[i - eb]) | ||
| 351 | continue; | ||
| 352 | if ((eb + erase_cycles) & 1) | ||
| 353 | patt = patt_5A5; | ||
| 354 | else | ||
| 355 | patt = patt_A5A; | ||
| 356 | err = check_eraseblock(i, patt); | ||
| 357 | if (err) { | ||
| 358 | printk(PRINT_PREF "verify failed for %s" | ||
| 359 | " pattern\n", | ||
| 360 | ((eb + erase_cycles) & 1) ? | ||
| 361 | "0x55AA55..." : "0xAA55AA..."); | ||
| 362 | goto out; | ||
| 363 | } | ||
| 364 | cond_resched(); | ||
| 365 | } | ||
| 366 | } | ||
| 367 | |||
| 368 | erase_cycles += 1; | ||
| 369 | |||
| 370 | if (erase_cycles % gran == 0) { | ||
| 371 | long ms; | ||
| 372 | |||
| 373 | stop_timing(); | ||
| 374 | ms = (finish.tv_sec - start.tv_sec) * 1000 + | ||
| 375 | (finish.tv_usec - start.tv_usec) / 1000; | ||
| 376 | printk(PRINT_PREF "%08u erase cycles done, took %lu " | ||
| 377 | "milliseconds (%lu seconds)\n", | ||
| 378 | erase_cycles, ms, ms / 1000); | ||
| 379 | start_timing(); | ||
| 380 | } | ||
| 381 | |||
| 382 | if (!infinite && --cycles_count == 0) | ||
| 383 | break; | ||
| 384 | } | ||
| 385 | out: | ||
| 386 | |||
| 387 | printk(PRINT_PREF "finished after %u erase cycles\n", | ||
| 388 | erase_cycles); | ||
| 389 | kfree(check_buf); | ||
| 390 | out_patt_FF: | ||
| 391 | kfree(patt_FF); | ||
| 392 | out_patt_A5A: | ||
| 393 | kfree(patt_A5A); | ||
| 394 | out_patt_5A5: | ||
| 395 | kfree(patt_5A5); | ||
| 396 | out_mtd: | ||
| 397 | put_mtd_device(mtd); | ||
| 398 | if (err) | ||
| 399 | printk(PRINT_PREF "error %d occurred during torturing\n", err); | ||
| 400 | printk(KERN_INFO "=================================================\n"); | ||
| 401 | return err; | ||
| 402 | } | ||
| 403 | module_init(tort_init); | ||
| 404 | |||
| 405 | static void __exit tort_exit(void) | ||
| 406 | { | ||
| 407 | return; | ||
| 408 | } | ||
| 409 | module_exit(tort_exit); | ||
| 410 | |||
| 411 | static int countdiffs(unsigned char *buf, unsigned char *check_buf, | ||
| 412 | unsigned offset, unsigned len, unsigned *bytesp, | ||
| 413 | unsigned *bitsp); | ||
| 414 | static void print_bufs(unsigned char *read, unsigned char *written, int start, | ||
| 415 | int len); | ||
| 416 | |||
| 417 | /* | ||
| 418 | * Report the detailed information about how the read EB differs from what was | ||
| 419 | * written. | ||
| 420 | */ | ||
| 421 | static void report_corrupt(unsigned char *read, unsigned char *written) | ||
| 422 | { | ||
| 423 | int i; | ||
| 424 | int bytes, bits, pages, first; | ||
| 425 | int offset, len; | ||
| 426 | size_t check_len = mtd->erasesize; | ||
| 427 | |||
| 428 | if (pgcnt) | ||
| 429 | check_len = pgcnt * pgsize; | ||
| 430 | |||
| 431 | bytes = bits = pages = 0; | ||
| 432 | for (i = 0; i < check_len; i += pgsize) | ||
| 433 | if (countdiffs(written, read, i, pgsize, &bytes, | ||
| 434 | &bits) >= 0) | ||
| 435 | pages++; | ||
| 436 | |||
| 437 | printk(PRINT_PREF "verify fails on %d pages, %d bytes/%d bits\n", | ||
| 438 | pages, bytes, bits); | ||
| 439 | printk(PRINT_PREF "The following is a list of all differences between" | ||
| 440 | " what was read from flash and what was expected\n"); | ||
| 441 | |||
| 442 | for (i = 0; i < check_len; i += pgsize) { | ||
| 443 | cond_resched(); | ||
| 444 | bytes = bits = 0; | ||
| 445 | first = countdiffs(written, read, i, pgsize, &bytes, | ||
| 446 | &bits); | ||
| 447 | if (first < 0) | ||
| 448 | continue; | ||
| 449 | |||
| 450 | printk("-------------------------------------------------------" | ||
| 451 | "----------------------------------\n"); | ||
| 452 | |||
| 453 | printk(PRINT_PREF "Page %zd has %d bytes/%d bits failing verify," | ||
| 454 | " starting at offset 0x%x\n", | ||
| 455 | (mtd->erasesize - check_len + i) / pgsize, | ||
| 456 | bytes, bits, first); | ||
| 457 | |||
| 458 | offset = first & ~0x7; | ||
| 459 | len = ((first + bytes) | 0x7) + 1 - offset; | ||
| 460 | |||
| 461 | print_bufs(read, written, offset, len); | ||
| 462 | } | ||
| 463 | } | ||
| 464 | |||
| 465 | static void print_bufs(unsigned char *read, unsigned char *written, int start, | ||
| 466 | int len) | ||
| 467 | { | ||
| 468 | int i = 0, j1, j2; | ||
| 469 | char *diff; | ||
| 470 | |||
| 471 | printk("Offset Read Written\n"); | ||
| 472 | while (i < len) { | ||
| 473 | printk("0x%08x: ", start + i); | ||
| 474 | diff = " "; | ||
| 475 | for (j1 = 0; j1 < 8 && i + j1 < len; j1++) { | ||
| 476 | printk(" %02x", read[start + i + j1]); | ||
| 477 | if (read[start + i + j1] != written[start + i + j1]) | ||
| 478 | diff = "***"; | ||
| 479 | } | ||
| 480 | |||
| 481 | while (j1 < 8) { | ||
| 482 | printk(" "); | ||
| 483 | j1 += 1; | ||
| 484 | } | ||
| 485 | |||
| 486 | printk(" %s ", diff); | ||
| 487 | |||
| 488 | for (j2 = 0; j2 < 8 && i + j2 < len; j2++) | ||
| 489 | printk(" %02x", written[start + i + j2]); | ||
| 490 | printk("\n"); | ||
| 491 | i += 8; | ||
| 492 | } | ||
| 493 | } | ||
| 494 | |||
| 495 | /* | ||
| 496 | * Count the number of differing bytes and bits and return the first differing | ||
| 497 | * offset. | ||
| 498 | */ | ||
| 499 | static int countdiffs(unsigned char *buf, unsigned char *check_buf, | ||
| 500 | unsigned offset, unsigned len, unsigned *bytesp, | ||
| 501 | unsigned *bitsp) | ||
| 502 | { | ||
| 503 | unsigned i, bit; | ||
| 504 | int first = -1; | ||
| 505 | |||
| 506 | for (i = offset; i < offset + len; i++) | ||
| 507 | if (buf[i] != check_buf[i]) { | ||
| 508 | first = i; | ||
| 509 | break; | ||
| 510 | } | ||
| 511 | |||
| 512 | while (i < offset + len) { | ||
| 513 | if (buf[i] != check_buf[i]) { | ||
| 514 | (*bytesp)++; | ||
| 515 | bit = 1; | ||
| 516 | while (bit < 256) { | ||
| 517 | if ((buf[i] & bit) != (check_buf[i] & bit)) | ||
| 518 | (*bitsp)++; | ||
| 519 | bit <<= 1; | ||
| 520 | } | ||
| 521 | } | ||
| 522 | i++; | ||
| 523 | } | ||
| 524 | |||
| 525 | return first; | ||
| 526 | } | ||
| 527 | |||
| 528 | MODULE_DESCRIPTION("Eraseblock torturing module"); | ||
| 529 | MODULE_AUTHOR("Artem Bityutskiy, Jarkko Lavinen, Adrian Hunter"); | ||
| 530 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 7caf22cd5ad0..9082768cc6c3 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c | |||
| @@ -561,7 +561,7 @@ static int io_init(struct ubi_device *ubi) | |||
| 561 | */ | 561 | */ |
| 562 | 562 | ||
| 563 | ubi->peb_size = ubi->mtd->erasesize; | 563 | ubi->peb_size = ubi->mtd->erasesize; |
| 564 | ubi->peb_count = ubi->mtd->size / ubi->mtd->erasesize; | 564 | ubi->peb_count = mtd_div_by_eb(ubi->mtd->size, ubi->mtd); |
| 565 | ubi->flash_size = ubi->mtd->size; | 565 | ubi->flash_size = ubi->mtd->size; |
| 566 | 566 | ||
| 567 | if (ubi->mtd->block_isbad && ubi->mtd->block_markbad) | 567 | if (ubi->mtd->block_isbad && ubi->mtd->block_markbad) |
diff --git a/drivers/mtd/ubi/gluebi.c b/drivers/mtd/ubi/gluebi.c index 605812bb0b1a..6dd4f5e77f82 100644 --- a/drivers/mtd/ubi/gluebi.c +++ b/drivers/mtd/ubi/gluebi.c | |||
| @@ -215,7 +215,8 @@ static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
| 215 | struct ubi_volume *vol; | 215 | struct ubi_volume *vol; |
| 216 | struct ubi_device *ubi; | 216 | struct ubi_device *ubi; |
| 217 | 217 | ||
| 218 | dbg_gen("erase %u bytes at offset %u", instr->len, instr->addr); | 218 | dbg_gen("erase %llu bytes at offset %llu", (unsigned long long)instr->len, |
| 219 | (unsigned long long)instr->addr); | ||
| 219 | 220 | ||
| 220 | if (instr->addr < 0 || instr->addr > mtd->size - mtd->erasesize) | 221 | if (instr->addr < 0 || instr->addr > mtd->size - mtd->erasesize) |
| 221 | return -EINVAL; | 222 | return -EINVAL; |
| @@ -223,11 +224,11 @@ static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
| 223 | if (instr->len < 0 || instr->addr + instr->len > mtd->size) | 224 | if (instr->len < 0 || instr->addr + instr->len > mtd->size) |
| 224 | return -EINVAL; | 225 | return -EINVAL; |
| 225 | 226 | ||
| 226 | if (instr->addr % mtd->writesize || instr->len % mtd->writesize) | 227 | if (mtd_mod_by_ws(instr->addr, mtd) || mtd_mod_by_ws(instr->len, mtd)) |
| 227 | return -EINVAL; | 228 | return -EINVAL; |
| 228 | 229 | ||
| 229 | lnum = instr->addr / mtd->erasesize; | 230 | lnum = mtd_div_by_eb(instr->addr, mtd); |
| 230 | count = instr->len / mtd->erasesize; | 231 | count = mtd_div_by_eb(instr->len, mtd); |
| 231 | 232 | ||
| 232 | vol = container_of(mtd, struct ubi_volume, gluebi_mtd); | 233 | vol = container_of(mtd, struct ubi_volume, gluebi_mtd); |
| 233 | ubi = vol->ubi; | 234 | ubi = vol->ubi; |
| @@ -255,7 +256,7 @@ static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
| 255 | 256 | ||
| 256 | out_err: | 257 | out_err: |
| 257 | instr->state = MTD_ERASE_FAILED; | 258 | instr->state = MTD_ERASE_FAILED; |
| 258 | instr->fail_addr = lnum * mtd->erasesize; | 259 | instr->fail_addr = (long long)lnum * mtd->erasesize; |
| 259 | return err; | 260 | return err; |
| 260 | } | 261 | } |
| 261 | 262 | ||
| @@ -294,7 +295,7 @@ int ubi_create_gluebi(struct ubi_device *ubi, struct ubi_volume *vol) | |||
| 294 | * bytes. | 295 | * bytes. |
| 295 | */ | 296 | */ |
| 296 | if (vol->vol_type == UBI_DYNAMIC_VOLUME) | 297 | if (vol->vol_type == UBI_DYNAMIC_VOLUME) |
| 297 | mtd->size = vol->usable_leb_size * vol->reserved_pebs; | 298 | mtd->size = (long long)vol->usable_leb_size * vol->reserved_pebs; |
| 298 | else | 299 | else |
| 299 | mtd->size = vol->used_bytes; | 300 | mtd->size = vol->used_bytes; |
| 300 | 301 | ||
| @@ -304,8 +305,8 @@ int ubi_create_gluebi(struct ubi_device *ubi, struct ubi_volume *vol) | |||
| 304 | return -ENFILE; | 305 | return -ENFILE; |
| 305 | } | 306 | } |
| 306 | 307 | ||
| 307 | dbg_gen("added mtd%d (\"%s\"), size %u, EB size %u", | 308 | dbg_gen("added mtd%d (\"%s\"), size %llu, EB size %u", |
| 308 | mtd->index, mtd->name, mtd->size, mtd->erasesize); | 309 | mtd->index, mtd->name, (unsigned long long)mtd->size, mtd->erasesize); |
| 309 | return 0; | 310 | return 0; |
| 310 | } | 311 | } |
| 311 | 312 | ||
diff --git a/fs/jffs2/compr_rubin.c b/fs/jffs2/compr_rubin.c index c73fa89b5f8a..170d289ac785 100644 --- a/fs/jffs2/compr_rubin.c +++ b/fs/jffs2/compr_rubin.c | |||
| @@ -22,9 +22,7 @@ | |||
| 22 | 22 | ||
| 23 | 23 | ||
| 24 | #define BIT_DIVIDER_MIPS 1043 | 24 | #define BIT_DIVIDER_MIPS 1043 |
| 25 | static int bits_mips[8] = { 277,249,290,267,229,341,212,241}; /* mips32 */ | 25 | static int bits_mips[8] = { 277, 249, 290, 267, 229, 341, 212, 241}; |
| 26 | |||
| 27 | #include <linux/errno.h> | ||
| 28 | 26 | ||
| 29 | struct pushpull { | 27 | struct pushpull { |
| 30 | unsigned char *buf; | 28 | unsigned char *buf; |
| @@ -43,7 +41,9 @@ struct rubin_state { | |||
| 43 | int bits[8]; | 41 | int bits[8]; |
| 44 | }; | 42 | }; |
| 45 | 43 | ||
| 46 | static inline void init_pushpull(struct pushpull *pp, char *buf, unsigned buflen, unsigned ofs, unsigned reserve) | 44 | static inline void init_pushpull(struct pushpull *pp, char *buf, |
| 45 | unsigned buflen, unsigned ofs, | ||
| 46 | unsigned reserve) | ||
| 47 | { | 47 | { |
| 48 | pp->buf = buf; | 48 | pp->buf = buf; |
| 49 | pp->buflen = buflen; | 49 | pp->buflen = buflen; |
| @@ -53,16 +53,14 @@ static inline void init_pushpull(struct pushpull *pp, char *buf, unsigned buflen | |||
| 53 | 53 | ||
| 54 | static inline int pushbit(struct pushpull *pp, int bit, int use_reserved) | 54 | static inline int pushbit(struct pushpull *pp, int bit, int use_reserved) |
| 55 | { | 55 | { |
| 56 | if (pp->ofs >= pp->buflen - (use_reserved?0:pp->reserve)) { | 56 | if (pp->ofs >= pp->buflen - (use_reserved?0:pp->reserve)) |
| 57 | return -ENOSPC; | 57 | return -ENOSPC; |
| 58 | } | ||
| 59 | 58 | ||
| 60 | if (bit) { | 59 | if (bit) |
| 61 | pp->buf[pp->ofs >> 3] |= (1<<(7-(pp->ofs &7))); | 60 | pp->buf[pp->ofs >> 3] |= (1<<(7-(pp->ofs & 7))); |
| 62 | } | 61 | else |
| 63 | else { | 62 | pp->buf[pp->ofs >> 3] &= ~(1<<(7-(pp->ofs & 7))); |
| 64 | pp->buf[pp->ofs >> 3] &= ~(1<<(7-(pp->ofs &7))); | 63 | |
| 65 | } | ||
| 66 | pp->ofs++; | 64 | pp->ofs++; |
| 67 | 65 | ||
| 68 | return 0; | 66 | return 0; |
| @@ -97,6 +95,7 @@ static void init_rubin(struct rubin_state *rs, int div, int *bits) | |||
| 97 | rs->p = (long) (2 * UPPER_BIT_RUBIN); | 95 | rs->p = (long) (2 * UPPER_BIT_RUBIN); |
| 98 | rs->bit_number = (long) 0; | 96 | rs->bit_number = (long) 0; |
| 99 | rs->bit_divider = div; | 97 | rs->bit_divider = div; |
| 98 | |||
| 100 | for (c=0; c<8; c++) | 99 | for (c=0; c<8; c++) |
| 101 | rs->bits[c] = bits[c]; | 100 | rs->bits[c] = bits[c]; |
| 102 | } | 101 | } |
| @@ -108,7 +107,8 @@ static int encode(struct rubin_state *rs, long A, long B, int symbol) | |||
| 108 | long i0, i1; | 107 | long i0, i1; |
| 109 | int ret; | 108 | int ret; |
| 110 | 109 | ||
| 111 | while ((rs->q >= UPPER_BIT_RUBIN) || ((rs->p + rs->q) <= UPPER_BIT_RUBIN)) { | 110 | while ((rs->q >= UPPER_BIT_RUBIN) || |
| 111 | ((rs->p + rs->q) <= UPPER_BIT_RUBIN)) { | ||
| 112 | rs->bit_number++; | 112 | rs->bit_number++; |
| 113 | 113 | ||
| 114 | ret = pushbit(&rs->pp, (rs->q & UPPER_BIT_RUBIN) ? 1 : 0, 0); | 114 | ret = pushbit(&rs->pp, (rs->q & UPPER_BIT_RUBIN) ? 1 : 0, 0); |
| @@ -119,12 +119,12 @@ static int encode(struct rubin_state *rs, long A, long B, int symbol) | |||
| 119 | rs->p <<= 1; | 119 | rs->p <<= 1; |
| 120 | } | 120 | } |
| 121 | i0 = A * rs->p / (A + B); | 121 | i0 = A * rs->p / (A + B); |
| 122 | if (i0 <= 0) { | 122 | if (i0 <= 0) |
| 123 | i0 = 1; | 123 | i0 = 1; |
| 124 | } | 124 | |
| 125 | if (i0 >= rs->p) { | 125 | if (i0 >= rs->p) |
| 126 | i0 = rs->p - 1; | 126 | i0 = rs->p - 1; |
| 127 | } | 127 | |
| 128 | i1 = rs->p - i0; | 128 | i1 = rs->p - i0; |
| 129 | 129 | ||
| 130 | if (symbol == 0) | 130 | if (symbol == 0) |
| @@ -157,11 +157,13 @@ static void init_decode(struct rubin_state *rs, int div, int *bits) | |||
| 157 | /* behalve lower */ | 157 | /* behalve lower */ |
| 158 | rs->rec_q = 0; | 158 | rs->rec_q = 0; |
| 159 | 159 | ||
| 160 | for (rs->bit_number = 0; rs->bit_number++ < RUBIN_REG_SIZE; rs->rec_q = rs->rec_q * 2 + (long) (pullbit(&rs->pp))) | 160 | for (rs->bit_number = 0; rs->bit_number++ < RUBIN_REG_SIZE; |
| 161 | rs->rec_q = rs->rec_q * 2 + (long) (pullbit(&rs->pp))) | ||
| 161 | ; | 162 | ; |
| 162 | } | 163 | } |
| 163 | 164 | ||
| 164 | static void __do_decode(struct rubin_state *rs, unsigned long p, unsigned long q) | 165 | static void __do_decode(struct rubin_state *rs, unsigned long p, |
| 166 | unsigned long q) | ||
| 165 | { | 167 | { |
| 166 | register unsigned long lower_bits_rubin = LOWER_BITS_RUBIN; | 168 | register unsigned long lower_bits_rubin = LOWER_BITS_RUBIN; |
| 167 | unsigned long rec_q; | 169 | unsigned long rec_q; |
| @@ -207,12 +209,11 @@ static int decode(struct rubin_state *rs, long A, long B) | |||
| 207 | __do_decode(rs, p, q); | 209 | __do_decode(rs, p, q); |
| 208 | 210 | ||
| 209 | i0 = A * rs->p / (A + B); | 211 | i0 = A * rs->p / (A + B); |
| 210 | if (i0 <= 0) { | 212 | if (i0 <= 0) |
| 211 | i0 = 1; | 213 | i0 = 1; |
| 212 | } | 214 | |
| 213 | if (i0 >= rs->p) { | 215 | if (i0 >= rs->p) |
| 214 | i0 = rs->p - 1; | 216 | i0 = rs->p - 1; |
| 215 | } | ||
| 216 | 217 | ||
| 217 | threshold = rs->q + i0; | 218 | threshold = rs->q + i0; |
| 218 | symbol = rs->rec_q >= threshold; | 219 | symbol = rs->rec_q >= threshold; |
| @@ -234,14 +235,15 @@ static int out_byte(struct rubin_state *rs, unsigned char byte) | |||
| 234 | struct rubin_state rs_copy; | 235 | struct rubin_state rs_copy; |
| 235 | rs_copy = *rs; | 236 | rs_copy = *rs; |
| 236 | 237 | ||
| 237 | for (i=0;i<8;i++) { | 238 | for (i=0; i<8; i++) { |
| 238 | ret = encode(rs, rs->bit_divider-rs->bits[i],rs->bits[i],byte&1); | 239 | ret = encode(rs, rs->bit_divider-rs->bits[i], |
| 240 | rs->bits[i], byte & 1); | ||
| 239 | if (ret) { | 241 | if (ret) { |
| 240 | /* Failed. Restore old state */ | 242 | /* Failed. Restore old state */ |
| 241 | *rs = rs_copy; | 243 | *rs = rs_copy; |
| 242 | return ret; | 244 | return ret; |
| 243 | } | 245 | } |
| 244 | byte=byte>>1; | 246 | byte >>= 1 ; |
| 245 | } | 247 | } |
| 246 | return 0; | 248 | return 0; |
| 247 | } | 249 | } |
| @@ -251,7 +253,8 @@ static int in_byte(struct rubin_state *rs) | |||
| 251 | int i, result = 0, bit_divider = rs->bit_divider; | 253 | int i, result = 0, bit_divider = rs->bit_divider; |
| 252 | 254 | ||
| 253 | for (i = 0; i < 8; i++) | 255 | for (i = 0; i < 8; i++) |
| 254 | result |= decode(rs, bit_divider - rs->bits[i], rs->bits[i]) << i; | 256 | result |= decode(rs, bit_divider - rs->bits[i], |
| 257 | rs->bits[i]) << i; | ||
| 255 | 258 | ||
| 256 | return result; | 259 | return result; |
| 257 | } | 260 | } |
| @@ -259,7 +262,8 @@ static int in_byte(struct rubin_state *rs) | |||
| 259 | 262 | ||
| 260 | 263 | ||
| 261 | static int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in, | 264 | static int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in, |
| 262 | unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen) | 265 | unsigned char *cpage_out, uint32_t *sourcelen, |
| 266 | uint32_t *dstlen) | ||
| 263 | { | 267 | { |
| 264 | int outpos = 0; | 268 | int outpos = 0; |
| 265 | int pos=0; | 269 | int pos=0; |
| @@ -295,7 +299,8 @@ static int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in, | |||
| 295 | int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, | 299 | int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, |
| 296 | uint32_t *sourcelen, uint32_t *dstlen, void *model) | 300 | uint32_t *sourcelen, uint32_t *dstlen, void *model) |
| 297 | { | 301 | { |
| 298 | return rubin_do_compress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen); | 302 | return rubin_do_compress(BIT_DIVIDER_MIPS, bits_mips, data_in, |
| 303 | cpage_out, sourcelen, dstlen); | ||
| 299 | } | 304 | } |
| 300 | #endif | 305 | #endif |
| 301 | static int jffs2_dynrubin_compress(unsigned char *data_in, | 306 | static int jffs2_dynrubin_compress(unsigned char *data_in, |
| @@ -316,9 +321,8 @@ static int jffs2_dynrubin_compress(unsigned char *data_in, | |||
| 316 | return -1; | 321 | return -1; |
| 317 | 322 | ||
| 318 | memset(histo, 0, 256); | 323 | memset(histo, 0, 256); |
| 319 | for (i=0; i<mysrclen; i++) { | 324 | for (i=0; i<mysrclen; i++) |
| 320 | histo[data_in[i]]++; | 325 | histo[data_in[i]]++; |
| 321 | } | ||
| 322 | memset(bits, 0, sizeof(int)*8); | 326 | memset(bits, 0, sizeof(int)*8); |
| 323 | for (i=0; i<256; i++) { | 327 | for (i=0; i<256; i++) { |
| 324 | if (i&128) | 328 | if (i&128) |
| @@ -346,7 +350,8 @@ static int jffs2_dynrubin_compress(unsigned char *data_in, | |||
| 346 | cpage_out[i] = bits[i]; | 350 | cpage_out[i] = bits[i]; |
| 347 | } | 351 | } |
| 348 | 352 | ||
| 349 | ret = rubin_do_compress(256, bits, data_in, cpage_out+8, &mysrclen, &mydstlen); | 353 | ret = rubin_do_compress(256, bits, data_in, cpage_out+8, &mysrclen, |
| 354 | &mydstlen); | ||
| 350 | if (ret) | 355 | if (ret) |
| 351 | return ret; | 356 | return ret; |
| 352 | 357 | ||
| @@ -363,8 +368,10 @@ static int jffs2_dynrubin_compress(unsigned char *data_in, | |||
| 363 | return 0; | 368 | return 0; |
| 364 | } | 369 | } |
| 365 | 370 | ||
| 366 | static void rubin_do_decompress(int bit_divider, int *bits, unsigned char *cdata_in, | 371 | static void rubin_do_decompress(int bit_divider, int *bits, |
| 367 | unsigned char *page_out, uint32_t srclen, uint32_t destlen) | 372 | unsigned char *cdata_in, |
| 373 | unsigned char *page_out, uint32_t srclen, | ||
| 374 | uint32_t destlen) | ||
| 368 | { | 375 | { |
| 369 | int outpos = 0; | 376 | int outpos = 0; |
| 370 | struct rubin_state rs; | 377 | struct rubin_state rs; |
| @@ -372,9 +379,8 @@ static void rubin_do_decompress(int bit_divider, int *bits, unsigned char *cdata | |||
| 372 | init_pushpull(&rs.pp, cdata_in, srclen, 0, 0); | 379 | init_pushpull(&rs.pp, cdata_in, srclen, 0, 0); |
| 373 | init_decode(&rs, bit_divider, bits); | 380 | init_decode(&rs, bit_divider, bits); |
| 374 | 381 | ||
| 375 | while (outpos < destlen) { | 382 | while (outpos < destlen) |
| 376 | page_out[outpos++] = in_byte(&rs); | 383 | page_out[outpos++] = in_byte(&rs); |
| 377 | } | ||
| 378 | } | 384 | } |
| 379 | 385 | ||
| 380 | 386 | ||
| @@ -383,7 +389,8 @@ static int jffs2_rubinmips_decompress(unsigned char *data_in, | |||
| 383 | uint32_t sourcelen, uint32_t dstlen, | 389 | uint32_t sourcelen, uint32_t dstlen, |
| 384 | void *model) | 390 | void *model) |
| 385 | { | 391 | { |
| 386 | rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen); | 392 | rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, |
| 393 | cpage_out, sourcelen, dstlen); | ||
| 387 | return 0; | 394 | return 0; |
| 388 | } | 395 | } |
| 389 | 396 | ||
| @@ -398,52 +405,53 @@ static int jffs2_dynrubin_decompress(unsigned char *data_in, | |||
| 398 | for (c=0; c<8; c++) | 405 | for (c=0; c<8; c++) |
| 399 | bits[c] = data_in[c]; | 406 | bits[c] = data_in[c]; |
| 400 | 407 | ||
| 401 | rubin_do_decompress(256, bits, data_in+8, cpage_out, sourcelen-8, dstlen); | 408 | rubin_do_decompress(256, bits, data_in+8, cpage_out, sourcelen-8, |
| 409 | dstlen); | ||
| 402 | return 0; | 410 | return 0; |
| 403 | } | 411 | } |
| 404 | 412 | ||
| 405 | static struct jffs2_compressor jffs2_rubinmips_comp = { | 413 | static struct jffs2_compressor jffs2_rubinmips_comp = { |
| 406 | .priority = JFFS2_RUBINMIPS_PRIORITY, | 414 | .priority = JFFS2_RUBINMIPS_PRIORITY, |
| 407 | .name = "rubinmips", | 415 | .name = "rubinmips", |
| 408 | .compr = JFFS2_COMPR_DYNRUBIN, | 416 | .compr = JFFS2_COMPR_DYNRUBIN, |
| 409 | .compress = NULL, /*&jffs2_rubinmips_compress,*/ | 417 | .compress = NULL, /*&jffs2_rubinmips_compress,*/ |
| 410 | .decompress = &jffs2_rubinmips_decompress, | 418 | .decompress = &jffs2_rubinmips_decompress, |
| 411 | #ifdef JFFS2_RUBINMIPS_DISABLED | 419 | #ifdef JFFS2_RUBINMIPS_DISABLED |
| 412 | .disabled = 1, | 420 | .disabled = 1, |
| 413 | #else | 421 | #else |
| 414 | .disabled = 0, | 422 | .disabled = 0, |
| 415 | #endif | 423 | #endif |
| 416 | }; | 424 | }; |
| 417 | 425 | ||
| 418 | int jffs2_rubinmips_init(void) | 426 | int jffs2_rubinmips_init(void) |
| 419 | { | 427 | { |
| 420 | return jffs2_register_compressor(&jffs2_rubinmips_comp); | 428 | return jffs2_register_compressor(&jffs2_rubinmips_comp); |
| 421 | } | 429 | } |
| 422 | 430 | ||
| 423 | void jffs2_rubinmips_exit(void) | 431 | void jffs2_rubinmips_exit(void) |
| 424 | { | 432 | { |
| 425 | jffs2_unregister_compressor(&jffs2_rubinmips_comp); | 433 | jffs2_unregister_compressor(&jffs2_rubinmips_comp); |
| 426 | } | 434 | } |
| 427 | 435 | ||
| 428 | static struct jffs2_compressor jffs2_dynrubin_comp = { | 436 | static struct jffs2_compressor jffs2_dynrubin_comp = { |
| 429 | .priority = JFFS2_DYNRUBIN_PRIORITY, | 437 | .priority = JFFS2_DYNRUBIN_PRIORITY, |
| 430 | .name = "dynrubin", | 438 | .name = "dynrubin", |
| 431 | .compr = JFFS2_COMPR_RUBINMIPS, | 439 | .compr = JFFS2_COMPR_RUBINMIPS, |
| 432 | .compress = jffs2_dynrubin_compress, | 440 | .compress = jffs2_dynrubin_compress, |
| 433 | .decompress = &jffs2_dynrubin_decompress, | 441 | .decompress = &jffs2_dynrubin_decompress, |
| 434 | #ifdef JFFS2_DYNRUBIN_DISABLED | 442 | #ifdef JFFS2_DYNRUBIN_DISABLED |
| 435 | .disabled = 1, | 443 | .disabled = 1, |
| 436 | #else | 444 | #else |
| 437 | .disabled = 0, | 445 | .disabled = 0, |
| 438 | #endif | 446 | #endif |
| 439 | }; | 447 | }; |
| 440 | 448 | ||
| 441 | int jffs2_dynrubin_init(void) | 449 | int jffs2_dynrubin_init(void) |
| 442 | { | 450 | { |
| 443 | return jffs2_register_compressor(&jffs2_dynrubin_comp); | 451 | return jffs2_register_compressor(&jffs2_dynrubin_comp); |
| 444 | } | 452 | } |
| 445 | 453 | ||
| 446 | void jffs2_dynrubin_exit(void) | 454 | void jffs2_dynrubin_exit(void) |
| 447 | { | 455 | { |
| 448 | jffs2_unregister_compressor(&jffs2_dynrubin_comp); | 456 | jffs2_unregister_compressor(&jffs2_dynrubin_comp); |
| 449 | } | 457 | } |
diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c index 259461b910af..c32b4a1ad6cf 100644 --- a/fs/jffs2/erase.c +++ b/fs/jffs2/erase.c | |||
| @@ -175,7 +175,7 @@ static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock | |||
| 175 | { | 175 | { |
| 176 | /* For NAND, if the failure did not occur at the device level for a | 176 | /* For NAND, if the failure did not occur at the device level for a |
| 177 | specific physical page, don't bother updating the bad block table. */ | 177 | specific physical page, don't bother updating the bad block table. */ |
| 178 | if (jffs2_cleanmarker_oob(c) && (bad_offset != MTD_FAIL_ADDR_UNKNOWN)) { | 178 | if (jffs2_cleanmarker_oob(c) && (bad_offset != (uint32_t)MTD_FAIL_ADDR_UNKNOWN)) { |
| 179 | /* We had a device-level failure to erase. Let's see if we've | 179 | /* We had a device-level failure to erase. Let's see if we've |
| 180 | failed too many times. */ | 180 | failed too many times. */ |
| 181 | if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) { | 181 | if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) { |
| @@ -209,7 +209,8 @@ static void jffs2_erase_callback(struct erase_info *instr) | |||
| 209 | struct erase_priv_struct *priv = (void *)instr->priv; | 209 | struct erase_priv_struct *priv = (void *)instr->priv; |
| 210 | 210 | ||
| 211 | if(instr->state != MTD_ERASE_DONE) { | 211 | if(instr->state != MTD_ERASE_DONE) { |
| 212 | printk(KERN_WARNING "Erase at 0x%08x finished, but state != MTD_ERASE_DONE. State is 0x%x instead.\n", instr->addr, instr->state); | 212 | printk(KERN_WARNING "Erase at 0x%08llx finished, but state != MTD_ERASE_DONE. State is 0x%x instead.\n", |
| 213 | (unsigned long long)instr->addr, instr->state); | ||
| 213 | jffs2_erase_failed(priv->c, priv->jeb, instr->fail_addr); | 214 | jffs2_erase_failed(priv->c, priv->jeb, instr->fail_addr); |
| 214 | } else { | 215 | } else { |
| 215 | jffs2_erase_succeeded(priv->c, priv->jeb); | 216 | jffs2_erase_succeeded(priv->c, priv->jeb); |
diff --git a/include/linux/mtd/cfi.h b/include/linux/mtd/cfi.h index 00e2b575021f..88d3d8fbf9f2 100644 --- a/include/linux/mtd/cfi.h +++ b/include/linux/mtd/cfi.h | |||
| @@ -520,6 +520,7 @@ struct cfi_fixup { | |||
| 520 | 520 | ||
| 521 | #define CFI_MFR_AMD 0x0001 | 521 | #define CFI_MFR_AMD 0x0001 |
| 522 | #define CFI_MFR_ATMEL 0x001F | 522 | #define CFI_MFR_ATMEL 0x001F |
| 523 | #define CFI_MFR_SAMSUNG 0x00EC | ||
| 523 | #define CFI_MFR_ST 0x0020 /* STMicroelectronics */ | 524 | #define CFI_MFR_ST 0x0020 /* STMicroelectronics */ |
| 524 | 525 | ||
| 525 | void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup* fixups); | 526 | void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup* fixups); |
diff --git a/include/linux/mtd/ftl.h b/include/linux/mtd/ftl.h index 0be442f881dd..0555f7a0b9ed 100644 --- a/include/linux/mtd/ftl.h +++ b/include/linux/mtd/ftl.h | |||
| @@ -32,25 +32,25 @@ | |||
| 32 | #define _LINUX_FTL_H | 32 | #define _LINUX_FTL_H |
| 33 | 33 | ||
| 34 | typedef struct erase_unit_header_t { | 34 | typedef struct erase_unit_header_t { |
| 35 | u_int8_t LinkTargetTuple[5]; | 35 | uint8_t LinkTargetTuple[5]; |
| 36 | u_int8_t DataOrgTuple[10]; | 36 | uint8_t DataOrgTuple[10]; |
| 37 | u_int8_t NumTransferUnits; | 37 | uint8_t NumTransferUnits; |
| 38 | u_int32_t EraseCount; | 38 | uint32_t EraseCount; |
| 39 | u_int16_t LogicalEUN; | 39 | uint16_t LogicalEUN; |
| 40 | u_int8_t BlockSize; | 40 | uint8_t BlockSize; |
| 41 | u_int8_t EraseUnitSize; | 41 | uint8_t EraseUnitSize; |
| 42 | u_int16_t FirstPhysicalEUN; | 42 | uint16_t FirstPhysicalEUN; |
| 43 | u_int16_t NumEraseUnits; | 43 | uint16_t NumEraseUnits; |
| 44 | u_int32_t FormattedSize; | 44 | uint32_t FormattedSize; |
| 45 | u_int32_t FirstVMAddress; | 45 | uint32_t FirstVMAddress; |
| 46 | u_int16_t NumVMPages; | 46 | uint16_t NumVMPages; |
| 47 | u_int8_t Flags; | 47 | uint8_t Flags; |
| 48 | u_int8_t Code; | 48 | uint8_t Code; |
| 49 | u_int32_t SerialNumber; | 49 | uint32_t SerialNumber; |
| 50 | u_int32_t AltEUHOffset; | 50 | uint32_t AltEUHOffset; |
| 51 | u_int32_t BAMOffset; | 51 | uint32_t BAMOffset; |
| 52 | u_int8_t Reserved[12]; | 52 | uint8_t Reserved[12]; |
| 53 | u_int8_t EndTuple[2]; | 53 | uint8_t EndTuple[2]; |
| 54 | } erase_unit_header_t; | 54 | } erase_unit_header_t; |
| 55 | 55 | ||
| 56 | /* Flags in erase_unit_header_t */ | 56 | /* Flags in erase_unit_header_t */ |
diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h index aa30244492c6..b981b8772217 100644 --- a/include/linux/mtd/map.h +++ b/include/linux/mtd/map.h | |||
| @@ -223,6 +223,7 @@ struct map_info { | |||
| 223 | must leave it enabled. */ | 223 | must leave it enabled. */ |
| 224 | void (*set_vpp)(struct map_info *, int); | 224 | void (*set_vpp)(struct map_info *, int); |
| 225 | 225 | ||
| 226 | unsigned long pfow_base; | ||
| 226 | unsigned long map_priv_1; | 227 | unsigned long map_priv_1; |
| 227 | unsigned long map_priv_2; | 228 | unsigned long map_priv_2; |
| 228 | void *fldrv_priv; | 229 | void *fldrv_priv; |
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 64433eb411d7..3aa5d77c2cdb 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h | |||
| @@ -15,6 +15,8 @@ | |||
| 15 | #include <linux/mtd/compatmac.h> | 15 | #include <linux/mtd/compatmac.h> |
| 16 | #include <mtd/mtd-abi.h> | 16 | #include <mtd/mtd-abi.h> |
| 17 | 17 | ||
| 18 | #include <asm/div64.h> | ||
| 19 | |||
| 18 | #define MTD_CHAR_MAJOR 90 | 20 | #define MTD_CHAR_MAJOR 90 |
| 19 | #define MTD_BLOCK_MAJOR 31 | 21 | #define MTD_BLOCK_MAJOR 31 |
| 20 | #define MAX_MTD_DEVICES 32 | 22 | #define MAX_MTD_DEVICES 32 |
| @@ -25,20 +27,20 @@ | |||
| 25 | #define MTD_ERASE_DONE 0x08 | 27 | #define MTD_ERASE_DONE 0x08 |
| 26 | #define MTD_ERASE_FAILED 0x10 | 28 | #define MTD_ERASE_FAILED 0x10 |
| 27 | 29 | ||
| 28 | #define MTD_FAIL_ADDR_UNKNOWN 0xffffffff | 30 | #define MTD_FAIL_ADDR_UNKNOWN -1LL |
| 29 | 31 | ||
| 30 | /* If the erase fails, fail_addr might indicate exactly which block failed. If | 32 | /* If the erase fails, fail_addr might indicate exactly which block failed. If |
| 31 | fail_addr = MTD_FAIL_ADDR_UNKNOWN, the failure was not at the device level or was not | 33 | fail_addr = MTD_FAIL_ADDR_UNKNOWN, the failure was not at the device level or was not |
| 32 | specific to any particular block. */ | 34 | specific to any particular block. */ |
| 33 | struct erase_info { | 35 | struct erase_info { |
| 34 | struct mtd_info *mtd; | 36 | struct mtd_info *mtd; |
| 35 | u_int32_t addr; | 37 | uint64_t addr; |
| 36 | u_int32_t len; | 38 | uint64_t len; |
| 37 | u_int32_t fail_addr; | 39 | uint64_t fail_addr; |
| 38 | u_long time; | 40 | u_long time; |
| 39 | u_long retries; | 41 | u_long retries; |
| 40 | u_int dev; | 42 | unsigned dev; |
| 41 | u_int cell; | 43 | unsigned cell; |
| 42 | void (*callback) (struct erase_info *self); | 44 | void (*callback) (struct erase_info *self); |
| 43 | u_long priv; | 45 | u_long priv; |
| 44 | u_char state; | 46 | u_char state; |
| @@ -46,9 +48,9 @@ struct erase_info { | |||
| 46 | }; | 48 | }; |
| 47 | 49 | ||
| 48 | struct mtd_erase_region_info { | 50 | struct mtd_erase_region_info { |
| 49 | u_int32_t offset; /* At which this region starts, from the beginning of the MTD */ | 51 | uint64_t offset; /* At which this region starts, from the beginning of the MTD */ |
| 50 | u_int32_t erasesize; /* For this region */ | 52 | uint32_t erasesize; /* For this region */ |
| 51 | u_int32_t numblocks; /* Number of blocks of erasesize in this region */ | 53 | uint32_t numblocks; /* Number of blocks of erasesize in this region */ |
| 52 | unsigned long *lockmap; /* If keeping bitmap of locks */ | 54 | unsigned long *lockmap; /* If keeping bitmap of locks */ |
| 53 | }; | 55 | }; |
| 54 | 56 | ||
| @@ -100,14 +102,14 @@ struct mtd_oob_ops { | |||
| 100 | 102 | ||
| 101 | struct mtd_info { | 103 | struct mtd_info { |
| 102 | u_char type; | 104 | u_char type; |
| 103 | u_int32_t flags; | 105 | uint32_t flags; |
| 104 | u_int32_t size; // Total size of the MTD | 106 | uint64_t size; // Total size of the MTD |
| 105 | 107 | ||
| 106 | /* "Major" erase size for the device. Naïve users may take this | 108 | /* "Major" erase size for the device. Naïve users may take this |
| 107 | * to be the only erase size available, or may use the more detailed | 109 | * to be the only erase size available, or may use the more detailed |
| 108 | * information below if they desire | 110 | * information below if they desire |
| 109 | */ | 111 | */ |
| 110 | u_int32_t erasesize; | 112 | uint32_t erasesize; |
| 111 | /* Minimal writable flash unit size. In case of NOR flash it is 1 (even | 113 | /* Minimal writable flash unit size. In case of NOR flash it is 1 (even |
| 112 | * though individual bits can be cleared), in case of NAND flash it is | 114 | * though individual bits can be cleared), in case of NAND flash it is |
| 113 | * one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR | 115 | * one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR |
| @@ -115,10 +117,20 @@ struct mtd_info { | |||
| 115 | * Any driver registering a struct mtd_info must ensure a writesize of | 117 | * Any driver registering a struct mtd_info must ensure a writesize of |
| 116 | * 1 or larger. | 118 | * 1 or larger. |
| 117 | */ | 119 | */ |
| 118 | u_int32_t writesize; | 120 | uint32_t writesize; |
| 121 | |||
| 122 | uint32_t oobsize; // Amount of OOB data per block (e.g. 16) | ||
| 123 | uint32_t oobavail; // Available OOB bytes per block | ||
| 119 | 124 | ||
| 120 | u_int32_t oobsize; // Amount of OOB data per block (e.g. 16) | 125 | /* |
| 121 | u_int32_t oobavail; // Available OOB bytes per block | 126 | * If erasesize is a power of 2 then the shift is stored in |
| 127 | * erasesize_shift otherwise erasesize_shift is zero. Ditto writesize. | ||
| 128 | */ | ||
| 129 | unsigned int erasesize_shift; | ||
| 130 | unsigned int writesize_shift; | ||
| 131 | /* Masks based on erasesize_shift and writesize_shift */ | ||
| 132 | unsigned int erasesize_mask; | ||
| 133 | unsigned int writesize_mask; | ||
| 122 | 134 | ||
| 123 | // Kernel-only stuff starts here. | 135 | // Kernel-only stuff starts here. |
| 124 | const char *name; | 136 | const char *name; |
| @@ -190,8 +202,8 @@ struct mtd_info { | |||
| 190 | void (*sync) (struct mtd_info *mtd); | 202 | void (*sync) (struct mtd_info *mtd); |
| 191 | 203 | ||
| 192 | /* Chip-supported device locking */ | 204 | /* Chip-supported device locking */ |
| 193 | int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len); | 205 | int (*lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len); |
| 194 | int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len); | 206 | int (*unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len); |
| 195 | 207 | ||
| 196 | /* Power Management functions */ | 208 | /* Power Management functions */ |
| 197 | int (*suspend) (struct mtd_info *mtd); | 209 | int (*suspend) (struct mtd_info *mtd); |
| @@ -221,6 +233,35 @@ struct mtd_info { | |||
| 221 | void (*put_device) (struct mtd_info *mtd); | 233 | void (*put_device) (struct mtd_info *mtd); |
| 222 | }; | 234 | }; |
| 223 | 235 | ||
| 236 | static inline uint32_t mtd_div_by_eb(uint64_t sz, struct mtd_info *mtd) | ||
| 237 | { | ||
| 238 | if (mtd->erasesize_shift) | ||
| 239 | return sz >> mtd->erasesize_shift; | ||
| 240 | do_div(sz, mtd->erasesize); | ||
| 241 | return sz; | ||
| 242 | } | ||
| 243 | |||
| 244 | static inline uint32_t mtd_mod_by_eb(uint64_t sz, struct mtd_info *mtd) | ||
| 245 | { | ||
| 246 | if (mtd->erasesize_shift) | ||
| 247 | return sz & mtd->erasesize_mask; | ||
| 248 | return do_div(sz, mtd->erasesize); | ||
| 249 | } | ||
| 250 | |||
| 251 | static inline uint32_t mtd_div_by_ws(uint64_t sz, struct mtd_info *mtd) | ||
| 252 | { | ||
| 253 | if (mtd->writesize_shift) | ||
| 254 | return sz >> mtd->writesize_shift; | ||
| 255 | do_div(sz, mtd->writesize); | ||
| 256 | return sz; | ||
| 257 | } | ||
| 258 | |||
| 259 | static inline uint32_t mtd_mod_by_ws(uint64_t sz, struct mtd_info *mtd) | ||
| 260 | { | ||
| 261 | if (mtd->writesize_shift) | ||
| 262 | return sz & mtd->writesize_mask; | ||
| 263 | return do_div(sz, mtd->writesize); | ||
| 264 | } | ||
| 224 | 265 | ||
| 225 | /* Kernel-side ioctl definitions */ | 266 | /* Kernel-side ioctl definitions */ |
| 226 | 267 | ||
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 733d3f3b4eb8..db5b63da2a7e 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h | |||
| @@ -335,17 +335,12 @@ struct nand_buffers { | |||
| 335 | * @erase_cmd: [INTERN] erase command write function, selectable due to AND support | 335 | * @erase_cmd: [INTERN] erase command write function, selectable due to AND support |
| 336 | * @scan_bbt: [REPLACEABLE] function to scan bad block table | 336 | * @scan_bbt: [REPLACEABLE] function to scan bad block table |
| 337 | * @chip_delay: [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR) | 337 | * @chip_delay: [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR) |
| 338 | * @wq: [INTERN] wait queue to sleep on if a NAND operation is in progress | ||
| 339 | * @state: [INTERN] the current state of the NAND device | 338 | * @state: [INTERN] the current state of the NAND device |
| 340 | * @oob_poi: poison value buffer | 339 | * @oob_poi: poison value buffer |
| 341 | * @page_shift: [INTERN] number of address bits in a page (column address bits) | 340 | * @page_shift: [INTERN] number of address bits in a page (column address bits) |
| 342 | * @phys_erase_shift: [INTERN] number of address bits in a physical eraseblock | 341 | * @phys_erase_shift: [INTERN] number of address bits in a physical eraseblock |
| 343 | * @bbt_erase_shift: [INTERN] number of address bits in a bbt entry | 342 | * @bbt_erase_shift: [INTERN] number of address bits in a bbt entry |
| 344 | * @chip_shift: [INTERN] number of address bits in one chip | 343 | * @chip_shift: [INTERN] number of address bits in one chip |
| 345 | * @datbuf: [INTERN] internal buffer for one page + oob | ||
| 346 | * @oobbuf: [INTERN] oob buffer for one eraseblock | ||
| 347 | * @oobdirty: [INTERN] indicates that oob_buf must be reinitialized | ||
| 348 | * @data_poi: [INTERN] pointer to a data buffer | ||
| 349 | * @options: [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about | 344 | * @options: [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about |
| 350 | * special functionality. See the defines for further explanation | 345 | * special functionality. See the defines for further explanation |
| 351 | * @badblockpos: [INTERN] position of the bad block marker in the oob area | 346 | * @badblockpos: [INTERN] position of the bad block marker in the oob area |
| @@ -399,7 +394,7 @@ struct nand_chip { | |||
| 399 | int bbt_erase_shift; | 394 | int bbt_erase_shift; |
| 400 | int chip_shift; | 395 | int chip_shift; |
| 401 | int numchips; | 396 | int numchips; |
| 402 | unsigned long chipsize; | 397 | uint64_t chipsize; |
| 403 | int pagemask; | 398 | int pagemask; |
| 404 | int pagebuf; | 399 | int pagebuf; |
| 405 | int subpagesize; | 400 | int subpagesize; |
diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h index c92b4d439609..a45dd831b3f8 100644 --- a/include/linux/mtd/partitions.h +++ b/include/linux/mtd/partitions.h | |||
| @@ -36,9 +36,9 @@ | |||
| 36 | 36 | ||
| 37 | struct mtd_partition { | 37 | struct mtd_partition { |
| 38 | char *name; /* identifier string */ | 38 | char *name; /* identifier string */ |
| 39 | u_int32_t size; /* partition size */ | 39 | uint64_t size; /* partition size */ |
| 40 | u_int32_t offset; /* offset within the master MTD space */ | 40 | uint64_t offset; /* offset within the master MTD space */ |
| 41 | u_int32_t mask_flags; /* master MTD flags to mask out for this partition */ | 41 | uint32_t mask_flags; /* master MTD flags to mask out for this partition */ |
| 42 | struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only)*/ | 42 | struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only)*/ |
| 43 | struct mtd_info **mtdp; /* pointer to store the MTD object */ | 43 | struct mtd_info **mtdp; /* pointer to store the MTD object */ |
| 44 | }; | 44 | }; |
diff --git a/include/linux/mtd/pfow.h b/include/linux/mtd/pfow.h new file mode 100644 index 000000000000..b730d4f84655 --- /dev/null +++ b/include/linux/mtd/pfow.h | |||
| @@ -0,0 +1,159 @@ | |||
| 1 | /* Primary function overlay window definitions | ||
| 2 | * and service functions used by LPDDR chips | ||
| 3 | */ | ||
| 4 | #ifndef __LINUX_MTD_PFOW_H | ||
| 5 | #define __LINUX_MTD_PFOW_H | ||
| 6 | |||
| 7 | #include <linux/mtd/qinfo.h> | ||
| 8 | |||
| 9 | /* PFOW registers addressing */ | ||
| 10 | /* Address of symbol "P" */ | ||
| 11 | #define PFOW_QUERY_STRING_P 0x0000 | ||
| 12 | /* Address of symbol "F" */ | ||
| 13 | #define PFOW_QUERY_STRING_F 0x0002 | ||
| 14 | /* Address of symbol "O" */ | ||
| 15 | #define PFOW_QUERY_STRING_O 0x0004 | ||
| 16 | /* Address of symbol "W" */ | ||
| 17 | #define PFOW_QUERY_STRING_W 0x0006 | ||
| 18 | /* Identification info for LPDDR chip */ | ||
| 19 | #define PFOW_MANUFACTURER_ID 0x0020 | ||
| 20 | #define PFOW_DEVICE_ID 0x0022 | ||
| 21 | /* Address in PFOW where prog buffer can can be found */ | ||
| 22 | #define PFOW_PROGRAM_BUFFER_OFFSET 0x0040 | ||
| 23 | /* Size of program buffer in words */ | ||
| 24 | #define PFOW_PROGRAM_BUFFER_SIZE 0x0042 | ||
| 25 | /* Address command code register */ | ||
| 26 | #define PFOW_COMMAND_CODE 0x0080 | ||
| 27 | /* command data register */ | ||
| 28 | #define PFOW_COMMAND_DATA 0x0084 | ||
| 29 | /* command address register lower address bits */ | ||
| 30 | #define PFOW_COMMAND_ADDRESS_L 0x0088 | ||
| 31 | /* command address register upper address bits */ | ||
| 32 | #define PFOW_COMMAND_ADDRESS_H 0x008a | ||
| 33 | /* number of bytes to be proggrammed lower address bits */ | ||
| 34 | #define PFOW_DATA_COUNT_L 0x0090 | ||
| 35 | /* number of bytes to be proggrammed higher address bits */ | ||
| 36 | #define PFOW_DATA_COUNT_H 0x0092 | ||
| 37 | /* command execution register, the only possible value is 0x01 */ | ||
| 38 | #define PFOW_COMMAND_EXECUTE 0x00c0 | ||
| 39 | /* 0x01 should be written at this address to clear buffer */ | ||
| 40 | #define PFOW_CLEAR_PROGRAM_BUFFER 0x00c4 | ||
| 41 | /* device program/erase suspend register */ | ||
| 42 | #define PFOW_PROGRAM_ERASE_SUSPEND 0x00c8 | ||
| 43 | /* device status register */ | ||
| 44 | #define PFOW_DSR 0x00cc | ||
| 45 | |||
| 46 | /* LPDDR memory device command codes */ | ||
| 47 | /* They are possible values of PFOW command code register */ | ||
| 48 | #define LPDDR_WORD_PROGRAM 0x0041 | ||
| 49 | #define LPDDR_BUFF_PROGRAM 0x00E9 | ||
| 50 | #define LPDDR_BLOCK_ERASE 0x0020 | ||
| 51 | #define LPDDR_LOCK_BLOCK 0x0061 | ||
| 52 | #define LPDDR_UNLOCK_BLOCK 0x0062 | ||
| 53 | #define LPDDR_READ_BLOCK_LOCK_STATUS 0x0065 | ||
| 54 | #define LPDDR_INFO_QUERY 0x0098 | ||
| 55 | #define LPDDR_READ_OTP 0x0097 | ||
| 56 | #define LPDDR_PROG_OTP 0x00C0 | ||
| 57 | #define LPDDR_RESUME 0x00D0 | ||
| 58 | |||
| 59 | /* Defines possible value of PFOW command execution register */ | ||
| 60 | #define LPDDR_START_EXECUTION 0x0001 | ||
| 61 | |||
| 62 | /* Defines possible value of PFOW program/erase suspend register */ | ||
| 63 | #define LPDDR_SUSPEND 0x0001 | ||
| 64 | |||
| 65 | /* Possible values of PFOW device status register */ | ||
| 66 | /* access R - read; RC read & clearable */ | ||
| 67 | #define DSR_DPS (1<<1) /* RC; device protect status | ||
| 68 | * 0 - not protected 1 - locked */ | ||
| 69 | #define DSR_PSS (1<<2) /* R; program suspend status; | ||
| 70 | * 0-prog in progress/completed, | ||
| 71 | * 1- prog suspended */ | ||
| 72 | #define DSR_VPPS (1<<3) /* RC; 0-Vpp OK, * 1-Vpp low */ | ||
| 73 | #define DSR_PROGRAM_STATUS (1<<4) /* RC; 0-successful, 1-error */ | ||
| 74 | #define DSR_ERASE_STATUS (1<<5) /* RC; erase or blank check status; | ||
| 75 | * 0-success erase/blank check, | ||
| 76 | * 1 blank check error */ | ||
| 77 | #define DSR_ESS (1<<6) /* R; erase suspend status; | ||
| 78 | * 0-erase in progress/complete, | ||
| 79 | * 1 erase suspended */ | ||
| 80 | #define DSR_READY_STATUS (1<<7) /* R; Device status | ||
| 81 | * 0-busy, | ||
| 82 | * 1-ready */ | ||
| 83 | #define DSR_RPS (0x3<<8) /* RC; region program status | ||
| 84 | * 00 - Success, | ||
| 85 | * 01-re-program attempt in region with | ||
| 86 | * object mode data, | ||
| 87 | * 10-object mode program w attempt in | ||
| 88 | * region with control mode data | ||
| 89 | * 11-attempt to program invalid half | ||
| 90 | * with 0x41 command */ | ||
| 91 | #define DSR_AOS (1<<12) /* RC; 1- AO related failure */ | ||
| 92 | #define DSR_AVAILABLE (1<<15) /* R; Device availbility | ||
| 93 | * 1 - Device available | ||
| 94 | * 0 - not available */ | ||
| 95 | |||
| 96 | /* The superset of all possible error bits in DSR */ | ||
| 97 | #define DSR_ERR 0x133A | ||
| 98 | |||
| 99 | static inline void send_pfow_command(struct map_info *map, | ||
| 100 | unsigned long cmd_code, unsigned long adr, | ||
| 101 | unsigned long len, map_word *datum) | ||
| 102 | { | ||
| 103 | int bits_per_chip = map_bankwidth(map) * 8; | ||
| 104 | int chipnum; | ||
| 105 | struct lpddr_private *lpddr = map->fldrv_priv; | ||
| 106 | chipnum = adr >> lpddr->chipshift; | ||
| 107 | |||
| 108 | map_write(map, CMD(cmd_code), map->pfow_base + PFOW_COMMAND_CODE); | ||
| 109 | map_write(map, CMD(adr & ((1<<bits_per_chip) - 1)), | ||
| 110 | map->pfow_base + PFOW_COMMAND_ADDRESS_L); | ||
| 111 | map_write(map, CMD(adr>>bits_per_chip), | ||
| 112 | map->pfow_base + PFOW_COMMAND_ADDRESS_H); | ||
| 113 | if (len) { | ||
| 114 | map_write(map, CMD(len & ((1<<bits_per_chip) - 1)), | ||
| 115 | map->pfow_base + PFOW_DATA_COUNT_L); | ||
| 116 | map_write(map, CMD(len>>bits_per_chip), | ||
| 117 | map->pfow_base + PFOW_DATA_COUNT_H); | ||
| 118 | } | ||
| 119 | if (datum) | ||
| 120 | map_write(map, *datum, map->pfow_base + PFOW_COMMAND_DATA); | ||
| 121 | |||
| 122 | /* Command execution start */ | ||
| 123 | map_write(map, CMD(LPDDR_START_EXECUTION), | ||
| 124 | map->pfow_base + PFOW_COMMAND_EXECUTE); | ||
| 125 | } | ||
| 126 | |||
| 127 | static inline void print_drs_error(unsigned dsr) | ||
| 128 | { | ||
| 129 | int prog_status = (dsr & DSR_RPS) >> 8; | ||
| 130 | |||
| 131 | if (!(dsr & DSR_AVAILABLE)) | ||
| 132 | printk(KERN_NOTICE"DSR.15: (0) Device not Available\n"); | ||
| 133 | if (prog_status & 0x03) | ||
| 134 | printk(KERN_NOTICE"DSR.9,8: (11) Attempt to program invalid " | ||
| 135 | "half with 41h command\n"); | ||
| 136 | else if (prog_status & 0x02) | ||
| 137 | printk(KERN_NOTICE"DSR.9,8: (10) Object Mode Program attempt " | ||
| 138 | "in region with Control Mode data\n"); | ||
| 139 | else if (prog_status & 0x01) | ||
| 140 | printk(KERN_NOTICE"DSR.9,8: (01) Program attempt in region " | ||
| 141 | "with Object Mode data\n"); | ||
| 142 | if (!(dsr & DSR_READY_STATUS)) | ||
| 143 | printk(KERN_NOTICE"DSR.7: (0) Device is Busy\n"); | ||
| 144 | if (dsr & DSR_ESS) | ||
| 145 | printk(KERN_NOTICE"DSR.6: (1) Erase Suspended\n"); | ||
| 146 | if (dsr & DSR_ERASE_STATUS) | ||
| 147 | printk(KERN_NOTICE"DSR.5: (1) Erase/Blank check error\n"); | ||
| 148 | if (dsr & DSR_PROGRAM_STATUS) | ||
| 149 | printk(KERN_NOTICE"DSR.4: (1) Program Error\n"); | ||
| 150 | if (dsr & DSR_VPPS) | ||
| 151 | printk(KERN_NOTICE"DSR.3: (1) Vpp low detect, operation " | ||
| 152 | "aborted\n"); | ||
| 153 | if (dsr & DSR_PSS) | ||
| 154 | printk(KERN_NOTICE"DSR.2: (1) Program suspended\n"); | ||
| 155 | if (dsr & DSR_DPS) | ||
| 156 | printk(KERN_NOTICE"DSR.1: (1) Aborted Erase/Program attempt " | ||
| 157 | "on locked block\n"); | ||
| 158 | } | ||
| 159 | #endif /* __LINUX_MTD_PFOW_H */ | ||
diff --git a/include/linux/mtd/physmap.h b/include/linux/mtd/physmap.h index c8e63a5ee72e..76f7cabf07d3 100644 --- a/include/linux/mtd/physmap.h +++ b/include/linux/mtd/physmap.h | |||
| @@ -24,6 +24,7 @@ struct physmap_flash_data { | |||
| 24 | unsigned int width; | 24 | unsigned int width; |
| 25 | void (*set_vpp)(struct map_info *, int); | 25 | void (*set_vpp)(struct map_info *, int); |
| 26 | unsigned int nr_parts; | 26 | unsigned int nr_parts; |
| 27 | unsigned int pfow_base; | ||
| 27 | struct mtd_partition *parts; | 28 | struct mtd_partition *parts; |
| 28 | }; | 29 | }; |
| 29 | 30 | ||
diff --git a/include/linux/mtd/qinfo.h b/include/linux/mtd/qinfo.h new file mode 100644 index 000000000000..7b3d487d8b3f --- /dev/null +++ b/include/linux/mtd/qinfo.h | |||
| @@ -0,0 +1,91 @@ | |||
| 1 | #ifndef __LINUX_MTD_QINFO_H | ||
| 2 | #define __LINUX_MTD_QINFO_H | ||
| 3 | |||
| 4 | #include <linux/mtd/map.h> | ||
| 5 | #include <linux/wait.h> | ||
| 6 | #include <linux/spinlock.h> | ||
| 7 | #include <linux/delay.h> | ||
| 8 | #include <linux/mtd/mtd.h> | ||
| 9 | #include <linux/mtd/flashchip.h> | ||
| 10 | #include <linux/mtd/partitions.h> | ||
| 11 | |||
| 12 | /* lpddr_private describes lpddr flash chip in memory map | ||
| 13 | * @ManufactId - Chip Manufacture ID | ||
| 14 | * @DevId - Chip Device ID | ||
| 15 | * @qinfo - pointer to qinfo records describing the chip | ||
| 16 | * @numchips - number of chips including virual RWW partitions | ||
| 17 | * @chipshift - Chip/partiton size 2^chipshift | ||
| 18 | * @chips - per-chip data structure | ||
| 19 | */ | ||
| 20 | struct lpddr_private { | ||
| 21 | uint16_t ManufactId; | ||
| 22 | uint16_t DevId; | ||
| 23 | struct qinfo_chip *qinfo; | ||
| 24 | int numchips; | ||
| 25 | unsigned long chipshift; | ||
| 26 | struct flchip chips[0]; | ||
| 27 | }; | ||
| 28 | |||
| 29 | /* qinfo_query_info structure contains request information for | ||
| 30 | * each qinfo record | ||
| 31 | * @major - major number of qinfo record | ||
| 32 | * @major - minor number of qinfo record | ||
| 33 | * @id_str - descriptive string to access the record | ||
| 34 | * @desc - detailed description for the qinfo record | ||
| 35 | */ | ||
| 36 | struct qinfo_query_info { | ||
| 37 | uint8_t major; | ||
| 38 | uint8_t minor; | ||
| 39 | char *id_str; | ||
| 40 | char *desc; | ||
| 41 | }; | ||
| 42 | |||
| 43 | /* | ||
| 44 | * qinfo_chip structure contains necessary qinfo records data | ||
| 45 | * @DevSizeShift - Device size 2^n bytes | ||
| 46 | * @BufSizeShift - Program buffer size 2^n bytes | ||
| 47 | * @TotalBlocksNum - Total number of blocks | ||
| 48 | * @UniformBlockSizeShift - Uniform block size 2^UniformBlockSizeShift bytes | ||
| 49 | * @HWPartsNum - Number of hardware partitions | ||
| 50 | * @SuspEraseSupp - Suspend erase supported | ||
| 51 | * @SingleWordProgTime - Single word program 2^SingleWordProgTime u-sec | ||
| 52 | * @ProgBufferTime - Program buffer write 2^ProgBufferTime u-sec | ||
| 53 | * @BlockEraseTime - Block erase 2^BlockEraseTime m-sec | ||
| 54 | */ | ||
| 55 | struct qinfo_chip { | ||
| 56 | /* General device info */ | ||
| 57 | uint16_t DevSizeShift; | ||
| 58 | uint16_t BufSizeShift; | ||
| 59 | /* Erase block information */ | ||
| 60 | uint16_t TotalBlocksNum; | ||
| 61 | uint16_t UniformBlockSizeShift; | ||
| 62 | /* Partition information */ | ||
| 63 | uint16_t HWPartsNum; | ||
| 64 | /* Optional features */ | ||
| 65 | uint16_t SuspEraseSupp; | ||
| 66 | /* Operation typical time */ | ||
| 67 | uint16_t SingleWordProgTime; | ||
| 68 | uint16_t ProgBufferTime; | ||
| 69 | uint16_t BlockEraseTime; | ||
| 70 | }; | ||
| 71 | |||
| 72 | /* defines for fixup usage */ | ||
| 73 | #define LPDDR_MFR_ANY 0xffff | ||
| 74 | #define LPDDR_ID_ANY 0xffff | ||
| 75 | #define NUMONYX_MFGR_ID 0x0089 | ||
| 76 | #define R18_DEVICE_ID_1G 0x893c | ||
| 77 | |||
| 78 | static inline map_word lpddr_build_cmd(u_long cmd, struct map_info *map) | ||
| 79 | { | ||
| 80 | map_word val = { {0} }; | ||
| 81 | val.x[0] = cmd; | ||
| 82 | return val; | ||
| 83 | } | ||
| 84 | |||
| 85 | #define CMD(x) lpddr_build_cmd(x, map) | ||
| 86 | #define CMDVAL(cmd) cmd.x[0] | ||
| 87 | |||
| 88 | struct mtd_info *lpddr_cmdset(struct map_info *); | ||
| 89 | |||
| 90 | #endif | ||
| 91 | |||
diff --git a/include/linux/mtd/sharpsl.h b/include/linux/mtd/sharpsl.h new file mode 100644 index 000000000000..25f4d2a845c1 --- /dev/null +++ b/include/linux/mtd/sharpsl.h | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | /* | ||
| 2 | * SharpSL NAND support | ||
| 3 | * | ||
| 4 | * Copyright (C) 2008 Dmitry Baryshkov | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License version 2 as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <linux/mtd/nand.h> | ||
| 12 | #include <linux/mtd/nand_ecc.h> | ||
| 13 | #include <linux/mtd/partitions.h> | ||
| 14 | |||
| 15 | struct sharpsl_nand_platform_data { | ||
| 16 | struct nand_bbt_descr *badblock_pattern; | ||
| 17 | struct nand_ecclayout *ecc_layout; | ||
| 18 | struct mtd_partition *partitions; | ||
| 19 | unsigned int nr_partitions; | ||
| 20 | }; | ||
