[PATCH][SCSI] wd33c93.c: Fast SCSI with WD33C93B

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




Attached are patches, which help to utilize more of the WD33C93B SCSI
controller's capabilities.

1st Stage:

1) Added/changed all the necessary code to enable Burst Mode DMA. Only
   Single Byte DMA was used before.

2) Added/changed all the necessary code to enable Fast-10 SCSI transfers.

3) The original driver inadvertently used a transfer period of 1000-800ns
   (the lowest possible transfer rate) for asynchronous data transfers,
   instead of the (configurable) default period intended for this purpose,
   if the target responded to a SDTR not with a Reject-message, but with
   a zero-SDTR. This issue was fixed.
   Moreover, in case of a Reject the driver used the default-period's
   initialization-value instead of its (maybe smaller) current value. The
   missing assignment was added.

4) The driver's commandline- and proc-file-interface was augmented to
   handle the new options properly.

2nd Stage:

The original driver used to rely on a fixed table (sx_table) to select
transfer periods and map them to register values. This table contained
periods for (only) the lower limits of the respective input-clock-frequency
ranges (8-10/12-15/16-20 MHz). Although it seems, that no problems ocurred
with this setting so far, it seemed desirable to adjust the transfer periods
closer to the really attached, possibly 25% higher, input-clock, since

a) the wd33c93 may really use a significant shorter period, than it has
   negotiated (eg. thrashing the target, which expects 4/8MHz, with 5/10MHz
   instead).
b) the wd33c93 may ask the target for a lower transfer rate, than the target
   is capable of (eg. negotiating for an assumed minimum of 252ns instead of
   possible 200ns).
   And indeed, this issue shows up as an approx. 10% lower transfer rate.

I'd preferred to fix this by simply replacing the lower-limit period-values
(252,376,500,624,752,876,1000) by upper-limit values (200,300,400,500,600,
700,800). IMHO these would do no harm, if used with actually lower input-
clock frequencies, and automatically provide best performance.

But for to keep the driver's former state available, to keep it as the default
state, so that there's no difference, when the driver is used by existing,
unchanged platform-drivers, i choose the more complicated approach of always
calculating an appropriate table for the respective clock-parameter.

The disadvantage is, that a platform-driver, that can and wishes to benefit
from the higher input-clock-frequency on its system, must be changed (e.g.)
from calling wd33c93_init(... WD33C93_FS_16_20) to calling
wd33c93_init(... WD33C93_FS_MHZ(20)).


Performance figures:

This modified driver was tested, and is in use, on a SiliconGraphics Indigo2
with Quantum Atlas V Disk(s) and Pioneer DVD305S attached.
Read performance was measured by copying a disk-partition (resp. a CDROM)
to /dev/null, write performance by copying 1GB from /dev/zero to a disk
file.
"external transfer rate" is meant according to the "real" time spent in
the "time dd ..." command, while
"internal transfer rate" is meant according to the cpu-cycles spent from
calling dma_setup to the return from dma_stop.

Disk reads (synchronous) on average:
           original    burst       burst+fast
external   2.87        3.63        5.71        MB/s
internal   3.05        3.84        6.14        MB/s

Disk writes (synchronous) on average:
           original    burst       burst+fast
external   3.01        3.97        6.42        MB/s
internal   3.21        4.26        7.03        MB/s

Asynchronous reads on average:
                       burst+fast  burst+fast
           original    init.def.p. optimum default_per
external   1.09        2.02        3.44        MB/s
internal   1.15        2.13        3.63        MB/s

"fast" without "burst" had no effect.
Figures for the new driver were at least 10% lower, if 2nd Stage was
not applied.

So, these patches applied, together with
"echo 'burst:1 fast:1 period:200'>/proc/scsi/SGIWD93/0" (and "...93/1")
should provide a much more enjoyable system :-)

The WD33C93 manual, found at
http://www.datasheet.in/datasheet-html/W/D/3/WD33C93B_WesternDigital.pdf.html,
was very helpfull.

with kind regards


Signed-off-by: peter fuerst <post@xxxxxxxx>

=== 1st Stage ==========================================================
--- 6ab3d5624e172c553004ecc862bfeac16d9d68b7/drivers/scsi/wd33c93.h	Sat Jul  1 00:00:00 2006
+++ stage1/drivers/scsi/wd33c93.h	Sun Feb 11 20:05:53 2007
@@ -253,6 +253,8 @@
     uchar            sync_stat[8];     /* status of sync negotiation per target */
     uchar            no_sync;          /* bitmask: don't do sync on these targets */
     uchar            no_dma;           /* set this flag to disable DMA */
+    uchar            dma_mode;         /* DMA Burst Mode or Single Byte DMA */
+    uchar            fast;             /* set this flag to enable Fast SCSI */
 #ifdef PROC_INTERFACE
     uchar            proc;             /* bitmask: what's in proc output */
 #ifdef PROC_STATISTICS
