Ejemplo n.º 1
0
/*
 * 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);
}
Ejemplo n.º 2
0
/*
 * 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);
}