diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2010-05-06 03:23:51 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2010-05-06 03:23:51 +0000 |
commit | a36a498c577ae5c9daa8487c8440df46d325bab3 (patch) | |
tree | ba8a35620ee10da2a7e1d6e6ed9234f0f35647d9 /firmware/target/arm/imx31/dvfs_dptc-imx31.c | |
parent | 8fd3ec97271001d0b50d4404f5891c9a4e77d960 (diff) | |
download | rockbox-a36a498c577ae5c9daa8487c8440df46d325bab3.tar.gz rockbox-a36a498c577ae5c9daa8487c8440df46d325bab3.zip |
i.MX31/Gigabeat S: This should fix stability problems. One problem was to start using the DVFS controller properly so that interrupts will be masked at the lowest and highest frequency indexes. Millions of useless interrupts were occurring at 132MHz because its index was 2, not 3, which masks it automatically when it can't go slower. Stopping the flood was enough to actually see the difference in general. IRQ must be disabled when fiddling with the CCM registers and only enabled when waiting for voltage ramp as having them enables also causes spurious DVFS ints. Implement interruptible ISR pro/epilogue more safely (always using IRQ stack even in SVC mode handling). Fix an improper inequality in DVFS code (which set regs for down when going up and v.v.). Misc. support changes. Have internal tables take less RAM.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@25837 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target/arm/imx31/dvfs_dptc-imx31.c')
-rw-r--r-- | firmware/target/arm/imx31/dvfs_dptc-imx31.c | 41 |
1 files changed, 29 insertions, 12 deletions
diff --git a/firmware/target/arm/imx31/dvfs_dptc-imx31.c b/firmware/target/arm/imx31/dvfs_dptc-imx31.c index 680b015c81..cae9a384c9 100644 --- a/firmware/target/arm/imx31/dvfs_dptc-imx31.c +++ b/firmware/target/arm/imx31/dvfs_dptc-imx31.c | |||
@@ -72,6 +72,7 @@ static bool dvfs_running = false; /* Has driver enabled DVFS? */ | |||
72 | unsigned int dvfs_nr_dn = 0; | 72 | unsigned int dvfs_nr_dn = 0; |
73 | unsigned int dvfs_nr_up = 0; | 73 | unsigned int dvfs_nr_up = 0; |
74 | unsigned int dvfs_nr_pnc = 0; | 74 | unsigned int dvfs_nr_pnc = 0; |
75 | unsigned int dvfs_nr_no = 0; | ||
75 | 76 | ||
76 | static void dvfs_stop(void); | 77 | static void dvfs_stop(void); |
77 | 78 | ||
@@ -83,7 +84,7 @@ static inline void wait_for_dvfs_update_en(void) | |||
83 | } | 84 | } |
84 | 85 | ||
85 | 86 | ||
86 | static void do_dvfs_update(unsigned int level) | 87 | static void do_dvfs_update(unsigned int level, bool in_isr) |
87 | { | 88 | { |
88 | const struct dvfs_clock_table_entry *setting = &dvfs_clock_table[level]; | 89 | const struct dvfs_clock_table_entry *setting = &dvfs_clock_table[level]; |
89 | unsigned long pmcr0 = CCM_PMCR0; | 90 | unsigned long pmcr0 = CCM_PMCR0; |
@@ -96,7 +97,7 @@ static void do_dvfs_update(unsigned int level) | |||
96 | 97 | ||
97 | pmcr0 &= ~CCM_PMCR0_VSCNT; | 98 | pmcr0 &= ~CCM_PMCR0_VSCNT; |
98 | 99 | ||
99 | if (level > ((pmcr0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS)) | 100 | if (level < ((pmcr0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS)) |
100 | { | 101 | { |
101 | pmcr0 |= CCM_PMCR0_UDSC; /* Up scaling, increase */ | 102 | pmcr0 |= CCM_PMCR0_UDSC; /* Up scaling, increase */ |
102 | pmcr0 |= setting->vscnt << CCM_PMCR0_VSCNT_POS; | 103 | pmcr0 |= setting->vscnt << CCM_PMCR0_VSCNT_POS; |
@@ -126,7 +127,15 @@ static void do_dvfs_update(unsigned int level) | |||
126 | } | 127 | } |
127 | 128 | ||
128 | CCM_PMCR0 = pmcr0; | 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(); | ||
129 | udelay(100); /* Software wait for voltage ramp-up */ | 136 | udelay(100); /* Software wait for voltage ramp-up */ |
137 | if (in_isr) | ||
138 | disable_irq(); | ||
130 | CCM_PDR0 = setting->pdr_val; | 139 | CCM_PDR0 = setting->pdr_val; |
131 | 140 | ||
132 | if (!(pmcr0 & CCM_PMCR0_DFSUP_POST_DIVIDERS)) | 141 | if (!(pmcr0 & CCM_PMCR0_DFSUP_POST_DIVIDERS)) |
@@ -160,7 +169,7 @@ static void set_current_dvfs_level(unsigned int level) | |||
160 | 169 | ||
161 | wait_for_dvfs_update_en(); | 170 | wait_for_dvfs_update_en(); |
162 | 171 | ||
163 | do_dvfs_update(level); | 172 | do_dvfs_update(level, false); |
164 | 173 | ||
165 | wait_for_dvfs_update_en(); | 174 | wait_for_dvfs_update_en(); |
166 | 175 | ||
@@ -191,7 +200,8 @@ static void __attribute__((used)) dvfs_int(void) | |||
191 | 200 | ||
192 | /* Upon the DECREASE event, the frequency will be changed to the next | 201 | /* Upon the DECREASE event, the frequency will be changed to the next |
193 | * higher state index. */ | 202 | * higher state index. */ |
194 | level++; | 203 | while (((1u << ++level) & DVFS_LEVEL_MASK) == 0); |
204 | |||
195 | dvfs_nr_dn++; | 205 | dvfs_nr_dn++; |
196 | break; | 206 | break; |
197 | 207 | ||
@@ -202,7 +212,8 @@ static void __attribute__((used)) dvfs_int(void) | |||
202 | 212 | ||
203 | /* Upon the INCREASE event, the frequency will be changed to the next | 213 | /* Upon the INCREASE event, the frequency will be changed to the next |
204 | * lower state index. */ | 214 | * lower state index. */ |
205 | level--; | 215 | while (((1u << --level) & DVFS_LEVEL_MASK) == 0); |
216 | |||
206 | dvfs_nr_up++; | 217 | dvfs_nr_up++; |
207 | break; | 218 | break; |
208 | 219 | ||
@@ -218,11 +229,11 @@ static void __attribute__((used)) dvfs_int(void) | |||
218 | break; | 229 | break; |
219 | 230 | ||
220 | case CCM_PMCR0_FSVAI_NO_INT: | 231 | case CCM_PMCR0_FSVAI_NO_INT: |
221 | default: | 232 | dvfs_nr_no++; |
222 | return; /* Do nothing. Freq change is not required */ | 233 | return; /* Do nothing. Freq change is not required */ |
223 | } /* end switch */ | 234 | } /* end switch */ |
224 | 235 | ||
225 | do_dvfs_update(level); | 236 | do_dvfs_update(level, true); |
226 | } | 237 | } |
227 | 238 | ||
228 | 239 | ||
@@ -230,9 +241,9 @@ static void __attribute__((used)) dvfs_int(void) | |||
230 | static __attribute__((naked, interrupt("IRQ"))) void CCM_DVFS_HANDLER(void) | 241 | static __attribute__((naked, interrupt("IRQ"))) void CCM_DVFS_HANDLER(void) |
231 | { | 242 | { |
232 | /* Audio can glitch with the long udelay if nested IRQ isn't allowed. */ | 243 | /* Audio can glitch with the long udelay if nested IRQ isn't allowed. */ |
233 | AVIC_NESTED_NI_CALL_PROLOGUE(INT_PRIO_DVFS); | 244 | AVIC_NESTED_NI_CALL_PROLOGUE(INT_PRIO_DVFS, 32*4); |
234 | asm volatile ("bl dvfs_int"); | 245 | asm volatile ("bl dvfs_int"); |
235 | AVIC_NESTED_NI_CALL_EPILOGUE(); | 246 | AVIC_NESTED_NI_CALL_EPILOGUE(32*4); |
236 | } | 247 | } |
237 | 248 | ||
238 | 249 | ||
@@ -281,7 +292,7 @@ static void dvfs_init(void) | |||
281 | imx31_regmod32(&CCM_LTR0, | 292 | imx31_regmod32(&CCM_LTR0, |
282 | DVFS_UPTHR << CCM_LTR0_UPTHR_POS | | 293 | DVFS_UPTHR << CCM_LTR0_UPTHR_POS | |
283 | DVFS_DNTHR << CCM_LTR0_DNTHR_POS | | 294 | DVFS_DNTHR << CCM_LTR0_DNTHR_POS | |
284 | DVFS_DIV3CK, | 295 | DVFS_DIV3CK << CCM_LTR0_DIV3CK_POS, |
285 | CCM_LTR0_UPTHR | CCM_LTR0_DNTHR | CCM_LTR0_DIV3CK); | 296 | CCM_LTR0_UPTHR | CCM_LTR0_DNTHR | CCM_LTR0_DIV3CK); |
286 | 297 | ||
287 | /* Set up LTR1. */ | 298 | /* Set up LTR1. */ |
@@ -356,7 +367,7 @@ static void dvfs_stop(void) | |||
356 | { | 367 | { |
357 | /* Set default frequency level */ | 368 | /* Set default frequency level */ |
358 | wait_for_dvfs_update_en(); | 369 | wait_for_dvfs_update_en(); |
359 | do_dvfs_update(DVFS_LEVEL_DEFAULT); | 370 | do_dvfs_update(DVFS_LEVEL_DEFAULT, false); |
360 | wait_for_dvfs_update_en(); | 371 | wait_for_dvfs_update_en(); |
361 | } | 372 | } |
362 | 373 | ||
@@ -379,6 +390,7 @@ static bool dptc_running = false; /* Has driver enabled DPTC? */ | |||
379 | unsigned int dptc_nr_dn = 0; | 390 | unsigned int dptc_nr_dn = 0; |
380 | unsigned int dptc_nr_up = 0; | 391 | unsigned int dptc_nr_up = 0; |
381 | unsigned int dptc_nr_pnc = 0; | 392 | unsigned int dptc_nr_pnc = 0; |
393 | unsigned int dptc_nr_no = 0; | ||
382 | 394 | ||
383 | static struct spi_transfer_desc dptc_pmic_xfer; /* Transfer descriptor */ | 395 | static struct spi_transfer_desc dptc_pmic_xfer; /* Transfer descriptor */ |
384 | static const unsigned char dptc_pmic_regs[2] = /* Register subaddresses */ | 396 | static const unsigned char dptc_pmic_regs[2] = /* Register subaddresses */ |
@@ -492,7 +504,12 @@ static void dptc_new_wp(unsigned int wp) | |||
492 | /* Interrupt vector for DPTC */ | 504 | /* Interrupt vector for DPTC */ |
493 | static __attribute__((interrupt("IRQ"))) void CCM_CLK_HANDLER(void) | 505 | static __attribute__((interrupt("IRQ"))) void CCM_CLK_HANDLER(void) |
494 | { | 506 | { |
495 | dptc_int(CCM_PMCR0); | 507 | unsigned long pmcr0 = CCM_PMCR0; |
508 | |||
509 | if ((pmcr0 & CCM_PMCR0_PTVAI) == CCM_PMCR0_PTVAI_NO_INT) | ||
510 | dptc_nr_no++; | ||
511 | |||
512 | dptc_int(pmcr0); | ||
496 | } | 513 | } |
497 | 514 | ||
498 | 515 | ||