mtd: add OTP (one-time-programmable) erase ioctl
This may sound like a contradiction but some SPI-NOR flashes really support erasing their OTP region until it is finally locked. Having the possibility to erase an OTP region might come in handy during development. The ioctl argument follows the OTPLOCK style. Signed-off-by: Michael Walle <michael@walle.cc> Acked-by: Vignesh Raghavendra <vigneshr@ti.com> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> Link: https://lore.kernel.org/linux-mtd/20210303201819.2752-1-michael@walle.cc
This commit is contained in:
parent
1e97743fd1
commit
e3c1f1c92d
@ -666,6 +666,7 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
|
|||||||
case MEMWRITEOOB64:
|
case MEMWRITEOOB64:
|
||||||
case MEMWRITE:
|
case MEMWRITE:
|
||||||
case OTPLOCK:
|
case OTPLOCK:
|
||||||
|
case OTPERASE:
|
||||||
if (!(file->f_mode & FMODE_WRITE))
|
if (!(file->f_mode & FMODE_WRITE))
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
break;
|
break;
|
||||||
@ -930,6 +931,7 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
case OTPLOCK:
|
case OTPLOCK:
|
||||||
|
case OTPERASE:
|
||||||
{
|
{
|
||||||
struct otp_info oinfo;
|
struct otp_info oinfo;
|
||||||
|
|
||||||
@ -937,7 +939,10 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (copy_from_user(&oinfo, argp, sizeof(oinfo)))
|
if (copy_from_user(&oinfo, argp, sizeof(oinfo)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
ret = mtd_lock_user_prot_reg(mtd, oinfo.start, oinfo.length);
|
if (cmd == OTPLOCK)
|
||||||
|
ret = mtd_lock_user_prot_reg(mtd, oinfo.start, oinfo.length);
|
||||||
|
else
|
||||||
|
ret = mtd_erase_user_prot_reg(mtd, oinfo.start, oinfo.length);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1919,6 +1919,18 @@ int mtd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(mtd_lock_user_prot_reg);
|
EXPORT_SYMBOL_GPL(mtd_lock_user_prot_reg);
|
||||||
|
|
||||||
|
int mtd_erase_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len)
|
||||||
|
{
|
||||||
|
struct mtd_info *master = mtd_get_master(mtd);
|
||||||
|
|
||||||
|
if (!master->_erase_user_prot_reg)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
if (!len)
|
||||||
|
return 0;
|
||||||
|
return master->_erase_user_prot_reg(master, from, len);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(mtd_erase_user_prot_reg);
|
||||||
|
|
||||||
/* Chip-supported device locking */
|
/* Chip-supported device locking */
|
||||||
int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||||
{
|
{
|
||||||
|
@ -337,6 +337,8 @@ struct mtd_info {
|
|||||||
size_t len, size_t *retlen, u_char *buf);
|
size_t len, size_t *retlen, u_char *buf);
|
||||||
int (*_lock_user_prot_reg) (struct mtd_info *mtd, loff_t from,
|
int (*_lock_user_prot_reg) (struct mtd_info *mtd, loff_t from,
|
||||||
size_t len);
|
size_t len);
|
||||||
|
int (*_erase_user_prot_reg) (struct mtd_info *mtd, loff_t from,
|
||||||
|
size_t len);
|
||||||
int (*_writev) (struct mtd_info *mtd, const struct kvec *vecs,
|
int (*_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);
|
||||||
void (*_sync) (struct mtd_info *mtd);
|
void (*_sync) (struct mtd_info *mtd);
|
||||||
@ -518,6 +520,7 @@ int mtd_read_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len,
|
|||||||
int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len,
|
int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len,
|
||||||
size_t *retlen, u_char *buf);
|
size_t *retlen, u_char *buf);
|
||||||
int mtd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len);
|
int mtd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len);
|
||||||
|
int mtd_erase_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len);
|
||||||
|
|
||||||
int mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,
|
int mtd_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);
|
||||||
|
@ -205,6 +205,8 @@ struct otp_info {
|
|||||||
* without OOB, e.g., NOR flash.
|
* without OOB, e.g., NOR flash.
|
||||||
*/
|
*/
|
||||||
#define MEMWRITE _IOWR('M', 24, struct mtd_write_req)
|
#define MEMWRITE _IOWR('M', 24, struct mtd_write_req)
|
||||||
|
/* Erase a given range of user data (must be in mode %MTD_FILE_MODE_OTP_USER) */
|
||||||
|
#define OTPERASE _IOW('M', 25, struct otp_info)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Obsolete legacy interface. Keep it in order not to break userspace
|
* Obsolete legacy interface. Keep it in order not to break userspace
|
||||||
|
Loading…
Reference in New Issue
Block a user