static void ahci_dummy_done(struct ata_xfer *xa);
static void ahci_empty_done(struct ahci_ccb *ccb);
static void ahci_ata_cmd_done(struct ahci_ccb *ccb);
+static u_int32_t ahci_pactive(struct ahci_port *ap);
/*
* Initialize the global AHCI hardware. This code does not set up any of
}
/*
- * [re]initialize an idle port. No CCBs should be active.
+ * [re]initialize an idle port. No CCBs should be active. (from port thread)
*
* This function is called during the initial port allocation sequence
* and is also called on hot-plug insertion. We take no chances and
int
ahci_port_init(struct ahci_port *ap)
{
+ u_int32_t cmd;
+
/*
* Register [re]initialization
+ *
+ * Flush the TFD and SERR and make sure the port is stopped before
+ * enabling its interrupt. We no longer cycle the port start as
+ * the port should not be started unless a device is present.
+ *
+ * XXX should we enable FIS reception? (FRE)?
*/
+ ahci_pwrite(ap, AHCI_PREG_IE, 0);
+ ahci_port_stop(ap, 0);
if (ap->ap_sc->sc_cap & AHCI_REG_CAP_SSNTF)
ahci_pwrite(ap, AHCI_PREG_SNTF, -1);
- ap->ap_probe = ATA_PROBE_NEED_HARD_RESET;
- ap->ap_pmcount = 0;
+ ahci_flush_tfd(ap);
+ ahci_pwrite(ap, AHCI_PREG_SERR, -1);
/*
- * Flush the TFD and SERR and make sure the port is stopped before
- * enabling its interrupt. We no longer cycle the port start as
- * the port should not be started unless a device is present.
+ * If we are being harsh try to kill the port completely.
*
- * XXX should we enable FIS reception? (FRE)?
+ * AP_F_HARSH_REINIT is cleared in the hard reset state
*/
+ if (ap->ap_flags & AP_F_HARSH_REINIT) {
+ ahci_pwrite(ap, AHCI_PREG_SCTL, AHCI_PREG_SCTL_IPM_DISABLED);
+ ahci_pwrite(ap, AHCI_PREG_CMD, 0);
+
+ ahci_os_sleep(1000);
+
+ cmd = ahci_pread(ap, AHCI_PREG_CMD) & ~AHCI_PREG_CMD_ICC;
+ cmd &= ~(AHCI_PREG_CMD_CLO | AHCI_PREG_CMD_PMA);
+ cmd |= AHCI_PREG_CMD_FRE | AHCI_PREG_CMD_POD |
+ AHCI_PREG_CMD_SUD;
+ ahci_pwrite(ap, AHCI_PREG_CMD, cmd | AHCI_PREG_CMD_ICC_ACTIVE);
+ cmd = ahci_pread(ap, AHCI_PREG_CMD) & ~AHCI_PREG_CMD_ICC;
+ if ((cmd & AHCI_PREG_CMD_FRE) == 0) {
+ kprintf("%s: Warning: FRE did not come up during "
+ "harsh reinitialization\n",
+ PORTNAME(ap));
+ }
+ ahci_os_sleep(1000);
+ }
+
+ /*
+ * Clear any pending garbage and re-enable the interrupt before
+ * going to the next stage.
+ */
+ ap->ap_probe = ATA_PROBE_NEED_HARD_RESET;
+ ap->ap_pmcount = 0;
+
+ if (ap->ap_sc->sc_cap & AHCI_REG_CAP_SSNTF)
+ ahci_pwrite(ap, AHCI_PREG_SNTF, -1);
ahci_flush_tfd(ap);
ahci_pwrite(ap, AHCI_PREG_SERR, -1);
- ahci_port_stop(ap, 0);
+ ahci_pwrite(ap, AHCI_PREG_IS, -1);
+
ahci_port_interrupt_enable(ap);
+
return (0);
}
ahci_os_lock_port(ap);
ahci_pwrite(ap, AHCI_PREG_SERR,
- AHCI_PREG_SERR_DIAG_N | AHCI_PREG_SERR_DIAG_W);
+ AHCI_PREG_SERR_DIAG_N | AHCI_PREG_SERR_DIAG_W);
ahci_pwrite(ap, AHCI_PREG_IS, AHCI_PREG_IS_PRCS);
ap->ap_intmask |= AHCI_PREG_IE_PRCE;
sc->sc_ports[port] = NULL;
}
+static
+u_int32_t
+ahci_pactive(struct ahci_port *ap)
+{
+ u_int32_t mask;
+
+ mask = ahci_pread(ap, AHCI_PREG_CI);
+ if (ap->ap_sc->sc_cap & AHCI_REG_CAP_SNCQ)
+ mask |= ahci_pread(ap, AHCI_PREG_SACT);
+ return(mask);
+}
+
/*
* Start high-level command processing on the port
*/
* for the detect logic to settle down. If this is too
* short the softreset code will fail.
*/
- ahci_os_sleep(100);
+ if (ap->ap_flags & AP_F_HARSH_REINIT)
+ ahci_os_sleep(1000);
+ else
+ ahci_os_sleep(200);
+ ap->ap_flags &= ~AP_F_HARSH_REINIT;
/*
* Only SERR_DIAG_X needs to be cleared for TFD updates, but
ap->ap_type = ATA_PORT_T_NONE;
ahci_port_stop(ap, 0);
cmd = ahci_pread(ap, AHCI_PREG_CMD);
+ cmd &= ~(AHCI_PREG_CMD_CLO | AHCI_PREG_CMD_PMA | AHCI_PREG_CMD_ICC);
+ ahci_pwrite(ap, AHCI_PREG_CMD, cmd);
/*
* Clean up AT sub-ports on SATA port.
}
/*
- * Turn off port-multiplier control bit
- */
- if (cmd & AHCI_PREG_CMD_PMA) {
- cmd &= ~AHCI_PREG_CMD_PMA;
- ahci_pwrite(ap, AHCI_PREG_CMD, cmd);
- }
-
- /*
* Make sure FRE is active. There isn't anything we can do if it
* fails so just ignore errors.
*/
* This only applies if the controller supports SUD.
* NEVER use AHCI_PREG_DET_DISABLE.
*/
- cmd |= AHCI_PREG_CMD_SUD;
+ cmd |= AHCI_PREG_CMD_POD | AHCI_PREG_CMD_SUD;
+ cmd |= AHCI_PREG_CMD_ICC_ACTIVE;
ahci_pwrite(ap, AHCI_PREG_CMD, cmd);
ahci_os_sleep(1);
}
} while (timeout > 0);
- kprintf("%s: Poll timeout slot %d CMD: %b TFD: 0x%b SERR: %b\n",
- ATANAME(ap, ccb->ccb_xa.at), ccb->ccb_slot,
- ahci_pread(ap, AHCI_PREG_CMD), AHCI_PFMT_CMD,
- ahci_pread(ap, AHCI_PREG_TFD), AHCI_PFMT_TFD_STS,
- ahci_pread(ap, AHCI_PREG_SERR), AHCI_PFMT_SERR);
+ if ((ccb->ccb_xa.flags & ATA_F_SILENT) == 0) {
+ kprintf("%s: Poll timeout slot %d CMD: %b TFD: 0x%b SERR: %b\n",
+ ATANAME(ap, ccb->ccb_xa.at), ccb->ccb_slot,
+ ahci_pread(ap, AHCI_PREG_CMD), AHCI_PFMT_CMD,
+ ahci_pread(ap, AHCI_PREG_TFD), AHCI_PFMT_TFD_STS,
+ ahci_pread(ap, AHCI_PREG_SERR), AHCI_PFMT_SERR);
+ }
timeout_fn(ccb);
struct ahci_softc *sc = ap->ap_sc;
u_int32_t is, ci_saved, ci_masked;
int slot;
+ int stopped = 0;
struct ahci_ccb *ccb = NULL;
struct ata_port *ccb_at = NULL;
volatile u_int32_t *active;
AHCI_PREG_IS_OFS |
AHCI_PREG_IS_UFS;
- enum { NEED_NOTHING, NEED_RESTART, NEED_HOTPLUG_INSERT,
- NEED_HOTPLUG_REMOVE } need = NEED_NOTHING;
+ enum { NEED_NOTHING, NEED_REINIT, NEED_RESTART,
+ NEED_HOTPLUG_INSERT, NEED_HOTPLUG_REMOVE } need = NEED_NOTHING;
/*
* All basic command completions are always processed.
ap->ap_sactive, ahci_pread(ap, AHCI_PREG_SACT));
#endif
- /* ignore AHCI_PREG_IS_PRCS when link power management is on */
+ /*
+ * Ignore AHCI_PREG_IS_PRCS when link power management is on
+ */
if (ap->link_pwr_mgmt != AHCI_LINK_PWR_MGMT_NONE) {
is &= ~AHCI_PREG_IS_PRCS;
ahci_pwrite(ap, AHCI_PREG_SERR,
AHCI_PREG_SERR_DIAG_N | AHCI_PREG_SERR_DIAG_W);
}
+ /*
+ * Command failed (blockable).
+ *
+ * See AHCI 1.1 spec 6.2.2.1 and 6.2.2.2.
+ *
+ * This stops command processing.
+ */
if (is & AHCI_PREG_IS_TFES) {
- /*
- * Command failed (blockable).
- *
- * See AHCI 1.1 spec 6.2.2.1 and 6.2.2.2.
- *
- * This stops command processing.
- */
u_int32_t tfd, serr;
int err_slot;
kprintf("%s: Issuing CLO\n", PORTNAME(ap));
ahci_port_clo(ap);
}
- ahci_port_start(ap);
+
+ /*
+ * We are now stopped and need a restart. If we have to
+ * process a NCQ error we will temporarily start and then
+ * stop the port again, so this condition holds.
+ */
+ stopped = 1;
need = NEED_RESTART;
/*
* Otherwise process the error for the slot.
*/
if (ap->ap_sactive) {
+ ahci_port_start(ap);
err_slot = ahci_port_read_ncq_error(ap, 0);
+ ahci_port_stop(ap, 0);
} else if (ap->ap_active == 0) {
kprintf("%s: TFES with no commands pending\n",
PORTNAME(ap));
}
/*
- * Spurious IFS errors (blockable).
+ * Spurious IFS errors (blockable) - when AP_F_IGNORE_IFS is set.
*
* Spurious IFS errors can occur while we are doing a reset
- * sequence through a PM. Try to recover if we are being asked
- * to ignore IFS errors during these periods.
+ * sequence through a PM, probably due to an unexpected FIS
+ * being received during the PM target reset sequence. Chipsets
+ * are supposed to mask these events but some do not.
+ *
+ * Try to recover from the condition.
*/
if ((is & AHCI_PREG_IS_IFS) && (ap->ap_flags & AP_F_IGNORE_IFS)) {
u_int32_t serr = ahci_pread(ap, AHCI_PREG_SERR);
if ((ap->ap_flags & AP_F_IFS_IGNORED) == 0) {
- kprintf("%s: Ignoring IFS (XXX) (IS: %b, SERR: %b)\n",
+ kprintf("%s: IFS during PM probe (ignored) "
+ "IS=%b, SERR=%b\n",
PORTNAME(ap),
is, AHCI_PFMT_IS,
serr, AHCI_PFMT_SERR);
ap->ap_flags |= AP_F_IFS_IGNORED;
}
- ap->ap_flags |= AP_F_IFS_OCCURED;
+
+ /*
+ * Try to clear the error condition. The IFS error killed
+ * the port so stop it so we can restart it.
+ */
ahci_pwrite(ap, AHCI_PREG_SERR, -1);
ahci_pwrite(ap, AHCI_PREG_IS, AHCI_PREG_IS_IFS);
is &= ~AHCI_PREG_IS_IFS;
- ahci_port_stop(ap, 0);
- ahci_port_start(ap);
- kprintf("%s: Spurious IFS error\n", PORTNAME(ap));
+ need = NEED_RESTART;
goto failall;
- /* need = NEED_RESTART; */
}
/*
* Port change (hot-plug) (blockable).
*
- * A PCS interrupt will occur on hot-plug once communication is
- * established.
+ * A PRCS interrupt can occur:
+ * (1) On hot-unplug / normal-unplug (phy lost)
+ * (2) Sometimes on hot-plug too.
*
- * A PRCS interrupt will occur on hot-unplug (and possibly also
- * on hot-plug).
+ * A PCS interrupt can occur in a number of situations:
+ * (1) On hot-plug once communication is established
+ * (2) On hot-unplug sometimes.
+ * (3) For chipsets with badly written firmware it can occur
+ * during INIT/RESET sequences due to the device reset.
+ * (4) For chipsets with badly written firmware it can occur
+ * when it thinks an unsolicited COMRESET is received
+ * during a INIT/RESET sequence, even though we actually
+ * did request it.
*
* XXX We can then check the CPS (Cold Presence State) bit, if
* supported, to determine if a device is plugged in or not and do
* the right thing.
*
- * WARNING: A PCS interrupt is cleared by clearing DIAG_X, and
- * can also occur if an unsolicited COMINIT is received.
- * If this occurs command processing is automatically
- * stopped (CR goes inactive) and the port must be stopped
- * and restarted.
+ * PCS interrupts are cleared by clearing DIAG_X. If this occurs
+ * command processing is automatically stopped (CR goes inactive)
+ * and the port must be stopped and restarted.
+ *
+ * WARNING: AMD parts (e.g. 880G chipset, probably others) can
+ * generate PCS on initialization even when device is
+ * already connected up. It is unclear why this happens.
+ * Depending on the state of the device detect this can
+ * cause us to go into harsh reinit or hot-plug insertion
+ * mode.
+ *
+ * WARNING: PCS errors can be repetitive (e.g. unsolicited COMRESET
+ * continues to flow in from the device), we must clear the
+ * interrupt in all cases and enforce a delay to prevent
+ * a livelock and give the port time to settle down.
+ * Only print something if we aren't in INIT/HARD-RESET.
*/
-
if (is & (AHCI_PREG_IS_PCS | AHCI_PREG_IS_PRCS)) {
- kprintf("%s: Transient Errors: %b\n",
- PORTNAME(ap), is, AHCI_PFMT_IS);
- ahci_pwrite(ap, AHCI_PREG_SERR,
- (AHCI_PREG_SERR_DIAG_N | AHCI_PREG_SERR_DIAG_X));
+ /*
+ * Try to clear the error. Because of the repetitiveness
+ * of this interrupt avoid any harsh action if the port is
+ * already in the init or hard-reset probe state.
+ */
+ ahci_pwrite(ap, AHCI_PREG_SERR, -1);
+ /* (AHCI_PREG_SERR_DIAG_N | AHCI_PREG_SERR_DIAG_X) */
ahci_pwrite(ap, AHCI_PREG_IS,
is & (AHCI_PREG_IS_PCS | AHCI_PREG_IS_PRCS));
+
+ if (ap->ap_probe == ATA_PROBE_NEED_INIT ||
+ ap->ap_probe == ATA_PROBE_NEED_HARD_RESET) {
+ is &= ~(AHCI_PREG_IS_PCS | AHCI_PREG_IS_PRCS);
+ need = NEED_NOTHING;
+ ahci_os_sleep(1000);
+ goto failall;
+ }
+ kprintf("%s: Transient Errors: %b (%d)\n",
+ PORTNAME(ap), is, AHCI_PFMT_IS, ap->ap_probe);
is &= ~(AHCI_PREG_IS_PCS | AHCI_PREG_IS_PRCS);
+ ahci_os_sleep(200);
+
+ /*
+ * Stop the port and figure out what to do next.
+ */
ahci_port_stop(ap, 0);
+ stopped = 1;
switch (ahci_pread(ap, AHCI_PREG_SSTS) & AHCI_PREG_SSTS_DET) {
case AHCI_PREG_SSTS_DET_DEV:
+ /*
+ * Device detect
+ */
if (ap->ap_probe == ATA_PROBE_FAILED) {
need = NEED_HOTPLUG_INSERT;
goto fatal;
}
need = NEED_RESTART;
break;
+ case AHCI_PREG_SSTS_DET_DEV_NE:
+ /*
+ * Device not communicating. AMD parts seem to
+ * like to throw this error on initialization
+ * for no reason that I can fathom.
+ */
+ kprintf("%s: Device present but not communicating, "
+ "attempting port restart\n",
+ PORTNAME(ap));
+ need = NEED_REINIT;
+ goto fatal;
default:
if (ap->ap_probe != ATA_PROBE_FAILED) {
need = NEED_HOTPLUG_REMOVE;
is &= ~(AHCI_PREG_IS_TFES | AHCI_PREG_IS_HBFS |
AHCI_PREG_IS_IFS | AHCI_PREG_IS_OFS |
AHCI_PREG_IS_UFS);
- /* XXX try recovery first */
+
+ /*
+ * Fail all commands but then what? For now try to
+ * reinitialize the port.
+ */
+ need = NEED_REINIT;
goto fatal;
}
if (ap->ap_state == AP_S_FATAL_ERROR) {
fatal:
ap->ap_state = AP_S_FATAL_ERROR;
- ahci_port_stop(ap, 0);
failall:
- kprintf("%s: Failing all commands\n", PORTNAME(ap));
+ ahci_port_stop(ap, 0);
+ stopped = 1;
/*
- * Error all the active slots not already errored. If
- * running across a PM try to error out just the slots
- * related to the target.
+ * Error all the active slots not already errored.
*/
ci_masked = ci_saved & *active & ~ap->ap_expired;
+ if (ci_masked) {
+ kprintf("%s: Failing all commands: %08x\n",
+ PORTNAME(ap), ci_masked);
+ }
+
while (ci_masked) {
slot = ffs(ci_masked) - 1;
ccb = &ap->ap_ccbs[slot];
- if (ccb_at == ccb->ccb_xa.at ||
- ap->ap_state == AP_S_FATAL_ERROR) {
- ccb->ccb_xa.state = ATA_S_TIMEOUT;
- ap->ap_expired |= 1 << slot;
- ci_saved &= ~(1 << slot);
- }
+ ccb->ccb_xa.state = ATA_S_TIMEOUT;
+ ap->ap_expired |= 1 << slot;
+ ci_saved &= ~(1 << slot);
ci_masked &= ~(1 << slot);
}
}
/*
+ * If we are stopped the AHCI chipset is supposed to have cleared
+ * CI and SACT. Did it? If it didn't we try very hard to clear
+ * the fields otherwise we may end up completing CCBs which are
+ * actually still active.
+ *
+ * IFS errors on (at least) AMD chipsets create this confusion.
+ */
+ if (stopped) {
+ u_int32_t mask;
+ if ((mask = ahci_pactive(ap)) != 0) {
+ kprintf("%s: chipset failed to clear "
+ "active cmds %08x\n",
+ PORTNAME(ap), mask);
+ ahci_port_start(ap);
+ ahci_port_stop(ap, 0);
+ if ((mask = ahci_pactive(ap)) != 0) {
+ kprintf("%s: unable to prod the chip into "
+ "clearing active cmds %08x\n",
+ PORTNAME(ap), mask);
+ /* what do we do now? */
+ }
+ }
+ }
+
+ /*
* CCB completion (non blocking).
*
* CCB completion is detected by noticing its slot's bit in CI has
ccb->ccb_done(ccb);
}
}
- ahci_issue_pending_commands(ap, NULL);
/*
* Cleanup. Will not be set if non-blocking.
kprintf("%s: Restart %08x\n", PORTNAME(ap), ci_saved);
ahci_issue_saved_commands(ap, ci_saved);
}
+
+ /*
+ * Potentially issue new commands if not in a failed
+ * state.
+ */
+ if (ap->ap_state != AP_S_FATAL_ERROR) {
+ ahci_port_start(ap);
+ ahci_issue_pending_commands(ap, NULL);
+ }
+ break;
+ case NEED_REINIT:
+ /*
+ * Something horrible happened to the port and we
+ * need to reinitialize it.
+ */
+ kprintf("%s: REINIT - Attempting to reinitialize the port "
+ "after it had a horrible accident\n",
+ PORTNAME(ap));
+ ap->ap_flags |= AP_F_IN_RESET;
+ ap->ap_flags |= AP_F_HARSH_REINIT;
+ ap->ap_probe = ATA_PROBE_NEED_INIT;
+ ahci_cam_changed(ap, NULL, -1);
break;
case NEED_HOTPLUG_INSERT:
/*
KKASSERT(ccb->ccb_xa.state == ATA_S_PUT);
TAILQ_REMOVE(&ap->ap_ccb_free, ccb, ccb_entry);
ccb->ccb_xa.state = ATA_S_SETUP;
+ ccb->ccb_xa.flags = 0;
ccb->ccb_xa.at = NULL;
}
lockmgr(&ap->ap_ccb_lock, LK_RELEASE);
struct ata_xfer *xa = &ccb->ccb_xa;
struct ahci_port *ap = ccb->ccb_port;
struct ata_port *at;
- int ci_saved;
+ u_int32_t ci_saved;
+ u_int32_t mask;
int slot;
at = ccb->ccb_xa.at;
ahci_port_clo(ap);
}
ahci_port_start(ap);
+
+ /*
+ * We absolutely must make sure the chipset cleared activity on
+ * all slots. This sometimes might not happen due to races with
+ * a chipset interrupt which stops the port before we can manage
+ * to. For some reason some chipsets don't clear the active
+ * commands when we turn off CMD_ST after the chip has stopped
+ * operations itself.
+ */
+ if (ahci_pactive(ap) != 0) {
+ ahci_port_stop(ap, 0);
+ ahci_port_start(ap);
+ if ((mask = ahci_pactive(ap)) != 0) {
+ kprintf("%s: quick-timeout: chipset failed "
+ "to clear active cmds %08x\n",
+ PORTNAME(ap), mask);
+ }
+ }
ahci_issue_saved_commands(ap, ci_saved & ~ap->ap_expired);
ahci_issue_pending_commands(ap, NULL);
ahci_port_intr(ap, 0);
ahci_quick_timeout(struct ahci_ccb *ccb)
{
struct ahci_port *ap = ccb->ccb_port;
+ u_int32_t mask;
switch (ccb->ccb_xa.state) {
case ATA_S_PENDING:
ccb->ccb_xa.state = ATA_S_TIMEOUT;
break;
case ATA_S_ONCHIP:
+ /*
+ * We have to clear the command on-chip.
+ */
KKASSERT(ap->ap_active == (1 << ccb->ccb_slot) &&
ap->ap_sactive == 0);
ahci_port_stop(ap, 0);
ahci_port_start(ap);
+ if (ahci_pactive(ap) != 0) {
+ ahci_port_stop(ap, 0);
+ ahci_port_start(ap);
+ if ((mask = ahci_pactive(ap)) != 0) {
+ kprintf("%s: quick-timeout: chipset failed "
+ "to clear active cmds %08x\n",
+ PORTNAME(ap), mask);
+ }
+ }
ccb->ccb_xa.state = ATA_S_TIMEOUT;
ap->ap_active &= ~(1 << ccb->ccb_slot);
}
int
-ahci_set_feature(struct ahci_port *ap, struct ata_port *atx, int feature, int enable)
+ahci_set_feature(struct ahci_port *ap, struct ata_port *atx,
+ int feature, int enable)
{
struct ata_port *at;
struct ata_xfer *xa;
* the signature.
*/
ccb = ahci_get_err_ccb(ap);
- ccb->ccb_xa.flags = ATA_F_POLL;
+ ccb->ccb_xa.flags = ATA_F_POLL | ATA_F_SILENT;
ccb->ccb_xa.complete = ahci_pm_dummy_done;
ccb->ccb_xa.at = ap->ap_ata[15];
cmd_slot = ccb->ccb_cmd_hdr;
* The probing has to be done whether or not a device is probed on
* target 0, because when a PM is attached target 0 represents
* slot #0 behind the PM.
+ *
+ * Port multipliers are expected to answer more quickly than normal
+ * devices, use a shorter timeout than normal.
+ *
+ * If there is no PM here this command can still succeed due to
+ * the _C_
*/
- if (ahci_poll(ccb, 1000, ahci_quick_timeout) != ATA_S_COMPLETE) {
- kprintf("%s: PMPROBE First FIS failed,\n",
- PORTNAME(ap));
- kprintf("%s: PMPROBE No Port Multiplier was found.\n",
+ if (ahci_poll(ccb, 500, ahci_quick_timeout) != ATA_S_COMPLETE) {
+ kprintf("%s: PMPROBE(1) No Port Multiplier was found.\n",
PORTNAME(ap));
if (--count) {
ahci_put_err_ccb(ccb);
error = EBUSY;
goto err;
}
+
if (ahci_pwait_clr(ap, AHCI_PREG_TFD,
AHCI_PREG_TFD_STS_BSY | AHCI_PREG_TFD_STS_DRQ)) {
kprintf("%s: PMPROBE Busy after first FIS\n", PORTNAME(ap));
/*
* The device may have muffed up the PHY when it reset.
*/
- ahci_os_sleep(100);
ahci_flush_tfd(ap);
ahci_pwrite(ap, AHCI_PREG_SERR, -1);
/* ahci_pm_phy_status(ap, 15, &cmd); */
* It is unclear which other fields in the FIS are used. Just zero
* everything.
*/
- ccb->ccb_xa.flags = ATA_F_POLL;
+ ccb->ccb_xa.flags = ATA_F_POLL | ATA_F_SILENT;
bzero(fis, sizeof(ccb->ccb_cmd_table->cfis));
fis[0] = ATA_FIS_TYPE_H2D;
* slot #0 behind the PM.
*/
if (ahci_poll(ccb, 5000, ahci_quick_timeout) != ATA_S_COMPLETE) {
- kprintf("%s: PMPROBE Second FIS failed,\n",
- PORTNAME(ap));
- kprintf("%s: PMPROBE No Port Multiplier was found.\n",
+ kprintf("%s: PMPROBE(2) No Port Multiplier was found.\n",
PORTNAME(ap));
if (--count) {
ahci_put_err_ccb(ccb);
PORTNAME(ap), target, data);
/*
* Clear SERR on the target so we get a new NOTIFY event if a hot-plug
- * or hot-unplug occurs.
+ * or hot-unplug occurs. Clear any spurious IFS that may have
+ * occured during the probe.
+ *
+ * WARNING! 100ms seems to work in most cases but
*/
ahci_os_sleep(100);
+ ahci_pm_write(ap, target, SATA_PMREG_SERR, -1);
+ ahci_pwrite(ap, AHCI_PREG_SERR, -1);
+ ahci_pwrite(ap, AHCI_PREG_IS, AHCI_PREG_IS_IFS);
error = 0;
err:
* NOTE: This cannot be safely done between the first and second
* softreset FISs. It's now or never.
*/
-#if 1
if (ahci_pm_phy_status(ap, target, &data)) {
kprintf("%s: (B)Cannot clear phy status\n",
ATANAME(ap ,at));
}
ahci_pm_write(ap, target, SATA_PMREG_SERR, -1);
-#endif
/*
* Prep first D2H command with SRST feature & clear busy/reset flags
ccb->ccb_xa.state = ATA_S_PENDING;
/*
- * XXX hack to ignore IFS errors which can occur during the target
- * device's reset.
+ * This soft reset of the AP target can cause a stream of IFS
+ * errors to occur. Setting AP_F_IGNORE_IFS prevents the port
+ * from being hard reset (because its the target behind the
+ * port that isn't happy).
*
- * If an IFS error occurs the target is probably powering up,
- * so we try for a longer period of time.
+ * The act of sending the soft reset can cause the target to
+ * blow the port up and generate IFS errors.
*/
ap->ap_flags |= AP_F_IGNORE_IFS;
- ap->ap_flags &= ~(AP_F_IFS_IGNORED | AP_F_IFS_OCCURED);
+ ap->ap_flags &= ~AP_F_IFS_IGNORED;
if (ahci_poll(ccb, 1000, ahci_ata_cmd_timeout) != ATA_S_COMPLETE) {
- kprintf("%s: (PM) First FIS failed\n", ATANAME(ap, at));
- if (ap->ap_flags & AP_F_IFS_OCCURED) {
- if (tried_longer == 0)
- count += 4;
- ++tried_longer;
- }
+ kprintf("%s: Soft-reset through PM failed, %s\n",
+ ATANAME(ap, at),
+ (count > 1 ? "retrying" : "giving up"));
ahci_put_err_ccb(ccb);
- if (--count)
+ if (--count) {
+ if (ap->ap_flags & AP_F_IFS_IGNORED)
+ ahci_os_sleep(5000);
+ else
+ ahci_os_sleep(1000);
+ ahci_pwrite(ap, AHCI_PREG_SERR, -1);
+ ahci_pwrite(ap, AHCI_PREG_IS, AHCI_PREG_IS_IFS);
goto retry;
+ }
goto err;
}
ccb->ccb_xa.state = ATA_S_PENDING;
ccb->ccb_xa.flags = ATA_F_POLL | ATA_F_EXCLUSIVE | ATA_F_AUTOSENSE;
+ ap->ap_flags &= ~AP_F_IFS_IGNORED;
+
if (ahci_poll(ccb, 1000, ahci_ata_cmd_timeout) != ATA_S_COMPLETE) {
- kprintf("%s: (PM) Second FIS failed\n", ATANAME(ap, at));
- ahci_put_err_ccb(ccb);
-#if 1
- if (--count)
+ kprintf("%s: Soft-reset(2) through PM failed, %s\n",
+ ATANAME(ap, at),
+ (count > 1 ? "retrying" : "giving up"));
+ if (--count) {
+ ahci_os_sleep(1000);
+ ahci_put_err_ccb(ccb);
+ ahci_pwrite(ap, AHCI_PREG_SERR, -1);
+ ahci_pwrite(ap, AHCI_PREG_IS, AHCI_PREG_IS_IFS);
goto retry;
-#endif
+ }
goto err;
}
ahci_put_err_ccb(ccb);
ahci_os_sleep(100);
+ ahci_pwrite(ap, AHCI_PREG_SERR, -1);
+ ahci_pwrite(ap, AHCI_PREG_IS, AHCI_PREG_IS_IFS);
+
ahci_pm_write(ap, target, SATA_PMREG_SERR, -1);
if (ahci_pm_phy_status(ap, target, &data)) {
kprintf("%s: (C)Cannot clear phy status\n",
* Who knows what kind of mess occured. We have exclusive access
* to the port so try to clean up potential problems.
*/
- ahci_os_sleep(100);
err:
+ ahci_os_sleep(100);
+
/*
* Clear error status so we can detect removal.
*/
if (ahci_pm_write(ap, target, SATA_PMREG_SERR, -1)) {
kprintf("%s: ahci_pm_softreset unable to clear SERR\n",
ATANAME(ap, at));
- ap->ap_flags &= ~AP_F_IGNORE_IFS;
}
-/* ahci_pwrite(ap, AHCI_PREG_SERR, -1);*/
+ ahci_pwrite(ap, AHCI_PREG_SERR, -1);
+ ahci_pwrite(ap, AHCI_PREG_IS, AHCI_PREG_IS_IFS);
+ ap->ap_flags &= ~(AP_F_IGNORE_IFS | AP_F_IFS_IGNORED);
at->at_probe = error ? ATA_PROBE_FAILED : ATA_PROBE_NEED_IDENT;
return (error);