diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2019-10-29 16:27:48 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2019-10-29 16:27:48 +0000 |
commit | f724de8dde4cc8d8159e4811714aeca222d9eb70 (patch) | |
tree | d9f88e884b8fd4585423e6f6ccc807ba31fad596 /hw/scsi | |
parent | 4599cb953c9744bb7dc3513d688f7f0100ee35e2 (diff) | |
parent | f7d85525f14b99aaa7bf561c9f3cf11c85a080c9 (diff) |
Merge remote-tracking branch 'remotes/vivier/tags/q800-branch-pull-request' into staging
Add Macintosh Quadra 800 machine in hw/m68k
# gpg: Signature made Mon 28 Oct 2019 18:14:25 GMT
# gpg: using RSA key CD2F75DDC8E3A4DC2E4F5173F30C38BD3F2FBE3C
# gpg: issuer "laurent@vivier.eu"
# gpg: Good signature from "Laurent Vivier <lvivier@redhat.com>" [full]
# gpg: aka "Laurent Vivier <laurent@vivier.eu>" [full]
# gpg: aka "Laurent Vivier (Red Hat) <lvivier@redhat.com>" [full]
# Primary key fingerprint: CD2F 75DD C8E3 A4DC 2E4F 5173 F30C 38BD 3F2F BE3C
* remotes/vivier/tags/q800-branch-pull-request:
BootLinuxConsoleTest: Test the Quadra 800
hw/m68k: define Macintosh Quadra 800
hw/m68k: add a dummy SWIM floppy controller
hw/m68k: add Nubus macfb video card
hw/m68k: add Nubus support
hw/m68k: implement ADB bus support for via
hw/m68k: add VIA support
dp8393x: manage big endian bus
esp: add pseudo-DMA as used by Macintosh
esp: move get_cmd() post-DMA code to get_cmd_cb()
esp: move handle_ti_cmd() cleanup code to esp_do_dma().
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw/scsi')
-rw-r--r-- | hw/scsi/esp.c | 336 |
1 files changed, 308 insertions, 28 deletions
diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index 841d79b60e..f8fc30cccb 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -38,6 +38,8 @@ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C100.txt * and * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR53C9X.txt + * + * On Macintosh Quadra it is a NCR53C96. */ static void esp_raise_irq(ESPState *s) @@ -58,6 +60,16 @@ static void esp_lower_irq(ESPState *s) } } +static void esp_raise_drq(ESPState *s) +{ + qemu_irq_raise(s->irq_data); +} + +static void esp_lower_drq(ESPState *s) +{ + qemu_irq_lower(s->irq_data); +} + void esp_dma_enable(ESPState *s, int irq, int level) { if (level) { @@ -84,6 +96,58 @@ void esp_request_cancelled(SCSIRequest *req) } } +static void set_pdma(ESPState *s, enum pdma_origin_id origin, + uint32_t index, uint32_t len) +{ + s->pdma_origin = origin; + s->pdma_start = index; + s->pdma_cur = index; + s->pdma_len = len; +} + +static uint8_t *get_pdma_buf(ESPState *s) +{ + switch (s->pdma_origin) { + case PDMA: + return s->pdma_buf; + case TI: + return s->ti_buf; + case CMD: + return s->cmdbuf; + case ASYNC: + return s->async_buf; + } + return NULL; +} + +static int get_cmd_cb(ESPState *s) +{ + int target; + + target = s->wregs[ESP_WBUSID] & BUSID_DID; + + s->ti_size = 0; + s->ti_rptr = 0; + s->ti_wptr = 0; + + if (s->current_req) { + /* Started a new command before the old one finished. Cancel it. */ + scsi_req_cancel(s->current_req); + s->async_len = 0; + } + + s->current_dev = scsi_device_find(&s->bus, 0, target, 0); + if (!s->current_dev) { + /* No such drive */ + s->rregs[ESP_RSTAT] = 0; + s->rregs[ESP_RINTR] = INTR_DC; + s->rregs[ESP_RSEQ] = SEQ_0; + esp_raise_irq(s); + return -1; + } + return 0; +} + static uint32_t get_cmd(ESPState *s, uint8_t *buf, uint8_t buflen) { uint32_t dmalen; @@ -97,7 +161,14 @@ static uint32_t get_cmd(ESPState *s, uint8_t *buf, uint8_t buflen) if (dmalen > buflen) { return 0; } - s->dma_memory_read(s->dma_opaque, buf, dmalen); + if (s->dma_memory_read) { + s->dma_memory_read(s->dma_opaque, buf, dmalen); + } else { + memcpy(s->pdma_buf, buf, dmalen); + set_pdma(s, PDMA, 0, dmalen); + esp_raise_drq(s); + return 0; + } } else { dmalen = s->ti_size; if (dmalen > TI_BUFSZ) { @@ -108,23 +179,7 @@ static uint32_t get_cmd(ESPState *s, uint8_t *buf, uint8_t buflen) } trace_esp_get_cmd(dmalen, target); - s->ti_size = 0; - s->ti_rptr = 0; - s->ti_wptr = 0; - - if (s->current_req) { - /* Started a new command before the old one finished. Cancel it. */ - scsi_req_cancel(s->current_req); - s->async_len = 0; - } - - s->current_dev = scsi_device_find(&s->bus, 0, target, 0); - if (!s->current_dev) { - // No such drive - s->rregs[ESP_RSTAT] = 0; - s->rregs[ESP_RINTR] = INTR_DC; - s->rregs[ESP_RSEQ] = SEQ_0; - esp_raise_irq(s); + if (get_cmd_cb(s) < 0) { return 0; } return dmalen; @@ -165,6 +220,16 @@ static void do_cmd(ESPState *s, uint8_t *buf) do_busid_cmd(s, &buf[1], busid); } +static void satn_pdma_cb(ESPState *s) +{ + if (get_cmd_cb(s) < 0) { + return; + } + if (s->pdma_cur != s->pdma_start) { + do_cmd(s, get_pdma_buf(s) + s->pdma_start); + } +} + static void handle_satn(ESPState *s) { uint8_t buf[32]; @@ -174,11 +239,22 @@ static void handle_satn(ESPState *s) s->dma_cb = handle_satn; return; } + s->pdma_cb = satn_pdma_cb; len = get_cmd(s, buf, sizeof(buf)); if (len) do_cmd(s, buf); } +static void s_without_satn_pdma_cb(ESPState *s) +{ + if (get_cmd_cb(s) < 0) { + return; + } + if (s->pdma_cur != s->pdma_start) { + do_busid_cmd(s, get_pdma_buf(s) + s->pdma_start, 0); + } +} + static void handle_s_without_atn(ESPState *s) { uint8_t buf[32]; @@ -188,18 +264,36 @@ static void handle_s_without_atn(ESPState *s) s->dma_cb = handle_s_without_atn; return; } + s->pdma_cb = s_without_satn_pdma_cb; len = get_cmd(s, buf, sizeof(buf)); if (len) { do_busid_cmd(s, buf, 0); } } +static void satn_stop_pdma_cb(ESPState *s) +{ + if (get_cmd_cb(s) < 0) { + return; + } + s->cmdlen = s->pdma_cur - s->pdma_start; + if (s->cmdlen) { + trace_esp_handle_satn_stop(s->cmdlen); + s->do_cmd = 1; + s->rregs[ESP_RSTAT] = STAT_TC | STAT_CD; + s->rregs[ESP_RINTR] = INTR_BS | INTR_FC; + s->rregs[ESP_RSEQ] = SEQ_CD; + esp_raise_irq(s); + } +} + static void handle_satn_stop(ESPState *s) { if (s->dma && !s->dma_enabled) { s->dma_cb = handle_satn_stop; return; } + s->pdma_cb = satn_stop_pdma_cb;; s->cmdlen = get_cmd(s, s->cmdbuf, sizeof(s->cmdbuf)); if (s->cmdlen) { trace_esp_handle_satn_stop(s->cmdlen); @@ -211,16 +305,31 @@ static void handle_satn_stop(ESPState *s) } } +static void write_response_pdma_cb(ESPState *s) +{ + s->rregs[ESP_RSTAT] = STAT_TC | STAT_ST; + s->rregs[ESP_RINTR] = INTR_BS | INTR_FC; + s->rregs[ESP_RSEQ] = SEQ_CD; + esp_raise_irq(s); +} + static void write_response(ESPState *s) { trace_esp_write_response(s->status); s->ti_buf[0] = s->status; s->ti_buf[1] = 0; if (s->dma) { - s->dma_memory_write(s->dma_opaque, s->ti_buf, 2); - s->rregs[ESP_RSTAT] = STAT_TC | STAT_ST; - s->rregs[ESP_RINTR] = INTR_BS | INTR_FC; - s->rregs[ESP_RSEQ] = SEQ_CD; + if (s->dma_memory_write) { + s->dma_memory_write(s->dma_opaque, s->ti_buf, 2); + s->rregs[ESP_RSTAT] = STAT_TC | STAT_ST; + s->rregs[ESP_RINTR] = INTR_BS | INTR_FC; + s->rregs[ESP_RSEQ] = SEQ_CD; + } else { + set_pdma(s, TI, 0, 2); + s->pdma_cb = write_response_pdma_cb; + esp_raise_drq(s); + return; + } } else { s->ti_size = 2; s->ti_rptr = 0; @@ -242,6 +351,41 @@ static void esp_dma_done(ESPState *s) esp_raise_irq(s); } +static void do_dma_pdma_cb(ESPState *s) +{ + int to_device = (s->ti_size < 0); + int len = s->pdma_cur - s->pdma_start; + if (s->do_cmd) { + s->ti_size = 0; + s->cmdlen = 0; + s->do_cmd = 0; + do_cmd(s, s->cmdbuf); + return; + } + s->dma_left -= len; + s->async_buf += len; + s->async_len -= len; + if (to_device) { + s->ti_size += len; + } else { + s->ti_size -= len; + } + if (s->async_len == 0) { + scsi_req_continue(s->current_req); + /* + * If there is still data to be read from the device then + * complete the DMA operation immediately. Otherwise defer + * until the scsi layer has completed. + */ + if (to_device || s->dma_left != 0 || s->ti_size == 0) { + return; + } + } + + /* Partially filled a scsi buffer. Complete immediately. */ + esp_dma_done(s); +} + static void esp_do_dma(ESPState *s) { uint32_t len; @@ -249,10 +393,26 @@ static void esp_do_dma(ESPState *s) len = s->dma_left; if (s->do_cmd) { + /* + * handle_ti_cmd() case: esp_do_dma() is called only from + * handle_ti_cmd() with do_cmd != NULL (see the assert()) + */ 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); + if (s->dma_memory_read) { + s->dma_memory_read(s->dma_opaque, &s->cmdbuf[s->cmdlen], len); + } else { + set_pdma(s, CMD, s->cmdlen, len); + s->pdma_cb = do_dma_pdma_cb; + esp_raise_drq(s); + return; + } + trace_esp_handle_ti_cmd(s->cmdlen); + s->ti_size = 0; + s->cmdlen = 0; + s->do_cmd = 0; + do_cmd(s, s->cmdbuf); return; } if (s->async_len == 0) { @@ -264,9 +424,23 @@ static void esp_do_dma(ESPState *s) } to_device = (s->ti_size < 0); if (to_device) { - s->dma_memory_read(s->dma_opaque, s->async_buf, len); + if (s->dma_memory_read) { + s->dma_memory_read(s->dma_opaque, s->async_buf, len); + } else { + set_pdma(s, ASYNC, 0, len); + s->pdma_cb = do_dma_pdma_cb; + esp_raise_drq(s); + return; + } } else { - s->dma_memory_write(s->dma_opaque, s->async_buf, len); + if (s->dma_memory_write) { + s->dma_memory_write(s->dma_opaque, s->async_buf, len); + } else { + set_pdma(s, ASYNC, 0, len); + s->pdma_cb = do_dma_pdma_cb; + esp_raise_drq(s); + return; + } } s->dma_left -= len; s->async_buf += len; @@ -373,8 +547,7 @@ static void handle_ti(ESPState *s) s->dma_left = minlen; s->rregs[ESP_RSTAT] &= ~STAT_TC; esp_do_dma(s); - } - if (s->do_cmd) { + } else if (s->do_cmd) { trace_esp_handle_ti_cmd(s->cmdlen); s->ti_size = 0; s->cmdlen = 0; @@ -401,6 +574,7 @@ void esp_hard_reset(ESPState *s) static void esp_soft_reset(ESPState *s) { qemu_irq_lower(s->irq); + qemu_irq_lower(s->irq_data); esp_hard_reset(s); } @@ -590,6 +764,28 @@ static bool esp_mem_accepts(void *opaque, hwaddr addr, return (size == 1) || (is_write && size == 4); } +static bool esp_pdma_needed(void *opaque) +{ + ESPState *s = opaque; + return s->dma_memory_read == NULL && s->dma_memory_write == NULL && + s->dma_enabled; +} + +static const VMStateDescription vmstate_esp_pdma = { + .name = "esp/pdma", + .version_id = 1, + .minimum_version_id = 1, + .needed = esp_pdma_needed, + .fields = (VMStateField[]) { + VMSTATE_BUFFER(pdma_buf, ESPState), + VMSTATE_INT32(pdma_origin, ESPState), + VMSTATE_UINT32(pdma_len, ESPState), + VMSTATE_UINT32(pdma_start, ESPState), + VMSTATE_UINT32(pdma_cur, ESPState), + VMSTATE_END_OF_LIST() + } +}; + const VMStateDescription vmstate_esp = { .name ="esp", .version_id = 4, @@ -611,6 +807,10 @@ const VMStateDescription vmstate_esp = { VMSTATE_UINT32(do_cmd, ESPState), VMSTATE_UINT32(dma_left, ESPState), VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * []) { + &vmstate_esp_pdma, + NULL } }; @@ -641,6 +841,82 @@ static const MemoryRegionOps sysbus_esp_mem_ops = { .valid.accepts = esp_mem_accepts, }; +static void sysbus_esp_pdma_write(void *opaque, hwaddr addr, + uint64_t val, unsigned int size) +{ + SysBusESPState *sysbus = opaque; + ESPState *s = &sysbus->esp; + uint32_t dmalen; + uint8_t *buf = get_pdma_buf(s); + + dmalen = s->rregs[ESP_TCLO]; + dmalen |= s->rregs[ESP_TCMID] << 8; + dmalen |= s->rregs[ESP_TCHI] << 16; + if (dmalen == 0 || s->pdma_len == 0) { + return; + } + switch (size) { + case 1: + buf[s->pdma_cur++] = val; + s->pdma_len--; + dmalen--; + break; + case 2: + buf[s->pdma_cur++] = val >> 8; + buf[s->pdma_cur++] = val; + s->pdma_len -= 2; + dmalen -= 2; + break; + } + s->rregs[ESP_TCLO] = dmalen & 0xff; + s->rregs[ESP_TCMID] = dmalen >> 8; + s->rregs[ESP_TCHI] = dmalen >> 16; + if (s->pdma_len == 0 && s->pdma_cb) { + esp_lower_drq(s); + s->pdma_cb(s); + s->pdma_cb = NULL; + } +} + +static uint64_t sysbus_esp_pdma_read(void *opaque, hwaddr addr, + unsigned int size) +{ + SysBusESPState *sysbus = opaque; + ESPState *s = &sysbus->esp; + uint8_t *buf = get_pdma_buf(s); + uint64_t val = 0; + + if (s->pdma_len == 0) { + return 0; + } + switch (size) { + case 1: + val = buf[s->pdma_cur++]; + s->pdma_len--; + break; + case 2: + val = buf[s->pdma_cur++]; + val = (val << 8) | buf[s->pdma_cur++]; + s->pdma_len -= 2; + break; + } + + if (s->pdma_len == 0 && s->pdma_cb) { + esp_lower_drq(s); + s->pdma_cb(s); + s->pdma_cb = NULL; + } + return val; +} + +static const MemoryRegionOps sysbus_esp_pdma_ops = { + .read = sysbus_esp_pdma_read, + .write = sysbus_esp_pdma_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 1, + .valid.max_access_size = 2, +}; + static const struct SCSIBusInfo esp_scsi_info = { .tcq = false, .max_target = ESP_MAX_DEVS, @@ -673,12 +949,16 @@ static void sysbus_esp_realize(DeviceState *dev, Error **errp) ESPState *s = &sysbus->esp; sysbus_init_irq(sbd, &s->irq); + sysbus_init_irq(sbd, &s->irq_data); assert(sysbus->it_shift != -1); s->chip_id = TCHI_FAS100A; memory_region_init_io(&sysbus->iomem, OBJECT(sysbus), &sysbus_esp_mem_ops, - sysbus, "esp", ESP_REGS << sysbus->it_shift); + sysbus, "esp-regs", ESP_REGS << sysbus->it_shift); sysbus_init_mmio(sbd, &sysbus->iomem); + memory_region_init_io(&sysbus->pdma, OBJECT(sysbus), &sysbus_esp_pdma_ops, + sysbus, "esp-pdma", 2); + sysbus_init_mmio(sbd, &sysbus->pdma); qdev_init_gpio_in(dev, sysbus_esp_gpio_demux, 2); |