Patchwork [4/4] cfi02: Use timer-based ROM mode switch

login
register
mail settings
Submitter Jan Kiszka
Date May 13, 2010, 2:16 p.m.
Message ID <252a2d5a55f41b7ea53e5211f983084c5cb67c32.1273760202.git.jan.kiszka@web.de>
Download mbox | patch
Permalink /patch/52485/
State New
Headers show

Comments

Jan Kiszka - May 13, 2010, 2:16 p.m.
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.kiszka@web.de>
---
 hw/pflash_cfi02.c |   42 ++++++++++++++++++++++++++++++++----------
 1 files changed, 32 insertions(+), 10 deletions(-)

Patch

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;