aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKristen Carlson Accardi <kristen.c.accardi@intel.com>2007-10-25 01:33:26 -0400
committerJeff Garzik <jeff@garzik.org>2007-10-29 11:00:35 -0400
commit31556594f913fa81d008cecfe46d7211c919a853 (patch)
tree424db37711a0805aef50f6e76b8686eb36ab5147
parentca77329fb713b7fea6a307068e0dd0248e7aa640 (diff)
[libata] AHCI: add hw link power management support
This patch will set the correct bits to turn on Aggressive Link Power Management (ALPM) for the ahci driver. This will cause the controller and disk to negotiate a lower power state for the link when there is no activity (see the AHCI 1.x spec for details). This feature is mutually exclusive with Hot Plug, so when ALPM is enabled, Hot Plug is disabled. ALPM will be enabled by default, but it is settable via the scsi host syfs interface. Possible settings for this feature are: Setting Effect ---------------------------------------------------------- min_power ALPM is enabled, and link set to enter lowest power state (SLUMBER) when idle Hot plug not allowed. max_performance ALPM is disabled, Hot Plug is allowed medium_power ALPM is enabled, and link set to enter second lowest power state (PARTIAL) when idle. Hot plug not allowed. Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@intel.com> Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
-rw-r--r--drivers/ata/ahci.c157
1 files changed, 155 insertions, 2 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
52static int ahci_enable_alpm(struct ata_port *ap,
53 enum link_pm policy);
54static void ahci_disable_alpm(struct ata_port *ap);
52 55
53enum { 56enum {
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);
254static int ahci_pci_device_resume(struct pci_dev *pdev); 261static int ahci_pci_device_resume(struct pci_dev *pdev);
255#endif 262#endif
256 263
264static struct class_device_attribute *ahci_shost_attrs[] = {
265 &class_device_attr_link_power_management_policy,
266 NULL
267};
268
257static struct scsi_host_template ahci_sht = { 269static 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
276static const struct ata_port_operations ahci_ops = { 289static 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
854static 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
903static 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
840static void ahci_power_down(struct ata_port *ap) 979static 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;