summaryrefslogtreecommitdiff
path: root/hw/ssi
diff options
context:
space:
mode:
authorPeter Crosthwaite <peter.crosthwaite@xilinx.com>2013-06-03 17:17:43 +0100
committerPeter Maydell <peter.maydell@linaro.org>2013-06-03 17:17:43 +0100
commit9151da25a71a171d19beec107a8ee2066cb65e7c (patch)
treedba3022e3040fad5b4c80e260fab3c026c008ad0 /hw/ssi
parent2133a5f6b8f8941a6a3734c6c1990656553de76c (diff)
downloadqemu-9151da25a71a171d19beec107a8ee2066cb65e7c.tar.gz
xilinx_spips: Fix striping behaviour
The QSPI controller was using byte-wide stripes when striping across the two flashes in dual parallel mode. The real hardware however uses individual bit striping. QEMU misbehaves in the (corner) case where data is written/read in dual-parallel mode and read/written back in single mode. Signed-off-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Edgar E. Iglesias <edgar.iglesias@gmail.com> Message-id: 718a61df1bf746ec06f6da44d12f8317af7b08ce.1369117359.git.peter.crosthwaite@xilinx.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw/ssi')
-rw-r--r--hw/ssi/xilinx_spips.c84
1 files changed, 63 insertions, 21 deletions
diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c
index 3e9e76c165..7222e152e6 100644
--- a/hw/ssi/xilinx_spips.c
+++ b/hw/ssi/xilinx_spips.c
@@ -273,35 +273,77 @@ static void xilinx_spips_reset(DeviceState *d)
xilinx_spips_update_cs_lines(s);
}
+/* N way (num) in place bit striper. Lay out row wise bits (LSB to MSB)
+ * column wise (from element 0 to N-1). num is the length of x, and dir
+ * reverses the direction of the transform. Best illustrated by example:
+ * Each digit in the below array is a single bit (num == 3):
+ *
+ * {{ 76543210, } ----- stripe (dir == false) -----> {{ FCheb630, }
+ * { hgfedcba, } { GDAfc741, }
+ * { HGFEDCBA, }} <---- upstripe (dir == true) ----- { HEBgda52, }}
+ */
+
+static inline void stripe8(uint8_t *x, int num, bool dir)
+{
+ uint8_t r[num];
+ memset(r, 0, sizeof(uint8_t) * num);
+ int idx[2] = {0, 0};
+ int bit[2] = {0, 0};
+ int d = dir;
+
+ for (idx[0] = 0; idx[0] < num; ++idx[0]) {
+ for (bit[0] = 0; bit[0] < 8; ++bit[0]) {
+ r[idx[d]] |= x[idx[!d]] & 1 << bit[!d] ? 1 << bit[d] : 0;
+ idx[1] = (idx[1] + 1) % num;
+ if (!idx[1]) {
+ bit[1]++;
+ }
+ }
+ }
+ memcpy(x, r, sizeof(uint8_t) * num);
+}
+
static void xilinx_spips_flush_txfifo(XilinxSPIPS *s)
{
for (;;) {
int i;
- uint8_t rx;
uint8_t tx = 0;
+ uint8_t tx_rx[num_effective_busses(s)];
- for (i = 0; i < num_effective_busses(s); ++i) {
- if (!i || s->snoop_state == SNOOP_STRIPING) {
- 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 {
- tx = fifo8_pop(&s->tx_fifo);
- }
+ 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;
}
- 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);
- }
+ 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("tx = %02x\n", tx_rx[i]);
+ tx_rx[i] = ssi_transfer(s->spi[i], (uint32_t)tx_rx[i]);
+ DB_PRINT("rx = %02x\n", tx_rx[i]);
+ }
+
+ if (fifo8_is_full(&s->rx_fifo)) {
+ s->regs[R_INTR_STATUS] |= IXR_RX_FIFO_OVERFLOW;
+ DB_PRINT("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]);
}
switch (s->snoop_state) {