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: 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 #include @@ -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<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