summaryrefslogtreecommitdiff
path: root/firmware/target/arm/imx31/dvfs_dptc-imx31.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/imx31/dvfs_dptc-imx31.c')
-rw-r--r--firmware/target/arm/imx31/dvfs_dptc-imx31.c213
1 files changed, 87 insertions, 126 deletions
diff --git a/firmware/target/arm/imx31/dvfs_dptc-imx31.c b/firmware/target/arm/imx31/dvfs_dptc-imx31.c
index 6e177ba6bd..217c8a8b83 100644
--- a/firmware/target/arm/imx31/dvfs_dptc-imx31.c
+++ b/firmware/target/arm/imx31/dvfs_dptc-imx31.c
@@ -53,7 +53,7 @@ static void update_dptc_counts(unsigned int level, unsigned int wp)
53} 53}
54 54
55 55
56static inline uint32_t check_regulator_setting(uint32_t setting) 56static uint32_t check_regulator_setting(uint32_t setting)
57{ 57{
58 /* Simply a safety check *in case* table gets scrambled */ 58 /* Simply a safety check *in case* table gets scrambled */
59 if (setting < VOLTAGE_SETTING_MIN) 59 if (setting < VOLTAGE_SETTING_MIN)
@@ -374,10 +374,18 @@ static void dvfs_stop(void)
374/** DPTC **/ 374/** DPTC **/
375 375
376/* Request tracking since boot */ 376/* Request tracking since boot */
377static bool dptc_running = false; /* Has driver enabled DPTC? */
378
377unsigned int dptc_nr_dn = 0; 379unsigned int dptc_nr_dn = 0;
378unsigned int dptc_nr_up = 0; 380unsigned int dptc_nr_up = 0;
379unsigned int dptc_nr_pnc = 0; 381unsigned int dptc_nr_pnc = 0;
380 382
383static struct spi_transfer_desc dptc_pmic_xfer; /* Transfer descriptor */
384static const unsigned char dptc_pmic_regs[2] = /* Register subaddresses */
385{ MC13783_SWITCHERS0, MC13783_SWITCHERS1 };
386static uint32_t dptc_reg_shadows[2]; /* shadow regs */
387static uint32_t dptc_regs_buf[2]; /* buffer for async write */
388
381 389
382/* Enable DPTC and unmask interrupt. */ 390/* Enable DPTC and unmask interrupt. */
383static void enable_dptc(void) 391static void enable_dptc(void)
@@ -397,123 +405,91 @@ static void enable_dptc(void)
397} 405}
398 406
399 407
400static void dptc_new_wp(unsigned int wp) 408/* Called after final PMIC read is completed */
409static void dptc_transfer_done_callback(struct spi_transfer_desc *xfer)
401{ 410{
402 unsigned int level = dvfs_level; 411 if (xfer->count != 0)
403 const union dvfs_dptc_voltage_table_entry *entry = &dvfs_dptc_voltage_table[wp]; 412 return;
404
405 uint32_t sw1a = check_regulator_setting(entry->sw1a);
406 uint32_t sw1advs = check_regulator_setting(entry->sw1advs);
407 uint32_t sw1bdvs = check_regulator_setting(entry->sw1bdvs);
408 uint32_t sw1bstby = check_regulator_setting(entry->sw1bstby);
409
410 dptc_wp = wp;
411
412 mc13783_write_masked(MC13783_SWITCHERS0,
413 sw1a << MC13783_SW1A_POS | /* SW1A */
414 sw1advs << MC13783_SW1ADVS_POS, /* SW1ADVS */
415 MC13783_SW1A | MC13783_SW1ADVS);
416
417 mc13783_write_masked(MC13783_SWITCHERS1,
418 sw1bdvs << MC13783_SW1BDVS_POS | /* SW1BDVS */
419 sw1bstby << MC13783_SW1BSTBY_POS, /* SW1BSTBY */
420 MC13783_SW1BDVS | MC13783_SW1BSTBY);
421
422 413
423 udelay(100); /* Wait to settle */ 414 update_dptc_counts(dvfs_level, dptc_wp);
424 415
425 update_dptc_counts(level, wp); 416 if (dptc_running)
417 enable_dptc();
426} 418}
427 419
428 420
429/* DPTC service thread */ 421/* Handle the DPTC interrupt and sometimes the manual setting */
430#ifdef ROCKBOX_HAS_LOGF 422static void dptc_int(unsigned long pmcr0)
431#define DPTC_STACK_SIZE DEFAULT_STACK_SIZE
432#else
433#define DPTC_STACK_SIZE 160
434#endif
435static int dptc_thread_stack[DPTC_STACK_SIZE/sizeof(int)];
436static const char * const dptc_thread_name = "dptc";
437static struct wakeup dptc_wakeup; /* Object to signal upon DPTC event */
438static struct mutex dptc_mutex; /* Avoid mutually disrupting voltage updates */
439static unsigned long dptc_int_data; /* Data passed to thread for each event */
440static bool dptc_running = false; /* Has driver enabled DPTC? */
441
442
443static void dptc_interrupt_thread(void)
444{ 423{
445 int wp; 424 const union dvfs_dptc_voltage_table_entry *entry;
425 uint32_t sw1a, sw1advs, sw1bdvs, sw1bstby;
446 426
447 mutex_lock(&dptc_mutex); 427 int wp = dptc_wp;
428
429 /* Mask DPTC interrupt and disable DPTC until the change request is
430 * serviced. */
431 CCM_PMCR0 = (pmcr0 & ~CCM_PMCR0_DPTEN) | CCM_PMCR0_PTVAIM;
448 432
449 while (1) 433 switch (pmcr0 & CCM_PMCR0_PTVAI)
450 { 434 {
451 mutex_unlock(&dptc_mutex); 435 case CCM_PMCR0_PTVAI_DECREASE:
436 wp++;
437 dptc_nr_dn++;
438 break;
452 439
453 wakeup_wait(&dptc_wakeup, TIMEOUT_BLOCK); 440 case CCM_PMCR0_PTVAI_INCREASE:
441 wp--;
442 dptc_nr_up++;
443 break;
454 444
455 mutex_lock(&dptc_mutex); 445 case CCM_PMCR0_PTVAI_INCREASE_NOW:
446 if (--wp > DPTC_WP_PANIC)
447 wp = DPTC_WP_PANIC;
448 dptc_nr_pnc++;
449 break;
456 450
457 if (!dptc_running) 451 case CCM_PMCR0_PTVAI_NO_INT:
458 continue; 452 break; /* Just maintain at global level */
453 }
459 454
460 wp = dptc_wp; 455 if (wp < 0)
456 wp = 0;
457 else if (wp >= DPTC_NUM_WP)
458 wp = DPTC_NUM_WP - 1;
461 459
462 switch (dptc_int_data & CCM_PMCR0_PTVAI) 460 entry = &dvfs_dptc_voltage_table[wp];
463 {
464 case CCM_PMCR0_PTVAI_DECREASE:
465 wp++;
466 dptc_nr_dn++;
467 break;
468 461
469 case CCM_PMCR0_PTVAI_INCREASE: 462 sw1a = check_regulator_setting(entry->sw1a);
470 wp--; 463 sw1advs = check_regulator_setting(entry->sw1advs);
471 dptc_nr_up++; 464 sw1bdvs = check_regulator_setting(entry->sw1bdvs);
472 break; 465 sw1bstby = check_regulator_setting(entry->sw1bstby);
473 466
474 case CCM_PMCR0_PTVAI_INCREASE_NOW: 467 dptc_regs_buf[0] = dptc_reg_shadows[0] |
475 wp = DPTC_WP_PANIC; 468 sw1a << MC13783_SW1A_POS | /* SW1A */
476 dptc_nr_pnc++; 469 sw1advs << MC13783_SW1ADVS_POS; /* SW1ADVS */
477 break; 470 dptc_regs_buf[1] = dptc_reg_shadows[1] |
471 sw1bdvs << MC13783_SW1BDVS_POS | /* SW1BDVS */
472 sw1bstby << MC13783_SW1BSTBY_POS; /* SW1BSTBY */
478 473
479 case CCM_PMCR0_PTVAI_NO_INT: 474 dptc_wp = wp;
480 logf("DPTC: unexpected INT");
481 continue;
482 }
483 475
484 if (wp < 0) 476 mc13783_write_async(&dptc_pmic_xfer, dptc_pmic_regs,
485 { 477 dptc_regs_buf, 2, dptc_transfer_done_callback);
486 wp = 0; 478}
487 logf("DPTC: already @ highest (%d)", wp);
488 }
489 else if (wp >= DPTC_NUM_WP)
490 {
491 wp = DPTC_NUM_WP - 1;
492 logf("DPTC: already @ lowest (%d)", wp);
493 }
494 else
495 {
496 logf("DPTC: new wp (%d)", wp);
497 }
498 479
499 dptc_new_wp(wp); 480
500 enable_dptc(); 481static void dptc_new_wp(unsigned int wp)
501 } 482{
483 dptc_wp = wp;
484 /* "NO_INT" so the working point isn't incremented, just set. */
485 dptc_int((CCM_PMCR0 & ~CCM_PMCR0_PTVAI) | CCM_PMCR0_PTVAI_NO_INT);
502} 486}
503 487
504 488
505/* Interrupt vector for DPTC */ 489/* Interrupt vector for DPTC */
506static __attribute__((interrupt("IRQ"))) void CCM_CLK_HANDLER(void) 490static __attribute__((interrupt("IRQ"))) void CCM_CLK_HANDLER(void)
507{ 491{
508 /* Snapshot the interrupt cause */ 492 dptc_int(CCM_PMCR0);
509 unsigned long pmcr0 = CCM_PMCR0;
510 dptc_int_data = pmcr0;
511
512 /* Mask DPTC interrupt and disable DPTC until the change request is
513 * serviced. */
514 CCM_PMCR0 = (pmcr0 & ~CCM_PMCR0_DPTEN) | CCM_PMCR0_PTVAIM;
515
516 wakeup_signal(&dptc_wakeup);
517} 493}
518 494
519 495
@@ -524,23 +500,27 @@ static void dptc_init(void)
524 imx31_regmod32(&CCM_PMCR0, CCM_PMCR0_PTVAIM, 500 imx31_regmod32(&CCM_PMCR0, CCM_PMCR0_PTVAIM,
525 CCM_PMCR0_PTVAIM | CCM_PMCR0_DPTEN); 501 CCM_PMCR0_PTVAIM | CCM_PMCR0_DPTEN);
526 502
503 /* Shadow the regulator registers */
504 mc13783_read_regs(dptc_pmic_regs, dptc_reg_shadows, 2);
505
506 /* Pre-mask the fields we change */
507 dptc_reg_shadows[0] &= ~(MC13783_SW1A | MC13783_SW1ADVS);
508 dptc_reg_shadows[1] &= ~(MC13783_SW1BDVS | MC13783_SW1BSTBY);
509
527 /* Set default, safe working point. */ 510 /* Set default, safe working point. */
528 dptc_new_wp(DPTC_WP_DEFAULT); 511 dptc_new_wp(DPTC_WP_DEFAULT);
529 512
530 /* Interrupt goes to MCU, specified reference circuits enabled when 513 /* Interrupt goes to MCU, specified reference circuits enabled when
531 * DPTC is active. */ 514 * DPTC is active. */
532 imx31_regset32(&CCM_PMCR0, CCM_PMCR0_PTVIS | DPTC_DRCE_MASK); 515 imx31_regset32(&CCM_PMCR0, CCM_PMCR0_PTVIS);
516
517 imx31_regmod32(&CCM_PMCR0, DPTC_DRCE_MASK,
518 CCM_PMCR0_DRCE0 | CCM_PMCR0_DRCE1 |
519 CCM_PMCR0_DRCE2 | CCM_PMCR0_DRCE3);
533 520
534 /* DPTC counting range = 256 system clocks */ 521 /* DPTC counting range = 256 system clocks */
535 imx31_regclr32(&CCM_PMCR0, CCM_PMCR0_DCR); 522 imx31_regclr32(&CCM_PMCR0, CCM_PMCR0_DCR);
536 523
537 /* Create PMIC regulator service. */
538 wakeup_init(&dptc_wakeup);
539 mutex_init(&dptc_mutex);
540 create_thread(dptc_interrupt_thread,
541 dptc_thread_stack, sizeof(dptc_thread_stack), 0,
542 dptc_thread_name IF_PRIO(, PRIORITY_REALTIME_1) IF_COP(, CPU));
543
544 logf("DPTC: Initialized"); 524 logf("DPTC: Initialized");
545} 525}
546 526
@@ -548,11 +528,7 @@ static void dptc_init(void)
548/* Start DPTC module */ 528/* Start DPTC module */
549static void dptc_start(void) 529static void dptc_start(void)
550{ 530{
551 int oldstate; 531 int oldlevel = disable_irq_save();
552
553 mutex_lock(&dptc_mutex);
554
555 oldstate = disable_irq_save();
556 532
557 if (!dptc_running) 533 if (!dptc_running)
558 { 534 {
@@ -566,9 +542,7 @@ static void dptc_start(void)
566 enable_dptc(); 542 enable_dptc();
567 } 543 }
568 544
569 restore_irq(oldstate); 545 restore_irq(oldlevel);
570
571 mutex_unlock(&dptc_mutex);
572 546
573 logf("DPTC: started"); 547 logf("DPTC: started");
574} 548}
@@ -577,28 +551,20 @@ static void dptc_start(void)
577/* Stop the DPTC hardware if running and go back to default working point */ 551/* Stop the DPTC hardware if running and go back to default working point */
578static void dptc_stop(void) 552static void dptc_stop(void)
579{ 553{
580 int oldlevel; 554 int oldlevel = disable_irq_save();
581
582 mutex_lock(&dptc_mutex);
583
584 oldlevel = disable_irq_save();
585 555
586 if (dptc_running) 556 if (dptc_running)
587 { 557 {
588 /* Disable DPTC and mask interrupt. */ 558 /* Disable DPTC and mask interrupt. */
589 CCM_PMCR0 = (CCM_PMCR0 & ~CCM_PMCR0_DPTEN) | CCM_PMCR0_PTVAIM; 559 CCM_PMCR0 = (CCM_PMCR0 & ~CCM_PMCR0_DPTEN) | CCM_PMCR0_PTVAIM;
590 avic_disable_int(INT_CCM_CLK); 560 avic_disable_int(INT_CCM_CLK);
591 dptc_int_data = 0;
592
593 dptc_running = false; 561 dptc_running = false;
594 } 562 }
595 563
596 restore_irq(oldlevel);
597
598 /* Go back to default working point. */ 564 /* Go back to default working point. */
599 dptc_new_wp(DPTC_WP_DEFAULT); 565 dptc_new_wp(DPTC_WP_DEFAULT);
600 566
601 mutex_unlock(&dptc_mutex); 567 restore_irq(oldlevel);
602 568
603 logf("DPTC: stopped"); 569 logf("DPTC: stopped");
604} 570}
@@ -618,10 +584,7 @@ void dvfs_dptc_init(void)
618void dvfs_dptc_start(void) 584void dvfs_dptc_start(void)
619{ 585{
620 dvfs_start(); 586 dvfs_start();
621 if (0) /* Hold off for now */ 587 dptc_start();
622 {
623 dptc_start();
624 }
625} 588}
626 589
627 590
@@ -731,12 +694,10 @@ unsigned int dptc_get_wp(void)
731/* If DPTC is not running, set the working point explicitly */ 694/* If DPTC is not running, set the working point explicitly */
732void dptc_set_wp(unsigned int wp) 695void dptc_set_wp(unsigned int wp)
733{ 696{
734 mutex_lock(&dptc_mutex); 697 int oldlevel = disable_irq_save();
735 698
736 if (!dptc_running && wp < DPTC_NUM_WP) 699 if (!dptc_running && wp < DPTC_NUM_WP)
737 {
738 dptc_new_wp(wp); 700 dptc_new_wp(wp);
739 }
740 701
741 mutex_unlock(&dptc_mutex); 702 restore_irq(oldlevel);
742} 703}