diff options
Diffstat (limited to 'firmware/target/arm/imx31/dvfs_dptc-imx31.c')
-rw-r--r-- | firmware/target/arm/imx31/dvfs_dptc-imx31.c | 213 |
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 | ||
56 | static inline uint32_t check_regulator_setting(uint32_t setting) | 56 | static 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 */ |
377 | static bool dptc_running = false; /* Has driver enabled DPTC? */ | ||
378 | |||
377 | unsigned int dptc_nr_dn = 0; | 379 | unsigned int dptc_nr_dn = 0; |
378 | unsigned int dptc_nr_up = 0; | 380 | unsigned int dptc_nr_up = 0; |
379 | unsigned int dptc_nr_pnc = 0; | 381 | unsigned int dptc_nr_pnc = 0; |
380 | 382 | ||
383 | static struct spi_transfer_desc dptc_pmic_xfer; /* Transfer descriptor */ | ||
384 | static const unsigned char dptc_pmic_regs[2] = /* Register subaddresses */ | ||
385 | { MC13783_SWITCHERS0, MC13783_SWITCHERS1 }; | ||
386 | static uint32_t dptc_reg_shadows[2]; /* shadow regs */ | ||
387 | static 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. */ |
383 | static void enable_dptc(void) | 391 | static void enable_dptc(void) |
@@ -397,123 +405,91 @@ static void enable_dptc(void) | |||
397 | } | 405 | } |
398 | 406 | ||
399 | 407 | ||
400 | static void dptc_new_wp(unsigned int wp) | 408 | /* Called after final PMIC read is completed */ |
409 | static 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 | 422 | static void dptc_int(unsigned long pmcr0) |
431 | #define DPTC_STACK_SIZE DEFAULT_STACK_SIZE | ||
432 | #else | ||
433 | #define DPTC_STACK_SIZE 160 | ||
434 | #endif | ||
435 | static int dptc_thread_stack[DPTC_STACK_SIZE/sizeof(int)]; | ||
436 | static const char * const dptc_thread_name = "dptc"; | ||
437 | static struct wakeup dptc_wakeup; /* Object to signal upon DPTC event */ | ||
438 | static struct mutex dptc_mutex; /* Avoid mutually disrupting voltage updates */ | ||
439 | static unsigned long dptc_int_data; /* Data passed to thread for each event */ | ||
440 | static bool dptc_running = false; /* Has driver enabled DPTC? */ | ||
441 | |||
442 | |||
443 | static 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(); | 481 | static 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 */ |
506 | static __attribute__((interrupt("IRQ"))) void CCM_CLK_HANDLER(void) | 490 | static __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 */ |
549 | static void dptc_start(void) | 529 | static 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 */ |
578 | static void dptc_stop(void) | 552 | static 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) | |||
618 | void dvfs_dptc_start(void) | 584 | void 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 */ |
732 | void dptc_set_wp(unsigned int wp) | 695 | void 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 | } |