aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Cave-Ayland <mark.cave-ayland@ilande.co.uk>2024-01-12 13:15:28 +0000
committerMichael Tokarev <mjt@tls.msk.ru>2024-01-20 18:31:36 +0300
commit5bdc6b957148c96acc3471b22ea6c77f60592e1c (patch)
treef05b5a7dbc9145a0a99b234af2a32c1711992cec
parentfd4e677c793855057a6204e1931eb927590f85f3 (diff)
hw/scsi/esp-pci: synchronise setting of DMA_STAT_DONE with ESP completion interrupt
The setting of DMA_STAT_DONE at the end of a DMA transfer can be configured to generate an interrupt, however the Linux driver manually checks for DMA_STAT_DONE being set and if it is, considers that a DMA transfer has completed. If DMA_STAT_DONE is set but the ESP device isn't indicating an interrupt then the Linux driver considers this to be a spurious interrupt. However this can occur in QEMU as there is a delay between the end of DMA transfer where DMA_STAT_DONE is set, and the ESP device raising its completion interrupt. This appears to be an incorrect assumption in the Linux driver as the ESP and PCI DMA interrupt sources are separate (and may not be raised exactly together), however we can work around this by synchronising the setting of DMA_STAT_DONE at the end of a DMA transfer with the ESP completion interrupt. In conjunction with the previous commit Linux is now able to correctly boot from an am53c974 PCI SCSI device on the hppa C3700 machine without emitting "iget: checksum invalid" and "Spurious irq, sreg=10" errors. Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> Reviewed-by: Guenter Roeck <linux@roeck-us.net> Tested-by: Guenter Roeck <linux@roeck-us.net> Message-ID: <20240112131529.515642-4-mark.cave-ayland@ilande.co.uk> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org> (cherry picked from commit 1e8e6644e063b20ad391140fae13d00ad7750b33) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
-rw-r--r--hw/scsi/esp-pci.c28
1 files changed, 13 insertions, 15 deletions
diff --git a/hw/scsi/esp-pci.c b/hw/scsi/esp-pci.c
index 59ce14f843..e9e94070ec 100644
--- a/hw/scsi/esp-pci.c
+++ b/hw/scsi/esp-pci.c
@@ -93,6 +93,18 @@ static void esp_irq_handler(void *opaque, int irq_num, int level)
if (level) {
pci->dma_regs[DMA_STAT] |= DMA_STAT_SCSIINT;
+
+ /*
+ * If raising the ESP IRQ to indicate end of DMA transfer, set
+ * DMA_STAT_DONE at the same time. In theory this should be done in
+ * esp_pci_dma_memory_rw(), however there is a delay between setting
+ * DMA_STAT_DONE and the ESP IRQ arriving which is visible to the
+ * guest that can cause confusion e.g. Linux
+ */
+ if ((pci->dma_regs[DMA_CMD] & DMA_CMD_MASK) == 0x3 &&
+ pci->dma_regs[DMA_WBC] == 0) {
+ pci->dma_regs[DMA_STAT] |= DMA_STAT_DONE;
+ }
} else {
pci->dma_regs[DMA_STAT] &= ~DMA_STAT_SCSIINT;
}
@@ -306,9 +318,6 @@ static void esp_pci_dma_memory_rw(PCIESPState *pci, uint8_t *buf, int len,
/* update status registers */
pci->dma_regs[DMA_WBC] -= len;
pci->dma_regs[DMA_WAC] += len;
- if (pci->dma_regs[DMA_WBC] == 0) {
- pci->dma_regs[DMA_STAT] |= DMA_STAT_DONE;
- }
}
static void esp_pci_dma_memory_read(void *opaque, uint8_t *buf, int len)
@@ -363,24 +372,13 @@ static const VMStateDescription vmstate_esp_pci_scsi = {
}
};
-static void esp_pci_command_complete(SCSIRequest *req, size_t resid)
-{
- ESPState *s = req->hba_private;
- PCIESPState *pci = container_of(s, PCIESPState, esp);
-
- esp_command_complete(req, resid);
- pci->dma_regs[DMA_WBC] = 0;
- pci->dma_regs[DMA_STAT] |= DMA_STAT_DONE;
- esp_pci_update_irq(pci);
-}
-
static const struct SCSIBusInfo esp_pci_scsi_info = {
.tcq = false,
.max_target = ESP_MAX_DEVS,
.max_lun = 7,
.transfer_data = esp_transfer_data,
- .complete = esp_pci_command_complete,
+ .complete = esp_command_complete,
.cancel = esp_request_cancelled,
};