@@ -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
@@ -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) |
@@ -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);
@@ -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) {
@@ -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",
};
@@ -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,
};
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(-)