Пример #1
0
/*
 * Check that a target is still good.
 */
void
ahci_pm_check_good(struct ahci_port *ap, int target)
{
	struct ata_port *at;
	u_int32_t data;

	/*
	 * It looks like we might have to read the EINFO register
	 * to allow the PM to generate a new event.
	 */
	if (ahci_pm_read(ap, 15, SATA_PMREG_EINFO, &data)) {
		kprintf("%s: Port multiplier EINFO could not be read\n",
			PORTNAME(ap));
	}

	if (ahci_pm_write(ap, target, SATA_PMREG_SERR, -1)) {
		kprintf("%s: Port multiplier: SERR could not be cleared\n",
			PORTNAME(ap));
	}

	if (target == CAM_TARGET_WILDCARD || target >= ap->ap_pmcount)
		return;
	at = ap->ap_ata[target];

	/*
	 * If the device needs an init or hard reset also make sure the
	 * PHY is turned on.
	 */
	if (at->at_probe <= ATA_PROBE_NEED_HARD_RESET) {
		/*kprintf("%s DOHARD\n", ATANAME(ap, at));*/
		ahci_pm_hardreset(ap, target, 1);
	}

	/*
	 * Read the detect status
	 */
	if (ahci_pm_read(ap, target, SATA_PMREG_SSTS, &data)) {
		kprintf("%s: Unable to access PM SSTS register target %d\n",
			PORTNAME(ap), target);
		return;
	}
	if ((data & AHCI_PREG_SSTS_DET) != AHCI_PREG_SSTS_DET_DEV) {
		/*kprintf("%s: DETECT %08x\n", ATANAME(ap, at), data);*/
		if (at->at_probe != ATA_PROBE_FAILED) {
			at->at_probe = ATA_PROBE_FAILED;
			at->at_type = ATA_PORT_T_NONE;
			at->at_features |= ATA_PORT_F_RESCAN;
			kprintf("%s: HOTPLUG (PM) - Device removed\n",
				ATANAME(ap, at));
		}
	} else {
		if (at->at_probe == ATA_PROBE_FAILED) {
			at->at_probe = ATA_PROBE_NEED_HARD_RESET;
			at->at_features |= ATA_PORT_F_RESCAN;
			kprintf("%s: HOTPLUG (PM) - Device inserted\n",
				ATANAME(ap, at));
		}
	}
}
Пример #2
0
/*
 * Setting the transfer mode is irrelevant for the SATA transport
 * but some (atapi) devices seem to need it anyway.  In addition
 * if we are running through a SATA->PATA converter for some reason
 * beyond my comprehension we might have to set the mode.
 *
 * We only support DMA modes for SATA attached devices, so don't bother
 * with legacy modes.
 */
static int
ahci_set_xfer(struct ahci_port *ap, struct ata_port *atx)
{
	struct ata_port *at;
	struct ata_xfer	*xa;
	u_int16_t mode;
	u_int16_t mask;

	at = atx ? atx : ap->ap_ata[0];

	/*
	 * Figure out the supported UDMA mode.  Ignore other legacy modes.
	 */
	mask = le16toh(at->at_identify.ultradma);
	if ((mask & 0xFF) == 0 || mask == 0xFFFF)
		return(0);
	mask &= 0xFF;
	mode = 0x4F;
	while ((mask & 0x8000) == 0) {
		mask <<= 1;
		--mode;
	}

	/*
	 * SATA atapi devices often still report a dma mode, even though
	 * it is irrelevant for SATA transport.  It is also possible that
	 * we are running through a SATA->PATA converter and seeing the
	 * PATA dma mode.
	 *
	 * In this case the device may require a (dummy) SETXFER to be
	 * sent before it will work properly.
	 */
	xa = ahci_ata_get_xfer(ap, atx);
	xa->complete = ahci_ata_dummy_done;
	xa->fis->command = ATA_C_SET_FEATURES;
	xa->fis->features = ATA_SF_SETXFER;
	xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
	xa->fis->sector_count = mode;
	xa->flags = ATA_F_PIO | ATA_F_POLL;
	xa->timeout = 1000;
	xa->datalen = 0;
	if (ahci_ata_cmd(xa) != ATA_S_COMPLETE) {
		kprintf("%s: Unable to set dummy xfer mode \n",
			ATANAME(ap, atx));
	} else if (bootverbose) {
		kprintf("%s: Set dummy xfer mode to %02x\n",
			ATANAME(ap, atx), mode);
	}
	ahci_ata_put_xfer(xa);
	return(0);
}
Пример #3
0
/*
 * DISK-specific probe after initial ident
 */
