summaryrefslogtreecommitdiff
path: root/firmware/target/arm/imx31/dvfs_dptc-imx31.c
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2010-05-06 03:23:51 +0000
committerMichael Sevakis <jethead71@rockbox.org>2010-05-06 03:23:51 +0000
commita36a498c577ae5c9daa8487c8440df46d325bab3 (patch)
treeba8a35620ee10da2a7e1d6e6ed9234f0f35647d9 /firmware/target/arm/imx31/dvfs_dptc-imx31.c
parent8fd3ec97271001d0b50d4404f5891c9a4e77d960 (diff)
downloadrockbox-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.c41
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? */
72unsigned int dvfs_nr_dn = 0; 72unsigned int dvfs_nr_dn = 0;
73unsigned int dvfs_nr_up = 0; 73unsigned int dvfs_nr_up = 0;
74unsigned int dvfs_nr_pnc = 0; 74unsigned int dvfs_nr_pnc = 0;
75unsigned int dvfs_nr_no = 0;
75 76
76static void dvfs_stop(void); 77static 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
86static void do_dvfs_update(unsigned int level) 87static 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)
230static __attribute__((naked, interrupt("IRQ"))) void CCM_DVFS_HANDLER(void) 241static __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? */
379unsigned int dptc_nr_dn = 0; 390unsigned int dptc_nr_dn = 0;
380unsigned int dptc_nr_up = 0; 391unsigned int dptc_nr_up = 0;
381unsigned int dptc_nr_pnc = 0; 392unsigned int dptc_nr_pnc = 0;
393unsigned int dptc_nr_no = 0;
382 394
383static struct spi_transfer_desc dptc_pmic_xfer; /* Transfer descriptor */ 395static struct spi_transfer_desc dptc_pmic_xfer; /* Transfer descriptor */
384static const unsigned char dptc_pmic_regs[2] = /* Register subaddresses */ 396static 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 */
493static __attribute__((interrupt("IRQ"))) void CCM_CLK_HANDLER(void) 505static __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