===================================================================
@@ -87,6 +87,15 @@
int spi_disable_blockprotect_at25fs010(struct flashctx *flash);
int spi_disable_blockprotect_at25fs040(struct flashctx *flash);
+/* at45db.c */
+int probe_spi_at45db(struct flashctx *flash);
+int read_at45db(struct flashctx *flash, uint8_t *buf, unsigned int start,
+ unsigned int len);
+int erase_at45db_sector(struct flashctx *flash, unsigned int addr,
+ unsigned int blocklen);
+int spi_write_at45db(struct flashctx *flash, uint8_t *buf,
+ unsigned int start, unsigned int len);
+
/* 82802ab.c */
uint8_t wait_82802ab(struct flashctx *flash);
int probe_82802ab(struct flashctx *flash);
===================================================================
@@ -227,6 +227,7 @@
write_gran_1bit,
write_gran_1byte,
write_gran_256bytes,
+ write_gran_264bytes,
};
extern int verbose_screen;
extern int verbose_logfile;
===================================================================
@@ -687,6 +687,22 @@
break;
}
break;
+ case write_gran_264bytes:
+ for (j = 0; j < len / 264; j++) {
+ limit = min (264, len - j * 264);
+ /* Are 'have' and 'want' identical? */
+ if (!memcmp(have + j * 264, want + j * 264, limit))
+ continue;
+ /* have needs to be in erased state. */
+ for (i = 0; i < limit; i++)
+ if (have[j * 264 + i] != 0xff) {
+ result = 1;
+ break;
+ }
+ if (result)
+ break;
+ }
+ break;
default:
msg_cerr("%s: Unsupported granularity! Please report a bug at "
"flashrom@flashrom.org\n", __func__);
@@ -733,6 +749,9 @@
case write_gran_256bytes:
stride = 256;
break;
+ case write_gran_264bytes:
+ stride = 264;
+ break;
default:
msg_cerr("%s: Unsupported granularity! Please report a bug at "
"flashrom@flashrom.org\n", __func__);
@@ -1240,7 +1259,12 @@
{
unsigned int starthere = 0, lenhere = 0;
int ret = 0, skip = 1, writecount = 0;
- enum write_granularity gran = write_gran_256bytes; /* FIXME */
+ enum write_granularity gran = write_gran_256bytes;
+
+ if(flash->page_size == 264 || flash->page_size == 528) {
+ // FIXME - this is wrong for 528-byte blocks
+ gran = write_gran_264bytes;
+ }
/* curcontents and newcontents are opaque to walk_eraseregions, and
* need to be adjusted here to keep the impression of proper abstraction
===================================================================
@@ -285,7 +285,7 @@
CHIP_OBJS = jedec.o stm50flw0x0x.o w39.o w29ee011.o \
sst28sf040.o m29f400bt.o 82802ab.o pm49fl00x.o \
sst49lfxxxc.o sst_fwhub.o flashchips.o spi.o spi25.o \
- a25.o at25.o opaque.o sfdp.o en29lv640b.o
+ a25.o at25.o opaque.o sfdp.o en29lv640b.o at45db.o
###############################################################################
# Library code.
===================================================================
@@ -0,0 +1,235 @@
+/*
+ * Support for Atmel AT45DBxxxD DataFlash chips.
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2012 Aidan Thornton
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+#include "flash.h"
+#include "flashchips.h"
+#include "chipdrivers.h"
+#include "programmer.h"
+#include "spi.h"
+
+#define AT45DB_READY 0x80
+#define AT45DBXXXD_POWEROF2 0x01
+
+#define AT45DB_STATUS 0xd7
+#define AT45DB_SECTOR_ERASE 0x7c
+#define AT45DB_BUFFER1_WRITE 0x84
+#define AT45DB_BUFFER1_PAGE_PROGRAM 0x88
+
+uint8_t at45db_read_status_register(struct flashctx *flash)
+{
+ static const unsigned char cmd[1] = { AT45DB_STATUS };
+ unsigned char readarr[1];
+ int ret;
+
+ /* Read Status Register */
+ ret = spi_send_command(flash, sizeof(cmd), sizeof(readarr), cmd,
+ readarr);
+ if (ret)
+ msg_cerr("RDSR failed!\n");
+
+ return readarr[0];
+}
+
+/* Extra probe function for AT45DBxxxD. Older chips in the AT45DB family should
+ * just use the standard probe_spi_rdid
+ */
+int probe_spi_at45db(struct flashctx *flash)
+{
+ uint8_t status;
+ int i, j;
+ if(!probe_spi_rdid(flash))
+ return 0;
+
+ status = at45db_read_status_register(flash);
+
+ // bit 6 is "compare result" which we don't care about.
+ // density is, per the data sheet, there for legacy reasons only
+ msg_cdbg("%s: status 0x%x: %s, density=0x%x, protection %s, page size %s\n",
+ __func__, status, ((status & AT45DB_READY)?"ready":"busy"),
+ (status >> 2) & 0xf, ((status & 0x2) ? "on" : "off"),
+ ((status & AT45DBXXXD_POWEROF2) ? "power-of-2" : "default"));
+
+ /* AT45DBxxxD chips support two different page sizes, 264 and 256.
+ * In order to tell which page size this chip has we need to read
+ * the status register, then adjust our size information accordingly
+ */
+ if((status & 1) == 0)
+ {
+ // adjust for non-power of 2 pages
+ flash->total_size = (flash->total_size / 32) * 33;
+ flash->page_size = (flash->page_size / 32) * 33;
+
+ for (i = 0; i < NUM_ERASEFUNCTIONS; i++) {
+ struct block_eraser *eraser = &flash->block_erasers[i];
+ for(j = 0; eraser->eraseblocks[j].size != 0; j++) {
+ eraser->eraseblocks[j].size = (eraser->eraseblocks[j].size / 32) * 33;
+ }
+ }
+ }
+ msg_cdbg("%s: total size %i kB, page size %i B\n", __func__, flash->total_size, flash->page_size);
+
+ return 1;
+}
+
+/* Converts page size to number of page address bits used */
+static unsigned int at45db_page_size_to_bits(unsigned int page_size)
+{
+ if(page_size <= 256) return 8;
+ else if(page_size <= 512) return 9;
+ else if(page_size <= 1024) return 10;
+ else return 0; /* FIXME */
+
+}
+
+/* We fake a contiguous address space for flashrom, but the actual underlying
+ address space may contain gaps at the end of each page. Convert from the
+ address flashrom uses to the address the DataFlash chips use.
+ Note that reads generally skip over the gaps in the address space.
+*/
+static unsigned int at45db_convert_addr(unsigned int addr, unsigned int page_size)
+{
+ unsigned int page_bits = at45db_page_size_to_bits(page_size);
+ return ((addr / page_size) << page_bits) | (addr % page_size);
+}
+
+int read_at45db(struct flashctx *flash, uint8_t *buf, unsigned int start,
+ unsigned int len)
+{
+ int rc = 0;
+ unsigned int i, toread, addr;
+ unsigned int page_size = flash->page_size;
+ unsigned int chunksize = flash->pgm->spi.max_data_read;
+ if (chunksize == MAX_DATA_UNSPECIFIED) {
+ msg_perr("%s called, but SPI read chunk size not defined "
+ "on this hardware.\n", __func__);
+ return 1;
+ }
+
+ /* We still have to split this up into chunks to fit within the
+ * programmer's read size limit, but those chunks can cross page
+ * boundaries.
+ */
+ for(i = 0; i < len; i += chunksize)
+ {
+ toread = min(chunksize, len - i);
+ addr = at45db_convert_addr(start + i, page_size);
+ /* Note that this only works on AT25DBxxxD chips */
+ rc = spi_nbyte_read(flash, addr, buf + i, toread);
+ if (rc)
+ break;
+ }
+
+ return rc;
+}
+
+int erase_at45db_sector(struct flashctx *flash, unsigned int addr,
+ unsigned int blocklen)
+{
+ addr = at45db_convert_addr(addr, flash->page_size);
+ const unsigned char cmd[4] = {
+ AT45DB_SECTOR_ERASE,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr >> 0) & 0xff,
+ };
+
+ /* send erase command */
+ spi_send_command(flash, sizeof(cmd), 0, cmd, NULL);
+
+ /* wait for completion */
+ while((at45db_read_status_register(flash) & AT45DB_READY) == 0)
+ programmer_delay(100 * 1000);
+
+ return 0;
+}
+
+static int at45db_write_buffer1(struct flashctx *flash, unsigned int addr,
+ uint8_t *bytes, unsigned int len)
+{
+ unsigned char cmd[260] = {
+ AT45DB_BUFFER1_WRITE,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr >> 0) & 0xff,
+ };
+ memcpy(&cmd[4], bytes, len);
+ return spi_send_command(flash, 4 + len, 0, cmd, NULL);
+}
+
+static int at45db_program_page(struct flashctx *flash, uint8_t *buf,
+ unsigned int addr, unsigned int chunksize)
+{
+ int rc = 0;
+ unsigned int i, towrite;
+ unsigned int page_size = flash->page_size;
+ unsigned char cmd[4] = {
+ AT45DB_BUFFER1_PAGE_PROGRAM,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr >> 0) & 0xff,
+ };
+
+ for(i = 0; i < page_size; i += chunksize)
+ {
+ towrite = min(chunksize, page_size - i);
+ rc = at45db_write_buffer1(flash, i, buf + i, towrite);
+ if (rc)
+ break;
+ }
+
+ if(rc)
+ return rc;
+
+ /* send program command */
+ spi_send_command(flash, sizeof(cmd), 0, cmd, NULL);
+
+ /* wait for completion */
+ while((at45db_read_status_register(flash) & AT45DB_READY) == 0)
+ programmer_delay(100);
+
+ return rc;
+}
+
+int spi_write_at45db(struct flashctx *flash, uint8_t *buf,
+ unsigned int start, unsigned int len)
+{
+ int rc = 0;
+ unsigned int i;
+ unsigned int max_data = flash->pgm->spi.max_data_write;
+ unsigned int page_size = flash->page_size;
+ if (max_data == MAX_DATA_UNSPECIFIED) {
+ msg_perr("%s called, but SPI write chunk size not defined "
+ "on this hardware.\n", __func__);
+ return 1;
+ }
+
+ if((start % page_size) != 0 || (len % page_size) != 0) {
+ msg_perr("%s: cannot write partial pages: start %u, len %u\n", __func__, start, len);
+ return 1;
+ }
+ for(i = 0; i < len; i += page_size) {
+ rc = at45db_program_page(flash, buf+i,
+ at45db_convert_addr(start+i, page_size), max_data);
+ if(rc != 0)
+ return rc;
+ }
+ return rc;
+}
===================================================================
@@ -2095,14 +2095,35 @@
.bustype = BUS_SPI,
.manufacture_id = ATMEL_ID,
.model_id = ATMEL_AT45DB011D,
- .total_size = 128 /* Size can only be determined from status register */,
- .page_size = 256 /* Size can only be determined from status register */,
+ .total_size = 128 /* or 132, determined from status register */,
+ .page_size = 256 /* or 264, determined from status register */,
/* does not support EWSR nor WREN and has no writable status register bits whatsoever */
- .tested = TEST_BAD_READ,
- .probe = probe_spi_rdid,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_at45db,
.probe_timing = TIMING_ZERO,
- .write = NULL,
- .read = NULL,
+ .block_erasers =
+ {
+ /*{
+ .eraseblocks = { {256, 512} },
+ .block_erase = ????, //spi_erase_at45db_page,
+ }, {
+ {
+ .eraseblocks = { {8 * 256, 512/8} },
+ .block_erase = ????, //spi_erase_at45db_block,
+ },*/ {
+ .eraseblocks = {
+ {8 * 256, 1},
+ {120 * 256, 1},
+ {128 * 256, 3},
+ },
+ .block_erase = erase_at45db_sector
+ }/*, {
+ .eraseblocks = { {128 * 1024, 1} },
+ .block_erase = ????, //spi_block_erase_c7,
+ }*/
+ },
+ .write = spi_write_at45db,
+ .read = read_at45db,
.voltage = {2700, 3600},
},
@@ -2113,13 +2134,13 @@
.manufacture_id = ATMEL_ID,
.model_id = ATMEL_AT45DB021D,
.total_size = 256 /* Size can only be determined from status register */,
- .page_size = 256 /* Size can only be determined from status register */,
+ .page_size = 256 /* or 264, determined from status register */,
/* does not support EWSR nor WREN and has no writable status register bits whatsoever */
- .tested = TEST_BAD_READ,
- .probe = probe_spi_rdid,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_at45db,
.probe_timing = TIMING_ZERO,
.write = NULL,
- .read = NULL,
+ .read = read_at45db,
.voltage = {2700, 3600},
},
@@ -2130,13 +2151,13 @@
.manufacture_id = ATMEL_ID,
.model_id = ATMEL_AT45DB041D,
.total_size = 512 /* Size can only be determined from status register */,
- .page_size = 256 /* Size can only be determined from status register */,
+ .page_size = 256 /* or 264, determined from status register */,
/* does not support EWSR nor WREN and has no writable status register bits whatsoever */
- .tested = TEST_BAD_READ,
- .probe = probe_spi_rdid,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_at45db,
.probe_timing = TIMING_ZERO,
.write = NULL,
- .read = NULL,
+ .read = read_at45db,
.voltage = {2500, 3600}, /* 2.5-3.6V & 2.7-3.6V models available */
},
@@ -2147,13 +2168,13 @@
.manufacture_id = ATMEL_ID,
.model_id = ATMEL_AT45DB081D,
.total_size = 1024 /* Size can only be determined from status register */,
- .page_size = 256 /* Size can only be determined from status register */,
+ .page_size = 256 /* or 264, determined from status register */,
/* does not support EWSR nor WREN and has no writable status register bits whatsoever */
- .tested = TEST_BAD_READ,
- .probe = probe_spi_rdid,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_at45db,
.probe_timing = TIMING_ZERO,
.write = NULL,
- .read = NULL,
+ .read = read_at45db,
.voltage = {2700, 3600}, /* 2.5-3.6V & 2.7-3.6V models available */
},
@@ -2164,13 +2185,13 @@
.manufacture_id = ATMEL_ID,
.model_id = ATMEL_AT45DB161D,
.total_size = 2048 /* Size can only be determined from status register */,
- .page_size = 512 /* Size can only be determined from status register */,
+ .page_size = 512 /* or 528, determined from status register */,
/* does not support EWSR nor WREN and has no writable status register bits whatsoever */
- .tested = TEST_BAD_READ,
- .probe = probe_spi_rdid,
+ .tested = TEST_UNTESTED,
+ .probe = probe_spi_at45db,
.probe_timing = TIMING_ZERO,
.write = NULL,
- .read = NULL,
+ .read = read_at45db,
.voltage = {2700, 3600}, /* 2.5-3.6V & 2.7-3.6V models available */
},