All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC v2] m25p80: Implement Octal SPI commands
@ 2023-03-01 13:18 Anton Kochkov
  0 siblings, 0 replies; only message in thread
From: Anton Kochkov @ 2023-03-01 13:18 UTC (permalink / raw)
  To: qemu-devel
  Cc: Anton Kochkov, Alistair Francis, Kevin Wolf, Hanna Reitz, qemu-block

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




^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2023-03-01 13:19 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-03-01 13:18 [RFC v2] m25p80: Implement Octal SPI commands Anton Kochkov

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.