changeset 80:1a4573062b37

Reshuffle in preparation for being able to have a common API for SPI flash and (emulated) EEPROM.
author Daniel O'Connor <darius@dons.net.au>
date Sun, 07 Jul 2013 22:49:02 +0930
parents cecb0506f4b8
children d7002925c15d
files BSDmakefile flash.c flash.h spiflash.c spiflash.h
diffstat 5 files changed, 498 insertions(+), 488 deletions(-) [+]
line wrap: on
line diff
--- a/BSDmakefile	Sun Jul 07 22:48:17 2013 +0930
+++ b/BSDmakefile	Sun Jul 07 22:49:02 2013 +0930
@@ -15,6 +15,7 @@
 		sd.c \
 		spi.c \
 		sprink.c \
+		spiflash.c \
 		stm32_eval_sdio_sd.c \
 		startup_stm32f10x_md_mthomas.c \
 		syscalls.c \
--- a/flash.c	Sun Jul 07 22:48:17 2013 +0930
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,438 +0,0 @@
-#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
-#include <stdlib.h>
-#include <assert.h>
-
-#include "stm32f10x.h"
-#include "spi.h"
-#include "flash.h"
-
-#define FL_SELECT()	GPIO_ResetBits(GPIOA, GPIO_Pin_4)
-#define FL_DESELECT()	GPIO_SetBits(GPIOA, GPIO_Pin_4)
-
-static const char *flstattbl[] = {
-    "BUSY",
-    "WEL",
-    "BP0",
-    "BP1",
-    "BP2",
-    "BP3",
-    "AAI",
-    "BPL"
-};
-
-#define RW_IDLE		0
-#define RW_RUNNING	1
-
-static int writestate = RW_IDLE;
-static int readstate = RW_IDLE;
-
-void
-flashcmd(int argc, char **argv) {
-    uint8_t status, tmp, len;
-    uint32_t addr;
-    
-    if (argc == 0) {
-	fputs("No command specified\n", stdout);
-	return;
-    }
-    
-    if (!strcmp(argv[0], "str")) {
-	status = flashreadstatus();
-	fputs("Status = ", stdout);
-	for (unsigned int i = 0; i < sizeof(flstattbl) / sizeof(flstattbl[0]); i++)
-	    if (status & 1 << i) {
-		fputs(flstattbl[i], stdout);
-		fputs(" ", stdout);
-	    }
-	printf("(0x%02x)\n", status);
-    } else if (!strcmp(argv[0], "stw")) {
-	if (argc != 2) {
-	    fputs("Incorrect number of arguments\n", stdout);
-	    return;
-	}
-	tmp = atoi(argv[1]);
-	flashwritestatus(tmp);
-	status = flashreadstatus();
-	printf("Wrote 0x%02x to status, now 0x%02x\n", tmp, status);
-    } else if (!strcmp(argv[0], "er")) {
-	if (argc != 2) {
-	    fputs("Incorrect number of arguments\n", stdout);
-	    return;
-	}
-	addr = atoi(argv[1]);
-	flash4kerase(addr);
-	printf("Erased 0x%x\n", (unsigned int)addr);
-    } else if (!strcmp(argv[0], "rd")) {
-	if (argc < 2) {
-	    fputs("Incorrect number of arguments\n", stdout);
-	    return;
-	}
-	
-	addr = atoi(argv[1]);
-
-	if (argc > 2)
-	    len = atoi(argv[2]);
-	else
-	    len = 16;
-
-	flashstartread(addr);
-	
-	for (uint32_t i = 0; i < len; i++)
-	    printf("Read 0x%02x from 0x%06x\n", flashreadbyte(), (unsigned int)(addr + i));
-	flashstopread();
-	
-	fputs("\n", stdout);
-    } else if (!strcmp(argv[0], "wr")) {
-	if (argc < 2) {
-	    fputs("Incorrect number of arguments\n", stdout);
-	    return;
-	}
-
-	addr = atoi(argv[1]);
-
-	if (argc > 2)
-	    len = atoi(argv[2]);
-	else
-	    len = 16;
-
-	for (int i = 0; i < 16; i += 2) {
-	    uint16_t data;
-	    data = ((i + 1) << 8) | i;
-	    printf("Writing 0x%04x to 0x%06x\n", data, (unsigned int)(addr + i));
-
-	    if (i == 0)
-		flashstartwrite(addr, data);
-	    else
-		flashwriteword(data);
-	}
-	flashstopwrite();
-    } else if (!strcmp(argv[0], "id")) {
-	printf("Flash ID = 0x%04hx (expect 0xbf41)\n", flashreadid());
-    } else {
-	fputs("Unknown sub command\n", stdout);
-	return;
-    }
-}
-
-void
-flash4kerase(uint32_t addr) {
-    flashenablewrite();			/* Enable writing */
-    
-    FL_SELECT();			/* Select device */
-
-    SPI_WriteByte(FL_4KERASE);		/* Send command */
-    SPI_WriteByte(addr >> 16);	 	/* Send address */
-    SPI_WriteByte(addr >> 8);
-    SPI_WriteByte(addr);
-  
-    FL_DESELECT();
-    
-    //fputs("4k erase ", stdout);
-    flashwait();
-}
-
-void
-flashwait(void) {
-    uint8_t	cnt;
-    
-    /* Wait for not BUSY */
-    for (cnt = 0; (flashreadstatus() & FL_BUSY) != 0; cnt++)
-	;
-
-    //printf("cnt = %d\n", cnt);
-}
-
-uint16_t
-flashreadid(void) {
-    uint8_t	fac, dev;
-    
-    FL_SELECT();			/* Select device */
-
-    SPI_WriteByte(FL_RDID);		/* Send command */
-    SPI_WriteByte(0x00);		/* Send address cycles (ID data starts at 0) */
-    SPI_WriteByte(0x00);
-    SPI_WriteByte(0x00);
-    fac = SPI_WriteByte(0x00);		/* Read ID */
-    dev = SPI_WriteByte(0x00);
-  
-    FL_DESELECT();			/* De-select device */
-
-    return fac << 8 | dev;
-}
-
-void
-flashenablewrite(void) {
-    FL_SELECT();			/* Select device */
-
-    SPI_WriteByte(FL_WREN);		/* Send command */
-  
-    FL_DESELECT();			/* De-select device */
-}
-
-uint8_t
-flashreadstatus(void) {
-    uint8_t	status;
-    
-    FL_SELECT();			/* Select device */
-
-    SPI_WriteByte(FL_RDSR);		/* Send command */
-    SPI_WriteByte(0x00);		/* Send dummy byte for address cycle */
-    status = SPI_WriteByte(0x00);	/* Read status */
-  
-    FL_DESELECT();			/* De-select device */
-
-    return status;
-}
-
-void
-flashwritestatus(uint8_t status) {
-    /* Enable status write */
-    FL_SELECT();			/* Select device */
-    SPI_WriteByte(FL_EWSR);		/* Send command */
-    SPI_WriteByte(0x00);		/* Send data byte */
-    FL_DESELECT();
-
-    /* Actually write status */
-    FL_SELECT();			/* Re-select device for new command */			
-    SPI_WriteByte(FL_WRSR);		/* Send command */
-    SPI_WriteByte(status);		/* Send data byte */
-    FL_DESELECT();			/* De-select device */
-}
-
-uint8_t
-flashread(uint32_t addr) {
-    uint8_t	data;
-    
-    FL_SELECT();			/* Select device */
-
-    SPI_WriteByte(FL_READ);		/* Send command */
-    SPI_WriteByte(addr >> 16);		/* Send address */
-    SPI_WriteByte(addr >> 8);
-    SPI_WriteByte(addr);
-    data = SPI_WriteByte(0x00);		/* Read data */
-  
-    FL_DESELECT();			/* De-select device */
-
-    return data;
-}
-
-void
-flashwrite(uint32_t addr, uint8_t data) {
-    flashwait();
-    flashenablewrite();			/* Enable writes */
-
-    FL_SELECT();			/* Select device */
-
-    SPI_WriteByte(FL_BYTEPROG);		/* Send command */
-    SPI_WriteByte(addr >> 16);		/* Send address */
-    SPI_WriteByte(addr >> 8);
-    SPI_WriteByte(addr);
-    SPI_WriteByte(data);		/* Write data */
-  
-    FL_DESELECT();			/* De-select device */
-
-}
-
-/*
- * fStream reading looks like so
- *
- */
-
-void
-flashstartread(uint32_t addr) {
-    assert(readstate == RW_IDLE);
-    
-    FL_SELECT();			/* Select device */
-
-    SPI_WriteByte(FL_READ);		/* Send command */
-    SPI_WriteByte(addr >> 16); 		/* Send address */
-    SPI_WriteByte(addr >> 8);
-    SPI_WriteByte(addr);
-
-    readstate = RW_RUNNING;
-}
-
-uint8_t
-flashreadbyte(void) {
-    assert(readstate == RW_RUNNING);
-    return SPI_WriteByte(0x00);		/* Read data */
-}
-
-void
-flashstopread(void) {
-    assert(readstate == RW_RUNNING);
-
-    FL_DESELECT();
-
-    readstate = RW_IDLE;
-}
-
-/* 
- * Auto increment writing looks like so
- *
- * Enable writing  				CS, WREN, nCS
- * Send start address & first data word		CS, AAI + addr + data, nCS
- * Send subsequent words			wait for nBUSY, CS, AAI + data, nCS
- * ...
- * Disable writing				CS, WRDI, nCS
- *
- * XXX: EBSY command links SO to flash busy state, I don't think the
- * STM32 could sample it without switching out of SPI mode.
- */
-void
-flashstartwrite(uint32_t addr, uint16_t data) {
-    assert(writestate == RW_IDLE);
-    
-    flashenablewrite();			/* Enable writes */
-
-    FL_SELECT();			/* Select device */
-
-    SPI_WriteByte(FL_AAIWP);		/* Send command */
-    SPI_WriteByte(addr >> 16);
-    SPI_WriteByte(addr >> 8);
-    SPI_WriteByte(addr & 0xff); 	/* Send address */
-
-    SPI_WriteByte(data & 0xff);		/* Write LSB */
-    SPI_WriteByte(data >> 8);		/* Write MSB */
-
-    FL_DESELECT();
-
-    writestate = RW_RUNNING;
-}
-
-void
-flashwriteword(uint16_t data) {
-    assert(writestate == RW_RUNNING);
-    
-    //fputs("write word ", stdout);
-    flashwait();			/* Wait until not busy */
-
-    FL_SELECT();			/* Select device */
-
-    SPI_WriteByte(FL_AAIWP);		/* Send command */
-    SPI_WriteByte(data & 0xff);		/* Write LSB */
-    SPI_WriteByte(data >> 8);		/* Write MSB */
-
-    FL_DESELECT();			/* De-select device */
-}
-
-void
-flashstopwrite(void) {
-    assert(writestate == RW_RUNNING);
-
-    //fputs("flash stop write start ", stdout);
-    flashwait();			/* Wait until not busy */
-
-    FL_SELECT();			/* Select device */
-
-    SPI_WriteByte(FL_WRDI);		/* Send command */
-
-    FL_DESELECT();			/* Deselect device */
-
-    //fputs("flash stop write end ", stdout);
-    flashwait();			/* Wait until not busy */
-
-    writestate = RW_IDLE;
-}
-
-int
-flashreadblock(uint32_t addr, uint32_t len, void *_data) {
-    uint8_t	*data = _data;
-    uint32_t	flashcrc, ramcrc;
-
-    /* Must be a multiple of 4 due to CRC check */
-    assert(len % 4 == 0);
-
-    flashstartread(addr);
-   CRC_ResetDR();
-    for (int i = len; i > 0; i--) {
-	*data = flashreadbyte();
-	CRC_CalcCRC(*data);
-	data++;
-    }
-
-    flashcrc = flashreadbyte();
-    flashcrc |= flashreadbyte() << 8;
-    flashcrc |= flashreadbyte() << 16;
-    flashcrc |= flashreadbyte() << 24;
-
-    flashstopread();
-
-    ramcrc = CRC_GetCRC();
-    
-    /* printf("RAM CRC 0x%08x Flash CRC 0x%08x\n", (uint)ramcrc, (uint)flashcrc); */
-    if (ramcrc == flashcrc)
-	return 1;
-    else
-	return 0;
-}
-
-uint32_t
-flashcrcblock(uint32_t addr, uint32_t len) {
-    assert(len % 4 == 0);
-
-   CRC_ResetDR();
-
-    flashstartread(addr);
-    for (int i = len; i > 0; i--)
-	CRC_CalcCRC(flashreadbyte());
-
-    flashstopread();
-
-    return CRC_GetCRC();
-}
-
-int
-flashwriteblock(uint32_t addr, uint32_t len, void *_data) {
-    uint16_t	*data = _data;
-    uint32_t	crc, vcrc;
-    
-    //printf("Writing %u bytes to 0x%06x\n", (uint)len, (uint)addr);
-    
-    /* Ensure data is
-     * - 16 bit aligned
-     * - a multiple of 32 bits in length (for CRCs, the flash only need 16 bits)
-     * - not longer than a sector 
-     */
-    assert(addr % 2 == 0);
-    assert(len % 4 == 0);
-    assert(len <= 4096);
-    
-    /* Disable write protect */
-    flashwritestatus(0x00);
-
-    /* Erase sector */
-    flash4kerase(addr);
- 
-   CRC_ResetDR();
-
-    /* Write data */
-   for (uint i = 0; i < len / 2; i++) {
-	if (i == 0)
-	    flashstartwrite(addr, *data);
-	else
-	    flashwriteword(*data);
-	CRC_CalcCRC(*data);
-	data++;
-    }
-
-   /* Calculate CRC */
-   crc = CRC_GetCRC();
-   
-   //printf("CRC is 0x%08x\n", (uint)crc);
-   
-   /* Write CRC */
-   flashwriteword(crc);
-   flashwriteword(crc >> 16);
-   
-   flashstopwrite();
-
-   /* Read back and check CRC */
-   vcrc = flashcrcblock(addr, len);
-   if (vcrc != crc)
-       return 1;
-   else
-       return 0;
-}
--- a/flash.h	Sun Jul 07 22:48:17 2013 +0930
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-void		flashcmd(int argc, char **argv);
-uint16_t	flashreadid(void);
-uint8_t		flashreadstatus(void);
-void		flashwritestatus(uint8_t status);
-void		flashwritectl(int enable);
-void		flash4kerase(uint32_t addr);
-uint8_t		flashread(uint32_t addr);
-void		flashenablewrite(void);
-void		flashwrite(uint32_t addr, uint8_t data);
-void		flashwait(void);
-int		flashreadblock(uint32_t addr, uint32_t len, void *_data);
-int		flashwriteblock(uint32_t addr, uint32_t len, void *_data);
-uint32_t	flashcrcblock(uint32_t addr, uint32_t len);
-
-/* Streaming read/write */
-void		flashstartread(uint32_t addr);
-uint8_t		flashreadbyte(void);
-void		flashstartwrite(uint32_t addr, uint16_t data);
-void		flashwriteword(uint16_t data);
-void		flashstopread(void);
-void		flashstopwrite(void);
-
-#define FL_BUSY		(1<<0)
-#define FL_WEL		(1<<1)
-#define FL_BP0		(1<<2)
-#define FL_BP1		(1<<3)
-#define FL_BP2		(1<<4)
-#define FL_BP3		(1<<5)
-#define FL_AAI		(1<<6)
-#define FL_BPL		(1<<7)
-
-#define FL_READ		0x03
-#define FL_HSREAD	0x0b
-#define FL_4KERASE	0x20
-#define FL_32KERASE	0x52
-#define FL_64KERASE	0xd8
-#define FL_CHIPERASE	0x60
-#define FL_BYTEPROG	0x02
-#define FL_AAIWP	0xad
-#define FL_RDSR		0x05
-#define FL_EWSR		0x50
-#define FL_WRSR		0x01
-#define FL_WREN		0x06
-#define FL_WRDI		0x04
-#define FL_RDID		0x90
-#define FL_JEDECID	0x9f
-#define FL_EBSY		0x70
-#define FL_DBSY		0x80
-
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spiflash.c	Sun Jul 07 22:49:02 2013 +0930
@@ -0,0 +1,447 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "stm32f10x.h"
+#include "spi.h"
+#include "spiflash.h"
+
+#define FL_SELECT()	GPIO_ResetBits(GPIOA, GPIO_Pin_4)
+#define FL_DESELECT()	GPIO_SetBits(GPIOA, GPIO_Pin_4)
+
+const char *flstattbl[] = {
+    "BUSY",
+    "WEL",
+    "BP0",
+    "BP1",
+    "BP2",
+    "BP3",
+    "AAI",
+    "BPL"
+};
+
+#define RW_IDLE		0
+#define RW_RUNNING	1
+
+static int writestate = RW_IDLE;
+static int readstate = RW_IDLE;
+#if 0
+void
+spiflashcmd(int argc, char **argv) {
+    uint8_t status, tmp, len;
+    uint32_t addr;
+    
+    if (argc == 0) {
+	fputs("No command specified\n", stdout);
+	return;
+    }
+    
+    if (!strcmp(argv[0], "str")) {
+	status = spiflashreadstatus();
+	fputs("Status = ", stdout);
+	for (unsigned int i = 0; i < sizeof(flstattbl) / sizeof(flstattbl[0]); i++)
+	    if (status & 1 << i) {
+		fputs(flstattbl[i], stdout);
+		fputs(" ", stdout);
+	    }
+	printf("(0x%02x)\n", status);
+    } else if (!strcmp(argv[0], "stw")) {
+	if (argc != 2) {
+	    fputs("Incorrect number of arguments\n", stdout);
+	    return;
+	}
+	tmp = atoi(argv[1]);
+	spiflashwritestatus(tmp);
+	status = spiflashreadstatus();
+	printf("Wrote 0x%02x to status, now 0x%02x\n", tmp, status);
+    } else if (!strcmp(argv[0], "er")) {
+	if (argc != 2) {
+	    fputs("Incorrect number of arguments\n", stdout);
+	    return;
+	}
+	addr = atoi(argv[1]);
+	spiflash4kerase(addr);
+	printf("Erased 0x%x\n", (unsigned int)addr);
+    } else if (!strcmp(argv[0], "rd")) {
+	if (argc < 2) {
+	    fputs("Incorrect number of arguments\n", stdout);
+	    return;
+	}
+	
+	addr = atoi(argv[1]);
+
+	if (argc > 2)
+	    len = atoi(argv[2]);
+	else
+	    len = 16;
+
+	spiflashstartread(addr);
+	
+	for (uint32_t i = 0; i < len; i++)
+	    printf("Read 0x%02x from 0x%06x\n", spiflashreadbyte(), (unsigned int)(addr + i));
+	spiflashstopread();
+	
+	fputs("\n", stdout);
+    } else if (!strcmp(argv[0], "wr")) {
+	if (argc < 2) {
+	    fputs("Incorrect number of arguments\n", stdout);
+	    return;
+	}
+
+	addr = atoi(argv[1]);
+
+	if (argc > 2)
+	    len = atoi(argv[2]);
+	else
+	    len = 16;
+
+	for (int i = 0; i < 16; i += 2) {
+	    uint16_t data;
+	    data = ((i + 1) << 8) | i;
+	    printf("Writing 0x%04x to 0x%06x\n", data, (unsigned int)(addr + i));
+
+	    if (i == 0)
+		spiflashstartwrite(addr, data);
+	    else
+		spiflashwriteword(data);
+	}
+	spiflashstopwrite();
+    } else if (!strcmp(argv[0], "id")) {
+	printf("Flash ID = 0x%04hx (expect 0xbf41)\n", spiflashreadid());
+    } else {
+	fputs("Unknown sub command\n", stdout);
+	return;
+    }
+}
+#endif
+void
+spiflash4kerase(uint32_t addr) {
+    spiflashenablewrite();			/* Enable writing */
+    
+    FL_SELECT();			/* Select device */
+
+    SPI_WriteByte(FL_4KERASE);		/* Send command */
+    SPI_WriteByte(addr >> 16);	 	/* Send address */
+    SPI_WriteByte(addr >> 8);
+    SPI_WriteByte(addr);
+  
+    FL_DESELECT();
+    
+    //fputs("4k erase ", stdout);
+    spiflashwait();
+}
+
+void
+spiflashwait(void) {
+    uint8_t	cnt;
+    
+    /* Wait for not BUSY */
+    for (cnt = 0; (spiflashreadstatus() & FL_BUSY) != 0; cnt++)
+	;
+
+    //printf("cnt = %d\n", cnt);
+}
+
+uint16_t
+spiflashreadid(void) {
+    uint8_t	fac, dev;
+    
+    FL_SELECT();			/* Select device */
+
+    SPI_WriteByte(FL_RDID);		/* Send command */
+    SPI_WriteByte(0x00);		/* Send address cycles (ID data starts at 0) */
+    SPI_WriteByte(0x00);
+    SPI_WriteByte(0x00);
+    fac = SPI_WriteByte(0x00);		/* Read ID */
+    dev = SPI_WriteByte(0x00);
+  
+    FL_DESELECT();			/* De-select device */
+
+    return fac << 8 | dev;
+}
+
+void
+spiflashenablewrite(void) {
+    FL_SELECT();			/* Select device */
+
+    SPI_WriteByte(FL_WREN);		/* Send command */
+  
+    FL_DESELECT();			/* De-select device */
+}
+
+uint8_t
+spiflashreadstatus(void) {
+    uint8_t	status;
+    
+    FL_SELECT();			/* Select device */
+
+    SPI_WriteByte(FL_RDSR);		/* Send command */
+    SPI_WriteByte(0x00);		/* Send dummy byte for address cycle */
+    status = SPI_WriteByte(0x00);	/* Read status */
+  
+    FL_DESELECT();			/* De-select device */
+
+    return status;
+}
+
+void
+spiflashwritestatus(uint8_t status) {
+    /* Enable status write */
+    FL_SELECT();			/* Select device */
+    SPI_WriteByte(FL_EWSR);		/* Send command */
+    SPI_WriteByte(0x00);		/* Send data byte */
+    FL_DESELECT();
+
+    /* Actually write status */
+    FL_SELECT();			/* Re-select device for new command */			
+    SPI_WriteByte(FL_WRSR);		/* Send command */
+    SPI_WriteByte(status);		/* Send data byte */
+    FL_DESELECT();			/* De-select device */
+}
+
+uint8_t
+spiflashread(uint32_t addr) {
+    uint8_t	data;
+    
+    FL_SELECT();			/* Select device */
+
+    SPI_WriteByte(FL_READ);		/* Send command */
+    SPI_WriteByte(addr >> 16);		/* Send address */
+    SPI_WriteByte(addr >> 8);
+    SPI_WriteByte(addr);
+    data = SPI_WriteByte(0x00);		/* Read data */
+  
+    FL_DESELECT();			/* De-select device */
+
+    return data;
+}
+
+void
+spiflashwrite(uint32_t addr, uint8_t data) {
+    spiflashwait();
+    spiflashenablewrite();			/* Enable writes */
+
+    FL_SELECT();			/* Select device */
+
+    SPI_WriteByte(FL_BYTEPROG);		/* Send command */
+    SPI_WriteByte(addr >> 16);		/* Send address */
+    SPI_WriteByte(addr >> 8);
+    SPI_WriteByte(addr);
+    SPI_WriteByte(data);		/* Write data */
+  
+    FL_DESELECT();			/* De-select device */
+
+}
+
+/*
+ * fStream reading looks like so
+ *
+ */
+
+void
+spiflashstartread(uint32_t addr) {
+    assert(readstate == RW_IDLE);
+    
+    FL_SELECT();			/* Select device */
+
+    SPI_WriteByte(FL_READ);		/* Send command */
+    SPI_WriteByte(addr >> 16); 		/* Send address */
+    SPI_WriteByte(addr >> 8);
+    SPI_WriteByte(addr);
+
+    readstate = RW_RUNNING;
+}
+
+uint8_t
+spiflashreadbyte(void) {
+    assert(readstate == RW_RUNNING);
+    return SPI_WriteByte(0x00);		/* Read data */
+}
+
+void
+spiflashstopread(void) {
+    assert(readstate == RW_RUNNING);
+
+    FL_DESELECT();
+
+    readstate = RW_IDLE;
+}
+
+/* 
+ * Auto increment writing looks like so
+ *
+ * Enable writing  				CS, WREN, nCS
+ * Send start address & first data word		CS, AAI + addr + data, nCS
+ * Send subsequent words			wait for nBUSY, CS, AAI + data, nCS
+ * ...
+ * Disable writing				CS, WRDI, nCS
+ *
+ * XXX: EBSY command links SO to flash busy state, I don't think the
+ * STM32 could sample it without switching out of SPI mode.
+ */
+void
+spiflashstartwrite(uint32_t addr, uint16_t data) {
+    assert(writestate == RW_IDLE);
+    
+    spiflashenablewrite();			/* Enable writes */
+
+    FL_SELECT();			/* Select device */
+
+    SPI_WriteByte(FL_AAIWP);		/* Send command */
+    SPI_WriteByte(addr >> 16);
+    SPI_WriteByte(addr >> 8);
+    SPI_WriteByte(addr & 0xff); 	/* Send address */
+
+    SPI_WriteByte(data & 0xff);		/* Write LSB */
+    SPI_WriteByte(data >> 8);		/* Write MSB */
+
+    FL_DESELECT();
+
+    writestate = RW_RUNNING;
+}
+
+void
+spiflashwriteword(uint16_t data) {
+    assert(writestate == RW_RUNNING);
+    
+    //fputs("write word ", stdout);
+    spiflashwait();			/* Wait until not busy */
+
+    FL_SELECT();			/* Select device */
+
+    SPI_WriteByte(FL_AAIWP);		/* Send command */
+    SPI_WriteByte(data & 0xff);		/* Write LSB */
+    SPI_WriteByte(data >> 8);		/* Write MSB */
+
+    FL_DESELECT();			/* De-select device */
+}
+
+void
+spiflashstopwrite(void) {
+    assert(writestate == RW_RUNNING);
+
+    //fputs("flash stop write start ", stdout);
+    spiflashwait();			/* Wait until not busy */
+
+    FL_SELECT();			/* Select device */
+
+    SPI_WriteByte(FL_WRDI);		/* Send command */
+
+    FL_DESELECT();			/* Deselect device */
+
+    //fputs("flash stop write end ", stdout);
+    spiflashwait();			/* Wait until not busy */
+
+    writestate = RW_IDLE;
+}
+
+int
+spiflashreadblock(uint32_t addr, uint32_t len, void *_data) {
+    uint8_t	*data = _data;
+    uint32_t	flashcrc, ramcrc;
+
+    /* Must be a multiple of 4 due to CRC check */
+    assert(len % 4 == 0);
+
+    spiflashstartread(addr);
+   CRC_ResetDR();
+    for (int i = len; i > 0; i--) {
+	*data = spiflashreadbyte();
+	CRC_CalcCRC(*data);
+	data++;
+    }
+
+    flashcrc = spiflashreadbyte();
+    flashcrc |= spiflashreadbyte() << 8;
+    flashcrc |= spiflashreadbyte() << 16;
+    flashcrc |= spiflashreadbyte() << 24;
+
+    spiflashstopread();
+
+    ramcrc = CRC_GetCRC();
+    
+    /* printf("RAM CRC 0x%08x Flash CRC 0x%08x\n", (uint)ramcrc, (uint)flashcrc); */
+    if (ramcrc == flashcrc)
+	return 1;
+    else
+	return 0;
+}
+
+uint32_t
+spiflashcrcblock(uint32_t addr, uint32_t len) {
+    assert(len % 4 == 0);
+
+   CRC_ResetDR();
+
+    spiflashstartread(addr);
+    for (int i = len; i > 0; i--)
+	CRC_CalcCRC(spiflashreadbyte());
+
+    spiflashstopread();
+
+    return CRC_GetCRC();
+}
+
+int
+spiflashwriteblock(uint32_t addr, uint32_t len, void *_data) {
+    uint16_t	*data = _data;
+    uint32_t	crc, vcrc;
+    
+    //printf("Writing %u bytes to 0x%06x\n", (uint)len, (uint)addr);
+    
+    /* Ensure data is
+     * - 16 bit aligned
+     * - a multiple of 32 bits in length (for CRCs, the flash only need 16 bits)
+     * - not longer than a sector 
+     */
+    assert(addr % 2 == 0);
+    assert(len % 4 == 0);
+    assert(len <= 4096);
+    
+    /* Disable write protect */
+    spiflashwritestatus(0x00);
+
+    /* Erase sector */
+    spiflash4kerase(addr);
+ 
+   CRC_ResetDR();
+
+    /* Write data */
+   for (uint i = 0; i < len / 2; i++) {
+	if (i == 0)
+	    spiflashstartwrite(addr, *data);
+	else
+	    spiflashwriteword(*data);
+	CRC_CalcCRC(*data);
+	data++;
+    }
+
+   /* Calculate CRC */
+   crc = CRC_GetCRC();
+   
+   //printf("CRC is 0x%08x\n", (uint)crc);
+   
+   /* Write CRC */
+   spiflashwriteword(crc);
+   spiflashwriteword(crc >> 16);
+   
+   spiflashstopwrite();
+
+   /* Read back and check CRC */
+   vcrc = spiflashcrcblock(addr, len);
+   if (vcrc != crc)
+       return 1;
+   else
+       return 0;
+}
+
+void
+spiflashprintstatus(uint8_t status, FILE *out) {
+    for (unsigned int i = 0; i < sizeof(flstattbl) / sizeof(flstattbl[0]); i++)
+	if (status & 1 << i) {
+	    fputs(flstattbl[i], out);
+	    fputs(" ", out);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spiflash.h	Sun Jul 07 22:49:02 2013 +0930
@@ -0,0 +1,50 @@
+void		spiflashcmd(int argc, char **argv);
+uint16_t	spiflashreadid(void);
+uint8_t		spiflashreadstatus(void);
+void		spiflashwritestatus(uint8_t status);
+void		spiflashwritectl(int enable);
+void		spiflash4kerase(uint32_t addr);
+uint8_t		spiflashread(uint32_t addr);
+void		spiflashenablewrite(void);
+void		spiflashwrite(uint32_t addr, uint8_t data);
+void		spiflashwait(void);
+int		spiflashreadblock(uint32_t addr, uint32_t len, void *_data);
+int		spiflashwriteblock(uint32_t addr, uint32_t len, void *_data);
+uint32_t	spiflashcrcblock(uint32_t addr, uint32_t len);
+void		spiflashprintstatus(uint8_t status, FILE *out);
+
+/* Streaming read/write */
+void		spiflashstartread(uint32_t addr);
+uint8_t		spiflashreadbyte(void);
+void		spiflashstartwrite(uint32_t addr, uint16_t data);
+void		spiflashwriteword(uint16_t data);
+void		spiflashstopread(void);
+void		spiflashstopwrite(void);
+
+#define FL_BUSY		(1<<0)
+#define FL_WEL		(1<<1)
+#define FL_BP0		(1<<2)
+#define FL_BP1		(1<<3)
+#define FL_BP2		(1<<4)
+#define FL_BP3		(1<<5)
+#define FL_AAI		(1<<6)
+#define FL_BPL		(1<<7)
+
+#define FL_READ		0x03
+#define FL_HSREAD	0x0b
+#define FL_4KERASE	0x20
+#define FL_32KERASE	0x52
+#define FL_64KERASE	0xd8
+#define FL_CHIPERASE	0x60
+#define FL_BYTEPROG	0x02
+#define FL_AAIWP	0xad
+#define FL_RDSR		0x05
+#define FL_EWSR		0x50
+#define FL_WRSR		0x01
+#define FL_WREN		0x06
+#define FL_WRDI		0x04
+#define FL_RDID		0x90
+#define FL_JEDECID	0x9f
+#define FL_EBSY		0x70
+#define FL_DBSY		0x80
+