version 1.1.1.12, 2018/04/24 19:01:38
|
version 1.1.1.13, 2018/04/24 19:29:33
|
Line 25
|
Line 25
|
#include "sysbus.h" |
#include "sysbus.h" |
#include "scsi.h" |
#include "scsi.h" |
#include "esp.h" |
#include "esp.h" |
|
#include "trace.h" |
/* debug ESP card */ |
|
//#define DEBUG_ESP |
|
|
|
/* |
/* |
* On Sparc32, this is the ESP (NCR53C90) part of chip STP2000 (Master I/O), |
* On Sparc32, this is the ESP (NCR53C90) part of chip STP2000 (Master I/O), |
Line 37
|
Line 35
|
* http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR53C9X.txt |
* http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR53C9X.txt |
*/ |
*/ |
|
|
#ifdef DEBUG_ESP |
|
#define DPRINTF(fmt, ...) \ |
|
do { printf("ESP: " fmt , ## __VA_ARGS__); } while (0) |
|
#else |
|
#define DPRINTF(fmt, ...) do {} while (0) |
|
#endif |
|
|
|
#define ESP_ERROR(fmt, ...) \ |
#define ESP_ERROR(fmt, ...) \ |
do { printf("ESP ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0) |
do { printf("ESP ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0) |
|
|
Line 54 typedef struct ESPState ESPState;
|
Line 45 typedef struct ESPState ESPState;
|
|
|
struct ESPState { |
struct ESPState { |
SysBusDevice busdev; |
SysBusDevice busdev; |
uint32_t it_shift; |
|
qemu_irq irq; |
|
uint8_t rregs[ESP_REGS]; |
uint8_t rregs[ESP_REGS]; |
uint8_t wregs[ESP_REGS]; |
uint8_t wregs[ESP_REGS]; |
|
qemu_irq irq; |
|
uint32_t it_shift; |
int32_t ti_size; |
int32_t ti_size; |
uint32_t ti_rptr, ti_wptr; |
uint32_t ti_rptr, ti_wptr; |
uint8_t ti_buf[TI_BUFSZ]; |
|
uint32_t status; |
uint32_t status; |
uint32_t dma; |
uint32_t dma; |
|
uint8_t ti_buf[TI_BUFSZ]; |
SCSIBus bus; |
SCSIBus bus; |
SCSIDevice *current_dev; |
SCSIDevice *current_dev; |
SCSIRequest *current_req; |
SCSIRequest *current_req; |
Line 75 struct ESPState {
|
Line 66 struct ESPState {
|
/* The size of the current DMA transfer. Zero if no transfer is in |
/* The size of the current DMA transfer. Zero if no transfer is in |
progress. */ |
progress. */ |
uint32_t dma_counter; |
uint32_t dma_counter; |
uint8_t *async_buf; |
int dma_enabled; |
|
|
uint32_t async_len; |
uint32_t async_len; |
|
uint8_t *async_buf; |
|
|
ESPDMAMemoryReadWriteFunc dma_memory_read; |
ESPDMAMemoryReadWriteFunc dma_memory_read; |
ESPDMAMemoryReadWriteFunc dma_memory_write; |
ESPDMAMemoryReadWriteFunc dma_memory_write; |
void *dma_opaque; |
void *dma_opaque; |
int dma_enabled; |
|
void (*dma_cb)(ESPState *s); |
void (*dma_cb)(ESPState *s); |
}; |
}; |
|
|
Line 157 static void esp_raise_irq(ESPState *s)
|
Line 149 static void esp_raise_irq(ESPState *s)
|
if (!(s->rregs[ESP_RSTAT] & STAT_INT)) { |
if (!(s->rregs[ESP_RSTAT] & STAT_INT)) { |
s->rregs[ESP_RSTAT] |= STAT_INT; |
s->rregs[ESP_RSTAT] |= STAT_INT; |
qemu_irq_raise(s->irq); |
qemu_irq_raise(s->irq); |
DPRINTF("Raise IRQ\n"); |
trace_esp_raise_irq(); |
} |
} |
} |
} |
|
|
Line 166 static void esp_lower_irq(ESPState *s)
|
Line 158 static void esp_lower_irq(ESPState *s)
|
if (s->rregs[ESP_RSTAT] & STAT_INT) { |
if (s->rregs[ESP_RSTAT] & STAT_INT) { |
s->rregs[ESP_RSTAT] &= ~STAT_INT; |
s->rregs[ESP_RSTAT] &= ~STAT_INT; |
qemu_irq_lower(s->irq); |
qemu_irq_lower(s->irq); |
DPRINTF("Lower IRQ\n"); |
trace_esp_lower_irq(); |
} |
} |
} |
} |
|
|
Line 177 static void esp_dma_enable(void *opaque,
|
Line 169 static void esp_dma_enable(void *opaque,
|
|
|
if (level) { |
if (level) { |
s->dma_enabled = 1; |
s->dma_enabled = 1; |
DPRINTF("Raise enable\n"); |
trace_esp_dma_enable(); |
if (s->dma_cb) { |
if (s->dma_cb) { |
s->dma_cb(s); |
s->dma_cb(s); |
s->dma_cb = NULL; |
s->dma_cb = NULL; |
} |
} |
} else { |
} else { |
DPRINTF("Lower enable\n"); |
trace_esp_dma_disable(); |
s->dma_enabled = 0; |
s->dma_enabled = 0; |
} |
} |
} |
} |
Line 213 static uint32_t get_cmd(ESPState *s, uin
|
Line 205 static uint32_t get_cmd(ESPState *s, uin
|
memcpy(buf, s->ti_buf, dmalen); |
memcpy(buf, s->ti_buf, dmalen); |
buf[0] = buf[2] >> 5; |
buf[0] = buf[2] >> 5; |
} |
} |
DPRINTF("get_cmd: len %d target %d\n", dmalen, target); |
trace_esp_get_cmd(dmalen, target); |
|
|
s->ti_size = 0; |
s->ti_size = 0; |
s->ti_rptr = 0; |
s->ti_rptr = 0; |
Line 225 static uint32_t get_cmd(ESPState *s, uin
|
Line 217 static uint32_t get_cmd(ESPState *s, uin
|
s->async_len = 0; |
s->async_len = 0; |
} |
} |
|
|
if (target >= ESP_MAX_DEVS || !s->bus.devs[target]) { |
s->current_dev = scsi_device_find(&s->bus, 0, target, 0); |
|
if (!s->current_dev) { |
// No such drive |
// No such drive |
s->rregs[ESP_RSTAT] = 0; |
s->rregs[ESP_RSTAT] = 0; |
s->rregs[ESP_RINTR] = INTR_DC; |
s->rregs[ESP_RINTR] = INTR_DC; |
Line 233 static uint32_t get_cmd(ESPState *s, uin
|
Line 226 static uint32_t get_cmd(ESPState *s, uin
|
esp_raise_irq(s); |
esp_raise_irq(s); |
return 0; |
return 0; |
} |
} |
s->current_dev = s->bus.devs[target]; |
|
return dmalen; |
return dmalen; |
} |
} |
|
|
Line 241 static void do_busid_cmd(ESPState *s, ui
|
Line 233 static void do_busid_cmd(ESPState *s, ui
|
{ |
{ |
int32_t datalen; |
int32_t datalen; |
int lun; |
int lun; |
|
SCSIDevice *current_lun; |
|
|
DPRINTF("do_busid_cmd: busid 0x%x\n", busid); |
trace_esp_do_busid_cmd(busid); |
lun = busid & 7; |
lun = busid & 7; |
s->current_req = scsi_req_new(s->current_dev, 0, lun, NULL); |
current_lun = scsi_device_find(&s->bus, 0, s->current_dev->id, lun); |
datalen = scsi_req_enqueue(s->current_req, buf); |
s->current_req = scsi_req_new(current_lun, 0, lun, buf, NULL); |
|
datalen = scsi_req_enqueue(s->current_req); |
s->ti_size = datalen; |
s->ti_size = datalen; |
if (datalen != 0) { |
if (datalen != 0) { |
s->rregs[ESP_RSTAT] = STAT_TC; |
s->rregs[ESP_RSTAT] = STAT_TC; |
Line 307 static void handle_satn_stop(ESPState *s
|
Line 301 static void handle_satn_stop(ESPState *s
|
} |
} |
s->cmdlen = get_cmd(s, s->cmdbuf); |
s->cmdlen = get_cmd(s, s->cmdbuf); |
if (s->cmdlen) { |
if (s->cmdlen) { |
DPRINTF("Set ATN & Stop: cmdlen %d\n", s->cmdlen); |
trace_esp_handle_satn_stop(s->cmdlen); |
s->do_cmd = 1; |
s->do_cmd = 1; |
s->rregs[ESP_RSTAT] = STAT_TC | STAT_CD; |
s->rregs[ESP_RSTAT] = STAT_TC | STAT_CD; |
s->rregs[ESP_RINTR] = INTR_BS | INTR_FC; |
s->rregs[ESP_RINTR] = INTR_BS | INTR_FC; |
Line 318 static void handle_satn_stop(ESPState *s
|
Line 312 static void handle_satn_stop(ESPState *s
|
|
|
static void write_response(ESPState *s) |
static void write_response(ESPState *s) |
{ |
{ |
DPRINTF("Transfer status (status=%d)\n", s->status); |
trace_esp_write_response(s->status); |
s->ti_buf[0] = s->status; |
s->ti_buf[0] = s->status; |
s->ti_buf[1] = 0; |
s->ti_buf[1] = 0; |
if (s->dma) { |
if (s->dma) { |
Line 354 static void esp_do_dma(ESPState *s)
|
Line 348 static void esp_do_dma(ESPState *s)
|
to_device = (s->ti_size < 0); |
to_device = (s->ti_size < 0); |
len = s->dma_left; |
len = s->dma_left; |
if (s->do_cmd) { |
if (s->do_cmd) { |
DPRINTF("command len %d + %d\n", s->cmdlen, len); |
trace_esp_do_dma(s->cmdlen, len); |
s->dma_memory_read(s->dma_opaque, &s->cmdbuf[s->cmdlen], len); |
s->dma_memory_read(s->dma_opaque, &s->cmdbuf[s->cmdlen], len); |
s->ti_size = 0; |
s->ti_size = 0; |
s->cmdlen = 0; |
s->cmdlen = 0; |
Line 399 static void esp_command_complete(SCSIReq
|
Line 393 static void esp_command_complete(SCSIReq
|
{ |
{ |
ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->bus->qbus.parent); |
ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->bus->qbus.parent); |
|
|
DPRINTF("SCSI Command complete\n"); |
trace_esp_command_complete(); |
if (s->ti_size != 0) { |
if (s->ti_size != 0) { |
DPRINTF("SCSI command completed unexpectedly\n"); |
trace_esp_command_complete_unexpected(); |
} |
} |
s->ti_size = 0; |
s->ti_size = 0; |
s->dma_left = 0; |
s->dma_left = 0; |
s->async_len = 0; |
s->async_len = 0; |
if (status) { |
if (status) { |
DPRINTF("Command failed\n"); |
trace_esp_command_complete_fail(); |
} |
} |
s->status = status; |
s->status = status; |
s->rregs[ESP_RSTAT] = STAT_ST; |
s->rregs[ESP_RSTAT] = STAT_ST; |
Line 423 static void esp_transfer_data(SCSIReques
|
Line 417 static void esp_transfer_data(SCSIReques
|
{ |
{ |
ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->bus->qbus.parent); |
ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->bus->qbus.parent); |
|
|
DPRINTF("transfer %d/%d\n", s->dma_left, s->ti_size); |
trace_esp_transfer_data(s->dma_left, s->ti_size); |
s->async_len = len; |
s->async_len = len; |
s->async_buf = scsi_req_get_buf(req); |
s->async_buf = scsi_req_get_buf(req); |
if (s->dma_left) { |
if (s->dma_left) { |
Line 451 static void handle_ti(ESPState *s)
|
Line 445 static void handle_ti(ESPState *s)
|
minlen = (dmalen < -s->ti_size) ? dmalen : -s->ti_size; |
minlen = (dmalen < -s->ti_size) ? dmalen : -s->ti_size; |
else |
else |
minlen = (dmalen < s->ti_size) ? dmalen : s->ti_size; |
minlen = (dmalen < s->ti_size) ? dmalen : s->ti_size; |
DPRINTF("Transfer Information len %d\n", minlen); |
trace_esp_handle_ti(minlen); |
if (s->dma) { |
if (s->dma) { |
s->dma_left = minlen; |
s->dma_left = minlen; |
s->rregs[ESP_RSTAT] &= ~STAT_TC; |
s->rregs[ESP_RSTAT] &= ~STAT_TC; |
esp_do_dma(s); |
esp_do_dma(s); |
} else if (s->do_cmd) { |
} else if (s->do_cmd) { |
DPRINTF("command len %d\n", s->cmdlen); |
trace_esp_handle_ti_cmd(s->cmdlen); |
s->ti_size = 0; |
s->ti_size = 0; |
s->cmdlen = 0; |
s->cmdlen = 0; |
s->do_cmd = 0; |
s->do_cmd = 0; |
Line 516 static uint32_t esp_mem_readb(void *opaq
|
Line 510 static uint32_t esp_mem_readb(void *opaq
|
uint32_t saddr, old_val; |
uint32_t saddr, old_val; |
|
|
saddr = addr >> s->it_shift; |
saddr = addr >> s->it_shift; |
DPRINTF("read reg[%d]: 0x%2.2x\n", saddr, s->rregs[saddr]); |
trace_esp_mem_readb(saddr, s->rregs[saddr]); |
switch (saddr) { |
switch (saddr) { |
case ESP_FIFO: |
case ESP_FIFO: |
if (s->ti_size > 0) { |
if (s->ti_size > 0) { |
Line 557 static void esp_mem_writeb(void *opaque,
|
Line 551 static void esp_mem_writeb(void *opaque,
|
uint32_t saddr; |
uint32_t saddr; |
|
|
saddr = addr >> s->it_shift; |
saddr = addr >> s->it_shift; |
DPRINTF("write reg[%d]: 0x%2.2x -> 0x%2.2x\n", saddr, s->wregs[saddr], |
trace_esp_mem_writeb(saddr, s->wregs[saddr], val); |
val); |
|
switch (saddr) { |
switch (saddr) { |
case ESP_TCLO: |
case ESP_TCLO: |
case ESP_TCMID: |
case ESP_TCMID: |
Line 586 static void esp_mem_writeb(void *opaque,
|
Line 579 static void esp_mem_writeb(void *opaque,
|
} |
} |
switch(val & CMD_CMD) { |
switch(val & CMD_CMD) { |
case CMD_NOP: |
case CMD_NOP: |
DPRINTF("NOP (%2.2x)\n", val); |
trace_esp_mem_writeb_cmd_nop(val); |
break; |
break; |
case CMD_FLUSH: |
case CMD_FLUSH: |
DPRINTF("Flush FIFO (%2.2x)\n", val); |
trace_esp_mem_writeb_cmd_flush(val); |
//s->ti_size = 0; |
//s->ti_size = 0; |
s->rregs[ESP_RINTR] = INTR_FC; |
s->rregs[ESP_RINTR] = INTR_FC; |
s->rregs[ESP_RSEQ] = 0; |
s->rregs[ESP_RSEQ] = 0; |
s->rregs[ESP_RFLAGS] = 0; |
s->rregs[ESP_RFLAGS] = 0; |
break; |
break; |
case CMD_RESET: |
case CMD_RESET: |
DPRINTF("Chip reset (%2.2x)\n", val); |
trace_esp_mem_writeb_cmd_reset(val); |
esp_soft_reset(&s->busdev.qdev); |
esp_soft_reset(&s->busdev.qdev); |
break; |
break; |
case CMD_BUSRESET: |
case CMD_BUSRESET: |
DPRINTF("Bus reset (%2.2x)\n", val); |
trace_esp_mem_writeb_cmd_bus_reset(val); |
s->rregs[ESP_RINTR] = INTR_RST; |
s->rregs[ESP_RINTR] = INTR_RST; |
if (!(s->wregs[ESP_CFG1] & CFG1_RESREPT)) { |
if (!(s->wregs[ESP_CFG1] & CFG1_RESREPT)) { |
esp_raise_irq(s); |
esp_raise_irq(s); |
Line 610 static void esp_mem_writeb(void *opaque,
|
Line 603 static void esp_mem_writeb(void *opaque,
|
handle_ti(s); |
handle_ti(s); |
break; |
break; |
case CMD_ICCS: |
case CMD_ICCS: |
DPRINTF("Initiator Command Complete Sequence (%2.2x)\n", val); |
trace_esp_mem_writeb_cmd_iccs(val); |
write_response(s); |
write_response(s); |
s->rregs[ESP_RINTR] = INTR_FC; |
s->rregs[ESP_RINTR] = INTR_FC; |
s->rregs[ESP_RSTAT] |= STAT_MI; |
s->rregs[ESP_RSTAT] |= STAT_MI; |
break; |
break; |
case CMD_MSGACC: |
case CMD_MSGACC: |
DPRINTF("Message Accepted (%2.2x)\n", val); |
trace_esp_mem_writeb_cmd_msgacc(val); |
s->rregs[ESP_RINTR] = INTR_DC; |
s->rregs[ESP_RINTR] = INTR_DC; |
s->rregs[ESP_RSEQ] = 0; |
s->rregs[ESP_RSEQ] = 0; |
s->rregs[ESP_RFLAGS] = 0; |
s->rregs[ESP_RFLAGS] = 0; |
esp_raise_irq(s); |
esp_raise_irq(s); |
break; |
break; |
case CMD_PAD: |
case CMD_PAD: |
DPRINTF("Transfer padding (%2.2x)\n", val); |
trace_esp_mem_writeb_cmd_pad(val); |
s->rregs[ESP_RSTAT] = STAT_TC; |
s->rregs[ESP_RSTAT] = STAT_TC; |
s->rregs[ESP_RINTR] = INTR_FC; |
s->rregs[ESP_RINTR] = INTR_FC; |
s->rregs[ESP_RSEQ] = 0; |
s->rregs[ESP_RSEQ] = 0; |
break; |
break; |
case CMD_SATN: |
case CMD_SATN: |
DPRINTF("Set ATN (%2.2x)\n", val); |
trace_esp_mem_writeb_cmd_satn(val); |
break; |
break; |
case CMD_SEL: |
case CMD_SEL: |
DPRINTF("Select without ATN (%2.2x)\n", val); |
trace_esp_mem_writeb_cmd_sel(val); |
handle_s_without_atn(s); |
handle_s_without_atn(s); |
break; |
break; |
case CMD_SELATN: |
case CMD_SELATN: |
DPRINTF("Select with ATN (%2.2x)\n", val); |
trace_esp_mem_writeb_cmd_selatn(val); |
handle_satn(s); |
handle_satn(s); |
break; |
break; |
case CMD_SELATNS: |
case CMD_SELATNS: |
DPRINTF("Select with ATN & stop (%2.2x)\n", val); |
trace_esp_mem_writeb_cmd_selatns(val); |
handle_satn_stop(s); |
handle_satn_stop(s); |
break; |
break; |
case CMD_ENSEL: |
case CMD_ENSEL: |
DPRINTF("Enable selection (%2.2x)\n", val); |
trace_esp_mem_writeb_cmd_ensel(val); |
s->rregs[ESP_RINTR] = 0; |
s->rregs[ESP_RINTR] = 0; |
break; |
break; |
default: |
default: |
Line 729 void esp_init(target_phys_addr_t espaddr
|
Line 722 void esp_init(target_phys_addr_t espaddr
|
*dma_enable = qdev_get_gpio_in(dev, 1); |
*dma_enable = qdev_get_gpio_in(dev, 1); |
} |
} |
|
|
static const struct SCSIBusOps esp_scsi_ops = { |
static const struct SCSIBusInfo esp_scsi_info = { |
|
.tcq = false, |
|
.max_target = ESP_MAX_DEVS, |
|
.max_lun = 7, |
|
|
.transfer_data = esp_transfer_data, |
.transfer_data = esp_transfer_data, |
.complete = esp_command_complete, |
.complete = esp_command_complete, |
.cancel = esp_request_cancelled |
.cancel = esp_request_cancelled |
Line 749 static int esp_init1(SysBusDevice *dev)
|
Line 746 static int esp_init1(SysBusDevice *dev)
|
|
|
qdev_init_gpio_in(&dev->qdev, esp_gpio_demux, 2); |
qdev_init_gpio_in(&dev->qdev, esp_gpio_demux, 2); |
|
|
scsi_bus_new(&s->bus, &dev->qdev, 0, ESP_MAX_DEVS, &esp_scsi_ops); |
scsi_bus_new(&s->bus, &dev->qdev, &esp_scsi_info); |
return scsi_bus_legacy_handle_cmdline(&s->bus); |
return scsi_bus_legacy_handle_cmdline(&s->bus); |
} |
} |
|
|