Commit 493c6460 authored by Kyungmin Park's avatar Kyungmin Park Committed by Jarkko Lavinen

OneNAND: One-Time Programmable (OTP) support

One Block of the NAND Flash Array memory is reserved as
a One-Time Programmable Block memory area.
Also, 1st Block of NAND Flash Array can be used as OTP.

The OTP block can be read, programmed and locked using the same
operations as any other NAND Flash Array memory block.
OTP block cannot be erased.

OTP block is fully-guaranteed to be a valid block.
Signed-off-by: default avatarKyungmin Park <kyungmin.park@samsung.com>
parent 3cecf69e
...@@ -552,7 +552,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, ...@@ -552,7 +552,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
break; break;
} }
#ifdef CONFIG_MTD_OTP #if defined(CONFIG_MTD_OTP) || defined(CONFIG_MTD_ONENAND_OTP)
case OTPSELECT: case OTPSELECT:
{ {
int mode; int mode;
......
...@@ -29,6 +29,20 @@ config MTD_ONENAND_GENERIC ...@@ -29,6 +29,20 @@ config MTD_ONENAND_GENERIC
help help
Support for OneNAND flash via platform device driver. Support for OneNAND flash via platform device driver.
config MTD_ONENAND_OTP
bool "OneNAND OTP Support"
depends on MTD_ONENAND
help
One Block of the NAND Flash Array memory is reserved as
a One-Time Programmable Block memory area.
Also, 1st Block of NAND Flash Array can be used as OTP.
The OTP block can be read, programmed and locked using the same
operations as any other NAND Flash Array memory block.
OTP block cannot be erased.
OTP block is fully-guaranteed to be a valid block.
config MTD_ONENAND_SYNC_READ config MTD_ONENAND_SYNC_READ
bool "OneNAND Sync. Burst Read Support" bool "OneNAND Sync. Burst Read Support"
depends on ARCH_OMAP depends on ARCH_OMAP
......
...@@ -191,7 +191,7 @@ static int onenand_buffer_address(int dataram1, int sectors, int count) ...@@ -191,7 +191,7 @@ static int onenand_buffer_address(int dataram1, int sectors, int count)
static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t len) static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t len)
{ {
struct onenand_chip *this = mtd->priv; struct onenand_chip *this = mtd->priv;
int value, readcmd = 0; int value, readcmd = 0, block_cmd = 0;
int block, page; int block, page;
/* Now we use page size operation */ /* Now we use page size operation */
int sectors = 4, count = 4; int sectors = 4, count = 4;
...@@ -207,6 +207,8 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le ...@@ -207,6 +207,8 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
case ONENAND_CMD_ERASE: case ONENAND_CMD_ERASE:
case ONENAND_CMD_BUFFERRAM: case ONENAND_CMD_BUFFERRAM:
case ONENAND_CMD_OTP_ACCESS:
block_cmd = 1;
block = (int) (addr >> this->erase_shift); block = (int) (addr >> this->erase_shift);
page = -1; page = -1;
break; break;
...@@ -235,7 +237,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le ...@@ -235,7 +237,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
value = onenand_block_address(this, block); value = onenand_block_address(this, block);
this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
if (cmd == ONENAND_CMD_ERASE) { if (cmd == block_cmd) {
/* Select DataRAM for DDP */ /* Select DataRAM for DDP */
value = onenand_bufferram_address(this, block); value = onenand_bufferram_address(this, block);
this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
...@@ -1412,6 +1414,304 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) ...@@ -1412,6 +1414,304 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
return 0; return 0;
} }
#ifdef CONFIG_MTD_ONENAND_OTP
/* Interal OTP operation */
typedef int (*otp_op_t)(struct mtd_info *mtd, loff_t form, size_t len,
size_t *retlen, u_char *buf);
/**
* do_otp_read - [DEFAULT] Read OTP block area
* @param mtd MTD device structure
* @param from The offset to read
* @param len number of bytes to read
* @param retlen pointer to variable to store the number of readbytes
* @param buf the databuffer to put/get data
*
* Read OTP block area.
*/
static int do_otp_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
struct onenand_chip *this = mtd->priv;
int ret;
/* Enter OTP access mode */
this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
this->wait(mtd, FL_OTPING);
ret = mtd->read(mtd, from, len, retlen, buf);
/* Exit OTP access mode */
this->command(mtd, ONENAND_CMD_RESET, 0, 0);
this->wait(mtd, FL_RESETING);
return ret;
}
/**
* do_otp_write - [DEFAULT] Write OTP block area
* @param mtd MTD device structure
* @param from The offset to write
* @param len number of bytes to write
* @param retlen pointer to variable to store the number of write bytes
* @param buf the databuffer to put/get data
*
* Write OTP block area.
*/
static int do_otp_write(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
struct onenand_chip *this = mtd->priv;
unsigned char *pbuf = buf;
int ret;
/* Force buffer page aligned */
if (len < mtd->oobblock) {
memcpy(this->page_buf, buf, len);
memset(this->page_buf + len, 0xff, mtd->oobblock - len);
pbuf = this->page_buf;
len = mtd->oobblock;
}
/* Enter OTP access mode */
this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
this->wait(mtd, FL_OTPING);
ret = mtd->write(mtd, from, len, retlen, pbuf);
/* Exit OTP access mode */
this->command(mtd, ONENAND_CMD_RESET, 0, 0);
this->wait(mtd, FL_RESETING);
return ret;
}
/**
* do_otp_lock - [DEFAULT] Lock OTP block area
* @param mtd MTD device structure
* @param from The offset to lock
* @param len number of bytes to lock
* @param retlen pointer to variable to store the number of lock bytes
* @param buf the databuffer to put/get data
*
* Lock OTP block area.
*/
static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
struct onenand_chip *this = mtd->priv;
int ret;
/* Enter OTP access mode */
this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
this->wait(mtd, FL_OTPING);
ret = mtd->write_oob(mtd, from, len, retlen, buf);
/* Exit OTP access mode */
this->command(mtd, ONENAND_CMD_RESET, 0, 0);
this->wait(mtd, FL_RESETING);
return ret;
}
/**
* onenand_otp_walk - [DEFAULT] Handle OTP operation
* @param mtd MTD device structure
* @param from The offset to read/write
* @param len number of bytes to read/write
* @param retlen pointer to variable to store the number of read bytes
* @param buf the databuffer to put/get data
* @param action do given action
* @param mode specify user and factory
*
* Handle OTP operation.
*/
static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf,
otp_op_t action, int mode)
{
struct onenand_chip *this = mtd->priv;
int otp_pages;
int density;
int ret = 0;
*retlen = 0;
density = this->device_id >> ONENAND_DEVICE_DENSITY_SHIFT;
if (density < ONENAND_DEVICE_DENSITY_512Mb)
otp_pages = 20;
else
otp_pages = 10;
if (mode == MTD_OTP_FACTORY) {
from += mtd->oobblock * otp_pages;
otp_pages = 64 - otp_pages;
}
/* Check User/Factory boundary */
if (((mtd->oobblock * otp_pages) - (from + len)) < 0)
return 0;
while (len > 0 && otp_pages > 0) {
if (!action) { /* OTP Info functions */
struct otp_info *otpinfo;
len -= sizeof(struct otp_info);
if (len <= 0)
return -ENOSPC;
otpinfo = (struct otp_info *) buf;
otpinfo->start = from;
otpinfo->length = mtd->oobblock;
otpinfo->locked = 0;
from += mtd->oobblock;
buf += sizeof(struct otp_info);
*retlen += sizeof(struct otp_info);
} else {
size_t tmp_retlen;
int size = len;
ret = action(mtd, from, len, &tmp_retlen, buf);
buf += size;
len -= size;
*retlen += size;
if (ret < 0)
return ret;
}
otp_pages--;
}
return 0;
}
/**
* onenand_get_fact_prot_info - [MTD Interface] Read factory OTP info
* @param mtd MTD device structure
* @param buf the databuffer to put/get data
* @param len number of bytes to read
*
* Read factory OTP info.
*/
static int onenand_get_fact_prot_info(struct mtd_info *mtd,
struct otp_info *buf, size_t len)
{
size_t retlen;
int ret;
ret = onenand_otp_walk(mtd, 0, len, &retlen, (u_char *) buf, NULL, MTD_OTP_FACTORY);
return ret ? : retlen;
}
/**
* onenand_read_fact_prot_reg - [MTD Interface] Read factory OTP area
* @param mtd MTD device structure
* @param from The offset to read
* @param len number of bytes to read
* @param retlen pointer to variable to store the number of read bytes
* @param buf the databuffer to put/get data
*
* Read factory OTP area.
*/
static int onenand_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen, u_char *buf)
{
return onenand_otp_walk(mtd, from, len, retlen, buf, do_otp_read, MTD_OTP_FACTORY);
}
/**
* onenand_get_user_prot_info - [MTD Interface] Read user OTP info
* @param mtd MTD device structure
* @param buf the databuffer to put/get data
* @param len number of bytes to read
*
* Read user OTP info.
*/
static int onenand_get_user_prot_info(struct mtd_info *mtd,
struct otp_info *buf, size_t len)
{
size_t retlen;
int ret;
ret = onenand_otp_walk(mtd, 0, len, &retlen, (u_char *) buf, NULL, MTD_OTP_USER);
return ret ? : retlen;
}
/**
* onenand_read_user_prot_reg - [MTD Interface] Read user OTP area
* @param mtd MTD device structure
* @param from The offset to read
* @param len number of bytes to read
* @param retlen pointer to variable to store the number of read bytes
* @param buf the databuffer to put/get data
*
* Read user OTP area.
*/
static int onenand_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen, u_char *buf)
{
return onenand_otp_walk(mtd, from, len, retlen, buf, do_otp_read, MTD_OTP_USER);
}
/**
* onenand_write_user_prot_reg - [MTD Interface] Write user OTP area
* @param mtd MTD device structure
* @param from The offset to write
* @param len number of bytes to write
* @param retlen pointer to variable to store the number of write bytes
* @param buf the databuffer to put/get data
*
* Write user OTP area.
*/
static int onenand_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen, u_char *buf)
{
return onenand_otp_walk(mtd, from, len, retlen, buf, do_otp_write, MTD_OTP_USER);
}
/**
* onenand_lock_user_prot_reg - [MTD Interface] Lock user OTP area
* @param mtd MTD device structure
* @param from The offset to lock
* @param len number of bytes to unlock
*
* Write lock mark on spare area in page 0 in OTP block
*/
static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len)
{
unsigned char oob_buf[64];
size_t retlen;
int ret;
memset(oob_buf, 0xff, mtd->oobsize);
/*
* Note: OTP lock operation
* OTP block : 0xXXFC
* 1st block : 0xXXF3 (If chip support)
* Both : 0xXXF0 (If chip support)
*/
oob_buf[ONENAND_OTP_LOCK_OFFSET] = 0xFC;
/*
* Write lock mark to 8th word of sector0 of page0 of the spare0.
* We write 16 bytes spare area instead of 2 bytes.
*/
from = 0;
len = 16;
ret = onenand_otp_walk(mtd, from, len, &retlen, oob_buf, do_otp_lock, MTD_OTP_USER);
return ret ? : retlen;
}
#endif /* CONFIG_MTD_ONENAND_OTP */
/** /**
* onenand_print_device_info - Print device ID * onenand_print_device_info - Print device ID
* @param device device ID * @param device device ID
...@@ -1563,7 +1863,6 @@ static void onenand_resume(struct mtd_info *mtd) ...@@ -1563,7 +1863,6 @@ static void onenand_resume(struct mtd_info *mtd)
"in suspended state\n"); "in suspended state\n");
} }
/** /**
* onenand_scan - [OneNAND Interface] Scan for the OneNAND device * onenand_scan - [OneNAND Interface] Scan for the OneNAND device
* @param mtd MTD device structure * @param mtd MTD device structure
...@@ -1655,6 +1954,14 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) ...@@ -1655,6 +1954,14 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
mtd->write_ecc = onenand_write_ecc; mtd->write_ecc = onenand_write_ecc;
mtd->read_oob = onenand_read_oob; mtd->read_oob = onenand_read_oob;
mtd->write_oob = onenand_write_oob; mtd->write_oob = onenand_write_oob;
#ifdef CONFIG_MTD_ONENAND_OTP
mtd->get_fact_prot_info = onenand_get_fact_prot_info;
mtd->read_fact_prot_reg = onenand_read_fact_prot_reg;
mtd->get_user_prot_info = onenand_get_user_prot_info;
mtd->read_user_prot_reg = onenand_read_user_prot_reg;
mtd->write_user_prot_reg = onenand_write_user_prot_reg;
mtd->lock_user_prot_reg = onenand_lock_user_prot_reg;
#endif
mtd->readv = NULL; mtd->readv = NULL;
mtd->readv_ecc = NULL; mtd->readv_ecc = NULL;
mtd->writev = onenand_writev; mtd->writev = onenand_writev;
......
...@@ -35,6 +35,8 @@ typedef enum { ...@@ -35,6 +35,8 @@ typedef enum {
FL_SYNCING, FL_SYNCING,
FL_UNLOCKING, FL_UNLOCKING,
FL_LOCKING, FL_LOCKING,
FL_RESETING,
FL_OTPING,
FL_PM_SUSPENDED, FL_PM_SUSPENDED,
} onenand_state_t; } onenand_state_t;
......
...@@ -112,6 +112,7 @@ ...@@ -112,6 +112,7 @@
#define ONENAND_CMD_LOCK_TIGHT (0x2C) #define ONENAND_CMD_LOCK_TIGHT (0x2C)
#define ONENAND_CMD_ERASE (0x94) #define ONENAND_CMD_ERASE (0x94)
#define ONENAND_CMD_RESET (0xF0) #define ONENAND_CMD_RESET (0xF0)
#define ONENAND_CMD_OTP_ACCESS (0x65)
#define ONENAND_CMD_READID (0x90) #define ONENAND_CMD_READID (0x90)
/* NOTE: Those are not *REAL* commands */ /* NOTE: Those are not *REAL* commands */
...@@ -152,6 +153,8 @@ ...@@ -152,6 +153,8 @@
#define ONENAND_CTRL_ERASE (1 << 11) #define ONENAND_CTRL_ERASE (1 << 11)
#define ONENAND_CTRL_ERROR (1 << 10) #define ONENAND_CTRL_ERROR (1 << 10)
#define ONENAND_CTRL_RSTB (1 << 7) #define ONENAND_CTRL_RSTB (1 << 7)
#define ONENAND_CTRL_OTP_L (1 << 6)
#define ONENAND_CTRL_OTP_BL (1 << 5)
/* /*
* Interrupt Status Register F241h (R) * Interrupt Status Register F241h (R)
...@@ -177,4 +180,9 @@ ...@@ -177,4 +180,9 @@
#define ONENAND_ECC_2BIT (1 << 1) #define ONENAND_ECC_2BIT (1 << 1)
#define ONENAND_ECC_2BIT_ALL (0xAAAA) #define ONENAND_ECC_2BIT_ALL (0xAAAA)
/*
* One-Time Programmable (OTP)
*/
#define ONENAND_OTP_LOCK_OFFSET (14)
#endif /* __ONENAND_REG_H */ #endif /* __ONENAND_REG_H */
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