@@ -23,24 +23,38 @@
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_address.h>
+#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/remoteproc.h>
#include <linux/reset.h>
#include <linux/soc/qcom/smem.h>
+#include <linux/iopoll.h>
#include <linux/soc/qcom/smem_state.h>
+#include <linux/mutex.h>
#include "remoteproc_internal.h"
#include "qcom_mdt_loader.h"
#include <linux/qcom_scm.h>
-#define MBA_FIRMWARE_NAME "mba.b00"
+#define MBA_FIRMWARE_NAME "mba.mbn"
#define MPSS_FIRMWARE_NAME "modem.mdt"
#define MPSS_CRASH_REASON_SMEM 421
+/* PBL/MBA interface registers */
+#define RMB_MBA_IMAGE 0x00
+#define RMB_PBL_STATUS 0x04
+#define RMB_MBA_COMMAND 0x08
+#define RMB_MBA_STATUS 0x0C
+#define RMB_PMI_META_DATA 0x10
+#define RMB_PMI_CODE_START 0x14
+#define RMB_PMI_CODE_LENGTH 0x18
+#define RMB_PROTOCOL_VERSION 0x1C
+#define RMB_MBA_DEBUG_INFORMATION 0x20
+
/* RMB Status Register Values */
#define RMB_PBL_SUCCESS 0x1
@@ -49,37 +63,33 @@
#define RMB_MBA_META_DATA_AUTH_SUCCESS 0x3
#define RMB_MBA_AUTH_COMPLETE 0x4
-/* PBL/MBA interface registers */
-#define RMB_MBA_IMAGE_REG 0x00
-#define RMB_PBL_STATUS_REG 0x04
-#define RMB_MBA_COMMAND_REG 0x08
-#define RMB_MBA_STATUS_REG 0x0C
-#define RMB_PMI_META_DATA_REG 0x10
-#define RMB_PMI_CODE_START_REG 0x14
-#define RMB_PMI_CODE_LENGTH_REG 0x18
-
#define RMB_CMD_META_DATA_READY 0x1
#define RMB_CMD_LOAD_READY 0x2
/* QDSP6SS Register Offsets */
-#define QDSP6SS_RESET_REG 0x014
-#define QDSP6SS_GFMUX_CTL_REG 0x020
-#define QDSP6SS_PWR_CTL_REG 0x030
+#define QDSP6SS_RESET 0x014
+#define QDSP6SS_GFMUX_CTL 0x020
+#define QDSP6SS_PWR_CTL 0x030
+#define QDSP6V6SS_MEM_PWR_CTL 0x034
+#define QDSP6SS_BHS_STATUS 0x078
+#define QDSP6SS_MEM_PWR_CTL 0x0B0
+#define QDSP6SS_STRAP_ACC 0x110
+#define QDSP6V62SS_BHS_STATUS 0x0C4
/* AXI Halt Register Offsets */
#define AXI_HALTREQ_REG 0x0
#define AXI_HALTACK_REG 0x4
#define AXI_IDLE_REG 0x8
-#define HALT_ACK_TIMEOUT_MS 100
+#define HALT_ACK_TIMEOUT_US 100000
/* QDSP6SS_RESET */
#define Q6SS_STOP_CORE BIT(0)
#define Q6SS_CORE_ARES BIT(1)
-#define Q6SS_BUS_ARES_ENABLE BIT(2)
+#define Q6SS_BUS_ARES_ENA BIT(2)
/* QDSP6SS_GFMUX_CTL */
-#define Q6SS_CLK_ENABLE BIT(1)
+#define Q6SS_CLK_ENA BIT(1)
/* QDSP6SS_PWR_CTL */
#define Q6SS_L2DATA_SLP_NRET_N_0 BIT(0)
@@ -93,28 +103,56 @@
#define QDSS_BHS_ON BIT(21)
#define QDSS_LDO_BYP BIT(22)
+/* QDSP6v55 parameters */
+#define QDSP6v55_LDO_ON BIT(26)
+#define QDSP6v55_LDO_BYP BIT(25)
+#define QDSP6v55_BHS_ON BIT(24)
+#define QDSP6v55_CLAMP_WL BIT(21)
+#define QDSP6v55_CLAMP_QMC_MEM BIT(22)
+#define L1IU_SLP_NRET_N BIT(15)
+#define L1DU_SLP_NRET_N BIT(14)
+#define L2PLRU_SLP_NRET_N BIT(13)
+#define QDSP6v55_BHS_EN_REST_ACK BIT(0)
+
+#define HALT_CHECK_MAX_LOOPS (200)
+#define BHS_CHECK_MAX_LOOPS (200)
+#define QDSP6SS_XO_CBCR (0x0038)
+
+#define QDSP6SS_ACC_OVERRIDE_VAL 0x20
+#define STATUS_PBL_SUCCESS 0x1
+
+#define POLL_INTERVAL_US 50
+
+
+
struct q6v5 {
struct device *dev;
struct rproc *rproc;
void __iomem *reg_base;
void __iomem *rmb_base;
-
- struct regmap *halt_map;
- u32 halt_q6;
- u32 halt_modem;
- u32 halt_nc;
+ void __iomem *restart_reg;
+ void __iomem *axi_halt_q6;
+ void __iomem *axi_halt_modem;
+ void __iomem *axi_halt_nc;
struct reset_control *mss_restart;
struct qcom_smem_state *state;
unsigned stop_bit;
- struct regulator_bulk_data supply[4];
+ struct regulator_bulk_data supply[3];
struct clk *ahb_clk;
struct clk *axi_clk;
struct clk *rom_clk;
+ struct clk *gpll0_mss_clk;
+ struct clk *snoc_axi_clk;
+ struct clk *mnoc_axi_clk;
+
+ struct clk *xo;
+ struct clk *pnoc_clk;
+ struct clk *qdss_clk;
struct completion start_done;
struct completion stop_done;
@@ -128,12 +166,16 @@ struct q6v5 {
phys_addr_t mpss_reloc;
void *mpss_region;
size_t mpss_size;
+ bool ahb_clk_vote;
+
+ struct mutex q6_lock;
+ u32 boot_count;
+ bool unvote_flag;
};
enum {
Q6V5_SUPPLY_CX,
Q6V5_SUPPLY_MX,
- Q6V5_SUPPLY_MSS,
Q6V5_SUPPLY_PLL,
};
@@ -141,10 +183,9 @@ static int q6v5_regulator_init(struct q6v5 *qproc)
{
int ret;
- qproc->supply[Q6V5_SUPPLY_CX].supply = "cx";
- qproc->supply[Q6V5_SUPPLY_MX].supply = "mx";
- qproc->supply[Q6V5_SUPPLY_MSS].supply = "mss";
- qproc->supply[Q6V5_SUPPLY_PLL].supply = "pll";
+ qproc->supply[Q6V5_SUPPLY_CX].supply = "vdd_cx";
+ qproc->supply[Q6V5_SUPPLY_MX].supply = "vdd_mx";
+ qproc->supply[Q6V5_SUPPLY_PLL].supply = "vdd_pll";
ret = devm_regulator_bulk_get(qproc->dev,
ARRAY_SIZE(qproc->supply), qproc->supply);
@@ -153,40 +194,187 @@ static int q6v5_regulator_init(struct q6v5 *qproc)
return ret;
}
- regulator_set_load(qproc->supply[Q6V5_SUPPLY_CX].consumer, 100000);
- regulator_set_load(qproc->supply[Q6V5_SUPPLY_MSS].consumer, 100000);
- regulator_set_load(qproc->supply[Q6V5_SUPPLY_PLL].consumer, 10000);
return 0;
}
static int q6v5_regulator_enable(struct q6v5 *qproc)
{
- struct regulator *mss = qproc->supply[Q6V5_SUPPLY_MSS].consumer;
struct regulator *mx = qproc->supply[Q6V5_SUPPLY_MX].consumer;
+ struct regulator *cx = qproc->supply[Q6V5_SUPPLY_CX].consumer;
+ struct regulator *vdd_pll = qproc->supply[Q6V5_SUPPLY_PLL].consumer;
int ret;
- /* TODO: Q6V5_SUPPLY_CX is supposed to be set to super-turbo here */
+ ret = regulator_set_voltage(mx, INT_MAX, INT_MAX);
+ if (ret) {
+ dev_err(qproc->dev, "Failed to set voltage for vreg_mx\n");
+ return ret;
+ }
+
+ ret = regulator_enable(mx);
+ if (ret) {
+ dev_err(qproc->dev, "Failed to enable vreg_mx\n");
+ goto err_mx_enable;
+ }
+
+ ret = regulator_set_voltage(cx, INT_MAX, INT_MAX);
+ if (ret) {
+ dev_err(qproc->dev, "Failed to request vdd_cx voltage.\n");
+ goto err_cx_voltage;
+ }
+
+ ret = regulator_set_load(cx, 100000);
+ if (ret < 0) {
+ dev_err(qproc->dev, "Failed to set vdd_cx mode.\n");
+ goto err_cx_set_load;
+ }
+
+ ret = regulator_enable(cx);
+ if (ret) {
+ dev_err(qproc->dev, "Failed to vote for vdd_cx\n");
+ goto err_cx_enable;
+ }
- ret = regulator_set_voltage(mx, 1050000, INT_MAX);
+ ret = regulator_set_voltage(vdd_pll, 1800000, 1800000);
+ if (ret) {
+ dev_err(qproc->dev, "Failed to set voltage for vdd_pll\n");
+ goto err_vreg_pll_set_vol;
+ }
+
+ ret = regulator_set_load(vdd_pll, 10000);
+ if (ret < 0) {
+ dev_err(qproc->dev, "Failed to set vdd_pll mode.\n");
+ goto err_vreg_pll_load;
+ }
+
+ ret = regulator_enable(vdd_pll);
+ if (ret) {
+ dev_err(qproc->dev, "Failed to vote for vdd_pll\n");
+ goto err_vreg_pll_enable;
+ }
+
+ return 0;
+
+err_vreg_pll_enable:
+ regulator_set_load(vdd_pll, 0);
+err_vreg_pll_load:
+ regulator_set_voltage(vdd_pll, 0, INT_MAX);
+err_vreg_pll_set_vol:
+ regulator_disable(cx);
+err_cx_enable:
+ regulator_set_load(cx, 0);
+err_cx_set_load:
+ regulator_set_voltage(cx, 0, INT_MAX);
+err_cx_voltage:
+ regulator_disable(mx);
+err_mx_enable:
+ regulator_set_voltage(mx, 0, INT_MAX);
+
+ return ret;
+}
+
+static int q6v5_clk_enable(struct q6v5 *qproc)
+{
+ int ret;
+
+ ret = clk_prepare_enable(qproc->xo);
if (ret)
- return ret;
+ goto out;
- regulator_set_voltage(mss, 1000000, 1150000);
+ ret = clk_prepare_enable(qproc->pnoc_clk);
+ if (ret)
+ goto err_pnoc_vote;
- return regulator_bulk_enable(ARRAY_SIZE(qproc->supply), qproc->supply);
+ ret = clk_prepare_enable(qproc->qdss_clk);
+ if (ret)
+ goto err_qdss_vote;
+
+ ret = clk_prepare_enable(qproc->ahb_clk);
+ if (ret)
+ goto err_ahb_clk;
+
+ ret = clk_prepare_enable(qproc->axi_clk);
+ if (ret)
+ goto err_axi_clk;
+
+ ret = clk_prepare_enable(qproc->rom_clk);
+ if (ret)
+ goto err_rom_clk;
+
+ ret = clk_prepare_enable(qproc->gpll0_mss_clk);
+ if (ret)
+ goto err_gpll0_mss_clk;
+
+ ret = clk_prepare_enable(qproc->snoc_axi_clk);
+ if (ret)
+ goto err_snoc_axi_clk;
+
+ ret = clk_prepare_enable(qproc->mnoc_axi_clk);
+ if (ret)
+ goto err_mnoc_axi_clk;
+
+ qproc->unvote_flag = false;
+ return 0;
+err_mnoc_axi_clk:
+ clk_disable_unprepare(qproc->snoc_axi_clk);
+err_snoc_axi_clk:
+ clk_disable_unprepare(qproc->gpll0_mss_clk);
+err_gpll0_mss_clk:
+ clk_disable_unprepare(qproc->rom_clk);
+err_rom_clk:
+ clk_disable_unprepare(qproc->axi_clk);
+err_axi_clk:
+ clk_disable_unprepare(qproc->ahb_clk);
+err_ahb_clk:
+ clk_disable_unprepare(qproc->qdss_clk);
+err_qdss_vote:
+ clk_disable_unprepare(qproc->pnoc_clk);
+err_pnoc_vote:
+ clk_disable_unprepare(qproc->xo);
+out:
+ dev_err(qproc->dev, "Clk Vote Failed\n");
+ return ret;
}
-static void q6v5_regulator_disable(struct q6v5 *qproc)
+static void q6v5_clk_disable(struct q6v5 *qproc)
{
- struct regulator *mss = qproc->supply[Q6V5_SUPPLY_MSS].consumer;
- struct regulator *mx = qproc->supply[Q6V5_SUPPLY_MX].consumer;
- /* TODO: Q6V5_SUPPLY_CX corner votes should be released */
+ clk_disable_unprepare(qproc->mnoc_axi_clk);
+ clk_disable_unprepare(qproc->snoc_axi_clk);
+ clk_disable_unprepare(qproc->gpll0_mss_clk);
+ clk_disable_unprepare(qproc->rom_clk);
+ clk_disable_unprepare(qproc->axi_clk);
+ if (!qproc->ahb_clk_vote)
+ clk_disable_unprepare(qproc->ahb_clk);
+}
- regulator_bulk_disable(ARRAY_SIZE(qproc->supply), qproc->supply);
- regulator_set_voltage(mx, 0, INT_MAX);
- regulator_set_voltage(mss, 0, 1150000);
+void q6v5_proxy_unvote(struct q6v5 *qproc)
+{
+ if (!qproc->unvote_flag) {
+ regulator_disable(qproc->supply[Q6V5_SUPPLY_PLL].consumer);
+ regulator_set_load(qproc->supply[Q6V5_SUPPLY_PLL].consumer, 0);
+ regulator_disable(qproc->supply[Q6V5_SUPPLY_CX].consumer);
+ regulator_set_load(qproc->supply[Q6V5_SUPPLY_CX].consumer, 0);
+ regulator_set_voltage(qproc->supply[Q6V5_SUPPLY_CX].consumer,
+ 0, INT_MAX);
+
+ clk_disable_unprepare(qproc->qdss_clk);
+ clk_disable_unprepare(qproc->pnoc_clk);
+ clk_disable_unprepare(qproc->xo);
+
+ regulator_disable(qproc->supply[Q6V5_SUPPLY_MX].consumer);
+ regulator_set_voltage(qproc->supply[Q6V5_SUPPLY_MX].consumer,
+ 0, INT_MAX);
+ }
+ qproc->unvote_flag = true;
+}
+
+static void pil_mss_restart_reg(struct q6v5 *qproc, u32 mss_restart)
+{
+ if (qproc->restart_reg) {
+ writel_relaxed(mss_restart, qproc->restart_reg);
+ udelay(2);
+ }
}
static int q6v5_load(struct rproc *rproc, const struct firmware *fw)
@@ -203,6 +391,25 @@ static int q6v5_load(struct rproc *rproc, const struct firmware *fw)
.load = q6v5_load,
};
+static void modem_log_rmb_regs(void __iomem *base)
+{
+ pr_err("RMB_MBA_IMAGE: %08x\n", readl_relaxed(base + RMB_MBA_IMAGE));
+ pr_err("RMB_PBL_STATUS: %08x\n", readl_relaxed(base + RMB_PBL_STATUS));
+ pr_err("RMB_MBA_COMMAND: %08x\n",
+ readl_relaxed(base + RMB_MBA_COMMAND));
+ pr_err("RMB_MBA_STATUS: %08x\n", readl_relaxed(base + RMB_MBA_STATUS));
+ pr_err("RMB_PMI_META_DATA: %08x\n",
+ readl_relaxed(base + RMB_PMI_META_DATA));
+ pr_err("RMB_PMI_CODE_START: %08x\n",
+ readl_relaxed(base + RMB_PMI_CODE_START));
+ pr_err("RMB_PMI_CODE_LENGTH: %08x\n",
+ readl_relaxed(base + RMB_PMI_CODE_LENGTH));
+ pr_err("RMB_PROTOCOL_VERSION: %08x\n",
+ readl_relaxed(base + RMB_PROTOCOL_VERSION));
+ pr_err("RMB_MBA_DEBUG_INFORMATION: %08x\n",
+ readl_relaxed(base + RMB_MBA_DEBUG_INFORMATION));
+}
+
static int q6v5_rmb_pbl_wait(struct q6v5 *qproc, int ms)
{
unsigned long timeout;
@@ -210,12 +417,14 @@ static int q6v5_rmb_pbl_wait(struct q6v5 *qproc, int ms)
timeout = jiffies + msecs_to_jiffies(ms);
for (;;) {
- val = readl(qproc->rmb_base + RMB_PBL_STATUS_REG);
+ val = readl_relaxed(qproc->rmb_base + RMB_PBL_STATUS);
if (val)
break;
- if (time_after(jiffies, timeout))
+ if (time_after(jiffies, timeout)) {
+ modem_log_rmb_regs(qproc->rmb_base);
return -ETIMEDOUT;
+ }
msleep(1);
}
@@ -231,7 +440,7 @@ static int q6v5_rmb_mba_wait(struct q6v5 *qproc, u32 status, int ms)
timeout = jiffies + msecs_to_jiffies(ms);
for (;;) {
- val = readl(qproc->rmb_base + RMB_MBA_STATUS_REG);
+ val = readl_relaxed(qproc->rmb_base + RMB_MBA_STATUS);
if (val < 0)
break;
@@ -240,8 +449,10 @@ static int q6v5_rmb_mba_wait(struct q6v5 *qproc, u32 status, int ms)
else if (status && val == status)
break;
- if (time_after(jiffies, timeout))
+ if (time_after(jiffies, timeout)) {
+ modem_log_rmb_regs(qproc->rmb_base);
return -ETIMEDOUT;
+ }
msleep(1);
}
@@ -251,53 +462,89 @@ static int q6v5_rmb_mba_wait(struct q6v5 *qproc, u32 status, int ms)
static int q6v5proc_reset(struct q6v5 *qproc)
{
- u32 val;
- int ret;
+ int ret, i, count;
+ u64 val;
+
+ /* Override the ACC value if required */
+ writel_relaxed(QDSP6SS_ACC_OVERRIDE_VAL,
+ qproc->reg_base + QDSP6SS_STRAP_ACC);
/* Assert resets, stop core */
- val = readl(qproc->reg_base + QDSP6SS_RESET_REG);
- val |= (Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENABLE | Q6SS_STOP_CORE);
- writel(val, qproc->reg_base + QDSP6SS_RESET_REG);
+ val = readl_relaxed(qproc->reg_base + QDSP6SS_RESET);
+ val |= (Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENA | Q6SS_STOP_CORE);
+ writel_relaxed(val, qproc->reg_base + QDSP6SS_RESET);
+
+ /* BHS require xo cbcr to be enabled */
+ val = readl_relaxed(qproc->reg_base + QDSP6SS_XO_CBCR);
+ val |= 0x1;
+ writel_relaxed(val, qproc->reg_base + QDSP6SS_XO_CBCR);
+ mb();
+ for (count = HALT_CHECK_MAX_LOOPS; count > 0; count--) {
+ val = readl_relaxed(qproc->reg_base + QDSP6SS_XO_CBCR);
+ if (!(val & BIT(31)))
+ break;
+ udelay(1);
+ }
+
+ val = readl_relaxed(qproc->reg_base + QDSP6SS_XO_CBCR);
+ if ((val & BIT(31)))
+ dev_err(qproc->dev, "Failed to enable xo branch clock.\n");
/* Enable power block headswitch, and wait for it to stabilize */
- val = readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG);
- val |= QDSS_BHS_ON | QDSS_LDO_BYP;
- writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG);
+ val = readl_relaxed(qproc->reg_base + QDSP6SS_PWR_CTL);
+ val |= QDSP6v55_BHS_ON;
+ writel_relaxed(val, qproc->reg_base + QDSP6SS_PWR_CTL);
udelay(1);
- /*
- * Turn on memories. L2 banks should be done individually
- * to minimize inrush current.
- */
- val = readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG);
- val |= Q6SS_SLP_RET_N | Q6SS_L2TAG_SLP_NRET_N |
- Q6SS_ETB_SLP_NRET_N | Q6SS_L2DATA_STBY_N;
- writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG);
- val |= Q6SS_L2DATA_SLP_NRET_N_2;
- writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG);
- val |= Q6SS_L2DATA_SLP_NRET_N_1;
- writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG);
- val |= Q6SS_L2DATA_SLP_NRET_N_0;
- writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG);
+ /* Put LDO in bypass mode */
+ val |= QDSP6v55_LDO_BYP;
+ writel_relaxed(val, qproc->reg_base + QDSP6SS_PWR_CTL);
+ mb();
+
+ /* Deassert QDSP6 compiler memory clamp */
+ val = readl_relaxed(qproc->reg_base + QDSP6SS_PWR_CTL);
+ val &= ~QDSP6v55_CLAMP_QMC_MEM;
+ writel_relaxed(val, qproc->reg_base + QDSP6SS_PWR_CTL);
+ mb();
+
+ /* Deassert memory peripheral sleep and L2 memory standby */
+ val |= (Q6SS_L2DATA_STBY_N | Q6SS_SLP_RET_N);
+ writel_relaxed(val, qproc->reg_base + QDSP6SS_PWR_CTL);
+ mb();
+
+ /* Turn on L1, L2, ETB and JU memories 1 at a time */
+ val = readl_relaxed(qproc->reg_base + QDSP6SS_MEM_PWR_CTL);
+ for (i = 19; i >= 0; i--) {
+ val |= BIT(i);
+ writel_relaxed(val, qproc->reg_base +
+ QDSP6SS_MEM_PWR_CTL);
+ /*
+ * Wait for 1us for both memory peripheral and
+ * data array to turn on.
+ */
+ mb();
+ udelay(1);
+ }
+
+ /* Remove word line clamp */
+ val = readl_relaxed(qproc->reg_base + QDSP6SS_PWR_CTL);
+ val &= ~QDSP6v55_CLAMP_WL;
+ writel_relaxed(val, qproc->reg_base + QDSP6SS_PWR_CTL);
+ mb();
/* Remove IO clamp */
val &= ~Q6SS_CLAMP_IO;
- writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG);
+ writel_relaxed(val, qproc->reg_base + QDSP6SS_PWR_CTL);
/* Bring core out of reset */
- val = readl(qproc->reg_base + QDSP6SS_RESET_REG);
- val &= ~Q6SS_CORE_ARES;
- writel(val, qproc->reg_base + QDSP6SS_RESET_REG);
+ val = readl_relaxed(qproc->reg_base + QDSP6SS_RESET);
+ val &= ~(Q6SS_CORE_ARES | Q6SS_STOP_CORE);
+ writel_relaxed(val, qproc->reg_base + QDSP6SS_RESET);
/* Turn on core clock */
- val = readl(qproc->reg_base + QDSP6SS_GFMUX_CTL_REG);
- val |= Q6SS_CLK_ENABLE;
- writel(val, qproc->reg_base + QDSP6SS_GFMUX_CTL_REG);
-
- /* Start core execution */
- val = readl(qproc->reg_base + QDSP6SS_RESET_REG);
- val &= ~Q6SS_STOP_CORE;
- writel(val, qproc->reg_base + QDSP6SS_RESET_REG);
+ val = readl_relaxed(qproc->reg_base + QDSP6SS_GFMUX_CTL);
+ val |= Q6SS_CLK_ENA;
+ writel_relaxed(val, qproc->reg_base + QDSP6SS_GFMUX_CTL);
/* Wait for PBL status */
ret = q6v5_rmb_pbl_wait(qproc, 1000);
@@ -313,48 +560,37 @@ static int q6v5proc_reset(struct q6v5 *qproc)
return ret;
}
-static void q6v5proc_halt_axi_port(struct q6v5 *qproc,
- struct regmap *halt_map,
- u32 offset)
+static void q6v5proc_halt_axi_port(void __iomem *halt_base)
{
- unsigned long timeout;
- unsigned int val;
int ret;
-
- /* Check if we're already idle */
- ret = regmap_read(halt_map, offset + AXI_IDLE_REG, &val);
- if (!ret && val)
- return;
+ u32 status;
/* Assert halt request */
- regmap_write(halt_map, offset + AXI_HALTREQ_REG, 1);
+ writel_relaxed(1, halt_base + AXI_HALTREQ_REG);
/* Wait for halt */
- timeout = jiffies + msecs_to_jiffies(HALT_ACK_TIMEOUT_MS);
- for (;;) {
- ret = regmap_read(halt_map, offset + AXI_HALTACK_REG, &val);
- if (ret || val || time_after(jiffies, timeout))
- break;
-
- msleep(1);
- }
-
- ret = regmap_read(halt_map, offset + AXI_IDLE_REG, &val);
- if (ret || !val)
- dev_err(qproc->dev, "port failed halt\n");
+ ret = readl_poll_timeout(halt_base + AXI_HALTACK_REG,
+ status, status != 0, 50, HALT_ACK_TIMEOUT_US);
+ if (ret)
+ pr_warn("Port %p halt timeout\n", halt_base);
+ else if (!readl_relaxed(halt_base + AXI_IDLE_REG))
+ pr_warn("Port %p halt failed\n", halt_base);
/* Clear halt request (port will remain halted until reset) */
- regmap_write(halt_map, offset + AXI_HALTREQ_REG, 0);
+ writel_relaxed(0, halt_base + AXI_HALTREQ_REG);
}
-static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw)
+static int q6v5_mpss_init_image(struct q6v5 *qproc,
+ const struct firmware *fw)
{
- unsigned long dma_attrs = DMA_ATTR_FORCE_CONTIGUOUS;
dma_addr_t phys;
void *ptr;
int ret;
+ unsigned long attrs;
- ptr = dma_alloc_attrs(qproc->dev, fw->size, &phys, GFP_KERNEL, dma_attrs);
+ attrs = DMA_ATTR_FORCE_CONTIGUOUS;
+ ptr = dma_alloc_attrs(qproc->dev, fw->size, &phys, GFP_KERNEL,
+ attrs);
if (!ptr) {
dev_err(qproc->dev, "failed to allocate mdt buffer\n");
return -ENOMEM;
@@ -362,16 +598,20 @@ static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw)
memcpy(ptr, fw->data, fw->size);
- writel(phys, qproc->rmb_base + RMB_PMI_META_DATA_REG);
- writel(RMB_CMD_META_DATA_READY, qproc->rmb_base + RMB_MBA_COMMAND_REG);
+ writel_relaxed(phys,
+ qproc->rmb_base + RMB_PMI_META_DATA);
+ writel_relaxed(RMB_CMD_META_DATA_READY,
+ qproc->rmb_base + RMB_MBA_COMMAND);
- ret = q6v5_rmb_mba_wait(qproc, RMB_MBA_META_DATA_AUTH_SUCCESS, 1000);
+ ret = q6v5_rmb_mba_wait(qproc, RMB_MBA_META_DATA_AUTH_SUCCESS,
+ 1000);
if (ret == -ETIMEDOUT)
dev_err(qproc->dev, "MPSS header authentication timed out\n");
else if (ret < 0)
- dev_err(qproc->dev, "MPSS header authentication failed: %d\n", ret);
+ dev_err(qproc->dev, "MPSS header authentication failed: %d\n",
+ ret);
- dma_free_attrs(qproc->dev, fw->size, ptr, phys, dma_attrs);
+ dma_free_attrs(qproc->dev, fw->size, ptr, phys, attrs);
return ret < 0 ? ret : 0;
}
@@ -413,14 +653,16 @@ static int q6v5_mpss_validate(struct q6v5 *qproc, const struct firmware *fw)
if (!phdr->p_memsz)
continue;
- size = readl(qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG);
+ size = readl_relaxed(qproc->rmb_base + RMB_PMI_CODE_LENGTH);
if (!size) {
- writel(boot_addr, qproc->rmb_base + RMB_PMI_CODE_START_REG);
- writel(RMB_CMD_LOAD_READY, qproc->rmb_base + RMB_MBA_COMMAND_REG);
+ writel_relaxed(boot_addr,
+ qproc->rmb_base + RMB_PMI_CODE_START);
+ writel_relaxed(RMB_CMD_LOAD_READY,
+ qproc->rmb_base + RMB_MBA_COMMAND);
}
size += phdr->p_memsz;
- writel(size, qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG);
+ writel_relaxed(size, qproc->rmb_base + RMB_PMI_CODE_LENGTH);
}
ret = q6v5_rmb_mba_wait(qproc, RMB_MBA_AUTH_COMPLETE, 10000);
@@ -455,7 +697,8 @@ static int q6v5_mpss_load(struct q6v5 *qproc)
qproc->mpss_reloc = fw_addr;
/* Initialize the RMB validator */
- writel(0, qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG);
+ writel_relaxed(0,
+ qproc->rmb_base + RMB_PMI_CODE_LENGTH);
ret = q6v5_mpss_init_image(qproc, fw);
if (ret)
@@ -473,9 +716,8 @@ static int q6v5_mpss_load(struct q6v5 *qproc)
return ret < 0 ? ret : 0;
}
-static int q6v5_start(struct rproc *rproc)
+static int q6v5_start(struct q6v5 *qproc)
{
- struct q6v5 *qproc = (struct q6v5 *)rproc->priv;
int ret;
ret = q6v5_regulator_enable(qproc);
@@ -484,25 +726,16 @@ static int q6v5_start(struct rproc *rproc)
return ret;
}
- ret = reset_control_deassert(qproc->mss_restart);
+ ret = q6v5_clk_enable(qproc);
if (ret) {
- dev_err(qproc->dev, "failed to deassert mss restart\n");
- goto disable_vdd;
+ dev_err(qproc->dev, "failed to enable clocks\n");
+ goto err_clks;
}
- ret = clk_prepare_enable(qproc->ahb_clk);
- if (ret)
- goto assert_reset;
+ pil_mss_restart_reg(qproc, 0);
- ret = clk_prepare_enable(qproc->axi_clk);
- if (ret)
- goto disable_ahb_clk;
-
- ret = clk_prepare_enable(qproc->rom_clk);
- if (ret)
- goto disable_axi_clk;
-
- writel(qproc->mba_phys, qproc->rmb_base + RMB_MBA_IMAGE_REG);
+ writel_relaxed(qproc->mba_phys,
+ qproc->rmb_base + RMB_MBA_IMAGE);
ret = q6v5proc_reset(qproc);
if (ret)
@@ -517,9 +750,8 @@ static int q6v5_start(struct rproc *rproc)
dev_err(qproc->dev, "MBA returned unexpected status %d\n", ret);
ret = -EINVAL;
goto halt_axi_ports;
- }
-
- dev_info(qproc->dev, "MBA booted, loading mpss\n");
+ } else
+ dev_info(qproc->dev, "MBA booted, loading segments\n");
ret = q6v5_mpss_load(qproc);
if (ret)
@@ -534,57 +766,93 @@ static int q6v5_start(struct rproc *rproc)
}
qproc->running = true;
-
- /* TODO: All done, release the handover resources */
-
return 0;
halt_axi_ports:
- q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_q6);
- q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_modem);
- q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc);
-
- clk_disable_unprepare(qproc->rom_clk);
-disable_axi_clk:
- clk_disable_unprepare(qproc->axi_clk);
-disable_ahb_clk:
- clk_disable_unprepare(qproc->ahb_clk);
-assert_reset:
- reset_control_assert(qproc->mss_restart);
-disable_vdd:
- q6v5_regulator_disable(qproc);
+ q6v5proc_halt_axi_port(qproc->axi_halt_q6);
+ q6v5proc_halt_axi_port(qproc->axi_halt_modem);
+ q6v5proc_halt_axi_port(qproc->axi_halt_nc);
+ q6v5_clk_disable(qproc);
+err_clks:
+ pil_mss_restart_reg(qproc, 1);
+ q6v5_proxy_unvote(qproc);
return ret;
}
-static int q6v5_stop(struct rproc *rproc)
+static int q6v5_stop(struct q6v5 *qproc)
{
- struct q6v5 *qproc = (struct q6v5 *)rproc->priv;
int ret;
-
- qproc->running = false;
+ u64 val;
qcom_smem_state_update_bits(qproc->state,
BIT(qproc->stop_bit), BIT(qproc->stop_bit));
ret = wait_for_completion_timeout(&qproc->stop_done,
- msecs_to_jiffies(5000));
+ msecs_to_jiffies(10000));
if (ret == 0)
dev_err(qproc->dev, "timed out on wait\n");
qcom_smem_state_update_bits(qproc->state, BIT(qproc->stop_bit), 0);
- q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_q6);
- q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_modem);
- q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc);
+ q6v5proc_halt_axi_port(qproc->axi_halt_q6);
+ q6v5proc_halt_axi_port(qproc->axi_halt_modem);
+ q6v5proc_halt_axi_port(qproc->axi_halt_nc);
- reset_control_assert(qproc->mss_restart);
- clk_disable_unprepare(qproc->rom_clk);
- clk_disable_unprepare(qproc->axi_clk);
- clk_disable_unprepare(qproc->ahb_clk);
- q6v5_regulator_disable(qproc);
+ /*
+ * Assert QDSP6 I/O clamp, memory wordline clamp, and compiler
+ * memory clamp as a software workaround to avoid high MX
+ * current during LPASS/MSS restart.
+ */
+
+ val = readl_relaxed(qproc->reg_base + QDSP6SS_PWR_CTL);
+ val |= (Q6SS_CLAMP_IO | QDSP6v55_CLAMP_WL |
+ QDSP6v55_CLAMP_QMC_MEM);
+ writel_relaxed(val, qproc->reg_base + QDSP6SS_PWR_CTL);
+ mb();
+
+ pil_mss_restart_reg(qproc, 1);
+ if (qproc->running) {
+ q6v5_clk_disable(qproc);
+ q6v5_proxy_unvote(qproc);
+ qproc->running = false;
+ }
+
+ return 0;
+}
+
+static int mss_boot(struct rproc *rproc)
+{
+ struct q6v5 *qproc = (struct q6v5 *)rproc->priv;
+ int ret;
+ mutex_lock(&qproc->q6_lock);
+ if (!qproc->boot_count) {
+ ret = q6v5_start(qproc);
+ if (ret)
+ goto err_start;
+ }
+ qproc->boot_count++;
+ mutex_unlock(&qproc->q6_lock);
return 0;
+
+err_start:
+ mutex_unlock(&qproc->q6_lock);
+ q6v5_stop(qproc);
+
+ return ret;
+}
+
+static int mss_stop(struct rproc *rproc)
+{
+ struct q6v5 *qproc = (struct q6v5 *)rproc->priv;
+ int ret;
+
+ mutex_lock(&qproc->q6_lock);
+ if (!--qproc->boot_count)
+ ret = q6v5_stop(qproc);
+ mutex_unlock(&qproc->q6_lock);
+ return ret;
}
static void *q6v5_da_to_va(struct rproc *rproc, u64 da, int len)
@@ -600,8 +868,8 @@ static void *q6v5_da_to_va(struct rproc *rproc, u64 da, int len)
}
static const struct rproc_ops q6v5_ops = {
- .start = q6v5_start,
- .stop = q6v5_stop,
+ .start = mss_boot,
+ .stop = mss_stop,
.da_to_va = q6v5_da_to_va,
};
@@ -651,86 +919,144 @@ static irqreturn_t q6v5_fatal_interrupt(int irq, void *dev)
return IRQ_HANDLED;
}
-static irqreturn_t q6v5_handover_interrupt(int irq, void *dev)
+static irqreturn_t q6v5_ready_interrupt(int irq, void *dev)
{
struct q6v5 *qproc = dev;
+ dev_info(qproc->dev, "Subsystem error monitoring/handling services are up\n");
complete(&qproc->start_done);
return IRQ_HANDLED;
}
+static irqreturn_t q6v5_handover_interrupt(int irq, void *dev)
+{
+ struct q6v5 *qproc = dev;
+
+ dev_info(qproc->dev, "Power/Clock ready interrupt received\n");
+ q6v5_proxy_unvote(qproc);
+ return IRQ_HANDLED;
+}
+
static irqreturn_t q6v5_stop_ack_interrupt(int irq, void *dev)
{
struct q6v5 *qproc = dev;
+ dev_info(qproc->dev, "Received stop ack interrupt from modem\n");
complete(&qproc->stop_done);
return IRQ_HANDLED;
}
static int q6v5_init_mem(struct q6v5 *qproc, struct platform_device *pdev)
{
- struct of_phandle_args args;
struct resource *res;
- int ret;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qdsp6");
qproc->reg_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(qproc->reg_base))
+ if (IS_ERR(qproc->reg_base)) {
+ dev_err(qproc->dev, "failed to get qdsp6_base\n");
return PTR_ERR(qproc->reg_base);
+ }
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rmb");
qproc->rmb_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(qproc->rmb_base))
+ if (IS_ERR(qproc->rmb_base)) {
+ dev_err(qproc->dev, "failed to get rmb_base\n");
return PTR_ERR(qproc->rmb_base);
+ }
- ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
- "qcom,halt-regs", 3, 0, &args);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to parse qcom,halt-regs\n");
- return -EINVAL;
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "restart_reg");
+ qproc->restart_reg = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (IS_ERR(qproc->restart_reg)) {
+ dev_err(qproc->dev, "failed to get restart_reg\n");
+ return PTR_ERR(qproc->restart_reg);
}
- qproc->halt_map = syscon_node_to_regmap(args.np);
- of_node_put(args.np);
- if (IS_ERR(qproc->halt_map))
- return PTR_ERR(qproc->halt_map);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "halt_q6");
+ qproc->axi_halt_q6 = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (IS_ERR(qproc->axi_halt_q6)) {
+ dev_err(qproc->dev, "failed to get axi_halt_q6\n");
+ return PTR_ERR(qproc->axi_halt_q6);
+ }
- qproc->halt_q6 = args.args[0];
- qproc->halt_modem = args.args[1];
- qproc->halt_nc = args.args[2];
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "halt_modem");
+ qproc->axi_halt_modem = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (IS_ERR(qproc->axi_halt_modem)) {
+ dev_err(qproc->dev, "failed to get halt_modem\n");
+ return PTR_ERR(qproc->axi_halt_modem);
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "halt_nc");
+ qproc->axi_halt_nc = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (IS_ERR(qproc->axi_halt_nc)) {
+ dev_err(qproc->dev, "failed to get axi_halt_nc\n");
+ return PTR_ERR(qproc->axi_halt_nc);
+ }
return 0;
}
static int q6v5_init_clocks(struct q6v5 *qproc)
{
- qproc->ahb_clk = devm_clk_get(qproc->dev, "iface");
+ qproc->ahb_clk = devm_clk_get(qproc->dev, "iface_clk");
if (IS_ERR(qproc->ahb_clk)) {
dev_err(qproc->dev, "failed to get iface clock\n");
return PTR_ERR(qproc->ahb_clk);
}
- qproc->axi_clk = devm_clk_get(qproc->dev, "bus");
+ qproc->axi_clk = devm_clk_get(qproc->dev, "bus_clk");
if (IS_ERR(qproc->axi_clk)) {
dev_err(qproc->dev, "failed to get bus clock\n");
return PTR_ERR(qproc->axi_clk);
}
- qproc->rom_clk = devm_clk_get(qproc->dev, "mem");
+ qproc->rom_clk = devm_clk_get(qproc->dev, "mem_clk");
if (IS_ERR(qproc->rom_clk)) {
dev_err(qproc->dev, "failed to get mem clock\n");
return PTR_ERR(qproc->rom_clk);
}
- return 0;
-}
+ qproc->snoc_axi_clk = devm_clk_get(qproc->dev, "snoc_axi_clk");
+ if (IS_ERR(qproc->snoc_axi_clk)) {
+ dev_err(qproc->dev, "failed to get snoc_axi_clk\n");
+ return PTR_ERR(qproc->snoc_axi_clk);
+ }
-static int q6v5_init_reset(struct q6v5 *qproc)
-{
- qproc->mss_restart = devm_reset_control_get(qproc->dev, NULL);
- if (IS_ERR(qproc->mss_restart)) {
- dev_err(qproc->dev, "failed to acquire mss restart\n");
- return PTR_ERR(qproc->mss_restart);
+ qproc->mnoc_axi_clk = devm_clk_get(qproc->dev, "mnoc_axi_clk");
+ if (IS_ERR(qproc->mnoc_axi_clk)) {
+ dev_err(qproc->dev, "failed to get mnoc_axi_clk clock\n");
+ return PTR_ERR(qproc->mnoc_axi_clk);
+ }
+
+ qproc->gpll0_mss_clk = devm_clk_get(qproc->dev, "gpll0_mss_clk");
+ if (IS_ERR(qproc->gpll0_mss_clk)) {
+ dev_err(qproc->dev, "failed to get gpll0_mss_clk clock\n");
+ return PTR_ERR(qproc->gpll0_mss_clk);
+ }
+
+ qproc->xo = devm_clk_get(qproc->dev, "xo");
+ if (IS_ERR(qproc->xo)) {
+ dev_err(qproc->dev, "failed to get xo\n");
+ return PTR_ERR(qproc->xo);
+ }
+
+ qproc->pnoc_clk = devm_clk_get(qproc->dev, "pnoc_clk");
+ if (IS_ERR(qproc->pnoc_clk)) {
+ dev_err(qproc->dev, "failed to get pnoc_clk clock\n");
+ return PTR_ERR(qproc->pnoc_clk);
+ }
+
+ qproc->qdss_clk = devm_clk_get(qproc->dev, "qdss_clk");
+ if (IS_ERR(qproc->qdss_clk)) {
+ dev_err(qproc->dev, "failed to get qdss_clk clock\n");
+ return PTR_ERR(qproc->qdss_clk);
}
return 0;
@@ -776,7 +1102,8 @@ static int q6v5_alloc_memory_region(struct q6v5 *qproc)
qproc->mba_phys = r.start;
qproc->mba_size = resource_size(&r);
- qproc->mba_region = devm_ioremap_wc(qproc->dev, qproc->mba_phys, qproc->mba_size);
+ qproc->mba_region = devm_ioremap_wc(qproc->dev, qproc->mba_phys,
+ qproc->mba_size);
if (!qproc->mba_region) {
dev_err(qproc->dev, "unable to map memory region: %pa+%zx\n",
&r.start, qproc->mba_size);
@@ -793,7 +1120,8 @@ static int q6v5_alloc_memory_region(struct q6v5 *qproc)
qproc->mpss_phys = qproc->mpss_reloc = r.start;
qproc->mpss_size = resource_size(&r);
- qproc->mpss_region = devm_ioremap_wc(qproc->dev, qproc->mpss_phys, qproc->mpss_size);
+ qproc->mpss_region = devm_ioremap_wc(qproc->dev, qproc->mpss_phys,
+ qproc->mpss_size);
if (!qproc->mpss_region) {
dev_err(qproc->dev, "unable to map memory region: %pa+%zx\n",
&r.start, qproc->mpss_size);
@@ -803,11 +1131,40 @@ static int q6v5_alloc_memory_region(struct q6v5 *qproc)
return 0;
}
+/* user interface to boot standalone modem */
+
+static ssize_t qproc_boot_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct q6v5 *qproc = dev_get_drvdata(dev);
+ int ret;
+
+ ret = rproc_boot(qproc->rproc);
+ return ret ? : size;
+}
+
+static ssize_t qproc_shutdown_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct q6v5 *qproc = dev_get_drvdata(dev);
+
+ rproc_shutdown(qproc->rproc);
+ return size;
+}
+
+static const struct device_attribute qproc_attrs[] = {
+ __ATTR(boot_modem, S_IWUSR, 0, qproc_boot_store),
+ __ATTR(shutdown_modem, S_IWUSR, 0, qproc_shutdown_store),
+};
+
+
static int q6v5_probe(struct platform_device *pdev)
{
struct q6v5 *qproc;
struct rproc *rproc;
- int ret;
+ int ret, i;
rproc = rproc_alloc(&pdev->dev, pdev->name, &q6v5_ops,
MBA_FIRMWARE_NAME, sizeof(*qproc));
@@ -842,9 +1199,9 @@ static int q6v5_probe(struct platform_device *pdev)
if (ret)
goto free_rproc;
- ret = q6v5_init_reset(qproc);
- if (ret)
- goto free_rproc;
+ qproc->ahb_clk_vote = of_property_read_bool(pdev->dev.of_node,
+ "qcom,ahb-clk-vote");
+ mutex_init(&qproc->q6_lock);
ret = q6v5_request_irq(qproc, pdev, "wdog", q6v5_wdog_interrupt);
if (ret < 0)
@@ -854,20 +1211,33 @@ static int q6v5_probe(struct platform_device *pdev)
if (ret < 0)
goto free_rproc;
- ret = q6v5_request_irq(qproc, pdev, "handover", q6v5_handover_interrupt);
+ ret = q6v5_request_irq(qproc, pdev, "ready", q6v5_ready_interrupt);
+ if (ret < 0)
+ goto free_rproc;
+
+ ret = q6v5_request_irq(qproc, pdev, "handover",
+ q6v5_handover_interrupt);
if (ret < 0)
goto free_rproc;
- ret = q6v5_request_irq(qproc, pdev, "stop-ack", q6v5_stop_ack_interrupt);
+ ret = q6v5_request_irq(qproc, pdev, "stop-ack",
+ q6v5_stop_ack_interrupt);
if (ret < 0)
goto free_rproc;
- qproc->state = qcom_smem_state_get(&pdev->dev, "stop", &qproc->stop_bit);
+ qproc->state = qcom_smem_state_get(&pdev->dev, "stop",
+ &qproc->stop_bit);
if (IS_ERR(qproc->state)) {
- ret = PTR_ERR(qproc->state);
+ dev_err(&pdev->dev, "unable to get smem state\n");
goto free_rproc;
}
+ for (i = 0; i < ARRAY_SIZE(qproc_attrs); i++) {
+ ret = device_create_file(&pdev->dev, &qproc_attrs[i]);
+ if (ret)
+ dev_err(&pdev->dev, "unable to create sysfs file\n");
+ }
+
ret = rproc_add(rproc);
if (ret)
goto free_rproc;
@@ -875,7 +1245,7 @@ static int q6v5_probe(struct platform_device *pdev)
return 0;
free_rproc:
- rproc_free(rproc);
+ rproc_put(rproc);
return ret;
}
@@ -885,7 +1255,7 @@ static int q6v5_remove(struct platform_device *pdev)
struct q6v5 *qproc = platform_get_drvdata(pdev);
rproc_del(qproc->rproc);
- rproc_free(qproc->rproc);
+ rproc_put(qproc->rproc);
return 0;
}