📄 lsi53c895a.c
字号:
/* * QEMU LSI53C895A SCSI Host Bus Adapter emulation * * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * * This code is licenced under the LGPL. *//* ??? Need to check if the {read,write}[wl] routines work properly on big-endian targets. */#include "vl.h"//#define DEBUG_LSI//#define DEBUG_LSI_REG#ifdef DEBUG_LSI#define DPRINTF(fmt, args...) \do { printf("lsi_scsi: " fmt , ##args); } while (0)#define BADF(fmt, args...) \do { fprintf(stderr, "lsi_scsi: error: " fmt , ##args); exit(1);} while (0)#else#define DPRINTF(fmt, args...) do {} while(0)#define BADF(fmt, args...) \do { fprintf(stderr, "lsi_scsi: error: " fmt , ##args);} while (0)#endif#define LSI_SCNTL0_TRG 0x01#define LSI_SCNTL0_AAP 0x02#define LSI_SCNTL0_EPC 0x08#define LSI_SCNTL0_WATN 0x10#define LSI_SCNTL0_START 0x20#define LSI_SCNTL1_SST 0x01#define LSI_SCNTL1_IARB 0x02#define LSI_SCNTL1_AESP 0x04#define LSI_SCNTL1_RST 0x08#define LSI_SCNTL1_CON 0x10#define LSI_SCNTL1_DHP 0x20#define LSI_SCNTL1_ADB 0x40#define LSI_SCNTL1_EXC 0x80#define LSI_SCNTL2_WSR 0x01#define LSI_SCNTL2_VUE0 0x02#define LSI_SCNTL2_VUE1 0x04#define LSI_SCNTL2_WSS 0x08#define LSI_SCNTL2_SLPHBEN 0x10#define LSI_SCNTL2_SLPMD 0x20#define LSI_SCNTL2_CHM 0x40#define LSI_SCNTL2_SDU 0x80#define LSI_ISTAT0_DIP 0x01#define LSI_ISTAT0_SIP 0x02#define LSI_ISTAT0_INTF 0x04#define LSI_ISTAT0_CON 0x08#define LSI_ISTAT0_SEM 0x10#define LSI_ISTAT0_SIGP 0x20#define LSI_ISTAT0_SRST 0x40#define LSI_ISTAT0_ABRT 0x80#define LSI_ISTAT1_SI 0x01#define LSI_ISTAT1_SRUN 0x02#define LSI_ISTAT1_FLSH 0x04#define LSI_SSTAT0_SDP0 0x01#define LSI_SSTAT0_RST 0x02#define LSI_SSTAT0_WOA 0x04#define LSI_SSTAT0_LOA 0x08#define LSI_SSTAT0_AIP 0x10#define LSI_SSTAT0_OLF 0x20#define LSI_SSTAT0_ORF 0x40#define LSI_SSTAT0_ILF 0x80#define LSI_SIST0_PAR 0x01#define LSI_SIST0_RST 0x02#define LSI_SIST0_UDC 0x04#define LSI_SIST0_SGE 0x08#define LSI_SIST0_RSL 0x10#define LSI_SIST0_SEL 0x20#define LSI_SIST0_CMP 0x40#define LSI_SIST0_MA 0x80#define LSI_SIST1_HTH 0x01#define LSI_SIST1_GEN 0x02#define LSI_SIST1_STO 0x04#define LSI_SIST1_SBMC 0x10#define LSI_SOCL_IO 0x01#define LSI_SOCL_CD 0x02#define LSI_SOCL_MSG 0x04#define LSI_SOCL_ATN 0x08#define LSI_SOCL_SEL 0x10#define LSI_SOCL_BSY 0x20#define LSI_SOCL_ACK 0x40#define LSI_SOCL_REQ 0x80#define LSI_DSTAT_IID 0x01#define LSI_DSTAT_SIR 0x04#define LSI_DSTAT_SSI 0x08#define LSI_DSTAT_ABRT 0x10#define LSI_DSTAT_BF 0x20#define LSI_DSTAT_MDPE 0x40#define LSI_DSTAT_DFE 0x80#define LSI_DCNTL_COM 0x01#define LSI_DCNTL_IRQD 0x02#define LSI_DCNTL_STD 0x04#define LSI_DCNTL_IRQM 0x08#define LSI_DCNTL_SSM 0x10#define LSI_DCNTL_PFEN 0x20#define LSI_DCNTL_PFF 0x40#define LSI_DCNTL_CLSE 0x80#define LSI_DMODE_MAN 0x01#define LSI_DMODE_BOF 0x02#define LSI_DMODE_ERMP 0x04#define LSI_DMODE_ERL 0x08#define LSI_DMODE_DIOM 0x10#define LSI_DMODE_SIOM 0x20#define LSI_CTEST2_DACK 0x01#define LSI_CTEST2_DREQ 0x02#define LSI_CTEST2_TEOP 0x04#define LSI_CTEST2_PCICIE 0x08#define LSI_CTEST2_CM 0x10#define LSI_CTEST2_CIO 0x20#define LSI_CTEST2_SIGP 0x40#define LSI_CTEST2_DDIR 0x80#define LSI_CTEST5_BL2 0x04#define LSI_CTEST5_DDIR 0x08#define LSI_CTEST5_MASR 0x10#define LSI_CTEST5_DFSN 0x20#define LSI_CTEST5_BBCK 0x40#define LSI_CTEST5_ADCK 0x80#define LSI_CCNTL0_DILS 0x01#define LSI_CCNTL0_DISFC 0x10#define LSI_CCNTL0_ENNDJ 0x20#define LSI_CCNTL0_PMJCTL 0x40#define LSI_CCNTL0_ENPMJ 0x80#define PHASE_DO 0#define PHASE_DI 1#define PHASE_CMD 2#define PHASE_ST 3#define PHASE_MO 6#define PHASE_MI 7#define PHASE_MASK 7/* The HBA is ID 7, so for simplicitly limit to 7 devices. */#define LSI_MAX_DEVS 7/* Maximum length of MSG IN data. */#define LSI_MAX_MSGIN_LEN 8/* Flag set if this is a tagged command. */#define LSI_TAG_VALID (1 << 16)typedef struct { uint32_t tag; uint32_t pending; int out;} lsi_queue;typedef struct { PCIDevice pci_dev; int mmio_io_addr; int ram_io_addr; uint32_t script_ram_base; int carry; /* ??? Should this be an a visible register somewhere? */ int sense; /* Action to take at the end of a MSG IN phase. 0 = COMMAND, 1 = disconect, 2 = DATA OUT, 3 = DATA IN. */ int msg_action; int msg_len; uint8_t msg[LSI_MAX_MSGIN_LEN]; /* 0 if SCRIPTS are running or stopped. * 1 if a Wait Reselect instruction has been issued. * 2 if processing DMA from lsi_execute_script. * 3 if a DMA operation is in progress. */ int waiting; SCSIDevice *scsi_dev[LSI_MAX_DEVS]; 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; uint8_t *dma_buf; lsi_queue *queue; int queue_len; int active_commands; uint32_t dsa; uint32_t temp; uint32_t dnad; uint32_t dbc; uint8_t istat0; uint8_t istat1; uint8_t dcmd; uint8_t dstat; uint8_t dien; uint8_t sist0; uint8_t sist1; uint8_t sien0; uint8_t sien1; uint8_t mbox0; uint8_t mbox1; uint8_t dfifo; uint8_t ctest3; uint8_t ctest4; uint8_t ctest5; uint8_t ccntl0; uint8_t ccntl1; uint32_t dsp; uint32_t dsps; uint8_t dmode; uint8_t dcntl; uint8_t scntl0; uint8_t scntl1; uint8_t scntl2; uint8_t scntl3; uint8_t sstat0; uint8_t sstat1; uint8_t scid; uint8_t sxfer; uint8_t socl; uint8_t sdid; uint8_t ssid; uint8_t sfbr; uint8_t stest1; uint8_t stest2; uint8_t stest3; uint8_t sidl; uint8_t stime0; uint8_t respid0; uint8_t respid1; uint32_t mmrs; uint32_t mmws; uint32_t sfs; uint32_t drs; uint32_t sbms; uint32_t dmbs; uint32_t dnad64; uint32_t pmjad1; uint32_t pmjad2; uint32_t rbc; uint32_t ua; uint32_t ia; uint32_t sbc; uint32_t csbc; uint32_t scratch[13]; /* SCRATCHA-SCRATCHR */ /* Script ram is stored as 32-bit words in host byteorder. */ uint32_t script_ram[2048];} LSIState;static void lsi_soft_reset(LSIState *s){ DPRINTF("Reset\n"); s->carry = 0; s->waiting = 0; s->dsa = 0; s->dnad = 0; s->dbc = 0; s->temp = 0; memset(s->scratch, 0, sizeof(s->scratch)); s->istat0 = 0; s->istat1 = 0; s->dcmd = 0; s->dstat = 0; s->dien = 0; s->sist0 = 0; s->sist1 = 0; s->sien0 = 0; s->sien1 = 0; s->mbox0 = 0; s->mbox1 = 0; s->dfifo = 0; s->ctest3 = 0; s->ctest4 = 0; s->ctest5 = 0; s->ccntl0 = 0; s->ccntl1 = 0; s->dsp = 0; s->dsps = 0; s->dmode = 0; s->dcntl = 0; s->scntl0 = 0xc0; s->scntl1 = 0; s->scntl2 = 0; s->scntl3 = 0; s->sstat0 = 0; s->sstat1 = 0; s->scid = 7; s->sxfer = 0; s->socl = 0; s->stest1 = 0; s->stest2 = 0; s->stest3 = 0; s->sidl = 0; s->stime0 = 0; s->respid0 = 0x80; s->respid1 = 0; s->mmrs = 0; s->mmws = 0; s->sfs = 0; s->drs = 0; s->sbms = 0; s->dmbs = 0; s->dnad64 = 0; s->pmjad1 = 0; s->pmjad2 = 0; s->rbc = 0; s->ua = 0; s->ia = 0; s->sbc = 0; s->csbc = 0;}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){ uint32_t buf; /* Optimize reading from SCRIPTS RAM. */ if ((addr & 0xffffe000) == s->script_ram_base) { return s->script_ram[(addr & 0x1fff) >> 2]; } cpu_physical_memory_read(addr, (uint8_t *)&buf, 4); return cpu_to_le32(buf);}static void lsi_stop_script(LSIState *s){ s->istat1 &= ~LSI_ISTAT1_SRUN;}static void lsi_update_irq(LSIState *s){ int level; static int last_level; /* It's unclear whether the DIP/SIP bits should be cleared when the Interrupt Status Registers are cleared or when istat0 is read. We currently do the formwer, which seems to work. */ level = 0; if (s->dstat) { if (s->dstat & s->dien) level = 1; s->istat0 |= LSI_ISTAT0_DIP; } else { s->istat0 &= ~LSI_ISTAT0_DIP; } if (s->sist0 || s->sist1) { if ((s->sist0 & s->sien0) || (s->sist1 & s->sien1)) level = 1; s->istat0 |= LSI_ISTAT0_SIP; } else { s->istat0 &= ~LSI_ISTAT0_SIP; } if (s->istat0 & LSI_ISTAT0_INTF) level = 1; if (level != last_level) { DPRINTF("Update IRQ level %d dstat %02x sist %02x%02x\n", level, s->dstat, s->sist1, s->sist0); last_level = level; } pci_set_irq(&s->pci_dev, 0, level);}/* Stop SCRIPTS execution and raise a SCSI interrupt. */static void lsi_script_scsi_interrupt(LSIState *s, int stat0, int stat1){ uint32_t mask0; uint32_t mask1; DPRINTF("SCSI Interrupt 0x%02x%02x prev 0x%02x%02x\n", stat1, stat0, s->sist1, s->sist0); s->sist0 |= stat0; s->sist1 |= stat1; /* Stop processor on fatal or unmasked interrupt. As a special hack we don't stop processing when raising STO. Instead continue execution and stop at the next insn that accesses the SCSI bus. */ mask0 = s->sien0 | ~(LSI_SIST0_CMP | LSI_SIST0_SEL | LSI_SIST0_RSL); mask1 = s->sien1 | ~(LSI_SIST1_GEN | LSI_SIST1_HTH); mask1 &= ~LSI_SIST1_STO; if (s->sist0 & mask0 || s->sist1 & mask1) { lsi_stop_script(s); } lsi_update_irq(s);}/* Stop SCRIPTS execution and raise a DMA interrupt. */static void lsi_script_dma_interrupt(LSIState *s, int stat){ DPRINTF("DMA Interrupt 0x%x prev 0x%x\n", stat, s->dstat); s->dstat |= stat; lsi_update_irq(s); lsi_stop_script(s);}static inline void lsi_set_phase(LSIState *s, int phase){ s->sstat1 = (s->sstat1 & ~PHASE_MASK) | phase;}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; } else { s->dsp = s->pmjad2; } DPRINTF("Data phase mismatch jump to %08x\n", s->dsp); } else { DPRINTF("Phase mismatch interrupt\n"); lsi_script_scsi_interrupt(s, LSI_SIST0_MA, 0); lsi_stop_script(s); } lsi_set_phase(s, new_phase);}/* Resume SCRIPTS execution after a DMA operation. */static void lsi_resume_script(LSIState *s){ if (s->waiting != 2) { s->waiting = 0; lsi_execute_script(s); } else { s->waiting = 0; }}/* Initiate a SCSI layer data transfer. */static void lsi_do_dma(LSIState *s, int out){ uint32_t count; uint32_t addr; if (!s->current_dma_len) { /* Wait until data is available. */ DPRINTF("DMA no data available\n"); return; } count = s->dbc; if (count > s->current_dma_len) count = s->current_dma_len; DPRINTF("DMA addr=0x%08x len=%d\n", s->dnad, count); addr = s->dnad; s->csbc += count; s->dnad += count; s->dbc -= count;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -