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.
This commit is contained in:
parent
6cafcc75f4
commit
a7c7dd7fc2
@ -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 */
|
/* from ahcisata_core.c */
|
||||||
|
|
||||||
@ -79,7 +79,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/cdefs.h>
|
#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/types.h>
|
||||||
#include <sys/malloc.h>
|
#include <sys/malloc.h>
|
||||||
@ -211,6 +211,27 @@ siisata_attach(struct siisata_softc *sc)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
static void
|
||||||
siisata_init_port(struct siisata_softc *sc, int port)
|
siisata_init_port(struct siisata_softc *sc, int port)
|
||||||
{
|
{
|
||||||
@ -225,11 +246,8 @@ siisata_init_port(struct siisata_softc *sc, int port)
|
|||||||
PR_PC_32BA | PR_PC_PORT_RESET);
|
PR_PC_32BA | PR_PC_PORT_RESET);
|
||||||
/* initialize port */
|
/* initialize port */
|
||||||
siisata_reinit_port(chp);
|
siisata_reinit_port(chp);
|
||||||
/* clear any interrupts */
|
|
||||||
PRWRITE(sc, PRX(chp->ch_channel, PRO_PIS), 0xffffffff);
|
|
||||||
/* enable CmdErrr+CmdCmpl interrupting */
|
/* enable CmdErrr+CmdCmpl interrupting */
|
||||||
PRWRITE(sc, PRX(chp->ch_channel, PRO_PIES),
|
siisata_enable_port_interrupt(chp);
|
||||||
PR_PIS_CMDERRR | PR_PIS_CMDCMPL);
|
|
||||||
/* enable port interrupt */
|
/* enable port interrupt */
|
||||||
GRWRITE(sc, GR_GC, GRREAD(sc, GR_GC) | GR_GC_PXIE(chp->ch_channel));
|
GRWRITE(sc, GR_GC, GRREAD(sc, GR_GC) | GR_GC_PXIE(chp->ch_channel));
|
||||||
}
|
}
|
||||||
@ -480,6 +498,9 @@ siisata_intr_port(struct siisata_channel *schp)
|
|||||||
}
|
}
|
||||||
siisata_reinit_port(chp);
|
siisata_reinit_port(chp);
|
||||||
} else {
|
} 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" */
|
/* okay, we have a "Fatal Error" */
|
||||||
siisata_device_reset(chp);
|
siisata_device_reset(chp);
|
||||||
}
|
}
|
||||||
@ -487,10 +508,8 @@ siisata_intr_port(struct siisata_channel *schp)
|
|||||||
|
|
||||||
/* clear some (ok, all) ints */
|
/* clear some (ok, all) ints */
|
||||||
PRWRITE(sc, PRX(chp->ch_channel, PRO_PIS), 0xffffffff);
|
PRWRITE(sc, PRX(chp->ch_channel, PRO_PIS), 0xffffffff);
|
||||||
|
if (xfer && xfer->c_intr)
|
||||||
KASSERT(xfer != NULL);
|
xfer->c_intr(chp, xfer, slot);
|
||||||
KASSERT(xfer->c_intr != NULL);
|
|
||||||
xfer->c_intr(chp, xfer, slot);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -567,7 +586,7 @@ siisata_reset_channel(struct ata_channel *chp, int flags)
|
|||||||
|
|
||||||
if (sata_reset_interface(chp, sc->sc_prt, schp->sch_scontrol,
|
if (sata_reset_interface(chp, sc->sc_prt, schp->sch_scontrol,
|
||||||
schp->sch_sstatus) != SStatus_DET_DEV) {
|
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);
|
SIISATANAME(sc), chp->ch_channel);
|
||||||
/* XXX and then ? */
|
/* XXX and then ? */
|
||||||
}
|
}
|
||||||
@ -612,6 +631,7 @@ siisata_probe_drive(struct ata_channel *chp)
|
|||||||
uint32_t sig;
|
uint32_t sig;
|
||||||
int slot = SIISATA_NON_NCQ_SLOT;
|
int slot = SIISATA_NON_NCQ_SLOT;
|
||||||
struct siisata_prb *prb;
|
struct siisata_prb *prb;
|
||||||
|
bool timed_out;
|
||||||
|
|
||||||
SIISATA_DEBUG_PRINT(("%s: %s: port %d start\n", SIISATANAME(sc),
|
SIISATA_DEBUG_PRINT(("%s: %s: port %d start\n", SIISATANAME(sc),
|
||||||
__func__, chp->ch_channel), DEBUG_FUNCS);
|
__func__, chp->ch_channel), DEBUG_FUNCS);
|
||||||
@ -622,30 +642,56 @@ siisata_probe_drive(struct ata_channel *chp)
|
|||||||
chp->ch_drive[i].drive = i;
|
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)) {
|
schp->sch_sstatus)) {
|
||||||
case SStatus_DET_DEV:
|
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 */
|
/* wait for ready */
|
||||||
while (!(PRREAD(sc, PRX(chp->ch_channel, PRO_PS))
|
while (!(PRREAD(sc, PRX(chp->ch_channel, PRO_PS))
|
||||||
& PR_PS_PORT_READY))
|
& PR_PS_PORT_READY))
|
||||||
DELAY(10);
|
DELAY(10);
|
||||||
|
|
||||||
prb = schp->sch_prb[slot];
|
prb = schp->sch_prb[slot];
|
||||||
memset(prb, 0, sizeof(struct siisata_prb));
|
memset(prb, 0, sizeof(struct siisata_prb));
|
||||||
prb->prb_control =
|
prb->prb_control = htole16(PRB_CF_SOFT_RESET);
|
||||||
htole16(PRB_CF_SOFT_RESET | PRB_CF_INTERRUPT_MASK);
|
|
||||||
|
|
||||||
siisata_activate_prb(schp, slot);
|
siisata_activate_prb(schp, slot);
|
||||||
|
|
||||||
for(i = 0; i < 31000; i++) {
|
timed_out = 1;
|
||||||
if (PRREAD(sc, PRX(chp->ch_channel, PRO_PSS)) &
|
for(i = 0; i < 3100; i++) {
|
||||||
PR_PXSS(slot))
|
if ((PRREAD(sc, PRX(chp->ch_channel, PRO_PSS)) &
|
||||||
DELAY(1000);
|
PR_PXSS(slot)) == 0) {
|
||||||
else
|
/* prb completed */
|
||||||
|
timed_out = 0;
|
||||||
break;
|
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);
|
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 */
|
/* read the signature out of the FIS */
|
||||||
sig = 0;
|
sig = 0;
|
||||||
@ -684,6 +730,7 @@ siisata_probe_drive(struct ata_channel *chp)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
siisata_enable_port_interrupt(chp);
|
||||||
SIISATA_DEBUG_PRINT(("%s: %s: port %d done\n", SIISATANAME(sc),
|
SIISATA_DEBUG_PRINT(("%s: %s: port %d done\n", SIISATANAME(sc),
|
||||||
__func__, chp->ch_channel), DEBUG_PROBE);
|
__func__, chp->ch_channel), DEBUG_PROBE);
|
||||||
return;
|
return;
|
||||||
@ -755,7 +802,6 @@ siisata_exec_command(struct ata_drive_datas *drvp, struct ata_command *ata_c)
|
|||||||
void
|
void
|
||||||
siisata_cmd_start(struct ata_channel *chp, struct ata_xfer *xfer)
|
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 siisata_channel *schp = (struct siisata_channel *)chp;
|
||||||
struct ata_command *ata_c = xfer->c_cmd;
|
struct ata_command *ata_c = xfer->c_cmd;
|
||||||
int slot = SIISATA_NON_NCQ_SLOT;
|
int slot = SIISATA_NON_NCQ_SLOT;
|
||||||
@ -787,6 +833,7 @@ siisata_cmd_start(struct ata_channel *chp, struct ata_xfer *xfer)
|
|||||||
if (xfer->c_flags & C_POLL) {
|
if (xfer->c_flags & C_POLL) {
|
||||||
/* polled command, disable interrupts */
|
/* polled command, disable interrupts */
|
||||||
prb->prb_control = htole16(PRB_CF_INTERRUPT_MASK);
|
prb->prb_control = htole16(PRB_CF_INTERRUPT_MASK);
|
||||||
|
siisata_disable_port_interrupt(chp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* go for it */
|
/* go for it */
|
||||||
@ -815,7 +862,7 @@ siisata_cmd_start(struct ata_channel *chp, struct ata_xfer *xfer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* reenable interrupts */
|
/* reenable interrupts */
|
||||||
GRWRITE(sc, GR_GC, GRREAD(sc, GR_GC) | GR_GC_PXIE(chp->ch_channel));
|
siisata_enable_port_interrupt(chp);
|
||||||
out:
|
out:
|
||||||
SIISATA_DEBUG_PRINT(
|
SIISATA_DEBUG_PRINT(
|
||||||
("%s: %s: done\n", SIISATANAME(sc), __func__), DEBUG_FUNCS);
|
("%s: %s: done\n", SIISATANAME(sc), __func__), DEBUG_FUNCS);
|
||||||
@ -961,7 +1008,6 @@ siisata_ata_bio(struct ata_drive_datas *drvp, struct ata_bio *ata_bio)
|
|||||||
void
|
void
|
||||||
siisata_bio_start(struct ata_channel *chp, struct ata_xfer *xfer)
|
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_channel *schp = (struct siisata_channel *)chp;
|
||||||
struct siisata_prb *prb;
|
struct siisata_prb *prb;
|
||||||
struct ata_bio *ata_bio = xfer->c_cmd;
|
struct ata_bio *ata_bio = xfer->c_cmd;
|
||||||
@ -994,6 +1040,7 @@ siisata_bio_start(struct ata_channel *chp, struct ata_xfer *xfer)
|
|||||||
if (xfer->c_flags & C_POLL) {
|
if (xfer->c_flags & C_POLL) {
|
||||||
/* polled command, disable interrupts */
|
/* polled command, disable interrupts */
|
||||||
prb->prb_control = htole16(PRB_CF_INTERRUPT_MASK);
|
prb->prb_control = htole16(PRB_CF_INTERRUPT_MASK);
|
||||||
|
siisata_disable_port_interrupt(chp);
|
||||||
}
|
}
|
||||||
|
|
||||||
siisata_activate_prb(schp, slot);
|
siisata_activate_prb(schp, slot);
|
||||||
@ -1015,7 +1062,7 @@ siisata_bio_start(struct ata_channel *chp, struct ata_xfer *xfer)
|
|||||||
DELAY(1000);
|
DELAY(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
GRWRITE(sc, GR_GC, GRREAD(sc, GR_GC) | GR_GC_PXIE(chp->ch_channel));
|
siisata_enable_port_interrupt(chp);
|
||||||
out:
|
out:
|
||||||
SIISATA_DEBUG_PRINT(
|
SIISATA_DEBUG_PRINT(
|
||||||
("%s: %s: done\n", SIISATANAME(sc), __func__), DEBUG_FUNCS);
|
("%s: %s: done\n", SIISATANAME(sc), __func__), DEBUG_FUNCS);
|
||||||
@ -1147,7 +1194,7 @@ siisata_dma_setup(struct ata_channel *chp, int slot, void *data,
|
|||||||
error = bus_dmamap_load(sc->sc_dmat, schp->sch_datad[slot],
|
error = bus_dmamap_load(sc->sc_dmat, schp->sch_datad[slot],
|
||||||
data, count, NULL, BUS_DMA_NOWAIT | BUS_DMA_STREAMING | op);
|
data, count, NULL, BUS_DMA_NOWAIT | BUS_DMA_STREAMING | op);
|
||||||
if (error) {
|
if (error) {
|
||||||
log(LOG_ERR, "%s port %d: "
|
aprint_error("%s port %d: "
|
||||||
"failed to load xfer in slot %d: error %d\n",
|
"failed to load xfer in slot %d: error %d\n",
|
||||||
SIISATANAME(sc), chp->ch_channel, slot, error);
|
SIISATANAME(sc), chp->ch_channel, slot, error);
|
||||||
return error;
|
return error;
|
||||||
@ -1499,7 +1546,6 @@ siisata_atapi_scsipi_request(struct scsipi_channel *chan,
|
|||||||
void
|
void
|
||||||
siisata_atapi_start(struct ata_channel *chp, struct ata_xfer *xfer)
|
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_channel *schp = (struct siisata_channel *)chp;
|
||||||
struct siisata_prb *prbp;
|
struct siisata_prb *prbp;
|
||||||
|
|
||||||
@ -1543,6 +1589,7 @@ siisata_atapi_start(struct ata_channel *chp, struct ata_xfer *xfer)
|
|||||||
if (xfer->c_flags & C_POLL) {
|
if (xfer->c_flags & C_POLL) {
|
||||||
/* polled command, disable interrupts */
|
/* polled command, disable interrupts */
|
||||||
prbp->prb_control = htole16(PRB_CF_INTERRUPT_MASK);
|
prbp->prb_control = htole16(PRB_CF_INTERRUPT_MASK);
|
||||||
|
siisata_disable_port_interrupt(chp);
|
||||||
}
|
}
|
||||||
|
|
||||||
siisata_activate_prb(schp, slot);
|
siisata_activate_prb(schp, slot);
|
||||||
@ -1568,7 +1615,7 @@ siisata_atapi_start(struct ata_channel *chp, struct ata_xfer *xfer)
|
|||||||
siisata_atapi_complete(chp, xfer, slot);
|
siisata_atapi_complete(chp, xfer, slot);
|
||||||
}
|
}
|
||||||
/* reenable interrupts */
|
/* reenable interrupts */
|
||||||
GRWRITE(sc, GR_GC, GRREAD(sc, GR_GC) | GR_GC_PXIE(chp->ch_channel));
|
siisata_enable_port_interrupt(chp);
|
||||||
out:
|
out:
|
||||||
SIISATA_DEBUG_PRINT(
|
SIISATA_DEBUG_PRINT(
|
||||||
("%s: %s: done\n", SIISATANAME(sc), __func__), DEBUG_FUNCS);
|
("%s: %s: done\n", SIISATANAME(sc), __func__), DEBUG_FUNCS);
|
||||||
|
Loading…
Reference in New Issue
Block a user