Skip to content

Commit 1648eaa

Browse files
stroesededekind
authored andcommitted
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 <[email protected]> Tested-by: Holger Brunck <[email protected]> Signed-off-by: Artem Bityutskiy <[email protected]>
1 parent 422f389 commit 1648eaa

File tree

4 files changed

+222
-0
lines changed

4 files changed

+222
-0
lines changed

Documentation/devicetree/bindings/mtd/mtd-physmap.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ file systems on embedded devices.
2626
- linux,mtd-name: allow to specify the mtd name for retro capability with
2727
physmap-flash drivers as boot loader pass the mtd partition via the old
2828
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.
2932

3033
For JEDEC compatible devices, the following additional properties
3134
are defined:

drivers/mtd/chips/cfi_cmdset_0002.c

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
#include <linux/delay.h>
3434
#include <linux/interrupt.h>
3535
#include <linux/reboot.h>
36+
#include <linux/of.h>
37+
#include <linux/of_platform.h>
3638
#include <linux/mtd/map.h>
3739
#include <linux/mtd/mtd.h>
3840
#include <linux/mtd/cfi.h>
@@ -74,6 +76,10 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad
7476
static int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
7577
static int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
7678

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+
7783
static struct mtd_chip_driver cfi_amdstd_chipdrv = {
7884
.probe = NULL, /* Not usable directly */
7985
.destroy = cfi_amdstd_destroy,
@@ -496,6 +502,7 @@ static void cfi_fixup_m29ew_delay_after_resume(struct cfi_private *cfi)
496502
struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
497503
{
498504
struct cfi_private *cfi = map->fldrv_priv;
505+
struct device_node __maybe_unused *np = map->device_node;
499506
struct mtd_info *mtd;
500507
int i;
501508

@@ -570,6 +577,17 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
570577
cfi_tell_features(extp);
571578
#endif
572579

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+
573591
bootloc = extp->TopBottom;
574592
if ((bootloc < 2) || (bootloc > 5)) {
575593
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)
21722190
return cfi_varsize_frob(mtd, do_atmel_unlock, ofs, len, NULL);
21732191
}
21742192

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+
}
21752392

21762393
static void cfi_amdstd_sync (struct mtd_info *mtd)
21772394
{

drivers/mtd/maps/physmap_of.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ static int of_flash_probe(struct platform_device *dev)
241241
info->list[i].map.phys = res.start;
242242
info->list[i].map.size = res_size;
243243
info->list[i].map.bankwidth = be32_to_cpup(width);
244+
info->list[i].map.device_node = dp;
244245

245246
err = -ENOMEM;
246247
info->list[i].map.virt = ioremap(info->list[i].map.phys,

include/linux/mtd/map.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ struct map_info {
245245
unsigned long pfow_base;
246246
unsigned long map_priv_1;
247247
unsigned long map_priv_2;
248+
struct device_node *device_node;
248249
void *fldrv_priv;
249250
struct mtd_chip_driver *fldrv;
250251
};

0 commit comments

Comments
 (0)