This patch add support in spi-nor for allmost all dataflash memories
supported by old mtd_dataflash driver.

Signed-off-by: Radu Pirea <radu.pi...@microchip.com>
---
 drivers/mtd/spi-nor/spi-nor.c | 150 +++++++++++++++++++++++++++++++++++++++++-
 include/linux/mtd/spi-nor.h   |  10 +++
 2 files changed, 158 insertions(+), 2 deletions(-)

diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index d445a4d..4cb3cf7 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -89,6 +89,7 @@ struct flash_info {
 #define NO_CHIP_ERASE          BIT(12) /* Chip does not support chip erase */
 #define SPI_NOR_SKIP_SFDP      BIT(13) /* Skip parsing of SFDP tables */
 #define USE_CLSR               BIT(14) /* use CLSR command */
+#define SPI_DATAFLASH          BIT(15) /* Atmel Dataflash memory */
 
        int     (*quad_enable)(struct spi_nor *nor);
 };
@@ -306,6 +307,20 @@ static int s3an_sr_ready(struct spi_nor *nor)
        return !!(val & XSR_RDY);
 }
 
+static int dataflash_sr_ready(struct spi_nor *nor)
+{
+       int ret;
+       u8 val;
+
+       ret = nor->read_reg(nor, SPINOR_OP_DFRDSR, &val, 1);
+       if (ret < 0) {
+               dev_err(nor->dev, "error %d reading DFSR\n", ret);
+               return ret;
+       }
+
+       return !!(val & DFSR_RDY);
+}
+
 static inline int spi_nor_sr_ready(struct spi_nor *nor)
 {
        int sr = read_sr(nor);
@@ -354,6 +369,8 @@ static int spi_nor_ready(struct spi_nor *nor)
 
        if (nor->flags & SNOR_F_READY_XSR_RDY)
                sr = s3an_sr_ready(nor);
+       else if (nor->flags & SPI_DATAFLASH)
+               sr = dataflash_sr_ready(nor);
        else
                sr = spi_nor_sr_ready(nor);
        if (sr < 0)
@@ -457,6 +474,44 @@ static loff_t spi_nor_s3an_addr_convert(struct spi_nor 
*nor, unsigned int addr)
        return page | offset;
 }
 
+static loff_t spi_nor_dataflash_addr_convert(struct spi_nor *nor,
+                                            unsigned int addr)
+{
+       unsigned int offset;
+       unsigned int page;
+       unsigned int page_offset;
+
+       page = addr / nor->page_size;
+       offset = addr % nor->page_size;
+       page_offset = fls(nor->page_size);
+       if (is_power_of_2(nor->page_size))
+               page_offset--;
+
+       return (page << page_offset) | offset;
+}
+
+static int spi_nor_dataflash_erase_sector(struct spi_nor *nor, u32 addr)
+{
+       u32 block_size = 8 * nor->page_size;
+       u32 blocks = nor->mtd.erasesize / block_size;
+       u32 addr_local;
+       u8 buf[SPI_NOR_MAX_ADDR_WIDTH];
+       int i, j;
+
+       for (j = 0; j < blocks; j++) {
+               addr_local = spi_nor_dataflash_addr_convert(nor, addr);
+               for (i = nor->addr_width - 1; i >= 0; i--) {
+                       buf[i] = addr_local & 0xff;
+                       addr_local >>= 8;
+               }
+               nor->write_reg(nor, nor->erase_opcode, buf, nor->addr_width);
+               addr += block_size;
+               spi_nor_wait_till_ready(nor);
+       }
+
+       return 0;
+}
+
 /*
  * Initiate the erasure of a single sector
  */
