[flashrom] Add support for unlock and write to Atmel AT26F040

Tim Small tim at buttersideup.com
Tue Jun 21 01:38:55 CEST 2011


Add support for unlock and write to Atmel AT26F040 SPI 512k chips.

Tested using a bus pirate only.

I'm unavailble for a couple of weeks imminently, but will reply on
return if pos...

Cheers,

Tim.



~# flashrom  -c AT26F004 -p buspirate_spi:dev=/dev/ttyUSB0 -Vw rr620WDA-read-rom-image 
flashrom v0.9.3-r1349 on Linux 2.6.32-5-686 (i686), built with libpci 3.1.7, GCC 4.4.5, little endian
flashrom is free software, get the source code at http://www.flashrom.org

Calibrating delay loop... OS timer resolution is 1 usecs, 1088M loops per second, 10 myus = 10 us, 100 myus = 95 us, 1000 myus = 945 us, 10000 myus = 9495 us, 4 myus = 5 us, OK.
Initializing buspirate_spi programmer
SPI speed is 8MHz
Raw bitbang mode version 1
Raw SPI mode version 1
Probing for Atmel AT26F004, 512 kB: probe_spi_rdid_generic: id1 0x1f, id2 0x400
Chip status register is 1c
Found chip "Atmel AT26F004" (512 kB, SPI) on buspirate_spi.
===
This flash part has status UNTESTED for operations: PROBE READ ERASE WRITE
The test status of this chip may have been updated in the latest development
version of flashrom. If you are running the latest development version,
please email a report to flashrom at flashrom.org if any of the above operations
work correctly for you with this flash part. Please include the flashrom
output with the additional -V option for all operations you tested (-V, -Vr,
-Vw, -VE), and mention which mainboard or programmer you tested.
Please mention your board in the subject line. Thanks for your help!
Some block protection in effect, disabling
All write protection disabled!
Reading old flash chip contents...
Erasing and writing flash chip... Looking at blockwise erase function 0... trying... 0x000000-0x000fff:EW, 0x001000-0x001fff:W, 0x002000-0x002fff:S, 0x003000-0x003fff:S, 0x004000-0x004fff:S, 0x005000-0x005fff:S, 0x006000-0x006fff:S, 0x007000-0x007fff:S, 0x008000-0x008fff:S, 0x009000-0x009fff:S, 0x00a000-0x00afff:S, 0x00b000-0x00bfff:S, 0x00c000-0x00cfff:W, 0x00d000-0x00dfff:S, 0x00e000-0x00efff:S, 0x00f000-0x00ffff:S, 0x010000-0x010fff:EW, 0x011000-0x011fff:EW, 0x012000-0x012fff:EW, 0x013000-0x013fff:EW, 0x014000-0x014fff:EW, 0x015000-0x015fff:W, 0x016000-0x016fff:W, 0x017000-0x017fff:S, 0x018000-0x018fff:S, 0x019000-0x019fff:S, 0x01a000-0x01afff:S, 0x01b000-0x01bfff:S, 0x01c000-0x01cfff:S, 0x01d000-0x01dfff:S, 0x01e000-0x01efff:S, 0x01f000-0x01ffff:S, 0x020000-0x020fff:EW, 0x021000-0x021fff:W, 0x022000-0x022fff:W, 0x023000-0x023fff:W, 0x024000-0x024fff:W, 0x025000-0x025fff:W, 0x026000-0x026fff:W, 0x027000-0x027fff:W, 0x028000-0x028fff:W, 0x029000-0x029fff:W, 0x02a000-0x02aff
 f:W, 0x02b000-0x02bfff:W, 0x02c000-0x02cfff:W, 0x02d000-0x02dfff:W, 0x02e000-0x02efff:W, 0x02f000-0x02ffff:W, 0x030000-0x030fff:W, 0x031000-0x031fff:W, 0x032000-0x032fff:W, 0x033000-0x033fff:W, 0x034000-0x034fff:W, 0x035000-0x035fff:W, 0x036000-0x036fff:W, 0x037000-0x037fff:W, 0x038000-0x038fff:W, 0x039000-0x039fff:W, 0x03a000-0x03afff:W, 0x03b000-0x03bfff:W, 0x03c000-0x03cfff:W, 0x03d000-0x03dfff:S, 0x03e000-0x03efff:S, 0x03f000-0x03ffff:S, 0x040000-0x040fff:S, 0x041000-0x041fff:S, 0x042000-0x042fff:S, 0x043000-0x043fff:S, 0x044000-0x044fff:S, 0x045000-0x045fff:S, 0x046000-0x046fff:S, 0x047000-0x047fff:S, 0x048000-0x048fff:S, 0x049000-0x049fff:S, 0x04a000-0x04afff:S, 0x04b000-0x04bfff:S, 0x04c000-0x04cfff:S, 0x04d000-0x04dfff:S, 0x04e000-0x04efff:S, 0x04f000-0x04ffff:S, 0x050000-0x050fff:S, 0x051000-0x051fff:S, 0x052000-0x052fff:S, 0x053000-0x053fff:S, 0x054000-0x054fff:S, 0x055000-0x055fff:S, 0x056000-0x056fff:S, 0x057000-0x057fff:S, 0x058000-0x058fff:S, 0x059000-0x059fff:
 S, 0x05a000-0x05afff:S, 0x05b000-0x05bfff:W, 0x05c000-0x05cfff:W, 0x05d000-0x05dfff:S, 0x05e000-0x05efff:S, 0x05f000-0x05ffff:S, 0x060000-0x060fff:S, 0x061000-0x061fff:S, 0x062000-0x062fff:S, 0x063000-0x063fff:S, 0x064000-0x064fff:S, 0x065000-0x065fff:S, 0x066000-0x066fff:S, 0x067000-0x067fff:S, 0x068000-0x068fff:S, 0x069000-0x069fff:S, 0x06a000-0x06afff:S, 0x06b000-0x06bfff:S, 0x06c000-0x06cfff:S, 0x06d000-0x06dfff:S, 0x06e000-0x06efff:S, 0x06f000-0x06ffff:S, 0x070000-0x070fff:S, 0x071000-0x071fff:S, 0x072000-0x072fff:S, 0x073000-0x073fff:S, 0x074000-0x074fff:S, 0x075000-0x075fff:S, 0x076000-0x076fff:S, 0x077000-0x077fff:S, 0x078000-0x078fff:S, 0x079000-0x079fff:S, 0x07a000-0x07afff:S, 0x07b000-0x07bfff:S, 0x07c000-0x07cfff:S, 0x07d000-0x07dfff:S, 0x07e000-0x07efff:S, 0x07f000-0x07ffff:S

