blob: 95f98b67850bbbb0623729a64cb45f910251ec8d [file] [log] [blame]
/*
* Copyright (C) 2013 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_kernel_common.h"
#include "mali_osk.h"
#include "mali_pm_domain.h"
#include "mali_pmu.h"
#include "mali_pm.h"
#include "mali_group.h"
#define MALI_PM_DOMAIN_MAX_DOMAINS 7
static struct mali_pm_domain *mali_pm_domains[MALI_PM_DOMAIN_MAX_DOMAINS] = { NULL, };
static void mali_pm_domain_lock(struct mali_pm_domain *domain)
{
_mali_osk_lock_wait(domain->lock, _MALI_OSK_LOCKMODE_RW);
}
static void mali_pm_domain_unlock(struct mali_pm_domain *domain)
{
_mali_osk_lock_signal(domain->lock, _MALI_OSK_LOCKMODE_RW);
}
MALI_STATIC_INLINE void mali_pm_domain_state_set(struct mali_pm_domain *domain, mali_pm_domain_state state)
{
domain->state = state;
}
struct mali_pm_domain *mali_pm_domain_create(u32 id, u32 pmu_mask)
{
struct mali_pm_domain* domain;
MALI_DEBUG_PRINT(2, ("Mali PM domain: Creating Mali PM domain (mask=0x%08X)\n", pmu_mask));
domain = (struct mali_pm_domain *)_mali_osk_malloc(sizeof(struct mali_pm_domain));
if (NULL != domain)
{
_mali_osk_lock_flags_t flags = _MALI_OSK_LOCKFLAG_SPINLOCK_IRQ | _MALI_OSK_LOCKFLAG_NONINTERRUPTABLE;
domain->lock = _mali_osk_lock_init(flags, 0, _MALI_OSK_LOCK_ORDER_PM_DOMAIN);
if (NULL == domain->lock)
{
_mali_osk_free(domain);
return NULL;
}
domain->state = MALI_PM_DOMAIN_ON;
domain->pmu_mask = pmu_mask;
domain->use_count = 0;
domain->group_list = NULL;
domain->group_count = 0;
domain->l2 = NULL;
MALI_DEBUG_ASSERT(MALI_PM_DOMAIN_MAX_DOMAINS > id);
mali_pm_domains[id] = domain;
return domain;
}
else
{
MALI_DEBUG_PRINT_ERROR(("Unable to create PM domain\n"));
}
return NULL;
}
void mali_pm_domain_delete(struct mali_pm_domain *domain)
{
if (NULL == domain)
{
return;
}
_mali_osk_lock_term(domain->lock);
_mali_osk_free(domain);
}
void mali_pm_domain_terminate(void)
{
int i;
/* Delete all domains */
for (i = 0; i < MALI_PM_DOMAIN_MAX_DOMAINS; i++)
{
mali_pm_domain_delete(mali_pm_domains[i]);
}
}
void mali_pm_domain_add_group(u32 id, struct mali_group *group)
{
struct mali_pm_domain *domain = mali_pm_domain_get(id);
struct mali_group *next;
if (NULL == domain) return;
MALI_DEBUG_ASSERT_POINTER(group);
/* Assume domain is on and group is enabled initially. */
mali_pm_domain_ref_get(domain);
++domain->group_count;
next = domain->group_list;
domain->group_list = group;
group->pm_domain_list = next;
mali_group_set_pm_domain(group, domain);
}
void mali_pm_domain_add_l2(u32 id, struct mali_l2_cache_core *l2)
{
struct mali_pm_domain *domain = mali_pm_domain_get(id);
if (NULL == domain) return;
MALI_DEBUG_ASSERT(NULL == domain->l2);
MALI_DEBUG_ASSERT(NULL != l2);
domain->l2 = l2;
mali_l2_cache_set_pm_domain(l2, domain);
}
struct mali_pm_domain *mali_pm_domain_get(u32 id)
{
MALI_DEBUG_ASSERT(MALI_PM_DOMAIN_MAX_DOMAINS > id);
return mali_pm_domains[id];
}
void mali_pm_domain_ref_get(struct mali_pm_domain *domain)
{
if (NULL == domain) return;
mali_pm_domain_lock(domain);
++domain->use_count;
if (MALI_PM_DOMAIN_ON != domain->state)
{
/* Power on */
struct mali_pmu_core *pmu = mali_pmu_get_global_pmu_core();
MALI_DEBUG_PRINT(3, ("PM Domain: Powering on 0x%08x\n", domain->pmu_mask));
if (NULL != pmu)
{
_mali_osk_errcode_t err;
err = mali_pmu_power_up(pmu, domain->pmu_mask);
if (_MALI_OSK_ERR_OK != err && _MALI_OSK_ERR_BUSY != err)
{
MALI_PRINT_ERROR(("PM Domain: Failed to power up PM domain 0x%08x\n",
domain->pmu_mask));
}
}
mali_pm_domain_state_set(domain, MALI_PM_DOMAIN_ON);
}
else
{
MALI_DEBUG_ASSERT(MALI_PM_DOMAIN_ON == mali_pm_domain_state_get(domain));
}
mali_pm_domain_unlock(domain);
}
void mali_pm_domain_ref_put(struct mali_pm_domain *domain)
{
if (NULL == domain) return;
mali_pm_domain_lock(domain);
--domain->use_count;
if (0 == domain->use_count && MALI_PM_DOMAIN_OFF != domain->state)
{
/* Power off */
struct mali_pmu_core *pmu = mali_pmu_get_global_pmu_core();
MALI_DEBUG_PRINT(3, ("PM Domain: Powering off 0x%08x\n", domain->pmu_mask));
mali_pm_domain_state_set(domain, MALI_PM_DOMAIN_OFF);
if (NULL != pmu)
{
_mali_osk_errcode_t err;
err = mali_pmu_power_down(pmu, domain->pmu_mask);
if (_MALI_OSK_ERR_OK != err && _MALI_OSK_ERR_BUSY != err)
{
MALI_PRINT_ERROR(("PM Domain: Failed to power down PM domain 0x%08x\n",
domain->pmu_mask));
}
}
}
mali_pm_domain_unlock(domain);
}
mali_bool mali_pm_domain_lock_state(struct mali_pm_domain *domain)
{
mali_bool is_powered = MALI_TRUE;
/* Take a reference without powering on */
if (NULL != domain)
{
mali_pm_domain_lock(domain);
++domain->use_count;
if (MALI_PM_DOMAIN_ON != domain->state)
{
is_powered = MALI_FALSE;
}
mali_pm_domain_unlock(domain);
}
if(!_mali_osk_pm_dev_ref_add_no_power_on())
{
is_powered = MALI_FALSE;
}
return is_powered;
}
void mali_pm_domain_unlock_state(struct mali_pm_domain *domain)
{
_mali_osk_pm_dev_ref_dec_no_power_on();
if (NULL != domain)
{
mali_pm_domain_ref_put(domain);
}
}