/* set cpu low power mode before WFI instruction */ void mxc_cpu_lp_set(enum mxc_cpu_pwr_mode mode) { int stop_mode = 0; void __iomem *anatop_base = IO_ADDRESS(ANATOP_BASE_ADDR); u32 ccm_clpcr, anatop_val; ccm_clpcr = __raw_readl(MXC_CCM_CLPCR) & ~(MXC_CCM_CLPCR_LPM_MASK); switch (mode) { case WAIT_CLOCKED: break; case WAIT_UNCLOCKED: ccm_clpcr |= 0x1 << MXC_CCM_CLPCR_LPM_OFFSET; break; case WAIT_UNCLOCKED_POWER_OFF: case STOP_POWER_OFF: case ARM_POWER_OFF: if (mode == WAIT_UNCLOCKED_POWER_OFF) { ccm_clpcr &= ~MXC_CCM_CLPCR_VSTBY; ccm_clpcr &= ~MXC_CCM_CLPCR_SBYOS; ccm_clpcr |= 0x1 << MXC_CCM_CLPCR_LPM_OFFSET; if (cpu_is_mx6sl()) { ccm_clpcr |= MXC_CCM_CLPCR_BYP_MMDC_CH0_LPM_HS; ccm_clpcr |= MXC_CCM_CLPCR_BYPASS_PMIC_VFUNC_READY; } else ccm_clpcr |= MXC_CCM_CLPCR_BYP_MMDC_CH1_LPM_HS; stop_mode = 0; } else if (mode == STOP_POWER_OFF) { ccm_clpcr |= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET; ccm_clpcr |= 0x3 << MXC_CCM_CLPCR_STBY_COUNT_OFFSET; ccm_clpcr |= MXC_CCM_CLPCR_VSTBY; ccm_clpcr |= MXC_CCM_CLPCR_SBYOS; if (cpu_is_mx6sl()) { ccm_clpcr |= MXC_CCM_CLPCR_BYP_MMDC_CH0_LPM_HS; ccm_clpcr |= MXC_CCM_CLPCR_BYPASS_PMIC_VFUNC_READY; } else ccm_clpcr |= MXC_CCM_CLPCR_BYP_MMDC_CH1_LPM_HS; stop_mode = 1; } else { ccm_clpcr |= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET; ccm_clpcr |= 0x3 << MXC_CCM_CLPCR_STBY_COUNT_OFFSET; ccm_clpcr |= MXC_CCM_CLPCR_VSTBY; ccm_clpcr |= MXC_CCM_CLPCR_SBYOS; if (cpu_is_mx6sl()) { ccm_clpcr |= MXC_CCM_CLPCR_BYP_MMDC_CH0_LPM_HS; ccm_clpcr |= MXC_CCM_CLPCR_BYPASS_PMIC_VFUNC_READY; } else ccm_clpcr |= MXC_CCM_CLPCR_BYP_MMDC_CH1_LPM_HS; stop_mode = 2; } break; case STOP_XTAL_ON: ccm_clpcr |= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET; ccm_clpcr |= MXC_CCM_CLPCR_VSTBY; ccm_clpcr &= ~MXC_CCM_CLPCR_SBYOS; if (cpu_is_mx6sl()) { ccm_clpcr |= MXC_CCM_CLPCR_BYP_MMDC_CH0_LPM_HS; ccm_clpcr |= MXC_CCM_CLPCR_BYPASS_PMIC_VFUNC_READY; } else ccm_clpcr |= MXC_CCM_CLPCR_BYP_MMDC_CH1_LPM_HS; stop_mode = 3; break; default: printk(KERN_WARNING "UNKNOWN cpu power mode: %d\n", mode); return; } if (stop_mode > 0) { gpc_set_wakeup(gpc_wake_irq); /* Power down and power up sequence */ /* The PUPSCR counter counts in terms of CLKIL (32KHz) cycles. * The PUPSCR should include the time it takes for the ARM LDO to * ramp up. */ __raw_writel(0x202, gpc_base + GPC_PGC_CPU_PUPSCR_OFFSET); /* The PDNSCR is a counter that counts in IPG_CLK cycles. This counter * can be set to minimum values to power down faster. */ __raw_writel(0x101, gpc_base + GPC_PGC_CPU_PDNSCR_OFFSET); if (stop_mode >= 2) { /* dormant mode, need to power off the arm core */ __raw_writel(0x1, gpc_base + GPC_PGC_CPU_PDN_OFFSET); if (cpu_is_mx6q() || cpu_is_mx6dl()) { /* If stop_mode_config is clear, then 2P5 will be off, need to enable weak 2P5, as DDR IO need 2P5 as pre-driver */ if ((__raw_readl(anatop_base + HW_ANADIG_ANA_MISC0) & BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG) == 0) { /* Enable weak 2P5 linear regulator */ anatop_val = __raw_readl(anatop_base + HW_ANADIG_REG_2P5); anatop_val |= BM_ANADIG_REG_2P5_ENABLE_WEAK_LINREG; __raw_writel(anatop_val, anatop_base + HW_ANADIG_REG_2P5); } if (mx6q_revision() != IMX_CHIP_REVISION_1_0) { /* Enable fet_odrive */ anatop_val = __raw_readl(anatop_base + HW_ANADIG_REG_CORE); anatop_val |= BM_ANADIG_REG_CORE_FET_ODRIVE; __raw_writel(anatop_val, anatop_base + HW_ANADIG_REG_CORE); } } else { if (stop_mode == 2) { /* Disable VDDHIGH_IN to VDDSNVS_IN * power path, only used when VDDSNVS_IN * is powered by dedicated * power rail */ anatop_val = __raw_readl(anatop_base + HW_ANADIG_ANA_MISC0); anatop_val |= BM_ANADIG_ANA_MISC0_RTC_RINGOSC_EN; __raw_writel(anatop_val, anatop_base + HW_ANADIG_ANA_MISC0); /* Need to enable pull down if 2P5 is disabled */ anatop_val = __raw_readl(anatop_base + HW_ANADIG_REG_2P5); anatop_val |= BM_ANADIG_REG_2P5_ENABLE_PULLDOWN; __raw_writel(anatop_val, anatop_base + HW_ANADIG_REG_2P5); } } if (stop_mode != 3) { /* Make sure we clear WB_COUNT * and re-config it. */ __raw_writel(__raw_readl(MXC_CCM_CCR) & (~MXC_CCM_CCR_WB_COUNT_MASK), MXC_CCM_CCR); /* Reconfigure WB, need to set WB counter * to 0x7 to make sure it work normally */ __raw_writel(__raw_readl(MXC_CCM_CCR) | (0x7 << MXC_CCM_CCR_WB_COUNT_OFFSET), MXC_CCM_CCR); /* Set WB_PER enable */ ccm_clpcr |= MXC_CCM_CLPCR_WB_PER_AT_LPM; } } if (cpu_is_mx6sl() || (mx6q_revision() > IMX_CHIP_REVISION_1_1) || (mx6dl_revision() > IMX_CHIP_REVISION_1_0)) { u32 reg; /* We need to allow the memories to be clock gated * in STOP mode, else the power consumption will * be very high. */ reg = __raw_readl(MXC_CCM_CGPR); reg |= MXC_CCM_CGPR_MEM_IPG_STOP_MASK; if (!cpu_is_mx6sl()) { /* * For MX6QTO1.2 or later and MX6DLTO1.1 or later, * ensure that the CCM_CGPR bit 17 is cleared before * dormant mode is entered. */ reg &= ~MXC_CCM_CGPR_WAIT_MODE_FIX; } __raw_writel(reg, MXC_CCM_CGPR); } } __raw_writel(ccm_clpcr, MXC_CCM_CLPCR); }
/* set cpu low power mode before WFI instruction */ void mxc_cpu_lp_set(enum mxc_cpu_pwr_mode mode) { int stop_mode = 0; void __iomem *anatop_base = IO_ADDRESS(ANATOP_BASE_ADDR); u32 ccm_clpcr, anatop_val; ccm_clpcr = __raw_readl(MXC_CCM_CLPCR) & ~(MXC_CCM_CLPCR_LPM_MASK); switch (mode) { case WAIT_CLOCKED: break; case WAIT_UNCLOCKED: ccm_clpcr |= 0x1 << MXC_CCM_CLPCR_LPM_OFFSET; break; case WAIT_UNCLOCKED_POWER_OFF: case STOP_POWER_OFF: case ARM_POWER_OFF: if (mode == WAIT_UNCLOCKED_POWER_OFF) { ccm_clpcr &= ~MXC_CCM_CLPCR_VSTBY; ccm_clpcr &= ~MXC_CCM_CLPCR_SBYOS; ccm_clpcr |= 0x1 << MXC_CCM_CLPCR_LPM_OFFSET; ccm_clpcr |= MXC_CCM_CLPCR_BYP_MMDC_CH1_LPM_HS; stop_mode = 0; } else if (mode == STOP_POWER_OFF) { ccm_clpcr |= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET; ccm_clpcr |= 0x3 << MXC_CCM_CLPCR_STBY_COUNT_OFFSET; ccm_clpcr |= MXC_CCM_CLPCR_VSTBY; ccm_clpcr |= MXC_CCM_CLPCR_SBYOS; ccm_clpcr |= MXC_CCM_CLPCR_BYP_MMDC_CH1_LPM_HS; stop_mode = 1; } else { ccm_clpcr |= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET; ccm_clpcr |= 0x3 << MXC_CCM_CLPCR_STBY_COUNT_OFFSET; ccm_clpcr |= MXC_CCM_CLPCR_VSTBY; ccm_clpcr |= MXC_CCM_CLPCR_SBYOS; ccm_clpcr |= MXC_CCM_CLPCR_BYP_MMDC_CH1_LPM_HS; stop_mode = 2; } break; case STOP_POWER_ON: ccm_clpcr |= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET; break; default: printk(KERN_WARNING "UNKNOWN cpu power mode: %d\n", mode); return; } if (stop_mode > 0) { gpc_set_wakeup(gpc_wake_irq); /* Power down and power up sequence */ __raw_writel(0xFFFFFFFF, gpc_base + GPC_PGC_CPU_PUPSCR_OFFSET); __raw_writel(0xFFFFFFFF, gpc_base + GPC_PGC_CPU_PDNSCR_OFFSET); /* dormant mode, need to power off the arm core */ if (stop_mode == 2) { __raw_writel(0x1, gpc_base + GPC_PGC_CPU_PDN_OFFSET); __raw_writel(0x1, gpc_base + GPC_PGC_GPU_PGCR_OFFSET); __raw_writel(0x1, gpc_base + GPC_CNTR_OFFSET); /* Enable weak 2P5 linear regulator */ anatop_val = __raw_readl(anatop_base + ANATOP_REG_2P5_OFFSET); anatop_val |= 1 << 18; __raw_writel(anatop_val, anatop_base + ANATOP_REG_2P5_OFFSET); /* Make sure ARM and SOC domain has same voltage and PU domain off */ anatop_val = __raw_readl(anatop_base + ANATOP_REG_CORE_OFFSET); anatop_val &= 0xff83001f; anatop_val |= (anatop_val & 0x1f) << 18; __raw_writel(anatop_val, anatop_base + ANATOP_REG_CORE_OFFSET); __raw_writel(__raw_readl(MXC_CCM_CCR) | MXC_CCM_CCR_RBC_EN, MXC_CCM_CCR); ccm_clpcr |= MXC_CCM_CLPCR_WB_PER_AT_LPM; } } __raw_writel(ccm_clpcr, MXC_CCM_CLPCR); }