aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/ata/libata-core.c
diff options
context:
space:
mode:
authorKristen Carlson Accardi <kristen.c.accardi@intel.com>2007-10-25 00:58:59 -0400
committerJeff Garzik <jeff@garzik.org>2007-10-29 11:00:35 -0400
commitca77329fb713b7fea6a307068e0dd0248e7aa640 (patch)
tree6a1b987f489d7c3f0bbe81647b4ee2b0216afe8a /drivers/ata/libata-core.c
parentab6fc95f609b372a19e18ea689986846ab1ba29c (diff)
[libata] Link power management infrastructure
Device Initiated Power Management, which is defined in SATA 2.5 can be enabled for disks which support it. This patch enables DIPM when the user sets the link power management policy to "min_power". Additionally, libata drivers can define a function (enable_pm) that will perform hardware specific actions to enable whatever power management policy the user set up for Host Initiated Power management (HIPM). This power management policy will be activated after all disks have been enumerated and intialized. Drivers should also define disable_pm, which will turn off link power management, but not change link power management policy. Documentation/scsi/link_power_management_policy.txt has additional information. Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@intel.com> Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
Diffstat (limited to 'drivers/ata/libata-core.c')
-rw-r--r--drivers/ata/libata-core.c196
1 files changed, 195 insertions, 1 deletions
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 3891cdc6bd3d..513babe6a143 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -620,6 +620,177 @@ void ata_dev_disable(struct ata_device *dev)
620 } 620 }
621} 621}
622 622
623static int ata_dev_set_dipm(struct ata_device *dev, enum link_pm policy)
624{
625 struct ata_link *link = dev->link;
626 struct ata_port *ap = link->ap;
627 u32 scontrol;
628 unsigned int err_mask;
629 int rc;
630
631 /*
632 * disallow DIPM for drivers which haven't set
633 * ATA_FLAG_IPM. This is because when DIPM is enabled,
634 * phy ready will be set in the interrupt status on
635 * state changes, which will cause some drivers to
636 * think there are errors - additionally drivers will
637 * need to disable hot plug.
638 */
639 if (!(ap->flags & ATA_FLAG_IPM) || !ata_dev_enabled(dev)) {
640 ap->pm_policy = NOT_AVAILABLE;
641 return -EINVAL;
642 }
643
644 /*
645 * For DIPM, we will only enable it for the
646 * min_power setting.
647 *
648 * Why? Because Disks are too stupid to know that
649 * If the host rejects a request to go to SLUMBER
650 * they should retry at PARTIAL, and instead it
651 * just would give up. So, for medium_power to
652 * work at all, we need to only allow HIPM.
653 */
654 rc = sata_scr_read(link, SCR_CONTROL, &scontrol);
655 if (rc)
656 return rc;
657
658 switch (policy) {
659 case MIN_POWER:
660 /* no restrictions on IPM transitions */
661 scontrol &= ~(0x3 << 8);
662 rc = sata_scr_write(link, SCR_CONTROL, scontrol);
663 if (rc)
664 return rc;
665
666 /* enable DIPM */
667 if (dev->flags & ATA_DFLAG_DIPM)
668 err_mask = ata_dev_set_feature(dev,
669 SETFEATURES_SATA_ENABLE, SATA_DIPM);
670 break;
671 case MEDIUM_POWER:
672 /* allow IPM to PARTIAL */
673 scontrol &= ~(0x1 << 8);
674 scontrol |= (0x2 << 8);
675 rc = sata_scr_write(link, SCR_CONTROL, scontrol);
676 if (rc)
677 return rc;
678
679 /* disable DIPM */
680 if (ata_dev_enabled(dev) && (dev->flags & ATA_DFLAG_DIPM))
681 err_mask = ata_dev_set_feature(dev,
682 SETFEATURES_SATA_DISABLE, SATA_DIPM);
683 break;
684 case NOT_AVAILABLE:
685 case MAX_PERFORMANCE:
686 /* disable all IPM transitions */
687 scontrol |= (0x3 << 8);
688 rc = sata_scr_write(link, SCR_CONTROL, scontrol);
689 if (rc)
690 return rc;
691
692 /* disable DIPM */
693 if (ata_dev_enabled(dev) && (dev->flags & ATA_DFLAG_DIPM))
694 err_mask = ata_dev_set_feature(dev,
695 SETFEATURES_SATA_DISABLE, SATA_DIPM);
696 break;
697 }
698
699 /* FIXME: handle SET FEATURES failure */
700 (void) err_mask;
701
702 return 0;
703}
704
705/**
706 * ata_dev_enable_pm - enable SATA interface power management
707 * @device - device to enable ipm for
708 * @policy - the link power management policy
709 *
710 * Enable SATA Interface power management. This will enable
711 * Device Interface Power Management (DIPM) for min_power
712 * policy, and then call driver specific callbacks for
713 * enabling Host Initiated Power management.
714 *
715 * Locking: Caller.
716 * Returns: -EINVAL if IPM is not supported, 0 otherwise.
717 */
718void ata_dev_enable_pm(struct ata_device *dev, enum link_pm policy)
719{
720 int rc = 0;
721 struct ata_port *ap = dev->link->ap;
722
723 /* set HIPM first, then DIPM */
724 if (ap->ops->enable_pm)
725 rc = ap->ops->enable_pm(ap, policy);
726 if (rc)
727 goto enable_pm_out;
728 rc = ata_dev_set_dipm(dev, policy);
729
730enable_pm_out:
731 if (rc)
732 ap->pm_policy = MAX_PERFORMANCE;
733 else
734 ap->pm_policy = policy;
735 return /* rc */; /* hopefully we can use 'rc' eventually */
736}
737
738/**
739 * ata_dev_disable_pm - disable SATA interface power management
740 * @device - device to enable ipm for
741 *
742 * Disable SATA Interface power management. This will disable
743 * Device Interface Power Management (DIPM) without changing
744 * policy, call driver specific callbacks for disabling Host
745 * Initiated Power management.
746 *
747 * Locking: Caller.
748 * Returns: void
749 */
750static void ata_dev_disable_pm(struct ata_device *dev)
751{
752 struct ata_port *ap = dev->link->ap;
753
754 ata_dev_set_dipm(dev, MAX_PERFORMANCE);
755 if (ap->ops->disable_pm)
756 ap->ops->disable_pm(ap);
757}
758
759void ata_lpm_schedule(struct ata_port *ap, enum link_pm policy)
760{
761 ap->pm_policy = policy;
762 ap->link.eh_info.action |= ATA_EHI_LPM;
763 ap->link.eh_info.flags |= ATA_EHI_NO_AUTOPSY;
764 ata_port_schedule_eh(ap);
765}
766
767static void ata_lpm_enable(struct ata_host *host)
768{
769 struct ata_link *link;
770 struct ata_port *ap;
771 struct ata_device *dev;
772 int i;
773
774 for (i = 0; i < host->n_ports; i++) {
775 ap = host->ports[i];
776 ata_port_for_each_link(link, ap) {
777 ata_link_for_each_dev(dev, link)
778 ata_dev_disable_pm(dev);
779 }
780 }
781}
782
783static void ata_lpm_disable(struct ata_host *host)
784{
785 int i;
786
787 for (i = 0; i < host->n_ports; i++) {
788 struct ata_port *ap = host->ports[i];
789 ata_lpm_schedule(ap, ap->pm_policy);
790 }
791}
792
793
623/** 794/**
624 * ata_devchk - PATA device presence detection 795 * ata_devchk - PATA device presence detection
625 * @ap: ATA channel to examine 796 * @ap: ATA channel to examine
@@ -2101,6 +2272,13 @@ int ata_dev_configure(struct ata_device *dev)
2101 if (dev->flags & ATA_DFLAG_LBA48) 2272 if (dev->flags & ATA_DFLAG_LBA48)
2102 dev->max_sectors = ATA_MAX_SECTORS_LBA48; 2273 dev->max_sectors = ATA_MAX_SECTORS_LBA48;
2103 2274
2275 if (!(dev->horkage & ATA_HORKAGE_IPM)) {
2276 if (ata_id_has_hipm(dev->id))
2277 dev->flags |= ATA_DFLAG_HIPM;
2278 if (ata_id_has_dipm(dev->id))
2279 dev->flags |= ATA_DFLAG_DIPM;
2280 }
2281
2104 if (dev->horkage & ATA_HORKAGE_DIAGNOSTIC) { 2282 if (dev->horkage & ATA_HORKAGE_DIAGNOSTIC) {
2105 /* Let the user know. We don't want to disallow opens for 2283 /* Let the user know. We don't want to disallow opens for
2106 rescue purposes, or in case the vendor is just a blithering 2284 rescue purposes, or in case the vendor is just a blithering
@@ -2126,6 +2304,13 @@ int ata_dev_configure(struct ata_device *dev)
2126 dev->max_sectors = min_t(unsigned int, ATA_MAX_SECTORS_128, 2304 dev->max_sectors = min_t(unsigned int, ATA_MAX_SECTORS_128,
2127 dev->max_sectors); 2305 dev->max_sectors);
2128 2306
2307 if (ata_dev_blacklisted(dev) & ATA_HORKAGE_IPM) {
2308 dev->horkage |= ATA_HORKAGE_IPM;
2309
2310 /* reset link pm_policy for this port to no pm */
2311 ap->pm_policy = MAX_PERFORMANCE;
2312 }
2313
2129 if (ap->ops->dev_config) 2314 if (ap->ops->dev_config)
2130 ap->ops->dev_config(dev); 2315 ap->ops->dev_config(dev);
2131 2316
@@ -6361,6 +6546,12 @@ int ata_host_suspend(struct ata_host *host, pm_message_t mesg)
6361{ 6546{
6362 int rc; 6547 int rc;
6363 6548
6549 /*
6550 * disable link pm on all ports before requesting
6551 * any pm activity
6552 */
6553 ata_lpm_enable(host);
6554
6364 rc = ata_host_request_pm(host, mesg, 0, ATA_EHI_QUIET, 1); 6555 rc = ata_host_request_pm(host, mesg, 0, ATA_EHI_QUIET, 1);
6365 if (rc == 0) 6556 if (rc == 0)
6366 host->dev->power.power_state = mesg; 6557 host->dev->power.power_state = mesg;
@@ -6383,6 +6574,9 @@ void ata_host_resume(struct ata_host *host)
6383 ata_host_request_pm(host, PMSG_ON, ATA_EH_SOFTRESET, 6574 ata_host_request_pm(host, PMSG_ON, ATA_EH_SOFTRESET,
6384 ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET, 0); 6575 ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET, 0);
6385 host->dev->power.power_state = PMSG_ON; 6576 host->dev->power.power_state = PMSG_ON;
6577
6578 /* reenable link pm */
6579 ata_lpm_disable(host);
6386} 6580}
6387#endif 6581#endif
6388 6582
@@ -6925,6 +7119,7 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht)
6925 struct ata_port *ap = host->ports[i]; 7119 struct ata_port *ap = host->ports[i];
6926 7120
6927 ata_scsi_scan_host(ap, 1); 7121 ata_scsi_scan_host(ap, 1);
7122 ata_lpm_schedule(ap, ap->pm_policy);
6928 } 7123 }
6929 7124
6930 return 0; 7125 return 0;
@@ -7321,7 +7516,6 @@ const struct ata_port_info ata_dummy_port_info = {
7321 * likely to change as new drivers are added and updated. 7516 * likely to change as new drivers are added and updated.
7322 * Do not depend on ABI/API stability. 7517 * Do not depend on ABI/API stability.
7323 */ 7518 */
7324
7325EXPORT_SYMBOL_GPL(sata_deb_timing_normal); 7519EXPORT_SYMBOL_GPL(sata_deb_timing_normal);
7326EXPORT_SYMBOL_GPL(sata_deb_timing_hotplug); 7520EXPORT_SYMBOL_GPL(sata_deb_timing_hotplug);
7327EXPORT_SYMBOL_GPL(sata_deb_timing_long); 7521EXPORT_SYMBOL_GPL(sata_deb_timing_long);