| /* |
| * Copyright (C) 2011-2016 ARM Limited. All rights reserved. |
| * |
| * This program is free software and is provided to you under the terms of the GNU General Public License version 2 |
| * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence. |
| * |
| * A copy of the licence is included with the program, and can also be obtained from Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "mali_pm.h" |
| #include "mali_kernel_common.h" |
| #include "mali_osk.h" |
| #include "mali_osk_mali.h" |
| #include "mali_scheduler.h" |
| #include "mali_group.h" |
| #include "mali_pm_domain.h" |
| #include "mali_pmu.h" |
| |
| #include "mali_executor.h" |
| #include "mali_control_timer.h" |
| |
| #if defined(DEBUG) |
| u32 num_pm_runtime_resume = 0; |
| u32 num_pm_updates = 0; |
| u32 num_pm_updates_up = 0; |
| u32 num_pm_updates_down = 0; |
| #endif |
| |
| #define MALI_PM_DOMAIN_DUMMY_MASK (1 << MALI_DOMAIN_INDEX_DUMMY) |
| |
| /* lock protecting power state (including pm_domains) */ |
| static _mali_osk_spinlock_irq_t *pm_lock_state = NULL; |
| |
| /* the wanted domain mask (protected by pm_lock_state) */ |
| static u32 pd_mask_wanted = 0; |
| |
| /* used to deferring the actual power changes */ |
| static _mali_osk_wq_work_t *pm_work = NULL; |
| |
| /* lock protecting power change execution */ |
| static _mali_osk_mutex_t *pm_lock_exec = NULL; |
| |
| /* PMU domains which are actually powered on (protected by pm_lock_exec) */ |
| static u32 pmu_mask_current = 0; |
| |
| /* |
| * domains which marked as powered on (protected by pm_lock_exec) |
| * This can be different from pmu_mask_current right after GPU power on |
| * if the PMU domains default to powered up. |
| */ |
| static u32 pd_mask_current = 0; |
| |
| static u16 domain_config[MALI_MAX_NUMBER_OF_DOMAINS] = { |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 1 << MALI_DOMAIN_INDEX_DUMMY |
| }; |
| |
| /* The relative core power cost */ |
| #define MALI_GP_COST 3 |
| #define MALI_PP_COST 6 |
| #define MALI_L2_COST 1 |
| |
| /* |
| *We have MALI_MAX_NUMBER_OF_PP_PHYSICAL_CORES + 1 rows in this matrix |
| *because we mush store the mask of different pp cores: 0, 1, 2, 3, 4, 5, 6, 7, 8. |
| */ |
| static int mali_pm_domain_power_cost_result[MALI_MAX_NUMBER_OF_PHYSICAL_PP_GROUPS + 1][MALI_MAX_NUMBER_OF_DOMAINS]; |
| /* |
| * Keep track of runtime PM state, so that we know |
| * how to resume during OS resume. |
| */ |
| #ifdef CONFIG_PM_RUNTIME |
| static mali_bool mali_pm_runtime_active = MALI_FALSE; |
| #else |
| /* when kernel don't enable PM_RUNTIME, set the flag always true, |
| * for GPU will not power off by runtime */ |
| static mali_bool mali_pm_runtime_active = MALI_TRUE; |
| #endif |
| |
| static void mali_pm_state_lock(void); |
| static void mali_pm_state_unlock(void); |
| static _mali_osk_errcode_t mali_pm_create_pm_domains(void); |
| static void mali_pm_set_pmu_domain_config(void); |
| static u32 mali_pm_get_registered_cores_mask(void); |
| static void mali_pm_update_sync_internal(void); |
| static mali_bool mali_pm_common_suspend(void); |
| static void mali_pm_update_work(void *data); |
| #if defined(DEBUG) |
| const char *mali_pm_mask_to_string(u32 mask); |
| const char *mali_pm_group_stats_to_string(void); |
| #endif |
| |
| _mali_osk_errcode_t mali_pm_initialize(void) |
| { |
| _mali_osk_errcode_t err; |
| struct mali_pmu_core *pmu; |
| |
| pm_lock_state = _mali_osk_spinlock_irq_init(_MALI_OSK_LOCKFLAG_ORDERED, |
| _MALI_OSK_LOCK_ORDER_PM_STATE); |
| if (NULL == pm_lock_state) { |
| mali_pm_terminate(); |
| return _MALI_OSK_ERR_FAULT; |
| } |
| |
| pm_lock_exec = _mali_osk_mutex_init(_MALI_OSK_LOCKFLAG_ORDERED, |
| _MALI_OSK_LOCK_ORDER_PM_STATE); |
| if (NULL == pm_lock_exec) { |
| mali_pm_terminate(); |
| return _MALI_OSK_ERR_FAULT; |
| } |
| |
| pm_work = _mali_osk_wq_create_work(mali_pm_update_work, NULL); |
| if (NULL == pm_work) { |
| mali_pm_terminate(); |
| return _MALI_OSK_ERR_FAULT; |
| } |
| |
| pmu = mali_pmu_get_global_pmu_core(); |
| if (NULL != pmu) { |
| /* |
| * We have a Mali PMU, set the correct domain |
| * configuration (default or custom) |
| */ |
| |
| u32 registered_cores_mask; |
| |
| mali_pm_set_pmu_domain_config(); |
| |
| registered_cores_mask = mali_pm_get_registered_cores_mask(); |
| mali_pmu_set_registered_cores_mask(pmu, registered_cores_mask); |
| |
| MALI_DEBUG_ASSERT(0 == pd_mask_wanted); |
| } |
| |
| /* Create all power domains needed (at least one dummy domain) */ |
| err = mali_pm_create_pm_domains(); |
| if (_MALI_OSK_ERR_OK != err) { |
| mali_pm_terminate(); |
| return err; |
| } |
| |
| return _MALI_OSK_ERR_OK; |
| } |
| |
| void mali_pm_terminate(void) |
| { |
| if (NULL != pm_work) { |
| _mali_osk_wq_delete_work(pm_work); |
| pm_work = NULL; |
| } |
| |
| mali_pm_domain_terminate(); |
| |
| if (NULL != pm_lock_exec) { |
| _mali_osk_mutex_term(pm_lock_exec); |
| pm_lock_exec = NULL; |
| } |
| |
| if (NULL != pm_lock_state) { |
| _mali_osk_spinlock_irq_term(pm_lock_state); |
| pm_lock_state = NULL; |
| } |
| } |
| |
| struct mali_pm_domain *mali_pm_register_l2_cache(u32 domain_index, |
| struct mali_l2_cache_core *l2_cache) |
| { |
| struct mali_pm_domain *domain; |
| |
| domain = mali_pm_domain_get_from_mask(domain_config[domain_index]); |
| if (NULL == domain) { |
| MALI_DEBUG_ASSERT(0 == domain_config[domain_index]); |
| domain = mali_pm_domain_get_from_index( |
| MALI_DOMAIN_INDEX_DUMMY); |
| domain_config[domain_index] = MALI_PM_DOMAIN_DUMMY_MASK; |
| } else { |
| MALI_DEBUG_ASSERT(0 != domain_config[domain_index]); |
| } |
| |
| MALI_DEBUG_ASSERT(NULL != domain); |
| |
| mali_pm_domain_add_l2_cache(domain, l2_cache); |
| |
| return domain; /* return the actual domain this was registered in */ |
| } |
| |
| struct mali_pm_domain *mali_pm_register_group(u32 domain_index, |
| struct mali_group *group) |
| { |
| struct mali_pm_domain *domain; |
| |
| domain = mali_pm_domain_get_from_mask(domain_config[domain_index]); |
| if (NULL == domain) { |
| MALI_DEBUG_ASSERT(0 == domain_config[domain_index]); |
| domain = mali_pm_domain_get_from_index( |
| MALI_DOMAIN_INDEX_DUMMY); |
| domain_config[domain_index] = MALI_PM_DOMAIN_DUMMY_MASK; |
| } else { |
| MALI_DEBUG_ASSERT(0 != domain_config[domain_index]); |
| } |
| |
| MALI_DEBUG_ASSERT(NULL != domain); |
| |
| mali_pm_domain_add_group(domain, group); |
| |
| return domain; /* return the actual domain this was registered in */ |
| } |
| |
| mali_bool mali_pm_get_domain_refs(struct mali_pm_domain **domains, |
| struct mali_group **groups, |
| u32 num_domains) |
| { |
| mali_bool ret = MALI_TRUE; /* Assume all is powered on instantly */ |
| u32 i; |
| |
| mali_pm_state_lock(); |
| |
| for (i = 0; i < num_domains; i++) { |
| MALI_DEBUG_ASSERT_POINTER(domains[i]); |
| pd_mask_wanted |= mali_pm_domain_ref_get(domains[i]); |
| if (MALI_FALSE == mali_pm_domain_power_is_on(domains[i])) { |
| /* |
| * Tell caller that the corresponding group |
| * was not already powered on. |
| */ |
| ret = MALI_FALSE; |
| } else { |
| /* |
| * There is a time gap between we power on the domain and |
| * set the power state of the corresponding groups to be on. |
| */ |
| if (NULL != groups[i] && |
| MALI_FALSE == mali_group_power_is_on(groups[i])) { |
| ret = MALI_FALSE; |
| } |
| } |
| } |
| |
| MALI_DEBUG_PRINT(3, ("PM: wanted domain mask = 0x%08X (get refs)\n", pd_mask_wanted)); |
| |
| mali_pm_state_unlock(); |
| |
| return ret; |
| } |
| |
| mali_bool mali_pm_put_domain_refs(struct mali_pm_domain **domains, |
| u32 num_domains) |
| { |
| u32 mask = 0; |
| mali_bool ret; |
| u32 i; |
| |
| mali_pm_state_lock(); |
| |
| for (i = 0; i < num_domains; i++) { |
| MALI_DEBUG_ASSERT_POINTER(domains[i]); |
| mask |= mali_pm_domain_ref_put(domains[i]); |
| } |
| |
| if (0 == mask) { |
| /* return false, all domains should still stay on */ |
| ret = MALI_FALSE; |
| } else { |
| /* Assert that we are dealing with a change */ |
| MALI_DEBUG_ASSERT((pd_mask_wanted & mask) == mask); |
| |
| /* Update our desired domain mask */ |
| pd_mask_wanted &= ~mask; |
| |
| /* return true; one or more domains can now be powered down */ |
| ret = MALI_TRUE; |
| } |
| |
| MALI_DEBUG_PRINT(3, ("PM: wanted domain mask = 0x%08X (put refs)\n", pd_mask_wanted)); |
| |
| mali_pm_state_unlock(); |
| |
| return ret; |
| } |
| |
| void mali_pm_init_begin(void) |
| { |
| struct mali_pmu_core *pmu = mali_pmu_get_global_pmu_core(); |
| |
| _mali_osk_pm_dev_ref_get_sync(); |
| |
| /* Ensure all PMU domains are on */ |
| if (NULL != pmu) { |
| mali_pmu_power_up_all(pmu); |
| } |
| } |
| |
| void mali_pm_init_end(void) |
| { |
| struct mali_pmu_core *pmu = mali_pmu_get_global_pmu_core(); |
| |
| /* Ensure all PMU domains are off */ |
| if (NULL != pmu) { |
| mali_pmu_power_down_all(pmu); |
| } |
| |
| _mali_osk_pm_dev_ref_put(); |
| } |
| |
| void mali_pm_update_sync(void) |
| { |
| mali_pm_exec_lock(); |
| |
| if (MALI_TRUE == mali_pm_runtime_active) { |
| /* |
| * Only update if GPU is powered on. |
| * Deactivation of the last group will result in both a |
| * deferred runtime PM suspend operation and |
| * deferred execution of this function. |
| * mali_pm_runtime_active will be false if runtime PM |
| * executed first and thus the GPU is now fully powered off. |
| */ |
| mali_pm_update_sync_internal(); |
| } |
| |
| mali_pm_exec_unlock(); |
| } |
| |
| void mali_pm_update_async(void) |
| { |
| _mali_osk_wq_schedule_work(pm_work); |
| } |
| |
| void mali_pm_os_suspend(mali_bool os_suspend) |
| { |
| int ret; |
| |
| MALI_DEBUG_PRINT(3, ("Mali PM: OS suspend\n")); |
| |
| /* Suspend execution of all jobs, and go to inactive state */ |
| mali_executor_suspend(); |
| |
| if (os_suspend) { |
| mali_control_timer_suspend(MALI_TRUE); |
| } |
| |
| mali_pm_exec_lock(); |
| |
| ret = mali_pm_common_suspend(); |
| |
| MALI_DEBUG_ASSERT(MALI_TRUE == ret); |
| MALI_IGNORE(ret); |
| |
| mali_pm_exec_unlock(); |
| } |
| |
| void mali_pm_os_resume(void) |
| { |
| struct mali_pmu_core *pmu = mali_pmu_get_global_pmu_core(); |
| |
| MALI_DEBUG_PRINT(3, ("Mali PM: OS resume\n")); |
| |
| mali_pm_exec_lock(); |
| |
| #if defined(DEBUG) |
| mali_pm_state_lock(); |
| |
| /* Assert that things are as we left them in os_suspend(). */ |
| MALI_DEBUG_ASSERT(0 == pd_mask_wanted); |
| MALI_DEBUG_ASSERT(0 == pd_mask_current); |
| MALI_DEBUG_ASSERT(0 == pmu_mask_current); |
| |
| MALI_DEBUG_ASSERT(MALI_TRUE == mali_pm_domain_all_unused()); |
| |
| mali_pm_state_unlock(); |
| #endif |
| |
| if (MALI_TRUE == mali_pm_runtime_active) { |
| /* Runtime PM was active, so reset PMU */ |
| if (NULL != pmu) { |
| mali_pmu_reset(pmu); |
| pmu_mask_current = mali_pmu_get_mask(pmu); |
| |
| MALI_DEBUG_PRINT(3, ("Mali PM: OS resume 0x%x \n", pmu_mask_current)); |
| } |
| |
| mali_pm_update_sync_internal(); |
| } |
| |
| mali_pm_exec_unlock(); |
| |
| /* Start executing jobs again */ |
| mali_executor_resume(); |
| } |
| |
| mali_bool mali_pm_runtime_suspend(void) |
| { |
| mali_bool ret; |
| |
| MALI_DEBUG_PRINT(3, ("Mali PM: Runtime suspend\n")); |
| |
| mali_pm_exec_lock(); |
| |
| /* |
| * Put SW state directly into "off" state, and do not bother to power |
| * down each power domain, because entire GPU will be powered off |
| * when we return. |
| * For runtime PM suspend, in contrast to OS suspend, there is a race |
| * between this function and the mali_pm_update_sync_internal(), which |
| * is fine... |
| */ |
| ret = mali_pm_common_suspend(); |
| if (MALI_TRUE == ret) { |
| mali_pm_runtime_active = MALI_FALSE; |
| } else { |
| /* |
| * Process the "power up" instead, |
| * which could have been "lost" |
| */ |
| mali_pm_update_sync_internal(); |
| } |
| |
| mali_pm_exec_unlock(); |
| |
| return ret; |
| } |
| |
| void mali_pm_runtime_resume(void) |
| { |
| struct mali_pmu_core *pmu = mali_pmu_get_global_pmu_core(); |
| |
| mali_pm_exec_lock(); |
| |
| mali_pm_runtime_active = MALI_TRUE; |
| |
| #if defined(DEBUG) |
| ++num_pm_runtime_resume; |
| |
| mali_pm_state_lock(); |
| |
| /* |
| * Assert that things are as we left them in runtime_suspend(), |
| * except for pd_mask_wanted which normally will be the reason we |
| * got here (job queued => domains wanted) |
| */ |
| MALI_DEBUG_ASSERT(0 == pd_mask_current); |
| MALI_DEBUG_ASSERT(0 == pmu_mask_current); |
| |
| mali_pm_state_unlock(); |
| #endif |
| |
| if (NULL != pmu) { |
| mali_pmu_reset(pmu); |
| pmu_mask_current = mali_pmu_get_mask(pmu); |
| MALI_DEBUG_PRINT(3, ("Mali PM: Runtime resume 0x%x \n", pmu_mask_current)); |
| } |
| |
| /* |
| * Normally we are resumed because a job has just been queued. |
| * pd_mask_wanted should thus be != 0. |
| * It is however possible for others to take a Mali Runtime PM ref |
| * without having a job queued. |
| * We should however always call mali_pm_update_sync_internal(), |
| * because this will take care of any potential mismatch between |
| * pmu_mask_current and pd_mask_current. |
| */ |
| mali_pm_update_sync_internal(); |
| |
| mali_pm_exec_unlock(); |
| } |
| |
| #if MALI_STATE_TRACKING |
| u32 mali_pm_dump_state_domain(struct mali_pm_domain *domain, |
| char *buf, u32 size) |
| { |
| int n = 0; |
| |
| n += _mali_osk_snprintf(buf + n, size - n, |
| "\tPower domain: id %u\n", |
| mali_pm_domain_get_id(domain)); |
| |
| n += _mali_osk_snprintf(buf + n, size - n, |
| "\t\tMask: 0x%04x\n", |
| mali_pm_domain_get_mask(domain)); |
| |
| n += _mali_osk_snprintf(buf + n, size - n, |
| "\t\tUse count: %u\n", |
| mali_pm_domain_get_use_count(domain)); |
| |
| n += _mali_osk_snprintf(buf + n, size - n, |
| "\t\tCurrent power state: %s\n", |
| (mali_pm_domain_get_mask(domain) & pd_mask_current) ? |
| "On" : "Off"); |
| |
| n += _mali_osk_snprintf(buf + n, size - n, |
| "\t\tWanted power state: %s\n", |
| (mali_pm_domain_get_mask(domain) & pd_mask_wanted) ? |
| "On" : "Off"); |
| |
| return n; |
| } |
| #endif |
| |
| static void mali_pm_state_lock(void) |
| { |
| _mali_osk_spinlock_irq_lock(pm_lock_state); |
| } |
| |
| static void mali_pm_state_unlock(void) |
| { |
| _mali_osk_spinlock_irq_unlock(pm_lock_state); |
| } |
| |
| void mali_pm_exec_lock(void) |
| { |
| _mali_osk_mutex_wait(pm_lock_exec); |
| } |
| |
| void mali_pm_exec_unlock(void) |
| { |
| _mali_osk_mutex_signal(pm_lock_exec); |
| } |
| |
| static void mali_pm_domain_power_up(u32 power_up_mask, |
| struct mali_group *groups_up[MALI_MAX_NUMBER_OF_GROUPS], |
| u32 *num_groups_up, |
| struct mali_l2_cache_core *l2_up[MALI_MAX_NUMBER_OF_L2_CACHE_CORES], |
| u32 *num_l2_up) |
| { |
| u32 domain_bit; |
| u32 notify_mask = power_up_mask; |
| |
| MALI_DEBUG_ASSERT(0 != power_up_mask); |
| MALI_DEBUG_ASSERT_POINTER(groups_up); |
| MALI_DEBUG_ASSERT_POINTER(num_groups_up); |
| MALI_DEBUG_ASSERT(0 == *num_groups_up); |
| MALI_DEBUG_ASSERT_POINTER(l2_up); |
| MALI_DEBUG_ASSERT_POINTER(num_l2_up); |
| MALI_DEBUG_ASSERT(0 == *num_l2_up); |
| |
| MALI_DEBUG_ASSERT_LOCK_HELD(pm_lock_exec); |
| MALI_DEBUG_ASSERT_LOCK_HELD(pm_lock_state); |
| |
| MALI_DEBUG_PRINT(5, |
| ("PM update: Powering up domains: . [%s]\n", |
| mali_pm_mask_to_string(power_up_mask))); |
| |
| pd_mask_current |= power_up_mask; |
| |
| domain_bit = _mali_osk_fls(notify_mask); |
| while (0 != domain_bit) { |
| u32 domain_id = domain_bit - 1; |
| struct mali_pm_domain *domain = |
| mali_pm_domain_get_from_index( |
| domain_id); |
| struct mali_l2_cache_core *l2_cache; |
| struct mali_l2_cache_core *l2_cache_tmp; |
| struct mali_group *group; |
| struct mali_group *group_tmp; |
| |
| /* Mark domain as powered up */ |
| mali_pm_domain_set_power_on(domain, MALI_TRUE); |
| |
| /* |
| * Make a note of the L2 and/or group(s) to notify |
| * (need to release the PM state lock before doing so) |
| */ |
| |
| _MALI_OSK_LIST_FOREACHENTRY(l2_cache, |
| l2_cache_tmp, |
| mali_pm_domain_get_l2_cache_list( |
| domain), |
| struct mali_l2_cache_core, |
| pm_domain_list) { |
| MALI_DEBUG_ASSERT(*num_l2_up < |
| MALI_MAX_NUMBER_OF_L2_CACHE_CORES); |
| l2_up[*num_l2_up] = l2_cache; |
| (*num_l2_up)++; |
| } |
| |
| _MALI_OSK_LIST_FOREACHENTRY(group, |
| group_tmp, |
| mali_pm_domain_get_group_list(domain), |
| struct mali_group, |
| pm_domain_list) { |
| MALI_DEBUG_ASSERT(*num_groups_up < |
| MALI_MAX_NUMBER_OF_GROUPS); |
| groups_up[*num_groups_up] = group; |
| |
| (*num_groups_up)++; |
| } |
| |
| /* Remove current bit and find next */ |
| notify_mask &= ~(1 << (domain_id)); |
| domain_bit = _mali_osk_fls(notify_mask); |
| } |
| } |
| static void mali_pm_domain_power_down(u32 power_down_mask, |
| struct mali_group *groups_down[MALI_MAX_NUMBER_OF_GROUPS], |
| u32 *num_groups_down, |
| struct mali_l2_cache_core *l2_down[MALI_MAX_NUMBER_OF_L2_CACHE_CORES], |
| u32 *num_l2_down) |
| { |
| u32 domain_bit; |
| u32 notify_mask = power_down_mask; |
| |
| MALI_DEBUG_ASSERT(0 != power_down_mask); |
| MALI_DEBUG_ASSERT_POINTER(groups_down); |
| MALI_DEBUG_ASSERT_POINTER(num_groups_down); |
| MALI_DEBUG_ASSERT(0 == *num_groups_down); |
| MALI_DEBUG_ASSERT_POINTER(l2_down); |
| MALI_DEBUG_ASSERT_POINTER(num_l2_down); |
| MALI_DEBUG_ASSERT(0 == *num_l2_down); |
| |
| MALI_DEBUG_ASSERT_LOCK_HELD(pm_lock_exec); |
| MALI_DEBUG_ASSERT_LOCK_HELD(pm_lock_state); |
| |
| MALI_DEBUG_PRINT(5, |
| ("PM update: Powering down domains: [%s]\n", |
| mali_pm_mask_to_string(power_down_mask))); |
| |
| pd_mask_current &= ~power_down_mask; |
| |
| domain_bit = _mali_osk_fls(notify_mask); |
| while (0 != domain_bit) { |
| u32 domain_id = domain_bit - 1; |
| struct mali_pm_domain *domain = |
| mali_pm_domain_get_from_index(domain_id); |
| struct mali_l2_cache_core *l2_cache; |
| struct mali_l2_cache_core *l2_cache_tmp; |
| struct mali_group *group; |
| struct mali_group *group_tmp; |
| |
| /* Mark domain as powered down */ |
| mali_pm_domain_set_power_on(domain, MALI_FALSE); |
| |
| /* |
| * Make a note of the L2s and/or groups to notify |
| * (need to release the PM state lock before doing so) |
| */ |
| |
| _MALI_OSK_LIST_FOREACHENTRY(l2_cache, |
| l2_cache_tmp, |
| mali_pm_domain_get_l2_cache_list(domain), |
| struct mali_l2_cache_core, |
| pm_domain_list) { |
| MALI_DEBUG_ASSERT(*num_l2_down < |
| MALI_MAX_NUMBER_OF_L2_CACHE_CORES); |
| l2_down[*num_l2_down] = l2_cache; |
| (*num_l2_down)++; |
| } |
| |
| _MALI_OSK_LIST_FOREACHENTRY(group, |
| group_tmp, |
| mali_pm_domain_get_group_list(domain), |
| struct mali_group, |
| pm_domain_list) { |
| MALI_DEBUG_ASSERT(*num_groups_down < |
| MALI_MAX_NUMBER_OF_GROUPS); |
| groups_down[*num_groups_down] = group; |
| (*num_groups_down)++; |
| } |
| |
| /* Remove current bit and find next */ |
| notify_mask &= ~(1 << (domain_id)); |
| domain_bit = _mali_osk_fls(notify_mask); |
| } |
| } |
| |
| /* |
| * Execute pending power domain changes |
| * pm_lock_exec lock must be taken by caller. |
| */ |
| static void mali_pm_update_sync_internal(void) |
| { |
| /* |
| * This should only be called in non-atomic context |
| * (normally as deferred work) |
| * |
| * Look at the pending power domain changes, and execute these. |
| * Make sure group and schedulers are notified about changes. |
| */ |
| |
| struct mali_pmu_core *pmu = mali_pmu_get_global_pmu_core(); |
| |
| u32 power_down_mask; |
| u32 power_up_mask; |
| |
| MALI_DEBUG_ASSERT_LOCK_HELD(pm_lock_exec); |
| |
| #if defined(DEBUG) |
| ++num_pm_updates; |
| #endif |
| |
| /* Hold PM state lock while we look at (and obey) the wanted state */ |
| mali_pm_state_lock(); |
| |
| MALI_DEBUG_PRINT(5, ("PM update pre: Wanted domain mask: .. [%s]\n", |
| mali_pm_mask_to_string(pd_mask_wanted))); |
| MALI_DEBUG_PRINT(5, ("PM update pre: Current domain mask: . [%s]\n", |
| mali_pm_mask_to_string(pd_mask_current))); |
| MALI_DEBUG_PRINT(5, ("PM update pre: Current PMU mask: .... [%s]\n", |
| mali_pm_mask_to_string(pmu_mask_current))); |
| MALI_DEBUG_PRINT(5, ("PM update pre: Group power stats: ... <%s>\n", |
| mali_pm_group_stats_to_string())); |
| |
| /* Figure out which cores we need to power on */ |
| power_up_mask = pd_mask_wanted & |
| (pd_mask_wanted ^ pd_mask_current); |
| |
| if (0 != power_up_mask) { |
| u32 power_up_mask_pmu; |
| struct mali_group *groups_up[MALI_MAX_NUMBER_OF_GROUPS]; |
| u32 num_groups_up = 0; |
| struct mali_l2_cache_core * |
| l2_up[MALI_MAX_NUMBER_OF_L2_CACHE_CORES]; |
| u32 num_l2_up = 0; |
| u32 i; |
| |
| #if defined(DEBUG) |
| ++num_pm_updates_up; |
| #endif |
| |
| /* |
| * Make sure dummy/global domain is always included when |
| * powering up, since this is controlled by runtime PM, |
| * and device power is on at this stage. |
| */ |
| power_up_mask |= MALI_PM_DOMAIN_DUMMY_MASK; |
| |
| /* Power up only real PMU domains */ |
| power_up_mask_pmu = power_up_mask & ~MALI_PM_DOMAIN_DUMMY_MASK; |
| |
| /* But not those that happen to be powered on already */ |
| power_up_mask_pmu &= (power_up_mask ^ pmu_mask_current) & |
| power_up_mask; |
| |
| if (0 != power_up_mask_pmu) { |
| MALI_DEBUG_ASSERT(NULL != pmu); |
| pmu_mask_current |= power_up_mask_pmu; |
| mali_pmu_power_up(pmu, power_up_mask_pmu); |
| } |
| |
| /* |
| * Put the domains themselves in power up state. |
| * We get the groups and L2s to notify in return. |
| */ |
| mali_pm_domain_power_up(power_up_mask, |
| groups_up, &num_groups_up, |
| l2_up, &num_l2_up); |
| |
| /* Need to unlock PM state lock before notifying L2 + groups */ |
| mali_pm_state_unlock(); |
| |
| /* Notify each L2 cache that we have be powered up */ |
| for (i = 0; i < num_l2_up; i++) { |
| mali_l2_cache_power_up(l2_up[i]); |
| } |
| |
| /* |
| * Tell execution module about all the groups we have |
| * powered up. Groups will be notified as a result of this. |
| */ |
| mali_executor_group_power_up(groups_up, num_groups_up); |
| |
| /* Lock state again before checking for power down */ |
| mali_pm_state_lock(); |
| } |
| |
| /* Figure out which cores we need to power off */ |
| power_down_mask = pd_mask_current & |
| (pd_mask_wanted ^ pd_mask_current); |
| |
| /* |
| * Never power down the dummy/global domain here. This is to be done |
| * from a suspend request (since this domain is only physicall powered |
| * down at that point) |
| */ |
| power_down_mask &= ~MALI_PM_DOMAIN_DUMMY_MASK; |
| |
| if (0 != power_down_mask) { |
| u32 power_down_mask_pmu; |
| struct mali_group *groups_down[MALI_MAX_NUMBER_OF_GROUPS]; |
| u32 num_groups_down = 0; |
| struct mali_l2_cache_core * |
| l2_down[MALI_MAX_NUMBER_OF_L2_CACHE_CORES]; |
| u32 num_l2_down = 0; |
| u32 i; |
| |
| #if defined(DEBUG) |
| ++num_pm_updates_down; |
| #endif |
| |
| /* |
| * Put the domains themselves in power down state. |
| * We get the groups and L2s to notify in return. |
| */ |
| mali_pm_domain_power_down(power_down_mask, |
| groups_down, &num_groups_down, |
| l2_down, &num_l2_down); |
| |
| /* Need to unlock PM state lock before notifying L2 + groups */ |
| mali_pm_state_unlock(); |
| |
| /* |
| * Tell execution module about all the groups we will be |
| * powering down. Groups will be notified as a result of this. |
| */ |
| if (0 < num_groups_down) { |
| mali_executor_group_power_down(groups_down, num_groups_down); |
| } |
| |
| /* Notify each L2 cache that we will be powering down */ |
| for (i = 0; i < num_l2_down; i++) { |
| mali_l2_cache_power_down(l2_down[i]); |
| } |
| |
| /* |
| * Power down only PMU domains which should not stay on |
| * Some domains might for instance currently be incorrectly |
| * powered up if default domain power state is all on. |
| */ |
| power_down_mask_pmu = pmu_mask_current & (~pd_mask_current); |
| |
| if (0 != power_down_mask_pmu) { |
| MALI_DEBUG_ASSERT(NULL != pmu); |
| pmu_mask_current &= ~power_down_mask_pmu; |
| mali_pmu_power_down(pmu, power_down_mask_pmu); |
| |
| } |
| } else { |
| /* |
| * Power down only PMU domains which should not stay on |
| * Some domains might for instance currently be incorrectly |
| * powered up if default domain power state is all on. |
| */ |
| u32 power_down_mask_pmu; |
| |
| /* No need for state lock since we'll only update PMU */ |
| mali_pm_state_unlock(); |
| |
| power_down_mask_pmu = pmu_mask_current & (~pd_mask_current); |
| |
| if (0 != power_down_mask_pmu) { |
| MALI_DEBUG_ASSERT(NULL != pmu); |
| pmu_mask_current &= ~power_down_mask_pmu; |
| mali_pmu_power_down(pmu, power_down_mask_pmu); |
| } |
| } |
| |
| MALI_DEBUG_PRINT(5, ("PM update post: Current domain mask: . [%s]\n", |
| mali_pm_mask_to_string(pd_mask_current))); |
| MALI_DEBUG_PRINT(5, ("PM update post: Current PMU mask: .... [%s]\n", |
| mali_pm_mask_to_string(pmu_mask_current))); |
| MALI_DEBUG_PRINT(5, ("PM update post: Group power stats: ... <%s>\n", |
| mali_pm_group_stats_to_string())); |
| } |
| |
| static mali_bool mali_pm_common_suspend(void) |
| { |
| mali_pm_state_lock(); |
| |
| if (0 != pd_mask_wanted) { |
| MALI_DEBUG_PRINT(5, ("PM: Aborting suspend operation\n\n\n")); |
| mali_pm_state_unlock(); |
| return MALI_FALSE; |
| } |
| |
| MALI_DEBUG_PRINT(5, ("PM suspend pre: Wanted domain mask: .. [%s]\n", |
| mali_pm_mask_to_string(pd_mask_wanted))); |
| MALI_DEBUG_PRINT(5, ("PM suspend pre: Current domain mask: . [%s]\n", |
| mali_pm_mask_to_string(pd_mask_current))); |
| MALI_DEBUG_PRINT(5, ("PM suspend pre: Current PMU mask: .... [%s]\n", |
| mali_pm_mask_to_string(pmu_mask_current))); |
| MALI_DEBUG_PRINT(5, ("PM suspend pre: Group power stats: ... <%s>\n", |
| mali_pm_group_stats_to_string())); |
| |
| if (0 != pd_mask_current) { |
| /* |
| * We have still some domains powered on. |
| * It is for instance very normal that at least the |
| * dummy/global domain is marked as powered on at this point. |
| * (because it is physically powered on until this function |
| * returns) |
| */ |
| |
| struct mali_group *groups_down[MALI_MAX_NUMBER_OF_GROUPS]; |
| u32 num_groups_down = 0; |
| struct mali_l2_cache_core * |
| l2_down[MALI_MAX_NUMBER_OF_L2_CACHE_CORES]; |
| u32 num_l2_down = 0; |
| u32 i; |
| |
| /* |
| * Put the domains themselves in power down state. |
| * We get the groups and L2s to notify in return. |
| */ |
| mali_pm_domain_power_down(pd_mask_current, |
| groups_down, |
| &num_groups_down, |
| l2_down, |
| &num_l2_down); |
| |
| MALI_DEBUG_ASSERT(0 == pd_mask_current); |
| MALI_DEBUG_ASSERT(MALI_TRUE == mali_pm_domain_all_unused()); |
| |
| /* Need to unlock PM state lock before notifying L2 + groups */ |
| mali_pm_state_unlock(); |
| |
| /* |
| * Tell execution module about all the groups we will be |
| * powering down. Groups will be notified as a result of this. |
| */ |
| if (0 < num_groups_down) { |
| mali_executor_group_power_down(groups_down, num_groups_down); |
| } |
| |
| /* Notify each L2 cache that we will be powering down */ |
| for (i = 0; i < num_l2_down; i++) { |
| mali_l2_cache_power_down(l2_down[i]); |
| } |
| |
| pmu_mask_current = 0; |
| } else { |
| MALI_DEBUG_ASSERT(0 == pmu_mask_current); |
| |
| MALI_DEBUG_ASSERT(MALI_TRUE == mali_pm_domain_all_unused()); |
| |
| mali_pm_state_unlock(); |
| } |
| |
| MALI_DEBUG_PRINT(5, ("PM suspend post: Current domain mask: [%s]\n", |
| mali_pm_mask_to_string(pd_mask_current))); |
| MALI_DEBUG_PRINT(5, ("PM suspend post: Current PMU mask: ... [%s]\n", |
| mali_pm_mask_to_string(pmu_mask_current))); |
| MALI_DEBUG_PRINT(5, ("PM suspend post: Group power stats: .. <%s>\n", |
| mali_pm_group_stats_to_string())); |
| |
| return MALI_TRUE; |
| } |
| |
| static void mali_pm_update_work(void *data) |
| { |
| MALI_IGNORE(data); |
| mali_pm_update_sync(); |
| } |
| |
| static _mali_osk_errcode_t mali_pm_create_pm_domains(void) |
| { |
| int i; |
| |
| /* Create all domains (including dummy domain) */ |
| for (i = 0; i < MALI_MAX_NUMBER_OF_DOMAINS; i++) { |
| if (0x0 == domain_config[i]) continue; |
| |
| if (NULL == mali_pm_domain_create(domain_config[i])) { |
| return _MALI_OSK_ERR_NOMEM; |
| } |
| } |
| |
| return _MALI_OSK_ERR_OK; |
| } |
| |
| static void mali_pm_set_default_pm_domain_config(void) |
| { |
| MALI_DEBUG_ASSERT(0 != _mali_osk_resource_base_address()); |
| |
| /* GP core */ |
| if (_MALI_OSK_ERR_OK == _mali_osk_resource_find( |
| MALI_OFFSET_GP, NULL)) { |
| domain_config[MALI_DOMAIN_INDEX_GP] = 0x01; |
| } |
| |
| /* PP0 - PP3 core */ |
| if (_MALI_OSK_ERR_OK == _mali_osk_resource_find( |
| MALI_OFFSET_PP0, NULL)) { |
| if (mali_is_mali400()) { |
| domain_config[MALI_DOMAIN_INDEX_PP0] = 0x01 << 2; |
| } else if (mali_is_mali450()) { |
| domain_config[MALI_DOMAIN_INDEX_PP0] = 0x01 << 1; |
| } else if (mali_is_mali470()) { |
| domain_config[MALI_DOMAIN_INDEX_PP0] = 0x01 << 0; |
| } |
| } |
| |
| if (_MALI_OSK_ERR_OK == _mali_osk_resource_find( |
| MALI_OFFSET_PP1, NULL)) { |
| if (mali_is_mali400()) { |
| domain_config[MALI_DOMAIN_INDEX_PP1] = 0x01 << 3; |
| } else if (mali_is_mali450()) { |
| domain_config[MALI_DOMAIN_INDEX_PP1] = 0x01 << 2; |
| } else if (mali_is_mali470()) { |
| domain_config[MALI_DOMAIN_INDEX_PP1] = 0x01 << 1; |
| } |
| } |
| |
| if (_MALI_OSK_ERR_OK == _mali_osk_resource_find( |
| MALI_OFFSET_PP2, NULL)) { |
| if (mali_is_mali400()) { |
| domain_config[MALI_DOMAIN_INDEX_PP2] = 0x01 << 4; |
| } else if (mali_is_mali450()) { |
| domain_config[MALI_DOMAIN_INDEX_PP2] = 0x01 << 2; |
| } else if (mali_is_mali470()) { |
| domain_config[MALI_DOMAIN_INDEX_PP2] = 0x01 << 1; |
| } |
| } |
| |
| if (_MALI_OSK_ERR_OK == _mali_osk_resource_find( |
| MALI_OFFSET_PP3, NULL)) { |
| if (mali_is_mali400()) { |
| domain_config[MALI_DOMAIN_INDEX_PP3] = 0x01 << 5; |
| } else if (mali_is_mali450()) { |
| domain_config[MALI_DOMAIN_INDEX_PP3] = 0x01 << 2; |
| } else if (mali_is_mali470()) { |
| domain_config[MALI_DOMAIN_INDEX_PP3] = 0x01 << 1; |
| } |
| } |
| |
| /* PP4 - PP7 */ |
| if (_MALI_OSK_ERR_OK == _mali_osk_resource_find( |
| MALI_OFFSET_PP4, NULL)) { |
| domain_config[MALI_DOMAIN_INDEX_PP4] = 0x01 << 3; |
| } |
| |
| if (_MALI_OSK_ERR_OK == _mali_osk_resource_find( |
| MALI_OFFSET_PP5, NULL)) { |
| domain_config[MALI_DOMAIN_INDEX_PP5] = 0x01 << 3; |
| } |
| |
| if (_MALI_OSK_ERR_OK == _mali_osk_resource_find( |
| MALI_OFFSET_PP6, NULL)) { |
| domain_config[MALI_DOMAIN_INDEX_PP6] = 0x01 << 3; |
| } |
| |
| if (_MALI_OSK_ERR_OK == _mali_osk_resource_find( |
| MALI_OFFSET_PP7, NULL)) { |
| domain_config[MALI_DOMAIN_INDEX_PP7] = 0x01 << 3; |
| } |
| |
| /* L2gp/L2PP0/L2PP4 */ |
| if (mali_is_mali400()) { |
| if (_MALI_OSK_ERR_OK == _mali_osk_resource_find( |
| MALI400_OFFSET_L2_CACHE0, NULL)) { |
| domain_config[MALI_DOMAIN_INDEX_L20] = 0x01 << 1; |
| } |
| } else if (mali_is_mali450()) { |
| if (_MALI_OSK_ERR_OK == _mali_osk_resource_find( |
| MALI450_OFFSET_L2_CACHE0, NULL)) { |
| domain_config[MALI_DOMAIN_INDEX_L20] = 0x01 << 0; |
| } |
| |
| if (_MALI_OSK_ERR_OK == _mali_osk_resource_find( |
| MALI450_OFFSET_L2_CACHE1, NULL)) { |
| domain_config[MALI_DOMAIN_INDEX_L21] = 0x01 << 1; |
| } |
| |
| if (_MALI_OSK_ERR_OK == _mali_osk_resource_find( |
| MALI450_OFFSET_L2_CACHE2, NULL)) { |
| domain_config[MALI_DOMAIN_INDEX_L22] = 0x01 << 3; |
| } |
| } else if (mali_is_mali470()) { |
| if (_MALI_OSK_ERR_OK == _mali_osk_resource_find( |
| MALI470_OFFSET_L2_CACHE1, NULL)) { |
| domain_config[MALI_DOMAIN_INDEX_L21] = 0x01 << 0; |
| } |
| } |
| } |
| |
| static u32 mali_pm_get_registered_cores_mask(void) |
| { |
| int i = 0; |
| u32 mask = 0; |
| |
| for (i = 0; i < MALI_DOMAIN_INDEX_DUMMY; i++) { |
| mask |= domain_config[i]; |
| } |
| |
| return mask; |
| } |
| |
| static void mali_pm_set_pmu_domain_config(void) |
| { |
| int i = 0; |
| |
| _mali_osk_device_data_pmu_config_get(domain_config, MALI_MAX_NUMBER_OF_DOMAINS - 1); |
| |
| for (i = 0; i < MALI_MAX_NUMBER_OF_DOMAINS - 1; i++) { |
| if (0 != domain_config[i]) { |
| MALI_DEBUG_PRINT(2, ("Using customer pmu config:\n")); |
| break; |
| } |
| } |
| |
| if (MALI_MAX_NUMBER_OF_DOMAINS - 1 == i) { |
| MALI_DEBUG_PRINT(2, ("Using hw detect pmu config:\n")); |
| mali_pm_set_default_pm_domain_config(); |
| } |
| |
| for (i = 0; i < MALI_MAX_NUMBER_OF_DOMAINS - 1; i++) { |
| if (domain_config[i]) { |
| MALI_DEBUG_PRINT(2, ("domain_config[%d] = 0x%x \n", i, domain_config[i])); |
| } |
| } |
| /* Can't override dummy domain mask */ |
| domain_config[MALI_DOMAIN_INDEX_DUMMY] = |
| 1 << MALI_DOMAIN_INDEX_DUMMY; |
| } |
| |
| #if defined(DEBUG) |
| const char *mali_pm_mask_to_string(u32 mask) |
| { |
| static char bit_str[MALI_MAX_NUMBER_OF_DOMAINS + 1]; |
| int bit; |
| int str_pos = 0; |
| |
| /* Must be protected by lock since we use shared string buffer */ |
| if (NULL != pm_lock_exec) { |
| MALI_DEBUG_ASSERT_LOCK_HELD(pm_lock_exec); |
| } |
| |
| for (bit = MALI_MAX_NUMBER_OF_DOMAINS - 1; bit >= 0; bit--) { |
| if (mask & (1 << bit)) { |
| bit_str[str_pos] = 'X'; |
| } else { |
| bit_str[str_pos] = '-'; |
| } |
| str_pos++; |
| } |
| |
| bit_str[MALI_MAX_NUMBER_OF_DOMAINS] = '\0'; |
| |
| return bit_str; |
| } |
| |
| const char *mali_pm_group_stats_to_string(void) |
| { |
| static char bit_str[MALI_MAX_NUMBER_OF_GROUPS + 1]; |
| u32 num_groups = mali_group_get_glob_num_groups(); |
| u32 i; |
| |
| /* Must be protected by lock since we use shared string buffer */ |
| if (NULL != pm_lock_exec) { |
| MALI_DEBUG_ASSERT_LOCK_HELD(pm_lock_exec); |
| } |
| |
| for (i = 0; i < num_groups && i < MALI_MAX_NUMBER_OF_GROUPS; i++) { |
| struct mali_group *group; |
| |
| group = mali_group_get_glob_group(i); |
| |
| if (MALI_TRUE == mali_group_power_is_on(group)) { |
| bit_str[i] = 'X'; |
| } else { |
| bit_str[i] = '-'; |
| } |
| } |
| |
| bit_str[i] = '\0'; |
| |
| return bit_str; |
| } |
| #endif |
| |
| /* |
| * num_pp is the number of PP cores which will be powered on given this mask |
| * cost is the total power cost of cores which will be powered on given this mask |
| */ |
| static void mali_pm_stat_from_mask(u32 mask, u32 *num_pp, u32 *cost) |
| { |
| u32 i; |
| |
| /* loop through all cores */ |
| for (i = 0; i < MALI_MAX_NUMBER_OF_DOMAINS; i++) { |
| if (!(domain_config[i] & mask)) { |
| continue; |
| } |
| |
| switch (i) { |
| case MALI_DOMAIN_INDEX_GP: |
| *cost += MALI_GP_COST; |
| |
| break; |
| case MALI_DOMAIN_INDEX_PP0: /* Fall through */ |
| case MALI_DOMAIN_INDEX_PP1: /* Fall through */ |
| case MALI_DOMAIN_INDEX_PP2: /* Fall through */ |
| case MALI_DOMAIN_INDEX_PP3: |
| if (mali_is_mali400()) { |
| if ((domain_config[MALI_DOMAIN_INDEX_L20] & mask) |
| || (domain_config[MALI_DOMAIN_INDEX_DUMMY] |
| == domain_config[MALI_DOMAIN_INDEX_L20])) { |
| *num_pp += 1; |
| } |
| } else { |
| if ((domain_config[MALI_DOMAIN_INDEX_L21] & mask) |
| || (domain_config[MALI_DOMAIN_INDEX_DUMMY] |
| == domain_config[MALI_DOMAIN_INDEX_L21])) { |
| *num_pp += 1; |
| } |
| } |
| |
| *cost += MALI_PP_COST; |
| break; |
| case MALI_DOMAIN_INDEX_PP4: /* Fall through */ |
| case MALI_DOMAIN_INDEX_PP5: /* Fall through */ |
| case MALI_DOMAIN_INDEX_PP6: /* Fall through */ |
| case MALI_DOMAIN_INDEX_PP7: |
| MALI_DEBUG_ASSERT(mali_is_mali450()); |
| |
| if ((domain_config[MALI_DOMAIN_INDEX_L22] & mask) |
| || (domain_config[MALI_DOMAIN_INDEX_DUMMY] |
| == domain_config[MALI_DOMAIN_INDEX_L22])) { |
| *num_pp += 1; |
| } |
| |
| *cost += MALI_PP_COST; |
| break; |
| case MALI_DOMAIN_INDEX_L20: /* Fall through */ |
| case MALI_DOMAIN_INDEX_L21: /* Fall through */ |
| case MALI_DOMAIN_INDEX_L22: |
| *cost += MALI_L2_COST; |
| |
| break; |
| } |
| } |
| } |
| |
| void mali_pm_power_cost_setup(void) |
| { |
| /* |
| * Two parallel arrays which store the best domain mask and its cost |
| * The index is the number of PP cores, E.g. Index 0 is for 1 PP option, |
| * might have mask 0x2 and with cost of 1, lower cost is better |
| */ |
| u32 best_mask[MALI_MAX_NUMBER_OF_PHYSICAL_PP_GROUPS] = { 0 }; |
| u32 best_cost[MALI_MAX_NUMBER_OF_PHYSICAL_PP_GROUPS] = { 0 }; |
| /* Array cores_in_domain is used to store the total pp cores in each pm domain. */ |
| u32 cores_in_domain[MALI_MAX_NUMBER_OF_DOMAINS] = { 0 }; |
| /* Domain_count is used to represent the max domain we have.*/ |
| u32 max_domain_mask = 0; |
| u32 max_domain_id = 0; |
| u32 always_on_pp_cores = 0; |
| |
| u32 num_pp, cost, mask; |
| u32 i, j , k; |
| |
| /* Initialize statistics */ |
| for (i = 0; i < MALI_MAX_NUMBER_OF_PHYSICAL_PP_GROUPS; i++) { |
| best_mask[i] = 0; |
| best_cost[i] = 0xFFFFFFFF; /* lower cost is better */ |
| } |
| |
| for (i = 0; i < MALI_MAX_NUMBER_OF_PHYSICAL_PP_GROUPS + 1; i++) { |
| for (j = 0; j < MALI_MAX_NUMBER_OF_DOMAINS; j++) { |
| mali_pm_domain_power_cost_result[i][j] = 0; |
| } |
| } |
| |
| /* Caculate number of pp cores of a given domain config. */ |
| for (i = MALI_DOMAIN_INDEX_PP0; i <= MALI_DOMAIN_INDEX_PP7; i++) { |
| if (0 < domain_config[i]) { |
| /* Get the max domain mask value used to caculate power cost |
| * and we don't count in always on pp cores. */ |
| if (MALI_PM_DOMAIN_DUMMY_MASK != domain_config[i] |
| && max_domain_mask < domain_config[i]) { |
| max_domain_mask = domain_config[i]; |
| } |
| |
| if (MALI_PM_DOMAIN_DUMMY_MASK == domain_config[i]) { |
| always_on_pp_cores++; |
| } |
| } |
| } |
| max_domain_id = _mali_osk_fls(max_domain_mask); |
| |
| /* |
| * Try all combinations of power domains and check how many PP cores |
| * they have and their power cost. |
| */ |
| for (mask = 0; mask < (1 << max_domain_id); mask++) { |
| num_pp = 0; |
| cost = 0; |
| |
| mali_pm_stat_from_mask(mask, &num_pp, &cost); |
| |
| /* This mask is usable for all MP1 up to num_pp PP cores, check statistics for all */ |
| for (i = 0; i < num_pp; i++) { |
| if (best_cost[i] >= cost) { |
| best_cost[i] = cost; |
| best_mask[i] = mask; |
| } |
| } |
| } |
| |
| /* |
| * If we want to enable x pp cores, if x is less than number of always_on pp cores, |
| * all of pp cores we will enable must be always_on pp cores. |
| */ |
| for (i = 0; i < mali_executor_get_num_cores_total(); i++) { |
| if (i < always_on_pp_cores) { |
| mali_pm_domain_power_cost_result[i + 1][MALI_MAX_NUMBER_OF_DOMAINS - 1] |
| = i + 1; |
| } else { |
| mali_pm_domain_power_cost_result[i + 1][MALI_MAX_NUMBER_OF_DOMAINS - 1] |
| = always_on_pp_cores; |
| } |
| } |
| |
| /* In this loop, variable i represent for the number of non-always on pp cores we want to enabled. */ |
| for (i = 0; i < (mali_executor_get_num_cores_total() - always_on_pp_cores); i++) { |
| if (best_mask[i] == 0) { |
| /* This MP variant is not available */ |
| continue; |
| } |
| |
| for (j = 0; j < MALI_MAX_NUMBER_OF_DOMAINS; j++) { |
| cores_in_domain[j] = 0; |
| } |
| |
| for (j = MALI_DOMAIN_INDEX_PP0; j <= MALI_DOMAIN_INDEX_PP7; j++) { |
| if (0 < domain_config[j] |
| && (MALI_PM_DOMAIN_DUMMY_MASK != domain_config[i])) { |
| cores_in_domain[_mali_osk_fls(domain_config[j]) - 1]++; |
| } |
| } |
| |
| /* In this loop, j represent for the number we have already enabled.*/ |
| for (j = 0; j <= i;) { |
| /* j used to visit all of domain to get the number of pp cores remained in it. */ |
| for (k = 0; k < max_domain_id; k++) { |
| /* If domain k in best_mask[i] is enabled and this domain has extra pp cores, |
| * we know we must pick at least one pp core from this domain. |
| * And then we move to next enabled pm domain. */ |
| if ((best_mask[i] & (0x1 << k)) && (0 < cores_in_domain[k])) { |
| cores_in_domain[k]--; |
| mali_pm_domain_power_cost_result[always_on_pp_cores + i + 1][k]++; |
| j++; |
| if (j > i) { |
| break; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /* |
| * When we are doing core scaling, |
| * this function is called to return the best mask to |
| * achieve the best pp group power cost. |
| */ |
| void mali_pm_get_best_power_cost_mask(int num_requested, int *dst) |
| { |
| MALI_DEBUG_ASSERT((mali_executor_get_num_cores_total() >= num_requested) && (0 <= num_requested)); |
| |
| _mali_osk_memcpy(dst, mali_pm_domain_power_cost_result[num_requested], MALI_MAX_NUMBER_OF_DOMAINS * sizeof(int)); |
| } |
| |
| u32 mali_pm_get_current_mask(void) |
| { |
| return pd_mask_current; |
| } |
| |
| u32 mali_pm_get_wanted_mask(void) |
| { |
| return pd_mask_wanted; |
| } |