Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 25 May 2011 13:55:49 +0000 (UTC)
From:      Alexander Motin <mav@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r222285 - in head/sys/dev: ahci mvs siis
Message-ID:  <201105251355.p4PDtnDw017474@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: mav
Date: Wed May 25 13:55:49 2011
New Revision: 222285
URL: http://svn.freebsd.org/changeset/base/222285

Log:
  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.

Modified:
  head/sys/dev/ahci/ahci.c
  head/sys/dev/mvs/mvs.c
  head/sys/dev/siis/siis.c

Modified: head/sys/dev/ahci/ahci.c
==============================================================================
--- head/sys/dev/ahci/ahci.c	Wed May 25 13:30:44 2011	(r222284)
+++ head/sys/dev/ahci/ahci.c	Wed May 25 13:55:49 2011	(r222285)
@@ -1835,9 +1835,11 @@ ahci_execute_transaction(struct ahci_slo
 			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_slo
 				}
 			} 
 		}
-		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 *s
 		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 *s
 			/* 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 *s
 	} 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)) {

Modified: head/sys/dev/mvs/mvs.c
==============================================================================
--- head/sys/dev/mvs/mvs.c	Wed May 25 13:30:44 2011	(r222284)
+++ head/sys/dev/mvs/mvs.c	Wed May 25 13:55:49 2011	(r222285)
@@ -1738,13 +1738,6 @@ mvs_end_transaction(struct mvs_slot *slo
 		ch->numhslots++;
 	} else
 		xpt_done(ccb);
-	/* Unfreeze frozen command. */
-	if (ch->frozen && !mvs_check_collision(dev, ch->frozen)) {
-		union ccb *fccb = ch->frozen;
-		ch->frozen = NULL;
-		mvs_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. */
@@ -1764,6 +1757,13 @@ mvs_end_transaction(struct mvs_slot *slo
 	} else if ((ch->rslots & ~ch->toslots) == 0 &&
 	    et != MVS_ERR_TIMEOUT)
 		mvs_rearm_timeout(dev);
+	/* Unfreeze frozen command. */
+	if (ch->frozen && !mvs_check_collision(dev, ch->frozen)) {
+		union ccb *fccb = ch->frozen;
+		ch->frozen = NULL;
+		mvs_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)) {
@@ -2080,7 +2080,8 @@ mvs_softreset(device_t dev, union ccb *c
 {
 	struct mvs_channel *ch = device_get_softc(dev);
 	int port = ccb->ccb_h.target_id & 0x0f;
-	int i;
+	int i, stuck;
+	uint8_t status;
 
 	mvs_set_edma_mode(dev, MVS_EDMA_OFF);
 	ATA_OUTB(ch->r_mem, SATA_SATAICTL, port << SATA_SATAICTL_PMPTX_SHIFT);
@@ -2089,12 +2090,35 @@ mvs_softreset(device_t dev, union ccb *c
 	ATA_OUTB(ch->r_mem, ATA_CONTROL, 0);
 	ccb->ccb_h.status &= ~CAM_STATUS_MASK;
 	/* Wait for clearing busy status. */
-	if ((i = mvs_wait(dev, 0, ATA_S_BUSY | ATA_S_DRQ, ccb->ccb_h.timeout)) < 0) {
+	if ((i = mvs_wait(dev, 0, ATA_S_BUSY, ccb->ccb_h.timeout)) < 0) {
 		ccb->ccb_h.status |= CAM_CMD_TIMEOUT;
+		stuck = 1;
 	} else {
-		ccb->ccb_h.status |= CAM_REQ_CMP;
+		status = mvs_getstatus(dev, 0);
+		if (status & ATA_S_ERROR)
+			ccb->ccb_h.status |= CAM_ATA_STATUS_ERROR;
+		else
+			ccb->ccb_h.status |= CAM_REQ_CMP;
+		if (status & ATA_S_DRQ)
+			stuck = 1;
+		else
+			stuck = 0;
 	}
 	mvs_tfd_read(dev, ccb);
+
+	/*
+	 * XXX: If some device on PMP failed to soft-reset,
+	 * try to recover by sending dummy soft-reset to PMP.
+	 */
+	if (stuck && ch->pm_present && port != 15) {
+		ATA_OUTB(ch->r_mem, SATA_SATAICTL,
+		    15 << SATA_SATAICTL_PMPTX_SHIFT);
+		ATA_OUTB(ch->r_mem, ATA_CONTROL, ATA_A_RESET);
+		DELAY(10000);
+		ATA_OUTB(ch->r_mem, ATA_CONTROL, 0);
+		mvs_wait(dev, 0, ATA_S_BUSY | ATA_S_DRQ, ccb->ccb_h.timeout);
+	}
+
 	xpt_done(ccb);
 }
 

Modified: head/sys/dev/siis/siis.c
==============================================================================
--- head/sys/dev/siis/siis.c	Wed May 25 13:30:44 2011	(r222284)
+++ head/sys/dev/siis/siis.c	Wed May 25 13:55:49 2011	(r222285)
@@ -1178,11 +1178,22 @@ siis_timeout(struct siis_slot *slot)
 {
 	device_t dev = slot->dev;
 	struct siis_channel *ch = device_get_softc(dev);
+	union ccb *ccb = slot->ccb;
 
 	mtx_assert(&ch->mtx, MA_OWNED);
 	/* Check for stale timeout. */
 	if (slot->state < SIIS_SLOT_RUNNING)
 		return;
+
+	/* Handle soft-reset timeouts without doing hard-reset. */
+	if ((ccb->ccb_h.func_code == XPT_ATA_IO) &&
+	    (ccb->ataio.cmd.flags & CAM_ATAIO_CONTROL) &&
+	    (ccb->ataio.cmd.control & ATA_A_RESET)) {
+		xpt_freeze_simq(ch->sim, ch->numrslots);
+		siis_end_transaction(slot, SIIS_ERR_TFE);
+		return;
+	}
+
 	device_printf(dev, "Timeout on slot %d\n", slot->slot);
 	device_printf(dev, "%s is %08x ss %08x rs %08x es %08x sts %08x serr %08x\n",
 	    __func__, ATA_INL(ch->r_mem, SIIS_P_IS),
@@ -1331,13 +1342,6 @@ siis_end_transaction(struct siis_slot *s
 		ch->numhslots++;
 	} else
 		xpt_done(ccb);
-	/* Unfreeze frozen command. */
-	if (ch->frozen && !siis_check_collision(dev, ch->frozen)) {
-		union ccb *fccb = ch->frozen;
-		ch->frozen = NULL;
-		siis_begin_transaction(dev, fccb);
-		xpt_release_simq(ch->sim, TRUE);
-	}
 	/* If we have no other active commands, ... */
 	if (ch->rslots == 0) {
 		/* if there were timeouts or fatal error - reset port. */
@@ -1355,6 +1359,13 @@ siis_end_transaction(struct siis_slot *s
 	} else if ((ch->rslots & ~ch->toslots) == 0 &&
 	    et != SIIS_ERR_TIMEOUT)
 		siis_rearm_timeout(dev);
+	/* Unfreeze frozen command. */
+	if (ch->frozen && !siis_check_collision(dev, ch->frozen)) {
+		union ccb *fccb = ch->frozen;
+		ch->frozen = NULL;
+		siis_begin_transaction(dev, fccb);
+		xpt_release_simq(ch->sim, TRUE);
+	}
 }
 
 static void



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201105251355.p4PDtnDw017474>