static int
ahci_cam_probe_disk(struct ahci_port *ap, struct ata_port *atx)
{
	struct ata_port *at;
	struct ata_xfer	*xa;

	at = atx ? atx : ap->ap_ata[0];

	/*
	 * Set dummy xfer mode
	 */
	ahci_set_xfer(ap, atx);

	/*
	 * Enable write cache if supported
	 *
	 * NOTE: "WD My Book" external disk devices have a very poor
	 *	 daughter board between the the ESATA and the HD.  Sending
	 *	 any ATA_C_SET_FEATURES commands will break the hardware port
	 *	 with a fatal protocol error.  However, this device also
	 *	 indicates that WRITECACHE is already on and READAHEAD is
	 *	 not supported so we avoid the issue.
	 */
	if ((at->at_identify.cmdset82 & ATA_IDENTIFY_WRITECACHE) &&
	    (at->at_identify.features85 & ATA_IDENTIFY_WRITECACHE) == 0) {
		xa = ahci_ata_get_xfer(ap, atx);
		xa->complete = ahci_ata_dummy_done;
		xa->fis->command = ATA_C_SET_FEATURES;
		xa->fis->features = ATA_SF_WRITECACHE_EN;
		/* xa->fis->features = ATA_SF_LOOKAHEAD_EN; */
		xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
		xa->fis->device = 0;
		xa->flags = ATA_F_PIO | ATA_F_POLL;
		xa->timeout = 1000;
		xa->datalen = 0;
		if (ahci_ata_cmd(xa) == ATA_S_COMPLETE)
			at->at_features |= ATA_PORT_F_WCACHE;
		else
			kprintf("%s: Unable to enable write-caching\n",
				ATANAME(ap, atx));
		ahci_ata_put_xfer(xa);
	}

	/*
	 * Enable readahead if supported
	 */
	if ((at->at_identify.cmdset82 & ATA_IDENTIFY_LOOKAHEAD) &&
	    (at->at_identify.features85 & ATA_IDENTIFY_LOOKAHEAD) == 0) {
		xa = ahci_ata_get_xfer(ap, atx);
		xa->complete = ahci_ata_dummy_done;
		xa->fis->command = ATA_C_SET_FEATURES;
		xa->fis->features = ATA_SF_LOOKAHEAD_EN;
		xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
		xa->fis->device = 0;
		xa->flags = ATA_F_PIO | ATA_F_POLL;
		xa->timeout = 1000;
		xa->datalen = 0;
		if (ahci_ata_cmd(xa) == ATA_S_COMPLETE)
			at->at_features |= ATA_PORT_F_RAHEAD;
		else
			kprintf("%s: Unable to enable read-ahead\n",
				ATANAME(ap, atx));
		ahci_ata_put_xfer(xa);
	}

	/*
	 * FREEZE LOCK the device so malicious users can't lock it on us.
	 * As there is no harm in issuing this to devices that don't
	 * support the security feature set we just send it, and don't bother
	 * checking if the device sends a command abort to tell us it doesn't
	 * support it
	 */
	if ((at->at_identify.cmdset82 & ATA_IDENTIFY_SECURITY) &&
	    (at->at_identify.securestatus & ATA_SECURE_FROZEN) == 0 &&
	    (AhciNoFeatures & (1 << ap->ap_num)) == 0) {
		xa = ahci_ata_get_xfer(ap, atx);
		xa->complete = ahci_ata_dummy_done;
		xa->fis->command = ATA_C_SEC_FREEZE_LOCK;
		xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;
		xa->flags = ATA_F_PIO | ATA_F_POLL;
		xa->timeout = 1000;
		xa->datalen = 0;
		if (ahci_ata_cmd(xa) == ATA_S_COMPLETE)
			at->at_features |= ATA_PORT_F_FRZLCK;
		else
			kprintf("%s: Unable to set security freeze\n",
				ATANAME(ap, atx));
		ahci_ata_put_xfer(xa);
	}

	return (0);
}
Пример #4
0
/*
 * Once the AHCI port has been attached we need to probe for a device or
 * devices on the port and setup various options.
 *
 * If at is NULL we are probing the direct-attached device on the port,
 * which may or may not be a port multiplier.
 */
int
ahci_cam_probe(struct ahci_port *ap, struct ata_port *atx)
{
	struct ata_port	*at;
	struct ata_xfer	*xa;
	u_int64_t	capacity;
	u_int64_t	capacity_bytes;
	int		model_len;
	int		firmware_len;
	int		serial_len;
	int		error;
	int		devncqdepth;
	int		i;
	const char	*model_id;
	const char	*firmware_id;
	const char	*serial_id;
	const char	*wcstr;
	const char	*rastr;
	const char	*scstr;
	const char	*type;

	error = EIO;

	/*
	 * Delayed CAM attachment for initial probe, sim may be NULL
	 */
	if (ap->ap_sim == NULL)
		return(0);

	/*
	 * A NULL atx indicates a probe of the directly connected device.
	 * A non-NULL atx indicates a device connected via a port multiplier.
	 * We need to preserve atx for calls to ahci_ata_get_xfer().
	 *
	 * at is always non-NULL.  For directly connected devices we supply
	 * an (at) pointing to target 0.
	 */
	if (atx == NULL) {
		at = ap->ap_ata[0];	/* direct attached - device 0 */
		if (ap->ap_type == ATA_PORT_T_PM) {
			kprintf("%s: Found Port Multiplier\n",
				ATANAME(ap, atx));
			return (0);
		}
		at->at_type = ap->ap_type;
	} else {
		at = atx;
		if (atx->at_type == ATA_PORT_T_PM) {
			kprintf("%s: Bogus device, reducing port count to %d\n",
				ATANAME(ap, atx), atx->at_target);
			if (ap->ap_pmcount > atx->at_target)
				ap->ap_pmcount = atx->at_target;
			goto err;
		}
	}
	if (ap->ap_type == ATA_PORT_T_NONE)
		goto err;
	if (at->at_type == ATA_PORT_T_NONE)
		goto err;

	/*
	 * Issue identify, saving the result
	 */
	xa = ahci_ata_get_xfer(ap, atx);
	xa->complete = ahci_ata_dummy_done;
	xa->data = &at->at_identify;
	xa->datalen = sizeof(at->at_identify);
	xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
	xa->fis->flags = ATA_H2D_FLAGS_CMD | at->at_target;

	switch(at->at_type) {
	case ATA_PORT_T_DISK:
		xa->fis->command = ATA_C_IDENTIFY;
		type = "DISK";
		break;
	case ATA_PORT_T_ATAPI:
		xa->fis->command = ATA_C_ATAPI_IDENTIFY;
		xa->flags |= ATA_F_AUTOSENSE;
		type = "ATAPI";
		break;
	default:
		xa->fis->command = ATA_C_ATAPI_IDENTIFY;
		type = "UNKNOWN(ATAPI?)";
		break;
	}
	xa->fis->features = 0;
	xa->fis->device = 0;
	xa->timeout = 1000;

	if (ahci_ata_cmd(xa) != ATA_S_COMPLETE) {
		kprintf("%s: Detected %s device but unable to IDENTIFY\n",
			ATANAME(ap, atx), type);
		ahci_ata_put_xfer(xa);
		goto err;
	}
	ahci_ata_put_xfer(xa);

	ata_fix_identify(&at->at_identify);

	/*
	 * Read capacity using SATA probe info.
	 */
	if (le16toh(at->at_identify.cmdset83) & 0x0400) {
		/* LBA48 feature set supported */
		capacity = 0;
		for (i = 3; i >= 0; --i) {
			capacity <<= 16;
			capacity +=
			    le16toh(at->at_identify.addrsecxt[i]);
		}
	} else {
		capacity = le16toh(at->at_identify.addrsec[1]);
		capacity <<= 16;
		capacity += le16toh(at->at_identify.addrsec[0]);
	}
	if (capacity == 0)
		capacity = 1024 * 1024 / 512;
	at->at_capacity = capacity;
	if (atx == NULL)
		ap->ap_probe = ATA_PROBE_GOOD;

	capacity_bytes = capacity * 512;

	/*
	 * Negotiate NCQ, throw away any ata_xfer's beyond the negotiated
	 * number of slots and limit the number of CAM ccb's to one less
	 * so we always have a slot available for recovery.
	 *
	 * NCQ is not used if ap_ncqdepth is 1 or the host controller does
	 * not support it, and in that case the driver can handle extra
	 * ccb's.
	 *
	 * NCQ is currently used only with direct-attached disks.  It is
	 * not used with port multipliers or direct-attached ATAPI devices.
	 *
	 * Remember at least one extra CCB needs to be reserved for the
	 * error ccb.
	 */
	if ((ap->ap_sc->sc_cap & AHCI_REG_CAP_SNCQ) &&
	    ap->ap_type == ATA_PORT_T_DISK &&
	    (le16toh(at->at_identify.satacap) & (1 << 8))) {
		at->at_ncqdepth = (le16toh(at->at_identify.qdepth) & 0x1F) + 1;
		devncqdepth = at->at_ncqdepth;
		if (at->at_ncqdepth > ap->ap_sc->sc_ncmds)
			at->at_ncqdepth = ap->ap_sc->sc_ncmds;
		if (at->at_ncqdepth > 1) {
			for (i = 0; i < ap->ap_sc->sc_ncmds; ++i) {
				xa = ahci_ata_get_xfer(ap, atx);
				if (xa->tag < at->at_ncqdepth) {
					xa->state = ATA_S_COMPLETE;
					ahci_ata_put_xfer(xa);
				}
			}
			if (at->at_ncqdepth >= ap->ap_sc->sc_ncmds) {
				cam_sim_set_max_tags(ap->ap_sim,
						     at->at_ncqdepth - 1);
			}
		}
	} else {
		devncqdepth = 0;
	}

	model_len = sizeof(at->at_identify.model);
	model_id = at->at_identify.model;
	ahci_strip_string(&model_id, &model_len);

	firmware_len = sizeof(at->at_identify.firmware);
	firmware_id = at->at_identify.firmware;
	ahci_strip_string(&firmware_id, &firmware_len);

	serial_len = sizeof(at->at_identify.serial);
	serial_id = at->at_identify.serial;
	ahci_strip_string(&serial_id, &serial_len);

	/*
	 * Generate informatiive strings.
	 *
	 * NOTE: We do not automatically set write caching, lookahead,
	 *	 or the security state for ATAPI devices.
	 */
	if (at->at_identify.cmdset82 & ATA_IDENTIFY_WRITECACHE) {
		if (at->at_identify.features85 & ATA_IDENTIFY_WRITECACHE)
			wcstr = "enabled";
		else if (at->at_type == ATA_PORT_T_ATAPI)
			wcstr = "disabled";
		else
			wcstr = "enabling";
	} else {
		    wcstr = "notsupp";
	}

	if (at->at_identify.cmdset82 & ATA_IDENTIFY_LOOKAHEAD) {
		if (at->at_identify.features85 & ATA_IDENTIFY_LOOKAHEAD)
			rastr = "enabled";
		else if (at->at_type == ATA_PORT_T_ATAPI)
			rastr = "disabled";
		else
			rastr = "enabling";
	} else {
		    rastr = "notsupp";
	}

	if (at->at_identify.cmdset82 & ATA_IDENTIFY_SECURITY) {
		if (at->at_identify.securestatus & ATA_SECURE_FROZEN)
			scstr = "frozen";
		else if (at->at_type == ATA_PORT_T_ATAPI)
			scstr = "unfrozen";
		else if (AhciNoFeatures & (1 << ap->ap_num))
			scstr = "<disabled>";
		else
			scstr = "freezing";
	} else {
		    scstr = "notsupp";
	}

	kprintf("%s: Found %s \"%*.*s %*.*s\" serial=\"%*.*s\"\n"
		"%s: tags=%d/%d satacap=%04x satafea=%04x NCQ=%s "
		"capacity=%lld.%02dMB\n",

		ATANAME(ap, atx),
		type,
		model_len, model_len, model_id,
		firmware_len, firmware_len, firmware_id,
		serial_len, serial_len, serial_id,

		ATANAME(ap, atx),
		devncqdepth, ap->ap_sc->sc_ncmds,
		at->at_identify.satacap,
		at->at_identify.satafsup,
		(at->at_ncqdepth > 1 ? "YES" : "NO"),
		(long long)capacity_bytes / (1024 * 1024),
		(int)(capacity_bytes % (1024 * 1024)) * 100 / (1024 * 1024)
	);
	kprintf("%s: f85=%04x f86=%04x f87=%04x WC=%s RA=%s SEC=%s\n",
		ATANAME(ap, atx),
		at->at_identify.features85,
		at->at_identify.features86,
		at->at_identify.features87,
		wcstr,
		rastr,
		scstr
	);

	/*
	 * Additional type-specific probing
	 */
	switch(at->at_type) {
	case ATA_PORT_T_DISK:
		error = ahci_cam_probe_disk(ap, atx);
		break;
	case ATA_PORT_T_ATAPI:
		error = ahci_cam_probe_atapi(ap, atx);
		break;
	default:
		error = EIO;
		break;
	}
err:
	if (error) {
		at->at_probe = ATA_PROBE_FAILED;
		if (atx == NULL)
			ap->ap_probe = at->at_probe;
	} else {
		at->at_probe = ATA_PROBE_GOOD;
		if (atx == NULL)
			ap->ap_probe = at->at_probe;
	}
	return (error);
}
Пример #5
0
/*
 * AHCI soft reset through port multiplier.
 *
 * This function keeps port communications intact and attempts to generate
 * a reset to the connected device using device commands.  Unlike
 * hard-port operations we can't do fancy stop/starts or stuff like
 * that without messing up other commands that might be running or
 * queued.
 */
