patch-2.3.1 linux/drivers/block/pdc4030.c

Next file: linux/drivers/block/piix.c
Previous file: linux/drivers/block/pdc202xx.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.0/linux/drivers/block/pdc4030.c linux/drivers/block/pdc4030.c
@@ -1,17 +1,18 @@
 /*  -*- linux-c -*-
- *  linux/drivers/block/pdc4030.c	Version 0.08  Nov 30, 1997
+ *  linux/drivers/block/pdc4030.c	Version 0.10  Jan 25, 1999
  *
- *  Copyright (C) 1995-1998  Linus Torvalds & authors (see below)
+ *  Copyright (C) 1995-1999  Linus Torvalds & authors (see below)
  */
 
 /*
  *  Principal Author/Maintainer:  peterd@pnd-pc.demon.co.uk
  *
  *  This file provides support for the second port and cache of Promise
- *  IDE interfaces, e.g. DC4030, DC5030.
+ *  IDE interfaces, e.g. DC4030VL, DC4030VL-1 and DC4030VL-2.
  *
  *  Thanks are due to Mark Lord for advice and patiently answering stupid
- *  questions, and all those mugs^H^H^H^Hbrave souls who've tested this.
+ *  questions, and all those mugs^H^H^H^Hbrave souls who've tested this,
+ *  especially Andre Hedrick.
  *
  *  Version 0.01	Initial version, #include'd in ide.c rather than
  *                      compiled separately.
@@ -29,6 +30,9 @@
  *  Version 0.07	Added support for DC4030 variants
  *			Secondary interface autodetection
  *  Version 0.08	Renamed to pdc4030.c
+ *  Version 0.09	Obsolete - never released - did manual write request
+ *			splitting before max_sectors[major][minor] available.
+ *  Version 0.10	Updated for 2.1 series of kernels
  */
 
 /*
@@ -37,12 +41,17 @@
  *
  *	'linux ide0=dc4030'
  *
- * As before, it seems that somewhere around 3Megs when writing, bad things
- * start to happen [timeouts/retries -ml]. If anyone can give me more feedback,
- * I'd really appreciate it.  [email: peterd@pnd-pc.demon.co.uk]
+ * It should now work as a second controller also ('ide1=dc4030') but only
+ * if you DON'T have BIOS V4.44, which has a bug. If you have this and EPROM
+ * programming facilities, I can tell you what to fix...
  *
+ * As of January 1999, Promise Technology Inc. have finally supplied me with
+ * some technical information which has shed a glimmer of light on some of the
+ * problems I was having, especially with writes. 
  */
 
+#define DEBUG_READ
+#define DEBUG_WRITE
 
 #undef REALLY_SLOW_IO		/* most systems can safely undef this */
 
@@ -54,9 +63,11 @@
 #include <linux/ioport.h>
 #include <linux/blkdev.h>
 #include <linux/hdreg.h>
+#include <linux/ide.h>
+
 #include <asm/io.h>
 #include <asm/irq.h>
