Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mtd: cfi_cmdset_0002: Support Persistent Protection Bits (PPB) locking

Currently cfi_cmdset_0002.c does not support PPB locking of sectors. This
patch adds support for this locking/unlocking mechanism. It is needed on
some platforms, since newer U-Boot versions do support this PPB locking
and protect for example their environment sector(s) this way.

This PPB locking/unlocking will be enabled for all devices supported by
cfi_cmdset_0002 reporting 8 in the CFI word 0x49 (Sector Protect/Unprotect
scheme).

Please note that PPB locking does support sector-by-sector locking. But
the whole chip can only be unlocked together. So unlocking one sector
will automatically unlock all sectors of this device. Because of this
chip limitation, the PPB unlocking function saves the current locking
status of all sectors before unlocking the whole device. After unlocking
the saved locking status is re-configured. This way only the addressed
sectors will be unlocked.

To selectively enable this advanced sector protection mechanism, the
device-tree property "use-advanced-sector-protection" has been created.
To enable support for this locking this property needs to be present in the
flash DT node. E.g.:

nor_flash@0,0 {
compatible = "amd,s29gl256n", "cfi-flash";
bank-width = <2>;
use-advanced-sector-protection;
...

Tested with Spansion S29GL512S10THI and Micron JS28F512M29EWx flash
devices.

Signed-off-by: Stefan Roese <sr@denx.de>
Tested-by: Holger Brunck <holger.brunck@keymile.com>
Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>

authored by

Stefan Roese and committed by
Artem Bityutskiy
1648eaaa 422f3890

+222
+3
Documentation/devicetree/bindings/mtd/mtd-physmap.txt
··· 26 26 - linux,mtd-name: allow to specify the mtd name for retro capability with 27 27 physmap-flash drivers as boot loader pass the mtd partition via the old 28 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 33 For JEDEC compatible devices, the following additional properties 31 34 are defined:
+217
drivers/mtd/chips/cfi_cmdset_0002.c
··· 33 33 #include <linux/delay.h> 34 34 #include <linux/interrupt.h> 35 35 #include <linux/reboot.h> 36 + #include <linux/of.h> 37 + #include <linux/of_platform.h> 36 38 #include <linux/mtd/map.h> 37 39 #include <linux/mtd/mtd.h> 38 40 #include <linux/mtd/cfi.h> ··· 75 73 76 74 static int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); 77 75 static int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); 76 + 77 + static int cfi_ppb_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); 78 + static int cfi_ppb_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); 79 + static int cfi_ppb_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len); 78 80 79 81 static struct mtd_chip_driver cfi_amdstd_chipdrv = { 80 82 .probe = NULL, /* Not usable directly */ ··· 502 496 struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) 503 497 { 504 498 struct cfi_private *cfi = map->fldrv_priv; 499 + struct device_node __maybe_unused *np = map->device_node; 505 500 struct mtd_info *mtd; 506 501 int i; 507 502 ··· 575 568 #ifdef DEBUG_CFI_FEATURES 576 569 /* Tell the user about it in lots of lovely detail */ 577 570 cfi_tell_features(extp); 571 + #endif 572 + 573 + #ifdef CONFIG_OF 574 + if (np && of_property_read_bool( 575 + np, "use-advanced-sector-protection") 576 + && extp->BlkProtUnprot == 8) { 577 + printk(KERN_INFO " Advanced Sector Protection (PPB Locking) supported\n"); 578 + mtd->_lock = cfi_ppb_lock; 579 + mtd->_unlock = cfi_ppb_unlock; 580 + mtd->_is_locked = cfi_ppb_is_locked; 581 + } 578 582 #endif 579 583 580 584 bootloc = extp->TopBottom; ··· 2190 2172 return cfi_varsize_frob(mtd, do_atmel_unlock, ofs, len, NULL); 2191 2173 } 2192 2174 2175 + /* 2176 + * Advanced Sector Protection - PPB (Persistent Protection Bit) locking 2177 + */ 2178 + 2179 + struct ppb_lock { 2180 + struct flchip *chip; 2181 + loff_t offset; 2182 + int locked; 2183 + }; 2184 + 2185 + #define MAX_SECTORS 512 2186 + 2187 + #define DO_XXLOCK_ONEBLOCK_LOCK ((void *)1) 2188 + #define DO_XXLOCK_ONEBLOCK_UNLOCK ((void *)2) 2189 + #define DO_XXLOCK_ONEBLOCK_GETLOCK ((void *)3) 2190 + 2191 + static int __maybe_unused do_ppb_xxlock(struct map_info *map, 2192 + struct flchip *chip, 2193 + unsigned long adr, int len, void *thunk) 2194 + { 2195 + struct cfi_private *cfi = map->fldrv_priv; 2196 + unsigned long timeo; 2197 + int ret; 2198 + 2199 + mutex_lock(&chip->mutex); 2200 + ret = get_chip(map, chip, adr + chip->start, FL_LOCKING); 2201 + if (ret) { 2202 + mutex_unlock(&chip->mutex); 2203 + return ret; 2204 + } 2205 + 2206 + pr_debug("MTD %s(): XXLOCK 0x%08lx len %d\n", __func__, adr, len); 2207 + 2208 + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, 2209 + cfi->device_type, NULL); 2210 + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, 2211 + cfi->device_type, NULL); 2212 + /* PPB entry command */ 2213 + cfi_send_gen_cmd(0xC0, cfi->addr_unlock1, chip->start, map, cfi, 2214 + cfi->device_type, NULL); 2215 + 2216 + if (thunk == DO_XXLOCK_ONEBLOCK_LOCK) { 2217 + chip->state = FL_LOCKING; 2218 + map_write(map, CMD(0xA0), chip->start + adr); 2219 + map_write(map, CMD(0x00), chip->start + adr); 2220 + } else if (thunk == DO_XXLOCK_ONEBLOCK_UNLOCK) { 2221 + /* 2222 + * Unlocking of one specific sector is not supported, so we 2223 + * have to unlock all sectors of this device instead 2224 + */ 2225 + chip->state = FL_UNLOCKING; 2226 + map_write(map, CMD(0x80), chip->start); 2227 + map_write(map, CMD(0x30), chip->start); 2228 + } else if (thunk == DO_XXLOCK_ONEBLOCK_GETLOCK) { 2229 + chip->state = FL_JEDEC_QUERY; 2230 + /* Return locked status: 0->locked, 1->unlocked */ 2231 + ret = !cfi_read_query(map, adr); 2232 + } else 2233 + BUG(); 2234 + 2235 + /* 2236 + * Wait for some time as unlocking of all sectors takes quite long 2237 + */ 2238 + timeo = jiffies + msecs_to_jiffies(2000); /* 2s max (un)locking */ 2239 + for (;;) { 2240 + if (chip_ready(map, adr)) 2241 + break; 2242 + 2243 + if (time_after(jiffies, timeo)) { 2244 + printk(KERN_ERR "Waiting for chip to be ready timed out.\n"); 2245 + ret = -EIO; 2246 + break; 2247 + } 2248 + 2249 + UDELAY(map, chip, adr, 1); 2250 + } 2251 + 2252 + /* Exit BC commands */ 2253 + map_write(map, CMD(0x90), chip->start); 2254 + map_write(map, CMD(0x00), chip->start); 2255 + 2256 + chip->state = FL_READY; 2257 + put_chip(map, chip, adr + chip->start); 2258 + mutex_unlock(&chip->mutex); 2259 + 2260 + return ret; 2261 + } 2262 + 2263 + static int __maybe_unused cfi_ppb_lock(struct mtd_info *mtd, loff_t ofs, 2264 + uint64_t len) 2265 + { 2266 + return cfi_varsize_frob(mtd, do_ppb_xxlock, ofs, len, 2267 + DO_XXLOCK_ONEBLOCK_LOCK); 2268 + } 2269 + 2270 + static int __maybe_unused cfi_ppb_unlock(struct mtd_info *mtd, loff_t ofs, 2271 + uint64_t len) 2272 + { 2273 + struct mtd_erase_region_info *regions = mtd->eraseregions; 2274 + struct map_info *map = mtd->priv; 2275 + struct cfi_private *cfi = map->fldrv_priv; 2276 + struct ppb_lock *sect; 2277 + unsigned long adr; 2278 + loff_t offset; 2279 + uint64_t length; 2280 + int chipnum; 2281 + int i; 2282 + int sectors; 2283 + int ret; 2284 + 2285 + /* 2286 + * PPB unlocking always unlocks all sectors of the flash chip. 2287 + * We need to re-lock all previously locked sectors. So lets 2288 + * first check the locking status of all sectors and save 2289 + * it for future use. 2290 + */ 2291 + sect = kzalloc(MAX_SECTORS * sizeof(struct ppb_lock), GFP_KERNEL); 2292 + if (!sect) 2293 + return -ENOMEM; 2294 + 2295 + /* 2296 + * This code to walk all sectors is a slightly modified version 2297 + * of the cfi_varsize_frob() code. 2298 + */ 2299 + i = 0; 2300 + chipnum = 0; 2301 + adr = 0; 2302 + sectors = 0; 2303 + offset = 0; 2304 + length = mtd->size; 2305 + 2306 + while (length) { 2307 + int size = regions[i].erasesize; 2308 + 2309 + /* 2310 + * Only test sectors that shall not be unlocked. The other 2311 + * sectors shall be unlocked, so lets keep their locking 2312 + * status at "unlocked" (locked=0) for the final re-locking. 2313 + */ 2314 + if ((adr < ofs) || (adr >= (ofs + len))) { 2315 + sect[sectors].chip = &cfi->chips[chipnum]; 2316 + sect[sectors].offset = offset; 2317 + sect[sectors].locked = do_ppb_xxlock( 2318 + map, &cfi->chips[chipnum], adr, 0, 2319 + DO_XXLOCK_ONEBLOCK_GETLOCK); 2320 + } 2321 + 2322 + adr += size; 2323 + offset += size; 2324 + length -= size; 2325 + 2326 + if (offset == regions[i].offset + size * regions[i].numblocks) 2327 + i++; 2328 + 2329 + if (adr >> cfi->chipshift) { 2330 + adr = 0; 2331 + chipnum++; 2332 + 2333 + if (chipnum >= cfi->numchips) 2334 + break; 2335 + } 2336 + 2337 + sectors++; 2338 + if (sectors >= MAX_SECTORS) { 2339 + printk(KERN_ERR "Only %d sectors for PPB locking supported!\n", 2340 + MAX_SECTORS); 2341 + kfree(sect); 2342 + return -EINVAL; 2343 + } 2344 + } 2345 + 2346 + /* Now unlock the whole chip */ 2347 + ret = cfi_varsize_frob(mtd, do_ppb_xxlock, ofs, len, 2348 + DO_XXLOCK_ONEBLOCK_UNLOCK); 2349 + if (ret) { 2350 + kfree(sect); 2351 + return ret; 2352 + } 2353 + 2354 + /* 2355 + * PPB unlocking always unlocks all sectors of the flash chip. 2356 + * We need to re-lock all previously locked sectors. 2357 + */ 2358 + for (i = 0; i < sectors; i++) { 2359 + if (sect[i].locked) 2360 + do_ppb_xxlock(map, sect[i].chip, sect[i].offset, 0, 2361 + DO_XXLOCK_ONEBLOCK_LOCK); 2362 + } 2363 + 2364 + kfree(sect); 2365 + return ret; 2366 + } 2367 + 2368 + static int __maybe_unused cfi_ppb_is_locked(struct mtd_info *mtd, loff_t ofs, 2369 + uint64_t len) 2370 + { 2371 + return cfi_varsize_frob(mtd, do_ppb_xxlock, ofs, len, 2372 + DO_XXLOCK_ONEBLOCK_GETLOCK) ? 1 : 0; 2373 + } 2193 2374 2194 2375 static void cfi_amdstd_sync (struct mtd_info *mtd) 2195 2376 {
+1
drivers/mtd/maps/physmap_of.c
··· 241 241 info->list[i].map.phys = res.start; 242 242 info->list[i].map.size = res_size; 243 243 info->list[i].map.bankwidth = be32_to_cpup(width); 244 + info->list[i].map.device_node = dp; 244 245 245 246 err = -ENOMEM; 246 247 info->list[i].map.virt = ioremap(info->list[i].map.phys,
+1
include/linux/mtd/map.h
··· 245 245 unsigned long pfow_base; 246 246 unsigned long map_priv_1; 247 247 unsigned long map_priv_2; 248 + struct device_node *device_node; 248 249 void *fldrv_priv; 249 250 struct mtd_chip_driver *fldrv; 250 251 };