static uint64_t imx_spi_read(void *opaque, hwaddr offset, unsigned size) { uint32_t value = 0; IMXSPIState *s = opaque; uint32_t index = offset >> 2; if (index >= ECSPI_MAX) { qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" HWADDR_PRIx "\n", TYPE_IMX_SPI, __func__, offset); return 0; } switch (index) { case ECSPI_RXDATA: if (!imx_spi_is_enabled(s)) { value = 0; } else if (fifo32_is_empty(&s->rx_fifo)) { /* value is undefined */ value = 0xdeadbeef; } else { /* read from the RX FIFO */ value = fifo32_pop(&s->rx_fifo); } break; case ECSPI_TXDATA: qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to read from TX FIFO\n", TYPE_IMX_SPI, __func__); /* Reading from TXDATA gives 0 */ break; case ECSPI_MSGDATA: qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to read from MSG FIFO\n", TYPE_IMX_SPI, __func__); /* Reading from MSGDATA gives 0 */ break; default: value = s->regs[index]; break; } DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx_spi_reg_name(index), value); imx_spi_update_irq(s); return (uint64_t)value; }
static void imx_spi_flush_txfifo(IMXSPIState *s) { uint32_t tx; uint32_t rx; DPRINTF("Begin: TX Fifo Size = %d, RX Fifo Size = %d\n", fifo32_num_used(&s->tx_fifo), fifo32_num_used(&s->rx_fifo)); while (!fifo32_is_empty(&s->tx_fifo)) { int tx_burst = 0; int index = 0; if (s->burst_length <= 0) { s->burst_length = imx_spi_burst_length(s); DPRINTF("Burst length = %d\n", s->burst_length); if (imx_spi_is_multiple_master_burst(s)) { s->regs[ECSPI_CONREG] |= ECSPI_CONREG_XCH; } } tx = fifo32_pop(&s->tx_fifo); DPRINTF("data tx:0x%08x\n", tx); tx_burst = MIN(s->burst_length, 32); rx = 0; while (tx_burst) { uint8_t byte = tx & 0xff; DPRINTF("writing 0x%02x\n", (uint32_t)byte); /* We need to write one byte at a time */ byte = ssi_transfer(s->bus, byte); DPRINTF("0x%02x read\n", (uint32_t)byte); tx = tx >> 8; rx |= (byte << (index * 8)); /* Remove 8 bits from the actual burst */ tx_burst -= 8; s->burst_length -= 8; index++; } DPRINTF("data rx:0x%08x\n", rx); if (fifo32_is_full(&s->rx_fifo)) { s->regs[ECSPI_STATREG] |= ECSPI_STATREG_RO; } else { fifo32_push(&s->rx_fifo, (uint8_t)rx); } if (s->burst_length <= 0) { s->regs[ECSPI_CONREG] &= ~ECSPI_CONREG_XCH; if (!imx_spi_is_multiple_master_burst(s)) { s->regs[ECSPI_STATREG] |= ECSPI_STATREG_TC; break; } } } if (fifo32_is_empty(&s->tx_fifo)) { s->regs[ECSPI_STATREG] |= ECSPI_STATREG_TC; } /* TODO: We should also use TDR and RDR bits */ DPRINTF("End: TX Fifo Size = %d, RX Fifo Size = %d\n", fifo32_num_used(&s->tx_fifo), fifo32_num_used(&s->rx_fifo)); }
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); } } } }