Skip to content

Commit

Permalink
[MTD] dataflash OTP support
Browse files Browse the repository at this point in the history
Now that we can tell when we have one of the newer DataFlash chips,
optionally expose the 128 bytes of OTP memory they provide.  Tested
on at45db642 revision B and D chips.

Switch mtdchar over to a generic HAVE_MTD_OTP flag instead of adding
another #ifdef for each type of chip whose driver has OTP support.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Cc: Bryan Wu <cooloney@kernel.org>
Cc: Michael Hennerich <michael.hennerich@analog.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
  • Loading branch information
David Brownell authored and David Woodhouse committed Aug 1, 2008
1 parent 623fa57 commit 34a8244
Show file tree
Hide file tree
Showing 6 changed files with 219 additions and 9 deletions.
5 changes: 5 additions & 0 deletions drivers/mtd/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,11 @@ config MTD_CHAR
memory chips, and also use ioctl() to obtain information about
the device, or to erase parts of it.

config HAVE_MTD_OTP
bool
help
Enable access to OTP regions using MTD_CHAR.

config MTD_BLKDEVS
tristate "Common interface to block layer for MTD 'translation layers'"
depends on BLOCK
Expand Down
1 change: 1 addition & 0 deletions drivers/mtd/chips/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ config MTD_CFI_I8
config MTD_OTP
bool "Protection Registers aka one-time programmable (OTP) bits"
depends on MTD_CFI_ADV_OPTIONS
select HAVE_MTD_OTP
default n
help
This enables support for reading, writing and locking so called
Expand Down
11 changes: 11 additions & 0 deletions drivers/mtd/devices/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,17 @@ config MTD_DATAFLASH
Sometimes DataFlash chips are packaged inside MMC-format
cards; at this writing, the MMC stack won't handle those.

config MTD_DATAFLASH_OTP
bool "DataFlash OTP support (Security Register)"
depends on MTD_DATAFLASH
select HAVE_MTD_OTP
help
Newer DataFlash chips (revisions C and D) support 128 bytes of
one-time-programmable (OTP) data. The first half may be written
(once) with up to 64 bytes of data, such as a serial number or
other key product data. The second half is programmed with a
unique-to-each-chip bit pattern at the factory.

config MTD_M25P80
tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)"
depends on SPI_MASTER && EXPERIMENTAL
Expand Down
206 changes: 199 additions & 7 deletions drivers/mtd/devices/mtd_dataflash.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@
*/
#define OP_READ_ID 0x9F
#define OP_READ_SECURITY 0x77
#define OP_WRITE_SECURITY 0x9A /* OTP bits */
#define OP_WRITE_SECURITY_REVC 0x9A
#define OP_WRITE_SECURITY 0x9B /* revision D */


