static void pflash_write(pflash_t *pfl, hwaddr offset, uint32_t value, int width, int be) { uint8_t *p; uint8_t cmd; cmd = value; DPRINTF("%s: writing offset " TARGET_FMT_plx " value %08x width %d wcycle 0x%x\n", __func__, offset, value, width, pfl->wcycle); if (!pfl->wcycle) { /* Set the device in I/O access mode */ memory_region_rom_device_set_readable(&pfl->mem, false); } switch (pfl->wcycle) { case 0: /* read mode */ switch (cmd) { case 0x00: /* ??? */ goto reset_flash; case 0x10: /* Single Byte Program */ case 0x40: /* Single Byte Program */ DPRINTF("%s: Single Byte Program\n", __func__); break; case 0x20: /* Block erase */ p = pfl->storage; offset &= ~(pfl->sector_len - 1); DPRINTF("%s: block erase at " TARGET_FMT_plx " bytes %x\n", __func__, offset, (unsigned)pfl->sector_len); if (!pfl->ro) { memset(p + offset, 0xff, pfl->sector_len); pflash_update(pfl, offset, pfl->sector_len); } else { pfl->status |= 0x20; /* Block erase error */ } pfl->status |= 0x80; /* Ready! */ break; case 0x50: /* Clear status bits */ DPRINTF("%s: Clear status bits\n", __func__); pfl->status = 0x0; goto reset_flash; case 0x60: /* Block (un)lock */ DPRINTF("%s: Block unlock\n", __func__); break; case 0x70: /* Status Register */ DPRINTF("%s: Read status register\n", __func__); pfl->cmd = cmd; return; case 0x90: /* Read Device ID */ DPRINTF("%s: Read Device information\n", __func__); pfl->cmd = cmd; return; case 0x98: /* CFI query */ DPRINTF("%s: CFI query\n", __func__); break; case 0xe8: /* Write to buffer */ DPRINTF("%s: Write to buffer\n", __func__); pfl->status |= 0x80; /* Ready! */ break; case 0xf0: /* Probe for AMD flash */ DPRINTF("%s: Probe for AMD flash\n", __func__); goto reset_flash; case 0xff: /* Read array mode */ DPRINTF("%s: Read array mode\n", __func__); goto reset_flash; default: goto error_flash; } pfl->wcycle++; pfl->cmd = cmd; break; case 1: switch (pfl->cmd) { case 0x10: /* Single Byte Program */ case 0x40: /* Single Byte Program */ DPRINTF("%s: Single Byte Program\n", __func__); if (!pfl->ro) { pflash_data_write(pfl, offset, value, width, be); pflash_update(pfl, offset, width); } else { pfl->status |= 0x10; /* Programming error */ } pfl->status |= 0x80; /* Ready! */ pfl->wcycle = 0; break; case 0x20: /* Block erase */ case 0x28: if (cmd == 0xd0) { /* confirm */ pfl->wcycle = 0; pfl->status |= 0x80; } else if (cmd == 0xff) { /* read array mode */ goto reset_flash; } else goto error_flash; break; case 0xe8: DPRINTF("%s: block write of %x bytes\n", __func__, value); pfl->counter = value; pfl->wcycle++; break; case 0x60: if (cmd == 0xd0) { pfl->wcycle = 0; pfl->status |= 0x80; } else if (cmd == 0x01) { pfl->wcycle = 0; pfl->status |= 0x80; } else if (cmd == 0xff) { goto reset_flash; } else { DPRINTF("%s: Unknown (un)locking command\n", __func__); goto reset_flash; } break; case 0x98: if (cmd == 0xff) { goto reset_flash; } else { DPRINTF("%s: leaving query mode\n", __func__); } break; default: goto error_flash; } break; case 2: switch (pfl->cmd) { case 0xe8: /* Block write */ if (!pfl->ro) { pflash_data_write(pfl, offset, value, width, be); } else { pfl->status |= 0x10; /* Programming error */ } pfl->status |= 0x80; if (!pfl->counter) { hwaddr mask = pfl->writeblock_size - 1; mask = ~mask; DPRINTF("%s: block write finished\n", __func__); pfl->wcycle++; if (!pfl->ro) { /* Flush the entire write buffer onto backing storage. */ pflash_update(pfl, offset & mask, pfl->writeblock_size); } else { pfl->status |= 0x10; /* Programming error */ } } pfl->counter--; break; default: goto error_flash; } break; case 3: /* Confirm mode */ switch (pfl->cmd) { case 0xe8: /* Block write */ if (cmd == 0xd0) { pfl->wcycle = 0; pfl->status |= 0x80; } else { DPRINTF("%s: unknown command for \"write block\"\n", __func__); PFLASH_BUG("Write block confirm"); goto reset_flash; } break; default: goto error_flash; } break; default: /* Should never happen */ DPRINTF("%s: invalid write state\n", __func__); goto reset_flash; } return; error_flash: qemu_log_mask(LOG_UNIMP, "%s: Unimplemented flash cmd sequence " "(offset " TARGET_FMT_plx ", wcycle 0x%x cmd 0x%x value 0x%x)" "\n", __func__, offset, pfl->wcycle, pfl->cmd, value); reset_flash: memory_region_rom_device_set_readable(&pfl->mem, true); pfl->bypass = 0; pfl->wcycle = 0; pfl->cmd = 0; }