Author: hailfinger
Date: Thu Jul 29 15:09:18 2010
New Revision: 1115
URL: http://flashrom.org/trac/coreboot/changeset/1115

Log:
Add detailed status register printing and unlocking for all ATMEL AT25*
chips.

Add support for Atmel AT25DF081A and AT25DQ161.

Some chips require EWSR before WRSR, others require WREN before WRSR,
and some support both variants. Add feature_bits to select the correct
SPI command, and default to EWSR.

Signed-off-by: Carl-Daniel Hailfinger <[email protected]>
Tested-by: Steven Rosario
Acked-by: Uwe Hermann <[email protected]>

Modified:
   trunk/chipdrivers.h
   trunk/flash.h
   trunk/flashchips.c
   trunk/flashchips.h
   trunk/spi.c
   trunk/spi.h
   trunk/spi25.c

Modified: trunk/chipdrivers.h
==============================================================================
--- trunk/chipdrivers.h Thu Jul 29 00:20:20 2010        (r1114)
+++ trunk/chipdrivers.h Thu Jul 29 15:09:18 2010        (r1115)
@@ -47,7 +47,17 @@
 int spi_chip_write_256_new(struct flashchip *flash, uint8_t *buf, int start, 
int len);
 int spi_chip_read(struct flashchip *flash, uint8_t *buf, int start, int len);
 uint8_t spi_read_status_register(void);
+int spi_prettyprint_status_register_at25df(struct flashchip *flash);
+int spi_prettyprint_status_register_at25df_sec(struct flashchip *flash);
+int spi_prettyprint_status_register_at25f(struct flashchip *flash);
+int spi_prettyprint_status_register_at25fs010(struct flashchip *flash);
+int spi_prettyprint_status_register_at25fs040(struct flashchip *flash);
 int spi_disable_blockprotect(struct flashchip *flash);
+int spi_disable_blockprotect_at25df(struct flashchip *flash);
+int spi_disable_blockprotect_at25df_sec(struct flashchip *flash);
+int spi_disable_blockprotect_at25f(struct flashchip *flash);
+int spi_disable_blockprotect_at25fs010(struct flashchip *flash);
+int spi_disable_blockprotect_at25fs040(struct flashchip *flash);
 int spi_byte_program(int addr, uint8_t databyte);
 int spi_nbyte_program(int addr, uint8_t *bytes, int len);
 int spi_nbyte_read(int addr, uint8_t *bytes, int len);

Modified: trunk/flash.h
==============================================================================
--- trunk/flash.h       Thu Jul 29 00:20:20 2010        (r1114)
+++ trunk/flash.h       Thu Jul 29 15:09:18 2010        (r1115)
@@ -69,8 +69,9 @@
 
 /*
  * How many different erase functions do we have per chip?
+ * Atmel AT25FS010 has 6 different functions.
  */
-#define NUM_ERASEFUNCTIONS 5
+#define NUM_ERASEFUNCTIONS 6
 
 #define FEATURE_REGISTERMAP    (1 << 0)
 #define FEATURE_BYTEWRITES     (1 << 1)
@@ -82,6 +83,9 @@
 #define FEATURE_ADDR_2AA       (1 << 2)
 #define FEATURE_ADDR_AAA       (2 << 2)
 #define FEATURE_ADDR_SHIFTED   (1 << 5)
+#define FEATURE_WRSR_EWSR      (1 << 6)
+#define FEATURE_WRSR_WREN      (1 << 7)
+#define FEATURE_WRSR_EITHER    (FEATURE_WRSR_EWSR | FEATURE_WRSR_WREN)
 
 struct flashchip {
        const char *vendor;

Modified: trunk/flashchips.c
==============================================================================
--- trunk/flashchips.c  Thu Jul 29 00:20:20 2010        (r1114)
+++ trunk/flashchips.c  Thu Jul 29 15:09:18 2010        (r1115)
@@ -789,6 +789,7 @@
                .model_id       = AT_25DF021,
                .total_size     = 256,
                .page_size      = 256,
+               .feature_bits   = FEATURE_WRSR_WREN,
                .tested         = TEST_UNTESTED,
                .probe          = probe_spi_rdid,
                .probe_timing   = TIMING_ZERO,
@@ -811,7 +812,8 @@
                                .block_erase = spi_block_erase_c7,
                        }
                },
-               .unlock         = spi_disable_blockprotect,
+               .printlock      = spi_prettyprint_status_register_at25df,
+               .unlock         = spi_disable_blockprotect_at25df,
                .write          = spi_chip_write_256,
                .read           = spi_chip_read,
        },
@@ -824,6 +826,7 @@
                .model_id       = AT_25DF041A,
                .total_size     = 512,
                .page_size      = 256,
+               .feature_bits   = FEATURE_WRSR_WREN,
                .tested         = TEST_UNTESTED,
                .probe          = probe_spi_rdid,
                .probe_timing   = TIMING_ZERO,
@@ -846,7 +849,8 @@
                                .block_erase = spi_block_erase_c7,
                        }
                },
-               .unlock         = spi_disable_blockprotect,
+               .printlock      = spi_prettyprint_status_register_at25df,
+               .unlock         = spi_disable_blockprotect_at25df,
                .write          = spi_chip_write_256,
                .read           = spi_chip_read,
        },
@@ -859,6 +863,7 @@
                .model_id       = AT_25DF081,
                .total_size     = 1024,
                .page_size      = 256,
+               .feature_bits   = FEATURE_WRSR_WREN,
                .tested         = TEST_UNTESTED,
                .probe          = probe_spi_rdid,
                .probe_timing   = TIMING_ZERO,
@@ -881,7 +886,45 @@
                                .block_erase = spi_block_erase_c7,
                        }
                },
-               .unlock         = spi_disable_blockprotect,
+               .printlock      = spi_prettyprint_status_register_at25df,
+               .unlock         = spi_disable_blockprotect_at25df,
+               .write          = spi_chip_write_256,
+               .read           = spi_chip_read,
+       },
+
+       {
+               .vendor         = "Atmel",
+               .name           = "AT25DF081A",
+               .bustype        = CHIP_BUSTYPE_SPI,
+               .manufacture_id = ATMEL_ID,
+               .model_id       = AT_25DF081A,
+               .total_size     = 1024,
+               .page_size      = 256,
+               .feature_bits   = FEATURE_WRSR_WREN,
+               .tested         = TEST_UNTESTED,
+               .probe          = probe_spi_rdid,
+               .probe_timing   = TIMING_ZERO,
+               .block_erasers  =
+               {
+                       {
+                               .eraseblocks = { {4 * 1024, 256} },
+                               .block_erase = spi_block_erase_20,
+                       }, {
+                               .eraseblocks = { {32 * 1024, 32} },
+                               .block_erase = spi_block_erase_52,
+                       }, {
+                               .eraseblocks = { {64 * 1024, 16} },
+                               .block_erase = spi_block_erase_d8,
+                       }, {
+                               .eraseblocks = { {1024 * 1024, 1} },
+                               .block_erase = spi_block_erase_60,
+                       }, {
+                               .eraseblocks = { {1024 * 1024, 1} },
+                               .block_erase = spi_block_erase_c7,
+                       }
+               },
+               .printlock      = spi_prettyprint_status_register_at25df_sec,
+               .unlock         = spi_disable_blockprotect_at25df_sec,
                .write          = spi_chip_write_256,
                .read           = spi_chip_read,
        },
@@ -894,6 +937,7 @@
                .model_id       = AT_25DF161,
                .total_size     = 2048,
                .page_size      = 256,
+               .feature_bits   = FEATURE_WRSR_WREN,
                .tested         = TEST_UNTESTED,
                .probe          = probe_spi_rdid,
                .probe_timing   = TIMING_ZERO,
@@ -916,7 +960,8 @@
                                .block_erase = spi_block_erase_c7,
                        }
                },
-               .unlock         = spi_disable_blockprotect,
+               .printlock      = spi_prettyprint_status_register_at25df_sec,
+               .unlock         = spi_disable_blockprotect_at25df_sec,
                .write          = spi_chip_write_256,
                .read           = spi_chip_read,
        },
@@ -929,6 +974,7 @@
                .model_id       = AT_25DF321,
                .total_size     = 4096,
                .page_size      = 256,
+               .feature_bits   = FEATURE_WRSR_WREN,
                .tested         = TEST_OK_PRW,
                .probe          = probe_spi_rdid,
                .probe_timing   = TIMING_ZERO,
@@ -951,7 +997,8 @@
                                .block_erase = spi_block_erase_c7,
                        }
                },
-               .unlock         = spi_disable_blockprotect,
+               .printlock      = spi_prettyprint_status_register_at25df,
+               .unlock         = spi_disable_blockprotect_at25df,
                .write          = spi_chip_write_256,
                .read           = spi_chip_read,
        },
@@ -964,6 +1011,7 @@
                .model_id       = AT_25DF321A,
                .total_size     = 4096,
                .page_size      = 256,
+               .feature_bits   = FEATURE_WRSR_WREN,
                .tested         = TEST_UNTESTED,
                .probe          = probe_spi_rdid,
                .probe_timing   = TIMING_ZERO,
@@ -986,7 +1034,8 @@
                                .block_erase = spi_block_erase_c7,
                        }
                },
-               .unlock         = spi_disable_blockprotect,
+               .printlock      = spi_prettyprint_status_register_at25df_sec,
+               .unlock         = spi_disable_blockprotect_at25df_sec,
                .write          = spi_chip_write_256,
                .read           = spi_chip_read,
        },
@@ -999,6 +1048,7 @@
                .model_id       = AT_25DF641,
                .total_size     = 8192,
                .page_size      = 256,
+               .feature_bits   = FEATURE_WRSR_WREN,
                .tested         = TEST_UNTESTED,
                .probe          = probe_spi_rdid,
                .probe_timing   = TIMING_ZERO,
@@ -1021,7 +1071,45 @@
                                .block_erase = spi_block_erase_c7,
                        }
                },
-               .unlock         = spi_disable_blockprotect,
+               .printlock      = spi_prettyprint_status_register_at25df_sec,
+               .unlock         = spi_disable_blockprotect_at25df_sec,
+               .write          = spi_chip_write_256,
+               .read           = spi_chip_read,
+       },
+
+       {
+               .vendor         = "Atmel",
+               .name           = "AT25DQ161",
+               .bustype        = CHIP_BUSTYPE_SPI,
+               .manufacture_id = ATMEL_ID,
+               .model_id       = AT_25DQ161,
+               .total_size     = 2048,
+               .page_size      = 256,
+               .feature_bits   = FEATURE_WRSR_WREN,
+               .tested         = TEST_UNTESTED,
+               .probe          = probe_spi_rdid,
+               .probe_timing   = TIMING_ZERO,
+               .block_erasers  =
+               {
+                       {
+                               .eraseblocks = { {4 * 1024, 512} },
+                               .block_erase = spi_block_erase_20,
+                       }, {
+                               .eraseblocks = { {32 * 1024, 64} },
+                               .block_erase = spi_block_erase_52,
+                       }, {
+                               .eraseblocks = { {64 * 1024, 32} },
+                               .block_erase = spi_block_erase_d8,
+                       }, {
+                               .eraseblocks = { {2 * 1024 * 1024, 1} },
+                               .block_erase = spi_block_erase_60,
+                       }, {
+                               .eraseblocks = { {2 * 1024 * 1024, 1} },
+                               .block_erase = spi_block_erase_c7,
+                       }
+               },
+               .printlock      = spi_prettyprint_status_register_at25df_sec,
+               .unlock         = spi_disable_blockprotect_at25df_sec,
                .write          = spi_chip_write_256,
                .read           = spi_chip_read,
        },
@@ -1034,6 +1122,7 @@
                .model_id       = AT_25F512B,
                .total_size     = 64,
                .page_size      = 256,
+               .feature_bits   = FEATURE_WRSR_WREN,
                .tested         = TEST_UNTESTED,
                .probe          = probe_spi_rdid,
                .probe_timing   = TIMING_ZERO,
