Module Name:    src
Committed By:   bouyer
Date:           Tue May 15 19:06:27 UTC 2012

Modified Files:
        src/sys/dev/ic: siisata.c

Log Message:
Gather disable/enable interruptst at the Port Interrupt Enable level in
2 functions, and use them to disable interrupts for polled commands.
In siisata_probe_drive(), disable interrupt while resetting the PHY and
sending the SOFT_RESET FIS. Also dectect timeout/errors at this level and
disable the port if needed.
Make siisata_intr_port() more resistent to interrupts without xfer (especially
error interrupts which can be asynchrounous, but can also happen for
timed out xfer).

With this, the kernel doesn't pannic when a siisata controller is connected
to a SATA port multiplier. More work is needed to support port multiplier
though.


To generate a diff of this commit:
cvs rdiff -u -r1.16 -r1.17 src/sys/dev/ic/siisata.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/ic/siisata.c
diff -u src/sys/dev/ic/siisata.c:1.16 src/sys/dev/ic/siisata.c:1.17
--- src/sys/dev/ic/siisata.c:1.16	Fri Apr 20 20:23:20 2012
+++ src/sys/dev/ic/siisata.c	Tue May 15 19:06:26 2012
@@ -1,4 +1,4 @@
-/* $NetBSD: siisata.c,v 1.16 2012/04/20 20:23:20 bouyer Exp $ */
+/* $NetBSD: siisata.c,v 1.17 2012/05/15 19:06:26 bouyer Exp $ */
 
 /* from ahcisata_core.c */
 
@@ -79,7 +79,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: siisata.c,v 1.16 2012/04/20 20:23:20 bouyer Exp $");
+__KERNEL_RCSID(0, "$NetBSD: siisata.c,v 1.17 2012/05/15 19:06:26 bouyer Exp $");
 
 #include <sys/types.h>
 #include <sys/malloc.h>
@@ -212,6 +212,27 @@ siisata_attach(struct siisata_softc *sc)
 }
 
 static void
+siisata_disable_port_interrupt(struct ata_channel *chp)
+{
+	struct siisata_softc *sc = (struct siisata_softc *)chp->ch_atac;
+
+	PRWRITE(sc, PRX(chp->ch_channel, PRO_PIEC), 0xffffffff);
+}
+
+static void
+siisata_enable_port_interrupt(struct ata_channel *chp)
+{
+	struct siisata_softc *sc = (struct siisata_softc *)chp->ch_atac;
+
+	/* clear any interrupts */
+	(void)PRREAD(sc, PRX(chp->ch_channel, PRO_PSS));
+	PRWRITE(sc, PRX(chp->ch_channel, PRO_PIS), 0xffffffff);
+	/* and enable CmdErrr+CmdCmpl interrupting */
+	PRWRITE(sc, PRX(chp->ch_channel, PRO_PIES),
+	    PR_PIS_CMDERRR | PR_PIS_CMDCMPL);
+}
+
+static void
 siisata_init_port(struct siisata_softc *sc, int port)
 {
 	struct siisata_channel *schp;
@@ -225,11 +246,8 @@ siisata_init_port(struct siisata_softc *
 	    PR_PC_32BA | PR_PC_PORT_RESET);
 	/* initialize port */
 	siisata_reinit_port(chp);
-	/* clear any interrupts */
-	PRWRITE(sc, PRX(chp->ch_channel, PRO_PIS), 0xffffffff);
 	/* enable CmdErrr+CmdCmpl interrupting */
-	PRWRITE(sc, PRX(chp->ch_channel, PRO_PIES),
-	    PR_PIS_CMDERRR | PR_PIS_CMDCMPL);
+	siisata_enable_port_interrupt(chp);
 	/* enable port interrupt */
 	GRWRITE(sc, GR_GC, GRREAD(sc, GR_GC) | GR_GC_PXIE(chp->ch_channel));
 }
@@ -480,6 +498,9 @@ siisata_intr_port(struct siisata_channel
 			}
 			siisata_reinit_port(chp);
 		} else {
+			aprint_error_dev(sc->sc_atac.atac_dev,
+			    "fatal error %d on channel %d, resetting\n",
+			    ec, chp->ch_channel);
 			/* okay, we have a "Fatal Error" */
 			siisata_device_reset(chp);
 		}
