aboutsummaryrefslogtreecommitdiff
path: root/hw/lsi53c895a.c
diff options
context:
space:
mode:
authorpbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>2006-08-12 01:04:27 +0000
committerpbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>2006-08-12 01:04:27 +0000
commit4d611c9a2f4c5d9080d8b6a6f0d7431233cd56f9 (patch)
tree6217063ef291bd680f1c81a82bdaef7345356c16 /hw/lsi53c895a.c
parent4ca9c76f3620dc20e56d9b7027a6f1115ea48eea (diff)
SCSI and USB async IO support.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2107 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'hw/lsi53c895a.c')
-rw-r--r--hw/lsi53c895a.c80
1 files changed, 52 insertions, 28 deletions
diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c
index 24dff0eff5..8f56725348 100644
--- a/hw/lsi53c895a.c
+++ b/hw/lsi53c895a.c
@@ -152,6 +152,9 @@ do { fprintf(stderr, "lsi_scsi: " fmt , ##args); } while (0)
/* The HBA is ID 7, so for simplicitly limit to 7 devices. */
#define LSI_MAX_DEVS 7
+/* Size of internal DMA buffer for async IO requests. */
+#define LSI_DMA_BLOCK_SIZE 0x10000
+
typedef struct {
PCIDevice pci_dev;
int mmio_io_addr;
@@ -162,7 +165,9 @@ typedef struct {
int carry; /* ??? Should this be an a visible register somewhere? */
int sense;
uint8_t msg;
- /* Nonzero if a Wait Reselect instruction has been issued. */
+ /* 0 if SCRIPTS are running or stopped.
+ * 1 if a Wait Reselect instruction has been issued.
+ * 2 if a DMA operation is in progress. */
int waiting;
SCSIDevice *scsi_dev[LSI_MAX_DEVS];
SCSIDevice *current_dev;
@@ -226,6 +231,7 @@ typedef struct {
uint32_t csbc;
uint32_t scratch[13]; /* SCRATCHA-SCRATCHR */
+ uint8_t dma_buf[LSI_DMA_BLOCK_SIZE];
/* Script ram is stored as 32-bit words in host byteorder. */
uint32_t script_ram[2048];
} LSIState;
@@ -295,6 +301,7 @@ static void lsi_soft_reset(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 inline uint32_t read_dword(LSIState *s, uint32_t addr)
{
@@ -402,21 +409,20 @@ static void lsi_bad_phase(LSIState *s, int out, int new_phase)
lsi_set_phase(s, new_phase);
}
+/* Initiate a SCSI layer data transfer. */
static void lsi_do_dma(LSIState *s, int out)
{
- uint8_t buf[TARGET_PAGE_SIZE];
- uint32_t addr;
uint32_t count;
- int n;
count = s->dbc;
- addr = s->dnad;
- DPRINTF("DMA %s addr=0x%08x len=%d avail=%d\n", out ? "out" : "in",
+ if (count > LSI_DMA_BLOCK_SIZE)
+ count = LSI_DMA_BLOCK_SIZE;
+ DPRINTF("DMA addr=0x%08x len=%d avail=%d\n",
addr, count, s->data_len);
/* ??? Too long transfers are truncated. Don't know if this is the
correct behavior. */
if (count > s->data_len) {
- /* If the DMA length is greater then the device data length then
+ /* If the DMA length is greater than the device data length then
a phase mismatch will occur. */
count = s->data_len;
s->dbc = count;
@@ -426,20 +432,47 @@ static void lsi_do_dma(LSIState *s, int out)
s->csbc += count;
/* ??? Set SFBR to first data byte. */
- while (count) {
- n = (count > TARGET_PAGE_SIZE) ? TARGET_PAGE_SIZE : count;
- if (out) {
- cpu_physical_memory_read(addr, buf, n);
- scsi_write_data(s->current_dev, buf, n);
- } else {
- scsi_read_data(s->current_dev, buf, n);
- cpu_physical_memory_write(addr, buf, n);
- }
- addr += n;
- count -= n;
+ if ((s->sstat1 & PHASE_MASK) == PHASE_DO) {
+ cpu_physical_memory_read(s->dnad, s->dma_buf, count);
+ scsi_write_data(s->current_dev, s->dma_buf, count);
+ } else {
+ scsi_read_data(s->current_dev, s->dma_buf, count);
}
+ /* If the DMA did not complete then suspend execution. */
+ if (s->dbc)
+ s->waiting = 2;
}
+/* Callback to indicate that the SCSI layer has completed a transfer. */
+static void lsi_command_complete(void *opaque, uint32_t reason, int sense)
+{
+ LSIState *s = (LSIState *)opaque;
+ uint32_t count;
+ int out;
+
+ out = ((s->sstat1 & PHASE_MASK) == PHASE_DO);
+ count = s->dbc;
+ if (count > LSI_DMA_BLOCK_SIZE)
+ count = LSI_DMA_BLOCK_SIZE;
+ if (!out)
+ cpu_physical_memory_write(s->dnad, s->dma_buf, count);
+ s->dnad += count;
+ s->dbc -= count;
+
+ if (reason == SCSI_REASON_DONE) {
+ DPRINTF("Command complete sense=%d\n", sense);
+ s->sense = sense;
+ lsi_set_phase(s, PHASE_ST);
+ }
+
+ if (s->dbc) {
+ lsi_do_dma(s, out);
+ } else if (s->waiting == 2) {
+ /* Restart SCRIPTS execution. */
+ s->waiting = 0;
+ lsi_execute_script(s);
+ }
+}
static void lsi_do_command(LSIState *s)
{
@@ -461,15 +494,6 @@ static void lsi_do_command(LSIState *s)
}
}
-static void lsi_command_complete(void *opaque, uint32_t tag, int sense)
-{
- LSIState *s = (LSIState *)opaque;
-
- DPRINTF("Command complete sense=%d\n", sense);
- s->sense = sense;
- lsi_set_phase(s, PHASE_ST);
-}
-
static void lsi_do_status(LSIState *s)
{
DPRINTF("Get status len=%d sense=%d\n", s->dbc, s->sense);
@@ -1134,7 +1158,7 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
s->istat0 &= ~LSI_ISTAT0_INTF;
lsi_update_irq(s);
}
- if (s->waiting && val & LSI_ISTAT0_SIGP) {
+ if (s->waiting == 1 && val & LSI_ISTAT0_SIGP) {
DPRINTF("Woken by SIGP\n");
s->waiting = 0;
s->dsp = s->dnad;