[packages/kernel] - added AHCI power management cleanups
baggins
baggins at pld-linux.org
Tue Apr 28 22:07:34 CEST 2015
commit 2155c5a28cf9b651b5371c81dfa869a42c4215e7
Author: Jan Rękorajski <baggins at pld-linux.org>
Date: Tue Apr 28 22:07:07 2015 +0200
- added AHCI power management cleanups
kernel-libata-ahci-pm.patch | 627 ++++++++++++++++++++++++++++++++++++++++++++
kernel.spec | 2 +
2 files changed, 629 insertions(+)
---
diff --git a/kernel.spec b/kernel.spec
index faa2b62..94b47a3 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
diff --git a/kernel-libata-ahci-pm.patch b/kernel-libata-ahci-pm.patch
new file mode 100644
index 0000000..e203d11
--- /dev/null
+++ b/kernel-libata-ahci-pm.patch
@@ -0,0 +1,627 @@
+From: Matthew Garrett <mjg59 at coreos.com>
+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 <mjg59 at srcf.ucam.org>
+---
+ 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 at 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 <mjg59 at coreos.com>
+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 <mjg59 at coreos.com>
+---
+ .../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 at 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 <mjg59 at coreos.com>
+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 <mjg59 at coreos.com>
+---
+ 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 at vger.kernel.org
+More majordomo info at http://vger.kernel.org/majordomo-info.html
+Please read the FAQ at http://www.tux.org/lkml/
================================================================
---- gitweb:
http://git.pld-linux.org/gitweb.cgi/packages/kernel.git/commitdiff/2155c5a28cf9b651b5371c81dfa869a42c4215e7
More information about the pld-cvs-commit
mailing list