[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