X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=hw%2Flsi53c895a.c;h=e9904c49d9297d4250420ab42e0ed662e3b70591;hb=8e31bf388e56e5babd9600b110a94381d1be07b1;hp=a0aa406bfa76bb7bcff2d784201194dd3254694a;hpb=43b443b6688bee628f85b85836ccf618169c2647;p=qemu.git diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c index a0aa406bf..e9904c49d 100644 --- a/hw/lsi53c895a.c +++ b/hw/lsi53c895a.c @@ -4,13 +4,13 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. */ /* ??? Need to check if the {read,write}[wl] routines work properly on big-endian targets. */ -#include \ +#include #include "hw.h" #include "pci.h" @@ -31,6 +31,8 @@ do { fprintf(stderr, "lsi_scsi: error: " fmt , ## __VA_ARGS__); exit(1);} while do { fprintf(stderr, "lsi_scsi: error: " fmt , ## __VA_ARGS__);} while (0) #endif +#define LSI_MAX_DEVS 7 + #define LSI_SCNTL0_TRG 0x01 #define LSI_SCNTL0_AAP 0x02 #define LSI_SCNTL0_EPC 0x08 @@ -152,6 +154,9 @@ do { fprintf(stderr, "lsi_scsi: error: " fmt , ## __VA_ARGS__);} while (0) #define LSI_CCNTL1_DDAC 0x08 #define LSI_CCNTL1_ZMOD 0x80 +/* Enable Response to Reselection */ +#define LSI_SCID_RRE 0x60 + #define LSI_CCNTL1_40BIT (LSI_CCNTL1_EN64TIBMV|LSI_CCNTL1_64TIMOD) #define PHASE_DO 0 @@ -168,11 +173,15 @@ do { fprintf(stderr, "lsi_scsi: error: " fmt , ## __VA_ARGS__);} while (0) /* Flag set if this is a tagged command. */ #define LSI_TAG_VALID (1 << 16) -typedef struct { +typedef struct lsi_request { + SCSIRequest *req; uint32_t tag; + uint32_t dma_len; + uint8_t *dma_buf; uint32_t pending; int out; -} lsi_queue; + QTAILQ_ENTRY(lsi_request) next; +} lsi_request; typedef struct { PCIDevice dev; @@ -181,9 +190,9 @@ typedef struct { uint32_t script_ram_base; int carry; /* ??? Should this be an a visible register somewhere? */ - int sense; + int status; /* Action to take at the end of a MSG IN phase. - 0 = COMMAND, 1 = disconect, 2 = DATA OUT, 3 = DATA IN. */ + 0 = COMMAND, 1 = disconnect, 2 = DATA OUT, 3 = DATA IN. */ int msg_action; int msg_len; uint8_t msg[LSI_MAX_MSGIN_LEN]; @@ -193,16 +202,12 @@ typedef struct { * 3 if a DMA operation is in progress. */ int waiting; SCSIBus bus; - SCSIDevice *current_dev; int current_lun; /* The tag is a combination of the device ID and the SCSI tag. */ - uint32_t current_tag; - uint32_t current_dma_len; + uint32_t select_tag; int command_complete; - uint8_t *dma_buf; - lsi_queue *queue; - int queue_len; - int active_commands; + QTAILQ_HEAD(, lsi_request) queue; + lsi_request *current; uint32_t dsa; uint32_t temp; @@ -270,11 +275,20 @@ typedef struct { uint32_t script_ram[2048]; } LSIState; +static inline int lsi_irq_on_rsl(LSIState *s) +{ + return (s->sien0 & LSI_SIST0_RSL) && (s->scid & LSI_SCID_RRE); +} + static void lsi_soft_reset(LSIState *s) { + lsi_request *p; + DPRINTF("Reset\n"); s->carry = 0; + s->msg_action = 0; + s->msg_len = 0; s->waiting = 0; s->dsa = 0; s->dnad = 0; @@ -283,8 +297,8 @@ static void lsi_soft_reset(LSIState *s) memset(s->scratch, 0, sizeof(s->scratch)); s->istat0 = 0; s->istat1 = 0; - s->dcmd = 0; - s->dstat = 0; + s->dcmd = 0x40; + s->dstat = LSI_DSTAT_DFE; s->dien = 0; s->sist0 = 0; s->sist1 = 0; @@ -293,7 +307,7 @@ static void lsi_soft_reset(LSIState *s) s->mbox0 = 0; s->mbox1 = 0; s->dfifo = 0; - s->ctest2 = 0; + s->ctest2 = LSI_CTEST2_DACK; s->ctest3 = 0; s->ctest4 = 0; s->ctest5 = 0; @@ -312,6 +326,8 @@ static void lsi_soft_reset(LSIState *s) s->scid = 7; s->sxfer = 0; s->socl = 0; + s->sdid = 0; + s->ssid = 0; s->stest1 = 0; s->stest2 = 0; s->stest3 = 0; @@ -334,6 +350,15 @@ static void lsi_soft_reset(LSIState *s) s->sbc = 0; s->csbc = 0; s->sbr = 0; + while (!QTAILQ_EMPTY(&s->queue)) { + p = QTAILQ_FIRST(&s->queue); + QTAILQ_REMOVE(&s->queue, p, next); + qemu_free(p); + } + if (s->current) { + qemu_free(s->current); + s->current = NULL; + } } static int lsi_dma_40bit(LSIState *s) @@ -360,6 +385,7 @@ static int lsi_dma_64bit(LSIState *s) static uint8_t lsi_reg_readb(LSIState *s, int offset); static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val); static void lsi_execute_script(LSIState *s); +static void lsi_reselect(LSIState *s, lsi_request *p); static inline uint32_t read_dword(LSIState *s, uint32_t addr) { @@ -382,6 +408,7 @@ static void lsi_update_irq(LSIState *s) { int level; static int last_level; + lsi_request *p; /* It's unclear whether the DIP/SIP bits should be cleared when the Interrupt Status Registers are cleared or when istat0 is read. @@ -411,6 +438,17 @@ static void lsi_update_irq(LSIState *s) last_level = level; } qemu_set_irq(s->dev.irq[0], level); + + if (!level && lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON)) { + DPRINTF("Handled IRQs & disconnected, looking for pending " + "processes\n"); + QTAILQ_FOREACH(p, &s->queue, next) { + if (p->pending) { + lsi_reselect(s, p); + break; + } + } + } } /* Stop SCRIPTS execution and raise a SCSI interrupt. */ @@ -453,10 +491,10 @@ static void lsi_bad_phase(LSIState *s, int out, int new_phase) { /* Trigger a phase mismatch. */ if (s->ccntl0 & LSI_CCNTL0_ENPMJ) { - if ((s->ccntl0 & LSI_CCNTL0_PMJCTL) || out) { - s->dsp = s->pmjad1; + if ((s->ccntl0 & LSI_CCNTL0_PMJCTL)) { + s->dsp = out ? s->pmjad1 : s->pmjad2; } else { - s->dsp = s->pmjad2; + s->dsp = (s->scntl2 & LSI_SCNTL2_WSR ? s->pmjad2 : s->pmjad1); } DPRINTF("Data phase mismatch jump to %08x\n", s->dsp); } else { @@ -479,21 +517,43 @@ static void lsi_resume_script(LSIState *s) } } +static void lsi_disconnect(LSIState *s) +{ + s->scntl1 &= ~LSI_SCNTL1_CON; + s->sstat1 &= ~PHASE_MASK; +} + +static void lsi_bad_selection(LSIState *s, uint32_t id) +{ + DPRINTF("Selected absent target %d\n", id); + lsi_script_scsi_interrupt(s, 0, LSI_SIST1_STO); + lsi_disconnect(s); +} + /* Initiate a SCSI layer data transfer. */ static void lsi_do_dma(LSIState *s, int out) { - uint32_t count; + uint32_t count, id; target_phys_addr_t addr; + SCSIDevice *dev; - if (!s->current_dma_len) { + assert(s->current); + if (!s->current->dma_len) { /* Wait until data is available. */ DPRINTF("DMA no data available\n"); return; } + id = (s->current->tag >> 8) & 0xf; + dev = s->bus.devs[id]; + if (!dev) { + lsi_bad_selection(s, id); + return; + } + count = s->dbc; - if (count > s->current_dma_len) - count = s->current_dma_len; + if (count > s->current->dma_len) + count = s->current->dma_len; addr = s->dnad; /* both 40 and Table Indirect 64-bit DMAs store upper bits in dnad64 */ @@ -508,30 +568,21 @@ static void lsi_do_dma(LSIState *s, int out) s->csbc += count; s->dnad += count; s->dbc -= count; - - if (s->dma_buf == NULL) { - s->dma_buf = s->current_dev->info->get_buf(s->current_dev, - s->current_tag); + if (s->current->dma_buf == NULL) { + s->current->dma_buf = scsi_req_get_buf(s->current->req); } - /* ??? Set SFBR to first data byte. */ if (out) { - cpu_physical_memory_read(addr, s->dma_buf, count); + cpu_physical_memory_read(addr, s->current->dma_buf, count); } else { - cpu_physical_memory_write(addr, s->dma_buf, count); + cpu_physical_memory_write(addr, s->current->dma_buf, count); } - s->current_dma_len -= count; - if (s->current_dma_len == 0) { - s->dma_buf = NULL; - if (out) { - /* Write the data. */ - s->current_dev->info->write_data(s->current_dev, s->current_tag); - } else { - /* Request any remaining data. */ - s->current_dev->info->read_data(s->current_dev, s->current_tag); - } + s->current->dma_len -= count; + if (s->current->dma_len == 0) { + s->current->dma_buf = NULL; + scsi_req_continue(s->current->req); } else { - s->dma_buf += count; + s->current->dma_buf += count; lsi_resume_script(s); } } @@ -540,15 +591,14 @@ static void lsi_do_dma(LSIState *s, int out) /* Add a command to the queue. */ static void lsi_queue_command(LSIState *s) { - lsi_queue *p; + lsi_request *p = s->current; + + DPRINTF("Queueing tag=0x%x\n", p->tag); + assert(s->current != NULL); + assert(s->current->dma_len == 0); + QTAILQ_INSERT_TAIL(&s->queue, s->current, next); + s->current = NULL; - DPRINTF("Queueing tag=0x%x\n", s->current_tag); - if (s->queue_len == s->active_commands) { - s->queue_len++; - s->queue = qemu_realloc(s->queue, s->queue_len * sizeof(lsi_queue)); - } - p = &s->queue[s->active_commands++]; - p->tag = s->current_tag; p->pending = 0; p->out = (s->sstat1 & PHASE_MASK) == PHASE_DO; } @@ -565,113 +615,154 @@ static void lsi_add_msg_byte(LSIState *s, uint8_t data) } /* Perform reselection to continue a command. */ -static void lsi_reselect(LSIState *s, uint32_t tag) +static void lsi_reselect(LSIState *s, lsi_request *p) { - lsi_queue *p; - int n; int id; - p = NULL; - for (n = 0; n < s->active_commands; n++) { - p = &s->queue[n]; - if (p->tag == tag) - break; - } - if (n == s->active_commands) { - BADF("Reselected non-existant command tag=0x%x\n", tag); - return; - } - id = (tag >> 8) & 0xf; + assert(s->current == NULL); + QTAILQ_REMOVE(&s->queue, p, next); + s->current = p; + + id = (p->tag >> 8) & 0xf; s->ssid = id | 0x80; + /* LSI53C700 Family Compatibility, see LSI53C895A 4-73 */ + if (!(s->dcntl & LSI_DCNTL_COM)) { + s->sfbr = 1 << (id & 0x7); + } DPRINTF("Reselected target %d\n", id); - s->current_dev = s->bus.devs[id]; - s->current_tag = tag; s->scntl1 |= LSI_SCNTL1_CON; lsi_set_phase(s, PHASE_MI); s->msg_action = p->out ? 2 : 3; - s->current_dma_len = p->pending; - s->dma_buf = NULL; + s->current->dma_len = p->pending; lsi_add_msg_byte(s, 0x80); - if (s->current_tag & LSI_TAG_VALID) { + if (s->current->tag & LSI_TAG_VALID) { lsi_add_msg_byte(s, 0x20); - lsi_add_msg_byte(s, tag & 0xff); + lsi_add_msg_byte(s, p->tag & 0xff); } - s->active_commands--; - if (n != s->active_commands) { - s->queue[n] = s->queue[s->active_commands]; + if (lsi_irq_on_rsl(s)) { + lsi_script_scsi_interrupt(s, LSI_SIST0_RSL, 0); } } -/* Record that data is available for a queued command. Returns zero if - the device was reselected, nonzero if the IO is deferred. */ -static int lsi_queue_tag(LSIState *s, uint32_t tag, uint32_t arg) +static lsi_request *lsi_find_by_tag(LSIState *s, uint32_t tag) { - lsi_queue *p; - int i; - for (i = 0; i < s->active_commands; i++) { - p = &s->queue[i]; + lsi_request *p; + + QTAILQ_FOREACH(p, &s->queue, next) { if (p->tag == tag) { - if (p->pending) { - BADF("Multiple IO pending for tag %d\n", tag); - } - p->pending = arg; - if (s->waiting == 1) { - /* Reselect device. */ - lsi_reselect(s, tag); - return 0; - } else { - DPRINTF("Queueing IO tag=0x%x\n", tag); - p->pending = arg; - return 1; - } + return p; } } - BADF("IO with unknown tag %d\n", tag); - return 1; + + return NULL; +} + +static void lsi_request_cancelled(SCSIRequest *req) +{ + LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent); + lsi_request *p = req->hba_private; + + if (s->current && req == s->current->req) { + scsi_req_unref(req); + qemu_free(s->current); + s->current = NULL; + return; + } + + if (p) { + QTAILQ_REMOVE(&s->queue, p, next); + scsi_req_unref(req); + qemu_free(p); + } +} + +/* Record that data is available for a queued command. Returns zero if + the device was reselected, nonzero if the IO is deferred. */ +static int lsi_queue_req(LSIState *s, SCSIRequest *req, uint32_t len) +{ + lsi_request *p = req->hba_private; + + if (p->pending) { + BADF("Multiple IO pending for request %p\n", p); + } + p->pending = len; + /* Reselect if waiting for it, or if reselection triggers an IRQ + and the bus is free. + Since no interrupt stacking is implemented in the emulation, it + is also required that there are no pending interrupts waiting + for service from the device driver. */ + if (s->waiting == 1 || + (lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON) && + !(s->istat0 & (LSI_ISTAT0_SIP | LSI_ISTAT0_DIP)))) { + /* Reselect device. */ + lsi_reselect(s, p); + return 0; + } else { + DPRINTF("Queueing IO tag=0x%x\n", tag); + p->pending = len; + return 1; + } } -/* Callback to indicate that the SCSI layer has completed a transfer. */ -static void lsi_command_complete(SCSIBus *bus, int reason, uint32_t tag, - uint32_t arg) + /* Callback to indicate that the SCSI layer has completed a command. */ +static void lsi_command_complete(SCSIRequest *req, uint32_t status) { - LSIState *s = DO_UPCAST(LSIState, dev.qdev, bus->qbus.parent); + LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent); int out; out = (s->sstat1 & PHASE_MASK) == PHASE_DO; - if (reason == SCSI_REASON_DONE) { - DPRINTF("Command complete sense=%d\n", (int)arg); - s->sense = arg; - s->command_complete = 2; - if (s->waiting && s->dbc != 0) { - /* Raise phase mismatch for short transfers. */ - lsi_bad_phase(s, out, PHASE_ST); - } else { - lsi_set_phase(s, PHASE_ST); - } - lsi_resume_script(s); - return; + DPRINTF("Command complete status=%d\n", (int)status); + s->status = status; + s->command_complete = 2; + if (s->waiting && s->dbc != 0) { + /* Raise phase mismatch for short transfers. */ + lsi_bad_phase(s, out, PHASE_ST); + } else { + lsi_set_phase(s, PHASE_ST); + } + + if (s->current && req == s->current->req) { + scsi_req_unref(s->current->req); + qemu_free(s->current); + s->current = NULL; } + lsi_resume_script(s); +} - if (s->waiting == 1 || tag != s->current_tag) { - if (lsi_queue_tag(s, tag, arg)) + /* Callback to indicate that the SCSI layer has completed a transfer. */ +static void lsi_transfer_data(SCSIRequest *req, uint32_t len) +{ + LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent); + int out; + + if (s->waiting == 1 || !s->current || req->hba_private != s->current || + (lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON))) { + if (lsi_queue_req(s, req, len)) { return; + } } - DPRINTF("Data ready tag=0x%x len=%d\n", tag, arg); - s->current_dma_len = arg; + + out = (s->sstat1 & PHASE_MASK) == PHASE_DO; + + /* host adapter (re)connected */ + DPRINTF("Data ready tag=0x%x len=%d\n", req->tag, len); + s->current->dma_len = len; s->command_complete = 1; - if (!s->waiting) - return; - if (s->waiting == 1 || s->dbc == 0) { - lsi_resume_script(s); - } else { - lsi_do_dma(s, out); + if (s->waiting) { + if (s->waiting == 1 || s->dbc == 0) { + lsi_resume_script(s); + } else { + lsi_do_dma(s, out); + } } } static void lsi_do_command(LSIState *s) { + SCSIDevice *dev; uint8_t buf[16]; + uint32_t id; int n; DPRINTF("Send command len=%d\n", s->dbc); @@ -680,16 +771,29 @@ static void lsi_do_command(LSIState *s) cpu_physical_memory_read(s->dnad, buf, s->dbc); s->sfbr = buf[0]; s->command_complete = 0; - n = s->current_dev->info->send_command(s->current_dev, s->current_tag, buf, - s->current_lun); - if (n > 0) { - lsi_set_phase(s, PHASE_DI); - s->current_dev->info->read_data(s->current_dev, s->current_tag); - } else if (n < 0) { - lsi_set_phase(s, PHASE_DO); - s->current_dev->info->write_data(s->current_dev, s->current_tag); + + id = (s->select_tag >> 8) & 0xf; + dev = s->bus.devs[id]; + if (!dev) { + lsi_bad_selection(s, id); + return; } + assert(s->current == NULL); + s->current = qemu_mallocz(sizeof(lsi_request)); + s->current->tag = s->select_tag; + s->current->req = scsi_req_new(dev, s->current->tag, s->current_lun, + s->current); + + n = scsi_req_enqueue(s->current->req, buf); + if (n) { + if (n > 0) { + lsi_set_phase(s, PHASE_DI); + } else if (n < 0) { + lsi_set_phase(s, PHASE_DO); + } + scsi_req_continue(s->current->req); + } if (!s->command_complete) { if (n) { /* Command did not complete immediately so disconnect. */ @@ -708,25 +812,19 @@ static void lsi_do_command(LSIState *s) static void lsi_do_status(LSIState *s) { - uint8_t sense; - DPRINTF("Get status len=%d sense=%d\n", s->dbc, s->sense); + uint8_t status; + DPRINTF("Get status len=%d status=%d\n", s->dbc, s->status); if (s->dbc != 1) BADF("Bad Status move\n"); s->dbc = 1; - sense = s->sense; - s->sfbr = sense; - cpu_physical_memory_write(s->dnad, &sense, 1); + status = s->status; + s->sfbr = status; + cpu_physical_memory_write(s->dnad, &status, 1); lsi_set_phase(s, PHASE_MI); s->msg_action = 1; lsi_add_msg_byte(s, 0); /* COMMAND COMPLETE */ } -static void lsi_disconnect(LSIState *s) -{ - s->scntl1 &= ~LSI_SCNTL1_CON; - s->sstat1 &= ~PHASE_MASK; -} - static void lsi_do_msgin(LSIState *s) { int len; @@ -773,10 +871,29 @@ static uint8_t lsi_get_msgbyte(LSIState *s) return data; } +/* Skip the next n bytes during a MSGOUT phase. */ +static void lsi_skip_msgbytes(LSIState *s, unsigned int n) +{ + s->dnad += n; + s->dbc -= n; +} + static void lsi_do_msgout(LSIState *s) { uint8_t msg; int len; + uint32_t current_tag; + lsi_request *current_req, *p, *p_next; + int id; + + if (s->current) { + current_tag = s->current->tag; + current_req = s->current; + } else { + current_tag = s->select_tag; + current_req = lsi_find_by_tag(s, current_tag); + } + id = (current_tag >> 8) & 0xf; DPRINTF("MSG out len=%d\n", s->dbc); while (s->dbc) { @@ -784,7 +901,7 @@ static void lsi_do_msgout(LSIState *s) s->sfbr = msg; switch (msg) { - case 0x00: + case 0x04: DPRINTF("MSG: Disconnect\n"); lsi_disconnect(s); break; @@ -795,31 +912,80 @@ static void lsi_do_msgout(LSIState *s) case 0x01: len = lsi_get_msgbyte(s); msg = lsi_get_msgbyte(s); + (void)len; /* avoid a warning about unused variable*/ DPRINTF("Extended message 0x%x (len %d)\n", msg, len); switch (msg) { case 1: DPRINTF("SDTR (ignored)\n"); - s->dbc -= 2; + lsi_skip_msgbytes(s, 2); break; case 3: DPRINTF("WDTR (ignored)\n"); - s->dbc -= 1; + lsi_skip_msgbytes(s, 1); break; default: goto bad; } break; case 0x20: /* SIMPLE queue */ - s->current_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID; - DPRINTF("SIMPLE queue tag=0x%x\n", s->current_tag & 0xff); + s->select_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID; + DPRINTF("SIMPLE queue tag=0x%x\n", s->select_tag & 0xff); break; case 0x21: /* HEAD of queue */ BADF("HEAD queue not implemented\n"); - s->current_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID; + s->select_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID; break; case 0x22: /* ORDERED queue */ BADF("ORDERED queue not implemented\n"); - s->current_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID; + s->select_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID; + break; + case 0x0d: + /* The ABORT TAG message clears the current I/O process only. */ + DPRINTF("MSG: ABORT TAG tag=0x%x\n", current_tag); + if (current_req) { + scsi_req_cancel(current_req->req); + } + lsi_disconnect(s); + break; + case 0x06: + case 0x0e: + case 0x0c: + /* The ABORT message clears all I/O processes for the selecting + initiator on the specified logical unit of the target. */ + if (msg == 0x06) { + DPRINTF("MSG: ABORT tag=0x%x\n", current_tag); + } + /* The CLEAR QUEUE message clears all I/O processes for all + initiators on the specified logical unit of the target. */ + if (msg == 0x0e) { + DPRINTF("MSG: CLEAR QUEUE tag=0x%x\n", current_tag); + } + /* The BUS DEVICE RESET message clears all I/O processes for all + initiators on all logical units of the target. */ + if (msg == 0x0c) { + DPRINTF("MSG: BUS DEVICE RESET tag=0x%x\n", current_tag); + } + + /* clear the current I/O process */ + if (s->current) { + scsi_req_cancel(s->current->req); + } + + /* As the current implemented devices scsi_disk and scsi_generic + only support one LUN, we don't need to keep track of LUNs. + Clearing I/O processes for other initiators could be possible + for scsi_generic by sending a SG_SCSI_RESET to the /dev/sgX + device, but this is currently not implemented (and seems not + to be really necessary). So let's simply clear all queued + commands for the current device: */ + id = current_tag & 0x0000ff00; + QTAILQ_FOREACH_SAFE(p, &s->queue, next, p_next) { + if ((p->tag & 0x0000ff00) == id) { + scsi_req_cancel(p->req); + } + } + + lsi_disconnect(s); break; default: if ((msg & 0x80) == 0) { @@ -864,17 +1030,17 @@ static void lsi_memcpy(LSIState *s, uint32_t dest, uint32_t src, int count) static void lsi_wait_reselect(LSIState *s) { - int i; + lsi_request *p; + DPRINTF("Wait Reselect\n"); - if (s->current_dma_len) - BADF("Reselect with pending DMA\n"); - for (i = 0; i < s->active_commands; i++) { - if (s->queue[i].pending) { - lsi_reselect(s, s->queue[i].tag); + + QTAILQ_FOREACH(p, &s->queue, next) { + if (p->pending) { + lsi_reselect(s, p); break; } } - if (s->current_dma_len == 0) { + if (s->current == NULL) { s->waiting = 1; } } @@ -1024,7 +1190,7 @@ again: if (insn & (1 << 25)) { id = read_dword(s, s->dsa + sxt24(insn)); } else { - id = addr; + id = insn; } id = (id >> 16) & 0xf; if (insn & (1 << 26)) { @@ -1034,16 +1200,15 @@ again: switch (opcode) { case 0: /* Select */ s->sdid = id; - if (s->current_dma_len && (s->ssid & 0xf) == id) { - DPRINTF("Already reselected by target %d\n", id); + if (s->scntl1 & LSI_SCNTL1_CON) { + DPRINTF("Already reselected, jumping to alternative address\n"); + s->dsp = s->dnad; break; } s->sstat0 |= LSI_SSTAT0_WOA; s->scntl1 &= ~LSI_SCNTL1_IARB; if (id >= LSI_MAX_DEVS || !s->bus.devs[id]) { - DPRINTF("Selected absent target %d\n", id); - lsi_script_scsi_interrupt(s, 0, LSI_SIST1_STO); - lsi_disconnect(s); + lsi_bad_selection(s, id); break; } DPRINTF("Selected target %d%s\n", @@ -1051,8 +1216,7 @@ again: /* ??? Linux drivers compain when this is set. Maybe it only applies in low-level mode (unimplemented). lsi_script_scsi_interrupt(s, LSI_SIST0_CMP, 0); */ - s->current_dev = s->bus.devs[id]; - s->current_tag = id << 8; + s->select_tag = id << 8; s->scntl1 |= LSI_SCNTL1_CON; if (insn & (1 << 3)) { s->socl |= LSI_SOCL_ATN; @@ -1060,11 +1224,13 @@ again: lsi_set_phase(s, PHASE_MO); break; case 1: /* Disconnect */ - DPRINTF("Wait Disconect\n"); + DPRINTF("Wait Disconnect\n"); s->scntl1 &= ~LSI_SCNTL1_CON; break; case 2: /* Wait Reselect */ - lsi_wait_reselect(s); + if (!lsi_irq_on_rsl(s)) { + lsi_wait_reselect(s); + } break; case 3: /* Set */ DPRINTF("Set%s%s%s%s\n", @@ -1521,8 +1687,19 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val) BADF("Immediate Arbritration not implemented\n"); } if (val & LSI_SCNTL1_RST) { - s->sstat0 |= LSI_SSTAT0_RST; - lsi_script_scsi_interrupt(s, LSI_SIST0_RST, 0); + if (!(s->sstat0 & LSI_SSTAT0_RST)) { + DeviceState *dev; + int id; + + for (id = 0; id < s->bus.ndev; id++) { + if (s->bus.devs[id]) { + dev = &s->bus.devs[id]->qdev; + dev->info->reset(dev); + } + } + s->sstat0 |= LSI_SSTAT0_RST; + lsi_script_scsi_interrupt(s, LSI_SIST0_RST, 0); + } } else { s->sstat0 &= ~LSI_SSTAT0_RST; } @@ -1552,9 +1729,9 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val) SCRIPTS register move instructions are. */ s->sfbr = val; break; - case 0x0a: case 0x0b: + case 0x0a: case 0x0b: /* Openserver writes to these readonly registers on startup */ - return; + return; case 0x0c: case 0x0d: case 0x0e: case 0x0f: /* Linux writes to these readonly registers on startup. */ return; @@ -1849,7 +2026,7 @@ static uint32_t lsi_ram_readw(void *opaque, target_phys_addr_t addr) val = s->script_ram[addr >> 2]; if (addr & 2) val >>= 16; - return le16_to_cpu(val); + return val; } static uint32_t lsi_ram_readl(void *opaque, target_phys_addr_t addr) @@ -1857,7 +2034,7 @@ static uint32_t lsi_ram_readl(void *opaque, target_phys_addr_t addr) LSIState *s = opaque; addr &= 0x1fff; - return le32_to_cpu(s->script_ram[addr >> 2]); + return s->script_ram[addr >> 2]; } static CPUReadMemoryFunc * const lsi_ram_readfn[3] = { @@ -1925,11 +2102,11 @@ static void lsi_io_writel(void *opaque, uint32_t addr, uint32_t val) } static void lsi_io_mapfunc(PCIDevice *pci_dev, int region_num, - uint32_t addr, uint32_t size, int type) + pcibus_t addr, pcibus_t size, int type) { LSIState *s = DO_UPCAST(LSIState, dev, pci_dev); - DPRINTF("Mapping IO at %08x\n", addr); + DPRINTF("Mapping IO at %08"FMT_PCIBUS"\n", addr); register_ioport_write(addr, 256, 1, lsi_io_writeb, s); register_ioport_read(addr, 256, 1, lsi_io_readb, s); @@ -1940,31 +2117,31 @@ static void lsi_io_mapfunc(PCIDevice *pci_dev, int region_num, } static void lsi_ram_mapfunc(PCIDevice *pci_dev, int region_num, - uint32_t addr, uint32_t size, int type) + pcibus_t addr, pcibus_t size, int type) { LSIState *s = DO_UPCAST(LSIState, dev, pci_dev); - DPRINTF("Mapping ram at %08x\n", addr); + DPRINTF("Mapping ram at %08"FMT_PCIBUS"\n", addr); s->script_ram_base = addr; cpu_register_physical_memory(addr + 0, 0x2000, s->ram_io_addr); } -static void lsi_mmio_mapfunc(PCIDevice *pci_dev, int region_num, - uint32_t addr, uint32_t size, int type) +static void lsi_scsi_reset(DeviceState *dev) { - LSIState *s = DO_UPCAST(LSIState, dev, pci_dev); + LSIState *s = DO_UPCAST(LSIState, dev.qdev, dev); - DPRINTF("Mapping registers at %08x\n", addr); - cpu_register_physical_memory(addr + 0, 0x400, s->mmio_io_addr); + lsi_soft_reset(s); } static void lsi_pre_save(void *opaque) { LSIState *s = opaque; - assert(s->dma_buf == NULL); - assert(s->current_dma_len == 0); - assert(s->active_commands == 0); + if (s->current) { + assert(s->current->dma_buf == NULL); + assert(s->current->dma_len == 0); + } + assert(QTAILQ_EMPTY(&s->queue)); } static const VMStateDescription vmstate_lsi_scsi = { @@ -1977,7 +2154,7 @@ static const VMStateDescription vmstate_lsi_scsi = { VMSTATE_PCI_DEVICE(dev, LSIState), VMSTATE_INT32(carry, LSIState), - VMSTATE_INT32(sense, LSIState), + VMSTATE_INT32(status, LSIState), VMSTATE_INT32(msg_action, LSIState), VMSTATE_INT32(msg_len, LSIState), VMSTATE_BUFFER(msg, LSIState), @@ -2057,11 +2234,15 @@ static int lsi_scsi_uninit(PCIDevice *d) cpu_unregister_io_memory(s->mmio_io_addr); cpu_unregister_io_memory(s->ram_io_addr); - qemu_free(s->queue); - return 0; } +static const struct SCSIBusOps lsi_scsi_ops = { + .transfer_data = lsi_transfer_data, + .complete = lsi_command_complete, + .cancel = lsi_request_cancelled +}; + static int lsi_scsi_init(PCIDevice *dev) { LSIState *s = DO_UPCAST(LSIState, dev, dev); @@ -2069,42 +2250,30 @@ static int lsi_scsi_init(PCIDevice *dev) pci_conf = s->dev.config; - /* PCI Vendor ID (word) */ - pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_LSI_LOGIC); - /* PCI device ID (word) */ - pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_LSI_53C895A); - /* PCI base class code */ - pci_config_set_class(pci_conf, PCI_CLASS_STORAGE_SCSI); - /* PCI subsystem ID */ - pci_conf[0x2e] = 0x00; - pci_conf[0x2f] = 0x10; /* PCI latency timer = 255 */ - pci_conf[0x0d] = 0xff; + pci_conf[PCI_LATENCY_TIMER] = 0xff; + /* TODO: RST# value should be 0 */ /* Interrupt pin 1 */ - pci_conf[0x3d] = 0x01; + pci_conf[PCI_INTERRUPT_PIN] = 0x01; s->mmio_io_addr = cpu_register_io_memory(lsi_mmio_readfn, - lsi_mmio_writefn, s); + lsi_mmio_writefn, s, + DEVICE_NATIVE_ENDIAN); s->ram_io_addr = cpu_register_io_memory(lsi_ram_readfn, - lsi_ram_writefn, s); - - pci_register_bar((struct PCIDevice *)s, 0, 256, - PCI_ADDRESS_SPACE_IO, lsi_io_mapfunc); - pci_register_bar((struct PCIDevice *)s, 1, 0x400, - PCI_ADDRESS_SPACE_MEM, lsi_mmio_mapfunc); - pci_register_bar((struct PCIDevice *)s, 2, 0x2000, - PCI_ADDRESS_SPACE_MEM, lsi_ram_mapfunc); - s->queue = qemu_malloc(sizeof(lsi_queue)); - s->queue_len = 1; - s->active_commands = 0; + lsi_ram_writefn, s, + DEVICE_NATIVE_ENDIAN); - lsi_soft_reset(s); + pci_register_bar(&s->dev, 0, 256, + PCI_BASE_ADDRESS_SPACE_IO, lsi_io_mapfunc); + pci_register_bar_simple(&s->dev, 1, 0x400, 0, s->mmio_io_addr); + pci_register_bar(&s->dev, 2, 0x2000, + PCI_BASE_ADDRESS_SPACE_MEMORY, lsi_ram_mapfunc); + QTAILQ_INIT(&s->queue); - scsi_bus_new(&s->bus, &dev->qdev, 1, LSI_MAX_DEVS, lsi_command_complete); + scsi_bus_new(&s->bus, &dev->qdev, 1, LSI_MAX_DEVS, &lsi_scsi_ops); if (!dev->qdev.hotplugged) { - scsi_bus_legacy_handle_cmdline(&s->bus); + return scsi_bus_legacy_handle_cmdline(&s->bus); } - vmstate_register(-1, &vmstate_lsi_scsi, s); return 0; } @@ -2112,8 +2281,14 @@ static PCIDeviceInfo lsi_info = { .qdev.name = "lsi53c895a", .qdev.alias = "lsi", .qdev.size = sizeof(LSIState), + .qdev.reset = lsi_scsi_reset, + .qdev.vmsd = &vmstate_lsi_scsi, .init = lsi_scsi_init, .exit = lsi_scsi_uninit, + .vendor_id = PCI_VENDOR_ID_LSI_LOGIC, + .device_id = PCI_DEVICE_ID_LSI_53C895A, + .class_id = PCI_CLASS_STORAGE_SCSI, + .subsystem_id = 0x1000, }; static void lsi53c895a_register_devices(void)