#include <linux/suspend.h>
#define LPAPM_CLK 24000000
+#define DDR_AUDIO_CLK 50000000
#define DDR_MED_CLK 400000000
#define DDR3_NORMAL_CLK 528000000
#define GPC_PGC_GPU_PGCR_OFFSET 0x260
extern void mx6sl_wait (int arm_podf, unsigned long wfi_iram_addr);
void *mx6sl_ddr_freq_base;
-void (*mx6sl_ddr_freq_change_iram)(int ddr_freq) = NULL;
+void (*mx6sl_ddr_freq_change_iram)(int ddr_freq, int low_bus_freq_mode) = NULL;
extern void mx6sl_ddr_iram(int ddr_freq);
extern int init_mmdc_settings(void);
if (lp_audio_freq) {
/* Need to ensure that PLL2_PFD_400M is kept ON. */
clk_enable(pll2_400);
- update_ddr_freq(50000000);
+ update_ddr_freq(DDR_AUDIO_CLK);
/* Make sure periph clk's parent also got updated */
clk_set_parent(periph_clk, pll2_200);
audio_bus_freq_mode = 1;
low_bus_freq_mode = 0;
} else {
- update_ddr_freq(24000000);
+ update_ddr_freq(LPAPM_CLK);
/* Make sure periph clk's parent also got updated */
clk_set_parent(periph_clk, osc_clk);
if (audio_bus_freq_mode)
spin_lock_irqsave(&freq_lock, flags);
- /* Set periph_clk to be sourced from OSC_CLK */
- /* Set AXI to 24MHz. */
- clk_set_parent(periph_clk, osc_clk);
- clk_set_rate(axi_clk, clk_round_rate(axi_clk, LPAPM_CLK));
- /* Set AHB to 24MHz. */
- clk_set_rate(ahb_clk, clk_round_rate(ahb_clk, LPAPM_CLK));
-
- /* Set MMDC clk to 24MHz. */
- /* Since we are going to set PLL2 in bypass mode,
- * move the CPU clock off PLL2.
- */
- /* Ensure that the clock will be at lowest possible freq. */
- org_arm_podf = __raw_readl(MXC_CCM_CACRR);
- div = clk_get_rate(pll1) / cpu_op_tbl[cpu_op_nr - 1].cpu_rate;
-
- reg = __raw_writel(div - 1, MXC_CCM_CACRR);
- while (__raw_readl(MXC_CCM_CDHIPR))
- ;
- clk_set_parent(pll1_sw_clk, pll1);
+ if (high_bus_freq_mode) {
+ /* Set periph_clk to be sourced from OSC_CLK */
+ /* Set AXI to 24MHz. */
+ clk_set_parent(periph_clk, osc_clk);
+ clk_set_rate(axi_clk,
+ clk_round_rate(axi_clk, LPAPM_CLK));
+ /* Set AHB to 24MHz. */
+ clk_set_rate(ahb_clk,
+ clk_round_rate(ahb_clk, LPAPM_CLK));
+ }
+ if (lp_audio_freq) {
+ /* PLL2 is on in this mode, as DDR is at 50MHz. */
+ /* Now change DDR freq while running from IRAM. */
+ mx6sl_ddr_freq_change_iram(DDR_AUDIO_CLK,
+ low_bus_freq_mode);
+
+ if (low_bus_freq_mode) {
+ /* Swtich ARM to run off PLL2_PFD2_400MHz
+ * since DDR is anway at 50MHz.
+ */
+ clk_set_parent(pll1_sw_clk, pll2_400);
+
+ /* Ensure that the clock will be
+ * at original speed.
+ */
+ reg = __raw_writel(org_arm_podf, MXC_CCM_CACRR);
+ while (__raw_readl(MXC_CCM_CDHIPR))
+ ;
+ }
+ low_bus_freq_mode = 0;
+ audio_bus_freq_mode = 1;
+ } else {
+ /* Set MMDC clk to 24MHz. */
+ /* Since we are going to set PLL2 in bypass mode,
+ * move the CPU clock off PLL2.
+ */
+ /* Ensure that the clock will be at
+ * lowest possible freq.
+ */
+ org_arm_podf = __raw_readl(MXC_CCM_CACRR);
+ div = clk_get_rate(pll1) /
+ cpu_op_tbl[cpu_op_nr - 1].cpu_rate;
- /* Now change DDR freq while running from IRAM. */
- mx6sl_ddr_freq_change_iram(LPAPM_CLK);
+ reg = __raw_writel(div - 1, MXC_CCM_CACRR);
+ while (__raw_readl(MXC_CCM_CDHIPR))
+ ;
+ clk_set_parent(pll1_sw_clk, pll1);
- low_bus_freq_mode = 1;
- audio_bus_freq_mode = 0;
+ /* Now change DDR freq while running from IRAM. */
+ mx6sl_ddr_freq_change_iram(LPAPM_CLK,
+ low_bus_freq_mode);
+ low_bus_freq_mode = 1;
+ audio_bus_freq_mode = 0;
+ }
spin_unlock_irqrestore(&freq_lock, flags);
}
high_bus_freq_mode = 0;
unsigned long flags;
spin_lock_irqsave(&freq_lock, flags);
-
/* Change DDR freq in IRAM. */
- mx6sl_ddr_freq_change_iram(ddr_normal_rate);
+ mx6sl_ddr_freq_change_iram(ddr_normal_rate, low_bus_freq_mode);
/* Set periph_clk to be sourced from pll2_pfd2_400M */
/* First need to set the divider before changing the */
clk_round_rate(axi_clk, LPAPM_CLK / 2));
clk_set_parent(periph_clk, pll2_400);
- /* Now move ARM to be sourced from PLL2_400 too. */
- clk_set_parent(pll1_sw_clk, pll2_400);
-
- /* Ensure that the clock will be at original speed. */
- reg = __raw_writel(org_arm_podf, MXC_CCM_CACRR);
- while (__raw_readl(MXC_CCM_CDHIPR))
- ;
+ if (low_bus_freq_mode) {
+ /* Now move ARM to be sourced from PLL2_400 too. */
+ clk_set_parent(pll1_sw_clk, pll2_400);
+ /* Ensure that the clock will be at original speed. */
+ reg = __raw_writel(org_arm_podf, MXC_CCM_CACRR);
+ while (__raw_readl(MXC_CCM_CDHIPR))
+ ;
+ }
high_bus_freq_mode = 1;
low_bus_freq_mode = 0;
audio_bus_freq_mode = 0;
-
spin_unlock_irqrestore(&freq_lock, flags);
} else {
clk_enable(pll3);
cmp r6, #0
bne periph2_clk_switch3
+ /* Now set the MMDC PODF back to 1.*/
+ ldr r6, [r2, #0x14]
+ bic r6, r6, #0x38
+ str r6, [r2, #0x14]
+
+mmdc_podf0:
+ ldr r6, [r2, #0x48]
+ cmp r6, #0
+ bne mmdc_podf0
+
.endm
.macro ddr_switch_400MHz
+ /* Check if we are switching between
+ * 400Mhz <-> 50MHz. If so, we only need to
+ * update MMDC divider.
+ */
+ cmp r1, #0
+ beq change_divider_only
+
/* Set MMDC divider first, in case PLL3 is at 480MHz. */
ldr r6, [r3, #0x10]
and r6, r6, #0x10000
cmp r6, #0
bne periph2_clk_switch6
- /* Now set the MMDC PODF back to 1.*/
-
+change_divider_only:
+ /* Calculate the MMDC divider
+ * based on the requested freq.
+ */
+ ldr r6, =400000000
+ ldr r4, =0
+Loop2:
+ sub r6, r6, r0
+ cmp r6, r0
+ blt Div_Found
+ add r4, r4, #1
+ bgt Loop2
+
+ /* Shift divider into correct offset. */
+ lsl r4, r4, #3
+Div_Found:
+ /* Set the MMDC PODF. */
ldr r6, [r2, #0x14]
bic r6, r6, #0x38
+ orr r6, r6, r4
str r6, [r2, #0x14]
mmdc_podf1:
.endm
- .macro mmdc_clk_lower_100MHz
-
- /* Prior to reducing the DDR frequency (at 528/400 MHz),
- read the Measure unit count bits (MU_UNIT_DEL_NUM) */
- ldr r5, =0x8B8
- ldr r6, [r8, r5]
- /* Original MU unit count */
- mov r6, r6, LSR #16
- ldr r4, =0x3FF
- and r6, r6, r4
- /* Original MU unit count * 2 */
- mov r1, r6, LSL #1
- /* Bypass the automatic measure unit when below 100 MHz
- by setting the Measure unit bypass enable bit (MU_BYP_EN) */
- ldr r6, [r8, r5]
- orr r6, r6, #0x400
- str r6, [r8, r5]
- /* Double the measure count value read in step 1 and program it in the
- measurement bypass bits (MU_BYP_VAL) of the MMDC PHY Measure Unit
- Register for the reduced frequency operation below 100 MHz */
- ldr r6, [r8, r5]
- ldr r4, =0x3FF
- bic r6, r6, r4
- orr r6, r6, r1
- str r6, [r8, r5]
- .endm
-
- .macro mmdc_clk_above_100MHz
-
- /* Make sure that the PHY measurement unit is NOT in bypass mode */
- ldr r5, =0x8B8
- ldr r6, [r8, r5]
- bic r6, r6, #0x400
- str r6, [r8, r5]
- .endm
+ .macro mmdc_clk_lower_100MHz
+
+ /* Prior to reducing the DDR frequency (at 528/400 MHz),
+ read the Measure unit count bits (MU_UNIT_DEL_NUM) */
+ ldr r5, =0x8B8
+ ldr r6, [r8, r5]
+ /* Original MU unit count */
+ mov r6, r6, LSR #16
+ ldr r4, =0x3FF
+ and r6, r6, r4
+ /* Original MU unit count * 2 */
+ mov r7, r6, LSL #1
+ /* Bypass the automatic measure unit when below 100 MHz
+ by setting the Measure unit bypass enable bit (MU_BYP_EN) */
+ ldr r6, [r8, r5]
+ orr r6, r6, #0x400
+ str r6, [r8, r5]
+ /* Double the measure count value read in step 1 and program it in the
+ * measurement bypass bits (MU_BYP_VAL) of the MMDC PHY Measure Unit
+ * Register for the reduced frequency operation below 100 MHz
+ */
+ ldr r6, [r8, r5]
+ ldr r4, =0x3FF
+ bic r6, r6, r4
+ orr r6, r6, r7
+ str r6, [r8, r5]
+ /* Now perform a Force Measurement. */
+ ldr r6, [r8, r5]
+ orr r6, r6, #0x800
+ str r6, [r8, r5]
+ /* Wait for FRC_MSR to clear. */
+force_measure:
+ ldr r6, [r8, r5]
+ and r6, r6, #0x800
+ cmp r6, #0x0
+ bne force_measure
+
+ .endm
+
+ .macro mmdc_clk_above_100MHz
+
+ /* Make sure that the PHY measurement unit is NOT in bypass mode */
+ ldr r5, =0x8B8
+ ldr r6, [r8, r5]
+ bic r6, r6, #0x400
+ str r6, [r8, r5]
+ /* Now perform a Force Measurement. */
+ ldr r6, [r8, r5]
+ orr r6, r6, #0x800
+ str r6, [r8, r5]
+ /* Wait for FRC_MSR to clear. */
+force_measure1:
+ ldr r6, [r8, r5]
+ and r6, r6, #0x800
+ cmp r6, #0x0
+ bne force_measure1
+ .endm
/*
* mx6sl_ddr_iram
* Make sure DDR is in self-refresh.
* IRQs are already disabled.
* r0 : DDR freq.
+ * r1: low_bus_freq_mode flag
*/
ENTRY(mx6sl_ddr_iram)
str r6, [r8, #0x4]
/* Delay for a while */
- ldr r1, =10
+ ldr r10, =10
delay1:
ldr r7, =0
cont1:
add r7, r7, #4
cmp r7, #16
bne cont1
- sub r1, r1, #1
- cmp r1, #0
+ sub r10, r10, #1
+ cmp r10, #0
bgt delay1
/* Make the DDR explicitly enter self-refresh. */
orr r6, r6, #0x100
str r6, [r8, #0x410]
- ldr r1, =24000000
- cmp r0, r1
+ ldr r10, =100000000
+ cmp r0, r10
+ bgt set_ddr_mu_above_100
+ mmdc_clk_lower_100MHz
+
+set_ddr_mu_above_100:
+ ldr r10, =24000000
+ cmp r0, r10
beq set_to_24MHz
- mmdc_clk_above_100MHz
ddr_switch_400MHz
+ ldr r10, =100000000
+ cmp r0, r10
+ blt done
+ mmdc_clk_above_100MHz
b done
set_to_24MHz:
- mmdc_clk_lower_100MHz
mx6sl_switch_to_24MHz
done:
bic r6, r6, #0x01
str r6, [r8, #0x404]
- ldr r1, =24000000
- cmp r0, r1
+ ldr r10, =24000000
+ cmp r0, r10
beq skip_power_down
/* Enable MMDC power down timer. */
ca9_do_idle();
} else {
- if (low_bus_freq_mode && cpu_is_mx6sl()) {
- u32 org_arm_podf = __raw_readl(MXC_CCM_CACRR);
+ if (low_bus_freq_mode || audio_bus_freq_mode) {
+ if (cpu_is_mx6sl() && low_bus_freq_mode) {
+ /* In this mode PLL2 i already in bypass,
+ * ARM is sourced from PLL1. The code in IRAM
+ * will set ARM to be sourced from STEP_CLK
+ * at 24MHz. It will also set DDR to 1MHz to
+ * reduce power.
+ */
+ u32 org_arm_podf = __raw_readl(MXC_CCM_CACRR);
- /* Need to run WFI code from IRAM so that
- * we can lower DDR freq.
- */
- mx6sl_wfi_iram(org_arm_podf,
- (unsigned long)mx6sl_wfi_iram_base);
+ /* Need to run WFI code from IRAM so that
+ * we can lower DDR freq.
+ */
+ mx6sl_wfi_iram(org_arm_podf,
+ (unsigned long)mx6sl_wfi_iram_base);
+ } else {
+ /* Need to set ARM to run at 24MHz since IPG
+ * is at 12MHz. This is valid for audio mode on
+ * MX6SL, and all low power modes on MX6DLS.
+ */
+ /* PLL1_SW_CLK is sourced from PLL2_PFD2400MHz
+ * at this point. Move it to bypassed PLL1.
+ */
+ reg = __raw_readl(MXC_CCM_CCSR);
+ reg &= ~MXC_CCM_CCSR_PLL1_SW_CLK_SEL;
+ __raw_writel(reg, MXC_CCM_CCSR);
- /* Clear the chicken bit to allow memories
- * to be powered down
- */
+ ca9_do_idle();
+
+ reg |= MXC_CCM_CCSR_PLL1_SW_CLK_SEL;
+ __raw_writel(reg, MXC_CCM_CCSR);
+ }
} else {
/*
* Implement the 12:5 ARM:IPG_CLK ratio