@@ -542,7 +597,11 @@ static int spi_nor_erase(struct mtd_info *mtd, struct 
erase_info *instr)
                while (len) {
                        write_enable(nor);
 
-                       ret = spi_nor_erase_sector(nor, addr);
+                       if (nor->flags & SPI_DATAFLASH)
+                               ret = spi_nor_dataflash_erase_sector(nor, addr);
+                       else
+                               ret = spi_nor_erase_sector(nor, addr);
+
                        if (ret)
                                goto erase_err;
 
@@ -914,6 +973,20 @@ static int macronix_quad_enable(struct spi_nor *nor);
                .page_size = 256,                                       \
                .flags = (_flags),
 
+#define INFOP(_jedec_id, _ext_id, _sector_size, _n_sectors, _page_size, 
_flags)        \
+               .id = {                                                 \
+                       ((_jedec_id) >> 16) & 0xff,                     \
+                       ((_jedec_id) >> 8) & 0xff,                      \
+                       (_jedec_id) & 0xff,                             \
+                       ((_ext_id) >> 8) & 0xff,                        \
+                       (_ext_id) & 0xff,                               \
+                       },                                              \
+               .id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))),       
\
+               .sector_size = (_sector_size),                          \
+               .n_sectors = (_n_sectors),                              \
+               .page_size = _page_size,                                \
+               .flags = (_flags),
+
 #define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)    \
                .id = {                                                 \
                        ((_jedec_id) >> 16) & 0xff,                     \
@@ -975,7 +1048,14 @@ static const struct flash_info spi_nor_ids[] = {
        { "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) },
        { "at26df321",  INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) },
 
-       { "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) },
+       { "at45db021d", INFOP(0x1f2300, 0, 512 * 264,  2, 264, SPI_DATAFLASH | 
NO_CHIP_ERASE) },
+       { "at45db041d", INFOP(0x1f2400, 0, 256 * 264,  8, 264, SPI_DATAFLASH | 
NO_CHIP_ERASE) },
+       { "at45db081d", INFOP(0x1f2500, 0, 256 * 264, 16, 264, SPI_DATAFLASH | 
NO_CHIP_ERASE) },
+       { "at45db161d", INFOP(0x1f2600, 0, 256 * 528, 16, 528, SPI_DATAFLASH | 
NO_CHIP_ERASE) },
+       { "at45db321d", INFOP(0x1f2700, 0, 128 * 528, 64, 528, SPI_DATAFLASH | 
NO_CHIP_ERASE) },
+       { "at45db321d", INFOP(0x1f2701, 0, 128 * 528, 64, 528, SPI_DATAFLASH | 
NO_CHIP_ERASE) },
+       { "at45db642d", INFOP(0x1f2800, 0, 256 * 1056, 32, 1056, SPI_DATAFLASH 
| NO_CHIP_ERASE) },
+       { "at45db641e", INFOP(0x1f2800, 0x0100, 1024 * 264, 32, 264, 
SPI_DATAFLASH | NO_CHIP_ERASE) },
 
        /* EON -- en25xxx */
        { "en25f32",    INFO(0x1c3116, 0, 64 * 1024,   64, SECT_4K) },
@@ -1278,6 +1358,9 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t 
from, size_t len,
                if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT)
                        addr = spi_nor_s3an_addr_convert(nor, addr);
 
+               if (nor->flags & SPI_DATAFLASH)
+                       addr = spi_nor_dataflash_addr_convert(nor, addr);
+
                ret = nor->read(nor, addr, len, buf);
                if (ret == 0) {
                        /* We shouldn't see 0-length reads */
@@ -1379,6 +1462,21 @@ static int sst_write(struct mtd_info *mtd, loff_t to, 
size_t len,
        return ret;
 }
 
+static int dataflash_memtobuf(struct spi_nor *nor, u32 addr)
+{
+       u8 buf[SPI_NOR_MAX_ADDR_WIDTH];
+       int i;
+
+       addr = spi_nor_dataflash_addr_convert(nor, addr);
+
+       for (i = nor->addr_width - 1; i >= 0; i--) {
+               buf[i] = addr & 0xff;
+               addr >>= 8;
+       }
+
+       return nor->write_reg(nor, SPINOR_OP_DFMTB, buf, nor->addr_width);
+}
+
 /*
  * Write an address range to the nor chip.  Data must be written in
  * FLASH_PAGESIZE chunks.  The address range may be any size provided
@@ -1420,9 +1518,17 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t 
to, size_t len,
                page_remain = min_t(size_t,
                                    nor->page_size - page_offset, len - i);
 
+               if (nor->flags & SPI_DATAFLASH) {
+                       dataflash_memtobuf(nor, addr);
+                       spi_nor_wait_till_ready(nor);
+               }
+
                if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT)
                        addr = spi_nor_s3an_addr_convert(nor, addr);
 
+               if (nor->flags & SPI_DATAFLASH)
+                       addr = spi_nor_dataflash_addr_convert(nor, addr);
+
                write_enable(nor);
                ret = nor->write(nor, addr, page_remain, buf + i);
                if (ret < 0)
@@ -1742,6 +1848,37 @@ static int s3an_nor_scan(const struct flash_info *info, 
struct spi_nor *nor)
        return 0;
 }
 
+static int dataflash_nor_scan(const struct flash_info *info,
+                             struct spi_nor *nor)
+{
+       int ret;
+       u8 val;
+
+       ret = nor->read_reg(nor, SPINOR_OP_DFRDSR, &val, 1);
+       if (ret < 0) {
+               dev_err(nor->dev, "error reading status register\n");
+               return ret;
+       }
+
+       nor->erase_opcode = SPINOR_OP_DFBE_8k;
+       nor->program_opcode = SPINOR_OP_DFBTOM;
+       nor->read_opcode = SPINOR_OP_DFRD;
+       nor->flags |= SNOR_F_NO_OP_CHIP_ERASE;
+
+       if (val & DFSR_PAGESIZE) {
+               /* Flash in Power of 2 mode */
+               nor->mtd.erasesize = nor->mtd.erasesize / nor->page_size *
+               (1 << (fls(nor->page_size) - 1));
+               nor->mtd.size = nor->mtd.erasesize * info->n_sectors;
+               nor->page_size = 1 << (fls(nor->page_size) - 1);
+               nor->mtd.writebufsize = nor->page_size;
+       }
+
+       nor->read_dummy = 32;
+
+       return 0;
+}
+
 struct spi_nor_read_command {
        u8                      num_mode_clocks;
        u8                      num_wait_states;
@@ -2818,6 +2955,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
        if (info->flags & SPI_S3AN)
                nor->flags |=  SNOR_F_READY_XSR_RDY;
 
+       if (info->flags & SPI_DATAFLASH)
+               nor->flags |= SPI_DATAFLASH;
+
        /* Parse the Serial Flash Discoverable Parameters table. */
        ret = spi_nor_init_params(nor, info, &params);
        if (ret)
@@ -2922,6 +3062,12 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
                        return ret;
        }
 
+       if (info->flags & SPI_DATAFLASH) {
+               ret = dataflash_nor_scan(info, nor);
+               if (ret)
+                       return ret;
+       }
+
        /* Send all the required SPI flash commands to initialize device */
        nor->info = info;
        ret = spi_nor_init(nor);
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index de36969..843edf9 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -91,6 +91,16 @@
 #define SPINOR_OP_WRDI         0x04    /* Write disable */
 #define SPINOR_OP_AAI_WP       0xad    /* Auto address increment word program 
*/
 
+/* Used for Atmel Dataflashes only. */
+#define SPINOR_OP_DFRDSR       0xd7
+#define SPINOR_OP_DFBE_8k      0x50
+#define SPINOR_OP_DFMTB                0x53
+#define SPINOR_OP_DFBTOM       0x82
+#define SPINOR_OP_DFRD         0xe8
+
+#define DFSR_PAGESIZE          BIT(0)  /* Page size in Po2 or Linear */
+#define DFSR_RDY               BIT(7)  /* Ready */
+
 /* Used for S3AN flashes only */
 #define SPINOR_OP_XSE          0x50    /* Sector erase */
 #define SPINOR_OP_XPP          0x82    /* Page program */
-- 
2.7.4

Reply via email to