Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/dev/ic Gather disable/enable interruptst at the Port Int...



details:   https://anonhg.NetBSD.org/src/rev/837f063d9ecb
branches:  trunk
changeset: 779289:837f063d9ecb
user:      bouyer <bouyer%NetBSD.org@localhost>
date:      Tue May 15 19:06:26 2012 +0000

description:
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.

diffstat:

 sys/dev/ic/siisata.c |  101 +++++++++++++++++++++++++++++++++++++-------------
 1 files changed, 74 insertions(+), 27 deletions(-)

diffs (257 lines):

diff -r ed22880eba4c -r 837f063d9ecb sys/dev/ic/siisata.c
--- a/sys/dev/ic/siisata.c      Tue May 15 19:01:10 2012 +0000
+++ b/sys/dev/ic/siisata.c      Tue May 15 19:06:26 2012 +0000
@@ -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 @@
 }
 
 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 @@
            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_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 @@
 
        /* 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 @@
 
        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 @@
        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 @@
                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 @@
                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 @@
 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 @@
        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 @@
        }
 
        /* 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 @@
 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 @@
        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 @@
                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 @@
        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 @@
 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 @@
        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_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);



Home | Main Index | Thread Index | Old Index