diff options
Diffstat (limited to 'hw/block/pflash_cfi02.c')
-rw-r--r-- | hw/block/pflash_cfi02.c | 316 |
1 files changed, 160 insertions, 156 deletions
diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c index 7962cff745..25c053693c 100644 --- a/hw/block/pflash_cfi02.c +++ b/hw/block/pflash_cfi02.c @@ -48,14 +48,6 @@ #include "migration/vmstate.h" #include "trace.h" -#define PFLASH_DEBUG false -#define DPRINTF(fmt, ...) \ -do { \ - if (PFLASH_DEBUG) { \ - fprintf(stderr, "PFLASH: " fmt, ## __VA_ARGS__); \ - } \ -} while (0) - #define PFLASH_LAZY_ROMD_THRESHOLD 42 /* @@ -100,14 +92,15 @@ struct PFlashCFI02 { uint16_t unlock_addr1; uint8_t cfi_table[0x4d]; QEMUTimer timer; - /* The device replicates the flash memory across its memory space. Emulate + /* + * The device replicates the flash memory across its memory space. Emulate * that by having a container (.mem) filled with an array of aliases * (.mem_mappings) pointing to the flash memory (.orig_mem). */ MemoryRegion mem; MemoryRegion *mem_mappings; /* array; one per mapping */ MemoryRegion orig_mem; - int rom_mode; + bool rom_mode; int read_counter; /* used for lazy switch-back to rom mode */ int sectors_to_erase; uint64_t erase_time_remaining; @@ -180,12 +173,22 @@ static void pflash_setup_mappings(PFlashCFI02 *pfl) "pflash-alias", &pfl->orig_mem, 0, size); memory_region_add_subregion(&pfl->mem, i * size, &pfl->mem_mappings[i]); } + pfl->rom_mode = true; } -static void pflash_register_memory(PFlashCFI02 *pfl, int rom_mode) +static void pflash_reset_state_machine(PFlashCFI02 *pfl) { - memory_region_rom_device_set_romd(&pfl->orig_mem, rom_mode); - pfl->rom_mode = rom_mode; + trace_pflash_reset(pfl->name); + pfl->cmd = 0x00; + pfl->wcycle = 0; +} + +static void pflash_mode_read_array(PFlashCFI02 *pfl) +{ + trace_pflash_mode_read_array(pfl->name); + pflash_reset_state_machine(pfl); + pfl->rom_mode = true; + memory_region_rom_device_set_romd(&pfl->orig_mem, true); } static size_t pflash_regions_count(PFlashCFI02 *pfl) @@ -220,7 +223,7 @@ static void pflash_timer(void *opaque) { PFlashCFI02 *pfl = opaque; - trace_pflash_timer_expired(pfl->cmd); + trace_pflash_timer_expired(pfl->name, pfl->cmd); if (pfl->cmd == 0x30) { /* * Sector erase. If DQ3 is 0 when the timer expires, then the 50 @@ -233,11 +236,10 @@ static void pflash_timer(void *opaque) uint64_t timeout = pflash_erase_time(pfl); timer_mod(&pfl->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + timeout); - DPRINTF("%s: erase timeout fired; erasing %d sectors\n", - __func__, pfl->sectors_to_erase); + trace_pflash_erase_timeout(pfl->name, pfl->sectors_to_erase); return; } - DPRINTF("%s: sector erase complete\n", __func__); + trace_pflash_erase_complete(pfl->name); bitmap_zero(pfl->sector_erase_map, pfl->total_sectors); pfl->sectors_to_erase = 0; reset_dq3(pfl); @@ -247,11 +249,10 @@ static void pflash_timer(void *opaque) toggle_dq7(pfl); if (pfl->bypass) { pfl->wcycle = 2; + pfl->cmd = 0; } else { - pflash_register_memory(pfl, 1); - pfl->wcycle = 0; + pflash_mode_read_array(pfl); } - pfl->cmd = 0; } /* @@ -262,7 +263,7 @@ static uint64_t pflash_data_read(PFlashCFI02 *pfl, hwaddr offset, { uint8_t *p = (uint8_t *)pfl->storage + offset; uint64_t ret = pfl->be ? ldn_be_p(p, width) : ldn_le_p(p, width); - trace_pflash_data_read(offset, width, ret); + trace_pflash_data_read(pfl->name, offset, width, ret); return ret; } @@ -313,7 +314,7 @@ static uint64_t pflash_read(void *opaque, hwaddr offset, unsigned int width) /* Lazy reset to ROMD mode after a certain amount of read accesses */ if (!pfl->rom_mode && pfl->wcycle == 0 && ++pfl->read_counter > PFLASH_LAZY_ROMD_THRESHOLD) { - pflash_register_memory(pfl, 1); + pflash_mode_read_array(pfl); } offset &= pfl->chip_len - 1; boff = offset & 0xFF; @@ -325,9 +326,8 @@ static uint64_t pflash_read(void *opaque, hwaddr offset, unsigned int width) switch (pfl->cmd) { default: /* This should never happen : reset state & treat it as a read*/ - DPRINTF("%s: unknown command state: %x\n", __func__, pfl->cmd); - pfl->wcycle = 0; - pfl->cmd = 0; + trace_pflash_read_unknown_state(pfl->name, pfl->cmd); + pflash_reset_state_machine(pfl); /* fall through to the read code */ case 0x80: /* Erase (unlock) */ /* We accept reads during second unlock sequence... */ @@ -338,7 +338,7 @@ static uint64_t pflash_read(void *opaque, hwaddr offset, unsigned int width) toggle_dq2(pfl); /* Status register read */ ret = pfl->status; - DPRINTF("%s: status %" PRIx64 "\n", __func__, ret); + trace_pflash_read_status(pfl->name, ret); break; } /* Flash area read */ @@ -363,7 +363,7 @@ static uint64_t pflash_read(void *opaque, hwaddr offset, unsigned int width) default: ret = pflash_data_read(pfl, offset, width); } - DPRINTF("%s: ID " TARGET_FMT_plx " %" PRIx64 "\n", __func__, boff, ret); + trace_pflash_read_done(pfl->name, boff, ret); break; case 0x10: /* Chip Erase */ case 0x30: /* Sector Erase */ @@ -375,7 +375,7 @@ static uint64_t pflash_read(void *opaque, hwaddr offset, unsigned int width) toggle_dq6(pfl); /* Status register read */ ret = pfl->status; - DPRINTF("%s: status %" PRIx64 "\n", __func__, ret); + trace_pflash_read_status(pfl->name, ret); break; case 0x98: /* CFI query mode */ @@ -386,7 +386,7 @@ static uint64_t pflash_read(void *opaque, hwaddr offset, unsigned int width) } break; } - trace_pflash_io_read(offset, width, ret, pfl->cmd, pfl->wcycle); + trace_pflash_io_read(pfl->name, offset, width, ret, pfl->cmd, pfl->wcycle); return ret; } @@ -415,9 +415,8 @@ static void pflash_sector_erase(PFlashCFI02 *pfl, hwaddr offset) SectorInfo sector_info = pflash_sector_info(pfl, offset); uint64_t sector_len = sector_info.len; offset &= ~(sector_len - 1); - DPRINTF("%s: start sector erase at %0*" PRIx64 "-%0*" PRIx64 "\n", - __func__, pfl->width * 2, offset, - pfl->width * 2, offset + sector_len - 1); + trace_pflash_sector_erase_start(pfl->name, pfl->width * 2, offset, + pfl->width * 2, offset + sector_len - 1); if (!pfl->ro) { uint8_t *p = pfl->storage; memset(p + offset, 0xff, sector_len); @@ -438,7 +437,7 @@ static void pflash_write(void *opaque, hwaddr offset, uint64_t value, uint8_t *p; uint8_t cmd; - trace_pflash_io_write(offset, width, value, pfl->wcycle); + trace_pflash_io_write(pfl->name, offset, width, value, pfl->wcycle); cmd = value; if (pfl->cmd != 0xA0) { /* Reset does nothing during chip erase and sector erase. */ @@ -465,8 +464,10 @@ static void pflash_write(void *opaque, hwaddr offset, uint64_t value, switch (pfl->wcycle) { case 0: /* Set the device in I/O access mode if required */ - if (pfl->rom_mode) - pflash_register_memory(pfl, 0); + if (pfl->rom_mode) { + pfl->rom_mode = false; + memory_region_rom_device_set_romd(&pfl->orig_mem, false); + } pfl->read_counter = 0; /* We're in read mode */ check_unlock0: @@ -496,27 +497,25 @@ static void pflash_write(void *opaque, hwaddr offset, uint64_t value, return; } if (boff != pfl->unlock_addr0 || cmd != 0xAA) { - DPRINTF("%s: unlock0 failed " TARGET_FMT_plx " %02x %04x\n", - __func__, boff, cmd, pfl->unlock_addr0); + trace_pflash_unlock0_failed(pfl->name, boff, + cmd, pfl->unlock_addr0); goto reset_flash; } - DPRINTF("%s: unlock sequence started\n", __func__); + trace_pflash_write(pfl->name, "unlock sequence started"); break; case 1: /* We started an unlock sequence */ check_unlock1: if (boff != pfl->unlock_addr1 || cmd != 0x55) { - DPRINTF("%s: unlock1 failed " TARGET_FMT_plx " %02x\n", __func__, - boff, cmd); + trace_pflash_unlock1_failed(pfl->name, boff, cmd); goto reset_flash; } - DPRINTF("%s: unlock sequence done\n", __func__); + trace_pflash_write(pfl->name, "unlock sequence done"); break; case 2: /* We finished an unlock sequence */ if (!pfl->bypass && boff != pfl->unlock_addr0) { - DPRINTF("%s: command failed " TARGET_FMT_plx " %02x\n", __func__, - boff, cmd); + trace_pflash_write_failed(pfl->name, boff, cmd); goto reset_flash; } switch (cmd) { @@ -527,10 +526,10 @@ static void pflash_write(void *opaque, hwaddr offset, uint64_t value, case 0x90: /* Autoselect */ case 0xA0: /* Program */ pfl->cmd = cmd; - DPRINTF("%s: starting command %02x\n", __func__, cmd); + trace_pflash_write_start(pfl->name, cmd); break; default: - DPRINTF("%s: unknown command %02x\n", __func__, cmd); + trace_pflash_write_unknown(pfl->name, cmd); goto reset_flash; } break; @@ -548,7 +547,7 @@ static void pflash_write(void *opaque, hwaddr offset, uint64_t value, } goto reset_flash; } - trace_pflash_data_write(offset, width, value, 0); + trace_pflash_data_write(pfl->name, offset, width, value, 0); if (!pfl->ro) { p = (uint8_t *)pfl->storage + offset; if (pfl->be) { @@ -586,8 +585,7 @@ static void pflash_write(void *opaque, hwaddr offset, uint64_t value, } /* fall through */ default: - DPRINTF("%s: invalid write for command %02x\n", - __func__, pfl->cmd); + trace_pflash_write_invalid(pfl->name, pfl->cmd); goto reset_flash; } case 4: @@ -600,8 +598,7 @@ static void pflash_write(void *opaque, hwaddr offset, uint64_t value, goto check_unlock1; default: /* Should never happen */ - DPRINTF("%s: invalid command state %02x (wc 4)\n", - __func__, pfl->cmd); + trace_pflash_write_invalid_state(pfl->name, pfl->cmd, 5); goto reset_flash; } break; @@ -613,12 +610,11 @@ static void pflash_write(void *opaque, hwaddr offset, uint64_t value, switch (cmd) { case 0x10: /* Chip Erase */ if (boff != pfl->unlock_addr0) { - DPRINTF("%s: chip erase: invalid address " TARGET_FMT_plx "\n", - __func__, offset); + trace_pflash_chip_erase_invalid(pfl->name, offset); goto reset_flash; } /* Chip erase */ - DPRINTF("%s: start chip erase\n", __func__); + trace_pflash_chip_erase_start(pfl->name); if (!pfl->ro) { memset(pfl->storage, 0xff, pfl->chip_len); pflash_update(pfl, 0, pfl->chip_len); @@ -632,7 +628,7 @@ static void pflash_write(void *opaque, hwaddr offset, uint64_t value, pflash_sector_erase(pfl, offset); break; default: - DPRINTF("%s: invalid command %02x (wc 5)\n", __func__, cmd); + trace_pflash_write_invalid_command(pfl->name, cmd); goto reset_flash; } pfl->cmd = cmd; @@ -663,8 +659,7 @@ static void pflash_write(void *opaque, hwaddr offset, uint64_t value, } reset_dq3(pfl); timer_del(&pfl->timer); - pfl->wcycle = 0; - pfl->cmd = 0; + pflash_reset_state_machine(pfl); return; } /* @@ -683,19 +678,18 @@ static void pflash_write(void *opaque, hwaddr offset, uint64_t value, return; default: /* Should never happen */ - DPRINTF("%s: invalid command state %02x (wc 6)\n", - __func__, pfl->cmd); + trace_pflash_write_invalid_state(pfl->name, pfl->cmd, 6); goto reset_flash; } break; /* Special values for CFI queries */ case WCYCLE_CFI: case WCYCLE_AUTOSELECT_CFI: - DPRINTF("%s: invalid write in CFI query mode\n", __func__); + trace_pflash_write(pfl->name, "invalid write in CFI query mode"); goto reset_flash; default: /* Should never happen */ - DPRINTF("%s: invalid write state (wc 7)\n", __func__); + trace_pflash_write(pfl->name, "invalid write state (wc 7)"); goto reset_flash; } pfl->wcycle++; @@ -704,10 +698,8 @@ static void pflash_write(void *opaque, hwaddr offset, uint64_t value, /* Reset flash */ reset_flash: - trace_pflash_reset(); pfl->bypass = 0; - pfl->wcycle = 0; - pfl->cmd = 0; + pflash_reset_state_machine(pfl); return; do_bypass: @@ -723,6 +715,104 @@ static const MemoryRegionOps pflash_cfi02_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; +static void pflash_cfi02_fill_cfi_table(PFlashCFI02 *pfl, int nb_regions) +{ + /* Hardcoded CFI table (mostly from SG29 Spansion flash) */ + const uint16_t pri_ofs = 0x40; + /* Standard "QRY" string */ + pfl->cfi_table[0x10] = 'Q'; + pfl->cfi_table[0x11] = 'R'; + pfl->cfi_table[0x12] = 'Y'; + /* Command set (AMD/Fujitsu) */ + pfl->cfi_table[0x13] = 0x02; + pfl->cfi_table[0x14] = 0x00; + /* Primary extended table address */ + pfl->cfi_table[0x15] = pri_ofs; + pfl->cfi_table[0x16] = pri_ofs >> 8; + /* Alternate command set (none) */ + pfl->cfi_table[0x17] = 0x00; + pfl->cfi_table[0x18] = 0x00; + /* Alternate extended table (none) */ + pfl->cfi_table[0x19] = 0x00; + pfl->cfi_table[0x1A] = 0x00; + /* Vcc min */ + pfl->cfi_table[0x1B] = 0x27; + /* Vcc max */ + pfl->cfi_table[0x1C] = 0x36; + /* Vpp min (no Vpp pin) */ + pfl->cfi_table[0x1D] = 0x00; + /* Vpp max (no Vpp pin) */ + pfl->cfi_table[0x1E] = 0x00; + /* Timeout per single byte/word write (128 ms) */ + pfl->cfi_table[0x1F] = 0x07; + /* Timeout for min size buffer write (NA) */ + pfl->cfi_table[0x20] = 0x00; + /* Typical timeout for block erase (512 ms) */ + pfl->cfi_table[0x21] = 0x09; + /* Typical timeout for full chip erase (4096 ms) */ + pfl->cfi_table[0x22] = 0x0C; + /* Reserved */ + pfl->cfi_table[0x23] = 0x01; + /* Max timeout for buffer write (NA) */ + pfl->cfi_table[0x24] = 0x00; + /* Max timeout for block erase */ + pfl->cfi_table[0x25] = 0x0A; + /* Max timeout for chip erase */ + pfl->cfi_table[0x26] = 0x0D; + /* Device size */ + pfl->cfi_table[0x27] = ctz32(pfl->chip_len); + /* Flash device interface (8 & 16 bits) */ + pfl->cfi_table[0x28] = 0x02; + pfl->cfi_table[0x29] = 0x00; + /* Max number of bytes in multi-bytes write */ + /* + * XXX: disable buffered write as it's not supported + * pfl->cfi_table[0x2A] = 0x05; + */ + pfl->cfi_table[0x2A] = 0x00; + pfl->cfi_table[0x2B] = 0x00; + /* Number of erase block regions */ + pfl->cfi_table[0x2c] = nb_regions; + /* Erase block regions */ + for (int i = 0; i < nb_regions; ++i) { + uint32_t sector_len_per_device = pfl->sector_len[i]; + pfl->cfi_table[0x2d + 4 * i] = pfl->nb_blocs[i] - 1; + pfl->cfi_table[0x2e + 4 * i] = (pfl->nb_blocs[i] - 1) >> 8; + pfl->cfi_table[0x2f + 4 * i] = sector_len_per_device >> 8; + pfl->cfi_table[0x30 + 4 * i] = sector_len_per_device >> 16; + } + assert(0x2c + 4 * nb_regions < pri_ofs); + + /* Extended */ + pfl->cfi_table[0x00 + pri_ofs] = 'P'; + pfl->cfi_table[0x01 + pri_ofs] = 'R'; + pfl->cfi_table[0x02 + pri_ofs] = 'I'; + + /* Extended version 1.0 */ + pfl->cfi_table[0x03 + pri_ofs] = '1'; + pfl->cfi_table[0x04 + pri_ofs] = '0'; + + /* Address sensitive unlock required. */ + pfl->cfi_table[0x05 + pri_ofs] = 0x00; + /* Erase suspend to read/write. */ + pfl->cfi_table[0x06 + pri_ofs] = 0x02; + /* Sector protect not supported. */ + pfl->cfi_table[0x07 + pri_ofs] = 0x00; + /* Temporary sector unprotect not supported. */ + pfl->cfi_table[0x08 + pri_ofs] = 0x00; + + /* Sector protect/unprotect scheme. */ + pfl->cfi_table[0x09 + pri_ofs] = 0x00; + + /* Simultaneous operation not supported. */ + pfl->cfi_table[0x0a + pri_ofs] = 0x00; + /* Burst mode not supported. */ + pfl->cfi_table[0x0b + pri_ofs] = 0x00; + /* Page mode not supported. */ + pfl->cfi_table[0x0c + pri_ofs] = 0x00; + assert(0x0c + pri_ofs < ARRAY_SIZE(pfl->cfi_table)); +} + static void pflash_cfi02_realize(DeviceState *dev, Error **errp) { ERRP_GUARD(); @@ -828,106 +918,19 @@ static void pflash_cfi02_realize(DeviceState *dev, Error **errp) pfl->sector_erase_map = bitmap_new(pfl->total_sectors); pflash_setup_mappings(pfl); - pfl->rom_mode = 1; sysbus_init_mmio(SYS_BUS_DEVICE(dev), &pfl->mem); timer_init_ns(&pfl->timer, QEMU_CLOCK_VIRTUAL, pflash_timer, pfl); - pfl->wcycle = 0; - pfl->cmd = 0; pfl->status = 0; - /* Hardcoded CFI table (mostly from SG29 Spansion flash) */ - const uint16_t pri_ofs = 0x40; - /* Standard "QRY" string */ - pfl->cfi_table[0x10] = 'Q'; - pfl->cfi_table[0x11] = 'R'; - pfl->cfi_table[0x12] = 'Y'; - /* Command set (AMD/Fujitsu) */ - pfl->cfi_table[0x13] = 0x02; - pfl->cfi_table[0x14] = 0x00; - /* Primary extended table address */ - pfl->cfi_table[0x15] = pri_ofs; - pfl->cfi_table[0x16] = pri_ofs >> 8; - /* Alternate command set (none) */ - pfl->cfi_table[0x17] = 0x00; - pfl->cfi_table[0x18] = 0x00; - /* Alternate extended table (none) */ - pfl->cfi_table[0x19] = 0x00; - pfl->cfi_table[0x1A] = 0x00; - /* Vcc min */ - pfl->cfi_table[0x1B] = 0x27; - /* Vcc max */ - pfl->cfi_table[0x1C] = 0x36; - /* Vpp min (no Vpp pin) */ - pfl->cfi_table[0x1D] = 0x00; - /* Vpp max (no Vpp pin) */ - pfl->cfi_table[0x1E] = 0x00; - /* Timeout per single byte/word write (128 ms) */ - pfl->cfi_table[0x1F] = 0x07; - /* Timeout for min size buffer write (NA) */ - pfl->cfi_table[0x20] = 0x00; - /* Typical timeout for block erase (512 ms) */ - pfl->cfi_table[0x21] = 0x09; - /* Typical timeout for full chip erase (4096 ms) */ - pfl->cfi_table[0x22] = 0x0C; - /* Reserved */ - pfl->cfi_table[0x23] = 0x01; - /* Max timeout for buffer write (NA) */ - pfl->cfi_table[0x24] = 0x00; - /* Max timeout for block erase */ - pfl->cfi_table[0x25] = 0x0A; - /* Max timeout for chip erase */ - pfl->cfi_table[0x26] = 0x0D; - /* Device size */ - pfl->cfi_table[0x27] = ctz32(pfl->chip_len); - /* Flash device interface (8 & 16 bits) */ - pfl->cfi_table[0x28] = 0x02; - pfl->cfi_table[0x29] = 0x00; - /* Max number of bytes in multi-bytes write */ - /* XXX: disable buffered write as it's not supported */ - // pfl->cfi_table[0x2A] = 0x05; - pfl->cfi_table[0x2A] = 0x00; - pfl->cfi_table[0x2B] = 0x00; - /* Number of erase block regions */ - pfl->cfi_table[0x2c] = nb_regions; - /* Erase block regions */ - for (int i = 0; i < nb_regions; ++i) { - uint32_t sector_len_per_device = pfl->sector_len[i]; - pfl->cfi_table[0x2d + 4 * i] = pfl->nb_blocs[i] - 1; - pfl->cfi_table[0x2e + 4 * i] = (pfl->nb_blocs[i] - 1) >> 8; - pfl->cfi_table[0x2f + 4 * i] = sector_len_per_device >> 8; - pfl->cfi_table[0x30 + 4 * i] = sector_len_per_device >> 16; - } - assert(0x2c + 4 * nb_regions < pri_ofs); - - /* Extended */ - pfl->cfi_table[0x00 + pri_ofs] = 'P'; - pfl->cfi_table[0x01 + pri_ofs] = 'R'; - pfl->cfi_table[0x02 + pri_ofs] = 'I'; - - /* Extended version 1.0 */ - pfl->cfi_table[0x03 + pri_ofs] = '1'; - pfl->cfi_table[0x04 + pri_ofs] = '0'; - - /* Address sensitive unlock required. */ - pfl->cfi_table[0x05 + pri_ofs] = 0x00; - /* Erase suspend to read/write. */ - pfl->cfi_table[0x06 + pri_ofs] = 0x02; - /* Sector protect not supported. */ - pfl->cfi_table[0x07 + pri_ofs] = 0x00; - /* Temporary sector unprotect not supported. */ - pfl->cfi_table[0x08 + pri_ofs] = 0x00; + pflash_cfi02_fill_cfi_table(pfl, nb_regions); +} - /* Sector protect/unprotect scheme. */ - pfl->cfi_table[0x09 + pri_ofs] = 0x00; +static void pflash_cfi02_reset(DeviceState *dev) +{ + PFlashCFI02 *pfl = PFLASH_CFI02(dev); - /* Simultaneous operation not supported. */ - pfl->cfi_table[0x0a + pri_ofs] = 0x00; - /* Burst mode not supported. */ - pfl->cfi_table[0x0b + pri_ofs] = 0x00; - /* Page mode not supported. */ - pfl->cfi_table[0x0c + pri_ofs] = 0x00; - assert(0x0c + pri_ofs < ARRAY_SIZE(pfl->cfi_table)); + pflash_reset_state_machine(pfl); } static Property pflash_cfi02_properties[] = { @@ -967,6 +970,7 @@ static void pflash_cfi02_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = pflash_cfi02_realize; + dc->reset = pflash_cfi02_reset; dc->unrealize = pflash_cfi02_unrealize; device_class_set_props(dc, pflash_cfi02_properties); set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); |