aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/ahci/ahci.c
diff options
context:
space:
mode:
authorAlexander Motin <mav@FreeBSD.org>2011-05-25 13:55:49 +0000
committerAlexander Motin <mav@FreeBSD.org>2011-05-25 13:55:49 +0000
commit08c8fde0079c7b19d7e13ad65744faf56454e193 (patch)
treed7a62b5c971d72bfadd01b2a8d309adbe7494852 /sys/dev/ahci/ahci.c
parentceef8f2477ce6f0e311c60ca7ecd30fb31f3e3f3 (diff)
downloadsrc-08c8fde0079c7b19d7e13ad65744faf56454e193.tar.gz
src-08c8fde0079c7b19d7e13ad65744faf56454e193.zip
According to SATA specification, when Serial ATA Enclosure Management Bridge
(SEMB) is unable to communicate to Storage Enclosure Processor (SEP), in response to hard and soft resets it should among other things return value 0x7F in Status register. The weird side is that it means DRQ bit set, which tells that reset request is not completed. It would be fine if SEMB was the only device on port. But if SEMB connected to PMP or built into it, it may block access to other devices sharing same SATA port. Make some tunings/fixes to soft-reset handling to workaround the issue: - ahci(4): request CLO on the port after soft reset to ignore DRQ bit; - siis(4): gracefully reinitialize port after soft reset timeout (hardware doesn't detect reset request completion in this case); - mvs(4): if PMP is used, send dummy soft-reset to the PMP port to make it clear DRQ bit for us. For now this makes quirks in ata_pmp.c, hiding SEMB ports of SiI3726/SiI4726 PMPs, less important. Further, if hardware permit, I hope to implement real SEMB support.
Notes
Notes: svn path=/head/; revision=222285
Diffstat (limited to 'sys/dev/ahci/ahci.c')
-rw-r--r--sys/dev/ahci/ahci.c25
1 files changed, 13 insertions, 12 deletions
diff --git a/sys/dev/ahci/ahci.c b/sys/dev/ahci/ahci.c
index 2a064923cf3c..5db3a04a36ee 100644
--- a/sys/dev/ahci/ahci.c
+++ b/sys/dev/ahci/ahci.c
@@ -1835,9 +1835,11 @@ ahci_execute_transaction(struct ahci_slot *slot)
if (!(ATA_INL(ch->r_mem, AHCI_P_CI) & (1 << slot->slot)))
break;
if (ATA_INL(ch->r_mem, AHCI_P_TFD) & ATA_S_ERROR) {
+#if 0
device_printf(ch->dev,
"Poll error on slot %d, TFD: %04x\n",
slot->slot, ATA_INL(ch->r_mem, AHCI_P_TFD));
+#endif
et = AHCI_ERR_TFE;
break;
}
@@ -1877,14 +1879,12 @@ ahci_execute_transaction(struct ahci_slot *slot)
}
}
}
- ahci_end_transaction(slot, et);
/* Kick controller into sane state and enable FBS. */
if ((ccb->ccb_h.func_code == XPT_ATA_IO) &&
(ccb->ataio.cmd.flags & CAM_ATAIO_CONTROL) &&
- (ccb->ataio.cmd.control & ATA_A_RESET) == 0) {
- ahci_stop(ch->dev);
- ahci_start(ch->dev, 1);
- }
+ (ccb->ataio.cmd.control & ATA_A_RESET) == 0)
+ ch->eslots |= (1 << slot->slot);
+ ahci_end_transaction(slot, et);
return;
}
/* Start command execution timeout */
@@ -2169,13 +2169,6 @@ ahci_end_transaction(struct ahci_slot *slot, enum ahci_err_type et)
ch->numhslots++;
} else
xpt_done(ccb);
- /* Unfreeze frozen command. */
- if (ch->frozen && !ahci_check_collision(dev, ch->frozen)) {
- union ccb *fccb = ch->frozen;
- ch->frozen = NULL;
- ahci_begin_transaction(dev, fccb);
- xpt_release_simq(ch->sim, TRUE);
- }
/* If we have no other active commands, ... */
if (ch->rslots == 0) {
/* if there was fatal error - reset port. */
@@ -2185,6 +2178,7 @@ ahci_end_transaction(struct ahci_slot *slot, enum ahci_err_type et)
/* if we have slots in error, we can reinit port. */
if (ch->eslots != 0) {
ahci_stop(dev);
+ ahci_clo(dev);
ahci_start(dev, 1);
}
/* if there commands on hold, we can do READ LOG. */
@@ -2195,6 +2189,13 @@ ahci_end_transaction(struct ahci_slot *slot, enum ahci_err_type et)
} else if ((ch->rslots & ~ch->toslots) == 0 &&
et != AHCI_ERR_TIMEOUT)
ahci_rearm_timeout(dev);
+ /* Unfreeze frozen command. */
+ if (ch->frozen && !ahci_check_collision(dev, ch->frozen)) {
+ union ccb *fccb = ch->frozen;
+ ch->frozen = NULL;
+ ahci_begin_transaction(dev, fccb);
+ xpt_release_simq(ch->sim, TRUE);
+ }
/* Start PM timer. */
if (ch->numrslots == 0 && ch->pm_level > 3 &&
(ch->curr[ch->pm_present ? 15 : 0].caps & CTS_SATA_CAPS_D_PMREQ)) {