]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/mtd/devices/mtd_dataflash.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/teigland/dlm
[linux-2.6-omap-h63xx.git] / drivers / mtd / devices / mtd_dataflash.c
index 8bd0dea6885f4ad91288d8f5313f08cb636fe10e..6dd9aff8bb2d37181fb711c98f9d839f1fd52a15 100644 (file)
  * doesn't (yet) use these for any kind of i/o overlap or prefetching.
  *
  * Sometimes DataFlash is packaged in MMC-format cards, although the
- * MMC stack can't use SPI (yet), or distinguish between MMC and DataFlash
+ * MMC stack can't (yet?) distinguish between MMC and DataFlash
  * protocols during enumeration.
  */
 
-#define CONFIG_DATAFLASH_WRITE_VERIFY
-
 /* reads can bypass the buffers */
 #define OP_READ_CONTINUOUS     0xE8
 #define OP_READ_PAGE           0xD2
@@ -80,7 +78,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 {
@@ -402,7 +401,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
                (void) dataflash_waitready(priv->spi);
 
 
-#ifdef CONFIG_DATAFLASH_WRITE_VERIFY
+#ifdef CONFIG_MTD_DATAFLASH_VERIFY_WRITE
 
                /* (3) Compare to Buffer1 */
                addr = pageaddr << priv->page_offset;
@@ -431,7 +430,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
                } else
                        status = 0;
 
-#endif /* CONFIG_DATAFLASH_WRITE_VERIFY */
+#endif /* CONFIG_MTD_DATAFLASH_VERIFY_WRITE */
 
                remaining = remaining - writelen;
                pageaddr++;
@@ -451,16 +450,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, char revision)
+{
+       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 +664,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 +698,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 +851,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