]> git.pld-linux.org Git - packages/kernel.git/commitdiff
- added AHCI power management cleanups
authorJan Rękorajski <baggins@pld-linux.org>
Tue, 28 Apr 2015 20:07:07 +0000 (22:07 +0200)
committerJan Rękorajski <baggins@pld-linux.org>
Tue, 28 Apr 2015 20:07:07 +0000 (22:07 +0200)
kernel-libata-ahci-pm.patch [new file with mode: 0644]
kernel.spec

diff --git a/kernel-libata-ahci-pm.patch b/kernel-libata-ahci-pm.patch
new file mode 100644 (file)
index 0000000..e203d11
--- /dev/null
@@ -0,0 +1,627 @@
+From:  Matthew Garrett <mjg59@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@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@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@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@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@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@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@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@vger.kernel.org
+More majordomo info at  http://vger.kernel.org/majordomo-info.html
+Please read the FAQ at  http://www.tux.org/lkml/
index faa2b629d87211beb64047e2f78b4cf07b2eb538..94b47a32abacf77e3193ff463b32773b2a8e29d5 100644 (file)
@@ -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
This page took 0.104814 seconds and 4 git commands to generate.