diff --git a/apps/debug_menu.c b/apps/debug_menu.c index 968355f518..d0b919fea3 100644 --- a/apps/debug_menu.c +++ b/apps/debug_menu.c @@ -2345,6 +2345,9 @@ static const struct the_menu_item menuitems[] = { #ifdef HAVE_ADJUSTABLE_CPU_FREQ { "CPU frequency", dbg_cpufreq }, #endif +#if CONFIG_CPU == IMX31L + { "DVFS/DPTC", __dbg_dvfs_dptc }, +#endif #if defined(IRIVER_H100_SERIES) && !defined(SIMULATOR) { "S/PDIF analyzer", dbg_spdif }, #endif diff --git a/firmware/target/arm/imx31/debug-imx31.c b/firmware/target/arm/imx31/debug-imx31.c index 1239c7cae7..f8dacbedd5 100644 --- a/firmware/target/arm/imx31/debug-imx31.c +++ b/firmware/target/arm/imx31/debug-imx31.c @@ -29,6 +29,7 @@ #include "adc.h" #include "ccm-imx31.h" #include "dvfs_dptc-imx31.h" +#include bool __dbg_hw_info(void) { @@ -242,4 +243,148 @@ bool dbg_ports(void) if (button_get_w_tmo(HZ/10) == (DEBUG_CANCEL|BUTTON_REL)) return false; } -} +} + + +bool __dbg_dvfs_dptc(void) +{ + int ltwlevel; + unsigned long ltdetect; + int dvfs_wp, dvfs_mask; + bool dptc_on; + int i; + char buf[32]; + unsigned long ltw[4]; + bool ltwassert[4]; + + lcd_clear_display(); + lcd_setfont(FONT_SYSFIXED); + + dvfs_mask = dvfs_level_mask(); + + dvfs_wp = dvfs_enabled() ? -1 : (int)dvfs_get_level(); + dptc_on = dptc_enabled(); + dvfs_get_gp_sense(<wlevel, <detect); + + while (1) + { + int line = 0; + + int button = button_get_w_tmo(HZ/10); + + if (dvfs_wp < 0) + strcpy(buf, "Auto"); + else + snprintf(buf, sizeof(buf), "%d", dvfs_wp); + + lcd_puts(0, line, "[DVFS/DPTC]"); + line += 2; + lcd_putsf(0, line, "CPU freq. point (Up/Dn) : %s", buf); + line += 2; + lcd_putsf(0, line, "DPTC volt. scale (Play) : %s", + dptc_on ? "Enabled" : "Disabled"); + line += 2; + lcd_putsf(0, line, "GP load level (Vol +/-) : %d", ltwlevel); + line += 2; + lcd_puts(0, line, "----------------------------------------"); + line += 2; + lcd_putsf(0, line++, "Frequency: %dHz", cpu_frequency); + i = dvfs_dptc_get_voltage(); + lcd_putsf(0, line++, "Voltage : %d.%03d V", i / 1000, i % 1000); + + for (i = 0; i <= 3; i++) + { + ltw[i] = dvfs_get_lt_weight(i + DVFS_LT_SIG_DVGP0); + ltwassert[i] = dvfs_get_gp_bit(i + DVFS_DVGP_0); + } + + lcd_putsf(0, line++, "GPW (3-0): %lu%lu%lu%lu %c%c%c%c", + ltw[3], ltw[2], ltw[1], ltw[0], + ltwassert[3] ? 'y' : 'n', + ltwassert[2] ? 'y' : 'n', + ltwassert[1] ? 'y' : 'n', + ltwassert[0] ? 'y' : 'n'); + + line += 2; + lcd_puts(8, line, "(Press SELECT to revert)"); + + switch (button) + { + case DEBUG_CANCEL|BUTTON_REL: + return false; + + /* CPU frequency */ + case BUTTON_UP: + if (++dvfs_wp >= DVFS_NUM_LEVELS) + { + /* Going back to automatic */ + dvfs_wp = -1; + dvfs_start(); + } + else + { + if (dvfs_wp == 0) + { + /* Going to manual setting */ + dvfs_stop(); + } + + /* Skip gaps in mask */ + while (((1 << dvfs_wp) & dvfs_mask) == 0) dvfs_wp++; + dvfs_set_level(dvfs_wp); + } + + break; + + case BUTTON_DOWN: + if (--dvfs_wp == -1) + { + /* Going back to automatic */ + dvfs_start(); + } + else + { + if (dvfs_wp <= -2) + { + /* Going to manual setting */ + dvfs_stop(); + dvfs_wp = DVFS_NUM_LEVELS - 1; + } + + /* Skip gaps in mask */ + while (((1 << dvfs_wp) & dvfs_mask) == 0) dvfs_wp--; + dvfs_set_level(dvfs_wp); + } + break; + + /* GP Load tracking */ + case BUTTON_VOL_UP: + if (ltwlevel < 28) + dvfs_set_gp_sense(++ltwlevel, ltdetect); + break; + + case BUTTON_VOL_DOWN: + if (ltwlevel > 0) + dvfs_set_gp_sense(--ltwlevel, ltdetect); + break; + + case BUTTON_PLAY: + dptc_on = !dptc_enabled(); + dptc_on ? dptc_start() : dptc_stop(); + break; + + case BUTTON_SELECT: + dvfs_start(); + dptc_start(); + dvfs_set_gp_sense(-1, 0); + + dvfs_wp = dvfs_enabled() ? -1 : (int)dvfs_get_level(); + dptc_on = dptc_enabled(); + dvfs_get_gp_sense(<wlevel, <detect); + break; + } + + lcd_update(); + yield(); + } +} diff --git a/firmware/target/arm/imx31/debug-target.h b/firmware/target/arm/imx31/debug-target.h index 06baee5ea0..ca02ab84de 100644 --- a/firmware/target/arm/imx31/debug-target.h +++ b/firmware/target/arm/imx31/debug-target.h @@ -24,5 +24,6 @@ #define DEBUG_CANCEL BUTTON_BACK bool __dbg_hw_info(void); bool dbg_ports(void); +bool __dbg_dvfs_dptc(void); #endif /* DEBUG_TARGET_H */ diff --git a/firmware/target/arm/imx31/dvfs_dptc-imx31.c b/firmware/target/arm/imx31/dvfs_dptc-imx31.c index 555e030af5..b78a995f87 100644 --- a/firmware/target/arm/imx31/dvfs_dptc-imx31.c +++ b/firmware/target/arm/imx31/dvfs_dptc-imx31.c @@ -74,8 +74,6 @@ unsigned int dvfs_nr_up = 0; unsigned int dvfs_nr_pnc = 0; unsigned int dvfs_nr_no = 0; -static void dvfs_stop(void); - /* Wait for the UPDTEN flag to be set so that all bits may be written */ static inline void wait_for_dvfs_update_en(void) @@ -279,6 +277,9 @@ static void INIT_ATTR dvfs_init(void) iomuxc_set_pin_mux(IOMUXC_GPIO1_5, IOMUXC_MUX_OUT_FUNCTIONAL | IOMUXC_MUX_IN_FUNCTIONAL); #endif + + /* GP load bits disabled */ + bitclr32(&CCM_PMCR1, 0xf); /* Initialize DVFS signal weights and detection modes. */ int i; @@ -321,66 +322,6 @@ static void INIT_ATTR dvfs_init(void) logf("DVFS: Initialized"); } - -/* Start the DVFS hardware */ -static void dvfs_start(void) -{ - int oldlevel; - - /* Have to wait at least 3 div3 clocks before enabling after being - * stopped. */ - udelay(1500); - - oldlevel = disable_irq_save(); - - if (!dvfs_running) - { - dvfs_running = true; - - /* Unmask DVFS interrupt source and enable DVFS. */ - avic_enable_int(INT_CCM_DVFS, INT_TYPE_IRQ, INT_PRIO_DVFS, - CCM_DVFS_HANDLER); - - CCM_PMCR0 = (CCM_PMCR0 & ~CCM_PMCR0_FSVAIM) | CCM_PMCR0_DVFEN; - } - - restore_irq(oldlevel); - - logf("DVFS: started"); -} - - -/* Stop the DVFS hardware and return to default frequency */ -static void dvfs_stop(void) -{ - int oldlevel = disable_irq_save(); - - if (dvfs_running) - { - /* Mask DVFS interrupts. */ - CCM_PMCR0 |= CCM_PMCR0_FSVAIM | CCM_PMCR0_LBMI; - avic_disable_int(INT_CCM_DVFS); - - if (((CCM_PMCR0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS) != - DVFS_LEVEL_DEFAULT) - { - /* Set default frequency level */ - wait_for_dvfs_update_en(); - do_dvfs_update(DVFS_LEVEL_DEFAULT, false); - wait_for_dvfs_update_en(); - } - - /* Disable DVFS. */ - CCM_PMCR0 &= ~CCM_PMCR0_DVFEN; - dvfs_running = false; - } - - restore_irq(oldlevel); - - logf("DVFS: stopped"); -} - - /** DPTC **/ /* Request tracking since boot */ @@ -540,52 +481,6 @@ static void INIT_ATTR dptc_init(void) } -/* Start DPTC module */ -static void dptc_start(void) -{ - int oldlevel = disable_irq_save(); - - if (!dptc_running) - { - dptc_running = true; - - /* Enable DPTC and unmask interrupt. */ - avic_enable_int(INT_CCM_CLK, INT_TYPE_IRQ, INT_PRIO_DPTC, - CCM_CLK_HANDLER); - - update_dptc_counts(dvfs_level, dptc_wp); - enable_dptc(); - } - - restore_irq(oldlevel); - - logf("DPTC: started"); -} - - -/* Stop the DPTC hardware if running and go back to default working point */ -static void dptc_stop(void) -{ - int oldlevel = disable_irq_save(); - - if (dptc_running) - { - dptc_running = false; - - /* Disable DPTC and mask interrupt. */ - CCM_PMCR0 = (CCM_PMCR0 & ~CCM_PMCR0_DPTEN) | CCM_PMCR0_PTVAIM; - avic_disable_int(INT_CCM_CLK); - - /* Go back to default working point. */ - dptc_new_wp(DPTC_WP_DEFAULT); - } - - restore_irq(oldlevel); - - logf("DPTC: stopped"); -} - - /** Main module interface **/ /* Initialize DVFS and DPTC */ @@ -611,6 +506,64 @@ void dvfs_dptc_stop(void) dvfs_stop(); } +/* Start the DVFS hardware */ +void dvfs_start(void) +{ + int oldlevel; + + /* Have to wait at least 3 div3 clocks before enabling after being + * stopped. */ + udelay(1500); + + oldlevel = disable_irq_save(); + + if (!dvfs_running) + { + dvfs_running = true; + + /* Unmask DVFS interrupt source and enable DVFS. */ + avic_enable_int(INT_CCM_DVFS, INT_TYPE_IRQ, INT_PRIO_DVFS, + CCM_DVFS_HANDLER); + + CCM_PMCR0 = (CCM_PMCR0 & ~CCM_PMCR0_FSVAIM) | CCM_PMCR0_DVFEN; + } + + restore_irq(oldlevel); + + logf("DVFS: started"); +} + + +/* Stop the DVFS hardware and return to default frequency */ +void dvfs_stop(void) +{ + int oldlevel = disable_irq_save(); + + if (dvfs_running) + { + /* Mask DVFS interrupts. */ + CCM_PMCR0 |= CCM_PMCR0_FSVAIM | CCM_PMCR0_LBMI; + avic_disable_int(INT_CCM_DVFS); + + if (((CCM_PMCR0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS) != + DVFS_LEVEL_DEFAULT) + { + /* Set default frequency level */ + wait_for_dvfs_update_en(); + do_dvfs_update(DVFS_LEVEL_DEFAULT, false); + wait_for_dvfs_update_en(); + } + + /* Disable DVFS. */ + CCM_PMCR0 &= ~CCM_PMCR0_DVFEN; + dvfs_running = false; + } + + restore_irq(oldlevel); + + logf("DVFS: stopped"); +} + /* Mask the DVFS interrupt without affecting running status */ void dvfs_int_mask(bool mask) @@ -648,6 +601,26 @@ void dvfs_set_lt_weight(enum DVFS_LT_SIGS index, unsigned long value) } +/* Return a signal load tracking weight */ +unsigned long dvfs_get_lt_weight(enum DVFS_LT_SIGS index) +{ + volatile unsigned long *reg_p = &CCM_LTR2; + unsigned int shift = 3 * index; + + if (index < 9) + { + reg_p = &CCM_LTR3; + shift += 5; /* Bits 7:5, 10:8 ... 31:29 */ + } + else if (index < 16) + { + shift -= 16; /* Bits 13:11, 16:14 ... 31:29 */ + } + + return (*reg_p & (0x7 << shift)) >> shift; +} + + /* Set a signal load detection mode */ void dvfs_set_lt_detect(enum DVFS_LT_SIGS index, bool edge) { @@ -662,6 +635,21 @@ void dvfs_set_lt_detect(enum DVFS_LT_SIGS index, bool edge) } +/* Returns a signal load detection mode */ +bool dvfs_get_lt_detect(enum DVFS_LT_SIGS index) +{ + unsigned int shift = 32; + + if ((unsigned)index < 13) + shift = index + 3; + else if ((unsigned)index < 16) + shift = index + 29; + + return !!((CCM_LTR0 & (1ul << shift)) >> shift); +} + + +/* Set/clear the general-purpose load tracking bit */ void dvfs_set_gp_bit(enum DVFS_DVGPS dvgp, bool assert) { if ((unsigned)dvgp <= 3) @@ -672,6 +660,82 @@ void dvfs_set_gp_bit(enum DVFS_DVGPS dvgp, bool assert) } +/* Return the general-purpose load tracking bit */ +bool dvfs_get_gp_bit(enum DVFS_DVGPS dvgp) +{ + if ((unsigned)dvgp <= 3) + return (CCM_PMCR1 & (1ul << dvgp)) != 0; + return false; +} + + +/* Set GP load tracking by code. + * level_code: + * lt 0 =defaults + * 0 =all off -> + * 28 =highest load + * gte 28=highest load + * detect_mask bits: + * b[3:0]: 1=LTn edge detect, 0=LTn level detect + */ +void dvfs_set_gp_sense(int level_code, unsigned long detect_mask) +{ + int i; + + for (i = 0; i <= 3; i++) + { + int ltsig_num = DVFS_LT_SIG_DVGP0 + i; + int gpw_num = DVFS_DVGP_0 + i; + unsigned long weight; + bool edge; + bool assert; + + if (level_code < 0) + { + /* defaults */ + detect_mask = 0; + assert = 0; + weight = lt_signals[ltsig_num].weight; + edge = lt_signals[ltsig_num].detect != 0; + } + else + { + weight = MIN(level_code, 7); + edge = !!(detect_mask & 1); + assert = weight > 0; + detect_mask >>= 1; + level_code -= 7; + if (level_code < 0) + level_code = 0; + } + + dvfs_set_lt_weight(ltsig_num, weight); /* set weight */ + dvfs_set_lt_detect(ltsig_num, edge); /* set detect mode */ + dvfs_set_gp_bit(gpw_num, assert); /* set activity */ + } +} + +/* Return GP weight settings */ +void dvfs_get_gp_sense(int *level_code, unsigned long *detect_mask) +{ + int i; + int code = 0; + unsigned long mask = 0; + + for (i = DVFS_LT_SIG_DVGP0; i <= DVFS_LT_SIG_DVGP3; i++) + { + code += dvfs_get_lt_weight(i); + mask = (mask << 1) | (dvfs_get_lt_detect(i) ? 1 : 0); + } + + if (level_code) + *level_code = code; + + if (detect_mask) + *detect_mask = mask; +} + + /* Turn the wait-for-interrupt monitoring on or off */ void dvfs_wfi_monitor(bool on) { @@ -700,21 +764,83 @@ unsigned int dvfs_get_level(void) } +/* Is DVFS enabled? */ +bool dvfs_enabled(void) +{ + return dvfs_running; +} + + +/* Get bitmask of levels supported */ +unsigned int dvfs_level_mask(void) +{ + return DVFS_LEVEL_MASK; +} + + /* If DVFS is disabled, set the level explicitly */ void dvfs_set_level(unsigned int level) { int oldlevel = disable_irq_save(); - unsigned int currlevel = - (CCM_PMCR0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS; - - if (!dvfs_running && level < DVFS_NUM_LEVELS && level != currlevel) - set_current_dvfs_level(level); + if (!dvfs_running && level < DVFS_NUM_LEVELS) + { + unsigned int currlevel = + (CCM_PMCR0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS; + if (level != currlevel && ((1 << level) & DVFS_LEVEL_MASK)) + set_current_dvfs_level(level); + } restore_irq(oldlevel); } +/* Start DPTC module */ +void dptc_start(void) +{ + int oldlevel = disable_irq_save(); + + if (!dptc_running) + { + dptc_running = true; + + /* Enable DPTC and unmask interrupt. */ + avic_enable_int(INT_CCM_CLK, INT_TYPE_IRQ, INT_PRIO_DPTC, + CCM_CLK_HANDLER); + + update_dptc_counts(dvfs_level, dptc_wp); + enable_dptc(); + } + + restore_irq(oldlevel); + + logf("DPTC: started"); +} + + +/* Stop the DPTC hardware if running and go back to default working point */ +void dptc_stop(void) +{ + int oldlevel = disable_irq_save(); + + if (dptc_running) + { + dptc_running = false; + + /* Disable DPTC and mask interrupt. */ + CCM_PMCR0 = (CCM_PMCR0 & ~CCM_PMCR0_DPTEN) | CCM_PMCR0_PTVAIM; + avic_disable_int(INT_CCM_CLK); + + /* Go back to default working point. */ + dptc_new_wp(DPTC_WP_DEFAULT); + } + + restore_irq(oldlevel); + + logf("DPTC: stopped"); +} + + /* Get the current DPTC working point */ unsigned int dptc_get_wp(void) { @@ -722,6 +848,13 @@ unsigned int dptc_get_wp(void) } +/* Is DPTC enabled? */ +bool dptc_enabled(void) +{ + return dptc_running; +} + + /* If DPTC is not running, set the working point explicitly */ void dptc_set_wp(unsigned int wp) { diff --git a/firmware/target/arm/imx31/dvfs_dptc-imx31.h b/firmware/target/arm/imx31/dvfs_dptc-imx31.h index 6b59bffae6..1f3f1ab20d 100644 --- a/firmware/target/arm/imx31/dvfs_dptc-imx31.h +++ b/firmware/target/arm/imx31/dvfs_dptc-imx31.h @@ -124,16 +124,28 @@ void dvfs_dptc_init(void); void dvfs_dptc_start(void); void dvfs_dptc_stop(void); +void dvfs_start(void); +void dvfs_stop(void); +bool dvfs_enabled(void); void dvfs_wfi_monitor(bool on); void dvfs_set_lt_weight(enum DVFS_LT_SIGS index, unsigned long value); +unsigned long dvfs_get_lt_weight(enum DVFS_LT_SIGS index); void dvfs_set_lt_detect(enum DVFS_LT_SIGS index, bool edge); +bool dvfs_get_lt_detect(enum DVFS_LT_SIGS index); void dvfs_set_gp_bit(enum DVFS_DVGPS dvgp, bool assert); +bool dvfs_get_gp_bit(enum DVFS_DVGPS dvgp); void dvfs_int_mask(bool mask); +void dvfs_set_gp_sense(int level_code, unsigned long detect_mask); +void dvfs_get_gp_sense(int *level_code, unsigned long *detect_mask); +unsigned int dvfs_level_mask(void); unsigned int dvfs_dptc_get_voltage(void); unsigned int dvfs_get_level(void); void dvfs_set_level(unsigned int level); +void dptc_start(void); +void dptc_stop(void); +bool dptc_enabled(void); unsigned int dptc_get_wp(void); void dptc_set_wp(unsigned int wp); diff --git a/firmware/target/arm/imx31/gigabeat-s/dvfs_dptc_tables-target.h b/firmware/target/arm/imx31/gigabeat-s/dvfs_dptc_tables-target.h index 89edf09675..f81ef83ac7 100644 --- a/firmware/target/arm/imx31/gigabeat-s/dvfs_dptc_tables-target.h +++ b/firmware/target/arm/imx31/gigabeat-s/dvfs_dptc_tables-target.h @@ -241,10 +241,10 @@ static const struct dvfs_lt_signal_descriptor lt_signals[16] = { 0, 0 }, /* DVFS_LT_SIG_ARM11_P_FIQ_B_RBT_GATE */ { 0, 0 }, /* DVFS_LT_SIG_IPI_GPIO1_INT0 */ { 0, 0 }, /* DVFS_LT_SIG_IPI_INT_IPU_FUNC */ - { 7, 0 }, /* DVFS_LT_SIG_DVGP0 */ - { 7, 0 }, /* DVFS_LT_SIG_DVGP1 */ - { 7, 0 }, /* DVFS_LT_SIG_DVGP2 */ - { 7, 0 }, /* DVFS_LT_SIG_DVGP3 */ + { 0, 0 }, /* DVFS_LT_SIG_DVGP0 */ + { 0, 0 }, /* DVFS_LT_SIG_DVGP1 */ + { 0, 0 }, /* DVFS_LT_SIG_DVGP2 */ + { 0, 0 }, /* DVFS_LT_SIG_DVGP3 */ }; #endif /* _DVFS_DPTC_TARGET_H_ */