Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7d77b247 authored by Tejun Heo's avatar Tejun Heo Committed by Jeff Garzik
Browse files

libata-pmp-prep: implement sata_async_notification()



AN serves multiple purposes.  For ATAPI, it's used for media change
notification.  For PMP, for downstream PHY status change notification.
Implement sata_async_notification() which demultiplexes AN.

To avoid unnecessary port events, ATAPI AN is not enabled if PMP is
attached but SNTF is not available.

Signed-off-by: default avatarTejun Heo <htejun@gmail.com>
Cc: Kriten Carlson Accardi <kristen.c.accardi@intel.com>
Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
parent e31e8531
Loading
Loading
Loading
Loading
+7 −17
Original line number Diff line number Diff line
@@ -1356,27 +1356,17 @@ static void ahci_port_intr(struct ata_port *ap)
	}

	if (status & PORT_IRQ_SDB_FIS) {
		/*
		 * if this is an ATAPI device with AN turned on,
		 * then we should interrogate the device to
		 * determine the cause of the interrupt
		 *
		 * for AN - this we should check the SDB FIS
		 * and find the I and N bits set
		/* If the 'N' bit in word 0 of the FIS is set, we just
		 * received asynchronous notification.  Tell libata
		 * about it.  Note that as the SDB FIS itself is
		 * accessible, SNotification can be emulated by the
		 * driver but don't bother for the time being.
		 */
		const __le32 *f = pp->rx_fis + RX_FIS_SDB;
		u32 f0 = le32_to_cpu(f[0]);

		/* check the 'N' bit in word 0 of the FIS */
		if (f0 & (1 << 15)) {
			int port_addr = ((f0 & 0x00000f00) >> 8);
			struct ata_device *adev;
			if (port_addr < ATA_MAX_DEVICES) {
				adev = &ap->link.device[port_addr];
				if (adev->flags & ATA_DFLAG_AN)
					ata_scsi_media_change_notify(adev);
			}
		}
		if (f0 & (1 << 15))
			sata_async_notification(ap);
	}

	if (ap->link.sactive)
+9 −4
Original line number Diff line number Diff line
@@ -2016,6 +2016,7 @@ int ata_dev_configure(struct ata_device *dev)
	else if (dev->class == ATA_DEV_ATAPI) {
		const char *cdb_intr_string = "";
		const char *atapi_an_string = "";
		u32 sntf;

		rc = atapi_cdb_len(id);
		if ((rc < 12) || (rc > ATAPI_CDB_LEN)) {
@@ -2027,11 +2028,14 @@ int ata_dev_configure(struct ata_device *dev)
		}
		dev->cdb_len = (unsigned int) rc;

		/*
		 * check to see if this ATAPI device supports
		 * Asynchronous Notification
		/* Enable ATAPI AN if both the host and device have
		 * the support.  If PMP is attached, SNTF is required
		 * to enable ATAPI AN to discern between PHY status
		 * changed notifications and ATAPI ANs.
		 */
		if ((ap->flags & ATA_FLAG_AN) && ata_id_has_atapi_AN(id)) {
		if ((ap->flags & ATA_FLAG_AN) && ata_id_has_atapi_AN(id) &&
		    (!ap->nr_pmp_links ||
		     sata_scr_read(&ap->link, SCR_NOTIFICATION, &sntf) == 0)) {
			unsigned int err_mask;

			/* issue SET feature command to turn this on */
@@ -7248,6 +7252,7 @@ EXPORT_SYMBOL_GPL(ata_port_schedule_eh);
EXPORT_SYMBOL_GPL(ata_link_abort);
EXPORT_SYMBOL_GPL(ata_port_abort);
EXPORT_SYMBOL_GPL(ata_port_freeze);
EXPORT_SYMBOL_GPL(sata_async_notification);
EXPORT_SYMBOL_GPL(ata_eh_freeze_port);
EXPORT_SYMBOL_GPL(ata_eh_thaw_port);
EXPORT_SYMBOL_GPL(ata_eh_qc_complete);
+73 −0
Original line number Diff line number Diff line
@@ -904,6 +904,79 @@ int ata_port_freeze(struct ata_port *ap)
	return nr_aborted;
}

/**
 *	sata_async_notification - SATA async notification handler
 *	@ap: ATA port where async notification is received
 *
 *	Handler to be called when async notification via SDB FIS is
 *	received.  This function schedules EH if necessary.
 *
 *	LOCKING:
 *	spin_lock_irqsave(host lock)
 *
 *	RETURNS:
 *	1 if EH is scheduled, 0 otherwise.
 */
int sata_async_notification(struct ata_port *ap)
{
	u32 sntf;
	int rc;

	if (!(ap->flags & ATA_FLAG_AN))
		return 0;

	rc = sata_scr_read(&ap->link, SCR_NOTIFICATION, &sntf);
	if (rc == 0)
		sata_scr_write(&ap->link, SCR_NOTIFICATION, sntf);

	if (!ap->nr_pmp_links || rc) {
		/* PMP is not attached or SNTF is not available */
		if (!ap->nr_pmp_links) {
			/* PMP is not attached.  Check whether ATAPI
			 * AN is configured.  If so, notify media
			 * change.
			 */
			struct ata_device *dev = ap->link.device;

			if ((dev->class == ATA_DEV_ATAPI) &&
			    (dev->flags & ATA_DFLAG_AN))
				ata_scsi_media_change_notify(dev);
			return 0;
		} else {
			/* PMP is attached but SNTF is not available.
			 * ATAPI async media change notification is
			 * not used.  The PMP must be reporting PHY
			 * status change, schedule EH.
			 */
			ata_port_schedule_eh(ap);
			return 1;
		}
	} else {
		/* PMP is attached and SNTF is available */
		struct ata_link *link;

		/* check and notify ATAPI AN */
		ata_port_for_each_link(link, ap) {
			if (!(sntf & (1 << link->pmp)))
				continue;

			if ((link->device->class == ATA_DEV_ATAPI) &&
			    (link->device->flags & ATA_DFLAG_AN))
				ata_scsi_media_change_notify(link->device);
		}

		/* If PMP is reporting that PHY status of some
		 * downstream ports has changed, schedule EH.
		 */
		if (sntf & (1 << SATA_PMP_CTRL_PORT)) {
			ata_port_schedule_eh(ap);
			return 1;
		}

		return 0;
	}
}

/**
 *	ata_eh_freeze_port - EH helper to freeze port
 *	@ap: ATA port to freeze
+0 −1
Original line number Diff line number Diff line
@@ -3244,7 +3244,6 @@ void ata_scsi_media_change_notify(struct ata_device *dev)
		scsi_device_event_notify(dev->sdev, SDEV_MEDIA_CHANGE);
#endif
}
EXPORT_SYMBOL_GPL(ata_scsi_media_change_notify);

/**
 *	ata_scsi_hotplug - SCSI part of hotplug
+1 −0
Original line number Diff line number Diff line
@@ -119,6 +119,7 @@ extern int ata_scsi_add_hosts(struct ata_host *host,
			      struct scsi_host_template *sht);
extern void ata_scsi_scan_host(struct ata_port *ap, int sync);
extern int ata_scsi_offline_dev(struct ata_device *dev);
extern void ata_scsi_media_change_notify(struct ata_device *dev);
extern void ata_scsi_hotplug(struct work_struct *work);
extern unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf,
			       unsigned int buflen);
Loading