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);