-#include "ide.h"
+
 #include "pdc4030.h"
 
 /* This is needed as the controller may not interrupt if the required data is
@@ -124,40 +135,48 @@
 {
 	ide_hwif_t *hwif = hwif_required;
         ide_drive_t *drive;
-	ide_hwif_t *second_hwif;
+	ide_hwif_t *hwif2;
 	struct dc_ident ident;
 	int i;
 	
 	if (!hwif) return 0;
 
 	drive = &hwif->drives[0];
-	second_hwif = &ide_hwifs[hwif->index+1];
-	if(hwif->chipset == ide_pdc4030) /* we've already been found ! */
-	    return 1;
-
-	if(IN_BYTE(IDE_NSECTOR_REG) == 0xFF || IN_BYTE(IDE_SECTOR_REG) == 0xFF)
-	{
-	    return 0;
+	hwif2 = &ide_hwifs[hwif->index+1];
+	if (hwif->chipset == ide_pdc4030) /* we've already been found ! */
+		return 1;
+
+	if (IN_BYTE(IDE_NSECTOR_REG) == 0xFF || IN_BYTE(IDE_SECTOR_REG) == 0xFF) {
+		return 0;
 	}
 	OUT_BYTE(0x08,IDE_CONTROL_REG);
-	if(pdc4030_cmd(drive,PROMISE_GET_CONFIG)) {
-	    return 0;
+	if (pdc4030_cmd(drive,PROMISE_GET_CONFIG)) {
+		return 0;
 	}
-	if(ide_wait_stat(drive,DATA_READY,BAD_W_STAT,WAIT_DRQ)) {
-	    printk("%s: Failed Promise read config!\n",hwif->name);
-	    return 0;
+	if (ide_wait_stat(drive,DATA_READY,BAD_W_STAT,WAIT_DRQ)) {
+		printk(KERN_INFO
+			"%s: Failed Promise read config!\n",hwif->name);
+		return 0;
 	}
 	ide_input_data(drive,&ident,SECTOR_WORDS);
-	if(ident.id[1] != 'P' || ident.id[0] != 'T') {
-            return 0;
+	if (ident.id[1] != 'P' || ident.id[0] != 'T') {
+		return 0;
 	}
-	printk("%s: Promise caching controller, ",hwif->name);
+	printk(KERN_INFO "%s: Promise caching controller, ",hwif->name);
 	switch(ident.type) {
-            case 0x43:	printk("DC4030VL-2, "); break;
-            case 0x41:	printk("DC4030VL-1, "); break;
-	    case 0x40:	printk("DC4030VL, "); break;
-            default:	printk("unknown - type 0x%02x - please report!\n"
+		case 0x43:	printk("DC4030VL-2, "); break;
+		case 0x41:	printk("DC4030VL-1, "); break;
+		case 0x40:	printk("DC4030VL, "); break;
+		default:
+			printk("unknown - type 0x%02x - please report!\n"
 			       ,ident.type);
+			printk("Please e-mail the following data to "
+			       "promise@pnd-pc.demon.co.uk along with\n"
+			       "a description of your card and drives:\n");
+			for (i=0; i < 0x90; i++) {
+				printk("%02x ", ((unsigned char *)&ident)[i]);
+				if ((i & 0x0f) == 0x0f) printk("\n");
+			}
 			return 0;
 	}
 	printk("%dKB cache, ",(int)ident.cache_mem);
@@ -167,27 +186,35 @@
             default:   hwif->irq = 15; break;
 	}
 	printk("on IRQ %d\n",hwif->irq);
-	hwif->chipset     = second_hwif->chipset    = ide_pdc4030;
-	hwif->mate        = second_hwif;
-	second_hwif->mate = hwif;
-	second_hwif->channel = 1;
-	hwif->selectproc  = second_hwif->selectproc = &promise_selectproc;
+	hwif->chipset	= hwif2->chipset = ide_pdc4030;
+	hwif->mate	= hwif2;
+	hwif2->mate	= hwif;
+	hwif2->channel	= 1;
+	hwif->selectproc = hwif2->selectproc = &promise_selectproc;
 /* Shift the remaining interfaces down by one */
 	for (i=MAX_HWIFS-1 ; i > hwif->index+1 ; i--) {
 		ide_hwif_t *h = &ide_hwifs[i];
 
-		printk("Shifting i/f %d values to i/f %d\n",i-1,i);
-		ide_init_hwif_ports(h->io_ports, (h-1)->io_ports[IDE_DATA_OFFSET], NULL);
-		h->io_ports[IDE_CONTROL_OFFSET] = (h-1)->io_ports[IDE_CONTROL_OFFSET];
+#ifdef DEBUG
+		printk(KERN_DEBUG "Shifting i/f %d values to i/f %d\n",i-1,i);
+#endif
+		ide_init_hwif_ports(&h->hw, (h-1)->io_ports[IDE_DATA_OFFSET], 0, NULL);
+		memcpy(h->io_ports, h->hw.io_ports, sizeof(h->io_ports));
 		h->noprobe = (h-1)->noprobe;
 	}
-	ide_init_hwif_ports(second_hwif->io_ports, hwif->io_ports[IDE_DATA_OFFSET], NULL);
-	second_hwif->io_ports[IDE_CONTROL_OFFSET] = hwif->io_ports[IDE_CONTROL_OFFSET];
-	second_hwif->irq = hwif->irq;
+	ide_init_hwif_ports(&hwif2->hw, hwif->io_ports[IDE_DATA_OFFSET], 0, NULL);
+	memcpy(hwif2->io_ports, hwif->hw.io_ports, sizeof(hwif2->io_ports));
+	hwif2->irq = hwif->irq;
+	hwif2->hw.irq = hwif->hw.irq = hwif->irq;
 	for (i=0; i<2 ; i++) {
-            hwif->drives[i].io_32bit = 3;
-	    second_hwif->drives[i].io_32bit = 3;
-	    if(!ident.current_tm[i+2].cyl) second_hwif->drives[i].noprobe=1;
+		hwif->drives[i].io_32bit = 3;
+		hwif2->drives[i].io_32bit = 3;
+		hwif->drives[i].keep_settings = 1;
+		hwif2->drives[i].keep_settings = 1;
+		if (!ident.current_tm[i].cyl)
+			hwif->drives[i].noprobe = 1;
+		if (!ident.current_tm[i+2].cyl)
+			hwif2->drives[i].noprobe = 1;
 	}
         return 1;
 }
@@ -198,7 +225,7 @@
 static void promise_read_intr (ide_drive_t *drive)
 {
 	byte stat;
-	int i;
+	int total_remaining;
 	unsigned int sectors_left, sectors_avail, nsect;
 	struct request *rq;
 
@@ -209,99 +236,140 @@
 
 read_again:
 	do {
-	    sectors_left = IN_BYTE(IDE_NSECTOR_REG);
-	    IN_BYTE(IDE_SECTOR_REG);
+		sectors_left = IN_BYTE(IDE_NSECTOR_REG);
+		IN_BYTE(IDE_SECTOR_REG);
 	} while (IN_BYTE(IDE_NSECTOR_REG) != sectors_left);
 	rq = HWGROUP(drive)->rq;
 	sectors_avail = rq->nr_sectors - sectors_left;
+	if (!sectors_avail)
+		goto read_again;
 
 read_next:
 	rq = HWGROUP(drive)->rq;
-	if ((nsect = rq->current_nr_sectors) > sectors_avail)
+	nsect = rq->current_nr_sectors;
+	if (nsect > sectors_avail)
 		nsect = sectors_avail;
 	sectors_avail -= nsect;
 	ide_input_data(drive, rq->buffer, nsect * SECTOR_WORDS);
-#ifdef DEBUG
-	printk("%s:  promise_read: sectors(%ld-%ld), buffer=0x%08lx, "
-	       "remaining=%ld\n", drive->name, rq->sector, rq->sector+nsect-1, 
-	       (unsigned long) rq->buffer+(nsect<<9), rq->nr_sectors-nsect);
+#ifdef DEBUG_READ
+	printk(KERN_DEBUG "%s:  promise_read: sectors(%ld-%ld), "
+	       "buf=0x%08lx, rem=%ld\n", drive->name, rq->sector,
+	       rq->sector+nsect-1, (unsigned long) rq->buffer,
+	       rq->nr_sectors-nsect);
 #endif
 	rq->sector += nsect;
 	rq->buffer += nsect<<9;
 	rq->errors = 0;
-	i = (rq->nr_sectors -= nsect);
-	if ((rq->current_nr_sectors -= nsect) <= 0)
+	rq->nr_sectors -= nsect;
+	total_remaining = rq->nr_sectors;
+	if ((rq->current_nr_sectors -= nsect) <= 0) {
 		ide_end_request(1, HWGROUP(drive));
-	if (i > 0) {
+	}
+/*
+ * Now the data has been read in, do the following:
+ * 
+ * if there are still sectors left in the request, 
+ *   if we know there are still sectors available from the interface,
+ *     go back and read the next bit of the request.
+ *   else if DRQ is asserted, there are more sectors available, so
+ *     go back and find out how many, then read them in.
+ *   else if BUSY is asserted, we are going to get an interrupt, so
+ *     set the handler for the interrupt and just return
+ */
+	if (total_remaining > 0) {
 		if (sectors_avail)
-		    goto read_next;
+			goto read_next;
 		stat = GET_STAT();
-		if(stat & DRQ_STAT)
-		    goto read_again;
-		if(stat & BUSY_STAT) {
-		    ide_set_handler (drive, &promise_read_intr, WAIT_CMD);
-		    return;
+		if (stat & DRQ_STAT)
+			goto read_again;
+		if (stat & BUSY_STAT) {
+#ifdef DEBUG_READ
+			printk(KERN_DEBUG "%s: promise_read: waiting for"
+			       "interrupt\n", drive->name);
+#endif 
+			ide_set_handler (drive, &promise_read_intr, WAIT_CMD);
+			return;
 		}
-		printk("Ah! promise read intr: sectors left !DRQ !BUSY\n");
+		printk(KERN_ERR "%s: Eeek! promise_read_intr: sectors left "
+		       "!DRQ !BUSY\n", drive->name);
 		ide_error(drive, "promise read intr", stat);
 	}
 }
 
 /*
+ * promise_write_intr()
+ * This interrupt is called after the particularly odd polling for completion
+ * of the write request, once all the data has been sent.
+ */ 
+static void promise_write_intr(ide_drive_t *drive)
+{
+	byte stat;
+	int i;
+	struct request *rq;
+
+	if (!OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
+		ide_error(drive, "promise_write_intr", stat);
+	}
+
+#ifdef DEBUG_WRITE
+	printk(KERN_DEBUG "%s: Write complete - end_request\n", drive->name);
+#endif
+	rq = HWGROUP(drive)->rq;
+	for (i = rq->nr_sectors; i > 0;) {
+		i -= rq->current_nr_sectors;
+		ide_end_request(1, HWGROUP(drive));
+	}
+}
+
+/*
  * promise_write_pollfunc() is the handler for disk write completion polling.
  */
 static void promise_write_pollfunc (ide_drive_t *drive)
 {
-	int i;
-	ide_hwgroup_t *hwgroup = HWGROUP(drive);
-	struct request *rq;
+	if (IN_BYTE(IDE_NSECTOR_REG) != 0) {
+		if (time_before(jiffies, HWGROUP(drive)->poll_timeout)) {
+			ide_set_handler (drive, &promise_write_pollfunc, 1);
+			return; /* continue polling... */
+		}
+		printk(KERN_ERR "%s: write timed-out!\n",drive->name);
+		ide_error (drive, "write timeout", GET_STAT());
+		return;
+	}
 
-        if (IN_BYTE(IDE_NSECTOR_REG) != 0) {
-            if (time_before(jiffies, hwgroup->poll_timeout)) {
-                ide_set_handler (drive, &promise_write_pollfunc, 1);
-                return; /* continue polling... */
-            }
-            printk("%s: write timed-out!\n",drive->name);
-            ide_error (drive, "write timeout", GET_STAT());
-            return;
-        }
-        
+#ifdef DEBUG_WRITE
+	printk(KERN_DEBUG "%s: Doing last 4 sectors\n", drive->name);
+#endif
 	ide_multwrite(drive, 4);
-        rq = hwgroup->rq;
-        for (i = rq->nr_sectors; i > 0;) {
-            i -= rq->current_nr_sectors;
-            ide_end_request(1, hwgroup);
-        }
-        return;
+	ide_set_handler(drive, &promise_write_intr, WAIT_CMD);
+	return;
 }
 
 /*
  * promise_write() transfers a block of one or more sectors of data to a
  * drive as part of a disk write operation. All but 4 sectors are transfered
  * in the first attempt, then the interface is polled (nicely!) for completion
- * before the final 4 sectors are transfered. Don't ask me why, but this is
- * how it's done in the drivers for other O/Ses. There is no interrupt
- * generated on writes, which is why we have to do it like this.
+ * before the final 4 sectors are transfered. The interrupt generated on 
+ * writes occurs after this process, which is why I got it wrong for so long!
  */
 static void promise_write (ide_drive_t *drive)
 {
-    ide_hwgroup_t *hwgroup = HWGROUP(drive);
-    struct request *rq = &hwgroup->wrq;
-    int i;
-
-    if (rq->nr_sectors > 4) {
-        ide_multwrite(drive, rq->nr_sectors - 4);
-        hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
-        ide_set_handler (drive, &promise_write_pollfunc, 1);
-        return;
-    } else {
-        ide_multwrite(drive, rq->nr_sectors);
-        rq = hwgroup->rq;
-        for (i = rq->nr_sectors; i > 0;) {
-            i -= rq->current_nr_sectors;
-            ide_end_request(1, hwgroup);
-        }
-    }
+	ide_hwgroup_t *hwgroup = HWGROUP(drive);
+	struct request *rq = &hwgroup->wrq;
+
+#ifdef DEBUG_WRITE
+	printk(KERN_DEBUG "%s: promise_write: sectors(%ld-%ld), "
+	       "buffer=0x%08lx\n", drive->name, rq->sector,
+	       rq->sector + rq->nr_sectors - 1, rq->buffer);
+#endif
+	if (rq->nr_sectors > 4) {
+		ide_multwrite(drive, rq->nr_sectors - 4);
+		hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
+		ide_set_handler (drive, &promise_write_pollfunc, 1);
+		return;
+	} else {
+		ide_multwrite(drive, rq->nr_sectors);
+		ide_set_handler(drive, &promise_write_intr, WAIT_CMD);
+	}
 }
 
 /*
@@ -315,43 +383,54 @@
 	byte stat;
 
 	if (rq->cmd == READ) {
-	    ide_set_handler(drive, &promise_read_intr, WAIT_CMD);
-	    OUT_BYTE(PROMISE_READ, IDE_COMMAND_REG);
-/* The card's behaviour is odd at this point. If the data is
-   available, DRQ will be true, and no interrupt will be
-   generated by the card. If this is the case, we need to simulate
-   an interrupt. Ugh! Otherwise, if an interrupt will occur, bit0
-   of the SELECT register will be high, so we can just return and
-   be interrupted.*/
-	    timeout = jiffies + HZ/20; /* 50ms wait */
-	    do {
-		stat=GET_STAT();
-		if(stat & DRQ_STAT) {
-                    disable_irq(HWIF(drive)->irq);
-		    ide_intr(HWIF(drive)->irq,HWGROUP(drive),NULL);
-                    enable_irq(HWIF(drive)->irq);
-		    return;
-		}
-		if(IN_BYTE(IDE_SELECT_REG) & 0x01)
-		    return;
-		udelay(1);
-	    } while (time_before(jiffies, timeout));
-	    printk("%s: reading: No DRQ and not waiting - Odd!\n",
-		   drive->name);
-	    return;
-	}
-	if (rq->cmd == WRITE) {
-	    OUT_BYTE(PROMISE_WRITE, IDE_COMMAND_REG);
-	    if (ide_wait_stat(drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) {
-		printk("%s: no DRQ after issuing PROMISE_WRITE\n", drive->name);
-		return;
-	    }
-	    if (!drive->unmask)
-		__cli();	/* local CPU only */
-	    HWGROUP(drive)->wrq = *rq; /* scratchpad */
-	    promise_write(drive);
-	    return;
+		OUT_BYTE(PROMISE_READ, IDE_COMMAND_REG);
+/*
+ * The card's behaviour is odd at this point. If the data is
+ * available, DRQ will be true, and no interrupt will be
+ * generated by the card. If this is the case, we need to call the 
+ * "interrupt" handler (promise_read_intr) directly. Otherwise, if
+ * an interrupt is going to occur, bit0 of the SELECT register will
+ * be high, so we can set the handler the just return and be interrupted.
+ * If neither of these is the case, we wait for up to 50ms (badly I'm
+ * afraid!) until one of them is.
+ */
+		timeout = jiffies + HZ/20; /* 50ms wait */
+		do {
+			stat=GET_STAT();
+			if (stat & DRQ_STAT) {
+				udelay(1);
+				promise_read_intr(drive);
+				return;
+			}
+			if (IN_BYTE(IDE_SELECT_REG) & 0x01) {
+#ifdef DEBUG_READ
+				printk(KERN_DEBUG "%s: read: waiting for "
+				                  "interrupt\n", drive->name);
+#endif
+				ide_set_handler(drive, &promise_read_intr, WAIT_CMD);
+				return;
+			}
+			udelay(1);
+		} while (time_before(jiffies, timeout));
+
+		printk(KERN_ERR "%s: reading: No DRQ and not waiting - Odd!\n",
+			drive->name);
+
+	} else if (rq->cmd == WRITE) {
+		OUT_BYTE(PROMISE_WRITE, IDE_COMMAND_REG);
+		if (ide_wait_stat(drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) {
+			printk(KERN_ERR "%s: no DRQ after issuing "
+			       "PROMISE_WRITE\n", drive->name);
+			return;
+	    	}
+		if (!drive->unmask)
+			__cli();	/* local CPU only */
+		HWGROUP(drive)->wrq = *rq; /* scratchpad */
+		promise_write(drive);
+
+	} else {
+		printk("KERN_WARNING %s: bad command: %d\n",
+		       drive->name, rq->cmd);
+		ide_end_request(0, HWGROUP(drive));
 	}
-	printk("%s: bad command: %d\n", drive->name, rq->cmd);
-	ide_end_request(0, HWGROUP(drive));
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)