in qcom_q6v5_mss.c [604:819]
static int q6v5proc_reset(struct q6v5 *qproc)
{
u32 val;
int ret;
int i;
if (qproc->version == MSS_SDM845) {
val = readl(qproc->reg_base + QDSP6SS_SLEEP);
val |= Q6SS_CBCR_CLKEN;
writel(val, qproc->reg_base + QDSP6SS_SLEEP);
ret = readl_poll_timeout(qproc->reg_base + QDSP6SS_SLEEP,
val, !(val & Q6SS_CBCR_CLKOFF), 1,
Q6SS_CBCR_TIMEOUT_US);
if (ret) {
dev_err(qproc->dev, "QDSP6SS Sleep clock timed out\n");
return -ETIMEDOUT;
}
/* De-assert QDSP6 stop core */
writel(1, qproc->reg_base + QDSP6SS_BOOT_CORE_START);
/* Trigger boot FSM */
writel(1, qproc->reg_base + QDSP6SS_BOOT_CMD);
ret = readl_poll_timeout(qproc->rmb_base + RMB_MBA_MSS_STATUS,
val, (val & BIT(0)) != 0, 10, BOOT_FSM_TIMEOUT);
if (ret) {
dev_err(qproc->dev, "Boot FSM failed to complete.\n");
/* Reset the modem so that boot FSM is in reset state */
q6v5_reset_deassert(qproc);
return ret;
}
goto pbl_wait;
} else if (qproc->version == MSS_SC7180 || qproc->version == MSS_SC7280) {
val = readl(qproc->reg_base + QDSP6SS_SLEEP);
val |= Q6SS_CBCR_CLKEN;
writel(val, qproc->reg_base + QDSP6SS_SLEEP);
ret = readl_poll_timeout(qproc->reg_base + QDSP6SS_SLEEP,
val, !(val & Q6SS_CBCR_CLKOFF), 1,
Q6SS_CBCR_TIMEOUT_US);
if (ret) {
dev_err(qproc->dev, "QDSP6SS Sleep clock timed out\n");
return -ETIMEDOUT;
}
/* Turn on the XO clock needed for PLL setup */
val = readl(qproc->reg_base + QDSP6SS_XO_CBCR);
val |= Q6SS_CBCR_CLKEN;
writel(val, qproc->reg_base + QDSP6SS_XO_CBCR);
ret = readl_poll_timeout(qproc->reg_base + QDSP6SS_XO_CBCR,
val, !(val & Q6SS_CBCR_CLKOFF), 1,
Q6SS_CBCR_TIMEOUT_US);
if (ret) {
dev_err(qproc->dev, "QDSP6SS XO clock timed out\n");
return -ETIMEDOUT;
}
/* Configure Q6 core CBCR to auto-enable after reset sequence */
val = readl(qproc->reg_base + QDSP6SS_CORE_CBCR);
val |= Q6SS_CBCR_CLKEN;
writel(val, qproc->reg_base + QDSP6SS_CORE_CBCR);
/* De-assert the Q6 stop core signal */
writel(1, qproc->reg_base + QDSP6SS_BOOT_CORE_START);
/* Wait for 10 us for any staggering logic to settle */
usleep_range(10, 20);
/* Trigger the boot FSM to start the Q6 out-of-reset sequence */
writel(1, qproc->reg_base + QDSP6SS_BOOT_CMD);
/* Poll the MSS_STATUS for FSM completion */
ret = readl_poll_timeout(qproc->rmb_base + RMB_MBA_MSS_STATUS,
val, (val & BIT(0)) != 0, 10, BOOT_FSM_TIMEOUT);
if (ret) {
dev_err(qproc->dev, "Boot FSM failed to complete.\n");
/* Reset the modem so that boot FSM is in reset state */
q6v5_reset_deassert(qproc);
return ret;
}
goto pbl_wait;
} else if (qproc->version == MSS_MSM8996 ||
qproc->version == MSS_MSM8998) {
int mem_pwr_ctl;
/* Override the ACC value if required */
writel(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);
/* BHS require xo cbcr to be enabled */
val = readl(qproc->reg_base + QDSP6SS_XO_CBCR);
val |= Q6SS_CBCR_CLKEN;
writel(val, qproc->reg_base + QDSP6SS_XO_CBCR);
/* Read CLKOFF bit to go low indicating CLK is enabled */
ret = readl_poll_timeout(qproc->reg_base + QDSP6SS_XO_CBCR,
val, !(val & Q6SS_CBCR_CLKOFF), 1,
Q6SS_CBCR_TIMEOUT_US);
if (ret) {
dev_err(qproc->dev,
"xo cbcr enabling timed out (rc:%d)\n", ret);
return ret;
}
/* Enable power block headswitch and wait for it to stabilize */
val = readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG);
val |= QDSP6v56_BHS_ON;
writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG);
val |= readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG);
udelay(1);
/* Put LDO in bypass mode */
val |= QDSP6v56_LDO_BYP;
writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG);
/* Deassert QDSP6 compiler memory clamp */
val = readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG);
val &= ~QDSP6v56_CLAMP_QMC_MEM;
writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG);
/* Deassert memory peripheral sleep and L2 memory standby */
val |= Q6SS_L2DATA_STBY_N | Q6SS_SLP_RET_N;
writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG);
/* Turn on L1, L2, ETB and JU memories 1 at a time */
if (qproc->version == MSS_MSM8996) {
mem_pwr_ctl = QDSP6SS_MEM_PWR_CTL;
i = 19;
} else {
/* MSS_MSM8998 */
mem_pwr_ctl = QDSP6V6SS_MEM_PWR_CTL;
i = 28;
}
val = readl(qproc->reg_base + mem_pwr_ctl);
for (; i >= 0; i--) {
val |= BIT(i);
writel(val, qproc->reg_base + mem_pwr_ctl);
/*
* Read back value to ensure the write is done then
* wait for 1us for both memory peripheral and data
* array to turn on.
*/
val |= readl(qproc->reg_base + mem_pwr_ctl);
udelay(1);
}
/* Remove word line clamp */
val = readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG);
val &= ~QDSP6v56_CLAMP_WL;
writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG);
} else {
/* 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);
/* 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(qproc->reg_base + QDSP6SS_PWR_CTL_REG);
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);
}
/* Remove IO clamp */
val &= ~Q6SS_CLAMP_IO;
writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG);
/* 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);
/* 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);
pbl_wait:
/* Wait for PBL status */
ret = q6v5_rmb_pbl_wait(qproc, 1000);
if (ret == -ETIMEDOUT) {
dev_err(qproc->dev, "PBL boot timed out\n");
} else if (ret != RMB_PBL_SUCCESS) {
dev_err(qproc->dev, "PBL returned unexpected status %d\n", ret);
ret = -EINVAL;
} else {
ret = 0;
}
return ret;
}