diff options
author | Ranjani Vaidyanathan <ra5478@freescale.com> | 2013-08-20 15:30:16 -0400 |
---|---|---|
committer | Nitin Garg <nitin.garg@freescale.com> | 2014-04-16 09:01:25 -0400 |
commit | 7091e3d1c771eb34cc2fdd683e43b13949085b83 (patch) | |
tree | 11b0b82ee164d3496dfc69e3130e45d4ed1657cf /arch/arm/mach-imx/busfreq-imx6.c | |
parent | 39f7fd7077dbe19579e124dc34fca6a8ac08435f (diff) |
ENGR00275974-1 [iMX6DQ/iMX6DL] Add busfreq support
Add support to drop DDR and AHB frequency to 24MHz in
system IDLE state.
Signed-off-by: Ranjani Vaidyanathan <ra5478@freescale.com>
Diffstat (limited to 'arch/arm/mach-imx/busfreq-imx6.c')
-rw-r--r-- | arch/arm/mach-imx/busfreq-imx6.c | 599 |
1 files changed, 599 insertions, 0 deletions
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"); | ||