1278 lines
54 KiB
C
1278 lines
54 KiB
C
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include "ibm.h"
|
|
|
|
#include "device.h"
|
|
#include "dma.h"
|
|
#include "io.h"
|
|
#include "mca.h"
|
|
#include "mem.h"
|
|
#include "pic.h"
|
|
#include "rom.h"
|
|
#include "scsi.h"
|
|
#include "timer.h"
|
|
|
|
#include "scsi_ibm.h"
|
|
|
|
#define SCSI_TIME (20)
|
|
#define POLL_TIME (TIMER_USEC * 10)
|
|
#define MAX_BYTES_TRANSFERRED_PER_POLL 50
|
|
|
|
extern char ide_fn[4][512];
|
|
|
|
typedef enum
|
|
{
|
|
SCB_STATE_IDLE,
|
|
SCB_STATE_START,
|
|
SCB_STATE_WAIT,
|
|
SCB_STATE_COMPLETE
|
|
} sbc_state_t;
|
|
|
|
typedef enum
|
|
{
|
|
SCSI_STATE_IDLE,
|
|
SCSI_STATE_SELECT,
|
|
SCSI_STATE_SELECT_FAILED,
|
|
SCSI_STATE_SEND_COMMAND,
|
|
SCSI_STATE_NEXT_PHASE,
|
|
SCSI_STATE_END_PHASE,
|
|
SCSI_STATE_READ_DATA,
|
|
SCSI_STATE_WRITE_DATA,
|
|
SCSI_STATE_READ_STATUS,
|
|
SCSI_STATE_READ_MESSAGE
|
|
} scsi_state_t;
|
|
|
|
typedef struct scb_t
|
|
{
|
|
uint16_t command;
|
|
uint16_t enable;
|
|
uint32_t lba_addr;
|
|
uint32_t sys_buf_addr;
|
|
uint32_t sys_buf_byte_count;
|
|
uint32_t term_status_block_addr;
|
|
uint32_t scb_chain_addr;
|
|
uint16_t block_count;
|
|
uint16_t block_length;
|
|
} scb_t;
|
|
|
|
typedef struct scsi_ibm_t
|
|
{
|
|
rom_t bios_rom;
|
|
|
|
uint8_t pos_regs[8];
|
|
|
|
uint8_t basic_ctrl;
|
|
uint32_t command;
|
|
|
|
uint8_t attention;
|
|
uint8_t attention_pending;
|
|
int attention_waiting;
|
|
|
|
uint8_t cir[4];
|
|
uint8_t cir_pending[4];
|
|
|
|
uint8_t irq_status;
|
|
|
|
uint32_t scb_addr;
|
|
|
|
uint8_t status;
|
|
|
|
int in_invalid;
|
|
int in_reset;
|
|
sbc_state_t scb_state;
|
|
|
|
scb_t scb;
|
|
int scb_id;
|
|
|
|
int cmd_status;
|
|
int cir_status;
|
|
|
|
uint8_t pacing;
|
|
|
|
uint8_t buf[0x600];
|
|
|
|
uint8_t int_buffer[512];
|
|
|
|
struct
|
|
{
|
|
int phys_id;
|
|
int lun_id;
|
|
} dev_id[16];
|
|
|
|
struct
|
|
{
|
|
int idx, len;
|
|
uint8_t data[256];
|
|
|
|
int data_idx, data_len;
|
|
uint32_t data_pointer;
|
|
|
|
uint8_t last_status;
|
|
|
|
int bytes_transferred;
|
|
|
|
int target_id;
|
|
} cdb;
|
|
|
|
int irq_requests[16];
|
|
|
|
int cmd_timer;
|
|
pc_timer_t callback_timer;
|
|
|
|
scsi_state_t scsi_state;
|
|
scsi_bus_t bus;
|
|
} scsi_ibm_t;
|
|
|
|
#define CTRL_RESET (1 << 7)
|
|
#define CTRL_DMA_ENA (1 << 1)
|
|
#define CTRL_IRQ_ENA (1 << 0)
|
|
|
|
#define STATUS_CMD_FULL (1 << 3)
|
|
#define STATUS_CMD_EMPTY (1 << 2)
|
|
#define STATUS_IRQ (1 << 1)
|
|
#define STATUS_BUSY (1 << 0)
|
|
|
|
#define ENABLE_PT (1 << 12)
|
|
|
|
#define CMD_MASK 0xff3f
|
|
#define CMD_ASSIGN 0x040e
|
|
#define CMD_DEVICE_INQUIRY 0x1c0b
|
|
#define CMD_DMA_PACING_CONTROL 0x040d
|
|
#define CMD_FEATURE_CONTROL 0x040c
|
|
#define CMD_GET_POS_INFO 0x1c0a
|
|
#define CMD_INVALID_412 0x0412
|
|
#define CMD_GET_COMPLETE_STATUS 0x1c07
|
|
#define CMD_READ_DATA 0x1c01
|
|
#define CMD_READ_DEVICE_CAPACITY 0x1c09
|
|
#define CMD_REQUEST_SENSE 0x1c08
|
|
#define CMD_RESET 0x0400
|
|
#define CMD_SEND_OTHER_SCSI 0x241f
|
|
#define CMD_UNKNOWN_1c10 0x1c10
|
|
#define CMD_UNKNOWN_1c11 0x1c11
|
|
#define CMD_WRITE_DATA 0x1c02
|
|
#define CMD_VERIFY 0x1c03
|
|
|
|
#define IRQ_TYPE_NONE 0x0
|
|
#define IRQ_TYPE_SCB_COMPLETE 0x1
|
|
#define IRQ_TYPE_SCB_COMPLETE_RETRY 0x5
|
|
#define IRQ_TYPE_ADAPTER_HW_FAILURE 0x7
|
|
#define IRQ_TYPE_IMM_CMD_COMPLETE 0xa
|
|
#define IRQ_TYPE_COMMAND_FAIL 0xc
|
|
#define IRQ_TYPE_COMMAND_ERROR 0xe
|
|
#define IRQ_TYPE_SW_SEQ_ERROR 0xf
|
|
#define IRQ_TYPE_RESET_COMPLETE 0x10
|
|
|
|
static void scsi_rethink_irqs(scsi_ibm_t *scsi)
|
|
{
|
|
int irq_pending = 0;
|
|
int c;
|
|
|
|
/* pclog("scsi_rethink_irqs: basic_ctrl=%02x status=%02x\n", scsi->basic_ctrl, scsi->irq_status);
|
|
pclog(" ");
|
|
for (c = 0; c < 16; c++)
|
|
pclog(" %02x", scsi->irq_requests[c]);
|
|
pclog("\n");*/
|
|
|
|
if (!scsi->irq_status)
|
|
{
|
|
for (c = 0; c < 16; c++)
|
|
{
|
|
if (scsi->irq_requests[c] != IRQ_TYPE_NONE)
|
|
{
|
|
// pclog(" Found IRQ on %i\n", c);
|
|
/*Found IRQ*/
|
|
scsi->irq_status = c | (scsi->irq_requests[c] << 4);
|
|
scsi->status |= STATUS_IRQ;
|
|
irq_pending = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
irq_pending = 1;
|
|
|
|
if (scsi->basic_ctrl & CTRL_IRQ_ENA)
|
|
{
|
|
if (irq_pending)
|
|
{
|
|
picintlevel(1 << 14);
|
|
// pclog(" SCSI assert INT\n");
|
|
}
|
|
else
|
|
{
|
|
/*No IRQs pending, clear IRQ state*/
|
|
scsi->irq_status = 0;
|
|
scsi->status &= ~STATUS_IRQ;
|
|
picintc(1 << 14);
|
|
// pclog(" SCSI clear INT - no IRQ\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
picintc(1 << 14);
|
|
// pclog(" SCSI clear INT - IRQs disabled\n");
|
|
}
|
|
}
|
|
|
|
static inline void scsi_ibm_set_irq(scsi_ibm_t *scsi, int id, int type)
|
|
{
|
|
// pclog("scsi_ibm_set_irq id=%i type=%x %02x\n", id, type, scsi->irq_status);
|
|
scsi->irq_requests[id] = type;
|
|
if (!scsi->irq_status) /*Don't change IRQ status if one is currently being processed*/
|
|
scsi_rethink_irqs(scsi);
|
|
}
|
|
static inline void scsi_clear_irq(scsi_ibm_t *scsi, int id)
|
|
{
|
|
scsi->irq_requests[id] = IRQ_TYPE_NONE;
|
|
scsi_rethink_irqs(scsi);
|
|
}
|
|
|
|
static uint8_t scsi_read(uint16_t port, void *p)
|
|
{
|
|
scsi_ibm_t *scsi = (scsi_ibm_t *)p;
|
|
uint8_t temp = 0xff;
|
|
|
|
switch (port & 7)
|
|
{
|
|
case 0: case 1: case 2: case 3: /*Command Interface Register*/
|
|
temp = scsi->cir_pending[port & 3];
|
|
break;
|
|
|
|
case 4: /*Attention Register*/
|
|
temp = scsi->attention_pending;
|
|
break;
|
|
case 5: /*Basic Control Register*/
|
|
temp = scsi->basic_ctrl;
|
|
break;
|
|
case 6: /*IRQ status*/
|
|
temp = scsi->irq_status;
|
|
break;
|
|
case 7: /*Status*/
|
|
temp = scsi->status;
|
|
if (scsi->cir_status == 0)
|
|
temp |= STATUS_CMD_EMPTY;
|
|
if (scsi->cir_status == 3)
|
|
temp |= STATUS_CMD_FULL;
|
|
break;
|
|
}
|
|
|
|
// pclog("scsi_read: port=%04x val=%02x %04x(%05x):%04x %i %02x\n", port, temp,CS,cs,cpu_state.pc, 0/*scsi->callback*/, BH);
|
|
return temp;
|
|
}
|
|
|
|
static void scsi_write(uint16_t port, uint8_t val, void *p)
|
|
{
|
|
scsi_ibm_t *scsi = (scsi_ibm_t *)p;
|
|
|
|
// pclog("scsi_write: port=%04x val=%02x %04x:%04x\n", port, val,CS,cpu_state.pc);
|
|
|
|
switch (port & 7)
|
|
{
|
|
case 0: case 1: case 2: case 3: /*Command Interface Register*/
|
|
scsi->cir_pending[port & 3] = val;
|
|
if (port & 2)
|
|
scsi->cir_status |= 2;
|
|
else
|
|
scsi->cir_status |= 1;
|
|
break;
|
|
|
|
case 4: /*Attention Register*/
|
|
scsi->attention_pending = val;
|
|
scsi->attention_waiting = 2;
|
|
scsi->status |= STATUS_BUSY;
|
|
break;
|
|
|
|
case 5: /*Basic Control Register*/
|
|
if ((scsi->basic_ctrl & CTRL_RESET) && !(val & CTRL_RESET))
|
|
{
|
|
// pclog("SCSI reset\n");
|
|
scsi->in_reset = 1;
|
|
scsi->cmd_timer = SCSI_TIME*2;
|
|
scsi->status |= STATUS_BUSY;
|
|
}
|
|
scsi->basic_ctrl = val;
|
|
scsi_rethink_irqs(scsi);
|
|
break;
|
|
|
|
// default:
|
|
// fatal("scsi_write port=%04x val=%02x\n", port, val);
|
|
}
|
|
}
|
|
|
|
static uint16_t scsi_readw(uint16_t port, void *p)
|
|
{
|
|
scsi_ibm_t *scsi = (scsi_ibm_t *)p;
|
|
uint16_t temp = 0xffff;
|
|
|
|
switch (port & 7)
|
|
{
|
|
case 0: /*Command Interface Register*/
|
|
temp = scsi->cir_pending[0] | (scsi->cir_pending[1] << 8);
|
|
break;
|
|
case 2: /*Command Interface Register*/
|
|
temp = scsi->cir_pending[2] | (scsi->cir_pending[3] << 8);
|
|
break;
|
|
|
|
// default:
|
|
// fatal("scsi_readw port=%04x\n", port);
|
|
}
|
|
|
|
// pclog("scsi_readw: port=%04x val=%04x\n", port, temp);
|
|
return temp;
|
|
}
|
|
|
|
static void scsi_writew(uint16_t port, uint16_t val, void *p)
|
|
{
|
|
scsi_ibm_t *scsi = (scsi_ibm_t *)p;
|
|
|
|
switch (port & 7)
|
|
{
|
|
case 0: /*Command Interface Register*/
|
|
scsi->cir_pending[0] = val & 0xff;
|
|
scsi->cir_pending[1] = val >> 8;
|
|
scsi->cir_status |= 1;
|
|
break;
|
|
case 2: /*Command Interface Register*/
|
|
scsi->cir_pending[2] = val & 0xff;
|
|
scsi->cir_pending[3] = val >> 8;
|
|
scsi->cir_status |= 2;
|
|
break;
|
|
|
|
// default:
|
|
// fatal("scsi_writew port=%04x val=%04x\n", port, val);
|
|
}
|
|
|
|
// pclog("scsi_writew: port=%04x val=%04x\n", port, val);
|
|
}
|
|
|
|
static void sg_fetch_next(scsi_ibm_t *scsi)
|
|
{
|
|
if (scsi->scb.sys_buf_byte_count > 0)
|
|
{
|
|
scsi->cdb.data_idx = 0;
|
|
scsi->cdb.data_pointer = mem_readl_phys(scsi->scb.sys_buf_addr);
|
|
scsi->cdb.data_len = mem_readl_phys(scsi->scb.sys_buf_addr + 4);
|
|
// pclog("SG list: from %08x addr=%08x len=%08x\n", scsi->scb.sys_buf_addr, scsi->cdb.data_pointer, scsi->cdb.data_len);
|
|
scsi->scb.sys_buf_addr += 8;
|
|
scsi->scb.sys_buf_byte_count -= 8;
|
|
}
|
|
}
|
|
|
|
static void process_ibm_cmd(void *p)
|
|
{
|
|
scsi_ibm_t *scsi = (scsi_ibm_t *)p;
|
|
scb_t *scb = &scsi->scb;
|
|
int c;
|
|
int old_scb_state;
|
|
|
|
// pclog("scsi_callback: in_reset=%i command=%x\n", scsi->in_reset, scsi->command);
|
|
if (scsi->in_reset)
|
|
{
|
|
scsi->status &= ~STATUS_BUSY;
|
|
|
|
scsi->irq_status = 0;
|
|
for (c = 0; c < 16; c++)
|
|
scsi_clear_irq(scsi, c);
|
|
if (scsi->in_reset == 1)
|
|
{
|
|
scsi->basic_ctrl |= CTRL_IRQ_ENA;
|
|
scsi_ibm_set_irq(scsi, 0xf, IRQ_TYPE_RESET_COMPLETE);
|
|
}
|
|
else
|
|
scsi_ibm_set_irq(scsi, 0xf, IRQ_TYPE_RESET_COMPLETE);
|
|
|
|
/*Reset device mappings*/
|
|
for (c = 0; c < 7; c++)
|
|
{
|
|
scsi->dev_id[c].phys_id = c;
|
|
scsi->dev_id[c].lun_id = 0;
|
|
}
|
|
for (; c < 15; c++)
|
|
scsi->dev_id[c].phys_id = -1;
|
|
|
|
scsi->in_reset = 0;
|
|
// pclog("scsi reset complete\n");
|
|
return;
|
|
}
|
|
if (scsi->in_invalid)
|
|
{
|
|
scsi_ibm_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_COMMAND_ERROR);
|
|
scsi->in_invalid = 0;
|
|
return;
|
|
}
|
|
do
|
|
{
|
|
old_scb_state = scsi->scb_state;
|
|
|
|
// pclog("scb_state=%i\n", scsi->scb_state);
|
|
switch (scsi->scb_state)
|
|
{
|
|
case SCB_STATE_IDLE:
|
|
break;
|
|
|
|
case SCB_STATE_START:
|
|
if (scsi->dev_id[scsi->scb_id].phys_id == -1)
|
|
{
|
|
scsi_ibm_set_irq(scsi, scsi->scb_id, IRQ_TYPE_COMMAND_FAIL);
|
|
scsi->scb_state = SCB_STATE_IDLE;
|
|
mem_writew_phys(scb->term_status_block_addr + 0x7*2, (0xe << 8) | 0);
|
|
mem_writew_phys(scb->term_status_block_addr + 0x8*2, (0xa << 8) | 0);
|
|
break;
|
|
}
|
|
|
|
scb->command = mem_readw_phys(scsi->scb_addr);
|
|
scb->enable = mem_readw_phys(scsi->scb_addr + 0x02);
|
|
scb->lba_addr = mem_readl_phys(scsi->scb_addr + 0x04);
|
|
scb->sys_buf_addr = mem_readl_phys(scsi->scb_addr + 0x08);
|
|
scb->sys_buf_byte_count = mem_readl_phys(scsi->scb_addr + 0x0c);
|
|
scb->term_status_block_addr = mem_readl_phys(scsi->scb_addr + 0x10);
|
|
scb->scb_chain_addr = mem_readl_phys(scsi->scb_addr + 0x14);
|
|
scb->block_count = mem_readw_phys(scsi->scb_addr + 0x18);
|
|
scb->block_length = mem_readw_phys(scsi->scb_addr + 0x1a);
|
|
|
|
/* pclog("SCB : \n"
|
|
" Command = %04x\n"
|
|
" Enable = %04x\n"
|
|
" LBA addr = %08x\n"
|
|
" System buffer addr = %08x\n"
|
|
" System buffer byte count = %08x\n"
|
|
" Terminate status block addr = %08x\n"
|
|
" SCB chain address = %08x\n"
|
|
" Block count = %04x\n"
|
|
" Block length = %04x\n",
|
|
scb->command, scb->enable, scb->lba_addr,
|
|
scb->sys_buf_addr, scb->sys_buf_byte_count,
|
|
scb->term_status_block_addr, scb->scb_chain_addr,
|
|
scb->block_count, scb->block_length);*/
|
|
|
|
if (scb->enable & ENABLE_PT)
|
|
sg_fetch_next(scsi);
|
|
else
|
|
{
|
|
scsi->cdb.data_idx = 0;
|
|
scsi->cdb.data_pointer = scb->sys_buf_addr;
|
|
scsi->cdb.data_len = scb->sys_buf_byte_count;
|
|
}
|
|
|
|
switch (scb->command & CMD_MASK)
|
|
{
|
|
case CMD_GET_COMPLETE_STATUS:
|
|
mem_writew_phys(scb->sys_buf_addr + 0x0*2, 0x201); /*SCB status*/
|
|
mem_writew_phys(scb->sys_buf_addr + 0x1*2, 0); /*Retry counts*/
|
|
mem_writel_phys(scb->sys_buf_addr + 0x2*2, 0); /*Residual byte count*/
|
|
mem_writel_phys(scb->sys_buf_addr + 0x4*2, 0); /*Scatter/gather list element address*/
|
|
mem_writew_phys(scb->sys_buf_addr + 0x6*2, 0xc); /*Device dependent status length*/
|
|
mem_writew_phys(scb->sys_buf_addr + 0x7*2, scsi->cmd_status << 8);
|
|
mem_writew_phys(scb->sys_buf_addr + 0x8*2, 0); /*Error*/
|
|
mem_writew_phys(scb->sys_buf_addr + 0x9*2, 0); /*Reserved*/
|
|
mem_writew_phys(scb->sys_buf_addr + 0xa*2, 0); /*Cache information status*/
|
|
mem_writel_phys(scb->sys_buf_addr + 0xb*2, scsi->scb_addr);
|
|
scsi->scb_state = SCB_STATE_COMPLETE;
|
|
break;
|
|
|
|
case CMD_UNKNOWN_1c10:
|
|
for (c = 0; c < 0x600 && c < scb->sys_buf_byte_count; c++)
|
|
scsi->buf[c] = mem_readb_phys(scb->sys_buf_addr + c);
|
|
scsi->scb_state = SCB_STATE_COMPLETE;
|
|
break;
|
|
case CMD_UNKNOWN_1c11:
|
|
for (c = 0; c < 0x600 && c < scb->sys_buf_byte_count; c++)
|
|
mem_writeb_phys(scb->sys_buf_addr + c, scsi->buf[c]);
|
|
scsi->scb_state = SCB_STATE_COMPLETE;
|
|
break;
|
|
|
|
case CMD_GET_POS_INFO:
|
|
mem_writew_phys(scb->sys_buf_addr + 0x0*2, 0x8eff);
|
|
mem_writew_phys(scb->sys_buf_addr + 0x1*2, scsi->pos_regs[3] | (scsi->pos_regs[2] << 8));
|
|
mem_writew_phys(scb->sys_buf_addr + 0x2*2, 0x0e | (scsi->pos_regs[4] << 8));
|
|
mem_writew_phys(scb->sys_buf_addr + 0x3*2, 1 << 12);
|
|
mem_writew_phys(scb->sys_buf_addr + 0x4*2, (7 << 8) | 8);
|
|
mem_writew_phys(scb->sys_buf_addr + 0x5*2, (16 << 8) | scsi->pacing);
|
|
mem_writew_phys(scb->sys_buf_addr + 0x6*2, (30 << 8) | 1);
|
|
mem_writew_phys(scb->sys_buf_addr + 0x7*2, 0);
|
|
mem_writew_phys(scb->sys_buf_addr + 0x8*2, 0);
|
|
scsi->scb_state = SCB_STATE_COMPLETE;
|
|
break;
|
|
|
|
case CMD_DEVICE_INQUIRY:
|
|
scsi->cdb.data[0] = SCSI_INQUIRY;
|
|
scsi->cdb.data[1] = scsi->dev_id[scsi->scb_id].lun_id << 5; /*LUN*/
|
|
scsi->cdb.data[2] = 0; /*Page code*/
|
|
scsi->cdb.data[3] = 0;
|
|
scsi->cdb.data[4] = scb->sys_buf_byte_count; /*Allocation length*/
|
|
scsi->cdb.data[5] = 0; /*Control*/
|
|
scsi->cdb.idx = 0;
|
|
scsi->cdb.len = 6;
|
|
scsi->cdb.target_id = scsi->dev_id[scsi->scb_id].phys_id;
|
|
scsi->cdb.data_pointer = scb->sys_buf_addr;
|
|
scsi->cdb.data_idx = 0;
|
|
scsi->cdb.data_len = scb->sys_buf_byte_count;
|
|
scsi->scsi_state = SCSI_STATE_SELECT;
|
|
scsi->scb_state = SCB_STATE_WAIT;
|
|
return;
|
|
|
|
case CMD_SEND_OTHER_SCSI:
|
|
// pclog("SEND_OTHER_SCSI:");
|
|
for (c = 0; c < 12; c++)
|
|
{
|
|
scsi->cdb.data[c] = mem_readb_phys(scsi->scb_addr + 0x18 + c);
|
|
// pclog(" %02x", scsi->cdb.data[c]);
|
|
}
|
|
// pclog("\n");
|
|
scsi->cdb.data[1] = (scsi->cdb.data[1] & 0x1f) | (scsi->dev_id[scsi->scb_id].lun_id << 5); /*Patch correct LUN into command*/
|
|
scsi->cdb.idx = 0;
|
|
scsi->cdb.len = (scb->lba_addr & 0xff) ? (scb->lba_addr & 0xff) : 6;
|
|
// pclog("SEND_OTHER_SCSI: %04x\n", scb->lba_addr & 0xffff);
|
|
scsi->cdb.target_id = scsi->dev_id[scsi->scb_id].phys_id;
|
|
scsi->scsi_state = SCSI_STATE_SELECT;
|
|
scsi->scb_state = SCB_STATE_WAIT;
|
|
return;
|
|
|
|
case CMD_READ_DEVICE_CAPACITY:
|
|
scsi->cdb.data[0] = SCSI_READ_CAPACITY_10;
|
|
scsi->cdb.data[1] = scsi->dev_id[scsi->scb_id].lun_id << 5; /*LUN*/
|
|
scsi->cdb.data[2] = 0; /*LBA*/
|
|
scsi->cdb.data[3] = 0;
|
|
scsi->cdb.data[4] = 0;
|
|
scsi->cdb.data[5] = 0;
|
|
scsi->cdb.data[6] = 0; /*Reserved*/
|
|
scsi->cdb.data[7] = 0;
|
|
scsi->cdb.data[8] = 0;
|
|
scsi->cdb.data[9] = 0; /*Control*/
|
|
scsi->cdb.idx = 0;
|
|
scsi->cdb.len = 10;
|
|
scsi->cdb.target_id = scsi->dev_id[scsi->scb_id].phys_id;
|
|
scsi->scsi_state = SCSI_STATE_SELECT;
|
|
scsi->scb_state = SCB_STATE_WAIT;
|
|
return;
|
|
|
|
case CMD_READ_DATA:
|
|
scsi->cdb.data[0] = SCSI_READ_10;
|
|
scsi->cdb.data[1] = scsi->dev_id[scsi->scb_id].lun_id << 5; /*LUN*/
|
|
scsi->cdb.data[2] = (scb->lba_addr >> 24) & 0xff; /*LBA*/
|
|
scsi->cdb.data[3] = (scb->lba_addr >> 16) & 0xff;
|
|
scsi->cdb.data[4] = (scb->lba_addr >> 8) & 0xff;
|
|
scsi->cdb.data[5] = scb->lba_addr & 0xff;
|
|
scsi->cdb.data[6] = 0; /*Reserved*/
|
|
scsi->cdb.data[7] = (scb->block_count >> 8) & 0xff;
|
|
scsi->cdb.data[8] = scb->block_count & 0xff;
|
|
scsi->cdb.data[9] = 0; /*Control*/
|
|
scsi->cdb.idx = 0;
|
|
scsi->cdb.len = 10;
|
|
scsi->cdb.target_id = scsi->dev_id[scsi->scb_id].phys_id;
|
|
scsi->scsi_state = SCSI_STATE_SELECT;
|
|
scsi->scb_state = SCB_STATE_WAIT;
|
|
return;
|
|
|
|
case CMD_REQUEST_SENSE:
|
|
scsi->cdb.data[0] = SCSI_REQUEST_SENSE;
|
|
scsi->cdb.data[1] = scsi->dev_id[scsi->scb_id].lun_id << 5; /*LUN*/
|
|
scsi->cdb.data[2] = 0;
|
|
scsi->cdb.data[3] = 0;
|
|
scsi->cdb.data[4] = scb->block_count & 0xff; /*Allocation length*/
|
|
scsi->cdb.data[5] = 0;
|
|
scsi->cdb.idx = 0;
|
|
scsi->cdb.len = 6;
|
|
scsi->cdb.target_id = scsi->dev_id[scsi->scb_id].phys_id;
|
|
scsi->scsi_state = SCSI_STATE_SELECT;
|
|
scsi->scb_state = SCB_STATE_WAIT;
|
|
return;
|
|
|
|
case CMD_WRITE_DATA:
|
|
scsi->cdb.data[0] = SCSI_WRITE_10;
|
|
scsi->cdb.data[1] = scsi->dev_id[scsi->scb_id].lun_id << 5; /*LUN*/
|
|
scsi->cdb.data[2] = (scb->lba_addr >> 24) & 0xff; /*LBA*/
|
|
scsi->cdb.data[3] = (scb->lba_addr >> 16) & 0xff;
|
|
scsi->cdb.data[4] = (scb->lba_addr >> 8) & 0xff;
|
|
scsi->cdb.data[5] = scb->lba_addr & 0xff;
|
|
scsi->cdb.data[6] = 0; /*Reserved*/
|
|
scsi->cdb.data[7] = (scb->block_count >> 8) & 0xff;
|
|
scsi->cdb.data[8] = scb->block_count & 0xff;
|
|
scsi->cdb.data[9] = 0; /*Control*/
|
|
scsi->cdb.idx = 0;
|
|
scsi->cdb.len = 10;
|
|
scsi->cdb.target_id = scsi->dev_id[scsi->scb_id].phys_id;
|
|
scsi->scsi_state = SCSI_STATE_SELECT;
|
|
scsi->scb_state = SCB_STATE_WAIT;
|
|
return;
|
|
|
|
case CMD_VERIFY:
|
|
scsi->cdb.data[0] = SCSI_VERIFY_10;
|
|
scsi->cdb.data[1] = scsi->dev_id[scsi->scb_id].lun_id << 5; /*LUN*/
|
|
scsi->cdb.data[2] = (scb->lba_addr >> 24) & 0xff; /*LBA*/
|
|
scsi->cdb.data[3] = (scb->lba_addr >> 16) & 0xff;
|
|
scsi->cdb.data[4] = (scb->lba_addr >> 8) & 0xff;
|
|
scsi->cdb.data[5] = scb->lba_addr & 0xff;
|
|
scsi->cdb.data[6] = 0; /*Reserved*/
|
|
scsi->cdb.data[7] = (scb->block_count >> 8) & 0xff;
|
|
scsi->cdb.data[8] = scb->block_count & 0xff;
|
|
scsi->cdb.data[9] = 0; /*Control*/
|
|
scsi->cdb.idx = 0;
|
|
scsi->cdb.len = 10;
|
|
scsi->cdb.target_id = scsi->dev_id[scsi->scb_id].phys_id;
|
|
scsi->cdb.data_len = 0;
|
|
scsi->scsi_state = SCSI_STATE_SELECT;
|
|
scsi->scb_state = SCB_STATE_WAIT;
|
|
return;
|
|
|
|
#ifndef RELEASE_BUILD
|
|
default:
|
|
fatal("SCB command %04x\n", scb->command);
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case SCB_STATE_WAIT:
|
|
if (scsi->scsi_state == SCSI_STATE_IDLE)
|
|
{
|
|
if (scsi->cdb.last_status == STATUS_GOOD)
|
|
scsi->scb_state = SCB_STATE_COMPLETE;
|
|
else if (scsi->cdb.last_status == STATUS_CHECK_CONDITION)
|
|
{
|
|
scsi_ibm_set_irq(scsi, scsi->scb_id, IRQ_TYPE_COMMAND_FAIL);
|
|
scsi->scb_state = SCB_STATE_IDLE;
|
|
mem_writew_phys(scb->term_status_block_addr + 0x7*2, (0xc << 8) | 2);
|
|
mem_writew_phys(scb->term_status_block_addr + 0x8*2, 0x20);
|
|
mem_writew_phys(scb->term_status_block_addr + 0xb*2, scsi->scb_addr & 0xffff);
|
|
mem_writew_phys(scb->term_status_block_addr + 0xc*2, scsi->scb_addr >> 16);
|
|
}
|
|
#ifndef RELEASE_BUILD
|
|
else
|
|
fatal("SCB unknown result %02x\n", scsi->cdb.last_status);
|
|
#endif
|
|
}
|
|
else if (scsi->scsi_state == SCSI_STATE_SELECT_FAILED)
|
|
{
|
|
// pclog("SCB_STATE_WAIT SELECT_FAILED\n");
|
|
scsi_ibm_set_irq(scsi, scsi->scb_id, IRQ_TYPE_COMMAND_FAIL);
|
|
scsi->scb_state = SCB_STATE_IDLE;
|
|
mem_writew_phys(scb->term_status_block_addr + 0x7*2, (0xc << 8) | 2);
|
|
mem_writew_phys(scb->term_status_block_addr + 0x8*2, 0x10);
|
|
}
|
|
#ifndef RELEASE_BUILD
|
|
else
|
|
fatal("SCB_STATE_WAIT other %i\n", scsi->scsi_state);
|
|
#endif
|
|
break;
|
|
|
|
case SCB_STATE_COMPLETE:
|
|
if (scb->enable & 1)
|
|
{
|
|
scsi->scb_state = SCB_STATE_START;
|
|
scsi->scb_addr = scb->scb_chain_addr;
|
|
// pclog("Next SCB - %08x\n", scsi->scb_addr);
|
|
}
|
|
else
|
|
{
|
|
scsi_ibm_set_irq(scsi, scsi->scb_id, IRQ_TYPE_SCB_COMPLETE);
|
|
scsi->scb_state = SCB_STATE_IDLE;
|
|
}
|
|
break;
|
|
}
|
|
} while (scsi->scb_state != old_scb_state);
|
|
}
|
|
|
|
static void process_immediate_command(scsi_ibm_t *scsi)
|
|
{
|
|
switch (scsi->command & CMD_MASK)
|
|
{
|
|
case CMD_ASSIGN:
|
|
// pclog("Assign: adapter dev=%x scsi ID=%i LUN=%i\n", (scsi->command >> 16) & 0xf, (scsi->command >> 20) & 7, (scsi->command >> 24) & 7);
|
|
if (((scsi->command >> 16) & 0xf) == 0xf && ((scsi->command >> 20) & 7) == 7) /*Device 15 always adapter*/
|
|
scsi_ibm_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_IMM_CMD_COMPLETE);
|
|
else if (((scsi->command >> 16) & 0xf) == 0xf) /*Can not re-assign device 15 (always adapter)*/
|
|
scsi_ibm_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_COMMAND_FAIL);
|
|
else if (scsi->command & (1 << 23))
|
|
{
|
|
scsi->dev_id[(scsi->command >> 16) & 0xf].phys_id = -1;
|
|
scsi_ibm_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_IMM_CMD_COMPLETE);
|
|
}
|
|
else if (((scsi->command >> 20) & 7) == 7) /*Can not assign adapter*/
|
|
scsi_ibm_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_COMMAND_FAIL);
|
|
else
|
|
{
|
|
scsi->dev_id[(scsi->command >> 16) & 0xf].phys_id = (scsi->command >> 20) & 7;
|
|
scsi->dev_id[(scsi->command >> 16) & 0xf].lun_id = (scsi->command >> 24) & 7;
|
|
scsi_ibm_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_IMM_CMD_COMPLETE);
|
|
}
|
|
break;
|
|
|
|
case CMD_DMA_PACING_CONTROL:
|
|
scsi->pacing = scsi->cir[2];
|
|
// pclog("Pacing control: %i\n", scsi->pacing);
|
|
scsi_ibm_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_IMM_CMD_COMPLETE);
|
|
break;
|
|
|
|
case CMD_FEATURE_CONTROL:
|
|
// pclog("Feature control: timeout=%is d-rate=%i\n", (scsi->command >> 16) & 0x1fff, scsi->command >> 29);
|
|
scsi_ibm_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_IMM_CMD_COMPLETE);
|
|
break;
|
|
|
|
case CMD_INVALID_412:
|
|
scsi_ibm_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_IMM_CMD_COMPLETE);
|
|
break;
|
|
|
|
case CMD_RESET:
|
|
if ((scsi->attention & 0xf) == 0xf) /*Adapter reset*/
|
|
{
|
|
scsi_bus_reset(&scsi->bus);
|
|
scsi->scb_state = SCB_STATE_IDLE;
|
|
}
|
|
else /*Device reset*/
|
|
{
|
|
}
|
|
scsi_ibm_set_irq(scsi, scsi->attention & 0x0f, IRQ_TYPE_IMM_CMD_COMPLETE);
|
|
break;
|
|
|
|
default:
|
|
fatal("scsi_callback: Bad command %02x\n", scsi->command);
|
|
}
|
|
}
|
|
|
|
static int wait_for_bus(scsi_bus_t *bus, int state, int req_needed)
|
|
{
|
|
int c;
|
|
|
|
for (c = 0; c < 20; c++)
|
|
{
|
|
int bus_state = scsi_bus_read(bus);
|
|
|
|
if ((bus_state & (BUS_IO | BUS_CD | BUS_MSG)) == state && (bus_state & BUS_BSY))
|
|
{
|
|
if (!req_needed || (bus_state & BUS_REQ))
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void process_scsi(scsi_ibm_t *scsi)
|
|
{
|
|
int c;
|
|
int bytes_transferred = 0;
|
|
|
|
switch (scsi->scsi_state)
|
|
{
|
|
case SCSI_STATE_IDLE:
|
|
break;
|
|
|
|
case SCSI_STATE_SELECT:
|
|
scsi->cdb.last_status = 0;
|
|
// pclog("Select target ID %i\n", scsi->cdb.target_id);
|
|
scsi_bus_update(&scsi->bus, BUS_SEL | BUS_SETDATA(1 << scsi->cdb.target_id));
|
|
if (!(scsi_bus_read(&scsi->bus) & BUS_BSY) || scsi->cdb.target_id > 6)
|
|
{
|
|
// pclog("STATE_SCSI_SELECT failed to select target %i\n", scsi->cdb.target_id);
|
|
scsi->scsi_state = SCSI_STATE_SELECT_FAILED;
|
|
if (!scsi->cmd_timer)
|
|
scsi->cmd_timer = 1;
|
|
break;
|
|
}
|
|
|
|
scsi_bus_update(&scsi->bus, 0);
|
|
if (!(scsi_bus_read(&scsi->bus) & BUS_BSY))
|
|
{
|
|
// pclog("STATE_SCSI_SELECT failed to select target %i 2\n", scsi->cdb.target_id);
|
|
scsi->scsi_state = SCSI_STATE_SELECT_FAILED;
|
|
if (!scsi->cmd_timer)
|
|
scsi->cmd_timer = 1;
|
|
break;
|
|
}
|
|
|
|
/*Device should now be selected*/
|
|
if (!wait_for_bus(&scsi->bus, BUS_CD, 1))
|
|
{
|
|
// pclog("Device failed to request command\n");
|
|
scsi->scsi_state = SCSI_STATE_SELECT_FAILED;
|
|
if (!scsi->cmd_timer)
|
|
scsi->cmd_timer = 1;
|
|
break;
|
|
}
|
|
|
|
scsi->scsi_state = SCSI_STATE_SEND_COMMAND;
|
|
break;
|
|
|
|
case SCSI_STATE_SELECT_FAILED:
|
|
break;
|
|
|
|
case SCSI_STATE_SEND_COMMAND:
|
|
while (scsi->cdb.idx < scsi->cdb.len && bytes_transferred < MAX_BYTES_TRANSFERRED_PER_POLL)
|
|
{
|
|
int bus_out;
|
|
|
|
for (c = 0; c < 20; c++)
|
|
{
|
|
int bus_state = scsi_bus_read(&scsi->bus);
|
|
|
|
#ifndef RELEASE_BUILD
|
|
if (!(bus_state & BUS_BSY))
|
|
fatal("SEND_COMMAND - dropped BSY\n");
|
|
if ((bus_state & (BUS_IO | BUS_CD | BUS_MSG)) != BUS_CD)
|
|
fatal("SEND_COMMAND - bus phase incorrect\n");
|
|
#endif
|
|
if (bus_state & BUS_REQ)
|
|
break;
|
|
}
|
|
if (c == 20)
|
|
{
|
|
// pclog("SEND_COMMAND timed out\n");
|
|
break;
|
|
}
|
|
|
|
bus_out = BUS_SETDATA(scsi->cdb.data[scsi->cdb.idx]);
|
|
// pclog(" Command send %02x %i\n", scsi->cdb.data[scsi->cdb.idx], scsi->cdb.len);
|
|
scsi->cdb.idx++;
|
|
bytes_transferred++;
|
|
|
|
scsi_bus_update(&scsi->bus, bus_out | BUS_ACK);
|
|
scsi_bus_update(&scsi->bus, bus_out & ~BUS_ACK);
|
|
}
|
|
if (scsi->cdb.idx == scsi->cdb.len)
|
|
scsi->scsi_state = SCSI_STATE_NEXT_PHASE;
|
|
break;
|
|
|
|
case SCSI_STATE_NEXT_PHASE:
|
|
/*Wait for SCSI command to move to next phase*/
|
|
for (c = 0; c < 20; c++)
|
|
{
|
|
int bus_state = scsi_bus_read(&scsi->bus);
|
|
#ifndef RELEASE_BUILD
|
|
if (!(bus_state & BUS_BSY))
|
|
fatal("NEXT_PHASE - dropped BSY waiting\n");
|
|
#endif
|
|
if (bus_state & BUS_REQ)
|
|
{
|
|
int bus_out;
|
|
// pclog("SCSI next phase - %x\n", bus_state);
|
|
switch (bus_state & (BUS_IO | BUS_CD | BUS_MSG))
|
|
{
|
|
case 0:
|
|
// pclog("Move to write data\n");
|
|
scsi->scsi_state = SCSI_STATE_WRITE_DATA;
|
|
break;
|
|
|
|
case BUS_IO:
|
|
// pclog("Move to read data\n");
|
|
scsi->scsi_state = SCSI_STATE_READ_DATA;
|
|
break;
|
|
|
|
case (BUS_CD | BUS_IO):
|
|
// pclog("Move to read status\n");
|
|
scsi->scsi_state = SCSI_STATE_READ_STATUS;
|
|
break;
|
|
|
|
case (BUS_CD | BUS_IO | BUS_MSG):
|
|
// pclog("Move to read message\n");
|
|
scsi->scsi_state = SCSI_STATE_READ_MESSAGE;
|
|
break;
|
|
|
|
case BUS_CD:
|
|
bus_out = BUS_SETDATA(0);
|
|
|
|
scsi_bus_update(&scsi->bus, bus_out | BUS_ACK);
|
|
scsi_bus_update(&scsi->bus, bus_out & ~BUS_ACK);
|
|
break;
|
|
|
|
#ifndef RELEASE_BUILD
|
|
default:
|
|
fatal(" Bad new phase %x\n", bus_state);
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SCSI_STATE_END_PHASE:
|
|
/*Wait for SCSI command to move to next phase*/
|
|
for (c = 0; c < 20; c++)
|
|
{
|
|
int bus_state = scsi_bus_read(&scsi->bus);
|
|
|
|
if (!(bus_state & BUS_BSY))
|
|
{
|
|
// pclog("END_PHASE - dropped BSY waiting\n");
|
|
// if (scsi->ccb.req_sense_len == CCB_SENSE_NONE)
|
|
// fatal("No sense data\n");
|
|
scsi->scsi_state = SCSI_STATE_IDLE;
|
|
// pclog("End of command\n");
|
|
if (!scsi->cmd_timer)
|
|
scsi->cmd_timer = 1;
|
|
break;
|
|
}
|
|
#ifndef RELEASE_BUILD
|
|
if (bus_state & BUS_REQ)
|
|
fatal("END_PHASE - unexpected REQ\n");
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case SCSI_STATE_READ_DATA:
|
|
while (scsi->scsi_state == SCSI_STATE_READ_DATA && bytes_transferred < MAX_BYTES_TRANSFERRED_PER_POLL)
|
|
{
|
|
int d;
|
|
|
|
for (d = 0; d < 20; d++)
|
|
{
|
|
int bus_state = scsi_bus_read(&scsi->bus);
|
|
#ifndef RELEASE_BUILD
|
|
if (!(bus_state & BUS_BSY))
|
|
fatal("READ_DATA - dropped BSY waiting\n");
|
|
#endif
|
|
if ((bus_state & (BUS_IO | BUS_CD | BUS_MSG)) != BUS_IO)
|
|
{
|
|
// pclog("READ_DATA - changed phase\n");
|
|
scsi->scsi_state = SCSI_STATE_NEXT_PHASE;
|
|
break;
|
|
}
|
|
|
|
if (bus_state & BUS_REQ)
|
|
{
|
|
uint8_t data = BUS_GETDATA(bus_state);
|
|
int bus_out = 0;
|
|
|
|
if (scsi->cdb.data_idx < scsi->cdb.data_len)
|
|
{
|
|
if (scsi->cdb.data_pointer == -1)
|
|
scsi->int_buffer[scsi->cdb.data_idx] = data;
|
|
else
|
|
mem_writeb_phys(scsi->cdb.data_pointer + scsi->cdb.data_idx, data);
|
|
scsi->cdb.data_idx++;
|
|
|
|
if ((scsi->scb.enable & ENABLE_PT) && scsi->cdb.data_idx == scsi->cdb.data_len)
|
|
sg_fetch_next(scsi);
|
|
}
|
|
scsi->cdb.bytes_transferred++;
|
|
|
|
// pclog("Read data %02x %i %06x\n", data, scsi->cdb.data_idx, scsi->cdb.data_pointer + scsi->cdb.data_idx);
|
|
scsi_bus_update(&scsi->bus, bus_out | BUS_ACK);
|
|
scsi_bus_update(&scsi->bus, bus_out & ~BUS_ACK);
|
|
break;
|
|
}
|
|
}
|
|
|
|
bytes_transferred++;
|
|
}
|
|
break;
|
|
|
|
case SCSI_STATE_WRITE_DATA:
|
|
while (scsi->scsi_state == SCSI_STATE_WRITE_DATA && bytes_transferred < MAX_BYTES_TRANSFERRED_PER_POLL)
|
|
{
|
|
int d;
|
|
|
|
for (d = 0; d < 20; d++)
|
|
{
|
|
int bus_state = scsi_bus_read(&scsi->bus);
|
|
#ifndef RELEASE_BUILD
|
|
if (!(bus_state & BUS_BSY))
|
|
fatal("WRITE_DATA - dropped BSY waiting\n");
|
|
#endif
|
|
if ((bus_state & (BUS_IO | BUS_CD | BUS_MSG)) != 0)
|
|
{
|
|
// pclog("WRITE_DATA - changed phase\n");
|
|
scsi->scsi_state = SCSI_STATE_NEXT_PHASE;
|
|
break;
|
|
}
|
|
|
|
if (bus_state & BUS_REQ)
|
|
{
|
|
uint8_t data;// = BUS_GETDATA(bus_state);
|
|
int bus_out;
|
|
|
|
if (scsi->cdb.data_idx < scsi->cdb.data_len)
|
|
{
|
|
if (scsi->cdb.data_pointer == -1)
|
|
data = scsi->int_buffer[scsi->cdb.data_idx];
|
|
else
|
|
data = mem_readb_phys(scsi->cdb.data_pointer + scsi->cdb.data_idx);
|
|
|
|
scsi->cdb.data_idx++;
|
|
if ((scsi->scb.enable & ENABLE_PT) && scsi->cdb.data_idx == scsi->cdb.data_len)
|
|
sg_fetch_next(scsi);
|
|
}
|
|
else
|
|
data = 0xff;
|
|
scsi->cdb.bytes_transferred++;
|
|
|
|
// pclog("Write data %02x %i\n", data, scsi->cdb.data_idx);
|
|
bus_out = BUS_SETDATA(data);
|
|
scsi_bus_update(&scsi->bus, bus_out | BUS_ACK);
|
|
scsi_bus_update(&scsi->bus, bus_out & ~BUS_ACK);
|
|
break;
|
|
}
|
|
}
|
|
|
|
bytes_transferred++;
|
|
}
|
|
// if (scsi->cdb.data_idx == scsi->cdb.data_len)
|
|
// scsi->scsi_state = SCSI_STATE_NEXT_PHASE;
|
|
break;
|
|
|
|
case SCSI_STATE_READ_STATUS:
|
|
for (c = 0; c < 20; c++)
|
|
{
|
|
int bus_state = scsi_bus_read(&scsi->bus);
|
|
#ifndef RELEASE_BUILD
|
|
if (!(bus_state & BUS_BSY))
|
|
fatal("READ_STATUS - dropped BSY waiting\n");
|
|
#endif
|
|
if ((bus_state & (BUS_IO | BUS_CD | BUS_MSG)) != (BUS_CD | BUS_IO))
|
|
{
|
|
// pclog("READ_STATUS - changed phase\n");
|
|
scsi->scsi_state = SCSI_STATE_NEXT_PHASE;
|
|
break;
|
|
}
|
|
|
|
if (bus_state & BUS_REQ)
|
|
{
|
|
uint8_t status = BUS_GETDATA(bus_state);
|
|
int bus_out = 0;
|
|
|
|
// pclog("Read status %02x\n", status);
|
|
scsi->cdb.last_status = status;
|
|
|
|
scsi_bus_update(&scsi->bus, bus_out | BUS_ACK);
|
|
scsi_bus_update(&scsi->bus, bus_out & ~BUS_ACK);
|
|
|
|
scsi->scsi_state = SCSI_STATE_NEXT_PHASE;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SCSI_STATE_READ_MESSAGE:
|
|
for (c = 0; c < 20; c++)
|
|
{
|
|
int bus_state = scsi_bus_read(&scsi->bus);
|
|
#ifndef RELEASE_BUILD
|
|
if (!(bus_state & BUS_BSY))
|
|
fatal("READ_MESSAGE - dropped BSY waiting\n");
|
|
#endif
|
|
if ((bus_state & (BUS_IO | BUS_CD | BUS_MSG)) != (BUS_CD | BUS_IO | BUS_MSG))
|
|
{
|
|
// pclog("READ_MESSAGE - changed phase\n");
|
|
scsi->scsi_state = SCSI_STATE_NEXT_PHASE;
|
|
break;
|
|
}
|
|
|
|
if (bus_state & BUS_REQ)
|
|
{
|
|
uint8_t msg = BUS_GETDATA(bus_state);
|
|
int bus_out = 0;
|
|
|
|
// pclog("Read message %02x\n", msg);
|
|
scsi_bus_update(&scsi->bus, bus_out | BUS_ACK);
|
|
scsi_bus_update(&scsi->bus, bus_out & ~BUS_ACK);
|
|
|
|
switch (msg)
|
|
{
|
|
case MSG_COMMAND_COMPLETE:
|
|
scsi->scsi_state = SCSI_STATE_END_PHASE;
|
|
break;
|
|
#ifndef RELEASE_BUILD
|
|
default:
|
|
fatal("READ_MESSAGE - unknown message %02x\n", msg);
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
#ifndef RELEASE_BUILD
|
|
default:
|
|
fatal("Unknown SCSI_state %d\n", scsi->scsi_state);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static void scsi_callback(void *p)
|
|
{
|
|
scsi_ibm_t *scsi = (scsi_ibm_t *)p;
|
|
|
|
timer_advance_u64(&scsi->callback_timer, POLL_TIME);
|
|
|
|
// pclog("poll %i\n", scsi->cmd_state);
|
|
if (scsi->cmd_timer)
|
|
{
|
|
scsi->cmd_timer--;
|
|
if (!scsi->cmd_timer)
|
|
process_ibm_cmd(scsi);
|
|
}
|
|
if (scsi->attention_waiting &&
|
|
(scsi->scb_state == SCB_STATE_IDLE || (scsi->attention_pending & 0xf0) == 0xe0))
|
|
{
|
|
scsi->attention_waiting--;
|
|
if (!scsi->attention_waiting)
|
|
{
|
|
// pclog("Process attention request\n");
|
|
scsi->attention = scsi->attention_pending;
|
|
scsi->status &= ~STATUS_BUSY;
|
|
scsi->cir[0] = scsi->cir_pending[0];
|
|
scsi->cir[1] = scsi->cir_pending[1];
|
|
scsi->cir[2] = scsi->cir_pending[2];
|
|
scsi->cir[3] = scsi->cir_pending[3];
|
|
scsi->cir_status = 0;
|
|
|
|
switch (scsi->attention >> 4)
|
|
{
|
|
case 0x1: /*Immediate command*/
|
|
scsi->cmd_status = 0xa;
|
|
scsi->command = scsi->cir[0] | (scsi->cir[1] << 8) | (scsi->cir[2] << 16) | (scsi->cir[3] << 24);
|
|
switch (scsi->command & CMD_MASK)
|
|
{
|
|
case CMD_ASSIGN:
|
|
case CMD_DMA_PACING_CONTROL:
|
|
case CMD_FEATURE_CONTROL:
|
|
case CMD_INVALID_412:
|
|
case CMD_RESET:
|
|
process_immediate_command(scsi);
|
|
break;
|
|
|
|
#ifndef RELEASE_BUILD
|
|
default:
|
|
fatal("Unknown immediate command %08x\n", scsi->command);
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case 0x3: case 0x4: case 0xf: /*Start SCB*/
|
|
scsi->cmd_status = 0x1;
|
|
scsi->scb_addr = scsi->cir[0] | (scsi->cir[1] << 8) | (scsi->cir[2] << 16) | (scsi->cir[3] << 24);
|
|
scsi->scb_state = SCB_STATE_START;
|
|
scsi->scb_id = scsi->attention & 0xf;
|
|
scsi->cmd_timer = SCSI_TIME*2;
|
|
// pclog("Start SCB at %08x\n", scsi->scb_addr);
|
|
break;
|
|
|
|
case 5: /*Invalid*/
|
|
case 0xa: /*Invalid*/
|
|
scsi->in_invalid = 1;
|
|
scsi->cmd_timer = SCSI_TIME*2;
|
|
break;
|
|
|
|
case 0xe: /*EOI*/
|
|
scsi->irq_status = 0;
|
|
scsi_clear_irq(scsi, scsi->attention & 0xf);
|
|
break;
|
|
#ifndef RELEASE_BUILD
|
|
default:
|
|
fatal("Attention Register 5 %02x\n", scsi->attention);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
process_scsi(scsi);
|
|
}
|
|
|
|
static uint8_t scsi_ibm_mca_read(int port, void *p)
|
|
{
|
|
scsi_ibm_t *scsi = (scsi_ibm_t *)p;
|
|
|
|
// pclog("scsi_mca_read: port=%04x %02x %04x:%04x\n", port, scsi->pos_regs[port & 7], CS,cpu_state.pc);
|
|
|
|
return scsi->pos_regs[port & 7];
|
|
}
|
|
|
|
static void scsi_ibm_mca_write(int port, uint8_t val, void *p)
|
|
{
|
|
scsi_ibm_t *scsi = (scsi_ibm_t *)p;
|
|
|
|
if (port < 0x102)
|
|
return;
|
|
|
|
// pclog("scsi_mca_write: port=%04x val=%02x %04x:%04x\n", port, val, CS, cpu_state.pc);
|
|
|
|
io_removehandler((((scsi->pos_regs[2] >> 1) & 7) * 8) + 0x3540, 0x0008, scsi_read, scsi_readw, NULL, scsi_write, scsi_writew, NULL, scsi);
|
|
mem_mapping_disable(&scsi->bios_rom.mapping);
|
|
|
|
scsi->pos_regs[port & 7] = val;
|
|
|
|
if (scsi->pos_regs[2] & 1)
|
|
{
|
|
// pclog(" scsi io = %04x\n", (((scsi->pos_regs[2] >> 1) & 7) * 8) + 0x3540);
|
|
io_sethandler((((scsi->pos_regs[2] >> 1) & 7) * 8) + 0x3540, 0x0008, scsi_read, scsi_readw, NULL, scsi_write, scsi_writew, NULL, scsi);
|
|
if ((scsi->pos_regs[2] & 0xf0) != 0xf0)
|
|
{
|
|
mem_mapping_enable(&scsi->bios_rom.mapping);
|
|
mem_mapping_set_addr(&scsi->bios_rom.mapping, ((scsi->pos_regs[2] >> 4) * 0x2000) + 0xc0000, 0x8000);
|
|
// pclog("SCSI BIOS ROM at %05x %02x\n", ((scsi->pos_regs[2] >> 4) * 0x2000) + 0xc0000, scsi->pos_regs[2]);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void scsi_ibm_reset(void *p)
|
|
{
|
|
scsi_ibm_t *scsi = (scsi_ibm_t *)p;
|
|
|
|
// pclog("scsi_ibm_reset\n");
|
|
scsi->in_reset = 2;
|
|
scsi->cmd_timer = SCSI_TIME * 50;
|
|
scsi->status = STATUS_BUSY;
|
|
scsi->scsi_state = SCSI_STATE_IDLE;
|
|
scsi->scb_state = SCB_STATE_IDLE;
|
|
scsi->in_invalid = 0;
|
|
scsi->attention_waiting = 0;
|
|
scsi->basic_ctrl = 0;
|
|
scsi_bus_reset(&scsi->bus);
|
|
}
|
|
|
|
static void *scsi_ibm_init()
|
|
{
|
|
int c;
|
|
scsi_ibm_t *scsi = malloc(sizeof(scsi_ibm_t));
|
|
memset(scsi, 0, sizeof(scsi_ibm_t));
|
|
|
|
rom_init_interleaved(&scsi->bios_rom, "92F2244.U68", "92F2245.U69", 0xc8000, 0x8000, 0x7fff, 0x4000, MEM_MAPPING_EXTERNAL);
|
|
mem_mapping_disable(&scsi->bios_rom.mapping);
|
|
|
|
timer_add(&scsi->callback_timer, scsi_callback, scsi, 1);
|
|
|
|
mca_add(scsi_ibm_mca_read, scsi_ibm_mca_write, scsi_ibm_reset, scsi);
|
|
|
|
scsi->pos_regs[0] = 0xff;
|
|
scsi->pos_regs[1] = 0x8e;
|
|
|
|
scsi->in_reset = 2;
|
|
scsi->cmd_timer = SCSI_TIME * 50;
|
|
scsi->status = STATUS_BUSY;
|
|
|
|
scsi_bus_init(&scsi->bus);
|
|
|
|
/* for (c = 0; c < 7; c++)
|
|
{
|
|
scsi->dev_id[c].phys_id = c;
|
|
scsi->dev_id[c].lun_id = 0;
|
|
}*/
|
|
for (c = 0; c < 15; c++)
|
|
scsi->dev_id[c].phys_id = -1;
|
|
scsi->dev_id[15].phys_id = 7;
|
|
|
|
return scsi;
|
|
}
|
|
|
|
static void scsi_ibm_close(void *p)
|
|
{
|
|
scsi_ibm_t *scsi = (scsi_ibm_t *)p;
|
|
|
|
free(scsi);
|
|
}
|
|
|
|
static int scsi_ibm_available()
|
|
{
|
|
return rom_present("92F2244.U68") && rom_present("92F2245.U69");
|
|
}
|
|
|
|
device_t scsi_ibm_device =
|
|
{
|
|
"IBM SCSI Adapter with Cache (MCA)",
|
|
DEVICE_MCA,
|
|
scsi_ibm_init,
|
|
scsi_ibm_close,
|
|
scsi_ibm_available,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
};
|