diff options
-rw-r--r-- | Documentation/devicetree/bindings/arm/imx/busfreq-imx6.txt | 64 | ||||
-rw-r--r-- | arch/arm/mach-imx/Makefile | 6 | ||||
-rw-r--r-- | arch/arm/mach-imx/busfreq-imx6.c | 599 | ||||
-rw-r--r-- | arch/arm/mach-imx/busfreq_ddr3.c | 466 | ||||
-rw-r--r-- | arch/arm/mach-imx/ddr3_freq_imx6.S | 921 | ||||
-rw-r--r-- | include/linux/busfreq-imx6.h | 23 |
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 @@ | |||
1 | Freescale Busfreq driver | ||
2 | |||
3 | It is a generic driver that manages the frequency of the DDR, AHB and AXI buses in the iMX6x architecture. | ||
4 | It works for both SMP and UP systems and for both DDR3 and LPDDR2 memory types. | ||
5 | |||
6 | Required 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 | |||
12 | Examples: | ||
13 | For 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 | |||
25 | The Freescale Busfreq driver supports the following setpoints for the DDR freq: | ||
26 | enum 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 | |||
33 | Currently the Freescale Busfreq driver implementation requires drivers to call the following APIs: | ||
34 | 1. 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 | |||
38 | 2. 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 | |||
42 | Examples: | ||
43 | In the IPU driver, the requesting and releasing of the required bus frequency is tied into the runtime PM implementation: | ||
44 | |||
45 | int 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 | |||
53 | int 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 | |||
61 | static 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 | |||
104 | AFLAGS_suspend-imx6.o :=-Wa,-march=armv7-a | 104 | AFLAGS_suspend-imx6.o :=-Wa,-march=armv7-a |
105 | obj-$(CONFIG_PM) += pm-imx6.o headsmp.o suspend-imx6.o | 105 | obj-$(CONFIG_PM) += pm-imx6.o headsmp.o suspend-imx6.o |
106 | 106 | ||
107 | ifeq ($(CONFIG_ARM_IMX6Q_CPUFREQ),y) | ||
108 | obj-y += busfreq-imx6.o | ||
109 | obj-$(CONFIG_SOC_IMX6Q) += ddr3_freq_imx6.o busfreq_ddr3.o | ||
110 | endif | ||
111 | |||
112 | |||
107 | # i.MX5 based machines | 113 | # i.MX5 based machines |
108 | obj-$(CONFIG_MACH_MX51_BABBAGE) += mach-mx51_babbage.o | 114 | obj-$(CONFIG_MACH_MX51_BABBAGE) += mach-mx51_babbage.o |
109 | obj-$(CONFIG_MACH_EUKREA_CPUIMX51SD) += mach-cpuimx51sd.o | 115 | obj-$(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 | |||
52 | int high_bus_freq_mode; | ||
53 | int med_bus_freq_mode; | ||
54 | int audio_bus_freq_mode; | ||
55 | int low_bus_freq_mode; | ||
56 | |||
57 | static int bus_freq_scaling_initialized; | ||
58 | static struct device *busfreq_dev; | ||
59 | static int busfreq_suspended; | ||
60 | |||
61 | static int bus_freq_scaling_is_active; | ||
62 | static int high_bus_count, med_bus_count, audio_bus_count; | ||
63 | static unsigned int ddr_low_rate; | ||
64 | unsigned int ddr_med_rate; | ||
65 | unsigned int ddr_normal_rate; | ||
66 | |||
67 | extern int init_mmdc_settings(struct platform_device *dev); | ||
68 | extern int update_ddr_freq(int ddr_rate); | ||
69 | |||
70 | DEFINE_MUTEX(bus_freq_mutex); | ||
71 | |||
72 | static struct clk *pll2_400; | ||
73 | static struct clk *periph_clk; | ||
74 | static struct clk *periph_pre_clk; | ||
75 | static struct clk *periph_clk2_sel; | ||
76 | static struct clk *periph_clk2; | ||
77 | static struct clk *osc_clk; | ||
78 | static struct clk *cpu_clk; | ||
79 | static struct clk *pll3; | ||
80 | static struct clk *pll2; | ||
81 | static struct clk *pll2_200; | ||
82 | |||
83 | static struct delayed_work low_bus_freq_handler; | ||
84 | static struct delayed_work bus_freq_daemon; | ||
85 | |||
86 | int low_bus_freq; | ||
87 | |||
88 | int 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 | |||
146 | static 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 | */ | ||
160 | int 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 | */ | ||
183 | int 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 | |||
266 | void 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 | } | ||
305 | EXPORT_SYMBOL(request_bus_freq); | ||
306 | |||
307 | void 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 | } | ||
356 | EXPORT_SYMBOL(release_bus_freq); | ||
357 | |||
358 | static 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 | |||
367 | static 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 | |||
376 | static 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 | |||
399 | static 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 | |||
420 | static struct notifier_block imx_bus_freq_pm_notifier = { | ||
421 | .notifier_call = bus_freq_pm_notify, | ||
422 | }; | ||
423 | |||
424 | static 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 | |||
436 | static 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 | |||
555 | static const struct of_device_id imx6_busfreq_ids[] = { | ||
556 | { .compatible = "fsl,imx6_busfreq", }, | ||
557 | { /* sentinel */ } | ||
558 | }; | ||
559 | |||
560 | static 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 | |||
575 | static 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 | |||
585 | static 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 | |||
594 | module_init(busfreq_init); | ||
595 | module_exit(busfreq_cleanup); | ||
596 | |||
597 | MODULE_AUTHOR("Freescale Semiconductor, Inc."); | ||
598 | MODULE_DESCRIPTION("BusFreq driver"); | ||
599 | MODULE_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 */ | ||
46 | static unsigned long (*iram_ddr_settings)[2]; | ||
47 | static unsigned long (*normal_mmdc_settings)[2]; | ||
48 | static unsigned long (*iram_iomux_settings)[2]; | ||
49 | static void __iomem *mmdc_base; | ||
50 | static void __iomem *iomux_base; | ||
51 | static void __iomem *ccm_base; | ||
52 | static void __iomem *l2_base; | ||
53 | static void __iomem *gic_dist_base; | ||
54 | static u32 *irqs_used; | ||
55 | |||
56 | void (*mx6_change_ddr_freq)(u32 freq, void *ddr_settings, | ||
57 | bool dll_mode, void *iomux_offsets) = NULL; | ||
58 | |||
59 | extern unsigned int ddr_med_rate; | ||
60 | extern unsigned int ddr_normal_rate; | ||
61 | extern int low_bus_freq_mode; | ||
62 | extern int audio_bus_freq_mode; | ||
63 | extern void mx6_ddr3_freq_change(u32 freq, void *ddr_settings, | ||
64 | bool dll_mode, void *iomux_offsets); | ||
65 | |||
66 | static void *ddr_freq_change_iram_base; | ||
67 | static int ddr_settings_size; | ||
68 | static int iomux_settings_size; | ||
69 | static volatile unsigned int cpus_in_wfe; | ||
70 | static volatile bool wait_for_ddr_freq_update; | ||
71 | static 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 | |||
77 | unsigned 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 | |||
87 | unsigned 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 | |||
98 | unsigned 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 | |||
108 | unsigned 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 | |||
119 | unsigned 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 | |||
130 | unsigned 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 | |||
141 | int 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 | */ | ||
152 | irqreturn_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. */ | ||
167 | int 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 | |||
252 | int 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 | |||
76 | wait_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 | |||
86 | periph_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 | |||
93 | set_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 | |||
105 | wait_div_update528_1: | ||
106 | ldr r0, [r6, #CCM_CDHIPR] | ||
107 | cmp r0, #0 | ||
108 | bne wait_div_update528_1 | ||
109 | |||
110 | switch_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 | |||
122 | periph_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 | |||
156 | periph_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 | |||
163 | set_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 | |||
175 | wait_div_update400_1: | ||
176 | ldr r0, [r6, #CCM_CDHIPR] | ||
177 | cmp r0, #0 | ||
178 | bne wait_div_update400_1 | ||
179 | |||
180 | switch_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 | |||
193 | periph_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 | |||
211 | wait_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 | |||
250 | periph_clk_switch_50: | ||
251 | ldr r0, [r6, #CCM_CDHIPR] | ||
252 | cmp r0, #0 | ||
253 | bne periph_clk_switch_50 | ||
254 | |||
255 | switch_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 | |||
279 | wait_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 | |||
289 | periph_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 | |||
324 | periph_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. */ | ||
337 | wait_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 | */ | ||
357 | ENTRY(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 | |||
395 | ddr_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 | ||
434 | delay1: | ||
435 | ldr r2, =0 | ||
436 | cont1: | ||
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] | ||
448 | poll_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 | |||
467 | dll_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 | ||
480 | delay1a: | ||
481 | ldr r2, =0 | ||
482 | cont1a: | ||
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 | |||
491 | continue_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 | |||
501 | poll_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 | |||
514 | switch_freq_24: | ||
515 | switch_to_24MHz | ||
516 | |||
517 | continue_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 | |||
529 | poll_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 | ||
559 | delay_1: | ||
560 | ldr r2, =0 | ||
561 | cont_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 | ||
587 | update_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 | |||
636 | continue_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] | ||
644 | poll_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 | |||
652 | dll_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. */ | ||
663 | poll_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 | |||
677 | switch_freq_528: | ||
678 | switch_to_528MHz | ||
679 | |||
680 | continue_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 | |||
692 | poll_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 | ||
726 | delay5: | ||
727 | ldr r2, =0 | ||
728 | cont5: | ||
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 | ||
740 | update_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 | ||
775 | delay7: | ||
776 | ldr r2, =0 | ||
777 | cont7: | ||
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 | ||
795 | delay8: | ||
796 | ldr r2, =0 | ||
797 | cont8: | ||
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 | ||
850 | delay15: | ||
851 | ldr r2, =0 | ||
852 | cont15: | ||
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 | |||
873 | update_calibration_only: | ||
874 | ldr r1, [r8] | ||
875 | sub r1, r1, #7 | ||
876 | add r8, r8, #64 | ||
877 | b update_calib | ||
878 | |||
879 | update_calibration: | ||
880 | /* write the new calibration values. */ | ||
881 | mov r1, r9 | ||
882 | sub r1, r1, #7 | ||
883 | |||
884 | update_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] | ||
906 | poll_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 | |||
912 | done: | ||
913 | /* restore registers */ | ||
914 | |||
915 | ldmfd sp!, {r4-r12} | ||
916 | mov pc, lr | ||
917 | |||
918 | .type mx6_do_ddr3_freq_change, #object | ||
919 | ENTRY(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 | */ | ||
15 | enum bus_freq_mode { | ||
16 | BUS_FREQ_HIGH, | ||
17 | BUS_FREQ_MED, | ||
18 | BUS_FREQ_AUDIO, | ||
19 | BUS_FREQ_LOW, | ||
20 | }; | ||
21 | void request_bus_freq(enum bus_freq_mode mode); | ||
22 | void release_bus_freq(enum bus_freq_mode mode); | ||
23 | #endif | ||