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 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; } }
static void xilinx_spips_flush_txfifo(XilinxSPIPS *s) { int debug_level = 0; XilinxQSPIPS *q = (XilinxQSPIPS *) object_dynamic_cast(OBJECT(s), TYPE_XILINX_QSPIPS); for (;;) { int i; uint8_t tx = 0; uint8_t tx_rx[MAX_NUM_BUSSES] = { 0 }; uint8_t dummy_cycles = 0; uint8_t addr_length; if (fifo8_is_empty(&s->tx_fifo)) { xilinx_spips_update_ixr(s); return; } else if (s->snoop_state == SNOOP_STRIPING) { for (i = 0; i < num_effective_busses(s); ++i) { tx_rx[i] = fifo8_pop(&s->tx_fifo); } stripe8(tx_rx, num_effective_busses(s), false); } else if (s->snoop_state >= SNOOP_ADDR) { tx = fifo8_pop(&s->tx_fifo); for (i = 0; i < num_effective_busses(s); ++i) { tx_rx[i] = tx; } } else { /* Extract a dummy byte and generate dummy cycles according to the * link state */ tx = fifo8_pop(&s->tx_fifo); dummy_cycles = 8 / s->link_state; } for (i = 0; i < num_effective_busses(s); ++i) { int bus = num_effective_busses(s) - 1 - i; if (dummy_cycles) { int d; for (d = 0; d < dummy_cycles; ++d) { tx_rx[0] = ssi_transfer(s->spi[bus], (uint32_t)tx_rx[0]); } } else { DB_PRINT_L(debug_level, "tx = %02x\n", tx_rx[i]); tx_rx[i] = ssi_transfer(s->spi[bus], (uint32_t)tx_rx[i]); DB_PRINT_L(debug_level, "rx = %02x\n", tx_rx[i]); } } if (s->regs[R_CMND] & R_CMND_RXFIFO_DRAIN) { DB_PRINT_L(debug_level, "dircarding drained rx byte\n"); /* Do nothing */ } else if (s->rx_discard) { DB_PRINT_L(debug_level, "dircarding discarded rx byte\n"); s->rx_discard -= 8 / s->link_state; } else if (fifo8_is_full(&s->rx_fifo)) { s->regs[R_INTR_STATUS] |= IXR_RX_FIFO_OVERFLOW; DB_PRINT_L(0, "rx FIFO overflow"); } else if (s->snoop_state == SNOOP_STRIPING) { stripe8(tx_rx, num_effective_busses(s), true); for (i = 0; i < num_effective_busses(s); ++i) { fifo8_push(&s->rx_fifo, (uint8_t)tx_rx[i]); DB_PRINT_L(debug_level, "pushing striped rx byte\n"); } } else { DB_PRINT_L(debug_level, "pushing unstriped rx byte\n"); fifo8_push(&s->rx_fifo, (uint8_t)tx_rx[0]); } if (s->link_state_next_when) { s->link_state_next_when--; if (!s->link_state_next_when) { s->link_state = s->link_state_next; } } DB_PRINT_L(debug_level, "initial snoop state: %x\n", (unsigned)s->snoop_state); switch (s->snoop_state) { case (SNOOP_CHECKING): /* Store the count of dummy bytes in the txfifo */ s->cmd_dummies = xilinx_spips_num_dummies(q, tx); addr_length = get_addr_length(s, tx); if (s->cmd_dummies < 0) { s->snoop_state = SNOOP_NONE; } else { s->snoop_state = SNOOP_ADDR + addr_length - 1; } switch (tx) { case DPP: case DOR: case DOR_4: s->link_state_next = 2; s->link_state_next_when = addr_length + s->cmd_dummies; break; case QPP: case QPP_4: case QOR: case QOR_4: s->link_state_next = 4; s->link_state_next_when = addr_length + s->cmd_dummies; break; case DIOR: case DIOR_4: s->link_state = 2; break; case QIOR: case QIOR_4: s->link_state = 4; break; } break; case (SNOOP_ADDR): /* Address has been transmitted, transmit dummy cycles now if * needed */ if (s->cmd_dummies < 0) { s->snoop_state = SNOOP_NONE; } else { s->snoop_state = s->cmd_dummies; } break; case (SNOOP_STRIPING): case (SNOOP_NONE): /* Once we hit the boring stuff - squelch debug noise */ if (!debug_level) { DB_PRINT_L(0, "squelching debug info ....\n"); debug_level = 1; } break; default: s->snoop_state--; } DB_PRINT_L(debug_level, "final snoop state: %x\n", (unsigned)s->snoop_state); } }