#include "hw/hw.h"
#include "hw/block/flash.h"
#include "sysemu/block-backend.h"
+#include "qapi/error.h"
#include "qemu/timer.h"
#include "qemu/bitops.h"
-#include "exec/address-spaces.h"
#include "qemu/host-utils.h"
+#include "qemu/log.h"
#include "hw/sysbus.h"
+#include "sysemu/sysemu.h"
+#include "trace.h"
#define PFLASH_BUG(fmt, ...) \
do { \
#define DPRINTF(fmt, ...) do { } while (0)
#endif
-#define TYPE_CFI_PFLASH01 "cfi.pflash01"
#define CFI_PFLASH01(obj) OBJECT_CHECK(pflash_t, (obj), TYPE_CFI_PFLASH01)
#define PFLASH_BE 0
uint16_t ident1;
uint16_t ident2;
uint16_t ident3;
- uint8_t cfi_len;
uint8_t cfi_table[0x52];
uint64_t counter;
unsigned int writeblock_size;
MemoryRegion mem;
char *name;
void *storage;
+ VMChangeStateEntry *vmstate;
+ bool old_multiple_chip_handling;
};
static int pflash_post_load(void *opaque, int version_id);
{
pflash_t *pfl = opaque;
- DPRINTF("%s: command %02x done\n", __func__, pfl->cmd);
+ trace_pflash_timer_expired(pfl->cmd);
/* Reset flash */
pfl->status ^= 0x80;
memory_region_rom_device_set_romd(&pfl->mem, true);
boff = offset >> (ctz32(pfl->bank_width) +
ctz32(pfl->max_device_width) - ctz32(pfl->device_width));
- if (boff > pfl->cfi_len) {
+ if (boff >= sizeof(pfl->cfi_table)) {
return 0;
}
/* Now we will construct the CFI response generated by a single
switch (boff & 0xFF) {
case 0:
resp = pfl->ident0;
- DPRINTF("%s: Manufacturer Code %04x\n", __func__, resp);
+ trace_pflash_manufacturer_id(resp);
break;
case 1:
resp = pfl->ident1;
- DPRINTF("%s: Device ID Code %04x\n", __func__, resp);
+ trace_pflash_device_id(resp);
break;
default:
- DPRINTF("%s: Read Device Information offset=%x\n", __func__,
- (unsigned)offset);
+ trace_pflash_device_info(offset);
return 0;
break;
}
switch (width) {
case 1:
ret = p[offset];
- DPRINTF("%s: data offset " TARGET_FMT_plx " %02x\n",
- __func__, offset, ret);
+ trace_pflash_data_read8(offset, ret);
break;
case 2:
if (be) {
ret = p[offset];
ret |= p[offset + 1] << 8;
}
- DPRINTF("%s: data offset " TARGET_FMT_plx " %04x\n",
- __func__, offset, ret);
+ trace_pflash_data_read16(offset, ret);
break;
case 4:
if (be) {
ret |= p[offset + 2] << 16;
ret |= p[offset + 3] << 24;
}
- DPRINTF("%s: data offset " TARGET_FMT_plx " %08x\n",
- __func__, offset, ret);
+ trace_pflash_data_read32(offset, ret);
break;
default:
DPRINTF("BUG in %s\n", __func__);
uint32_t ret;
ret = -1;
-
-#if 0
- DPRINTF("%s: reading offset " TARGET_FMT_plx " under cmd %02x width %d\n",
- __func__, offset, pfl->cmd, width);
-#endif
+ trace_pflash_read(offset, pfl->cmd, width, pfl->wcycle);
switch (pfl->cmd) {
default:
/* This should never happen : reset state & treat it as a read */
switch (boff) {
case 0:
ret = pfl->ident0 << 8 | pfl->ident1;
- DPRINTF("%s: Manufacturer Code %04x\n", __func__, ret);
+ trace_pflash_manufacturer_id(ret);
break;
case 1:
ret = pfl->ident2 << 8 | pfl->ident3;
- DPRINTF("%s: Device ID Code %04x\n", __func__, ret);
+ trace_pflash_device_id(ret);
break;
default:
- DPRINTF("%s: Read Device Information boff=%x\n", __func__,
- (unsigned)boff);
+ trace_pflash_device_info(boff);
ret = 0;
break;
}
boff = boff >> 2;
}
- if (boff > pfl->cfi_len) {
- ret = 0;
- } else {
+ if (boff < sizeof(pfl->cfi_table)) {
ret = pfl->cfi_table[boff];
+ } else {
+ ret = 0;
}
} else {
/* If we have a read larger than the bank_width, combine multiple
int offset_end;
if (pfl->blk) {
offset_end = offset + size;
- /* round to sectors */
- offset = offset >> 9;
- offset_end = (offset_end + 511) >> 9;
- blk_write(pfl->blk, offset, pfl->storage + (offset << 9),
- offset_end - offset);
+ /* widen to sector boundaries */
+ offset = QEMU_ALIGN_DOWN(offset, BDRV_SECTOR_SIZE);
+ offset_end = QEMU_ALIGN_UP(offset_end, BDRV_SECTOR_SIZE);
+ blk_pwrite(pfl->blk, offset, pfl->storage + offset,
+ offset_end - offset, 0);
}
}
{
uint8_t *p = pfl->storage;
- DPRINTF("%s: block write offset " TARGET_FMT_plx
- " value %x counter %016" PRIx64 "\n",
- __func__, offset, value, pfl->counter);
+ trace_pflash_data_write(offset, value, width, pfl->counter);
switch (width) {
case 1:
p[offset] = value;
cmd = value;
- DPRINTF("%s: writing offset " TARGET_FMT_plx " value %08x width %d wcycle 0x%x\n",
- __func__, offset, value, width, pfl->wcycle);
-
+ trace_pflash_write(offset, value, width, pfl->wcycle);
if (!pfl->wcycle) {
/* Set the device in I/O access mode */
memory_region_rom_device_set_romd(&pfl->mem, false);
"\n", __func__, offset, pfl->wcycle, pfl->cmd, value);
reset_flash:
+ trace_pflash_reset();
memory_region_rom_device_set_romd(&pfl->mem, true);
-
pfl->wcycle = 0;
pfl->cmd = 0;
}
pflash_t *pfl = CFI_PFLASH01(dev);
uint64_t total_len;
int ret;
- uint64_t blocks_per_device, device_len;
+ uint64_t blocks_per_device, sector_len_per_device, device_len;
int num_devices;
Error *local_err = NULL;
+ if (pfl->sector_len == 0) {
+ error_setg(errp, "attribute \"sector-length\" not specified or zero.");
+ return;
+ }
+ if (pfl->nb_blocs == 0) {
+ error_setg(errp, "attribute \"num-blocks\" not specified or zero.");
+ return;
+ }
+ if (pfl->name == NULL) {
+ error_setg(errp, "attribute \"name\" not specified.");
+ return;
+ }
+
total_len = pfl->sector_len * pfl->nb_blocs;
/* These are only used to expose the parameters of each device
* in the cfi_table[].
*/
num_devices = pfl->device_width ? (pfl->bank_width / pfl->device_width) : 1;
- blocks_per_device = pfl->nb_blocs / num_devices;
- device_len = pfl->sector_len * blocks_per_device;
+ if (pfl->old_multiple_chip_handling) {
+ blocks_per_device = pfl->nb_blocs / num_devices;
+ sector_len_per_device = pfl->sector_len;
+ } else {
+ blocks_per_device = pfl->nb_blocs;
+ sector_len_per_device = pfl->sector_len / num_devices;
+ }
+ device_len = sector_len_per_device * blocks_per_device;
/* XXX: to be fixed */
#if 0
return;
}
- vmstate_register_ram(&pfl->mem, DEVICE(pfl));
pfl->storage = memory_region_get_ram_ptr(&pfl->mem);
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &pfl->mem);
+ if (pfl->blk) {
+ uint64_t perm;
+ pfl->ro = blk_is_read_only(pfl->blk);
+ perm = BLK_PERM_CONSISTENT_READ | (pfl->ro ? 0 : BLK_PERM_WRITE);
+ ret = blk_set_perm(pfl->blk, perm, BLK_PERM_ALL, errp);
+ if (ret < 0) {
+ return;
+ }
+ } else {
+ pfl->ro = 0;
+ }
+
if (pfl->blk) {
/* read the initial flash content */
- ret = blk_read(pfl->blk, 0, pfl->storage, total_len >> 9);
+ ret = blk_pread(pfl->blk, 0, pfl->storage, total_len);
if (ret < 0) {
vmstate_unregister_ram(&pfl->mem, DEVICE(pfl));
}
}
- if (pfl->blk) {
- pfl->ro = blk_is_read_only(pfl->blk);
- } else {
- pfl->ro = 0;
- }
-
/* Default to devices being used at their maximum device width. This was
* assumed before the device_width support was added.
*/
pfl->cmd = 0;
pfl->status = 0;
/* Hardcoded CFI table */
- pfl->cfi_len = 0x52;
/* Standard "QRY" string */
pfl->cfi_table[0x10] = 'Q';
pfl->cfi_table[0x11] = 'R';
pfl->cfi_table[0x2A] = 0x0B;
}
pfl->writeblock_size = 1 << pfl->cfi_table[0x2A];
+ if (!pfl->old_multiple_chip_handling && num_devices > 1) {
+ pfl->writeblock_size *= num_devices;
+ }
pfl->cfi_table[0x2B] = 0x00;
/* Number of erase block regions (uniform) */
/* Erase block region 1 */
pfl->cfi_table[0x2D] = blocks_per_device - 1;
pfl->cfi_table[0x2E] = (blocks_per_device - 1) >> 8;
- pfl->cfi_table[0x2F] = pfl->sector_len >> 8;
- pfl->cfi_table[0x30] = pfl->sector_len >> 16;
+ pfl->cfi_table[0x2F] = sector_len_per_device >> 8;
+ pfl->cfi_table[0x30] = sector_len_per_device >> 16;
/* Extended */
pfl->cfi_table[0x31] = 'P';
DEFINE_PROP_UINT16("id2", struct pflash_t, ident2, 0),
DEFINE_PROP_UINT16("id3", struct pflash_t, ident3, 0),
DEFINE_PROP_STRING("name", struct pflash_t, name),
+ DEFINE_PROP_BOOL("old-multiple-chip-handling", struct pflash_t,
+ old_multiple_chip_handling, false),
DEFINE_PROP_END_OF_LIST(),
};
return &fl->mem;
}
+static void postload_update_cb(void *opaque, int running, RunState state)
+{
+ pflash_t *pfl = opaque;
+
+ /* This is called after bdrv_invalidate_cache_all. */
+ qemu_del_vm_change_state_handler(pfl->vmstate);
+ pfl->vmstate = NULL;
+
+ DPRINTF("%s: updating bdrv for %s\n", __func__, pfl->name);
+ pflash_update(pfl, 0, pfl->sector_len * pfl->nb_blocs);
+}
+
static int pflash_post_load(void *opaque, int version_id)
{
pflash_t *pfl = opaque;
if (!pfl->ro) {
- DPRINTF("%s: updating bdrv for %s\n", __func__, pfl->name);
- pflash_update(pfl, 0, pfl->sector_len * pfl->nb_blocs);
+ pfl->vmstate = qemu_add_vm_change_state_handler(postload_update_cb,
+ pfl);
}
return 0;
}