version 1.62, 2018/07/09 10:44:44 |
version 1.62.2.9, 2018/10/11 20:57:51 |
Line 59 static void ahci_setup_channel(struct at |
|
Line 59 static void ahci_setup_channel(struct at |
|
|
|
static int ahci_ata_bio(struct ata_drive_datas *, struct ata_xfer *); |
static int ahci_ata_bio(struct ata_drive_datas *, struct ata_xfer *); |
static int ahci_do_reset_drive(struct ata_channel *, int, int, uint32_t *, |
static int ahci_do_reset_drive(struct ata_channel *, int, int, uint32_t *, |
struct ata_xfer *xfer); |
uint8_t); |
static void ahci_reset_drive(struct ata_drive_datas *, int, uint32_t *); |
static void ahci_reset_drive(struct ata_drive_datas *, int, uint32_t *); |
static void ahci_reset_channel(struct ata_channel *, int); |
static void ahci_reset_channel(struct ata_channel *, int); |
static int ahci_exec_command(struct ata_drive_datas *, struct ata_xfer *); |
static int ahci_exec_command(struct ata_drive_datas *, struct ata_xfer *); |
Line 569 ahci_intr_port(struct ahci_softc *sc, st |
|
Line 569 ahci_intr_port(struct ahci_softc *sc, st |
|
struct ata_xfer *xfer; |
struct ata_xfer *xfer; |
int slot = -1; |
int slot = -1; |
bool recover = false; |
bool recover = false; |
|
uint32_t aslots; |
|
|
is = AHCI_READ(sc, AHCI_P_IS(chp->ch_channel)); |
is = AHCI_READ(sc, AHCI_P_IS(chp->ch_channel)); |
AHCI_WRITE(sc, AHCI_P_IS(chp->ch_channel), is); |
AHCI_WRITE(sc, AHCI_P_IS(chp->ch_channel), is); |
Line 622 ahci_intr_port(struct ahci_softc *sc, st |
|
Line 623 ahci_intr_port(struct ahci_softc *sc, st |
|
DEBUG_INTR); |
DEBUG_INTR); |
} |
} |
|
|
if (!achp->ahcic_recovering) |
if (!ISSET(chp->ch_flags, ATACH_RECOVERING)) |
recover = true; |
recover = true; |
} else if (is & (AHCI_P_IX_DHRS|AHCI_P_IX_SDBS)) { |
} else if (is & (AHCI_P_IX_DHRS|AHCI_P_IX_SDBS)) { |
tfd = AHCI_READ(sc, AHCI_P_TFD(chp->ch_channel)); |
tfd = AHCI_READ(sc, AHCI_P_TFD(chp->ch_channel)); |
|
|
/* D2H Register FIS or Set Device Bits */ |
/* D2H Register FIS or Set Device Bits */ |
if ((tfd & WDCS_ERR) != 0) { |
if ((tfd & WDCS_ERR) != 0) { |
if (!achp->ahcic_recovering) |
if (!ISSET(chp->ch_flags, ATACH_RECOVERING)) |
recover = true; |
recover = true; |
|
|
AHCIDEBUG_PRINT(("%s port %d: transfer aborted 0x%x\n", |
AHCIDEBUG_PRINT(("%s port %d: transfer aborted 0x%x\n", |
Line 643 ahci_intr_port(struct ahci_softc *sc, st |
|
Line 644 ahci_intr_port(struct ahci_softc *sc, st |
|
if (__predict_false(recover)) |
if (__predict_false(recover)) |
ata_channel_freeze(chp); |
ata_channel_freeze(chp); |
|
|
|
aslots = ata_queue_active(chp); |
|
|
if (slot >= 0) { |
if (slot >= 0) { |
if ((achp->ahcic_cmds_active & __BIT(slot)) != 0 && |
if ((aslots & __BIT(slot)) != 0 && |
(sact & __BIT(slot)) == 0) { |
(sact & __BIT(slot)) == 0) { |
xfer = ata_queue_hwslot_to_xfer(chp, slot); |
xfer = ata_queue_hwslot_to_xfer(chp, slot); |
xfer->c_intr(chp, xfer, tfd); |
xfer->ops->c_intr(chp, xfer, tfd); |
} |
} |
} else { |
} else { |
/* |
/* |
Line 659 ahci_intr_port(struct ahci_softc *sc, st |
|
Line 662 ahci_intr_port(struct ahci_softc *sc, st |
|
* can activate another command(s), so must only process |
* can activate another command(s), so must only process |
* commands active before we start processing. |
* commands active before we start processing. |
*/ |
*/ |
uint32_t aslots = achp->ahcic_cmds_active; |
|
|
|
for (slot=0; slot < sc->sc_ncmds; slot++) { |
for (slot=0; slot < sc->sc_ncmds; slot++) { |
if ((aslots & __BIT(slot)) != 0 && |
if ((aslots & __BIT(slot)) != 0 && |
(sact & __BIT(slot)) == 0) { |
(sact & __BIT(slot)) == 0) { |
xfer = ata_queue_hwslot_to_xfer(chp, slot); |
xfer = ata_queue_hwslot_to_xfer(chp, slot); |
xfer->c_intr(chp, xfer, tfd); |
xfer->ops->c_intr(chp, xfer, tfd); |
} |
} |
} |
} |
} |
} |
Line 681 ahci_reset_drive(struct ata_drive_datas |
|
Line 683 ahci_reset_drive(struct ata_drive_datas |
|
{ |
{ |
struct ata_channel *chp = drvp->chnl_softc; |
struct ata_channel *chp = drvp->chnl_softc; |
struct ahci_softc *sc = (struct ahci_softc *)chp->ch_atac; |
struct ahci_softc *sc = (struct ahci_softc *)chp->ch_atac; |
struct ata_xfer *xfer; |
uint8_t c_slot; |
|
|
xfer = ata_get_xfer_ext(chp, C_RECOVERY, 0); |
ata_channel_lock_owned(chp); |
|
|
ata_channel_lock(chp); |
/* get a slot for running the command on */ |
|
if (!ata_queue_alloc_slot(chp, &c_slot, ATA_MAX_OPENINGS)) { |
|
panic("%s: %s: failed to get xfer for reset, port %d\n", |
|
device_xname(sc->sc_atac.atac_dev), |
|
__func__, chp->ch_channel); |
|
/* NOTREACHED */ |
|
} |
|
|
AHCI_WRITE(sc, AHCI_GHC, |
AHCI_WRITE(sc, AHCI_GHC, |
AHCI_READ(sc, AHCI_GHC) & ~AHCI_GHC_IE); |
AHCI_READ(sc, AHCI_GHC) & ~AHCI_GHC_IE); |
ahci_channel_stop(sc, chp, flags); |
ahci_channel_stop(sc, chp, flags); |
if (ahci_do_reset_drive(chp, drvp->drive, flags, sigp, xfer) != 0) |
ahci_do_reset_drive(chp, drvp->drive, flags, sigp, c_slot); |
ata_reset_channel(chp, flags); |
|
AHCI_WRITE(sc, AHCI_GHC, AHCI_READ(sc, AHCI_GHC) | AHCI_GHC_IE); |
AHCI_WRITE(sc, AHCI_GHC, AHCI_READ(sc, AHCI_GHC) | AHCI_GHC_IE); |
|
|
ata_channel_unlock(chp); |
ata_queue_free_slot(chp, c_slot); |
|
|
ata_free_xfer(chp, xfer); |
|
|
|
return; |
|
} |
} |
|
|
/* return error code from ata_bio */ |
/* return error code from ata_bio */ |
Line 755 ahci_exec_fis(struct ata_channel *chp, i |
|
Line 758 ahci_exec_fis(struct ata_channel *chp, i |
|
|
|
static int |
static int |
ahci_do_reset_drive(struct ata_channel *chp, int drive, int flags, |
ahci_do_reset_drive(struct ata_channel *chp, int drive, int flags, |
uint32_t *sigp, struct ata_xfer *xfer) |
uint32_t *sigp, uint8_t c_slot) |
{ |
{ |
struct ahci_channel *achp = (struct ahci_channel *)chp; |
struct ahci_channel *achp = (struct ahci_channel *)chp; |
struct ahci_softc *sc = (struct ahci_softc *)chp->ch_atac; |
struct ahci_softc *sc = (struct ahci_softc *)chp->ch_atac; |
|
|
|
|
/* polled command, assume interrupts are disabled */ |
/* polled command, assume interrupts are disabled */ |
|
|
cmd_h = &achp->ahcic_cmdh[xfer->c_slot]; |
cmd_h = &achp->ahcic_cmdh[c_slot]; |
cmd_tbl = achp->ahcic_cmd_tbl[xfer->c_slot]; |
cmd_tbl = achp->ahcic_cmd_tbl[c_slot]; |
cmd_h->cmdh_flags = htole16(AHCI_CMDH_F_RST | AHCI_CMDH_F_CBSY | |
cmd_h->cmdh_flags = htole16(AHCI_CMDH_F_RST | AHCI_CMDH_F_CBSY | |
RHD_FISLEN / 4 | (drive << AHCI_CMDH_F_PMP_SHIFT)); |
RHD_FISLEN / 4 | (drive << AHCI_CMDH_F_PMP_SHIFT)); |
cmd_h->cmdh_prdbc = 0; |
cmd_h->cmdh_prdbc = 0; |
|
|
cmd_tbl->cmdt_cfis[fis_type] = RHD_FISTYPE; |
cmd_tbl->cmdt_cfis[fis_type] = RHD_FISTYPE; |
cmd_tbl->cmdt_cfis[rhd_c] = drive; |
cmd_tbl->cmdt_cfis[rhd_c] = drive; |
cmd_tbl->cmdt_cfis[rhd_control] = WDCTL_RST; |
cmd_tbl->cmdt_cfis[rhd_control] = WDCTL_RST; |
switch(ahci_exec_fis(chp, 100, flags, xfer->c_slot)) { |
switch(ahci_exec_fis(chp, 100, flags, c_slot)) { |
case ERR_DF: |
case ERR_DF: |
case TIMEOUT: |
case TIMEOUT: |
aprint_error("%s channel %d: setting WDCTL_RST failed " |
aprint_error("%s channel %d: setting WDCTL_RST failed " |
|
|
cmd_tbl->cmdt_cfis[fis_type] = RHD_FISTYPE; |
cmd_tbl->cmdt_cfis[fis_type] = RHD_FISTYPE; |
cmd_tbl->cmdt_cfis[rhd_c] = drive; |
cmd_tbl->cmdt_cfis[rhd_c] = drive; |
cmd_tbl->cmdt_cfis[rhd_control] = 0; |
cmd_tbl->cmdt_cfis[rhd_control] = 0; |
switch(ahci_exec_fis(chp, 310, flags, xfer->c_slot)) { |
switch(ahci_exec_fis(chp, 310, flags, c_slot)) { |
case ERR_DF: |
case ERR_DF: |
case TIMEOUT: |
case TIMEOUT: |
if ((sc->sc_ahci_quirks & AHCI_QUIRK_BADPMPRESET) != 0 && |
if ((sc->sc_ahci_quirks & AHCI_QUIRK_BADPMPRESET) != 0 && |
Line 884 ahci_reset_channel(struct ata_channel *c |
|
Line 887 ahci_reset_channel(struct ata_channel *c |
|
struct ahci_channel *achp = (struct ahci_channel *)chp; |
struct ahci_channel *achp = (struct ahci_channel *)chp; |
int i, tfd; |
int i, tfd; |
|
|
ata_channel_lock(chp); |
ata_channel_lock_owned(chp); |
|
|
ahci_channel_stop(sc, chp, flags); |
ahci_channel_stop(sc, chp, flags); |
if (sata_reset_interface(chp, sc->sc_ahcit, achp->ahcic_scontrol, |
if (sata_reset_interface(chp, sc->sc_ahcit, achp->ahcic_scontrol, |
Line 914 ahci_reset_channel(struct ata_channel *c |
|
Line 917 ahci_reset_channel(struct ata_channel *c |
|
/* clear port interrupt register */ |
/* clear port interrupt register */ |
AHCI_WRITE(sc, AHCI_P_IS(chp->ch_channel), 0xffffffff); |
AHCI_WRITE(sc, AHCI_P_IS(chp->ch_channel), 0xffffffff); |
|
|
ata_channel_unlock(chp); |
|
|
|
return; |
return; |
} |
} |
|
|
Line 943 ahci_probe_drive(struct ata_channel *chp |
|
Line 944 ahci_probe_drive(struct ata_channel *chp |
|
struct ahci_softc *sc = (struct ahci_softc *)chp->ch_atac; |
struct ahci_softc *sc = (struct ahci_softc *)chp->ch_atac; |
struct ahci_channel *achp = (struct ahci_channel *)chp; |
struct ahci_channel *achp = (struct ahci_channel *)chp; |
uint32_t sig; |
uint32_t sig; |
struct ata_xfer *xfer; |
uint8_t c_slot; |
|
|
|
ata_channel_lock(chp); |
|
|
xfer = ata_get_xfer_ext(chp, 0, 0); |
/* get a slot for running the command on */ |
if (xfer == NULL) { |
if (!ata_queue_alloc_slot(chp, &c_slot, ATA_MAX_OPENINGS)) { |
aprint_error_dev(sc->sc_atac.atac_dev, |
aprint_error_dev(sc->sc_atac.atac_dev, |
"%s: failed to get xfer port %d\n", |
"%s: failed to get xfer port %d\n", |
__func__, chp->ch_channel); |
__func__, chp->ch_channel); |
|
ata_channel_unlock(chp); |
return; |
return; |
} |
} |
|
|
ata_channel_lock(chp); |
|
|
|
/* bring interface up, accept FISs, power up and spin up device */ |
/* bring interface up, accept FISs, power up and spin up device */ |
AHCI_WRITE(sc, AHCI_P_CMD(chp->ch_channel), |
AHCI_WRITE(sc, AHCI_P_CMD(chp->ch_channel), |
Line 966 ahci_probe_drive(struct ata_channel *chp |
|
Line 968 ahci_probe_drive(struct ata_channel *chp |
|
ata_delay(chp, 500, "ahcidv", AT_WAIT); |
ata_delay(chp, 500, "ahcidv", AT_WAIT); |
if (sc->sc_ahci_cap & AHCI_CAP_SPM) { |
if (sc->sc_ahci_cap & AHCI_CAP_SPM) { |
ahci_do_reset_drive(chp, PMP_PORT_CTL, AT_WAIT, &sig, |
ahci_do_reset_drive(chp, PMP_PORT_CTL, AT_WAIT, &sig, |
xfer); |
c_slot); |
} else { |
} else { |
ahci_do_reset_drive(chp, 0, AT_WAIT, &sig, xfer); |
ahci_do_reset_drive(chp, 0, AT_WAIT, &sig, c_slot); |
} |
} |
sata_interpret_sig(chp, 0, sig); |
sata_interpret_sig(chp, 0, sig); |
/* if we have a PMP attached, inform the controller */ |
/* if we have a PMP attached, inform the controller */ |
Line 980 ahci_probe_drive(struct ata_channel *chp |
|
Line 982 ahci_probe_drive(struct ata_channel *chp |
|
} |
} |
/* clear port interrupt register */ |
/* clear port interrupt register */ |
AHCI_WRITE(sc, AHCI_P_IS(chp->ch_channel), 0xffffffff); |
AHCI_WRITE(sc, AHCI_P_IS(chp->ch_channel), 0xffffffff); |
|
|
/* and enable interrupts */ |
/* and enable interrupts */ |
AHCI_WRITE(sc, AHCI_P_IE(chp->ch_channel), |
AHCI_WRITE(sc, AHCI_P_IE(chp->ch_channel), |
AHCI_P_IX_TFES | AHCI_P_IX_HBFS | AHCI_P_IX_HBDS | |
AHCI_P_IX_TFES | AHCI_P_IX_HBFS | AHCI_P_IX_HBDS | |
Line 994 ahci_probe_drive(struct ata_channel *chp |
|
Line 996 ahci_probe_drive(struct ata_channel *chp |
|
default: |
default: |
break; |
break; |
} |
} |
|
|
|
ata_queue_free_slot(chp, c_slot); |
|
|
ata_channel_unlock(chp); |
ata_channel_unlock(chp); |
} |
} |
|
|
Line 1003 ahci_setup_channel(struct ata_channel *c |
|
Line 1008 ahci_setup_channel(struct ata_channel *c |
|
return; |
return; |
} |
} |
|
|
|
static const struct ata_xfer_ops ahci_cmd_xfer_ops = { |
|
.c_start = ahci_cmd_start, |
|
.c_poll = ahci_cmd_poll, |
|
.c_abort = ahci_cmd_abort, |
|
.c_intr = ahci_cmd_complete, |
|
.c_kill_xfer = ahci_cmd_kill_xfer, |
|
}; |
|
|
static int |
static int |
ahci_exec_command(struct ata_drive_datas *drvp, struct ata_xfer *xfer) |
ahci_exec_command(struct ata_drive_datas *drvp, struct ata_xfer *xfer) |
{ |
{ |
Line 1022 ahci_exec_command(struct ata_drive_datas |
|
Line 1035 ahci_exec_command(struct ata_drive_datas |
|
xfer->c_drive = drvp->drive; |
xfer->c_drive = drvp->drive; |
xfer->c_databuf = ata_c->data; |
xfer->c_databuf = ata_c->data; |
xfer->c_bcount = ata_c->bcount; |
xfer->c_bcount = ata_c->bcount; |
xfer->c_start = ahci_cmd_start; |
xfer->ops = &ahci_cmd_xfer_ops; |
xfer->c_poll = ahci_cmd_poll; |
|
xfer->c_abort = ahci_cmd_abort; |
|
xfer->c_intr = ahci_cmd_complete; |
|
xfer->c_kill_xfer = ahci_cmd_kill_xfer; |
|
s = splbio(); |
s = splbio(); |
ata_exec_xfer(chp, xfer); |
ata_exec_xfer(chp, xfer); |
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
Line 1038 ahci_exec_command(struct ata_drive_datas |
|
Line 1047 ahci_exec_command(struct ata_drive_datas |
|
ret = ATACMD_COMPLETE; |
ret = ATACMD_COMPLETE; |
} else { |
} else { |
if (ata_c->flags & AT_WAIT) { |
if (ata_c->flags & AT_WAIT) { |
ata_channel_lock(chp); |
ata_wait_cmd(chp, xfer); |
if ((ata_c->flags & AT_DONE) == 0) { |
|
ata_wait_xfer(chp, xfer); |
|
KASSERT((ata_c->flags & AT_DONE) != 0); |
|
} |
|
ata_channel_unlock(chp); |
|
ret = ATACMD_COMPLETE; |
ret = ATACMD_COMPLETE; |
} else { |
} else { |
ret = ATACMD_QUEUED; |
ret = ATACMD_QUEUED; |
Line 1069 ahci_cmd_start(struct ata_channel *chp, |
|
Line 1073 ahci_cmd_start(struct ata_channel *chp, |
|
DEBUG_XFERS); |
DEBUG_XFERS); |
|
|
ata_channel_lock_owned(chp); |
ata_channel_lock_owned(chp); |
KASSERT((achp->ahcic_cmds_active & (1U << slot)) == 0); |
|
|
|
cmd_tbl = achp->ahcic_cmd_tbl[slot]; |
cmd_tbl = achp->ahcic_cmd_tbl[slot]; |
AHCIDEBUG_PRINT(("%s port %d tbl %p\n", AHCINAME(sc), chp->ch_channel, |
AHCIDEBUG_PRINT(("%s port %d tbl %p\n", AHCINAME(sc), chp->ch_channel, |
Line 1103 ahci_cmd_start(struct ata_channel *chp, |
|
Line 1106 ahci_cmd_start(struct ata_channel *chp, |
|
} |
} |
/* start command */ |
/* start command */ |
AHCI_WRITE(sc, AHCI_P_CI(chp->ch_channel), 1U << slot); |
AHCI_WRITE(sc, AHCI_P_CI(chp->ch_channel), 1U << slot); |
/* and says we started this command */ |
|
achp->ahcic_cmds_active |= 1U << slot; |
|
|
|
if ((ata_c->flags & AT_POLL) == 0) { |
if ((ata_c->flags & AT_POLL) == 0) { |
callout_reset(&xfer->c_timo_callout, mstohz(ata_c->timeout), |
callout_reset(&chp->c_timo_callout, mstohz(ata_c->timeout), |
ata_timeout, xfer); |
ata_timeout, chp); |
return ATASTART_STARTED; |
return ATASTART_STARTED; |
} else |
} else |
return ATASTART_POLL; |
return ATASTART_POLL; |
Line 1147 ahci_cmd_poll(struct ata_channel *chp, s |
|
Line 1148 ahci_cmd_poll(struct ata_channel *chp, s |
|
|
|
if ((xfer->c_ata_c.flags & AT_DONE) == 0) { |
if ((xfer->c_ata_c.flags & AT_DONE) == 0) { |
xfer->c_ata_c.flags |= AT_TIMEOU; |
xfer->c_ata_c.flags |= AT_TIMEOU; |
xfer->c_intr(chp, xfer, 0); |
xfer->ops->c_intr(chp, xfer, 0); |
} |
} |
/* reenable interrupts */ |
/* reenable interrupts */ |
AHCI_WRITE(sc, AHCI_GHC, AHCI_READ(sc, AHCI_GHC) | AHCI_GHC_IE); |
AHCI_WRITE(sc, AHCI_GHC, AHCI_READ(sc, AHCI_GHC) | AHCI_GHC_IE); |
Line 1162 ahci_cmd_abort(struct ata_channel *chp, |
|
Line 1163 ahci_cmd_abort(struct ata_channel *chp, |
|
static void |
static void |
ahci_cmd_kill_xfer(struct ata_channel *chp, struct ata_xfer *xfer, int reason) |
ahci_cmd_kill_xfer(struct ata_channel *chp, struct ata_xfer *xfer, int reason) |
{ |
{ |
struct ahci_channel *achp = (struct ahci_channel *)chp; |
|
struct ata_command *ata_c = &xfer->c_ata_c; |
struct ata_command *ata_c = &xfer->c_ata_c; |
bool deactivate = true; |
bool deactivate = true; |
|
|
Line 1187 ahci_cmd_kill_xfer(struct ata_channel *c |
|
Line 1187 ahci_cmd_kill_xfer(struct ata_channel *c |
|
panic("ahci_cmd_kill_xfer"); |
panic("ahci_cmd_kill_xfer"); |
} |
} |
|
|
if (deactivate) { |
|
KASSERT((achp->ahcic_cmds_active & (1U << xfer->c_slot)) != 0); |
|
achp->ahcic_cmds_active &= ~(1U << xfer->c_slot); |
|
ata_deactivate_xfer(chp, xfer); |
|
} |
|
|
|
ahci_cmd_done_end(chp, xfer); |
ahci_cmd_done_end(chp, xfer); |
|
|
|
if (deactivate) |
|
ata_deactivate_xfer(chp, xfer); |
} |
} |
|
|
static int |
static int |
Line 1211 ahci_cmd_complete(struct ata_channel *ch |
|
Line 1208 ahci_cmd_complete(struct ata_channel *ch |
|
if (ata_waitdrain_xfer_check(chp, xfer)) |
if (ata_waitdrain_xfer_check(chp, xfer)) |
return 0; |
return 0; |
|
|
KASSERT((achp->ahcic_cmds_active & (1U << xfer->c_slot)) != 0); |
|
achp->ahcic_cmds_active &= ~(1U << xfer->c_slot); |
|
ata_deactivate_xfer(chp, xfer); |
|
|
|
if (xfer->c_flags & C_TIMEOU) { |
if (xfer->c_flags & C_TIMEOU) { |
ata_c->flags |= AT_TIMEOU; |
ata_c->flags |= AT_TIMEOU; |
} |
} |
Line 1230 ahci_cmd_complete(struct ata_channel *ch |
|
Line 1223 ahci_cmd_complete(struct ata_channel *ch |
|
satafis_rdh_cmd_readreg(ata_c, achp->ahcic_rfis->rfis_rfis); |
satafis_rdh_cmd_readreg(ata_c, achp->ahcic_rfis->rfis_rfis); |
|
|
ahci_cmd_done(chp, xfer); |
ahci_cmd_done(chp, xfer); |
|
|
|
ata_deactivate_xfer(chp, xfer); |
|
|
|
if ((ata_c->flags & (AT_TIMEOU|AT_ERROR)) == 0) |
|
atastart(chp); |
|
|
return 0; |
return 0; |
} |
} |
|
|
Line 1240 ahci_cmd_done(struct ata_channel *chp, s |
|
Line 1239 ahci_cmd_done(struct ata_channel *chp, s |
|
struct ahci_channel *achp = (struct ahci_channel *)chp; |
struct ahci_channel *achp = (struct ahci_channel *)chp; |
struct ata_command *ata_c = &xfer->c_ata_c; |
struct ata_command *ata_c = &xfer->c_ata_c; |
uint16_t *idwordbuf; |
uint16_t *idwordbuf; |
int flags = ata_c->flags; |
|
int i; |
int i; |
|
|
AHCIDEBUG_PRINT(("ahci_cmd_done channel %d flags %#x/%#x\n", |
AHCIDEBUG_PRINT(("ahci_cmd_done channel %d flags %#x/%#x\n", |
Line 1268 ahci_cmd_done(struct ata_channel *chp, s |
|
Line 1266 ahci_cmd_done(struct ata_channel *chp, s |
|
|
|
if (achp->ahcic_cmdh[xfer->c_slot].cmdh_prdbc) |
if (achp->ahcic_cmdh[xfer->c_slot].cmdh_prdbc) |
ata_c->flags |= AT_XFDONE; |
ata_c->flags |= AT_XFDONE; |
|
|
ahci_cmd_done_end(chp, xfer); |
ahci_cmd_done_end(chp, xfer); |
if ((flags & (AT_TIMEOU|AT_ERROR)) == 0) |
|
atastart(chp); |
|
} |
} |
|
|
static void |
static void |
Line 1278 ahci_cmd_done_end(struct ata_channel *ch |
|
Line 1275 ahci_cmd_done_end(struct ata_channel *ch |
|
{ |
{ |
struct ata_command *ata_c = &xfer->c_ata_c; |
struct ata_command *ata_c = &xfer->c_ata_c; |
|
|
ata_channel_lock(chp); |
|
|
|
ata_c->flags |= AT_DONE; |
ata_c->flags |= AT_DONE; |
|
|
if (ata_c->flags & AT_WAIT) |
|
ata_wake_xfer(chp, xfer); |
|
|
|
ata_channel_unlock(chp); |
|
return; |
|
} |
} |
|
|
|
static const struct ata_xfer_ops ahci_bio_xfer_ops = { |
|
.c_start = ahci_bio_start, |
|
.c_poll = ahci_bio_poll, |
|
.c_abort = ahci_bio_abort, |
|
.c_intr = ahci_bio_complete, |
|
.c_kill_xfer = ahci_bio_kill_xfer, |
|
}; |
|
|
static int |
static int |
ahci_ata_bio(struct ata_drive_datas *drvp, struct ata_xfer *xfer) |
ahci_ata_bio(struct ata_drive_datas *drvp, struct ata_xfer *xfer) |
{ |
{ |
Line 1304 ahci_ata_bio(struct ata_drive_datas *drv |
|
Line 1301 ahci_ata_bio(struct ata_drive_datas *drv |
|
xfer->c_drive = drvp->drive; |
xfer->c_drive = drvp->drive; |
xfer->c_databuf = ata_bio->databuf; |
xfer->c_databuf = ata_bio->databuf; |
xfer->c_bcount = ata_bio->bcount; |
xfer->c_bcount = ata_bio->bcount; |
xfer->c_start = ahci_bio_start; |
xfer->ops = &ahci_bio_xfer_ops; |
xfer->c_poll = ahci_bio_poll; |
|
xfer->c_abort = ahci_bio_abort; |
|
xfer->c_intr = ahci_bio_complete; |
|
xfer->c_kill_xfer = ahci_bio_kill_xfer; |
|
ata_exec_xfer(chp, xfer); |
ata_exec_xfer(chp, xfer); |
return (ata_bio->flags & ATA_ITSDONE) ? ATACMD_COMPLETE : ATACMD_QUEUED; |
return (ata_bio->flags & ATA_ITSDONE) ? ATACMD_COMPLETE : ATACMD_QUEUED; |
} |
} |
Line 1359 ahci_bio_start(struct ata_channel *chp, |
|
Line 1352 ahci_bio_start(struct ata_channel *chp, |
|
AHCI_WRITE(sc, AHCI_P_SACT(chp->ch_channel), 1U << xfer->c_slot); |
AHCI_WRITE(sc, AHCI_P_SACT(chp->ch_channel), 1U << xfer->c_slot); |
/* start command */ |
/* start command */ |
AHCI_WRITE(sc, AHCI_P_CI(chp->ch_channel), 1U << xfer->c_slot); |
AHCI_WRITE(sc, AHCI_P_CI(chp->ch_channel), 1U << xfer->c_slot); |
/* and says we started this command */ |
|
achp->ahcic_cmds_active |= 1U << xfer->c_slot; |
|
|
|
if ((xfer->c_flags & C_POLL) == 0) { |
if ((xfer->c_flags & C_POLL) == 0) { |
callout_reset(&xfer->c_timo_callout, mstohz(ATA_DELAY), |
callout_reset(&chp->c_timo_callout, mstohz(ATA_DELAY), |
ata_timeout, xfer); |
ata_timeout, chp); |
return ATASTART_STARTED; |
return ATASTART_STARTED; |
} else |
} else |
return ATASTART_POLL; |
return ATASTART_POLL; |
Line 1396 ahci_bio_poll(struct ata_channel *chp, s |
|
Line 1387 ahci_bio_poll(struct ata_channel *chp, s |
|
DEBUG_XFERS); |
DEBUG_XFERS); |
if ((xfer->c_bio.flags & ATA_ITSDONE) == 0) { |
if ((xfer->c_bio.flags & ATA_ITSDONE) == 0) { |
xfer->c_bio.error = TIMEOUT; |
xfer->c_bio.error = TIMEOUT; |
xfer->c_intr(chp, xfer, 0); |
xfer->ops->c_intr(chp, xfer, 0); |
} |
} |
/* reenable interrupts */ |
/* reenable interrupts */ |
AHCI_WRITE(sc, AHCI_GHC, AHCI_READ(sc, AHCI_GHC) | AHCI_GHC_IE); |
AHCI_WRITE(sc, AHCI_GHC, AHCI_READ(sc, AHCI_GHC) | AHCI_GHC_IE); |
Line 1413 ahci_bio_kill_xfer(struct ata_channel *c |
|
Line 1404 ahci_bio_kill_xfer(struct ata_channel *c |
|
{ |
{ |
int drive = xfer->c_drive; |
int drive = xfer->c_drive; |
struct ata_bio *ata_bio = &xfer->c_bio; |
struct ata_bio *ata_bio = &xfer->c_bio; |
struct ahci_channel *achp = (struct ahci_channel *)chp; |
|
bool deactivate = true; |
bool deactivate = true; |
|
|
AHCIDEBUG_PRINT(("ahci_bio_kill_xfer channel %d\n", chp->ch_channel), |
AHCIDEBUG_PRINT(("ahci_bio_kill_xfer channel %d\n", chp->ch_channel), |
Line 1439 ahci_bio_kill_xfer(struct ata_channel *c |
|
Line 1429 ahci_bio_kill_xfer(struct ata_channel *c |
|
} |
} |
ata_bio->r_error = WDCE_ABRT; |
ata_bio->r_error = WDCE_ABRT; |
|
|
if (deactivate) { |
if (deactivate) |
KASSERT((achp->ahcic_cmds_active & (1U << xfer->c_slot)) != 0); |
|
achp->ahcic_cmds_active &= ~(1U << xfer->c_slot); |
|
ata_deactivate_xfer(chp, xfer); |
ata_deactivate_xfer(chp, xfer); |
} |
|
|
|
(*chp->ch_drive[drive].drv_done)(chp->ch_drive[drive].drv_softc, xfer); |
(*chp->ch_drive[drive].drv_done)(chp->ch_drive[drive].drv_softc, xfer); |
} |
} |
Line 1462 ahci_bio_complete(struct ata_channel *ch |
|
Line 1449 ahci_bio_complete(struct ata_channel *ch |
|
if (ata_waitdrain_xfer_check(chp, xfer)) |
if (ata_waitdrain_xfer_check(chp, xfer)) |
return 0; |
return 0; |
|
|
KASSERT((achp->ahcic_cmds_active & (1U << xfer->c_slot)) != 0); |
|
achp->ahcic_cmds_active &= ~(1U << xfer->c_slot); |
|
ata_deactivate_xfer(chp, xfer); |
|
|
|
if (xfer->c_flags & C_TIMEOU) { |
if (xfer->c_flags & C_TIMEOU) { |
ata_bio->error = TIMEOUT; |
ata_bio->error = TIMEOUT; |
} |
} |
Line 1506 ahci_bio_complete(struct ata_channel *ch |
|
Line 1489 ahci_bio_complete(struct ata_channel *ch |
|
le32toh(achp->ahcic_cmdh[xfer->c_slot].cmdh_prdbc); |
le32toh(achp->ahcic_cmdh[xfer->c_slot].cmdh_prdbc); |
} |
} |
AHCIDEBUG_PRINT((" now %ld\n", ata_bio->bcount), DEBUG_XFERS); |
AHCIDEBUG_PRINT((" now %ld\n", ata_bio->bcount), DEBUG_XFERS); |
|
|
|
ata_deactivate_xfer(chp, xfer); |
|
|
(*chp->ch_drive[drive].drv_done)(chp->ch_drive[drive].drv_softc, xfer); |
(*chp->ch_drive[drive].drv_done)(chp->ch_drive[drive].drv_softc, xfer); |
if ((AHCI_TFD_ST(tfd) & WDCS_ERR) == 0) |
if ((AHCI_TFD_ST(tfd) & WDCS_ERR) == 0) |
atastart(chp); |
atastart(chp); |
Line 1578 ahci_channel_start(struct ahci_softc *sc |
|
Line 1564 ahci_channel_start(struct ahci_softc *sc |
|
AHCI_WRITE(sc, AHCI_P_CMD(chp->ch_channel), p_cmd); |
AHCI_WRITE(sc, AHCI_P_CMD(chp->ch_channel), p_cmd); |
} |
} |
|
|
static void |
|
ahci_hold(struct ahci_channel *achp) |
|
{ |
|
achp->ahcic_cmds_hold |= achp->ahcic_cmds_active; |
|
achp->ahcic_cmds_active = 0; |
|
} |
|
|
|
static void |
|
ahci_unhold(struct ahci_channel *achp) |
|
{ |
|
achp->ahcic_cmds_active = achp->ahcic_cmds_hold; |
|
achp->ahcic_cmds_hold = 0; |
|
} |
|
|
|
/* Recover channel after command failure */ |
/* Recover channel after command failure */ |
void |
void |
ahci_channel_recover(struct ahci_softc *sc, struct ata_channel *chp, int tfd) |
ahci_channel_recover(struct ahci_softc *sc, struct ata_channel *chp, int tfd) |
{ |
{ |
struct ahci_channel *achp = (struct ahci_channel *)chp; |
int drive = ATACH_NODRIVE; |
struct ata_drive_datas *drvp; |
|
uint8_t slot, eslot, st, err; |
|
int drive = -1, error; |
|
struct ata_xfer *xfer; |
|
bool reset = false; |
bool reset = false; |
|
|
KASSERT(!achp->ahcic_recovering); |
ata_channel_lock(chp); |
|
KASSERT(!ISSET(chp->ch_flags, ATACH_RECOVERING)); |
achp->ahcic_recovering = true; |
SET(chp->ch_flags, ATACH_RECOVERING); |
|
ata_channel_unlock(chp); |
|
|
/* |
/* |
* Read FBS to get the drive which caused the error, if PM is in use. |
* Read FBS to get the drive which caused the error, if PM is in use. |
Line 1636 ahci_channel_recover(struct ahci_softc * |
|
Line 1605 ahci_channel_recover(struct ahci_softc * |
|
} |
} |
if ((fbs & AHCI_P_FBS_DEC) != 0) { |
if ((fbs & AHCI_P_FBS_DEC) != 0) { |
/* follow non-device specific recovery */ |
/* follow non-device specific recovery */ |
drive = -1; |
drive = ATACH_NODRIVE; |
reset = true; |
reset = true; |
} |
} |
} else { |
} else { |
/* not device specific, reset channel */ |
/* not device specific, reset channel */ |
drive = -1; |
drive = ATACH_NODRIVE; |
reset = true; |
reset = true; |
} |
} |
} else |
} else |
drive = 0; |
drive = 0; |
|
|
drvp = &chp->ch_drive[drive]; |
|
|
|
/* |
/* |
* If BSY or DRQ bits are set, must execute COMRESET to return |
* If BSY or DRQ bits are set, must execute COMRESET to return |
* device to idle state. If drive is idle, it's enough to just |
* device to idle state. If drive is idle, it's enough to just |
Line 1656 ahci_channel_recover(struct ahci_softc * |
|
Line 1623 ahci_channel_recover(struct ahci_softc * |
|
* After resetting CMD.ST, need to execute READ LOG EXT for NCQ |
* After resetting CMD.ST, need to execute READ LOG EXT for NCQ |
* to unblock device processing if COMRESET was not done. |
* to unblock device processing if COMRESET was not done. |
*/ |
*/ |
if (reset || (AHCI_TFD_ST(tfd) & (WDCS_BSY|WDCS_DRQ)) != 0) |
if (reset || (AHCI_TFD_ST(tfd) & (WDCS_BSY|WDCS_DRQ)) != 0) { |
goto reset; |
ata_channel_lock(chp); |
|
|
KASSERT(drive >= 0); |
|
ahci_channel_stop(sc, chp, AT_POLL); |
|
ahci_channel_start(sc, chp, AT_POLL, |
|
(sc->sc_ahci_cap & AHCI_CAP_CLO) ? 1 : 0); |
|
|
|
ahci_hold(achp); |
|
|
|
/* |
|
* When running NCQ commands, READ LOG EXT is necessary to clear the |
|
* error condition and unblock the device. |
|
*/ |
|
error = ata_read_log_ext_ncq(drvp, AT_POLL, &eslot, &st, &err); |
|
|
|
ahci_unhold(achp); |
|
|
|
switch (error) { |
|
case 0: |
|
/* Error out the particular NCQ xfer, then requeue the others */ |
|
if ((achp->ahcic_cmds_active & (1U << eslot)) != 0) { |
|
xfer = ata_queue_hwslot_to_xfer(chp, eslot); |
|
xfer->c_flags |= C_RECOVERED; |
|
xfer->c_intr(chp, xfer, |
|
(err << AHCI_P_TFD_ERR_SHIFT) | st); |
|
} |
|
break; |
|
|
|
case EOPNOTSUPP: |
|
/* |
|
* Non-NCQ command error, just find the slot and end with |
|
* the error. |
|
*/ |
|
for (slot = 0; slot < sc->sc_ncmds; slot++) { |
|
if ((achp->ahcic_cmds_active & (1U << slot)) != 0) { |
|
xfer = ata_queue_hwslot_to_xfer(chp, slot); |
|
xfer->c_intr(chp, xfer, tfd); |
|
} |
|
} |
|
break; |
|
|
|
case EAGAIN: |
|
/* |
|
* Failed to get resources to run the recovery command, must |
|
* reset the drive. This will also kill all still outstanding |
|
* transfers. |
|
*/ |
|
reset: |
|
ahci_reset_channel(chp, AT_POLL); |
ahci_reset_channel(chp, AT_POLL); |
|
ata_channel_unlock(chp); |
goto out; |
goto out; |
/* NOTREACHED */ |
|
|
|
default: |
|
/* |
|
* The command to get the slot failed. Kill outstanding |
|
* commands for the same drive only. No need to reset |
|
* the drive, it's unblocked nevertheless. |
|
*/ |
|
break; |
|
} |
} |
|
|
/* Requeue all unfinished commands for same drive as failed command */ |
KASSERT(drive != ATACH_NODRIVE && drive >= 0); |
for (slot = 0; slot < sc->sc_ncmds; slot++) { |
ahci_channel_stop(sc, chp, AT_POLL); |
if ((achp->ahcic_cmds_active & (1U << slot)) == 0) |
ahci_channel_start(sc, chp, AT_POLL, |
continue; |
(sc->sc_ahci_cap & AHCI_CAP_CLO) ? 1 : 0); |
|
|
xfer = ata_queue_hwslot_to_xfer(chp, slot); |
|
if (drive != xfer->c_drive) |
|
continue; |
|
|
|
xfer->c_kill_xfer(chp, xfer, |
ata_recovery_resume(chp, drive, tfd, AT_POLL); |
(error == 0) ? KILL_REQUEUE : KILL_RESET); |
|
} |
|
|
|
out: |
out: |
/* Drive unblocked, back to normal operation */ |
/* Drive unblocked, back to normal operation */ |
achp->ahcic_recovering = false; |
ata_channel_lock(chp); |
|
CLR(chp->ch_flags, ATACH_RECOVERING); |
|
ata_channel_unlock(chp); |
|
|
atastart(chp); |
atastart(chp); |
} |
} |
|
|
Line 1836 ahci_atapi_kill_pending(struct scsipi_pe |
|
Line 1745 ahci_atapi_kill_pending(struct scsipi_pe |
|
ata_kill_pending(&chp->ch_drive[periph->periph_target]); |
ata_kill_pending(&chp->ch_drive[periph->periph_target]); |
} |
} |
|
|
|
static const struct ata_xfer_ops ahci_atapi_xfer_ops = { |
|
.c_start = ahci_atapi_start, |
|
.c_poll = ahci_atapi_poll, |
|
.c_abort = ahci_atapi_abort, |
|
.c_intr = ahci_atapi_complete, |
|
.c_kill_xfer = ahci_atapi_kill_xfer, |
|
}; |
|
|
static void |
static void |
ahci_atapi_scsipi_request(struct scsipi_channel *chan, |
ahci_atapi_scsipi_request(struct scsipi_channel *chan, |
scsipi_adapter_req_t req, void *arg) |
scsipi_adapter_req_t req, void *arg) |
Line 1859 ahci_atapi_scsipi_request(struct scsipi_ |
|
Line 1776 ahci_atapi_scsipi_request(struct scsipi_ |
|
scsipi_done(sc_xfer); |
scsipi_done(sc_xfer); |
return; |
return; |
} |
} |
xfer = ata_get_xfer_ext(atac->atac_channels[channel], 0, 0); |
xfer = ata_get_xfer(atac->atac_channels[channel], false); |
if (xfer == NULL) { |
if (xfer == NULL) { |
sc_xfer->error = XS_RESOURCE_SHORTAGE; |
sc_xfer->error = XS_RESOURCE_SHORTAGE; |
scsipi_done(sc_xfer); |
scsipi_done(sc_xfer); |
Line 1870 ahci_atapi_scsipi_request(struct scsipi_ |
|
Line 1787 ahci_atapi_scsipi_request(struct scsipi_ |
|
xfer->c_flags |= C_POLL; |
xfer->c_flags |= C_POLL; |
xfer->c_drive = drive; |
xfer->c_drive = drive; |
xfer->c_flags |= C_ATAPI; |
xfer->c_flags |= C_ATAPI; |
xfer->c_scsipi = sc_xfer; |
|
xfer->c_databuf = sc_xfer->data; |
xfer->c_databuf = sc_xfer->data; |
xfer->c_bcount = sc_xfer->datalen; |
xfer->c_bcount = sc_xfer->datalen; |
xfer->c_start = ahci_atapi_start; |
xfer->ops = &ahci_atapi_xfer_ops; |
xfer->c_poll = ahci_atapi_poll; |
xfer->c_scsipi = sc_xfer; |
xfer->c_abort = ahci_atapi_abort; |
xfer->c_atapi.c_dscpoll = 0; |
xfer->c_intr = ahci_atapi_complete; |
|
xfer->c_kill_xfer = ahci_atapi_kill_xfer; |
|
xfer->c_dscpoll = 0; |
|
s = splbio(); |
s = splbio(); |
ata_exec_xfer(atac->atac_channels[channel], xfer); |
ata_exec_xfer(atac->atac_channels[channel], xfer); |
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
Line 1944 ahci_atapi_start(struct ata_channel *chp |
|
Line 1857 ahci_atapi_start(struct ata_channel *chp |
|
} |
} |
/* start command */ |
/* start command */ |
AHCI_WRITE(sc, AHCI_P_CI(chp->ch_channel), 1U << xfer->c_slot); |
AHCI_WRITE(sc, AHCI_P_CI(chp->ch_channel), 1U << xfer->c_slot); |
/* and says we started this command */ |
|
achp->ahcic_cmds_active |= 1U << xfer->c_slot; |
|
|
|
if ((xfer->c_flags & C_POLL) == 0) { |
if ((xfer->c_flags & C_POLL) == 0) { |
callout_reset(&xfer->c_timo_callout, mstohz(sc_xfer->timeout), |
callout_reset(&chp->c_timo_callout, mstohz(sc_xfer->timeout), |
ata_timeout, xfer); |
ata_timeout, chp); |
return ATASTART_STARTED; |
return ATASTART_STARTED; |
} else |
} else |
return ATASTART_POLL; |
return ATASTART_POLL; |
Line 1981 ahci_atapi_poll(struct ata_channel *chp, |
|
Line 1892 ahci_atapi_poll(struct ata_channel *chp, |
|
DEBUG_XFERS); |
DEBUG_XFERS); |
if ((xfer->c_scsipi->xs_status & XS_STS_DONE) == 0) { |
if ((xfer->c_scsipi->xs_status & XS_STS_DONE) == 0) { |
xfer->c_scsipi->error = XS_TIMEOUT; |
xfer->c_scsipi->error = XS_TIMEOUT; |
xfer->c_intr(chp, xfer, 0); |
xfer->ops->c_intr(chp, xfer, 0); |
} |
} |
/* reenable interrupts */ |
/* reenable interrupts */ |
AHCI_WRITE(sc, AHCI_GHC, AHCI_READ(sc, AHCI_GHC) | AHCI_GHC_IE); |
AHCI_WRITE(sc, AHCI_GHC, AHCI_READ(sc, AHCI_GHC) | AHCI_GHC_IE); |
Line 2006 ahci_atapi_complete(struct ata_channel * |
|
Line 1917 ahci_atapi_complete(struct ata_channel * |
|
if (ata_waitdrain_xfer_check(chp, xfer)) |
if (ata_waitdrain_xfer_check(chp, xfer)) |
return 0; |
return 0; |
|
|
KASSERT((achp->ahcic_cmds_active & (1U << xfer->c_slot)) != 0); |
|
achp->ahcic_cmds_active &= ~(1U << xfer->c_slot); |
|
ata_deactivate_xfer(chp, xfer); |
|
|
|
if (xfer->c_flags & C_TIMEOU) { |
if (xfer->c_flags & C_TIMEOU) { |
sc_xfer->error = XS_TIMEOUT; |
sc_xfer->error = XS_TIMEOUT; |
} |
} |
Line 2039 ahci_atapi_complete(struct ata_channel * |
|
Line 1946 ahci_atapi_complete(struct ata_channel * |
|
sc_xfer->error = XS_BUSY; |
sc_xfer->error = XS_BUSY; |
sc_xfer->status = SCSI_CHECK; |
sc_xfer->status = SCSI_CHECK; |
} |
} |
} |
} |
|
|
|
ata_deactivate_xfer(chp, xfer); |
|
|
ata_free_xfer(chp, xfer); |
ata_free_xfer(chp, xfer); |
scsipi_done(sc_xfer); |
scsipi_done(sc_xfer); |
if ((AHCI_TFD_ST(tfd) & WDCS_ERR) == 0) |
if ((AHCI_TFD_ST(tfd) & WDCS_ERR) == 0) |
|
|
ahci_atapi_kill_xfer(struct ata_channel *chp, struct ata_xfer *xfer, int reason) |
ahci_atapi_kill_xfer(struct ata_channel *chp, struct ata_xfer *xfer, int reason) |
{ |
{ |
struct scsipi_xfer *sc_xfer = xfer->c_scsipi; |
struct scsipi_xfer *sc_xfer = xfer->c_scsipi; |
struct ahci_channel *achp = (struct ahci_channel *)chp; |
|
bool deactivate = true; |
bool deactivate = true; |
|
|
/* remove this command from xfer queue */ |
/* remove this command from xfer queue */ |
Line 2073 ahci_atapi_kill_xfer(struct ata_channel |
|
Line 1982 ahci_atapi_kill_xfer(struct ata_channel |
|
panic("ahci_ata_atapi_kill_xfer"); |
panic("ahci_ata_atapi_kill_xfer"); |
} |
} |
|
|
if (deactivate) { |
if (deactivate) |
KASSERT((achp->ahcic_cmds_active & (1U << xfer->c_slot)) != 0); |
|
achp->ahcic_cmds_active &= ~(1U << xfer->c_slot); |
|
ata_deactivate_xfer(chp, xfer); |
ata_deactivate_xfer(chp, xfer); |
} |
|
|
|
ata_free_xfer(chp, xfer); |
ata_free_xfer(chp, xfer); |
scsipi_done(sc_xfer); |
scsipi_done(sc_xfer); |