aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/arm/imx/busfreq-imx6.txt64
-rw-r--r--arch/arm/mach-imx/Makefile6
-rw-r--r--arch/arm/mach-imx/busfreq-imx6.c599
-rw-r--r--arch/arm/mach-imx/busfreq_ddr3.c466
-rw-r--r--arch/arm/mach-imx/ddr3_freq_imx6.S921
-rw-r--r--include/linux/busfreq-imx6.h23
6 files changed, 2079 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/arm/imx/busfreq-imx6.txt b/Documentation/devicetree/bindings/arm/imx/busfreq-imx6.txt
new file mode 100644
index 000000000000..93386a87b197
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/imx/busfreq-imx6.txt
@@ -0,0 +1,64 @@
1Freescale Busfreq driver
2
3It is a generic driver that manages the frequency of the DDR, AHB and AXI buses in the iMX6x architecture.
4It works for both SMP and UP systems and for both DDR3 and LPDDR2 memory types.
5
6Required properties are listed below:
7- compatible: should be "fsl,imx6_busfreq"
8- clocks: Lists the various clocks used by the busfreq driver
9- interrupts - Lists the interrupts used by the busfreq driver. This is needed only for SMP architecutre.
10- fsl,max_ddr_freq - The max ddr freq for this chip
11
12Examples:
13For SOC imx6q.dtsi:
14 busfreq { /* BUSFREQ */
15 compatible = "fsl,imx6_busfreq";
16 clocks = <&clks 171>, <&clks 6>, <&clks 11>, <&clks 104>, <&clks 172>, <&clks 58>,
17 <&clks 18>, <&clks 60>, <&clks 20>, <&clks 3>;
18 clock-names = "pll2_bus", "pll2_pfd2_396m", "pll2_198m", "arm", "pll3_usb_otg", "periph",
19 "periph_pre", "periph_clk2", "periph_clk2_sel", "osc";
20 interrupts = <0 107 0x04>, <0 112 0x4>, <0 113 0x4>, <0 114 0x4>;
21 interrupt-names = "irq_busfreq_0", "irq_busfreq_1", "irq_busfreq_2", "irq_busfreq_3";
22 fsl,max_ddr_freq = <528000000>;
23 };
24
25The Freescale Busfreq driver supports the following setpoints for the DDR freq:
26enum bus_freq_mode {
27 BUS_FREQ_HIGH, -> The max freq the SOC supports
28 BUS_FREQ_MED, -> Medium setpoint (ex 400MHz for DDR3 when the max is 528MHz)
29 BUS_FREQ_AUDIO, -> Audio playback freq (50MHz)
30 BUS_FREQ_LOW, -> Low power IDLE freq (24MHz)
31};
32
33Currently the Freescale Busfreq driver implementation requires drivers to call the following APIs:
341. request_bus_freq(enum bus_freq_mode):
35 The driver is requesting the system and ddr freq to be set to the requested value. The driver should call this
36 API before it even enables its clocks.
37
382. release_bus_freq(enum bus_freq_mode):
39 The driver no longer needs the system and ddr freq at the required value. The driver should call this API after
40 its work is done and it has disabled its clocks.
41
42Examples:
43In the IPU driver, the requesting and releasing of the required bus frequency is tied into the runtime PM implementation:
44
45int ipu_runtime_suspend(struct device *dev)
46{
47 release_bus_freq(BUS_FREQ_HIGH);
48 dev_dbg(dev, "ipu busfreq high release.\n");
49
50 return 0;
51}
52
53int ipu_runtime_resume(struct device *dev)
54{
55 request_bus_freq(BUS_FREQ_HIGH);
56 dev_dbg(dev, "ipu busfreq high requst.\n");
57
58 return 0;
59}
60
61static const struct dev_pm_ops ipu_pm_ops = {
62 SET_RUNTIME_PM_OPS(ipu_runtime_suspend, ipu_runtime_resume, NULL)
63 SET_SYSTEM_SLEEP_PM_OPS(ipu_suspend, ipu_resume)
64};
diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile
index 64aacbe49e61..d1e29d1afdef 100644
--- a/arch/arm/mach-imx/Makefile
+++ b/arch/arm/mach-imx/Makefile
@@ -104,6 +104,12 @@ obj-$(CONFIG_SOC_IMX6SL) += clk-imx6sl.o mach-imx6sl.o
104AFLAGS_suspend-imx6.o :=-Wa,-march=armv7-a 104AFLAGS_suspend-imx6.o :=-Wa,-march=armv7-a
105obj-$(CONFIG_PM) += pm-imx6.o headsmp.o suspend-imx6.o 105obj-$(CONFIG_PM) += pm-imx6.o headsmp.o suspend-imx6.o
106 106
107ifeq ($(CONFIG_ARM_IMX6Q_CPUFREQ),y)
108obj-y += busfreq-imx6.o
109obj-$(CONFIG_SOC_IMX6Q) += ddr3_freq_imx6.o busfreq_ddr3.o
110endif
111
112
107# i.MX5 based machines 113# i.MX5 based machines
108obj-$(CONFIG_MACH_MX51_BABBAGE) += mach-mx51_babbage.o 114obj-$(CONFIG_MACH_MX51_BABBAGE) += mach-mx51_babbage.o
109obj-$(CONFIG_MACH_EUKREA_CPUIMX51SD) += mach-cpuimx51sd.o 115obj-$(CONFIG_MACH_EUKREA_CPUIMX51SD) += mach-cpuimx51sd.o
diff --git a/arch/arm/mach-imx/busfreq-imx6.c b/arch/arm/mach-imx/busfreq-imx6.c
new file mode 100644
index 000000000000..c7a7cce30315
--- /dev/null
+++ b/arch/arm/mach-imx/busfreq-imx6.c
@@ -0,0 +1,599 @@
1/*
2 * Copyright (C) 2011-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/*!
20 * @file busfreq-imx6.c
21 *
22 * @brief A common API for the Freescale Semiconductor iMX6 Busfreq API
23 *
24 * The APIs are for setting bus frequency to different values based on the
25 * highest freqeuncy requested.
26 *
27 * @ingroup PM
28 */
29
30#include <asm/cacheflush.h>
31#include <asm/io.h>
32#include <asm/mach/map.h>
33#include <asm/mach-types.h>
34#include <asm/tlb.h>
35#include <linux/busfreq-imx6.h>
36#include <linux/clk.h>
37#include <linux/clk-provider.h>
38#include <linux/delay.h>
39#include <linux/module.h>
40#include <linux/mutex.h>
41#include <linux/of.h>
42#include <linux/platform_device.h>
43#include <linux/proc_fs.h>
44#include <linux/regulator/consumer.h>
45#include <linux/sched.h>
46#include <linux/suspend.h>
47#include "hardware.h"
48
49#define LPAPM_CLK 24000000
50#define DDR_AUDIO_CLK 50000000
51
52int high_bus_freq_mode;
53int med_bus_freq_mode;
54int audio_bus_freq_mode;
55int low_bus_freq_mode;
56
57static int bus_freq_scaling_initialized;
58static struct device *busfreq_dev;
59static int busfreq_suspended;
60
61static int bus_freq_scaling_is_active;
62static int high_bus_count, med_bus_count, audio_bus_count;
63static unsigned int ddr_low_rate;
64unsigned int ddr_med_rate;
65unsigned int ddr_normal_rate;
66
67extern int init_mmdc_settings(struct platform_device *dev);
68extern int update_ddr_freq(int ddr_rate);
69
70DEFINE_MUTEX(bus_freq_mutex);
71
72static struct clk *pll2_400;
73static struct clk *periph_clk;
74static struct clk *periph_pre_clk;
75static struct clk *periph_clk2_sel;
76static struct clk *periph_clk2;
77static struct clk *osc_clk;
78static struct clk *cpu_clk;
79static struct clk *pll3;
80static struct clk *pll2;
81static struct clk *pll2_200;
82
83static struct delayed_work low_bus_freq_handler;
84static struct delayed_work bus_freq_daemon;
85
86int low_bus_freq;
87
88int reduce_bus_freq(void)
89{
90 int ret = 0;
91 clk_prepare_enable(pll3);
92 if (low_bus_freq) {
93 /* Need to ensure that PLL2_PFD_400M is kept ON. */
94 clk_prepare_enable(pll2_400);
95 update_ddr_freq(DDR_AUDIO_CLK);
96 /* Make sure periph clk's parent also got updated */
97 ret = clk_set_parent(periph_clk2_sel, pll3);
98 if (ret)
99 dev_WARN(busfreq_dev, "%s: %d: clk set parent fail!\n",
100 __func__, __LINE__);
101 ret = clk_set_parent(periph_pre_clk, pll2_200);
102 if (ret)
103 dev_WARN(busfreq_dev, "%s: %d: clk set parent fail!\n",
104 __func__, __LINE__);
105 ret = clk_set_parent(periph_clk, periph_pre_clk);
106 if (ret)
107 dev_WARN(busfreq_dev, "%s: %d: clk set parent fail!\n",
108 __func__, __LINE__);
109 audio_bus_freq_mode = 1;
110 low_bus_freq_mode = 0;
111 } else {
112 update_ddr_freq(LPAPM_CLK);
113 /* Make sure periph clk's parent also got updated */
114 ret = clk_set_parent(periph_clk2_sel, osc_clk);
115 if (ret)
116 dev_WARN(busfreq_dev, "%s: %d: clk set parent fail!\n",
117 __func__, __LINE__);
118 ret = clk_set_parent(periph_clk, periph_clk2);
119 if (ret)
120 dev_WARN(busfreq_dev, "%s: %d: clk set parent fail!\n",
121 __func__, __LINE__);
122 if (audio_bus_freq_mode)
123 clk_disable_unprepare(pll2_400);
124 low_bus_freq_mode = 1;
125 audio_bus_freq_mode = 0;
126 }
127 if (high_bus_freq_mode && cpu_is_imx6dl())
128 clk_disable_unprepare(pll2_400);
129
130 clk_disable_unprepare(pll3);
131 med_bus_freq_mode = 0;
132 high_bus_freq_mode = 0;
133
134 if (audio_bus_freq_mode)
135 dev_dbg(busfreq_dev, "Bus freq set to audio mode. Count:\
136 high %d, med %d, audio %d\n",
137 high_bus_count, med_bus_count, audio_bus_count);
138 if (low_bus_freq_mode)
139 dev_dbg(busfreq_dev, "Bus freq set to low mode. Count:\
140 high %d, med %d, audio %d\n",
141 high_bus_count, med_bus_count, audio_bus_count);
142
143 return ret;
144}
145
146static void reduce_bus_freq_handler(struct work_struct *work)
147{
148 mutex_lock(&bus_freq_mutex);
149
150 reduce_bus_freq();
151
152 mutex_unlock(&bus_freq_mutex);
153}
154
155/*
156 * Set the DDR, AHB to 24MHz.
157 * This mode will be activated only when none of the modules that
158 * need a higher DDR or AHB frequency are active.
159 */
160int set_low_bus_freq(int low_bus_mode)
161{
162 if (busfreq_suspended)
163 return 0;
164
165 if (!bus_freq_scaling_initialized || !bus_freq_scaling_is_active)
166 return 0;
167
168 /*
169 * Don't lower the frequency immediately. Instead
170 * scheduled a delayed work and drop the freq if
171 * the conditions still remain the same.
172 */
173 low_bus_freq = low_bus_mode;
174 schedule_delayed_work(&low_bus_freq_handler,
175 usecs_to_jiffies(3000000));
176 return 0;
177}
178
179/*
180 * Set the DDR to either 528MHz or 400MHz for iMX6qd
181 * or 400MHz for iMX6dl.
182 */
183int set_high_bus_freq(int high_bus_freq)
184{
185 int ret = 0;
186
187 if (bus_freq_scaling_initialized && bus_freq_scaling_is_active)
188 cancel_delayed_work_sync(&low_bus_freq_handler);
189
190 if (busfreq_suspended)
191 return 0;
192
193 /* for high setpoint, i.MX6Q is 528MHz, i.MX6DL is 400MHz */
194 if (cpu_is_imx6q())
195 high_bus_freq = 1;
196 else
197 high_bus_freq = 0;
198
199 if (!bus_freq_scaling_initialized || !bus_freq_scaling_is_active)
200 return 0;
201
202 if (high_bus_freq_mode)
203 return 0;
204
205 /* medium bus freq is only supported for MX6DQ */
206 if (med_bus_freq_mode && !high_bus_freq)
207 return 0;
208
209 clk_prepare_enable(pll3);
210 if (high_bus_freq) {
211 update_ddr_freq(ddr_normal_rate);
212 /* Make sure periph clk's parent also got updated */
213 ret = clk_set_parent(periph_clk2_sel, pll3);
214 if (ret)
215 dev_WARN(busfreq_dev, "%s: %d: clk set parent fail!\n",
216 __func__, __LINE__);
217 ret = clk_set_parent(periph_pre_clk, pll2);
218 if (ret)
219 dev_WARN(busfreq_dev, "%s: %d: clk set parent fail!\n",
220 __func__, __LINE__);
221 ret = clk_set_parent(periph_clk, periph_pre_clk);
222 if (ret)
223 dev_WARN(busfreq_dev, "%s: %d: clk set parent fail!\n",
224 __func__, __LINE__);
225 if (med_bus_freq_mode)
226 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 }
244 if (audio_bus_freq_mode)
245 clk_disable_unprepare(pll2_400);
246
247 high_bus_freq_mode = 1;
248 med_bus_freq_mode = 0;
249 low_bus_freq_mode = 0;
250 audio_bus_freq_mode = 0;
251
252 clk_disable_unprepare(pll3);
253
254 if (high_bus_freq_mode)
255 dev_dbg(busfreq_dev, "Bus freq set to high mode. Count:\
256 high %d, med %d, audio %d\n",
257 high_bus_count, med_bus_count, audio_bus_count);
258 if (med_bus_freq_mode)
259 dev_dbg(busfreq_dev, "Bus freq set to med mode. Count:\
260 high %d, med %d, audio %d\n",
261 high_bus_count, med_bus_count, audio_bus_count);
262
263 return 0;
264}
265
266void request_bus_freq(enum bus_freq_mode mode)
267{
268 mutex_lock(&bus_freq_mutex);
269
270 if (mode == BUS_FREQ_HIGH)
271 high_bus_count++;
272 else if (mode == BUS_FREQ_MED)
273 med_bus_count++;
274 else if (mode == BUS_FREQ_AUDIO)
275 audio_bus_count++;
276
277 if (busfreq_suspended || !bus_freq_scaling_initialized ||
278 !bus_freq_scaling_is_active) {
279 mutex_unlock(&bus_freq_mutex);
280 return;
281 }
282
283 cancel_delayed_work_sync(&low_bus_freq_handler);
284 if ((mode == BUS_FREQ_HIGH) && (!high_bus_freq_mode)) {
285 set_high_bus_freq(1);
286 mutex_unlock(&bus_freq_mutex);
287 return;
288 }
289
290 if ((mode == BUS_FREQ_MED) && (!high_bus_freq_mode) &&
291 (!med_bus_freq_mode)) {
292 set_high_bus_freq(0);
293 mutex_unlock(&bus_freq_mutex);
294 return;
295 }
296 if ((mode == BUS_FREQ_AUDIO) && (!high_bus_freq_mode) &&
297 (!med_bus_freq_mode) && (!audio_bus_freq_mode)) {
298 set_low_bus_freq(1);
299 mutex_unlock(&bus_freq_mutex);
300 return;
301 }
302 mutex_unlock(&bus_freq_mutex);
303 return;
304}
305EXPORT_SYMBOL(request_bus_freq);
306
307void release_bus_freq(enum bus_freq_mode mode)
308{
309 mutex_lock(&bus_freq_mutex);
310
311 if (mode == BUS_FREQ_HIGH) {
312 if (high_bus_count == 0) {
313 dev_err(busfreq_dev, "high bus count mismatch!\n");
314 dump_stack();
315 mutex_unlock(&bus_freq_mutex);
316 return;
317 }
318 high_bus_count--;
319 } else if (mode == BUS_FREQ_MED) {
320 if (med_bus_count == 0) {
321 dev_err(busfreq_dev, "med bus count mismatch!\n");
322 dump_stack();
323 mutex_unlock(&bus_freq_mutex);
324 return;
325 }
326 med_bus_count--;
327 } else if (mode == BUS_FREQ_AUDIO) {
328 if (audio_bus_count == 0) {
329 dev_err(busfreq_dev, "audio bus count mismatch!\n");
330 dump_stack();
331 mutex_unlock(&bus_freq_mutex);
332 return;
333 }
334 audio_bus_count--;
335 }
336
337 if (busfreq_suspended || !bus_freq_scaling_initialized ||
338 !bus_freq_scaling_is_active) {
339 mutex_unlock(&bus_freq_mutex);
340 return;
341 }
342
343 if ((!audio_bus_freq_mode) && (high_bus_count == 0) &&
344 (med_bus_count == 0) && (audio_bus_count != 0)) {
345 set_low_bus_freq(1);
346 mutex_unlock(&bus_freq_mutex);
347 return;
348 }
349 if ((!low_bus_freq_mode) && (high_bus_count == 0) &&
350 (med_bus_count == 0) && (audio_bus_count == 0))
351 set_low_bus_freq(0);
352
353 mutex_unlock(&bus_freq_mutex);
354 return;
355}
356EXPORT_SYMBOL(release_bus_freq);
357
358static void bus_freq_daemon_handler(struct work_struct *work)
359{
360 mutex_lock(&bus_freq_mutex);
361 if ((!low_bus_freq_mode) && (high_bus_count == 0) &&
362 (med_bus_count == 0) && (audio_bus_count == 0))
363 set_low_bus_freq(0);
364 mutex_unlock(&bus_freq_mutex);
365}
366
367static ssize_t bus_freq_scaling_enable_show(struct device *dev,
368 struct device_attribute *attr, char *buf)
369{
370 if (bus_freq_scaling_is_active)
371 return sprintf(buf, "Bus frequency scaling is enabled\n");
372 else
373 return sprintf(buf, "Bus frequency scaling is disabled\n");
374}
375
376static ssize_t bus_freq_scaling_enable_store(struct device *dev,
377 struct device_attribute *attr,
378 const char *buf, size_t size)
379{
380 if (strncmp(buf, "1", 1) == 0) {
381 bus_freq_scaling_is_active = 1;
382 set_high_bus_freq(1);
383 /*
384 * We set bus freq to highest at the beginning,
385 * so we use this daemon thread to make sure system
386 * can enter low bus mode if
387 * there is no high bus request pending
388 */
389 schedule_delayed_work(&bus_freq_daemon,
390 usecs_to_jiffies(5000000));
391 } else if (strncmp(buf, "0", 1) == 0) {
392 if (bus_freq_scaling_is_active)
393 set_high_bus_freq(1);
394 bus_freq_scaling_is_active = 0;
395 }
396 return size;
397}
398
399static int bus_freq_pm_notify(struct notifier_block *nb, unsigned long event,
400 void *dummy)
401{
402 mutex_lock(&bus_freq_mutex);
403
404 if (event == PM_SUSPEND_PREPARE) {
405 high_bus_count++;
406 set_high_bus_freq(1);
407 busfreq_suspended = 1;
408 } else if (event == PM_POST_SUSPEND) {
409 busfreq_suspended = 0;
410 high_bus_count--;
411 schedule_delayed_work(&bus_freq_daemon,
412 usecs_to_jiffies(5000000));
413 }
414
415 mutex_unlock(&bus_freq_mutex);
416
417 return NOTIFY_OK;
418}
419
420static struct notifier_block imx_bus_freq_pm_notifier = {
421 .notifier_call = bus_freq_pm_notify,
422};
423
424static DEVICE_ATTR(enable, 0644, bus_freq_scaling_enable_show,
425 bus_freq_scaling_enable_store);
426
427/*!
428 * This is the probe routine for the bus frequency driver.
429 *
430 * @param pdev The platform device structure
431 *
432 * @return The function returns 0 on success
433 *
434 */
435
436static int busfreq_probe(struct platform_device *pdev)
437{
438 u32 err;
439
440 busfreq_dev = &pdev->dev;
441
442 pll2_400 = devm_clk_get(&pdev->dev, "pll2_pfd2_396m");
443 if (IS_ERR(pll2_400)) {
444 dev_err(busfreq_dev, "%s: failed to get pll2_pfd2_396m\n",
445 __func__);
446 return PTR_ERR(pll2_400);
447 }
448
449 pll2_200 = devm_clk_get(&pdev->dev, "pll2_198m");
450 if (IS_ERR(pll2_200)) {
451 dev_err(busfreq_dev, "%s: failed to get pll2_198m\n",
452 __func__);
453 return PTR_ERR(pll2_200);
454 }
455
456 pll2 = devm_clk_get(&pdev->dev, "pll2_bus");
457 if (IS_ERR(pll2)) {
458 dev_err(busfreq_dev, "%s: failed to get pll2_bus\n",
459 __func__);
460 return PTR_ERR(pll2);
461 }
462
463 cpu_clk = devm_clk_get(&pdev->dev, "arm");
464 if (IS_ERR(cpu_clk)) {
465 dev_err(busfreq_dev, "%s: failed to get cpu_clk\n",
466 __func__);
467 return PTR_ERR(cpu_clk);
468 }
469
470 pll3 = devm_clk_get(&pdev->dev, "pll3_usb_otg");
471 if (IS_ERR(pll3)) {
472 dev_err(busfreq_dev, "%s: failed to get pll3_usb_otg\n",
473 __func__);
474 return PTR_ERR(pll3);
475 }
476
477 periph_clk = devm_clk_get(&pdev->dev, "periph");
478 if (IS_ERR(periph_clk)) {
479 dev_err(busfreq_dev, "%s: failed to get periph\n",
480 __func__);
481 return PTR_ERR(periph_clk);
482 }
483
484 periph_pre_clk = devm_clk_get(&pdev->dev, "periph_pre");
485 if (IS_ERR(periph_pre_clk)) {
486 dev_err(busfreq_dev, "%s: failed to get periph_pre\n",
487 __func__);
488 return PTR_ERR(periph_pre_clk);
489 }
490
491 periph_clk2 = devm_clk_get(&pdev->dev, "periph_clk2");
492 if (IS_ERR(periph_clk2)) {
493 dev_err(busfreq_dev, "%s: failed to get periph_clk2\n",
494 __func__);
495 return PTR_ERR(periph_clk2);
496 }
497
498 periph_clk2_sel = devm_clk_get(&pdev->dev, "periph_clk2_sel");
499 if (IS_ERR(periph_clk2_sel)) {
500 dev_err(busfreq_dev, "%s: failed to get periph_clk2_sel\n",
501 __func__);
502 return PTR_ERR(periph_clk2_sel);
503 }
504
505 osc_clk = devm_clk_get(&pdev->dev, "osc");
506 if (IS_ERR(osc_clk)) {
507 dev_err(busfreq_dev, "%s: failed to get osc_clk\n",
508 __func__);
509 return PTR_ERR(osc_clk);
510 }
511
512 err = sysfs_create_file(&busfreq_dev->kobj, &dev_attr_enable.attr);
513 if (err) {
514 dev_err(busfreq_dev,
515 "Unable to register sysdev entry for BUSFREQ");
516 return err;
517 }
518
519 if (of_property_read_u32(pdev->dev.of_node, "fsl,max_ddr_freq",
520 &ddr_normal_rate)) {
521 dev_err(busfreq_dev, "max_ddr_freq entry missing\n");
522 return -EINVAL;
523 }
524
525 high_bus_freq_mode = 1;
526 med_bus_freq_mode = 0;
527 low_bus_freq_mode = 0;
528 audio_bus_freq_mode = 0;
529
530 bus_freq_scaling_is_active = 1;
531 bus_freq_scaling_initialized = 1;
532
533 ddr_low_rate = LPAPM_CLK;
534 if (cpu_is_imx6q()) {
535 if (of_property_read_u32(pdev->dev.of_node, "fsl,med_ddr_freq",
536 &ddr_med_rate)) {
537 dev_err(busfreq_dev,
538 "DDR medium rate not supported.\n");
539 ddr_med_rate = ddr_normal_rate;
540 }
541 }
542
543 INIT_DELAYED_WORK(&low_bus_freq_handler, reduce_bus_freq_handler);
544 INIT_DELAYED_WORK(&bus_freq_daemon, bus_freq_daemon_handler);
545 register_pm_notifier(&imx_bus_freq_pm_notifier);
546
547 err = init_mmdc_settings(pdev);
548 if (err) {
549 dev_err(busfreq_dev, "Busfreq init of MMDC failed\n");
550 return err;
551 }
552 return 0;
553}
554
555static const struct of_device_id imx6_busfreq_ids[] = {
556 { .compatible = "fsl,imx6_busfreq", },
557 { /* sentinel */ }
558};
559
560static struct platform_driver busfreq_driver = {
561 .driver = {
562 .name = "imx6_busfreq",
563 .owner = THIS_MODULE,
564 .of_match_table = imx6_busfreq_ids,
565 },
566 .probe = busfreq_probe,
567};
568
569/*!
570 * Initialise the busfreq_driver.
571 *
572 * @return The function always returns 0.
573 */
574
575static int __init busfreq_init(void)
576{
577 if (platform_driver_register(&busfreq_driver) != 0)
578 return -ENODEV;
579
580 printk(KERN_INFO "Bus freq driver module loaded\n");
581
582 return 0;
583}
584
585static void __exit busfreq_cleanup(void)
586{
587 sysfs_remove_file(&busfreq_dev->kobj, &dev_attr_enable.attr);
588
589 /* Unregister the device structure */
590 platform_driver_unregister(&busfreq_driver);
591 bus_freq_scaling_initialized = 0;
592}
593
594module_init(busfreq_init);
595module_exit(busfreq_cleanup);
596
597MODULE_AUTHOR("Freescale Semiconductor, Inc.");
598MODULE_DESCRIPTION("BusFreq driver");
599MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-imx/busfreq_ddr3.c b/arch/arm/mach-imx/busfreq_ddr3.c
new file mode 100644
index 000000000000..d410f1fbb94d
--- /dev/null
+++ b/arch/arm/mach-imx/busfreq_ddr3.c
@@ -0,0 +1,466 @@
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_ddr3.c
16 *
17 * @brief iMX6 DDR3 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 */
46static unsigned long (*iram_ddr_settings)[2];
47static unsigned long (*normal_mmdc_settings)[2];
48static unsigned long (*iram_iomux_settings)[2];
49static void __iomem *mmdc_base;
50static void __iomem *iomux_base;
51static void __iomem *ccm_base;
52static void __iomem *l2_base;
53static void __iomem *gic_dist_base;
54static u32 *irqs_used;
55
56void (*mx6_change_ddr_freq)(u32 freq, void *ddr_settings,
57 bool dll_mode, void *iomux_offsets) = NULL;
58
59extern unsigned int ddr_med_rate;
60extern unsigned int ddr_normal_rate;
61extern int low_bus_freq_mode;
62extern int audio_bus_freq_mode;
63extern void mx6_ddr3_freq_change(u32 freq, void *ddr_settings,
64 bool dll_mode, void *iomux_offsets);
65
66static void *ddr_freq_change_iram_base;
67static int ddr_settings_size;
68static int iomux_settings_size;
69static volatile unsigned int cpus_in_wfe;
70static volatile bool wait_for_ddr_freq_update;
71static int curr_ddr_rate;
72
73#define MIN_DLL_ON_FREQ 333000000
74#define MAX_DLL_OFF_FREQ 125000000
75#define DDR_FREQ_CHANGE_SIZE 0x2000
76
77unsigned long ddr3_dll_mx6q[][2] = {
78 {0x0c, 0x0},
79 {0x10, 0x0},
80 {0x1C, 0x04088032},
81 {0x1C, 0x0408803a},
82 {0x1C, 0x08408030},
83 {0x1C, 0x08408038},
84 {0x818, 0x0},
85};
86
87unsigned long ddr3_calibration[][2] = {
88 {0x83c, 0x0},
89 {0x840, 0x0},
90 {0x483c, 0x0},
91 {0x4840, 0x0},
92 {0x848, 0x0},
93 {0x4848, 0x0},
94 {0x850, 0x0},
95 {0x4850, 0x0},
96};
97
98unsigned long ddr3_dll_mx6dl[][2] = {
99 {0x0c, 0x0},
100 {0x10, 0x0},
101 {0x1C, 0x04008032},
102 {0x1C, 0x0400803a},
103 {0x1C, 0x07208030},
104 {0x1C, 0x07208038},
105 {0x818, 0x0},
106};
107
108unsigned long iomux_offsets_mx6q[][2] = {
109 {0x5A8, 0x0},
110 {0x5B0, 0x0},
111 {0x524, 0x0},
112 {0x51C, 0x0},
113 {0x518, 0x0},
114 {0x50C, 0x0},
115 {0x5B8, 0x0},
116 {0x5C0, 0x0},
117};
118
119unsigned long iomux_offsets_mx6dl[][2] = {
120 {0x4BC, 0x0},
121 {0x4C0, 0x0},
122 {0x4C4, 0x0},
123 {0x4C8, 0x0},
124 {0x4CC, 0x0},
125 {0x4D0, 0x0},
126 {0x4D4, 0x0},
127 {0x4D8, 0x0},
128};
129
130unsigned long ddr3_400[][2] = {
131 {0x83c, 0x42490249},
132 {0x840, 0x02470247},
133 {0x483c, 0x42570257},
134 {0x4840, 0x02400240},
135 {0x848, 0x4039363C},
136 {0x4848, 0x3A39333F},
137 {0x850, 0x38414441},
138 {0x4850, 0x472D4833}
139};
140
141int can_change_ddr_freq(void)
142{
143 return 1;
144}
145
146/*
147 * each active core apart from the one changing
148 * the DDR frequency will execute this function.
149 * the rest of the cores have to remain in WFE
150 * state until the frequency is changed.
151 */
152irqreturn_t wait_in_wfe_irq(int irq, void *dev_id)
153{
154 u32 me = smp_processor_id();
155
156 *((char *)(&cpus_in_wfe) + (u8)me) = 0xff;
157
158 while (wait_for_ddr_freq_update)
159 wfe();
160
161 *((char *)(&cpus_in_wfe) + (u8)me) = 0;
162
163 return IRQ_HANDLED;
164}
165
166/* change the DDR frequency. */
167int update_ddr_freq(int ddr_rate)
168{
169 int i, j;
170 unsigned int reg;
171 bool dll_off = false;
172 unsigned int online_cpus = 0;
173 int cpu = 0;
174 int me;
175
176 if (!can_change_ddr_freq())
177 return -1;
178
179 if (ddr_rate == curr_ddr_rate)
180 return 0;
181
182 printk(KERN_DEBUG "\nBus freq set to %d start...\n", ddr_rate);
183
184 if (low_bus_freq_mode || audio_bus_freq_mode)
185 dll_off = true;
186
187 iram_ddr_settings[0][0] = ddr_settings_size;
188 iram_iomux_settings[0][0] = iomux_settings_size;
189 if (ddr_rate == ddr_med_rate && cpu_is_imx6q()) {
190 for (i = 0; i < ARRAY_SIZE(ddr3_dll_mx6q); i++) {
191 iram_ddr_settings[i + 1][0] =
192 normal_mmdc_settings[i][0];
193 iram_ddr_settings[i + 1][1] =
194 normal_mmdc_settings[i][1];
195 }
196 for (j = 0, i = ARRAY_SIZE(ddr3_dll_mx6q);
197 i < iram_ddr_settings[0][0]; j++, i++) {
198 iram_ddr_settings[i + 1][0] =
199 ddr3_400[j][0];
200 iram_ddr_settings[i + 1][1] =
201 ddr3_400[j][1];
202 }
203 } else if (ddr_rate == ddr_normal_rate) {
204 for (i = 0; i < iram_ddr_settings[0][0]; i++) {
205 iram_ddr_settings[i + 1][0] =
206 normal_mmdc_settings[i][0];
207 iram_ddr_settings[i + 1][1] =
208 normal_mmdc_settings[i][1];
209 }
210 }
211
212 /* ensure that all Cores are in WFE. */
213 local_irq_disable();
214
215 me = smp_processor_id();
216
217 *((char *)(&cpus_in_wfe) + (u8)me) = 0xff;
218 wait_for_ddr_freq_update = true;
219 for_each_online_cpu(cpu) {
220 *((char *)(&online_cpus) + (u8)cpu) = 0xff;
221 if (cpu != me) {
222 /* set the interrupt to be pending in the GIC. */
223 reg = 1 << (irqs_used[cpu] % 32);
224 writel_relaxed(reg, gic_dist_base + GIC_DIST_PENDING_SET
225 + (irqs_used[cpu] / 32) * 4);
226 }
227 }
228 while (cpus_in_wfe != online_cpus)
229 udelay(5);
230
231 /* Now we can change the DDR frequency. */
232 mx6_change_ddr_freq(ddr_rate, iram_ddr_settings,
233 dll_off, iram_iomux_settings);
234
235 curr_ddr_rate = ddr_rate;
236
237 /* DDR frequency change is done . */
238 wait_for_ddr_freq_update = false;
239
240 /* wake up all the cores. */
241 sev();
242
243 *((char *)(&cpus_in_wfe) + (u8)me) = 0;
244
245 local_irq_enable();
246
247 printk(KERN_DEBUG "Bus freq set to %d done!\n", ddr_rate);
248
249 return 0;
250}
251
252int init_mmdc_settings(struct platform_device *busfreq_pdev)
253{
254 struct device *dev = &busfreq_pdev->dev;
255 struct platform_device *ocram_dev;
256 unsigned int iram_paddr;
257 int i, err;
258 u32 cpu;
259 struct device_node *node;
260 struct gen_pool *iram_pool;
261
262 node = of_find_compatible_node(NULL, NULL, "fsl,imx6q-mmdc-combine");
263 if (!node) {
264 printk(KERN_ERR "failed to find imx6q-mmdc device tree data!\n");
265 return -EINVAL;
266 }
267 mmdc_base = of_iomap(node, 0);
268 WARN(!mmdc_base, "unable to map mmdc registers\n");
269
270 node = NULL;
271 if (cpu_is_imx6q())
272 node = of_find_compatible_node(NULL, NULL, "fsl,imx6q-iomuxc");
273 if (cpu_is_imx6dl())
274 node = of_find_compatible_node(NULL, NULL,
275 "fsl,imx6dl-iomuxc");
276 if (!node) {
277 printk(KERN_ERR "failed to find imx6q-iomux device tree data!\n");
278 return -EINVAL;
279 }
280 iomux_base = of_iomap(node, 0);
281 WARN(!iomux_base, "unable to map iomux registers\n");
282
283 node = of_find_compatible_node(NULL, NULL, "fsl,imx6q-ccm");
284 if (!node) {
285 printk(KERN_ERR "failed to find imx6q-ccm device tree data!\n");
286 return -EINVAL;
287 }
288 ccm_base = of_iomap(node, 0);
289 WARN(!mmdc_base, "unable to map mmdc registers\n");
290
291 node = of_find_compatible_node(NULL, NULL, "arm,pl310-cache");
292 if (!node) {
293 printk(KERN_ERR "failed to find imx6q-pl310-cache device tree data!\n");
294 return -EINVAL;
295 }
296 l2_base = of_iomap(node, 0);
297 WARN(!mmdc_base, "unable to map mmdc registers\n");
298
299 node = NULL;
300 node = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-gic");
301 if (!node) {
302 printk(KERN_ERR "failed to find imx6q-a9-gic device tree data!\n");
303 return -EINVAL;
304 }
305 gic_dist_base = of_iomap(node, 0);
306 WARN(!gic_dist_base, "unable to map gic dist registers\n");
307
308 if (cpu_is_imx6q())
309 ddr_settings_size = ARRAY_SIZE(ddr3_dll_mx6q) +
310 ARRAY_SIZE(ddr3_calibration);
311 if (cpu_is_imx6dl())
312 ddr_settings_size = ARRAY_SIZE(ddr3_dll_mx6dl) +
313 ARRAY_SIZE(ddr3_calibration);
314
315 normal_mmdc_settings = kmalloc((ddr_settings_size * 8), GFP_KERNEL);
316 if (cpu_is_imx6q()) {
317 memcpy(normal_mmdc_settings, ddr3_dll_mx6q,
318 sizeof(ddr3_dll_mx6q));
319 memcpy(((char *)normal_mmdc_settings + sizeof(ddr3_dll_mx6q)),
320 ddr3_calibration, sizeof(ddr3_calibration));
321 }
322 if (cpu_is_imx6dl()) {
323 memcpy(normal_mmdc_settings, ddr3_dll_mx6dl,
324 sizeof(ddr3_dll_mx6dl));
325 memcpy(((char *)normal_mmdc_settings + sizeof(ddr3_dll_mx6dl)),
326 ddr3_calibration, sizeof(ddr3_calibration));
327 }
328 /* store the original DDR settings at boot. */
329 for (i = 0; i < ddr_settings_size; i++) {
330 /*
331 * writes via command mode register cannot be read back.
332 * hence hardcode them in the initial static array.
333 * this may require modification on a per customer basis.
334 */
335 if (normal_mmdc_settings[i][0] != 0x1C)
336 normal_mmdc_settings[i][1] =
337 readl_relaxed(mmdc_base
338 + normal_mmdc_settings[i][0]);
339 }
340
341 irqs_used = devm_kzalloc(dev, sizeof(u32) * num_present_cpus(),
342 GFP_KERNEL);
343
344 for_each_present_cpu(cpu) {
345 int irq;
346
347 /*
348 * set up a reserved interrupt to get all
349 * the active cores into a WFE state
350 * before changing the DDR frequency.
351 */
352 irq = platform_get_irq(busfreq_pdev, cpu);
353 err = request_irq(irq, wait_in_wfe_irq,
354 IRQF_PERCPU, "mmdc_1", NULL);
355 if (err) {
356 dev_err(dev,
357 "Busfreq:request_irq failed %d, err = %d\n",
358 irq, err);
359 return err;
360 }
361 err = irq_set_affinity(irq, cpumask_of(cpu));
362 if (err) {
363 dev_err(dev,
364 "Busfreq: Cannot set irq affinity irq=%d,\n",
365 irq);
366 return err;
367 }
368 irqs_used[cpu] = irq;
369 }
370
371 node = NULL;
372 node = of_find_compatible_node(NULL, NULL, "mmio-sram");
373 if (!node) {
374 dev_err(dev, "%s: failed to find ocram node\n",
375 __func__);
376 return -EINVAL;
377 }
378
379 ocram_dev = of_find_device_by_node(node);
380 if (!ocram_dev) {
381 dev_err(dev, "failed to find ocram device!\n");
382 return -EINVAL;
383 }
384
385 iram_pool = dev_get_gen_pool(&ocram_dev->dev);
386 if (!iram_pool) {
387 dev_err(dev, "iram pool unavailable!\n");
388 return -EINVAL;
389 }
390
391 iomux_settings_size = ARRAY_SIZE(iomux_offsets_mx6q);
392 iram_iomux_settings = gen_pool_alloc(iram_pool,
393 (iomux_settings_size * 8) + 8);
394 if (!iram_iomux_settings) {
395 dev_err(dev, "unable to alloc iram for IOMUX settings!\n");
396 return -ENOMEM;
397 }
398
399 /*
400 * Allocate extra space to store the number of entries in the
401 * ddr_settings plus 4 extra regsiter information that needs
402 * to be passed to the frequency change code.
403 * sizeof(iram_ddr_settings) = sizeof(ddr_settings) +
404 * entries in ddr_settings + 16.
405 * The last 4 enties store the addresses of the registers:
406 * CCM_BASE_ADDR
407 * MMDC_BASE_ADDR
408 * IOMUX_BASE_ADDR
409 * L2X0_BASE_ADDR
410 */
411 iram_ddr_settings = gen_pool_alloc(iram_pool,
412 (ddr_settings_size * 8) + 8 + 32);
413 if (!iram_ddr_settings) {
414 dev_err(dev, "unable to alloc iram for ddr settings!\n");
415 return -ENOMEM;
416 }
417 i = ddr_settings_size + 1;
418 iram_ddr_settings[i][0] = (unsigned long)mmdc_base;
419 iram_ddr_settings[i+1][0] = (unsigned long)ccm_base;
420 iram_ddr_settings[i+2][0] = (unsigned long)iomux_base;
421 iram_ddr_settings[i+3][0] = (unsigned long)l2_base;
422
423 if (cpu_is_imx6q()) {
424 /* store the IOMUX settings at boot. */
425 for (i = 0; i < iomux_settings_size; i++) {
426 iomux_offsets_mx6q[i][1] =
427 readl_relaxed(iomux_base +
428 iomux_offsets_mx6q[i][0]);
429 iram_iomux_settings[i+1][0] = iomux_offsets_mx6q[i][0];
430 iram_iomux_settings[i+1][1] = iomux_offsets_mx6q[i][1];
431 }
432 }
433
434 if (cpu_is_imx6dl()) {
435 for (i = 0; i < iomux_settings_size; i++) {
436 iomux_offsets_mx6dl[i][1] =
437 readl_relaxed(iomux_base +
438 iomux_offsets_mx6dl[i][0]);
439 iram_iomux_settings[i+1][0] = iomux_offsets_mx6dl[i][0];
440 iram_iomux_settings[i+1][1] = iomux_offsets_mx6dl[i][1];
441 }
442 }
443
444 ddr_freq_change_iram_base = gen_pool_alloc(iram_pool,
445 DDR_FREQ_CHANGE_SIZE);
446 if (!ddr_freq_change_iram_base) {
447 dev_err(dev, "Cannot alloc iram for ddr freq change code!\n");
448 return -ENOMEM;
449 }
450
451 iram_paddr = gen_pool_virt_to_phys(iram_pool,
452 (unsigned long)ddr_freq_change_iram_base);
453 /*
454 * need to remap the area here since we want
455 * the memory region to be executable.
456 */
457 ddr_freq_change_iram_base = __arm_ioremap(iram_paddr,
458 DDR_FREQ_CHANGE_SIZE,
459 MT_MEMORY_NONCACHED);
460 mx6_change_ddr_freq = (void *)fncpy(ddr_freq_change_iram_base,
461 &mx6_ddr3_freq_change, DDR_FREQ_CHANGE_SIZE);
462
463 curr_ddr_rate = ddr_normal_rate;
464
465 return 0;
466}
diff --git a/arch/arm/mach-imx/ddr3_freq_imx6.S b/arch/arm/mach-imx/ddr3_freq_imx6.S
new file mode 100644
index 000000000000..e7e67ce34382
--- /dev/null
+++ b/arch/arm/mach-imx/ddr3_freq_imx6.S
@@ -0,0 +1,921 @@
1/*
2 * Copyright (C) 2011-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#define MMDC0_MDPDC 0x4
22#define MMDC0_MDCF0 0x0c
23#define MMDC0_MDCF1 0x10
24#define MMDC0_MDMISC 0x18
25#define MMDC0_MDSCR 0x1c
26#define MMDC0_MAPSR 0x404
27#define MMDC0_MADPCR0 0x410
28#define MMDC0_MPZQHWCTRL 0x800
29#define MMDC1_MPZQHWCTRL 0x4800
30#define MMDC0_MPODTCTRL 0x818
31#define MMDC1_MPODTCTRL 0x4818
32#define MMDC0_MPDGCTRL0 0x83c
33#define MMDC1_MPDGCTRL0 0x483c
34#define MMDC0_MPMUR0 0x8b8
35#define MMDC1_MPMUR0 0x48b8
36
37#define CCM_CBCDR 0x14
38#define CCM_CBCMR 0x18
39#define CCM_CSCMR1 0x1c
40#define CCM_CDHIPR 0x48
41
42#define L2_CACHE_SYNC 0x730
43
44 .align 3
45
46 .macro switch_to_528MHz
47
48 /* check if periph_clk_sel is already set */
49 ldr r0, [r6, #CCM_CBCDR]
50 and r0, r0, #(1 << 25)
51 cmp r0, #(1 << 25)
52 beq set_ahb_podf_before_switch
53
54 /* change periph_clk to be sourced from pll3_clk. */
55 ldr r0, [r6, #CCM_CBCMR]
56 bic r0, r0, #(3 << 12)
57 str r0, [r6, #CCM_CBCMR]
58
59 ldr r0, [r6, #CCM_CBCDR]
60 bic r0, r0, #(0x38 << 20)
61 str r0, [r6, #CCM_CBCDR]
62
63 /*
64 * set the AHB dividers before the switch,
65 * don't change AXI clock divider,
66 * set the MMDC_DIV=1, AXI_DIV = 2, AHB_DIV=4,
67 * (need to maintain GPT divider).
68 */
69 ldr r0, [r6, #CCM_CBCDR]
70 ldr r2, =0x3f1f00
71 bic r0, r0, r2
72 orr r0, r0, #0xd00
73 orr r0, r0, #(1 << 16)
74 str r0, [r6, #CCM_CBCDR]
75
76wait_div_update528:
77 ldr r0, [r6, #CCM_CDHIPR]
78 cmp r0, #0
79 bne wait_div_update528
80
81 /* now switch periph_clk to pll3_main_clk. */
82 ldr r0, [r6, #CCM_CBCDR]
83 orr r0, r0, #(1 << 25)
84 str r0, [r6, #CCM_CBCDR]
85
86periph_clk_switch3:
87 ldr r0, [r6, #CCM_CDHIPR]
88 cmp r0, #0
89 bne periph_clk_switch3
90
91 b switch_pre_periph_clk_528
92
93set_ahb_podf_before_switch:
94 /*
95 * set the MMDC_DIV=1, AXI_DIV = 2, AHB_DIV=4,
96 * (need to maintain GPT divider).
97 */
98 ldr r0, [r6, #CCM_CBCDR]
99 ldr r2, =0x3f1f00
100 bic r0, r0, r2
101 orr r0, r0, #0xd00
102 orr r0, r0, #(1 << 16)
103 str r0, [r6, #CCM_CBCDR]
104
105wait_div_update528_1:
106 ldr r0, [r6, #CCM_CDHIPR]
107 cmp r0, #0
108 bne wait_div_update528_1
109
110switch_pre_periph_clk_528:
111
112 /* now switch pre_periph_clk to PLL2_528MHz. */
113 ldr r0, [r6, #CCM_CBCMR]
114 bic r0, r0, #(0xc << 16)
115 str r0, [r6, #CCM_CBCMR]
116
117 /* now switch periph_clk back. */
118 ldr r0, [r6, #CCM_CBCDR]
119 bic r0, r0, #(1 << 25)
120 str r0, [r6, #CCM_CBCDR]
121
122periph_clk_switch4:
123 ldr r0, [r6, #CCM_CDHIPR]
124 cmp r0, #0
125 bne periph_clk_switch4
126
127 /* change the perclk divider so that its at 6MHz. */
128 ldr r0, [r6, #CCM_CSCMR1]
129 bic r0, r0, #0x3F
130 orr r0, r0, #0xA
131 str r0, [r6, #CCM_CSCMR1]
132 .endm
133
134 .macro switch_to_400MHz
135
136 /* check if periph_clk_sel is already set. */
137 ldr r0, [r6, #CCM_CBCDR]
138 and r0, r0, #(1 << 25)
139 cmp r0, #(1 << 25)
140 beq set_ahb_podf_before_switch1
141
142 /* change periph_clk to be sourced from pll3_clk. */
143 ldr r0, [r6, #CCM_CBCMR]
144 bic r0, r0, #(3 << 12)
145 str r0, [r6, #CCM_CBCMR]
146
147 ldr r0, [r6, #CCM_CBCDR]
148 bic r0, r0, #(0x38 << 24)
149 str r0, [r6, #CCM_CBCDR]
150
151 /* now switch periph_clk to pll3_main_clk. */
152 ldr r0, [r6, #CCM_CBCDR]
153 orr r0, r0, #(1 << 25)
154 str r0, [r6, #CCM_CBCDR]
155
156periph_clk_switch5:
157 ldr r0, [r6, #CCM_CDHIPR]
158 cmp r0, #0
159 bne periph_clk_switch5
160
161 b switch_pre_periph_clk_400
162
163set_ahb_podf_before_switch1:
164 /*
165 * set the MMDC_DIV=1, AXI_DIV = 2, AHB_DIV=4,
166 * (need to maintain GPT divider).
167 */
168 ldr r0, [r6, #CCM_CBCDR]
169 ldr r2, =0x3f1f00
170 bic r0, r0, r2
171 orr r0, r0, #(0x9 << 8)
172 orr r0, r0, #(1 << 16)
173 str r0, [r6, #CCM_CBCDR]
174
175wait_div_update400_1:
176 ldr r0, [r6, #CCM_CDHIPR]
177 cmp r0, #0
178 bne wait_div_update400_1
179
180switch_pre_periph_clk_400:
181
182 /* now switch pre_periph_clk to PFD_400MHz. */
183 ldr r0, [r6, #CCM_CBCMR]
184 bic r0, r0, #(0xc << 16)
185 orr r0, r0, #(0x4 << 16)
186 str r0, [r6, #CCM_CBCMR]
187
188 /* now switch periph_clk back. */
189 ldr r0, [r6, #CCM_CBCDR]
190 bic r0, r0, #(1 << 25)
191 str r0, [r6, #CCM_CBCDR]
192
193periph_clk_switch6:
194 ldr r0, [r6, #CCM_CDHIPR]
195 cmp r0, #0
196 bne periph_clk_switch6
197
198 /*
199 * change AHB divider so that we are at 400/3=133MHz.
200 * don't change AXI clock divider.
201 * set the MMDC_DIV=1, AXI_DIV=2, AHB_DIV=3,
202 * (need to maintain GPT divider).
203 */
204 ldr r0, [r6, #CCM_CBCDR]
205 ldr r2, =0x3f1f00
206 bic r0, r0, r2
207 orr r0, r0, #(0x9 << 8)
208 orr r0, r0, #(1 << 16)
209 str r0, [r6, #CCM_CBCDR]
210
211wait_div_update400_2:
212 ldr r0, [r6, #CCM_CDHIPR]
213 cmp r0, #0
214 bne wait_div_update400_2
215
216 /* change the perclk divider so that its at 6MHz. */
217 ldr r0, [r6, #CCM_CSCMR1]
218 bic r0, r0, #0x3F
219 orr r0, r0, #0xA
220 str r0, [r6, #CCM_CSCMR1]
221
222 .endm
223
224 .macro switch_to_50MHz
225
226 /* check if periph_clk_sel is already set. */
227 ldr r0, [r6, #CCM_CBCDR]
228 and r0, r0, #(1 << 25)
229 cmp r0, #(1 << 25)
230 beq switch_pre_periph_clk_50
231
232 /*
233 * set the periph_clk to be sourced from PLL2_PFD_200M
234 * change periph_clk to be sourced from pll3_clk.
235 * ensure PLL3 is the source and set the divider to 1.
236 */
237 ldr r0, [r6, #CCM_CBCMR]
238 bic r0, r0, #(0x3 << 12)
239 str r0, [r6, #CCM_CBCMR]
240
241 ldr r0, [r6, #CCM_CBCDR]
242 bic r0, r0, #(0x38 << 24)
243 str r0, [r6, #CCM_CBCDR]
244
245 /* now switch periph_clk to pll3_main_clk. */
246 ldr r0, [r6, #CCM_CBCDR]
247 orr r0, r0, #(1 << 25)
248 str r0, [r6, #CCM_CBCDR]
249
250periph_clk_switch_50:
251 ldr r0, [r6, #CCM_CDHIPR]
252 cmp r0, #0
253 bne periph_clk_switch_50
254
255switch_pre_periph_clk_50:
256
257 /* now switch pre_periph_clk to PFD_200MHz. */
258 ldr r0, [r6, #CCM_CBCMR]
259 orr r0, r0, #(0xc << 16)
260 str r0, [r6, #CCM_CBCMR]
261
262 /*
263 * set the MMDC_DIV=4, AXI_DIV = 4, AHB_DIV=8,
264 * (need to maintain GPT divider).
265 */
266 ldr r0, [r6, #CCM_CBCDR]
267 ldr r2, =0x3f1f00
268 bic r0, r0, r2
269 orr r0, r0, #(0x18 << 16)
270 orr r0, r0, #(0x3 << 16)
271
272 /*
273 * if changing AHB divider remember to change
274 * the IPGPER divider too below.
275 */
276 orr r0, r0, #0x1d00
277 str r0, [r6, #CCM_CBCDR]
278
279wait_div_update_50:
280 ldr r0, [r6, #CCM_CDHIPR]
281 cmp r0, #0
282 bne wait_div_update_50
283
284 /* now switch periph_clk back. */
285 ldr r0, [r6, #CCM_CBCDR]
286 bic r0, r0, #(1 << 25)
287 str r0, [r6, #CCM_CBCDR]
288
289periph_clk_switch2:
290 ldr r0, [r6, #CCM_CDHIPR]
291 cmp r0, #0
292 bne periph_clk_switch2
293
294 /* change the perclk divider so that its at 6MHz. */
295 ldr r0, [r6, #CCM_CSCMR1]
296 bic r0, r0, #0x3F
297 orr r0, r0, #0x1
298 str r0, [r6, #CCM_CSCMR1]
299
300 .endm
301
302 .macro switch_to_24MHz
303 /*
304 * change the freq now try setting DDR to 24MHz.
305 * source it from the periph_clk2 ensure the
306 * periph_clk2 is sourced from 24MHz and the
307 * divider is 1.
308 */
309
310 ldr r0, [r6, #CCM_CBCMR]
311 bic r0, r0, #(0x3 << 12)
312 orr r0, r0, #(1 << 12)
313 str r0, [r6, #CCM_CBCMR]
314
315 ldr r0, [r6, #CCM_CBCDR]
316 bic r0, r0, #(0x38 << 24)
317 str r0, [r6, #CCM_CBCDR]
318
319 /* now switch periph_clk to 24MHz. */
320 ldr r0, [r6, #CCM_CBCDR]
321 orr r0, r0, #(1 << 25)
322 str r0, [r6, #CCM_CBCDR]
323
324periph_clk_switch1:
325 ldr r0, [r6, #CCM_CDHIPR]
326 cmp r0, #0
327 bne periph_clk_switch1
328
329 /* change all the dividers to 1. */
330 ldr r0, [r6, #CCM_CBCDR]
331 ldr r2, =0x3f1f00
332 bic r0, r0, r2
333 orr r0, r0, #(1 << 8)
334 str r0, [r6, #CCM_CBCDR]
335
336 /* Wait for the divider to change. */
337wait_div_update:
338 ldr r0, [r6, #CCM_CDHIPR]
339 cmp r0, #0
340 bne wait_div_update
341
342 /* change the perclk divider so that its at 6MHz. */
343 ldr r0, [r6, #CCM_CSCMR1]
344 bic r0, r0, #0x3F
345 orr r0, r0, #0x1
346 str r0, [r6, #CCM_CSCMR1]
347
348 .endm
349
350/*
351 * mx6_ddr3_freq_change
352 *
353 * idle the processor (eg, wait for interrupt).
354 * make sure DDR is in self-refresh.
355 * IRQs are already disabled.
356 */
357ENTRY(mx6_ddr3_freq_change)
358
359 stmfd sp!, {r4-r12}
360
361 /*
362 * r5 -> mmdc_base
363 * r6 -> ccm_base
364 * r7 -> iomux_base
365 * r12 -> l2_base
366 */
367 mov r4, r0
368 mov r8, r1
369 mov r9, r2
370 mov r11, r3
371
372 /*
373 * Get the addresses of the registers.
374 * They are last few entries in the
375 * ddr_settings parameter.
376 * The first entry contains the count,
377 * and each entry is 2 words.
378 */
379 ldr r0, [r1]
380 add r0, r0, #1
381 lsl r0, r0, #3
382 add r1, r0, r1
383 /* mmdc_base. */
384 ldr r5, [r1]
385 add r1, #8
386 /* ccm_base */
387 ldr r6, [r1]
388 add r1, #8
389 /*iomux_base */
390 ldr r7, [r1]
391 add r1, #8
392 /*l2_base */
393 ldr r12, [r1]
394
395ddr_freq_change:
396 /*
397 * make sure no TLB miss will occur when
398 * the DDR is in self refresh. invalidate
399 * TLB single entry to ensure that the
400 * address is not already in the TLB.
401 */
402
403 adr r10, ddr_freq_change
404
405 ldr r2, [r6]
406 ldr r2, [r5]
407 ldr r2, [r7]
408 ldr r2, [r8]
409 ldr r2, [r10]
410 ldr r2, [r11]
411 ldr r2, [r12]
412
413#ifdef CONFIG_CACHE_L2X0
414 /*
415 * Make sure the L2 buffers are drained.
416 * Sync operation on L2 drains the buffers.
417 */
418 mov r1, #0x0
419 str r1, [r12, #L2_CACHE_SYNC]
420#endif
421
422 /* disable automatic power saving. */
423 ldr r0, [r5, #MMDC0_MAPSR]
424 orr r0, r0, #0x01
425 str r0, [r5, #MMDC0_MAPSR]
426
427 /* disable MMDC power down timer. */
428 ldr r0, [r5, #MMDC0_MDPDC]
429 bic r0, r0, #(0xff << 8)
430 str r0, [r5, #MMDC0_MDPDC]
431
432 /* delay for a while */
433 ldr r1, =4
434delay1:
435 ldr r2, =0
436cont1:
437 ldr r0, [r5, r2]
438 add r2, r2, #4
439 cmp r2, #16
440 bne cont1
441 sub r1, r1, #1
442 cmp r1, #0
443 bgt delay1
444
445 /* set CON_REG */
446 ldr r0, =0x8000
447 str r0, [r5, #MMDC0_MDSCR]
448poll_conreq_set_1:
449 ldr r0, [r5, #MMDC0_MDSCR]
450 and r0, r0, #(0x4 << 12)
451 cmp r0, #(0x4 << 12)
452 bne poll_conreq_set_1
453
454 ldr r0, =0x00008010
455 str r0, [r5, #MMDC0_MDSCR]
456 ldr r0, =0x00008018
457 str r0, [r5, #MMDC0_MDSCR]
458
459 /*
460 * if requested frequency is greater than
461 * 300MHz go to DLL on mode.
462 */
463 ldr r1, =300000000
464 cmp r4, r1
465 bge dll_on_mode
466
467dll_off_mode:
468
469 /* if DLL is currently on, turn it off. */
470 cmp r9, #1
471 beq continue_dll_off_1
472
473 ldr r0, =0x00018031
474 str r0, [r5, #MMDC0_MDSCR]
475
476 ldr r0, =0x00018039
477 str r0, [r5, #MMDC0_MDSCR]
478
479 ldr r1, =10
480delay1a:
481 ldr r2, =0
482cont1a:
483 ldr r0, [r5, r2]
484 add r2, r2, #4
485 cmp r2, #16
486 bne cont1a
487 sub r1, r1, #1
488 cmp r1, #0
489 bgt delay1a
490
491continue_dll_off_1:
492 /* set DVFS - enter self refresh mode */
493 ldr r0, [r5, #MMDC0_MAPSR]
494 orr r0, r0, #(1 << 21)
495 str r0, [r5, #MMDC0_MAPSR]
496
497 /* de-assert con_req */
498 mov r0, #0x0
499 str r0, [r5, #MMDC0_MDSCR]
500
501poll_dvfs_set_1:
502 ldr r0, [r5, #MMDC0_MAPSR]
503 and r0, r0, #(1 << 25)
504 cmp r0, #(1 << 25)
505 bne poll_dvfs_set_1
506
507 ldr r1, =24000000
508 cmp r4, r1
509 beq switch_freq_24
510
511 switch_to_50MHz
512 b continue_dll_off_2
513
514switch_freq_24:
515 switch_to_24MHz
516
517continue_dll_off_2:
518
519 /* set SBS - block ddr accesses */
520 ldr r0, [r5, #MMDC0_MADPCR0]
521 orr r0, r0, #(1 << 8)
522 str r0, [r5, #MMDC0_MADPCR0]
523
524 /* clear DVFS - exit from self refresh mode */
525 ldr r0, [r5, #MMDC0_MAPSR]
526 bic r0, r0, #(1 << 21)
527 str r0, [r5, #MMDC0_MAPSR]
528
529poll_dvfs_clear_1:
530 ldr r0, [r5, #MMDC0_MAPSR]
531 and r0, r0, #(1 << 25)
532 cmp r0, #(1 << 25)
533 beq poll_dvfs_clear_1
534
535 /* if DLL was previously on, continue DLL off routine. */
536 cmp r9, #1
537 beq continue_dll_off_3
538
539 ldr r0, =0x00018031
540 str r0, [r5, #MMDC0_MDSCR]
541
542 ldr r0, =0x00018039
543 str r0, [r5, #MMDC0_MDSCR]
544
545 ldr r0, =0x08208030
546 str r0, [r5, #MMDC0_MDSCR]
547
548 ldr r0, =0x08208038
549 str r0, [r5, #MMDC0_MDSCR]
550
551 ldr r0, =0x00088032
552 str r0, [r5, #MMDC0_MDSCR]
553
554 ldr r0, =0x0008803A
555 str r0, [r5, #MMDC0_MDSCR]
556
557 /* delay for a while. */
558 ldr r1, =4
559delay_1:
560 ldr r2, =0
561cont_1:
562 ldr r0, [r5, r2]
563 add r2, r2, #4
564 cmp r2, #16
565 bne cont_1
566 sub r1, r1, #1
567 cmp r1, #0
568 bgt delay_1
569
570 ldr r0, [r5, #MMDC0_MDCF0]
571 bic r0, r0, #0xf
572 orr r0, r0, #0x3
573 str r0, [r5, #MMDC0_MDCF0]
574
575 ldr r0, [r5, #MMDC0_MDCF1]
576 bic r0, r0, #0x7
577 orr r0, r0, #0x4
578 str r0, [r5, #MMDC0_MDCF1]
579
580 ldr r0, =0x00091680
581 str r0, [r5, #MMDC0_MDMISC]
582
583 /* enable dqs pull down in the IOMUX. */
584 ldr r1, [r11]
585 add r11, r11, #8
586 ldr r2, =0x3028
587update_iomux:
588 ldr r0, [r11, #0x0]
589 ldr r3, [r7, r0]
590 bic r3, r3, r2
591 orr r3, r3, #(0x3 << 12)
592 orr r3, r3, #0x28
593 str r3, [r7, r0]
594 add r11, r11, #8
595 sub r1, r1, #1
596 cmp r1, #0
597 bgt update_iomux
598
599 /* ODT disabled. */
600 ldr r0, =0x0
601 ldr r2, =MMDC0_MPODTCTRL
602 str r0, [r5, r2]
603 ldr r2, =MMDC1_MPODTCTRL
604 str r0, [r5, r2]
605
606 /* DQS gating disabled. */
607 ldr r2, =MMDC0_MPDGCTRL0
608 ldr r0, [r5, r2]
609 orr r0, r0, #(1 << 29)
610 str r0, [r5, r2]
611
612 ldr r2, =MMDC1_MPDGCTRL0
613 ldr r0, [r5, r2]
614 orr r0, r0, #(0x1 << 29)
615 str r0, [r5, r2]
616
617 /* MMDC0_MAPSR adopt power down enable. */
618 ldr r0, [r5, #MMDC0_MAPSR]
619 bic r0, r0, #0x01
620 str r0, [r5, #MMDC0_MAPSR]
621
622 /* frc_msr + mu bypass */
623 ldr r0, =0x00000060
624 str r0, [r5, #MMDC0_MPMUR0]
625 ldr r2, =MMDC1_MPMUR0
626 str r0, [r5, r2]
627 ldr r0, =0x00000460
628 str r0, [r5, #MMDC0_MPMUR0]
629 ldr r2, =MMDC1_MPMUR0
630 str r0, [r5, r2]
631 ldr r0, =0x00000c60
632 str r0, [r5, #MMDC0_MPMUR0]
633 ldr r2, =MMDC1_MPMUR0
634 str r0, [r5, r2]
635
636continue_dll_off_3:
637 /* clear SBS - unblock accesses to DDR. */
638 ldr r0, [r5, #MMDC0_MADPCR0]
639 bic r0, r0, #(0x1 << 8)
640 str r0, [r5, #MMDC0_MADPCR0]
641
642 mov r0, #0x0
643 str r0, [r5, #MMDC0_MDSCR]
644poll_conreq_clear_1:
645 ldr r0, [r5, #MMDC0_MDSCR]
646 and r0, r0, #(0x4 << 12)
647 cmp r0, #(0x4 << 12)
648 beq poll_conreq_clear_1
649
650 b done
651
652dll_on_mode:
653 /* assert DVFS - enter self refresh mode. */
654 ldr r0, [r5, #MMDC0_MAPSR]
655 orr r0, r0, #(1 << 21)
656 str r0, [r5, #MMDC0_MAPSR]
657
658 /* de-assert CON_REQ. */
659 mov r0, #0x0
660 str r0, [r5, #MMDC0_MDSCR]
661
662 /* poll DVFS ack. */
663poll_dvfs_set_2:
664 ldr r0, [r5, #MMDC0_MAPSR]
665 and r0, r0, #(1 << 25)
666 cmp r0, #(1 << 25)
667 bne poll_dvfs_set_2
668
669 ldr r1, =528000000
670 cmp r4, r1
671 beq switch_freq_528
672
673 switch_to_400MHz
674
675 b continue_dll_on
676
677switch_freq_528:
678 switch_to_528MHz
679
680continue_dll_on:
681
682 /* set SBS step-by-step mode. */
683 ldr r0, [r5, #MMDC0_MADPCR0]
684 orr r0, r0, #( 1 << 8)
685 str r0, [r5, #MMDC0_MADPCR0]
686
687 /* clear DVFS - exit self refresh mode. */
688 ldr r0, [r5, #MMDC0_MAPSR]
689 bic r0, r0, #(1 << 21)
690 str r0, [r5, #MMDC0_MAPSR]
691
692poll_dvfs_clear_2:
693 ldr r0, [r5, #MMDC0_MAPSR]
694 and r0, r0, #(1 << 25)
695 cmp r0, #(1 << 25)
696 beq poll_dvfs_clear_2
697
698 /* if DLL is currently off, turn it back on. */
699 cmp r9, #0
700 beq update_calibration_only
701
702 ldr r0, =0xa5390003
703 str r0, [r5, #MMDC0_MPZQHWCTRL]
704 ldr r2, =MMDC1_MPZQHWCTRL
705 str r0, [r5, r2]
706
707 /* enable DQS gating. */
708 ldr r2, =MMDC0_MPDGCTRL0
709 ldr r0, [r5, r2]
710 bic r0, r0, #(1 << 29)
711 str r0, [r5, r2]
712
713 ldr r2, =MMDC1_MPDGCTRL0
714 ldr r0, [r5, r2]
715 bic r0, r0, #(1 << 29)
716 str r0, [r5, r2]
717
718 /* force measure. */
719 ldr r0, =0x00000800
720 str r0, [r5, #MMDC0_MPMUR0]
721 ldr r2, =MMDC1_MPMUR0
722 str r0, [r5, r2]
723
724 /* delay for while. */
725 ldr r1, =4
726delay5:
727 ldr r2, =0
728cont5:
729 ldr r0, [r5, r2]
730 add r2, r2, #4
731 cmp r2, #16
732 bne cont5
733 sub r1, r1, #1
734 cmp r1, #0
735 bgt delay5
736
737 /* disable dqs pull down in the IOMUX. */
738 ldr r1, [r11]
739 add r11, r11, #8
740update_iomux1:
741 ldr r0, [r11, #0x0]
742 ldr r3, [r11, #0x4]
743 str r3, [r7, r0]
744 add r11, r11, #8
745 sub r1, r1, #1
746 cmp r1, #0
747 bgt update_iomux1
748
749 /* config MMDC timings to 528MHz. */
750 ldr r9, [r8]
751 add r8, r8, #8
752 ldr r0, [r8, #0x0]
753 ldr r3, [r8, #0x4]
754 str r3, [r5, r0]
755 add r8, r8, #8
756
757 ldr r0, [r8, #0x0]
758 ldr r3, [r8, #0x4]
759 str r3, [r5, r0]
760 add r8, r8, #8
761
762 /* update MISC register: WALAT, RALAT */
763 ldr r0, =0x00081740
764 str r0, [r5, #MMDC0_MDMISC]
765
766 /* configure ddr devices to dll on, odt. */
767 ldr r0, =0x00028031
768 str r0, [r5, #MMDC0_MDSCR]
769
770 ldr r0, =0x00028039
771 str r0, [r5, #MMDC0_MDSCR]
772
773 /* delay for while. */
774 ldr r1, =4
775delay7:
776 ldr r2, =0
777cont7:
778 ldr r0, [r5, r2]
779 add r2, r2, #4
780 cmp r2, #16
781 bne cont7
782 sub r1, r1, #1
783 cmp r1, #0
784 bgt delay7
785
786 /* reset dll. */
787 ldr r0, =0x09208030
788 str r0, [r5, #MMDC0_MDSCR]
789
790 ldr r0, =0x09208038
791 str r0, [r5, #MMDC0_MDSCR]
792
793 /* delay for while. */
794 ldr r1, =100
795delay8:
796 ldr r2, =0
797cont8:
798 ldr r0, [r5, r2]
799 add r2, r2, #4
800 cmp r2, #16
801 bne cont8
802 sub r1, r1, #1
803 cmp r1, #0
804 bgt delay8
805
806 ldr r0, [r8, #0x0]
807 ldr r3, [r8, #0x4]
808 str r3, [r5, r0]
809 add r8, r8, #8
810
811 ldr r0, [r8, #0x0]
812 ldr r3, [r8, #0x4]
813 str r3, [r5, r0]
814 add r8, r8, #8
815
816 ldr r0, =0x00428031
817 str r0, [r5, #MMDC0_MDSCR]
818
819 ldr r0, =0x00428039
820 str r0, [r5, #MMDC0_MDSCR]
821
822 ldr r0, [r8, #0x0]
823 ldr r3, [r8, #0x4]
824 str r3, [r5, r0]
825 add r8, r8, #8
826
827 ldr r0, [r8, #0x0]
828 ldr r3, [r8, #0x4]
829 str r3, [r5, r0]
830 add r8, r8, #8
831
832 /* issue a zq command. */
833 ldr r0, =0x04008040
834 str r0, [r5, #MMDC0_MDSCR]
835
836 ldr r0, =0x04008048
837 str r0, [r5, #MMDC0_MDSCR]
838
839 /* MMDC ODT enable. */
840 ldr r0, [r8, #0x0]
841 ldr r3, [r8, #0x4]
842 str r3, [r5, r0]
843 add r8, r8, #8
844
845 ldr r2, =0x4818
846 str r0, [r5, r2]
847
848 /* delay for while. */
849 ldr r1, =40
850delay15:
851 ldr r2, =0
852cont15:
853 ldr r0, [r5, r2]
854 add r2, r2, #4
855 cmp r2, #16
856 bne cont15
857 sub r1, r1, #1
858 cmp r1, #0
859 bgt delay15
860
861 /* MMDC0_MAPSR adopt power down enable. */
862 ldr r0, [r5, #MMDC0_MAPSR]
863 bic r0, r0, #0x01
864 str r0, [r5, #MMDC0_MAPSR]
865
866 /* enable MMDC power down timer. */
867 ldr r0, [r5, #MMDC0_MDPDC]
868 orr r0, r0, #(0x55 << 8)
869 str r0, [r5, #MMDC0_MDPDC]
870
871 b update_calibration
872
873update_calibration_only:
874 ldr r1, [r8]
875 sub r1, r1, #7
876 add r8, r8, #64
877 b update_calib
878
879update_calibration:
880 /* write the new calibration values. */
881 mov r1, r9
882 sub r1, r1, #7
883
884update_calib:
885 ldr r0, [r8, #0x0]
886 ldr r3, [r8, #0x4]
887 str r3, [r5, r0]
888 add r8, r8, #8
889 sub r1, r1, #1
890 cmp r1, #0
891 bgt update_calib
892
893 /* perform a force measurement. */
894 ldr r0, =0x800
895 str r0, [r5, #MMDC0_MPMUR0]
896 ldr r2, =MMDC1_MPMUR0
897 str r0, [r5, r2]
898
899 /* clear SBS - unblock DDR accesses. */
900 ldr r0, [r5, #MMDC0_MADPCR0]
901 bic r0, r0, #(1 << 8)
902 str r0, [r5, #MMDC0_MADPCR0]
903
904 mov r0, #0x0
905 str r0, [r5, #MMDC0_MDSCR]
906poll_conreq_clear_2:
907 ldr r0, [r5, #MMDC0_MDSCR]
908 and r0, r0, #(0x4 << 12)
909 cmp r0, #(0x4 << 12)
910 beq poll_conreq_clear_2
911
912done:
913 /* restore registers */
914
915 ldmfd sp!, {r4-r12}
916 mov pc, lr
917
918 .type mx6_do_ddr3_freq_change, #object
919ENTRY(mx6_do_ddr_freq_change)
920 .word mx6_ddr3_freq_change
921 .size mx6_ddr3_freq_change, . - mx6_ddr3_freq_change
diff --git a/include/linux/busfreq-imx6.h b/include/linux/busfreq-imx6.h
new file mode 100644
index 000000000000..18eb0f874a7a
--- /dev/null
+++ b/include/linux/busfreq-imx6.h
@@ -0,0 +1,23 @@
1/*
2 * Copyright 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 version 2 as
6 * published by the Free Software Foundation.
7 */
8
9#ifndef __ASM_ARCH_MXC_BUSFREQ_H__
10#define __ASM_ARCH_MXC_BUSFREQ_H__
11
12/*
13 * This enumerates busfreq mode.
14 */
15enum bus_freq_mode {
16 BUS_FREQ_HIGH,
17 BUS_FREQ_MED,
18 BUS_FREQ_AUDIO,
19 BUS_FREQ_LOW,
20};
21void request_bus_freq(enum bus_freq_mode mode);
22void release_bus_freq(enum bus_freq_mode mode);
23#endif