#include "hw/ssi/ssi.h"
#include "qemu/bitops.h"
#include "qemu/log.h"
+#include "qemu/error-report.h"
#include "qapi/error.h"
#ifndef M25P80_ERR_DEBUG
uint32_t n_sectors;
uint32_t page_size;
uint16_t flags;
+ /*
+ * Big sized spi nor are often stacked devices, thus sometime
+ * replace chip erase with die erase.
+ * This field inform how many die is in the chip.
+ */
+ uint8_t die_cnt;
} FlashPartInfo;
/* adapted from linux */
.sector_size = (_sector_size),\
.n_sectors = (_n_sectors),\
.page_size = 256,\
- .flags = (_flags),
+ .flags = (_flags),\
+ .die_cnt = 0
#define INFO6(_part_name, _jedec_id, _ext_id, _sector_size, _n_sectors, _flags)\
.part_name = _part_name,\
.n_sectors = (_n_sectors),\
.page_size = 256,\
.flags = (_flags),\
+ .die_cnt = 0
+
+#define INFO_STACKED(_part_name, _jedec_id, _ext_id, _sector_size, _n_sectors,\
+ _flags, _die_cnt)\
+ .part_name = _part_name,\
+ .id = {\
+ ((_jedec_id) >> 16) & 0xff,\
+ ((_jedec_id) >> 8) & 0xff,\
+ (_jedec_id) & 0xff,\
+ ((_ext_id) >> 8) & 0xff,\
+ (_ext_id) & 0xff,\
+ },\
+ .id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))),\
+ .sector_size = (_sector_size),\
+ .n_sectors = (_n_sectors),\
+ .page_size = 256,\
+ .flags = (_flags),\
+ .die_cnt = _die_cnt
#define JEDEC_NUMONYX 0x20
#define JEDEC_WINBOND 0xEF
#define CFG_DUMMY_CLK_LEN 4
#define NVCFG_DUMMY_CLK_POS 12
#define VCFG_DUMMY_CLK_POS 4
-#define EVCFG_OUT_DRIVER_STRENGHT_DEF 7
+#define EVCFG_OUT_DRIVER_STRENGTH_DEF 7
#define EVCFG_VPP_ACCELERATOR (1 << 3)
#define EVCFG_RESET_HOLD_ENABLED (1 << 4)
#define NVCFG_DUAL_IO_MASK (1 << 2)
{ INFO("n25q128", 0x20ba18, 0, 64 << 10, 256, 0) },
{ INFO("n25q256a", 0x20ba19, 0, 64 << 10, 512, ER_4K) },
{ INFO("n25q512a", 0x20ba20, 0, 64 << 10, 1024, ER_4K) },
- { INFO("mt25ql01g", 0x20ba21, 0, 64 << 10, 2048, ER_4K) },
- { INFO("mt25qu01g", 0x20bb21, 0, 64 << 10, 2048, ER_4K) },
+ { INFO_STACKED("n25q00", 0x20ba21, 0x1000, 64 << 10, 2048, ER_4K, 4) },
+ { INFO_STACKED("n25q00a", 0x20bb21, 0x1000, 64 << 10, 2048, ER_4K, 4) },
+ { INFO_STACKED("mt25ql01g", 0x20ba21, 0x1040, 64 << 10, 2048, ER_4K, 2) },
+ { INFO_STACKED("mt25qu01g", 0x20bb21, 0x1040, 64 << 10, 2048, ER_4K, 2) },
/* Spansion -- single (large) sector size only, at least
* for the chips listed here (without boot sectors).
PP4_4 = 0x3e,
DPP = 0xa2,
QPP = 0x32,
+ QPP_4 = 0x34,
ERASE_4K = 0x20,
ERASE4_4K = 0x21,
REVCR = 0x65,
WEVCR = 0x61,
+
+ DIE_ERASE = 0xC4,
} FlashCMD;
typedef enum {
MAN_GENERIC,
} Manufacturer;
+#define M25P80_INTERNAL_DATA_BUFFER_SZ 16
+
typedef struct Flash {
SSISlave parent_obj;
int page_size;
uint8_t state;
- uint8_t data[16];
+ uint8_t data[M25P80_INTERNAL_DATA_BUFFER_SZ];
uint32_t len;
uint32_t pos;
uint8_t needed_bytes;
case BULK_ERASE:
len = s->size;
break;
+ case DIE_ERASE:
+ if (s->pi->die_cnt) {
+ len = s->size / s->pi->die_cnt;
+ offset = offset & (~(len - 1));
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "M25P80: die erase is not supported"
+ " by device\n");
+ return;
+ }
+ break;
default:
abort();
}
switch (s->cmd_in_progress) {
case PP4:
case PP4_4:
+ case QPP_4:
case READ4:
case QIOR4:
case ERASE4_4K:
switch (s->cmd_in_progress) {
case DPP:
case QPP:
+ case QPP_4:
case PP:
case PP4:
case PP4_4:
case ERASE4_32K:
case ERASE_SECTOR:
case ERASE4_SECTOR:
+ case DIE_ERASE:
flash_erase(s, s->cur_addr, s->cmd_in_progress);
break;
case WRSR:
);
s->enh_volatile_cfg = 0;
- s->enh_volatile_cfg |= EVCFG_OUT_DRIVER_STRENGHT_DEF;
+ s->enh_volatile_cfg |= EVCFG_OUT_DRIVER_STRENGTH_DEF;
s->enh_volatile_cfg |= EVCFG_VPP_ACCELERATOR;
s->enh_volatile_cfg |= EVCFG_RESET_HOLD_ENABLED;
if (s->nonvolatile_cfg & NVCFG_DUAL_IO_MASK) {
case READ4:
case DPP:
case QPP:
+ case QPP_4:
case PP:
case PP4:
case PP4_4:
+ case DIE_ERASE:
s->needed_bytes = get_addr_length(s);
s->pos = 0;
s->len = 0;
case STATE_COLLECTING_DATA:
case STATE_COLLECTING_VAR_LEN_DATA:
+
+ if (s->len >= M25P80_INTERNAL_DATA_BUFFER_SZ) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "M25P80: Write overrun internal data buffer. "
+ "SPI controller (QEMU emulator or guest driver) "
+ "is misbehaving\n");
+ s->len = s->pos = 0;
+ s->state = STATE_IDLE;
+ break;
+ }
+
s->data[s->len] = (uint8_t)tx;
s->len++;
break;
case STATE_READING_DATA:
+
+ if (s->pos >= M25P80_INTERNAL_DATA_BUFFER_SZ) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "M25P80: Read overrun internal data buffer. "
+ "SPI controller (QEMU emulator or guest driver) "
+ "is misbehaving\n");
+ s->len = s->pos = 0;
+ s->state = STATE_IDLE;
+ break;
+ }
+
r = s->data[s->pos];
s->pos++;
if (s->pos == s->len) {
.pre_save = m25p80_pre_save,
.fields = (VMStateField[]) {
VMSTATE_UINT8(state, Flash),
- VMSTATE_UINT8_ARRAY(data, Flash, 16),
+ VMSTATE_UINT8_ARRAY(data, Flash, M25P80_INTERNAL_DATA_BUFFER_SZ),
VMSTATE_UINT32(len, Flash),
VMSTATE_UINT32(pos, Flash),
VMSTATE_UINT8(needed_bytes, Flash),