Subject: Re: kern/33445: Fixes for Promise SATA (pdcsata) PCI driver
To: None <gnats-bugs@NetBSD.org, kern-bug-people@NetBSD.org,>
From: Brian Buhrow <buhrow@lothlorien.nfbcal.org>
List: netbsd-bugs
Date: 05/17/2006 14:05:03
	Hello Timo.  I think I see your problem.  I was a bit confused about
the NetBSD interrupt frame work and wasn't sure if the return value from an
interrupt routine mattered.  It does, and your problem is that the revised
interrupt routine always claimed all interrupts.  That works fine for
non-shared interrupts, but you're sharing interrupts with the fxp1 card and
the pdcsata driver, so things went south.
	Here's a patch which fixes that issue.  Could you try it and confirm
that all is now well?
-thanks
-Brian

Index: pdcsata.c
===================================================================
RCS file: /cvsroot/src/sys/dev/pci/pdcsata.c,v
retrieving revision 1.3.2.2
diff -u -r1.3.2.2 pdcsata.c
--- pdcsata.c	5 Feb 2006 17:13:57 -0000	1.3.2.2
+++ pdcsata.c	17 May 2006 20:57:55 -0000
@@ -48,17 +48,19 @@
 
 #define PDC203xx_BAR_IDEREGS 0x1c /* BAR where the IDE registers are mapped */
 
+#define PDC_CHANNELBASE(ch) 0x200 + ((ch) * 0x80)
+#define PDC_ERRMASK 0x00780700
+
 static void pdcsata_chip_map(struct pciide_softc *, struct pci_attach_args *);
 static void pdc203xx_setup_channel(struct ata_channel *);
-static int  pdc203xx_pci_intr(void *);
 static void pdc203xx_irqack(struct ata_channel *);
 static int  pdc203xx_dma_init(void *, int, int, void *, size_t, int);
 static void pdc203xx_dma_start(void *,int ,int);
 static int  pdc203xx_dma_finish(void *, int, int, int);
+static int  pdcsata_pci_intr(void *);
+static void pdcsata_do_reset(struct ata_channel *, int);
 
 /* PDC205xx, PDC405xx and PDC407xx. but tested only pdc40718 */
-static int  pdc205xx_pci_intr(void *);
-static void pdc205xx_do_reset(struct ata_channel *, int);
 static void pdc205xx_drv_probe(struct ata_channel *);
 
 static int  pdcsata_match(struct device *, struct cfdata *, void *);
@@ -183,30 +185,8 @@
 		return;
 	}
 	intrstr = pci_intr_string(pa->pa_pc, intrhandle);
