206 lines
5.9 KiB
C
206 lines
5.9 KiB
C
/*
|
|
*
|
|
* (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);
|
|
}
|
|
|