diff --git a/sys/dev/disk/ahci/ahci.c b/sys/dev/disk/ahci/ahci.c index 68cdabc..c34b82a 100644 --- a/sys/dev/disk/ahci/ahci.c +++ b/sys/dev/disk/ahci/ahci.c @@ -2378,8 +2378,10 @@ finish_error: * Spurious IFS errors (blockable). * * 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 due to attempts to write PM registers. + * + * Try to recover if we are being asked to ignore IFS errors during + * these periods. */ if ((is & AHCI_PREG_IS_IFS) && (ap->ap_flags & AP_F_IGNORE_IFS)) { u_int32_t serr = ahci_pread(ap, AHCI_PREG_SERR); @@ -2410,6 +2412,8 @@ finish_error: * A PRCS interrupt will occur on hot-unplug (and possibly also * on hot-plug). * + * These bits do not stop command processing. + * * 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. @@ -2419,36 +2423,47 @@ finish_error: * If this occurs command processing is automatically * stopped (CR goes inactive) and the port must be stopped * and restarted. + * + * PRCS is cleared by clearing DIAG_N. */ 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)); ahci_pwrite(ap, AHCI_PREG_IS, is & (AHCI_PREG_IS_PCS | AHCI_PREG_IS_PRCS)); is &= ~(AHCI_PREG_IS_PCS | AHCI_PREG_IS_PRCS); - ahci_port_stop(ap, 0); - switch (ahci_pread(ap, AHCI_PREG_SSTS) & AHCI_PREG_SSTS_DET) { - case AHCI_PREG_SSTS_DET_DEV: - if (ap->ap_probe == ATA_PROBE_FAILED) { - need = NEED_HOTPLUG_INSERT; - goto fatal; - } - need = NEED_RESTART; - break; - default: - if (ap->ap_type != ATA_PROBE_FAILED) { - need = NEED_HOTPLUG_REMOVE; - goto fatal; + + if (ap->ap_flags & AP_F_IN_RESET) { + kprintf("%s: Transient Errors during reset: " + "%b (ignored)\n", + PORTNAME(ap), is, AHCI_PFMT_IS); + } else { + kprintf("%s: Transient Errors: %b\n", + PORTNAME(ap), is, AHCI_PFMT_IS); + ahci_port_stop(ap, 0); + switch (ahci_pread(ap, AHCI_PREG_SSTS) & AHCI_PREG_SSTS_DET) { + case AHCI_PREG_SSTS_DET_DEV: + if (ap->ap_probe == ATA_PROBE_FAILED) { + need = NEED_HOTPLUG_INSERT; + goto fatal; + } + need = NEED_RESTART; + break; + default: + if (ap->ap_type != ATA_PROBE_FAILED) { + need = NEED_HOTPLUG_REMOVE; + goto fatal; + } + need = NEED_RESTART; + break; } - need = NEED_RESTART; - break; } } /* * Check for remaining errors - they are fatal. (blockable) + * + * These bits stop command processing. */ if (is & (AHCI_PREG_IS_TFES | AHCI_PREG_IS_HBFS | AHCI_PREG_IS_IFS | AHCI_PREG_IS_OFS | AHCI_PREG_IS_UFS)) { diff --git a/sys/dev/disk/ahci/ahci_pm.c b/sys/dev/disk/ahci/ahci_pm.c index 47be498..303a4b2 100644 --- a/sys/dev/disk/ahci/ahci_pm.c +++ b/sys/dev/disk/ahci/ahci_pm.c @@ -76,6 +76,7 @@ retry: * NOTE: On retry the port might be running, stopped, or failed. */ ahci_port_stop(ap, 0); + ap->ap_flags |= AP_F_IN_RESET; ap->ap_state = AP_S_NORMAL; cmd = ahci_pread(ap, AHCI_PREG_CMD) & ~AHCI_PREG_CMD_ICC; if ((cmd & AHCI_PREG_CMD_PMA) == 0) { @@ -225,6 +226,7 @@ retry: err: if (ccb != NULL) ahci_put_err_ccb(ccb); + ap->ap_flags &= ~AP_F_IN_RESET; if (error == 0 && ahci_pm_identify(ap)) { kprintf("%s: PM - cannot identify port multiplier\n", @@ -568,8 +570,9 @@ retry: ccb->ccb_xa.state = ATA_S_PENDING; /* - * XXX hack to ignore IFS errors which can occur during the target - * device's reset. + * XXX hack to ignore IFS errors for operations through port + * multipliers. Target device's resets through the PM + * appear to be able to cause IFS errors ?? * * If an IFS error occurs the target is probably powering up, * so we try for a longer period of time. @@ -682,11 +685,20 @@ err: 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; + if (error == 0) + error = EIO; } -/* ahci_pwrite(ap, AHCI_PREG_SERR, -1);*/ - at->at_probe = error ? ATA_PROBE_FAILED : ATA_PROBE_NEED_IDENT; + /* + * Final disposition. Cleanup on error. If no error occurred we + * continue to ignore IFS while attached to the PM. + */ + if (error) { + ap->ap_flags &= ~AP_F_IGNORE_IFS; + at->at_probe = ATA_PROBE_FAILED; + } else { + at->at_probe = ATA_PROBE_NEED_IDENT; + } return (error); }