Commit f77814dd authored by Nicolas Pitre's avatar Nicolas Pitre Committed by Thomas Gleixner

[MTD] Support for protection register support on Intel FLASH chips

This enables support for reading, writing and locking so called
"Protection Registers" present on some flash chips.
A subset of them are pre-programmed at the factory with a
unique set of values. The rest is user-programmable.
Signed-off-by: default avatarNicolas Pitre <nico@cam.org>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent 67d9e95c
# drivers/mtd/chips/Kconfig # drivers/mtd/chips/Kconfig
# $Id: Kconfig,v 1.13 2004/12/01 15:49:10 nico Exp $ # $Id: Kconfig,v 1.14 2005/02/08 17:11:15 nico Exp $
menu "RAM/ROM/Flash chip drivers" menu "RAM/ROM/Flash chip drivers"
depends on MTD!=n depends on MTD!=n
...@@ -155,6 +155,31 @@ config MTD_CFI_I8 ...@@ -155,6 +155,31 @@ config MTD_CFI_I8
If your flash chips are interleaved in eights - i.e. you have eight If your flash chips are interleaved in eights - i.e. you have eight
flash chips addressed by each bus cycle, then say 'Y'. flash chips addressed by each bus cycle, then say 'Y'.
config MTD_OTP
bool "Protection Registers aka one-time programmable (OTP) bits"
depends on MTD_CFI_ADV_OPTIONS
default n
help
This enables support for reading, writing and locking so called
"Protection Registers" present on some flash chips.
A subset of them are pre-programmed at the factory with a
unique set of values. The rest is user-programmable.
The user-programmable Protection Registers contain one-time
programmable (OTP) bits; when programmed, register bits cannot be
erased. Each Protection Register can be accessed multiple times to
program individual bits, as long as the register remains unlocked.
Each Protection Register has an associated Lock Register bit. When a
Lock Register bit is programmed, the associated Protection Register
can only be read; it can no longer be programmed. Additionally,
because the Lock Register bits themselves are OTP, when programmed,
Lock Register bits cannot be erased. Therefore, when a Protection
Register is locked, it cannot be unlocked.
This feature should therefore be used with extreme care. Any mistake
in the programming of OTP bits will waste them.
config MTD_CFI_INTELEXT config MTD_CFI_INTELEXT
tristate "Support for Intel/Sharp flash chips" tristate "Support for Intel/Sharp flash chips"
depends on MTD_GEN_PROBE depends on MTD_GEN_PROBE
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* *
* (C) 2000 Red Hat. GPL'd * (C) 2000 Red Hat. GPL'd
* *
* $Id: cfi_cmdset_0001.c,v 1.165 2005/02/05 02:06:15 nico Exp $ * $Id: cfi_cmdset_0001.c,v 1.167 2005/02/08 17:11:15 nico Exp $
* *
* *
* 10/10/2000 Nicolas Pitre <nico@cam.org> * 10/10/2000 Nicolas Pitre <nico@cam.org>
...@@ -48,14 +48,20 @@ ...@@ -48,14 +48,20 @@
#define M50LPW080 0x002F #define M50LPW080 0x002F
static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); 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 *);
static int cfi_intelext_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); static int cfi_intelext_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
static int cfi_intelext_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); static int cfi_intelext_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
static int cfi_intelext_erase_varsize(struct mtd_info *, struct erase_info *); static int cfi_intelext_erase_varsize(struct mtd_info *, struct erase_info *);
static void cfi_intelext_sync (struct mtd_info *); static void cfi_intelext_sync (struct mtd_info *);
static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len); static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len);
static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len); static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len);
static int cfi_intelext_read_fact_prot_reg (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_write_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
static int cfi_intelext_lock_user_prot_reg (struct mtd_info *, loff_t, size_t);
static int cfi_intelext_get_fact_prot_info (struct mtd_info *,
struct otp_info *, size_t);
static int cfi_intelext_get_user_prot_info (struct mtd_info *,
struct otp_info *, size_t);
static int cfi_intelext_suspend (struct mtd_info *); static int cfi_intelext_suspend (struct mtd_info *);
static void cfi_intelext_resume (struct mtd_info *); static void cfi_intelext_resume (struct mtd_info *);
...@@ -423,9 +429,13 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd) ...@@ -423,9 +429,13 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd)
mtd->eraseregions[i].numblocks); mtd->eraseregions[i].numblocks);
} }
#if 0 #ifdef CONFIG_MTD_OTP
mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg;
mtd->read_fact_prot_reg = cfi_intelext_read_fact_prot_reg; mtd->read_fact_prot_reg = cfi_intelext_read_fact_prot_reg;
mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg;
mtd->write_user_prot_reg = cfi_intelext_write_user_prot_reg;
mtd->lock_user_prot_reg = cfi_intelext_lock_user_prot_reg;
mtd->get_fact_prot_info = cfi_intelext_get_fact_prot_info;
mtd->get_user_prot_info = cfi_intelext_get_user_prot_info;
#endif #endif
/* This function has the potential to distort the reality /* This function has the potential to distort the reality
...@@ -565,7 +575,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr ...@@ -565,7 +575,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
resettime: resettime:
timeo = jiffies + HZ; timeo = jiffies + HZ;
retry: retry:
if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING)) { if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING || mode == FL_OTP_WRITE)) {
/* /*
* OK. We have possibility for contension on the write/erase * OK. We have possibility for contension on the write/erase
* operations which are global to the real chip and not per * operations which are global to the real chip and not per
...@@ -1178,111 +1188,11 @@ static int cfi_intelext_read (struct mtd_info *mtd, loff_t from, size_t len, siz ...@@ -1178,111 +1188,11 @@ static int cfi_intelext_read (struct mtd_info *mtd, loff_t from, size_t len, siz
return ret; return ret;
} }
#if 0
static int __xipram cfi_intelext_read_prot_reg (struct mtd_info *mtd,
loff_t from, size_t len,
size_t *retlen,
u_char *buf,
int base_offst, int reg_sz)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
struct cfi_pri_intelext *extp = cfi->cmdset_priv;
struct flchip *chip;
int ofs_factor = cfi->interleave * cfi->device_type;
int count = len;
int chip_num, offst;
int ret;
chip_num = ((unsigned int)from/reg_sz);
offst = from - (reg_sz*chip_num)+base_offst;
while (count) {
/* Calculate which chip & protection register offset we need */
if (chip_num >= cfi->numchips)
goto out;
chip = &cfi->chips[chip_num];
spin_lock(chip->mutex);
ret = get_chip(map, chip, chip->start, FL_JEDEC_QUERY);
if (ret) {
spin_unlock(chip->mutex);
return (len-count)?:ret;
}
xip_disable(map, chip, chip->start);
if (chip->state != FL_JEDEC_QUERY) {
map_write(map, CMD(0x90), chip->start);
chip->state = FL_JEDEC_QUERY;
}
while (count && ((offst-base_offst) < reg_sz)) {
*buf = map_read8(map,(chip->start+((extp->ProtRegAddr+1)*ofs_factor)+offst));
buf++;
offst++;
count--;
}
xip_enable(map, chip, chip->start);
put_chip(map, chip, chip->start);
spin_unlock(chip->mutex);
/* Move on to the next chip */
chip_num++;
offst = base_offst;
}
out:
return len-count;
}
static int cfi_intelext_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
struct cfi_pri_intelext *extp=cfi->cmdset_priv;
int base_offst,reg_sz;
/* Check that we actually have some protection registers */
if(!extp || !(extp->FeatureSupport&64)){
printk(KERN_WARNING "%s: This flash device has no protection data to read!\n",map->name);
return 0;
}
base_offst=(1<<extp->FactProtRegSize);
reg_sz=(1<<extp->UserProtRegSize);
return cfi_intelext_read_prot_reg(mtd, from, len, retlen, buf, base_offst, reg_sz);
}
static int cfi_intelext_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
struct cfi_pri_intelext *extp=cfi->cmdset_priv;
int base_offst,reg_sz;
/* Check that we actually have some protection registers */
if(!extp || !(extp->FeatureSupport&64)){
printk(KERN_WARNING "%s: This flash device has no protection data to read!\n",map->name);
return 0;
}
base_offst=0;
reg_sz=(1<<extp->FactProtRegSize);
return cfi_intelext_read_prot_reg(mtd, from, len, retlen, buf, base_offst, reg_sz);
}
#endif
static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
unsigned long adr, map_word datum) unsigned long adr, map_word datum, int mode)
{ {
struct cfi_private *cfi = map->fldrv_priv; struct cfi_private *cfi = map->fldrv_priv;
map_word status, status_OK; map_word status, status_OK, write_cmd;
unsigned long timeo; unsigned long timeo;
int z, ret=0; int z, ret=0;
...@@ -1290,9 +1200,14 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, ...@@ -1290,9 +1200,14 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
/* Let's determine this according to the interleave only once */ /* Let's determine this according to the interleave only once */
status_OK = CMD(0x80); status_OK = CMD(0x80);
switch (mode) {
case FL_WRITING: write_cmd = CMD(0x40); break;
case FL_OTP_WRITE: write_cmd = CMD(0xc0); break;
default: return -EINVAL;
}
spin_lock(chip->mutex); spin_lock(chip->mutex);
ret = get_chip(map, chip, adr, FL_WRITING); ret = get_chip(map, chip, adr, mode);
if (ret) { if (ret) {
spin_unlock(chip->mutex); spin_unlock(chip->mutex);
return ret; return ret;
...@@ -1301,9 +1216,9 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, ...@@ -1301,9 +1216,9 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map)); XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map));
ENABLE_VPP(map); ENABLE_VPP(map);
xip_disable(map, chip, adr); xip_disable(map, chip, adr);
map_write(map, CMD(0x40), adr); map_write(map, write_cmd, adr);
map_write(map, datum, adr); map_write(map, datum, adr);
chip->state = FL_WRITING; chip->state = mode;
spin_unlock(chip->mutex); spin_unlock(chip->mutex);
INVALIDATE_CACHED_RANGE(map, adr, map_bankwidth(map)); INVALIDATE_CACHED_RANGE(map, adr, map_bankwidth(map));
...@@ -1313,7 +1228,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, ...@@ -1313,7 +1228,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
timeo = jiffies + (HZ/2); timeo = jiffies + (HZ/2);
z = 0; z = 0;
for (;;) { for (;;) {
if (chip->state != FL_WRITING) { if (chip->state != mode) {
/* Someone's suspended the write. Sleep */ /* Someone's suspended the write. Sleep */
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
...@@ -1401,7 +1316,7 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le ...@@ -1401,7 +1316,7 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le
datum = map_word_load_partial(map, datum, buf, gap, n); datum = map_word_load_partial(map, datum, buf, gap, n);
ret = do_write_oneword(map, &cfi->chips[chipnum], ret = do_write_oneword(map, &cfi->chips[chipnum],
bus_ofs, datum); bus_ofs, datum, FL_WRITING);
if (ret) if (ret)
return ret; return ret;
...@@ -1422,7 +1337,7 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le ...@@ -1422,7 +1337,7 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le
map_word datum = map_word_load(map, buf); map_word datum = map_word_load(map, buf);
ret = do_write_oneword(map, &cfi->chips[chipnum], ret = do_write_oneword(map, &cfi->chips[chipnum],
ofs, datum); ofs, datum, FL_WRITING);
if (ret) if (ret)
return ret; return ret;
...@@ -1446,7 +1361,7 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le ...@@ -1446,7 +1361,7 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le
datum = map_word_load_partial(map, datum, buf, 0, len); datum = map_word_load_partial(map, datum, buf, 0, len);
ret = do_write_oneword(map, &cfi->chips[chipnum], ret = do_write_oneword(map, &cfi->chips[chipnum],
ofs, datum); ofs, datum, FL_WRITING);
if (ret) if (ret)
return ret; return ret;
...@@ -2036,6 +1951,262 @@ static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) ...@@ -2036,6 +1951,262 @@ static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
return ret; return ret;
} }
#ifdef CONFIG_MTD_OTP
typedef int (*otp_op_t)(struct map_info *map, struct flchip *chip,
u_long data_offset, u_char *buf, u_int size,
u_long prot_offset, u_int groupno, u_int groupsize);
static int __xipram
do_otp_read(struct map_info *map, struct flchip *chip, u_long offset,
u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz)
{
struct cfi_private *cfi = map->fldrv_priv;
int ret;
spin_lock(chip->mutex);
ret = get_chip(map, chip, chip->start, FL_JEDEC_QUERY);
if (ret) {
spin_unlock(chip->mutex);
return ret;
}
/* let's ensure we're not reading back cached data from array mode */
if (map->inval_cache)
map->inval_cache(map, chip->start + offset, size);
xip_disable(map, chip, chip->start);
if (chip->state != FL_JEDEC_QUERY) {
map_write(map, CMD(0x90), chip->start);
chip->state = FL_JEDEC_QUERY;
}
map_copy_from(map, buf, chip->start + offset, size);
xip_enable(map, chip, chip->start);
/* then ensure we don't keep OTP data in the cache */
if (map->inval_cache)
map->inval_cache(map, chip->start + offset, size);
put_chip(map, chip, chip->start);
spin_unlock(chip->mutex);
return 0;
}
static int
do_otp_write(struct map_info *map, struct flchip *chip, u_long offset,
u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz)
{
int ret;
while (size) {
unsigned long bus_ofs = offset & ~(map_bankwidth(map)-1);
int gap = offset - bus_ofs;
int n = min_t(int, size, map_bankwidth(map)-gap);
map_word datum = map_word_ff(map);
datum = map_word_load_partial(map, datum, buf, gap, n);
ret = do_write_oneword(map, chip, bus_ofs, datum, FL_OTP_WRITE);
if (ret)
return ret;
offset += n;
buf += n;
size -= n;
}
return 0;
}
static int
do_otp_lock(struct map_info *map, struct flchip *chip, u_long offset,
u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz)
{
struct cfi_private *cfi = map->fldrv_priv;
map_word datum;
/* make sure area matches group boundaries */
if (offset != 0 || size != grpsz)
return -EXDEV;
datum = map_word_ff(map);
datum = map_word_clr(map, datum, CMD(1 << grpno));
return do_write_oneword(map, chip, prot, datum, FL_OTP_WRITE);
}
static int cfi_intelext_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf,
otp_op_t action, int user_regs)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
struct cfi_pri_intelext *extp = cfi->cmdset_priv;
struct flchip *chip;
struct cfi_intelext_otpinfo *otp;
u_long devsize, reg_prot_offset, data_offset;
u_int chip_num, chip_step, field, reg_fact_size, reg_user_size;
u_int groups, groupno, groupsize, reg_fact_groups, reg_user_groups;
int ret;
*retlen = 0;
/* Check that we actually have some OTP registers */
if (!extp || !(extp->FeatureSupport & 64) || !extp->NumProtectionFields)
return -ENODATA;
/* we need real chips here not virtual ones */
devsize = (1 << cfi->cfiq->DevSize) * cfi->interleave;
chip_step = devsize >> cfi->chipshift;
for (chip_num = 0; chip_num < cfi->numchips; chip_num += chip_step) {
chip = &cfi->chips[chip_num];
otp = (struct cfi_intelext_otpinfo *)&extp->extra[0];
/* first OTP region */
field = 0;
reg_prot_offset = extp->ProtRegAddr;
reg_fact_groups = 1;
reg_fact_size = 1 << extp->FactProtRegSize;
reg_user_groups = 1;
reg_user_size = 1 << extp->UserProtRegSize;
while (len > 0) {
/* flash geometry fixup */
data_offset = reg_prot_offset + 1;
data_offset *= cfi->interleave * cfi->device_type;
reg_prot_offset *= cfi->interleave * cfi->device_type;
reg_fact_size *= cfi->interleave;
reg_user_size *= cfi->interleave;
if (user_regs) {
groups = reg_user_groups;
groupsize = reg_user_size;
/* skip over factory reg area */
groupno = reg_fact_groups;
data_offset += reg_fact_groups * reg_fact_size;
} else {
groups = reg_fact_groups;
groupsize = reg_fact_size;
groupno = 0;
}
while (groups > 0) {
if (!action) {
/*
* Special case: if action is NULL
* we fill buf with otp_info records.
*/
struct otp_info *otpinfo;
map_word lockword;
len -= sizeof(struct otp_info);
if (len <= 0)
return -ENOSPC;
ret = do_otp_read(map, chip,
reg_prot_offset,
(u_char *)&lockword,
map_bankwidth(map),
0, 0, 0);
if (ret)
return ret;
otpinfo = (struct otp_info *)buf;
otpinfo->start = from;
otpinfo->length = groupsize;
otpinfo->locked =
!map_word_bitsset(map, lockword,
CMD(1 << groupno));
from += groupsize;
buf += sizeof(*otpinfo);
*retlen += sizeof(*otpinfo);
} else if (from >= groupsize) {
from -= groupsize;
} else {
int size = groupsize;
data_offset += from;
size -= from;
from = 0;
if (size > len)
size = len;
ret = action(map, chip, data_offset,
buf, size, reg_prot_offset,
groupno, groupsize);
if (ret < 0)
return ret;
buf += size;
len -= size;
*retlen += size;
}
groupno++;
groups--;
}
/* next OTP region */
if (++field == extp->NumProtectionFields)
break;
reg_prot_offset = otp->ProtRegAddr;
reg_fact_groups = otp->FactGroups;
reg_fact_size = 1 << otp->FactProtRegSize;
reg_user_groups = otp->UserGroups;
reg_user_size = 1 << otp->UserProtRegSize;
otp++;
}
}
return 0;
}
static int cfi_intelext_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen,
u_char *buf)
{
return cfi_intelext_otp_walk(mtd, from, len, retlen,
buf, do_otp_read, 0);
}
static int cfi_intelext_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen,
u_char *buf)
{
return cfi_intelext_otp_walk(mtd, from, len, retlen,
buf, do_otp_read, 1);
}
static int cfi_intelext_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen,
u_char *buf)
{
return cfi_intelext_otp_walk(mtd, from, len, retlen,
buf, do_otp_write, 1);
}
static int cfi_intelext_lock_user_prot_reg(struct mtd_info *mtd,
loff_t from, size_t len)
{
size_t retlen;
return cfi_intelext_otp_walk(mtd, from, len, &retlen,
NULL, do_otp_lock, 1);
}
static int cfi_intelext_get_fact_prot_info(struct mtd_info *mtd,
struct otp_info *buf, size_t len)
{
size_t retlen;
int ret;
ret = cfi_intelext_otp_walk(mtd, 0, len, &retlen, (u_char *)buf, NULL, 0);
return ret ? : retlen;
}
static int cfi_intelext_get_user_prot_info(struct mtd_info *mtd,
struct otp_info *buf, size_t len)
{
size_t retlen;
int ret;
ret = cfi_intelext_otp_walk(mtd, 0, len, &retlen, (u_char *)buf, NULL, 1);
return ret ? : retlen;
}
#endif
static int cfi_intelext_suspend(struct mtd_info *mtd) static int cfi_intelext_suspend(struct mtd_info *mtd)
{ {
struct map_info *map = mtd->priv; struct map_info *map = mtd->priv;
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* *
* This code is GPL * This code is GPL
* *
* $Id: mtdpart.c,v 1.51 2004/11/16 18:28:59 dwmw2 Exp $ * $Id: mtdpart.c,v 1.53 2005/02/08 17:11:13 nico Exp $
* *
* 02-21-2002 Thomas Gleixner <gleixner@autronix.de> * 02-21-2002 Thomas Gleixner <gleixner@autronix.de>
* added support for read_oob, write_oob * added support for read_oob, write_oob
...@@ -116,6 +116,13 @@ static int part_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t le ...@@ -116,6 +116,13 @@ static int part_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t le
len, retlen, buf); len, retlen, buf);
} }
static int part_get_user_prot_info (struct mtd_info *mtd,
struct otp_info *buf, size_t len)
{
struct mtd_part *part = PART(mtd);
return part->master->get_user_prot_info (part->master, buf, len);
}
static int part_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, static int part_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf) size_t *retlen, u_char *buf)
{ {
...@@ -124,6 +131,13 @@ static int part_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t le ...@@ -124,6 +131,13 @@ static int part_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t le
len, retlen, buf); len, retlen, buf);
} }
static int part_get_fact_prot_info (struct mtd_info *mtd,
struct otp_info *buf, size_t len)
{
struct mtd_part *part = PART(mtd);
return part->master->get_fact_prot_info (part->master, buf, len);
}
static int part_write (struct mtd_info *mtd, loff_t to, size_t len, static int part_write (struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf) size_t *retlen, const u_char *buf)
{ {
...@@ -182,6 +196,12 @@ static int part_write_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t l ...@@ -182,6 +196,12 @@ static int part_write_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t l
len, retlen, buf); len, retlen, buf);
} }
static int part_lock_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len)
{
struct mtd_part *part = PART(mtd);
return part->master->lock_user_prot_reg (part->master, from, len);
}
static int part_writev (struct mtd_info *mtd, const struct kvec *vecs, static int part_writev (struct mtd_info *mtd, const struct kvec *vecs,
unsigned long count, loff_t to, size_t *retlen) unsigned long count, loff_t to, size_t *retlen)
{ {
...@@ -409,6 +429,12 @@ int add_mtd_partitions(struct mtd_info *master, ...@@ -409,6 +429,12 @@ int add_mtd_partitions(struct mtd_info *master,
slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg; slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg;
if(master->write_user_prot_reg) if(master->write_user_prot_reg)
slave->mtd.write_user_prot_reg = part_write_user_prot_reg; slave->mtd.write_user_prot_reg = part_write_user_prot_reg;
if(master->lock_user_prot_reg)
slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg;
if(master->get_user_prot_info)
slave->mtd.get_user_prot_info = part_get_user_prot_info;
if(master->get_fact_prot_info)
slave->mtd.get_fact_prot_info = part_get_fact_prot_info;
if (master->sync) if (master->sync)
slave->mtd.sync = part_sync; slave->mtd.sync = part_sync;
if (!i && master->suspend && master->resume) { if (!i && master->suspend && master->resume) {
......
/* Common Flash Interface structures /* Common Flash Interface structures
* See http://support.intel.com/design/flash/technote/index.htm * See http://support.intel.com/design/flash/technote/index.htm
* $Id: cfi.h,v 1.51 2005/02/05 02:06:16 nico Exp $ * $Id: cfi.h,v 1.52 2005/02/08 17:11:15 nico Exp $
*/ */
#ifndef __MTD_CFI_H__ #ifndef __MTD_CFI_H__
...@@ -252,7 +252,7 @@ static inline uint32_t cfi_build_cmd_addr(uint32_t cmd_ofs, int interleave, int ...@@ -252,7 +252,7 @@ static inline uint32_t cfi_build_cmd_addr(uint32_t cmd_ofs, int interleave, int
* It looks too long to be inline, but in the common case it should almost all * It looks too long to be inline, but in the common case it should almost all
* get optimised away. * get optimised away.
*/ */
static inline map_word cfi_build_cmd(u_char cmd, struct map_info *map, struct cfi_private *cfi) static inline map_word cfi_build_cmd(u_long cmd, struct map_info *map, struct cfi_private *cfi)
{ {
map_word val = { {0} }; map_word val = { {0} };
int wordwidth, words_per_bus, chip_mode, chips_per_word; int wordwidth, words_per_bus, chip_mode, chips_per_word;
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* *
* (C) 2000 Red Hat. GPLd. * (C) 2000 Red Hat. GPLd.
* *
* $Id: flashchip.h,v 1.15 2004/11/05 22:41:06 nico Exp $ * $Id: flashchip.h,v 1.16 2005/02/08 17:11:15 nico Exp $
* *
*/ */
...@@ -29,6 +29,7 @@ typedef enum { ...@@ -29,6 +29,7 @@ typedef enum {
FL_ERASE_SUSPENDED, FL_ERASE_SUSPENDED,
FL_WRITING, FL_WRITING,
FL_WRITING_TO_BUFFER, FL_WRITING_TO_BUFFER,
FL_OTP_WRITE,
FL_WRITE_SUSPENDING, FL_WRITE_SUSPENDING,
FL_WRITE_SUSPENDED, FL_WRITE_SUSPENDED,
FL_PM_SUSPENDED, FL_PM_SUSPENDED,
......
/* Overhauled routines for dealing with different mmap regions of flash */ /* Overhauled routines for dealing with different mmap regions of flash */
/* $Id: map.h,v 1.46 2005/01/05 17:09:44 dwmw2 Exp $ */ /* $Id: map.h,v 1.47 2005/02/08 17:11:15 nico Exp $ */
#ifndef __LINUX_MTD_MAP_H__ #ifndef __LINUX_MTD_MAP_H__
#define __LINUX_MTD_MAP_H__ #define __LINUX_MTD_MAP_H__
...@@ -263,6 +263,17 @@ static inline map_word map_word_and(struct map_info *map, map_word val1, map_wor ...@@ -263,6 +263,17 @@ static inline map_word map_word_and(struct map_info *map, map_word val1, map_wor
return r; return r;
} }
static inline map_word map_word_clr(struct map_info *map, map_word val1, map_word val2)
{
map_word r;
int i;
for (i=0; i<map_words(map); i++) {
r.x[i] = val1.x[i] & ~val2.x[i];
}
return r;
}
static inline map_word map_word_or(struct map_info *map, map_word val1, map_word val2) static inline map_word map_word_or(struct map_info *map, map_word val1, map_word val2)
{ {
map_word r; map_word r;
...@@ -273,6 +284,7 @@ static inline map_word map_word_or(struct map_info *map, map_word val1, map_word ...@@ -273,6 +284,7 @@ static inline map_word map_word_or(struct map_info *map, map_word val1, map_word
} }
return r; return r;
} }
#define map_word_andequal(m, a, b, z) map_word_equal(m, z, map_word_and(m, a, b)) #define map_word_andequal(m, a, b, z) map_word_equal(m, z, map_word_and(m, a, b))
static inline int map_word_bitsset(struct map_info *map, map_word val1, map_word val2) static inline int map_word_bitsset(struct map_info *map, map_word val1, map_word val2)
...@@ -338,6 +350,7 @@ static inline map_word map_word_ff(struct map_info *map) ...@@ -338,6 +350,7 @@ static inline map_word map_word_ff(struct map_info *map)
} }
return r; return r;
} }
static inline map_word inline_map_read(struct map_info *map, unsigned long ofs) static inline map_word inline_map_read(struct map_info *map, unsigned long ofs)
{ {
map_word r; map_word r;
......
/* /*
* $Id: mtd.h,v 1.56 2004/08/09 18:46:04 dmarlin Exp $ * $Id: mtd.h,v 1.57 2005/02/08 17:11:15 nico Exp $
* *
* Copyright (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> et al. * Copyright (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> et al.
* *
...@@ -113,12 +113,12 @@ struct mtd_info { ...@@ -113,12 +113,12 @@ struct mtd_info {
* flash devices. The user data is one time programmable but the * flash devices. The user data is one time programmable but the
* factory data is read only. * factory data is read only.
*/ */
int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
/* This function is not yet implemented */ int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);
/* kvec-based read/write methods. We need these especially for NAND flash, /* kvec-based read/write methods. We need these especially for NAND flash,
with its limited number of write cycles per erase. with its limited number of write cycles per erase.
......
/* /*
* $Id: mtd-abi.h,v 1.7 2004/11/23 15:37:32 gleixner Exp $ * $Id: mtd-abi.h,v 1.8 2005/02/08 17:11:16 nico Exp $
* *
* Portions of MTD ABI definition which are shared by kernel and user space * Portions of MTD ABI definition which are shared by kernel and user space
*/ */
...@@ -80,6 +80,12 @@ struct region_info_user { ...@@ -80,6 +80,12 @@ struct region_info_user {
uint32_t regionindex; uint32_t regionindex;
}; };
struct otp_info {
uint32_t start;
uint32_t length;
uint32_t locked;
};
#define MEMGETINFO _IOR('M', 1, struct mtd_info_user) #define MEMGETINFO _IOR('M', 1, struct mtd_info_user)
#define MEMERASE _IOW('M', 2, struct erase_info_user) #define MEMERASE _IOW('M', 2, struct erase_info_user)
#define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf) #define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment