123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537 |
- /*
- * This file is part of the flashrom project.
- *
- * Copyright (C) 2014 Google Inc.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * Neither the name of Google or the names of contributors or
- * licensors may be used to endorse or promote products derived from this
- * software without specific prior written permission.
- *
- * This software is provided "AS IS," without a warranty of any kind.
- * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
- * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
- * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
- * GOOGLE INC AND ITS LICENSORS SHALL NOT BE LIABLE
- * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
- * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
- * GOOGLE OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
- * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
- * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
- * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
- * EVEN IF GOOGLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
- *
- * s25f.c - Helper functions for Spansion S25FL and S25FS SPI flash chips.
- * Uses 24 bit addressing for the FS chips and 32 bit addressing for the FL
- * chips (which is required by the overlayed sector size devices).
- * TODO: Implement fancy hybrid sector architecture helpers.
- */
- #include <string.h>
- #include "chipdrivers.h"
- #include "spi.h"
- #include "writeprotect.h"
- /*
- * RDAR and WRAR are supported on chips which have more than one set of status
- * and control registers and take an address of the register to read/write.
- * WRR, RDSR2, and RDCR are used on chips with a more limited set of control/
- * status registers.
- *
- * WRR is somewhat peculiar. It shares the same opcode as JEDEC_WRSR, and if
- * given one data byte (following the opcode) it acts the same way. If it's
- * given two data bytes, the first data byte overwrites status register 1
- * and the second data byte overwrites config register 1.
- */
- #define CMD_WRR 0x01
- #define CMD_WRDI 0x04
- #define CMD_RDSR2 0x07 /* note: read SR1 with JEDEC RDSR opcode */
- #define CMD_RDCR 0x35
- #define CMD_RDAR 0x65
- #define CMD_WRAR 0x71
- /* TODO: For now, commands which use an address assume 24-bit addressing */
- #define CMD_WRR_LEN 3
- #define CMD_WRDI_LEN 1
- #define CMD_RDAR_LEN 4
- #define CMD_WRAR_LEN 5
- #define CMD_RSTEN 0x66
- #define CMD_RST 0x99
- #define CR1NV_ADDR 0x000002
- #define CR1_BPNV_O (1 << 3)
- #define CR1_TBPROT_O (1 << 5)
- #define CR3NV_ADDR 0x000004
- #define CR3NV_20H_NV (1 << 3)
- /* See "Embedded Algorithm Performance Tables for additional timing specs. */
- #define T_W 145 * 1000 /* NV register write time (145ms) */
- #define T_RPH 35 /* Reset pulse hold time (35us) */
- #define S25FS_T_SE 145 * 1000 /* Sector Erase Time (145ms) */
- #define S25FL_T_SE 130 * 1000 /* Sector Erase Time (130ms) */
- static int s25f_legacy_software_reset(const struct flashctx *flash)
- {
- int result;
- struct spi_command cmds[] = {
- {
- .writecnt = 1,
- .writearr = (const unsigned char[]){ CMD_RSTEN },
- .readcnt = 0,
- .readarr = NULL,
- }, {
- .writecnt = 1,
- .writearr = (const unsigned char[]){ 0xf0 },
- .readcnt = 0,
- .readarr = NULL,
- }, {
- .writecnt = 0,
- .writearr = NULL,
- .readcnt = 0,
- .readarr = NULL,
- }};
- result = spi_send_multicommand(flash, cmds);
- if (result) {
- msg_cerr("%s failed during command execution\n", __func__);
- return result;
- }
- /* Allow time for reset command to execute. The datasheet specifies
- * Trph = 35us, double that to be safe. */
- programmer_delay(T_RPH * 2);
- return 0;
- }
- /* "Legacy software reset" is disabled by default on S25FS, use this instead. */
- static int s25fs_software_reset(struct flashctx *flash)
- {
- int result;
- struct spi_command cmds[] = {
- {
- .writecnt = 1,
- .writearr = (const unsigned char[]){ CMD_RSTEN },
- .readcnt = 0,
- .readarr = NULL,
- }, {
- .writecnt = 1,
- .writearr = (const unsigned char[]){ CMD_RST },
- .readcnt = 0,
- .readarr = NULL,
- }, {
- .writecnt = 0,
- .writearr = NULL,
- .readcnt = 0,
- .readarr = NULL,
- }};
- result = spi_send_multicommand(flash, cmds);
- if (result) {
- msg_cerr("%s failed during command execution\n", __func__);
- return result;
- }
- /* Allow time for reset command to execute. Double tRPH to be safe. */
- programmer_delay(T_RPH * 2);
- return 0;
- }
- static int s25f_poll_status(const struct flashctx *flash)
- {
- uint8_t tmp = spi_read_status_register(flash);
- while (tmp & JEDEC_RDSR_BIT_WIP) {
- /*
- * The WIP bit on S25F chips remains set to 1 if erase or
- * programming errors occur, so we must check for those
- * errors here. If an error is encountered, do a software
- * reset to clear WIP and other volatile bits, otherwise
- * the chip will be unresponsive to further commands.
- */
- if (tmp & JEDEC_RDSR_BIT_ERASE_ERR) {
- msg_cerr("Erase error occurred\n");
- s25f_legacy_software_reset(flash);
- return -1;
- }
- if (tmp & (1 << 6)) {
- msg_cerr("Programming error occurred\n");
- s25f_legacy_software_reset(flash);
- return -1;
- }
- programmer_delay(1000 * 10);
- tmp = spi_read_status_register(flash);
- }
- return 0;
- }
- /* "Read Any Register" instruction only supported on S25FS */
- static int s25fs_read_cr(const struct flashctx *flash, uint32_t addr)
- {
- int result;
- uint8_t cfg;
- /* By default, 8 dummy cycles are necessary for variable-latency
- commands such as RDAR (see CR2NV[3:0]). */
- unsigned char read_cr_cmd[] = {
- CMD_RDAR,
- (addr >> 16) & 0xff,
- (addr >> 8) & 0xff,
- (addr & 0xff),
- 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00,
- };
- result = spi_send_command(flash, sizeof(read_cr_cmd), 1, read_cr_cmd, &cfg);
- if (result) {
- msg_cerr("%s failed during command execution at address 0x%x\n",
- __func__, addr);
- return -1;
- }
- return cfg;
- }
- static int s25f_read_cr1(const struct flashctx *flash)
- {
- int result;
- uint8_t cfg;
- unsigned char read_cr_cmd[] = { CMD_RDCR };
- result = spi_send_command(flash, sizeof(read_cr_cmd), 1, read_cr_cmd, &cfg);
- if (result) {
- msg_cerr("%s failed during command execution\n", __func__);
- return -1;
- }
- return cfg;
- }
- /* "Write Any Register" instruction only supported on S25FS */
- static int s25fs_write_cr(const struct flashctx *flash,
- uint32_t addr, uint8_t data)
- {
- int result;
- struct spi_command cmds[] = {
- {
- .writecnt = JEDEC_WREN_OUTSIZE,
- .writearr = (const unsigned char[]){ JEDEC_WREN },
- .readcnt = 0,
- .readarr = NULL,
- }, {
- .writecnt = CMD_WRAR_LEN,
- .writearr = (const unsigned char[]){
- CMD_WRAR,
- (addr >> 16) & 0xff,
- (addr >> 8) & 0xff,
- (addr & 0xff),
- data
- },
- .readcnt = 0,
- .readarr = NULL,
- }, {
- .writecnt = 0,
- .writearr = NULL,
- .readcnt = 0,
- .readarr = NULL,
- }};
- result = spi_send_multicommand(flash, cmds);
- if (result) {
- msg_cerr("%s failed during command execution at address 0x%x\n",
- __func__, addr);
- return -1;
- }
- programmer_delay(T_W);
- return s25f_poll_status(flash);
- }
- static int s25f_write_cr1(const struct flashctx *flash, uint8_t data)
- {
- int result;
- struct spi_command cmds[] = {
- {
- .writecnt = JEDEC_WREN_OUTSIZE,
- .writearr = (const unsigned char[]){ JEDEC_WREN },
- .readcnt = 0,
- .readarr = NULL,
- }, {
- .writecnt = CMD_WRR_LEN,
- .writearr = (const unsigned char[]){
- CMD_WRR,
- spi_read_status_register(flash),
- data,
- },
- .readcnt = 0,
- .readarr = NULL,
- }, {
- .writecnt = 0,
- .writearr = NULL,
- .readcnt = 0,
- .readarr = NULL,
- }};
- result = spi_send_multicommand(flash, cmds);
- if (result) {
- msg_cerr("%s failed during command execution\n", __func__);
- return -1;
- }
- programmer_delay(T_W);
- return s25f_poll_status(flash);
- }
- static int s25fs_restore_cr3nv(struct flashctx *flash, uint8_t cfg)
- {
- int ret = 0;
- msg_cdbg("Restoring CR3NV value to 0x%02x\n", cfg);
- ret |= s25fs_write_cr(flash, CR3NV_ADDR, cfg);
- ret |= s25fs_software_reset(flash);
- return ret;
- }
- /* returns state of top/bottom block protection, or <0 to indicate error */
- static int s25f_get_tbprot_o(const struct flashctx *flash)
- {
- int cr1 = s25f_read_cr1(flash);
- if (cr1 < 0)
- return -1;
- /*
- * 1 = BP starts at bottom (low address)
- * 0 = BP start at top (high address)
- */
- return cr1 & CR1_TBPROT_O ? 1 : 0;
- }
- /* fills modifier_bits struct, returns 0 to indicate success */
- int s25f_get_modifier_bits(const struct flashctx *flash,
- struct generic_modifier_bits *m)
- {
- int tmp;
- memset(m, 0, sizeof(*m));
- tmp = s25f_get_tbprot_o(flash);
- if (tmp < 0)
- return -1;
- m->tb = tmp;
- return 0;
- }
- int s25f_set_modifier_bits(const struct flashctx *flash,
- struct generic_modifier_bits *m)
- {
- int cr1, cr1_orig;
- cr1 = cr1_orig = s25f_read_cr1(flash);
- if (cr1 < 0)
- return -1;
- /*
- * Clear BPNV so that setting BP2-0 in status register gets
- * written to non-volatile memory.
- *
- * For TBPROT:
- * 1 = BP starts at bottom (low address)
- * 0 = BP start at top (high address)
- */
- cr1 &= ~(CR1_BPNV_O | CR1_TBPROT_O);
- cr1 |= m->tb ? CR1_TBPROT_O : 0;
- if (cr1 != cr1_orig) {
- msg_cdbg("%s: setting cr1 bits to 0x%02x\n", __func__, cr1);
- if (s25f_write_cr1(flash, cr1) < 0)
- return -1;
- if (s25f_read_cr1(flash) != cr1) {
- msg_cerr("%s: failed to set CR1 value\n", __func__);
- return -1;
- }
- } else {
- msg_cdbg("%s: cr1 bits already match desired value: "
- "0x%02x\n", __func__, cr1);
- }
- return 0;
- }
- int s25fs_block_erase_d8(struct flashctx *flash,
- unsigned int addr, unsigned int blocklen)
- {
- unsigned char cfg;
- int result;
- static int cr3nv_checked = 0;
- struct spi_command erase_cmds[] = {
- {
- .writecnt = JEDEC_WREN_OUTSIZE,
- .writearr = (const unsigned char[]){ JEDEC_WREN },
- .readcnt = 0,
- .readarr = NULL,
- }, {
- .writecnt = JEDEC_BE_D8_OUTSIZE,
- .writearr = (const unsigned char[]){
- JEDEC_BE_D8,
- (addr >> 16) & 0xff,
- (addr >> 8) & 0xff,
- (addr & 0xff)
- },
- .readcnt = 0,
- .readarr = NULL,
- }, {
- .writecnt = 0,
- .writearr = NULL,
- .readcnt = 0,
- .readarr = NULL,
- }};
- /* Check if hybrid sector architecture is in use and, if so,
- * switch to uniform sectors. */
- if (!cr3nv_checked) {
- cfg = s25fs_read_cr(flash, CR3NV_ADDR);
- if (!(cfg & CR3NV_20H_NV)) {
- s25fs_write_cr(flash, CR3NV_ADDR, cfg | CR3NV_20H_NV);
- s25fs_software_reset(flash);
- cfg = s25fs_read_cr(flash, CR3NV_ADDR);
- if (!(cfg & CR3NV_20H_NV)) {
- msg_cerr("%s: Unable to enable uniform "
- "block sizes.\n", __func__);
- return 1;
- }
- msg_cdbg("\n%s: CR3NV updated (0x%02x -> 0x%02x)\n",
- __func__, cfg,
- s25fs_read_cr(flash, CR3NV_ADDR));
- /* Restore CR3V when flashrom exits */
- register_chip_restore(s25fs_restore_cr3nv, flash, cfg);
- }
- cr3nv_checked = 1;
- }
- result = spi_send_multicommand(flash, erase_cmds);
- if (result) {
- msg_cerr("%s failed during command execution at address 0x%x\n",
- __func__, addr);
- return result;
- }
- programmer_delay(S25FS_T_SE);
- return s25f_poll_status(flash);
- }
- int s25fl_block_erase(struct flashctx *flash,
- unsigned int addr, unsigned int blocklen)
- {
- int result;
- struct spi_command erase_cmds[] = {
- {
- .writecnt = JEDEC_WREN_OUTSIZE,
- .writearr = (const unsigned char[]){
- JEDEC_WREN
- },
- .readcnt = 0,
- .readarr = NULL,
- }, {
- .writecnt = JEDEC_BE_DC_OUTSIZE,
- .writearr = (const unsigned char[]){
- JEDEC_BE_DC,
- (addr >> 24) & 0xff,
- (addr >> 16) & 0xff,
- (addr >> 8) & 0xff,
- (addr & 0xff)
- },
- .readcnt = 0,
- .readarr = NULL,
- }, {
- .writecnt = 0,
- .readcnt = 0,
- }
- };
- result = spi_send_multicommand(flash, erase_cmds);
- if (result) {
- msg_cerr("%s failed during command execution at address 0x%x\n",
- __func__, addr);
- return result;
- }
- programmer_delay(S25FL_T_SE);
- return s25f_poll_status(flash);
- }
- int probe_spi_big_spansion(struct flashctx *flash)
- {
- static const unsigned char cmd = JEDEC_RDID;
- int ret;
- unsigned char dev_id[6]; /* We care only about 6 first bytes */
- ret = spi_send_command(flash, sizeof(cmd), sizeof(dev_id), &cmd, dev_id);
- if (!ret) {
- int i;
- for (i = 0; i < sizeof(dev_id); i++)
- msg_gdbg(" 0x%02x", dev_id[i]);
- msg_gdbg(".\n");
- if (dev_id[0] == flash->manufacture_id) {
- union {
- uint8_t array[4];
- uint32_t whole;
- } model_id;
- /*
- * The structure of the RDID output is as follows:
- *
- * offset value meaning
- * 00h 01h Manufacturer ID for Spansion
- * 01h 20h 128 Mb capacity
- * 01h 02h 256 Mb capacity
- * 02h 18h 128 Mb capacity
- * 02h 19h 256 Mb capacity
- * 03h 4Dh Full size of the RDID output (ignored)
- * 04h 00h FS: 256-kB physical sectors
- * 04h 01h FS: 64-kB physical sectors
- * 04h 00h FL: 256-kB physical sectors
- * 04h 01h FL: Mix of 64-kB and 4KB overlayed sectors
- * 05h 80h FL family
- * 05h 81h FS family
- *
- * Need to use bytes 1, 2, 4, and 5 to properly identify one of eight
- * possible chips:
- *
- * 2 types * 2 possible sizes * 2 possible sector layouts
- *
- */
- memcpy(model_id.array, dev_id + 1, 2);
- memcpy(model_id.array + 2, dev_id + 4, 2);
- if (be_to_cpu32(model_id.whole) == flash->model_id)
- return 1;
- }
- }
- return 0;
- }
|