/* * enter_lpm_imx6_up and exit_lpm_imx6_up is used by * i.MX6SX/i.MX6UL for entering and exiting lpm mode. */ static void enter_lpm_imx6_up(void) { if (cpu_is_imx6sx() && imx_src_is_m4_enabled()) if (!check_m4_sleep()) pr_err("M4 is NOT in sleep!!!\n"); /* set periph_clk2 to source from OSC for periph */ imx_clk_set_parent(periph_clk2_sel, osc_clk); imx_clk_set_parent(periph_clk, periph_clk2); /* set ahb/ocram to 24MHz */ imx_clk_set_rate(ahb_clk, LPAPM_CLK); imx_clk_set_rate(ocram_clk, LPAPM_CLK); if (audio_bus_count) { /* Need to ensure that PLL2_PFD_400M is kept ON. */ clk_prepare_enable(pll2_400); if (ddr_type == MMDC_MDMISC_DDR_TYPE_DDR3) update_ddr_freq_imx6_up(LOW_AUDIO_CLK); else if (ddr_type == MMDC_MDMISC_DDR_TYPE_LPDDR2) update_lpddr2_freq(HIGH_AUDIO_CLK); imx_clk_set_parent(periph2_clk2_sel, pll3); imx_clk_set_parent(periph2_pre_clk, pll2_400); imx_clk_set_parent(periph2_clk, periph2_pre_clk); /* * As periph2_clk's parent is not changed from * high mode to audio mode, so clk framework * will not update its children's freq, but we * change the mmdc's podf in asm code, so here * need to update mmdc rate to make sure clk * tree is right, although it will not do any * change to hardware. */ if (high_bus_freq_mode) { if (ddr_type == MMDC_MDMISC_DDR_TYPE_DDR3) imx_clk_set_rate(mmdc_clk, LOW_AUDIO_CLK); else if (ddr_type == MMDC_MDMISC_DDR_TYPE_LPDDR2) imx_clk_set_rate(mmdc_clk, HIGH_AUDIO_CLK); } audio_bus_freq_mode = 1; low_bus_freq_mode = 0; cur_bus_freq_mode = BUS_FREQ_AUDIO; } else { if (ddr_type == MMDC_MDMISC_DDR_TYPE_DDR3) update_ddr_freq_imx6_up(LPAPM_CLK); else if (ddr_type == MMDC_MDMISC_DDR_TYPE_LPDDR2) update_lpddr2_freq(LPAPM_CLK); imx_clk_set_parent(periph2_clk2_sel, osc_clk); imx_clk_set_parent(periph2_clk, periph2_clk2); if (audio_bus_freq_mode) clk_disable_unprepare(pll2_400); low_bus_freq_mode = 1; audio_bus_freq_mode = 0; cur_bus_freq_mode = BUS_FREQ_LOW; } }
static void exit_lpm_imx6_up(void) { clk_prepare_enable(pll2_400); /* * lower ahb/ocram's freq first to avoid too high * freq during parent switch from OSC to pll3. */ if (cpu_is_imx6ul()) imx_clk_set_rate(ahb_clk, LPAPM_CLK / 4); else imx_clk_set_rate(ahb_clk, LPAPM_CLK / 3); imx_clk_set_rate(ocram_clk, LPAPM_CLK / 2); /* set periph clk to from pll2_bus on i.MX6UL */ if (cpu_is_imx6ul()) imx_clk_set_parent(periph_pre_clk, pll2_bus); /* set periph clk to from pll2_400 */ else imx_clk_set_parent(periph_pre_clk, pll2_400); imx_clk_set_parent(periph_clk, periph_pre_clk); /* set periph_clk2 to pll3 */ imx_clk_set_parent(periph_clk2_sel, pll3); if (ddr_type == MMDC_MDMISC_DDR_TYPE_DDR3) update_ddr_freq_imx6_up(ddr_normal_rate); else if (ddr_type == MMDC_MDMISC_DDR_TYPE_LPDDR2) update_lpddr2_freq(ddr_normal_rate); /* correct parent info after ddr freq change in asm code */ imx_clk_set_parent(periph2_pre_clk, pll2_400); imx_clk_set_parent(periph2_clk, periph2_pre_clk); imx_clk_set_parent(periph2_clk2_sel, pll3); /* * As periph2_clk's parent is not changed from * audio mode to high mode, so clk framework * will not update its children's freq, but we * change the mmdc's podf in asm code, so here * need to update mmdc rate to make sure clk * tree is right, although it will not do any * change to hardware. */ if (audio_bus_freq_mode) imx_clk_set_rate(mmdc_clk, ddr_normal_rate); clk_disable_unprepare(pll2_400); if (audio_bus_freq_mode) clk_disable_unprepare(pll2_400); }
static void exit_lpm_imx6sl(void) { /* Change DDR freq in IRAM. */ update_lpddr2_freq(ddr_normal_rate); /* * Fix the clock tree in kernel. * Make sure PLL2 rate is updated as it gets * un-bypassed in the DDR freq change code. */ imx_clk_set_parent(pll2_bypass, pll2); imx_clk_set_parent(periph2_pre_clk, pll2_400); imx_clk_set_parent(periph2_clk, periph2_pre_clk); /* Ensure that periph_clk is sourced from PLL2_400. */ imx_clk_set_parent(periph_pre_clk, pll2_400); /* * Before switching the perhiph_clk, ensure that the * AHB/AXI will not be too fast. */ imx_clk_set_rate(ahb_clk, LPAPM_CLK / 3); imx_clk_set_rate(ocram_clk, LPAPM_CLK / 2); imx_clk_set_parent(periph_clk, periph_pre_clk); if (low_bus_freq_mode || ultra_low_bus_freq_mode) { /* Move ARM from PLL1_SW_CLK to PLL2_400. */ imx_clk_set_parent(step_clk, pll2_400); imx_clk_set_parent(pll1_sw_clk, step_clk); /* * Need to ensure that PLL1 is bypassed and enabled * before ARM-PODF is set. */ clk_set_parent(pll1_bypass, pll1_bypass_src); imx_clk_set_rate(cpu_clk, org_arm_rate); ultra_low_bus_freq_mode = 0; } }
static void enter_lpm_imx6sl(void) { if (high_bus_freq_mode) { pll2_org_rate = clk_get_rate(pll2_bus); /* Set periph_clk to be sourced from OSC_CLK */ imx_clk_set_parent(periph_clk2_sel, osc_clk); imx_clk_set_parent(periph_clk, periph_clk2); /* Ensure AHB/AXI clks are at 24MHz. */ imx_clk_set_rate(ahb_clk, LPAPM_CLK); imx_clk_set_rate(ocram_clk, LPAPM_CLK); } if (audio_bus_count) { /* Set AHB to 8MHz to lower pwer.*/ imx_clk_set_rate(ahb_clk, LPAPM_CLK / 3); /* Set up DDR to 100MHz. */ update_lpddr2_freq(HIGH_AUDIO_CLK); /* Fix the clock tree in kernel */ imx_clk_set_parent(periph2_pre_clk, pll2_200); imx_clk_set_parent(periph2_clk, periph2_pre_clk); if (low_bus_freq_mode || ultra_low_bus_freq_mode) { /* * Fix the clock tree in kernel, make sure * pll2_bypass is updated as it is * sourced from PLL2. */ imx_clk_set_parent(pll2_bypass, pll2); /* * Swtich ARM to run off PLL2_PFD2_400MHz * since DDR is anyway at 100MHz. */ imx_clk_set_parent(step_clk, pll2_400); imx_clk_set_parent(pll1_sw_clk, step_clk); /* * Need to ensure that PLL1 is bypassed and enabled * before ARM-PODF is set. */ clk_set_parent(pll1_bypass, pll1_bypass_src); /* * Ensure that the clock will be * at original speed. */ imx_clk_set_rate(cpu_clk, org_arm_rate); } low_bus_freq_mode = 0; ultra_low_bus_freq_mode = 0; audio_bus_freq_mode = 1; cur_bus_freq_mode = BUS_FREQ_AUDIO; } else { u32 arm_div, pll1_rate; org_arm_rate = clk_get_rate(cpu_clk); if (low_bus_freq_mode && low_bus_count == 0) { /* * We are already in DDR @ 24MHz state, but * no one but ARM needs the DDR. In this case, * we can lower the DDR freq to 1MHz when ARM * enters WFI in this state. Keep track of this state. */ ultra_low_bus_freq_mode = 1; low_bus_freq_mode = 0; audio_bus_freq_mode = 0; cur_bus_freq_mode = BUS_FREQ_ULTRA_LOW; } else { if (!ultra_low_bus_freq_mode && !low_bus_freq_mode) { /* * Anyway, make sure the AHB is running at 24MHz * in low_bus_freq_mode. */ if (audio_bus_freq_mode) imx_clk_set_rate(ahb_clk, LPAPM_CLK); /* * Set DDR to 24MHz. * Since we are going to bypass PLL2, * we need to move ARM clk off PLL2_PFD2 * to PLL1. Make sure the PLL1 is running * at the lowest possible freq. * To work well with CPUFREQ we want to ensure that * the CPU freq does not change, so attempt to * get a freq as close to 396MHz as possible. */ imx_clk_set_rate(pll1, clk_round_rate(pll1, (org_arm_rate * 2))); pll1_rate = clk_get_rate(pll1); arm_div = pll1_rate / org_arm_rate; if (pll1_rate / arm_div > org_arm_rate) arm_div++; /* * Need to ensure that PLL1 is bypassed and enabled * before ARM-PODF is set. */ clk_set_parent(pll1_bypass, pll1); /* * Ensure ARM CLK is lower before * changing the parent. */ imx_clk_set_rate(cpu_clk, org_arm_rate / arm_div); /* Now set the ARM clk parent to PLL1_SYS. */ imx_clk_set_parent(pll1_sw_clk, pll1_sys); /* * Set STEP_CLK back to OSC to save power and * also to maintain the parent.The WFI iram code * will switch step_clk to osc, but the clock API * is not aware of the change and when a new request * to change the step_clk parent to pll2_pfd2_400M * is requested sometime later, the change is ignored. */ imx_clk_set_parent(step_clk, osc_clk); /* Now set DDR to 24MHz. */ update_lpddr2_freq(LPAPM_CLK); /* * Fix the clock tree in kernel. * Make sure PLL2 rate is updated as it gets * bypassed in the DDR freq change code. */ imx_clk_set_parent(pll2_bypass, pll2_bypass_src); imx_clk_set_parent(periph2_clk2_sel, pll2_bus); imx_clk_set_parent(periph2_clk, periph2_clk2); } if (low_bus_count == 0) { ultra_low_bus_freq_mode = 1; low_bus_freq_mode = 0; cur_bus_freq_mode = BUS_FREQ_ULTRA_LOW; } else { ultra_low_bus_freq_mode = 0; low_bus_freq_mode = 1; cur_bus_freq_mode = BUS_FREQ_LOW; } audio_bus_freq_mode = 0; } } }