| /* | 
|  * | 
|  * (C) COPYRIGHT 2010-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. | 
|  * | 
|  */ | 
|   | 
|   | 
|   | 
|   | 
|   | 
| /** | 
|  * @file mali_kbase_pm.c | 
|  * Base kernel power management APIs | 
|  */ | 
|   | 
| #include <mali_kbase.h> | 
| #include <mali_midg_regmap.h> | 
| #include <mali_kbase_vinstr.h> | 
|   | 
| #include <mali_kbase_pm.h> | 
|   | 
| int kbase_pm_powerup(struct kbase_device *kbdev, unsigned int flags) | 
| { | 
|     return kbase_hwaccess_pm_powerup(kbdev, flags); | 
| } | 
|   | 
| void kbase_pm_halt(struct kbase_device *kbdev) | 
| { | 
|     kbase_hwaccess_pm_halt(kbdev); | 
| } | 
|   | 
| void kbase_pm_context_active(struct kbase_device *kbdev) | 
| { | 
|     (void)kbase_pm_context_active_handle_suspend(kbdev, KBASE_PM_SUSPEND_HANDLER_NOT_POSSIBLE); | 
| } | 
|   | 
| int kbase_pm_context_active_handle_suspend(struct kbase_device *kbdev, enum kbase_pm_suspend_handler suspend_handler) | 
| { | 
|     struct kbasep_js_device_data *js_devdata = &kbdev->js_data; | 
|     int c; | 
|     int old_count; | 
|   | 
|     KBASE_DEBUG_ASSERT(kbdev != NULL); | 
|   | 
|     /* Trace timeline information about how long it took to handle the decision | 
|      * to powerup. Sometimes the event might be missed due to reading the count | 
|      * outside of mutex, but this is necessary to get the trace timing | 
|      * correct. */ | 
|     old_count = kbdev->pm.active_count; | 
|     if (old_count == 0) | 
|         kbase_timeline_pm_send_event(kbdev, KBASE_TIMELINE_PM_EVENT_GPU_ACTIVE); | 
|   | 
|     mutex_lock(&js_devdata->runpool_mutex); | 
|     mutex_lock(&kbdev->pm.lock); | 
|     if (kbase_pm_is_suspending(kbdev)) { | 
|         switch (suspend_handler) { | 
|         case KBASE_PM_SUSPEND_HANDLER_DONT_REACTIVATE: | 
|             if (kbdev->pm.active_count != 0) | 
|                 break; | 
|             /* FALLTHROUGH */ | 
|         case KBASE_PM_SUSPEND_HANDLER_DONT_INCREASE: | 
|             mutex_unlock(&kbdev->pm.lock); | 
|             mutex_unlock(&js_devdata->runpool_mutex); | 
|             if (old_count == 0) | 
|                 kbase_timeline_pm_handle_event(kbdev, KBASE_TIMELINE_PM_EVENT_GPU_ACTIVE); | 
|             return 1; | 
|   | 
|         case KBASE_PM_SUSPEND_HANDLER_NOT_POSSIBLE: | 
|             /* FALLTHROUGH */ | 
|         default: | 
|             KBASE_DEBUG_ASSERT_MSG(false, "unreachable"); | 
|             break; | 
|         } | 
|     } | 
|     c = ++kbdev->pm.active_count; | 
|     KBASE_TIMELINE_CONTEXT_ACTIVE(kbdev, c); | 
|     KBASE_TRACE_ADD_REFCOUNT(kbdev, PM_CONTEXT_ACTIVE, NULL, NULL, 0u, c); | 
|   | 
|     /* Trace the event being handled */ | 
|     if (old_count == 0) | 
|         kbase_timeline_pm_handle_event(kbdev, KBASE_TIMELINE_PM_EVENT_GPU_ACTIVE); | 
|   | 
|     if (c == 1) | 
|         /* First context active: Power on the GPU and any cores requested by | 
|          * the policy */ | 
|         kbase_hwaccess_pm_gpu_active(kbdev); | 
|   | 
|     mutex_unlock(&kbdev->pm.lock); | 
|     mutex_unlock(&js_devdata->runpool_mutex); | 
|   | 
|     return 0; | 
| } | 
|   | 
| KBASE_EXPORT_TEST_API(kbase_pm_context_active); | 
|   | 
| void kbase_pm_context_idle(struct kbase_device *kbdev) | 
| { | 
|     struct kbasep_js_device_data *js_devdata = &kbdev->js_data; | 
|     int c; | 
|     int old_count; | 
|   | 
|     KBASE_DEBUG_ASSERT(kbdev != NULL); | 
|   | 
|     /* Trace timeline information about how long it took to handle the decision | 
|      * to powerdown. Sometimes the event might be missed due to reading the | 
|      * count outside of mutex, but this is necessary to get the trace timing | 
|      * correct. */ | 
|     old_count = kbdev->pm.active_count; | 
|     if (old_count == 0) | 
|         kbase_timeline_pm_send_event(kbdev, KBASE_TIMELINE_PM_EVENT_GPU_IDLE); | 
|   | 
|     mutex_lock(&js_devdata->runpool_mutex); | 
|     mutex_lock(&kbdev->pm.lock); | 
|   | 
|     c = --kbdev->pm.active_count; | 
|     KBASE_TIMELINE_CONTEXT_ACTIVE(kbdev, c); | 
|     KBASE_TRACE_ADD_REFCOUNT(kbdev, PM_CONTEXT_IDLE, NULL, NULL, 0u, c); | 
|   | 
|     KBASE_DEBUG_ASSERT(c >= 0); | 
|   | 
|     /* Trace the event being handled */ | 
|     if (old_count == 0) | 
|         kbase_timeline_pm_handle_event(kbdev, KBASE_TIMELINE_PM_EVENT_GPU_IDLE); | 
|   | 
|     if (c == 0) { | 
|         /* Last context has gone idle */ | 
|         kbase_hwaccess_pm_gpu_idle(kbdev); | 
|   | 
|         /* Wake up anyone waiting for this to become 0 (e.g. suspend). The | 
|          * waiters must synchronize with us by locking the pm.lock after | 
|          * waiting */ | 
|         wake_up(&kbdev->pm.zero_active_count_wait); | 
|     } | 
|   | 
|     mutex_unlock(&kbdev->pm.lock); | 
|     mutex_unlock(&js_devdata->runpool_mutex); | 
| } | 
|   | 
| KBASE_EXPORT_TEST_API(kbase_pm_context_idle); | 
|   | 
| void kbase_pm_suspend(struct kbase_device *kbdev) | 
| { | 
|     KBASE_DEBUG_ASSERT(kbdev); | 
|   | 
|     /* Suspend vinstr. | 
|      * This call will block until vinstr is suspended. */ | 
|     kbase_vinstr_suspend(kbdev->vinstr_ctx); | 
|   | 
|     mutex_lock(&kbdev->pm.lock); | 
|     KBASE_DEBUG_ASSERT(!kbase_pm_is_suspending(kbdev)); | 
|     kbdev->pm.suspending = true; | 
|     mutex_unlock(&kbdev->pm.lock); | 
|   | 
|     /* From now on, the active count will drop towards zero. Sometimes, it'll | 
|      * go up briefly before going down again. However, once it reaches zero it | 
|      * will stay there - guaranteeing that we've idled all pm references */ | 
|   | 
|     /* Suspend job scheduler and associated components, so that it releases all | 
|      * the PM active count references */ | 
|     kbasep_js_suspend(kbdev); | 
|   | 
|     /* Wait for the active count to reach zero. This is not the same as | 
|      * waiting for a power down, since not all policies power down when this | 
|      * reaches zero. */ | 
|     wait_event(kbdev->pm.zero_active_count_wait, kbdev->pm.active_count == 0); | 
|   | 
|     /* NOTE: We synchronize with anything that was just finishing a | 
|      * kbase_pm_context_idle() call by locking the pm.lock below */ | 
|   | 
|     kbase_hwaccess_pm_suspend(kbdev); | 
| } | 
|   | 
| void kbase_pm_resume(struct kbase_device *kbdev) | 
| { | 
|     /* MUST happen before any pm_context_active calls occur */ | 
|     kbase_hwaccess_pm_resume(kbdev); | 
|   | 
|     /* Initial active call, to power on the GPU/cores if needed */ | 
|     kbase_pm_context_active(kbdev); | 
|   | 
|     /* Resume any blocked atoms (which may cause contexts to be scheduled in | 
|      * and dependent atoms to run) */ | 
|     kbase_resume_suspended_soft_jobs(kbdev); | 
|   | 
|     /* Resume the Job Scheduler and associated components, and start running | 
|      * atoms */ | 
|     kbasep_js_resume(kbdev); | 
|   | 
|     /* Matching idle call, to power off the GPU/cores if we didn't actually | 
|      * need it and the policy doesn't want it on */ | 
|     kbase_pm_context_idle(kbdev); | 
|   | 
|     /* Resume vinstr operation */ | 
|     kbase_vinstr_resume(kbdev->vinstr_ctx); | 
| } |