With SPI-GPIO we don't have the input bits until all 8 bits of the output have been shifted out, so we have to prime the MISO with bogus values (0xFF).
Signed-off-by: Iris Chen <irische...@fb.com> --- hw/block/m25p80.c | 29 ++++++++++++++++++++++++++++- hw/ssi/ssi.c | 4 ---- include/hw/ssi/ssi.h | 5 +++++ 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index a8d2519141..9b26bb96e5 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -1462,7 +1462,7 @@ static int m25p80_cs(SSIPeripheral *ss, bool select) return 0; } -static uint32_t m25p80_transfer8(SSIPeripheral *ss, uint32_t tx) +static uint32_t m25p80_transfer8_ex(SSIPeripheral *ss, uint32_t tx) { Flash *s = M25P80(ss); uint32_t r = 0; @@ -1548,6 +1548,33 @@ static uint32_t m25p80_transfer8(SSIPeripheral *ss, uint32_t tx) return r; } +/* + * Pre-reading logic for m25p80_transfer8: + * The existing SPI model assumes the output byte is fully formed, + * can be passed to the SPI device, and the input byte can be returned, + * all in one operation. With SPI-GPIO, we don't have the input byte + * until all 8 bits of the output have been shifted out, so we have + * to prime the MISO with bogus values (0xFF). + */ +static uint32_t m25p80_transfer8(SSIPeripheral *ss, uint32_t tx) +{ + Flash *s = M25P80(ss); + SSIBus *ssibus = (SSIBus *)qdev_get_parent_bus(DEVICE(s)); + + uint8_t prev_state = s->state; + uint32_t r = m25p80_transfer8_ex(ss, tx); + uint8_t curr_state = s->state; + + if (ssibus->preread && + /* cmd state has changed into DATA reading state */ + (!(prev_state == STATE_READ || prev_state == STATE_READING_DATA) && + (curr_state == STATE_READ || curr_state == STATE_READING_DATA))) { + r = m25p80_transfer8_ex(ss, 0xFF); + } + + return r; +} + static void m25p80_write_protect_pin_irq_handler(void *opaque, int n, int level) { Flash *s = M25P80(opaque); diff --git a/hw/ssi/ssi.c b/hw/ssi/ssi.c index 003931fb50..21570fe25f 100644 --- a/hw/ssi/ssi.c +++ b/hw/ssi/ssi.c @@ -19,10 +19,6 @@ #include "qapi/error.h" #include "qom/object.h" -struct SSIBus { - BusState parent_obj; -}; - #define TYPE_SSI_BUS "SSI" OBJECT_DECLARE_SIMPLE_TYPE(SSIBus, SSI_BUS) diff --git a/include/hw/ssi/ssi.h b/include/hw/ssi/ssi.h index f411858ab0..e54073d822 100644 --- a/include/hw/ssi/ssi.h +++ b/include/hw/ssi/ssi.h @@ -30,6 +30,11 @@ enum SSICSMode { SSI_CS_HIGH, }; +struct SSIBus { + BusState parent_obj; + bool preread; +}; + /* Peripherals. */ struct SSIPeripheralClass { DeviceClass parent_class; -- 2.30.2