Subject: Support for polled IDE controllers
To: None <tech-kern@netbsd.org>
From: Ben Harris <bjh21@netbsd.org>
List: tech-kern
Date: 06/09/2001 13:38:06
Since breaking the IDE drivers is likely to make me very unpopular, could
someone who understands them better than I sanity-check the following?

I've got a couple of IDE controllers that don't have their interrupt lines
wired up, and I'd like the NetBSD wdc driver to handle them.  I've
arranged this by adding a new capability, WDC_CAPABILITY_NOIRQ, and having
the three functions that can queue transfers (wdc_ata_bio(),
wdc_exec_command() and wdc_atapi_scsipi_request()) check that and mark all
transfers as polled if it's set.

In order to make this work, I found that I also had to disable the checks
for ATA_POLL in wdc_ata_bio_done() and wdc_ata_bio_kill_xfer() and have
them unconditionally call wddone().  On the assumption that those tests
were there to avoid calling wddone() when dumping, I added a check on
sc_bp in wddone(), and made wddump() clear it, so that wddone() is a no-op
if the transfer was caused by wddump().

Here's the patch:

Index: sys/dev/ata/ata_wdc.c
===================================================================
RCS file: /cvsroot/syssrc/sys/dev/ata/ata_wdc.c,v
retrieving revision 1.29
diff -u -p -r1.29 ata_wdc.c
--- sys/dev/ata/ata_wdc.c	2001/04/18 05:40:03	1.29
+++ sys/dev/ata/ata_wdc.c	2001/06/09 12:36:22
@@ -141,6 +141,8 @@ wdc_ata_bio(drvp, ata_bio)
 	xfer = wdc_get_xfer(WDC_NOSLEEP);
 	if (xfer == NULL)
 		return WDC_TRY_AGAIN;
+	if (chp->wdc->cap & WDC_CAPABILITY_NOIRQ)
+		ata_bio->flags |= ATA_POLL;
 	if (ata_bio->flags & ATA_POLL)
 		xfer->c_flags |= C_POLL;
 	if ((drvp->drive_flags & (DRIVE_DMA | DRIVE_UDMA)) &&
@@ -592,10 +594,8 @@ wdc_ata_bio_kill_xfer(chp, xfer)
 	ata_bio->flags |= ATA_ITSDONE;
 	ata_bio->error = ERR_NODEV;
 	ata_bio->r_error = WDCE_ABRT;
-	if ((ata_bio->flags & ATA_POLL) == 0) {
-		WDCDEBUG_PRINT(("wdc_ata_done: wddone\n"), DEBUG_XFERS);
-		wddone(chp->ch_drive[drive].drv_softc);
-	}
+	WDCDEBUG_PRINT(("wdc_ata_done: wddone\n"), DEBUG_XFERS);
+	wddone(chp->ch_drive[drive].drv_softc);
 }

 void
@@ -620,10 +620,8 @@ wdc_ata_bio_done(chp, xfer)
 	wdc_free_xfer(chp, xfer);

 	ata_bio->flags |= ATA_ITSDONE;
-	if ((ata_bio->flags & ATA_POLL) == 0) {
-		WDCDEBUG_PRINT(("wdc_ata_done: wddone\n"), DEBUG_XFERS);
-		wddone(chp->ch_drive[drive].drv_softc);
-	}
+	WDCDEBUG_PRINT(("wdc_ata_done: wddone\n"), DEBUG_XFERS);
+	wddone(chp->ch_drive[drive].drv_softc);
 	WDCDEBUG_PRINT(("wdcstart from wdc_ata_done, flags 0x%x\n",
 	    chp->ch_flags), DEBUG_XFERS);
 	wdcstart(chp);
Index: sys/dev/ata/wd.c
===================================================================
RCS file: /cvsroot/syssrc/sys/dev/ata/wd.c,v
retrieving revision 1.213
diff -u -p -r1.213 wd.c
--- sys/dev/ata/wd.c	2001/05/06 18:30:57	1.213
+++ sys/dev/ata/wd.c	2001/06/09 12:36:22
@@ -573,6 +573,8 @@ wddone(v)
 	WDCDEBUG_PRINT(("wddone %s\n", wd->sc_dev.dv_xname),
 	    DEBUG_XFERS);

+	if (bp == NULL)
+		return;
 	bp->b_resid = wd->sc_wdc_bio.bcount;
 	errbuf[0] = '\0';
 	switch (wd->sc_wdc_bio.error) {
@@ -1219,6 +1221,7 @@ wddump(dev, blkno, va, size)

 	while (nblks > 0) {
 again:
+		wd->sc_bp = NULL;
 		wd->sc_wdc_bio.blkno = blkno;
 		wd->sc_wdc_bio.flags = ATA_POLL;
 		if (wddumpmulti == 1)
Index: sys/dev/ic/wdc.c
===================================================================
RCS file: /cvsroot/syssrc/sys/dev/ic/wdc.c,v
retrieving revision 1.96
diff -u -p -r1.96 wdc.c
--- sys/dev/ic/wdc.c	2001/04/25 17:53:35	1.96
+++ sys/dev/ic/wdc.c	2001/06/09 12:36:24
@@ -651,6 +651,8 @@ wdcstart(chp)
 		chp->ch_drive[xfer->drive].drive_flags &= ~DRIVE_RESET;
 		chp->ch_drive[xfer->drive].state = 0;
 	}
+	if (chp->wdc->cap & WDC_CAPABILITY_NOIRQ)
+		KASSERT(xfer->c_flags & C_POLL);
 	xfer->c_start(chp, xfer);
 }

@@ -1256,6 +1258,8 @@ wdc_exec_command(drvp, wdc_c)
 		return WDC_TRY_AGAIN;
 	 }

+	if (chp->wdc->cap & WDC_CAPABILITY_NOIRQ)
+		wdc_c->flags |= AT_POLL;
 	if (wdc_c->flags & AT_POLL)
 		xfer->c_flags |= C_POLL;
 	xfer->drive = drvp->drive;
Index: sys/dev/ic/wdcvar.h
===================================================================
RCS file: /cvsroot/syssrc/sys/dev/ic/wdcvar.h,v
retrieving revision 1.29
diff -u -p -r1.29 wdcvar.h
--- sys/dev/ic/wdcvar.h	2001/04/25 17:53:35	1.29
+++ sys/dev/ic/wdcvar.h	2001/06/09 12:36:25
@@ -101,6 +101,7 @@ struct wdc_softc { /* Per controller sta
 #define WDC_CAPABILITY_PREATA 0x0200 /* ctrl can be a pre-ata one */
 #define WDC_CAPABILITY_IRQACK 0x0400 /* callback to ack interrupt */
 #define WDC_CAPABILITY_SINGLE_DRIVE 0x0800 /* Don't probe second drive */
+#define WDC_CAPABILITY_NOIRQ  0x1000	/* Controller never interrupts */
 	u_int8_t      PIO_cap; /* highest PIO mode supported */
 	u_int8_t      DMA_cap; /* highest DMA mode supported */
 	u_int8_t      UDMA_cap; /* highest UDMA mode supported */
Index: sys/dev/scsipi/atapi_wdc.c
===================================================================
RCS file: /cvsroot/syssrc/sys/dev/scsipi/atapi_wdc.c,v
retrieving revision 1.41
diff -u -p -r1.41 atapi_wdc.c
--- sys/dev/scsipi/atapi_wdc.c	2001/05/15 13:53:20	1.41
+++ sys/dev/scsipi/atapi_wdc.c	2001/06/09 12:36:26
@@ -348,6 +348,8 @@ wdc_atapi_scsipi_request(chan, req, arg)
 			return;
 		}

+		if (wdc->cap & WDC_CAPABILITY_NOIRQ)
+			sc_xfer->xs_control |= XS_CTL_POLL;
 		if (sc_xfer->xs_control & XS_CTL_POLL)
 			xfer->c_flags |= C_POLL;
 		xfer->drive = drive;

-- 
Ben Harris                                                   <bjh21@netbsd.org>
Portmaster, NetBSD/arm26               <URL:http://www.netbsd.org/Ports/arm26/>