Done.
Verifying flash... VERIFIED.          
Raw bitbang mode version 1
Bus Pirate shutdown completed.




Signed-off-by: Tim Small <tim at seoss.co.uk>


Index: at25.c
===================================================================
--- at25.c	(revision 1349)
+++ at25.c	(working copy)
@@ -215,6 +215,8 @@
 	/* spi_disable_blockprotect_at25df is not really the right way to do
 	 * this, but the side effects of said function work here as well.
 	 */
+	/* FIXME? This does a global unlock, but at25f don't have this command?
+	 */
 	return spi_disable_blockprotect_at25df(flash);
 }
 
@@ -285,3 +287,71 @@
 	}
 	return 0;
 }
+
+
+int spi_disable_blockprotect_at26f040(struct flashchip *flash)
+{
+	uint8_t status;
+	uint32_t i;
+	int result;
+
+	/* See Page 19							 *
+	 * http://www.atmel.com/dyn/resources/prod_documents/doc3588.pdf */
+
+       	/* Write Protect Pin status: 0 == cannot set SPRL to 0 (unlocked) */
+	const uint8_t stat_bit_WPP = (1 << 4);
+	
+       	/* Software Protection Status: 00 == all sectors unprotected */
+	const uint8_t stat_bit_SWP = (3 << 2);
+
+       	/* Sector Protection registers locked: 0 == unlocked. *
+	 * Cannot be set to 0 if WPP is 1 (hardware lock).    */
+	const uint8_t stat_bit_SPRL = (1 << 7);
+
+	/* Page 6 */
+	const uint8_t at26f004_unprotect_sector = 0x39;
+
+	status = spi_read_status_register();
+	/* If block protection is disabled, OK to go ahead, stop here. */
+	if ((status & stat_bit_SWP) == 0)
+		return 0;
+
+	msg_cdbg("Some block protection in effect, disabling\n");
+	if (status & stat_bit_SPRL) {
+		msg_cdbg("Need to disable Sector Protection Register Lock\n");
+		if ((status & stat_bit_WPP) == 0) {
+			msg_cerr("The chips write-protect pin is being "
+				 "asserted (driven low) by external hardware."
+				 "\nWriting is impossible :-(.\n");
+			return 1;
+		}
+		/* All bits except bit 7 (SPRL) are readonly. */
+		/* Unlock sector protection registers. */
+		result = spi_write_status_register(flash, status & ~stat_bit_SPRL);
+		if (result) {
+			msg_cerr("spi_write_status_register failed\n");
+			return result;
+		}
+		
+	}
+	/* Call unprotect sector on each sector.  We need to give an address */
+        /* within each sector, and the smallest sector is 8KB. */
+	for (i = 0; i < 0x80000; i += 0x1000) {
+		const unsigned char cmd[4] = { at26f004_unprotect_sector,
+		       			       (i >> 16) & 0xff,
+					       (i >> 8) & 0xff,
+					       i & 0xff};
+		/* set the chip's "WEL" write latch - WREN (Write Enable) */
+		spi_write_enable();
+		spi_send_command(sizeof(cmd), 0, cmd, NULL);
+	}
+	status = spi_read_status_register();
+	if ((status & stat_bit_SWP) != 0) {
+		msg_cerr("Block protection could not be disabled!\n");
+		return 1;
+	} else {
+		msg_cerr("All write protection disabled!\n");
+	}
+
+	return 0;
+}
Index: spi25.c
===================================================================
--- spi25.c	(revision 1349)
+++ spi25.c	(working copy)
@@ -1210,3 +1210,94 @@
 
 	return 0;
 }
