diff options
author | Tejun Heo <htejun@gmail.com> | 2007-09-23 00:14:13 -0400 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2007-10-12 14:55:42 -0400 |
commit | 7d77b247088fb360aa74bfdd9e19bce1e1987668 (patch) | |
tree | add1b03309dd6fa82eb0f47e1a88766695f38f28 | |
parent | e31e8531d668c9c4dc7883054788f89805188003 (diff) |
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: Tejun Heo <htejun@gmail.com>
Cc: Kriten Carlson Accardi <kristen.c.accardi@intel.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
-rw-r--r-- | drivers/ata/ahci.c | 24 | ||||
-rw-r--r-- | drivers/ata/libata-core.c | 13 | ||||
-rw-r--r-- | drivers/ata/libata-eh.c | 73 | ||||
-rw-r--r-- | drivers/ata/libata-scsi.c | 1 | ||||
-rw-r--r-- | drivers/ata/libata.h | 1 | ||||
-rw-r--r-- | drivers/ata/sata_sil24.c | 5 | ||||
-rw-r--r-- | include/linux/libata.h | 4 |
7 files changed, 93 insertions, 28 deletions
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index cf3404467ceb..9f3c591c7214 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c | |||
@@ -1356,27 +1356,17 @@ static void ahci_port_intr(struct ata_port *ap) | |||
1356 | } | 1356 | } |
1357 | 1357 | ||
1358 | if (status & PORT_IRQ_SDB_FIS) { | 1358 | if (status & PORT_IRQ_SDB_FIS) { |
1359 | /* | 1359 | /* If the 'N' bit in word 0 of the FIS is set, we just |
1360 | * if this is an ATAPI device with AN turned on, | 1360 | * received asynchronous notification. Tell libata |
1361 | * then we should interrogate the device to | 1361 | * about it. Note that as the SDB FIS itself is |
1362 | * determine the cause of the interrupt | 1362 | * accessible, SNotification can be emulated by the |
1363 | * | 1363 | * driver but don't bother for the time being. |
1364 | * for AN - this we should check the SDB FIS | ||
1365 | * and find the I and N bits set | ||
1366 | */ | 1364 | */ |
1367 | const __le32 *f = pp->rx_fis + RX_FIS_SDB; | 1365 | const __le32 *f = pp->rx_fis + RX_FIS_SDB; |
1368 | u32 f0 = le32_to_cpu(f[0]); | 1366 | u32 f0 = le32_to_cpu(f[0]); |
1369 | 1367 | ||
1370 | /* check the 'N' bit in word 0 of the FIS */ | 1368 | if (f0 & (1 << 15)) |
1371 | if (f0 & (1 << 15)) { | 1369 | sata_async_notification(ap); |
1372 | int port_addr = ((f0 & 0x00000f00) >> 8); | ||
1373 | struct ata_device *adev; | ||
1374 | if (port_addr < ATA_MAX_DEVICES) { | ||
1375 | adev = &ap->link.device[port_addr]; | ||
1376 | if (adev->flags & ATA_DFLAG_AN) | ||
1377 | ata_scsi_media_change_notify(adev); | ||
1378 | } | ||
1379 | } | ||
1380 | } | 1370 | } |
1381 | 1371 | ||
1382 | if (ap->link.sactive) | 1372 | if (ap->link.sactive) |
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 376dbd80cc93..8b08e7bdd24d 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c | |||
@@ -2016,6 +2016,7 @@ int ata_dev_configure(struct ata_device *dev) | |||
2016 | else if (dev->class == ATA_DEV_ATAPI) { | 2016 | else if (dev->class == ATA_DEV_ATAPI) { |
2017 | const char *cdb_intr_string = ""; | 2017 | const char *cdb_intr_string = ""; |
2018 | const char *atapi_an_string = ""; | 2018 | const char *atapi_an_string = ""; |
2019 | u32 sntf; | ||
2019 | 2020 | ||
2020 | rc = atapi_cdb_len(id); | 2021 | rc = atapi_cdb_len(id); |
2021 | if ((rc < 12) || (rc > ATAPI_CDB_LEN)) { | 2022 | if ((rc < 12) || (rc > ATAPI_CDB_LEN)) { |
@@ -2027,11 +2028,14 @@ int ata_dev_configure(struct ata_device *dev) | |||
2027 | } | 2028 | } |
2028 | dev->cdb_len = (unsigned int) rc; | 2029 | dev->cdb_len = (unsigned int) rc; |
2029 | 2030 | ||
2030 | /* | 2031 | /* Enable ATAPI AN if both the host and device have |
2031 | * check to see if this ATAPI device supports | 2032 | * the support. If PMP is attached, SNTF is required |
2032 | * Asynchronous Notification | 2033 | * to enable ATAPI AN to discern between PHY status |
2034 | * changed notifications and ATAPI ANs. | ||
2033 | */ | 2035 | */ |
2034 | if ((ap->flags & ATA_FLAG_AN) && ata_id_has_atapi_AN(id)) { | 2036 | if ((ap->flags & ATA_FLAG_AN) && ata_id_has_atapi_AN(id) && |
2037 | (!ap->nr_pmp_links || | ||
2038 | sata_scr_read(&ap->link, SCR_NOTIFICATION, &sntf) == 0)) { | ||
2035 | unsigned int err_mask; | 2039 | unsigned int err_mask; |
2036 | 2040 | ||
2037 | /* issue SET feature command to turn this on */ | 2041 | /* issue SET feature command to turn this on */ |
@@ -7248,6 +7252,7 @@ EXPORT_SYMBOL_GPL(ata_port_schedule_eh); | |||
7248 | EXPORT_SYMBOL_GPL(ata_link_abort); | 7252 | EXPORT_SYMBOL_GPL(ata_link_abort); |
7249 | EXPORT_SYMBOL_GPL(ata_port_abort); | 7253 | EXPORT_SYMBOL_GPL(ata_port_abort); |
7250 | EXPORT_SYMBOL_GPL(ata_port_freeze); | 7254 | EXPORT_SYMBOL_GPL(ata_port_freeze); |
7255 | EXPORT_SYMBOL_GPL(sata_async_notification); | ||
7251 | EXPORT_SYMBOL_GPL(ata_eh_freeze_port); | 7256 | EXPORT_SYMBOL_GPL(ata_eh_freeze_port); |
7252 | EXPORT_SYMBOL_GPL(ata_eh_thaw_port); | 7257 | EXPORT_SYMBOL_GPL(ata_eh_thaw_port); |
7253 | EXPORT_SYMBOL_GPL(ata_eh_qc_complete); | 7258 | EXPORT_SYMBOL_GPL(ata_eh_qc_complete); |
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 3c31e10caf21..60186f8ac3a1 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c | |||
@@ -905,6 +905,79 @@ int ata_port_freeze(struct ata_port *ap) | |||
905 | } | 905 | } |
906 | 906 | ||
907 | /** | 907 | /** |
908 | * sata_async_notification - SATA async notification handler | ||
909 | * @ap: ATA port where async notification is received | ||
910 | * | ||
911 | * Handler to be called when async notification via SDB FIS is | ||
912 | * received. This function schedules EH if necessary. | ||
913 | * | ||
914 | * LOCKING: | ||
915 | * spin_lock_irqsave(host lock) | ||
916 | * | ||
917 | * RETURNS: | ||
918 | * 1 if EH is scheduled, 0 otherwise. | ||
919 | */ | ||
920 | int sata_async_notification(struct ata_port *ap) | ||
921 | { | ||
922 | u32 sntf; | ||
923 | int rc; | ||
924 | |||
925 | if (!(ap->flags & ATA_FLAG_AN)) | ||
926 | return 0; | ||
927 | |||
928 | rc = sata_scr_read(&ap->link, SCR_NOTIFICATION, &sntf); | ||
929 | if (rc == 0) | ||
930 | sata_scr_write(&ap->link, SCR_NOTIFICATION, sntf); | ||
931 | |||
932 | if (!ap->nr_pmp_links || rc) { | ||
933 | /* PMP is not attached or SNTF is not available */ | ||
934 | if (!ap->nr_pmp_links) { | ||
935 | /* PMP is not attached. Check whether ATAPI | ||
936 | * AN is configured. If so, notify media | ||
937 | * change. | ||
938 | */ | ||
939 | struct ata_device *dev = ap->link.device; | ||
940 | |||
941 | if ((dev->class == ATA_DEV_ATAPI) && | ||
942 | (dev->flags & ATA_DFLAG_AN)) | ||
943 | ata_scsi_media_change_notify(dev); | ||
944 | return 0; | ||
945 | } else { | ||
946 | /* PMP is attached but SNTF is not available. | ||
947 | * ATAPI async media change notification is | ||
948 | * not used. The PMP must be reporting PHY | ||
949 | * status change, schedule EH. | ||
950 | */ | ||
951 | ata_port_schedule_eh(ap); | ||
952 | return 1; | ||
953 | } | ||
954 | } else { | ||
955 | /* PMP is attached and SNTF is available */ | ||
956 | struct ata_link *link; | ||
957 | |||
958 | /* check and notify ATAPI AN */ | ||
959 | ata_port_for_each_link(link, ap) { | ||
960 | if (!(sntf & (1 << link->pmp))) | ||
961 | continue; | ||
962 | |||
963 | if ((link->device->class == ATA_DEV_ATAPI) && | ||
964 | (link->device->flags & ATA_DFLAG_AN)) | ||
965 | ata_scsi_media_change_notify(link->device); | ||
966 | } | ||
967 | |||
968 | /* If PMP is reporting that PHY status of some | ||
969 | * downstream ports has changed, schedule EH. | ||
970 | */ | ||
971 | if (sntf & (1 << SATA_PMP_CTRL_PORT)) { | ||
972 | ata_port_schedule_eh(ap); | ||
973 | return 1; | ||
974 | } | ||
975 | |||
976 | return 0; | ||
977 | } | ||
978 | } | ||
979 | |||
980 | /** | ||
908 | * ata_eh_freeze_port - EH helper to freeze port | 981 | * ata_eh_freeze_port - EH helper to freeze port |
909 | * @ap: ATA port to freeze | 982 | * @ap: ATA port to freeze |
910 | * | 983 | * |
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 451f79c6fbac..df2e05738f3b 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c | |||
@@ -3244,7 +3244,6 @@ void ata_scsi_media_change_notify(struct ata_device *dev) | |||
3244 | scsi_device_event_notify(dev->sdev, SDEV_MEDIA_CHANGE); | 3244 | scsi_device_event_notify(dev->sdev, SDEV_MEDIA_CHANGE); |
3245 | #endif | 3245 | #endif |
3246 | } | 3246 | } |
3247 | EXPORT_SYMBOL_GPL(ata_scsi_media_change_notify); | ||
3248 | 3247 | ||
3249 | /** | 3248 | /** |
3250 | * ata_scsi_hotplug - SCSI part of hotplug | 3249 | * ata_scsi_hotplug - SCSI part of hotplug |
diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 0f3e355fdfd7..ebe22982e80c 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h | |||
@@ -119,6 +119,7 @@ extern int ata_scsi_add_hosts(struct ata_host *host, | |||
119 | struct scsi_host_template *sht); | 119 | struct scsi_host_template *sht); |
120 | extern void ata_scsi_scan_host(struct ata_port *ap, int sync); | 120 | extern void ata_scsi_scan_host(struct ata_port *ap, int sync); |
121 | extern int ata_scsi_offline_dev(struct ata_device *dev); | 121 | extern int ata_scsi_offline_dev(struct ata_device *dev); |
122 | extern void ata_scsi_media_change_notify(struct ata_device *dev); | ||
122 | extern void ata_scsi_hotplug(struct work_struct *work); | 123 | extern void ata_scsi_hotplug(struct work_struct *work); |
123 | extern unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf, | 124 | extern unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf, |
124 | unsigned int buflen); | 125 | unsigned int buflen); |
diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c index 9acfce43bde4..b4f81eb8bbbe 100644 --- a/drivers/ata/sata_sil24.c +++ b/drivers/ata/sata_sil24.c | |||
@@ -821,11 +821,8 @@ static void sil24_error_intr(struct ata_port *ap) | |||
821 | ata_ehi_push_desc(ehi, "irq_stat 0x%08x", irq_stat); | 821 | ata_ehi_push_desc(ehi, "irq_stat 0x%08x", irq_stat); |
822 | 822 | ||
823 | if (irq_stat & PORT_IRQ_SDB_NOTIFY) { | 823 | if (irq_stat & PORT_IRQ_SDB_NOTIFY) { |
824 | struct ata_device *dev = ap->link.device; | ||
825 | |||
826 | ata_ehi_push_desc(ehi, "SDB notify"); | 824 | ata_ehi_push_desc(ehi, "SDB notify"); |
827 | if (dev->flags & ATA_DFLAG_AN) | 825 | sata_async_notification(ap); |
828 | ata_scsi_media_change_notify(dev); | ||
829 | } | 826 | } |
830 | 827 | ||
831 | if (irq_stat & (PORT_IRQ_PHYRDY_CHG | PORT_IRQ_DEV_XCHG)) { | 828 | if (irq_stat & (PORT_IRQ_PHYRDY_CHG | PORT_IRQ_DEV_XCHG)) { |
diff --git a/include/linux/libata.h b/include/linux/libata.h index 56b218771114..cd9c2a28136a 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h | |||
@@ -139,7 +139,7 @@ enum { | |||
139 | ATA_DFLAG_FLUSH_EXT = (1 << 4), /* do FLUSH_EXT instead of FLUSH */ | 139 | ATA_DFLAG_FLUSH_EXT = (1 << 4), /* do FLUSH_EXT instead of FLUSH */ |
140 | ATA_DFLAG_ACPI_PENDING = (1 << 5), /* ACPI resume action pending */ | 140 | ATA_DFLAG_ACPI_PENDING = (1 << 5), /* ACPI resume action pending */ |
141 | ATA_DFLAG_ACPI_FAILED = (1 << 6), /* ACPI on devcfg has failed */ | 141 | ATA_DFLAG_ACPI_FAILED = (1 << 6), /* ACPI on devcfg has failed */ |
142 | ATA_DFLAG_AN = (1 << 7), /* device supports AN */ | 142 | ATA_DFLAG_AN = (1 << 7), /* AN configured */ |
143 | ATA_DFLAG_CFG_MASK = (1 << 12) - 1, | 143 | ATA_DFLAG_CFG_MASK = (1 << 12) - 1, |
144 | 144 | ||
145 | ATA_DFLAG_PIO = (1 << 12), /* device limited to PIO mode */ | 145 | ATA_DFLAG_PIO = (1 << 12), /* device limited to PIO mode */ |
@@ -787,7 +787,6 @@ extern void ata_host_init(struct ata_host *, struct device *, | |||
787 | extern int ata_scsi_detect(struct scsi_host_template *sht); | 787 | extern int ata_scsi_detect(struct scsi_host_template *sht); |
788 | extern int ata_scsi_ioctl(struct scsi_device *dev, int cmd, void __user *arg); | 788 | extern int ata_scsi_ioctl(struct scsi_device *dev, int cmd, void __user *arg); |
789 | extern int ata_scsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)); | 789 | extern int ata_scsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)); |
790 | extern void ata_scsi_media_change_notify(struct ata_device *atadev); | ||
791 | extern void ata_sas_port_destroy(struct ata_port *); | 790 | extern void ata_sas_port_destroy(struct ata_port *); |
792 | extern struct ata_port *ata_sas_port_alloc(struct ata_host *, | 791 | extern struct ata_port *ata_sas_port_alloc(struct ata_host *, |
793 | struct ata_port_info *, struct Scsi_Host *); | 792 | struct ata_port_info *, struct Scsi_Host *); |
@@ -953,6 +952,7 @@ extern void ata_port_schedule_eh(struct ata_port *ap); | |||
953 | extern int ata_link_abort(struct ata_link *link); | 952 | extern int ata_link_abort(struct ata_link *link); |
954 | extern int ata_port_abort(struct ata_port *ap); | 953 | extern int ata_port_abort(struct ata_port *ap); |
955 | extern int ata_port_freeze(struct ata_port *ap); | 954 | extern int ata_port_freeze(struct ata_port *ap); |
955 | extern int sata_async_notification(struct ata_port *ap); | ||
956 | 956 | ||
957 | extern void ata_eh_freeze_port(struct ata_port *ap); | 957 | extern void ata_eh_freeze_port(struct ata_port *ap); |
958 | extern void ata_eh_thaw_port(struct ata_port *ap); | 958 | extern void ata_eh_thaw_port(struct ata_port *ap); |