From 51b0b45623fa098c7427f34fd936bc31a384ee45 Mon Sep 17 00:00:00 2001
From: LeoLiu-oc <LeoLiu-oc@zhaoxin.com>
Date: Wed, 7 Apr 2021 14:26:06 +0800
Subject: [PATCH] xhci: Add Zhaoxin xHCI LPM U1/U2 feature support

zhaoxin inclusion
category: feature
bugzilla: https://bugzilla.openeuler.org/show_bug.cgi?id=19
CVE: NA

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

Add LPM u1/u2 feature support for xHCI of zhaoxin

The patch is scheduled to be submitted to the kernel mainline in 2021.

Signed-off-by: LeoLiu-oc <LeoLiu-oc@zhaoxin.com>
Reviewed-by: Hanjun Guo <guohanjun@huawei.com>
Reviewed-by: LeoLiu-oc <LeoLiu-oc@zhaoxin.com>
Conflicts:
	drivers/usb/host/xhci.h
	drivers/usb/host/xhci.c
[Cheng Jian: adjust context]
Signed-off-by: Cheng Jian <cj.chengjian@huawei.com>
---
 drivers/usb/host/xhci-pci.c |  4 ++++
 drivers/usb/host/xhci.c     | 34 ++++++++++++++++++++++++++++++++--
 drivers/usb/host/xhci.h     |  1 +
 3 files changed, 37 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 1efc53bfcc80..7bb23edf5b7c 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -218,6 +218,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
 	}
 	if (pdev->vendor == PCI_VENDOR_ID_VIA)
 		xhci->quirks |= XHCI_RESET_ON_RESUME;
+	if (pdev->vendor == PCI_VENDOR_ID_ZHAOXIN) {
+		xhci->quirks |= XHCI_LPM_SUPPORT;
+		xhci->quirks |= XHCI_ZHAOXIN_HOST;
+	}
 
 	/* See https://bugzilla.kernel.org/show_bug.cgi?id=79511 */
 	if (pdev->vendor == PCI_VENDOR_ID_VIA &&
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index faa76c3fa15d..918aeca9e789 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -4554,7 +4554,7 @@ static u16 xhci_calculate_u1_timeout(struct xhci_hcd *xhci,
 		}
 	}
 
-	if (xhci->quirks & XHCI_INTEL_HOST)
+	if (xhci->quirks & (XHCI_INTEL_HOST | XHCI_ZHAOXIN_HOST))
 		timeout_ns = xhci_calculate_intel_u1_timeout(udev, desc);
 	else
 		timeout_ns = udev->u1_params.sel;
@@ -4618,7 +4618,7 @@ static u16 xhci_calculate_u2_timeout(struct xhci_hcd *xhci,
 		}
 	}
 
-	if (xhci->quirks & XHCI_INTEL_HOST)
+	if (xhci->quirks & (XHCI_INTEL_HOST | XHCI_ZHAOXIN_HOST))
 		timeout_ns = xhci_calculate_intel_u2_timeout(udev, desc);
 	else
 		timeout_ns = udev->u2_params.sel;
@@ -4715,12 +4715,42 @@ static int xhci_check_intel_tier_policy(struct usb_device *udev,
 	return -E2BIG;
 }
 
+static int xhci_check_zhaoxin_tier_policy(struct usb_device *udev,
+							enum usb3_link_state state)
+{
+	struct usb_device *parent;
+	unsigned int num_hubs;
+	char *state_name;
+
+	if (state == USB3_LPM_U1)
+		state_name = "U1";
+	else if (state == USB3_LPM_U2)
+		state_name = "U2";
+	else
+		state_name = "Unknown";
+	/* Don't enable U1/U2 if the device is on an external hub*/
+	for (parent = udev->parent, num_hubs = 0; parent->parent;
+			parent = parent->parent)
+			num_hubs++;
+
+	if (num_hubs < 1)
+		return 0;
+
+	dev_dbg(&udev->dev, "Disabling %s link state for device" \
+							" below external hub.\n", state_name);
+	dev_dbg(&udev->dev, "Plug device into root port " \
+							"to decrease power consumption.\n");
+	return -E2BIG;
+}
+
 static int xhci_check_tier_policy(struct xhci_hcd *xhci,
 		struct usb_device *udev,
 		enum usb3_link_state state)
 {
 	if (xhci->quirks & XHCI_INTEL_HOST)
 		return xhci_check_intel_tier_policy(udev, state);
+	else if (xhci->quirks & XHCI_ZHAOXIN_HOST)
+		return xhci_check_zhaoxin_tier_policy(udev, state);
 	else
 		return 0;
 }
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 4dedc822237f..b02911887d20 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1872,6 +1872,7 @@ struct xhci_hcd {
 #define XHCI_ZERO_64B_REGS	BIT_ULL(32)
 #define XHCI_RESET_PLL_ON_DISCONNECT	BIT_ULL(34)
 #define XHCI_SNPS_BROKEN_SUSPEND    BIT_ULL(35)
+#define XHCI_ZHAOXIN_HOST       BIT_ULL(36)
 
 	unsigned int		num_active_eps;
 	unsigned int		limit_active_eps;
-- 
GitLab