diff options
38 files changed, 1881 insertions, 331 deletions
diff --git a/Documentation/devicetree/bindings/mtd/elm.txt b/Documentation/devicetree/bindings/mtd/elm.txt new file mode 100644 index 000000000000..8c1528c421d4 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/elm.txt | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | Error location module | ||
| 2 | |||
| 3 | Required properties: | ||
| 4 | - compatible: Must be "ti,am33xx-elm" | ||
| 5 | - reg: physical base address and size of the registers map. | ||
| 6 | - interrupts: Interrupt number for the elm. | ||
| 7 | |||
| 8 | Optional properties: | ||
| 9 | - ti,hwmods: Name of the hwmod associated to the elm | ||
| 10 | |||
| 11 | Example: | ||
| 12 | elm: elm@0 { | ||
| 13 | compatible = "ti,am3352-elm"; | ||
| 14 | reg = <0x48080000 0x2000>; | ||
| 15 | interrupts = <4>; | ||
| 16 | }; | ||
diff --git a/Documentation/devicetree/bindings/mtd/mtd-physmap.txt b/Documentation/devicetree/bindings/mtd/mtd-physmap.txt index dab7847fc800..61c5ec850f2f 100644 --- a/Documentation/devicetree/bindings/mtd/mtd-physmap.txt +++ b/Documentation/devicetree/bindings/mtd/mtd-physmap.txt | |||
| @@ -26,6 +26,9 @@ file systems on embedded devices. | |||
| 26 | - linux,mtd-name: allow to specify the mtd name for retro capability with | 26 | - linux,mtd-name: allow to specify the mtd name for retro capability with |
| 27 | physmap-flash drivers as boot loader pass the mtd partition via the old | 27 | physmap-flash drivers as boot loader pass the mtd partition via the old |
| 28 | device name physmap-flash. | 28 | device name physmap-flash. |
| 29 | - use-advanced-sector-protection: boolean to enable support for the | ||
| 30 | advanced sector protection (Spansion: PPB - Persistent Protection | ||
| 31 | Bits) locking. | ||
| 29 | 32 | ||
| 30 | For JEDEC compatible devices, the following additional properties | 33 | For JEDEC compatible devices, the following additional properties |
| 31 | are defined: | 34 | are defined: |
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 03f2eb5627ec..557bec599f4f 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig | |||
| @@ -74,8 +74,8 @@ config MTD_REDBOOT_PARTS_READONLY | |||
| 74 | endif # MTD_REDBOOT_PARTS | 74 | endif # MTD_REDBOOT_PARTS |
| 75 | 75 | ||
| 76 | config MTD_CMDLINE_PARTS | 76 | config MTD_CMDLINE_PARTS |
| 77 | bool "Command line partition table parsing" | 77 | tristate "Command line partition table parsing" |
| 78 | depends on MTD = "y" | 78 | depends on MTD |
| 79 | ---help--- | 79 | ---help--- |
| 80 | Allow generic configuration of the MTD partition tables via the kernel | 80 | Allow generic configuration of the MTD partition tables via the kernel |
| 81 | command line. Multiple flash resources are supported for hardware where | 81 | command line. Multiple flash resources are supported for hardware where |
diff --git a/drivers/mtd/ar7part.c b/drivers/mtd/ar7part.c index 7c057a05adb6..ddc0a4287a4b 100644 --- a/drivers/mtd/ar7part.c +++ b/drivers/mtd/ar7part.c | |||
| @@ -142,7 +142,13 @@ static int __init ar7_parser_init(void) | |||
| 142 | return register_mtd_parser(&ar7_parser); | 142 | return register_mtd_parser(&ar7_parser); |
| 143 | } | 143 | } |
| 144 | 144 | ||
| 145 | static void __exit ar7_parser_exit(void) | ||
| 146 | { | ||
| 147 | deregister_mtd_parser(&ar7_parser); | ||
| 148 | } | ||
| 149 | |||
| 145 | module_init(ar7_parser_init); | 150 | module_init(ar7_parser_init); |
| 151 | module_exit(ar7_parser_exit); | ||
| 146 | 152 | ||
| 147 | MODULE_LICENSE("GPL"); | 153 | MODULE_LICENSE("GPL"); |
| 148 | MODULE_AUTHOR( "Felix Fietkau <nbd@openwrt.org>, " | 154 | MODULE_AUTHOR( "Felix Fietkau <nbd@openwrt.org>, " |
diff --git a/drivers/mtd/bcm47xxpart.c b/drivers/mtd/bcm47xxpart.c index 6a1180502cc1..63feb75cc8e0 100644 --- a/drivers/mtd/bcm47xxpart.c +++ b/drivers/mtd/bcm47xxpart.c | |||
| @@ -19,12 +19,6 @@ | |||
| 19 | /* 10 parts were found on sflash on Netgear WNDR4500 */ | 19 | /* 10 parts were found on sflash on Netgear WNDR4500 */ |
| 20 | #define BCM47XXPART_MAX_PARTS 12 | 20 | #define BCM47XXPART_MAX_PARTS 12 |
| 21 | 21 | ||
| 22 | /* | ||
| 23 | * Amount of bytes we read when analyzing each block of flash memory. | ||
| 24 | * Set it big enough to allow detecting partition and reading important data. | ||
| 25 | */ | ||
| 26 | #define BCM47XXPART_BYTES_TO_READ 0x404 | ||
| 27 | |||
| 28 | /* Magics */ | 22 | /* Magics */ |
| 29 | #define BOARD_DATA_MAGIC 0x5246504D /* MPFR */ | 23 | #define BOARD_DATA_MAGIC 0x5246504D /* MPFR */ |
| 30 | #define POT_MAGIC1 0x54544f50 /* POTT */ | 24 | #define POT_MAGIC1 0x54544f50 /* POTT */ |
| @@ -59,13 +53,21 @@ static int bcm47xxpart_parse(struct mtd_info *master, | |||
| 59 | uint32_t *buf; | 53 | uint32_t *buf; |
| 60 | size_t bytes_read; | 54 | size_t bytes_read; |
| 61 | uint32_t offset; | 55 | uint32_t offset; |
| 62 | uint32_t blocksize = 0x10000; | 56 | uint32_t blocksize = master->erasesize; |
| 63 | struct trx_header *trx; | 57 | struct trx_header *trx; |
| 58 | int trx_part = -1; | ||
| 59 | int last_trx_part = -1; | ||
| 60 | int max_bytes_to_read = 0x8004; | ||
| 61 | |||
| 62 | if (blocksize <= 0x10000) | ||
| 63 | blocksize = 0x10000; | ||
| 64 | if (blocksize == 0x20000) | ||
| 65 | max_bytes_to_read = 0x18004; | ||
| 64 | 66 | ||
| 65 | /* Alloc */ | 67 | /* Alloc */ |
| 66 | parts = kzalloc(sizeof(struct mtd_partition) * BCM47XXPART_MAX_PARTS, | 68 | parts = kzalloc(sizeof(struct mtd_partition) * BCM47XXPART_MAX_PARTS, |
| 67 | GFP_KERNEL); | 69 | GFP_KERNEL); |
| 68 | buf = kzalloc(BCM47XXPART_BYTES_TO_READ, GFP_KERNEL); | 70 | buf = kzalloc(max_bytes_to_read, GFP_KERNEL); |
| 69 | 71 | ||
| 70 | /* Parse block by block looking for magics */ | 72 | /* Parse block by block looking for magics */ |
| 71 | for (offset = 0; offset <= master->size - blocksize; | 73 | for (offset = 0; offset <= master->size - blocksize; |
| @@ -80,7 +82,7 @@ static int bcm47xxpart_parse(struct mtd_info *master, | |||
| 80 | } | 82 | } |
| 81 | 83 | ||
| 82 | /* Read beginning of the block */ | 84 | /* Read beginning of the block */ |
| 83 | if (mtd_read(master, offset, BCM47XXPART_BYTES_TO_READ, | 85 | if (mtd_read(master, offset, max_bytes_to_read, |
| 84 | &bytes_read, (uint8_t *)buf) < 0) { | 86 | &bytes_read, (uint8_t *)buf) < 0) { |
| 85 | pr_err("mtd_read error while parsing (offset: 0x%X)!\n", | 87 | pr_err("mtd_read error while parsing (offset: 0x%X)!\n", |
| 86 | offset); | 88 | offset); |
| @@ -95,9 +97,16 @@ static int bcm47xxpart_parse(struct mtd_info *master, | |||
| 95 | } | 97 | } |
| 96 | 98 | ||
| 97 | /* Standard NVRAM */ | 99 | /* Standard NVRAM */ |
| 98 | if (buf[0x000 / 4] == NVRAM_HEADER) { | 100 | if (buf[0x000 / 4] == NVRAM_HEADER || |
| 101 | buf[0x1000 / 4] == NVRAM_HEADER || | ||
| 102 | buf[0x8000 / 4] == NVRAM_HEADER || | ||
| 103 | (blocksize == 0x20000 && ( | ||
| 104 | buf[0x10000 / 4] == NVRAM_HEADER || | ||
| 105 | buf[0x11000 / 4] == NVRAM_HEADER || | ||
| 106 | buf[0x18000 / 4] == NVRAM_HEADER))) { | ||
| 99 | bcm47xxpart_add_part(&parts[curr_part++], "nvram", | 107 | bcm47xxpart_add_part(&parts[curr_part++], "nvram", |
| 100 | offset, 0); | 108 | offset, 0); |
| 109 | offset = rounddown(offset, blocksize); | ||
| 101 | continue; | 110 | continue; |
| 102 | } | 111 | } |
| 103 | 112 | ||
| @@ -131,6 +140,10 @@ static int bcm47xxpart_parse(struct mtd_info *master, | |||
| 131 | if (buf[0x000 / 4] == TRX_MAGIC) { | 140 | if (buf[0x000 / 4] == TRX_MAGIC) { |
| 132 | trx = (struct trx_header *)buf; | 141 | trx = (struct trx_header *)buf; |
| 133 | 142 | ||
| 143 | trx_part = curr_part; | ||
| 144 | bcm47xxpart_add_part(&parts[curr_part++], "firmware", | ||
| 145 | offset, 0); | ||
| 146 | |||
| 134 | i = 0; | 147 | i = 0; |
| 135 | /* We have LZMA loader if offset[2] points to sth */ | 148 | /* We have LZMA loader if offset[2] points to sth */ |
| 136 | if (trx->offset[2]) { | 149 | if (trx->offset[2]) { |
| @@ -154,6 +167,8 @@ static int bcm47xxpart_parse(struct mtd_info *master, | |||
| 154 | offset + trx->offset[i], 0); | 167 | offset + trx->offset[i], 0); |
| 155 | i++; | 168 | i++; |
| 156 | 169 | ||
| 170 | last_trx_part = curr_part - 1; | ||
| 171 | |||
| 157 | /* | 172 | /* |
| 158 | * We have whole TRX scanned, skip to the next part. Use | 173 | * We have whole TRX scanned, skip to the next part. Use |
| 159 | * roundown (not roundup), as the loop will increase | 174 | * roundown (not roundup), as the loop will increase |
| @@ -169,11 +184,15 @@ static int bcm47xxpart_parse(struct mtd_info *master, | |||
| 169 | * Assume that partitions end at the beginning of the one they are | 184 | * Assume that partitions end at the beginning of the one they are |
| 170 | * followed by. | 185 | * followed by. |
| 171 | */ | 186 | */ |
| 172 | for (i = 0; i < curr_part - 1; i++) | 187 | for (i = 0; i < curr_part; i++) { |
| 173 | parts[i].size = parts[i + 1].offset - parts[i].offset; | 188 | u64 next_part_offset = (i < curr_part - 1) ? |
| 174 | if (curr_part > 0) | 189 | parts[i + 1].offset : master->size; |
| 175 | parts[curr_part - 1].size = | 190 | |
| 176 | master->size - parts[curr_part - 1].offset; | 191 | parts[i].size = next_part_offset - parts[i].offset; |
| 192 | if (i == last_trx_part && trx_part >= 0) | ||
| 193 | parts[trx_part].size = next_part_offset - | ||
| 194 | parts[trx_part].offset; | ||
| 195 | } | ||
| 177 | 196 | ||
| 178 | *pparts = parts; | 197 | *pparts = parts; |
| 179 | return curr_part; | 198 | return curr_part; |
diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index b86197286f24..fff665d59a0d 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c | |||
| @@ -33,6 +33,8 @@ | |||
| 33 | #include <linux/delay.h> | 33 | #include <linux/delay.h> |
| 34 | #include <linux/interrupt.h> | 34 | #include <linux/interrupt.h> |
| 35 | #include <linux/reboot.h> | 35 | #include <linux/reboot.h> |
| 36 | #include <linux/of.h> | ||
| 37 | #include <linux/of_platform.h> | ||
| 36 | #include <linux/mtd/map.h> | 38 | #include <linux/mtd/map.h> |
| 37 | #include <linux/mtd/mtd.h> | 39 | #include <linux/mtd/mtd.h> |
| 38 | #include <linux/mtd/cfi.h> | 40 | #include <linux/mtd/cfi.h> |
| @@ -74,6 +76,10 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad | |||
| 74 | static int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); | 76 | static int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); |
| 75 | static int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); | 77 | static int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); |
| 76 | 78 | ||
| 79 | static int cfi_ppb_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); | ||
| 80 | static int cfi_ppb_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); | ||
| 81 | static int cfi_ppb_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len); | ||
| 82 | |||
| 77 | static struct mtd_chip_driver cfi_amdstd_chipdrv = { | 83 | static struct mtd_chip_driver cfi_amdstd_chipdrv = { |
| 78 | .probe = NULL, /* Not usable directly */ | 84 | .probe = NULL, /* Not usable directly */ |
| 79 | .destroy = cfi_amdstd_destroy, | 85 | .destroy = cfi_amdstd_destroy, |
| @@ -496,6 +502,7 @@ static void cfi_fixup_m29ew_delay_after_resume(struct cfi_private *cfi) | |||
| 496 | struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) | 502 | struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) |
| 497 | { | 503 | { |
| 498 | struct cfi_private *cfi = map->fldrv_priv; | 504 | struct cfi_private *cfi = map->fldrv_priv; |
| 505 | struct device_node __maybe_unused *np = map->device_node; | ||
| 499 | struct mtd_info *mtd; | 506 | struct mtd_info *mtd; |
| 500 | int i; | 507 | int i; |
| 501 | 508 | ||
| @@ -570,6 +577,17 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) | |||
| 570 | cfi_tell_features(extp); | 577 | cfi_tell_features(extp); |
| 571 | #endif | 578 | #endif |
| 572 | 579 | ||
| 580 | #ifdef CONFIG_OF | ||
| 581 | if (np && of_property_read_bool( | ||
| 582 | np, "use-advanced-sector-protection") | ||
| 583 | && extp->BlkProtUnprot == 8) { | ||
| 584 | printk(KERN_INFO " Advanced Sector Protection (PPB Locking) supported\n"); | ||
| 585 | mtd->_lock = cfi_ppb_lock; | ||
| 586 | mtd->_unlock = cfi_ppb_unlock; | ||
| 587 | mtd->_is_locked = cfi_ppb_is_locked; | ||
| 588 | } | ||
| 589 | #endif | ||
| 590 | |||
| 573 | bootloc = extp->TopBottom; | 591 | bootloc = extp->TopBottom; |
| 574 | if ((bootloc < 2) || (bootloc > 5)) { | 592 | if ((bootloc < 2) || (bootloc > 5)) { |
| 575 | printk(KERN_WARNING "%s: CFI contains unrecognised boot " | 593 | printk(KERN_WARNING "%s: CFI contains unrecognised boot " |
| @@ -2172,6 +2190,205 @@ static int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) | |||
| 2172 | return cfi_varsize_frob(mtd, do_atmel_unlock, ofs, len, NULL); | 2190 | return cfi_varsize_frob(mtd, do_atmel_unlock, ofs, len, NULL); |
| 2173 | } | 2191 | } |
| 2174 | 2192 | ||
| 2193 | /* | ||
| 2194 | * Advanced Sector Protection - PPB (Persistent Protection Bit) locking | ||
| 2195 | */ | ||
| 2196 | |||
| 2197 | struct ppb_lock { | ||
| 2198 | struct flchip *chip; | ||
| 2199 | loff_t offset; | ||
| 2200 | int locked; | ||
| 2201 | }; | ||
| 2202 | |||
| 2203 | #define MAX_SECTORS 512 | ||
| 2204 | |||
| 2205 | #define DO_XXLOCK_ONEBLOCK_LOCK ((void *)1) | ||
| 2206 | #define DO_XXLOCK_ONEBLOCK_UNLOCK ((void *)2) | ||
| 2207 | #define DO_XXLOCK_ONEBLOCK_GETLOCK ((void *)3) | ||
| 2208 | |||
| 2209 | static int __maybe_unused do_ppb_xxlock(struct map_info *map, | ||
| 2210 | struct flchip *chip, | ||
| 2211 | unsigned long adr, int len, void *thunk) | ||
| 2212 | { | ||
| 2213 | struct cfi_private *cfi = map->fldrv_priv; | ||
| 2214 | unsigned long timeo; | ||
| 2215 | int ret; | ||
| 2216 | |||
| 2217 | mutex_lock(&chip->mutex); | ||
| 2218 | ret = get_chip(map, chip, adr + chip->start, FL_LOCKING); | ||
| 2219 | if (ret) { | ||
| 2220 | mutex_unlock(&chip->mutex); | ||
| 2221 | return ret; | ||
| 2222 | } | ||
| 2223 | |||
| 2224 | pr_debug("MTD %s(): XXLOCK 0x%08lx len %d\n", __func__, adr, len); | ||
| 2225 | |||
| 2226 | cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, | ||
| 2227 | cfi->device_type, NULL); | ||
| 2228 | cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, | ||
| 2229 | cfi->device_type, NULL); | ||
| 2230 | /* PPB entry command */ | ||
| 2231 | cfi_send_gen_cmd(0xC0, cfi->addr_unlock1, chip->start, map, cfi, | ||
| 2232 | cfi->device_type, NULL); | ||
| 2233 | |||
| 2234 | if (thunk == DO_XXLOCK_ONEBLOCK_LOCK) { | ||
| 2235 | chip->state = FL_LOCKING; | ||
| 2236 | map_write(map, CMD(0xA0), chip->start + adr); | ||
| 2237 | map_write(map, CMD(0x00), chip->start + adr); | ||
| 2238 | } else if (thunk == DO_XXLOCK_ONEBLOCK_UNLOCK) { | ||
| 2239 | /* | ||
| 2240 | * Unlocking of one specific sector is not supported, so we | ||
| 2241 | * have to unlock all sectors of this device instead | ||
| 2242 | */ | ||
| 2243 | chip->state = FL_UNLOCKING; | ||
| 2244 | map_write(map, CMD(0x80), chip->start); | ||
| 2245 | map_write(map, CMD(0x30), chip->start); | ||
| 2246 | } else if (thunk == DO_XXLOCK_ONEBLOCK_GETLOCK) { | ||
| 2247 | chip->state = FL_JEDEC_QUERY; | ||
| 2248 | /* Return locked status: 0->locked, 1->unlocked */ | ||
| 2249 | ret = !cfi_read_query(map, adr); | ||
| 2250 | } else | ||
| 2251 | BUG(); | ||
| 2252 | |||
| 2253 | /* | ||
| 2254 | * Wait for some time as unlocking of all sectors takes quite long | ||
| 2255 | */ | ||
| 2256 | timeo = jiffies + msecs_to_jiffies(2000); /* 2s max (un)locking */ | ||
| 2257 | for (;;) { | ||
| 2258 | if (chip_ready(map, adr)) | ||
| 2259 | break; | ||
| 2260 | |||
| 2261 | if (time_after(jiffies, timeo)) { | ||
| 2262 | printk(KERN_ERR "Waiting for chip to be ready timed out.\n"); | ||
| 2263 | ret = -EIO; | ||
| 2264 | break; | ||
| 2265 | } | ||
| 2266 | |||
| 2267 | UDELAY(map, chip, adr, 1); | ||
| 2268 | } | ||
| 2269 | |||
| 2270 | /* Exit BC commands */ | ||
| 2271 | map_write(map, CMD(0x90), chip->start); | ||
| 2272 | map_write(map, CMD(0x00), chip->start); | ||
| 2273 | |||
| 2274 | chip->state = FL_READY; | ||
| 2275 | put_chip(map, chip, adr + chip->start); | ||
| 2276 | mutex_unlock(&chip->mutex); | ||
| 2277 | |||
| 2278 | return ret; | ||
| 2279 | } | ||
| 2280 | |||
| 2281 | static int __maybe_unused cfi_ppb_lock(struct mtd_info *mtd, loff_t ofs, | ||
| 2282 | uint64_t len) | ||
| 2283 | { | ||
| 2284 | return cfi_varsize_frob(mtd, do_ppb_xxlock, ofs, len, | ||
| 2285 | DO_XXLOCK_ONEBLOCK_LOCK); | ||
| 2286 | } | ||
| 2287 | |||
| 2288 | static int __maybe_unused cfi_ppb_unlock(struct mtd_info *mtd, loff_t ofs, | ||
| 2289 | uint64_t len) | ||
| 2290 | { | ||
| 2291 | struct mtd_erase_region_info *regions = mtd->eraseregions; | ||
| 2292 | struct map_info *map = mtd->priv; | ||
| 2293 | struct cfi_private *cfi = map->fldrv_priv; | ||
| 2294 | struct ppb_lock *sect; | ||
| 2295 | unsigned long adr; | ||
| 2296 | loff_t offset; | ||
| 2297 | uint64_t length; | ||
| 2298 | int chipnum; | ||
| 2299 | int i; | ||
| 2300 | int sectors; | ||
| 2301 | int ret; | ||
| 2302 | |||
| 2303 | /* | ||
| 2304 | * PPB unlocking always unlocks all sectors of the flash chip. | ||
| 2305 | * We need to re-lock all previously locked sectors. So lets | ||
| 2306 | * first check the locking status of all sectors and save | ||
| 2307 | * it for future use. | ||
| 2308 | */ | ||
| 2309 | sect = kzalloc(MAX_SECTORS * sizeof(struct ppb_lock), GFP_KERNEL); | ||
| 2310 | if (!sect) | ||
| 2311 | return -ENOMEM; | ||
| 2312 | |||
| 2313 | /* | ||
| 2314 | * This code to walk all sectors is a slightly modified version | ||
| 2315 | * of the cfi_varsize_frob() code. | ||
| 2316 | */ | ||
| 2317 | i = 0; | ||
| 2318 | chipnum = 0; | ||
| 2319 | adr = 0; | ||
| 2320 | sectors = 0; | ||
| 2321 | offset = 0; | ||
| 2322 | length = mtd->size; | ||
| 2323 | |||
| 2324 | while (length) { | ||
| 2325 | int size = regions[i].erasesize; | ||
| 2326 | |||
| 2327 | /* | ||
| 2328 | * Only test sectors that shall not be unlocked. The other | ||
| 2329 | * sectors shall be unlocked, so lets keep their locking | ||
| 2330 | * status at "unlocked" (locked=0) for the final re-locking. | ||
| 2331 | */ | ||
| 2332 | if ((adr < ofs) || (adr >= (ofs + len))) { | ||
| 2333 | sect[sectors].chip = &cfi->chips[chipnum]; | ||
| 2334 | sect[sectors].offset = offset; | ||
| 2335 | sect[sectors].locked = do_ppb_xxlock( | ||
| 2336 | map, &cfi->chips[chipnum], adr, 0, | ||
| 2337 | DO_XXLOCK_ONEBLOCK_GETLOCK); | ||
| 2338 | } | ||
| 2339 | |||
| 2340 | adr += size; | ||
| 2341 | offset += size; | ||
| 2342 | length -= size; | ||
| 2343 | |||
| 2344 | if (offset == regions[i].offset + size * regions[i].numblocks) | ||
| 2345 | i++; | ||
| 2346 | |||
| 2347 | if (adr >> cfi->chipshift) { | ||
| 2348 | adr = 0; | ||
| 2349 | chipnum++; | ||
| 2350 | |||
| 2351 | if (chipnum >= cfi->numchips) | ||
| 2352 | break; | ||
| 2353 | } | ||
| 2354 | |||
| 2355 | sectors++; | ||
| 2356 | if (sectors >= MAX_SECTORS) { | ||
| 2357 | printk(KERN_ERR "Only %d sectors for PPB locking supported!\n", | ||
| 2358 | MAX_SECTORS); | ||
| 2359 | kfree(sect); | ||
| 2360 | return -EINVAL; | ||
| 2361 | } | ||
| 2362 | } | ||
| 2363 | |||
| 2364 | /* Now unlock the whole chip */ | ||
| 2365 | ret = cfi_varsize_frob(mtd, do_ppb_xxlock, ofs, len, | ||
| 2366 | DO_XXLOCK_ONEBLOCK_UNLOCK); | ||
| 2367 | if (ret) { | ||
| 2368 | kfree(sect); | ||
| 2369 | return ret; | ||
| 2370 | } | ||
| 2371 | |||
| 2372 | /* | ||
| 2373 | * PPB unlocking always unlocks all sectors of the flash chip. | ||
| 2374 | * We need to re-lock all previously locked sectors. | ||
| 2375 | */ | ||
| 2376 | for (i = 0; i < sectors; i++) { | ||
| 2377 | if (sect[i].locked) | ||
| 2378 | do_ppb_xxlock(map, sect[i].chip, sect[i].offset, 0, | ||
| 2379 | DO_XXLOCK_ONEBLOCK_LOCK); | ||
| 2380 | } | ||
| 2381 | |||
| 2382 | kfree(sect); | ||
| 2383 | return ret; | ||
| 2384 | } | ||
| 2385 | |||
| 2386 | static int __maybe_unused cfi_ppb_is_locked(struct mtd_info *mtd, loff_t ofs, | ||
| 2387 | uint64_t len) | ||
| 2388 | { | ||
| 2389 | return cfi_varsize_frob(mtd, do_ppb_xxlock, ofs, len, | ||
| 2390 | DO_XXLOCK_ONEBLOCK_GETLOCK) ? 1 : 0; | ||
| 2391 | } | ||
| 2175 | 2392 | ||
| 2176 | static void cfi_amdstd_sync (struct mtd_info *mtd) | 2393 | static void cfi_amdstd_sync (struct mtd_info *mtd) |
| 2177 | { | 2394 | { |
diff --git a/drivers/mtd/cmdlinepart.c b/drivers/mtd/cmdlinepart.c index c533f27d863f..721caebbc5cc 100644 --- a/drivers/mtd/cmdlinepart.c +++ b/drivers/mtd/cmdlinepart.c | |||
| @@ -22,11 +22,22 @@ | |||
| 22 | * | 22 | * |
| 23 | * mtdparts=<mtddef>[;<mtddef] | 23 | * mtdparts=<mtddef>[;<mtddef] |
| 24 | * <mtddef> := <mtd-id>:<partdef>[,<partdef>] | 24 | * <mtddef> := <mtd-id>:<partdef>[,<partdef>] |
| 25 | * where <mtd-id> is the name from the "cat /proc/mtd" command | 25 | * <partdef> := <size>[@<offset>][<name>][ro][lk] |
| 26 | * <partdef> := <size>[@offset][<name>][ro][lk] | ||
| 27 | * <mtd-id> := unique name used in mapping driver/device (mtd->name) | 26 | * <mtd-id> := unique name used in mapping driver/device (mtd->name) |
| 28 | * <size> := standard linux memsize OR "-" to denote all remaining space | 27 | * <size> := standard linux memsize OR "-" to denote all remaining space |
| 28 | * size is automatically truncated at end of device | ||
| 29 | * if specified or trucated size is 0 the part is skipped | ||
| 30 | * <offset> := standard linux memsize | ||
| 31 | * if omitted the part will immediately follow the previous part | ||
| 32 | * or 0 if the first part | ||
| 29 | * <name> := '(' NAME ')' | 33 | * <name> := '(' NAME ')' |
| 34 | * NAME will appear in /proc/mtd | ||
| 35 | * | ||
| 36 | * <size> and <offset> can be specified such that the parts are out of order | ||
| 37 | * in physical memory and may even overlap. | ||
| 38 | * | ||
| 39 | * The parts are assigned MTD numbers in the order they are specified in the | ||
| 40 | * command line regardless of their order in physical memory. | ||
| 30 | * | 41 | * |
| 31 | * Examples: | 42 | * Examples: |
| 32 | * | 43 | * |
| @@ -70,6 +81,7 @@ struct cmdline_mtd_partition { | |||
| 70 | static struct cmdline_mtd_partition *partitions; | 81 | static struct cmdline_mtd_partition *partitions; |
| 71 | 82 | ||
| 72 | /* the command line passed to mtdpart_setup() */ | 83 | /* the command line passed to mtdpart_setup() */ |
| 84 | static char *mtdparts; | ||
| 73 | static char *cmdline; | 85 | static char *cmdline; |
| 74 | static int cmdline_parsed; | 86 | static int cmdline_parsed; |
| 75 | 87 | ||
| @@ -330,6 +342,14 @@ static int parse_cmdline_partitions(struct mtd_info *master, | |||
| 330 | if (part->parts[i].size == SIZE_REMAINING) | 342 | if (part->parts[i].size == SIZE_REMAINING) |
| 331 | part->parts[i].size = master->size - offset; | 343 | part->parts[i].size = master->size - offset; |
| 332 | 344 | ||
| 345 | if (offset + part->parts[i].size > master->size) { | ||
| 346 | printk(KERN_WARNING ERRP | ||
| 347 | "%s: partitioning exceeds flash size, truncating\n", | ||
| 348 | part->mtd_id); | ||
| 349 | part->parts[i].size = master->size - offset; | ||
| 350 | } | ||
| 351 | offset += part->parts[i].size; | ||
| 352 | |||
| 333 | if (part->parts[i].size == 0) { | 353 | if (part->parts[i].size == 0) { |
| 334 | printk(KERN_WARNING ERRP | 354 | printk(KERN_WARNING ERRP |
| 335 | "%s: skipping zero sized partition\n", | 355 | "%s: skipping zero sized partition\n", |
| @@ -337,16 +357,8 @@ static int parse_cmdline_partitions(struct mtd_info *master, | |||
| 337 | part->num_parts--; | 357 | part->num_parts--; |
| 338 | memmove(&part->parts[i], &part->parts[i + 1], | 358 | memmove(&part->parts[i], &part->parts[i + 1], |
| 339 | sizeof(*part->parts) * (part->num_parts - i)); | 359 | sizeof(*part->parts) * (part->num_parts - i)); |
| 340 | continue; | 360 | i--; |
| 341 | } | ||
| 342 | |||
| 343 | if (offset + part->parts[i].size > master->size) { | ||
| 344 | printk(KERN_WARNING ERRP | ||
| 345 | "%s: partitioning exceeds flash size, truncating\n", | ||
| 346 | part->mtd_id); | ||
| 347 | part->parts[i].size = master->size - offset; | ||
| 348 | } | 361 | } |
| 349 | offset += part->parts[i].size; | ||
| 350 | } | 362 | } |
| 351 | 363 | ||
| 352 | *pparts = kmemdup(part->parts, sizeof(*part->parts) * part->num_parts, | 364 | *pparts = kmemdup(part->parts, sizeof(*part->parts) * part->num_parts, |
| @@ -365,7 +377,7 @@ static int parse_cmdline_partitions(struct mtd_info *master, | |||
| 365 | * | 377 | * |
| 366 | * This function needs to be visible for bootloaders. | 378 | * This function needs to be visible for bootloaders. |
| 367 | */ | 379 | */ |
| 368 | static int mtdpart_setup(char *s) | 380 | static int __init mtdpart_setup(char *s) |
| 369 | { | 381 | { |
| 370 | cmdline = s; | 382 | cmdline = s; |
| 371 | return 1; | 383 | return 1; |
| @@ -381,10 +393,21 @@ static struct mtd_part_parser cmdline_parser = { | |||
| 381 | 393 | ||
| 382 | static int __init cmdline_parser_init(void) | 394 | static int __init cmdline_parser_init(void) |
| 383 | { | 395 | { |
| 396 | if (mtdparts) | ||
| 397 | mtdpart_setup(mtdparts); | ||
| 384 | return register_mtd_parser(&cmdline_parser); | 398 | return register_mtd_parser(&cmdline_parser); |
| 385 | } | 399 | } |
| 386 | 400 | ||
| 401 | static void __exit cmdline_parser_exit(void) | ||
| 402 | { | ||
| 403 | deregister_mtd_parser(&cmdline_parser); | ||
| 404 | } | ||
| 405 | |||
| 387 | module_init(cmdline_parser_init); | 406 | module_init(cmdline_parser_init); |
| 407 | module_exit(cmdline_parser_exit); | ||
| 408 | |||
| 409 | MODULE_PARM_DESC(mtdparts, "Partitioning specification"); | ||
| 410 | module_param(mtdparts, charp, 0); | ||
| 388 | 411 | ||
| 389 | MODULE_LICENSE("GPL"); | 412 | MODULE_LICENSE("GPL"); |
| 390 | MODULE_AUTHOR("Marius Groeger <mag@sysgo.de>"); | 413 | MODULE_AUTHOR("Marius Groeger <mag@sysgo.de>"); |
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index 395733a30ef4..369a1943ca25 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile | |||
| @@ -17,8 +17,10 @@ obj-$(CONFIG_MTD_LART) += lart.o | |||
| 17 | obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o | 17 | obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o |
| 18 | obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o | 18 | obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o |
| 19 | obj-$(CONFIG_MTD_M25P80) += m25p80.o | 19 | obj-$(CONFIG_MTD_M25P80) += m25p80.o |
| 20 | obj-$(CONFIG_MTD_NAND_OMAP_BCH) += elm.o | ||
| 20 | obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o | 21 | obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o |
| 21 | obj-$(CONFIG_MTD_SST25L) += sst25l.o | 22 | obj-$(CONFIG_MTD_SST25L) += sst25l.o |
| 22 | obj-$(CONFIG_MTD_BCM47XXSFLASH) += bcm47xxsflash.o | 23 | obj-$(CONFIG_MTD_BCM47XXSFLASH) += bcm47xxsflash.o |
| 23 | 24 | ||
| 24 | CFLAGS_docg3.o += -I$(src) \ No newline at end of file | 25 | |
| 26 | CFLAGS_docg3.o += -I$(src) | ||
diff --git a/drivers/mtd/devices/bcm47xxsflash.c b/drivers/mtd/devices/bcm47xxsflash.c index 4714584aa993..95266285acb1 100644 --- a/drivers/mtd/devices/bcm47xxsflash.c +++ b/drivers/mtd/devices/bcm47xxsflash.c | |||
| @@ -5,6 +5,8 @@ | |||
| 5 | #include <linux/platform_device.h> | 5 | #include <linux/platform_device.h> |
| 6 | #include <linux/bcma/bcma.h> | 6 | #include <linux/bcma/bcma.h> |
| 7 | 7 | ||
| 8 | #include "bcm47xxsflash.h" | ||
| 9 | |||
| 8 | MODULE_LICENSE("GPL"); | 10 | MODULE_LICENSE("GPL"); |
| 9 | MODULE_DESCRIPTION("Serial flash driver for BCMA bus"); | 11 | MODULE_DESCRIPTION("Serial flash driver for BCMA bus"); |
| 10 | 12 | ||
| @@ -13,26 +15,28 @@ static const char *probes[] = { "bcm47xxpart", NULL }; | |||
| 13 | static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len, | 15 | static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len, |
| 14 | size_t *retlen, u_char *buf) | 16 | size_t *retlen, u_char *buf) |
| 15 | { | 17 | { |
| 16 | struct bcma_sflash *sflash = mtd->priv; | 18 | struct bcm47xxsflash *b47s = mtd->priv; |
| 17 | 19 | ||
| 18 | /* Check address range */ | 20 | /* Check address range */ |
| 19 | if ((from + len) > mtd->size) | 21 | if ((from + len) > mtd->size) |
| 20 | return -EINVAL; | 22 | return -EINVAL; |
| 21 | 23 | ||
| 22 | memcpy_fromio(buf, (void __iomem *)KSEG0ADDR(sflash->window + from), | 24 | memcpy_fromio(buf, (void __iomem *)KSEG0ADDR(b47s->window + from), |
| 23 | len); | 25 | len); |
| 26 | *retlen = len; | ||
| 24 | 27 | ||
| 25 | return len; | 28 | return len; |
| 26 | } | 29 | } |
| 27 | 30 | ||
| 28 | static void bcm47xxsflash_fill_mtd(struct bcma_sflash *sflash, | 31 | static void bcm47xxsflash_fill_mtd(struct bcm47xxsflash *b47s) |
| 29 | struct mtd_info *mtd) | ||
| 30 | { | 32 | { |
| 31 | mtd->priv = sflash; | 33 | struct mtd_info *mtd = &b47s->mtd; |
| 34 | |||
| 35 | mtd->priv = b47s; | ||
| 32 | mtd->name = "bcm47xxsflash"; | 36 | mtd->name = "bcm47xxsflash"; |
| 33 | mtd->owner = THIS_MODULE; | 37 | mtd->owner = THIS_MODULE; |
| 34 | mtd->type = MTD_ROM; | 38 | mtd->type = MTD_ROM; |
| 35 | mtd->size = sflash->size; | 39 | mtd->size = b47s->size; |
| 36 | mtd->_read = bcm47xxsflash_read; | 40 | mtd->_read = bcm47xxsflash_read; |
| 37 | 41 | ||
| 38 | /* TODO: implement writing support and verify/change following code */ | 42 | /* TODO: implement writing support and verify/change following code */ |
| @@ -40,19 +44,30 @@ static void bcm47xxsflash_fill_mtd(struct bcma_sflash *sflash, | |||
| 40 | mtd->writebufsize = mtd->writesize = 1; | 44 | mtd->writebufsize = mtd->writesize = 1; |
| 41 | } | 45 | } |
| 42 | 46 | ||
| 43 | static int bcm47xxsflash_probe(struct platform_device *pdev) | 47 | /************************************************** |
| 48 | * BCMA | ||
| 49 | **************************************************/ | ||
| 50 | |||
| 51 | static int bcm47xxsflash_bcma_probe(struct platform_device *pdev) | ||
| 44 | { | 52 | { |
| 45 | struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev); | 53 | struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev); |
| 54 | struct bcm47xxsflash *b47s; | ||
| 46 | int err; | 55 | int err; |
| 47 | 56 | ||
| 48 | sflash->mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL); | 57 | b47s = kzalloc(sizeof(*b47s), GFP_KERNEL); |
| 49 | if (!sflash->mtd) { | 58 | if (!b47s) { |
| 50 | err = -ENOMEM; | 59 | err = -ENOMEM; |
| 51 | goto out; | 60 | goto out; |
| 52 | } | 61 | } |
| 53 | bcm47xxsflash_fill_mtd(sflash, sflash->mtd); | 62 | sflash->priv = b47s; |
| 54 | 63 | ||
| 55 | err = mtd_device_parse_register(sflash->mtd, probes, NULL, NULL, 0); | 64 | b47s->window = sflash->window; |
| 65 | b47s->blocksize = sflash->blocksize; | ||
| 66 | b47s->numblocks = sflash->numblocks; | ||
| 67 | b47s->size = sflash->size; | ||
| 68 | bcm47xxsflash_fill_mtd(b47s); | ||
| 69 | |||
| 70 | err = mtd_device_parse_register(&b47s->mtd, probes, NULL, NULL, 0); | ||
| 56 | if (err) { | 71 | if (err) { |
| 57 | pr_err("Failed to register MTD device: %d\n", err); | 72 | pr_err("Failed to register MTD device: %d\n", err); |
| 58 | goto err_dev_reg; | 73 | goto err_dev_reg; |
| @@ -61,34 +76,40 @@ static int bcm47xxsflash_probe(struct platform_device *pdev) | |||
| 61 | return 0; | 76 | return 0; |
| 62 | 77 | ||
| 63 | err_dev_reg: | 78 | err_dev_reg: |
| 64 | kfree(sflash->mtd); | 79 | kfree(&b47s->mtd); |
| 65 | out: | 80 | out: |
| 66 | return err; | 81 | return err; |
| 67 | } | 82 | } |
| 68 | 83 | ||
| 69 | static int bcm47xxsflash_remove(struct platform_device *pdev) | 84 | static int bcm47xxsflash_bcma_remove(struct platform_device *pdev) |
| 70 | { | 85 | { |
| 71 | struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev); | 86 | struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev); |
| 87 | struct bcm47xxsflash *b47s = sflash->priv; | ||
| 72 | 88 | ||
| 73 | mtd_device_unregister(sflash->mtd); | 89 | mtd_device_unregister(&b47s->mtd); |
| 74 | kfree(sflash->mtd); | 90 | kfree(b47s); |
| 75 | 91 | ||
| 76 | return 0; | 92 | return 0; |
| 77 | } | 93 | } |
| 78 | 94 | ||
| 79 | static struct platform_driver bcma_sflash_driver = { | 95 | static struct platform_driver bcma_sflash_driver = { |
| 80 | .remove = bcm47xxsflash_remove, | 96 | .probe = bcm47xxsflash_bcma_probe, |
| 97 | .remove = bcm47xxsflash_bcma_remove, | ||
| 81 | .driver = { | 98 | .driver = { |
| 82 | .name = "bcma_sflash", | 99 | .name = "bcma_sflash", |
| 83 | .owner = THIS_MODULE, | 100 | .owner = THIS_MODULE, |
| 84 | }, | 101 | }, |
| 85 | }; | 102 | }; |
| 86 | 103 | ||
| 104 | /************************************************** | ||
| 105 | * Init | ||
| 106 | **************************************************/ | ||
| 107 | |||
| 87 | static int __init bcm47xxsflash_init(void) | 108 | static int __init bcm47xxsflash_init(void) |
| 88 | { | 109 | { |
| 89 | int err; | 110 | int err; |
| 90 | 111 | ||
| 91 | err = platform_driver_probe(&bcma_sflash_driver, bcm47xxsflash_probe); | 112 | err = platform_driver_register(&bcma_sflash_driver); |
| 92 | if (err) | 113 | if (err) |
| 93 | pr_err("Failed to register BCMA serial flash driver: %d\n", | 114 | pr_err("Failed to register BCMA serial flash driver: %d\n", |
| 94 | err); | 115 | err); |
diff --git a/drivers/mtd/devices/bcm47xxsflash.h b/drivers/mtd/devices/bcm47xxsflash.h new file mode 100644 index 000000000000..ebf6f710e23c --- /dev/null +++ b/drivers/mtd/devices/bcm47xxsflash.h | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | #ifndef __BCM47XXSFLASH_H | ||
| 2 | #define __BCM47XXSFLASH_H | ||
| 3 | |||
| 4 | #include <linux/mtd/mtd.h> | ||
| 5 | |||
| 6 | struct bcm47xxsflash { | ||
| 7 | u32 window; | ||
| 8 | u32 blocksize; | ||
| 9 | u16 numblocks; | ||
| 10 | u32 size; | ||
| 11 | |||
| 12 | struct mtd_info mtd; | ||
| 13 | }; | ||
| 14 | |||
| 15 | #endif /* BCM47XXSFLASH */ | ||
diff --git a/drivers/mtd/devices/elm.c b/drivers/mtd/devices/elm.c new file mode 100644 index 000000000000..2ec5da9ee248 --- /dev/null +++ b/drivers/mtd/devices/elm.c | |||
| @@ -0,0 +1,404 @@ | |||
| 1 | /* | ||
| 2 | * Error Location Module | ||
| 3 | * | ||
| 4 | * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.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; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | */ | ||
| 17 | |||
| 18 | #include <linux/platform_device.h> | ||
| 19 | #include <linux/module.h> | ||
| 20 | #include <linux/interrupt.h> | ||
| 21 | #include <linux/io.h> | ||
| 22 | #include <linux/of.h> | ||
| 23 | #include <linux/pm_runtime.h> | ||
| 24 | #include <linux/platform_data/elm.h> | ||
| 25 | |||
| 26 | #define ELM_IRQSTATUS 0x018 | ||
| 27 | #define ELM_IRQENABLE 0x01c | ||
| 28 | #define ELM_LOCATION_CONFIG 0x020 | ||
| 29 | #define ELM_PAGE_CTRL 0x080 | ||
| 30 | #define ELM_SYNDROME_FRAGMENT_0 0x400 | ||
| 31 | #define ELM_SYNDROME_FRAGMENT_6 0x418 | ||
| 32 | #define ELM_LOCATION_STATUS 0x800 | ||
| 33 | #define ELM_ERROR_LOCATION_0 0x880 | ||
| 34 | |||
| 35 | /* ELM Interrupt Status Register */ | ||
| 36 | #define INTR_STATUS_PAGE_VALID BIT(8) | ||
| 37 | |||
| 38 | /* ELM Interrupt Enable Register */ | ||
| 39 | #define INTR_EN_PAGE_MASK BIT(8) | ||
| 40 | |||
| 41 | /* ELM Location Configuration Register */ | ||
| 42 | #define ECC_BCH_LEVEL_MASK 0x3 | ||
| 43 | |||
| 44 | /* ELM syndrome */ | ||
| 45 | #define ELM_SYNDROME_VALID BIT(16) | ||
| 46 | |||
| 47 | /* ELM_LOCATION_STATUS Register */ | ||
| 48 | #define ECC_CORRECTABLE_MASK BIT(8) | ||
| 49 | #define ECC_NB_ERRORS_MASK 0x1f | ||
| 50 | |||
| 51 | /* ELM_ERROR_LOCATION_0-15 Registers */ | ||
| 52 | #define ECC_ERROR_LOCATION_MASK 0x1fff | ||
| 53 | |||
| 54 | #define ELM_ECC_SIZE 0x7ff | ||
| 55 | |||
| 56 | #define SYNDROME_FRAGMENT_REG_SIZE 0x40 | ||
| 57 | #define ERROR_LOCATION_SIZE 0x100 | ||
| 58 | |||
| 59 | struct elm_info { | ||
| 60 | struct device *dev; | ||
| 61 | void __iomem *elm_base; | ||
| 62 | struct completion elm_completion; | ||
| 63 | struct list_head list; | ||
| 64 | enum bch_ecc bch_type; | ||
| 65 | }; | ||
| 66 | |||
| 67 | static LIST_HEAD(elm_devices); | ||
| 68 | |||
| 69 | static void elm_write_reg(struct elm_info *info, int offset, u32 val) | ||
| 70 | { | ||
| 71 | writel(val, info->elm_base + offset); | ||
| 72 | } | ||
| 73 | |||
| 74 | static u32 elm_read_reg(struct elm_info *info, int offset) | ||
| 75 | { | ||
| 76 | return readl(info->elm_base + offset); | ||
| 77 | } | ||
| 78 | |||
| 79 | /** | ||
| 80 | * elm_config - Configure ELM module | ||
| 81 | * @dev: ELM device | ||
| 82 | * @bch_type: Type of BCH ecc | ||
| 83 | */ | ||
| 84 | void elm_config(struct device *dev, enum bch_ecc bch_type) | ||
| 85 | { | ||
| 86 | u32 reg_val; | ||
| 87 | struct elm_info *info = dev_get_drvdata(dev); | ||
| 88 | |||
| 89 | reg_val = (bch_type & ECC_BCH_LEVEL_MASK) | (ELM_ECC_SIZE << 16); | ||
| 90 | elm_write_reg(info, ELM_LOCATION_CONFIG, reg_val); | ||
| 91 | info->bch_type = bch_type; | ||
| 92 | } | ||
| 93 | EXPORT_SYMBOL(elm_config); | ||
| 94 | |||
| 95 | /** | ||
| 96 | * elm_configure_page_mode - Enable/Disable page mode | ||
| 97 | * @info: elm info | ||
| 98 | * @index: index number of syndrome fragment vector | ||
| 99 | * @enable: enable/disable flag for page mode | ||
| 100 | * | ||
| 101 | * Enable page mode for syndrome fragment index | ||
| 102 | */ | ||
| 103 | static void elm_configure_page_mode(struct elm_info *info, int index, | ||
| 104 | bool enable) | ||
| 105 | { | ||
| 106 | u32 reg_val; | ||
| 107 | |||
| 108 | reg_val = elm_read_reg(info, ELM_PAGE_CTRL); | ||
| 109 | if (enable) | ||
| 110 | reg_val |= BIT(index); /* enable page mode */ | ||
| 111 | else | ||
| 112 | reg_val &= ~BIT(index); /* disable page mode */ | ||
| 113 | |||
| 114 | elm_write_reg(info, ELM_PAGE_CTRL, reg_val); | ||
| 115 | } | ||
| 116 | |||
| 117 | /** | ||
| 118 | * elm_load_syndrome - Load ELM syndrome reg | ||
| 119 | * @info: elm info | ||
| 120 | * @err_vec: elm error vectors | ||
| 121 | * @ecc: buffer with calculated ecc | ||
| 122 | * | ||
| 123 | * Load syndrome fragment registers with calculated ecc in reverse order. | ||
| 124 | */ | ||
| 125 | static void elm_load_syndrome(struct elm_info *info, | ||
| 126 | struct elm_errorvec *err_vec, u8 *ecc) | ||
| 127 | { | ||
| 128 | int i, offset; | ||
| 129 | u32 val; | ||
| 130 | |||
| 131 | for (i = 0; i < ERROR_VECTOR_MAX; i++) { | ||
| 132 | |||
| 133 | /* Check error reported */ | ||
| 134 | if (err_vec[i].error_reported) { | ||
| 135 | elm_configure_page_mode(info, i, true); | ||
| 136 | offset = ELM_SYNDROME_FRAGMENT_0 + | ||
| 137 | SYNDROME_FRAGMENT_REG_SIZE * i; | ||
| 138 | |||
| 139 | /* BCH8 */ | ||
| 140 | if (info->bch_type) { | ||
| 141 | |||
| 142 | /* syndrome fragment 0 = ecc[9-12B] */ | ||
| 143 | val = cpu_to_be32(*(u32 *) &ecc[9]); | ||
| 144 | elm_write_reg(info, offset, val); | ||
| 145 | |||
| 146 | /* syndrome fragment 1 = ecc[5-8B] */ | ||
| 147 | offset += 4; | ||
| 148 | val = cpu_to_be32(*(u32 *) &ecc[5]); | ||
| 149 | elm_write_reg(info, offset, val); | ||
| 150 | |||
| 151 | /* syndrome fragment 2 = ecc[1-4B] */ | ||
| 152 | offset += 4; | ||
| 153 | val = cpu_to_be32(*(u32 *) &ecc[1]); | ||
| 154 | elm_write_reg(info, offset, val); | ||
| 155 | |||
| 156 | /* syndrome fragment 3 = ecc[0B] */ | ||
| 157 | offset += 4; | ||
| 158 | val = ecc[0]; | ||
| 159 | elm_write_reg(info, offset, val); | ||
| 160 | } else { | ||
| 161 | /* syndrome fragment 0 = ecc[20-52b] bits */ | ||
| 162 | val = (cpu_to_be32(*(u32 *) &ecc[3]) >> 4) | | ||
| 163 | ((ecc[2] & 0xf) << 28); | ||
| 164 | elm_write_reg(info, offset, val); | ||
| 165 | |||
| 166 | /* syndrome fragment 1 = ecc[0-20b] bits */ | ||
| 167 | offset += 4; | ||
| 168 | val = cpu_to_be32(*(u32 *) &ecc[0]) >> 12; | ||
| 169 | elm_write_reg(info, offset, val); | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | /* Update ecc pointer with ecc byte size */ | ||
| 174 | ecc += info->bch_type ? BCH8_SIZE : BCH4_SIZE; | ||
| 175 | } | ||
| 176 | } | ||
| 177 | |||
| 178 | /** | ||
| 179 | * elm_start_processing - start elm syndrome processing | ||
| 180 | * @info: elm info | ||
| 181 | * @err_vec: elm error vectors | ||
| 182 | * | ||
| 183 | * Set syndrome valid bit for syndrome fragment registers for which | ||
| 184 | * elm syndrome fragment registers are loaded. This enables elm module | ||
| 185 | * to start processing syndrome vectors. | ||
| 186 | */ | ||
| 187 | static void elm_start_processing(struct elm_info *info, | ||
| 188 | struct elm_errorvec *err_vec) | ||
| 189 | { | ||
| 190 | int i, offset; | ||
| 191 | u32 reg_val; | ||
| 192 | |||
| 193 | /* | ||
| 194 | * Set syndrome vector valid, so that ELM module | ||
| 195 | * will process it for vectors error is reported | ||
| 196 | */ | ||
| 197 | for (i = 0; i < ERROR_VECTOR_MAX; i++) { | ||
| 198 | if (err_vec[i].error_reported) { | ||
| 199 | offset = ELM_SYNDROME_FRAGMENT_6 + | ||
| 200 | SYNDROME_FRAGMENT_REG_SIZE * i; | ||
| 201 | reg_val = elm_read_reg(info, offset); | ||
| 202 | reg_val |= ELM_SYNDROME_VALID; | ||
| 203 | elm_write_reg(info, offset, reg_val); | ||
| 204 | } | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | /** | ||
| 209 | * elm_error_correction - locate correctable error position | ||
| 210 | * @info: elm info | ||
| 211 | * @err_vec: elm error vectors | ||
| 212 | * | ||
| 213 | * On completion of processing by elm module, error location status | ||
| 214 | * register updated with correctable/uncorrectable error information. | ||
| 215 | * In case of correctable errors, number of errors located from | ||
| 216 | * elm location status register & read the positions from | ||
| 217 | * elm error location register. | ||
| 218 | */ | ||
| 219 | static void elm_error_correction(struct elm_info *info, | ||
| 220 | struct elm_errorvec *err_vec) | ||
| 221 | { | ||
| 222 | int i, j, errors = 0; | ||
| 223 | int offset; | ||
| 224 | u32 reg_val; | ||
| 225 | |||
| 226 | for (i = 0; i < ERROR_VECTOR_MAX; i++) { | ||
| 227 | |||
| 228 | /* Check error reported */ | ||
| 229 | if (err_vec[i].error_reported) { | ||
| 230 | offset = ELM_LOCATION_STATUS + ERROR_LOCATION_SIZE * i; | ||
| 231 | reg_val = elm_read_reg(info, offset); | ||
| 232 | |||
| 233 | /* Check correctable error or not */ | ||
| 234 | if (reg_val & ECC_CORRECTABLE_MASK) { | ||
| 235 | offset = ELM_ERROR_LOCATION_0 + | ||
| 236 | ERROR_LOCATION_SIZE * i; | ||
| 237 | |||
| 238 | /* Read count of correctable errors */ | ||
| 239 | err_vec[i].error_count = reg_val & | ||
| 240 | ECC_NB_ERRORS_MASK; | ||
| 241 | |||
| 242 | /* Update the error locations in error vector */ | ||
| 243 | for (j = 0; j < err_vec[i].error_count; j++) { | ||
| 244 | |||
| 245 | reg_val = elm_read_reg(info, offset); | ||
| 246 | err_vec[i].error_loc[j] = reg_val & | ||
| 247 | ECC_ERROR_LOCATION_MASK; | ||
| 248 | |||
| 249 | /* Update error location register */ | ||
| 250 | offset += 4; | ||
| 251 | } | ||
| 252 | |||
| 253 | errors += err_vec[i].error_count; | ||
| 254 | } else { | ||
| 255 | err_vec[i].error_uncorrectable = true; | ||
| 256 | } | ||
| 257 | |||
| 258 | /* Clearing interrupts for processed error vectors */ | ||
| 259 | elm_write_reg(info, ELM_IRQSTATUS, BIT(i)); | ||
| 260 | |||
| 261 | /* Disable page mode */ | ||
| 262 | elm_configure_page_mode(info, i, false); | ||
| 263 | } | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 267 | /** | ||
| 268 | * elm_decode_bch_error_page - Locate error position | ||
| 269 | * @dev: device pointer | ||
| 270 | * @ecc_calc: calculated ECC bytes from GPMC | ||
| 271 | * @err_vec: elm error vectors | ||
| 272 | * | ||
| 273 | * Called with one or more error reported vectors & vectors with | ||
| 274 | * error reported is updated in err_vec[].error_reported | ||
| 275 | */ | ||
| 276 | void elm_decode_bch_error_page(struct device *dev, u8 *ecc_calc, | ||
| 277 | struct elm_errorvec *err_vec) | ||
| 278 | { | ||
| 279 | struct elm_info *info = dev_get_drvdata(dev); | ||
| 280 | u32 reg_val; | ||
| 281 | |||
| 282 | /* Enable page mode interrupt */ | ||
| 283 | reg_val = elm_read_reg(info, ELM_IRQSTATUS); | ||
| 284 | elm_write_reg(info, ELM_IRQSTATUS, reg_val & INTR_STATUS_PAGE_VALID); | ||
| 285 | elm_write_reg(info, ELM_IRQENABLE, INTR_EN_PAGE_MASK); | ||
| 286 | |||
| 287 | /* Load valid ecc byte to syndrome fragment register */ | ||
| 288 | elm_load_syndrome(info, err_vec, ecc_calc); | ||
| 289 | |||
| 290 | /* Enable syndrome processing for which syndrome fragment is updated */ | ||
| 291 | elm_start_processing(info, err_vec); | ||
| 292 | |||
| 293 | /* Wait for ELM module to finish locating error correction */ | ||
| 294 | wait_for_completion(&info->elm_completion); | ||
| 295 | |||
| 296 | /* Disable page mode interrupt */ | ||
| 297 | reg_val = elm_read_reg(info, ELM_IRQENABLE); | ||
| 298 | elm_write_reg(info, ELM_IRQENABLE, reg_val & ~INTR_EN_PAGE_MASK); | ||
| 299 | elm_error_correction(info, err_vec); | ||
| 300 | } | ||
| 301 | EXPORT_SYMBOL(elm_decode_bch_error_page); | ||
| 302 | |||
| 303 | static irqreturn_t elm_isr(int this_irq, void *dev_id) | ||
| 304 | { | ||
| 305 | u32 reg_val; | ||
| 306 | struct elm_info *info = dev_id; | ||
| 307 | |||
| 308 | reg_val = elm_read_reg(info, ELM_IRQSTATUS); | ||
| 309 | |||
| 310 | /* All error vectors processed */ | ||
| 311 | if (reg_val & INTR_STATUS_PAGE_VALID) { | ||
| 312 | elm_write_reg(info, ELM_IRQSTATUS, | ||
| 313 | reg_val & INTR_STATUS_PAGE_VALID); | ||
| 314 | complete(&info->elm_completion); | ||
| 315 | return IRQ_HANDLED; | ||
| 316 | } | ||
| 317 | |||
| 318 | return IRQ_NONE; | ||
| 319 | } | ||
| 320 | |||
| 321 | static int elm_probe(struct platform_device *pdev) | ||
| 322 | { | ||
| 323 | int ret = 0; | ||
| 324 | struct resource *res, *irq; | ||
| 325 | struct elm_info *info; | ||
| 326 | |||
| 327 | info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); | ||
| 328 | if (!info) { | ||
| 329 | dev_err(&pdev->dev, "failed to allocate memory\n"); | ||
| 330 | return -ENOMEM; | ||
| 331 | } | ||
| 332 | |||
| 333 | info->dev = &pdev->dev; | ||
| 334 | |||
| 335 | irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | ||
| 336 | if (!irq) { | ||
| 337 | dev_err(&pdev->dev, "no irq resource defined\n"); | ||
| 338 | return -ENODEV; | ||
| 339 | } | ||
| 340 | |||
| 341 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 342 | if (!res) { | ||
| 343 | dev_err(&pdev->dev, "no memory resource defined\n"); | ||
| 344 | return -ENODEV; | ||
| 345 | } | ||
| 346 | |||
| 347 | info->elm_base = devm_request_and_ioremap(&pdev->dev, res); | ||
| 348 | if (!info->elm_base) | ||
| 349 | return -EADDRNOTAVAIL; | ||
| 350 | |||
| 351 | ret = devm_request_irq(&pdev->dev, irq->start, elm_isr, 0, | ||
| 352 | pdev->name, info); | ||
| 353 | if (ret) { | ||
| 354 | dev_err(&pdev->dev, "failure requesting irq %i\n", irq->start); | ||
| 355 | return ret; | ||
| 356 | } | ||
| 357 | |||
| 358 | pm_runtime_enable(&pdev->dev); | ||
| 359 | if (pm_runtime_get_sync(&pdev->dev)) { | ||
| 360 | ret = -EINVAL; | ||
| 361 | pm_runtime_disable(&pdev->dev); | ||
| 362 | dev_err(&pdev->dev, "can't enable clock\n"); | ||
| 363 | return ret; | ||
| 364 | } | ||
| 365 | |||
| 366 | init_completion(&info->elm_completion); | ||
| 367 | INIT_LIST_HEAD(&info->list); | ||
| 368 | list_add(&info->list, &elm_devices); | ||
| 369 | platform_set_drvdata(pdev, info); | ||
| 370 | return ret; | ||
| 371 | } | ||
| 372 | |||
| 373 | static int elm_remove(struct platform_device *pdev) | ||
| 374 | { | ||
| 375 | pm_runtime_put_sync(&pdev->dev); | ||
| 376 | pm_runtime_disable(&pdev->dev); | ||
| 377 | platform_set_drvdata(pdev, NULL); | ||
| 378 | return 0; | ||
| 379 | } | ||
| 380 | |||
| 381 | #ifdef CONFIG_OF | ||
| 382 | static const struct of_device_id elm_of_match[] = { | ||
| 383 | { .compatible = "ti,am3352-elm" }, | ||
| 384 | {}, | ||
| 385 | }; | ||
| 386 | MODULE_DEVICE_TABLE(of, elm_of_match); | ||
| 387 | #endif | ||
| 388 | |||
| 389 | static struct platform_driver elm_driver = { | ||
| 390 | .driver = { | ||
| 391 | .name = "elm", | ||
| 392 | .owner = THIS_MODULE, | ||
| 393 | .of_match_table = of_match_ptr(elm_of_match), | ||
| 394 | }, | ||
| 395 | .probe = elm_probe, | ||
| 396 | .remove = elm_remove, | ||
| 397 | }; | ||
| 398 | |||
| 399 | module_platform_driver(elm_driver); | ||
| 400 | |||
| 401 | MODULE_DESCRIPTION("ELM driver for BCH error correction"); | ||
| 402 | MODULE_AUTHOR("Texas Instruments"); | ||
| 403 | MODULE_ALIAS("platform: elm"); | ||
| 404 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 4eeeb2d7f6ea..5b6b0728be21 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c | |||
| @@ -565,6 +565,96 @@ time_out: | |||
| 565 | return ret; | 565 | return ret; |
| 566 | } | 566 | } |
| 567 | 567 | ||
| 568 | static int m25p80_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) | ||
| 569 | { | ||
| 570 | struct m25p *flash = mtd_to_m25p(mtd); | ||
| 571 | uint32_t offset = ofs; | ||
| 572 | uint8_t status_old, status_new; | ||
| 573 | int res = 0; | ||
| 574 | |||
| 575 | mutex_lock(&flash->lock); | ||
| 576 | /* Wait until finished previous command */ | ||
| 577 | if (wait_till_ready(flash)) { | ||
| 578 | res = 1; | ||
| 579 | goto err; | ||
| 580 | } | ||
| 581 | |||
| 582 | status_old = read_sr(flash); | ||
| 583 | |||
| 584 | if (offset < flash->mtd.size-(flash->mtd.size/2)) | ||
| 585 | status_new = status_old | SR_BP2 | SR_BP1 | SR_BP0; | ||
| 586 | else if (offset < flash->mtd.size-(flash->mtd.size/4)) | ||
| 587 | status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1; | ||
| 588 | else if (offset < flash->mtd.size-(flash->mtd.size/8)) | ||
| 589 | status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0; | ||
| 590 | else if (offset < flash->mtd.size-(flash->mtd.size/16)) | ||
| 591 | status_new = (status_old & ~(SR_BP0|SR_BP1)) | SR_BP2; | ||
| 592 | else if (offset < flash->mtd.size-(flash->mtd.size/32)) | ||
| 593 | status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0; | ||
| 594 | else if (offset < flash->mtd.size-(flash->mtd.size/64)) | ||
| 595 | status_new = (status_old & ~(SR_BP2|SR_BP0)) | SR_BP1; | ||
| 596 | else | ||
| 597 | status_new = (status_old & ~(SR_BP2|SR_BP1)) | SR_BP0; | ||
| 598 | |||
| 599 | /* Only modify protection if it will not unlock other areas */ | ||
| 600 | if ((status_new&(SR_BP2|SR_BP1|SR_BP0)) > | ||
| 601 | (status_old&(SR_BP2|SR_BP1|SR_BP0))) { | ||
| 602 | write_enable(flash); | ||
| 603 | if (write_sr(flash, status_new) < 0) { | ||
| 604 | res = 1; | ||
| 605 | goto err; | ||
| 606 | } | ||
| 607 | } | ||
| 608 | |||
| 609 | err: mutex_unlock(&flash->lock); | ||
| 610 | return res; | ||
| 611 | } | ||
| 612 | |||
| 613 | static int m25p80_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) | ||
| 614 | { | ||
| 615 | struct m25p *flash = mtd_to_m25p(mtd); | ||
| 616 | uint32_t offset = ofs; | ||
| 617 | uint8_t status_old, status_new; | ||
| 618 | int res = 0; | ||
| 619 | |||
| 620 | mutex_lock(&flash->lock); | ||
| 621 | /* Wait until finished previous command */ | ||
| 622 | if (wait_till_ready(flash)) { | ||
| 623 | res = 1; | ||
| 624 | goto err; | ||
| 625 | } | ||
| 626 | |||
| 627 | status_old = read_sr(flash); | ||
| 628 | |||
| 629 | if (offset+len > flash->mtd.size-(flash->mtd.size/64)) | ||
| 630 | status_new = status_old & ~(SR_BP2|SR_BP1|SR_BP0); | ||
| 631 | else if (offset+len > flash->mtd.size-(flash->mtd.size/32)) | ||
| 632 | status_new = (status_old & ~(SR_BP2|SR_BP1)) | SR_BP0; | ||
| 633 | else if (offset+len > flash->mtd.size-(flash->mtd.size/16)) | ||
| 634 | status_new = (status_old & ~(SR_BP2|SR_BP0)) | SR_BP1; | ||
| 635 | else if (offset+len > flash->mtd.size-(flash->mtd.size/8)) | ||
| 636 | status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0; | ||
| 637 | else if (offset+len > flash->mtd.size-(flash->mtd.size/4)) | ||
| 638 | status_new = (status_old & ~(SR_BP0|SR_BP1)) | SR_BP2; | ||
| 639 | else if (offset+len > flash->mtd.size-(flash->mtd.size/2)) | ||
| 640 | status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0; | ||
| 641 | else | ||
| 642 | status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1; | ||
| 643 | |||
| 644 | /* Only modify protection if it will not lock other areas */ | ||
| 645 | if ((status_new&(SR_BP2|SR_BP1|SR_BP0)) < | ||
| 646 | (status_old&(SR_BP2|SR_BP1|SR_BP0))) { | ||
| 647 | write_enable(flash); | ||
| 648 | if (write_sr(flash, status_new) < 0) { | ||
| 649 | res = 1; | ||
| 650 | goto err; | ||
| 651 | } | ||
| 652 | } | ||
| 653 | |||
| 654 | err: mutex_unlock(&flash->lock); | ||
| 655 | return res; | ||
| 656 | } | ||
| 657 | |||
| 568 | /****************************************************************************/ | 658 | /****************************************************************************/ |
| 569 | 659 | ||
| 570 | /* | 660 | /* |
| @@ -642,6 +732,10 @@ static const struct spi_device_id m25p_ids[] = { | |||
| 642 | /* Everspin */ | 732 | /* Everspin */ |
| 643 | { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2) }, | 733 | { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2) }, |
| 644 | 734 | ||
| 735 | /* GigaDevice */ | ||
| 736 | { "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) }, | ||
| 737 | { "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SECT_4K) }, | ||
| 738 | |||
| 645 | /* Intel/Numonyx -- xxxs33b */ | 739 | /* Intel/Numonyx -- xxxs33b */ |
| 646 | { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) }, | 740 | { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) }, |
| 647 | { "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) }, | 741 | { "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) }, |
| @@ -899,6 +993,12 @@ static int m25p_probe(struct spi_device *spi) | |||
| 899 | flash->mtd._erase = m25p80_erase; | 993 | flash->mtd._erase = m25p80_erase; |
| 900 | flash->mtd._read = m25p80_read; | 994 | flash->mtd._read = m25p80_read; |
| 901 | 995 | ||
| 996 | /* flash protection support for STmicro chips */ | ||
| 997 | if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) { | ||
| 998 | flash->mtd._lock = m25p80_lock; | ||
| 999 | flash->mtd._unlock = m25p80_unlock; | ||
| 1000 | } | ||
| 1001 | |||
| 902 | /* sst flash chips use AAI word program */ | 1002 | /* sst flash chips use AAI word program */ |
| 903 | if (JEDEC_MFR(info->jedec_id) == CFI_MFR_SST) | 1003 | if (JEDEC_MFR(info->jedec_id) == CFI_MFR_SST) |
| 904 | flash->mtd._write = sst_write; | 1004 | flash->mtd._write = sst_write; |
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 62ba82c396c2..3ed17c4d4358 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig | |||
| @@ -429,7 +429,7 @@ config MTD_GPIO_ADDR | |||
| 429 | 429 | ||
| 430 | config MTD_UCLINUX | 430 | config MTD_UCLINUX |
| 431 | bool "Generic uClinux RAM/ROM filesystem support" | 431 | bool "Generic uClinux RAM/ROM filesystem support" |
| 432 | depends on MTD_RAM=y && (!MMU || COLDFIRE) | 432 | depends on (MTD_RAM=y || MTD_ROM=y) && (!MMU || COLDFIRE) |
| 433 | help | 433 | help |
| 434 | Map driver to support image based filesystems for uClinux. | 434 | Map driver to support image based filesystems for uClinux. |
| 435 | 435 | ||
diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index 7901d72c9242..363939dfad05 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c | |||
| @@ -68,9 +68,6 @@ static int of_flash_remove(struct platform_device *dev) | |||
| 68 | kfree(info->list[i].res); | 68 | kfree(info->list[i].res); |
| 69 | } | 69 | } |
| 70 | } | 70 | } |
| 71 | |||
| 72 | kfree(info); | ||
| 73 | |||
| 74 | return 0; | 71 | return 0; |
| 75 | } | 72 | } |
| 76 | 73 | ||
| @@ -199,8 +196,9 @@ static int of_flash_probe(struct platform_device *dev) | |||
| 199 | map_indirect = of_property_read_bool(dp, "no-unaligned-direct-access"); | 196 | map_indirect = of_property_read_bool(dp, "no-unaligned-direct-access"); |
| 200 | 197 | ||
| 201 | err = -ENOMEM; | 198 | err = -ENOMEM; |
| 202 | info = kzalloc(sizeof(struct of_flash) + | 199 | info = devm_kzalloc(&dev->dev, |
| 203 | sizeof(struct of_flash_list) * count, GFP_KERNEL); | 200 | sizeof(struct of_flash) + |
| 201 | sizeof(struct of_flash_list) * count, GFP_KERNEL); | ||
| 204 | if (!info) | 202 | if (!info) |
| 205 | goto err_flash_remove; | 203 | goto err_flash_remove; |
| 206 | 204 | ||
| @@ -241,6 +239,7 @@ static int of_flash_probe(struct platform_device *dev) | |||
| 241 | info->list[i].map.phys = res.start; | 239 | info->list[i].map.phys = res.start; |
| 242 | info->list[i].map.size = res_size; | 240 | info->list[i].map.size = res_size; |
| 243 | info->list[i].map.bankwidth = be32_to_cpup(width); | 241 | info->list[i].map.bankwidth = be32_to_cpup(width); |
| 242 | info->list[i].map.device_node = dp; | ||
| 244 | 243 | ||
| 245 | err = -ENOMEM; | 244 | err = -ENOMEM; |
| 246 | info->list[i].map.virt = ioremap(info->list[i].map.phys, | 245 | info->list[i].map.virt = ioremap(info->list[i].map.phys, |
diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c index 299bf88a6f41..c1af83db5202 100644 --- a/drivers/mtd/maps/uclinux.c +++ b/drivers/mtd/maps/uclinux.c | |||
| @@ -23,12 +23,26 @@ | |||
| 23 | 23 | ||
| 24 | /****************************************************************************/ | 24 | /****************************************************************************/ |
| 25 | 25 | ||
| 26 | #ifdef CONFIG_MTD_ROM | ||
| 27 | #define MAP_NAME "rom" | ||
| 28 | #else | ||
| 29 | #define MAP_NAME "ram" | ||
| 30 | #endif | ||
| 31 | |||
| 32 | /* | ||
| 33 | * Blackfin uses uclinux_ram_map during startup, so it must not be static. | ||
| 34 | * Provide a dummy declaration to make sparse happy. | ||
| 35 | */ | ||
| 36 | extern struct map_info uclinux_ram_map; | ||
| 37 | |||
| 26 | struct map_info uclinux_ram_map = { | 38 | struct map_info uclinux_ram_map = { |
| 27 | .name = "RAM", | 39 | .name = MAP_NAME, |
| 28 | .phys = (unsigned long)__bss_stop, | ||
| 29 | .size = 0, | 40 | .size = 0, |
| 30 | }; | 41 | }; |
| 31 | 42 | ||
| 43 | static unsigned long physaddr = -1; | ||
| 44 | module_param(physaddr, ulong, S_IRUGO); | ||
| 45 | |||
| 32 | static struct mtd_info *uclinux_ram_mtdinfo; | 46 | static struct mtd_info *uclinux_ram_mtdinfo; |
| 33 | 47 | ||
| 34 | /****************************************************************************/ | 48 | /****************************************************************************/ |
| @@ -60,11 +74,17 @@ static int __init uclinux_mtd_init(void) | |||
| 60 | struct map_info *mapp; | 74 | struct map_info *mapp; |
| 61 | 75 | ||
| 62 | mapp = &uclinux_ram_map; | 76 | mapp = &uclinux_ram_map; |
| 77 | |||
| 78 | if (physaddr == -1) | ||
| 79 | mapp->phys = (resource_size_t)__bss_stop; | ||
| 80 | else | ||
| 81 | mapp->phys = physaddr; | ||
| 82 | |||
| 63 | if (!mapp->size) | 83 | if (!mapp->size) |
| 64 | mapp->size = PAGE_ALIGN(ntohl(*((unsigned long *)(mapp->phys + 8)))); | 84 | mapp->size = PAGE_ALIGN(ntohl(*((unsigned long *)(mapp->phys + 8)))); |
| 65 | mapp->bankwidth = 4; | 85 | mapp->bankwidth = 4; |
| 66 | 86 | ||
| 67 | printk("uclinux[mtd]: RAM probe address=0x%x size=0x%x\n", | 87 | printk("uclinux[mtd]: probe address=0x%x size=0x%x\n", |
| 68 | (int) mapp->phys, (int) mapp->size); | 88 | (int) mapp->phys, (int) mapp->size); |
| 69 | 89 | ||
| 70 | /* | 90 | /* |
| @@ -82,7 +102,7 @@ static int __init uclinux_mtd_init(void) | |||
| 82 | 102 | ||
| 83 | simple_map_init(mapp); | 103 | simple_map_init(mapp); |
| 84 | 104 | ||
| 85 | mtd = do_map_probe("map_ram", mapp); | 105 | mtd = do_map_probe("map_" MAP_NAME, mapp); |
| 86 | if (!mtd) { | 106 | if (!mtd) { |
| 87 | printk("uclinux[mtd]: failed to find a mapping?\n"); | 107 | printk("uclinux[mtd]: failed to find a mapping?\n"); |
| 88 | return(-ENXIO); | 108 | return(-ENXIO); |
| @@ -118,6 +138,6 @@ module_exit(uclinux_mtd_cleanup); | |||
| 118 | 138 | ||
| 119 | MODULE_LICENSE("GPL"); | 139 | MODULE_LICENSE("GPL"); |
| 120 | MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>"); | 140 | MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>"); |
| 121 | MODULE_DESCRIPTION("Generic RAM based MTD for uClinux"); | 141 | MODULE_DESCRIPTION("Generic MTD for uClinux"); |
| 122 | 142 | ||
| 123 | /****************************************************************************/ | 143 | /****************************************************************************/ |
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index c516a9408087..ffcbcca2fd2d 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c | |||
| @@ -101,6 +101,8 @@ struct atmel_nand_host { | |||
| 101 | u8 pmecc_corr_cap; | 101 | u8 pmecc_corr_cap; |
| 102 | u16 pmecc_sector_size; | 102 | u16 pmecc_sector_size; |
| 103 | u32 pmecc_lookup_table_offset; | 103 | u32 pmecc_lookup_table_offset; |
| 104 | u32 pmecc_lookup_table_offset_512; | ||
| 105 | u32 pmecc_lookup_table_offset_1024; | ||
| 104 | 106 | ||
| 105 | int pmecc_bytes_per_sector; | 107 | int pmecc_bytes_per_sector; |
| 106 | int pmecc_sector_number; | 108 | int pmecc_sector_number; |
| @@ -908,6 +910,84 @@ static void atmel_pmecc_core_init(struct mtd_info *mtd) | |||
| 908 | pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE); | 910 | pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE); |
| 909 | } | 911 | } |
| 910 | 912 | ||
| 913 | /* | ||
| 914 | * Get ECC requirement in ONFI parameters, returns -1 if ONFI | ||
| 915 | * parameters is not supported. | ||
| 916 | * return 0 if success to get the ECC requirement. | ||
| 917 | */ | ||
| 918 | static int get_onfi_ecc_param(struct nand_chip *chip, | ||
| 919 | int *ecc_bits, int *sector_size) | ||
| 920 | { | ||
| 921 | *ecc_bits = *sector_size = 0; | ||
| 922 | |||
| 923 | if (chip->onfi_params.ecc_bits == 0xff) | ||
| 924 | /* TODO: the sector_size and ecc_bits need to be find in | ||
| 925 | * extended ecc parameter, currently we don't support it. | ||
| 926 | */ | ||
| 927 | return -1; | ||
| 928 | |||
| 929 | *ecc_bits = chip->onfi_params.ecc_bits; | ||
| 930 | |||
| 931 | /* The default sector size (ecc codeword size) is 512 */ | ||
| 932 | *sector_size = 512; | ||
| 933 | |||
| 934 | return 0; | ||
| 935 | } | ||
| 936 | |||
| 937 | /* | ||
| 938 | * Get ecc requirement from ONFI parameters ecc requirement. | ||
| 939 | * If pmecc-cap, pmecc-sector-size in DTS are not specified, this function | ||
| 940 | * will set them according to ONFI ecc requirement. Otherwise, use the | ||
| 941 | * value in DTS file. | ||
| 942 | * return 0 if success. otherwise return error code. | ||
| 943 | */ | ||
| 944 | static int pmecc_choose_ecc(struct atmel_nand_host *host, | ||
| 945 | int *cap, int *sector_size) | ||
| 946 | { | ||
| 947 | /* Get ECC requirement from ONFI parameters */ | ||
| 948 | *cap = *sector_size = 0; | ||
| 949 | if (host->nand_chip.onfi_version) { | ||
| 950 | if (!get_onfi_ecc_param(&host->nand_chip, cap, sector_size)) | ||
| 951 | dev_info(host->dev, "ONFI params, minimum required ECC: %d bits in %d bytes\n", | ||
| 952 | *cap, *sector_size); | ||
| 953 | else | ||
| 954 | dev_info(host->dev, "NAND chip ECC reqirement is in Extended ONFI parameter, we don't support yet.\n"); | ||
| 955 | } else { | ||
| 956 | dev_info(host->dev, "NAND chip is not ONFI compliant, assume ecc_bits is 2 in 512 bytes"); | ||
| 957 | } | ||
| 958 | if (*cap == 0 && *sector_size == 0) { | ||
| 959 | *cap = 2; | ||
| 960 | *sector_size = 512; | ||
| 961 | } | ||
| 962 | |||
| 963 | /* If dts file doesn't specify then use the one in ONFI parameters */ | ||
| 964 | if (host->pmecc_corr_cap == 0) { | ||
| 965 | /* use the most fitable ecc bits (the near bigger one ) */ | ||
| 966 | if (*cap <= 2) | ||
| 967 | host->pmecc_corr_cap = 2; | ||
| 968 | else if (*cap <= 4) | ||
| 969 | host->pmecc_corr_cap = 4; | ||
| 970 | else if (*cap < 8) | ||
| 971 | host->pmecc_corr_cap = 8; | ||
| 972 | else if (*cap < 12) | ||
| 973 | host->pmecc_corr_cap = 12; | ||
| 974 | else if (*cap < 24) | ||
| 975 | host->pmecc_corr_cap = 24; | ||
| 976 | else | ||
| 977 | return -EINVAL; | ||
| 978 | } | ||
| 979 | if (host->pmecc_sector_size == 0) { | ||
| 980 | /* use the most fitable sector size (the near smaller one ) */ | ||
| 981 | if (*sector_size >= 1024) | ||
| 982 | host->pmecc_sector_size = 1024; | ||
| 983 | else if (*sector_size >= 512) | ||
| 984 | host->pmecc_sector_size = 512; | ||
| 985 | else | ||
| 986 | return -EINVAL; | ||
| 987 | } | ||
| 988 | return 0; | ||
| 989 | } | ||
| 990 | |||
| 911 | static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev, | 991 | static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev, |
| 912 | struct atmel_nand_host *host) | 992 | struct atmel_nand_host *host) |
| 913 | { | 993 | { |
| @@ -916,8 +996,22 @@ static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev, | |||
| 916 | struct resource *regs, *regs_pmerr, *regs_rom; | 996 | struct resource *regs, *regs_pmerr, *regs_rom; |
| 917 | int cap, sector_size, err_no; | 997 | int cap, sector_size, err_no; |
| 918 | 998 | ||
| 999 | err_no = pmecc_choose_ecc(host, &cap, §or_size); | ||
| 1000 | if (err_no) { | ||
| 1001 | dev_err(host->dev, "The NAND flash's ECC requirement are not support!"); | ||
| 1002 | return err_no; | ||
| 1003 | } | ||
| 1004 | |||
| 1005 | if (cap != host->pmecc_corr_cap || | ||
| 1006 | sector_size != host->pmecc_sector_size) | ||
| 1007 | dev_info(host->dev, "WARNING: Be Caution! Using different PMECC parameters from Nand ONFI ECC reqirement.\n"); | ||
| 1008 | |||
| 919 | cap = host->pmecc_corr_cap; | 1009 | cap = host->pmecc_corr_cap; |
| 920 | sector_size = host->pmecc_sector_size; | 1010 | sector_size = host->pmecc_sector_size; |
| 1011 | host->pmecc_lookup_table_offset = (sector_size == 512) ? | ||
| 1012 | host->pmecc_lookup_table_offset_512 : | ||
| 1013 | host->pmecc_lookup_table_offset_1024; | ||
| 1014 | |||
| 921 | dev_info(host->dev, "Initialize PMECC params, cap: %d, sector: %d\n", | 1015 | dev_info(host->dev, "Initialize PMECC params, cap: %d, sector: %d\n", |
| 922 | cap, sector_size); | 1016 | cap, sector_size); |
| 923 | 1017 | ||
| @@ -1215,7 +1309,7 @@ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode) | |||
| 1215 | static int atmel_of_init_port(struct atmel_nand_host *host, | 1309 | static int atmel_of_init_port(struct atmel_nand_host *host, |
| 1216 | struct device_node *np) | 1310 | struct device_node *np) |
| 1217 | { | 1311 | { |
| 1218 | u32 val, table_offset; | 1312 | u32 val; |
| 1219 | u32 offset[2]; | 1313 | u32 offset[2]; |
| 1220 | int ecc_mode; | 1314 | int ecc_mode; |
| 1221 | struct atmel_nand_data *board = &host->board; | 1315 | struct atmel_nand_data *board = &host->board; |
| @@ -1259,42 +1353,41 @@ static int atmel_of_init_port(struct atmel_nand_host *host, | |||
| 1259 | 1353 | ||
| 1260 | /* use PMECC, get correction capability, sector size and lookup | 1354 | /* use PMECC, get correction capability, sector size and lookup |
| 1261 | * table offset. | 1355 | * table offset. |
| 1356 | * If correction bits and sector size are not specified, then find | ||
| 1357 | * them from NAND ONFI parameters. | ||
| 1262 | */ | 1358 | */ |
| 1263 | if (of_property_read_u32(np, "atmel,pmecc-cap", &val) != 0) { | 1359 | if (of_property_read_u32(np, "atmel,pmecc-cap", &val) == 0) { |
| 1264 | dev_err(host->dev, "Cannot decide PMECC Capability\n"); | 1360 | if ((val != 2) && (val != 4) && (val != 8) && (val != 12) && |
| 1265 | return -EINVAL; | 1361 | (val != 24)) { |
| 1266 | } else if ((val != 2) && (val != 4) && (val != 8) && (val != 12) && | 1362 | dev_err(host->dev, |
| 1267 | (val != 24)) { | 1363 | "Unsupported PMECC correction capability: %d; should be 2, 4, 8, 12 or 24\n", |
| 1268 | dev_err(host->dev, | 1364 | val); |
| 1269 | "Unsupported PMECC correction capability: %d; should be 2, 4, 8, 12 or 24\n", | 1365 | return -EINVAL; |
| 1270 | val); | 1366 | } |
| 1271 | return -EINVAL; | 1367 | host->pmecc_corr_cap = (u8)val; |
| 1272 | } | 1368 | } |
| 1273 | host->pmecc_corr_cap = (u8)val; | ||
| 1274 | 1369 | ||
| 1275 | if (of_property_read_u32(np, "atmel,pmecc-sector-size", &val) != 0) { | 1370 | if (of_property_read_u32(np, "atmel,pmecc-sector-size", &val) == 0) { |
| 1276 | dev_err(host->dev, "Cannot decide PMECC Sector Size\n"); | 1371 | if ((val != 512) && (val != 1024)) { |
| 1277 | return -EINVAL; | 1372 | dev_err(host->dev, |
| 1278 | } else if ((val != 512) && (val != 1024)) { | 1373 | "Unsupported PMECC sector size: %d; should be 512 or 1024 bytes\n", |
| 1279 | dev_err(host->dev, | 1374 | val); |
| 1280 | "Unsupported PMECC sector size: %d; should be 512 or 1024 bytes\n", | 1375 | return -EINVAL; |
| 1281 | val); | 1376 | } |
| 1282 | return -EINVAL; | 1377 | host->pmecc_sector_size = (u16)val; |
| 1283 | } | 1378 | } |
| 1284 | host->pmecc_sector_size = (u16)val; | ||
| 1285 | 1379 | ||
| 1286 | if (of_property_read_u32_array(np, "atmel,pmecc-lookup-table-offset", | 1380 | if (of_property_read_u32_array(np, "atmel,pmecc-lookup-table-offset", |
| 1287 | offset, 2) != 0) { | 1381 | offset, 2) != 0) { |
| 1288 | dev_err(host->dev, "Cannot get PMECC lookup table offset\n"); | 1382 | dev_err(host->dev, "Cannot get PMECC lookup table offset\n"); |
| 1289 | return -EINVAL; | 1383 | return -EINVAL; |
| 1290 | } | 1384 | } |
| 1291 | table_offset = host->pmecc_sector_size == 512 ? offset[0] : offset[1]; | 1385 | if (!offset[0] && !offset[1]) { |
| 1292 | |||
| 1293 | if (!table_offset) { | ||
| 1294 | dev_err(host->dev, "Invalid PMECC lookup table offset\n"); | 1386 | dev_err(host->dev, "Invalid PMECC lookup table offset\n"); |
| 1295 | return -EINVAL; | 1387 | return -EINVAL; |
| 1296 | } | 1388 | } |
| 1297 | host->pmecc_lookup_table_offset = table_offset; | 1389 | host->pmecc_lookup_table_offset_512 = offset[0]; |
| 1390 | host->pmecc_lookup_table_offset_1024 = offset[1]; | ||
| 1298 | 1391 | ||
| 1299 | return 0; | 1392 | return 0; |
| 1300 | } | 1393 | } |
diff --git a/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h b/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h index 0bdb2ce4da75..c005a62330b1 100644 --- a/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h +++ b/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h | |||
| @@ -1,6 +1,10 @@ | |||
| 1 | #ifndef __BCM47XXNFLASH_H | 1 | #ifndef __BCM47XXNFLASH_H |
| 2 | #define __BCM47XXNFLASH_H | 2 | #define __BCM47XXNFLASH_H |
| 3 | 3 | ||
| 4 | #ifndef pr_fmt | ||
| 5 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 6 | #endif | ||
| 7 | |||
| 4 | #include <linux/mtd/mtd.h> | 8 | #include <linux/mtd/mtd.h> |
| 5 | #include <linux/mtd/nand.h> | 9 | #include <linux/mtd/nand.h> |
| 6 | 10 | ||
diff --git a/drivers/mtd/nand/bcm47xxnflash/main.c b/drivers/mtd/nand/bcm47xxnflash/main.c index 8363a9a5fa3f..7bae569fdc79 100644 --- a/drivers/mtd/nand/bcm47xxnflash/main.c +++ b/drivers/mtd/nand/bcm47xxnflash/main.c | |||
| @@ -9,14 +9,14 @@ | |||
| 9 | * | 9 | * |
| 10 | */ | 10 | */ |
| 11 | 11 | ||
| 12 | #include "bcm47xxnflash.h" | ||
| 13 | |||
| 12 | #include <linux/module.h> | 14 | #include <linux/module.h> |
| 13 | #include <linux/kernel.h> | 15 | #include <linux/kernel.h> |
| 14 | #include <linux/slab.h> | 16 | #include <linux/slab.h> |
| 15 | #include <linux/platform_device.h> | 17 | #include <linux/platform_device.h> |
| 16 | #include <linux/bcma/bcma.h> | 18 | #include <linux/bcma/bcma.h> |
| 17 | 19 | ||
| 18 | #include "bcm47xxnflash.h" | ||
| 19 | |||
| 20 | MODULE_DESCRIPTION("NAND flash driver for BCMA bus"); | 20 | MODULE_DESCRIPTION("NAND flash driver for BCMA bus"); |
| 21 | MODULE_LICENSE("GPL"); | 21 | MODULE_LICENSE("GPL"); |
| 22 | MODULE_AUTHOR("Rafał Miłecki"); | 22 | MODULE_AUTHOR("Rafał Miłecki"); |
| @@ -77,6 +77,7 @@ static int bcm47xxnflash_remove(struct platform_device *pdev) | |||
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | static struct platform_driver bcm47xxnflash_driver = { | 79 | static struct platform_driver bcm47xxnflash_driver = { |
| 80 | .probe = bcm47xxnflash_probe, | ||
| 80 | .remove = bcm47xxnflash_remove, | 81 | .remove = bcm47xxnflash_remove, |
| 81 | .driver = { | 82 | .driver = { |
| 82 | .name = "bcma_nflash", | 83 | .name = "bcma_nflash", |
| @@ -88,13 +89,10 @@ static int __init bcm47xxnflash_init(void) | |||
| 88 | { | 89 | { |
| 89 | int err; | 90 | int err; |
| 90 | 91 | ||
| 91 | /* | 92 | err = platform_driver_register(&bcm47xxnflash_driver); |
| 92 | * Platform device "bcma_nflash" exists on SoCs and is registered very | ||
| 93 | * early, it won't be added during runtime (use platform_driver_probe). | ||
| 94 | */ | ||
| 95 | err = platform_driver_probe(&bcm47xxnflash_driver, bcm47xxnflash_probe); | ||
| 96 | if (err) | 93 | if (err) |
| 97 | pr_err("Failed to register serial flash driver: %d\n", err); | 94 | pr_err("Failed to register bcm47xx nand flash driver: %d\n", |
| 95 | err); | ||
| 98 | 96 | ||
| 99 | return err; | 97 | return err; |
| 100 | } | 98 | } |
diff --git a/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c b/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c index 595de4012e71..b2ab373c9eef 100644 --- a/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c +++ b/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c | |||
| @@ -9,13 +9,13 @@ | |||
| 9 | * | 9 | * |
| 10 | */ | 10 | */ |
| 11 | 11 | ||
| 12 | #include "bcm47xxnflash.h" | ||
| 13 | |||
| 12 | #include <linux/module.h> | 14 | #include <linux/module.h> |
| 13 | #include <linux/kernel.h> | 15 | #include <linux/kernel.h> |
| 14 | #include <linux/slab.h> | 16 | #include <linux/slab.h> |
| 15 | #include <linux/bcma/bcma.h> | 17 | #include <linux/bcma/bcma.h> |
| 16 | 18 | ||
| 17 | #include "bcm47xxnflash.h" | ||
| 18 | |||
| 19 | /* Broadcom uses 1'000'000 but it seems to be too many. Tests on WNDR4500 has | 19 | /* Broadcom uses 1'000'000 but it seems to be too many. Tests on WNDR4500 has |
| 20 | * shown ~1000 retries as maxiumum. */ | 20 | * shown ~1000 retries as maxiumum. */ |
| 21 | #define NFLASH_READY_RETRIES 10000 | 21 | #define NFLASH_READY_RETRIES 10000 |
diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index feae55c7b880..94e17af8e450 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c | |||
| @@ -606,7 +606,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev) | |||
| 606 | if (pdev->id < 0 || pdev->id > 3) | 606 | if (pdev->id < 0 || pdev->id > 3) |
| 607 | return -ENODEV; | 607 | return -ENODEV; |
| 608 | 608 | ||
| 609 | info = kzalloc(sizeof(*info), GFP_KERNEL); | 609 | info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); |
| 610 | if (!info) { | 610 | if (!info) { |
| 611 | dev_err(&pdev->dev, "unable to allocate memory\n"); | 611 | dev_err(&pdev->dev, "unable to allocate memory\n"); |
| 612 | ret = -ENOMEM; | 612 | ret = -ENOMEM; |
| @@ -623,11 +623,11 @@ static int __init nand_davinci_probe(struct platform_device *pdev) | |||
| 623 | goto err_nomem; | 623 | goto err_nomem; |
| 624 | } | 624 | } |
| 625 | 625 | ||
| 626 | vaddr = ioremap(res1->start, resource_size(res1)); | 626 | vaddr = devm_request_and_ioremap(&pdev->dev, res1); |
| 627 | base = ioremap(res2->start, resource_size(res2)); | 627 | base = devm_request_and_ioremap(&pdev->dev, res2); |
| 628 | if (!vaddr || !base) { | 628 | if (!vaddr || !base) { |
| 629 | dev_err(&pdev->dev, "ioremap failed\n"); | 629 | dev_err(&pdev->dev, "ioremap failed\n"); |
| 630 | ret = -EINVAL; | 630 | ret = -EADDRNOTAVAIL; |
| 631 | goto err_ioremap; | 631 | goto err_ioremap; |
| 632 | } | 632 | } |
| 633 | 633 | ||
| @@ -717,7 +717,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev) | |||
| 717 | } | 717 | } |
| 718 | info->chip.ecc.mode = ecc_mode; | 718 | info->chip.ecc.mode = ecc_mode; |
| 719 | 719 | ||
| 720 | info->clk = clk_get(&pdev->dev, "aemif"); | 720 | info->clk = devm_clk_get(&pdev->dev, "aemif"); |
| 721 | if (IS_ERR(info->clk)) { | 721 | if (IS_ERR(info->clk)) { |
| 722 | ret = PTR_ERR(info->clk); | 722 | ret = PTR_ERR(info->clk); |
| 723 | dev_dbg(&pdev->dev, "unable to get AEMIF clock, err %d\n", ret); | 723 | dev_dbg(&pdev->dev, "unable to get AEMIF clock, err %d\n", ret); |
| @@ -845,8 +845,6 @@ err_timing: | |||
| 845 | clk_disable_unprepare(info->clk); | 845 | clk_disable_unprepare(info->clk); |
| 846 | 846 | ||
| 847 | err_clk_enable: | 847 | err_clk_enable: |
| 848 | clk_put(info->clk); | ||
| 849 | |||
| 850 | spin_lock_irq(&davinci_nand_lock); | 848 | spin_lock_irq(&davinci_nand_lock); |
| 851 | if (ecc_mode == NAND_ECC_HW_SYNDROME) | 849 | if (ecc_mode == NAND_ECC_HW_SYNDROME) |
| 852 | ecc4_busy = false; | 850 | ecc4_busy = false; |
| @@ -855,13 +853,7 @@ err_clk_enable: | |||
| 855 | err_ecc: | 853 | err_ecc: |
| 856 | err_clk: | 854 | err_clk: |
| 857 | err_ioremap: | 855 | err_ioremap: |
| 858 | if (base) | ||
| 859 | iounmap(base); | ||
| 860 | if (vaddr) | ||
| 861 | iounmap(vaddr); | ||
| 862 | |||
| 863 | err_nomem: | 856 | err_nomem: |
| 864 | kfree(info); | ||
| 865 | return ret; | 857 | return ret; |
| 866 | } | 858 | } |
| 867 | 859 | ||
| @@ -874,15 +866,9 @@ static int __exit nand_davinci_remove(struct platform_device *pdev) | |||
| 874 | ecc4_busy = false; | 866 | ecc4_busy = false; |
| 875 | spin_unlock_irq(&davinci_nand_lock); | 867 | spin_unlock_irq(&davinci_nand_lock); |
| 876 | 868 | ||
| 877 | iounmap(info->base); | ||
| 878 | iounmap(info->vaddr); | ||
| 879 | |||
| 880 | nand_release(&info->mtd); | 869 | nand_release(&info->mtd); |
| 881 | 870 | ||
| 882 | clk_disable_unprepare(info->clk); | 871 | clk_disable_unprepare(info->clk); |
| 883 | clk_put(info->clk); | ||
| 884 | |||
| 885 | kfree(info); | ||
| 886 | 872 | ||
| 887 | return 0; | 873 | return 0; |
| 888 | } | 874 | } |
diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index ad6222627fed..f1f7f12ab501 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c | |||
| @@ -176,8 +176,8 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) | |||
| 176 | 176 | ||
| 177 | ifc_nand_ctrl->page = page_addr; | 177 | ifc_nand_ctrl->page = page_addr; |
| 178 | /* Program ROW0/COL0 */ | 178 | /* Program ROW0/COL0 */ |
| 179 | out_be32(&ifc->ifc_nand.row0, page_addr); | 179 | iowrite32be(page_addr, &ifc->ifc_nand.row0); |
| 180 | out_be32(&ifc->ifc_nand.col0, (oob ? IFC_NAND_COL_MS : 0) | column); | 180 | iowrite32be((oob ? IFC_NAND_COL_MS : 0) | column, &ifc->ifc_nand.col0); |
| 181 | 181 | ||
| 182 | buf_num = page_addr & priv->bufnum_mask; | 182 | buf_num = page_addr & priv->bufnum_mask; |
| 183 | 183 | ||
| @@ -239,18 +239,19 @@ static void fsl_ifc_run_command(struct mtd_info *mtd) | |||
| 239 | int i; | 239 | int i; |
| 240 | 240 | ||
| 241 | /* set the chip select for NAND Transaction */ | 241 | /* set the chip select for NAND Transaction */ |
| 242 | out_be32(&ifc->ifc_nand.nand_csel, priv->bank << IFC_NAND_CSEL_SHIFT); | 242 | iowrite32be(priv->bank << IFC_NAND_CSEL_SHIFT, |
| 243 | &ifc->ifc_nand.nand_csel); | ||
| 243 | 244 | ||
| 244 | dev_vdbg(priv->dev, | 245 | dev_vdbg(priv->dev, |
| 245 | "%s: fir0=%08x fcr0=%08x\n", | 246 | "%s: fir0=%08x fcr0=%08x\n", |
| 246 | __func__, | 247 | __func__, |
| 247 | in_be32(&ifc->ifc_nand.nand_fir0), | 248 | ioread32be(&ifc->ifc_nand.nand_fir0), |
| 248 | in_be32(&ifc->ifc_nand.nand_fcr0)); | 249 | ioread32be(&ifc->ifc_nand.nand_fcr0)); |
| 249 | 250 | ||
| 250 | ctrl->nand_stat = 0; | 251 | ctrl->nand_stat = 0; |
| 251 | 252 | ||
| 252 | /* start read/write seq */ | 253 | /* start read/write seq */ |
| 253 | out_be32(&ifc->ifc_nand.nandseq_strt, IFC_NAND_SEQ_STRT_FIR_STRT); | 254 | iowrite32be(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt); |
| 254 | 255 | ||
| 255 | /* wait for command complete flag or timeout */ | 256 | /* wait for command complete flag or timeout */ |
| 256 | wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat, | 257 | wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat, |
| @@ -273,7 +274,7 @@ static void fsl_ifc_run_command(struct mtd_info *mtd) | |||
| 273 | int sector_end = sector + chip->ecc.steps - 1; | 274 | int sector_end = sector + chip->ecc.steps - 1; |
| 274 | 275 | ||
| 275 | for (i = sector / 4; i <= sector_end / 4; i++) | 276 | for (i = sector / 4; i <= sector_end / 4; i++) |
| 276 | eccstat[i] = in_be32(&ifc->ifc_nand.nand_eccstat[i]); | 277 | eccstat[i] = ioread32be(&ifc->ifc_nand.nand_eccstat[i]); |
| 277 | 278 | ||
| 278 | for (i = sector; i <= sector_end; i++) { | 279 | for (i = sector; i <= sector_end; i++) { |
| 279 | errors = check_read_ecc(mtd, ctrl, eccstat, i); | 280 | errors = check_read_ecc(mtd, ctrl, eccstat, i); |
| @@ -313,31 +314,33 @@ static void fsl_ifc_do_read(struct nand_chip *chip, | |||
| 313 | 314 | ||
| 314 | /* Program FIR/IFC_NAND_FCR0 for Small/Large page */ | 315 | /* Program FIR/IFC_NAND_FCR0 for Small/Large page */ |
| 315 | if (mtd->writesize > 512) { | 316 | if (mtd->writesize > 512) { |
| 316 | out_be32(&ifc->ifc_nand.nand_fir0, | 317 | iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | |
| 317 | (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | | 318 | (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | |
| 318 | (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | | 319 | (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | |
| 319 | (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | | 320 | (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) | |
| 320 | (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) | | 321 | (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP4_SHIFT), |
| 321 | (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP4_SHIFT)); | 322 | &ifc->ifc_nand.nand_fir0); |
| 322 | out_be32(&ifc->ifc_nand.nand_fir1, 0x0); | 323 | iowrite32be(0x0, &ifc->ifc_nand.nand_fir1); |
| 323 | 324 | ||
| 324 | out_be32(&ifc->ifc_nand.nand_fcr0, | 325 | iowrite32be((NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) | |
| 325 | (NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) | | 326 | (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT), |
| 326 | (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT)); | 327 | &ifc->ifc_nand.nand_fcr0); |
| 327 | } else { | 328 | } else { |
| 328 | out_be32(&ifc->ifc_nand.nand_fir0, | 329 | iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | |
| 329 | (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | | 330 | (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | |
| 330 | (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | | 331 | (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | |
| 331 | (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | | 332 | (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP3_SHIFT), |
| 332 | (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP3_SHIFT)); | 333 | &ifc->ifc_nand.nand_fir0); |
| 333 | out_be32(&ifc->ifc_nand.nand_fir1, 0x0); | 334 | iowrite32be(0x0, &ifc->ifc_nand.nand_fir1); |
| 334 | 335 | ||
| 335 | if (oob) | 336 | if (oob) |
| 336 | out_be32(&ifc->ifc_nand.nand_fcr0, | 337 | iowrite32be(NAND_CMD_READOOB << |
| 337 | NAND_CMD_READOOB << IFC_NAND_FCR0_CMD0_SHIFT); | 338 | IFC_NAND_FCR0_CMD0_SHIFT, |
| 339 | &ifc->ifc_nand.nand_fcr0); | ||
| 338 | else | 340 | else |
| 339 | out_be32(&ifc->ifc_nand.nand_fcr0, | 341 | iowrite32be(NAND_CMD_READ0 << |
| 340 | NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT); | 342 | IFC_NAND_FCR0_CMD0_SHIFT, |
| 343 | &ifc->ifc_nand.nand_fcr0); | ||
| 341 | } | 344 | } |
| 342 | } | 345 | } |
| 343 | 346 | ||
| @@ -357,7 +360,7 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, | |||
| 357 | switch (command) { | 360 | switch (command) { |
| 358 | /* READ0 read the entire buffer to use hardware ECC. */ | 361 | /* READ0 read the entire buffer to use hardware ECC. */ |
| 359 | case NAND_CMD_READ0: | 362 | case NAND_CMD_READ0: |
| 360 | out_be32(&ifc->ifc_nand.nand_fbcr, 0); | 363 | iowrite32be(0, &ifc->ifc_nand.nand_fbcr); |
| 361 | set_addr(mtd, 0, page_addr, 0); | 364 | set_addr(mtd, 0, page_addr, 0); |
| 362 | 365 | ||
| 363 | ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize; | 366 | ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize; |
| @@ -372,7 +375,7 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, | |||
| 372 | 375 | ||
| 373 | /* READOOB reads only the OOB because no ECC is performed. */ | 376 | /* READOOB reads only the OOB because no ECC is performed. */ |
| 374 | case NAND_CMD_READOOB: | 377 | case NAND_CMD_READOOB: |
| 375 | out_be32(&ifc->ifc_nand.nand_fbcr, mtd->oobsize - column); | 378 | iowrite32be(mtd->oobsize - column, &ifc->ifc_nand.nand_fbcr); |
| 376 | set_addr(mtd, column, page_addr, 1); | 379 | set_addr(mtd, column, page_addr, 1); |
| 377 | 380 | ||
| 378 | ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize; | 381 | ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize; |
| @@ -388,19 +391,19 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, | |||
| 388 | if (command == NAND_CMD_PARAM) | 391 | if (command == NAND_CMD_PARAM) |
| 389 | timing = IFC_FIR_OP_RBCD; | 392 | timing = IFC_FIR_OP_RBCD; |
| 390 | 393 | ||
| 391 | out_be32(&ifc->ifc_nand.nand_fir0, | 394 | iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | |
| 392 | (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | | 395 | (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | |
| 393 | (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | | 396 | (timing << IFC_NAND_FIR0_OP2_SHIFT), |
| 394 | (timing << IFC_NAND_FIR0_OP2_SHIFT)); | 397 | &ifc->ifc_nand.nand_fir0); |
| 395 | out_be32(&ifc->ifc_nand.nand_fcr0, | 398 | iowrite32be(command << IFC_NAND_FCR0_CMD0_SHIFT, |
| 396 | command << IFC_NAND_FCR0_CMD0_SHIFT); | 399 | &ifc->ifc_nand.nand_fcr0); |
| 397 | out_be32(&ifc->ifc_nand.row3, column); | 400 | iowrite32be(column, &ifc->ifc_nand.row3); |
| 398 | 401 | ||
| 399 | /* | 402 | /* |
| 400 | * although currently it's 8 bytes for READID, we always read | 403 | * although currently it's 8 bytes for READID, we always read |
| 401 | * the maximum 256 bytes(for PARAM) | 404 | * the maximum 256 bytes(for PARAM) |
| 402 | */ | 405 | */ |
| 403 | out_be32(&ifc->ifc_nand.nand_fbcr, 256); | 406 | iowrite32be(256, &ifc->ifc_nand.nand_fbcr); |
| 404 | ifc_nand_ctrl->read_bytes = 256; | 407 | ifc_nand_ctrl->read_bytes = 256; |
| 405 | 408 | ||
| 406 | set_addr(mtd, 0, 0, 0); | 409 | set_addr(mtd, 0, 0, 0); |
| @@ -415,16 +418,16 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, | |||
| 415 | 418 | ||
| 416 | /* ERASE2 uses the block and page address from ERASE1 */ | 419 | /* ERASE2 uses the block and page address from ERASE1 */ |
| 417 | case NAND_CMD_ERASE2: | 420 | case NAND_CMD_ERASE2: |
| 418 | out_be32(&ifc->ifc_nand.nand_fir0, | 421 | iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | |
| 419 | (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | | 422 | (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP1_SHIFT) | |
| 420 | (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP1_SHIFT) | | 423 | (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP2_SHIFT), |
| 421 | (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP2_SHIFT)); | 424 | &ifc->ifc_nand.nand_fir0); |
| 422 | 425 | ||
| 423 | out_be32(&ifc->ifc_nand.nand_fcr0, | 426 | iowrite32be((NAND_CMD_ERASE1 << IFC_NAND_FCR0_CMD0_SHIFT) | |
| 424 | (NAND_CMD_ERASE1 << IFC_NAND_FCR0_CMD0_SHIFT) | | 427 | (NAND_CMD_ERASE2 << IFC_NAND_FCR0_CMD1_SHIFT), |
| 425 | (NAND_CMD_ERASE2 << IFC_NAND_FCR0_CMD1_SHIFT)); | 428 | &ifc->ifc_nand.nand_fcr0); |
| 426 | 429 | ||
| 427 | out_be32(&ifc->ifc_nand.nand_fbcr, 0); | 430 | iowrite32be(0, &ifc->ifc_nand.nand_fbcr); |
| 428 | ifc_nand_ctrl->read_bytes = 0; | 431 | ifc_nand_ctrl->read_bytes = 0; |
| 429 | fsl_ifc_run_command(mtd); | 432 | fsl_ifc_run_command(mtd); |
| 430 | return; | 433 | return; |
| @@ -440,26 +443,28 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, | |||
| 440 | (NAND_CMD_SEQIN << IFC_NAND_FCR0_CMD0_SHIFT) | | 443 | (NAND_CMD_SEQIN << IFC_NAND_FCR0_CMD0_SHIFT) | |
| 441 | (NAND_CMD_PAGEPROG << IFC_NAND_FCR0_CMD1_SHIFT); | 444 | (NAND_CMD_PAGEPROG << IFC_NAND_FCR0_CMD1_SHIFT); |
| 442 | 445 | ||
| 443 | out_be32(&ifc->ifc_nand.nand_fir0, | 446 | iowrite32be( |
| 444 | (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | | 447 | (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | |
| 445 | (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | | 448 | (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | |
| 446 | (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | | 449 | (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | |
| 447 | (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP3_SHIFT) | | 450 | (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP3_SHIFT) | |
| 448 | (IFC_FIR_OP_CW1 << IFC_NAND_FIR0_OP4_SHIFT)); | 451 | (IFC_FIR_OP_CW1 << IFC_NAND_FIR0_OP4_SHIFT), |
| 452 | &ifc->ifc_nand.nand_fir0); | ||
| 449 | } else { | 453 | } else { |
| 450 | nand_fcr0 = ((NAND_CMD_PAGEPROG << | 454 | nand_fcr0 = ((NAND_CMD_PAGEPROG << |
| 451 | IFC_NAND_FCR0_CMD1_SHIFT) | | 455 | IFC_NAND_FCR0_CMD1_SHIFT) | |
| 452 | (NAND_CMD_SEQIN << | 456 | (NAND_CMD_SEQIN << |
| 453 | IFC_NAND_FCR0_CMD2_SHIFT)); | 457 | IFC_NAND_FCR0_CMD2_SHIFT)); |
| 454 | 458 | ||
| 455 | out_be32(&ifc->ifc_nand.nand_fir0, | 459 | iowrite32be( |
| 456 | (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | | 460 | (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | |
| 457 | (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP1_SHIFT) | | 461 | (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP1_SHIFT) | |
| 458 | (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP2_SHIFT) | | 462 | (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP2_SHIFT) | |
| 459 | (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP3_SHIFT) | | 463 | (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP3_SHIFT) | |
| 460 | (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP4_SHIFT)); | 464 | (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP4_SHIFT), |
| 461 | out_be32(&ifc->ifc_nand.nand_fir1, | 465 | &ifc->ifc_nand.nand_fir0); |
| 462 | (IFC_FIR_OP_CW1 << IFC_NAND_FIR1_OP5_SHIFT)); | 466 | iowrite32be(IFC_FIR_OP_CW1 << IFC_NAND_FIR1_OP5_SHIFT, |
| 467 | &ifc->ifc_nand.nand_fir1); | ||
| 463 | 468 | ||
| 464 | if (column >= mtd->writesize) | 469 | if (column >= mtd->writesize) |
| 465 | nand_fcr0 |= | 470 | nand_fcr0 |= |
| @@ -474,7 +479,7 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, | |||
| 474 | column -= mtd->writesize; | 479 | column -= mtd->writesize; |
| 475 | ifc_nand_ctrl->oob = 1; | 480 | ifc_nand_ctrl->oob = 1; |
| 476 | } | 481 | } |
| 477 | out_be32(&ifc->ifc_nand.nand_fcr0, nand_fcr0); | 482 | iowrite32be(nand_fcr0, &ifc->ifc_nand.nand_fcr0); |
| 478 | set_addr(mtd, column, page_addr, ifc_nand_ctrl->oob); | 483 | set_addr(mtd, column, page_addr, ifc_nand_ctrl->oob); |
| 479 | return; | 484 | return; |
| 480 | } | 485 | } |
| @@ -482,10 +487,11 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, | |||
| 482 | /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ | 487 | /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ |
| 483 | case NAND_CMD_PAGEPROG: { | 488 | case NAND_CMD_PAGEPROG: { |
| 484 | if (ifc_nand_ctrl->oob) { | 489 | if (ifc_nand_ctrl->oob) { |
| 485 | out_be32(&ifc->ifc_nand.nand_fbcr, | 490 | iowrite32be(ifc_nand_ctrl->index - |
| 486 | ifc_nand_ctrl->index - ifc_nand_ctrl->column); | 491 | ifc_nand_ctrl->column, |
| 492 | &ifc->ifc_nand.nand_fbcr); | ||
| 487 | } else { | 493 | } else { |
| 488 | out_be32(&ifc->ifc_nand.nand_fbcr, 0); | 494 | iowrite32be(0, &ifc->ifc_nand.nand_fbcr); |
| 489 | } | 495 | } |
| 490 | 496 | ||
| 491 | fsl_ifc_run_command(mtd); | 497 | fsl_ifc_run_command(mtd); |
| @@ -493,12 +499,12 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, | |||
| 493 | } | 499 | } |
| 494 | 500 | ||
| 495 | case NAND_CMD_STATUS: | 501 | case NAND_CMD_STATUS: |
| 496 | out_be32(&ifc->ifc_nand.nand_fir0, | 502 | iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | |
| 497 | (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | | 503 | (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP1_SHIFT), |
| 498 | (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP1_SHIFT)); | 504 | &ifc->ifc_nand.nand_fir0); |
| 499 | out_be32(&ifc->ifc_nand.nand_fcr0, | 505 | iowrite32be(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT, |
| 500 | NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT); | 506 | &ifc->ifc_nand.nand_fcr0); |
| 501 | out_be32(&ifc->ifc_nand.nand_fbcr, 1); | 507 | iowrite32be(1, &ifc->ifc_nand.nand_fbcr); |
| 502 | set_addr(mtd, 0, 0, 0); | 508 | set_addr(mtd, 0, 0, 0); |
| 503 | ifc_nand_ctrl->read_bytes = 1; | 509 | ifc_nand_ctrl->read_bytes = 1; |
| 504 | 510 | ||
| @@ -512,10 +518,10 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, | |||
| 512 | return; | 518 | return; |
| 513 | 519 | ||
| 514 | case NAND_CMD_RESET: | 520 | case NAND_CMD_RESET: |
| 515 | out_be32(&ifc->ifc_nand.nand_fir0, | 521 | iowrite32be(IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT, |
| 516 | IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT); | 522 | &ifc->ifc_nand.nand_fir0); |
| 517 | out_be32(&ifc->ifc_nand.nand_fcr0, | 523 | iowrite32be(NAND_CMD_RESET << IFC_NAND_FCR0_CMD0_SHIFT, |
| 518 | NAND_CMD_RESET << IFC_NAND_FCR0_CMD0_SHIFT); | 524 | &ifc->ifc_nand.nand_fcr0); |
| 519 | fsl_ifc_run_command(mtd); | 525 | fsl_ifc_run_command(mtd); |
| 520 | return; | 526 | return; |
| 521 | 527 | ||
| @@ -639,18 +645,18 @@ static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip) | |||
| 639 | u32 nand_fsr; | 645 | u32 nand_fsr; |
| 640 | 646 | ||
| 641 | /* Use READ_STATUS command, but wait for the device to be ready */ | 647 | /* Use READ_STATUS command, but wait for the device to be ready */ |
| 642 | out_be32(&ifc->ifc_nand.nand_fir0, | 648 | iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | |
| 643 | (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | | 649 | (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR0_OP1_SHIFT), |
| 644 | (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR0_OP1_SHIFT)); | 650 | &ifc->ifc_nand.nand_fir0); |
| 645 | out_be32(&ifc->ifc_nand.nand_fcr0, NAND_CMD_STATUS << | 651 | iowrite32be(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT, |
| 646 | IFC_NAND_FCR0_CMD0_SHIFT); | 652 | &ifc->ifc_nand.nand_fcr0); |
| 647 | out_be32(&ifc->ifc_nand.nand_fbcr, 1); | 653 | iowrite32be(1, &ifc->ifc_nand.nand_fbcr); |
| 648 | set_addr(mtd, 0, 0, 0); | 654 | set_addr(mtd, 0, 0, 0); |
| 649 | ifc_nand_ctrl->read_bytes = 1; | 655 | ifc_nand_ctrl->read_bytes = 1; |
| 650 | 656 | ||
| 651 | fsl_ifc_run_command(mtd); | 657 | fsl_ifc_run_command(mtd); |
| 652 | 658 | ||
| 653 | nand_fsr = in_be32(&ifc->ifc_nand.nand_fsr); | 659 | nand_fsr = ioread32be(&ifc->ifc_nand.nand_fsr); |
| 654 | 660 | ||
| 655 | /* | 661 | /* |
| 656 | * The chip always seems to report that it is | 662 | * The chip always seems to report that it is |
| @@ -744,34 +750,34 @@ static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv) | |||
| 744 | uint32_t cs = priv->bank; | 750 | uint32_t cs = priv->bank; |
| 745 | 751 | ||
| 746 | /* Save CSOR and CSOR_ext */ | 752 | /* Save CSOR and CSOR_ext */ |
| 747 | csor = in_be32(&ifc->csor_cs[cs].csor); | 753 | csor = ioread32be(&ifc->csor_cs[cs].csor); |
| 748 | csor_ext = in_be32(&ifc->csor_cs[cs].csor_ext); | 754 | csor_ext = ioread32be(&ifc->csor_cs[cs].csor_ext); |
| 749 | 755 | ||
| 750 | /* chage PageSize 8K and SpareSize 1K*/ | 756 | /* chage PageSize 8K and SpareSize 1K*/ |
| 751 | csor_8k = (csor & ~(CSOR_NAND_PGS_MASK)) | 0x0018C000; | 757 | csor_8k = (csor & ~(CSOR_NAND_PGS_MASK)) | 0x0018C000; |
| 752 | out_be32(&ifc->csor_cs[cs].csor, csor_8k); | 758 | iowrite32be(csor_8k, &ifc->csor_cs[cs].csor); |
| 753 | out_be32(&ifc->csor_cs[cs].csor_ext, 0x0000400); | 759 | iowrite32be(0x0000400, &ifc->csor_cs[cs].csor_ext); |
| 754 | 760 | ||
| 755 | /* READID */ | 761 | /* READID */ |
| 756 | out_be32(&ifc->ifc_nand.nand_fir0, | 762 | iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | |
| 757 | (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | | 763 | (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | |
| 758 | (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | | 764 | (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT), |
| 759 | (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT)); | 765 | &ifc->ifc_nand.nand_fir0); |
| 760 | out_be32(&ifc->ifc_nand.nand_fcr0, | 766 | iowrite32be(NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT, |
| 761 | NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT); | 767 | &ifc->ifc_nand.nand_fcr0); |
| 762 | out_be32(&ifc->ifc_nand.row3, 0x0); | 768 | iowrite32be(0x0, &ifc->ifc_nand.row3); |
| 763 | 769 | ||
| 764 | out_be32(&ifc->ifc_nand.nand_fbcr, 0x0); | 770 | iowrite32be(0x0, &ifc->ifc_nand.nand_fbcr); |
| 765 | 771 | ||
| 766 | /* Program ROW0/COL0 */ | 772 | /* Program ROW0/COL0 */ |
| 767 | out_be32(&ifc->ifc_nand.row0, 0x0); | 773 | iowrite32be(0x0, &ifc->ifc_nand.row0); |
| 768 | out_be32(&ifc->ifc_nand.col0, 0x0); | 774 | iowrite32be(0x0, &ifc->ifc_nand.col0); |
| 769 | 775 | ||
| 770 | /* set the chip select for NAND Transaction */ | 776 | /* set the chip select for NAND Transaction */ |
| 771 | out_be32(&ifc->ifc_nand.nand_csel, cs << IFC_NAND_CSEL_SHIFT); | 777 | iowrite32be(cs << IFC_NAND_CSEL_SHIFT, &ifc->ifc_nand.nand_csel); |
| 772 | 778 | ||
| 773 | /* start read seq */ | 779 | /* start read seq */ |
| 774 | out_be32(&ifc->ifc_nand.nandseq_strt, IFC_NAND_SEQ_STRT_FIR_STRT); | 780 | iowrite32be(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt); |
| 775 | 781 | ||
| 776 | /* wait for command complete flag or timeout */ | 782 | /* wait for command complete flag or timeout */ |
| 777 | wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat, | 783 | wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat, |
| @@ -781,8 +787,8 @@ static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv) | |||
| 781 | printk(KERN_ERR "fsl-ifc: Failed to Initialise SRAM\n"); | 787 | printk(KERN_ERR "fsl-ifc: Failed to Initialise SRAM\n"); |
| 782 | 788 | ||
| 783 | /* Restore CSOR and CSOR_ext */ | 789 | /* Restore CSOR and CSOR_ext */ |
| 784 | out_be32(&ifc->csor_cs[cs].csor, csor); | 790 | iowrite32be(csor, &ifc->csor_cs[cs].csor); |
| 785 | out_be32(&ifc->csor_cs[cs].csor_ext, csor_ext); | 791 | iowrite32be(csor_ext, &ifc->csor_cs[cs].csor_ext); |
| 786 | } | 792 | } |
| 787 | 793 | ||
| 788 | static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) | 794 | static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) |
| @@ -799,7 +805,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) | |||
| 799 | 805 | ||
| 800 | /* fill in nand_chip structure */ | 806 | /* fill in nand_chip structure */ |
| 801 | /* set up function call table */ | 807 | /* set up function call table */ |
| 802 | if ((in_be32(&ifc->cspr_cs[priv->bank].cspr)) & CSPR_PORT_SIZE_16) | 808 | if ((ioread32be(&ifc->cspr_cs[priv->bank].cspr)) & CSPR_PORT_SIZE_16) |
| 803 | chip->read_byte = fsl_ifc_read_byte16; | 809 | chip->read_byte = fsl_ifc_read_byte16; |
| 804 | else | 810 | else |
| 805 | chip->read_byte = fsl_ifc_read_byte; | 811 | chip->read_byte = fsl_ifc_read_byte; |
| @@ -813,13 +819,13 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) | |||
| 813 | chip->bbt_td = &bbt_main_descr; | 819 | chip->bbt_td = &bbt_main_descr; |
| 814 | chip->bbt_md = &bbt_mirror_descr; | 820 | chip->bbt_md = &bbt_mirror_descr; |
| 815 | 821 | ||
| 816 | out_be32(&ifc->ifc_nand.ncfgr, 0x0); | 822 | iowrite32be(0x0, &ifc->ifc_nand.ncfgr); |
| 817 | 823 | ||
| 818 | /* set up nand options */ | 824 | /* set up nand options */ |
| 819 | chip->bbt_options = NAND_BBT_USE_FLASH; | 825 | chip->bbt_options = NAND_BBT_USE_FLASH; |
| 820 | 826 | ||
| 821 | 827 | ||
| 822 | if (in_be32(&ifc->cspr_cs[priv->bank].cspr) & CSPR_PORT_SIZE_16) { | 828 | if (ioread32be(&ifc->cspr_cs[priv->bank].cspr) & CSPR_PORT_SIZE_16) { |
| 823 | chip->read_byte = fsl_ifc_read_byte16; | 829 | chip->read_byte = fsl_ifc_read_byte16; |
| 824 | chip->options |= NAND_BUSWIDTH_16; | 830 | chip->options |= NAND_BUSWIDTH_16; |
| 825 | } else { | 831 | } else { |
| @@ -832,7 +838,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) | |||
| 832 | chip->ecc.read_page = fsl_ifc_read_page; | 838 | chip->ecc.read_page = fsl_ifc_read_page; |
| 833 | chip->ecc.write_page = fsl_ifc_write_page; | 839 | chip->ecc.write_page = fsl_ifc_write_page; |
| 834 | 840 | ||
| 835 | csor = in_be32(&ifc->csor_cs[priv->bank].csor); | 841 | csor = ioread32be(&ifc->csor_cs[priv->bank].csor); |
| 836 | 842 | ||
| 837 | /* Hardware generates ECC per 512 Bytes */ | 843 | /* Hardware generates ECC per 512 Bytes */ |
| 838 | chip->ecc.size = 512; | 844 | chip->ecc.size = 512; |
| @@ -884,7 +890,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) | |||
| 884 | chip->ecc.mode = NAND_ECC_SOFT; | 890 | chip->ecc.mode = NAND_ECC_SOFT; |
| 885 | } | 891 | } |
| 886 | 892 | ||
| 887 | ver = in_be32(&ifc->ifc_rev); | 893 | ver = ioread32be(&ifc->ifc_rev); |
| 888 | if (ver == FSL_IFC_V1_1_0) | 894 | if (ver == FSL_IFC_V1_1_0) |
| 889 | fsl_ifc_sram_init(priv); | 895 | fsl_ifc_sram_init(priv); |
| 890 | 896 | ||
| @@ -910,7 +916,7 @@ static int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv) | |||
| 910 | static int match_bank(struct fsl_ifc_regs __iomem *ifc, int bank, | 916 | static int match_bank(struct fsl_ifc_regs __iomem *ifc, int bank, |
| 911 | phys_addr_t addr) | 917 | phys_addr_t addr) |
| 912 | { | 918 | { |
| 913 | u32 cspr = in_be32(&ifc->cspr_cs[bank].cspr); | 919 | u32 cspr = ioread32be(&ifc->cspr_cs[bank].cspr); |
| 914 | 920 | ||
| 915 | if (!(cspr & CSPR_V)) | 921 | if (!(cspr & CSPR_V)) |
| 916 | return 0; | 922 | return 0; |
| @@ -997,17 +1003,16 @@ static int fsl_ifc_nand_probe(struct platform_device *dev) | |||
| 997 | 1003 | ||
| 998 | dev_set_drvdata(priv->dev, priv); | 1004 | dev_set_drvdata(priv->dev, priv); |
| 999 | 1005 | ||
| 1000 | out_be32(&ifc->ifc_nand.nand_evter_en, | 1006 | iowrite32be(IFC_NAND_EVTER_EN_OPC_EN | |
| 1001 | IFC_NAND_EVTER_EN_OPC_EN | | 1007 | IFC_NAND_EVTER_EN_FTOER_EN | |
| 1002 | IFC_NAND_EVTER_EN_FTOER_EN | | 1008 | IFC_NAND_EVTER_EN_WPER_EN, |
| 1003 | IFC_NAND_EVTER_EN_WPER_EN); | 1009 | &ifc->ifc_nand.nand_evter_en); |
| 1004 | 1010 | ||
| 1005 | /* enable NAND Machine Interrupts */ | 1011 | /* enable NAND Machine Interrupts */ |
| 1006 | out_be32(&ifc->ifc_nand.nand_evter_intr_en, | 1012 | iowrite32be(IFC_NAND_EVTER_INTR_OPCIR_EN | |
| 1007 | IFC_NAND_EVTER_INTR_OPCIR_EN | | 1013 | IFC_NAND_EVTER_INTR_FTOERIR_EN | |
| 1008 | IFC_NAND_EVTER_INTR_FTOERIR_EN | | 1014 | IFC_NAND_EVTER_INTR_WPERIR_EN, |
| 1009 | IFC_NAND_EVTER_INTR_WPERIR_EN); | 1015 | &ifc->ifc_nand.nand_evter_intr_en); |
| 1010 | |||
| 1011 | priv->mtd.name = kasprintf(GFP_KERNEL, "%x.flash", (unsigned)res.start); | 1016 | priv->mtd.name = kasprintf(GFP_KERNEL, "%x.flash", (unsigned)res.start); |
| 1012 | if (!priv->mtd.name) { | 1017 | if (!priv->mtd.name) { |
| 1013 | ret = -ENOMEM; | 1018 | ret = -ENOMEM; |
diff --git a/drivers/mtd/nand/gpmi-nand/bch-regs.h b/drivers/mtd/nand/gpmi-nand/bch-regs.h index a0924515c396..588f5374047c 100644 --- a/drivers/mtd/nand/gpmi-nand/bch-regs.h +++ b/drivers/mtd/nand/gpmi-nand/bch-regs.h | |||
| @@ -61,6 +61,16 @@ | |||
| 61 | & BM_BCH_FLASH0LAYOUT0_ECC0) \ | 61 | & BM_BCH_FLASH0LAYOUT0_ECC0) \ |
| 62 | ) | 62 | ) |
| 63 | 63 | ||
| 64 | #define MX6Q_BP_BCH_FLASH0LAYOUT0_GF_13_14 10 | ||
| 65 | #define MX6Q_BM_BCH_FLASH0LAYOUT0_GF_13_14 \ | ||
| 66 | (0x1 << MX6Q_BP_BCH_FLASH0LAYOUT0_GF_13_14) | ||
| 67 | #define BF_BCH_FLASH0LAYOUT0_GF(v, x) \ | ||
| 68 | ((GPMI_IS_MX6Q(x) && ((v) == 14)) \ | ||
| 69 | ? (((1) << MX6Q_BP_BCH_FLASH0LAYOUT0_GF_13_14) \ | ||
| 70 | & MX6Q_BM_BCH_FLASH0LAYOUT0_GF_13_14) \ | ||
| 71 | : 0 \ | ||
| 72 | ) | ||
| 73 | |||
| 64 | #define BP_BCH_FLASH0LAYOUT0_DATA0_SIZE 0 | 74 | #define BP_BCH_FLASH0LAYOUT0_DATA0_SIZE 0 |
| 65 | #define BM_BCH_FLASH0LAYOUT0_DATA0_SIZE \ | 75 | #define BM_BCH_FLASH0LAYOUT0_DATA0_SIZE \ |
| 66 | (0xfff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE) | 76 | (0xfff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE) |
| @@ -93,6 +103,16 @@ | |||
| 93 | & BM_BCH_FLASH0LAYOUT1_ECCN) \ | 103 | & BM_BCH_FLASH0LAYOUT1_ECCN) \ |
| 94 | ) | 104 | ) |
| 95 | 105 | ||
| 106 | #define MX6Q_BP_BCH_FLASH0LAYOUT1_GF_13_14 10 | ||
| 107 | #define MX6Q_BM_BCH_FLASH0LAYOUT1_GF_13_14 \ | ||
| 108 | (0x1 << MX6Q_BP_BCH_FLASH0LAYOUT1_GF_13_14) | ||
| 109 | #define BF_BCH_FLASH0LAYOUT1_GF(v, x) \ | ||
| 110 | ((GPMI_IS_MX6Q(x) && ((v) == 14)) \ | ||
| 111 | ? (((1) << MX6Q_BP_BCH_FLASH0LAYOUT1_GF_13_14) \ | ||
| 112 | & MX6Q_BM_BCH_FLASH0LAYOUT1_GF_13_14) \ | ||
| 113 | : 0 \ | ||
| 114 | ) | ||
| 115 | |||
| 96 | #define BP_BCH_FLASH0LAYOUT1_DATAN_SIZE 0 | 116 | #define BP_BCH_FLASH0LAYOUT1_DATAN_SIZE 0 |
| 97 | #define BM_BCH_FLASH0LAYOUT1_DATAN_SIZE \ | 117 | #define BM_BCH_FLASH0LAYOUT1_DATAN_SIZE \ |
| 98 | (0xfff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) | 118 | (0xfff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) |
| @@ -103,4 +123,6 @@ | |||
| 103 | ? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ | 123 | ? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ |
| 104 | : ((v) & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ | 124 | : ((v) & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ |
| 105 | ) | 125 | ) |
| 126 | |||
| 127 | #define HW_BCH_VERSION 0x00000160 | ||
| 106 | #endif | 128 | #endif |
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c index d84699c7968e..4f8857fa48a7 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c | |||
| @@ -208,6 +208,11 @@ void gpmi_dump_info(struct gpmi_nand_data *this) | |||
| 208 | } | 208 | } |
| 209 | 209 | ||
| 210 | /* start to print out the BCH info */ | 210 | /* start to print out the BCH info */ |
| 211 | pr_err("Show BCH registers :\n"); | ||
| 212 | for (i = 0; i <= HW_BCH_VERSION / 0x10 + 1; i++) { | ||
| 213 | reg = readl(r->bch_regs + i * 0x10); | ||
| 214 | pr_err("offset 0x%.3x : 0x%.8x\n", i * 0x10, reg); | ||
| 215 | } | ||
| 211 | pr_err("BCH Geometry :\n"); | 216 | pr_err("BCH Geometry :\n"); |
| 212 | pr_err("GF length : %u\n", geo->gf_len); | 217 | pr_err("GF length : %u\n", geo->gf_len); |
| 213 | pr_err("ECC Strength : %u\n", geo->ecc_strength); | 218 | pr_err("ECC Strength : %u\n", geo->ecc_strength); |
| @@ -232,6 +237,7 @@ int bch_set_geometry(struct gpmi_nand_data *this) | |||
| 232 | unsigned int metadata_size; | 237 | unsigned int metadata_size; |
| 233 | unsigned int ecc_strength; | 238 | unsigned int ecc_strength; |
| 234 | unsigned int page_size; | 239 | unsigned int page_size; |
| 240 | unsigned int gf_len; | ||
| 235 | int ret; | 241 | int ret; |
| 236 | 242 | ||
| 237 | if (common_nfc_set_geometry(this)) | 243 | if (common_nfc_set_geometry(this)) |
| @@ -242,6 +248,7 @@ int bch_set_geometry(struct gpmi_nand_data *this) | |||
| 242 | metadata_size = bch_geo->metadata_size; | 248 | metadata_size = bch_geo->metadata_size; |
| 243 | ecc_strength = bch_geo->ecc_strength >> 1; | 249 | ecc_strength = bch_geo->ecc_strength >> 1; |
| 244 | page_size = bch_geo->page_size; | 250 | page_size = bch_geo->page_size; |
| 251 | gf_len = bch_geo->gf_len; | ||
| 245 | 252 | ||
| 246 | ret = gpmi_enable_clk(this); | 253 | ret = gpmi_enable_clk(this); |
| 247 | if (ret) | 254 | if (ret) |
| @@ -263,11 +270,13 @@ int bch_set_geometry(struct gpmi_nand_data *this) | |||
| 263 | writel(BF_BCH_FLASH0LAYOUT0_NBLOCKS(block_count) | 270 | writel(BF_BCH_FLASH0LAYOUT0_NBLOCKS(block_count) |
| 264 | | BF_BCH_FLASH0LAYOUT0_META_SIZE(metadata_size) | 271 | | BF_BCH_FLASH0LAYOUT0_META_SIZE(metadata_size) |
| 265 | | BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this) | 272 | | BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this) |
| 273 | | BF_BCH_FLASH0LAYOUT0_GF(gf_len, this) | ||
| 266 | | BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size, this), | 274 | | BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size, this), |
| 267 | r->bch_regs + HW_BCH_FLASH0LAYOUT0); | 275 | r->bch_regs + HW_BCH_FLASH0LAYOUT0); |
| 268 | 276 | ||
| 269 | writel(BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) | 277 | writel(BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) |
| 270 | | BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this) | 278 | | BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this) |
| 279 | | BF_BCH_FLASH0LAYOUT1_GF(gf_len, this) | ||
| 271 | | BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size, this), | 280 | | BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size, this), |
| 272 | r->bch_regs + HW_BCH_FLASH0LAYOUT1); | 281 | r->bch_regs + HW_BCH_FLASH0LAYOUT1); |
| 273 | 282 | ||
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index e9b1c47e3cf9..717881a3d1b8 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c | |||
| @@ -94,6 +94,25 @@ static inline int get_ecc_strength(struct gpmi_nand_data *this) | |||
| 94 | return round_down(ecc_strength, 2); | 94 | return round_down(ecc_strength, 2); |
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | static inline bool gpmi_check_ecc(struct gpmi_nand_data *this) | ||
| 98 | { | ||
| 99 | struct bch_geometry *geo = &this->bch_geometry; | ||
| 100 | |||
| 101 | /* Do the sanity check. */ | ||
| 102 | if (GPMI_IS_MX23(this) || GPMI_IS_MX28(this)) { | ||
| 103 | /* The mx23/mx28 only support the GF13. */ | ||
| 104 | if (geo->gf_len == 14) | ||
| 105 | return false; | ||
| 106 | |||
| 107 | if (geo->ecc_strength > MXS_ECC_STRENGTH_MAX) | ||
| 108 | return false; | ||
| 109 | } else if (GPMI_IS_MX6Q(this)) { | ||
| 110 | if (geo->ecc_strength > MX6_ECC_STRENGTH_MAX) | ||
| 111 | return false; | ||
| 112 | } | ||
| 113 | return true; | ||
| 114 | } | ||
| 115 | |||
| 97 | int common_nfc_set_geometry(struct gpmi_nand_data *this) | 116 | int common_nfc_set_geometry(struct gpmi_nand_data *this) |
| 98 | { | 117 | { |
| 99 | struct bch_geometry *geo = &this->bch_geometry; | 118 | struct bch_geometry *geo = &this->bch_geometry; |
| @@ -112,17 +131,24 @@ int common_nfc_set_geometry(struct gpmi_nand_data *this) | |||
| 112 | /* The default for the length of Galois Field. */ | 131 | /* The default for the length of Galois Field. */ |
| 113 | geo->gf_len = 13; | 132 | geo->gf_len = 13; |
| 114 | 133 | ||
| 115 | /* The default for chunk size. There is no oobsize greater then 512. */ | 134 | /* The default for chunk size. */ |
| 116 | geo->ecc_chunk_size = 512; | 135 | geo->ecc_chunk_size = 512; |
| 117 | while (geo->ecc_chunk_size < mtd->oobsize) | 136 | while (geo->ecc_chunk_size < mtd->oobsize) { |
| 118 | geo->ecc_chunk_size *= 2; /* keep C >= O */ | 137 | geo->ecc_chunk_size *= 2; /* keep C >= O */ |
| 138 | geo->gf_len = 14; | ||
| 139 | } | ||
| 119 | 140 | ||
| 120 | geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size; | 141 | geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size; |
| 121 | 142 | ||
| 122 | /* We use the same ECC strength for all chunks. */ | 143 | /* We use the same ECC strength for all chunks. */ |
| 123 | geo->ecc_strength = get_ecc_strength(this); | 144 | geo->ecc_strength = get_ecc_strength(this); |
| 124 | if (!geo->ecc_strength) { | 145 | if (!gpmi_check_ecc(this)) { |
| 125 | pr_err("wrong ECC strength.\n"); | 146 | dev_err(this->dev, |
| 147 | "We can not support this nand chip." | ||
| 148 | " Its required ecc strength(%d) is beyond our" | ||
| 149 | " capability(%d).\n", geo->ecc_strength, | ||
| 150 | (GPMI_IS_MX6Q(this) ? MX6_ECC_STRENGTH_MAX | ||
| 151 | : MXS_ECC_STRENGTH_MAX)); | ||
| 126 | return -EINVAL; | 152 | return -EINVAL; |
| 127 | } | 153 | } |
| 128 | 154 | ||
| @@ -920,8 +946,7 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, | |||
| 920 | dma_addr_t auxiliary_phys; | 946 | dma_addr_t auxiliary_phys; |
| 921 | unsigned int i; | 947 | unsigned int i; |
| 922 | unsigned char *status; | 948 | unsigned char *status; |
| 923 | unsigned int failed; | 949 | unsigned int max_bitflips = 0; |
| 924 | unsigned int corrected; | ||
| 925 | int ret; | 950 | int ret; |
| 926 | 951 | ||
| 927 | pr_debug("page number is : %d\n", page); | 952 | pr_debug("page number is : %d\n", page); |
| @@ -945,35 +970,25 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, | |||
| 945 | payload_virt, payload_phys); | 970 | payload_virt, payload_phys); |
| 946 | if (ret) { | 971 | if (ret) { |
| 947 | pr_err("Error in ECC-based read: %d\n", ret); | 972 | pr_err("Error in ECC-based read: %d\n", ret); |
| 948 | goto exit_nfc; | 973 | return ret; |
| 949 | } | 974 | } |
| 950 | 975 | ||
| 951 | /* handle the block mark swapping */ | 976 | /* handle the block mark swapping */ |
| 952 | block_mark_swapping(this, payload_virt, auxiliary_virt); | 977 | block_mark_swapping(this, payload_virt, auxiliary_virt); |
| 953 | 978 | ||
| 954 | /* Loop over status bytes, accumulating ECC status. */ | 979 | /* Loop over status bytes, accumulating ECC status. */ |
| 955 | failed = 0; | 980 | status = auxiliary_virt + nfc_geo->auxiliary_status_offset; |
| 956 | corrected = 0; | ||
| 957 | status = auxiliary_virt + nfc_geo->auxiliary_status_offset; | ||
| 958 | 981 | ||
| 959 | for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) { | 982 | for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) { |
| 960 | if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED)) | 983 | if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED)) |
| 961 | continue; | 984 | continue; |
| 962 | 985 | ||
| 963 | if (*status == STATUS_UNCORRECTABLE) { | 986 | if (*status == STATUS_UNCORRECTABLE) { |
| 964 | failed++; | 987 | mtd->ecc_stats.failed++; |
| 965 | continue; | 988 | continue; |
| 966 | } | 989 | } |
| 967 | corrected += *status; | 990 | mtd->ecc_stats.corrected += *status; |
| 968 | } | 991 | max_bitflips = max_t(unsigned int, max_bitflips, *status); |
| 969 | |||
| 970 | /* | ||
| 971 | * Propagate ECC status to the owning MTD only when failed or | ||
| 972 | * corrected times nearly reaches our ECC correction threshold. | ||
| 973 | */ | ||
| 974 | if (failed || corrected >= (nfc_geo->ecc_strength - 1)) { | ||
| 975 | mtd->ecc_stats.failed += failed; | ||
| 976 | mtd->ecc_stats.corrected += corrected; | ||
| 977 | } | 992 | } |
| 978 | 993 | ||
| 979 | if (oob_required) { | 994 | if (oob_required) { |
| @@ -995,8 +1010,8 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, | |||
| 995 | this->payload_virt, this->payload_phys, | 1010 | this->payload_virt, this->payload_phys, |
| 996 | nfc_geo->payload_size, | 1011 | nfc_geo->payload_size, |
| 997 | payload_virt, payload_phys); | 1012 | payload_virt, payload_phys); |
| 998 | exit_nfc: | 1013 | |
| 999 | return ret; | 1014 | return max_bitflips; |
| 1000 | } | 1015 | } |
| 1001 | 1016 | ||
| 1002 | static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip, | 1017 | static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip, |
| @@ -1668,8 +1683,8 @@ exit_nfc_init: | |||
| 1668 | release_resources(this); | 1683 | release_resources(this); |
| 1669 | exit_acquire_resources: | 1684 | exit_acquire_resources: |
| 1670 | platform_set_drvdata(pdev, NULL); | 1685 | platform_set_drvdata(pdev, NULL); |
| 1671 | kfree(this); | ||
| 1672 | dev_err(this->dev, "driver registration failed: %d\n", ret); | 1686 | dev_err(this->dev, "driver registration failed: %d\n", ret); |
| 1687 | kfree(this); | ||
| 1673 | 1688 | ||
| 1674 | return ret; | 1689 | return ret; |
| 1675 | } | 1690 | } |
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h index 3d93a5e39090..072947731277 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h | |||
| @@ -284,6 +284,10 @@ extern int gpmi_read_page(struct gpmi_nand_data *, | |||
| 284 | #define STATUS_ERASED 0xff | 284 | #define STATUS_ERASED 0xff |
| 285 | #define STATUS_UNCORRECTABLE 0xfe | 285 | #define STATUS_UNCORRECTABLE 0xfe |
| 286 | 286 | ||
| 287 | /* BCH's bit correction capability. */ | ||
| 288 | #define MXS_ECC_STRENGTH_MAX 20 /* mx23 and mx28 */ | ||
| 289 | #define MX6_ECC_STRENGTH_MAX 40 | ||
| 290 | |||
| 287 | /* Use the platform_id to distinguish different Archs. */ | 291 | /* Use the platform_id to distinguish different Archs. */ |
| 288 | #define IS_MX23 0x0 | 292 | #define IS_MX23 0x0 |
| 289 | #define IS_MX28 0x1 | 293 | #define IS_MX28 0x1 |
diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 60ac5b98b718..07e5784e5cd3 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c | |||
| @@ -530,12 +530,23 @@ static void send_page_v1(struct mtd_info *mtd, unsigned int ops) | |||
| 530 | 530 | ||
| 531 | static void send_read_id_v3(struct mxc_nand_host *host) | 531 | static void send_read_id_v3(struct mxc_nand_host *host) |
| 532 | { | 532 | { |
| 533 | struct nand_chip *this = &host->nand; | ||
| 534 | |||
| 533 | /* Read ID into main buffer */ | 535 | /* Read ID into main buffer */ |
| 534 | writel(NFC_ID, NFC_V3_LAUNCH); | 536 | writel(NFC_ID, NFC_V3_LAUNCH); |
| 535 | 537 | ||
| 536 | wait_op_done(host, true); | 538 | wait_op_done(host, true); |
| 537 | 539 | ||
| 538 | memcpy32_fromio(host->data_buf, host->main_area0, 16); | 540 | memcpy32_fromio(host->data_buf, host->main_area0, 16); |
| 541 | |||
| 542 | if (this->options & NAND_BUSWIDTH_16) { | ||
| 543 | /* compress the ID info */ | ||
| 544 | host->data_buf[1] = host->data_buf[2]; | ||
| 545 | host->data_buf[2] = host->data_buf[4]; | ||
| 546 | host->data_buf[3] = host->data_buf[6]; | ||
| 547 | host->data_buf[4] = host->data_buf[8]; | ||
| 548 | host->data_buf[5] = host->data_buf[10]; | ||
| 549 | } | ||
| 539 | } | 550 | } |
| 540 | 551 | ||
| 541 | /* Request the NANDFC to perform a read of the NAND device ID. */ | 552 | /* Request the NANDFC to perform a read of the NAND device ID. */ |
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 3766682a0289..43214151b882 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c | |||
| @@ -825,13 +825,8 @@ static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip, | |||
| 825 | static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) | 825 | static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) |
| 826 | { | 826 | { |
| 827 | 827 | ||
| 828 | unsigned long timeo = jiffies; | ||
| 829 | int status, state = chip->state; | 828 | int status, state = chip->state; |
| 830 | 829 | unsigned long timeo = (state == FL_ERASING ? 400 : 20); | |
| 831 | if (state == FL_ERASING) | ||
| 832 | timeo += (HZ * 400) / 1000; | ||
| 833 | else | ||
| 834 | timeo += (HZ * 20) / 1000; | ||
| 835 | 830 | ||
| 836 | led_trigger_event(nand_led_trigger, LED_FULL); | 831 | led_trigger_event(nand_led_trigger, LED_FULL); |
| 837 | 832 | ||
| @@ -849,6 +844,7 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) | |||
| 849 | if (in_interrupt() || oops_in_progress) | 844 | if (in_interrupt() || oops_in_progress) |
| 850 | panic_nand_wait(mtd, chip, timeo); | 845 | panic_nand_wait(mtd, chip, timeo); |
| 851 | else { | 846 | else { |
| 847 | timeo = jiffies + msecs_to_jiffies(timeo); | ||
| 852 | while (time_before(jiffies, timeo)) { | 848 | while (time_before(jiffies, timeo)) { |
| 853 | if (chip->dev_ready) { | 849 | if (chip->dev_ready) { |
| 854 | if (chip->dev_ready(mtd)) | 850 | if (chip->dev_ready(mtd)) |
diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c index b7cfe0d37121..053c9a2d47c3 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/nand_ecc.c | |||
| @@ -55,8 +55,7 @@ struct mtd_info; | |||
| 55 | #define MODULE_AUTHOR(x) /* x */ | 55 | #define MODULE_AUTHOR(x) /* x */ |
| 56 | #define MODULE_DESCRIPTION(x) /* x */ | 56 | #define MODULE_DESCRIPTION(x) /* x */ |
| 57 | 57 | ||
| 58 | #define printk printf | 58 | #define pr_err printf |
| 59 | #define KERN_ERR "" | ||
| 60 | #endif | 59 | #endif |
| 61 | 60 | ||
| 62 | /* | 61 | /* |
| @@ -507,7 +506,7 @@ int __nand_correct_data(unsigned char *buf, | |||
| 507 | if ((bitsperbyte[b0] + bitsperbyte[b1] + bitsperbyte[b2]) == 1) | 506 | if ((bitsperbyte[b0] + bitsperbyte[b1] + bitsperbyte[b2]) == 1) |
| 508 | return 1; /* error in ECC data; no action needed */ | 507 | return 1; /* error in ECC data; no action needed */ |
| 509 | 508 | ||
| 510 | printk(KERN_ERR "uncorrectable error : "); | 509 | pr_err("%s: uncorrectable ECC error", __func__); |
| 511 | return -1; | 510 | return -1; |
| 512 | } | 511 | } |
| 513 | EXPORT_SYMBOL(__nand_correct_data); | 512 | EXPORT_SYMBOL(__nand_correct_data); |
diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 8f30d385bfa3..891c52a30e6a 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c | |||
| @@ -1468,12 +1468,12 @@ int do_read_error(struct nandsim *ns, int num) | |||
| 1468 | 1468 | ||
| 1469 | void do_bit_flips(struct nandsim *ns, int num) | 1469 | void do_bit_flips(struct nandsim *ns, int num) |
| 1470 | { | 1470 | { |
| 1471 | if (bitflips && random32() < (1 << 22)) { | 1471 | if (bitflips && prandom_u32() < (1 << 22)) { |
| 1472 | int flips = 1; | 1472 | int flips = 1; |
| 1473 | if (bitflips > 1) | 1473 | if (bitflips > 1) |
| 1474 | flips = (random32() % (int) bitflips) + 1; | 1474 | flips = (prandom_u32() % (int) bitflips) + 1; |
| 1475 | while (flips--) { | 1475 | while (flips--) { |
| 1476 | int pos = random32() % (num * 8); | 1476 | int pos = prandom_u32() % (num * 8); |
| 1477 | ns->buf.byte[pos / 8] ^= (1 << (pos % 8)); | 1477 | ns->buf.byte[pos / 8] ^= (1 << (pos % 8)); |
| 1478 | NS_WARN("read_page: flipping bit %d in page %d " | 1478 | NS_WARN("read_page: flipping bit %d in page %d " |
| 1479 | "reading from %d ecc: corrected=%u failed=%u\n", | 1479 | "reading from %d ecc: corrected=%u failed=%u\n", |
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 1d333497cfcb..8e820ddf4e08 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c | |||
| @@ -22,9 +22,12 @@ | |||
| 22 | #include <linux/omap-dma.h> | 22 | #include <linux/omap-dma.h> |
| 23 | #include <linux/io.h> | 23 | #include <linux/io.h> |
| 24 | #include <linux/slab.h> | 24 | #include <linux/slab.h> |
| 25 | #include <linux/of.h> | ||
| 26 | #include <linux/of_device.h> | ||
| 25 | 27 | ||
| 26 | #ifdef CONFIG_MTD_NAND_OMAP_BCH | 28 | #ifdef CONFIG_MTD_NAND_OMAP_BCH |
| 27 | #include <linux/bch.h> | 29 | #include <linux/bch.h> |
| 30 | #include <linux/platform_data/elm.h> | ||
| 28 | #endif | 31 | #endif |
| 29 | 32 | ||
| 30 | #include <linux/platform_data/mtd-nand-omap2.h> | 33 | #include <linux/platform_data/mtd-nand-omap2.h> |
| @@ -117,6 +120,33 @@ | |||
| 117 | 120 | ||
| 118 | #define OMAP24XX_DMA_GPMC 4 | 121 | #define OMAP24XX_DMA_GPMC 4 |
| 119 | 122 | ||
| 123 | #define BCH8_MAX_ERROR 8 /* upto 8 bit correctable */ | ||
| 124 | #define BCH4_MAX_ERROR 4 /* upto 4 bit correctable */ | ||
| 125 | |||
| 126 | #define SECTOR_BYTES 512 | ||
| 127 | /* 4 bit padding to make byte aligned, 56 = 52 + 4 */ | ||
| 128 | #define BCH4_BIT_PAD 4 | ||
| 129 | #define BCH8_ECC_MAX ((SECTOR_BYTES + BCH8_ECC_OOB_BYTES) * 8) | ||
| 130 | #define BCH4_ECC_MAX ((SECTOR_BYTES + BCH4_ECC_OOB_BYTES) * 8) | ||
| 131 | |||
| 132 | /* GPMC ecc engine settings for read */ | ||
| 133 | #define BCH_WRAPMODE_1 1 /* BCH wrap mode 1 */ | ||
| 134 | #define BCH8R_ECC_SIZE0 0x1a /* ecc_size0 = 26 */ | ||
| 135 | #define BCH8R_ECC_SIZE1 0x2 /* ecc_size1 = 2 */ | ||
| 136 | #define BCH4R_ECC_SIZE0 0xd /* ecc_size0 = 13 */ | ||
| 137 | #define BCH4R_ECC_SIZE1 0x3 /* ecc_size1 = 3 */ | ||
| 138 | |||
| 139 | /* GPMC ecc engine settings for write */ | ||
| 140 | #define BCH_WRAPMODE_6 6 /* BCH wrap mode 6 */ | ||
| 141 | #define BCH_ECC_SIZE0 0x0 /* ecc_size0 = 0, no oob protection */ | ||
| 142 | #define BCH_ECC_SIZE1 0x20 /* ecc_size1 = 32 */ | ||
| 143 | |||
| 144 | #ifdef CONFIG_MTD_NAND_OMAP_BCH | ||
| 145 | static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc, | ||
| 146 | 0xac, 0x6b, 0xff, 0x99, 0x7b}; | ||
| 147 | static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10}; | ||
| 148 | #endif | ||
| 149 | |||
| 120 | /* oob info generated runtime depending on ecc algorithm and layout selected */ | 150 | /* oob info generated runtime depending on ecc algorithm and layout selected */ |
| 121 | static struct nand_ecclayout omap_oobinfo; | 151 | static struct nand_ecclayout omap_oobinfo; |
| 122 | /* Define some generic bad / good block scan pattern which are used | 152 | /* Define some generic bad / good block scan pattern which are used |
| @@ -156,6 +186,9 @@ struct omap_nand_info { | |||
| 156 | #ifdef CONFIG_MTD_NAND_OMAP_BCH | 186 | #ifdef CONFIG_MTD_NAND_OMAP_BCH |
| 157 | struct bch_control *bch; | 187 | struct bch_control *bch; |
| 158 | struct nand_ecclayout ecclayout; | 188 | struct nand_ecclayout ecclayout; |
| 189 | bool is_elm_used; | ||
| 190 | struct device *elm_dev; | ||
| 191 | struct device_node *of_node; | ||
| 159 | #endif | 192 | #endif |
| 160 | }; | 193 | }; |
| 161 | 194 | ||
| @@ -1031,6 +1064,13 @@ static int omap_dev_ready(struct mtd_info *mtd) | |||
| 1031 | * omap3_enable_hwecc_bch - Program OMAP3 GPMC to perform BCH ECC correction | 1064 | * omap3_enable_hwecc_bch - Program OMAP3 GPMC to perform BCH ECC correction |
| 1032 | * @mtd: MTD device structure | 1065 | * @mtd: MTD device structure |
| 1033 | * @mode: Read/Write mode | 1066 | * @mode: Read/Write mode |
| 1067 | * | ||
| 1068 | * When using BCH, sector size is hardcoded to 512 bytes. | ||
| 1069 | * Using wrapping mode 6 both for reading and writing if ELM module not uses | ||
| 1070 | * for error correction. | ||
| 1071 | * On writing, | ||
| 1072 | * eccsize0 = 0 (no additional protected byte in spare area) | ||
| 1073 | * eccsize1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area) | ||
| 1034 | */ | 1074 | */ |
| 1035 | static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode) | 1075 | static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode) |
| 1036 | { | 1076 | { |
| @@ -1039,32 +1079,57 @@ static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode) | |||
| 1039 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, | 1079 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, |
| 1040 | mtd); | 1080 | mtd); |
| 1041 | struct nand_chip *chip = mtd->priv; | 1081 | struct nand_chip *chip = mtd->priv; |
| 1042 | u32 val; | 1082 | u32 val, wr_mode; |
| 1083 | unsigned int ecc_size1, ecc_size0; | ||
| 1084 | |||
| 1085 | /* Using wrapping mode 6 for writing */ | ||
| 1086 | wr_mode = BCH_WRAPMODE_6; | ||
| 1043 | 1087 | ||
| 1044 | nerrors = (info->nand.ecc.bytes == 13) ? 8 : 4; | ||
| 1045 | dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0; | ||
| 1046 | nsectors = 1; | ||
| 1047 | /* | 1088 | /* |
| 1048 | * Program GPMC to perform correction on one 512-byte sector at a time. | 1089 | * ECC engine enabled for valid ecc_size0 nibbles |
| 1049 | * Using 4 sectors at a time (i.e. ecc.size = 2048) is also possible and | 1090 | * and disabled for ecc_size1 nibbles. |
| 1050 | * gives a slight (5%) performance gain (but requires additional code). | ||
| 1051 | */ | 1091 | */ |
| 1092 | ecc_size0 = BCH_ECC_SIZE0; | ||
| 1093 | ecc_size1 = BCH_ECC_SIZE1; | ||
| 1094 | |||
| 1095 | /* Perform ecc calculation on 512-byte sector */ | ||
| 1096 | nsectors = 1; | ||
| 1097 | |||
| 1098 | /* Update number of error correction */ | ||
| 1099 | nerrors = info->nand.ecc.strength; | ||
| 1100 | |||
| 1101 | /* Multi sector reading/writing for NAND flash with page size < 4096 */ | ||
| 1102 | if (info->is_elm_used && (mtd->writesize <= 4096)) { | ||
| 1103 | if (mode == NAND_ECC_READ) { | ||
| 1104 | /* Using wrapping mode 1 for reading */ | ||
| 1105 | wr_mode = BCH_WRAPMODE_1; | ||
| 1106 | |||
| 1107 | /* | ||
| 1108 | * ECC engine enabled for ecc_size0 nibbles | ||
| 1109 | * and disabled for ecc_size1 nibbles. | ||
| 1110 | */ | ||
| 1111 | ecc_size0 = (nerrors == 8) ? | ||
| 1112 | BCH8R_ECC_SIZE0 : BCH4R_ECC_SIZE0; | ||
| 1113 | ecc_size1 = (nerrors == 8) ? | ||
| 1114 | BCH8R_ECC_SIZE1 : BCH4R_ECC_SIZE1; | ||
| 1115 | } | ||
| 1116 | |||
| 1117 | /* Perform ecc calculation for one page (< 4096) */ | ||
| 1118 | nsectors = info->nand.ecc.steps; | ||
| 1119 | } | ||
| 1052 | 1120 | ||
| 1053 | writel(ECC1, info->reg.gpmc_ecc_control); | 1121 | writel(ECC1, info->reg.gpmc_ecc_control); |
| 1054 | 1122 | ||
| 1055 | /* | 1123 | /* Configure ecc size for BCH */ |
| 1056 | * When using BCH, sector size is hardcoded to 512 bytes. | 1124 | val = (ecc_size1 << ECCSIZE1_SHIFT) | (ecc_size0 << ECCSIZE0_SHIFT); |
| 1057 | * Here we are using wrapping mode 6 both for reading and writing, with: | ||
| 1058 | * size0 = 0 (no additional protected byte in spare area) | ||
| 1059 | * size1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area) | ||
| 1060 | */ | ||
| 1061 | val = (32 << ECCSIZE1_SHIFT) | (0 << ECCSIZE0_SHIFT); | ||
| 1062 | writel(val, info->reg.gpmc_ecc_size_config); | 1125 | writel(val, info->reg.gpmc_ecc_size_config); |
| 1063 | 1126 | ||
| 1127 | dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0; | ||
| 1128 | |||
| 1064 | /* BCH configuration */ | 1129 | /* BCH configuration */ |
| 1065 | val = ((1 << 16) | /* enable BCH */ | 1130 | val = ((1 << 16) | /* enable BCH */ |
| 1066 | (((nerrors == 8) ? 1 : 0) << 12) | /* 8 or 4 bits */ | 1131 | (((nerrors == 8) ? 1 : 0) << 12) | /* 8 or 4 bits */ |
| 1067 | (0x06 << 8) | /* wrap mode = 6 */ | 1132 | (wr_mode << 8) | /* wrap mode */ |
| 1068 | (dev_width << 7) | /* bus width */ | 1133 | (dev_width << 7) | /* bus width */ |
| 1069 | (((nsectors-1) & 0x7) << 4) | /* number of sectors */ | 1134 | (((nsectors-1) & 0x7) << 4) | /* number of sectors */ |
| 1070 | (info->gpmc_cs << 1) | /* ECC CS */ | 1135 | (info->gpmc_cs << 1) | /* ECC CS */ |
| @@ -1072,7 +1137,7 @@ static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode) | |||
| 1072 | 1137 | ||
| 1073 | writel(val, info->reg.gpmc_ecc_config); | 1138 | writel(val, info->reg.gpmc_ecc_config); |
| 1074 | 1139 | ||
| 1075 | /* clear ecc and enable bits */ | 1140 | /* Clear ecc and enable bits */ |
| 1076 | writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control); | 1141 | writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control); |
| 1077 | } | 1142 | } |
| 1078 | 1143 | ||
| @@ -1162,6 +1227,298 @@ static int omap3_calculate_ecc_bch8(struct mtd_info *mtd, const u_char *dat, | |||
| 1162 | } | 1227 | } |
| 1163 | 1228 | ||
| 1164 | /** | 1229 | /** |
| 1230 | * omap3_calculate_ecc_bch - Generate bytes of ECC bytes | ||
| 1231 | * @mtd: MTD device structure | ||
| 1232 | * @dat: The pointer to data on which ecc is computed | ||
| 1233 | * @ecc_code: The ecc_code buffer | ||
| 1234 | * | ||
| 1235 | * Support calculating of BCH4/8 ecc vectors for the page | ||
| 1236 | */ | ||
| 1237 | static int omap3_calculate_ecc_bch(struct mtd_info *mtd, const u_char *dat, | ||
| 1238 | u_char *ecc_code) | ||
| 1239 | { | ||
| 1240 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, | ||
| 1241 | mtd); | ||
| 1242 | unsigned long nsectors, bch_val1, bch_val2, bch_val3, bch_val4; | ||
| 1243 | int i, eccbchtsel; | ||
| 1244 | |||
| 1245 | nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1; | ||
| 1246 | /* | ||
| 1247 | * find BCH scheme used | ||
| 1248 | * 0 -> BCH4 | ||
| 1249 | * 1 -> BCH8 | ||
| 1250 | */ | ||
| 1251 | eccbchtsel = ((readl(info->reg.gpmc_ecc_config) >> 12) & 0x3); | ||
| 1252 | |||
| 1253 | for (i = 0; i < nsectors; i++) { | ||
| 1254 | |||
| 1255 | /* Read hw-computed remainder */ | ||
| 1256 | bch_val1 = readl(info->reg.gpmc_bch_result0[i]); | ||
| 1257 | bch_val2 = readl(info->reg.gpmc_bch_result1[i]); | ||
| 1258 | if (eccbchtsel) { | ||
| 1259 | bch_val3 = readl(info->reg.gpmc_bch_result2[i]); | ||
| 1260 | bch_val4 = readl(info->reg.gpmc_bch_result3[i]); | ||
| 1261 | } | ||
| 1262 | |||
| 1263 | if (eccbchtsel) { | ||
| 1264 | /* BCH8 ecc scheme */ | ||
| 1265 | *ecc_code++ = (bch_val4 & 0xFF); | ||
| 1266 | *ecc_code++ = ((bch_val3 >> 24) & 0xFF); | ||
| 1267 | *ecc_code++ = ((bch_val3 >> 16) & 0xFF); | ||
| 1268 | *ecc_code++ = ((bch_val3 >> 8) & 0xFF); | ||
| 1269 | *ecc_code++ = (bch_val3 & 0xFF); | ||
| 1270 | *ecc_code++ = ((bch_val2 >> 24) & 0xFF); | ||
| 1271 | *ecc_code++ = ((bch_val2 >> 16) & 0xFF); | ||
| 1272 | *ecc_code++ = ((bch_val2 >> 8) & 0xFF); | ||
| 1273 | *ecc_code++ = (bch_val2 & 0xFF); | ||
| 1274 | *ecc_code++ = ((bch_val1 >> 24) & 0xFF); | ||
| 1275 | *ecc_code++ = ((bch_val1 >> 16) & 0xFF); | ||
| 1276 | *ecc_code++ = ((bch_val1 >> 8) & 0xFF); | ||
| 1277 | *ecc_code++ = (bch_val1 & 0xFF); | ||
| 1278 | /* | ||
| 1279 | * Setting 14th byte to zero to handle | ||
| 1280 | * erased page & maintain compatibility | ||
| 1281 | * with RBL | ||
| 1282 | */ | ||
| 1283 | *ecc_code++ = 0x0; | ||
| 1284 | } else { | ||
| 1285 | /* BCH4 ecc scheme */ | ||
| 1286 | *ecc_code++ = ((bch_val2 >> 12) & 0xFF); | ||
| 1287 | *ecc_code++ = ((bch_val2 >> 4) & 0xFF); | ||
| 1288 | *ecc_code++ = ((bch_val2 & 0xF) << 4) | | ||
| 1289 | ((bch_val1 >> 28) & 0xF); | ||
| 1290 | *ecc_code++ = ((bch_val1 >> 20) & 0xFF); | ||
| 1291 | *ecc_code++ = ((bch_val1 >> 12) & 0xFF); | ||
| 1292 | *ecc_code++ = ((bch_val1 >> 4) & 0xFF); | ||
| 1293 | *ecc_code++ = ((bch_val1 & 0xF) << 4); | ||
| 1294 | /* | ||
| 1295 | * Setting 8th byte to zero to handle | ||
| 1296 | * erased page | ||
| 1297 | */ | ||
| 1298 | *ecc_code++ = 0x0; | ||
| 1299 | } | ||
| 1300 | } | ||
| 1301 | |||
| 1302 | return 0; | ||
| 1303 | } | ||
| 1304 | |||
| 1305 | /** | ||
| 1306 | * erased_sector_bitflips - count bit flips | ||
| 1307 | * @data: data sector buffer | ||
| 1308 | * @oob: oob buffer | ||
| 1309 | * @info: omap_nand_info | ||
| 1310 | * | ||
| 1311 | * Check the bit flips in erased page falls below correctable level. | ||
| 1312 | * If falls below, report the page as erased with correctable bit | ||
| 1313 | * flip, else report as uncorrectable page. | ||
| 1314 | */ | ||
| 1315 | static int erased_sector_bitflips(u_char *data, u_char *oob, | ||
| 1316 | struct omap_nand_info *info) | ||
| 1317 | { | ||
| 1318 | int flip_bits = 0, i; | ||
| 1319 | |||
| 1320 | for (i = 0; i < info->nand.ecc.size; i++) { | ||
| 1321 | flip_bits += hweight8(~data[i]); | ||
| 1322 | if (flip_bits > info->nand.ecc.strength) | ||
| 1323 | return 0; | ||
| 1324 | } | ||
| 1325 | |||
| 1326 | for (i = 0; i < info->nand.ecc.bytes - 1; i++) { | ||
| 1327 | flip_bits += hweight8(~oob[i]); | ||
| 1328 | if (flip_bits > info->nand.ecc.strength) | ||
| 1329 | return 0; | ||
| 1330 | } | ||
| 1331 | |||
| 1332 | /* | ||
| 1333 | * Bit flips falls in correctable level. | ||
| 1334 | * Fill data area with 0xFF | ||
| 1335 | */ | ||
| 1336 | if (flip_bits) { | ||
| 1337 | memset(data, 0xFF, info->nand.ecc.size); | ||
| 1338 | memset(oob, 0xFF, info->nand.ecc.bytes); | ||
| 1339 | } | ||
| 1340 | |||
| 1341 | return flip_bits; | ||
| 1342 | } | ||
| 1343 | |||
| 1344 | /** | ||
| 1345 | * omap_elm_correct_data - corrects page data area in case error reported | ||
| 1346 | * @mtd: MTD device structure | ||
| 1347 | * @data: page data | ||
| 1348 | * @read_ecc: ecc read from nand flash | ||
| 1349 | * @calc_ecc: ecc read from HW ECC registers | ||
| 1350 | * | ||
| 1351 | * Calculated ecc vector reported as zero in case of non-error pages. | ||
| 1352 | * In case of error/erased pages non-zero error vector is reported. | ||
| 1353 | * In case of non-zero ecc vector, check read_ecc at fixed offset | ||
| 1354 | * (x = 13/7 in case of BCH8/4 == 0) to find page programmed or not. | ||
| 1355 | * To handle bit flips in this data, count the number of 0's in | ||
| 1356 | * read_ecc[x] and check if it greater than 4. If it is less, it is | ||
| 1357 | * programmed page, else erased page. | ||
| 1358 | * | ||
| 1359 | * 1. If page is erased, check with standard ecc vector (ecc vector | ||
| 1360 | * for erased page to find any bit flip). If check fails, bit flip | ||
| 1361 | * is present in erased page. Count the bit flips in erased page and | ||
| 1362 | * if it falls under correctable level, report page with 0xFF and | ||
| 1363 | * update the correctable bit information. | ||
| 1364 | * 2. If error is reported on programmed page, update elm error | ||
| 1365 | * vector and correct the page with ELM error correction routine. | ||
| 1366 | * | ||
| 1367 | */ | ||
| 1368 | static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, | ||
| 1369 | u_char *read_ecc, u_char *calc_ecc) | ||
| 1370 | { | ||
| 1371 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, | ||
| 1372 | mtd); | ||
| 1373 | int eccsteps = info->nand.ecc.steps; | ||
| 1374 | int i , j, stat = 0; | ||
| 1375 | int eccsize, eccflag, ecc_vector_size; | ||
| 1376 | struct elm_errorvec err_vec[ERROR_VECTOR_MAX]; | ||
| 1377 | u_char *ecc_vec = calc_ecc; | ||
| 1378 | u_char *spare_ecc = read_ecc; | ||
| 1379 | u_char *erased_ecc_vec; | ||
| 1380 | enum bch_ecc type; | ||
| 1381 | bool is_error_reported = false; | ||
| 1382 | |||
| 1383 | /* Initialize elm error vector to zero */ | ||
| 1384 | memset(err_vec, 0, sizeof(err_vec)); | ||
| 1385 | |||
| 1386 | if (info->nand.ecc.strength == BCH8_MAX_ERROR) { | ||
| 1387 | type = BCH8_ECC; | ||
| 1388 | erased_ecc_vec = bch8_vector; | ||
| 1389 | } else { | ||
| 1390 | type = BCH4_ECC; | ||
| 1391 | erased_ecc_vec = bch4_vector; | ||
| 1392 | } | ||
| 1393 | |||
| 1394 | ecc_vector_size = info->nand.ecc.bytes; | ||
| 1395 | |||
| 1396 | /* | ||
| 1397 | * Remove extra byte padding for BCH8 RBL | ||
| 1398 | * compatibility and erased page handling | ||
| 1399 | */ | ||
| 1400 | eccsize = ecc_vector_size - 1; | ||
| 1401 | |||
| 1402 | for (i = 0; i < eccsteps ; i++) { | ||
| 1403 | eccflag = 0; /* initialize eccflag */ | ||
| 1404 | |||
| 1405 | /* | ||
| 1406 | * Check any error reported, | ||
| 1407 | * In case of error, non zero ecc reported. | ||
| 1408 | */ | ||
| 1409 | |||
| 1410 | for (j = 0; (j < eccsize); j++) { | ||
| 1411 | if (calc_ecc[j] != 0) { | ||
| 1412 | eccflag = 1; /* non zero ecc, error present */ | ||
| 1413 | break; | ||
| 1414 | } | ||
| 1415 | } | ||
| 1416 | |||
| 1417 | if (eccflag == 1) { | ||
| 1418 | /* | ||
| 1419 | * Set threshold to minimum of 4, half of ecc.strength/2 | ||
| 1420 | * to allow max bit flip in byte to 4 | ||
| 1421 | */ | ||
| 1422 | unsigned int threshold = min_t(unsigned int, 4, | ||
| 1423 | info->nand.ecc.strength / 2); | ||
| 1424 | |||
| 1425 | /* | ||
| 1426 | * Check data area is programmed by counting | ||
| 1427 | * number of 0's at fixed offset in spare area. | ||
| 1428 | * Checking count of 0's against threshold. | ||
| 1429 | * In case programmed page expects at least threshold | ||
| 1430 | * zeros in byte. | ||
| 1431 | * If zeros are less than threshold for programmed page/ | ||
| 1432 | * zeros are more than threshold erased page, either | ||
| 1433 | * case page reported as uncorrectable. | ||
| 1434 | */ | ||
| 1435 | if (hweight8(~read_ecc[eccsize]) >= threshold) { | ||
| 1436 | /* | ||
| 1437 | * Update elm error vector as | ||
| 1438 | * data area is programmed | ||
| 1439 | */ | ||
| 1440 | err_vec[i].error_reported = true; | ||
| 1441 | is_error_reported = true; | ||
| 1442 | } else { | ||
| 1443 | /* Error reported in erased page */ | ||
| 1444 | int bitflip_count; | ||
| 1445 | u_char *buf = &data[info->nand.ecc.size * i]; | ||
| 1446 | |||
| 1447 | if (memcmp(calc_ecc, erased_ecc_vec, eccsize)) { | ||
| 1448 | bitflip_count = erased_sector_bitflips( | ||
| 1449 | buf, read_ecc, info); | ||
| 1450 | |||
| 1451 | if (bitflip_count) | ||
| 1452 | stat += bitflip_count; | ||
| 1453 | else | ||
| 1454 | return -EINVAL; | ||
| 1455 | } | ||
| 1456 | } | ||
| 1457 | } | ||
| 1458 | |||
| 1459 | /* Update the ecc vector */ | ||
| 1460 | calc_ecc += ecc_vector_size; | ||
| 1461 | read_ecc += ecc_vector_size; | ||
| 1462 | } | ||
| 1463 | |||
| 1464 | /* Check if any error reported */ | ||
| 1465 | if (!is_error_reported) | ||
| 1466 | return 0; | ||
| 1467 | |||
| 1468 | /* Decode BCH error using ELM module */ | ||
| 1469 | elm_decode_bch_error_page(info->elm_dev, ecc_vec, err_vec); | ||
| 1470 | |||
| 1471 | for (i = 0; i < eccsteps; i++) { | ||
| 1472 | if (err_vec[i].error_reported) { | ||
| 1473 | for (j = 0; j < err_vec[i].error_count; j++) { | ||
| 1474 | u32 bit_pos, byte_pos, error_max, pos; | ||
| 1475 | |||
| 1476 | if (type == BCH8_ECC) | ||
| 1477 | error_max = BCH8_ECC_MAX; | ||
| 1478 | else | ||
| 1479 | error_max = BCH4_ECC_MAX; | ||
| 1480 | |||
| 1481 | if (info->nand.ecc.strength == BCH8_MAX_ERROR) | ||
| 1482 | pos = err_vec[i].error_loc[j]; | ||
| 1483 | else | ||
| 1484 | /* Add 4 to take care 4 bit padding */ | ||
| 1485 | pos = err_vec[i].error_loc[j] + | ||
| 1486 | BCH4_BIT_PAD; | ||
| 1487 | |||
| 1488 | /* Calculate bit position of error */ | ||
| 1489 | bit_pos = pos % 8; | ||
| 1490 | |||
| 1491 | /* Calculate byte position of error */ | ||
| 1492 | byte_pos = (error_max - pos - 1) / 8; | ||
| 1493 | |||
| 1494 | if (pos < error_max) { | ||
| 1495 | if (byte_pos < 512) | ||
| 1496 | data[byte_pos] ^= 1 << bit_pos; | ||
| 1497 | else | ||
| 1498 | spare_ecc[byte_pos - 512] ^= | ||
| 1499 | 1 << bit_pos; | ||
| 1500 | } | ||
| 1501 | /* else, not interested to correct ecc */ | ||
| 1502 | } | ||
| 1503 | } | ||
| 1504 | |||
| 1505 | /* Update number of correctable errors */ | ||
| 1506 | stat += err_vec[i].error_count; | ||
| 1507 | |||
| 1508 | /* Update page data with sector size */ | ||
| 1509 | data += info->nand.ecc.size; | ||
| 1510 | spare_ecc += ecc_vector_size; | ||
| 1511 | } | ||
| 1512 | |||
| 1513 | for (i = 0; i < eccsteps; i++) | ||
| 1514 | /* Return error if uncorrectable error present */ | ||
| 1515 | if (err_vec[i].error_uncorrectable) | ||
| 1516 | return -EINVAL; | ||
| 1517 | |||
| 1518 | return stat; | ||
| 1519 | } | ||
| 1520 | |||
| 1521 | /** | ||
| 1165 | * omap3_correct_data_bch - Decode received data and correct errors | 1522 | * omap3_correct_data_bch - Decode received data and correct errors |
| 1166 | * @mtd: MTD device structure | 1523 | * @mtd: MTD device structure |
| 1167 | * @data: page data | 1524 | * @data: page data |
| @@ -1194,6 +1551,92 @@ static int omap3_correct_data_bch(struct mtd_info *mtd, u_char *data, | |||
| 1194 | } | 1551 | } |
| 1195 | 1552 | ||
| 1196 | /** | 1553 | /** |
| 1554 | * omap_write_page_bch - BCH ecc based write page function for entire page | ||
| 1555 | * @mtd: mtd info structure | ||
| 1556 | * @chip: nand chip info structure | ||
| 1557 | * @buf: data buffer | ||
| 1558 | * @oob_required: must write chip->oob_poi to OOB | ||
| 1559 | * | ||
| 1560 | * Custom write page method evolved to support multi sector writing in one shot | ||
| 1561 | */ | ||
| 1562 | static int omap_write_page_bch(struct mtd_info *mtd, struct nand_chip *chip, | ||
| 1563 | const uint8_t *buf, int oob_required) | ||
| 1564 | { | ||
| 1565 | int i; | ||
| 1566 | uint8_t *ecc_calc = chip->buffers->ecccalc; | ||
| 1567 | uint32_t *eccpos = chip->ecc.layout->eccpos; | ||
| 1568 | |||
| 1569 | /* Enable GPMC ecc engine */ | ||
| 1570 | chip->ecc.hwctl(mtd, NAND_ECC_WRITE); | ||
| 1571 | |||
| 1572 | /* Write data */ | ||
| 1573 | chip->write_buf(mtd, buf, mtd->writesize); | ||
| 1574 | |||
| 1575 | /* Update ecc vector from GPMC result registers */ | ||
| 1576 | chip->ecc.calculate(mtd, buf, &ecc_calc[0]); | ||
| 1577 | |||
| 1578 | for (i = 0; i < chip->ecc.total; i++) | ||
| 1579 | chip->oob_poi[eccpos[i]] = ecc_calc[i]; | ||
| 1580 | |||
| 1581 | /* Write ecc vector to OOB area */ | ||
| 1582 | chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); | ||
| 1583 | return 0; | ||
| 1584 | } | ||
| 1585 | |||
| 1586 | /** | ||
| 1587 | * omap_read_page_bch - BCH ecc based page read function for entire page | ||
| 1588 | * @mtd: mtd info structure | ||
| 1589 | * @chip: nand chip info structure | ||
| 1590 | * @buf: buffer to store read data | ||
| 1591 | * @oob_required: caller requires OOB data read to chip->oob_poi | ||
| 1592 | * @page: page number to read | ||
| 1593 | * | ||
| 1594 | * For BCH ecc scheme, GPMC used for syndrome calculation and ELM module | ||
| 1595 | * used for error correction. | ||
| 1596 | * Custom method evolved to support ELM error correction & multi sector | ||
| 1597 | * reading. On reading page data area is read along with OOB data with | ||
| 1598 | * ecc engine enabled. ecc vector updated after read of OOB data. | ||
| 1599 | * For non error pages ecc vector reported as zero. | ||
| 1600 | */ | ||
| 1601 | static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip, | ||
| 1602 | uint8_t *buf, int oob_required, int page) | ||
| 1603 | { | ||
| 1604 | uint8_t *ecc_calc = chip->buffers->ecccalc; | ||
| 1605 | uint8_t *ecc_code = chip->buffers->ecccode; | ||
| 1606 | uint32_t *eccpos = chip->ecc.layout->eccpos; | ||
| 1607 | uint8_t *oob = &chip->oob_poi[eccpos[0]]; | ||
| 1608 | uint32_t oob_pos = mtd->writesize + chip->ecc.layout->eccpos[0]; | ||
| 1609 | int stat; | ||
| 1610 | unsigned int max_bitflips = 0; | ||
| 1611 | |||
| 1612 | /* Enable GPMC ecc engine */ | ||
| 1613 | chip->ecc.hwctl(mtd, NAND_ECC_READ); | ||
| 1614 | |||
| 1615 | /* Read data */ | ||
| 1616 | chip->read_buf(mtd, buf, mtd->writesize); | ||
| 1617 | |||
| 1618 | /* Read oob bytes */ | ||
| 1619 | chip->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_pos, -1); | ||
| 1620 | chip->read_buf(mtd, oob, chip->ecc.total); | ||
| 1621 | |||
| 1622 | /* Calculate ecc bytes */ | ||
| 1623 | chip->ecc.calculate(mtd, buf, ecc_calc); | ||
| 1624 | |||
| 1625 | memcpy(ecc_code, &chip->oob_poi[eccpos[0]], chip->ecc.total); | ||
| 1626 | |||
| 1627 | stat = chip->ecc.correct(mtd, buf, ecc_code, ecc_calc); | ||
| 1628 | |||
| 1629 | if (stat < 0) { | ||
| 1630 | mtd->ecc_stats.failed++; | ||
| 1631 | } else { | ||
| 1632 | mtd->ecc_stats.corrected += stat; | ||
| 1633 | max_bitflips = max_t(unsigned int, max_bitflips, stat); | ||
| 1634 | } | ||
| 1635 | |||
| 1636 | return max_bitflips; | ||
| 1637 | } | ||
| 1638 | |||
| 1639 | /** | ||
| 1197 | * omap3_free_bch - Release BCH ecc resources | 1640 | * omap3_free_bch - Release BCH ecc resources |
| 1198 | * @mtd: MTD device structure | 1641 | * @mtd: MTD device structure |
| 1199 | */ | 1642 | */ |
| @@ -1218,43 +1661,86 @@ static int omap3_init_bch(struct mtd_info *mtd, int ecc_opt) | |||
| 1218 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, | 1661 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, |
| 1219 | mtd); | 1662 | mtd); |
| 1220 | #ifdef CONFIG_MTD_NAND_OMAP_BCH8 | 1663 | #ifdef CONFIG_MTD_NAND_OMAP_BCH8 |
| 1221 | const int hw_errors = 8; | 1664 | const int hw_errors = BCH8_MAX_ERROR; |
| 1222 | #else | 1665 | #else |
| 1223 | const int hw_errors = 4; | 1666 | const int hw_errors = BCH4_MAX_ERROR; |
| 1224 | #endif | 1667 | #endif |
| 1668 | enum bch_ecc bch_type; | ||
| 1669 | const __be32 *parp; | ||
| 1670 | int lenp; | ||
| 1671 | struct device_node *elm_node; | ||
| 1672 | |||
| 1225 | info->bch = NULL; | 1673 | info->bch = NULL; |
| 1226 | 1674 | ||
| 1227 | max_errors = (ecc_opt == OMAP_ECC_BCH8_CODE_HW) ? 8 : 4; | 1675 | max_errors = (ecc_opt == OMAP_ECC_BCH8_CODE_HW) ? |
| 1676 | BCH8_MAX_ERROR : BCH4_MAX_ERROR; | ||
| 1228 | if (max_errors != hw_errors) { | 1677 | if (max_errors != hw_errors) { |
| 1229 | pr_err("cannot configure %d-bit BCH ecc, only %d-bit supported", | 1678 | pr_err("cannot configure %d-bit BCH ecc, only %d-bit supported", |
| 1230 | max_errors, hw_errors); | 1679 | max_errors, hw_errors); |
| 1231 | goto fail; | 1680 | goto fail; |
| 1232 | } | 1681 | } |
| 1233 | 1682 | ||
| 1234 | /* software bch library is only used to detect and locate errors */ | 1683 | info->nand.ecc.size = 512; |
| 1235 | info->bch = init_bch(13, max_errors, 0x201b /* hw polynomial */); | 1684 | info->nand.ecc.hwctl = omap3_enable_hwecc_bch; |
| 1236 | if (!info->bch) | 1685 | info->nand.ecc.mode = NAND_ECC_HW; |
| 1237 | goto fail; | 1686 | info->nand.ecc.strength = max_errors; |
| 1238 | 1687 | ||
| 1239 | info->nand.ecc.size = 512; | 1688 | if (hw_errors == BCH8_MAX_ERROR) |
| 1240 | info->nand.ecc.hwctl = omap3_enable_hwecc_bch; | 1689 | bch_type = BCH8_ECC; |
| 1241 | info->nand.ecc.correct = omap3_correct_data_bch; | 1690 | else |
| 1242 | info->nand.ecc.mode = NAND_ECC_HW; | 1691 | bch_type = BCH4_ECC; |
| 1243 | 1692 | ||
| 1244 | /* | 1693 | /* Detect availability of ELM module */ |
| 1245 | * The number of corrected errors in an ecc block that will trigger | 1694 | parp = of_get_property(info->of_node, "elm_id", &lenp); |
| 1246 | * block scrubbing defaults to the ecc strength (4 or 8). | 1695 | if ((parp == NULL) && (lenp != (sizeof(void *) * 2))) { |
| 1247 | * Set mtd->bitflip_threshold here to define a custom threshold. | 1696 | pr_err("Missing elm_id property, fall back to Software BCH\n"); |
| 1248 | */ | 1697 | info->is_elm_used = false; |
| 1698 | } else { | ||
| 1699 | struct platform_device *pdev; | ||
| 1249 | 1700 | ||
| 1250 | if (max_errors == 8) { | 1701 | elm_node = of_find_node_by_phandle(be32_to_cpup(parp)); |
| 1251 | info->nand.ecc.strength = 8; | 1702 | pdev = of_find_device_by_node(elm_node); |
| 1252 | info->nand.ecc.bytes = 13; | 1703 | info->elm_dev = &pdev->dev; |
| 1253 | info->nand.ecc.calculate = omap3_calculate_ecc_bch8; | 1704 | elm_config(info->elm_dev, bch_type); |
| 1705 | info->is_elm_used = true; | ||
| 1706 | } | ||
| 1707 | |||
| 1708 | if (info->is_elm_used && (mtd->writesize <= 4096)) { | ||
| 1709 | |||
| 1710 | if (hw_errors == BCH8_MAX_ERROR) | ||
| 1711 | info->nand.ecc.bytes = BCH8_SIZE; | ||
| 1712 | else | ||
| 1713 | info->nand.ecc.bytes = BCH4_SIZE; | ||
| 1714 | |||
| 1715 | info->nand.ecc.correct = omap_elm_correct_data; | ||
| 1716 | info->nand.ecc.calculate = omap3_calculate_ecc_bch; | ||
| 1717 | info->nand.ecc.read_page = omap_read_page_bch; | ||
| 1718 | info->nand.ecc.write_page = omap_write_page_bch; | ||
| 1254 | } else { | 1719 | } else { |
| 1255 | info->nand.ecc.strength = 4; | 1720 | /* |
| 1256 | info->nand.ecc.bytes = 7; | 1721 | * software bch library is only used to detect and |
| 1257 | info->nand.ecc.calculate = omap3_calculate_ecc_bch4; | 1722 | * locate errors |
| 1723 | */ | ||
| 1724 | info->bch = init_bch(13, max_errors, | ||
| 1725 | 0x201b /* hw polynomial */); | ||
| 1726 | if (!info->bch) | ||
| 1727 | goto fail; | ||
| 1728 | |||
| 1729 | info->nand.ecc.correct = omap3_correct_data_bch; | ||
| 1730 | |||
| 1731 | /* | ||
| 1732 | * The number of corrected errors in an ecc block that will | ||
| 1733 | * trigger block scrubbing defaults to the ecc strength (4 or 8) | ||
| 1734 | * Set mtd->bitflip_threshold here to define a custom threshold. | ||
| 1735 | */ | ||
| 1736 | |||
| 1737 | if (max_errors == 8) { | ||
| 1738 | info->nand.ecc.bytes = 13; | ||
| 1739 | info->nand.ecc.calculate = omap3_calculate_ecc_bch8; | ||
| 1740 | } else { | ||
| 1741 | info->nand.ecc.bytes = 7; | ||
| 1742 | info->nand.ecc.calculate = omap3_calculate_ecc_bch4; | ||
| 1743 | } | ||
| 1258 | } | 1744 | } |
| 1259 | 1745 | ||
| 1260 | pr_info("enabling NAND BCH ecc with %d-bit correction\n", max_errors); | 1746 | pr_info("enabling NAND BCH ecc with %d-bit correction\n", max_errors); |
| @@ -1270,7 +1756,7 @@ fail: | |||
| 1270 | */ | 1756 | */ |
| 1271 | static int omap3_init_bch_tail(struct mtd_info *mtd) | 1757 | static int omap3_init_bch_tail(struct mtd_info *mtd) |
| 1272 | { | 1758 | { |
| 1273 | int i, steps; | 1759 | int i, steps, offset; |
| 1274 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, | 1760 | struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, |
| 1275 | mtd); | 1761 | mtd); |
| 1276 | struct nand_ecclayout *layout = &info->ecclayout; | 1762 | struct nand_ecclayout *layout = &info->ecclayout; |
| @@ -1292,11 +1778,21 @@ static int omap3_init_bch_tail(struct mtd_info *mtd) | |||
| 1292 | goto fail; | 1778 | goto fail; |
| 1293 | } | 1779 | } |
| 1294 | 1780 | ||
| 1781 | /* ECC layout compatible with RBL for BCH8 */ | ||
| 1782 | if (info->is_elm_used && (info->nand.ecc.bytes == BCH8_SIZE)) | ||
| 1783 | offset = 2; | ||
| 1784 | else | ||
| 1785 | offset = mtd->oobsize - layout->eccbytes; | ||
| 1786 | |||
| 1295 | /* put ecc bytes at oob tail */ | 1787 | /* put ecc bytes at oob tail */ |
| 1296 | for (i = 0; i < layout->eccbytes; i++) | 1788 | for (i = 0; i < layout->eccbytes; i++) |
| 1297 | layout->eccpos[i] = mtd->oobsize-layout->eccbytes+i; | 1789 | layout->eccpos[i] = offset + i; |
| 1790 | |||
| 1791 | if (info->is_elm_used && (info->nand.ecc.bytes == BCH8_SIZE)) | ||
| 1792 | layout->oobfree[0].offset = 2 + layout->eccbytes * steps; | ||
| 1793 | else | ||
| 1794 | layout->oobfree[0].offset = 2; | ||
| 1298 | 1795 | ||
| 1299 | layout->oobfree[0].offset = 2; | ||
| 1300 | layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes; | 1796 | layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes; |
| 1301 | info->nand.ecc.layout = layout; | 1797 | info->nand.ecc.layout = layout; |
| 1302 | 1798 | ||
| @@ -1360,6 +1856,9 @@ static int omap_nand_probe(struct platform_device *pdev) | |||
| 1360 | 1856 | ||
| 1361 | info->nand.options = pdata->devsize; | 1857 | info->nand.options = pdata->devsize; |
| 1362 | info->nand.options |= NAND_SKIP_BBTSCAN; | 1858 | info->nand.options |= NAND_SKIP_BBTSCAN; |
| 1859 | #ifdef CONFIG_MTD_NAND_OMAP_BCH | ||
| 1860 | info->of_node = pdata->of_node; | ||
| 1861 | #endif | ||
| 1363 | 1862 | ||
| 1364 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1863 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 1365 | if (res == NULL) { | 1864 | if (res == NULL) { |
diff --git a/drivers/mtd/ofpart.c b/drivers/mtd/ofpart.c index dbd3aa574eaf..30bd907a260a 100644 --- a/drivers/mtd/ofpart.c +++ b/drivers/mtd/ofpart.c | |||
| @@ -174,7 +174,14 @@ out: | |||
| 174 | return rc; | 174 | return rc; |
| 175 | } | 175 | } |
| 176 | 176 | ||
| 177 | static void __exit ofpart_parser_exit(void) | ||
| 178 | { | ||
| 179 | deregister_mtd_parser(&ofpart_parser); | ||
| 180 | deregister_mtd_parser(&ofoldpart_parser); | ||
| 181 | } | ||
| 182 | |||
| 177 | module_init(ofpart_parser_init); | 183 | module_init(ofpart_parser_init); |
| 184 | module_exit(ofpart_parser_exit); | ||
| 178 | 185 | ||
| 179 | MODULE_LICENSE("GPL"); | 186 | MODULE_LICENSE("GPL"); |
| 180 | MODULE_DESCRIPTION("Parser for MTD partitioning information in device tree"); | 187 | MODULE_DESCRIPTION("Parser for MTD partitioning information in device tree"); |
diff --git a/drivers/mtd/tests/mtd_nandecctest.c b/drivers/mtd/tests/mtd_nandecctest.c index db2f22e7966a..70106607c247 100644 --- a/drivers/mtd/tests/mtd_nandecctest.c +++ b/drivers/mtd/tests/mtd_nandecctest.c | |||
| @@ -44,7 +44,7 @@ struct nand_ecc_test { | |||
| 44 | static void single_bit_error_data(void *error_data, void *correct_data, | 44 | static void single_bit_error_data(void *error_data, void *correct_data, |
| 45 | size_t size) | 45 | size_t size) |
| 46 | { | 46 | { |
| 47 | unsigned int offset = random32() % (size * BITS_PER_BYTE); | 47 | unsigned int offset = prandom_u32() % (size * BITS_PER_BYTE); |
| 48 | 48 | ||
| 49 | memcpy(error_data, correct_data, size); | 49 | memcpy(error_data, correct_data, size); |
| 50 | __change_bit_le(offset, error_data); | 50 | __change_bit_le(offset, error_data); |
| @@ -55,9 +55,9 @@ static void double_bit_error_data(void *error_data, void *correct_data, | |||
| 55 | { | 55 | { |
| 56 | unsigned int offset[2]; | 56 | unsigned int offset[2]; |
| 57 | 57 | ||
| 58 | offset[0] = random32() % (size * BITS_PER_BYTE); | 58 | offset[0] = prandom_u32() % (size * BITS_PER_BYTE); |
| 59 | do { | 59 | do { |
| 60 | offset[1] = random32() % (size * BITS_PER_BYTE); | 60 | offset[1] = prandom_u32() % (size * BITS_PER_BYTE); |
| 61 | } while (offset[0] == offset[1]); | 61 | } while (offset[0] == offset[1]); |
| 62 | 62 | ||
| 63 | memcpy(error_data, correct_data, size); | 63 | memcpy(error_data, correct_data, size); |
| @@ -68,7 +68,7 @@ static void double_bit_error_data(void *error_data, void *correct_data, | |||
| 68 | 68 | ||
| 69 | static unsigned int random_ecc_bit(size_t size) | 69 | static unsigned int random_ecc_bit(size_t size) |
| 70 | { | 70 | { |
| 71 | unsigned int offset = random32() % (3 * BITS_PER_BYTE); | 71 | unsigned int offset = prandom_u32() % (3 * BITS_PER_BYTE); |
| 72 | 72 | ||
| 73 | if (size == 256) { | 73 | if (size == 256) { |
| 74 | /* | 74 | /* |
| @@ -76,7 +76,7 @@ static unsigned int random_ecc_bit(size_t size) | |||
| 76 | * and 17th bit) in ECC code for 256 byte data block | 76 | * and 17th bit) in ECC code for 256 byte data block |
| 77 | */ | 77 | */ |
| 78 | while (offset == 16 || offset == 17) | 78 | while (offset == 16 || offset == 17) |
| 79 | offset = random32() % (3 * BITS_PER_BYTE); | 79 | offset = prandom_u32() % (3 * BITS_PER_BYTE); |
| 80 | } | 80 | } |
| 81 | 81 | ||
| 82 | return offset; | 82 | return offset; |
diff --git a/drivers/mtd/tests/mtd_stresstest.c b/drivers/mtd/tests/mtd_stresstest.c index 2d7e6cffd6d4..787f539d16ca 100644 --- a/drivers/mtd/tests/mtd_stresstest.c +++ b/drivers/mtd/tests/mtd_stresstest.c | |||
| @@ -55,7 +55,7 @@ static int rand_eb(void) | |||
| 55 | unsigned int eb; | 55 | unsigned int eb; |
| 56 | 56 | ||
| 57 | again: | 57 | again: |
| 58 | eb = random32(); | 58 | eb = prandom_u32(); |
| 59 | /* Read or write up 2 eraseblocks at a time - hence 'ebcnt - 1' */ | 59 | /* Read or write up 2 eraseblocks at a time - hence 'ebcnt - 1' */ |
| 60 | eb %= (ebcnt - 1); | 60 | eb %= (ebcnt - 1); |
| 61 | if (bbt[eb]) | 61 | if (bbt[eb]) |
| @@ -67,7 +67,7 @@ static int rand_offs(void) | |||
| 67 | { | 67 | { |
| 68 | unsigned int offs; | 68 | unsigned int offs; |
| 69 | 69 | ||
| 70 | offs = random32(); | 70 | offs = prandom_u32(); |
| 71 | offs %= bufsize; | 71 | offs %= bufsize; |
| 72 | return offs; | 72 | return offs; |
| 73 | } | 73 | } |
| @@ -76,7 +76,7 @@ static int rand_len(int offs) | |||
| 76 | { | 76 | { |
| 77 | unsigned int len; | 77 | unsigned int len; |
| 78 | 78 | ||
| 79 | len = random32(); | 79 | len = prandom_u32(); |
| 80 | len %= (bufsize - offs); | 80 | len %= (bufsize - offs); |
| 81 | return len; | 81 | return len; |
| 82 | } | 82 | } |
| @@ -191,7 +191,7 @@ static int do_write(void) | |||
| 191 | 191 | ||
| 192 | static int do_operation(void) | 192 | static int do_operation(void) |
| 193 | { | 193 | { |
| 194 | if (random32() & 1) | 194 | if (prandom_u32() & 1) |
| 195 | return do_read(); | 195 | return do_read(); |
| 196 | else | 196 | else |
| 197 | return do_write(); | 197 | return do_write(); |
diff --git a/drivers/mtd/tests/mtd_torturetest.c b/drivers/mtd/tests/mtd_torturetest.c index c4cde1e9eddb..3a9f6a6a79f9 100644 --- a/drivers/mtd/tests/mtd_torturetest.c +++ b/drivers/mtd/tests/mtd_torturetest.c | |||
| @@ -208,7 +208,7 @@ static inline int write_pattern(int ebnum, void *buf) | |||
| 208 | static int __init tort_init(void) | 208 | static int __init tort_init(void) |
| 209 | { | 209 | { |
| 210 | int err = 0, i, infinite = !cycles_count; | 210 | int err = 0, i, infinite = !cycles_count; |
| 211 | int bad_ebs[ebcnt]; | 211 | int *bad_ebs; |
| 212 | 212 | ||
| 213 | printk(KERN_INFO "\n"); | 213 | printk(KERN_INFO "\n"); |
| 214 | printk(KERN_INFO "=================================================\n"); | 214 | printk(KERN_INFO "=================================================\n"); |
| @@ -250,28 +250,24 @@ static int __init tort_init(void) | |||
| 250 | 250 | ||
| 251 | err = -ENOMEM; | 251 | err = -ENOMEM; |
| 252 | patt_5A5 = kmalloc(mtd->erasesize, GFP_KERNEL); | 252 | patt_5A5 = kmalloc(mtd->erasesize, GFP_KERNEL); |
| 253 | if (!patt_5A5) { | 253 | if (!patt_5A5) |
| 254 | pr_err("error: cannot allocate memory\n"); | ||
| 255 | goto out_mtd; | 254 | goto out_mtd; |
| 256 | } | ||
| 257 | 255 | ||
| 258 | patt_A5A = kmalloc(mtd->erasesize, GFP_KERNEL); | 256 | patt_A5A = kmalloc(mtd->erasesize, GFP_KERNEL); |
| 259 | if (!patt_A5A) { | 257 | if (!patt_A5A) |
| 260 | pr_err("error: cannot allocate memory\n"); | ||
| 261 | goto out_patt_5A5; | 258 | goto out_patt_5A5; |
| 262 | } | ||
| 263 | 259 | ||
| 264 | patt_FF = kmalloc(mtd->erasesize, GFP_KERNEL); | 260 | patt_FF = kmalloc(mtd->erasesize, GFP_KERNEL); |
| 265 | if (!patt_FF) { | 261 | if (!patt_FF) |
| 266 | pr_err("error: cannot allocate memory\n"); | ||
| 267 | goto out_patt_A5A; | 262 | goto out_patt_A5A; |
| 268 | } | ||
| 269 | 263 | ||
| 270 | check_buf = kmalloc(mtd->erasesize, GFP_KERNEL); | 264 | check_buf = kmalloc(mtd->erasesize, GFP_KERNEL); |
| 271 | if (!check_buf) { | 265 | if (!check_buf) |
| 272 | pr_err("error: cannot allocate memory\n"); | ||
| 273 | goto out_patt_FF; | 266 | goto out_patt_FF; |
| 274 | } | 267 | |
| 268 | bad_ebs = kcalloc(ebcnt, sizeof(*bad_ebs), GFP_KERNEL); | ||
| 269 | if (!bad_ebs) | ||
| 270 | goto out_check_buf; | ||
| 275 | 271 | ||
| 276 | err = 0; | 272 | err = 0; |
| 277 | 273 | ||
| @@ -290,7 +286,6 @@ static int __init tort_init(void) | |||
| 290 | /* | 286 | /* |
| 291 | * Check if there is a bad eraseblock among those we are going to test. | 287 | * Check if there is a bad eraseblock among those we are going to test. |
| 292 | */ | 288 | */ |
| 293 | memset(&bad_ebs[0], 0, sizeof(int) * ebcnt); | ||
| 294 | if (mtd_can_have_bb(mtd)) { | 289 | if (mtd_can_have_bb(mtd)) { |
| 295 | for (i = eb; i < eb + ebcnt; i++) { | 290 | for (i = eb; i < eb + ebcnt; i++) { |
| 296 | err = mtd_block_isbad(mtd, (loff_t)i * mtd->erasesize); | 291 | err = mtd_block_isbad(mtd, (loff_t)i * mtd->erasesize); |
| @@ -394,6 +389,8 @@ out: | |||
| 394 | 389 | ||
| 395 | pr_info("finished after %u erase cycles\n", | 390 | pr_info("finished after %u erase cycles\n", |
| 396 | erase_cycles); | 391 | erase_cycles); |
| 392 | kfree(bad_ebs); | ||
| 393 | out_check_buf: | ||
| 397 | kfree(check_buf); | 394 | kfree(check_buf); |
| 398 | out_patt_FF: | 395 | out_patt_FF: |
| 399 | kfree(patt_FF); | 396 | kfree(patt_FF); |
diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h index 33f8f3b2c9b2..cba89fcd1587 100644 --- a/drivers/mtd/ubi/debug.h +++ b/drivers/mtd/ubi/debug.h | |||
| @@ -86,7 +86,7 @@ static inline int ubi_dbg_is_bgt_disabled(const struct ubi_device *ubi) | |||
| 86 | static inline int ubi_dbg_is_bitflip(const struct ubi_device *ubi) | 86 | static inline int ubi_dbg_is_bitflip(const struct ubi_device *ubi) |
| 87 | { | 87 | { |
| 88 | if (ubi->dbg.emulate_bitflips) | 88 | if (ubi->dbg.emulate_bitflips) |
| 89 | return !(random32() % 200); | 89 | return !(prandom_u32() % 200); |
| 90 | return 0; | 90 | return 0; |
| 91 | } | 91 | } |
| 92 | 92 | ||
| @@ -100,7 +100,7 @@ static inline int ubi_dbg_is_bitflip(const struct ubi_device *ubi) | |||
| 100 | static inline int ubi_dbg_is_write_failure(const struct ubi_device *ubi) | 100 | static inline int ubi_dbg_is_write_failure(const struct ubi_device *ubi) |
| 101 | { | 101 | { |
| 102 | if (ubi->dbg.emulate_io_failures) | 102 | if (ubi->dbg.emulate_io_failures) |
| 103 | return !(random32() % 500); | 103 | return !(prandom_u32() % 500); |
| 104 | return 0; | 104 | return 0; |
| 105 | } | 105 | } |
| 106 | 106 | ||
| @@ -114,7 +114,7 @@ static inline int ubi_dbg_is_write_failure(const struct ubi_device *ubi) | |||
| 114 | static inline int ubi_dbg_is_erase_failure(const struct ubi_device *ubi) | 114 | static inline int ubi_dbg_is_erase_failure(const struct ubi_device *ubi) |
| 115 | { | 115 | { |
| 116 | if (ubi->dbg.emulate_io_failures) | 116 | if (ubi->dbg.emulate_io_failures) |
| 117 | return !(random32() % 400); | 117 | return !(prandom_u32() % 400); |
| 118 | return 0; | 118 | return 0; |
| 119 | } | 119 | } |
| 120 | 120 | ||
diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h index 1d002b58b60b..8390c474f69a 100644 --- a/include/linux/bcma/bcma_driver_chipcommon.h +++ b/include/linux/bcma/bcma_driver_chipcommon.h | |||
| @@ -528,6 +528,7 @@ struct bcma_sflash { | |||
| 528 | u32 size; | 528 | u32 size; |
| 529 | 529 | ||
| 530 | struct mtd_info *mtd; | 530 | struct mtd_info *mtd; |
| 531 | void *priv; | ||
| 531 | }; | 532 | }; |
| 532 | #endif | 533 | #endif |
| 533 | 534 | ||
diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h index f6eb4332ac92..4b02512e421c 100644 --- a/include/linux/mtd/map.h +++ b/include/linux/mtd/map.h | |||
| @@ -245,6 +245,7 @@ struct map_info { | |||
| 245 | unsigned long pfow_base; | 245 | unsigned long pfow_base; |
| 246 | unsigned long map_priv_1; | 246 | unsigned long map_priv_1; |
| 247 | unsigned long map_priv_2; | 247 | unsigned long map_priv_2; |
| 248 | struct device_node *device_node; | ||
| 248 | void *fldrv_priv; | 249 | void *fldrv_priv; |
| 249 | struct mtd_chip_driver *fldrv; | 250 | struct mtd_chip_driver *fldrv; |
| 250 | }; | 251 | }; |
| @@ -328,7 +329,7 @@ static inline int map_word_bitsset(struct map_info *map, map_word val1, map_word | |||
| 328 | 329 | ||
| 329 | static inline map_word map_word_load(struct map_info *map, const void *ptr) | 330 | static inline map_word map_word_load(struct map_info *map, const void *ptr) |
| 330 | { | 331 | { |
| 331 | map_word r = {{0} }; | 332 | map_word r; |
| 332 | 333 | ||
| 333 | if (map_bankwidth_is_1(map)) | 334 | if (map_bankwidth_is_1(map)) |
| 334 | r.x[0] = *(unsigned char *)ptr; | 335 | r.x[0] = *(unsigned char *)ptr; |
| @@ -342,6 +343,8 @@ static inline map_word map_word_load(struct map_info *map, const void *ptr) | |||
| 342 | #endif | 343 | #endif |
| 343 | else if (map_bankwidth_is_large(map)) | 344 | else if (map_bankwidth_is_large(map)) |
| 344 | memcpy(r.x, ptr, map->bankwidth); | 345 | memcpy(r.x, ptr, map->bankwidth); |
| 346 | else | ||
| 347 | BUG(); | ||
| 345 | 348 | ||
| 346 | return r; | 349 | return r; |
| 347 | } | 350 | } |
| @@ -391,7 +394,7 @@ static inline map_word map_word_ff(struct map_info *map) | |||
| 391 | 394 | ||
| 392 | static inline map_word inline_map_read(struct map_info *map, unsigned long ofs) | 395 | static inline map_word inline_map_read(struct map_info *map, unsigned long ofs) |
| 393 | { | 396 | { |
| 394 | map_word uninitialized_var(r); | 397 | map_word r; |
| 395 | 398 | ||
| 396 | if (map_bankwidth_is_1(map)) | 399 | if (map_bankwidth_is_1(map)) |
| 397 | r.x[0] = __raw_readb(map->virt + ofs); | 400 | r.x[0] = __raw_readb(map->virt + ofs); |
| @@ -425,6 +428,8 @@ static inline void inline_map_write(struct map_info *map, const map_word datum, | |||
| 425 | #endif | 428 | #endif |
| 426 | else if (map_bankwidth_is_large(map)) | 429 | else if (map_bankwidth_is_large(map)) |
| 427 | memcpy_toio(map->virt+ofs, datum.x, map->bankwidth); | 430 | memcpy_toio(map->virt+ofs, datum.x, map->bankwidth); |
| 431 | else | ||
| 432 | BUG(); | ||
| 428 | mb(); | 433 | mb(); |
| 429 | } | 434 | } |
| 430 | 435 | ||
diff --git a/include/linux/platform_data/elm.h b/include/linux/platform_data/elm.h new file mode 100644 index 000000000000..1bd5244d1dcd --- /dev/null +++ b/include/linux/platform_data/elm.h | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | /* | ||
| 2 | * BCH Error Location Module | ||
| 3 | * | ||
| 4 | * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.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; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | */ | ||
| 17 | |||
| 18 | #ifndef __ELM_H | ||
| 19 | #define __ELM_H | ||
| 20 | |||
| 21 | enum bch_ecc { | ||
| 22 | BCH4_ECC = 0, | ||
| 23 | BCH8_ECC, | ||
| 24 | }; | ||
| 25 | |||
| 26 | /* ELM support 8 error syndrome process */ | ||
| 27 | #define ERROR_VECTOR_MAX 8 | ||
| 28 | |||
| 29 | #define BCH8_ECC_OOB_BYTES 13 | ||
| 30 | #define BCH4_ECC_OOB_BYTES 7 | ||
| 31 | /* RBL requires 14 byte even though BCH8 uses only 13 byte */ | ||
| 32 | #define BCH8_SIZE (BCH8_ECC_OOB_BYTES + 1) | ||
| 33 | /* Uses 1 extra byte to handle erased pages */ | ||
| 34 | #define BCH4_SIZE (BCH4_ECC_OOB_BYTES + 1) | ||
| 35 | |||
| 36 | /** | ||
| 37 | * struct elm_errorvec - error vector for elm | ||
| 38 | * @error_reported: set true for vectors error is reported | ||
| 39 | * @error_uncorrectable: number of uncorrectable errors | ||
| 40 | * @error_count: number of correctable errors in the sector | ||
| 41 | * @error_loc: buffer for error location | ||
| 42 | * | ||
| 43 | */ | ||
| 44 | struct elm_errorvec { | ||
| 45 | bool error_reported; | ||
| 46 | bool error_uncorrectable; | ||
| 47 | int error_count; | ||
| 48 | int error_loc[ERROR_VECTOR_MAX]; | ||
| 49 | }; | ||
| 50 | |||
| 51 | void elm_decode_bch_error_page(struct device *dev, u8 *ecc_calc, | ||
| 52 | struct elm_errorvec *err_vec); | ||
| 53 | void elm_config(struct device *dev, enum bch_ecc bch_type); | ||
| 54 | #endif /* __ELM_H */ | ||
