[PATCH 10/14] libata-hp-prep: implement sata_phy_debounce()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



With hotplug, PHY always needs to be debounced before a reset as any
reset might find new devices.  Extract PHY waiting code from
sata_phy_resume() and extend it to include SStatus debouncing.  Note
that sata_phy_debounce() is superset of what used to be done inside
sata_phy_resume().

Two sets of debounce timings are defined and an argument is added to
sata_phy_resume() to select between the two.

---

 drivers/scsi/libata-core.c |  103 ++++++++++++++++++++++++++++++++++++++------
 include/linux/libata.h     |   13 ++++++
 2 files changed, 101 insertions(+), 15 deletions(-)

31e0810e495194fb383ea61ddd059f22449352b0
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index 1861373..636f044 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -2417,10 +2417,83 @@ err_out:
 	DPRINTK("EXIT\n");
 }
 
-static int sata_phy_resume(struct ata_port *ap)
+/**
+ *	sata_phy_debounce - debounce SATA phy status
+ *	@ap: ATA port to debounce SATA phy status for
+ *	@interval_msec: polling interval in millisecs
+ *	@duration_msec: debounce duration in millisecs
+ *	@timeout_msec: timeout in millisecs
+ *
+ *	Make sure SStatus of @ap reaches stable state, determined by
+ *	holding the same value where DET is not 1 for @duration_msec
+ *	polled every @interval_msec, before @timeout_msec.  Timeout
+ *	constraints the beginning of the stable state.  Because, after
+ *	hot unplugging, DET gets stuck at 1 on some controllers, this
+ *	functions waits until timeout then returns 0 if DET is stable
+ *	at 1.
+ *
+ *	LOCKING:
+ *	Kernel thread context (may sleep)
+ *
+ *	RETURNS:
+ *	0 on success, -errno on failure.
+ */
+int sata_phy_debounce(struct ata_port *ap, unsigned long interval_msec,
+		      unsigned long duration_msec, unsigned long timeout_msec)
+{
+	unsigned long duration = duration_msec * HZ / 1000;
+	unsigned long timeout = jiffies + timeout_msec * HZ / 1000;
+	unsigned long last_jiffies;
+	u32 last, cur;
+	int rc;
+
+	if ((rc = ata_scr_read(ap, SCR_STATUS, &cur)))
+		return rc;
+	cur &= 0xf;
+
+	last = cur;
+	last_jiffies = jiffies;
+
+	while (1) {
+		msleep(interval_msec);
+		if ((rc = ata_scr_read(ap, SCR_STATUS, &cur)))
+			return rc;
+		cur &= 0xf;
+
+		/* DET stable? */
+		if (cur == last) {
+			if (cur == 1 && time_before(jiffies, timeout))
+				continue;
+			if (time_after(jiffies, last_jiffies + duration))
+				return 0;
+			continue;
+		}
+
+		/* unstable, start over */
+		last = cur;
+		last_jiffies = jiffies;
+
+		/* check timeout */
+		if (time_after(jiffies, timeout))
+			return -EBUSY;
+	}
+}
+
+/**
+ *	sata_phy_resume - resume SATA phy
+ *	@ap: ATA port to resume SATA phy for
+ *
+ *	Resume SATA phy of @ap and debounce it.
+ *
+ *	LOCKING:
+ *	Kernel thread context (may sleep)
+ *
+ *	RETURNS:
+ *	0 on success, -errno on failure.
+ */
+int sata_phy_resume(struct ata_port *ap, int quick)
 {
-	unsigned long timeout = jiffies + (HZ * 5);
-	u32 scontrol, sstatus;
+	u32 scontrol;
 	int rc;
 
 	if ((rc = ata_scr_read(ap, SCR_CONTROL, &scontrol)))
@@ -2431,16 +2504,14 @@ static int sata_phy_resume(struct ata_po
 	if ((rc = ata_scr_write(ap, SCR_CONTROL, scontrol)))
 		return rc;
 
-	/* Wait for phy to become ready, if necessary. */
-	do {
-		msleep(200);
-		if ((rc = ata_scr_read(ap, SCR_STATUS, &sstatus)))
-			return rc;
-		if ((sstatus & 0xf) != 1)
-			return 0;
-	} while (time_before(jiffies, timeout));
-
-	return -EBUSY;
+	if (quick)
+		return sata_phy_debounce(ap, ATA_DEBOUNCE_QUICK_INTERVAL,
+					 ATA_DEBOUNCE_QUICK_DURATION,
+					 ATA_DEBOUNCE_QUICK_TIMEOUT);
+	else
+		return sata_phy_debounce(ap, ATA_DEBOUNCE_INTERVAL,
+					 ATA_DEBOUNCE_DURATION,
+					 ATA_DEBOUNCE_TIMEOUT);
 }
 
 /**
@@ -2459,7 +2530,7 @@ static int sata_phy_resume(struct ata_po
 void ata_std_probeinit(struct ata_port *ap)
 {
 	/* resume link */
-	sata_phy_resume(ap);
+	sata_phy_resume(ap, 1);
 
 	/* wait for device */
 	if (ata_port_online(ap))
@@ -2575,7 +2646,7 @@ int sata_std_hardreset(struct ata_port *
 	msleep(1);
 
 	/* bring phy back */
-	sata_phy_resume(ap);
+	sata_phy_resume(ap, 0);
 
 	/* TODO: phy layer with polling, timeouts, etc. */
 	if (ata_port_offline(ap)) {
@@ -5722,6 +5793,8 @@ EXPORT_SYMBOL_GPL(ata_set_sata_spd);
 EXPORT_SYMBOL_GPL(sata_phy_reset);
 EXPORT_SYMBOL_GPL(__sata_phy_reset);
 EXPORT_SYMBOL_GPL(ata_bus_reset);
+EXPORT_SYMBOL_GPL(sata_phy_debounce);
+EXPORT_SYMBOL_GPL(sata_phy_resume);
 EXPORT_SYMBOL_GPL(ata_std_probeinit);
 EXPORT_SYMBOL_GPL(ata_std_softreset);
 EXPORT_SYMBOL_GPL(sata_std_hardreset);
diff --git a/include/linux/libata.h b/include/linux/libata.h
index f4a544e..5c843a8 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -255,6 +255,15 @@ enum {
 	ATA_PROBE_MAX_TRIES	= 3,
 	ATA_EH_RESET_TRIES	= 3,
 	ATA_EH_DEV_TRIES	= 3,
+
+	/* timing constants in millisecs */
+	ATA_DEBOUNCE_QUICK_INTERVAL	= 5,
+	ATA_DEBOUNCE_QUICK_DURATION	= 100,
+	ATA_DEBOUNCE_QUICK_TIMEOUT	= 5000,
+
+	ATA_DEBOUNCE_INTERVAL	= 10,
+	ATA_DEBOUNCE_DURATION	= 500,
+	ATA_DEBOUNCE_TIMEOUT	= 10000,
 };
 
 enum hsm_task_states {
@@ -600,6 +609,10 @@ extern void __sata_phy_reset(struct ata_
 extern void sata_phy_reset(struct ata_port *ap);
 extern void ata_bus_reset(struct ata_port *ap);
 extern int ata_set_sata_spd(struct ata_port *ap);
+extern int sata_phy_debounce(struct ata_port *ap, unsigned long interval_msec,
+			     unsigned long duration_msec,
+			     unsigned long timeout_msec);
+extern int sata_phy_resume(struct ata_port *ap, int quick);
 extern int ata_drive_probe_reset(struct ata_port *ap,
 			ata_probeinit_fn_t probeinit,
 			ata_reset_fn_t softreset, ata_reset_fn_t hardreset,
-- 
1.2.4


-
: send the line "unsubscribe linux-ide" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Filesystems]     [Linux SCSI]     [Linux RAID]     [Git]     [Kernel Newbies]     [Linux Newbie]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Samba]     [Device Mapper]

  Powered by Linux