123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430 |
- #define MODULE "spiflash"
- #define DEBUG 1
- #include "common.h"
- #include "spiflash.h"
- #include "spz.h"
- #include "fw.h"
- #include "jtag.h"
- /*
- * SPI flash parameters
- */
- #define JTAG_SPIFLASH_HZ 10000000 /* Max 26 MHz due to ESP32 */
- static const struct jtag_config jtag_config_spiflash = {
- .hz = JTAG_SPIFLASH_HZ,
- .pin_tms = 10, /* CS# */
- .pin_tdi = 12, /* MOSI */
- .pin_tdo = 13, /* MISO */
- .pin_tck = 11, /* SCLK */
- .be = true /* Bit order within bytes */
- };
- /*
- * Set up a command header with an address according to the SPI
- * addressing mode. Returns a pointer to the first byte past the
- * address.
- */
- static void *spiflash_setup_addrcmd(uint32_t addr,
- uint8_t cmd24, uint8_t cmd32,
- void *cmdbuf)
- {
- enum spiflash_addr_mode mode = SPIFLASH_ADDR_DYNAMIC;
- uint8_t *cmd = cmdbuf;
- if (!mode)
- mode = addr < (1 << 24) ? SPIFLASH_ADDR_24BIT : SPIFLASH_ADDR_32BIT;
- if (mode == SPIFLASH_ADDR_24BIT) {
- *cmd++ = cmd24;
- } else {
- *cmd++ = cmd32;
- *cmd++ = addr >> 24;
- }
- *cmd++ = addr >> 16;
- *cmd++ = addr >> 8;
- *cmd++ = addr;
- return cmd;
- }
- # define SHOW_COMMAND() \
- do { \
- MSG("command: ", cmdbuf, cmdlen); \
- for (size_t i = 0; i < cmdlen; i++) \
- CMSG(" %02x", ((const uint8_t *)cmdbuf)[i]); \
- CMSG("\n"); \
- } while(0)
- static int spiflash_simple_command(uint32_t cmd)
- {
- jtag_io(8, JIO_CS, &cmd, NULL);
- return 0;
- }
- static int spiflash_plain_command(const void *cmdbuf, size_t cmdlen)
- {
- #if DEBUG > 1
- MSG("plain: cmdbuf = %p (%zu), databuf = %p (%zu)\n",
- cmdbuf, cmdlen);
- SHOW_COMMAND();
- #endif
- jtag_io(cmdlen << 3, JIO_CS, cmdbuf, NULL);
- return 0;
- }
- static int spiflash_output_command(const void *cmdbuf, size_t cmdlen,
- const void *databuf, size_t datalen)
- {
- if (!datalen)
- return spiflash_plain_command(cmdbuf, cmdlen);
- #if DEBUG > 1
- MSG("output: cmdbuf = %p (%zu), databuf = %p (%zu)\n",
- cmdbuf, cmdlen, databuf, datalen);
- SHOW_COMMAND();
- #endif
- jtag_io(cmdlen << 3, 0, cmdbuf, NULL);
- jtag_io(datalen << 3, JIO_CS, databuf, NULL);
- return 0;
- }
- static int spiflash_input_command(const void *cmdbuf, size_t cmdlen,
- void *databuf, size_t datalen)
- {
- if (!datalen)
- return spiflash_plain_command(cmdbuf, cmdlen);
- #if DEBUG > 1
- MSG("input: cmdbuf = %p (%zu), databuf = %p (%zu)\n",
- cmdbuf, cmdlen, databuf, datalen);
- SHOW_COMMAND();
- #endif
- jtag_io(cmdlen << 3, 0, cmdbuf, NULL);
- jtag_io(datalen << 3, JIO_CS, NULL, databuf);
- return 0;
- }
- static int spiflash_read_status(uint32_t reg)
- {
- uint32_t val = 0;
- jtag_io(8, 0, (const uint8_t *)®, NULL);
- jtag_io(8, JIO_CS, NULL, (uint8_t *)&val);
- return val;
- }
- /* This needs a timeout function */
- static int spiflash_wait_status(uint8_t mask, uint8_t val)
- {
- unsigned int wait_loops = 100000;
- #if DEBUG > 1
- MSG("waiting for status %02x/%02x... ", mask, val);
- #endif
- while (wait_loops--) {
- uint8_t sr1 = spiflash_read_status(ROM_READ_SR1);
- if ((sr1 & mask) == val) {
- #if DEBUG > 1
- CMSG("ok\n");
- #endif
- return 0;
- }
- yield();
- }
- #if DEBUG > 1
- CMSG("timeout\n");
- #endif
- return -1;
- }
- static int spiflash_read(uint32_t addr, void *buffer, size_t len)
- {
- uint32_t cmdbuf[2];
- uint8_t *cmd = (uint8_t *)cmdbuf;
- const uint8_t cmd24 = ROM_FAST_READ;
- const uint8_t cmd32 = ROM_FAST_READ_32BIT;
- const size_t max_read_len = -1;
- int rv;
- while (len) {
- size_t clen = len;
- if (clen > max_read_len)
- clen = max_read_len;
- cmd = spiflash_setup_addrcmd(addr, cmd24, cmd32, cmdbuf);
- *cmd++ = 0; /* Dummy cycles */
- rv = spiflash_input_command(cmdbuf, cmd - (uint8_t *)cmdbuf,
- buffer, clen);
- if (rv)
- return rv;
- addr += clen;
- buffer = (uint8_t *)buffer + clen;
- len -= clen;
- }
- return 0;
- }
- static int spiflash_write_enable(void)
- {
- uint8_t sr1;
- int rv;
- rv = spiflash_wait_status(1, 0);
- if (rv)
- return rv;
- spiflash_simple_command(ROM_WRITE_ENABLE);
- return spiflash_wait_status(3, 2);
- }
- static int spiflash_program_sector(uint32_t addr, const void *buffer)
- {
- uint32_t cmdbuf[2];
- uint8_t *cmd = (uint8_t *)cmdbuf;
- const uint8_t cmd24 = ROM_PAGE_PROGRAM;
- const uint8_t cmd32 = ROM_PAGE_PROGRAM_32BIT;
- int rv;
- int loops = SPIFLASH_SECTOR_SIZE / SPIFLASH_PAGE_SIZE;
- const char *p = buffer;
- while (loops--) {
- rv = spiflash_write_enable();
- if (rv)
- return rv;
- cmd = spiflash_setup_addrcmd(addr, cmd24, cmd32, cmdbuf);
- spiflash_output_command(cmdbuf, cmd - (uint8_t *)cmdbuf,
- p, SPIFLASH_PAGE_SIZE);
- rv = spiflash_wait_status(3, 0);
- if (rv)
- return rv;
- addr += SPIFLASH_PAGE_SIZE;
- p += SPIFLASH_PAGE_SIZE;
- }
- return 0;
- }
- static int spiflash_erase_sector(uint32_t addr)
- {
- uint32_t cmdbuf[2];
- uint8_t *cmd = (uint8_t *)cmdbuf;
- const uint8_t cmd24 = ROM_ERASE_4K;
- const uint8_t cmd32 = ROM_ERASE_4K_32BIT;
- int rv;
- rv = spiflash_write_enable();
- if (rv)
- return rv;
- cmd = spiflash_setup_addrcmd(addr, cmd24, cmd32, cmdbuf);
- spiflash_plain_command(cmdbuf, cmd - (uint8_t *)cmdbuf);
- return spiflash_wait_status(3, 0);
- }
- /*
- * from: current flash contents
- * to: desired flash contents
- *
- * These are assumed to be aligned full block buffers
- */
- enum flashmem_status {
- FMS_DONE, /* All done, no programming needed */
- FMS_PROGRAM, /* Can be programmed */
- FMS_ERASE, /* Needs erase before programming */
- FMS_NOTCHECKED /* Not checked yet */
- };
- static enum flashmem_status
- spiflash_memcmp(const void *from, const void *to, size_t len)
- {
- const uint32_t *pf = from;
- const uint32_t *pt = to;
- const uint32_t *pfend = (const uint32_t *)((const char *)from + len);
- uint32_t doprog = 0;
- uint32_t doerase = 0;
- while (pf < pfend) {
- uint32_t f = *pf++;
- uint32_t t = *pt++;
- doprog += !!(f ^ t); /* Need programming if any data mismatch */
- doerase += !!(~f & t); /* Need erasing if any 0 -> 1 */
- }
- return doerase ? FMS_ERASE : doprog ? FMS_PROGRAM : FMS_DONE;
- }
- static int spiflash_write_sector(spz_stream *spz, unsigned int addr)
- {
- enum flashmem_status status = FMS_NOTCHECKED;
- MSG("flash sector at 0x%06x: ", addr);
- while (1) {
- enum flashmem_status oldstatus = status;
- status = spiflash_memcmp(spz->vbuf, spz->dbuf, SPIFLASH_SECTOR_SIZE);
- if (status >= oldstatus) {
- CMSG("X [%u>%u]", oldstatus, status);
- break;
- } else if (status == FMS_DONE) {
- CMSG("V");
- break;
- } else if (status == FMS_ERASE) {
- CMSG("E");
- if (spiflash_erase_sector(addr))
- break;
- } else if (status == FMS_PROGRAM) {
- CMSG("P");
- if (spiflash_program_sector(addr, spz->dbuf))
- break;
- }
- memset(spz->vbuf, 0xdd, SPIFLASH_SECTOR_SIZE);
- spiflash_read(addr, spz->vbuf, SPIFLASH_SECTOR_SIZE);
- }
- int rv;
- if (status == FMS_DONE) {
- CMSG(" OK\n");
- rv = 0;
- } else {
- CMSG(" FAILED\n");
- rv = (status == FMS_PROGRAM)
- ? FWUPDATE_ERR_PROGRAM_FAILED : FWUPDATE_ERR_ERASE_FAILED;
- }
- if (!spz->err)
- spz->err = rv;
- return rv;
- }
- static int spiflash_read_jedec_id(void)
- {
- const uint32_t cmd = ROM_JEDEC_ID;
- uint32_t jid = 0;
- spiflash_input_command((uint8_t *)&cmd, 1, (uint8_t *)&jid, 3);
- MSG("JEDEC ID: vendor %02x type %02x capacity %02x\n",
- (uint8_t)jid, (uint8_t)(jid >> 8), (uint8_t)(jid >> 16));
- return 0;
- }
- static void spiflash_show_status(void)
- {
- MSG("status regs: %02x %02x %02x\n",
- spiflash_read_status(ROM_READ_SR1),
- spiflash_read_status(ROM_READ_SR2),
- spiflash_read_status(ROM_READ_SR3));
- }
- #define PIN_FPGA_READY 9
- #define PIN_FPGA_BOARD_ID 1
- /* Set data and data_len if the data to be written is not from the spz */
- int spiflash_write_spz(spz_stream *spz,
- const void *data, unsigned int data_left)
- {
- esp_err_t rv;
- const uint8_t *dptr = data;
- unsigned int addr = spz->header.addr;
- if (!dptr)
- data_left = spz->header.len;
- if (!data_left || spz->err)
- return spz->err;
- pinMode(PIN_FPGA_READY, INPUT);
- pinMode(PIN_FPGA_BOARD_ID, INPUT);
- MSG("waiting for FPGA bypass to be ready..");
- while (digitalRead(PIN_FPGA_READY) != LOW) {
- CMSG(".");
- yield();
- }
- CMSG("\n");
- MSG("FPGA bypass ready, board version v%c.\n",
- digitalRead(PIN_FPGA_BOARD_ID) ? '1' : '2');
- jtag_enable(&jtag_config_spiflash);
- spiflash_read_jedec_id();
- spiflash_show_status();
- while (data_left && !spz->err) {
- unsigned int pre_padding = addr & (SPIFLASH_SECTOR_SIZE-1);
- unsigned int post_padding;
- unsigned int bytes;
- bytes = SPIFLASH_SECTOR_SIZE - pre_padding;
- post_padding = 0;
- if (bytes > data_left) {
- post_padding = bytes - data_left;
- bytes = data_left;
- }
- addr -= pre_padding;
- /* Read the current content of this block into vbuf */
- memset(spz->vbuf, 0xee, SPIFLASH_SECTOR_SIZE);
- rv = spiflash_read(addr, spz->vbuf, SPIFLASH_SECTOR_SIZE);
- if (rv)
- goto err;
- /* Copy any invariant chunk */
- if (pre_padding)
- memcpy(spz->dbuf, spz->vbuf, pre_padding);
- if (post_padding)
- memcpy(spz->dbuf+SPIFLASH_SECTOR_SIZE-post_padding,
- spz->vbuf+SPIFLASH_SECTOR_SIZE-post_padding,
- post_padding);
- if (dptr) {
- memcpy(spz->dbuf+pre_padding, dptr, bytes);
- dptr += bytes;
- } else {
- rv = spz_read_data(spz, spz->dbuf+pre_padding, bytes);
- if (rv != (int)bytes) {
- MSG("needed %u bytes got %d\n", bytes, rv);
- rv = Z_DATA_ERROR;
- goto err;
- }
- }
- rv = spiflash_write_sector(spz, addr);
- if (rv) {
- spz->err = rv;
- goto err;
- }
- addr += pre_padding + bytes;
- data_left -= bytes;
- }
- rv = 0;
- err:
- if (!spz->err)
- spz->err = rv;
- jtag_disable(NULL);
- return spz->err;
- }
|