+
+
+/* This is for Atmel chips only (AT26F004, AT26DF161A etc.) - the 25F004 only
+ * does either this mode, or byte-at-a-time. */
+int spi_at26_write_sequential(struct flashchip *flash, uint8_t *buf, int start, int len)
+{
+	/* ref http://www.atmel.com/dyn/resources/prod_documents/doc3588.pdf
+	 * page 9 etc. */
+	const int AT26_SEQ_BYTE_WRITE_OUTSIZE = 5;
+	const int AT26_SEQ_BYTE_WRITE_CONT_OUTSIZE = 2;
+	const int AT26_SEQ_BYTE_WRITE_CMD = 0xaf;
+	uint32_t pos = start;
+	int result;
+	unsigned char cmd[4] = {
+		AT26_SEQ_BYTE_WRITE_CMD
+	};
+	struct spi_command cmds[] = {
+	{
+		.writecnt	= JEDEC_WREN_OUTSIZE,
+		.writearr	= (const unsigned char[]){ JEDEC_WREN },
+		.readcnt	= 0,
+		.readarr	= NULL,
+	}, {
+		.writecnt	= AT26_SEQ_BYTE_WRITE_OUTSIZE,
+		.writearr	= (const unsigned char[]){
+					AT26_SEQ_BYTE_WRITE_CMD,
+					(start >> 16) & 0xff,
+					(start >> 8) & 0xff,
+					(start & 0xff),
+					buf[0]
+				},
+		.readcnt	= 0,
+		.readarr	= NULL,
+	}, {
+		.writecnt	= 0,
+		.writearr	= NULL,
+		.readcnt	= 0,
+		.readarr	= NULL,
+	}};
+
+	switch (spi_programmer->type) {
+#if CONFIG_INTERNAL == 1
+#if defined(__i386__) || defined(__x86_64__)
+	case SPI_CONTROLLER_IT87XX:
+	case SPI_CONTROLLER_WBSIO:
+		msg_perr("%s: impossible with this SPI controller,"
+				" degrading to byte program\n", __func__);
+		return spi_chip_write_1(flash, buf, start, len);
+#endif
+#endif
+	default:
+		break;
+	}
+
+	cmds[1].writearr = (const unsigned char[]){
+		0xAF,
+			(pos >> 16) & 0xff,
+			(pos >> 8) & 0xff,
+			(pos & 0xff),
+			buf[pos - start]
+	};
+	
+
+	result = spi_send_multicommand(cmds);
+	if (result) {
+		msg_cerr("%s failed during start command execution\n",
+			 __func__);
+		/* FIXME: Should we send WRDI here as well to make sure the chip
+		 * is not in seuquential wirte mode?
+		 */
+		return result;
+	}
+	while (spi_read_status_register() & JEDEC_RDSR_BIT_WIP)
+		programmer_delay(5);
+
+	pos ++;
+
+	while (pos < start + len) {
+		cmd[1] = buf[pos++ - start];
+		spi_send_command(AT26_SEQ_BYTE_WRITE_CONT_OUTSIZE, 0, cmd, NULL);
+		while (spi_read_status_register() & JEDEC_RDSR_BIT_WIP)
+			programmer_delay(5);
+	}
+
+	/* Use WRDI to exit sequential write mode. This needs to be done before
+	 * issuing any other non-sequential-byte-write commands.
+	 */
+	spi_write_disable();
+
+	return 0;
+}
Index: flashchips.c
===================================================================
--- flashchips.c	(revision 1349)
+++ flashchips.c	(working copy)
@@ -1883,6 +1883,7 @@
 		},
 		.printlock	= spi_prettyprint_status_register_atmel_at26df081a,
 		.unlock		= spi_disable_blockprotect,
