diff options
author | Ranjani Vaidyanathan <ra5478@freescale.com> | 2013-09-17 18:09:48 -0400 |
---|---|---|
committer | Nitin Garg <nitin.garg@freescale.com> | 2014-04-16 09:05:40 -0400 |
commit | 604e3e1076cd5c6c1802672198bb2a6a9e2608cb (patch) | |
tree | 6acc026de4f74c78c744a36c4e911e0c30079348 | |
parent | fa8c9f5067418ea20dca46a3f32b01e53b9878fd (diff) |
ENGR00280101-1 [iMX6SL] Add busfreq support
Add support to scale the DDR frequency between 400MHz and 24MHz.
Add support to scale AHB between 132MHz and 24MHz.
Signed-off-by: Ranjani Vaidyanathan <ra5478@freescale.com>
-rw-r--r-- | arch/arm/mach-imx/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/mach-imx/busfreq-imx6.c | 453 | ||||
-rw-r--r-- | arch/arm/mach-imx/busfreq_ddr3.c | 40 | ||||
-rw-r--r-- | arch/arm/mach-imx/busfreq_lpddr2.c | 172 | ||||
-rw-r--r-- | arch/arm/mach-imx/clk-imx6sl.c | 63 | ||||
-rw-r--r-- | arch/arm/mach-imx/lpddr2_freq_imx6.S | 483 | ||||
-rw-r--r-- | include/dt-bindings/clock/imx6sl-clock.h | 4 |
7 files changed, 1073 insertions, 143 deletions
diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile index 9b80b2328785..50887de73cde 100644 --- a/arch/arm/mach-imx/Makefile +++ b/arch/arm/mach-imx/Makefile | |||
@@ -108,6 +108,7 @@ obj-$(CONFIG_PM) += pm-imx6.o headsmp.o suspend-imx6.o | |||
108 | ifeq ($(CONFIG_ARM_IMX6_CPUFREQ),y) | 108 | ifeq ($(CONFIG_ARM_IMX6_CPUFREQ),y) |
109 | obj-y += busfreq-imx6.o | 109 | obj-y += busfreq-imx6.o |
110 | obj-$(CONFIG_SOC_IMX6Q) += ddr3_freq_imx6.o busfreq_ddr3.o | 110 | obj-$(CONFIG_SOC_IMX6Q) += ddr3_freq_imx6.o busfreq_ddr3.o |
111 | obj-$(CONFIG_SOC_IMX6SL) += lpddr2_freq_imx6.o busfreq_lpddr2.o | ||
111 | endif | 112 | endif |
112 | 113 | ||
113 | 114 | ||
diff --git a/arch/arm/mach-imx/busfreq-imx6.c b/arch/arm/mach-imx/busfreq-imx6.c index c7a7cce30315..53f4ebff52c4 100644 --- a/arch/arm/mach-imx/busfreq-imx6.c +++ b/arch/arm/mach-imx/busfreq-imx6.c | |||
@@ -41,33 +41,37 @@ | |||
41 | #include <linux/of.h> | 41 | #include <linux/of.h> |
42 | #include <linux/platform_device.h> | 42 | #include <linux/platform_device.h> |
43 | #include <linux/proc_fs.h> | 43 | #include <linux/proc_fs.h> |
44 | #include <linux/reboot.h> | ||
44 | #include <linux/regulator/consumer.h> | 45 | #include <linux/regulator/consumer.h> |
45 | #include <linux/sched.h> | 46 | #include <linux/sched.h> |
46 | #include <linux/suspend.h> | 47 | #include <linux/suspend.h> |
47 | #include "hardware.h" | 48 | #include "hardware.h" |
48 | 49 | ||
49 | #define LPAPM_CLK 24000000 | 50 | #define LPAPM_CLK 24000000 |
50 | #define DDR_AUDIO_CLK 50000000 | 51 | #define DDR_AUDIO_CLK 100000000 |
51 | 52 | ||
52 | int high_bus_freq_mode; | 53 | int high_bus_freq_mode; |
53 | int med_bus_freq_mode; | 54 | int med_bus_freq_mode; |
54 | int audio_bus_freq_mode; | 55 | int audio_bus_freq_mode; |
55 | int low_bus_freq_mode; | 56 | int low_bus_freq_mode; |
57 | unsigned int ddr_med_rate; | ||
58 | unsigned int ddr_normal_rate; | ||
56 | 59 | ||
57 | static int bus_freq_scaling_initialized; | 60 | static int bus_freq_scaling_initialized; |
58 | static struct device *busfreq_dev; | 61 | static struct device *busfreq_dev; |
59 | static int busfreq_suspended; | 62 | static int busfreq_suspended; |
60 | 63 | static u32 org_arm_rate; | |
61 | static int bus_freq_scaling_is_active; | 64 | static int bus_freq_scaling_is_active; |
62 | static int high_bus_count, med_bus_count, audio_bus_count; | 65 | static int high_bus_count, med_bus_count, audio_bus_count; |
63 | static unsigned int ddr_low_rate; | 66 | static unsigned int ddr_low_rate; |
64 | unsigned int ddr_med_rate; | ||
65 | unsigned int ddr_normal_rate; | ||
66 | 67 | ||
67 | extern int init_mmdc_settings(struct platform_device *dev); | 68 | extern int init_mmdc_lpddr2_settings(struct platform_device *dev); |
69 | extern int init_mmdc_ddr3_settings(struct platform_device *dev); | ||
68 | extern int update_ddr_freq(int ddr_rate); | 70 | extern int update_ddr_freq(int ddr_rate); |
71 | extern int update_lpddr2_freq(int ddr_rate); | ||
69 | 72 | ||
70 | DEFINE_MUTEX(bus_freq_mutex); | 73 | DEFINE_MUTEX(bus_freq_mutex); |
74 | static DEFINE_SPINLOCK(freq_lock); | ||
71 | 75 | ||
72 | static struct clk *pll2_400; | 76 | static struct clk *pll2_400; |
73 | static struct clk *periph_clk; | 77 | static struct clk *periph_clk; |
@@ -79,55 +83,181 @@ static struct clk *cpu_clk; | |||
79 | static struct clk *pll3; | 83 | static struct clk *pll3; |
80 | static struct clk *pll2; | 84 | static struct clk *pll2; |
81 | static struct clk *pll2_200; | 85 | static struct clk *pll2_200; |
82 | 86 | static struct clk *pll1_sys; | |
87 | static struct clk *periph2_clk; | ||
88 | static struct clk *ocram_clk; | ||
89 | static struct clk *ahb_clk; | ||
90 | static struct clk *pll1_sw_clk; | ||
91 | static struct clk *periph2_pre_clk; | ||
92 | static struct clk *periph2_clk2_sel; | ||
93 | static struct clk *periph2_clk2; | ||
94 | |||
95 | static u32 pll2_org_rate; | ||
83 | static struct delayed_work low_bus_freq_handler; | 96 | static struct delayed_work low_bus_freq_handler; |
84 | static struct delayed_work bus_freq_daemon; | 97 | static struct delayed_work bus_freq_daemon; |
85 | 98 | ||
86 | int low_bus_freq; | 99 | static void enter_lpm_imx6sl(void) |
87 | |||
88 | int reduce_bus_freq(void) | ||
89 | { | 100 | { |
90 | int ret = 0; | 101 | unsigned long flags; |
91 | clk_prepare_enable(pll3); | 102 | |
92 | if (low_bus_freq) { | 103 | if (high_bus_freq_mode) { |
93 | /* Need to ensure that PLL2_PFD_400M is kept ON. */ | 104 | pll2_org_rate = clk_get_rate(pll2); |
94 | clk_prepare_enable(pll2_400); | 105 | /* Set periph_clk to be sourced from OSC_CLK */ |
95 | update_ddr_freq(DDR_AUDIO_CLK); | 106 | clk_set_parent(periph_clk2_sel, osc_clk); |
96 | /* Make sure periph clk's parent also got updated */ | 107 | clk_set_parent(periph_clk, periph_clk2); |
97 | ret = clk_set_parent(periph_clk2_sel, pll3); | 108 | /* Ensure AHB/AXI clks are at 24MHz. */ |
98 | if (ret) | 109 | clk_set_rate(ahb_clk, LPAPM_CLK); |
99 | dev_WARN(busfreq_dev, "%s: %d: clk set parent fail!\n", | 110 | clk_set_rate(ocram_clk, LPAPM_CLK); |
100 | __func__, __LINE__); | 111 | } |
101 | ret = clk_set_parent(periph_pre_clk, pll2_200); | 112 | if (audio_bus_count) { |
102 | if (ret) | 113 | /* Set up DDR to 100MHz. */ |
103 | dev_WARN(busfreq_dev, "%s: %d: clk set parent fail!\n", | 114 | spin_lock_irqsave(&freq_lock, flags); |
104 | __func__, __LINE__); | 115 | update_lpddr2_freq(DDR_AUDIO_CLK); |
105 | ret = clk_set_parent(periph_clk, periph_pre_clk); | 116 | spin_unlock_irqrestore(&freq_lock, flags); |
106 | if (ret) | 117 | |
107 | dev_WARN(busfreq_dev, "%s: %d: clk set parent fail!\n", | 118 | /* Fix the clock tree in kernel */ |
108 | __func__, __LINE__); | 119 | clk_set_rate(pll2, pll2_org_rate); |
109 | audio_bus_freq_mode = 1; | 120 | clk_set_parent(periph2_pre_clk, pll2_200); |
121 | clk_set_parent(periph2_clk, periph2_pre_clk); | ||
122 | |||
123 | if (low_bus_freq_mode) { | ||
124 | /* | ||
125 | * Swtich ARM to run off PLL2_PFD2_400MHz | ||
126 | * since DDR is anyway at 100MHz. | ||
127 | */ | ||
128 | clk_set_parent(pll1_sw_clk, pll2_400); | ||
129 | /* | ||
130 | * Ensure that the clock will be | ||
131 | * at original speed. | ||
132 | */ | ||
133 | clk_set_rate(cpu_clk, org_arm_rate); | ||
134 | } | ||
110 | low_bus_freq_mode = 0; | 135 | low_bus_freq_mode = 0; |
136 | audio_bus_freq_mode = 1; | ||
111 | } else { | 137 | } else { |
112 | update_ddr_freq(LPAPM_CLK); | 138 | u32 arm_div, pll1_rate; |
113 | /* Make sure periph clk's parent also got updated */ | 139 | org_arm_rate = clk_get_rate(cpu_clk); |
114 | ret = clk_set_parent(periph_clk2_sel, osc_clk); | 140 | /* |
115 | if (ret) | 141 | * Set DDR to 24MHz. |
116 | dev_WARN(busfreq_dev, "%s: %d: clk set parent fail!\n", | 142 | * Since we are going to bypass PLL2, we need |
117 | __func__, __LINE__); | 143 | * to move ARM clk off PLL2_PFD2 to PLL1. |
118 | ret = clk_set_parent(periph_clk, periph_clk2); | 144 | * Make sure the PLL1 is running at the lowest possible freq. |
119 | if (ret) | 145 | */ |
120 | dev_WARN(busfreq_dev, "%s: %d: clk set parent fail!\n", | 146 | clk_set_rate(pll1_sys, clk_round_rate(pll1_sys, org_arm_rate)); |
121 | __func__, __LINE__); | 147 | pll1_rate = clk_get_rate(pll1_sys); |
122 | if (audio_bus_freq_mode) | 148 | arm_div = pll1_rate / org_arm_rate + 1; |
123 | clk_disable_unprepare(pll2_400); | 149 | /* Ensure ARM CLK is lower before changing the parent. */ |
150 | clk_set_rate(cpu_clk, org_arm_rate / arm_div); | ||
151 | /* Now set the ARM clk parent to PLL1_SYS. */ | ||
152 | clk_set_parent(pll1_sw_clk, pll1_sys); | ||
153 | |||
154 | /* Now set DDR to 24MHz. */ | ||
155 | spin_lock_irqsave(&freq_lock, flags); | ||
156 | update_lpddr2_freq(LPAPM_CLK); | ||
157 | spin_unlock_irqrestore(&freq_lock, flags); | ||
158 | |||
159 | /* | ||
160 | * Fix the clock tree in kernel. | ||
161 | * Make sure PLL2 rate is updated as it gets | ||
162 | * bypassed in the DDR freq change code. | ||
163 | */ | ||
164 | clk_set_rate(pll2, LPAPM_CLK); | ||
165 | clk_set_parent(periph2_clk2_sel, pll2); | ||
166 | clk_set_parent(periph2_clk, periph2_clk2_sel); | ||
167 | |||
124 | low_bus_freq_mode = 1; | 168 | low_bus_freq_mode = 1; |
125 | audio_bus_freq_mode = 0; | 169 | audio_bus_freq_mode = 0; |
126 | } | 170 | } |
127 | if (high_bus_freq_mode && cpu_is_imx6dl()) | 171 | } |
128 | clk_disable_unprepare(pll2_400); | 172 | |
173 | static void exit_lpm_imx6sl(void) | ||
174 | { | ||
175 | unsigned long flags; | ||
176 | spin_lock_irqsave(&freq_lock, flags); | ||
177 | /* Change DDR freq in IRAM. */ | ||
178 | update_lpddr2_freq(ddr_normal_rate); | ||
179 | spin_unlock_irqrestore(&freq_lock, flags); | ||
129 | 180 | ||
181 | /* | ||
182 | * Fix the clock tree in kernel. | ||
183 | * Make sure PLL2 rate is updated as it gets | ||
184 | * un-bypassed in the DDR freq change code. | ||
185 | */ | ||
186 | clk_set_rate(pll2, pll2_org_rate); | ||
187 | clk_set_parent(periph2_pre_clk, pll2_400); | ||
188 | clk_set_parent(periph2_clk, periph2_pre_clk); | ||
189 | |||
190 | /* Ensure that periph_clk is sourced from PLL2_400. */ | ||
191 | clk_set_parent(periph_pre_clk, pll2_400); | ||
192 | /* | ||
193 | * Before switching the perhiph_clk, ensure that the | ||
194 | * AHB/AXI will not be too fast. | ||
195 | */ | ||
196 | clk_set_rate(ahb_clk, LPAPM_CLK / 3); | ||
197 | clk_set_rate(ocram_clk, LPAPM_CLK / 2); | ||
198 | clk_set_parent(periph_clk, periph_pre_clk); | ||
199 | |||
200 | if (low_bus_freq_mode) { | ||
201 | /* Move ARM from PLL1_SW_CLK to PLL2_400. */ | ||
202 | clk_set_parent(pll1_sw_clk, pll2_400); | ||
203 | clk_set_rate(cpu_clk, org_arm_rate); | ||
204 | } | ||
205 | } | ||
206 | |||
207 | int reduce_bus_freq(void) | ||
208 | { | ||
209 | int ret = 0; | ||
210 | clk_prepare_enable(pll3); | ||
211 | if (cpu_is_imx6sl()) | ||
212 | enter_lpm_imx6sl(); | ||
213 | else { | ||
214 | if (audio_bus_count) { | ||
215 | /* Need to ensure that PLL2_PFD_400M is kept ON. */ | ||
216 | clk_prepare_enable(pll2_400); | ||
217 | update_ddr_freq(DDR_AUDIO_CLK); | ||
218 | /* Make sure periph clk's parent also got updated */ | ||
219 | ret = clk_set_parent(periph_clk2_sel, pll3); | ||
220 | if (ret) | ||
221 | dev_WARN(busfreq_dev, | ||
222 | "%s: %d: clk set parent fail!\n", | ||
223 | __func__, __LINE__); | ||
224 | ret = clk_set_parent(periph_pre_clk, pll2_200); | ||
225 | if (ret) | ||
226 | dev_WARN(busfreq_dev, | ||
227 | "%s: %d: clk set parent fail!\n", | ||
228 | __func__, __LINE__); | ||
229 | ret = clk_set_parent(periph_clk, periph_pre_clk); | ||
230 | if (ret) | ||
231 | dev_WARN(busfreq_dev, | ||
232 | "%s: %d: clk set parent fail!\n", | ||
233 | __func__, __LINE__); | ||
234 | audio_bus_freq_mode = 1; | ||
235 | low_bus_freq_mode = 0; | ||
236 | } else { | ||
237 | update_ddr_freq(LPAPM_CLK); | ||
238 | /* Make sure periph clk's parent also got updated */ | ||
239 | ret = clk_set_parent(periph_clk2_sel, osc_clk); | ||
240 | if (ret) | ||
241 | dev_WARN(busfreq_dev, | ||
242 | "%s: %d: clk set parent fail!\n", | ||
243 | __func__, __LINE__); | ||
244 | /* Set periph_clk parent to OSC via periph_clk2_sel */ | ||
245 | ret = clk_set_parent(periph_clk, periph_clk2); | ||
246 | if (ret) | ||
247 | dev_WARN(busfreq_dev, | ||
248 | "%s: %d: clk set parent fail!\n", | ||
249 | __func__, __LINE__); | ||
250 | if (audio_bus_freq_mode) | ||
251 | clk_disable_unprepare(pll2_400); | ||
252 | low_bus_freq_mode = 1; | ||
253 | audio_bus_freq_mode = 0; | ||
254 | } | ||
255 | if (high_bus_freq_mode && cpu_is_imx6dl()) | ||
256 | clk_disable_unprepare(pll2_400); | ||
257 | |||
258 | } | ||
130 | clk_disable_unprepare(pll3); | 259 | clk_disable_unprepare(pll3); |
260 | |||
131 | med_bus_freq_mode = 0; | 261 | med_bus_freq_mode = 0; |
132 | high_bus_freq_mode = 0; | 262 | high_bus_freq_mode = 0; |
133 | 263 | ||
@@ -153,10 +283,10 @@ static void reduce_bus_freq_handler(struct work_struct *work) | |||
153 | } | 283 | } |
154 | 284 | ||
155 | /* | 285 | /* |
156 | * Set the DDR, AHB to 24MHz. | 286 | * Set the DDR, AHB to 24MHz. |
157 | * This mode will be activated only when none of the modules that | 287 | * This mode will be activated only when none of the modules that |
158 | * need a higher DDR or AHB frequency are active. | 288 | * need a higher DDR or AHB frequency are active. |
159 | */ | 289 | */ |
160 | int set_low_bus_freq(int low_bus_mode) | 290 | int set_low_bus_freq(int low_bus_mode) |
161 | { | 291 | { |
162 | if (busfreq_suspended) | 292 | if (busfreq_suspended) |
@@ -166,23 +296,31 @@ int set_low_bus_freq(int low_bus_mode) | |||
166 | return 0; | 296 | return 0; |
167 | 297 | ||
168 | /* | 298 | /* |
169 | * Don't lower the frequency immediately. Instead | 299 | * Check to see if we need to got from |
170 | * scheduled a delayed work and drop the freq if | 300 | * low bus freq mode to audio bus freq mode. |
171 | * the conditions still remain the same. | 301 | * If so, the change needs to be done immediately. |
172 | */ | 302 | */ |
173 | low_bus_freq = low_bus_mode; | 303 | if (audio_bus_count && low_bus_freq_mode) |
174 | schedule_delayed_work(&low_bus_freq_handler, | 304 | reduce_bus_freq(); |
175 | usecs_to_jiffies(3000000)); | 305 | else |
306 | /* | ||
307 | * Don't lower the frequency immediately. Instead | ||
308 | * scheduled a delayed work and drop the freq if | ||
309 | * the conditions still remain the same. | ||
310 | */ | ||
311 | schedule_delayed_work(&low_bus_freq_handler, | ||
312 | usecs_to_jiffies(3000000)); | ||
176 | return 0; | 313 | return 0; |
177 | } | 314 | } |
178 | 315 | ||
179 | /* | 316 | /* |
180 | * Set the DDR to either 528MHz or 400MHz for iMX6qd | 317 | * Set the DDR to either 528MHz or 400MHz for iMX6qd |
181 | * or 400MHz for iMX6dl. | 318 | * or 400MHz for iMX6dl. |
182 | */ | 319 | */ |
183 | int set_high_bus_freq(int high_bus_freq) | 320 | int set_high_bus_freq(int high_bus_freq) |
184 | { | 321 | { |
185 | int ret = 0; | 322 | int ret = 0; |
323 | struct clk *periph_clk_parent; | ||
186 | 324 | ||
187 | if (bus_freq_scaling_initialized && bus_freq_scaling_is_active) | 325 | if (bus_freq_scaling_initialized && bus_freq_scaling_is_active) |
188 | cancel_delayed_work_sync(&low_bus_freq_handler); | 326 | cancel_delayed_work_sync(&low_bus_freq_handler); |
@@ -190,11 +328,10 @@ int set_high_bus_freq(int high_bus_freq) | |||
190 | if (busfreq_suspended) | 328 | if (busfreq_suspended) |
191 | return 0; | 329 | return 0; |
192 | 330 | ||
193 | /* for high setpoint, i.MX6Q is 528MHz, i.MX6DL is 400MHz */ | ||
194 | if (cpu_is_imx6q()) | 331 | if (cpu_is_imx6q()) |
195 | high_bus_freq = 1; | 332 | periph_clk_parent = pll2; |
196 | else | 333 | else |
197 | high_bus_freq = 0; | 334 | periph_clk_parent = pll2_400; |
198 | 335 | ||
199 | if (!bus_freq_scaling_initialized || !bus_freq_scaling_is_active) | 336 | if (!bus_freq_scaling_initialized || !bus_freq_scaling_is_active) |
200 | return 0; | 337 | return 0; |
@@ -207,42 +344,49 @@ int set_high_bus_freq(int high_bus_freq) | |||
207 | return 0; | 344 | return 0; |
208 | 345 | ||
209 | clk_prepare_enable(pll3); | 346 | clk_prepare_enable(pll3); |
210 | if (high_bus_freq) { | 347 | if (cpu_is_imx6sl()) |
211 | update_ddr_freq(ddr_normal_rate); | 348 | exit_lpm_imx6sl(); |
212 | /* Make sure periph clk's parent also got updated */ | 349 | else { |
213 | ret = clk_set_parent(periph_clk2_sel, pll3); | 350 | if (high_bus_freq) { |
214 | if (ret) | 351 | update_ddr_freq(ddr_normal_rate); |
215 | dev_WARN(busfreq_dev, "%s: %d: clk set parent fail!\n", | 352 | /* Make sure periph clk's parent also got updated */ |
216 | __func__, __LINE__); | 353 | ret = clk_set_parent(periph_clk2_sel, pll3); |
217 | ret = clk_set_parent(periph_pre_clk, pll2); | 354 | if (ret) |
218 | if (ret) | 355 | dev_WARN(busfreq_dev, |
219 | dev_WARN(busfreq_dev, "%s: %d: clk set parent fail!\n", | 356 | "%s: %d: clk set parent fail!\n", |
220 | __func__, __LINE__); | 357 | __func__, __LINE__); |
221 | ret = clk_set_parent(periph_clk, periph_pre_clk); | 358 | ret = clk_set_parent(periph_pre_clk, periph_clk_parent); |
222 | if (ret) | 359 | if (ret) |
223 | dev_WARN(busfreq_dev, "%s: %d: clk set parent fail!\n", | 360 | dev_WARN(busfreq_dev, |
224 | __func__, __LINE__); | 361 | "%s: %d: clk set parent fail!\n", |
225 | if (med_bus_freq_mode) | 362 | __func__, __LINE__); |
363 | ret = clk_set_parent(periph_clk, periph_pre_clk); | ||
364 | if (ret) | ||
365 | dev_WARN(busfreq_dev, | ||
366 | "%s: %d: clk set parent fail!\n", | ||
367 | __func__, __LINE__); | ||
368 | } else { | ||
369 | update_ddr_freq(ddr_med_rate); | ||
370 | /* Make sure periph clk's parent also got updated */ | ||
371 | ret = clk_set_parent(periph_clk2_sel, pll3); | ||
372 | if (ret) | ||
373 | dev_WARN(busfreq_dev, | ||
374 | "%s: %d: clk set parent fail!\n", | ||
375 | __func__, __LINE__); | ||
376 | ret = clk_set_parent(periph_pre_clk, pll2_400); | ||
377 | if (ret) | ||
378 | dev_WARN(busfreq_dev, | ||
379 | "%s: %d: clk set parent fail!\n", | ||
380 | __func__, __LINE__); | ||
381 | ret = clk_set_parent(periph_clk, periph_pre_clk); | ||
382 | if (ret) | ||
383 | dev_WARN(busfreq_dev, | ||
384 | "%s: %d: clk set parent fail!\n", | ||
385 | __func__, __LINE__); | ||
386 | } | ||
387 | if (audio_bus_freq_mode) | ||
226 | clk_disable_unprepare(pll2_400); | 388 | clk_disable_unprepare(pll2_400); |
227 | } else { | ||
228 | clk_prepare_enable(pll2_400); | ||
229 | update_ddr_freq(ddr_med_rate); | ||
230 | /* Make sure periph clk's parent also got updated */ | ||
231 | ret = clk_set_parent(periph_clk2_sel, pll3); | ||
232 | if (ret) | ||
233 | dev_WARN(busfreq_dev, "%s: %d: clk set parent fail!\n", | ||
234 | __func__, __LINE__); | ||
235 | ret = clk_set_parent(periph_pre_clk, pll2_400); | ||
236 | if (ret) | ||
237 | dev_WARN(busfreq_dev, "%s: %d: clk set parent fail!\n", | ||
238 | __func__, __LINE__); | ||
239 | ret = clk_set_parent(periph_clk, periph_pre_clk); | ||
240 | if (ret) | ||
241 | dev_WARN(busfreq_dev, "%s: %d: clk set parent fail!\n", | ||
242 | __func__, __LINE__); | ||
243 | } | 389 | } |
244 | if (audio_bus_freq_mode) | ||
245 | clk_disable_unprepare(pll2_400); | ||
246 | 390 | ||
247 | high_bus_freq_mode = 1; | 391 | high_bus_freq_mode = 1; |
248 | med_bus_freq_mode = 0; | 392 | med_bus_freq_mode = 0; |
@@ -281,6 +425,15 @@ void request_bus_freq(enum bus_freq_mode mode) | |||
281 | } | 425 | } |
282 | 426 | ||
283 | cancel_delayed_work_sync(&low_bus_freq_handler); | 427 | cancel_delayed_work_sync(&low_bus_freq_handler); |
428 | |||
429 | if (cpu_is_imx6dl()) { | ||
430 | /* No support for medium setpoint on MX6DL. */ | ||
431 | if (mode == BUS_FREQ_MED) { | ||
432 | high_bus_count++; | ||
433 | mode = BUS_FREQ_HIGH; | ||
434 | } | ||
435 | } | ||
436 | |||
284 | if ((mode == BUS_FREQ_HIGH) && (!high_bus_freq_mode)) { | 437 | if ((mode == BUS_FREQ_HIGH) && (!high_bus_freq_mode)) { |
285 | set_high_bus_freq(1); | 438 | set_high_bus_freq(1); |
286 | mutex_unlock(&bus_freq_mutex); | 439 | mutex_unlock(&bus_freq_mutex); |
@@ -340,6 +493,14 @@ void release_bus_freq(enum bus_freq_mode mode) | |||
340 | return; | 493 | return; |
341 | } | 494 | } |
342 | 495 | ||
496 | if (cpu_is_imx6dl()) { | ||
497 | /* No support for medium setpoint on MX6DL. */ | ||
498 | if (mode == BUS_FREQ_MED) { | ||
499 | high_bus_count--; | ||
500 | mode = BUS_FREQ_HIGH; | ||
501 | } | ||
502 | } | ||
503 | |||
343 | if ((!audio_bus_freq_mode) && (high_bus_count == 0) && | 504 | if ((!audio_bus_freq_mode) && (high_bus_count == 0) && |
344 | (med_bus_count == 0) && (audio_bus_count != 0)) { | 505 | (med_bus_count == 0) && (audio_bus_count != 0)) { |
345 | set_low_bus_freq(1); | 506 | set_low_bus_freq(1); |
@@ -417,10 +578,24 @@ static int bus_freq_pm_notify(struct notifier_block *nb, unsigned long event, | |||
417 | return NOTIFY_OK; | 578 | return NOTIFY_OK; |
418 | } | 579 | } |
419 | 580 | ||
581 | static int busfreq_reboot_notifier_event(struct notifier_block *this, | ||
582 | unsigned long event, void *ptr) | ||
583 | { | ||
584 | /* System is rebooting. Set the system into high_bus_freq_mode. */ | ||
585 | request_bus_freq(BUS_FREQ_HIGH); | ||
586 | |||
587 | return 0; | ||
588 | } | ||
589 | |||
420 | static struct notifier_block imx_bus_freq_pm_notifier = { | 590 | static struct notifier_block imx_bus_freq_pm_notifier = { |
421 | .notifier_call = bus_freq_pm_notify, | 591 | .notifier_call = bus_freq_pm_notify, |
422 | }; | 592 | }; |
423 | 593 | ||
594 | static struct notifier_block imx_busfreq_reboot_notifier = { | ||
595 | .notifier_call = busfreq_reboot_notifier_event, | ||
596 | }; | ||
597 | |||
598 | |||
424 | static DEVICE_ATTR(enable, 0644, bus_freq_scaling_enable_show, | 599 | static DEVICE_ATTR(enable, 0644, bus_freq_scaling_enable_show, |
425 | bus_freq_scaling_enable_store); | 600 | bus_freq_scaling_enable_store); |
426 | 601 | ||
@@ -442,73 +617,135 @@ static int busfreq_probe(struct platform_device *pdev) | |||
442 | pll2_400 = devm_clk_get(&pdev->dev, "pll2_pfd2_396m"); | 617 | pll2_400 = devm_clk_get(&pdev->dev, "pll2_pfd2_396m"); |
443 | if (IS_ERR(pll2_400)) { | 618 | if (IS_ERR(pll2_400)) { |
444 | dev_err(busfreq_dev, "%s: failed to get pll2_pfd2_396m\n", | 619 | dev_err(busfreq_dev, "%s: failed to get pll2_pfd2_396m\n", |
445 | __func__); | 620 | __func__); |
446 | return PTR_ERR(pll2_400); | 621 | return PTR_ERR(pll2_400); |
447 | } | 622 | } |
448 | 623 | ||
449 | pll2_200 = devm_clk_get(&pdev->dev, "pll2_198m"); | 624 | pll2_200 = devm_clk_get(&pdev->dev, "pll2_198m"); |
450 | if (IS_ERR(pll2_200)) { | 625 | if (IS_ERR(pll2_200)) { |
451 | dev_err(busfreq_dev, "%s: failed to get pll2_198m\n", | 626 | dev_err(busfreq_dev, "%s: failed to get pll2_198m\n", |
452 | __func__); | 627 | __func__); |
453 | return PTR_ERR(pll2_200); | 628 | return PTR_ERR(pll2_200); |
454 | } | 629 | } |
455 | 630 | ||
456 | pll2 = devm_clk_get(&pdev->dev, "pll2_bus"); | 631 | pll2 = devm_clk_get(&pdev->dev, "pll2_bus"); |
457 | if (IS_ERR(pll2)) { | 632 | if (IS_ERR(pll2)) { |
458 | dev_err(busfreq_dev, "%s: failed to get pll2_bus\n", | 633 | dev_err(busfreq_dev, "%s: failed to get pll2_bus\n", |
459 | __func__); | 634 | __func__); |
460 | return PTR_ERR(pll2); | 635 | return PTR_ERR(pll2); |
461 | } | 636 | } |
462 | 637 | ||
463 | cpu_clk = devm_clk_get(&pdev->dev, "arm"); | 638 | cpu_clk = devm_clk_get(&pdev->dev, "arm"); |
464 | if (IS_ERR(cpu_clk)) { | 639 | if (IS_ERR(cpu_clk)) { |
465 | dev_err(busfreq_dev, "%s: failed to get cpu_clk\n", | 640 | dev_err(busfreq_dev, "%s: failed to get cpu_clk\n", |
466 | __func__); | 641 | __func__); |
467 | return PTR_ERR(cpu_clk); | 642 | return PTR_ERR(cpu_clk); |
468 | } | 643 | } |
469 | 644 | ||
470 | pll3 = devm_clk_get(&pdev->dev, "pll3_usb_otg"); | 645 | pll3 = devm_clk_get(&pdev->dev, "pll3_usb_otg"); |
471 | if (IS_ERR(pll3)) { | 646 | if (IS_ERR(pll3)) { |
472 | dev_err(busfreq_dev, "%s: failed to get pll3_usb_otg\n", | 647 | dev_err(busfreq_dev, "%s: failed to get pll3_usb_otg\n", |
473 | __func__); | 648 | __func__); |
474 | return PTR_ERR(pll3); | 649 | return PTR_ERR(pll3); |
475 | } | 650 | } |
476 | 651 | ||
477 | periph_clk = devm_clk_get(&pdev->dev, "periph"); | 652 | periph_clk = devm_clk_get(&pdev->dev, "periph"); |
478 | if (IS_ERR(periph_clk)) { | 653 | if (IS_ERR(periph_clk)) { |
479 | dev_err(busfreq_dev, "%s: failed to get periph\n", | 654 | dev_err(busfreq_dev, "%s: failed to get periph\n", |
480 | __func__); | 655 | __func__); |
481 | return PTR_ERR(periph_clk); | 656 | return PTR_ERR(periph_clk); |
482 | } | 657 | } |
483 | 658 | ||
484 | periph_pre_clk = devm_clk_get(&pdev->dev, "periph_pre"); | 659 | periph_pre_clk = devm_clk_get(&pdev->dev, "periph_pre"); |
485 | if (IS_ERR(periph_pre_clk)) { | 660 | if (IS_ERR(periph_pre_clk)) { |
486 | dev_err(busfreq_dev, "%s: failed to get periph_pre\n", | 661 | dev_err(busfreq_dev, "%s: failed to get periph_pre\n", |
487 | __func__); | 662 | __func__); |
488 | return PTR_ERR(periph_pre_clk); | 663 | return PTR_ERR(periph_pre_clk); |
489 | } | 664 | } |
490 | 665 | ||
491 | periph_clk2 = devm_clk_get(&pdev->dev, "periph_clk2"); | 666 | periph_clk2 = devm_clk_get(&pdev->dev, "periph_clk2"); |
492 | if (IS_ERR(periph_clk2)) { | 667 | if (IS_ERR(periph_clk2)) { |
493 | dev_err(busfreq_dev, "%s: failed to get periph_clk2\n", | 668 | dev_err(busfreq_dev, "%s: failed to get periph_clk2\n", |
494 | __func__); | 669 | __func__); |
495 | return PTR_ERR(periph_clk2); | 670 | return PTR_ERR(periph_clk2); |
496 | } | 671 | } |
497 | 672 | ||
498 | periph_clk2_sel = devm_clk_get(&pdev->dev, "periph_clk2_sel"); | 673 | periph_clk2_sel = devm_clk_get(&pdev->dev, "periph_clk2_sel"); |
499 | if (IS_ERR(periph_clk2_sel)) { | 674 | if (IS_ERR(periph_clk2_sel)) { |
500 | dev_err(busfreq_dev, "%s: failed to get periph_clk2_sel\n", | 675 | dev_err(busfreq_dev, "%s: failed to get periph_clk2_sel\n", |
501 | __func__); | 676 | __func__); |
502 | return PTR_ERR(periph_clk2_sel); | 677 | return PTR_ERR(periph_clk2_sel); |
503 | } | 678 | } |
504 | 679 | ||
505 | osc_clk = devm_clk_get(&pdev->dev, "osc"); | 680 | osc_clk = devm_clk_get(&pdev->dev, "osc"); |
506 | if (IS_ERR(osc_clk)) { | 681 | if (IS_ERR(osc_clk)) { |
507 | dev_err(busfreq_dev, "%s: failed to get osc_clk\n", | 682 | dev_err(busfreq_dev, "%s: failed to get osc_clk\n", |
508 | __func__); | 683 | __func__); |
509 | return PTR_ERR(osc_clk); | 684 | return PTR_ERR(osc_clk); |
510 | } | 685 | } |
511 | 686 | ||
687 | if (cpu_is_imx6sl()) { | ||
688 | pll1_sys = devm_clk_get(&pdev->dev, "pll1_sys"); | ||
689 | if (IS_ERR(pll1_sys)) { | ||
690 | dev_err(busfreq_dev, "%s: failed to get pll1_sys\n", | ||
691 | __func__); | ||
692 | return PTR_ERR(pll1_sys); | ||
693 | } | ||
694 | |||
695 | ahb_clk = devm_clk_get(&pdev->dev, "ahb"); | ||
696 | if (IS_ERR(ahb_clk)) { | ||
697 | dev_err(busfreq_dev, "%s: failed to get ahb_clk\n", | ||
698 | __func__); | ||
699 | return PTR_ERR(ahb_clk); | ||
700 | } | ||
701 | |||
702 | ocram_clk = devm_clk_get(&pdev->dev, "ocram"); | ||
703 | if (IS_ERR(ocram_clk)) { | ||
704 | dev_err(busfreq_dev, "%s: failed to get ocram_clk\n", | ||
705 | __func__); | ||
706 | return PTR_ERR(ocram_clk); | ||
707 | } | ||
708 | |||
709 | pll1_sw_clk = devm_clk_get(&pdev->dev, "pll1_sw"); | ||
710 | if (IS_ERR(pll1_sw_clk)) { | ||
711 | dev_err(busfreq_dev, "%s: failed to get pll1_sw_clk\n", | ||
712 | __func__); | ||
713 | return PTR_ERR(pll1_sw_clk); | ||
714 | } | ||
715 | |||
716 | periph2_clk = devm_clk_get(&pdev->dev, "periph2"); | ||
717 | if (IS_ERR(periph2_clk)) { | ||
718 | dev_err(busfreq_dev, "%s: failed to get periph2\n", | ||
719 | __func__); | ||
720 | return PTR_ERR(periph2_clk); | ||
721 | } | ||
722 | |||
723 | periph2_pre_clk = devm_clk_get(&pdev->dev, "periph2_pre"); | ||
724 | if (IS_ERR(periph2_pre_clk)) { | ||
725 | dev_err(busfreq_dev, | ||
726 | "%s: failed to get periph2_pre_clk\n", | ||
727 | __func__); | ||
728 | return PTR_ERR(periph2_pre_clk); | ||
729 | } | ||
730 | |||
731 | periph2_clk2 = devm_clk_get(&pdev->dev, "periph2_clk2"); | ||
732 | if (IS_ERR(periph2_clk2)) { | ||
733 | dev_err(busfreq_dev, | ||
734 | "%s: failed to get periph2_clk2\n", | ||
735 | __func__); | ||
736 | return PTR_ERR(periph2_clk2); | ||
737 | } | ||
738 | |||
739 | periph2_clk2_sel = devm_clk_get(&pdev->dev, "periph2_clk2_sel"); | ||
740 | if (IS_ERR(periph2_clk2_sel)) { | ||
741 | dev_err(busfreq_dev, | ||
742 | "%s: failed to get periph2_clk2_sel\n", | ||
743 | __func__); | ||
744 | return PTR_ERR(periph2_clk2_sel); | ||
745 | } | ||
746 | |||
747 | } | ||
748 | |||
512 | err = sysfs_create_file(&busfreq_dev->kobj, &dev_attr_enable.attr); | 749 | err = sysfs_create_file(&busfreq_dev->kobj, &dev_attr_enable.attr); |
513 | if (err) { | 750 | if (err) { |
514 | dev_err(busfreq_dev, | 751 | dev_err(busfreq_dev, |
@@ -534,7 +771,7 @@ static int busfreq_probe(struct platform_device *pdev) | |||
534 | if (cpu_is_imx6q()) { | 771 | if (cpu_is_imx6q()) { |
535 | if (of_property_read_u32(pdev->dev.of_node, "fsl,med_ddr_freq", | 772 | if (of_property_read_u32(pdev->dev.of_node, "fsl,med_ddr_freq", |
536 | &ddr_med_rate)) { | 773 | &ddr_med_rate)) { |
537 | dev_err(busfreq_dev, | 774 | dev_WARN(busfreq_dev, |
538 | "DDR medium rate not supported.\n"); | 775 | "DDR medium rate not supported.\n"); |
539 | ddr_med_rate = ddr_normal_rate; | 776 | ddr_med_rate = ddr_normal_rate; |
540 | } | 777 | } |
@@ -543,8 +780,12 @@ static int busfreq_probe(struct platform_device *pdev) | |||
543 | INIT_DELAYED_WORK(&low_bus_freq_handler, reduce_bus_freq_handler); | 780 | INIT_DELAYED_WORK(&low_bus_freq_handler, reduce_bus_freq_handler); |
544 | INIT_DELAYED_WORK(&bus_freq_daemon, bus_freq_daemon_handler); | 781 | INIT_DELAYED_WORK(&bus_freq_daemon, bus_freq_daemon_handler); |
545 | register_pm_notifier(&imx_bus_freq_pm_notifier); | 782 | register_pm_notifier(&imx_bus_freq_pm_notifier); |
783 | register_reboot_notifier(&imx_busfreq_reboot_notifier); | ||
546 | 784 | ||
547 | err = init_mmdc_settings(pdev); | 785 | if (cpu_is_imx6sl()) |
786 | err = init_mmdc_lpddr2_settings(pdev); | ||
787 | else | ||
788 | err = init_mmdc_ddr3_settings(pdev); | ||
548 | if (err) { | 789 | if (err) { |
549 | dev_err(busfreq_dev, "Busfreq init of MMDC failed\n"); | 790 | dev_err(busfreq_dev, "Busfreq init of MMDC failed\n"); |
550 | return err; | 791 | return err; |
diff --git a/arch/arm/mach-imx/busfreq_ddr3.c b/arch/arm/mach-imx/busfreq_ddr3.c index 6ecd2f8a4407..6ac7d27fe86f 100644 --- a/arch/arm/mach-imx/busfreq_ddr3.c +++ b/arch/arm/mach-imx/busfreq_ddr3.c | |||
@@ -54,6 +54,13 @@ static void __iomem *l2_base; | |||
54 | static void __iomem *gic_dist_base; | 54 | static void __iomem *gic_dist_base; |
55 | static u32 *irqs_used; | 55 | static u32 *irqs_used; |
56 | 56 | ||
57 | static void *ddr_freq_change_iram_base; | ||
58 | static int ddr_settings_size; | ||
59 | static int iomux_settings_size; | ||
60 | static volatile unsigned int cpus_in_wfe; | ||
61 | static volatile bool wait_for_ddr_freq_update; | ||
62 | static int curr_ddr_rate; | ||
63 | |||
57 | void (*mx6_change_ddr_freq)(u32 freq, void *ddr_settings, | 64 | void (*mx6_change_ddr_freq)(u32 freq, void *ddr_settings, |
58 | bool dll_mode, void *iomux_offsets) = NULL; | 65 | bool dll_mode, void *iomux_offsets) = NULL; |
59 | 66 | ||
@@ -64,13 +71,6 @@ extern int audio_bus_freq_mode; | |||
64 | extern void mx6_ddr3_freq_change(u32 freq, void *ddr_settings, | 71 | extern void mx6_ddr3_freq_change(u32 freq, void *ddr_settings, |
65 | bool dll_mode, void *iomux_offsets); | 72 | bool dll_mode, void *iomux_offsets); |
66 | 73 | ||
67 | static void *ddr_freq_change_iram_base; | ||
68 | static int ddr_settings_size; | ||
69 | static int iomux_settings_size; | ||
70 | static volatile unsigned int cpus_in_wfe; | ||
71 | static volatile bool wait_for_ddr_freq_update; | ||
72 | static int curr_ddr_rate; | ||
73 | |||
74 | #define MIN_DLL_ON_FREQ 333000000 | 74 | #define MIN_DLL_ON_FREQ 333000000 |
75 | #define MAX_DLL_OFF_FREQ 125000000 | 75 | #define MAX_DLL_OFF_FREQ 125000000 |
76 | #define DDR_FREQ_CHANGE_SIZE 0x2000 | 76 | #define DDR_FREQ_CHANGE_SIZE 0x2000 |
@@ -250,7 +250,7 @@ int update_ddr_freq(int ddr_rate) | |||
250 | return 0; | 250 | return 0; |
251 | } | 251 | } |
252 | 252 | ||
253 | int init_mmdc_settings(struct platform_device *busfreq_pdev) | 253 | int init_mmdc_ddr3_settings(struct platform_device *busfreq_pdev) |
254 | { | 254 | { |
255 | struct device *dev = &busfreq_pdev->dev; | 255 | struct device *dev = &busfreq_pdev->dev; |
256 | struct platform_device *ocram_dev; | 256 | struct platform_device *ocram_dev; |
@@ -400,17 +400,17 @@ int init_mmdc_settings(struct platform_device *busfreq_pdev) | |||
400 | } | 400 | } |
401 | 401 | ||
402 | /* | 402 | /* |
403 | * Allocate extra space to store the number of entries in the | 403 | * Allocate extra space to store the number of entries in the |
404 | * ddr_settings plus 4 extra regsiter information that needs | 404 | * ddr_settings plus 4 extra regsiter information that needs |
405 | * to be passed to the frequency change code. | 405 | * to be passed to the frequency change code. |
406 | * sizeof(iram_ddr_settings) = sizeof(ddr_settings) + | 406 | * sizeof(iram_ddr_settings) = sizeof(ddr_settings) + |
407 | * entries in ddr_settings + 16. | 407 | * entries in ddr_settings + 16. |
408 | * The last 4 enties store the addresses of the registers: | 408 | * The last 4 enties store the addresses of the registers: |
409 | * CCM_BASE_ADDR | 409 | * CCM_BASE_ADDR |
410 | * MMDC_BASE_ADDR | 410 | * MMDC_BASE_ADDR |
411 | * IOMUX_BASE_ADDR | 411 | * IOMUX_BASE_ADDR |
412 | * L2X0_BASE_ADDR | 412 | * L2X0_BASE_ADDR |
413 | */ | 413 | */ |
414 | iram_addr = (void *)gen_pool_alloc(iram_pool, | 414 | iram_addr = (void *)gen_pool_alloc(iram_pool, |
415 | (ddr_settings_size * 8) + 8 + 32); | 415 | (ddr_settings_size * 8) + 8 + 32); |
416 | iram_ddr_settings = iram_addr; | 416 | iram_ddr_settings = iram_addr; |
@@ -456,7 +456,7 @@ int init_mmdc_settings(struct platform_device *busfreq_pdev) | |||
456 | iram_paddr = gen_pool_virt_to_phys(iram_pool, | 456 | iram_paddr = gen_pool_virt_to_phys(iram_pool, |
457 | (unsigned long)ddr_freq_change_iram_base); | 457 | (unsigned long)ddr_freq_change_iram_base); |
458 | /* | 458 | /* |
459 | * need to remap the area here since we want | 459 | * Need to remap the area here since we want |
460 | * the memory region to be executable. | 460 | * the memory region to be executable. |
461 | */ | 461 | */ |
462 | ddr_freq_change_iram_base = __arm_ioremap(iram_paddr, | 462 | ddr_freq_change_iram_base = __arm_ioremap(iram_paddr, |
diff --git a/arch/arm/mach-imx/busfreq_lpddr2.c b/arch/arm/mach-imx/busfreq_lpddr2.c new file mode 100644 index 000000000000..f15b8290669c --- /dev/null +++ b/arch/arm/mach-imx/busfreq_lpddr2.c | |||
@@ -0,0 +1,172 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved. | ||
3 | */ | ||
4 | |||
5 | /* | ||
6 | * The code contained herein is licensed under the GNU General Public | ||
7 | * License. You may obtain a copy of the GNU General Public License | ||
8 | * Version 2 or later at the following locations: | ||
9 | * | ||
10 | * http://www.opensource.org/licenses/gpl-license.html | ||
11 | * http://www.gnu.org/copyleft/gpl.html | ||
12 | */ | ||
13 | |||
14 | /*! | ||
15 | * @file busfreq_lpddr2.c | ||
16 | * | ||
17 | * @brief iMX6 LPDDR2 frequency change specific file. | ||
18 | * | ||
19 | * @ingroup PM | ||
20 | */ | ||
21 | #include <asm/cacheflush.h> | ||
22 | #include <asm/fncpy.h> | ||
23 | #include <asm/io.h> | ||
24 | #include <asm/mach/map.h> | ||
25 | #include <asm/mach-types.h> | ||
26 | #include <asm/tlb.h> | ||
27 | #include <linux/clk.h> | ||
28 | #include <linux/cpumask.h> | ||
29 | #include <linux/delay.h> | ||
30 | #include <linux/genalloc.h> | ||
31 | #include <linux/interrupt.h> | ||
32 | #include <linux/irqchip/arm-gic.h> | ||
33 | #include <linux/kernel.h> | ||
34 | #include <linux/mutex.h> | ||
35 | #include <linux/of.h> | ||
36 | #include <linux/of_address.h> | ||
37 | #include <linux/of_device.h> | ||
38 | #include <linux/platform_device.h> | ||
39 | #include <linux/proc_fs.h> | ||
40 | #include <linux/sched.h> | ||
41 | #include <linux/smp.h> | ||
42 | |||
43 | #include "hardware.h" | ||
44 | |||
45 | /* DDR settings */ | ||
46 | static void __iomem *mmdc_base; | ||
47 | static void __iomem *anatop_base; | ||
48 | static void __iomem *ccm_base; | ||
49 | static void __iomem *l2_base; | ||
50 | static struct device *busfreq_dev; | ||
51 | static void *ddr_freq_change_iram_base; | ||
52 | static int curr_ddr_rate; | ||
53 | static unsigned long reg_addrs[4]; | ||
54 | |||
55 | void (*mx6_change_lpddr2_freq)(u32 ddr_freq, int bus_freq_mode, | ||
56 | void *iram_addr) = NULL; | ||
57 | |||
58 | extern unsigned int ddr_normal_rate; | ||
59 | extern int low_bus_freq_mode; | ||
60 | extern int audio_bus_freq_mode; | ||
61 | extern void mx6_lpddr2_freq_change(u32 freq, int bus_freq_mode, | ||
62 | void *iram_addr); | ||
63 | |||
64 | |||
65 | #define LPDDR2_FREQ_CHANGE_SIZE 0x1000 | ||
66 | |||
67 | |||
68 | /* change the DDR frequency. */ | ||
69 | int update_lpddr2_freq(int ddr_rate) | ||
70 | { | ||
71 | if (ddr_rate == curr_ddr_rate) | ||
72 | return 0; | ||
73 | |||
74 | mx6_change_lpddr2_freq(ddr_rate, low_bus_freq_mode, reg_addrs); | ||
75 | |||
76 | curr_ddr_rate = ddr_rate; | ||
77 | |||
78 | dev_dbg(busfreq_dev, "\nBus freq set to %d start...\n", ddr_rate); | ||
79 | |||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | int init_mmdc_lpddr2_settings(struct platform_device *busfreq_pdev) | ||
84 | { | ||
85 | struct platform_device *ocram_dev; | ||
86 | unsigned int iram_paddr; | ||
87 | struct device_node *node; | ||
88 | struct gen_pool *iram_pool; | ||
89 | |||
90 | busfreq_dev = &busfreq_pdev->dev; | ||
91 | node = of_find_compatible_node(NULL, NULL, "fsl,imx6sl-mmdc"); | ||
92 | if (!node) { | ||
93 | printk(KERN_ERR "failed to find imx6sl-mmdc device tree data!\n"); | ||
94 | return -EINVAL; | ||
95 | } | ||
96 | mmdc_base = of_iomap(node, 0); | ||
97 | WARN(!mmdc_base, "unable to map mmdc registers\n"); | ||
98 | |||
99 | node = NULL; | ||
100 | node = of_find_compatible_node(NULL, NULL, "fsl,imx6sl-ccm"); | ||
101 | if (!node) { | ||
102 | printk(KERN_ERR "failed to find imx6sl-ccm device tree data!\n"); | ||
103 | return -EINVAL; | ||
104 | } | ||
105 | ccm_base = of_iomap(node, 0); | ||
106 | WARN(!ccm_base, "unable to map ccm registers\n"); | ||
107 | |||
108 | node = of_find_compatible_node(NULL, NULL, "arm,pl310-cache"); | ||
109 | if (!node) { | ||
110 | printk(KERN_ERR "failed to find imx6sl-pl310-cache device tree data!\n"); | ||
111 | return -EINVAL; | ||
112 | } | ||
113 | l2_base = of_iomap(node, 0); | ||
114 | WARN(!l2_base, "unable to map PL310 registers\n"); | ||
115 | |||
116 | node = of_find_compatible_node(NULL, NULL, "fsl,imx6sl-anatop"); | ||
117 | if (!node) { | ||
118 | printk(KERN_ERR "failed to find imx6sl-pl310-cache device tree data!\n"); | ||
119 | return -EINVAL; | ||
120 | } | ||
121 | anatop_base = of_iomap(node, 0); | ||
122 | WARN(!anatop_base, "unable to map anatop registers\n"); | ||
123 | |||
124 | node = NULL; | ||
125 | node = of_find_compatible_node(NULL, NULL, "mmio-sram"); | ||
126 | if (!node) { | ||
127 | dev_err(busfreq_dev, "%s: failed to find ocram node\n", | ||
128 | __func__); | ||
129 | return -EINVAL; | ||
130 | } | ||
131 | |||
132 | ocram_dev = of_find_device_by_node(node); | ||
133 | if (!ocram_dev) { | ||
134 | dev_err(busfreq_dev, "failed to find ocram device!\n"); | ||
135 | return -EINVAL; | ||
136 | } | ||
137 | |||
138 | iram_pool = dev_get_gen_pool(&ocram_dev->dev); | ||
139 | if (!iram_pool) { | ||
140 | dev_err(busfreq_dev, "iram pool unavailable!\n"); | ||
141 | return -EINVAL; | ||
142 | } | ||
143 | |||
144 | reg_addrs[0] = (unsigned long)anatop_base; | ||
145 | reg_addrs[1] = (unsigned long)ccm_base; | ||
146 | reg_addrs[2] = (unsigned long)mmdc_base; | ||
147 | reg_addrs[3] = (unsigned long)l2_base; | ||
148 | |||
149 | ddr_freq_change_iram_base = (void *)gen_pool_alloc(iram_pool, | ||
150 | LPDDR2_FREQ_CHANGE_SIZE); | ||
151 | if (!ddr_freq_change_iram_base) { | ||
152 | dev_err(busfreq_dev, | ||
153 | "Cannot alloc iram for ddr freq change code!\n"); | ||
154 | return -ENOMEM; | ||
155 | } | ||
156 | |||
157 | iram_paddr = gen_pool_virt_to_phys(iram_pool, | ||
158 | (unsigned long)ddr_freq_change_iram_base); | ||
159 | /* | ||
160 | * Need to remap the area here since we want | ||
161 | * the memory region to be executable. | ||
162 | */ | ||
163 | ddr_freq_change_iram_base = __arm_ioremap(iram_paddr, | ||
164 | LPDDR2_FREQ_CHANGE_SIZE, | ||
165 | MT_MEMORY_NONCACHED); | ||
166 | mx6_change_lpddr2_freq = (void *)fncpy(ddr_freq_change_iram_base, | ||
167 | &mx6_lpddr2_freq_change, LPDDR2_FREQ_CHANGE_SIZE); | ||
168 | |||
169 | curr_ddr_rate = ddr_normal_rate; | ||
170 | |||
171 | return 0; | ||
172 | } | ||
diff --git a/arch/arm/mach-imx/clk-imx6sl.c b/arch/arm/mach-imx/clk-imx6sl.c index 5a7e76af7698..660656c449bc 100644 --- a/arch/arm/mach-imx/clk-imx6sl.c +++ b/arch/arm/mach-imx/clk-imx6sl.c | |||
@@ -7,9 +7,13 @@ | |||
7 | * | 7 | * |
8 | */ | 8 | */ |
9 | 9 | ||
10 | #define CCM_CCDR_OFFSET 0x4 | ||
11 | #define CCDR_CH0_HS_BYP 17 | ||
12 | |||
10 | #include <linux/clk.h> | 13 | #include <linux/clk.h> |
11 | #include <linux/clkdev.h> | 14 | #include <linux/clkdev.h> |
12 | #include <linux/err.h> | 15 | #include <linux/err.h> |
16 | #include <linux/io.h> | ||
13 | #include <linux/of.h> | 17 | #include <linux/of.h> |
14 | #include <linux/of_address.h> | 18 | #include <linux/of_address.h> |
15 | #include <linux/of_irq.h> | 19 | #include <linux/of_irq.h> |
@@ -25,10 +29,11 @@ static const char const *ocram_sels[] = { "periph", "ocram_alt_sels", }; | |||
25 | static const char const *pre_periph_sels[] = { "pll2_bus", "pll2_pfd2", "pll2_pfd0", "pll2_198m", }; | 29 | static const char const *pre_periph_sels[] = { "pll2_bus", "pll2_pfd2", "pll2_pfd0", "pll2_198m", }; |
26 | static const char const *periph_clk2_sels[] = { "pll3_usb_otg", "osc", "osc", "dummy", }; | 30 | static const char const *periph_clk2_sels[] = { "pll3_usb_otg", "osc", "osc", "dummy", }; |
27 | static const char const *periph2_clk2_sels[] = { "pll3_usb_otg", "pll2_bus", }; | 31 | static const char const *periph2_clk2_sels[] = { "pll3_usb_otg", "pll2_bus", }; |
28 | static const char const *periph_sels[] = { "pre_periph_sel", "periph_clk2_podf", }; | 32 | static const char const *periph_sels[] = { "pre_periph_sel", "periph_clk2", }; |
29 | static const char const *periph2_sels[] = { "pre_periph2_sel", "periph2_clk2_podf", }; | 33 | static const char const *periph2_sels[] = { "pre_periph2_sel", "periph2_clk2", }; |
30 | static const char const *csi_sels[] = { "osc", "pll2_pfd2", "pll3_120m", "pll3_pfd1", }; | 34 | static const char const *csi_sels[] = { "osc", "pll2_pfd2", "pll3_120m", "pll3_pfd1", }; |
31 | static const char const *lcdif_axi_sels[] = { "pll2_bus", "pll2_pfd2", "pll3_usb_otg", "pll3_pfd1", }; | 35 | static const char const *lcdif_axi_sels[] = { "pll2_bus", "pll2_pfd2", "pll3_usb_otg", "pll3_pfd1", }; |
36 | static const char const *csi_lcdif_sels[] = { "mmdc", "pll2_pfd2", "pll3_120m", "pll3_pfd1", }; | ||
32 | static const char const *usdhc_sels[] = { "pll2_pfd2", "pll2_pfd0", }; | 37 | static const char const *usdhc_sels[] = { "pll2_pfd2", "pll2_pfd0", }; |
33 | static const char const *ssi_sels[] = { "pll3_pfd2", "pll3_pfd3", "pll4_audio_div", "dummy", }; | 38 | static const char const *ssi_sels[] = { "pll3_pfd2", "pll3_pfd3", "pll4_audio_div", "dummy", }; |
34 | static const char const *perclk_sels[] = { "ipg", "osc", }; | 39 | static const char const *perclk_sels[] = { "ipg", "osc", }; |
@@ -69,27 +74,49 @@ static struct clk *clks[IMX6SL_CLK_CLK_END]; | |||
69 | static struct clk_onecell_data clk_data; | 74 | static struct clk_onecell_data clk_data; |
70 | static u32 cur_arm_podf; | 75 | static u32 cur_arm_podf; |
71 | 76 | ||
77 | extern int low_bus_freq_mode; | ||
78 | |||
72 | /* | 79 | /* |
73 | * On MX6SL, need to ensure that the ARM:IPG clock ratio is maintained | 80 | * On MX6SL, need to ensure that the ARM:IPG clock ratio is maintained |
74 | * within 12:5 when the clocks to ARM are gated when the SOC enters | 81 | * within 12:5 when the clocks to ARM are gated when the SOC enters |
75 | * WAIT mode. This is necessary to avoid WAIT mode issue (an early | 82 | * WAIT mode. This is necessary to avoid WAIT mode issue (an early |
76 | * interrupt waking up the ARM). | 83 | * interrupt waking up the ARM). |
77 | * This function will set the ARM clk to max value within the 12:5 limit. | 84 | * This function will set the ARM clk to max value within the 12:5 limit. |
78 | */ | 85 | */ |
79 | void imx6sl_set_wait_clk(bool enter) | 86 | void imx6sl_set_wait_clk(bool enter) |
80 | { | 87 | { |
81 | u32 parent_rate = clk_get_rate(clk_get_parent(clks[IMX6SL_CLK_ARM])); | 88 | u32 parent_rate; |
82 | 89 | ||
83 | if (enter) { | 90 | if (enter) { |
91 | u32 wait_podf, new_parent_rate; | ||
84 | u32 ipg_rate = clk_get_rate(clks[IMX6SL_CLK_IPG]); | 92 | u32 ipg_rate = clk_get_rate(clks[IMX6SL_CLK_IPG]); |
85 | u32 max_arm_wait_clk = (12 * ipg_rate) / 5; | 93 | u32 max_arm_wait_clk = (12 * ipg_rate) / 5; |
86 | u32 wait_podf = (parent_rate + max_arm_wait_clk - 1) / | 94 | parent_rate = clk_get_rate(clks[IMX6SL_CLK_PLL1_SW]); |
95 | cur_arm_podf = parent_rate / clk_get_rate(clks[IMX6SL_CLK_ARM]); | ||
96 | if (low_bus_freq_mode) { | ||
97 | /* | ||
98 | * IPG clk is at 12MHz at this point, we can only run | ||
99 | * ARM at a max of 28.8MHz. So we need to set ARM | ||
100 | * to run from the 24MHz OSC, as there is no way to | ||
101 | * get 28.8MHz when ARM is sourced from PLL1. | ||
102 | */ | ||
103 | clk_set_parent(clks[IMX6SL_CLK_STEP], | ||
104 | clks[IMX6SL_CLK_OSC]); | ||
105 | clk_set_parent(clks[IMX6SL_CLK_PLL1_SW], | ||
106 | clks[IMX6SL_CLK_STEP]); | ||
107 | } | ||
108 | new_parent_rate = clk_get_rate(clks[IMX6SL_CLK_PLL1_SW]); | ||
109 | wait_podf = (new_parent_rate + max_arm_wait_clk - 1) / | ||
87 | max_arm_wait_clk; | 110 | max_arm_wait_clk; |
88 | 111 | ||
89 | cur_arm_podf = parent_rate / clk_get_rate(clks[IMX6SL_CLK_ARM]); | 112 | clk_set_rate(clks[IMX6SL_CLK_ARM], new_parent_rate / wait_podf); |
90 | clk_set_rate(clks[IMX6SL_CLK_ARM], parent_rate / wait_podf); | 113 | } else { |
91 | } else | 114 | if (low_bus_freq_mode) |
115 | /* Move ARM back to PLL1. */ | ||
116 | clk_set_parent(clks[IMX6SL_CLK_PLL1_SW], clks[IMX6SL_CLK_PLL1_SYS]); | ||
117 | parent_rate = clk_get_rate(clks[IMX6SL_CLK_PLL1_SW]); | ||
92 | clk_set_rate(clks[IMX6SL_CLK_ARM], parent_rate / cur_arm_podf); | 118 | clk_set_rate(clks[IMX6SL_CLK_ARM], parent_rate / cur_arm_podf); |
119 | } | ||
93 | } | 120 | } |
94 | 121 | ||
95 | static void __init imx6sl_clocks_init(struct device_node *ccm_node) | 122 | static void __init imx6sl_clocks_init(struct device_node *ccm_node) |
@@ -99,6 +126,7 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node) | |||
99 | int irq; | 126 | int irq; |
100 | int ret; | 127 | int ret; |
101 | int i; | 128 | int i; |
129 | u32 reg; | ||
102 | 130 | ||
103 | clks[IMX6SL_CLK_DUMMY] = imx_clk_fixed("dummy", 0); | 131 | clks[IMX6SL_CLK_DUMMY] = imx_clk_fixed("dummy", 0); |
104 | clks[IMX6SL_CLK_CKIL] = imx_obtain_fixed_clock("ckil", 0); | 132 | clks[IMX6SL_CLK_CKIL] = imx_obtain_fixed_clock("ckil", 0); |
@@ -193,8 +221,8 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node) | |||
193 | 221 | ||
194 | /* name parent_name reg shift width */ | 222 | /* name parent_name reg shift width */ |
195 | clks[IMX6SL_CLK_OCRAM_PODF] = imx_clk_divider("ocram_podf", "ocram_sel", base + 0x14, 16, 3); | 223 | clks[IMX6SL_CLK_OCRAM_PODF] = imx_clk_divider("ocram_podf", "ocram_sel", base + 0x14, 16, 3); |
196 | clks[IMX6SL_CLK_PERIPH_CLK2_PODF] = imx_clk_divider("periph_clk2_podf", "periph_clk2_sel", base + 0x14, 27, 3); | 224 | clks[IMX6SL_CLK_PERIPH_CLK2] = imx_clk_divider("periph_clk2", "periph_clk2_sel", base + 0x14, 27, 3); |
197 | clks[IMX6SL_CLK_PERIPH2_CLK2_PODF] = imx_clk_divider("periph2_clk2_podf", "periph2_clk2_sel", base + 0x14, 0, 3); | 225 | clks[IMX6SL_CLK_PERIPH2_CLK2] = imx_clk_divider("periph2_clk2", "periph2_clk2_sel", base + 0x14, 0, 3); |
198 | clks[IMX6SL_CLK_IPG] = imx_clk_divider("ipg", "ahb", base + 0x14, 8, 2); | 226 | clks[IMX6SL_CLK_IPG] = imx_clk_divider("ipg", "ahb", base + 0x14, 8, 2); |
199 | clks[IMX6SL_CLK_CSI_PODF] = imx_clk_divider("csi_podf", "csi_sel", base + 0x3c, 11, 3); | 227 | clks[IMX6SL_CLK_CSI_PODF] = imx_clk_divider("csi_podf", "csi_sel", base + 0x3c, 11, 3); |
200 | clks[IMX6SL_CLK_LCDIF_AXI_PODF] = imx_clk_divider("lcdif_axi_podf", "lcdif_axi_sel", base + 0x3c, 16, 3); | 228 | clks[IMX6SL_CLK_LCDIF_AXI_PODF] = imx_clk_divider("lcdif_axi_podf", "lcdif_axi_sel", base + 0x3c, 16, 3); |
@@ -332,6 +360,11 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node) | |||
332 | /* Set initial power mode */ | 360 | /* Set initial power mode */ |
333 | imx6_set_lpm(WAIT_CLOCKED); | 361 | imx6_set_lpm(WAIT_CLOCKED); |
334 | 362 | ||
363 | /* Ensure that CH0 handshake is bypassed. */ | ||
364 | reg = readl_relaxed(base + CCM_CCDR_OFFSET); | ||
365 | reg |= 1 << CCDR_CH0_HS_BYP; | ||
366 | writel_relaxed(reg, base + CCM_CCDR_OFFSET); | ||
367 | |||
335 | np = of_find_compatible_node(NULL, NULL, "fsl,imx6sl-gpt"); | 368 | np = of_find_compatible_node(NULL, NULL, "fsl,imx6sl-gpt"); |
336 | base = of_iomap(np, 0); | 369 | base = of_iomap(np, 0); |
337 | WARN_ON(!base); | 370 | WARN_ON(!base); |
diff --git a/arch/arm/mach-imx/lpddr2_freq_imx6.S b/arch/arm/mach-imx/lpddr2_freq_imx6.S new file mode 100644 index 000000000000..091bfae96478 --- /dev/null +++ b/arch/arm/mach-imx/lpddr2_freq_imx6.S | |||
@@ -0,0 +1,483 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012-2013 Freescale Semiconductor, Inc. All Rights Reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | |||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | |||
14 | * You should have received a copy of the GNU General Public License along | ||
15 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
17 | */ | ||
18 | |||
19 | #include <linux/linkage.h> | ||
20 | |||
21 | .macro mx6sl_switch_to_24MHz | ||
22 | |||
23 | /* | ||
24 | * Set MMDC clock to be sourced from PLL3. | ||
25 | * Ensure first periph2_clk2 is sourced from PLL3. | ||
26 | * Set the PERIPH2_CLK2_PODF to divide by 2. | ||
27 | */ | ||
28 | ldr r6, [r2, #0x14] | ||
29 | bic r6, r6, #0x7 | ||
30 | orr r6, r6, #0x1 | ||
31 | str r6, [r2, #0x14] | ||
32 | |||
33 | /* Select PLL3 to source MMDC. */ | ||
34 | ldr r6, [r2, #0x18] | ||
35 | bic r6, r6, #0x100000 | ||
36 | str r6, [r2, #0x18] | ||
37 | |||
38 | /* Swtich periph2_clk_sel to run from PLL3. */ | ||
39 | ldr r6, [r2, #0x14] | ||
40 | orr r6, r6, #0x4000000 | ||
41 | str r6, [r2, #0x14] | ||
42 | |||
43 | periph2_clk_switch1: | ||
44 | ldr r6, [r2, #0x48] | ||
45 | cmp r6, #0 | ||
46 | bne periph2_clk_switch1 | ||
47 | |||
48 | /* | ||
49 | * Need to clock gate the 528 PFDs before | ||
50 | * powering down PLL2. | ||
51 | * Only the PLL2_PFD2_400M should be ON | ||
52 | * at this time, so only clock gate that one. | ||
53 | */ | ||
54 | ldr r6, [r3, #0x100] | ||
55 | orr r6, r6, #0x800000 | ||
56 | str r6, [r3, #0x100] | ||
57 | |||
58 | /* | ||
59 | * Set PLL2 to bypass state. We should be here | ||
60 | * only if MMDC is not sourced from PLL2. | ||
61 | */ | ||
62 | ldr r6, [r3, #0x30] | ||
63 | orr r6, r6, #0x10000 | ||
64 | str r6, [r3, #0x30] | ||
65 | |||
66 | ldr r6, [r3, #0x30] | ||
67 | orr r6, r6, #0x1000 | ||
68 | str r6, [r3, #0x30] | ||
69 | |||
70 | /* Ensure pre_periph2_clk_mux is set to pll2 */ | ||
71 | ldr r6, [r2, #0x18] | ||
72 | bic r6, r6, #0x600000 | ||
73 | str r6, [r2, #0x18] | ||
74 | |||
75 | /* Set MMDC clock to be sourced from the bypassed PLL2. */ | ||
76 | ldr r6, [r2, #0x14] | ||
77 | bic r6, r6, #0x4000000 | ||
78 | str r6, [r2, #0x14] | ||
79 | |||
80 | periph2_clk_switch2: | ||
81 | ldr r6, [r2, #0x48] | ||
82 | cmp r6, #0 | ||
83 | bne periph2_clk_switch2 | ||
84 | |||
85 | /* | ||
86 | * Now move MMDC back to periph2_clk2 source. | ||
87 | * after selecting PLL2 as the option. | ||
88 | * Select PLL2 as the source. | ||
89 | */ | ||
90 | ldr r6, [r2, #0x18] | ||
91 | orr r6, r6, #0x100000 | ||
92 | str r6, [r2, #0x18] | ||
93 | |||
94 | /* set periph2_clk2_podf to divide by 1. */ | ||
95 | ldr r6, [r2, #0x14] | ||
96 | bic r6, r6, #0x7 | ||
97 | str r6, [r2, #0x14] | ||
98 | |||
99 | /* Now move periph2_clk to periph2_clk2 source */ | ||
100 | ldr r6, [r2, #0x14] | ||
101 | orr r6, r6, #0x4000000 | ||
102 | str r6, [r2, #0x14] | ||
103 | |||
104 | periph2_clk_switch3: | ||
105 | ldr r6, [r2, #0x48] | ||
106 | cmp r6, #0 | ||
107 | bne periph2_clk_switch3 | ||
108 | |||
109 | /* Now set the MMDC PODF back to 1.*/ | ||
110 | ldr r6, [r2, #0x14] | ||
111 | bic r6, r6, #0x38 | ||
112 | str r6, [r2, #0x14] | ||
113 | |||
114 | mmdc_podf0: | ||
115 | ldr r6, [r2, #0x48] | ||
116 | cmp r6, #0 | ||
117 | bne mmdc_podf0 | ||
118 | |||
119 | .endm | ||
120 | |||
121 | .macro ddr_switch_400MHz | ||
122 | |||
123 | /* Set MMDC divider first, in case PLL3 is at 480MHz. */ | ||
124 | ldr r6, [r3, #0x10] | ||
125 | and r6, r6, #0x10000 | ||
126 | cmp r6, #0x10000 | ||
127 | beq pll3_in_bypass | ||
128 | |||
129 | /* Set MMDC divder to divide by 2. */ | ||
130 | ldr r6, [r2, #0x14] | ||
131 | bic r6, r6, #0x38 | ||
132 | orr r6, r6, #0x8 | ||
133 | str r6, [r2, #0x14] | ||
134 | |||
135 | mmdc_podf: | ||
136 | ldr r6, [r2, #0x48] | ||
137 | cmp r6, #0 | ||
138 | bne mmdc_podf | ||
139 | |||
140 | pll3_in_bypass: | ||
141 | /* | ||
142 | * Check if we are switching between | ||
143 | * 400Mhz <-> 100MHz.If so, we should | ||
144 | * try to source MMDC from PLL2_200M. | ||
145 | */ | ||
146 | cmp r1, #0 | ||
147 | beq not_low_bus_freq | ||
148 | |||
149 | /* Ensure that MMDC is sourced from PLL2 mux first. */ | ||
150 | ldr r6, [r2, #0x14] | ||
151 | bic r6, r6, #0x4000000 | ||
152 | str r6, [r2, #0x14] | ||
153 | |||
154 | periph2_clk_switch4: | ||
155 | ldr r6, [r2, #0x48] | ||
156 | cmp r6, #0 | ||
157 | bne periph2_clk_switch4 | ||
158 | |||
159 | not_low_bus_freq: | ||
160 | /* Now ensure periph2_clk2_sel mux is set to PLL3 */ | ||
161 | ldr r6, [r2, #0x18] | ||
162 | bic r6, r6, #0x100000 | ||
163 | str r6, [r2, #0x18] | ||
164 | |||
165 | /* Now switch MMDC to PLL3. */ | ||
166 | ldr r6, [r2, #0x14] | ||
167 | orr r6, r6, #0x4000000 | ||
168 | str r6, [r2, #0x14] | ||
169 | |||
170 | periph2_clk_switch5: | ||
171 | ldr r6, [r2, #0x48] | ||
172 | cmp r6, #0 | ||
173 | bne periph2_clk_switch5 | ||
174 | |||
175 | /* | ||
176 | * Check if PLL2 is already unlocked. | ||
177 | * If so do nothing with PLL2. | ||
178 | */ | ||
179 | cmp r1, #0 | ||
180 | beq pll2_already_on | ||
181 | |||
182 | /* Now power up PLL2 and unbypass it. */ | ||
183 | ldr r6, [r3, #0x30] | ||
184 | bic r6, r6, #0x1000 | ||
185 | str r6, [r3, #0x30] | ||
186 | |||
187 | /* Make sure PLL2 has locked.*/ | ||
188 | wait_for_pll_lock: | ||
189 | ldr r6, [r3, #0x30] | ||
190 | and r6, r6, #0x80000000 | ||
191 | cmp r6, #0x80000000 | ||
192 | bne wait_for_pll_lock | ||
193 | |||
194 | ldr r6, [r3, #0x30] | ||
195 | bic r6, r6, #0x10000 | ||
196 | str r6, [r3, #0x30] | ||
197 | |||
198 | /* | ||
199 | * Need to enable the 528 PFDs after | ||
200 | * powering up PLL2. | ||
201 | * Only the PLL2_PFD2_400M should be ON | ||
202 | * as it feeds the MMDC. Rest should have | ||
203 | * been managed by clock code. | ||
204 | */ | ||
205 | ldr r6, [r3, #0x100] | ||
206 | bic r6, r6, #0x800000 | ||
207 | str r6, [r3, #0x100] | ||
208 | |||
209 | pll2_already_on: | ||
210 | /* | ||
211 | * Now switch MMDC clk back to pll2_mux option. | ||
212 | * Ensure pre_periph2_clk2 is set to pll2_pfd_400M. | ||
213 | * If switching to audio DDR freq, set the | ||
214 | * pre_periph2_clk2 to PLL2_PFD_200M | ||
215 | */ | ||
216 | ldr r6, =400000000 | ||
217 | cmp r6, r0 | ||
218 | bne use_pll2_pfd_200M | ||
219 | |||
220 | ldr r6, [r2, #0x18] | ||
221 | bic r6, r6, #0x600000 | ||
222 | orr r6, r6, #0x200000 | ||
223 | str r6, [r2, #0x18] | ||
224 | ldr r6, =400000000 | ||
225 | b cont2 | ||
226 | |||
227 | use_pll2_pfd_200M: | ||
228 | ldr r6, [r2, #0x18] | ||
229 | orr r6, r6, #0x600000 | ||
230 | str r6, [r2, #0x18] | ||
231 | ldr r6, =200000000 | ||
232 | |||
233 | cont2: | ||
234 | ldr r4, [r2, #0x14] | ||
235 | bic r4, r4, #0x4000000 | ||
236 | str r4, [r2, #0x14] | ||
237 | |||
238 | periph2_clk_switch6: | ||
239 | ldr r4, [r2, #0x48] | ||
240 | cmp r4, #0 | ||
241 | bne periph2_clk_switch6 | ||
242 | |||
243 | change_divider_only: | ||
244 | /* | ||
245 | * Calculate the MMDC divider | ||
246 | * based on the requested freq. | ||
247 | */ | ||
248 | ldr r4, =0 | ||
249 | Loop2: | ||
250 | sub r6, r6, r0 | ||
251 | cmp r6, r0 | ||
252 | blt Div_Found | ||
253 | add r4, r4, #1 | ||
254 | bgt Loop2 | ||
255 | |||
256 | /* Shift divider into correct offset. */ | ||
257 | lsl r4, r4, #3 | ||
258 | Div_Found: | ||
259 | /* Set the MMDC PODF. */ | ||
260 | ldr r6, [r2, #0x14] | ||
261 | bic r6, r6, #0x38 | ||
262 | orr r6, r6, r4 | ||
263 | str r6, [r2, #0x14] | ||
264 | |||
265 | mmdc_podf1: | ||
266 | ldr r6, [r2, #0x48] | ||
267 | cmp r6, #0 | ||
268 | bne mmdc_podf1 | ||
269 | |||
270 | .endm | ||
271 | |||
272 | .macro mmdc_clk_lower_100MHz | ||
273 | |||
274 | /* | ||
275 | * Prior to reducing the DDR frequency (at 528/400 MHz), | ||
276 | * read the Measure unit count bits (MU_UNIT_DEL_NUM) | ||
277 | */ | ||
278 | ldr r5, =0x8B8 | ||
279 | ldr r6, [r8, r5] | ||
280 | /* Original MU unit count */ | ||
281 | mov r6, r6, LSR #16 | ||
282 | ldr r4, =0x3FF | ||
283 | and r6, r6, r4 | ||
284 | /* Original MU unit count * 2 */ | ||
285 | mov r7, r6, LSL #1 | ||
286 | /* | ||
287 | * Bypass the automatic measure unit when below 100 MHz | ||
288 | * by setting the Measure unit bypass enable bit (MU_BYP_EN) | ||
289 | */ | ||
290 | ldr r6, [r8, r5] | ||
291 | orr r6, r6, #0x400 | ||
292 | str r6, [r8, r5] | ||
293 | /* | ||
294 | * Double the measure count value read in step 1 and program it in the | ||
295 | * measurement bypass bits (MU_BYP_VAL) of the MMDC PHY Measure Unit | ||
296 | * Register for the reduced frequency operation below 100 MHz | ||
297 | */ | ||
298 | ldr r6, [r8, r5] | ||
299 | ldr r4, =0x3FF | ||
300 | bic r6, r6, r4 | ||
301 | orr r6, r6, r7 | ||
302 | str r6, [r8, r5] | ||
303 | /* Now perform a Force Measurement. */ | ||
304 | ldr r6, [r8, r5] | ||
305 | orr r6, r6, #0x800 | ||
306 | str r6, [r8, r5] | ||
307 | /* Wait for FRC_MSR to clear. */ | ||
308 | force_measure: | ||
309 | ldr r6, [r8, r5] | ||
310 | and r6, r6, #0x800 | ||
311 | cmp r6, #0x0 | ||
312 | bne force_measure | ||
313 | |||
314 | .endm | ||
315 | |||
316 | .macro mmdc_clk_above_100MHz | ||
317 | |||
318 | /* Make sure that the PHY measurement unit is NOT in bypass mode */ | ||
319 | ldr r5, =0x8B8 | ||
320 | ldr r6, [r8, r5] | ||
321 | bic r6, r6, #0x400 | ||
322 | str r6, [r8, r5] | ||
323 | /* Now perform a Force Measurement. */ | ||
324 | ldr r6, [r8, r5] | ||
325 | orr r6, r6, #0x800 | ||
326 | str r6, [r8, r5] | ||
327 | /* Wait for FRC_MSR to clear. */ | ||
328 | force_measure1: | ||
329 | ldr r6, [r8, r5] | ||
330 | and r6, r6, #0x800 | ||
331 | cmp r6, #0x0 | ||
332 | bne force_measure1 | ||
333 | .endm | ||
334 | |||
335 | /* | ||
336 | * mx6_lpddr2_freq_change | ||
337 | * | ||
338 | * Make sure DDR is in self-refresh. | ||
339 | * IRQs are already disabled. | ||
340 | * r0 : DDR freq. | ||
341 | * r1: low_bus_freq_mode flag | ||
342 | * r2: Pointer to array containing addresses of registers. | ||
343 | */ | ||
344 | ENTRY(mx6_lpddr2_freq_change) | ||
345 | |||
346 | push {r4-r10} | ||
347 | |||
348 | mov r4, r2 | ||
349 | ldr r3, [r4] @ANATOP_BASE_ADDR | ||
350 | ldr r2, [r4, #0x4] @CCM_BASE_ADDR | ||
351 | ldr r8, [r4, #0x8] @MMDC_P0_BASE_ADDR | ||
352 | ldr r7, [r4, #0x8] @L2_BASE_ADDR | ||
353 | |||
354 | lpddr2_freq_change: | ||
355 | adr r9, lpddr2_freq_change | ||
356 | |||
357 | /* Prime all TLB entries. */ | ||
358 | ldr r6, [r9] | ||
359 | ldr r6, [r8] | ||
360 | ldr r6, [r3] | ||
361 | ldr r6, [r2] | ||
362 | |||
363 | /* Drain all the L1 buffers. */ | ||
364 | dsb | ||
365 | |||
366 | #ifdef CONFIG_CACHE_L2X0 | ||
367 | /* | ||
368 | * Need to make sure the buffers in L2 are drained. | ||
369 | * Performing a sync operation does this. | ||
370 | */ | ||
371 | mov r6, #0x0 | ||
372 | str r6, [r7, #0x730] | ||
373 | #endif | ||
374 | |||
375 | /* | ||
376 | * The second dsb might be needed to keep cache sync (device write) | ||
377 | * ordering with the memory accesses before it. | ||
378 | */ | ||
379 | dsb | ||
380 | isb | ||
381 | |||
382 | /* Disable Automatic power savings. */ | ||
383 | ldr r6, [r8, #0x404] | ||
384 | orr r6, r6, #0x01 | ||
385 | str r6, [r8, #0x404] | ||
386 | |||
387 | /* MMDC0_MDPDC disable power down timer */ | ||
388 | ldr r6, [r8, #0x4] | ||
389 | bic r6, r6, #0xff00 | ||
390 | str r6, [r8, #0x4] | ||
391 | |||
392 | /* Delay for a while */ | ||
393 | ldr r10, =10 | ||
394 | delay1: | ||
395 | ldr r7, =0 | ||
396 | cont1: | ||
397 | ldr r6, [r8, r7] | ||
398 | add r7, r7, #4 | ||
399 | cmp r7, #16 | ||
400 | bne cont1 | ||
401 | sub r10, r10, #1 | ||
402 | cmp r10, #0 | ||
403 | bgt delay1 | ||
404 | |||
405 | /* Make the DDR explicitly enter self-refresh. */ | ||
406 | ldr r6, [r8, #0x404] | ||
407 | orr r6, r6, #0x200000 | ||
408 | str r6, [r8, #0x404] | ||
409 | |||
410 | poll_dvfs_set_1: | ||
411 | ldr r6, [r8, #0x404] | ||
412 | and r6, r6, #0x2000000 | ||
413 | cmp r6, #0x2000000 | ||
414 | bne poll_dvfs_set_1 | ||
415 | |||
416 | /* set SBS step-by-step mode */ | ||
417 | ldr r6, [r8, #0x410] | ||
418 | orr r6, r6, #0x100 | ||
419 | str r6, [r8, #0x410] | ||
420 | |||
421 | ldr r10, =100000000 | ||
422 | cmp r0, r10 | ||
423 | bgt set_ddr_mu_above_100 | ||
424 | mmdc_clk_lower_100MHz | ||
425 | |||
426 | set_ddr_mu_above_100: | ||
427 | ldr r10, =24000000 | ||
428 | cmp r0, r10 | ||
429 | beq set_to_24MHz | ||
430 | |||
431 | ddr_switch_400MHz | ||
432 | |||
433 | ldr r10,=100000000 | ||
434 | cmp r0, r10 | ||
435 | blt done | ||
436 | mmdc_clk_above_100MHz | ||
437 | |||
438 | b done | ||
439 | |||
440 | set_to_24MHz: | ||
441 | mx6sl_switch_to_24MHz | ||
442 | |||
443 | done: | ||
444 | /* clear DVFS - exit from self refresh mode */ | ||
445 | ldr r6, [r8, #0x404] | ||
446 | bic r6, r6, #0x200000 | ||
447 | str r6, [r8, #0x404] | ||
448 | |||
449 | poll_dvfs_clear_1: | ||
450 | ldr r6, [r8, #0x404] | ||
451 | and r6, r6, #0x2000000 | ||
452 | cmp r6, #0x2000000 | ||
453 | beq poll_dvfs_clear_1 | ||
454 | |||
455 | /* Enable Automatic power savings. */ | ||
456 | ldr r6, [r8, #0x404] | ||
457 | bic r6, r6, #0x01 | ||
458 | str r6, [r8, #0x404] | ||
459 | |||
460 | ldr r10, =24000000 | ||
461 | cmp r0, r10 | ||
462 | beq skip_power_down | ||
463 | |||
464 | /* Enable MMDC power down timer. */ | ||
465 | ldr r6, [r8, #0x4] | ||
466 | orr r6, r6, #0x5500 | ||
467 | str r6, [r8, #0x4] | ||
468 | |||
469 | skip_power_down: | ||
470 | /* clear SBS - unblock DDR accesses */ | ||
471 | ldr r6, [r8, #0x410] | ||
472 | bic r6, r6, #0x100 | ||
473 | str r6, [r8, #0x410] | ||
474 | |||
475 | pop {r4-r10} | ||
476 | |||
477 | /* Restore registers */ | ||
478 | mov pc, lr | ||
479 | |||
480 | .type mx6_lpddr2_do_iram, #object | ||
481 | ENTRY(mx6_lpddr2_do_iram) | ||
482 | .word mx6_lpddr2_freq_change | ||
483 | .size mx6_lpddr2_freq_change, . - mx6_lpddr2_freq_change | ||
diff --git a/include/dt-bindings/clock/imx6sl-clock.h b/include/dt-bindings/clock/imx6sl-clock.h index 118b730bc635..b04e64a2ec2a 100644 --- a/include/dt-bindings/clock/imx6sl-clock.h +++ b/include/dt-bindings/clock/imx6sl-clock.h | |||
@@ -71,8 +71,8 @@ | |||
71 | #define IMX6SL_CLK_PERIPH 58 | 71 | #define IMX6SL_CLK_PERIPH 58 |
72 | #define IMX6SL_CLK_PERIPH2 59 | 72 | #define IMX6SL_CLK_PERIPH2 59 |
73 | #define IMX6SL_CLK_OCRAM_PODF 60 | 73 | #define IMX6SL_CLK_OCRAM_PODF 60 |
74 | #define IMX6SL_CLK_PERIPH_CLK2_PODF 61 | 74 | #define IMX6SL_CLK_PERIPH_CLK2 61 |
75 | #define IMX6SL_CLK_PERIPH2_CLK2_PODF 62 | 75 | #define IMX6SL_CLK_PERIPH2_CLK2 62 |
76 | #define IMX6SL_CLK_IPG 63 | 76 | #define IMX6SL_CLK_IPG 63 |
77 | #define IMX6SL_CLK_CSI_PODF 64 | 77 | #define IMX6SL_CLK_CSI_PODF 64 |
78 | #define IMX6SL_CLK_LCDIF_AXI_PODF 65 | 78 | #define IMX6SL_CLK_LCDIF_AXI_PODF 65 |