It is my first patch, so any comment are really welcome. Changes: * Removed unused variable * Added support for n25q256a and n25q512a * Added support for 4bytes address mode * Added support for banked read mode * Added support for sw reset flash commands * Added Read Flag Status register command support
Signed-off-by: Marcin Krzeminski <marcin.krzemin...@nokia.com> --- hw/block/m25p80.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 88 insertions(+), 6 deletions(-) diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index efc43dd..c8b92d8 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -47,6 +47,9 @@ */ #define WR_1 0x100 +/* 16 MiB max in 3 byte address mode */ +#define MAX_3BYTES_SIZE 0x1000000 + typedef struct FlashPartInfo { const char *part_name; /* jedec code. (jedec >> 16) & 0xff is the 1st byte, >> 8 the 2nd etc */ @@ -206,6 +209,8 @@ static const FlashPartInfo known_devices[] = { /* Numonyx -- n25q128 */ { 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) }, }; typedef enum { @@ -216,6 +221,7 @@ typedef enum { WREN = 0x6, JEDEC_READ = 0x9f, BULK_ERASE = 0xc7, + READ_FSL = 0x70, READ = 0x3, FAST_READ = 0xb, @@ -231,6 +237,15 @@ typedef enum { ERASE_4K = 0x20, ERASE_32K = 0x52, ERASE_SECTOR = 0xd8, + + ENTER_4BYTE_ADDR_MODE = 0xB7, + LEAVE_4BYTE_ADDR_MODE = 0xE9, + + EXTEND_ADDR_READ = 0xC8, + EXTEND_ADDR_WRITE = 0xC5, + + RESET_ENABLE = 0x66, + RESET_MEMORY = 0x99, } FlashCMD; typedef enum { @@ -244,8 +259,6 @@ typedef enum { typedef struct Flash { SSISlave parent_obj; - uint32_t r; - BlockBackend *blk; uint8_t *storage; @@ -260,6 +273,9 @@ typedef struct Flash { uint8_t cmd_in_progress; uint64_t cur_addr; bool write_enable; + bool four_bytes_address_mode; + bool reset_enable; + uint8_t extended_addr_reg; int64_t dirty_page; @@ -397,9 +413,17 @@ void flash_write8(Flash *s, uint64_t addr, uint8_t data) static void complete_collecting_data(Flash *s) { - s->cur_addr = s->data[0] << 16; - s->cur_addr |= s->data[1] << 8; - s->cur_addr |= s->data[2]; + if (s->four_bytes_address_mode) { + s->cur_addr = s->data[0] << 24; + s->cur_addr |= s->data[1] << 16; + s->cur_addr |= s->data[2] << 8; + s->cur_addr |= s->data[3]; + } else { + s->cur_addr = s->data[0] << 16; + s->cur_addr |= s->data[1] << 8; + s->cur_addr |= s->data[2]; + s->cur_addr += (s->extended_addr_reg&0x3)*MAX_3BYTES_SIZE; + } s->state = STATE_IDLE; @@ -427,11 +452,28 @@ static void complete_collecting_data(Flash *s) s->write_enable = false; } break; + case EXTEND_ADDR_WRITE: + s->extended_addr_reg = s->data[0]; + break; default: break; } } +static void reset_memory(Flash *s) +{ + s->cmd_in_progress = NOP; + s->cur_addr = 0; + s->extended_addr_reg = 0; + s->four_bytes_address_mode = false; + s->len = 0; + s->needed_bytes = 0; + s->pos = 0; + s->state = STATE_IDLE; + s->write_enable = false; + s->reset_enable = false; +} + static void decode_new_cmd(Flash *s, uint32_t value) { s->cmd_in_progress = value; @@ -446,7 +488,7 @@ static void decode_new_cmd(Flash *s, uint32_t value) case DPP: case QPP: case PP: - s->needed_bytes = 3; + s->needed_bytes = s->four_bytes_address_mode ? 4 : 3; s->pos = 0; s->len = 0; s->state = STATE_COLLECTING_DATA; @@ -514,6 +556,13 @@ static void decode_new_cmd(Flash *s, uint32_t value) s->state = STATE_READING_DATA; break; + case READ_FSL: + s->data[0] = (1<<7); /*Indicates flash is ready */ + s->pos = 0; + s->len = 1; + s->state = STATE_READING_DATA; + break; + case JEDEC_READ: DB_PRINT_L(0, "populated jedec code\n"); s->data[0] = (s->pi->jedec >> 16) & 0xff; @@ -541,6 +590,34 @@ static void decode_new_cmd(Flash *s, uint32_t value) break; case NOP: break; + case ENTER_4BYTE_ADDR_MODE: + s->four_bytes_address_mode = true; + break; + case LEAVE_4BYTE_ADDR_MODE: + s->four_bytes_address_mode = false; + break; + case EXTEND_ADDR_READ: + s->data[0] = s->extended_addr_reg; + s->pos = 0; + s->len = 1; + s->state = STATE_READING_DATA; + break; + case EXTEND_ADDR_WRITE: + 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; + case RESET_MEMORY: + if (s->reset_enable) { + reset_memory(s); + } + break; default: qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Unknown cmd %x\n", value); break; @@ -622,6 +699,8 @@ static int m25p80_init(SSISlave *ss) s->size = s->pi->sector_size * s->pi->n_sectors; s->dirty_page = -1; + reset_memory(s); + /* FIXME use a qdev drive property instead of drive_get_next() */ dinfo = drive_get_next(IF_MTD); @@ -666,6 +745,9 @@ static const VMStateDescription vmstate_m25p80 = { VMSTATE_UINT8(cmd_in_progress, Flash), VMSTATE_UINT64(cur_addr, Flash), VMSTATE_BOOL(write_enable, Flash), + VMSTATE_BOOL(four_bytes_address_mode, Flash), + VMSTATE_UINT8(extended_addr_reg, Flash), + VMSTATE_BOOL(reset_enable, Flash), VMSTATE_END_OF_LIST() } }; -- 1.9.1 Regards, Marcin Krzeminski