diff options
author | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-29 15:12:34 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-29 15:12:34 -0400 |
commit | 3529a233421fc43fa7bfdf7a4317daf28348a23d (patch) | |
tree | 520558ffb70e4f4743495ad7a62d4c775c0c1ea3 /drivers/ata | |
parent | 00cda56d39f013cce60f44f1e3da19b87eba5d85 (diff) | |
parent | 31556594f913fa81d008cecfe46d7211c919a853 (diff) |
Merge branch 'alpm' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik/libata-dev
* 'alpm' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik/libata-dev:
[libata] AHCI: add hw link power management support
[libata] Link power management infrastructure
Diffstat (limited to 'drivers/ata')
-rw-r--r-- | drivers/ata/ahci.c | 157 | ||||
-rw-r--r-- | drivers/ata/libata-core.c | 196 | ||||
-rw-r--r-- | drivers/ata/libata-eh.c | 4 | ||||
-rw-r--r-- | drivers/ata/libata-scsi.c | 68 | ||||
-rw-r--r-- | drivers/ata/libata.h | 2 |
5 files changed, 424 insertions, 3 deletions
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index c8ab947cf359..ed9b407e42d4 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c | |||
@@ -49,6 +49,9 @@ | |||
49 | #define DRV_NAME "ahci" | 49 | #define DRV_NAME "ahci" |
50 | #define DRV_VERSION "3.0" | 50 | #define DRV_VERSION "3.0" |
51 | 51 | ||
52 | static int ahci_enable_alpm(struct ata_port *ap, | ||
53 | enum link_pm policy); | ||
54 | static void ahci_disable_alpm(struct ata_port *ap); | ||
52 | 55 | ||
53 | enum { | 56 | enum { |
54 | AHCI_PCI_BAR = 5, | 57 | AHCI_PCI_BAR = 5, |
@@ -99,6 +102,7 @@ enum { | |||
99 | HOST_CAP_SSC = (1 << 14), /* Slumber capable */ | 102 | HOST_CAP_SSC = (1 << 14), /* Slumber capable */ |
100 | HOST_CAP_PMP = (1 << 17), /* Port Multiplier support */ | 103 | HOST_CAP_PMP = (1 << 17), /* Port Multiplier support */ |
101 | HOST_CAP_CLO = (1 << 24), /* Command List Override support */ | 104 | HOST_CAP_CLO = (1 << 24), /* Command List Override support */ |
105 | HOST_CAP_ALPM = (1 << 26), /* Aggressive Link PM support */ | ||
102 | HOST_CAP_SSS = (1 << 27), /* Staggered Spin-up */ | 106 | HOST_CAP_SSS = (1 << 27), /* Staggered Spin-up */ |
103 | HOST_CAP_SNTF = (1 << 29), /* SNotification register */ | 107 | HOST_CAP_SNTF = (1 << 29), /* SNotification register */ |
104 | HOST_CAP_NCQ = (1 << 30), /* Native Command Queueing */ | 108 | HOST_CAP_NCQ = (1 << 30), /* Native Command Queueing */ |
@@ -155,6 +159,8 @@ enum { | |||
155 | PORT_IRQ_PIOS_FIS | PORT_IRQ_D2H_REG_FIS, | 159 | PORT_IRQ_PIOS_FIS | PORT_IRQ_D2H_REG_FIS, |
156 | 160 | ||
157 | /* PORT_CMD bits */ | 161 | /* PORT_CMD bits */ |
162 | PORT_CMD_ASP = (1 << 27), /* Aggressive Slumber/Partial */ | ||
163 | PORT_CMD_ALPE = (1 << 26), /* Aggressive Link PM enable */ | ||
158 | PORT_CMD_ATAPI = (1 << 24), /* Device is ATAPI */ | 164 | PORT_CMD_ATAPI = (1 << 24), /* Device is ATAPI */ |
159 | PORT_CMD_PMP = (1 << 17), /* PMP attached */ | 165 | PORT_CMD_PMP = (1 << 17), /* PMP attached */ |
160 | PORT_CMD_LIST_ON = (1 << 15), /* cmd list DMA engine running */ | 166 | PORT_CMD_LIST_ON = (1 << 15), /* cmd list DMA engine running */ |
@@ -178,13 +184,14 @@ enum { | |||
178 | AHCI_HFLAG_MV_PATA = (1 << 4), /* PATA port */ | 184 | AHCI_HFLAG_MV_PATA = (1 << 4), /* PATA port */ |
179 | AHCI_HFLAG_NO_MSI = (1 << 5), /* no PCI MSI */ | 185 | AHCI_HFLAG_NO_MSI = (1 << 5), /* no PCI MSI */ |
180 | AHCI_HFLAG_NO_PMP = (1 << 6), /* no PMP */ | 186 | AHCI_HFLAG_NO_PMP = (1 << 6), /* no PMP */ |
187 | AHCI_HFLAG_NO_HOTPLUG = (1 << 7), /* ignore PxSERR.DIAG.N */ | ||
181 | 188 | ||
182 | /* ap->flags bits */ | 189 | /* ap->flags bits */ |
183 | AHCI_FLAG_NO_HOTPLUG = (1 << 24), /* ignore PxSERR.DIAG.N */ | ||
184 | 190 | ||
185 | AHCI_FLAG_COMMON = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | | 191 | AHCI_FLAG_COMMON = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | |
186 | ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | | 192 | ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | |
187 | ATA_FLAG_ACPI_SATA | ATA_FLAG_AN, | 193 | ATA_FLAG_ACPI_SATA | ATA_FLAG_AN | |
194 | ATA_FLAG_IPM, | ||
188 | AHCI_LFLAG_COMMON = ATA_LFLAG_SKIP_D2H_BSY, | 195 | AHCI_LFLAG_COMMON = ATA_LFLAG_SKIP_D2H_BSY, |
189 | }; | 196 | }; |
190 | 197 | ||
@@ -254,6 +261,11 @@ static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg); | |||
254 | static int ahci_pci_device_resume(struct pci_dev *pdev); | 261 | static int ahci_pci_device_resume(struct pci_dev *pdev); |
255 | #endif | 262 | #endif |
256 | 263 | ||
264 | static struct class_device_attribute *ahci_shost_attrs[] = { | ||
265 | &class_device_attr_link_power_management_policy, | ||
266 | NULL | ||
267 | }; | ||
268 | |||
257 | static struct scsi_host_template ahci_sht = { | 269 | static struct scsi_host_template ahci_sht = { |
258 | .module = THIS_MODULE, | 270 | .module = THIS_MODULE, |
259 | .name = DRV_NAME, | 271 | .name = DRV_NAME, |
@@ -271,6 +283,7 @@ static struct scsi_host_template ahci_sht = { | |||
271 | .slave_configure = ata_scsi_slave_config, | 283 | .slave_configure = ata_scsi_slave_config, |
272 | .slave_destroy = ata_scsi_slave_destroy, | 284 | .slave_destroy = ata_scsi_slave_destroy, |
273 | .bios_param = ata_std_bios_param, | 285 | .bios_param = ata_std_bios_param, |
286 | .shost_attrs = ahci_shost_attrs, | ||
274 | }; | 287 | }; |
275 | 288 | ||
276 | static const struct ata_port_operations ahci_ops = { | 289 | static const struct ata_port_operations ahci_ops = { |
@@ -302,6 +315,8 @@ static const struct ata_port_operations ahci_ops = { | |||
302 | .port_suspend = ahci_port_suspend, | 315 | .port_suspend = ahci_port_suspend, |
303 | .port_resume = ahci_port_resume, | 316 | .port_resume = ahci_port_resume, |
304 | #endif | 317 | #endif |
318 | .enable_pm = ahci_enable_alpm, | ||
319 | .disable_pm = ahci_disable_alpm, | ||
305 | 320 | ||
306 | .port_start = ahci_port_start, | 321 | .port_start = ahci_port_start, |
307 | .port_stop = ahci_port_stop, | 322 | .port_stop = ahci_port_stop, |
@@ -836,6 +851,130 @@ static void ahci_power_up(struct ata_port *ap) | |||
836 | writel(cmd | PORT_CMD_ICC_ACTIVE, port_mmio + PORT_CMD); | 851 | writel(cmd | PORT_CMD_ICC_ACTIVE, port_mmio + PORT_CMD); |
837 | } | 852 | } |
838 | 853 | ||
854 | static void ahci_disable_alpm(struct ata_port *ap) | ||
855 | { | ||
856 | struct ahci_host_priv *hpriv = ap->host->private_data; | ||
857 | void __iomem *port_mmio = ahci_port_base(ap); | ||
858 | u32 cmd; | ||
859 | struct ahci_port_priv *pp = ap->private_data; | ||
860 | |||
861 | /* IPM bits should be disabled by libata-core */ | ||
862 | /* get the existing command bits */ | ||
863 | cmd = readl(port_mmio + PORT_CMD); | ||
864 | |||
865 | /* disable ALPM and ASP */ | ||
866 | cmd &= ~PORT_CMD_ASP; | ||
867 | cmd &= ~PORT_CMD_ALPE; | ||
868 | |||
869 | /* force the interface back to active */ | ||
870 | cmd |= PORT_CMD_ICC_ACTIVE; | ||
871 | |||
872 | /* write out new cmd value */ | ||
873 | writel(cmd, port_mmio + PORT_CMD); | ||
874 | cmd = readl(port_mmio + PORT_CMD); | ||
875 | |||
876 | /* wait 10ms to be sure we've come out of any low power state */ | ||
877 | msleep(10); | ||
878 | |||
879 | /* clear out any PhyRdy stuff from interrupt status */ | ||
880 | writel(PORT_IRQ_PHYRDY, port_mmio + PORT_IRQ_STAT); | ||
881 | |||
882 | /* go ahead and clean out PhyRdy Change from Serror too */ | ||
883 | ahci_scr_write(ap, SCR_ERROR, ((1 << 16) | (1 << 18))); | ||
884 | |||
885 | /* | ||
886 | * Clear flag to indicate that we should ignore all PhyRdy | ||
887 | * state changes | ||
888 | */ | ||
889 | hpriv->flags &= ~AHCI_HFLAG_NO_HOTPLUG; | ||
890 | |||
891 | /* | ||
892 | * Enable interrupts on Phy Ready. | ||
893 | */ | ||
894 | pp->intr_mask |= PORT_IRQ_PHYRDY; | ||
895 | writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK); | ||
896 | |||
897 | /* | ||
898 | * don't change the link pm policy - we can be called | ||
899 | * just to turn of link pm temporarily | ||
900 | */ | ||
901 | } | ||
902 | |||
903 | static int ahci_enable_alpm(struct ata_port *ap, | ||
904 | enum link_pm policy) | ||
905 | { | ||
906 | struct ahci_host_priv *hpriv = ap->host->private_data; | ||
907 | void __iomem *port_mmio = ahci_port_base(ap); | ||
908 | u32 cmd; | ||
909 | struct ahci_port_priv *pp = ap->private_data; | ||
910 | u32 asp; | ||
911 | |||
912 | /* Make sure the host is capable of link power management */ | ||
913 | if (!(hpriv->cap & HOST_CAP_ALPM)) | ||
914 | return -EINVAL; | ||
915 | |||
916 | switch (policy) { | ||
917 | case MAX_PERFORMANCE: | ||
918 | case NOT_AVAILABLE: | ||
919 | /* | ||
920 | * if we came here with NOT_AVAILABLE, | ||
921 | * it just means this is the first time we | ||
922 | * have tried to enable - default to max performance, | ||
923 | * and let the user go to lower power modes on request. | ||
924 | */ | ||
925 | ahci_disable_alpm(ap); | ||
926 | return 0; | ||
927 | case MIN_POWER: | ||
928 | /* configure HBA to enter SLUMBER */ | ||
929 | asp = PORT_CMD_ASP; | ||
930 | break; | ||
931 | case MEDIUM_POWER: | ||
932 | /* configure HBA to enter PARTIAL */ | ||
933 | asp = 0; | ||
934 | break; | ||
935 | default: | ||
936 | return -EINVAL; | ||
937 | } | ||
938 | |||
939 | /* | ||
940 | * Disable interrupts on Phy Ready. This keeps us from | ||
941 | * getting woken up due to spurious phy ready interrupts | ||
942 | * TBD - Hot plug should be done via polling now, is | ||
943 | * that even supported? | ||
944 | */ | ||
945 | pp->intr_mask &= ~PORT_IRQ_PHYRDY; | ||
946 | writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK); | ||
947 | |||
948 | /* | ||
949 | * Set a flag to indicate that we should ignore all PhyRdy | ||
950 | * state changes since these can happen now whenever we | ||
951 | * change link state | ||
952 | */ | ||
953 | hpriv->flags |= AHCI_HFLAG_NO_HOTPLUG; | ||
954 | |||
955 | /* get the existing command bits */ | ||
956 | cmd = readl(port_mmio + PORT_CMD); | ||
957 | |||
958 | /* | ||
959 | * Set ASP based on Policy | ||
960 | */ | ||
961 | cmd |= asp; | ||
962 | |||
963 | /* | ||
964 | * Setting this bit will instruct the HBA to aggressively | ||
965 | * enter a lower power link state when it's appropriate and | ||
966 | * based on the value set above for ASP | ||
967 | */ | ||
968 | cmd |= PORT_CMD_ALPE; | ||
969 | |||
970 | /* write out new cmd value */ | ||
971 | writel(cmd, port_mmio + PORT_CMD); | ||
972 | cmd = readl(port_mmio + PORT_CMD); | ||
973 | |||
974 | /* IPM bits should be set by libata-core */ | ||
975 | return 0; | ||
976 | } | ||
977 | |||
839 | #ifdef CONFIG_PM | 978 | #ifdef CONFIG_PM |
840 | static void ahci_power_down(struct ata_port *ap) | 979 | static void ahci_power_down(struct ata_port *ap) |
841 | { | 980 | { |
@@ -1504,6 +1643,17 @@ static void ahci_port_intr(struct ata_port *ap) | |||
1504 | if (unlikely(resetting)) | 1643 | if (unlikely(resetting)) |
1505 | status &= ~PORT_IRQ_BAD_PMP; | 1644 | status &= ~PORT_IRQ_BAD_PMP; |
1506 | 1645 | ||
1646 | /* If we are getting PhyRdy, this is | ||
1647 | * just a power state change, we should | ||
1648 | * clear out this, plus the PhyRdy/Comm | ||
1649 | * Wake bits from Serror | ||
1650 | */ | ||
1651 | if ((hpriv->flags & AHCI_HFLAG_NO_HOTPLUG) && | ||
1652 | (status & PORT_IRQ_PHYRDY)) { | ||
1653 | status &= ~PORT_IRQ_PHYRDY; | ||
1654 | ahci_scr_write(ap, SCR_ERROR, ((1 << 16) | (1 << 18))); | ||
1655 | } | ||
1656 | |||
1507 | if (unlikely(status & PORT_IRQ_ERROR)) { | 1657 | if (unlikely(status & PORT_IRQ_ERROR)) { |
1508 | ahci_error_intr(ap, status); | 1658 | ahci_error_intr(ap, status); |
1509 | return; | 1659 | return; |
@@ -2151,6 +2301,9 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
2151 | ata_port_pbar_desc(ap, AHCI_PCI_BAR, | 2301 | ata_port_pbar_desc(ap, AHCI_PCI_BAR, |
2152 | 0x100 + ap->port_no * 0x80, "port"); | 2302 | 0x100 + ap->port_no * 0x80, "port"); |
2153 | 2303 | ||
2304 | /* set initial link pm policy */ | ||
2305 | ap->pm_policy = NOT_AVAILABLE; | ||
2306 | |||
2154 | /* standard SATA port setup */ | 2307 | /* standard SATA port setup */ |
2155 | if (hpriv->port_map & (1 << i)) | 2308 | if (hpriv->port_map & (1 << i)) |
2156 | ap->ioaddr.cmd_addr = port_mmio; | 2309 | ap->ioaddr.cmd_addr = port_mmio; |
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index e512903b8dbb..63035d71a61a 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 | ||
623 | static 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 | */ | ||
718 | void 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 | |||
730 | enable_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 | */ | ||
750 | static 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 | |||
759 | void 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 | |||
767 | static 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 | |||
783 | static 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 | ||
@@ -6362,6 +6547,12 @@ int ata_host_suspend(struct ata_host *host, pm_message_t mesg) | |||
6362 | { | 6547 | { |
6363 | int rc; | 6548 | int rc; |
6364 | 6549 | ||
6550 | /* | ||
6551 | * disable link pm on all ports before requesting | ||
6552 | * any pm activity | ||
6553 | */ | ||
6554 | ata_lpm_enable(host); | ||
6555 | |||
6365 | rc = ata_host_request_pm(host, mesg, 0, ATA_EHI_QUIET, 1); | 6556 | rc = ata_host_request_pm(host, mesg, 0, ATA_EHI_QUIET, 1); |
6366 | if (rc == 0) | 6557 | if (rc == 0) |
6367 | host->dev->power.power_state = mesg; | 6558 | host->dev->power.power_state = mesg; |
@@ -6384,6 +6575,9 @@ void ata_host_resume(struct ata_host *host) | |||
6384 | ata_host_request_pm(host, PMSG_ON, ATA_EH_SOFTRESET, | 6575 | ata_host_request_pm(host, PMSG_ON, ATA_EH_SOFTRESET, |
6385 | ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET, 0); | 6576 | ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET, 0); |
6386 | host->dev->power.power_state = PMSG_ON; | 6577 | host->dev->power.power_state = PMSG_ON; |
6578 | |||
6579 | /* reenable link pm */ | ||
6580 | ata_lpm_disable(host); | ||
6387 | } | 6581 | } |
6388 | #endif | 6582 | #endif |
6389 | 6583 | ||
@@ -6926,6 +7120,7 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) | |||
6926 | struct ata_port *ap = host->ports[i]; | 7120 | struct ata_port *ap = host->ports[i]; |
6927 | 7121 | ||
6928 | ata_scsi_scan_host(ap, 1); | 7122 | ata_scsi_scan_host(ap, 1); |
7123 | ata_lpm_schedule(ap, ap->pm_policy); | ||
6929 | } | 7124 | } |
6930 | 7125 | ||
6931 | return 0; | 7126 | return 0; |
@@ -7322,7 +7517,6 @@ const struct ata_port_info ata_dummy_port_info = { | |||
7322 | * likely to change as new drivers are added and updated. | 7517 | * likely to change as new drivers are added and updated. |
7323 | * Do not depend on ABI/API stability. | 7518 | * Do not depend on ABI/API stability. |
7324 | */ | 7519 | */ |
7325 | |||
7326 | EXPORT_SYMBOL_GPL(sata_deb_timing_normal); | 7520 | EXPORT_SYMBOL_GPL(sata_deb_timing_normal); |
7327 | EXPORT_SYMBOL_GPL(sata_deb_timing_hotplug); | 7521 | EXPORT_SYMBOL_GPL(sata_deb_timing_hotplug); |
7328 | EXPORT_SYMBOL_GPL(sata_deb_timing_long); | 7522 | EXPORT_SYMBOL_GPL(sata_deb_timing_long); |
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index ec55d63cf20e..fefea7470e51 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c | |||
@@ -2628,6 +2628,10 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, | |||
2628 | ehc->i.flags &= ~ATA_EHI_SETMODE; | 2628 | ehc->i.flags &= ~ATA_EHI_SETMODE; |
2629 | } | 2629 | } |
2630 | 2630 | ||
2631 | if (ehc->i.action & ATA_EHI_LPM) | ||
2632 | ata_link_for_each_dev(dev, link) | ||
2633 | ata_dev_enable_pm(dev, ap->pm_policy); | ||
2634 | |||
2631 | /* this link is okay now */ | 2635 | /* this link is okay now */ |
2632 | ehc->i.flags = 0; | 2636 | ehc->i.flags = 0; |
2633 | continue; | 2637 | continue; |
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index f752eddc19ed..93bd36c19690 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c | |||
@@ -110,6 +110,74 @@ static struct scsi_transport_template ata_scsi_transport_template = { | |||
110 | }; | 110 | }; |
111 | 111 | ||
112 | 112 | ||
113 | static const struct { | ||
114 | enum link_pm value; | ||
115 | const char *name; | ||
116 | } link_pm_policy[] = { | ||
117 | { NOT_AVAILABLE, "max_performance" }, | ||
118 | { MIN_POWER, "min_power" }, | ||
119 | { MAX_PERFORMANCE, "max_performance" }, | ||
120 | { MEDIUM_POWER, "medium_power" }, | ||
121 | }; | ||
122 | |||
123 | const char *ata_scsi_lpm_get(enum link_pm policy) | ||
124 | { | ||
125 | int i; | ||
126 | |||
127 | for (i = 0; i < ARRAY_SIZE(link_pm_policy); i++) | ||
128 | if (link_pm_policy[i].value == policy) | ||
129 | return link_pm_policy[i].name; | ||
130 | |||
131 | return NULL; | ||
132 | } | ||
133 | |||
134 | static ssize_t ata_scsi_lpm_put(struct class_device *class_dev, | ||
135 | const char *buf, size_t count) | ||
136 | { | ||
137 | struct Scsi_Host *shost = class_to_shost(class_dev); | ||
138 | struct ata_port *ap = ata_shost_to_port(shost); | ||
139 | enum link_pm policy = 0; | ||
140 | int i; | ||
141 | |||
142 | /* | ||
143 | * we are skipping array location 0 on purpose - this | ||
144 | * is because a value of NOT_AVAILABLE is displayed | ||
145 | * to the user as max_performance, but when the user | ||
146 | * writes "max_performance", they actually want the | ||
147 | * value to match MAX_PERFORMANCE. | ||
148 | */ | ||
149 | for (i = 1; i < ARRAY_SIZE(link_pm_policy); i++) { | ||
150 | const int len = strlen(link_pm_policy[i].name); | ||
151 | if (strncmp(link_pm_policy[i].name, buf, len) == 0 && | ||
152 | buf[len] == '\n') { | ||
153 | policy = link_pm_policy[i].value; | ||
154 | break; | ||
155 | } | ||
156 | } | ||
157 | if (!policy) | ||
158 | return -EINVAL; | ||
159 | |||
160 | ata_lpm_schedule(ap, policy); | ||
161 | return count; | ||
162 | } | ||
163 | |||
164 | static ssize_t | ||
165 | ata_scsi_lpm_show(struct class_device *class_dev, char *buf) | ||
166 | { | ||
167 | struct Scsi_Host *shost = class_to_shost(class_dev); | ||
168 | struct ata_port *ap = ata_shost_to_port(shost); | ||
169 | const char *policy = | ||
170 | ata_scsi_lpm_get(ap->pm_policy); | ||
171 | |||
172 | if (!policy) | ||
173 | return -EINVAL; | ||
174 | |||
175 | return snprintf(buf, 23, "%s\n", policy); | ||
176 | } | ||
177 | CLASS_DEVICE_ATTR(link_power_management_policy, S_IRUGO | S_IWUSR, | ||
178 | ata_scsi_lpm_show, ata_scsi_lpm_put); | ||
179 | EXPORT_SYMBOL_GPL(class_device_attr_link_power_management_policy); | ||
180 | |||
113 | static void ata_scsi_invalid_field(struct scsi_cmnd *cmd, | 181 | static void ata_scsi_invalid_field(struct scsi_cmnd *cmd, |
114 | void (*done)(struct scsi_cmnd *)) | 182 | void (*done)(struct scsi_cmnd *)) |
115 | { | 183 | { |
diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 90df58a3edc9..0e6cf3a484dc 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h | |||
@@ -101,6 +101,8 @@ extern int sata_link_init_spd(struct ata_link *link); | |||
101 | extern int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg); | 101 | extern int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg); |
102 | extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg); | 102 | extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg); |
103 | extern struct ata_port *ata_port_alloc(struct ata_host *host); | 103 | extern struct ata_port *ata_port_alloc(struct ata_host *host); |
104 | extern void ata_dev_enable_pm(struct ata_device *dev, enum link_pm policy); | ||
105 | extern void ata_lpm_schedule(struct ata_port *ap, enum link_pm); | ||
104 | 106 | ||
105 | /* libata-acpi.c */ | 107 | /* libata-acpi.c */ |
106 | #ifdef CONFIG_ATA_ACPI | 108 | #ifdef CONFIG_ATA_ACPI |