From 2155c5a28cf9b651b5371c81dfa869a42c4215e7 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jan=20R=C4=99korajski?= Date: Tue, 28 Apr 2015 22:07:07 +0200 Subject: [PATCH] - added AHCI power management cleanups --- kernel-libata-ahci-pm.patch | 627 ++++++++++++++++++++++++++++++++++++ kernel.spec | 2 + 2 files changed, 629 insertions(+) create mode 100644 kernel-libata-ahci-pm.patch diff --git a/kernel-libata-ahci-pm.patch b/kernel-libata-ahci-pm.patch new file mode 100644 index 00000000..e203d113 --- /dev/null +++ b/kernel-libata-ahci-pm.patch @@ -0,0 +1,627 @@ +From: Matthew Garrett +Date: Sat, 18 Apr 2015 08:26:34 -0700 +Subject: [PATCH 1/3] libata: Stash initial power management configuration + +The initial configuration for power management settings may be a result of +the firmware using its knowledge of the installed hardware to program +reasonable defaults. Stash as much of it as possible for later use. + +Signed-off-by: Matthew Garrett +--- + drivers/ata/acard-ahci.c | 3 +++ + drivers/ata/ahci.c | 3 +++ + drivers/ata/ahci.h | 7 +++++++ + drivers/ata/libahci.c | 46 +++++++++++++++++++++++++++++++++++------- + drivers/ata/libahci_platform.c | 4 ++++ + drivers/ata/libata-core.c | 19 ++++++++++------- + drivers/ata/libata-pmp.c | 2 +- + drivers/ata/libata.h | 2 +- + drivers/ata/sata_highbank.c | 4 ++++ + include/linux/libata.h | 3 +++ + 10 files changed, 77 insertions(+), 16 deletions(-) + +diff --git a/drivers/ata/acard-ahci.c b/drivers/ata/acard-ahci.c +index 12489ce..0029229 100644 +--- a/drivers/ata/acard-ahci.c ++++ b/drivers/ata/acard-ahci.c +@@ -476,6 +476,9 @@ static int acard_ahci_init_one(struct pci_dev *pdev, const struct pci_device_id + ata_port_pbar_desc(ap, AHCI_PCI_BAR, + 0x100 + ap->port_no * 0x80, "port"); + ++ rc = ahci_setup_port_privdata(ap); ++ if (rc) ++ return rc; + /* set initial link pm policy */ + /* + ap->pm_policy = NOT_AVAILABLE; +diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c +index c7a92a7..0f875e2 100644 +--- a/drivers/ata/ahci.c ++++ b/drivers/ata/ahci.c +@@ -1436,6 +1436,9 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) + if (ap->flags & ATA_FLAG_EM) + ap->em_message_type = hpriv->em_msg_type; + ++ rc = ahci_setup_port_privdata(ap); ++ if (rc) ++ return rc; + + /* disabled/not-implemented port */ + if (!(hpriv->port_map & (1 << i))) +diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h +index 71262e0..c1a4b6a 100644 +--- a/drivers/ata/ahci.h ++++ b/drivers/ata/ahci.h +@@ -313,6 +313,12 @@ struct ahci_port_priv { + /* enclosure management info per PM slot */ + struct ahci_em_priv em_priv[EM_MAX_SLOTS]; + char *irq_desc; /* desc in /proc/interrupts */ ++ bool init_alpe; /* alpe enabled by default */ ++ bool init_asp; /* asp enabled by default */ ++ bool init_devslp; /* devslp enabled by default */ ++ u32 init_dito; /* initial dito configuration */ ++ u32 init_deto; /* initial deto configuration */ ++ u32 init_mdat; /* initial mdat configuration */ + }; + + struct ahci_host_priv { +@@ -371,6 +377,7 @@ extern struct ata_port_operations ahci_platform_ops; + extern struct ata_port_operations ahci_pmp_retry_srst_ops; + + unsigned int ahci_dev_classify(struct ata_port *ap); ++int ahci_setup_port_privdata(struct ata_port *ap); + void ahci_fill_cmd_slot(struct ahci_port_priv *pp, unsigned int tag, + u32 opts); + void ahci_save_initial_config(struct device *dev, +diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c +index 61a9c07..f0c7120 100644 +--- a/drivers/ata/libahci.c ++++ b/drivers/ata/libahci.c +@@ -2212,19 +2212,53 @@ static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg) + } + #endif + ++/* ++ * Allocate port privdata and read back initial power management configuration ++ */ ++int ahci_setup_port_privdata(struct ata_port *ap) ++{ ++ struct ahci_port_priv *pp; ++ u32 cmd, devslp; ++ void __iomem *port_mmio = ahci_port_base(ap); ++ ++ pp = kzalloc(sizeof(*pp), GFP_KERNEL); ++ if (!pp) ++ return -ENOMEM; ++ ++ ap->private_data = pp; ++ ++ cmd = readl(port_mmio + PORT_CMD); ++ ++ if (cmd & PORT_CMD_ALPE) ++ pp->init_alpe = true; ++ ++ if (cmd & PORT_CMD_ASP) ++ pp->init_asp = true; ++ ++ devslp = readl(port_mmio + PORT_DEVSLP); ++ ++ /* devslp unsupported or disabled */ ++ if (!(devslp & PORT_DEVSLP_DSP) || !(devslp & PORT_DEVSLP_ADSE)) ++ return 0; ++ ++ pp->init_devslp = true; ++ pp->init_dito = (devslp >> PORT_DEVSLP_DITO_OFFSET) & 0x3ff; ++ pp->init_deto = (devslp >> PORT_DEVSLP_DETO_OFFSET) & 0xff; ++ pp->init_mdat = (devslp >> PORT_DEVSLP_MDAT_OFFSET) & 0x1f; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(ahci_setup_port_privdata); ++ + static int ahci_port_start(struct ata_port *ap) + { + struct ahci_host_priv *hpriv = ap->host->private_data; ++ struct ahci_port_priv *pp = ap->private_data; + struct device *dev = ap->host->dev; +- struct ahci_port_priv *pp; + void *mem; + dma_addr_t mem_dma; + size_t dma_sz, rx_fis_sz; + +- pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL); +- if (!pp) +- return -ENOMEM; +- + if (ap->host->n_ports > 1) { + pp->irq_desc = devm_kzalloc(dev, 8, GFP_KERNEL); + if (!pp->irq_desc) { +@@ -2303,8 +2337,6 @@ static int ahci_port_start(struct ata_port *ap) + ap->lock = &pp->lock; + } + +- ap->private_data = pp; +- + /* engage engines, captain */ + return ahci_port_resume(ap); + } +diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c +index d89305d..39946d4 100644 +--- a/drivers/ata/libahci_platform.c ++++ b/drivers/ata/libahci_platform.c +@@ -563,6 +563,10 @@ int ahci_platform_init_host(struct platform_device *pdev, + if (ap->flags & ATA_FLAG_EM) + ap->em_message_type = hpriv->em_msg_type; + ++ rc = ahci_setup_port_privdata(ap); ++ if (rc) ++ return rc; ++ + /* disabled/not-implemented port */ + if (!(hpriv->port_map & (1 << i))) + ap->ops = &ata_dummy_port_ops; +diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c +index f6cb1f1..c037f33 100644 +--- a/drivers/ata/libata-core.c ++++ b/drivers/ata/libata-core.c +@@ -2024,6 +2024,9 @@ retry: + } + } + ++ if (id[79] & SATA_DIPM) ++ dev->init_dipm = true; ++ + *p_class = class; + + return 0; +@@ -5583,11 +5586,11 @@ void ata_link_init(struct ata_port *ap, struct ata_link *link, int pmp) + } + + /** +- * sata_link_init_spd - Initialize link->sata_spd_limit +- * @link: Link to configure sata_spd_limit for ++ * sata_link_init_config - Initialize link->sata_spd_limit and init_lpm ++ * @link: Link to configure sata_spd_limit and init_lpm for + * +- * Initialize @link->[hw_]sata_spd_limit to the currently +- * configured value. ++ * Initialize @link->[hw_]sata_spd_limit and @link->init_lpm to the ++ * currently configured value. + * + * LOCKING: + * Kernel thread context (may sleep). +@@ -5595,7 +5598,7 @@ void ata_link_init(struct ata_port *ap, struct ata_link *link, int pmp) + * RETURNS: + * 0 on success, -errno on failure. + */ +-int sata_link_init_spd(struct ata_link *link) ++int sata_link_init_config(struct ata_link *link) + { + u8 spd; + int rc; +@@ -5612,6 +5615,8 @@ int sata_link_init_spd(struct ata_link *link) + + link->sata_spd_limit = link->hw_sata_spd_limit; + ++ link->init_lpm = (link->saved_scontrol >> 8) & 0x7; ++ + return 0; + } + +@@ -6161,9 +6166,9 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) + ap->cbl = ATA_CBL_SATA; + + /* init sata_spd_limit to the current value */ +- sata_link_init_spd(&ap->link); ++ sata_link_init_config(&ap->link); + if (ap->slave_link) +- sata_link_init_spd(ap->slave_link); ++ sata_link_init_config(ap->slave_link); + + /* print per-port info to dmesg */ + xfer_mask = ata_pack_xfermask(ap->pio_mask, ap->mwdma_mask, +diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c +index 7ccc084..b9f7ce8 100644 +--- a/drivers/ata/libata-pmp.c ++++ b/drivers/ata/libata-pmp.c +@@ -531,7 +531,7 @@ int sata_pmp_attach(struct ata_device *dev) + ap->ops->pmp_attach(ap); + + ata_for_each_link(tlink, ap, EDGE) +- sata_link_init_spd(tlink); ++ sata_link_init_config(tlink); + + return 0; + +diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h +index a998a17..35016d6 100644 +--- a/drivers/ata/libata.h ++++ b/drivers/ata/libata.h +@@ -99,7 +99,7 @@ extern bool ata_phys_link_online(struct ata_link *link); + extern bool ata_phys_link_offline(struct ata_link *link); + extern void ata_dev_init(struct ata_device *dev); + extern void ata_link_init(struct ata_port *ap, struct ata_link *link, int pmp); +-extern int sata_link_init_spd(struct ata_link *link); ++extern int sata_link_init_config(struct ata_link *link); + extern int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg); + extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg); + extern struct ata_port *ata_port_alloc(struct ata_host *host); +diff --git a/drivers/ata/sata_highbank.c b/drivers/ata/sata_highbank.c +index 24e311f..a2adf3f 100644 +--- a/drivers/ata/sata_highbank.c ++++ b/drivers/ata/sata_highbank.c +@@ -556,6 +556,10 @@ static int ahci_highbank_probe(struct platform_device *pdev) + if (ap->flags & ATA_FLAG_EM) + ap->em_message_type = hpriv->em_msg_type; + ++ rc = ahci_setup_port_privdata(ap); ++ if (rc) ++ goto err0; ++ + /* disabled/not-implemented port */ + if (!(hpriv->port_map & (1 << i))) + ap->ops = &ata_dummy_port_ops; +diff --git a/include/linux/libata.h b/include/linux/libata.h +index 8dad4a3..31c149b 100644 +--- a/include/linux/libata.h ++++ b/include/linux/libata.h +@@ -719,6 +719,8 @@ struct ata_device { + int spdn_cnt; + /* ering is CLEAR_END, read comment above CLEAR_END */ + struct ata_ering ering; ++ /* Initial DIPM configuration */ ++ bool init_dipm; + }; + + /* Fields between ATA_DEVICE_CLEAR_BEGIN and ATA_DEVICE_CLEAR_END are +@@ -788,6 +790,7 @@ struct ata_link { + struct ata_eh_context eh_context; + + struct ata_device device[ATA_MAX_DEVICES]; ++ u8 init_lpm; /* initial lpm configuration */ + }; + #define ATA_LINK_CLEAR_BEGIN offsetof(struct ata_link, active_tag) + #define ATA_LINK_CLEAR_END offsetof(struct ata_link, device[0]) +-- +2.3.5 + +-- +To unsubscribe from this list: send the line "unsubscribe linux-kernel" in +the body of a message to majordomo@vger.kernel.org +More majordomo info at http://vger.kernel.org/majordomo-info.html +Please read the FAQ at http://www.tux.org/lkml/ +From: Matthew Garrett +Date: Sat, 18 Apr 2015 08:26:35 -0700 +Subject: [PATCH 2/3] libata: Add firmware_default LPM policy + +System vendors may configure SATA link power management appropriately for +their systems in firmware, based on their knowledge of the hardware +installed. Add an additional LPM policy designed to inherit the +configuration provided by the firmware. + +Signed-off-by: Matthew Garrett +--- + .../scsi/link_power_management_policy.txt | 5 +- + drivers/ata/libahci.c | 62 +++++++++++++++------- + drivers/ata/libata-core.c | 7 ++- + drivers/ata/libata-eh.c | 15 +++--- + drivers/ata/libata-scsi.c | 1 + + include/linux/libata.h | 1 + + 6 files changed, 63 insertions(+), 28 deletions(-) + +diff --git a/Documentation/scsi/link_power_management_policy.txt b/Documentation/scsi/link_power_management_policy.txt +index d18993d..0285601 100644 +--- a/Documentation/scsi/link_power_management_policy.txt ++++ b/Documentation/scsi/link_power_management_policy.txt +@@ -1,8 +1,11 @@ + This parameter allows the user to set the link (interface) power management. +-There are 3 possible options: ++There are 4 possible options: + + Value Effect + ---------------------------------------------------------------------------- ++firmware_defaults Inherit configuration from the state programmed by ++ the firmware during system init. ++ + min_power Tell the controller to try to make the link use the + least possible power when possible. This may + sacrifice some performance due to increased latency +diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c +index f0c7120..fabcff4 100644 +--- a/drivers/ata/libahci.c ++++ b/drivers/ata/libahci.c +@@ -684,6 +684,7 @@ static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, + { + struct ata_port *ap = link->ap; + struct ahci_host_priv *hpriv = ap->host->private_data; ++ struct ahci_port_priv *ppriv = ap->private_data; + struct ahci_port_priv *pp = ap->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + +@@ -701,9 +702,9 @@ static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, + + if (hpriv->cap & HOST_CAP_ALPM) { + u32 cmd = readl(port_mmio + PORT_CMD); ++ cmd &= ~(PORT_CMD_ASP | PORT_CMD_ALPE); + + if (policy == ATA_LPM_MAX_POWER || !(hints & ATA_LPM_HIPM)) { +- cmd &= ~(PORT_CMD_ASP | PORT_CMD_ALPE); + cmd |= PORT_CMD_ICC_ACTIVE; + + writel(cmd, port_mmio + PORT_CMD); +@@ -711,6 +712,13 @@ static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, + + /* wait 10ms to be sure we've come out of LPM state */ + ata_msleep(ap, 10); ++ } else if (policy == ATA_LPM_FIRMWARE_DEFAULTS) { ++ if (ppriv->init_alpe) ++ cmd |= PORT_CMD_ALPE; ++ if (ppriv->init_asp) ++ cmd |= PORT_CMD_ASP; ++ ++ writel(cmd, port_mmio + PORT_CMD); + } else { + cmd |= PORT_CMD_ALPE; + if (policy == ATA_LPM_MIN_POWER) +@@ -725,10 +733,17 @@ static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, + if ((hpriv->cap2 & HOST_CAP2_SDS) && + (hpriv->cap2 & HOST_CAP2_SADM) && + (link->device->flags & ATA_DFLAG_DEVSLP)) { +- if (policy == ATA_LPM_MIN_POWER) ++ switch (policy) { ++ case ATA_LPM_MIN_POWER: + ahci_set_aggressive_devslp(ap, true); +- else ++ break; ++ case ATA_LPM_FIRMWARE_DEFAULTS: ++ ahci_set_aggressive_devslp(ap, ppriv->init_devslp); ++ break; ++ default: + ahci_set_aggressive_devslp(ap, false); ++ break; ++ } + } + + if (policy == ATA_LPM_MAX_POWER) { +@@ -1995,6 +2010,7 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc) + static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep) + { + struct ahci_host_priv *hpriv = ap->host->private_data; ++ struct ahci_port_priv *ppriv = ap->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + struct ata_device *dev = ap->link.device; + u32 devslp, dm, dito, mdat, deto; +@@ -2030,26 +2046,32 @@ static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep) + if (rc) + return; + +- dm = (devslp & PORT_DEVSLP_DM_MASK) >> PORT_DEVSLP_DM_OFFSET; +- dito = devslp_idle_timeout / (dm + 1); +- if (dito > 0x3ff) +- dito = 0x3ff; ++ if (ppriv->init_devslp) { ++ dito = ppriv->init_dito; ++ deto = ppriv->init_deto; ++ mdat = ppriv->init_mdat; ++ } else { ++ dm = (devslp & PORT_DEVSLP_DM_MASK) >> PORT_DEVSLP_DM_OFFSET; ++ dito = devslp_idle_timeout / (dm + 1); ++ if (dito > 0x3ff) ++ dito = 0x3ff; + +- /* Use the nominal value 10 ms if the read MDAT is zero, +- * the nominal value of DETO is 20 ms. +- */ +- if (dev->devslp_timing[ATA_LOG_DEVSLP_VALID] & +- ATA_LOG_DEVSLP_VALID_MASK) { +- mdat = dev->devslp_timing[ATA_LOG_DEVSLP_MDAT] & +- ATA_LOG_DEVSLP_MDAT_MASK; +- if (!mdat) ++ /* Use the nominal value 10 ms if the read MDAT is zero, ++ * the nominal value of DETO is 20 ms. ++ */ ++ if (dev->devslp_timing[ATA_LOG_DEVSLP_VALID] & ++ ATA_LOG_DEVSLP_VALID_MASK) { ++ mdat = dev->devslp_timing[ATA_LOG_DEVSLP_MDAT] & ++ ATA_LOG_DEVSLP_MDAT_MASK; ++ if (!mdat) ++ mdat = 10; ++ deto = dev->devslp_timing[ATA_LOG_DEVSLP_DETO]; ++ if (!deto) ++ deto = 20; ++ } else { + mdat = 10; +- deto = dev->devslp_timing[ATA_LOG_DEVSLP_DETO]; +- if (!deto) + deto = 20; +- } else { +- mdat = 10; +- deto = 20; ++ } + } + + devslp |= ((dito << PORT_DEVSLP_DITO_OFFSET) | +diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c +index c037f33..0a78f01 100644 +--- a/drivers/ata/libata-core.c ++++ b/drivers/ata/libata-core.c +@@ -2024,7 +2024,7 @@ retry: + } + } + +- if (id[79] & SATA_DIPM) ++ if (id[79] & (1 << SATA_DIPM)) + dev->init_dipm = true; + + *p_class = class; +@@ -3672,6 +3672,11 @@ int sata_link_scr_lpm(struct ata_link *link, enum ata_lpm_policy policy, + return rc; + + switch (policy) { ++ case ATA_LPM_FIRMWARE_DEFAULTS: ++ /* use the values we read at probe */ ++ scontrol &= ~(0x7 << 8); ++ scontrol |= (link->init_lpm << 8); ++ break; + case ATA_LPM_MAX_POWER: + /* disable all LPM transitions */ + scontrol |= (0x7 << 8); +diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c +index 07f41be..c36fa56 100644 +--- a/drivers/ata/libata-eh.c ++++ b/drivers/ata/libata-eh.c +@@ -3519,9 +3519,9 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, + return 0; + + /* +- * DIPM is enabled only for MIN_POWER as some devices +- * misbehave when the host NACKs transition to SLUMBER. Order +- * device and link configurations such that the host always ++ * DIPM is enabled only for MIN_POWER and FIRMWARE_DEFAULT as some ++ * devices misbehave when the host NACKs transition to SLUMBER. ++ * Order device and link configurations such that the host always + * allows DIPM requests. + */ + ata_for_each_dev(dev, link, ENABLED) { +@@ -3581,10 +3581,13 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, + if (ap && ap->slave_link) + ap->slave_link->lpm_policy = policy; + +- /* host config updated, enable DIPM if transitioning to MIN_POWER */ ++ /* host config updated, enable DIPM if transitioning to MIN_POWER or ++ * FIRMWARE_DEFAULT when enabled by firmware ++ */ + ata_for_each_dev(dev, link, ENABLED) { +- if (policy == ATA_LPM_MIN_POWER && !no_dipm && +- ata_id_has_dipm(dev->id)) { ++ if ((policy == ATA_LPM_MIN_POWER && !no_dipm && ++ ata_id_has_dipm(dev->id)) || ++ (policy == ATA_LPM_FIRMWARE_DEFAULTS && dev->init_dipm)) { + err_mask = ata_dev_set_feature(dev, + SETFEATURES_SATA_ENABLE, SATA_DIPM); + if (err_mask && err_mask != AC_ERR_DEV) { +diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c +index 3131adc..f1ea052 100644 +--- a/drivers/ata/libata-scsi.c ++++ b/drivers/ata/libata-scsi.c +@@ -107,6 +107,7 @@ static const u8 def_control_mpage[CONTROL_MPAGE_LEN] = { + static const char *ata_lpm_policy_names[] = { + [ATA_LPM_UNKNOWN] = "max_performance", + [ATA_LPM_MAX_POWER] = "max_performance", ++ [ATA_LPM_FIRMWARE_DEFAULTS] = "firmware_defaults", + [ATA_LPM_MED_POWER] = "medium_power", + [ATA_LPM_MIN_POWER] = "min_power", + }; +diff --git a/include/linux/libata.h b/include/linux/libata.h +index 31c149b..57b465d 100644 +--- a/include/linux/libata.h ++++ b/include/linux/libata.h +@@ -507,6 +507,7 @@ enum ata_completion_errors { + enum ata_lpm_policy { + ATA_LPM_UNKNOWN, + ATA_LPM_MAX_POWER, ++ ATA_LPM_FIRMWARE_DEFAULTS, + ATA_LPM_MED_POWER, + ATA_LPM_MIN_POWER, + }; +-- +2.3.5 + +-- +To unsubscribe from this list: send the line "unsubscribe linux-kernel" in +the body of a message to majordomo@vger.kernel.org +More majordomo info at http://vger.kernel.org/majordomo-info.html +Please read the FAQ at http://www.tux.org/lkml/ +From: Matthew Garrett +Date: Sat, 18 Apr 2015 08:26:36 -0700 +Subject: [PATCH 3/3] libata: Change medium_power LPM policy to match Intel recommendations + +Intel publish a document on designing energy efficient SATA devices at +http://www.intel.com/content/dam/doc/reference-guide/sata-devices-implementation-recommendations.pdf +which recommends that ALPE be set, ASPE be cleared and that DIPM be enabled +on the device. Right now we have no policy that matches that - medium_power +does not enable DIPM and min_power sets ASPE. Change medium_power to +implement these recommendations, with the addition of devslp state being +inherited from the initial configuration. With luck this will provide +reasonable power savings without causing the device breakages we +occasionally see with the min_power policy. + +Signed-off-by: Matthew Garrett +--- + drivers/ata/libahci.c | 1 + + drivers/ata/libata-core.c | 4 ---- + drivers/ata/libata-eh.c | 10 ++++------ + 3 files changed, 5 insertions(+), 10 deletions(-) + +diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c +index fabcff4..8efacb9 100644 +--- a/drivers/ata/libahci.c ++++ b/drivers/ata/libahci.c +@@ -738,6 +738,7 @@ static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, + ahci_set_aggressive_devslp(ap, true); + break; + case ATA_LPM_FIRMWARE_DEFAULTS: ++ case ATA_LPM_MED_POWER: + ahci_set_aggressive_devslp(ap, ppriv->init_devslp); + break; + default: +diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c +index 0a78f01..99a7b8f 100644 +--- a/drivers/ata/libata-core.c ++++ b/drivers/ata/libata-core.c +@@ -3687,10 +3687,6 @@ int sata_link_scr_lpm(struct ata_link *link, enum ata_lpm_policy policy, + } + break; + case ATA_LPM_MED_POWER: +- /* allow LPM to PARTIAL */ +- scontrol &= ~(0x1 << 8); +- scontrol |= (0x6 << 8); +- break; + case ATA_LPM_MIN_POWER: + if (ata_link_nr_enabled(link) > 0) + /* no restrictions on LPM transitions */ +diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c +index c36fa56..25d5f37 100644 +--- a/drivers/ata/libata-eh.c ++++ b/drivers/ata/libata-eh.c +@@ -3519,8 +3519,6 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, + return 0; + + /* +- * DIPM is enabled only for MIN_POWER and FIRMWARE_DEFAULT as some +- * devices misbehave when the host NACKs transition to SLUMBER. + * Order device and link configurations such that the host always + * allows DIPM requests. + */ +@@ -3540,7 +3538,7 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, + hints &= ~ATA_LPM_HIPM; + + /* disable DIPM before changing link config */ +- if (policy != ATA_LPM_MIN_POWER && dipm) { ++ if (policy < ATA_LPM_MED_POWER && dipm) { + err_mask = ata_dev_set_feature(dev, + SETFEATURES_SATA_DISABLE, SATA_DIPM); + if (err_mask && err_mask != AC_ERR_DEV) { +@@ -3581,11 +3579,11 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, + if (ap && ap->slave_link) + ap->slave_link->lpm_policy = policy; + +- /* host config updated, enable DIPM if transitioning to MIN_POWER or +- * FIRMWARE_DEFAULT when enabled by firmware ++ /* host config updated, enable DIPM if transitioning to MED_POWER, ++ * MIN_POWER or FIRMWARE_DEFAULT when enabled by firmware + */ + ata_for_each_dev(dev, link, ENABLED) { +- if ((policy == ATA_LPM_MIN_POWER && !no_dipm && ++ if ((policy >= ATA_LPM_MED_POWER && !no_dipm && + ata_id_has_dipm(dev->id)) || + (policy == ATA_LPM_FIRMWARE_DEFAULTS && dev->init_dipm)) { + err_mask = ata_dev_set_feature(dev, +-- +2.3.5 + +-- +To unsubscribe from this list: send the line "unsubscribe linux-kernel" in +the body of a message to majordomo@vger.kernel.org +More majordomo info at http://vger.kernel.org/majordomo-info.html +Please read the FAQ at http://www.tux.org/lkml/ diff --git a/kernel.spec b/kernel.spec index faa2b629..94b47a32 100644 --- a/kernel.spec +++ b/kernel.spec @@ -234,6 +234,7 @@ Patch505: dm-crypt-sort-requests.patch Patch2000: kernel-small_fixes.patch Patch2001: kernel-pwc-uncompress.patch Patch2003: kernel-regressions.patch +Patch2004: kernel-libata-ahci-pm.patch # bzr co lp:apparmor # ls apparmor/kernel-patches/3.12 @@ -750,6 +751,7 @@ cd linux-%{basever} %patch2000 -p1 %patch2001 -p1 #%patch2003 -p1 +%patch2004 -p1 # Do not remove this, please! #%%patch50000 -p1 -- 2.44.0