| /* |
| * |
| * (C) COPYRIGHT 2013-2015 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. |
| * |
| */ |
| |
| |
| |
| /* |
| * Base kernel core availability APIs |
| */ |
| |
| #include <mali_kbase.h> |
| #include <mali_kbase_pm.h> |
| #include <backend/gpu/mali_kbase_pm_internal.h> |
| |
| static const struct kbase_pm_ca_policy *const policy_list[] = { |
| &kbase_pm_ca_fixed_policy_ops, |
| #if !MALI_CUSTOMER_RELEASE |
| &kbase_pm_ca_demand_policy_ops, |
| &kbase_pm_ca_random_policy_ops, |
| #endif |
| }; |
| |
| /** |
| * POLICY_COUNT - The number of policies available in the system. |
| * |
| * This is derived from the number of functions listed in policy_list. |
| */ |
| #define POLICY_COUNT (sizeof(policy_list)/sizeof(*policy_list)) |
| |
| int kbase_pm_ca_init(struct kbase_device *kbdev) |
| { |
| KBASE_DEBUG_ASSERT(kbdev != NULL); |
| |
| kbdev->pm.backend.ca_current_policy = policy_list[0]; |
| |
| kbdev->pm.backend.ca_current_policy->init(kbdev); |
| |
| return 0; |
| } |
| |
| void kbase_pm_ca_term(struct kbase_device *kbdev) |
| { |
| kbdev->pm.backend.ca_current_policy->term(kbdev); |
| } |
| |
| int kbase_pm_ca_list_policies(const struct kbase_pm_ca_policy * const **list) |
| { |
| if (!list) |
| return POLICY_COUNT; |
| |
| *list = policy_list; |
| |
| return POLICY_COUNT; |
| } |
| |
| KBASE_EXPORT_TEST_API(kbase_pm_ca_list_policies); |
| |
| const struct kbase_pm_ca_policy |
| *kbase_pm_ca_get_policy(struct kbase_device *kbdev) |
| { |
| KBASE_DEBUG_ASSERT(kbdev != NULL); |
| |
| return kbdev->pm.backend.ca_current_policy; |
| } |
| |
| KBASE_EXPORT_TEST_API(kbase_pm_ca_get_policy); |
| |
| void kbase_pm_ca_set_policy(struct kbase_device *kbdev, |
| const struct kbase_pm_ca_policy *new_policy) |
| { |
| const struct kbase_pm_ca_policy *old_policy; |
| unsigned long flags; |
| |
| KBASE_DEBUG_ASSERT(kbdev != NULL); |
| KBASE_DEBUG_ASSERT(new_policy != NULL); |
| |
| KBASE_TRACE_ADD(kbdev, PM_CA_SET_POLICY, NULL, NULL, 0u, |
| new_policy->id); |
| |
| /* During a policy change we pretend the GPU is active */ |
| /* A suspend won't happen here, because we're in a syscall from a |
| * userspace thread */ |
| kbase_pm_context_active(kbdev); |
| |
| mutex_lock(&kbdev->pm.lock); |
| |
| /* Remove the policy to prevent IRQ handlers from working on it */ |
| spin_lock_irqsave(&kbdev->pm.power_change_lock, flags); |
| old_policy = kbdev->pm.backend.ca_current_policy; |
| kbdev->pm.backend.ca_current_policy = NULL; |
| spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags); |
| |
| if (old_policy->term) |
| old_policy->term(kbdev); |
| |
| if (new_policy->init) |
| new_policy->init(kbdev); |
| |
| spin_lock_irqsave(&kbdev->pm.power_change_lock, flags); |
| kbdev->pm.backend.ca_current_policy = new_policy; |
| |
| /* If any core power state changes were previously attempted, but |
| * couldn't be made because the policy was changing (current_policy was |
| * NULL), then re-try them here. */ |
| kbase_pm_update_cores_state_nolock(kbdev); |
| |
| kbdev->pm.backend.ca_current_policy->update_core_status(kbdev, |
| kbdev->shader_ready_bitmap, |
| kbdev->shader_transitioning_bitmap); |
| |
| spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags); |
| |
| mutex_unlock(&kbdev->pm.lock); |
| |
| /* Now the policy change is finished, we release our fake context active |
| * reference */ |
| kbase_pm_context_idle(kbdev); |
| } |
| |
| KBASE_EXPORT_TEST_API(kbase_pm_ca_set_policy); |
| |
| u64 kbase_pm_ca_get_core_mask(struct kbase_device *kbdev) |
| { |
| lockdep_assert_held(&kbdev->pm.power_change_lock); |
| |
| /* All cores must be enabled when instrumentation is in use */ |
| if (kbdev->pm.backend.instr_enabled) |
| return kbdev->gpu_props.props.raw_props.shader_present & |
| kbdev->pm.debug_core_mask_all; |
| |
| if (kbdev->pm.backend.ca_current_policy == NULL) |
| return kbdev->gpu_props.props.raw_props.shader_present & |
| kbdev->pm.debug_core_mask_all; |
| |
| return kbdev->pm.backend.ca_current_policy->get_core_mask(kbdev) & |
| kbdev->pm.debug_core_mask_all; |
| } |
| |
| KBASE_EXPORT_TEST_API(kbase_pm_ca_get_core_mask); |
| |
| void kbase_pm_ca_update_core_status(struct kbase_device *kbdev, u64 cores_ready, |
| u64 cores_transitioning) |
| { |
| lockdep_assert_held(&kbdev->pm.power_change_lock); |
| |
| if (kbdev->pm.backend.ca_current_policy != NULL) |
| kbdev->pm.backend.ca_current_policy->update_core_status(kbdev, |
| cores_ready, |
| cores_transitioning); |
| } |
| |
| void kbase_pm_ca_instr_enable(struct kbase_device *kbdev) |
| { |
| unsigned long flags; |
| |
| spin_lock_irqsave(&kbdev->pm.power_change_lock, flags); |
| kbdev->pm.backend.instr_enabled = true; |
| |
| kbase_pm_update_cores_state_nolock(kbdev); |
| spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags); |
| } |
| |
| void kbase_pm_ca_instr_disable(struct kbase_device *kbdev) |
| { |
| unsigned long flags; |
| |
| spin_lock_irqsave(&kbdev->pm.power_change_lock, flags); |
| kbdev->pm.backend.instr_enabled = false; |
| |
| kbase_pm_update_cores_state_nolock(kbdev); |
| spin_unlock_irqrestore(&kbdev->pm.power_change_lock, flags); |
| } |