+		/* Should support spi_at26_write_sequential too, but it's untested */
 		.write		= spi_chip_write_256,
 		.read		= spi_chip_read,
 		.voltage	= {2700, 3600},
@@ -1914,6 +1915,7 @@
 		.model_id	= ATMEL_AT26F004,
 		.total_size	= 512,
 		.page_size	= 256,
+		.feature_bits   = FEATURE_WRSR_WREN,
 		.tested		= TEST_UNTESTED,
 		.probe		= probe_spi_rdid,
 		.probe_timing	= TIMING_ZERO,
@@ -1936,7 +1938,9 @@
 				.block_erase = spi_block_erase_c7,
 			}
 		},
-		.write		= NULL /* Incompatible Page write */,
+		.unlock		= spi_disable_blockprotect_at26f040,
+		/* also supports spi_chip_write_1, but nothing else */
+		.write		= spi_at26_write_sequential, 
 		.read		= spi_chip_read,
 		.voltage	= {2700, 3600},
 	},
Index: chipdrivers.h
===================================================================
--- chipdrivers.h	(revision 1349)
+++ chipdrivers.h	(working copy)
@@ -55,6 +55,7 @@
 int spi_read_chunked(struct flashchip *flash, uint8_t *buf, int start, int len, int chunksize);
 int spi_write_chunked(struct flashchip *flash, uint8_t *buf, int start, int len, int chunksize);
 int spi_aai_write(struct flashchip *flash, uint8_t *buf, int start, int len);
+int spi_at26_write_sequential(struct flashchip *flash, uint8_t *buf, int start, int len);
 
 /* a25.c */
 int spi_prettyprint_status_register_amic_a25l05p(struct flashchip *flash);
@@ -74,6 +75,7 @@
 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_disable_blockprotect_at26f040(struct flashchip *flash);
 
 /* 82802ab.c */
 uint8_t wait_82802ab(struct flashchip *flash);




More information about the flashrom mailing list