summaryrefslogtreecommitdiff
path: root/drivers/usb/dwc3/dwc3-msm.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/dwc3/dwc3-msm.c')
-rw-r--r--drivers/usb/dwc3/dwc3-msm.c52
1 files changed, 52 insertions, 0 deletions
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index c2d788bc4bc5..334e7576d5e6 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -70,6 +70,8 @@ MODULE_PARM_DESC(cpu_to_affin, "affin usb irq to this cpu");
/* XHCI registers */
#define USB3_HCSPARAMS1 (0x4)
+#define USB3_HCCPARAMS2 (0x1c)
+#define HCC_CTC(p) ((p) & (1 << 3))
#define USB3_PORTSC (0x420)
/**
@@ -211,6 +213,7 @@ struct dwc3_msm {
struct notifier_block dwc3_cpu_notifier;
struct notifier_block usbdev_nb;
bool hc_died;
+ bool xhci_ss_compliance_enable;
struct extcon_dev *extcon_vbus;
struct extcon_dev *extcon_id;
@@ -2796,6 +2799,34 @@ static ssize_t speed_store(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR_RW(speed);
static void msm_dwc3_perf_vote_work(struct work_struct *w);
+static ssize_t xhci_link_compliance_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dwc3_msm *mdwc = dev_get_drvdata(dev);
+
+ if (mdwc->xhci_ss_compliance_enable)
+ return snprintf(buf, PAGE_SIZE, "y\n");
+ else
+ return snprintf(buf, PAGE_SIZE, "n\n");
+}
+
+static ssize_t xhci_link_compliance_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct dwc3_msm *mdwc = dev_get_drvdata(dev);
+ bool value;
+ int ret;
+
+ ret = strtobool(buf, &value);
+ if (!ret) {
+ mdwc->xhci_ss_compliance_enable = value;
+ return count;
+ }
+
+ return ret;
+}
+
+static DEVICE_ATTR_RW(xhci_link_compliance);
static int dwc3_msm_probe(struct platform_device *pdev)
{
@@ -3139,6 +3170,7 @@ static int dwc3_msm_probe(struct platform_device *pdev)
device_create_file(&pdev->dev, &dev_attr_mode);
device_create_file(&pdev->dev, &dev_attr_speed);
+ device_create_file(&pdev->dev, &dev_attr_xhci_link_compliance);
host_mode = usb_get_dr_mode(&mdwc->dwc3->dev) == USB_DR_MODE_HOST;
if (host_mode ||
@@ -3170,6 +3202,7 @@ static int dwc3_msm_remove(struct platform_device *pdev)
int ret_pm;
device_remove_file(&pdev->dev, &dev_attr_mode);
+ device_remove_file(&pdev->dev, &dev_attr_xhci_link_compliance);
if (cpu_to_affin)
unregister_cpu_notifier(&mdwc->dwc3_cpu_notifier);
@@ -3434,6 +3467,25 @@ static int dwc3_otg_start_host(struct dwc3_msm *mdwc, int on)
}
/*
+ * If the Compliance Transition Capability(CTC) flag of
+ * HCCPARAMS2 register is set and xhci_link_compliance sysfs
+ * param has been enabled by the user for the SuperSpeed host
+ * controller, then write 10 (Link in Compliance Mode State)
+ * onto the Port Link State(PLS) field of the PORTSC register
+ * for 3.0 host controller which is at an offset of USB3_PORTSC
+ * + 0x10 from the DWC3 base address. Also, disable the runtime
+ * PM of 3.0 root hub (root hub of shared_hcd of xhci device)
+ */
+ if (HCC_CTC(dwc3_msm_read_reg(mdwc->base, USB3_HCCPARAMS2))
+ && mdwc->xhci_ss_compliance_enable
+ && dwc->maximum_speed == USB_SPEED_SUPER) {
+ dwc3_msm_write_reg(mdwc->base, USB3_PORTSC + 0x10,
+ 0x10340);
+ pm_runtime_disable(&hcd_to_xhci(platform_get_drvdata(
+ dwc->xhci))->shared_hcd->self.root_hub->dev);
+ }
+
+ /*
* In some cases it is observed that USB PHY is not going into
* suspend with host mode suspend functionality. Hence disable
* XHCI's runtime PM here if disable_host_mode_pm is set.