qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

hw/sd/bcm2835_sdhost: Don't raise spurious interrupts

The Linux bcm2835_sdhost driver doesn't work on QEMU, because our
model raises spurious data interrupts. Our function
bcm2835_sdhost_fifo_run() will flag an interrupt any time it is
called with s->datacnt == 0, even if the host hasn't actually issued
a data read or write command yet. This means that the driver gets a
spurious data interrupt as soon as it enables IRQs and then does
something else that causes us to call the fifo_run routine, like
writing to SDHCFG, and before it does the write to SDCMD to issue the
read. The driver's IRQ handler then spins forever complaining that
there's no data and the SD controller isn't in a state where there's
going to be any data:

[ 41.040738] sdhost-bcm2835 3f202000.mmc: fsm 1, hsts 00000000
[ 41.042059] sdhost-bcm2835 3f202000.mmc: fsm 1, hsts 00000000
(continues forever).

Move the interrupt flag setting to more plausible places:
* for BUSY, raise this as soon as a BUSYWAIT command has executed
* for DATA, raise this when the FIFO has any space free (for a write)
or any data in it (for a read)
* for BLOCK, raise this when the data count is 0 and we've
actually done some reading or writing

This is pure guesswork since the documentation for this hardware is
not public, but it is sufficient to get the Linux bcm2835_sdhost
driver to work.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Tested-by: Gerd Hoffmann <kraxel@redhat.com>
Message-id: 20180319161556.16446-3-peter.maydell@linaro.org

+25 -19
+25 -19
hw/sd/bcm2835_sdhost.c
··· 137 137 } 138 138 #undef RWORD 139 139 } 140 + /* We never really delay commands, so if this was a 'busywait' command 141 + * then we've completed it now and can raise the interrupt. 142 + */ 143 + if ((s->cmd & SDCMD_BUSYWAIT) && (s->config & SDHCFG_BUSY_IRPT_EN)) { 144 + s->status |= SDHSTS_BUSY_IRPT; 145 + } 140 146 return; 141 147 142 148 error: ··· 187 193 n++; 188 194 if (n == 4) { 189 195 bcm2835_sdhost_fifo_push(s, value); 196 + s->status |= SDHSTS_DATA_FLAG; 197 + if (s->config & SDHCFG_DATA_IRPT_EN) { 198 + s->status |= SDHSTS_SDIO_IRPT; 199 + } 190 200 n = 0; 191 201 value = 0; 192 202 } 193 203 } 194 204 if (n != 0) { 195 205 bcm2835_sdhost_fifo_push(s, value); 206 + s->status |= SDHSTS_DATA_FLAG; 196 207 } 197 208 } else { /* write */ 198 209 n = 0; 199 210 while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) { 200 211 if (n == 0) { 201 212 value = bcm2835_sdhost_fifo_pop(s); 213 + s->status |= SDHSTS_DATA_FLAG; 214 + if (s->config & SDHCFG_DATA_IRPT_EN) { 215 + s->status |= SDHSTS_SDIO_IRPT; 216 + } 202 217 n = 4; 203 218 } 204 219 n--; ··· 207 222 value >>= 8; 208 223 } 209 224 } 210 - } 211 - if (s->datacnt == 0) { 212 - s->status |= SDHSTS_DATA_FLAG; 213 - 214 - s->edm &= ~0xf; 215 - s->edm |= SDEDM_FSM_DATAMODE; 216 - trace_bcm2835_sdhost_edm_change("datacnt 0", s->edm); 217 - 218 - if (s->config & SDHCFG_DATA_IRPT_EN) { 219 - s->status |= SDHSTS_SDIO_IRPT; 220 - } 225 + if (s->datacnt == 0) { 226 + s->edm &= ~SDEDM_FSM_MASK; 227 + s->edm |= SDEDM_FSM_DATAMODE; 228 + trace_bcm2835_sdhost_edm_change("datacnt 0", s->edm); 221 229 222 - if ((s->cmd & SDCMD_BUSYWAIT) && (s->config & SDHCFG_BUSY_IRPT_EN)) { 223 - s->status |= SDHSTS_BUSY_IRPT; 230 + if ((s->cmd & SDCMD_WRITE_CMD) && 231 + (s->config & SDHCFG_BLOCK_IRPT_EN)) { 232 + s->status |= SDHSTS_BLOCK_IRPT; 233 + } 224 234 } 235 + } 225 236 226 - if ((s->cmd & SDCMD_WRITE_CMD) && (s->config & SDHCFG_BLOCK_IRPT_EN)) { 227 - s->status |= SDHSTS_BLOCK_IRPT; 228 - } 229 - 230 - bcm2835_sdhost_update_irq(s); 231 - } 237 + bcm2835_sdhost_update_irq(s); 232 238 233 239 s->edm &= ~(0x1f << 4); 234 240 s->edm |= ((s->fifo_len & 0x1f) << 4);