forked from Minki/linux
mtd: cfi_cmdset_0002: Add support for reading OTP
The Micron M29EW has a 256 byte one time programmable (OTP) memory. This patch adds support for reading this memory. This support will be extended for locking and writing in subsequent patches. Signed-off-by: Christian Riesch <christian.riesch@omicron.at> Signed-off-by: Brian Norris <computersforpeace@gmail.com>
This commit is contained in:
parent
c14deddec1
commit
dc7e9ecdd6
@ -58,7 +58,15 @@ static void cfi_amdstd_sync (struct mtd_info *);
|
||||
static int cfi_amdstd_suspend (struct mtd_info *);
|
||||
static void cfi_amdstd_resume (struct mtd_info *);
|
||||
static int cfi_amdstd_reboot(struct notifier_block *, unsigned long, void *);
|
||||
static int cfi_amdstd_get_fact_prot_info(struct mtd_info *, size_t,
|
||||
size_t *, struct otp_info *);
|
||||
static int cfi_amdstd_get_user_prot_info(struct mtd_info *, size_t,
|
||||
size_t *, struct otp_info *);
|
||||
static int cfi_amdstd_secsi_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
|
||||
static int cfi_amdstd_read_fact_prot_reg(struct mtd_info *, loff_t, size_t,
|
||||
size_t *, u_char *);
|
||||
static int cfi_amdstd_read_user_prot_reg(struct mtd_info *, loff_t, size_t,
|
||||
size_t *, u_char *);
|
||||
|
||||
static int cfi_amdstd_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t *retlen, const u_char *buf);
|
||||
@ -518,6 +526,10 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
|
||||
mtd->_sync = cfi_amdstd_sync;
|
||||
mtd->_suspend = cfi_amdstd_suspend;
|
||||
mtd->_resume = cfi_amdstd_resume;
|
||||
mtd->_read_user_prot_reg = cfi_amdstd_read_user_prot_reg;
|
||||
mtd->_read_fact_prot_reg = cfi_amdstd_read_fact_prot_reg;
|
||||
mtd->_get_fact_prot_info = cfi_amdstd_get_fact_prot_info;
|
||||
mtd->_get_user_prot_info = cfi_amdstd_get_user_prot_info;
|
||||
mtd->flags = MTD_CAP_NORFLASH;
|
||||
mtd->name = map->name;
|
||||
mtd->writesize = 1;
|
||||
@ -1137,6 +1149,8 @@ static int cfi_amdstd_read (struct mtd_info *mtd, loff_t from, size_t len, size_
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef int (*otp_op_t)(struct map_info *map, struct flchip *chip,
|
||||
loff_t adr, size_t len, u_char *buf);
|
||||
|
||||
static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf)
|
||||
{
|
||||
@ -1219,6 +1233,148 @@ static int cfi_amdstd_secsi_read (struct mtd_info *mtd, loff_t from, size_t len,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cfi_amdstd_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;
|
||||
int ofs_factor = cfi->interleave * cfi->device_type;
|
||||
unsigned long base;
|
||||
int chipnum;
|
||||
struct flchip *chip;
|
||||
uint8_t otp, lockreg;
|
||||
int ret;
|
||||
|
||||
size_t user_size, factory_size, otpsize;
|
||||
loff_t user_offset, factory_offset, otpoffset;
|
||||
int user_locked = 0, otplocked;
|
||||
|
||||
*retlen = 0;
|
||||
|
||||
for (chipnum = 0; chipnum < cfi->numchips; chipnum++) {
|
||||
chip = &cfi->chips[chipnum];
|
||||
factory_size = 0;
|
||||
user_size = 0;
|
||||
|
||||
/* Micron M29EW family */
|
||||
if (is_m29ew(cfi)) {
|
||||
base = chip->start;
|
||||
|
||||
/* check whether secsi area is factory locked
|
||||
or user lockable */
|
||||
mutex_lock(&chip->mutex);
|
||||
ret = get_chip(map, chip, base, FL_CFI_QUERY);
|
||||
if (ret) {
|
||||
mutex_unlock(&chip->mutex);
|
||||
return ret;
|
||||
}
|
||||
cfi_qry_mode_on(base, map, cfi);
|
||||
otp = cfi_read_query(map, base + 0x3 * ofs_factor);
|
||||
cfi_qry_mode_off(base, map, cfi);
|
||||
put_chip(map, chip, base);
|
||||
mutex_unlock(&chip->mutex);
|
||||
|
||||
if (otp & 0x80) {
|
||||
/* factory locked */
|
||||
factory_offset = 0;
|
||||
factory_size = 0x100;
|
||||
} else {
|
||||
/* customer lockable */
|
||||
user_offset = 0;
|
||||
user_size = 0x100;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
ret = get_chip(map, chip, base, FL_LOCKING);
|
||||
|
||||
/* Enter lock register command */
|
||||
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1,
|
||||
chip->start, map, cfi,
|
||||
cfi->device_type, NULL);
|
||||
cfi_send_gen_cmd(0x55, cfi->addr_unlock2,
|
||||
chip->start, map, cfi,
|
||||
cfi->device_type, NULL);
|
||||
cfi_send_gen_cmd(0x40, cfi->addr_unlock1,
|
||||
chip->start, map, cfi,
|
||||
cfi->device_type, NULL);
|
||||
/* read lock register */
|
||||
lockreg = cfi_read_query(map, 0);
|
||||
/* exit protection commands */
|
||||
map_write(map, CMD(0x90), chip->start);
|
||||
map_write(map, CMD(0x00), chip->start);
|
||||
put_chip(map, chip, chip->start);
|
||||
mutex_unlock(&chip->mutex);
|
||||
|
||||
user_locked = ((lockreg & 0x01) == 0x00);
|
||||
}
|
||||
}
|
||||
|
||||
otpsize = user_regs ? user_size : factory_size;
|
||||
if (!otpsize)
|
||||
continue;
|
||||
otpoffset = user_regs ? user_offset : factory_offset;
|
||||
otplocked = user_regs ? user_locked : 1;
|
||||
|
||||
if (!action) {
|
||||
/* return otpinfo */
|
||||
struct otp_info *otpinfo;
|
||||
len -= sizeof(*otpinfo);
|
||||
if (len <= 0)
|
||||
return -ENOSPC;
|
||||
otpinfo = (struct otp_info *)buf;
|
||||
otpinfo->start = from;
|
||||
otpinfo->length = otpsize;
|
||||
otpinfo->locked = otplocked;
|
||||
buf += sizeof(*otpinfo);
|
||||
*retlen += sizeof(*otpinfo);
|
||||
from += otpsize;
|
||||
} else if ((from < otpsize) && (len > 0)) {
|
||||
size_t size;
|
||||
size = (len < otpsize - from) ? len : otpsize - from;
|
||||
ret = action(map, chip, otpoffset + from, size, buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
buf += size;
|
||||
len -= size;
|
||||
*retlen += size;
|
||||
from = 0;
|
||||
} else {
|
||||
from -= otpsize;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cfi_amdstd_get_fact_prot_info(struct mtd_info *mtd, size_t len,
|
||||
size_t *retlen, struct otp_info *buf)
|
||||
{
|
||||
return cfi_amdstd_otp_walk(mtd, 0, len, retlen, (u_char *)buf,
|
||||
NULL, 0);
|
||||
}
|
||||
|
||||
static int cfi_amdstd_get_user_prot_info(struct mtd_info *mtd, size_t len,
|
||||
size_t *retlen, struct otp_info *buf)
|
||||
{
|
||||
return cfi_amdstd_otp_walk(mtd, 0, len, retlen, (u_char *)buf,
|
||||
NULL, 1);
|
||||
}
|
||||
|
||||
static int cfi_amdstd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
|
||||
size_t len, size_t *retlen,
|
||||
u_char *buf)
|
||||
{
|
||||
return cfi_amdstd_otp_walk(mtd, from, len, retlen,
|
||||
buf, do_read_secsi_onechip, 0);
|
||||
}
|
||||
|
||||
static int cfi_amdstd_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
|
||||
size_t len, size_t *retlen,
|
||||
u_char *buf)
|
||||
{
|
||||
return cfi_amdstd_otp_walk(mtd, from, len, retlen,
|
||||
buf, do_read_secsi_onechip, 1);
|
||||
}
|
||||
|
||||
static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, map_word datum)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user