-
-	switch (sc->sc_pp->ide_product) {
-	case PCI_PRODUCT_PROMISE_PDC20318:
-	case PCI_PRODUCT_PROMISE_PDC20319:
-	case PCI_PRODUCT_PROMISE_PDC20371:
-	case PCI_PRODUCT_PROMISE_PDC20375:
-	case PCI_PRODUCT_PROMISE_PDC20376:
-	case PCI_PRODUCT_PROMISE_PDC20377:
-	case PCI_PRODUCT_PROMISE_PDC20378:
-	case PCI_PRODUCT_PROMISE_PDC20379:
-	default:
-		sc->sc_pci_ih = pci_intr_establish(pa->pa_pc,
-		    intrhandle, IPL_BIO, pdc203xx_pci_intr, sc);
-		break;
-
-	case PCI_PRODUCT_PROMISE_PDC40718:
-	case PCI_PRODUCT_PROMISE_PDC40719:
-	case PCI_PRODUCT_PROMISE_PDC20571:
-	case PCI_PRODUCT_PROMISE_PDC20575:
-	case PCI_PRODUCT_PROMISE_PDC20579:
-		sc->sc_pci_ih = pci_intr_establish(pa->pa_pc,
-		    intrhandle, IPL_BIO, pdc205xx_pci_intr, sc);
-		break;
-	}
+	sc->sc_pci_ih = pci_intr_establish(pa->pa_pc,
+	    intrhandle, IPL_BIO, pdcsata_pci_intr, sc);
 
 	if (sc->sc_pci_ih == NULL) {
 		aprint_error("%s: couldn't establish native-PCI interrupt",
@@ -258,6 +238,8 @@
 	sc->sc_wdcdev.sc_atac.atac_set_modes = pdc203xx_setup_channel;
 	sc->sc_wdcdev.sc_atac.atac_channels = sc->wdc_chanarray;
 
+	sc->sc_wdcdev.reset = pdcsata_do_reset;
+
 	switch (sc->sc_pp->ide_product) {
 	case PCI_PRODUCT_PROMISE_PDC20318:
 	case PCI_PRODUCT_PROMISE_PDC20319:
@@ -281,7 +263,6 @@
 		bus_space_write_4(sc->sc_ba5_st, sc->sc_ba5_sh, 0x60, 0x00ff00ff);
 		sc->sc_wdcdev.sc_atac.atac_nchannels = PDC40718_NCHANNELS;
 
-		sc->sc_wdcdev.reset = pdc205xx_do_reset;
 		sc->sc_wdcdev.sc_atac.atac_probe = pdc205xx_drv_probe;
 
 		break;
@@ -290,7 +271,6 @@
 		bus_space_write_4(sc->sc_ba5_st, sc->sc_ba5_sh, 0x60, 0x00ff00ff);
 		sc->sc_wdcdev.sc_atac.atac_nchannels = PDC20575_NCHANNELS;
 
-		sc->sc_wdcdev.reset = pdc205xx_do_reset;
 		sc->sc_wdcdev.sc_atac.atac_probe = pdc205xx_drv_probe;
 
 		break;
@@ -403,53 +383,37 @@
 }
 
 static int
-pdc203xx_pci_intr(void *arg)
+pdcsata_pci_intr(void *arg)
 {
 	struct pciide_softc *sc = arg;
 	struct pciide_channel *cp;
 	struct ata_channel *wdc_cp;
 	int i, rv, crv;
-	u_int32_t scr;
-
-	rv = 0;
-	scr = bus_space_read_4(sc->sc_ba5_st, sc->sc_ba5_sh, 0x00040);
-
-	for (i = 0; i < sc->sc_wdcdev.sc_atac.atac_nchannels; i++) {
-		cp = &sc->pciide_channels[i];
-		wdc_cp = &cp->ata_channel;
-		if (scr & (1 << (i + 1))) {
-			crv = wdcintr(wdc_cp);
-			if (crv == 0) {
-				printf("%s:%d: bogus intr (reg 0x%x)\n",
-				    sc->sc_wdcdev.sc_atac.atac_dev.dv_xname,
-				    i, scr);
-			} else
-				rv = 1;
-		}
-	}
-	return rv;
-}
-
-static int
-pdc205xx_pci_intr(void *arg)
-{
-	struct pciide_softc *sc = arg;
-	struct pciide_channel *cp;
-	struct ata_channel *wdc_cp;
-	int i, rv, crv;
-	u_int32_t scr, status;
+	u_int32_t scr, status, chanbase;
 
 	rv = 0;
 	scr = bus_space_read_4(sc->sc_ba5_st, sc->sc_ba5_sh, 0x40);
+	if (scr == 0xffffffff) return(rv);
 	bus_space_write_4(sc->sc_ba5_st, sc->sc_ba5_sh, 0x40, scr & 0x0000ffff);
-
-	status = bus_space_read_4(sc->sc_ba5_st, sc->sc_ba5_sh, 0x60);
-	bus_space_write_4(sc->sc_ba5_st, sc->sc_ba5_sh, 0x60, status & 0x000000ff);
+	scr = scr & 0x0000ffff;
+	if (!scr) return(rv);
 
 	for (i = 0; i < sc->sc_wdcdev.sc_atac.atac_nchannels; i++) {
 		cp = &sc->pciide_channels[i];
 		wdc_cp = &cp->ata_channel;
 		if (scr & (1 << (i + 1))) {
+			chanbase = PDC_CHANNELBASE(i) + 0x48;
+			status = bus_space_read_4(sc->sc_ba5_st, sc->sc_ba5_sh, chanbase);
+			if (status & PDC_ERRMASK) {
+				chanbase = PDC_CHANNELBASE(i) + 0x60;
+				status = bus_space_read_4(sc->sc_ba5_st, sc->sc_ba5_sh, chanbase);
+				status |= 0x800;
+				bus_space_write_4(sc->sc_ba5_st, sc->sc_ba5_sh, chanbase, status);
+				status &= ~0x800;
+				bus_space_write_4(sc->sc_ba5_st, sc->sc_ba5_sh, chanbase, status);
+				status = bus_space_read_4(sc->sc_ba5_st, sc->sc_ba5_sh, chanbase);
+				continue;
+			}
 			crv = wdcintr(wdc_cp);
 			if (crv == 0) {
 				printf("%s:%d: bogus intr (reg 0x%x)\n",
@@ -541,24 +505,29 @@
 
 
 static void
-pdc205xx_do_reset(struct ata_channel *chp, int poll)
+pdcsata_do_reset(struct ata_channel *chp, int poll)
 {
 	struct pciide_softc *sc = CHAN_TO_PCIIDE(chp);
-	u_int32_t scontrol;
-
-	wdc_do_reset(chp, poll);
+	int reset, status, i, chanbase;
 
 	/* reset SATA */
-	scontrol = SControl_DET_INIT | SControl_SPD_ANY | SControl_IPM_NONE;
-	SCONTROL_WRITE(sc, chp->ch_channel, scontrol);
-	delay(50*1000);
-
-	scontrol &= ~SControl_DET_INIT;
-	SCONTROL_WRITE(sc, chp->ch_channel, scontrol);
-	delay(50*1000);
-}
+	reset = (1 << 11);
+	chanbase = PDC_CHANNELBASE(chp->ch_channel) + 0x60;
+	for (i = 0; i < 11;i ++) {
+		status = bus_space_read_4(sc->sc_ba5_st, sc->sc_ba5_sh, chanbase);
+		if (status & reset) break;
+		delay(100);
+		status |= reset;
+		bus_space_write_4(sc->sc_ba5_st, sc->sc_ba5_sh, chanbase, status);
+	}
+	status = bus_space_read_4(sc->sc_ba5_st, sc->sc_ba5_sh, chanbase);
+	status &= ~reset;
+	bus_space_write_4(sc->sc_ba5_st, sc->sc_ba5_sh, chanbase, status);
+	status = bus_space_read_4(sc->sc_ba5_st, sc->sc_ba5_sh, chanbase);
 
+	wdc_do_reset(chp, poll);
 
+}
 
 static void
 pdc205xx_drv_probe(struct ata_channel *chp)