hw/block: m25p80: Implement AAI-WP command support for SST flashes
Auto Address Increment (AAI) Word-Program is a special command of SST flashes. AAI-WP allows multiple bytes of data to be programmed without re-issuing the next sequential address location. Signed-off-by: Xuzhou Cheng <xuzhou.cheng@windriver.com> Signed-off-by: Bin Meng <bin.meng@windriver.com> Reviewed-by: Francisco Iglesias <frasse.iglesias@gmail.com> Message-id: 1608688825-81519-2-git-send-email-bmeng.cn@gmail.com Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
This commit is contained in:
parent
1695854b37
commit
465ef47abe
|
@ -360,6 +360,7 @@ typedef enum {
|
||||||
QPP_4 = 0x34,
|
QPP_4 = 0x34,
|
||||||
RDID_90 = 0x90,
|
RDID_90 = 0x90,
|
||||||
RDID_AB = 0xab,
|
RDID_AB = 0xab,
|
||||||
|
AAI_WP = 0xad,
|
||||||
|
|
||||||
ERASE_4K = 0x20,
|
ERASE_4K = 0x20,
|
||||||
ERASE4_4K = 0x21,
|
ERASE4_4K = 0x21,
|
||||||
|
@ -456,6 +457,7 @@ struct Flash {
|
||||||
bool four_bytes_address_mode;
|
bool four_bytes_address_mode;
|
||||||
bool reset_enable;
|
bool reset_enable;
|
||||||
bool quad_enable;
|
bool quad_enable;
|
||||||
|
bool aai_enable;
|
||||||
uint8_t ear;
|
uint8_t ear;
|
||||||
|
|
||||||
int64_t dirty_page;
|
int64_t dirty_page;
|
||||||
|
@ -671,6 +673,11 @@ static void complete_collecting_data(Flash *s)
|
||||||
case PP4_4:
|
case PP4_4:
|
||||||
s->state = STATE_PAGE_PROGRAM;
|
s->state = STATE_PAGE_PROGRAM;
|
||||||
break;
|
break;
|
||||||
|
case AAI_WP:
|
||||||
|
/* AAI programming starts from the even address */
|
||||||
|
s->cur_addr &= ~BIT(0);
|
||||||
|
s->state = STATE_PAGE_PROGRAM;
|
||||||
|
break;
|
||||||
case READ:
|
case READ:
|
||||||
case READ4:
|
case READ4:
|
||||||
case FAST_READ:
|
case FAST_READ:
|
||||||
|
@ -769,6 +776,7 @@ static void reset_memory(Flash *s)
|
||||||
s->write_enable = false;
|
s->write_enable = false;
|
||||||
s->reset_enable = false;
|
s->reset_enable = false;
|
||||||
s->quad_enable = false;
|
s->quad_enable = false;
|
||||||
|
s->aai_enable = false;
|
||||||
|
|
||||||
switch (get_man(s)) {
|
switch (get_man(s)) {
|
||||||
case MAN_NUMONYX:
|
case MAN_NUMONYX:
|
||||||
|
@ -974,6 +982,11 @@ static void decode_qio_read_cmd(Flash *s)
|
||||||
s->state = STATE_COLLECTING_DATA;
|
s->state = STATE_COLLECTING_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool is_valid_aai_cmd(uint32_t cmd)
|
||||||
|
{
|
||||||
|
return cmd == AAI_WP || cmd == WRDI || cmd == RDSR;
|
||||||
|
}
|
||||||
|
|
||||||
static void decode_new_cmd(Flash *s, uint32_t value)
|
static void decode_new_cmd(Flash *s, uint32_t value)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
@ -985,6 +998,11 @@ static void decode_new_cmd(Flash *s, uint32_t value)
|
||||||
s->reset_enable = false;
|
s->reset_enable = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (get_man(s) == MAN_SST && s->aai_enable && !is_valid_aai_cmd(value)) {
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"M25P80: Invalid cmd within AAI programming sequence");
|
||||||
|
}
|
||||||
|
|
||||||
switch (value) {
|
switch (value) {
|
||||||
|
|
||||||
case ERASE_4K:
|
case ERASE_4K:
|
||||||
|
@ -1104,6 +1122,9 @@ static void decode_new_cmd(Flash *s, uint32_t value)
|
||||||
|
|
||||||
case WRDI:
|
case WRDI:
|
||||||
s->write_enable = false;
|
s->write_enable = false;
|
||||||
|
if (get_man(s) == MAN_SST) {
|
||||||
|
s->aai_enable = false;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case WREN:
|
case WREN:
|
||||||
s->write_enable = true;
|
s->write_enable = true;
|
||||||
|
@ -1114,6 +1135,10 @@ static void decode_new_cmd(Flash *s, uint32_t value)
|
||||||
if (get_man(s) == MAN_MACRONIX) {
|
if (get_man(s) == MAN_MACRONIX) {
|
||||||
s->data[0] |= (!!s->quad_enable) << 6;
|
s->data[0] |= (!!s->quad_enable) << 6;
|
||||||
}
|
}
|
||||||
|
if (get_man(s) == MAN_SST) {
|
||||||
|
s->data[0] |= (!!s->aai_enable) << 6;
|
||||||
|
}
|
||||||
|
|
||||||
s->pos = 0;
|
s->pos = 0;
|
||||||
s->len = 1;
|
s->len = 1;
|
||||||
s->data_read_loop = true;
|
s->data_read_loop = true;
|
||||||
|
@ -1261,6 +1286,24 @@ static void decode_new_cmd(Flash *s, uint32_t value)
|
||||||
case RSTQIO:
|
case RSTQIO:
|
||||||
s->quad_enable = false;
|
s->quad_enable = false;
|
||||||
break;
|
break;
|
||||||
|
case AAI_WP:
|
||||||
|
if (get_man(s) == MAN_SST) {
|
||||||
|
if (s->write_enable) {
|
||||||
|
if (s->aai_enable) {
|
||||||
|
s->state = STATE_PAGE_PROGRAM;
|
||||||
|
} else {
|
||||||
|
s->aai_enable = true;
|
||||||
|
s->needed_bytes = get_addr_length(s);
|
||||||
|
s->state = STATE_COLLECTING_DATA;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"M25P80: AAI_WP with write protect\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Unknown cmd %x\n", value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
s->pos = 0;
|
s->pos = 0;
|
||||||
s->len = 1;
|
s->len = 1;
|
||||||
|
@ -1306,6 +1349,17 @@ static uint32_t m25p80_transfer8(SSIPeripheral *ss, uint32_t tx)
|
||||||
trace_m25p80_page_program(s, s->cur_addr, (uint8_t)tx);
|
trace_m25p80_page_program(s, s->cur_addr, (uint8_t)tx);
|
||||||
flash_write8(s, s->cur_addr, (uint8_t)tx);
|
flash_write8(s, s->cur_addr, (uint8_t)tx);
|
||||||
s->cur_addr = (s->cur_addr + 1) & (s->size - 1);
|
s->cur_addr = (s->cur_addr + 1) & (s->size - 1);
|
||||||
|
|
||||||
|
if (get_man(s) == MAN_SST && s->aai_enable && s->cur_addr == 0) {
|
||||||
|
/*
|
||||||
|
* There is no wrap mode during AAI programming once the highest
|
||||||
|
* unprotected memory address is reached. The Write-Enable-Latch
|
||||||
|
* bit is automatically reset, and AAI programming mode aborts.
|
||||||
|
*/
|
||||||
|
s->write_enable = false;
|
||||||
|
s->aai_enable = false;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STATE_READ:
|
case STATE_READ:
|
||||||
|
@ -1451,6 +1505,24 @@ static const VMStateDescription vmstate_m25p80_data_read_loop = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static bool m25p80_aai_enable_needed(void *opaque)
|
||||||
|
{
|
||||||
|
Flash *s = (Flash *)opaque;
|
||||||
|
|
||||||
|
return s->aai_enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_m25p80_aai_enable = {
|
||||||
|
.name = "m25p80/aai_enable",
|
||||||
|
.version_id = 1,
|
||||||
|
.minimum_version_id = 1,
|
||||||
|
.needed = m25p80_aai_enable_needed,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_BOOL(aai_enable, Flash),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static const VMStateDescription vmstate_m25p80 = {
|
static const VMStateDescription vmstate_m25p80 = {
|
||||||
.name = "m25p80",
|
.name = "m25p80",
|
||||||
.version_id = 0,
|
.version_id = 0,
|
||||||
|
@ -1481,6 +1553,7 @@ static const VMStateDescription vmstate_m25p80 = {
|
||||||
},
|
},
|
||||||
.subsections = (const VMStateDescription * []) {
|
.subsections = (const VMStateDescription * []) {
|
||||||
&vmstate_m25p80_data_read_loop,
|
&vmstate_m25p80_data_read_loop,
|
||||||
|
&vmstate_m25p80_aai_enable,
|
||||||
NULL
|
NULL
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue