1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
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
;
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
;
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
);
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
;
125 /* Post-dividers update only */
126 pmcr0
|= CCM_PMCR0_DFSUP_POST_DIVIDERS
;
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. */
136 udelay(100); /* Software wait for voltage ramp-up */
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
;
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
;
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. */
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);
208 /* Single-step frequency increase */
209 case CCM_PMCR0_FSVAI_INCREASE
:
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);
220 /* Right to highest if panic */
221 case CCM_PMCR0_FSVAI_INCREASE_NOW
:
223 return; /* DVFS already at highest level */
225 /* Upon the INCREASE_NOW event, the frequency will be increased to
226 * the maximum (index 0). */
231 case CCM_PMCR0_FSVAI_NO_INT
:
233 return; /* Do nothing. Freq change is not required */
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 INIT_ATTR
dvfs_init(void)
253 if (CCM_PMCR0
& CCM_PMCR0_DVFEN
)
255 /* Turn it off first. Really, shouldn't happen though. */
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
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 bitclr32(&GPIO1_GDIR
, (1 << 5));
279 iomuxc_set_pin_mux(IOMUXC_GPIO1_5
,
280 IOMUXC_MUX_OUT_FUNCTIONAL
| IOMUXC_MUX_IN_FUNCTIONAL
);
283 /* Initialize DVFS signal weights and detection modes. */
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
);
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
);
300 DVFS_DNCNT
<< CCM_LTR1_DNCNT_POS
|
301 DVFS_UPCNT
<< CCM_LTR1_UPCNT_POS
|
302 DVFS_PNCTHR
<< CCM_LTR1_PNCTHR_POS
|
304 CCM_LTR1_DNCNT
| CCM_LTR1_UPCNT
|
305 CCM_LTR1_PNCTHR
| CCM_LTR1_LTBRSR
);
307 /* Set up LTR2-- EMA configuration. */
308 bitmod32(&CCM_LTR2
, DVFS_EMAC
<< CCM_LTR2_EMAC_POS
, CCM_LTR2_EMAC
);
310 /* DVFS interrupt goes to MCU. Mask load buffer full interrupt. */
311 bitset32(&CCM_PMCR0
, CCM_PMCR0_DVFIS
| CCM_PMCR0_LBMI
);
313 /* Initialize current core PLL and dividers for default level. Assumes
314 * clocking scheme has been set up appropriately in other init code. */
315 ccm_set_mcupll_and_pdr(dvfs_clock_table
[DVFS_LEVEL_DEFAULT
].pll_val
,
316 dvfs_clock_table
[DVFS_LEVEL_DEFAULT
].pdr_val
);
318 /* Set initial level and working point. */
319 set_current_dvfs_level(DVFS_LEVEL_DEFAULT
);
321 logf("DVFS: Initialized");
325 /* Start the DVFS hardware */
326 static void dvfs_start(void)
330 /* Have to wait at least 3 div3 clocks before enabling after being
334 oldlevel
= disable_irq_save();
340 /* Unmask DVFS interrupt source and enable DVFS. */
341 avic_enable_int(INT_CCM_DVFS
, INT_TYPE_IRQ
, INT_PRIO_DVFS
,
344 CCM_PMCR0
= (CCM_PMCR0
& ~CCM_PMCR0_FSVAIM
) | CCM_PMCR0_DVFEN
;
347 restore_irq(oldlevel
);
349 logf("DVFS: started");
353 /* Stop the DVFS hardware and return to default frequency */
354 static void dvfs_stop(void)
356 int oldlevel
= disable_irq_save();
360 /* Mask DVFS interrupts. */
361 CCM_PMCR0
|= CCM_PMCR0_FSVAIM
| CCM_PMCR0_LBMI
;
362 avic_disable_int(INT_CCM_DVFS
);
364 if (((CCM_PMCR0
& CCM_PMCR0_DVSUP
) >> CCM_PMCR0_DVSUP_POS
) !=
367 /* Set default frequency level */
368 wait_for_dvfs_update_en();
369 do_dvfs_update(DVFS_LEVEL_DEFAULT
, false);
370 wait_for_dvfs_update_en();
374 CCM_PMCR0
&= ~CCM_PMCR0_DVFEN
;
375 dvfs_running
= false;
378 restore_irq(oldlevel
);
380 logf("DVFS: stopped");
386 /* Request tracking since boot */
387 static bool dptc_running
= false; /* Has driver enabled DPTC? */
389 unsigned int dptc_nr_dn
= 0;
390 unsigned int dptc_nr_up
= 0;
391 unsigned int dptc_nr_pnc
= 0;
392 unsigned int dptc_nr_no
= 0;
394 static struct spi_transfer_desc dptc_pmic_xfer
; /* Transfer descriptor */
395 static const unsigned char dptc_pmic_regs
[2] = /* Register subaddresses */
396 { MC13783_SWITCHERS0
, MC13783_SWITCHERS1
};
397 static uint32_t dptc_reg_shadows
[2]; /* shadow regs */
398 static uint32_t dptc_regs_buf
[2]; /* buffer for async write */
401 /* Enable DPTC and unmask interrupt. */
402 static void enable_dptc(void)
404 int oldlevel
= disable_irq_save();
406 /* Enable DPTC, assert voltage change request. */
407 CCM_PMCR0
= (CCM_PMCR0
& ~CCM_PMCR0_PTVAIM
) | CCM_PMCR0_DPTEN
|
412 /* Set voltage valid *after* setting change request */
413 CCM_PMCR0
|= CCM_PMCR0_DPVV
;
415 restore_irq(oldlevel
);
419 /* Called after asynchronous PMIC write is completed */
420 static void dptc_transfer_done_callback(struct spi_transfer_desc
*xfer
)
422 if (xfer
->count
!= 0)
425 update_dptc_counts(dvfs_level
, dptc_wp
);
432 /* Handle the DPTC interrupt and sometimes the manual setting */
433 static void dptc_int(unsigned long pmcr0
)
435 const union dvfs_dptc_voltage_table_entry
*entry
;
436 uint32_t sw1a
, sw1advs
, sw1bdvs
, sw1bstby
;
437 uint32_t switchers0
, switchers1
;
441 /* Mask DPTC interrupt and disable DPTC until the change request is
443 CCM_PMCR0
= (pmcr0
& ~CCM_PMCR0_DPTEN
) | CCM_PMCR0_PTVAIM
;
445 switch (pmcr0
& CCM_PMCR0_PTVAI
)
447 case CCM_PMCR0_PTVAI_DECREASE
:
452 case CCM_PMCR0_PTVAI_INCREASE
:
457 case CCM_PMCR0_PTVAI_INCREASE_NOW
:
458 if (--wp
> DPTC_WP_PANIC
)
463 case CCM_PMCR0_PTVAI_NO_INT
:
464 break; /* Just maintain at global level */
469 else if (wp
>= DPTC_NUM_WP
)
470 wp
= DPTC_NUM_WP
- 1;
472 entry
= &dvfs_dptc_voltage_table
[wp
];
474 sw1a
= check_regulator_setting(entry
->sw1a
);
475 sw1advs
= check_regulator_setting(entry
->sw1advs
);
476 sw1bdvs
= check_regulator_setting(entry
->sw1bdvs
);
477 sw1bstby
= check_regulator_setting(entry
->sw1bstby
);
479 switchers0
= dptc_reg_shadows
[0] & ~(MC13783_SW1A
| MC13783_SW1ADVS
);
480 dptc_regs_buf
[0] = switchers0
|
481 sw1a
<< MC13783_SW1A_POS
| /* SW1A */
482 sw1advs
<< MC13783_SW1ADVS_POS
; /* SW1ADVS */
483 switchers1
= dptc_reg_shadows
[1] & ~(MC13783_SW1BDVS
| MC13783_SW1BSTBY
);
484 dptc_regs_buf
[1] = switchers1
|
485 sw1bdvs
<< MC13783_SW1BDVS_POS
| /* SW1BDVS */
486 sw1bstby
<< MC13783_SW1BSTBY_POS
; /* SW1BSTBY */
490 mc13783_write_async(&dptc_pmic_xfer
, dptc_pmic_regs
,
491 dptc_regs_buf
, 2, dptc_transfer_done_callback
);
495 static void dptc_new_wp(unsigned int wp
)
498 /* "NO_INT" so the working point isn't incremented, just set. */
499 dptc_int((CCM_PMCR0
& ~CCM_PMCR0_PTVAI
) | CCM_PMCR0_PTVAI_NO_INT
);
503 /* Interrupt vector for DPTC */
504 static __attribute__((interrupt("IRQ"))) void CCM_CLK_HANDLER(void)
506 unsigned long pmcr0
= CCM_PMCR0
;
508 if ((pmcr0
& CCM_PMCR0_PTVAI
) == CCM_PMCR0_PTVAI_NO_INT
)
515 /* Initialize the DPTC hardware */
516 static void INIT_ATTR
dptc_init(void)
518 /* Force DPTC off if running for some reason. */
519 bitmod32(&CCM_PMCR0
, CCM_PMCR0_PTVAIM
,
520 CCM_PMCR0_PTVAIM
| CCM_PMCR0_DPTEN
);
522 /* Shadow the regulator registers */
523 mc13783_read_regs(dptc_pmic_regs
, dptc_reg_shadows
, 2);
525 /* Set default, safe working point. */
526 dptc_new_wp(DPTC_WP_DEFAULT
);
528 /* Interrupt goes to MCU, specified reference circuits enabled when
530 bitset32(&CCM_PMCR0
, CCM_PMCR0_PTVIS
);
532 bitmod32(&CCM_PMCR0
, DPTC_DRCE_MASK
,
533 CCM_PMCR0_DRCE0
| CCM_PMCR0_DRCE1
|
534 CCM_PMCR0_DRCE2
| CCM_PMCR0_DRCE3
);
536 /* DPTC counting range = 256 system clocks */
537 bitclr32(&CCM_PMCR0
, CCM_PMCR0_DCR
);
539 logf("DPTC: Initialized");
543 /* Start DPTC module */
544 static void dptc_start(void)
546 int oldlevel
= disable_irq_save();
552 /* Enable DPTC and unmask interrupt. */
553 avic_enable_int(INT_CCM_CLK
, INT_TYPE_IRQ
, INT_PRIO_DPTC
,
556 update_dptc_counts(dvfs_level
, dptc_wp
);
560 restore_irq(oldlevel
);
562 logf("DPTC: started");
566 /* Stop the DPTC hardware if running and go back to default working point */
567 static void dptc_stop(void)
569 int oldlevel
= disable_irq_save();
573 dptc_running
= false;
575 /* Disable DPTC and mask interrupt. */
576 CCM_PMCR0
= (CCM_PMCR0
& ~CCM_PMCR0_DPTEN
) | CCM_PMCR0_PTVAIM
;
577 avic_disable_int(INT_CCM_CLK
);
579 /* Go back to default working point. */
580 dptc_new_wp(DPTC_WP_DEFAULT
);
583 restore_irq(oldlevel
);
585 logf("DPTC: stopped");
589 /** Main module interface **/
591 /* Initialize DVFS and DPTC */
592 void INIT_ATTR
dvfs_dptc_init(void)
599 /* Start DVFS and DPTC */
600 void dvfs_dptc_start(void)
607 /* Stop DVFS and DPTC */
608 void dvfs_dptc_stop(void)
615 /* Mask the DVFS interrupt without affecting running status */
616 void dvfs_int_mask(bool mask
)
620 /* Just disable, not running = already disabled */
621 avic_mask_int(INT_CCM_DVFS
);
623 else if (dvfs_running
)
625 /* DVFS is running; unmask it */
626 avic_unmask_int(INT_CCM_DVFS
);
631 /* Set a signal load tracking weight */
632 void dvfs_set_lt_weight(enum DVFS_LT_SIGS index
, unsigned long value
)
634 volatile unsigned long *reg_p
= &CCM_LTR2
;
635 unsigned int shift
= 3 * index
;
640 shift
+= 5; /* Bits 7:5, 10:8 ... 31:29 */
644 shift
-= 16; /* Bits 13:11, 16:14 ... 31:29 */
647 bitmod32(reg_p
, value
<< shift
, 0x7 << shift
);
651 /* Set a signal load detection mode */
652 void dvfs_set_lt_detect(enum DVFS_LT_SIGS index
, bool edge
)
654 unsigned long bit
= 0;
656 if ((unsigned)index
< 13)
657 bit
= 1ul << (index
+ 3);
658 else if ((unsigned)index
< 16)
659 bit
= 1ul << (index
+ 29);
661 bitmod32(&CCM_LTR0
, edge
? bit
: 0, bit
);
665 void dvfs_set_gp_bit(enum DVFS_DVGPS dvgp
, bool assert)
667 if ((unsigned)dvgp
<= 3)
669 unsigned long bit
= 1ul << dvgp
;
670 bitmod32(&CCM_PMCR1
, assert ? bit
: 0, bit
);
675 /* Turn the wait-for-interrupt monitoring on or off */
676 void dvfs_wfi_monitor(bool on
)
678 bitmod32(&CCM_PMCR0
, on
? 0 : CCM_PMCR0_WFIM
, CCM_PMCR0_WFIM
);
682 /* Obtain the current core voltage setting, in millivolts 8-) */
683 unsigned int dvfs_dptc_get_voltage(void)
687 int oldlevel
= disable_irq_save();
688 v
= dvfs_dptc_voltage_table
[dptc_wp
].sw
[dvfs_level
];
689 restore_irq(oldlevel
);
691 /* 25mV steps from 0.900V to 1.675V */
696 /* Get the current DVFS level */
697 unsigned int dvfs_get_level(void)
703 /* If DVFS is disabled, set the level explicitly */
704 void dvfs_set_level(unsigned int level
)
706 int oldlevel
= disable_irq_save();
708 unsigned int currlevel
=
709 (CCM_PMCR0
& CCM_PMCR0_DVSUP
) >> CCM_PMCR0_DVSUP_POS
;
711 if (!dvfs_running
&& level
< DVFS_NUM_LEVELS
&& level
!= currlevel
)
712 set_current_dvfs_level(level
);
714 restore_irq(oldlevel
);
718 /* Get the current DPTC working point */
719 unsigned int dptc_get_wp(void)
725 /* If DPTC is not running, set the working point explicitly */
726 void dptc_set_wp(unsigned int wp
)
728 int oldlevel
= disable_irq_save();
730 if (!dptc_running
&& wp
< DPTC_NUM_WP
)
733 restore_irq(oldlevel
);