summaryrefslogtreecommitdiff
path: root/firmware/target/arm/imx31/dvfs_dptc-imx31.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/imx31/dvfs_dptc-imx31.c')
-rw-r--r--firmware/target/arm/imx31/dvfs_dptc-imx31.c712
1 files changed, 703 insertions, 9 deletions
diff --git a/firmware/target/arm/imx31/dvfs_dptc-imx31.c b/firmware/target/arm/imx31/dvfs_dptc-imx31.c
index 8f32fd0fba..6e177ba6bd 100644
--- a/firmware/target/arm/imx31/dvfs_dptc-imx31.c
+++ b/firmware/target/arm/imx31/dvfs_dptc-imx31.c
@@ -22,27 +22,721 @@
22 ****************************************************************************/ 22 ****************************************************************************/
23#include "config.h" 23#include "config.h"
24#include "system.h" 24#include "system.h"
25#include "ccm-imx31.h" 25#include "logf.h"
26#include "mc13783.h" 26#include "mc13783.h"
27#include "iomuxc-imx31.h"
28#include "ccm-imx31.h"
29#include "avic-imx31.h"
30#include "dvfs_dptc-imx31.h"
31#include "dvfs_dptc_tables-target.h"
27 32
28/* Most of the code in here is based upon the Linux BSP provided by Freescale 33/* Most of the code in here is based upon the Linux BSP provided by Freescale
29 * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved. */ 34 * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved. */
30 35
31void dvfs_dptc_start(void) 36/* The current DVFS index level */
37static volatile unsigned int dvfs_level = DVFS_LEVEL_DEFAULT;
38/* The current DPTC working point */
39static volatile unsigned int dptc_wp = DPTC_WP_DEFAULT;
40
41
42static void update_dptc_counts(unsigned int level, unsigned int wp)
43{
44 int oldlevel = disable_irq_save();
45 const struct dptc_dcvr_table_entry *entry = &dptc_dcvr_table[level][wp];
46
47 CCM_DCVR0 = entry->dcvr0;
48 CCM_DCVR1 = entry->dcvr1;
49 CCM_DCVR2 = entry->dcvr2;
50 CCM_DCVR3 = entry->dcvr3;
51
52 restore_irq(oldlevel);
53}
54
55
56static inline uint32_t check_regulator_setting(uint32_t setting)
57{
58 /* Simply a safety check *in case* table gets scrambled */
59 if (setting < VOLTAGE_SETTING_MIN)
60 setting = VOLTAGE_SETTING_MIN;
61 else if (setting > VOLTAGE_SETTING_MAX)
62 setting = VOLTAGE_SETTING_MAX;
63
64 return setting;
65}
66
67
68/** DVFS **/
69static bool dvfs_running = false; /* Has driver enabled DVFS? */
70
71/* Request tracking since boot */
72unsigned int dvfs_nr_dn = 0;
73unsigned int dvfs_nr_up = 0;
74unsigned int dvfs_nr_pnc = 0;
75
76static void dvfs_stop(void);
77
78
79/* Wait for the UPDTEN flag to be set so that all bits may be written */
80static inline void wait_for_dvfs_update_en(void)
81{
82 while (!(CCM_PMCR0 & CCM_PMCR0_UPDTEN));
83}
84
85
86static void do_dvfs_update(unsigned int level)
87{
88 const struct dvfs_clock_table_entry *setting = &dvfs_clock_table[level];
89 unsigned long pmcr0 = CCM_PMCR0;
90
91 if (pmcr0 & CCM_PMCR0_DPTEN)
92 {
93 /* Ignore voltage change request from DPTC. Voltage is *not* valid. */
94 pmcr0 &= ~CCM_PMCR0_DPVCR;
95 }
96
97 pmcr0 &= ~CCM_PMCR0_VSCNT;
98
99 if (level > ((pmcr0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS))
100 {
101 pmcr0 |= CCM_PMCR0_UDSC; /* Up scaling, increase */
102 pmcr0 |= setting->vscnt << CCM_PMCR0_VSCNT_POS;
103 }
104 else
105 {
106 pmcr0 &= ~CCM_PMCR0_UDSC; /* Down scaling, decrease */
107 pmcr0 |= 0x1 << CCM_PMCR0_VSCNT_POS;
108 }
109
110 /* DVSUP (new frequency index) setup */
111 pmcr0 = (pmcr0 & ~CCM_PMCR0_DVSUP) | (level << CCM_PMCR0_DVSUP_POS);
112
113 dvfs_level = level;
114
115 if ((setting->pll_num << CCM_PMCR0_DFSUP_MCUPLL_POS) ^
116 (pmcr0 & CCM_PMCR0_DFSUP_MCUPLL))
117 {
118 /* Update pll and post-dividers. */
119 pmcr0 ^= CCM_PMCR0_DFSUP_MCUPLL;
120 pmcr0 &= ~CCM_PMCR0_DFSUP_POST_DIVIDERS;
121 }
122 else
123 {
124 /* Post-dividers update only */
125 pmcr0 |= CCM_PMCR0_DFSUP_POST_DIVIDERS;
126 }
127
128 CCM_PMCR0 = pmcr0;
129 udelay(100); /* Software wait for voltage ramp-up */
130 CCM_PDR0 = setting->pdr_val;
131
132 if (!(pmcr0 & CCM_PMCR0_DFSUP_POST_DIVIDERS))
133 {
134 /* Update the PLL settings */
135 if (pmcr0 & CCM_PMCR0_DFSUP_MCUPLL)
136 CCM_MPCTL = setting->pll_val;
137 else
138 CCM_SPCTL = setting->pll_val;
139 }
140
141 cpu_frequency = ccm_get_mcu_clk();
142
143 if (pmcr0 & CCM_PMCR0_DPTEN)
144 {
145 update_dptc_counts(level, dptc_wp);
146 /* Enable DPTC to request voltage changes. Voltage is valid. */
147 CCM_PMCR0 |= CCM_PMCR0_DPVCR;
148 udelay(2);
149 CCM_PMCR0 |= CCM_PMCR0_DPVV;
150 }
151}
152
153
154/* Start DVFS, change the set point and stop it */
155static void set_current_dvfs_level(unsigned int level)
156{
157 int oldlevel = disable_irq_save();
158
159 CCM_PMCR0 |= CCM_PMCR0_DVFEN;
160
161 wait_for_dvfs_update_en();
162
163 do_dvfs_update(level);
164
165 wait_for_dvfs_update_en();
166
167 CCM_PMCR0 &= ~CCM_PMCR0_DVFEN;
168
169 restore_irq(oldlevel);
170}
171
172
173/* DVFS Interrupt handler */
174static void __attribute__((used)) dvfs_int(void)
175{
176 unsigned long pmcr0 = CCM_PMCR0;
177 unsigned long fsvai = pmcr0 & CCM_PMCR0_FSVAI;
178 unsigned int level = (pmcr0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS;
179
180 if (pmcr0 & CCM_PMCR0_FSVAIM)
181 return; /* Do nothing. DVFS interrupt is masked. */
182
183 if (!(pmcr0 & CCM_PMCR0_UPDTEN))
184 return; /* Do nothing. DVFS didn't finish previous flow update. */
185
186 switch (fsvai)
187 {
188 case CCM_PMCR0_FSVAI_DECREASE:
189 if (level >= DVFS_NUM_LEVELS - 1)
190 return; /* DVFS already at lowest level */
191
192 /* Upon the DECREASE event, the frequency will be changed to the next
193 * higher state index. */
194 level++;
195 dvfs_nr_dn++;
196 break;
197
198 /* Single-step frequency increase */
199 case CCM_PMCR0_FSVAI_INCREASE:
200 if (level == 0)
201 return; /* DVFS already at highest level */
202
203 /* Upon the INCREASE event, the frequency will be changed to the next
204 * lower state index. */
205 level--;
206 dvfs_nr_up++;
207 break;
208
209 /* Right to highest if panic */
210 case CCM_PMCR0_FSVAI_INCREASE_NOW:
211 if (level == 0)
212 return; /* DVFS already at highest level */
213
214 /* Upon the INCREASE_NOW event, the frequency will be increased to
215 * the maximum (index 0). */
216 level = 0;
217 dvfs_nr_pnc++;
218 break;
219
220 case CCM_PMCR0_FSVAI_NO_INT:
221 default:
222 return; /* Do nothing. Freq change is not required */
223 } /* end switch */
224
225 do_dvfs_update(level);
226}
227
228
229/* Interrupt vector for DVFS */
230static __attribute__((naked, interrupt("IRQ"))) void CCM_DVFS_HANDLER(void)
231{
232 /* Audio can glitch with the long udelay if nested IRQ isn't allowed. */
233 AVIC_NESTED_NI_CALL_PROLOGUE();
234 asm volatile ("bl dvfs_int");
235 AVIC_NESTED_NI_CALL_EPILOGUE();
236}
237
238
239/* Initialize the DVFS hardware */
240static void dvfs_init(void)
241{
242 if (CCM_PMCR0 & CCM_PMCR0_DVFEN)
243 {
244 /* Turn it off first. Really, shouldn't happen though. */
245 dvfs_running = true;
246 dvfs_stop();
247 }
248
249 /* Combine SW1A and SW1B DVS pins for a possible five DVS levels
250 * per working point. Four, MAXIMUM, are actually used, one for each
251 * frequency. */
252 mc13783_set(MC13783_ARBITRATION_SWITCHERS, MC13783_SW1ABDVS);
253
254 /* Set DVS speed to 25mV every 4us. */
255 mc13783_write_masked(MC13783_SWITCHERS4, MC13783_SW1ADVSSPEED_4US,
256 MC13783_SW1ADVSSPEED);
257
258 /* Set DVFS pins to functional outputs. Input mode and pad setting is
259 * fixed in hardware. */
260 iomuxc_set_pin_mux(IOMUXC_DVFS0,
261 IOMUXC_MUX_OUT_FUNCTIONAL | IOMUXC_MUX_IN_NONE);
262 iomuxc_set_pin_mux(IOMUXC_DVFS1,
263 IOMUXC_MUX_OUT_FUNCTIONAL | IOMUXC_MUX_IN_NONE);
264
265#ifndef DVFS_NO_PWRRDY
266 /* Configure PWRRDY signal pin. */
267 imx31_regclr32(&GPIO1_GDIR, (1 << 5));
268 iomuxc_set_pin_mux(IOMUXC_GPIO1_5,
269 IOMUXC_MUX_OUT_FUNCTIONAL | IOMUXC_MUX_IN_FUNCTIONAL);
270#endif
271
272 /* Initialize DVFS signal weights and detection modes. */
273 int i;
274 for (i = 0; i < 16; i++)
275 {
276 dvfs_set_lt_weight(i, lt_signals[i].weight);
277 dvfs_set_lt_detect(i, lt_signals[i].detect);
278 }
279
280 /* Set up LTR0. */
281 imx31_regmod32(&CCM_LTR0,
282 DVFS_UPTHR << CCM_LTR0_UPTHR_POS |
283 DVFS_DNTHR << CCM_LTR0_DNTHR_POS |
284 DVFS_DIV3CK,
285 CCM_LTR0_UPTHR | CCM_LTR0_DNTHR | CCM_LTR0_DIV3CK);
286
287 /* Set up LTR1. */
288 imx31_regmod32(&CCM_LTR1,
289 DVFS_DNCNT << CCM_LTR1_DNCNT_POS |
290 DVFS_UPCNT << CCM_LTR1_UPCNT_POS |
291 DVFS_PNCTHR << CCM_LTR1_PNCTHR_POS |
292 CCM_LTR1_LTBRSR,
293 CCM_LTR1_DNCNT | CCM_LTR1_UPCNT |
294 CCM_LTR1_PNCTHR | CCM_LTR1_LTBRSR);
295
296 /* Set up LTR2-- EMA configuration. */
297 imx31_regmod32(&CCM_LTR2, DVFS_EMAC << CCM_LTR2_EMAC_POS,
298 CCM_LTR2_EMAC);
299
300 /* DVFS interrupt goes to MCU. Mask load buffer full interrupt. */
301 imx31_regset32(&CCM_PMCR0, CCM_PMCR0_DVFIS | CCM_PMCR0_LBMI);
302
303 /* Initialize current core PLL and dividers for default level. Assumes
304 * clocking scheme has been set up appropriately in other init code. */
305 ccm_set_mcupll_and_pdr(dvfs_clock_table[DVFS_LEVEL_DEFAULT].pll_val,
306 dvfs_clock_table[DVFS_LEVEL_DEFAULT].pdr_val);
307
308 /* Set initial level and working point. */
309 set_current_dvfs_level(DVFS_LEVEL_DEFAULT);
310
311 logf("DVFS: Initialized");
312}
313
314
315/* Start the DVFS hardware */
316static void dvfs_start(void)
317{
318 int oldlevel;
319
320 /* Have to wait at least 3 div3 clocks before enabling after being
321 * stopped. */
322 udelay(1500);
323
324 oldlevel = disable_irq_save();
325
326 if (!dvfs_running)
327 {
328 dvfs_running = true;
329
330 /* Unmask DVFS interrupt source and enable DVFS. */
331 avic_enable_int(INT_CCM_DVFS, INT_TYPE_IRQ, INT_PRIO_DVFS,
332 CCM_DVFS_HANDLER);
333
334 CCM_PMCR0 = (CCM_PMCR0 & ~CCM_PMCR0_FSVAIM) | CCM_PMCR0_DVFEN;
335 }
336
337 restore_irq(oldlevel);
338
339 logf("DVFS: started");
340}
341
342
343/* Stop the DVFS hardware and return to default frequency */
344static void dvfs_stop(void)
32{ 345{
33 /* For now, just set the regulator voltage off of overdrive mode */ 346 int oldlevel = disable_irq_save();
34 /* For 264 MHz, DPTC is not needed and lower V can be used */ 347
348 if (dvfs_running)
349 {
350 /* Mask DVFS interrupts. */
351 CCM_PMCR0 |= CCM_PMCR0_FSVAIM | CCM_PMCR0_LBMI;
352 avic_disable_int(INT_CCM_DVFS);
353
354 if (((CCM_PMCR0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS) !=
355 DVFS_LEVEL_DEFAULT)
356 {
357 /* Set default frequency level */
358 wait_for_dvfs_update_en();
359 do_dvfs_update(DVFS_LEVEL_DEFAULT);
360 wait_for_dvfs_update_en();
361 }
362
363 /* Disable DVFS. */
364 CCM_PMCR0 &= ~CCM_PMCR0_DVFEN;
365 dvfs_running = false;
366 }
367
368 restore_irq(oldlevel);
369
370 logf("DVFS: stopped");
371}
372
373
374/** DPTC **/
375
376/* Request tracking since boot */
377unsigned int dptc_nr_dn = 0;
378unsigned int dptc_nr_up = 0;
379unsigned int dptc_nr_pnc = 0;
380
381
382/* Enable DPTC and unmask interrupt. */
383static void enable_dptc(void)
384{
385 int oldlevel = disable_irq_save();
386
387 /* Enable DPTC, assert voltage change request. */
388 CCM_PMCR0 = (CCM_PMCR0 & ~CCM_PMCR0_PTVAIM) | CCM_PMCR0_DPTEN |
389 CCM_PMCR0_DPVCR;
390
391 udelay(2);
392
393 /* Set voltage valid *after* setting change request */
394 CCM_PMCR0 |= CCM_PMCR0_DPVV;
395
396 restore_irq(oldlevel);
397}
398
399
400static void dptc_new_wp(unsigned int wp)
401{
402 unsigned int level = dvfs_level;
403 const union dvfs_dptc_voltage_table_entry *entry = &dvfs_dptc_voltage_table[wp];
404
405 uint32_t sw1a = check_regulator_setting(entry->sw1a);
406 uint32_t sw1advs = check_regulator_setting(entry->sw1advs);
407 uint32_t sw1bdvs = check_regulator_setting(entry->sw1bdvs);
408 uint32_t sw1bstby = check_regulator_setting(entry->sw1bstby);
409
410 dptc_wp = wp;
35 411
36 mc13783_write_masked(MC13783_SWITCHERS0, 412 mc13783_write_masked(MC13783_SWITCHERS0,
37 MC13783_SW_1_350 << MC13783_SW1A_POS, 413 sw1a << MC13783_SW1A_POS | /* SW1A */
38 MC13783_SW1A); 414 sw1advs << MC13783_SW1ADVS_POS, /* SW1ADVS */
39 imx31_regmod32(&CCM_PMCR0, CCM_PMCR0_DVS1_0_DVS0_0, 415 MC13783_SW1A | MC13783_SW1ADVS);
40 CCM_PMCR0_DVSUP_DVS); 416
417 mc13783_write_masked(MC13783_SWITCHERS1,
418 sw1bdvs << MC13783_SW1BDVS_POS | /* SW1BDVS */
419 sw1bstby << MC13783_SW1BSTBY_POS, /* SW1BSTBY */
420 MC13783_SW1BDVS | MC13783_SW1BSTBY);
421
422
423 udelay(100); /* Wait to settle */
424
425 update_dptc_counts(level, wp);
426}
427
428
429/* DPTC service thread */
430#ifdef ROCKBOX_HAS_LOGF
431#define DPTC_STACK_SIZE DEFAULT_STACK_SIZE
432#else
433#define DPTC_STACK_SIZE 160
434#endif
435static int dptc_thread_stack[DPTC_STACK_SIZE/sizeof(int)];
436static const char * const dptc_thread_name = "dptc";
437static struct wakeup dptc_wakeup; /* Object to signal upon DPTC event */
438static struct mutex dptc_mutex; /* Avoid mutually disrupting voltage updates */
439static unsigned long dptc_int_data; /* Data passed to thread for each event */
440static bool dptc_running = false; /* Has driver enabled DPTC? */
441
442
443static void dptc_interrupt_thread(void)
444{
445 int wp;
446
447 mutex_lock(&dptc_mutex);
448
449 while (1)
450 {
451 mutex_unlock(&dptc_mutex);
452
453 wakeup_wait(&dptc_wakeup, TIMEOUT_BLOCK);
454
455 mutex_lock(&dptc_mutex);
456
457 if (!dptc_running)
458 continue;
459
460 wp = dptc_wp;
461
462 switch (dptc_int_data & CCM_PMCR0_PTVAI)
463 {
464 case CCM_PMCR0_PTVAI_DECREASE:
465 wp++;
466 dptc_nr_dn++;
467 break;
468
469 case CCM_PMCR0_PTVAI_INCREASE:
470 wp--;
471 dptc_nr_up++;
472 break;
473
474 case CCM_PMCR0_PTVAI_INCREASE_NOW:
475 wp = DPTC_WP_PANIC;
476 dptc_nr_pnc++;
477 break;
478
479 case CCM_PMCR0_PTVAI_NO_INT:
480 logf("DPTC: unexpected INT");
481 continue;
482 }
483
484 if (wp < 0)
485 {
486 wp = 0;
487 logf("DPTC: already @ highest (%d)", wp);
488 }
489 else if (wp >= DPTC_NUM_WP)
490 {
491 wp = DPTC_NUM_WP - 1;
492 logf("DPTC: already @ lowest (%d)", wp);
493 }
494 else
495 {
496 logf("DPTC: new wp (%d)", wp);
497 }
498
499 dptc_new_wp(wp);
500 enable_dptc();
501 }
502}
503
504
505/* Interrupt vector for DPTC */
506static __attribute__((interrupt("IRQ"))) void CCM_CLK_HANDLER(void)
507{
508 /* Snapshot the interrupt cause */
509 unsigned long pmcr0 = CCM_PMCR0;
510 dptc_int_data = pmcr0;
511
512 /* Mask DPTC interrupt and disable DPTC until the change request is
513 * serviced. */
514 CCM_PMCR0 = (pmcr0 & ~CCM_PMCR0_DPTEN) | CCM_PMCR0_PTVAIM;
515
516 wakeup_signal(&dptc_wakeup);
517}
518
519
520/* Initialize the DPTC hardware */
521static void dptc_init(void)
522{
523 /* Force DPTC off if running for some reason. */
524 imx31_regmod32(&CCM_PMCR0, CCM_PMCR0_PTVAIM,
525 CCM_PMCR0_PTVAIM | CCM_PMCR0_DPTEN);
526
527 /* Set default, safe working point. */
528 dptc_new_wp(DPTC_WP_DEFAULT);
529
530 /* Interrupt goes to MCU, specified reference circuits enabled when
531 * DPTC is active. */
532 imx31_regset32(&CCM_PMCR0, CCM_PMCR0_PTVIS | DPTC_DRCE_MASK);
533
534 /* DPTC counting range = 256 system clocks */
535 imx31_regclr32(&CCM_PMCR0, CCM_PMCR0_DCR);
536
537 /* Create PMIC regulator service. */
538 wakeup_init(&dptc_wakeup);
539 mutex_init(&dptc_mutex);
540 create_thread(dptc_interrupt_thread,
541 dptc_thread_stack, sizeof(dptc_thread_stack), 0,
542 dptc_thread_name IF_PRIO(, PRIORITY_REALTIME_1) IF_COP(, CPU));
543
544 logf("DPTC: Initialized");
41} 545}
42 546
43 547
548/* Start DPTC module */
549static void dptc_start(void)
550{
551 int oldstate;
552
553 mutex_lock(&dptc_mutex);
554
555 oldstate = disable_irq_save();
556
557 if (!dptc_running)
558 {
559 dptc_running = true;
560
561 /* Enable DPTC and unmask interrupt. */
562 avic_enable_int(INT_CCM_CLK, INT_TYPE_IRQ, INT_PRIO_DPTC,
563 CCM_CLK_HANDLER);
564
565 update_dptc_counts(dvfs_level, dptc_wp);
566 enable_dptc();
567 }
568
569 restore_irq(oldstate);
570
571 mutex_unlock(&dptc_mutex);
572
573 logf("DPTC: started");
574}
575
576
577/* Stop the DPTC hardware if running and go back to default working point */
578static void dptc_stop(void)
579{
580 int oldlevel;
581
582 mutex_lock(&dptc_mutex);
583
584 oldlevel = disable_irq_save();
585
586 if (dptc_running)
587 {
588 /* Disable DPTC and mask interrupt. */
589 CCM_PMCR0 = (CCM_PMCR0 & ~CCM_PMCR0_DPTEN) | CCM_PMCR0_PTVAIM;
590 avic_disable_int(INT_CCM_CLK);
591 dptc_int_data = 0;
592
593 dptc_running = false;
594 }
595
596 restore_irq(oldlevel);
597
598 /* Go back to default working point. */
599 dptc_new_wp(DPTC_WP_DEFAULT);
600
601 mutex_unlock(&dptc_mutex);
602
603 logf("DPTC: stopped");
604}
605
606
607/** Main module interface **/
608
609/* Initialize DVFS and DPTC */
610void dvfs_dptc_init(void)
611{
612 dptc_init();
613 dvfs_init();
614}
615
616
617/* Start DVFS and DPTC */
618void dvfs_dptc_start(void)
619{
620 dvfs_start();
621 if (0) /* Hold off for now */
622 {
623 dptc_start();
624 }
625}
626
627
628/* Stop DVFS and DPTC */
44void dvfs_dptc_stop(void) 629void dvfs_dptc_stop(void)
45{ 630{
46 /* Nothing for now */ 631 dptc_stop();
632 dvfs_stop();
47} 633}
48 634
635
636/* Set a signal load tracking weight */
637void dvfs_set_lt_weight(enum DVFS_LT_SIGS index, unsigned long value)
638{
639 volatile unsigned long *reg_p = &CCM_LTR2;
640 unsigned int shift = 3 * index;
641
642 if (index < 9)
643 {
644 reg_p = &CCM_LTR3;
645 shift += 5; /* Bits 7:5, 10:8 ... 31:29 */
646 }
647 else if (index < 16)
648 {
649 shift -= 16; /* Bits 13:11, 16:14 ... 31:29 */
650 }
651
652 imx31_regmod32(reg_p, value << shift, 0x7 << shift);
653}
654
655
656/* Set a signal load detection mode */
657void dvfs_set_lt_detect(enum DVFS_LT_SIGS index, bool edge)
658{
659 unsigned long bit = 0;
660
661 if ((unsigned)index < 13)
662 bit = 1ul << (index + 3);
663 else if ((unsigned)index < 16)
664 bit = 1ul << (index + 29);
665
666 imx31_regmod32(&CCM_LTR0, edge ? bit : 0, bit);
667}
668
669
670void dvfs_set_gp_bit(enum DVFS_DVGPS dvgp, bool assert)
671{
672 if ((unsigned)dvgp <= 3)
673 {
674 unsigned long bit = 1ul << dvgp;
675 imx31_regmod32(&CCM_PMCR1, assert ? bit : 0, bit);
676 }
677}
678
679
680/* Turn the wait-for-interrupt monitoring on or off */
681void dvfs_wfi_monitor(bool on)
682{
683 imx31_regmod32(&CCM_PMCR0, on ? 0 : CCM_PMCR0_WFIM,
684 CCM_PMCR0_WFIM);
685}
686
687
688/* Obtain the current core voltage setting, in millivolts 8-) */
689unsigned int dvfs_dptc_get_voltage(void)
690{
691 unsigned int v;
692
693 int oldlevel = disable_irq_save();
694 v = dvfs_dptc_voltage_table[dptc_wp].sw[dvfs_level];
695 restore_irq(oldlevel);
696
697 /* 25mV steps from 0.900V to 1.675V */
698 return v * 25 + 900;
699}
700
701
702/* Get the current DVFS level */
703unsigned int dvfs_get_level(void)
704{
705 return dvfs_level;
706}
707
708
709/* If DVFS is disabled, set the level explicitly */
710void dvfs_set_level(unsigned int level)
711{
712 int oldlevel = disable_irq_save();
713
714 unsigned int currlevel =
715 (CCM_PMCR0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS;
716
717 if (!dvfs_running && level < DVFS_NUM_LEVELS && level != currlevel)
718 set_current_dvfs_level(level);
719
720 restore_irq(oldlevel);
721}
722
723
724/* Get the current DPTC working point */
725unsigned int dptc_get_wp(void)
726{
727 return dptc_wp;
728}
729
730
731/* If DPTC is not running, set the working point explicitly */
732void dptc_set_wp(unsigned int wp)
733{
734 mutex_lock(&dptc_mutex);
735
736 if (!dptc_running && wp < DPTC_NUM_WP)
737 {
738 dptc_new_wp(wp);
739 }
740
741 mutex_unlock(&dptc_mutex);
742}