--- 79bd3f8563a275d1d068bbb9189a746dc6e96f3e/drivers/scsi/wd33c93.c	Sat Jul 15 00:00:00 2006
+++ stage1/drivers/scsi/wd33c93.c	Sun Feb 11 22:01:17 2007
@@ -69,6 +69,10 @@
  * Added support for pre -A chips, which don't have advanced features
  * and will generate CSR_RESEL rather than CSR_RESEL_AM.
  *	Richard Hirst <richard@xxxxxxxxxxxxxxxxxxx>  August 2000
+ *
+ * Added support for Burst Mode DMA and Fast SCSI. Enabled the use of
+ * default_sx_per for asynchronous data transfers.
+ *  peter fuerst <post@xxxxxxxx>  February 2007
  */

 #include <linux/module.h>
@@ -88,8 +92,8 @@
 #include "wd33c93.h"


-#define WD33C93_VERSION    "1.26"
-#define WD33C93_DATE       "22/Feb/2003"
+#define WD33C93_VERSION    "1.26++"
+#define WD33C93_DATE       "10/Feb/2007"

 MODULE_AUTHOR("John Shifflett");
 MODULE_DESCRIPTION("Generic WD33C93 SCSI driver");
@@ -123,6 +127,13 @@ MODULE_LICENSE("GPL");
  *                    defines in wd33c93.h
  * -  clock:x        -x = clock input in MHz for WD33c93 chip. Normal values
  *                    would be from 8 through 20. Default is 8.
+ * -  burst:x        -x = 1 to use Burst Mode (or Demand-Mode) DMA, x = 0 to use
+ *                    Single Byte DMA, which is the default. Argument is
+ *                    optional - if not present, same as "burst:1".
+ * -  fast:x         -x = 1 to enable Fast SCSI, which is only effective with
+ *                    input-clock divisor 4 (WD33C93_FS_16_20), x = 0 to disable
+ *                    it, which is the default.  Argument is optional - if not
+ *                    present, same as "fast:1".
  * -  next           -No argument. Used to separate blocks of keywords when
  *                    there's more than one host adapter in the system.
  *
@@ -149,7 +160,7 @@ MODULE_LICENSE("GPL");
  */

 /* Normally, no defaults are specified */
-static char *setup_args[] = { "", "", "", "", "", "", "", "", "" };
+static char *setup_args[] = { "", "", "", "", "", "", "", "", "", "" };

 static char *setup_strings;
 module_param(setup_strings, charp, 0);
@@ -325,17 +336,48 @@ round_period(unsigned int period)
 	return 7;
 }

+/*
+ * Calculate Synchronous Transfer Register value from SDTR code.
+ */
 static uchar
-calc_sync_xfer(unsigned int period, unsigned int offset)
+calc_sync_xfer(unsigned int period, unsigned int offset, unsigned int fast)
 {
+	/* When doing Fast SCSI synchronous data transfers, the corresponding
+	 * value in 'sx_table' is two times the actually used transfer period.
+	 */
 	uchar result;

+	if (offset && fast) {
+		fast = STR_FSS;
+		period *= 2;
+	} else {
+		fast = 0;
+	}
 	period *= 4;		/* convert SDTR code to ns */
 	result = sx_table[round_period(period)].reg_value;
 	result |= (offset < OPTIMUM_SX_OFF) ? offset : OPTIMUM_SX_OFF;
+	result |= fast;
 	return result;
 }

+/*
+ * Calculate SDTR code bytes [3],[4] from period and offset.
+ */
+static inline void
+calc_sync_msg(unsigned int period, unsigned int offset, unsigned int fast,
+                uchar  msg[2])
+{
+	/* 'period' is a "normal"-mode value, like the ones in 'sx_table'. The
+	 * actually used transfer period for Fast SCSI synchronous data
+	 * transfers is half that value.
+	 */
+	period /= 4;
+	if (offset && fast)
+		period /= 2;
+	msg[0] = period;
+	msg[1] = offset;
+}
+
 int
 wd33c93_queuecommand(struct scsi_cmnd *cmd,
 		void (*done)(struct scsi_cmnd *))
@@ -633,7 +675,7 @@ wd33c93_execute(struct Scsi_Host *instan
 				write_wd33c93_count(regs,
 						    cmd->SCp.this_residual);
 				write_wd33c93(regs, WD_CONTROL,
-					      CTRL_IDI | CTRL_EDI | CTRL_DMA);
+					      CTRL_IDI | CTRL_EDI | hostdata->dma_mode);
 				hostdata->dma = D_DMA_RUNNING;
 			}
 		} else
@@ -713,6 +755,8 @@ transfer_bytes(const wd33c93_regs regs,
 		cmd->SCp.ptr = page_address(cmd->SCp.buffer->page) +
 		    cmd->SCp.buffer->offset;
 	}
