diff options
Diffstat (limited to 'hw/block/m25p80.c')
| -rw-r--r-- | hw/block/m25p80.c | 85 |
1 files changed, 78 insertions, 7 deletions
diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index d29ff4cb4f..2d6eb46a04 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -28,6 +28,7 @@ #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 @@ -73,6 +74,12 @@ typedef struct FlashPartInfo { 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 */ @@ -90,7 +97,8 @@ typedef struct FlashPartInfo { .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,\ @@ -107,6 +115,24 @@ typedef struct FlashPartInfo { .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 @@ -121,7 +147,7 @@ typedef struct FlashPartInfo { #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) @@ -203,6 +229,7 @@ static const FlashPartInfo known_devices[] = { { INFO("mx25l25655e", 0xc22619, 0, 64 << 10, 512, 0) }, { INFO("mx66u51235f", 0xc2253a, 0, 64 << 10, 1024, ER_4K | ER_32K) }, { INFO("mx66u1g45g", 0xc2253b, 0, 64 << 10, 2048, ER_4K | ER_32K) }, + { INFO("mx66l1g45g", 0xc2201b, 0, 64 << 10, 2048, ER_4K | ER_32K) }, /* Micron */ { INFO("n25q032a11", 0x20bb16, 0, 64 << 10, 64, ER_4K) }, @@ -216,8 +243,10 @@ static const FlashPartInfo known_devices[] = { { 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). @@ -325,6 +354,7 @@ typedef enum { PP4_4 = 0x3e, DPP = 0xa2, QPP = 0x32, + QPP_4 = 0x34, ERASE_4K = 0x20, ERASE4_4K = 0x21, @@ -357,6 +387,8 @@ typedef enum { REVCR = 0x65, WEVCR = 0x61, + + DIE_ERASE = 0xC4, } FlashCMD; typedef enum { @@ -376,6 +408,8 @@ typedef enum { MAN_GENERIC, } Manufacturer; +#define M25P80_INTERNAL_DATA_BUFFER_SZ 16 + typedef struct Flash { SSISlave parent_obj; @@ -386,7 +420,7 @@ typedef struct Flash { 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; @@ -512,6 +546,16 @@ static void flash_erase(Flash *s, int offset, FlashCMD cmd) 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(); } @@ -573,6 +617,7 @@ static inline int get_addr_length(Flash *s) switch (s->cmd_in_progress) { case PP4: case PP4_4: + case QPP_4: case READ4: case QIOR4: case ERASE4_4K: @@ -606,6 +651,7 @@ static void complete_collecting_data(Flash *s) switch (s->cmd_in_progress) { case DPP: case QPP: + case QPP_4: case PP: case PP4: case PP4_4: @@ -631,6 +677,7 @@ static void complete_collecting_data(Flash *s) 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: @@ -700,7 +747,7 @@ static void reset_memory(Flash *s) ); 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) { @@ -873,9 +920,11 @@ static void decode_new_cmd(Flash *s, uint32_t value) 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; @@ -1114,6 +1163,17 @@ static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx) 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++; @@ -1123,6 +1183,17 @@ static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx) 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) { @@ -1195,7 +1266,7 @@ static const VMStateDescription vmstate_m25p80 = { .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), |