static void imx_spi_update_irq(IMXSPIState *s) { int level; if (fifo32_is_empty(&s->rx_fifo)) { s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_RR; } else { s->regs[ECSPI_STATREG] |= ECSPI_STATREG_RR; } if (fifo32_is_full(&s->rx_fifo)) { s->regs[ECSPI_STATREG] |= ECSPI_STATREG_RF; } else { s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_RF; } if (fifo32_is_empty(&s->tx_fifo)) { s->regs[ECSPI_STATREG] |= ECSPI_STATREG_TE; } else { s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_TE; } if (fifo32_is_full(&s->tx_fifo)) { s->regs[ECSPI_STATREG] |= ECSPI_STATREG_TF; } else { s->regs[ECSPI_STATREG] &= ~ECSPI_STATREG_TF; } level = s->regs[ECSPI_STATREG] & s->regs[ECSPI_INTREG] ? 1 : 0; qemu_set_irq(s->irq, level); DPRINTF("IRQ level is %d\n", level); }
static void xlnx_zynqmp_qspips_update_ixr(XlnxZynqMPQSPIPS *s) { uint32_t gqspi_int; int new_irqline; s->regs[R_GQSPI_ISR] &= ~IXR_SELF_CLEAR; s->regs[R_GQSPI_ISR] |= (fifo32_is_empty(&s->fifo_g) ? IXR_GENERIC_FIFO_EMPTY : 0) | (fifo32_is_full(&s->fifo_g) ? IXR_GENERIC_FIFO_FULL : 0) | (s->fifo_g.fifo.num < s->regs[R_GQSPI_GFIFO_THRESH] ? IXR_GENERIC_FIFO_NOT_FULL : 0) | (fifo8_is_empty(&s->rx_fifo_g) ? IXR_RX_FIFO_EMPTY : 0) | (fifo8_is_full(&s->rx_fifo_g) ? IXR_RX_FIFO_FULL : 0) | (s->rx_fifo_g.num >= s->regs[R_GQSPI_RX_THRESH] ? IXR_RX_FIFO_NOT_EMPTY : 0) | (fifo8_is_empty(&s->tx_fifo_g) ? IXR_TX_FIFO_EMPTY : 0) | (fifo8_is_full(&s->tx_fifo_g) ? IXR_TX_FIFO_FULL : 0) | (s->tx_fifo_g.num < s->regs[R_GQSPI_TX_THRESH] ? IXR_TX_FIFO_NOT_FULL : 0); /* GQSPI Interrupt Trigger Status */ gqspi_int = (~s->regs[R_GQSPI_IMR]) & s->regs[R_GQSPI_ISR] & GQSPI_IXR_MASK; new_irqline = !!(gqspi_int & IXR_ALL); /* drive external interrupt pin */ if (new_irqline != s->gqspi_irqline) { s->gqspi_irqline = new_irqline; qemu_set_irq(XILINX_SPIPS(s)->irq, s->gqspi_irqline); } }
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_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { IMXSPIState *s = opaque; uint32_t index = offset >> 2; uint32_t change_mask; 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; } DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx_spi_reg_name(index), (uint32_t)value); change_mask = s->regs[index] ^ value; switch (index) { case ECSPI_RXDATA: qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to write to RX FIFO\n", TYPE_IMX_SPI, __func__); break; case ECSPI_TXDATA: case ECSPI_MSGDATA: /* Is there any difference between TXDATA and MSGDATA ? */ /* I'll have to look in the linux driver */ if (!imx_spi_is_enabled(s)) { /* Ignore writes if device is disabled */ break; } else if (fifo32_is_full(&s->tx_fifo)) { /* Ignore writes if queue is full */ break; } fifo32_push(&s->tx_fifo, (uint32_t)value); if (imx_spi_channel_is_master(s) && (s->regs[ECSPI_CONREG] & ECSPI_CONREG_SMC)) { /* * Start emitting if current channel is master and SMC bit is * set. */ imx_spi_flush_txfifo(s); } break; case ECSPI_STATREG: /* the RO and TC bits are write-one-to-clear */ value &= ECSPI_STATREG_RO | ECSPI_STATREG_TC; s->regs[ECSPI_STATREG] &= ~value; break; case ECSPI_CONREG: s->regs[ECSPI_CONREG] = value; if (!imx_spi_is_enabled(s)) { /* device is disabled, so this is a reset */ imx_spi_reset(DEVICE(s)); return; } if (imx_spi_channel_is_master(s)) { int i; /* We are in master mode */ for (i = 0; i < 4; i++) { qemu_set_irq(s->cs_lines[i], i == imx_spi_selected_channel(s) ? 0 : 1); } if ((value & change_mask & ECSPI_CONREG_SMC) && !fifo32_is_empty(&s->tx_fifo)) { /* SMC bit is set and TX FIFO has some slots filled in */ imx_spi_flush_txfifo(s); } else if ((value & change_mask & ECSPI_CONREG_XCH) && !(value & ECSPI_CONREG_SMC)) { /* This is a request to start emitting */ imx_spi_flush_txfifo(s); } } break; default: s->regs[index] = value; break; } imx_spi_update_irq(s); }
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); } } } }