Due to a bug in pflash_read, we did not switch back to ROM mode when we should have. But simply fixing the inverted logic leaves us with slow word-wise flash programming when the guest verifies each write via a read.
For this reason, this patch establishes a 100 ms timer to trigger the ROM mode switch. It also fixes the code to start this sequence from all places that sets write cycle to 0. Signed-off-by: Jan Kiszka <jan.kis...@web.de> --- hw/pflash_cfi02.c | 42 ++++++++++++++++++++++++++++++++---------- 1 files changed, 32 insertions(+), 10 deletions(-) diff --git a/hw/pflash_cfi02.c b/hw/pflash_cfi02.c index 6bc58b4..3d5ea9a 100644 --- a/hw/pflash_cfi02.c +++ b/hw/pflash_cfi02.c @@ -66,7 +66,8 @@ struct pflash_t { uint16_t unlock_addr[2]; uint8_t cfi_len; uint8_t cfi_table[0x52]; - QEMUTimer *timer; + QEMUTimer *cmd_timer; + QEMUTimer *mode_timer; ram_addr_t off; int fl_mem; int rom_mode; @@ -82,12 +83,22 @@ static void pflash_register_memory(pflash_t *pfl, int rom_mode) phys_offset |= pfl->off | IO_MEM_ROMD; pfl->rom_mode = rom_mode; + DPRINTF("%s: rom_mode %d\n", __func__, rom_mode); for (i = 0; i < pfl->mappings; i++) cpu_register_physical_memory(pfl->base + i * pfl->chip_len, pfl->chip_len, phys_offset); } -static void pflash_timer (void *opaque) +static void pflash_start_rom_mode_timer(pflash_t *pfl) +{ + if (!pfl->rom_mode) { + /* Defer switch by 100 ms */ + qemu_mod_timer(pfl->mode_timer, + qemu_get_clock(vm_clock) + get_ticks_per_sec() / 10); + } +} + +static void pflash_cmd_timer(void *opaque) { pflash_t *pfl = opaque; @@ -97,12 +108,21 @@ static void pflash_timer (void *opaque) if (pfl->bypass) { pfl->wcycle = 2; } else { - pflash_register_memory(pfl, 1); + pflash_start_rom_mode_timer(pfl); pfl->wcycle = 0; } pfl->cmd = 0; } +static void pflash_mode_timer(void *opaque) +{ + pflash_t *pfl = opaque; + + if (!pfl->rom_mode && pfl->wcycle == 0) { + pflash_register_memory(pfl, 1); + } +} + static uint32_t pflash_read (pflash_t *pfl, target_phys_addr_t offset, int width, int be) { @@ -112,10 +132,9 @@ static uint32_t pflash_read (pflash_t *pfl, target_phys_addr_t offset, DPRINTF("%s: offset " TARGET_FMT_plx "\n", __func__, offset); ret = -1; - if (pfl->rom_mode) { - /* Lazy reset of to ROMD mode */ - if (pfl->wcycle == 0) - pflash_register_memory(pfl, 1); + if (pfl->wcycle == 0) { + /* Lazy switch to ROMD mode */ + pflash_start_rom_mode_timer(pfl); } offset &= pfl->chip_len - 1; boff = offset & 0xFF; @@ -127,6 +146,7 @@ static uint32_t pflash_read (pflash_t *pfl, target_phys_addr_t offset, default: /* This should never happen : reset state & treat it as a read*/ DPRINTF("%s: unknown command state: %x\n", __func__, pfl->cmd); + pflash_start_rom_mode_timer(pfl); pfl->wcycle = 0; pfl->cmd = 0; case 0x80: @@ -389,7 +409,7 @@ static void pflash_write (pflash_t *pfl, target_phys_addr_t offset, pfl->status = 0x00; pflash_update(pfl, 0, pfl->chip_len); /* Let's wait 5 seconds before chip erase is done */ - qemu_mod_timer(pfl->timer, + qemu_mod_timer(pfl->cmd_timer, qemu_get_clock(vm_clock) + (get_ticks_per_sec() * 5)); break; case 0x30: @@ -402,7 +422,7 @@ static void pflash_write (pflash_t *pfl, target_phys_addr_t offset, pflash_update(pfl, offset, pfl->sector_len); pfl->status = 0x00; /* Let's wait 1/2 second before sector erase is done */ - qemu_mod_timer(pfl->timer, + qemu_mod_timer(pfl->cmd_timer, qemu_get_clock(vm_clock) + (get_ticks_per_sec() / 2)); break; default: @@ -440,6 +460,7 @@ static void pflash_write (pflash_t *pfl, target_phys_addr_t offset, /* Reset flash */ reset_flash: + pflash_start_rom_mode_timer(pfl); pfl->bypass = 0; pfl->wcycle = 0; pfl->cmd = 0; @@ -647,7 +668,8 @@ pflash_t *pflash_cfi02_register(target_phys_addr_t base, ram_addr_t off, #else pfl->ro = 0; #endif - pfl->timer = qemu_new_timer(vm_clock, pflash_timer, pfl); + pfl->cmd_timer = qemu_new_timer(vm_clock, pflash_cmd_timer, pfl); + pfl->mode_timer = qemu_new_timer(vm_clock, pflash_mode_timer, pfl); pfl->sector_len = sector_len; pfl->width = width; pfl->wcycle = 0; -- 1.6.0.2