diff options
Diffstat (limited to 'drivers/scsi/wd33c93.c')
-rw-r--r-- | drivers/scsi/wd33c93.c | 201 |
1 files changed, 125 insertions, 76 deletions
diff --git a/drivers/scsi/wd33c93.c b/drivers/scsi/wd33c93.c index 14cb0f37a..6d60d12ae 100644 --- a/drivers/scsi/wd33c93.c +++ b/drivers/scsi/wd33c93.c @@ -84,8 +84,9 @@ #include "scsi.h" #include "hosts.h" -#define WD33C93_VERSION "1.24" -#define WD33C93_DATE "29/Jan/1997" +#define WD33C93_VERSION "1.25" +#define WD33C93_DATE "09/Jul/1997" +/* NOTE: 1.25 for m68k is related to in2000-1.31 for x86 */ /* * Note - the following defines have been moved to 'wd33c93.h': @@ -103,11 +104,15 @@ -/* setup_strings is an array of strings that define some of the operating - * parameters and settings for this driver. It is used unless an amiboot - * or insmod command line has been specified, in which case those settings - * are combined with the ones here. The driver recognizes the following - * keywords (lower case required) and arguments: +/* + * 'setup_strings' is a single string used to pass operating parameters and + * settings from the kernel/module command-line to the driver. 'setup_args[]' + * is an array of strings that define the compile-time default values for + * these settings. If Linux boots with an amiboot or insmod command-line, + * those settings are combined with 'setup_args[]'. Note that amiboot + * command-lines are prefixed with "wd33c93=" while insmod uses a + * "setup_strings=" prefix. The driver recognizes the following keywords + * (lower case required) and arguments: * * - nosync:bitmask -bitmask is a byte where the 1st 7 bits correspond with * the 7 possible SCSI devices. Set a bit to negotiate for @@ -135,13 +140,11 @@ * _must_ be a colon between a keyword and its numeric argument, with no * spaces. * - Keywords are separated by commas, no spaces, in the standard kernel - * command-line manner, except in the case of 'setup_strings[]' (see - * below), which is simply a C array of pointers to char. Each element - * in the array is a string comprising one keyword & argument. + * command-line manner. * - A keyword in the 'nth' comma-separated command-line member will overwrite - * the 'nth' element of setup_strings[]. A blank command-line member (in + * the 'nth' element of setup_args[]. A blank command-line member (in * other words, a comma with no preceding keyword) will _not_ overwrite - * the corresponding setup_strings[] element. + * the corresponding setup_args[] element. * - If a keyword is used more than once, the first one applies to the first * SCSI host found, the second to the second card, etc, unless the 'next' * keyword is used to change the order. @@ -154,8 +157,16 @@ * - wd33c93=debug:0x1c */ -static char *setup_strings[] = - {"","","","","","","","","","","",""}; +/* Normally, no defaults are specified */ +static char *setup_args[] = + {"","","","","","","","",""}; + +/* filled in by 'insmod' */ +static char *setup_strings = 0; + +#ifdef MODULE_PARM +MODULE_PARM(setup_strings, "s"); +#endif static inline uchar read_wd33c93(wd33c93_regs *regp,uchar reg_num) @@ -319,8 +330,24 @@ int wd33c93_queuecommand (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) cmd->SCp.this_residual = cmd->request_bufflen; } - /* Preset the command status to GOOD, since that's the normal case */ - cmd->SCp.Status = GOOD; + /* WD docs state that at the conclusion of a "LEVEL2" command, the + * status byte can be retrieved from the LUN register. Apparently, + * this is the case only for *uninterrupted* LEVEL2 commands! If + * there are any unexpected phases entered, even if they are 100% + * legal (different devices may choose to do things differently), + * the LEVEL2 command sequence is exited. This often occurs prior + * to receiving the status byte, in which case the driver does a + * status phase interrupt and gets the status byte on its own. + * While such a command can then be "resumed" (ie restarted to + * finish up as a LEVEL2 command), the LUN register will NOT be + * a valid status byte at the command's conclusion, and we must + * use the byte obtained during the earlier interrupt. Here, we + * preset SCp.Status to an illegal value (0xff) so that when + * this command finally completes, we can tell where the actual + * status byte is stored. + */ + + cmd->SCp.Status = ILLEGAL_STATUS_BYTE; /* Add the cmd to the end of 'input_Q'. Note that REQUEST SENSE * commands are added to the head of the queue so that the desired @@ -641,7 +668,8 @@ use_transfer_pio: write_wd33c93(regp, WD_CONTROL, (CTRL_IDI | CTRL_EDI | CTRL_DMA)); /* write_wd33c93_count(regp, cmd->SCp.this_residual); */ - if ((hostdata->level2 >= L2_DATA) || (cmd->SCp.phase == 0)) { + if ((hostdata->level2 >= L2_DATA) || + (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) { write_wd33c93(regp, WD_COMMAND_PHASE, 0x45); write_wd33c93_cmd(regp, WD_CMD_SEL_ATN_XFER); hostdata->state = S_RUNNING_LEVEL2; @@ -808,8 +836,10 @@ void wd33c93_intr (struct Scsi_Host *instance) case CSR_XFER_DONE|PHS_STATUS: case CSR_UNEXP |PHS_STATUS: case CSR_SRV_REQ |PHS_STATUS: - DB(DB_INTR,printk("STATUS")); + DB(DB_INTR,printk("STATUS=")); + cmd->SCp.Status = read_1_byte(regp); + DB(DB_INTR,printk("%02x",cmd->SCp.Status)); if (hostdata->level2 >= L2_BASIC) { /* clear interrupt */ sr = read_wd33c93(regp, WD_SCSI_STATUS); @@ -817,7 +847,6 @@ void wd33c93_intr (struct Scsi_Host *instance) write_wd33c93(regp, WD_COMMAND_PHASE, 0x50); write_wd33c93_cmd(regp, WD_CMD_SEL_ATN_XFER); } else { - DB(DB_INTR, printk("=%02x", cmd->SCp.Status)); hostdata->state = S_CONNECTED; } break; @@ -999,21 +1028,22 @@ void wd33c93_intr (struct Scsi_Host *instance) DB(DB_INTR, printk("SX-DONE-%ld", cmd->pid)); cmd->SCp.Message = COMMAND_COMPLETE; lun = read_wd33c93(regp, WD_TARGET_LUN); - if (cmd->SCp.Status == GOOD) - cmd->SCp.Status = lun; + DB(DB_INTR,printk(":%d.%d",cmd->SCp.Status,lun)); hostdata->connected = NULL; - if (cmd->cmnd[0] != REQUEST_SENSE) - cmd->result = (cmd->SCp.Status | - (cmd->SCp.Message << 8)); - else if (cmd->SCp.Status != GOOD) - cmd->result = ((cmd->result & 0x00ffff) | - (DID_ERROR << 16)); hostdata->busy[cmd->target] &= ~(1 << cmd->lun); hostdata->state = S_UNCONNECTED; + if (cmd->SCp.Status == ILLEGAL_STATUS_BYTE) + cmd->SCp.Status = lun; + if (cmd->cmnd[0] == REQUEST_SENSE + && cmd->SCp.Status != GOOD) + cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16); + else + cmd->result = cmd->SCp.Status | + (cmd->SCp.Message << 8); cmd->scsi_done(cmd); - /* We are no longer connected to a target - check to see if - * there are commands waiting to be executed. + /* We are no longer connected to a target - check to + * see if there are commands waiting to be executed. */ restore_flags(flags); wd33c93_execute(instance); @@ -1081,10 +1111,13 @@ void wd33c93_intr (struct Scsi_Host *instance) hostdata->connected = NULL; hostdata->busy[cmd->target] &= ~(1 << cmd->lun); hostdata->state = S_UNCONNECTED; - if (cmd->cmnd[0] != REQUEST_SENSE) - cmd->result = (cmd->SCp.Status | (cmd->SCp.Message << 8)); - else if (cmd->SCp.Status != GOOD) - cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16); + if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD) + cmd->result = (cmd->result & 0x00ffff) | + (DID_ERROR << 16); + else + cmd->result = cmd->SCp.Status | + (cmd->SCp.Message << 8); + cmd->scsi_done(cmd); /* We are no longer connected to a target - check to see if @@ -1110,12 +1143,14 @@ void wd33c93_intr (struct Scsi_Host *instance) hostdata->connected = NULL; hostdata->busy[cmd->target] &= ~(1 << cmd->lun); hostdata->state = S_UNCONNECTED; - if (cmd->cmnd[0] != REQUEST_SENSE) - cmd->result = (cmd->SCp.Status | - (cmd->SCp.Message << 8)); - else if (cmd->SCp.Status != GOOD) - cmd->result = ((cmd->result & 0x00ffff) | - (DID_ERROR << 16)); + DB(DB_INTR,printk(":%d",cmd->SCp.Status)) + if (cmd->cmnd[0] == REQUEST_SENSE && + cmd->SCp.Status != GOOD) + cmd->result = (cmd->result & 0x00ffff) | + (DID_ERROR << 16); + else + cmd->result = cmd->SCp.Status | + (cmd->SCp.Message << 8); cmd->scsi_done(cmd); restore_flags(flags); break; @@ -1455,10 +1490,11 @@ int wd33c93_abort (Scsi_Cmnd *cmd) } #define MAX_WD33C93_HOSTS 4 -#define MAX_SETUP_STRINGS (sizeof(setup_strings) / sizeof(char *)) +#define MAX_SETUP_ARGS (sizeof(setup_args) / sizeof(char *)) #define SETUP_BUFFER_SIZE 200 static char setup_buffer[SETUP_BUFFER_SIZE]; -static char setup_used[MAX_SETUP_STRINGS]; +static char setup_used[MAX_SETUP_ARGS]; +static int done_setup = 0; void wd33c93_setup (char *str, int *ints) { @@ -1486,41 +1522,42 @@ void wd33c93_setup (char *str, int *ints) setup_buffer[SETUP_BUFFER_SIZE - 1] = '\0'; p1 = setup_buffer; i = 0; - while (*p1 && (i < MAX_SETUP_STRINGS)) { + while (*p1 && (i < MAX_SETUP_ARGS)) { p2 = strchr(p1, ','); if (p2) { *p2 = '\0'; if (p1 != p2) - setup_strings[i] = p1; + setup_args[i] = p1; p1 = p2 + 1; i++; } else { - setup_strings[i] = p1; + setup_args[i] = p1; break; } } - for (i = 0; i < MAX_SETUP_STRINGS; i++) + for (i = 0; i < MAX_SETUP_ARGS; i++) setup_used[i] = 0; + done_setup = 1; } -/* check_setup_strings() returns index if key found, 0 if not */ -static int check_setup_strings(char *key, int *flags, int *val, char *buf) +/* check_setup_args() returns index if key found, 0 if not */ +static int check_setup_args(char *key, int *flags, int *val, char *buf) { int x; char *cp; - for (x = 0; x < MAX_SETUP_STRINGS; x++) { + for (x = 0; x < MAX_SETUP_ARGS; x++) { if (setup_used[x]) continue; - if (!strncmp(setup_strings[x], key, strlen(key))) + if (!strncmp(setup_args[x], key, strlen(key))) break; - if (!strncmp(setup_strings[x], "next", strlen("next"))) + if (!strncmp(setup_args[x], "next", strlen("next"))) return 0; } - if (x == MAX_SETUP_STRINGS) + if (x == MAX_SETUP_ARGS) return 0; setup_used[x] = 1; - cp = setup_strings[x] + strlen(key); + cp = setup_args[x] + strlen(key); *val = -1; if (*cp != ':') return ++x; @@ -1535,12 +1572,16 @@ void wd33c93_init (struct Scsi_Host *instance, wd33c93_regs *regs, dma_setup_t setup, dma_stop_t stop, int clock_freq) { static int shown = 0; - struct WD33C93_hostdata *hostdata = INSTHOSTDATA(instance); + struct WD33C93_hostdata *hostdata; int i; int flags; int val; char buf[32]; + if (!done_setup && setup_strings) + wd33c93_setup(setup_strings,0); + hostdata = INSTHOSTDATA(instance); + hostdata->regp = regs; hostdata->clock_freq = clock_freq; hostdata->dma_setup = setup; @@ -1580,27 +1621,30 @@ void wd33c93_init (struct Scsi_Host *instance, wd33c93_regs *regs, #endif - if (check_setup_strings("nosync", &flags, &val, buf)) + if (check_setup_args("nosync",&flags,&val,buf)) hostdata->no_sync = val; - if (check_setup_strings("nodma",&flags,&val,buf)) + if (check_setup_args("nodma",&flags,&val,buf)) hostdata->no_dma = (val == -1) ? 1 : val; - if (check_setup_strings("period", &flags, &val, buf)) + if (check_setup_args("period",&flags,&val,buf)) hostdata->default_sx_per = sx_table[round_period((unsigned int)val)].period_ns; - if (check_setup_strings("disconnect", &flags, &val, buf)) { + if (check_setup_args("disconnect",&flags,&val,buf)) { if ((val >= DIS_NEVER) && (val <= DIS_ALWAYS)) hostdata->disconnect = val; else hostdata->disconnect = DIS_ADAPTIVE; } - if (check_setup_strings("debug", &flags, &val, buf)) + if (check_setup_args("level2",&flags,&val,buf)) + hostdata->level2 = val; + + if (check_setup_args("debug",&flags,&val,buf)) hostdata->args = val & DB_MASK; - if (check_setup_strings("clock", &flags, &val, buf)) { + if (check_setup_args("clock",&flags,&val,buf)) { if ((val > 7) && (val < 11)) val = WD33C93_FS_8_10; else if ((val > 11) && (val < 16)) @@ -1612,13 +1656,13 @@ void wd33c93_init (struct Scsi_Host *instance, wd33c93_regs *regs, hostdata->clock_freq = val; } - if ((i = check_setup_strings("next", &flags, &val, buf))) { + if ((i = check_setup_args("next",&flags,&val,buf))) { while (i) setup_used[--i] = 1; } #ifdef PROC_INTERFACE - if (check_setup_strings("proc", &flags, &val, buf)) + if (check_setup_args("proc",&flags,&val,buf)) hostdata->proc = val; #endif @@ -1635,9 +1679,9 @@ void wd33c93_init (struct Scsi_Host *instance, wd33c93_regs *regs, printk(" debugging=OFF\n"); #endif #if 0 - printk("wd33c93-%d: setup_strings=", instance->host_no); - for (i = 0; i < MAX_SETUP_STRINGS; i++) - printk("%s,", setup_strings[i]); + printk("wd33c93-%d: setup_args=", instance->host_no); + for (i = 0; i < MAX_SETUP_ARGS; i++) + printk("%s,", setup_args[i]); printk("\n"); printk("wd33c93-%d: debug_flags = %04x\n", instance->host_no, hostdata->args); @@ -1715,6 +1759,11 @@ int wd33c93_proc_info(char *buf, char **start, off_t off, int len, int hn, int i bp += 6; hd->no_dma = simple_strtoul(bp,NULL,0); } + else if (!strncmp(bp,"level2:",7)) { + bp += 7; + hd->level2 = simple_strtoul(bp,NULL,0); + } + return len; } @@ -1731,32 +1780,32 @@ int wd33c93_proc_info(char *buf, char **start, off_t off, int len, int hn, int i sprintf(tbuf,"\nclock_freq=%02x no_sync=%02x no_dma=%d", hd->clock_freq,hd->no_sync,hd->no_dma); strcat(bp,tbuf); - strcat(bp,"\nsync_xfer[] ="); - for (x=0; x<8; x++) { - sprintf(tbuf," %02x",hd->sync_xfer[x]); + strcat(bp,"\nsync_xfer[] = "); + for (x=0; x<7; x++) { + sprintf(tbuf,"\t%02x",hd->sync_xfer[x]); strcat(bp,tbuf); } - strcat(bp,"\nsync_stat[] ="); - for (x=0; x<8; x++) { - sprintf(tbuf," %02x",hd->sync_stat[x]); + strcat(bp,"\nsync_stat[] = "); + for (x=0; x<7; x++) { + sprintf(tbuf,"\t%02x",hd->sync_stat[x]); strcat(bp,tbuf); } } #ifdef PROC_STATISTICS if (hd->proc & PR_STATISTICS) { strcat(bp,"\ncommands issued: "); - for (x=0; x<8; x++) { - sprintf(tbuf," %ld",hd->cmd_cnt[x]); + for (x=0; x<7; x++) { + sprintf(tbuf,"\t%ld",hd->cmd_cnt[x]); strcat(bp,tbuf); } strcat(bp,"\ndisconnects allowed:"); - for (x=0; x<8; x++) { - sprintf(tbuf," %ld",hd->disc_allowed_cnt[x]); + for (x=0; x<7; x++) { + sprintf(tbuf,"\t%ld",hd->disc_allowed_cnt[x]); strcat(bp,tbuf); } strcat(bp,"\ndisconnects done: "); - for (x=0; x<8; x++) { - sprintf(tbuf," %ld",hd->disc_done_cnt[x]); + for (x=0; x<7; x++) { + sprintf(tbuf,"\t%ld",hd->disc_done_cnt[x]); strcat(bp,tbuf); } sprintf(tbuf, |