summaryrefslogtreecommitdiffstats
path: root/drivers/scsi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi')
-rw-r--r--drivers/scsi/53c7,8xx.c364
-rw-r--r--drivers/scsi/53c7,8xx.h2
-rw-r--r--drivers/scsi/Config.in9
-rw-r--r--drivers/scsi/Makefile8
-rw-r--r--drivers/scsi/README.aic7xxx44
-rw-r--r--drivers/scsi/aic7xxx.c7361
-rw-r--r--drivers/scsi/aic7xxx.h9
-rw-r--r--drivers/scsi/aic7xxx.seq1127
-rw-r--r--drivers/scsi/aic7xxx_asm.c734
-rw-r--r--drivers/scsi/aic7xxx_proc.c42
-rw-r--r--drivers/scsi/aic7xxx_reg.h1154
-rw-r--r--drivers/scsi/atari_scsi.c32
-rw-r--r--drivers/scsi/eata.h41
-rw-r--r--drivers/scsi/esp.c20
-rw-r--r--drivers/scsi/ppa.c26
-rw-r--r--drivers/scsi/ppa.h2
16 files changed, 5106 insertions, 5869 deletions
diff --git a/drivers/scsi/53c7,8xx.c b/drivers/scsi/53c7,8xx.c
index 44a78b12c..3d28dae2f 100644
--- a/drivers/scsi/53c7,8xx.c
+++ b/drivers/scsi/53c7,8xx.c
@@ -560,7 +560,7 @@ issue_to_cmd (struct Scsi_Host *host, struct NCR53c7x0_hostdata *hostdata,
* sure what the relationship between the NCR structures
* and host structures were going to be.
*/
- (struct NCR53c7x0_cmd *) ((char *) bus_to_virt (issue[1]) -
+ (struct NCR53c7x0_cmd *) ((char *) bus_to_virt (le32_to_cpu(issue[1])) -
(hostdata->E_dsa_code_begin - hostdata->E_dsa_code_template) -
offsetof(struct NCR53c7x0_cmd, dsa))
/* If the IF TRUE bit is not set, it's a NOP */
@@ -795,18 +795,18 @@ NCR53c7x0_driver_init (struct Scsi_Host *host) {
for (i = 0, curr = (u32 *) hostdata->schedule;
i < host->can_queue; ++i, curr += 2) {
curr[0] = hostdata->NOP_insn;
- curr[1] = 0xdeadbeef;
+ curr[1] = le32_to_cpu(0xdeadbeef);
}
- curr[0] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24) | DBC_TCI_TRUE;
- curr[1] = (u32) virt_to_bus (hostdata->script) +
- hostdata->E_wait_reselect;
+ curr[0] = le32_to_cpu(((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24) | DBC_TCI_TRUE);
+ curr[1] = (u32) le32_to_cpu(virt_to_bus (hostdata->script) +
+ hostdata->E_wait_reselect);
hostdata->reconnect_dsa_head = 0;
hostdata->addr_reconnect_dsa_head = (u32)
- virt_to_bus((void *) &(hostdata->reconnect_dsa_head));
+ le32_to_cpu(virt_to_bus((void *) &(hostdata->reconnect_dsa_head)));
hostdata->expecting_iid = 0;
hostdata->expecting_sto = 0;
if (hostdata->options & OPTION_ALWAYS_SYNCHRONOUS)
- hostdata->initiate_sdtr = 0xffff;
+ hostdata->initiate_sdtr = le32_to_cpu(0xffff);
else
hostdata->initiate_sdtr = 0;
hostdata->talked_to = 0;
@@ -877,7 +877,7 @@ clock_to_ccf (int clock) {
* Returns : 0 on success, -1 on failure.
*/
-static inline int
+static int
NCR53c7x0_init (struct Scsi_Host *host) {
NCR53c7x0_local_declare();
int i, ccf, expected_ccf;
@@ -899,6 +899,7 @@ NCR53c7x0_init (struct Scsi_Host *host) {
* will differ.
*/
int expected_mapping = OPTION_IO_MAPPED;
+
NCR53c7x0_local_setup(host);
switch (hostdata->chip) {
@@ -929,10 +930,10 @@ NCR53c7x0_init (struct Scsi_Host *host) {
/* Assign constants accessed by NCR */
hostdata->NCR53c7xx_zero = 0;
- hostdata->NCR53c7xx_msg_reject = MESSAGE_REJECT;
- hostdata->NCR53c7xx_msg_abort = ABORT;
- hostdata->NCR53c7xx_msg_nop = NOP;
- hostdata->NOP_insn = (DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24;
+ hostdata->NCR53c7xx_msg_reject = le32_to_cpu(MESSAGE_REJECT);
+ hostdata->NCR53c7xx_msg_abort = le32_to_cpu(ABORT);
+ hostdata->NCR53c7xx_msg_nop = le32_to_cpu(NOP);
+ hostdata->NOP_insn = le32_to_cpu((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24);
if (expected_mapping == -1 ||
(hostdata->options & (OPTION_MEMORY_MAPPED)) !=
@@ -1131,7 +1132,14 @@ NCR53c7x0_init (struct Scsi_Host *host) {
search->irq == host->irq && search != host); search=search->next);
if (!search) {
- if (request_irq(host->irq, NCR53c7x0_intr, SA_INTERRUPT, "53c7,8xx", NULL)) {
+#ifdef __powerpc__
+ if (request_irq(host->irq, NCR53c7x0_intr, SA_SHIRQ, "53c7,8xx", NULL))
+#else
+ if (request_irq(host->irq, NCR53c7x0_intr, SA_INTERRUPT, "53c7,8xx", NULL))
+#endif
+ {
+
+
printk("scsi%d : IRQ%d not free, detaching\n"
" You have either a configuration problem, or a\n"
" broken BIOS. You may wish to manually assign\n"
@@ -1453,6 +1461,21 @@ ncr_pci_init (Scsi_Host_Template *tpnt, int board, int chip,
return -1;
}
+#ifdef __powerpc__
+ if ( ! (command & PCI_COMMAND_MASTER)) {
+ printk("SCSI: PCI Master Bit has not been set. Setting...\n");
+ command |= PCI_COMMAND_MASTER|PCI_COMMAND_IO;
+ pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command);
+ if (io_port >= 0x10000000) {
+ /* Mapping on PowerPC can't handle this! */
+ unsigned long new_io_port;
+ new_io_port = (io_port & 0x00FFFFFF) | 0x01000000;
+ printk("SCSI: I/O moved from %08X to %08x\n", io_port, new_io_port);
+ io_port = new_io_port;
+ pcibios_write_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0, io_port);
+ }
+ }
+#endif
/*
* Bit 0 is the address space indicator and must be one for I/O
@@ -1613,6 +1636,9 @@ NCR53c8x0_init_fixup (struct Scsi_Host *host) {
unsigned char tmp;
int i, ncr_to_memory, memory_to_ncr;
u32 base;
+#ifdef __powerpc__
+ unsigned long *script_ptr;
+#endif
NCR53c7x0_local_setup(host);
@@ -1801,6 +1827,14 @@ NCR53c8x0_init_fixup (struct Scsi_Host *host) {
printk("scsi%d : NCR dsa_fields start is %d not %d\n",
host->host_no, A_dsa_fields_start, Ent_dsa_code_template_end -
Ent_dsa_zero);
+#ifdef __powerpc__
+/* The PowerPC is Big Endian - adjust script appropriately */
+ script_ptr = hostdata->script;
+ for (i = 0; i < sizeof(SCRIPT); i += sizeof(long))
+ {
+ *script_ptr++ = le32_to_cpu(*script_ptr);
+ }
+#endif
printk("scsi%d : NCR code relocated to 0x%lx (virt 0x%p)\n", host->host_no,
virt_to_bus(hostdata->script), hostdata->script);
@@ -1884,7 +1918,7 @@ NCR53c8xx_run_tests (struct Scsi_Host *host) {
" also verify that the board is jumpered to use PCI INTA, since\n"
" most PCI motherboards lack support for INTB, INTC, and INTD.\n"
: "");
- else if (hostdata->test_completed != 1)
+ else if (hostdata->test_completed != 1)
printk ("scsi%d : test 1 bad interrupt value (%d)\n",
host->host_no, hostdata->test_completed);
else
@@ -1922,16 +1956,17 @@ NCR53c8xx_run_tests (struct Scsi_Host *host) {
cmd[1] = cmd[2] = cmd[3] = cmd[5] = 0;
cmd[4] = sizeof(data);
- dsa[2] = 1;
- dsa[3] = virt_to_bus(&identify);
- dsa[4] = 6;
- dsa[5] = virt_to_bus(&cmd);
- dsa[6] = sizeof(data);
- dsa[7] = virt_to_bus(&data);
- dsa[8] = 1;
- dsa[9] = virt_to_bus(&status);
- dsa[10] = 1;
- dsa[11] = virt_to_bus(&msg);
+/* Need to adjust for endian-ness */
+ dsa[2] = le32_to_cpu(1);
+ dsa[3] = le32_to_cpu(virt_to_bus(&identify));
+ dsa[4] = le32_to_cpu(6);
+ dsa[5] = le32_to_cpu(virt_to_bus(&cmd));
+ dsa[6] = le32_to_cpu(sizeof(data));
+ dsa[7] = le32_to_cpu(virt_to_bus(&data));
+ dsa[8] = le32_to_cpu(1);
+ dsa[9] = le32_to_cpu(virt_to_bus(&status));
+ dsa[10] = le32_to_cpu(1);
+ dsa[11] = le32_to_cpu(virt_to_bus(&msg));
for (i = 0; i < 3; ++i) {
cli();
@@ -1942,7 +1977,7 @@ NCR53c8xx_run_tests (struct Scsi_Host *host) {
}
/* SCNTL3 SDID */
- dsa[0] = (0x33 << 24) | (i << 16) ;
+ dsa[0] = le32_to_cpu((0x33 << 24) | (i << 16)) ;
hostdata->idle = 0;
hostdata->test_running = 2;
hostdata->test_completed = -1;
@@ -2004,9 +2039,22 @@ NCR53c8xx_dsa_fixup (struct NCR53c7x0_cmd *cmd) {
struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
host->hostdata;
int i;
+#ifdef __powerpc__
+ int len;
+ unsigned long *dsa_ptr;
+#endif
memcpy (cmd->dsa, hostdata->script + (hostdata->E_dsa_code_template / 4),
hostdata->E_dsa_code_template_end - hostdata->E_dsa_code_template);
+#ifdef __powerpc__
+ /* Note: the script has already been 'endianized' */
+ dsa_ptr = cmd->dsa;
+ len = hostdata->E_dsa_code_template_end - hostdata->E_dsa_code_template;
+ for (i = 0; i < len; i += sizeof(long))
+ {
+ *dsa_ptr++ = le32_to_cpu(*dsa_ptr);
+ }
+#endif
/*
* Note : within the NCR 'C' code, dsa points to the _start_
@@ -2046,6 +2094,14 @@ NCR53c8xx_dsa_fixup (struct NCR53c7x0_cmd *cmd) {
/* XXX - new start stuff */
patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
dsa_temp_addr_dsa_value, virt_to_bus(&cmd->dsa_addr));
+#ifdef __powerpc__
+ dsa_ptr = cmd->dsa;
+ len = hostdata->E_dsa_code_template_end - hostdata->E_dsa_code_template;
+ for (i = 0; i < len; i += sizeof(long))
+ {
+ *dsa_ptr++ = le32_to_cpu(*dsa_ptr);
+ }
+#endif
}
@@ -2125,7 +2181,7 @@ abnormal_finished (struct NCR53c7x0_cmd *cmd, int result) {
if (issue_to_cmd (host, hostdata, (u32 *) curr) == cmd)
{
curr[0] = hostdata->NOP_insn;
- curr[1] = 0xdeadbeef;
+ curr[1] = le32_to_cpu(0xdeadbeef);
++found;
break;
}
@@ -2140,13 +2196,13 @@ abnormal_finished (struct NCR53c7x0_cmd *cmd, int result) {
*/
for (left = host->can_queue,
- ncr_search = hostdata->reconnect_dsa_head,
+ ncr_search = le32_to_cpu(hostdata->reconnect_dsa_head),
ncr_prev = &hostdata->reconnect_dsa_head;
left >= 0 && ncr_search &&
((char*)bus_to_virt(ncr_search) + hostdata->dsa_start)
!= (char *) cmd->dsa;
ncr_prev = (u32*) ((char*)bus_to_virt(ncr_search) +
- hostdata->dsa_next), ncr_search = *ncr_prev, --left);
+ hostdata->dsa_next), ncr_search = le32_to_cpu(*ncr_prev), --left);
if (left < 0)
printk("scsi%d: loop detected in ncr reconnect list\n",
@@ -2491,7 +2547,7 @@ NCR53c8x0_dstat_sir_intr (struct Scsi_Host *host, struct
dsps = NCR53c7x0_read32(DSPS_REG);
dsp = (u32 *) bus_to_virt(NCR53c7x0_read32(DSP_REG));
- if (hostdata->options & OPTION_DEBUG_INTR)
+ if (hostdata->options & OPTION_DEBUG_INTR)
printk ("scsi%d : DSPS = 0x%x\n", host->host_no, dsps);
switch (dsps) {
@@ -2584,9 +2640,9 @@ NCR53c8x0_dstat_sir_intr (struct Scsi_Host *host, struct
hostdata->msg_buf[4] = 0; /* 0 offset = async */
asynchronous (host, c->target);
}
- patch_dsa_32 (cmd->dsa, dsa_msgout_other, 0, 5);
+ patch_dsa_32 (cmd->dsa, dsa_msgout_other, 0, le32_to_cpu(5));
patch_dsa_32 (cmd->dsa, dsa_msgout_other, 1, (u32)
- virt_to_bus ((void *)&hostdata->msg_buf));
+ le32_to_cpu(virt_to_bus ((void *)&hostdata->msg_buf)));
hostdata->dsp = hostdata->script +
hostdata->E_respond_message / sizeof(u32);
hostdata->dsp_changed = 1;
@@ -2649,14 +2705,14 @@ NCR53c8x0_dstat_sir_intr (struct Scsi_Host *host, struct
* agrees with this being an untagged queue'd command.
*/
- patch_dsa_32 (cmd->dsa, dsa_msgout, 0, 1);
+ patch_dsa_32 (cmd->dsa, dsa_msgout, 0, le32_to_cpu(1));
/*
* Modify the table indirect for COMMAND OUT phase, since
* Request Sense is a six byte command.
*/
- patch_dsa_32 (cmd->dsa, dsa_cmdout, 0, 6);
+ patch_dsa_32 (cmd->dsa, dsa_cmdout, 0, le32_to_cpu(6));
c->cmnd[0] = REQUEST_SENSE;
c->cmnd[1] &= 0xe0; /* Zero all but LUN */
@@ -2672,17 +2728,17 @@ NCR53c8x0_dstat_sir_intr (struct Scsi_Host *host, struct
*/
patch_dsa_32 (cmd->dsa, dsa_dataout, 0,
- virt_to_bus(hostdata->script) + hostdata->E_other_transfer);
+ le32_to_cpu(virt_to_bus(hostdata->script) + hostdata->E_other_transfer));
patch_dsa_32 (cmd->dsa, dsa_datain, 0,
- virt_to_bus(cmd->data_transfer_start));
- cmd->data_transfer_start[0] = (((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I |
- DCMD_BMI_IO)) << 24) | sizeof(c->sense_buffer);
- cmd->data_transfer_start[1] = (u32) virt_to_bus(c->sense_buffer);
+ le32_to_cpu(virt_to_bus(cmd->data_transfer_start)));
+ cmd->data_transfer_start[0] = le32_to_cpu((((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I |
+ DCMD_BMI_IO)) << 24) | sizeof(c->sense_buffer));
+ cmd->data_transfer_start[1] = (u32) le32_to_cpu(virt_to_bus(c->sense_buffer));
- cmd->data_transfer_start[2] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP)
- << 24) | DBC_TCI_TRUE;
- cmd->data_transfer_start[3] = (u32) virt_to_bus(hostdata->script) +
- hostdata->E_other_transfer;
+ cmd->data_transfer_start[2] = le32_to_cpu(((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP)
+ << 24) | DBC_TCI_TRUE);
+ cmd->data_transfer_start[3] = (u32) le32_to_cpu(virt_to_bus(hostdata->script) +
+ hostdata->E_other_transfer);
/*
* Currently, this command is flagged as completed, ie
@@ -2691,7 +2747,7 @@ NCR53c8x0_dstat_sir_intr (struct Scsi_Host *host, struct
* status, etc are used.
*/
- cmd->cmd->result = 0xffff;
+ cmd->cmd->result = le32_to_cpu(0xffff);
/*
* Restart command as a REQUEST SENSE.
@@ -2732,7 +2788,7 @@ NCR53c8x0_dstat_sir_intr (struct Scsi_Host *host, struct
host->host_no, NCR53c7x0_read32(DSA_REG), dsa);
printk("scsi%d : resume address is 0x%x (virt 0x%p)\n",
host->host_no, cmd->saved_data_pointer,
- bus_to_virt(cmd->saved_data_pointer));
+ bus_to_virt(le32_to_cpu(cmd->saved_data_pointer)));
print_insn (host, hostdata->script + Ent_reselected_ok /
sizeof(u32), "", 1);
printk ("scsi%d : sxfer=0x%x, scntl3=0x%x\n",
@@ -2765,7 +2821,7 @@ NCR53c8x0_dstat_sir_intr (struct Scsi_Host *host, struct
if (dsa) {
printk("scsi%d : resume address is 0x%x (virt 0x%p)\n",
host->host_no, cmd->saved_data_pointer,
- bus_to_virt (cmd->saved_data_pointer));
+ bus_to_virt (le32_to_cpu(cmd->saved_data_pointer)));
#if 0
printk("scsi%d : template code :\n", host->host_no);
for (code = dsa + (Ent_dsa_code_check_reselect - Ent_dsa_zero)
@@ -2794,7 +2850,7 @@ NCR53c8x0_dstat_sir_intr (struct Scsi_Host *host, struct
printk("scsi%d : resume address is 0x%x (virt 0x%p)\n"
" (temp was 0x%x (virt 0x%p))\n",
host->host_no, cmd->saved_data_pointer,
- bus_to_virt (cmd->saved_data_pointer),
+ bus_to_virt (le32_to_cpu(cmd->saved_data_pointer)),
NCR53c7x0_read32 (TEMP_REG),
bus_to_virt (NCR53c7x0_read32(TEMP_REG)));
}
@@ -2888,7 +2944,7 @@ NCR53c8x0_dstat_sir_intr (struct Scsi_Host *host, struct
OPTION_DEBUG_DISCONNECT)) {
printk ("scsi%d : saved data pointer 0x%x (virt 0x%p)\n",
host->host_no, cmd->saved_data_pointer,
- bus_to_virt (cmd->saved_data_pointer));
+ bus_to_virt (le32_to_cpu(cmd->saved_data_pointer)));
print_progress (c);
}
return SPECIFIC_INT_RESTART;
@@ -2901,11 +2957,11 @@ NCR53c8x0_dstat_sir_intr (struct Scsi_Host *host, struct
int size;
printk ("scsi%d : restored data pointer 0x%x (virt 0x%p)\n",
host->host_no, cmd->saved_data_pointer, bus_to_virt (
- cmd->saved_data_pointer));
+ le32_to_cpu(cmd->saved_data_pointer)));
size = print_insn (host, (u32 *)
- bus_to_virt(cmd->saved_data_pointer), "", 1);
+ bus_to_virt(le32_to_cpu(cmd->saved_data_pointer)), "", 1);
size = print_insn (host, (u32 *)
- bus_to_virt(cmd->saved_data_pointer) + size, "", 1);
+ bus_to_virt(le32_to_cpu(cmd->saved_data_pointer)) + size, "", 1);
print_progress (c);
}
#if 0
@@ -2947,8 +3003,8 @@ NCR53c8x0_dstat_sir_intr (struct Scsi_Host *host, struct
(int) NCR53c7x0_read8(SCNTL3_REG_800),
datapath_residual (host)) ;
print_insn (host, dsp, "", 1);
- size = print_insn (host, (u32 *) bus_to_virt(dsp[1]), "", 1);
- print_insn (host, (u32 *) bus_to_virt(dsp[1]) + size, "", 1);
+ size = print_insn (host, (u32 *) bus_to_virt(le32_to_cpu(dsp[1])), "", 1);
+ print_insn (host, (u32 *) bus_to_virt(le32_to_cpu(dsp[1])) + size, "", 1);
}
return SPECIFIC_INT_RESTART;
#endif
@@ -3543,7 +3599,6 @@ create_cmd (Scsi_Cmnd *cmd) {
case MODE_SELECT:
case WRITE_6:
case WRITE_10:
- case START_STOP: /* also SCAN, which may do DATA OUT */
#if 0
printk("scsi%d : command is ", host->host_no);
print_command(cmd->cmnd);
@@ -3562,6 +3617,7 @@ create_cmd (Scsi_Cmnd *cmd) {
* These commands do no data transfer, we should force an
* interrupt if a data phase is attempted on them.
*/
+ case START_STOP: /* also SCAN, which may do DATA OUT */
case TEST_UNIT_READY:
datain = dataout = 0;
break;
@@ -3612,8 +3668,8 @@ create_cmd (Scsi_Cmnd *cmd) {
* will start the data transfer over at the beginning.
*/
- tmp->saved_data_pointer = virt_to_bus (hostdata->script) +
- hostdata->E_data_transfer;
+ tmp->saved_data_pointer = le32_to_cpu(virt_to_bus (hostdata->script) +
+ hostdata->E_data_transfer);
/*
* Initialize Linux specific fields.
@@ -3622,9 +3678,9 @@ create_cmd (Scsi_Cmnd *cmd) {
tmp->cmd = cmd;
tmp->next = NULL;
tmp->flags = 0;
- tmp->dsa_next_addr = virt_to_bus(tmp->dsa) + hostdata->dsa_next -
- hostdata->dsa_start;
- tmp->dsa_addr = virt_to_bus(tmp->dsa) - hostdata->dsa_start;
+ tmp->dsa_next_addr = le32_to_cpu(virt_to_bus(tmp->dsa) + hostdata->dsa_next -
+ hostdata->dsa_start);
+ tmp->dsa_addr = le32_to_cpu(virt_to_bus(tmp->dsa) - hostdata->dsa_start);
/*
* Calculate addresses of dynamic code to fill in DSA
@@ -3650,8 +3706,8 @@ create_cmd (Scsi_Cmnd *cmd) {
if (hostdata->dsa_fixup)
hostdata->dsa_fixup(tmp);
- patch_dsa_32(tmp->dsa, dsa_next, 0, 0);
- patch_dsa_32(tmp->dsa, dsa_cmnd, 0, virt_to_bus(cmd));
+ patch_dsa_32(tmp->dsa, dsa_next, 0, le32_to_cpu(0));
+ patch_dsa_32(tmp->dsa, dsa_cmnd, 0, le32_to_cpu(virt_to_bus(cmd)));
if (hostdata->options & OPTION_DEBUG_SYNCHRONOUS)
if (hostdata->sync[cmd->target].select_indirect !=
@@ -3664,8 +3720,8 @@ create_cmd (Scsi_Cmnd *cmd) {
}
- patch_dsa_32(tmp->dsa, dsa_select, 0, hostdata->sync[cmd->target].
- select_indirect);
+ patch_dsa_32(tmp->dsa, dsa_select, 0, le32_to_cpu(hostdata->sync[cmd->target].
+ select_indirect));
/*
* Right now, we'll do the WIDE and SYNCHRONOUS negotiations on
* different commands; although it should be trivial to do them
@@ -3674,7 +3730,7 @@ create_cmd (Scsi_Cmnd *cmd) {
if (hostdata->initiate_wdtr & (1 << cmd->target)) {
memcpy ((void *) (tmp->select + 1), (void *) wdtr_message,
sizeof(wdtr_message));
- patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1 + sizeof(wdtr_message));
+ patch_dsa_32(tmp->dsa, dsa_msgout, 0, le32_to_cpu(1 + sizeof(wdtr_message)));
save_flags(flags);
cli();
hostdata->initiate_wdtr &= ~(1 << cmd->target);
@@ -3682,7 +3738,7 @@ create_cmd (Scsi_Cmnd *cmd) {
} else if (hostdata->initiate_sdtr & (1 << cmd->target)) {
memcpy ((void *) (tmp->select + 1), (void *) sdtr_message,
sizeof(sdtr_message));
- patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1 + sizeof(sdtr_message));
+ patch_dsa_32(tmp->dsa, dsa_msgout, 0, le32_to_cpu(1 + sizeof(sdtr_message)));
tmp->flags |= CMD_FLAG_SDTR;
save_flags(flags);
cli();
@@ -3695,40 +3751,40 @@ create_cmd (Scsi_Cmnd *cmd) {
!(hostdata->options & OPTION_NO_ASYNC)) {
memcpy ((void *) (tmp->select + 1), (void *) async_message,
sizeof(async_message));
- patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1 + sizeof(async_message));
+ patch_dsa_32(tmp->dsa, dsa_msgout, 0, le32_to_cpu(1 + sizeof(async_message)));
tmp->flags |= CMD_FLAG_SDTR;
}
#endif
else
- patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1);
+ patch_dsa_32(tmp->dsa, dsa_msgout, 0, le32_to_cpu(1));
hostdata->talked_to |= (1 << cmd->target);
tmp->select[0] = (hostdata->options & OPTION_DISCONNECT) ?
IDENTIFY (1, cmd->lun) : IDENTIFY (0, cmd->lun);
- patch_dsa_32(tmp->dsa, dsa_msgout, 1, virt_to_bus(tmp->select));
- patch_dsa_32(tmp->dsa, dsa_cmdout, 0, cmd->cmd_len);
- patch_dsa_32(tmp->dsa, dsa_cmdout, 1, virt_to_bus(cmd->cmnd));
- patch_dsa_32(tmp->dsa, dsa_dataout, 0, cmd_dataout ?
+ patch_dsa_32(tmp->dsa, dsa_msgout, 1, le32_to_cpu(virt_to_bus(tmp->select)));
+ patch_dsa_32(tmp->dsa, dsa_cmdout, 0, le32_to_cpu(cmd->cmd_len));
+ patch_dsa_32(tmp->dsa, dsa_cmdout, 1, le32_to_cpu(virt_to_bus(cmd->cmnd)));
+ patch_dsa_32(tmp->dsa, dsa_dataout, 0, le32_to_cpu(cmd_dataout ?
virt_to_bus (cmd_dataout)
- : virt_to_bus (hostdata->script) + hostdata->E_other_transfer);
- patch_dsa_32(tmp->dsa, dsa_datain, 0, cmd_datain ?
+ : virt_to_bus (hostdata->script) + hostdata->E_other_transfer));
+ patch_dsa_32(tmp->dsa, dsa_datain, 0, le32_to_cpu(cmd_datain ?
virt_to_bus (cmd_datain)
- : virt_to_bus (hostdata->script) + hostdata->E_other_transfer);
+ : virt_to_bus (hostdata->script) + hostdata->E_other_transfer));
/*
* XXX - need to make endian aware, should use separate variables
* for both status and message bytes.
*/
- patch_dsa_32(tmp->dsa, dsa_msgin, 0, 1);
+ patch_dsa_32(tmp->dsa, dsa_msgin, 0, le32_to_cpu(1));
/*
* FIXME : these only works for little endian. We probably want to
* provide message and status fields in the NCR53c7x0_cmd
* structure, and assign them to cmd->result when we're done.
*/
- patch_dsa_32(tmp->dsa, dsa_msgin, 1, virt_to_bus(&cmd->result) + 1);
- patch_dsa_32(tmp->dsa, dsa_status, 0, 1);
- patch_dsa_32(tmp->dsa, dsa_status, 1, virt_to_bus(&cmd->result));
- patch_dsa_32(tmp->dsa, dsa_msgout_other, 0, 1);
+ patch_dsa_32(tmp->dsa, dsa_msgin, 1, le32_to_cpu(virt_to_bus(&cmd->result) + 1));
+ patch_dsa_32(tmp->dsa, dsa_status, 0, le32_to_cpu(1));
+ patch_dsa_32(tmp->dsa, dsa_status, 1, le32_to_cpu(virt_to_bus(&cmd->result)));
+ patch_dsa_32(tmp->dsa, dsa_msgout_other, 0, le32_to_cpu(1));
patch_dsa_32(tmp->dsa, dsa_msgout_other, 1,
- virt_to_bus(&(hostdata->NCR53c7xx_msg_nop)));
+ le32_to_cpu(virt_to_bus(&(hostdata->NCR53c7xx_msg_nop))));
/*
* Generate code for zero or more of the DATA IN, DATA OUT phases
@@ -3779,15 +3835,15 @@ create_cmd (Scsi_Cmnd *cmd) {
if (datain) {
/* CALL other_in, WHEN NOT DATA_IN */
- cmd_datain[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL |
+ cmd_datain[0] = le32_to_cpu(((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL |
DCMD_TCI_IO) << 24) |
- DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE;
- cmd_datain[1] = virt_to_bus (hostdata->script) +
- hostdata->E_other_in;
+ DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE);
+ cmd_datain[1] = le32_to_cpu(virt_to_bus (hostdata->script) +
+ hostdata->E_other_in);
/* MOVE count, buf, WHEN DATA_IN */
- cmd_datain[2] = ((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I | DCMD_BMI_IO)
- << 24) | count;
- cmd_datain[3] = buf;
+ cmd_datain[2] = le32_to_cpu(((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I | DCMD_BMI_IO)
+ << 24) | count);
+ cmd_datain[3] = le32_to_cpu(buf);
#if 0
print_insn (host, cmd_datain, "dynamic ", 1);
print_insn (host, cmd_datain + 2, "dynamic ", 1);
@@ -3795,14 +3851,14 @@ create_cmd (Scsi_Cmnd *cmd) {
}
if (dataout) {
/* CALL other_out, WHEN NOT DATA_OUT */
- cmd_dataout[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL) << 24) |
- DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE;
- cmd_dataout[1] = virt_to_bus(hostdata->script) +
- hostdata->E_other_out;
+ cmd_dataout[0] = le32_to_cpu(((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL) << 24) |
+ DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE);
+ cmd_dataout[1] = le32_to_cpu(virt_to_bus(hostdata->script) +
+ hostdata->E_other_out);
/* MOVE count, buf, WHEN DATA+OUT */
- cmd_dataout[2] = ((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I) << 24)
- | count;
- cmd_dataout[3] = buf;
+ cmd_dataout[2] = le32_to_cpu(((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I) << 24)
+ | count);
+ cmd_dataout[3] = le32_to_cpu(buf);
#if 0
print_insn (host, cmd_dataout, "dynamic ", 1);
print_insn (host, cmd_dataout + 2, "dynamic ", 1);
@@ -3817,10 +3873,10 @@ create_cmd (Scsi_Cmnd *cmd) {
if (datain) {
- cmd_datain[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP) << 24) |
- DBC_TCI_TRUE;
- cmd_datain[1] = virt_to_bus(hostdata->script) +
- hostdata->E_other_transfer;
+ cmd_datain[0] = le32_to_cpu(((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP) << 24) |
+ DBC_TCI_TRUE);
+ cmd_datain[1] = le32_to_cpu(virt_to_bus(hostdata->script) +
+ hostdata->E_other_transfer);
#if 0
print_insn (host, cmd_datain, "dynamic jump ", 1);
#endif
@@ -3834,10 +3890,10 @@ create_cmd (Scsi_Cmnd *cmd) {
}
#endif
if (dataout) {
- cmd_dataout[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP) << 24) |
- DBC_TCI_TRUE;
- cmd_dataout[1] = virt_to_bus(hostdata->script) +
- hostdata->E_other_transfer;
+ cmd_dataout[0] = le32_to_cpu(((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP) << 24) |
+ DBC_TCI_TRUE);
+ cmd_dataout[1] = le32_to_cpu(virt_to_bus(hostdata->script) +
+ hostdata->E_other_transfer);
#if 0
print_insn (host, cmd_dataout, "dynamic jump ", 1);
#endif
@@ -3894,26 +3950,25 @@ NCR53c7xx_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *)) {
|| hostdata->state == STATE_DISABLED) {
printk("scsi%d : disabled or bad target %d lun %d\n", host->host_no,
cmd->target, cmd->lun);
- cmd->result = (DID_BAD_TARGET << 16);
+ cmd->result = DID_BAD_TARGET << 16;
} else if ((hostdata->options & OPTION_DEBUG_NCOMMANDS_LIMIT) &&
(hostdata->debug_count_limit == 0)) {
printk("scsi%d : maximum commands exceeded\n", host->host_no);
- cmd->result = (DID_BAD_TARGET << 16);
- cmd->result = (DID_BAD_TARGET << 16);
+ cmd->result = DID_BAD_TARGET << 16;
} else if (hostdata->options & OPTION_DEBUG_READ_ONLY) {
switch (cmd->cmnd[0]) {
case WRITE_6:
case WRITE_10:
printk("scsi%d : WRITE attempted with NO_WRITE debugging flag set\n",
host->host_no);
- cmd->result = (DID_BAD_TARGET << 16);
+ cmd->result = DID_BAD_TARGET << 16;
}
} else {
if ((hostdata->options & OPTION_DEBUG_TARGET_LIMIT) &&
hostdata->debug_count_limit != -1)
--hostdata->debug_count_limit;
restore_flags (flags);
- cmd->result = 0xffff; /* The NCR will overwrite message
+ cmd->result = le32_to_cpu(0xffff); /* The NCR will overwrite message
and status with valid data */
cmd->host_scribble = (unsigned char *) tmp = create_cmd (cmd);
}
@@ -3967,7 +4022,7 @@ to_schedule_list (struct Scsi_Host *host, struct NCR53c7x0_hostdata *hostdata,
int i;
NCR53c7x0_local_setup(host);
-#if 0
+#if 0
printk("scsi%d : new dsa is 0x%lx (virt 0x%p)\n", host->host_no,
virt_to_bus(dsa), dsa);
#endif
@@ -3982,7 +4037,7 @@ to_schedule_list (struct Scsi_Host *host, struct NCR53c7x0_hostdata *hostdata,
if (hostdata->state == STATE_DISABLED) {
printk("scsi%d : driver disabled\n", host->host_no);
- tmp->result = (DID_BAD_TARGET << 16);
+ tmp->result = DID_BAD_TARGET << 16;
cmd->next = (struct NCR53c7x0_cmd *) hostdata->free;
hostdata->free = cmd;
tmp->scsi_done(tmp);
@@ -4001,18 +4056,18 @@ to_schedule_list (struct Scsi_Host *host, struct NCR53c7x0_hostdata *hostdata,
/* Restore this instruction to a NOP once the command starts */
cmd->dsa [(hostdata->dsa_jump_dest - hostdata->dsa_start) /
- sizeof(u32)] = (u32) virt_to_bus ((void *)curr);
+ sizeof(u32)] = (u32) le32_to_cpu(virt_to_bus ((void *)curr));
/* Replace the current jump operand. */
curr[1] =
- virt_to_bus ((void *) cmd->dsa) + hostdata->E_dsa_code_begin -
- hostdata->E_dsa_code_template;
+ le32_to_cpu(virt_to_bus ((void *) cmd->dsa) + hostdata->E_dsa_code_begin -
+ hostdata->E_dsa_code_template);
/* Replace the NOP instruction with a JUMP */
- curr[0] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24) |
- DBC_TCI_TRUE;
+ curr[0] = le32_to_cpu(((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24) |
+ DBC_TCI_TRUE);
} else {
printk ("scsi%d: no free slot\n", host->host_no);
disable(host);
- tmp->result = (DID_ERROR << 16);
+ tmp->result = DID_ERROR << 16;
cmd->next = (struct NCR53c7x0_cmd *) hostdata->free;
hostdata->free = cmd;
tmp->scsi_done(tmp);
@@ -4101,7 +4156,7 @@ process_issue_queue (unsigned long flags) {
if (hostdata->state == STATE_DISABLED) {
tmp = (Scsi_Cmnd *) hostdata->issue_queue;
hostdata->issue_queue = (Scsi_Cmnd *) tmp->SCp.ptr;
- tmp->result = (DID_BAD_TARGET << 16);
+ tmp->result = DID_BAD_TARGET << 16;
if (tmp->host_scribble) {
((struct NCR53c7x0_cmd *)tmp->host_scribble)->next =
hostdata->free;
@@ -4133,6 +4188,7 @@ process_issue_queue (unsigned long flags) {
(struct NCR53c7x0_cmd *)
tmp->host_scribble);
} else {
+ tmp->result = le32_to_cpu(tmp->result);
if (((tmp->result & 0xff) == 0xff) ||
((tmp->result & 0xff00) == 0xff00)) {
printk ("scsi%d : danger Will Robinson!\n",
@@ -4370,7 +4426,6 @@ NCR53c7x0_intr (int irq, void *dev_id, struct pt_regs * regs) {
char buf[80]; /* Debugging sprintf buffer */
size_t buflen; /* Length of same */
#endif
-
do {
done = 1;
for (host = first_host; host; host = host->next)
@@ -4449,10 +4504,16 @@ restart:
printk ("scsi%d : looking at result of 0x%x\n",
host->host_no, cmd->cmd->result);
#endif
-
+
+#ifdef __powerpc__
+ if (tmp->result == le32_to_cpu(0xffff))
+ continue;
+ tmp->result = le32_to_cpu(tmp->result);
+#else
if (((tmp->result & 0xff) == 0xff) ||
((tmp->result & 0xff00) == 0xff00))
continue;
+#endif
search_found = 1;
@@ -4537,7 +4598,6 @@ restart:
printk("scsi%d : no active command\n", host->host_no);
}
}
-
if (istat & ISTAT_SIP) {
if (hostdata->options & OPTION_DEBUG_INTR)
printk ("scsi%d : ISTAT_SIP\n", host->host_no);
@@ -4872,12 +4932,12 @@ intr_phase_mismatch (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) {
* from normal dynamic code.
*/
if (dsp != cmd->residual + 2) {
- cmd->residual[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL |
+ cmd->residual[0] = le32_to_cpu(((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL |
((dcmd & DCMD_BMI_IO) ? DCMD_TCI_IO : 0)) << 24) |
- DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE;
- cmd->residual[1] = virt_to_bus(hostdata->script)
+ DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE);
+ cmd->residual[1] = le32_to_cpu(virt_to_bus(hostdata->script)
+ ((dcmd & DCMD_BMI_IO)
- ? hostdata->E_other_in : hostdata->E_other_out);
+ ? hostdata->E_other_in : hostdata->E_other_out));
}
/*
@@ -4885,17 +4945,17 @@ intr_phase_mismatch (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) {
* move instruction, reflecting the pointer and count at the
* time of the phase mismatch.
*/
- cmd->residual[2] = dbc_dcmd + residual;
- cmd->residual[3] = NCR53c7x0_read32(DNAD_REG) - residual;
+ cmd->residual[2] = le32_to_cpu(dbc_dcmd + residual);
+ cmd->residual[3] = le32_to_cpu(NCR53c7x0_read32(DNAD_REG) - residual);
/*
* The third and final instruction is a jump to the instruction
* which follows the instruction which had to be 'split'
*/
if (dsp != cmd->residual + 2) {
- cmd->residual[4] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP)
- << 24) | DBC_TCI_TRUE;
- cmd->residual[5] = virt_to_bus(dsp_next);
+ cmd->residual[4] = le32_to_cpu(((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP)
+ << 24) | DBC_TCI_TRUE);
+ cmd->residual[5] = le32_to_cpu(virt_to_bus(dsp_next));
}
/*
@@ -5375,11 +5435,11 @@ print_insn (struct Scsi_Host *host, const u32 *insn,
*/
sprintf(buf, "%s0x%lx (virt 0x%p) : 0x%08x 0x%08x (virt 0x%p)",
(prefix ? prefix : ""), virt_to_bus((void *) insn), insn,
- insn[0], insn[1], bus_to_virt (insn[1]));
+ insn[0], insn[1], bus_to_virt (le32_to_cpu(insn[1])));
tmp = buf + strlen(buf);
if ((dcmd & DCMD_TYPE_MASK) == DCMD_TYPE_MMI) {
sprintf (tmp, " 0x%08x (virt 0x%p)\n", insn[2],
- bus_to_virt(insn[2]));
+ bus_to_virt(le32_to_cpu(insn[2])));
size = 3;
} else {
sprintf (tmp, "\n");
@@ -5439,6 +5499,7 @@ NCR53c7xx_abort (Scsi_Cmnd *cmd) {
struct NCR53c7x0_hostdata *hostdata = host ? (struct NCR53c7x0_hostdata *)
host->hostdata : NULL;
unsigned long flags;
+ unsigned long result;
struct NCR53c7x0_cmd *curr, **prev;
Scsi_Cmnd *me, **last;
#if 0
@@ -5530,7 +5591,8 @@ NCR53c7xx_abort (Scsi_Cmnd *cmd) {
&(curr->next), curr = (struct NCR53c7x0_cmd *) curr->next);
if (curr) {
- if ((cmd->result & 0xff) != 0xff && (cmd->result & 0xff00) != 0xff00) {
+ result = le32_to_cpu(cmd->result);
+ if ((result & 0xff) != 0xff && (result & 0xff00) != 0xff00) {
if (prev)
*prev = (struct NCR53c7x0_cmd *) curr->next;
curr->next = (struct NCR53c7x0_cmd *) hostdata->free;
@@ -5561,8 +5623,9 @@ NCR53c7xx_abort (Scsi_Cmnd *cmd) {
cmd->host_scribble = NULL;
}
- if (((cmd->result & 0xff00) == 0xff00) ||
- ((cmd->result & 0xff) == 0xff)) {
+ result = le32_to_cpu(cmd->result);
+ if (((result & 0xff00) == 0xff00) ||
+ ((result & 0xff) == 0xff)) {
printk ("scsi%d : did this command ever run?\n", host->host_no);
cmd->result = DID_ABORT << 16;
} else {
@@ -5709,7 +5772,7 @@ insn_to_offset (Scsi_Cmnd *cmd, u32 *insn) {
(insn >= ncmd->residual &&
insn < (ncmd->residual +
sizeof(ncmd->residual))))) {
- ptr = bus_to_virt(insn[3]);
+ ptr = bus_to_virt(le32_to_cpu(insn[3]));
if ((buffers = cmd->use_sg)) {
for (offset = 0,
@@ -5764,7 +5827,7 @@ print_progress (Scsi_Cmnd *cmd) {
continue;
if (!i) {
where = "saved";
- ptr = bus_to_virt(ncmd->saved_data_pointer);
+ ptr = bus_to_virt(le32_to_cpu(ncmd->saved_data_pointer));
} else {
where = "active";
ptr = bus_to_virt (NCR53c7x0_read32 (DSP_REG) -
@@ -5782,9 +5845,9 @@ print_progress (Scsi_Cmnd *cmd) {
cmd->host->host_no, where);
if (ncmd) {
size = print_insn (cmd->host,
- bus_to_virt(ncmd->saved_data_pointer), "", 1);
+ bus_to_virt(le32_to_cpu(ncmd->saved_data_pointer)), "", 1);
print_insn (cmd->host,
- bus_to_virt(ncmd->saved_data_pointer) + size * sizeof(u32),
+ bus_to_virt(le32_to_cpu(ncmd->saved_data_pointer)) + size * sizeof(u32),
"", 1);
}
}
@@ -5809,9 +5872,9 @@ print_dsa (struct Scsi_Host *host, u32 *dsa, const char *prefix) {
" + %d : dsa_msgout length = %u, data = 0x%x (virt 0x%p)\n" ,
prefix ? prefix : "",
host->host_no, virt_to_bus (dsa), dsa, hostdata->dsa_msgout,
- dsa[hostdata->dsa_msgout / sizeof(u32)],
- dsa[hostdata->dsa_msgout / sizeof(u32) + 1],
- bus_to_virt (dsa[hostdata->dsa_msgout / sizeof(u32) + 1]));
+ le32_to_cpu(dsa[hostdata->dsa_msgout / sizeof(u32)]),
+ le32_to_cpu(dsa[hostdata->dsa_msgout / sizeof(u32) + 1]),
+ bus_to_virt (le32_to_cpu(dsa[hostdata->dsa_msgout / sizeof(u32) + 1])));
/*
* Only print messages if they're sane in length so we don't
@@ -5819,10 +5882,10 @@ print_dsa (struct Scsi_Host *host, u32 *dsa, const char *prefix) {
* anything.
*/
- if (dsa[hostdata->dsa_msgout / sizeof(u32)] <
+ if (le32_to_cpu(dsa[hostdata->dsa_msgout / sizeof(u32)]) <
sizeof (hostdata->free->select))
- for (i = dsa[hostdata->dsa_msgout / sizeof(u32)],
- ptr = bus_to_virt (dsa[hostdata->dsa_msgout / sizeof(u32) + 1]);
+ for (i = le32_to_cpu(dsa[hostdata->dsa_msgout / sizeof(u32)]),
+ ptr = bus_to_virt (le32_to_cpu(dsa[hostdata->dsa_msgout / sizeof(u32) + 1]));
i > 0 && !check_address ((unsigned long) ptr, 1);
ptr += len, i -= len) {
printk(" ");
@@ -5833,8 +5896,8 @@ print_dsa (struct Scsi_Host *host, u32 *dsa, const char *prefix) {
}
printk(" + %d : select_indirect = 0x%x\n",
- hostdata->dsa_select, dsa[hostdata->dsa_select / sizeof(u32)]);
- cmd = (Scsi_Cmnd *) bus_to_virt(dsa[hostdata->dsa_cmnd / sizeof(u32)]);
+ hostdata->dsa_select, le32_to_cpu(dsa[hostdata->dsa_select / sizeof(u32)]));
+ cmd = (Scsi_Cmnd *) bus_to_virt(le32_to_cpu(dsa[hostdata->dsa_cmnd / sizeof(u32)]));
printk(" + %d : dsa_cmnd = 0x%x ", hostdata->dsa_cmnd,
(u32) virt_to_bus(cmd));
if (cmd) {
@@ -5844,7 +5907,7 @@ print_dsa (struct Scsi_Host *host, u32 *dsa, const char *prefix) {
} else
printk("\n");
printk(" + %d : dsa_next = 0x%x\n", hostdata->dsa_next,
- dsa[hostdata->dsa_next / sizeof(u32)]);
+ le32_to_cpu(dsa[hostdata->dsa_next / sizeof(u32)]));
if (cmd) {
printk("scsi%d target %d : sxfer_sanity = 0x%x, scntl3_sanity = 0x%x\n"
" script : ",
@@ -5891,8 +5954,7 @@ print_queues (struct Scsi_Host *host) {
host->host_no, cmd->pid);
/* print_dsa does sanity check on address, no need to check */
else
- print_dsa (host, ((struct NCR53c7x0_cmd *) cmd->host_scribble)
- -> dsa, "");
+ print_dsa (host, le32_to_cpu(((struct NCR53c7x0_cmd *) cmd->host_scribble)-> dsa), "");
} else
printk ("scsi%d : scsi pid %ld for target %d lun %d has no NCR53c7x0_cmd\n",
host->host_no, cmd->pid, cmd->target, cmd->lun);
@@ -5917,7 +5979,7 @@ print_queues (struct Scsi_Host *host) {
left > 0; curr += 2, --left)
if (curr[0] != hostdata->NOP_insn)
/* FIXME : convert pointer to dsa_begin to pointer to dsa. */
- print_dsa (host, bus_to_virt (curr[1] -
+ print_dsa (host, bus_to_virt (le32_to_cpu(curr[1]) -
(hostdata->E_dsa_code_begin -
hostdata->E_dsa_code_template)), "");
printk ("scsi%d : end schedule dsa array\n", host->host_no);
@@ -5925,7 +5987,7 @@ print_queues (struct Scsi_Host *host) {
printk ("scsi%d : reconnect_dsa_head :\n", host->host_no);
for (left = host->can_queue,
- dsa = bus_to_virt (hostdata->reconnect_dsa_head);
+ dsa = bus_to_virt (le32_to_cpu(hostdata->reconnect_dsa_head));
left >= 0 && dsa;
dsa = next_dsa) {
save_flags (flags);
@@ -5937,7 +5999,7 @@ print_queues (struct Scsi_Host *host) {
}
else
{
- next_dsa = bus_to_virt(dsa[hostdata->dsa_next / sizeof(u32)]);
+ next_dsa = bus_to_virt(le32_to_cpu(dsa[hostdata->dsa_next / sizeof(u32)]));
print_dsa (host, dsa, "");
}
restore_flags(flags);
@@ -6131,7 +6193,7 @@ return_outstanding_commands (struct Scsi_Host *host, int free, int issue) {
for (i = 0, curr = (u32 *) hostdata->schedule;
i < host->can_queue; ++i, curr += 2) {
curr[0] = hostdata->NOP_insn;
- curr[1] = 0xdeadbeef;
+ curr[1] = le32_to_cpu(0xdeadbeef);
}
hostdata->curr = NULL;
}
diff --git a/drivers/scsi/53c7,8xx.h b/drivers/scsi/53c7,8xx.h
index cfddfa681..cb0071e9d 100644
--- a/drivers/scsi/53c7,8xx.h
+++ b/drivers/scsi/53c7,8xx.h
@@ -1574,7 +1574,7 @@ struct NCR53c7x0_hostdata {
if (hostdata->options & OPTION_DEBUG_DSA) \
printk("scsi : dsa %s symbol %s(%d) word %d now 0x%x\n", \
#dsa, #symbol, hostdata->##symbol, \
- (word), (u32) (value)); \
+ (word), (u32) le32_to_cpu(value)); \
}
/* Paranoid people could use panic() here. */
diff --git a/drivers/scsi/Config.in b/drivers/scsi/Config.in
index ce08d4873..3f24e681f 100644
--- a/drivers/scsi/Config.in
+++ b/drivers/scsi/Config.in
@@ -21,7 +21,14 @@ dep_tristate '7000FASST SCSI support' CONFIG_SCSI_7000FASST $CONFIG_SCSI
dep_tristate 'Adaptec AHA152X/2825 support' CONFIG_SCSI_AHA152X $CONFIG_SCSI
dep_tristate 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 $CONFIG_SCSI
dep_tristate 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 $CONFIG_SCSI
-dep_tristate 'Adaptec AHA274X/284X/294X support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI
+dep_tristate 'Adaptec AIC7xxx support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI
+if [ "$CONFIG_SCSI_AIC7XXX" != "n" ]; then
+ bool ' Enable tagged command queueing' CONFIG_AIC7XXX_TAGGED_QUEUEING Y
+ int ' Maximum number of commands per LUN' CONFIG_AIC7XXX_CMDS_PER_LUN 8
+ bool ' Enable SCB paging' CONFIG_AIC7XXX_PAGE_ENABLE N
+ bool ' Collect statistics to report in /proc' CONFIG_AIC7XXX_PROC_STATS N
+ int ' delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 15
+fi
dep_tristate 'AdvanSys SCSI support' CONFIG_SCSI_ADVANSYS $CONFIG_SCSI
dep_tristate 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 $CONFIG_SCSI
dep_tristate 'AM53/79C974 PCI SCSI support' CONFIG_SCSI_AM53C974 $CONFIG_SCSI
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 02c066202..dcb47e47e 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -429,12 +429,8 @@ BusLogic.o: BusLogic.c FlashPoint.c
aha152x.o: aha152x.c
$(CC) $(CFLAGS) $(AHA152X) -c aha152x.c
-aic7xxx_asm: aic7xxx_asm.c
- $(HOSTCC) -o $@ aic7xxx_asm.c
-
-aic7xxx.c: aic7xxx_seq.h
-aic7xxx_seq.h: aic7xxx_asm aic7xxx.seq
- ./aic7xxx_asm -o $@ aic7xxx.seq
+aic7xxx.o: aic7xxx.c aic7xxx_seq.h aic7xxx_reg.h
+ $(CC) $(CFLAGS) -c -o $@ aic7xxx.c
seagate.o: seagate.c
$(CC) $(CFLAGS) -DARBITRATE -DSLOW_HANDSHAKE -DFAST32 -DPARITY -c seagate.c
diff --git a/drivers/scsi/README.aic7xxx b/drivers/scsi/README.aic7xxx
index ccbf4a6e2..5ec8a51d2 100644
--- a/drivers/scsi/README.aic7xxx
+++ b/drivers/scsi/README.aic7xxx
@@ -1,5 +1,5 @@
AIC7xxx Driver for Linux
- April 15, 1996
+ July 20, 1997
Introduction
------------------------
@@ -31,6 +31,7 @@ linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD
-----------------------
AIC-777x
AIC-785x
+ AIC-786x
AIC-787x
AIC-788x
@@ -58,6 +59,7 @@ linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD
Dan Eischen deischen@iworks.InterWorks.org (Linux Driver Co-maintainer)
Dean Gehnert deang@teleport.com (Linux FTP/patch maintainer)
Jess Johnson jester@frenzy.com (AIC7xxx FAQ author)
+ Doug Ledford dledford@dialnet.net (Stress tester/bug squasher)
Special thanks go to John Aycock (aycock@cpsc.ucalgary.ca), the original
author of the driver. John has since retired from the project. Thanks
@@ -82,10 +84,17 @@ linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD
Send regular messages and replies to: AIC7xxx@FreeBSD.ORG
- Command line options
- ------------------------
+ Command line options ("aic7xxx=option[,option...]")
+ ---------------------------------------------------
"aic7xxx=no_reset" - Eliminate the SCSI reset delay during startup.
Some SCSI devices need some extra time to reset.
+ "aic7xxx=extended" - Force extended translation.
+ "aic7xxx=ultra" - Force Ultra mode
+ "aic7xxx=irq_trigger:[0,1]" - Edge (0) or Level (1) triggered
+ interrupts. AFAIK, the driver only works with level triggered
+ interrupts. This only applies to EISA adapters.
+ "aic7xxx=verbose" - Enable more bootup messages. PLEASE use this
+ if you have problems with the driver.
/proc support
------------------------
@@ -106,10 +115,37 @@ linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD
- US Linux mirror of Teleport site
ftp://ekf2.vsb.cz/pub/linux/kernel/aic7xxx/ftp.teleport.com/
- European Linux mirror of Teleport site
+ ftp://ftp.pcnet.com/users/eischen/Linux/
+ - Daniel Eischens experimental/development ftp site that is
+ also home of the Linux aic7xxx sequencer assembler source.
+
+ Sequencer assembler
+ ------------------------
+ The sequencer assembler is no longer being distributed with the
+ Linux kernel. The sequencer assembler (aic7xxx_asm) is now being
+ maintained by Justin Gibbs under a BSD copyright (which pretty
+ much lets you do anything you want with it). I keep a Linux
+ version of the assembler at my ftp site should you wish to hack
+ the sequencer code (ftp://ftp.pcnet.com/users/eischen/Linux/).
+ Please note that you do NOT need the assembler to build a kernel
+ with aic7xxx support. The assembler generates the code that is
+ downloaded to the aic7xxx controllers; this code IS part of the
+ Linux kernel (aic7xxx_seq.h and aic7xxx_reg.h).
+
+ Problems compiling the kernel with aic7xxx support
+ --------------------------------------------------
+ This is probably due to having modified the sequencer files in
+ some way. If you are not modifying the sequencer source (in
+ drivers/scsi/aic7xxx/aic7xxx.seq), then you can just re-extract
+ the necessary files from your kernel tarball. Otherwise, visit
+ my anonymous ftp site (ftp.pcnet.com) and grab the sequencer
+ assembler source.
Dean W. Gehnert
deang@teleport.com
-$Revision: 3.0 $
+(Modified by D. Eischen, 7/20/97)
+
+$Revision: 3.1a $
diff --git a/drivers/scsi/aic7xxx.c b/drivers/scsi/aic7xxx.c
index 96e66defd..005c889d7 100644
--- a/drivers/scsi/aic7xxx.c
+++ b/drivers/scsi/aic7xxx.c
@@ -1,5 +1,3 @@
-#define EXPERIMENTAL_FLAGS 0
-
/*+M*************************************************************************
* Adaptec AIC7xxx device driver for Linux.
*
@@ -29,21 +27,73 @@
* the Adaptec AIC-7770 Data Book, the ANSI SCSI specification, the
* ANSI SCSI-2 specification (draft 10c), ...
*
- * ----------------------------------------------------------------
- * Modified to include support for wide and twin bus adapters,
- * DMAing of SCBs, tagged queueing, IRQ sharing, bug fixes,
+ * --------------------------------------------------------------------------
+ *
+ * Modifications by Daniel M. Eischen (deischen@iworks.InterWorks.org):
+ *
+ * Substantially modified to include support for wide and twin bus
+ * adapters, DMAing of SCBs, tagged queueing, IRQ sharing, bug fixes,
* SCB paging, and other rework of the code.
*
- * Parts of this driver are based on the FreeBSD driver by Justin
- * T. Gibbs.
+ * Parts of this driver were also based on the FreeBSD driver by
+ * Justin T. Gibbs. His copyright follows:
+ *
+ * --------------------------------------------------------------------------
+ * Copyright (c) 1994-1997 Justin Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of
+ * the GNU Public License ("GPL") and the terms of the GPL would require the
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: aic7xxx.c,v 1.119 1997/06/27 19:39:18 gibbs Exp $
+ *---------------------------------------------------------------------------
+ *
+ * Thanks also go to (in alphabetical order) the following:
+ *
+ * Rory Bolt - Sequencer bug fixes
+ * Jay Estabrook - Initial DEC Alpha support
+ * Doug Ledford - Much needed abort/reset bug fixes
+ * Kai Makisara - DMAing of SCBs
*
* A Boot time option was also added for not resetting the scsi bus.
*
- * Form: aic7xxx=extended,no_reset
+ * Form: aic7xxx=extended
+ * aic7xxx=no_reset
+ * aic7xxx=ultra
+ * aic7xxx=irq_trigger:[0,1] # 0 edge, 1 level
+ * aic7xxx=verbose
*
- * -- Daniel M. Eischen, deischen@iworks.InterWorks.org, 07/07/96
+ * Daniel M. Eischen, deischen@iworks.InterWorks.org, 1/23/97
*
- * $Id: aic7xxx.c,v 4.0 1996/10/13 08:23:42 deang Exp $
+ * $Id: aic7xxx.c,v 4.1 1997/06/12 08:23:42 deang Exp $
*-M*************************************************************************/
#ifdef MODULE
@@ -67,7 +117,11 @@
#include "scsi.h"
#include "hosts.h"
#include "aic7xxx.h"
+
+#include "aic7xxx/sequencer.h"
+#include "aic7xxx/scsi_message.h"
#include "aic7xxx_reg.h"
+#include "aic7xxx_seq.h"
#include <linux/stat.h>
#include <linux/malloc.h> /* for kmalloc() */
@@ -79,16 +133,20 @@
*/
#define VIRT_TO_BUS(a) (unsigned int)virt_to_bus((void *)(a))
-static struct proc_dir_entry proc_scsi_aic7xxx = {
+struct proc_dir_entry proc_scsi_aic7xxx = {
PROC_SCSI_AIC7XXX, 7, "aic7xxx",
- S_IFDIR | S_IRUGO | S_IXUGO, 2
+ S_IFDIR | S_IRUGO | S_IXUGO, 2,
+ 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};
-#define AIC7XXX_C_VERSION "$Revision: 4.0 $"
+#define AIC7XXX_C_VERSION "$Revision: 4.1 $"
#define NUMBER(arr) (sizeof(arr) / sizeof(arr[0]))
-#define MIN(a,b) ((a < b) ? a : b)
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
#define ALL_TARGETS -1
+#define ALL_CHANNELS '\0'
+#define ALL_LUNS -1
#ifndef TRUE
# define TRUE 1
#endif
@@ -107,11 +165,8 @@ static struct proc_dir_entry proc_scsi_aic7xxx = {
* support because all PCI dependent code is bracketed with
* "#ifdef CONFIG_PCI ... #endif CONFIG_PCI".
*
- * o Twin bus support - this has been tested and does work.
- *
- * o DMAing of SCBs - thanks to Kai Makisara, this now works.
- * This define is now taken out and DMAing of SCBs is always
- * performed (8/12/95 - DE).
+ * o Twin bus support - this has been tested and does work. It is
+ * not an option anymore.
*
* o Tagged queueing - this driver is capable of tagged queueing
* but I am unsure as to how well the higher level driver implements
@@ -140,16 +195,16 @@ static struct proc_dir_entry proc_scsi_aic7xxx = {
* LUN using its own heuristic based on the number of available
* SCBs.
*
- * o 3985 support - The 3985 adapter is much like the 3940, but
- * has three 7870 controllers as opposed to two for the 3940.
- * It will get probed and recognized as three different adapters,
- * but all three controllers can share the same external bank of
- * 255 SCBs. If you enable AIC7XXX_SHARE_SCBS, then the driver
- * will attempt to share the common bank of SCBs between the three
- * controllers of the 3985. This is experimental and hasn't
- * been tested. By default, we do not share the bank of SCBs,
- * and force the controllers to use their own internal bank of
- * 16 SCBs. Please let us know if sharing the SCB array works.
+ * o 3985 support - The 3985 adapter is much like the 3940, but has
+ * three 7870 controllers as opposed to two for the 3940. It will
+ * be probed and recognized as three different adapters, but all
+ * three controllers can share the same external bank of 255 SCBs.
+ * If you enable AIC7XXX_USE_EXT_SCBRAM, then the driver will attempt
+ * to use and share the common bank of SCBs between the three
+ * controllers of the 3985. This is experimental and hasn't been
+ * been tested. By default, we do not use external SCB RAM, and
+ * force the controllers to use their own internal bank of 16 SCBs.
+ * Please let us know if using the external SCB array works.
*
* o SCB paging support - SCB paging is enabled by defining
* AIC7XXX_PAGE_ENABLE. Support for this was taken from the
@@ -162,43 +217,54 @@ static struct proc_dir_entry proc_scsi_aic7xxx = {
* Note that sharing of IRQs is not an option any longer. Linux supports
* it so we support it.
*
- * Daniel M. Eischen, deischen@iworks.InterWorks.org, 06/30/96
+ * Daniel M. Eischen, deischen@iworks.InterWorks.org, 01/26/96
*/
-/* Uncomment this for testing twin bus support. */
-#define AIC7XXX_TWIN_SUPPORT
-
/* Uncomment this for tagged queueing. */
-/* #define AIC7XXX_TAGGED_QUEUEING */
+#ifdef CONFIG_AIC7XXX_TAGGED_QUEUEING
+#define AIC7XXX_TAGGED_QUEUEING
+#endif
/*
* You can try raising me if tagged queueing is enabled, or lowering
* me if you only have 4 SCBs.
*/
-/* #define AIC7XXX_CMDS_PER_LUN 8 */
+#ifdef CONFIG_AIC7XXX_CMDS_PER_LUN
+#define AIC7XXX_CMDS_PER_LUN CONFIG_AIC7XXX_CMDS_PER_LUN
+#endif
/* Set this to the delay in seconds after SCSI bus reset. */
+#ifdef CONFIG_AIC7XXX_RESET_DELAY
+#define AIC7XXX_RESET_DELAY CONFIG_AIC7XXX_RESET_DELAY
+#else
#define AIC7XXX_RESET_DELAY 15
+#endif
/*
- * Uncomment the following define for collection of SCSI transfer statistics
- * for the /proc filesystem.
+ * Control collection of SCSI transfer statistics for the /proc filesystem.
*
* NOTE: Do NOT enable this when running on kernels version 1.2.x and below.
* NOTE: This does affect performance since it has to maintain statistics.
*/
-/* #define AIC7XXX_PROC_STATS */
+#ifdef CONFIG_AIC7XXX_PROC_STATS
+#define AIC7XXX_PROC_STATS
+#endif
/*
- * Uncomment the following to enable SCB paging.
+ * Enable SCB paging.
*/
-/* #define AIC7XXX_PAGE_ENABLE */
+#ifdef CONFIG_AIC7XXX_PAGE_ENABLE
+#define AIC7XXX_PAGE_ENABLE
+#endif
/*
- * Uncomment the following to enable sharing of the external bank
- * of 255 SCBs for the 3985.
+ * Uncomment the following to enable use of the external bank
+ * of 255 SCBs. For 3985 adapters, this will also enable sharing
+ * of the SCB array across all three controllers.
*/
-#define AIC7XXX_SHARE_SCBS
+#ifdef CONFIG_AIC7XXX_USE_EXT_SCBRAM
+#define AIC7XXX_USE_EXT_SCBRAM
+#endif
/*
* For debugging the abort/reset code.
@@ -211,6 +277,87 @@ static struct proc_dir_entry proc_scsi_aic7xxx = {
#define AIC7XXX_DEBUG
/*
+ * Set this for defining the number of tagged commands on a device
+ * by device, and controller by controller basis. The first set
+ * of tagged commands will be used for the first detected aic7xxx
+ * controller, the second set will be used for the second detected
+ * aic7xxx controller, and so on. These values will *only* be used
+ * for targets that are tagged queueing capable; these values will
+ * be ignored in all other cases. The tag_commands is an array of
+ * 16 to allow for wide and twin adapters. Twin adapters will use
+ * indexes 0-7 for channel 0, and indexes 8-15 for channel 1.
+ *
+ * *** Determining commands per LUN ***
+ *
+ * When AIC7XXX_CMDS_PER_LUN is not defined, the driver will use its
+ * own algorithm to determine the commands/LUN. If SCB paging is
+ * enabled, the commands/LUN is 8. When SCB paging is not enabled,
+ * then commands/LUN is 8 for adapters with 16 or more hardware SCBs
+ * and 4 commands/LUN for adapters with 3 or 4 SCBs.
+ *
+ */
+/* #define AIC7XXX_TAGGED_QUEUEING_BY_DEVICE */
+
+#ifdef AIC7XXX_TAGGED_QUEUEING_BY_DEVICE
+typedef struct
+{
+ char tag_commands[16]; /* Allow for wide/twin channel adapters. */
+} adapter_tag_info_t;
+
+/*
+ * Make a define that will tell the driver to use it's own algorithm
+ * for determining commands/LUN (see Determining commands per LUN
+ * above).
+ */
+#define DEFAULT_TAG_COMMANDS {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+
+/*
+ * Modify this as you see fit for your system. By setting tag_commands
+ * to 0, the driver will use it's own algorithm for determining the
+ * number of commands to use (see above). When -1, the driver will
+ * not enable tagged queueing for that particular device. When positive
+ * (> 0) the values in the array are used for the queue_depth. Note
+ * that the maximum value for an entry is 127.
+ *
+ * In this example, the first line will enable tagged queueing for all
+ * the devices on the first probed aic7xxx adapter and tells the driver
+ * to use it's own algorithm for determining commands/LUN.
+ *
+ * The second line enables tagged queueing with 4 commands/LUN for IDs
+ * (1, 2-11, 13-15), disables tagged queueing for ID 12, and tells the
+ * driver to use its own algorithm for ID 1.
+ *
+ * The third line is the same as the first line.
+ *
+ * The fourth line disables tagged queueing for devices 0 and 3. It
+ * enables tagged queueing for the other IDs, with 16 commands/LUN
+ * for IDs 1 and 4, 127 commands/LUN for ID 8, and 4 commands/LUN for
+ * IDs 2, 5-7, and 9-15.
+ */
+adapter_tag_info_t aic7xxx_tag_info[] =
+{
+ {DEFAULT_TAG_COMMANDS},
+ {{4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, -1, 4, 4, 4}},
+ {DEFAULT_TAG_COMMANDS},
+ {{-1, 16, 4, -1, 16, 4, 4, 4, 127, 4, 4, 4, 4, 4, 4, 4}}
+};
+#endif
+
+/*
+ * Don't define this unless you have problems with the driver
+ * interrupt handler. The old method would register the drivers
+ * interrupt handler as a "fast" type interrupt handler that would
+ * lock out other interrupts. Since this driver can spend a lot
+ * of time in the interrupt handler, this is _not_ a good idea.
+ * It also conflicts with some of the more common ethernet drivers
+ * that don't use fast interrupts. Currently, Linux does not allow
+ * IRQ sharing unless both drivers can agree on the type of interrupt
+ * handler.
+ */
+/* #define AIC7XXX_OLD_ISR_TYPE */
+
+
+/*
* Controller type and options
*/
typedef enum {
@@ -232,14 +379,15 @@ typedef enum {
AIC_7882, /* PCI aic7882 on 3940 Ultra */
AIC_7883, /* PCI aic7883 on 3985 Ultra */
AIC_7884 /* PCI aic7884 on 294x Ultra Differential */
-} aha_type;
+} aha_chip_type;
typedef enum {
AIC_777x, /* AIC-7770 based */
- AIC_785x, /* AIC-7850 based */
+ AIC_785x, /* AIC-7850 based (3 SCBs)*/
+ AIC_786x, /* AIC-7860 based (7850 ultra) */
AIC_787x, /* AIC-7870 based */
- AIC_788x /* AIC-7880 based */
-} aha_chip_type;
+ AIC_788x /* AIC-7880 based (ultra) */
+} aha_chip_class_type;
typedef enum {
AIC_SINGLE, /* Single Channel */
@@ -269,24 +417,24 @@ typedef enum {
* Don't forget to change this when changing the types!
*/
static const char *board_names[] = {
- "<AIC-7xxx Unknown>", /* AIC_NONE */
- "AIC-7770", /* AIC_7770 */
- "AHA-2740", /* AIC_7771 */
- "AHA-2840", /* AIC_284x */
- "AIC-7850", /* AIC_7850 */
- "AIC-7855", /* AIC_7855 */
- "AIC-7850 Ultra", /* AIC_7860 */
- "AHA-2940A Ultra", /* AIC_7861 */
- "AIC-7870", /* AIC_7870 */
- "AHA-2940", /* AIC_7871 */
- "AHA-3940", /* AIC_7872 */
- "AHA-3985", /* AIC_7873 */
- "AHA-2940 Differential", /* AIC_7874 */
- "AIC-7880 Ultra", /* AIC_7880 */
- "AHA-2940 Ultra", /* AIC_7881 */
- "AHA-3940 Ultra", /* AIC_7882 */
- "AHA-3985 Ultra", /* AIC_7883 */
- "AHA-2940 Ultra Differential" /* AIC_7884 */
+ "AIC-7xxx Unknown", /* AIC_NONE */
+ "Adaptec AIC-7770 SCSI host adapter", /* AIC_7770 */
+ "Adaptec AHA-274X SCSI host adapter", /* AIC_7771 */
+ "Adaptec AHA-284X SCSI host adapter", /* AIC_284x */
+ "Adaptec AIC-7850 SCSI host adapter", /* AIC_7850 */
+ "Adaptec AIC-7855 SCSI host adapter", /* AIC_7855 */
+ "Adaptec AIC-7860 Ultra SCSI host adapter", /* AIC_7860 */
+ "Adaptec AHA-2940A Ultra SCSI host adapter", /* AIC_7861 */
+ "Adaptec AIC-7870 SCSI host adapter", /* AIC_7870 */
+ "Adaptec AHA-294X SCSI host adapter", /* AIC_7871 */
+ "Adaptec AHA-394X SCSI host adapter", /* AIC_7872 */
+ "Adaptec AHA-398X SCSI host adapter", /* AIC_7873 */
+ "Adaptec AHA-2944 SCSI host adapter", /* AIC_7874 */
+ "Adaptec AIC-7880 Ultra SCSI host adapter", /* AIC_7880 */
+ "Adaptec AHA-294X Ultra SCSI host adapter", /* AIC_7881 */
+ "Adaptec AHA-394X Ultra SCSI host adapter", /* AIC_7882 */
+ "Adaptec AHA-398X Ultra SCSI host adapter", /* AIC_7883 */
+ "Adaptec AHA-2944 Ultra SCSI host adapter" /* AIC_7884 */
};
/*
@@ -310,12 +458,17 @@ static const char *board_names[] = {
*/
#define DID_RETRY_COMMAND DID_ERROR
+#define HSCSIID 0x07
+#define HWSCSIID 0x0F
+#define SCSI_RESET 0x040
+
/*
* EISA/VL-bus stuff
*/
#define MINSLOT 1
#define MAXSLOT 15
#define SLOTBASE(x) ((x) << 12)
+#define BASE_TO_SLOT(x) ((x) >> 12)
/*
* Standard EISA Host ID regs (Offset from slot base)
@@ -334,14 +487,6 @@ static const char *board_names[] = {
#define INTDEF 0x5C /* Interrupt Definition Register */
/*
- * Some defines for the HCNTRL register.
- */
-#define REQ_PAUSE IRQMS | INTEN | PAUSE
-#define UNPAUSE_274X IRQMS | INTEN
-#define UNPAUSE_284X INTEN
-#define UNPAUSE_294X IRQMS | INTEN
-
-/*
* AIC-78X0 PCI registers
*/
#define CLASS_PROGIF_REVID 0x08
@@ -377,7 +522,7 @@ static const char *board_names[] = {
* each word, while the C56 and C66 (4096 bits) use 8 bits to
* address each word.
*/
-typedef enum {c46 = 6, c56_66 = 8} seeprom_chip_type;
+typedef enum {C46 = 6, C56_66 = 8} seeprom_chip_type;
/*
*
@@ -417,15 +562,15 @@ struct seeprom_config {
/*
* Host Adapter Control Bits
*/
-/* UNUSED 0x0001 */
+#define CFAUTOTERM 0x0001 /* Perform Auto termination */
#define CFULTRAEN 0x0002 /* Ultra SCSI speed enable (Ultra cards) */
#define CF284XSELTO 0x0003 /* Selection timeout (284x cards) */
#define CF284XFIFO 0x000C /* FIFO Threshold (284x cards) */
-#define CFSTERM 0x0004 /* SCSI low byte termination (non-wide cards) */
+#define CFSTERM 0x0004 /* SCSI low byte termination */
#define CFWSTERM 0x0008 /* SCSI high byte termination (wide card) */
#define CFSPARITY 0x0010 /* SCSI parity */
#define CF284XSTERM 0x0020 /* SCSI low byte termination (284x cards) */
-#define CFRESETB 0x0040 /* reset SCSI bus at IC initialization */
+#define CFRESETB 0x0040 /* reset SCSI bus at boot */
/* UNUSED 0xFF80 */
unsigned short adapter_control; /* word 17 */
@@ -448,36 +593,17 @@ struct seeprom_config {
unsigned short checksum; /* word 31 */
};
+#define SELBUS_MASK 0x0a
+#define SELNARROW 0x00
+#define SELBUSB 0x08
+#define SINGLE_BUS 0x00
-#define SCSI_RESET 0x040
-
-/*
- * Pause the sequencer and wait for it to actually stop - this
- * is important since the sequencer can disable pausing for critical
- * sections.
- */
-#define PAUSE_SEQUENCER(p) \
- synchronize_irq(); \
- outb(p->pause, HCNTRL + p->base); \
- while ((inb(HCNTRL + p->base) & PAUSE) == 0) \
- ; \
-
-/*
- * Unpause the sequencer. Unremarkable, yet done often enough to
- * warrant an easy way to do it.
- */
-#define UNPAUSE_SEQUENCER(p) \
- outb(p->unpause, HCNTRL + p->base)
-
-/*
- * Restart the sequencer program from address zero
- */
-#define RESTART_SEQUENCER(p) \
- do { \
- outb(SEQRESET | FASTMODE, SEQCTL + p->base); \
- } while (inb(SEQADDR0 + p->base) != 0 && \
- inb(SEQADDR1 + p->base) != 0); \
- UNPAUSE_SEQUENCER(p);
+#define SCB_TARGET(scb) \
+ (((scb)->hscb->target_channel_lun & TID) >> 4)
+#define SCB_LUN(scb) \
+ ((scb)->hscb->target_channel_lun & LID)
+#define SCB_IS_SCSIBUS_B(scb) \
+ (((scb)->hscb->target_channel_lun & SELBUSB) != 0)
/*
* If an error occurs during a data transfer phase, run the command
@@ -541,12 +667,6 @@ static struct Scsi_Host *aic7xxx_boards[NR_IRQS + 1];
static int aic7xxx_spurious_count;
/*
- * The driver keeps up to four scb structures per card in memory. Only the
- * first 25 bytes of the structure are valid for the hardware, the rest used
- * for driver level bookkeeping.
- */
-
-/*
* As of Linux 2.1, the mid-level SCSI code uses virtual addresses
* in the scatter-gather lists. We need to convert the virtual
* addresses to physical addresses.
@@ -559,20 +679,28 @@ struct hw_scatterlist {
/*
* Maximum number of SG segments these cards can support.
*/
-#define MAX_SG 256
+#define AIC7XXX_MAX_SG 27
-struct aic7xxx_scb {
+/*
+ * The maximum number of SCBs we could have for ANY type
+ * of card. DON'T FORGET TO CHANGE THE SCB MASK IN THE
+ * SEQUENCER CODE IF THIS IS MODIFIED!
+ */
+#define AIC7XXX_MAXSCB 255
+
+
+struct aic7xxx_hwscb {
/* ------------ Begin hardware supported fields ---------------- */
/* 0*/ unsigned char control;
/* 1*/ unsigned char target_channel_lun; /* 4/1/3 bits */
/* 2*/ unsigned char target_status;
/* 3*/ unsigned char SG_segment_count;
-/* 4*/ unsigned char SG_list_pointer[4] __attribute__ ((packed));
+/* 4*/ unsigned int SG_list_pointer;
/* 8*/ unsigned char residual_SG_segment_count;
-/* 9*/ unsigned char residual_data_count[3] __attribute__ ((packed));
-/*12*/ unsigned char data_pointer[4] __attribute__ ((packed));
-/*16*/ unsigned int data_count __attribute__ ((packed)); /* must be 32 bits */
-/*20*/ unsigned char SCSI_cmd_pointer[4] __attribute__ ((packed));
+/* 9*/ unsigned char residual_data_count[3];
+/*12*/ unsigned int data_pointer;
+/*16*/ unsigned int data_count;
+/*20*/ unsigned int SCSI_cmd_pointer;
/*24*/ unsigned char SCSI_cmd_length;
/*25*/ u_char tag; /* Index into our kernel SCB array.
* Also used as the tag for tagged I/O
@@ -580,29 +708,47 @@ struct aic7xxx_scb {
#define SCB_PIO_TRANSFER_SIZE 26 /* amount we need to upload/download
* via PIO to initialize a transaction.
*/
-/*26*/ u_char next; /* Used to thread SCBs awaiting selection
+/*26*/ unsigned char next; /* Used to thread SCBs awaiting selection
* or disconnected down in the sequencer.
*/
- /*-----------------end of hardware supported fields----------------*/
- Scsi_Cmnd *cmd; /* Scsi_Cmnd for this scb */
- struct aic7xxx_scb *q_next; /* next scb in queue */
-#define SCB_FREE 0x00
-#define SCB_ACTIVE 0x01
-#define SCB_ABORTED 0x02
-#define SCB_DEVICE_RESET 0x04
-#define SCB_IMMED 0x08
-#define SCB_SENSE 0x10
-#define SCB_QUEUED_FOR_DONE 0x40
-#define SCB_PAGED_OUT 0x80
-#define SCB_WAITINGQ 0x100
-#define SCB_ASSIGNEDQ 0x200
-#define SCB_SENTORDEREDTAG 0x400
-#define SCB_IN_PROGRESS (SCB_ACTIVE | SCB_PAGED_OUT | \
- SCB_WAITINGQ | SCB_ASSIGNEDQ)
- int state; /* current state of scb */
- unsigned int position; /* Position in scb array */
- struct hw_scatterlist sg_list[MAX_SG]; /* SG list in adapter format */
- unsigned char sense_cmd[6]; /* Allocate 6 characters for sense command */
+/*27*/ unsigned char prev;
+/*28*/ unsigned int pad; /*
+ * Unused by the kernel, but we require
+ * the padding so that the array of
+ * hardware SCBs is alligned on 32 byte
+ * boundaries so the sequencer can index
+ */
+};
+
+typedef enum {
+ SCB_FREE = 0x0000,
+ SCB_ACTIVE = 0x0001,
+ SCB_ABORTED = 0x0002,
+ SCB_DEVICE_RESET = 0x0004,
+ SCB_SENSE = 0x0008,
+ SCB_TIMEDOUT = 0x0010,
+ SCB_QUEUED_FOR_DONE = 0x0020,
+ SCB_RECOVERY_SCB = 0x0040,
+ SCB_WAITINGQ = 0x0080,
+ SCB_ASSIGNEDQ = 0x0100,
+ SCB_SENTORDEREDTAG = 0x0200,
+ SCB_MSGOUT_SDTR = 0x0400,
+ SCB_MSGOUT_WDTR = 0x0800,
+ SCB_ABORT = 0x1000,
+ SCB_QUEUED_ABORT = 0x2000
+} scb_flag_type;
+
+struct aic7xxx_scb {
+ struct aic7xxx_hwscb *hscb; /* corresponding hardware scb */
+ Scsi_Cmnd *cmd; /* Scsi_Cmnd for this scb */
+ struct aic7xxx_scb *q_next; /* next scb in queue */
+ scb_flag_type flags; /* current state of scb */
+ struct hw_scatterlist *sg_list; /* SG list in adapter format */
+ unsigned char sg_count;
+ unsigned char sense_cmd[6]; /*
+ * Allocate 6 characters for
+ * sense command.
+ */
};
/*
@@ -627,20 +773,17 @@ static unsigned char
generic_sense[] = { REQUEST_SENSE, 0, 0, 0, 255, 0 };
typedef struct {
+ struct aic7xxx_hwscb *hscbs;
scb_queue_type free_scbs; /*
* SCBs assigned to free slot on
* card (no paging required)
*/
- int numscbs; /* current number of scbs */
- int activescbs; /* active scbs */
-} scb_usage_type;
-
-/*
- * The maximum number of SCBs we could have for ANY type
- * of card. DON'T FORGET TO CHANGE THE SCB MASK IN THE
- * SEQUENCER CODE IF THIS IS MODIFIED!
- */
-#define AIC7XXX_MAXSCB 255
+ unsigned char numscbs; /* current number of scbs */
+ unsigned char maxhscbs; /* hardware scbs */
+ unsigned char maxscbs; /* max scbs including pageable scbs */
+ struct aic7xxx_scb *scb_array[AIC7XXX_MAXSCB];
+ unsigned int reserve[100];
+} scb_data_type;
/*
* Define a structure used for each host adapter, only one per IRQ.
@@ -648,17 +791,26 @@ typedef struct {
struct aic7xxx_host {
struct Scsi_Host *host; /* pointer to scsi host */
int host_no; /* SCSI host number */
+ int instance; /* aic7xxx instance number */
+ int scsi_id; /* host adapter SCSI ID */
+ int scsi_id_b; /* channel B for twin adapters */
+ int irq; /* IRQ for this adapter */
int base; /* card base address */
- int maxhscbs; /* hardware SCBs */
- int maxscbs; /* max SCBs (including pageable) */
-#define A_SCANNED 0x0001
-#define B_SCANNED 0x0002
-#define EXTENDED_TRANSLATION 0x0004
-#define HAVE_SEEPROM 0x0008
-#define ULTRA_ENABLED 0x0010
-#define PAGE_ENABLED 0x0020
-#define IN_ISR 0x0040
-#define USE_DEFAULTS 0x0080
+ unsigned int mbase; /* I/O memory address */
+ volatile unsigned char *maddr; /* memory mapped address */
+#define A_SCANNED 0x0001
+#define B_SCANNED 0x0002
+#define EXTENDED_TRANSLATION 0x0004
+#define FLAGS_CHANNEL_B_PRIMARY 0x0008
+#define MULTI_CHANNEL 0x0010
+#define ULTRA_ENABLED 0x0020
+#define PAGE_ENABLED 0x0040
+#define USE_DEFAULTS 0x0080
+#define BIOS_ENABLED 0x0100
+#define IN_ISR 0x0200
+#define IN_TIMEOUT 0x0400
+#define SHARED_SCBDATA 0x0800
+#define HAVE_SEEPROM 0x1000
unsigned int flags;
unsigned int isr_count; /* Interrupt count */
unsigned short needsdtr_copy; /* default config */
@@ -669,36 +821,22 @@ struct aic7xxx_host {
unsigned short wdtr_pending;
unsigned short orderedtag;
unsigned short discenable; /* Targets allowed to disconnect */
- aha_type type; /* card type */
- aha_chip_type chip_type; /* chip base type */
+ aha_chip_type chip_type; /* card type */
+ aha_chip_class_type chip_class;
aha_bus_type bus_type; /* normal/twin/wide bus */
- char * mbase; /* I/O memory address */
- unsigned char chan_num; /* for 3940/3985, channel number */
+ unsigned char chan_num; /* for 39xx, channel number */
unsigned char unpause; /* unpause value for HCNTRL */
unsigned char pause; /* pause value for HCNTRL */
unsigned char qcntmask;
- struct seeprom_config seeprom;
+ unsigned char qfullcount;
+ unsigned char curqincnt;
struct Scsi_Host *next; /* allow for multiple IRQs */
- struct aic7xxx_scb *scb_array[AIC7XXX_MAXSCB]; /* active commands */
- struct aic7xxx_scb *pagedout_ntscbs[16]; /*
- * paged-out, non-tagged scbs
- * indexed by target.
- */
- scb_queue_type page_scbs; /*
- * SCBs that will require paging
- * before use (no assigned slot)
- */
+ unsigned char activescbs; /* active scbs */
scb_queue_type waiting_scbs; /*
- * SCBs waiting to be paged and
- * started.
+ * SCBs waiting for space in
+ * the QINFIFO.
*/
- scb_queue_type assigned_scbs; /*
- * SCBs that were waiting but have
- * have now been assigned a slot
- * by aic7xxx_free_scb
- */
- scb_usage_type scb_usage;
- scb_usage_type *scb_link;
+ scb_data_type *scb_data;
struct aic7xxx_cmd_queue {
Scsi_Cmnd *head;
@@ -710,6 +848,7 @@ struct aic7xxx_host {
#define BUS_DEVICE_RESET_PENDING 0x02
int flags;
int commands_sent;
+ int active_cmds;
} device_status[16];
#ifdef AIC7XXX_PROC_STATS
/*
@@ -735,34 +874,10 @@ struct aic7xxx_host {
#endif /* AIC7XXX_PROC_STATS */
};
-struct aic7xxx_host_config {
- int irq; /* IRQ number */
- int mbase; /* memory base address*/
- int base; /* I/O base address*/
- int maxhscbs; /* hardware SCBs */
- int maxscbs; /* max SCBs (including pageable) */
- int unpause; /* unpause value for HCNTRL */
- int pause; /* pause value for HCNTRL */
- int scsi_id; /* host SCSI ID */
- int scsi_id_b; /* host SCSI ID B channel for twin cards */
- unsigned int flags; /* used the same as struct aic7xxx_host flags */
- int chan_num; /* for 3940/3985, channel number */
- unsigned char busrtime; /* bus release time */
- unsigned char bus_speed; /* bus speed */
- unsigned char qcntmask;
- aha_type type; /* card type */
- aha_chip_type chip_type; /* chip base type */
- aha_bus_type bus_type; /* normal/twin/wide bus */
- aha_status_type bios; /* BIOS is enabled/disabled */
- aha_status_type parity; /* bus parity enabled/disabled */
- aha_status_type low_term; /* bus termination low byte */
- aha_status_type high_term; /* bus termination high byte (wide cards only) */
-};
-
/*
* Valid SCSIRATE values. (p. 3-17)
- * Provides a mapping of transfer periods in ns to the proper value to
- * stick in the scsiscfr reg to use that transfer rate.
+ * Provides a mapping of transfer periods in ns/4 to the proper value to
+ * stick in the SCSIRATE reg to use that transfer rate.
*/
static struct {
short period;
@@ -771,17 +886,17 @@ static struct {
short rate;
const char *english;
} aic7xxx_syncrates[] = {
- { 50, 0x100, "20.0" },
- { 62, 0x110, "16.0" },
- { 75, 0x120, "13.4" },
- { 100, 0x000, "10.0" },
- { 125, 0x010, "8.0" },
- { 150, 0x020, "6.67" },
- { 175, 0x030, "5.7" },
- { 200, 0x040, "5.0" },
- { 225, 0x050, "4.4" },
- { 250, 0x060, "4.0" },
- { 275, 0x070, "3.6" }
+ { 12, 0x100, "20.0" },
+ { 15, 0x110, "16.0" },
+ { 18, 0x120, "13.4" },
+ { 25, 0x000, "10.0" },
+ { 31, 0x010, "8.0" },
+ { 37, 0x020, "6.67" },
+ { 43, 0x030, "5.7" },
+ { 50, 0x040, "5.0" },
+ { 56, 0x050, "4.4" },
+ { 62, 0x060, "4.0" },
+ { 68, 0x070, "3.6" }
};
static int num_aic7xxx_syncrates =
@@ -790,166 +905,51 @@ static int num_aic7xxx_syncrates =
#ifdef CONFIG_PCI
static int number_of_3940s = 0;
static int number_of_3985s = 0;
-#ifdef AIC7XXX_SHARE_SCBS
-static scb_usage_type *shared_3985_scbs = NULL;
-#endif
-#endif CONFIG_PCI
+#endif /* CONFIG_PCI */
#ifdef AIC7XXX_DEBUG
-static void
-debug_config(struct aic7xxx_host_config *p)
-{
- int scsi_conf;
- unsigned char brelease;
- unsigned char dfthresh;
-
- static int DFT[] = { 0, 50, 75, 100 };
- static int SST[] = { 256, 128, 64, 32 };
- static const char *BUSW[] = { "", "-TWIN", "-WIDE" };
-
- scsi_conf = inb(SCSICONF + p->base);
-
- /*
- * Scale the Data FIFO Threshhold and the Bus Release Time; they are
- * stored in formats compatible for writing to sequencer registers.
- */
- dfthresh = p->bus_speed >> 6;
-
- if (p->chip_type == AIC_777x)
- {
- brelease = p->busrtime >> 2;
- }
- else
- {
- brelease = p->busrtime;
- }
- if (brelease == 0)
- {
- brelease = 2;
- }
-
- switch (p->type)
- {
- case AIC_7770:
- case AIC_7771:
- printk("%s%s AT EISA SLOT %d:\n", board_names[p->type], BUSW[p->bus_type],
- p->base >> 12);
- break;
-
- case AIC_284x:
- printk("%s%s AT VLB SLOT %d:\n", board_names[p->type], BUSW[p->bus_type],
- p->base >> 12);
- break;
-
- case AIC_7850:
- case AIC_7855:
- case AIC_7860:
- case AIC_7861:
- case AIC_7870:
- case AIC_7871:
- case AIC_7872:
- case AIC_7873:
- case AIC_7874:
- case AIC_7880:
- case AIC_7881:
- case AIC_7882:
- case AIC_7883:
- case AIC_7884:
- printk("%s%s (PCI-bus), I/O 0x%x, Mem 0x%x:\n", board_names[p->type],
- BUSW[p->bus_type], p->base, p->mbase);
- break;
-
- default:
- panic("aic7xxx: (debug_config) internal error.\n");
- }
-
- printk(" irq %d\n"
- " bus release time %d bclks\n"
- " data fifo threshold %d%%\n",
- p->irq,
- brelease,
- DFT[dfthresh]);
-
- printk(" SCSI CHANNEL A:\n"
- " scsi id %d\n"
- " scsi selection timeout %d ms\n"
- " scsi bus reset at power-on %sabled\n",
- scsi_conf & 0x07,
- SST[(scsi_conf >> 3) & 0x03],
- (scsi_conf & 0x40) ? "en" : "dis");
-
- if ((p->chip_type == AIC_777x) && (p->parity == AIC_UNKNOWN))
- {
- /*
- * Set the parity for 7770 based cards.
- */
- p->parity = (scsi_conf & 0x20) ? AIC_ENABLED : AIC_DISABLED;
- }
- if (p->parity != AIC_UNKNOWN)
- {
- printk(" scsi bus parity %sabled\n",
- (p->parity == AIC_ENABLED) ? "en" : "dis");
- }
-
- if ((p->type == AIC_7770) || (p->type == AIC_7771))
- {
- p->low_term = (scsi_conf & 0x80) ? AIC_ENABLED : AIC_DISABLED;
- }
- if (p->low_term != AIC_UNKNOWN)
- {
- printk(" scsi bus termination (low byte) %sabled\n",
- (p->low_term == AIC_ENABLED) ? "en" : "dis");
- }
- if ((p->bus_type == AIC_WIDE) && (p->high_term != AIC_UNKNOWN))
- {
- printk(" scsi bus termination (high byte) %sabled\n",
- (p->high_term == AIC_ENABLED) ? "en" : "dis");
- }
-}
-
#if 0
static void
debug_scb(struct aic7xxx_scb *scb)
{
- printk("control 0x%x, tcl 0x%x, sg_count %d, sg_ptr 0x%x, cmdp 0x%x, cmdlen %d\n",
- scb->control, scb->target_channel_lun, scb->SG_segment_count,
- (scb->SG_list_pointer[3] << 24) | (scb->SG_list_pointer[2] << 16) |
- (scb->SG_list_pointer[1] << 8) | scb->SG_list_pointer[0],
- (scb->SCSI_cmd_pointer[3] << 24) | (scb->SCSI_cmd_pointer[2] << 16) |
- (scb->SCSI_cmd_pointer[1] << 8) | scb->SCSI_cmd_pointer[0],
- scb->SCSI_cmd_length);
- printk("reserved 0x%x, target status 0x%x, resid SG count %d, resid data count %d\n",
- (scb->RESERVED[1] << 8) | scb->RESERVED[0], scb->target_status,
- scb->residual_SG_segment_count,
- ((scb->residual_data_count[2] << 16) |
- (scb->residual_data_count[1] << 8) |
- (scb->residual_data_count[0]));
- printk("data ptr 0x%x, data count %d, next waiting %d\n",
- (scb->data_pointer[3] << 24) | (scb->data_pointer[2] << 16) |
- (scb->data_pointer[1] << 8) | scb->data_pointer[0],
- scb->data_count, scb->next_waiting);
- printk("next ptr 0x%lx, Scsi Cmnd 0x%lx, state 0x%x, position %d\n",
- (unsigned long) scb->next, (unsigned long) scb->cmd, scb->state,
- scb->position);
+ struct aic7xxx_hwscb *hscb = scb->hscb;
+
+ printk("scb:%p control:0x%x tcl:0x%x cmdlen:%d cmdpointer:0x%lx\n",
+ scb,
+ hscb->control,
+ hscb->target_channel_lun,
+ hscb->SCSI_cmd_length,
+ hscb->SCSI_cmd_pointer );
+ printk(" datlen:%d data:0x%lx segs:0x%x segp:0x%lx\n",
+ hscb->data_count,
+ hscb->data_pointer,
+ hscb->SG_segment_count,
+ hscb->SG_list_pointer);
+ printk(" sg_addr:%lx sg_len:%ld\n",
+ hscb->sg_list[0].address,
+ hscb->sg_list[0].length);
}
#endif
#else
-# define debug_config(x)
# define debug_scb(x)
#endif AIC7XXX_DEBUG
-#define TCL_OF_SCB(x) (((x)->target_channel_lun >> 4) & 0xf), \
- (((x)->target_channel_lun >> 3) & 0x01), \
- ((x)->target_channel_lun & 0x07)
+#define TCL_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 4) & 0xf), \
+ (((scb->hscb)->target_channel_lun >> 3) & 0x01), \
+ ((scb->hscb)->target_channel_lun & 0x07)
+
+#define TC_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 4) & 0xf), \
+ (((scb->hscb)->target_channel_lun >> 3) & 0x01)
-#define TARGET_INDEX(x) ((x)->target | ((x)->channel << 3))
+#define CHAN_TO_INT(chan) ((chan) == 'A' ? 0 : 1)
+
+#define TARGET_INDEX(cmd) ((cmd)->target | ((cmd)->channel << 3))
/*
* XXX - these options apply unilaterally to _all_ 274x/284x/294x
- * cards in the system. This should be fixed, but then,
- * does anyone really have more than one in a machine?
+ * cards in the system. This should be fixed.
*/
static unsigned int aic7xxx_extended = 0; /* extended translation on? */
static unsigned int aic7xxx_no_reset = 0; /* no resetting of SCSI bus */
@@ -959,6 +959,53 @@ static int aic7xxx_irq_trigger = -1; /*
* 1 use level triggered
*/
static int aic7xxx_enable_ultra = 0; /* enable ultra SCSI speeds */
+static int aic7xxx_verbose = 0; /* verbose messages */
+
+
+/****************************************************************************
+ *
+ * These functions are not used yet, but when we do memory mapped
+ * IO, we'll use them then.
+ *
+ ***************************************************************************/
+static inline unsigned char
+aic_inb(struct aic7xxx_host *p, long port)
+{
+ if (p->maddr != NULL)
+ return (p->maddr[port]);
+ else
+ return (inb(p->base + port));
+}
+
+static inline void
+aic_outb(struct aic7xxx_host *p, unsigned char val, long port)
+{
+ if (p->maddr != NULL)
+ p->maddr[port] = val;
+ else
+ outb(val, p->base + port);
+}
+
+static inline void
+aic_outsb(struct aic7xxx_host *p, long port, unsigned char *valp, size_t size)
+{
+ if (p->maddr != NULL)
+ {
+ __asm __volatile("
+ cld;
+ 1: lodsb;
+ movb %%al,(%0);
+ loop 1b" :
+ :
+ "r" ((p)->maddr + (port)),
+ "S" ((valp)), "c" ((size)) :
+ "%esi", "%ecx", "%eax");
+ }
+ else
+ {
+ outsb(p->base + port, valp, size);
+ }
+}
/*+F*************************************************************************
* Function:
@@ -983,6 +1030,7 @@ aic7xxx_setup(char *s, int *dummy)
{ "no_reset", &aic7xxx_no_reset },
{ "irq_trigger", &aic7xxx_irq_trigger },
{ "ultra", &aic7xxx_enable_ultra },
+ { "verbose", &aic7xxx_verbose },
{ NULL, NULL }
};
@@ -1008,58 +1056,230 @@ aic7xxx_setup(char *s, int *dummy)
/*+F*************************************************************************
* Function:
+ * pause_sequencer
+ *
+ * Description:
+ * Pause the sequencer and wait for it to actually stop - this
+ * is important since the sequencer can disable pausing for critical
+ * sections.
+ *-F*************************************************************************/
+static inline void
+pause_sequencer(struct aic7xxx_host *p)
+{
+ outb(p->pause, p->base + HCNTRL);
+ while ((inb(p->base + HCNTRL) & PAUSE) == 0)
+ {
+ ;
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * unpause_sequencer
+ *
+ * Description:
+ * Unpause the sequencer. Unremarkable, yet done often enough to
+ * warrant an easy way to do it.
+ *-F*************************************************************************/
+static inline void
+unpause_sequencer(struct aic7xxx_host *p, int unpause_always)
+{
+ if (unpause_always ||
+ ((inb(p->base + INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) == 0))
+ {
+ outb(p->unpause, p->base + HCNTRL);
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * restart_sequencer
+ *
+ * Description:
+ * Restart the sequencer program from address zero. This assumes
+ * that the sequencer is already paused.
+ *-F*************************************************************************/
+static inline void
+restart_sequencer(struct aic7xxx_host *p)
+{
+ /* Set the sequencer address to 0. */
+ outb(0, p->base + SEQADDR0);
+ outb(0, p->base + SEQADDR1);
+
+ /*
+ * Reset and unpause the sequencer. The reset is suppose to
+ * start the sequencer running, but we do an unpause to make
+ * sure.
+ */
+ outb(SEQRESET | FASTMODE, p->base + SEQCTL);
+
+ unpause_sequencer(p, /*unpause_always*/ TRUE);
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_next_patch
+ *
+ * Description:
+ * Find the next patch to download.
+ *-F*************************************************************************/
+static struct patch *
+aic7xxx_next_patch(struct patch *cur_patch, int options, int instrptr)
+{
+ while (cur_patch != NULL)
+ {
+ if ((((cur_patch->options & options) != 0) && (cur_patch->negative == FALSE))
+ || (((cur_patch->options & options) == 0) && (cur_patch->negative == TRUE))
+ || (instrptr >= cur_patch->end))
+ {
+ /*
+ * Either we want to keep this section of code, or we have consumed
+ * this patch. Skip to the next patch.
+ */
+ cur_patch++;
+ if (cur_patch->options == 0)
+ {
+ /* Out of patches. */
+ cur_patch = NULL;
+ }
+ }
+ else
+ {
+ /* Found an OK patch. */
+ break;
+ }
+ }
+ return (cur_patch);
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_download_instr
+ *
+ * Description:
+ * Find the next patch to download.
+ *-F*************************************************************************/
+static void
+aic7xxx_download_instr(struct aic7xxx_host *p, int options, int instrptr)
+{
+ unsigned char opcode;
+ struct ins_format3 *instr;
+
+ instr = (struct ins_format3 *) &seqprog[instrptr * 4];
+ /* Pull the opcode */
+ opcode = instr->opcode_addr >> 1;
+ switch (opcode)
+ {
+ case AIC_OP_JMP:
+ case AIC_OP_JC:
+ case AIC_OP_JNC:
+ case AIC_OP_CALL:
+ case AIC_OP_JNE:
+ case AIC_OP_JNZ:
+ case AIC_OP_JE:
+ case AIC_OP_JZ:
+ {
+ int address_offset;
+ struct ins_format3 new_instr;
+ unsigned int address;
+ struct patch *patch;
+ int i;
+
+ address_offset = 0;
+ new_instr = *instr; /* Strucure copy */
+ address = new_instr.address;
+ address |= (new_instr.opcode_addr & ADDR_HIGH_BIT) << 8;
+ for (i = 0; i < NUMBER(patches); i++)
+ {
+ patch = &patches[i];
+ if ((((patch->options & options) == 0) && (patch->negative == FALSE)) ||
+ (((patch->options & options) != 0) && (patch->negative == TRUE)))
+ {
+ if (address >= patch->end)
+ {
+ address_offset += patch->end - patch->begin;
+ }
+ }
+ }
+ address -= address_offset;
+ new_instr.address = address &0xFF;
+ new_instr.opcode_addr &= ~ADDR_HIGH_BIT;
+ new_instr.opcode_addr |= (address >> 8) & ADDR_HIGH_BIT;
+ outsb(p->base + SEQRAM, &new_instr.immediate, 4);
+ break;
+ }
+
+ case AIC_OP_OR:
+ case AIC_OP_AND:
+ case AIC_OP_XOR:
+ case AIC_OP_ADD:
+ case AIC_OP_ADC:
+ case AIC_OP_ROL:
+ outsb(p->base + SEQRAM, &instr->immediate, 4);
+ break;
+
+ default:
+ panic("aic7xxx: Unknown opcode encountered in sequencer program.");
+ break;
+ }
+}
+
+
+/*+F*************************************************************************
+ * Function:
* aic7xxx_loadseq
*
* Description:
* Load the sequencer code into the controller memory.
*-F*************************************************************************/
static void
-aic7xxx_loadseq(int base)
+aic7xxx_loadseq(struct aic7xxx_host *p)
{
- static unsigned char seqprog[] = {
- /*
- * Each sequencer instruction is 29 bits
- * long (fill in the excess with zeroes)
- * and has to be loaded from least -> most
- * significant byte, so this table has the
- * byte ordering reversed.
- */
-# include "aic7xxx_seq.h"
- };
+ int options;
+ struct patch *cur_patch;
+ int i;
+ int downloaded;
- /*
- * When the AIC-7770 is paused (as on chip reset), the
- * sequencer address can be altered and a sequencer
- * program can be loaded by writing it, byte by byte, to
- * the sequencer RAM port - the Adaptec documentation
- * recommends using REP OUTSB to do this, hence the inline
- * assembly. Since the address autoincrements as we load
- * the program, reset it back to zero afterward. Disable
- * sequencer RAM parity error detection while loading, and
- * make sure the LOADRAM bit is enabled for loading.
- */
- outb(PERRORDIS | SEQRESET | LOADRAM, SEQCTL + base);
+ if (aic7xxx_verbose)
+ {
+ printk(KERN_INFO "aic7xxx: Downloading sequencer code...");
+ }
+ options = 1; /* Code for all options. */
+ downloaded = 0;
+ if ((p->flags & ULTRA_ENABLED) != 0)
+ options |= ULTRA;
+ if (p->bus_type == AIC_TWIN)
+ options |= TWIN_CHANNEL;
+ if (p->scb_data->maxscbs > p->scb_data->maxhscbs)
+ options |= SCB_PAGING;
- outsb(SEQRAM + base, seqprog, sizeof(seqprog));
+ cur_patch = patches;
+ outb(PERRORDIS | LOADRAM, p->base + SEQCTL);
+ outb(0, p->base + SEQADDR0);
+ outb(0, p->base + SEQADDR1);
- /*
- * WARNING! This is a magic sequence! After extensive
- * experimentation, it seems that you MUST turn off the
- * LOADRAM bit before you play with SEQADDR again, else
- * you will end up with parity errors being flagged on
- * your sequencer program. (You would also think that
- * turning off LOADRAM and setting SEQRESET to reset the
- * address to zero would work, but you need to do it twice
- * for it to take effect on the address. Timing problem?)
- */
- do {
- /*
- * Actually, reset it until
- * the address shows up as
- * zero just to be safe..
- */
- outb(SEQRESET | FASTMODE, SEQCTL + base);
- } while ((inb(SEQADDR0 + base) != 0) && (inb(SEQADDR1 + base) != 0));
+ for (i = 0; i < sizeof(seqprog) / 4; i++)
+ {
+ cur_patch = aic7xxx_next_patch(cur_patch, options, i);
+ if (cur_patch && (cur_patch->begin <= i) && (cur_patch->end > i))
+ {
+ /* Skip this instruction for this configuration. */
+ continue;
+ }
+ aic7xxx_download_instr(p, options, i);
+ downloaded++;
+ }
+
+ outb(FASTMODE, p->base + SEQCTL);
+ outb(0, p->base + SEQADDR0);
+ outb(0, p->base + SEQADDR1);
+
+ if (aic7xxx_verbose)
+ {
+ printk(" %d instructions downloaded\n", downloaded);
+ }
}
/*+F*************************************************************************
@@ -1067,18 +1287,22 @@ aic7xxx_loadseq(int base)
* aic7xxx_delay
*
* Description:
- * Delay for specified amount of time.
+ * Delay for specified amount of time. We use udelay because the timer
+ * interrupt is not guaranteed to be enabled. This will cause an
+ * infinite loop since jiffies (clock ticks) is not updated.
*-F*************************************************************************/
static void
aic7xxx_delay(int seconds)
{
- unsigned long i;
-
- i = jiffies + (seconds * HZ); /* compute time to stop */
+ int i;
- while (jiffies < i)
+ /*
+ * Call udelay() for 1 millisecond inside a loop for
+ * the requested amount of seconds.
+ */
+ for (i=0; i < seconds*1000; i++)
{
- ; /* Do nothing! */
+ udelay(1000); /* Delay for 1 millisecond. */
}
}
@@ -1090,7 +1314,7 @@ aic7xxx_delay(int seconds)
* Return a string containing just the RCS version number from either
* an Id or Revision RCS clause.
*-F*************************************************************************/
-static const char *
+const char *
rcs_version(const char *version_info)
{
static char buf[10];
@@ -1150,8 +1374,10 @@ aic7xxx_info(struct Scsi_Host *notused)
strcat(buffer, rcs_version(AIC7XXX_C_VERSION));
strcat(buffer, "/");
strcat(buffer, rcs_version(AIC7XXX_H_VERSION));
+#if 0
strcat(buffer, "/");
strcat(buffer, rcs_version(AIC7XXX_SEQ_VER));
+#endif
return buffer;
}
@@ -1161,9 +1387,12 @@ aic7xxx_info(struct Scsi_Host *notused)
* aic7xxx_length
*
* Description:
- * How much data should be transferred for this SCSI command? Stop
- * at segment sg_last if it's a scatter-gather command so we can
- * compute underflow easily.
+ * How much data should be transferred for this SCSI command? Assume
+ * all segments are to be transferred except for the last sg_last
+ * segments. This will allow us to compute underflow easily. To
+ * calculate the total length of the command, use sg_last = 0. To
+ * calculate the length of all but the last 2 SG segments, use
+ * sg_last = 2.
*-F*************************************************************************/
static unsigned
aic7xxx_length(Scsi_Cmnd *cmd, int sg_last)
@@ -1177,7 +1406,7 @@ aic7xxx_length(Scsi_Cmnd *cmd, int sg_last)
if (cmd->use_sg)
{
- for (i = length = 0; (i < cmd->use_sg) && (i < segments); i++)
+ for (i = length = 0; i < segments; i++)
{
length += sg[i].length;
}
@@ -1199,9 +1428,9 @@ aic7xxx_length(Scsi_Cmnd *cmd, int sg_last)
*-F*************************************************************************/
static void
aic7xxx_scsirate(struct aic7xxx_host *p, unsigned char *scsirate,
- short period, unsigned char offset, int target, char channel)
+ unsigned char *period, unsigned char *offset, int target, char channel)
{
- int i;
+ int i = num_aic7xxx_syncrates;
unsigned long ultra_enb_addr;
unsigned char ultra_enb, sxfrctl0;
@@ -1209,11 +1438,11 @@ aic7xxx_scsirate(struct aic7xxx_host *p, unsigned char *scsirate,
* If the offset is 0, then the device is requesting asynchronous
* transfers.
*/
- if (offset != 0)
+ if ((*period >= aic7xxx_syncrates[i].period) && *offset != 0)
{
for (i = 0; i < num_aic7xxx_syncrates; i++)
{
- if ((aic7xxx_syncrates[i].period - period) >= 0)
+ if (*period <= aic7xxx_syncrates[i].period)
{
/*
* Watch out for Ultra speeds when ultra is not enabled and
@@ -1229,99 +1458,57 @@ aic7xxx_scsirate(struct aic7xxx_host *p, unsigned char *scsirate,
*/
continue;
}
- *scsirate = (aic7xxx_syncrates[i].rate) | (offset & 0x0F);
+ *scsirate = (aic7xxx_syncrates[i].rate & 0xF0) | (*offset & 0x0F);
+ *period = aic7xxx_syncrates[i].period;
- /*
- * Ensure Ultra mode is set properly for this target.
- */
- ultra_enb_addr = ULTRA_ENB;
- if ((channel == 'B') || (target > 7))
- {
- ultra_enb_addr++;
- }
- ultra_enb = inb(p->base + ultra_enb_addr);
- sxfrctl0 = inb(p->base + SXFRCTL0);
- if (aic7xxx_syncrates[i].rate & ULTRA_SXFR)
+ if (aic7xxx_verbose)
{
- ultra_enb |= 0x01 << (target & 0x07);
- sxfrctl0 |= ULTRAEN;
+ printk("scsi%d: Target %d, channel %c, now synchronous at %sMHz, "
+ "offset %d.\n", p->host_no, target, channel,
+ aic7xxx_syncrates[i].english, *offset);
}
- else
- {
- ultra_enb &= ~(0x01 << (target & 0x07));
- sxfrctl0 &= ~ULTRAEN;
- }
- outb(ultra_enb, p->base + ultra_enb_addr);
- outb(sxfrctl0, p->base + SXFRCTL0);
-
- printk("scsi%d: Target %d, channel %c, now synchronous at %sMHz, "
- "offset %d.\n", p->host_no, target, channel,
- aic7xxx_syncrates[i].english, offset);
- return;
+ break;
}
}
}
- /*
- * Default to asynchronous transfer
- */
- *scsirate = 0;
- printk("scsi%d: Target %d, channel %c, using asynchronous transfers.\n",
- p->host_no, target, channel);
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_putscb
- *
- * Description:
- * Transfer a SCB to the controller.
- *-F*************************************************************************/
-static inline void
-aic7xxx_putscb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
- int base = p->base;
-
- outb(SCBAUTO, SCBCNT + base);
+ if (i >= num_aic7xxx_syncrates)
+ {
+ /*
+ * Use asynchronous transfers.
+ */
+ *scsirate = 0;
+ *period = 0;
+ *offset = 0;
+ if (aic7xxx_verbose)
+ {
+ printk("scsi%d: Target %d, channel %c, using asynchronous transfers.\n",
+ p->host_no, target, channel);
+ }
+ }
/*
- * By turning on the SCB auto increment, any reference
- * to the SCB I/O space postincrements the SCB address
- * we're looking at. So turn this on and dump the relevant
- * portion of the SCB to the card.
- *
- * We can do 16bit transfers on all but 284x.
+ * Ensure Ultra mode is set properly for this target.
*/
- if (p->type == AIC_284x)
+ ultra_enb_addr = ULTRA_ENB;
+ if ((channel == 'B') || (target > 7))
+ {
+ ultra_enb_addr++;
+ }
+ ultra_enb = inb(p->base + ultra_enb_addr);
+ sxfrctl0 = inb(p->base + SXFRCTL0);
+ if ((*scsirate != 0) && (aic7xxx_syncrates[i].rate & ULTRA_SXFR))
{
- outsb(SCBARRAY + base, scb, SCB_PIO_TRANSFER_SIZE);
+ ultra_enb |= 0x01 << (target & 0x07);
+ sxfrctl0 |= FAST20;
}
else
{
- outsl(SCBARRAY + base, scb, (SCB_PIO_TRANSFER_SIZE + 3) / 4);
+ ultra_enb &= ~(0x01 << (target & 0x07));
+ sxfrctl0 &= ~FAST20;
}
-
- outb(0, SCBCNT + base);
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_getscb
- *
- * Description:
- * Get a SCB from the controller.
- *-F*************************************************************************/
-static inline void
-aic7xxx_getscb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
- int base = p->base;
-
- /*
- * This is almost identical to aic7xxx_putscb().
- */
- outb(SCBAUTO, SCBCNT + base);
- insb(SCBARRAY + base, scb, SCB_PIO_TRANSFER_SIZE);
- outb(0, SCBCNT + base);
+ outb(ultra_enb, p->base + ultra_enb_addr);
+ outb(sxfrctl0, p->base + SXFRCTL0);
}
/*+F*************************************************************************
@@ -1375,6 +1562,47 @@ scbq_remove_head(scb_queue_type *queue)
/*+F*************************************************************************
* Function:
+ * scbq_remove
+ *
+ * Description:
+ * Removes an SCB from the list.
+ *
+ *-F*************************************************************************/
+static inline void
+scbq_remove(scb_queue_type *queue, struct aic7xxx_scb *scb)
+{
+ if (queue->head == scb)
+ {
+ /* At beginning of queue, remove from head. */
+ scbq_remove_head(queue);
+ }
+ else
+ {
+ struct aic7xxx_scb *curscb = queue->head;
+
+ /*
+ * Search until the next scb is the one we're looking for, or
+ * we run out of queue.
+ */
+ while ((curscb != NULL) && (curscb->q_next != scb))
+ {
+ curscb = curscb->q_next;
+ }
+ if (curscb != NULL)
+ {
+ /* Found it. */
+ curscb->q_next = scb->q_next;
+ if (scb->q_next == NULL)
+ {
+ /* Update the tail when removing the tail. */
+ queue->tail = curscb;
+ }
+ }
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
* scbq_insert_tail
*
* Description:
@@ -1404,23 +1632,87 @@ scbq_insert_tail(scb_queue_type *queue, struct aic7xxx_scb *scb)
* to be reset and all devices on that channel must be aborted.
*-F*************************************************************************/
static int
-aic7xxx_match_scb(struct aic7xxx_scb *scb, int target, char channel)
+aic7xxx_match_scb(struct aic7xxx_scb *scb, int target, char channel,
+ int lun, unsigned char tag)
{
- int targ = (scb->target_channel_lun >> 4) & 0x0F;
- char chan = (scb->target_channel_lun & SELBUSB) ? 'B' : 'A';
+ int targ = (scb->hscb->target_channel_lun >> 4) & 0x0F;
+ char chan = (scb->hscb->target_channel_lun & SELBUSB) ? 'B' : 'A';
+ int slun = scb->hscb->target_channel_lun & 0x07;
+ int match;
#ifdef AIC7XXX_DEBUG_ABORT
- printk("aic7xxx: (match_scb) comparing target/channel %d/%c to scb %d/%c\n",
- target, channel, targ, chan);
+ printk("scsi%d: (targ %d/chan %c) matching scb to (targ %d/chan %c)\n",
+ scb->cmd->device->host->host_no, target, channel, targ, chan);
#endif
- if (target == ALL_TARGETS)
+ match = ((chan == channel) || (channel == ALL_CHANNELS));
+ if (match != 0)
+ match = ((targ == target) || (target == ALL_TARGETS));
+ if (match != 0)
+ match = ((lun == slun) || (lun == ALL_LUNS));
+ if (match != 0)
+ match = ((tag == scb->hscb->tag) || (tag == SCB_LIST_NULL));
+
+ return (match);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_add_curscb_to_free_list
+ *
+ * Description:
+ * Adds the current scb (in SCBPTR) to the list of free SCBs.
+ *-F*************************************************************************/
+static void
+aic7xxx_add_curscb_to_free_list(struct aic7xxx_host *p)
+{
+ /*
+ * Invalidate the tag so that aic7xxx_find_scb doesn't think
+ * it's active
+ */
+ outb(SCB_LIST_NULL, p->base + SCB_TAG);
+
+ outb(inb(p->base + FREE_SCBH), p->base + SCB_NEXT);
+ outb(inb(p->base + SCBPTR), p->base + FREE_SCBH);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_rem_scb_from_disc_list
+ *
+ * Description:
+ * Removes the current SCB from the disconnected list and adds it
+ * to the free list.
+ *-F*************************************************************************/
+static unsigned char
+aic7xxx_rem_scb_from_disc_list(struct aic7xxx_host *p, unsigned char scbptr)
+{
+ unsigned char next;
+ unsigned char prev;
+
+ outb(scbptr, p->base + SCBPTR);
+ next = inb(p->base + SCB_NEXT);
+ prev = inb(p->base + SCB_PREV);
+
+ outb(0, p->base + SCB_CONTROL);
+
+ aic7xxx_add_curscb_to_free_list(p);
+
+ if (prev != SCB_LIST_NULL)
{
- return (chan == channel);
+ outb(prev, p->base + SCBPTR);
+ outb(next, p->base + SCB_NEXT);
}
else
{
- return ((chan == channel) && (targ == target));
+ outb(next, p->base + DISCONNECTED_SCBH);
}
+
+ if (next != SCB_LIST_NULL)
+ {
+ outb(next, p->base + SCBPTR);
+ outb(prev, p->base + SCB_PREV);
+ }
+ return next;
}
/*+F*************************************************************************
@@ -1428,51 +1720,93 @@ aic7xxx_match_scb(struct aic7xxx_scb *scb, int target, char channel)
* aic7xxx_busy_target
*
* Description:
- * Set the specified target active.
+ * Set the specified target busy.
*-F*************************************************************************/
static void
-aic7xxx_busy_target(unsigned char target, char channel, int base)
+aic7xxx_busy_target(struct aic7xxx_host *p, unsigned char target,
+ char channel, unsigned char scbid)
{
- unsigned char active;
- unsigned long active_port = ACTIVE_A + base;
+ unsigned char active_scb;
+ unsigned char info_scb;
+ unsigned int scb_offset;
+
+ info_scb = target / 4;
+ if (channel == 'B')
+ info_scb = info_scb + 2;
+
+ active_scb = inb(p->base + SCBPTR);
+ outb(info_scb, p->base + SCBPTR);
+ scb_offset = SCB_BUSYTARGETS + (target & 0x03);
+ outb(scbid, p->base + scb_offset);
+ outb(active_scb, p->base + SCBPTR);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_index_busy_target
+ *
+ * Description:
+ * Returns the index of the busy target, and optionally sets the
+ * target inactive.
+ *-F*************************************************************************/
+static unsigned char
+aic7xxx_index_busy_target(struct aic7xxx_host *p, unsigned char target,
+ char channel, int unbusy)
+{
+ unsigned char active_scb;
+ unsigned char info_scb;
+ unsigned char busy_scbid;
+ unsigned int scb_offset;
+
+ info_scb = target / 4;
+ if (channel == 'B')
+ info_scb = info_scb + 2;
- if ((target > 0x07) || (channel == 'B'))
+ active_scb = inb(p->base + SCBPTR);
+ outb(info_scb, p->base + SCBPTR);
+ scb_offset = SCB_BUSYTARGETS + (target & 0x03);
+ busy_scbid = inb(p->base + scb_offset);
+ if (unbusy)
{
- /*
- * targets on the Second channel or above id 7 store info in byte two
- * of ACTIVE
- */
- active_port++;
+ outb(SCB_LIST_NULL, p->base + scb_offset);
}
- active = inb(active_port);
- active |= (0x01 << (target & 0x07));
- outb(active, active_port);
+ outb(active_scb, p->base + SCBPTR);
+ return (busy_scbid);
}
/*+F*************************************************************************
* Function:
- * aic7xxx_unbusy_target
+ * aic7xxx_find_scb
*
* Description:
- * Set the specified target inactive.
+ * Look through the SCB array of the card and attempt to find the
+ * hardware SCB that corresponds to the passed in SCB. Return
+ * SCB_LIST_NULL if unsuccessful. This routine assumes that the
+ * card is already paused.
*-F*************************************************************************/
-static void
-aic7xxx_unbusy_target(unsigned char target, char channel, int base)
+static unsigned char
+aic7xxx_find_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
{
- unsigned char active;
- unsigned long active_port = ACTIVE_A + base;
+ unsigned char saved_scbptr;
+ unsigned char curindex;
- if ((target > 0x07) || (channel == 'B'))
+ saved_scbptr = inb(p->base + SCBPTR);
+ curindex = 0;
+ for (curindex = 0; curindex < p->scb_data->maxhscbs; curindex++)
{
- /*
- * targets on the Second channel or above id 7 store info in byte two
- * of ACTIVE
- */
- active_port++;
+ outb(curindex, p->base + SCBPTR);
+ if (inb(p->base + SCB_TAG) == scb->hscb->tag)
+ {
+ break;
+ }
}
- active = inb(active_port);
- active &= ~(0x01 << (target & 0x07));
- outb(active, active_port);
+ outb(saved_scbptr, p->base + SCBPTR);
+ if (curindex >= p->scb_data->maxhscbs)
+ {
+ curindex = SCB_LIST_NULL;
+ }
+
+ return (curindex);
}
/*+F*************************************************************************
@@ -1480,68 +1814,60 @@ aic7xxx_unbusy_target(unsigned char target, char channel, int base)
* aic7xxx_allocate_scb
*
* Description:
- * Get a free SCB either from one already assigned to a hardware
- * slot, or one that will require an SCB to be paged out before
- * use. If there are none, attempt to allocate a new one.
+ * Get an SCB from the free list or by allocating a new one.
*-F*************************************************************************/
static struct aic7xxx_scb *
aic7xxx_allocate_scb(struct aic7xxx_host *p)
{
- struct aic7xxx_scb *scbp = NULL;
- int maxscbs;
+ struct aic7xxx_scb *scbp = NULL;
+ struct aic7xxx_hwscb *hscbp = NULL;
+#ifdef AGRESSIVE
+ long processor_flags;
- scbp = p->scb_link->free_scbs.head;
+ save_flags(processor_flags);
+ cli();
+#endif
+
+ scbp = p->scb_data->free_scbs.head;
if (scbp != NULL)
{
- scbq_remove_head(&p->scb_link->free_scbs);
+ scbq_remove_head(&p->scb_data->free_scbs);
}
else
{
- /*
- * This should always be NULL if paging is not enabled.
- */
- scbp = p->page_scbs.head;
- if (scbp != NULL)
+ if (p->scb_data->numscbs < p->scb_data->maxscbs)
{
- scbq_remove_head(&p->page_scbs);
- }
- else
- {
- /*
- * Set limit the SCB allocation to the maximum number of
- * hardware SCBs if paging is not enabled; otherwise use
- * the maximum (255).
- */
- if (p->flags & PAGE_ENABLED)
- maxscbs = p->maxscbs;
- else
- maxscbs = p->maxhscbs;
- if (p->scb_link->numscbs < maxscbs)
- {
- int scb_index = p->scb_link->numscbs;
- int scb_size = sizeof(struct aic7xxx_scb);
+ int scb_index = p->scb_data->numscbs;
+ int scb_size = sizeof(struct aic7xxx_scb) +
+ sizeof (struct hw_scatterlist) * AIC7XXX_MAX_SG;
- p->scb_array[scb_index] = kmalloc(scb_size, GFP_ATOMIC | GFP_DMA);
- scbp = (p->scb_array[scb_index]);
- if (scbp != NULL)
- {
- memset(scbp, 0, sizeof(*scbp));
- scbp->tag = scb_index;
- if (scb_index < p->maxhscbs)
- scbp->position = scb_index;
- else
- scbp->position = SCB_LIST_NULL;
- p->scb_link->numscbs++;
- }
+ scbp = kmalloc(scb_size, GFP_ATOMIC);
+ if (scbp != NULL)
+ {
+ memset(scbp, 0, sizeof(struct aic7xxx_scb));
+ hscbp = &p->scb_data->hscbs[scb_index];
+ scbp->hscb = hscbp;
+ scbp->sg_list = (struct hw_scatterlist *) &scbp[1];
+ memset(hscbp, 0, sizeof(struct aic7xxx_hwscb));
+ hscbp->tag = scb_index;
+ p->scb_data->numscbs++;
+ /*
+ * Place in the scb array; never is removed
+ */
+ p->scb_data->scb_array[scb_index] = scbp;
}
}
}
+#ifdef AIC7XXX_DEBUG
if (scbp != NULL)
{
-#ifdef AIC7XXX_DEBUG
- p->scb_link->activescbs++;
-#endif
+ p->activescbs++;
}
+#endif
+
+#ifdef AGRESSIVE
+ restore_flags(processor_flags);
+#endif
return (scbp);
}
@@ -1581,6 +1907,7 @@ aic7xxx_done_cmds_complete(struct aic7xxx_host *p)
cmd = p->completeq.head;
p->completeq.head = (Scsi_Cmnd *)cmd->host_scribble;
cmd->host_scribble = NULL;
+ p->device_status[TARGET_INDEX(cmd)].active_cmds--;
cmd->scsi_done(cmd);
}
p->completeq.tail = NULL;
@@ -1591,53 +1918,29 @@ aic7xxx_done_cmds_complete(struct aic7xxx_host *p)
* aic7xxx_free_scb
*
* Description:
- * Free the scb and update the page, waiting, free scb lists.
+ * Free the scb and insert into the free scb list.
*-F*************************************************************************/
static void
aic7xxx_free_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
{
- struct aic7xxx_scb *wscb;
+ struct aic7xxx_hwscb *hscb;
+ long flags;
+
+ hscb = scb->hscb;
+ save_flags(flags);
+ cli();
- scb->state = SCB_FREE;
+ scb->flags = SCB_FREE;
scb->cmd = NULL;
- scb->control = 0;
- scb->state = 0;
+ hscb->control = 0;
+ hscb->target_status = 0;
- if (scb->position == SCB_LIST_NULL)
- {
- scbq_insert_head(&p->page_scbs, scb);
- }
- else
- {
- /*
- * If there are any SCBS on the waiting queue, assign the slot of this
- * "freed" SCB to the first one. We'll run the waiting queues after
- * all command completes for a particular interrupt are completed or
- * when we start another command.
- */
- wscb = p->waiting_scbs.head;
- if (wscb != NULL)
- {
- scbq_remove_head(&p->waiting_scbs);
- wscb->position = scb->position;
- scbq_insert_tail(&p->assigned_scbs, wscb);
- wscb->state = (wscb->state & ~SCB_WAITINGQ) | SCB_ASSIGNEDQ;
-
- /*
- * The "freed" SCB will need to be assigned a slot before being
- * used, so put it in the page_scbs queue.
- */
- scb->position = SCB_LIST_NULL;
- scbq_insert_head(&p->page_scbs, scb);
- }
- else
- {
- scbq_insert_head(&p->scb_link->free_scbs, scb);
- }
+ scbq_insert_head(&p->scb_data->free_scbs, scb);
#ifdef AIC7XXX_DEBUG
- p->scb_link->activescbs--; /* For debugging purposes. */
+ p->activescbs--; /* For debugging purposes. */
#endif
- }
+
+ restore_flags(flags);
}
/*+F*************************************************************************
@@ -1652,68 +1955,113 @@ aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
{
Scsi_Cmnd *cmd = scb->cmd;
+ if (scb->flags & SCB_RECOVERY_SCB)
+ {
+ p->flags &= ~IN_TIMEOUT;
+ }
+ if (cmd->result == DID_OK)
+ {
+ if (scb->flags & SCB_ABORTED)
+ {
+ cmd->result = (DID_RESET << 16);
+ }
+ }
+ if ((scb->flags & (SCB_MSGOUT_WDTR | SCB_MSGOUT_SDTR)) != 0)
+ {
+ unsigned short mask;
+
+ mask = 0x01 << TARGET_INDEX(scb->cmd);
+ if (scb->flags & SCB_MSGOUT_WDTR)
+ {
+ p->wdtr_pending &= ~mask;
+ }
+ if (scb->flags & SCB_MSGOUT_SDTR)
+ {
+ p->sdtr_pending &= ~mask;
+ }
+ }
aic7xxx_free_scb(p, scb);
aic7xxx_queue_cmd_complete(p, cmd);
+#ifdef AIC7XXX_PROC_STATS
+ {
+ int actual;
+
+ /*
+ * XXX: we should actually know how much actually transferred
+ * XXX: for each command, but apparently that's too difficult.
+ */
+ actual = aic7xxx_length(cmd, 0);
+ if (!(scb->flags & (SCB_ABORTED | SCB_SENSE)) && (actual > 0)
+ && (aic7xxx_error(cmd) == 0))
+ {
+ struct aic7xxx_xferstats *sp;
+ long *ptr;
+ int x;
+
+ sp = &p->stats[cmd->channel & 0x01][cmd->target & 0x0F][cmd->lun & 0x07];
+ sp->xfers++;
+
+ if (cmd->request.cmd == WRITE)
+ {
+ sp->w_total++;
+ sp->w_total512 += (actual >> 9);
+ ptr = sp->w_bins;
+ }
+ else
+ {
+ sp->r_total++;
+ sp->r_total512 += (actual >> 9);
+ ptr = sp->r_bins;
+ }
+ for (x = 9; x <= 17; x++)
+ {
+ if (actual < (1 << x))
+ {
+ ptr[x - 9]++;
+ break;
+ }
+ }
+ if (x > 17)
+ {
+ ptr[x - 9]++;
+ }
+ }
+ }
+#endif /* AIC7XXX_PROC_STATS */
}
/*+F*************************************************************************
* Function:
- * aic7xxx_done_aborted_scbs
+ * aic7xxx_run_done_queue
*
* Description:
- * Calls the scsi_done() for the Scsi_Cmnd of each scb in the
- * aborted list, and adds each scb to the free list.
+ * Calls the aic7xxx_done() for the Scsi_Cmnd of each scb in the
+ * aborted list, and adds each scb to the free list. If complete
+ * is TRUE, we also process the commands complete list.
*-F*************************************************************************/
static void
-aic7xxx_done_aborted_scbs(struct aic7xxx_host *p)
+aic7xxx_run_done_queue(struct aic7xxx_host *p, /*complete*/ int complete)
{
- Scsi_Cmnd *cmd;
struct aic7xxx_scb *scb;
int i;
- for (i = 0; i < p->scb_link->numscbs; i++)
+ for (i = 0; i < p->scb_data->numscbs; i++)
{
- scb = (p->scb_array[i]);
- if (scb->state & SCB_QUEUED_FOR_DONE)
+ scb = p->scb_data->scb_array[i];
+ if (scb->flags & SCB_QUEUED_FOR_DONE)
{
#ifdef AIC7XXX_DEBUG_ABORT
- printk("aic7xxx: (done_aborted_scbs) Aborting scb %d, TCL=%d/%d/%d\n",
- scb->position, TCL_OF_SCB(scb));
+ printk("(scsi%d:%d:%d) Aborting scb %d\n",
+ p->host_no, TC_OF_SCB(scb), scb->hscb->tag);
#endif
- /*
- * Process the command after marking the scb as free
- * and adding it to the free list.
- */
- cmd = scb->cmd;
- p->device_status[TARGET_INDEX(cmd)].flags = 0;
- aic7xxx_free_scb(p, scb);
- cmd->scsi_done(cmd); /* call the done function */
+ aic7xxx_done(p, scb);
}
}
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_add_waiting_scb
- *
- * Description:
- * Add this SCB to the head of the "waiting for selection" list.
- *-F*************************************************************************/
-static void
-aic7xxx_add_waiting_scb(u_long base, struct aic7xxx_scb *scb)
-{
- unsigned char next;
- unsigned char curscb;
-
- curscb = inb(SCBPTR + base);
- next = inb(WAITING_SCBH + base);
-
- outb(scb->position, SCBPTR + base);
- outb(next, SCB_NEXT + base);
- outb(scb->position, WAITING_SCBH + base);
-
- outb(curscb, SCBPTR + base);
+ if (complete)
+ {
+ aic7xxx_done_cmds_complete(p);
+ }
}
/*+F*************************************************************************
@@ -1726,26 +2074,23 @@ aic7xxx_add_waiting_scb(u_long base, struct aic7xxx_scb *scb)
*-F*************************************************************************/
static unsigned char
aic7xxx_abort_waiting_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
- unsigned char prev)
+ unsigned char scbpos, unsigned char prev)
{
unsigned char curscb, next;
- int target = (scb->target_channel_lun >> 4) & 0x0F;
- char channel = (scb->target_channel_lun & SELBUSB) ? 'B' : 'A';
- int base = p->base;
/*
* Select the SCB we want to abort and pull the next pointer out of it.
*/
- curscb = inb(SCBPTR + base);
- outb(scb->position, SCBPTR + base);
- next = inb(SCB_NEXT + base);
+ curscb = inb(p->base + SCBPTR);
+ outb(scbpos, p->base + SCBPTR);
+ next = inb(p->base + SCB_NEXT);
/*
* Clear the necessary fields
*/
- outb(0, SCB_CONTROL + base);
- outb(SCB_LIST_NULL, SCB_NEXT + base);
- aic7xxx_unbusy_target(target, channel, base);
+ outb(0, p->base + SCB_CONTROL);
+
+ aic7xxx_add_curscb_to_free_list(p);
/*
* Update the waiting list
@@ -1755,22 +2100,23 @@ aic7xxx_abort_waiting_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
/*
* First in the list
*/
- outb(next, WAITING_SCBH + base);
+ outb(next, p->base + WAITING_SCBH);
}
else
{
/*
* Select the scb that pointed to us and update its next pointer.
*/
- outb(prev, SCBPTR + base);
- outb(next, SCB_NEXT + base);
+ outb(prev, p->base + SCBPTR);
+ outb(next, p->base + SCB_NEXT);
}
/*
* Point us back at the original scb position and inform the SCSI
* system that the command has been aborted.
*/
- outb(curscb, SCBPTR + base);
- scb->state |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
+ outb(curscb, p->base + SCBPTR);
+ scb->flags |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
+ scb->flags &= ~SCB_ACTIVE;
scb->cmd->result = (DID_RESET << 16);
return (next);
@@ -1778,6 +2124,75 @@ aic7xxx_abort_waiting_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
/*+F*************************************************************************
* Function:
+ * aic7xxx_search_qinfifo
+ *
+ * Description:
+ * Search the queue-in FIFO for matching SCBs and conditionally
+ * requeue. Returns the number of matching SCBs.
+ *-F*************************************************************************/
+static int
+aic7xxx_search_qinfifo(struct aic7xxx_host *p, int target, char channel,
+ int lun, unsigned char tag, int flags, int requeue)
+{
+ unsigned char saved_queue[AIC7XXX_MAXSCB];
+ int queued = inb(p->base + QINCNT) & p->qcntmask;
+ int i;
+ int found;
+ struct aic7xxx_scb *scbp;
+ scb_queue_type removed_scbs;
+
+ found = 0;
+ scbq_init (&removed_scbs);
+ for (i = 0; i < (queued - found); i++)
+ {
+ saved_queue[i] = inb(p->base + QINFIFO);
+ scbp = p->scb_data->scb_array[saved_queue[i]];
+ if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+ {
+ /*
+ * We found an scb that needs to be removed.
+ */
+ if (requeue)
+ {
+ scbq_insert_head(&removed_scbs, scbp);
+ }
+ else
+ {
+ scbp->flags = flags;
+ scbp->flags &= ~SCB_ACTIVE;
+ /*
+ * XXX - Don't know what error to use here.
+ */
+ aic7xxx_error(scbp->cmd) = DID_RESET;
+ }
+ i--;
+ found++;
+ }
+ }
+ /* Now put the saved scbs back. */
+ for (queued = 0; queued < i; queued++)
+ outb(saved_queue[queued], p->base + QINFIFO);
+
+ if (requeue)
+ {
+ scbp = removed_scbs.head;
+ while (scbp != NULL)
+ {
+ scbq_remove_head(&removed_scbs);
+ /*
+ * XXX - Shouldn't we be adding this to the free list?
+ */
+ scbq_insert_head(&p->waiting_scbs, scbp);
+ scbp->flags |= SCB_WAITINGQ;
+ scbp = removed_scbs.head;
+ }
+ }
+
+ return (found);
+}
+
+/*+F*************************************************************************
+ * Function:
* aic7xxx_reset_device
*
* Description:
@@ -1785,131 +2200,280 @@ aic7xxx_abort_waiting_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
* all active and queued scbs for that target/channel.
*-F*************************************************************************/
static int
-aic7xxx_reset_device(struct aic7xxx_host *p, int target, char channel)
+aic7xxx_reset_device(struct aic7xxx_host *p, int target, char channel,
+ int lun, unsigned char tag)
{
- int base = p->base;
- struct aic7xxx_scb *scb;
+ struct aic7xxx_scb *scbp;
unsigned char active_scb;
int i = 0;
- int found = 0;
+ int found;
/*
* Restore this when we're done
*/
- active_scb = inb(SCBPTR + base);
+ active_scb = inb(p->base + SCBPTR);
#ifdef AIC7XXX_DEBUG_ABORT
- printk("aic7xxx: (reset_device) target/channel %d/%c, active_scb %d\n",
- target, channel, active_scb);
+ printk("(scsi%d:%d:%d) Reset device, active_scb %d\n",
+ p->host_no, target, CHAN_TO_INT(channel), active_scb);
#endif
+
/*
- * Search the QINFIFO.
+ * Deal with the busy target and linked next issues.
*/
{
- int saved_queue[AIC7XXX_MAXSCB];
- int queued = inb(QINCNT + base) & p->qcntmask;
+ int min_target, max_target;
+ unsigned char busy_scbid;
- for (i = 0; i < (queued - found); i++)
+ /* Make all targets 'relative' to bus A. */
+ if (target == ALL_TARGETS)
{
- saved_queue[i] = inb(QINFIFO + base);
- outb(saved_queue[i], SCBPTR + base);
- scb = (p->scb_array[inb(SCB_TAG + base)]);
- if (aic7xxx_match_scb(scb, target, channel))
+ switch (channel)
{
- /*
- * We found an scb that needs to be aborted.
- */
-#ifdef AIC7XXX_DEBUG_ABORT
- printk("aic7xxx: (reset_device) aborting SCB %d, TCL=%d/%d/%d\n",
- saved_queue[i], TCL_OF_SCB(scb));
-#endif
- scb->state |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
- scb->cmd->result = (DID_RESET << 16);
- outb(0, SCB_CONTROL + base);
- i--;
- found++;
+ case 'A':
+ min_target = 0;
+ max_target = (p->bus_type == AIC_SINGLE) ? 7 : 15;
+ break;
+ case 'B':
+ min_target = 8;
+ max_target = 15;
+ break;
+ case ALL_CHANNELS:
+ default:
+ min_target = 0;
+ max_target = (p->bus_type == AIC_SINGLE) ? 7 : 15;
+ break;
}
}
- /*
- * Now put the saved scbs back.
- */
- for (queued = 0; queued < i; queued++)
+ else
+ {
+ min_target = target + channel == 'B' ? 8 : 0;
+ max_target = min_target;
+ }
+
+ for (i = min_target; i <= max_target; i++)
{
- outb(saved_queue[queued], QINFIFO + base);
+ busy_scbid = aic7xxx_index_busy_target(p, i, 'A', /*unbusy*/FALSE);
+ if (busy_scbid < p->scb_data->numscbs)
+ {
+ struct aic7xxx_scb *busy_scb;
+ struct aic7xxx_scb *next_scb;
+ unsigned char next_scbid;
+
+ busy_scb = p->scb_data->scb_array[busy_scbid];
+
+ next_scbid = busy_scb->hscb->data_count >> 24;
+
+ if (next_scbid == SCB_LIST_NULL)
+ {
+ busy_scbid = aic7xxx_find_scb(p, busy_scb);
+
+ if (busy_scbid != SCB_LIST_NULL)
+ {
+ outb(busy_scbid, p->base + SCBPTR);
+ next_scbid = inb(p->base + SCB_LINKED_NEXT);
+ }
+ }
+
+ if (aic7xxx_match_scb(busy_scb, target, channel, lun, tag))
+ {
+ aic7xxx_index_busy_target(p, i, 'A', /*unbusy*/TRUE);
+ }
+
+ if (next_scbid != SCB_LIST_NULL)
+ {
+ next_scb = p->scb_data->scb_array[next_scbid];
+ if (aic7xxx_match_scb(next_scb, target, channel, lun, tag))
+ {
+ continue;
+ }
+ /* Requeue for later processing */
+ scbq_insert_head(&p->waiting_scbs, next_scb);
+ next_scb->flags |= SCB_WAITINGQ;
+ }
+ }
}
}
+ found = aic7xxx_search_qinfifo(p, target, channel, lun, tag,
+ SCB_ABORTED | SCB_QUEUED_FOR_DONE, /* requeue */ FALSE);
+
/*
* Search waiting for selection list.
*/
{
- unsigned char next, prev;
+ unsigned char next, prev, scb_index;
- next = inb(WAITING_SCBH + base); /* Start at head of list. */
+ next = inb(p->base + WAITING_SCBH); /* Start at head of list. */
prev = SCB_LIST_NULL;
while (next != SCB_LIST_NULL)
{
- outb(next, SCBPTR + base);
- scb = (p->scb_array[inb(SCB_TAG + base)]);
- /*
- * Select the SCB.
- */
- if (aic7xxx_match_scb(scb, target, channel))
+ outb(next, p->base + SCBPTR);
+ scb_index = inb(p->base + SCB_TAG);
+ if (scb_index >= p->scb_data->numscbs)
+ {
+ panic("aic7xxx: Waiting List inconsistency; SCB index=%d, numscbs=%d\n",
+ scb_index, p->scb_data->numscbs);
+ }
+ scbp = p->scb_data->scb_array[scb_index];
+ if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
{
- next = aic7xxx_abort_waiting_scb(p, scb, prev);
+ unsigned char linked_next;
+
+ next = aic7xxx_abort_waiting_scb(p, scbp, next, prev);
+ linked_next = inb(p->base + SCB_LINKED_NEXT);
+ if (linked_next != SCB_LIST_NULL)
+ {
+ struct aic7xxx_scb *next_scb;
+ /*
+ * Requeue the waiting SCB via the waiting list.
+ */
+ next_scb = p->scb_data->scb_array[linked_next];
+ if (! aic7xxx_match_scb(next_scb, target, channel, lun, tag))
+ {
+ scbq_insert_head(&p->waiting_scbs, next_scb);
+ next_scb->flags |= SCB_WAITINGQ;
+ }
+ }
found++;
}
else
{
prev = next;
- next = inb(SCB_NEXT + base);
+ next = inb(p->base + SCB_NEXT);
+ }
+ }
+ }
+
+ /*
+ * Go through disconnected list and remove any entries we have queued
+ * for completion, zeroing their control byte too.
+ */
+ {
+ unsigned char next, prev, scb_index;
+
+ next = inb(p->base + DISCONNECTED_SCBH);
+ prev = SCB_LIST_NULL;
+
+ while (next != SCB_LIST_NULL)
+ {
+ outb(next, p->base + SCBPTR);
+ scb_index = inb(p->base + SCB_TAG);
+ if (scb_index > p->scb_data->numscbs)
+ {
+ panic("aic7xxx: Disconnected List inconsistency, SCB index = %d, "
+ "num scbs = %d.\n", scb_index, p->scb_data->numscbs);
+ }
+ scbp = p->scb_data->scb_array[scb_index];
+ if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+ {
+ next = aic7xxx_rem_scb_from_disc_list(p, next);
+ }
+ else
+ {
+ prev = next;
+ next = inb(p->base + SCB_NEXT);
+ }
+ }
+ }
+
+ /*
+ * Go through the hardware SCB array looking for commands that
+ * were active but not on any list.
+ */
+ for (i = 0; i < p->scb_data->maxhscbs; i++)
+ {
+ unsigned char scbid;
+
+ outb(i, p->base + SCBPTR);
+ scbid = inb(p->base + SCB_TAG);
+ if (scbid < p->scb_data->numscbs)
+ {
+ scbp = p->scb_data->scb_array[scbid];
+ if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+ {
+ aic7xxx_add_curscb_to_free_list(p);
}
}
}
/*
* Go through the entire SCB array now and look for commands for
- * for this target that are active. These are other (most likely
+ * for this target that are stillactive. These are other (most likely
* tagged) commands that were disconnected when the reset occurred.
*/
- for (i = 0; i < p->scb_link->numscbs; i++)
+ for (i = 0; i < p->scb_data->numscbs; i++)
{
- scb = (p->scb_array[i]);
- if ((scb->state & SCB_ACTIVE) && aic7xxx_match_scb(scb, target, channel))
+ scbp = p->scb_data->scb_array[i];
+ if (((scbp->flags & SCB_ACTIVE) != 0) &&
+ aic7xxx_match_scb(scbp, target, channel, lun, tag))
{
- /*
- * Ensure the target is "free"
- */
- aic7xxx_unbusy_target(target, channel, base);
- if (! (scb->state & SCB_PAGED_OUT))
+ scbp->flags |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
+ scbp->flags &= ~SCB_ACTIVE;
+ aic7xxx_error(scbp->cmd) = DID_RESET;
+
+ found++;
+
+ if ((scbp->flags & SCB_WAITINGQ) != 0)
{
- outb(scb->position, SCBPTR + base);
- outb(0, SCB_CONTROL + base);
+ scbq_remove(&p->waiting_scbs, scbp);
+ scbp->flags &= ~SCB_WAITINGQ;
}
- scb->state |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
- scb->cmd->result = (DID_RESET << 16);
- found++;
}
}
- outb(active_scb, SCBPTR + base);
+ outb(active_scb, p->base + SCBPTR);
return (found);
}
/*+F*************************************************************************
* Function:
+ * aic7xxx_clear_intstat
+ *
+ * Description:
+ * Clears the interrupt status.
+ *-F*************************************************************************/
+static void
+aic7xxx_clear_intstat(struct aic7xxx_host *p)
+{
+ /* Clear any interrupt conditions this may have caused. */
+ outb(CLRSELDO | CLRSELDI | CLRSELINGO, p->base + CLRSINT0);
+ outb(CLRSELTIMEO | CLRATNO | CLRSCSIRSTI | CLRBUSFREE | CLRSCSIPERR |
+ CLRPHASECHG | CLRREQINIT, p->base + CLRSINT1);
+ outb(CLRSCSIINT, p->base + CLRINT);
+}
+
+/*+F*************************************************************************
+ * Function:
* aic7xxx_reset_current_bus
*
* Description:
* Reset the current SCSI bus.
*-F*************************************************************************/
static void
-aic7xxx_reset_current_bus(int base)
+aic7xxx_reset_current_bus(struct aic7xxx_host *p)
{
- outb(SCSIRSTO, SCSISEQ + base);
+ unsigned char scsiseq;
+
+ /* Disable reset interrupts. */
+ outb(inb(p->base + SIMODE1) & ~ENSCSIRST, p->base + SIMODE1);
+
+ /* Turn on the bus reset. */
+ scsiseq = inb(p->base + SCSISEQ);
+ outb(scsiseq | SCSIRSTO, p->base + SCSISEQ);
+
+ udelay(1000);
+
+ /* Turn off the bus reset. */
+ outb(scsiseq & ~SCSIRSTO, p->base + SCSISEQ);
+
+ aic7xxx_clear_intstat(p);
+
+ /* Re-enable reset interrupts. */
+ outb(inb(p->base + SIMODE1) | ENSCSIRST, p->base + SIMODE1);
+
udelay(1000);
- outb(0, SCSISEQ + base);
}
/*+F*************************************************************************
@@ -1922,25 +2486,24 @@ aic7xxx_reset_current_bus(int base)
static int
aic7xxx_reset_channel(struct aic7xxx_host *p, char channel, int initiate_reset)
{
- int base = p->base;
- unsigned char sblkctl;
- char cur_channel;
unsigned long offset, offset_max;
int found;
+ unsigned char sblkctl;
+ char cur_channel;
+ pause_sequencer(p);
/*
- * Clean up all the state information for the
- * pending transactions on this bus.
+ * Clean up all the state information for the pending transactions
+ * on this bus.
*/
- found = aic7xxx_reset_device(p, ALL_TARGETS, channel);
+ found = aic7xxx_reset_device(p, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL);
if (channel == 'B')
{
p->needsdtr |= (p->needsdtr_copy & 0xFF00);
p->sdtr_pending &= 0x00FF;
- outb(0, ACTIVE_B + base);
- offset = TARG_SCRATCH + base + 8;
- offset_max = TARG_SCRATCH + base + 16;
+ offset = TARG_SCRATCH + 8;
+ offset_max = TARG_SCRATCH + 16;
}
else
{
@@ -1950,132 +2513,100 @@ aic7xxx_reset_channel(struct aic7xxx_host *p, char channel, int initiate_reset)
p->needwdtr = p->needwdtr_copy;
p->sdtr_pending = 0x0;
p->wdtr_pending = 0x0;
- outb(0, ACTIVE_A + base);
- outb(0, ACTIVE_B + base);
- offset = TARG_SCRATCH + base;
- offset_max = TARG_SCRATCH + base + 16;
+ offset = TARG_SCRATCH;
+ offset_max = TARG_SCRATCH + 16;
}
else
{
+ /* Channel A */
p->needsdtr |= (p->needsdtr_copy & 0x00FF);
p->sdtr_pending &= 0xFF00;
- outb(0, ACTIVE_A + base);
- offset = TARG_SCRATCH + base;
- offset_max = TARG_SCRATCH + base + 8;
+ offset = TARG_SCRATCH;
+ offset_max = TARG_SCRATCH + 8;
}
}
+
while (offset < offset_max)
{
/*
- * Revert to async/narrow transfers
- * until we renegotiate.
+ * Revert to async/narrow transfers until we renegotiate.
*/
u_char targ_scratch;
- targ_scratch = inb(offset);
+
+ targ_scratch = inb(p->base + offset);
targ_scratch &= SXFR;
- outb(targ_scratch, offset);
+ outb(targ_scratch, p->base + offset);
offset++;
}
/*
* Reset the bus and unpause/restart the controller
*/
-
- /*
- * Case 1: Command for another bus is active
- */
- sblkctl = inb(SBLKCTL + base);
+ sblkctl = inb(p->base + SBLKCTL);
cur_channel = (sblkctl & SELBUSB) ? 'B' : 'A';
if (cur_channel != channel)
{
+ /*
+ * Case 1: Command for another bus is active
+ */
#ifdef AIC7XXX_DEBUG_ABORT
- printk("aic7xxx: (reset_channel) Stealthily resetting channel %c\n",
- channel);
+ printk("scsi%d: Stealthily resetting channel %c\n",
+ p->host_no, channel);
#endif
/*
- * Stealthily reset the other bus without upsetting the current bus
+ * Stealthily reset the other bus without upsetting the current bus.
*/
- outb(sblkctl ^ SELBUSB, SBLKCTL + base);
+ outb(sblkctl ^ SELBUSB, p->base + SBLKCTL);
+ outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1);
if (initiate_reset)
{
- aic7xxx_reset_current_bus(base);
+ aic7xxx_reset_current_bus(p);
+ /*
+ * Cause the mid-level SCSI code to delay any further
+ * queueing by the bus settle time for us.
+ */
+ p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ));
}
- outb(CLRSCSIRSTI | CLRSELTIMEO, CLRSINT1 + base);
- outb(CLRSCSIINT, CLRINT + base);
- outb(sblkctl, SBLKCTL + base);
-
- UNPAUSE_SEQUENCER(p);
+ outb(0, p->base + SCSISEQ);
+ aic7xxx_clear_intstat(p);
+ outb(sblkctl, p->base + SBLKCTL);
+ unpause_sequencer(p, /* unpause_always */ FALSE);
}
- /*
- * Case 2: A command from this bus is active or we're idle
- */
else
{
+ /*
+ * Case 2: A command from this bus is active or we're idle.
+ */
#ifdef AIC7XXX_DEBUG_ABORT
- printk("aic7xxx: (reset_channel) Resetting current channel %c\n",
- channel);
+ printk("scsi%d: Resetting current channel %c\n",
+ p->host_no, channel);
#endif
+ outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1);
if (initiate_reset)
{
- aic7xxx_reset_current_bus(base);
+ aic7xxx_reset_current_bus(p);
+ /*
+ * Cause the mid-level SCSI code to delay any further
+ * queueing by the bus settle time for us.
+ */
+#if 0
+ p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ));
+#endif
}
- outb(CLRSCSIRSTI | CLRSELTIMEO, CLRSINT1 + base);
- outb(CLRSCSIINT, CLRINT + base);
- RESTART_SEQUENCER(p);
+ outb(0, p->base + SCSISEQ);
+ aic7xxx_clear_intstat(p);
+ restart_sequencer(p);
#ifdef AIC7XXX_DEBUG_ABORT
- printk("aic7xxx: (reset_channel) Channel reset, sequencer restarted\n");
+ printk("scsi%d: Channel reset, sequencer restarted\n", p->host_no);
#endif
}
/*
- * Cause the mid-level SCSI code to delay any further
- * queueing by the bus settle time for us.
- */
- p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ));
-
- /*
* Now loop through all the SCBs that have been marked for abortion,
* and call the scsi_done routines.
*/
- aic7xxx_done_aborted_scbs(p);
- return found;
-}
-
-/*+F*************************************************************************
- * Function:
- * aic7xxx_page_scb
- *
- * Description:
- * Swap in_scbp for out_scbp down in the cards SCB array.
- * We assume that the SCB for out_scbp is already selected in SCBPTR.
- *
- *-F*************************************************************************/
-static inline void
-aic7xxx_page_scb(struct aic7xxx_host *p, struct aic7xxx_scb *out_scbp,
- struct aic7xxx_scb *in_scbp)
-{
- int index;
-
- /* Page-out */
-#if 0
-printk("aic7xxx: Paging out target %d SCB and paging in target %d SCB\n",
- out_scbp->cmd->target, in_scbp->cmd->target);
-#endif
- aic7xxx_getscb(p, out_scbp);
- out_scbp->state |= SCB_PAGED_OUT;
- if (!(out_scbp->control & TAG_ENB))
- {
- /* Stick in non-tagged array */
- index = (out_scbp->target_channel_lun >> 4) |
- (out_scbp->target_channel_lun & SELBUSB);
- p->pagedout_ntscbs[index] = out_scbp;
- }
-
- /* Page-in */
- in_scbp->position = out_scbp->position;
- out_scbp->position = SCB_LIST_NULL;
- aic7xxx_putscb(p, in_scbp);
- in_scbp->state &= ~SCB_PAGED_OUT;
+ aic7xxx_run_done_queue(p, /*complete*/ TRUE);
+ return (found);
}
/*+F*************************************************************************
@@ -2083,1159 +2614,1326 @@ printk("aic7xxx: Paging out target %d SCB and paging in target %d SCB\n",
* aic7xxx_run_waiting_queues
*
* Description:
- * Scan the assigned_scbs and waiting_scbs queues. For scbs in the
- * assigned_scbs queue, we download and start them. For scbs in the
- * waiting_scbs queue, we page in as many as we can being careful
- * not to cause a deadlock for a reconnecting target.
- *
+ * Scan the awaiting_scbs queue downloading and starting as many
+ * scbs as we can.
*-F*************************************************************************/
static inline void
aic7xxx_run_waiting_queues(struct aic7xxx_host *p)
{
struct aic7xxx_scb *scb;
- u_char cur_scb, intstat;
- u_long base = p->base;
- long flags;
- if ((p->assigned_scbs.head == NULL) && (p->waiting_scbs.head == NULL))
+ if (p->waiting_scbs.head == NULL)
return;
- save_flags(flags);
- cli();
-
- PAUSE_SEQUENCER(p);
- cur_scb = inb(SCBPTR + base);
- intstat = inb(INTSTAT + base);
-
+ pause_sequencer(p);
/*
* First handle SCBs that are waiting but have been assigned a slot.
*/
- scb = p->assigned_scbs.head;
- while (scb != NULL)
- {
- scbq_remove_head(&(p->assigned_scbs));
- outb(scb->position, SCBPTR + base);
- aic7xxx_putscb(p, scb);
- /* Mark this as an active command. */
- scb->state = (scb->state & ~SCB_ASSIGNEDQ) | SCB_ACTIVE;
- outb(scb->position, QINFIFO + base);
- scb = p->assigned_scbs.head;
- }
-
- /* Now deal with SCBs that require paging. */
scb = p->waiting_scbs.head;
- if (scb != NULL)
+ while (scb != NULL)
{
- u_char disc_scb = inb(DISCONNECTED_SCBH + base);
- u_char active = inb(FLAGS + base) & (SELECTED | IDENTIFY_SEEN);
- int count = 0;
- u_char next_scb;
-
- while (scb != NULL)
+ if (p->curqincnt >= p->qfullcount)
{
- /* Attempt to page this SCB in */
- if (disc_scb == SCB_LIST_NULL)
- break;
-
- /*
- * Advance disc_scb to the next one in the list.
- */
- outb(disc_scb, SCBPTR + base);
- next_scb = inb(SCB_NEXT + base);
-
- /*
- * We have to be careful about when we allow an SCB to be paged out.
- * There must always be at least one slot availible for a reconnecting
- * target in case it references an SCB that has been paged out. Our
- * heuristic is that either the disconnected list has at least two
- * entries in it or there is one entry and the sequencer is activily
- * working on an SCB which implies that it will either complete or
- * disconnect before another reconnection can occur.
- */
- if ((next_scb != SCB_LIST_NULL) || active)
+ p->curqincnt = inb(p->base + QINCNT) & p->qcntmask;
+ if (p->curqincnt >= p->qfullcount)
{
- u_char out_scbi;
- struct aic7xxx_scb *out_scbp;
-
- scbq_remove_head(&(p->waiting_scbs));
-
- /*
- * Find the in-core SCB for the one we're paging out.
- */
- out_scbi = inb(SCB_TAG + base);
- out_scbp = (p->scb_array[out_scbi]);
-
- /* Do the page out and mark the paged in SCB as active. */
- aic7xxx_page_scb(p, out_scbp, scb);
-
- /* Mark this as an active command. */
- scb->state = (scb->state & ~SCB_WAITINGQ) | SCB_ACTIVE;
-
- /* Queue the command */
- outb(scb->position, QINFIFO + base);
- count++;
-
- /* Advance to the next disconnected SCB */
- disc_scb = next_scb;
- scb = p->waiting_scbs.head;
+ break;
}
- else
- scb = NULL;
}
- if (count)
+ /*
+ * We have some space.
+ */
+ scbq_remove_head(&(p->waiting_scbs));
+ scb->flags &= ~SCB_WAITINGQ;
+
+ outb(scb->hscb->tag, p->base + QINFIFO);
+
+ if ((p->flags & PAGE_ENABLED) != 0)
{
- /*
- * Update the head of the disconnected list.
+ /*
+ * We only care about this statistic when paging
+ * since it's impossible to overflow the qinfifo
+ * in the non-paging case.
*/
- outb(disc_scb, DISCONNECTED_SCBH + base);
- if (disc_scb != SCB_LIST_NULL)
- {
- outb(disc_scb, SCBPTR + base);
- outb(SCB_LIST_NULL, SCB_PREV + base);
- }
+ p->curqincnt++;
}
+ scb = p->waiting_scbs.head;
}
- /* Restore old position */
- outb(cur_scb, SCBPTR + base);
- /*
- * Guard against unpausing the sequencer if there is an interrupt
- * waiting to happen.
- */
- if (!(intstat & (BRKADRINT | SEQINT | SCSIINT)))
- {
- UNPAUSE_SEQUENCER(p);
- }
+ unpause_sequencer(p, FALSE);
+}
- restore_flags(flags);
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_construct_sdtr
+ *
+ * Description:
+ * Constucts a synchronous data transfer message in the message
+ * buffer on the sequencer.
+ *-F*************************************************************************/
+static void
+aic7xxx_construct_sdtr(struct aic7xxx_host *p, int start_byte,
+ unsigned char period, unsigned char offset)
+{
+ outb(MSG_EXTENDED, p->base + MSG_OUT + start_byte);
+ outb(MSG_EXT_SDTR_LEN, p->base + MSG_OUT + 1 + start_byte);
+ outb(MSG_EXT_SDTR, p->base + MSG_OUT + 2 + start_byte);
+ outb(period, p->base + MSG_OUT + 3 + start_byte);
+ outb(offset, p->base + MSG_OUT + 4 + start_byte);
+ outb(start_byte + 5, p->base + MSG_LEN);
}
/*+F*************************************************************************
* Function:
- * aic7xxx_isr
+ * aic7xxx_construct_wdtr
*
* Description:
- * SCSI controller interrupt handler.
+ * Constucts a wide data transfer message in the message buffer
+ * on the sequencer.
+ *-F*************************************************************************/
+static void
+aic7xxx_construct_wdtr(struct aic7xxx_host *p, int start_byte,
+ unsigned char bus_width)
+{
+ outb(MSG_EXTENDED, p->base + MSG_OUT + start_byte);
+ outb(MSG_EXT_WDTR_LEN, p->base + MSG_OUT + 1 + start_byte);
+ outb(MSG_EXT_WDTR, p->base + MSG_OUT + 2 + start_byte);
+ outb(bus_width, p->base + MSG_OUT + 3 + start_byte);
+ outb(start_byte + 4, p->base + MSG_LEN);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_calc_residual
*
- * NOTE: Since we declared this using SA_INTERRUPT, interrupts should
- * be disabled all through this function unless we say otherwise.
+ * Description:
+ * Calculate the residual data not yet transferred.
*-F*************************************************************************/
static void
-aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
+aic7xxx_calculate_residual (struct aic7xxx_host *p, struct aic7xxx_scb *scb)
{
- int base, intstat, actual, scb_index, run_aborted_queue = FALSE;
- struct aic7xxx_host *p;
- struct aic7xxx_scb *scb = NULL;
- short transfer;
- unsigned char ha_flags, scsi_id, bus_width;
- unsigned char offset, rate, scratch, scratch_offset;
- unsigned char max_offset, rej_byte;
- unsigned short target_mask;
- char channel;
- unsigned int addr; /* must be 32 bits */
+ struct aic7xxx_hwscb *hscb;
Scsi_Cmnd *cmd;
+ int actual;
- p = (struct aic7xxx_host *) aic7xxx_boards[irq]->hostdata;
+ cmd = scb->cmd;
+ hscb = scb->hscb;
/*
- * Search for the host with a pending interrupt. If we can't find
- * one, then we've encountered a spurious interrupt.
+ * Don't destroy valid residual information with
+ * residual coming from a check sense operation.
*/
- while ((p != NULL) && !(inb(INTSTAT + p->base) & INT_PEND))
+ if (((scb->hscb->control & DISCONNECTED) == 0) &&
+ (scb->flags & SCB_SENSE) == 0)
{
- if (p->next == NULL)
- {
- p = NULL;
- }
- else
+ /*
+ * We had an underflow. At this time, there's only
+ * one other driver that bothers to check for this,
+ * and cmd->underflow seems to be set rather half-
+ * heartedly in the higher-level SCSI code.
+ */
+ actual = aic7xxx_length(cmd, hscb->residual_SG_segment_count);
+
+ actual -= (hscb->residual_data_count[2] << 16) |
+ (hscb->residual_data_count[1] << 8) |
+ hscb->residual_data_count[0];
+
+ if (actual < cmd->underflow)
{
- p = (struct aic7xxx_host *) p->next->hostdata;
+ printk(KERN_WARNING "(scsi%d:%d:%d) Underflow - "
+ "Wanted at least %u, got %u, residual SG count %d.\n",
+ p->host_no, TC_OF_SCB(scb), cmd->underflow, actual,
+ hscb->residual_SG_segment_count);
+ aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+ aic7xxx_status(cmd) = hscb->target_status;
}
}
- if (p == NULL)
- return;
-
/*
- * Keep track of interrupts for /proc/scsi
+ * Clean out the residual information in the SCB for the
+ * next consumer.
*/
- p->isr_count++;
+ hscb->residual_data_count[2] = 0;
+ hscb->residual_data_count[1] = 0;
+ hscb->residual_data_count[0] = 0;
+ hscb->residual_SG_segment_count = 0;
+}
- if (!(p->flags & A_SCANNED) && (p->isr_count == 1))
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_handle_device_reset
+ *
+ * Description:
+ * Interrupt handler for sequencer interrupts (SEQINT).
+ *-F*************************************************************************/
+static void
+aic7xxx_handle_device_reset(struct aic7xxx_host *p, int target, char channel)
+{
+ unsigned short targ_mask;
+ unsigned char targ_scratch;
+ int scratch_offset = target;
+ int found;
+
+ if (channel == 'B')
{
- /*
- * We must only have one card at this IRQ and it must have been
- * added to the board data before the spurious interrupt occurred.
- * It is sufficient that we check isr_count and not the spurious
- * interrupt count.
- */
- printk("aic7xxx: (aic7xxx_isr) Encountered spurious interrupt.\n");
- return;
+ scratch_offset += 8;
}
-
- base = p->base;
+ targ_mask = (0x01 << scratch_offset);
/*
- * Handle all the interrupt sources - especially for SCSI
- * interrupts, we won't get a second chance at them.
+ * Go back to async/narrow transfers and renegotiate.
*/
- intstat = inb(INTSTAT + base);
+ p->needsdtr |= p->needsdtr_copy & targ_mask;
+ p->needwdtr |= p->needwdtr_copy & targ_mask;
+ p->sdtr_pending &= ~targ_mask;
+ p->wdtr_pending &= ~targ_mask;
+ targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
+ targ_scratch &= SXFR;
+ outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset);
+ found = aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
+ printk(KERN_WARNING "(scsi%d:%d:%d) Bus Device Reset delivered, "
+ "%d SCBs aborted.\n", p->host_no, target, CHAN_TO_INT(channel), found);
+ aic7xxx_run_done_queue(p, /*complete*/ TRUE);
+}
- /*
- * Indicate that we're in the interrupt handler.
- */
- p->flags |= IN_ISR;
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_handle_seqint
+ *
+ * Description:
+ * Interrupt handler for sequencer interrupts (SEQINT).
+ *-F*************************************************************************/
+static void
+aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat)
+{
+ struct aic7xxx_scb *scb;
+ unsigned short target_mask;
+ unsigned char target, scratch_offset;
+ char channel;
- if (intstat & BRKADRINT)
+ if ((inb(p->base + SEQ_FLAGS) & RESELECTED) != 0)
{
- int i;
- unsigned char errno = inb(ERROR + base);
-
- printk(KERN_ERR "scsi%d: BRKADRINT error(0x%x):\n", p->host_no, errno);
- for (i = 0; i < NUMBER(hard_error); i++)
- {
- if (errno & hard_error[i].errno)
- {
- printk(KERN_ERR " %s\n", hard_error[i].errmesg);
- }
- }
- panic("scsi%d: BRKADRINT, error 0x%x, seqaddr 0x%x.\n", p->host_no,
- inb(ERROR + base), (inb(SEQADDR1 + base) << 8) | inb(SEQADDR0 + base));
+ target = (inb(p->base + SELID) >> 4) & 0x0F;
}
+ else
+ {
+ target = (inb(p->base + SCSIID) >> 4) & 0x0F;
+ }
+ scratch_offset = target;
+ channel = 'A';
+ if (inb(p->base + SBLKCTL) & SELBUSB)
+ {
+ channel = 'B';
+ scratch_offset += 8;
+ }
+ target_mask = (0x01 << scratch_offset);
- if (intstat & SEQINT)
+ switch (intstat & SEQINT_MASK)
{
- /*
- * Although the sequencer is paused immediately on
- * a SEQINT, an interrupt for a SCSIINT condition will
- * unpaused the sequencer before this point.
- */
- PAUSE_SEQUENCER(p);
+ case NO_MATCH:
+ {
+ /*
+ * This could be for a normal abort request. Figure out
+ * which SCB we were trying to find and only give an error
+ * if we didn't ask for this to happen.
+ */
+ unsigned char scb_index;
+ unsigned char busy_scbid;
+ unsigned char arg1;
- scsi_id = (inb(SCSIID + base) >> 4) & 0x0F;
- scratch_offset = scsi_id;
- channel = 'A';
- if (inb(SBLKCTL + base) & SELBUSB)
- {
- channel = 'B';
- scratch_offset += 8;
- }
- target_mask = (0x01 << scratch_offset);
+ busy_scbid = aic7xxx_index_busy_target(p, target, channel,
+ /*unbusy*/ FALSE);
+ arg1 = inb(p->base + ARG_1);
- switch (intstat & SEQINT_MASK)
- {
- case NO_MATCH:
- if (p->flags & PAGE_ENABLED)
+ if (arg1 == SCB_LIST_NULL)
{
- /* SCB Page-in request */
- struct aic7xxx_scb *outscb;
- u_char arg_1 = inb(ARG_1 + base);
- int use_disconnected = FALSE;
-
- /*
- * The sequencer expects this value upon return. Assume
- * we will find the paged out SCB and set the value now.
- * If we don't, and one of the methods used to acquire an
- * SCB calls aic7xxx_done(), we will end up in our queue
- * routine and unpause the sequencer without giving it the
- * correct return value, which causes a hang.
- */
- outb(SCB_PAGEDIN, RETURN_1 + base);
- if (arg_1 == SCB_LIST_NULL)
- {
- /* Non-tagged command */
- int index = scsi_id;
- if (channel == 'B')
- {
- index |= SELBUSB;
- }
- scb = p->pagedout_ntscbs[index];
- }
- else
- scb = (p->scb_array[arg_1]);
+ /* untagged request */
+ scb_index = busy_scbid;
+ }
+ else
+ {
+ scb_index = arg1;
+ }
- if (!(scb->state & SCB_PAGED_OUT))
+ if (scb_index < p->scb_data->numscbs)
+ {
+ scb = p->scb_data->scb_array[scb_index];
+ if (scb->hscb->control & ABORT_SCB)
{
- printk(KERN_WARNING "scsi%d: No active paged-out SCB for reconnecting "
- "target %d, channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n",
- p->host_no, scsi_id, channel, inb(SAVED_TCL + base));
- aic7xxx_unbusy_target(scsi_id, channel, base);
- outb(CLRSELTIMEO, CLRSINT1 + base);
- outb(0, RETURN_1 + base);
+ /*
+ * We expected this. Let the busfree handler take care
+ * of this when we the abort is finially sent. Set
+ * IDENTIFY_SEEN so that the busfree handler knows that
+ * there is an SCB to cleanup.
+ */
+ outb(inb(p->base + SEQ_FLAGS) | IDENTIFY_SEEN, p->base + SEQ_FLAGS);
+ printk(KERN_INFO "(scsi%d:%d:%d) reconnect SCB abort successful\n",
+ p->host_no, TC_OF_SCB(scb));
break;
}
+ }
+ printk(KERN_WARNING "(scsi%d:%d:%d) No active SCB for reconnecting "
+ "target - Issuing BUS DEVICE RESET.\n",
+ p->host_no, target, CHAN_TO_INT(channel));
+
+ printk(KERN_WARNING " SAVED_TCL=0x%x, ARG_1=0x%x, SEQADDR=0x%x\n",
+ inb(p->base + SAVED_TCL), arg1,
+ (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0));
+ aic7xxx_handle_device_reset(p, target, channel);
+ }
+ break;
- /*
- * Now to pick the SCB to page out. Either take a free SCB, an
- * assigned SCB, an SCB that just completed, or the first one
- * on the disconnected SCB list.
- */
- if (p->scb_link->free_scbs.head != NULL)
- {
- outscb = p->scb_link->free_scbs.head;
- scbq_remove_head(&p->scb_link->free_scbs);
- scb->position = outscb->position;
- outscb->position = SCB_LIST_NULL;
- scbq_insert_head(&p->page_scbs, outscb);
- outb(scb->position, SCBPTR + base);
- aic7xxx_putscb(p, scb);
- scb->state &= ~SCB_PAGED_OUT;
- }
- else if (p->assigned_scbs.head != NULL)
- {
- outscb = p->assigned_scbs.head;
- scbq_remove_head(&p->assigned_scbs);
- scb->position = outscb->position;
- outscb->position = SCB_LIST_NULL;
- scbq_insert_head(&p->waiting_scbs, outscb);
- outscb->state = (outscb->state & ~SCB_ASSIGNEDQ) | SCB_WAITINGQ;
- outb(scb->position, SCBPTR + base);
- aic7xxx_putscb(p, scb);
- scb->state &= ~SCB_PAGED_OUT;
- }
- else if (intstat & CMDCMPLT)
- {
- int scb_index;
+ case NO_MATCH_BUSY:
+ {
+ /*
+ * XXX - Leave this as a panic for the time being since it
+ * indicates a bug in the timeout code for this to happen.
+ */
+ unsigned char scb_index;
- outb(CLRCMDINT, CLRINT + base);
- scb_index = inb(QOUTFIFO + base);
- if (!(inb(QOUTCNT + base) & p->qcntmask))
- {
- intstat &= ~CMDCMPLT;
- }
- outscb = (p->scb_array[scb_index]);
- if (!(outscb->state & SCB_ACTIVE))
+ scb_index = inb(p->base + CUR_SCBID);
+ scb = p->scb_data->scb_array[scb_index];
+
+ panic("scsi%d: Target %d, channel %c, Target busy link failure, "
+ "but busy SCB exists!\n",
+ p->host_no, target, channel);
+ }
+ break;
+
+ case SEND_REJECT:
+ {
+ unsigned char rej_byte;
+
+ rej_byte = inb(p->base + REJBYTE);
+ printk(KERN_WARNING "(scsi%d:%d:%d) Rejecting unknown message (0x%x) "
+ "received from target, SEQ_FLAGS=0x%x\n",
+ p->host_no, target, CHAN_TO_INT(channel), rej_byte,
+ inb(p->base + SEQ_FLAGS));
+ }
+ break;
+
+ case NO_IDENT:
+ {
+ /*
+ * The reconnecting target either did not send an identify
+ * message, or did, but we didn't find and SCB to match and
+ * before it could respond to our ATN/abort, it hit a dataphase.
+ * The only safe thing to do is to blow it away with a bus
+ * reset.
+ */
+ int found;
+
+ printk(KERN_WARNING "(scsi%d:%d:%d): Target did not send an IDENTIFY "
+ "message; LASTPHASE 0x%x, SAVED_TCL 0x%x\n",
+ p->host_no, target, CHAN_TO_INT(channel),
+ inb(p->base + LASTPHASE), inb(p->base + SAVED_TCL));
+
+ found = aic7xxx_reset_channel(p, channel, /*initiate reset*/ TRUE);
+
+ printk(KERN_WARNING "scsi%d: Issued channel %c bus reset; "
+ "%d SCBs aborted\n", p->host_no, channel, found);
+ }
+ break;
+
+ case BAD_PHASE:
+ if (inb(p->base + LASTPHASE) == P_BUSFREE)
+ {
+ printk(KERN_WARNING "(scsi%d:%d:%d): Missed busfree.\n",
+ p->host_no, CHAN_TO_INT(channel), target);
+ restart_sequencer(p);
+ }
+ else
+ {
+ printk(KERN_WARNING "(scsi%d:%d:%d): Unknown scsi bus phase, attempting "
+ "to continue\n", p->host_no, CHAN_TO_INT(channel), target);
+ }
+ break;
+
+ case EXTENDED_MSG:
+ {
+ unsigned char message_length;
+ unsigned char message_code;
+ unsigned char scb_index;
+
+ message_length = inb(p->base + MSGIN_EXT_LEN);
+ message_code = inb(p->base + MSGIN_EXT_OPCODE);
+ scb_index = inb(p->base + SCB_TAG);
+ scb = p->scb_data->scb_array[scb_index];
+
+ switch (message_code)
+ {
+ case MSG_EXT_SDTR:
+ {
+ unsigned char period;
+ unsigned char offset;
+ unsigned char saved_offset;
+ unsigned char targ_scratch;
+ unsigned char max_offset;
+ unsigned char rate;
+
+ if (message_length != MSG_EXT_SDTR_LEN)
{
- printk(KERN_WARNING "scsi%d: No command for completed SCB %d "
- "during NO_MATCH interrupt\n", scb_index, p->host_no);
- use_disconnected = TRUE;
+ outb(SEND_REJ, p->base + RETURN_1);
+ break;
}
+
+ period = inb(p->base + MSGIN_EXT_BYTES);
+ saved_offset = inb(p->base + MSGIN_EXT_BYTES + 1);
+ targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
+
+ if (targ_scratch & WIDEXFER)
+ max_offset = MAX_OFFSET_16BIT;
else
+ max_offset = MAX_OFFSET_8BIT;
+ offset = MIN(saved_offset, max_offset);
+
+ aic7xxx_scsirate(p, &rate, &period, &offset, target, channel);
+
+ /*
+ * Preserve the WideXfer flag.
+ */
+ targ_scratch = rate | (targ_scratch & WIDEXFER);
+
+ /*
+ * Update both the target scratch area and current SCSIRATE.
+ */
+ outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset);
+ outb(targ_scratch, p->base + SCSIRATE);
+
+ /*
+ * See if we initiated Sync Negotiation and didn't have
+ * have to fall down to async transfers.
+ */
+ if ((scb->flags & SCB_MSGOUT_SDTR) != 0)
{
- scb->position = outscb->position;
- outscb->position = SCB_LIST_NULL;
- outb(scb->position, SCBPTR + base);
- aic7xxx_putscb(p, scb);
- scb->state &= ~SCB_PAGED_OUT;
- outscb->cmd->result |= (aic7xxx_error(outscb->cmd) << 16);
- if ((outscb->cmd->flags & WAS_SENSE) &&
- !(outscb->cmd->flags & ASKED_FOR_SENSE))
+ /* We started it. */
+ if (saved_offset == offset)
{
- /*
- * Got sense information.
- */
- outscb->cmd->flags &= ASKED_FOR_SENSE;
+ /*
+ * Don't send an SDTR back to the target.
+ */
+ outb(0, p->base + RETURN_1);
+ }
+ else
+ {
+ /* We went too low - force async. */
+ outb(SEND_REJ, p->base + RETURN_1);
}
- p->device_status[TARGET_INDEX(outscb->cmd)].flags
- |= DEVICE_SUCCESS;
- aic7xxx_done(p, outscb);
}
+ else
+ {
+ /*
+ * Send our own SDTR in reply.
+ *
+ * We want to see this message as we don't expect a target
+ * to send us a SDTR request first.
+ */
+ printk(KERN_WARNING "scsi%d: Sending SDTR!!\n", p->host_no);
+ aic7xxx_construct_sdtr(p, /* start byte */ 0, period, offset);
+ outb(SEND_MSG, p->base + RETURN_1);
+ }
+ /*
+ * Clear the flags.
+ */
+ p->needsdtr &= ~target_mask;
+ break;
}
- else
- {
- use_disconnected = TRUE;
- }
- if (use_disconnected)
+
+ case MSG_EXT_WDTR:
{
- u_char tag;
- u_char next;
- u_char disc_scb = inb(DISCONNECTED_SCBH + base);
- if (disc_scb != SCB_LIST_NULL)
+ unsigned char scratch, bus_width;
+
+ if (message_length != MSG_EXT_WDTR_LEN)
{
- outb(disc_scb, SCBPTR + base);
- tag = inb(SCB_TAG + base);
- outscb = (p->scb_array[tag]);
- next = inb(SCB_NEXT + base);
- if (next != SCB_LIST_NULL)
- {
- outb(next, SCBPTR + base);
- outb(SCB_LIST_NULL, SCB_PREV + base);
- outb(disc_scb, SCBPTR + base);
- }
- outb(next, DISCONNECTED_SCBH + base);
- aic7xxx_page_scb(p, outscb, scb);
- }
- else if (inb(QINCNT + base) & p->qcntmask)
+ outb(SEND_REJ, p->base + RETURN_1);
+ break;
+ }
+
+ bus_width = inb(p->base + MSGIN_EXT_BYTES);
+ scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
+
+ if ((scb->flags & SCB_MSGOUT_WDTR) != 0)
{
- /* Pull one of our queued commands as a last resort. */
- disc_scb = inb(QINFIFO + base);
- outb(disc_scb, SCBPTR + base);
- tag = inb(SCB_TAG + base);
- outscb = (p->scb_array[tag]);
- if ((outscb->control & 0x23) != TAG_ENB)
+ /*
+ * Don't send an WDTR back to the target, since we asked first.
+ */
+ outb(0, p->base + RETURN_1);
+ switch (bus_width)
{
- /*
- * This is not a simple tagged command so its position
- * in the queue matters. Take the command at the end of
- * the queue instead.
- */
- int i;
- int saved_queue[AIC7XXX_MAXSCB];
- int queued = inb(QINCNT + base) & p->qcntmask;
-
- /* Count the command we removed already */
- saved_queue[0] = disc_scb;
- queued++;
-
- /* Empty the input queue. */
- for (i = 1; i < queued; i++)
- {
- saved_queue[i] = inb(QINFIFO + base);
- }
-
- /* Put everyone back but the last entry. */
- queued--;
- for (i = 0; i < queued; i++)
- {
- outb(saved_queue[i], QINFIFO + base);
- }
-
- outb(saved_queue[queued], SCBPTR + base);
- tag = inb(SCB_TAG + base);
- outscb = (p->scb_array[tag]);
+ case BUS_8_BIT:
+ scratch &= 0x7F;
+ break;
+
+ case BUS_16_BIT:
+ if (aic7xxx_verbose)
+ {
+ printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 "
+ "bit transfers.\n", p->host_no, target, channel);
+ }
+ scratch |= WIDEXFER;
+ break;
+
+ case BUS_32_BIT:
+ outb(SEND_REJ, p->base + RETURN_1);
+ /* No verbose here! We want to see this condition. */
+ printk(KERN_WARNING "scsi%d: Target %d, channel %c, "
+ "requesting 32 bit transfers, rejecting...\n",
+ p->host_no, target, channel);
+ break;
+
+ default:
+ break;
}
- scb->position = outscb->position;
- outscb->position = SCB_LIST_NULL;
- scbq_insert_head(&p->waiting_scbs, outscb);
- outscb->state |= SCB_WAITINGQ;
- aic7xxx_putscb(p, scb);
- scb->state &= ~SCB_PAGED_OUT;
}
else
{
- printk(KERN_WARNING "scsi%d: Page-in request with no candidates "
- "target %d, channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n",
- p->host_no, scsi_id, channel, inb(SAVED_TCL + base));
- aic7xxx_unbusy_target(scsi_id, channel, base);
- outb(CLRSELTIMEO, CLRSINT1 + base);
- outb(0, RETURN_1 + base);
+ /*
+ * Send our own WDTR in reply.
+ */
+ switch (bus_width)
+ {
+ case BUS_8_BIT:
+ scratch &= 0x7F;
+ break;
+
+ case BUS_32_BIT:
+ case BUS_16_BIT:
+ if (p->bus_type == AIC_WIDE)
+ {
+ printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 "
+ "bit transfers.\n", p->host_no, target, channel);
+ bus_width = BUS_16_BIT;
+ scratch |= WIDEXFER;
+ }
+ else
+ {
+ bus_width = BUS_8_BIT;
+ scratch &= 0x7F; /* XXX - FreeBSD doesn't do this. */
+ }
+ break;
+
+ default:
+ break;
+ }
+ aic7xxx_construct_wdtr(p, /* start byte */ 0, bus_width);
+ outb(SEND_MSG, p->base + RETURN_1);
}
- }
- }
- else
- {
- printk(KERN_WARNING "scsi%d: No active SCB for reconnecting "
- "target %d, channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n",
- p->host_no, scsi_id, channel, inb(SAVED_TCL + base));
- aic7xxx_unbusy_target(scsi_id, channel, base);
- outb(0, SCB_CONTROL + base);
- outb(CLRSELTIMEO, CLRSINT1 + base);
- outb(0, RETURN_1 + base);
- }
- break;
-
- case BAD_PHASE:
- panic("scsi%d: Unknown scsi bus phase.\n", p->host_no);
- break;
-
- case SEND_REJECT:
- rej_byte = inb(REJBYTE + base);
- if ((rej_byte & 0xF0) == 0x20)
- {
- scb_index = inb(SCB_TAG + base);
- scb = (p->scb_array[scb_index]);
- printk(KERN_WARNING "scsi%d: Tagged message received without identify."
- "Disabling tagged commands for target %d channel %c.\n",
- p->host_no, scsi_id, channel);
- scb->cmd->device->tagged_supported = 0;
- scb->cmd->device->tagged_queue = 0;
- }
- else
- {
- printk(KERN_WARNING "scsi%d: Rejecting unknown message (0x%x) received "
- "from target %d channel %c.\n",
- p->host_no, rej_byte, scsi_id, channel);
- }
- break;
+ p->needwdtr &= ~target_mask;
+ outb(scratch, p->base + TARG_SCRATCH + scratch_offset);
+ outb(scratch, p->base + SCSIRATE);
+ break;
+ } /* case MSG_EXT_WDTR */
- case NO_IDENT:
- panic("scsi%d: Target %d, channel %c, did not send an IDENTIFY "
- "message. SAVED_TCL 0x%x.\n",
- p->host_no, scsi_id, channel, inb(SAVED_TCL + base));
- break;
+ default:
+ /*
+ * Unknown extended message - reject it.
+ */
+ outb(SEND_REJ, p->base + RETURN_1);
+ break;
+ } /* switch (message_code) */
+ } /* case EXTENDED_MSG */
+ break;
- case SDTR_MSG:
- /*
- * Help the sequencer to translate the negotiated
- * transfer rate. Transfer is 1/4 the period
- * in ns as is returned by the sync negotiation
- * message. So, we must multiply by four.
- */
- transfer = (inb(ARG_1 + base) << 2);
- offset = inb(ACCUM + base);
- scratch = inb(TARG_SCRATCH + base + scratch_offset);
+ case REJECT_MSG:
+ {
/*
- * The maximum offset for a wide device is 0x08; for a
- * 8-bit bus device the maximum offset is 0x0F.
+ * What we care about here is if we had an outstanding SDTR
+ * or WDTR message for this target. If we did, this is a
+ * signal that the target is refusing negotiation.
*/
- if (scratch & WIDEXFER)
+ unsigned char targ_scratch;
+ unsigned char scb_index;
+
+ scb_index = inb(p->base + SCB_TAG);
+ scb = p->scb_data->scb_array[scb_index];
+ targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
+
+ if ((scb->flags & SCB_MSGOUT_WDTR) != 0)
{
- max_offset = 0x08;
+ /*
+ * note 8bit xfers and clear flag
+ */
+ targ_scratch &= 0x7F;
+ p->needwdtr &= ~target_mask;
+ printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing WIDE "
+ "negotiation; using 8 bit transfers.\n",
+ p->host_no, target, channel);
}
else
{
- max_offset = 0x0F;
- }
- aic7xxx_scsirate(p, &rate, transfer, MIN(offset, max_offset),
- scsi_id, channel);
- /*
- * Preserve the wide transfer flag.
- */
- scratch = rate | (scratch & WIDEXFER);
- outb(scratch, TARG_SCRATCH + base + scratch_offset);
- outb(scratch, SCSIRATE + base);
- if ((scratch & 0x0F) == 0)
- {
- /*
- * One of two things happened. Either the device requested
- * asynchronous data transfers, or it requested a synchronous
- * data transfer rate that was so low that asynchronous
- * transfers are faster (not to mention the controller won't
- * support them). In both cases the synchronous data transfer
- * rate and the offset are set to 0 indicating asynchronous
- * transfers.
- *
- * If the device requested an asynchronous transfer, then
- * accept the request. If the device is being forced to
- * asynchronous data transfers and this is the first time
- * we've seen the request, accept the request. If we've
- * already seen the request, then attempt to force
- * asynchronous data transfers by rejecting the message.
- */
- if ((offset == 0) || (p->sdtr_pending & target_mask))
+ if ((scb->flags & SCB_MSGOUT_SDTR) != 0)
{
/*
- * Device requested asynchronous transfers or we're
- * forcing asynchronous transfers for the first time.
+ * note asynch xfers and clear flag
*/
- outb(0, RETURN_1 + base);
+ targ_scratch &= 0xF0;
+ p->needsdtr &= ~target_mask;
+ printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing "
+ "synchronous negotiation; using asynchronous transfers.\n",
+ p->host_no, target, channel);
}
- else
- {
- /*
- * The first time in forcing asynchronous transfers
- * failed, so we try sending a reject message.
- */
- outb(SEND_REJ, RETURN_1 + base);
- }
- }
- else
- {
- /*
- * See if we initiated Sync Negotiation
- */
- if (p->sdtr_pending & target_mask)
- {
- /*
- * Don't send an SDTR back to the target.
- */
- outb(0, RETURN_1 + base);
- }
- else
- {
- /*
- * Send our own SDTR in reply.
- */
- printk("aic7xxx: Sending SDTR!!\n");
- outb(SEND_SDTR, RETURN_1 + base);
- }
- }
- /*
- * Clear the flags.
- */
- p->needsdtr &= ~target_mask;
- p->sdtr_pending &= ~target_mask;
- break;
-
- case WDTR_MSG:
- {
- bus_width = inb(ARG_1 + base);
- printk(KERN_INFO "scsi%d: Received MSG_WDTR, Target %d, channel %c "
- "needwdtr(0x%x).\n", p->host_no, scsi_id, channel, p->needwdtr);
- scratch = inb(TARG_SCRATCH + base + scratch_offset);
-
- if (p->wdtr_pending & target_mask)
- {
- /*
- * Don't send an WDTR back to the target, since we asked first.
- */
- outb(0, RETURN_1 + base);
- switch (bus_width)
- {
- case BUS_8_BIT:
- scratch &= 0x7F;
- break;
-
- case BUS_16_BIT:
- printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 bit "
- "transfers.\n", p->host_no, scsi_id, channel);
- scratch |= 0x80;
- break;
-
- case BUS_32_BIT:
- outb(SEND_REJ, RETURN_1 + base);
- printk(KERN_INFO "scsi%d: Target %d, channel %c, requesting 32 bit "
- "transfers, rejecting...\n", p->host_no, scsi_id, channel);
- break;
- }
- }
- else
- {
- /*
- * Send our own WDTR in reply.
- */
- printk(KERN_INFO "scsi%d: Will send WDTR!!\n", p->host_no);
- switch (bus_width)
- {
- case BUS_8_BIT:
- scratch &= 0x7F;
- break;
-
- case BUS_32_BIT:
- /*
- * Negotiate 16 bits.
- */
- bus_width = BUS_16_BIT;
- /* Yes, we mean to fall thru here. */
-
- case BUS_16_BIT:
- printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 bit "
- "transfers.\n", p->host_no, scsi_id, channel);
- scratch |= 0x80;
- break;
- }
- outb(bus_width | SEND_WDTR, RETURN_1 + base);
+ /*
+ * Otherwise, we ignore it.
+ */
}
- p->needwdtr &= ~target_mask;
- p->wdtr_pending &= ~target_mask;
- outb(scratch, TARG_SCRATCH + base + scratch_offset);
- outb(scratch, SCSIRATE + base);
- break;
+ outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset);
+ outb(targ_scratch, p->base + SCSIRATE);
}
+ break;
- case REJECT_MSG:
+ case BAD_STATUS:
{
- /*
- * What we care about here is if we had an
- * outstanding SDTR or WDTR message for this
- * target. If we did, this is a signal that
- * the target is refusing negotiation.
+ unsigned char scb_index;
+ struct aic7xxx_hwscb *hscb;
+ Scsi_Cmnd *cmd;
+
+ /* The sequencer will notify us when a command has an error that
+ * would be of interest to the kernel. This allows us to leave
+ * the sequencer running in the common case of command completes
+ * without error. The sequencer will have DMA'd the SCB back
+ * up to us, so we can reference the drivers SCB array.
*/
+ scb_index = inb(p->base + SCB_TAG);
+ scb = p->scb_data->scb_array[scb_index];
+ hscb = scb->hscb;
- scratch = inb(TARG_SCRATCH + base + scratch_offset);
-
- if (p->wdtr_pending & target_mask)
- {
- /*
- * note 8bit xfers and clear flag
- */
- scratch &= 0x7F;
- p->needwdtr &= ~target_mask;
- p->wdtr_pending &= ~target_mask;
- printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing WIDE "
- "negotiation; using 8 bit transfers.\n",
- p->host_no, scsi_id, channel);
- }
- else
- {
- if (p->sdtr_pending & target_mask)
- {
- /*
- * note asynch xfers and clear flag
- */
- scratch &= 0xF0;
- p->needsdtr &= ~target_mask;
- p->sdtr_pending &= ~target_mask;
- printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing "
- "synchronous negotiation; using asynchronous transfers.\n",
- p->host_no, scsi_id, channel);
- }
- /*
- * Otherwise, we ignore it.
- */
- }
- outb(scratch, TARG_SCRATCH + base + scratch_offset);
- outb(scratch, SCSIRATE + base);
- break;
- }
-
- case BAD_STATUS:
- /* The sequencer will notify us when a command has an error that
- * would be of interest to the kernel. This allows us to leave
- * the sequencerrunning in the common case of command completes
- * without error.
- */
-
- scb_index = inb(SCB_TAG + base);
- scb = (p->scb_array[scb_index]);
- outb(0, RETURN_1 + base); /* CHECK_CONDITION may change this */
- if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
+ /*
+ * Set the default return value to 0 indicating not to send
+ * sense. The sense code will change this if needed and this
+ * reduces code duplication.
+ */
+ outb(0, p->base + RETURN_1);
+ if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
{
- printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
- "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no,
- intstat, scb_index, scb->state, (unsigned long) scb->cmd);
+ printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
+ "SEQINT 0x%x, scb %d, flags 0x%x, cmd 0x%x.\n", p->host_no,
+ intstat, scb_index, scb->flags, (unsigned int) scb->cmd);
}
else
{
- cmd = scb->cmd;
- scb->target_status = inb(SCB_TARGET_STATUS + base);
- aic7xxx_status(cmd) = scb->target_status;
+ cmd = scb->cmd;
+ hscb->target_status = inb(p->base + SCB_TARGET_STATUS);
+ aic7xxx_status(cmd) = hscb->target_status;
- cmd->result |= scb->target_status;
+ cmd->result |= hscb->target_status;
- switch (status_byte(scb->target_status))
- {
- case GOOD:
- printk(KERN_WARNING "aic7xxx: Interrupted for status of GOOD???\n");
- break;
-
- case CHECK_CONDITION:
- if ((aic7xxx_error(cmd) == 0) && !(cmd->flags & WAS_SENSE))
- {
- unsigned char tcl;
- unsigned int req_buf; /* must be 32 bits */
+ switch (status_byte(hscb->target_status))
+ {
+ case GOOD:
+ printk(KERN_WARNING "(scsi%d:%d:%d) Interrupted for status of "
+ "GOOD???\n", p->host_no, TC_OF_SCB(scb));
+ break;
- tcl = scb->target_channel_lun;
+ case CHECK_CONDITION:
+ if ((aic7xxx_error(cmd) == 0) && !(scb->flags & SCB_SENSE))
+ {
+ unsigned int addr; /* must be 32 bits */
+ /*
+ * XXX - How do we save the residual (if there is one).
+ */
+ aic7xxx_calculate_residual(p, scb);
+
+ /*
+ * Send a sense command to the requesting target.
+ * XXX - revisit this and get rid of the memcopys.
+ */
+ memcpy((void *) scb->sense_cmd, (void *) generic_sense,
+ sizeof(generic_sense));
+
+ scb->sense_cmd[1] = (cmd->lun << 5);
+ scb->sense_cmd[4] = sizeof(cmd->sense_buffer);
+
+ scb->sg_list[0].address = VIRT_TO_BUS(&cmd->sense_buffer);
+ scb->sg_list[0].length = sizeof(cmd->sense_buffer);
+ cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]);
- /*
- * Send a sense command to the requesting target.
+ /*
+ * XXX - We should allow disconnection, but can't as it
+ * might allow overlapped tagged commands.
*/
- cmd->flags |= WAS_SENSE;
- memcpy((void *) scb->sense_cmd, (void *) generic_sense,
- sizeof(generic_sense));
-
- scb->sense_cmd[1] = (cmd->lun << 5);
- scb->sense_cmd[4] = sizeof(cmd->sense_buffer);
-
- scb->sg_list[0].address = VIRT_TO_BUS(&cmd->sense_buffer);
- scb->sg_list[0].length = sizeof(cmd->sense_buffer);
- req_buf = VIRT_TO_BUS(&scb->sg_list[0]);
- cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]);
-
- scb->control = scb->control & DISCENB;
- scb->target_channel_lun = tcl;
- addr = VIRT_TO_BUS(scb->sense_cmd);
- scb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]);
- memcpy(scb->SCSI_cmd_pointer, &addr,
- sizeof(scb->SCSI_cmd_pointer));
- scb->SG_segment_count = 1;
- memcpy(scb->SG_list_pointer, &req_buf,
- sizeof(scb->SG_list_pointer));
- scb->data_count = scb->sg_list[0].length;
- memcpy(scb->data_pointer, &(scb->sg_list[0].address),
- sizeof(scb->data_pointer));
-
- aic7xxx_putscb(p, scb);
+ /* hscb->control &= DISCENB; */
+ hscb->control = 0;
+ hscb->target_status = 0;
+ hscb->SG_segment_count = 1;
+
+ addr = VIRT_TO_BUS(&scb->sg_list[0]);
+ memcpy(&hscb->SG_list_pointer, &addr,
+ sizeof(hscb->SG_list_pointer));
+
+ memcpy(&hscb->data_pointer, &(scb->sg_list[0].address),
+ sizeof(hscb->data_pointer));
+ /* Maintain SCB_LINKED_NEXT */
+ hscb->data_count &= 0xFF000000;
+ hscb->data_count |= scb->sg_list[0].length;
+
+ addr = VIRT_TO_BUS(scb->sense_cmd);
+ memcpy(&hscb->SCSI_cmd_pointer, &addr,
+ sizeof(hscb->SCSI_cmd_pointer));
+ hscb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]);
+
+ scb->sg_count = hscb->SG_segment_count;
+ scb->flags |= SCB_SENSE;
/*
- * Ensure that the target is "BUSY" so we don't get overlapping
- * commands if we happen to be doing tagged I/O.
+ * Ensure the target is busy since this will be an
+ * an untagged request.
*/
- aic7xxx_busy_target(scsi_id, channel, base);
+ aic7xxx_busy_target(p, target, channel, hscb->tag);
+ outb(SEND_SENSE, p->base + RETURN_1);
+ } /* first time sense, no errors */
+ else
+ {
+ if (aic7xxx_error(cmd) == 0)
+ {
+ aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+ }
+ }
+ break;
- aic7xxx_add_waiting_scb(base, scb);
- outb(SEND_SENSE, RETURN_1 + base);
- } /* first time sense, no errors */
- else
+ case QUEUE_FULL:
+#ifdef NOT_YET
+ if (scb->hscb->control & TAG_ENB)
{
- cmd->flags &= ~ASKED_FOR_SENSE;
- if (aic7xxx_error(cmd) == 0)
- {
- aic7xxx_error(cmd) = DID_RETRY_COMMAND;
- }
+ if (cmd->device->queue_depth > 2)
+ {
+ cmd->device->queue_depth--; /* Not correct */
+ printk(KERN_WARNING "(scsi%d:%d:%d) Tagged queue depth "
+ "reduced to %d\n", p->host_no,
+ TC_OF_SCB(scb), cmd->device->queue_depth);
+ }
+ /*
+ * XXX - Requeue this unconditionally?
+ */
+
+ /*
+ * We'd like to be able to give the SCB some more time
+ * (untimeout, then timeout).
+ */
+ break;
}
- break;
-
- case BUSY:
- printk(KERN_WARNING "scsi%d: Target busy, TCL=0x%x.\n",
- p->host_no, scb->target_channel_lun);
- if (!aic7xxx_error(cmd))
- {
- /* The error code here used to be DID_BUS_BUSY,
- * but after extensive testing, it has been determined
- * that a DID_BUS_BUSY return is a waste of time. If
- * the problem is something that will go away, then it
- * will, if it isn't, then you don't want the endless
- * looping that you get with a DID_BUS_BUSY. Better
- * to be on the safe side and specify an error condition
- * that will eventually lead to a reset or abort of some
- * sort instead of an endless loop.
- */
- aic7xxx_error(cmd) = DID_RETRY_COMMAND;
- }
- break;
-
- case QUEUE_FULL:
- printk(KERN_WARNING "scsi%d: Queue full.\n", p->host_no);
- scb->state |= SCB_ASSIGNEDQ;
- scbq_insert_tail(&p->assigned_scbs, scb);
- break;
-
- default:
- printk(KERN_WARNING "scsi%d: Unexpected target status 0x%x.\n",
- p->host_no, scb->target_status);
- if (!aic7xxx_error(cmd))
- {
- aic7xxx_error(cmd) = DID_RETRY_COMMAND;
- }
- break;
- } /* end switch */
+#endif
+ printk(KERN_WARNING "(scsi%d:%d:%d) Queue full received; "
+ "queue depth %d, active %d\n", p->host_no,
+ TC_OF_SCB(scb), cmd->device->queue_depth,
+ p->device_status[TARGET_INDEX(cmd)].active_cmds);
+
+ /* Else treat this as if it was a BUSY condition. */
+ scb->hscb->target_status = (BUSY << 1) |
+ (scb->hscb->target_status & 0x01);
+ /* Fall through to the BUSY case. */
+
+ case BUSY:
+ printk(KERN_WARNING "(scsi%d:%d:%d) Target busy\n",
+ p->host_no, TC_OF_SCB(scb));
+ if (!aic7xxx_error(cmd))
+ {
+ /*
+ * The mid-level SCSI code should be fixed to
+ * retry the command at a later time instead of
+ * trying right away.
+ */
+ aic7xxx_error(cmd) = DID_BUS_BUSY | (SUGGEST_RETRY << 8);
+ }
+ udelay(1000); /* A small pause (1ms) to help the drive */
+ break;
+
+ default:
+ printk(KERN_WARNING "(scsi%d:%d:%d) Unexpected target "
+ "status 0x%x.\n", p->host_no,
+ TC_OF_SCB(scb), scb->hscb->target_status);
+ if (!aic7xxx_error(cmd))
+ {
+ aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+ }
+ break;
+ } /* end switch */
} /* end else of */
- break;
+ }
+ break;
- case RESIDUAL:
- scb_index = inb(SCB_TAG + base);
- scb = (p->scb_array[scb_index]);
- if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
- {
- printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
- "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no,
- intstat, scb_index, scb->state, (unsigned long) scb->cmd);
- }
- else
- {
- cmd = scb->cmd;
- /*
- * Don't destroy valid residual information with
- * residual coming from a check sense operation.
- */
- if (!(cmd->flags & WAS_SENSE))
- {
- /*
- * We had an underflow. At this time, there's only
- * one other driver that bothers to check for this,
- * and cmd->underflow seems to be set rather half-
- * heartedly in the higher-level SCSI code.
- */
- actual = aic7xxx_length(cmd, scb->residual_SG_segment_count);
-
- actual -= (inb(SCB_RESID_DCNT2 + base) << 16) |
- (inb(SCB_RESID_DCNT1 + base) << 8) |
- inb(SCB_RESID_DCNT0 + base);
-
- if (actual < cmd->underflow)
- {
- printk(KERN_WARNING "scsi%d: Target %d underflow - "
- "Wanted at least %u, got %u, residual SG count %d.\n",
- p->host_no, cmd->target, cmd->underflow, actual,
- inb(SCB_RESID_SGCNT + base));
- aic7xxx_error(cmd) = DID_RETRY_COMMAND;
- aic7xxx_status(cmd) = scb->target_status;
- }
- }
- }
- break;
+ case AWAITING_MSG:
+ {
+ unsigned char scb_index;
+ unsigned char message_offset;
- case ABORT_TAG:
- scb_index = inb(SCB_TAG + base);
- scb = (p->scb_array[scb_index]);
- if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
- {
- printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
- "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx\n", p->host_no,
- intstat, scb_index, scb->state, (unsigned long) scb->cmd);
- }
- else
- {
- cmd = scb->cmd;
- /*
- * We didn't receive a valid tag back from the target
- * on a reconnect.
- */
- printk("scsi%d: Invalid tag received on target %d, channel %c, "
- "lun %d - Sending ABORT_TAG.\n", p->host_no,
- scsi_id, channel, cmd->lun & 0x07);
-
- cmd->result = (DID_RETRY_COMMAND << 16);
- aic7xxx_done(p, scb);
- }
- break;
+ scb_index = inb(p->base + SCB_TAG);
+ scb = p->scb_data->scb_array[scb_index];
- case AWAITING_MSG:
- scb_index = inb(SCB_TAG + base);
- scb = (p->scb_array[scb_index]);
- if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
+ /*
+ * This SCB had a MK_MESSAGE set in its control byte informing
+ * the sequencer that we wanted to send a special message to
+ * this target.
+ */
+ message_offset = inb(p->base + MSG_LEN);
+ if (scb->flags & SCB_DEVICE_RESET)
{
- printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
- "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no,
- intstat, scb_index, scb->state, (unsigned long) scb->cmd);
+ outb(MSG_BUS_DEV_RESET, p->base + MSG_OUT);
+ outb(1, p->base + MSG_LEN);
+ printk(KERN_INFO "(scsi%d:%d:%d) Bus device reset sent\n",
+ p->host_no, TC_OF_SCB(scb));
}
- else
+ else if (scb->flags & SCB_ABORT)
+ {
+ if ((scb->hscb->control & TAG_ENB) != 0)
+ {
+ outb(MSG_ABORT_TAG, p->base + MSG_OUT + message_offset);
+ }
+ else
+ {
+ outb(MSG_ABORT, p->base + MSG_OUT + message_offset);
+ }
+ outb(message_offset + 1, p->base + MSG_LEN);
+ printk(KERN_WARNING "(scsi%d:%d:%d): Abort message sent.\n",
+ p->host_no, TC_OF_SCB(scb));
+ }
+ else if (scb->flags & SCB_MSGOUT_WDTR)
{
- /*
- * This SCB had a zero length command, informing the sequencer
- * that we wanted to send a special message to this target.
- * We only do this for BUS_DEVICE_RESET messages currently.
- */
- if (scb->state & SCB_DEVICE_RESET)
- {
-#ifdef AIC7XXX_DEBUG_ABORT
- printk ("aic7xxx: (isr) sending bus device reset to target %d\n",
- scsi_id);
-#endif
- outb(MSG_BUS_DEVICE_RESET, MSG0 + base);
- outb(1, MSG_LEN + base);
- }
- else
- {
- panic("scsi%d: AWAITING_SCB for an SCB that does "
- "not have a waiting message.\n", p->host_no);
- }
- }
- break;
-
- case IMMEDDONE:
- scb_index = inb(SCB_TAG + base);
- scb = (p->scb_array[scb_index]);
-#ifdef AIC7XXX_DEBUG_ABORT
- printk("aic7xxx: received IMMEDDONE for target %d, scb %d, state %d\n",
- scsi_id, scb_index, scb->state);
-#endif
- if (scb->state & SCB_DEVICE_RESET)
+ aic7xxx_construct_wdtr(p, message_offset, BUS_16_BIT);
+ }
+ else if (scb->flags & SCB_MSGOUT_SDTR)
{
- int found;
+ unsigned char target_scratch;
+ unsigned short ultra_enable;
+ int i, sxfr;
/*
- * Go back to async/narrow transfers and renegotiate.
+ * Pull the user defined setting from scratch RAM.
*/
- aic7xxx_unbusy_target(scsi_id, channel, base);
- p->needsdtr |= (p->needsdtr_copy & target_mask);
- p->needwdtr |= (p->needwdtr_copy & target_mask);
- p->sdtr_pending &= ~target_mask;
- p->wdtr_pending &= ~target_mask;
- scratch = inb(TARG_SCRATCH + base + scratch_offset);
- scratch &= SXFR;
- outb(scratch, TARG_SCRATCH + base + scratch_offset);
- found = aic7xxx_reset_device(p, (int) scsi_id, channel);
- printk(KERN_INFO "scsi%d: Bus Device Reset delivered, %d SCBs "
- "aborted.\n", p->host_no, found);
- /* Indicate that we want to call aic7xxx_done_aborted_scbs() */
- run_aborted_queue = TRUE;
+ target_scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
+ sxfr = target_scratch & SXFR;
+ ultra_enable = inb(p->base + ULTRA_ENB) |
+ (inb(p->base + ULTRA_ENB + 1) << 8);
+ if (ultra_enable & target_mask)
+ {
+ sxfr |= 0x100;
+ }
+ for (i = 0; i < num_aic7xxx_syncrates; i++)
+ {
+ if (sxfr == aic7xxx_syncrates[i].rate)
+ break;
+ }
+ aic7xxx_construct_sdtr(p, message_offset,
+ aic7xxx_syncrates[i].period,
+ target_scratch & WIDEXFER ?
+ MAX_OFFSET_16BIT : MAX_OFFSET_8BIT);
}
- else
+ else
{
- panic("scsi%d: Immediate complete for unknown operation.\n",
- p->host_no);
- }
- break;
+ panic("aic7xxx: AWAITING_MSG for an SCB that does "
+ "not have a waiting message.");
+ }
+ }
+ break;
- case DATA_OVERRUN:
+ case DATA_OVERRUN:
{
- unsigned int overrun;
-
- scb = (p->scb_array[inb(base + SCB_TAG)]);
- overrun = inb(base + STCNT0) | (inb(base + STCNT1) << 8) |
- (inb(base + STCNT2) << 16);
- overrun =0x00FFFFFF - overrun;
- printk(KERN_WARNING "scsi%d: data overrun of %d bytes detected; forcing "
- "a retry.\n", p->host_no, overrun);
- aic7xxx_error(scb->cmd) = DID_RETRY_COMMAND;
- break;
+ unsigned char scb_index = inb(p->base + SCB_TAG);
+ unsigned char lastphase = inb(p->base + LASTPHASE);
+ unsigned int i, overrun;
+
+ scb = (p->scb_data->scb_array[scb_index]);
+ overrun = inb(p->base + STCNT) | (inb(p->base + STCNT + 1) << 8) |
+ (inb(p->base + STCNT + 2) << 16);
+ overrun = 0x00FFFFFF - overrun;
+ printk(KERN_WARNING "(scsi%d:%d:%d) Data overrun of %d bytes detected "
+ "in %s phase, tag %d; forcing a retry.\n",
+ p->host_no, TC_OF_SCB(scb), overrun,
+ lastphase == P_DATAIN ? "Data-In" : "Data-Out",
+ scb->hscb->tag);
+ printk(KERN_WARNING "%s seen Data Phase. Length = %d, NumSGs = %d.\n",
+ inb(p->base + SEQ_FLAGS) & DPHASE ? "Have" : "Haven't",
+ aic7xxx_length(scb->cmd, 0), scb->sg_count);
+ for (i = 0; i < scb->sg_count; i++)
+ {
+ printk(KERN_INFO " sg[%d] - Addr 0x%x : Length %d\n",
+ i, scb->sg_list[i].address, scb->sg_list[i].length);
+ }
+ /*
+ * XXX - What do we really want to do on an overrun? The
+ * mid-level SCSI code should handle this, but for now,
+ * we'll just indicate that the command should retried.
+ */
+ aic7xxx_error(scb->cmd) = DID_RETRY_COMMAND;
}
+ break;
-#if AIC7XXX_NOT_YET
- /* XXX Fill these in later */
- case MESG_BUFFER_BUSY:
- break;
- case MSGIN_PHASEMIS:
- break;
-#endif
+/* #if AIC7XXX_NOT_YET */
+ /* XXX Fill these in later */
+ case MSG_BUFFER_BUSY:
+ printk("aic7xxx: Message buffer busy.\n");
+ break;
+ case MSGIN_PHASEMIS:
+ printk("aic7xxx: Message-in phasemis.\n");
+ break;
+/*#endif */
+
+ case ABORT_CMDCMPLT:
+ /* This interrupt serves to pause the sequencer until we can clean
+ * up the QOUTFIFO allowing us to handle any abort SCBs that may
+ * completed yet still have an SCB in the QINFIFO or waiting for
+ * selection queue. By the time we get here, we should have
+ * already cleaned up the queues, so all we need to do is unpause
+ * the sequencer.
+ */
+ break;
+
+ default: /* unknown */
+ printk(KERN_WARNING "scsi%d: SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n",
+ p->host_no, intstat, inb(p->base + SCSISIGI));
+ break;
+ }
+
+ /*
+ * Clear the sequencer interrupt and unpause the sequencer.
+ */
+ outb(CLRSEQINT, p->base + CLRINT);
+ unpause_sequencer(p, /* unpause always */ TRUE);
+}
- default: /* unknown */
- printk(KERN_WARNING "scsi%d: SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n",
- p->host_no, intstat, inb(SCSISIGI + base));
- break;
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_handle_scsiint
+ *
+ * Description:
+ * Interrupt handler for SCSI interrupts (SCSIINT).
+ *-F*************************************************************************/
+static void
+aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat)
+{
+ unsigned char scb_index;
+ unsigned char status;
+ struct aic7xxx_scb *scb;
+
+ scb_index = inb(p->base + SCB_TAG);
+ status = inb(p->base + SSTAT1);
+
+ if (scb_index < p->scb_data->numscbs)
+ {
+ scb = p->scb_data->scb_array[scb_index];
+ if ((scb->flags & SCB_ACTIVE) == 0)
+ {
+ scb = NULL;
}
+ }
+ else
+ {
+ scb = NULL;
+ }
+
+ if ((status & SCSIRSTI) != 0)
+ {
+ char channel;
+ channel = (inb(p->base + SBLKCTL) & SELBUSB) ? 'B' : 'A';
+
+ printk(KERN_WARNING "scsi%d: SCSIINT - Someone reset channel %c.\n",
+ p->host_no, channel);
/*
- * Clear the sequencer interrupt and unpause the sequencer.
+ * Go through and abort all commands for the channel, but do not
+ * reset the channel again.
*/
- outb(CLRSEQINT, CLRINT + base);
- UNPAUSE_SEQUENCER(p);
+ aic7xxx_reset_channel(p, channel, /* Initiate Reset */ FALSE);
+ scb = NULL;
}
-
- if (intstat & SCSIINT)
+ else if ( ((status & BUSFREE) != 0) && ((status & SELTO) == 0) )
{
- int status = inb(SSTAT1 + base);
- scsi_id = (inb(SCSIID + base) >> 4) & 0x0F;
- channel = 'A';
- if (inb(SBLKCTL + base) & SELBUSB)
- {
- channel = 'B';
- }
+ /*
+ * First look at what phase we were last in. If it's message-out,
+ * chances are pretty good that the bus free was in response to
+ * one of our abort requests.
+ */
+ unsigned char lastphase = inb(p->base + LASTPHASE);
+ unsigned char target = (inb(p->base + SAVED_TCL) >> 4) & 0x0F;
+ char channel = (inb(p->base + SBLKCTL) & SELBUSB) ? 'B' : 'A';
+ int printerror = TRUE;
- scb_index = inb(SCB_TAG + base);
- scb = (p->scb_array[scb_index]);
- if (status & SCSIRSTI)
- {
- PAUSE_SEQUENCER(p);
- printk(KERN_WARNING "scsi%d: SCSIINT - Someone reset channel %c.\n",
- p->host_no, channel);
- /*
- * Go through and abort all commands for the channel, but do not
- * reset the channel again.
- */
- aic7xxx_reset_channel(p, channel, FALSE);
- run_aborted_queue = TRUE;
- }
- else if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
+ outb(0, p->base + SCSISEQ);
+ if (lastphase == P_MESGOUT)
{
- printk(KERN_WARNING "scsi%d: SCSIINT - No command for SCB.\n", p->host_no);
- /*
- * Turn off the interrupt and set status to zero, so that it
- * falls through the rest of the SCSIINT code.
- */
- outb(status, CLRSINT1 + base);
- UNPAUSE_SEQUENCER(p);
- outb(CLRSCSIINT, CLRINT + base);
- scb = NULL;
+ unsigned char sindex;
+ unsigned char message;
+
+ sindex = inb(p->base + SINDEX);
+ message = inb(p->base + sindex - 1);
+
+ if (message == MSG_ABORT)
+ {
+ printk(KERN_WARNING "(scsi%d:%d:%d) SCB %d abort completed.\n",
+ p->host_no, TC_OF_SCB(scb), scb->hscb->tag);
+ aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), SCB_LIST_NULL);
+ aic7xxx_run_done_queue(p, /* complete */ TRUE);
+ scb = NULL;
+ printerror = 0;
+ }
+ else if (message == MSG_ABORT_TAG)
+ {
+ printk(KERN_WARNING "(scsi%d:%d:%d) SCB %d abort Tag completed.\n",
+ p->host_no, TC_OF_SCB(scb), scb->hscb->tag);
+ aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), scb->hscb->tag);
+ aic7xxx_run_done_queue(p, /* complete */ TRUE);
+ scb = NULL;
+ printerror = 0;
+ }
+ else if (message == MSG_BUS_DEV_RESET)
+ {
+ aic7xxx_handle_device_reset(p, target, channel);
+ scb = NULL;
+ printerror = 0;
+ }
}
- else if (status & SCSIPERR)
+ if (printerror != 0)
{
- char *phase;
- unsigned char mesg_out = MSG_NOP;
- unsigned char lastphase = inb(LASTPHASE + base);
+ if (scb != NULL)
+ {
+ unsigned char tag;
- cmd = scb->cmd;
- switch (lastphase)
+ if ((scb->hscb->control & TAG_ENB) != 0)
+ {
+ tag = scb->hscb->tag;
+ }
+ else
+ {
+ tag = SCB_LIST_NULL;
+ }
+ aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), tag);
+ }
+ else
{
- case P_DATAOUT:
- phase = "Data-Out";
- break;
- case P_DATAIN:
- phase = "Data-In";
- mesg_out = MSG_INITIATOR_DET_ERROR;
- break;
- case P_COMMAND:
- phase = "Command";
- break;
- case P_MESGOUT:
- phase = "Message-Out";
- break;
- case P_STATUS:
- phase = "Status";
- mesg_out = MSG_INITIATOR_DET_ERROR;
- break;
- case P_MESGIN:
- phase = "Message-In";
- mesg_out = MSG_MSG_PARITY_ERROR;
- break;
- default:
- phase = "unknown";
- break;
+ aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
}
+ printk(KERN_WARNING "scsi%d: Unexpected busfree, LASTPHASE = 0x%x, "
+ "SEQADDR = 0x%x\n", p->host_no, lastphase,
+ (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0));
+ }
+ outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1);
+ outb(CLRBUSFREE, p->base + CLRSINT1);
+ outb(CLRSCSIINT, p->base + CLRINT);
+ restart_sequencer(p);
+ }
+ else if ((status & SELTO) != 0)
+ {
+ unsigned char scbptr;
+ unsigned char nextscb;
+ Scsi_Cmnd *cmd;
- /*
- * A parity error has occurred during a data
- * transfer phase. Flag it and continue.
- */
- printk(KERN_WARNING "scsi%d: Parity error during phase %s on target %d, "
- "channel %d, lun %d.\n", p->host_no, phase,
- cmd->target, cmd->channel & 0x01, cmd->lun & 0x07);
+ scbptr = inb(p->base + WAITING_SCBH);
+ outb(scbptr, p->base + SCBPTR);
+ scb_index = inb(p->base + SCB_TAG);
- /*
- * We've set the hardware to assert ATN if we get a parity
- * error on "in" phases, so all we need to do is stuff the
- * message buffer with the appropriate message. In phases
- * have set mesg_out to something other than MSG_NOP.
- */
- if (mesg_out != MSG_NOP)
- {
- outb(mesg_out, MSG0 + base);
- outb(1, MSG_LEN + base);
- cmd->result = DID_PARITY << 16;
- }
- else
+ scb = NULL;
+ if (scb_index < p->scb_data->numscbs)
+ {
+ scb = p->scb_data->scb_array[scb_index];
+ if ((scb->flags & SCB_ACTIVE) == 0)
{
- /*
- * Should we allow the target to make this decision for us?
- */
- cmd->result = DID_RETRY_COMMAND << 16;
+ scb = NULL;
}
- aic7xxx_done(p, scb);
}
- else if (status & SELTO)
+ if (scb == NULL)
{
- unsigned char waiting;
-
+ printk(KERN_WARNING "scsi%d: Referenced SCB %d not valid during SELTO.\n",
+ p->host_no, scb_index);
+ printk(KERN_WARNING " SCSISEQ = 0x%x SEQADDR = 0x%x SSTAT0 = 0x%x "
+ "SSTAT1 = 0x%x\n", inb(p->base + SCSISEQ),
+ inb(p->base + SEQADDR0) | (inb(p->base + SEQADDR1) << 8),
+ inb(p->base + SSTAT0), inb(p->base + SSTAT1));
+ }
+ else
+ {
+ /*
+ * XXX - If we queued an abort tag, go clean up the disconnected list.
+ */
cmd = scb->cmd;
-
cmd->result = (DID_TIME_OUT << 16);
+
/*
* Clear an pending messages for the timed out
* target and mark the target as free.
*/
- ha_flags = inb(FLAGS + base);
- outb(0, MSG_LEN + base);
- aic7xxx_unbusy_target(scsi_id, channel, base);
+ outb(0, p->base + MSG_LEN);
+ aic7xxx_index_busy_target(p, cmd->target,
+ cmd->channel ? 'B': 'A', /*unbusy*/ TRUE);
+ outb(0, p->base + SCB_CONTROL);
+
/*
- * Stop the selection.
+ * Shift the waiting for selection queue forward
*/
- outb(0, SCSISEQ + base);
- outb(0, SCB_CONTROL + base);
- outb(CLRSELTIMEO, CLRSINT1 + base);
- outb(CLRSCSIINT, CLRINT + base);
+ nextscb = inb(p->base + SCB_NEXT);
+ outb(nextscb, p->base + WAITING_SCBH);
/*
- * Shift the waiting for selection queue forward
+ * Put this SCB back on the free list.
*/
- waiting = inb(WAITING_SCBH + base);
- outb(waiting, SCBPTR + base);
- waiting = inb(SCB_NEXT + base);
- outb(waiting, WAITING_SCBH + base);
+ aic7xxx_add_curscb_to_free_list(p);
+ }
+ /*
+ * Stop the selection.
+ */
+ outb(0, p->base + SCSISEQ);
+ outb(CLRSELTIMEO | CLRBUSFREE, p->base + CLRSINT1);
+ outb(CLRSCSIINT, p->base + CLRINT);
+ restart_sequencer(p);
+ }
+ else if (scb == NULL)
+ {
+ printk(KERN_WARNING "scsi%d: aic7xxx_isr - referenced scb not valid "
+ "during scsiint 0x%x scb(%d)\n"
+ " SIMODE0 0x%x, SIMODE1 0x%x, SSTAT0 0x%x, SEQADDR 0x%x\n",
+ p->host_no, status, scb_index, inb(p->base + SIMODE0),
+ inb(p->base + SIMODE1), inb(p->base + SSTAT0),
+ (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0));
+ /*
+ * Turn off the interrupt and set status to zero, so that it
+ * falls through the rest of the SCSIINT code.
+ */
+ outb(status, p->base + CLRSINT1);
+ outb(CLRSCSIINT, p->base + CLRINT);
+ unpause_sequencer(p, /* unpause always */ TRUE);
+ scb = NULL;
+ }
+ else if (status & SCSIPERR)
+ {
+ /*
+ * Determine the bus phase and queue an appropriate message.
+ */
+ char *phase;
+ Scsi_Cmnd *cmd;
+ unsigned char mesg_out = MSG_NOOP;
+ unsigned char lastphase = inb(p->base + LASTPHASE);
- RESTART_SEQUENCER(p);
- aic7xxx_done(p, scb);
+ cmd = scb->cmd;
+ switch (lastphase)
+ {
+ case P_DATAOUT:
+ phase = "Data-Out";
+ break;
+ case P_DATAIN:
+ phase = "Data-In";
+ mesg_out = MSG_INITIATOR_DET_ERR;
+ break;
+ case P_COMMAND:
+ phase = "Command";
+ break;
+ case P_MESGOUT:
+ phase = "Message-Out";
+ break;
+ case P_STATUS:
+ phase = "Status";
+ mesg_out = MSG_INITIATOR_DET_ERR;
+ break;
+ case P_MESGIN:
+ phase = "Message-In";
+ mesg_out = MSG_PARITY_ERROR;
+ break;
+ default:
+ phase = "unknown";
+ break;
+ }
+
+ /*
+ * A parity error has occurred during a data
+ * transfer phase. Flag it and continue.
+ */
+ printk(KERN_WARNING "(scsi%d:%d:%d) Parity error during phase %s.\n",
+ p->host_no, TC_OF_SCB(scb), phase);
+
+ /*
+ * We've set the hardware to assert ATN if we get a parity
+ * error on "in" phases, so all we need to do is stuff the
+ * message buffer with the appropriate message. "In" phases
+ * have set mesg_out to something other than MSG_NOP.
+ */
+ if (mesg_out != MSG_NOOP)
+ {
+ outb(mesg_out, p->base + MSG_OUT);
+ outb(1, p->base + MSG_LEN);
+ scb = NULL;
}
- else if (!(status & BUSFREE))
+ else
{
/*
- * We don't know what's going on. Turn off the
- * interrupt source and try to continue.
+ * Should we allow the target to make this decision for us?
*/
- printk(KERN_WARNING "aic7xxx: SSTAT1(0x%x).\n", status);
- outb(status, CLRSINT1 + base);
- UNPAUSE_SEQUENCER(p);
- outb(CLRSCSIINT, CLRINT + base);
+ cmd->result = DID_RETRY_COMMAND << 16;
+ }
+ outb(CLRSCSIPERR, p->base + CLRSINT1);
+ outb(CLRSCSIINT, p->base + CLRINT);
+ unpause_sequencer(p, /* unpause_always */ TRUE);
+ }
+ else
+ {
+ /*
+ * We don't know what's going on. Turn off the
+ * interrupt source and try to continue.
+ */
+ printk(KERN_WARNING "aic7xxx: SSTAT1(0x%x).\n", status);
+ outb(status, p->base + CLRSINT1);
+ outb(CLRSCSIINT, p->base + CLRINT);
+ unpause_sequencer(p, /* unpause always */ TRUE);
+ scb = NULL;
+ }
+ if (scb != NULL)
+ {
+ aic7xxx_done(p, scb);
+ aic7xxx_done_cmds_complete(p);
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_isr
+ *
+ * Description:
+ * SCSI controller interrupt handler.
+ *
+ * NOTE: Since we declared this using SA_INTERRUPT, interrupts should
+ * be disabled all through this function unless we say otherwise.
+ *-F*************************************************************************/
+static void
+aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct aic7xxx_host *p;
+ unsigned char intstat;
+ unsigned long flags;
+
+ p = (struct aic7xxx_host *) aic7xxx_boards[irq]->hostdata;
+
+ /*
+ * Search for the host with a pending interrupt. If we can't find
+ * one, then we've encountered a spurious interrupt.
+ */
+ while ((p != NULL) && !(inb(p->base + INTSTAT) & INT_PEND))
+ {
+ if (p->next == NULL)
+ {
+ p = NULL;
+ }
+ else
+ {
+ p = (struct aic7xxx_host *) p->next->hostdata;
+ }
+ }
+
+ if (p == NULL)
+ return;
+
+ /*
+ * Handle all the interrupt sources - especially for SCSI
+ * interrupts, we won't get a second chance at them.
+ */
+ intstat = inb(p->base + INTSTAT);
+
+ /*
+ * Keep track of interrupts for /proc/scsi
+ */
+ p->isr_count++;
+
+ if (!(p->flags & A_SCANNED) && (p->isr_count == 1))
+ {
+ /*
+ * We must only have one card at this IRQ and it must have been
+ * added to the board data before the spurious interrupt occurred.
+ * It is sufficient that we check isr_count and not the spurious
+ * interrupt count.
+ */
+ printk("scsi%d: Encountered spurious interrupt.\n", p->host_no);
+ if (intstat)
+ {
+ /* Try clearing all interrupts. */
+ outb(CLRBRKADRINT | CLRSCSIINT | CLRCMDINT | CLRSEQINT, p->base + CLRINT);
}
+ return;
}
- if (run_aborted_queue)
- aic7xxx_done_aborted_scbs(p);
+ if (p->flags & IN_ISR)
+ {
+ printk(KERN_WARNING "scsi%d: Warning!! Interrupt routine called reentrantly!\n",
+ p->host_no);
+ return;
+ }
+
+ /*
+ * Indicate that we're in the interrupt handler.
+ */
+ save_flags(flags);
+ cli();
+ p->flags |= IN_ISR;
if (intstat & CMDCMPLT)
{
- int complete;
+ struct aic7xxx_scb *scb = NULL;
+ Scsi_Cmnd *cmd;
+ unsigned char qoutcnt;
+ unsigned char scb_index;
+ int i, interrupts_cleared = 0;
/*
* The sequencer will continue running when it
* issues this interrupt. There may be >1 commands
* finished, so loop until we've processed them all.
*/
- do {
- complete = inb(QOUTFIFO + base);
+ qoutcnt = inb(p->base + QOUTCNT) & p->qcntmask;
- scb = (p->scb_array[complete]);
- if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
- {
- printk(KERN_WARNING "scsi%d: CMDCMPLT without command for SCB %d.\n"
- " QOUTCNT %d, QINCNT %d, SCB state 0x%x, cmd 0x%lx, "
- "pos(%d).\n", p->host_no, complete, inb(QOUTCNT + base),
- inb(QINCNT + base), scb->state, (unsigned long) scb->cmd,
- scb->position);
- outb(CLRCMDINT, CLRINT + base);
- continue;
- }
- cmd = scb->cmd;
- cmd->result |= (aic7xxx_error(cmd) << 16);
- if ((cmd->flags & WAS_SENSE) && !(cmd->flags & ASKED_FOR_SENSE))
+#if 1
+ if (qoutcnt >= p->qfullcount - 1)
+ printk(KERN_WARNING "aic7xxx: Command complete near Qfull count, "
+ "qoutcnt = %d.\n", qoutcnt);
+#endif
+ while (qoutcnt > 0)
+ {
+ for (i = 0; i < qoutcnt; i++)
{
- /*
- * Got sense information.
- */
- cmd->flags &= ASKED_FOR_SENSE;
+ scb_index = inb(p->base + QOUTFIFO);
+ scb = p->scb_data->scb_array[scb_index];
+ if (scb == NULL)
+ {
+ printk(KERN_WARNING "scsi%d: CMDCMPLT with invalid SCB index %d, "
+ "QOUTCNT %d, QINCNT %d\n", p->host_no, scb_index,
+ inb(p->base + QOUTCNT), inb(p->base + QINCNT));
+ continue;
+ }
+ else if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
+ {
+ printk(KERN_WARNING "scsi%d: CMDCMPLT without command for SCB %d, "
+ "QOUTCNT %d, QINCNT %d, SCB flags 0x%x, cmd 0x%lx\n",
+ p->host_no, scb_index, inb(p->base + QOUTCNT),
+ inb(p->base + QINCNT), scb->flags, (unsigned long) scb->cmd);
+ continue;
+ }
+ cmd = scb->cmd;
+ if (scb->hscb->residual_SG_segment_count != 0)
+ {
+ aic7xxx_calculate_residual(p, scb);
+ }
+ if ((scb->flags & SCB_QUEUED_ABORT) != 0)
+ {
+ /*
+ * Have to clean up any possible entries in the
+ * waiting queue and the QINFIFO.
+ */
+ int target;
+ char channel;
+ int lun;
+ unsigned char tag;
+
+ tag = SCB_LIST_NULL;
+ target = cmd->target;
+ lun = cmd->lun;
+ channel = (scb->hscb->target_channel_lun & SELBUSB) ? 'B' : 'A';
+ if (scb->hscb->control & TAG_ENB)
+ {
+ tag = scb->hscb->tag;
+ }
+ aic7xxx_reset_device(p, target, channel, lun, tag);
+ /*
+ * Run the done queue, but don't complete the commands; we
+ * do this once at the end of the loop.
+ */
+ aic7xxx_run_done_queue(p, /*complete*/ FALSE);
+ }
+ cmd->result |= (aic7xxx_error(cmd) << 16);
+ p->device_status[TARGET_INDEX(cmd)].flags |= DEVICE_SUCCESS;
+ aic7xxx_done(p, scb);
}
- p->device_status[TARGET_INDEX(cmd)].flags |= DEVICE_SUCCESS;
-
/*
* Clear interrupt status before checking the output queue again.
* This eliminates a race condition whereby a command could
@@ -3243,56 +3941,152 @@ aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
* so notification of the command being complete never made it
* back up to the kernel.
*/
- outb(CLRCMDINT, CLRINT + base);
- aic7xxx_done(p, scb);
+ outb(CLRCMDINT, p->base + CLRINT);
+ interrupts_cleared++;
+ qoutcnt = inb(p->base + QOUTCNT) & p->qcntmask;
+ }
-#ifdef AIC7XXX_PROC_STATS
- /*
- * XXX: we should actually know how much actually transferred
- * XXX: for each command, but apparently that's too difficult.
- */
- actual = aic7xxx_length(cmd, 0);
- if (!(cmd->flags & WAS_SENSE) && (actual > 0))
+ if (interrupts_cleared == 0)
+ {
+ outb(CLRCMDINT, p->base + CLRINT);
+ }
+
+ aic7xxx_done_cmds_complete(p);
+ }
+
+ if (intstat & BRKADRINT)
+ {
+ int i;
+ unsigned char errno = inb(p->base + ERROR);
+
+ printk(KERN_ERR "scsi%d: BRKADRINT error(0x%x):\n", p->host_no, errno);
+ for (i = 0; i < NUMBER(hard_error); i++)
+ {
+ if (errno & hard_error[i].errno)
{
- struct aic7xxx_xferstats *sp;
- long *ptr;
- int x;
+ printk(KERN_ERR " %s\n", hard_error[i].errmesg);
+ }
+ }
+ printk("scsi%d: BRKADRINT, error 0x%x, seqaddr 0x%x.\n", p->host_no,
+ inb(p->base + ERROR),
+ (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0));
+ aic7xxx_reset_device(p, ALL_TARGETS, ALL_CHANNELS, ALL_LUNS, SCB_LIST_NULL);
+ aic7xxx_run_done_queue(p, /*complete*/ TRUE);
+ }
+
+ if (intstat & SEQINT)
+ {
+ aic7xxx_handle_seqint(p, intstat);
+ }
- sp = &p->stats[cmd->channel & 0x01][cmd->target & 0x0F][cmd->lun & 0x07];
- sp->xfers++;
+ if (intstat & SCSIINT)
+ {
+ aic7xxx_handle_scsiint(p, intstat);
+ }
- if (cmd->request.cmd == WRITE)
+ if (p->waiting_scbs.head != NULL)
+ {
+ aic7xxx_run_waiting_queues(p);
+ }
+
+ p->flags &= ~IN_ISR;
+ restore_flags(flags);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_device_queue_depth
+ *
+ * Description:
+ * Determines the queue depth for a given device. There are two ways
+ * a queue depth can be obtained for a tagged queueing device. One
+ * way is the default queue depth which is determined by whether
+ * AIC7XXX_CMDS_PER_LUN is defined. If it is defined, then it is used
+ * as the default queue depth. Otherwise, we use either 4 or 8 as the
+ * default queue depth (dependent on the number of hardware SCBs).
+ * The other way we determine queue depth is through the use of the
+ * aic7xxx_tag_info array which is enabled by defining
+ * AIC7XXX_TAGGED_QUEUEING_BY_DEVICE. This array can be initialized
+ * with queue depths for individual devices. It also allows tagged
+ * queueing to be [en|dis]abled for a specific adapter.
+ *-F*************************************************************************/
+static void
+aic7xxx_device_queue_depth(struct aic7xxx_host *p, Scsi_Device *device)
+{
+ int default_depth = 2;
+
+ device->queue_depth = default_depth;
+#ifdef AIC7XXX_TAGGED_QUEUEING
+ if (device->tagged_supported)
+ {
+ unsigned short target_mask;
+ int tag_enabled = TRUE;
+
+ target_mask = (1 << (device->id | (device->channel << 3)));
+
+#ifdef AIC7XXX_CMDS_PER_LUN
+ default_depth = AIC7XXX_CMDS_PER_LUN;
+#else
+ if (p->scb_data->maxhscbs <= 4)
+ {
+ default_depth = 4; /* Not many SCBs to work with. */
+ }
+ else
+ {
+ default_depth = 8;
+ }
+#endif
+
+ if (!(p->discenable & target_mask))
+ {
+ printk(KERN_INFO "(scsi%d:%d:%d) Disconnection disabled, unable to "
+ "enable tagged queueing.\n",
+ p->host_no, device->id, device->channel);
+ }
+ else
+ {
+#ifndef AIC7XXX_TAGGED_QUEUEING_BY_DEVICE
+ device->queue_depth = default_depth;
+#else
+ if (p->instance > NUMBER(aic7xxx_tag_info))
+ {
+ device->queue_depth = default_depth;
+ }
+ else
+ {
+ unsigned char tindex;
+
+ tindex = device->id | (device->channel << 3);
+ if (aic7xxx_tag_info[p->instance].tag_commands[tindex] < 0)
{
- sp->w_total++;
- sp->w_total512 += (actual >> 9);
- ptr = sp->w_bins;
+ tag_enabled = FALSE;
+ device->queue_depth = 2; /* Tagged queueing is disabled. */
}
- else
+ else if (aic7xxx_tag_info[p->instance].tag_commands[tindex] == 0)
{
- sp->r_total++;
- sp->r_total512 += (actual >> 9);
- ptr = sp->r_bins;
+ device->queue_depth = default_depth;
}
- for (x = 9; x <= 17; x++)
+ else
{
- if (actual < (1 << x))
- {
- ptr[x - 9]++;
- break;
- }
+ device->queue_depth =
+ aic7xxx_tag_info[p->instance].tag_commands[tindex];
}
- if (x > 17)
+ }
+#endif
+ if ((device->tagged_queue == 0) && tag_enabled)
+ {
+ if (aic7xxx_verbose)
{
- ptr[x - 9]++;
+ printk(KERN_INFO "(scsi%d:%d:%d) Enabled tagged queuing, "
+ "queue depth %d.\n", p->host_no,
+ device->id, device->channel, device->queue_depth);
}
+ device->tagged_queue = 1;
+ device->current_tag = SCB_LIST_NULL;
}
-#endif /* AIC7XXX_PROC_STATS */
-
- } while (inb(QOUTCNT + base) & p->qcntmask);
+ }
}
- aic7xxx_done_cmds_complete(p);
- p->flags &= ~IN_ISR;
- aic7xxx_run_waiting_queues(p);
+#endif
}
/*+F*************************************************************************
@@ -3307,59 +4101,18 @@ aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
* algorithm for determining the queue depth based on the maximum
* SCBs for the controller.
*-F*************************************************************************/
-static void aic7xxx_select_queue_depth(struct Scsi_Host *host,
+static void
+aic7xxx_select_queue_depth(struct Scsi_Host *host,
Scsi_Device *scsi_devs)
{
- Scsi_Device *device = scsi_devs;
- int tq_depth = 2;
+ Scsi_Device *device;
struct aic7xxx_host *p = (struct aic7xxx_host *) host->hostdata;
-#ifdef AIC7XXX_CMDS_PER_LUN
- tq_depth = AIC7XXX_CMDS_PER_LUN;
-#else
- {
- if (p->maxhscbs <= 4)
- {
- tq_depth = 4; /* Not many SCBs to work with. */
- }
- else
- {
- tq_depth = 8;
- }
- }
-#endif
-
for (device = scsi_devs; device != NULL; device = device->next)
{
if (device->host == host)
{
- device->queue_depth = 2;
-#ifdef AIC7XXX_TAGGED_QUEUEING
- if (device->tagged_supported)
- {
- unsigned short target_mask = (1 << device->id) | device->channel;
-
- if (!(p->discenable & target_mask))
- {
- printk(KERN_INFO "scsi%d: Disconnection disabled, unable to enable "
- "tagged queueing for target %d, channel %d, LUN %d.\n",
- host->host_no, device->id, device->channel, device->lun);
- }
- else
- {
- device->queue_depth = tq_depth;
- if (device->tagged_queue == 0)
- {
- printk(KERN_INFO "scsi%d: Enabled tagged queuing for target %d, "
- "channel %d, LUN %d, queue depth %d.\n", host->host_no,
- device->id, device->channel, device->lun,
- device->queue_depth);
- device->tagged_queue = 1;
- device->current_tag = SCB_LIST_NULL;
- }
- }
- }
-#endif
+ aic7xxx_device_queue_depth(p, device);
}
}
}
@@ -3386,7 +4139,7 @@ static void aic7xxx_select_queue_depth(struct Scsi_Host *host,
* The fourth byte's lowest bit seems to be an enabled/disabled
* flag (rest of the bits are reserved?).
*-F*************************************************************************/
-static aha_type
+static aha_chip_type
aic7xxx_probe(int slot, int base, aha_status_type *bios)
{
int i;
@@ -3395,7 +4148,7 @@ aic7xxx_probe(int slot, int base, aha_status_type *bios)
static struct {
int n;
unsigned char signature[sizeof(buf)];
- aha_type type;
+ aha_chip_type type;
int bios_disabled;
} AIC7xxx[] = {
{ 4, { 0x04, 0x90, 0x77, 0x71 }, AIC_7771, FALSE }, /* host adapter 274x */
@@ -3434,7 +4187,8 @@ aic7xxx_probe(int slot, int base, aha_status_type *bios)
return (AIC7xxx[i].type);
}
- printk("aic7xxx: Disabled at slot %d, ignored.\n", slot);
+ printk("aic7xxx: <Adaptec 7770 SCSI Host Adapter> "
+ "disabled at slot %d, ignored.\n", slot);
}
}
@@ -3461,10 +4215,9 @@ aic7xxx_probe(int slot, int base, aha_status_type *bios)
* useful in that it gives us an 800 nsec timer. After a read from the
* SEECTL_2840 register the timing flag is cleared and goes high 800 nsec
* later.
- *
*-F*************************************************************************/
static int
-read_2840_seeprom(int base, struct seeprom_config *sc)
+read_284x_seeprom(struct aic7xxx_host *p, struct seeprom_config *sc)
{
int i = 0, k = 0;
unsigned char temp;
@@ -3477,11 +4230,11 @@ read_2840_seeprom(int base, struct seeprom_config *sc)
struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
#define CLOCK_PULSE(p) \
- while ((inb(STATUS_2840 + base) & EEPROM_TF) == 0) \
+ while ((inb(p->base + STATUS_2840) & EEPROM_TF) == 0) \
{ \
; /* Do nothing */ \
} \
- (void) inb(SEECTL_2840 + base);
+ (void) inb(p->base + SEECTL_2840);
/*
* Read the first 32 registers of the seeprom. For the 2840,
@@ -3494,8 +4247,8 @@ read_2840_seeprom(int base, struct seeprom_config *sc)
/*
* Send chip select for one clock cycle.
*/
- outb(CK_2840 | CS_2840, SEECTL_2840 + base);
- CLOCK_PULSE(base);
+ outb(CK_2840 | CS_2840, p->base + SEECTL_2840);
+ CLOCK_PULSE(p);
/*
* Now we're ready to send the read command followed by the
@@ -3504,11 +4257,11 @@ read_2840_seeprom(int base, struct seeprom_config *sc)
for (i = 0; i < seeprom_read.len; i++)
{
temp = CS_2840 | seeprom_read.bits[i];
- outb(temp, SEECTL_2840 + base);
- CLOCK_PULSE(base);
+ outb(temp, p->base + SEECTL_2840);
+ CLOCK_PULSE(p);
temp = temp ^ CK_2840;
- outb(temp, SEECTL_2840 + base);
- CLOCK_PULSE(base);
+ outb(temp, p->base + SEECTL_2840);
+ CLOCK_PULSE(p);
}
/*
* Send the 6 bit address (MSB first, LSB last).
@@ -3518,11 +4271,11 @@ read_2840_seeprom(int base, struct seeprom_config *sc)
temp = k;
temp = (temp >> i) & 1; /* Mask out all but lower bit. */
temp = CS_2840 | temp;
- outb(temp, SEECTL_2840 + base);
- CLOCK_PULSE(base);
+ outb(temp, p->base + SEECTL_2840);
+ CLOCK_PULSE(p);
temp = temp ^ CK_2840;
- outb(temp, SEECTL_2840 + base);
- CLOCK_PULSE(base);
+ outb(temp, p->base + SEECTL_2840);
+ CLOCK_PULSE(p);
}
/*
@@ -3534,12 +4287,12 @@ read_2840_seeprom(int base, struct seeprom_config *sc)
for (i = 0; i <= 16; i++)
{
temp = CS_2840;
- outb(temp, SEECTL_2840 + base);
- CLOCK_PULSE(base);
+ outb(temp, p->base + SEECTL_2840);
+ CLOCK_PULSE(p);
temp = temp ^ CK_2840;
- seeprom[k] = (seeprom[k] << 1) | (inb(STATUS_2840 + base) & DI_2840);
- outb(temp, SEECTL_2840 + base);
- CLOCK_PULSE(base);
+ seeprom[k] = (seeprom[k] << 1) | (inb(p->base + STATUS_2840) & DI_2840);
+ outb(temp, p->base + SEECTL_2840);
+ CLOCK_PULSE(p);
}
/*
* The serial EEPROM has a checksum in the last word. Keep a
@@ -3555,12 +4308,12 @@ read_2840_seeprom(int base, struct seeprom_config *sc)
/*
* Reset the chip select for the next command cycle.
*/
- outb(0, SEECTL_2840 + base);
- CLOCK_PULSE(base);
- outb(CK_2840, SEECTL_2840 + base);
- CLOCK_PULSE(base);
- outb(0, SEECTL_2840 + base);
- CLOCK_PULSE(base);
+ outb(0, p->base + SEECTL_2840);
+ CLOCK_PULSE(p);
+ outb(CK_2840, p->base + SEECTL_2840);
+ CLOCK_PULSE(p);
+ outb(0, p->base + SEECTL_2840);
+ CLOCK_PULSE(p);
}
#if 0
@@ -3589,6 +4342,53 @@ read_2840_seeprom(int base, struct seeprom_config *sc)
/*+F*************************************************************************
* Function:
+ * acquire_seeprom
+ *
+ * Description:
+ * Acquires access to the memory port on PCI controllers.
+ *-F*************************************************************************/
+static inline int
+acquire_seeprom(struct aic7xxx_host *p)
+{
+ int wait;
+
+ /*
+ * Request access of the memory port. When access is
+ * granted, SEERDY will go high. We use a 1 second
+ * timeout which should be near 1 second more than
+ * is needed. Reason: after the 7870 chip reset, there
+ * should be no contention.
+ */
+ outb(SEEMS, p->base + SEECTL);
+ wait = 1000; /* 1000 msec = 1 second */
+ while ((wait > 0) && ((inb(p->base + SEECTL) & SEERDY) == 0))
+ {
+ wait--;
+ udelay(1000); /* 1 msec */
+ }
+ if ((inb(p->base + SEECTL) & SEERDY) == 0)
+ {
+ outb(0, p->base + SEECTL);
+ return (0);
+ }
+ return (1);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * release_seeprom
+ *
+ * Description:
+ * Releases access to the memory port on PCI controllers.
+ *-F*************************************************************************/
+static inline void
+release_seeprom(struct aic7xxx_host *p)
+{
+ outb(0, p->base + SEECTL);
+}
+
+/*+F*************************************************************************
+ * Function:
* read_seeprom
*
* Description:
@@ -3626,7 +4426,7 @@ read_2840_seeprom(int base, struct seeprom_config *sc)
* first). The clock cycling from low to high initiates the next data
* bit to be sent from the chip.
*
- * The 7870 interface to the 93C46 serial EEPROM is through the SEECTL
+ * The 78xx interface to the 93C46 serial EEPROM is through the SEECTL
* register. After successful arbitration for the memory port, the
* SEECS bit of the SEECTL register is connected to the chip select.
* The SEECK, SEEDO, and SEEDI are connected to the clock, data out,
@@ -3636,17 +4436,14 @@ read_2840_seeprom(int base, struct seeprom_config *sc)
* to this is when we first request access to the memory port. The
* SEERDY goes high to signify that access has been granted and, for
* this case, has no implied timing.
- *
*-F*************************************************************************/
static int
-read_seeprom(int base, int offset, struct seeprom_config *sc,
- seeprom_chip_type chip)
+read_seeprom(struct aic7xxx_host *p, int offset, unsigned short *scarray,
+ unsigned int len, seeprom_chip_type chip)
{
int i = 0, k;
- unsigned long timeout;
unsigned char temp;
unsigned short checksum = 0;
- unsigned short *seeprom = (unsigned short *) sc;
struct seeprom_cmd {
unsigned char len;
unsigned char bits[3];
@@ -3654,43 +4451,33 @@ read_seeprom(int base, int offset, struct seeprom_config *sc,
struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
#define CLOCK_PULSE(p) \
- while ((inb(SEECTL + base) & SEERDY) == 0) \
+ while ((inb(p->base + SEECTL) & SEERDY) == 0) \
{ \
; /* Do nothing */ \
}
/*
- * Request access of the memory port. When access is
- * granted, SEERDY will go high. We use a 1 second
- * timeout which should be near 1 second more than
- * is needed. Reason: after the 7870 chip reset, there
- * should be no contention.
+ * Request access of the memory port.
*/
- outb(SEEMS, SEECTL + base);
- timeout = jiffies + 100; /* 1 second timeout */
- while ((jiffies < timeout) && ((inb(SEECTL + base) & SEERDY) == 0))
- {
- ; /* Do nothing! Wait for access to be granted. */
- }
- if ((inb(SEECTL + base) & SEERDY) == 0)
+ if (acquire_seeprom(p) == 0)
{
- outb(0, SEECTL + base);
return (0);
}
/*
- * Read the first 32 registers of the seeprom. For the 7870,
- * the 93C46 SEEPROM is a 1024-bit device with 64 16-bit registers
- * but only the first 32 are used by Adaptec BIOS. The loop
- * will range from 0 to 31.
+ * Read 'len' registers of the seeprom. For the 7870, the 93C46
+ * SEEPROM is a 1024-bit device with 64 16-bit registers but only
+ * the first 32 are used by Adaptec BIOS. Some adapters use the
+ * 93C56 SEEPROM which is a 2048-bit device. The loop will range
+ * from 0 to 'len' - 1.
*/
- for (k = 0; k < (sizeof(*sc) / 2); k++)
+ for (k = 0; k < len; k++)
{
/*
* Send chip select for one clock cycle.
*/
- outb(SEEMS | SEECK | SEECS, SEECTL + base);
- CLOCK_PULSE(base);
+ outb(SEEMS | SEECK | SEECS, p->base + SEECTL);
+ CLOCK_PULSE(p);
/*
* Now we're ready to send the read command followed by the
@@ -3699,25 +4486,25 @@ read_seeprom(int base, int offset, struct seeprom_config *sc,
for (i = 0; i < seeprom_read.len; i++)
{
temp = SEEMS | SEECS | (seeprom_read.bits[i] << 1);
- outb(temp, SEECTL + base);
- CLOCK_PULSE(base);
+ outb(temp, p->base + SEECTL);
+ CLOCK_PULSE(p);
temp = temp ^ SEECK;
- outb(temp, SEECTL + base);
- CLOCK_PULSE(base);
+ outb(temp, p->base + SEECTL);
+ CLOCK_PULSE(p);
}
/*
- * Send the 6 bit address (MSB first, LSB last).
+ * Send the 6 or 8 bit address (MSB first, LSB last).
*/
for (i = ((int) chip - 1); i >= 0; i--)
{
temp = k + offset;
temp = (temp >> i) & 1; /* Mask out all but lower bit. */
temp = SEEMS | SEECS | (temp << 1);
- outb(temp, SEECTL + base);
- CLOCK_PULSE(base);
+ outb(temp, p->base + SEECTL);
+ CLOCK_PULSE(p);
temp = temp ^ SEECK;
- outb(temp, SEECTL + base);
- CLOCK_PULSE(base);
+ outb(temp, p->base + SEECTL);
+ CLOCK_PULSE(p);
}
/*
@@ -3729,56 +4516,57 @@ read_seeprom(int base, int offset, struct seeprom_config *sc,
for (i = 0; i <= 16; i++)
{
temp = SEEMS | SEECS;
- outb(temp, SEECTL + base);
- CLOCK_PULSE(base);
+ outb(temp, p->base + SEECTL);
+ CLOCK_PULSE(p);
temp = temp ^ SEECK;
- seeprom[k] = (seeprom[k] << 1) | (inb(SEECTL + base) & SEEDI);
- outb(temp, SEECTL + base);
- CLOCK_PULSE(base);
+ scarray[k] = (scarray[k] << 1) | (inb(p->base + SEECTL) & SEEDI);
+ outb(temp, p->base + SEECTL);
+ CLOCK_PULSE(p);
}
/*
- * The serial EEPROM has a checksum in the last word. Keep a
- * running checksum for all words read except for the last
- * word. We'll verify the checksum after all words have been
- * read.
+ * The serial EEPROM should have a checksum in the last word.
+ * Keep a running checksum for all words read except for the
+ * last word. We'll verify the checksum after all words have
+ * been read.
*/
- if (k < (sizeof(*sc) / 2) - 1)
+ if (k < (len - 1))
{
- checksum = checksum + seeprom[k];
+ checksum = checksum + scarray[k];
}
/*
* Reset the chip select for the next command cycle.
*/
- outb(SEEMS, SEECTL + base);
- CLOCK_PULSE(base);
- outb(SEEMS | SEECK, SEECTL + base);
- CLOCK_PULSE(base);
- outb(SEEMS, SEECTL + base);
- CLOCK_PULSE(base);
+ outb(SEEMS, p->base + SEECTL);
+ CLOCK_PULSE(p);
+ outb(SEEMS | SEECK, p->base + SEECTL);
+ CLOCK_PULSE(p);
+ outb(SEEMS, p->base + SEECTL);
+ CLOCK_PULSE(p);
}
/*
* Release access to the memory port and the serial EEPROM.
*/
- outb(0, SEECTL + base);
+ release_seeprom(p);
#if 0
- printk("Computed checksum 0x%x, checksum read 0x%x\n", checksum, sc->checksum);
+ printk("Computed checksum 0x%x, checksum read 0x%x\n",
+ checksum, scarray[len - 1]);
printk("Serial EEPROM:");
- for (k = 0; k < (sizeof(*sc) / 2); k++)
+ for (k = 0; k < len; k++)
{
if (((k % 8) == 0) && (k != 0))
{
printk("\n ");
}
- printk(" 0x%x", seeprom[k]);
+ printk(" 0x%x", scarray[k]);
}
printk("\n");
#endif
- if (checksum != sc->checksum)
+ if (checksum != scarray[len - 1])
{
return (0);
}
@@ -3789,563 +4577,452 @@ read_seeprom(int base, int offset, struct seeprom_config *sc,
/*+F*************************************************************************
* Function:
- * detect_maxscb
+ * write_brdctl
*
* Description:
- * Detects the maximum number of SCBs for the controller and returns
- * the count and a mask in config (config->maxscbs, config->qcntmask).
+ * Writes a value to the BRDCTL register.
*-F*************************************************************************/
-static void
-detect_maxscb(struct aic7xxx_host_config *config)
+static inline void
+write_brdctl(struct aic7xxx_host *p, unsigned char value)
{
- unsigned char sblkctl_reg;
- int base, i;
-
-#ifdef AIC7XXX_PAGE_ENABLE
- config->flags |= PAGE_ENABLED;
-#endif
- base = config->base;
- switch (config->type)
- {
- case AIC_7770:
- case AIC_7771:
- case AIC_284x:
- /*
- * Check for Rev C or E boards. Rev E boards can supposedly have
- * more than 4 SCBs, while the Rev C boards are limited to 4 SCBs.
- * It's still not clear extactly what is different about the Rev E
- * boards, but we think it allows 8 bit entries in the QOUTFIFO to
- * support "paging" SCBs (more than 4 commands can be active at once).
- *
- * The Rev E boards have a read/write autoflush bit in the
- * SBLKCTL register, while in the Rev C boards it is read only.
- */
- sblkctl_reg = inb(SBLKCTL + base) ^ AUTOFLUSHDIS;
- outb(sblkctl_reg, SBLKCTL + base);
- if (inb(SBLKCTL + base) == sblkctl_reg)
- {
- /*
- * We detected a Rev E board, we allow paging on this board.
- */
- printk(KERN_INFO "aic7xxx: %s Rev E and subsequent.\n",
- board_names[config->type]);
- outb(sblkctl_reg ^ AUTOFLUSHDIS, SBLKCTL + base);
- }
- else
- {
- /* Do not allow paging. */
- config->flags &= ~PAGE_ENABLED;
- printk(KERN_INFO "aic7xxx: %s Rev C and previous.\n",
- board_names[config->type]);
- }
- break;
-
- default:
- break;
- }
-
- /*
- * Walk the SCBs to determine how many there are.
- */
- i = 1;
- outb(0, SCBPTR + base);
- outb(0, SCBARRAY + base);
-
- while (i < AIC7XXX_MAXSCB)
- {
- outb(i, SCBPTR + base);
- outb(i, SCBARRAY + base);
- if (inb(SCBARRAY + base) != i)
- break;
- outb(0, SCBPTR + base);
- if (inb(SCBARRAY + base) != 0)
- break;
-
- outb(i, SCBPTR + base); /* Clear the control byte. */
- outb(0, SCBARRAY + base);
-
- config->qcntmask |= i; /* Update the count mask. */
- i++;
- }
- outb(i, SCBPTR + base); /* Ensure we clear the control bytes. */
- outb(0, SCBARRAY + base);
- outb(0, SCBPTR + base);
- outb(0, SCBARRAY + base);
-
- config->maxhscbs = i;
- config->qcntmask |= i;
- if ((config->flags & PAGE_ENABLED) && (config->maxhscbs < AIC7XXX_MAXSCB))
- {
- config->maxscbs = AIC7XXX_MAXSCB;
- }
- else
- {
- config->flags &= ~PAGE_ENABLED; /* Disable paging if we have 255 SCBs!. */
- config->maxscbs = config->maxhscbs;
- }
-
- printk(KERN_INFO "aic7xxx: Memory check yields %d SCBs", config->maxhscbs);
- if (config->flags & PAGE_ENABLED)
- printk(", %d page-enabled SCBs.\n", config->maxscbs);
- else
- printk(", paging not enabled.\n");
-
+ unsigned char brdctl;
+
+ brdctl = BRDCS | BRDSTB;
+ outb(brdctl, p->base + BRDCTL);
+ brdctl |= value;
+ outb(brdctl, p->base + BRDCTL);
+ brdctl &= ~BRDSTB;
+ outb(brdctl, p->base + BRDCTL);
+ brdctl &= ~BRDCS;
+ outb(brdctl, p->base + BRDCTL);
}
/*+F*************************************************************************
* Function:
- * aic7xxx_register
+ * read_brdctl
*
* Description:
- * Register a Adaptec aic7xxx chip SCSI controller with the kernel.
+ * Reads the BRDCTL register.
*-F*************************************************************************/
-static int
-aic7xxx_register(Scsi_Host_Template *template,
- struct aic7xxx_host_config *config)
+static inline unsigned char
+read_brdctl(struct aic7xxx_host *p)
{
- int i;
- unsigned char sblkctl, flags = 0;
- int max_targets;
- int found = 1;
- unsigned int sram, base;
- unsigned char target_settings;
- unsigned char scsi_conf, host_conf;
- unsigned short ultraenable = 0;
- int have_seeprom = FALSE;
- struct Scsi_Host *host;
- struct aic7xxx_host *p;
- struct seeprom_config sc;
-
- base = config->base;
-
- /*
- * Lock out other contenders for our i/o space.
- */
- request_region(base, MAXREG - MINREG, "aic7xxx");
+ outb(BRDRW | BRDCS, p->base + BRDCTL);
+ return (inb(p->base + BRDCTL));
+}
- switch (config->type)
+/*+F*************************************************************************
+ * Function:
+ * configure_termination
+ *
+ * Description:
+ * Configures the termination settings on PCI adapters that have
+ * SEEPROMs available.
+ *-F*************************************************************************/
+static void
+configure_termination(struct aic7xxx_host *p, unsigned char *sxfrctl1,
+ unsigned short adapter_control, unsigned char max_targ)
+{
+ unsigned char brdctl_int, brdctl_ext;
+ int internal50_present;
+ int internal68_present = 0;
+ int external_present = 0;
+ int eprom_present;
+ int high_on;
+ int low_on;
+ int old_verbose;
+
+ if (acquire_seeprom(p))
{
- case AIC_7770:
- case AIC_7771:
- /*
- * Use the boot-time option for the interrupt trigger type. If not
- * supplied (-1), then we use BIOS settings to determine the interrupt
- * trigger type (level or edge) and use this value for pausing and
- * unpausing the sequencer.
- */
- switch (aic7xxx_irq_trigger)
- {
- case 0: config->unpause = INTEN; /* Edge */
- break;
- case 1: config->unpause = IRQMS | INTEN; /* Level */
- break;
- case -1:
- default: config->unpause = (inb(HCNTRL + base) & IRQMS) | INTEN;
- break;
- }
- config->pause = config->unpause | PAUSE;
+ if (adapter_control & CFAUTOTERM)
+ {
+ old_verbose = aic7xxx_verbose;
+ printk(KERN_INFO "aic7xxx: Warning - detected auto-termination. Please "
+ "verify driver");
+ printk(KERN_INFO " detected settings and use manual termination "
+ "if necessary.");
+
+ /* Configure auto termination. */
+ outb(SEECS | SEEMS, p->base + SEECTL);
/*
- * For some 274x boards, we must clear the CHIPRST bit and pause
- * the sequencer. For some reason, this makes the driver work.
- * For 284x boards, we give it a CHIPRST just like the 294x boards.
+ * First read the status of our cables. Set the rom bank to
+ * 0 since the bank setting serves as a multiplexor for the
+ * cable detection logic. BRDDAT5 controls the bank switch.
*/
- outb(config->pause | CHIPRST, HCNTRL + base);
- aic7xxx_delay(1);
- if (inb(HCNTRL + base) & CHIPRST)
- {
- printk(KERN_INFO "aic7xxx: Chip reset not cleared; clearing manually.\n");
- }
- outb(config->pause, HCNTRL + base);
+ write_brdctl(p, 0);
/*
- * Just to be on the safe side with the 274x, we will re-read the irq
- * since there was some issue about resetting the board.
+ * Now read the state of the internal connectors. The
+ * bits BRDDAT6 and BRDDAT7 are 0 when cables are present
+ * set when cables are not present (BRDDAT6 is INT50 and
+ * BRDDAT7 is INT68).
*/
- config->irq = inb(INTDEF + base) & 0x0F;
- if ((config->type == AIC_7771) &&
- (inb(HA_274_BIOSCTRL + base) & BIOSMODE) == BIOSDISABLED)
+ brdctl_int = read_brdctl(p);
+ internal50_present = (brdctl_int & BRDDAT6) ? 0 : 1;
+ if (max_targ > 8)
{
- config->bios = AIC_DISABLED;
- config->flags |= USE_DEFAULTS;
- }
- else
- {
- host_conf = inb(HOSTCONF + base);
- config->bus_speed = host_conf & DFTHRSH;
- config->busrtime = (host_conf << 2) & BOFF;
+ internal68_present = (brdctl_int & BRDDAT7) ? 0 : 1;
}
/*
- * Setup the FIFO threshold and the bus off time
+ * Set the rom bank to 1 and determine
+ * the other signals.
*/
- outb(config->bus_speed & DFTHRSH, BUSSPD + base);
- outb(config->busrtime, BUSTIME + base);
+ write_brdctl(p, BRDDAT5);
/*
- * A reminder until this can be detected automatically.
+ * Now read the state of the external connectors. BRDDAT6 is
+ * 0 when an external cable is present, and BRDDAT7 (EPROMPS) is
+ * set when the eprom is present.
*/
- printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
- (config->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
- break;
-
- case AIC_284x:
- outb(CHIPRST, HCNTRL + base);
- config->unpause = UNPAUSE_284X;
- config->pause = REQ_PAUSE; /* DWG would like to be like the rest */
- aic7xxx_delay(1);
- outb(config->pause, HCNTRL + base);
-
- config->parity = AIC_ENABLED;
- config->irq = inb(INTDEF + base) & 0x0F;
- host_conf = inb(HOSTCONF + base);
-
- printk(KERN_INFO "aic7xxx: Reading SEEPROM...");
- have_seeprom = read_2840_seeprom(base, &sc);
- if (!have_seeprom)
+ brdctl_ext = read_brdctl(p);
+ external_present = (brdctl_ext & BRDDAT6) ? 0 : 1;
+ eprom_present = brdctl_ext & BRDDAT7;
+ if (aic7xxx_verbose)
{
- printk("aic7xxx: Unable to read SEEPROM.\n");
- }
- else
- {
- printk("done.\n");
- config->flags |= HAVE_SEEPROM;
- if (sc.bios_control & CF284XEXTEND)
- config->flags |= EXTENDED_TRANSLATION;
- if (!(sc.bios_control & CFBIOSEN))
+ if (max_targ > 8)
{
- /*
- * The BIOS is disabled; the values left over in scratch
- * RAM are still valid. Do not use defaults as in the
- * AIC-7770 case.
- */
- config->bios = AIC_DISABLED;
+ printk(KERN_INFO "aic7xxx: Cables present (Int-50 %s, Int-68 %s, "
+ "Ext-68 %s)\n",
+ internal50_present ? "YES" : "NO",
+ internal68_present ? "YES" : "NO",
+ external_present ? "YES" : "NO");
}
else
{
- config->parity = (sc.adapter_control & CFSPARITY) ?
- AIC_ENABLED : AIC_DISABLED;
- config->low_term = (sc.adapter_control & CF284XSTERM) ?
- AIC_ENABLED : AIC_DISABLED;
- /*
- * XXX - Adaptec *does* make 284x wide controllers, but the
- * documents do not say where the high byte termination
- * enable bit is located.
- */
+ printk(KERN_INFO "aic7xxx: Cables present (Int-50 %s, Ext-50 %s)\n",
+ internal50_present ? "YES" : "NO",
+ external_present ? "YES" : "NO");
}
+ printk(KERN_INFO "aic7xxx: eprom %s present, brdctl_int=0x%x, "
+ "brdctl_ext=0x%x\n",
+ eprom_present ? "is" : "not", brdctl_int, brdctl_ext);
}
- host_conf = inb(HOSTCONF + base);
- config->bus_speed = host_conf & DFTHRSH;
- config->busrtime = (host_conf << 2) & BOFF;
-
- /*
- * Setup the FIFO threshold and the bus off time
- */
- outb(config->bus_speed & DFTHRSH, BUSSPD + base);
- outb(config->busrtime, BUSTIME + base);
-
- printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
- (config->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
- break;
-
- case AIC_7860:
- case AIC_7861:
- case AIC_7880:
- case AIC_7881:
- case AIC_7882:
- case AIC_7883:
- case AIC_7884:
- /*
- * Remember if Ultra was enabled in case there is no SEEPROM.
- * Fall through to the rest of the AIC_78xx code.
- */
- if ((inb(SXFRCTL0 + base) & ULTRAEN) || aic7xxx_enable_ultra)
- config->flags |= ULTRA_ENABLED;
-
- case AIC_7850:
- case AIC_7855:
- case AIC_7870:
- case AIC_7871:
- case AIC_7872:
- case AIC_7873:
- case AIC_7874:
/*
- * Grab the SCSI ID before chip reset in case there is no SEEPROM.
+ * Now set the termination based on what we found. BRDDAT6
+ * controls wide termination enable.
*/
- config->scsi_id = inb(SCSIID + base) & OID;
- outb(CHIPRST, HCNTRL + base);
- config->unpause = UNPAUSE_294X;
- config->pause = config->unpause | PAUSE;
- aic7xxx_delay(1);
- outb(config->pause, HCNTRL + base);
-
- config->parity = AIC_ENABLED;
+ high_on = FALSE;
+ low_on = FALSE;
+ if ((max_targ > 8) &&
+ ((external_present == 0) || (internal68_present == 0)))
+ {
+ high_on = TRUE;
+ }
- printk(KERN_INFO "aic7xxx: Reading SEEPROM...");
- if ((config->type == AIC_7873) || (config->type == AIC_7883))
+ if ((internal50_present + internal68_present + external_present) <= 1)
{
- have_seeprom = read_seeprom(base, config->chan_num * (sizeof(sc) / 2),
- &sc, c56_66);
+ low_on = TRUE;
}
- else
+
+ if (internal50_present && internal68_present && external_present)
{
- have_seeprom = read_seeprom(base, config->chan_num * (sizeof(sc) / 2),
- &sc, c46);
+ printk(KERN_WARNING "aic7xxx: Illegal cable configuration!!\n"
+ " Only two connectors on the adapter may be "
+ "used at a time!\n");
}
- if (!have_seeprom)
+
+ if (high_on == TRUE)
+ write_brdctl(p, BRDDAT6);
+ else
+ write_brdctl(p, 0);
+
+ if (low_on == TRUE)
+ *sxfrctl1 |= STPWEN;
+
+ if (aic7xxx_verbose)
{
- for (sram = base + TARG_SCRATCH; sram < base + 0x60; sram++)
+ if (max_targ > 8)
{
- if (inb(sram) != 0x00)
- break;
- }
- if (sram == base + TARG_SCRATCH)
- {
- for (sram = base + TARG_SCRATCH; sram < base + 0x60; sram++)
- {
- if (inb(sram) != 0xFF)
- break;
- }
- }
- if ((sram != base + 0x60) && (config->scsi_id != 0))
- {
- config->flags &= ~USE_DEFAULTS;
- printk("\naic7xxx: Unable to read SEEPROM; "
- "using leftover BIOS values.\n");
+ printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n",
+ low_on ? "ON" : "OFF",
+ high_on ? "ON" : "OFF");
}
else
{
- printk("\n");
- printk(KERN_INFO "aic7xxx: Unable to read SEEPROM; using default "
- "settings.\n");
- config->flags |= USE_DEFAULTS;
- config->flags &= ~ULTRA_ENABLED;
- config->scsi_id = 7;
+ printk(KERN_INFO "aic7xxx: Termination %s\n", low_on ? "ON" : "OFF");
}
- scsi_conf = ENSPCHK | RESET_SCSI;
+ }
+ aic7xxx_verbose = old_verbose;
+ }
+ else
+ {
+ if (adapter_control & CFSTERM)
+ {
+ *sxfrctl1 |= STPWEN;
+ }
+ outb(SEEMS | SEECS, p->base + SEECTL);
+ /*
+ * Configure high byte termination.
+ */
+ if (adapter_control & CFWSTERM)
+ {
+ write_brdctl(p, BRDDAT6);
}
else
{
- printk("done.\n");
- config->flags |= HAVE_SEEPROM;
- if (!(sc.bios_control & CFBIOSEN))
- {
- /*
- * The BIOS is disabled; the values left over in scratch
- * RAM are still valid. Do not use defaults as in the
- * AIC-7770 case.
- */
- config->bios = AIC_DISABLED;
- scsi_conf = ENSPCHK | RESET_SCSI;
- }
- else
- {
- scsi_conf = 0;
- if (sc.adapter_control & CFRESETB)
- scsi_conf |= RESET_SCSI;
- if (sc.adapter_control & CFSPARITY)
- scsi_conf |= ENSPCHK;
- if (sc.bios_control & CFEXTEND)
- config->flags |= EXTENDED_TRANSLATION;
- config->scsi_id = (sc.brtime_id & CFSCSIID);
- config->parity = (sc.adapter_control & CFSPARITY) ?
- AIC_ENABLED : AIC_DISABLED;
- config->low_term = (sc.adapter_control & CFSTERM) ?
- AIC_ENABLED : AIC_DISABLED;
- config->high_term = (sc.adapter_control & CFWSTERM) ?
- AIC_ENABLED : AIC_DISABLED;
- config->busrtime = ((sc.brtime_id & CFBRTIME) >> 8);
- if (((config->type == AIC_7880) || (config->type == AIC_7881) ||
- (config->type == AIC_7882) || (config->type == AIC_7883) ||
- (config->type == AIC_7884)) && (sc.adapter_control & CFULTRAEN))
- {
- printk(KERN_INFO "aic7xxx: Enabling support for Ultra SCSI "
- "speed.\n");
- config->flags |= ULTRA_ENABLED;
- }
- }
+ write_brdctl(p, 0);
}
+ if (aic7xxx_verbose)
+ {
+ printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n",
+ (adapter_control & CFSTERM) ? "ON" : "OFF",
+ (adapter_control & CFWSTERM) ? "ON" : "OFF");
+ }
+ }
+ release_seeprom(p);
+ }
+}
- outb(scsi_conf | (config->scsi_id & 0x07), SCSICONF + base);
- config->bus_speed = DFTHRSH_100;
- outb(config->bus_speed, DSPCISTATUS + base);
+/*+F*************************************************************************
+ * Function:
+ * detect_maxscb
+ *
+ * Description:
+ * Detects the maximum number of SCBs for the controller and returns
+ * the count and a mask in p (p->maxscbs, p->qcntmask).
+ *-F*************************************************************************/
+static void
+detect_maxscb(struct aic7xxx_host *p)
+{
+ int i;
+ unsigned char max_scbid = 255;
- /*
- * In case we are a wide card...
- */
- outb(config->scsi_id, SCSICONF + base + 1);
+ /*
+ * It's possible that we've already done this for multichannel
+ * adapters.
+ */
+ if (p->scb_data->maxhscbs == 0)
+ {
+ /*
+ * We haven't initialized the SCB settings yet. Walk the SCBs to
+ * determince how many there are.
+ */
+ outb(0, p->base + FREE_SCBH);
- printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
- (config->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
- break;
+ for (i = 0; i < AIC7XXX_MAXSCB; i++)
+ {
+ outb(i, p->base + SCBPTR);
+ outb(i, p->base + SCB_CONTROL);
+ if (inb(p->base + SCB_CONTROL) != i)
+ break;
+ outb(0, p->base + SCBPTR);
+ if (inb(p->base + SCB_CONTROL) != 0)
+ break;
- default:
- panic(KERN_WARNING "aic7xxx: (aic7xxx_register) Internal error.\n");
+ outb(i, p->base + SCBPTR);
+ outb(0, p->base + SCB_CONTROL); /* Clear the control byte. */
+ outb(i + 1, p->base + SCB_NEXT); /* Set the next pointer. */
+ outb(SCB_LIST_NULL, p->base + SCB_TAG); /* Make the tag invalid. */
+
+ /* Make the non-tagged targets not busy. */
+ outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS);
+ outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS + 1);
+ outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS + 2);
+ outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS + 3);
+ }
+
+ /* Make sure the last SCB terminates the free list. */
+ outb(i - 1, p->base + SCBPTR);
+ outb(SCB_LIST_NULL, p->base + SCB_NEXT);
+
+ /* Ensure we clear the first (0) SCBs control byte. */
+ outb(0, p->base + SCBPTR);
+ outb(0, p->base + SCB_CONTROL);
+
+ p->scb_data->maxhscbs = i;
}
- detect_maxscb(config);
+ if ((p->flags & PAGE_ENABLED) && (p->scb_data->maxhscbs < AIC7XXX_MAXSCB))
+ {
+ /* Determine the number of valid bits in the FIFOs. */
+ outb(max_scbid, p->base + QINFIFO);
+ max_scbid = inb(p->base + QINFIFO);
+ p->scb_data->maxscbs = MIN(AIC7XXX_MAXSCB, max_scbid + 1);
+ }
+ else
+ {
+ p->scb_data->maxscbs = p->scb_data->maxhscbs;
+ }
+ if (p->scb_data->maxscbs == p->scb_data->maxhscbs)
+ {
+ /*
+ * Disable paging if the QINFIFO doesn't allow more SCBs than
+ * we have in hardware.
+ */
+ p->flags &= ~PAGE_ENABLED;
+ }
- if (config->chip_type == AIC_777x)
+ /*
+ * Set the Queue Full Count. Some cards have more queue space than
+ * SCBs.
+ */
+ switch (p->chip_class)
{
- if (config->pause & IRQMS)
- {
- printk(KERN_INFO "aic7xxx: Using level sensitive interrupts.\n");
- }
- else
- {
- printk(KERN_INFO "aic7xxx: Using edge triggered interrupts.\n");
- }
+ case AIC_777x:
+ p->qfullcount = 4;
+ p->qcntmask = 0x07;
+ break;
+ case AIC_785x:
+ case AIC_786x:
+ p->qfullcount = 8;
+ p->qcntmask = 0x0f;
+ break;
+ case AIC_787x:
+ case AIC_788x:
+ if (p->scb_data->maxhscbs == AIC7XXX_MAXSCB)
+ {
+ p->qfullcount = AIC7XXX_MAXSCB;
+ p->qcntmask = 0xFF;
+ }
+ else
+ {
+ p->qfullcount = 16;
+ p->qcntmask = 0x1F;
+ }
+ break;
}
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_register
+ *
+ * Description:
+ * Register a Adaptec aic7xxx chip SCSI controller with the kernel.
+ *-F*************************************************************************/
+static int
+aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p)
+{
+ int i;
+ unsigned char sblkctl, flags = 0;
+ int max_targets;
+ int found = 1;
+ char channel_ids[] = {'A', 'B', 'C'};
+ unsigned char target_settings;
+ unsigned char scsi_conf, sxfrctl1;
+ unsigned short ultraenable = 0;
+ struct Scsi_Host *host;
+
+ /*
+ * Lock out other contenders for our i/o space.
+ */
+ request_region(p->base, MAXREG - MINREG, "aic7xxx");
/*
* Read the bus type from the SBLKCTL register. Set the FLAGS
* register in the sequencer for twin and wide bus cards.
*/
- sblkctl = inb(SBLKCTL + base);
- if (config->flags & PAGE_ENABLED)
+ sblkctl = inb(p->base + SBLKCTL);
+ if (p->flags & PAGE_ENABLED)
flags = PAGESCBS;
switch (sblkctl & SELBUS_MASK)
{
case SELNARROW: /* narrow/normal bus */
- config->scsi_id = inb(SCSICONF + base) & 0x07;
- config->bus_type = AIC_SINGLE;
- outb(flags | SINGLE_BUS, FLAGS + base);
+ p->scsi_id = inb(p->base + SCSICONF) & 0x07;
+ p->bus_type = AIC_SINGLE;
+ p->flags &= ~FLAGS_CHANNEL_B_PRIMARY;
+ if (p->flags & MULTI_CHANNEL)
+ {
+ printk(KERN_INFO "aic7xxx: Channel %c, SCSI ID %d, ",
+ channel_ids[p->chan_num], p->scsi_id);
+ }
+ else
+ {
+ printk (KERN_INFO "aic7xxx: Single Channel, SCSI ID %d, ",
+ p->scsi_id);
+ }
+ outb(flags | SINGLE_BUS, p->base + SEQ_FLAGS);
break;
case SELWIDE: /* Wide bus */
- config->scsi_id = inb(SCSICONF + base + 1) & 0x0F;
- config->bus_type = AIC_WIDE;
- printk("aic7xxx: Enabling wide channel of %s-Wide.\n",
- board_names[config->type]);
- outb(flags | WIDE_BUS, FLAGS + base);
+ p->scsi_id = inb(p->base + SCSICONF + 1) & HWSCSIID;
+ p->bus_type = AIC_WIDE;
+ p->flags &= ~FLAGS_CHANNEL_B_PRIMARY;
+ if (p->flags & MULTI_CHANNEL)
+ {
+ printk(KERN_INFO "aic7xxx: Wide Channel %c, SCSI ID %d, ",
+ channel_ids[p->chan_num], p->scsi_id);
+ }
+ else
+ {
+ printk (KERN_INFO "aic7xxx: Wide Channel, SCSI ID %d, ",
+ p->scsi_id);
+ }
+ outb(flags | WIDE_BUS, p->base + SEQ_FLAGS);
break;
case SELBUSB: /* Twin bus */
- config->scsi_id = inb(SCSICONF + base) & 0x07;
-#ifdef AIC7XXX_TWIN_SUPPORT
- config->scsi_id_b = inb(SCSICONF + base + 1) & 0x07;
- config->bus_type = AIC_TWIN;
- printk(KERN_INFO "aic7xxx: Enabled channel B of %s-Twin.\n",
- board_names[config->type]);
- outb(flags | TWIN_BUS, FLAGS + base);
-#else
- config->bus_type = AIC_SINGLE;
- printk(KERN_INFO "aic7xxx: Channel B of %s-Twin will be ignored.\n",
- board_names[config->type]);
- outb(flags, FLAGS + base);
-#endif
+ p->scsi_id = inb(p->base + SCSICONF) & HSCSIID;
+ p->scsi_id_b = inb(p->base + SCSICONF + 1) & HSCSIID;
+ p->bus_type = AIC_TWIN;
+ printk(KERN_INFO "aic7xxx: Twin Channel, A SCSI ID %d, B SCSI ID %d, ",
+ p->scsi_id, p->scsi_id_b);
+ outb(flags | TWIN_BUS, p->base + SEQ_FLAGS);
break;
default:
printk(KERN_WARNING "aic7xxx: Unsupported type 0x%x, please "
- "mail deang@teleport.com\n", inb(SBLKCTL + base));
- outb(0, FLAGS + base);
+ "mail deang@teleport.com\n", inb(p->base + SBLKCTL));
+ outb(0, p->base + SEQ_FLAGS);
return (0);
}
/*
- * For the 294x cards, clearing DIAGLEDEN and DIAGLEDON, will
- * take the card out of diagnostic mode and make the host adapter
- * LED follow bus activity (will not always be on).
+ * Detect SCB parameters and initialize the SCB array.
*/
- outb(sblkctl & ~(DIAGLEDEN | DIAGLEDON), SBLKCTL + base);
+ detect_maxscb(p);
+ printk("%d/%d SCBs, QFull %d, QMask 0x%x\n",
+ p->scb_data->maxhscbs, p->scb_data->maxscbs,
+ p->qfullcount, p->qcntmask);
- /*
- * The IRQ level in i/o port 4 maps directly onto the real
- * IRQ number. If it's ok, register it with the kernel.
- *
- * NB. the Adaptec documentation says the IRQ number is only
- * in the lower four bits; the ECU information shows the
- * high bit being used as well. Which is correct?
- *
- * The PCI cards get their interrupt from PCI BIOS.
- */
- if ((config->chip_type == AIC_777x) && ((config->irq < 9) || (config->irq > 15)))
- {
- printk(KERN_WARNING "aic7xxx: Host adapter uses unsupported IRQ level, "
- "ignoring.\n");
- return (0);
- }
+ host = p->host;
- /*
- * Print out debugging information before re-enabling
- * the card - a lot of registers on it can't be read
- * when the sequencer is active.
- */
- debug_config(config);
-
- /*
- * Register each "host" and fill in the returned Scsi_Host
- * structure as best we can. Some of the parameters aren't
- * really relevant for bus types beyond ISA, and none of the
- * high-level SCSI code looks at it anyway. Why are the fields
- * there? Also save the pointer so that we can find the
- * information when an IRQ is triggered.
- */
- host = scsi_register(template, sizeof(struct aic7xxx_host));
- host->can_queue = config->maxscbs;
+ host->can_queue = p->scb_data->maxscbs;
host->cmd_per_lun = 2;
+ host->sg_tablesize = AIC7XXX_MAX_SG;
host->select_queue_depths = aic7xxx_select_queue_depth;
- host->this_id = config->scsi_id;
- host->io_port = config->base;
+ host->this_id = p->scsi_id;
+ host->io_port = p->base;
host->n_io_port = 0xFF;
- host->base = (unsigned char *)config->mbase;
- host->irq = config->irq;
- if (config->bus_type == AIC_WIDE)
+ host->base = (unsigned char *) p->mbase;
+ host->irq = p->irq;
+ if (p->bus_type == AIC_WIDE)
{
host->max_id = 16;
}
- if (config->bus_type == AIC_TWIN)
+ if (p->bus_type == AIC_TWIN)
{
host->max_channel = 1;
}
- p = (struct aic7xxx_host *) host->hostdata;
-
p->host = host;
- p->host_no = (int)host->host_no;
+ p->host_no = host->host_no;
p->isr_count = 0;
- p->base = base;
- p->maxscbs = config->maxscbs;
- p->maxhscbs = config->maxhscbs;
- p->qcntmask = config->qcntmask;
- p->mbase = (char *)config->mbase;
- p->type = config->type;
- p->chip_type = config->chip_type;
- p->flags = config->flags;
- p->chan_num = config->chan_num;
- p->scb_link = &(p->scb_usage);
-#if defined(CONFIG_PCI) && defined(AIC7XXX_SHARE_SCBS)
- if ((p->chan_num == 0) && ((p->type == AIC_7873) | (p->type == AIC_7883)))
- {
- shared_3985_scbs = &(p->scb_usage);
- p->scb_link = &(p->scb_usage);
- }
-#endif
- p->scb_link->numscbs = 0;
- p->bus_type = config->bus_type;
- p->seeprom = sc;
p->next = NULL;
p->completeq.head = NULL;
p->completeq.tail = NULL;
- scbq_init(&p->scb_link->free_scbs);
- scbq_init(&p->page_scbs);
+ scbq_init(&p->scb_data->free_scbs);
scbq_init(&p->waiting_scbs);
- scbq_init(&p->assigned_scbs);
- p->unpause = config->unpause;
- p->pause = config->pause;
-
- for (i = 0; i <= 15; i++)
+ for (i = 0; i <= NUMBER(p->device_status); i++)
{
p->device_status[i].commands_sent = 0;
p->device_status[i].flags = 0;
+ p->device_status[i].active_cmds = 0;
p->device_status[i].last_reset = 0;
}
- if (aic7xxx_boards[config->irq] == NULL)
+ if (aic7xxx_boards[p->irq] == NULL)
{
+ int result;
+ int irq_flags = 0;
+
+#ifdef AIC7XXX_OLD_ISR_TYPE
+ irg_flags = SA_INTERRUPT;
+#endif
/*
* Warning! This must be done before requesting the irq. It is
* possible for some boards to raise an interrupt as soon as
@@ -4353,17 +5030,26 @@ aic7xxx_register(Scsi_Host_Template *template,
* kernel, an interrupt is triggered immediately. Therefore, we
* must ensure the board data is correctly set before the request.
*/
- aic7xxx_boards[config->irq] = host;
+ aic7xxx_boards[p->irq] = host;
/*
- * Register IRQ with the kernel.
+ * Register IRQ with the kernel. Only allow sharing IRQs with
+ * PCI devices.
*/
- if (request_irq(config->irq, aic7xxx_isr, SA_INTERRUPT | SA_SHIRQ,
- "aic7xxx", NULL))
+ if (p->chip_class == AIC_777x)
+ {
+ result = (request_irq(p->irq, aic7xxx_isr, irq_flags, "aic7xxx", NULL));
+ }
+ else
+ {
+ result = (request_irq(p->irq, aic7xxx_isr, irq_flags | SA_SHIRQ,
+ "aic7xxx", NULL));
+ }
+ if (result < 0)
{
printk(KERN_WARNING "aic7xxx: Couldn't register IRQ %d, ignoring.\n",
- config->irq);
- aic7xxx_boards[config->irq] = NULL;
+ p->irq);
+ aic7xxx_boards[p->irq] = NULL;
return (0);
}
}
@@ -4374,79 +5060,74 @@ aic7xxx_register(Scsi_Host_Template *template,
* registered host adapter. Add this host adapter's Scsi_Host
* to the beginning of the linked list of hosts at the same IRQ.
*/
- p->next = aic7xxx_boards[config->irq];
- aic7xxx_boards[config->irq] = host;
- }
-
- /*
- * Load the sequencer program, then re-enable the board -
- * resetting the AIC-7770 disables it, leaving the lights
- * on with nobody home. On the PCI bus you *may* be home,
- * but then your mailing address is dynamically assigned
- * so no one can find you anyway :-)
- */
- printk(KERN_INFO "aic7xxx: Downloading sequencer code...");
- aic7xxx_loadseq(base);
-
- /*
- * Set Fast Mode and Enable the board
- */
- outb(FASTMODE, SEQCTL + base);
-
- if (p->chip_type == AIC_777x)
- {
- outb(ENABLE, BCTL + base);
+ p->next = aic7xxx_boards[p->irq];
+ aic7xxx_boards[p->irq] = host;
}
- printk("done.\n");
-
/*
* Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels
*/
if (p->bus_type == AIC_TWIN)
{
/*
- * Select Channel B.
+ * The controller is gated to channel B after a chip reset; set
+ * bus B values first.
*/
- outb((sblkctl & ~SELBUS_MASK) | SELBUSB, SBLKCTL + base);
-
- outb(config->scsi_id_b, SCSIID + base);
- scsi_conf = inb(SCSICONF + base + 1) & (ENSPCHK | STIMESEL);
- outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1 + base);
-#if EXPERIMENTAL_FLAGS
- outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1 + base);
-#else
- outb(ENSELTIMO, SIMODE1 + base);
-#endif
+ outb(p->scsi_id_b, p->base + SCSIID);
+ scsi_conf = inb(p->base + SCSICONF + 1);
+ sxfrctl1 = inb(p->base + SXFRCTL1);
+ outb((scsi_conf & (ENSPCHK | STIMESEL)) | (sxfrctl1 & STPWEN) |
+ ENSTIMER | ACTNEGEN, p->base + SXFRCTL1);
+ outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, p->base + SIMODE1);
if (p->flags & ULTRA_ENABLED)
{
- outb(DFON | SPIOEN | ULTRAEN, SXFRCTL0 + base);
+ outb(DFON | SPIOEN | FAST20, p->base + SXFRCTL0);
}
else
{
- outb(DFON | SPIOEN, SXFRCTL0 + base);
+ outb(DFON | SPIOEN, p->base + SXFRCTL0);
}
- /*
- * Select Channel A
- */
- outb((sblkctl & ~SELBUS_MASK) | SELNARROW, SBLKCTL + base);
+ if ((scsi_conf & RESET_SCSI) && (aic7xxx_no_reset == 0))
+ {
+ /* Reset SCSI bus B. */
+ if (aic7xxx_verbose)
+ printk(KERN_INFO "aic7xxx: Resetting channel B\n");
+
+ aic7xxx_reset_current_bus(p);
+ }
+
+ /* Select channel A */
+ outb(SELNARROW, p->base + SBLKCTL);
}
- outb(config->scsi_id, SCSIID + base);
- scsi_conf = inb(SCSICONF + base) & (ENSPCHK | STIMESEL);
- outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1 + base);
-#if EXPERIMENTAL_FLAGS
- outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1 + base);
-#else
- outb(ENSELTIMO, SIMODE1 + base);
-#endif
+
+ outb(p->scsi_id, p->base + SCSIID);
+ scsi_conf = inb(p->base + SCSICONF);
+ sxfrctl1 = inb(p->base + SXFRCTL1);
+ outb((scsi_conf & (ENSPCHK | STIMESEL)) | (sxfrctl1 & STPWEN) |
+ ENSTIMER | ACTNEGEN, p->base + SXFRCTL1);
+ outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, p->base + SIMODE1);
if (p->flags & ULTRA_ENABLED)
{
- outb(DFON | SPIOEN | ULTRAEN, SXFRCTL0 + base);
+ outb(DFON | SPIOEN | FAST20, p->base + SXFRCTL0);
}
else
{
- outb(DFON | SPIOEN, SXFRCTL0 + base);
+ outb(DFON | SPIOEN, p->base + SXFRCTL0);
+ }
+
+ if ((scsi_conf & RESET_SCSI) && (aic7xxx_no_reset == 0))
+ {
+ /* Reset SCSI bus A. */
+ if (aic7xxx_verbose)
+ printk(KERN_INFO "aic7xxx: Resetting channel A\n");
+
+ aic7xxx_reset_current_bus(p);
+
+ /*
+ * Delay for the reset delay.
+ */
+ aic7xxx_delay(AIC7XXX_RESET_DELAY);
}
/*
@@ -4473,67 +5154,47 @@ aic7xxx_register(Scsi_Host_Template *template,
/*
* Grab the disconnection disable table and invert it for our needs
*/
- if (have_seeprom)
+ if (p->flags & USE_DEFAULTS)
{
- p->discenable = 0x0;
+ printk(KERN_INFO "aic7xxx: Host adapter BIOS disabled. Using default SCSI "
+ "device parameters.\n");
+ p->discenable = 0xFFFF;
}
else
{
- if (config->bios == AIC_DISABLED)
- {
- printk(KERN_INFO "aic7xxx : Host adapter BIOS disabled. Using default SCSI "
- "device parameters.\n");
- p->discenable = 0xFFFF;
- }
- else
- {
- p->discenable = ~((inb(DISC_DSB + base + 1) << 8) |
- inb(DISC_DSB + base));
- }
+ p->discenable = ~((inb(p->base + DISC_DSB + 1) << 8) |
+ inb(p->base + DISC_DSB));
}
for (i = 0; i < max_targets; i++)
{
- if (config->flags & USE_DEFAULTS)
+ if (p->flags & USE_DEFAULTS)
{
- target_settings = 0; /* 10 MHz */
+ target_settings = 0; /* 10 or 20 MHz depending on Ultra enable */
p->needsdtr_copy |= (0x01 << i);
p->needwdtr_copy |= (0x01 << i);
+ if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x))
+ ultraenable |= (0x01 << i);
}
else
{
- if (have_seeprom)
+ target_settings = inb(p->base + TARG_SCRATCH + i);
+ if (target_settings & 0x0F)
{
- target_settings = ((sc.device_flags[i] & CFXFER) << 4);
- if (sc.device_flags[i] & CFSYNCH)
- {
- p->needsdtr_copy |= (0x01 << i);
- }
- if (sc.device_flags[i] & CFWIDEB)
- {
- p->needwdtr_copy |= (0x01 << i);
- }
- if (sc.device_flags[i] & CFDISC)
- {
- p->discenable |= (0x01 << i);
- }
+ p->needsdtr_copy |= (0x01 << i);
+ /*
+ * Default to asynchronous transfers (0 offset)
+ */
+ target_settings &= 0xF0;
}
- else
+ if (target_settings & 0x80)
{
- target_settings = inb(TARG_SCRATCH + base + i);
- if (target_settings & 0x0F)
- {
- p->needsdtr_copy |= (0x01 << i);
- /*
- * Default to asynchronous transfers (0 offset)
- */
- target_settings &= 0xF0;
- }
- if (target_settings & 0x80)
- {
- p->needwdtr_copy |= (0x01 << i);
- target_settings &= 0x7F;
- }
+ p->needwdtr_copy |= (0x01 << i);
+ /*
+ * Clear the wide flag. When wide negotiation is successful,
+ * we'll enable it.
+ */
+ target_settings &= 0x7F;
}
if (p->flags & ULTRA_ENABLED)
{
@@ -4544,7 +5205,7 @@ aic7xxx_register(Scsi_Host_Template *template,
case 0x20:
ultraenable |= (0x01 << i);
break;
- case 0x40:
+ case 0x40: /* treat 10MHz as 10MHz without Ultra enabled */
target_settings &= ~(0x70);
break;
default:
@@ -4552,7 +5213,7 @@ aic7xxx_register(Scsi_Host_Template *template,
}
}
}
- outb(target_settings, (TARG_SCRATCH + base + i));
+ outb(target_settings, p->base + TARG_SCRATCH + i);
}
/*
@@ -4567,103 +5228,448 @@ aic7xxx_register(Scsi_Host_Template *template,
p->needsdtr = p->needsdtr_copy;
p->needwdtr = p->needwdtr_copy;
p->orderedtag = 0;
-#if 0
- printk("NeedSdtr = 0x%x, 0x%x\n", p->needsdtr_copy, p->needsdtr);
- printk("NeedWdtr = 0x%x, 0x%x\n", p->needwdtr_copy, p->needwdtr);
-#endif
- outb(ultraenable & 0xFF, ULTRA_ENB + base);
- outb((ultraenable >> 8) & 0xFF, ULTRA_ENB + base + 1);
+ outb(ultraenable & 0xFF, p->base + ULTRA_ENB);
+ outb((ultraenable >> 8) & 0xFF, p->base + ULTRA_ENB + 1);
/*
- * Set the number of available SCBs.
+ * Set the number of available hardware SCBs.
*/
- outb(config->maxhscbs, SCBCOUNT + base);
+ outb(p->scb_data->maxhscbs, p->base + SCBCOUNT);
/*
* 2s compliment of maximum tag value.
*/
- i = p->maxscbs;
- outb(-i & 0xFF, COMP_SCBCOUNT + base);
+ i = p->scb_data->maxscbs;
+ outb(-i & 0xFF, p->base + COMP_SCBCOUNT);
/*
- * Set the QCNT (queue count) mask to deal with broken aic7850s that
- * sporatically get garbage in the upper bits of their QCNT registers.
+ * Allocate enough hardware scbs to handle the maximum number of
+ * concurrent transactions we can have. We have to make sure that
+ * the allocated memory is contiguous memory. The Linux kmalloc
+ * routine should only allocate contiguous memory, but note that
+ * this could be a problem if kmalloc() is changed.
*/
- outb(config->qcntmask, QCNTMASK + base);
+ if (p->scb_data->hscbs == NULL)
+ {
+ size_t array_size;
+ unsigned int hscb_physaddr;
+
+ array_size = p->scb_data->maxscbs * sizeof(struct aic7xxx_hwscb);
+ p->scb_data->hscbs = kmalloc(array_size, GFP_ATOMIC);
+ if (p->scb_data->hscbs == NULL)
+ {
+ printk("aic7xxx: Unable to allocate hardware SCB array; "
+ "failing detection.\n");
+ release_region(p->base, MAXREG - MINREG);
+ /*
+ * Ensure that we only free the IRQ when there is _not_ another
+ * aic7xxx adapter sharing this IRQ. The adapters are always
+ * added to the beginning of the list, so we can grab the next
+ * pointer and place it back in the board array.
+ */
+ if (p->next == NULL)
+ {
+ free_irq(p->irq, aic7xxx_isr);
+ }
+ aic7xxx_boards[p->irq] = p->next;
+ return(0);
+ }
+ /* At least the control byte of each SCB needs to be 0. */
+ memset(p->scb_data->hscbs, 0, array_size);
+
+ /* Tell the sequencer where it can find the hardware SCB array. */
+ hscb_physaddr = VIRT_TO_BUS(p->scb_data->hscbs);
+ outb(hscb_physaddr & 0xFF, p->base + HSCB_ADDR);
+ outb((hscb_physaddr >> 8) & 0xFF, p->base + HSCB_ADDR + 1);
+ outb((hscb_physaddr >> 16) & 0xFF, p->base + HSCB_ADDR + 2);
+ outb((hscb_physaddr >> 24) & 0xFF, p->base + HSCB_ADDR + 3);
+ }
/*
- * Clear the active flags - no targets are busy.
- */
- outb(0, ACTIVE_A + base);
- outb(0, ACTIVE_B + base);
+ * QCount mask to deal with broken aic7850s that sporadically get
+ * garbage in the upper bits of their QCNT registers.
+ */
+ outb(p->qcntmask, p->base + QCNTMASK);
/*
* We don't have any waiting selections or disconnected SCBs.
*/
- outb(SCB_LIST_NULL, WAITING_SCBH + base);
- outb(SCB_LIST_NULL, DISCONNECTED_SCBH + base);
+ outb(SCB_LIST_NULL, p->base + WAITING_SCBH);
+ outb(SCB_LIST_NULL, p->base + DISCONNECTED_SCBH);
/*
* Message out buffer starts empty
*/
- outb(0, MSG_LEN + base);
+ outb(0, p->base + MSG_LEN);
/*
- * Reset the SCSI bus. Is this necessary?
- * There may be problems for a warm boot without resetting
- * the SCSI bus. Either BIOS settings in scratch RAM
- * will not get reinitialized, or devices may stay at
- * previous negotiated settings (SDTR and WDTR) while
- * the driver will think that no negotiations have been
- * performed.
- *
- * Some devices need a long time to "settle" after a SCSI
- * bus reset.
+ * Load the sequencer program, then re-enable the board -
+ * resetting the AIC-7770 disables it, leaving the lights
+ * on with nobody home. On the PCI bus you *may* be home,
+ * but then your mailing address is dynamically assigned
+ * so no one can find you anyway :-)
+ */
+ aic7xxx_loadseq(p);
+
+ if (p->chip_class == AIC_777x)
+ {
+ outb(ENABLE, p->base + BCTL); /* Enable the boards BUS drivers. */
+ }
+
+ /*
+ * Unpause the sequencer before returning and enable
+ * interrupts - we shouldn't get any until the first
+ * command is sent to us by the high-level SCSI code.
+ */
+ unpause_sequencer(p, /* unpause_always */ TRUE);
+
+ return (found);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_chip_reset
+ *
+ * Description:
+ * Perform a chip reset on the aic7xxx SCSI controller. The controller
+ * is paused upon return.
+ *-F*************************************************************************/
+static void
+aic7xxx_chip_reset(struct aic7xxx_host *p)
+{
+ unsigned char hcntrl;
+ int wait;
+
+ /* Retain the IRQ type across the chip reset. */
+ hcntrl = (inb(p->base + HCNTRL) & IRQMS) | INTEN;
+
+ /*
+ * For some 274x boards, we must clear the CHIPRST bit and pause
+ * the sequencer. For some reason, this makes the driver work.
+ */
+ outb(PAUSE | CHIPRST, p->base + HCNTRL);
+
+ /*
+ * In the future, we may call this function as a last resort for
+ * error handling. Let's be nice and not do any unecessary delays.
+ */
+ wait = 1000; /* 1 second (1000 * 1000 usec) */
+ while ((wait > 0) && ((inb(p->base + HCNTRL) & CHIPRSTACK) == 0))
+ {
+ udelay(1000); /* 1 msec = 1000 usec */
+ wait = wait - 1;
+ }
+
+ if ((inb(p->base + HCNTRL) & CHIPRSTACK) == 0)
+ {
+ printk(KERN_INFO "aic7xxx: Chip reset not cleared; clearing manually.\n");
+ }
+
+ outb(hcntrl | PAUSE, p->base + HCNTRL);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_alloc
+ *
+ * Description:
+ * Allocate and initialize a host structure. Returns NULL upon error
+ * and a pointer to a aic7xxx_host struct upon success.
+ *-F*************************************************************************/
+static struct aic7xxx_host *
+aic7xxx_alloc(Scsi_Host_Template *sht, unsigned int base, unsigned int mbase,
+ aha_chip_type chip_type, int flags, scb_data_type *scb_data)
+{
+ struct aic7xxx_host *p = NULL;
+ struct Scsi_Host *host;
+
+ /*
+ * Allocate a storage area by registering us with the mid-level
+ * SCSI layer.
*/
- if (!aic7xxx_no_reset)
+ host = scsi_register(sht, sizeof(struct aic7xxx_host));
+
+ if (host != NULL)
{
- printk("aic7xxx: Resetting the SCSI bus...");
- if (p->bus_type == AIC_TWIN)
+ p = (struct aic7xxx_host *) host->hostdata;
+ memset(p, 0, sizeof(struct aic7xxx_host));
+ p->host = host;
+
+ if (scb_data != NULL)
+ {
+ /*
+ * We are sharing SCB data areas; use the SCB data pointer
+ * provided.
+ */
+ p->scb_data = scb_data;
+ p->flags |= SHARED_SCBDATA;
+ }
+ else
{
/*
- * Select Channel B.
+ * We are not sharing SCB data; allocate one.
*/
- outb((sblkctl & ~SELBUS_MASK) | SELBUSB, SBLKCTL + base);
+ p->scb_data = kmalloc(sizeof(scb_data_type), GFP_ATOMIC);
+ if (p->scb_data != NULL)
+ {
+ memset(p->scb_data, 0, sizeof(scb_data_type));
+ scbq_init (&p->scb_data->free_scbs);
+ }
+ else
+ {
+ /*
+ * For some reason we don't have enough memory. Free the
+ * allocated memory for the aic7xxx_host struct, and return NULL.
+ */
+ scsi_unregister(host);
+ p = NULL;
+ }
+ }
+ if (p != NULL)
+ {
+ p->host_no = host->host_no;
+ p->base = base;
+ p->mbase = mbase;
+ p->maddr = NULL;
+ p->flags = flags;
+ p->chip_type = chip_type;
+ p->unpause = (inb(p->base + HCNTRL) & IRQMS) | INTEN;
+ p->pause = p->unpause | PAUSE;
+ }
+ }
+ return (p);
+}
- outb(SCSIRSTO, SCSISEQ + base);
- udelay(1000);
- outb(0, SCSISEQ + base);
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_free
+ *
+ * Description:
+ * Frees and releases all resources associated with an instance of
+ * the driver (struct aic7xxx_host *).
+ *-F*************************************************************************/
+static void
+aic7xxx_free (struct aic7xxx_host *p)
+{
+ int i;
- /* Ensure we don't get a RSTI interrupt from this. */
- outb(CLRSCSIRSTI, CLRSINT1 + base);
- outb(CLRSCSIINT, CLRINT + base);
+ /*
+ * We should be careful in freeing the scb_data area. For those
+ * adapters sharing external SCB RAM(398x), there will be only one
+ * scb_data area allocated. The flag SHARED_SCBDATA indicates if
+ * one adapter is sharing anothers SCB RAM.
+ */
+ if (!(p->flags & SHARED_SCBDATA))
+ {
+ /*
+ * Free the allocated hardware SCB space.
+ */
+ if (p->scb_data->hscbs != NULL)
+ {
+ kfree(p->scb_data->hscbs);
+ }
+ /*
+ * Free the driver SCBs. These were allocated on an as-need
+ * basis.
+ */
+ for (i = 0; i < p->scb_data->numscbs; i++)
+ {
+ kfree(p->scb_data->scb_array[i]);
+ }
+ /*
+ * Free the hardware SCBs.
+ */
+ if (p->scb_data->hscbs != NULL)
+ {
+ kfree(p->scb_data->hscbs);
+ }
- /*
- * Select Channel A.
+ /*
+ * Free the SCB data area.
+ */
+ kfree(p->scb_data);
+ }
+ /*
+ * Free the instance of the device structure.
+ */
+ scsi_unregister(p->host);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_load_seeprom
+ *
+ * Description:
+ * Load the seeprom and configure adapter and target settings.
+ * Returns 1 if the load was successful and 0 otherwise.
+ *-F*************************************************************************/
+static int
+load_seeprom (struct aic7xxx_host *p, unsigned char *sxfrctl1)
+{
+ int have_seeprom = 0;
+ int i, max_targets;
+ unsigned char target_settings, scsi_conf;
+ unsigned short scarray[128];
+ struct seeprom_config *sc = (struct seeprom_config *) scarray;
+
+ if (aic7xxx_verbose)
+ {
+ printk(KERN_INFO "aic7xxx: Loading serial EEPROM...");
+ }
+ switch (p->chip_type)
+ {
+ case AIC_7770: /* None of these adapters have seeproms. */
+ case AIC_7771:
+ case AIC_7850:
+ case AIC_7855:
+ break;
+
+ case AIC_284x:
+ have_seeprom = read_284x_seeprom(p, (struct seeprom_config *) scarray);
+ break;
+
+ case AIC_7861:
+ case AIC_7870:
+ case AIC_7871:
+ case AIC_7872:
+ case AIC_7874:
+ case AIC_7881:
+ case AIC_7882:
+ case AIC_7884:
+ have_seeprom = read_seeprom(p, p->chan_num * (sizeof(*sc)/2),
+ scarray, sizeof(*sc)/2, C46);
+ break;
+
+ case AIC_7860: /* Motherboard Ultra controllers might have RAID port. */
+ case AIC_7880:
+ have_seeprom = read_seeprom(p, 0, scarray, sizeof(*sc)/2, C46);
+ if (!have_seeprom)
+ {
+ have_seeprom = read_seeprom(p, 0, scarray, sizeof(scarray)/2, C56_66);
+ }
+ break;
+
+ case AIC_7873: /* The 3985 adapters use the 93c56 serial EEPROM. */
+ case AIC_7883:
+ have_seeprom = read_seeprom(p, p->chan_num * (sizeof(*sc)/2),
+ scarray, sizeof(scarray)/2, C56_66);
+ break;
+
+ default:
+ break;
+ }
+
+ if (!have_seeprom)
+ {
+ if (aic7xxx_verbose)
+ {
+ printk("\naic7xxx: No SEEPROM available; using defaults.\n");
+ }
+ p->flags |= USE_DEFAULTS;
+ }
+ else
+ {
+ if (aic7xxx_verbose)
+ {
+ printk("done\n");
+ }
+ p->flags |= HAVE_SEEPROM;
+
+ /*
+ * Update the settings in sxfrctl1 to match the termination settings.
+ */
+ *sxfrctl1 = 0;
+
+ /*
+ * First process the settings that are different between the VLB
+ * and PCI adapter seeproms.
+ */
+ if (p->chip_class == AIC_777x)
+ {
+ /* VLB adapter seeproms */
+ if (sc->bios_control & CF284XEXTEND)
+ p->flags |= EXTENDED_TRANSLATION;
+
+ if (sc->adapter_control & CF284XSTERM)
+ *sxfrctl1 |= STPWEN;
+ /*
+ * The 284x SEEPROM doesn't have a max targets field. We
+ * set it to 16 to make sure we take care of the 284x-wide
+ * adapters. For narrow adapters, going through the extra
+ * 8 target entries will not cause any harm since they will
+ * will not be used.
+ *
+ * XXX - We should probably break out the bus detection
+ * from the register function so we can use it here
+ * to tell us how many targets there really are.
*/
- outb((sblkctl & ~SELBUS_MASK) | SELNARROW, SBLKCTL + base);
+ max_targets = 16;
}
+ else
+ {
+ /* PCI adapter seeproms */
+ if (sc->bios_control & CFEXTEND)
+ p->flags |= EXTENDED_TRANSLATION;
- outb(SCSIRSTO, SCSISEQ + base);
- udelay(1000);
- outb(0, SCSISEQ + base);
+ if (sc->adapter_control & CFSTERM)
+ *sxfrctl1 |= STPWEN;
- /* Ensure we don't get a RSTI interrupt from this. */
- outb(CLRSCSIRSTI, CLRSINT1 + base);
- outb(CLRSCSIINT, CLRINT + base);
+ /* Limit to 16 targets just in case. */
+ max_targets = MIN(sc->max_targets & CFMAXTARG, 16);
+ }
- aic7xxx_delay(AIC7XXX_RESET_DELAY);
+ for (i = 0; i < max_targets; i++)
+ {
+ target_settings = (sc->device_flags[i] & CFXFER) << 4;
+ if (sc->device_flags[i] & CFSYNCH)
+ target_settings |= SOFS;
+ if (sc->device_flags[i] & CFWIDEB)
+ target_settings |= WIDEXFER;
+ if (sc->device_flags[i] & CFDISC)
+ p->discenable |= (0x01 << i);
+ outb(target_settings, p->base + TARG_SCRATCH + i);
+ }
+ outb(~(p->discenable & 0xFF), p->base + DISC_DSB);
+ outb(~((p->discenable >> 8) & 0xFF), p->base + DISC_DSB + 1);
- printk("done.\n");
- }
+ p->scsi_id = sc->brtime_id & CFSCSIID;
- /*
- * Unpause the sequencer before returning and enable
- * interrupts - we shouldn't get any until the first
- * command is sent to us by the high-level SCSI code.
- */
- UNPAUSE_SEQUENCER(p);
- return (found);
+ scsi_conf = (p->scsi_id & 0x7);
+ if (sc->adapter_control & CFSPARITY)
+ scsi_conf |= ENSPCHK;
+ if (sc->adapter_control & CFRESETB)
+ scsi_conf |= RESET_SCSI;
+
+ if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x))
+ {
+ /*
+ * We allow the operator to override ultra enable through
+ * the boot prompt.
+ */
+ if (!(sc->adapter_control & CFULTRAEN) && (aic7xxx_enable_ultra == 0))
+ {
+ /* Treat us as a non-ultra card */
+ p->flags &= ~ULTRA_ENABLED;
+ }
+ }
+
+ /* Set the host ID */
+ outb(scsi_conf, p->base + SCSICONF);
+ /* In case we are a wide card */
+ outb(p->scsi_id, p->base + SCSICONF + 1);
+
+ if (p->chip_class != AIC_777x)
+ {
+ /*
+ * Update the settings in sxfrctl1 to match the termination
+ * settings.
+ */
+ *sxfrctl1 = 0;
+ configure_termination(p, sxfrctl1, sc->adapter_control,
+ (unsigned char) sc->max_targets & CFMAXTARG);
+ }
+ }
+ return (have_seeprom);
}
/*+F*************************************************************************
@@ -4672,17 +5678,24 @@ aic7xxx_register(Scsi_Host_Template *template,
*
* Description:
* Try to detect and register an Adaptec 7770 or 7870 SCSI controller.
+ *
+ * XXX - This should really be called aic7xxx_probe(). A sequence of
+ * probe(), attach()/detach(), and init() makes more sense than
+ * one do-it-all function. This may be useful when (and if) the
+ * mid-level SCSI code is overhauled.
*-F*************************************************************************/
int
aic7xxx_detect(Scsi_Host_Template *template)
{
- int found = 0, slot, base;
- unsigned char irq = 0;
+ int found = 0;
+ aha_status_type adapter_bios;
+ aha_chip_class_type chip_class;
+ aha_chip_type chip_type;
+ int slot, base;
+ int chan_num = 0;
+ unsigned char hcntrl, sxfrctl1, sblkctl, hostconf, irq = 0;
int i;
- struct aic7xxx_host_config config;
-
- template->proc_dir = &proc_scsi_aic7xxx;
- config.chan_num = 0;
+ struct aic7xxx_host *p;
/*
* Since we may allow sharing of IRQs, it is imperative
@@ -4696,6 +5709,10 @@ aic7xxx_detect(Scsi_Host_Template *template)
aic7xxx_boards[i] = NULL;
}
+ template->proc_dir = &proc_scsi_aic7xxx;
+ template->name = aic7xxx_info(NULL);
+ template->sg_tablesize = AIC7XXX_MAX_SG;
+
/*
* Initialize the spurious count to 0.
*/
@@ -4717,33 +5734,174 @@ aic7xxx_detect(Scsi_Host_Template *template)
continue;
}
- config.type = aic7xxx_probe(slot, HID0 + base, &(config.bios));
- if (config.type != AIC_NONE)
+ chip_type = aic7xxx_probe(slot, base + HID0, &(adapter_bios));
+ if (chip_type != AIC_NONE)
{
+
+ switch (chip_type)
+ {
+ case AIC_7770:
+ case AIC_7771:
+ printk("aic7xxx: <%s> at EISA %d\n",
+ board_names[chip_type], slot);
+ break;
+ case AIC_284x:
+ printk("aic7xxx: <%s> at VLB %d\n",
+ board_names[chip_type], slot);
+ break;
+ default:
+ break;
+ }
+
/*
* We found a card, allow 1 spurious interrupt.
*/
aic7xxx_spurious_count = 1;
/*
- * We "find" a AIC-7770 if we locate the card
- * signature and we can set it up and register
- * it with the kernel without incident.
+ * Pause the card preserving the IRQ type. Allow the operator
+ * to override the IRQ trigger.
*/
- config.chip_type = AIC_777x;
- config.base = base;
- config.mbase = 0;
- config.irq = irq;
- config.parity = AIC_ENABLED;
- config.low_term = AIC_UNKNOWN;
- config.high_term = AIC_UNKNOWN;
- config.flags = 0;
- if (aic7xxx_extended)
- config.flags |= EXTENDED_TRANSLATION;
- config.bus_speed = DFTHRSH_100;
- config.busrtime = BOFF;
- found += aic7xxx_register(template, &config);
+ if (aic7xxx_irq_trigger == 1)
+ hcntrl = IRQMS; /* Level */
+ else if (aic7xxx_irq_trigger == 0)
+ hcntrl = 0; /* Edge */
+ else
+ hcntrl = inb(base + HCNTRL) & IRQMS; /* Default */
+ outb(hcntrl | PAUSE, base + HCNTRL);
+
+ irq = inb(INTDEF + base) & 0x0F;
+ switch (irq)
+ {
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 14:
+ case 15:
+ break;
+
+ default:
+ printk(KERN_WARNING "aic7xxx: Host adapter uses unsupported IRQ "
+ "level, ignoring.\n");
+ irq = 0;
+ break;
+ }
+
+ if (irq != 0)
+ {
+ p = aic7xxx_alloc(template, base, 0, chip_type, 0, NULL);
+ if (p == NULL)
+ {
+ printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n");
+ continue;
+ }
+ p->irq = irq & 0x0F;
+ p->chip_class = AIC_777x;
+#ifdef AIC7XXX_PAGE_ENABLE
+ p->flags |= PAGE_ENABLED;
+#endif
+ p->instance = found;
+ if (aic7xxx_extended)
+ {
+ p->flags |= EXTENDED_TRANSLATION;
+ }
+ aic7xxx_chip_reset(p);
+
+ switch (p->chip_type)
+ {
+ case AIC_7770:
+ case AIC_7771:
+ {
+ unsigned char biosctrl = inb(p->base + HA_274_BIOSCTRL);
+
+ /*
+ * Get the primary channel information. Right now we don't
+ * do anything with this, but someday we will be able to inform
+ * the mid-level SCSI code which channel is primary.
+ */
+ if (biosctrl & CHANNEL_B_PRIMARY)
+ {
+ p->flags |= FLAGS_CHANNEL_B_PRIMARY;
+ }
+
+ if ((biosctrl & BIOSMODE) == BIOSDISABLED)
+ {
+ p->flags |= USE_DEFAULTS;
+ }
+ break;
+ }
+
+ case AIC_284x:
+ if (!load_seeprom(p, &sxfrctl1))
+ {
+ if (aic7xxx_verbose)
+ printk(KERN_INFO "aic7xxx: SEEPROM not available.\n");
+ }
+ break;
+
+ default: /* Won't get here. */
+ break;
+ }
+ printk(KERN_INFO "aic7xxx: BIOS %sabled, IO Port 0x%x, IRQ %d (%s), ",
+ (p->flags & USE_DEFAULTS) ? "dis" : "en", p->base, p->irq,
+ (p->pause & IRQMS) ? "level sensitive" : "edge triggered");
+ /*
+ * Check for Rev C or E boards. Rev E boards can supposedly have
+ * more than 4 SCBs, while the Rev C boards are limited to 4 SCBs.
+ * It's still not clear extactly what is different about the Rev E
+ * boards, but we think it allows 8 bit entries in the QOUTFIFO to
+ * support "paging" SCBs (more than 4 commands can be active at once).
+ *
+ * The Rev E boards have a read/write autoflush bit in the
+ * SBLKCTL register, while in the Rev C boards it is read only.
+ */
+ sblkctl = inb(p->base + SBLKCTL) ^ AUTOFLUSHDIS;
+ outb(sblkctl, p->base + SBLKCTL);
+ if (inb(p->base + SBLKCTL) == sblkctl)
+ {
+ /*
+ * We detected a Rev E board, we allow paging on this board.
+ */
+ printk("Revision >= E\n");
+ outb(sblkctl & ~AUTOFLUSHDIS, base + SBLKCTL);
+ }
+ else
+ {
+ /* Do not allow paging. */
+ p->flags &= ~PAGE_ENABLED;
+ printk("Revision <= C\n");
+ }
+
+ if (aic7xxx_verbose)
+ printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
+ (p->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
+
+ /*
+ * Set the FIFO threshold and the bus off time.
+ */
+ hostconf = inb(p->base + HOSTCONF);
+ outb(hostconf & DFTHRSH, p->base + BUSSPD);
+ outb((hostconf << 2) & BOFF, p->base + BUSTIME);
+ /*
+ * Try to initialize the card and register it with the kernel.
+ */
+ if (aic7xxx_register(template, p))
+ {
+ /*
+ * We successfully found a board and registered it.
+ */
+ found = found + 1;
+ }
+ else
+ {
+ /*
+ * Something went wrong; release and free all resources.
+ */
+ aic7xxx_free(p);
+ }
+ }
/*
* Disallow spurious interrupts.
*/
@@ -4759,15 +5917,15 @@ aic7xxx_detect(Scsi_Host_Template *template)
{
struct
{
- unsigned short vendor_id;
- unsigned short device_id;
- aha_type card_type;
- aha_chip_type chip_type;
+ unsigned short vendor_id;
+ unsigned short device_id;
+ aha_chip_type chip_type;
+ aha_chip_class_type chip_class;
} const aic7xxx_pci_devices[] = {
{PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7850, AIC_7850, AIC_785x},
{PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7855, AIC_7855, AIC_785x},
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AIC_7860, AIC_785x},
- {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AIC_7861, AIC_785x},
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AIC_7860, AIC_786x},
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AIC_7861, AIC_786x},
{PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7870, AIC_7870, AIC_787x},
{PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7871, AIC_7871, AIC_787x},
{PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7872, AIC_7872, AIC_787x},
@@ -4780,14 +5938,14 @@ aic7xxx_detect(Scsi_Host_Template *template)
{PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AIC_7884, AIC_788x}
};
- int error;
+ int error, flags;
int done = 0;
unsigned int iobase, mbase;
unsigned short index = 0;
unsigned char pci_bus, pci_device_fn;
- unsigned int csize_lattime;
- unsigned int class_revid;
- unsigned int devconfig;
+ unsigned char ultra_enb = 0;
+ unsigned int devconfig, class_revid;
+ scb_data_type *shared_scb_data = NULL;
char rev_id[] = {'B', 'C', 'D'};
for (i = 0; i < NUMBER(aic7xxx_pci_devices); i++)
@@ -4804,36 +5962,33 @@ aic7xxx_detect(Scsi_Host_Template *template)
}
else /* Found an Adaptec PCI device. */
{
- config.type = aic7xxx_pci_devices[i].card_type;
- config.chip_type = aic7xxx_pci_devices[i].chip_type;
- config.chan_num = 0;
- config.bios = AIC_ENABLED; /* Assume bios is enabled. */
- config.flags = 0;
- config.busrtime = 40;
- switch (config.type)
+ chip_class = aic7xxx_pci_devices[i].chip_class;
+ chip_type = aic7xxx_pci_devices[i].chip_type;
+ chan_num = 0;
+ flags = 0;
+ switch (aic7xxx_pci_devices[i].chip_type)
{
case AIC_7850:
case AIC_7855:
- case AIC_7860:
- case AIC_7861:
- config.bios = AIC_DISABLED;
- config.flags |= USE_DEFAULTS;
- config.bus_speed = DFTHRSH_100;
+ flags |= USE_DEFAULTS;
break;
case AIC_7872: /* 3940 */
case AIC_7882: /* 3940-Ultra */
- config.chan_num = number_of_3940s & 0x1; /* Has 2 controllers */
+ flags |= MULTI_CHANNEL;
+ chan_num = number_of_3940s & 0x1; /* Has 2 controllers */
number_of_3940s++;
break;
case AIC_7873: /* 3985 */
case AIC_7883: /* 3985-Ultra */
- config.chan_num = number_of_3985s; /* Has 3 controllers */
+ chan_num = number_of_3985s; /* Has 3 controllers */
+ flags |= MULTI_CHANNEL;
number_of_3985s++;
if (number_of_3985s == 3)
{
number_of_3985s = 0;
+ shared_scb_data = NULL;
}
break;
@@ -4850,39 +6005,165 @@ aic7xxx_detect(Scsi_Host_Template *template)
PCI_INTERRUPT_LINE, &irq);
error += pcibios_read_config_dword(pci_bus, pci_device_fn,
PCI_BASE_ADDRESS_1, &mbase);
+ error += pcibios_read_config_dword(pci_bus, pci_device_fn,
+ DEVCONFIG, &devconfig);
+ error += pcibios_read_config_dword(pci_bus, pci_device_fn,
+ CLASS_PROGIF_REVID, &class_revid);
+
+ printk("aic7xxx: <%s> at PCI %d\n",
+ board_names[chip_type], PCI_SLOT(pci_device_fn));
/*
- * The first bit of PCI_BASE_ADDRESS_0 is always set, so
+ * The first bit (LSB) of PCI_BASE_ADDRESS_0 is always set, so
* we mask it off.
*/
iobase &= PCI_BASE_ADDRESS_IO_MASK;
+ p = aic7xxx_alloc(template, iobase, mbase, chip_type, flags,
+ shared_scb_data);
+
+ if (p == NULL)
+ {
+ printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n");
+ continue;
+ }
+
+ /* Remember to set the channel number, irq, and chip class. */
+ p->chan_num = chan_num;
+ p->irq = irq;
+ p->chip_class = chip_class;
+#ifdef AIC7XXX_PAGE_ENABLE
+ p->flags |= PAGE_ENABLED;
+#endif
+ p->instance = found;
+
/*
- * Read the PCI burst size and latency timer.
+ * Remember how the card was setup in case there is no seeprom.
*/
- error += pcibios_read_config_dword(pci_bus, pci_device_fn,
- CSIZE_LATTIME, &csize_lattime);
- printk(KERN_INFO "aic7xxx: BurstLen = %d DWDs, Latency Timer = %d "
- "PCLKS\n", (int) (csize_lattime & CACHESIZE),
- (csize_lattime >> 8) & 0x000000ff);
+ p->scsi_id = inb(p->base + SCSIID) & OID;
+ if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x))
+ {
+ p->flags |= ULTRA_ENABLED;
+ ultra_enb = inb(p->base + SXFRCTL1) & FAST20;
+ }
+ sxfrctl1 = inb(p->base + SXFRCTL1) & STPWEN;
- error += pcibios_read_config_dword(pci_bus, pci_device_fn,
- CLASS_PROGIF_REVID, &class_revid);
- if ((class_revid & DEVREVID) < 3)
+ aic7xxx_chip_reset(p);
+
+#ifdef AIC7XXX_USE_EXT_SCBRAM
+ if (devconfig & RAMPSM)
{
- printk(KERN_INFO "aic7xxx: %s Rev %c.\n", board_names[config.type],
- rev_id[class_revid & DEVREVID]);
+ printk(KERN_INFO "aic7xxx: External RAM detected; enabling RAM "
+ "access.\n");
+ /*
+ * XXX - Assume 9 bit SRAM and enable parity checking.
+ */
+ devconfig |= EXTSCBPEN;
+
+ /*
+ * XXX - Assume fast SRAM and only enable 2 cycle access if we
+ * are sharing the SRAM across multiple adapters (398x).
+ */
+ if ((devconfig & MPORTMODE) == 0)
+ {
+ devconfig |= EXTSCBTIME;
+ }
+ devconfig &= ~SCBRAMSEL;
+ pcibios_write_config_dword(pci_bus, pci_device_fn,
+ DEVCONFIG, devconfig);
}
+#endif
- error += pcibios_read_config_dword(pci_bus, pci_device_fn,
- DEVCONFIG, &devconfig);
- if (error)
+ if ((p->flags & USE_DEFAULTS) == 0)
{
- panic("aic7xxx: (aic7xxx_detect) Error %d reading PCI registers.\n",
- error);
+ load_seeprom(p, &sxfrctl1);
+ }
+
+ /*
+ * Take the LED out of diagnostic mode
+ */
+ sblkctl = inb(p->base + SBLKCTL);
+ outb((sblkctl & ~(DIAGLEDEN | DIAGLEDON)), p->base + SBLKCTL);
+
+ /*
+ * We don't know where this is set in the SEEPROM or by the
+ * BIOS, so we default to 100%.
+ */
+ outb(DFTHRSH_100, p->base + DSPCISTATUS);
+
+ if (p->flags & USE_DEFAULTS)
+ {
+ int j;
+ /*
+ * Default setup; should only be used if the adapter does
+ * not have a SEEPROM.
+ */
+ /*
+ * Check the target scratch area to see if someone set us
+ * up already. We are previously set up if the scratch
+ * area contains something other than all zeroes and ones.
+ */
+ for (j = TARG_SCRATCH; j < 0x60; j++)
+ {
+ if (inb(p->base + j) != 0x00) /* Check for all zeroes. */
+ break;
+ }
+ if (j == TARG_SCRATCH)
+ {
+ for (j = TARG_SCRATCH; j < 0x60; j++)
+ {
+ if (inb(p->base + 1) != 0xFF) /* Check for all ones. */
+ break;
+ }
+ }
+ if ((j != 0x60) && (p->scsi_id != 0))
+ {
+ p->flags &= ~USE_DEFAULTS;
+ if (aic7xxx_verbose)
+ {
+ printk(KERN_INFO "aic7xxx: Using leftover BIOS values.\n");
+ }
+ }
+ else
+ {
+ if (aic7xxx_verbose)
+ {
+ printk(KERN_INFO "aic7xxx: No BIOS found; using default "
+ "settings.\n");
+ }
+ /*
+ * Assume only one connector and always turn on
+ * termination.
+ */
+ sxfrctl1 = STPWEN;
+ p->scsi_id = 7;
+ }
+ outb((p->scsi_id & HSCSIID) | ENSPCHK | RESET_SCSI,
+ p->base + SCSICONF);
+ /* In case we are a wide card. */
+ outb(p->scsi_id, p->base + SCSICONF + 1);
+ if ((ultra_enb == 0) && ((p->flags & USE_DEFAULTS) == 0))
+ {
+ /*
+ * If there wasn't a BIOS or the board wasn't in this mode
+ * to begin with, turn off Ultra.
+ */
+ p->flags &= ~ULTRA_ENABLED;
+ }
}
- printk(KERN_INFO "aic7xxx: devconfig = 0x%x.\n", devconfig);
+ /*
+ * Print some additional information about the adapter.
+ */
+ printk(KERN_INFO "aic7xxx: BIOS %sabled, IO Port 0x%x, "
+ "IO Mem 0x%x, IRQ %d",
+ (p->flags & USE_DEFAULTS) ? "dis" : "en",
+ p->base, p->mbase, p->irq);
+ if ((class_revid & DEVREVID) < 3)
+ {
+ printk(", Revision %c", rev_id[class_revid & DEVREVID]);
+ }
+ printk("\n");
/*
* I don't think we need to bother with allowing
@@ -4891,58 +6172,57 @@ aic7xxx_detect(Scsi_Host_Template *template)
*/
aic7xxx_spurious_count = 1;
- config.base = iobase;
- config.mbase = mbase;
- config.irq = irq;
- config.parity = AIC_ENABLED;
- config.low_term = AIC_UNKNOWN;
- config.high_term = AIC_UNKNOWN;
if (aic7xxx_extended)
- config.flags |= EXTENDED_TRANSLATION;
-#ifdef AIC7XXX_SHARE_SCBs
- if (devconfig & RAMPSM)
-#else
- if ((devconfig & RAMPSM) && (config.type != AIC_7873) &&
- (config.type != AIC_7883))
-#endif
+ p->flags |= EXTENDED_TRANSLATION;
+
+ if (aic7xxx_verbose)
+ printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
+ (p->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
+
+ /*
+ * Put our termination setting into sxfrctl1 now that the
+ * generic initialization is complete.
+ */
+ sxfrctl1 |= inb(p->base + SXFRCTL1);
+ outb(sxfrctl1, p->base + SXFRCTL1);
+
+ if (aic7xxx_register(template, p) == 0)
{
+ aic7xxx_free(p);
+ }
+ else
+ {
+ found = found + 1;
+
+#ifdef AIC7XXX_USE_EXT_SCBRAM
/*
- * External SRAM present. The probe will walk the SCBs to see
- * how much SRAM we have and set the number of SCBs accordingly.
- * We have to turn off SCBRAMSEL to access the external SCB
- * SRAM.
- *
- * It seems that early versions of the aic7870 didn't use these
- * bits, hence the hack for the 3940 above. I would guess that
- * recent 3940s using later aic7870 or aic7880 chips do actually
- * set RAMPSM.
+ * Set the shared SCB data once we've successfully probed a
+ * 398x adapter.
*
- * The documentation isn't clear, but it sounds like the value
- * written to devconfig must not have RAMPSM set. The second
- * sixteen bits of the register are R/O anyway, so it shouldn't
- * affect RAMPSM either way.
+ * Note that we can only do this if the use of external
+ * SCB RAM is enabled.
*/
- printk(KERN_INFO "aic7xxx: External RAM detected; enabling RAM "
- "access.\n");
- devconfig &= ~(RAMPSM | SCBRAMSEL);
- pcibios_write_config_dword(pci_bus, pci_device_fn,
- DEVCONFIG, devconfig);
+ if ((p->chip_type == AIC_7873) || (p->chip_type == AIC_7883))
+ {
+ if (shared_scb_data == NULL)
+ {
+ shared_scb_data = p->scb_data;
+ }
+ }
+#endif
}
- found += aic7xxx_register(template, &config);
+ index++;
/*
* Disable spurious interrupts.
*/
aic7xxx_spurious_count = 0;
-
- index++;
} /* Found an Adaptec PCI device. */
}
}
}
#endif CONFIG_PCI
- template->name = aic7xxx_info(NULL);
return (found);
}
@@ -4958,45 +6238,45 @@ static void
aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd,
struct aic7xxx_scb *scb)
{
- unsigned int addr; /* must be 32 bits */
unsigned short mask;
+ struct aic7xxx_hwscb *hscb;
mask = (0x01 << TARGET_INDEX(cmd));
+ hscb = scb->hscb;
+
/*
* Setup the control byte if we need negotiation and have not
* already requested it.
*/
-#ifdef AIC7XXX_TAGGED_QUEUEING
- if (cmd->device->tagged_queue)
+ if (p->discenable & mask)
{
- cmd->tag = scb->tag;
- cmd->device->current_tag = scb->tag;
- scb->control |= TAG_ENB;
- p->device_status[TARGET_INDEX(cmd)].commands_sent++;
- if (p->device_status[TARGET_INDEX(cmd)].commands_sent == 200)
- {
- scb->control |= 0x02;
- p->device_status[TARGET_INDEX(cmd)].commands_sent = 0;
- }
-#if 0
- if (p->orderedtag & mask)
+ hscb->control |= DISCENB;
+#ifdef AIC7XXX_TAGGED_QUEUEING
+ if (cmd->device->tagged_queue)
{
- scb->control |= 0x02;
- p->orderedtag = p->orderedtag & ~mask;
+ cmd->tag = hscb->tag;
+ p->device_status[TARGET_INDEX(cmd)].commands_sent++;
+ if (p->device_status[TARGET_INDEX(cmd)].commands_sent < 75)
+ {
+ hscb->control |= MSG_SIMPLE_Q_TAG;
+ }
+ else
+ {
+ hscb->control |= MSG_ORDERED_Q_TAG;
+ p->device_status[TARGET_INDEX(cmd)].commands_sent = 0;
+ }
}
-#endif
- }
-#endif
- if (p->discenable & mask)
- {
- scb->control |= DISCENB;
+#endif /* Tagged queueing */
}
+
if ((p->needwdtr & mask) && !(p->wdtr_pending & mask))
{
p->wdtr_pending |= mask;
- scb->control |= NEEDWDTR;
+ hscb->control |= MK_MESSAGE;
+ scb->flags |= SCB_MSGOUT_WDTR;
#if 0
- printk("aic7xxx: Sending WDTR request to target %d.\n", cmd->target);
+ printk("scsi%d: Sending WDTR request to target %d.\n",
+ p->host_no, cmd->target);
#endif
}
else
@@ -5004,19 +6284,20 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd,
if ((p->needsdtr & mask) && !(p->sdtr_pending & mask))
{
p->sdtr_pending |= mask;
- scb->control |= NEEDSDTR;
+ hscb->control |= MK_MESSAGE;
+ scb->flags |= SCB_MSGOUT_SDTR;
#if 0
- printk("aic7xxx: Sending SDTR request to target %d.\n", cmd->target);
+ printk("scsi%d: Sending SDTR request to target %d.\n",
+ p->host_no, cmd->target);
#endif
}
}
-
#if 0
printk("aic7xxx: (build_scb) Target %d, cmd(0x%x) size(%u) wdtr(0x%x) "
"mask(0x%x).\n",
cmd->target, cmd->cmnd[0], cmd->cmd_len, p->needwdtr, mask);
#endif
- scb->target_channel_lun = ((cmd->target << 4) & 0xF0) |
+ hscb->target_channel_lun = ((cmd->target << 4) & 0xF0) |
((cmd->channel & 0x01) << 3) | (cmd->lun & 0x07);
/*
@@ -5030,9 +6311,8 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd,
* XXX - this relies on the host data being stored in a
* little-endian format.
*/
- addr = VIRT_TO_BUS(cmd->cmnd);
- scb->SCSI_cmd_length = cmd->cmd_len;
- memcpy(scb->SCSI_cmd_pointer, &addr, sizeof(scb->SCSI_cmd_pointer));
+ hscb->SCSI_cmd_length = cmd->cmd_len;
+ hscb->SCSI_cmd_pointer = VIRT_TO_BUS(cmd->cmnd);
if (cmd->use_sg)
{
@@ -5052,15 +6332,16 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd,
scb->sg_list[i].address = VIRT_TO_BUS(sg[i].address);
scb->sg_list[i].length = (unsigned int) sg[i].length;
}
- scb->SG_segment_count = cmd->use_sg;
- addr = VIRT_TO_BUS(scb->sg_list);
- memcpy(scb->SG_list_pointer, &addr, sizeof(scb->SG_list_pointer));
- memcpy(scb->data_pointer, &(scb->sg_list[0].address),
- sizeof(scb->data_pointer));
- scb->data_count = scb->sg_list[0].length;
+ hscb->SG_list_pointer = VIRT_TO_BUS(scb->sg_list);
+ hscb->SG_segment_count = cmd->use_sg;
+ scb->sg_count = hscb->SG_segment_count;
+
+ /* Copy the first SG into the data pointer area. */
+ hscb->data_pointer = scb->sg_list[0].address;
+ hscb->data_count = scb->sg_list[0].length | (SCB_LIST_NULL << 24);
#if 0
printk("aic7xxx: (build_scb) SG segs(%d), length(%u), sg[0].length(%d).\n",
- cmd->use_sg, aic7xxx_length(cmd, 0), scb->data_count);
+ cmd->use_sg, aic7xxx_length(cmd, 0), hscb->data_count);
#endif
}
else
@@ -5069,28 +6350,23 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd,
printk("aic7xxx: (build_scb) Creating scatterlist, addr(0x%lx) length(%d).\n",
(unsigned long) cmd->request_buffer, cmd->request_bufflen);
#endif
- if (cmd->request_bufflen == 0)
+ if (cmd->request_bufflen)
{
- /*
- * In case the higher level SCSI code ever tries to send a zero
- * length command, ensure the SCB indicates no data. The driver
- * will interpret a zero length command as a Bus Device Reset.
- */
- scb->SG_segment_count = 0;
- memset(scb->SG_list_pointer, 0, sizeof(scb->SG_list_pointer));
- memset(scb->data_pointer, 0, sizeof(scb->data_pointer));
- scb->data_count = 0;
+ hscb->SG_segment_count = 1;
+ scb->sg_count = 1;
+ scb->sg_list[0].address = VIRT_TO_BUS(cmd->request_buffer);
+ scb->sg_list[0].length = cmd->request_bufflen;
+ hscb->SG_list_pointer = VIRT_TO_BUS(&scb->sg_list[0]);
+ hscb->data_count = scb->sg_list[0].length | (SCB_LIST_NULL << 24);
+ hscb->data_pointer = VIRT_TO_BUS(cmd->request_buffer);
}
else
{
- scb->SG_segment_count = 1;
- scb->sg_list[0].address = VIRT_TO_BUS(cmd->request_buffer);
- scb->sg_list[0].length = cmd->request_bufflen;
- addr = VIRT_TO_BUS(&scb->sg_list[0]);
- memcpy(scb->SG_list_pointer, &addr, sizeof(scb->SG_list_pointer));
- scb->data_count = scb->sg_list[0].length;
- addr = VIRT_TO_BUS(cmd->request_buffer);
- memcpy(scb->data_pointer, &addr, sizeof(scb->data_pointer));
+ hscb->SG_segment_count = 0;
+ scb->sg_count = 0;
+ hscb->SG_list_pointer = 0;
+ hscb->data_pointer = 0;
+ hscb->data_count = SCB_LIST_NULL << 24;
}
}
}
@@ -5108,7 +6384,6 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
long processor_flags;
struct aic7xxx_host *p;
struct aic7xxx_scb *scb;
- u_char curscb, intstat;
p = (struct aic7xxx_host *) cmd->host->hostdata;
if (p->host != cmd->host)
@@ -5140,34 +6415,21 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
cmd->lun & 0x07);
#endif
- /*
- * This is a critical section, since we don't want the interrupt
- * routine mucking with the host data or the card. For this reason
- * it is nice to know that this function can only be called in one
- * of two ways from scsi.c First, as part of a routine queue command,
- * in which case, the irq for our card is disabled before this
- * function is called. This doesn't help us if there is more than
- * one card using more than one IRQ in our system, therefore, we
- * should disable all interrupts on these grounds alone. Second,
- * this can be called as part of the scsi_done routine, in which case
- * we are in the aic7xxx_isr routine already and interrupts are
- * disabled, therefore we should saveflags first, then disable the
- * interrupts, do our work, then restore the CPU flags. If it weren't
- * for the possibility of more than one card using more than one IRQ
- * in our system, we wouldn't have to touch the interrupt flags at all.
- */
- save_flags(processor_flags);
- cli();
-
+ if (p->device_status[TARGET_INDEX(cmd)].active_cmds
+ > cmd->device->queue_depth)
+ {
+ printk(KERN_WARNING "(scsi%d:%d:%d) Commands queued exceeds queue depth\n",
+ p->host_no, cmd->target, cmd->channel);
+ }
scb = aic7xxx_allocate_scb(p);
if (scb == NULL)
{
- panic("aic7xxx: (aic7xxx_free) Couldn't find a free SCB.\n");
+ panic("aic7xxx: (aic7xxx_queue) Couldn't find a free SCB.\n");
}
else
{
scb->cmd = cmd;
- aic7xxx_position(cmd) = scb->tag;
+ aic7xxx_position(cmd) = scb->hscb->tag;
#if 0
debug_scb(scb);
#endif;
@@ -5179,14 +6441,14 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
aic7xxx_buildscb(p, cmd, scb);
#if 0
- if (scb != (p->scb_array[scb->position]))
+ if (scb != (p->scb_data->scb_array[scb->hscb->tag]))
{
printk("aic7xxx: (queue) Address of SCB by position does not match SCB "
"address.\n");
}
printk("aic7xxx: (queue) SCB pos(%d) cmdptr(0x%x) state(%d) freescb(0x%x)\n",
- scb->position, (unsigned int) scb->cmd,
- scb->state, (unsigned int) p->free_scb);
+ scb->hscb->tag, (unsigned int) scb->cmd,
+ scb->flags, (unsigned int) p->free_scb);
#endif
/*
@@ -5201,70 +6463,28 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
cmd->host_scribble = NULL;
memset(&cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
- if (scb->position != SCB_LIST_NULL)
- {
- /* We've got a valid slot, yeah! */
- if (p->flags & IN_ISR)
- {
- scbq_insert_tail(&p->assigned_scbs, scb);
- scb->state |= SCB_ASSIGNEDQ;
- }
- else
- {
- /*
- * Pause the sequencer so we can play with its registers -
- * wait for it to acknowledge the pause.
- *
- * XXX - should the interrupts be left on while doing this?
- */
- PAUSE_SEQUENCER(p);
- intstat = inb(INTSTAT + p->base);
-
- /*
- * Save the SCB pointer and put our own pointer in - this
- * selects one of the four banks of SCB registers. Load
- * the SCB, then write its pointer into the queue in FIFO
- * and restore the saved SCB pointer.
- */
- curscb = inb(SCBPTR + p->base);
- outb(scb->position, SCBPTR + p->base);
- aic7xxx_putscb(p, scb);
- outb(curscb, SCBPTR + p->base);
- outb(scb->position, QINFIFO + p->base);
- scb->state |= SCB_ACTIVE;
+ scb->flags |= SCB_ACTIVE | SCB_WAITINGQ;
- /*
- * Guard against unpausing the sequencer if there is an interrupt
- * waiting to happen.
- */
- if (!(intstat & (BRKADRINT | SEQINT | SCSIINT)))
- {
- UNPAUSE_SEQUENCER(p);
- }
- }
- }
- else
+ save_flags(processor_flags);
+ cli();
+ scbq_insert_tail(&p->waiting_scbs, scb);
+ if ((p->flags & (IN_ISR | IN_TIMEOUT)) == 0)
{
- scb->state |= SCB_WAITINGQ;
- scbq_insert_tail(&p->waiting_scbs, scb);
- if (!(p->flags & IN_ISR))
- {
- aic7xxx_run_waiting_queues(p);
- }
+ aic7xxx_run_waiting_queues(p);
}
+ restore_flags(processor_flags);
#if 0
printk("aic7xxx: (queue) After - cmd(0x%lx) scb->cmd(0x%lx) pos(%d).\n",
- (long) cmd, (long) scb->cmd, scb->position);
+ (long) cmd, (long) scb->cmd, scb->hscb->tag);
#endif;
- restore_flags(processor_flags);
}
return (0);
}
/*+F*************************************************************************
* Function:
- * aic7xxx_abort_reset
+ * aic7xxx_bus_device_reset
*
* Description:
* Abort or reset the current SCSI command(s). If the scb has not
@@ -5276,204 +6496,257 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
static int
aic7xxx_bus_device_reset(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
{
- struct aic7xxx_scb *scb;
+ struct aic7xxx_scb *scb;
+ struct aic7xxx_hwscb *hscb;
unsigned char bus_state;
- int base, result = -1;
+ int result = -1;
char channel;
- scb = (p->scb_array[aic7xxx_position(cmd)]);
- base = p->base;
+ scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
+ hscb = scb->hscb;
- channel = scb->target_channel_lun & SELBUSB ? 'B': 'A';
- if ((cmd == scb->cmd) && (scb->state & SCB_IN_PROGRESS))
+ /*
+ * Ensure that the card doesn't do anything behind our back.
+ * Also make sure that we didn't just miss an interrupt that
+ * could affect this abort/reset.
+ */
+ pause_sequencer(p);
+ while (inb(p->base + INTSTAT) & INT_PEND);
{
+ aic7xxx_isr(p->irq, (void *) NULL, (void *) NULL);
+ pause_sequencer(p);
+ }
+ if ((cmd != scb->cmd) || ((scb->flags & SCB_ACTIVE) == 0))
+ {
+ result = SCSI_RESET_NOT_RUNNING;
+ unpause_sequencer(p, /* unpause_always */ TRUE);
+ return(result);
+ }
+
- if (scb->state & SCB_IN_PROGRESS)
+ printk(KERN_WARNING "(scsi%d:%d:%d) Abort_reset, scb flags 0x%x, ",
+ p->host_no, TC_OF_SCB(scb), scb->flags);
+ bus_state = inb(p->base + LASTPHASE);
+
+ switch (bus_state)
+ {
+ case P_DATAOUT:
+ printk("Data-Out phase, ");
+ break;
+ case P_DATAIN:
+ printk("Data-In phase, ");
+ break;
+ case P_COMMAND:
+ printk("Command phase, ");
+ break;
+ case P_MESGOUT:
+ printk("Message-Out phase, ");
+ break;
+ case P_STATUS:
+ printk("Status phase, ");
+ break;
+ case P_MESGIN:
+ printk("Message-In phase, ");
+ break;
+ default:
+ /*
+ * We're not in a valid phase, so assume we're idle.
+ */
+ printk("while idle, LASTPHASE = 0x%x, ", bus_state);
+ break;
+ }
+ printk("SCSISIGI 0x%x, SEQADDR 0x%x, SSTAT0 0x%x, SSTAT1 0x%x\n",
+ inb(p->base + SCSISIGI),
+ inb(p->base + SEQADDR0) | (inb(p->base + SEQADDR1) << 8),
+ inb(p->base + SSTAT0), inb(p->base + SSTAT1));
+
+ channel = hscb->target_channel_lun & SELBUSB ? 'B': 'A';
+ /*
+ * Determine our course of action.
+ */
+ if (scb->flags & SCB_ABORT)
+ {
+ /*
+ * Been down this road before; do a full bus reset.
+ */
+ scb->flags |= SCB_RECOVERY_SCB;
+ unpause_sequencer(p, /* unpause_always */ TRUE);
+ result = -1;
+ }
+#if 0
+ else if (hscb->control & TAG_ENB)
{
/*
- * Ensure that the card doesn't do anything
- * behind our back.
+ * We could be starving this command; try sending and ordered tag
+ * command to the target we come from.
*/
- PAUSE_SEQUENCER(p);
+ scb->flags |= SCB_SENTORDEREDTAG | SCB_RECOVERY_SCB;
+ p->orderedtag = p->orderedtag | 0xFF;
+ result = SCSI_RESET_PENDING;
+ unpause_sequencer(p, /* unpause_always */ TRUE);
+ printk(KERN_WARNING "scsi%d: Abort_reset, odered tag queued.\n",
+ p->host_no);
+ }
+#endif
+ else
+ {
+ unsigned char active_scb_index, saved_scbptr;
+ struct aic7xxx_scb *active_scb;
- printk(KERN_WARNING "aic7xxx: (abort_reset) scb state 0x%x, ", scb->state);
- bus_state = inb(LASTPHASE + p->base);
+ /*
+ * Send an Abort Message:
+ * The target that is holding up the bus may not be the same as
+ * the one that triggered this timeout (different commands have
+ * different timeout lengths). Our strategy here is to queue an
+ * abort message to the timed out target if it is disconnected.
+ * Otherwise, if we have an active target we stuff the message buffer
+ * with an abort message and assert ATN in the hopes that the target
+ * will let go of the bus and go to the mesgout phase. If this
+ * fails, we'll get another timeout a few seconds later which will
+ * attempt a bus reset.
+ */
+ saved_scbptr = inb(p->base + SCBPTR);
+ active_scb_index = inb(p->base + SCB_TAG);
+ active_scb = p->scb_data->scb_array[active_scb_index];
- switch (bus_state)
+ if (bus_state != P_BUSFREE)
+ {
+ if (active_scb_index >= p->scb_data->numscbs)
{
- case P_DATAOUT:
- printk("Data-Out phase, ");
- break;
- case P_DATAIN:
- printk("Data-In phase, ");
- break;
- case P_COMMAND:
- printk("Command phase, ");
- break;
- case P_MESGOUT:
- printk("Message-Out phase, ");
- break;
- case P_STATUS:
- printk("Status phase, ");
- break;
- case P_MESGIN:
- printk("Message-In phase, ");
- break;
- default:
- printk("while idle, LASTPHASE = 0x%x, ", bus_state);
- /*
- * We're not in a valid phase, so assume we're idle.
- */
- bus_state = 0;
- break;
+ /*
+ * Perform a bus reset.
+ *
+ * XXX - We want to queue an abort for the timedout SCB
+ * instead.
+ */
+ result = -1;
+ printk(KERN_WARNING "scsi%d: Invalid SCB ID %d is active, "
+ "SCB flags = 0x%x.\n", p->host_no, scb->hscb->tag, scb->flags);
}
- printk("SCSISIGI = 0x%x\n", inb(p->base + SCSISIGI));
-
- /*
- * First, determine if we want to do a bus reset or simply a bus device
- * reset. If this is the first time that a transaction has timed out
- * and the SCB is not paged out, just schedule a bus device reset.
- * Otherwise, we reset the bus and abort all pending I/Os on that bus.
- */
- if (!(scb->state & (SCB_ABORTED | SCB_PAGED_OUT)))
+ else
{
-#if 0
- if (scb->control & TAG_ENB)
- {
+ /* Send the abort message to the active SCB. */
+ outb(1, p->base + MSG_LEN);
+ if (active_scb->hscb->control & TAG_ENB)
+ {
+ outb(MSG_ABORT_TAG, p->base + MSG_OUT);
+ }
+ else
+ {
+ outb(MSG_ABORT, p->base + MSG_OUT);
+ }
+ outb(bus_state | ATNO, p->base + SCSISIGO);
+ printk(KERN_WARNING "scsi%d: abort message in message buffer\n",
+ p->host_no);
+ active_scb->flags |= SCB_ABORT | SCB_RECOVERY_SCB;
+ if (active_scb != scb)
+ {
/*
- * We could be starving this command; try sending and ordered tag
- * command to the target we come from.
+ * XXX - We would like to increment the timeout on scb, but
+ * access to that routine is denied because it is hidden
+ * in scsi.c. If we were able to do this, it would give
+ * scb a new lease on life.
*/
- scb->state = scb->state | SCB_ABORTED | SCB_SENTORDEREDTAG;
- p->orderedtag = p->orderedtag | 0xFF;
result = SCSI_RESET_PENDING;
- UNPAUSE_SEQUENCER(p);
- printk(KERN_WARNING "aic7xxx: (abort_reset) Ordered tag queued.\n");
- }
-#endif
- unsigned char active_scb, control;
- struct aic7xxx_scb *active_scbp;
+ aic7xxx_error(active_scb->cmd) = DID_RESET;
+ }
+ else
+ {
+ aic7xxx_error(scb->cmd) = DID_RESET;
+ result = SCSI_RESET_PENDING;
+ }
+ unpause_sequencer(p, /* unpause_always */ TRUE);
+ }
+ }
+ else
+ {
+ unsigned char hscb_index, linked_next;
+ int disconnected;
- /*
- * Send a Bus Device Reset Message:
- * The target we select to send the message to may be entirely
- * different than the target pointed to by the scb that timed
- * out. If the command is in the QINFIFO or the waiting for
- * selection list, its not tying up the bus and isn't responsible
- * for the delay so we pick off the active command which should
- * be the SCB selected by SCBPTR. If its disconnected or active,
- * we device reset the target scbp points to. Although it may
- * be that this target is not responsible for the delay, it may
- * may also be that we're timing out on a command that just takes
- * too much time, so we try the bus device reset there first.
- */
- active_scb = inb(SCBPTR + base);
- active_scbp = (p->scb_array[inb(SCB_TAG + base)]);
- control = inb(SCB_CONTROL + base);
+ disconnected = FALSE;
+ hscb_index = aic7xxx_find_scb(p, scb);
+ if (hscb_index == SCB_LIST_NULL)
+ {
+ disconnected = TRUE;
+ linked_next = (scb->hscb->data_count >> 24) & 0xFF;
+ }
+ else
+ {
+ outb(hscb_index, p->base + SCBPTR);
+ if (inb(p->base + SCB_CONTROL) & DISCONNECTED)
+ {
+ disconnected = TRUE;
+ }
+ linked_next = inb(p->base + SCB_LINKED_NEXT);
+ }
+ if (disconnected)
+ {
+ /*
+ * Simply set the ABORT_SCB control bit and preserve the
+ * linked next pointer.
+ */
+ scb->hscb->control |= ABORT_SCB | MK_MESSAGE;
+ scb->hscb->data_count &= ~0xFF000000;
+ scb->hscb->data_count |= linked_next << 24;
+ if ((p->flags & PAGE_ENABLED) == 0)
+ {
+ scb->hscb->control &= ~DISCONNECTED;
+ }
+ scb->flags |= SCB_QUEUED_ABORT | SCB_ABORT | SCB_RECOVERY_SCB;
+ if (hscb_index != SCB_LIST_NULL)
+ {
+ unsigned char scb_control;
- /*
- * Test to see if scbp is disconnected
- */
- outb(scb->position, SCBPTR + base);
- if (inb(SCB_CONTROL + base) & DISCONNECTED)
- {
-#ifdef AIC7XXX_DEBUG_ABORT
- printk("aic7xxx: (abort_scb) scb %d is disconnected; "
- "bus device reset message queued.\n", scb->position);
-#endif
- if (p->flags & PAGE_ENABLED)
- {
- /* Pull this SCB out of the disconnected list. */
- u_char prev = inb(SCB_PREV + base);
- u_char next = inb(SCB_NEXT + base);
- if (prev == SCB_LIST_NULL)
- {
- /* Head of list */
- outb(next, DISCONNECTED_SCBH + base);
- }
- else
- {
- outb(prev, SCBPTR + base);
- outb(next, SCB_NEXT + base);
- if (next != SCB_LIST_NULL)
- {
- outb(next, SCBPTR + base);
- outb(prev, SCB_PREV + base);
- }
- outb(scb->position, SCBPTR + base);
- }
- }
- scb->state |= (SCB_DEVICE_RESET | SCB_ABORTED);
- scb->control = scb->control & DISCENB;
- scb->SCSI_cmd_length = 0;
- scb->SG_segment_count = 0;
- memset(scb->SG_list_pointer, 0, sizeof(scb->SG_list_pointer));
- memset(scb->data_pointer, 0, sizeof(scb->data_pointer));
- scb->data_count = 0;
- aic7xxx_putscb(p, scb);
- aic7xxx_add_waiting_scb(base, scb);
- outb(active_scb, SCBPTR + base);
- result = SCSI_RESET_PENDING;
- UNPAUSE_SEQUENCER(p);
- }
- else
- {
- /*
- * Is the active SCB really active?
- */
- if ((active_scbp->state & SCB_ACTIVE) && bus_state)
- {
- /*
- * Load the message buffer and assert attention.
- */
- active_scbp->state |= (SCB_DEVICE_RESET | SCB_ABORTED);
- outb(1, MSG_LEN + base);
- outb(MSG_BUS_DEVICE_RESET, MSG0 + base);
- outb(bus_state | ATNO, SCSISIGO + base);
-#ifdef AIC7XXX_DEBUG_ABORT
- printk("aic7xxx: (abort_scb) asserted ATN - "
- "bus device reset in message buffer.\n");
-#endif
- if (active_scbp != scb)
- {
- /*
- * XXX - We would like to increment the timeout on scb, but
- * access to that routine is denied because it is hidden
- * in scsi.c. If we were able to do this, it would give
- * scb a new lease on life.
- */
- ;
- }
- aic7xxx_error(scb->cmd) = DID_RESET;
- /*
- * Restore the active SCB and unpause the sequencer.
- */
- outb(active_scb, SCBPTR + base);
- if (active_scbp != scb)
- {
- /*
- * The mid-level SCSI code requested us to reset a command
- * different from the one that we actually reset. Return
- * a "not running" indication and hope that the SCSI code
- * will Do the Right Thing (tm).
- */
- result = SCSI_RESET_NOT_RUNNING;
- }
- else
- {
- result = SCSI_RESET_PENDING;
- }
- UNPAUSE_SEQUENCER(p);
- }
- }
+ scb_control = inb(p->base + SCB_CONTROL);
+ outb(scb_control | MK_MESSAGE| ABORT_SCB, p->base + SCB_CONTROL);
+ }
+ /*
+ * Actually requeue this SCB in case we can select the
+ * device before it reconnects. If the transaction we
+ * want to abort is not tagged, unbusy it first so that
+ * we don't get held back from sending the command.
+ */
+ if ((scb->hscb->control & TAG_ENB) == 0)
+ {
+ unsigned char target;
+ int lun;
+
+ target = scb->cmd->target;
+ lun = scb->cmd->lun;
+ aic7xxx_search_qinfifo(p, target, channel, lun, SCB_LIST_NULL,
+ 0, /* requeue */ TRUE);
+ }
+ printk(KERN_WARNING "(scsi%d:%d:%d) Queueing an Abort SCB.\n",
+ p->host_no, TC_OF_SCB(scb));
+ scbq_insert_head(&p->waiting_scbs, scb);
+ scb->flags |= SCB_WAITINGQ;
+ outb(saved_scbptr, p->base + SCBPTR);
+ if ((p->flags & IN_ISR) == 0)
+ {
+ /*
+ * Processing the waiting queue may unpause us.
+ */
+ aic7xxx_run_waiting_queues(p);
+ /*
+ * If we are using AAP, aic7xxx_run_waiting_queues() will not
+ * unpause us, so ensure we are unpaused.
+ */
+ unpause_sequencer(p, /*unpause_always*/ FALSE);
+ }
+ else
+ {
+ unpause_sequencer(p, /*unpause_always*/ TRUE);
+ }
+ result = SCSI_RESET_PENDING;
+ }
+ else
+ {
+ scb->flags |= SCB_RECOVERY_SCB;
+ unpause_sequencer(p, /* unpause_always */ TRUE);
+ result = -1;
}
}
}
- /* Make sure the sequencer is unpaused upon return. */
- if (result == -1)
- {
- UNPAUSE_SEQUENCER(p);
- }
return (result);
}
@@ -5491,16 +6764,48 @@ aic7xxx_abort(Scsi_Cmnd *cmd)
struct aic7xxx_scb *scb = NULL;
struct aic7xxx_host *p;
int base, result;
+ unsigned long processor_flags;
p = (struct aic7xxx_host *) cmd->host->hostdata;
- scb = (p->scb_array[aic7xxx_position(cmd)]);
+ scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
base = p->base;
+ save_flags(processor_flags);
+ cli();
+
#ifdef AIC7XXX_DEBUG_ABORT
- printk("aic7xxx: (abort) Aborting scb %d, TCL %d/%d/%d\n",
- scb->position, TCL_OF_SCB(scb));
+ if (scb != NULL)
+ {
+ printk("(scsi%d:%d:%d) Aborting scb %d, flags 0x%x\n",
+ p->host_no, TC_OF_SCB(scb), scb->hscb->tag, scb->flags);
+ }
+ else
+ {
+ printk("aic7xxx: Abort called with no SCB for cmd.\n");
+ }
#endif
+ if (p->flags & IN_TIMEOUT)
+ {
+ /*
+ * We've already started a recovery operation.
+ */
+ if ((scb->flags & SCB_RECOVERY_SCB) == 0)
+ {
+ restore_flags(processor_flags);
+ return (SCSI_ABORT_PENDING);
+ }
+ else
+ {
+ /*
+ * This is the second time we've tried to abort the recovery
+ * SCB. We want the mid-level SCSI code to call the reset
+ * function to reset the SCSI bus.
+ */
+ restore_flags(processor_flags);
+ return (SCSI_ABORT_NOT_RUNNING);
+ }
+ }
if (cmd->serial_number != cmd->serial_number_at_timeout)
{
result = SCSI_ABORT_NOT_RUNNING;
@@ -5509,14 +6814,34 @@ aic7xxx_abort(Scsi_Cmnd *cmd)
{
result = SCSI_ABORT_NOT_RUNNING;
}
- else if ((scb->cmd != cmd) || (!(scb->state & SCB_IN_PROGRESS)))
+ else if ((scb->cmd != cmd) || (!(scb->flags & SCB_ACTIVE)))
{
result = SCSI_ABORT_NOT_RUNNING;
}
else
{
- result = SCSI_ABORT_SNOOZE;
+ /*
+ * XXX - Check use of IN_TIMEOUT to see if we're Doing the
+ * Right Thing with it.
+ */
+ p->flags |= IN_TIMEOUT;
+ result = aic7xxx_bus_device_reset(p, scb->cmd);
+ switch (result)
+ {
+ case SCSI_RESET_NOT_RUNNING:
+ p->flags &= ~IN_TIMEOUT;
+ result = SCSI_ABORT_NOT_RUNNING;
+ break;
+ case SCSI_RESET_PENDING:
+ result = SCSI_ABORT_PENDING;
+ break;
+ default:
+ p->flags &= ~IN_TIMEOUT;
+ result = SCSI_ABORT_SNOOZE;
+ break;
+ }
}
+ restore_flags(processor_flags);
return (result);
}
@@ -5536,18 +6861,27 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags)
{
struct aic7xxx_scb *scb = NULL;
struct aic7xxx_host *p;
- int base, found, tindex, min_target, max_target, result = -1;
+ int base, found, tindex, min_target, max_target;
+ int result = -1;
char channel = 'A';
unsigned long processor_flags;
p = (struct aic7xxx_host *) cmd->host->hostdata;
- scb = (p->scb_array[aic7xxx_position(cmd)]);
+ scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
base = p->base;
channel = cmd->channel ? 'B': 'A';
tindex = (cmd->channel << 4) | cmd->target;
-#ifdef AIC7XXX_DEBUG_ABORT
- printk("aic7xxx: (reset) target/channel %d/%d\n", cmd->target, cmd->channel);
+#ifdef 0 /* AIC7XXX_DEBUG_ABORT */
+ if (scb != NULL)
+ {
+ printk("(scsi%d:%d:%d) Reset called, scb %d, flags 0x%x\n",
+ p->host_no, TC_OF_SCB(scb), scb->hscb->tag, scb->flags);
+ }
+ else
+ {
+ printk("aic7xxx: Reset called with no SCB for cmd.\n");
+ }
#endif
/*
@@ -5562,34 +6896,45 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags)
if (scb->cmd != cmd)
scb = NULL;
- if (!(flags & (SCSI_RESET_SUGGEST_HOST_RESET | SCSI_RESET_SUGGEST_BUS_RESET))
- && (scb != NULL))
+ if (p->flags & IN_TIMEOUT)
{
/*
- * Attempt a bus device reset if commands have completed successfully
- * since the last bus device reset, or it has been less than 100ms
- * since the last reset.
+ * We've already started a recovery operation.
*/
- if ((p->flags & DEVICE_SUCCESS) ||
- ((jiffies - p->device_status[tindex].last_reset) < HZ/10))
+ if ((scb->flags & SCB_RECOVERY_SCB) == 0)
{
- if (cmd->serial_number != cmd->serial_number_at_timeout)
- {
- result = SCSI_RESET_NOT_RUNNING;
- }
- else
+ restore_flags(processor_flags);
+ return (SCSI_RESET_PENDING);
+ }
+ }
+ else
+ {
+ if (!(flags & (SCSI_RESET_SUGGEST_HOST_RESET | SCSI_RESET_SUGGEST_BUS_RESET))
+ && (scb != NULL))
+ {
+ /*
+ * Attempt a bus device reset if commands have completed successfully
+ * since the last bus device reset, or it has been less than 100ms
+ * since the last reset.
+ */
+ if ((p->flags & DEVICE_SUCCESS) ||
+ ((jiffies - p->device_status[tindex].last_reset) < HZ/10))
{
- if (scb == NULL)
+ if (cmd->serial_number != cmd->serial_number_at_timeout)
+ {
+ result = SCSI_RESET_NOT_RUNNING;
+ }
+ else if (scb == NULL)
{
result = SCSI_RESET_NOT_RUNNING;
}
else if (flags & SCSI_RESET_ASYNCHRONOUS)
{
- if (scb->state & SCB_ABORTED)
+ if (scb->flags & SCB_ABORTED)
{
result = SCSI_RESET_PENDING;
}
- else if (!(scb->state & SCB_IN_PROGRESS))
+ else if (!(scb->flags & SCB_ACTIVE))
{
result = SCSI_RESET_NOT_RUNNING;
}
@@ -5600,20 +6945,23 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags)
if ((flags & SCSI_RESET_SYNCHRONOUS) &&
(p->device_status[tindex].flags & BUS_DEVICE_RESET_PENDING))
{
- scb->state |= SCB_ABORTED;
+ scb->flags |= SCB_ABORTED;
result = SCSI_RESET_PENDING;
}
else
{
+ p->flags |= IN_TIMEOUT;
result = aic7xxx_bus_device_reset(p, cmd);
if (result == 0)
+ {
+ p->flags &= ~IN_TIMEOUT;
result = SCSI_RESET_PENDING;
+ }
}
- }
+ }
}
}
}
-
if (result == -1)
{
/*
@@ -5626,11 +6974,11 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags)
{
result = SCSI_RESET_NOT_RUNNING;
}
- else if (!(scb->state & SCB_IN_PROGRESS))
+ else if (!(scb->flags & SCB_ACTIVE))
{
result = SCSI_RESET_NOT_RUNNING;
}
- else if ((scb->state & SCB_ABORTED) &&
+ else if ((scb->flags & SCB_ABORTED) &&
(!(p->device_status[tindex].flags & BUS_DEVICE_RESET_PENDING)))
{
result = SCSI_RESET_PENDING;
@@ -5642,8 +6990,9 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags)
/*
* The reset channel function assumes that the sequencer is paused.
*/
- PAUSE_SEQUENCER(p);
+ pause_sequencer(p);
found = aic7xxx_reset_channel(p, channel, TRUE);
+ p->flags = p->flags & ~IN_TIMEOUT;
/*
* If this is a synchronous reset and there is no SCB for this
@@ -5689,8 +7038,10 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags)
}
result = SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET;
+ p->flags &= ~IN_TIMEOUT;
}
}
+ aic7xxx_run_waiting_queues(p);
restore_flags(processor_flags);
return (result);
}
diff --git a/drivers/scsi/aic7xxx.h b/drivers/scsi/aic7xxx.h
index d4de8fd83..11836c405 100644
--- a/drivers/scsi/aic7xxx.h
+++ b/drivers/scsi/aic7xxx.h
@@ -18,12 +18,12 @@
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: aic7xxx.h,v 3.2 1996/07/23 03:37:26 deang Exp $
+ * $Id: aic7xxx.h,v 1.1.1.1 1997/06/01 03:17:41 ralf Exp $
*-M*************************************************************************/
#ifndef _aic7xxx_h
#define _aic7xxx_h
-#define AIC7XXX_H_VERSION "$Revision: 3.2 $"
+#define AIC7XXX_H_VERSION "$Revision: 1.1.1.1 $"
/*
* Scsi_Host_Template (see hosts.h) for AIC-7xxx - some fields
@@ -40,13 +40,13 @@
aic7xxx_info, \
NULL, \
aic7xxx_queue, \
- aic7xxx_abort, \
+ NULL, \
aic7xxx_reset, \
NULL, \
aic7xxx_biosparam, \
-1, /* max simultaneous cmds */\
-1, /* scsi id of host adapter */\
- SG_ALL, /* max scatter-gather cmds */\
+ 0, /* max scatter-gather cmds */\
2, /* cmds per lun (linked cmds) */\
0, /* number of 7xxx's present */\
0, /* no memory DMA restrictions */\
@@ -57,7 +57,6 @@ extern int aic7xxx_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *));
extern int aic7xxx_biosparam(Disk *, kdev_t, int[]);
extern int aic7xxx_detect(Scsi_Host_Template *);
extern int aic7xxx_command(Scsi_Cmnd *);
-extern int aic7xxx_abort(Scsi_Cmnd *);
extern int aic7xxx_reset(Scsi_Cmnd *, unsigned int);
extern const char *aic7xxx_info(struct Scsi_Host *);
diff --git a/drivers/scsi/aic7xxx.seq b/drivers/scsi/aic7xxx.seq
index 98d4b9545..e69de29bb 100644
--- a/drivers/scsi/aic7xxx.seq
+++ b/drivers/scsi/aic7xxx.seq
@@ -1,1127 +0,0 @@
-/*+M*************************************************************************
- * Adaptec AIC7xxx device driver for Linux and FreeBSD.
- *
- * Copyright (c) 1994 John Aycock
- * The University of Calgary Department of Computer Science.
- *
- *Modifications/enhancements:
- * Copyright (c) 1994, 1995, 1996 Justin Gibbs. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * FreeBSD, Twin, Wide, 2 command per target support, tagged queuing and other
- * optimizations provided by Justin T. Gibbs (gibbs@FreeBSD.org)
- *
- * This version corresponds to version 1.42 of FreeBSDs aic7xxx.seq.
- *
- *-M*************************************************************************/
-
-VERSION AIC7XXX_SEQ_VER "$Id: aic7xxx.seq,v 4.0 1996/10/13 08:23:42 deang Exp $"
-
-#ifdef linux
-#include "aic7xxx_reg.h"
-#else
-#if defined(__NetBSD__)
-#include "../../../../dev/ic/aic7xxxreg.h"
-#elif defined(__FreeBSD__)
-#include "../../dev/aic7xxx/aic7xxx_reg.h"
-#endif
-#endif
-
-/*
- * We can't just use ACCUM in the sequencer code because it
- * must be treated specially by the assembler, and it currently
- * looks for the symbol 'A'. This is the only register defined in
- * the assembler's symbol space.
- */
-A = ACCUM
-
-/* After starting the selection hardware, we check for reconnecting targets
- * as well as for our selection to complete just in case the reselection wins
- * bus arbitration. The problem with this is that we must keep track of the
- * SCB that we've already pulled from the QINFIFO and started the selection
- * on just in case the reselection wins so that we can retry the selection at
- * a later time. This problem cannot be resolved by holding a single entry
- * in scratch ram since a reconnecting target can request sense and this will
- * create yet another SCB waiting for selection. The solution used here is to
- * use byte 27 of the SCB as a pseudo-next pointer and to thread a list
- * of SCBs that are awaiting selection. Since 0-0xfe are valid SCB offsets,
- * SCB_LIST_NULL is 0xff which is out of range. The kernel driver must
- * add an entry to this list every time a request sense occurs. The sequencer
- * will automatically consume the entries.
- */
-
-/*
- * We assume that the kernel driver may reset us at any time, even in the
- * middle of a DMA, so clear DFCNTRL too.
- */
-reset:
- clr DFCNTRL
- clr SCSISIGO /* De-assert BSY */
-/*
- * We jump to start after every bus free.
- */
-start:
- and FLAGS,0x0f /* clear target specific flags */
- mvi SCSISEQ,ENRSELI /* Always allow reselection */
- clr SCSIRATE /*
- * We don't know the target we will
- * connect to, so default to narrow
- * transfers to avoid parity problems.
- */
-poll_for_work:
- /*
- * Are we a twin channel device?
- * For fairness, we check the other bus first,
- * since we just finished a transaction on the
- * current channel.
- */
- test FLAGS,TWIN_BUS jz start2
- xor SBLKCTL,SELBUSB /* Toggle to the other bus */
- test SSTAT0,SELDI jnz reselect
- xor SBLKCTL,SELBUSB /* Toggle to the original bus */
-start2:
- test SSTAT0,SELDI jnz reselect
- cmp WAITING_SCBH,SCB_LIST_NULL jne start_waiting
- mov A, QCNTMASK
- test QINCNT,A jz poll_for_work
-
-/*
- * We have at least one queued SCB now and we don't have any
- * SCBs in the list of SCBs awaiting selection. Set the SCB
- * pointer from the FIFO so we see the right bank of SCB
- * registers.
- */
- mov SCBPTR,QINFIFO
-
-/*
- * See if there is not already an active SCB for this target. This code
- * locks out on a per target basis instead of target/lun. Although this
- * is not ideal for devices that have multiple luns active at the same
- * time, it is faster than looping through all SCB's looking for active
- * commands. It may be beneficial to make findscb a more general procedure
- * to see if the added cost of the search is negligible. This code also
- * assumes that the kernel driver will clear the active flags on board
- * initialization, board reset, and a target SELTO. Tagged commands
- * don't set the active bits since you can queue more than one command
- * at a time. We do, however, look to see if there are any non-tagged
- * I/Os in progress, and requeue the command if there are. Tagged and
- * non-tagged commands cannot be mixed to a single target.
- */
-
-test_busy:
- mov FUNCTION1,SCB_TCL
- mov A,FUNCTION1
- test SCB_TCL,0x88 jz test_a /* Id < 8 && A channel */
-
- test ACTIVE_B,A jnz requeue
- test SCB_CONTROL,TAG_ENB jnz start_scb
- /* Mark the current target as busy */
- or ACTIVE_B,A
- jmp start_scb
-
-/* Place the currently active SCB back on the queue for later processing */
-requeue:
- mov QINFIFO, SCBPTR
- jmp poll_for_work
-
-/*
- * Pull the first entry off of the waiting for selection list
- * We don't have to "test_busy" because only transactions that
- * have passed that test can be in the waiting_scb list.
- */
-start_waiting:
- mov SCBPTR,WAITING_SCBH
- jmp start_scb2
-
-test_a:
- test ACTIVE_A,A jnz requeue
- test SCB_CONTROL,TAG_ENB jnz start_scb
- /* Mark the current target as busy */
- or ACTIVE_A,A
-
-start_scb:
- mov SCB_NEXT,WAITING_SCBH
- mov WAITING_SCBH, SCBPTR
-start_scb2:
- and SINDEX,0xf7,SBLKCTL /* Clear the channel select bit */
- and A,0x08,SCB_TCL /* Get new channel bit */
- or SINDEX,A
- mov SBLKCTL,SINDEX /* select channel */
- mov SCB_TCL call initialize_scsiid
-
-/*
- * Enable selection phase as an initiator, and do automatic ATN
- * after the selection. We do this now so that we can overlap the
- * rest of our work to set up this target with the arbitration and
- * selection bus phases.
- */
-start_selection:
- mvi SCSISEQ,0x58 /* ENSELO|ENAUTOATNO|ENRSELI */
-
-/*
- * As soon as we get a successful selection, the target should go
- * into the message out phase since we have ATN asserted. Prepare
- * the message to send.
- *
- * Messages are stored in scratch RAM starting with a length byte
- * followed by the message itself.
- */
- test SCB_CMDLEN,0xff jnz mk_identify /* 0 Length Command? */
-
-/*
- * The kernel has sent us an SCB with no command attached. This implies
- * that the kernel wants to send a message of some sort to this target,
- * so we interrupt the driver, allow it to fill the message buffer, and
- * then go back into the arbitration loop
- */
- mvi INTSTAT,AWAITING_MSG
- jmp wait_for_selection
-
-mk_identify:
- and A,DISCENB,SCB_CONTROL /* mask off disconnect privledge */
-
- and MSG0,0x7,SCB_TCL /* lun */
- or MSG0,A /* or in disconnect privledge */
- or MSG0,MSG_IDENTIFY
- mvi MSG_LEN, 1
-
- test SCB_CONTROL,0xb0 jz !message /* WDTR, SDTR or TAG?? */
-/*
- * Send a tag message if TAG_ENB is set in the SCB control block.
- * Use SCB_TAG (the position in the kernel's SCB array) as the tag value.
- */
-
-mk_tag:
- mvi DINDEX, MSG1
- test SCB_CONTROL,TAG_ENB jz mk_tag_done
- and DINDIR,0x23,SCB_CONTROL
- mov DINDIR,SCB_TAG
-
- add MSG_LEN,COMP_MSG0,DINDEX /* update message length */
-
-mk_tag_done:
-
- test SCB_CONTROL,0x90 jz !message /* NEEDWDTR|NEEDSDTR */
- mov DINDEX call mk_dtr /* build DTR message if needed */
-
-!message:
-wait_for_selection:
- test SSTAT0,SELDO jnz select
- test SSTAT0,SELDI jz wait_for_selection
-
-/*
- * Reselection has been initiated by a target. Make a note that we've been
- * reselected, but haven't seen an IDENTIFY message from the target
- * yet.
- */
-reselect:
- clr MSG_LEN /* Don't have anything in the mesg buffer */
- mov SELID call initialize_scsiid
- or FLAGS,RESELECTED
- jmp select2
-
-/*
- * After the selection, remove this SCB from the "waiting for selection"
- * list. This is achieved by simply moving our "next" pointer into
- * WAITING_SCBH. Our next pointer will be set to null the next time this
- * SCB is used, so don't bother with it now.
- */
-select:
- mov WAITING_SCBH,SCB_NEXT
- or FLAGS,SELECTED
-select2:
-/*
- * Set CLRCHN here before the target has entered a data transfer mode -
- * with synchronous SCSI, if you do it later, you blow away some
- * data in the SCSI FIFO that the target has already sent to you.
- */
- or SXFRCTL0,CLRCHN
-/*
- * Initialize SCSIRATE with the appropriate value for this target.
- */
- call ndx_dtr
- mov SCSIRATE,SINDIR
-
-/*
- * Initialize Ultra mode setting.
- */
- mov FUNCTION1,SCSIID
- mov A,FUNCTION1
- and SINDEX,0xdf,SXFRCTL0 /* default to Ultra disabled */
- test SCSIID, 0x80 jnz ultra_b /* Target ID > 7 */
- test SBLKCTL, SELBUSB jnz ultra_b /* Second channel device */
- test ULTRA_ENB,A jz set_sxfrctl0
- or SINDEX, ULTRAEN jmp set_sxfrctl0
-ultra_b:
- test ULTRA_ENB_B,A jz set_sxfrctl0
- or SINDEX, ULTRAEN
-
-set_sxfrctl0:
- mov SXFRCTL0,SINDEX
-
- mvi SCSISEQ,ENAUTOATNP /*
- * ATN on parity errors
- * for "in" phases
- */
- mvi CLRSINT1,CLRBUSFREE
- mvi CLRSINT0,0x60 /* CLRSELDI|CLRSELDO */
-/*
- * Main loop for information transfer phases. If BSY is false, then
- * we have a bus free condition, expected or not. Otherwise, wait
- * for the target to assert REQ before checking MSG, C/D and I/O
- * for the bus phase.
- *
- */
-ITloop:
- test SSTAT1,BUSFREE jnz p_busfree
- test SSTAT1,REQINIT jz ITloop
-
- and A,PHASE_MASK,SCSISIGI
- mov LASTPHASE,A
- mov SCSISIGO,A
-
- cmp ALLZEROS,A je p_dataout
- cmp A,P_DATAIN je p_datain
- cmp A,P_COMMAND je p_command
- cmp A,P_MESGOUT je p_mesgout
- cmp A,P_STATUS je p_status
- cmp A,P_MESGIN je p_mesgin
-
- mvi INTSTAT,BAD_PHASE /* unknown phase - signal driver */
- jmp ITloop /* Try reading the bus again. */
-
-p_dataout:
- mvi DMAPARAMS,0x7d /*
- * WIDEODD|SCSIEN|SDMAEN|HDMAEN|
- * DIRECTION|FIFORESET
- */
- jmp data_phase_init
-
-/*
- * If we re-enter the data phase after going through another phase, the
- * STCNT may have been cleared, so restore it from the residual field.
- */
-data_phase_reinit:
- mov STCNT0,SCB_RESID_DCNT0
- mov STCNT1,SCB_RESID_DCNT1
- mov STCNT2,SCB_RESID_DCNT2
- jmp data_phase_loop
-
-p_datain:
- mvi DMAPARAMS,0x79 /*
- * WIDEODD|SCSIEN|SDMAEN|HDMAEN|
- * !DIRECTION|FIFORESET
- */
-data_phase_init:
- call assert
-
- test FLAGS, DPHASE jnz data_phase_reinit
- call sg_scb2ram
- or FLAGS, DPHASE /* We have seen a data phase */
-
-data_phase_loop:
-/* Guard against overruns */
- test SG_COUNT, 0xff jnz data_phase_inbounds
-/*
- * Turn on 'Bit Bucket' mode, set the transfer count to
- * 16meg and let the target run until it changes phase.
- * When the transfer completes, notify the host that we
- * had an overrun.
- */
- or SXFRCTL1,BITBUCKET
- mvi STCNT0,0xff
- mvi STCNT1,0xff
- mvi STCNT2,0xff
-
-data_phase_inbounds:
-/* If we are the last SG block, don't set wideodd. */
- cmp SG_COUNT,0x01 jne data_phase_wideodd
- and DMAPARAMS, 0xbf /* Turn off WIDEODD */
-data_phase_wideodd:
- mov DMAPARAMS call dma
-
-/* Go tell the host about any overruns */
- test SXFRCTL1,BITBUCKET jnz data_phase_overrun
-
-/* Exit if we had an underrun */
- test SSTAT0,SDONE jz data_phase_finish /* underrun STCNT != 0 */
-
-/*
- * Advance the scatter-gather pointers if needed
- */
-sg_advance:
- dec SG_COUNT /* one less segment to go */
-
- test SG_COUNT, 0xff jz data_phase_finish /* Are we done? */
-
- clr A /* add sizeof(struct scatter) */
- add SG_NEXT0,SG_SIZEOF,SG_NEXT0
- adc SG_NEXT1,A,SG_NEXT1
-
-/*
- * Load a struct scatter and set up the data address and length.
- * If the working value of the SG count is nonzero, then
- * we need to load a new set of values.
- *
- * This, like all DMA's, assumes a little-endian host data storage.
- */
-sg_load:
- clr HCNT2
- clr HCNT1
- mvi HCNT0,SG_SIZEOF
-
- mov HADDR0,SG_NEXT0
- mov HADDR1,SG_NEXT1
- mov HADDR2,SG_NEXT2
- mov HADDR3,SG_NEXT3
-
- or DFCNTRL,0xd /* HDMAEN|DIRECTION|FIFORESET */
-
-/*
- * Wait for DMA from host memory to data FIFO to complete, then disable
- * DMA and wait for it to acknowledge that it's off.
- */
-dma_finish:
- test DFSTATUS,HDONE jz dma_finish
- /* Turn off DMA preserving WIDEODD */
- and DFCNTRL,WIDEODD
-dma_finish2:
- test DFCNTRL,HDMAENACK jnz dma_finish2
-
-/*
- * Copy data from FIFO into SCB data pointer and data count. In
- * both FreeBSD and Linux, the scatter list entry is 8 bytes.
- *
- * struct ahc_dma_seg {
- * physaddr addr; four bytes, little-endian order
- * long len; four bytes, little endian order
- * };
- */
-
- mov HADDR0,DFDAT
- mov HADDR1,DFDAT
- mov HADDR2,DFDAT
- mov HADDR3,DFDAT
- mov HCNT0,DFDAT
- mov HCNT1,DFDAT
- mov HCNT2,DFDAT
-
-/* Load STCNT as well. It is a mirror of HCNT */
- mov STCNT0,HCNT0
- mov STCNT1,HCNT1
- mov STCNT2,HCNT2
- test SSTAT1,PHASEMIS jz data_phase_loop
-
-data_phase_finish:
-/*
- * After a DMA finishes, save the SG and STCNT residuals back into the SCB
- * We use STCNT instead of HCNT, since it's a reflection of how many bytes
- * were transferred on the SCSI (as opposed to the host) bus.
- */
- mov SCB_RESID_DCNT0,STCNT0
- mov SCB_RESID_DCNT1,STCNT1
- mov SCB_RESID_DCNT2,STCNT2
- mov SCB_RESID_SGCNT, SG_COUNT
- jmp ITloop
-
-data_phase_overrun:
-/*
- * Turn off BITBUCKET mode and notify the host
- */
- and SXFRCTL1,0x7f /* ~BITBUCKET */
- mvi INTSTAT,DATA_OVERRUN
- jmp ITloop
-
-/*
- * Command phase. Set up the DMA registers and let 'er rip.
- */
-p_command:
- call assert
-
-/*
- * Load HADDR and HCNT.
- */
- mov HADDR0, SCB_CMDPTR0
- mov HADDR1, SCB_CMDPTR1
- mov HADDR2, SCB_CMDPTR2
- mov HADDR3, SCB_CMDPTR3
- mov HCNT0, SCB_CMDLEN
- clr HCNT1
- clr HCNT2
-
- mov STCNT0, HCNT0
- mov STCNT1, HCNT1
- mov STCNT2, HCNT2
-
- mvi 0x3d call dma # SCSIEN|SDMAEN|HDMAEN|
- # DIRECTION|FIFORESET
- jmp ITloop
-
-/*
- * Status phase. Wait for the data byte to appear, then read it
- * and store it into the SCB.
- */
-p_status:
- mvi SCB_TARGET_STATUS call inb_first
- jmp mesgin_done
-
-/*
- * Message out phase. If there is not an active message, but the target
- * took us into this phase anyway, build a no-op message and send it.
- */
-p_mesgout:
- test MSG_LEN, 0xff jnz p_mesgout_start
- mvi MSG_NOP call mk_mesg /* build NOP message */
-
-p_mesgout_start:
-/*
- * Set up automatic PIO transfer from MSG0. Bit 3 in
- * SXFRCTL0 (SPIOEN) is already on.
- */
- mvi SINDEX,MSG0
- mov DINDEX,MSG_LEN
-
-/*
- * When target asks for a byte, drop ATN if it's the last one in
- * the message. Otherwise, keep going until the message is exhausted.
- *
- * Keep an eye out for a phase change, in case the target issues
- * a MESSAGE REJECT.
- */
-p_mesgout_loop:
- test SSTAT1,PHASEMIS jnz p_mesgout_phasemis
- test SSTAT0,SPIORDY jz p_mesgout_loop
- test SSTAT1,PHASEMIS jnz p_mesgout_phasemis
- cmp DINDEX,1 jne p_mesgout_outb /* last byte? */
- mvi CLRSINT1,CLRATNO /* drop ATN */
-p_mesgout_outb:
- dec DINDEX
- or CLRSINT0, CLRSPIORDY
- mov SCSIDATL,SINDIR
-
-p_mesgout4:
- test DINDEX,0xff jnz p_mesgout_loop
-
-/*
- * If the next bus phase after ATN drops is a message out, it means
- * that the target is requesting that the last message(s) be resent.
- */
-p_mesgout_snoop:
- test SSTAT1,BUSFREE jnz p_mesgout_done
- test SSTAT1,REQINIT jz p_mesgout_snoop
-
- test SSTAT1,PHASEMIS jnz p_mesgout_done
-
- or SCSISIGO,ATNO /* turn on ATNO */
-
- jmp ITloop
-
-p_mesgout_phasemis:
- mvi CLRSINT1,CLRATNO /* Be sure to turn ATNO off */
-p_mesgout_done:
- clr MSG_LEN /* no active msg */
- jmp ITloop
-
-/*
- * Message in phase. Bytes are read using Automatic PIO mode.
- */
-p_mesgin:
- mvi A call inb_first /* read the 1st message byte */
- mov REJBYTE,A /* save it for the driver */
-
- test A,MSG_IDENTIFY jnz mesgin_identify
- cmp A,MSG_DISCONNECT je mesgin_disconnect
- cmp A,MSG_SDPTRS je mesgin_sdptrs
- cmp ALLZEROS,A je mesgin_complete
- cmp A,MSG_RDPTRS je mesgin_rdptrs
- cmp A,MSG_EXTENDED je mesgin_extended
- cmp A,MSG_REJECT je mesgin_reject
-
-rej_mesgin:
-/*
- * We have no idea what this message in is, and there's no way
- * to pass it up to the kernel, so we issue a message reject and
- * hope for the best. Since we're now using manual PIO mode to
- * read in the message, there should no longer be a race condition
- * present when we assert ATN. In any case, rejection should be a
- * rare occurrence - signal the driver when it happens.
- */
- or SCSISIGO,ATNO /* turn on ATNO */
- mvi INTSTAT,SEND_REJECT /* let driver know */
-
- mvi MSG_REJECT call mk_mesg
-
-mesgin_done:
- call inb_last /*ack & turn auto PIO back on*/
- jmp ITloop
-
-
-mesgin_complete:
-/*
- * We got a "command complete" message, so put the SCB_TAG into QUEUEOUT,
- * and trigger a completion interrupt. Check status for non zero return
- * and interrupt driver if needed. This allows the driver to interpret
- * errors only when they occur instead of always uploading the scb. If
- * the status is SCSI_CHECK, the driver will download a new scb requesting
- * sense to replace the old one, modify the "waiting for selection" SCB list
- * and set RETURN_1 to SEND_SENSE. If RETURN_1 is set to SEND_SENSE the
- * sequencer imediately jumps to main loop where it will run down the waiting
- * SCB list and process the sense request. If the kernel driver does not
- * wish to request sense, it need only clear RETURN_1, and the command is
- * allowed to complete. We don't bother to post to the QOUTFIFO in the
- * error case since it would require extra work in the kernel driver to
- * ensure that the entry was removed before the command complete code tried
- * processing it.
- *
- * First check for residuals
- */
- test SCB_RESID_SGCNT,0xff jz check_status
-/*
- * If we have a residual count, interrupt and tell the host. Other
- * alternatives are to pause the sequencer on all command completes (yuck),
- * dma the resid directly to the host (slick, we may have space to do it now)
- * or have the sequencer pause itself when it encounters a non-zero resid
- * (unnecessary pause just to flag the command -yuck-, but takes one instruction
- * and since it shouldn't happen that often is good enough for our purposes).
- */
-resid:
- mvi INTSTAT,RESIDUAL
-
-check_status:
- test SCB_TARGET_STATUS,0xff jz status_ok /* Good Status? */
- mvi INTSTAT,BAD_STATUS /* let driver know */
- cmp RETURN_1, SEND_SENSE jne status_ok
- jmp mesgin_done
-
-status_ok:
-/* First, mark this target as free. */
- test SCB_CONTROL,TAG_ENB jnz test_immediate /*
- * Tagged commands
- * don't busy the
- * target.
- */
- mov FUNCTION1,SCB_TCL
- mov A,FUNCTION1
- test SCB_TCL,0x88 jz clear_a
- xor ACTIVE_B,A
- jmp test_immediate
-
-clear_a:
- xor ACTIVE_A,A
-
-test_immediate:
- test SCB_CMDLEN,0xff jnz complete /* Immediate message complete */
-/*
- * Pause the sequencer until the driver gets around to handling the command
- * complete. This is so that any action that might require careful timing
- * with the completion of this command can occur.
- */
- mvi INTSTAT,IMMEDDONE
- jmp start
-complete:
- mov QOUTFIFO,SCB_TAG
- mvi INTSTAT,CMDCMPLT
- jmp mesgin_done
-
-
-/*
- * Is it an extended message? We only support the synchronous and wide data
- * transfer request messages, which will probably be in response to
- * WDTR or SDTR message outs from us. If it's not SDTR or WDTR, reject it -
- * apparently this can be done after any message in byte, according
- * to the SCSI-2 spec.
- */
-mesgin_extended:
- mvi ARG_1 call inb_next /* extended message length */
- mvi REJBYTE_EXT call inb_next /* extended message code */
-
- cmp REJBYTE_EXT,MSG_SDTR je p_mesginSDTR
- cmp REJBYTE_EXT,MSG_WDTR je p_mesginWDTR
- jmp rej_mesgin
-
-p_mesginWDTR:
- cmp ARG_1,2 jne rej_mesgin /* extended mesg length=2 */
- mvi ARG_1 call inb_next /* Width of bus */
- mvi INTSTAT,WDTR_MSG /* let driver know */
- test RETURN_1,0xff jz mesgin_done /* Do we need to send WDTR? */
- cmp RETURN_1,SEND_REJ je rej_mesgin /*
- * Bus width was too large
- * Reject it.
- */
-
-/* We didn't initiate the wide negotiation, so we must respond to the request */
- and RETURN_1,0x7f /* Clear the SEND_WDTR Flag */
- mvi DINDEX,MSG0
- mvi MSG0 call mk_wdtr /* build WDTR message */
- or SCSISIGO,ATNO /* turn on ATNO */
- jmp mesgin_done
-
-p_mesginSDTR:
- cmp ARG_1,3 jne rej_mesgin /* extended mesg length=3 */
- mvi ARG_1 call inb_next /* xfer period */
- mvi A call inb_next /* REQ/ACK offset */
- mvi INTSTAT,SDTR_MSG /* call driver to convert */
-
- test RETURN_1,0xff jz mesgin_done /* Do we need to mk_sdtr/rej */
- cmp RETURN_1,SEND_REJ je rej_mesgin /*
- * Requested SDTR too small
- * Reject it.
- */
- clr ARG_1 /* Use the scratch ram rate */
- mvi DINDEX, MSG0
- mvi MSG0 call mk_sdtr
- or SCSISIGO,ATNO /* turn on ATNO */
- jmp mesgin_done
-
-/*
- * Is it a disconnect message? Set a flag in the SCB to remind us
- * and await the bus going free.
- */
-mesgin_disconnect:
- or SCB_CONTROL,DISCONNECTED
- test FLAGS, PAGESCBS jz mesgin_done
-/*
- * Link this SCB into the DISCONNECTED list. This list holds the
- * candidates for paging out an SCB if one is needed for a new command.
- * Modifying the disconnected list is a critical(pause dissabled) section.
- */
- mvi SCB_PREV, SCB_LIST_NULL
- mvi SEQCTL,0x50 /* PAUSEDIS|FASTMODE */
- mov SCB_NEXT, DISCONNECTED_SCBH
- mov DISCONNECTED_SCBH, SCBPTR
- cmp SCB_NEXT,SCB_LIST_NULL je linkdone
- mov SCBPTR,SCB_NEXT
- mov SCB_PREV,DISCONNECTED_SCBH
- mov SCBPTR,DISCONNECTED_SCBH
-linkdone:
- mvi SEQCTL,0x10 /* !PAUSEDIS|FASTMODE */
- jmp mesgin_done
-
-/*
- * Save data pointers message? Copy working values into the SCB,
- * usually in preparation for a disconnect.
- */
-mesgin_sdptrs:
- call sg_ram2scb
- jmp mesgin_done
-
-/*
- * Restore pointers message? Data pointers are recopied from the
- * SCB anytime we enter a data phase for the first time, so all
- * we need to do is clear the DPHASE flag and let the data phase
- * code do the rest.
- */
-mesgin_rdptrs:
- and FLAGS,0xef /*
- * !DPHASE we'll reload them
- * the next time through
- */
- jmp mesgin_done
-
-/*
- * Identify message? For a reconnecting target, this tells us the lun
- * that the reconnection is for - find the correct SCB and switch to it,
- * clearing the "disconnected" bit so we don't "find" it by accident later.
- */
-mesgin_identify:
- test A,0x78 jnz rej_mesgin /*!DiscPriv|!LUNTAR|!Reserved*/
-
- and A,0x07 /* lun in lower three bits */
- or SAVED_TCL,A,SELID
- and SAVED_TCL,0xf7
- and A,SELBUSB,SBLKCTL /* B Channel?? */
- or SAVED_TCL,A
- call inb_last /* ACK */
-
-/*
- * Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message.
- * If we get one, we use the tag returned to switch to find the proper
- * SCB. With SCB paging, this requires using findSCB for both tagged
- * and non-tagged transactions since the SCB may exist in any slot.
- * If we're not using SCB paging, we can use the tag as the direct
- * index to the SCB.
- */
- mvi ARG_1,SCB_LIST_NULL /* Default to no-tag */
-snoop_tag_loop:
- test SSTAT1,BUSFREE jnz use_findSCB
- test SSTAT1,REQINIT jz snoop_tag_loop
- test SSTAT1,PHASEMIS jnz use_findSCB
- mvi A call inb_first
- cmp A,MSG_SIMPLE_TAG jne use_findSCB
-get_tag:
- mvi ARG_1 call inb_next /* tag value */
-/*
- * See if the tag is in range. The tag is < SCBCOUNT if we add
- * the complement of SCBCOUNT to the incoming tag and there is
- * no carry.
- */
- mov A,COMP_SCBCOUNT
- add SINDEX,A,ARG_1
- jc abort_tag
-
-/*
- * Ensure that the SCB the tag points to is for a SCB transaction
- * to the reconnecting target.
- */
- test FLAGS, PAGESCBS jz index_by_tag
- call inb_last /* Ack Tag */
-use_findSCB:
- mov ALLZEROS call findSCB /* Have to search */
-setup_SCB:
- and SCB_CONTROL,0xfb /* clear disconnect bit in SCB */
- or FLAGS,IDENTIFY_SEEN /* make note of IDENTIFY */
- jmp ITloop
-index_by_tag:
- mov SCBPTR,ARG_1
- mov A,SAVED_TCL
- cmp SCB_TCL,A jne abort_tag
- test SCB_CONTROL,TAG_ENB jz abort_tag
- call inb_last /* Ack Successful tag */
- jmp setup_SCB
-
-abort_tag:
- or SCSISIGO,ATNO /* turn on ATNO */
- mvi INTSTAT,ABORT_TAG /* let driver know */
- mvi MSG_ABORT_TAG call mk_mesg /* ABORT TAG message */
- jmp mesgin_done
-
-/*
- * Message reject? Let the kernel driver handle this. If we have an
- * outstanding WDTR or SDTR negotiation, assume that it's a response from
- * the target selecting 8bit or asynchronous transfer, otherwise just ignore
- * it since we have no clue what it pertains to.
- */
-mesgin_reject:
- mvi INTSTAT, REJECT_MSG
- jmp mesgin_done
-
-/*
- * [ ADD MORE MESSAGE HANDLING HERE ]
- */
-
-/*
- * Bus free phase. It might be useful to interrupt the device
- * driver if we aren't expecting this. For now, make sure that
- * ATN isn't being asserted and look for a new command.
- */
-p_busfree:
- mvi CLRSINT1,CLRATNO
- clr LASTPHASE
-
-/*
- * if this is an immediate command, perform a pseudo command complete to
- * notify the driver.
- */
- test SCB_CMDLEN,0xff jz status_ok
- jmp start
-
-/*
- * Locking the driver out, build a one-byte message passed in SINDEX
- * if there is no active message already. SINDEX is returned intact.
- */
-mk_mesg:
- mvi SEQCTL,0x50 /* PAUSEDIS|FASTMODE */
- test MSG_LEN,0xff jz mk_mesg1 /* Should always succeed */
-
- /*
- * Hmmm. For some reason the mesg buffer is in use.
- * Tell the driver. It should look at SINDEX to find
- * out what we wanted to use the buffer for and resolve
- * the conflict.
- */
- mvi SEQCTL,0x10 /* !PAUSEDIS|FASTMODE */
- mvi INTSTAT,MSG_BUFFER_BUSY
-
-mk_mesg1:
- mvi MSG_LEN,1 /* length = 1 */
- mov MSG0,SINDEX /* 1-byte message */
- mvi SEQCTL,0x10 ret /* !PAUSEDIS|FASTMODE */
-
-/*
- * Functions to read data in Automatic PIO mode.
- *
- * According to Adaptec's documentation, an ACK is not sent on input from
- * the target until SCSIDATL is read from. So we wait until SCSIDATL is
- * latched (the usual way), then read the data byte directly off the bus
- * using SCSIBUSL. When we have pulled the ATN line, or we just want to
- * acknowledge the byte, then we do a dummy read from SCISDATL. The SCSI
- * spec guarantees that the target will hold the data byte on the bus until
- * we send our ACK.
- *
- * The assumption here is that these are called in a particular sequence,
- * and that REQ is already set when inb_first is called. inb_{first,next}
- * use the same calling convention as inb.
- */
-
-inb_next:
- or CLRSINT0, CLRSPIORDY
- mov NONE,SCSIDATL /*dummy read from latch to ACK*/
-inb_next_wait:
- test SSTAT1,PHASEMIS jnz mesgin_phasemis
- test SSTAT0,SPIORDY jz inb_next_wait /* wait for next byte */
-inb_first:
- mov DINDEX,SINDEX
- test SSTAT1,PHASEMIS jnz mesgin_phasemis
- mov DINDIR,SCSIBUSL ret /*read byte directly from bus*/
-inb_last:
- mov NONE,SCSIDATL ret /*dummy read from latch to ACK*/
-
-mesgin_phasemis:
-/*
- * We expected to receive another byte, but the target changed phase
- */
- mvi INTSTAT, MSGIN_PHASEMIS
- jmp ITloop
-
-/*
- * DMA data transfer. HADDR and HCNT must be loaded first, and
- * SINDEX should contain the value to load DFCNTRL with - 0x3d for
- * host->scsi, or 0x39 for scsi->host. The SCSI channel is cleared
- * during initialization.
- */
-dma:
- mov DFCNTRL,SINDEX
-dma1:
- test SSTAT0,DMADONE jnz dma3
- test SSTAT1,PHASEMIS jz dma1 /* ie. underrun */
-
-/*
- * We will be "done" DMAing when the transfer count goes to zero, or
- * the target changes the phase (in light of this, it makes sense that
- * the DMA circuitry doesn't ACK when PHASEMIS is active). If we are
- * doing a SCSI->Host transfer, the data FIFO should be flushed auto-
- * magically on STCNT=0 or a phase change, so just wait for FIFO empty
- * status.
- */
-dma3:
- test SINDEX,DIRECTION jnz dma5
-dma4:
- test DFSTATUS,FIFOEMP jz dma4
-
-/*
- * Now shut the DMA enables off and make sure that the DMA enables are
- * actually off first lest we get an ILLSADDR.
- */
-dma5:
- /* disable DMA, but maintain WIDEODD */
- and DFCNTRL,WIDEODD
-dma6:
- test DFCNTRL,0x38 jnz dma6 /* SCSIENACK|SDMAENACK|HDMAENACK */
-
- ret
-
-/*
- * Common SCSI initialization for selection and reselection. Expects
- * the target SCSI ID to be in the upper four bits of SINDEX, and A's
- * contents are stomped on return.
- */
-initialize_scsiid:
- and SINDEX,0xf0 /* Get target ID */
- and A,0x0f,SCSIID
- or SINDEX,A
- mov SCSIID,SINDEX ret
-
-/*
- * Assert that if we've been reselected, then we've seen an IDENTIFY
- * message.
- */
-assert:
- test FLAGS,RESELECTED jz return /* reselected? */
- test FLAGS,IDENTIFY_SEEN jnz return /* seen IDENTIFY? */
-
- mvi INTSTAT,NO_IDENT ret /* no - cause a kernel panic */
-
-/*
- * Locate the SCB matching the target ID/channel/lun in SAVED_TCL, and the tag
- * value in ARG_1. If ARG_1 == SCB_LIST_NULL, we're looking for a non-tagged
- * SCB. Have the kernel print a warning message if it can't be found, and
- * generate an ABORT/ABORT_TAG message to the target. SINDEX should be
- * cleared on call.
- */
-findSCB:
- mov A,SAVED_TCL
- mov SCBPTR,SINDEX /* switch to next SCB */
- mvi SEQCTL,0x50 /* PAUSEDIS|FASTMODE */
- cmp SCB_TCL,A jne findSCB1 /* target ID/channel/lun match? */
- test SCB_CONTROL,DISCONNECTED jz findSCB1 /*should be disconnected*/
- test SCB_CONTROL,TAG_ENB jnz findTaggedSCB
- cmp ARG_1,SCB_LIST_NULL je foundSCB
- jmp findSCB1
-findTaggedSCB:
- mov A, ARG_1 /* Tag passed in ARG_1 */
- cmp SCB_TAG,A jne findSCB1 /* Found it? */
-foundSCB:
- test FLAGS,PAGESCBS jz foundSCB_ret
-/* Remove this SCB from the disconnection list */
- cmp SCB_NEXT,SCB_LIST_NULL je unlink_prev
- mov SAVED_LINKPTR, SCB_PREV
- mov SCBPTR, SCB_NEXT
- mov SCB_PREV, SAVED_LINKPTR
- mov SCBPTR, SINDEX
-unlink_prev:
- cmp SCB_PREV,SCB_LIST_NULL je rHead/* At the head of the list */
- mov SAVED_LINKPTR, SCB_NEXT
- mov SCBPTR, SCB_PREV
- mov SCB_NEXT, SAVED_LINKPTR
- mov SCBPTR, SINDEX
- mvi SEQCTL,0x10 ret /* !PAUSEDIS|FASTMODE */
-rHead:
- mov DISCONNECTED_SCBH,SCB_NEXT
-foundSCB_ret:
- mvi SEQCTL,0x10 ret /* !PAUSEDIS|FASTMODE */
-
-findSCB1:
- mvi SEQCTL,0x10 /* !PAUSEDIS|FASTMODE */
- inc SINDEX
- mov A,SCBCOUNT
- cmp SINDEX,A jne findSCB
-
- mvi INTSTAT,NO_MATCH /* not found - signal kernel */
- cmp RETURN_1,SCB_PAGEDIN je return
- or SCSISIGO,ATNO /* assert ATNO */
- cmp ARG_1,SCB_LIST_NULL jne find_abort_tag
- mvi MSG_ABORT call mk_mesg
- jmp ITloop
-find_abort_tag:
- mvi MSG_ABORT_TAG call mk_mesg
- jmp ITloop
-
-/*
- * Make a working copy of the scatter-gather parameters from the SCB.
- */
-sg_scb2ram:
- mov HADDR0, SCB_DATAPTR0
- mov HADDR1, SCB_DATAPTR1
- mov HADDR2, SCB_DATAPTR2
- mov HADDR3, SCB_DATAPTR3
- mov HCNT0, SCB_DATACNT0
- mov HCNT1, SCB_DATACNT1
- mov HCNT2, SCB_DATACNT2
-
- mov STCNT0, HCNT0
- mov STCNT1, HCNT1
- mov STCNT2, HCNT2
-
- mov SG_COUNT,SCB_SGCOUNT
-
- mov SG_NEXT0, SCB_SGPTR0
- mov SG_NEXT1, SCB_SGPTR1
- mov SG_NEXT2, SCB_SGPTR2
- mov SG_NEXT3, SCB_SGPTR3 ret
-
-/*
- * Copying RAM values back to SCB, for Save Data Pointers message, but
- * only if we've actually been into a data phase to change them. This
- * protects against bogus data in scratch ram and the residual counts
- * since they are only initialized when we go into data_in or data_out.
- */
-sg_ram2scb:
- test FLAGS, DPHASE jz return
- mov SCB_SGCOUNT,SG_COUNT
-
- mov SCB_SGPTR0,SG_NEXT0
- mov SCB_SGPTR1,SG_NEXT1
- mov SCB_SGPTR2,SG_NEXT2
- mov SCB_SGPTR3,SG_NEXT3
-
- mov SCB_DATAPTR0,SHADDR0
- mov SCB_DATAPTR1,SHADDR1
- mov SCB_DATAPTR2,SHADDR2
- mov SCB_DATAPTR3,SHADDR3
-
-/*
- * Use the residual number since STCNT is corrupted by any message transfer
- */
- mov SCB_DATACNT0,SCB_RESID_DCNT0
- mov SCB_DATACNT1,SCB_RESID_DCNT1
- mov SCB_DATACNT2,SCB_RESID_DCNT2 ret
-
-/*
- * Add the array base TARG_SCRATCH to the target offset (the target address
- * is in SCSIID), and return the result in SINDEX. The accumulator
- * contains the 3->8 decoding of the target ID on return.
- */
-ndx_dtr:
- shr A,SCSIID,4
- test SBLKCTL,SELBUSB jz ndx_dtr_2
- or A,0x08 /* Channel B entries add 8 */
-ndx_dtr_2:
- add SINDEX,TARG_SCRATCH,A ret
-
-/*
- * If we need to negotiate transfer parameters, build the WDTR or SDTR message
- * starting at the address passed in SINDEX. DINDEX is modified on return.
- * The SCSI-II spec requires that Wide negotiation occur first and you can
- * only negotiate one or the other at a time otherwise in the event of a message
- * reject, you wouldn't be able to tell which message was the culprit.
- */
-mk_dtr:
- test SCB_CONTROL,NEEDWDTR jnz mk_wdtr_16bit
- mvi ARG_1, MAXOFFSET /* Force an offset of 15 or 8 if WIDE */
-
-mk_sdtr:
- mvi DINDIR,1 /* extended message */
- mvi DINDIR,3 /* extended message length = 3 */
- mvi DINDIR,1 /* SDTR code */
- call sdtr_to_rate
- mov DINDIR,RETURN_1 /* REQ/ACK transfer period */
- cmp ARG_1, MAXOFFSET je mk_sdtr_max_offset
- and DINDIR,0x0f,SINDIR /* Sync Offset */
-
-mk_sdtr_done:
- add MSG_LEN,COMP_MSG0,DINDEX ret /* update message length */
-
-mk_sdtr_max_offset:
-/*
- * We're initiating sync negotiation, so request the max offset we can (15 or 8)
- */
- /* Talking to a WIDE device? */
- test SCSIRATE, WIDEXFER jnz wmax_offset
- mvi DINDIR, MAX_OFFSET_8BIT
- jmp mk_sdtr_done
-
-wmax_offset:
- mvi DINDIR, MAX_OFFSET_16BIT
- jmp mk_sdtr_done
-
-mk_wdtr_16bit:
- mvi ARG_1,BUS_16_BIT
-mk_wdtr:
- mvi DINDIR,1 /* extended message */
- mvi DINDIR,2 /* extended message length = 2 */
- mvi DINDIR,3 /* WDTR code */
- mov DINDIR,ARG_1 /* bus width */
-
- add MSG_LEN,COMP_MSG0,DINDEX ret /* update message length */
-
-sdtr_to_rate:
- call ndx_dtr /* index scratch space for target */
- shr A,SINDIR,0x4
- dec SINDEX /* Preserve SINDEX */
- and A,0x7
- clr RETURN_1
-sdtr_to_rate_loop:
- test A,0x0f jz sdtr_to_rate_done
- add RETURN_1,0x19
- dec A
- jmp sdtr_to_rate_loop
-sdtr_to_rate_done:
- shr RETURN_1,0x2
- add RETURN_1,0x19
- test SXFRCTL0,ULTRAEN jz return
- shr RETURN_1,0x1
-return:
- ret
diff --git a/drivers/scsi/aic7xxx_asm.c b/drivers/scsi/aic7xxx_asm.c
index 544edf0fa..e69de29bb 100644
--- a/drivers/scsi/aic7xxx_asm.c
+++ b/drivers/scsi/aic7xxx_asm.c
@@ -1,734 +0,0 @@
-/*+M*************************************************************************
- * Adaptec AIC7xxx sequencer code assembler.
- *
- * Copyright (c) 1994 John Aycock
- * The University of Calgary Department of Computer Science.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Comments are started by `#' and continue to the end of the line; lines
- * may be of the form:
- * <label>*
- * <label>* <undef-sym> = <value>
- * <label>* <opcode> <operand>*
- *
- * A <label> is an <undef-sym> ending in a colon. Spaces, tabs, and commas
- * are token separators.
- *-M*************************************************************************/
-static const char id[] = "$Id: aic7xxx_asm.c,v 3.0 1996/04/16 08:52:23 deang Exp $";
-#include <ctype.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#define MEMORY 448
-#define MAXLINE 1024
-#define MAXTOKEN 32
-#define ADOTOUT "a.out"
-#define NOVALUE -1
-
-#ifndef TRUE
-# define TRUE 1
-#endif
-#ifndef FALSE
-# define FALSE 0
-#endif
-#define MAX_ARGS 16
-static const char *cpp[] = {
- "/lib/cpp -P - -",
- "/usr/lib/cpp -P - -",
- "/usr/bin/cpp -P - -",
- "/usr/bin/gcc -E -P -",
- "/usr/bin/cc -E -P -"
-};
-
-#define NUMBER(arr) (sizeof(arr) / sizeof(arr[0]))
-
-/*
- * AIC-7770/AIC-7870 register definitions
- */
-#define R_SINDEX 0x65
-#define R_ALLONES 0x69
-#define R_ALLZEROS 0x6a
-#define R_NONE 0x6a
-
-int debug;
-int lineno, LC;
-char *filename;
-unsigned char M[MEMORY][4];
-
-void
-error(const char *s)
-{
- fprintf(stderr, "%s: %s at line %d\n", filename, s, lineno);
- exit(EXIT_FAILURE);
-}
-
-void *
-Malloc(size_t size)
-{
- void *p = malloc(size);
- if (!p)
- error("out of memory");
- return(p);
-}
-
-void *
-Realloc(void *ptr, size_t size)
-{
- void *p = realloc(ptr, size);
- if (!p)
- error("out of memory");
- return(p);
-}
-
-char *
-Strdup(char *s)
-{
- char *p = (char *)Malloc(strlen(s) + 1);
- strcpy(p, s);
- return(p);
-}
-
-typedef struct sym_t {
- struct sym_t *next; /* MUST BE FIRST */
- char *name;
- int value;
- int npatch;
- int *patch;
-} sym_t;
-
-sym_t *head;
-
-void
-define(char *name, int value)
-{
- sym_t *p, *q;
-
- for (p = head, q = (sym_t *)&head; p; p = p->next) {
- if (!strcmp(p->name, name))
- error("redefined symbol");
- q = p;
- }
-
- p = q->next = (sym_t *)Malloc(sizeof(sym_t));
- p->next = NULL;
- p->name = Strdup(name);
- p->value = value;
- p->npatch = 0;
- p->patch = NULL;
-
- if (debug) {
- fprintf(stderr, "\"%s\" ", p->name);
- if (p->value != NOVALUE)
- fprintf(stderr, "defined as 0x%x\n", p->value);
- else
- fprintf(stderr, "undefined\n");
- }
-}
-
-sym_t *
-lookup(char *name)
-{
- sym_t *p;
-
- for (p = head; p; p = p->next)
- if (!strcmp(p->name, name))
- return(p);
- return(NULL);
-}
-
-void
-patch(sym_t *p, int location)
-{
- p->npatch += 1;
- p->patch = (int *)Realloc(p->patch, p->npatch * sizeof(int *));
-
- p->patch[p->npatch - 1] = location;
-}
-
-void backpatch(void)
-{
- int i;
- sym_t *p;
-
- for (p = head; p; p = p->next) {
-
- if (p->value == NOVALUE) {
- fprintf(stderr,
- "%s: undefined symbol \"%s\"\n",
- filename, p->name);
- exit(EXIT_FAILURE);
- }
-
- if (p->npatch) {
- if (debug)
- fprintf(stderr,
- "\"%s\" (0x%x) patched at",
- p->name, p->value);
-
- for (i = 0; i < p->npatch; i++) {
- M[p->patch[i]][0] &= ~1;
- M[p->patch[i]][0] |= ((p->value >> 8) & 1);
- M[p->patch[i]][1] = p->value & 0xff;
-
- if (debug)
- fprintf(stderr, " 0x%x", p->patch[i]);
- }
-
- if (debug)
- fputc('\n', stderr);
- }
- }
-}
-
-/*
- * Output words in byte-reversed order (least significant first)
- * since the sequencer RAM is loaded that way.
- */
-void
-output(FILE *fp)
-{
- int i;
-
- for (i = 0; i < LC; i++)
- fprintf(fp, "\t0x%02x, 0x%02x, 0x%02x, 0x%02x,\n",
- M[i][3],
- M[i][2],
- M[i][1],
- M[i][0]);
- printf("%d out of %d instructions used.\n", LC, MEMORY);
-}
-
-char **
-getl(int *n)
-{
- int i;
- char *p, *quote;
- static char buf[MAXLINE];
- static char *a[MAXTOKEN];
-
- i = 0;
-
- while (fgets(buf, sizeof(buf), stdin)) {
-
- lineno += 1;
-
- if (buf[strlen(buf)-1] != '\n')
- error("line too long");
-
- p = strchr(buf, '#');
- if (p)
- *p = '\0';
- p = buf;
-rescan:
- quote = strchr(p, '\"');
- if (quote)
- *quote = '\0';
- for (p = strtok(p, ", \t\n"); p; p = strtok(NULL, ", \t\n"))
- if (i < MAXTOKEN-1)
- a[i++] = p;
- else
- error("too many tokens");
- if (quote) {
- quote++;
- p = strchr(quote, '\"');
- if (!p)
- error("unterminated string constant");
- else if (i < MAXTOKEN-1) {
- a[i++] = quote;
- *p = '\0';
- p++;
- }
- else
- error("too many tokens");
- goto rescan;
- }
- if (i) {
- *n = i;
- return(a);
- }
- }
- return(NULL);
-}
-
-#define A 0x8000 /* `A'ccumulator ok */
-#define I 0x4000 /* use as immediate value */
-#define SL 0x2000 /* shift left */
-#define SR 0x1000 /* shift right */
-#define RL 0x0800 /* rotate left */
-#define RR 0x0400 /* rotate right */
-#define LO 0x8000 /* lookup: ori-{jmp,jc,jnc,call} */
-#define LA 0x4000 /* lookup: and-{jz,jnz} */
-#define LX 0x2000 /* lookup: xor-{je,jne} */
-#define NA -1 /* not applicable */
-
-struct {
- const char *name;
- int n; /* number of operands, including opcode */
- unsigned int op; /* immediate or L?|pos_from_0 */
- unsigned int dest; /* NA, pos_from_0, or I|immediate */
- unsigned int src; /* NA, pos_from_0, or I|immediate */
- unsigned int imm; /* pos_from_0, A|pos_from_0, or I|immediate */
- unsigned int addr; /* NA or pos_from_0 */
- int fmt; /* instruction format - 1, 2, or 3 */
-} instr[] = {
-/*
- * N OP DEST SRC IMM ADDR FMT
- */
- { "mov", 3, 1, 1, 2, I|0xff, NA, 1 },
- { "mov", 4, LO|2, NA, 1, I|0, 3, 3 },
- { "mvi", 3, 0, 1, I|R_ALLZEROS, A|2, NA, 1 },
- { "mvi", 4, LO|2, NA, I|R_ALLZEROS, 1, 3, 3 },
- { "not", 2, 2, 1, 1, I|0xff, NA, 1 },
- { "and", 3, 1, 1, 1, A|2, NA, 1 },
- { "and", 4, 1, 1, 3, A|2, NA, 1 },
- { "or", 3, 0, 1, 1, A|2, NA, 1 },
- { "or", 4, 0, 1, 3, A|2, NA, 1 },
- { "or", 5, LO|3, NA, 1, 2, 4, 3 },
- { "xor", 3, 2, 1, 1, A|2, NA, 1 },
- { "xor", 4, 2, 1, 3, A|2, NA, 1 },
- { "nop", 1, 1, I|R_NONE, I|R_ALLZEROS, I|0xff, NA, 1 },
- { "inc", 2, 3, 1, 1, I|1, NA, 1 },
- { "inc", 3, 3, 1, 2, I|1, NA, 1 },
- { "dec", 2, 3, 1, 1, I|0xff, NA, 1 },
- { "dec", 3, 3, 1, 2, I|0xff, NA, 1 },
- { "jmp", 2, LO|0, NA, I|R_SINDEX, I|0, 1, 3 },
- { "jc", 2, LO|0, NA, I|R_SINDEX, I|0, 1, 3 },
- { "jnc", 2, LO|0, NA, I|R_SINDEX, I|0, 1, 3 },
- { "call", 2, LO|0, NA, I|R_SINDEX, I|0, 1, 3 },
- { "test", 5, LA|3, NA, 1, A|2, 4, 3 },
- { "cmp", 5, LX|3, NA, 1, A|2, 4, 3 },
- { "ret", 1, 1, I|R_NONE, I|R_ALLZEROS, I|0xff, NA, 1 },
- { "ret", 1, 1, I|R_NONE, I|R_ALLZEROS, I|0xff, NA, 1 },
- { "clc", 1, 3, I|R_NONE, I|R_ALLZEROS, I|1, NA, 1 },
- { "clc", 4, 3, 2, I|R_ALLZEROS, A|3, NA, 1 },
- { "stc", 2, 3, 1, I|R_ALLONES, I|1, NA, 1 },
- { "add", 3, 3, 1, 1, A|2, NA, 1 },
- { "add", 4, 3, 1, 3, A|2, NA, 1 },
- { "adc", 3, 4, 1, 1, A|2, NA, 1 },
- { "adc", 4, 4, 1, 3, A|2, NA, 1 },
- { "shl", 3, 5, 1, 1, SL|2, NA, 2 },
- { "shl", 4, 5, 1, 2, SL|3, NA, 2 },
- { "shr", 3, 5, 1, 1, SR|2, NA, 2 },
- { "shr", 4, 5, 1, 2, SR|3, NA, 2 },
- { "rol", 3, 5, 1, 1, RL|2, NA, 2 },
- { "rol", 4, 5, 1, 2, RL|3, NA, 2 },
- { "ror", 3, 5, 1, 1, RR|2, NA, 2 },
- { "ror", 4, 5, 1, 2, RR|3, NA, 2 },
- /*
- * Extensions (note also that mvi allows A)
- */
- { "clr", 2, 1, 1, I|R_ALLZEROS, I|0xff, NA, 1 },
- { 0, 0, 0, 0, 0, 0, 0, 0 }
-};
-
-int
-eval_operand(char **a, int spec)
-{
- int i;
- unsigned int want = spec & (LO|LA|LX);
-
- static struct {
- unsigned int what;
- const char *name;
- int value;
- } jmptab[] = {
- { LO, "jmp", 8 },
- { LO, "jc", 9 },
- { LO, "jnc", 10 },
- { LO, "call", 11 },
- { LA, "jz", 15 },
- { LA, "jnz", 13 },
- { LX, "je", 14 },
- { LX, "jne", 12 },
- };
-
- spec &= ~(LO|LA|LX);
-
- for (i = 0; i < sizeof(jmptab)/sizeof(jmptab[0]); i++)
- if (jmptab[i].what == want &&
- !strcmp(jmptab[i].name, a[spec]))
- {
- return(jmptab[i].value);
- }
-
- if (want)
- error("invalid jump");
-
- return(spec); /* "case 0" - no flags set */
-}
-
-int
-eval_sdi(char **a, int spec)
-{
- sym_t *p;
- unsigned val;
-
- if (spec == NA)
- return(NA);
-
- switch (spec & (A|I|SL|SR|RL|RR)) {
- case SL:
- case SR:
- case RL:
- case RR:
- if (isdigit(*a[spec &~ (SL|SR|RL|RR)]))
- val = strtol(a[spec &~ (SL|SR|RL|RR)], NULL, 0);
- else {
- p = lookup(a[spec &~ (SL|SR|RL|RR)]);
- if (!p)
- error("undefined symbol used");
- val = p->value;
- }
-
- switch (spec & (SL|SR|RL|RR)) { /* blech */
- case SL:
- if (val > 7)
- return(0xf0);
- return(((val % 8) << 4) |
- (val % 8));
- case SR:
- if (val > 7)
- return(0xf0);
- return(((val % 8) << 4) |
- (1 << 3) |
- ((8 - (val % 8)) % 8));
- case RL:
- return(val % 8);
- case RR:
- return((8 - (val % 8)) % 8);
- }
- case I:
- return(spec &~ I);
- case A:
- /*
- * An immediate field of zero selects
- * the accumulator. Vigorously object
- * if zero is given otherwise - it's
- * most likely an error.
- */
- spec &= ~A;
- if (!strcmp("A", a[spec]))
- return(0);
- if (isdigit(*a[spec]) &&
- strtol(a[spec], NULL, 0) == 0)
- {
- error("immediate value of zero selects accumulator");
- }
- /* falls through */
- case 0:
- if (isdigit(*a[spec]))
- return(strtol(a[spec], NULL, 0));
- p = lookup(a[spec]);
- if (p)
- return(p->value);
- error("undefined symbol used");
- }
-
- return(NA); /* shut the compiler up */
-}
-
-int
-eval_addr(char **a, int spec)
-{
- sym_t *p;
-
- if (spec == NA)
- return(NA);
- if (isdigit(*a[spec]))
- return(strtol(a[spec], NULL, 0));
-
- p = lookup(a[spec]);
-
- if (p) {
- if (p->value != NOVALUE)
- return(p->value);
- patch(p, LC);
- } else {
- define(a[spec], NOVALUE);
- p = lookup(a[spec]);
- patch(p, LC);
- }
-
- return(NA); /* will be patched in later */
-}
-
-int
-crack(char **a, int n)
-{
- int i;
- int I_imm, I_addr;
- int I_op, I_dest, I_src, I_ret;
-
- /*
- * Check for "ret" at the end of the line; remove
- * it unless it's "ret" alone - we still want to
- * look it up in the table.
- */
- I_ret = (strcmp(a[n-1], "ret") ? 0 : !0);
- if (I_ret && n > 1)
- n -= 1;
-
- for (i = 0; instr[i].name; i++) {
- /*
- * Look for match in table given constraints,
- * currently just the name and the number of
- * operands.
- */
- if (!strcmp(instr[i].name, *a) && instr[i].n == n)
- break;
- }
- if (!instr[i].name)
- error("unknown opcode or wrong number of operands");
-
- I_op = eval_operand(a, instr[i].op);
- I_src = eval_sdi(a, instr[i].src);
- I_imm = eval_sdi(a, instr[i].imm);
- I_dest = eval_sdi(a, instr[i].dest);
- I_addr = eval_addr(a, instr[i].addr);
-
- if( LC >= MEMORY )
- error("Memory exhausted!\n");
-
- switch (instr[i].fmt) {
- case 1:
- case 2:
- M[LC][0] = (I_op << 1) | I_ret;
- M[LC][1] = I_dest;
- M[LC][2] = I_src;
- M[LC][3] = I_imm;
- break;
- case 3:
- if (I_ret)
- error("illegal use of \"ret\"");
- M[LC][0] = (I_op << 1) | ((I_addr >> 8) & 1);
- M[LC][1] = I_addr & 0xff;
- M[LC][2] = I_src;
- M[LC][3] = I_imm;
- break;
- }
-
- return (1); /* no two-byte instructions yet */
-}
-
-#undef SL
-#undef SR
-#undef RL
-#undef RR
-#undef LX
-#undef LA
-#undef LO
-#undef I
-#undef A
-
-void
-assemble(FILE *ofile)
-{
- int n;
- char **a;
- sym_t *p;
-
- while ((a = getl(&n))) {
-
- while (a[0][strlen(*a)-1] == ':') {
- a[0][strlen(*a)-1] = '\0';
- p = lookup(*a);
- if (p)
- p->value = LC;
- else
- define(*a, LC);
- a += 1;
- n -= 1;
- }
-
- if (!n) /* line was all labels */
- continue;
-
- if (n == 3 && !strcmp("VERSION", *a))
- fprintf(ofile, "#define %s \"%s\"\n", a[1], a[2]);
- else {
- if (n == 3 && !strcmp("=", a[1]))
- define(*a, strtol(a[2], NULL, 0));
- else
- LC += crack(a, n);
- }
- }
-
- backpatch();
- output(ofile);
-
- if (debug)
- output(stderr);
-}
-
-int
-main(int argc, char **argv)
-{
- int c;
- int pid;
- int ifile;
- int status;
- FILE *ofile;
- char *ofilename;
- int fd[2];
-
- ofile = NULL;
- ofilename = NULL;
- while ((c = getopt(argc, argv, "dho:vD:")) != EOF) {
- switch (c) {
- case 'd':
- debug = !0;
- break;
- case 'D':
- {
- char *p;
- if ((p = strchr(optarg, '=')) != NULL) {
- *p = '\0';
- define(optarg, strtol(p + 1, NULL, 0));
- }
- else
- define(optarg, 1);
- break;
- }
- case 'o':
- ofilename = optarg;
- if ((ofile = fopen(ofilename, "w")) == NULL) {
- perror(optarg);
- exit(EXIT_FAILURE);
- }
- break;
- case 'h':
- printf("usage: %s [-d] [-Dname] [-ooutput] input\n",
- *argv);
- exit(EXIT_SUCCESS);
- break;
- case 'v':
- printf("%s\n", id);
- exit(EXIT_SUCCESS);
- break;
- default:
- exit(EXIT_FAILURE);
- break;
- }
- }
-
- if (argc - optind != 1) {
- fprintf(stderr, "%s: must have one input file\n", *argv);
- exit(EXIT_FAILURE);
- }
- filename = argv[optind];
-
-
- if ((ifile = open(filename, O_RDONLY)) < 0) {
- perror(filename);
- exit(EXIT_FAILURE);
- }
-
- if (!ofilename) {
- ofilename = ADOTOUT;
- if ((ofile = fopen(ofilename, "w")) < 0) {
- perror(ofilename);
- exit(EXIT_FAILURE);
- }
- }
-
- if (pipe(fd) < 0) {
- perror("pipe failed");
- exit(1);
- }
-
- if ((pid = fork()) < 0 ) {
- perror("fork failed");
- exit(1);
- }
- else if (pid > 0) { /* Parent */
- close(fd[1]); /* Close write end */
- if (fd[0] != STDIN_FILENO) {
- if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) {
- perror("dup2 error on stdin");
- exit(EXIT_FAILURE);
- }
- close(fd[0]);
- }
- assemble(ofile);
- if (wait(&status) < 0) {
- perror("wait error");
- }
-
- if (status != 0) {
- unlink(ofilename);
- }
- exit(status);
- } else { /* Child */
- int i, arg_cnt, found;
- char *args[MAX_ARGS];
- char *buf;
-
- arg_cnt = 0;
- found = FALSE;
- for (i = 0; (!found && (i < NUMBER(cpp))); i++) {
- char *bp;
-
- buf = strdup(cpp[i]);
-
- for (bp = strtok(buf, " \t\n"), arg_cnt = 0;
- bp != NULL;
- bp = strtok(NULL, " \t\n"), arg_cnt++) {
- if (arg_cnt == 0) {
- if (access(bp, X_OK) == 0) {
- found = TRUE;
- }
- }
-
- args[arg_cnt] = bp;
- }
-
- if (!found) {
- free(buf);
- }
- }
- args[arg_cnt] = NULL;
-
- if (found) {
- close(fd[0]); /* Close Read end */
- if (fd[1] != STDOUT_FILENO) {
- if (dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO) {
- perror("dup2 error on stdout");
- exit(EXIT_FAILURE);
- }
- close(fd[1]);
- }
- if (ifile != STDIN_FILENO) {
- if (dup2(ifile, STDIN_FILENO) != STDIN_FILENO) {
- perror("dup2 error on stdin");
- exit(EXIT_FAILURE);
- }
- close(ifile);
- }
-
- if (execvp(args[0], args) < 0) {
- perror("execvp() error");
- exit(EXIT_FAILURE);
- }
- } else {
- fprintf(stderr, "%s: Cannot find CPP command.\n", argv[0]);
- exit(EXIT_FAILURE);
- }
- }
- return(EXIT_SUCCESS);
-}
diff --git a/drivers/scsi/aic7xxx_proc.c b/drivers/scsi/aic7xxx_proc.c
index a968c324f..1092d4862 100644
--- a/drivers/scsi/aic7xxx_proc.c
+++ b/drivers/scsi/aic7xxx_proc.c
@@ -24,7 +24,7 @@
*
* Dean W. Gehnert, deang@teleport.com, 05/01/96
*
- * $Id: aic7xxx_proc.c,v 4.0 1996/10/13 08:23:42 deang Exp $
+ * $Id: aic7xxx_proc.c,v 4.1 1997/06/97 08:23:42 deang Exp $
*-M*************************************************************************/
#define BLS buffer + len + size
@@ -77,16 +77,18 @@ aic7xxx_proc_info(char *buffer, char **start, off_t offset, int length,
struct Scsi_Host *HBAptr;
struct aic7xxx_host *p;
static u8 buff[512];
- int i;
+ int i;
+ int found = FALSE;
int size = 0;
int len = 0;
off_t begin = 0;
off_t pos = 0;
static char *bus_names[] = { "Single", "Twin", "Wide" };
- static char *chip_names[] = { "AIC-777x", "AIC-785x", "AIC-787x", "AIC-788x" };
+ static char *chip_names[] = { "AIC-777x", "AIC-785x", "AIC-786x",
+ "AIC-787x", "AIC-788x" };
HBAptr = NULL;
- for (i = 0; i < NUMBER(aic7xxx_boards); i++)
+ for (i=0; i < NUMBER(aic7xxx_boards); i++)
{
if ((HBAptr = aic7xxx_boards[i]) != NULL)
{
@@ -95,16 +97,23 @@ aic7xxx_proc_info(char *buffer, char **start, off_t offset, int length,
break;
}
- while ((HBAptr->hostdata != NULL) &&
+ while ((HBAptr->hostdata != NULL) && !found &&
((HBAptr = ((struct aic7xxx_host *) HBAptr->hostdata)->next) != NULL))
{
if (HBAptr->host_no == hostno)
{
- break; break;
+ found = TRUE;
}
}
- HBAptr = NULL;
+ if (!found)
+ {
+ HBAptr = NULL;
+ }
+ else
+ {
+ break;
+ }
}
}
@@ -129,8 +138,10 @@ aic7xxx_proc_info(char *buffer, char **start, off_t offset, int length,
size += sprintf(BLS, "Adaptec AIC7xxx driver version: ");
size += sprintf(BLS, "%s/", rcs_version(AIC7XXX_C_VERSION));
- size += sprintf(BLS, "%s/", rcs_version(AIC7XXX_H_VERSION));
+ size += sprintf(BLS, "%s", rcs_version(AIC7XXX_H_VERSION));
+#if 0
size += sprintf(BLS, "%s\n", rcs_version(AIC7XXX_SEQ_VER));
+#endif
len += size; pos = begin + len; size = 0;
size += sprintf(BLS, "\n");
@@ -141,11 +152,6 @@ aic7xxx_proc_info(char *buffer, char **start, off_t offset, int length,
#ifdef AIC7XXX_CMDS_PER_LUN
size += sprintf(BLS, " AIC7XXX_CMDS_PER_LUN : %d\n", AIC7XXX_CMDS_PER_LUN);
#endif
-#ifdef AIC7XXX_TWIN_SUPPORT
- size += sprintf(BLS, " AIC7XXX_TWIN_SUPPORT : Enabled\n");
-#else
- size += sprintf(BLS, " AIC7XXX_TWIN_SUPPORT : Disabled\n");
-#endif
#ifdef AIC7XXX_TAGGED_QUEUEING
size += sprintf(BLS, " AIC7XXX_TAGGED_QUEUEING: Enabled\n");
#else
@@ -165,16 +171,18 @@ aic7xxx_proc_info(char *buffer, char **start, off_t offset, int length,
size += sprintf(BLS, "\n");
size += sprintf(BLS, "Adapter Configuration:\n");
- size += sprintf(BLS, " SCSI Adapter: %s\n", board_names[p->type]);
+ size += sprintf(BLS, " SCSI Adapter: %s\n",
+ board_names[p->chip_type]);
size += sprintf(BLS, " (%s chipset)\n",
- chip_names[p->chip_type]);
+ chip_names[p->chip_class]);
size += sprintf(BLS, " Host Bus: %s\n", bus_names[p->bus_type]);
size += sprintf(BLS, " Base IO: %#.4x\n", p->base);
+ size += sprintf(BLS, " Base IO Memory: 0x%x\n", p->mbase);
size += sprintf(BLS, " IRQ: %d\n", HBAptr->irq);
size += sprintf(BLS, " SCBs: Used %d, HW %d, Page %d\n",
- p->scb_link->numscbs, p->maxhscbs, p->maxscbs);
+ p->scb_data->numscbs, p->scb_data->maxhscbs, p->scb_data->maxscbs);
size += sprintf(BLS, " Interrupts: %d", p->isr_count);
- if (p->chip_type == AIC_777x)
+ if (p->chip_class == AIC_777x)
{
size += sprintf(BLS, " %s\n",
(p->pause & IRQMS) ? "(Level Sensitive)" : "(Edge Triggered)");
diff --git a/drivers/scsi/aic7xxx_reg.h b/drivers/scsi/aic7xxx_reg.h
index d26a0c20e..f3b8c349c 100644
--- a/drivers/scsi/aic7xxx_reg.h
+++ b/drivers/scsi/aic7xxx_reg.h
@@ -1,771 +1,469 @@
-/*+M*************************************************************************
- * Adaptec AIC7xxx register and scratch ram definitions.
- *
- * Copyright (c) 1994, 1995, 1996 Justin T. Gibbs.
- * All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * This version corresponds to version 1.12 of FreeBSDs aic7xxx_reg.h
- *
- * $Id: aic7xxx_reg.h,v 4.0 1996/10/13 08:23:42 deang Exp $
- *-M*************************************************************************/
-
/*
- * This header is shared by the sequencer code and the kernel level driver.
- *
- * All page numbers refer to the Adaptec AIC-7770 Data Book available from
- * Adaptec's Technical Documents Department 1-800-934-2766
- */
+ * DO NOT EDIT - This file is automatically generated.
+ */
+
+#define SCSISEQ 0x00
+#define TEMODE 0x80
+#define ENSELO 0x40
+#define ENSELI 0x20
+#define ENRSELI 0x10
+#define ENAUTOATNO 0x08
+#define ENAUTOATNI 0x04
+#define ENAUTOATNP 0x02
+#define SCSIRSTO 0x01
+
+#define SXFRCTL0 0x01
+#define DFON 0x80
+#define DFPEXP 0x40
+#define FAST20 0x20
+#define CLRSTCNT 0x10
+#define SPIOEN 0x08
+#define SCAMEN 0x04
+#define CLRCHN 0x02
+
+#define SXFRCTL1 0x02
+#define BITBUCKET 0x80
+#define SWRAPEN 0x40
+#define ENSPCHK 0x20
+#define STIMESEL 0x18
+#define ENSTIMER 0x04
+#define ACTNEGEN 0x02
+#define STPWEN 0x01
+
+#define SCSISIGO 0x03
+#define CDO 0x80
+#define IOO 0x40
+#define MSGO 0x20
+#define ATNO 0x10
+#define SELO 0x08
+#define BSYO 0x04
+#define REQO 0x02
+#define ACKO 0x01
+
+#define SCSISIGI 0x03
+#define ATNI 0x10
+#define SELI 0x08
+#define BSYI 0x04
+#define REQI 0x02
+#define ACKI 0x01
+
+#define SCSIRATE 0x04
+#define WIDEXFER 0x80
+#define SXFR 0x70
+#define SOFS 0x0f
+
+#define SCSIID 0x05
+#define OID 0x0f
+
+#define SCSIDATL 0x06
+
+#define SCSIDATH 0x07
+
+#define STCNT 0x08
+
+#define CLRSINT0 0x0b
+#define CLRSELDO 0x40
+#define CLRSELDI 0x20
+#define CLRSELINGO 0x10
+#define CLRSWRAP 0x08
+#define CLRSPIORDY 0x02
+
+#define SSTAT0 0x0b
+#define TARGET 0x80
+#define SELDO 0x40
+#define SELDI 0x20
+#define SELINGO 0x10
+#define SWRAP 0x08
+#define SDONE 0x04
+#define SPIORDY 0x02
+#define DMADONE 0x01
+
+#define CLRSINT1 0x0c
+#define CLRSELTIMEO 0x80
+#define CLRATNO 0x40
+#define CLRSCSIRSTI 0x20
+#define CLRBUSFREE 0x08
+#define CLRSCSIPERR 0x04
+#define CLRPHASECHG 0x02
+#define CLRREQINIT 0x01
+
+#define SSTAT1 0x0c
+#define SELTO 0x80
+#define ATNTARG 0x40
+#define SCSIRSTI 0x20
+#define PHASEMIS 0x10
+#define BUSFREE 0x08
+#define SCSIPERR 0x04
+#define PHASECHG 0x02
+#define REQINIT 0x01
+
+#define SSTAT2 0x0d
+#define OVERRUN 0x80
+#define SFCNT 0x1f
+
+#define SSTAT3 0x0e
+#define SCSICNT 0xf0
+#define OFFCNT 0x0f
+
+#define SCSITEST 0x0f
+#define RQAKCNT 0x04
+#define CNTRTEST 0x02
+#define CMODE 0x01
+
+#define SIMODE0 0x10
+#define ENSELDO 0x40
+#define ENSELDI 0x20
+#define ENSELINGO 0x10
+#define ENSWRAP 0x08
+#define ENSDONE 0x04
+#define ENSPIORDY 0x02
+#define ENDMADONE 0x01
+
+#define SIMODE1 0x11
+#define ENSELTIMO 0x80
+#define ENATNTARG 0x40
+#define ENSCSIRST 0x20
+#define ENPHASEMIS 0x10
+#define ENBUSFREE 0x08
+#define ENSCSIPERR 0x04
+#define ENPHASECHG 0x02
+#define ENREQINIT 0x01
+
+#define SCSIBUSL 0x12
+
+#define SCSIBUSH 0x13
+
+#define SHADDR 0x14
+
+#define SELTIMER 0x18
+#define STAGE6 0x20
+#define STAGE5 0x10
+#define STAGE4 0x08
+#define STAGE3 0x04
+#define STAGE2 0x02
+#define STAGE1 0x01
+
+#define SELID 0x19
+#define SELID_MASK 0xf0
+#define ONEBIT 0x08
+
+#define BRDCTL 0x1d
+#define BRDDAT7 0x80
+#define BRDDAT6 0x40
+#define BRDDAT5 0x20
+#define BRDSTB 0x10
+#define BRDCS 0x08
+#define BRDRW 0x04
+#define BRDCTL1 0x02
+#define BRDCTL0 0x01
+
+#define SEECTL 0x1e
+#define EXTARBACK 0x80
+#define EXTARBREQ 0x40
+#define SEEMS 0x20
+#define SEERDY 0x10
+#define SEECS 0x08
+#define SEECK 0x04
+#define SEEDO 0x02
+#define SEEDI 0x01
+
+#define SBLKCTL 0x1f
+#define DIAGLEDEN 0x80
+#define DIAGLEDON 0x40
+#define AUTOFLUSHDIS 0x20
+#define SELWIDE 0x02
+
+#define SRAM_BASE 0x20
+
+#define TARG_SCRATCH 0x20
+
+#define ULTRA_ENB 0x30
+
+#define DISC_DSB 0x32
+
+#define MSG_LEN 0x34
+
+#define MSG_OUT 0x35
+
+#define DMAPARAMS 0x3d
+#define WIDEODD 0x40
+#define SCSIEN 0x20
+#define SDMAENACK 0x10
+#define SDMAEN 0x10
+#define HDMAEN 0x08
+#define HDMAENACK 0x08
+#define DIRECTION 0x04
+#define FIFOFLUSH 0x02
+#define FIFORESET 0x01
-/*
- * SCSI Sequence Control (p. 3-11).
- * Each bit, when set starts a specific SCSI sequence on the bus
- */
-#define SCSISEQ 0x000
-#define TEMODEO 0x80
-#define ENSELO 0x40
-#define ENSELI 0x20
-#define ENRSELI 0x10
-#define ENAUTOATNO 0x08
-#define ENAUTOATNI 0x04
-#define ENAUTOATNP 0x02
-#define SCSIRSTO 0x01
+#define SCBCOUNT 0x3e
-/*
- * SCSI Transfer Control 0 Register (pp. 3-13).
- * Controls the SCSI module data path.
- */
-#define SXFRCTL0 0x001
-#define DFON 0x80
-#define DFPEXP 0x40
-#define ULTRAEN 0x20
-#define CLRSTCNT 0x10
-#define SPIOEN 0x08
-#define SCAMEN 0x04
-#define CLRCHN 0x02
-/* UNUSED 0x01 */
+#define COMP_SCBCOUNT 0x3f
-/*
- * SCSI Transfer Control 1 Register (pp. 3-14,15).
- * Controls the SCSI module data path.
- */
-#define SXFRCTL1 0x002
-#define BITBUCKET 0x80
-#define SWRAPEN 0x40
-#define ENSPCHK 0x20
-#define STIMESEL 0x18
-#define ENSTIMER 0x04
-#define ACTNEGEN 0x02
-#define STPWEN 0x01 /* Powered Termination */
+#define QCNTMASK 0x40
-/*
- * SCSI Control Signal Read Register (p. 3-15).
- * Reads the actual state of the SCSI bus pins
- */
-#define SCSISIGI 0x003
-#define CDI 0x80
-#define IOI 0x40
-#define MSGI 0x20
-#define ATNI 0x10
-#define SELI 0x08
-#define BSYI 0x04
-#define REQI 0x02
-#define ACKI 0x01
+#define SEQ_FLAGS 0x41
+#define RESELECTED 0x80
+#define IDENTIFY_SEEN 0x40
+#define TAGGED_SCB 0x20
+#define DPHASE 0x10
+#define PAGESCBS 0x04
+#define WIDE_BUS 0x02
+#define TWIN_BUS 0x01
-/*
- * Possible phases in SCSISIGI
- */
-#define PHASE_MASK 0xe0
-#define P_DATAOUT 0x00
-#define P_DATAIN 0x40
-#define P_COMMAND 0x80
-#define P_MESGOUT 0xa0
-#define P_STATUS 0xc0
-#define P_MESGIN 0xe0
-/*
- * SCSI Control Signal Write Register (p. 3-16).
- * Writing to this register modifies the control signals on the bus. Only
- * those signals that are allowed in the current mode (Initiator/Target) are
- * asserted.
- */
-#define SCSISIGO 0x003
-#define CDO 0x80
-#define IOO 0x40
-#define MSGO 0x20
-#define ATNO 0x10
-#define SELO 0x08
-#define BSYO 0x04
-#define REQO 0x02
-#define ACKO 0x01
-
-/*
- * SCSI Rate Control (p. 3-17).
- * Contents of this register determine the Synchronous SCSI data transfer
- * rate and the maximum synchronous Req/Ack offset. An offset of 0 in the
- * SOFS (3:0) bits disables synchronous data transfers. Any offset value
- * greater than 0 enables synchronous transfers.
- */
-#define SCSIRATE 0x004
-#define WIDEXFER 0x80 /* Wide transfer control */
-#define SXFR 0x70 /* Sync transfer rate */
-#define SOFS 0x0f /* Sync offset */
+#define SAVED_TCL 0x42
-/*
- * SCSI ID (p. 3-18).
- * Contains the ID of the board and the current target on the
- * selected channel.
- */
-#define SCSIID 0x005
-#define TID 0xf0 /* Target ID mask */
-#define OID 0x0f /* Our ID mask */
+#define SG_COUNT 0x43
-/*
- * SCSI Latched Data (p. 3-19).
- * Read/Write latches used to transfer data on the SCSI bus during
- * Automatic or Manual PIO mode. SCSIDATH can be used for the
- * upper byte of a 16bit wide asynchronous data phase transfer.
- */
-#define SCSIDATL 0x006
-#define SCSIDATH 0x007
+#define SG_NEXT 0x44
-/*
- * SCSI Transfer Count (pp. 3-19,20)
- * These registers count down the number of bytes transfered
- * across the SCSI bus. The counter is decremented only once
- * the data has been safely transfered. SDONE in SSTAT0 is
- * set when STCNT goes to 0
- */
-#define STCNT 0x008
-#define STCNT0 0x008
-#define STCNT1 0x009
-#define STCNT2 0x00a
+#define WAITING_SCBH 0x48
-/*
- * Clear SCSI Interrupt 0 (p. 3-20)
- * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT0.
- */
-#define CLRSINT0 0x00b
-#define CLRSELDO 0x40
-#define CLRSELDI 0x20
-#define CLRSELINGO 0x10
-#define CLRSWRAP 0x08
-/* UNUSED 0x04 */
-#define CLRSPIORDY 0x02
-/* UNUSED 0x01 */
+#define SAVED_LINKPTR 0x49
-/*
- * SCSI Status 0 (p. 3-21)
- * Contains one set of SCSI Interrupt codes
- * These are most likely of interest to the sequencer
- */
-#define SSTAT0 0x00b
-#define TARGET 0x80 /* Board acting as target */
-#define SELDO 0x40 /* Selection Done */
-#define SELDI 0x20 /* Board has been selected */
-#define SELINGO 0x10 /* Selection In Progress */
-#define SWRAP 0x08 /* 24bit counter wrap */
-#define SDONE 0x04 /* STCNT = 0x000000 */
-#define SPIORDY 0x02 /* SCSI PIO Ready */
-#define DMADONE 0x01 /* DMA transfer completed */
+#define SAVED_SCBPTR 0x4a
-/*
- * Clear SCSI Interrupt 1 (p. 3-23)
- * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT1.
- */
-#define CLRSINT1 0x00c
-#define CLRSELTIMEO 0x80
-#define CLRATNO 0x40
-#define CLRSCSIRSTI 0x20
-/* UNUSED 0x10 */
-#define CLRBUSFREE 0x08
-#define CLRSCSIPERR 0x04
-#define CLRPHASECHG 0x02
-#define CLRREQINIT 0x01
+#define REJBYTE 0x4b
-/*
- * SCSI Status 1 (p. 3-24)
- */
-#define SSTAT1 0x00c
-#define SELTO 0x80
-#define ATNTARG 0x40
-#define SCSIRSTI 0x20
-#define PHASEMIS 0x10
-#define BUSFREE 0x08
-#define SCSIPERR 0x04
-#define PHASECHG 0x02
-#define REQINIT 0x01
+#define LASTPHASE 0x4c
+#define P_MESGIN 0xe0
+#define PHASE_MASK 0xe0
+#define P_STATUS 0xc0
+#define P_MESGOUT 0xa0
+#define P_COMMAND 0x80
+#define CDI 0x80
+#define IOI 0x40
+#define P_DATAIN 0x40
+#define MSGI 0x20
+#define P_BUSFREE 0x01
+#define P_DATAOUT 0x00
-/*
- * SCSI Interrupt Mode 1 (pp. 3-28,29)
- * Setting any bit will enable the corresponding function
- * in SIMODE1 to interrupt via the IRQ pin.
- */
-#define SIMODE1 0x011
-#define ENSELTIMO 0x80
-#define ENATNTARG 0x40
-#define ENSCSIRST 0x20
-#define ENPHASEMIS 0x10
-#define ENBUSFREE 0x08
-#define ENSCSIPERR 0x04
-#define ENPHASECHG 0x02
-#define ENREQINIT 0x01
+#define MSGIN_EXT_LEN 0x4d
-/*
- * SCSI Data Bus (High) (p. 3-29)
- * This register reads data on the SCSI Data bus directly.
- */
-#define SCSIBUSL 0x012
-#define SCSIBUSH 0x013
+#define MSGIN_EXT_OPCODE 0x4e
-/*
- * SCSI/Host Address (p. 3-30)
- * These registers hold the host address for the byte about to be
- * transfered on the SCSI bus. They are counted up in the same
- * manner as STCNT is counted down. SHADDR should always be used
- * to determine the address of the last byte transfered since HADDR
- * can be skewed by write ahead.
- */
-#define SHADDR 0x014
-#define SHADDR0 0x014
-#define SHADDR1 0x015
-#define SHADDR2 0x016
-#define SHADDR3 0x017
+#define MSGIN_EXT_BYTES 0x4f
-/*
- * Selection/Reselection ID (p. 3-31)
- * Upper four bits are the device id. The ONEBIT is set when the re/selecting
- * device did not set its own ID.
- */
-#define SELID 0x019
-#define SELID_MASK 0xf0
-#define ONEBIT 0x08
-/* UNUSED 0x07 */
+#define DISCONNECTED_SCBH 0x52
-/*
- * SCSI Block Control (p. 3-32)
- * Controls Bus type and channel selection. In a twin channel configuration
- * addresses 0x00-0x1e are gated to the appropriate channel based on this
- * register. SELWIDE allows for the coexistence of 8bit and 16bit devices
- * on a wide bus.
- */
-#define SBLKCTL 0x01f
-#define DIAGLEDEN 0x80 /* Aic78X0 only */
-#define DIAGLEDON 0x40 /* Aic78X0 only */
-#define AUTOFLUSHDIS 0x20
-/* UNUSED 0x10 */
-#define SELBUS_MASK 0x0a
-#define SELBUSB 0x08
-/* UNUSED 0x04 */
-#define SELWIDE 0x02
-/* UNUSED 0x01 */
-#define SELNARROW 0x00
+#define FREE_SCBH 0x53
-/*
- * Sequencer Control (p. 3-33)
- * Error detection mode and speed configuration
- */
-#define SEQCTL 0x060
-#define PERRORDIS 0x80
-#define PAUSEDIS 0x40
-#define FAILDIS 0x20
-#define FASTMODE 0x10
-#define BRKADRINTEN 0x08
-#define STEP 0x04
-#define SEQRESET 0x02
-#define LOADRAM 0x01
+#define HSCB_ADDR 0x54
-/*
- * Sequencer RAM Data (p. 3-34)
- * Single byte window into the Scratch Ram area starting at the address
- * specified by SEQADDR0 and SEQADDR1. To write a full word, simply write
- * four bytes in succession. The SEQADDRs will increment after the most
- * significant byte is written
- */
-#define SEQRAM 0x061
+#define CUR_SCBID 0x58
-/*
- * Sequencer Address Registers (p. 3-35)
- * Only the first bit of SEQADDR1 holds addressing information
- */
-#define SEQADDR0 0x062
-#define SEQADDR1 0x063
-#define SEQADDR1_MASK 0x01
+#define ARG_1 0x59
+#define RETURN_1 0x59
+#define SEND_MSG 0x80
+#define SEND_SENSE 0x40
+#define SEND_REJ 0x20
-/*
- * Accumulator
- * We cheat by passing arguments in the Accumulator up to the kernel driver
- */
-#define ACCUM 0x064
-
-#define SINDEX 0x065
-#define DINDEX 0x066
-#define ALLZEROS 0x06a
-#define NONE 0x06a
-#define SINDIR 0x06c
-#define DINDIR 0x06d
-#define FUNCTION1 0x06e
+#define SCSICONF 0x5a
+#define RESET_SCSI 0x40
-/*
- * Host Address (p. 3-48)
- * This register contains the address of the byte about
- * to be transfered across the host bus.
- */
-#define HADDR 0x088
-#define HADDR0 0x088
-#define HADDR1 0x089
-#define HADDR2 0x08a
-#define HADDR3 0x08b
-
-#define HCNT 0x08c
-#define HCNT0 0x08c
-#define HCNT1 0x08d
-#define HCNT2 0x08e
-/*
- * SCB Pointer (p. 3-49)
- * Gate one of the four SCBs into the SCBARRAY window.
- */
-#define SCBPTR 0x090
+#define HOSTCONF 0x5d
-/*
- * Board Control (p. 3-43)
- */
-#define BCTL 0x084
-/* RSVD 0xf0 */
-#define ACE 0x08 /* Support for external processors */
-/* RSVD 0x06 */
-#define ENABLE 0x01
+#define HA_274_BIOSCTRL 0x5f
+#define BIOSMODE 0x30
+#define BIOSDISABLED 0x30
+#define CHANNEL_B_PRIMARY 0x08
-/*
- * On the aic78X0 chips, Board Control is replaced by the DSCommand
- * register (p. 4-64)
- */
-#define DSCOMMAND 0x084
-#define CACHETHEN 0x80 /* Cache Threshold enable */
-#define DPARCKEN 0x40 /* Data Parity Check Enable */
-#define MPARCKEN 0x20 /* Memory Parity Check Enable */
-#define EXTREQLCK 0x10 /* External Request Lock */
+#define SEQCTL 0x60
+#define PERRORDIS 0x80
+#define PAUSEDIS 0x40
+#define FAILDIS 0x20
+#define FASTMODE 0x10
+#define BRKADRINTEN 0x08
+#define STEP 0x04
+#define SEQRESET 0x02
+#define LOADRAM 0x01
-/*
- * Bus On/Off Time (p. 3-44)
- */
-#define BUSTIME 0x085
-#define BOFF 0xf0
-#define BON 0x0f
+#define SEQRAM 0x61
-/*
- * Bus Speed (p. 3-45)
- */
-#define BUSSPD 0x086
-#define DFTHRSH 0xc0
-#define STBOFF 0x38
-#define STBON 0x07
-#define DFTHRSH_100 0xc0
+#define SEQADDR0 0x62
-/*
- * Host Control (p. 3-47) R/W
- * Overall host control of the device.
- */
-#define HCNTRL 0x087
-/* UNUSED 0x80 */
-#define POWRDN 0x40
-/* UNUSED 0x20 */
-#define SWINT 0x10
-#define IRQMS 0x08
-#define PAUSE 0x04
-#define INTEN 0x02
-#define CHIPRST 0x01
-#define CHIPRSTACK 0x01
+#define SEQADDR1 0x63
+#define SEQADDR1_MASK 0x01
-/*
- * Interrupt Status (p. 3-50)
- * Status for system interrupts
- */
-#define INTSTAT 0x091
-#define SEQINT_MASK 0xf1 /* SEQINT Status Codes */
-#define BAD_PHASE 0x01 /* unknown scsi bus phase */
-#define SEND_REJECT 0x11 /* sending a message reject */
-#define NO_IDENT 0x21 /* no IDENTIFY after reconnect*/
-#define NO_MATCH 0x31 /* no cmd match for reconnect */
-#define SDTR_MSG 0x41 /* SDTR message received */
-#define WDTR_MSG 0x51 /* WDTR message received */
-#define REJECT_MSG 0x61 /* Reject message received */
-#define BAD_STATUS 0x71 /* Bad status from target */
-#define RESIDUAL 0x81 /* Residual byte count != 0 */
-#define ABORT_TAG 0x91 /* Sent an ABORT_TAG message */
-#define AWAITING_MSG 0xa1 /*
- * Kernel requested to specify
- * a message to this target
- * (command was null), so tell
- * it that it can fill the
- * message buffer.
- */
-#define IMMEDDONE 0xb1 /*
- * An immediate command has
- * completed
- */
-#define MSG_BUFFER_BUSY 0xc1 /*
- * Sequencer wants to use the
- * message buffer, but it
- * already contains a message
- */
-#define MSGIN_PHASEMIS 0xd1 /*
- * Target changed phase on us
- * when we were expecting
- * another msgin byte.
- */
-#define DATA_OVERRUN 0xe1 /*
- * Target attempted to write
- * beyond the bounds of its
- * command.
- */
-#define BRKADRINT 0x08
-#define SCSIINT 0x04
-#define CMDCMPLT 0x02
-#define SEQINT 0x01
-#define INT_PEND (BRKADRINT | SEQINT | SCSIINT | CMDCMPLT)
+#define ACCUM 0x64
-/*
- * Hard Error (p. 3-53)
- * Reporting of catastrophic errors. You usually cannot recover from
- * these without a full board reset.
- */
-#define ERROR 0x092
-/* UNUSED 0xf0 */
-#define PARERR 0x08
-#define ILLOPCODE 0x04
-#define ILLSADDR 0x02
-#define ILLHADDR 0x01
+#define SINDEX 0x65
-/*
- * Clear Interrupt Status (p. 3-52)
- */
-#define CLRINT 0x092
-#define CLRBRKADRINT 0x08
-#define CLRSCSIINT 0x04
-#define CLRCMDINT 0x02
-#define CLRSEQINT 0x01
-
-#define DFCNTRL 0x093
-#define WIDEODD 0x40
-#define SCSIEN 0x20
-#define SDMAEN 0x10
-#define SDMAENACK 0x10
-#define HDMAEN 0x08
-#define HDMAENACK 0x08
-#define DIRECTION 0x04
-#define FIFOFLUSH 0x02
-#define FIFORESET 0x01
-
-#define DFSTATUS 0x094
-#define HDONE 0x08
-#define FIFOEMP 0x01
-
-#define DFDAT 0x099
+#define DINDEX 0x66
-/*
- * SCB Auto Increment (p. 3-59)
- * Byte offset into the SCB Array and an optional bit to allow auto
- * incrementing of the address during download and upload operations
- */
-#define SCBCNT 0x09a
-#define SCBAUTO 0x80
-#define SCBCNT_MASK 0x1f
+#define ALLONES 0x69
-/*
- * Queue In FIFO (p. 3-60)
- * Input queue for queued SCBs (commands that the sequencer has yet to start)
- */
-#define QINFIFO 0x09b
+#define ALLZEROS 0x6a
-/*
- * Queue In Count (p. 3-60)
- * Number of queued SCBs
- */
-#define QINCNT 0x09c
+#define NONE 0x6a
-/*
- * Queue Out FIFO (p. 3-61)
- * Queue of SCBs that have completed and await the host
- */
-#define QOUTFIFO 0x09d
+#define FLAGS 0x6b
+#define ZERO 0x02
+#define CARRY 0x01
-/*
- * Queue Out Count (p. 3-61)
- * Number of queued SCBs in the Out FIFO
- */
-#define QOUTCNT 0x09e
+#define SINDIR 0x6c
-/*
- * SCB Definition (p. 5-4)
- * The two reserved bytes at SCBARRAY+1[23] are expected to be set to
- * zero. Bit 3 in SCBARRAY+0 is used as an internal flag to indicate
- * whether or not to DMA an SCB from host ram. This flag prevents the
- * "re-fetching" of transactions that are requeued because the target is
- * busy with another command. We also use bits 6 & 7 to indicate whether
- * or not to initiate SDTR or WDTR respectively when starting this command.
- */
-#define SCBARRAY 0x0a0
-#define SCB_CONTROL 0x0a0
-#define NEEDWDTR 0x80
-#define DISCENB 0x40
-#define TAG_ENB 0x20
-#define NEEDSDTR 0x10
-#define DISCONNECTED 0x04
-#define SCB_TAG_TYPE 0x03
-#define SCB_TCL 0x0a1
-#define SCB_TARGET_STATUS 0x0a2
-#define SCB_SGCOUNT 0x0a3
-#define SCB_SGPTR 0x0a4
-#define SCB_SGPTR0 0x0a4
-#define SCB_SGPTR1 0x0a5
-#define SCB_SGPTR2 0x0a6
-#define SCB_SGPTR3 0x0a7
-#define SCB_RESID_SGCNT 0x0a8
-#define SCB_RESID_DCNT 0x0a9
-#define SCB_RESID_DCNT0 0x0a9
-#define SCB_RESID_DCNT1 0x0aa
-#define SCB_RESID_DCNT2 0x0ab
-#define SCB_DATAPTR 0x0ac
-#define SCB_DATAPTR0 0x0ac
-#define SCB_DATAPTR1 0x0ad
-#define SCB_DATAPTR2 0x0ae
-#define SCB_DATAPTR3 0x0af
-#define SCB_DATACNT 0x0b0
-#define SCB_DATACNT0 0x0b0
-#define SCB_DATACNT1 0x0b1
-#define SCB_DATACNT2 0x0b2
-/* UNUSED - QUAD PADDING 0x0b3 */
-#define SCB_CMDPTR 0x0b4
-#define SCB_CMDPTR0 0x0b4
-#define SCB_CMDPTR1 0x0b5
-#define SCB_CMDPTR2 0x0b6
-#define SCB_CMDPTR3 0x0b7
-#define SCB_CMDLEN 0x0b8
-#define SCB_TAG 0x0b9
-#define SCB_NEXT 0x0ba
-#define SCB_PREV 0x0bb
-
-#define SG_SIZEOF 0x08 /* sizeof(struct ahc_dma) */
-
-/* --------------------- AHA-2840-only definitions -------------------- */
-
-#define SEECTL_2840 0x0c0
-/* UNUSED 0xf8 */
-#define CS_2840 0x04
-#define CK_2840 0x02
-#define DO_2840 0x01
-
-#define STATUS_2840 0x0c1
-#define EEPROM_TF 0x80
-#define BIOS_SEL 0x60
-#define ADSEL 0x1e
-#define DI_2840 0x01
-
-/* --------------------- AIC-7870-only definitions -------------------- */
-
-#define DSPCISTATUS 0x086
+#define DINDIR 0x6d
-/*
- * Serial EEPROM Control (p. 4-92 in 7870 Databook)
- * Controls the reading and writing of an external serial 1-bit
- * EEPROM Device. In order to access the serial EEPROM, you must
- * first set the SEEMS bit that generates a request to the memory
- * port for access to the serial EEPROM device. When the memory
- * port is not busy servicing another request, it reconfigures
- * to allow access to the serial EEPROM. When this happens, SEERDY
- * gets set high to verify that the memory port access has been
- * granted.
- *
- * After successful arbitration for the memory port, the SEECS bit of
- * the SEECTL register is connected to the chip select. The SEECK,
- * SEEDO, and SEEDI are connected to the clock, data out, and data in
- * lines respectively. The SEERDY bit of SEECTL is useful in that it
- * gives us an 800 nsec timer. After a write to the SEECTL register,
- * the SEERDY goes high 800 nsec later. The one exception to this is
- * when we first request access to the memory port. The SEERDY goes
- * high to signify that access has been granted and, for this case, has
- * no implied timing.
- *
- * See 93cx6.c for detailed information on the protocol necessary to
- * read the serial EEPROM.
- */
-#define SEECTL 0x01e
-#define EXTARBACK 0x80
-#define EXTARBREQ 0x40
-#define SEEMS 0x20
-#define SEERDY 0x10
-#define SEECS 0x08
-#define SEECK 0x04
-#define SEEDO 0x02
-#define SEEDI 0x01
-
-/* ---------------------- Scratch RAM Offsets ------------------------- */
-/* These offsets are either to values that are initialized by the board's
- * BIOS or are specified by the sequencer code.
- *
- * The host adapter card (at least the BIOS) uses 20-2f for SCSI
- * device information, 32-33 and 5a-5f as well. As it turns out, the
- * BIOS trashes 20-2f, writing the synchronous negotiation results
- * on top of the BIOS values, so we re-use those for our per-target
- * scratchspace (actually a value that can be copied directly into
- * SCSIRATE). The kernel driver will enable synchronous negotiation
- * for all targets that have a value other than 0 in the lower four
- * bits of the target scratch space. This should work regardless of
- * whether the bios has been installed.
- */
+#define FUNCTION1 0x6e
-/*
- * 1 byte per target starting at this address for configuration values
- */
-#define TARG_SCRATCH 0x020
+#define STACK 0x6f
-/*
- * The sequencer will stick the frist byte of any rejected message here so
- * we can see what is getting thrown away. Extended messages put the
- * extended message type in REJBYTE_EXT.
- */
-#define REJBYTE 0x030
-#define REJBYTE_EXT 0x031
+#define BCTL 0x84
+#define ACE 0x08
+#define ENABLE 0x01
-/*
- * Bit vector of targets that have disconnection disabled.
- */
-#define DISC_DSB 0x032
-#define DISC_DSB_A 0x032
-#define DISC_DSB_B 0x033
+#define DSCOMMAND 0x84
+#define CACHETHEN 0x80
+#define DPARCKEN 0x40
+#define MPARCKEN 0x20
+#define EXTREQLCK 0x10
-/*
- * Length of pending message
- */
-#define MSG_LEN 0x034
-
-/* We reserve 8bytes to store outgoing messages */
-#define MSG0 0x035
-#define COMP_MSG0 0xcb /* 2's complement of MSG0 */
-#define MSG1 0x036
-#define MSG2 0x037
-#define MSG3 0x038
-#define MSG4 0x039
-#define MSG5 0x03a
-#define MSG6 0x03b
-#define MSG7 0x03c
+#define BUSTIME 0x85
+#define BOFF 0xf0
+#define BON 0x0f
-/*
- * These are offsets into the card's scratch ram. Some of the values are
- * specified in the AHA2742 technical reference manual and are initialized
- * by the BIOS at boot time.
- */
-#define LASTPHASE 0x03d
-#define ARG_1 0x03e
-#define MAXOFFSET 0x01
-#define RETURN_1 0x03f
-#define SEND_WDTR 0x80
-#define SEND_SDTR 0x60
-#define SEND_SENSE 0x40
-#define SEND_REJ 0x20
-#define SCB_PAGEDIN 0x10
-
-#define SIGSTATE 0x040
-
-#define DMAPARAMS 0x041 /* Parameters for DMA Logic */
-
-#define SG_COUNT 0x042
-#define SG_NEXT 0x043 /* working value of SG pointer */
-#define SG_NEXT0 0x043
-#define SG_NEXT1 0x044
-#define SG_NEXT2 0x045
-#define SG_NEXT3 0x046
-
-#define SCBCOUNT 0x047 /*
- * Number of SCBs supported by
- * this card.
- */
-#define COMP_SCBCOUNT 0x048 /*
- * Two's compliment of SCBCOUNT
- */
-#define QCNTMASK 0x049 /*
- * Mask of bits to test against
- * when looking at the Queue Count
- * registers. Works around a bug
- * on aic7850 chips.
- */
-#define FLAGS 0x04a
-#define SINGLE_BUS 0x00
-#define TWIN_BUS 0x01
-#define WIDE_BUS 0x02
-#define PAGESCBS 0x04
-#define DPHASE 0x10
-#define SELECTED 0x20
-#define IDENTIFY_SEEN 0x40
-#define RESELECTED 0x80
-
-#define SAVED_TCL 0x04b /*
- * Temporary storage for the
- * target/channel/lun of a
- * reconnecting target
- */
-#define ACTIVE_A 0x04c
-#define ACTIVE_B 0x04d
-#define WAITING_SCBH 0x04e /*
- * head of list of SCBs awaiting
- * selection
- */
-#define DISCONNECTED_SCBH 0x04f /*
- * head of list of SCBs that are
- * disconnected. Used for SCB
- * paging.
- */
-#define SCB_LIST_NULL 0xff
-
-#define SAVED_LINKPTR 0x050
-#define SAVED_SCBPTR 0x051
-#define ULTRA_ENB 0x052
-#define ULTRA_ENB_B 0x053
-
-#define SCSICONF 0x05a
-#define RESET_SCSI 0x40
-
-#define HOSTCONF 0x05d
-
-#define HA_274_BIOSCTRL 0x05f
-#define BIOSMODE 0x30
-#define BIOSDISABLED 0x30
-#define CHANNEL_B_PRIMARY 0x08
-
-/* Message codes */
-#define MSG_EXTENDED 0x01
-#define MSG_SDTR 0x01
-#define MSG_WDTR 0x03
-#define MSG_SDPTRS 0x02
-#define MSG_RDPTRS 0x03
-#define MSG_DISCONNECT 0x04
-#define MSG_INITIATOR_DET_ERROR 0x05
-#define MSG_ABORT 0x06
-#define MSG_REJECT 0x07
-#define MSG_NOP 0x08
-#define MSG_MSG_PARITY_ERROR 0x09
-#define MSG_BUS_DEVICE_RESET 0x0c
-#define MSG_ABORT_TAG 0x0d
-#define MSG_SIMPLE_TAG 0x20
-#define MSG_IDENTIFY 0x80
-
-/* WDTR Message values */
-#define BUS_8_BIT 0x00
-#define BUS_16_BIT 0x01
-#define BUS_32_BIT 0x02
-
-#define MAX_OFFSET_8BIT 0x0f
-#define MAX_OFFSET_16BIT 0x08
+#define BUSSPD 0x86
+#define DFTHRSH_100 0xc0
+#define DFTHRSH 0xc0
+#define STBOFF 0x38
+#define STBON 0x07
+
+#define DSPCISTATUS 0x86
+
+#define HCNTRL 0x87
+#define POWRDN 0x40
+#define SWINT 0x10
+#define IRQMS 0x08
+#define PAUSE 0x04
+#define INTEN 0x02
+#define CHIPRST 0x01
+#define CHIPRSTACK 0x01
+
+#define HADDR 0x88
+
+#define HCNT 0x8c
+
+#define SCBPTR 0x90
+
+#define INTSTAT 0x91
+#define SEQINT_MASK 0xf1
+#define DATA_OVERRUN 0xe1
+#define MSGIN_PHASEMIS 0xd1
+#define MSG_BUFFER_BUSY 0xc1
+#define AWAITING_MSG 0xa1
+#define ABORT_CMDCMPLT 0x91
+#define RESIDUAL 0x81
+#define BAD_STATUS 0x71
+#define REJECT_MSG 0x61
+#define NO_MATCH_BUSY 0x51
+#define EXTENDED_MSG 0x41
+#define NO_MATCH 0x31
+#define NO_IDENT 0x21
+#define SEND_REJECT 0x11
+#define INT_PEND 0x0f
+#define BRKADRINT 0x08
+#define SCSIINT 0x04
+#define CMDCMPLT 0x02
+#define BAD_PHASE 0x01
+#define SEQINT 0x01
+
+#define CLRINT 0x92
+#define CLRBRKADRINT 0x08
+#define CLRSCSIINT 0x04
+#define CLRCMDINT 0x02
+#define CLRSEQINT 0x01
+
+#define ERROR 0x92
+#define PARERR 0x08
+#define ILLOPCODE 0x04
+#define ILLSADDR 0x02
+#define ILLHADDR 0x01
+
+#define DFCNTRL 0x93
+
+#define DFSTATUS 0x94
+#define DWORDEMP 0x20
+#define MREQPEND 0x10
+#define HDONE 0x08
+#define DFTHRESH 0x04
+#define FIFOFULL 0x02
+#define FIFOEMP 0x01
+
+#define DFDAT 0x99
+
+#define SCBCNT 0x9a
+#define SCBAUTO 0x80
+#define SCBCNT_MASK 0x1f
+
+#define QINFIFO 0x9b
+
+#define QINCNT 0x9c
+
+#define QOUTFIFO 0x9d
+
+#define QOUTCNT 0x9e
+
+#define SCB_CONTROL 0xa0
+#define MK_MESSAGE 0x80
+#define DISCENB 0x40
+#define TAG_ENB 0x20
+#define MUST_DMAUP_SCB 0x10
+#define ABORT_SCB 0x08
+#define DISCONNECTED 0x04
+#define SCB_TAG_TYPE 0x03
+
+#define SCB_BASE 0xa0
+
+#define SCB_TCL 0xa1
+#define TID 0xf0
+#define SELBUSB 0x08
+#define LID 0x07
+
+#define SCB_TARGET_STATUS 0xa2
+
+#define SCB_SGCOUNT 0xa3
+
+#define SCB_SGPTR 0xa4
+
+#define SCB_RESID_SGCNT 0xa8
+
+#define SCB_RESID_DCNT 0xa9
+
+#define SCB_DATAPTR 0xac
+
+#define SCB_DATACNT 0xb0
+
+#define SCB_LINKED_NEXT 0xb3
+
+#define SCB_CMDPTR 0xb4
+
+#define SCB_CMDLEN 0xb8
+
+#define SCB_TAG 0xb9
+
+#define SCB_NEXT 0xba
+
+#define SCB_PREV 0xbb
+
+#define SCB_BUSYTARGETS 0xbc
+
+#define SEECTL_2840 0xc0
+#define CS_2840 0x04
+#define CK_2840 0x02
+#define DO_2840 0x01
+
+#define STATUS_2840 0xc1
+#define EEPROM_TF 0x80
+#define BIOS_SEL 0x60
+#define ADSEL 0x1e
+#define DI_2840 0x01
+
+
+#define BUS_8_BIT 0x00
+#define MAX_OFFSET_8BIT 0x0f
+#define BUS_16_BIT 0x01
+#define MAX_OFFSET_16BIT 0x08
+#define SCB_LIST_NULL 0xff
+#define SG_SIZEOF 0x08
+#define BUS_32_BIT 0x02
diff --git a/drivers/scsi/atari_scsi.c b/drivers/scsi/atari_scsi.c
index 15dfb01f1..50ce159a2 100644
--- a/drivers/scsi/atari_scsi.c
+++ b/drivers/scsi/atari_scsi.c
@@ -90,6 +90,7 @@
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/init.h>
+#include <linux/nvram.h>
#include <asm/setup.h>
#include <asm/atarihw.h>
@@ -596,20 +597,6 @@ int atari_queue_command (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
#endif
-#define RTC_READ(reg) \
- ({ unsigned char __val; \
- outb(reg,&tt_rtc.regsel); \
- __val = tt_rtc.data; \
- __val; \
- })
-
-#define RTC_WRITE(reg,val) \
- do { \
- outb(reg,&tt_rtc.regsel); \
- tt_rtc.data = (val); \
- } while(0)
-
-
int atari_scsi_detect (Scsi_Host_Template *host)
{
static int called = 0;
@@ -645,20 +632,11 @@ int atari_scsi_detect (Scsi_Host_Template *host)
/* use 7 as default */
host->this_id = 7;
/* Test if a host id is set in the NVRam */
- if (ATARIHW_PRESENT(TT_CLK)) {
- unsigned char sum = 0, b;
- int i;
-
- /* Make checksum */
- for( i = 14; i < 62; ++i )
- sum += RTC_READ(i);
-
- if (/* NV-Ram checksum valid? */
- RTC_READ(62) == sum && RTC_READ(63) == ~sum &&
- /* Arbitration enabled? (for TOS) */
- (b = RTC_READ( 30 )) & 0x80) {
+ if (ATARIHW_PRESENT(TT_CLK) && nvram_check_checksum()) {
+ unsigned char b = nvram_read_byte( 14 );
+ /* Arbitration enabled? (for TOS) If yes, use configured host ID */
+ if (b & 0x80)
host->this_id = b & 7;
- }
}
}
diff --git a/drivers/scsi/eata.h b/drivers/scsi/eata.h
index cba391414..e69de29bb 100644
--- a/drivers/scsi/eata.h
+++ b/drivers/scsi/eata.h
@@ -1,41 +0,0 @@
-/*
- * eata.h - used by the low-level driver for EATA/DMA SCSI host adapters.
- */
-#ifndef _EATA_H
-#define _EATA_H
-
-#include <scsi/scsicam.h>
-
-int eata2x_detect(Scsi_Host_Template *);
-int eata2x_release(struct Scsi_Host *);
-int eata2x_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
-int eata2x_abort(Scsi_Cmnd *);
-int eata2x_reset(Scsi_Cmnd *, unsigned int);
-
-#define EATA_VERSION "3.10.00"
-
-
-#define EATA { \
- NULL, /* Ptr for modules */ \
- NULL, /* usage count for modules */ \
- NULL, \
- NULL, \
- "EATA/DMA 2.0x rev. " EATA_VERSION " ", \
- eata2x_detect, \
- eata2x_release, \
- NULL, \
- NULL, \
- eata2x_queuecommand, \
- eata2x_abort, \
- eata2x_reset, \
- NULL, \
- scsicam_bios_param, \
- 0, /* can_queue, reset by detect */ \
- 7, /* this_id, reset by detect */ \
- 0, /* sg_tablesize, reset by detect */ \
- 0, /* cmd_per_lun, reset by detect */ \
- 0, /* number of boards present */ \
- 1, /* unchecked isa dma, reset by detect */ \
- ENABLE_CLUSTERING \
- }
-#endif
diff --git a/drivers/scsi/esp.c b/drivers/scsi/esp.c
index 0380ff99a..b479cc9fc 100644
--- a/drivers/scsi/esp.c
+++ b/drivers/scsi/esp.c
@@ -1705,7 +1705,7 @@ int esp_abort(Scsi_Cmnd *SCptr)
* the nexus and tell the device to abort. However, we really
* cannot 'reconnect' per se, therefore we tell the upper layer
* the safest thing we can. This is, wait a bit, if nothing
- * happens, we are really hung so reset the bug.
+ * happens, we are really hung so reset the bus.
*/
return SCSI_ABORT_SNOOZE;
@@ -1743,11 +1743,10 @@ static void esp_done(struct Sparc_ESP *esp, int error)
done_SC->request_bufflen,
esp->edev->my_bus);
} else {
- struct scatterlist *scl = (struct scatterlist *)done_SC->buffer;
#ifdef DEBUG_ESP_SG
printk("esp%d: unmapping sg ", esp->esp_id);
#endif
- mmu_release_scsi_sgl((struct mmu_sglist *) scl,
+ mmu_release_scsi_sgl((struct mmu_sglist *) done_SC->buffer,
done_SC->use_sg - 1,
esp->edev->my_bus);
#ifdef DEBUG_ESP_SG
@@ -2010,7 +2009,7 @@ static inline int dma_can_transfer(Scsi_Cmnd *sp, enum dvma_rev drev)
if(sz > 0x1000000)
sz = 0x1000000;
} else {
- base = ((__u32)sp->SCp.ptr);
+ base = ((__u32)((unsigned long)sp->SCp.ptr));
base &= (0x1000000 - 1);
end = (base + sp->SCp.this_residual);
if(end > 0x1000000)
@@ -2280,11 +2279,12 @@ static inline int esp_do_data(struct Sparc_ESP *esp, struct Sparc_ESP_regs *ereg
tmp |= DMA_ST_WRITE;
else
tmp &= ~(DMA_ST_WRITE);
- dregs->st_addr = ((__u32)SCptr->SCp.ptr);
+ dregs->st_addr = ((__u32)((unsigned long)SCptr->SCp.ptr));
dregs->cond_reg = tmp;
} else {
esp_setcount(eregs, hmuch, 0);
- dma_setup(dregs, esp->dma->revision, ((__u32)SCptr->SCp.ptr),
+ dma_setup(dregs, esp->dma->revision,
+ ((__u32)((unsigned long)SCptr->SCp.ptr)),
hmuch, (thisphase == in_datain));
ESPDATA(("DMA|TI --> do_intr_end\n"));
esp_cmd(esp, eregs, ESP_CMD_DMA | ESP_CMD_TI);
@@ -4059,8 +4059,12 @@ repeat:
}
#else
+/* XXX Gross hack for sun4u SMP, fix it right later... -DaveM */
#ifdef __sparc_v9__
-#error Dave you need to fix some things first...
+extern unsigned char ino_to_pil[];
+#define INO_TO_PIL(esp) (ino_to_pil[(esp)->irq])
+#else
+#define INO_TO_PIL(esp) ((esp)->irq & 0xf)
#endif
/* For SMP we only service one ESP on the list list at our IRQ level! */
@@ -4070,7 +4074,7 @@ static void esp_intr(int irq, void *dev_id, struct pt_regs *pregs)
/* Handle all ESP interrupts showing at this IRQ level. */
for_each_esp(esp) {
- if((esp->irq & 0xf) == irq) {
+ if(INO_TO_PIL(esp) == irq) {
if(DMA_IRQ_P(esp->dregs)) {
DMA_INTSOFF(esp->dregs);
diff --git a/drivers/scsi/ppa.c b/drivers/scsi/ppa.c
index e9bdcee9b..c28958703 100644
--- a/drivers/scsi/ppa.c
+++ b/drivers/scsi/ppa.c
@@ -144,7 +144,7 @@ static char *PPA_MODE_STRING[] =
"Unknown"};
typedef struct {
- struct ppd *dev; /* Parport device entry */
+ struct pardevice *dev; /* Parport device entry */
int speed; /* General PPA delay constant */
int speed_fast; /* Const for nibble/byte modes */
int epp_speed; /* Reset time period */
@@ -210,22 +210,22 @@ static int ppa_sg = SG_ALL; /* enable/disable scatter-gather. */
#define w_fifo(x,y) outb(y, PPA_BASE(x)+0x400)
#define w_ecr(x,y) outb(y, PPA_BASE(x)+0x402)
-int ppa_wakeup(void *ref)
+static void ppa_wakeup(void *ref)
{
ppa_struct *ppa_dev = (ppa_struct *) ref;
if (!ppa_dev->ppa_wait_q)
- return 1; /* Wake up whom ? */
+ return; /* Wake up whom ? */
/* Claim the Parport */
if (parport_claim(ppa_dev->dev))
- return 1; /* Shouldn't happen */
+ return; /* Shouldn't happen */
wake_up(&ppa_dev->ppa_wait_q);
- return 0;
+ return;
}
-int ppa_release(struct Scsi_Host *host)
+static int ppa_release(struct Scsi_Host *host)
{
int host_no = host->unique_id;
@@ -1137,7 +1137,7 @@ int ppa_detect(Scsi_Host_Template * host)
int modes = pb->modes;
/* We only understand PC-style ports */
- if (modes & PARPORT_MODE_SPP) {
+ if (modes & PARPORT_MODE_PCSPP) {
/* transfer global values here */
if (ppa_speed >= 0)
@@ -1156,16 +1156,16 @@ int ppa_detect(Scsi_Host_Template * host)
w_ctr(i, 0x0c);
ppa_hosts[i].mode = PPA_NIBBLE;
- if (modes & (PARPORT_MODE_EPP | PARPORT_MODE_ECPEPP)) {
+ if (modes & (PARPORT_MODE_PCEPP | PARPORT_MODE_PCECPEPP)) {
ppa_hosts[i].mode = PPA_EPP_32;
- printk("PPA: Parport [ EPP ]\n");
- } else if (modes & PARPORT_MODE_ECP) {
+ printk("PPA: Parport [ PCEPP ]\n");
+ } else if (modes & PARPORT_MODE_PCECP) {
w_ecr(i, 0x20);
ppa_hosts[i].mode = PPA_PS2;
- printk("PPA: Parport [ ECP in PS2 submode ]\n");
- } else if (modes & PARPORT_MODE_PS2) {
+ printk("PPA: Parport [ PCECP in PS2 submode ]\n");
+ } else if (modes & PARPORT_MODE_PCPS2) {
ppa_hosts[i].mode = PPA_PS2;
- printk("PPA: Parport [ PS2 ]\n");
+ printk("PPA: Parport [ PCPS2 ]\n");
}
/* Done configuration */
ppa_pb_release(i);
diff --git a/drivers/scsi/ppa.h b/drivers/scsi/ppa.h
index 84fa2dec8..cdc2fdaa6 100644
--- a/drivers/scsi/ppa.h
+++ b/drivers/scsi/ppa.h
@@ -52,7 +52,7 @@ int ppa_abort(Scsi_Cmnd *);
int ppa_reset(Scsi_Cmnd *, unsigned int);
int ppa_proc_info(char *, char **, off_t, int, int, int);
int ppa_biosparam(Disk *, kdev_t, int*);
-int ppa_release(struct Scsi_Host *);
+static int ppa_release(struct Scsi_Host *);
#ifndef MODULE
#ifdef PPA_CODE