msm: pil-8660: Break off Q6v3 boot code into platform driver

Create a platform driver to manage the Q6v3 instead of having a
module that exists for any 8660 target. This allows us to add
platform devices when the device really exists.

Change-Id: I6eb0f235334388e9a2fe3f9028504c8ca099ba41
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
diff --git a/arch/arm/configs/msm8660-perf_defconfig b/arch/arm/configs/msm8660-perf_defconfig
index 985d735..bf01b40 100644
--- a/arch/arm/configs/msm8660-perf_defconfig
+++ b/arch/arm/configs/msm8660-perf_defconfig
@@ -62,6 +62,7 @@
 CONFIG_MSM_RMT_STORAGE_CLIENT=y
 CONFIG_MSM_SDIO_SMEM=y
 # CONFIG_MSM_HW3D is not set
+CONFIG_MSM_PIL_QDSP6V3=y
 CONFIG_MSM_SUBSYSTEM_RESTART=y
 CONFIG_MSM_RPM_LOG=y
 CONFIG_MSM_RPM_STATS_LOG=y
diff --git a/arch/arm/configs/msm8660_defconfig b/arch/arm/configs/msm8660_defconfig
index dbde99f..0472ad7 100644
--- a/arch/arm/configs/msm8660_defconfig
+++ b/arch/arm/configs/msm8660_defconfig
@@ -54,6 +54,7 @@
 # CONFIG_MSM_RPCSERVER_HANDSET is not set
 CONFIG_MSM_RMT_STORAGE_CLIENT=y
 # CONFIG_MSM_HW3D is not set
+CONFIG_MSM_PIL_QDSP6V3=y
 CONFIG_MSM_SUBSYSTEM_RESTART=y
 CONFIG_MSM_RPM_LOG=y
 CONFIG_MSM_RPM_STATS_LOG=y
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 204aa5d..7aeb560 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -1580,6 +1580,13 @@
 
 	  Say yes to support these devices.
 
+config MSM_PIL_QDSP6V3
+	tristate "QDSP6v3 (Hexagon) Boot Support"
+	depends on MSM_PIL
+	help
+	  Support for booting and shutting down QDSP6v3 processors (hexagon).
+	  The QDSP6 is a low power DSP used in audio software applications.
+
 config MSM_PIL_QDSP6V4
 	tristate "QDSP6v4 (Hexagon) Boot Support"
 	depends on MSM_PIL
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 48708e5..5fda40c 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -71,6 +71,7 @@
 obj-$(CONFIG_ARCH_MSM8X60) += peripheral-reset.o
 obj-$(CONFIG_ARCH_MSM8960) += peripheral-reset-8960.o
 endif
+obj-$(CONFIG_MSM_PIL_QDSP6V3) += pil-q6v3.o
 obj-$(CONFIG_MSM_PIL_QDSP6V4) += pil-q6v4.o
 obj-$(CONFIG_ARCH_QSD8X50) += sirc.o
 obj-$(CONFIG_ARCH_FSM9XXX) += sirc-fsm9xxx.o
diff --git a/arch/arm/mach-msm/board-msm8x60.c b/arch/arm/mach-msm/board-msm8x60.c
index bad3e82..0a539c5 100644
--- a/arch/arm/mach-msm/board-msm8x60.c
+++ b/arch/arm/mach-msm/board-msm8x60.c
@@ -5082,6 +5082,7 @@
 static struct platform_device *surf_devices[] __initdata = {
 	&msm_device_smd,
 	&msm_device_uart_dm12,
+	&msm_pil_q6v3,
 #ifdef CONFIG_I2C_QUP
 	&msm_gsbi3_qup_i2c_device,
 	&msm_gsbi4_qup_i2c_device,
diff --git a/arch/arm/mach-msm/clock-8x60.c b/arch/arm/mach-msm/clock-8x60.c
index 01f97f1..504cf9a 100644
--- a/arch/arm/mach-msm/clock-8x60.c
+++ b/arch/arm/mach-msm/clock-8x60.c
@@ -3576,8 +3576,7 @@
 
 static struct clk_lookup msm_clocks_8x60[] = {
 	CLK_LOOKUP("cxo",		cxo_clk.c,	NULL),
-	CLK_LOOKUP("pll4",		pll4_clk.c,	NULL),
-	CLK_LOOKUP("pll4",		pll4_clk.c,	"peripheral-reset"),
+	CLK_LOOKUP("pll4",		pll4_clk.c,	"pil_qdsp6v3"),
 	CLK_LOOKUP("measure",		measure_clk.c,	"debug"),
 
 	CLK_LOOKUP("bus_clk",		afab_clk.c,	"msm_apps_fab"),
diff --git a/arch/arm/mach-msm/devices-msm8x60.c b/arch/arm/mach-msm/devices-msm8x60.c
index 72fdcf7..aa9e380 100644
--- a/arch/arm/mach-msm/devices-msm8x60.c
+++ b/arch/arm/mach-msm/devices-msm8x60.c
@@ -188,6 +188,23 @@
 	}
 }
 
