diff options
| -rw-r--r-- | Documentation/devicetree/bindings/mtd/mtd-physmap.txt | 3 | ||||
| -rw-r--r-- | drivers/mtd/chips/cfi_cmdset_0002.c | 217 | ||||
| -rw-r--r-- | drivers/mtd/maps/physmap_of.c | 1 | ||||
| -rw-r--r-- | include/linux/mtd/map.h | 1 |
4 files changed, 222 insertions, 0 deletions
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/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/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index 67cc73c18ddd..263d9e1b8001 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c | |||
| @@ -241,6 +241,7 @@ static int of_flash_probe(struct platform_device *dev) | |||
| 241 | info->list[i].map.phys = res.start; | 241 | info->list[i].map.phys = res.start; |
| 242 | info->list[i].map.size = res_size; | 242 | info->list[i].map.size = res_size; |
| 243 | info->list[i].map.bankwidth = be32_to_cpup(width); | 243 | info->list[i].map.bankwidth = be32_to_cpup(width); |
| 244 | info->list[i].map.device_node = dp; | ||
| 244 | 245 | ||
| 245 | err = -ENOMEM; | 246 | err = -ENOMEM; |
| 246 | info->list[i].map.virt = ioremap(info->list[i].map.phys, | 247 | info->list[i].map.virt = ioremap(info->list[i].map.phys, |
diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h index f6eb4332ac92..8b9bfd7dcaa3 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 | }; |
