static void decode_fast_read_cmd(Flash *s) { s->needed_bytes = get_addr_length(s); switch (get_man(s)) { /* Dummy cycles - modeled with bytes writes instead of bits */ case MAN_WINBOND: s->needed_bytes += 8; break; case MAN_NUMONYX: s->needed_bytes += extract32(s->volatile_cfg, 4, 4); break; case MAN_MACRONIX: if (extract32(s->volatile_cfg, 6, 2) == 1) { s->needed_bytes += 6; } else { s->needed_bytes += 8; } break; case MAN_SPANSION: s->needed_bytes += extract32(s->spansion_cr2v, SPANSION_DUMMY_CLK_POS, SPANSION_DUMMY_CLK_LEN ); break; default: break; } s->pos = 0; s->len = 0; s->state = STATE_COLLECTING_DATA; }
static void decode_qio_read_cmd(Flash *s) { s->needed_bytes = get_addr_length(s); /* Dummy cycles modeled with bytes writes instead of bits */ switch (get_man(s)) { case MAN_WINBOND: s->needed_bytes += WINBOND_CONTINUOUS_READ_MODE_CMD_LEN; s->needed_bytes += 4; break; case MAN_SPANSION: s->needed_bytes += SPANSION_CONTINUOUS_READ_MODE_CMD_LEN; s->needed_bytes += extract32(s->spansion_cr2v, SPANSION_DUMMY_CLK_POS, SPANSION_DUMMY_CLK_LEN ); break; case MAN_NUMONYX: s->needed_bytes += extract32(s->volatile_cfg, 4, 4); break; case MAN_MACRONIX: switch (extract32(s->volatile_cfg, 6, 2)) { case 1: s->needed_bytes += 4; break; case 2: s->needed_bytes += 8; break; default: s->needed_bytes += 6; break; } break; default: break; } s->pos = 0; s->len = 0; s->state = STATE_COLLECTING_DATA; }
static void decode_new_cmd(Flash *s, uint32_t value) { s->cmd_in_progress = value; int i; DB_PRINT_L(0, "decoded new command:%x\n", value); if (value != RESET_MEMORY) { s->reset_enable = false; } switch (value) { case ERASE_4K: case ERASE4_4K: case ERASE_32K: case ERASE4_32K: case ERASE_SECTOR: case ERASE4_SECTOR: case READ: case READ4: case DPP: case QPP: case PP: case PP4: case PP4_4: s->needed_bytes = get_addr_length(s); s->pos = 0; s->len = 0; s->state = STATE_COLLECTING_DATA; break; case FAST_READ: case FAST_READ4: case DOR: case DOR4: case QOR: case QOR4: decode_fast_read_cmd(s); break; case DIOR: case DIOR4: decode_dio_read_cmd(s); break; case QIOR: case QIOR4: decode_qio_read_cmd(s); break; case WRSR: if (s->write_enable) { switch (get_man(s)) { case MAN_SPANSION: s->needed_bytes = 2; s->state = STATE_COLLECTING_DATA; break; case MAN_MACRONIX: s->needed_bytes = 2; s->state = STATE_COLLECTING_VAR_LEN_DATA; break; default: s->needed_bytes = 1; s->state = STATE_COLLECTING_DATA; } s->pos = 0; } break; case WRDI: s->write_enable = false; break; case WREN: s->write_enable = true; break; case RDSR: s->data[0] = (!!s->write_enable) << 1; if (get_man(s) == MAN_MACRONIX) { s->data[0] |= (!!s->quad_enable) << 6; } s->pos = 0; s->len = 1; s->state = STATE_READING_DATA; break; case READ_FSR: s->data[0] = FSR_FLASH_READY; if (s->four_bytes_address_mode) { s->data[0] |= FSR_4BYTE_ADDR_MODE_ENABLED; } s->pos = 0; s->len = 1; s->state = STATE_READING_DATA; break; case JEDEC_READ: DB_PRINT_L(0, "populated jedec code\n"); for (i = 0; i < s->pi->id_len; i++) { s->data[i] = s->pi->id[i]; } s->len = s->pi->id_len; s->pos = 0; s->state = STATE_READING_DATA; break; case RDCR: s->data[0] = s->volatile_cfg & 0xFF; s->data[0] |= (!!s->four_bytes_address_mode) << 5; s->pos = 0; s->len = 1; s->state = STATE_READING_DATA; break; case BULK_ERASE: if (s->write_enable) { DB_PRINT_L(0, "chip erase\n"); flash_erase(s, 0, BULK_ERASE); } else { qemu_log_mask(LOG_GUEST_ERROR, "M25P80: chip erase with write " "protect!\n"); } break; case NOP: break; case EN_4BYTE_ADDR: s->four_bytes_address_mode = true; break; case EX_4BYTE_ADDR: s->four_bytes_address_mode = false; break; case EXTEND_ADDR_READ: s->data[0] = s->ear; s->pos = 0; s->len = 1; s->state = STATE_READING_DATA; break; case EXTEND_ADDR_WRITE: if (s->write_enable) { s->needed_bytes = 1; s->pos = 0; s->len = 0; s->state = STATE_COLLECTING_DATA; } break; case RNVCR: s->data[0] = s->nonvolatile_cfg & 0xFF; s->data[1] = (s->nonvolatile_cfg >> 8) & 0xFF; s->pos = 0; s->len = 2; s->state = STATE_READING_DATA; break; case WNVCR: if (s->write_enable && get_man(s) == MAN_NUMONYX) { s->needed_bytes = 2; s->pos = 0; s->len = 0; s->state = STATE_COLLECTING_DATA; } break; case RVCR: s->data[0] = s->volatile_cfg & 0xFF; s->pos = 0; s->len = 1; s->state = STATE_READING_DATA; break; case WVCR: if (s->write_enable) { s->needed_bytes = 1; s->pos = 0; s->len = 0; s->state = STATE_COLLECTING_DATA; } break; case REVCR: s->data[0] = s->enh_volatile_cfg & 0xFF; s->pos = 0; s->len = 1; s->state = STATE_READING_DATA; break; case WEVCR: if (s->write_enable) { s->needed_bytes = 1; s->pos = 0; s->len = 0; s->state = STATE_COLLECTING_DATA; } break; case RESET_ENABLE: s->reset_enable = true; break; case RESET_MEMORY: if (s->reset_enable) { reset_memory(s); } break; case RDCR_EQIO: switch (get_man(s)) { case MAN_SPANSION: s->data[0] = (!!s->quad_enable) << 1; s->pos = 0; s->len = 1; s->state = STATE_READING_DATA; break; case MAN_MACRONIX: s->quad_enable = true; break; default: break; } break; case RSTQIO: s->quad_enable = false; break; default: qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Unknown cmd %x\n", value); break; } }
static void reset_memory(Flash *s) { s->cmd_in_progress = NOP; s->cur_addr = 0; s->ear = 0; s->four_bytes_address_mode = false; s->len = 0; s->needed_bytes = 0; s->pos = 0; s->state = STATE_IDLE; s->write_enable = false; s->reset_enable = false; s->quad_enable = false; switch (get_man(s)) { case MAN_NUMONYX: s->volatile_cfg = 0; s->volatile_cfg |= VCFG_DUMMY; s->volatile_cfg |= VCFG_WRAP_SEQUENTIAL; if ((s->nonvolatile_cfg & NVCFG_XIP_MODE_MASK) != NVCFG_XIP_MODE_DISABLED) { s->volatile_cfg |= VCFG_XIP_MODE_ENABLED; } s->volatile_cfg |= deposit32(s->volatile_cfg, VCFG_DUMMY_CLK_POS, CFG_DUMMY_CLK_LEN, extract32(s->nonvolatile_cfg, NVCFG_DUMMY_CLK_POS, CFG_DUMMY_CLK_LEN) ); s->enh_volatile_cfg = 0; s->enh_volatile_cfg |= EVCFG_OUT_DRIVER_STRENGHT_DEF; s->enh_volatile_cfg |= EVCFG_VPP_ACCELERATOR; s->enh_volatile_cfg |= EVCFG_RESET_HOLD_ENABLED; if (s->nonvolatile_cfg & NVCFG_DUAL_IO_MASK) { s->enh_volatile_cfg |= EVCFG_DUAL_IO_ENABLED; } if (s->nonvolatile_cfg & NVCFG_QUAD_IO_MASK) { s->enh_volatile_cfg |= EVCFG_QUAD_IO_ENABLED; } if (!(s->nonvolatile_cfg & NVCFG_4BYTE_ADDR_MASK)) { s->four_bytes_address_mode = true; } if (!(s->nonvolatile_cfg & NVCFG_LOWER_SEGMENT_MASK)) { s->ear = s->size / MAX_3BYTES_SIZE - 1; } break; case MAN_MACRONIX: s->volatile_cfg = 0x7; break; case MAN_SPANSION: s->spansion_cr1v = s->spansion_cr1nv; s->spansion_cr2v = s->spansion_cr2nv; s->spansion_cr3v = s->spansion_cr3nv; s->spansion_cr4v = s->spansion_cr4nv; s->quad_enable = extract32(s->spansion_cr1v, SPANSION_QUAD_CFG_POS, SPANSION_QUAD_CFG_LEN ); s->four_bytes_address_mode = extract32(s->spansion_cr2v, SPANSION_ADDR_LEN_POS, SPANSION_ADDR_LEN_LEN ); break; default: break; } DB_PRINT_L(0, "Reset done.\n"); }
static void complete_collecting_data(Flash *s) { int i, n; n = get_addr_length(s); s->cur_addr = (n == 3 ? s->ear : 0); for (i = 0; i < n; ++i) { s->cur_addr <<= 8; s->cur_addr |= s->data[i]; } s->cur_addr &= s->size - 1; s->state = STATE_IDLE; switch (s->cmd_in_progress) { case DPP: case QPP: case PP: case PP4: case PP4_4: s->state = STATE_PAGE_PROGRAM; break; case READ: case READ4: case FAST_READ: case FAST_READ4: case DOR: case DOR4: case QOR: case QOR4: case DIOR: case DIOR4: case QIOR: case QIOR4: s->state = STATE_READ; break; case ERASE_4K: case ERASE4_4K: case ERASE_32K: case ERASE4_32K: case ERASE_SECTOR: case ERASE4_SECTOR: flash_erase(s, s->cur_addr, s->cmd_in_progress); break; case WRSR: switch (get_man(s)) { case MAN_SPANSION: s->quad_enable = !!(s->data[1] & 0x02); break; case MAN_MACRONIX: s->quad_enable = extract32(s->data[0], 6, 1); if (s->len > 1) { s->four_bytes_address_mode = extract32(s->data[1], 5, 1); } break; default: break; } if (s->write_enable) { s->write_enable = false; } break; case EXTEND_ADDR_WRITE: s->ear = s->data[0]; break; case WNVCR: s->nonvolatile_cfg = s->data[0] | (s->data[1] << 8); break; case WVCR: s->volatile_cfg = s->data[0]; break; case WEVCR: s->enh_volatile_cfg = s->data[0]; break; default: break; } }
static void complete_collecting_data(Flash *s) { int i, n; n = get_addr_length(s); s->cur_addr = (n == 3 ? s->ear : 0); for (i = 0; i < n; ++i) { s->cur_addr <<= 8; s->cur_addr |= s->data[i]; } s->cur_addr &= s->size - 1; s->state = STATE_IDLE; switch (s->cmd_in_progress) { case DPP: case QPP: case QPP_4: case PP: case PP4: case PP4_4: s->state = STATE_PAGE_PROGRAM; break; case READ: case READ4: case FAST_READ: case FAST_READ4: case DOR: case DOR4: case QOR: case QOR4: case DIOR: case DIOR4: case QIOR: case QIOR4: s->state = STATE_READ; break; case ERASE_4K: case ERASE4_4K: case ERASE_32K: case ERASE4_32K: case ERASE_SECTOR: case ERASE4_SECTOR: case DIE_ERASE: flash_erase(s, s->cur_addr, s->cmd_in_progress); break; case WRSR: switch (get_man(s)) { case MAN_SPANSION: s->quad_enable = !!(s->data[1] & 0x02); break; case MAN_MACRONIX: s->quad_enable = extract32(s->data[0], 6, 1); if (s->len > 1) { s->four_bytes_address_mode = extract32(s->data[1], 5, 1); } break; default: break; } if (s->write_enable) { s->write_enable = false; } break; case BRWR: case EXTEND_ADDR_WRITE: s->ear = s->data[0]; break; case WNVCR: s->nonvolatile_cfg = s->data[0] | (s->data[1] << 8); break; case WVCR: s->volatile_cfg = s->data[0]; break; case WEVCR: s->enh_volatile_cfg = s->data[0]; break; case RDID_90: case RDID_AB: if (get_man(s) == MAN_SST) { if (s->cur_addr <= 1) { if (s->cur_addr) { s->data[0] = s->pi->id[2]; s->data[1] = s->pi->id[0]; } else { s->data[0] = s->pi->id[0]; s->data[1] = s->pi->id[2]; } s->pos = 0; s->len = 2; s->data_read_loop = true; s->state = STATE_READING_DATA; } else { qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Invalid read id address\n"); } } else { qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Read id (command 0x90/0xAB) is not supported" " by device\n"); } break; default: break; } }