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");