| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431 | #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){    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_statusspiflash_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);    if (digitalRead(PIN_FPGA_READY) == LOW) {	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;}
 |