aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-imx/busfreq-imx6.c
diff options
context:
space:
mode:
authorRanjani Vaidyanathan <ra5478@freescale.com>2013-08-20 15:30:16 -0400
committerNitin Garg <nitin.garg@freescale.com>2014-04-16 09:01:25 -0400
commit7091e3d1c771eb34cc2fdd683e43b13949085b83 (patch)
tree11b0b82ee164d3496dfc69e3130e45d4ed1657cf /arch/arm/mach-imx/busfreq-imx6.c
parent39f7fd7077dbe19579e124dc34fca6a8ac08435f (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.c599
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
52int high_bus_freq_mode;
53int med_bus_freq_mode;
54int audio_bus_freq_mode;
55int low_bus_freq_mode;
56
57static int bus_freq_scaling_initialized;
58static struct device *busfreq_dev;
59static int busfreq_suspended;
60
61static int bus_freq_scaling_is_active;
62static int high_bus_count, med_bus_count, audio_bus_count;
63static unsigned int ddr_low_rate;
64unsigned int ddr_med_rate;
65unsigned int ddr_normal_rate;
66
67extern int init_mmdc_settings(struct platform_device *dev);
68extern int update_ddr_freq(int ddr_rate);
69
70DEFINE_MUTEX(bus_freq_mutex);
71
72static struct clk *pll2_400;
73static struct clk *periph_clk;
74static struct clk *periph_pre_clk;
75static struct clk *periph_clk2_sel;
76static struct clk *periph_clk2;
77static struct clk *osc_clk;
78static struct clk *cpu_clk;
79static struct clk *pll3;
80static struct clk *pll2;
81static struct clk *pll2_200;
82
83static struct delayed_work low_bus_freq_handler;
84static struct delayed_work bus_freq_daemon;
85
86int low_bus_freq;
87
88int reduce_bus_freq(void)
89{
90 int ret = 0;
91 clk_prepare_enable(pll3);
92 if (low_bus_freq) {
93 /* Need to ensure that PLL2_PFD_400M is kept ON. */
94 clk_prepare_enable(pll2_400);
95 update_ddr_freq(DDR_AUDIO_CLK);
96 /* Make sure periph clk's parent also got updated */
97 ret = clk_set_parent(periph_clk2_sel, pll3);
98 if (ret)
99 dev_WARN(busfreq_dev, "%s: %d: clk set parent fail!\n",
100 __func__, __LINE__);
101 ret = clk_set_parent(periph_pre_clk, pll2_200);
102 if (ret)
103 dev_WARN(busfreq_dev, "%s: %d: clk set parent fail!\n",
104 __func__, __LINE__);
105 ret = clk_set_parent(periph_clk, periph_pre_clk);
106 if (ret)
107 dev_WARN(busfreq_dev, "%s: %d: clk set parent fail!\n",
108 __func__, __LINE__);
109 audio_bus_freq_mode = 1;
110 low_bus_freq_mode = 0;
111 } else {
112 update_ddr_freq(LPAPM_CLK);
113 /* Make sure periph clk's parent also got updated */
114 ret = clk_set_parent(periph_clk2_sel, osc_clk);
115 if (ret)
116 dev_WARN(busfreq_dev, "%s: %d: clk set parent fail!\n",
117 __func__, __LINE__);
118 ret = clk_set_parent(periph_clk, periph_clk2);
119 if (ret)
120 dev_WARN(busfreq_dev, "%s: %d: clk set parent fail!\n",
121 __func__, __LINE__);
122 if (audio_bus_freq_mode)
123 clk_disable_unprepare(pll2_400);
124 low_bus_freq_mode = 1;
125 audio_bus_freq_mode = 0;
126 }
127 if (high_bus_freq_mode && cpu_is_imx6dl())
128 clk_disable_unprepare(pll2_400);
129
130 clk_disable_unprepare(pll3);
131 med_bus_freq_mode = 0;
132 high_bus_freq_mode = 0;
133
134 if (audio_bus_freq_mode)
135 dev_dbg(busfreq_dev, "Bus freq set to audio mode. Count:\
136 high %d, med %d, audio %d\n",
137 high_bus_count, med_bus_count, audio_bus_count);
138 if (low_bus_freq_mode)
139 dev_dbg(busfreq_dev, "Bus freq set to low mode. Count:\
140 high %d, med %d, audio %d\n",
141 high_bus_count, med_bus_count, audio_bus_count);
142
143 return ret;
144}
145
146static void reduce_bus_freq_handler(struct work_struct *work)
147{
148 mutex_lock(&bus_freq_mutex);
149
150 reduce_bus_freq();
151
152 mutex_unlock(&bus_freq_mutex);
153}
154
155/*
156 * Set the DDR, AHB to 24MHz.
157 * This mode will be activated only when none of the modules that
158 * need a higher DDR or AHB frequency are active.
159 */
160int set_low_bus_freq(int low_bus_mode)
161{
162 if (busfreq_suspended)
163 return 0;
164
165 if (!bus_freq_scaling_initialized || !bus_freq_scaling_is_active)
166 return 0;
167
168 /*
169 * Don't lower the frequency immediately. Instead
170 * scheduled a delayed work and drop the freq if
171 * the conditions still remain the same.
172 */
173 low_bus_freq = low_bus_mode;
174 schedule_delayed_work(&low_bus_freq_handler,
175 usecs_to_jiffies(3000000));
176 return 0;
177}
178
179/*
180 * Set the DDR to either 528MHz or 400MHz for iMX6qd
181 * or 400MHz for iMX6dl.
182 */
183int set_high_bus_freq(int high_bus_freq)
184{
185 int ret = 0;
186
187 if (bus_freq_scaling_initialized && bus_freq_scaling_is_active)
188 cancel_delayed_work_sync(&low_bus_freq_handler);
189
190 if (busfreq_suspended)
191 return 0;
192
193 /* for high setpoint, i.MX6Q is 528MHz, i.MX6DL is 400MHz */
194 if (cpu_is_imx6q())
195 high_bus_freq = 1;
196 else
197 high_bus_freq = 0;
198
199 if (!bus_freq_scaling_initialized || !bus_freq_scaling_is_active)
200 return 0;
201
202 if (high_bus_freq_mode)
203 return 0;
204
205 /* medium bus freq is only supported for MX6DQ */
206 if (med_bus_freq_mode && !high_bus_freq)
207 return 0;
208
209 clk_prepare_enable(pll3);
210 if (high_bus_freq) {
211 update_ddr_freq(ddr_normal_rate);
212 /* Make sure periph clk's parent also got updated */
213 ret = clk_set_parent(periph_clk2_sel, pll3);
214 if (ret)
215 dev_WARN(busfreq_dev, "%s: %d: clk set parent fail!\n",
216 __func__, __LINE__);
217 ret = clk_set_parent(periph_pre_clk, pll2);
218 if (ret)
219 dev_WARN(busfreq_dev, "%s: %d: clk set parent fail!\n",
220 __func__, __LINE__);
221 ret = clk_set_parent(periph_clk, periph_pre_clk);
222 if (ret)
223 dev_WARN(busfreq_dev, "%s: %d: clk set parent fail!\n",
224 __func__, __LINE__);
225 if (med_bus_freq_mode)
226 clk_disable_unprepare(pll2_400);
227 } else {
228 clk_prepare_enable(pll2_400);
229 update_ddr_freq(ddr_med_rate);
230 /* Make sure periph clk's parent also got updated */
231 ret = clk_set_parent(periph_clk2_sel, pll3);
232 if (ret)
233 dev_WARN(busfreq_dev, "%s: %d: clk set parent fail!\n",
234 __func__, __LINE__);
235 ret = clk_set_parent(periph_pre_clk, pll2_400);
236 if (ret)
237 dev_WARN(busfreq_dev, "%s: %d: clk set parent fail!\n",
238 __func__, __LINE__);
239 ret = clk_set_parent(periph_clk, periph_pre_clk);
240 if (ret)
241 dev_WARN(busfreq_dev, "%s: %d: clk set parent fail!\n",
242 __func__, __LINE__);
243 }
244 if (audio_bus_freq_mode)
245 clk_disable_unprepare(pll2_400);
246
247 high_bus_freq_mode = 1;
248 med_bus_freq_mode = 0;
249 low_bus_freq_mode = 0;
250 audio_bus_freq_mode = 0;
251
252 clk_disable_unprepare(pll3);
253
254 if (high_bus_freq_mode)
255 dev_dbg(busfreq_dev, "Bus freq set to high mode. Count:\
256 high %d, med %d, audio %d\n",
257 high_bus_count, med_bus_count, audio_bus_count);
258 if (med_bus_freq_mode)
259 dev_dbg(busfreq_dev, "Bus freq set to med mode. Count:\
260 high %d, med %d, audio %d\n",
261 high_bus_count, med_bus_count, audio_bus_count);
262
263 return 0;
264}
265
266void request_bus_freq(enum bus_freq_mode mode)
267{
268 mutex_lock(&bus_freq_mutex);
269
270 if (mode == BUS_FREQ_HIGH)
271 high_bus_count++;
272 else if (mode == BUS_FREQ_MED)
273 med_bus_count++;
274 else if (mode == BUS_FREQ_AUDIO)
275 audio_bus_count++;
276
277 if (busfreq_suspended || !bus_freq_scaling_initialized ||
278 !bus_freq_scaling_is_active) {
279 mutex_unlock(&bus_freq_mutex);
280 return;
281 }
282
283 cancel_delayed_work_sync(&low_bus_freq_handler);
284 if ((mode == BUS_FREQ_HIGH) && (!high_bus_freq_mode)) {
285 set_high_bus_freq(1);
286 mutex_unlock(&bus_freq_mutex);
287 return;
288 }
289
290 if ((mode == BUS_FREQ_MED) && (!high_bus_freq_mode) &&
291 (!med_bus_freq_mode)) {
292 set_high_bus_freq(0);
293 mutex_unlock(&bus_freq_mutex);
294 return;
295 }
296 if ((mode == BUS_FREQ_AUDIO) && (!high_bus_freq_mode) &&
297 (!med_bus_freq_mode) && (!audio_bus_freq_mode)) {
298 set_low_bus_freq(1);
299 mutex_unlock(&bus_freq_mutex);
300 return;
301 }
302 mutex_unlock(&bus_freq_mutex);
303 return;
304}
305EXPORT_SYMBOL(request_bus_freq);
306
307void release_bus_freq(enum bus_freq_mode mode)
308{
309 mutex_lock(&bus_freq_mutex);
310
311 if (mode == BUS_FREQ_HIGH) {
312 if (high_bus_count == 0) {
313 dev_err(busfreq_dev, "high bus count mismatch!\n");
314 dump_stack();
315 mutex_unlock(&bus_freq_mutex);
316 return;
317 }
318 high_bus_count--;
319 } else if (mode == BUS_FREQ_MED) {
320 if (med_bus_count == 0) {
321 dev_err(busfreq_dev, "med bus count mismatch!\n");
322 dump_stack();
323 mutex_unlock(&bus_freq_mutex);
324 return;
325 }
326 med_bus_count--;
327 } else if (mode == BUS_FREQ_AUDIO) {
328 if (audio_bus_count == 0) {
329 dev_err(busfreq_dev, "audio bus count mismatch!\n");
330 dump_stack();
331 mutex_unlock(&bus_freq_mutex);
332 return;
333 }
334 audio_bus_count--;
335 }
336
337 if (busfreq_suspended || !bus_freq_scaling_initialized ||
338 !bus_freq_scaling_is_active) {
339 mutex_unlock(&bus_freq_mutex);
340 return;
341 }
342
343 if ((!audio_bus_freq_mode) && (high_bus_count == 0) &&
344 (med_bus_count == 0) && (audio_bus_count != 0)) {
345 set_low_bus_freq(1);
346 mutex_unlock(&bus_freq_mutex);
347 return;
348 }
349 if ((!low_bus_freq_mode) && (high_bus_count == 0) &&
350 (med_bus_count == 0) && (audio_bus_count == 0))
351 set_low_bus_freq(0);
352
353 mutex_unlock(&bus_freq_mutex);
354 return;
355}
356EXPORT_SYMBOL(release_bus_freq);
357
358static void bus_freq_daemon_handler(struct work_struct *work)
359{
360 mutex_lock(&bus_freq_mutex);
361 if ((!low_bus_freq_mode) && (high_bus_count == 0) &&
362 (med_bus_count == 0) && (audio_bus_count == 0))
363 set_low_bus_freq(0);
364 mutex_unlock(&bus_freq_mutex);
365}
366
367static ssize_t bus_freq_scaling_enable_show(struct device *dev,
368 struct device_attribute *attr, char *buf)
369{
370 if (bus_freq_scaling_is_active)
371 return sprintf(buf, "Bus frequency scaling is enabled\n");
372 else
373 return sprintf(buf, "Bus frequency scaling is disabled\n");
374}
375
376static ssize_t bus_freq_scaling_enable_store(struct device *dev,
377 struct device_attribute *attr,
378 const char *buf, size_t size)
379{
380 if (strncmp(buf, "1", 1) == 0) {
381 bus_freq_scaling_is_active = 1;
382 set_high_bus_freq(1);
383 /*
384 * We set bus freq to highest at the beginning,
385 * so we use this daemon thread to make sure system
386 * can enter low bus mode if
387 * there is no high bus request pending
388 */
389 schedule_delayed_work(&bus_freq_daemon,
390 usecs_to_jiffies(5000000));
391 } else if (strncmp(buf, "0", 1) == 0) {
392 if (bus_freq_scaling_is_active)
393 set_high_bus_freq(1);
394 bus_freq_scaling_is_active = 0;
395 }
396 return size;
397}
398
399static int bus_freq_pm_notify(struct notifier_block *nb, unsigned long event,
400 void *dummy)
401{
402 mutex_lock(&bus_freq_mutex);
403
404 if (event == PM_SUSPEND_PREPARE) {
405 high_bus_count++;
406 set_high_bus_freq(1);
407 busfreq_suspended = 1;
408 } else if (event == PM_POST_SUSPEND) {
409 busfreq_suspended = 0;
410 high_bus_count--;
411 schedule_delayed_work(&bus_freq_daemon,
412 usecs_to_jiffies(5000000));
413 }
414
415 mutex_unlock(&bus_freq_mutex);
416
417 return NOTIFY_OK;
418}
419
420static struct notifier_block imx_bus_freq_pm_notifier = {
421 .notifier_call = bus_freq_pm_notify,
422};
423
424static DEVICE_ATTR(enable, 0644, bus_freq_scaling_enable_show,
425 bus_freq_scaling_enable_store);
426
427/*!
428 * This is the probe routine for the bus frequency driver.
429 *
430 * @param pdev The platform device structure
431 *
432 * @return The function returns 0 on success
433 *
434 */
435
436static int busfreq_probe(struct platform_device *pdev)
437{
438 u32 err;
439
440 busfreq_dev = &pdev->dev;
441
442 pll2_400 = devm_clk_get(&pdev->dev, "pll2_pfd2_396m");
443 if (IS_ERR(pll2_400)) {
444 dev_err(busfreq_dev, "%s: failed to get pll2_pfd2_396m\n",
445 __func__);
446 return PTR_ERR(pll2_400);
447 }
448
449 pll2_200 = devm_clk_get(&pdev->dev, "pll2_198m");
450 if (IS_ERR(pll2_200)) {
451 dev_err(busfreq_dev, "%s: failed to get pll2_198m\n",
452 __func__);
453 return PTR_ERR(pll2_200);
454 }
455
456 pll2 = devm_clk_get(&pdev->dev, "pll2_bus");
457 if (IS_ERR(pll2)) {
458 dev_err(busfreq_dev, "%s: failed to get pll2_bus\n",
459 __func__);
460 return PTR_ERR(pll2);
461 }
462
463 cpu_clk = devm_clk_get(&pdev->dev, "arm");
464 if (IS_ERR(cpu_clk)) {
465 dev_err(busfreq_dev, "%s: failed to get cpu_clk\n",
466 __func__);
467 return PTR_ERR(cpu_clk);
468 }
469
470 pll3 = devm_clk_get(&pdev->dev, "pll3_usb_otg");
471 if (IS_ERR(pll3)) {
472 dev_err(busfreq_dev, "%s: failed to get pll3_usb_otg\n",
473 __func__);
474 return PTR_ERR(pll3);
475 }
476
477 periph_clk = devm_clk_get(&pdev->dev, "periph");
478 if (IS_ERR(periph_clk)) {
479 dev_err(busfreq_dev, "%s: failed to get periph\n",
480 __func__);
481 return PTR_ERR(periph_clk);
482 }
483
484 periph_pre_clk = devm_clk_get(&pdev->dev, "periph_pre");
485 if (IS_ERR(periph_pre_clk)) {
486 dev_err(busfreq_dev, "%s: failed to get periph_pre\n",
487 __func__);
488 return PTR_ERR(periph_pre_clk);
489 }
490
491 periph_clk2 = devm_clk_get(&pdev->dev, "periph_clk2");
492 if (IS_ERR(periph_clk2)) {
493 dev_err(busfreq_dev, "%s: failed to get periph_clk2\n",
494 __func__);
495 return PTR_ERR(periph_clk2);
496 }
497
498 periph_clk2_sel = devm_clk_get(&pdev->dev, "periph_clk2_sel");
499 if (IS_ERR(periph_clk2_sel)) {
500 dev_err(busfreq_dev, "%s: failed to get periph_clk2_sel\n",
501 __func__);
502 return PTR_ERR(periph_clk2_sel);
503 }
504
505 osc_clk = devm_clk_get(&pdev->dev, "osc");
506 if (IS_ERR(osc_clk)) {
507 dev_err(busfreq_dev, "%s: failed to get osc_clk\n",
508 __func__);
509 return PTR_ERR(osc_clk);
510 }
511
512 err = sysfs_create_file(&busfreq_dev->kobj, &dev_attr_enable.attr);
513 if (err) {
514 dev_err(busfreq_dev,
515 "Unable to register sysdev entry for BUSFREQ");
516 return err;
517 }
518
519 if (of_property_read_u32(pdev->dev.of_node, "fsl,max_ddr_freq",
520 &ddr_normal_rate)) {
521 dev_err(busfreq_dev, "max_ddr_freq entry missing\n");
522 return -EINVAL;
523 }
524
525 high_bus_freq_mode = 1;
526 med_bus_freq_mode = 0;
527 low_bus_freq_mode = 0;
528 audio_bus_freq_mode = 0;
529
530 bus_freq_scaling_is_active = 1;
531 bus_freq_scaling_initialized = 1;
532
533 ddr_low_rate = LPAPM_CLK;
534 if (cpu_is_imx6q()) {
535 if (of_property_read_u32(pdev->dev.of_node, "fsl,med_ddr_freq",
536 &ddr_med_rate)) {
537 dev_err(busfreq_dev,
538 "DDR medium rate not supported.\n");
539 ddr_med_rate = ddr_normal_rate;
540 }
541 }
542
543 INIT_DELAYED_WORK(&low_bus_freq_handler, reduce_bus_freq_handler);
544 INIT_DELAYED_WORK(&bus_freq_daemon, bus_freq_daemon_handler);
545 register_pm_notifier(&imx_bus_freq_pm_notifier);
546
547 err = init_mmdc_settings(pdev);
548 if (err) {
549 dev_err(busfreq_dev, "Busfreq init of MMDC failed\n");
550 return err;
551 }
552 return 0;
553}
554
555static const struct of_device_id imx6_busfreq_ids[] = {
556 { .compatible = "fsl,imx6_busfreq", },
557 { /* sentinel */ }
558};
559
560static struct platform_driver busfreq_driver = {
561 .driver = {
562 .name = "imx6_busfreq",
563 .owner = THIS_MODULE,
564 .of_match_table = imx6_busfreq_ids,
565 },
566 .probe = busfreq_probe,
567};
568
569/*!
570 * Initialise the busfreq_driver.
571 *
572 * @return The function always returns 0.
573 */
574
575static int __init busfreq_init(void)
576{
577 if (platform_driver_register(&busfreq_driver) != 0)
578 return -ENODEV;
579
580 printk(KERN_INFO "Bus freq driver module loaded\n");
581
582 return 0;
583}
584
585static void __exit busfreq_cleanup(void)
586{
587 sysfs_remove_file(&busfreq_dev->kobj, &dev_attr_enable.attr);
588
589 /* Unregister the device structure */
590 platform_driver_unregister(&busfreq_driver);
591 bus_freq_scaling_initialized = 0;
592}
593
594module_init(busfreq_init);
595module_exit(busfreq_cleanup);
596
597MODULE_AUTHOR("Freescale Semiconductor, Inc.");
598MODULE_DESCRIPTION("BusFreq driver");
599MODULE_LICENSE("GPL");