+	if (!cmd->SCp.this_residual) /* avoid bogus setups */
+		return;

 	write_wd33c93(regs, WD_SYNCHRONOUS_TRANSFER,
 		      hostdata->sync_xfer[cmd->device->id]);
@@ -745,7 +789,7 @@ transfer_bytes(const wd33c93_regs regs,
 #ifdef PROC_STATISTICS
 		hostdata->dma_cnt++;
 #endif
-		write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_DMA);
+		write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | hostdata->dma_mode);
 		write_wd33c93_count(regs, cmd->SCp.this_residual);

 		if ((hostdata->level2 >= L2_DATA) ||
@@ -863,9 +907,6 @@ wd33c93_intr(struct Scsi_Host *instance)
 			hostdata->outgoing_msg[0] |= 0x40;

 		if (hostdata->sync_stat[cmd->device->id] == SS_FIRST) {
-#ifdef SYNC_DEBUG
-			printk(" sending SDTR ");
-#endif

 			hostdata->sync_stat[cmd->device->id] = SS_WAITING;

@@ -879,14 +920,19 @@ wd33c93_intr(struct Scsi_Host *instance)
 			hostdata->outgoing_msg[2] = 3;
 			hostdata->outgoing_msg[3] = EXTENDED_SDTR;
 			if (hostdata->no_sync & (1 << cmd->device->id)) {
-				hostdata->outgoing_msg[4] =
-				    hostdata->default_sx_per / 4;
-				hostdata->outgoing_msg[5] = 0;
+				calc_sync_msg(hostdata->default_sx_per, 0,
+						0, hostdata->outgoing_msg + 4);
 			} else {
-				hostdata->outgoing_msg[4] = OPTIMUM_SX_PER / 4;
-				hostdata->outgoing_msg[5] = OPTIMUM_SX_OFF;
+				calc_sync_msg(OPTIMUM_SX_PER, OPTIMUM_SX_OFF,
+						hostdata->fast,
+						hostdata->outgoing_msg + 4);
 			}
 			hostdata->outgoing_len = 6;
+#ifdef SYNC_DEBUG
+			ucp = hostdata->outgoing_msg + 1;
+			printk(" sending SDTR %02x03%02x%02x%02x ",
+				ucp[0], ucp[2], ucp[3], ucp[4]);
+#endif
 		} else
 			hostdata->outgoing_len = 1;

@@ -1002,8 +1048,13 @@ wd33c93_intr(struct Scsi_Host *instance)
 #ifdef SYNC_DEBUG
 			    printk("-REJ-");
 #endif
-			if (hostdata->sync_stat[cmd->device->id] == SS_WAITING)
+			if (hostdata->sync_stat[cmd->device->id] == SS_WAITING) {
 				hostdata->sync_stat[cmd->device->id] = SS_SET;
+				/* we want default_sx_per, not DEFAULT_SX_PER */
+				hostdata->sync_xfer[cmd->device->id] =
+					calc_sync_xfer(hostdata->default_sx_per
+						/ 4, 0, 0);
+			}
 			write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK);
 			hostdata->state = S_CONNECTED;
 			break;
@@ -1023,7 +1074,10 @@ wd33c93_intr(struct Scsi_Host *instance)

 				switch (ucp[2]) {	/* what's the EXTENDED code? */
 				case EXTENDED_SDTR:
-					id = calc_sync_xfer(ucp[3], ucp[4]);
+					/* default to default async period */
+					id = calc_sync_xfer(hostdata->
+							default_sx_per / 4, 0,
+							0);
 					if (hostdata->sync_stat[cmd->device->id] !=
 					    SS_WAITING) {

@@ -1042,20 +1096,21 @@ wd33c93_intr(struct Scsi_Host *instance)
 						hostdata->outgoing_msg[1] = 3;
 						hostdata->outgoing_msg[2] =
 						    EXTENDED_SDTR;
-						hostdata->outgoing_msg[3] =
-						    hostdata->default_sx_per /
-						    4;
-						hostdata->outgoing_msg[4] = 0;
+						calc_sync_msg(hostdata->
+							default_sx_per, 0,
+							0, hostdata->outgoing_msg + 3);
 						hostdata->outgoing_len = 5;
-						hostdata->sync_xfer[cmd->device->id] =
-						    calc_sync_xfer(hostdata->
-								   default_sx_per
-								   / 4, 0);
 					} else {
-						hostdata->sync_xfer[cmd->device->id] = id;
+						if (ucp[4]) /* well, sync transfer */
+							id = calc_sync_xfer(ucp[3], ucp[4],
+									hostdata->fast);
+						else if (ucp[3]) /* very unlikely... */
+							id = calc_sync_xfer(ucp[3], ucp[4],
+									0);
 					}
+					hostdata->sync_xfer[cmd->device->id] = id;
 #ifdef SYNC_DEBUG
-					printk("sync_xfer=%02x",
+					printk(" sync_xfer=%02x\n",
 					       hostdata->sync_xfer[cmd->device->id]);
 #endif
 					hostdata->sync_stat[cmd->device->id] =
@@ -1487,7 +1542,7 @@ reset_wd33c93(struct Scsi_Host *instance
 	write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
 	write_wd33c93(regs, WD_SYNCHRONOUS_TRANSFER,
 		      calc_sync_xfer(hostdata->default_sx_per / 4,
-				     DEFAULT_SX_OFF));
+				     DEFAULT_SX_OFF, 0));
 	write_wd33c93(regs, WD_COMMAND, WD_CMD_RESET);


@@ -1513,6 +1568,9 @@ reset_wd33c93(struct Scsi_Host *instance
 	} else
 		hostdata->chip = C_UNKNOWN_CHIP;

+	if (hostdata->chip != C_WD33C93B)	/* Fast SCSI unavailable */
+		hostdata->fast = 0;
+
 	write_wd33c93(regs, WD_TIMEOUT_PERIOD, TIMEOUT_PERIOD_VALUE);
 	write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
 }
