/* * Copyright (C) 2013-2014, 2016-2017 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_group.h" #include "mali_pm.h" static struct mali_pm_domain *mali_pm_domains[MALI_MAX_NUMBER_OF_DOMAINS] = { NULL, }; void mali_pm_domain_initialize(void) { /* Domains will be initialized/created on demand */ } void mali_pm_domain_terminate(void) { int i; /* Delete all domains that has been created */ for (i = 0; i < MALI_MAX_NUMBER_OF_DOMAINS; i++) { mali_pm_domain_delete(mali_pm_domains[i]); mali_pm_domains[i] = NULL; } } struct mali_pm_domain *mali_pm_domain_create(u32 pmu_mask) { struct mali_pm_domain *domain = NULL; u32 domain_id = 0; domain = mali_pm_domain_get_from_mask(pmu_mask); if (NULL != domain) return 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) { domain->power_is_on = MALI_FALSE; domain->pmu_mask = pmu_mask; domain->use_count = 0; _mali_osk_list_init(&domain->group_list); _mali_osk_list_init(&domain->l2_cache_list); domain_id = _mali_osk_fls(pmu_mask) - 1; /* Verify the domain_id */ MALI_DEBUG_ASSERT(MALI_MAX_NUMBER_OF_DOMAINS > domain_id); /* Verify that pmu_mask only one bit is set */ MALI_DEBUG_ASSERT((1 << domain_id) == pmu_mask); mali_pm_domains[domain_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_list_delinit(&domain->group_list); _mali_osk_list_delinit(&domain->l2_cache_list); _mali_osk_free(domain); } void mali_pm_domain_add_group(struct mali_pm_domain *domain, struct mali_group *group) { MALI_DEBUG_ASSERT_POINTER(domain); MALI_DEBUG_ASSERT_POINTER(group); /* * Use addtail because virtual group is created last and it needs * to be at the end of the list (in order to be activated after * all children. */ _mali_osk_list_addtail(&group->pm_domain_list, &domain->group_list); } void mali_pm_domain_add_l2_cache(struct mali_pm_domain *domain, struct mali_l2_cache_core *l2_cache) { MALI_DEBUG_ASSERT_POINTER(domain); MALI_DEBUG_ASSERT_POINTER(l2_cache); _mali_osk_list_add(&l2_cache->pm_domain_list, &domain->l2_cache_list); } struct mali_pm_domain *mali_pm_domain_get_from_mask(u32 mask) { u32 id = 0; if (0 == mask) { return NULL; } id = _mali_osk_fls(mask) - 1; MALI_DEBUG_ASSERT(MALI_MAX_NUMBER_OF_DOMAINS > id); /* Verify that pmu_mask only one bit is set */ MALI_DEBUG_ASSERT((1 << id) == mask); return mali_pm_domains[id]; } struct mali_pm_domain *mali_pm_domain_get_from_index(u32 id) { MALI_DEBUG_ASSERT(MALI_MAX_NUMBER_OF_DOMAINS > id); return mali_pm_domains[id]; } u32 mali_pm_domain_ref_get(struct mali_pm_domain *domain) { MALI_DEBUG_ASSERT_POINTER(domain); if (0 == domain->use_count) { _mali_osk_pm_dev_ref_get_async(); } ++domain->use_count; MALI_DEBUG_PRINT(4, ("PM domain %p: ref_get, use_count => %u\n", domain, domain->use_count)); /* Return our mask so caller can check this against wanted mask */ return domain->pmu_mask; } u32 mali_pm_domain_ref_put(struct mali_pm_domain *domain) { MALI_DEBUG_ASSERT_POINTER(domain); --domain->use_count; MALI_DEBUG_PRINT(4, ("PM domain %p: ref_put, use_count => %u\n", domain, domain->use_count)); if (0 == domain->use_count) { _mali_osk_pm_dev_ref_put(); } /* * Return the PMU mask which now could be be powered down * (the bit for this domain). * This is the responsibility of the caller (mali_pm) */ return (0 == domain->use_count ? domain->pmu_mask : 0); } #if MALI_STATE_TRACKING u32 mali_pm_domain_get_id(struct mali_pm_domain *domain) { u32 id = 0; MALI_DEBUG_ASSERT_POINTER(domain); MALI_DEBUG_ASSERT(0 != domain->pmu_mask); id = _mali_osk_fls(domain->pmu_mask) - 1; MALI_DEBUG_ASSERT(MALI_MAX_NUMBER_OF_DOMAINS > id); /* Verify that pmu_mask only one bit is set */ MALI_DEBUG_ASSERT((1 << id) == domain->pmu_mask); /* Verify that we have stored the domain at right id/index */ MALI_DEBUG_ASSERT(domain == mali_pm_domains[id]); return id; } #endif #if defined(DEBUG) mali_bool mali_pm_domain_all_unused(void) { int i; for (i = 0; i < MALI_MAX_NUMBER_OF_DOMAINS; i++) { if (NULL == mali_pm_domains[i]) { /* Nothing to check */ continue; } if (MALI_TRUE == mali_pm_domains[i]->power_is_on) { /* Not ready for suspend! */ return MALI_FALSE; } if (0 != mali_pm_domains[i]->use_count) { /* Not ready for suspend! */ return MALI_FALSE; } } return MALI_TRUE; } #endif