diff options
Diffstat (limited to 'hw')
-rw-r--r-- | hw/lsi53c895a.c | 43 |
1 files changed, 40 insertions, 3 deletions
diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c index 7fed1d8664..ab0de86104 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", |