@@ -1056,7 +1145,8 @@
                                .block_erase = spi_block_erase_c7,
                        }
                },
-               .unlock         = spi_disable_blockprotect,
+               .printlock      = spi_prettyprint_status_register_at25f,
+               .unlock         = spi_disable_blockprotect_at25f,
                .write          = spi_chip_write_256,
                .read           = spi_chip_read,
        },
@@ -1078,6 +1168,9 @@
                                .eraseblocks = { {4 * 1024, 32} },
                                .block_erase = spi_block_erase_20,
                        }, {
+                               .eraseblocks = { {4 * 1024, 32} },
+                               .block_erase = spi_block_erase_d7,
+                       }, {
                                .eraseblocks = { {32 * 1024, 4} },
                                .block_erase = spi_block_erase_52,
                        }, {
@@ -1091,7 +1184,8 @@
                                .block_erase = spi_block_erase_c7,
                        }
                },
-               .unlock         = spi_disable_blockprotect,
+               .printlock      = spi_prettyprint_status_register_at25fs010,
+               .unlock         = spi_disable_blockprotect_at25fs010,
                .write          = spi_chip_write_256,
                .read           = spi_chip_read,
        },
@@ -1126,7 +1220,8 @@
                                .block_erase = spi_block_erase_c7,
                        }
                },
-               .unlock         = spi_disable_blockprotect,
+               .printlock      = spi_prettyprint_status_register_at25fs040,
+               .unlock         = spi_disable_blockprotect_at25fs040,
                .write          = spi_chip_write_256,
                .read           = spi_chip_read,
        },

Modified: trunk/flashchips.h
==============================================================================
--- trunk/flashchips.h  Thu Jul 29 00:20:20 2010        (r1114)
+++ trunk/flashchips.h  Thu Jul 29 15:09:18 2010        (r1115)
@@ -110,12 +110,19 @@
 #define AT_25DF021             0x4300
 #define AT_25DF041A            0x4401
 #define AT_25DF081             0x4502
+#define AT_25DF081A            0x4501  /* Yes, 81A has a lower number than 81 
*/
 #define AT_25DF161             0x4602
 #define AT_25DF321             0x4700  /* Same as 26DF321 */
 #define AT_25DF321A            0x4701
 #define AT_25DF641             0x4800
-#define AT_25F512A             0x65 /* Needs special RDID. AT25F512A_RDID 15 
1d */
+#define AT_25DQ161             0x8600
+#define AT25F512               /* No device ID found in datasheet. Vendor ID
+                                * can be read with AT25F512A_RDID */
+#define AT_25F512A             0x65 /* Needs AT25F512A_RDID */
 #define AT_25F512B             0x6500
+#define AT25F1024              /* No device ID found in datasheet. Vendor ID
+                                * can be read with AT25F512A_RDID */
+#define AT_25F1024A            0x60 /* Needs AT25F512A_RDID */
 #define AT_25FS010             0x6601
 #define AT_25FS040             0x6604
 #define AT_26DF041             0x4400

Modified: trunk/spi.c
==============================================================================
--- trunk/spi.c Thu Jul 29 00:20:20 2010        (r1114)
+++ trunk/spi.c Thu Jul 29 15:09:18 2010        (r1115)
@@ -30,8 +30,6 @@
 
 enum spi_controller spi_controller = SPI_CONTROLLER_NONE;
 