@@ -487,10 +508,8 @@ siisata_intr_port(struct siisata_channel
 
 	/* clear some (ok, all) ints */
 	PRWRITE(sc, PRX(chp->ch_channel, PRO_PIS), 0xffffffff);
-
-	KASSERT(xfer != NULL);
-	KASSERT(xfer->c_intr != NULL);
-	xfer->c_intr(chp, xfer, slot);
+	if (xfer && xfer->c_intr)
+		xfer->c_intr(chp, xfer, slot);
 
 	return;
 }
@@ -567,7 +586,7 @@ siisata_reset_channel(struct ata_channel
 
 	if (sata_reset_interface(chp, sc->sc_prt, schp->sch_scontrol,
 	    schp->sch_sstatus) != SStatus_DET_DEV) {
-		log(LOG_CRIT, "%s port %d: reset failed\n",
+		aprint_error("%s port %d: reset failed\n",
 		    SIISATANAME(sc), chp->ch_channel);
 		/* XXX and then ? */
 	}
@@ -612,6 +631,7 @@ siisata_probe_drive(struct ata_channel *
 	uint32_t sig;
 	int slot = SIISATA_NON_NCQ_SLOT;
 	struct siisata_prb *prb;
+	bool timed_out;
 
 	SIISATA_DEBUG_PRINT(("%s: %s: port %d start\n", SIISATANAME(sc),
 	    __func__, chp->ch_channel), DEBUG_FUNCS);
@@ -622,30 +642,56 @@ siisata_probe_drive(struct ata_channel *
 		chp->ch_drive[i].drive = i;
 	}
 
-	switch (sata_reset_interface(chp, sc->sc_prt, schp->sch_scontrol,
+	/*
+	 * disable port interrupt as we're polling for PHY up and
+	 * prb completion
+	 */
+	siisata_disable_port_interrupt(chp);
+
+	switch(sata_reset_interface(chp, sc->sc_prt, schp->sch_scontrol,
 		schp->sch_sstatus)) {
 	case SStatus_DET_DEV:
+		/* clear any interrupts */
+		(void)PRREAD(sc, PRX(chp->ch_channel, PRO_PSS));
+		PRWRITE(sc, PRX(chp->ch_channel, PRO_PIS), 0xffffffff);
 		/* wait for ready */
 		while (!(PRREAD(sc, PRX(chp->ch_channel, PRO_PS))
 		    & PR_PS_PORT_READY))
 			DELAY(10);
-
 		prb = schp->sch_prb[slot];
 		memset(prb, 0, sizeof(struct siisata_prb));
-		prb->prb_control =
-		    htole16(PRB_CF_SOFT_RESET | PRB_CF_INTERRUPT_MASK);
+		prb->prb_control = htole16(PRB_CF_SOFT_RESET);
 
 		siisata_activate_prb(schp, slot);
 
-		for(i = 0; i < 31000; i++) {
-			if (PRREAD(sc, PRX(chp->ch_channel, PRO_PSS)) &
-			    PR_PXSS(slot))
-				DELAY(1000);
-			else
+		timed_out = 1;
+		for(i = 0; i < 3100; i++) {
+			if ((PRREAD(sc, PRX(chp->ch_channel, PRO_PSS)) &
+			    PR_PXSS(slot)) == 0) {
+				/* prb completed */
+				timed_out = 0;
 				break;
+			}
+			if (PRREAD(sc, PRX(chp->ch_channel, PRO_PIS)) &
+			    (PR_PIS_CMDERRR << 16)) {
+				/* we got an error; handle as timeout */
+				break;
+			}
+
+			tsleep(schp, PRIBIO, "siiprb", mstohz(10));
 		}
 
 		siisata_deactivate_prb(schp, slot);
+		if (timed_out) {
+			aprint_error_dev(sc->sc_atac.atac_dev,
+			    "SOFT_RESET failed on port %d (error %d PSS 0x%x), "
+			    "disabling\n", chp->ch_channel,
+			    PRREAD(sc, PRX(chp->ch_channel, PRO_PCE)),
+			    PRREAD(sc, PRX(chp->ch_channel, PRO_PSS)));
+			PRWRITE(sc, PRX(chp->ch_channel, PRO_PCS),
+			    PR_PC_PORT_RESET);
+			break;
+		}
 
 		/* read the signature out of the FIS */
 		sig = 0;
@@ -684,6 +730,7 @@ siisata_probe_drive(struct ata_channel *
 		break;
 	}
 
+	siisata_enable_port_interrupt(chp);
 	SIISATA_DEBUG_PRINT(("%s: %s: port %d done\n", SIISATANAME(sc),
 	    __func__, chp->ch_channel), DEBUG_PROBE);
 	return;
@@ -755,7 +802,6 @@ siisata_exec_command(struct ata_drive_da
 void
 siisata_cmd_start(struct ata_channel *chp, struct ata_xfer *xfer)
 {
-	struct siisata_softc *sc = (struct siisata_softc *)chp->ch_atac;
 	struct siisata_channel *schp = (struct siisata_channel *)chp;
 	struct ata_command *ata_c = xfer->c_cmd;
 	int slot = SIISATA_NON_NCQ_SLOT;
@@ -787,6 +833,7 @@ siisata_cmd_start(struct ata_channel *ch
 	if (xfer->c_flags & C_POLL) {
 		/* polled command, disable interrupts */
 		prb->prb_control = htole16(PRB_CF_INTERRUPT_MASK);
+		siisata_disable_port_interrupt(chp);
 	}
 
 	/* go for it */
@@ -815,7 +862,7 @@ siisata_cmd_start(struct ata_channel *ch
 	}
 
 	/* reenable interrupts */
-	GRWRITE(sc, GR_GC, GRREAD(sc, GR_GC) | GR_GC_PXIE(chp->ch_channel));
+	siisata_enable_port_interrupt(chp);
 out:
 	SIISATA_DEBUG_PRINT(
 	    ("%s: %s: done\n", SIISATANAME(sc), __func__), DEBUG_FUNCS);
@@ -961,7 +1008,6 @@ siisata_ata_bio(struct ata_drive_datas *
 void
 siisata_bio_start(struct ata_channel *chp, struct ata_xfer *xfer)
 {
-	struct siisata_softc *sc = (struct siisata_softc *)chp->ch_atac;
 	struct siisata_channel *schp = (struct siisata_channel *)chp;
 	struct siisata_prb *prb;
 	struct ata_bio *ata_bio = xfer->c_cmd;
@@ -994,6 +1040,7 @@ siisata_bio_start(struct ata_channel *ch
 	if (xfer->c_flags & C_POLL) {
 		/* polled command, disable interrupts */
 		prb->prb_control = htole16(PRB_CF_INTERRUPT_MASK);
+		siisata_disable_port_interrupt(chp);
 	}
 
 	siisata_activate_prb(schp, slot);
@@ -1015,7 +1062,7 @@ siisata_bio_start(struct ata_channel *ch
 		DELAY(1000);
 	}
 
-	GRWRITE(sc, GR_GC, GRREAD(sc, GR_GC) | GR_GC_PXIE(chp->ch_channel));
+	siisata_enable_port_interrupt(chp);
 out:
 	SIISATA_DEBUG_PRINT(
 	    ("%s: %s: done\n", SIISATANAME(sc), __func__), DEBUG_FUNCS);
@@ -1147,7 +1194,7 @@ siisata_dma_setup(struct ata_channel *ch
 	error = bus_dmamap_load(sc->sc_dmat, schp->sch_datad[slot],
 	    data, count, NULL, BUS_DMA_NOWAIT | BUS_DMA_STREAMING | op);
 	if (error) {
-		log(LOG_ERR, "%s port %d: "
+		aprint_error("%s port %d: "
 		    "failed to load xfer in slot %d: error %d\n",
 		    SIISATANAME(sc), chp->ch_channel, slot, error);
 		return error;
@@ -1499,7 +1546,6 @@ siisata_atapi_scsipi_request(struct scsi
 void
 siisata_atapi_start(struct ata_channel *chp, struct ata_xfer *xfer)
 {
-	struct siisata_softc *sc = (struct siisata_softc *)chp->ch_atac;
 	struct siisata_channel *schp = (struct siisata_channel *)chp;
 	struct siisata_prb *prbp;
 
@@ -1543,6 +1589,7 @@ siisata_atapi_start(struct ata_channel *
 	if (xfer->c_flags & C_POLL) {
 		/* polled command, disable interrupts */
 		prbp->prb_control = htole16(PRB_CF_INTERRUPT_MASK);
+		siisata_disable_port_interrupt(chp);
 	}
 
 	siisata_activate_prb(schp, slot);
@@ -1568,7 +1615,7 @@ siisata_atapi_start(struct ata_channel *
 		siisata_atapi_complete(chp, xfer, slot);
 	}
 	/* reenable interrupts */
-	GRWRITE(sc, GR_GC, GRREAD(sc, GR_GC) | GR_GC_PXIE(chp->ch_channel));
+	siisata_enable_port_interrupt(chp);
 out:
 	SIISATA_DEBUG_PRINT(
 	    ("%s: %s: done\n", SIISATANAME(sc), __func__), DEBUG_FUNCS);

Reply via email to