@@ -26,6 +26,7 @@
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
#include "hw/ssi/ssi.h"
+#include "qemu/bitops.h"
#ifndef M25P80_ERR_DEBUG
#define M25P80_ERR_DEBUG 0
@@ -245,6 +246,15 @@ typedef enum {
RESET_ENABLE = 0x66,
RESET_MEMORY = 0x99,
+
+ RNVCR = 0xB5,
+ WNVCR = 0xB1,
+
+ RVCR = 0x85,
+ WVCR = 0x81,
+
+ REVCR = 0x65,
+ WEVCR = 0x61,
} FlashCMD;
typedef enum {
@@ -271,6 +281,9 @@ typedef struct Flash {
uint8_t needed_bytes;
uint8_t cmd_in_progress;
uint64_t cur_addr;
+ uint32_t nonvolatile_cfg;
+ uint32_t volatile_cfg;
+ uint32_t enh_volatile_cfg;
bool write_enable;
bool four_bytes_address_mode;
bool reset_enable;
@@ -460,6 +473,15 @@ static void complete_collecting_data(Flash *s)
case EXTEND_ADDR_WRITE:
s->ear = s->data[0];
break;
+ case WNVCR:
+ s->nonvolatile_cfg = s->data[0] | (s->data[1] << 8);
+ break;
+ case WVCR:
+ s->volatile_cfg = s->data[0];
+ break;
+ case WEVCR:
+ s->enh_volatile_cfg = s->data[0];
+ break;
default:
break;
}
@@ -618,6 +640,49 @@ static void decode_new_cmd(Flash *s, uint32_t value)
s->state = STATE_COLLECTING_DATA;
}
break;
+ case RNVCR:
+ s->data[0] = s->nonvolatile_cfg & 0xFF;
+ s->data[1] = (s->nonvolatile_cfg >> 8) & 0xFF;
+ s->pos = 0;
+ s->len = 1;
+ s->state = STATE_READING_DATA;
+ break;
+ case WNVCR:
+ if (s->write_enable) {
+ s->needed_bytes = 2;
+ s->pos = 0;
+ s->len = 0;
+ s->state = STATE_COLLECTING_DATA;
+ }
+ break;
+ case RVCR:
+ s->data[0] = s->volatile_cfg & 0xFF;
+ s->pos = 0;
+ s->len = 1;
+ s->state = STATE_READING_DATA;
+ break;
+ case WVCR:
+ if (s->write_enable) {
+ s->needed_bytes = 1;
+ s->pos = 0;
+ s->len = 0;
+ s->state = STATE_COLLECTING_DATA;
+ }
+ break;
+ case REVCR:
+ s->data[0] = s->enh_volatile_cfg & 0xFF;
+ s->pos = 0;
+ s->len = 1;
+ s->state = STATE_READING_DATA;
+ break;
+ case WEVCR:
+ if (s->write_enable) {
+ s->needed_bytes = 1;
+ s->pos = 0;
+ s->len = 0;
+ s->state = STATE_COLLECTING_DATA;
+ }
+ break;
case RESET_ENABLE:
s->reset_enable = true;
break;
@@ -710,6 +775,41 @@ static int m25p80_init(SSISlave *ss)
reset_memory(s);
+ s->volatile_cfg = 0;
+ /* WRAP & reserved*/
+ s->volatile_cfg |= 0x3;
+ /* XIP */
+ if (extract32(s->nonvolatile_cfg,9,3) != 0x7)
+ {
+ s->volatile_cfg |= (1 << 3);
+ }
+ /* Number of dummy cycles */
+ s->volatile_cfg |= deposit32(s->volatile_cfg,4,4,extract32(s->nonvolatile_cfg,12,4));
+
+ s->enh_volatile_cfg = 0;
+ /* Output driver strength */
+ s->enh_volatile_cfg |= 0x7;
+ /* Vpp accelerator */
+ s->enh_volatile_cfg |= (1 << 3);
+ /* Reset/hold & reserved */
+ s->enh_volatile_cfg |= (1 << 4);
+ /* Dual I/O protocol */
+ if ( (s->nonvolatile_cfg >> 1) & 0x1) {
+ s->enh_volatile_cfg |= (1 << 6);
+ }
+ /* Quad I/O protocol */
+ if ( (s->nonvolatile_cfg >> 3) & 0x1) {
+ s->enh_volatile_cfg |= (1 << 7);
+ }
+
+ if ( !(s->nonvolatile_cfg & 0x1)) {
+ s->four_bytes_address_mode = true;
+ }
+
+ if ( !((s->nonvolatile_cfg >> 1) & 0x1)) {
+ s->ear = 0x3;
+ }
+
/* FIXME use a qdev drive property instead of drive_get_next() */
dinfo = drive_get_next(IF_MTD);
@@ -740,6 +840,11 @@ static void m25p80_pre_save(void *opaque)
flash_sync_dirty((Flash *)opaque, -1);
}
+static Property m25p80_properties[] = {
+ DEFINE_PROP_UINT32("nonvolatile-cfg", Flash, nonvolatile_cfg, 0x8FFF),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static const VMStateDescription vmstate_m25p80 = {
.name = "xilinx_spi",
.version_id = 2,
@@ -757,6 +862,9 @@ static const VMStateDescription vmstate_m25p80 = {
VMSTATE_BOOL(four_bytes_address_mode, Flash),
VMSTATE_UINT8(ear, Flash),
VMSTATE_BOOL(reset_enable, Flash),
+ VMSTATE_UINT32(nonvolatile_cfg, Flash),
+ VMSTATE_UINT32(volatile_cfg, Flash),
+ VMSTATE_UINT32(enh_volatile_cfg, Flash),
VMSTATE_END_OF_LIST()
}
};
@@ -772,6 +880,7 @@ static void m25p80_class_init(ObjectClass *klass, void *data)
k->set_cs = m25p80_cs;
k->cs_polarity = SSI_CS_LOW;
dc->vmsd = &vmstate_m25p80;
+ dc->props = m25p80_properties;
mc->pi = data;
}