#include "qemu/osdep.h"
#include "hw/sysbus.h"
+#include "migration/vmstate.h"
+#include "hw/irq.h"
#include "hw/scsi/esp.h"
#include "trace.h"
-#include "qapi/error.h"
#include "qemu/log.h"
+#include "qemu/module.h"
/*
* On Sparc32, this is the ESP (NCR53C90) part of chip STP2000 (Master I/O),
} else {
s->ti_size = 2;
s->ti_rptr = 0;
- s->ti_wptr = 0;
+ s->ti_wptr = 2;
s->rregs[ESP_RFLAGS] = 2;
}
esp_raise_irq(s);
uint32_t len;
int to_device;
- to_device = (s->ti_size < 0);
len = s->dma_left;
if (s->do_cmd) {
trace_esp_do_dma(s->cmdlen, len);
+ assert (s->cmdlen <= sizeof(s->cmdbuf) &&
+ len <= sizeof(s->cmdbuf) - s->cmdlen);
s->dma_memory_read(s->dma_opaque, &s->cmdbuf[s->cmdlen], len);
- s->ti_size = 0;
- s->cmdlen = 0;
- s->do_cmd = 0;
- do_cmd(s, s->cmdbuf);
return;
}
if (s->async_len == 0) {
if (len > s->async_len) {
len = s->async_len;
}
+ to_device = (s->ti_size < 0);
if (to_device) {
s->dma_memory_read(s->dma_opaque, s->async_buf, len);
} else {
esp_dma_done(s);
}
-void esp_command_complete(SCSIRequest *req, uint32_t status,
- size_t resid)
+static void esp_report_command_complete(ESPState *s, uint32_t status)
{
- ESPState *s = req->hba_private;
-
trace_esp_command_complete();
if (s->ti_size != 0) {
trace_esp_command_complete_unexpected();
}
}
+void esp_command_complete(SCSIRequest *req, uint32_t status,
+ size_t resid)
+{
+ ESPState *s = req->hba_private;
+
+ if (s->rregs[ESP_RSTAT] & STAT_INT) {
+ /* Defer handling command complete until the previous
+ * interrupt has been handled.
+ */
+ trace_esp_command_complete_deferred();
+ s->deferred_status = status;
+ s->deferred_complete = true;
+ return;
+ }
+ esp_report_command_complete(s, status);
+}
+
void esp_transfer_data(SCSIRequest *req, uint32_t len)
{
ESPState *s = req->hba_private;
+ assert(!s->do_cmd);
trace_esp_transfer_data(s->dma_left, s->ti_size);
s->async_len = len;
s->async_buf = scsi_req_get_buf(req);
s->dma_counter = dmalen;
if (s->do_cmd)
- minlen = (dmalen < 32) ? dmalen : 32;
+ minlen = (dmalen < ESP_CMDBUF_SZ) ? dmalen : ESP_CMDBUF_SZ;
else if (s->ti_size < 0)
minlen = (dmalen < -s->ti_size) ? dmalen : -s->ti_size;
else
s->dma_left = minlen;
s->rregs[ESP_RSTAT] &= ~STAT_TC;
esp_do_dma(s);
- } else if (s->do_cmd) {
+ }
+ if (s->do_cmd) {
trace_esp_handle_ti_cmd(s->cmdlen);
s->ti_size = 0;
s->cmdlen = 0;
s->do_cmd = 0;
do_cmd(s, s->cmdbuf);
- return;
}
}
/* Data out. */
qemu_log_mask(LOG_UNIMP, "esp: PIO data read not implemented\n");
s->rregs[ESP_FIFO] = 0;
- esp_raise_irq(s);
} else if (s->ti_rptr < s->ti_wptr) {
s->ti_size--;
s->rregs[ESP_FIFO] = s->ti_buf[s->ti_rptr++];
- esp_raise_irq(s);
}
if (s->ti_rptr == s->ti_wptr) {
s->ti_rptr = 0;
s->rregs[ESP_RSTAT] &= ~STAT_TC;
s->rregs[ESP_RSEQ] = SEQ_CD;
esp_lower_irq(s);
-
+ if (s->deferred_complete) {
+ esp_report_command_complete(s, s->deferred_status);
+ s->deferred_complete = false;
+ }
return old_val;
case ESP_TCHI:
/* Return the unique id if the value has never been written */
break;
case ESP_FIFO:
if (s->do_cmd) {
- if (s->cmdlen < TI_BUFSZ) {
+ if (s->cmdlen < ESP_CMDBUF_SZ) {
s->cmdbuf[s->cmdlen++] = val & 0xff;
} else {
trace_esp_error_fifo_overrun();
}
static bool esp_mem_accepts(void *opaque, hwaddr addr,
- unsigned size, bool is_write)
+ unsigned size, bool is_write,
+ MemTxAttrs attrs)
{
return (size == 1) || (is_write && size == 4);
}
const VMStateDescription vmstate_esp = {
.name ="esp",
- .version_id = 3,
+ .version_id = 4,
.minimum_version_id = 3,
.fields = (VMStateField[]) {
VMSTATE_BUFFER(rregs, ESPState),
VMSTATE_UINT32(ti_wptr, ESPState),
VMSTATE_BUFFER(ti_buf, ESPState),
VMSTATE_UINT32(status, ESPState),
+ VMSTATE_UINT32(deferred_status, ESPState),
+ VMSTATE_BOOL(deferred_complete, ESPState),
VMSTATE_UINT32(dma, ESPState),
- VMSTATE_BUFFER(cmdbuf, ESPState),
+ VMSTATE_PARTIAL_BUFFER(cmdbuf, ESPState, 16),
+ VMSTATE_BUFFER_START_MIDDLE_V(cmdbuf, ESPState, 16, 4),
VMSTATE_UINT32(cmdlen, ESPState),
VMSTATE_UINT32(do_cmd, ESPState),
VMSTATE_UINT32(dma_left, ESPState),
}
};
-#define TYPE_ESP "esp"
-#define ESP(obj) OBJECT_CHECK(SysBusESPState, (obj), TYPE_ESP)
-
-typedef struct {
- /*< private >*/
- SysBusDevice parent_obj;
- /*< public >*/
-
- MemoryRegion iomem;
- uint32_t it_shift;
- ESPState esp;
-} SysBusESPState;
-
static void sysbus_esp_mem_write(void *opaque, hwaddr addr,
uint64_t val, unsigned int size)
{
.valid.accepts = esp_mem_accepts,
};
-void esp_init(hwaddr espaddr, int it_shift,
- ESPDMAMemoryReadWriteFunc dma_memory_read,
- ESPDMAMemoryReadWriteFunc dma_memory_write,
- void *dma_opaque, qemu_irq irq, qemu_irq *reset,
- qemu_irq *dma_enable)
-{
- DeviceState *dev;
- SysBusDevice *s;
- SysBusESPState *sysbus;
- ESPState *esp;
-
- dev = qdev_create(NULL, TYPE_ESP);
- sysbus = ESP(dev);
- esp = &sysbus->esp;
- esp->dma_memory_read = dma_memory_read;
- esp->dma_memory_write = dma_memory_write;
- esp->dma_opaque = dma_opaque;
- sysbus->it_shift = it_shift;
- /* XXX for now until rc4030 has been changed to use DMA enable signal */
- esp->dma_enabled = 1;
- qdev_init_nofail(dev);
- s = SYS_BUS_DEVICE(dev);
- sysbus_connect_irq(s, 0, irq);
- sysbus_mmio_map(s, 0, espaddr);
- *reset = qdev_get_gpio_in(dev, 0);
- *dma_enable = qdev_get_gpio_in(dev, 1);
-}
-
static const struct SCSIBusInfo esp_scsi_info = {
.tcq = false,
.max_target = ESP_MAX_DEVS,
static void sysbus_esp_gpio_demux(void *opaque, int irq, int level)
{
- SysBusESPState *sysbus = ESP(opaque);
+ SysBusESPState *sysbus = ESP_STATE(opaque);
ESPState *s = &sysbus->esp;
switch (irq) {
static void sysbus_esp_realize(DeviceState *dev, Error **errp)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
- SysBusESPState *sysbus = ESP(dev);
+ SysBusESPState *sysbus = ESP_STATE(dev);
ESPState *s = &sysbus->esp;
- Error *err = NULL;
sysbus_init_irq(sbd, &s->irq);
assert(sysbus->it_shift != -1);
qdev_init_gpio_in(dev, sysbus_esp_gpio_demux, 2);
scsi_bus_new(&s->bus, sizeof(s->bus), dev, &esp_scsi_info, NULL);
- scsi_bus_legacy_handle_cmdline(&s->bus, &err);
- if (err != NULL) {
- error_propagate(errp, err);
- return;
- }
}
static void sysbus_esp_hard_reset(DeviceState *dev)
{
- SysBusESPState *sysbus = ESP(dev);
+ SysBusESPState *sysbus = ESP_STATE(dev);
esp_hard_reset(&sysbus->esp);
}
static const VMStateDescription vmstate_sysbus_esp_scsi = {
.name = "sysbusespscsi",
- .version_id = 0,
- .minimum_version_id = 0,
+ .version_id = 1,
+ .minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_STRUCT(esp, SysBusESPState, 0, vmstate_esp, ESPState),
VMSTATE_END_OF_LIST()