From 9d0a75717d954f99d815fb3fb3830a2bf5f58c54 Mon Sep 17 00:00:00 2001 From: Ranjani Vaidyanathan Date: Wed, 22 Aug 2012 13:26:11 -0500 Subject: [PATCH] ENGR00221161 [MX6SL]- Add audio bus freq mode support. Set DDR to 50MHz in low power audio playback. AHB/AXI are at 24MHz. Also fix correct usecount for PLL1 main clock. If not it causes issues when pll1_sw_clk's parent is changed. Signed-off-by: Ranjani Vaidyanathan --- arch/arm/mach-mx6/bus_freq.c | 101 +++++++++++++-------- arch/arm/mach-mx6/clock_mx6sl.c | 7 +- arch/arm/mach-mx6/mx6sl_ddr.S | 156 ++++++++++++++++++++++---------- arch/arm/mach-mx6/mx6sl_wfi.S | 22 +++++ arch/arm/mach-mx6/system.c | 40 ++++++-- 5 files changed, 231 insertions(+), 95 deletions(-) diff --git a/arch/arm/mach-mx6/bus_freq.c b/arch/arm/mach-mx6/bus_freq.c index 53a65fb893f6..8c04e51d8827 100644 --- a/arch/arm/mach-mx6/bus_freq.c +++ b/arch/arm/mach-mx6/bus_freq.c @@ -47,6 +47,7 @@ #include #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 @@ -81,7 +82,7 @@ void (*mx6sl_wfi_iram)(int arm_podf, unsigned long wfi_iram_addr) = NULL; 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); @@ -140,13 +141,13 @@ static void reduce_bus_freq_handler(struct work_struct *work) 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) @@ -167,32 +168,61 @@ static void reduce_bus_freq_handler(struct work_struct *work) 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; @@ -258,9 +288,8 @@ int set_high_bus_freq(int high_bus_freq) 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 */ @@ -271,18 +300,18 @@ int set_high_bus_freq(int high_bus_freq) 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); diff --git a/arch/arm/mach-mx6/clock_mx6sl.c b/arch/arm/mach-mx6/clock_mx6sl.c index d93320d59dd8..4839f1542b3a 100755 --- a/arch/arm/mach-mx6/clock_mx6sl.c +++ b/arch/arm/mach-mx6/clock_mx6sl.c @@ -1236,6 +1236,7 @@ static int _clk_arm_set_rate(struct clk *clk, unsigned long rate) pll1_sys_main_clk.disable(&pll1_sys_main_clk); pll1_sys_main_clk.usecount = 0; } + spin_unlock_irqrestore(&mx6sl_clk_lock, flags); return 0; @@ -2451,7 +2452,7 @@ static struct clk ssi1_clk = { #else .secondary = &mmdc_ch1_axi_clk[0], #endif - .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE, + .flags = AHB_AUDIO_SET_POINT | CPU_FREQ_TRIG_UPDATE, }; static unsigned long _clk_ssi2_get_rate(struct clk *clk) @@ -2525,7 +2526,7 @@ static struct clk ssi2_clk = { #else .secondary = &mmdc_ch1_axi_clk[0], #endif - .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE, + .flags = AHB_AUDIO_SET_POINT | CPU_FREQ_TRIG_UPDATE, }; static unsigned long _clk_ssi3_get_rate(struct clk *clk) @@ -2598,7 +2599,7 @@ static struct clk ssi3_clk = { #else .secondary = &mmdc_ch1_axi_clk[0], #endif - .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE, + .flags = AHB_AUDIO_SET_POINT | CPU_FREQ_TRIG_UPDATE, }; static unsigned long _clk_epdc_lcdif_pix_round_rate(struct clk *clk, diff --git a/arch/arm/mach-mx6/mx6sl_ddr.S b/arch/arm/mach-mx6/mx6sl_ddr.S index 78208c192b39..3059f3aa3c8c 100644 --- a/arch/arm/mach-mx6/mx6sl_ddr.S +++ b/arch/arm/mach-mx6/mx6sl_ddr.S @@ -102,10 +102,27 @@ periph2_clk_switch3: 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 @@ -191,10 +208,26 @@ periph2_clk_switch6: 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: @@ -204,41 +237,63 @@ 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 @@ -247,6 +302,7 @@ mmdc_podf1: * Make sure DDR is in self-refresh. * IRQs are already disabled. * r0 : DDR freq. + * r1: low_bus_freq_mode flag */ ENTRY(mx6sl_ddr_iram) @@ -285,7 +341,7 @@ mx6sl_ddr_freq_change: str r6, [r8, #0x4] /* Delay for a while */ - ldr r1, =10 + ldr r10, =10 delay1: ldr r7, =0 cont1: @@ -293,8 +349,8 @@ 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. */ @@ -313,16 +369,24 @@ poll_dvfs_set_1: 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: @@ -342,8 +406,8 @@ poll_dvfs_clear_1: 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. */ diff --git a/arch/arm/mach-mx6/mx6sl_wfi.S b/arch/arm/mach-mx6/mx6sl_wfi.S index 610a57f13730..d2d910383cb7 100644 --- a/arch/arm/mach-mx6/mx6sl_wfi.S +++ b/arch/arm/mach-mx6/mx6sl_wfi.S @@ -201,6 +201,11 @@ poll_dvfs_set_1: cmp r6, #0x2000000 bne poll_dvfs_set_1 + /* set SBS step-by-step mode */ + ldr r6, [r8, #0x410] + orr r6, r6, #0x100 + str r6, [r8, #0x410] + /* Now set DDR rate to 1MHz. */ /* DDR is from bypassed PLL2 on periph2_clk2 path. * Set the periph2_clk2_podf to divide by 8. @@ -215,6 +220,12 @@ poll_dvfs_set_1: orr r6, r6, #0x10 str r6, [r2, #0x14] + /* Loop till podf is accepted. */ +mmdc_podf: + ldr r6, [r2, #0x48] + cmp r6, #0x0 + bne mmdc_podf + /* Set the DDR IO in LPM state. */ sl_ddr_io_set_lpm @@ -321,6 +332,11 @@ ahb_podf1: bic r6, r6, #0x3f str r6, [r2, #0x14] +mmdc_podf1: + ldr r6, [r2, #0x48] + cmp r6, #0x0 + bne mmdc_podf1 + /* clear DVFS - exit from self refresh mode */ ldr r6, [r8, #0x404] bic r6, r6, #0x200000 @@ -337,6 +353,12 @@ poll_dvfs_clear_1: bic r6, r6, #0x01 str r6, [r8, #0x404] + /* clear SBS - unblock DDR accesses */ + ldr r6, [r8, #0x410] + bic r6, r6, #0x100 + str r6, [r8, #0x410] + + pop {r4,r5, r6, r7, r8, r9, r10} /* Restore registers */ diff --git a/arch/arm/mach-mx6/system.c b/arch/arm/mach-mx6/system.c index 75701dc6485a..686c58c1e0e5 100644 --- a/arch/arm/mach-mx6/system.c +++ b/arch/arm/mach-mx6/system.c @@ -252,18 +252,38 @@ void arch_idle_single_core(void) 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 -- 2.39.5