-void spi_prettyprint_status_register(struct flashchip *flash);
-
 const struct spi_programmer spi_programmer[] = {
        { /* SPI_CONTROLLER_NONE */
                .command = NULL,

Modified: trunk/spi.h
==============================================================================
--- trunk/spi.h Thu Jul 29 00:20:20 2010        (r1114)
+++ trunk/spi.h Thu Jul 29 15:09:18 2010        (r1115)
@@ -31,7 +31,7 @@
 #define JEDEC_RDID_INSIZE      0x03
 
 /* AT25F512A has bit 3 as don't care bit in commands */
-#define AT25F512A_RDID         0x15
+#define AT25F512A_RDID         0x15    /* 0x15 or 0x1d */
 #define AT25F512A_RDID_OUTSIZE 0x01
 #define AT25F512A_RDID_INSIZE  0x02
 
@@ -123,5 +123,6 @@
 #define SPI_INVALID_OPCODE     -2
 #define SPI_INVALID_ADDRESS    -3
 #define SPI_INVALID_LENGTH     -4
+#define SPI_FLASHROM_BUG       -5
 
 #endif         /* !__SPI_H__ */

Modified: trunk/spi25.c
==============================================================================
--- trunk/spi25.c       Thu Jul 29 00:20:20 2010        (r1114)
+++ trunk/spi25.c       Thu Jul 29 15:09:18 2010        (r1115)
@@ -312,7 +312,16 @@
 }
 
 /* Prettyprint the status register. Common definitions. */
-void spi_prettyprint_status_register_common(uint8_t status)
+static void spi_prettyprint_status_register_welwip(uint8_t status)
+{
+       msg_cdbg("Chip status register: Write Enable Latch (WEL) is "
+                    "%sset\n", (status & (1 << 1)) ? "" : "not ");
+       msg_cdbg("Chip status register: Write In Progress (WIP/BUSY) is "
+                    "%sset\n", (status & (1 << 0)) ? "" : "not ");
+}
+
+/* Prettyprint the status register. Common definitions. */
+static void spi_prettyprint_status_register_common(uint8_t status)
 {
        msg_cdbg("Chip status register: Bit 5 / Block Protect 3 (BP3) is "
                     "%sset\n", (status & (1 << 5)) ? "" : "not ");
@@ -322,10 +331,7 @@
                     "%sset\n", (status & (1 << 3)) ? "" : "not ");
        msg_cdbg("Chip status register: Bit 2 / Block Protect 0 (BP0) is "
                     "%sset\n", (status & (1 << 2)) ? "" : "not ");
-       msg_cdbg("Chip status register: Write Enable Latch (WEL) is "
-                    "%sset\n", (status & (1 << 1)) ? "" : "not ");
-       msg_cdbg("Chip status register: Write In Progress (WIP/BUSY) is "
-                    "%sset\n", (status & (1 << 0)) ? "" : "not ");
+       spi_prettyprint_status_register_welwip(status);
 }
 
 /* Prettyprint the status register. Works for
@@ -338,6 +344,121 @@
        spi_prettyprint_status_register_common(status);
 }
 
+/* Prettyprint the status register. Common definitions. */
+static void spi_prettyprint_status_register_at25_srplepewpp(uint8_t status)
+{
+       msg_cdbg("Chip status register: Sector Protection Register Lock (SRPL) "
+                "is %sset\n", (status & (1 << 7)) ? "" : "not ");
+       msg_cdbg("Chip status register: Bit 6 "
+                "is %sset\n", (status & (1 << 6)) ? "" : "not ");
+       msg_cdbg("Chip status register: Erase/Program Error (EPE) "
+                "is %sset\n", (status & (1 << 5)) ? "" : "not ");
+       msg_cdbg("Chip status register: WP# pin (WPP) "
+                "is %sactive\n", (status & (1 << 4)) ? "not " : "");
+}
+
+int spi_prettyprint_status_register_at25df(struct flashchip *flash)
+{
+       uint8_t status;
+
+       status = spi_read_status_register();
+       msg_cdbg("Chip status register is %02x\n", status);
+
+       spi_prettyprint_status_register_at25_srplepewpp(status);
+       msg_cdbg("Chip status register: Software Protection Status (SWP): ");
+       switch (status & (3 << 2)) {
+       case 0x0 << 2:
+               msg_cdbg("no sectors are protected\n");
+               break;
+       case 0x1 << 2:
+               msg_cdbg("some sectors are protected\n");
+               /* FIXME: Read individual Sector Protection Registers. */
+               break;
+       case 0x3 << 2:
+               msg_cdbg("all sectors are protected\n");
+               break;
+       default:
+               msg_cdbg("reserved for future use\n");
+               break;
+       }
+       spi_prettyprint_status_register_welwip(status);
+       return 0;
+}
+
+int spi_prettyprint_status_register_at25df_sec(struct flashchip *flash)
+{
+       /* FIXME: We should check the security lockdown. */
+       msg_cdbg("Ignoring security lockdown (if present)\n");
+       msg_cdbg("Ignoring status register byte 2\n");
+       return spi_prettyprint_status_register_at25df(flash);
+}
+
+int spi_prettyprint_status_register_at25f(struct flashchip *flash)
+{
+       uint8_t status;
+
+       status = spi_read_status_register();
+       msg_cdbg("Chip status register is %02x\n", status);
+
+       spi_prettyprint_status_register_at25_srplepewpp(status);
+       msg_cdbg("Chip status register: Bit 3 "
+                "is %sset\n", (status & (1 << 3)) ? "" : "not ");
+       msg_cdbg("Chip status register: Block Protect 0 (BP0) is "
+                "%sset, %s sectors are protected\n",
+                (status & (1 << 2)) ? "" : "not ",
+                (status & (1 << 2)) ? "all" : "no");
+       spi_prettyprint_status_register_welwip(status);
+       return 0;
+}
+
+int spi_prettyprint_status_register_at25fs010(struct flashchip *flash)
+{
+       uint8_t status;
+
+       status = spi_read_status_register();
+       msg_cdbg("Chip status register is %02x\n", status);
+
+       msg_cdbg("Chip status register: Status Register Write Protect (WPEN) "
+                "is %sset\n", (status & (1 << 7)) ? "" : "not ");
+       msg_cdbg("Chip status register: Bit 6 / Block Protect 4 (BP4) is "
+                "%sset\n", (status & (1 << 6)) ? "" : "not ");
+       msg_cdbg("Chip status register: Bit 5 / Block Protect 3 (BP3) is "
+                "%sset\n", (status & (1 << 5)) ? "" : "not ");
+       msg_cdbg("Chip status register: Bit 4 is "
+                "%sset\n", (status & (1 << 4)) ? "" : "not ");
+       msg_cdbg("Chip status register: Bit 3 / Block Protect 1 (BP1) is "
+                "%sset\n", (status & (1 << 3)) ? "" : "not ");
+       msg_cdbg("Chip status register: Bit 2 / Block Protect 0 (BP0) is "
+                "%sset\n", (status & (1 << 2)) ? "" : "not ");
+       /* FIXME: Pretty-print detailed sector protection status. */
+       spi_prettyprint_status_register_welwip(status);
+       return 0;
+}
+
+int spi_prettyprint_status_register_at25fs040(struct flashchip *flash)
+{
+       uint8_t status;
+
+       status = spi_read_status_register();
+       msg_cdbg("Chip status register is %02x\n", status);
+
+       msg_cdbg("Chip status register: Status Register Write Protect (WPEN) "
+                "is %sset\n", (status & (1 << 7)) ? "" : "not ");
+       msg_cdbg("Chip status register: Bit 6 / Block Protect 4 (BP4) is "
+                "%sset\n", (status & (1 << 6)) ? "" : "not ");
+       msg_cdbg("Chip status register: Bit 5 / Block Protect 3 (BP3) is "
+                "%sset\n", (status & (1 << 5)) ? "" : "not ");
+       msg_cdbg("Chip status register: Bit 4 / Block Protect 2 (BP2) is "
+                "%sset\n", (status & (1 << 4)) ? "" : "not ");
+       msg_cdbg("Chip status register: Bit 3 / Block Protect 1 (BP1) is "
+                "%sset\n", (status & (1 << 3)) ? "" : "not ");
+       msg_cdbg("Chip status register: Bit 2 / Block Protect 0 (BP0) is "
+                "%sset\n", (status & (1 << 2)) ? "" : "not ");
+       /* FIXME: Pretty-print detailed sector protection status. */
+       spi_prettyprint_status_register_welwip(status);
+       return 0;
+}
+
 /* Prettyprint the status register. Works for
  * ST M25P series
  * MX MX25L series
@@ -732,12 +853,12 @@
  * This is according the SST25VF016 datasheet, who knows it is more
  * generic that this...
  */
