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 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 bool fifo_update_and_get(IVShmemState *s, const uint8_t *buf, int size, void *data, size_t len) { const uint8_t *p; uint32_t num; assert(len <= sizeof(int64_t)); /* limitation of the fifo */ if (fifo8_is_empty(&s->incoming_fifo) && size == len) { memcpy(data, buf, size); return true; } IVSHMEM_DPRINTF("short read of %d bytes\n", size); num = MIN(size, sizeof(int64_t) - fifo8_num_used(&s->incoming_fifo)); fifo8_push_all(&s->incoming_fifo, buf, num); if (fifo8_num_used(&s->incoming_fifo) < len) { assert(num == 0); return false; } size -= num; buf += num; p = fifo8_pop_buf(&s->incoming_fifo, len, &num); assert(num == len); memcpy(data, p, len); if (size > 0) { fifo8_push_all(&s->incoming_fifo, buf, size); } return true; }
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_update_ixr(XilinxSPIPS *s) { if (!(s->regs[R_LQSPI_CFG] & LQSPI_CFG_LQ_MODE)) { s->regs[R_INTR_STATUS] &= ~IXR_SELF_CLEAR; s->regs[R_INTR_STATUS] |= (fifo8_is_full(&s->rx_fifo) ? IXR_RX_FIFO_FULL : 0) | (s->rx_fifo.num >= s->regs[R_RX_THRES] ? IXR_RX_FIFO_NOT_EMPTY : 0) | (fifo8_is_full(&s->tx_fifo) ? IXR_TX_FIFO_FULL : 0) | (fifo8_is_empty(&s->tx_fifo) ? IXR_TX_FIFO_EMPTY : 0) | (s->tx_fifo.num < s->regs[R_TX_THRES] ? IXR_TX_FIFO_NOT_FULL : 0); } int new_irqline = !!(s->regs[R_INTR_MASK] & s->regs[R_INTR_STATUS] & IXR_ALL); if (new_irqline != s->irqline) { s->irqline = new_irqline; qemu_set_irq(s->irq, s->irqline); } }
static void xlx_spi_update_irq(XilinxSPI *s) { uint32_t pending; s->regs[R_IPISR] |= (!fifo8_is_empty(&s->rx_fifo) ? IRQ_DRR_NOT_EMPTY : 0) | (fifo8_is_full(&s->rx_fifo) ? IRQ_DRR_FULL : 0); pending = s->regs[R_IPISR] & s->regs[R_IPIER]; pending = pending && (s->regs[R_DGIER] & R_DGIER_IE); pending = !!pending; /* This call lies right in the data paths so don't call the irq chain unless things really changed. */ if (pending != s->irqline) { s->irqline = pending; DB_PRINT("irq_change of state %d ISR:%x IER:%X\n", pending, s->regs[R_IPISR], s->regs[R_IPIER]); qemu_set_irq(s->irq, pending); } }
static void xilinx_spips_update_cs_lines(XilinxSPIPS *s) { int field = ~((s->regs[R_CONFIG] & CS) >> CS_SHIFT); /* In dual parallel, mirror low CS to both */ if (num_effective_busses(s) == 2) { /* Single bit chip-select for qspi */ field &= 0x1; field |= field << 1; /* Dual stack U-Page */ } else if (s->regs[R_LQSPI_CFG] & LQSPI_CFG_TWO_MEM && s->regs[R_LQSPI_STS] & LQSPI_CFG_U_PAGE) { /* Single bit chip-select for qspi */ field &= 0x1; /* change from CS0 to CS1 */ field <<= 1; } /* Auto CS */ if (!(s->regs[R_CONFIG] & MANUAL_CS) && fifo8_is_empty(&s->tx_fifo)) { field = 0; } xilinx_spips_update_cs(s, field); }
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 inline bool xilinx_spips_cs_is_set(XilinxSPIPS *s, int i, int field) { return ~field & (1 << i) && (s->regs[R_CONFIG] & MANUAL_CS || !fifo8_is_empty(&s->tx_fifo)); }
static void ivshmem_read(void *opaque, const uint8_t *buf, int size) { IVShmemState *s = opaque; int incoming_fd, tmp_fd; int guest_max_eventfd; long incoming_posn; if (fifo8_is_empty(&s->incoming_fifo) && size == sizeof(incoming_posn)) { memcpy(&incoming_posn, buf, size); } else { const uint8_t *p; uint32_t num; IVSHMEM_DPRINTF("short read of %d bytes\n", size); num = MAX(size, sizeof(long) - fifo8_num_used(&s->incoming_fifo)); fifo8_push_all(&s->incoming_fifo, buf, num); if (fifo8_num_used(&s->incoming_fifo) < sizeof(incoming_posn)) { return; } size -= num; buf += num; p = fifo8_pop_buf(&s->incoming_fifo, sizeof(incoming_posn), &num); g_assert(num == sizeof(incoming_posn)); memcpy(&incoming_posn, p, sizeof(incoming_posn)); if (size > 0) { fifo8_push_all(&s->incoming_fifo, buf, size); } } if (incoming_posn < -1) { IVSHMEM_DPRINTF("invalid incoming_posn %ld\n", incoming_posn); return; } /* pick off s->server_chr->msgfd and store it, posn should accompany msg */ tmp_fd = qemu_chr_fe_get_msgfd(s->server_chr); IVSHMEM_DPRINTF("posn is %ld, fd is %d\n", incoming_posn, tmp_fd); /* make sure we have enough space for this guest */ if (incoming_posn >= s->nb_peers) { if (increase_dynamic_storage(s, incoming_posn) < 0) { error_report("increase_dynamic_storage() failed"); if (tmp_fd != -1) { close(tmp_fd); } return; } } if (tmp_fd == -1) { /* if posn is positive and unseen before then this is our posn*/ if ((incoming_posn >= 0) && (s->peers[incoming_posn].eventfds == NULL)) { /* receive our posn */ s->vm_id = incoming_posn; return; } else { /* otherwise an fd == -1 means an existing guest has gone away */ IVSHMEM_DPRINTF("posn %ld has gone away\n", incoming_posn); close_guest_eventfds(s, incoming_posn); return; } } /* because of the implementation of get_msgfd, we need a dup */ incoming_fd = dup(tmp_fd); if (incoming_fd == -1) { error_report("could not allocate file descriptor %s", strerror(errno)); close(tmp_fd); return; } /* if the position is -1, then it's shared memory region fd */ if (incoming_posn == -1) { void * map_ptr; s->max_peer = 0; if (check_shm_size(s, incoming_fd) == -1) { exit(1); } /* mmap the region and map into the BAR2 */ map_ptr = mmap(0, s->ivshmem_size, PROT_READ|PROT_WRITE, MAP_SHARED, incoming_fd, 0); memory_region_init_ram_ptr(&s->ivshmem, OBJECT(s), "ivshmem.bar2", s->ivshmem_size, map_ptr); vmstate_register_ram(&s->ivshmem, DEVICE(s)); IVSHMEM_DPRINTF("guest h/w addr = %p, size = %" PRIu64 "\n", map_ptr, s->ivshmem_size); memory_region_add_subregion(&s->bar, 0, &s->ivshmem); /* only store the fd if it is successfully mapped */ s->shm_fd = incoming_fd; return; } /* each guest has an array of eventfds, and we keep track of how many * guests for each VM */ guest_max_eventfd = s->peers[incoming_posn].nb_eventfds; if (guest_max_eventfd == 0) { /* one eventfd per MSI vector */ s->peers[incoming_posn].eventfds = g_new(EventNotifier, s->vectors); } /* this is an eventfd for a particular guest VM */ IVSHMEM_DPRINTF("eventfds[%ld][%d] = %d\n", incoming_posn, guest_max_eventfd, incoming_fd); event_notifier_init_fd(&s->peers[incoming_posn].eventfds[guest_max_eventfd], incoming_fd); /* increment count for particular guest */ s->peers[incoming_posn].nb_eventfds++; /* keep track of the maximum VM ID */ if (incoming_posn > s->max_peer) { s->max_peer = incoming_posn; } if (incoming_posn == s->vm_id) { s->eventfd_chr[guest_max_eventfd] = create_eventfd_chr_device(s, &s->peers[s->vm_id].eventfds[guest_max_eventfd], guest_max_eventfd); } if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) { ivshmem_add_eventfd(s, incoming_posn, guest_max_eventfd); } }
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); } } } }