Gigabeat S bootloader: Fix archaic call placement in dptc_stop which hangs the bootlo...
[kugel-rb.git] / firmware / target / arm / imx31 / dvfs_dptc-imx31.c
blob129e47d55e286e958339ae3d95fc74237d5152e7
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2010 by Michael Sevakis
12 * i.MX31 DVFS and DPTC drivers
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
22 ****************************************************************************/
23 #include "config.h"
24 #include "system.h"
25 #include "logf.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"
33 /* Most of the code in here is based upon the Linux BSP provided by Freescale
34 * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved. */
36 /* The current DVFS index level */
37 static volatile unsigned int dvfs_level = DVFS_LEVEL_DEFAULT;
38 /* The current DPTC working point */
39 static volatile unsigned int dptc_wp = DPTC_WP_DEFAULT;
42 static void update_dptc_counts(unsigned int level, unsigned int wp)
44 int oldlevel = disable_irq_save();
45 const struct dptc_dcvr_table_entry *entry = &dptc_dcvr_table[level][wp];
47 CCM_DCVR0 = entry->dcvr0;
48 CCM_DCVR1 = entry->dcvr1;
49 CCM_DCVR2 = entry->dcvr2;
50 CCM_DCVR3 = entry->dcvr3;
52 restore_irq(oldlevel);
56 static uint32_t check_regulator_setting(uint32_t setting)
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;
64 return setting;
68 /** DVFS **/
69 static bool dvfs_running = false; /* Has driver enabled DVFS? */
71 /* Request tracking since boot */
72 unsigned int dvfs_nr_dn = 0;
73 unsigned int dvfs_nr_up = 0;
74 unsigned int dvfs_nr_pnc = 0;
75 unsigned int dvfs_nr_no = 0;
77 static void dvfs_stop(void);
80 /* Wait for the UPDTEN flag to be set so that all bits may be written */
81 static inline void wait_for_dvfs_update_en(void)
83 while (!(CCM_PMCR0 & CCM_PMCR0_UPDTEN));
87 static void do_dvfs_update(unsigned int level, bool in_isr)
89 const struct dvfs_clock_table_entry *setting = &dvfs_clock_table[level];
90 unsigned long pmcr0 = CCM_PMCR0;
92 if (pmcr0 & CCM_PMCR0_DPTEN)
94 /* Ignore voltage change request from DPTC. Voltage is *not* valid. */
95 pmcr0 &= ~CCM_PMCR0_DPVCR;
98 pmcr0 &= ~CCM_PMCR0_VSCNT;
100 if (level < ((pmcr0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS))
102 pmcr0 |= CCM_PMCR0_UDSC; /* Up scaling, increase */
103 pmcr0 |= setting->vscnt << CCM_PMCR0_VSCNT_POS;
105 else
107 pmcr0 &= ~CCM_PMCR0_UDSC; /* Down scaling, decrease */
108 pmcr0 |= 0x1 << CCM_PMCR0_VSCNT_POS;
111 /* DVSUP (new frequency index) setup */
112 pmcr0 = (pmcr0 & ~CCM_PMCR0_DVSUP) | (level << CCM_PMCR0_DVSUP_POS);
114 dvfs_level = level;
116 if ((setting->pll_num << CCM_PMCR0_DFSUP_MCUPLL_POS) ^
117 (pmcr0 & CCM_PMCR0_DFSUP_MCUPLL))
119 /* Update pll and post-dividers. */
120 pmcr0 ^= CCM_PMCR0_DFSUP_MCUPLL;
121 pmcr0 &= ~CCM_PMCR0_DFSUP_POST_DIVIDERS;
123 else
125 /* Post-dividers update only */
126 pmcr0 |= CCM_PMCR0_DFSUP_POST_DIVIDERS;
129 CCM_PMCR0 = pmcr0;
130 /* Note: changes to frequency with ints unmaked seem to cause spurious
131 * DVFS interrupts with value CCM_PMCR0_FSVAI_NO_INT. These aren't
132 * supposed to happen. Only do the lengthy delay with them enabled iff
133 * called from the IRQ handler. */
134 if (in_isr)
135 enable_irq();
136 udelay(100); /* Software wait for voltage ramp-up */
137 if (in_isr)
138 disable_irq();
139 CCM_PDR0 = setting->pdr_val;
141 if (!(pmcr0 & CCM_PMCR0_DFSUP_POST_DIVIDERS))
143 /* Update the PLL settings */
144 if (pmcr0 & CCM_PMCR0_DFSUP_MCUPLL)
145 CCM_MPCTL = setting->pll_val;
146 else
147 CCM_SPCTL = setting->pll_val;
150 cpu_frequency = ccm_get_mcu_clk();
152 if (pmcr0 & CCM_PMCR0_DPTEN)
154 update_dptc_counts(level, dptc_wp);
155 /* Enable DPTC to request voltage changes. Voltage is valid. */
156 CCM_PMCR0 |= CCM_PMCR0_DPVCR;
157 udelay(2);
158 CCM_PMCR0 |= CCM_PMCR0_DPVV;
163 /* Start DVFS, change the set point and stop it */
164 static void set_current_dvfs_level(unsigned int level)
166 int oldlevel = disable_irq_save();
168 CCM_PMCR0 |= CCM_PMCR0_DVFEN;
170 wait_for_dvfs_update_en();
172 do_dvfs_update(level, false);
174 wait_for_dvfs_update_en();
176 CCM_PMCR0 &= ~CCM_PMCR0_DVFEN;
178 restore_irq(oldlevel);
182 /* DVFS Interrupt handler */
183 static void __attribute__((used)) dvfs_int(void)
185 unsigned long pmcr0 = CCM_PMCR0;
186 unsigned long fsvai = pmcr0 & CCM_PMCR0_FSVAI;
187 unsigned int level = (pmcr0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS;
189 if (pmcr0 & CCM_PMCR0_FSVAIM)
190 return; /* Do nothing. DVFS interrupt is masked. */
192 if (!(pmcr0 & CCM_PMCR0_UPDTEN))
193 return; /* Do nothing. DVFS didn't finish previous flow update. */
195 switch (fsvai)
197 case CCM_PMCR0_FSVAI_DECREASE:
198 if (level >= DVFS_NUM_LEVELS - 1)
199 return; /* DVFS already at lowest level */
201 /* Upon the DECREASE event, the frequency will be changed to the next
202 * higher state index. */
203 while (((1u << ++level) & DVFS_LEVEL_MASK) == 0);
205 dvfs_nr_dn++;
206 break;
208 /* Single-step frequency increase */
209 case CCM_PMCR0_FSVAI_INCREASE:
210 if (level == 0)
211 return; /* DVFS already at highest level */
213 /* Upon the INCREASE event, the frequency will be changed to the next
214 * lower state index. */
215 while (((1u << --level) & DVFS_LEVEL_MASK) == 0);
217 dvfs_nr_up++;
218 break;
220 /* Right to highest if panic */
221 case CCM_PMCR0_FSVAI_INCREASE_NOW:
222 if (level == 0)
223 return; /* DVFS already at highest level */
225 /* Upon the INCREASE_NOW event, the frequency will be increased to
226 * the maximum (index 0). */
227 level = 0;
228 dvfs_nr_pnc++;
229 break;
231 case CCM_PMCR0_FSVAI_NO_INT:
232 dvfs_nr_no++;
233 return; /* Do nothing. Freq change is not required */
234 } /* end switch */
236 do_dvfs_update(level, true);
240 /* Interrupt vector for DVFS */
241 static __attribute__((naked, interrupt("IRQ"))) void CCM_DVFS_HANDLER(void)
243 /* Audio can glitch with the long udelay if nested IRQ isn't allowed. */
244 AVIC_NESTED_NI_CALL_PROLOGUE(INT_PRIO_DVFS, 32*4);
245 asm volatile ("bl dvfs_int");
246 AVIC_NESTED_NI_CALL_EPILOGUE(32*4);
250 /* Initialize the DVFS hardware */
251 static void dvfs_init(void)
253 if (CCM_PMCR0 & CCM_PMCR0_DVFEN)
255 /* Turn it off first. Really, shouldn't happen though. */
256 dvfs_running = true;
257 dvfs_stop();
260 /* Combine SW1A and SW1B DVS pins for a possible five DVS levels
261 * per working point. Four, MAXIMUM, are actually used, one for each
262 * frequency. */
263 mc13783_set(MC13783_ARBITRATION_SWITCHERS, MC13783_SW1ABDVS);
265 /* Set DVS speed to 25mV every 4us. */
266 mc13783_write_masked(MC13783_SWITCHERS4, MC13783_SW1ADVSSPEED_4US,
267 MC13783_SW1ADVSSPEED);
269 /* Set DVFS pins to functional outputs. Input mode and pad setting is
270 * fixed in hardware. */
271 iomuxc_set_pin_mux(IOMUXC_DVFS0,
272 IOMUXC_MUX_OUT_FUNCTIONAL | IOMUXC_MUX_IN_NONE);
273 iomuxc_set_pin_mux(IOMUXC_DVFS1,
274 IOMUXC_MUX_OUT_FUNCTIONAL | IOMUXC_MUX_IN_NONE);
276 #ifndef DVFS_NO_PWRRDY
277 /* Configure PWRRDY signal pin. */
278 imx31_regclr32(&GPIO1_GDIR, (1 << 5));
279 iomuxc_set_pin_mux(IOMUXC_GPIO1_5,
280 IOMUXC_MUX_OUT_FUNCTIONAL | IOMUXC_MUX_IN_FUNCTIONAL);
281 #endif
283 /* Initialize DVFS signal weights and detection modes. */
284 int i;
285 for (i = 0; i < 16; i++)
287 dvfs_set_lt_weight(i, lt_signals[i].weight);
288 dvfs_set_lt_detect(i, lt_signals[i].detect);
291 /* Set up LTR0. */
292 imx31_regmod32(&CCM_LTR0,
293 DVFS_UPTHR << CCM_LTR0_UPTHR_POS |
294 DVFS_DNTHR << CCM_LTR0_DNTHR_POS |
295 DVFS_DIV3CK << CCM_LTR0_DIV3CK_POS,
296 CCM_LTR0_UPTHR | CCM_LTR0_DNTHR | CCM_LTR0_DIV3CK);
298 /* Set up LTR1. */
299 imx31_regmod32(&CCM_LTR1,
300 DVFS_DNCNT << CCM_LTR1_DNCNT_POS |
301 DVFS_UPCNT << CCM_LTR1_UPCNT_POS |
302 DVFS_PNCTHR << CCM_LTR1_PNCTHR_POS |
303 CCM_LTR1_LTBRSR,
304 CCM_LTR1_DNCNT | CCM_LTR1_UPCNT |
305 CCM_LTR1_PNCTHR | CCM_LTR1_LTBRSR);
307 /* Set up LTR2-- EMA configuration. */
308 imx31_regmod32(&CCM_LTR2, DVFS_EMAC << CCM_LTR2_EMAC_POS,
309 CCM_LTR2_EMAC);
311 /* DVFS interrupt goes to MCU. Mask load buffer full interrupt. */
312 imx31_regset32(&CCM_PMCR0, CCM_PMCR0_DVFIS | CCM_PMCR0_LBMI);
314 /* Initialize current core PLL and dividers for default level. Assumes
315 * clocking scheme has been set up appropriately in other init code. */
316 ccm_set_mcupll_and_pdr(dvfs_clock_table[DVFS_LEVEL_DEFAULT].pll_val,
317 dvfs_clock_table[DVFS_LEVEL_DEFAULT].pdr_val);
319 /* Set initial level and working point. */
320 set_current_dvfs_level(DVFS_LEVEL_DEFAULT);
322 logf("DVFS: Initialized");
326 /* Start the DVFS hardware */
327 static void dvfs_start(void)
329 int oldlevel;
331 /* Have to wait at least 3 div3 clocks before enabling after being
332 * stopped. */
333 udelay(1500);
335 oldlevel = disable_irq_save();
337 if (!dvfs_running)
339 dvfs_running = true;
341 /* Unmask DVFS interrupt source and enable DVFS. */
342 avic_enable_int(INT_CCM_DVFS, INT_TYPE_IRQ, INT_PRIO_DVFS,
343 CCM_DVFS_HANDLER);
345 CCM_PMCR0 = (CCM_PMCR0 & ~CCM_PMCR0_FSVAIM) | CCM_PMCR0_DVFEN;
348 restore_irq(oldlevel);
350 logf("DVFS: started");
354 /* Stop the DVFS hardware and return to default frequency */
355 static void dvfs_stop(void)
357 int oldlevel = disable_irq_save();
359 if (dvfs_running)
361 /* Mask DVFS interrupts. */
362 CCM_PMCR0 |= CCM_PMCR0_FSVAIM | CCM_PMCR0_LBMI;
363 avic_disable_int(INT_CCM_DVFS);
365 if (((CCM_PMCR0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS) !=
366 DVFS_LEVEL_DEFAULT)
368 /* Set default frequency level */
369 wait_for_dvfs_update_en();
370 do_dvfs_update(DVFS_LEVEL_DEFAULT, false);
371 wait_for_dvfs_update_en();
374 /* Disable DVFS. */
375 CCM_PMCR0 &= ~CCM_PMCR0_DVFEN;
376 dvfs_running = false;
379 restore_irq(oldlevel);
381 logf("DVFS: stopped");
385 /** DPTC **/
387 /* Request tracking since boot */
388 static bool dptc_running = false; /* Has driver enabled DPTC? */
390 unsigned int dptc_nr_dn = 0;
391 unsigned int dptc_nr_up = 0;
392 unsigned int dptc_nr_pnc = 0;
393 unsigned int dptc_nr_no = 0;
395 static struct spi_transfer_desc dptc_pmic_xfer; /* Transfer descriptor */
396 static const unsigned char dptc_pmic_regs[2] = /* Register subaddresses */
397 { MC13783_SWITCHERS0, MC13783_SWITCHERS1 };
398 static uint32_t dptc_reg_shadows[2]; /* shadow regs */
399 static uint32_t dptc_regs_buf[2]; /* buffer for async write */
402 /* Enable DPTC and unmask interrupt. */
403 static void enable_dptc(void)
405 int oldlevel = disable_irq_save();
407 /* Enable DPTC, assert voltage change request. */
408 CCM_PMCR0 = (CCM_PMCR0 & ~CCM_PMCR0_PTVAIM) | CCM_PMCR0_DPTEN |
409 CCM_PMCR0_DPVCR;
411 udelay(2);
413 /* Set voltage valid *after* setting change request */
414 CCM_PMCR0 |= CCM_PMCR0_DPVV;
416 restore_irq(oldlevel);
420 /* Called after asynchronous PMIC write is completed */
421 static void dptc_transfer_done_callback(struct spi_transfer_desc *xfer)
423 if (xfer->count != 0)
424 return;
426 update_dptc_counts(dvfs_level, dptc_wp);
428 if (dptc_running)
429 enable_dptc();
433 /* Handle the DPTC interrupt and sometimes the manual setting */
434 static void dptc_int(unsigned long pmcr0)
436 const union dvfs_dptc_voltage_table_entry *entry;
437 uint32_t sw1a, sw1advs, sw1bdvs, sw1bstby;
438 uint32_t switchers0, switchers1;
440 int wp = dptc_wp;
442 /* Mask DPTC interrupt and disable DPTC until the change request is
443 * serviced. */
444 CCM_PMCR0 = (pmcr0 & ~CCM_PMCR0_DPTEN) | CCM_PMCR0_PTVAIM;
446 switch (pmcr0 & CCM_PMCR0_PTVAI)
448 case CCM_PMCR0_PTVAI_DECREASE:
449 wp++;
450 dptc_nr_dn++;
451 break;
453 case CCM_PMCR0_PTVAI_INCREASE:
454 wp--;
455 dptc_nr_up++;
456 break;
458 case CCM_PMCR0_PTVAI_INCREASE_NOW:
459 if (--wp > DPTC_WP_PANIC)
460 wp = DPTC_WP_PANIC;
461 dptc_nr_pnc++;
462 break;
464 case CCM_PMCR0_PTVAI_NO_INT:
465 break; /* Just maintain at global level */
468 if (wp < 0)
469 wp = 0;
470 else if (wp >= DPTC_NUM_WP)
471 wp = DPTC_NUM_WP - 1;
473 entry = &dvfs_dptc_voltage_table[wp];
475 sw1a = check_regulator_setting(entry->sw1a);
476 sw1advs = check_regulator_setting(entry->sw1advs);
477 sw1bdvs = check_regulator_setting(entry->sw1bdvs);
478 sw1bstby = check_regulator_setting(entry->sw1bstby);
480 switchers0 = dptc_reg_shadows[0] & ~(MC13783_SW1A | MC13783_SW1ADVS);
481 dptc_regs_buf[0] = switchers0 |
482 sw1a << MC13783_SW1A_POS | /* SW1A */
483 sw1advs << MC13783_SW1ADVS_POS; /* SW1ADVS */
484 switchers1 = dptc_reg_shadows[1] & ~(MC13783_SW1BDVS | MC13783_SW1BSTBY);
485 dptc_regs_buf[1] = switchers1 |
486 sw1bdvs << MC13783_SW1BDVS_POS | /* SW1BDVS */
487 sw1bstby << MC13783_SW1BSTBY_POS; /* SW1BSTBY */
489 dptc_wp = wp;
491 mc13783_write_async(&dptc_pmic_xfer, dptc_pmic_regs,
492 dptc_regs_buf, 2, dptc_transfer_done_callback);
496 static void dptc_new_wp(unsigned int wp)
498 dptc_wp = wp;
499 /* "NO_INT" so the working point isn't incremented, just set. */
500 dptc_int((CCM_PMCR0 & ~CCM_PMCR0_PTVAI) | CCM_PMCR0_PTVAI_NO_INT);
504 /* Interrupt vector for DPTC */
505 static __attribute__((interrupt("IRQ"))) void CCM_CLK_HANDLER(void)
507 unsigned long pmcr0 = CCM_PMCR0;
509 if ((pmcr0 & CCM_PMCR0_PTVAI) == CCM_PMCR0_PTVAI_NO_INT)
510 dptc_nr_no++;
512 dptc_int(pmcr0);
516 /* Initialize the DPTC hardware */
517 static void dptc_init(void)
519 /* Force DPTC off if running for some reason. */
520 imx31_regmod32(&CCM_PMCR0, CCM_PMCR0_PTVAIM,
521 CCM_PMCR0_PTVAIM | CCM_PMCR0_DPTEN);
523 /* Shadow the regulator registers */
524 mc13783_read_regs(dptc_pmic_regs, dptc_reg_shadows, 2);
526 /* Set default, safe working point. */
527 dptc_new_wp(DPTC_WP_DEFAULT);
529 /* Interrupt goes to MCU, specified reference circuits enabled when
530 * DPTC is active. */
531 imx31_regset32(&CCM_PMCR0, CCM_PMCR0_PTVIS);
533 imx31_regmod32(&CCM_PMCR0, DPTC_DRCE_MASK,
534 CCM_PMCR0_DRCE0 | CCM_PMCR0_DRCE1 |
535 CCM_PMCR0_DRCE2 | CCM_PMCR0_DRCE3);
537 /* DPTC counting range = 256 system clocks */
538 imx31_regclr32(&CCM_PMCR0, CCM_PMCR0_DCR);
540 logf("DPTC: Initialized");
544 /* Start DPTC module */
545 static void dptc_start(void)
547 int oldlevel = disable_irq_save();
549 if (!dptc_running)
551 dptc_running = true;
553 /* Enable DPTC and unmask interrupt. */
554 avic_enable_int(INT_CCM_CLK, INT_TYPE_IRQ, INT_PRIO_DPTC,
555 CCM_CLK_HANDLER);
557 update_dptc_counts(dvfs_level, dptc_wp);
558 enable_dptc();
561 restore_irq(oldlevel);
563 logf("DPTC: started");
567 /* Stop the DPTC hardware if running and go back to default working point */
568 static void dptc_stop(void)
570 int oldlevel = disable_irq_save();
572 if (dptc_running)
574 dptc_running = false;
576 /* Disable DPTC and mask interrupt. */
577 CCM_PMCR0 = (CCM_PMCR0 & ~CCM_PMCR0_DPTEN) | CCM_PMCR0_PTVAIM;
578 avic_disable_int(INT_CCM_CLK);
580 /* Go back to default working point. */
581 dptc_new_wp(DPTC_WP_DEFAULT);
584 restore_irq(oldlevel);
586 logf("DPTC: stopped");
590 /** Main module interface **/
592 /* Initialize DVFS and DPTC */
593 void dvfs_dptc_init(void)
595 dptc_init();
596 dvfs_init();
600 /* Start DVFS and DPTC */
601 void dvfs_dptc_start(void)
603 dvfs_start();
604 dptc_start();
608 /* Stop DVFS and DPTC */
609 void dvfs_dptc_stop(void)
611 dptc_stop();
612 dvfs_stop();
616 /* Set a signal load tracking weight */
617 void dvfs_set_lt_weight(enum DVFS_LT_SIGS index, unsigned long value)
619 volatile unsigned long *reg_p = &CCM_LTR2;
620 unsigned int shift = 3 * index;
622 if (index < 9)
624 reg_p = &CCM_LTR3;
625 shift += 5; /* Bits 7:5, 10:8 ... 31:29 */
627 else if (index < 16)
629 shift -= 16; /* Bits 13:11, 16:14 ... 31:29 */
632 imx31_regmod32(reg_p, value << shift, 0x7 << shift);
636 /* Set a signal load detection mode */
637 void dvfs_set_lt_detect(enum DVFS_LT_SIGS index, bool edge)
639 unsigned long bit = 0;
641 if ((unsigned)index < 13)
642 bit = 1ul << (index + 3);
643 else if ((unsigned)index < 16)
644 bit = 1ul << (index + 29);
646 imx31_regmod32(&CCM_LTR0, edge ? bit : 0, bit);
650 void dvfs_set_gp_bit(enum DVFS_DVGPS dvgp, bool assert)
652 if ((unsigned)dvgp <= 3)
654 unsigned long bit = 1ul << dvgp;
655 imx31_regmod32(&CCM_PMCR1, assert ? bit : 0, bit);
660 /* Turn the wait-for-interrupt monitoring on or off */
661 void dvfs_wfi_monitor(bool on)
663 imx31_regmod32(&CCM_PMCR0, on ? 0 : CCM_PMCR0_WFIM,
664 CCM_PMCR0_WFIM);
668 /* Obtain the current core voltage setting, in millivolts 8-) */
669 unsigned int dvfs_dptc_get_voltage(void)
671 unsigned int v;
673 int oldlevel = disable_irq_save();
674 v = dvfs_dptc_voltage_table[dptc_wp].sw[dvfs_level];
675 restore_irq(oldlevel);
677 /* 25mV steps from 0.900V to 1.675V */
678 return v * 25 + 900;
682 /* Get the current DVFS level */
683 unsigned int dvfs_get_level(void)
685 return dvfs_level;
689 /* If DVFS is disabled, set the level explicitly */
690 void dvfs_set_level(unsigned int level)
692 int oldlevel = disable_irq_save();
694 unsigned int currlevel =
695 (CCM_PMCR0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS;
697 if (!dvfs_running && level < DVFS_NUM_LEVELS && level != currlevel)
698 set_current_dvfs_level(level);
700 restore_irq(oldlevel);
704 /* Get the current DPTC working point */
705 unsigned int dptc_get_wp(void)
707 return dptc_wp;
711 /* If DPTC is not running, set the working point explicitly */
712 void dptc_set_wp(unsigned int wp)
714 int oldlevel = disable_irq_save();
716 if (!dptc_running && wp < DPTC_NUM_WP)
717 dptc_new_wp(wp);
719 restore_irq(oldlevel);