@@ -1534,7 +1592,7 @@ wd33c93_host_reset(struct scsi_cmnd * SC
 	for (i = 0; i < 8; i++) {
 		hostdata->busy[i] = 0;
 		hostdata->sync_xfer[i] =
-		    calc_sync_xfer(DEFAULT_SX_PER / 4, DEFAULT_SX_OFF);
+			calc_sync_xfer(DEFAULT_SX_PER / 4, DEFAULT_SX_OFF, 0);
 		hostdata->sync_stat[i] = SS_UNSET;	/* using default sync values */
 	}
 	hostdata->input_Q = NULL;
@@ -1783,6 +1841,38 @@ check_setup_args(char *key, int *flags,
 	return ++x;
 }

+/*
+ * check and, maybe, map an init- or "clock:"- argument.
+ */
+static uchar
+set_clk_freq(int freq)
+{
+	if (freq != WD33C93_FS_8_10 &&
+	    freq != WD33C93_FS_12_15 &&
+	    freq != WD33C93_FS_16_20)
+		if (freq > 7 && freq < 11)
+			freq = WD33C93_FS_8_10;
+		else if (freq > 11 && freq < 16)
+			freq = WD33C93_FS_12_15;
+		else if (freq > 15 && freq < 21)
+			freq = WD33C93_FS_16_20;
+		else
+			/* Hmm, wouldn't it be safer to assume highest freq here? */
+			freq = WD33C93_FS_8_10;
+	return freq;
+}
+
+/*
+ * to be used with the resync: fast: ... options
+ */
+static inline void set_resync ( struct WD33C93_hostdata *hd, int mask )
+{
+	int i;
+	for (i = 0; i < 8; i++)
+		if (mask & (1 << i))
+			hd->sync_stat[i] = SS_UNSET;
+}
+
 void
 wd33c93_init(struct Scsi_Host *instance, const wd33c93_regs regs,
 	     dma_setup_t setup, dma_stop_t stop, int clock_freq)
@@ -1799,7 +1889,7 @@ wd33c93_init(struct Scsi_Host *instance,
 	hostdata = (struct WD33C93_hostdata *) instance->hostdata;

 	hostdata->regs = regs;
-	hostdata->clock_freq = clock_freq;
+	hostdata->clock_freq = set_clk_freq(clock_freq);
 	hostdata->dma_setup = setup;
 	hostdata->dma_stop = stop;
 	hostdata->dma_bounce_buffer = NULL;
@@ -1807,7 +1897,7 @@ wd33c93_init(struct Scsi_Host *instance,
 	for (i = 0; i < 8; i++) {
 		hostdata->busy[i] = 0;
 		hostdata->sync_xfer[i] =
-		    calc_sync_xfer(DEFAULT_SX_PER / 4, DEFAULT_SX_OFF);
+			calc_sync_xfer(DEFAULT_SX_PER / 4, DEFAULT_SX_OFF, 0);
 		hostdata->sync_stat[i] = SS_UNSET;	/* using default sync values */
 #ifdef PROC_STATISTICS
 		hostdata->cmd_cnt[i] = 0;
@@ -1829,6 +1919,8 @@ wd33c93_init(struct Scsi_Host *instance,
 	hostdata->default_sx_per = DEFAULT_SX_PER;
 	hostdata->no_sync = 0xff;	/* sync defaults to off */
 	hostdata->no_dma = 0;	/* default is DMA enabled */
+	hostdata->fast = 0;	/* default is Fast SCSI transfers disabled */
+	hostdata->dma_mode = CTRL_DMA;	/* default is Single Byte DMA */

 #ifdef PROC_INTERFACE
 	hostdata->proc = PR_VERSION | PR_INFO | PR_STATISTICS |
@@ -1840,6 +1932,9 @@ wd33c93_init(struct Scsi_Host *instance,
 #endif
 #endif

+	if (check_setup_args("clock", &flags, &val, buf))
+		hostdata->clock_freq = set_clk_freq(val);
+
 	if (check_setup_args("nosync", &flags, &val, buf))
 		hostdata->no_sync = val;

@@ -1863,17 +1958,12 @@ wd33c93_init(struct Scsi_Host *instance,
 	if (check_setup_args("debug", &flags, &val, buf))
 		hostdata->args = val & DB_MASK;

-	if (check_setup_args("clock", &flags, &val, buf)) {
-		if (val > 7 && val < 11)
-			val = WD33C93_FS_8_10;
-		else if (val > 11 && val < 16)
-			val = WD33C93_FS_12_15;
-		else if (val > 15 && val < 21)
-			val = WD33C93_FS_16_20;
-		else
-			val = WD33C93_FS_8_10;
-		hostdata->clock_freq = val;
-	}
+	if (check_setup_args("burst", &flags, &val, buf))
+		hostdata->dma_mode = val ? CTRL_BURST:CTRL_DMA;
+
+	if (WD33C93_FS_16_20 == hostdata->clock_freq /* divisor 4 */
+		&& check_setup_args("fast", &flags, &val, buf))
+		hostdata->fast = !!val;

 	if ((i = check_setup_args("next", &flags, &val, buf))) {
 		while (i)
@@ -1924,47 +2014,58 @@ wd33c93_proc_info(struct Scsi_Host *inst
 	hd = (struct WD33C93_hostdata *) instance->hostdata;

 /* If 'in' is TRUE we need to _read_ the proc file. We accept the following
- * keywords (same format as command-line, but only ONE per read):
+ * keywords (same format as command-line, but arguments are not optional):
  *    debug
  *    disconnect
  *    period
  *    resync
  *    proc
  *    nodma
+ *    level2
+ *    burst
+ *    fast
+ *    nosync
  */

 	if (in) {
 		buf[len] = '\0';
-		bp = buf;
+		for (bp = buf; *bp; ) {
+			while (',' == *bp || ' ' == *bp)
+				++bp;
 		if (!strncmp(bp, "debug:", 6)) {
-			bp += 6;
-			hd->args = simple_strtoul(bp, NULL, 0) & DB_MASK;
+				hd->args = simple_strtoul(bp+6, &bp, 0) & DB_MASK;
 		} else if (!strncmp(bp, "disconnect:", 11)) {
-			bp += 11;
-			x = simple_strtoul(bp, NULL, 0);
+				x = simple_strtoul(bp+11, &bp, 0);
 			if (x < DIS_NEVER || x > DIS_ALWAYS)
 				x = DIS_ADAPTIVE;
 			hd->disconnect = x;
 		} else if (!strncmp(bp, "period:", 7)) {
-			bp += 7;
-			x = simple_strtoul(bp, NULL, 0);
+				x = simple_strtoul(bp+7, &bp, 0);
 			hd->default_sx_per =
 			    sx_table[round_period((unsigned int) x)].period_ns;
 		} else if (!strncmp(bp, "resync:", 7)) {
-			bp += 7;
-			x = simple_strtoul(bp, NULL, 0);
-			for (i = 0; i < 7; i++)
-				if (x & (1 << i))
-					hd->sync_stat[i] = SS_UNSET;
+				set_resync(hd, (int)simple_strtoul(bp+7, &bp, 0));
 		} else if (!strncmp(bp, "proc:", 5)) {
-			bp += 5;
-			hd->proc = simple_strtoul(bp, NULL, 0);
+				hd->proc = simple_strtoul(bp+5, &bp, 0);
 		} else if (!strncmp(bp, "nodma:", 6)) {
-			bp += 6;
-			hd->no_dma = simple_strtoul(bp, NULL, 0);
+				hd->no_dma = simple_strtoul(bp+6, &bp, 0);
 		} else if (!strncmp(bp, "level2:", 7)) {
-			bp += 7;
-			hd->level2 = simple_strtoul(bp, NULL, 0);
+				hd->level2 = simple_strtoul(bp+7, &bp, 0);
+			} else if (!strncmp(bp, "burst:", 6)) {
+				hd->dma_mode =
+					simple_strtol(bp+6, &bp, 0) ? CTRL_BURST:CTRL_DMA;
+			} else if (!strncmp(bp, "fast:", 5)) {
+				x = !!simple_strtol(bp+5, &bp, 0);
+				if (x != hd->fast)
+					set_resync(hd, 0xff);
+				hd->fast = x;
+			} else if (!strncmp(bp, "nosync:", 7)) {
+				x = simple_strtoul(bp+7, &bp, 0);
+				set_resync(hd, x ^ hd->no_sync);
+				hd->no_sync = x;
+			} else {
+				break; /* unknown keyword,syntax-error,... */
+			}
 		}
 		return len;
 	}
@@ -1978,8 +2079,9 @@ wd33c93_proc_info(struct Scsi_Host *inst
 		strcat(bp, tbuf);
 	}
 	if (hd->proc & PR_INFO) {
-		sprintf(tbuf, "\nclock_freq=%02x no_sync=%02x no_dma=%d",
-			hd->clock_freq, hd->no_sync, hd->no_dma);
+		sprintf(tbuf, "\nclock_freq=%02x no_sync=%02x no_dma=%d"
+			" dma_mode=%02x fast=%d",
+			hd->clock_freq, hd->no_sync, hd->no_dma, hd->dma_mode, hd->fast);
 		strcat(bp, tbuf);
 		strcat(bp, "\nsync_xfer[] =       ");
 		for (x = 0; x < 7; x++) {
========================================================================


=== 2nd Stage ==========================================================
--- stage1/drivers/scsi/wd33c93.h	Sun Feb 11 20:05:53 2007
+++ stage2/drivers/scsi/wd33c93.h	Sun Feb 11 20:05:53 2007
@@ -155,6 +155,9 @@
 #define WD33C93_FS_12_15 OWNID_FS_12
 #define WD33C93_FS_16_20 OWNID_FS_16

+   /* pass input-clock explicitely. accepted mhz values are 8-10,12-20 */
+#define WD33C93_FS_MHZ(mhz) (mhz)
+
    /* Control register */
 #define CTRL_HSP     0x01
 #define CTRL_HA      0x02
@@ -255,6 +258,7 @@
     uchar            no_dma;           /* set this flag to disable DMA */
     uchar            dma_mode;         /* DMA Burst Mode or Single Byte DMA */
     uchar            fast;             /* set this flag to enable Fast SCSI */
+    struct sx_period sx_table[9];      /* transfer periods for actual DTC-setting */
 #ifdef PROC_INTERFACE
     uchar            proc;             /* bitmask: what's in proc output */
 #ifdef PROC_STATISTICS
--- stage1/drivers/scsi/wd33c93.c	Sun Feb 11 22:01:17 2007
+++ stage2/drivers/scsi/wd33c93.c	Sun Feb 11 22:01:17 2007
@@ -71,7 +71,8 @@
  *	Richard Hirst <richard@xxxxxxxxxxxxxxxxxxx>  August 2000
  *
  * Added support for Burst Mode DMA and Fast SCSI. Enabled the use of
- * default_sx_per for asynchronous data transfers.
+ * default_sx_per for asynchronous data transfers. Added adjustment
+ * of transfer periods in sx_table to the actual input-clock.
  *  peter fuerst <post@xxxxxxxx>  February 2007
  */

@@ -91,6 +92,8 @@

 #include "wd33c93.h"

+#define optimum_sx_per(hostdata) (hostdata)->sx_table[1].period_ns
+

 #define WD33C93_VERSION    "1.26++"
 #define WD33C93_DATE       "10/Feb/2007"
@@ -310,20 +313,8 @@ read_1_byte(const wd33c93_regs regs)
 	return x;
 }

-static struct sx_period sx_table[] = {
-	{1, 0x20},
-	{252, 0x20},
-	{376, 0x30},
-	{500, 0x40},
-	{624, 0x50},
-	{752, 0x60},
-	{876, 0x70},
-	{1000, 0x00},
-	{0, 0}
-};
-
 static int
-round_period(unsigned int period)
+round_period(unsigned int period, const struct sx_period *sx_table)
 {
 	int x;

@@ -340,7 +331,8 @@ round_period(unsigned int period)
  * Calculate Synchronous Transfer Register value from SDTR code.
  */
 static uchar
-calc_sync_xfer(unsigned int period, unsigned int offset, unsigned int fast)
+calc_sync_xfer(unsigned int period, unsigned int offset, unsigned int fast,
+               const struct sx_period *sx_table)
 {
 	/* When doing Fast SCSI synchronous data transfers, the corresponding
 	 * value in 'sx_table' is two times the actually used transfer period.
@@ -354,7 +346,7 @@ calc_sync_xfer(unsigned int period, unsi
 		fast = 0;
 	}
 	period *= 4;		/* convert SDTR code to ns */
-	result = sx_table[round_period(period)].reg_value;
+	result = sx_table[round_period(period,sx_table)].reg_value;
 	result |= (offset < OPTIMUM_SX_OFF) ? offset : OPTIMUM_SX_OFF;
 	result |= fast;
 	return result;
@@ -923,7 +915,8 @@ wd33c93_intr(struct Scsi_Host *instance)
 				calc_sync_msg(hostdata->default_sx_per, 0,
 						0, hostdata->outgoing_msg + 4);
 			} else {
-				calc_sync_msg(OPTIMUM_SX_PER, OPTIMUM_SX_OFF,
+				calc_sync_msg(optimum_sx_per(hostdata),
+						OPTIMUM_SX_OFF,
 						hostdata->fast,
 						hostdata->outgoing_msg + 4);
 			}
@@ -1053,7 +1046,7 @@ wd33c93_intr(struct Scsi_Host *instance)
 				/* we want default_sx_per, not DEFAULT_SX_PER */
 				hostdata->sync_xfer[cmd->device->id] =
 					calc_sync_xfer(hostdata->default_sx_per
-						/ 4, 0, 0);
+						/ 4, 0, 0, hostdata->sx_table);
 			}
 			write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK);
 			hostdata->state = S_CONNECTED;
@@ -1077,7 +1070,7 @@ wd33c93_intr(struct Scsi_Host *instance)
 					/* default to default async period */
 					id = calc_sync_xfer(hostdata->
 							default_sx_per / 4, 0,
-							0);
+							0, hostdata->sx_table);
 					if (hostdata->sync_stat[cmd->device->id] !=
 					    SS_WAITING) {

@@ -1103,10 +1096,11 @@ wd33c93_intr(struct Scsi_Host *instance)
 					} else {
 						if (ucp[4]) /* well, sync transfer */
 							id = calc_sync_xfer(ucp[3], ucp[4],
-									hostdata->fast);
+									hostdata->fast,
+									hostdata->sx_table);
 						else if (ucp[3]) /* very unlikely... */
 							id = calc_sync_xfer(ucp[3], ucp[4],
-									0);
+									0, hostdata->sx_table);
 					}
 					hostdata->sync_xfer[cmd->device->id] = id;
 #ifdef SYNC_DEBUG
@@ -1542,7 +1536,7 @@ reset_wd33c93(struct Scsi_Host *instance
 	write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
 	write_wd33c93(regs, WD_SYNCHRONOUS_TRANSFER,
 		      calc_sync_xfer(hostdata->default_sx_per / 4,
-				     DEFAULT_SX_OFF, 0));
+				     DEFAULT_SX_OFF, 0, hostdata->sx_table));
 	write_wd33c93(regs, WD_COMMAND, WD_CMD_RESET);


@@ -1592,7 +1586,8 @@ wd33c93_host_reset(struct scsi_cmnd * SC
 	for (i = 0; i < 8; i++) {
 		hostdata->busy[i] = 0;
 		hostdata->sync_xfer[i] =
-			calc_sync_xfer(DEFAULT_SX_PER / 4, DEFAULT_SX_OFF, 0);
+			calc_sync_xfer(DEFAULT_SX_PER / 4, DEFAULT_SX_OFF,
+					0, hostdata->sx_table);
 		hostdata->sync_stat[i] = SS_UNSET;	/* using default sync values */
 	}
 	hostdata->input_Q = NULL;
@@ -1842,24 +1837,84 @@ check_setup_args(char *key, int *flags,
 }

 /*
+ * Calculate internal data-transfer-clock cycle from input-clock
+ * frequency (/MHz) and fill 'sx_table'.
+ *
+ * The original driver used to rely on a fixed sx_table, containing periods
+ * for (only) the lower limits of the respective input-clock-frequency ranges
+ * (8-10/12-15/16-20 MHz). Although it seems, that no problems ocurred with
+ * this setting so far, it might be desirable to adjust the transfer periods
+ * closer to the really attached, possibly 25% higher, input-clock, since
+ * - the wd33c93 may really use a significant shorter period, than it has
+ *   negotiated (eg. thrashing the target, which expects 4/8MHz, with 5/10MHz
+ *   instead).
+ * - the wd33c93 may ask the target for a lower transfer rate, than the target
+ *   is capable of (eg. negotiating for an assumed minimum of 252ns instead of
+ *   possible 200ns, which indeed shows up in tests as an approx. 10% lower
+ *   transfer rate).
+ */
+static inline unsigned int
+round_4(unsigned int x)
+{
+	switch (x & 3) {
+		case 1: --x;
+			break;
+		case 2: ++x;
+		case 3: ++x;
+	}
+	return x;
+}
+
+static void
+calc_sx_table(unsigned int mhz, struct sx_period sx_table[9])
+{
+	unsigned int d, i;
+	if (mhz < 11)
+		d = 2;	/* divisor for  8-10 MHz input-clock */
+	else if (mhz < 16)
+		d = 3;	/* divisor for 12-15 MHz input-clock */
+	else
+		d = 4;	/* divisor for 16-20 MHz input-clock */
+
+	d = (100000 * d) / 2 / mhz; /* 100 x DTCC / nanosec */
+
+	sx_table[0].period_ns = 1;
+	sx_table[0].reg_value = 0x20;
+	for (i = 1; i < 8; i++) {
+		sx_table[i].period_ns = round_4((i+1)*d / 100);
+		sx_table[i].reg_value = (i+1)*0x10;
+	}
+	sx_table[7].reg_value = 0;
+	sx_table[8].period_ns = 0;
+	sx_table[8].reg_value = 0;
+}
+
+/*
  * check and, maybe, map an init- or "clock:"- argument.
  */
 static uchar
-set_clk_freq(int freq)
+set_clk_freq(int freq, int *mhz)
 {
-	if (freq != WD33C93_FS_8_10 &&
-	    freq != WD33C93_FS_12_15 &&
-	    freq != WD33C93_FS_16_20)
-		if (freq > 7 && freq < 11)
-			freq = WD33C93_FS_8_10;
+	int x = freq;
+	if (WD33C93_FS_8_10 == freq)
+		freq = 8;
+	else if (WD33C93_FS_12_15 == freq)
+		freq = 12;
+	else if (WD33C93_FS_16_20 == freq)
+		freq = 16;
+	else if (freq > 7 && freq < 11)
+		x = WD33C93_FS_8_10;
 		else if (freq > 11 && freq < 16)
-			freq = WD33C93_FS_12_15;
+		x = WD33C93_FS_12_15;
 		else if (freq > 15 && freq < 21)
-			freq = WD33C93_FS_16_20;
-		else
+		x = WD33C93_FS_16_20;
+	else {
 			/* Hmm, wouldn't it be safer to assume highest freq here? */
-			freq = WD33C93_FS_8_10;
-	return freq;
+		x = WD33C93_FS_8_10;
+		freq = 8;
+	}
+	*mhz = freq;
+	return x;
 }

 /*
@@ -1889,7 +1944,8 @@ wd33c93_init(struct Scsi_Host *instance,
 	hostdata = (struct WD33C93_hostdata *) instance->hostdata;

 	hostdata->regs = regs;
-	hostdata->clock_freq = set_clk_freq(clock_freq);
+	hostdata->clock_freq = set_clk_freq(clock_freq, &i);
+	calc_sx_table(i, hostdata->sx_table);
 	hostdata->dma_setup = setup;
 	hostdata->dma_stop = stop;
 	hostdata->dma_bounce_buffer = NULL;
@@ -1897,7 +1953,8 @@ wd33c93_init(struct Scsi_Host *instance,
 	for (i = 0; i < 8; i++) {
 		hostdata->busy[i] = 0;
 		hostdata->sync_xfer[i] =
-			calc_sync_xfer(DEFAULT_SX_PER / 4, DEFAULT_SX_OFF, 0);
+			calc_sync_xfer(DEFAULT_SX_PER / 4, DEFAULT_SX_OFF,
+					0, hostdata->sx_table);
 		hostdata->sync_stat[i] = SS_UNSET;	/* using default sync values */
 #ifdef PROC_STATISTICS
 		hostdata->cmd_cnt[i] = 0;
@@ -1932,8 +1989,10 @@ wd33c93_init(struct Scsi_Host *instance,
 #endif
 #endif

-	if (check_setup_args("clock", &flags, &val, buf))
-		hostdata->clock_freq = set_clk_freq(val);
+	if (check_setup_args("clock", &flags, &val, buf)) {
+		hostdata->clock_freq = set_clk_freq(val, &val);
+		calc_sx_table(val, hostdata->sx_table);
+	}

 	if (check_setup_args("nosync", &flags, &val, buf))
 		hostdata->no_sync = val;
@@ -1943,7 +2002,8 @@ wd33c93_init(struct Scsi_Host *instance,

 	if (check_setup_args("period", &flags, &val, buf))
 		hostdata->default_sx_per =
-		    sx_table[round_period((unsigned int) val)].period_ns;
+		    hostdata->sx_table[round_period((unsigned int) val,
+		                                    hostdata->sx_table)].period_ns;

 	if (check_setup_args("disconnect", &flags, &val, buf)) {
 		if ((val >= DIS_NEVER) && (val <= DIS_ALWAYS))
@@ -2042,7 +2102,8 @@ wd33c93_proc_info(struct Scsi_Host *inst
 			} else if (!strncmp(bp, "period:", 7)) {
 				x = simple_strtoul(bp+7, &bp, 0);
 				hd->default_sx_per =
-				    sx_table[round_period((unsigned int) x)].period_ns;
+				    hd->sx_table[round_period((unsigned int) x,
+				                              hd->sx_table)].period_ns;
 			} else if (!strncmp(bp, "resync:", 7)) {
 				set_resync(hd, (int)simple_strtoul(bp+7, &bp, 0));
 			} else if (!strncmp(bp, "proc:", 5)) {
========================================================================
-
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]
  Powered by Linux