diff options
64 files changed, 2795 insertions, 251 deletions
diff --git a/Documentation/powerpc/dts-bindings/mtd-physmap.txt b/Documentation/powerpc/dts-bindings/mtd-physmap.txt index 667c9bde8699..80152cb567d9 100644 --- a/Documentation/powerpc/dts-bindings/mtd-physmap.txt +++ b/Documentation/powerpc/dts-bindings/mtd-physmap.txt | |||
| @@ -1,18 +1,19 @@ | |||
| 1 | CFI or JEDEC memory-mapped NOR flash | 1 | CFI or JEDEC memory-mapped NOR flash, MTD-RAM (NVRAM...) |
| 2 | 2 | ||
| 3 | Flash chips (Memory Technology Devices) are often used for solid state | 3 | Flash chips (Memory Technology Devices) are often used for solid state |
| 4 | file systems on embedded devices. | 4 | file systems on embedded devices. |
| 5 | 5 | ||
| 6 | - compatible : should contain the specific model of flash chip(s) | 6 | - compatible : should contain the specific model of mtd chip(s) |
| 7 | used, if known, followed by either "cfi-flash" or "jedec-flash" | 7 | used, if known, followed by either "cfi-flash", "jedec-flash" |
| 8 | - reg : Address range(s) of the flash chip(s) | 8 | or "mtd-ram". |
| 9 | - reg : Address range(s) of the mtd chip(s) | ||
| 9 | It's possible to (optionally) define multiple "reg" tuples so that | 10 | It's possible to (optionally) define multiple "reg" tuples so that |
| 10 | non-identical NOR chips can be described in one flash node. | 11 | non-identical chips can be described in one node. |
| 11 | - bank-width : Width (in bytes) of the flash bank. Equal to the | 12 | - bank-width : Width (in bytes) of the bank. Equal to the |
| 12 | device width times the number of interleaved chips. | 13 | device width times the number of interleaved chips. |
| 13 | - device-width : (optional) Width of a single flash chip. If | 14 | - device-width : (optional) Width of a single mtd chip. If |
| 14 | omitted, assumed to be equal to 'bank-width'. | 15 | omitted, assumed to be equal to 'bank-width'. |
| 15 | - #address-cells, #size-cells : Must be present if the flash has | 16 | - #address-cells, #size-cells : Must be present if the device has |
| 16 | sub-nodes representing partitions (see below). In this case | 17 | sub-nodes representing partitions (see below). In this case |
| 17 | both #address-cells and #size-cells must be equal to 1. | 18 | both #address-cells and #size-cells must be equal to 1. |
| 18 | 19 | ||
| @@ -22,24 +23,24 @@ are defined: | |||
| 22 | - vendor-id : Contains the flash chip's vendor id (1 byte). | 23 | - vendor-id : Contains the flash chip's vendor id (1 byte). |
| 23 | - device-id : Contains the flash chip's device id (1 byte). | 24 | - device-id : Contains the flash chip's device id (1 byte). |
| 24 | 25 | ||
| 25 | In addition to the information on the flash bank itself, the | 26 | In addition to the information on the mtd bank itself, the |
| 26 | device tree may optionally contain additional information | 27 | device tree may optionally contain additional information |
| 27 | describing partitions of the flash address space. This can be | 28 | describing partitions of the address space. This can be |
| 28 | used on platforms which have strong conventions about which | 29 | used on platforms which have strong conventions about which |
| 29 | portions of the flash are used for what purposes, but which don't | 30 | portions of a flash are used for what purposes, but which don't |
| 30 | use an on-flash partition table such as RedBoot. | 31 | use an on-flash partition table such as RedBoot. |
| 31 | 32 | ||
| 32 | Each partition is represented as a sub-node of the flash device. | 33 | Each partition is represented as a sub-node of the mtd device. |
| 33 | Each node's name represents the name of the corresponding | 34 | Each node's name represents the name of the corresponding |
| 34 | partition of the flash device. | 35 | partition of the mtd device. |
| 35 | 36 | ||
| 36 | Flash partitions | 37 | Flash partitions |
| 37 | - reg : The partition's offset and size within the flash bank. | 38 | - reg : The partition's offset and size within the mtd bank. |
| 38 | - label : (optional) The label / name for this flash partition. | 39 | - label : (optional) The label / name for this partition. |
| 39 | If omitted, the label is taken from the node name (excluding | 40 | If omitted, the label is taken from the node name (excluding |
| 40 | the unit address). | 41 | the unit address). |
| 41 | - read-only : (optional) This parameter, if present, is a hint to | 42 | - read-only : (optional) This parameter, if present, is a hint to |
| 42 | Linux that this flash partition should only be mounted | 43 | Linux that this partition should only be mounted |
| 43 | read-only. This is usually used for flash partitions | 44 | read-only. This is usually used for flash partitions |
| 44 | containing early-boot firmware images or data which should not | 45 | containing early-boot firmware images or data which should not |
| 45 | be clobbered. | 46 | be clobbered. |
| @@ -78,3 +79,12 @@ Here an example with multiple "reg" tuples: | |||
| 78 | reg = <0 0x04000000>; | 79 | reg = <0 0x04000000>; |
| 79 | }; | 80 | }; |
| 80 | }; | 81 | }; |
| 82 | |||
| 83 | An example using SRAM: | ||
| 84 | |||
| 85 | sram@2,0 { | ||
| 86 | compatible = "samsung,k6f1616u6a", "mtd-ram"; | ||
| 87 | reg = <2 0 0x00200000>; | ||
| 88 | bank-width = <2>; | ||
| 89 | }; | ||
| 90 | |||
diff --git a/arch/arm/configs/nhk8815_defconfig b/arch/arm/configs/nhk8815_defconfig index 9bb45b932f04..600cb270f2bf 100644 --- a/arch/arm/configs/nhk8815_defconfig +++ b/arch/arm/configs/nhk8815_defconfig | |||
| @@ -498,7 +498,7 @@ CONFIG_MTD_CFI_I2=y | |||
| 498 | # CONFIG_MTD_DOC2001PLUS is not set | 498 | # CONFIG_MTD_DOC2001PLUS is not set |
| 499 | CONFIG_MTD_NAND=y | 499 | CONFIG_MTD_NAND=y |
| 500 | CONFIG_MTD_NAND_VERIFY_WRITE=y | 500 | CONFIG_MTD_NAND_VERIFY_WRITE=y |
| 501 | # CONFIG_MTD_NAND_ECC_SMC is not set | 501 | CONFIG_MTD_NAND_ECC_SMC=y |
| 502 | # CONFIG_MTD_NAND_MUSEUM_IDS is not set | 502 | # CONFIG_MTD_NAND_MUSEUM_IDS is not set |
| 503 | # CONFIG_MTD_NAND_GPIO is not set | 503 | # CONFIG_MTD_NAND_GPIO is not set |
| 504 | CONFIG_MTD_NAND_IDS=y | 504 | CONFIG_MTD_NAND_IDS=y |
diff --git a/arch/arm/mach-nomadik/board-nhk8815.c b/arch/arm/mach-nomadik/board-nhk8815.c index 79bdea943eb4..6bfd537d5afb 100644 --- a/arch/arm/mach-nomadik/board-nhk8815.c +++ b/arch/arm/mach-nomadik/board-nhk8815.c | |||
| @@ -16,12 +16,164 @@ | |||
| 16 | #include <linux/amba/bus.h> | 16 | #include <linux/amba/bus.h> |
| 17 | #include <linux/interrupt.h> | 17 | #include <linux/interrupt.h> |
| 18 | #include <linux/gpio.h> | 18 | #include <linux/gpio.h> |
| 19 | #include <linux/mtd/mtd.h> | ||
| 20 | #include <linux/mtd/nand.h> | ||
| 21 | #include <linux/mtd/partitions.h> | ||
| 22 | #include <linux/io.h> | ||
| 23 | #include <asm/sizes.h> | ||
| 19 | #include <asm/mach-types.h> | 24 | #include <asm/mach-types.h> |
| 20 | #include <asm/mach/arch.h> | 25 | #include <asm/mach/arch.h> |
| 21 | #include <asm/mach/irq.h> | 26 | #include <asm/mach/irq.h> |
| 27 | #include <asm/mach/flash.h> | ||
| 22 | #include <mach/setup.h> | 28 | #include <mach/setup.h> |
| 29 | #include <mach/nand.h> | ||
| 30 | #include <mach/fsmc.h> | ||
| 23 | #include "clock.h" | 31 | #include "clock.h" |
| 24 | 32 | ||
| 33 | /* These adresses span 16MB, so use three individual pages */ | ||
| 34 | static struct resource nhk8815_nand_resources[] = { | ||
| 35 | { | ||
| 36 | .name = "nand_addr", | ||
| 37 | .start = NAND_IO_ADDR, | ||
| 38 | .end = NAND_IO_ADDR + 0xfff, | ||
| 39 | .flags = IORESOURCE_MEM, | ||
| 40 | }, { | ||
| 41 | .name = "nand_cmd", | ||
| 42 | .start = NAND_IO_CMD, | ||
| 43 | .end = NAND_IO_CMD + 0xfff, | ||
| 44 | .flags = IORESOURCE_MEM, | ||
| 45 | }, { | ||
| 46 | .name = "nand_data", | ||
| 47 | .start = NAND_IO_DATA, | ||
| 48 | .end = NAND_IO_DATA + 0xfff, | ||
| 49 | .flags = IORESOURCE_MEM, | ||
| 50 | } | ||
| 51 | }; | ||
| 52 | |||
| 53 | static int nhk8815_nand_init(void) | ||
| 54 | { | ||
| 55 | /* FSMC setup for nand chip select (8-bit nand in 8815NHK) */ | ||
| 56 | writel(0x0000000E, FSMC_PCR(0)); | ||
| 57 | writel(0x000D0A00, FSMC_PMEM(0)); | ||
| 58 | writel(0x00100A00, FSMC_PATT(0)); | ||
| 59 | |||
| 60 | /* enable access to the chip select area */ | ||
| 61 | writel(readl(FSMC_PCR(0)) | 0x04, FSMC_PCR(0)); | ||
| 62 | |||
| 63 | return 0; | ||
| 64 | } | ||
| 65 | |||
| 66 | /* | ||
| 67 | * These partitions are the same as those used in the 2.6.20 release | ||
| 68 | * shipped by the vendor; the first two partitions are mandated | ||
| 69 | * by the boot ROM, and the bootloader area is somehow oversized... | ||
| 70 | */ | ||
| 71 | static struct mtd_partition nhk8815_partitions[] = { | ||
| 72 | { | ||
| 73 | .name = "X-Loader(NAND)", | ||
| 74 | .offset = 0, | ||
| 75 | .size = SZ_256K, | ||
| 76 | }, { | ||
| 77 | .name = "MemInit(NAND)", | ||
| 78 | .offset = MTDPART_OFS_APPEND, | ||
| 79 | .size = SZ_256K, | ||
| 80 | }, { | ||
| 81 | .name = "BootLoader(NAND)", | ||
| 82 | .offset = MTDPART_OFS_APPEND, | ||
| 83 | .size = SZ_2M, | ||
| 84 | }, { | ||
| 85 | .name = "Kernel zImage(NAND)", | ||
| 86 | .offset = MTDPART_OFS_APPEND, | ||
| 87 | .size = 3 * SZ_1M, | ||
| 88 | }, { | ||
| 89 | .name = "Root Filesystem(NAND)", | ||
| 90 | .offset = MTDPART_OFS_APPEND, | ||
| 91 | .size = 22 * SZ_1M, | ||
| 92 | }, { | ||
| 93 | .name = "User Filesystem(NAND)", | ||
| 94 | .offset = MTDPART_OFS_APPEND, | ||
| 95 | .size = MTDPART_SIZ_FULL, | ||
| 96 | } | ||
| 97 | }; | ||
| 98 | |||
| 99 | static struct nomadik_nand_platform_data nhk8815_nand_data = { | ||
| 100 | .parts = nhk8815_partitions, | ||
| 101 | .nparts = ARRAY_SIZE(nhk8815_partitions), | ||
| 102 | .options = NAND_COPYBACK | NAND_CACHEPRG | NAND_NO_PADDING \ | ||
| 103 | | NAND_NO_READRDY | NAND_NO_AUTOINCR, | ||
| 104 | .init = nhk8815_nand_init, | ||
| 105 | }; | ||
| 106 | |||
| 107 | static struct platform_device nhk8815_nand_device = { | ||
| 108 | .name = "nomadik_nand", | ||
| 109 | .dev = { | ||
| 110 | .platform_data = &nhk8815_nand_data, | ||
| 111 | }, | ||
| 112 | .resource = nhk8815_nand_resources, | ||
| 113 | .num_resources = ARRAY_SIZE(nhk8815_nand_resources), | ||
| 114 | }; | ||
| 115 | |||
| 116 | /* These are the partitions for the OneNand device, different from above */ | ||
| 117 | static struct mtd_partition nhk8815_onenand_partitions[] = { | ||
| 118 | { | ||
| 119 | .name = "X-Loader(OneNAND)", | ||
| 120 | .offset = 0, | ||
| 121 | .size = SZ_256K, | ||
| 122 | }, { | ||
| 123 | .name = "MemInit(OneNAND)", | ||
| 124 | .offset = MTDPART_OFS_APPEND, | ||
| 125 | .size = SZ_256K, | ||
| 126 | }, { | ||
| 127 | .name = "BootLoader(OneNAND)", | ||
| 128 | .offset = MTDPART_OFS_APPEND, | ||
| 129 | .size = SZ_2M-SZ_256K, | ||
| 130 | }, { | ||
| 131 | .name = "SysImage(OneNAND)", | ||
| 132 | .offset = MTDPART_OFS_APPEND, | ||
| 133 | .size = 4 * SZ_1M, | ||
| 134 | }, { | ||
| 135 | .name = "Root Filesystem(OneNAND)", | ||
| 136 | .offset = MTDPART_OFS_APPEND, | ||
| 137 | .size = 22 * SZ_1M, | ||
| 138 | }, { | ||
| 139 | .name = "User Filesystem(OneNAND)", | ||
| 140 | .offset = MTDPART_OFS_APPEND, | ||
| 141 | .size = MTDPART_SIZ_FULL, | ||
| 142 | } | ||
| 143 | }; | ||
| 144 | |||
| 145 | static struct flash_platform_data nhk8815_onenand_data = { | ||
| 146 | .parts = nhk8815_onenand_partitions, | ||
| 147 | .nr_parts = ARRAY_SIZE(nhk8815_onenand_partitions), | ||
| 148 | }; | ||
| 149 | |||
| 150 | static struct resource nhk8815_onenand_resource[] = { | ||
| 151 | { | ||
| 152 | .start = 0x30000000, | ||
| 153 | .end = 0x30000000 + SZ_128K - 1, | ||
| 154 | .flags = IORESOURCE_MEM, | ||
| 155 | }, | ||
| 156 | }; | ||
| 157 | |||
| 158 | static struct platform_device nhk8815_onenand_device = { | ||
| 159 | .name = "onenand", | ||
| 160 | .id = -1, | ||
| 161 | .dev = { | ||
| 162 | .platform_data = &nhk8815_onenand_data, | ||
| 163 | }, | ||
| 164 | .resource = nhk8815_onenand_resource, | ||
| 165 | .num_resources = ARRAY_SIZE(nhk8815_onenand_resource), | ||
| 166 | }; | ||
| 167 | |||
| 168 | static void __init nhk8815_onenand_init(void) | ||
| 169 | { | ||
| 170 | #ifdef CONFIG_ONENAND | ||
| 171 | /* Set up SMCS0 for OneNand */ | ||
| 172 | writel(0x000030db, FSMC_BCR0); | ||
| 173 | writel(0x02100551, FSMC_BTR0); | ||
| 174 | #endif | ||
| 175 | } | ||
| 176 | |||
| 25 | #define __MEM_4K_RESOURCE(x) \ | 177 | #define __MEM_4K_RESOURCE(x) \ |
| 26 | .res = {.start = (x), .end = (x) + SZ_4K - 1, .flags = IORESOURCE_MEM} | 178 | .res = {.start = (x), .end = (x) + SZ_4K - 1, .flags = IORESOURCE_MEM} |
| 27 | 179 | ||
| @@ -81,6 +233,8 @@ static int __init nhk8815_eth_init(void) | |||
| 81 | device_initcall(nhk8815_eth_init); | 233 | device_initcall(nhk8815_eth_init); |
| 82 | 234 | ||
| 83 | static struct platform_device *nhk8815_platform_devices[] __initdata = { | 235 | static struct platform_device *nhk8815_platform_devices[] __initdata = { |
| 236 | &nhk8815_nand_device, | ||
| 237 | &nhk8815_onenand_device, | ||
| 84 | &nhk8815_eth_device, | 238 | &nhk8815_eth_device, |
| 85 | /* will add more devices */ | 239 | /* will add more devices */ |
| 86 | }; | 240 | }; |
| @@ -90,6 +244,7 @@ static void __init nhk8815_platform_init(void) | |||
| 90 | int i; | 244 | int i; |
| 91 | 245 | ||
| 92 | cpu8815_platform_init(); | 246 | cpu8815_platform_init(); |
| 247 | nhk8815_onenand_init(); | ||
| 93 | platform_add_devices(nhk8815_platform_devices, | 248 | platform_add_devices(nhk8815_platform_devices, |
| 94 | ARRAY_SIZE(nhk8815_platform_devices)); | 249 | ARRAY_SIZE(nhk8815_platform_devices)); |
| 95 | 250 | ||
diff --git a/arch/arm/mach-nomadik/include/mach/fsmc.h b/arch/arm/mach-nomadik/include/mach/fsmc.h new file mode 100644 index 000000000000..8c2c05183685 --- /dev/null +++ b/arch/arm/mach-nomadik/include/mach/fsmc.h | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | |||
| 2 | /* Definitions for the Nomadik FSMC "Flexible Static Memory controller" */ | ||
| 3 | |||
| 4 | #ifndef __ASM_ARCH_FSMC_H | ||
| 5 | #define __ASM_ARCH_FSMC_H | ||
| 6 | |||
| 7 | #include <mach/hardware.h> | ||
| 8 | /* | ||
| 9 | * Register list | ||
| 10 | */ | ||
| 11 | |||
| 12 | /* bus control reg. and bus timing reg. for CS0..CS3 */ | ||
| 13 | #define FSMC_BCR(x) (NOMADIK_FSMC_VA + (x << 3)) | ||
| 14 | #define FSMC_BTR(x) (NOMADIK_FSMC_VA + (x << 3) + 0x04) | ||
| 15 | |||
| 16 | /* PC-card and NAND: | ||
| 17 | * PCR = control register | ||
| 18 | * PMEM = memory timing | ||
| 19 | * PATT = attribute timing | ||
| 20 | * PIO = I/O timing | ||
| 21 | * PECCR = ECC result | ||
| 22 | */ | ||
| 23 | #define FSMC_PCR(x) (NOMADIK_FSMC_VA + ((2 + x) << 5) + 0x00) | ||
| 24 | #define FSMC_PMEM(x) (NOMADIK_FSMC_VA + ((2 + x) << 5) + 0x08) | ||
| 25 | #define FSMC_PATT(x) (NOMADIK_FSMC_VA + ((2 + x) << 5) + 0x0c) | ||
| 26 | #define FSMC_PIO(x) (NOMADIK_FSMC_VA + ((2 + x) << 5) + 0x10) | ||
| 27 | #define FSMC_PECCR(x) (NOMADIK_FSMC_VA + ((2 + x) << 5) + 0x14) | ||
| 28 | |||
| 29 | #endif /* __ASM_ARCH_FSMC_H */ | ||
diff --git a/arch/arm/mach-nomadik/include/mach/nand.h b/arch/arm/mach-nomadik/include/mach/nand.h new file mode 100644 index 000000000000..c3c8254c22a5 --- /dev/null +++ b/arch/arm/mach-nomadik/include/mach/nand.h | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | #ifndef __ASM_ARCH_NAND_H | ||
| 2 | #define __ASM_ARCH_NAND_H | ||
| 3 | |||
| 4 | struct nomadik_nand_platform_data { | ||
| 5 | struct mtd_partition *parts; | ||
| 6 | int nparts; | ||
| 7 | int options; | ||
| 8 | int (*init) (void); | ||
| 9 | int (*exit) (void); | ||
| 10 | }; | ||
| 11 | |||
| 12 | #define NAND_IO_DATA 0x40000000 | ||
| 13 | #define NAND_IO_CMD 0x40800000 | ||
| 14 | #define NAND_IO_ADDR 0x41000000 | ||
| 15 | |||
| 16 | #endif /* __ASM_ARCH_NAND_H */ | ||
diff --git a/arch/arm/mach-omap2/board-apollon.c b/arch/arm/mach-omap2/board-apollon.c index 7a2b54c7291a..a1132288c701 100644 --- a/arch/arm/mach-omap2/board-apollon.c +++ b/arch/arm/mach-omap2/board-apollon.c | |||
| @@ -87,7 +87,7 @@ static struct mtd_partition apollon_partitions[] = { | |||
| 87 | }, | 87 | }, |
| 88 | }; | 88 | }; |
| 89 | 89 | ||
| 90 | static struct flash_platform_data apollon_flash_data = { | 90 | static struct onenand_platform_data apollon_flash_data = { |
| 91 | .parts = apollon_partitions, | 91 | .parts = apollon_partitions, |
| 92 | .nr_parts = ARRAY_SIZE(apollon_partitions), | 92 | .nr_parts = ARRAY_SIZE(apollon_partitions), |
| 93 | }; | 93 | }; |
| @@ -99,7 +99,7 @@ static struct resource apollon_flash_resource[] = { | |||
| 99 | }; | 99 | }; |
| 100 | 100 | ||
| 101 | static struct platform_device apollon_onenand_device = { | 101 | static struct platform_device apollon_onenand_device = { |
| 102 | .name = "onenand", | 102 | .name = "onenand-flash", |
| 103 | .id = -1, | 103 | .id = -1, |
| 104 | .dev = { | 104 | .dev = { |
| 105 | .platform_data = &apollon_flash_data, | 105 | .platform_data = &apollon_flash_data, |
diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c index f91934b2b092..15876828db23 100644 --- a/arch/arm/mach-omap2/gpmc.c +++ b/arch/arm/mach-omap2/gpmc.c | |||
| @@ -57,6 +57,11 @@ | |||
| 57 | #define GPMC_CHUNK_SHIFT 24 /* 16 MB */ | 57 | #define GPMC_CHUNK_SHIFT 24 /* 16 MB */ |
| 58 | #define GPMC_SECTION_SHIFT 28 /* 128 MB */ | 58 | #define GPMC_SECTION_SHIFT 28 /* 128 MB */ |
| 59 | 59 | ||
| 60 | #define PREFETCH_FIFOTHRESHOLD (0x40 << 8) | ||
| 61 | #define CS_NUM_SHIFT 24 | ||
| 62 | #define ENABLE_PREFETCH (0x1 << 7) | ||
| 63 | #define DMA_MPU_MODE 2 | ||
| 64 | |||
| 60 | static struct resource gpmc_mem_root; | 65 | static struct resource gpmc_mem_root; |
| 61 | static struct resource gpmc_cs_mem[GPMC_CS_NUM]; | 66 | static struct resource gpmc_cs_mem[GPMC_CS_NUM]; |
| 62 | static DEFINE_SPINLOCK(gpmc_mem_lock); | 67 | static DEFINE_SPINLOCK(gpmc_mem_lock); |
| @@ -386,6 +391,63 @@ void gpmc_cs_free(int cs) | |||
| 386 | } | 391 | } |
| 387 | EXPORT_SYMBOL(gpmc_cs_free); | 392 | EXPORT_SYMBOL(gpmc_cs_free); |
| 388 | 393 | ||
| 394 | /** | ||
| 395 | * gpmc_prefetch_enable - configures and starts prefetch transfer | ||
| 396 | * @cs: nand cs (chip select) number | ||
| 397 | * @dma_mode: dma mode enable (1) or disable (0) | ||
| 398 | * @u32_count: number of bytes to be transferred | ||
| 399 | * @is_write: prefetch read(0) or write post(1) mode | ||
| 400 | */ | ||
| 401 | int gpmc_prefetch_enable(int cs, int dma_mode, | ||
| 402 | unsigned int u32_count, int is_write) | ||
| 403 | { | ||
| 404 | uint32_t prefetch_config1; | ||
| 405 | |||
| 406 | if (!(gpmc_read_reg(GPMC_PREFETCH_CONTROL))) { | ||
| 407 | /* Set the amount of bytes to be prefetched */ | ||
| 408 | gpmc_write_reg(GPMC_PREFETCH_CONFIG2, u32_count); | ||
| 409 | |||
| 410 | /* Set dma/mpu mode, the prefetch read / post write and | ||
| 411 | * enable the engine. Set which cs is has requested for. | ||
| 412 | */ | ||
| 413 | prefetch_config1 = ((cs << CS_NUM_SHIFT) | | ||
| 414 | PREFETCH_FIFOTHRESHOLD | | ||
| 415 | ENABLE_PREFETCH | | ||
| 416 | (dma_mode << DMA_MPU_MODE) | | ||
| 417 | (0x1 & is_write)); | ||
| 418 | gpmc_write_reg(GPMC_PREFETCH_CONFIG1, prefetch_config1); | ||
| 419 | } else { | ||
| 420 | return -EBUSY; | ||
| 421 | } | ||
| 422 | /* Start the prefetch engine */ | ||
| 423 | gpmc_write_reg(GPMC_PREFETCH_CONTROL, 0x1); | ||
| 424 | |||
| 425 | return 0; | ||
| 426 | } | ||
| 427 | EXPORT_SYMBOL(gpmc_prefetch_enable); | ||
| 428 | |||
| 429 | /** | ||
| 430 | * gpmc_prefetch_reset - disables and stops the prefetch engine | ||
| 431 | */ | ||
| 432 | void gpmc_prefetch_reset(void) | ||
| 433 | { | ||
| 434 | /* Stop the PFPW engine */ | ||
| 435 | gpmc_write_reg(GPMC_PREFETCH_CONTROL, 0x0); | ||
| 436 | |||
| 437 | /* Reset/disable the PFPW engine */ | ||
| 438 | gpmc_write_reg(GPMC_PREFETCH_CONFIG1, 0x0); | ||
| 439 | } | ||
| 440 | EXPORT_SYMBOL(gpmc_prefetch_reset); | ||
| 441 | |||
| 442 | /** | ||
| 443 | * gpmc_prefetch_status - reads prefetch status of engine | ||
| 444 | */ | ||
| 445 | int gpmc_prefetch_status(void) | ||
| 446 | { | ||
| 447 | return gpmc_read_reg(GPMC_PREFETCH_STATUS); | ||
| 448 | } | ||
| 449 | EXPORT_SYMBOL(gpmc_prefetch_status); | ||
| 450 | |||
| 389 | static void __init gpmc_mem_init(void) | 451 | static void __init gpmc_mem_init(void) |
| 390 | { | 452 | { |
| 391 | int cs; | 453 | int cs; |
| @@ -452,6 +514,5 @@ void __init gpmc_init(void) | |||
| 452 | l &= 0x03 << 3; | 514 | l &= 0x03 << 3; |
| 453 | l |= (0x02 << 3) | (1 << 0); | 515 | l |= (0x02 << 3) | (1 << 0); |
| 454 | gpmc_write_reg(GPMC_SYSCONFIG, l); | 516 | gpmc_write_reg(GPMC_SYSCONFIG, l); |
| 455 | |||
| 456 | gpmc_mem_init(); | 517 | gpmc_mem_init(); |
| 457 | } | 518 | } |
diff --git a/arch/arm/plat-omap/include/mach/gpmc.h b/arch/arm/plat-omap/include/mach/gpmc.h index 921b16532ff5..9c99cda77ba6 100644 --- a/arch/arm/plat-omap/include/mach/gpmc.h +++ b/arch/arm/plat-omap/include/mach/gpmc.h | |||
| @@ -103,6 +103,10 @@ extern int gpmc_cs_request(int cs, unsigned long size, unsigned long *base); | |||
| 103 | extern void gpmc_cs_free(int cs); | 103 | extern void gpmc_cs_free(int cs); |
| 104 | extern int gpmc_cs_set_reserved(int cs, int reserved); | 104 | extern int gpmc_cs_set_reserved(int cs, int reserved); |
| 105 | extern int gpmc_cs_reserved(int cs); | 105 | extern int gpmc_cs_reserved(int cs); |
| 106 | extern int gpmc_prefetch_enable(int cs, int dma_mode, | ||
| 107 | unsigned int u32_count, int is_write); | ||
| 108 | extern void gpmc_prefetch_reset(void); | ||
| 109 | extern int gpmc_prefetch_status(void); | ||
| 106 | extern void __init gpmc_init(void); | 110 | extern void __init gpmc_init(void); |
| 107 | 111 | ||
| 108 | #endif | 112 | #endif |
diff --git a/drivers/Makefile b/drivers/Makefile index ccfa259fa848..6ee53c7a57a1 100644 --- a/drivers/Makefile +++ b/drivers/Makefile | |||
| @@ -43,6 +43,8 @@ obj-y += macintosh/ | |||
| 43 | obj-$(CONFIG_IDE) += ide/ | 43 | obj-$(CONFIG_IDE) += ide/ |
| 44 | obj-$(CONFIG_SCSI) += scsi/ | 44 | obj-$(CONFIG_SCSI) += scsi/ |
| 45 | obj-$(CONFIG_ATA) += ata/ | 45 | obj-$(CONFIG_ATA) += ata/ |
| 46 | obj-$(CONFIG_MTD) += mtd/ | ||
| 47 | obj-$(CONFIG_SPI) += spi/ | ||
| 46 | obj-y += net/ | 48 | obj-y += net/ |
| 47 | obj-$(CONFIG_ATM) += atm/ | 49 | obj-$(CONFIG_ATM) += atm/ |
| 48 | obj-$(CONFIG_FUSION) += message/ | 50 | obj-$(CONFIG_FUSION) += message/ |
| @@ -51,8 +53,6 @@ obj-y += ieee1394/ | |||
| 51 | obj-$(CONFIG_UIO) += uio/ | 53 | obj-$(CONFIG_UIO) += uio/ |
| 52 | obj-y += cdrom/ | 54 | obj-y += cdrom/ |
| 53 | obj-y += auxdisplay/ | 55 | obj-y += auxdisplay/ |
| 54 | obj-$(CONFIG_MTD) += mtd/ | ||
| 55 | obj-$(CONFIG_SPI) += spi/ | ||
| 56 | obj-$(CONFIG_PCCARD) += pcmcia/ | 56 | obj-$(CONFIG_PCCARD) += pcmcia/ |
| 57 | obj-$(CONFIG_DIO) += dio/ | 57 | obj-$(CONFIG_DIO) += dio/ |
| 58 | obj-$(CONFIG_SBUS) += sbus/ | 58 | obj-$(CONFIG_SBUS) += sbus/ |
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index b8e35a0b4d72..e4ec3659759a 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig | |||
| @@ -25,6 +25,14 @@ config MTD_DEBUG_VERBOSE | |||
| 25 | help | 25 | help |
| 26 | Determines the verbosity level of the MTD debugging messages. | 26 | Determines the verbosity level of the MTD debugging messages. |
| 27 | 27 | ||
| 28 | config MTD_TESTS | ||
| 29 | tristate "MTD tests support" | ||
| 30 | depends on m | ||
| 31 | help | ||
| 32 | This option includes various MTD tests into compilation. The tests | ||
| 33 | should normally be compiled as kernel modules. The modules perform | ||
| 34 | various checks and verifications when loaded. | ||
| 35 | |||
| 28 | config MTD_CONCAT | 36 | config MTD_CONCAT |
| 29 | tristate "MTD concatenating support" | 37 | tristate "MTD concatenating support" |
| 30 | help | 38 | help |
| @@ -45,14 +53,6 @@ config MTD_PARTITIONS | |||
| 45 | devices. Partitioning on NFTL 'devices' is a different - that's the | 53 | devices. Partitioning on NFTL 'devices' is a different - that's the |
| 46 | 'normal' form of partitioning used on a block device. | 54 | 'normal' form of partitioning used on a block device. |
| 47 | 55 | ||
| 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 | |||
| 56 | config MTD_REDBOOT_PARTS | 56 | config MTD_REDBOOT_PARTS |
| 57 | tristate "RedBoot partition table parsing" | 57 | tristate "RedBoot partition table parsing" |
| 58 | depends on MTD_PARTITIONS | 58 | depends on MTD_PARTITIONS |
diff --git a/drivers/mtd/afs.c b/drivers/mtd/afs.c index d072ca5be689..cec7ab98b2a9 100644 --- a/drivers/mtd/afs.c +++ b/drivers/mtd/afs.c | |||
| @@ -239,7 +239,7 @@ static int parse_afs_partitions(struct mtd_info *mtd, | |||
| 239 | parts[idx].offset = img_ptr; | 239 | parts[idx].offset = img_ptr; |
| 240 | parts[idx].mask_flags = 0; | 240 | parts[idx].mask_flags = 0; |
| 241 | 241 | ||
| 242 | printk(" mtd%d: at 0x%08x, %5dKB, %8u, %s\n", | 242 | printk(" mtd%d: at 0x%08x, %5lluKiB, %8u, %s\n", |
| 243 | idx, img_ptr, parts[idx].size / 1024, | 243 | idx, img_ptr, parts[idx].size / 1024, |
| 244 | iis.imageNumber, str); | 244 | iis.imageNumber, str); |
| 245 | 245 | ||
diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 61ea833e0908..94bb61e19047 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c | |||
| @@ -282,16 +282,6 @@ static void fixup_s29gl032n_sectors(struct mtd_info *mtd, void *param) | |||
| 282 | } | 282 | } |
| 283 | } | 283 | } |
| 284 | 284 | ||
| 285 | static void fixup_M29W128G_write_buffer(struct mtd_info *mtd, void *param) | ||
| 286 | { | ||
| 287 | struct map_info *map = mtd->priv; | ||
| 288 | struct cfi_private *cfi = map->fldrv_priv; | ||
| 289 | if (cfi->cfiq->BufWriteTimeoutTyp) { | ||
| 290 | pr_warning("Don't use write buffer on ST flash M29W128G\n"); | ||
| 291 | cfi->cfiq->BufWriteTimeoutTyp = 0; | ||
| 292 | } | ||
| 293 | } | ||
| 294 | |||
| 295 | static struct cfi_fixup cfi_fixup_table[] = { | 285 | static struct cfi_fixup cfi_fixup_table[] = { |
| 296 | { CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL }, | 286 | { CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL }, |
| 297 | #ifdef AMD_BOOTLOC_BUG | 287 | #ifdef AMD_BOOTLOC_BUG |
| @@ -308,7 +298,6 @@ static struct cfi_fixup cfi_fixup_table[] = { | |||
| 308 | { CFI_MFR_AMD, 0x1301, fixup_s29gl064n_sectors, NULL, }, | 298 | { CFI_MFR_AMD, 0x1301, fixup_s29gl064n_sectors, NULL, }, |
| 309 | { CFI_MFR_AMD, 0x1a00, fixup_s29gl032n_sectors, NULL, }, | 299 | { CFI_MFR_AMD, 0x1a00, fixup_s29gl032n_sectors, NULL, }, |
| 310 | { CFI_MFR_AMD, 0x1a01, fixup_s29gl032n_sectors, NULL, }, | 300 | { CFI_MFR_AMD, 0x1a01, fixup_s29gl032n_sectors, NULL, }, |
| 311 | { CFI_MFR_ST, 0x227E, fixup_M29W128G_write_buffer, NULL, }, | ||
| 312 | #if !FORCE_WORD_WRITE | 301 | #if !FORCE_WORD_WRITE |
| 313 | { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, }, | 302 | { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, }, |
| 314 | #endif | 303 | #endif |
diff --git a/drivers/mtd/chips/cfi_util.c b/drivers/mtd/chips/cfi_util.c index 34d40e25d312..c5a84fda5410 100644..100755 --- a/drivers/mtd/chips/cfi_util.c +++ b/drivers/mtd/chips/cfi_util.c | |||
| @@ -81,6 +81,10 @@ void __xipram cfi_qry_mode_off(uint32_t base, struct map_info *map, | |||
| 81 | { | 81 | { |
| 82 | cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); | 82 | cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); |
| 83 | cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); | 83 | cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); |
| 84 | /* M29W128G flashes require an additional reset command | ||
| 85 | when exit qry mode */ | ||
| 86 | if ((cfi->mfr == CFI_MFR_ST) && (cfi->id == 0x227E || cfi->id == 0x7E)) | ||
| 87 | cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); | ||
| 84 | } | 88 | } |
| 85 | EXPORT_SYMBOL_GPL(cfi_qry_mode_off); | 89 | EXPORT_SYMBOL_GPL(cfi_qry_mode_off); |
| 86 | 90 | ||
diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index ccc4cfc7e4b5..736a3be265f2 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c | |||
| @@ -111,6 +111,11 @@ | |||
| 111 | #define I28F320B3B 0x8897 | 111 | #define I28F320B3B 0x8897 |
| 112 | #define I28F640B3T 0x8898 | 112 | #define I28F640B3T 0x8898 |
| 113 | #define I28F640B3B 0x8899 | 113 | #define I28F640B3B 0x8899 |
| 114 | #define I28F640C3B 0x88CD | ||
| 115 | #define I28F160F3T 0x88F3 | ||
| 116 | #define I28F160F3B 0x88F4 | ||
| 117 | #define I28F160C3T 0x88C2 | ||
| 118 | #define I28F160C3B 0x88C3 | ||
| 114 | #define I82802AB 0x00ad | 119 | #define I82802AB 0x00ad |
| 115 | #define I82802AC 0x00ac | 120 | #define I82802AC 0x00ac |
| 116 | 121 | ||
| @@ -150,6 +155,7 @@ | |||
| 150 | #define M50LPW080 0x002F | 155 | #define M50LPW080 0x002F |
| 151 | #define M50FLW080A 0x0080 | 156 | #define M50FLW080A 0x0080 |
| 152 | #define M50FLW080B 0x0081 | 157 | #define M50FLW080B 0x0081 |
| 158 | #define PSD4256G6V 0x00e9 | ||
| 153 | 159 | ||
| 154 | /* SST */ | 160 | /* SST */ |
| 155 | #define SST29EE020 0x0010 | 161 | #define SST29EE020 0x0010 |
| @@ -201,6 +207,7 @@ enum uaddr { | |||
| 201 | MTD_UADDR_0x0555_0x02AA, | 207 | MTD_UADDR_0x0555_0x02AA, |
| 202 | MTD_UADDR_0x0555_0x0AAA, | 208 | MTD_UADDR_0x0555_0x0AAA, |
| 203 | MTD_UADDR_0x5555_0x2AAA, | 209 | MTD_UADDR_0x5555_0x2AAA, |
| 210 | MTD_UADDR_0x0AAA_0x0554, | ||
| 204 | MTD_UADDR_0x0AAA_0x0555, | 211 | MTD_UADDR_0x0AAA_0x0555, |
| 205 | MTD_UADDR_0xAAAA_0x5555, | 212 | MTD_UADDR_0xAAAA_0x5555, |
| 206 | MTD_UADDR_DONT_CARE, /* Requires an arbitrary address */ | 213 | MTD_UADDR_DONT_CARE, /* Requires an arbitrary address */ |
| @@ -245,6 +252,11 @@ static const struct unlock_addr unlock_addrs[] = { | |||
| 245 | .addr2 = 0x2aaa | 252 | .addr2 = 0x2aaa |
| 246 | }, | 253 | }, |
| 247 | 254 | ||
| 255 | [MTD_UADDR_0x0AAA_0x0554] = { | ||
| 256 | .addr1 = 0x0AAA, | ||
| 257 | .addr2 = 0x0554 | ||
| 258 | }, | ||
| 259 | |||
| 248 | [MTD_UADDR_0x0AAA_0x0555] = { | 260 | [MTD_UADDR_0x0AAA_0x0555] = { |
| 249 | .addr1 = 0x0AAA, | 261 | .addr1 = 0x0AAA, |
| 250 | .addr2 = 0x0555 | 262 | .addr2 = 0x0555 |
| @@ -1103,6 +1115,19 @@ static const struct amd_flash_info jedec_table[] = { | |||
| 1103 | } | 1115 | } |
| 1104 | }, { | 1116 | }, { |
| 1105 | .mfr_id = MANUFACTURER_INTEL, | 1117 | .mfr_id = MANUFACTURER_INTEL, |
| 1118 | .dev_id = I28F640C3B, | ||
| 1119 | .name = "Intel 28F640C3B", | ||
| 1120 | .devtypes = CFI_DEVICETYPE_X16, | ||
| 1121 | .uaddr = MTD_UADDR_UNNECESSARY, | ||
| 1122 | .dev_size = SIZE_8MiB, | ||
| 1123 | .cmd_set = P_ID_INTEL_STD, | ||
| 1124 | .nr_regions = 2, | ||
| 1125 | .regions = { | ||
| 1126 | ERASEINFO(0x02000, 8), | ||
| 1127 | ERASEINFO(0x10000, 127), | ||
| 1128 | } | ||
| 1129 | }, { | ||
| 1130 | .mfr_id = MANUFACTURER_INTEL, | ||
| 1106 | .dev_id = I82802AB, | 1131 | .dev_id = I82802AB, |
| 1107 | .name = "Intel 82802AB", | 1132 | .name = "Intel 82802AB", |
| 1108 | .devtypes = CFI_DEVICETYPE_X8, | 1133 | .devtypes = CFI_DEVICETYPE_X8, |
| @@ -1156,8 +1181,8 @@ static const struct amd_flash_info jedec_table[] = { | |||
| 1156 | .mfr_id = MANUFACTURER_NEC, | 1181 | .mfr_id = MANUFACTURER_NEC, |
| 1157 | .dev_id = UPD29F064115, | 1182 | .dev_id = UPD29F064115, |
| 1158 | .name = "NEC uPD29F064115", | 1183 | .name = "NEC uPD29F064115", |
| 1159 | .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, | 1184 | .devtypes = CFI_DEVICETYPE_X16, |
| 1160 | .uaddr = MTD_UADDR_0x0555_0x02AA, /* ???? */ | 1185 | .uaddr = MTD_UADDR_0xAAAA_0x5555, |
| 1161 | .dev_size = SIZE_8MiB, | 1186 | .dev_size = SIZE_8MiB, |
| 1162 | .cmd_set = P_ID_AMD_STD, | 1187 | .cmd_set = P_ID_AMD_STD, |
| 1163 | .nr_regions = 3, | 1188 | .nr_regions = 3, |
| @@ -1726,6 +1751,18 @@ static const struct amd_flash_info jedec_table[] = { | |||
| 1726 | ERASEINFO(0x1000,16), | 1751 | ERASEINFO(0x1000,16), |
| 1727 | } | 1752 | } |
| 1728 | }, { | 1753 | }, { |
| 1754 | .mfr_id = 0xff00 | MANUFACTURER_ST, | ||
| 1755 | .dev_id = 0xff00 | PSD4256G6V, | ||
| 1756 | .name = "ST PSD4256G6V", | ||
| 1757 | .devtypes = CFI_DEVICETYPE_X16, | ||
| 1758 | .uaddr = MTD_UADDR_0x0AAA_0x0554, | ||
| 1759 | .dev_size = SIZE_1MiB, | ||
| 1760 | .cmd_set = P_ID_AMD_STD, | ||
| 1761 | .nr_regions = 1, | ||
| 1762 | .regions = { | ||
| 1763 | ERASEINFO(0x10000,16), | ||
| 1764 | } | ||
| 1765 | }, { | ||
| 1729 | .mfr_id = MANUFACTURER_TOSHIBA, | 1766 | .mfr_id = MANUFACTURER_TOSHIBA, |
| 1730 | .dev_id = TC58FVT160, | 1767 | .dev_id = TC58FVT160, |
| 1731 | .name = "Toshiba TC58FVT160", | 1768 | .name = "Toshiba TC58FVT160", |
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 325fab92a62c..c222514bb70d 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig | |||
| @@ -104,6 +104,16 @@ config M25PXX_USE_FAST_READ | |||
| 104 | help | 104 | help |
| 105 | This option enables FAST_READ access supported by ST M25Pxx. | 105 | This option enables FAST_READ access supported by ST M25Pxx. |
| 106 | 106 | ||
| 107 | config MTD_SST25L | ||
| 108 | tristate "Support SST25L (non JEDEC) SPI Flash chips" | ||
| 109 | depends on SPI_MASTER | ||
| 110 | help | ||
| 111 | This enables access to the non JEDEC SST25L SPI flash chips, used | ||
| 112 | for program and data storage. | ||
| 113 | |||
| 114 | Set up your spi devices with the right board-specific platform data, | ||
| 115 | if you want to specify device partitioning. | ||
| 116 | |||
| 107 | config MTD_SLRAM | 117 | config MTD_SLRAM |
| 108 | tristate "Uncached system RAM" | 118 | tristate "Uncached system RAM" |
| 109 | help | 119 | help |
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index 0993d5cf3923..ab5c9b92ac82 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile | |||
| @@ -16,3 +16,4 @@ obj-$(CONFIG_MTD_LART) += lart.o | |||
| 16 | obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o | 16 | obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o |
| 17 | obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o | 17 | obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o |
| 18 | obj-$(CONFIG_MTD_M25P80) += m25p80.o | 18 | obj-$(CONFIG_MTD_M25P80) += m25p80.o |
| 19 | obj-$(CONFIG_MTD_SST25L) += sst25l.o | ||
diff --git a/drivers/mtd/devices/lart.c b/drivers/mtd/devices/lart.c index 578de1c67bfe..f4359fe7150f 100644 --- a/drivers/mtd/devices/lart.c +++ b/drivers/mtd/devices/lart.c | |||
| @@ -393,7 +393,8 @@ static int flash_erase (struct mtd_info *mtd,struct erase_info *instr) | |||
| 393 | * erase range is aligned with the erase size which is in | 393 | * erase range is aligned with the erase size which is in |
| 394 | * effect here. | 394 | * effect here. |
| 395 | */ | 395 | */ |
| 396 | if (instr->addr & (mtd->eraseregions[i].erasesize - 1)) return (-EINVAL); | 396 | if (i < 0 || (instr->addr & (mtd->eraseregions[i].erasesize - 1))) |
| 397 | return -EINVAL; | ||
| 397 | 398 | ||
| 398 | /* Remember the erase region we start on */ | 399 | /* Remember the erase region we start on */ |
| 399 | first = i; | 400 | first = i; |
| @@ -409,7 +410,8 @@ static int flash_erase (struct mtd_info *mtd,struct erase_info *instr) | |||
| 409 | i--; | 410 | i--; |
| 410 | 411 | ||
| 411 | /* is the end aligned on a block boundary? */ | 412 | /* is the end aligned on a block boundary? */ |
| 412 | if ((instr->addr + instr->len) & (mtd->eraseregions[i].erasesize - 1)) return (-EINVAL); | 413 | if (i < 0 || ((instr->addr + instr->len) & (mtd->eraseregions[i].erasesize - 1))) |
| 414 | return -EINVAL; | ||
| 413 | 415 | ||
| 414 | addr = instr->addr; | 416 | addr = instr->addr; |
| 415 | len = instr->len; | 417 | len = instr->len; |
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index eb495d83064f..379c316f329e 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c | |||
| @@ -44,6 +44,11 @@ | |||
| 44 | #define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ | 44 | #define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ |
| 45 | #define OPCODE_RDID 0x9f /* Read JEDEC ID */ | 45 | #define OPCODE_RDID 0x9f /* Read JEDEC ID */ |
| 46 | 46 | ||
| 47 | /* Used for SST flashes only. */ | ||
| 48 | #define OPCODE_BP 0x02 /* Byte program */ | ||
| 49 | #define OPCODE_WRDI 0x04 /* Write disable */ | ||
| 50 | #define OPCODE_AAI_WP 0xad /* Auto address increment word program */ | ||
| 51 | |||
| 47 | /* Status Register bits. */ | 52 | /* Status Register bits. */ |
| 48 | #define SR_WIP 1 /* Write in progress */ | 53 | #define SR_WIP 1 /* Write in progress */ |
| 49 | #define SR_WEL 2 /* Write enable latch */ | 54 | #define SR_WEL 2 /* Write enable latch */ |
| @@ -132,6 +137,15 @@ static inline int write_enable(struct m25p *flash) | |||
| 132 | return spi_write_then_read(flash->spi, &code, 1, NULL, 0); | 137 | return spi_write_then_read(flash->spi, &code, 1, NULL, 0); |
| 133 | } | 138 | } |
| 134 | 139 | ||
| 140 | /* | ||
| 141 | * Send write disble instruction to the chip. | ||
| 142 | */ | ||
| 143 | static inline int write_disable(struct m25p *flash) | ||
| 144 | { | ||
| 145 | u8 code = OPCODE_WRDI; | ||
| 146 | |||
| 147 | return spi_write_then_read(flash->spi, &code, 1, NULL, 0); | ||
| 148 | } | ||
| 135 | 149 | ||
| 136 | /* | 150 | /* |
| 137 | * Service routine to read status register until ready, or timeout occurs. | 151 | * Service routine to read status register until ready, or timeout occurs. |
| @@ -454,6 +468,111 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
| 454 | return 0; | 468 | return 0; |
| 455 | } | 469 | } |
| 456 | 470 | ||
| 471 | static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, | ||
| 472 | size_t *retlen, const u_char *buf) | ||
| 473 | { | ||
| 474 | struct m25p *flash = mtd_to_m25p(mtd); | ||
| 475 | struct spi_transfer t[2]; | ||
| 476 | struct spi_message m; | ||
| 477 | size_t actual; | ||
| 478 | int cmd_sz, ret; | ||
| 479 | |||
| 480 | if (retlen) | ||
| 481 | *retlen = 0; | ||
| 482 | |||
| 483 | /* sanity checks */ | ||
| 484 | if (!len) | ||
| 485 | return 0; | ||
| 486 | |||
| 487 | if (to + len > flash->mtd.size) | ||
| 488 | return -EINVAL; | ||
| 489 | |||
| 490 | spi_message_init(&m); | ||
| 491 | memset(t, 0, (sizeof t)); | ||
| 492 | |||
| 493 | t[0].tx_buf = flash->command; | ||
| 494 | t[0].len = CMD_SIZE; | ||
| 495 | spi_message_add_tail(&t[0], &m); | ||
| 496 | |||
| 497 | t[1].tx_buf = buf; | ||
| 498 | spi_message_add_tail(&t[1], &m); | ||
| 499 | |||
| 500 | mutex_lock(&flash->lock); | ||
| 501 | |||
| 502 | /* Wait until finished previous write command. */ | ||
| 503 | ret = wait_till_ready(flash); | ||
| 504 | if (ret) | ||
| 505 | goto time_out; | ||
| 506 | |||
| 507 | write_enable(flash); | ||
| 508 | |||
| 509 | actual = to % 2; | ||
| 510 | /* Start write from odd address. */ | ||
| 511 | if (actual) { | ||
| 512 | flash->command[0] = OPCODE_BP; | ||
| 513 | flash->command[1] = to >> 16; | ||
| 514 | flash->command[2] = to >> 8; | ||
| 515 | flash->command[3] = to; | ||
| 516 | |||
| 517 | /* write one byte. */ | ||
| 518 | t[1].len = 1; | ||
| 519 | spi_sync(flash->spi, &m); | ||
| 520 | ret = wait_till_ready(flash); | ||
| 521 | if (ret) | ||
| 522 | goto time_out; | ||
| 523 | *retlen += m.actual_length - CMD_SIZE; | ||
| 524 | } | ||
| 525 | to += actual; | ||
| 526 | |||
| 527 | flash->command[0] = OPCODE_AAI_WP; | ||
| 528 | flash->command[1] = to >> 16; | ||
| 529 | flash->command[2] = to >> 8; | ||
| 530 | flash->command[3] = to; | ||
| 531 | |||
| 532 | /* Write out most of the data here. */ | ||
| 533 | cmd_sz = CMD_SIZE; | ||
| 534 | for (; actual < len - 1; actual += 2) { | ||
| 535 | t[0].len = cmd_sz; | ||
| 536 | /* write two bytes. */ | ||
| 537 | t[1].len = 2; | ||
| 538 | t[1].tx_buf = buf + actual; | ||
| 539 | |||
| 540 | spi_sync(flash->spi, &m); | ||
| 541 | ret = wait_till_ready(flash); | ||
| 542 | if (ret) | ||
| 543 | goto time_out; | ||
| 544 | *retlen += m.actual_length - cmd_sz; | ||
| 545 | cmd_sz = 1; | ||
| 546 | to += 2; | ||
| 547 | } | ||
| 548 | write_disable(flash); | ||
| 549 | ret = wait_till_ready(flash); | ||
| 550 | if (ret) | ||
| 551 | goto time_out; | ||
| 552 | |||
| 553 | /* Write out trailing byte if it exists. */ | ||
| 554 | if (actual != len) { | ||
| 555 | write_enable(flash); | ||
| 556 | flash->command[0] = OPCODE_BP; | ||
| 557 | flash->command[1] = to >> 16; | ||
| 558 | flash->command[2] = to >> 8; | ||
| 559 | flash->command[3] = to; | ||
| 560 | t[0].len = CMD_SIZE; | ||
| 561 | t[1].len = 1; | ||
| 562 | t[1].tx_buf = buf + actual; | ||
| 563 | |||
| 564 | spi_sync(flash->spi, &m); | ||
| 565 | ret = wait_till_ready(flash); | ||
| 566 | if (ret) | ||
| 567 | goto time_out; | ||
| 568 | *retlen += m.actual_length - CMD_SIZE; | ||
| 569 | write_disable(flash); | ||
| 570 | } | ||
| 571 | |||
| 572 | time_out: | ||
| 573 | mutex_unlock(&flash->lock); | ||
| 574 | return ret; | ||
| 575 | } | ||
| 457 | 576 | ||
| 458 | /****************************************************************************/ | 577 | /****************************************************************************/ |
| 459 | 578 | ||
| @@ -501,7 +620,10 @@ static struct flash_info __devinitdata m25p_data [] = { | |||
| 501 | { "at26df321", 0x1f4701, 0, 64 * 1024, 64, SECT_4K, }, | 620 | { "at26df321", 0x1f4701, 0, 64 * 1024, 64, SECT_4K, }, |
| 502 | 621 | ||
| 503 | /* Macronix */ | 622 | /* Macronix */ |
| 623 | { "mx25l3205d", 0xc22016, 0, 64 * 1024, 64, }, | ||
| 624 | { "mx25l6405d", 0xc22017, 0, 64 * 1024, 128, }, | ||
| 504 | { "mx25l12805d", 0xc22018, 0, 64 * 1024, 256, }, | 625 | { "mx25l12805d", 0xc22018, 0, 64 * 1024, 256, }, |
| 626 | { "mx25l12855e", 0xc22618, 0, 64 * 1024, 256, }, | ||
| 505 | 627 | ||
| 506 | /* Spansion -- single (large) sector size only, at least | 628 | /* Spansion -- single (large) sector size only, at least |
| 507 | * for the chips listed here (without boot sectors). | 629 | * for the chips listed here (without boot sectors). |
| @@ -511,14 +633,20 @@ static struct flash_info __devinitdata m25p_data [] = { | |||
| 511 | { "s25sl016a", 0x010214, 0, 64 * 1024, 32, }, | 633 | { "s25sl016a", 0x010214, 0, 64 * 1024, 32, }, |
| 512 | { "s25sl032a", 0x010215, 0, 64 * 1024, 64, }, | 634 | { "s25sl032a", 0x010215, 0, 64 * 1024, 64, }, |
| 513 | { "s25sl064a", 0x010216, 0, 64 * 1024, 128, }, | 635 | { "s25sl064a", 0x010216, 0, 64 * 1024, 128, }, |
| 514 | { "s25sl12800", 0x012018, 0x0300, 256 * 1024, 64, }, | 636 | { "s25sl12800", 0x012018, 0x0300, 256 * 1024, 64, }, |
| 515 | { "s25sl12801", 0x012018, 0x0301, 64 * 1024, 256, }, | 637 | { "s25sl12801", 0x012018, 0x0301, 64 * 1024, 256, }, |
| 638 | { "s25fl129p0", 0x012018, 0x4d00, 256 * 1024, 64, }, | ||
| 639 | { "s25fl129p1", 0x012018, 0x4d01, 64 * 1024, 256, }, | ||
| 516 | 640 | ||
| 517 | /* SST -- large erase sizes are "overlays", "sectors" are 4K */ | 641 | /* SST -- large erase sizes are "overlays", "sectors" are 4K */ |
| 518 | { "sst25vf040b", 0xbf258d, 0, 64 * 1024, 8, SECT_4K, }, | 642 | { "sst25vf040b", 0xbf258d, 0, 64 * 1024, 8, SECT_4K, }, |
| 519 | { "sst25vf080b", 0xbf258e, 0, 64 * 1024, 16, SECT_4K, }, | 643 | { "sst25vf080b", 0xbf258e, 0, 64 * 1024, 16, SECT_4K, }, |
| 520 | { "sst25vf016b", 0xbf2541, 0, 64 * 1024, 32, SECT_4K, }, | 644 | { "sst25vf016b", 0xbf2541, 0, 64 * 1024, 32, SECT_4K, }, |
| 521 | { "sst25vf032b", 0xbf254a, 0, 64 * 1024, 64, SECT_4K, }, | 645 | { "sst25vf032b", 0xbf254a, 0, 64 * 1024, 64, SECT_4K, }, |
| 646 | { "sst25wf512", 0xbf2501, 0, 64 * 1024, 1, SECT_4K, }, | ||
| 647 | { "sst25wf010", 0xbf2502, 0, 64 * 1024, 2, SECT_4K, }, | ||
| 648 | { "sst25wf020", 0xbf2503, 0, 64 * 1024, 4, SECT_4K, }, | ||
| 649 | { "sst25wf040", 0xbf2504, 0, 64 * 1024, 8, SECT_4K, }, | ||
| 522 | 650 | ||
| 523 | /* ST Microelectronics -- newer production may have feature updates */ | 651 | /* ST Microelectronics -- newer production may have feature updates */ |
| 524 | { "m25p05", 0x202010, 0, 32 * 1024, 2, }, | 652 | { "m25p05", 0x202010, 0, 32 * 1024, 2, }, |
| @@ -667,7 +795,12 @@ static int __devinit m25p_probe(struct spi_device *spi) | |||
| 667 | flash->mtd.size = info->sector_size * info->n_sectors; | 795 | flash->mtd.size = info->sector_size * info->n_sectors; |
| 668 | flash->mtd.erase = m25p80_erase; | 796 | flash->mtd.erase = m25p80_erase; |
| 669 | flash->mtd.read = m25p80_read; | 797 | flash->mtd.read = m25p80_read; |
| 670 | flash->mtd.write = m25p80_write; | 798 | |
| 799 | /* sst flash chips use AAI word program */ | ||
| 800 | if (info->jedec_id >> 16 == 0xbf) | ||
| 801 | flash->mtd.write = sst_write; | ||
| 802 | else | ||
| 803 | flash->mtd.write = m25p80_write; | ||
| 671 | 804 | ||
| 672 | /* prefer "small sector" erase if possible */ | 805 | /* prefer "small sector" erase if possible */ |
| 673 | if (info->flags & SECT_4K) { | 806 | if (info->flags & SECT_4K) { |
diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index 211c27acd01e..93e3627be74c 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c | |||
| @@ -401,7 +401,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
| 401 | (void) dataflash_waitready(priv->spi); | 401 | (void) dataflash_waitready(priv->spi); |
| 402 | 402 | ||
| 403 | 403 | ||
| 404 | #ifdef CONFIG_MTD_DATAFLASH_VERIFY_WRITE | 404 | #ifdef CONFIG_MTD_DATAFLASH_WRITE_VERIFY |
| 405 | 405 | ||
| 406 | /* (3) Compare to Buffer1 */ | 406 | /* (3) Compare to Buffer1 */ |
| 407 | addr = pageaddr << priv->page_offset; | 407 | addr = pageaddr << priv->page_offset; |
| @@ -430,7 +430,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
| 430 | } else | 430 | } else |
| 431 | status = 0; | 431 | status = 0; |
| 432 | 432 | ||
| 433 | #endif /* CONFIG_MTD_DATAFLASH_VERIFY_WRITE */ | 433 | #endif /* CONFIG_MTD_DATAFLASH_WRITE_VERIFY */ |
| 434 | 434 | ||
| 435 | remaining = remaining - writelen; | 435 | remaining = remaining - writelen; |
| 436 | pageaddr++; | 436 | pageaddr++; |
diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c index 088fbb7595b5..1696bbecaa7e 100644 --- a/drivers/mtd/devices/phram.c +++ b/drivers/mtd/devices/phram.c | |||
| @@ -14,6 +14,9 @@ | |||
| 14 | * Example: | 14 | * Example: |
| 15 | * phram=swap,64Mi,128Mi phram=test,900Mi,1Mi | 15 | * phram=swap,64Mi,128Mi phram=test,900Mi,1Mi |
| 16 | */ | 16 | */ |
| 17 | |||
| 18 | #define pr_fmt(fmt) "phram: " fmt | ||
| 19 | |||
| 17 | #include <asm/io.h> | 20 | #include <asm/io.h> |
| 18 | #include <linux/init.h> | 21 | #include <linux/init.h> |
| 19 | #include <linux/kernel.h> | 22 | #include <linux/kernel.h> |
| @@ -23,8 +26,6 @@ | |||
| 23 | #include <linux/slab.h> | 26 | #include <linux/slab.h> |
| 24 | #include <linux/mtd/mtd.h> | 27 | #include <linux/mtd/mtd.h> |
| 25 | 28 | ||
| 26 | #define ERROR(fmt, args...) printk(KERN_ERR "phram: " fmt , ## args) | ||
| 27 | |||
| 28 | struct phram_mtd_list { | 29 | struct phram_mtd_list { |
| 29 | struct mtd_info mtd; | 30 | struct mtd_info mtd; |
| 30 | struct list_head list; | 31 | struct list_head list; |
| @@ -132,7 +133,7 @@ static int register_device(char *name, unsigned long start, unsigned long len) | |||
| 132 | ret = -EIO; | 133 | ret = -EIO; |
| 133 | new->mtd.priv = ioremap(start, len); | 134 | new->mtd.priv = ioremap(start, len); |
| 134 | if (!new->mtd.priv) { | 135 | if (!new->mtd.priv) { |
| 135 | ERROR("ioremap failed\n"); | 136 | pr_err("ioremap failed\n"); |
| 136 | goto out1; | 137 | goto out1; |
| 137 | } | 138 | } |
| 138 | 139 | ||
| @@ -152,7 +153,7 @@ static int register_device(char *name, unsigned long start, unsigned long len) | |||
| 152 | 153 | ||
| 153 | ret = -EAGAIN; | 154 | ret = -EAGAIN; |
| 154 | if (add_mtd_device(&new->mtd)) { | 155 | if (add_mtd_device(&new->mtd)) { |
| 155 | ERROR("Failed to register new device\n"); | 156 | pr_err("Failed to register new device\n"); |
| 156 | goto out2; | 157 | goto out2; |
| 157 | } | 158 | } |
| 158 | 159 | ||
| @@ -227,8 +228,8 @@ static inline void kill_final_newline(char *str) | |||
| 227 | 228 | ||
| 228 | 229 | ||
| 229 | #define parse_err(fmt, args...) do { \ | 230 | #define parse_err(fmt, args...) do { \ |
| 230 | ERROR(fmt , ## args); \ | 231 | pr_err(fmt , ## args); \ |
| 231 | return 0; \ | 232 | return 1; \ |
| 232 | } while (0) | 233 | } while (0) |
| 233 | 234 | ||
| 234 | static int phram_setup(const char *val, struct kernel_param *kp) | 235 | static int phram_setup(const char *val, struct kernel_param *kp) |
| @@ -256,12 +257,8 @@ static int phram_setup(const char *val, struct kernel_param *kp) | |||
| 256 | parse_err("not enough arguments\n"); | 257 | parse_err("not enough arguments\n"); |
| 257 | 258 | ||
| 258 | ret = parse_name(&name, token[0]); | 259 | ret = parse_name(&name, token[0]); |
| 259 | if (ret == -ENOMEM) | ||
| 260 | parse_err("out of memory\n"); | ||
| 261 | if (ret == -ENOSPC) | ||
| 262 | parse_err("name too long\n"); | ||
| 263 | if (ret) | 260 | if (ret) |
| 264 | return 0; | 261 | return ret; |
| 265 | 262 | ||
| 266 | ret = parse_num32(&start, token[1]); | 263 | ret = parse_num32(&start, token[1]); |
| 267 | if (ret) { | 264 | if (ret) { |
| @@ -275,9 +272,11 @@ static int phram_setup(const char *val, struct kernel_param *kp) | |||
| 275 | parse_err("illegal device length\n"); | 272 | parse_err("illegal device length\n"); |
| 276 | } | 273 | } |
| 277 | 274 | ||
| 278 | register_device(name, start, len); | 275 | ret = register_device(name, start, len); |
| 276 | if (!ret) | ||
| 277 | pr_info("%s device: %#x at %#x\n", name, len, start); | ||
| 279 | 278 | ||
| 280 | return 0; | 279 | return ret; |
| 281 | } | 280 | } |
| 282 | 281 | ||
| 283 | module_param_call(phram, phram_setup, NULL, NULL, 000); | 282 | module_param_call(phram, phram_setup, NULL, NULL, 000); |
diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c index 7d846e9173da..3aa05cd18ea1 100644 --- a/drivers/mtd/devices/slram.c +++ b/drivers/mtd/devices/slram.c | |||
| @@ -341,7 +341,7 @@ static int __init init_slram(void) | |||
| 341 | #else | 341 | #else |
| 342 | int count; | 342 | int count; |
| 343 | 343 | ||
| 344 | for (count = 0; (map[count]) && (count < SLRAM_MAX_DEVICES_PARAMS); | 344 | for (count = 0; count < SLRAM_MAX_DEVICES_PARAMS && map[count]; |
| 345 | count++) { | 345 | count++) { |
| 346 | } | 346 | } |
| 347 | 347 | ||
diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c new file mode 100644 index 000000000000..c2baf3353f84 --- /dev/null +++ b/drivers/mtd/devices/sst25l.c | |||
| @@ -0,0 +1,512 @@ | |||
| 1 | /* | ||
| 2 | * sst25l.c | ||
| 3 | * | ||
| 4 | * Driver for SST25L SPI Flash chips | ||
| 5 | * | ||
| 6 | * Copyright © 2009 Bluewater Systems Ltd | ||
| 7 | * Author: Andre Renaud <andre@bluewatersys.com> | ||
| 8 | * Author: Ryan Mallon <ryan@bluewatersys.com> | ||
| 9 | * | ||
| 10 | * Based on m25p80.c | ||
| 11 | * | ||
| 12 | * This code is free software; you can redistribute it and/or modify | ||
| 13 | * it under the terms of the GNU General Public License version 2 as | ||
| 14 | * published by the Free Software Foundation. | ||
| 15 | * | ||
| 16 | */ | ||
| 17 | |||
| 18 | #include <linux/init.h> | ||
| 19 | #include <linux/module.h> | ||
| 20 | #include <linux/device.h> | ||
| 21 | #include <linux/mutex.h> | ||
| 22 | #include <linux/interrupt.h> | ||
| 23 | |||
| 24 | #include <linux/mtd/mtd.h> | ||
| 25 | #include <linux/mtd/partitions.h> | ||
| 26 | |||
| 27 | #include <linux/spi/spi.h> | ||
| 28 | #include <linux/spi/flash.h> | ||
| 29 | |||
| 30 | /* Erases can take up to 3 seconds! */ | ||
| 31 | #define MAX_READY_WAIT_JIFFIES msecs_to_jiffies(3000) | ||
| 32 | |||
| 33 | #define SST25L_CMD_WRSR 0x01 /* Write status register */ | ||
| 34 | #define SST25L_CMD_WRDI 0x04 /* Write disable */ | ||
| 35 | #define SST25L_CMD_RDSR 0x05 /* Read status register */ | ||
| 36 | #define SST25L_CMD_WREN 0x06 /* Write enable */ | ||
| 37 | #define SST25L_CMD_READ 0x03 /* High speed read */ | ||
| 38 | |||
| 39 | #define SST25L_CMD_EWSR 0x50 /* Enable write status register */ | ||
| 40 | #define SST25L_CMD_SECTOR_ERASE 0x20 /* Erase sector */ | ||
| 41 | #define SST25L_CMD_READ_ID 0x90 /* Read device ID */ | ||
| 42 | #define SST25L_CMD_AAI_PROGRAM 0xaf /* Auto address increment */ | ||
| 43 | |||
| 44 | #define SST25L_STATUS_BUSY (1 << 0) /* Chip is busy */ | ||
| 45 | #define SST25L_STATUS_WREN (1 << 1) /* Write enabled */ | ||
| 46 | #define SST25L_STATUS_BP0 (1 << 2) /* Block protection 0 */ | ||
| 47 | #define SST25L_STATUS_BP1 (1 << 3) /* Block protection 1 */ | ||
| 48 | |||
| 49 | struct sst25l_flash { | ||
| 50 | struct spi_device *spi; | ||
| 51 | struct mutex lock; | ||
| 52 | struct mtd_info mtd; | ||
| 53 | |||
| 54 | int partitioned; | ||
| 55 | }; | ||
| 56 | |||
| 57 | struct flash_info { | ||
| 58 | const char *name; | ||
| 59 | uint16_t device_id; | ||
| 60 | unsigned page_size; | ||
| 61 | unsigned nr_pages; | ||
| 62 | unsigned erase_size; | ||
| 63 | }; | ||
| 64 | |||
| 65 | #define to_sst25l_flash(x) container_of(x, struct sst25l_flash, mtd) | ||
| 66 | |||
| 67 | static struct flash_info __initdata sst25l_flash_info[] = { | ||
| 68 | {"sst25lf020a", 0xbf43, 256, 1024, 4096}, | ||
| 69 | {"sst25lf040a", 0xbf44, 256, 2048, 4096}, | ||
| 70 | }; | ||
| 71 | |||
| 72 | static int sst25l_status(struct sst25l_flash *flash, int *status) | ||
| 73 | { | ||
| 74 | unsigned char command, response; | ||
| 75 | int err; | ||
| 76 | |||
| 77 | command = SST25L_CMD_RDSR; | ||
| 78 | err = spi_write_then_read(flash->spi, &command, 1, &response, 1); | ||
| 79 | if (err < 0) | ||
| 80 | return err; | ||
| 81 | |||
| 82 | *status = response; | ||
| 83 | return 0; | ||
| 84 | } | ||
| 85 | |||
| 86 | static int sst25l_write_enable(struct sst25l_flash *flash, int enable) | ||
| 87 | { | ||
| 88 | unsigned char command[2]; | ||
| 89 | int status, err; | ||
| 90 | |||
| 91 | command[0] = enable ? SST25L_CMD_WREN : SST25L_CMD_WRDI; | ||
| 92 | err = spi_write(flash->spi, command, 1); | ||
| 93 | if (err) | ||
| 94 | return err; | ||
| 95 | |||
| 96 | command[0] = SST25L_CMD_EWSR; | ||
| 97 | err = spi_write(flash->spi, command, 1); | ||
| 98 | if (err) | ||
| 99 | return err; | ||
| 100 | |||
| 101 | command[0] = SST25L_CMD_WRSR; | ||
| 102 | command[1] = enable ? 0 : SST25L_STATUS_BP0 | SST25L_STATUS_BP1; | ||
| 103 | err = spi_write(flash->spi, command, 2); | ||
| 104 | if (err) | ||
| 105 | return err; | ||
| 106 | |||
| 107 | if (enable) { | ||
| 108 | err = sst25l_status(flash, &status); | ||
| 109 | if (err) | ||
| 110 | return err; | ||
| 111 | if (!(status & SST25L_STATUS_WREN)) | ||
| 112 | return -EROFS; | ||
| 113 | } | ||
| 114 | |||
| 115 | return 0; | ||
| 116 | } | ||
| 117 | |||
| 118 | static int sst25l_wait_till_ready(struct sst25l_flash *flash) | ||
| 119 | { | ||
| 120 | unsigned long deadline; | ||
| 121 | int status, err; | ||
| 122 | |||
| 123 | deadline = jiffies + MAX_READY_WAIT_JIFFIES; | ||
| 124 | do { | ||
| 125 | err = sst25l_status(flash, &status); | ||
| 126 | if (err) | ||
| 127 | return err; | ||
| 128 | if (!(status & SST25L_STATUS_BUSY)) | ||
| 129 | return 0; | ||
| 130 | |||
| 131 | cond_resched(); | ||
| 132 | } while (!time_after_eq(jiffies, deadline)); | ||
| 133 | |||
| 134 | return -ETIMEDOUT; | ||
| 135 | } | ||
| 136 | |||
| 137 | static int sst25l_erase_sector(struct sst25l_flash *flash, uint32_t offset) | ||
| 138 | { | ||
| 139 | unsigned char command[4]; | ||
| 140 | int err; | ||
| 141 | |||
| 142 | err = sst25l_write_enable(flash, 1); | ||
| 143 | if (err) | ||
| 144 | return err; | ||
| 145 | |||
| 146 | command[0] = SST25L_CMD_SECTOR_ERASE; | ||
| 147 | command[1] = offset >> 16; | ||
| 148 | command[2] = offset >> 8; | ||
| 149 | command[3] = offset; | ||
| 150 | err = spi_write(flash->spi, command, 4); | ||
| 151 | if (err) | ||
| 152 | return err; | ||
| 153 | |||
| 154 | err = sst25l_wait_till_ready(flash); | ||
| 155 | if (err) | ||
| 156 | return err; | ||
| 157 | |||
| 158 | return sst25l_write_enable(flash, 0); | ||
| 159 | } | ||
| 160 | |||
| 161 | static int sst25l_erase(struct mtd_info *mtd, struct erase_info *instr) | ||
| 162 | { | ||
| 163 | struct sst25l_flash *flash = to_sst25l_flash(mtd); | ||
| 164 | uint32_t addr, end; | ||
| 165 | int err; | ||
| 166 | |||
| 167 | /* Sanity checks */ | ||
| 168 | if (instr->addr + instr->len > flash->mtd.size) | ||
| 169 | return -EINVAL; | ||
| 170 | |||
| 171 | if ((uint32_t)instr->len % mtd->erasesize) | ||
| 172 | return -EINVAL; | ||
| 173 | |||
| 174 | if ((uint32_t)instr->addr % mtd->erasesize) | ||
| 175 | return -EINVAL; | ||
| 176 | |||
| 177 | addr = instr->addr; | ||
| 178 | end = addr + instr->len; | ||
| 179 | |||
| 180 | mutex_lock(&flash->lock); | ||
| 181 | |||
| 182 | err = sst25l_wait_till_ready(flash); | ||
| 183 | if (err) { | ||
| 184 | mutex_unlock(&flash->lock); | ||
| 185 | return err; | ||
| 186 | } | ||
| 187 | |||
| 188 | while (addr < end) { | ||
| 189 | err = sst25l_erase_sector(flash, addr); | ||
| 190 | if (err) { | ||
| 191 | mutex_unlock(&flash->lock); | ||
| 192 | instr->state = MTD_ERASE_FAILED; | ||
| 193 | dev_err(&flash->spi->dev, "Erase failed\n"); | ||
| 194 | return err; | ||
| 195 | } | ||
| 196 | |||
| 197 | addr += mtd->erasesize; | ||
| 198 | } | ||
| 199 | |||
| 200 | mutex_unlock(&flash->lock); | ||
| 201 | |||
| 202 | instr->state = MTD_ERASE_DONE; | ||
| 203 | mtd_erase_callback(instr); | ||
| 204 | return 0; | ||
| 205 | } | ||
| 206 | |||
| 207 | static int sst25l_read(struct mtd_info *mtd, loff_t from, size_t len, | ||
| 208 | size_t *retlen, unsigned char *buf) | ||
| 209 | { | ||
| 210 | struct sst25l_flash *flash = to_sst25l_flash(mtd); | ||
| 211 | struct spi_transfer transfer[2]; | ||
| 212 | struct spi_message message; | ||
| 213 | unsigned char command[4]; | ||
| 214 | int ret; | ||
| 215 | |||
| 216 | /* Sanity checking */ | ||
| 217 | if (len == 0) | ||
| 218 | return 0; | ||
| 219 | |||
| 220 | if (from + len > flash->mtd.size) | ||
| 221 | return -EINVAL; | ||
| 222 | |||
| 223 | if (retlen) | ||
| 224 | *retlen = 0; | ||
| 225 | |||
| 226 | spi_message_init(&message); | ||
| 227 | memset(&transfer, 0, sizeof(transfer)); | ||
| 228 | |||
| 229 | command[0] = SST25L_CMD_READ; | ||
| 230 | command[1] = from >> 16; | ||
| 231 | command[2] = from >> 8; | ||
| 232 | command[3] = from; | ||
| 233 | |||
| 234 | transfer[0].tx_buf = command; | ||
| 235 | transfer[0].len = sizeof(command); | ||
| 236 | spi_message_add_tail(&transfer[0], &message); | ||
| 237 | |||
| 238 | transfer[1].rx_buf = buf; | ||
| 239 | transfer[1].len = len; | ||
| 240 | spi_message_add_tail(&transfer[1], &message); | ||
| 241 | |||
| 242 | mutex_lock(&flash->lock); | ||
| 243 | |||
| 244 | /* Wait for previous write/erase to complete */ | ||
| 245 | ret = sst25l_wait_till_ready(flash); | ||
| 246 | if (ret) { | ||
| 247 | mutex_unlock(&flash->lock); | ||
| 248 | return ret; | ||
| 249 | } | ||
| 250 | |||
| 251 | spi_sync(flash->spi, &message); | ||
| 252 | |||
| 253 | if (retlen && message.actual_length > sizeof(command)) | ||
| 254 | *retlen += message.actual_length - sizeof(command); | ||
| 255 | |||
| 256 | mutex_unlock(&flash->lock); | ||
| 257 | return 0; | ||
| 258 | } | ||
| 259 | |||
| 260 | static int sst25l_write(struct mtd_info *mtd, loff_t to, size_t len, | ||
| 261 | size_t *retlen, const unsigned char *buf) | ||
| 262 | { | ||
| 263 | struct sst25l_flash *flash = to_sst25l_flash(mtd); | ||
| 264 | int i, j, ret, bytes, copied = 0; | ||
| 265 | unsigned char command[5]; | ||
| 266 | |||
| 267 | /* Sanity checks */ | ||
| 268 | if (!len) | ||
| 269 | return 0; | ||
| 270 | |||
| 271 | if (to + len > flash->mtd.size) | ||
| 272 | return -EINVAL; | ||
| 273 | |||
| 274 | if ((uint32_t)to % mtd->writesize) | ||
| 275 | return -EINVAL; | ||
| 276 | |||
| 277 | mutex_lock(&flash->lock); | ||
| 278 | |||
| 279 | ret = sst25l_write_enable(flash, 1); | ||
| 280 | if (ret) | ||
| 281 | goto out; | ||
| 282 | |||
| 283 | for (i = 0; i < len; i += mtd->writesize) { | ||
| 284 | ret = sst25l_wait_till_ready(flash); | ||
| 285 | if (ret) | ||
| 286 | goto out; | ||
| 287 | |||
| 288 | /* Write the first byte of the page */ | ||
| 289 | command[0] = SST25L_CMD_AAI_PROGRAM; | ||
| 290 | command[1] = (to + i) >> 16; | ||
| 291 | command[2] = (to + i) >> 8; | ||
| 292 | command[3] = (to + i); | ||
| 293 | command[4] = buf[i]; | ||
| 294 | ret = spi_write(flash->spi, command, 5); | ||
| 295 | if (ret < 0) | ||
| 296 | goto out; | ||
| 297 | copied++; | ||
| 298 | |||
| 299 | /* | ||
| 300 | * Write the remaining bytes using auto address | ||
| 301 | * increment mode | ||
| 302 | */ | ||
| 303 | bytes = min_t(uint32_t, mtd->writesize, len - i); | ||
| 304 | for (j = 1; j < bytes; j++, copied++) { | ||
| 305 | ret = sst25l_wait_till_ready(flash); | ||
| 306 | if (ret) | ||
| 307 | goto out; | ||
| 308 | |||
| 309 | command[1] = buf[i + j]; | ||
| 310 | ret = spi_write(flash->spi, command, 2); | ||
| 311 | if (ret) | ||
| 312 | goto out; | ||
| 313 | } | ||
| 314 | } | ||
| 315 | |||
| 316 | out: | ||
| 317 | ret = sst25l_write_enable(flash, 0); | ||
| 318 | |||
| 319 | if (retlen) | ||
| 320 | *retlen = copied; | ||
| 321 | |||
| 322 | mutex_unlock(&flash->lock); | ||
| 323 | return ret; | ||
| 324 | } | ||
| 325 | |||
| 326 | static struct flash_info *__init sst25l_match_device(struct spi_device *spi) | ||
| 327 | { | ||
| 328 | struct flash_info *flash_info = NULL; | ||
| 329 | unsigned char command[4], response; | ||
| 330 | int i, err; | ||
| 331 | uint16_t id; | ||
| 332 | |||
| 333 | command[0] = SST25L_CMD_READ_ID; | ||
| 334 | command[1] = 0; | ||
| 335 | command[2] = 0; | ||
| 336 | command[3] = 0; | ||
| 337 | err = spi_write_then_read(spi, command, sizeof(command), &response, 1); | ||
| 338 | if (err < 0) { | ||
| 339 | dev_err(&spi->dev, "error reading device id msb\n"); | ||
| 340 | return NULL; | ||
| 341 | } | ||
| 342 | |||
| 343 | id = response << 8; | ||
| 344 | |||
| 345 | command[0] = SST25L_CMD_READ_ID; | ||
| 346 | command[1] = 0; | ||
| 347 | command[2] = 0; | ||
| 348 | command[3] = 1; | ||
| 349 | err = spi_write_then_read(spi, command, sizeof(command), &response, 1); | ||
| 350 | if (err < 0) { | ||
| 351 | dev_err(&spi->dev, "error reading device id lsb\n"); | ||
| 352 | return NULL; | ||
| 353 | } | ||
| 354 | |||
| 355 | id |= response; | ||
| 356 | |||
| 357 | for (i = 0; i < ARRAY_SIZE(sst25l_flash_info); i++) | ||
| 358 | if (sst25l_flash_info[i].device_id == id) | ||
| 359 | flash_info = &sst25l_flash_info[i]; | ||
| 360 | |||
| 361 | if (!flash_info) | ||
| 362 | dev_err(&spi->dev, "unknown id %.4x\n", id); | ||
| 363 | |||
| 364 | return flash_info; | ||
| 365 | } | ||
| 366 | |||
| 367 | static int __init sst25l_probe(struct spi_device *spi) | ||
| 368 | { | ||
| 369 | struct flash_info *flash_info; | ||
| 370 | struct sst25l_flash *flash; | ||
| 371 | struct flash_platform_data *data; | ||
| 372 | int ret, i; | ||
| 373 | |||
| 374 | flash_info = sst25l_match_device(spi); | ||
| 375 | if (!flash_info) | ||
| 376 | return -ENODEV; | ||
| 377 | |||
| 378 | flash = kzalloc(sizeof(struct sst25l_flash), GFP_KERNEL); | ||
| 379 | if (!flash) | ||
| 380 | return -ENOMEM; | ||
| 381 | |||
| 382 | flash->spi = spi; | ||
| 383 | mutex_init(&flash->lock); | ||
| 384 | dev_set_drvdata(&spi->dev, flash); | ||
| 385 | |||
| 386 | data = spi->dev.platform_data; | ||
| 387 | if (data && data->name) | ||
| 388 | flash->mtd.name = data->name; | ||
| 389 | else | ||
| 390 | flash->mtd.name = dev_name(&spi->dev); | ||
| 391 | |||
| 392 | flash->mtd.type = MTD_NORFLASH; | ||
| 393 | flash->mtd.flags = MTD_CAP_NORFLASH; | ||
| 394 | flash->mtd.erasesize = flash_info->erase_size; | ||
| 395 | flash->mtd.writesize = flash_info->page_size; | ||
| 396 | flash->mtd.size = flash_info->page_size * flash_info->nr_pages; | ||
| 397 | flash->mtd.erase = sst25l_erase; | ||
| 398 | flash->mtd.read = sst25l_read; | ||
| 399 | flash->mtd.write = sst25l_write; | ||
| 400 | |||
| 401 | dev_info(&spi->dev, "%s (%lld KiB)\n", flash_info->name, | ||
| 402 | (long long)flash->mtd.size >> 10); | ||
| 403 | |||
| 404 | DEBUG(MTD_DEBUG_LEVEL2, | ||
| 405 | "mtd .name = %s, .size = 0x%llx (%lldMiB) " | ||
| 406 | ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n", | ||
| 407 | flash->mtd.name, | ||
| 408 | (long long)flash->mtd.size, (long long)(flash->mtd.size >> 20), | ||
| 409 | flash->mtd.erasesize, flash->mtd.erasesize / 1024, | ||
| 410 | flash->mtd.numeraseregions); | ||
| 411 | |||
| 412 | if (flash->mtd.numeraseregions) | ||
| 413 | for (i = 0; i < flash->mtd.numeraseregions; i++) | ||
| 414 | DEBUG(MTD_DEBUG_LEVEL2, | ||
| 415 | "mtd.eraseregions[%d] = { .offset = 0x%llx, " | ||
| 416 | ".erasesize = 0x%.8x (%uKiB), " | ||
| 417 | ".numblocks = %d }\n", | ||
| 418 | i, (long long)flash->mtd.eraseregions[i].offset, | ||
| 419 | flash->mtd.eraseregions[i].erasesize, | ||
| 420 | flash->mtd.eraseregions[i].erasesize / 1024, | ||
| 421 | flash->mtd.eraseregions[i].numblocks); | ||
| 422 | |||
| 423 | if (mtd_has_partitions()) { | ||
| 424 | struct mtd_partition *parts = NULL; | ||
| 425 | int nr_parts = 0; | ||
| 426 | |||
| 427 | if (mtd_has_cmdlinepart()) { | ||
| 428 | static const char *part_probes[] = | ||
| 429 | {"cmdlinepart", NULL}; | ||
| 430 | |||
| 431 | nr_parts = parse_mtd_partitions(&flash->mtd, | ||
| 432 | part_probes, | ||
| 433 | &parts, 0); | ||
| 434 | } | ||
| 435 | |||
| 436 | if (nr_parts <= 0 && data && data->parts) { | ||
| 437 | parts = data->parts; | ||
| 438 | nr_parts = data->nr_parts; | ||
| 439 | } | ||
| 440 | |||
| 441 | if (nr_parts > 0) { | ||
| 442 | for (i = 0; i < nr_parts; i++) { | ||
| 443 | DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = " | ||
| 444 | "{.name = %s, .offset = 0x%llx, " | ||
| 445 | ".size = 0x%llx (%lldKiB) }\n", | ||
| 446 | i, parts[i].name, | ||
| 447 | (long long)parts[i].offset, | ||
| 448 | (long long)parts[i].size, | ||
| 449 | (long long)(parts[i].size >> 10)); | ||
| 450 | } | ||
| 451 | |||
| 452 | flash->partitioned = 1; | ||
| 453 | return add_mtd_partitions(&flash->mtd, | ||
| 454 | parts, nr_parts); | ||
| 455 | } | ||
| 456 | |||
| 457 | } else if (data->nr_parts) { | ||
| 458 | dev_warn(&spi->dev, "ignoring %d default partitions on %s\n", | ||
| 459 | data->nr_parts, data->name); | ||
| 460 | } | ||
| 461 | |||
| 462 | ret = add_mtd_device(&flash->mtd); | ||
| 463 | if (ret == 1) { | ||
| 464 | kfree(flash); | ||
| 465 | dev_set_drvdata(&spi->dev, NULL); | ||
| 466 | return -ENODEV; | ||
| 467 | } | ||
| 468 | |||
| 469 | return 0; | ||
| 470 | } | ||
| 471 | |||
| 472 | static int __exit sst25l_remove(struct spi_device *spi) | ||
| 473 | { | ||
| 474 | struct sst25l_flash *flash = dev_get_drvdata(&spi->dev); | ||
| 475 | int ret; | ||
| 476 | |||
| 477 | if (mtd_has_partitions() && flash->partitioned) | ||
| 478 | ret = del_mtd_partitions(&flash->mtd); | ||
| 479 | else | ||
| 480 | ret = del_mtd_device(&flash->mtd); | ||
| 481 | if (ret == 0) | ||
| 482 | kfree(flash); | ||
| 483 | return ret; | ||
| 484 | } | ||
| 485 | |||
| 486 | static struct spi_driver sst25l_driver = { | ||
| 487 | .driver = { | ||
| 488 | .name = "sst25l", | ||
| 489 | .bus = &spi_bus_type, | ||
| 490 | .owner = THIS_MODULE, | ||
| 491 | }, | ||
| 492 | .probe = sst25l_probe, | ||
| 493 | .remove = __exit_p(sst25l_remove), | ||
| 494 | }; | ||
| 495 | |||
| 496 | static int __init sst25l_init(void) | ||
| 497 | { | ||
| 498 | return spi_register_driver(&sst25l_driver); | ||
| 499 | } | ||
| 500 | |||
| 501 | static void __exit sst25l_exit(void) | ||
| 502 | { | ||
| 503 | spi_unregister_driver(&sst25l_driver); | ||
| 504 | } | ||
| 505 | |||
| 506 | module_init(sst25l_init); | ||
| 507 | module_exit(sst25l_exit); | ||
| 508 | |||
| 509 | MODULE_DESCRIPTION("MTD SPI driver for SST25L Flash chips"); | ||
| 510 | MODULE_AUTHOR("Andre Renaud <andre@bluewatersys.com>, " | ||
| 511 | "Ryan Mallon <ryan@bluewatersys.com>"); | ||
| 512 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c index d8cf29c01cc4..8aca5523a337 100644..100755 --- a/drivers/mtd/inftlcore.c +++ b/drivers/mtd/inftlcore.c | |||
| @@ -550,7 +550,7 @@ hitused: | |||
| 550 | * waiting to be picked up. We're going to have to fold | 550 | * waiting to be picked up. We're going to have to fold |
| 551 | * a chain to make room. | 551 | * a chain to make room. |
| 552 | */ | 552 | */ |
| 553 | thisEUN = INFTL_makefreeblock(inftl, BLOCK_NIL); | 553 | thisEUN = INFTL_makefreeblock(inftl, block); |
| 554 | 554 | ||
| 555 | /* | 555 | /* |
| 556 | * Hopefully we free something, lets try again. | 556 | * Hopefully we free something, lets try again. |
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 7a58bd5522fd..3a9a960644b6 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig | |||
| @@ -484,9 +484,19 @@ config MTD_BFIN_ASYNC | |||
| 484 | 484 | ||
| 485 | If compiled as a module, it will be called bfin-async-flash. | 485 | If compiled as a module, it will be called bfin-async-flash. |
| 486 | 486 | ||
| 487 | config MTD_GPIO_ADDR | ||
| 488 | tristate "GPIO-assisted Flash Chip Support" | ||
| 489 | depends on MTD_COMPLEX_MAPPINGS | ||
| 490 | select MTD_PARTITIONS | ||
| 491 | help | ||
| 492 | Map driver which allows flashes to be partially physically addressed | ||
| 493 | and assisted by GPIOs. | ||
| 494 | |||
| 495 | If compiled as a module, it will be called gpio-addr-flash. | ||
| 496 | |||
| 487 | config MTD_UCLINUX | 497 | config MTD_UCLINUX |
| 488 | bool "Generic uClinux RAM/ROM filesystem support" | 498 | bool "Generic uClinux RAM/ROM filesystem support" |
| 489 | depends on MTD_PARTITIONS && MTD_RAM && !MMU | 499 | depends on MTD_PARTITIONS && MTD_RAM=y && !MMU |
| 490 | help | 500 | help |
| 491 | Map driver to support image based filesystems for uClinux. | 501 | Map driver to support image based filesystems for uClinux. |
| 492 | 502 | ||
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 5beb0662d724..1d5cf8636723 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile | |||
| @@ -58,5 +58,4 @@ obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o | |||
| 58 | obj-$(CONFIG_MTD_OMAP_NOR) += omap_nor.o | 58 | obj-$(CONFIG_MTD_OMAP_NOR) += omap_nor.o |
| 59 | obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o | 59 | obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o |
| 60 | obj-$(CONFIG_MTD_BFIN_ASYNC) += bfin-async-flash.o | 60 | obj-$(CONFIG_MTD_BFIN_ASYNC) += bfin-async-flash.o |
| 61 | obj-$(CONFIG_MTD_RBTX4939) += rbtx4939-flash.o | 61 | obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr-flash.o |
| 62 | obj-$(CONFIG_MTD_VMU) += vmu-flash.o | ||
diff --git a/drivers/mtd/maps/gpio-addr-flash.c b/drivers/mtd/maps/gpio-addr-flash.c new file mode 100644 index 000000000000..44ef9a49a860 --- /dev/null +++ b/drivers/mtd/maps/gpio-addr-flash.c | |||
| @@ -0,0 +1,311 @@ | |||
| 1 | /* | ||
| 2 | * drivers/mtd/maps/gpio-addr-flash.c | ||
| 3 | * | ||
| 4 | * Handle the case where a flash device is mostly addressed using physical | ||
| 5 | * line and supplemented by GPIOs. This way you can hook up say a 8MiB flash | ||
| 6 | * to a 2MiB memory range and use the GPIOs to select a particular range. | ||
| 7 | * | ||
| 8 | * Copyright © 2000 Nicolas Pitre <nico@cam.org> | ||
| 9 | * Copyright © 2005-2009 Analog Devices Inc. | ||
| 10 | * | ||
| 11 | * Enter bugs at http://blackfin.uclinux.org/ | ||
| 12 | * | ||
| 13 | * Licensed under the GPL-2 or later. | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/init.h> | ||
| 17 | #include <linux/kernel.h> | ||
| 18 | #include <linux/module.h> | ||
| 19 | #include <linux/mtd/mtd.h> | ||
| 20 | #include <linux/mtd/map.h> | ||
| 21 | #include <linux/mtd/partitions.h> | ||
| 22 | #include <linux/mtd/physmap.h> | ||
| 23 | #include <linux/platform_device.h> | ||
| 24 | #include <linux/types.h> | ||
| 25 | |||
| 26 | #include <asm/gpio.h> | ||
| 27 | #include <asm/io.h> | ||
| 28 | |||
| 29 | #define pr_devinit(fmt, args...) ({ static const __devinitconst char __fmt[] = fmt; printk(__fmt, ## args); }) | ||
| 30 | |||
| 31 | #define DRIVER_NAME "gpio-addr-flash" | ||
| 32 | #define PFX DRIVER_NAME ": " | ||
| 33 | |||
| 34 | /** | ||
| 35 | * struct async_state - keep GPIO flash state | ||
| 36 | * @mtd: MTD state for this mapping | ||
| 37 | * @map: MTD map state for this flash | ||
| 38 | * @gpio_count: number of GPIOs used to address | ||
| 39 | * @gpio_addrs: array of GPIOs to twiddle | ||
| 40 | * @gpio_values: cached GPIO values | ||
| 41 | * @win_size: dedicated memory size (if no GPIOs) | ||
| 42 | */ | ||
| 43 | struct async_state { | ||
| 44 | struct mtd_info *mtd; | ||
| 45 | struct map_info map; | ||
| 46 | size_t gpio_count; | ||
| 47 | unsigned *gpio_addrs; | ||
| 48 | int *gpio_values; | ||
| 49 | unsigned long win_size; | ||
| 50 | }; | ||
| 51 | #define gf_map_info_to_state(mi) ((struct async_state *)(mi)->map_priv_1) | ||
| 52 | |||
| 53 | /** | ||
| 54 | * gf_set_gpios() - set GPIO address lines to access specified flash offset | ||
| 55 | * @state: GPIO flash state | ||
| 56 | * @ofs: desired offset to access | ||
| 57 | * | ||
| 58 | * Rather than call the GPIO framework every time, cache the last-programmed | ||
| 59 | * value. This speeds up sequential accesses (which are by far the most common | ||
| 60 | * type). We rely on the GPIO framework to treat non-zero value as high so | ||
| 61 | * that we don't have to normalize the bits. | ||
| 62 | */ | ||
| 63 | static void gf_set_gpios(struct async_state *state, unsigned long ofs) | ||
| 64 | { | ||
| 65 | size_t i = 0; | ||
| 66 | int value; | ||
| 67 | ofs /= state->win_size; | ||
| 68 | do { | ||
| 69 | value = ofs & (1 << i); | ||
| 70 | if (state->gpio_values[i] != value) { | ||
| 71 | gpio_set_value(state->gpio_addrs[i], value); | ||
| 72 | state->gpio_values[i] = value; | ||
| 73 | } | ||
| 74 | } while (++i < state->gpio_count); | ||
| 75 | } | ||
| 76 | |||
| 77 | /** | ||
| 78 | * gf_read() - read a word at the specified offset | ||
| 79 | * @map: MTD map state | ||
| 80 | * @ofs: desired offset to read | ||
| 81 | */ | ||
| 82 | static map_word gf_read(struct map_info *map, unsigned long ofs) | ||
| 83 | { | ||
| 84 | struct async_state *state = gf_map_info_to_state(map); | ||
| 85 | uint16_t word; | ||
| 86 | map_word test; | ||
| 87 | |||
| 88 | gf_set_gpios(state, ofs); | ||
| 89 | |||
| 90 | word = readw(map->virt + (ofs % state->win_size)); | ||
| 91 | test.x[0] = word; | ||
| 92 | return test; | ||
| 93 | } | ||
| 94 | |||
| 95 | /** | ||
| 96 | * gf_copy_from() - copy a chunk of data from the flash | ||
| 97 | * @map: MTD map state | ||
| 98 | * @to: memory to copy to | ||
| 99 | * @from: flash offset to copy from | ||
| 100 | * @len: how much to copy | ||
| 101 | * | ||
| 102 | * We rely on the MTD layer to chunk up copies such that a single request here | ||
| 103 | * will not cross a window size. This allows us to only wiggle the GPIOs once | ||
| 104 | * before falling back to a normal memcpy. Reading the higher layer code shows | ||
| 105 | * that this is indeed the case, but add a BUG_ON() to future proof. | ||
| 106 | */ | ||
| 107 | static void gf_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) | ||
| 108 | { | ||
| 109 | struct async_state *state = gf_map_info_to_state(map); | ||
| 110 | |||
| 111 | gf_set_gpios(state, from); | ||
| 112 | |||
| 113 | /* BUG if operation crosses the win_size */ | ||
| 114 | BUG_ON(!((from + len) % state->win_size <= (from + len))); | ||
| 115 | |||
| 116 | /* operation does not cross the win_size, so one shot it */ | ||
| 117 | memcpy_fromio(to, map->virt + (from % state->win_size), len); | ||
| 118 | } | ||
| 119 | |||
| 120 | /** | ||
| 121 | * gf_write() - write a word at the specified offset | ||
| 122 | * @map: MTD map state | ||
| 123 | * @ofs: desired offset to write | ||
| 124 | */ | ||
| 125 | static void gf_write(struct map_info *map, map_word d1, unsigned long ofs) | ||
| 126 | { | ||
| 127 | struct async_state *state = gf_map_info_to_state(map); | ||
| 128 | uint16_t d; | ||
| 129 | |||
| 130 | gf_set_gpios(state, ofs); | ||
| 131 | |||
| 132 | d = d1.x[0]; | ||
| 133 | writew(d, map->virt + (ofs % state->win_size)); | ||
| 134 | } | ||
| 135 | |||
| 136 | /** | ||
| 137 | * gf_copy_to() - copy a chunk of data to the flash | ||
| 138 | * @map: MTD map state | ||
| 139 | * @to: flash offset to copy to | ||
| 140 | * @from: memory to copy from | ||
| 141 | * @len: how much to copy | ||
| 142 | * | ||
| 143 | * See gf_copy_from() caveat. | ||
| 144 | */ | ||
| 145 | static void gf_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) | ||
| 146 | { | ||
| 147 | struct async_state *state = gf_map_info_to_state(map); | ||
| 148 | |||
| 149 | gf_set_gpios(state, to); | ||
| 150 | |||
| 151 | /* BUG if operation crosses the win_size */ | ||
| 152 | BUG_ON(!((to + len) % state->win_size <= (to + len))); | ||
| 153 | |||
| 154 | /* operation does not cross the win_size, so one shot it */ | ||
| 155 | memcpy_toio(map->virt + (to % state->win_size), from, len); | ||
| 156 | } | ||
| 157 | |||
| 158 | #ifdef CONFIG_MTD_PARTITIONS | ||
| 159 | static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL }; | ||
| 160 | #endif | ||
| 161 | |||
| 162 | /** | ||
| 163 | * gpio_flash_probe() - setup a mapping for a GPIO assisted flash | ||
| 164 | * @pdev: platform device | ||
| 165 | * | ||
| 166 | * The platform resource layout expected looks something like: | ||
| 167 | * struct mtd_partition partitions[] = { ... }; | ||
| 168 | * struct physmap_flash_data flash_data = { ... }; | ||
| 169 | * unsigned flash_gpios[] = { GPIO_XX, GPIO_XX, ... }; | ||
| 170 | * struct resource flash_resource[] = { | ||
| 171 | * { | ||
| 172 | * .name = "cfi_probe", | ||
| 173 | * .start = 0x20000000, | ||
| 174 | * .end = 0x201fffff, | ||
| 175 | * .flags = IORESOURCE_MEM, | ||
| 176 | * }, { | ||
| 177 | * .start = (unsigned long)flash_gpios, | ||
| 178 | * .end = ARRAY_SIZE(flash_gpios), | ||
| 179 | * .flags = IORESOURCE_IRQ, | ||
| 180 | * } | ||
| 181 | * }; | ||
| 182 | * struct platform_device flash_device = { | ||
| 183 | * .name = "gpio-addr-flash", | ||
| 184 | * .dev = { .platform_data = &flash_data, }, | ||
| 185 | * .num_resources = ARRAY_SIZE(flash_resource), | ||
| 186 | * .resource = flash_resource, | ||
| 187 | * ... | ||
| 188 | * }; | ||
| 189 | */ | ||
| 190 | static int __devinit gpio_flash_probe(struct platform_device *pdev) | ||
| 191 | { | ||
| 192 | int ret; | ||
| 193 | size_t i, arr_size; | ||
| 194 | struct physmap_flash_data *pdata; | ||
| 195 | struct resource *memory; | ||
| 196 | struct resource *gpios; | ||
| 197 | struct async_state *state; | ||
| 198 | |||
| 199 | pdata = pdev->dev.platform_data; | ||
| 200 | memory = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 201 | gpios = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | ||
| 202 | |||
| 203 | if (!memory || !gpios || !gpios->end) | ||
| 204 | return -EINVAL; | ||
| 205 | |||
| 206 | arr_size = sizeof(int) * gpios->end; | ||
| 207 | state = kzalloc(sizeof(*state) + arr_size, GFP_KERNEL); | ||
| 208 | if (!state) | ||
| 209 | return -ENOMEM; | ||
| 210 | |||
| 211 | state->gpio_count = gpios->end; | ||
| 212 | state->gpio_addrs = (void *)gpios->start; | ||
| 213 | state->gpio_values = (void *)(state + 1); | ||
| 214 | state->win_size = memory->end - memory->start + 1; | ||
| 215 | memset(state->gpio_values, 0xff, arr_size); | ||
| 216 | |||
| 217 | state->map.name = DRIVER_NAME; | ||
| 218 | state->map.read = gf_read; | ||
| 219 | state->map.copy_from = gf_copy_from; | ||
| 220 | state->map.write = gf_write; | ||
| 221 | state->map.copy_to = gf_copy_to; | ||
| 222 | state->map.bankwidth = pdata->width; | ||
| 223 | state->map.size = state->win_size * (1 << state->gpio_count); | ||
| 224 | state->map.virt = (void __iomem *)memory->start; | ||
| 225 | state->map.phys = NO_XIP; | ||
| 226 | state->map.map_priv_1 = (unsigned long)state; | ||
| 227 | |||
| 228 | platform_set_drvdata(pdev, state); | ||
| 229 | |||
| 230 | i = 0; | ||
| 231 | do { | ||
| 232 | if (gpio_request(state->gpio_addrs[i], DRIVER_NAME)) { | ||
| 233 | pr_devinit(KERN_ERR PFX "failed to request gpio %d\n", | ||
| 234 | state->gpio_addrs[i]); | ||
| 235 | while (i--) | ||
| 236 | gpio_free(state->gpio_addrs[i]); | ||
| 237 | kfree(state); | ||
| 238 | return -EBUSY; | ||
| 239 | } | ||
| 240 | gpio_direction_output(state->gpio_addrs[i], 0); | ||
| 241 | } while (++i < state->gpio_count); | ||
| 242 | |||
| 243 | pr_devinit(KERN_NOTICE PFX "probing %d-bit flash bus\n", | ||
| 244 | state->map.bankwidth * 8); | ||
| 245 | state->mtd = do_map_probe(memory->name, &state->map); | ||
| 246 | if (!state->mtd) { | ||
| 247 | for (i = 0; i < state->gpio_count; ++i) | ||
| 248 | gpio_free(state->gpio_addrs[i]); | ||
| 249 | kfree(state); | ||
| 250 | return -ENXIO; | ||
| 251 | } | ||
| 252 | |||
| 253 | #ifdef CONFIG_MTD_PARTITIONS | ||
| 254 | ret = parse_mtd_partitions(state->mtd, part_probe_types, &pdata->parts, 0); | ||
| 255 | if (ret > 0) { | ||
| 256 | pr_devinit(KERN_NOTICE PFX "Using commandline partition definition\n"); | ||
| 257 | add_mtd_partitions(state->mtd, pdata->parts, ret); | ||
| 258 | kfree(pdata->parts); | ||
| 259 | |||
| 260 | } else if (pdata->nr_parts) { | ||
| 261 | pr_devinit(KERN_NOTICE PFX "Using board partition definition\n"); | ||
| 262 | add_mtd_partitions(state->mtd, pdata->parts, pdata->nr_parts); | ||
| 263 | |||
| 264 | } else | ||
| 265 | #endif | ||
| 266 | { | ||
| 267 | pr_devinit(KERN_NOTICE PFX "no partition info available, registering whole flash at once\n"); | ||
| 268 | add_mtd_device(state->mtd); | ||
| 269 | } | ||
| 270 | |||
| 271 | return 0; | ||
| 272 | } | ||
| 273 | |||
| 274 | static int __devexit gpio_flash_remove(struct platform_device *pdev) | ||
| 275 | { | ||
| 276 | struct async_state *state = platform_get_drvdata(pdev); | ||
| 277 | size_t i = 0; | ||
| 278 | do { | ||
| 279 | gpio_free(state->gpio_addrs[i]); | ||
| 280 | } while (++i < state->gpio_count); | ||
| 281 | #ifdef CONFIG_MTD_PARTITIONS | ||
| 282 | del_mtd_partitions(state->mtd); | ||
| 283 | #endif | ||
| 284 | map_destroy(state->mtd); | ||
| 285 | kfree(state); | ||
| 286 | return 0; | ||
| 287 | } | ||
| 288 | |||
| 289 | static struct platform_driver gpio_flash_driver = { | ||
| 290 | .probe = gpio_flash_probe, | ||
| 291 | .remove = __devexit_p(gpio_flash_remove), | ||
| 292 | .driver = { | ||
| 293 | .name = DRIVER_NAME, | ||
| 294 | }, | ||
| 295 | }; | ||
| 296 | |||
| 297 | static int __init gpio_flash_init(void) | ||
| 298 | { | ||
| 299 | return platform_driver_register(&gpio_flash_driver); | ||
| 300 | } | ||
| 301 | module_init(gpio_flash_init); | ||
| 302 | |||
| 303 | static void __exit gpio_flash_exit(void) | ||
| 304 | { | ||
| 305 | platform_driver_unregister(&gpio_flash_driver); | ||
| 306 | } | ||
| 307 | module_exit(gpio_flash_exit); | ||
| 308 | |||
| 309 | MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>"); | ||
| 310 | MODULE_DESCRIPTION("MTD map driver for flashes addressed physically and with gpios"); | ||
| 311 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index 39d357b2eb47..61e4eb48bb2d 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c | |||
| @@ -190,6 +190,7 @@ static int __devinit of_flash_probe(struct of_device *dev, | |||
| 190 | const u32 *p; | 190 | const u32 *p; |
| 191 | int reg_tuple_size; | 191 | int reg_tuple_size; |
| 192 | struct mtd_info **mtd_list = NULL; | 192 | struct mtd_info **mtd_list = NULL; |
| 193 | resource_size_t res_size; | ||
| 193 | 194 | ||
| 194 | reg_tuple_size = (of_n_addr_cells(dp) + of_n_size_cells(dp)) * sizeof(u32); | 195 | reg_tuple_size = (of_n_addr_cells(dp) + of_n_size_cells(dp)) * sizeof(u32); |
| 195 | 196 | ||
| @@ -204,7 +205,7 @@ static int __devinit of_flash_probe(struct of_device *dev, | |||
| 204 | dev_err(&dev->dev, "Malformed reg property on %s\n", | 205 | dev_err(&dev->dev, "Malformed reg property on %s\n", |
| 205 | dev->node->full_name); | 206 | dev->node->full_name); |
| 206 | err = -EINVAL; | 207 | err = -EINVAL; |
| 207 | goto err_out; | 208 | goto err_flash_remove; |
| 208 | } | 209 | } |
| 209 | count /= reg_tuple_size; | 210 | count /= reg_tuple_size; |
| 210 | 211 | ||
| @@ -212,14 +213,14 @@ static int __devinit of_flash_probe(struct of_device *dev, | |||
| 212 | info = kzalloc(sizeof(struct of_flash) + | 213 | info = kzalloc(sizeof(struct of_flash) + |
| 213 | sizeof(struct of_flash_list) * count, GFP_KERNEL); | 214 | sizeof(struct of_flash_list) * count, GFP_KERNEL); |
| 214 | if (!info) | 215 | if (!info) |
| 215 | goto err_out; | 216 | goto err_flash_remove; |
| 216 | |||
| 217 | mtd_list = kzalloc(sizeof(struct mtd_info) * count, GFP_KERNEL); | ||
| 218 | if (!info) | ||
| 219 | goto err_out; | ||
| 220 | 217 | ||
| 221 | dev_set_drvdata(&dev->dev, info); | 218 | dev_set_drvdata(&dev->dev, info); |
| 222 | 219 | ||
| 220 | mtd_list = kzalloc(sizeof(struct mtd_info) * count, GFP_KERNEL); | ||
| 221 | if (!mtd_list) | ||
| 222 | goto err_flash_remove; | ||
| 223 | |||
| 223 | for (i = 0; i < count; i++) { | 224 | for (i = 0; i < count; i++) { |
| 224 | err = -ENXIO; | 225 | err = -ENXIO; |
| 225 | if (of_address_to_resource(dp, i, &res)) { | 226 | if (of_address_to_resource(dp, i, &res)) { |
| @@ -233,8 +234,8 @@ static int __devinit of_flash_probe(struct of_device *dev, | |||
| 233 | (unsigned long long)res.end); | 234 | (unsigned long long)res.end); |
| 234 | 235 | ||
| 235 | err = -EBUSY; | 236 | err = -EBUSY; |
| 236 | info->list[i].res = request_mem_region(res.start, res.end - | 237 | res_size = resource_size(&res); |
| 237 | res.start + 1, | 238 | info->list[i].res = request_mem_region(res.start, res_size, |
| 238 | dev_name(&dev->dev)); | 239 | dev_name(&dev->dev)); |
| 239 | if (!info->list[i].res) | 240 | if (!info->list[i].res) |
| 240 | goto err_out; | 241 | goto err_out; |
| @@ -249,7 +250,7 @@ static int __devinit of_flash_probe(struct of_device *dev, | |||
| 249 | 250 | ||
| 250 | info->list[i].map.name = dev_name(&dev->dev); | 251 | info->list[i].map.name = dev_name(&dev->dev); |
| 251 | info->list[i].map.phys = res.start; | 252 | info->list[i].map.phys = res.start; |
| 252 | info->list[i].map.size = res.end - res.start + 1; | 253 | info->list[i].map.size = res_size; |
| 253 | info->list[i].map.bankwidth = *width; | 254 | info->list[i].map.bankwidth = *width; |
| 254 | 255 | ||
| 255 | err = -ENOMEM; | 256 | err = -ENOMEM; |
| @@ -338,6 +339,7 @@ static int __devinit of_flash_probe(struct of_device *dev, | |||
| 338 | 339 | ||
| 339 | err_out: | 340 | err_out: |
| 340 | kfree(mtd_list); | 341 | kfree(mtd_list); |
| 342 | err_flash_remove: | ||
| 341 | of_flash_remove(dev); | 343 | of_flash_remove(dev); |
| 342 | 344 | ||
| 343 | return err; | 345 | return err; |
| @@ -360,6 +362,10 @@ static struct of_device_id of_flash_match[] = { | |||
| 360 | .data = (void *)"jedec_probe", | 362 | .data = (void *)"jedec_probe", |
| 361 | }, | 363 | }, |
| 362 | { | 364 | { |
| 365 | .compatible = "mtd-ram", | ||
| 366 | .data = (void *)"map_ram", | ||
| 367 | }, | ||
| 368 | { | ||
| 363 | .type = "rom", | 369 | .type = "rom", |
| 364 | .compatible = "direct-mapped" | 370 | .compatible = "direct-mapped" |
| 365 | }, | 371 | }, |
diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c index 49c9ece76477..dafb91944e70 100644 --- a/drivers/mtd/maps/plat-ram.c +++ b/drivers/mtd/maps/plat-ram.c | |||
| @@ -175,7 +175,7 @@ static int platram_probe(struct platform_device *pdev) | |||
| 175 | /* setup map parameters */ | 175 | /* setup map parameters */ |
| 176 | 176 | ||
| 177 | info->map.phys = res->start; | 177 | info->map.phys = res->start; |
| 178 | info->map.size = (res->end - res->start) + 1; | 178 | info->map.size = resource_size(res); |
| 179 | info->map.name = pdata->mapname != NULL ? | 179 | info->map.name = pdata->mapname != NULL ? |
| 180 | (char *)pdata->mapname : (char *)pdev->name; | 180 | (char *)pdata->mapname : (char *)pdev->name; |
| 181 | info->map.bankwidth = pdata->bankwidth; | 181 | info->map.bankwidth = pdata->bankwidth; |
diff --git a/drivers/mtd/maps/pmcmsp-flash.c b/drivers/mtd/maps/pmcmsp-flash.c index 4768bd5459d6..c8fd8da4bc87 100644 --- a/drivers/mtd/maps/pmcmsp-flash.c +++ b/drivers/mtd/maps/pmcmsp-flash.c | |||
| @@ -50,7 +50,7 @@ static int fcnt; | |||
| 50 | 50 | ||
| 51 | static int __init init_msp_flash(void) | 51 | static int __init init_msp_flash(void) |
| 52 | { | 52 | { |
| 53 | int i, j; | 53 | int i, j, ret = -ENOMEM; |
| 54 | int offset, coff; | 54 | int offset, coff; |
| 55 | char *env; | 55 | char *env; |
| 56 | int pcnt; | 56 | int pcnt; |
| @@ -75,14 +75,16 @@ static int __init init_msp_flash(void) | |||
| 75 | printk(KERN_NOTICE "Found %d PMC flash devices\n", fcnt); | 75 | printk(KERN_NOTICE "Found %d PMC flash devices\n", fcnt); |
| 76 | 76 | ||
| 77 | msp_flash = kmalloc(fcnt * sizeof(struct map_info *), GFP_KERNEL); | 77 | msp_flash = kmalloc(fcnt * sizeof(struct map_info *), GFP_KERNEL); |
| 78 | if (!msp_flash) | ||
| 79 | return -ENOMEM; | ||
| 80 | |||
| 78 | msp_parts = kmalloc(fcnt * sizeof(struct mtd_partition *), GFP_KERNEL); | 81 | msp_parts = kmalloc(fcnt * sizeof(struct mtd_partition *), GFP_KERNEL); |
| 82 | if (!msp_parts) | ||
| 83 | goto free_msp_flash; | ||
| 84 | |||
| 79 | msp_maps = kcalloc(fcnt, sizeof(struct mtd_info), GFP_KERNEL); | 85 | msp_maps = kcalloc(fcnt, sizeof(struct mtd_info), GFP_KERNEL); |
| 80 | if (!msp_flash || !msp_parts || !msp_maps) { | 86 | if (!msp_maps) |
| 81 | kfree(msp_maps); | 87 | goto free_msp_parts; |
| 82 | kfree(msp_parts); | ||
| 83 | kfree(msp_flash); | ||
| 84 | return -ENOMEM; | ||
| 85 | } | ||
| 86 | 88 | ||
| 87 | /* loop over the flash devices, initializing each */ | 89 | /* loop over the flash devices, initializing each */ |
| 88 | for (i = 0; i < fcnt; i++) { | 90 | for (i = 0; i < fcnt; i++) { |
| @@ -100,13 +102,18 @@ static int __init init_msp_flash(void) | |||
| 100 | 102 | ||
| 101 | msp_parts[i] = kcalloc(pcnt, sizeof(struct mtd_partition), | 103 | msp_parts[i] = kcalloc(pcnt, sizeof(struct mtd_partition), |
| 102 | GFP_KERNEL); | 104 | GFP_KERNEL); |
| 105 | if (!msp_parts[i]) | ||
| 106 | goto cleanup_loop; | ||
| 103 | 107 | ||
| 104 | /* now initialize the devices proper */ | 108 | /* now initialize the devices proper */ |
| 105 | flash_name[5] = '0' + i; | 109 | flash_name[5] = '0' + i; |
| 106 | env = prom_getenv(flash_name); | 110 | env = prom_getenv(flash_name); |
| 107 | 111 | ||
| 108 | if (sscanf(env, "%x:%x", &addr, &size) < 2) | 112 | if (sscanf(env, "%x:%x", &addr, &size) < 2) { |
| 109 | return -ENXIO; | 113 | ret = -ENXIO; |
| 114 | kfree(msp_parts[i]); | ||
| 115 | goto cleanup_loop; | ||
| 116 | } | ||
| 110 | addr = CPHYSADDR(addr); | 117 | addr = CPHYSADDR(addr); |
| 111 | 118 | ||
| 112 | printk(KERN_NOTICE | 119 | printk(KERN_NOTICE |
| @@ -122,13 +129,23 @@ static int __init init_msp_flash(void) | |||
| 122 | */ | 129 | */ |
| 123 | if (size > CONFIG_MSP_FLASH_MAP_LIMIT) | 130 | if (size > CONFIG_MSP_FLASH_MAP_LIMIT) |
| 124 | size = CONFIG_MSP_FLASH_MAP_LIMIT; | 131 | size = CONFIG_MSP_FLASH_MAP_LIMIT; |
| 132 | |||
| 125 | msp_maps[i].virt = ioremap(addr, size); | 133 | msp_maps[i].virt = ioremap(addr, size); |
| 134 | if (msp_maps[i].virt == NULL) { | ||
| 135 | ret = -ENXIO; | ||
| 136 | kfree(msp_parts[i]); | ||
| 137 | goto cleanup_loop; | ||
| 138 | } | ||
| 139 | |||
| 126 | msp_maps[i].bankwidth = 1; | 140 | msp_maps[i].bankwidth = 1; |
| 127 | msp_maps[i].name = strncpy(kmalloc(7, GFP_KERNEL), | 141 | msp_maps[i].name = kmalloc(7, GFP_KERNEL); |
| 128 | flash_name, 7); | 142 | if (!msp_maps[i].name) { |
| 143 | iounmap(msp_maps[i].virt); | ||
| 144 | kfree(msp_parts[i]); | ||
| 145 | goto cleanup_loop; | ||
| 146 | } | ||
| 129 | 147 | ||
| 130 | if (msp_maps[i].virt == NULL) | 148 | msp_maps[i].name = strncpy(msp_maps[i].name, flash_name, 7); |
| 131 | return -ENXIO; | ||
| 132 | 149 | ||
| 133 | for (j = 0; j < pcnt; j++) { | 150 | for (j = 0; j < pcnt; j++) { |
| 134 | part_name[5] = '0' + i; | 151 | part_name[5] = '0' + i; |
| @@ -136,8 +153,14 @@ static int __init init_msp_flash(void) | |||
| 136 | 153 | ||
| 137 | env = prom_getenv(part_name); | 154 | env = prom_getenv(part_name); |
| 138 | 155 | ||
| 139 | if (sscanf(env, "%x:%x:%n", &offset, &size, &coff) < 2) | 156 | if (sscanf(env, "%x:%x:%n", &offset, &size, |
| 140 | return -ENXIO; | 157 | &coff) < 2) { |
| 158 | ret = -ENXIO; | ||
| 159 | kfree(msp_maps[i].name); | ||
| 160 | iounmap(msp_maps[i].virt); | ||
| 161 | kfree(msp_parts[i]); | ||
| 162 | goto cleanup_loop; | ||
| 163 | } | ||
| 141 | 164 | ||
| 142 | msp_parts[i][j].size = size; | 165 | msp_parts[i][j].size = size; |
| 143 | msp_parts[i][j].offset = offset; | 166 | msp_parts[i][j].offset = offset; |
| @@ -152,18 +175,37 @@ static int __init init_msp_flash(void) | |||
| 152 | add_mtd_partitions(msp_flash[i], msp_parts[i], pcnt); | 175 | add_mtd_partitions(msp_flash[i], msp_parts[i], pcnt); |
| 153 | } else { | 176 | } else { |
| 154 | printk(KERN_ERR "map probe failed for flash\n"); | 177 | printk(KERN_ERR "map probe failed for flash\n"); |
| 155 | return -ENXIO; | 178 | ret = -ENXIO; |
| 179 | kfree(msp_maps[i].name); | ||
| 180 | iounmap(msp_maps[i].virt); | ||
| 181 | kfree(msp_parts[i]); | ||
| 182 | goto cleanup_loop; | ||
| 156 | } | 183 | } |
| 157 | } | 184 | } |
| 158 | 185 | ||
| 159 | return 0; | 186 | return 0; |
| 187 | |||
| 188 | cleanup_loop: | ||
| 189 | while (i--) { | ||
| 190 | del_mtd_partitions(msp_flash[i]); | ||
| 191 | map_destroy(msp_flash[i]); | ||
| 192 | kfree(msp_maps[i].name); | ||
| 193 | iounmap(msp_maps[i].virt); | ||
| 194 | kfree(msp_parts[i]); | ||
| 195 | } | ||
| 196 | kfree(msp_maps); | ||
| 197 | free_msp_parts: | ||
| 198 | kfree(msp_parts); | ||
| 199 | free_msp_flash: | ||
| 200 | kfree(msp_flash); | ||
| 201 | return ret; | ||
| 160 | } | 202 | } |
| 161 | 203 | ||
| 162 | static void __exit cleanup_msp_flash(void) | 204 | static void __exit cleanup_msp_flash(void) |
| 163 | { | 205 | { |
| 164 | int i; | 206 | int i; |
| 165 | 207 | ||
| 166 | for (i = 0; i < sizeof(msp_flash) / sizeof(struct mtd_info **); i++) { | 208 | for (i = 0; i < fcnt; i++) { |
| 167 | del_mtd_partitions(msp_flash[i]); | 209 | del_mtd_partitions(msp_flash[i]); |
| 168 | map_destroy(msp_flash[i]); | 210 | map_destroy(msp_flash[i]); |
| 169 | iounmap((void *)msp_maps[i].virt); | 211 | iounmap((void *)msp_maps[i].virt); |
diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c index d4314fb88212..35009294b435 100644 --- a/drivers/mtd/maps/uclinux.c +++ b/drivers/mtd/maps/uclinux.c | |||
| @@ -89,7 +89,11 @@ static int __init uclinux_mtd_init(void) | |||
| 89 | mtd->priv = mapp; | 89 | mtd->priv = mapp; |
| 90 | 90 | ||
| 91 | uclinux_ram_mtdinfo = mtd; | 91 | uclinux_ram_mtdinfo = mtd; |
| 92 | #ifdef CONFIG_MTD_PARTITIONS | ||
| 92 | add_mtd_partitions(mtd, uclinux_romfs, NUM_PARTITIONS); | 93 | add_mtd_partitions(mtd, uclinux_romfs, NUM_PARTITIONS); |
| 94 | #else | ||
| 95 | add_mtd_device(mtd); | ||
| 96 | #endif | ||
| 93 | 97 | ||
| 94 | return(0); | 98 | return(0); |
| 95 | } | 99 | } |
| @@ -99,7 +103,11 @@ static int __init uclinux_mtd_init(void) | |||
| 99 | static void __exit uclinux_mtd_cleanup(void) | 103 | static void __exit uclinux_mtd_cleanup(void) |
| 100 | { | 104 | { |
| 101 | if (uclinux_ram_mtdinfo) { | 105 | if (uclinux_ram_mtdinfo) { |
| 106 | #ifdef CONFIG_MTD_PARTITIONS | ||
| 102 | del_mtd_partitions(uclinux_ram_mtdinfo); | 107 | del_mtd_partitions(uclinux_ram_mtdinfo); |
| 108 | #else | ||
| 109 | del_mtd_device(uclinux_ram_mtdinfo); | ||
| 110 | #endif | ||
| 103 | map_destroy(uclinux_ram_mtdinfo); | 111 | map_destroy(uclinux_ram_mtdinfo); |
| 104 | uclinux_ram_mtdinfo = NULL; | 112 | uclinux_ram_mtdinfo = NULL; |
| 105 | } | 113 | } |
diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index 2d70295a5fa3..9f41b1a853c1 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c | |||
| @@ -84,7 +84,7 @@ static int erase_write (struct mtd_info *mtd, unsigned long pos, | |||
| 84 | remove_wait_queue(&wait_q, &wait); | 84 | remove_wait_queue(&wait_q, &wait); |
| 85 | 85 | ||
| 86 | /* | 86 | /* |
| 87 | * Next, writhe data to flash. | 87 | * Next, write the data to flash. |
| 88 | */ | 88 | */ |
| 89 | 89 | ||
| 90 | ret = mtd->write(mtd, pos, len, &retlen, buf); | 90 | ret = mtd->write(mtd, pos, len, &retlen, buf); |
diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 792b547786b8..db6de74082ad 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c | |||
| @@ -427,7 +427,7 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
| 427 | * to-be-erased area begins. Verify that the starting | 427 | * to-be-erased area begins. Verify that the starting |
| 428 | * offset is aligned to this region's erase size: | 428 | * offset is aligned to this region's erase size: |
| 429 | */ | 429 | */ |
| 430 | if (instr->addr & (erase_regions[i].erasesize - 1)) | 430 | if (i < 0 || instr->addr & (erase_regions[i].erasesize - 1)) |
| 431 | return -EINVAL; | 431 | return -EINVAL; |
| 432 | 432 | ||
| 433 | /* | 433 | /* |
| @@ -440,8 +440,8 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
| 440 | /* | 440 | /* |
| 441 | * check if the ending offset is aligned to this region's erase size | 441 | * check if the ending offset is aligned to this region's erase size |
| 442 | */ | 442 | */ |
| 443 | if ((instr->addr + instr->len) & (erase_regions[i].erasesize - | 443 | if (i < 0 || ((instr->addr + instr->len) & |
| 444 | 1)) | 444 | (erase_regions[i].erasesize - 1))) |
| 445 | return -EINVAL; | 445 | return -EINVAL; |
| 446 | } | 446 | } |
| 447 | 447 | ||
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 69007a6eff50..467a4f177bfb 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c | |||
| @@ -213,11 +213,11 @@ static struct attribute *mtd_attrs[] = { | |||
| 213 | NULL, | 213 | NULL, |
| 214 | }; | 214 | }; |
| 215 | 215 | ||
| 216 | struct attribute_group mtd_group = { | 216 | static struct attribute_group mtd_group = { |
| 217 | .attrs = mtd_attrs, | 217 | .attrs = mtd_attrs, |
| 218 | }; | 218 | }; |
| 219 | 219 | ||
| 220 | const struct attribute_group *mtd_groups[] = { | 220 | static const struct attribute_group *mtd_groups[] = { |
| 221 | &mtd_group, | 221 | &mtd_group, |
| 222 | NULL, | 222 | NULL, |
| 223 | }; | 223 | }; |
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 742504ea96f5..b8043a9ba32d 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c | |||
| @@ -453,7 +453,8 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, | |||
| 453 | for (i = 0; i < max && regions[i].offset <= slave->offset; i++) | 453 | for (i = 0; i < max && regions[i].offset <= slave->offset; i++) |
| 454 | ; | 454 | ; |
| 455 | /* The loop searched for the region _behind_ the first one */ | 455 | /* The loop searched for the region _behind_ the first one */ |
| 456 | i--; | 456 | if (i > 0) |
| 457 | i--; | ||
| 457 | 458 | ||
| 458 | /* Pick biggest erasesize */ | 459 | /* Pick biggest erasesize */ |
| 459 | for (; i < max && regions[i].offset < end; i++) { | 460 | for (; i < max && regions[i].offset < end; i++) { |
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index ce96c091f01b..2fda0b615246 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig | |||
| @@ -80,6 +80,23 @@ config MTD_NAND_OMAP2 | |||
| 80 | help | 80 | help |
| 81 | Support for NAND flash on Texas Instruments OMAP2 and OMAP3 platforms. | 81 | Support for NAND flash on Texas Instruments OMAP2 and OMAP3 platforms. |
| 82 | 82 | ||
| 83 | config MTD_NAND_OMAP_PREFETCH | ||
| 84 | bool "GPMC prefetch support for NAND Flash device" | ||
| 85 | depends on MTD_NAND && MTD_NAND_OMAP2 | ||
| 86 | default y | ||
| 87 | help | ||
| 88 | The NAND device can be accessed for Read/Write using GPMC PREFETCH engine | ||
| 89 | to improve the performance. | ||
| 90 | |||
| 91 | config MTD_NAND_OMAP_PREFETCH_DMA | ||
| 92 | depends on MTD_NAND_OMAP_PREFETCH | ||
| 93 | bool "DMA mode" | ||
| 94 | default n | ||
| 95 | help | ||
| 96 | The GPMC PREFETCH engine can be configured eigther in MPU interrupt mode | ||
| 97 | or in DMA interrupt mode. | ||
| 98 | Say y for DMA mode or MPU mode will be used | ||
| 99 | |||
| 83 | config MTD_NAND_TS7250 | 100 | config MTD_NAND_TS7250 |
| 84 | tristate "NAND Flash device on TS-7250 board" | 101 | tristate "NAND Flash device on TS-7250 board" |
| 85 | depends on MACH_TS72XX | 102 | depends on MACH_TS72XX |
| @@ -426,6 +443,12 @@ config MTD_NAND_MXC | |||
| 426 | This enables the driver for the NAND flash controller on the | 443 | This enables the driver for the NAND flash controller on the |
| 427 | MXC processors. | 444 | MXC processors. |
| 428 | 445 | ||
| 446 | config MTD_NAND_NOMADIK | ||
| 447 | tristate "ST Nomadik 8815 NAND support" | ||
| 448 | depends on ARCH_NOMADIK | ||
| 449 | help | ||
| 450 | Driver for the NAND flash controller on the Nomadik, with ECC. | ||
| 451 | |||
| 429 | config MTD_NAND_SH_FLCTL | 452 | config MTD_NAND_SH_FLCTL |
| 430 | tristate "Support for NAND on Renesas SuperH FLCTL" | 453 | tristate "Support for NAND on Renesas SuperH FLCTL" |
| 431 | depends on MTD_NAND && SUPERH && CPU_SUBTYPE_SH7723 | 454 | depends on MTD_NAND && SUPERH && CPU_SUBTYPE_SH7723 |
| @@ -452,4 +475,11 @@ config MTD_NAND_SOCRATES | |||
| 452 | help | 475 | help |
| 453 | Enables support for NAND Flash chips wired onto Socrates board. | 476 | Enables support for NAND Flash chips wired onto Socrates board. |
| 454 | 477 | ||
| 478 | config MTD_NAND_W90P910 | ||
| 479 | tristate "Support for NAND on w90p910 evaluation board." | ||
| 480 | depends on ARCH_W90X900 && MTD_PARTITIONS | ||
| 481 | help | ||
| 482 | This enables the driver for the NAND Flash on evaluation board based | ||
| 483 | on w90p910. | ||
| 484 | |||
| 455 | endif # MTD_NAND | 485 | endif # MTD_NAND |
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index f3a786b3cff3..6950d3dabf10 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile | |||
| @@ -40,5 +40,7 @@ obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o | |||
| 40 | obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o | 40 | obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o |
| 41 | obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o | 41 | obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o |
| 42 | obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o | 42 | obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o |
| 43 | obj-$(CONFIG_MTD_NAND_W90P910) += w90p910_nand.o | ||
| 44 | obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o | ||
| 43 | 45 | ||
| 44 | nand-objs := nand_base.o nand_bbt.o | 46 | nand-objs := nand_base.o nand_bbt.o |
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 20c828ba9405..f8e9975c86e5 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c | |||
| @@ -218,7 +218,7 @@ static int atmel_nand_calculate(struct mtd_info *mtd, | |||
| 218 | * buf: buffer to store read data | 218 | * buf: buffer to store read data |
| 219 | */ | 219 | */ |
| 220 | static int atmel_nand_read_page(struct mtd_info *mtd, | 220 | static int atmel_nand_read_page(struct mtd_info *mtd, |
| 221 | struct nand_chip *chip, uint8_t *buf) | 221 | struct nand_chip *chip, uint8_t *buf, int page) |
| 222 | { | 222 | { |
| 223 | int eccsize = chip->ecc.size; | 223 | int eccsize = chip->ecc.size; |
| 224 | int eccbytes = chip->ecc.bytes; | 224 | int eccbytes = chip->ecc.bytes; |
diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index 1b4690bdfdb3..c828d9ac7bd7 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c | |||
| @@ -381,7 +381,7 @@ static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, | |||
| 381 | * we need a special oob layout and handling. | 381 | * we need a special oob layout and handling. |
| 382 | */ | 382 | */ |
| 383 | static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, | 383 | static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, |
| 384 | uint8_t *buf) | 384 | uint8_t *buf, int page) |
| 385 | { | 385 | { |
| 386 | struct cafe_priv *cafe = mtd->priv; | 386 | struct cafe_priv *cafe = mtd->priv; |
| 387 | 387 | ||
diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index 0fad6487e6f4..f13f5b9afaf7 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c | |||
| @@ -348,6 +348,12 @@ compare: | |||
| 348 | if (!(syndrome[0] | syndrome[1] | syndrome[2] | syndrome[3])) | 348 | if (!(syndrome[0] | syndrome[1] | syndrome[2] | syndrome[3])) |
| 349 | return 0; | 349 | return 0; |
| 350 | 350 | ||
| 351 | /* | ||
| 352 | * Clear any previous address calculation by doing a dummy read of an | ||
| 353 | * error address register. | ||
| 354 | */ | ||
| 355 | davinci_nand_readl(info, NAND_ERR_ADD1_OFFSET); | ||
| 356 | |||
| 351 | /* Start address calculation, and wait for it to complete. | 357 | /* Start address calculation, and wait for it to complete. |
| 352 | * We _could_ start reading more data while this is working, | 358 | * We _could_ start reading more data while this is working, |
| 353 | * to speed up the overall page read. | 359 | * to speed up the overall page read. |
| @@ -359,8 +365,10 @@ compare: | |||
| 359 | 365 | ||
| 360 | switch ((fsr >> 8) & 0x0f) { | 366 | switch ((fsr >> 8) & 0x0f) { |
| 361 | case 0: /* no error, should not happen */ | 367 | case 0: /* no error, should not happen */ |
| 368 | davinci_nand_readl(info, NAND_ERR_ERRVAL1_OFFSET); | ||
| 362 | return 0; | 369 | return 0; |
| 363 | case 1: /* five or more errors detected */ | 370 | case 1: /* five or more errors detected */ |
| 371 | davinci_nand_readl(info, NAND_ERR_ERRVAL1_OFFSET); | ||
| 364 | return -EIO; | 372 | return -EIO; |
| 365 | case 2: /* error addresses computed */ | 373 | case 2: /* error addresses computed */ |
| 366 | case 3: | 374 | case 3: |
| @@ -500,6 +508,26 @@ static struct nand_ecclayout hwecc4_small __initconst = { | |||
| 500 | }, | 508 | }, |
| 501 | }; | 509 | }; |
| 502 | 510 | ||
| 511 | /* An ECC layout for using 4-bit ECC with large-page (2048bytes) flash, | ||
| 512 | * storing ten ECC bytes plus the manufacturer's bad block marker byte, | ||
| 513 | * and not overlapping the default BBT markers. | ||
| 514 | */ | ||
| 515 | static struct nand_ecclayout hwecc4_2048 __initconst = { | ||
| 516 | .eccbytes = 40, | ||
| 517 | .eccpos = { | ||
| 518 | /* at the end of spare sector */ | ||
| 519 | 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, | ||
| 520 | 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, | ||
| 521 | 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, | ||
| 522 | 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, | ||
| 523 | }, | ||
| 524 | .oobfree = { | ||
| 525 | /* 2 bytes at offset 0 hold manufacturer badblock markers */ | ||
| 526 | {.offset = 2, .length = 22, }, | ||
| 527 | /* 5 bytes at offset 8 hold BBT markers */ | ||
| 528 | /* 8 bytes at offset 16 hold JFFS2 clean markers */ | ||
| 529 | }, | ||
| 530 | }; | ||
| 503 | 531 | ||
| 504 | static int __init nand_davinci_probe(struct platform_device *pdev) | 532 | static int __init nand_davinci_probe(struct platform_device *pdev) |
| 505 | { | 533 | { |
| @@ -690,15 +718,20 @@ static int __init nand_davinci_probe(struct platform_device *pdev) | |||
| 690 | info->mtd.oobsize - 16; | 718 | info->mtd.oobsize - 16; |
| 691 | goto syndrome_done; | 719 | goto syndrome_done; |
| 692 | } | 720 | } |
| 721 | if (chunks == 4) { | ||
| 722 | info->ecclayout = hwecc4_2048; | ||
| 723 | info->chip.ecc.mode = NAND_ECC_HW_OOB_FIRST; | ||
| 724 | goto syndrome_done; | ||
| 725 | } | ||
| 693 | 726 | ||
| 694 | /* For large page chips we'll be wanting to use a | 727 | /* 4KiB page chips are not yet supported. The eccpos from |
| 695 | * not-yet-implemented mode that reads OOB data | 728 | * nand_ecclayout cannot hold 80 bytes and change to eccpos[] |
| 696 | * before reading the body of the page, to avoid | 729 | * breaks userspace ioctl interface with mtd-utils. Once we |
| 697 | * the "infix OOB" model of NAND_ECC_HW_SYNDROME | 730 | * resolve this issue, NAND_ECC_HW_OOB_FIRST mode can be used |
| 698 | * (and preserve manufacturer badblock markings). | 731 | * for the 4KiB page chips. |
| 699 | */ | 732 | */ |
| 700 | dev_warn(&pdev->dev, "no 4-bit ECC support yet " | 733 | dev_warn(&pdev->dev, "no 4-bit ECC support yet " |
| 701 | "for large page NAND\n"); | 734 | "for 4KiB-page NAND\n"); |
| 702 | ret = -EIO; | 735 | ret = -EIO; |
| 703 | goto err_scan; | 736 | goto err_scan; |
| 704 | 737 | ||
diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 1f6eb2578717..ddd37d2554ed 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c | |||
| @@ -739,7 +739,8 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) | |||
| 739 | 739 | ||
| 740 | static int fsl_elbc_read_page(struct mtd_info *mtd, | 740 | static int fsl_elbc_read_page(struct mtd_info *mtd, |
| 741 | struct nand_chip *chip, | 741 | struct nand_chip *chip, |
| 742 | uint8_t *buf) | 742 | uint8_t *buf, |
| 743 | int page) | ||
| 743 | { | 744 | { |
| 744 | fsl_elbc_read_buf(mtd, buf, mtd->writesize); | 745 | fsl_elbc_read_buf(mtd, buf, mtd->writesize); |
| 745 | fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize); | 746 | fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize); |
diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 76beea40d2cf..65b26d5a5c0d 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c | |||
| @@ -857,6 +857,17 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, | |||
| 857 | } | 857 | } |
| 858 | } | 858 | } |
| 859 | 859 | ||
| 860 | /* Define some generic bad / good block scan pattern which are used | ||
| 861 | * while scanning a device for factory marked good / bad blocks. */ | ||
| 862 | static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; | ||
| 863 | |||
| 864 | static struct nand_bbt_descr smallpage_memorybased = { | ||
| 865 | .options = NAND_BBT_SCAN2NDPAGE, | ||
| 866 | .offs = 5, | ||
| 867 | .len = 1, | ||
| 868 | .pattern = scan_ff_pattern | ||
| 869 | }; | ||
| 870 | |||
| 860 | static int __init mxcnd_probe(struct platform_device *pdev) | 871 | static int __init mxcnd_probe(struct platform_device *pdev) |
| 861 | { | 872 | { |
| 862 | struct nand_chip *this; | 873 | struct nand_chip *this; |
| @@ -973,7 +984,10 @@ static int __init mxcnd_probe(struct platform_device *pdev) | |||
| 973 | goto escan; | 984 | goto escan; |
| 974 | } | 985 | } |
| 975 | 986 | ||
| 976 | host->pagesize_2k = (mtd->writesize == 2048) ? 1 : 0; | 987 | if (mtd->writesize == 2048) { |
| 988 | host->pagesize_2k = 1; | ||
| 989 | this->badblock_pattern = &smallpage_memorybased; | ||
| 990 | } | ||
| 977 | 991 | ||
| 978 | if (this->ecc.mode == NAND_ECC_HW) { | 992 | if (this->ecc.mode == NAND_ECC_HW) { |
| 979 | switch (mtd->oobsize) { | 993 | switch (mtd->oobsize) { |
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 8c21b89d2d0c..22113865438b 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c | |||
| @@ -688,8 +688,7 @@ nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state) | |||
| 688 | retry: | 688 | retry: |
| 689 | spin_lock(lock); | 689 | spin_lock(lock); |
| 690 | 690 | ||
| 691 | /* Hardware controller shared among independend devices */ | 691 | /* Hardware controller shared among independent devices */ |
| 692 | /* Hardware controller shared among independend devices */ | ||
| 693 | if (!chip->controller->active) | 692 | if (!chip->controller->active) |
| 694 | chip->controller->active = chip; | 693 | chip->controller->active = chip; |
| 695 | 694 | ||
| @@ -766,7 +765,7 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) | |||
| 766 | * Not for syndrome calculating ecc controllers, which use a special oob layout | 765 | * Not for syndrome calculating ecc controllers, which use a special oob layout |
| 767 | */ | 766 | */ |
| 768 | static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, | 767 | static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, |
| 769 | uint8_t *buf) | 768 | uint8_t *buf, int page) |
| 770 | { | 769 | { |
| 771 | chip->read_buf(mtd, buf, mtd->writesize); | 770 | chip->read_buf(mtd, buf, mtd->writesize); |
| 772 | chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); | 771 | chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); |
| @@ -782,7 +781,7 @@ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, | |||
| 782 | * We need a special oob layout and handling even when OOB isn't used. | 781 | * We need a special oob layout and handling even when OOB isn't used. |
| 783 | */ | 782 | */ |
| 784 | static int nand_read_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip, | 783 | static int nand_read_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip, |
| 785 | uint8_t *buf) | 784 | uint8_t *buf, int page) |
| 786 | { | 785 | { |
| 787 | int eccsize = chip->ecc.size; | 786 | int eccsize = chip->ecc.size; |
| 788 | int eccbytes = chip->ecc.bytes; | 787 | int eccbytes = chip->ecc.bytes; |
| @@ -821,7 +820,7 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *c | |||
| 821 | * @buf: buffer to store read data | 820 | * @buf: buffer to store read data |
| 822 | */ | 821 | */ |
| 823 | static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, | 822 | static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, |
| 824 | uint8_t *buf) | 823 | uint8_t *buf, int page) |
| 825 | { | 824 | { |
| 826 | int i, eccsize = chip->ecc.size; | 825 | int i, eccsize = chip->ecc.size; |
| 827 | int eccbytes = chip->ecc.bytes; | 826 | int eccbytes = chip->ecc.bytes; |
| @@ -831,7 +830,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, | |||
| 831 | uint8_t *ecc_code = chip->buffers->ecccode; | 830 | uint8_t *ecc_code = chip->buffers->ecccode; |
| 832 | uint32_t *eccpos = chip->ecc.layout->eccpos; | 831 | uint32_t *eccpos = chip->ecc.layout->eccpos; |
| 833 | 832 | ||
| 834 | chip->ecc.read_page_raw(mtd, chip, buf); | 833 | chip->ecc.read_page_raw(mtd, chip, buf, page); |
| 835 | 834 | ||
| 836 | for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) | 835 | for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) |
| 837 | chip->ecc.calculate(mtd, p, &ecc_calc[i]); | 836 | chip->ecc.calculate(mtd, p, &ecc_calc[i]); |
| @@ -944,7 +943,7 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint3 | |||
| 944 | * Not for syndrome calculating ecc controllers which need a special oob layout | 943 | * Not for syndrome calculating ecc controllers which need a special oob layout |
| 945 | */ | 944 | */ |
| 946 | static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, | 945 | static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, |
| 947 | uint8_t *buf) | 946 | uint8_t *buf, int page) |
| 948 | { | 947 | { |
| 949 | int i, eccsize = chip->ecc.size; | 948 | int i, eccsize = chip->ecc.size; |
| 950 | int eccbytes = chip->ecc.bytes; | 949 | int eccbytes = chip->ecc.bytes; |
| @@ -980,6 +979,54 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, | |||
| 980 | } | 979 | } |
| 981 | 980 | ||
| 982 | /** | 981 | /** |
| 982 | * nand_read_page_hwecc_oob_first - [REPLACABLE] hw ecc, read oob first | ||
| 983 | * @mtd: mtd info structure | ||
| 984 | * @chip: nand chip info structure | ||
| 985 | * @buf: buffer to store read data | ||
| 986 | * | ||
| 987 | * Hardware ECC for large page chips, require OOB to be read first. | ||
| 988 | * For this ECC mode, the write_page method is re-used from ECC_HW. | ||
| 989 | * These methods read/write ECC from the OOB area, unlike the | ||
| 990 | * ECC_HW_SYNDROME support with multiple ECC steps, follows the | ||
| 991 | * "infix ECC" scheme and reads/writes ECC from the data area, by | ||
| 992 | * overwriting the NAND manufacturer bad block markings. | ||
| 993 | */ | ||
| 994 | static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, | ||
| 995 | struct nand_chip *chip, uint8_t *buf, int page) | ||
| 996 | { | ||
| 997 | int i, eccsize = chip->ecc.size; | ||
| 998 | int eccbytes = chip->ecc.bytes; | ||
| 999 | int eccsteps = chip->ecc.steps; | ||
| 1000 | uint8_t *p = buf; | ||
| 1001 | uint8_t *ecc_code = chip->buffers->ecccode; | ||
| 1002 | uint32_t *eccpos = chip->ecc.layout->eccpos; | ||
| 1003 | uint8_t *ecc_calc = chip->buffers->ecccalc; | ||
| 1004 | |||
| 1005 | /* Read the OOB area first */ | ||
| 1006 | chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); | ||
| 1007 | chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); | ||
| 1008 | chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); | ||
| 1009 | |||
| 1010 | for (i = 0; i < chip->ecc.total; i++) | ||
| 1011 | ecc_code[i] = chip->oob_poi[eccpos[i]]; | ||
| 1012 | |||
| 1013 | for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { | ||
| 1014 | int stat; | ||
| 1015 | |||
| 1016 | chip->ecc.hwctl(mtd, NAND_ECC_READ); | ||
| 1017 | chip->read_buf(mtd, p, eccsize); | ||
| 1018 | chip->ecc.calculate(mtd, p, &ecc_calc[i]); | ||
| 1019 | |||
| 1020 | stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL); | ||
| 1021 | if (stat < 0) | ||
| 1022 | mtd->ecc_stats.failed++; | ||
| 1023 | else | ||
| 1024 | mtd->ecc_stats.corrected += stat; | ||
| 1025 | } | ||
| 1026 | return 0; | ||
| 1027 | } | ||
| 1028 | |||
| 1029 | /** | ||
| 983 | * nand_read_page_syndrome - [REPLACABLE] hardware ecc syndrom based page read | 1030 | * nand_read_page_syndrome - [REPLACABLE] hardware ecc syndrom based page read |
| 984 | * @mtd: mtd info structure | 1031 | * @mtd: mtd info structure |
| 985 | * @chip: nand chip info structure | 1032 | * @chip: nand chip info structure |
| @@ -989,7 +1036,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, | |||
| 989 | * we need a special oob layout and handling. | 1036 | * we need a special oob layout and handling. |
| 990 | */ | 1037 | */ |
| 991 | static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, | 1038 | static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, |
| 992 | uint8_t *buf) | 1039 | uint8_t *buf, int page) |
| 993 | { | 1040 | { |
| 994 | int i, eccsize = chip->ecc.size; | 1041 | int i, eccsize = chip->ecc.size; |
| 995 | int eccbytes = chip->ecc.bytes; | 1042 | int eccbytes = chip->ecc.bytes; |
| @@ -1131,11 +1178,13 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, | |||
| 1131 | 1178 | ||
| 1132 | /* Now read the page into the buffer */ | 1179 | /* Now read the page into the buffer */ |
| 1133 | if (unlikely(ops->mode == MTD_OOB_RAW)) | 1180 | if (unlikely(ops->mode == MTD_OOB_RAW)) |
| 1134 | ret = chip->ecc.read_page_raw(mtd, chip, bufpoi); | 1181 | ret = chip->ecc.read_page_raw(mtd, chip, |
| 1182 | bufpoi, page); | ||
| 1135 | else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob) | 1183 | else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob) |
| 1136 | ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi); | 1184 | ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi); |
| 1137 | else | 1185 | else |
| 1138 | ret = chip->ecc.read_page(mtd, chip, bufpoi); | 1186 | ret = chip->ecc.read_page(mtd, chip, bufpoi, |
| 1187 | page); | ||
| 1139 | if (ret < 0) | 1188 | if (ret < 0) |
| 1140 | break; | 1189 | break; |
| 1141 | 1190 | ||
| @@ -1413,8 +1462,8 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, | |||
| 1413 | int len; | 1462 | int len; |
| 1414 | uint8_t *buf = ops->oobbuf; | 1463 | uint8_t *buf = ops->oobbuf; |
| 1415 | 1464 | ||
| 1416 | DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n", | 1465 | DEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08Lx, len = %i\n", |
| 1417 | (unsigned long long)from, readlen); | 1466 | __func__, (unsigned long long)from, readlen); |
| 1418 | 1467 | ||
| 1419 | if (ops->mode == MTD_OOB_AUTO) | 1468 | if (ops->mode == MTD_OOB_AUTO) |
| 1420 | len = chip->ecc.layout->oobavail; | 1469 | len = chip->ecc.layout->oobavail; |
| @@ -1422,8 +1471,8 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, | |||
| 1422 | len = mtd->oobsize; | 1471 | len = mtd->oobsize; |
| 1423 | 1472 | ||
| 1424 | if (unlikely(ops->ooboffs >= len)) { | 1473 | if (unlikely(ops->ooboffs >= len)) { |
| 1425 | DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " | 1474 | DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to start read " |
| 1426 | "Attempt to start read outside oob\n"); | 1475 | "outside oob\n", __func__); |
| 1427 | return -EINVAL; | 1476 | return -EINVAL; |
| 1428 | } | 1477 | } |
| 1429 | 1478 | ||
| @@ -1431,8 +1480,8 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, | |||
| 1431 | if (unlikely(from >= mtd->size || | 1480 | if (unlikely(from >= mtd->size || |
| 1432 | ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) - | 1481 | ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) - |
| 1433 | (from >> chip->page_shift)) * len)) { | 1482 | (from >> chip->page_shift)) * len)) { |
| 1434 | DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " | 1483 | DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt read beyond end " |
| 1435 | "Attempt read beyond end of device\n"); | 1484 | "of device\n", __func__); |
| 1436 | return -EINVAL; | 1485 | return -EINVAL; |
| 1437 | } | 1486 | } |
| 1438 | 1487 | ||
| @@ -1506,8 +1555,8 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, | |||
| 1506 | 1555 | ||
| 1507 | /* Do not allow reads past end of device */ | 1556 | /* Do not allow reads past end of device */ |
| 1508 | if (ops->datbuf && (from + ops->len) > mtd->size) { | 1557 | if (ops->datbuf && (from + ops->len) > mtd->size) { |
| 1509 | DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " | 1558 | DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt read " |
| 1510 | "Attempt read beyond end of device\n"); | 1559 | "beyond end of device\n", __func__); |
| 1511 | return -EINVAL; | 1560 | return -EINVAL; |
| 1512 | } | 1561 | } |
| 1513 | 1562 | ||
| @@ -1816,8 +1865,8 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, | |||
| 1816 | 1865 | ||
| 1817 | /* reject writes, which are not page aligned */ | 1866 | /* reject writes, which are not page aligned */ |
| 1818 | if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { | 1867 | if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { |
| 1819 | printk(KERN_NOTICE "nand_write: " | 1868 | printk(KERN_NOTICE "%s: Attempt to write not " |
| 1820 | "Attempt to write not page aligned data\n"); | 1869 | "page aligned data\n", __func__); |
| 1821 | return -EINVAL; | 1870 | return -EINVAL; |
| 1822 | } | 1871 | } |
| 1823 | 1872 | ||
| @@ -1944,8 +1993,8 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, | |||
| 1944 | int chipnr, page, status, len; | 1993 | int chipnr, page, status, len; |
| 1945 | struct nand_chip *chip = mtd->priv; | 1994 | struct nand_chip *chip = mtd->priv; |
| 1946 | 1995 | ||
| 1947 | DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", | 1996 | DEBUG(MTD_DEBUG_LEVEL3, "%s: to = 0x%08x, len = %i\n", |
| 1948 | (unsigned int)to, (int)ops->ooblen); | 1997 | __func__, (unsigned int)to, (int)ops->ooblen); |
| 1949 | 1998 | ||
| 1950 | if (ops->mode == MTD_OOB_AUTO) | 1999 | if (ops->mode == MTD_OOB_AUTO) |
| 1951 | len = chip->ecc.layout->oobavail; | 2000 | len = chip->ecc.layout->oobavail; |
| @@ -1954,14 +2003,14 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, | |||
| 1954 | 2003 | ||
| 1955 | /* Do not allow write past end of page */ | 2004 | /* Do not allow write past end of page */ |
| 1956 | if ((ops->ooboffs + ops->ooblen) > len) { | 2005 | if ((ops->ooboffs + ops->ooblen) > len) { |
| 1957 | DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " | 2006 | DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to write " |
| 1958 | "Attempt to write past end of page\n"); | 2007 | "past end of page\n", __func__); |
| 1959 | return -EINVAL; | 2008 | return -EINVAL; |
| 1960 | } | 2009 | } |
| 1961 | 2010 | ||
| 1962 | if (unlikely(ops->ooboffs >= len)) { | 2011 | if (unlikely(ops->ooboffs >= len)) { |
| 1963 | DEBUG(MTD_DEBUG_LEVEL0, "nand_do_write_oob: " | 2012 | DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to start " |
| 1964 | "Attempt to start write outside oob\n"); | 2013 | "write outside oob\n", __func__); |
| 1965 | return -EINVAL; | 2014 | return -EINVAL; |
| 1966 | } | 2015 | } |
| 1967 | 2016 | ||
| @@ -1970,8 +2019,8 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, | |||
| 1970 | ops->ooboffs + ops->ooblen > | 2019 | ops->ooboffs + ops->ooblen > |
| 1971 | ((mtd->size >> chip->page_shift) - | 2020 | ((mtd->size >> chip->page_shift) - |
| 1972 | (to >> chip->page_shift)) * len)) { | 2021 | (to >> chip->page_shift)) * len)) { |
| 1973 | DEBUG(MTD_DEBUG_LEVEL0, "nand_do_write_oob: " | 2022 | DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt write beyond " |
| 1974 | "Attempt write beyond end of device\n"); | 2023 | "end of device\n", __func__); |
| 1975 | return -EINVAL; | 2024 | return -EINVAL; |
| 1976 | } | 2025 | } |
| 1977 | 2026 | ||
| @@ -2026,8 +2075,8 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to, | |||
| 2026 | 2075 | ||
| 2027 | /* Do not allow writes past end of device */ | 2076 | /* Do not allow writes past end of device */ |
| 2028 | if (ops->datbuf && (to + ops->len) > mtd->size) { | 2077 | if (ops->datbuf && (to + ops->len) > mtd->size) { |
| 2029 | DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " | 2078 | DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt write beyond " |
| 2030 | "Attempt write beyond end of device\n"); | 2079 | "end of device\n", __func__); |
| 2031 | return -EINVAL; | 2080 | return -EINVAL; |
| 2032 | } | 2081 | } |
| 2033 | 2082 | ||
| @@ -2117,26 +2166,27 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, | |||
| 2117 | unsigned int bbt_masked_page = 0xffffffff; | 2166 | unsigned int bbt_masked_page = 0xffffffff; |
| 2118 | loff_t len; | 2167 | loff_t len; |
| 2119 | 2168 | ||
| 2120 | DEBUG(MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%012llx, len = %llu\n", | 2169 | DEBUG(MTD_DEBUG_LEVEL3, "%s: start = 0x%012llx, len = %llu\n", |
| 2121 | (unsigned long long)instr->addr, (unsigned long long)instr->len); | 2170 | __func__, (unsigned long long)instr->addr, |
| 2171 | (unsigned long long)instr->len); | ||
| 2122 | 2172 | ||
| 2123 | /* Start address must align on block boundary */ | 2173 | /* Start address must align on block boundary */ |
| 2124 | if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) { | 2174 | if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) { |
| 2125 | DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n"); | 2175 | DEBUG(MTD_DEBUG_LEVEL0, "%s: Unaligned address\n", __func__); |
| 2126 | return -EINVAL; | 2176 | return -EINVAL; |
| 2127 | } | 2177 | } |
| 2128 | 2178 | ||
| 2129 | /* Length must align on block boundary */ | 2179 | /* Length must align on block boundary */ |
| 2130 | if (instr->len & ((1 << chip->phys_erase_shift) - 1)) { | 2180 | if (instr->len & ((1 << chip->phys_erase_shift) - 1)) { |
| 2131 | DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " | 2181 | DEBUG(MTD_DEBUG_LEVEL0, "%s: Length not block aligned\n", |
| 2132 | "Length not block aligned\n"); | 2182 | __func__); |
| 2133 | return -EINVAL; | 2183 | return -EINVAL; |
| 2134 | } | 2184 | } |
| 2135 | 2185 | ||
| 2136 | /* Do not allow erase past end of device */ | 2186 | /* Do not allow erase past end of device */ |
| 2137 | if ((instr->len + instr->addr) > mtd->size) { | 2187 | if ((instr->len + instr->addr) > mtd->size) { |
| 2138 | DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " | 2188 | DEBUG(MTD_DEBUG_LEVEL0, "%s: Erase past end of device\n", |
| 2139 | "Erase past end of device\n"); | 2189 | __func__); |
| 2140 | return -EINVAL; | 2190 | return -EINVAL; |
| 2141 | } | 2191 | } |
| 2142 | 2192 | ||
| @@ -2157,8 +2207,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, | |||
| 2157 | 2207 | ||
| 2158 | /* Check, if it is write protected */ | 2208 | /* Check, if it is write protected */ |
| 2159 | if (nand_check_wp(mtd)) { | 2209 | if (nand_check_wp(mtd)) { |
| 2160 | DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " | 2210 | DEBUG(MTD_DEBUG_LEVEL0, "%s: Device is write protected!!!\n", |
| 2161 | "Device is write protected!!!\n"); | 2211 | __func__); |
| 2162 | instr->state = MTD_ERASE_FAILED; | 2212 | instr->state = MTD_ERASE_FAILED; |
| 2163 | goto erase_exit; | 2213 | goto erase_exit; |
| 2164 | } | 2214 | } |
| @@ -2183,8 +2233,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, | |||
| 2183 | */ | 2233 | */ |
| 2184 | if (nand_block_checkbad(mtd, ((loff_t) page) << | 2234 | if (nand_block_checkbad(mtd, ((loff_t) page) << |
| 2185 | chip->page_shift, 0, allowbbt)) { | 2235 | chip->page_shift, 0, allowbbt)) { |
| 2186 | printk(KERN_WARNING "nand_erase: attempt to erase a " | 2236 | printk(KERN_WARNING "%s: attempt to erase a bad block " |
| 2187 | "bad block at page 0x%08x\n", page); | 2237 | "at page 0x%08x\n", __func__, page); |
| 2188 | instr->state = MTD_ERASE_FAILED; | 2238 | instr->state = MTD_ERASE_FAILED; |
| 2189 | goto erase_exit; | 2239 | goto erase_exit; |
| 2190 | } | 2240 | } |
| @@ -2211,8 +2261,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, | |||
| 2211 | 2261 | ||
| 2212 | /* See if block erase succeeded */ | 2262 | /* See if block erase succeeded */ |
| 2213 | if (status & NAND_STATUS_FAIL) { | 2263 | if (status & NAND_STATUS_FAIL) { |
| 2214 | DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " | 2264 | DEBUG(MTD_DEBUG_LEVEL0, "%s: Failed erase, " |
| 2215 | "Failed erase, page 0x%08x\n", page); | 2265 | "page 0x%08x\n", __func__, page); |
| 2216 | instr->state = MTD_ERASE_FAILED; | 2266 | instr->state = MTD_ERASE_FAILED; |
| 2217 | instr->fail_addr = | 2267 | instr->fail_addr = |
| 2218 | ((loff_t)page << chip->page_shift); | 2268 | ((loff_t)page << chip->page_shift); |
| @@ -2272,9 +2322,9 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, | |||
| 2272 | if (!rewrite_bbt[chipnr]) | 2322 | if (!rewrite_bbt[chipnr]) |
| 2273 | continue; | 2323 | continue; |
| 2274 | /* update the BBT for chip */ | 2324 | /* update the BBT for chip */ |
| 2275 | DEBUG(MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt " | 2325 | DEBUG(MTD_DEBUG_LEVEL0, "%s: nand_update_bbt " |
| 2276 | "(%d:0x%0llx 0x%0x)\n", chipnr, rewrite_bbt[chipnr], | 2326 | "(%d:0x%0llx 0x%0x)\n", __func__, chipnr, |
| 2277 | chip->bbt_td->pages[chipnr]); | 2327 | rewrite_bbt[chipnr], chip->bbt_td->pages[chipnr]); |
| 2278 | nand_update_bbt(mtd, rewrite_bbt[chipnr]); | 2328 | nand_update_bbt(mtd, rewrite_bbt[chipnr]); |
| 2279 | } | 2329 | } |
| 2280 | 2330 | ||
| @@ -2292,7 +2342,7 @@ static void nand_sync(struct mtd_info *mtd) | |||
| 2292 | { | 2342 | { |
| 2293 | struct nand_chip *chip = mtd->priv; | 2343 | struct nand_chip *chip = mtd->priv; |
| 2294 | 2344 | ||
| 2295 | DEBUG(MTD_DEBUG_LEVEL3, "nand_sync: called\n"); | 2345 | DEBUG(MTD_DEBUG_LEVEL3, "%s: called\n", __func__); |
| 2296 | 2346 | ||
| 2297 | /* Grab the lock and see if the device is available */ | 2347 | /* Grab the lock and see if the device is available */ |
| 2298 | nand_get_device(chip, mtd, FL_SYNCING); | 2348 | nand_get_device(chip, mtd, FL_SYNCING); |
| @@ -2356,8 +2406,8 @@ static void nand_resume(struct mtd_info *mtd) | |||
| 2356 | if (chip->state == FL_PM_SUSPENDED) | 2406 | if (chip->state == FL_PM_SUSPENDED) |
| 2357 | nand_release_device(mtd); | 2407 | nand_release_device(mtd); |
| 2358 | else | 2408 | else |
| 2359 | printk(KERN_ERR "nand_resume() called for a chip which is not " | 2409 | printk(KERN_ERR "%s called for a chip which is not " |
| 2360 | "in suspended state\n"); | 2410 | "in suspended state\n", __func__); |
| 2361 | } | 2411 | } |
| 2362 | 2412 | ||
| 2363 | /* | 2413 | /* |
| @@ -2671,6 +2721,17 @@ int nand_scan_tail(struct mtd_info *mtd) | |||
| 2671 | */ | 2721 | */ |
| 2672 | 2722 | ||
| 2673 | switch (chip->ecc.mode) { | 2723 | switch (chip->ecc.mode) { |
| 2724 | case NAND_ECC_HW_OOB_FIRST: | ||
| 2725 | /* Similar to NAND_ECC_HW, but a separate read_page handle */ | ||
| 2726 | if (!chip->ecc.calculate || !chip->ecc.correct || | ||
| 2727 | !chip->ecc.hwctl) { | ||
| 2728 | printk(KERN_WARNING "No ECC functions supplied; " | ||
| 2729 | "Hardware ECC not possible\n"); | ||
| 2730 | BUG(); | ||
| 2731 | } | ||
| 2732 | if (!chip->ecc.read_page) | ||
| 2733 | chip->ecc.read_page = nand_read_page_hwecc_oob_first; | ||
| 2734 | |||
| 2674 | case NAND_ECC_HW: | 2735 | case NAND_ECC_HW: |
| 2675 | /* Use standard hwecc read page function ? */ | 2736 | /* Use standard hwecc read page function ? */ |
| 2676 | if (!chip->ecc.read_page) | 2737 | if (!chip->ecc.read_page) |
| @@ -2693,7 +2754,7 @@ int nand_scan_tail(struct mtd_info *mtd) | |||
| 2693 | chip->ecc.read_page == nand_read_page_hwecc || | 2754 | chip->ecc.read_page == nand_read_page_hwecc || |
| 2694 | !chip->ecc.write_page || | 2755 | !chip->ecc.write_page || |
| 2695 | chip->ecc.write_page == nand_write_page_hwecc)) { | 2756 | chip->ecc.write_page == nand_write_page_hwecc)) { |
| 2696 | printk(KERN_WARNING "No ECC functions supplied, " | 2757 | printk(KERN_WARNING "No ECC functions supplied; " |
| 2697 | "Hardware ECC not possible\n"); | 2758 | "Hardware ECC not possible\n"); |
| 2698 | BUG(); | 2759 | BUG(); |
| 2699 | } | 2760 | } |
| @@ -2728,7 +2789,8 @@ int nand_scan_tail(struct mtd_info *mtd) | |||
| 2728 | chip->ecc.write_page_raw = nand_write_page_raw; | 2789 | chip->ecc.write_page_raw = nand_write_page_raw; |
| 2729 | chip->ecc.read_oob = nand_read_oob_std; | 2790 | chip->ecc.read_oob = nand_read_oob_std; |
| 2730 | chip->ecc.write_oob = nand_write_oob_std; | 2791 | chip->ecc.write_oob = nand_write_oob_std; |
| 2731 | chip->ecc.size = 256; | 2792 | if (!chip->ecc.size) |
| 2793 | chip->ecc.size = 256; | ||
| 2732 | chip->ecc.bytes = 3; | 2794 | chip->ecc.bytes = 3; |
| 2733 | break; | 2795 | break; |
| 2734 | 2796 | ||
| @@ -2858,7 +2920,8 @@ int nand_scan(struct mtd_info *mtd, int maxchips) | |||
| 2858 | 2920 | ||
| 2859 | /* Many callers got this wrong, so check for it for a while... */ | 2921 | /* Many callers got this wrong, so check for it for a while... */ |
| 2860 | if (!mtd->owner && caller_is_module()) { | 2922 | if (!mtd->owner && caller_is_module()) { |
| 2861 | printk(KERN_CRIT "nand_scan() called with NULL mtd->owner!\n"); | 2923 | printk(KERN_CRIT "%s called with NULL mtd->owner!\n", |
| 2924 | __func__); | ||
| 2862 | BUG(); | 2925 | BUG(); |
| 2863 | } | 2926 | } |
| 2864 | 2927 | ||
diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c index c0cb87d6d16e..db7ae9d6a296 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/nand_ecc.c | |||
| @@ -417,22 +417,22 @@ int nand_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf, | |||
| 417 | EXPORT_SYMBOL(nand_calculate_ecc); | 417 | EXPORT_SYMBOL(nand_calculate_ecc); |
| 418 | 418 | ||
| 419 | /** | 419 | /** |
| 420 | * nand_correct_data - [NAND Interface] Detect and correct bit error(s) | 420 | * __nand_correct_data - [NAND Interface] Detect and correct bit error(s) |
| 421 | * @mtd: MTD block structure | ||
| 422 | * @buf: raw data read from the chip | 421 | * @buf: raw data read from the chip |
| 423 | * @read_ecc: ECC from the chip | 422 | * @read_ecc: ECC from the chip |
| 424 | * @calc_ecc: the ECC calculated from raw data | 423 | * @calc_ecc: the ECC calculated from raw data |
| 424 | * @eccsize: data bytes per ecc step (256 or 512) | ||
| 425 | * | 425 | * |
| 426 | * Detect and correct a 1 bit error for 256/512 byte block | 426 | * Detect and correct a 1 bit error for eccsize byte block |
| 427 | */ | 427 | */ |
| 428 | int nand_correct_data(struct mtd_info *mtd, unsigned char *buf, | 428 | int __nand_correct_data(unsigned char *buf, |
| 429 | unsigned char *read_ecc, unsigned char *calc_ecc) | 429 | unsigned char *read_ecc, unsigned char *calc_ecc, |
| 430 | unsigned int eccsize) | ||
| 430 | { | 431 | { |
| 431 | unsigned char b0, b1, b2, bit_addr; | 432 | unsigned char b0, b1, b2, bit_addr; |
| 432 | unsigned int byte_addr; | 433 | unsigned int byte_addr; |
| 433 | /* 256 or 512 bytes/ecc */ | 434 | /* 256 or 512 bytes/ecc */ |
| 434 | const uint32_t eccsize_mult = | 435 | const uint32_t eccsize_mult = eccsize >> 8; |
| 435 | (((struct nand_chip *)mtd->priv)->ecc.size) >> 8; | ||
| 436 | 436 | ||
| 437 | /* | 437 | /* |
| 438 | * b0 to b2 indicate which bit is faulty (if any) | 438 | * b0 to b2 indicate which bit is faulty (if any) |
| @@ -495,6 +495,23 @@ int nand_correct_data(struct mtd_info *mtd, unsigned char *buf, | |||
| 495 | printk(KERN_ERR "uncorrectable error : "); | 495 | printk(KERN_ERR "uncorrectable error : "); |
| 496 | return -1; | 496 | return -1; |
| 497 | } | 497 | } |
| 498 | EXPORT_SYMBOL(__nand_correct_data); | ||
| 499 | |||
| 500 | /** | ||
| 501 | * nand_correct_data - [NAND Interface] Detect and correct bit error(s) | ||
| 502 | * @mtd: MTD block structure | ||
| 503 | * @buf: raw data read from the chip | ||
| 504 | * @read_ecc: ECC from the chip | ||
| 505 | * @calc_ecc: the ECC calculated from raw data | ||
| 506 | * | ||
| 507 | * Detect and correct a 1 bit error for 256/512 byte block | ||
| 508 | */ | ||
| 509 | int nand_correct_data(struct mtd_info *mtd, unsigned char *buf, | ||
| 510 | unsigned char *read_ecc, unsigned char *calc_ecc) | ||
| 511 | { | ||
| 512 | return __nand_correct_data(buf, read_ecc, calc_ecc, | ||
| 513 | ((struct nand_chip *)mtd->priv)->ecc.size); | ||
| 514 | } | ||
| 498 | EXPORT_SYMBOL(nand_correct_data); | 515 | EXPORT_SYMBOL(nand_correct_data); |
| 499 | 516 | ||
| 500 | MODULE_LICENSE("GPL"); | 517 | MODULE_LICENSE("GPL"); |
diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index 89bf85af642c..40b5658bdbe6 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c | |||
| @@ -102,8 +102,8 @@ static int ndfc_calculate_ecc(struct mtd_info *mtd, | |||
| 102 | wmb(); | 102 | wmb(); |
| 103 | ecc = in_be32(ndfc->ndfcbase + NDFC_ECC); | 103 | ecc = in_be32(ndfc->ndfcbase + NDFC_ECC); |
| 104 | /* The NDFC uses Smart Media (SMC) bytes order */ | 104 | /* The NDFC uses Smart Media (SMC) bytes order */ |
| 105 | ecc_code[0] = p[2]; | 105 | ecc_code[0] = p[1]; |
| 106 | ecc_code[1] = p[1]; | 106 | ecc_code[1] = p[2]; |
| 107 | ecc_code[2] = p[3]; | 107 | ecc_code[2] = p[3]; |
| 108 | 108 | ||
| 109 | return 0; | 109 | return 0; |
diff --git a/drivers/mtd/nand/nomadik_nand.c b/drivers/mtd/nand/nomadik_nand.c new file mode 100644 index 000000000000..7c302d55910e --- /dev/null +++ b/drivers/mtd/nand/nomadik_nand.c | |||
| @@ -0,0 +1,250 @@ | |||
| 1 | /* | ||
| 2 | * drivers/mtd/nand/nomadik_nand.c | ||
| 3 | * | ||
| 4 | * Overview: | ||
| 5 | * Driver for on-board NAND flash on Nomadik Platforms | ||
| 6 | * | ||
| 7 | * Copyright © 2007 STMicroelectronics Pvt. Ltd. | ||
| 8 | * Author: Sachin Verma <sachin.verma@st.com> | ||
| 9 | * | ||
| 10 | * Copyright © 2009 Alessandro Rubini | ||
| 11 | * | ||
| 12 | * This program is free software; you can redistribute it and/or modify | ||
| 13 | * it under the terms of the GNU General Public License as published by | ||
| 14 | * the Free Software Foundation; either version 2 of the License, or | ||
| 15 | * (at your option) any later version. | ||
| 16 | * | ||
| 17 | * This program is distributed in the hope that it will be useful, | ||
| 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 20 | * GNU General Public License for more details. | ||
| 21 | * | ||
| 22 | */ | ||
| 23 | |||
| 24 | #include <linux/init.h> | ||
| 25 | #include <linux/module.h> | ||
| 26 | #include <linux/types.h> | ||
| 27 | #include <linux/mtd/mtd.h> | ||
| 28 | #include <linux/mtd/nand.h> | ||
| 29 | #include <linux/mtd/nand_ecc.h> | ||
| 30 | #include <linux/platform_device.h> | ||
| 31 | #include <linux/mtd/partitions.h> | ||
| 32 | #include <linux/io.h> | ||
| 33 | #include <mach/nand.h> | ||
| 34 | #include <mach/fsmc.h> | ||
| 35 | |||
| 36 | #include <mtd/mtd-abi.h> | ||
| 37 | |||
| 38 | struct nomadik_nand_host { | ||
| 39 | struct mtd_info mtd; | ||
| 40 | struct nand_chip nand; | ||
| 41 | void __iomem *data_va; | ||
| 42 | void __iomem *cmd_va; | ||
| 43 | void __iomem *addr_va; | ||
| 44 | struct nand_bbt_descr *bbt_desc; | ||
| 45 | }; | ||
| 46 | |||
| 47 | static struct nand_ecclayout nomadik_ecc_layout = { | ||
| 48 | .eccbytes = 3 * 4, | ||
| 49 | .eccpos = { /* each subpage has 16 bytes: pos 2,3,4 hosts ECC */ | ||
| 50 | 0x02, 0x03, 0x04, | ||
| 51 | 0x12, 0x13, 0x14, | ||
| 52 | 0x22, 0x23, 0x24, | ||
| 53 | 0x32, 0x33, 0x34}, | ||
| 54 | /* let's keep bytes 5,6,7 for us, just in case we change ECC algo */ | ||
| 55 | .oobfree = { {0x08, 0x08}, {0x18, 0x08}, {0x28, 0x08}, {0x38, 0x08} }, | ||
| 56 | }; | ||
| 57 | |||
| 58 | static void nomadik_ecc_control(struct mtd_info *mtd, int mode) | ||
| 59 | { | ||
| 60 | /* No need to enable hw ecc, it's on by default */ | ||
| 61 | } | ||
| 62 | |||
| 63 | static void nomadik_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) | ||
| 64 | { | ||
| 65 | struct nand_chip *nand = mtd->priv; | ||
| 66 | struct nomadik_nand_host *host = nand->priv; | ||
| 67 | |||
| 68 | if (cmd == NAND_CMD_NONE) | ||
| 69 | return; | ||
| 70 | |||
| 71 | if (ctrl & NAND_CLE) | ||
| 72 | writeb(cmd, host->cmd_va); | ||
| 73 | else | ||
| 74 | writeb(cmd, host->addr_va); | ||
| 75 | } | ||
| 76 | |||
| 77 | static int nomadik_nand_probe(struct platform_device *pdev) | ||
| 78 | { | ||
| 79 | struct nomadik_nand_platform_data *pdata = pdev->dev.platform_data; | ||
| 80 | struct nomadik_nand_host *host; | ||
| 81 | struct mtd_info *mtd; | ||
| 82 | struct nand_chip *nand; | ||
| 83 | struct resource *res; | ||
| 84 | int ret = 0; | ||
| 85 | |||
| 86 | /* Allocate memory for the device structure (and zero it) */ | ||
| 87 | host = kzalloc(sizeof(struct nomadik_nand_host), GFP_KERNEL); | ||
| 88 | if (!host) { | ||
| 89 | dev_err(&pdev->dev, "Failed to allocate device structure.\n"); | ||
| 90 | return -ENOMEM; | ||
| 91 | } | ||
| 92 | |||
| 93 | /* Call the client's init function, if any */ | ||
| 94 | if (pdata->init) | ||
| 95 | ret = pdata->init(); | ||
| 96 | if (ret < 0) { | ||
| 97 | dev_err(&pdev->dev, "Init function failed\n"); | ||
| 98 | goto err; | ||
| 99 | } | ||
| 100 | |||
| 101 | /* ioremap three regions */ | ||
| 102 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_addr"); | ||
| 103 | if (!res) { | ||
| 104 | ret = -EIO; | ||
| 105 | goto err_unmap; | ||
| 106 | } | ||
| 107 | host->addr_va = ioremap(res->start, res->end - res->start + 1); | ||
| 108 | |||
| 109 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data"); | ||
| 110 | if (!res) { | ||
| 111 | ret = -EIO; | ||
| 112 | goto err_unmap; | ||
| 113 | } | ||
| 114 | host->data_va = ioremap(res->start, res->end - res->start + 1); | ||
| 115 | |||
| 116 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_cmd"); | ||
| 117 | if (!res) { | ||
| 118 | ret = -EIO; | ||
| 119 | goto err_unmap; | ||
| 120 | } | ||
| 121 | host->cmd_va = ioremap(res->start, res->end - res->start + 1); | ||
| 122 | |||
| 123 | if (!host->addr_va || !host->data_va || !host->cmd_va) { | ||
| 124 | ret = -ENOMEM; | ||
| 125 | goto err_unmap; | ||
| 126 | } | ||
| 127 | |||
| 128 | /* Link all private pointers */ | ||
| 129 | mtd = &host->mtd; | ||
| 130 | nand = &host->nand; | ||
| 131 | mtd->priv = nand; | ||
| 132 | nand->priv = host; | ||
| 133 | |||
| 134 | host->mtd.owner = THIS_MODULE; | ||
| 135 | nand->IO_ADDR_R = host->data_va; | ||
| 136 | nand->IO_ADDR_W = host->data_va; | ||
| 137 | nand->cmd_ctrl = nomadik_cmd_ctrl; | ||
| 138 | |||
| 139 | /* | ||
| 140 | * This stanza declares ECC_HW but uses soft routines. It's because | ||
| 141 | * HW claims to make the calculation but not the correction. However, | ||
| 142 | * I haven't managed to get the desired data out of it until now. | ||
| 143 | */ | ||
| 144 | nand->ecc.mode = NAND_ECC_SOFT; | ||
| 145 | nand->ecc.layout = &nomadik_ecc_layout; | ||
| 146 | nand->ecc.hwctl = nomadik_ecc_control; | ||
| 147 | nand->ecc.size = 512; | ||
| 148 | nand->ecc.bytes = 3; | ||
| 149 | |||
| 150 | nand->options = pdata->options; | ||
| 151 | |||
| 152 | /* | ||
| 153 | * Scan to find existance of the device | ||
| 154 | */ | ||
| 155 | if (nand_scan(&host->mtd, 1)) { | ||
| 156 | ret = -ENXIO; | ||
| 157 | goto err_unmap; | ||
| 158 | } | ||
| 159 | |||
| 160 | #ifdef CONFIG_MTD_PARTITIONS | ||
| 161 | add_mtd_partitions(&host->mtd, pdata->parts, pdata->nparts); | ||
| 162 | #else | ||
| 163 | pr_info("Registering %s as whole device\n", mtd->name); | ||
| 164 | add_mtd_device(mtd); | ||
| 165 | #endif | ||
| 166 | |||
| 167 | platform_set_drvdata(pdev, host); | ||
| 168 | return 0; | ||
| 169 | |||
| 170 | err_unmap: | ||
| 171 | if (host->cmd_va) | ||
| 172 | iounmap(host->cmd_va); | ||
| 173 | if (host->data_va) | ||
| 174 | iounmap(host->data_va); | ||
| 175 | if (host->addr_va) | ||
| 176 | iounmap(host->addr_va); | ||
| 177 | err: | ||
| 178 | kfree(host); | ||
| 179 | return ret; | ||
| 180 | } | ||
| 181 | |||
| 182 | /* | ||
| 183 | * Clean up routine | ||
| 184 | */ | ||
| 185 | static int nomadik_nand_remove(struct platform_device *pdev) | ||
| 186 | { | ||
| 187 | struct nomadik_nand_host *host = platform_get_drvdata(pdev); | ||
| 188 | struct nomadik_nand_platform_data *pdata = pdev->dev.platform_data; | ||
| 189 | |||
| 190 | if (pdata->exit) | ||
| 191 | pdata->exit(); | ||
| 192 | |||
| 193 | if (host) { | ||
| 194 | iounmap(host->cmd_va); | ||
| 195 | iounmap(host->data_va); | ||
| 196 | iounmap(host->addr_va); | ||
| 197 | kfree(host); | ||
| 198 | } | ||
| 199 | return 0; | ||
| 200 | } | ||
| 201 | |||
| 202 | static int nomadik_nand_suspend(struct device *dev) | ||
| 203 | { | ||
| 204 | struct nomadik_nand_host *host = dev_get_drvdata(dev); | ||
| 205 | int ret = 0; | ||
| 206 | if (host) | ||
| 207 | ret = host->mtd.suspend(&host->mtd); | ||
| 208 | return ret; | ||
| 209 | } | ||
| 210 | |||
| 211 | static int nomadik_nand_resume(struct device *dev) | ||
| 212 | { | ||
| 213 | struct nomadik_nand_host *host = dev_get_drvdata(dev); | ||
| 214 | if (host) | ||
| 215 | host->mtd.resume(&host->mtd); | ||
| 216 | return 0; | ||
| 217 | } | ||
| 218 | |||
| 219 | static struct dev_pm_ops nomadik_nand_pm_ops = { | ||
| 220 | .suspend = nomadik_nand_suspend, | ||
| 221 | .resume = nomadik_nand_resume, | ||
| 222 | }; | ||
| 223 | |||
| 224 | static struct platform_driver nomadik_nand_driver = { | ||
| 225 | .probe = nomadik_nand_probe, | ||
| 226 | .remove = nomadik_nand_remove, | ||
| 227 | .driver = { | ||
| 228 | .owner = THIS_MODULE, | ||
| 229 | .name = "nomadik_nand", | ||
| 230 | .pm = &nomadik_nand_pm_ops, | ||
| 231 | }, | ||
| 232 | }; | ||
| 233 | |||
| 234 | static int __init nand_nomadik_init(void) | ||
| 235 | { | ||
| 236 | pr_info("Nomadik NAND driver\n"); | ||
| 237 | return platform_driver_register(&nomadik_nand_driver); | ||
| 238 | } | ||
| 239 | |||
| 240 | static void __exit nand_nomadik_exit(void) | ||
| 241 | { | ||
| 242 | platform_driver_unregister(&nomadik_nand_driver); | ||
| 243 | } | ||
| 244 | |||
| 245 | module_init(nand_nomadik_init); | ||
| 246 | module_exit(nand_nomadik_exit); | ||
| 247 | |||
| 248 | MODULE_LICENSE("GPL"); | ||
| 249 | MODULE_AUTHOR("ST Microelectronics (sachin.verma@st.com)"); | ||
| 250 | MODULE_DESCRIPTION("NAND driver for Nomadik Platform"); | ||
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index ebd07e95b814..090ab87086b5 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c | |||
| @@ -18,8 +18,7 @@ | |||
| 18 | #include <linux/mtd/partitions.h> | 18 | #include <linux/mtd/partitions.h> |
| 19 | #include <linux/io.h> | 19 | #include <linux/io.h> |
| 20 | 20 | ||
| 21 | #include <asm/dma.h> | 21 | #include <mach/dma.h> |
| 22 | |||
| 23 | #include <mach/gpmc.h> | 22 | #include <mach/gpmc.h> |
| 24 | #include <mach/nand.h> | 23 | #include <mach/nand.h> |
| 25 | 24 | ||
| @@ -112,6 +111,27 @@ | |||
| 112 | static const char *part_probes[] = { "cmdlinepart", NULL }; | 111 | static const char *part_probes[] = { "cmdlinepart", NULL }; |
| 113 | #endif | 112 | #endif |
| 114 | 113 | ||
| 114 | #ifdef CONFIG_MTD_NAND_OMAP_PREFETCH | ||
| 115 | static int use_prefetch = 1; | ||
| 116 | |||
| 117 | /* "modprobe ... use_prefetch=0" etc */ | ||
| 118 | module_param(use_prefetch, bool, 0); | ||
| 119 | MODULE_PARM_DESC(use_prefetch, "enable/disable use of PREFETCH"); | ||
| 120 | |||
| 121 | #ifdef CONFIG_MTD_NAND_OMAP_PREFETCH_DMA | ||
| 122 | static int use_dma = 1; | ||
| 123 | |||
| 124 | /* "modprobe ... use_dma=0" etc */ | ||
| 125 | module_param(use_dma, bool, 0); | ||
| 126 | MODULE_PARM_DESC(use_dma, "enable/disable use of DMA"); | ||
| 127 | #else | ||
| 128 | const int use_dma; | ||
| 129 | #endif | ||
| 130 | #else | ||
| 131 | const int use_prefetch; | ||
| 132 | const int use_dma; | ||
| 133 | #endif | ||
| 134 | |||
| 115 | struct omap_nand_info { | 135 | struct omap_nand_info { |
| 116 | struct nand_hw_control controller; | 136 | struct nand_hw_control controller; |
| 117 | struct omap_nand_platform_data *pdata; | 137 | struct omap_nand_platform_data *pdata; |
| @@ -124,6 +144,9 @@ struct omap_nand_info { | |||
| 124 | unsigned long phys_base; | 144 | unsigned long phys_base; |
| 125 | void __iomem *gpmc_cs_baseaddr; | 145 | void __iomem *gpmc_cs_baseaddr; |
| 126 | void __iomem *gpmc_baseaddr; | 146 | void __iomem *gpmc_baseaddr; |
| 147 | void __iomem *nand_pref_fifo_add; | ||
| 148 | struct completion comp; | ||
| 149 | int dma_ch; | ||
| 127 | }; | 150 | }; |
| 128 | 151 | ||
| 129 | /** | 152 | /** |
| @@ -189,6 +212,38 @@ static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) | |||
| 189 | } | 212 | } |
| 190 | 213 | ||
| 191 | /** | 214 | /** |
| 215 | * omap_read_buf8 - read data from NAND controller into buffer | ||
| 216 | * @mtd: MTD device structure | ||
| 217 | * @buf: buffer to store date | ||
| 218 | * @len: number of bytes to read | ||
| 219 | */ | ||
| 220 | static void omap_read_buf8(struct mtd_info *mtd, u_char *buf, int len) | ||
| 221 | { | ||
| 222 | struct nand_chip *nand = mtd->priv; | ||
| 223 | |||
| 224 | ioread8_rep(nand->IO_ADDR_R, buf, len); | ||
| 225 | } | ||
| 226 | |||
| 227 | /** | ||
| 228 | * omap_write_buf8 - write buffer to NAND controller | ||
| 229 | * @mtd: MTD device structure | ||
| 230 | * @buf: data buffer | ||
| 231 | * @len: number of bytes to write | ||
| 232 | */ | ||
| 233 | static void omap_write_buf8(struct mtd_info *mtd, const u_char *buf, int len) | ||
| 234 | { | ||
| 235 | struct omap_nand_info *info = container_of(mtd, | ||
| 236 | struct omap_nand_info, mtd); | ||
| 237 | u_char *p = (u_char *)buf; | ||
| 238 | |||
| 239 | while (len--) { | ||
| 240 | iowrite8(*p++, info->nand.IO_ADDR_W); | ||
| 241 | while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr + | ||
| 242 | GPMC_STATUS) & GPMC_BUF_FULL)); | ||
| 243 | } | ||
| 244 | } | ||
| 245 | |||
| 246 | /** | ||
| 192 | * omap_read_buf16 - read data from NAND controller into buffer | 247 | * omap_read_buf16 - read data from NAND controller into buffer |
| 193 | * @mtd: MTD device structure | 248 | * @mtd: MTD device structure |
| 194 | * @buf: buffer to store date | 249 | * @buf: buffer to store date |
| @@ -198,7 +253,7 @@ static void omap_read_buf16(struct mtd_info *mtd, u_char *buf, int len) | |||
| 198 | { | 253 | { |
| 199 | struct nand_chip *nand = mtd->priv; | 254 | struct nand_chip *nand = mtd->priv; |
| 200 | 255 | ||
| 201 | __raw_readsw(nand->IO_ADDR_R, buf, len / 2); | 256 | ioread16_rep(nand->IO_ADDR_R, buf, len / 2); |
| 202 | } | 257 | } |
| 203 | 258 | ||
| 204 | /** | 259 | /** |
| @@ -217,13 +272,242 @@ static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len) | |||
| 217 | len >>= 1; | 272 | len >>= 1; |
| 218 | 273 | ||
| 219 | while (len--) { | 274 | while (len--) { |
| 220 | writew(*p++, info->nand.IO_ADDR_W); | 275 | iowrite16(*p++, info->nand.IO_ADDR_W); |
| 221 | 276 | ||
| 222 | while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr + | 277 | while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr + |
| 223 | GPMC_STATUS) & GPMC_BUF_FULL)) | 278 | GPMC_STATUS) & GPMC_BUF_FULL)) |
| 224 | ; | 279 | ; |
| 225 | } | 280 | } |
| 226 | } | 281 | } |
| 282 | |||
| 283 | /** | ||
| 284 | * omap_read_buf_pref - read data from NAND controller into buffer | ||
| 285 | * @mtd: MTD device structure | ||
| 286 | * @buf: buffer to store date | ||
| 287 | * @len: number of bytes to read | ||
| 288 | */ | ||
| 289 | static void omap_read_buf_pref(struct mtd_info *mtd, u_char *buf, int len) | ||
| 290 | { | ||
| 291 | struct omap_nand_info *info = container_of(mtd, | ||
| 292 | struct omap_nand_info, mtd); | ||
| 293 | uint32_t pfpw_status = 0, r_count = 0; | ||
| 294 | int ret = 0; | ||
| 295 | u32 *p = (u32 *)buf; | ||
| 296 | |||
| 297 | /* take care of subpage reads */ | ||
| 298 | for (; len % 4 != 0; ) { | ||
| 299 | *buf++ = __raw_readb(info->nand.IO_ADDR_R); | ||
| 300 | len--; | ||
| 301 | } | ||
| 302 | p = (u32 *) buf; | ||
| 303 | |||
| 304 | /* configure and start prefetch transfer */ | ||
| 305 | ret = gpmc_prefetch_enable(info->gpmc_cs, 0x0, len, 0x0); | ||
| 306 | if (ret) { | ||
| 307 | /* PFPW engine is busy, use cpu copy method */ | ||
| 308 | if (info->nand.options & NAND_BUSWIDTH_16) | ||
| 309 | omap_read_buf16(mtd, buf, len); | ||
| 310 | else | ||
| 311 | omap_read_buf8(mtd, buf, len); | ||
| 312 | } else { | ||
| 313 | do { | ||
| 314 | pfpw_status = gpmc_prefetch_status(); | ||
| 315 | r_count = ((pfpw_status >> 24) & 0x7F) >> 2; | ||
| 316 | ioread32_rep(info->nand_pref_fifo_add, p, r_count); | ||
| 317 | p += r_count; | ||
| 318 | len -= r_count << 2; | ||
| 319 | } while (len); | ||
| 320 | |||
| 321 | /* disable and stop the PFPW engine */ | ||
| 322 | gpmc_prefetch_reset(); | ||
| 323 | } | ||
| 324 | } | ||
| 325 | |||
| 326 | /** | ||
| 327 | * omap_write_buf_pref - write buffer to NAND controller | ||
| 328 | * @mtd: MTD device structure | ||
| 329 | * @buf: data buffer | ||
| 330 | * @len: number of bytes to write | ||
| 331 | */ | ||
| 332 | static void omap_write_buf_pref(struct mtd_info *mtd, | ||
| 333 | const u_char *buf, int len) | ||
| 334 | { | ||
| 335 | struct omap_nand_info *info = container_of(mtd, | ||
| 336 | struct omap_nand_info, mtd); | ||
| 337 | uint32_t pfpw_status = 0, w_count = 0; | ||
| 338 | int i = 0, ret = 0; | ||
| 339 | u16 *p = (u16 *) buf; | ||
| 340 | |||
| 341 | /* take care of subpage writes */ | ||
| 342 | if (len % 2 != 0) { | ||
| 343 | writeb(*buf, info->nand.IO_ADDR_R); | ||
| 344 | p = (u16 *)(buf + 1); | ||
| 345 | len--; | ||
| 346 | } | ||
| 347 | |||
| 348 | /* configure and start prefetch transfer */ | ||
| 349 | ret = gpmc_prefetch_enable(info->gpmc_cs, 0x0, len, 0x1); | ||
| 350 | if (ret) { | ||
| 351 | /* PFPW engine is busy, use cpu copy method */ | ||
| 352 | if (info->nand.options & NAND_BUSWIDTH_16) | ||
| 353 | omap_write_buf16(mtd, buf, len); | ||
| 354 | else | ||
| 355 | omap_write_buf8(mtd, buf, len); | ||
| 356 | } else { | ||
| 357 | pfpw_status = gpmc_prefetch_status(); | ||
| 358 | while (pfpw_status & 0x3FFF) { | ||
| 359 | w_count = ((pfpw_status >> 24) & 0x7F) >> 1; | ||
| 360 | for (i = 0; (i < w_count) && len; i++, len -= 2) | ||
| 361 | iowrite16(*p++, info->nand_pref_fifo_add); | ||
| 362 | pfpw_status = gpmc_prefetch_status(); | ||
| 363 | } | ||
| 364 | |||
| 365 | /* disable and stop the PFPW engine */ | ||
| 366 | gpmc_prefetch_reset(); | ||
| 367 | } | ||
| 368 | } | ||
| 369 | |||
| 370 | #ifdef CONFIG_MTD_NAND_OMAP_PREFETCH_DMA | ||
| 371 | /* | ||
| 372 | * omap_nand_dma_cb: callback on the completion of dma transfer | ||
| 373 | * @lch: logical channel | ||
| 374 | * @ch_satuts: channel status | ||
| 375 | * @data: pointer to completion data structure | ||
| 376 | */ | ||
| 377 | static void omap_nand_dma_cb(int lch, u16 ch_status, void *data) | ||
| 378 | { | ||
| 379 | complete((struct completion *) data); | ||
| 380 | } | ||
| 381 | |||
| 382 | /* | ||
| 383 | * omap_nand_dma_transfer: configer and start dma transfer | ||
| 384 | * @mtd: MTD device structure | ||
| 385 | * @addr: virtual address in RAM of source/destination | ||
| 386 | * @len: number of data bytes to be transferred | ||
| 387 | * @is_write: flag for read/write operation | ||
| 388 | */ | ||
| 389 | static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, | ||
| 390 | unsigned int len, int is_write) | ||
| 391 | { | ||
| 392 | struct omap_nand_info *info = container_of(mtd, | ||
| 393 | struct omap_nand_info, mtd); | ||
| 394 | uint32_t prefetch_status = 0; | ||
| 395 | enum dma_data_direction dir = is_write ? DMA_TO_DEVICE : | ||
| 396 | DMA_FROM_DEVICE; | ||
| 397 | dma_addr_t dma_addr; | ||
| 398 | int ret; | ||
| 399 | |||
| 400 | /* The fifo depth is 64 bytes. We have a sync at each frame and frame | ||
| 401 | * length is 64 bytes. | ||
| 402 | */ | ||
| 403 | int buf_len = len >> 6; | ||
| 404 | |||
| 405 | if (addr >= high_memory) { | ||
| 406 | struct page *p1; | ||
| 407 | |||
| 408 | if (((size_t)addr & PAGE_MASK) != | ||
| 409 | ((size_t)(addr + len - 1) & PAGE_MASK)) | ||
| 410 | goto out_copy; | ||
| 411 | p1 = vmalloc_to_page(addr); | ||
| 412 | if (!p1) | ||
| 413 | goto out_copy; | ||
| 414 | addr = page_address(p1) + ((size_t)addr & ~PAGE_MASK); | ||
| 415 | } | ||
| 416 | |||
| 417 | dma_addr = dma_map_single(&info->pdev->dev, addr, len, dir); | ||
| 418 | if (dma_mapping_error(&info->pdev->dev, dma_addr)) { | ||
| 419 | dev_err(&info->pdev->dev, | ||
| 420 | "Couldn't DMA map a %d byte buffer\n", len); | ||
| 421 | goto out_copy; | ||
| 422 | } | ||
| 423 | |||
| 424 | if (is_write) { | ||
| 425 | omap_set_dma_dest_params(info->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT, | ||
| 426 | info->phys_base, 0, 0); | ||
| 427 | omap_set_dma_src_params(info->dma_ch, 0, OMAP_DMA_AMODE_POST_INC, | ||
| 428 | dma_addr, 0, 0); | ||
| 429 | omap_set_dma_transfer_params(info->dma_ch, OMAP_DMA_DATA_TYPE_S32, | ||
| 430 | 0x10, buf_len, OMAP_DMA_SYNC_FRAME, | ||
| 431 | OMAP24XX_DMA_GPMC, OMAP_DMA_DST_SYNC); | ||
| 432 | } else { | ||
| 433 | omap_set_dma_src_params(info->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT, | ||
| 434 | info->phys_base, 0, 0); | ||
| 435 | omap_set_dma_dest_params(info->dma_ch, 0, OMAP_DMA_AMODE_POST_INC, | ||
| 436 | dma_addr, 0, 0); | ||
| 437 | omap_set_dma_transfer_params(info->dma_ch, OMAP_DMA_DATA_TYPE_S32, | ||
| 438 | 0x10, buf_len, OMAP_DMA_SYNC_FRAME, | ||
| 439 | OMAP24XX_DMA_GPMC, OMAP_DMA_SRC_SYNC); | ||
| 440 | } | ||
| 441 | /* configure and start prefetch transfer */ | ||
| 442 | ret = gpmc_prefetch_enable(info->gpmc_cs, 0x1, len, is_write); | ||
| 443 | if (ret) | ||
| 444 | /* PFPW engine is busy, use cpu copy methode */ | ||
| 445 | goto out_copy; | ||
| 446 | |||
| 447 | init_completion(&info->comp); | ||
| 448 | |||
| 449 | omap_start_dma(info->dma_ch); | ||
| 450 | |||
| 451 | /* setup and start DMA using dma_addr */ | ||
| 452 | wait_for_completion(&info->comp); | ||
| 453 | |||
| 454 | while (0x3fff & (prefetch_status = gpmc_prefetch_status())) | ||
| 455 | ; | ||
| 456 | /* disable and stop the PFPW engine */ | ||
| 457 | gpmc_prefetch_reset(); | ||
| 458 | |||
| 459 | dma_unmap_single(&info->pdev->dev, dma_addr, len, dir); | ||
| 460 | return 0; | ||
| 461 | |||
| 462 | out_copy: | ||
| 463 | if (info->nand.options & NAND_BUSWIDTH_16) | ||
| 464 | is_write == 0 ? omap_read_buf16(mtd, (u_char *) addr, len) | ||
| 465 | : omap_write_buf16(mtd, (u_char *) addr, len); | ||
| 466 | else | ||
| 467 | is_write == 0 ? omap_read_buf8(mtd, (u_char *) addr, len) | ||
| 468 | : omap_write_buf8(mtd, (u_char *) addr, len); | ||
| 469 | return 0; | ||
| 470 | } | ||
| 471 | #else | ||
| 472 | static void omap_nand_dma_cb(int lch, u16 ch_status, void *data) {} | ||
| 473 | static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, | ||
| 474 | unsigned int len, int is_write) | ||
| 475 | { | ||
| 476 | return 0; | ||
| 477 | } | ||
| 478 | #endif | ||
| 479 | |||
| 480 | /** | ||
| 481 | * omap_read_buf_dma_pref - read data from NAND controller into buffer | ||
| 482 | * @mtd: MTD device structure | ||
| 483 | * @buf: buffer to store date | ||
| 484 | * @len: number of bytes to read | ||
| 485 | */ | ||
| 486 | static void omap_read_buf_dma_pref(struct mtd_info *mtd, u_char *buf, int len) | ||
| 487 | { | ||
| 488 | if (len <= mtd->oobsize) | ||
| 489 | omap_read_buf_pref(mtd, buf, len); | ||
| 490 | else | ||
| 491 | /* start transfer in DMA mode */ | ||
| 492 | omap_nand_dma_transfer(mtd, buf, len, 0x0); | ||
| 493 | } | ||
| 494 | |||
| 495 | /** | ||
| 496 | * omap_write_buf_dma_pref - write buffer to NAND controller | ||
| 497 | * @mtd: MTD device structure | ||
| 498 | * @buf: data buffer | ||
| 499 | * @len: number of bytes to write | ||
| 500 | */ | ||
| 501 | static void omap_write_buf_dma_pref(struct mtd_info *mtd, | ||
| 502 | const u_char *buf, int len) | ||
| 503 | { | ||
| 504 | if (len <= mtd->oobsize) | ||
| 505 | omap_write_buf_pref(mtd, buf, len); | ||
| 506 | else | ||
| 507 | /* start transfer in DMA mode */ | ||
| 508 | omap_nand_dma_transfer(mtd, buf, len, 0x1); | ||
| 509 | } | ||
| 510 | |||
| 227 | /** | 511 | /** |
| 228 | * omap_verify_buf - Verify chip data against buffer | 512 | * omap_verify_buf - Verify chip data against buffer |
| 229 | * @mtd: MTD device structure | 513 | * @mtd: MTD device structure |
| @@ -658,17 +942,12 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) | |||
| 658 | err = -ENOMEM; | 942 | err = -ENOMEM; |
| 659 | goto out_release_mem_region; | 943 | goto out_release_mem_region; |
| 660 | } | 944 | } |
| 945 | |||
| 661 | info->nand.controller = &info->controller; | 946 | info->nand.controller = &info->controller; |
| 662 | 947 | ||
| 663 | info->nand.IO_ADDR_W = info->nand.IO_ADDR_R; | 948 | info->nand.IO_ADDR_W = info->nand.IO_ADDR_R; |
| 664 | info->nand.cmd_ctrl = omap_hwcontrol; | 949 | info->nand.cmd_ctrl = omap_hwcontrol; |
| 665 | 950 | ||
| 666 | /* REVISIT: only supports 16-bit NAND flash */ | ||
| 667 | |||
| 668 | info->nand.read_buf = omap_read_buf16; | ||
| 669 | info->nand.write_buf = omap_write_buf16; | ||
| 670 | info->nand.verify_buf = omap_verify_buf; | ||
| 671 | |||
| 672 | /* | 951 | /* |
| 673 | * If RDY/BSY line is connected to OMAP then use the omap ready | 952 | * If RDY/BSY line is connected to OMAP then use the omap ready |
| 674 | * funcrtion and the generic nand_wait function which reads the status | 953 | * funcrtion and the generic nand_wait function which reads the status |
| @@ -689,6 +968,40 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) | |||
| 689 | == 0x1000) | 968 | == 0x1000) |
| 690 | info->nand.options |= NAND_BUSWIDTH_16; | 969 | info->nand.options |= NAND_BUSWIDTH_16; |
| 691 | 970 | ||
| 971 | if (use_prefetch) { | ||
| 972 | /* copy the virtual address of nand base for fifo access */ | ||
| 973 | info->nand_pref_fifo_add = info->nand.IO_ADDR_R; | ||
| 974 | |||
| 975 | info->nand.read_buf = omap_read_buf_pref; | ||
| 976 | info->nand.write_buf = omap_write_buf_pref; | ||
| 977 | if (use_dma) { | ||
| 978 | err = omap_request_dma(OMAP24XX_DMA_GPMC, "NAND", | ||
| 979 | omap_nand_dma_cb, &info->comp, &info->dma_ch); | ||
| 980 | if (err < 0) { | ||
| 981 | info->dma_ch = -1; | ||
| 982 | printk(KERN_WARNING "DMA request failed." | ||
| 983 | " Non-dma data transfer mode\n"); | ||
| 984 | } else { | ||
| 985 | omap_set_dma_dest_burst_mode(info->dma_ch, | ||
| 986 | OMAP_DMA_DATA_BURST_16); | ||
| 987 | omap_set_dma_src_burst_mode(info->dma_ch, | ||
| 988 | OMAP_DMA_DATA_BURST_16); | ||
| 989 | |||
| 990 | info->nand.read_buf = omap_read_buf_dma_pref; | ||
| 991 | info->nand.write_buf = omap_write_buf_dma_pref; | ||
| 992 | } | ||
| 993 | } | ||
| 994 | } else { | ||
| 995 | if (info->nand.options & NAND_BUSWIDTH_16) { | ||
| 996 | info->nand.read_buf = omap_read_buf16; | ||
| 997 | info->nand.write_buf = omap_write_buf16; | ||
| 998 | } else { | ||
| 999 | info->nand.read_buf = omap_read_buf8; | ||
| 1000 | info->nand.write_buf = omap_write_buf8; | ||
| 1001 | } | ||
| 1002 | } | ||
| 1003 | info->nand.verify_buf = omap_verify_buf; | ||
| 1004 | |||
| 692 | #ifdef CONFIG_MTD_NAND_OMAP_HWECC | 1005 | #ifdef CONFIG_MTD_NAND_OMAP_HWECC |
| 693 | info->nand.ecc.bytes = 3; | 1006 | info->nand.ecc.bytes = 3; |
| 694 | info->nand.ecc.size = 512; | 1007 | info->nand.ecc.size = 512; |
| @@ -744,9 +1057,12 @@ static int omap_nand_remove(struct platform_device *pdev) | |||
| 744 | struct omap_nand_info *info = mtd->priv; | 1057 | struct omap_nand_info *info = mtd->priv; |
| 745 | 1058 | ||
| 746 | platform_set_drvdata(pdev, NULL); | 1059 | platform_set_drvdata(pdev, NULL); |
| 1060 | if (use_dma) | ||
| 1061 | omap_free_dma(info->dma_ch); | ||
| 1062 | |||
| 747 | /* Release NAND device, its internal structures and partitions */ | 1063 | /* Release NAND device, its internal structures and partitions */ |
| 748 | nand_release(&info->mtd); | 1064 | nand_release(&info->mtd); |
| 749 | iounmap(info->nand.IO_ADDR_R); | 1065 | iounmap(info->nand_pref_fifo_add); |
| 750 | kfree(&info->mtd); | 1066 | kfree(&info->mtd); |
| 751 | return 0; | 1067 | return 0; |
| 752 | } | 1068 | } |
| @@ -763,6 +1079,15 @@ static struct platform_driver omap_nand_driver = { | |||
| 763 | static int __init omap_nand_init(void) | 1079 | static int __init omap_nand_init(void) |
| 764 | { | 1080 | { |
| 765 | printk(KERN_INFO "%s driver initializing\n", DRIVER_NAME); | 1081 | printk(KERN_INFO "%s driver initializing\n", DRIVER_NAME); |
| 1082 | |||
| 1083 | /* This check is required if driver is being | ||
| 1084 | * loaded run time as a module | ||
| 1085 | */ | ||
| 1086 | if ((1 == use_dma) && (0 == use_prefetch)) { | ||
| 1087 | printk(KERN_INFO"Wrong parameters: 'use_dma' can not be 1 " | ||
| 1088 | "without use_prefetch'. Prefetch will not be" | ||
| 1089 | " used in either mode (mpu or dma)\n"); | ||
| 1090 | } | ||
| 766 | return platform_driver_register(&omap_nand_driver); | 1091 | return platform_driver_register(&omap_nand_driver); |
| 767 | } | 1092 | } |
| 768 | 1093 | ||
diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index 0d9d4bc9c762..f59c07427af3 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c | |||
| @@ -171,7 +171,6 @@ static int __devexit orion_nand_remove(struct platform_device *pdev) | |||
| 171 | } | 171 | } |
| 172 | 172 | ||
| 173 | static struct platform_driver orion_nand_driver = { | 173 | static struct platform_driver orion_nand_driver = { |
| 174 | .probe = orion_nand_probe, | ||
| 175 | .remove = __devexit_p(orion_nand_remove), | 174 | .remove = __devexit_p(orion_nand_remove), |
| 176 | .driver = { | 175 | .driver = { |
| 177 | .name = "orion_nand", | 176 | .name = "orion_nand", |
| @@ -181,7 +180,7 @@ static struct platform_driver orion_nand_driver = { | |||
| 181 | 180 | ||
| 182 | static int __init orion_nand_init(void) | 181 | static int __init orion_nand_init(void) |
| 183 | { | 182 | { |
| 184 | return platform_driver_register(&orion_nand_driver); | 183 | return platform_driver_probe(&orion_nand_driver, orion_nand_probe); |
| 185 | } | 184 | } |
| 186 | 185 | ||
| 187 | static void __exit orion_nand_exit(void) | 186 | static void __exit orion_nand_exit(void) |
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 30a8ce6d3e69..6ea520ae2410 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c | |||
| @@ -102,6 +102,7 @@ enum { | |||
| 102 | ERR_SENDCMD = -2, | 102 | ERR_SENDCMD = -2, |
| 103 | ERR_DBERR = -3, | 103 | ERR_DBERR = -3, |
| 104 | ERR_BBERR = -4, | 104 | ERR_BBERR = -4, |
| 105 | ERR_SBERR = -5, | ||
| 105 | }; | 106 | }; |
| 106 | 107 | ||
| 107 | enum { | 108 | enum { |
| @@ -564,11 +565,13 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) | |||
| 564 | 565 | ||
| 565 | status = nand_readl(info, NDSR); | 566 | status = nand_readl(info, NDSR); |
| 566 | 567 | ||
| 567 | if (status & (NDSR_RDDREQ | NDSR_DBERR)) { | 568 | if (status & (NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR)) { |
| 568 | if (status & NDSR_DBERR) | 569 | if (status & NDSR_DBERR) |
| 569 | info->retcode = ERR_DBERR; | 570 | info->retcode = ERR_DBERR; |
| 571 | else if (status & NDSR_SBERR) | ||
| 572 | info->retcode = ERR_SBERR; | ||
| 570 | 573 | ||
| 571 | disable_int(info, NDSR_RDDREQ | NDSR_DBERR); | 574 | disable_int(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR); |
| 572 | 575 | ||
| 573 | if (info->use_dma) { | 576 | if (info->use_dma) { |
| 574 | info->state = STATE_DMA_READING; | 577 | info->state = STATE_DMA_READING; |
| @@ -670,7 +673,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, | |||
| 670 | if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr)) | 673 | if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr)) |
| 671 | break; | 674 | break; |
| 672 | 675 | ||
| 673 | pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR); | 676 | pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR); |
| 674 | 677 | ||
| 675 | /* We only are OOB, so if the data has error, does not matter */ | 678 | /* We only are OOB, so if the data has error, does not matter */ |
| 676 | if (info->retcode == ERR_DBERR) | 679 | if (info->retcode == ERR_DBERR) |
| @@ -687,7 +690,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, | |||
| 687 | if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr)) | 690 | if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr)) |
| 688 | break; | 691 | break; |
| 689 | 692 | ||
| 690 | pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR); | 693 | pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR); |
| 691 | 694 | ||
| 692 | if (info->retcode == ERR_DBERR) { | 695 | if (info->retcode == ERR_DBERR) { |
| 693 | /* for blank page (all 0xff), HW will calculate its ECC as | 696 | /* for blank page (all 0xff), HW will calculate its ECC as |
| @@ -861,8 +864,12 @@ static int pxa3xx_nand_ecc_correct(struct mtd_info *mtd, | |||
| 861 | * consider it as a ecc error which will tell the caller the | 864 | * consider it as a ecc error which will tell the caller the |
| 862 | * read fail We have distinguish all the errors, but the | 865 | * read fail We have distinguish all the errors, but the |
| 863 | * nand_read_ecc only check this function return value | 866 | * nand_read_ecc only check this function return value |
| 867 | * | ||
| 868 | * Corrected (single-bit) errors must also be noted. | ||
| 864 | */ | 869 | */ |
| 865 | if (info->retcode != ERR_NONE) | 870 | if (info->retcode == ERR_SBERR) |
| 871 | return 1; | ||
| 872 | else if (info->retcode != ERR_NONE) | ||
| 866 | return -1; | 873 | return -1; |
| 867 | 874 | ||
| 868 | return 0; | 875 | return 0; |
diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index 2bc896623e2d..02bef21f2e4b 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c | |||
| @@ -329,7 +329,7 @@ static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_va | |||
| 329 | } | 329 | } |
| 330 | 330 | ||
| 331 | static int flctl_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, | 331 | static int flctl_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, |
| 332 | uint8_t *buf) | 332 | uint8_t *buf, int page) |
| 333 | { | 333 | { |
| 334 | int i, eccsize = chip->ecc.size; | 334 | int i, eccsize = chip->ecc.size; |
| 335 | int eccbytes = chip->ecc.bytes; | 335 | int eccbytes = chip->ecc.bytes; |
| @@ -857,7 +857,6 @@ static int __exit flctl_remove(struct platform_device *pdev) | |||
| 857 | } | 857 | } |
| 858 | 858 | ||
| 859 | static struct platform_driver flctl_driver = { | 859 | static struct platform_driver flctl_driver = { |
| 860 | .probe = flctl_probe, | ||
| 861 | .remove = flctl_remove, | 860 | .remove = flctl_remove, |
| 862 | .driver = { | 861 | .driver = { |
| 863 | .name = "sh_flctl", | 862 | .name = "sh_flctl", |
| @@ -867,7 +866,7 @@ static struct platform_driver flctl_driver = { | |||
| 867 | 866 | ||
| 868 | static int __init flctl_nand_init(void) | 867 | static int __init flctl_nand_init(void) |
| 869 | { | 868 | { |
| 870 | return platform_driver_register(&flctl_driver); | 869 | return platform_driver_probe(&flctl_driver, flctl_probe); |
| 871 | } | 870 | } |
| 872 | 871 | ||
| 873 | static void __exit flctl_nand_cleanup(void) | 872 | static void __exit flctl_nand_cleanup(void) |
diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c index daa6a4c3b8ce..92c73344a669 100644 --- a/drivers/mtd/nand/tmio_nand.c +++ b/drivers/mtd/nand/tmio_nand.c | |||
| @@ -301,6 +301,21 @@ static int tmio_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, | |||
| 301 | return 0; | 301 | return 0; |
| 302 | } | 302 | } |
| 303 | 303 | ||
| 304 | static int tmio_nand_correct_data(struct mtd_info *mtd, unsigned char *buf, | ||
| 305 | unsigned char *read_ecc, unsigned char *calc_ecc) | ||
| 306 | { | ||
| 307 | int r0, r1; | ||
| 308 | |||
| 309 | /* assume ecc.size = 512 and ecc.bytes = 6 */ | ||
| 310 | r0 = __nand_correct_data(buf, read_ecc, calc_ecc, 256); | ||
| 311 | if (r0 < 0) | ||
| 312 | return r0; | ||
| 313 | r1 = __nand_correct_data(buf + 256, read_ecc + 3, calc_ecc + 3, 256); | ||
| 314 | if (r1 < 0) | ||
| 315 | return r1; | ||
| 316 | return r0 + r1; | ||
| 317 | } | ||
| 318 | |||
| 304 | static int tmio_hw_init(struct platform_device *dev, struct tmio_nand *tmio) | 319 | static int tmio_hw_init(struct platform_device *dev, struct tmio_nand *tmio) |
| 305 | { | 320 | { |
| 306 | struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data; | 321 | struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data; |
| @@ -424,7 +439,7 @@ static int tmio_probe(struct platform_device *dev) | |||
| 424 | nand_chip->ecc.bytes = 6; | 439 | nand_chip->ecc.bytes = 6; |
| 425 | nand_chip->ecc.hwctl = tmio_nand_enable_hwecc; | 440 | nand_chip->ecc.hwctl = tmio_nand_enable_hwecc; |
| 426 | nand_chip->ecc.calculate = tmio_nand_calculate_ecc; | 441 | nand_chip->ecc.calculate = tmio_nand_calculate_ecc; |
| 427 | nand_chip->ecc.correct = nand_correct_data; | 442 | nand_chip->ecc.correct = tmio_nand_correct_data; |
| 428 | 443 | ||
| 429 | if (data) | 444 | if (data) |
| 430 | nand_chip->badblock_pattern = data->badblock_pattern; | 445 | nand_chip->badblock_pattern = data->badblock_pattern; |
diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c index 488088eff2ca..73af8324d0d0 100644 --- a/drivers/mtd/nand/txx9ndfmc.c +++ b/drivers/mtd/nand/txx9ndfmc.c | |||
| @@ -189,18 +189,43 @@ static int txx9ndfmc_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat, | |||
| 189 | uint8_t *ecc_code) | 189 | uint8_t *ecc_code) |
| 190 | { | 190 | { |
| 191 | struct platform_device *dev = mtd_to_platdev(mtd); | 191 | struct platform_device *dev = mtd_to_platdev(mtd); |
| 192 | struct nand_chip *chip = mtd->priv; | ||
| 193 | int eccbytes; | ||
| 192 | u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); | 194 | u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); |
| 193 | 195 | ||
| 194 | mcr &= ~TXX9_NDFMCR_ECC_ALL; | 196 | mcr &= ~TXX9_NDFMCR_ECC_ALL; |
| 195 | txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); | 197 | txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); |
| 196 | txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_READ, TXX9_NDFMCR); | 198 | txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_READ, TXX9_NDFMCR); |
| 197 | ecc_code[1] = txx9ndfmc_read(dev, TXX9_NDFDTR); | 199 | for (eccbytes = chip->ecc.bytes; eccbytes > 0; eccbytes -= 3) { |
| 198 | ecc_code[0] = txx9ndfmc_read(dev, TXX9_NDFDTR); | 200 | ecc_code[1] = txx9ndfmc_read(dev, TXX9_NDFDTR); |
| 199 | ecc_code[2] = txx9ndfmc_read(dev, TXX9_NDFDTR); | 201 | ecc_code[0] = txx9ndfmc_read(dev, TXX9_NDFDTR); |
| 202 | ecc_code[2] = txx9ndfmc_read(dev, TXX9_NDFDTR); | ||
| 203 | ecc_code += 3; | ||
| 204 | } | ||
| 200 | txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); | 205 | txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); |
| 201 | return 0; | 206 | return 0; |
| 202 | } | 207 | } |
| 203 | 208 | ||
| 209 | static int txx9ndfmc_correct_data(struct mtd_info *mtd, unsigned char *buf, | ||
| 210 | unsigned char *read_ecc, unsigned char *calc_ecc) | ||
| 211 | { | ||
| 212 | struct nand_chip *chip = mtd->priv; | ||
| 213 | int eccsize; | ||
| 214 | int corrected = 0; | ||
| 215 | int stat; | ||
| 216 | |||
| 217 | for (eccsize = chip->ecc.size; eccsize > 0; eccsize -= 256) { | ||
| 218 | stat = __nand_correct_data(buf, read_ecc, calc_ecc, 256); | ||
| 219 | if (stat < 0) | ||
| 220 | return stat; | ||
| 221 | corrected += stat; | ||
| 222 | buf += 256; | ||
| 223 | read_ecc += 3; | ||
| 224 | calc_ecc += 3; | ||
| 225 | } | ||
| 226 | return corrected; | ||
| 227 | } | ||
| 228 | |||
| 204 | static void txx9ndfmc_enable_hwecc(struct mtd_info *mtd, int mode) | 229 | static void txx9ndfmc_enable_hwecc(struct mtd_info *mtd, int mode) |
| 205 | { | 230 | { |
| 206 | struct platform_device *dev = mtd_to_platdev(mtd); | 231 | struct platform_device *dev = mtd_to_platdev(mtd); |
| @@ -244,6 +269,22 @@ static void txx9ndfmc_initialize(struct platform_device *dev) | |||
| 244 | #define TXX9NDFMC_NS_TO_CYC(gbusclk, ns) \ | 269 | #define TXX9NDFMC_NS_TO_CYC(gbusclk, ns) \ |
| 245 | DIV_ROUND_UP((ns) * DIV_ROUND_UP(gbusclk, 1000), 1000000) | 270 | DIV_ROUND_UP((ns) * DIV_ROUND_UP(gbusclk, 1000), 1000000) |
| 246 | 271 | ||
| 272 | static int txx9ndfmc_nand_scan(struct mtd_info *mtd) | ||
| 273 | { | ||
| 274 | struct nand_chip *chip = mtd->priv; | ||
| 275 | int ret; | ||
| 276 | |||
| 277 | ret = nand_scan_ident(mtd, 1); | ||
| 278 | if (!ret) { | ||
| 279 | if (mtd->writesize >= 512) { | ||
| 280 | chip->ecc.size = mtd->writesize; | ||
| 281 | chip->ecc.bytes = 3 * (mtd->writesize / 256); | ||
| 282 | } | ||
| 283 | ret = nand_scan_tail(mtd); | ||
| 284 | } | ||
| 285 | return ret; | ||
| 286 | } | ||
| 287 | |||
| 247 | static int __init txx9ndfmc_probe(struct platform_device *dev) | 288 | static int __init txx9ndfmc_probe(struct platform_device *dev) |
| 248 | { | 289 | { |
| 249 | struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; | 290 | struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; |
| @@ -321,9 +362,10 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) | |||
| 321 | chip->cmd_ctrl = txx9ndfmc_cmd_ctrl; | 362 | chip->cmd_ctrl = txx9ndfmc_cmd_ctrl; |
| 322 | chip->dev_ready = txx9ndfmc_dev_ready; | 363 | chip->dev_ready = txx9ndfmc_dev_ready; |
| 323 | chip->ecc.calculate = txx9ndfmc_calculate_ecc; | 364 | chip->ecc.calculate = txx9ndfmc_calculate_ecc; |
| 324 | chip->ecc.correct = nand_correct_data; | 365 | chip->ecc.correct = txx9ndfmc_correct_data; |
| 325 | chip->ecc.hwctl = txx9ndfmc_enable_hwecc; | 366 | chip->ecc.hwctl = txx9ndfmc_enable_hwecc; |
| 326 | chip->ecc.mode = NAND_ECC_HW; | 367 | chip->ecc.mode = NAND_ECC_HW; |
| 368 | /* txx9ndfmc_nand_scan will overwrite ecc.size and ecc.bytes */ | ||
| 327 | chip->ecc.size = 256; | 369 | chip->ecc.size = 256; |
| 328 | chip->ecc.bytes = 3; | 370 | chip->ecc.bytes = 3; |
| 329 | chip->chip_delay = 100; | 371 | chip->chip_delay = 100; |
| @@ -349,7 +391,7 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) | |||
| 349 | if (plat->wide_mask & (1 << i)) | 391 | if (plat->wide_mask & (1 << i)) |
| 350 | chip->options |= NAND_BUSWIDTH_16; | 392 | chip->options |= NAND_BUSWIDTH_16; |
| 351 | 393 | ||
| 352 | if (nand_scan(mtd, 1)) { | 394 | if (txx9ndfmc_nand_scan(mtd)) { |
| 353 | kfree(txx9_priv->mtdname); | 395 | kfree(txx9_priv->mtdname); |
| 354 | kfree(txx9_priv); | 396 | kfree(txx9_priv); |
| 355 | continue; | 397 | continue; |
diff --git a/drivers/mtd/nand/w90p910_nand.c b/drivers/mtd/nand/w90p910_nand.c new file mode 100644 index 000000000000..7680e731348a --- /dev/null +++ b/drivers/mtd/nand/w90p910_nand.c | |||
| @@ -0,0 +1,382 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2009 Nuvoton technology corporation. | ||
| 3 | * | ||
| 4 | * Wan ZongShun <mcuos.com@gmail.com> | ||
| 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 as published by | ||
| 8 | * the Free Software Foundation;version 2 of the License. | ||
| 9 | * | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/slab.h> | ||
| 13 | #include <linux/init.h> | ||
| 14 | #include <linux/module.h> | ||
| 15 | #include <linux/interrupt.h> | ||
| 16 | #include <linux/io.h> | ||
| 17 | #include <linux/platform_device.h> | ||
| 18 | #include <linux/delay.h> | ||
| 19 | #include <linux/clk.h> | ||
| 20 | #include <linux/err.h> | ||
| 21 | |||
| 22 | #include <linux/mtd/mtd.h> | ||
| 23 | #include <linux/mtd/nand.h> | ||
| 24 | #include <linux/mtd/partitions.h> | ||
| 25 | |||
| 26 | #define REG_FMICSR 0x00 | ||
| 27 | #define REG_SMCSR 0xa0 | ||
| 28 | #define REG_SMISR 0xac | ||
| 29 | #define REG_SMCMD 0xb0 | ||
| 30 | #define REG_SMADDR 0xb4 | ||
| 31 | #define REG_SMDATA 0xb8 | ||
| 32 | |||
| 33 | #define RESET_FMI 0x01 | ||
| 34 | #define NAND_EN 0x08 | ||
| 35 | #define READYBUSY (0x01 << 18) | ||
| 36 | |||
| 37 | #define SWRST 0x01 | ||
| 38 | #define PSIZE (0x01 << 3) | ||
| 39 | #define DMARWEN (0x03 << 1) | ||
| 40 | #define BUSWID (0x01 << 4) | ||
| 41 | #define ECC4EN (0x01 << 5) | ||
| 42 | #define WP (0x01 << 24) | ||
| 43 | #define NANDCS (0x01 << 25) | ||
| 44 | #define ENDADDR (0x01 << 31) | ||
| 45 | |||
| 46 | #define read_data_reg(dev) \ | ||
| 47 | __raw_readl((dev)->reg + REG_SMDATA) | ||
| 48 | |||
| 49 | #define write_data_reg(dev, val) \ | ||
| 50 | __raw_writel((val), (dev)->reg + REG_SMDATA) | ||
| 51 | |||
| 52 | #define write_cmd_reg(dev, val) \ | ||
| 53 | __raw_writel((val), (dev)->reg + REG_SMCMD) | ||
| 54 | |||
| 55 | #define write_addr_reg(dev, val) \ | ||
| 56 | __raw_writel((val), (dev)->reg + REG_SMADDR) | ||
| 57 | |||
| 58 | struct w90p910_nand { | ||
| 59 | struct mtd_info mtd; | ||
| 60 | struct nand_chip chip; | ||
| 61 | void __iomem *reg; | ||
| 62 | struct clk *clk; | ||
| 63 | spinlock_t lock; | ||
| 64 | }; | ||
| 65 | |||
| 66 | static const struct mtd_partition partitions[] = { | ||
| 67 | { | ||
| 68 | .name = "NAND FS 0", | ||
| 69 | .offset = 0, | ||
| 70 | .size = 8 * 1024 * 1024 | ||
| 71 | }, | ||
| 72 | { | ||
| 73 | .name = "NAND FS 1", | ||
| 74 | .offset = MTDPART_OFS_APPEND, | ||
| 75 | .size = MTDPART_SIZ_FULL | ||
| 76 | } | ||
| 77 | }; | ||
| 78 | |||
| 79 | static unsigned char w90p910_nand_read_byte(struct mtd_info *mtd) | ||
| 80 | { | ||
| 81 | unsigned char ret; | ||
| 82 | struct w90p910_nand *nand; | ||
| 83 | |||
| 84 | nand = container_of(mtd, struct w90p910_nand, mtd); | ||
| 85 | |||
| 86 | ret = (unsigned char)read_data_reg(nand); | ||
| 87 | |||
| 88 | return ret; | ||
| 89 | } | ||
| 90 | |||
| 91 | static void w90p910_nand_read_buf(struct mtd_info *mtd, | ||
| 92 | unsigned char *buf, int len) | ||
| 93 | { | ||
| 94 | int i; | ||
| 95 | struct w90p910_nand *nand; | ||
| 96 | |||
| 97 | nand = container_of(mtd, struct w90p910_nand, mtd); | ||
| 98 | |||
| 99 | for (i = 0; i < len; i++) | ||
| 100 | buf[i] = (unsigned char)read_data_reg(nand); | ||
| 101 | } | ||
| 102 | |||
| 103 | static void w90p910_nand_write_buf(struct mtd_info *mtd, | ||
| 104 | const unsigned char *buf, int len) | ||
| 105 | { | ||
| 106 | int i; | ||
| 107 | struct w90p910_nand *nand; | ||
| 108 | |||
| 109 | nand = container_of(mtd, struct w90p910_nand, mtd); | ||
| 110 | |||
| 111 | for (i = 0; i < len; i++) | ||
| 112 | write_data_reg(nand, buf[i]); | ||
| 113 | } | ||
| 114 | |||
| 115 | static int w90p910_verify_buf(struct mtd_info *mtd, | ||
| 116 | const unsigned char *buf, int len) | ||
| 117 | { | ||
| 118 | int i; | ||
| 119 | struct w90p910_nand *nand; | ||
| 120 | |||
| 121 | nand = container_of(mtd, struct w90p910_nand, mtd); | ||
| 122 | |||
| 123 | for (i = 0; i < len; i++) { | ||
| 124 | if (buf[i] != (unsigned char)read_data_reg(nand)) | ||
| 125 | return -EFAULT; | ||
| 126 | } | ||
| 127 | |||
| 128 | return 0; | ||
| 129 | } | ||
| 130 | |||
| 131 | static int w90p910_check_rb(struct w90p910_nand *nand) | ||
| 132 | { | ||
| 133 | unsigned int val; | ||
| 134 | spin_lock(&nand->lock); | ||
| 135 | val = __raw_readl(REG_SMISR); | ||
| 136 | val &= READYBUSY; | ||
| 137 | spin_unlock(&nand->lock); | ||
| 138 | |||
| 139 | return val; | ||
| 140 | } | ||
| 141 | |||
| 142 | static int w90p910_nand_devready(struct mtd_info *mtd) | ||
| 143 | { | ||
| 144 | struct w90p910_nand *nand; | ||
| 145 | int ready; | ||
| 146 | |||
| 147 | nand = container_of(mtd, struct w90p910_nand, mtd); | ||
| 148 | |||
| 149 | ready = (w90p910_check_rb(nand)) ? 1 : 0; | ||
| 150 | return ready; | ||
| 151 | } | ||
| 152 | |||
| 153 | static void w90p910_nand_command_lp(struct mtd_info *mtd, | ||
| 154 | unsigned int command, int column, int page_addr) | ||
| 155 | { | ||
| 156 | register struct nand_chip *chip = mtd->priv; | ||
| 157 | struct w90p910_nand *nand; | ||
| 158 | |||
| 159 | nand = container_of(mtd, struct w90p910_nand, mtd); | ||
| 160 | |||
| 161 | if (command == NAND_CMD_READOOB) { | ||
| 162 | column += mtd->writesize; | ||
| 163 | command = NAND_CMD_READ0; | ||
| 164 | } | ||
| 165 | |||
| 166 | write_cmd_reg(nand, command & 0xff); | ||
| 167 | |||
| 168 | if (column != -1 || page_addr != -1) { | ||
| 169 | |||
| 170 | if (column != -1) { | ||
| 171 | if (chip->options & NAND_BUSWIDTH_16) | ||
| 172 | column >>= 1; | ||
| 173 | write_addr_reg(nand, column); | ||
| 174 | write_addr_reg(nand, column >> 8 | ENDADDR); | ||
| 175 | } | ||
| 176 | if (page_addr != -1) { | ||
| 177 | write_addr_reg(nand, page_addr); | ||
| 178 | |||
| 179 | if (chip->chipsize > (128 << 20)) { | ||
| 180 | write_addr_reg(nand, page_addr >> 8); | ||
| 181 | write_addr_reg(nand, page_addr >> 16 | ENDADDR); | ||
| 182 | } else { | ||
| 183 | write_addr_reg(nand, page_addr >> 8 | ENDADDR); | ||
| 184 | } | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 188 | switch (command) { | ||
| 189 | case NAND_CMD_CACHEDPROG: | ||
| 190 | case NAND_CMD_PAGEPROG: | ||
| 191 | case NAND_CMD_ERASE1: | ||
| 192 | case NAND_CMD_ERASE2: | ||
| 193 | case NAND_CMD_SEQIN: | ||
| 194 | case NAND_CMD_RNDIN: | ||
| 195 | case NAND_CMD_STATUS: | ||
| 196 | case NAND_CMD_DEPLETE1: | ||
| 197 | return; | ||
| 198 | |||
| 199 | case NAND_CMD_STATUS_ERROR: | ||
| 200 | case NAND_CMD_STATUS_ERROR0: | ||
| 201 | case NAND_CMD_STATUS_ERROR1: | ||
| 202 | case NAND_CMD_STATUS_ERROR2: | ||
| 203 | case NAND_CMD_STATUS_ERROR3: | ||
| 204 | udelay(chip->chip_delay); | ||
| 205 | return; | ||
| 206 | |||
| 207 | case NAND_CMD_RESET: | ||
| 208 | if (chip->dev_ready) | ||
| 209 | break; | ||
| 210 | udelay(chip->chip_delay); | ||
| 211 | |||
| 212 | write_cmd_reg(nand, NAND_CMD_STATUS); | ||
| 213 | write_cmd_reg(nand, command); | ||
| 214 | |||
| 215 | while (!w90p910_check_rb(nand)) | ||
| 216 | ; | ||
| 217 | |||
| 218 | return; | ||
| 219 | |||
| 220 | case NAND_CMD_RNDOUT: | ||
| 221 | write_cmd_reg(nand, NAND_CMD_RNDOUTSTART); | ||
| 222 | return; | ||
| 223 | |||
| 224 | case NAND_CMD_READ0: | ||
| 225 | |||
| 226 | write_cmd_reg(nand, NAND_CMD_READSTART); | ||
| 227 | default: | ||
| 228 | |||
| 229 | if (!chip->dev_ready) { | ||
| 230 | udelay(chip->chip_delay); | ||
| 231 | return; | ||
| 232 | } | ||
| 233 | } | ||
| 234 | |||
| 235 | /* Apply this short delay always to ensure that we do wait tWB in | ||
| 236 | * any case on any machine. */ | ||
| 237 | ndelay(100); | ||
| 238 | |||
| 239 | while (!chip->dev_ready(mtd)) | ||
| 240 | ; | ||
| 241 | } | ||
| 242 | |||
| 243 | |||
| 244 | static void w90p910_nand_enable(struct w90p910_nand *nand) | ||
| 245 | { | ||
| 246 | unsigned int val; | ||
| 247 | spin_lock(&nand->lock); | ||
| 248 | __raw_writel(RESET_FMI, (nand->reg + REG_FMICSR)); | ||
| 249 | |||
| 250 | val = __raw_readl(nand->reg + REG_FMICSR); | ||
| 251 | |||
| 252 | if (!(val & NAND_EN)) | ||
| 253 | __raw_writel(val | NAND_EN, REG_FMICSR); | ||
| 254 | |||
| 255 | val = __raw_readl(nand->reg + REG_SMCSR); | ||
| 256 | |||
| 257 | val &= ~(SWRST|PSIZE|DMARWEN|BUSWID|ECC4EN|NANDCS); | ||
| 258 | val |= WP; | ||
| 259 | |||
| 260 | __raw_writel(val, nand->reg + REG_SMCSR); | ||
| 261 | |||
| 262 | spin_unlock(&nand->lock); | ||
| 263 | } | ||
| 264 | |||
| 265 | static int __devinit w90p910_nand_probe(struct platform_device *pdev) | ||
| 266 | { | ||
| 267 | struct w90p910_nand *w90p910_nand; | ||
| 268 | struct nand_chip *chip; | ||
| 269 | int retval; | ||
| 270 | struct resource *res; | ||
| 271 | |||
| 272 | retval = 0; | ||
| 273 | |||
| 274 | w90p910_nand = kzalloc(sizeof(struct w90p910_nand), GFP_KERNEL); | ||
| 275 | if (!w90p910_nand) | ||
| 276 | return -ENOMEM; | ||
| 277 | chip = &(w90p910_nand->chip); | ||
| 278 | |||
| 279 | w90p910_nand->mtd.priv = chip; | ||
| 280 | w90p910_nand->mtd.owner = THIS_MODULE; | ||
| 281 | spin_lock_init(&w90p910_nand->lock); | ||
| 282 | |||
| 283 | w90p910_nand->clk = clk_get(&pdev->dev, NULL); | ||
| 284 | if (IS_ERR(w90p910_nand->clk)) { | ||
| 285 | retval = -ENOENT; | ||
| 286 | goto fail1; | ||
| 287 | } | ||
| 288 | clk_enable(w90p910_nand->clk); | ||
| 289 | |||
| 290 | chip->cmdfunc = w90p910_nand_command_lp; | ||
| 291 | chip->dev_ready = w90p910_nand_devready; | ||
| 292 | chip->read_byte = w90p910_nand_read_byte; | ||
| 293 | chip->write_buf = w90p910_nand_write_buf; | ||
| 294 | chip->read_buf = w90p910_nand_read_buf; | ||
| 295 | chip->verify_buf = w90p910_verify_buf; | ||
| 296 | chip->chip_delay = 50; | ||
| 297 | chip->options = 0; | ||
| 298 | chip->ecc.mode = NAND_ECC_SOFT; | ||
| 299 | |||
| 300 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 301 | if (!res) { | ||
| 302 | retval = -ENXIO; | ||
| 303 | goto fail1; | ||
| 304 | } | ||
| 305 | |||
| 306 | if (!request_mem_region(res->start, resource_size(res), pdev->name)) { | ||
| 307 | retval = -EBUSY; | ||
| 308 | goto fail1; | ||
| 309 | } | ||
| 310 | |||
| 311 | w90p910_nand->reg = ioremap(res->start, resource_size(res)); | ||
| 312 | if (!w90p910_nand->reg) { | ||
| 313 | retval = -ENOMEM; | ||
| 314 | goto fail2; | ||
| 315 | } | ||
| 316 | |||
| 317 | w90p910_nand_enable(w90p910_nand); | ||
| 318 | |||
| 319 | if (nand_scan(&(w90p910_nand->mtd), 1)) { | ||
| 320 | retval = -ENXIO; | ||
| 321 | goto fail3; | ||
| 322 | } | ||
| 323 | |||
| 324 | add_mtd_partitions(&(w90p910_nand->mtd), partitions, | ||
| 325 | ARRAY_SIZE(partitions)); | ||
| 326 | |||
| 327 | platform_set_drvdata(pdev, w90p910_nand); | ||
| 328 | |||
| 329 | return retval; | ||
| 330 | |||
| 331 | fail3: iounmap(w90p910_nand->reg); | ||
| 332 | fail2: release_mem_region(res->start, resource_size(res)); | ||
| 333 | fail1: kfree(w90p910_nand); | ||
| 334 | return retval; | ||
| 335 | } | ||
| 336 | |||
| 337 | static int __devexit w90p910_nand_remove(struct platform_device *pdev) | ||
| 338 | { | ||
| 339 | struct w90p910_nand *w90p910_nand = platform_get_drvdata(pdev); | ||
| 340 | struct resource *res; | ||
| 341 | |||
| 342 | iounmap(w90p910_nand->reg); | ||
| 343 | |||
| 344 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 345 | release_mem_region(res->start, resource_size(res)); | ||
| 346 | |||
| 347 | clk_disable(w90p910_nand->clk); | ||
| 348 | clk_put(w90p910_nand->clk); | ||
| 349 | |||
| 350 | kfree(w90p910_nand); | ||
| 351 | |||
| 352 | platform_set_drvdata(pdev, NULL); | ||
| 353 | |||
| 354 | return 0; | ||
| 355 | } | ||
| 356 | |||
| 357 | static struct platform_driver w90p910_nand_driver = { | ||
| 358 | .probe = w90p910_nand_probe, | ||
| 359 | .remove = __devexit_p(w90p910_nand_remove), | ||
| 360 | .driver = { | ||
| 361 | .name = "w90p910-fmi", | ||
| 362 | .owner = THIS_MODULE, | ||
| 363 | }, | ||
| 364 | }; | ||
| 365 | |||
| 366 | static int __init w90p910_nand_init(void) | ||
| 367 | { | ||
| 368 | return platform_driver_register(&w90p910_nand_driver); | ||
| 369 | } | ||
| 370 | |||
| 371 | static void __exit w90p910_nand_exit(void) | ||
| 372 | { | ||
| 373 | platform_driver_unregister(&w90p910_nand_driver); | ||
| 374 | } | ||
| 375 | |||
| 376 | module_init(w90p910_nand_init); | ||
| 377 | module_exit(w90p910_nand_exit); | ||
| 378 | |||
| 379 | MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>"); | ||
| 380 | MODULE_DESCRIPTION("w90p910 nand driver!"); | ||
| 381 | MODULE_LICENSE("GPL"); | ||
| 382 | MODULE_ALIAS("platform:w90p910-fmi"); | ||
diff --git a/drivers/mtd/ofpart.c b/drivers/mtd/ofpart.c index 3e164f0c9295..62d6a78c4eee 100644 --- a/drivers/mtd/ofpart.c +++ b/drivers/mtd/ofpart.c | |||
| @@ -46,21 +46,12 @@ int __devinit of_mtd_parse_partitions(struct device *dev, | |||
| 46 | const u32 *reg; | 46 | const u32 *reg; |
| 47 | int len; | 47 | int len; |
| 48 | 48 | ||
| 49 | /* check if this is a partition node */ | 49 | reg = of_get_property(pp, "reg", &len); |
| 50 | partname = of_get_property(pp, "name", &len); | 50 | if (!reg) { |
| 51 | if (strcmp(partname, "partition") != 0) { | ||
| 52 | nr_parts--; | 51 | nr_parts--; |
| 53 | continue; | 52 | continue; |
| 54 | } | 53 | } |
| 55 | 54 | ||
| 56 | reg = of_get_property(pp, "reg", &len); | ||
| 57 | if (!reg || (len != 2 * sizeof(u32))) { | ||
| 58 | of_node_put(pp); | ||
| 59 | dev_err(dev, "Invalid 'reg' on %s\n", node->full_name); | ||
| 60 | kfree(*pparts); | ||
| 61 | *pparts = NULL; | ||
| 62 | return -EINVAL; | ||
| 63 | } | ||
| 64 | (*pparts)[i].offset = reg[0]; | 55 | (*pparts)[i].offset = reg[0]; |
| 65 | (*pparts)[i].size = reg[1]; | 56 | (*pparts)[i].size = reg[1]; |
| 66 | 57 | ||
| @@ -75,6 +66,14 @@ int __devinit of_mtd_parse_partitions(struct device *dev, | |||
| 75 | i++; | 66 | i++; |
| 76 | } | 67 | } |
| 77 | 68 | ||
| 69 | if (!i) { | ||
| 70 | of_node_put(pp); | ||
| 71 | dev_err(dev, "No valid partition found on %s\n", node->full_name); | ||
| 72 | kfree(*pparts); | ||
| 73 | *pparts = NULL; | ||
| 74 | return -EINVAL; | ||
| 75 | } | ||
| 76 | |||
| 78 | return nr_parts; | 77 | return nr_parts; |
| 79 | } | 78 | } |
| 80 | EXPORT_SYMBOL(of_mtd_parse_partitions); | 79 | EXPORT_SYMBOL(of_mtd_parse_partitions); |
diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig index 79fa79e8f8de..a38f580c2bb3 100644 --- a/drivers/mtd/onenand/Kconfig +++ b/drivers/mtd/onenand/Kconfig | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | menuconfig MTD_ONENAND | 5 | menuconfig MTD_ONENAND |
| 6 | tristate "OneNAND Device Support" | 6 | tristate "OneNAND Device Support" |
| 7 | depends on MTD | 7 | depends on MTD |
| 8 | select MTD_PARTITIONS | ||
| 8 | help | 9 | help |
| 9 | This enables support for accessing all type of OneNAND flash | 10 | This enables support for accessing all type of OneNAND flash |
| 10 | devices. For further information see | 11 | devices. For further information see |
| @@ -23,7 +24,6 @@ config MTD_ONENAND_VERIFY_WRITE | |||
| 23 | 24 | ||
| 24 | config MTD_ONENAND_GENERIC | 25 | config MTD_ONENAND_GENERIC |
| 25 | tristate "OneNAND Flash device via platform device driver" | 26 | tristate "OneNAND Flash device via platform device driver" |
| 26 | depends on ARM | ||
| 27 | help | 27 | help |
| 28 | Support for OneNAND flash via platform device driver. | 28 | Support for OneNAND flash via platform device driver. |
| 29 | 29 | ||
| @@ -66,7 +66,6 @@ config MTD_ONENAND_2X_PROGRAM | |||
| 66 | 66 | ||
| 67 | config MTD_ONENAND_SIM | 67 | config MTD_ONENAND_SIM |
| 68 | tristate "OneNAND simulator support" | 68 | tristate "OneNAND simulator support" |
| 69 | depends on MTD_PARTITIONS | ||
| 70 | help | 69 | help |
| 71 | The simulator may simulate various OneNAND flash chips for the | 70 | The simulator may simulate various OneNAND flash chips for the |
| 72 | OneNAND MTD layer. | 71 | OneNAND MTD layer. |
diff --git a/drivers/mtd/onenand/generic.c b/drivers/mtd/onenand/generic.c index 3a496c33fb52..e78914938c5c 100644 --- a/drivers/mtd/onenand/generic.c +++ b/drivers/mtd/onenand/generic.c | |||
| @@ -19,12 +19,16 @@ | |||
| 19 | #include <linux/mtd/mtd.h> | 19 | #include <linux/mtd/mtd.h> |
| 20 | #include <linux/mtd/onenand.h> | 20 | #include <linux/mtd/onenand.h> |
| 21 | #include <linux/mtd/partitions.h> | 21 | #include <linux/mtd/partitions.h> |
| 22 | |||
| 23 | #include <asm/io.h> | 22 | #include <asm/io.h> |
| 24 | #include <asm/mach/flash.h> | ||
| 25 | |||
| 26 | #define DRIVER_NAME "onenand" | ||
| 27 | 23 | ||
| 24 | /* | ||
| 25 | * Note: Driver name and platform data format have been updated! | ||
| 26 | * | ||
| 27 | * This version of the driver is named "onenand-flash" and takes struct | ||
| 28 | * onenand_platform_data as platform data. The old ARM-specific version | ||
| 29 | * with the name "onenand" used to take struct flash_platform_data. | ||
| 30 | */ | ||
| 31 | #define DRIVER_NAME "onenand-flash" | ||
| 28 | 32 | ||
| 29 | #ifdef CONFIG_MTD_PARTITIONS | 33 | #ifdef CONFIG_MTD_PARTITIONS |
| 30 | static const char *part_probes[] = { "cmdlinepart", NULL, }; | 34 | static const char *part_probes[] = { "cmdlinepart", NULL, }; |
| @@ -39,16 +43,16 @@ struct onenand_info { | |||
| 39 | static int __devinit generic_onenand_probe(struct platform_device *pdev) | 43 | static int __devinit generic_onenand_probe(struct platform_device *pdev) |
| 40 | { | 44 | { |
| 41 | struct onenand_info *info; | 45 | struct onenand_info *info; |
| 42 | struct flash_platform_data *pdata = pdev->dev.platform_data; | 46 | struct onenand_platform_data *pdata = pdev->dev.platform_data; |
| 43 | struct resource *res = pdev->resource; | 47 | struct resource *res = pdev->resource; |
| 44 | unsigned long size = res->end - res->start + 1; | 48 | unsigned long size = resource_size(res); |
| 45 | int err; | 49 | int err; |
| 46 | 50 | ||
| 47 | info = kzalloc(sizeof(struct onenand_info), GFP_KERNEL); | 51 | info = kzalloc(sizeof(struct onenand_info), GFP_KERNEL); |
| 48 | if (!info) | 52 | if (!info) |
| 49 | return -ENOMEM; | 53 | return -ENOMEM; |
| 50 | 54 | ||
| 51 | if (!request_mem_region(res->start, size, pdev->dev.driver->name)) { | 55 | if (!request_mem_region(res->start, size, dev_name(&pdev->dev))) { |
| 52 | err = -EBUSY; | 56 | err = -EBUSY; |
| 53 | goto out_free_info; | 57 | goto out_free_info; |
| 54 | } | 58 | } |
| @@ -59,7 +63,7 @@ static int __devinit generic_onenand_probe(struct platform_device *pdev) | |||
| 59 | goto out_release_mem_region; | 63 | goto out_release_mem_region; |
| 60 | } | 64 | } |
| 61 | 65 | ||
| 62 | info->onenand.mmcontrol = pdata->mmcontrol; | 66 | info->onenand.mmcontrol = pdata ? pdata->mmcontrol : 0; |
| 63 | info->onenand.irq = platform_get_irq(pdev, 0); | 67 | info->onenand.irq = platform_get_irq(pdev, 0); |
| 64 | 68 | ||
| 65 | info->mtd.name = dev_name(&pdev->dev); | 69 | info->mtd.name = dev_name(&pdev->dev); |
| @@ -75,7 +79,7 @@ static int __devinit generic_onenand_probe(struct platform_device *pdev) | |||
| 75 | err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0); | 79 | err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0); |
| 76 | if (err > 0) | 80 | if (err > 0) |
| 77 | add_mtd_partitions(&info->mtd, info->parts, err); | 81 | add_mtd_partitions(&info->mtd, info->parts, err); |
| 78 | else if (err <= 0 && pdata->parts) | 82 | else if (err <= 0 && pdata && pdata->parts) |
| 79 | add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts); | 83 | add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts); |
| 80 | else | 84 | else |
| 81 | #endif | 85 | #endif |
| @@ -99,7 +103,7 @@ static int __devexit generic_onenand_remove(struct platform_device *pdev) | |||
| 99 | { | 103 | { |
| 100 | struct onenand_info *info = platform_get_drvdata(pdev); | 104 | struct onenand_info *info = platform_get_drvdata(pdev); |
| 101 | struct resource *res = pdev->resource; | 105 | struct resource *res = pdev->resource; |
| 102 | unsigned long size = res->end - res->start + 1; | 106 | unsigned long size = resource_size(res); |
| 103 | 107 | ||
| 104 | platform_set_drvdata(pdev, NULL); | 108 | platform_set_drvdata(pdev, NULL); |
| 105 | 109 | ||
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 6e829095ea9d..ff66e4330aa7 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c | |||
| @@ -1191,7 +1191,7 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, | |||
| 1191 | /* | 1191 | /* |
| 1192 | * Chip boundary handling in DDP | 1192 | * Chip boundary handling in DDP |
| 1193 | * Now we issued chip 1 read and pointed chip 1 | 1193 | * Now we issued chip 1 read and pointed chip 1 |
| 1194 | * bufferam so we have to point chip 0 bufferam. | 1194 | * bufferram so we have to point chip 0 bufferram. |
| 1195 | */ | 1195 | */ |
| 1196 | if (ONENAND_IS_DDP(this) && | 1196 | if (ONENAND_IS_DDP(this) && |
| 1197 | unlikely(from == (this->chipsize >> 1))) { | 1197 | unlikely(from == (this->chipsize >> 1))) { |
| @@ -1867,8 +1867,8 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, | |||
| 1867 | ONENAND_SET_NEXT_BUFFERRAM(this); | 1867 | ONENAND_SET_NEXT_BUFFERRAM(this); |
| 1868 | 1868 | ||
| 1869 | /* | 1869 | /* |
| 1870 | * 2 PLANE, MLC, and Flex-OneNAND doesn't support | 1870 | * 2 PLANE, MLC, and Flex-OneNAND do not support |
| 1871 | * write-while-programe feature. | 1871 | * write-while-program feature. |
| 1872 | */ | 1872 | */ |
| 1873 | if (!ONENAND_IS_2PLANE(this) && !first) { | 1873 | if (!ONENAND_IS_2PLANE(this) && !first) { |
| 1874 | ONENAND_SET_PREV_BUFFERRAM(this); | 1874 | ONENAND_SET_PREV_BUFFERRAM(this); |
| @@ -1879,7 +1879,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, | |||
| 1879 | onenand_update_bufferram(mtd, prev, !ret && !prev_subpage); | 1879 | onenand_update_bufferram(mtd, prev, !ret && !prev_subpage); |
| 1880 | if (ret) { | 1880 | if (ret) { |
| 1881 | written -= prevlen; | 1881 | written -= prevlen; |
| 1882 | printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret); | 1882 | printk(KERN_ERR "onenand_write_ops_nolock: write failed %d\n", ret); |
| 1883 | break; | 1883 | break; |
| 1884 | } | 1884 | } |
| 1885 | 1885 | ||
| @@ -1905,7 +1905,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, | |||
| 1905 | /* In partial page write we don't update bufferram */ | 1905 | /* In partial page write we don't update bufferram */ |
| 1906 | onenand_update_bufferram(mtd, to, !ret && !subpage); | 1906 | onenand_update_bufferram(mtd, to, !ret && !subpage); |
| 1907 | if (ret) { | 1907 | if (ret) { |
| 1908 | printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret); | 1908 | printk(KERN_ERR "onenand_write_ops_nolock: write failed %d\n", ret); |
| 1909 | break; | 1909 | break; |
| 1910 | } | 1910 | } |
| 1911 | 1911 | ||
| @@ -2201,7 +2201,7 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
| 2201 | /* Grab the lock and see if the device is available */ | 2201 | /* Grab the lock and see if the device is available */ |
| 2202 | onenand_get_device(mtd, FL_ERASING); | 2202 | onenand_get_device(mtd, FL_ERASING); |
| 2203 | 2203 | ||
| 2204 | /* Loop throught the pages */ | 2204 | /* Loop through the blocks */ |
| 2205 | instr->state = MTD_ERASING; | 2205 | instr->state = MTD_ERASING; |
| 2206 | 2206 | ||
| 2207 | while (len) { | 2207 | while (len) { |
| @@ -2328,7 +2328,7 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) | |||
| 2328 | if (bbm->bbt) | 2328 | if (bbm->bbt) |
| 2329 | bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); | 2329 | bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); |
| 2330 | 2330 | ||
| 2331 | /* We write two bytes, so we dont have to mess with 16 bit access */ | 2331 | /* We write two bytes, so we don't have to mess with 16-bit access */ |
| 2332 | ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); | 2332 | ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); |
| 2333 | /* FIXME : What to do when marking SLC block in partition | 2333 | /* FIXME : What to do when marking SLC block in partition |
| 2334 | * with MLC erasesize? For now, it is not advisable to | 2334 | * with MLC erasesize? For now, it is not advisable to |
| @@ -2557,7 +2557,7 @@ static void onenand_unlock_all(struct mtd_info *mtd) | |||
| 2557 | 2557 | ||
| 2558 | #ifdef CONFIG_MTD_ONENAND_OTP | 2558 | #ifdef CONFIG_MTD_ONENAND_OTP |
| 2559 | 2559 | ||
| 2560 | /* Interal OTP operation */ | 2560 | /* Internal OTP operation */ |
| 2561 | typedef int (*otp_op_t)(struct mtd_info *mtd, loff_t form, size_t len, | 2561 | typedef int (*otp_op_t)(struct mtd_info *mtd, loff_t form, size_t len, |
| 2562 | size_t *retlen, u_char *buf); | 2562 | size_t *retlen, u_char *buf); |
| 2563 | 2563 | ||
| @@ -2921,7 +2921,7 @@ static void onenand_check_features(struct mtd_info *mtd) | |||
| 2921 | this->options |= ONENAND_HAS_2PLANE; | 2921 | this->options |= ONENAND_HAS_2PLANE; |
| 2922 | 2922 | ||
| 2923 | case ONENAND_DEVICE_DENSITY_2Gb: | 2923 | case ONENAND_DEVICE_DENSITY_2Gb: |
| 2924 | /* 2Gb DDP don't have 2 plane */ | 2924 | /* 2Gb DDP does not have 2 plane */ |
| 2925 | if (!ONENAND_IS_DDP(this)) | 2925 | if (!ONENAND_IS_DDP(this)) |
| 2926 | this->options |= ONENAND_HAS_2PLANE; | 2926 | this->options |= ONENAND_HAS_2PLANE; |
| 2927 | this->options |= ONENAND_HAS_UNLOCK_ALL; | 2927 | this->options |= ONENAND_HAS_UNLOCK_ALL; |
| @@ -3364,7 +3364,7 @@ static int onenand_probe(struct mtd_info *mtd) | |||
| 3364 | /* It's real page size */ | 3364 | /* It's real page size */ |
| 3365 | this->writesize = mtd->writesize; | 3365 | this->writesize = mtd->writesize; |
| 3366 | 3366 | ||
| 3367 | /* REVIST: Multichip handling */ | 3367 | /* REVISIT: Multichip handling */ |
| 3368 | 3368 | ||
| 3369 | if (FLEXONENAND(this)) | 3369 | if (FLEXONENAND(this)) |
| 3370 | flexonenand_get_size(mtd); | 3370 | flexonenand_get_size(mtd); |
diff --git a/drivers/mtd/tests/mtd_oobtest.c b/drivers/mtd/tests/mtd_oobtest.c index a18e8d2f2557..5553cd4eab20 100644 --- a/drivers/mtd/tests/mtd_oobtest.c +++ b/drivers/mtd/tests/mtd_oobtest.c | |||
| @@ -512,7 +512,7 @@ static int __init mtd_oobtest_init(void) | |||
| 512 | goto out; | 512 | goto out; |
| 513 | 513 | ||
| 514 | addr0 = 0; | 514 | addr0 = 0; |
| 515 | for (i = 0; bbt[i] && i < ebcnt; ++i) | 515 | for (i = 0; i < ebcnt && bbt[i]; ++i) |
| 516 | addr0 += mtd->erasesize; | 516 | addr0 += mtd->erasesize; |
| 517 | 517 | ||
| 518 | /* Attempt to write off end of OOB */ | 518 | /* Attempt to write off end of OOB */ |
diff --git a/drivers/mtd/tests/mtd_pagetest.c b/drivers/mtd/tests/mtd_pagetest.c index 9648818b9e2c..103cac480fee 100644 --- a/drivers/mtd/tests/mtd_pagetest.c +++ b/drivers/mtd/tests/mtd_pagetest.c | |||
| @@ -116,11 +116,11 @@ static int verify_eraseblock(int ebnum) | |||
| 116 | loff_t addr = ebnum * mtd->erasesize; | 116 | loff_t addr = ebnum * mtd->erasesize; |
| 117 | 117 | ||
| 118 | addr0 = 0; | 118 | addr0 = 0; |
| 119 | for (i = 0; bbt[i] && i < ebcnt; ++i) | 119 | for (i = 0; i < ebcnt && bbt[i]; ++i) |
| 120 | addr0 += mtd->erasesize; | 120 | addr0 += mtd->erasesize; |
| 121 | 121 | ||
| 122 | addrn = mtd->size; | 122 | addrn = mtd->size; |
| 123 | for (i = 0; bbt[ebcnt - i - 1] && i < ebcnt; ++i) | 123 | for (i = 0; i < ebcnt && bbt[ebcnt - i - 1]; ++i) |
| 124 | addrn -= mtd->erasesize; | 124 | addrn -= mtd->erasesize; |
| 125 | 125 | ||
| 126 | set_random_data(writebuf, mtd->erasesize); | 126 | set_random_data(writebuf, mtd->erasesize); |
| @@ -219,11 +219,11 @@ static int crosstest(void) | |||
| 219 | memset(pp1, 0, pgsize * 4); | 219 | memset(pp1, 0, pgsize * 4); |
| 220 | 220 | ||
| 221 | addr0 = 0; | 221 | addr0 = 0; |
| 222 | for (i = 0; bbt[i] && i < ebcnt; ++i) | 222 | for (i = 0; i < ebcnt && bbt[i]; ++i) |
| 223 | addr0 += mtd->erasesize; | 223 | addr0 += mtd->erasesize; |
| 224 | 224 | ||
| 225 | addrn = mtd->size; | 225 | addrn = mtd->size; |
| 226 | for (i = 0; bbt[ebcnt - i - 1] && i < ebcnt; ++i) | 226 | for (i = 0; i < ebcnt && bbt[ebcnt - i - 1]; ++i) |
| 227 | addrn -= mtd->erasesize; | 227 | addrn -= mtd->erasesize; |
| 228 | 228 | ||
| 229 | /* Read 2nd-to-last page to pp1 */ | 229 | /* Read 2nd-to-last page to pp1 */ |
| @@ -317,7 +317,7 @@ static int erasecrosstest(void) | |||
| 317 | 317 | ||
| 318 | ebnum = 0; | 318 | ebnum = 0; |
| 319 | addr0 = 0; | 319 | addr0 = 0; |
| 320 | for (i = 0; bbt[i] && i < ebcnt; ++i) { | 320 | for (i = 0; i < ebcnt && bbt[i]; ++i) { |
| 321 | addr0 += mtd->erasesize; | 321 | addr0 += mtd->erasesize; |
| 322 | ebnum += 1; | 322 | ebnum += 1; |
| 323 | } | 323 | } |
| @@ -413,7 +413,7 @@ static int erasetest(void) | |||
| 413 | 413 | ||
| 414 | ebnum = 0; | 414 | ebnum = 0; |
| 415 | addr0 = 0; | 415 | addr0 = 0; |
| 416 | for (i = 0; bbt[i] && i < ebcnt; ++i) { | 416 | for (i = 0; i < ebcnt && bbt[i]; ++i) { |
| 417 | addr0 += mtd->erasesize; | 417 | addr0 += mtd->erasesize; |
| 418 | ebnum += 1; | 418 | ebnum += 1; |
| 419 | } | 419 | } |
diff --git a/fs/jffs2/background.c b/fs/jffs2/background.c index e9580104b6ba..3ff50da94789 100644 --- a/fs/jffs2/background.c +++ b/fs/jffs2/background.c | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include <linux/completion.h> | 15 | #include <linux/completion.h> |
| 16 | #include <linux/sched.h> | 16 | #include <linux/sched.h> |
| 17 | #include <linux/freezer.h> | 17 | #include <linux/freezer.h> |
| 18 | #include <linux/kthread.h> | ||
| 18 | #include "nodelist.h" | 19 | #include "nodelist.h" |
| 19 | 20 | ||
| 20 | 21 | ||
| @@ -31,7 +32,7 @@ void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c) | |||
| 31 | /* This must only ever be called when no GC thread is currently running */ | 32 | /* This must only ever be called when no GC thread is currently running */ |
| 32 | int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c) | 33 | int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c) |
| 33 | { | 34 | { |
| 34 | pid_t pid; | 35 | struct task_struct *tsk; |
| 35 | int ret = 0; | 36 | int ret = 0; |
| 36 | 37 | ||
| 37 | BUG_ON(c->gc_task); | 38 | BUG_ON(c->gc_task); |
| @@ -39,15 +40,16 @@ int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c) | |||
| 39 | init_completion(&c->gc_thread_start); | 40 | init_completion(&c->gc_thread_start); |
| 40 | init_completion(&c->gc_thread_exit); | 41 | init_completion(&c->gc_thread_exit); |
| 41 | 42 | ||
| 42 | pid = kernel_thread(jffs2_garbage_collect_thread, c, CLONE_FS|CLONE_FILES); | 43 | tsk = kthread_run(jffs2_garbage_collect_thread, c, "jffs2_gcd_mtd%d", c->mtd->index); |
| 43 | if (pid < 0) { | 44 | if (IS_ERR(tsk)) { |
| 44 | printk(KERN_WARNING "fork failed for JFFS2 garbage collect thread: %d\n", -pid); | 45 | printk(KERN_WARNING "fork failed for JFFS2 garbage collect thread: %ld\n", -PTR_ERR(tsk)); |
| 45 | complete(&c->gc_thread_exit); | 46 | complete(&c->gc_thread_exit); |
| 46 | ret = pid; | 47 | ret = PTR_ERR(tsk); |
| 47 | } else { | 48 | } else { |
| 48 | /* Wait for it... */ | 49 | /* Wait for it... */ |
| 49 | D1(printk(KERN_DEBUG "JFFS2: Garbage collect thread is pid %d\n", pid)); | 50 | D1(printk(KERN_DEBUG "JFFS2: Garbage collect thread is pid %d\n", tsk->pid)); |
| 50 | wait_for_completion(&c->gc_thread_start); | 51 | wait_for_completion(&c->gc_thread_start); |
| 52 | ret = tsk->pid; | ||
| 51 | } | 53 | } |
| 52 | 54 | ||
| 53 | return ret; | 55 | return ret; |
| @@ -71,7 +73,6 @@ static int jffs2_garbage_collect_thread(void *_c) | |||
| 71 | { | 73 | { |
| 72 | struct jffs2_sb_info *c = _c; | 74 | struct jffs2_sb_info *c = _c; |
| 73 | 75 | ||
| 74 | daemonize("jffs2_gcd_mtd%d", c->mtd->index); | ||
| 75 | allow_signal(SIGKILL); | 76 | allow_signal(SIGKILL); |
| 76 | allow_signal(SIGSTOP); | 77 | allow_signal(SIGSTOP); |
| 77 | allow_signal(SIGCONT); | 78 | allow_signal(SIGCONT); |
| @@ -107,6 +108,11 @@ static int jffs2_garbage_collect_thread(void *_c) | |||
| 107 | * the GC thread get there first. */ | 108 | * the GC thread get there first. */ |
| 108 | schedule_timeout_interruptible(msecs_to_jiffies(50)); | 109 | schedule_timeout_interruptible(msecs_to_jiffies(50)); |
| 109 | 110 | ||
| 111 | if (kthread_should_stop()) { | ||
| 112 | D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): kthread_stop() called.\n")); | ||
| 113 | goto die; | ||
| 114 | } | ||
| 115 | |||
| 110 | /* Put_super will send a SIGKILL and then wait on the sem. | 116 | /* Put_super will send a SIGKILL and then wait on the sem. |
| 111 | */ | 117 | */ |
| 112 | while (signal_pending(current) || freezing(current)) { | 118 | while (signal_pending(current) || freezing(current)) { |
diff --git a/fs/jffs2/malloc.c b/fs/jffs2/malloc.c index 9eff2bdae8a7..c082868910f2 100644 --- a/fs/jffs2/malloc.c +++ b/fs/jffs2/malloc.c | |||
| @@ -39,13 +39,13 @@ int __init jffs2_create_slab_caches(void) | |||
| 39 | 39 | ||
| 40 | raw_dirent_slab = kmem_cache_create("jffs2_raw_dirent", | 40 | raw_dirent_slab = kmem_cache_create("jffs2_raw_dirent", |
| 41 | sizeof(struct jffs2_raw_dirent), | 41 | sizeof(struct jffs2_raw_dirent), |
| 42 | 0, 0, NULL); | 42 | 0, SLAB_HWCACHE_ALIGN, NULL); |
| 43 | if (!raw_dirent_slab) | 43 | if (!raw_dirent_slab) |
| 44 | goto err; | 44 | goto err; |
| 45 | 45 | ||
| 46 | raw_inode_slab = kmem_cache_create("jffs2_raw_inode", | 46 | raw_inode_slab = kmem_cache_create("jffs2_raw_inode", |
| 47 | sizeof(struct jffs2_raw_inode), | 47 | sizeof(struct jffs2_raw_inode), |
| 48 | 0, 0, NULL); | 48 | 0, SLAB_HWCACHE_ALIGN, NULL); |
| 49 | if (!raw_inode_slab) | 49 | if (!raw_inode_slab) |
| 50 | goto err; | 50 | goto err; |
| 51 | 51 | ||
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 4030ebada49e..7a232a9bdd62 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h | |||
| @@ -121,6 +121,7 @@ typedef enum { | |||
| 121 | NAND_ECC_SOFT, | 121 | NAND_ECC_SOFT, |
| 122 | NAND_ECC_HW, | 122 | NAND_ECC_HW, |
| 123 | NAND_ECC_HW_SYNDROME, | 123 | NAND_ECC_HW_SYNDROME, |
| 124 | NAND_ECC_HW_OOB_FIRST, | ||
| 124 | } nand_ecc_modes_t; | 125 | } nand_ecc_modes_t; |
| 125 | 126 | ||
| 126 | /* | 127 | /* |
| @@ -271,13 +272,13 @@ struct nand_ecc_ctrl { | |||
| 271 | uint8_t *calc_ecc); | 272 | uint8_t *calc_ecc); |
| 272 | int (*read_page_raw)(struct mtd_info *mtd, | 273 | int (*read_page_raw)(struct mtd_info *mtd, |
| 273 | struct nand_chip *chip, | 274 | struct nand_chip *chip, |
| 274 | uint8_t *buf); | 275 | uint8_t *buf, int page); |
| 275 | void (*write_page_raw)(struct mtd_info *mtd, | 276 | void (*write_page_raw)(struct mtd_info *mtd, |
| 276 | struct nand_chip *chip, | 277 | struct nand_chip *chip, |
| 277 | const uint8_t *buf); | 278 | const uint8_t *buf); |
| 278 | int (*read_page)(struct mtd_info *mtd, | 279 | int (*read_page)(struct mtd_info *mtd, |
| 279 | struct nand_chip *chip, | 280 | struct nand_chip *chip, |
| 280 | uint8_t *buf); | 281 | uint8_t *buf, int page); |
| 281 | int (*read_subpage)(struct mtd_info *mtd, | 282 | int (*read_subpage)(struct mtd_info *mtd, |
| 282 | struct nand_chip *chip, | 283 | struct nand_chip *chip, |
| 283 | uint32_t offs, uint32_t len, | 284 | uint32_t offs, uint32_t len, |
diff --git a/include/linux/mtd/nand_ecc.h b/include/linux/mtd/nand_ecc.h index 090da505425d..052ea8ca2434 100644 --- a/include/linux/mtd/nand_ecc.h +++ b/include/linux/mtd/nand_ecc.h | |||
| @@ -21,6 +21,12 @@ struct mtd_info; | |||
| 21 | int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code); | 21 | int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code); |
| 22 | 22 | ||
| 23 | /* | 23 | /* |
| 24 | * Detect and correct a 1 bit error for eccsize byte block | ||
| 25 | */ | ||
| 26 | int __nand_correct_data(u_char *dat, u_char *read_ecc, u_char *calc_ecc, | ||
| 27 | unsigned int eccsize); | ||
| 28 | |||
| 29 | /* | ||
| 24 | * Detect and correct a 1 bit error for 256 byte block | 30 | * Detect and correct a 1 bit error for 256 byte block |
| 25 | */ | 31 | */ |
| 26 | int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc); | 32 | int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc); |
diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index 8ed873374381..4e49f3350678 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h | |||
| @@ -214,4 +214,12 @@ unsigned onenand_block(struct onenand_chip *this, loff_t addr); | |||
| 214 | loff_t onenand_addr(struct onenand_chip *this, int block); | 214 | loff_t onenand_addr(struct onenand_chip *this, int block); |
| 215 | int flexonenand_region(struct mtd_info *mtd, loff_t addr); | 215 | int flexonenand_region(struct mtd_info *mtd, loff_t addr); |
| 216 | 216 | ||
| 217 | struct mtd_partition; | ||
| 218 | |||
| 219 | struct onenand_platform_data { | ||
| 220 | void (*mmcontrol)(struct mtd_info *mtd, int sync_read); | ||
| 221 | struct mtd_partition *parts; | ||
| 222 | unsigned int nr_parts; | ||
| 223 | }; | ||
| 224 | |||
| 217 | #endif /* __LINUX_MTD_ONENAND_H */ | 225 | #endif /* __LINUX_MTD_ONENAND_H */ |
diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h index 86a6bbef6465..acadbf53a69f 100644 --- a/include/linux/mtd/onenand_regs.h +++ b/include/linux/mtd/onenand_regs.h | |||
| @@ -207,6 +207,9 @@ | |||
| 207 | #define ONENAND_ECC_2BIT (1 << 1) | 207 | #define ONENAND_ECC_2BIT (1 << 1) |
| 208 | #define ONENAND_ECC_2BIT_ALL (0xAAAA) | 208 | #define ONENAND_ECC_2BIT_ALL (0xAAAA) |
| 209 | #define FLEXONENAND_UNCORRECTABLE_ERROR (0x1010) | 209 | #define FLEXONENAND_UNCORRECTABLE_ERROR (0x1010) |
| 210 | #define ONENAND_ECC_3BIT (1 << 2) | ||
| 211 | #define ONENAND_ECC_4BIT (1 << 3) | ||
| 212 | #define ONENAND_ECC_4BIT_UNCORRECTABLE (0x1010) | ||
| 210 | 213 | ||
| 211 | /* | 214 | /* |
| 212 | * One-Time Programmable (OTP) | 215 | * One-Time Programmable (OTP) |
