summaryrefslogtreecommitdiff
path: root/drivers/ata/libahci.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ata/libahci.c')
-rw-r--r--drivers/ata/libahci.c34
1 files changed, 23 insertions, 11 deletions
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index 965842a08743..b5f57c69c487 100644
--- a/drivers/ata/libahci.c
+++ b/drivers/ata/libahci.c
@@ -35,6 +35,7 @@
#include <linux/kernel.h>
#include <linux/gfp.h>
#include <linux/module.h>
+#include <linux/nospec.h>
#include <linux/blkdev.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -800,6 +801,8 @@ static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
cmd |= PORT_CMD_ALPE;
if (policy == ATA_LPM_MIN_POWER)
cmd |= PORT_CMD_ASP;
+ else if (policy == ATA_LPM_MIN_POWER_WITH_PARTIAL)
+ cmd &= ~PORT_CMD_ASP;
/* write out new cmd value */
writel(cmd, port_mmio + PORT_CMD);
@@ -810,7 +813,8 @@ 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)
+ if (policy == ATA_LPM_MIN_POWER ||
+ policy == ATA_LPM_MIN_POWER_WITH_PARTIAL)
ahci_set_aggressive_devslp(ap, true);
else
ahci_set_aggressive_devslp(ap, false);
@@ -1146,10 +1150,12 @@ static ssize_t ahci_led_store(struct ata_port *ap, const char *buf,
/* get the slot number from the message */
pmp = (state & EM_MSG_LED_PMP_SLOT) >> 8;
- if (pmp < EM_MAX_SLOTS)
+ if (pmp < EM_MAX_SLOTS) {
+ pmp = array_index_nospec(pmp, EM_MAX_SLOTS);
emp = &pp->em_priv[pmp];
- else
+ } else {
return -EINVAL;
+ }
/* mask off the activity bits if we are in sw_activity
* mode, user should turn off sw_activity before setting
@@ -2104,7 +2110,7 @@ static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep)
struct ahci_host_priv *hpriv = ap->host->private_data;
void __iomem *port_mmio = ahci_port_base(ap);
struct ata_device *dev = ap->link.device;
- u32 devslp, dm, dito, mdat, deto;
+ u32 devslp, dm, dito, mdat, deto, dito_conf;
int rc;
unsigned int err_mask;
@@ -2128,8 +2134,15 @@ static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep)
return;
}
- /* device sleep was already enabled */
- if (devslp & PORT_DEVSLP_ADSE)
+ dm = (devslp & PORT_DEVSLP_DM_MASK) >> PORT_DEVSLP_DM_OFFSET;
+ dito = devslp_idle_timeout / (dm + 1);
+ if (dito > 0x3ff)
+ dito = 0x3ff;
+
+ dito_conf = (devslp >> PORT_DEVSLP_DITO_OFFSET) & 0x3FF;
+
+ /* device sleep was already enabled and same dito */
+ if ((devslp & PORT_DEVSLP_ADSE) && (dito_conf == dito))
return;
/* set DITO, MDAT, DETO and enable DevSlp, need to stop engine first */
@@ -2137,11 +2150,6 @@ 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;
-
/* Use the nominal value 10 ms if the read MDAT is zero,
* the nominal value of DETO is 20 ms.
*/
@@ -2159,6 +2167,8 @@ static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep)
deto = 20;
}
+ /* Make dito, mdat, deto bits to 0s */
+ devslp &= ~GENMASK_ULL(24, 2);
devslp |= ((dito << PORT_DEVSLP_DITO_OFFSET) |
(mdat << PORT_DEVSLP_MDAT_OFFSET) |
(deto << PORT_DEVSLP_DETO_OFFSET) |
@@ -2436,6 +2446,8 @@ static void ahci_port_stop(struct ata_port *ap)
* re-enabling INTx.
*/
writel(1 << ap->port_no, host_mmio + HOST_IRQ_STAT);
+
+ ahci_rpm_put_port(ap);
}
void ahci_print_info(struct ata_host *host, const char *scc_s)