usb: otg: Add workaround for PNOC performance issue

PNOC (Peripheral NOC) present on some MSM targets has
a hardware BUG which degrades the USB peripheral performance.
Fix this by adding a software workaround that changes the
USB mem-type attribute value (from 0 to 2) via VMIDMT.

Change-Id: I2249f1aa81c457032c6f8818f5c5d3820bea66d3
Signed-off-by: Manu Gautam <mgautam@codeaurora.org>
diff --git a/Documentation/devicetree/bindings/usb/msm-hsusb.txt b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
index 12fbfec..186a58d 100644
--- a/Documentation/devicetree/bindings/usb/msm-hsusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
@@ -22,10 +22,13 @@
             1 - PHY control
 	    2 - PMIC control
 	    3 - User control (via debugfs)
-- qcom,hsusb-otg-disable-reset: It present then core is RESET only during
-	    init, otherwise core is RESET for every cable disconnect as well
 
 Optional properties :
+- qcom,hsusb-otg-disable-reset: If present then core is RESET only during
+	    init, otherwise core is RESET for every cable disconnect as well
+- qcom,hsusb-otg-pnoc-errata-fix: If present then workaround for PNOC
+	    performance issue is applied which requires changing the mem-type
+	    attribute via VMIDMT.
 - qcom,hsusb-otg-default-mode: The default USB mode after boot-up.
   Applicable only when OTG is controlled by user. Can be one of
             0 - None. Low power mode
@@ -56,6 +59,7 @@
 		qcom,hsusb-otg-mode = <1>;
 		qcom,hsusb-otg-otg-control = <1>;
 		qcom,hsusb-otg-disable-reset;
+		qcom,hsusb-otg-pnoc-errata-fix;
 		qcom,hsusb-otg-default-mode = <2>;
 		qcom,hsusb-otg-phy-init-seq = <0x01 0x90 0xffffffff>;
 		qcom,hsusb-otg-power-budget = <500>;
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index 8e6f1e6..4934afa 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -99,6 +99,7 @@
 		qcom,hsusb-otg-mode = <1>;
 		qcom,hsusb-otg-otg-control = <1>;
 		qcom,hsusb-otg-disable-reset;
+		qcom,hsusb-otg-pnoc-errata-fix;
 
 		qcom,msm_bus,name = "usb2";
 		qcom,msm_bus,num_cases = <2>;
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index f27e0eb..bd3b700 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -42,6 +42,7 @@
 #include <linux/power_supply.h>
 #include <linux/mhl_8334.h>
 
+#include <mach/scm.h>
 #include <mach/clk.h>
 #include <mach/mpm.h>
 #include <mach/msm_xo.h>
@@ -3433,6 +3434,38 @@
 	debugfs_remove_recursive(msm_otg_dbg_root);
 }
 
+#define MSM_OTG_CMD_ID		0x09
+#define MSM_OTG_DEVICE_ID	0x04
+#define MSM_OTG_VMID_IDX	0xFF
+#define MSM_OTG_MEM_TYPE	0x02
+struct msm_otg_scm_cmd_buf {
+	unsigned int device_id;
+	unsigned int vmid_idx;
+	unsigned int mem_type;
+} __attribute__ ((__packed__));
+
+static void msm_otg_pnoc_errata_fix(struct msm_otg *motg)
+{
+	int ret;
+	struct msm_otg_platform_data *pdata = motg->pdata;
+	struct msm_otg_scm_cmd_buf cmd_buf;
+
+	if (!pdata->pnoc_errata_fix)
+		return;
+
+	dev_dbg(motg->phy.dev, "applying fix for pnoc h/w issue\n");
+
+	cmd_buf.device_id = MSM_OTG_DEVICE_ID;
+	cmd_buf.vmid_idx = MSM_OTG_VMID_IDX;
+	cmd_buf.mem_type = MSM_OTG_MEM_TYPE;
+
+	ret = scm_call(SCM_SVC_CP, MSM_OTG_CMD_ID, &cmd_buf,
+				sizeof(cmd_buf), NULL, 0);
+
+	if (ret)
+		dev_err(motg->phy.dev, "scm command failed to update VMIDMT\n");
+}
+
 static u64 msm_otg_dma_mask = DMA_BIT_MASK(64);
 static struct platform_device *msm_otg_add_pdev(
 		struct platform_device *ofdev, const char *name)
@@ -3546,6 +3579,8 @@
 				&pdata->pmic_id_irq);
 	pdata->disable_reset_on_disconnect = of_property_read_bool(node,
 				"qcom,hsusb-otg-disable-reset");
+	pdata->pnoc_errata_fix = of_property_read_bool(node,
+				"qcom,hsusb-otg-pnoc-errata-fix");
 
 	return pdata;
 }
@@ -3744,6 +3779,9 @@
 	}
 	clk_prepare_enable(motg->core_clk);
 
+	/* Check if USB mem_type change is needed to workaround PNOC hw issue */
+	msm_otg_pnoc_errata_fix(motg);
+
 	writel(0, USB_USBINTR);
 	writel(0, USB_OTGSC);
 	/* Ensure that above STOREs are completed before enabling interrupts */
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index d45889c..9294c27 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -192,6 +192,8 @@
  * @mhl_enable: indicates MHL connector or not.
  * @disable_reset_on_disconnect: perform USB PHY and LINK reset
  *              on USB cable disconnection.
+ * @pnoc_errata_fix: workaround needed for PNOC hardware bug that
+ *              affects USB performance.
  * @enable_lpm_on_suspend: Enable the USB core to go into Low
  *              Power Mode, when USB bus is suspended but cable
  *              is connected.
@@ -213,6 +215,7 @@
 	unsigned int mpm_otgsessvld_int;
 	bool mhl_enable;
 	bool disable_reset_on_disconnect;
+	bool pnoc_errata_fix;
 	bool enable_lpm_on_dev_suspend;
 	bool core_clk_always_on_workaround;
 	struct msm_bus_scale_pdata *bus_scale_table;