|  | @@ -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)
 | 
	
		
			
				|  |  | - */
 |