summaryrefslogtreecommitdiff
path: root/firmware
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2011-01-23 20:21:35 +0000
committerMichael Sevakis <jethead71@rockbox.org>2011-01-23 20:21:35 +0000
commit480c663e5b67d521f3e062fb90d10c7c5c186280 (patch)
tree688d19071afa676814b981e9338f3677f6556f3b /firmware
parent264e27d5b069104137587ba08cbae3ef301a850d (diff)
downloadrockbox-480c663e5b67d521f3e062fb90d10c7c5c186280.tar.gz
rockbox-480c663e5b67d521f3e062fb90d10c7c5c186280.zip
i.MX31: Now that it matters because there's a debug screeen that allows changing things while running, some DVFS/DPTC stuff has to be done more carefully. Trim out some stuff and group functions better as well.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@29122 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware')
-rw-r--r--firmware/target/arm/imx31/dvfs_dptc-imx31.c424
-rw-r--r--firmware/target/arm/imx31/dvfs_dptc-imx31.h21
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/kernel-gigabeat-s.c7
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.c3
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/system-gigabeat-s.c3
-rw-r--r--firmware/target/arm/imx31/mc13783-imx31.c2
6 files changed, 211 insertions, 249 deletions
diff --git a/firmware/target/arm/imx31/dvfs_dptc-imx31.c b/firmware/target/arm/imx31/dvfs_dptc-imx31.c
index 58be2118a1..1c5ad863ce 100644
--- a/firmware/target/arm/imx31/dvfs_dptc-imx31.c
+++ b/firmware/target/arm/imx31/dvfs_dptc-imx31.c
@@ -38,20 +38,28 @@ static volatile unsigned int dvfs_level = DVFS_LEVEL_DEFAULT;
38/* The current DPTC working point */ 38/* The current DPTC working point */
39static volatile unsigned int dptc_wp = DPTC_WP_DEFAULT; 39static volatile unsigned int dptc_wp = DPTC_WP_DEFAULT;
40 40
41 41/* Synchronize DPTC comparator value registers to new table row */
42static void update_dptc_counts(unsigned int level, unsigned int wp) 42static void update_dptc_counts(void)
43{ 43{
44 int oldlevel = disable_irq_save(); 44 const struct dptc_dcvr_table_entry * const entry =
45 const struct dptc_dcvr_table_entry *entry = &dptc_dcvr_table[level][wp]; 45 &dptc_dcvr_table[dvfs_level][dptc_wp];
46 46
47 CCM_DCVR0 = entry->dcvr0; 47 CCM_DCVR0 = entry->dcvr0;
48 CCM_DCVR1 = entry->dcvr1; 48 CCM_DCVR1 = entry->dcvr1;
49 CCM_DCVR2 = entry->dcvr2; 49 CCM_DCVR2 = entry->dcvr2;
50 CCM_DCVR3 = entry->dcvr3; 50 CCM_DCVR3 = entry->dcvr3;
51
52 restore_irq(oldlevel);
53} 51}
54 52
53/* Enable DPTC and unmask interrupt. */
54static void enable_dptc(void)
55{
56 /* Enable DPTC, assert voltage change request. */
57 CCM_PMCR0 = (CCM_PMCR0 & ~CCM_PMCR0_PTVAIM) | CCM_PMCR0_DPTEN |
58 CCM_PMCR0_DPVCR;
59 udelay(2);
60 /* Now set that voltage is valid */
61 CCM_PMCR0 |= CCM_PMCR0_DPVV;
62}
55 63
56static uint32_t check_regulator_setting(uint32_t setting) 64static uint32_t check_regulator_setting(uint32_t setting)
57{ 65{
@@ -74,9 +82,8 @@ unsigned int dvfs_nr_up = 0;
74unsigned int dvfs_nr_pnc = 0; 82unsigned int dvfs_nr_pnc = 0;
75unsigned int dvfs_nr_no = 0; 83unsigned int dvfs_nr_no = 0;
76 84
77
78/* Wait for the UPDTEN flag to be set so that all bits may be written */ 85/* Wait for the UPDTEN flag to be set so that all bits may be written */
79static inline void wait_for_dvfs_update_en(void) 86static inline void updten_wait(void)
80{ 87{
81 while (!(CCM_PMCR0 & CCM_PMCR0_UPDTEN)); 88 while (!(CCM_PMCR0 & CCM_PMCR0_UPDTEN));
82} 89}
@@ -111,6 +118,7 @@ static void do_dvfs_update(unsigned int level)
111 /* DVSUP (new frequency index) setup */ 118 /* DVSUP (new frequency index) setup */
112 pmcr0 = (pmcr0 & ~CCM_PMCR0_DVSUP) | (level << CCM_PMCR0_DVSUP_POS); 119 pmcr0 = (pmcr0 & ~CCM_PMCR0_DVSUP) | (level << CCM_PMCR0_DVSUP_POS);
113 120
121 /* Save new level */
114 dvfs_level = level; 122 dvfs_level = level;
115 123
116 if ((setting->pll_num << CCM_PMCR0_DFSUP_MCUPLL_POS) ^ 124 if ((setting->pll_num << CCM_PMCR0_DFSUP_MCUPLL_POS) ^
@@ -148,35 +156,30 @@ static void do_dvfs_update(unsigned int level)
148 156
149 if (pmcr0 & CCM_PMCR0_DPTEN) 157 if (pmcr0 & CCM_PMCR0_DPTEN)
150 { 158 {
151 update_dptc_counts(level, dptc_wp); 159 update_dptc_counts();
152 /* Enable DPTC to request voltage changes, unmask interrupt. */ 160 enable_dptc();
153 CCM_PMCR0 = (CCM_PMCR0 & ~CCM_PMCR0_PTVAIM) | CCM_PMCR0_DPVCR;
154 udelay(2);
155 /* Voltage is valid. */
156 CCM_PMCR0 |= CCM_PMCR0_DPVV;
157 } 161 }
158} 162}
159 163
160
161/* Start DVFS, change the set point and stop it */ 164/* Start DVFS, change the set point and stop it */
162static void set_current_dvfs_level(unsigned int level) 165static void set_current_dvfs_level(unsigned int level)
163{ 166{
164 int oldlevel = disable_irq_save(); 167 int oldlevel;
165
166 CCM_PMCR0 |= CCM_PMCR0_DVFEN;
167 168
168 wait_for_dvfs_update_en(); 169 /* Have to wait at least 3 div3 clocks before enabling after being
170 * stopped. */
171 udelay(1500);
169 172
173 oldlevel = disable_irq_save();
174 CCM_PMCR0 |= CCM_PMCR0_DVFEN;
170 do_dvfs_update(level); 175 do_dvfs_update(level);
176 restore_irq(oldlevel);
171 177
172 wait_for_dvfs_update_en(); 178 updten_wait();
173
174 CCM_PMCR0 &= ~CCM_PMCR0_DVFEN;
175 179
176 restore_irq(oldlevel); 180 bitclr32(&CCM_PMCR0, CCM_PMCR0_DVFEN);
177} 181}
178 182
179
180/* DVFS Interrupt handler */ 183/* DVFS Interrupt handler */
181static void __attribute__((used)) dvfs_int(void) 184static void __attribute__((used)) dvfs_int(void)
182{ 185{
@@ -234,7 +237,6 @@ static void __attribute__((used)) dvfs_int(void)
234 do_dvfs_update(level); 237 do_dvfs_update(level);
235} 238}
236 239
237
238/* Interrupt vector for DVFS */ 240/* Interrupt vector for DVFS */
239static __attribute__((naked, interrupt("IRQ"))) void CCM_DVFS_HANDLER(void) 241static __attribute__((naked, interrupt("IRQ"))) void CCM_DVFS_HANDLER(void)
240{ 242{
@@ -244,17 +246,9 @@ static __attribute__((naked, interrupt("IRQ"))) void CCM_DVFS_HANDLER(void)
244 AVIC_NESTED_NI_CALL_EPILOGUE(32*4); 246 AVIC_NESTED_NI_CALL_EPILOGUE(32*4);
245} 247}
246 248
247
248/* Initialize the DVFS hardware */ 249/* Initialize the DVFS hardware */
249static void INIT_ATTR dvfs_init(void) 250static void INIT_ATTR dvfs_init(void)
250{ 251{
251 if (CCM_PMCR0 & CCM_PMCR0_DVFEN)
252 {
253 /* Turn it off first. Really, shouldn't happen though. */
254 dvfs_running = true;
255 dvfs_stop();
256 }
257
258 /* Combine SW1A and SW1B DVS pins for a possible five DVS levels 252 /* Combine SW1A and SW1B DVS pins for a possible five DVS levels
259 * per working point. Four, MAXIMUM, are actually used, one for each 253 * per working point. Four, MAXIMUM, are actually used, one for each
260 * frequency. */ 254 * frequency. */
@@ -308,8 +302,10 @@ static void INIT_ATTR dvfs_init(void)
308 /* Set up LTR2-- EMA configuration. */ 302 /* Set up LTR2-- EMA configuration. */
309 bitmod32(&CCM_LTR2, DVFS_EMAC << CCM_LTR2_EMAC_POS, CCM_LTR2_EMAC); 303 bitmod32(&CCM_LTR2, DVFS_EMAC << CCM_LTR2_EMAC_POS, CCM_LTR2_EMAC);
310 304
311 /* DVFS interrupt goes to MCU. Mask load buffer full interrupt. */ 305 /* DVFS interrupt goes to MCU. Mask load buffer full interrupt. Do not
312 bitset32(&CCM_PMCR0, CCM_PMCR0_DVFIS | CCM_PMCR0_LBMI); 306 * always give an event. */
307 bitmod32(&CCM_PMCR0, CCM_PMCR0_DVFIS | CCM_PMCR0_LBMI,
308 CCM_PMCR0_DVFIS | CCM_PMCR0_LBMI | CCM_PMCR0_DVFEV);
313 309
314 /* Initialize current core PLL and dividers for default level. Assumes 310 /* Initialize current core PLL and dividers for default level. Assumes
315 * clocking scheme has been set up appropriately in other init code. */ 311 * clocking scheme has been set up appropriately in other init code. */
@@ -322,6 +318,7 @@ static void INIT_ATTR dvfs_init(void)
322 logf("DVFS: Initialized"); 318 logf("DVFS: Initialized");
323} 319}
324 320
321
325/** DPTC **/ 322/** DPTC **/
326 323
327/* Request tracking since boot */ 324/* Request tracking since boot */
@@ -332,54 +329,41 @@ unsigned int dptc_nr_up = 0;
332unsigned int dptc_nr_pnc = 0; 329unsigned int dptc_nr_pnc = 0;
333unsigned int dptc_nr_no = 0; 330unsigned int dptc_nr_no = 0;
334 331
335static struct spi_transfer_desc dptc_pmic_xfer; /* Transfer descriptor */ 332struct dptc_async_buf
336static const unsigned char dptc_pmic_regs[2] = /* Register subaddresses */
337{ MC13783_SWITCHERS0, MC13783_SWITCHERS1 };
338static uint32_t dptc_reg_shadows[2]; /* shadow regs */
339static uint32_t dptc_regs_buf[2]; /* buffer for async write */
340
341
342/* Enable DPTC and unmask interrupt. */
343static void enable_dptc(void)
344{ 333{
345 int oldlevel = disable_irq_save(); 334 struct spi_transfer_desc xfer; /* transfer descriptor */
346 335 unsigned int wp; /* new working point */
347 /* Enable DPTC, assert voltage change request. */ 336 uint32_t buf[2]; /* buffer for async write */
348 CCM_PMCR0 = (CCM_PMCR0 & ~CCM_PMCR0_PTVAIM) | CCM_PMCR0_DPTEN | 337};
349 CCM_PMCR0_DPVCR;
350
351 udelay(2);
352
353 /* Set voltage valid *after* setting change request */
354 CCM_PMCR0 |= CCM_PMCR0_DPVV;
355
356 restore_irq(oldlevel);
357}
358 338
339static struct dptc_async_buf dptc_async_buf; /* ISR async write buffer */
340static const unsigned char dptc_pmic_regs[2] = /* Register subaddresses */
341 { MC13783_SWITCHERS0, MC13783_SWITCHERS1 };
342static uint32_t dptc_reg_shadows[2]; /* shadow regs */
359 343
360/* Called after asynchronous PMIC write is completed */ 344/* Called (in SPI INT context) after asynchronous PMIC write is completed */
361static void dptc_transfer_done_callback(struct spi_transfer_desc *xfer) 345static void dptc_transfer_done_callback(struct spi_transfer_desc *xfer)
362{ 346{
363 if (xfer->count != 0) 347 if (xfer->count != 0)
364 return; 348 return;
365 349
366 update_dptc_counts(dvfs_level, dptc_wp); 350 /* Save new working point */
351 dptc_wp = ((struct dptc_async_buf *)xfer)->wp;
352
353 update_dptc_counts();
367 354
368 if (dptc_running) 355 if (dptc_running)
369 enable_dptc(); 356 enable_dptc();
370} 357}
371 358
372
373/* Handle the DPTC interrupt and sometimes the manual setting - always call 359/* Handle the DPTC interrupt and sometimes the manual setting - always call
374 * with IRQ masked. */ 360 * with IRQ masked. */
375static void dptc_int(unsigned long pmcr0) 361static void dptc_int(unsigned long pmcr0, int wp, struct dptc_async_buf *abuf)
376{ 362{
377 const union dvfs_dptc_voltage_table_entry *entry; 363 const union dvfs_dptc_voltage_table_entry *entry;
378 uint32_t sw1a, sw1advs, sw1bdvs, sw1bstby; 364 uint32_t sw1a, sw1advs, sw1bdvs, sw1bstby;
379 uint32_t switchers0, switchers1; 365 uint32_t switchers0, switchers1;
380 366
381 int wp = dptc_wp;
382
383 /* Mask DPTC interrupt and disable DPTC until the change request is 367 /* Mask DPTC interrupt and disable DPTC until the change request is
384 * serviced. */ 368 * serviced. */
385 CCM_PMCR0 = (pmcr0 & ~CCM_PMCR0_DPTEN) | CCM_PMCR0_PTVAIM; 369 CCM_PMCR0 = (pmcr0 & ~CCM_PMCR0_DPTEN) | CCM_PMCR0_PTVAIM;
@@ -387,30 +371,40 @@ static void dptc_int(unsigned long pmcr0)
387 switch (pmcr0 & CCM_PMCR0_PTVAI) 371 switch (pmcr0 & CCM_PMCR0_PTVAI)
388 { 372 {
389 case CCM_PMCR0_PTVAI_DECREASE: 373 case CCM_PMCR0_PTVAI_DECREASE:
374 /* Decrease voltage request - increment working point */
390 wp++; 375 wp++;
391 dptc_nr_dn++; 376 dptc_nr_dn++;
392 break; 377 break;
393 378
394 case CCM_PMCR0_PTVAI_INCREASE: 379 case CCM_PMCR0_PTVAI_INCREASE:
380 /* Increase voltage request - decrement working point */
395 wp--; 381 wp--;
396 dptc_nr_up++; 382 dptc_nr_up++;
397 break; 383 break;
398 384
399 case CCM_PMCR0_PTVAI_INCREASE_NOW: 385 case CCM_PMCR0_PTVAI_INCREASE_NOW:
386 /* Panic request - move immediately to panic working point if
387 * decrement results in greater working point than DPTC_WP_PANIC. */
400 if (--wp > DPTC_WP_PANIC) 388 if (--wp > DPTC_WP_PANIC)
401 wp = DPTC_WP_PANIC; 389 wp = DPTC_WP_PANIC;
402 dptc_nr_pnc++; 390 dptc_nr_pnc++;
403 break; 391 break;
404 392
405 case CCM_PMCR0_PTVAI_NO_INT: 393 case CCM_PMCR0_PTVAI_NO_INT:
406 break; /* Just maintain at global level */ 394 /* Just maintain at global level */
395 if (abuf == &dptc_async_buf)
396 dptc_nr_no++;
397 break;
407 } 398 }
408 399
400 /* Keep result in range */
409 if (wp < 0) 401 if (wp < 0)
410 wp = 0; 402 wp = 0;
411 else if (wp >= DPTC_NUM_WP) 403 else if (wp >= DPTC_NUM_WP)
412 wp = DPTC_NUM_WP - 1; 404 wp = DPTC_NUM_WP - 1;
413 405
406 /* Get new regulator register settings, sanity check them and write them
407 * in the background. */
414 entry = &dvfs_dptc_voltage_table[wp]; 408 entry = &dvfs_dptc_voltage_table[wp];
415 409
416 sw1a = check_regulator_setting(entry->sw1a); 410 sw1a = check_regulator_setting(entry->sw1a);
@@ -419,154 +413,183 @@ static void dptc_int(unsigned long pmcr0)
419 sw1bstby = check_regulator_setting(entry->sw1bstby); 413 sw1bstby = check_regulator_setting(entry->sw1bstby);
420 414
421 switchers0 = dptc_reg_shadows[0] & ~(MC13783_SW1A | MC13783_SW1ADVS); 415 switchers0 = dptc_reg_shadows[0] & ~(MC13783_SW1A | MC13783_SW1ADVS);
422 dptc_regs_buf[0] = switchers0 | 416 abuf->buf[0] = switchers0 |
423 sw1a << MC13783_SW1A_POS | /* SW1A */ 417 sw1a << MC13783_SW1A_POS | /* SW1A */
424 sw1advs << MC13783_SW1ADVS_POS; /* SW1ADVS */ 418 sw1advs << MC13783_SW1ADVS_POS; /* SW1ADVS */
425 switchers1 = dptc_reg_shadows[1] & ~(MC13783_SW1BDVS | MC13783_SW1BSTBY); 419 switchers1 = dptc_reg_shadows[1] & ~(MC13783_SW1BDVS | MC13783_SW1BSTBY);
426 dptc_regs_buf[1] = switchers1 | 420 abuf->buf[1] = switchers1 |
427 sw1bdvs << MC13783_SW1BDVS_POS | /* SW1BDVS */ 421 sw1bdvs << MC13783_SW1BDVS_POS | /* SW1BDVS */
428 sw1bstby << MC13783_SW1BSTBY_POS; /* SW1BSTBY */ 422 sw1bstby << MC13783_SW1BSTBY_POS; /* SW1BSTBY */
429
430 dptc_wp = wp;
431 423
432 mc13783_write_async(&dptc_pmic_xfer, dptc_pmic_regs, 424 abuf->wp = wp; /* Save new for xfer completion handler */
433 dptc_regs_buf, 2, dptc_transfer_done_callback); 425 mc13783_write_async(&abuf->xfer, dptc_pmic_regs, abuf->buf, 2,
426 dptc_transfer_done_callback);
434} 427}
435 428
436
437/* Handle setting the working point explicitly - always call with IRQ 429/* Handle setting the working point explicitly - always call with IRQ
438 * masked */ 430 * masked */
439static void dptc_new_wp(unsigned int wp) 431static void dptc_new_wp(unsigned int wp)
440{ 432{
441 dptc_wp = wp; 433 struct dptc_async_buf buf;
434
442 /* "NO_INT" so the working point isn't incremented, just set. */ 435 /* "NO_INT" so the working point isn't incremented, just set. */
443 dptc_int((CCM_PMCR0 & ~CCM_PMCR0_PTVAI) | CCM_PMCR0_PTVAI_NO_INT); 436 dptc_int(CCM_PMCR0 & ~CCM_PMCR0_PTVAI, wp, &buf);
444}
445 437
438 /* Wait for PMIC write */
439 while (!spi_transfer_complete(&buf.xfer))
440 {
441 enable_irq();
442 nop; nop; nop; nop; nop;
443 disable_irq();
444 }
445}
446 446
447/* Interrupt vector for DPTC */ 447/* Interrupt vector for DPTC */
448static __attribute__((interrupt("IRQ"))) void CCM_CLK_HANDLER(void) 448static __attribute__((interrupt("IRQ"))) void CCM_CLK_HANDLER(void)
449{ 449{
450 unsigned long pmcr0 = CCM_PMCR0; 450 dptc_int(CCM_PMCR0, dptc_wp, &dptc_async_buf);
451
452 if ((pmcr0 & CCM_PMCR0_PTVAI) == CCM_PMCR0_PTVAI_NO_INT)
453 dptc_nr_no++;
454
455 dptc_int(pmcr0);
456} 451}
457 452
458
459/* Initialize the DPTC hardware */ 453/* Initialize the DPTC hardware */
460static void INIT_ATTR dptc_init(void) 454static void INIT_ATTR dptc_init(void)
461{ 455{
462 /* Force DPTC off if running for some reason. */ 456 int oldlevel;
463 bitmod32(&CCM_PMCR0, CCM_PMCR0_PTVAIM,
464 CCM_PMCR0_PTVAIM | CCM_PMCR0_DPTEN);
465 457
466 /* Shadow the regulator registers */ 458 /* Shadow the regulator registers */
467 mc13783_read_regs(dptc_pmic_regs, dptc_reg_shadows, 2); 459 mc13783_read_regs(dptc_pmic_regs, dptc_reg_shadows, 2);
468 460
469 /* Set default, safe working point. */ 461 /* Set default, safe working point. */
462 oldlevel = disable_irq_save();
470 dptc_new_wp(DPTC_WP_DEFAULT); 463 dptc_new_wp(DPTC_WP_DEFAULT);
464 restore_irq(oldlevel);
471 465
472 /* Interrupt goes to MCU, specified reference circuits enabled when 466 /* Interrupt goes to MCU, specified reference circuits enabled when
473 * DPTC is active. */ 467 * DPTC is active, DPTC counting range = 256 system clocks */
474 bitset32(&CCM_PMCR0, CCM_PMCR0_PTVIS); 468 bitmod32(&CCM_PMCR0,
475 469 CCM_PMCR0_PTVIS | DPTC_DRCE_MASK,
476 bitmod32(&CCM_PMCR0, DPTC_DRCE_MASK, 470 CCM_PMCR0_PTVIS | CCM_PMCR0_DCR |
477 CCM_PMCR0_DRCE0 | CCM_PMCR0_DRCE1 | 471 CCM_PMCR0_DRCE0 | CCM_PMCR0_DRCE1 |
478 CCM_PMCR0_DRCE2 | CCM_PMCR0_DRCE3); 472 CCM_PMCR0_DRCE2 | CCM_PMCR0_DRCE3);
479 473
480 /* DPTC counting range = 256 system clocks */
481 bitclr32(&CCM_PMCR0, CCM_PMCR0_DCR);
482
483 logf("DPTC: Initialized"); 474 logf("DPTC: Initialized");
484} 475}
485 476
486 477
487/** Main module interface **/ 478/** Main module interface **/
488 479
480/** DVFS+DPTC **/
481
489/* Initialize DVFS and DPTC */ 482/* Initialize DVFS and DPTC */
490void INIT_ATTR dvfs_dptc_init(void) 483void INIT_ATTR dvfs_dptc_init(void)
491{ 484{
485 /* DVFS or DPTC on for some reason? Force off. */
486 bitmod32(&CCM_PMCR0,
487 CCM_PMCR0_FSVAIM | CCM_PMCR0_LBMI |
488 CCM_PMCR0_PTVAIM,
489 CCM_PMCR0_FSVAIM | CCM_PMCR0_LBMI | CCM_PMCR0_DVFEN |
490 CCM_PMCR0_PTVAIM | CCM_PMCR0_DPTEN);
491
492 /* Ensure correct order - after this, the two appear independent */
492 dptc_init(); 493 dptc_init();
493 dvfs_init(); 494 dvfs_init();
494} 495}
495 496
496 497/* Obtain the current core voltage setting, in millivolts 8-) */
497/* Start DVFS and DPTC */ 498unsigned int dvfs_dptc_get_voltage(void)
498void dvfs_dptc_start(void)
499{ 499{
500 dvfs_start(); 500 unsigned int v;
501 dptc_start();
502}
503 501
502 int oldlevel = disable_irq_save();
503 v = dvfs_dptc_voltage_table[dptc_wp].sw[dvfs_level];
504 restore_irq(oldlevel);
504 505
505/* Stop DVFS and DPTC */ 506 /* 25mV steps from 0.900V to 1.675V */
506void dvfs_dptc_stop(void) 507 return v * 25 + 900;
507{
508 dptc_stop();
509 dvfs_stop();
510} 508}
511 509
510
511/** DVFS **/
512
512/* Start the DVFS hardware */ 513/* Start the DVFS hardware */
513void dvfs_start(void) 514void dvfs_start(void)
514{ 515{
515 int oldlevel; 516 if (dvfs_running)
517 return;
516 518
517 /* Have to wait at least 3 div3 clocks before enabling after being 519 /* Have to wait at least 3 div3 clocks before enabling after being
518 * stopped. */ 520 * stopped. */
519 udelay(1500); 521 udelay(1500);
520 522
521 oldlevel = disable_irq_save(); 523 /* Unmask DVFS interrupt source and enable DVFS. */
524 bitmod32(&CCM_PMCR0, CCM_PMCR0_DVFEN,
525 CCM_PMCR0_FSVAIM | CCM_PMCR0_DVFEN);
522 526
523 if (!dvfs_running) 527 dvfs_running = true;
524 {
525 dvfs_running = true;
526 528
527 /* Unmask DVFS interrupt source and enable DVFS. */ 529 avic_enable_int(INT_CCM_DVFS, INT_TYPE_IRQ, INT_PRIO_DVFS,
528 avic_enable_int(INT_CCM_DVFS, INT_TYPE_IRQ, INT_PRIO_DVFS, 530 CCM_DVFS_HANDLER);
529 CCM_DVFS_HANDLER);
530
531 CCM_PMCR0 = (CCM_PMCR0 & ~CCM_PMCR0_FSVAIM) | CCM_PMCR0_DVFEN;
532 }
533
534 restore_irq(oldlevel);
535 531
536 logf("DVFS: started"); 532 logf("DVFS: started");
537} 533}
538 534
539
540/* Stop the DVFS hardware and return to default frequency */ 535/* Stop the DVFS hardware and return to default frequency */
541void dvfs_stop(void) 536void dvfs_stop(void)
542{ 537{
543 int oldlevel = disable_irq_save(); 538 if (!dvfs_running)
544 539 return;
545 if (dvfs_running)
546 {
547 /* Mask DVFS interrupts. */
548 CCM_PMCR0 |= CCM_PMCR0_FSVAIM | CCM_PMCR0_LBMI;
549 avic_disable_int(INT_CCM_DVFS);
550 540
551 if (((CCM_PMCR0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS) != 541 /* Mask DVFS interrupts. */
552 DVFS_LEVEL_DEFAULT) 542 avic_disable_int(INT_CCM_DVFS);
553 { 543 bitset32(&CCM_PMCR0, CCM_PMCR0_FSVAIM | CCM_PMCR0_LBMI);
554 /* Set default frequency level */
555 wait_for_dvfs_update_en();
556 do_dvfs_update(DVFS_LEVEL_DEFAULT);
557 wait_for_dvfs_update_en();
558 }
559 544
560 /* Disable DVFS. */ 545 if (((CCM_PMCR0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS) !=
561 CCM_PMCR0 &= ~CCM_PMCR0_DVFEN; 546 DVFS_LEVEL_DEFAULT)
562 dvfs_running = false; 547 {
548 int oldlevel;
549 /* Set default frequency level */
550 updten_wait();
551 oldlevel = disable_irq_save();
552 do_dvfs_update(DVFS_LEVEL_DEFAULT);
553 restore_irq(oldlevel);
554 updten_wait();
563 } 555 }
564 556
565 restore_irq(oldlevel); 557 /* Disable DVFS. */
558 bitclr32(&CCM_PMCR0, CCM_PMCR0_DVFEN);
559 dvfs_running = false;
566 560
567 logf("DVFS: stopped"); 561 logf("DVFS: stopped");
568} 562}
569 563
564/* Is DVFS enabled? */
565bool dvfs_enabled(void)
566{
567 return dvfs_running;
568}
569
570/* If DVFS is disabled, set the level explicitly */
571void dvfs_set_level(unsigned int level)
572{
573 if (dvfs_running ||
574 level >= DVFS_NUM_LEVELS ||
575 !((1 << level) & DVFS_LEVEL_MASK) ||
576 level == ((CCM_PMCR0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS))
577 return;
578
579 set_current_dvfs_level(level);
580}
581
582/* Get the current DVFS level */
583unsigned int dvfs_get_level(void)
584{
585 return dvfs_level;
586}
587
588/* Get bitmask of levels supported */
589unsigned int dvfs_level_mask(void)
590{
591 return DVFS_LEVEL_MASK;
592}
570 593
571/* Mask the DVFS interrupt without affecting running status */ 594/* Mask the DVFS interrupt without affecting running status */
572void dvfs_int_mask(bool mask) 595void dvfs_int_mask(bool mask)
@@ -583,7 +606,6 @@ void dvfs_int_mask(bool mask)
583 } 606 }
584} 607}
585 608
586
587/* Set a signal load tracking weight */ 609/* Set a signal load tracking weight */
588void dvfs_set_lt_weight(enum DVFS_LT_SIGS index, unsigned long value) 610void dvfs_set_lt_weight(enum DVFS_LT_SIGS index, unsigned long value)
589{ 611{
@@ -603,7 +625,6 @@ void dvfs_set_lt_weight(enum DVFS_LT_SIGS index, unsigned long value)
603 bitmod32(reg_p, value << shift, 0x7 << shift); 625 bitmod32(reg_p, value << shift, 0x7 << shift);
604} 626}
605 627
606
607/* Return a signal load tracking weight */ 628/* Return a signal load tracking weight */
608unsigned long dvfs_get_lt_weight(enum DVFS_LT_SIGS index) 629unsigned long dvfs_get_lt_weight(enum DVFS_LT_SIGS index)
609{ 630{
@@ -623,7 +644,6 @@ unsigned long dvfs_get_lt_weight(enum DVFS_LT_SIGS index)
623 return (*reg_p & (0x7 << shift)) >> shift; 644 return (*reg_p & (0x7 << shift)) >> shift;
624} 645}
625 646
626
627/* Set a signal load detection mode */ 647/* Set a signal load detection mode */
628void dvfs_set_lt_detect(enum DVFS_LT_SIGS index, bool edge) 648void dvfs_set_lt_detect(enum DVFS_LT_SIGS index, bool edge)
629{ 649{
@@ -637,7 +657,6 @@ void dvfs_set_lt_detect(enum DVFS_LT_SIGS index, bool edge)
637 bitmod32(&CCM_LTR0, edge ? bit : 0, bit); 657 bitmod32(&CCM_LTR0, edge ? bit : 0, bit);
638} 658}
639 659
640
641/* Returns a signal load detection mode */ 660/* Returns a signal load detection mode */
642bool dvfs_get_lt_detect(enum DVFS_LT_SIGS index) 661bool dvfs_get_lt_detect(enum DVFS_LT_SIGS index)
643{ 662{
@@ -651,7 +670,6 @@ bool dvfs_get_lt_detect(enum DVFS_LT_SIGS index)
651 return !!((CCM_LTR0 & (1ul << shift)) >> shift); 670 return !!((CCM_LTR0 & (1ul << shift)) >> shift);
652} 671}
653 672
654
655/* Set/clear the general-purpose load tracking bit */ 673/* Set/clear the general-purpose load tracking bit */
656void dvfs_set_gp_bit(enum DVFS_DVGPS dvgp, bool assert) 674void dvfs_set_gp_bit(enum DVFS_DVGPS dvgp, bool assert)
657{ 675{
@@ -662,7 +680,6 @@ void dvfs_set_gp_bit(enum DVFS_DVGPS dvgp, bool assert)
662 } 680 }
663} 681}
664 682
665
666/* Return the general-purpose load tracking bit */ 683/* Return the general-purpose load tracking bit */
667bool dvfs_get_gp_bit(enum DVFS_DVGPS dvgp) 684bool dvfs_get_gp_bit(enum DVFS_DVGPS dvgp)
668{ 685{
@@ -671,7 +688,6 @@ bool dvfs_get_gp_bit(enum DVFS_DVGPS dvgp)
671 return false; 688 return false;
672} 689}
673 690
674
675/* Set GP load tracking by code. 691/* Set GP load tracking by code.
676 * level_code: 692 * level_code:
677 * lt 0 =defaults 693 * lt 0 =defaults
@@ -738,7 +754,6 @@ void dvfs_get_gp_sense(int *level_code, unsigned long *detect_mask)
738 *detect_mask = mask; 754 *detect_mask = mask;
739} 755}
740 756
741
742/* Turn the wait-for-interrupt monitoring on or off */ 757/* Turn the wait-for-interrupt monitoring on or off */
743void dvfs_wfi_monitor(bool on) 758void dvfs_wfi_monitor(bool on)
744{ 759{
@@ -746,125 +761,68 @@ void dvfs_wfi_monitor(bool on)
746} 761}
747 762
748 763
749/* Obtain the current core voltage setting, in millivolts 8-) */ 764/** DPTC **/
750unsigned int dvfs_dptc_get_voltage(void)
751{
752 unsigned int v;
753
754 int oldlevel = disable_irq_save();
755 v = dvfs_dptc_voltage_table[dptc_wp].sw[dvfs_level];
756 restore_irq(oldlevel);
757
758 /* 25mV steps from 0.900V to 1.675V */
759 return v * 25 + 900;
760}
761
762
763/* Get the current DVFS level */
764unsigned int dvfs_get_level(void)
765{
766 return dvfs_level;
767}
768
769
770/* Is DVFS enabled? */
771bool dvfs_enabled(void)
772{
773 return dvfs_running;
774}
775
776
777/* Get bitmask of levels supported */
778unsigned int dvfs_level_mask(void)
779{
780 return DVFS_LEVEL_MASK;
781}
782
783
784/* If DVFS is disabled, set the level explicitly */
785void dvfs_set_level(unsigned int level)
786{
787 int oldlevel = disable_irq_save();
788
789 if (!dvfs_running && level < DVFS_NUM_LEVELS)
790 {
791 unsigned int currlevel =
792 (CCM_PMCR0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS;
793 if (level != currlevel && ((1 << level) & DVFS_LEVEL_MASK))
794 set_current_dvfs_level(level);
795 }
796
797 restore_irq(oldlevel);
798}
799
800 765
801/* Start DPTC module */ 766/* Start DPTC module */
802void dptc_start(void) 767void dptc_start(void)
803{ 768{
804 int oldlevel = disable_irq_save(); 769 int oldlevel;
805
806 if (!dptc_running)
807 {
808 dptc_running = true;
809 770
810 /* Enable DPTC and unmask interrupt. */ 771 if (dptc_running)
811 avic_enable_int(INT_CCM_CLK, INT_TYPE_IRQ, INT_PRIO_DPTC, 772 return;
812 CCM_CLK_HANDLER);
813 773
814 update_dptc_counts(dvfs_level, dptc_wp); 774 oldlevel = disable_irq_save();
815 enable_dptc(); 775 dptc_running = true;
816 }
817 776
777 /* Enable DPTC and unmask interrupt. */
778 update_dptc_counts();
779 enable_dptc();
818 restore_irq(oldlevel); 780 restore_irq(oldlevel);
819 781
782 avic_enable_int(INT_CCM_CLK, INT_TYPE_IRQ, INT_PRIO_DPTC,
783 CCM_CLK_HANDLER);
784
820 logf("DPTC: started"); 785 logf("DPTC: started");
821} 786}
822 787
823
824/* Stop the DPTC hardware if running and go back to default working point */ 788/* Stop the DPTC hardware if running and go back to default working point */
825void dptc_stop(void) 789void dptc_stop(void)
826{ 790{
827 int oldlevel = disable_irq_save(); 791 int oldlevel;
828 792
829 if (dptc_running) 793 if (!dptc_running)
830 { 794 return;
831 dptc_running = false;
832 795
833 /* Disable DPTC and mask interrupt. */ 796 avic_disable_int(INT_CCM_CLK);
834 CCM_PMCR0 = (CCM_PMCR0 & ~CCM_PMCR0_DPTEN) | CCM_PMCR0_PTVAIM;
835 avic_disable_int(INT_CCM_CLK);
836 797
837 /* Go back to default working point. */ 798 oldlevel = disable_irq_save();
838 dptc_new_wp(DPTC_WP_DEFAULT); 799 dptc_running = false;
839 }
840 800
801 /* Disable DPTC and mask interrupt. */
802 CCM_PMCR0 = (CCM_PMCR0 & ~CCM_PMCR0_DPTEN) | CCM_PMCR0_PTVAIM;
803
804 /* Go back to default working point. */
805 dptc_new_wp(DPTC_WP_DEFAULT);
841 restore_irq(oldlevel); 806 restore_irq(oldlevel);
842 807
843 logf("DPTC: stopped"); 808 logf("DPTC: stopped");
844} 809}
845 810
846
847/* Get the current DPTC working point */
848unsigned int dptc_get_wp(void)
849{
850 return dptc_wp;
851}
852
853
854/* Is DPTC enabled? */ 811/* Is DPTC enabled? */
855bool dptc_enabled(void) 812bool dptc_enabled(void)
856{ 813{
857 return dptc_running; 814 return dptc_running;
858} 815}
859 816
860
861/* If DPTC is not running, set the working point explicitly */ 817/* If DPTC is not running, set the working point explicitly */
862void dptc_set_wp(unsigned int wp) 818void dptc_set_wp(unsigned int wp)
863{ 819{
864 int oldlevel = disable_irq_save();
865
866 if (!dptc_running && wp < DPTC_NUM_WP) 820 if (!dptc_running && wp < DPTC_NUM_WP)
867 dptc_new_wp(wp); 821 dptc_new_wp(wp);
822}
868 823
869 restore_irq(oldlevel); 824/* Get the current DPTC working point */
825unsigned int dptc_get_wp(void)
826{
827 return dptc_wp;
870} 828}
diff --git a/firmware/target/arm/imx31/dvfs_dptc-imx31.h b/firmware/target/arm/imx31/dvfs_dptc-imx31.h
index 1f3f1ab20d..8c599fe402 100644
--- a/firmware/target/arm/imx31/dvfs_dptc-imx31.h
+++ b/firmware/target/arm/imx31/dvfs_dptc-imx31.h
@@ -118,15 +118,19 @@ struct dvfs_lt_signal_descriptor
118#define DVFS_LEVEL_2 (1u << 2) 118#define DVFS_LEVEL_2 (1u << 2)
119#define DVFS_LEVEL_3 (1u << 3) 119#define DVFS_LEVEL_3 (1u << 3)
120 120
121extern long cpu_voltage_setting; 121/* DVFS+DPTC */
122
123void dvfs_dptc_init(void); 122void dvfs_dptc_init(void);
124void dvfs_dptc_start(void); 123unsigned int dvfs_dptc_get_voltage(void);
125void dvfs_dptc_stop(void);
126 124
125/* DVFS */
127void dvfs_start(void); 126void dvfs_start(void);
128void dvfs_stop(void); 127void dvfs_stop(void);
129bool dvfs_enabled(void); 128bool dvfs_enabled(void);
129unsigned int dvfs_level_mask(void);
130void dvfs_set_level(unsigned int level);
131unsigned int dvfs_get_level(void);
132void dvfs_int_mask(bool mask);
133
130void dvfs_wfi_monitor(bool on); 134void dvfs_wfi_monitor(bool on);
131void dvfs_set_lt_weight(enum DVFS_LT_SIGS index, unsigned long value); 135void dvfs_set_lt_weight(enum DVFS_LT_SIGS index, unsigned long value);
132unsigned long dvfs_get_lt_weight(enum DVFS_LT_SIGS index); 136unsigned long dvfs_get_lt_weight(enum DVFS_LT_SIGS index);
@@ -134,19 +138,14 @@ void dvfs_set_lt_detect(enum DVFS_LT_SIGS index, bool edge);
134bool dvfs_get_lt_detect(enum DVFS_LT_SIGS index); 138bool dvfs_get_lt_detect(enum DVFS_LT_SIGS index);
135void dvfs_set_gp_bit(enum DVFS_DVGPS dvgp, bool assert); 139void dvfs_set_gp_bit(enum DVFS_DVGPS dvgp, bool assert);
136bool dvfs_get_gp_bit(enum DVFS_DVGPS dvgp); 140bool dvfs_get_gp_bit(enum DVFS_DVGPS dvgp);
137void dvfs_int_mask(bool mask);
138void dvfs_set_gp_sense(int level_code, unsigned long detect_mask); 141void dvfs_set_gp_sense(int level_code, unsigned long detect_mask);
139void dvfs_get_gp_sense(int *level_code, unsigned long *detect_mask); 142void dvfs_get_gp_sense(int *level_code, unsigned long *detect_mask);
140unsigned int dvfs_level_mask(void);
141
142unsigned int dvfs_dptc_get_voltage(void);
143unsigned int dvfs_get_level(void);
144void dvfs_set_level(unsigned int level);
145 143
144/* DPTC */
146void dptc_start(void); 145void dptc_start(void);
147void dptc_stop(void); 146void dptc_stop(void);
148bool dptc_enabled(void); 147bool dptc_enabled(void);
149unsigned int dptc_get_wp(void);
150void dptc_set_wp(unsigned int wp); 148void dptc_set_wp(unsigned int wp);
149unsigned int dptc_get_wp(void);
151 150
152#endif /* _DVFS_DPTC_IMX31_H_ */ 151#endif /* _DVFS_DPTC_IMX31_H_ */
diff --git a/firmware/target/arm/imx31/gigabeat-s/kernel-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/kernel-gigabeat-s.c
index 481cb467bd..f30287d4e9 100644
--- a/firmware/target/arm/imx31/gigabeat-s/kernel-gigabeat-s.c
+++ b/firmware/target/arm/imx31/gigabeat-s/kernel-gigabeat-s.c
@@ -71,10 +71,11 @@ void INIT_ATTR kernel_device_init(void)
71 spi_init(); 71 spi_init();
72 enable_interrupt(IRQ_FIQ_STATUS); 72 enable_interrupt(IRQ_FIQ_STATUS);
73 mc13783_init(); 73 mc13783_init();
74 dvfs_dptc_init(); 74 dvfs_dptc_init(); /* Init also sets default points */
75 dvfs_wfi_monitor(true); /* Monitor the WFI signal */
76#ifndef BOOTLOADER 75#ifndef BOOTLOADER
77 dvfs_dptc_start(); /* Should be ok to start even so early */ 76 dvfs_wfi_monitor(true); /* Monitor the WFI signal */
77 dvfs_start(); /* Should be ok to start even so early */
78 dptc_start();
78#endif 79#endif
79} 80}
80 81
diff --git a/firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.c
index 9d7d30547b..11276a6c3a 100644
--- a/firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.c
+++ b/firmware/target/arm/imx31/gigabeat-s/power-gigabeat-s.c
@@ -129,7 +129,8 @@ bool tuner_powered(void)
129void power_off(void) 129void power_off(void)
130{ 130{
131 /* Turn off voltage and frequency scaling */ 131 /* Turn off voltage and frequency scaling */
132 dvfs_dptc_stop(); 132 dvfs_stop();
133 dptc_stop();
133 134
134 /* Cut backlight */ 135 /* Cut backlight */
135 _backlight_off(); 136 _backlight_off();
diff --git a/firmware/target/arm/imx31/gigabeat-s/system-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/system-gigabeat-s.c
index 1f177d4252..16b17ba403 100644
--- a/firmware/target/arm/imx31/gigabeat-s/system-gigabeat-s.c
+++ b/firmware/target/arm/imx31/gigabeat-s/system-gigabeat-s.c
@@ -210,7 +210,8 @@ void INIT_ATTR system_init(void)
210 210
211void system_prepare_fw_start(void) 211void system_prepare_fw_start(void)
212{ 212{
213 dvfs_dptc_stop(); 213 dvfs_stop();
214 dptc_stop();
214 mc13783_close(); 215 mc13783_close();
215 tick_stop(); 216 tick_stop();
216 disable_interrupt(IRQ_FIQ_STATUS); 217 disable_interrupt(IRQ_FIQ_STATUS);
diff --git a/firmware/target/arm/imx31/mc13783-imx31.c b/firmware/target/arm/imx31/mc13783-imx31.c
index b236dcd2ab..d5d22e2c75 100644
--- a/firmware/target/arm/imx31/mc13783-imx31.c
+++ b/firmware/target/arm/imx31/mc13783-imx31.c
@@ -396,6 +396,7 @@ bool mc13783_read_async(struct spi_transfer_desc *xfer,
396 xfer->rxbuf = buffer; 396 xfer->rxbuf = buffer;
397 xfer->count = count; 397 xfer->count = count;
398 xfer->callback = callback; 398 xfer->callback = callback;
399 xfer->next = NULL;
399 400
400 return spi_transfer(xfer); 401 return spi_transfer(xfer);
401} 402}
@@ -422,6 +423,7 @@ bool mc13783_write_async(struct spi_transfer_desc *xfer,
422 xfer->rxbuf = NULL; 423 xfer->rxbuf = NULL;
423 xfer->count = count; 424 xfer->count = count;
424 xfer->callback = callback; 425 xfer->callback = callback;
426 xfer->next = NULL;
425 427
426 return spi_transfer(xfer); 428 return spi_transfer(xfer);
427} 429}