]> git.ipfire.org Git - thirdparty/u-boot.git/commitdiff
sf: Update spi_flash framework to handle all sizes of flashes
authorJagannadha Sutradharudu Teki <jaganna@xilinx.com>
Wed, 24 Apr 2013 20:04:00 +0000 (01:34 +0530)
committerMichal Simek <michal.simek@xilinx.com>
Thu, 25 Apr 2013 14:55:47 +0000 (16:55 +0200)
Updated the spi_flash framework to handle all sizes of flashes.

As most of the flashes introduces a bank/extended address registers
for accessing the flashes in 16Mbytes of banks if the flash size
is > 16Mbytes, this new scheme will add the bank selection feature
for performaing write/read/erase operations on all flashes.

u-boot.bin size:
- before 262192bytes
- after 262080bytes
sf speed(65536 bytes wr):
- before 1.528s, speed 43890 B/s
- after 1.533s, speed 43776 B/s

Signed-off-by: Jagannadha Sutradharudu Teki <jaganna@xilinx.com>
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
drivers/mtd/spi/spi_flash.c
drivers/mtd/spi/spi_flash_internal.h
include/spi_flash.h

index 7fe55825687e5f3fb00527c91118fb0264392bdc..c7dc775a5de51f4b84b2a6567468b8072bbce2ba 100644 (file)
@@ -34,30 +34,6 @@ static void spi_flash_addr(struct spi_flash *flash,
        }
 }
 
