[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[RFC v2] m25p80: Implement Octal SPI commands
From: |
Anton Kochkov |
Subject: |
[RFC v2] m25p80: Implement Octal SPI commands |
Date: |
Wed, 01 Mar 2023 13:18:32 +0000 |
Signed-off-by: Anton Kochkov <anton.kochkov@proton.me>
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1148
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1149
---
hw/block/m25p80.c | 194 +++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 184 insertions(+), 10 deletions(-)
diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
index 02adc87527..79ab090ed9 100644
--- a/hw/block/m25p80.c
+++ b/hw/block/m25p80.c
@@ -129,6 +129,7 @@ typedef struct FlashPartInfo {
.die_cnt = _die_cnt
#define JEDEC_NUMONYX 0x20
+#define JEDEC_MICRON 0x2C
#define JEDEC_WINBOND 0xEF
#define JEDEC_SPANSION 0x01
@@ -151,6 +152,9 @@ typedef struct FlashPartInfo {
#define NVCFG_4BYTE_ADDR_MASK (1 << 0)
#define NVCFG_LOWER_SEGMENT_MASK (1 << 1)
+/* Micron configuration register macros */
+#define NVCFG_OCTAL_IO_MASK (1 << 5)
+
/* Numonyx (Micron) Flag Status Register macros */
#define FSR_4BYTE_ADDR_MODE_ENABLED 0x1
#define FSR_FLASH_READY (1 << 7)
@@ -372,6 +376,8 @@ typedef enum {
READ4 = 0x13,
FAST_READ = 0x0b,
FAST_READ4 = 0x0c,
+ O_FAST_READ = 0x9d,
+ O_FAST_READ4 = 0xfd,
DOR = 0x3b,
DOR4 = 0x3c,
QOR = 0x6b,
@@ -380,6 +386,11 @@ typedef enum {
DIOR4 = 0xbc,
QIOR = 0xeb,
QIOR4 = 0xec,
+ OOR = 0x8b,
+ OOR4 = 0x8c,
+ OOR4_MT35X = 0x7c, /* according mt35x datasheet */
+ OIOR = 0xcb,
+ OIOR4 = 0xcc,
PP = 0x02,
PP4 = 0x12,
@@ -388,8 +399,11 @@ typedef enum {
QPP = 0x32,
QPP_4 = 0x34,
RDID_90 = 0x90,
+ RDID_9E = 0x9E,
RDID_AB = 0xab,
AAI_WP = 0xad,
+ OPP = 0x82,
+ OPP4 = 0x84,
ERASE_4K = 0x20,
ERASE4_4K = 0x21,
@@ -440,6 +454,7 @@ typedef enum {
MAN_SPANSION,
MAN_MACRONIX,
MAN_NUMONYX,
+ MAN_MICRON,
MAN_WINBOND,
MAN_SST,
MAN_ISSI,
@@ -475,6 +490,9 @@ struct Flash {
/* Configuration register for Macronix */
uint32_t volatile_cfg;
uint32_t enh_volatile_cfg;
+ /* Configuration register arrays for Micron */
+ uint8_t micron_volatile_cfg[8];
+ uint8_t micron_nonvolatile_cfg[8];
/* Spansion cfg registers. */
uint8_t spansion_cr1nv;
uint8_t spansion_cr2nv;
@@ -489,6 +507,7 @@ struct Flash {
bool four_bytes_address_mode;
bool reset_enable;
bool quad_enable;
+ bool octal_enable;
bool aai_enable;
bool block_protect0;
bool block_protect1;
@@ -517,6 +536,8 @@ static inline Manufacturer get_man(Flash *s)
switch (s->pi->id[0]) {
case 0x20:
return MAN_NUMONYX;
+ case 0x2C:
+ return MAN_MICRON;
case 0xEF:
return MAN_WINBOND;
case 0x01:
@@ -697,14 +718,19 @@ static inline int get_addr_length(Flash *s)
case PP4:
case PP4_4:
case QPP_4:
+ case OPP4:
case READ4:
case QIOR4:
+ case OIOR4:
case ERASE4_4K:
case ERASE4_32K:
case ERASE4_SECTOR:
case FAST_READ4:
+ case O_FAST_READ4:
case DOR4:
case QOR4:
+ case OOR4:
+ case OOR4_MT35X:
case DIOR4:
return 4;
default:
@@ -734,7 +760,9 @@ static void complete_collecting_data(Flash *s)
case DPP:
case QPP:
case QPP_4:
+ case OPP:
case PP:
+ case OPP4:
case PP4:
case PP4_4:
s->state = STATE_PAGE_PROGRAM;
@@ -748,6 +776,7 @@ static void complete_collecting_data(Flash *s)
case READ4:
case FAST_READ:
case FAST_READ4:
+ case O_FAST_READ:
case DOR:
case DOR4:
case QOR:
@@ -756,6 +785,12 @@ static void complete_collecting_data(Flash *s)
case DIOR4:
case QIOR:
case QIOR4:
+ case OOR:
+ case OOR4:
+ case OOR4_MT35X:
+ case O_FAST_READ4:
+ case OIOR:
+ case OIOR4:
s->state = STATE_READ;
break;
case ERASE_4K:
@@ -804,11 +839,43 @@ static void complete_collecting_data(Flash *s)
case EXTEND_ADDR_WRITE:
s->ear = s->data[0];
break;
+ case RNVCR:
+ g_assert(get_man(s) == MAN_MICRON);
+ s->data[0] = s->micron_nonvolatile_cfg[s->cur_addr & 0xFF];
+ s->pos = 0;
+ s->len = 1;
+ s->state = STATE_READING_DATA;
+ s->data_read_loop = true;
+ break;
+ case RVCR:
+ g_assert(get_man(s) == MAN_MICRON);
+ s->data[0] = s->micron_volatile_cfg[s->cur_addr & 0xFF];
+ s->pos = 0;
+ s->len = 1;
+ s->state = STATE_READING_DATA;
+ s->data_read_loop = true;
+ break;
case WNVCR:
- s->nonvolatile_cfg = s->data[0] | (s->data[1] << 8);
+ if (get_man(s) == MAN_MICRON) {
+ if (s->cur_addr <= 7) {
+ s->micron_nonvolatile_cfg[s->cur_addr] =
+ s->data[get_addr_length(s)];
+ }
+ s->octal_enable = !(s->micron_nonvolatile_cfg[0] &
NVCFG_OCTAL_IO_MASK);
+ } else {
+ s->nonvolatile_cfg = s->data[0] | (s->data[1] << 8);
+ }
break;
case WVCR:
- s->volatile_cfg = s->data[0];
+ if (get_man(s) == MAN_MICRON) {
+ if (s->cur_addr <= 7) {
+ s->micron_volatile_cfg[s->cur_addr] =
+ s->data[get_addr_length(s)];
+ }
+ s->octal_enable = !(s->micron_volatile_cfg[0] &
NVCFG_OCTAL_IO_MASK);
+ } else {
+ s->volatile_cfg = s->data[0];
+ }
break;
case WEVCR:
s->enh_volatile_cfg = s->data[0];
@@ -861,6 +928,7 @@ static void reset_memory(Flash *s)
s->write_enable = false;
s->reset_enable = false;
s->quad_enable = false;
+ s->octal_enable = false;
s->aai_enable = false;
switch (get_man(s)) {
@@ -897,6 +965,13 @@ static void reset_memory(Flash *s)
s->ear = s->size / MAX_3BYTES_SIZE - 1;
}
break;
+ case MAN_MICRON:
+ s->micron_nonvolatile_cfg[0] = 0xe7;
+ s->micron_nonvolatile_cfg[1] = 0x1f;
+ if (!(s->micron_nonvolatile_cfg[0] & NVCFG_OCTAL_IO_MASK)) {
+ s->octal_enable = true;
+ }
+ break;
case MAN_MACRONIX:
s->volatile_cfg = 0x7;
break;
@@ -956,6 +1031,32 @@ static uint8_t numonyx_extract_cfg_num_dummies(Flash *s)
return num_dummies;
}
+static uint8_t micron_extract_cfg_num_dummies(Flash *s)
+{
+ uint8_t num_dummies;
+ uint8_t mode;
+ assert(get_man(s) == MAN_MICRON);
+
+ mode = numonyx_mode(s);
+ num_dummies = s->micron_volatile_cfg[1];
+
+ if (num_dummies == 0x0 || num_dummies == 0xf) {
+ switch (s->cmd_in_progress) {
+ case OIOR:
+ case OIOR4:
+ case QIOR:
+ case QIOR4:
+ num_dummies = 10;
+ break;
+ default:
+ num_dummies = (mode == MODE_QIO) ? 10 : 8;
+ break;
+ }
+ }
+
+ return num_dummies;
+}
+
static void decode_fast_read_cmd(Flash *s)
{
s->needed_bytes = get_addr_length(s);
@@ -970,6 +1071,9 @@ static void decode_fast_read_cmd(Flash *s)
case MAN_NUMONYX:
s->needed_bytes += numonyx_extract_cfg_num_dummies(s);
break;
+ case MAN_MICRON:
+ s->needed_bytes += micron_extract_cfg_num_dummies(s);
+ break;
case MAN_MACRONIX:
if (extract32(s->volatile_cfg, 6, 2) == 1) {
s->needed_bytes += 6;
@@ -1099,6 +1203,7 @@ static void decode_qio_read_cmd(Flash *s)
s->needed_bytes += 3;
break;
default:
+ s->needed_bytes += 5;
break;
}
s->pos = 0;
@@ -1106,6 +1211,14 @@ static void decode_qio_read_cmd(Flash *s)
s->state = STATE_COLLECTING_DATA;
}
+static void decode_oio_read_cmd(Flash *s)
+{
+ s->needed_bytes = get_addr_length(s);
+ s->pos = 0;
+ s->len = 0;
+ s->state = STATE_COLLECTING_DATA;
+}
+
static bool is_valid_aai_cmd(uint32_t cmd)
{
return cmd == AAI_WP || cmd == WRDI || cmd == RDSR;
@@ -1127,6 +1240,8 @@ static void decode_new_cmd(Flash *s, uint32_t value)
"M25P80: Invalid cmd within AAI programming sequence");
}
+ s->needed_bytes = 0;
+
switch (value) {
case ERASE_4K:
@@ -1215,6 +1330,9 @@ static void decode_new_cmd(Flash *s, uint32_t value)
}
break;
+ case OIOR4:
+ s->needed_bytes += 1;
+ /* fall through */
case QIOR:
case QIOR4:
if (get_man(s) != MAN_NUMONYX || numonyx_mode(s) != MODE_DIO) {
@@ -1225,6 +1343,20 @@ static void decode_new_cmd(Flash *s, uint32_t value)
}
break;
+ case OOR:
+ case OOR4:
+ case OOR4_MT35X:
+ case O_FAST_READ:
+ case OPP:
+ case OPP4:
+ if (get_man(s) == MAN_MICRON) {
+ decode_oio_read_cmd(s);
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Cannot execute cmd %x in "
+ "OIO mode\n", s->cmd_in_progress);
+ }
+ break;
+
case WRSR:
/*
* If WP# is low and status_register_write_disabled is high,
@@ -1303,6 +1435,7 @@ static void decode_new_cmd(Flash *s, uint32_t value)
s->state = STATE_READING_DATA;
break;
+ case RDID_9E:
case JEDEC_READ:
if (get_man(s) != MAN_NUMONYX || numonyx_mode(s) == MODE_STD) {
trace_m25p80_populated_jedec(s);
@@ -1365,29 +1498,51 @@ static void decode_new_cmd(Flash *s, uint32_t value)
}
break;
case RNVCR:
- s->data[0] = s->nonvolatile_cfg & 0xFF;
- s->data[1] = (s->nonvolatile_cfg >> 8) & 0xFF;
+ if (get_man(s) == MAN_MICRON) {
+ s->needed_bytes = get_addr_length(s);
+ s->state = STATE_COLLECTING_DATA;
+ s->len = 0;
+ } else {
+ s->data[0] = s->nonvolatile_cfg & 0xFF;
+ s->data[1] = (s->nonvolatile_cfg >> 8) & 0xFF;
+ s->len = 2;
+ }
s->pos = 0;
- s->len = 2;
s->state = STATE_READING_DATA;
break;
case WNVCR:
- if (s->write_enable && get_man(s) == MAN_NUMONYX) {
- s->needed_bytes = 2;
+ if (s->write_enable) {
+ if (get_man(s) == MAN_NUMONYX) {
+ s->needed_bytes = 2;
+ } else if (get_man(s) == MAN_MICRON) {
+ s->needed_bytes = 1;
+ s->needed_bytes += get_addr_length(s);
+ } else {
+ break;
+ }
s->pos = 0;
s->len = 0;
s->state = STATE_COLLECTING_DATA;
}
break;
case RVCR:
- s->data[0] = s->volatile_cfg & 0xFF;
+ if (get_man(s) == MAN_MICRON) {
+ s->needed_bytes = get_addr_length(s);
+ s->state = STATE_COLLECTING_DATA;
+ s->len = 0;
+ } else {
+ s->data[0] = s->volatile_cfg & 0xFF;
+ s->state = STATE_READING_DATA;
+ s->len = 1;
+ }
s->pos = 0;
- s->len = 1;
- s->state = STATE_READING_DATA;
break;
case WVCR:
if (s->write_enable) {
s->needed_bytes = 1;
+ if (get_man(s) == MAN_MICRON) {
+ s->needed_bytes += get_addr_length(s);
+ }
s->pos = 0;
s->len = 0;
s->state = STATE_COLLECTING_DATA;
@@ -1751,6 +1906,24 @@ static const VMStateDescription
vmstate_m25p80_block_protect = {
}
};
+static bool m25p80_octal_enable_needed(void *opaque)
+{
+ Flash *s = (Flash *)opaque;
+
+ return s->octal_enable;
+}
+
+static const VMStateDescription vmstate_m25p80_octal = {
+ .name = "m25p80/octal",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = m25p80_octal_enable_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_BOOL(octal_enable, Flash),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_m25p80 = {
.name = "m25p80",
.version_id = 0,
@@ -1784,6 +1957,7 @@ static const VMStateDescription vmstate_m25p80 = {
&vmstate_m25p80_aai_enable,
&vmstate_m25p80_write_protect,
&vmstate_m25p80_block_protect,
+ &vmstate_m25p80_octal,
NULL
}
};
--
2.39.2
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [RFC v2] m25p80: Implement Octal SPI commands,
Anton Kochkov <=