struct dataflash {
Expand Down Expand Up @@ -451,16 +452,192 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,

/* ......................................................................... */

#ifdef CONFIG_MTD_DATAFLASH_OTP

static int dataflash_get_otp_info(struct mtd_info *mtd,
struct otp_info *info, size_t len)
{
/* Report both blocks as identical: bytes 0..64, locked.
* Unless the user block changed from all-ones, we can't
* tell whether it's still writable; so we assume it isn't.
*/
info->start = 0;
info->length = 64;
info->locked = 1;
return sizeof(*info);
}

static ssize_t otp_read(struct spi_device *spi, unsigned base,
uint8_t *buf, loff_t off, size_t len)
{
struct spi_message m;
size_t l;
uint8_t *scratch;
struct spi_transfer t;
int status;

if (off > 64)
return -EINVAL;

if ((off + len) > 64)
len = 64 - off;
if (len == 0)
return len;

spi_message_init(&m);

l = 4 + base + off + len;
scratch = kzalloc(l, GFP_KERNEL);
if (!scratch)
return -ENOMEM;

/* OUT: OP_READ_SECURITY, 3 don't-care bytes, zeroes
* IN: ignore 4 bytes, data bytes 0..N (max 127)
*/
scratch[0] = OP_READ_SECURITY;

memset(&t, 0, sizeof t);
t.tx_buf = scratch;
t.rx_buf = scratch;
t.len = l;
spi_message_add_tail(&t, &m);

dataflash_waitready(spi);

status = spi_sync(spi, &m);
if (status >= 0) {
memcpy(buf, scratch + 4 + base + off, len);
status = len;
}

kfree(scratch);
return status;
}

static int dataflash_read_fact_otp(struct mtd_info *mtd,
loff_t from, size_t len, size_t *retlen, u_char *buf)
{
struct dataflash *priv = (struct dataflash *)mtd->priv;
int status;

/* 64 bytes, from 0..63 ... start at 64 on-chip */
mutex_lock(&priv->lock);
status = otp_read(priv->spi, 64, buf, from, len);
mutex_unlock(&priv->lock);

if (status < 0)
return status;
*retlen = status;
return 0;
}

static int dataflash_read_user_otp(struct mtd_info *mtd,
loff_t from, size_t len, size_t *retlen, u_char *buf)
{
struct dataflash *priv = (struct dataflash *)mtd->priv;
int status;

/* 64 bytes, from 0..63 ... start at 0 on-chip */
mutex_lock(&priv->lock);
status = otp_read(priv->spi, 0, buf, from, len);
mutex_unlock(&priv->lock);

if (status < 0)
return status;
*retlen = status;
return 0;
}

static int dataflash_write_user_otp(struct mtd_info *mtd,
loff_t from, size_t len, size_t *retlen, u_char *buf)
{
struct spi_message m;
const size_t l = 4 + 64;
uint8_t *scratch;
struct spi_transfer t;
struct dataflash *priv = (struct dataflash *)mtd->priv;
int status;

if (len > 64)
return -EINVAL;

/* Strictly speaking, we *could* truncate the write ... but
* let's not do that for the only write that's ever possible.
*/
if ((from + len) > 64)
return -EINVAL;

/* OUT: OP_WRITE_SECURITY, 3 zeroes, 64 data-or-zero bytes
* IN: ignore all
*/
scratch = kzalloc(l, GFP_KERNEL);
if (!scratch)
return -ENOMEM;
scratch[0] = OP_WRITE_SECURITY;
memcpy(scratch + 4 + from, buf, len);

spi_message_init(&m);

memset(&t, 0, sizeof t);
t.tx_buf = scratch;
t.len = l;
spi_message_add_tail(&t, &m);

/* Write the OTP bits, if they've not yet been written.
* This modifies SRAM buffer1.
*/
mutex_lock(&priv->lock);
dataflash_waitready(priv->spi);
status = spi_sync(priv->spi, &m);
mutex_unlock(&priv->lock);

kfree(scratch);

if (status >= 0) {
status = 0;
*retlen = len;
}
return status;
}

static char *otp_setup(struct mtd_info *device, char revision)
{
device->get_fact_prot_info = dataflash_get_otp_info;
device->read_fact_prot_reg = dataflash_read_fact_otp;
device->get_user_prot_info = dataflash_get_otp_info;
device->read_user_prot_reg = dataflash_read_user_otp;

/* rev c parts (at45db321c and at45db1281 only!) use a
* different write procedure; not (yet?) implemented.
*/
if (revision > 'c')
device->write_user_prot_reg = dataflash_write_user_otp;

return ", OTP";
}

#else

static char *otp_setup(struct mtd_info *device)
{
return " (OTP)";
}

#endif

/* ......................................................................... */

/*
* Register DataFlash device with MTD subsystem.
*/
static int __devinit
add_dataflash(struct spi_device *spi, char *name,
int nr_pages, int pagesize, int pageoffset)
add_dataflash_otp(struct spi_device *spi, char *name,
int nr_pages, int pagesize, int pageoffset, char revision)
{
struct dataflash *priv;
struct mtd_info *device;
struct flash_platform_data *pdata = spi->dev.platform_data;
char *otp_tag = "";

priv = kzalloc(sizeof *priv, GFP_KERNEL);
if (!priv)
Expand Down Expand Up @@ -489,8 +666,12 @@ add_dataflash(struct spi_device *spi, char *name,
device->write = dataflash_write;
device->priv = priv;

dev_info(&spi->dev, "%s (%d KBytes) pagesize %d bytes\n",
name, DIV_ROUND_UP(device->size, 1024), pagesize);
if (revision >= 'c')
otp_tag = otp_setup(device, revision);

dev_info(&spi->dev, "%s (%d KBytes) pagesize %d bytes%s\n",
name, DIV_ROUND_UP(device->size, 1024),
pagesize, otp_tag);
dev_set_drvdata(&spi->dev, priv);

if (mtd_has_partitions()) {
Expand Down Expand Up @@ -519,6 +700,14 @@ add_dataflash(struct spi_device *spi, char *name,
return add_mtd_device(device) == 1 ? -ENODEV : 0;
}

static inline int __devinit
add_dataflash(struct spi_device *spi, char *name,
int nr_pages, int pagesize, int pageoffset)
{
return add_dataflash_otp(spi, name, nr_pages, pagesize,
pageoffset, 0);
}

struct flash_info {
char *name;

Expand Down Expand Up @@ -664,13 +853,16 @@ static int __devinit dataflash_probe(struct spi_device *spi)
* Try to detect dataflash by JEDEC ID.
* If it succeeds we know we have either a C or D part.
* D will support power of 2 pagesize option.
* Both support the security register, though with different
* write procedures.
*/
info = jedec_probe(spi);
if (IS_ERR(info))
return PTR_ERR(info);
if (info != NULL)
return add_dataflash(spi, info->name, info->nr_pages,
info->pagesize, info->pageoffset);
return add_dataflash_otp(spi, info->name, info->nr_pages,
info->pagesize, info->pageoffset,
(info->flags & SUP_POW2PS) ? 'd' : 'c');

/*
* Older chips support only legacy commands, identifing
Expand Down
4 changes: 2 additions & 2 deletions drivers/mtd/mtdchar.c
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ static void mtdchar_erase_callback (struct erase_info *instr)
wake_up((wait_queue_head_t *)instr->priv);
}

#if defined(CONFIG_MTD_OTP) || defined(CONFIG_MTD_ONENAND_OTP)
#ifdef CONFIG_HAVE_MTD_OTP
static int otp_select_filemode(struct mtd_file_info *mfi, int mode)
{
struct mtd_info *mtd = mfi->mtd;
Expand Down Expand Up @@ -663,7 +663,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
break;
}

#if defined(CONFIG_MTD_OTP) || defined(CONFIG_MTD_ONENAND_OTP)
#ifdef CONFIG_HAVE_MTD_OTP
case OTPSELECT:
{
int mode;
Expand Down
1 change: 1 addition & 0 deletions drivers/mtd/onenand/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ config MTD_ONENAND_GENERIC

config MTD_ONENAND_OTP
bool "OneNAND OTP Support"
select HAVE_MTD_OTP
help
One Block of the NAND Flash Array memory is reserved as
a One-Time Programmable Block memory area.
Expand Down

0 comments on commit 34a8244

Please sign in to comment.