X-Git-Url: https://vcs.maemo.org/git/?a=blobdiff_plain;ds=sidebyside;f=hw%2Fesp.c;h=96c6b34937ca4a40fa9561d887dfb2dd92c6eb48;hb=cd346349b45ef056f138a184f660b8c34c3213cc;hp=991e5151feca1b2a2d97870deed1969e1b3a9a8f;hpb=a917d384ac0d09cd68266a6f2ca5c94212c8f81e;p=qemu diff --git a/hw/esp.c b/hw/esp.c index 991e515..96c6b34 100644 --- a/hw/esp.c +++ b/hw/esp.c @@ -1,8 +1,8 @@ /* - * QEMU ESP emulation - * + * QEMU ESP/NCR53C9x emulation + * * Copyright (c) 2005-2006 Fabrice Bellard - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights @@ -26,33 +26,36 @@ /* debug ESP card */ //#define DEBUG_ESP +/* + * On Sparc32, this is the ESP (NCR53C90) part of chip STP2000 (Master I/O), also + * produced as NCR89C100. See + * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C100.txt + * and + * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR53C9X.txt + */ + #ifdef DEBUG_ESP #define DPRINTF(fmt, args...) \ do { printf("ESP: " fmt , ##args); } while (0) -#define pic_set_irq(irq, level) \ -do { printf("ESP: set_irq(%d): %d\n", (irq), (level)); pic_set_irq((irq),(level));} while (0) #else #define DPRINTF(fmt, args...) #endif -#define ESPDMA_REGS 4 -#define ESPDMA_MAXADDR (ESPDMA_REGS * 4 - 1) -#define ESP_MAXREG 0x3f +#define ESP_MASK 0x3f +#define ESP_REGS 16 +#define ESP_SIZE (ESP_REGS * 4) #define TI_BUFSZ 32 -#define DMA_VER 0xa0000000 -#define DMA_INTR 1 -#define DMA_INTREN 0x10 -#define DMA_WRITE_MEM 0x100 -#define DMA_LOADED 0x04000000 +/* The HBA is ID 7, so for simplicitly limit to 7 devices. */ +#define ESP_MAX_DEVS 7 + typedef struct ESPState ESPState; struct ESPState { + qemu_irq irq; BlockDriverState **bd; - uint8_t rregs[ESP_MAXREG]; - uint8_t wregs[ESP_MAXREG]; - int irq; - uint32_t espdmaregs[ESPDMA_REGS]; - uint32_t ti_size; + uint8_t rregs[ESP_REGS]; + uint8_t wregs[ESP_REGS]; + int32_t ti_size; uint32_t ti_rptr, ti_wptr; uint8_t ti_buf[TI_BUFSZ]; int sense; @@ -63,9 +66,14 @@ struct ESPState { int cmdlen; int do_cmd; + /* The amount of data left in the current DMA transfer. */ uint32_t dma_left; + /* The size of the current DMA transfer. Zero if no transfer is in + progress. */ + uint32_t dma_counter; uint8_t *async_buf; uint32_t async_len; + void *dma_opaque; }; #define STAT_DO 0x00 @@ -93,17 +101,15 @@ static int get_cmd(ESPState *s, uint8_t *buf) uint32_t dmalen; int target; - dmalen = s->wregs[0] | (s->wregs[1] << 8); + dmalen = s->rregs[0] | (s->rregs[1] << 8); target = s->wregs[4] & 7; DPRINTF("get_cmd: len %d target %d\n", dmalen, target); if (s->dma) { - DPRINTF("DMA Direction: %c, addr 0x%8.8x\n", - s->espdmaregs[0] & DMA_WRITE_MEM ? 'w': 'r', s->espdmaregs[1]); - sparc_iommu_memory_read(s->espdmaregs[1], buf, dmalen); + espdma_memory_read(s->dma_opaque, buf, dmalen); } else { - buf[0] = 0; - memcpy(&buf[1], s->ti_buf, dmalen); - dmalen++; + buf[0] = 0; + memcpy(&buf[1], s->ti_buf, dmalen); + dmalen++; } s->ti_size = 0; @@ -116,14 +122,13 @@ static int get_cmd(ESPState *s, uint8_t *buf) s->async_len = 0; } - if (target >= 4 || !s->scsi_dev[target]) { + if (target >= MAX_DISKS || !s->scsi_dev[target]) { // No such drive - s->rregs[4] = STAT_IN; - s->rregs[5] = INTR_DC; - s->rregs[6] = SEQ_0; - s->espdmaregs[0] |= DMA_INTR; - pic_set_irq(s->irq, 1); - return 0; + s->rregs[4] = STAT_IN; + s->rregs[5] = INTR_DC; + s->rregs[6] = SEQ_0; + qemu_irq_raise(s->irq); + return 0; } s->current_dev = s->scsi_dev[target]; return dmalen; @@ -137,25 +142,22 @@ static void do_cmd(ESPState *s, uint8_t *buf) DPRINTF("do_cmd: busid 0x%x\n", buf[0]); lun = buf[0] & 7; datalen = scsi_send_command(s->current_dev, 0, &buf[1], lun); - if (datalen == 0) { - s->ti_size = 0; - } else { + s->ti_size = datalen; + if (datalen != 0) { s->rregs[4] = STAT_IN | STAT_TC; s->dma_left = 0; + s->dma_counter = 0; if (datalen > 0) { s->rregs[4] |= STAT_DI; - s->ti_size = datalen; scsi_read_data(s->current_dev, 0); } else { s->rregs[4] |= STAT_DO; - s->ti_size = -datalen; scsi_write_data(s->current_dev, 0); } } s->rregs[5] = INTR_BS | INTR_FC; s->rregs[6] = SEQ_CD; - s->espdmaregs[0] |= DMA_INTR; - pic_set_irq(s->irq, 1); + qemu_irq_raise(s->irq); } static void handle_satn(ESPState *s) @@ -174,12 +176,10 @@ static void handle_satn_stop(ESPState *s) if (s->cmdlen) { DPRINTF("Set ATN & Stop: cmdlen %d\n", s->cmdlen); s->do_cmd = 1; - s->espdmaregs[1] += s->cmdlen; s->rregs[4] = STAT_IN | STAT_TC | STAT_CD; s->rregs[5] = INTR_BS | INTR_FC; s->rregs[6] = SEQ_CD; - s->espdmaregs[0] |= DMA_INTR; - pic_set_irq(s->irq, 1); + qemu_irq_raise(s->irq); } } @@ -189,21 +189,17 @@ static void write_response(ESPState *s) s->ti_buf[0] = s->sense; s->ti_buf[1] = 0; if (s->dma) { - DPRINTF("DMA Direction: %c\n", - s->espdmaregs[0] & DMA_WRITE_MEM ? 'w': 'r'); - sparc_iommu_memory_write(s->espdmaregs[1], s->ti_buf, 2); - s->rregs[4] = STAT_IN | STAT_TC | STAT_ST; - s->rregs[5] = INTR_BS | INTR_FC; - s->rregs[6] = SEQ_CD; + espdma_memory_write(s->dma_opaque, s->ti_buf, 2); + s->rregs[4] = STAT_IN | STAT_TC | STAT_ST; + s->rregs[5] = INTR_BS | INTR_FC; + s->rregs[6] = SEQ_CD; } else { - s->ti_size = 2; - s->ti_rptr = 0; - s->ti_wptr = 0; - s->rregs[7] = 2; + s->ti_size = 2; + s->ti_rptr = 0; + s->ti_wptr = 0; + s->rregs[7] = 2; } - s->espdmaregs[0] |= DMA_INTR; - pic_set_irq(s->irq, 1); - + qemu_irq_raise(s->irq); } static void esp_dma_done(ESPState *s) @@ -212,24 +208,21 @@ static void esp_dma_done(ESPState *s) s->rregs[5] = INTR_BS; s->rregs[6] = 0; s->rregs[7] = 0; - s->espdmaregs[0] |= DMA_INTR; - pic_set_irq(s->irq, 1); + s->rregs[0] = 0; + s->rregs[1] = 0; + qemu_irq_raise(s->irq); } static void esp_do_dma(ESPState *s) { - uint32_t addr, len; + uint32_t len; int to_device; - to_device = (s->espdmaregs[0] & DMA_WRITE_MEM) == 0; - addr = s->espdmaregs[1]; + to_device = (s->ti_size < 0); len = s->dma_left; - DPRINTF("DMA address %08x len %08x\n", addr, len); if (s->do_cmd) { - s->espdmaregs[1] += len; - s->ti_size -= len; DPRINTF("command len %d + %d\n", s->cmdlen, len); - sparc_iommu_memory_read(addr, &s->cmdbuf[s->cmdlen], len); + espdma_memory_read(s->dma_opaque, &s->cmdbuf[s->cmdlen], len); s->ti_size = 0; s->cmdlen = 0; s->do_cmd = 0; @@ -244,23 +237,32 @@ static void esp_do_dma(ESPState *s) len = s->async_len; } if (to_device) { - sparc_iommu_memory_read(addr, s->async_buf, len); + espdma_memory_read(s->dma_opaque, s->async_buf, len); } else { - sparc_iommu_memory_write(addr, s->async_buf, len); + espdma_memory_write(s->dma_opaque, s->async_buf, len); } - s->ti_size -= len; s->dma_left -= len; s->async_buf += len; s->async_len -= len; - s->espdmaregs[1] += len; + if (to_device) + s->ti_size += len; + else + s->ti_size -= len; if (s->async_len == 0) { if (to_device) { + // ti_size is negative scsi_write_data(s->current_dev, 0); } else { scsi_read_data(s->current_dev, 0); + /* If there is still data to be read from the device then + complete the DMA operation immeriately. Otherwise defer + until the scsi layer has completed. */ + if (s->dma_left == 0 && s->ti_size > 0) { + esp_dma_done(s); + } } - } - if (s->dma_left == 0) { + } else { + /* Partially filled a scsi buffer. Complete immediately. */ esp_dma_done(s); } } @@ -287,8 +289,13 @@ static void esp_command_complete(void *opaque, int reason, uint32_t tag, DPRINTF("transfer %d/%d\n", s->dma_left, s->ti_size); s->async_len = arg; s->async_buf = scsi_get_buf(s->current_dev, 0); - if (s->dma_left) + if (s->dma_left) { esp_do_dma(s); + } else if (s->dma_counter != 0 && s->ti_size <= 0) { + /* If this was the last part of a DMA transfer then the + completion interrupt is deferred to here. */ + esp_dma_done(s); + } } } @@ -296,13 +303,16 @@ static void handle_ti(ESPState *s) { uint32_t dmalen, minlen; - dmalen = s->wregs[0] | (s->wregs[1] << 8); + dmalen = s->rregs[0] | (s->rregs[1] << 8); if (dmalen==0) { dmalen=0x10000; } + s->dma_counter = dmalen; if (s->do_cmd) minlen = (dmalen < 32) ? dmalen : 32; + else if (s->ti_size < 0) + minlen = (dmalen < -s->ti_size) ? dmalen : -s->ti_size; else minlen = (dmalen < s->ti_size) ? dmalen : s->ti_size; DPRINTF("Transfer Information len %d\n", minlen); @@ -323,10 +333,10 @@ static void handle_ti(ESPState *s) static void esp_reset(void *opaque) { ESPState *s = opaque; - memset(s->rregs, 0, ESP_MAXREG); - memset(s->wregs, 0, ESP_MAXREG); + + memset(s->rregs, 0, ESP_REGS); + memset(s->wregs, 0, ESP_REGS); s->rregs[0x0e] = 0x4; // Indicate fas100a - memset(s->espdmaregs, 0, ESPDMA_REGS * 4); s->ti_size = 0; s->ti_rptr = 0; s->ti_wptr = 0; @@ -334,18 +344,24 @@ static void esp_reset(void *opaque) s->do_cmd = 0; } +static void parent_esp_reset(void *opaque, int irq, int level) +{ + if (level) + esp_reset(opaque); +} + static uint32_t esp_mem_readb(void *opaque, target_phys_addr_t addr) { ESPState *s = opaque; uint32_t saddr; - saddr = (addr & ESP_MAXREG) >> 2; + saddr = (addr & ESP_MASK) >> 2; DPRINTF("read reg[%d]: 0x%2.2x\n", saddr, s->rregs[saddr]); switch (saddr) { case 2: - // FIFO - if (s->ti_size > 0) { - s->ti_size--; + // FIFO + if (s->ti_size > 0) { + s->ti_size--; if ((s->rregs[4] & 6) == 0) { /* Data in/out. */ fprintf(stderr, "esp: PIO data read not implemented\n"); @@ -353,22 +369,21 @@ static uint32_t esp_mem_readb(void *opaque, target_phys_addr_t addr) } else { s->rregs[2] = s->ti_buf[s->ti_rptr++]; } - pic_set_irq(s->irq, 1); - } - if (s->ti_size == 0) { + qemu_irq_raise(s->irq); + } + if (s->ti_size == 0) { s->ti_rptr = 0; s->ti_wptr = 0; } - break; + break; case 5: // interrupt // Clear interrupt/error status bits s->rregs[4] &= ~(STAT_IN | STAT_GE | STAT_PE); - pic_set_irq(s->irq, 0); - s->espdmaregs[0] &= ~DMA_INTR; + qemu_irq_lower(s->irq); break; default: - break; + break; } return s->rregs[saddr]; } @@ -378,16 +393,15 @@ static void esp_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) ESPState *s = opaque; uint32_t saddr; - saddr = (addr & ESP_MAXREG) >> 2; + saddr = (addr & ESP_MASK) >> 2; DPRINTF("write reg[%d]: 0x%2.2x -> 0x%2.2x\n", saddr, s->wregs[saddr], val); switch (saddr) { case 0: case 1: - s->rregs[saddr] = val; s->rregs[4] &= ~STAT_TC; break; case 2: - // FIFO + // FIFO if (s->do_cmd) { s->cmdbuf[s->cmdlen++] = val & 0xff; } else if ((s->rregs[4] & 6) == 0) { @@ -399,68 +413,73 @@ static void esp_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) s->ti_size++; s->ti_buf[s->ti_wptr++] = val & 0xff; } - break; + break; case 3: s->rregs[saddr] = val; - // Command - if (val & 0x80) { - s->dma = 1; - } else { - s->dma = 0; - } - switch(val & 0x7f) { - case 0: - DPRINTF("NOP (%2.2x)\n", val); - break; - case 1: - DPRINTF("Flush FIFO (%2.2x)\n", val); + // Command + if (val & 0x80) { + s->dma = 1; + /* Reload DMA counter. */ + s->rregs[0] = s->wregs[0]; + s->rregs[1] = s->wregs[1]; + } else { + s->dma = 0; + } + switch(val & 0x7f) { + case 0: + DPRINTF("NOP (%2.2x)\n", val); + break; + case 1: + DPRINTF("Flush FIFO (%2.2x)\n", val); //s->ti_size = 0; - s->rregs[5] = INTR_FC; - s->rregs[6] = 0; - break; - case 2: - DPRINTF("Chip reset (%2.2x)\n", val); - esp_reset(s); - break; - case 3: - DPRINTF("Bus reset (%2.2x)\n", val); - s->rregs[5] = INTR_RST; + s->rregs[5] = INTR_FC; + s->rregs[6] = 0; + break; + case 2: + DPRINTF("Chip reset (%2.2x)\n", val); + esp_reset(s); + break; + case 3: + DPRINTF("Bus reset (%2.2x)\n", val); + s->rregs[5] = INTR_RST; if (!(s->wregs[8] & 0x40)) { - s->espdmaregs[0] |= DMA_INTR; - pic_set_irq(s->irq, 1); + qemu_irq_raise(s->irq); } - break; - case 0x10: - handle_ti(s); - break; - case 0x11: - DPRINTF("Initiator Command Complete Sequence (%2.2x)\n", val); - write_response(s); - break; - case 0x12: - DPRINTF("Message Accepted (%2.2x)\n", val); - write_response(s); - s->rregs[5] = INTR_DC; - s->rregs[6] = 0; - break; - case 0x1a: - DPRINTF("Set ATN (%2.2x)\n", val); - break; - case 0x42: - DPRINTF("Set ATN (%2.2x)\n", val); - handle_satn(s); - break; - case 0x43: - DPRINTF("Set ATN & stop (%2.2x)\n", val); - handle_satn_stop(s); - break; - default: - DPRINTF("Unhandled ESP command (%2.2x)\n", val); - break; - } - break; + break; + case 0x10: + handle_ti(s); + break; + case 0x11: + DPRINTF("Initiator Command Complete Sequence (%2.2x)\n", val); + write_response(s); + break; + case 0x12: + DPRINTF("Message Accepted (%2.2x)\n", val); + write_response(s); + s->rregs[5] = INTR_DC; + s->rregs[6] = 0; + break; + case 0x1a: + DPRINTF("Set ATN (%2.2x)\n", val); + break; + case 0x42: + DPRINTF("Set ATN (%2.2x)\n", val); + handle_satn(s); + break; + case 0x43: + DPRINTF("Set ATN & stop (%2.2x)\n", val); + handle_satn_stop(s); + break; + case 0x44: + DPRINTF("Enable selection (%2.2x)\n", val); + break; + default: + DPRINTF("Unhandled ESP command (%2.2x)\n", val); + break; + } + break; case 4 ... 7: - break; + break; case 8: s->rregs[saddr] = val; break; @@ -473,7 +492,7 @@ static void esp_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) s->rregs[saddr] = val; break; default: - break; + break; } s->wregs[saddr] = val; } @@ -490,126 +509,94 @@ static CPUWriteMemoryFunc *esp_mem_write[3] = { esp_mem_writeb, }; -static uint32_t espdma_mem_readl(void *opaque, target_phys_addr_t addr) -{ - ESPState *s = opaque; - uint32_t saddr; - - saddr = (addr & ESPDMA_MAXADDR) >> 2; - DPRINTF("read dmareg[%d]: 0x%8.8x\n", saddr, s->espdmaregs[saddr]); - - return s->espdmaregs[saddr]; -} - -static void espdma_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) -{ - ESPState *s = opaque; - uint32_t saddr; - - saddr = (addr & ESPDMA_MAXADDR) >> 2; - DPRINTF("write dmareg[%d]: 0x%8.8x -> 0x%8.8x\n", saddr, s->espdmaregs[saddr], val); - switch (saddr) { - case 0: - if (!(val & DMA_INTREN)) - pic_set_irq(s->irq, 0); - if (val & 0x80) { - esp_reset(s); - } else if (val & 0x40) { - val &= ~0x40; - } else if (val == 0) - val = 0x40; - val &= 0x0fffffff; - val |= DMA_VER; - break; - case 1: - s->espdmaregs[0] |= DMA_LOADED; - break; - default: - break; - } - s->espdmaregs[saddr] = val; -} - -static CPUReadMemoryFunc *espdma_mem_read[3] = { - espdma_mem_readl, - espdma_mem_readl, - espdma_mem_readl, -}; - -static CPUWriteMemoryFunc *espdma_mem_write[3] = { - espdma_mem_writel, - espdma_mem_writel, - espdma_mem_writel, -}; - static void esp_save(QEMUFile *f, void *opaque) { ESPState *s = opaque; - unsigned int i; - qemu_put_buffer(f, s->rregs, ESP_MAXREG); - qemu_put_buffer(f, s->wregs, ESP_MAXREG); - qemu_put_be32s(f, &s->irq); - for (i = 0; i < ESPDMA_REGS; i++) - qemu_put_be32s(f, &s->espdmaregs[i]); + qemu_put_buffer(f, s->rregs, ESP_REGS); + qemu_put_buffer(f, s->wregs, ESP_REGS); qemu_put_be32s(f, &s->ti_size); qemu_put_be32s(f, &s->ti_rptr); qemu_put_be32s(f, &s->ti_wptr); qemu_put_buffer(f, s->ti_buf, TI_BUFSZ); + qemu_put_be32s(f, &s->sense); qemu_put_be32s(f, &s->dma); + qemu_put_buffer(f, s->cmdbuf, TI_BUFSZ); + qemu_put_be32s(f, &s->cmdlen); + qemu_put_be32s(f, &s->do_cmd); + qemu_put_be32s(f, &s->dma_left); + // There should be no transfers in progress, so dma_counter is not saved } static int esp_load(QEMUFile *f, void *opaque, int version_id) { ESPState *s = opaque; - unsigned int i; - - if (version_id != 1) - return -EINVAL; - - qemu_get_buffer(f, s->rregs, ESP_MAXREG); - qemu_get_buffer(f, s->wregs, ESP_MAXREG); - qemu_get_be32s(f, &s->irq); - for (i = 0; i < ESPDMA_REGS; i++) - qemu_get_be32s(f, &s->espdmaregs[i]); + + if (version_id != 3) + return -EINVAL; // Cannot emulate 2 + + qemu_get_buffer(f, s->rregs, ESP_REGS); + qemu_get_buffer(f, s->wregs, ESP_REGS); qemu_get_be32s(f, &s->ti_size); qemu_get_be32s(f, &s->ti_rptr); qemu_get_be32s(f, &s->ti_wptr); qemu_get_buffer(f, s->ti_buf, TI_BUFSZ); + qemu_get_be32s(f, &s->sense); qemu_get_be32s(f, &s->dma); + qemu_get_buffer(f, s->cmdbuf, TI_BUFSZ); + qemu_get_be32s(f, &s->cmdlen); + qemu_get_be32s(f, &s->do_cmd); + qemu_get_be32s(f, &s->dma_left); return 0; } -void esp_init(BlockDriverState **bd, int irq, uint32_t espaddr, uint32_t espdaddr) +void esp_scsi_attach(void *opaque, BlockDriverState *bd, int id) +{ + ESPState *s = (ESPState *)opaque; + + if (id < 0) { + for (id = 0; id < ESP_MAX_DEVS; id++) { + if (s->scsi_dev[id] == NULL) + break; + } + } + if (id >= ESP_MAX_DEVS) { + DPRINTF("Bad Device ID %d\n", id); + return; + } + if (s->scsi_dev[id]) { + DPRINTF("Destroying device %d\n", id); + scsi_disk_destroy(s->scsi_dev[id]); + } + DPRINTF("Attaching block device %d\n", id); + /* Command queueing is not implemented. */ + s->scsi_dev[id] = scsi_disk_init(bd, 0, esp_command_complete, s); +} + +void *esp_init(BlockDriverState **bd, target_phys_addr_t espaddr, + void *dma_opaque, qemu_irq irq, qemu_irq *reset) { ESPState *s; - int esp_io_memory, espdma_io_memory; - int i; + int esp_io_memory; s = qemu_mallocz(sizeof(ESPState)); if (!s) - return; + return NULL; s->bd = bd; s->irq = irq; + s->dma_opaque = dma_opaque; esp_io_memory = cpu_register_io_memory(0, esp_mem_read, esp_mem_write, s); - cpu_register_physical_memory(espaddr, ESP_MAXREG*4, esp_io_memory); - - espdma_io_memory = cpu_register_io_memory(0, espdma_mem_read, espdma_mem_write, s); - cpu_register_physical_memory(espdaddr, 16, espdma_io_memory); + cpu_register_physical_memory(espaddr, ESP_SIZE, esp_io_memory); esp_reset(s); - register_savevm("esp", espaddr, 1, esp_save, esp_load, s); + register_savevm("esp", espaddr, 3, esp_save, esp_load, s); qemu_register_reset(esp_reset, s); - for (i = 0; i < MAX_DISKS; i++) { - if (bs_table[i]) { - /* Command queueing is not implemented. */ - s->scsi_dev[i] = - scsi_disk_init(bs_table[i], 0, esp_command_complete, s); - } - } -} + *reset = *qemu_allocate_irqs(parent_esp_reset, s, 1); + + return s; +}