i.MX31: Allow interrupts in thread context, not just ISR context, during the lengthy...
[kugel-rb.git] / firmware / target / arm / imx31 / dvfs_dptc-imx31.c
blob1c4e3a923f0c0b851a2e81cc5dd027e6e1a9a00c
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;
78 /* Wait for the UPDTEN flag to be set so that all bits may be written */
79 static inline void wait_for_dvfs_update_en(void)
81 while (!(CCM_PMCR0 & CCM_PMCR0_UPDTEN));
84 /* Do the actual frequency and DVFS pin change - always call with IRQ masked */
85 static void do_dvfs_update(unsigned int level)
87 const struct dvfs_clock_table_entry *setting = &dvfs_clock_table[level];
88 unsigned long pmcr0 = CCM_PMCR0;
89 int oldlevel;
91 if (pmcr0 & CCM_PMCR0_DPTEN)
93 /* Ignore voltage change request from DPTC. Voltage is *not* valid. */
94 pmcr0 &= ~CCM_PMCR0_DPVCR;
95 /* Mask DPTC interrupt for when called in thread context */
96 pmcr0 |= CCM_PMCR0_PTVAIM;
99 pmcr0 &= ~CCM_PMCR0_VSCNT;
101 if (level < ((pmcr0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS))
103 pmcr0 |= CCM_PMCR0_UDSC; /* Up scaling, increase */
104 pmcr0 |= setting->vscnt << CCM_PMCR0_VSCNT_POS;
106 else
108 pmcr0 &= ~CCM_PMCR0_UDSC; /* Down scaling, decrease */
109 pmcr0 |= 0x1 << CCM_PMCR0_VSCNT_POS;
112 /* DVSUP (new frequency index) setup */
113 pmcr0 = (pmcr0 & ~CCM_PMCR0_DVSUP) | (level << CCM_PMCR0_DVSUP_POS);
115 dvfs_level = level;
117 if ((setting->pll_num << CCM_PMCR0_DFSUP_MCUPLL_POS) ^
118 (pmcr0 & CCM_PMCR0_DFSUP_MCUPLL))
120 /* Update pll and post-dividers. */
121 pmcr0 ^= CCM_PMCR0_DFSUP_MCUPLL;
122 pmcr0 &= ~CCM_PMCR0_DFSUP_POST_DIVIDERS;
124 else
126 /* Post-dividers update only */
127 pmcr0 |= CCM_PMCR0_DFSUP_POST_DIVIDERS;
130 CCM_PMCR0 = pmcr0;
131 /* Note: changes to frequency with ints unmaked seem to cause spurious
132 * DVFS interrupts with value CCM_PMCR0_FSVAI_NO_INT. These aren't
133 * supposed to happen. Only do the lengthy delay with them enabled. */
134 enable_irq();
135 udelay(100); /* Software wait for voltage ramp-up */
136 disable_irq();
137 CCM_PDR0 = setting->pdr_val;
139 if (!(pmcr0 & CCM_PMCR0_DFSUP_POST_DIVIDERS))
141 /* Update the PLL settings */
142 if (pmcr0 & CCM_PMCR0_DFSUP_MCUPLL)
143 CCM_MPCTL = setting->pll_val;
144 else
145 CCM_SPCTL = setting->pll_val;
148 cpu_frequency = ccm_get_mcu_clk();
150 if (pmcr0 & CCM_PMCR0_DPTEN)
152 update_dptc_counts(level, dptc_wp);
153 /* Enable DPTC to request voltage changes, unmask interrupt. */
154 CCM_PMCR0 = (CCM_PMCR0 & ~CCM_PMCR0_PTVAIM) | CCM_PMCR0_DPVCR;
155 udelay(2);
156 /* Voltage is valid. */
157 CCM_PMCR0 |= CCM_PMCR0_DPVV;
162 /* Start DVFS, change the set point and stop it */
163 static void set_current_dvfs_level(unsigned int level)
165 int oldlevel = disable_irq_save();
167 CCM_PMCR0 |= CCM_PMCR0_DVFEN;
169 wait_for_dvfs_update_en();
171 do_dvfs_update(level);
173 wait_for_dvfs_update_en();
175 CCM_PMCR0 &= ~CCM_PMCR0_DVFEN;
177 restore_irq(oldlevel);
181 /* DVFS Interrupt handler */
182 static void __attribute__((used)) dvfs_int(void)
184 unsigned long pmcr0 = CCM_PMCR0;
185 unsigned long fsvai = pmcr0 & CCM_PMCR0_FSVAI;
186 unsigned int level = (pmcr0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS;
188 if (pmcr0 & CCM_PMCR0_FSVAIM)
189 return; /* Do nothing. DVFS interrupt is masked. */
191 if (!(pmcr0 & CCM_PMCR0_UPDTEN))
192 return; /* Do nothing. DVFS didn't finish previous flow update. */
194 switch (fsvai)
196 case CCM_PMCR0_FSVAI_DECREASE:
197 if (level >= DVFS_NUM_LEVELS - 1)
198 return; /* DVFS already at lowest level */
200 /* Upon the DECREASE event, the frequency will be changed to the next
201 * higher state index. */
202 while (((1u << ++level) & DVFS_LEVEL_MASK) == 0);
204 dvfs_nr_dn++;
205 break;
207 /* Single-step frequency increase */
208 case CCM_PMCR0_FSVAI_INCREASE:
209 if (level == 0)
210 return; /* DVFS already at highest level */
212 /* Upon the INCREASE event, the frequency will be changed to the next
213 * lower state index. */
214 while (((1u << --level) & DVFS_LEVEL_MASK) == 0);
216 dvfs_nr_up++;
217 break;
219 /* Right to highest if panic */
220 case CCM_PMCR0_FSVAI_INCREASE_NOW:
221 if (level == 0)
222 return; /* DVFS already at highest level */
224 /* Upon the INCREASE_NOW event, the frequency will be increased to
225 * the maximum (index 0). */
226 level = 0;
227 dvfs_nr_pnc++;
228 break;
230 case CCM_PMCR0_FSVAI_NO_INT:
231 dvfs_nr_no++;
232 return; /* Do nothing. Freq change is not required */
233 } /* end switch */
235 do_dvfs_update(level);
239 /* Interrupt vector for DVFS */
240 static __attribute__((naked, interrupt("IRQ"))) void CCM_DVFS_HANDLER(void)
242 /* Audio can glitch with the long udelay if nested IRQ isn't allowed. */
243 AVIC_NESTED_NI_CALL_PROLOGUE(INT_PRIO_DVFS, 32*4);
244 asm volatile ("bl dvfs_int");
245 AVIC_NESTED_NI_CALL_EPILOGUE(32*4);
249 /* Initialize the DVFS hardware */
250 static void INIT_ATTR dvfs_init(void)
252 if (CCM_PMCR0 & CCM_PMCR0_DVFEN)
254 /* Turn it off first. Really, shouldn't happen though. */
255 dvfs_running = true;
256 dvfs_stop();
259 /* Combine SW1A and SW1B DVS pins for a possible five DVS levels
260 * per working point. Four, MAXIMUM, are actually used, one for each
261 * frequency. */
262 mc13783_set(MC13783_ARBITRATION_SWITCHERS, MC13783_SW1ABDVS);
264 /* Set DVS speed to 25mV every 4us. */
265 mc13783_write_masked(MC13783_SWITCHERS4, MC13783_SW1ADVSSPEED_4US,
266 MC13783_SW1ADVSSPEED);
268 /* Set DVFS pins to functional outputs. Input mode and pad setting is
269 * fixed in hardware. */
270 iomuxc_set_pin_mux(IOMUXC_DVFS0,
271 IOMUXC_MUX_OUT_FUNCTIONAL | IOMUXC_MUX_IN_NONE);
272 iomuxc_set_pin_mux(IOMUXC_DVFS1,
273 IOMUXC_MUX_OUT_FUNCTIONAL | IOMUXC_MUX_IN_NONE);
275 #ifndef DVFS_NO_PWRRDY
276 /* Configure PWRRDY signal pin. */
277 bitclr32(&GPIO1_GDIR, (1 << 5));
278 iomuxc_set_pin_mux(IOMUXC_GPIO1_5,
279 IOMUXC_MUX_OUT_FUNCTIONAL | IOMUXC_MUX_IN_FUNCTIONAL);
280 #endif
282 /* GP load bits disabled */
283 bitclr32(&CCM_PMCR1, 0xf);
285 /* Initialize DVFS signal weights and detection modes. */
286 int i;
287 for (i = 0; i < 16; i++)
289 dvfs_set_lt_weight(i, lt_signals[i].weight);
290 dvfs_set_lt_detect(i, lt_signals[i].detect);
293 /* Set up LTR0. */
294 bitmod32(&CCM_LTR0,
295 DVFS_UPTHR << CCM_LTR0_UPTHR_POS |
296 DVFS_DNTHR << CCM_LTR0_DNTHR_POS |
297 DVFS_DIV3CK << CCM_LTR0_DIV3CK_POS,
298 CCM_LTR0_UPTHR | CCM_LTR0_DNTHR | CCM_LTR0_DIV3CK);
300 /* Set up LTR1. */
301 bitmod32(&CCM_LTR1,
302 DVFS_DNCNT << CCM_LTR1_DNCNT_POS |
303 DVFS_UPCNT << CCM_LTR1_UPCNT_POS |
304 DVFS_PNCTHR << CCM_LTR1_PNCTHR_POS |
305 CCM_LTR1_LTBRSR,
306 CCM_LTR1_DNCNT | CCM_LTR1_UPCNT |
307 CCM_LTR1_PNCTHR | CCM_LTR1_LTBRSR);
309 /* Set up LTR2-- EMA configuration. */
310 bitmod32(&CCM_LTR2, DVFS_EMAC << CCM_LTR2_EMAC_POS, CCM_LTR2_EMAC);
312 /* DVFS interrupt goes to MCU. Mask load buffer full interrupt. */
313 bitset32(&CCM_PMCR0, CCM_PMCR0_DVFIS | CCM_PMCR0_LBMI);
315 /* Initialize current core PLL and dividers for default level. Assumes
316 * clocking scheme has been set up appropriately in other init code. */
317 ccm_set_mcupll_and_pdr(dvfs_clock_table[DVFS_LEVEL_DEFAULT].pll_val,
318 dvfs_clock_table[DVFS_LEVEL_DEFAULT].pdr_val);
320 /* Set initial level and working point. */
321 set_current_dvfs_level(DVFS_LEVEL_DEFAULT);
323 logf("DVFS: Initialized");
326 /** DPTC **/
328 /* Request tracking since boot */
329 static bool dptc_running = false; /* Has driver enabled DPTC? */
331 unsigned int dptc_nr_dn = 0;
332 unsigned int dptc_nr_up = 0;
333 unsigned int dptc_nr_pnc = 0;
334 unsigned int dptc_nr_no = 0;
336 static struct spi_transfer_desc dptc_pmic_xfer; /* Transfer descriptor */
337 static const unsigned char dptc_pmic_regs[2] = /* Register subaddresses */
338 { MC13783_SWITCHERS0, MC13783_SWITCHERS1 };
339 static uint32_t dptc_reg_shadows[2]; /* shadow regs */
340 static uint32_t dptc_regs_buf[2]; /* buffer for async write */
343 /* Enable DPTC and unmask interrupt. */
344 static void enable_dptc(void)
346 int oldlevel = disable_irq_save();
348 /* Enable DPTC, assert voltage change request. */
349 CCM_PMCR0 = (CCM_PMCR0 & ~CCM_PMCR0_PTVAIM) | CCM_PMCR0_DPTEN |
350 CCM_PMCR0_DPVCR;
352 udelay(2);
354 /* Set voltage valid *after* setting change request */
355 CCM_PMCR0 |= CCM_PMCR0_DPVV;
357 restore_irq(oldlevel);
361 /* Called after asynchronous PMIC write is completed */
362 static void dptc_transfer_done_callback(struct spi_transfer_desc *xfer)
364 if (xfer->count != 0)
365 return;
367 update_dptc_counts(dvfs_level, dptc_wp);
369 if (dptc_running)
370 enable_dptc();
374 /* Handle the DPTC interrupt and sometimes the manual setting - always call
375 * with IRQ masked. */
376 static void dptc_int(unsigned long pmcr0)
378 const union dvfs_dptc_voltage_table_entry *entry;
379 uint32_t sw1a, sw1advs, sw1bdvs, sw1bstby;
380 uint32_t switchers0, switchers1;
382 int wp = dptc_wp;
384 /* Mask DPTC interrupt and disable DPTC until the change request is
385 * serviced. */
386 CCM_PMCR0 = (pmcr0 & ~CCM_PMCR0_DPTEN) | CCM_PMCR0_PTVAIM;
388 switch (pmcr0 & CCM_PMCR0_PTVAI)
390 case CCM_PMCR0_PTVAI_DECREASE:
391 wp++;
392 dptc_nr_dn++;
393 break;
395 case CCM_PMCR0_PTVAI_INCREASE:
396 wp--;
397 dptc_nr_up++;
398 break;
400 case CCM_PMCR0_PTVAI_INCREASE_NOW:
401 if (--wp > DPTC_WP_PANIC)
402 wp = DPTC_WP_PANIC;
403 dptc_nr_pnc++;
404 break;
406 case CCM_PMCR0_PTVAI_NO_INT:
407 break; /* Just maintain at global level */
410 if (wp < 0)
411 wp = 0;
412 else if (wp >= DPTC_NUM_WP)
413 wp = DPTC_NUM_WP - 1;
415 entry = &dvfs_dptc_voltage_table[wp];
417 sw1a = check_regulator_setting(entry->sw1a);
418 sw1advs = check_regulator_setting(entry->sw1advs);
419 sw1bdvs = check_regulator_setting(entry->sw1bdvs);
420 sw1bstby = check_regulator_setting(entry->sw1bstby);
422 switchers0 = dptc_reg_shadows[0] & ~(MC13783_SW1A | MC13783_SW1ADVS);
423 dptc_regs_buf[0] = switchers0 |
424 sw1a << MC13783_SW1A_POS | /* SW1A */
425 sw1advs << MC13783_SW1ADVS_POS; /* SW1ADVS */
426 switchers1 = dptc_reg_shadows[1] & ~(MC13783_SW1BDVS | MC13783_SW1BSTBY);
427 dptc_regs_buf[1] = switchers1 |
428 sw1bdvs << MC13783_SW1BDVS_POS | /* SW1BDVS */
429 sw1bstby << MC13783_SW1BSTBY_POS; /* SW1BSTBY */
431 dptc_wp = wp;
433 mc13783_write_async(&dptc_pmic_xfer, dptc_pmic_regs,
434 dptc_regs_buf, 2, dptc_transfer_done_callback);
438 /* Handle setting the working point explicitly - always call with IRQ
439 * masked */
440 static void dptc_new_wp(unsigned int wp)
442 dptc_wp = wp;
443 /* "NO_INT" so the working point isn't incremented, just set. */
444 dptc_int((CCM_PMCR0 & ~CCM_PMCR0_PTVAI) | CCM_PMCR0_PTVAI_NO_INT);
448 /* Interrupt vector for DPTC */
449 static __attribute__((interrupt("IRQ"))) void CCM_CLK_HANDLER(void)
451 unsigned long pmcr0 = CCM_PMCR0;
453 if ((pmcr0 & CCM_PMCR0_PTVAI) == CCM_PMCR0_PTVAI_NO_INT)
454 dptc_nr_no++;
456 dptc_int(pmcr0);
460 /* Initialize the DPTC hardware */
461 static void INIT_ATTR dptc_init(void)
463 /* Force DPTC off if running for some reason. */
464 bitmod32(&CCM_PMCR0, CCM_PMCR0_PTVAIM,
465 CCM_PMCR0_PTVAIM | CCM_PMCR0_DPTEN);
467 /* Shadow the regulator registers */
468 mc13783_read_regs(dptc_pmic_regs, dptc_reg_shadows, 2);
470 /* Set default, safe working point. */
471 dptc_new_wp(DPTC_WP_DEFAULT);
473 /* Interrupt goes to MCU, specified reference circuits enabled when
474 * DPTC is active. */
475 bitset32(&CCM_PMCR0, CCM_PMCR0_PTVIS);
477 bitmod32(&CCM_PMCR0, DPTC_DRCE_MASK,
478 CCM_PMCR0_DRCE0 | CCM_PMCR0_DRCE1 |
479 CCM_PMCR0_DRCE2 | CCM_PMCR0_DRCE3);
481 /* DPTC counting range = 256 system clocks */
482 bitclr32(&CCM_PMCR0, CCM_PMCR0_DCR);
484 logf("DPTC: Initialized");
488 /** Main module interface **/
490 /* Initialize DVFS and DPTC */
491 void INIT_ATTR dvfs_dptc_init(void)
493 dptc_init();
494 dvfs_init();
498 /* Start DVFS and DPTC */
499 void dvfs_dptc_start(void)
501 dvfs_start();
502 dptc_start();
506 /* Stop DVFS and DPTC */
507 void dvfs_dptc_stop(void)
509 dptc_stop();
510 dvfs_stop();
513 /* Start the DVFS hardware */
514 void dvfs_start(void)
516 int oldlevel;
518 /* Have to wait at least 3 div3 clocks before enabling after being
519 * stopped. */
520 udelay(1500);
522 oldlevel = disable_irq_save();
524 if (!dvfs_running)
526 dvfs_running = true;
528 /* Unmask DVFS interrupt source and enable DVFS. */
529 avic_enable_int(INT_CCM_DVFS, INT_TYPE_IRQ, INT_PRIO_DVFS,
530 CCM_DVFS_HANDLER);
532 CCM_PMCR0 = (CCM_PMCR0 & ~CCM_PMCR0_FSVAIM) | CCM_PMCR0_DVFEN;
535 restore_irq(oldlevel);
537 logf("DVFS: started");
541 /* Stop the DVFS hardware and return to default frequency */
542 void dvfs_stop(void)
544 int oldlevel = disable_irq_save();
546 if (dvfs_running)
548 /* Mask DVFS interrupts. */
549 CCM_PMCR0 |= CCM_PMCR0_FSVAIM | CCM_PMCR0_LBMI;
550 avic_disable_int(INT_CCM_DVFS);
552 if (((CCM_PMCR0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS) !=
553 DVFS_LEVEL_DEFAULT)
555 /* Set default frequency level */
556 wait_for_dvfs_update_en();
557 do_dvfs_update(DVFS_LEVEL_DEFAULT);
558 wait_for_dvfs_update_en();
561 /* Disable DVFS. */
562 CCM_PMCR0 &= ~CCM_PMCR0_DVFEN;
563 dvfs_running = false;
566 restore_irq(oldlevel);
568 logf("DVFS: stopped");
572 /* Mask the DVFS interrupt without affecting running status */
573 void dvfs_int_mask(bool mask)
575 if (mask)
577 /* Just disable, not running = already disabled */
578 avic_mask_int(INT_CCM_DVFS);
580 else if (dvfs_running)
582 /* DVFS is running; unmask it */
583 avic_unmask_int(INT_CCM_DVFS);
588 /* Set a signal load tracking weight */
589 void dvfs_set_lt_weight(enum DVFS_LT_SIGS index, unsigned long value)
591 volatile unsigned long *reg_p = &CCM_LTR2;
592 unsigned int shift = 3 * index;
594 if (index < 9)
596 reg_p = &CCM_LTR3;
597 shift += 5; /* Bits 7:5, 10:8 ... 31:29 */
599 else if (index < 16)
601 shift -= 16; /* Bits 13:11, 16:14 ... 31:29 */
604 bitmod32(reg_p, value << shift, 0x7 << shift);
608 /* Return a signal load tracking weight */
609 unsigned long dvfs_get_lt_weight(enum DVFS_LT_SIGS index)
611 volatile unsigned long *reg_p = &CCM_LTR2;
612 unsigned int shift = 3 * index;
614 if (index < 9)
616 reg_p = &CCM_LTR3;
617 shift += 5; /* Bits 7:5, 10:8 ... 31:29 */
619 else if (index < 16)
621 shift -= 16; /* Bits 13:11, 16:14 ... 31:29 */
624 return (*reg_p & (0x7 << shift)) >> shift;
628 /* Set a signal load detection mode */
629 void dvfs_set_lt_detect(enum DVFS_LT_SIGS index, bool edge)
631 unsigned long bit = 0;
633 if ((unsigned)index < 13)
634 bit = 1ul << (index + 3);
635 else if ((unsigned)index < 16)
636 bit = 1ul << (index + 29);
638 bitmod32(&CCM_LTR0, edge ? bit : 0, bit);
642 /* Returns a signal load detection mode */
643 bool dvfs_get_lt_detect(enum DVFS_LT_SIGS index)
645 unsigned int shift = 32;
647 if ((unsigned)index < 13)
648 shift = index + 3;
649 else if ((unsigned)index < 16)
650 shift = index + 29;
652 return !!((CCM_LTR0 & (1ul << shift)) >> shift);
656 /* Set/clear the general-purpose load tracking bit */
657 void dvfs_set_gp_bit(enum DVFS_DVGPS dvgp, bool assert)
659 if ((unsigned)dvgp <= 3)
661 unsigned long bit = 1ul << dvgp;
662 bitmod32(&CCM_PMCR1, assert ? bit : 0, bit);
667 /* Return the general-purpose load tracking bit */
668 bool dvfs_get_gp_bit(enum DVFS_DVGPS dvgp)
670 if ((unsigned)dvgp <= 3)
671 return (CCM_PMCR1 & (1ul << dvgp)) != 0;
672 return false;
676 /* Set GP load tracking by code.
677 * level_code:
678 * lt 0 =defaults
679 * 0 =all off ->
680 * 28 =highest load
681 * gte 28=highest load
682 * detect_mask bits:
683 * b[3:0]: 1=LTn edge detect, 0=LTn level detect
685 void dvfs_set_gp_sense(int level_code, unsigned long detect_mask)
687 int i;
689 for (i = 0; i <= 3; i++)
691 int ltsig_num = DVFS_LT_SIG_DVGP0 + i;
692 int gpw_num = DVFS_DVGP_0 + i;
693 unsigned long weight;
694 bool edge;
695 bool assert;
697 if (level_code < 0)
699 /* defaults */
700 detect_mask = 0;
701 assert = 0;
702 weight = lt_signals[ltsig_num].weight;
703 edge = lt_signals[ltsig_num].detect != 0;
705 else
707 weight = MIN(level_code, 7);
708 edge = !!(detect_mask & 1);
709 assert = weight > 0;
710 detect_mask >>= 1;
711 level_code -= 7;
712 if (level_code < 0)
713 level_code = 0;
716 dvfs_set_lt_weight(ltsig_num, weight); /* set weight */
717 dvfs_set_lt_detect(ltsig_num, edge); /* set detect mode */
718 dvfs_set_gp_bit(gpw_num, assert); /* set activity */
722 /* Return GP weight settings */
723 void dvfs_get_gp_sense(int *level_code, unsigned long *detect_mask)
725 int i;
726 int code = 0;
727 unsigned long mask = 0;
729 for (i = DVFS_LT_SIG_DVGP0; i <= DVFS_LT_SIG_DVGP3; i++)
731 code += dvfs_get_lt_weight(i);
732 mask = (mask << 1) | (dvfs_get_lt_detect(i) ? 1 : 0);
735 if (level_code)
736 *level_code = code;
738 if (detect_mask)
739 *detect_mask = mask;
743 /* Turn the wait-for-interrupt monitoring on or off */
744 void dvfs_wfi_monitor(bool on)
746 bitmod32(&CCM_PMCR0, on ? 0 : CCM_PMCR0_WFIM, CCM_PMCR0_WFIM);
750 /* Obtain the current core voltage setting, in millivolts 8-) */
751 unsigned int dvfs_dptc_get_voltage(void)
753 unsigned int v;
755 int oldlevel = disable_irq_save();
756 v = dvfs_dptc_voltage_table[dptc_wp].sw[dvfs_level];
757 restore_irq(oldlevel);
759 /* 25mV steps from 0.900V to 1.675V */
760 return v * 25 + 900;
764 /* Get the current DVFS level */
765 unsigned int dvfs_get_level(void)
767 return dvfs_level;
771 /* Is DVFS enabled? */
772 bool dvfs_enabled(void)
774 return dvfs_running;
778 /* Get bitmask of levels supported */
779 unsigned int dvfs_level_mask(void)
781 return DVFS_LEVEL_MASK;
785 /* If DVFS is disabled, set the level explicitly */
786 void dvfs_set_level(unsigned int level)
788 int oldlevel = disable_irq_save();
790 if (!dvfs_running && level < DVFS_NUM_LEVELS)
792 unsigned int currlevel =
793 (CCM_PMCR0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS;
794 if (level != currlevel && ((1 << level) & DVFS_LEVEL_MASK))
795 set_current_dvfs_level(level);
798 restore_irq(oldlevel);
802 /* Start DPTC module */
803 void dptc_start(void)
805 int oldlevel = disable_irq_save();
807 if (!dptc_running)
809 dptc_running = true;
811 /* Enable DPTC and unmask interrupt. */
812 avic_enable_int(INT_CCM_CLK, INT_TYPE_IRQ, INT_PRIO_DPTC,
813 CCM_CLK_HANDLER);
815 update_dptc_counts(dvfs_level, dptc_wp);
816 enable_dptc();
819 restore_irq(oldlevel);
821 logf("DPTC: started");
825 /* Stop the DPTC hardware if running and go back to default working point */
826 void dptc_stop(void)
828 int oldlevel = disable_irq_save();
830 if (dptc_running)
832 dptc_running = false;
834 /* Disable DPTC and mask interrupt. */
835 CCM_PMCR0 = (CCM_PMCR0 & ~CCM_PMCR0_DPTEN) | CCM_PMCR0_PTVAIM;
836 avic_disable_int(INT_CCM_CLK);
838 /* Go back to default working point. */
839 dptc_new_wp(DPTC_WP_DEFAULT);
842 restore_irq(oldlevel);
844 logf("DPTC: stopped");
848 /* Get the current DPTC working point */
849 unsigned int dptc_get_wp(void)
851 return dptc_wp;
855 /* Is DPTC enabled? */
856 bool dptc_enabled(void)
858 return dptc_running;
862 /* If DPTC is not running, set the working point explicitly */
863 void dptc_set_wp(unsigned int wp)
865 int oldlevel = disable_irq_save();
867 if (!dptc_running && wp < DPTC_NUM_WP)
868 dptc_new_wp(wp);
870 restore_irq(oldlevel);