* Re: Supporting flash that is locked by default
2004-09-25 10:33 ` David Woodhouse
@ 2004-09-28 23:43 ` Todd Poynor
2004-09-29 0:57 ` Nicolas Pitre
2004-10-08 1:34 ` Todd Poynor
1 sibling, 1 reply; 7+ messages in thread
From: Todd Poynor @ 2004-09-28 23:43 UTC (permalink / raw)
To: David Woodhouse; +Cc: Christopher Hoover, linux-mtd
On Sat, Sep 25, 2004 at 11:33:36AM +0100, David Woodhouse wrote:
> This locking scheme is daft. My original reaction was to hide it in the
> mapping driver for such idiotic flashes, but they're more common now and
> it really is a function of the flash not of the board. I suspect we
> ought to add a quirk entry for these flashes, and automatically unlock
> them on probe.
Here's a try at automatically unlocking all blocks of Intel CFI flash at
probe time and at system resume time. The new logic is in the chip
driver, but perhaps it would be better to move it generic, adding a new
chip driver callback for "is unlock needed?"; can rework things that way
if desired (was originally hoping to hide this "feature" from generic
code).
I did not notice a query feature bit that identifies whether the flash
has the "all blocks locked at power on" property in the datasheets, so
the patch checks whether block 0 is locked and assumes its one of those
flashes if so. I've tried booting a board with K3 StrataFlash (which
powers up locked) and a board with J3 StrataFlash (which does not) and
it seems to do the right thing.
Since all blocks may be unlocked at resume time, it would be up to a
userspace action to relock any previously explicitly locked blocks soon
afterwards. The chip driver (or generic layer) *could* keep track of
locked blocks and only unlock those not previously locked, can send a
patch for that if there's interest.
Comments? Thanks. -- Todd
Index: drivers/mtd/chips/cfi_cmdset_0001.c
===================================================================
RCS file: /home/cvs/mtd/drivers/mtd/chips/cfi_cmdset_0001.c,v
retrieving revision 1.156
diff -u -r1.156 cfi_cmdset_0001.c
--- drivers/mtd/chips/cfi_cmdset_0001.c 17 Sep 2004 11:45:05 -0000 1.156
+++ drivers/mtd/chips/cfi_cmdset_0001.c 28 Sep 2004 22:56:49 -0000
@@ -45,6 +45,12 @@
#define MANUFACTURER_ST 0x0020
#define M50LPW080 0x002F
+/*
+ * Block status register bits
+ */
+
+#define BSR_LOCK 0x1 /* Block lock status */
+
static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
//static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
//static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
@@ -299,6 +305,41 @@
return cfi_intelext_setup(mtd);
}
+static int checklockstatus(struct map_info *map, struct flchip *chip,
+ unsigned long adr, int len, void *thunk)
+{
+ struct cfi_private *cfi = map->fldrv_priv;
+ int ofs_factor = cfi->interleave * cfi->device_type;
+ int ret;
+
+ cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL);
+ ret = cfi_read_query(map, adr+(2*ofs_factor)) & BSR_LOCK;
+ chip->state = FL_JEDEC_QUERY;
+ return ret;
+}
+
+static void cfi_intelext_unlock_all(struct mtd_info *mtd)
+{
+ int i, locked;
+
+ locked = cfi_varsize_frob(mtd, checklockstatus,
+ 0, mtd->eraseregions[0].erasesize, 0);
+
+ if (locked != 1)
+ return;
+
+ printk(KERN_DEBUG "Unlocking locked CFI flash blocks.\n");
+ for (i = 0; i < mtd->numeraseregions; i++) {
+ int j;
+
+ for (j = 0; j < mtd->eraseregions[i].numblocks; j++){
+ cfi_intelext_unlock(mtd, mtd->eraseregions[i].offset +
+ j * mtd->eraseregions[i].erasesize,
+ mtd->eraseregions[i].erasesize);
+ }
+ }
+}
+
static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd)
{
struct map_info *map = mtd->priv;
@@ -358,6 +399,7 @@
if (cfi_intelext_partition_fixup(map, &cfi) != 0)
goto setup_err;
+ cfi_intelext_unlock_all(mtd);
__module_get(THIS_MODULE);
return mtd;
@@ -1756,6 +1798,8 @@
spin_unlock(chip->mutex);
}
+
+ cfi_intelext_unlock_all(mtd);
}
static void cfi_intelext_destroy(struct mtd_info *mtd)
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: Supporting flash that is locked by default
2004-09-25 10:33 ` David Woodhouse
2004-09-28 23:43 ` Todd Poynor
@ 2004-10-08 1:34 ` Todd Poynor
1 sibling, 0 replies; 7+ messages in thread
From: Todd Poynor @ 2004-10-08 1:34 UTC (permalink / raw)
To: David Woodhouse; +Cc: Christopher Hoover, linux-mtd
Another stab at handling flashes that bootup/resume with blocks locked:
The partition driver intercepts an EROFS return from the chip driver,
unlocks blocks, and retries the write/erase. If the flash is not
writeable per the partition map, or if the partition has been locked,
then leave locked blocks alone. This avoids the need to detect whether
the flash has the property that blocks for a partition intended to be
writeable may be locked anyway, and avoids a loop to unlock all
appropriate blocks at init time, as well as the need to save/restore
block lock status via a bitmap at suspend/resume time.
In order to allow a writeable partition to be setup but still explicitly
locked via flash_lock, a partition lock flag is added that is
set/cleared by flash_lock/unlock. Blocks in a partition locked via
flash_lock will not be automatically cleared. flash_lock will thus
trigger software locking a la the MTD_WRITEABLE flag in maps. This
might be a good thing, regardless of the merits of the previous
paragraph.
Comments? Thanks -- Todd
Index: drivers/mtd/mtdpart.c
===================================================================
RCS file: /home/cvs/mtd/drivers/mtd/mtdpart.c,v
retrieving revision 1.50
diff -u -r1.50 mtdpart.c
--- drivers/mtd/mtdpart.c 10 Aug 2004 16:18:34 -0000 1.50
+++ drivers/mtd/mtdpart.c 8 Oct 2004 01:06:52 -0000
@@ -33,8 +33,11 @@
int index;
struct list_head list;
int registered;
+ int flags;
};
+#define PART_LOCKED 1
+
/*
* Given a pointer to the MTD object in the mtd_part structure, we can retrieve
* the pointer to that structure with this macro.
@@ -124,23 +127,40 @@
len, retlen, buf);
}
+static int part_do_write (struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ struct mtd_part *part = PART(mtd);
+
+ if (part->master->write_ecc == NULL)
+ return part->master->write (part->master, to + part->offset,
+ len, retlen, buf);
+ else
+ return part->master->write_ecc (part->master, to + part->offset,
+ len, retlen, buf, NULL,
+ &mtd->oobinfo);
+}
+
static int part_write (struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
struct mtd_part *part = PART(mtd);
- if (!(mtd->flags & MTD_WRITEABLE))
+ int ret;
+ if (! (mtd->flags & MTD_WRITEABLE) || part->flags & PART_LOCKED)
return -EROFS;
if (to >= mtd->size)
len = 0;
else if (to + len > mtd->size)
len = mtd->size - to;
- if (part->master->write_ecc == NULL)
- return part->master->write (part->master, to + part->offset,
- len, retlen, buf);
- else
- return part->master->write_ecc (part->master, to + part->offset,
- len, retlen, buf, NULL, &mtd->oobinfo);
-
+
+ if (((ret = part_do_write (mtd, to, len, retlen, buf)) == -EROFS) &&
+ part->master->unlock) {
+ loff_t baddr = (to + part->offset) & ~(mtd->erasesize - 1);
+ if (! part->master->unlock (part->master, baddr,
+ mtd->erasesize))
+ ret = part_do_write (mtd, to, len, retlen, buf);
+ }
+ return ret;
}
static int part_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
@@ -148,7 +168,8 @@
u_char *eccbuf, struct nand_oobinfo *oobsel)
{
struct mtd_part *part = PART(mtd);
- if (!(mtd->flags & MTD_WRITEABLE))
+ int ret;
+ if (! (mtd->flags & MTD_WRITEABLE) || part->flags & PART_LOCKED)
return -EROFS;
if (oobsel == NULL)
oobsel = &mtd->oobinfo;
@@ -156,22 +177,44 @@
len = 0;
else if (to + len > mtd->size)
len = mtd->size - to;
- return part->master->write_ecc (part->master, to + part->offset,
- len, retlen, buf, eccbuf, oobsel);
+ if (((ret = part->master->write_ecc (part->master, to + part->offset,
+ len, retlen, buf, eccbuf,
+ oobsel)) == -EROFS) &&
+ part->master->unlock) {
+ loff_t baddr = (to + part->offset) & ~(mtd->erasesize - 1);
+ if (! part->master->unlock (part->master, baddr,
+ mtd->erasesize))
+ ret = part->master->write_ecc (part->master,
+ to + part->offset,
+ len, retlen, buf,
+ eccbuf, oobsel);
+ }
+ return ret;
}
static int part_write_oob (struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
struct mtd_part *part = PART(mtd);
- if (!(mtd->flags & MTD_WRITEABLE))
+ int ret;
+ if (! (mtd->flags & MTD_WRITEABLE) || part->flags & PART_LOCKED)
return -EROFS;
if (to >= mtd->size)
len = 0;
else if (to + len > mtd->size)
len = mtd->size - to;
- return part->master->write_oob (part->master, to + part->offset,
- len, retlen, buf);
+ if (((ret = part->master->write_oob (part->master, to + part->offset,
+ len, retlen, buf)) == -EROFS) &&
+ part->master->unlock) {
+ loff_t baddr = (to + part->offset) & ~(mtd->erasesize - 1);
+ if (! part->master->unlock (part->master, baddr,
+ mtd->erasesize))
+ ret = part->master->write_oob (part->master,
+ to + part->offset, len,
+ retlen, buf);
+ }
+
+ return ret;
}
static int part_write_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len,
@@ -182,12 +225,10 @@
len, retlen, buf);
}
-static int part_writev (struct mtd_info *mtd, const struct kvec *vecs,
- unsigned long count, loff_t to, size_t *retlen)
+static int part_do_writev (struct mtd_info *mtd, const struct kvec *vecs,
+ unsigned long count, loff_t to, size_t *retlen)
{
struct mtd_part *part = PART(mtd);
- if (!(mtd->flags & MTD_WRITEABLE))
- return -EROFS;
if (part->master->writev_ecc == NULL)
return part->master->writev (part->master, vecs, count,
to + part->offset, retlen);
@@ -197,6 +238,24 @@
NULL, &mtd->oobinfo);
}
+static int part_writev (struct mtd_info *mtd, const struct kvec *vecs,
+ unsigned long count, loff_t to, size_t *retlen)
+{
+ struct mtd_part *part = PART(mtd);
+ int ret;
+ if (! (mtd->flags & MTD_WRITEABLE) || part->flags & PART_LOCKED)
+ return -EROFS;
+
+ if (((ret = part_do_writev (mtd, vecs, count, to, retlen)) == -EROFS)
+ && part->master->unlock) {
+ loff_t baddr = (to + part->offset) & ~(mtd->erasesize - 1);
+ if (! part->master->unlock (part->master, baddr,
+ mtd->erasesize))
+ ret = part_do_writev (mtd, vecs, count, to, retlen);
+ }
+ return ret;
+}
+
static int part_readv (struct mtd_info *mtd, struct kvec *vecs,
unsigned long count, loff_t from, size_t *retlen)
{
@@ -215,13 +274,25 @@
u_char *eccbuf, struct nand_oobinfo *oobsel)
{
struct mtd_part *part = PART(mtd);
- if (!(mtd->flags & MTD_WRITEABLE))
+ int ret;
+ if (! (mtd->flags & MTD_WRITEABLE) || part->flags & PART_LOCKED)
return -EROFS;
if (oobsel == NULL)
oobsel = &mtd->oobinfo;
- return part->master->writev_ecc (part->master, vecs, count,
- to + part->offset, retlen,
- eccbuf, oobsel);
+ if (((ret = part->master->writev_ecc (part->master, vecs, count,
+ to + part->offset, retlen,
+ eccbuf, oobsel)) == -EROFS) &&
+ part->master->unlock) {
+ loff_t baddr = (to + part->offset) & ~(mtd->erasesize - 1);
+ if (! part->master->unlock (part->master, baddr,
+ mtd->erasesize))
+ ret = part->master->writev_ecc (part->master, vecs,
+ count,
+ to + part->offset,
+ retlen, eccbuf, oobsel);
+ }
+ return ret;
+
}
static int part_readv_ecc (struct mtd_info *mtd, struct kvec *vecs,
@@ -240,12 +311,17 @@
{
struct mtd_part *part = PART(mtd);
int ret;
- if (!(mtd->flags & MTD_WRITEABLE))
+ if (! (mtd->flags & MTD_WRITEABLE) || part->flags & PART_LOCKED)
return -EROFS;
if (instr->addr >= mtd->size)
return -EINVAL;
instr->addr += part->offset;
- ret = part->master->erase(part->master, instr);
+ if ((ret = part->master->erase(part->master, instr) == -EROFS) &&
+ part->master->unlock &&
+ (! part->master->unlock (part->master, instr->addr,
+ mtd->erasesize)))
+ ret = part->master->erase(part->master, instr);
+
return ret;
}
@@ -268,6 +344,7 @@
struct mtd_part *part = PART(mtd);
if ((len + ofs) > mtd->size)
return -EINVAL;
+ part->flags |= PART_LOCKED;
return part->master->lock(part->master, ofs + part->offset, len);
}
@@ -276,6 +353,7 @@
struct mtd_part *part = PART(mtd);
if ((len + ofs) > mtd->size)
return -EINVAL;
+ part->flags &= ~PART_LOCKED;
return part->master->unlock(part->master, ofs + part->offset, len);
}
@@ -309,7 +387,7 @@
static int part_block_markbad (struct mtd_info *mtd, loff_t ofs)
{
struct mtd_part *part = PART(mtd);
- if (!(mtd->flags & MTD_WRITEABLE))
+ if (! (mtd->flags & MTD_WRITEABLE) || part->flags & PART_LOCKED)
return -EROFS;
if (ofs >= mtd->size)
return -EINVAL;
^ permalink raw reply [flat|nested] 7+ messages in thread