diff options
Diffstat (limited to 'drivers/scsi/ncr53c8xx.c')
-rw-r--r-- | drivers/scsi/ncr53c8xx.c | 502 |
1 files changed, 421 insertions, 81 deletions
diff --git a/drivers/scsi/ncr53c8xx.c b/drivers/scsi/ncr53c8xx.c index aecbd4cf0..72f4ce11d 100644 --- a/drivers/scsi/ncr53c8xx.c +++ b/drivers/scsi/ncr53c8xx.c @@ -73,7 +73,7 @@ */ /* -** April 24 2000, version 3.2i +** May 11 2000, version 3.3b ** ** Supported SCSI-II features: ** Synchronous negotiation @@ -93,6 +93,7 @@ ** 53C895 (Wide, Fast 40, on board rom BIOS) ** 53C895A (Wide, Fast 40, on board rom BIOS) ** 53C896 (Wide, Fast 40, on board rom BIOS) +** 53C897 (Wide, Fast 40, on board rom BIOS) ** 53C1510D (Wide, Fast 40, on board rom BIOS) ** ** Other features: @@ -104,7 +105,7 @@ /* ** Name and version of the driver */ -#define SCSI_NCR_DRIVER_NAME "ncr53c8xx - version 3.2i" +#define SCSI_NCR_DRIVER_NAME "ncr53c8xx - version 3.3b" #define SCSI_NCR_DEBUG_FLAGS (0) @@ -187,6 +188,14 @@ typedef u64 u_int64; typedef u_long vm_offset_t; #include "ncr53c8xx.h" +/* +** Donnot compile integrity checking code for Linux-2.3.0 +** and above since SCSI data structures are not ready yet. +*/ +#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,0) +#define SCSI_NCR_INTEGRITY_CHECKING +#endif + #define NAME53C "ncr53c" #define NAME53C8XX "ncr53c8xx" #define DRIVER_SMP_LOCK ncr53c8xx_lock @@ -472,8 +481,10 @@ static u_char Tekram_sync[16] __initdata = **========================================================== */ +#define NS_NOCHANGE (0) #define NS_SYNC (1) #define NS_WIDE (2) +#define NS_PPR (4) /*========================================================== ** @@ -666,6 +677,13 @@ struct tcb { /*2*/ u_char widedone; /*3*/ u_char wval; +#ifdef SCSI_NCR_INTEGRITY_CHECKING + u_char ic_min_sync; + u_char ic_max_width; + u_char ic_maximums_set; + u_char ic_done; +#endif + /*---------------------------------------------------------------- ** User settable limits and options. ** These limits are read from the NVRAM if present. @@ -1204,6 +1222,17 @@ struct ncb { struct ccb *ccb; /* Global CCB */ struct usrcmd user; /* Command from user */ u_char release_stage; /* Synchronisation stage on release */ + +#ifdef SCSI_NCR_INTEGRITY_CHECKING + /*---------------------------------------------------------------- + ** Fields that are used for integrity check + **---------------------------------------------------------------- + */ + unsigned char check_integrity; /* Enable midlayer integ.check on + * bus scan. */ + unsigned char check_integ_par; /* Set if par or Init. Det. error + * used only during integ check */ +#endif }; #define NCB_SCRIPT_PHYS(np,lbl) (np->p_script + offsetof (struct script, lbl)) @@ -1382,6 +1411,10 @@ static void ncr_int_sir (ncb_p np); static void ncr_int_sto (ncb_p np); static u_long ncr_lookup (char* id); static void ncr_negotiate (struct ncb* np, struct tcb* tp); +static int ncr_prepare_nego(ncb_p np, ccb_p cp, u_char *msgptr); +#ifdef SCSI_NCR_INTEGRITY_CHECKING +static int ncr_ic_nego(ncb_p np, ccb_p cp, Scsi_Cmnd *cmd, u_char *msgptr); +#endif #ifdef SCSI_NCR_PROFILE_SUPPORT static void ncb_profile (ncb_p np, ccb_p cp); @@ -1396,6 +1429,7 @@ static void ncr_setsync (ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer); static void ncr_setup_tags (ncb_p np, u_char tn, u_char ln); static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide, u_char ack); static int ncr_show_msg (u_char * msg); +static void ncr_print_msg (ccb_p cp, char *label, u_char *msg); static int ncr_snooptest (ncb_p np); static void ncr_timeout (ncb_p np); static void ncr_wakeup (ncb_p np, u_long code); @@ -3792,6 +3826,17 @@ ncr_attach (Scsi_Host_Template *tpnt, int unit, ncr_device *device) instance->can_queue = (MAX_START-4); instance->select_queue_depths = ncr53c8xx_select_queue_depths; +#ifdef SCSI_NCR_INTEGRITY_CHECKING + np->check_integrity = 0; + instance->check_integrity = 0; + +#ifdef SCSI_NCR_ENABLE_INTEGRITY_CHECK + if ( !(driver_setup.bus_check & 0x04) ) { + np->check_integrity = 1; + instance->check_integrity = 1; + } +#endif +#endif /* ** Patch script to physical addresses */ @@ -4020,6 +4065,270 @@ static inline void ncr_flush_done_cmds(Scsi_Cmnd *lcmd) } } +/*========================================================== +** +** +** Prepare the next negotiation message for integrity check, +** if needed. +** +** Fill in the part of message buffer that contains the +** negotiation and the nego_status field of the CCB. +** Returns the size of the message in bytes. +** +** +**========================================================== +*/ + +#ifdef SCSI_NCR_INTEGRITY_CHECKING +static int ncr_ic_nego(ncb_p np, ccb_p cp, Scsi_Cmnd *cmd, u_char *msgptr) +{ + tcb_p tp = &np->target[cp->target]; + int msglen = 0; + int nego = 0; + u_char no_increase; + + if (tp->inq_done) { + + if (!tp->ic_maximums_set) { + tp->ic_maximums_set = 1; + + /* check target and host adapter capabilities */ + if ( (tp->inq_byte7 & INQ7_WIDE16) && + np->maxwide && tp->usrwide ) + tp->ic_max_width = 1; + else + tp->ic_max_width = 0; + + if ((tp->inq_byte7 & INQ7_SYNC) && tp->maxoffs) { + tp->ic_min_sync = (tp->minsync < np->minsync) ? + np->minsync : tp->minsync; + } + else + tp->ic_min_sync = 255; + + tp->period = 1; + tp->widedone = 1; + } + + if (DEBUG_FLAGS & DEBUG_IC) { + printk("%s: cmd->ic_nego %d, 1st byte 0x%2X\n", + ncr_name(np), cmd->ic_nego, cmd->cmnd[0]); + } + + /* First command from integrity check routine will request + * a PPR message. Disable. + */ + if ((cmd->ic_nego & NS_PPR) == NS_PPR) + cmd->ic_nego &= ~NS_PPR; + /* Previous command recorded a parity or an initiator + * detected error condition. Force bus to narrow for this + * target. Clear flag. Negotation on request sense. + * Note: kernel forces 2 bus resets :o( but clears itself out. + * Minor bug? in scsi_obsolete.c (ugly) + */ + if (np->check_integ_par) { + printk("%s: Parity Error. Target set to narrow.\n", + ncr_name(np)); + tp->ic_max_width = 0; + tp->widedone = tp->period = 0; + } + + /* In case of a bus reset, ncr_negotiate will reset + * the flags tp->widedone and tp->period to 0, forcing + * a new negotiation. + */ + no_increase = 0; + if (tp->widedone == 0) { + cmd->ic_nego = NS_WIDE; + tp->widedone = 1; + no_increase = 1; + } + else if (tp->period == 0) { + cmd->ic_nego = NS_SYNC; + tp->period = 1; + no_increase = 1; + } + + switch (cmd->ic_nego) { + case NS_WIDE: + /* + ** negotiate wide transfers ? + ** Do NOT negotiate if device only supports + ** narrow. + */ + if (tp->ic_max_width | np->check_integ_par) { + nego = NS_WIDE; + + msgptr[msglen++] = M_EXTENDED; + msgptr[msglen++] = 2; + msgptr[msglen++] = M_X_WIDE_REQ; + msgptr[msglen++] = cmd->ic_nego_width & tp->ic_max_width; + } + else + cmd->ic_nego_width &= tp->ic_max_width; + + break; + + case NS_SYNC: + /* + ** negotiate synchronous transfers? + ** Target must support sync transfers. + ** + ** If period becomes longer than max, reset to async + */ + + if (tp->inq_byte7 & INQ7_SYNC) { + + nego = NS_SYNC; + + msgptr[msglen++] = M_EXTENDED; + msgptr[msglen++] = 3; + msgptr[msglen++] = M_X_SYNC_REQ; + + switch (cmd->ic_nego_sync) { + case 2: /* increase the period */ + if (!no_increase) { + if (tp->ic_min_sync <= 0x0A) + tp->ic_min_sync = 0x0C; + else if (tp->ic_min_sync <= 0x0C) + tp->ic_min_sync = 0x19; + else if (tp->ic_min_sync <= 0x19) + tp->ic_min_sync *= 2; + else { + tp->ic_min_sync = 255; + cmd->ic_nego_sync = 0; + tp->maxoffs = 0; + } + } + msgptr[msglen++] = tp->maxoffs?tp->ic_min_sync:0; + msgptr[msglen++] = tp->maxoffs; + break; + + case 1: /* nego. to maximum */ + msgptr[msglen++] = tp->maxoffs?tp->ic_min_sync:0; + msgptr[msglen++] = tp->maxoffs; + break; + + case 0: /* nego to async */ + default: + msgptr[msglen++] = 0; + msgptr[msglen++] = 0; + break; + }; + } + else + cmd->ic_nego_sync = 0; + break; + + case NS_NOCHANGE: + default: + break; + }; + }; + + cp->nego_status = nego; + np->check_integ_par = 0; + + if (nego) { + tp->nego_cp = cp; + if (DEBUG_FLAGS & DEBUG_NEGO) { + ncr_print_msg(cp, nego == NS_WIDE ? + "wide/narrow msgout": "sync/async msgout", msgptr); + }; + }; + + return msglen; +} +#endif /* SCSI_NCR_INTEGRITY_CHECKING */ + +/*========================================================== +** +** +** Prepare the next negotiation message if needed. +** +** Fill in the part of message buffer that contains the +** negotiation and the nego_status field of the CCB. +** Returns the size of the message in bytes. +** +** +**========================================================== +*/ + + +static int ncr_prepare_nego(ncb_p np, ccb_p cp, u_char *msgptr) +{ + tcb_p tp = &np->target[cp->target]; + int msglen = 0; + int nego = 0; + + if (tp->inq_done) { + + /* + ** negotiate wide transfers ? + */ + + if (!tp->widedone) { + if (tp->inq_byte7 & INQ7_WIDE16) { + nego = NS_WIDE; +#ifdef SCSI_NCR_INTEGRITY_CHECKING + if (tp->ic_done) + tp->usrwide &= tp->ic_max_width; +#endif + } else + tp->widedone=1; + + }; + + /* + ** negotiate synchronous transfers? + */ + + if (!nego && !tp->period) { + if (tp->inq_byte7 & INQ7_SYNC) { + nego = NS_SYNC; +#ifdef SCSI_NCR_INTEGRITY_CHECKING + if ((tp->ic_done) && + (tp->minsync < tp->ic_min_sync)) + tp->minsync = tp->ic_min_sync; +#endif + } else { + tp->period =0xffff; + PRINT_TARGET(np, cp->target); + printk ("target did not report SYNC.\n"); + }; + }; + }; + + switch (nego) { + case NS_SYNC: + msgptr[msglen++] = M_EXTENDED; + msgptr[msglen++] = 3; + msgptr[msglen++] = M_X_SYNC_REQ; + msgptr[msglen++] = tp->maxoffs ? tp->minsync : 0; + msgptr[msglen++] = tp->maxoffs; + break; + case NS_WIDE: + msgptr[msglen++] = M_EXTENDED; + msgptr[msglen++] = 2; + msgptr[msglen++] = M_X_WIDE_REQ; + msgptr[msglen++] = tp->usrwide; + break; + }; + + cp->nego_status = nego; + + if (nego) { + tp->nego_cp = cp; + if (DEBUG_FLAGS & DEBUG_NEGO) { + ncr_print_msg(cp, nego == NS_WIDE ? + "wide msgout":"sync_msgout", msgptr); + }; + }; + + return msglen; +} + + /*========================================================== ** @@ -4038,7 +4347,7 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd) ccb_p cp; int segments; - u_char nego, idmsg, *msgptr; + u_char idmsg, *msgptr; u_int msglen; int direction; u_int32 lastp, goalp; @@ -4120,51 +4429,6 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd) cp->phys.header.stamp.start = jiffies; #endif - /*--------------------------------------------------- - ** - ** negotiation required? - ** - **--------------------------------------------------- - */ - - nego = 0; - - if ((!tp->widedone || !tp->period) && !tp->nego_cp && tp->inq_done && lp) { - - /* - ** negotiate wide transfers ? - */ - - if (!tp->widedone) { - if (tp->inq_byte7 & INQ7_WIDE16) { - nego = NS_WIDE; - } else - tp->widedone=1; - }; - - /* - ** negotiate synchronous transfers? - */ - - if (!nego && !tp->period) { - if (tp->inq_byte7 & INQ7_SYNC) { - nego = NS_SYNC; - } else { - tp->period =0xffff; - PRINT_TARGET(np, cmd->target); - printk ("device did not report SYNC.\n"); - }; - }; - - /* - ** remember nego is pending for the target. - ** Avoid to start a nego for all queued commands - ** when tagged command queuing is enabled. - */ - - if (nego) - tp->nego_cp = cp; - }; /*---------------------------------------------------- ** @@ -4225,34 +4489,6 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd) msgptr[msglen++] = (cp->tag << 1) + 1; } - switch (nego) { - case NS_SYNC: - msgptr[msglen++] = M_EXTENDED; - msgptr[msglen++] = 3; - msgptr[msglen++] = M_X_SYNC_REQ; - msgptr[msglen++] = tp->maxoffs ? tp->minsync : 0; - msgptr[msglen++] = tp->maxoffs; - if (DEBUG_FLAGS & DEBUG_NEGO) { - PRINT_ADDR(cp->cmd); - printk ("sync msgout: "); - ncr_show_msg (&cp->scsi_smsg [msglen-5]); - printk (".\n"); - }; - break; - case NS_WIDE: - msgptr[msglen++] = M_EXTENDED; - msgptr[msglen++] = 2; - msgptr[msglen++] = M_X_WIDE_REQ; - msgptr[msglen++] = tp->usrwide; - if (DEBUG_FLAGS & DEBUG_NEGO) { - PRINT_ADDR(cp->cmd); - printk ("wide msgout: "); - ncr_show_msg (&cp->scsi_smsg [msglen-4]); - printk (".\n"); - }; - break; - }; - /*---------------------------------------------------- ** ** Build the data descriptors @@ -4273,6 +4509,84 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd) segments = 0; } + /*--------------------------------------------------- + ** + ** negotiation required? + ** + ** (nego_status is filled by ncr_prepare_nego()) + ** + **--------------------------------------------------- + */ + + cp->nego_status = 0; + +#ifdef SCSI_NCR_INTEGRITY_CHECKING + if ((np->check_integrity && tp->ic_done) || !np->check_integrity) { + if ((!tp->widedone || !tp->period) && !tp->nego_cp && lp) { + msglen += ncr_prepare_nego (np, cp, msgptr + msglen); + } + } + else if (np->check_integrity && (cmd->ic_in_progress)) { + msglen += ncr_ic_nego (np, cp, cmd, msgptr + msglen); + } + else if (np->check_integrity && cmd->ic_complete) { + /* + * Midlayer signal to the driver that all of the scsi commands + * for the integrity check have completed. Save the negotiated + * parameters (extracted from sval and wval). + */ + + { + u_char idiv; + idiv = (tp->wval>>4) & 0x07; + if ((tp->sval&0x1f) && idiv ) + tp->period = (((tp->sval>>5)+4) + *div_10M[idiv-1])/np->clock_khz; + else + tp->period = 0xffff; + } + /* + * tp->period contains 10 times the transfer period, + * which itself is 4 * the requested negotiation rate. + */ + if (tp->period <= 250) tp->ic_min_sync = 10; + else if (tp->period <= 303) tp->ic_min_sync = 11; + else if (tp->period <= 500) tp->ic_min_sync = 12; + else + tp->ic_min_sync = (tp->period + 40 - 1) / 40; + + + /* + * Negotiation for this target it complete. + */ + tp->ic_max_width = (tp->wval & EWS) ? 1: 0; + tp->ic_done = 1; + tp->widedone = 1; + + printk("%s: Integrity Check Complete: \n", ncr_name(np)); + + printk("%s: %s %s SCSI", ncr_name(np), + (tp->sval&0x1f)?"SYNC":"ASYNC", + tp->ic_max_width?"WIDE":"NARROW"); + + if (tp->sval&0x1f) { + u_long mbs = 10000 * (tp->ic_max_width + 1); + + printk(" %d.%d MB/s", + (int) (mbs / tp->period), (int) (mbs % tp->period)); + + printk(" (%d ns, %d offset)\n", + tp->period/10, tp->sval&0x1f); + } + else + printk(" %d MB/s. \n ", (tp->ic_max_width+1)*5); + } +#else + if ((!tp->widedone || !tp->period) && !tp->nego_cp && lp) { + msglen += ncr_prepare_nego (np, cp, msgptr + msglen); + } +#endif /* SCSI_NCR_INTEGRITY_CHECKING */ + /*---------------------------------------------------- ** ** Determine xfer direction. @@ -4376,12 +4690,11 @@ static int ncr_queue_command (ncb_p np, Scsi_Cmnd *cmd) ** status */ cp->actualquirks = tp->quirks; - cp->host_status = nego ? HS_NEGOTIATE : HS_BUSY; + cp->host_status = cp->nego_status ? HS_NEGOTIATE : HS_BUSY; cp->scsi_status = S_ILLEGAL; cp->parity_status = 0; cp->xerr_status = XE_OK; - cp->nego_status = nego; #if 0 cp->sync_status = tp->sval; cp->wide_status = tp->wval; @@ -5041,7 +5354,13 @@ void ncr_complete (ncb_p np, ccb_p cp) for (i=0; i<14; i++) printk (" %x", *p++); printk (".\n"); } - + } else if ((cp->host_status == HS_COMPLETE) + && (cp->scsi_status == S_CONFLICT)) { + /* + ** Reservation Conflict condition code + */ + cmd->result = ScsiResult(DID_OK, S_CONFLICT); + } else if ((cp->host_status == HS_COMPLETE) && (cp->scsi_status == S_BUSY || cp->scsi_status == S_QUEUE_FULL)) { @@ -6398,6 +6717,14 @@ static int ncr_int_par (ncb_p np) else msg = M_ID_ERROR; +#ifdef SCSI_NCR_INTEGRITY_CHECKING + /* + ** Save error message. For integrity check use only. + */ + if (np->check_integrity) + np->check_integ_par = msg; +#endif + /* * If the NCR stopped on a MOVE ^ DATA_IN, we jump to a * script that will ignore all data in bytes until phase @@ -6908,6 +7235,16 @@ static int ncr_show_msg (u_char * msg) return (1); } +static void ncr_print_msg ( ccb_p cp, char *label, u_char *msg) +{ + if (cp) + PRINT_ADDR(cp->cmd); + if (label) + printk("%s: ", label); + + (void) ncr_show_msg (msg); + printk(".\n"); +} void ncr_int_sir (ncb_p np) { @@ -8275,6 +8612,7 @@ static void ncr_selectclock(ncb_p np, u_char scntl3) static unsigned __init ncrgetfreq (ncb_p np, int gen) { unsigned ms = 0; + char count = 0; /* * Measure GEN timer delay in order @@ -8299,8 +8637,10 @@ static unsigned __init ncrgetfreq (ncb_p np, int gen) OUTB (nc_scntl3, 4); /* set pre-scaler to divide by 3 */ OUTB (nc_stime1, 0); /* disable general purpose timer */ OUTB (nc_stime1, gen); /* set to nominal delay of 1<<gen * 125us */ - while (!(INW(nc_sist) & GEN) && ms++ < 100000) - UDELAY (1000); /* count ms */ + while (!(INW(nc_sist) & GEN) && ms++ < 100000) { + for (count = 0; count < 10; count ++) + UDELAY (100); /* count ms */ + } OUTB (nc_stime1, 0); /* disable general purpose timer */ /* * set prescaler to divide by whatever 0 means |