Commit 8ed2460e authored by LeoLiu-oc's avatar LeoLiu-oc Committed by Zheng Zengkai
Browse files

ata: Add support for PxSCT.LPM set based on actual LPM capability

zhaoxin inclusion
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I40QDN


CVE: NA

----------------------------------------------------------------

The ahci spec mentions that when PxCMD.PSC/SSC is cleared to '0',
the PxSCTL.LPM field in each port must be programmed to disallow
device initiated Partial/Slumber requests.

Signed-off-by: default avatarLeoLiu-oc <LeoLiu-oc@zhaoxin.com>
Signed-off-by: default avatarZheng Zengkai <zhengzengkai@huawei.com>
Acked-by: default avatarJackie Liu <liuyun01@kylinos.cn>
Reviewed-by: default avatarJason Yan <yanaijie@huawei.com>
parent f83af24f
Loading
Loading
Loading
Loading
+18 −2
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
 */

#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/module.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
@@ -368,6 +369,10 @@ int sata_link_scr_lpm(struct ata_link *link, enum ata_lpm_policy policy,
		      bool spm_wakeup)
{
	struct ata_eh_context *ehc = &link->eh_context;
	struct ata_port *ap = ata_is_host_link(link) ? link->ap : NULL;
	struct device *dev = ap ? ap->host->dev : NULL;
	struct pci_dev *pdev = (!dev || !dev_is_pci(dev)) ? NULL : to_pci_dev(dev);

	bool woken_up = false;
	u32 scontrol;
	int rc;
@@ -394,10 +399,21 @@ int sata_link_scr_lpm(struct ata_link *link, enum ata_lpm_policy policy,
	case ATA_LPM_MED_POWER_WITH_DIPM:
	case ATA_LPM_MIN_POWER_WITH_PARTIAL:
	case ATA_LPM_MIN_POWER:
		if (ata_link_nr_enabled(link) > 0)
		if (ata_link_nr_enabled(link) > 0) {
			/* no restrictions on LPM transitions */
			scontrol &= ~(0x7 << 8);
		else {

			/* if controller does not support partial, then disallows it,
			 * the same for slumber
			 */
			if (pdev && pdev->vendor == PCI_VENDOR_ID_ZHAOXIN) {
				if (!(link->ap->host->flags & ATA_HOST_PART))
					scontrol |= (0x1 << 8);

				if (!(link->ap->host->flags & ATA_HOST_SSC))
					scontrol |= (0x2 << 8);
			}
		} else {
			/* empty port, power off */
			scontrol &= ~0xf;
			scontrol |= (0x1 << 2);