summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorse <se@FreeBSD.org>1996-10-11 19:50:12 +0000
committerse <se@FreeBSD.org>1996-10-11 19:50:12 +0000
commit1e0af5014366f9bf43e608bf93a9fccfb0032d3e (patch)
tree42591b712c5e0aa930229bf92e04fa7fc0fa281e
parent430faa57f96285ccf81cdc8bf084551e42ae6f97 (diff)
downloadFreeBSD-src-1e0af5014366f9bf43e608bf93a9fccfb0032d3e.zip
FreeBSD-src-1e0af5014366f9bf43e608bf93a9fccfb0032d3e.tar.gz
Improve support of SCSI cards based on the 53c860 and 53c875.
Still no support for Ultra-SCSI and other new features, but the code should now correctly initialize the clock pre-scaler (based on freqency measurement results, if necessary). Fix support of 16 targets for WIDE SCSI. Disable bus reset in case no progress is made for too long ("ncr dead" message), which did not work too well with scanners and other slow devices.
-rw-r--r--sys/pci/ncr.c187
-rw-r--r--sys/pci/ncrreg.h4
2 files changed, 124 insertions, 67 deletions
diff --git a/sys/pci/ncr.c b/sys/pci/ncr.c
index 1ad35c0..70f2280 100644
--- a/sys/pci/ncr.c
+++ b/sys/pci/ncr.c
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** $Id: ncr.c,v 1.78 1996/09/07 21:27:24 bde Exp $
+** $Id: ncr.c,v 1.79 1996/10/10 04:09:37 pst Exp $
**
** Device driver for the NCR 53C810 PCI-SCSI-Controller.
**
@@ -1193,12 +1193,12 @@ struct script {
*/
#ifdef KERNEL
-static void ncr_alloc_ccb (ncb_p np, struct scsi_xfer * xp);
+static void ncr_alloc_ccb (ncb_p np, u_long target, u_long lun);
static void ncr_complete (ncb_p np, ccb_p cp);
static int ncr_delta (struct timeval * from, struct timeval * to);
static void ncr_exception (ncb_p np);
static void ncr_free_ccb (ncb_p np, ccb_p cp, int flags);
-static void ncr_getclock (ncb_p np, u_char scntl3);
+static void ncr_getclock (ncb_p np);
static ccb_p ncr_get_ccb (ncb_p np, u_long flags, u_long t,u_long l);
static u_int32_t ncr_info (int unit);
static void ncr_init (ncb_p np, char * msg, u_long code);
@@ -1250,7 +1250,7 @@ static void ncr_attach (pcici_t tag, int unit);
static char ident[] =
- "\n$Id: ncr.c,v 1.78 1996/09/07 21:27:24 bde Exp $\n";
+ "\n$Id: ncr.c,v 1.79 1996/10/10 04:09:37 pst Exp $\n";
static const u_long ncr_version = NCR_VERSION * 11
+ (u_long) sizeof (struct ncb) * 7
@@ -2643,7 +2643,7 @@ static struct script script0 = {
** - struct ccb
** to understand what's going on.
*/
- SCR_REG_SFBR (ssid, SCR_AND, 0x87),
+ SCR_REG_SFBR (ssid, SCR_AND, 0x8F),
0,
SCR_TO_REG (ctest0),
0,
@@ -3301,17 +3301,28 @@ static void ncr_attach (pcici_t config_id, int unit)
** Do chip dependent initialization.
*/
+ np->maxwide = 0;
+ np->rv_scntl3 = 0x13; /* default: 40MHz clock */
+
+ /*
+ ** Get the frequency of the chip's clock.
+ ** Find the right value for scntl3.
+ */
+
#ifdef __NetBSD__
switch (pa->pa_id) {
#else /* !__NetBSD__ */
switch (pci_conf_read (config_id, PCI_ID_REG)) {
#endif /* __NetBSD__ */
case NCR_825_ID:
- case NCR_875_ID:
np->maxwide = 1;
break;
- default:
- np->maxwide = 0;
+ case NCR_860_ID:
+ np->rv_scntl3 = 0x35; /* always assume 80MHz clock for 860 */
+ break;
+ case NCR_875_ID:
+ np->maxwide = 1;
+ ncr_getclock(np);
break;
}
@@ -3346,13 +3357,6 @@ static void ncr_attach (pcici_t config_id, int unit)
if (!np->myaddr) np->myaddr = SCSI_NCR_MYADDR;
/*
- ** Get the value of the chip's clock.
- ** Find the right value for scntl3.
- */
-
- ncr_getclock (np, INB(nc_scntl3));
-
- /*
** Reset chip.
*/
@@ -3601,10 +3605,25 @@ static int32_t ncr_start (struct scsi_xfer * xp)
};
};
+ if ((unsigned)xp->datalen > 128*1024*1024) {
+ PRINT_ADDR(xp);
+ printf ("trying to transfer %8x bytes, mem addr = %8x\n",
+ xp->datalen, xp->data);
+ {
+ int i;
+ PRINT_ADDR(xp);
+ printf ("command: %2x (", cmd->opcode);
+ for (i = 0; i<11; i++)
+ printf (" %2x", cmd->bytes[i]);
+ printf (")\n");
+ }
+ }
+
if (DEBUG_FLAGS & DEBUG_TINY) {
PRINT_ADDR(xp);
- printf ("CMD=%x F=%x L=%x ", cmd->opcode,
- (unsigned)xp->flags, (unsigned) xp->datalen);
+ printf ("CMD=%x F=%x A=%x L=%x ",
+ cmd->opcode, (unsigned)xp->flags,
+ (unsigned) xp->data, (unsigned) xp->datalen);
}
/*--------------------------------------------
@@ -4156,7 +4175,7 @@ void ncr_complete (ncb_p np, ccb_p cp)
/*
** Try to assign a ccb to this nexus
*/
- ncr_alloc_ccb (np, xp);
+ ncr_alloc_ccb (np, xp->sc_link->target, xp->sc_link->lun);
/*
** On inquire cmd (0x12) save some data.
@@ -4544,7 +4563,7 @@ static void ncr_setsync (ncb_p np, ccb_p cp, u_char sxfer)
{
struct scsi_xfer *xp;
tcb_p tp;
- u_char target = INB (nc_ctest0)&7;
+ u_char target = INB (nc_ctest0) & 0x0f;
assert (cp);
if (!cp) return;
@@ -4552,7 +4571,7 @@ static void ncr_setsync (ncb_p np, ccb_p cp, u_char sxfer)
xp = cp->xfer;
assert (xp);
if (!xp) return;
- assert (target == xp->sc_link->target & 7);
+ assert (target == (xp->sc_link->target & 0x0f));
tp = &np->target[target];
tp->period= sxfer&0xf ? ((sxfer>>5)+4) * np->ns_sync : 0xffff;
@@ -4601,7 +4620,7 @@ static void ncr_setsync (ncb_p np, ccb_p cp, u_char sxfer)
static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide)
{
struct scsi_xfer *xp;
- u_short target = INB (nc_ctest0)&7;
+ u_short target = INB (nc_ctest0) & 0x0f;
tcb_p tp;
u_char scntl3 = np->rv_scntl3 | (wide ? EWS : 0);
@@ -4611,7 +4630,7 @@ static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide)
xp = cp->xfer;
assert (xp);
if (!xp) return;
- assert (target == xp->sc_link->target & 7);
+ assert (target == (xp->sc_link->target & 0x0f));
tp = &np->target[target];
tp->widedone = wide+1;
@@ -4826,6 +4845,7 @@ static void ncr_timeout (ncb_p np)
OUTB (nc_istat, SIGP);
};
+#ifdef undef
if (np->latetime>4) {
/*
** Although we tried to wake it up,
@@ -4843,7 +4863,7 @@ static void ncr_timeout (ncb_p np)
ncr_init (np, "ncr dead ?", HS_TIMEOUT);
np->heartbeat = thistime;
};
-
+#endif
/*----------------------------------------------------
**
** handle ccb timeouts
@@ -4933,13 +4953,14 @@ void ncr_exception (ncb_p np)
** interrupt on the fly ?
*/
while ((istat = INB (nc_istat)) & INTF) {
- if (DEBUG_FLAGS & DEBUG_TINY) printf ("F");
+ if (DEBUG_FLAGS & DEBUG_TINY) printf ("F ");
OUTB (nc_istat, INTF);
np->profile.num_fly++;
ncr_wakeup (np, 0);
};
-
- if (!(istat & (SIP|DIP))) return;
+ if (!(istat & (SIP|DIP))) {
+ return;
+ }
/*
** Steinbach's Guideline for Systems Programming:
@@ -5517,7 +5538,7 @@ void ncr_int_sir (ncb_p np)
u_char num = INB (nc_dsps);
ccb_p cp=0;
u_long dsa;
- u_char target = INB (nc_ctest0) & 7;
+ u_char target = INB (nc_ctest0) & 0x0f;
tcb_p tp = &np->target[target];
int i;
if (DEBUG_FLAGS & DEBUG_TINY) printf ("I#%d", num);
@@ -6177,20 +6198,13 @@ void ncr_free_ccb (ncb_p np, ccb_p cp, int flags)
**==========================================================
*/
-static void ncr_alloc_ccb (ncb_p np, struct scsi_xfer * xp)
+static void ncr_alloc_ccb (ncb_p np, u_long target, u_long lun)
{
tcb_p tp;
lcb_p lp;
ccb_p cp;
- u_long target;
- u_long lun;
-
assert (np != NULL);
- assert (xp != NULL);
-
- target = xp->sc_link->target;
- lun = xp->sc_link->lun;
if (target>=MAX_TARGET) return;
if (lun >=MAX_LUN ) return;
@@ -6281,7 +6295,6 @@ static void ncr_alloc_ccb (ncb_p np, struct scsi_xfer * xp)
return;
if (DEBUG_FLAGS & DEBUG_ALLOC) {
- PRINT_ADDR(xp);
printf ("new ccb @%x.\n", (unsigned) cp);
}
@@ -6762,39 +6775,83 @@ static u_long ncr_lookup(char * id)
# define NCR_CLOCK 40
#endif /* NCR_CLOCK */
-
-static void ncr_getclock (ncb_p np, u_char scntl3)
-{
- u_char tbl[6] = {6,2,3,4,6,8};
- u_char f;
- u_char ns_clock = (1000/NCR_CLOCK);
-
- /*
- ** Compute the best value for scntl3.
- */
/*
- f = (2 * MIN_SYNC_PD - 1) / ns_clock;
- if (!f ) f=1;
- if (f>4) f=4;
- np -> ns_sync = (ns_clock * tbl[f]) / 2;
- np -> rv_scntl3 = f<<4;
-
- f = (2 * MIN_ASYNC_PD - 1) / ns_clock;
- if (!f ) f=1;
- if (f>4) f=4;
- np -> ns_async = (ns_clock * tbl[f]) / 2;
- np -> rv_scntl3 |= f;
- if (DEBUG_FLAGS & DEBUG_TIMING)
- printf ("%s: sclk=%d async=%d sync=%d (ns) scntl3=0x%x\n",
- ncr_name (np), ns_clock, np->ns_async, np->ns_sync, np->rv_scntl3);
-*/
+ * calculate NCR SCSI clock frequency (in KHz)
+ */
+static unsigned
+ncrgetfreq (ncb_p np, int gen)
+{
+ int ms = 0;
+ /*
+ * Measure GEN timer delay in order
+ * to calculate SCSI clock frequency
+ *
+ * This code will never execute too
+ * many loop iterations (if DELAY is
+ * reasonably correct). It could get
+ * too low a delay (too high a freq.)
+ * if the CPU is slow executing the
+ * loop for some reason (an NMI, for
+ * example). For this reason we will
+ * if multiple measurements are to be
+ * performed trust the higher delay
+ * (lower frequency returned).
+ */
+ OUTB (nc_stest1, 0); /* make sure clock doubler is OFF */
+ OUTW (nc_sien , 0); /* mask all scsi interrupts */
+ (void) INW (nc_sist); /* clear pending scsi interrupt */
+ OUTB (nc_dien , 0); /* mask all dma interrupts */
+ (void) INW (nc_sist); /* another one, just to be sure :) */
+ 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++ < 1000)
+ DELAY(1000); /* count ms */
+ OUTB (nc_stime1, 0); /* disable general purpose timer */
+ OUTB (nc_scntl3, 0);
+ /*
+ * Set prescaler to divide by whatever "0" means.
+ * "0" ought to choose divide by 2, but appears
+ * to set divide by 3.5 mode in my 53c810 ...
+ */
+ OUTB (nc_scntl3, 0);
+ if (bootverbose)
+ printf ("\tDelay (GEN=%d): %lu msec\n", gen, ms);
/*
- * For now just preserve the BIOS setting ...
+ * adjust for prescaler, and convert into KHz
*/
+ return ms ? ((1 << gen) * 4440) / ms : 0;
+}
- if ((scntl3 & 7) == 0) {
- scntl3 = 3; /* assume 40MHz if no value supplied by BIOS */
+static void ncr_getclock (ncb_p np)
+{
+ unsigned char scntl3;
+ unsigned char stest1;
+ scntl3 = INB(nc_scntl3);
+ stest1 = INB(nc_stest1);
+
+ /* always false, except for 875 with clock doubler selected */
+ if ((stest1 & (DBLEN+DBLSEL)) == DBLEN+DBLSEL) {
+ OUTB(nc_stest1, 0);
+ scntl3 = 3;
+ } else {
+ if ((scntl3 & 7) == 0) {
+ unsigned f1, f2;
+ /* throw away first result */
+ (void) ncrgetfreq (np, 11);
+ f1 = ncrgetfreq (np, 11);
+ f2 = ncrgetfreq (np, 11);
+
+ if (bootverbose)
+ printf ("\tNCR clock is %luKHz, %luKHz\n", f1, f2);
+ if (f1 > f2) f1 = f2; /* trust lower result */
+ if (f1 > 45000) {
+ scntl3 = 5; /* >45Mhz: assume 80MHz */
+ } else {
+ scntl3 = 3; /* <45Mhz: assume 40MHz */
+ }
+ }
}
np->ns_sync = 25;
@@ -6809,5 +6866,3 @@ static void ncr_getclock (ncb_p np, u_char scntl3)
/*=========================================================================*/
#endif /* KERNEL */
-
-
diff --git a/sys/pci/ncrreg.h b/sys/pci/ncrreg.h
index 1a8b9f1..2f88c42 100644
--- a/sys/pci/ncrreg.h
+++ b/sys/pci/ncrreg.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** $Id: ncrreg.h,v 1.2 1995/02/02 13:12:16 davidg Exp $
+** $Id: ncrreg.h,v 1.3 1995/03/21 22:48:36 se Exp $
**
** Device driver for the NCR 53C810 PCI-SCSI-Controller.
**
@@ -198,6 +198,8 @@ struct ncr_reg {
/*4c*/ u_char nc_stest0;
/*4d*/ u_char nc_stest1;
+ #define DBLEN 0x08 /* clock doubler running */
+ #define DBLSEL 0x04 /* clock doubler selected */
/*4e*/ u_char nc_stest2;
#define ROF 0x40 /* reset scsi offset (after gross error!) */
OpenPOWER on IntegriCloud