msm: pil-gss: Apply workaround for QGIC bus access issue

On 8064 v1.0, QGIC registers are inaccessible from the GSS A5
processor unless a special sequence of register writes and reads
is first performed from the Krait0 CPU. This sequence affects the
initial state of an internal GSS bus, resolving the access issue.

CRs-Fixed: 334608
Change-Id: If0bf1f3f16e5e73399de4593b02ba92daf10e5b6
Signed-off-by: Matt Wagantall <mattw@codeaurora.org>
diff --git a/arch/arm/mach-msm/pil-gss.c b/arch/arm/mach-msm/pil-gss.c
index 4c94b83..f3e83d9 100644
--- a/arch/arm/mach-msm/pil-gss.c
+++ b/arch/arm/mach-msm/pil-gss.c
@@ -21,6 +21,7 @@
 #include <linux/platform_device.h>
 #include <linux/workqueue.h>
 #include <linux/clk.h>
+#include <linux/smp.h>
 
 #include <mach/msm_iomap.h>
 #include <mach/msm_xo.h>
@@ -34,6 +35,7 @@
 #define GSS_CSR_CLK_ENABLE	0xC
 #define GSS_CSR_BOOT_REMAP	0x14
 #define GSS_CSR_POWER_UP_DOWN	0x18
+#define GSS_CSR_CFG_HID		0x2C
 
 #define GSS_SLP_CLK_CTL		(MSM_CLK_CTL_BASE + 0x2C60)
 #define GSS_RESET		(MSM_CLK_CTL_BASE + 0x2C64)
@@ -63,6 +65,7 @@
 
 struct gss_data {
 	void __iomem *base;
+	void __iomem *qgic2_base;
 	unsigned long start_addr;
 	struct delayed_work work;
 	struct clk *xo;
@@ -139,38 +142,15 @@
 	writel_relaxed(A5_RESET, base + GSS_CSR_RESET);
 }
 
-static int pil_gss_reset(struct pil_desc *pil)
+static void setup_qgic2_bus_access(void *data)
 {
-	struct gss_data *drv = dev_get_drvdata(pil->dev);
+	struct gss_data *drv = data;
 	void __iomem *base = drv->base;
-	unsigned long start_addr = drv->start_addr;
-	int ret;
+	int i;
 
-	ret = make_gss_proxy_votes(pil->dev);
-	if (ret)
-		return ret;
-
-	/* Vote PLL on in GSS's voting register and wait for it to enable. */
-	writel_relaxed(PLL5_VOTE, PLL_ENA_GSS);
-	while ((readl_relaxed(PLL5_STATUS) & PLL_STATUS) == 0)
-		cpu_relax();
-
-	/* Perform GSS initialization. */
-	gss_init(drv);
-
-	/* Configure boot address and enable remap. */
-	writel_relaxed(REMAP_ENABLE | (start_addr >> 16),
-			base + GSS_CSR_BOOT_REMAP);
-
-	/* Power up A5 core. */
-	writel_relaxed(A5_POWER_ENA, base + GSS_CSR_POWER_UP_DOWN);
-	while (!(readl_relaxed(base + GSS_CSR_POWER_UP_DOWN) & A5_POWER_STATUS))
-		cpu_relax();
-
-	/* Release A5 from reset. */
-	writel_relaxed(0x0, base + GSS_CSR_RESET);
-
-	return 0;
+	writel_relaxed(0x2, base + GSS_CSR_CFG_HID);
+	for (i = 0; i <= 3; i++)
+		readl_relaxed(drv->qgic2_base);
 }
 
 static int pil_gss_shutdown(struct pil_desc *pil)
@@ -225,6 +205,51 @@
 	return 0;
 }
 
+static int pil_gss_reset(struct pil_desc *pil)
+{
+	struct gss_data *drv = dev_get_drvdata(pil->dev);
+	void __iomem *base = drv->base;
+	unsigned long start_addr = drv->start_addr;
+	int ret;
+
+	ret = make_gss_proxy_votes(pil->dev);
+	if (ret)
+		return ret;
+
+	/* Vote PLL on in GSS's voting register and wait for it to enable. */
+	writel_relaxed(PLL5_VOTE, PLL_ENA_GSS);
+	while ((readl_relaxed(PLL5_STATUS) & PLL_STATUS) == 0)
+		cpu_relax();
+
+	/* Perform GSS initialization. */
+	gss_init(drv);
+
+	/* Configure boot address and enable remap. */
+	writel_relaxed(REMAP_ENABLE | (start_addr >> 16),
+			base + GSS_CSR_BOOT_REMAP);
+
+	/* Power up A5 core. */
+	writel_relaxed(A5_POWER_ENA, base + GSS_CSR_POWER_UP_DOWN);
+	while (!(readl_relaxed(base + GSS_CSR_POWER_UP_DOWN) & A5_POWER_STATUS))
+		cpu_relax();
+
+	/*
+	 * Apply a 8064 v1.0 workaround to configure QGIC bus access. This must
+	 * be done from Krait 0 to configure the Master ID correctly.
+	 */
+	ret = smp_call_function_single(0, setup_qgic2_bus_access, drv, 1);
+	if (ret) {
+		pr_err("Failed to configure QGIC2 bus access\n");
+		pil_gss_shutdown(pil);
+		return ret;
+	}
+
+	/* Release A5 from reset. */
+	writel_relaxed(0x0, base + GSS_CSR_RESET);
+
+	return 0;
+}
+
 static struct pil_reset_ops pil_gss_ops = {
 	.init_image = pil_gss_init_image,
 	.verify_blob = nop_verify_blob,
@@ -313,6 +338,15 @@
 	if (!desc)
 		return -ENOMEM;
 
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!res)
+		return -EINVAL;
+
+	drv->qgic2_base = devm_ioremap(&pdev->dev, res->start,
+					resource_size(res));
+	if (!drv->qgic2_base)
+		return -ENOMEM;
+
 	drv->xo = clk_get(&pdev->dev, "xo");
 	if (IS_ERR(drv->xo))
 		return PTR_ERR(drv->xo);