From 3c6bae9d0f544ed56e5fce6767e0792cc45a7a17 Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Thu, 18 Jun 2009 00:52:45 -0700 Subject: [PATCH] SILI - Fix port re-insertion probe problems when coming out of DET_INIT * When a port is pulled we enter into a listen state where we program SCTL to the DET_INIT state to send a continuous stream of COMRESETs. When a port is plugged back in we clear the DET_INIT state. However, we were not waiting nearly long enough for the PHY to renegotiate! It can take up to 4 seconds! Add a polling loop to wait for the PHY to renegotiate before we start the DEVRESET sequence. If we start the sequence too early the PHY will still be unstable and the DEVRESET sequence or something following it will likely fail with a fatal error interrupt (usually error code 4). * Reduce the physical port insertion delay to 7 seconds. Leave the PM port insertion delay at 10 seconds. --- sys/dev/disk/sili/sili.c | 24 +++++++++++++++++++++--- sys/dev/disk/sili/sili_pm.c | 20 ++++++++++++++------ 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/sys/dev/disk/sili/sili.c b/sys/dev/disk/sili/sili.c index 9e0213c420..f824341dff 100644 --- a/sys/dev/disk/sili/sili.c +++ b/sys/dev/disk/sili/sili.c @@ -498,9 +498,9 @@ sili_port_state_machine(struct sili_port *ap, int initial) */ { if (initial == 0 && ap->ap_probe <= ATA_PROBE_NEED_HARD_RESET) { - kprintf("%s: Waiting 10 seconds on insertion\n", + kprintf("%s: Waiting 7 seconds on insertion\n", PORTNAME(ap)); - sili_os_sleep(10000); + sili_os_sleep(7000); initial = 1; } if (ap->ap_probe == ATA_PROBE_NEED_INIT) @@ -874,7 +874,9 @@ sili_port_hardreset(struct sili_port *ap) /* * Set SCTL up for any speed restrictions before issuing the - * device reset. + * device reset. This may also take us out of an INIT state + * (if we were previously in a continuous reset state from + * sili_port_listen()). */ data = SILI_PREG_SCTL_SPM_NONE | SILI_PREG_SCTL_IPM_NONE | @@ -887,6 +889,22 @@ sili_port_hardreset(struct sili_port *ap) sili_pwrite(ap, SILI_PREG_SCTL, data); /* + * The transition from a continuous COMRESET state from + * sili_port_listen() back to device detect can take a + * few seconds. It's quite non-deterministic. Most of + * the time it takes far less. Use a polling loop to + * wait. + */ + loop = 4000; + while (loop > 0) { + data = sili_pread(ap, SILI_PREG_SSTS); + if (data & SILI_PREG_SSTS_DET) + break; + loop -= sili_os_softsleep(); + } + sili_os_sleep(100); + + /* * Issue Device Reset, give the phy a little time to settle down. * * NOTE: Unlike Port Reset, the port ready signal will not diff --git a/sys/dev/disk/sili/sili_pm.c b/sys/dev/disk/sili/sili_pm.c index 6d0055d530..45883bfe85 100644 --- a/sys/dev/disk/sili/sili_pm.c +++ b/sys/dev/disk/sili/sili_pm.c @@ -288,11 +288,16 @@ sili_pm_hardreset(struct sili_port *ap, int target, int hard) /* * Flush any status, then clear DET to initiate negotiation. + * + * We need to give the phy layer a bit of time to settle down + * or we may catch a detection glitch instead of the actual + * device detect. */ sili_pm_write(ap, target, SATA_PMREG_SERR, -1); data = SATA_PM_SCTL_IPM_DISABLED | SATA_PM_SCTL_DET_NONE; if (sili_pm_write(ap, target, SATA_PMREG_SCTL, data)) goto err; + sili_os_sleep(10); /* * Try to determine if there is a device on the port. @@ -338,15 +343,18 @@ sili_pm_hardreset(struct sili_port *ap, int target, int hard) } /* - * Device detected + * Device detected. + * + * Wait 200ms to give the device time to send its first D2H FIS. + * If we do not wait long enough our softreset sequence can collide + * with the end of the device's reset sequence. + * + * XXX how do we poll that particular target's BSY status via the + * PM? */ kprintf("%s.%d: Device detected data=%08x\n", PORTNAME(ap), target, data); - /* - * Clear SERR on the target so we get a new NOTIFY event if a hot-plug - * or hot-unplug occurs. - */ - sili_os_sleep(100); + sili_os_sleep(200); error = 0; err: -- 2.11.4.GIT