-static int spi_flash_check_bankaddr_access(struct spi_flash *flash, u32 *offset)
-{
-       int ret;
-
-       if (*offset >= 0x1000000) {
-               ret = spi_flash_bankaddr_access(flash, STATUS_BANKADDR_ENABLE);
-               if (ret) {
-                       debug("SF: fail to %s bank addr bit\n",
-                               STATUS_BANKADDR_ENABLE ? "set" : "reset");
-                       return ret;
-               }
-               *offset -= 0x1000000;
-       } else {
-               ret = spi_flash_bankaddr_access(flash, STATUS_BANKADDR_DISABLE);
-               if (ret) {
-                       debug("SF: fail to %s bank addr bit\n",
-                               STATUS_BANKADDR_DISABLE ? "set" : "reset");
-                       return ret;
-               }
-       }
-
-       return ret;
-}
-
 static int spi_flash_read_write(struct spi_slave *spi,
                                const u8 *cmd, size_t cmd_len,
                                const u8 *data_out, u8 *data_in,
@@ -107,18 +83,11 @@ int spi_flash_cmd_write_multi(struct spi_flash *flash, u32 offset,
        size_t chunk_len, actual;
        int ret;
        u8 cmd[flash->addr_width+1];
+       u32 start;
+       u8 bank_sel;
 
-       if ((flash->size > 0x1000000) && (flash->addr_width == 3)) {
-               ret = spi_flash_check_bankaddr_access(flash, &offset);
-               if (ret) {
-                       debug("SF: fail to acess bank_addr\n");
-                       return ret;
-               }
-       }
-
+       start = offset;
        page_size = flash->page_size;
-       page_addr = offset / page_size;
-       byte_addr = offset % page_size;
 
        ret = spi_claim_bus(flash->spi);
        if (ret) {
@@ -128,6 +97,18 @@ int spi_flash_cmd_write_multi(struct spi_flash *flash, u32 offset,
 
        cmd[0] = CMD_PAGE_PROGRAM;
        for (actual = 0; actual < len; actual += chunk_len) {
+               bank_sel = offset / SPI_FLASH_16MB_BOUN;
+
+               ret = spi_flash_cmd_bankaddr_write(flash,
+                                       bank_sel, flash->idcode0);
+               if (ret) {
+                       debug("SF: fail to set bank%d\n", bank_sel);
+                       return ret;
+               }
+
+               page_addr = (offset & SPI_FLASH_16MB_MASK) / page_size;
+               byte_addr = (offset & SPI_FLASH_16MB_MASK) % page_size;
+
                chunk_len = min(len - actual, page_size - byte_addr);
 
                if (flash->spi->max_write_size)
@@ -156,15 +137,11 @@ int spi_flash_cmd_write_multi(struct spi_flash *flash, u32 offset,
                if (ret)
                        break;
 
-               byte_addr += chunk_len;
-               if (byte_addr == page_size) {
-                       page_addr++;
-                       byte_addr = 0;
-               }
+               offset += chunk_len;
        }
 
        printf("SF: program %s %zu bytes @ %#x\n",
-             ret ? "failure" : "success", len, offset);
+             ret ? "failure" : "success", len, start);
 
        spi_release_bus(flash->spi);
        return ret;
@@ -186,33 +163,54 @@ int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd,
 int spi_flash_cmd_read_fast(struct spi_flash *flash, u32 offset,
                size_t len, void *data)
 {
-       unsigned long page_addr;
-       unsigned long page_size;
-       unsigned long byte_addr;
+       unsigned long page_addr, page_size, byte_addr;
        u8 cmd[flash->addr_width+2];
-       int ret;
-
-       if ((flash->size > 0x1000000) && (flash->addr_width == 3)) {
-               ret = spi_flash_check_bankaddr_access(flash, &offset);
-               if (ret) {
-                       debug("SF: fail to acess bank_addr\n");
-                       return ret;
-               }
-       }
-
-       page_size = flash->page_size;
-       page_addr = offset / page_size;
-       byte_addr = offset % page_size;
+       u8 bank_sel;
+       u32 remain_len, read_len;
+       int ret = -1;
 
        /* Handle memory-mapped SPI */
        if (flash->memory_map)
                memcpy(data, flash->memory_map + offset, len);
 
+       page_size = flash->page_size;
        cmd[0] = CMD_READ_ARRAY_FAST;
-       spi_flash_addr(flash, page_addr, byte_addr, cmd);
        cmd[sizeof(cmd)-1] = 0x00;
 
-       return spi_flash_read_common(flash, cmd, sizeof(cmd), data, len);
+       while (len) {
+               bank_sel = offset / SPI_FLASH_16MB_BOUN;
+               remain_len = (SPI_FLASH_16MB_BOUN * (bank_sel + 1) - offset);
+
+               ret = spi_flash_cmd_bankaddr_write(flash,
+                                       bank_sel, flash->idcode0);
+               if (ret) {
+                       debug("SF: fail to set bank%d\n", bank_sel);
+                       return ret;
+               }
+
+               if (len < remain_len)
+                       read_len = len;
+               else
+                       read_len = remain_len;
+
+               page_addr = (offset & SPI_FLASH_16MB_MASK) / page_size;
+               byte_addr = (offset & SPI_FLASH_16MB_MASK) % page_size;
+
+               spi_flash_addr(flash, page_addr, byte_addr, cmd);
+
+               ret = spi_flash_read_common(flash, cmd, sizeof(cmd),
+                                                       data, read_len);
+               if (ret < 0) {
+                       debug("SF: read failed\n");
+                       break;
+               }
+
+               offset += read_len;
+               len -= read_len;
+               data += read_len;
+       }
+
+       return ret;
 }
 
 int spi_flash_cmd_poll_bit(struct spi_flash *flash, unsigned long timeout,
@@ -264,14 +262,7 @@ int spi_flash_cmd_erase(struct spi_flash *flash, u32 offset, size_t len)
        int ret;
        unsigned long page_addr;
        u8 cmd[flash->addr_width+1];
-
-       if ((flash->size > 0x1000000) && (flash->addr_width == 3)) {
-               ret = spi_flash_check_bankaddr_access(flash, &offset);
-               if (ret) {
-                       debug("SF: fail to acess bank_addr\n");
-                       return ret;
-               }
-       }
+       u8 bank_sel;
 
        erase_size = flash->sector_size;
        if (offset % erase_size || len % erase_size) {
@@ -293,9 +284,17 @@ int spi_flash_cmd_erase(struct spi_flash *flash, u32 offset, size_t len)
        end = start + len;
 
        while (offset < end) {
-               page_addr = offset / flash->page_size;
+               bank_sel = offset / SPI_FLASH_16MB_BOUN;
+
+               ret = spi_flash_cmd_bankaddr_write(flash,
+                                       bank_sel, flash->idcode0);
+               if (ret) {
+                       debug("SF: fail to set bank%d\n", bank_sel);
+                       return ret;
+               }
+
+               page_addr = (offset & SPI_FLASH_16MB_MASK) / flash->page_size;
                spi_flash_addr(flash, page_addr, 0, cmd);
-               offset += erase_size;
 
                debug("SF: erase %2x %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1],
                        cmd[2], cmd[3], cmd[4], offset);
@@ -311,6 +310,8 @@ int spi_flash_cmd_erase(struct spi_flash *flash, u32 offset, size_t len)
                ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PAGE_ERASE_TIMEOUT);
                if (ret)
                        goto out;
+
+               offset += erase_size;
        }
 
        printf("SF: Successfully erased %zu bytes @ %#x\n", len, start);
@@ -347,22 +348,21 @@ int spi_flash_cmd_write_status(struct spi_flash *flash, u8 sr)
        return 0;
 }
 
-int spi_flash_cmd_bankaddr_write(struct spi_flash *flash, u8 ear)
+int spi_flash_cmd_bankaddr_write(struct spi_flash *flash,
+                       u8 bank_sel, u8 idcode0)
 {
        u8 cmd;
-       u8 idcode0;
        int ret;
 
-       ret = spi_flash_cmd(flash->spi, CMD_READ_ID, &idcode0, 1);
-       if (ret) {
-               debug("SF: fail to read read id\n");
-               return ret;
+       if (flash->bank_curr == bank_sel) {
+               debug("SF: not require to enable bank%d\n", bank_sel);
+               return 0;
        }
 
        if (idcode0 == 0x01) {
                cmd = CMD_BANKADDR_BRWR;
        } else if ((idcode0 == 0xef) || (idcode0 == 0x20)) {
-               cmd = CMD_EXT_WREAR;
+               cmd = CMD_EXTNADDR_WREAR;
        } else {
                printf("SF: unable to support extended addr reg write"
                                                " for %s flash\n", flash->name);
@@ -375,11 +375,12 @@ int spi_flash_cmd_bankaddr_write(struct spi_flash *flash, u8 ear)
                return ret;
        }
 
-       ret = spi_flash_cmd_write(flash->spi, &cmd, 1, &ear, 1);
+       ret = spi_flash_cmd_write(flash->spi, &cmd, 1, &bank_sel, 1);
        if (ret) {
                debug("SF: fail to write bank addr register\n");
                return ret;
        }
+       flash->bank_curr = bank_sel;
 
        ret = spi_flash_cmd_wait_ready(flash, SPI_FLASH_PROG_TIMEOUT);
        if (ret < 0) {
@@ -390,22 +391,14 @@ int spi_flash_cmd_bankaddr_write(struct spi_flash *flash, u8 ear)
        return 0;
 }
 
-int spi_flash_cmd_bankaddr_read(struct spi_flash *flash, void *data)
+int spi_flash_cmd_bankaddr_read(struct spi_flash *flash, void *data, u8 idcode0)
 {
        u8 cmd;
-       u8 idcode0;
-       int ret;
-
-       ret = spi_flash_cmd(flash->spi, CMD_READ_ID, &idcode0, 1);
-       if (ret) {
-               debug("SF: fail to read read id\n");
-               return ret;
-       }
 
        if (idcode0 == 0x01) {
                cmd = CMD_BANKADDR_BRRD;
        } else if ((idcode0 == 0xef) || (idcode0 == 0x20)) {
-               cmd = CMD_EXT_RDEAR;
+               cmd = CMD_EXTNADDR_RDEAR;
        } else {
                printf("SF: unable to support extended addr reg read"
                        " for %s flash\n", flash->name);
@@ -415,38 +408,6 @@ int spi_flash_cmd_bankaddr_read(struct spi_flash *flash, void *data)
        return spi_flash_read_common(flash, &cmd, 1, data, 1);
 }
 
-int spi_flash_bankaddr_access(struct spi_flash *flash, u8 status)
-{
-       int ret, pass;
-       u8 data = 0, write_done = 0;
-
-       for (pass = 0; pass < 2; pass++) {
-               ret = spi_flash_cmd_bankaddr_read(flash, (void *)&data);
-               if (ret < 0) {
-                       debug("SF: fail to read bank addr register\n");
-                       return ret;
-               }
-
-               if ((data != status) & !write_done) {
-                       debug("SF: need to %s bank addr bit\n",
-                                               status ? "set" : "reset");
-
-                       write_done = 1;
-                       ret = spi_flash_cmd_bankaddr_write(flash, status);
-                       if (ret < 0) {
-                               debug("SF: fail to write bank addr bit\n");
-                               return ret;
-                       }
-               } else {
-                       debug("SF: bank addr bit is %s.\n",
-                                               status ? "set" : "reset");
-                       return ret;
-               }
-       }
-
-       return -1;
-}
-
 #ifdef CONFIG_OF_CONTROL
 int spi_flash_decode_fdt(const void *blob, struct spi_flash *flash)
 {
@@ -548,6 +509,7 @@ struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs,
        struct spi_flash *flash = NULL;
        int ret, i, shift;
        u8 idcode[IDCODE_LEN], *idp;
+       u8 curr_bank = 0;
 
        spi = spi_setup_slave(bus, cs, max_hz, spi_mode);
        if (!spi) {
@@ -604,6 +566,19 @@ struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs,
                printf(", mapped at %p", flash->memory_map);
        puts("\n");
 
+       flash->idcode0 = *idp;
+       if ((flash->spi->is_dual == 0) &&
+           (flash->size > SPI_FLASH_16MB_BOUN)) {
+               if (spi_flash_cmd_bankaddr_read(flash, &curr_bank,
+                                               flash->idcode0)) {
+                       debug("SF: fail to read bank addr register\n");
+                       goto err_manufacturer_probe;
+               }
+               flash->bank_curr = curr_bank;
+       } else {
+               flash->bank_curr = curr_bank;
+       }
+
        spi_release_bus(spi);
 
        return flash;
index 2a54288e3e7db8d328590944a0c0745093fad0eb..2a5e74cefc7f5ecf88b3eabcf06efeb46e62d21d 100644 (file)
@@ -12,6 +12,9 @@
 #define SPI_FLASH_PAGE_ERASE_TIMEOUT   (5 * CONFIG_SYS_HZ)
 #define SPI_FLASH_SECTOR_ERASE_TIMEOUT (10 * CONFIG_SYS_HZ)
 
+#define SPI_FLASH_16MB_BOUN            0x1000000
+#define SPI_FLASH_16MB_MASK            0xFFFFFF
+
 /* Common commands */
 #define CMD_READ_ID                    0x9f
 
 /* Bank addr acess commands */
 #define CMD_BANKADDR_BRWR              0x17
 #define CMD_BANKADDR_BRRD              0x16
-#define CMD_EXT_WREAR                  0xC5
-#define CMD_EXT_RDEAR                  0xC8
+#define CMD_EXTNADDR_WREAR             0xC5
+#define CMD_EXTNADDR_RDEAR             0xC8
 
 /* Common status */
 #define STATUS_WIP                     0x01
-#define STATUS_BANKADDR_ENABLE         0x01
-#define STATUS_BANKADDR_DISABLE        0x00
 
 /* Send a single-byte command to the device and read the response */
 int spi_flash_cmd(struct spi_slave *spi, u8 cmd, void *response, size_t len);
@@ -86,17 +87,12 @@ static inline int spi_flash_cmd_write_disable(struct spi_flash *flash)
 int spi_flash_cmd_write_status(struct spi_flash *flash, u8 sr);
 
 /* Program the bank address register */
-int spi_flash_cmd_bankaddr_write(struct spi_flash *flash, u8 ear);
+int spi_flash_cmd_bankaddr_write(struct spi_flash *flash,
+                                       u8 bank_sel, u8 idcode0);
 
 /* Read the bank address register */
-int spi_flash_cmd_bankaddr_read(struct spi_flash *flash, void *data);
-
-/*
- * Bank address access
- * access 4th byte address in 3-byte addessing mode for flashes
- * which has an actual size of > 16MB.
- */
-int spi_flash_bankaddr_access(struct spi_flash *flash, u8 status);
+int spi_flash_cmd_bankaddr_read(struct spi_flash *flash,
+                                       void *data, u8 idcode0);
 
 /*
  * Same as spi_flash_cmd_read() except it also claims/releases the SPI
index a3b0dcdfeeace6d2e27785b1c1f93d4b0484b02b..2475be5de36b4fb026431aeb03290c0e5765d28e 100644 (file)
@@ -40,6 +40,10 @@ struct spi_flash {
        u32             sector_size;
        /* To find whether single/dual spi device */
        u8              addr_width;
+       /* ID code0 */
+       u8              idcode0;
+       /* Current bank */
+       u8              bank_curr;
 
        void *memory_map;       /* Address of read-only SPI flash access */
        int             (*read)(struct spi_flash *flash, u32 offset,