Mercurial > ~darius > hgwebdir.cgi > stm32temp
diff spiflash.c @ 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 | flash.c@85f16813c730 |
children | 05ba84c7da97 |
line wrap: on
line diff
--- /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); + } +}