Patchwork [PATCH/WIP] AT45DB011D flashing support

login
register
about
Submitter Aidan Thornton
Date 2012-08-17 17:12:57
Message ID <CAB=c7Tqov_+9RexjNQZvVEhErtb0t0+O1sNCSCCet=L4m0K6bg@mail.gmail.com>
Download mbox | patch
Permalink /patch/3715/
State Superseded
Headers show

Comments

Aidan Thornton - 2012-08-17 17:12:57
Let's try that again, this time remembering to svn add the new file.
Sorry about that

On Fri, Aug 17, 2012 at 5:53 PM, Aidan Thornton <makosoft@gmail.com> wrote:
> Hi,
>
> The attached patch is a somewhat messy attempt to add support for the
> AT45DB011D and potentially other AT45DBxxxD Atmel DataFlash chips.
> I've tested flashing the AT45DB011D-like integrated Flash on the
> Xilinx XC3S50AN with it and it appears to work. Not really ready to
> merge, but carldani asked me to post this so he could see if it could
> be combined with his old incomplete code for non-power-of-2 flash
> chips. Should in theory be able to read all AT45DBxxxD chips as-is and
> write all chips that have 256/264-byte pages with suitable additions
> to flashchips.c.
>
> Aidan
Aidan Thornton - 2012-08-27 23:24:11
On Fri, Aug 17, 2012 at 6:12 PM, Aidan Thornton <makosoft@gmail.com> wrote:
> Let's try that again, this time remembering to svn add the new file.
> Sorry about that

Also, forgot the Signed-Off-By line:

Signed-off-by: Aidan Thornton <makosoft@gmail.com>

Again, sorry, not sure how I missed that.

Aidan
Stefan Tauner - 2013-03-03 22:57:09
how to deal with page_size and especially the different granularities...

Stefan Tauner (4):
  Make write granularity a chip attribute.
  Add granularity handling for AT45DB series.
  Add support for Atmel AT45DB* chips.
  Add support for AT45DB321C.

 Makefile          |    2 +-
 at45db.c          |  505 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 chipdrivers.h     |   13 ++
 flash.h           |    5 +-
 flashchips.c      |  334 ++++++++++++++++++++++++++++++-----
 flashchips.h      |    3 +
 flashrom.c        |   41 +++--
 serprog.c         |    3 +-
 spi25_statusreg.c |    2 +-
 9 files changed, 839 insertions(+), 69 deletions(-)
 create mode 100644 at45db.c

Patch

Index: chipdrivers.h
===================================================================
--- chipdrivers.h	(revision 1575)
+++ chipdrivers.h	(working copy)
@@ -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);
Index: flash.h
===================================================================
--- flash.h	(revision 1575)
+++ flash.h	(working copy)
@@ -227,6 +227,7 @@ 
 	write_gran_1bit,
 	write_gran_1byte,
 	write_gran_256bytes,
+	write_gran_264bytes,
 };
 extern int verbose_screen;
 extern int verbose_logfile;
Index: flashrom.c
===================================================================
--- flashrom.c	(revision 1575)
+++ flashrom.c	(working copy)
@@ -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
Index: Makefile
===================================================================
--- Makefile	(revision 1575)
+++ Makefile	(working copy)
@@ -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.
Index: at45db.c
===================================================================
--- at45db.c	(revision 0)
+++ at45db.c	(working copy)
@@ -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;
+}
Index: flashchips.c
===================================================================
--- flashchips.c	(revision 1575)
+++ flashchips.c	(working copy)
@@ -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 */
 	},