summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'hw/lsi53c895a.c')
-rw-r--r--hw/lsi53c895a.c43
1 files changed, 40 insertions, 3 deletions
diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c
index 7fed1d866..ab0de8610 100644
--- a/hw/lsi53c895a.c
+++ b/hw/lsi53c895a.c
@@ -154,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
@@ -272,6 +275,11 @@ 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)
{
DPRINTF("Reset\n");
@@ -362,6 +370,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, uint32_t tag);
static inline uint32_t read_dword(LSIState *s, uint32_t addr)
{
@@ -382,6 +391,7 @@ static void lsi_stop_script(LSIState *s)
static void lsi_update_irq(LSIState *s)
{
+ int i;
int level;
static int last_level;
@@ -413,6 +423,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");
+ for (i = 0; i < s->active_commands; i++) {
+ if (s->queue[i].pending) {
+ lsi_reselect(s, s->queue[i].tag);
+ break;
+ }
+ }
+ }
}
/* Stop SCRIPTS execution and raise a SCSI interrupt. */
@@ -607,6 +628,10 @@ static void lsi_reselect(LSIState *s, uint32_t tag)
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
@@ -622,7 +647,14 @@ static int lsi_queue_tag(LSIState *s, uint32_t tag, uint32_t arg)
BADF("Multiple IO pending for tag %d\n", tag);
}
p->pending = arg;
- if (s->waiting == 1) {
+ /* 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, tag);
return 0;
@@ -659,10 +691,13 @@ static void lsi_command_complete(SCSIBus *bus, int reason, uint32_t tag,
return;
}
- if (s->waiting == 1 || tag != s->current_tag) {
+ if (s->waiting == 1 || tag != s->current_tag ||
+ (lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON))) {
if (lsi_queue_tag(s, tag, arg))
return;
}
+
+ /* host adapter (re)connected */
DPRINTF("Data ready tag=0x%x len=%d\n", tag, arg);
s->current_dma_len = arg;
s->command_complete = 1;
@@ -1071,7 +1106,9 @@ again:
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",