/* * Stop the OS-specific port helper thread and kill the per-port lock. */ void sili_os_stop_port(struct sili_port *ap) { if (ap->ap_thread) { sili_os_signal_port_thread(ap, AP_SIGF_STOP); sili_os_sleep(10); if (ap->ap_thread) { kprintf("%s: Waiting for thread to terminate\n", PORTNAME(ap)); while (ap->ap_thread) sili_os_sleep(100); kprintf("%s: thread terminated\n", PORTNAME(ap)); } } lockuninit(&ap->ap_lock); }
/* * Do a COMRESET sequence on the target behind a port multiplier. * * If hard is 2 we also cycle the phy on the target. * * This must be done prior to any softreset or probe attempts on * targets behind the port multiplier. * * Returns 0 on success or an error. */ int sili_pm_hardreset(struct sili_port *ap, int target, int hard) { struct ata_port *at; u_int32_t data; int loop; int error = EIO; at = &ap->ap_ata[target]; /* * Ensure that no other commands are pending. Our HW reset of * the PM target can skewer the port overall! */ sili_exclusive_access(ap); /* * Turn off power management and kill the phy on the target * if requested. Hold state for 10ms. */ data = SATA_PM_SCTL_IPM_DISABLED; #if 0 if (hard == 2) data |= SATA_PM_SCTL_DET_DISABLE; #endif if (sili_pm_write(ap, target, SATA_PMREG_SERR, -1)) goto err; if (sili_pm_write(ap, target, SATA_PMREG_SCTL, data)) goto err; sili_os_sleep(10); /* * Start transmitting COMRESET. COMRESET must be sent for at * least 1ms. * * It takes about 100ms for the DET logic to settle down, * from trial and error testing. If this is too short * the softreset code will fail. * * It is very important to allow the logic to settle before * we issue any additional commands or the target will interfere * with our PM commands. */ at->at_probe = ATA_PROBE_FAILED; at->at_type = ATA_PORT_T_NONE; data = SATA_PM_SCTL_IPM_DISABLED | SATA_PM_SCTL_DET_INIT; if (SiliForceGen1 & (1 << ap->ap_num)) { kprintf("%s.%d: Force 1.5GBits\n", PORTNAME(ap), target); data |= SATA_PM_SCTL_SPD_GEN1; } else { data |= SATA_PM_SCTL_SPD_ANY; } if (sili_pm_write(ap, target, SATA_PMREG_SCTL, data)) goto err; sili_os_sleep(100); if (sili_pm_phy_status(ap, target, &data)) { kprintf("%s: (A)Cannot clear phy status\n", ATANAME(ap ,at)); } /* * Flush any status, then clear DET to initiate negotiation. * * It is very important to allow the negotiation to settle before * we issue any additional commands or the target will interfere * with our PM commands. */ 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(100); /* * Try to determine if there is a device on the port. * * Give the device 3/10 second to at least be detected. * If we fail clear any pending status since we may have * cycled the phy and probably caused another PRCS interrupt. */ for (loop = 3; loop; --loop) { if (sili_pm_read(ap, target, SATA_PMREG_SSTS, &data)) goto err; if (data & SATA_PM_SSTS_DET) break; sili_os_sleep(100); } if (loop == 0) { kprintf("%s.%d: Port appears to be unplugged\n", PORTNAME(ap), target); error = ENODEV; goto err; } /* * There is something on the port. Give the device 3 seconds * to fully negotiate. */ for (loop = 30; loop; --loop) { if (sili_pm_read(ap, target, SATA_PMREG_SSTS, &data)) goto err; if ((data & SATA_PM_SSTS_DET) == SATA_PM_SSTS_DET_DEV) break; sili_os_sleep(100); } /* * Device not detected */ if (loop == 0) { kprintf("%s: Device may be powered down\n", PORTNAME(ap)); error = ENODEV; goto err; } /* * 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 and brick the port. * Some devices may need longer and we handle those cases in the * pm softreset code. * * XXX Looks like we have to wait a lot longer. If the Sili chip's * softreset fails due to a collision with the D2H FIS or the * unbusying it bricks the port. * * XXX how do we poll that particular target's BSY status via the * PM? */ kprintf("%s.%d: PM Device detected ssts=%08x\n", PORTNAME(ap), target, data); sili_os_sleep(5000); error = 0; err: at->at_probe = error ? ATA_PROBE_FAILED : ATA_PROBE_NEED_SOFT_RESET; return (error); }