+#define MSM_LPASS_QDSP6SS_PHYS 0x28800000
+
+static struct resource msm_8660_q6_resources[] = {
+	{
+		.start  = MSM_LPASS_QDSP6SS_PHYS,
+		.end    = MSM_LPASS_QDSP6SS_PHYS + SZ_256 - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+};
+
+struct platform_device msm_pil_q6v3 = {
+	.name = "pil_qdsp6v3",
+	.id = -1,
+	.num_resources  = ARRAY_SIZE(msm_8660_q6_resources),
+	.resource       = msm_8660_q6_resources,
+};
+
 static struct resource msm_uart1_dm_resources[] = {
 	{
 		.start = MSM_UART1DM_PHYS,
diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h
index ffe22f8..9743ee2 100644
--- a/arch/arm/mach-msm/devices.h
+++ b/arch/arm/mach-msm/devices.h
@@ -174,6 +174,7 @@
 extern struct platform_device msm_pcm_afe;
 extern struct platform_device msm_compr_dsp;
 
+extern struct platform_device msm_pil_q6v3;
 extern struct platform_device msm_8960_q6_lpass;
 extern struct platform_device msm_8960_q6_mss_fw;
 extern struct platform_device msm_8960_q6_mss_sw;
diff --git a/arch/arm/mach-msm/peripheral-reset.c b/arch/arm/mach-msm/peripheral-reset.c
index f3f5388..2d60a7e 100644
--- a/arch/arm/mach-msm/peripheral-reset.c
+++ b/arch/arm/mach-msm/peripheral-reset.c
@@ -34,7 +34,6 @@
 #define PROXY_VOTE_TIMEOUT		10000
 
 #define MSM_MMS_REGS_BASE		0x10200000
-#define MSM_LPASS_QDSP6SS_BASE		0x28800000
 
 #define MARM_RESET			(MSM_CLK_CTL_BASE + 0x2BD4)
 #define MARM_BOOT_CONTROL		(msm_mms_regs_base + 0x0010)
@@ -58,18 +57,12 @@
 #define PLL8_STATUS			(MSM_CLK_CTL_BASE + 0x3158)
 #define CLK_HALT_MSS_SMPSS_MISC_STATE	(MSM_CLK_CTL_BASE + 0x2FDC)
 
-#define LCC_Q6_FUNC			(MSM_LPASS_CLK_CTL_BASE + 0x001C)
-#define QDSP6SS_RST_EVB			(msm_lpass_qdsp6ss_base + 0x0000)
-#define QDSP6SS_STRAP_TCM		(msm_lpass_qdsp6ss_base + 0x001C)
-#define QDSP6SS_STRAP_AHB		(msm_lpass_qdsp6ss_base + 0x0020)
-
 #define PPSS_RESET			(MSM_CLK_CTL_BASE + 0x2594)
 #define PPSS_PROC_CLK_CTL		(MSM_CLK_CTL_BASE + 0x2588)
 #define CLK_HALT_DFAB_STATE		(MSM_CLK_CTL_BASE + 0x2FC8)
 
-static int modem_start, q6_start, dsps_start;
+static int modem_start, dsps_start;
 static void __iomem *msm_mms_regs_base;
-static void __iomem *msm_lpass_qdsp6ss_base;
 
 static int init_image_modem_trusted(struct pil_desc *pil, const u8 *metadata,
 				    size_t size)
@@ -85,20 +78,6 @@
 	return 0;
 }
 
-static int init_image_q6_trusted(struct pil_desc *pil,
-				 const u8 *metadata, size_t size)
-{
-	return pas_init_image(PAS_Q6, metadata, size);
-}
-
-static int init_image_q6_untrusted(struct pil_desc *pil, const u8 *metadata,
-				   size_t size)
-{
-	struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata;
-	q6_start = ehdr->e_entry;
-	return 0;
-}
-
 static int init_image_dsps_trusted(struct pil_desc *pil, const u8 *metadata,
 				   size_t size)
 {
@@ -291,154 +270,6 @@
 	return 0;
 }
 
-#define LV_EN 			BIT(27)
-#define STOP_CORE		BIT(26)
-#define CLAMP_IO 		BIT(25)
-#define Q6SS_PRIV_ARES		BIT(24)
-#define Q6SS_SS_ARES		BIT(23)
-#define Q6SS_ISDB_ARES		BIT(22)
-#define Q6SS_ETM_ARES		BIT(21)
-#define Q6_JTAG_CRC_EN		BIT(20)
-#define Q6_JTAG_INV_EN		BIT(19)
-#define Q6_JTAG_CXC_EN		BIT(18)
-#define Q6_PXO_CRC_EN		BIT(17)
-#define Q6_PXO_INV_EN		BIT(16)
-#define Q6_PXO_CXC_EN		BIT(15)
-#define Q6_PXO_SLEEP_EN		BIT(14)
-#define Q6_SLP_CRC_EN		BIT(13)
-#define Q6_SLP_INV_EN		BIT(12)
-#define Q6_SLP_CXC_EN		BIT(11)
-#define CORE_ARES		BIT(10)
-#define CORE_L1_MEM_CORE_EN	BIT(9)
-#define CORE_TCM_MEM_CORE_EN	BIT(8)
-#define CORE_TCM_MEM_PERPH_EN	BIT(7)
-#define CORE_GFM4_CLK_EN	BIT(2)
-#define CORE_GFM4_RES		BIT(1)
-#define RAMP_PLL_SRC_SEL	BIT(0)
-
-#define Q6_STRAP_AHB_UPPER	(0x290 << 12)
-#define Q6_STRAP_AHB_LOWER	0x280
-#define Q6_STRAP_TCM_BASE	(0x28C << 15)
-#define Q6_STRAP_TCM_CONFIG	0x28B
-
-static struct clk *pll4;
-
-static void remove_q6_proxy_votes(unsigned long data)
-{
-	clk_disable(pll4);
-}
-static DEFINE_TIMER(q6_timer, remove_q6_proxy_votes, 0, 0);
-
-static void make_q6_proxy_votes(void)
-{
-	/* Make proxy votes for Q6 and set up timer to disable it. */
-	clk_enable(pll4);
-	mod_timer(&q6_timer, jiffies + msecs_to_jiffies(PROXY_VOTE_TIMEOUT));
-}
-
-static void remove_q6_proxy_votes_now(void)
-{
-	/*
-	 * If the Q6 proxy vote hasn't been removed yet, them remove the
-	 * votes immediately.
-	 */
-	if (del_timer(&q6_timer))
-		remove_q6_proxy_votes(0);
-}
-
-static int reset_q6_untrusted(struct pil_desc *pil)
-{
-	u32 reg;
-
-	make_q6_proxy_votes();
-
-	/* Put Q6 into reset */
-	reg = __raw_readl(LCC_Q6_FUNC);
-	reg |= Q6SS_SS_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES | STOP_CORE |
-		CORE_ARES;
-	reg &= ~CORE_GFM4_CLK_EN;
-	__raw_writel(reg, LCC_Q6_FUNC);
-
-	/* Wait 8 AHB cycles for Q6 to be fully reset (AHB = 1.5Mhz) */
-	usleep_range(20, 30);
-
-	/* Turn on Q6 memory */
-	reg |= CORE_GFM4_CLK_EN | CORE_L1_MEM_CORE_EN | CORE_TCM_MEM_CORE_EN |
-		CORE_TCM_MEM_PERPH_EN;
-	__raw_writel(reg, LCC_Q6_FUNC);
-
-	/* Turn on Q6 core clocks and take core out of reset */
-	reg &= ~(CLAMP_IO | Q6SS_SS_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES |
-			CORE_ARES);
-	__raw_writel(reg, LCC_Q6_FUNC);
-
-	/* Wait for clocks to be enabled */
-	mb();
-	/* Program boot address */
-	__raw_writel((q6_start >> 12) & 0xFFFFF, QDSP6SS_RST_EVB);
-
-	__raw_writel(Q6_STRAP_TCM_CONFIG | Q6_STRAP_TCM_BASE,
-			QDSP6SS_STRAP_TCM);
-	__raw_writel(Q6_STRAP_AHB_UPPER | Q6_STRAP_AHB_LOWER,
-			QDSP6SS_STRAP_AHB);
-
-	/* Wait for addresses to be programmed before starting Q6 */
-	mb();
-
-	/* Start Q6 instruction execution */
-	reg &= ~STOP_CORE;
-	__raw_writel(reg, LCC_Q6_FUNC);
-
-	return 0;
-}
-
-static int reset_q6_trusted(struct pil_desc *pil)
-{
-	make_q6_proxy_votes();
-
-	return pas_auth_and_reset(PAS_Q6);
-}
-
-static int shutdown_q6_untrusted(struct pil_desc *pil)
-{
-	u32 reg;
-
-	/* Put Q6 into reset */
-	reg = __raw_readl(LCC_Q6_FUNC);
-	reg |= Q6SS_SS_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES | STOP_CORE |
-		CORE_ARES;
-	reg &= ~CORE_GFM4_CLK_EN;
-	__raw_writel(reg, LCC_Q6_FUNC);
-
-	/* Wait 8 AHB cycles for Q6 to be fully reset (AHB = 1.5Mhz) */
-	usleep_range(20, 30);
-
-	/* Turn off Q6 memory */
-	reg &= ~(CORE_L1_MEM_CORE_EN | CORE_TCM_MEM_CORE_EN |
-		CORE_TCM_MEM_PERPH_EN);
-	__raw_writel(reg, LCC_Q6_FUNC);
-
-	reg |= CLAMP_IO;
-	__raw_writel(reg, LCC_Q6_FUNC);
-
-	remove_q6_proxy_votes_now();
-
-	return 0;
-}
-
-static int shutdown_q6_trusted(struct pil_desc *pil)
-{
-	int ret;
-
-	ret = pas_shutdown(PAS_Q6);
-	if (ret)
-		return ret;
-
-	remove_q6_proxy_votes_now();
-
-	return 0;
-}
-
 static int reset_dsps_untrusted(struct pil_desc *pil)
 {
 	__raw_writel(0x10, PPSS_PROC_CLK_CTL);
@@ -490,13 +321,6 @@
 	.shutdown = shutdown_modem_untrusted,
 };
 
-struct pil_reset_ops pil_q6_ops = {
-	.init_image = init_image_q6_untrusted,
-	.verify_blob = verify_blob,
-	.auth_and_reset = reset_q6_untrusted,
-	.shutdown = shutdown_q6_untrusted,
-};
-
 struct pil_reset_ops pil_dsps_ops = {
 	.init_image = init_image_dsps_untrusted,
 	.verify_blob = verify_blob,
@@ -522,16 +346,6 @@
 	.ops = &pil_modem_ops,
 };
 
-static struct platform_device pil_q6 = {
-	.name = "pil_q6",
-};
-
-static struct pil_desc pil_q6_desc = {
-	.name = "q6",
-	.dev = &pil_q6.dev,
-	.ops = &pil_q6_ops,
-};
-
 static struct platform_device pil_playready = {
 	.name = "pil_playready",
 };
@@ -558,38 +372,22 @@
 	if (!msm_mms_regs_base)
 		goto err;
 
-	msm_lpass_qdsp6ss_base = ioremap(MSM_LPASS_QDSP6SS_BASE, SZ_256);
-	if (!msm_lpass_qdsp6ss_base)
-		goto err_lpass;
-
 	pxo = msm_xo_get(MSM_XO_PXO, "pil");
 	if (IS_ERR(pxo))
 		goto err_pxo;
 
-	pll4 = clk_get_sys("peripheral-reset", "pll4");
-	if (IS_ERR(pll4))
-		goto err_clk;
-
 	if (pas_supported(PAS_MODEM) > 0) {
 		pil_modem_ops.init_image = init_image_modem_trusted;
 		pil_modem_ops.auth_and_reset = reset_modem_trusted;
 		pil_modem_ops.shutdown = shutdown_modem_trusted;
 	}
 
-	if (pas_supported(PAS_Q6) > 0) {
-		pil_q6_ops.init_image = init_image_q6_trusted;
-		pil_q6_ops.auth_and_reset = reset_q6_trusted;
-		pil_q6_ops.shutdown = shutdown_q6_trusted;
-	}
-
 	if (pas_supported(PAS_DSPS) > 0) {
 		pil_dsps_ops.init_image = init_image_dsps_trusted;
 		pil_dsps_ops.auth_and_reset = reset_dsps_trusted;
 		pil_dsps_ops.shutdown = shutdown_dsps_trusted;
 	}
 
-	BUG_ON(platform_device_register(&pil_q6));
-	BUG_ON(msm_pil_register(&pil_q6_desc));
 	BUG_ON(platform_device_register(&pil_modem));
 	BUG_ON(msm_pil_register(&pil_modem_desc));
 	BUG_ON(platform_device_register(&pil_playready));
@@ -602,11 +400,7 @@
 
 	return 0;
 
-err_clk:
-	msm_xo_put(pxo);
 err_pxo:
-	iounmap(msm_lpass_qdsp6ss_base);
-err_lpass:
 	iounmap(msm_mms_regs_base);
 err:
 	return -ENOMEM;
@@ -615,7 +409,6 @@
 static void __exit msm_peripheral_reset_exit(void)
 {
 	iounmap(msm_mms_regs_base);
-	iounmap(msm_lpass_qdsp6ss_base);
 }
 
 arch_initcall(msm_peripheral_reset_init);
diff --git a/arch/arm/mach-msm/pil-q6v3.c b/arch/arm/mach-msm/pil-q6v3.c
new file mode 100644
index 0000000..7354d93
--- /dev/null
+++ b/arch/arm/mach-msm/pil-q6v3.c
@@ -0,0 +1,299 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/elf.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+
+#include <mach/msm_iomap.h>
+
+#include "peripheral-loader.h"
+#include "scm-pas.h"
+
+#define QDSP6SS_RST_EVB		0x0000
+#define QDSP6SS_STRAP_TCM	0x001C
+#define QDSP6SS_STRAP_AHB	0x0020
+
+#define LCC_Q6_FUNC		(MSM_LPASS_CLK_CTL_BASE + 0x001C)
+#define LV_EN			BIT(27)
+#define STOP_CORE		BIT(26)
+#define CLAMP_IO		BIT(25)
+#define Q6SS_PRIV_ARES		BIT(24)
+#define Q6SS_SS_ARES		BIT(23)
+#define Q6SS_ISDB_ARES		BIT(22)
+#define Q6SS_ETM_ARES		BIT(21)
+#define Q6_JTAG_CRC_EN		BIT(20)
+#define Q6_JTAG_INV_EN		BIT(19)
+#define Q6_JTAG_CXC_EN		BIT(18)
+#define Q6_PXO_CRC_EN		BIT(17)
+#define Q6_PXO_INV_EN		BIT(16)
+#define Q6_PXO_CXC_EN		BIT(15)
+#define Q6_PXO_SLEEP_EN		BIT(14)
+#define Q6_SLP_CRC_EN		BIT(13)
+#define Q6_SLP_INV_EN		BIT(12)
+#define Q6_SLP_CXC_EN		BIT(11)
+#define CORE_ARES		BIT(10)
+#define CORE_L1_MEM_CORE_EN	BIT(9)
+#define CORE_TCM_MEM_CORE_EN	BIT(8)
+#define CORE_TCM_MEM_PERPH_EN	BIT(7)
+#define CORE_GFM4_CLK_EN	BIT(2)
+#define CORE_GFM4_RES		BIT(1)
+#define RAMP_PLL_SRC_SEL	BIT(0)
+
+#define Q6_STRAP_AHB_UPPER	(0x290 << 12)
+#define Q6_STRAP_AHB_LOWER	0x280
+#define Q6_STRAP_TCM_BASE	(0x28C << 15)
+#define Q6_STRAP_TCM_CONFIG	0x28B
+
+#define PROXY_VOTE_TIMEOUT	10000
+
+struct q6v3_data {
+	void __iomem *base;
+	unsigned long start_addr;
+	struct clk *pll;
+	struct timer_list timer;
+};
+
+static int nop_verify_blob(struct pil_desc *pil, u32 phy_addr, size_t size)
+{
+	return 0;
+}
+
+static int pil_q6v3_init_image(struct pil_desc *pil, const u8 *metadata,
+		size_t size)
+{
+	const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata;
+	struct q6v3_data *drv = dev_get_drvdata(pil->dev);
+	drv->start_addr = ehdr->e_entry;
+	return 0;
+}
+
+static void q6v3_remove_proxy_votes(unsigned long data)
+{
+	struct q6v3_data *drv = (struct q6v3_data *)data;
+	clk_disable(drv->pll);
+}
+
+static void q6v3_make_proxy_votes(struct device *dev)
+{
+	int ret;
+	struct q6v3_data *drv = dev_get_drvdata(dev);
+
+	ret = clk_enable(drv->pll);
+	if (ret)
+		dev_err(dev, "Failed to enable PLL\n");
+	mod_timer(&drv->timer, jiffies + msecs_to_jiffies(PROXY_VOTE_TIMEOUT));
+}
+
+static void q6v3_remove_proxy_votes_now(struct q6v3_data *drv)
+{
+	/* If the proxy vote hasn't been removed yet, remove it immediately. */
+	if (del_timer(&drv->timer))
+		q6v3_remove_proxy_votes((unsigned long)drv);
+}
+
+static int pil_q6v3_reset(struct pil_desc *pil)
+{
+	u32 reg;
+	struct q6v3_data *drv = dev_get_drvdata(pil->dev);
+
+	q6v3_make_proxy_votes(pil->dev);
+
+	/* Put Q6 into reset */
+	reg = readl_relaxed(LCC_Q6_FUNC);
+	reg |= Q6SS_SS_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES | STOP_CORE |
+		CORE_ARES;
+	reg &= ~CORE_GFM4_CLK_EN;
+	writel_relaxed(reg, LCC_Q6_FUNC);
+
+	/* Wait 8 AHB cycles for Q6 to be fully reset (AHB = 1.5Mhz) */
+	usleep_range(20, 30);
+
+	/* Turn on Q6 memory */
+	reg |= CORE_GFM4_CLK_EN | CORE_L1_MEM_CORE_EN | CORE_TCM_MEM_CORE_EN |
+		CORE_TCM_MEM_PERPH_EN;
+	writel_relaxed(reg, LCC_Q6_FUNC);
+
+	/* Turn on Q6 core clocks and take core out of reset */
+	reg &= ~(CLAMP_IO | Q6SS_SS_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES |
+			CORE_ARES);
+	writel_relaxed(reg, LCC_Q6_FUNC);
+
+	/* Wait for clocks to be enabled */
+	mb();
+	/* Program boot address */
+	writel_relaxed((drv->start_addr >> 12) & 0xFFFFF,
+			drv->base + QDSP6SS_RST_EVB);
+
+	writel_relaxed(Q6_STRAP_TCM_CONFIG | Q6_STRAP_TCM_BASE,
+			drv->base + QDSP6SS_STRAP_TCM);
+	writel_relaxed(Q6_STRAP_AHB_UPPER | Q6_STRAP_AHB_LOWER,
+			drv->base + QDSP6SS_STRAP_AHB);
+
+	/* Wait for addresses to be programmed before starting Q6 */
+	mb();
+
+	/* Start Q6 instruction execution */
+	reg &= ~STOP_CORE;
+	writel_relaxed(reg, LCC_Q6_FUNC);
+
+	return 0;
+}
+
+static int pil_q6v3_shutdown(struct pil_desc *pil)
+{
+	u32 reg;
+	struct q6v3_data *drv = dev_get_drvdata(pil->dev);
+
+	/* Put Q6 into reset */
+	reg = readl_relaxed(LCC_Q6_FUNC);
+	reg |= Q6SS_SS_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES | STOP_CORE |
+		CORE_ARES;
+	reg &= ~CORE_GFM4_CLK_EN;
+	writel_relaxed(reg, LCC_Q6_FUNC);
+
+	/* Wait 8 AHB cycles for Q6 to be fully reset (AHB = 1.5Mhz) */
+	usleep_range(20, 30);
+
+	/* Turn off Q6 memory */
+	reg &= ~(CORE_L1_MEM_CORE_EN | CORE_TCM_MEM_CORE_EN |
+		CORE_TCM_MEM_PERPH_EN);
+	writel_relaxed(reg, LCC_Q6_FUNC);
+
+	reg |= CLAMP_IO;
+	writel_relaxed(reg, LCC_Q6_FUNC);
+
+	q6v3_remove_proxy_votes_now(drv);
+
+	return 0;
+}
+
+static struct pil_reset_ops pil_q6v3_ops = {
+	.init_image = pil_q6v3_init_image,
+	.verify_blob = nop_verify_blob,
+	.auth_and_reset = pil_q6v3_reset,
+	.shutdown = pil_q6v3_shutdown,
+};
+
+static int pil_q6v3_init_image_trusted(struct pil_desc *pil,
+		const u8 *metadata, size_t size)
+{
+	return pas_init_image(PAS_Q6, metadata, size);
+}
+
+static int pil_q6v3_reset_trusted(struct pil_desc *pil)
+{
+	q6v3_make_proxy_votes(pil->dev);
+	return pas_auth_and_reset(PAS_Q6);
+}
+
+static int pil_q6v3_shutdown_trusted(struct pil_desc *pil)
+{
+	int ret;
+	struct q6v3_data *drv = dev_get_drvdata(pil->dev);
+
+	ret = pas_shutdown(PAS_Q6);
+	if (ret)
+		return ret;
+
+	q6v3_remove_proxy_votes_now(drv);
+
+	return 0;
+}
+
+static struct pil_reset_ops pil_q6v3_ops_trusted = {
+	.init_image = pil_q6v3_init_image_trusted,
+	.verify_blob = nop_verify_blob,
+	.auth_and_reset = pil_q6v3_reset_trusted,
+	.shutdown = pil_q6v3_shutdown_trusted,
+};
+
+static int __devinit pil_q6v3_driver_probe(struct platform_device *pdev)
+{
+	struct q6v3_data *drv;
+	struct resource *res;
+	struct pil_desc *desc;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -EINVAL;
+
+	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+	if (!drv)
+		return -ENOMEM;
+	platform_set_drvdata(pdev, drv);
+
+	drv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	if (!drv->base)
+		return -ENOMEM;
+
+	desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
+	if (!drv)
+		return -ENOMEM;
+
+	drv->pll = clk_get(&pdev->dev, "pll4");
+	if (IS_ERR(drv->pll))
+		return PTR_ERR(drv->pll);
+
+	setup_timer(&drv->timer, q6v3_remove_proxy_votes, (unsigned long)drv);
+	desc->name = "q6";
+	desc->dev = &pdev->dev;
+
+	if (pas_supported(PAS_Q6) > 0) {
+		desc->ops = &pil_q6v3_ops_trusted;
+		dev_info(&pdev->dev, "using secure boot\n");
+	} else {
+		desc->ops = &pil_q6v3_ops;
+		dev_info(&pdev->dev, "using non-secure boot\n");
+	}
+
+	if (msm_pil_register(desc))
+		return -EINVAL;
+	return 0;
+}
+
+static int __devexit pil_q6v3_driver_exit(struct platform_device *pdev)
+{
+	struct q6v3_data *drv = platform_get_drvdata(pdev);
+	del_timer_sync(&drv->timer);
+	return 0;
+}
+
+static struct platform_driver pil_q6v3_driver = {
+	.probe = pil_q6v3_driver_probe,
+	.remove = __devexit_p(pil_q6v3_driver_exit),
+	.driver = {
+		.name = "pil_qdsp6v3",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init pil_q6v3_init(void)
+{
+	return platform_driver_register(&pil_q6v3_driver);
+}
+module_init(pil_q6v3_init);
+
+static void __exit pil_q6v3_exit(void)
+{
+	platform_driver_unregister(&pil_q6v3_driver);
+}
+module_exit(pil_q6v3_exit);
+
+MODULE_DESCRIPTION("Support for booting QDSP6v3 (Hexagon) processors");
+MODULE_LICENSE("GPL v2");