]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
ata: libata: disallow dev-initiated LPM transitions to unsupported states
authorNiklas Cassel <niklas.cassel@wdc.com>
Mon, 4 Sep 2023 20:42:56 +0000 (22:42 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 10 Oct 2023 19:43:40 +0000 (21:43 +0200)
commit 24e0e61db3cb86a66824531989f1df80e0939f26 upstream.

In AHCI 1.3.1, the register description for CAP.SSC:
"When cleared to ‘0’, software must not allow the HBA to initiate
transitions to the Slumber state via agressive link power management nor
the PxCMD.ICC field in each port, and the PxSCTL.IPM field in each port
must be programmed to disallow device initiated Slumber requests."

In AHCI 1.3.1, the register description for CAP.PSC:
"When cleared to ‘0’, software must not allow the HBA to initiate
transitions to the Partial state via agressive link power management nor
the PxCMD.ICC field in each port, and the PxSCTL.IPM field in each port
must be programmed to disallow device initiated Partial requests."

Ensure that we always set the corresponding bits in PxSCTL.IPM, such that
a device is not allowed to initiate transitions to power states which are
unsupported by the HBA.

DevSleep is always initiated by the HBA, however, for completeness, set the
corresponding bit in PxSCTL.IPM such that agressive link power management
cannot transition to DevSleep if DevSleep is not supported.

sata_link_scr_lpm() is used by libahci, ata_piix and libata-pmp.
However, only libahci has the ability to read the CAP/CAP2 register to see
if these features are supported. Therefore, in order to not introduce any
regressions on ata_piix or libata-pmp, create flags that indicate that the
respective feature is NOT supported. This way, the behavior for ata_piix
and libata-pmp should remain unchanged.

This change is based on a patch originally submitted by Runa Guo-oc.

Signed-off-by: Niklas Cassel <niklas.cassel@wdc.com>
Fixes: 1152b2617a6e ("libata: implement sata_link_scr_lpm() and make ata_dev_set_feature() global")
Cc: stable@vger.kernel.org
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
Signed-off-by: Niklas Cassel <niklas.cassel@wdc.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/ata/ahci.c
drivers/ata/libata-core.c
include/linux/libata.h

index 70930eb1e3286c7810d679531e806b35bcf6abe3..5b810a290b93c10271e39bd6ff59cf96e8cdc22c 100644 (file)
@@ -1775,6 +1775,15 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        else
                dev_info(&pdev->dev, "SSS flag set, parallel bus scan disabled\n");
 
+       if (!(hpriv->cap & HOST_CAP_PART))
+               host->flags |= ATA_HOST_NO_PART;
+
+       if (!(hpriv->cap & HOST_CAP_SSC))
+               host->flags |= ATA_HOST_NO_SSC;
+
+       if (!(hpriv->cap2 & HOST_CAP2_SDS))
+               host->flags |= ATA_HOST_NO_DEVSLP;
+
        if (pi.flags & ATA_FLAG_EM)
                ahci_reset_em(host);
 
index b8f770aed140306441bb81d9d484e7061d506aa2..d17107cb1e09c3ec5da2db71fbab570effaa0d34 100644 (file)
@@ -3993,10 +3993,23 @@ int sata_link_scr_lpm(struct ata_link *link, enum ata_lpm_policy policy,
                scontrol |= (0x6 << 8);
                break;
        case ATA_LPM_MIN_POWER:
-               if (ata_link_nr_enabled(link) > 0)
-                       /* no restrictions on LPM transitions */
+               if (ata_link_nr_enabled(link) > 0) {
+                       /* assume no restrictions on LPM transitions */
                        scontrol &= ~(0x7 << 8);
-               else {
+
+                       /*
+                        * If the controller does not support partial, slumber,
+                        * or devsleep, then disallow these transitions.
+                        */
+                       if (link->ap->host->flags & ATA_HOST_NO_PART)
+                               scontrol |= (0x1 << 8);
+
+                       if (link->ap->host->flags & ATA_HOST_NO_SSC)
+                               scontrol |= (0x2 << 8);
+
+                       if (link->ap->host->flags & ATA_HOST_NO_DEVSLP)
+                               scontrol |= (0x4 << 8);
+               } else {
                        /* empty port, power off */
                        scontrol &= ~0xf;
                        scontrol |= (0x1 << 2);
index 644dd0523cb359d00c9d86ef681d79b5508b90d8..484703fb8b16ec5877c16d793037a16fb4abca84 100644 (file)
@@ -279,6 +279,10 @@ enum {
        ATA_HOST_PARALLEL_SCAN  = (1 << 2),     /* Ports on this host can be scanned in parallel */
        ATA_HOST_IGNORE_ATA     = (1 << 3),     /* Ignore ATA devices on this host. */
 
+       ATA_HOST_NO_PART        = (1 << 4), /* Host does not support partial */
+       ATA_HOST_NO_SSC         = (1 << 5), /* Host does not support slumber */
+       ATA_HOST_NO_DEVSLP      = (1 << 6), /* Host does not support devslp */
+
        /* bits 24:31 of host->flags are reserved for LLD specific flags */
 
        /* various lengths of time */