int
ahci_pm_softreset(struct ahci_port *ap, int target)
{
	struct ata_port		*at;
	struct ahci_ccb		*ccb;
	struct ahci_cmd_hdr	*cmd_slot;
	u_int8_t		*fis;
	int			count;
	int			error;
	u_int32_t		data;

	error = EIO;
	at = ap->ap_ata[target];

	DPRINTF(AHCI_D_VERBOSE, "%s: soft reset\n", PORTNAME(ap));

	count = 2;
retry:
	/*
	 * Try to clear the phy so we get a good signature, otherwise
	 * the PM may not latch a new signature.
	 *
	 * NOTE: This cannot be safely done between the first and second
	 *	 softreset FISs.  It's now or never.
	 */
	if (ahci_pm_phy_status(ap, target, &data)) {
		kprintf("%s: (B)Cannot clear phy status\n",
			ATANAME(ap ,at));
	}
	ahci_pm_write(ap, target, SATA_PMREG_SERR, -1);

	/*
	 * Prep first D2H command with SRST feature & clear busy/reset flags
	 *
	 * It is unclear which other fields in the FIS are used.  Just zero
	 * everything.
	 *
	 * When soft-resetting a port behind a multiplier at will be
	 * non-NULL, assigning it to the ccb prevents the port interrupt
	 * from hard-resetting the port if a problem crops up.
	 */
	ccb = ahci_get_err_ccb(ap);
	ccb->ccb_xa.flags = ATA_F_POLL | ATA_F_EXCLUSIVE | ATA_F_AUTOSENSE;
	ccb->ccb_xa.complete = ahci_pm_dummy_done;
	ccb->ccb_xa.at = at;

	fis = ccb->ccb_cmd_table->cfis;
	bzero(fis, sizeof(ccb->ccb_cmd_table->cfis));
	fis[0] = ATA_FIS_TYPE_H2D;
	fis[1] = at->at_target;
	fis[15] = ATA_FIS_CONTROL_SRST|ATA_FIS_CONTROL_4BIT;

	cmd_slot = ccb->ccb_cmd_hdr;
	cmd_slot->prdtl = 0;
	cmd_slot->flags = htole16(5);	/* FIS length: 5 DWORDS */
	cmd_slot->flags |= htole16(AHCI_CMD_LIST_FLAG_C); /* Clear busy on OK */
	cmd_slot->flags |= htole16(AHCI_CMD_LIST_FLAG_R); /* Reset */
	cmd_slot->flags |= htole16(at->at_target <<
				   AHCI_CMD_LIST_FLAG_PMP_SHIFT);

	ccb->ccb_xa.state = ATA_S_PENDING;

	/*
	 * This soft reset of the AP target can cause a stream of IFS
	 * errors to occur.  Setting AP_F_IGNORE_IFS prevents the port
	 * from being hard reset (because its the target behind the
	 * port that isn't happy).
	 *
	 * The act of sending the soft reset can cause the target to
	 * blow the port up and generate IFS errors.
	 */
	ap->ap_flags |= AP_F_IGNORE_IFS;
	ap->ap_flags &= ~AP_F_IFS_IGNORED;

	if (ahci_poll(ccb, 1000, ahci_ata_cmd_timeout) != ATA_S_COMPLETE) {
		kprintf("%s: Soft-reset through PM failed, %s\n",
			ATANAME(ap, at),
			(count > 1 ? "retrying" : "giving up"));
		ahci_put_err_ccb(ccb);
		if (--count) {
			if (ap->ap_flags & AP_F_IFS_IGNORED)
				ahci_os_sleep(5000);
			else
				ahci_os_sleep(1000);
			ahci_pwrite(ap, AHCI_PREG_SERR, -1);
			ahci_pwrite(ap, AHCI_PREG_IS, AHCI_PREG_IS_IFS);
			goto retry;
		}
		goto err;
	}

	/*
	 * WARNING!  SENSITIVE TIME PERIOD!  WARNING!
	 *
	 * The first and second FISes are supposed to be back-to-back,
	 * I think the idea is to get the second sent and then after
	 * the device resets it will send a signature.  Do not delay
	 * here and most definitely do not issue any commands to other
	 * targets!
	 */

	/*
	 * Prep second D2H command to read status and complete reset sequence
	 * AHCI 10.4.1 and "Serial ATA Revision 2.6".  I can't find the ATA
	 * Rev 2.6 and it is unclear how the second FIS should be set up
	 * from the AHCI document.
	 *
	 * Give the device 3ms before sending the second FIS.
	 *
	 * It is unclear which other fields in the FIS are used.  Just zero
	 * everything.
	 */
	bzero(fis, sizeof(ccb->ccb_cmd_table->cfis));
	fis[0] = ATA_FIS_TYPE_H2D;
	fis[1] = at->at_target;
	fis[15] = ATA_FIS_CONTROL_4BIT;

	cmd_slot->prdtl = 0;
	cmd_slot->flags = htole16(5);	/* FIS length: 5 DWORDS */
	cmd_slot->flags |= htole16(at->at_target <<
				   AHCI_CMD_LIST_FLAG_PMP_SHIFT);

	ccb->ccb_xa.state = ATA_S_PENDING;
	ccb->ccb_xa.flags = ATA_F_POLL | ATA_F_EXCLUSIVE | ATA_F_AUTOSENSE;

	ap->ap_flags &= ~AP_F_IFS_IGNORED;

	if (ahci_poll(ccb, 1000, ahci_ata_cmd_timeout) != ATA_S_COMPLETE) {
		kprintf("%s: Soft-reset(2) through PM failed, %s\n",
			ATANAME(ap, at),
			(count > 1 ? "retrying" : "giving up"));
		if (--count) {
			ahci_os_sleep(1000);
			ahci_put_err_ccb(ccb);
			ahci_pwrite(ap, AHCI_PREG_SERR, -1);
			ahci_pwrite(ap, AHCI_PREG_IS, AHCI_PREG_IS_IFS);
			goto retry;
		}
		goto err;
	}

	ahci_put_err_ccb(ccb);
	ahci_os_sleep(100);
	ahci_pwrite(ap, AHCI_PREG_SERR, -1);
	ahci_pwrite(ap, AHCI_PREG_IS, AHCI_PREG_IS_IFS);

	ahci_pm_write(ap, target, SATA_PMREG_SERR, -1);
	if (ahci_pm_phy_status(ap, target, &data)) {
		kprintf("%s: (C)Cannot clear phy status\n",
			ATANAME(ap ,at));
	}
	ahci_pm_write(ap, target, SATA_PMREG_SERR, -1);

	/*
	 * Do it again, even if we think we got a good result
	 */
	if (--count) {
		fis[15] = 0;
		goto retry;
	}

	/*
	 * If the softreset is trying to clear a BSY condition after a
	 * normal portreset we assign the port type.
	 *
	 * If the softreset is being run first as part of the ccb error
	 * processing code then report if the device signature changed
	 * unexpectedly.
	 */
	if (at->at_type == ATA_PORT_T_NONE) {
		at->at_type = ahci_port_signature_detect(ap, at);
	} else {
		if (ahci_port_signature_detect(ap, at) != at->at_type) {
			kprintf("%s: device signature unexpectedly "
				"changed\n", ATANAME(ap, at));
			error = EBUSY; /* XXX */
		}
	}
	error = 0;

	/*
	 * Who knows what kind of mess occured.  We have exclusive access
	 * to the port so try to clean up potential problems.
	 */
err:
	ahci_os_sleep(100);

	/*
	 * Clear error status so we can detect removal.
	 */
	if (ahci_pm_write(ap, target, SATA_PMREG_SERR, -1)) {
		kprintf("%s: ahci_pm_softreset unable to clear SERR\n",
			ATANAME(ap, at));
	}
	ahci_pwrite(ap, AHCI_PREG_SERR, -1);
	ahci_pwrite(ap, AHCI_PREG_IS, AHCI_PREG_IS_IFS);
	ap->ap_flags &= ~(AP_F_IGNORE_IFS | AP_F_IFS_IGNORED);

	at->at_probe = error ? ATA_PROBE_FAILED : ATA_PROBE_NEED_IDENT;
	return (error);
}
Пример #6
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
ahci_pm_hardreset(struct ahci_port *ap, int target, int hard)
{
	struct ata_port *at;
	u_int32_t data;
	int loop;
	int error = EIO;

	at = ap->ap_ata[target];

	/*
	 * Turn off power management and kill the phy on the target
	 * if requested.  Hold state for 10ms.
	 */
	data = ap->ap_sc->sc_ipm_disable;
	if (hard == 2)
		data |= AHCI_PREG_SCTL_DET_DISABLE;
	if (ahci_pm_write(ap, target, SATA_PMREG_SERR, -1))
		goto err;
	if (ahci_pm_write(ap, target, SATA_PMREG_SCTL, data))
		goto err;
	ahci_os_sleep(10);

	/*
	 * Start transmitting COMRESET.  COMRESET must be sent for at
	 * least 1ms.
	 */
	at->at_probe = ATA_PROBE_FAILED;
	at->at_type = ATA_PORT_T_NONE;
	data = ap->ap_sc->sc_ipm_disable | AHCI_PREG_SCTL_DET_INIT;
	switch(AhciForceGen) {
	case 0:
		data |= AHCI_PREG_SCTL_SPD_ANY;
		break;
	case 1:
		data |= AHCI_PREG_SCTL_SPD_GEN1;
		break;
	case 2:
		data |= AHCI_PREG_SCTL_SPD_GEN2;
		break;
	case 3:
		data |= AHCI_PREG_SCTL_SPD_GEN3;
		break;
	default:
		data |= AHCI_PREG_SCTL_SPD_GEN3;
		break;
	}
	if (ahci_pm_write(ap, target, SATA_PMREG_SCTL, data))
		goto err;

	/*
	 * 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.
	 */
	ahci_os_sleep(100);

	if (ahci_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.
	 */
	ahci_pm_write(ap, target, SATA_PMREG_SERR, -1);
	data = ap->ap_sc->sc_ipm_disable | AHCI_PREG_SCTL_DET_NONE;
	if (ahci_pm_write(ap, target, SATA_PMREG_SCTL, data))
		goto err;

	/*
	 * Try to determine if there is a device on the port.  This
	 * operation usually runs sequentially on the PM, use a short
	 * 3/10 second timeout.  The disks should already be sufficiently
	 * powered.
	 *
	 * 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 (ahci_pm_read(ap, target, SATA_PMREG_SSTS, &data))
			goto err;
		if (data & AHCI_PREG_SSTS_DET)
			break;
		ahci_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 (ahci_pm_read(ap, target, SATA_PMREG_SSTS, &data))
			goto err;
		if ((data & AHCI_PREG_SSTS_DET) == AHCI_PREG_SSTS_DET_DEV)
			break;
		ahci_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
	 */
	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.  Clear any spurious IFS that may have
	 * occured during the probe.
	 *
	 * WARNING!  100ms seems to work in most cases but
	 */
	ahci_os_sleep(100);
	ahci_pm_write(ap, target, SATA_PMREG_SERR, -1);
	ahci_pwrite(ap, AHCI_PREG_SERR, -1);
	ahci_pwrite(ap, AHCI_PREG_IS, AHCI_PREG_IS_IFS);

	error = 0;
err:
	at->at_probe = error ? ATA_PROBE_FAILED : ATA_PROBE_NEED_SOFT_RESET;
	return (error);
}
Пример #7
0
/*
 * SILI soft reset through port multiplier.
 *
 * This function generates a soft reset through the port multiplier,
 * keeping port communications intact.
 *
 * The SII chip will do the whole mess for us.  However, the command
 * can brick the port if the target is still busy from the previous
 * COMRESET.
 */
int
sili_pm_softreset(struct sili_port *ap, int target)
{
	struct ata_port		*at;
	struct sili_ccb		*ccb;
	struct sili_prb		*prb;
	int			error;
	u_int32_t		data;
	u_int32_t		sig;
	int			timeout;

	error = EIO;
	at = &ap->ap_ata[target];

	kprintf("%s: PM softreset\n", ATANAME(ap, at));

	/*
	 * Prep the special soft-reset SII command.
	 */
	ccb = sili_get_err_ccb(ap);
	ccb->ccb_done = sili_pm_empty_done;
	ccb->ccb_xa.flags = ATA_F_POLL | ATA_F_EXCLUSIVE | ATA_F_AUTOSENSE;
	ccb->ccb_xa.complete = sili_pm_dummy_done;
	ccb->ccb_xa.at = at;

	prb = ccb->ccb_prb;
	bzero(&prb->prb_h2d, sizeof(prb->prb_h2d));
	prb->prb_h2d.flags = at->at_target;
	prb->prb_control = SILI_PRB_CTRL_SOFTRESET;
	prb->prb_override = 0;
	prb->prb_xfer_count = 0;

	ccb->ccb_xa.state = ATA_S_PENDING;

	timeout = (target == 15) ? 1000 : 8000;

	/*
	 * NOTE: Must use sili_quick_timeout() because we hold the err_ccb
	 */
	if (sili_poll(ccb, timeout, sili_quick_timeout) != ATA_S_COMPLETE) {
		if (target != 15) {
			kprintf("%s: (PM) Softreset FIS failed\n",
				ATANAME(ap, at));
		}
		sili_put_err_ccb(ccb);
		goto err;
	}

	sig = (prb->prb_d2h.lba_high << 24) |
	      (prb->prb_d2h.lba_mid << 16) |
	      (prb->prb_d2h.lba_low << 8) |
	      (prb->prb_d2h.sector_count);
	kprintf("%s: PM SOFTRESET SIGNATURE %08x\n", ATANAME(ap, at), sig);

	sili_put_err_ccb(ccb);

	/*
	 * Clear the phy status of the target so we can get a new event.
	 *
	 * Target 15 is the PM itself and these registers have
	 * different meanings.
	 */
	if (target != 15) {
		if (sili_pm_phy_status(ap, target, &data)) {
			kprintf("%s: (C)Cannot clear phy status\n",
				ATANAME(ap ,at));
		}
		sili_pm_write(ap, target, SATA_PMREG_SERR, -1);
	}

	/*
	 * If the softreset is trying to clear a BSY condition after a
	 * normal portreset we assign the port type.
	 *
	 * If the softreset is being run first as part of the ccb error
	 * processing code then report if the device signature changed
	 * unexpectedly.
	 */
	if (at->at_type == ATA_PORT_T_NONE) {
		at->at_type = sili_port_signature(ap, at, sig);
	} else {
		if (sili_port_signature(ap, at, sig) != at->at_type) {
			kprintf("%s: device signature unexpectedly "
				"changed\n", ATANAME(ap, at));
			error = EBUSY; /* XXX */
		}
	}
	error = 0;
err:
	/*
	 * Clear error status so we can detect removal.
	 *
	 * Target 15 is the PM itself and these registers have
	 * different meanings.
	 */
	kprintf("%s: PM softreset done error %d\n", ATANAME(ap, at), error);
	if (error == 0 && target != 15) {
		if (sili_pm_write(ap, target, SATA_PMREG_SERR, -1)) {
			kprintf("%s: sili_pm_softreset unable to clear SERR\n",
				ATANAME(ap, at));
			ap->ap_flags &= ~AP_F_IGNORE_IFS;
		}
	}

	at->at_probe = error ? ATA_PROBE_FAILED : ATA_PROBE_NEED_IDENT;
	return (error);
}
Пример #8
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);
}