--- /dev/null
+From 24e0e61db3cb86a66824531989f1df80e0939f26 Mon Sep 17 00:00:00 2001
+From: Niklas Cassel <niklas.cassel@wdc.com>
+Date: Mon, 4 Sep 2023 22:42:56 +0200
+Subject: ata: libata: disallow dev-initiated LPM transitions to unsupported states
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Niklas Cassel <niklas.cassel@wdc.com>
+
+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 | 9 +++++++++
+ drivers/ata/libata-core.c | 19 ++++++++++++++++---
+ include/linux/libata.h | 4 ++++
+ 3 files changed, 29 insertions(+), 3 deletions(-)
+
+--- a/drivers/ata/ahci.c
++++ b/drivers/ata/ahci.c
+@@ -1775,6 +1775,15 @@ static int ahci_init_one(struct pci_dev
+ 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);
+
+--- a/drivers/ata/libata-core.c
++++ b/drivers/ata/libata-core.c
+@@ -3993,10 +3993,23 @@ int sata_link_scr_lpm(struct ata_link *l
+ 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);
+--- a/include/linux/libata.h
++++ b/include/linux/libata.h
+@@ -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 */