From davem@extro.ucc.su.OZ.AU Sun Apr  3 16:05:30 EDT 1994
Article: 7272 of comp.os.linux.development
Newsgroups: comp.os.linux.development
Path: bigblue.oit.unc.edu!concert!news.duke.edu!MathWorks.Com!news.kei.com!hookup!usc!howland.reston.ans.net!agate!ihnp4.ucsd.edu!munnari.oz.au!metro!extro.ucc.su.OZ.AU!davem
From: davem@extro.ucc.su.OZ.AU (David Monro)
Subject: Patch for IDE performance on second AT controller
Message-ID: <davem.765049755@extro.ucc.su.OZ.AU>
Keywords: kernel patch IDE atdisk
Sender: news@ucc.su.OZ.AU
Nntp-Posting-Host: extro.ucc.su.oz.au
Organization: Information Services, Sydney University, Sydney, NSW, Australia
Date: Wed, 30 Mar 1994 17:49:15 GMT
Lines: 377


Here is a patch to apply the multimode ide patches to the second harddrive
controller driver created by applying the atdisk2 patches. The patch
is for atdisk2-0.9 patches applied to a v1.0.4 kernel, but should work
for any kernel from pre-1.0 to v1.0.5.

NOTE - for those with Western Digital and recent Conner drives, change
line 327 of the resulting hd1.c from
                        if ((i = ib[47] & 0xff) >= 32)
to
                        if ((i = ib[47] & 0xff) >= 16)
Otherwise it will decide that these drives are "older drives" and won't
enable multi mode. This applies to the original patch for hd.c as well
as to this patch for hd1.c

For those with other drives, define VERBOSE_DRIVEID as 1 at the top of
the file and see what the MaxMultSect field is when you boot. If it is
 16, you can probably use the same trick, but some drives are known to
have problems, I don;t know which ones though.

Thanks very much to Mark Lord for the original IDE performance package,
and to Delman Lee for the atdisk2 package.

	David Monro


--- hd.c.orig	Fri Jan 21 12:50:11 1994
+++ hd1.c	Sat Mar 12 14:47:45 1994
@@ -14,8 +14,14 @@
  *
  *  Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
  *  in the early extended-partition checks and added DM partitions
+ *
+ *  IDE IRQ-unmask & drive-id & multiple-mode code added by Mark Lord.
  */
 
+#define HD_UNMASK_INTR	1	/* set to 0 to mask other IRQs during hd I/O */
+#define VERBOSE_DRIVEID 0	/* set to 1 for more drive info at boot time */
+#define MAX_MULTIPLE	8	/* set to 1 to disable multiple mode support */
 
 #include <linux/errno.h>
 #include <linux/signal.h>
@@ -208,6 +213,133 @@
 	outb_p(cmd,++port);
 }
 
