diff options
Diffstat (limited to 'firmware/target/arm/imx31/dvfs_dptc-imx31.c')
-rw-r--r-- | firmware/target/arm/imx31/dvfs_dptc-imx31.c | 90 |
1 files changed, 51 insertions, 39 deletions
diff --git a/firmware/target/arm/imx31/dvfs_dptc-imx31.c b/firmware/target/arm/imx31/dvfs_dptc-imx31.c index 02955a5aa4..3f9e9a3dd8 100644 --- a/firmware/target/arm/imx31/dvfs_dptc-imx31.c +++ b/firmware/target/arm/imx31/dvfs_dptc-imx31.c | |||
@@ -75,6 +75,7 @@ static uint32_t check_regulator_setting(uint32_t setting) | |||
75 | 75 | ||
76 | 76 | ||
77 | /** DVFS **/ | 77 | /** DVFS **/ |
78 | #define DVFS_TVWAIT 100 /* Voltage ramp wait time */ | ||
78 | static bool dvfs_running = false; /* Has driver enabled DVFS? */ | 79 | static bool dvfs_running = false; /* Has driver enabled DVFS? */ |
79 | 80 | ||
80 | /* Request tracking since boot */ | 81 | /* Request tracking since boot */ |
@@ -90,10 +91,9 @@ static inline void updten_wait(void) | |||
90 | } | 91 | } |
91 | 92 | ||
92 | /* Do the actual frequency and DVFS pin change - always call with IRQ masked */ | 93 | /* Do the actual frequency and DVFS pin change - always call with IRQ masked */ |
93 | static void do_dvfs_update(unsigned int level) | 94 | static void do_dvfs_update(unsigned long pmcr0, unsigned int level) |
94 | { | 95 | { |
95 | const struct dvfs_clock_table_entry *setting = &dvfs_clock_table[level]; | 96 | const struct dvfs_clock_table_entry *setting = &dvfs_clock_table[level]; |
96 | unsigned long pmcr0 = CCM_PMCR0; | ||
97 | 97 | ||
98 | if (pmcr0 & CCM_PMCR0_DPTEN) | 98 | if (pmcr0 & CCM_PMCR0_DPTEN) |
99 | { | 99 | { |
@@ -106,7 +106,7 @@ static void do_dvfs_update(unsigned int level) | |||
106 | pmcr0 &= ~CCM_PMCR0_VSCNT; | 106 | pmcr0 &= ~CCM_PMCR0_VSCNT; |
107 | 107 | ||
108 | if (level < ((pmcr0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS)) | 108 | if (level < ((pmcr0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS)) |
109 | { | 109 | { |
110 | pmcr0 |= CCM_PMCR0_UDSC; /* Up scaling, increase */ | 110 | pmcr0 |= CCM_PMCR0_UDSC; /* Up scaling, increase */ |
111 | pmcr0 |= setting->vscnt << CCM_PMCR0_VSCNT_POS; | 111 | pmcr0 |= setting->vscnt << CCM_PMCR0_VSCNT_POS; |
112 | } | 112 | } |
@@ -136,12 +136,20 @@ static void do_dvfs_update(unsigned int level) | |||
136 | } | 136 | } |
137 | 137 | ||
138 | CCM_PMCR0 = pmcr0; | 138 | CCM_PMCR0 = pmcr0; |
139 | /* Note: changes to frequency with ints unmaked seem to cause spurious | 139 | |
140 | * DVFS interrupts with value CCM_PMCR0_FSVAI_NO_INT. These aren't | 140 | /* dvfs_int_voltage_wait_complete must be call to complete this; how that |
141 | * supposed to happen. Only do the lengthy delay with them enabled. */ | 141 | is accomplished depends upon whether this was an interrupt with DVFS |
142 | enable_irq(); | 142 | enabled or a manual setting of the CPU frequency */ |
143 | udelay(100); /* Software wait for voltage ramp-up */ | 143 | } |
144 | disable_irq(); | 144 | |
145 | /* Perform final DVFS frequency change steps after voltage ramp wait */ | ||
146 | static void dvfs_int_voltage_wait_complete(void) | ||
147 | { | ||
148 | const struct dvfs_clock_table_entry *setting = | ||
149 | &dvfs_clock_table[dvfs_level]; | ||
150 | |||
151 | unsigned long pmcr0 = CCM_PMCR0; | ||
152 | |||
145 | CCM_PDR0 = setting->pdr_val; | 153 | CCM_PDR0 = setting->pdr_val; |
146 | 154 | ||
147 | if (!(pmcr0 & CCM_PMCR0_DFSUP_POST_DIVIDERS)) | 155 | if (!(pmcr0 & CCM_PMCR0_DFSUP_POST_DIVIDERS)) |
@@ -155,6 +163,9 @@ static void do_dvfs_update(unsigned int level) | |||
155 | 163 | ||
156 | cpu_frequency = ccm_get_mcu_clk(); | 164 | cpu_frequency = ccm_get_mcu_clk(); |
157 | 165 | ||
166 | if (dvfs_running) | ||
167 | CCM_PMCR0 &= ~CCM_PMCR0_FSVAIM; | ||
168 | |||
158 | if (pmcr0 & CCM_PMCR0_DPTEN) | 169 | if (pmcr0 & CCM_PMCR0_DPTEN) |
159 | { | 170 | { |
160 | update_dptc_counts(); | 171 | update_dptc_counts(); |
@@ -165,24 +176,28 @@ static void do_dvfs_update(unsigned int level) | |||
165 | /* Start DVFS, change the set point and stop it */ | 176 | /* Start DVFS, change the set point and stop it */ |
166 | static void set_current_dvfs_level(unsigned int level) | 177 | static void set_current_dvfs_level(unsigned int level) |
167 | { | 178 | { |
168 | int oldlevel; | ||
169 | |||
170 | /* Have to wait at least 3 div3 clocks before enabling after being | 179 | /* Have to wait at least 3 div3 clocks before enabling after being |
171 | * stopped. */ | 180 | * stopped before calling. */ |
172 | udelay(1500); | ||
173 | 181 | ||
174 | oldlevel = disable_irq_save(); | 182 | updten_wait(); |
183 | |||
184 | int oldlevel = disable_irq_save(); | ||
175 | CCM_PMCR0 |= CCM_PMCR0_DVFEN; | 185 | CCM_PMCR0 |= CCM_PMCR0_DVFEN; |
176 | do_dvfs_update(level); | 186 | do_dvfs_update(CCM_PMCR0, level); |
177 | restore_irq(oldlevel); | 187 | restore_irq(oldlevel); |
178 | 188 | ||
179 | updten_wait(); | 189 | udelay(DVFS_TVWAIT); |
190 | |||
191 | oldlevel = disable_irq_save(); | ||
192 | dvfs_int_voltage_wait_complete(); | ||
193 | restore_irq(oldlevel); | ||
180 | 194 | ||
195 | updten_wait(); | ||
181 | bitclr32(&CCM_PMCR0, CCM_PMCR0_DVFEN); | 196 | bitclr32(&CCM_PMCR0, CCM_PMCR0_DVFEN); |
182 | } | 197 | } |
183 | 198 | ||
184 | /* DVFS Interrupt handler */ | 199 | /* Interrupt handler for DVFS */ |
185 | static void USED_ATTR dvfs_int(void) | 200 | static void dvfs_int(void) |
186 | { | 201 | { |
187 | unsigned long pmcr0 = CCM_PMCR0; | 202 | unsigned long pmcr0 = CCM_PMCR0; |
188 | unsigned long fsvai = pmcr0 & CCM_PMCR0_FSVAI; | 203 | unsigned long fsvai = pmcr0 & CCM_PMCR0_FSVAI; |
@@ -235,14 +250,19 @@ static void USED_ATTR dvfs_int(void) | |||
235 | return; /* Do nothing. Freq change is not required */ | 250 | return; /* Do nothing. Freq change is not required */ |
236 | } /* end switch */ | 251 | } /* end switch */ |
237 | 252 | ||
238 | do_dvfs_update(level); | 253 | /* Mask DVFS interrupt until voltage wait is complete */ |
254 | pmcr0 |= CCM_PMCR0_FSVAIM; | ||
255 | |||
256 | do_dvfs_update(pmcr0, level); | ||
257 | |||
258 | /* Complete this in a few microseconds from now */ | ||
259 | uevent(DVFS_TVWAIT, dvfs_int_voltage_wait_complete); | ||
239 | } | 260 | } |
240 | 261 | ||
241 | /* Interrupt vector for DVFS */ | 262 | /* Interrupt vector for DVFS */ |
242 | static __attribute__((naked, interrupt("IRQ"))) void CCM_DVFS_HANDLER(void) | 263 | static __attribute__((interrupt("IRQ"))) void CCM_DVFS_HANDLER(void) |
243 | { | 264 | { |
244 | /* Audio can glitch with the long udelay if nested IRQ isn't allowed. */ | 265 | dvfs_int(); |
245 | AVIC_NESTED_NI_CALL(dvfs_int, INT_PRIO_DVFS); | ||
246 | } | 266 | } |
247 | 267 | ||
248 | /* Initialize the DVFS hardware */ | 268 | /* Initialize the DVFS hardware */ |
@@ -273,7 +293,7 @@ static void INIT_ATTR dvfs_init(void) | |||
273 | 293 | ||
274 | /* GP load bits disabled */ | 294 | /* GP load bits disabled */ |
275 | bitclr32(&CCM_PMCR1, 0xf); | 295 | bitclr32(&CCM_PMCR1, 0xf); |
276 | 296 | ||
277 | /* Initialize DVFS signal weights and detection modes. */ | 297 | /* Initialize DVFS signal weights and detection modes. */ |
278 | int i; | 298 | int i; |
279 | for (i = 0; i < 16; i++) | 299 | for (i = 0; i < 16; i++) |
@@ -312,6 +332,7 @@ static void INIT_ATTR dvfs_init(void) | |||
312 | dvfs_clock_table[DVFS_LEVEL_DEFAULT].pdr_val); | 332 | dvfs_clock_table[DVFS_LEVEL_DEFAULT].pdr_val); |
313 | 333 | ||
314 | /* Set initial level and working point. */ | 334 | /* Set initial level and working point. */ |
335 | udelay(1500); | ||
315 | set_current_dvfs_level(DVFS_LEVEL_DEFAULT); | 336 | set_current_dvfs_level(DVFS_LEVEL_DEFAULT); |
316 | 337 | ||
317 | logf("DVFS: Initialized"); | 338 | logf("DVFS: Initialized"); |
@@ -537,33 +558,23 @@ void dvfs_stop(void) | |||
537 | if (!dvfs_running) | 558 | if (!dvfs_running) |
538 | return; | 559 | return; |
539 | 560 | ||
561 | uevent_cancel(); | ||
562 | |||
540 | /* Mask DVFS interrupts. */ | 563 | /* Mask DVFS interrupts. */ |
541 | avic_disable_int(INT_CCM_DVFS); | 564 | avic_disable_int(INT_CCM_DVFS); |
542 | bitset32(&CCM_PMCR0, CCM_PMCR0_FSVAIM | CCM_PMCR0_LBMI); | 565 | bitset32(&CCM_PMCR0, CCM_PMCR0_FSVAIM | CCM_PMCR0_LBMI); |
543 | 566 | ||
544 | if (((CCM_PMCR0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS) != | ||
545 | DVFS_LEVEL_DEFAULT) | ||
546 | { | ||
547 | int oldlevel; | ||
548 | /* Set default frequency level */ | ||
549 | updten_wait(); | ||
550 | oldlevel = disable_irq_save(); | ||
551 | do_dvfs_update(DVFS_LEVEL_DEFAULT); | ||
552 | restore_irq(oldlevel); | ||
553 | updten_wait(); | ||
554 | } | ||
555 | |||
556 | /* Disable DVFS. */ | ||
557 | bitclr32(&CCM_PMCR0, CCM_PMCR0_DVFEN); | ||
558 | dvfs_running = false; | 567 | dvfs_running = false; |
559 | 568 | ||
569 | /* Set default frequency level */ | ||
570 | set_current_dvfs_level(DVFS_LEVEL_DEFAULT); | ||
560 | logf("DVFS: stopped"); | 571 | logf("DVFS: stopped"); |
561 | } | 572 | } |
562 | 573 | ||
563 | /* Is DVFS enabled? */ | 574 | /* Is DVFS enabled? */ |
564 | bool dvfs_enabled(void) | 575 | bool dvfs_enabled(void) |
565 | { | 576 | { |
566 | return dvfs_running; | 577 | return dvfs_running; |
567 | } | 578 | } |
568 | 579 | ||
569 | /* If DVFS is disabled, set the level explicitly */ | 580 | /* If DVFS is disabled, set the level explicitly */ |
@@ -575,6 +586,7 @@ void dvfs_set_level(unsigned int level) | |||
575 | level == ((CCM_PMCR0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS)) | 586 | level == ((CCM_PMCR0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS)) |
576 | return; | 587 | return; |
577 | 588 | ||
589 | udelay(1500); | ||
578 | set_current_dvfs_level(level); | 590 | set_current_dvfs_level(level); |
579 | } | 591 | } |
580 | 592 | ||
@@ -778,7 +790,7 @@ void dptc_start(void) | |||
778 | enable_dptc(); | 790 | enable_dptc(); |
779 | restore_irq(oldlevel); | 791 | restore_irq(oldlevel); |
780 | 792 | ||
781 | avic_enable_int(INT_CCM_CLK, INT_TYPE_IRQ, INT_PRIO_DPTC, | 793 | avic_enable_int(INT_CCM_CLK, INT_TYPE_IRQ, INT_PRIO_DPTC, |
782 | CCM_CLK_HANDLER); | 794 | CCM_CLK_HANDLER); |
783 | 795 | ||
784 | logf("DPTC: started"); | 796 | logf("DPTC: started"); |