Repository navigation
PM runtime dynamic clock switching implementation #3205
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
59c3315
59aa292
813ae33
ceeabab
f47228a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1 @@ | ||
| CONFIG_CAVS_LPS=y | ||
| CONFIG_CAVS_LPRO=y |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1 @@ | ||
| CONFIG_CAVS_LPS=y | ||
| CONFIG_CAVS_LPRO=y |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,2 @@ | ||
| CONFIG_CAVS_LPS=y | ||
| CONFIG_CAVS_LPRO=y | ||
| CONFIG_PROBE=y |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -94,22 +94,46 @@ static inline void select_cpu_clock(int freq_idx, bool release_unused) | |
| sizeof(*clk_info) * PLATFORM_CORE_COUNT); | ||
| } | ||
|
|
||
| #if CONFIG_CAVS_USE_LPRO_IN_WAITI | ||
| /* Store clock source that was active before going to waiti, | ||
| * so it can be restored on wake up. | ||
| /* LPRO_ONLY mode */ | ||
| #if CONFIG_CAVS_LPRO_ONLY | ||
|
|
||
| /* | ||
| * This is call from public API, there is no clock switch for core waiti, so | ||
| * value of value frequency index in 'active' state doesn't need to be saved in | ||
| * any additional variable and restored in future. | ||
| */ | ||
| static SHARED_DATA int active_freq_idx = CPU_DEFAULT_IDX; | ||
| static inline void set_cpu_current_freq_idx(int freq_idx, bool release_unused) | ||
| { | ||
| select_cpu_clock(freq_idx, true); | ||
| } | ||
|
|
||
| static inline int get_current_freq_idx(int clock) | ||
| static void platform_clock_low_power_mode(int clock, bool enable) | ||
| { | ||
| struct clock_info *clk_info = clocks_get() + clock; | ||
| /* do nothing for LPRO_ONLY mode */ | ||
| } | ||
|
|
||
| return clk_info->current_freq_idx; | ||
| void platform_clock_on_waiti(void) | ||
| { | ||
| /* do nothing for LPRO_ONLY mode */ | ||
| } | ||
|
|
||
| void platform_clock_on_wakeup(void) | ||
| { | ||
| /* do nothing for LPRO_ONLY mode */ | ||
| } | ||
|
|
||
| /* USE_LPRO_IN_WAITI mode */ | ||
| #elif CONFIG_CAVS_USE_LPRO_IN_WAITI | ||
|
|
||
| /* Store clock source that was active before going to waiti, | ||
| * so it can be restored on wake up. | ||
| */ | ||
| static SHARED_DATA int active_freq_idx = CPU_DEFAULT_IDX; | ||
|
|
||
| /* | ||
| * This is call from public API, so overwrite currently used frequency index | ||
| * in 'active' state with new value. This value will be used in each wakeup. | ||
| * Caller should hold the pm_runtime lock. | ||
| */ | ||
| static inline void set_cpu_current_freq_idx(int freq_idx, bool release_unused) | ||
| { | ||
|
|
@@ -119,6 +143,13 @@ static inline void set_cpu_current_freq_idx(int freq_idx, bool release_unused) | |
| *uncached_freq_idx = freq_idx; | ||
| } | ||
|
|
||
| static inline int get_current_freq_idx(int clock) | ||
| { | ||
| struct clock_info *clk_info = clocks_get() + clock; | ||
|
|
||
| return clk_info->current_freq_idx; | ||
| } | ||
|
|
||
| static void platform_clock_low_power_mode(int clock, bool enable) | ||
| { | ||
| int current_freq_idx = get_current_freq_idx(clock); | ||
|
|
@@ -135,30 +166,118 @@ static void platform_clock_low_power_mode(int clock, bool enable) | |
| select_cpu_clock(freq_idx, false); | ||
| } | ||
|
|
||
| void platform_clock_on_wakeup(void) | ||
| void platform_clock_on_waiti(void) | ||
| { | ||
| pm_runtime_get(CORE_HP_CLK, cpu_get_id()); | ||
| struct pm_runtime_data *prd = pm_runtime_data_get(); | ||
| uint32_t flags; | ||
| int freq_idx; | ||
| bool pm_is_active; | ||
|
|
||
| /* hold the prd->lock for possible active_freq_idx switching */ | ||
| spin_lock_irq(&prd->lock, flags); | ||
|
|
||
| freq_idx = *cache_to_uncache(&active_freq_idx); | ||
| pm_is_active = pm_runtime_is_active(PM_RUNTIME_DSP, PLATFORM_MASTER_CORE_ID); | ||
|
|
||
| if (pm_is_active) { | ||
| /* set HPRO clock if not already enabled */ | ||
| if (freq_idx != CPU_HPRO_FREQ_IDX) | ||
| set_cpu_current_freq_idx(CPU_HPRO_FREQ_IDX, true); | ||
| } else { | ||
| /* set LPRO clock if not already enabled */ | ||
| if (freq_idx != CPU_LPRO_FREQ_IDX) | ||
| set_cpu_current_freq_idx(CPU_LPRO_FREQ_IDX, true); | ||
| } | ||
|
|
||
| spin_unlock_irq(&prd->lock, flags); | ||
|
|
||
| /* check if waiti HPRO->LPRO switching is needed */ | ||
| pm_runtime_put(CORE_HP_CLK, cpu_get_id()); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This version of on_waiti/on_wakeup is removed by the previous commit and restored by this one. Should those two ones be squashed to avoid intermediate commit that has some regression?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good point, let me squash them. |
||
| } | ||
|
|
||
| void platform_clock_on_waiti(void) | ||
| void platform_clock_on_wakeup(void) | ||
| { | ||
| pm_runtime_put(CORE_HP_CLK, cpu_get_id()); | ||
| /* check if LPRO->HPRO switching back is needed */ | ||
| pm_runtime_get(CORE_HP_CLK, cpu_get_id()); | ||
| } | ||
|
|
||
| #else | ||
|
|
||
| /* Store clock source that was active before going to waiti, | ||
| * so it can be restored on wake up. | ||
| */ | ||
| static SHARED_DATA int active_freq_idx = CPU_DEFAULT_IDX; | ||
|
|
||
| /* | ||
| * This is call from public API, there is no clock switch for core waiti, so | ||
| * value of value frequency index in 'active' state doesn't need to be saved in | ||
| * any additional variable and restored in future. | ||
| * This is call from public API, so overwrite currently used frequency index | ||
| * in 'active' state with new value. This value will be used in each wakeup. | ||
| * Caller should hold the pm_runtime lock. | ||
| */ | ||
| static inline void set_cpu_current_freq_idx(int freq_idx, bool release_unused) | ||
| { | ||
| select_cpu_clock(freq_idx, true); | ||
| int *uncached_freq_idx = cache_to_uncache(&active_freq_idx); | ||
|
|
||
| select_cpu_clock(freq_idx, release_unused); | ||
| *uncached_freq_idx = freq_idx; | ||
| } | ||
|
|
||
| static inline int get_current_freq_idx(int clock) | ||
| { | ||
| struct clock_info *clk_info = clocks_get() + clock; | ||
|
|
||
| return clk_info->current_freq_idx; | ||
| } | ||
|
|
||
| static void platform_clock_low_power_mode(int clock, bool enable) | ||
| { | ||
| } | ||
|
|
||
| void platform_clock_on_waiti(void) | ||
| { | ||
| struct pm_runtime_data *prd = pm_runtime_data_get(); | ||
| uint32_t flags; | ||
| int freq_idx; | ||
| bool pm_is_active; | ||
|
|
||
| /* hold the prd->lock for possible active_freq_idx switching */ | ||
| spin_lock_irq(&prd->lock, flags); | ||
|
|
||
| freq_idx = *cache_to_uncache(&active_freq_idx); | ||
| pm_is_active = pm_runtime_is_active(PM_RUNTIME_DSP, PLATFORM_MASTER_CORE_ID); | ||
|
|
||
| if (pm_is_active) { | ||
| /* set HPRO clock if not already enabled */ | ||
| if (freq_idx != CPU_HPRO_FREQ_IDX) | ||
| set_cpu_current_freq_idx(CPU_HPRO_FREQ_IDX, true); | ||
| } else { | ||
| /* set LPRO clock if not already enabled */ | ||
| if (freq_idx != CPU_LPRO_FREQ_IDX) | ||
| set_cpu_current_freq_idx(CPU_LPRO_FREQ_IDX, true); | ||
| } | ||
|
|
||
| spin_unlock_irq(&prd->lock, flags); | ||
| } | ||
|
|
||
| void platform_clock_on_wakeup(void) | ||
| { | ||
| struct pm_runtime_data *prd = pm_runtime_data_get(); | ||
| uint32_t flags; | ||
| int current_idx; | ||
| int target_idx; | ||
|
|
||
| /* hold the prd->lock for possible active_freq_idx switching */ | ||
| spin_lock_irq(&prd->lock, flags); | ||
|
|
||
| current_idx = get_current_freq_idx(CLK_CPU(cpu_get_id())); | ||
| target_idx = *cache_to_uncache(&active_freq_idx); | ||
|
|
||
| /* restore the active cpu freq_idx manually */ | ||
| if (current_idx != target_idx) | ||
| set_cpu_current_freq_idx(target_idx, true); | ||
|
|
||
| spin_unlock_irq(&prd->lock, flags); | ||
| } | ||
|
|
||
| #endif | ||
|
|
||
| static int clock_platform_set_cpu_freq(int clock, int freq_idx) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's probably best to put this depends CONFIG_DEBUG sine it's debug only.
I would also say "Force use of Low Power Oscillator"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, let me add this dependency. @mmaka1 @lbetlej are you OK for this?
for Dynamic switching mode, the LPRO is also used, so anyway LPRO will always be used/enabled, LPRO_ONLY is more accurate here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd not couple CONFIG_DEBUG and LPRO_ONLY since the former is used to compile extra debugability which may not fit into smaller MCPS budget. On the other hand, it is still useful to create special non-debug build with LPRO_ONLY turned on and run simple pipeline test for quick verification of MCPS consumption by base components.