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 | }; |