static void xilinx_spips_update_cs_lines(XilinxSPIPS *s) { int i, j; bool found = false; int field = s->regs[R_CONFIG] >> CS_SHIFT; for (i = 0; i < s->num_cs; i++) { for (j = 0; j < num_effective_busses(s); j++) { int upage = !!(s->regs[R_LQSPI_STS] & LQSPI_CFG_U_PAGE); int cs_to_set = (j * s->num_cs + i + upage) % (s->num_cs * s->num_busses); if (xilinx_spips_cs_is_set(s, i, field) && !found) { DB_PRINT_L(0, "selecting slave %d\n", i); qemu_set_irq(s->cs_lines[cs_to_set], 0); } else { DB_PRINT_L(0, "deselecting slave %d\n", i); qemu_set_irq(s->cs_lines[cs_to_set], 1); } } if (xilinx_spips_cs_is_set(s, i, field)) { found = true; } } if (!found) { s->snoop_state = SNOOP_CHECKING; DB_PRINT_L(1, "moving to snoop check state\n"); } }
static void m25p80_realize(SSISlave *ss, Error **errp) { Flash *s = M25P80(ss); M25P80Class *mc = M25P80_GET_CLASS(s); int ret; s->pi = mc->pi; s->size = s->pi->sector_size * s->pi->n_sectors; s->dirty_page = -1; if (s->blk) { uint64_t perm = BLK_PERM_CONSISTENT_READ | (blk_is_read_only(s->blk) ? 0 : BLK_PERM_WRITE); ret = blk_set_perm(s->blk, perm, BLK_PERM_ALL, errp); if (ret < 0) { return; } DB_PRINT_L(0, "Binding to IF_MTD drive\n"); s->storage = blk_blockalign(s->blk, s->size); if (blk_pread(s->blk, 0, s->storage, s->size) != s->size) { error_setg(errp, "failed to read the initial flash content"); return; } } else { DB_PRINT_L(0, "No BDRV - binding to RAM\n"); s->storage = blk_blockalign(NULL, s->size); memset(s->storage, 0xFF, s->size); } }
static uint64_t xilinx_spips_read(void *opaque, hwaddr addr, unsigned size) { XilinxSPIPS *s = opaque; uint32_t mask = ~0; uint32_t ret; uint8_t rx_buf[4]; addr >>= 2; switch (addr) { case R_CONFIG: mask = ~(R_CONFIG_RSVD | MAN_START_COM); break; case R_INTR_STATUS: ret = s->regs[addr] & IXR_ALL; s->regs[addr] = 0; DB_PRINT_L(0, "addr=" TARGET_FMT_plx " = %x\n", addr * 4, ret); return ret; case R_INTR_MASK: mask = IXR_ALL; break; case R_EN: mask = 0x1; break; case R_SLAVE_IDLE_COUNT: mask = 0xFF; break; case R_MOD_ID: mask = 0x01FFFFFF; break; case R_INTR_EN: case R_INTR_DIS: case R_TX_DATA: mask = 0; break; case R_RX_DATA: memset(rx_buf, 0, sizeof(rx_buf)); rx_data_bytes(s, rx_buf, s->num_txrx_bytes); ret = s->regs[R_CONFIG] & ENDIAN ? cpu_to_be32(*(uint32_t *)rx_buf) : cpu_to_le32(*(uint32_t *)rx_buf); DB_PRINT_L(0, "addr=" TARGET_FMT_plx " = %x\n", addr * 4, ret); xilinx_spips_update_ixr(s); return ret; } DB_PRINT_L(0, "addr=" TARGET_FMT_plx " = %x\n", addr * 4, s->regs[addr] & mask); return s->regs[addr] & mask; }
static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx) { Flash *s = M25P80(ss); uint32_t r = 0; switch (s->state) { case STATE_PAGE_PROGRAM: DB_PRINT_L(1, "page program cur_addr=%#" PRIx32 " data=%" PRIx8 "\n", s->cur_addr, (uint8_t)tx); flash_write8(s, s->cur_addr, (uint8_t)tx); s->cur_addr = (s->cur_addr + 1) & (s->size - 1); break; case STATE_READ: r = s->storage[s->cur_addr]; DB_PRINT_L(1, "READ 0x%" PRIx32 "=%" PRIx8 "\n", s->cur_addr, (uint8_t)r); s->cur_addr = (s->cur_addr + 1) & (s->size - 1); break; case STATE_COLLECTING_DATA: case STATE_COLLECTING_VAR_LEN_DATA: s->data[s->len] = (uint8_t)tx; s->len++; if (s->len == s->needed_bytes) { complete_collecting_data(s); } break; case STATE_READING_DATA: r = s->data[s->pos]; s->pos++; if (s->pos == s->len) { s->pos = 0; s->state = STATE_IDLE; } break; default: case STATE_IDLE: decode_new_cmd(s, (uint8_t)tx); break; } return r; }
static void flash_erase(Flash *s, int offset, FlashCMD cmd) { uint32_t len; uint8_t capa_to_assert = 0; switch (cmd) { case ERASE_4K: case ERASE4_4K: len = 4 << 10; capa_to_assert = ER_4K; break; case ERASE_32K: case ERASE4_32K: len = 32 << 10; capa_to_assert = ER_32K; break; case ERASE_SECTOR: case ERASE4_SECTOR: len = s->pi->sector_size; break; case BULK_ERASE: len = s->size; break; case DIE_ERASE: if (s->pi->die_cnt) { len = s->size / s->pi->die_cnt; offset = offset & (~(len - 1)); } else { qemu_log_mask(LOG_GUEST_ERROR, "M25P80: die erase is not supported" " by device\n"); return; } break; default: abort(); } DB_PRINT_L(0, "offset = %#x, len = %d\n", offset, len); if ((s->pi->flags & capa_to_assert) != capa_to_assert) { qemu_log_mask(LOG_GUEST_ERROR, "M25P80: %d erase size not supported by" " device\n", len); } if (!s->write_enable) { qemu_log_mask(LOG_GUEST_ERROR, "M25P80: erase with write protect!\n"); return; } memset(s->storage + offset, 0xff, len); flash_sync_area(s, offset, len); }
static void m25p80_realize(SSISlave *ss, Error **errp) { Flash *s = M25P80(ss); M25P80Class *mc = M25P80_GET_CLASS(s); s->pi = mc->pi; s->size = s->pi->sector_size * s->pi->n_sectors; s->dirty_page = -1; if (s->blk) { DB_PRINT_L(0, "Binding to IF_MTD drive\n"); s->storage = blk_blockalign(s->blk, s->size); if (blk_pread(s->blk, 0, s->storage, s->size) != s->size) { error_setg(errp, "failed to read the initial flash content"); return; } } else { DB_PRINT_L(0, "No BDRV - binding to RAM\n"); s->storage = blk_blockalign(NULL, s->size); memset(s->storage, 0xFF, s->size); } }
static void xilinx_spips_update_cs(XilinxSPIPS *s, int field) { int i; for (i = 0; i < s->num_cs; i++) { bool old_state = s->cs_lines_state[i]; bool new_state = field & (1 << i); if (old_state != new_state) { s->cs_lines_state[i] = new_state; s->rx_discard = ARRAY_FIELD_EX32(s->regs, CMND, RX_DISCARD); DB_PRINT_L(1, "%sselecting slave %d\n", new_state ? "" : "de", i); } qemu_set_irq(s->cs_lines[i], !new_state); } if (!(field & ((1 << s->num_cs) - 1))) { s->snoop_state = SNOOP_CHECKING; s->cmd_dummies = 0; s->link_state = 1; s->link_state_next = 1; s->link_state_next_when = 0; DB_PRINT_L(1, "moving to snoop check state\n"); } }
static int m25p80_cs(SSISlave *ss, bool select) { Flash *s = M25P80(ss); if (select) { if (s->state == STATE_COLLECTING_VAR_LEN_DATA) { complete_collecting_data(s); } s->len = 0; s->pos = 0; s->state = STATE_IDLE; flash_sync_dirty(s, -1); } DB_PRINT_L(0, "%sselect\n", select ? "de" : ""); return 0; }
static inline void flash_write8(Flash *s, uint64_t addr, uint8_t data) { int64_t page = addr / s->pi->page_size; uint8_t prev = s->storage[s->cur_addr]; if (!s->write_enable) { qemu_log_mask(LOG_GUEST_ERROR, "M25P80: write with write protect!\n"); } if ((prev ^ data) & data) { DB_PRINT_L(1, "programming zero to one! addr=%" PRIx64 " %" PRIx8 " -> %" PRIx8 "\n", addr, prev, data); } if (s->pi->flags & WR_1) { s->storage[s->cur_addr] = data; } else { s->storage[s->cur_addr] &= data; } flash_sync_dirty(s, page); s->dirty_page = page; }
static void decode_new_cmd(Flash *s, uint32_t value) { s->cmd_in_progress = value; DB_PRINT_L(0, "decoded new command:%x\n", value); switch (value) { case ERASE_4K: case ERASE_32K: case ERASE_SECTOR: case READ: case DPP: case QPP: case PP: s->needed_bytes = 3; s->pos = 0; s->len = 0; s->state = STATE_COLLECTING_DATA; break; case FAST_READ: case DOR: case QOR: s->needed_bytes = 4; s->pos = 0; s->len = 0; s->state = STATE_COLLECTING_DATA; break; case DIOR: switch ((s->pi->jedec >> 16) & 0xFF) { case JEDEC_WINBOND: case JEDEC_SPANSION: s->needed_bytes = 4; break; case JEDEC_NUMONYX: default: s->needed_bytes = 5; } s->pos = 0; s->len = 0; s->state = STATE_COLLECTING_DATA; break; case QIOR: switch ((s->pi->jedec >> 16) & 0xFF) { case JEDEC_WINBOND: case JEDEC_SPANSION: s->needed_bytes = 6; break; case JEDEC_NUMONYX: default: s->needed_bytes = 8; } s->pos = 0; s->len = 0; s->state = STATE_COLLECTING_DATA; break; case WRSR: if (s->write_enable) { s->needed_bytes = 1; s->pos = 0; s->len = 0; s->state = STATE_COLLECTING_DATA; } break; case WRDI: s->write_enable = false; break; case WREN: s->write_enable = true; break; case RDSR: s->data[0] = (!!s->write_enable) << 1; s->pos = 0; s->len = 1; s->state = STATE_READING_DATA; break; case JEDEC_READ: DB_PRINT_L(0, "populated jedec code\n"); s->data[0] = (s->pi->jedec >> 16) & 0xff; s->data[1] = (s->pi->jedec >> 8) & 0xff; s->data[2] = s->pi->jedec & 0xff; if (s->pi->ext_jedec) { s->data[3] = (s->pi->ext_jedec >> 8) & 0xff; s->data[4] = s->pi->ext_jedec & 0xff; s->len = 5; } else {
static void xilinx_spips_flush_txfifo(XilinxSPIPS *s) { int debug_level = 0; for (;;) { int i; uint8_t tx = 0; uint8_t tx_rx[num_effective_busses(s)]; if (fifo8_is_empty(&s->tx_fifo)) { if (!(s->regs[R_LQSPI_CFG] & LQSPI_CFG_LQ_MODE)) { s->regs[R_INTR_STATUS] |= IXR_TX_FIFO_UNDERFLOW; } 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 { tx = fifo8_pop(&s->tx_fifo); for (i = 0; i < num_effective_busses(s); ++i) { tx_rx[i] = tx; } } for (i = 0; i < num_effective_busses(s); ++i) { DB_PRINT_L(debug_level, "tx = %02x\n", tx_rx[i]); tx_rx[i] = ssi_transfer(s->spi[i], (uint32_t)tx_rx[i]); DB_PRINT_L(debug_level, "rx = %02x\n", tx_rx[i]); } 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]); } } else { fifo8_push(&s->rx_fifo, (uint8_t)tx_rx[0]); } DB_PRINT_L(debug_level, "initial snoop state: %x\n", (unsigned)s->snoop_state); switch (s->snoop_state) { case (SNOOP_CHECKING): switch (tx) { /* new instruction code */ case READ: /* 3 address bytes, no dummy bytes/cycles */ case PP: case DPP: case QPP: s->snoop_state = 3; break; case FAST_READ: /* 3 address bytes, 1 dummy byte */ case DOR: case QOR: case DIOR: /* FIXME: these vary between vendor - set to spansion */ s->snoop_state = 4; break; case QIOR: /* 3 address bytes, 2 dummy bytes */ s->snoop_state = 6; break; default: s->snoop_state = SNOOP_NONE; } 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); } }
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 uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx) { Flash *s = M25P80(ss); uint32_t r = 0; switch (s->state) { case STATE_PAGE_PROGRAM: DB_PRINT_L(1, "page program cur_addr=%#" PRIx32 " data=%" PRIx8 "\n", s->cur_addr, (uint8_t)tx); flash_write8(s, s->cur_addr, (uint8_t)tx); s->cur_addr = (s->cur_addr + 1) & (s->size - 1); break; case STATE_READ: r = s->storage[s->cur_addr]; DB_PRINT_L(1, "READ 0x%" PRIx32 "=%" PRIx8 "\n", s->cur_addr, (uint8_t)r); s->cur_addr = (s->cur_addr + 1) & (s->size - 1); break; case STATE_COLLECTING_DATA: case STATE_COLLECTING_VAR_LEN_DATA: if (s->len >= M25P80_INTERNAL_DATA_BUFFER_SZ) { qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Write overrun internal data buffer. " "SPI controller (QEMU emulator or guest driver) " "is misbehaving\n"); s->len = s->pos = 0; s->state = STATE_IDLE; break; } s->data[s->len] = (uint8_t)tx; s->len++; if (s->len == s->needed_bytes) { complete_collecting_data(s); } break; case STATE_READING_DATA: if (s->pos >= M25P80_INTERNAL_DATA_BUFFER_SZ) { qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Read overrun internal data buffer. " "SPI controller (QEMU emulator or guest driver) " "is misbehaving\n"); s->len = s->pos = 0; s->state = STATE_IDLE; break; } r = s->data[s->pos]; s->pos++; if (s->pos == s->len) { s->pos = 0; if (!s->data_read_loop) { s->state = STATE_IDLE; } } break; default: case STATE_IDLE: decode_new_cmd(s, (uint8_t)tx); break; } return r; }
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); } }
static void xlnx_zynqmp_qspips_flush_fifo_g(XlnxZynqMPQSPIPS *s) { while (s->regs[R_GQSPI_DATA_STS] || !fifo32_is_empty(&s->fifo_g)) { uint8_t tx_rx[2] = { 0 }; int num_stripes = 1; uint8_t busses; int i; if (!s->regs[R_GQSPI_DATA_STS]) { uint8_t imm; s->regs[R_GQSPI_GF_SNAPSHOT] = fifo32_pop(&s->fifo_g); DB_PRINT_L(0, "GQSPI command: %x\n", s->regs[R_GQSPI_GF_SNAPSHOT]); if (!s->regs[R_GQSPI_GF_SNAPSHOT]) { DB_PRINT_L(0, "Dummy GQSPI Delay Command Entry, Do nothing"); continue; } xlnx_zynqmp_qspips_update_cs_lines(s); imm = ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, IMMEDIATE_DATA); if (!ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, DATA_XFER)) { /* immedate transfer */ if (ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, TRANSMIT) || ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, RECIEVE)) { s->regs[R_GQSPI_DATA_STS] = 1; /* CS setup/hold - do nothing */ } else { s->regs[R_GQSPI_DATA_STS] = 0; } } else if (ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, EXPONENT)) { if (imm > 31) { qemu_log_mask(LOG_UNIMP, "QSPI exponential transfer too" " long - 2 ^ %" PRId8 " requested\n", imm); } s->regs[R_GQSPI_DATA_STS] = 1ul << imm; } else { s->regs[R_GQSPI_DATA_STS] = imm; } } /* Zero length transfer check */ if (!s->regs[R_GQSPI_DATA_STS]) { continue; } if (ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, RECIEVE) && fifo8_is_full(&s->rx_fifo_g)) { /* No space in RX fifo for transfer - try again later */ return; } if (ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, STRIPE) && (ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, TRANSMIT) || ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, RECIEVE))) { num_stripes = 2; } if (!ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, DATA_XFER)) { tx_rx[0] = ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, IMMEDIATE_DATA); } else if (ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, TRANSMIT)) { for (i = 0; i < num_stripes; ++i) { if (!fifo8_is_empty(&s->tx_fifo_g)) { tx_rx[i] = fifo8_pop(&s->tx_fifo_g); s->tx_fifo_g_align++; } else { return; } } } if (num_stripes == 1) { /* mirror */ tx_rx[1] = tx_rx[0]; } busses = ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, DATA_BUS_SELECT); for (i = 0; i < 2; ++i) { DB_PRINT_L(1, "bus %d tx = %02x\n", i, tx_rx[i]); tx_rx[i] = ssi_transfer(XILINX_SPIPS(s)->spi[i], tx_rx[i]); DB_PRINT_L(1, "bus %d rx = %02x\n", i, tx_rx[i]); } if (s->regs[R_GQSPI_DATA_STS] > 1 && busses == 0x3 && num_stripes == 2) { s->regs[R_GQSPI_DATA_STS] -= 2; } else if (s->regs[R_GQSPI_DATA_STS] > 0) { s->regs[R_GQSPI_DATA_STS]--; } if (ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, RECIEVE)) { for (i = 0; i < 2; ++i) { if (busses & (1 << i)) { DB_PRINT_L(1, "bus %d push_byte = %02x\n", i, tx_rx[i]); fifo8_push(&s->rx_fifo_g, tx_rx[i]); s->rx_fifo_g_align++; } } } if (!s->regs[R_GQSPI_DATA_STS]) { for (; s->tx_fifo_g_align % 4; s->tx_fifo_g_align++) { fifo8_pop(&s->tx_fifo_g); } for (; s->rx_fifo_g_align % 4; s->rx_fifo_g_align++) { fifo8_push(&s->rx_fifo_g, 0); } } } }