/* * This structure is for passing necessary data for low level ocram * suspend code(arch/arm/mach-imx/suspend-imx6.S), if this struct * definition is changed, the offset definition in * arch/arm/mach-imx/suspend-imx6.S must be also changed accordingly, * otherwise, the suspend to ocram function will be broken!
*/ struct imx6_cpu_pm_info {
phys_addr_t pbase; /* The physical address of pm_info. */
phys_addr_t resume_addr; /* The physical resume address for asm code */
u32 ddr_type;
u32 pm_info_size; /* Size of pm_info. */ struct imx6_pm_base mmdc_base; struct imx6_pm_base src_base; struct imx6_pm_base iomuxc_base; struct imx6_pm_base ccm_base; struct imx6_pm_base gpc_base; struct imx6_pm_base l2_base;
u32 mmdc_io_num; /* Number of MMDC IOs which need saved/restored. */
u32 mmdc_io_val[MX6_MAX_MMDC_IO_NUM][2]; /* To save offset and value */
} __aligned(8);
void imx6_set_int_mem_clk_lpm(bool enable)
{
u32 val = readl_relaxed(ccm_base + CGPR);
val &= ~BM_CGPR_INT_MEM_CLK_LPM; if (enable)
val |= BM_CGPR_INT_MEM_CLK_LPM;
writel_relaxed(val, ccm_base + CGPR);
}
void imx6_enable_rbc(bool enable)
{
u32 val;
/* * need to mask all interrupts in GPC before * operating RBC configurations
*/
imx_gpc_mask_all();
/* configure RBC enable bit */
val = readl_relaxed(ccm_base + CCR);
val &= ~BM_CCR_RBC_EN;
val |= enable ? BM_CCR_RBC_EN : 0;
writel_relaxed(val, ccm_base + CCR);
/* configure RBC count */
val = readl_relaxed(ccm_base + CCR);
val &= ~BM_CCR_RBC_BYPASS_COUNT;
val |= enable ? BM_CCR_RBC_BYPASS_COUNT : 0;
writel(val, ccm_base + CCR);
/* * need to delay at least 2 cycles of CKIL(32K) * due to hardware design requirement, which is * ~61us, here we use 65us for safe
*/
udelay(65);
/* configure well bias enable bit */
val = readl_relaxed(ccm_base + CLPCR);
val &= ~BM_CLPCR_WB_PER_AT_LPM;
val |= enable ? BM_CLPCR_WB_PER_AT_LPM : 0;
writel_relaxed(val, ccm_base + CLPCR);
/* configure well bias count */
val = readl_relaxed(ccm_base + CCR);
val &= ~BM_CCR_WB_COUNT;
val |= enable ? BM_CCR_WB_COUNT : 0;
writel_relaxed(val, ccm_base + CCR);
}
int imx6_set_lpm(enum mxc_cpu_pwr_mode mode)
{
u32 val = readl_relaxed(ccm_base + CLPCR);
val &= ~BM_CLPCR_LPM; switch (mode) { case WAIT_CLOCKED: break; case WAIT_UNCLOCKED:
val |= 0x1 << BP_CLPCR_LPM;
val |= BM_CLPCR_ARM_CLK_DIS_ON_LPM; break; case STOP_POWER_ON:
val |= 0x2 << BP_CLPCR_LPM;
val &= ~BM_CLPCR_VSTBY;
val &= ~BM_CLPCR_SBYOS; if (cpu_is_imx6sl())
val |= BM_CLPCR_BYPASS_PMIC_READY; if (cpu_is_imx6sl() || cpu_is_imx6sx() || cpu_is_imx6ul() ||
cpu_is_imx6ull() || cpu_is_imx6sll() || cpu_is_imx6ulz())
val |= BM_CLPCR_BYP_MMDC_CH0_LPM_HS; else
val |= BM_CLPCR_BYP_MMDC_CH1_LPM_HS; break; case WAIT_UNCLOCKED_POWER_OFF:
val |= 0x1 << BP_CLPCR_LPM;
val &= ~BM_CLPCR_VSTBY;
val &= ~BM_CLPCR_SBYOS; break; case STOP_POWER_OFF:
val |= 0x2 << BP_CLPCR_LPM;
val |= 0x3 << BP_CLPCR_STBY_COUNT;
val |= BM_CLPCR_VSTBY;
val |= BM_CLPCR_SBYOS; if (cpu_is_imx6sl() || cpu_is_imx6sx())
val |= BM_CLPCR_BYPASS_PMIC_READY; if (cpu_is_imx6sl() || cpu_is_imx6sx() || cpu_is_imx6ul() ||
cpu_is_imx6ull() || cpu_is_imx6sll() || cpu_is_imx6ulz())
val |= BM_CLPCR_BYP_MMDC_CH0_LPM_HS; else
val |= BM_CLPCR_BYP_MMDC_CH1_LPM_HS; break; default: return -EINVAL;
}
/* * ERR007265: CCM: When improper low-power sequence is used, * the SoC enters low power mode before the ARM core executes WFI. * * Software workaround: * 1) Software should trigger IRQ #32 (IOMUX) to be always pending * by setting IOMUX_GPR1_GINT. * 2) Software should then unmask IRQ #32 in GPC before setting CCM * Low-Power mode. * 3) Software should mask IRQ #32 right after CCM Low-Power mode * is set (set bits 0-1 of CCM_CLPCR). * * Note that IRQ #32 is GIC SPI #0.
*/ if (mode != WAIT_CLOCKED)
imx_gpc_hwirq_unmask(0);
writel_relaxed(val, ccm_base + CLPCR); if (mode != WAIT_CLOCKED)
imx_gpc_hwirq_mask(0);
return 0;
}
staticint imx6q_suspend_finish(unsignedlong val)
{ if (!imx6_suspend_in_ocram_fn) {
cpu_do_idle();
} else { /* * call low level suspend function in ocram, * as we need to float DDR IO.
*/
local_flush_tlb_all(); /* check if need to flush internal L2 cache */ if (!((struct imx6_cpu_pm_info *)
suspend_ocram_base)->l2_base.vbase)
flush_cache_all();
imx6_suspend_in_ocram_fn(suspend_ocram_base);
}
return 0;
}
staticint imx6q_pm_enter(suspend_state_t state)
{ switch (state) { case PM_SUSPEND_STANDBY:
imx6_set_lpm(STOP_POWER_ON);
imx6_set_int_mem_clk_lpm(true);
imx_gpc_pre_suspend(false); if (cpu_is_imx6sl())
imx6sl_set_wait_clk(true); /* Zzz ... */
cpu_do_idle(); if (cpu_is_imx6sl())
imx6sl_set_wait_clk(false);
imx_gpc_post_resume();
imx6_set_lpm(WAIT_CLOCKED); break; case PM_SUSPEND_MEM:
imx6_set_lpm(STOP_POWER_OFF);
imx6_set_int_mem_clk_lpm(false);
imx6q_enable_wb(true); /* * For suspend into ocram, asm code already take care of * RBC setting, so we do NOT need to do that here.
*/ if (!imx6_suspend_in_ocram_fn)
imx6_enable_rbc(true);
imx_gpc_pre_suspend(true);
imx_anatop_pre_suspend(); /* Zzz ... */
cpu_suspend(0, imx6q_suspend_finish); if (cpu_is_imx6q() || cpu_is_imx6dl())
imx_smp_prepare();
imx_anatop_post_resume();
imx_gpc_post_resume();
imx6_enable_rbc(false);
imx6q_enable_wb(false);
imx6_set_int_mem_clk_lpm(true);
imx6_set_lpm(WAIT_CLOCKED); break; default: return -EINVAL;
}
/* * ccm physical address is not used by asm code currently, * so get ccm virtual address directly.
*/
pm_info->ccm_base.vbase = ccm_base;
ret = imx6_pm_get_base(&pm_info->mmdc_base, socdata->mmdc_compat); if (ret) {
pr_warn("%s: failed to get mmdc base %d!\n", __func__, ret); goto put_device;
}
ret = imx6_pm_get_base(&pm_info->src_base, socdata->src_compat); if (ret) {
pr_warn("%s: failed to get src base %d!\n", __func__, ret); goto src_map_failed;
}
ret = imx6_pm_get_base(&pm_info->iomuxc_base, socdata->iomuxc_compat); if (ret) {
pr_warn("%s: failed to get iomuxc base %d!\n", __func__, ret); goto iomuxc_map_failed;
}
ret = imx6_pm_get_base(&pm_info->gpc_base, socdata->gpc_compat); if (ret) {
pr_warn("%s: failed to get gpc base %d!\n", __func__, ret); goto gpc_map_failed;
}
if (socdata->pl310_compat) {
ret = imx6_pm_get_base(&pm_info->l2_base, socdata->pl310_compat); if (ret) {
pr_warn("%s: failed to get pl310-cache base %d!\n",
__func__, ret); goto pl310_cache_map_failed;
}
}
if (IS_ENABLED(CONFIG_SUSPEND)) {
ret = imx6q_suspend_init(socdata); if (ret)
pr_warn("%s: No DDR LPM support with suspend %d!\n",
__func__, ret);
}
/* * This is for SW workaround step #1 of ERR007265, see comments * in imx6_set_lpm for details of this errata. * Force IOMUXC irq pending, so that the interrupt to GPC can be * used to deassert dsm_request signal when the signal gets * asserted unexpectedly.
*/
gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); if (!IS_ERR(gpr))
regmap_update_bits(gpr, IOMUXC_GPR1, IMX6Q_GPR1_GINT,
IMX6Q_GPR1_GINT);
}
/* * Initialize CCM_CLPCR_LPM into RUN mode to avoid ARM core * clock being shut down unexpectedly by WAIT mode.
*/
val = readl_relaxed(ccm_base + CLPCR);
val &= ~BM_CLPCR_LPM;
writel_relaxed(val, ccm_base + CLPCR);
if (of_property_read_bool(np, "fsl,pmic-stby-poweroff"))
imx6_pm_stby_poweroff_probe();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.