-int spi_write_status_register(int status)
+static int spi_write_status_register_ewsr(struct flashchip *flash, int status)
 {
        int result;
        struct spi_command cmds[] = {
        {
-       /* FIXME: WRSR requires either EWSR or WREN depending on chip type. */
+       /* WRSR requires either EWSR or WREN depending on chip type. */
                .writecnt       = JEDEC_EWSR_OUTSIZE,
                .writearr       = (const unsigned char[]){ JEDEC_EWSR },
                .readcnt        = 0,
@@ -759,9 +880,59 @@
                msg_cerr("%s failed during command execution\n",
                        __func__);
        }
+       /* WRSR performs a self-timed erase before the changes take effect. */
+       programmer_delay(100 * 1000);
        return result;
 }
 
+static int spi_write_status_register_wren(struct flashchip *flash, int status)
+{
+       int result;
+       struct spi_command cmds[] = {
+       {
+       /* WRSR requires either EWSR or WREN depending on chip type. */
+               .writecnt       = JEDEC_WREN_OUTSIZE,
+               .writearr       = (const unsigned char[]){ JEDEC_WREN },
+               .readcnt        = 0,
+               .readarr        = NULL,
+       }, {
+               .writecnt       = JEDEC_WRSR_OUTSIZE,
+               .writearr       = (const unsigned char[]){ JEDEC_WRSR, 
(unsigned char) status },
+               .readcnt        = 0,
+               .readarr        = NULL,
+       }, {
+               .writecnt       = 0,
+               .writearr       = NULL,
+               .readcnt        = 0,
+               .readarr        = NULL,
+       }};
+
+       result = spi_send_multicommand(cmds);
+       if (result) {
+               msg_cerr("%s failed during command execution\n",
+                       __func__);
+       }
+       /* WRSR performs a self-timed erase before the changes take effect. */
+       programmer_delay(100 * 1000);
+       return result;
+}
+
+static int spi_write_status_register(struct flashchip *flash, int status)
+{
+       int ret = 1;
+
+       if (!(flash->feature_bits & (FEATURE_WRSR_WREN | FEATURE_WRSR_EWSR))) {
+               msg_cdbg("Missing status register write definition, assuming "
+                        "EWSR is needed\n");
+               flash->feature_bits |= FEATURE_WRSR_EWSR;
+       }
+       if (flash->feature_bits & FEATURE_WRSR_WREN)
+               ret = spi_write_status_register_wren(flash, status);
+       if (ret && (flash->feature_bits & FEATURE_WRSR_EWSR))
+               ret = spi_write_status_register_ewsr(flash, status);
+       return ret;
+}
+
 int spi_byte_program(int addr, uint8_t databyte)
 {
        int result;
@@ -844,26 +1015,153 @@
        return result;
 }
 
+/* A generic brute-force block protection disable works like this:
+ * Write 0x00 to the status register. Check if any locks are still set (that
+ * part is chip specific). Repeat once.
+ */
 int spi_disable_blockprotect(struct flashchip *flash)
 {
        uint8_t status;
        int result;
 
        status = spi_read_status_register();
-       /* If there is block protection in effect, unprotect it first. */
+       /* If block protection is disabled, stop here. */
+       if ((status & 0x3c) == 0)
+               return 0;
+
+       msg_cdbg("Some block protection in effect, disabling\n");
+       result = spi_write_status_register(flash, status & ~0x3c);
+       if (result) {
+               msg_cerr("spi_write_status_register failed\n");
+               return result;
+       }
+       status = spi_read_status_register();
        if ((status & 0x3c) != 0) {
-               msg_cdbg("Some block protection in effect, disabling\n");
-               result = spi_write_status_register(status & ~0x3c);
+               msg_cerr("Block protection could not be disabled!\n");
+               return 1;
+       }
+       return 0;
+}
+
+int spi_disable_blockprotect_at25df(struct flashchip *flash)
+{
+       uint8_t status;
+       int result;
+
+       status = spi_read_status_register();
+       /* If block protection is disabled, stop here. */
+       if ((status & (3 << 2)) == 0)
+               return 0;
+
+       msg_cdbg("Some block protection in effect, disabling\n");
+       if (status & (1 << 7)) {
+               msg_cdbg("Need to disable Sector Protection Register Lock\n");
+               if ((status & (1 << 4)) == 0) {
+                       msg_cerr("WP# pin is active, disabling "
+                                "write protection is impossible.\n");
+                       return 1;
+               }
+               /* All bits except bit 7 (SPRL) are readonly. */
+               result = spi_write_status_register(flash, status & ~(1 << 7));
                if (result) {
                        msg_cerr("spi_write_status_register failed\n");
                        return result;
                }
-               status = spi_read_status_register();
-               if ((status & 0x3c) != 0) {
-                       msg_cerr("Block protection could not be disabled!\n");
-                       return 1;
+               
+       }
+       /* Global unprotect. Make sure to mask SPRL as well. */
+       result = spi_write_status_register(flash, status & ~0xbc);
+       if (result) {
+               msg_cerr("spi_write_status_register failed\n");
+               return result;
+       }
+       status = spi_read_status_register();
+       if ((status & (3 << 2)) != 0) {
+               msg_cerr("Block protection could not be disabled!\n");
+               return 1;
+       }
+       return 0;
+}
+
+int spi_disable_blockprotect_at25df_sec(struct flashchip *flash)
+{
+       /* FIXME: We should check the security lockdown. */
+       msg_cinfo("Ignoring security lockdown (if present)\n");
+       return spi_disable_blockprotect_at25df(flash);
+}
+
+int spi_disable_blockprotect_at25f(struct flashchip *flash)
+{
+       /* spi_disable_blockprotect_at25df is not really the right way to do
+        * this, but the side effects of said function work here as well.
+        */
+       return spi_disable_blockprotect_at25df(flash);
+}
+
+int spi_disable_blockprotect_at25fs010(struct flashchip *flash)
+{
+       uint8_t status;
+       int result;
+
+       status = spi_read_status_register();
+       /* If block protection is disabled, stop here. */
+       if ((status & 0x6c) == 0)
+               return 0;
+
+       msg_cdbg("Some block protection in effect, disabling\n");
+       if (status & (1 << 7)) {
+               msg_cdbg("Need to disable Status Register Write Protect\n");
+               /* Clear bit 7 (WPEN). */
+               result = spi_write_status_register(flash, status & ~(1 << 7));
+               if (result) {
+                       msg_cerr("spi_write_status_register failed\n");
+                       return result;
                }
        }
+       /* Global unprotect. Make sure to mask WPEN as well. */
+       result = spi_write_status_register(flash, status & ~0xec);
+       if (result) {
+               msg_cerr("spi_write_status_register failed\n");
+               return result;
+       }
+       status = spi_read_status_register();
+       if ((status & 0x6c) != 0) {
+               msg_cerr("Block protection could not be disabled!\n");
+               return 1;
+       }
+       return 0;
+}
+int spi_disable_blockprotect_at25fs040(struct flashchip *flash)
+{
+       uint8_t status;
+       int result;
+
+       status = spi_read_status_register();
+       /* If block protection is disabled, stop here. */
+       if ((status & 0x7c) == 0)
+               return 0;
+
+       msg_cdbg("Some block protection in effect, disabling\n");
+       if (status & (1 << 7)) {
+               msg_cdbg("Need to disable Status Register Write Protect\n");
+               /* Clear bit 7 (WPEN). */
+               result = spi_write_status_register(flash, status & ~(1 << 7));
+               if (result) {
+                       msg_cerr("spi_write_status_register failed\n");
+                       return result;
+               }
+       }
+       /* Global unprotect. Make sure to mask WPEN as well. */
+       result = spi_write_status_register(flash, status & ~0xfc);
+       if (result) {
+               msg_cerr("spi_write_status_register failed\n");
+               return result;
+       }
+       status = spi_read_status_register();
+       if ((status & 0x7c) != 0) {
+               msg_cerr("Block protection could not be disabled!\n");
+               return 1;
+       }
        return 0;
 }
 

_______________________________________________
flashrom mailing list
[email protected]
http://www.flashrom.org/mailman/listinfo/flashrom

Reply via email to