diff options
author | Ranjani Vaidyanathan <ra5478@freescale.com> | 2013-11-13 15:03:35 -0500 |
---|---|---|
committer | Nitin Garg <nitin.garg@freescale.com> | 2014-04-16 09:47:14 -0400 |
commit | fdb2a0eefc98f7b9d24d9b965b4a6b7e5830d5c1 (patch) | |
tree | 9cbb19106a20d6b3ea608710692a360593a6bc69 | |
parent | 8dfdc91887fc4ff52181875ef44a425cf2e8959e (diff) |
ENGR00288123 [iMX6SL] Add low power audio support
Add support for low power audio playback:
1. SSI2 is sourced from PLL4
2. Extern_audio_clk is sourced from pll4
3. PLL4 is in bypass mode during audio playback (based
on freq requested by extern_audio_clk and ssi2 clk)
4. DDR is at 100MHz, AHB is at 24MHz.
Signed-off-by: Ranjani Vaidyanathan <ra5478@freescale.com>
-rw-r--r-- | arch/arm/mach-imx/busfreq-imx6.c | 15 | ||||
-rw-r--r-- | arch/arm/mach-imx/clk-imx6sl.c | 31 | ||||
-rw-r--r-- | arch/arm/mach-imx/clk-pllv3.c | 86 |
3 files changed, 109 insertions, 23 deletions
diff --git a/arch/arm/mach-imx/busfreq-imx6.c b/arch/arm/mach-imx/busfreq-imx6.c index 142a0cc0228a..eddc5a0245a6 100644 --- a/arch/arm/mach-imx/busfreq-imx6.c +++ b/arch/arm/mach-imx/busfreq-imx6.c | |||
@@ -130,7 +130,8 @@ static void enter_lpm_imx6sl(void) | |||
130 | * Swtich ARM to run off PLL2_PFD2_400MHz | 130 | * Swtich ARM to run off PLL2_PFD2_400MHz |
131 | * since DDR is anyway at 100MHz. | 131 | * since DDR is anyway at 100MHz. |
132 | */ | 132 | */ |
133 | clk_set_parent(pll1_sw_clk, pll2_400); | 133 | clk_set_parent(step_clk, pll2_400); |
134 | clk_set_parent(pll1_sw_clk, step_clk); | ||
134 | /* | 135 | /* |
135 | * Ensure that the clock will be | 136 | * Ensure that the clock will be |
136 | * at original speed. | 137 | * at original speed. |
@@ -174,6 +175,15 @@ static void enter_lpm_imx6sl(void) | |||
174 | /* Now set the ARM clk parent to PLL1_SYS. */ | 175 | /* Now set the ARM clk parent to PLL1_SYS. */ |
175 | clk_set_parent(pll1_sw_clk, pll1_sys); | 176 | clk_set_parent(pll1_sw_clk, pll1_sys); |
176 | 177 | ||
178 | /* | ||
179 | * Set STEP_CLK back to OSC to save power and | ||
180 | * also to maintain the parent.The WFI iram code | ||
181 | * will switch step_clk to osc, but the clock API | ||
182 | * is not aware of the change and when a new request | ||
183 | * to change the step_clk parent to pll2_pfd2_400M | ||
184 | * is requested sometime later, the change is ignored. | ||
185 | */ | ||
186 | clk_set_parent(step_clk, osc_clk); | ||
177 | /* Now set DDR to 24MHz. */ | 187 | /* Now set DDR to 24MHz. */ |
178 | spin_lock_irqsave(&freq_lock, flags); | 188 | spin_lock_irqsave(&freq_lock, flags); |
179 | update_lpddr2_freq(LPAPM_CLK); | 189 | update_lpddr2_freq(LPAPM_CLK); |
@@ -336,7 +346,7 @@ int set_low_bus_freq(void) | |||
336 | * low bus freq mode to audio bus freq mode. | 346 | * low bus freq mode to audio bus freq mode. |
337 | * If so, the change needs to be done immediately. | 347 | * If so, the change needs to be done immediately. |
338 | */ | 348 | */ |
339 | if (audio_bus_count && low_bus_freq_mode) | 349 | if (audio_bus_count && (low_bus_freq_mode || ultra_low_bus_freq_mode)) |
340 | reduce_bus_freq(); | 350 | reduce_bus_freq(); |
341 | else | 351 | else |
342 | /* | 352 | /* |
@@ -465,7 +475,6 @@ void request_bus_freq(enum bus_freq_mode mode) | |||
465 | mutex_unlock(&bus_freq_mutex); | 475 | mutex_unlock(&bus_freq_mutex); |
466 | return; | 476 | return; |
467 | } | 477 | } |
468 | |||
469 | cancel_delayed_work_sync(&low_bus_freq_handler); | 478 | cancel_delayed_work_sync(&low_bus_freq_handler); |
470 | 479 | ||
471 | if (cpu_is_imx6dl()) { | 480 | if (cpu_is_imx6dl()) { |
diff --git a/arch/arm/mach-imx/clk-imx6sl.c b/arch/arm/mach-imx/clk-imx6sl.c index 7ff93fa2e84f..8210649fe4b3 100644 --- a/arch/arm/mach-imx/clk-imx6sl.c +++ b/arch/arm/mach-imx/clk-imx6sl.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #define PFD2_CLK_GATE (1 << 23) | 21 | #define PFD2_CLK_GATE (1 << 23) |
22 | #define PFD3_CLK_GATE (1 << 31) | 22 | #define PFD3_CLK_GATE (1 << 31) |
23 | #define CCDR_CH0_HS_BYP 17 | 23 | #define CCDR_CH0_HS_BYP 17 |
24 | #define OSC_RATE 24000000 | ||
24 | 25 | ||
25 | #define CCM_CCGR_OFFSET(index) (index * 2) | 26 | #define CCM_CCGR_OFFSET(index) (index * 2) |
26 | 27 | ||
@@ -88,6 +89,7 @@ static struct clk_div_table video_div_table[] = { | |||
88 | static struct clk *clks[IMX6SL_CLK_CLK_END]; | 89 | static struct clk *clks[IMX6SL_CLK_CLK_END]; |
89 | static struct clk_onecell_data clk_data; | 90 | static struct clk_onecell_data clk_data; |
90 | static u32 cur_arm_podf; | 91 | static u32 cur_arm_podf; |
92 | static u32 pll1_org_rate; | ||
91 | 93 | ||
92 | extern int low_bus_freq_mode; | 94 | extern int low_bus_freq_mode; |
93 | extern int audio_bus_freq_mode; | 95 | extern int audio_bus_freq_mode; |
@@ -104,7 +106,8 @@ void imx6sl_set_wait_clk(bool enter) | |||
104 | u32 parent_rate; | 106 | u32 parent_rate; |
105 | 107 | ||
106 | if (enter) { | 108 | if (enter) { |
107 | u32 wait_podf, new_parent_rate; | 109 | u32 wait_podf; |
110 | u32 new_parent_rate = OSC_RATE; | ||
108 | u32 ipg_rate = clk_get_rate(clks[IMX6SL_CLK_IPG]); | 111 | u32 ipg_rate = clk_get_rate(clks[IMX6SL_CLK_IPG]); |
109 | u32 max_arm_wait_clk = (12 * ipg_rate) / 5; | 112 | u32 max_arm_wait_clk = (12 * ipg_rate) / 5; |
110 | parent_rate = clk_get_rate(clks[IMX6SL_CLK_PLL1_SW]); | 113 | parent_rate = clk_get_rate(clks[IMX6SL_CLK_PLL1_SW]); |
@@ -127,10 +130,12 @@ void imx6sl_set_wait_clk(bool enter) | |||
127 | * from the bypassed PLL1 clocks so that we can run | 130 | * from the bypassed PLL1 clocks so that we can run |
128 | * ARM at 24MHz. | 131 | * ARM at 24MHz. |
129 | */ | 132 | */ |
130 | clk_set_parent(clks[IMX6SL_CLK_PLL1_SW], | 133 | pll1_org_rate = clk_get_rate(clks[IMX6SL_CLK_PLL1_SYS]); |
131 | clks[IMX6SL_CLK_PLL1_SYS]); | 134 | /* Ensure PLL1 is at 24MHz. */ |
132 | } | 135 | clk_set_rate(clks[IMX6SL_CLK_PLL1_SYS], OSC_RATE); |
133 | new_parent_rate = clk_get_rate(clks[IMX6SL_CLK_PLL1_SW]); | 136 | clk_set_parent(clks[IMX6SL_CLK_PLL1_SW], clks[IMX6SL_CLK_PLL1_SYS]); |
137 | } else | ||
138 | new_parent_rate = clk_get_rate(clks[IMX6SL_CLK_PLL1_SW]); | ||
134 | wait_podf = (new_parent_rate + max_arm_wait_clk - 1) / | 139 | wait_podf = (new_parent_rate + max_arm_wait_clk - 1) / |
135 | max_arm_wait_clk; | 140 | max_arm_wait_clk; |
136 | 141 | ||
@@ -140,10 +145,11 @@ void imx6sl_set_wait_clk(bool enter) | |||
140 | /* Move ARM back to PLL1. */ | 145 | /* Move ARM back to PLL1. */ |
141 | clk_set_parent(clks[IMX6SL_CLK_PLL1_SW], | 146 | clk_set_parent(clks[IMX6SL_CLK_PLL1_SW], |
142 | clks[IMX6SL_CLK_PLL1_SYS]); | 147 | clks[IMX6SL_CLK_PLL1_SYS]); |
143 | else if (audio_bus_freq_mode) | 148 | else if (audio_bus_freq_mode) { |
144 | /* Move ARM back to PLL2_PFD2 via STEP_CLK. */ | 149 | /* Move ARM back to PLL2_PFD2 via STEP_CLK. */ |
145 | clk_set_parent(clks[IMX6SL_CLK_PLL1_SW], | 150 | clk_set_parent(clks[IMX6SL_CLK_PLL1_SW], clks[IMX6SL_CLK_STEP]); |
146 | clks[IMX6SL_CLK_STEP]); | 151 | clk_set_rate(clks[IMX6SL_CLK_PLL1_SYS], pll1_org_rate); |
152 | } | ||
147 | parent_rate = clk_get_rate(clks[IMX6SL_CLK_PLL1_SW]); | 153 | parent_rate = clk_get_rate(clks[IMX6SL_CLK_PLL1_SW]); |
148 | clk_set_rate(clks[IMX6SL_CLK_ARM], parent_rate / cur_arm_podf); | 154 | clk_set_rate(clks[IMX6SL_CLK_ARM], parent_rate / cur_arm_podf); |
149 | } | 155 | } |
@@ -274,7 +280,7 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node) | |||
274 | clks[IMX6SL_CLK_EPDC_PIX_SEL] = imx_clk_mux_flags("epdc_pix_sel", base + 0x38, 15, 3, epdc_pix_sels, ARRAY_SIZE(epdc_pix_sels), CLK_SET_RATE_PARENT); | 280 | clks[IMX6SL_CLK_EPDC_PIX_SEL] = imx_clk_mux_flags("epdc_pix_sel", base + 0x38, 15, 3, epdc_pix_sels, ARRAY_SIZE(epdc_pix_sels), CLK_SET_RATE_PARENT); |
275 | clks[IMX6SL_CLK_SPDIF0_SEL] = imx_clk_mux("spdif0_sel", base + 0x30, 20, 2, audio_sels, ARRAY_SIZE(audio_sels)); | 281 | clks[IMX6SL_CLK_SPDIF0_SEL] = imx_clk_mux("spdif0_sel", base + 0x30, 20, 2, audio_sels, ARRAY_SIZE(audio_sels)); |
276 | clks[IMX6SL_CLK_SPDIF1_SEL] = imx_clk_mux("spdif1_sel", base + 0x30, 7, 2, audio_sels, ARRAY_SIZE(audio_sels)); | 282 | clks[IMX6SL_CLK_SPDIF1_SEL] = imx_clk_mux("spdif1_sel", base + 0x30, 7, 2, audio_sels, ARRAY_SIZE(audio_sels)); |
277 | clks[IMX6SL_CLK_EXTERN_AUDIO_SEL] = imx_clk_mux("extern_audio_sel", base + 0x20, 19, 2, audio_sels, ARRAY_SIZE(audio_sels)); | 283 | clks[IMX6SL_CLK_EXTERN_AUDIO_SEL] = imx_clk_mux_flags("extern_audio_sel", base + 0x20, 19, 2, audio_sels, ARRAY_SIZE(audio_sels), CLK_SET_RATE_PARENT); |
278 | clks[IMX6SL_CLK_ECSPI_SEL] = imx_clk_mux("ecspi_sel", base + 0x38, 18, 1, ecspi_sels, ARRAY_SIZE(ecspi_sels)); | 284 | clks[IMX6SL_CLK_ECSPI_SEL] = imx_clk_mux("ecspi_sel", base + 0x38, 18, 1, ecspi_sels, ARRAY_SIZE(ecspi_sels)); |
279 | clks[IMX6SL_CLK_UART_SEL] = imx_clk_mux("uart_sel", base + 0x24, 6, 1, uart_sels, ARRAY_SIZE(uart_sels)); | 285 | clks[IMX6SL_CLK_UART_SEL] = imx_clk_mux("uart_sel", base + 0x24, 6, 1, uart_sels, ARRAY_SIZE(uart_sels)); |
280 | 286 | ||
@@ -452,9 +458,16 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node) | |||
452 | /* Audio clocks */ | 458 | /* Audio clocks */ |
453 | clk_set_parent(clks[IMX6SL_CLK_SPDIF0_SEL], clks[IMX6SL_CLK_PLL3_PFD3]); | 459 | clk_set_parent(clks[IMX6SL_CLK_SPDIF0_SEL], clks[IMX6SL_CLK_PLL3_PFD3]); |
454 | 460 | ||
461 | /* set extern_audio to be sourced from PLL4/audio PLL */ | ||
462 | clk_set_parent(clks[IMX6SL_CLK_EXTERN_AUDIO_SEL], clks[IMX6SL_CLK_PLL4_AUDIO_DIV]); | ||
455 | /* set extern_audio to 24MHz */ | 463 | /* set extern_audio to 24MHz */ |
464 | clk_set_rate(clks[IMX6SL_CLK_PLL4_AUDIO], 24000000); | ||
456 | clk_set_rate(clks[IMX6SL_CLK_EXTERN_AUDIO], 24000000); | 465 | clk_set_rate(clks[IMX6SL_CLK_EXTERN_AUDIO], 24000000); |
457 | 466 | ||
467 | /* set SSI2 parent to PLL4 */ | ||
468 | clk_set_parent(clks[IMX6SL_CLK_SSI2_SEL], clks[IMX6SL_CLK_PLL4_AUDIO_DIV]); | ||
469 | clk_set_rate(clks[IMX6SL_CLK_SSI2], 24000000); | ||
470 | |||
458 | /* set perclk to source from OSC 24MHz */ | 471 | /* set perclk to source from OSC 24MHz */ |
459 | clk_set_parent(clks[IMX6SL_CLK_PERCLK_SEL], clks[IMX6SL_CLK_OSC]); | 472 | clk_set_parent(clks[IMX6SL_CLK_PERCLK_SEL], clks[IMX6SL_CLK_OSC]); |
460 | 473 | ||
diff --git a/arch/arm/mach-imx/clk-pllv3.c b/arch/arm/mach-imx/clk-pllv3.c index ab0e48f57829..f187052d639d 100644 --- a/arch/arm/mach-imx/clk-pllv3.c +++ b/arch/arm/mach-imx/clk-pllv3.c | |||
@@ -45,6 +45,7 @@ struct clk_pllv3 { | |||
45 | bool powerup_set; | 45 | bool powerup_set; |
46 | bool always_on; | 46 | bool always_on; |
47 | u32 div_mask; | 47 | u32 div_mask; |
48 | u32 rate_req; | ||
48 | }; | 49 | }; |
49 | 50 | ||
50 | #define to_clk_pllv3(_hw) container_of(_hw, struct clk_pllv3, hw) | 51 | #define to_clk_pllv3(_hw) container_of(_hw, struct clk_pllv3, hw) |
@@ -105,7 +106,8 @@ static int clk_pllv3_enable(struct clk_hw *hw) | |||
105 | struct clk_pllv3 *pll = to_clk_pllv3(hw); | 106 | struct clk_pllv3 *pll = to_clk_pllv3(hw); |
106 | u32 val; | 107 | u32 val; |
107 | 108 | ||
108 | clk_pllv3_power_up_down(hw, true); | 109 | if (pll->rate_req != BYPASS_RATE) |
110 | clk_pllv3_power_up_down(hw, true); | ||
109 | 111 | ||
110 | val = readl_relaxed(pll->base); | 112 | val = readl_relaxed(pll->base); |
111 | val |= BM_PLL_ENABLE; | 113 | val |= BM_PLL_ENABLE; |
@@ -124,7 +126,8 @@ static void clk_pllv3_disable(struct clk_hw *hw) | |||
124 | val &= ~BM_PLL_ENABLE; | 126 | val &= ~BM_PLL_ENABLE; |
125 | writel_relaxed(val, pll->base); | 127 | writel_relaxed(val, pll->base); |
126 | 128 | ||
127 | clk_pllv3_power_up_down(hw, false); | 129 | if (pll->rate_req != BYPASS_RATE) |
130 | clk_pllv3_power_up_down(hw, false); | ||
128 | } | 131 | } |
129 | 132 | ||
130 | static unsigned long clk_pllv3_recalc_rate(struct clk_hw *hw, | 133 | static unsigned long clk_pllv3_recalc_rate(struct clk_hw *hw, |
@@ -135,7 +138,7 @@ static unsigned long clk_pllv3_recalc_rate(struct clk_hw *hw, | |||
135 | u32 bypass = readl_relaxed(pll->base) & BYPASS_MASK; | 138 | u32 bypass = readl_relaxed(pll->base) & BYPASS_MASK; |
136 | u32 rate; | 139 | u32 rate; |
137 | 140 | ||
138 | if (bypass) | 141 | if (pll->rate_req == BYPASS_RATE && bypass) |
139 | rate = BYPASS_RATE; | 142 | rate = BYPASS_RATE; |
140 | else | 143 | else |
141 | rate = (div == 1) ? parent_rate * 22 : parent_rate * 20; | 144 | rate = (div == 1) ? parent_rate * 22 : parent_rate * 20; |
@@ -147,11 +150,9 @@ static long clk_pllv3_round_rate(struct clk_hw *hw, unsigned long rate, | |||
147 | unsigned long *prate) | 150 | unsigned long *prate) |
148 | { | 151 | { |
149 | unsigned long parent_rate = *prate; | 152 | unsigned long parent_rate = *prate; |
150 | struct clk_pllv3 *pll = to_clk_pllv3(hw); | ||
151 | u32 bypass = readl_relaxed(pll->base) & BYPASS_MASK; | ||
152 | 153 | ||
153 | /* If the PLL is bypassed, its rate is 24MHz. */ | 154 | /* If the PLL is bypassed, its rate is 24MHz. */ |
154 | if (bypass) | 155 | if (rate == BYPASS_RATE) |
155 | return BYPASS_RATE; | 156 | return BYPASS_RATE; |
156 | 157 | ||
157 | return (rate >= parent_rate * 22) ? parent_rate * 22 : | 158 | return (rate >= parent_rate * 22) ? parent_rate * 22 : |
@@ -163,12 +164,22 @@ static int clk_pllv3_set_rate(struct clk_hw *hw, unsigned long rate, | |||
163 | { | 164 | { |
164 | struct clk_pllv3 *pll = to_clk_pllv3(hw); | 165 | struct clk_pllv3 *pll = to_clk_pllv3(hw); |
165 | u32 val, div; | 166 | u32 val, div; |
166 | u32 bypass = readl_relaxed(pll->base) & BYPASS_MASK; | 167 | |
168 | pll->rate_req = rate; | ||
169 | val = readl_relaxed(pll->base); | ||
167 | 170 | ||
168 | /* If the PLL is bypassed, its rate is 24MHz. */ | 171 | /* If the PLL is bypassed, its rate is 24MHz. */ |
169 | if (bypass) | 172 | if (rate == BYPASS_RATE) { |
170 | return 0; | 173 | /* Set the bypass bit. */ |
174 | val |= BM_PLL_BYPASS; | ||
175 | if (pll->powerup_set) | ||
176 | val |= BM_PLL_POWER; | ||
177 | else | ||
178 | val &= ~BM_PLL_POWER; | ||
179 | writel_relaxed(val, pll->base); | ||
171 | 180 | ||
181 | return 0; | ||
182 | } | ||
172 | if (rate == parent_rate * 22) | 183 | if (rate == parent_rate * 22) |
173 | div = 1; | 184 | div = 1; |
174 | else if (rate == parent_rate * 20) | 185 | else if (rate == parent_rate * 20) |
@@ -197,6 +208,10 @@ static unsigned long clk_pllv3_sys_recalc_rate(struct clk_hw *hw, | |||
197 | { | 208 | { |
198 | struct clk_pllv3 *pll = to_clk_pllv3(hw); | 209 | struct clk_pllv3 *pll = to_clk_pllv3(hw); |
199 | u32 div = readl_relaxed(pll->base) & pll->div_mask; | 210 | u32 div = readl_relaxed(pll->base) & pll->div_mask; |
211 | u32 bypass = readl_relaxed(pll->base) & BYPASS_MASK; | ||
212 | |||
213 | if (pll->rate_req == BYPASS_RATE && bypass) | ||
214 | return BYPASS_RATE; | ||
200 | 215 | ||
201 | return parent_rate * div / 2; | 216 | return parent_rate * div / 2; |
202 | } | 217 | } |
@@ -209,6 +224,9 @@ static long clk_pllv3_sys_round_rate(struct clk_hw *hw, unsigned long rate, | |||
209 | unsigned long max_rate = parent_rate * 108 / 2; | 224 | unsigned long max_rate = parent_rate * 108 / 2; |
210 | u32 div; | 225 | u32 div; |
211 | 226 | ||
227 | if (rate == BYPASS_RATE) | ||
228 | return BYPASS_RATE; | ||
229 | |||
212 | if (rate > max_rate) | 230 | if (rate > max_rate) |
213 | rate = max_rate; | 231 | rate = max_rate; |
214 | else if (rate < min_rate) | 232 | else if (rate < min_rate) |
@@ -226,9 +244,26 @@ static int clk_pllv3_sys_set_rate(struct clk_hw *hw, unsigned long rate, | |||
226 | unsigned long max_rate = parent_rate * 108 / 2; | 244 | unsigned long max_rate = parent_rate * 108 / 2; |
227 | u32 val, div; | 245 | u32 val, div; |
228 | 246 | ||
229 | if (rate < min_rate || rate > max_rate) | 247 | if (rate != BYPASS_RATE && (rate < min_rate || rate > max_rate)) |
230 | return -EINVAL; | 248 | return -EINVAL; |
231 | 249 | ||
250 | pll->rate_req = rate; | ||
251 | val = readl_relaxed(pll->base); | ||
252 | |||
253 | if (rate == BYPASS_RATE) { | ||
254 | /* | ||
255 | * Set the PLL in bypass mode if rate requested is | ||
256 | * BYPASS_RATE. | ||
257 | */ | ||
258 | val |= BM_PLL_BYPASS; | ||
259 | /* Power down the PLL. */ | ||
260 | if (pll->powerup_set) | ||
261 | val &= ~BM_PLL_POWER; | ||
262 | else | ||
263 | val |= BM_PLL_POWER; | ||
264 | writel_relaxed(val, pll->base); | ||
265 | return 0; | ||
266 | } | ||
232 | div = rate * 2 / parent_rate; | 267 | div = rate * 2 / parent_rate; |
233 | val = readl_relaxed(pll->base); | 268 | val = readl_relaxed(pll->base); |
234 | val &= ~pll->div_mask; | 269 | val &= ~pll->div_mask; |
@@ -253,6 +288,10 @@ static unsigned long clk_pllv3_av_recalc_rate(struct clk_hw *hw, | |||
253 | u32 mfn = readl_relaxed(pll->base + PLL_NUM_OFFSET); | 288 | u32 mfn = readl_relaxed(pll->base + PLL_NUM_OFFSET); |
254 | u32 mfd = readl_relaxed(pll->base + PLL_DENOM_OFFSET); | 289 | u32 mfd = readl_relaxed(pll->base + PLL_DENOM_OFFSET); |
255 | u32 div = readl_relaxed(pll->base) & pll->div_mask; | 290 | u32 div = readl_relaxed(pll->base) & pll->div_mask; |
291 | u32 bypass = readl_relaxed(pll->base) & BYPASS_MASK; | ||
292 | |||
293 | if (pll->rate_req == BYPASS_RATE && bypass) | ||
294 | return BYPASS_RATE; | ||
256 | 295 | ||
257 | return (parent_rate * div) + ((parent_rate / mfd) * mfn); | 296 | return (parent_rate * div) + ((parent_rate / mfd) * mfn); |
258 | } | 297 | } |
@@ -267,6 +306,9 @@ static long clk_pllv3_av_round_rate(struct clk_hw *hw, unsigned long rate, | |||
267 | u32 mfn, mfd = 1000000; | 306 | u32 mfn, mfd = 1000000; |
268 | s64 temp64; | 307 | s64 temp64; |
269 | 308 | ||
309 | if (rate == BYPASS_RATE) | ||
310 | return BYPASS_RATE; | ||
311 | |||
270 | if (rate > max_rate) | 312 | if (rate > max_rate) |
271 | rate = max_rate; | 313 | rate = max_rate; |
272 | else if (rate < min_rate) | 314 | else if (rate < min_rate) |
@@ -291,9 +333,31 @@ static int clk_pllv3_av_set_rate(struct clk_hw *hw, unsigned long rate, | |||
291 | u32 mfn, mfd = 1000000; | 333 | u32 mfn, mfd = 1000000; |
292 | s64 temp64; | 334 | s64 temp64; |
293 | 335 | ||
294 | if (rate < min_rate || rate > max_rate) | 336 | if (rate != BYPASS_RATE && (rate < min_rate || rate > max_rate)) |
295 | return -EINVAL; | 337 | return -EINVAL; |
296 | 338 | ||
339 | pll->rate_req = rate; | ||
340 | val = readl_relaxed(pll->base); | ||
341 | |||
342 | if (rate == BYPASS_RATE) { | ||
343 | /* | ||
344 | * Set the PLL in bypass mode if rate requested is | ||
345 | * BYPASS_RATE. | ||
346 | */ | ||
347 | /* Bypass the PLL */ | ||
348 | val |= BM_PLL_BYPASS; | ||
349 | /* Power down the PLL. */ | ||
350 | if (pll->powerup_set) | ||
351 | val &= ~BM_PLL_POWER; | ||
352 | else | ||
353 | val |= BM_PLL_POWER; | ||
354 | writel_relaxed(val, pll->base); | ||
355 | return 0; | ||
356 | } | ||
357 | /* Else clear the bypass bit. */ | ||
358 | val &= ~BM_PLL_BYPASS; | ||
359 | writel_relaxed(val, pll->base); | ||
360 | |||
297 | div = rate / parent_rate; | 361 | div = rate / parent_rate; |
298 | temp64 = (u64) (rate - div * parent_rate); | 362 | temp64 = (u64) (rate - div * parent_rate); |
299 | temp64 *= mfd; | 363 | temp64 *= mfd; |