|
@@ -11,23 +11,26 @@
|
|
|
|
|
|
struct spz_stream;
|
|
|
typedef struct spz_stream spz_stream;
|
|
|
+
|
|
|
+#define NBUF 4
|
|
|
struct spz_stream {
|
|
|
z_stream zs;
|
|
|
const struct spiflash *flash;
|
|
|
- int (*read_data)(spz_stream *); /* Routine to get more data */
|
|
|
- int (*end_data)(z_stream *); /* Termination routine for zlib */
|
|
|
uint8_t *optr; /* Output data pointer into obuf */
|
|
|
/* Note: available output data ends at zs->next_out */
|
|
|
- uint8_t *ibuf; /* Input buffer if compressed */
|
|
|
- uint8_t *obuf; /* Output buffer */
|
|
|
- uint8_t *dbuf; /* Block data buffer */
|
|
|
- uint8_t *vbuf; /* Readback/verify buffer */
|
|
|
- const struct spiflash_header *header;
|
|
|
- uint32_t crc32; /* Input data CRC32 */
|
|
|
- unsigned int input_left; /* Input data unread */
|
|
|
+ union {
|
|
|
+ uint8_t *bufs[NBUF];
|
|
|
+ struct {
|
|
|
+ uint8_t *ibuf; /* Input buffer if compressed */
|
|
|
+ uint8_t *obuf; /* Output buffer */
|
|
|
+ uint8_t *dbuf; /* Block data buffer */
|
|
|
+ uint8_t *vbuf; /* Readback/verify buffer */
|
|
|
+ };
|
|
|
+ };
|
|
|
+ struct spiflash_header header; /* Header of currently processed chunk */
|
|
|
int err; /* Error code to return */
|
|
|
bool eoi; /* Reached end of input */
|
|
|
- bool free_header; /* header is malloc()'d */
|
|
|
+ bool cleanup; /* Call inflateEnd() */
|
|
|
};
|
|
|
|
|
|
static void *spz_malloc(spz_stream *spz, size_t bytes)
|
|
@@ -39,246 +42,88 @@ static void *spz_malloc(spz_stream *spz, size_t bytes)
|
|
|
return p;
|
|
|
}
|
|
|
|
|
|
-static int spiflash_read_data(spz_stream *spz)
|
|
|
+static int spiflash_read_data(spz_stream *spz, void *buf, unsigned int len)
|
|
|
{
|
|
|
- int rv;
|
|
|
- int (*read_data)(void *, void *, unsigned int);
|
|
|
- unsigned int read_block_size;
|
|
|
+ uint8_t *p = buf;
|
|
|
|
|
|
- if (spz->eoi || spz->err)
|
|
|
- return 0;
|
|
|
+ while (len) {
|
|
|
+ unsigned int avail = spz->zs.next_out - spz->optr;
|
|
|
|
|
|
- read_data = spz->flash->ops->read_data;
|
|
|
- if (!spz->input_left || !read_data) {
|
|
|
- spz->eoi = true;
|
|
|
- return 0;
|
|
|
- }
|
|
|
+ if (spz->err)
|
|
|
+ break;
|
|
|
|
|
|
- read_block_size = min(spz->input_left, SPIFLASH_BLOCK_SIZE);
|
|
|
+ if (avail) {
|
|
|
+ if (avail > len)
|
|
|
+ avail = len;
|
|
|
|
|
|
- if (!spz->ibuf) {
|
|
|
- spz->ibuf = spz_malloc(spz, SPIFLASH_BLOCK_SIZE);
|
|
|
- if (!spz->ibuf) {
|
|
|
- spz->eoi = true;
|
|
|
- return 0;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- spz->zs.next_in = spz->ibuf;
|
|
|
- spz->zs.avail_in = 0;
|
|
|
+ memcpy(p, spz->optr, avail);
|
|
|
+ p += avail;
|
|
|
+ spz->optr += avail;
|
|
|
+ len -= avail;
|
|
|
+ } else {
|
|
|
+ spz->optr = spz->zs.next_out = spz->obuf;
|
|
|
+ spz->zs.avail_out = SPIFLASH_BLOCK_SIZE;
|
|
|
+
|
|
|
+ while (spz->zs.avail_out) {
|
|
|
+ if (!spz->zs.avail_in && !spz->eoi) {
|
|
|
+ int (*read_data)(void *, void *, unsigned int);
|
|
|
+ read_data = spz->flash->read_data;
|
|
|
+ spz->zs.next_in = spz->ibuf;
|
|
|
+ int rlen = read_data(spz->flash->cookie,
|
|
|
+ spz->ibuf, SPIFLASH_BLOCK_SIZE);
|
|
|
+
|
|
|
+ spz->eoi = rlen < SPIFLASH_BLOCK_SIZE;
|
|
|
+ if (rlen < 0) {
|
|
|
+ if (!spz->err)
|
|
|
+ spz->err = rlen;
|
|
|
+ rlen = 0;
|
|
|
+ }
|
|
|
+ spz->zs.avail_in = rlen;
|
|
|
+ }
|
|
|
|
|
|
- rv = read_data(spz->flash->cookie, spz->ibuf, read_block_size);
|
|
|
- if (spz->err) {
|
|
|
- rv = 0;
|
|
|
- } else if (rv < 0) {
|
|
|
- spz->err = rv;
|
|
|
- rv = 0;
|
|
|
- }
|
|
|
- if (rv != (int)read_block_size)
|
|
|
- spz->eoi = true;
|
|
|
-
|
|
|
- if (rv) {
|
|
|
- spz->crc32 = crc32(spz->crc32, spz->ibuf, rv);
|
|
|
- spz->input_left -= rv;
|
|
|
- if (!spz->input_left) {
|
|
|
- if (spz->crc32 != spz->header->crc32) {
|
|
|
- spz->err = Z_STREAM_ERROR;
|
|
|
- rv = 0;
|
|
|
+ int rv = inflate(&spz->zs, Z_SYNC_FLUSH);
|
|
|
+ if (rv == Z_OK || (rv == Z_BUF_ERROR && !spz->eoi))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ spz->eoi = true;
|
|
|
+ if (rv != Z_STREAM_END && !spz->err)
|
|
|
+ spz->err = rv;
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- return spz->zs.avail_in = rv;
|
|
|
-}
|
|
|
-
|
|
|
-static int read_data_raw(spz_stream *spz)
|
|
|
-{
|
|
|
- int rlen;
|
|
|
-
|
|
|
- if (spz->eoi)
|
|
|
- return 0;
|
|
|
-
|
|
|
- rlen = spiflash_read_data(spz);
|
|
|
- if (rlen) {
|
|
|
- spz->optr = spz->ibuf;
|
|
|
- spz->zs.next_out = spz->ibuf + rlen;
|
|
|
- spz->zs.avail_out = SPIFLASH_BLOCK_SIZE - rlen;
|
|
|
- }
|
|
|
-
|
|
|
- return rlen;
|
|
|
-}
|
|
|
-
|
|
|
-static int read_data_inflate(spz_stream *spz)
|
|
|
-{
|
|
|
- int rv = Z_STREAM_END;
|
|
|
-
|
|
|
- spz->optr = spz->zs.next_out = spz->obuf;
|
|
|
- spz->zs.avail_out = SPIFLASH_BLOCK_SIZE;
|
|
|
-
|
|
|
- while (spz->zs.avail_out) {
|
|
|
- if (!spz->zs.avail_in && !spz->eoi) {
|
|
|
- int rlen = spiflash_read_data(spz);
|
|
|
- spz->zs.next_in = spz->ibuf;
|
|
|
- spz->zs.avail_in = rlen;
|
|
|
- }
|
|
|
-
|
|
|
- rv = inflate(&spz->zs, Z_SYNC_FLUSH);
|
|
|
-
|
|
|
- if (rv == Z_OK || (rv == Z_BUF_ERROR && !spz->eoi))
|
|
|
- continue;
|
|
|
-
|
|
|
- spz->eoi = true;
|
|
|
- if (rv != Z_STREAM_END && !spz->err)
|
|
|
- spz->err = rv;
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- return spz->zs.next_out - spz->optr;
|
|
|
+ return p - (uint8_t *)buf;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* spz needs to be initialized to zero except the flash, zs.next_in,
|
|
|
* and zs.avail_in fields.
|
|
|
- *
|
|
|
- * Returns Z_STREAM_END on end of data.
|
|
|
*/
|
|
|
static int spiflash_data_init(spz_stream *spz)
|
|
|
{
|
|
|
int rv = Z_OK;
|
|
|
uint8_t *rdbuf = NULL;
|
|
|
int rlen;
|
|
|
- int (*read_data)(void *cookie, void *buf, unsigned int bufsize);
|
|
|
uint32_t header_crc;
|
|
|
|
|
|
- MSG("update: ");
|
|
|
-
|
|
|
- spz->dbuf = spz_malloc(spz, SPIFLASH_BLOCK_SIZE);
|
|
|
- spz->vbuf = spz_malloc(spz, SPIFLASH_BLOCK_SIZE);
|
|
|
- if (!spz->dbuf || !spz->vbuf)
|
|
|
- goto err;
|
|
|
-
|
|
|
- rlen = spz->zs.avail_in;
|
|
|
- spz->header = (void *)spz->zs.next_in;
|
|
|
-
|
|
|
- read_data = spz->flash->ops->read_data;
|
|
|
- spz->eoi = !read_data;
|
|
|
- if (!rlen && read_data) {
|
|
|
- struct spiflash_header *header;
|
|
|
- spz->header = header = spz_malloc(spz, sizeof *spz->header);
|
|
|
- spz->free_header = true;
|
|
|
- spz->input_left = UINT_MAX; /* Unknown at this point */
|
|
|
- rlen = read_data(spz->flash->cookie, header, sizeof *header);
|
|
|
- }
|
|
|
- if (spz->err)
|
|
|
- goto err;
|
|
|
- if (!rlen) {
|
|
|
- MSG("done");
|
|
|
- spz->err = Z_STREAM_END;
|
|
|
- goto not_err;
|
|
|
- }
|
|
|
- if (rlen < (int)sizeof *spz->header) {
|
|
|
- MSG("input underrun");
|
|
|
- spz->err = Z_STREAM_ERROR;
|
|
|
- goto err;
|
|
|
- }
|
|
|
-
|
|
|
- if (memcmp(spz->header->target, spz->flash->target, 8)) {
|
|
|
- MSG("expected firmware for \"%.8s\", but this image is for \"%.8s\"\n",
|
|
|
- spz->flash->target, spz->header->target);
|
|
|
- spz->err = Z_DATA_ERROR;
|
|
|
- goto err;
|
|
|
- }
|
|
|
-
|
|
|
- rlen -= sizeof *spz->header;
|
|
|
- spz->zs.next_in += sizeof *spz->header;
|
|
|
-
|
|
|
- /*
|
|
|
- * Check header magic and CRC
|
|
|
- */
|
|
|
- if (spz->header->magic != SPIFLASH_MAGIC) {
|
|
|
- MSG("bad header magic");
|
|
|
- spz->err = Z_STREAM_ERROR;
|
|
|
- goto err;
|
|
|
- }
|
|
|
- header_crc = crc32(0, NULL, 0);
|
|
|
- header_crc = crc32(header_crc, (const void *)spz->header,
|
|
|
- sizeof *spz->header - 4);
|
|
|
- if (header_crc != spz->header->header_crc32) {
|
|
|
- MSG("header CRC error (0x%08x vs 0x%08x)...",
|
|
|
- header_crc, spz->header->header_crc32);
|
|
|
- spz->err = Z_STREAM_ERROR;
|
|
|
- goto err;
|
|
|
- }
|
|
|
- if (!spz->header->dlen) {
|
|
|
- /* End of data */
|
|
|
- spz->err = Z_STREAM_END;
|
|
|
- goto not_err;
|
|
|
- }
|
|
|
- if (spz->header->zlen > spz->header->dlen ||
|
|
|
- (spz->eoi && (int)spz->header->zlen > rlen)) {
|
|
|
- spz->err = Z_STREAM_ERROR;
|
|
|
- goto err;
|
|
|
- }
|
|
|
-
|
|
|
- MSG("data 0x%06x..0x%06x (%u bytes)\n",
|
|
|
- spz->header->address, spz->header->address + spz->header->dlen - 1,
|
|
|
- spz->header->dlen);
|
|
|
-
|
|
|
- if (rlen > (int)spz->header->zlen)
|
|
|
- rlen = spz->header->zlen;
|
|
|
- spz->zs.avail_in = rlen;
|
|
|
-
|
|
|
- spz->crc32 = crc32(0, NULL, 0);
|
|
|
- if (rlen) {
|
|
|
- /* Received data in input buffer already */
|
|
|
- spz->crc32 = crc32(spz->crc32, spz->zs.next_in, spz->zs.avail_in);
|
|
|
- }
|
|
|
-
|
|
|
- spz->input_left = spz->header->zlen - rlen;
|
|
|
- if (!spz->input_left) {
|
|
|
- if (spz->crc32 != spz->header->crc32) {
|
|
|
- spz->err = Z_STREAM_ERROR;
|
|
|
+ for (int i = 0; i < NBUF; i++) {
|
|
|
+ spz->bufs[i] = spz_malloc(spz, SPIFLASH_BLOCK_SIZE);
|
|
|
+ if (!spz->bufs[i])
|
|
|
goto err;
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
- if (spz->header->zlen == spz->header->dlen) {
|
|
|
- /* Assume it is a raw binary; input buffer is output buffer */
|
|
|
- spz->read_data = read_data_raw;
|
|
|
- spz->optr = spz->zs.next_in;
|
|
|
- spz->zs.next_out = spz->zs.next_in + spz->zs.avail_in;
|
|
|
- } else {
|
|
|
- /* Compressed data? */
|
|
|
- spz->obuf = spz_malloc(spz, SPIFLASH_BLOCK_SIZE);
|
|
|
- if (!spz->obuf)
|
|
|
- goto err;
|
|
|
-
|
|
|
- if (rlen >= 14 && !memcmp(spz->zs.next_in, "\37\213\10", 3)) {
|
|
|
- /* It is a gzip file */
|
|
|
- spz->read_data = read_data_inflate;
|
|
|
- /* gzip, max window size */
|
|
|
- rv = inflateInit2(&spz->zs, 16 + 15);
|
|
|
- if (rv != Z_OK && rv != Z_STREAM_END) {
|
|
|
- spz->err = rv;
|
|
|
- goto err;
|
|
|
- }
|
|
|
- spz->eoi = rv == Z_STREAM_END;
|
|
|
- spz->end_data = inflateEnd;
|
|
|
- } else {
|
|
|
- /* Unknown compression format */
|
|
|
- spz->err = Z_STREAM_ERROR;
|
|
|
- goto err;
|
|
|
- }
|
|
|
+ spz->eoi = !spz->flash->read_data;
|
|
|
+ /* gzip, max window size */
|
|
|
+ rv = inflateInit2(&spz->zs, 16 + 15);
|
|
|
+ if (rv != Z_OK && rv != Z_STREAM_END) {
|
|
|
+ spz->err = rv;
|
|
|
+ goto err;
|
|
|
}
|
|
|
+ spz->cleanup = true;
|
|
|
|
|
|
err:
|
|
|
- if (spz->err)
|
|
|
- MSG(" failed (err %d)\n", spz->err);
|
|
|
-
|
|
|
-not_err:
|
|
|
- MSG("\n");
|
|
|
return spz->err;
|
|
|
- }
|
|
|
+}
|
|
|
|
|
|
static int spiflash_data_cleanup(spz_stream *spz)
|
|
|
{
|
|
@@ -289,25 +134,13 @@ static int spiflash_data_cleanup(spz_stream *spz)
|
|
|
|
|
|
err = spz->err;
|
|
|
|
|
|
- if (spz->flash->ops->close_data) {
|
|
|
- int rv = spz->flash->ops->close_data(spz->flash->cookie);
|
|
|
- if (!err)
|
|
|
- err = rv;
|
|
|
- }
|
|
|
-
|
|
|
- if (spz->end_data)
|
|
|
- spz->end_data(&spz->zs);
|
|
|
- if (spz->free_header)
|
|
|
- free((void *)spz->header);
|
|
|
- if (spz->vbuf)
|
|
|
- free(spz->vbuf);
|
|
|
- if (spz->dbuf)
|
|
|
- free(spz->dbuf);
|
|
|
- if (spz->obuf)
|
|
|
- free(spz->obuf);
|
|
|
- if (spz->ibuf)
|
|
|
- free(spz->ibuf);
|
|
|
+ if (spz->cleanup)
|
|
|
+ inflateEnd(&spz->zs);
|
|
|
|
|
|
+ for (int i = 0; i < NBUF; i++) {
|
|
|
+ if (spz->bufs[i])
|
|
|
+ free(spz->bufs[i]);
|
|
|
+ }
|
|
|
return err;
|
|
|
}
|
|
|
|
|
@@ -522,24 +355,16 @@ spiflash_memcmp(const void *from, const void *to, size_t len)
|
|
|
* Check a block for sectors which need erasing and pages which need
|
|
|
* programming; the prog_mask is 256 bits long and so span multiple words.
|
|
|
*
|
|
|
- * The desired input is spz->dbuf and the existing flash content is
|
|
|
- * written to spz->vbuf.
|
|
|
+ * The desired input is spz->dbuf and the existing flash content should be
|
|
|
+ * already read into spz->vbuf.
|
|
|
*
|
|
|
*/
|
|
|
-static int spiflash_check_block(spz_stream *spz, uint32_t addr,
|
|
|
+static void spiflash_check_block(spz_stream *spz, uint32_t addr,
|
|
|
uint32_t *erase_mask, uint32_t *prog_mask)
|
|
|
{
|
|
|
- int rv;
|
|
|
const uint8_t *p, *q;
|
|
|
unsigned int page;
|
|
|
|
|
|
- rv = spiflash_read(spz->flash, addr, spz->vbuf, SPIFLASH_BLOCK_SIZE);
|
|
|
- if (rv) {
|
|
|
- if (!spz->err)
|
|
|
- spz->err = rv;
|
|
|
- return rv;
|
|
|
- }
|
|
|
-
|
|
|
*erase_mask = 0;
|
|
|
memset(prog_mask, 0, SPIFLASH_BLOCK_SIZE/SPIFLASH_PAGE_SIZE/8);
|
|
|
|
|
@@ -565,238 +390,303 @@ static int spiflash_check_block(spz_stream *spz, uint32_t addr,
|
|
|
p += SPIFLASH_PAGE_SIZE;
|
|
|
q += SPIFLASH_PAGE_SIZE;
|
|
|
}
|
|
|
-
|
|
|
- return 0;
|
|
|
}
|
|
|
|
|
|
-/* Serial Flash Discoverable Parameter Table, see JESD216 */
|
|
|
-static int spiflash_get_sfdp(const struct spiflash *flash, void *sfdp)
|
|
|
+static int spiflash_flash_chunk(spz_stream *spz)
|
|
|
{
|
|
|
- static const uint8_t cmd_read_sfdp[] = { ROM_READ_SFDP, 0, 0, 0, 0 };
|
|
|
+ unsigned int data_left = spz->header.len;
|
|
|
+ unsigned int addr = spz->header.addr;
|
|
|
+ int rv;
|
|
|
|
|
|
- return flash->ops->spi_read(flash->cookie, cmd_read_sfdp,
|
|
|
- sizeof cmd_read_sfdp, sfdp, SPIFLASH_SFDP_SIZE,
|
|
|
- flash->param->tshsl);
|
|
|
-}
|
|
|
-
|
|
|
+ while (data_left && !spz->err) {
|
|
|
+ unsigned int pre_padding = addr & (SPIFLASH_BLOCK_SIZE-1);
|
|
|
+ unsigned int post_padding;
|
|
|
+ unsigned int bytes;
|
|
|
|
|
|
-int spiflash_flash_files(const struct spiflash *flash, void *buf, size_t buflen)
|
|
|
-{
|
|
|
- spz_stream _spz;
|
|
|
- spz_stream * const spz = &_spz; /* For consistency in notation */
|
|
|
- int err = 0;
|
|
|
- enum flashmem_status fs;
|
|
|
+ bytes = SPIFLASH_BLOCK_SIZE - pre_padding;
|
|
|
+ post_padding = 0;
|
|
|
+ if (bytes > data_left) {
|
|
|
+ post_padding = bytes - data_left;
|
|
|
+ bytes = data_left;
|
|
|
+ }
|
|
|
|
|
|
-#if 0
|
|
|
- static const uint8_t read_sr_cmd[2] = { ROM_READ_SR1, ROM_READ_SR2 };
|
|
|
- uint8_t sr1;
|
|
|
- uint32_t *sfdp;
|
|
|
+ /* Read the current content of this block into vbuf */
|
|
|
+ rv = spiflash_read(spz->flash, addr, spz->vbuf, SPIFLASH_BLOCK_SIZE);
|
|
|
+ if (rv)
|
|
|
+ goto err;
|
|
|
|
|
|
- sfdp = malloc(SPIFLASH_SFDP_SIZE);
|
|
|
- memset(sfdp, 0, SPIFLASH_SFDP_SIZE);
|
|
|
+ /* Copy any invariant chunk */
|
|
|
+ if (pre_padding)
|
|
|
+ memcpy(spz->dbuf, spz->vbuf, pre_padding);
|
|
|
+ if (post_padding)
|
|
|
+ memcpy(spz->dbuf+SPIFLASH_BLOCK_SIZE-post_padding,
|
|
|
+ spz->vbuf+SPIFLASH_BLOCK_SIZE-post_padding,
|
|
|
+ post_padding);
|
|
|
+
|
|
|
+ rv = spiflash_read_data(spz, spz->dbuf+pre_padding, bytes);
|
|
|
+ if (rv != (int)bytes) {
|
|
|
+ MSG("needed %u bytes got %d\n", rv);
|
|
|
+ rv = Z_DATA_ERROR;
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
|
|
|
- /* Note: SFDP data is littleendian! */
|
|
|
- err = spiflash_get_sfdp(flash, sfdp);
|
|
|
- if (err)
|
|
|
- return err;
|
|
|
+ MSG("update: flash block at 0x%06x (%5u bytes):\n", addr, bytes);
|
|
|
|
|
|
- for (int i = 0; i < SPIFLASH_SFDP_SIZE; i += 16) {
|
|
|
- MSG("%04x :", i);
|
|
|
- for (int j = 0; j < 16; j += 4) {
|
|
|
- MSG(" %08x", sfdp[(i+j) >> 2]);
|
|
|
- }
|
|
|
- MSG("\n");
|
|
|
- }
|
|
|
-
|
|
|
- if (sfdp[0] != 0x50444653) {
|
|
|
- MSG("update: invalid SFDP information read\n");
|
|
|
- return SPIFLASH_ERR_DETECT;
|
|
|
- }
|
|
|
+ addr -= pre_padding;
|
|
|
|
|
|
- /*
|
|
|
- * If the flash is busy, try to reset it
|
|
|
- */
|
|
|
- err = spiflash_get_status(flash, ROM_READ_SR1, &sr1);
|
|
|
- if (err)
|
|
|
- return err;
|
|
|
- if (sr1 & 0x01) {
|
|
|
- udelay(60);
|
|
|
- err = spiflash_get_status(flash, ROM_READ_SR1, &sr1);
|
|
|
- if (err)
|
|
|
- return err;
|
|
|
- if (sr1 & 0x01) {
|
|
|
- MSG("update: flash busy, trying reset... ");
|
|
|
-
|
|
|
- err = spiflash_simple_command(flash, ROM_ENABLE_RESET);
|
|
|
- if (err)
|
|
|
- return err;
|
|
|
- err = spiflash_simple_command(flash, ROM_RESET);
|
|
|
- if (err)
|
|
|
- return err;
|
|
|
+ uint32_t erase_mask;
|
|
|
+ uint32_t prog_mask[SPIFLASH_BLOCK_SIZE >> (SPIFLASH_PAGE_SHIFT+5)];
|
|
|
|
|
|
- udelay(60);
|
|
|
- err = spiflash_get_status(flash, ROM_READ_SR1, &sr1);
|
|
|
- if (err || (sr1 & 0x01)) {
|
|
|
- MSG("failed\n");
|
|
|
- return SPIFLASH_ERR_NOT_READY;
|
|
|
+ spiflash_check_block(spz, addr, &erase_mask, prog_mask);
|
|
|
+ if (erase_mask) {
|
|
|
+ rv = spiflash_erase(spz->flash, addr, erase_mask);
|
|
|
+ if (rv)
|
|
|
+ goto err;
|
|
|
+
|
|
|
+ /* Verify that the sector did erase */
|
|
|
+ rv = spiflash_read(spz->flash, addr, spz->vbuf, SPIFLASH_BLOCK_SIZE);
|
|
|
+ if (rv)
|
|
|
+ goto err;
|
|
|
+
|
|
|
+ spiflash_check_block(spz, addr, &erase_mask, prog_mask);
|
|
|
+ if (erase_mask) {
|
|
|
+ MSG("[erase mask = %04x] ", erase_mask);
|
|
|
+ spz->err = SPIFLASH_ERR_ERASE_FAILED;
|
|
|
+ goto err;
|
|
|
}
|
|
|
- MSG("ok\n");
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
-#endif
|
|
|
-
|
|
|
- while (!err) {
|
|
|
- int rv;
|
|
|
- uint32_t addr;
|
|
|
- uint32_t data_left;
|
|
|
+ unsigned int page;
|
|
|
+ bool programmed = false;
|
|
|
|
|
|
- memset(spz, 0, sizeof *spz);
|
|
|
- spz->zs.avail_in = buflen;
|
|
|
- spz->zs.next_in = buf;
|
|
|
- spz->flash = flash;
|
|
|
+ for (page = 0; page < (SPIFLASH_BLOCK_SIZE >> SPIFLASH_PAGE_SHIFT);
|
|
|
+ page++) {
|
|
|
+ uint32_t page_offs = page << SPIFLASH_PAGE_SHIFT;
|
|
|
|
|
|
- if (spiflash_data_init(spz))
|
|
|
- goto err;
|
|
|
+ if (!(prog_mask[page >> 5] & (UINT32_C(1) << (page & 31))))
|
|
|
+ continue; /* No need to program */
|
|
|
+
|
|
|
+ programmed = true;
|
|
|
+
|
|
|
+ udelay(100);
|
|
|
+ MSG("\rupdate: writing at 0x%06x... ", addr + page_offs);
|
|
|
|
|
|
- if (!spz->ibuf) {
|
|
|
- /* No ibuf allocated, feeding from raw data buffer */
|
|
|
- unsigned int bufskip = (spz->header->zlen + sizeof *spz->header + 3) & ~3;
|
|
|
- if (bufskip >= buflen) {
|
|
|
- buflen = 0;
|
|
|
- buf = NULL;
|
|
|
- } else {
|
|
|
- buflen -= bufskip;
|
|
|
- buf += bufskip;
|
|
|
+ rv = spiflash_program(spz->flash, addr + page_offs,
|
|
|
+ spz->dbuf + page_offs,
|
|
|
+ SPIFLASH_PAGE_SIZE);
|
|
|
+ if (rv)
|
|
|
+ goto err;
|
|
|
+
|
|
|
+ /* Verify that the page did write */
|
|
|
+ rv = spiflash_read(spz->flash, addr + page_offs,
|
|
|
+ spz->vbuf + page_offs,
|
|
|
+ SPIFLASH_PAGE_SIZE);
|
|
|
+ if (rv) {
|
|
|
+ MSG("readback ");
|
|
|
+ goto err;
|
|
|
}
|
|
|
- } else {
|
|
|
- /* Buffer exhausted, additional data read */
|
|
|
- buflen = 0;
|
|
|
- buf = NULL;
|
|
|
- }
|
|
|
|
|
|
- data_left = spz->header->dlen;
|
|
|
- addr = spz->header->address;
|
|
|
+ if (memcmp(spz->dbuf + page_offs, spz->vbuf + page_offs,
|
|
|
+ SPIFLASH_PAGE_SIZE)) {
|
|
|
+ MSG("verify @ 0x%06x ", addr + page_offs);
|
|
|
+ spz->err = SPIFLASH_ERR_PROGRAM_FAILED;
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (programmed)
|
|
|
+ MSG("ok\n");
|
|
|
+ else
|
|
|
+ MSG("update: nothing to write\n");
|
|
|
+
|
|
|
+ addr += pre_padding + bytes;
|
|
|
+ data_left -= bytes;
|
|
|
+ }
|
|
|
+ return spz->err;
|
|
|
|
|
|
- while (data_left && !spz->err) {
|
|
|
- unsigned int bytes = 0;
|
|
|
- unsigned int padding;
|
|
|
-
|
|
|
- while (data_left && bytes < SPIFLASH_BLOCK_SIZE) {
|
|
|
- unsigned int avail = spz->zs.next_out - spz->optr;
|
|
|
- unsigned int need = SPIFLASH_BLOCK_SIZE - bytes;
|
|
|
- int rv;
|
|
|
+err:
|
|
|
+ if (!spz->err)
|
|
|
+ spz->err = rv;
|
|
|
|
|
|
- if (need > data_left)
|
|
|
- need = data_left;
|
|
|
-
|
|
|
- if (avail) {
|
|
|
- if (avail > need)
|
|
|
- avail = need;
|
|
|
-
|
|
|
- memcpy(spz->dbuf + bytes, spz->optr, avail);
|
|
|
- spz->optr += avail;
|
|
|
- bytes += avail;
|
|
|
- data_left -= avail;
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- rv = spz->read_data(spz);
|
|
|
+ return spz->err;
|
|
|
+}
|
|
|
|
|
|
- if (spz->err)
|
|
|
- goto err;
|
|
|
+/* Serial Flash Discoverable Parameter Table, see JESD216 */
|
|
|
+static int spiflash_get_sfdp(const struct spiflash *flash, void *sfdp)
|
|
|
+{
|
|
|
+ static const uint8_t cmd_read_sfdp[] = { ROM_READ_SFDP, 0, 0, 0, 0 };
|
|
|
|
|
|
- if (!rv) {
|
|
|
- spz->err = Z_STREAM_ERROR; /* Input underrun */
|
|
|
- goto err;
|
|
|
- }
|
|
|
- }
|
|
|
+ return flash->ops->spi_read(flash->cookie, cmd_read_sfdp,
|
|
|
+ sizeof cmd_read_sfdp, sfdp, SPIFLASH_SFDP_SIZE,
|
|
|
+ flash->param->tshsl);
|
|
|
+}
|
|
|
|
|
|
- if (bytes < SPIFLASH_BLOCK_SIZE) {
|
|
|
- memset(spz->dbuf + bytes, 0xff, SPIFLASH_BLOCK_SIZE - bytes);
|
|
|
- }
|
|
|
+static void *spiflash_read_chunk_str(spz_stream *spz)
|
|
|
+{
|
|
|
+ int rv;
|
|
|
+
|
|
|
+ if (spz->header.len >= SPIFLASH_BLOCK_SIZE) {
|
|
|
+ spz->err = Z_DATA_ERROR;
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
|
|
|
- MSG("update: flash block at 0x%06x (%5u bytes):\n", addr, bytes);
|
|
|
+ rv = spiflash_read_data(spz, spz->dbuf, spz->header.len);
|
|
|
+ if (spz->err) {
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+ if (rv != (int)spz->header.len) {
|
|
|
+ spz->err = Z_DATA_ERROR;
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+ spz->dbuf[spz->header.len] = '\0';
|
|
|
+ return spz->dbuf;
|
|
|
+}
|
|
|
|
|
|
- uint32_t erase_mask;
|
|
|
- uint32_t prog_mask[SPIFLASH_BLOCK_SIZE >> (SPIFLASH_PAGE_SHIFT+5)];
|
|
|
+/* Skip a data chunk */
|
|
|
+static int spiflash_skip_chunk(spz_stream *spz)
|
|
|
+{
|
|
|
+ unsigned int skip = spz->header.len;
|
|
|
+
|
|
|
+ while (skip) {
|
|
|
+ unsigned int block = min(skip, SPIFLASH_BLOCK_SIZE);
|
|
|
+ int rv = spiflash_read_data(spz, spz->dbuf, block);
|
|
|
+ if (spz->err)
|
|
|
+ return spz->err;
|
|
|
+ if (rv != (int)block) {
|
|
|
+ return spz->err = Z_DATA_ERROR;
|
|
|
+ }
|
|
|
+ skip -= block;
|
|
|
+ }
|
|
|
|
|
|
- rv = spiflash_check_block(spz, addr, &erase_mask, prog_mask);
|
|
|
- if (rv)
|
|
|
- goto err;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
- if (erase_mask) {
|
|
|
- rv = spiflash_erase(spz->flash, addr, erase_mask);
|
|
|
- if (rv) {
|
|
|
- spz->err = rv;
|
|
|
- goto err;
|
|
|
- }
|
|
|
+/* Process a data chunk; return a nonzero value if done */
|
|
|
+static int spiflash_process_chunk(spz_stream *spz)
|
|
|
+{
|
|
|
+ int rv;
|
|
|
+ char *str;
|
|
|
|
|
|
- rv = spiflash_check_block(spz, addr, &erase_mask, prog_mask);
|
|
|
- if (spz->err)
|
|
|
- goto err;
|
|
|
- if (erase_mask) {
|
|
|
- MSG("[erase mask = %04x] ", erase_mask);
|
|
|
- spz->err = SPIFLASH_ERR_ERASE_FAILED;
|
|
|
- goto err;
|
|
|
- }
|
|
|
- }
|
|
|
+ rv = spiflash_read_data(spz, &spz->header, sizeof spz->header);
|
|
|
+ if (spz->err)
|
|
|
+ return spz->err;
|
|
|
+ else if (!rv)
|
|
|
+ return Z_STREAM_END;
|
|
|
+ else if (rv != sizeof spz->header)
|
|
|
+ return spz->err = Z_STREAM_ERROR;
|
|
|
+
|
|
|
+ if (spz->header.magic != SPIFLASH_MAGIC) {
|
|
|
+ MSG("update: bad chunk header magic 0x%08x\n", spz->header.magic);
|
|
|
+ return spz->err = Z_DATA_ERROR;
|
|
|
+ }
|
|
|
|
|
|
- unsigned int page;
|
|
|
- bool programmed = false;
|
|
|
+ switch (spz->header.type) {
|
|
|
+ case FDT_END:
|
|
|
+ return Z_STREAM_END; /* End of data - not an error */
|
|
|
+ case FDT_DATA:
|
|
|
+ if (!spz->flash->ops)
|
|
|
+ return spiflash_skip_chunk(spz);
|
|
|
+ else
|
|
|
+ return spiflash_flash_chunk(spz);
|
|
|
+ case FDT_TARGET:
|
|
|
+ str = spiflash_read_chunk_str(spz);
|
|
|
+ if (!str || strcmp(str, spz->flash->target)) {
|
|
|
+ MSG("update: this firmware file targets \"%s\", need \"%s\"\n",
|
|
|
+ str, spz->flash->target);
|
|
|
+ return spz->err = Z_DATA_ERROR;
|
|
|
+ }
|
|
|
+ return Z_OK;
|
|
|
+ case FDT_NOTE:
|
|
|
+ str = spiflash_read_chunk_str(spz);
|
|
|
+ MSG("update: %s\n", str);
|
|
|
+ return Z_OK;
|
|
|
+ default:
|
|
|
+ if (spz->header.flags & FDF_OPTIONAL) {
|
|
|
+ return spiflash_skip_chunk(spz);
|
|
|
+ } else {
|
|
|
+ MSG("update: unknown chunk type: %u\n", spz->header.type);
|
|
|
+ return spz->err = Z_DATA_ERROR;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- for (page = 0; page < (SPIFLASH_BLOCK_SIZE >> SPIFLASH_PAGE_SHIFT);
|
|
|
- page++) {
|
|
|
- uint32_t page_offs = page << SPIFLASH_PAGE_SHIFT;
|
|
|
+int spiflash_flash_file(const struct spiflash *flash, void *buf, size_t buflen)
|
|
|
+{
|
|
|
+ spz_stream _spz;
|
|
|
+ spz_stream * const spz = &_spz; /* For consistency in notation */
|
|
|
+ int err = 0;
|
|
|
|
|
|
- if (!(prog_mask[page >> 5] & (UINT32_C(1) << (page & 31))))
|
|
|
- continue; /* No need to program */
|
|
|
+ memset(spz, 0, sizeof *spz);
|
|
|
+ spz->zs.avail_in = buflen;
|
|
|
+ spz->zs.next_in = buf;
|
|
|
+ spz->flash = flash;
|
|
|
|
|
|
- programmed = true;
|
|
|
+ err = spiflash_data_init(spz);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
|
|
|
- udelay(10000);
|
|
|
-
|
|
|
- MSG("\rupdate: writing at 0x%06x... ", addr + page_offs);
|
|
|
+ if (0 && flash->ops) {
|
|
|
+ static const uint8_t read_sr_cmd[2] = { ROM_READ_SR1, ROM_READ_SR2 };
|
|
|
+ uint8_t sr1;
|
|
|
+ uint32_t *sfdp;
|
|
|
|
|
|
- rv = spiflash_program(spz->flash, addr + page_offs,
|
|
|
- spz->dbuf + page_offs,
|
|
|
- SPIFLASH_PAGE_SIZE);
|
|
|
- if (rv) {
|
|
|
- spz->err = rv;
|
|
|
- goto err;
|
|
|
- }
|
|
|
+ sfdp = malloc(SPIFLASH_SFDP_SIZE);
|
|
|
+ memset(sfdp, 0, SPIFLASH_SFDP_SIZE);
|
|
|
|
|
|
- rv = spiflash_read(spz->flash, addr + page_offs,
|
|
|
- spz->vbuf + page_offs,
|
|
|
- SPIFLASH_PAGE_SIZE);
|
|
|
- if (rv) {
|
|
|
- MSG("readback ");
|
|
|
- goto err;
|
|
|
- }
|
|
|
+ /* Note: SFDP data is littleendian! */
|
|
|
+ err = spiflash_get_sfdp(flash, sfdp);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
|
|
|
+ for (int i = 0; i < SPIFLASH_SFDP_SIZE; i += 16) {
|
|
|
+ MSG("%04x :", i);
|
|
|
+ for (int j = 0; j < 16; j += 4) {
|
|
|
+ MSG(" %08x", sfdp[(i+j) >> 2]);
|
|
|
+ }
|
|
|
+ MSG("\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (sfdp[0] != 0x50444653) {
|
|
|
+ MSG("update: invalid SFDP information read\n");
|
|
|
+ return SPIFLASH_ERR_DETECT;
|
|
|
+ }
|
|
|
|
|
|
- if (memcmp(spz->dbuf + page_offs, spz->vbuf + page_offs,
|
|
|
- SPIFLASH_PAGE_SIZE)) {
|
|
|
- MSG("verify @ 0x%06x ", addr + page_offs);
|
|
|
- spz->err = SPIFLASH_ERR_PROGRAM_FAILED;
|
|
|
- goto err;
|
|
|
+ /*
|
|
|
+ * If the flash is busy, try to reset it
|
|
|
+ */
|
|
|
+ err = spiflash_get_status(flash, ROM_READ_SR1, &sr1);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ if (sr1 & 0x01) {
|
|
|
+ udelay(60);
|
|
|
+ err = spiflash_get_status(flash, ROM_READ_SR1, &sr1);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ if (sr1 & 0x01) {
|
|
|
+ MSG("update: flash busy, trying reset... ");
|
|
|
+
|
|
|
+ err = spiflash_simple_command(flash, ROM_ENABLE_RESET);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ err = spiflash_simple_command(flash, ROM_RESET);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ udelay(60);
|
|
|
+ err = spiflash_get_status(flash, ROM_READ_SR1, &sr1);
|
|
|
+ if (err || (sr1 & 0x01)) {
|
|
|
+ MSG("failed\n");
|
|
|
+ return SPIFLASH_ERR_NOT_READY;
|
|
|
}
|
|
|
- }
|
|
|
- if (programmed)
|
|
|
MSG("ok\n");
|
|
|
- else
|
|
|
- MSG("update: nothing to write\n");
|
|
|
-
|
|
|
- addr += SPIFLASH_BLOCK_SIZE;
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
- err:
|
|
|
- err = spiflash_data_cleanup(spz);
|
|
|
}
|
|
|
|
|
|
- if (err == Z_STREAM_END)
|
|
|
- err = 0; /* End of data is not an error */
|
|
|
+ while (!spiflash_process_chunk(spz)) {
|
|
|
+ /* Process data chunks until end */
|
|
|
+ }
|
|
|
|
|
|
+ err = spiflash_data_cleanup(spz);
|
|
|
if (err)
|
|
|
MSG("failed (err %d)\n", err);
|
|
|
|
|
@@ -827,7 +717,3 @@ int spiflash_read_vdid(const struct spiflash *flash, void *vdid)
|
|
|
sizeof read_vdid,
|
|
|
vdid, SPIFLASH_VDID_LEN, flash->param->tshsl);
|
|
|
}
|
|
|
-
|
|
|
-/*
|
|
|
- * Flash data from memory buffer(s)
|
|
|
- */
|