+#define WIN_MULTREAD	0xC4	/* read multiple sectors	*/
+#define WIN_MULTWRITE	0xC5	/* write multiple sectors	*/
+#define WIN_SETMULT	0xC6	/* enable read multiple		*/
+#define WIN_IDENTIFY	0xEC	/* ask drive to identify itself	*/
+
+static int mult_count[MAX_HD] = {0,}, writing_mult; 
+
+#if VERBOSE_DRIVEID
+
+char *cfg_str[] =
+{	"", " HardSect", " SoftSect", " NotMFM", " HdSw>15uSec", " SpinMotCtl",
+	" Fixed", " Removeable", " DTR<=5Mbs", " DTR>5Mbs", " DTR>10Mbs",
+	" RotSpdTol>.5%", " dStbOff", " TrkOff", " FmtGapReq", "",
+};
+
+char *SlowMedFast[] = {"slow", "medium", "fast"};
+char *BuffType[] = {"?", "1Sect", "DualPort", "DualPortCache"};
+
+#define YN(b)	(((b)==0)?"no":"yes")
+
+static void rawstring (char *prefix, char *s, int n)
+{
+	printk(prefix);
+	if (*s) {
+		int i;
+		for (i=0; i < n && s[i^1] == ' '; ++i); /* strip blanks */
+		for (; i < n && s[i^1]; ++i)
+			if (s[i^1] != ' ' || ((i+1) < n && s[(i+1)^1] != ' '))
+				printk("%c",s[i^1]);
+	}
+}
+
+static void dmpstr (char *prefix, unsigned int i, char *s[], unsigned int maxi)
+{
+	printk(prefix);
+	printk( (i > maxi) ? "?" : s[i] );
+}
+
+static void dump_identity (unsigned int dev, unsigned short ib[])
+{
+	int i;
+	char dashes[] = "\n+-------------------------------------------------------------------+\n";
+	printk (dashes);
+	printk ("hd1%c:  Drive Identification Info:\n", dev+'a');
+	rawstring (" Model=",(char *)&ib[27],40);
+	rawstring (", FwRev=",(char *)&ib[23],8);
+	rawstring (", SerialNo=",(char *)&ib[10],20);
+	printk ("\n Config={");
+	for (i=0; i<=15; i++) if (ib[0] & (1<<i)) printk (cfg_str[i]);
+	printk (" }\n");
+	printk (" Default c/h/s=%d/%d/%d, TrkSize=%d, SectSize=%d, ECCbytes=%d\n",
+		ib[1],ib[3],ib[6],ib[4],ib[5], ib[22]);
+	dmpstr (" BuffType=",ib[20],BuffType,3);
+	ib[47] &= 0xFF;
+	printk (", BuffSize=%dKB, MaxMultSect=%d\n", ib[21]/2, ib[47]);
+	printk (" Features: DblWordIO=%s, LBA=%s, DMA=%s",
+		YN(ib[48]&1),YN(ib[49]&0x20),YN(ib[49]&0x10));
+	dmpstr (", tPIO=",ib[51]>>8,SlowMedFast,2);
+	if (ib[49]&0x10 && (ib[53]&1)==0)
+		dmpstr (", tDMA=",ib[52]>>8,SlowMedFast,2);
+	printk ("\n (%s): Current c/h/s=%d/%d/%d, TotSect=%d",
+		(((ib[53]&1)==0)?"maybe":"valid"),
+		ib[54],ib[55],ib[56],*(int *)&ib[57]);
+	if (ib[49]&0x20)
+		printk (", LBAsect=%d", *(int *)&ib[60]);
+	printk ("\n (%s): CurMultSect=%d", ((ib[53]&1)==0)?"maybe":"valid",
+		(ib[59]&0x10)?ib[59]&0xFF:0);
+	if (ib[49]&0x10)
+		printk (", DMA-1w=%04X, DMA-mw=%04X", ib[62], ib[63]);
+	printk ("%s\n",dashes);
+}
+#endif /* VERBOSE_DRIVEID */
+
+static void set_multiple_intr(void)
+{
+	unsigned int dev = DEVICE_NR(CURRENT->dev);
+
+	if (inb_p(HD1_STATUS)&(BUSY_STAT|ERR_STAT)) {
+		mult_count[dev] = 1;	/* disable multiple mode */
+		printk ("  hd1%c: set multiple mode failed\n", dev+'a');
+	} else {
+		printk ("  hd1%c: enabled %d-sector multiple mode\n",
+			dev+'a', mult_count[dev]);
+	}
+	do_hd_request();
+	return;
+}
+
+static void identify_intr(void)
+{
+	unsigned short ib[64];
+	unsigned int dev = DEVICE_NR(CURRENT->dev);
+
+	if (inb_p(HD1_STATUS)&(BUSY_STAT|ERR_STAT))
+		printk ("  hd1%c: multiple mode not supported\n", dev+'a');
+	else {
+		insw(HD1_DATA,(char *)ib,64); /* get first 128 ID bytes */
+		sti();
+#if VERBOSE_DRIVEID
+		dump_identity(dev, ib);
+#endif /* VERBOSE_DRIVEID */
+		if (ib[27]) {
+			int i;
+			for (i=27; i<= 46; i++)
+				ib[i] = (ib[i]>>8) | (ib[i]<<8);
+			printk ("  hd1%c: %-.40s (%dMB IDE w/%dKB Cache)\n",
+				dev+'a', (char *)&ib[27], ib[1]*ib[3]*ib[6] / 2048, ib[21]>>1);
+			/* skip troublesome older drives with (MaxMult < 32) */
+			if ((i = ib[47] & 0xff) >= 32)
+				mult_count[dev] = MAX_MULTIPLE;
+			else
+				printk ("  hd1%c: older drive, multiple mode not enabled\n", dev+'a');
+		}
+		insw(HD1_DATA,(char *)ib,64); /* flush remaining 384 ID bytes */
+		insw(HD1_DATA,(char *)ib,64);
+ 		cli();
+		insw(HD1_DATA,(char *)ib,64);
+		if (mult_count[dev] > 1) {   /* try to enable multiple mode */
+			hd_out(dev,mult_count[dev],0,0,0,WIN_SETMULT,&set_multiple_intr);
+			if (!reset)
+				return;
+		}
+	}
+	do_hd_request();
+	return;
+}
+
 static int drive_busy(void)
 {
 	unsigned int i;
@@ -243,6 +375,11 @@
 
 repeat:
 	if (reset) {
+		for (i=0; i < NR_HD; i++) {
+			if (mult_count[i] > 1)
+				printk ("  hd1%c: multiple mode disabled\n", i+'a');
+			mult_count[i] = 1;	/* disable multiple mode */
+		}
 		reset = 0;
 		i = -1;
 #ifndef HD1_DontReset
@@ -310,8 +447,8 @@
 
 static void read_intr(void)
 {
-	int i;
-	int retries = 100000;
+	unsigned int dev = DEVICE_NR(CURRENT->dev);
+	int i, retries = 100000, msect = mult_count[dev];
 
 	do {
 		i = (unsigned) inb_p(HD1_STATUS);
@@ -333,22 +470,33 @@
 	do_hd_request();
 	return;
 ok_to_read:
-	insw(HD1_DATA,CURRENT->buffer,256);
-	CURRENT->errors = 0;
-	CURRENT->buffer += 512;
-	CURRENT->sector++;
-	i = --CURRENT->nr_sectors;
-	--CURRENT->current_nr_sectors;
+#if HD_UNMASK_INTR
+	sti();				/* permit other IRQs during xfer */
+#endif
+	if ((i = CURRENT->current_nr_sectors) > msect)
+		i = msect;
+	msect -= i;
+	CURRENT->sector += i;
+	CURRENT->current_nr_sectors -= i;
+	insw(HD1_DATA,CURRENT->buffer,(i<<8)-1); /* xfer all but final word */
+	CURRENT->buffer += i<<9;	/* incr buffer ptr by byte count */
+	cli();				/* mask IRQs before completing xfer */
+	*((short *)(CURRENT->buffer-2)) = inw(HD1_DATA);	/* xfer final word */
+
 #ifdef DEBUG
-	printk("hd1%d : sector = %d, %d remaining to buffer = %08x\n",
-		MINOR(CURRENT->dev), CURRENT->sector, i, CURRENT-> 
-		buffer);
+	printk("hd1%c: read: %d sectors(%d-%d), remaining=%d, buffer=%08x\n",
+		dev+'a', i, CURRENT->sector-i, CURRENT->sector-1,
+		CURRENT->nr_sectors, (int) CURRENT->buffer);
 #endif
-	if (!i || (CURRENT->bh && !SUBSECTOR(i)))
+	CURRENT->nr_sectors -= i;
+	i = CURRENT->nr_sectors;	/* in case it's freed by end_request */
+	if (!CURRENT->current_nr_sectors)
 		end_request(1);
 	if (i > 0) {
+		CURRENT->errors = 0;
+		if (msect)
+			goto ok_to_read;
 		SET_INTR(&read_intr);
-		sti();
 		return;
 	}
 	(void) inb_p(HD1_STATUS);
@@ -370,8 +518,19 @@
 			continue;
 		if ((i & STAT_MASK) != STAT_OK)
 			break;
-		if ((CURRENT->nr_sectors <= 1) || (i & DRQ_STAT))
-			goto ok_to_write;
+		if (!(i & DRQ_STAT)) {
+			if (writing_mult || CURRENT->nr_sectors <= 1) {
+				end_request(1);
+#if (HD_DELAY > 0)
+				last_req = read_timer();
+#endif
+				do_hd_request();
+				return;
+			}
+		} else {
+			if (CURRENT->nr_sectors > 1)
+				goto ok_to_write;
+		}
 	} while (--retries > 0);
 	sti();
 	printk("HD1: write_intr: status = 0x%02x\n",i);
@@ -384,23 +543,19 @@
 	do_hd_request();
 	return;
 ok_to_write:
-	CURRENT->sector++;
-	i = --CURRENT->nr_sectors;
-	--CURRENT->current_nr_sectors;
+	CURRENT->errors = 0;
 	CURRENT->buffer += 512;
-	if (!i || (CURRENT->bh && !SUBSECTOR(i)))
+	CURRENT->sector++;
+	CURRENT->nr_sectors--;
+	if (!--CURRENT->current_nr_sectors)
 		end_request(1);
-	if (i > 0) {
-		SET_INTR(&write_intr);
-		outsw(HD1_DATA,CURRENT->buffer,256);
-		sti();
-	} else {
-#if (HD_DELAY > 0)
-		last_req = read_timer();
+#if HD_UNMASK_INTR
+	sti();
 #endif
-		do_hd_request();
-	}
-	return;
+	outsw(HD1_DATA,CURRENT->buffer,255);
+	cli();
+	SET_INTR(&write_intr);
+	outw(((short *)CURRENT->buffer)[255],HD1_DATA);
 }
 
 static void recal_intr(void)
@@ -482,7 +637,6 @@
 		for (i=0; i < NR_HD; i++)
 			recalibrate[i] = 1;
 		reset_hd();
-		sti();
 		return;
 	}
 	if (recalibrate[dev]) {
@@ -490,11 +644,41 @@
 		hd_out(dev,hd1_info[dev].sect,0,0,0,WIN_RESTORE,&recal_intr);
 		if (reset)
 			goto repeat;
-		sti();
 		return;
 	}	
+	if (!mult_count[dev]) {
+		mult_count[dev] = 1;	/* as default, disable multiple mode */
+		hd_out(dev,0,0,0,0,WIN_IDENTIFY,identify_intr);
+		if (reset)
+			goto repeat;
+		return;
+	}
+	if (CURRENT->cmd == READ) {
+		unsigned int cmd = mult_count[dev] > 1 ? WIN_MULTREAD : WIN_READ;
+		hd_out(dev,nsect,sec,head,cyl,cmd,&read_intr);
+		if (reset)
+			goto repeat;
+		return;
+	}
 	if (CURRENT->cmd == WRITE) {
-		hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
+		unsigned int cmd, wcnt;
+		if ((wcnt = mult_count[dev]) > 1
+		 && nsect <= wcnt
+		 && nsect == CURRENT->current_nr_sectors) {
+			wcnt = (nsect<<8) - 1;
+			writing_mult = 1;
+			cmd = WIN_MULTWRITE;
+		} else {
+			wcnt = 255;
+			writing_mult = 0;
+			cmd = WIN_WRITE;
+		}
+#ifdef DEBUG
+		printk("hd1%c: writing %d sectors(%d-%d), buffer=%08x\n",
+			dev+'a', (wcnt+1)>>8, CURRENT->sector,
+			CURRENT->sector+nsect-1, (int) CURRENT->buffer);
+#endif
+		hd_out(dev,nsect,sec,head,cyl,cmd,NULL);
 		if (reset)
 			goto repeat;
 		if (wait_DRQ()) {
@@ -502,15 +686,13 @@
 			bad_rw_intr();
 			goto repeat;
 		}
-		outsw(HD1_DATA,CURRENT->buffer,256);
-		sti();
-		return;
-	}
-	if (CURRENT->cmd == READ) {
-		hd_out(dev,nsect,sec,head,cyl,WIN_READ,&read_intr);
-		if (reset)
-			goto repeat;
+#if HD_UNMASK_INTR
 		sti();
+#endif
+		outsw(HD1_DATA,CURRENT->buffer,wcnt);
+		cli();
+		SET_INTR(&write_intr);
+		outw(((short *)CURRENT->buffer)[wcnt],HD1_DATA);
 		return;
 	}
 	panic("unknown hd1-command");
-- 
mlord@bnr.ca	Mark Lord	BNR Ottawa,Canada	613-763-7482



