|
@@ -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 {
|
|
@@ -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)
|
|
@@ -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()) {
|
|
@@ -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;
|
|
|
|
|
@@ -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
|