Simulate various mode pages (serial number access and so forth).
+NOTE RACE: When stopping a port explicitly which has not self stopped,
+i.e. CR is still on, we can race command completion and not have a good
+idea what bits to reload into CI etc to restart the commands that
+were running. This should only be done if we intend to reset the port.
+
+NOTE RACE: A transient IFS interrupt (fatal phy/protocol error) can occur
+when soft-resetting through a port multiplier, between the first and second
+FISes. We need to be able to lock access to the port.
+
------ Misc probe info --------
<AHCI-PCI-SATA> port
SB800
S5000 (w/ ESB2)
(add more)
+---------------------------
+
+Set device bits FIS:
+
+ EEEEEEEE HHHHLLLL NIRxxxxx FFFFFFFF
+ rrrrrrrr rrrrrrrr rrrrrrrr rrrrrrrr (reserved)
+
+ F8 FIS TYPE (0xA1)
+ N Notification bit
+ I Interrupt bit
+ R Reset bit
+ H4 Status hi (bit 3 is 'r' bit?)
+ L4 Status Lo (bit 3 is 'r' bit?)
+ E8 Error code
+
+ ATAPI/DISK notification: Word78 of IDENTIFY,
+ Use SET FEATURES to set.
+
+IDENTIFY DEVICE Changed in SATA 2:
+
+ Word 75 4:0 Max Queue depth
+
+ Word 76 9 Supports IPM requests
+ 8 supports NCQ
+ 7-4 reservedr
+ 3 reserved
+ 2 supports GEN2
+ 1 supports GEN1
+ 0 reserved (set to 0)
+
+ Word 78 4 supports in-order data delivery
+ 3 supports IPMfrom device
+ 2 supports DMA setup AA opt
+ 1 supports non-zero buffer offssets in DMA setup
+ 0 reserved (set to 0
+
+ Word 79 (sata features enabled)
+
+
+ Device configuration overlay
+ Word 0-7 Defined by ATA
+ Word 8 3 suports async notification
+ 2 supports IPM
+ 1 supports nz buffer offsets in DMA setup FIS
+ 0 supports NCQ
+ Word 9 reserved for SATA
+ 10-255 as defined by ATA
+
+SET FEATURES DEF
+
+ Feature 10h Enable use of SATA feature
+ feature 90h Disable use of SATA feature
+
+ sector count register contains specific feature to enable
+
+ 01 No zero buffer offset in DMA setup fis
+ 02 DMA setup fis AA opt
+ 03 device initated power state transitions
+ 04 guaranteed in-order data delivery
+ 05 Asynchronous notification
+
+
+SCR REGISTERS
+
+ 0 SStatus
+ 1 SError
+ 2 SControl
+ 3 SActive
+ 4 SNotification <----
+ 5-15 reserved
at = &ap->ap_ata[i];
at->at_ahci_port = ap;
at->at_target = i;
- at->at_probe = ATA_PROBE_NEED_HARD_RESET;
+ at->at_probe = ATA_PROBE_NEED_INIT;
ksnprintf(at->at_name, sizeof(at->at_name),
"%s.%d", ap->ap_name, i);
}
ap->ap_sc = sc;
ap->ap_num = port;
- ap->ap_probe = ATA_PROBE_NEED_HARD_RESET;
+ ap->ap_probe = ATA_PROBE_NEED_INIT;
TAILQ_INIT(&ap->ap_ccb_free);
TAILQ_INIT(&ap->ap_ccb_pending);
lockinit(&ap->ap_ccb_lock, "ahcipo", 0, 0);
}
/*
- * Flush interupts on the port. XXX
+ * Flush interrupts on the port. XXX
*
* Enable interrupts on the port whether a device is sitting on
* it or not, to handle hot-plug events.
}
/*
+ * Run the port / target state machine from a main context.
+ *
+ * The state machine for the port is always run.
+ *
+ * If atx is non-NULL run the state machine for a particular target.
+ * If atx is NULL run the state machine for all targets.
+ */
+void
+ahci_port_state_machine(struct ahci_port *ap)
+{
+ struct ata_port *at;
+ u_int32_t data;
+ int target;
+ int didsleep;
+
+ if (ap->ap_type == ATA_PORT_T_NONE) {
+ if (ap->ap_probe == ATA_PROBE_NEED_INIT) {
+ for (target = 0; target < AHCI_MAX_PMPORTS; ++target) {
+ at = &ap->ap_ata[target];
+ at->at_probe = ATA_PROBE_NEED_INIT;
+ }
+ ahci_port_init(ap, NULL);
+ }
+ if (ap->ap_probe == ATA_PROBE_NEED_HARD_RESET)
+ ahci_port_reset(ap, NULL, 1);
+ if (ap->ap_probe == ATA_PROBE_NEED_SOFT_RESET)
+ ahci_port_reset(ap, NULL, 0);
+ if (ap->ap_probe == ATA_PROBE_NEED_IDENT)
+ ahci_cam_probe(ap, NULL);
+ }
+ if (ap->ap_type != ATA_PORT_T_PM) {
+ if (ap->ap_probe == ATA_PROBE_FAILED) {
+ ahci_cam_changed(ap, NULL, 0);
+ } else if (ap->ap_probe == ATA_PROBE_GOOD) {
+ ahci_cam_changed(ap, NULL, 1);
+ }
+ return;
+ }
+
+ for (;;) {
+ if (ahci_pm_read(ap, 15, AHCI_PMREG_EINFO, &data)) {
+ kprintf("%s: PM unable to read hot-plug bitmap\n",
+ PORTNAME(ap));
+ break;
+ }
+ data &= (1 << ap->ap_pmcount) - 1;
+
+ /*
+ * Stop if no ports on the target have indicated a state
+ * change.
+ */
+ if (data == 0)
+ break;
+
+ /*
+ * New devices showing up in the bitmap require some spin-up
+ * time before we start probing them. Reset didsleep. The
+ * first new device we detect will sleep before probing.
+ */
+ didsleep = 0;
+
+ for (target = 0; target < ap->ap_pmcount; ++target) {
+ at = &ap->ap_ata[target];
+
+ /*
+ * Check the target state for targets behind the PM
+ * which have changed state. This will adjust
+ * at_probe and set ATA_PORT_F_RESCAN
+ *
+ * We want to wait at least 4 seconds before probing
+ * a newly inserted device. If the check status
+ * indicates a device is present and in need of a
+ * hard reset, we make sure we have slept before
+ * continuing.
+ */
+ if (data & (1 << target)) {
+ ahci_pm_check_good(ap, target);
+ if (at->at_probe == ATA_PROBE_NEED_HARD_RESET) {
+ if (didsleep == 0) {
+ didsleep = 1;
+ ahci_os_sleep(4000);
+ }
+ }
+ }
+
+ /*
+ * Run through the state machine as necessary.
+ */
+ if (at->at_type == ATA_PORT_T_NONE &&
+ at->at_probe != ATA_PROBE_FAILED) {
+ if (at->at_probe == ATA_PROBE_NEED_INIT)
+ ahci_port_init(ap, at);
+ if (at->at_probe == ATA_PROBE_NEED_HARD_RESET)
+ ahci_port_reset(ap, at, 1);
+ if (at->at_probe == ATA_PROBE_NEED_SOFT_RESET)
+ ahci_port_reset(ap, at, 0);
+ if (at->at_probe == ATA_PROBE_NEED_IDENT)
+ ahci_cam_probe(ap, at);
+ }
+
+ if (data & (1 << target)) {
+ kprintf("%s: HOTPLUG event, ",
+ ATANAME(ap, at));
+ if (at->at_probe == ATA_PROBE_GOOD)
+ kprintf("device inserted\n");
+ else
+ kprintf("device removed\n");
+ }
+
+ /*
+ * Initial conditions set automatic add/rem
+ */
+ if (at->at_probe <= ATA_PROBE_NEED_HARD_RESET)
+ at->at_features |= ATA_PORT_F_RESCAN;
+
+ /*
+ * add or remove from CAM
+ */
+ if (at->at_features & ATA_PORT_F_RESCAN) {
+ at->at_features &= ~ATA_PORT_F_RESCAN;
+ if (at->at_probe == ATA_PROBE_FAILED) {
+ ahci_cam_changed(ap, at, 0);
+ } else if (at->at_probe == ATA_PROBE_GOOD) {
+ ahci_cam_changed(ap, at, 1);
+ }
+ }
+ }
+ }
+}
+
+
+/*
* De-initialize and detach a port.
*/
void
struct ahci_ccb *ccb = NULL;
struct ahci_cmd_hdr *cmd_slot;
u_int8_t *fis;
- int rc, count;
+ int error;
u_int32_t cmd;
- rc = EIO;
- count = 10; /* device reset delay x 100ms */
+ error = EIO;
kprintf("%s: START SOFTRESET %b\n", PORTNAME(ap),
ahci_pread(ap, AHCI_PREG_CMD), AHCI_PFMT_CMD);
PORTNAME(ap),
(ahci_read(ap->ap_sc, AHCI_REG_CAP) & AHCI_REG_CAP_SCLO)
? "failed" : "unsupported");
- rc = EBUSY;
+ error = EBUSY;
goto err;
}
ccb->ccb_xa.state = ATA_S_PENDING;
ccb->ccb_xa.flags = 0;
- if (ahci_poll(ccb, hz, NULL) != 0 ||
+ if (ahci_poll(ccb, 1000, NULL) != 0 ||
ccb->ccb_xa.state != ATA_S_COMPLETE) {
kprintf("%s: First FIS failed\n", PORTNAME(ap));
goto err;
/*
* The device may muff the PHY up.
*/
- DELAY(10000); /* XXX 3000 */
+ ahci_os_sleep(10); /* 3ms min, use 10 */
/*
* Prep second D2H command to read status and complete reset sequence
ccb->ccb_xa.state = ATA_S_PENDING;
ccb->ccb_xa.flags = 0;
- if (ahci_poll(ccb, hz, NULL) != 0 ||
+ if (ahci_poll(ccb, 1000, NULL) != 0 ||
ccb->ccb_xa.state != ATA_S_COMPLETE) {
kprintf("%s: Second FIS failed\n", PORTNAME(ap));
goto err;
kprintf("%s: device didn't come ready after reset, TFD: 0x%b\n",
PORTNAME(ap),
ahci_pread(ap, AHCI_PREG_TFD), AHCI_PFMT_TFD_STS);
- rc = EBUSY;
+ error = EBUSY;
goto err;
}
- DELAY(10000);
+ ahci_os_sleep(10);
/*
* If the softreset is trying to clear a BSY condition after a
if (ahci_port_signature_detect(ap, NULL) != ap->ap_type) {
kprintf("%s: device signature unexpectedly "
"changed\n", PORTNAME(ap));
- rc = EBUSY; /* XXX */
+ error = EBUSY; /* XXX */
}
}
- rc = 0;
+ error = 0;
- DELAY(3000);
+ ahci_os_sleep(3);
err:
if (ccb != NULL) {
/*
* Abort our command, if it failed, by stopping command DMA.
*/
- if (rc && (ap->ap_active & (1 << ccb->ccb_slot))) {
+ if (error && (ap->ap_active & (1 << ccb->ccb_slot))) {
kprintf("%s: stopping the port, softreset slot "
"%d was still active.\n",
PORTNAME(ap),
* Don't kill the port if the softreset is on a port multiplier
* target, that would kill all the targets!
*/
- if (rc) {
+ if (error) {
ahci_port_hardstop(ap);
+ /* ap_probe set to failed */
} else if (cmd & AHCI_PREG_CMD_ST) {
+ ap->ap_probe = ATA_PROBE_NEED_IDENT;
kprintf("%s: STARTING PORT\n", PORTNAME(ap));
ahci_port_start(ap);
} else {
+ ap->ap_probe = ATA_PROBE_NEED_IDENT;
kprintf("%s: STOPPING PORT\n", PORTNAME(ap));
ahci_port_stop(ap, !(cmd & AHCI_PREG_CMD_FRE));
}
- if (rc)
- ap->ap_probe = ATA_PROBE_FAILED;
- else
- ap->ap_probe = ATA_PROBE_NEED_IDENT;
-
+ ap->ap_flags &= ~AP_F_IN_RESET;
crit_exit();
kprintf("%s: END SOFTRESET\n", PORTNAME(ap));
- ap->ap_flags &= ~AP_F_IN_RESET;
- return (rc);
+ return (error);
}
/*
ahci_port_hardreset(struct ahci_port *ap, int hard)
{
u_int32_t cmd, r;
- int rc;
+ int error;
int loop;
int type;
*/
ahci_port_stop(ap, 0);
ap->ap_state = AP_S_NORMAL;
- ap->ap_probe = ATA_PROBE_FAILED;
- rc = 0;
+ error = 0;
/*
* The port may have been quiescent with its SUD bit cleared, so
if (hard == 2)
r |= AHCI_PREG_SCTL_DET_DISABLE;
ahci_pwrite(ap, AHCI_PREG_SCTL, r);
- DELAY(10000);
+ ahci_os_sleep(10);
/*
* Start transmitting COMRESET. COMRESET must be sent for at
r |= AHCI_PREG_SCTL_SPD_ANY;
}
ahci_pwrite(ap, AHCI_PREG_SCTL, r);
- DELAY(1000);
+ ahci_os_sleep(1);
/*
* Only SERR_DIAG_X needs to be cleared for TFD updates, but
r = ahci_pread(ap, AHCI_PREG_SSTS);
if (r & AHCI_PREG_SSTS_DET)
break;
- DELAY(10000);
+ ahci_os_sleep(10);
}
if (loop == 0) {
ahci_pwrite(ap, AHCI_PREG_IS, AHCI_PREG_IS_PRCS);
kprintf("%s: Port appears to be unplugged\n",
PORTNAME(ap));
- rc = ENODEV;
+ error = ENODEV;
}
/*
* There is something on the port. Give the device 3 seconds
* to fully negotiate.
*/
- if (rc == 0 &&
+ if (error == 0 &&
ahci_pwait_eq(ap, 3000, AHCI_PREG_SSTS,
AHCI_PREG_SSTS_DET, AHCI_PREG_SSTS_DET_DEV)) {
kprintf("%s: Device may be powered down\n",
PORTNAME(ap));
- rc = ENODEV;
+ error = ENODEV;
}
/*
* This can take more then a second, give it 3 seconds. If we
* succeed give the device another 3ms after that.
*
- * NOTE: Port Multipliers can do two things here. First they can
+ * NOTE: Port multipliers can do two things here. First they can
* return device-ready if a device is on target 0 and also
* return the signature for that device. If there is no
* device on target 0 then BSY/DRQ is never cleared and
* it never comes ready.
*/
- if (rc == 0 &&
+ if (error == 0 &&
ahci_pwait_clr_to(ap, 3000, AHCI_PREG_TFD,
AHCI_PREG_TFD_STS_BSY | AHCI_PREG_TFD_STS_DRQ)) {
/*
* The device is bricked or its a port multiplier and will
* not unbusy until we do the pmprobe CLO softreset sequence.
*/
- rc = ahci_port_pmprobe(ap);
- if (rc) {
+ error = ahci_port_pmprobe(ap);
+ if (error) {
kprintf("%s: Device will not come ready 0x%b\n",
PORTNAME(ap),
ahci_pread(ap, AHCI_PREG_TFD),
AHCI_PFMT_TFD_STS);
} else {
ap->ap_type = ATA_PORT_T_PM;
- ap->ap_probe = ATA_PROBE_NEED_SOFT_RESET;
- kprintf("%s: Port Multiplier detected\n",
- PORTNAME(ap));
}
- } else if (rc == 0) {
+ } else if (error == 0) {
/*
* We generally will not get a port multiplier signature in
* this case even if this is a port multiplier, because of
* success.
*/
type = ahci_port_signature_detect(ap, NULL);
- rc = ahci_port_pmprobe(ap);
- if (rc) {
+ error = ahci_port_pmprobe(ap);
+ if (error) {
ap->ap_type = type;
ap->ap_probe = ATA_PROBE_NEED_SOFT_RESET;
- rc = 0;
+ error = 0;
} else {
ap->ap_type = ATA_PORT_T_PM;
- ap->ap_probe = ATA_PROBE_NEED_SOFT_RESET;
- kprintf("%s: Port Multiplier detected\n",
+ kprintf("%s: Port multiplier detected\n",
PORTNAME(ap));
}
}
* hard-stop the port if we failed. This will set ap_probe
* to FAILED.
*/
- if (rc)
- ahci_port_hardstop(ap);
ap->ap_flags &= ~AP_F_IN_RESET;
- return (rc);
+ if (error) {
+ ahci_port_hardstop(ap);
+ /* ap_probe set to failed */
+ } else {
+ ap->ap_probe = (ap->ap_type == ATA_PORT_T_PM) ?
+ ATA_PROBE_GOOD : ATA_PROBE_NEED_SOFT_RESET;
+ }
+ return (error);
}
/*
ccb->ccb_xa.state = ATA_S_PENDING;
ccb->ccb_xa.flags = 0;
- if (ahci_poll(ccb, hz, NULL) != 0 ||
+ if (ahci_poll(ccb, 1000, NULL) != 0 ||
ccb->ccb_xa.state != ATA_S_COMPLETE) {
kprintf("%s: PMPROBE First FIS failed\n", PORTNAME(ap));
if (--count) {
/*
* The device may have muffed up the PHY when it reset.
*/
- DELAY(10000);
+ ahci_os_sleep(10);
ahci_flush_tfd(ap);
ahci_pwrite(ap, AHCI_PREG_SERR, -1);
/* ahci_pm_phy_status(ap, 15, &cmd); */
ccb->ccb_xa.state = ATA_S_PENDING;
ccb->ccb_xa.flags = 0;
- if (ahci_poll(ccb, hz, NULL) != 0 ||
+ if (ahci_poll(ccb, 1000, NULL) != 0 ||
ccb->ccb_xa.state != ATA_S_COMPLETE) {
kprintf("%s: PMPROBE Second FIS failed\n", PORTNAME(ap));
if (--count) {
ahci_put_err_ccb(ccb);
}
+ if (rc == 0 && ahci_pm_identify(ap)) {
+ kprintf("%s: PM - cannot identify port multiplier\n",
+ PORTNAME(ap));
+ rc = EBUSY;
+ }
+#if 0
+ if (rc == 0 && ahci_pm_set_feature(ap, ATA_SATAFT_ASYNCNOTIFY, 1)) {
+ kprintf("%s: PM - Warning, cannot enable async notify\n",
+ PORTNAME(ap));
+ /* ignore error */
+ }
+ if (rc == 0) {
+ u_int32_t data;
+ if (ahci_pm_read(ap, 2, 4, &data))
+ kprintf("Cannot read snotify\n");
+ else
+ kprintf("Read snotify %08x\n", data);
+ }
+#endif
+
/*
* If we failed turn off PMA, otherwise identify the port multiplier.
* CAM will iterate the devices.
cmd = ahci_pread(ap, AHCI_PREG_CMD) & ~AHCI_PREG_CMD_ICC;
cmd &= ~AHCI_PREG_CMD_PMA;
ahci_pwrite(ap, AHCI_PREG_CMD, cmd);
- } else {
- ahci_pm_identify(ap);
-#if 0
- ahci_pm_hardreset(ap, 0);
- ahci_pm_hardreset(ap, 1);
- ahci_pm_hardreset(ap, 2);
- ahci_pm_hardreset(ap, 3);
- ahci_pm_hardreset(ap, 4);
-#endif
}
ahci_port_stop(ap, 0);
for (i = 0; ap->ap_ata && i < AHCI_MAX_PMPORTS; ++i) {
at = &ap->ap_ata[i];
at->at_type = ATA_PORT_T_NONE;
- at->at_probe = ATA_PORT_T_NONE;
+ at->at_probe = ATA_PROBE_FAILED;
}
/*
* Deactivating SUD only applies if the controller supports SUD.
*/
ahci_pwrite(ap, AHCI_PREG_SCTL, AHCI_PREG_SCTL_IPM_DISABLED);
- DELAY(1000);
+ ahci_os_sleep(1);
if (cmd & AHCI_PREG_CMD_SUD) {
cmd &= ~AHCI_PREG_CMD_SUD;
ahci_pwrite(ap, AHCI_PREG_CMD, cmd);
}
- DELAY(1000);
+ ahci_os_sleep(1);
/*
* Transition su to the spin-up state. HVA shall send COMRESET and
*/
cmd |= AHCI_PREG_CMD_SUD;
ahci_pwrite(ap, AHCI_PREG_CMD, cmd);
- DELAY(1000);
+ ahci_os_sleep(1);
/*
* Transition us to the Reset state. Theoretically we send a
r |= AHCI_PREG_SCTL_SPD_ANY;
}
ahci_pwrite(ap, AHCI_PREG_SCTL, r);
- DELAY(1000);
+ ahci_os_sleep(1);
/*
* Flush SERR_DIAG_X so the TFD can update.
r = ahci_pread(ap, AHCI_PREG_SERR);
while (r & AHCI_PREG_SERR_DIAG_X) {
ahci_pwrite(ap, AHCI_PREG_SERR, AHCI_PREG_SERR_DIAG_X);
- DELAY(1000);
+ ahci_os_sleep(1);
r = ahci_pread(ap, AHCI_PREG_SERR);
}
}
return(ATA_PORT_T_ATAPI);
} else if ((sig & 0xffff0000) ==
(SATA_SIGNATURE_PORT_MULTIPLIER & 0xffff0000)) {
- kprintf("found PM\n");
return(ATA_PORT_T_PM);
} else {
return(ATA_PORT_T_DISK);
/*
* Start a command and poll for completion.
*
+ * timeout is in ms and only counts once the command gets on-chip.
+ *
* NOTE: If the caller specifies a NULL timeout function the caller is
* responsible for clearing hardware state on failure, but we will
* deal with removing the ccb from any pending queue.
ahci_poll(struct ahci_ccb *ccb, int timeout, void (*timeout_fn)(void *))
{
struct ahci_port *ap = ccb->ccb_port;
- u_int32_t slot_mask = 1 << ccb->ccb_slot;
if (ccb->ccb_port->ap_state == AP_S_FATAL_ERROR) {
ccb->ccb_xa.state = ATA_S_ERROR;
ahci_start(ccb);
do {
- if (ahci_port_intr(ap, AHCI_PREG_CI_ALL_SLOTS) & slot_mask) {
- crit_exit();
- return (0);
- }
+ ahci_port_intr(ap);
if (ccb->ccb_xa.state != ATA_S_ONCHIP &&
ccb->ccb_xa.state != ATA_S_PENDING) {
- break;
+ crit_exit();
+ return (0);
}
- DELAY(1000000 / hz);
- } while (--timeout > 0);
-
- if (ccb->ccb_xa.state != ATA_S_ONCHIP &&
- ccb->ccb_xa.state != ATA_S_PENDING) {
- kprintf("%s: Warning poll completed unexpectedly for slot %d\n",
- PORTNAME(ap), ccb->ccb_slot);
- crit_exit();
- return (0);
- }
+ ahci_os_sleep(100);
+ if (ccb->ccb_xa.state == ATA_S_ONCHIP)
+ timeout -= 100;
+ } while (timeout > 0);
kprintf("%s: Poll timed-out for slot %d state %d\n",
ATANAME(ap, ccb->ccb_xa.at), ccb->ccb_slot, ccb->ccb_xa.state);
return (1);
}
+static
+__inline
+void
+ahci_start_timeout(struct ahci_ccb *ccb)
+{
+ if (ccb->ccb_xa.flags & ATA_F_TIMEOUT_DESIRED) {
+ ccb->ccb_xa.flags |= ATA_F_TIMEOUT_RUNNING;
+ callout_reset(&ccb->ccb_timeout,
+ (ccb->ccb_xa.timeout * hz + 999) / 1000,
+ ahci_ata_cmd_timeout_unserialized, ccb);
+ }
+}
+
void
ahci_start(struct ahci_ccb *ccb)
{
if (ap->ap_active || TAILQ_FIRST(&ap->ap_ccb_pending)) {
TAILQ_INSERT_TAIL(&ap->ap_ccb_pending, ccb, ccb_entry);
} else {
+ ahci_start_timeout(ccb);
KKASSERT(ap->ap_active_cnt == 0);
ap->ap_sactive |= (1 << ccb->ccb_slot);
ccb->ccb_xa.state = ATA_S_ONCHIP;
if (ap->ap_sactive || ap->ap_active_cnt >= limit) {
TAILQ_INSERT_TAIL(&ap->ap_ccb_pending, ccb, ccb_entry);
} else {
+ ahci_start_timeout(ccb);
ap->ap_active |= 1 << ccb->ccb_slot;
ccb->ccb_xa.state = ATA_S_ONCHIP;
ahci_pwrite(ap, AHCI_PREG_CI, 1 << ccb->ccb_slot);
/* Start all the NCQ commands at the head of the pending list. */
do {
TAILQ_REMOVE(&ap->ap_ccb_pending, nextccb, ccb_entry);
+ ahci_start_timeout(nextccb);
sact_change |= 1 << nextccb->ccb_slot;
nextccb->ccb_xa.state = ATA_S_ONCHIP;
nextccb = TAILQ_FIRST(&ap->ap_ccb_pending);
while (ap->ap_active_cnt < 2 &&
nextccb && (nextccb->ccb_xa.flags & ATA_F_NCQ) == 0) {
TAILQ_REMOVE(&ap->ap_ccb_pending, nextccb, ccb_entry);
+ ahci_start_timeout(nextccb);
ap->ap_active |= 1 << nextccb->ccb_slot;
nextccb->ccb_xa.state = ATA_S_ONCHIP;
ahci_pwrite(ap, AHCI_PREG_CI, 1 << nextccb->ccb_slot);
/* Process interrupts for each port */
while (is) {
port = ffs(is) - 1;
- if (sc->sc_ports[port]) {
- ahci_port_intr(sc->sc_ports[port],
- AHCI_PREG_CI_ALL_SLOTS);
- }
+ if (sc->sc_ports[port])
+ ahci_port_intr(sc->sc_ports[port]);
is &= ~(1 << port);
}
ahci_write(sc, AHCI_REG_IS, ack);
}
-u_int32_t
-ahci_port_intr(struct ahci_port *ap, u_int32_t ci_mask)
+void
+ahci_port_intr(struct ahci_port *ap)
{
struct ahci_softc *sc = ap->ap_sc;
- u_int32_t is, ci_saved, ci_masked, processed = 0;
+ u_int32_t is, ci_saved, ci_masked;
int slot;
struct ahci_ccb *ccb = NULL;
struct ata_port *ccb_at = NULL;
is, AHCI_PFMT_IS);
#endif
- /* Ack port interrupt only if checking all command slots. */
- if (ci_mask == AHCI_PREG_CI_ALL_SLOTS)
- ahci_pwrite(ap, AHCI_PREG_IS, is);
-
- if (is)
- DPRINTF(AHCI_D_INTR, "%s: interrupt: %b\n", PORTNAME(ap),
- is, AHCI_PFMT_IS);
+ /*
+ * Ack the port interrupt
+ */
+ ahci_pwrite(ap, AHCI_PREG_IS, is);
if (ap->ap_sactive) {
/* Active NCQ commands - use SActive instead of CI */
data = ahci_pread(ap, AHCI_PREG_SNTF);
if (data) {
kprintf("%s: NOTIFY %08x\n", PORTNAME(ap), data);
- ahci_pwrite(ap, AHCI_PREG_SNTF, data);
+ ahci_pwrite(ap, AHCI_PREG_SERR, AHCI_PREG_SERR_DIAG_N);
ahci_pwrite(ap, AHCI_PREG_IS, AHCI_PREG_IS_SDBS);
+ ahci_pwrite(ap, AHCI_PREG_SNTF, data);
+ ahci_cam_changed(ap, NULL, -1);
}
}
-#if 0
- /* XXX future IFS recovery code? or just scrap it */
- if (is & AHCI_PREG_IS_IFS) {
+
+ /*
+ * 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.
+ */
+ if ((is & AHCI_PREG_IS_IFS) && (ap->ap_flags & AP_F_IGNORE_IFS)) {
u_int32_t serr = ahci_pread(ap, AHCI_PREG_SERR);
- kprintf("%s: Ignoring IFS (XXX) (IS: %b, SERR: %b)\n",
- PORTNAME(ap),
- is, AHCI_PFMT_IS,
- serr, AHCI_PFMT_SERR);
+ if ((ap->ap_flags & AP_F_IFS_IGNORED) == 0) {
+ kprintf("%s: Ignoring IFS (XXX) (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;
ahci_pwrite(ap, AHCI_PREG_SERR, -1);
ahci_pwrite(ap, AHCI_PREG_IS, AHCI_PREG_IS_IFS);
is &= ~AHCI_PREG_IS_IFS;
ahci_port_start(ap);
need = NEED_RESTART;
}
-#endif
/*
* Port change (hot-plug).
* Any active bits not saved are completed within the restrictions
* imposed by the caller.
*/
- ci_masked = ~ci_saved & *active & ci_mask;
+ ci_masked = ~ci_saved & *active;
while (ci_masked) {
slot = ffs(ci_masked) - 1;
ccb = &ap->ap_ccbs[slot];
--ap->ap_active_cnt;
}
ccb->ccb_done(ccb);
-
- processed |= 1 << ccb->ccb_slot;
}
switch(need) {
if ((ap->ap_flags & AP_F_IN_RESET) == 0) {
kprintf("%s: HOTPLUG - Device inserted\n",
PORTNAME(ap));
- if (ahci_port_init(ap, NULL) == 0)
- ahci_cam_changed(ap, 1);
+ ap->ap_probe = ATA_PROBE_NEED_INIT;
+ ahci_cam_changed(ap, NULL, -1);
}
break;
case NEED_HOTPLUG_REMOVE:
kprintf("%s: HOTPLUG - Device removed\n",
PORTNAME(ap));
ahci_port_hardstop(ap);
- ahci_cam_changed(ap, 0);
+ /* ap_probe set to failed */
+ ahci_cam_changed(ap, NULL, -1);
}
break;
default:
break;
}
- return (processed);
}
struct ahci_ccb *
}
ccb->ccb_xa.state = ATA_S_PENDING;
- if (ahci_poll(ccb, hz, NULL) != 0)
+ if (ahci_poll(ccb, 1000, NULL) != 0)
goto err;
rc = 0;
for (i = 0; i < 1000; i++) {
if ((ahci_read(sc, r) & mask) != target)
return (0);
- DELAY(1000);
+ ahci_os_sleep(1);
}
return (1);
for (i = 0; i < timeout; i++) {
if ((ahci_pread(ap, r) & mask) == target)
return (0);
- DELAY(1000);
+ ahci_os_sleep(1);
}
return (1);
}
crit_enter();
- xa->flags |= ATA_F_TIMEOUT_RUNNING;
- callout_reset(&ccb->ccb_timeout, xa->timeout,
- ahci_ata_cmd_timeout_unserialized, ccb);
+ xa->flags |= ATA_F_TIMEOUT_DESIRED;
ahci_start(ccb);
crit_exit();
return (ATA_QUEUED);
xa->flags &= ~ATA_F_TIMEOUT_RUNNING;
callout_stop(&ccb->ccb_timeout);
}
+ xa->flags &= ~ATA_F_TIMEOUT_DESIRED;
if (xa->state == ATA_S_ONCHIP || xa->state == ATA_S_ERROR)
ahci_issue_pending_commands(ccb->ccb_port,
/*
* NOTE: Timeout will not be running if the command was polled.
+ * If we got here at least one of these flags should be set.
*/
- KKASSERT(xa->flags & (ATA_F_POLL|ATA_F_TIMEOUT_RUNNING));
+ KKASSERT(xa->flags & (ATA_F_POLL | ATA_F_TIMEOUT_DESIRED |
+ ATA_F_TIMEOUT_RUNNING));
xa->flags &= ~ATA_F_TIMEOUT_RUNNING;
ncq_cmd = (xa->flags & ATA_F_NCQ);
active = ncq_cmd ? &ap->ap_sactive : &ap->ap_active;
"before it got on chip\n", PORTNAME(ap), ccb->ccb_slot);
TAILQ_REMOVE(&ap->ap_ccb_pending, ccb, ccb_entry);
ccb_was_started = 0;
- } else if (ccb->ccb_xa.state == ATA_S_ONCHIP &&
- ahci_port_intr(ap, 1 << ccb->ccb_slot)) {
- DPRINTF(AHCI_D_TIMEOUT, "%s: final poll of port completed "
- "command in slot %d\n", PORTNAME(ap), ccb->ccb_slot);
- goto ret;
} else if (ccb->ccb_xa.state != ATA_S_ONCHIP) {
DPRINTF(AHCI_D_TIMEOUT, "%s: command slot %d already "
"handled%s\n", PORTNAME(ap), ccb->ccb_slot,
"in slot %d, active %08x\n", PORTNAME(ap), ncq_cmd ? " NCQ"
: "", ccb->ccb_slot, *active);
/* XXX */
- if (ccb->ccb_xa.at) {
+ if (ccb->ccb_xa.at && ap->ap_type == ATA_PORT_T_PM) {
/* XXX how do we unbrick a PM target? */
kprintf("%s: PM target bricked and timed-out, "
"disabling PM target but trying to "
"leave the port intact\n",
ATANAME(ap, ccb->ccb_xa.at));
ccb->ccb_xa.at->at_probe = ATA_PROBE_FAILED;
- ahci_port_intr(ap, AHCI_PREG_CI_ALL_SLOTS);
+ ahci_port_intr(ap);
ahci_port_stop(ap, 0);
ahci_port_clo(ap);
ahci_port_start(ap);
"bricked on us\n",
PORTNAME(ap));
ap->ap_state = AP_S_FATAL_ERROR;
- ahci_port_intr(ap, AHCI_PREG_CI_ALL_SLOTS);
+ ahci_port_intr(ap);
status = 1;
} else {
status = 0;
#define AHCI_PREG_SCTL_IPM_NOPARTIAL 0x100
#define AHCI_PREG_SCTL_IPM_NOSLUMBER 0x200
#define AHCI_PREG_SCTL_IPM_DISABLED 0x300
+#define AHCI_PREG_SCTL_SPM 0xf000 /* Select Power Management */
+#define AHCI_PREG_SCTL_SPM_NONE 0x0000
+#define AHCI_PREG_SCTL_SPM_NOPARTIAL 0x1000
+#define AHCI_PREG_SCTL_SPM_NOSLUMBER 0x2000
+#define AHCI_PREG_SCTL_SPM_DISABLED 0x3000
+#define AHCI_PREG_SCTL_PMP 0xf0000 /* Set PM port for xmit FISes */
+#define AHCI_PREG_SCTL_PMP_SHIFT 16
#define AHCI_PREG_SERR 0x30 /* SATA Error */
#define AHCI_PREG_SERR_ERR_I (1<<0) /* Recovered Data Integrity */
#define AHCI_PMREG_SSTS 0 /* use AHCI_PREG_SSTS_ bit defs */
#define AHCI_PMREG_SERR 1 /* use AHCI_PREG_SERR_ bit defs */
#define AHCI_PMREG_SCTL 2 /* use AHCI_PREG_SCTL_ bit defs */
+#define AHCI_PMREG_SACT 3 /* (not implemented on PM) */
+
+/*
+ * AHCI port multiplier revision information SCR[1] (see ahci_pm_read)
+ *
+ * Rev 1.1 is the one that should support async notification.
+ */
+#define AHCI_PMREV_PM1_0 0x00000002
+#define AHCI_PMREV_PM1_1 0x00000004
+#define AHCI_PFMT_PM_REV "\20" "\003PM1.1" "\002PM1.0"
+
+/*
+ * GSCR[64] and GSCR[96] - Port Multiplier features available and features
+ * enabled.
+ */
+#define AHCI_PMREG_FEA 64
+#define AHCI_PMREG_FEAEN 96 /* (features enabled) */
+#define AHCI_PMFEA_BIST 0x00000001 /* BIST Support */
+#define AHCI_PMFEA_PMREQ 0x00000002 /* Can issue PMREQp to host */
+#define AHCI_PMFEA_DYNSSC 0x00000004 /* Dynamic SSC transmit enab */
+#define AHCI_PMFEA_ASYNCNOTIFY 0x00000008 /* Async notification */
+
+#define AHCI_PFMT_PM_FEA "\20" \
+ "\004AsyncNotify" \
+ "\003DynamicSSC" \
+ "\002PMREQ" \
+ "\001BIST"
+
+/*
+ * Enable generation of async notify events for individual targets
+ * via the PMEENA register. Each bit in PMEINFO is a wire-or of all
+ * SERROR bits for that target. To enable a new notification event
+ * the SERROR bits in PMSERROR_REGNO must be cleared.
+ */
+#define AHCI_PMREG_EINFO 32 /* error info 16 ports */
+#define AHCI_PMREG_EEENA 33 /* error info enable 16 ports */
/*
* AHCI mapped structures
#define AP_F_BUS_REGISTERED 0x0001
#define AP_F_CAM_ATTACHED 0x0002
#define AP_F_IN_RESET 0x0004
+#define AP_F_SCAN_RUNNING 0x0008
+#define AP_F_SCAN_REQUESTED 0x0010
+#define AP_F_IGNORE_IFS 0x0020
+#define AP_F_IFS_IGNORED 0x0040
+#define AP_F_IFS_OCCURED 0x0080
struct cam_sim *ap_sim;
- struct cam_path *ap_path;
struct ahci_rfis *ap_rfis;
struct ahci_dmamem *ap_dmamem_rfis;
const struct ahci_device *ahci_lookup_device(device_t dev);
int ahci_init(struct ahci_softc *);
int ahci_port_alloc(struct ahci_softc *, u_int);
+void ahci_port_state_machine(struct ahci_port *ap);
void ahci_port_free(struct ahci_softc *, u_int);
int ahci_port_reset(struct ahci_port *, struct ata_port *at, int);
int ahci_pwait_eq(struct ahci_port *, int, bus_size_t,
u_int32_t, u_int32_t);
void ahci_intr(void *);
-u_int32_t ahci_port_intr(struct ahci_port *, u_int32_t);
+void ahci_port_intr(struct ahci_port *);
int ahci_cam_attach(struct ahci_port *ap);
-void ahci_cam_changed(struct ahci_port *ap, int found);
+void ahci_cam_changed(struct ahci_port *ap, struct ata_port *at, int found);
void ahci_cam_detach(struct ahci_port *ap);
+int ahci_cam_probe(struct ahci_port *ap, struct ata_port *at);
struct ata_xfer *ahci_ata_get_xfer(struct ahci_port *ap, struct ata_port *at);
void ahci_ata_put_xfer(struct ata_xfer *xa);
int ahci_ata_cmd(struct ata_xfer *xa);
int ahci_pm_identify(struct ahci_port *ap);
+int ahci_pm_set_feature(struct ahci_port *ap, int feature, int enable);
int ahci_pm_hardreset(struct ahci_port *ap, int target, int hard);
int ahci_pm_softreset(struct ahci_port *ap, int target);
int ahci_pm_phy_status(struct ahci_port *ap, int target, u_int32_t *datap);
int which, u_int32_t *res);
int ahci_pm_write(struct ahci_port *ap, int target,
int which, u_int32_t data);
+void ahci_pm_check_good(struct ahci_port *ap, int target);
void ahci_ata_cmd_timeout(void *arg);
struct ahci_ccb *ahci_get_ccb(struct ahci_port *ap);
void ahci_put_ccb(struct ahci_ccb *ccb);
void (*timeout_fn)(void *));
int ahci_port_signature_detect(struct ahci_port *ap, struct ata_port *at);
+void ahci_os_sleep(int ticks);
extern u_int32_t AhciForceGen1;
}
error = ahci_port_alloc(sc, i);
if (error == 0) {
- ahci_cam_attach(sc->sc_ports[i]);
+ if (ahci_cam_attach(sc->sc_ports[i]) == 0)
+ ahci_cam_changed(sc->sc_ports[i], NULL, -1);
}
if (error == ENODEV)
error = 0;
static void ahci_ata_atapi_sense(struct ata_fis_d2h *rfis,
struct scsi_sense_data *sense_data);
-static int ahci_cam_probe(struct ahci_port *ap, struct ata_port *at);
static int ahci_cam_probe_disk(struct ahci_port *ap, struct ata_port *at);
static int ahci_cam_probe_atapi(struct ahci_port *ap, struct ata_port *at);
static void ahci_ata_dummy_done(struct ata_xfer *xa);
return (EINVAL);
}
ap->ap_flags |= AP_F_BUS_REGISTERED;
- error = xpt_create_path(&ap->ap_path, NULL, cam_sim_path(sim),
- CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
- if (error != CAM_REQ_CMP) {
- ahci_cam_detach(ap);
- return (ENOMEM);
- }
error = ahci_cam_probe(ap, NULL);
if (error) {
}
ap->ap_flags |= AP_F_CAM_ATTACHED;
- ahci_cam_rescan(ap);
-
return(0);
}
/*
- * Use a wildcard path to indicate that all the devices on the bus
- * were found, or lost.
+ * The state of the port has changed.
+ *
+ * If at is NULL the physical port has changed state.
+ * If at is non-NULL a particular target behind a PM has changed state.
+ *
+ * If found is -1 the target state must be queued to a non-interrupt context.
+ * (only works with at == NULL).
+ *
+ * If found is 0 the target was removed.
+ * If found is 1 the target was inserted.
*/
void
-ahci_cam_changed(struct ahci_port *ap, int found)
+ahci_cam_changed(struct ahci_port *ap, struct ata_port *atx, int found)
{
struct cam_path *tmppath;
+ int status;
+ int target;
+
+ target = atx ? atx->at_target : CAM_TARGET_WILDCARD;
if (ap->ap_sim == NULL)
return;
- if (xpt_create_path(&tmppath, NULL, cam_sim_path(ap->ap_sim),
- CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
- return;
- }
-
- if (found) {
- kprintf("%s: CAM RESCAN\n", PORTNAME(ap));
- ahci_cam_probe(ap, NULL);
- /*xpt_async(AC_FOUND_DEVICE, tmppath, NULL);*/
+ if (found == CAM_TARGET_WILDCARD) {
+ status = xpt_create_path(&tmppath, NULL,
+ cam_sim_path(ap->ap_sim),
+ target, CAM_LUN_WILDCARD);
+ if (status != CAM_REQ_CMP)
+ return;
ahci_cam_rescan(ap);
} else {
- xpt_async(AC_LOST_DEVICE, tmppath, NULL);
+ status = xpt_create_path(&tmppath, NULL,
+ cam_sim_path(ap->ap_sim),
+ target,
+ CAM_LUN_WILDCARD);
+ if (status != CAM_REQ_CMP)
+ return;
+#if 0
+ /*
+ * This confuses CAM
+ */
+ if (found)
+ xpt_async(AC_FOUND_DEVICE, tmppath, NULL);
+ else
+ xpt_async(AC_LOST_DEVICE, tmppath, NULL);
+#endif
}
xpt_free_path(tmppath);
}
if (ap->ap_sim) {
xpt_freeze_simq(ap->ap_sim, 1);
}
- if (ap->ap_path) {
- xpt_free_path(ap->ap_path);
- ap->ap_path = NULL;
- }
if (ap->ap_flags & AP_F_BUS_REGISTERED) {
error = xpt_bus_deregister(cam_sim_path(ap->ap_sim));
KKASSERT(error == CAM_REQ_CMP);
* If at is NULL we are probing the direct-attached device on the port,
* which may or may not be a port multiplier.
*/
-static int
+int
ahci_cam_probe(struct ahci_port *ap, struct ata_port *atx)
{
struct ata_port *at;
const char *scstr;
const char *type;
- /*
- * If nothing is probed on the port then there is nothing for us
- * to do. In this state ap->ap_ata will also probably be NULL.
- */
- if (ap->ap_type == ATA_PORT_T_NONE)
- return (EIO);
+ error = EIO;
/*
* A NULL atx indicates a probe of the directly connected device.
* an (at) pointing to target 0.
*/
if (atx == NULL) {
+ at = ap->ap_ata; /* direct attached - device 0 */
if (ap->ap_type == ATA_PORT_T_PM) {
kprintf("%s: Found Port Multiplier\n",
ATANAME(ap, atx));
ap->ap_probe = ATA_PROBE_GOOD;
return (0);
}
- at = ap->ap_ata; /* direct attached - device 0 */
at->at_type = ap->ap_type;
} else {
+ at = atx;
if (atx->at_type == ATA_PORT_T_PM) {
kprintf("%s: Bogus device, reducing port count to %d\n",
ATANAME(ap, atx), atx->at_target);
if (ap->ap_pmcount > atx->at_target)
ap->ap_pmcount = atx->at_target;
- return (EIO);
+ goto err;
}
- at = atx;
}
+ if (ap->ap_type == ATA_PORT_T_NONE)
+ goto err;
if (at->at_type == ATA_PORT_T_NONE)
- return (EIO);
+ goto err;
/*
* Issue identify, saving the result
xa->fis->features = 0;
xa->fis->device = 0;
xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
- xa->timeout = hz;
+ xa->timeout = 1000;
status = ahci_ata_cmd(xa);
if (status != ATA_COMPLETE) {
kprintf("%s: Detected %s device but unable to IDENTIFY\n",
ATANAME(ap, atx), type);
ahci_ata_put_xfer(xa);
- return(EIO);
+ goto err;
}
if (xa->state != ATA_S_COMPLETE) {
kprintf("%s: Detected %s device but unable to IDENTIFY "
" xa->state=%d\n",
ATANAME(ap, atx), type, xa->state);
ahci_ata_put_xfer(xa);
- return(EIO);
+ goto err;
}
ahci_ata_put_xfer(xa);
at->at_capacity = capacity;
if (atx == NULL)
ap->ap_probe = ATA_PROBE_GOOD;
- at->at_probe = ATA_PROBE_GOOD;
capacity_bytes = capacity * 512;
error = EIO;
break;
}
- return (0);
+err:
+ if (error) {
+ at->at_probe = ATA_PROBE_FAILED;
+ if (atx == NULL)
+ ap->ap_probe = at->at_probe;
+ } else {
+ at->at_probe = ATA_PROBE_GOOD;
+ if (atx == NULL)
+ ap->ap_probe = at->at_probe;
+ }
+ return (error);
}
/*
xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
xa->fis->device = 0;
xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
- xa->timeout = hz;
+ xa->timeout = 1000;
xa->datalen = 0;
status = ahci_ata_cmd(xa);
if (status == ATA_COMPLETE)
xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
xa->fis->device = 0;
xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
- xa->timeout = hz;
+ xa->timeout = 1000;
xa->datalen = 0;
status = ahci_ata_cmd(xa);
if (status == ATA_COMPLETE)
xa->fis->command = ATA_C_SEC_FREEZE_LOCK;
xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
- xa->timeout = hz;
+ xa->timeout = 1000;
xa->datalen = 0;
status = ahci_ata_cmd(xa);
if (status == ATA_COMPLETE)
}
/*
- * Initiate a bus scan.
+ * Use an engineering request to initiate a target scan for devices
+ * behind a port multiplier.
*
- * An asynchronous bus scan is used to avoid reentrancy issues
+ * An asynchronous bus scan is used to avoid reentrancy issues.
*/
static void
ahci_cam_rescan_callback(struct cam_periph *periph, union ccb *ccb)
{
+ struct ahci_port *ap = ccb->ccb_h.sim_priv.entries[0].ptr;
+
+ ap->ap_flags &= ~AP_F_SCAN_RUNNING;
+ if (ap->ap_flags & AP_F_SCAN_REQUESTED) {
+ ap->ap_flags &= ~AP_F_SCAN_REQUESTED;
+ ahci_cam_rescan(ap);
+ }
kfree(ccb, M_TEMP);
}
struct cam_path *path;
union ccb *ccb;
int status;
+ int i;
+
+ if (ap->ap_flags & AP_F_SCAN_RUNNING) {
+ ap->ap_flags |= AP_F_SCAN_REQUESTED;
+ return;
+ }
+ ap->ap_flags |= AP_F_SCAN_RUNNING;
+ for (i = 0; i < AHCI_MAX_PMPORTS; ++i) {
+ ap->ap_ata[i].at_features |= ATA_PORT_F_RESCAN;
+ }
ccb = kmalloc(sizeof(*ccb), M_TEMP, M_WAITOK | M_ZERO);
status = xpt_create_path(&path, xpt_periph, cam_sim_path(ap->ap_sim),
return;
xpt_setup_ccb(&ccb->ccb_h, path, 5); /* 5 = low priority */
- ccb->ccb_h.func_code = XPT_SCAN_BUS | XPT_FC_QUEUED;
+ ccb->ccb_h.func_code = XPT_ENG_EXEC | XPT_FC_QUEUED;
ccb->ccb_h.cbfcnp = ahci_cam_rescan_callback;
+ ccb->ccb_h.sim_priv.entries[0].ptr = ap;
ccb->crcn.flags = CAM_FLAG_NONE;
xpt_action(ccb);
/* scan is now underway */
}
+static void
+ahci_xpt_rescan(struct ahci_port *ap)
+{
+ struct cam_path *path;
+ union ccb *ccb;
+ int status;
+
+ status = xpt_create_path(&path, xpt_periph, cam_sim_path(ap->ap_sim),
+ CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
+ if (status != CAM_REQ_CMP)
+ return;
+ ccb = kmalloc(sizeof(*ccb), M_TEMP, M_WAITOK | M_ZERO);
+ xpt_setup_ccb(&ccb->ccb_h, path, 5); /* 5 = low priority */
+ ccb->ccb_h.func_code = XPT_SCAN_BUS | XPT_FC_QUEUED;
+ ccb->ccb_h.cbfcnp = ahci_cam_rescan_callback;
+ ccb->ccb_h.sim_priv.entries[0].ptr = ap;
+ ccb->crcn.flags = CAM_FLAG_NONE;
+ xpt_action(ccb);
+}
+
+#if 0
+ ccb = xpt_alloc_ccb();
+ status = xpt_create_path(&ccb->ccb_h.path, xpt_periph,
+ cam_sim_path(ap->ap_sim),
+ CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
+ if (status == CAM_REQ_CMP) {
+ kprintf("RESCAN SCSI BUS %d\n", ccb->ccb_h.timeout);
+ ccb->crcn.flags = CAM_FLAG_NONE;
+ xpt_rescan(ccb);
+ } else {
+ xpt_free_ccb(ccb);
+ }
+#endif
+
/*
* Action function - dispatch command
*/
unit = cam_sim_unit(sim);
/*
+ * Early failure checks. These checks do not apply to XPT_PATH_INQ,
+ * otherwise the bus rescan will not remove the dead devices when
+ * unplugging a PM.
+ *
* For non-wildcards we have one target (0) and one lun (0),
* unless we have a port multiplier.
*
*
* XXX What do we do with a LUN wildcard?
*/
- if (ccbh->target_id != CAM_TARGET_WILDCARD) {
+ if (ccbh->target_id != CAM_TARGET_WILDCARD &&
+ ccbh->func_code != XPT_PATH_INQ) {
if (ap->ap_type == ATA_PORT_T_NONE) {
- ccbh->status = CAM_REQ_INVALID;
+ ccbh->status = CAM_DEV_NOT_THERE;
xpt_done(ccb);
return;
}
* Switch on the meta XPT command
*/
switch(ccbh->func_code) {
+ case XPT_ENG_EXEC:
+ /*
+ * This routine is called after a port multiplier has been
+ * probed.
+ */
+ ccbh->status = CAM_REQ_CMP;
+ ahci_port_state_machine(ap);
+ xpt_done(ccb);
+
+ /*
+ * Rescanning the scsi bus should clean up the peripheral
+ * associations.
+ */
+ ahci_xpt_rescan(ap);
+ break;
case XPT_PATH_INQ:
+ /*
+ * This command always succeeds, otherwise the bus scan
+ * will not detach dead devices.
+ */
ccb->cpi.version_num = 1;
ccb->cpi.hba_inquiry = 0;
ccb->cpi.target_sprt = 0;
ccb->cpi.protocol = PROTO_SCSI;
ccb->cpi.protocol_version = SCSI_REV_2;
- /*
- * Non-zero target and lun ids will be used for future
- * port multiplication(?). A target wildcard indicates only
- * the general bus is being probed.
- *
- * XXX What do we do with a LUN wildcard?
- */
+ ccbh->status = CAM_REQ_CMP;
if (ccbh->target_id != CAM_TARGET_WILDCARD) {
switch(ahci_pread(ap, AHCI_PREG_SSTS) &
AHCI_PREG_SSTS_SPD) {
ccb->cpi.base_transfer_speed = 1000;
break;
}
- ccbh->status = CAM_REQ_CMP;
-
- /*
- * Initialize the port as needed
- */
- if (ap->ap_type == ATA_PORT_T_NONE) {
- if (ap->ap_probe == ATA_PROBE_NEED_HARD_RESET)
- ahci_port_reset(ap, NULL, 1);
- if (ap->ap_probe == ATA_PROBE_NEED_SOFT_RESET)
- ahci_port_reset(ap, NULL, 0);
- if (ap->ap_probe == ATA_PROBE_NEED_IDENT)
- ahci_cam_probe(ap, atx);
- if (ap->ap_type == ATA_PORT_T_NONE)
- ccbh->status = CAM_DEV_NOT_THERE;
- }
-
- /*
- * Initialize the target (if behind a port multiplier)
- * as needed.
- */
- if (ap->ap_type != ATA_PORT_T_NONE &&
- atx && atx->at_type == ATA_PORT_T_NONE) {
- if (atx->at_probe == ATA_PROBE_NEED_HARD_RESET)
- ahci_port_reset(ap, atx, 1);
- if (atx->at_probe == ATA_PROBE_NEED_SOFT_RESET)
- ahci_port_reset(ap, atx, 0);
- if (atx->at_probe == ATA_PROBE_NEED_IDENT)
- ahci_cam_probe(ap, atx);
- if (atx->at_type == ATA_PORT_T_NONE)
- ccbh->status = CAM_DEV_NOT_THERE;
- }
- } else {
- ccbh->status = CAM_REQ_CMP;
+#if 0
+ if (ap->ap_type == ATA_PORT_T_NONE)
+ ccbh->status = CAM_DEV_NOT_THERE;
+#endif
}
xpt_done(ccb);
break;
case XPT_RESET_DEV:
lwkt_serialize_enter(&ap->ap_sc->sc_serializer);
if (ap->ap_type == ATA_PORT_T_NONE) {
- ccbh->status = CAM_REQ_INVALID;
+ ccbh->status = CAM_DEV_NOT_THERE;
} else {
ahci_port_reset(ap, atx, 0);
ccbh->status = CAM_REQ_CMP;
lwkt_serialize_enter(&ap->ap_sc->sc_serializer);
ahci_port_reset(ap, NULL, 1);
lwkt_serialize_exit(&ap->ap_sc->sc_serializer);
-
- xpt_async(AC_BUS_RESET, ap->ap_path, NULL);
-
ccbh->status = CAM_REQ_CMP;
xpt_done(ccb);
break;
ap = cam_sim_softc(sim);
crit_enter();
lwkt_serialize_enter(&ap->ap_sc->sc_serializer);
- ahci_port_intr(ap, AHCI_PREG_CI_ALL_SLOTS);
+ ahci_port_intr(ap);
lwkt_serialize_exit(&ap->ap_sc->sc_serializer);
crit_exit();
}
fis->flags = ATA_H2D_FLAGS_CMD;
fis->command = ATA_C_FLUSH_CACHE;
fis->device = 0;
- if (xa->timeout < 45 * hz)
- xa->timeout = 45 * hz;
+ if (xa->timeout < 45000)
+ xa->timeout = 45000;
xa->datalen = 0;
xa->flags = ATA_F_READ;
xa->complete = ahci_ata_complete_disk_synchronize_cache;
xa->data = csio->data_ptr;
xa->datalen = csio->dxfer_len;
xa->complete = ahci_ata_complete_disk_rw;
- xa->timeout = ccbh->timeout * hz / 1000;
+ xa->timeout = ccbh->timeout; /* milliseconds */
if (ccbh->flags & CAM_POLLED)
xa->flags |= ATA_F_POLL;
break;
xa->flags = flags;
xa->data = csio->data_ptr;
xa->datalen = csio->dxfer_len;
- xa->timeout = ccbh->timeout * hz / 1000;
+ xa->timeout = ccbh->timeout; /* milliseconds */
if (ccbh->flags & CAM_POLLED)
xa->flags |= ATA_F_POLL;
}
#endif
+
+void
+ahci_os_sleep(int ms)
+{
+ int ticks;
+
+#if 0
+ if (mygd->gd_intr_nesting_level) {
+ DELAY(ms * 1000);
+ } else {
+#endif
+ {
+ ticks = hz * ms / 1000 + 1;
+ tsleep(&ticks, 0, "ahslp", ticks);
+ }
+}
#include <bus/cam/cam_periph.h>
#include <bus/cam/cam_sim.h>
#include <bus/cam/cam_xpt_sim.h>
+#include <bus/cam/cam_xpt_periph.h>
#include <bus/cam/cam_debug.h>
#include <bus/cam/scsi/scsi_all.h>
u_int32_t chipid;
u_int32_t rev;
u_int32_t nports;
-
- kprintf("%s: pm_identify\n", PORTNAME(ap));
+ u_int32_t data1;
+ u_int32_t data2;
ap->ap_pmcount = 0;
ap->ap_probe = ATA_PROBE_FAILED;
if (ahci_pm_read(ap, 15, 0, &chipid))
- return(EIO);
+ goto err;
if (ahci_pm_read(ap, 15, 1, &rev))
- return(EIO);
+ goto err;
if (ahci_pm_read(ap, 15, 2, &nports))
- return(EIO);
+ goto err;
+ nports &= 0x0000000F; /* only the low 4 bits */
ap->ap_probe = ATA_PROBE_GOOD;
- kprintf("%s: port multiplier chip=%08x rev=%08x nports=%d\n",
- PORTNAME(ap), chipid, rev, nports);
+ kprintf("%s: Port multiplier: chip=%08x rev=0x%b nports=%d\n",
+ PORTNAME(ap),
+ chipid,
+ rev, AHCI_PFMT_PM_REV,
+ nports);
ap->ap_pmcount = nports;
+
+ if (ahci_pm_read(ap, 15, AHCI_PMREG_FEA, &data1)) {
+ kprintf("%s: Port multiplier: Warning, "
+ "cannot read feature register\n", PORTNAME(ap));
+ } else {
+ kprintf("%s: Port multiplier features: 0x%b\n",
+ PORTNAME(ap),
+ data1,
+ AHCI_PFMT_PM_FEA);
+ }
+ if (ahci_pm_read(ap, 15, AHCI_PMREG_FEAEN, &data2) == 0) {
+ kprintf("%s: Port multiplier defaults: 0x%b\n",
+ PORTNAME(ap),
+ data2,
+ AHCI_PFMT_PM_FEA);
+ }
+
+ /*
+ * Turn on async notification if we support and the PM supports it.
+ * This allows the PM to forward async notification events to us and
+ * it will also generate an event for target 15 for hot-plug events
+ * (or is supposed to anyway).
+ */
+ if ((ap->ap_sc->sc_cap & AHCI_REG_CAP_SSNTF) &&
+ (data1 & AHCI_PMFEA_ASYNCNOTIFY)) {
+ u_int32_t serr_bits = AHCI_PREG_SERR_DIAG_N |
+ AHCI_PREG_SERR_DIAG_X;
+ data2 |= AHCI_PMFEA_ASYNCNOTIFY;
+ if (ahci_pm_write(ap, 15, AHCI_PMREG_FEAEN, data2)) {
+ kprintf("%s: Port multiplier: AsyncNotify cannot be "
+ "enabled\n", PORTNAME(ap));
+ } else if (ahci_pm_write(ap, 15, AHCI_PMREG_EEENA, serr_bits)) {
+ kprintf("%s: Port mulltiplier: AsyncNotify unable "
+ "to enable error info bits\n", PORTNAME(ap));
+ } else {
+ kprintf("%s: Port multiplier: AsyncNotify enabled\n",
+ PORTNAME(ap));
+ }
+ }
+
return (0);
+err:
+ kprintf("%s: Port multiplier cannot be identified\n", PORTNAME(ap));
+ return (EIO);
}
/*
struct ata_port *at;
u_int32_t data;
int loop;
+ int error = EIO;
- kprintf("%s.%d: ahci_pm_hardreset\n", PORTNAME(ap), target);
+ kprintf("%s.%d: ahci_pm_hardreset hard=%d\n",
+ PORTNAME(ap), target, hard);
at = &ap->ap_ata[target];
data = AHCI_PREG_SCTL_IPM_DISABLED;
if (hard == 2)
data |= AHCI_PREG_SCTL_DET_DISABLE;
+ if (ahci_pm_write(ap, target, AHCI_PMREG_SERR, -1))
+ goto err;
if (ahci_pm_write(ap, target, AHCI_PMREG_SCTL, data))
- return(EIO);
- DELAY(10000);
+ goto err;
+ ahci_os_sleep(10);
/*
* Start transmitting COMRESET. COMRESET must be sent for at
data |= AHCI_PREG_SCTL_SPD_ANY;
}
if (ahci_pm_write(ap, target, AHCI_PMREG_SCTL, data))
- return(EIO);
- DELAY(1000);
+ goto err;
+ ahci_os_sleep(2);
+
+#if 0
+ if (ahci_pm_phy_status(ap, target, &data)) {
+ kprintf("%s: Cannot clear phy status\n",
+ ATANAME(ap ,at));
+ }
+#endif
/*
* Flush any status, then clear DET to initiate negotiation.
ahci_pm_write(ap, target, AHCI_PMREG_SERR, -1);
data = AHCI_PREG_SCTL_IPM_DISABLED | AHCI_PREG_SCTL_DET_NONE;
if (ahci_pm_write(ap, target, AHCI_PMREG_SCTL, data))
- return(EIO);
+ goto err;
/*
* Try to determine if there is a device on the port.
* If we fail clear any pending status since we may have
* cycled the phy and probably caused another PRCS interrupt.
*/
- for (loop = 30; loop; --loop) {
+ for (loop = 3; loop; --loop) {
if (ahci_pm_read(ap, target, AHCI_PMREG_SSTS, &data))
- return(EIO);
+ goto err;
if (data & AHCI_PREG_SSTS_DET)
break;
+ ahci_os_sleep(100);
}
if (loop == 0) {
kprintf("%s.%d: Port appears to be unplugged\n",
PORTNAME(ap), target);
- return (ENODEV);
+ error = ENODEV;
+ goto err;
}
/*
* There is something on the port. Give the device 3 seconds
* to fully negotiate.
*/
- for (loop = 300; loop; --loop) {
+ for (loop = 30; loop; --loop) {
if (ahci_pm_read(ap, target, AHCI_PMREG_SSTS, &data))
- return(EIO);
+ goto err;
if ((data & AHCI_PREG_SSTS_DET) == AHCI_PREG_SSTS_DET_DEV)
break;
- DELAY(10000);
+ ahci_os_sleep(100);
}
+
+ /*
+ * Clear SERR on the target so we get a new NOTIFY event if a hot-plug
+ * or hot-unplug occurs.
+ */
+ ahci_pm_write(ap, target, AHCI_PMREG_SERR, -1);
+
+ /*
+ * Device not detected
+ */
if (loop == 0) {
kprintf("%s: Device may be powered down\n",
PORTNAME(ap));
- return (ENODEV);
+ error = ENODEV;
+ goto err;
}
/*
- * Device detected! Wait for it to become ready? XXX
+ * Device detected
*/
- ahci_pm_write(ap, target, AHCI_PMREG_SERR, -1);
kprintf("%s.%d: Device detected data=%08x\n",
PORTNAME(ap), target, data);
- at->at_probe = ATA_PROBE_NEED_SOFT_RESET;
- return (0);
+ ahci_os_sleep(100);
+ error = 0;
+err:
+ at->at_probe = error ? ATA_PROBE_FAILED : ATA_PROBE_NEED_SOFT_RESET;
+ return (EIO);
}
/*
struct ahci_ccb *ccb = NULL;
struct ahci_cmd_hdr *cmd_slot;
u_int8_t *fis;
- int rc, count;
+ int count;
+ int error;
u_int32_t data;
+ int tried_longer;
- rc = EIO;
+ error = EIO;
at = &ap->ap_ata[target];
DPRINTF(AHCI_D_VERBOSE, "%s: soft reset\n", PORTNAME(ap));
kprintf("%s: ahci_pm_softreset\n", ATANAME(ap, at));
count = 2;
-
+ tried_longer = 0;
retry:
/*
* Try to clear the phy so we get a good signature, otherwise
ccb->ccb_xa.state = ATA_S_PENDING;
ccb->ccb_xa.flags = 0;
- if (ahci_poll(ccb, hz, ahci_ata_cmd_timeout) != 0 ||
+ /*
+ * XXX hack to ignore IFS errors which can occur during the target
+ * device's reset.
+ *
+ * If an IFS error occurs the target is probably powering up,
+ * so we try for a longer period of time.
+ */
+ ap->ap_flags |= AP_F_IGNORE_IFS;
+ ap->ap_flags &= ~(AP_F_IFS_IGNORED | AP_F_IFS_OCCURED);
+
+ if (ahci_poll(ccb, 1000, ahci_ata_cmd_timeout) != 0 ||
ccb->ccb_xa.state != ATA_S_COMPLETE) {
kprintf("%s: First FIS failed\n", ATANAME(ap, at));
+ if (tried_longer == 0 && (ap->ap_flags & AP_F_IFS_OCCURED)) {
+ tried_longer = 1;
+ count += 4;
+ }
if (--count) {
fis[15] = 0;
ahci_put_ccb(ccb);
/*
* The device may muff the PHY up.
*/
- DELAY(100000); /* XXX 3000 */
+ ahci_os_sleep(100); /* XXX 3ms should do it? */
/*
* Prep second D2H command to read status and complete reset sequence
ccb->ccb_xa.state = ATA_S_PENDING;
ccb->ccb_xa.flags = 0;
- if (ahci_poll(ccb, hz, ahci_ata_cmd_timeout) != 0 ||
+ if (ahci_poll(ccb, 1000, ahci_ata_cmd_timeout) != 0 ||
ccb->ccb_xa.state != ATA_S_COMPLETE) {
kprintf("%s: Second FIS failed\n", ATANAME(ap, at));
if (--count) {
goto err;
}
- DELAY(10000);
+ ahci_os_sleep(10);
/*
* Do it again, even if we think we got a good result
if (ahci_port_signature_detect(ap, at) != at->at_type) {
kprintf("%s: device signature unexpectedly "
"changed\n", ATANAME(ap, at));
- rc = EBUSY; /* XXX */
+ error = EBUSY; /* XXX */
}
}
- rc = 0;
+ error = 0;
- DELAY(3000);
+ ahci_os_sleep(100);
err:
/*
* Clean up the CCB. If the command failed it already went through
* Don't kill the port if the softreset is on a port multiplier
* target, that would kill all the targets!
*/
- if (rc)
- at->at_probe = ATA_PROBE_FAILED;
- else
- at->at_probe = ATA_PROBE_NEED_IDENT;
- kprintf("%s: ahci_pm_softreset done %d\n", ATANAME(ap, at), rc);
+ ap->ap_flags &= ~AP_F_IGNORE_IFS;
+
+ /*
+ * Clear error status so we can detect removal
+ */
+ if (ahci_pm_write(ap, target, AHCI_PMREG_SERR, -1)) {
+ kprintf("%s: ahci_pm_softreset unable to clear SERR\n",
+ ATANAME(ap, at));
+ }
+
+ kprintf("%s: ahci_pm_softreset done %d\n", ATANAME(ap, at), error);
- return (rc);
+ at->at_probe = error ? ATA_PROBE_FAILED : ATA_PROBE_NEED_IDENT;
+ return (error);
}
return(error);
}
+int
+ahci_pm_set_feature(struct ahci_port *ap, int feature, int enable)
+{
+ struct ata_xfer *xa;
+ int status;
+ int error;
+
+ xa = ahci_ata_get_xfer(ap, &ap->ap_ata[15]);
+
+ bzero(xa->fis, sizeof(*xa->fis));
+ xa->fis->type = ATA_FIS_TYPE_H2D;
+ xa->fis->flags = ATA_H2D_FLAGS_CMD | 15;
+ xa->fis->command = enable ? ATA_C_SATA_FEATURE_ENA :
+ ATA_C_SATA_FEATURE_DIS;
+ xa->fis->sector_count = feature;
+ xa->fis->control = ATA_FIS_CONTROL_4BIT;
+
+ xa->complete = ahci_pm_dummy_done;
+ xa->datalen = 0;
+ xa->flags = ATA_F_READ | ATA_F_POLL;
+ xa->timeout = 1000;
+
+ status = ahci_ata_cmd(xa);
+ error = (status == ATA_COMPLETE && xa->state == ATA_S_COMPLETE) ?
+ 0 : EIO;
+ ahci_ata_put_xfer(xa);
+ return(error);
+}
+
+/*
+ * Check that a target is still good.
+ */
+void
+ahci_pm_check_good(struct ahci_port *ap, int target)
+{
+ struct ata_port *at;
+ u_int32_t data;
+
+ /*
+ * It looks like we might have to read the EINFO register
+ * to allow the PM to generate a new event.
+ */
+ if (ahci_pm_read(ap, 15, AHCI_PMREG_EINFO, &data)) {
+ kprintf("%s: Port multiplier EINFO could not be read\n",
+ PORTNAME(ap));
+ }
+ if (ahci_pm_write(ap, target, AHCI_PMREG_SERR, -1)) {
+ kprintf("%s: Port multiplier: SERR could not be cleared\n",
+ PORTNAME(ap));
+ }
+
+ if (target == CAM_TARGET_WILDCARD)
+ return;
+
+ at = &ap->ap_ata[target];
+ if (ahci_pm_read(ap, target, AHCI_PMREG_SSTS, &data)) {
+ kprintf("%s: Unable to access PM SSTS register target %d\n",
+ PORTNAME(ap), target);
+ return;
+ }
+ if ((data & AHCI_PREG_SSTS_DET) != AHCI_PREG_SSTS_DET_DEV) {
+ if (at->at_probe != ATA_PROBE_FAILED) {
+ at->at_probe = ATA_PROBE_FAILED;
+ at->at_type = ATA_PORT_T_NONE;
+ at->at_features |= ATA_PORT_F_RESCAN;
+ kprintf("%s: HOTPLUG - Device removed\n",
+ ATANAME(ap, at));
+ }
+ } else {
+ if (at->at_probe == ATA_PROBE_FAILED) {
+ at->at_probe = ATA_PROBE_NEED_HARD_RESET;
+ at->at_features |= ATA_PORT_F_RESCAN;
+ kprintf("%s: HOTPLUG - Device inserted\n",
+ ATANAME(ap, at));
+ }
+ }
+}
+
/*
* Read a PM register
*/
xa->complete = ahci_pm_dummy_done;
xa->datalen = 0;
xa->flags = ATA_F_READ | ATA_F_POLL;
- xa->timeout = hz;
+ xa->timeout = 1000;
status = ahci_ata_cmd(xa);
if (status == ATA_COMPLETE && xa->state == ATA_S_COMPLETE) {
(xa->rfis.lba_mid << 16) | (xa->rfis.lba_high << 24);
error = 0;
} else {
+ kprintf("pm_read status %d xa->state %d\n", status, xa->state);
*datap = 0;
error = EIO;
}
xa->complete = ahci_pm_dummy_done;
xa->datalen = 0;
xa->flags = ATA_F_READ | ATA_F_POLL;
- xa->timeout = hz;
+ xa->timeout = 1000;
status = ahci_ata_cmd(xa);
error = (status == ATA_COMPLETE && xa->state == ATA_S_COMPLETE) ?
* ATA commands
*/
+#define ATA_C_SATA_FEATURE_ENA 0x10
#define ATA_C_READDMA_EXT 0x25
#define ATA_C_READ_LOG_EXT 0x2f
#define ATA_C_WRITEDMA_EXT 0x35
#define ATA_C_READ_FPDMA 0x60
#define ATA_C_WRITE_FPDMA 0x61
+#define ATA_C_SATA_FEATURE_DIS 0x90
#define ATA_C_PACKET 0xa0
#define ATA_C_ATAPI_IDENTIFY 0xa1
#define ATA_C_READDMA 0xc8
#define ATA_C_SEC_FREEZE_LOCK 0xf5
/*
+ * ATA SATA FEATURES subcommands
+ */
+#define ATA_SATAFT_NONZDMA 0x01 /* DMA non-zero buffer offset */
+#define ATA_SATAFT_DMAAAOPT 0x02 /* DMA AA optimization */
+#define ATA_SATAFT_DEVIPS 0x03 /* Device-initiated pwr state*/
+#define ATA_SATAFT_INORDER 0x04 /* in-order data delivery */
+#define ATA_SATAFT_ASYNCNOTIFY 0x05 /* Async notification */
+
+/*
* ATA SET FEATURES subcommands
*/
#define ATA_SF_WRITECACHE_EN 0x02
#define ATA_PORT_F_WCACHE (1 << 0)
#define ATA_PORT_F_RAHEAD (1 << 1)
#define ATA_PORT_F_FRZLCK (1 << 2)
+#define ATA_PORT_F_RESCAN (1 << 3) /* re-check on bus scan */
int at_probe;
-#define ATA_PROBE_NEED_HARD_RESET 0
-#define ATA_PROBE_NEED_SOFT_RESET 1
-#define ATA_PROBE_NEED_IDENT 2
-#define ATA_PROBE_GOOD 3
+#define ATA_PROBE_NEED_INIT 0
+#define ATA_PROBE_NEED_HARD_RESET 1
+#define ATA_PROBE_NEED_SOFT_RESET 2
+#define ATA_PROBE_NEED_IDENT 3
+#define ATA_PROBE_GOOD 4
#define ATA_PROBE_FAILED 7
int at_ncqdepth;
u_int64_t at_capacity; /* only if ATA_PORT_T_DISK */
#define ATA_F_PACKET (1<<5)
#define ATA_F_NCQ (1<<6)
#define ATA_F_TIMEOUT_RUNNING (1<<7)
+#define ATA_F_TIMEOUT_DESIRED (1<<8)
#define ATA_FMT_FLAGS "\020" "\010TRUNNING" \
"\007NCQ" "\006PACKET" \
"\005PIO" "\004POLL" "\003NOWAIT" \