static void spi_flush_txfifo(XilinxSPI *s) { uint32_t tx; uint32_t rx; while (!fifo8_is_empty(&s->tx_fifo)) { tx = (uint32_t)fifo8_pop(&s->tx_fifo); DB_PRINT("data tx:%x\n", tx); rx = ssi_transfer(s->spi, tx); DB_PRINT("data rx:%x\n", rx); if (fifo8_is_full(&s->rx_fifo)) { s->regs[R_IPISR] |= IRQ_DRR_OVERRUN; } else { fifo8_push(&s->rx_fifo, (uint8_t)rx); if (fifo8_is_full(&s->rx_fifo)) { s->regs[R_SPISR] |= SR_RX_FULL; s->regs[R_IPISR] |= IRQ_DRR_FULL; } } s->regs[R_SPISR] &= ~SR_RX_EMPTY; s->regs[R_SPISR] &= ~SR_TX_FULL; s->regs[R_SPISR] |= SR_TX_EMPTY; s->regs[R_IPISR] |= IRQ_DTR_EMPTY; s->regs[R_IPISR] |= IRQ_DRR_NOT_EMPTY; } }
static void xilinx_spips_flush_txfifo(XilinxSPIPS *s) { for (;;) { int i; uint8_t rx; uint8_t tx = 0; for (i = 0; i < num_effective_busses(s); ++i) { if (!i || s->snoop_state == SNOOP_STRIPING) { if (fifo8_is_empty(&s->tx_fifo)) { s->regs[R_INTR_STATUS] |= IXR_TX_FIFO_UNDERFLOW; xilinx_spips_update_ixr(s); return; } else { tx = fifo8_pop(&s->tx_fifo); } } rx = ssi_transfer(s->spi[i], (uint32_t)tx); DB_PRINT("tx = %02x rx = %02x\n", tx, rx); if (!i || s->snoop_state == SNOOP_STRIPING) { if (fifo8_is_full(&s->rx_fifo)) { s->regs[R_INTR_STATUS] |= IXR_RX_FIFO_OVERFLOW; DB_PRINT("rx FIFO overflow"); } else { fifo8_push(&s->rx_fifo, (uint8_t)rx); } } } 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): break; default: s->snoop_state--; } } }
static inline void rx_data_bytes(XilinxSPIPS *s, uint8_t *value, int max) { int i; for (i = 0; i < max && !fifo8_is_empty(&s->rx_fifo); ++i) { value[i] = fifo8_pop(&s->rx_fifo); } }
static inline void rx_data_bytes(XilinxSPIPS *s, uint32_t *value, int max) { int i; *value = 0; for (i = 0; i < max && !fifo8_is_empty(&s->rx_fifo); ++i) { uint32_t next = fifo8_pop(&s->rx_fifo) & 0xFF; *value |= next << 8 * (s->regs[R_CONFIG] & ENDIAN ? 3-i : i); } }
static uint64_t spi_read(void *opaque, hwaddr addr, unsigned int size) { XilinxSPI *s = opaque; uint32_t r = 0; addr >>= 2; switch (addr) { case R_SPIDRR: if (fifo8_is_empty(&s->rx_fifo)) { DB_PRINT("Read from empty FIFO!\n"); return 0xdeadbeef; } s->regs[R_SPISR] &= ~SR_RX_FULL; r = fifo8_pop(&s->rx_fifo); if (fifo8_is_empty(&s->rx_fifo)) { s->regs[R_SPISR] |= SR_RX_EMPTY; } break; case R_SPISR: r = s->regs[addr]; break; default: if (addr < ARRAY_SIZE(s->regs)) { r = s->regs[addr]; } break; } DB_PRINT("addr=" TARGET_FMT_plx " = %x\n", addr * 4, r); xlx_spi_update_irq(s); return r; }
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 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); } } } }