diff options
author | Andrey Konovalov <andrey.konovalov@linaro.org> | 2012-05-22 20:39:20 +0400 |
---|---|---|
committer | Andrey Konovalov <andrey.konovalov@linaro.org> | 2012-05-22 20:39:20 +0400 |
commit | c4aed34d10ad8a961377d568e39c54f1de3b4e82 (patch) | |
tree | a1e266d02c5dc2d2d4ce699e14f6805fb8ffa176 /drivers/gpu/arm/mali/common/pmm | |
parent | 9c603efdd304e83cc140797d558cf7bfa510be92 (diff) | |
parent | 3478a2870005bbe5cda37e12612e91fa45c593a0 (diff) |
Automatically merging rebase-samslt-mali into merge-linux-linaro
Conflicting files:
Diffstat (limited to 'drivers/gpu/arm/mali/common/pmm')
-rw-r--r-- | drivers/gpu/arm/mali/common/pmm/mali_pmm.c | 1006 | ||||
-rw-r--r-- | drivers/gpu/arm/mali/common/pmm/mali_pmm.h | 348 | ||||
-rw-r--r-- | drivers/gpu/arm/mali/common/pmm/mali_pmm_pmu.c | 350 | ||||
-rw-r--r-- | drivers/gpu/arm/mali/common/pmm/mali_pmm_pmu.h | 87 | ||||
-rw-r--r-- | drivers/gpu/arm/mali/common/pmm/mali_pmm_policy.c | 243 | ||||
-rw-r--r-- | drivers/gpu/arm/mali/common/pmm/mali_pmm_policy.h | 155 | ||||
-rw-r--r-- | drivers/gpu/arm/mali/common/pmm/mali_pmm_policy_alwayson.c | 80 | ||||
-rw-r--r-- | drivers/gpu/arm/mali/common/pmm/mali_pmm_policy_alwayson.h | 62 | ||||
-rw-r--r-- | drivers/gpu/arm/mali/common/pmm/mali_pmm_policy_jobcontrol.c | 470 | ||||
-rw-r--r-- | drivers/gpu/arm/mali/common/pmm/mali_pmm_policy_jobcontrol.h | 80 | ||||
-rw-r--r-- | drivers/gpu/arm/mali/common/pmm/mali_pmm_state.c | 721 | ||||
-rw-r--r-- | drivers/gpu/arm/mali/common/pmm/mali_pmm_state.h | 295 | ||||
-rw-r--r-- | drivers/gpu/arm/mali/common/pmm/mali_pmm_system.h | 66 |
13 files changed, 3963 insertions, 0 deletions
diff --git a/drivers/gpu/arm/mali/common/pmm/mali_pmm.c b/drivers/gpu/arm/mali/common/pmm/mali_pmm.c new file mode 100644 index 00000000000..3c015ba26fe --- /dev/null +++ b/drivers/gpu/arm/mali/common/pmm/mali_pmm.c @@ -0,0 +1,1006 @@ +/* + * Copyright (C) 2010-2012 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_pmm.c + * Implementation of the power management module for the kernel device driver + */ + +#if USING_MALI_PMM + +#include "mali_ukk.h" +#include "mali_kernel_common.h" +#include "mali_kernel_subsystem.h" + +#include "mali_pmm.h" +#include "mali_pmm_system.h" +#include "mali_pmm_state.h" +#include "mali_pmm_policy.h" +#include "mali_pmm_pmu.h" +#include "mali_platform.h" + +/* Internal PMM subsystem state */ +static _mali_pmm_internal_state_t *pmm_state = NULL; +/* Mali kernel subsystem id */ +static mali_kernel_subsystem_identifier mali_subsystem_pmm_id = -1; + +#define GET_PMM_STATE_PTR (pmm_state) + +/* Internal functions */ +static _mali_osk_errcode_t malipmm_create(_mali_osk_resource_t *resource); +static void pmm_event_process( void ); +_mali_osk_errcode_t malipmm_irq_uhandler(void *data); +void malipmm_irq_bhandler(void *data); + +/** @brief Start the PMM subsystem + * + * @param id Subsystem id to uniquely identify this subsystem + * @return _MALI_OSK_ERR_OK if the system started successfully, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t malipmm_kernel_subsystem_start( mali_kernel_subsystem_identifier id ); + +/** @brief Perform post start up of the PMM subsystem + * + * Post start up includes initializing the current policy, now that the system is + * completely started - to stop policies turning off hardware during the start up + * + * @param id the unique subsystem id + * @return _MALI_OSK_ERR_OK if the post startup was successful, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t malipmm_kernel_load_complete( mali_kernel_subsystem_identifier id ); + +/** @brief Terminate the PMM subsystem + * + * @param id the unique subsystem id + */ +void malipmm_kernel_subsystem_terminate( mali_kernel_subsystem_identifier id ); + +#if MALI_STATE_TRACKING +u32 malipmm_subsystem_dump_state( char *buf, u32 size ); +#endif + + +/* This will be one of the subsystems in the array of subsystems: + static struct mali_kernel_subsystem * subsystems[]; + found in file: mali_kernel_core.c +*/ +struct mali_kernel_subsystem mali_subsystem_pmm= +{ + malipmm_kernel_subsystem_start, /* startup */ + malipmm_kernel_subsystem_terminate, /* shutdown */ + malipmm_kernel_load_complete, /* loaded all subsystems */ + NULL, + NULL, + NULL, + NULL, +#if MALI_STATE_TRACKING + malipmm_subsystem_dump_state, /* dump_state */ +#endif +}; + +#if PMM_OS_TEST + +u32 power_test_event = 0; +mali_bool power_test_flag = MALI_FALSE; +_mali_osk_timer_t *power_test_timer = NULL; + +void _mali_osk_pmm_power_up_done(mali_pmm_message_data data) +{ + MALI_PRINT(("POWER TEST OS UP DONE\n")); +} + +void _mali_osk_pmm_power_down_done(mali_pmm_message_data data) +{ + MALI_PRINT(("POWER TEST OS DOWN DONE\n")); +} + +/** + * Symbian OS Power Up call to the driver + */ +void power_test_callback( void *arg ) +{ + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + MALI_DEBUG_ASSERT_POINTER(pmm); + + power_test_flag = MALI_TRUE; + _mali_osk_irq_schedulework( pmm->irq ); +} + +void power_test_start() +{ + power_test_timer = _mali_osk_timer_init(); + _mali_osk_timer_setcallback( power_test_timer, power_test_callback, NULL ); + + /* First event is power down */ + power_test_event = MALI_PMM_EVENT_OS_POWER_DOWN; + _mali_osk_timer_add( power_test_timer, 10000 ); +} + +mali_bool power_test_check() +{ + if( power_test_flag ) + { + _mali_uk_pmm_message_s event = { + NULL, + 0, + 1 }; + event.id = power_test_event; + + power_test_flag = MALI_FALSE; + + /* Send event */ + _mali_ukk_pmm_event_message( &event ); + + /* Switch to next event to test */ + if( power_test_event == MALI_PMM_EVENT_OS_POWER_DOWN ) + { + power_test_event = MALI_PMM_EVENT_OS_POWER_UP; + } + else + { + power_test_event = MALI_PMM_EVENT_OS_POWER_DOWN; + } + _mali_osk_timer_add( power_test_timer, 5000 ); + + return MALI_TRUE; + } + + return MALI_FALSE; +} + +void power_test_end() +{ + _mali_osk_timer_del( power_test_timer ); + _mali_osk_timer_term( power_test_timer ); + power_test_timer = NULL; +} + +#endif + +void _mali_ukk_pmm_event_message( _mali_uk_pmm_message_s *args ) +{ + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + _mali_osk_notification_t *msg; + mali_pmm_message_t *event; + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_DEBUG_ASSERT_POINTER(args); + + MALIPMM_DEBUG_PRINT( ("PMM: sending message\n") ); + +#if MALI_PMM_TRACE && MALI_PMM_TRACE_SENT_EVENTS + _mali_pmm_trace_event_message( args, MALI_FALSE ); +#endif + + msg = _mali_osk_notification_create( MALI_PMM_NOTIFICATION_TYPE, sizeof( mali_pmm_message_t ) ); + + if( msg ) + { + event = (mali_pmm_message_t *)msg->result_buffer; + event->id = args->id; + event->ts = _mali_osk_time_tickcount(); + event->data = args->data; + + _mali_osk_atomic_inc( &(pmm->messages_queued) ); + + if( args->id > MALI_PMM_EVENT_INTERNALS ) + { + /* Internal PMM message */ + _mali_osk_notification_queue_send( pmm->iqueue, msg ); + #if (MALI_PMM_TRACE || MALI_STATE_TRACKING) + pmm->imessages_sent++; + #endif + } + else + { + /* Real event */ + _mali_osk_notification_queue_send( pmm->queue, msg ); + #if (MALI_PMM_TRACE || MALI_STATE_TRACKING) + pmm->messages_sent++; + #endif + } + } + else + { + MALI_PRINT_ERROR( ("PMM: Could not send message %d", args->id) ); + /* Make note of this OOM - which has caused a missed event */ + pmm->missed++; + } + + /* Schedule time to look at the event or the fact we couldn't create an event */ + _mali_osk_irq_schedulework( pmm->irq ); +} + +mali_pmm_state _mali_pmm_state( void ) +{ + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + MALI_DEBUG_ASSERT_POINTER(pmm); + + if( pmm && (mali_subsystem_pmm_id != -1) ) + { + return pmm->state; + } + + /* No working subsystem yet */ + return MALI_PMM_STATE_UNAVAILABLE; +} + + +mali_pmm_core_mask _mali_pmm_cores_list( void ) +{ + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + MALI_DEBUG_ASSERT_POINTER(pmm); + + return pmm->cores_registered; +} + +mali_pmm_core_mask _mali_pmm_cores_powered( void ) +{ + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + MALI_DEBUG_ASSERT_POINTER(pmm); + + return pmm->cores_powered; +} + + +_mali_osk_errcode_t _mali_pmm_list_policies( + u32 policy_list_size, + mali_pmm_policy *policy_list, + u32 *policies_available ) +{ + /* TBD - This is currently a stub function for basic power management */ + + MALI_ERROR( _MALI_OSK_ERR_UNSUPPORTED ); +} + +_mali_osk_errcode_t _mali_pmm_set_policy( mali_pmm_policy policy ) +{ + /* TBD - This is currently a stub function for basic power management */ + +/* TBD - When this is not a stub... include tracing... +#if MALI_PMM_TRACE + _mali_pmm_trace_policy_change( old, newpolicy ); +#endif +*/ + MALI_ERROR( _MALI_OSK_ERR_UNSUPPORTED ); +} + +_mali_osk_errcode_t _mali_pmm_get_policy( mali_pmm_policy *policy ) +{ + if( policy ) + { + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + MALI_DEBUG_ASSERT_POINTER(pmm); + + if( pmm ) + { + *policy = pmm->policy; + MALI_SUCCESS; + } + else + { + *policy = MALI_PMM_POLICY_NONE; + MALI_ERROR( _MALI_OSK_ERR_FAULT ); + } + } + + /* No return argument */ + MALI_ERROR( _MALI_OSK_ERR_INVALID_ARGS ); +} + +#if ( MALI_PMM_TRACE || MALI_STATE_TRACKING ) + +/* Event names - order must match mali_pmm_event_id enum */ +static char *pmm_trace_events[] = { + "OS_POWER_UP", + "OS_POWER_DOWN", + "JOB_SCHEDULED", + "JOB_QUEUED", + "JOB_FINISHED", + "TIMEOUT", +}; + +/* State names - order must match mali_pmm_state enum */ +static char *pmm_trace_state[] = { + "UNAVAILABLE", + "SYSTEM ON", + "SYSTEM OFF", + "SYSTEM TRANSITION", +}; + +/* Policy names - order must match mali_pmm_policy enum */ +static char *pmm_trace_policy[] = { + "NONE", + "ALWAYS ON", + "JOB CONTROL", +}; + +/* Status names - order must match mali_pmm_status enum */ +static char *pmm_trace_status[] = { + "MALI_PMM_STATUS_IDLE", /**< PMM is waiting next event */ + "MALI_PMM_STATUS_POLICY_POWER_DOWN", /**< Policy initiated power down */ + "MALI_PMM_STATUS_POLICY_POWER_UP", /**< Policy initiated power down */ + "MALI_PMM_STATUS_OS_WAITING", /**< PMM is waiting for OS power up */ + "MALI_PMM_STATUS_OS_POWER_DOWN", /**< OS initiated power down */ + "MALI_PMM_STATUS_RUNTIME_IDLE_IN_PROGRESS", + "MALI_PMM_STATUS_DVFS_PAUSE", /**< PMM DVFS Status Pause */ + "MALI_PMM_STATUS_OS_POWER_UP", /**< OS initiated power up */ + "MALI_PMM_STATUS_OFF", /**< PMM is not active */ +}; + +#endif /* MALI_PMM_TRACE || MALI_STATE_TRACKING */ +#if MALI_PMM_TRACE + +/* UK event names - order must match mali_pmm_event_id enum */ +static char *pmm_trace_events_uk[] = { + "UKS", + "UK_EXAMPLE", +}; + +/* Internal event names - order must match mali_pmm_event_id enum */ +static char *pmm_trace_events_internal[] = { + "INTERNALS", + "INTERNAL_POWER_UP_ACK", + "INTERNAL_POWER_DOWN_ACK", +}; + +void _mali_pmm_trace_hardware_change( mali_pmm_core_mask old, mali_pmm_core_mask newstate ) +{ + const char *dname; + const char *cname; + const char *ename; + + if( old != newstate ) + { + if( newstate == 0 ) + { + dname = "NO cores"; + } + else + { + dname = pmm_trace_get_core_name( newstate ); + } + + /* These state checks only work if the assumption that only cores can be + * turned on or turned off in seperate actions is true. If core power states can + * be toggled (some one, some off) at the same time, this check does not work + */ + if( old > newstate ) + { + /* Cores have turned off */ + cname = pmm_trace_get_core_name( old - newstate ); + ename = "OFF"; + } + else + { + /* Cores have turned on */ + cname = pmm_trace_get_core_name( newstate - old ); + ename = "ON"; + } + MALI_PRINT( ("PMM Trace: Hardware %s ON, %s just turned %s. { 0x%08x -> 0x%08x }", dname, cname, ename, old, newstate) ); + } +} + +void _mali_pmm_trace_state_change( mali_pmm_state old, mali_pmm_state newstate ) +{ + if( old != newstate ) + { + MALI_PRINT( ("PMM Trace: State changed from %s to %s", pmm_trace_state[old], pmm_trace_state[newstate]) ); + } +} + +void _mali_pmm_trace_policy_change( mali_pmm_policy old, mali_pmm_policy newpolicy ) +{ + if( old != newpolicy ) + { + MALI_PRINT( ("PMM Trace: Policy changed from %s to %s", pmm_trace_policy[old], pmm_trace_policy[newpolicy]) ); + } +} + +void _mali_pmm_trace_event_message( mali_pmm_message_t *event, mali_bool received ) +{ + const char *ename; + const char *dname; + const char *tname; + const char *format = "PMM Trace: Event %s { (%d) %s, %d ticks, (0x%x) %s }"; + + MALI_DEBUG_ASSERT_POINTER(event); + + tname = (received) ? "received" : "sent"; + + if( event->id >= MALI_PMM_EVENT_INTERNALS ) + { + ename = pmm_trace_events_internal[((int)event->id) - MALI_PMM_EVENT_INTERNALS]; + } + else if( event->id >= MALI_PMM_EVENT_UKS ) + { + ename = pmm_trace_events_uk[((int)event->id) - MALI_PMM_EVENT_UKS]; + } + else + { + ename = pmm_trace_events[event->id]; + } + + switch( event->id ) + { + case MALI_PMM_EVENT_OS_POWER_UP: + case MALI_PMM_EVENT_OS_POWER_DOWN: + dname = "os event"; + break; + + case MALI_PMM_EVENT_JOB_SCHEDULED: + case MALI_PMM_EVENT_JOB_QUEUED: + case MALI_PMM_EVENT_JOB_FINISHED: + case MALI_PMM_EVENT_INTERNAL_POWER_UP_ACK: + case MALI_PMM_EVENT_INTERNAL_POWER_DOWN_ACK: + dname = pmm_trace_get_core_name( (mali_pmm_core_mask)event->data ); + break; + + case MALI_PMM_EVENT_TIMEOUT: + dname = "timeout start"; + /* Print data with a different format */ + format = "PMM Trace: Event %s { (%d) %s, %d ticks, %d ticks %s }"; + break; + default: + dname = "unknown data"; + } + + MALI_PRINT( (format, tname, (u32)event->id, ename, event->ts, (u32)event->data, dname) ); +} + +#endif /* MALI_PMM_TRACE */ + + +/****************** Mali Kernel API *****************/ + +_mali_osk_errcode_t malipmm_kernel_subsystem_start( mali_kernel_subsystem_identifier id ) +{ + mali_subsystem_pmm_id = id; + MALI_CHECK_NO_ERROR(_mali_kernel_core_register_resource_handler(PMU, malipmm_create)); + MALI_SUCCESS; +} + +_mali_osk_errcode_t malipmm_create(_mali_osk_resource_t *resource) +{ + /* Create PMM state memory */ + MALI_DEBUG_ASSERT( pmm_state == NULL ); + pmm_state = (_mali_pmm_internal_state_t *) _mali_osk_malloc(sizeof(*pmm_state)); + MALI_CHECK_NON_NULL( pmm_state, _MALI_OSK_ERR_NOMEM ); + + /* All values get 0 as default */ + _mali_osk_memset(pmm_state, 0, sizeof(*pmm_state)); + + /* Set up the initial PMM state */ + pmm_state->waiting = 0; + pmm_state->status = MALI_PMM_STATUS_IDLE; + pmm_state->state = MALI_PMM_STATE_UNAVAILABLE; /* Until a core registers */ + + /* Set up policy via compile time option for the moment */ +#if MALI_PMM_ALWAYS_ON + pmm_state->policy = MALI_PMM_POLICY_ALWAYS_ON; +#else + pmm_state->policy = MALI_PMM_POLICY_JOB_CONTROL; +#endif + +#if MALI_PMM_TRACE + _mali_pmm_trace_policy_change( MALI_PMM_POLICY_NONE, pmm_state->policy ); +#endif + + /* Set up assumes all values are initialized to NULL or MALI_FALSE, so + * we can exit halfway through set up and perform clean up + */ + +#if USING_MALI_PMU + if( mali_pmm_pmu_init(resource) != _MALI_OSK_ERR_OK ) goto pmm_fail_cleanup; + pmm_state->pmu_initialized = MALI_TRUE; +#endif + pmm_state->queue = _mali_osk_notification_queue_init(); + if( !pmm_state->queue ) goto pmm_fail_cleanup; + + pmm_state->iqueue = _mali_osk_notification_queue_init(); + if( !pmm_state->iqueue ) goto pmm_fail_cleanup; + + /* We are creating an IRQ handler just for the worker thread it gives us */ + pmm_state->irq = _mali_osk_irq_init( _MALI_OSK_IRQ_NUMBER_PMM, + malipmm_irq_uhandler, + malipmm_irq_bhandler, + NULL, + NULL, + (void *)pmm_state, /* PMM state is passed to IRQ */ + "PMM handler" ); + + if( !pmm_state->irq ) goto pmm_fail_cleanup; + + pmm_state->lock = _mali_osk_lock_init((_mali_osk_lock_flags_t)(_MALI_OSK_LOCKFLAG_READERWRITER | _MALI_OSK_LOCKFLAG_ORDERED), 0, 75); + if( !pmm_state->lock ) goto pmm_fail_cleanup; + + if( _mali_osk_atomic_init( &(pmm_state->messages_queued), 0 ) != _MALI_OSK_ERR_OK ) + { + goto pmm_fail_cleanup; + } + + MALIPMM_DEBUG_PRINT( ("PMM: subsystem created, policy=%d\n", pmm_state->policy) ); + + MALI_SUCCESS; + +pmm_fail_cleanup: + MALI_PRINT_ERROR( ("PMM: subsystem failed to be created\n") ); + if( pmm_state ) + { + if( pmm_state->lock ) _mali_osk_lock_term( pmm_state->lock ); + if( pmm_state->irq ) _mali_osk_irq_term( pmm_state->irq ); + if( pmm_state->queue ) _mali_osk_notification_queue_term( pmm_state->queue ); + if( pmm_state->iqueue ) _mali_osk_notification_queue_term( pmm_state->iqueue ); +#if USING_MALI_PMU + if( pmm_state->pmu_initialized ) + { + _mali_osk_resource_type_t t = PMU; + mali_pmm_pmu_deinit(&t); + } +#endif /* USING_MALI_PMU */ + + _mali_osk_free(pmm_state); + pmm_state = NULL; + } + MALI_ERROR( _MALI_OSK_ERR_FAULT ); +} + +_mali_osk_errcode_t malipmm_kernel_load_complete( mali_kernel_subsystem_identifier id ) +{ + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + MALI_DEBUG_ASSERT_POINTER(pmm); + + MALIPMM_DEBUG_PRINT( ("PMM: subsystem loaded, policy initializing\n") ); + +#if PMM_OS_TEST + power_test_start(); +#endif + + /* Initialize the profile now the system has loaded - so that cores are + * not turned off during start up + */ + return pmm_policy_init( pmm ); +} + +void malipmm_force_powerup( void ) +{ + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_PMM_LOCK(pmm); + pmm->status = MALI_PMM_STATUS_OFF; + MALI_PMM_UNLOCK(pmm); + + /* flush PMM workqueue */ + _mali_osk_flush_workqueue( pmm->irq ); + + if (pmm->cores_powered == 0) + { + malipmm_powerup(pmm->cores_registered); + } +} + +void malipmm_kernel_subsystem_terminate( mali_kernel_subsystem_identifier id ) +{ + /* Check this is the right system */ + MALI_DEBUG_ASSERT( id == mali_subsystem_pmm_id ); + MALI_DEBUG_ASSERT_POINTER(pmm_state); + + if( pmm_state ) + { +#if PMM_OS_TEST + power_test_end(); +#endif + /* Get the lock so we can shutdown */ + MALI_PMM_LOCK(pmm_state); +#if MALI_STATE_TRACKING + pmm_state->mali_pmm_lock_acquired = 1; +#endif /* MALI_STATE_TRACKING */ + pmm_state->status = MALI_PMM_STATUS_OFF; +#if MALI_STATE_TRACKING + pmm_state->mali_pmm_lock_acquired = 0; +#endif /* MALI_STATE_TRACKING */ + MALI_PMM_UNLOCK(pmm_state); + _mali_osk_pmm_ospmm_cleanup(); + pmm_policy_term(pmm_state); + _mali_osk_irq_term( pmm_state->irq ); + _mali_osk_notification_queue_term( pmm_state->queue ); + _mali_osk_notification_queue_term( pmm_state->iqueue ); + if (pmm_state->cores_registered) malipmm_powerdown(pmm_state->cores_registered,MALI_POWER_MODE_LIGHT_SLEEP); +#if USING_MALI_PMU + if( pmm_state->pmu_initialized ) + { + _mali_osk_resource_type_t t = PMU; + mali_pmm_pmu_deinit(&t); + } +#endif /* USING_MALI_PMU */ + + _mali_osk_atomic_term( &(pmm_state->messages_queued) ); + MALI_PMM_LOCK_TERM(pmm_state); + _mali_osk_free(pmm_state); + pmm_state = NULL; + } + + MALIPMM_DEBUG_PRINT( ("PMM: subsystem terminated\n") ); +} + +_mali_osk_errcode_t malipmm_powerup( u32 cores ) +{ + _mali_osk_errcode_t err = _MALI_OSK_ERR_OK; + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + + /* If all the cores are powered down, power up the MALI */ + if (pmm->cores_powered == 0) + { + mali_platform_power_mode_change(MALI_POWER_MODE_ON); +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON + /* Initiate the power up */ + _mali_osk_pmm_dev_activate(); +#endif + } + +#if USING_MALI_PMU + err = mali_pmm_pmu_powerup( cores ); +#endif + return err; +} + +_mali_osk_errcode_t malipmm_powerdown( u32 cores, mali_power_mode power_mode ) +{ + _mali_osk_errcode_t err = _MALI_OSK_ERR_OK; + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; +#if USING_MALI_PMU + err = mali_pmm_pmu_powerdown( cores ); +#endif + + /* If all cores are powered down, power off the MALI */ + if (pmm->cores_powered == 0) + { +#if MALI_PMM_RUNTIME_JOB_CONTROL_ON + /* Initiate the power down */ + _mali_osk_pmm_dev_idle(); +#endif + mali_platform_power_mode_change(power_mode); + } + return err; +} + +_mali_osk_errcode_t malipmm_core_register( mali_pmm_core_id core ) +{ + _mali_osk_errcode_t err; + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + + if( pmm == NULL ) + { + /* PMM state has not been created, this is because the PMU resource has not been + * created yet. + * This probably means that the PMU resource has not been specfied as the first + * resource in the config file + */ + MALI_PRINT_ERROR( ("PMM: Cannot register core %s because the PMU resource has not been\n initialized. Please make sure the PMU resource is the first resource in the\n resource configuration.\n", + pmm_trace_get_core_name(core)) ); + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + MALI_PMM_LOCK(pmm); + +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 1; +#endif /* MALI_STATE_TRACKING */ + + + /* Check if the core is registered more than once in PMM */ + MALI_DEBUG_ASSERT( (pmm->cores_registered & core) == 0 ); + + MALIPMM_DEBUG_PRINT( ("PMM: core registered: (0x%x) %s\n", core, pmm_trace_get_core_name(core)) ); + +#if !MALI_PMM_NO_PMU + /* Make sure the core is powered up */ + err = malipmm_powerup( core ); +#else + err = _MALI_OSK_ERR_OK; +#endif + if( _MALI_OSK_ERR_OK == err ) + { +#if MALI_PMM_TRACE + mali_pmm_core_mask old_power = pmm->cores_powered; +#endif + /* Assume a registered core is now powered up and idle */ + pmm->cores_registered |= core; + pmm->cores_idle |= core; + pmm->cores_powered |= core; + pmm_update_system_state( pmm ); + +#if MALI_PMM_TRACE + _mali_pmm_trace_hardware_change( old_power, pmm->cores_powered ); +#endif + } + else + { + MALI_PRINT_ERROR( ("PMM: Error(%d) powering up registered core: (0x%x) %s\n", + err, core, pmm_trace_get_core_name(core)) ); + } + +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 0; +#endif /* MALI_STATE_TRACKING */ + + MALI_PMM_UNLOCK(pmm); + + return err; +} + +void malipmm_core_unregister( mali_pmm_core_id core ) +{ + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + MALI_DEBUG_ASSERT_POINTER(pmm); + + MALI_PMM_LOCK(pmm); +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 1; +#endif /* MALI_STATE_TRACKING */ + + + /* Check if the core is registered in PMM */ + MALI_PMM_DEBUG_ASSERT_CORES_SUBSET( pmm->cores_registered, core ); + + MALIPMM_DEBUG_PRINT( ("PMM: core unregistered: (0x%x) %s\n", core, pmm_trace_get_core_name(core)) ); + + { +#if MALI_PMM_TRACE + mali_pmm_core_mask old_power = pmm->cores_powered; +#endif + /* Remove the core from the system */ + pmm->cores_idle &= (~core); + pmm->cores_powered &= (~core); + pmm->cores_pend_down &= (~core); + pmm->cores_pend_up &= (~core); + pmm->cores_ack_down &= (~core); + pmm->cores_ack_up &= (~core); + + pmm_update_system_state( pmm ); + +#if MALI_PMM_TRACE + _mali_pmm_trace_hardware_change( old_power, pmm->cores_powered ); +#endif + } + +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 0; +#endif /* MALI_STATE_TRACKING */ + + MALI_PMM_UNLOCK(pmm); +} +void malipmm_core_power_down_okay( mali_pmm_core_id core ) +{ + _mali_uk_pmm_message_s event = { + NULL, + MALI_PMM_EVENT_INTERNAL_POWER_DOWN_ACK, + 0 }; + + event.data = core; + + _mali_ukk_pmm_event_message( &event ); +} + +void malipmm_set_policy_check() +{ + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + MALI_DEBUG_ASSERT_POINTER(pmm); + pmm->check_policy = MALI_TRUE; + + /* To check the policy we need to schedule some work */ + _mali_osk_irq_schedulework( pmm->irq ); +} + +_mali_osk_errcode_t malipmm_irq_uhandler(void *data) +{ + MALIPMM_DEBUG_PRINT( ("PMM: uhandler - not expected to be used\n") ); + + MALI_SUCCESS; +} + +void malipmm_irq_bhandler(void *data) +{ + _mali_pmm_internal_state_t *pmm; + pmm = (_mali_pmm_internal_state_t *)data; + MALI_DEBUG_ASSERT_POINTER(pmm); + +#if PMM_OS_TEST + if( power_test_check() ) return; +#endif + + MALI_PMM_LOCK(pmm); +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 1; +#endif /* MALI_STATE_TRACKING */ + + /* Quick out when we are shutting down */ + if( pmm->status == MALI_PMM_STATUS_OFF ) + { + + #if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 0; + #endif /* MALI_STATE_TRACKING */ + + MALI_PMM_UNLOCK(pmm); + return; + } + + MALIPMM_DEBUG_PRINT( ("PMM: bhandler - Processing event\n") ); + + if( pmm->missed > 0 ) + { + MALI_PRINT_ERROR( ("PMM: Failed to send %d events", pmm->missed) ); + pmm_fatal_reset( pmm ); + } + + if( pmm->check_policy ) + { + pmm->check_policy = MALI_FALSE; + pmm_policy_check_policy(pmm); + } + else + { + /* Perform event processing */ + pmm_event_process(); + if( pmm->fatal_power_err ) + { + /* Try a reset */ + pmm_fatal_reset( pmm ); + } + } + +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 0; +#endif /* MALI_STATE_TRACKING */ + + MALI_PMM_UNLOCK(pmm); +} + +static void pmm_event_process( void ) +{ + _mali_osk_errcode_t err = _MALI_OSK_ERR_OK; + _mali_osk_notification_t *msg = NULL; + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + mali_pmm_message_t *event; + u32 process_messages; + + MALI_DEBUG_ASSERT_POINTER(pmm); + + + /* Max number of messages to process before exiting - as we shouldn't stay + * processing the messages for a long time + */ + process_messages = _mali_osk_atomic_read( &(pmm->messages_queued) ); + + while( process_messages > 0 ) + { + /* Check internal message queue first */ + err = _mali_osk_notification_queue_dequeue( pmm->iqueue, &msg ); + + if( err != _MALI_OSK_ERR_OK ) + { + if( pmm->status == MALI_PMM_STATUS_IDLE || pmm->status == MALI_PMM_STATUS_OS_WAITING || pmm->status == MALI_PMM_STATUS_DVFS_PAUSE) + { + if( pmm->waiting > 0 ) pmm->waiting--; + + /* We aren't busy changing state, so look at real events */ + err = _mali_osk_notification_queue_dequeue( pmm->queue, &msg ); + + if( err != _MALI_OSK_ERR_OK ) + { + pmm->no_events++; + MALIPMM_DEBUG_PRINT( ("PMM: event_process - No message to process\n") ); + /* Nothing to do - so return */ + return; + } + else + { + #if (MALI_PMM_TRACE || MALI_STATE_TRACKING) + pmm->messages_received++; + #endif + } + } + else + { + /* Waiting for an internal message */ + pmm->waiting++; + MALIPMM_DEBUG_PRINT( ("PMM: event_process - Waiting for internal message, messages queued=%d\n", pmm->waiting) ); + return; + } + } + else + { + #if (MALI_PMM_TRACE || MALI_STATE_TRACKING) + pmm->imessages_received++; + #endif + } + + MALI_DEBUG_ASSERT_POINTER( msg ); + /* Check the message type matches */ + MALI_DEBUG_ASSERT( msg->notification_type == MALI_PMM_NOTIFICATION_TYPE ); + + event = msg->result_buffer; + + _mali_osk_atomic_dec( &(pmm->messages_queued) ); + process_messages--; + + #if MALI_PMM_TRACE + /* Trace before we process the event in case we have an error */ + _mali_pmm_trace_event_message( event, MALI_TRUE ); + #endif + err = pmm_policy_process( pmm, event ); + + + if( err != _MALI_OSK_ERR_OK ) + { + MALI_PRINT_ERROR( ("PMM: Error(%d) in policy %d when processing event message with id: %d", + err, pmm->policy, event->id) ); + } + + /* Delete notification */ + _mali_osk_notification_delete ( msg ); + + if( pmm->fatal_power_err ) + { + /* Nothing good has happened - exit */ + return; + } + + + #if MALI_PMM_TRACE + MALI_PRINT( ("PMM Trace: Event processed, msgs (sent/read) = %d/%d, int msgs (sent/read) = %d/%d, no events = %d, waiting = %d\n", + pmm->messages_sent, pmm->messages_received, pmm->imessages_sent, pmm->imessages_received, pmm->no_events, pmm->waiting) ); + #endif + } + + if( pmm->status == MALI_PMM_STATUS_IDLE && pmm->waiting > 0 ) + { + /* For events we ignored whilst we were busy, add a new + * scheduled time to look at them */ + _mali_osk_irq_schedulework( pmm->irq ); + } +} + +#if MALI_STATE_TRACKING +u32 malipmm_subsystem_dump_state(char *buf, u32 size) +{ + int len = 0; + _mali_pmm_internal_state_t *pmm = GET_PMM_STATE_PTR; + + if( !pmm ) + { + len += _mali_osk_snprintf(buf + len, size + len, "PMM: Null state\n"); + } + else + { + len += _mali_osk_snprintf(buf+len, size+len, "Locks:\n PMM lock acquired: %s\n", + pmm->mali_pmm_lock_acquired ? "true" : "false"); + len += _mali_osk_snprintf(buf+len, size+len, + "PMM state:\n Previous status: %s\n Status: %s\n Current event: %s\n Policy: %s\n Check policy: %s\n State: %s\n", + pmm_trace_status[pmm->mali_last_pmm_status], pmm_trace_status[pmm->status], + pmm_trace_events[pmm->mali_new_event_status], pmm_trace_policy[pmm->policy], + pmm->check_policy ? "true" : "false", pmm_trace_state[pmm->state]); + len += _mali_osk_snprintf(buf+len, size+len, + "PMM cores:\n Cores registered: %d\n Cores powered: %d\n Cores idle: %d\n" + " Cores pending down: %d\n Cores pending up: %d\n Cores ack down: %d\n Cores ack up: %d\n", + pmm->cores_registered, pmm->cores_powered, pmm->cores_idle, pmm->cores_pend_down, + pmm->cores_pend_up, pmm->cores_ack_down, pmm->cores_ack_up); + len += _mali_osk_snprintf(buf+len, size+len, "PMM misc:\n PMU init: %s\n Messages queued: %d\n" + " Waiting: %d\n No events: %d\n Missed events: %d\n Fatal power error: %s\n", + pmm->pmu_initialized ? "true" : "false", _mali_osk_atomic_read(&(pmm->messages_queued)), + pmm->waiting, pmm->no_events, pmm->missed, pmm->fatal_power_err ? "true" : "false"); + } + return len; +} +#endif /* MALI_STATE_TRACKING */ + +#endif /* USING_MALI_PMM */ diff --git a/drivers/gpu/arm/mali/common/pmm/mali_pmm.h b/drivers/gpu/arm/mali/common/pmm/mali_pmm.h new file mode 100644 index 00000000000..4a667bbe5d2 --- /dev/null +++ b/drivers/gpu/arm/mali/common/pmm/mali_pmm.h @@ -0,0 +1,348 @@ +/* + * Copyright (C) 2010-2012 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_pmm.h + * Defines the power management module for the kernel device driver + */ + +#ifndef __MALI_PMM_H__ +#define __MALI_PMM_H__ + +/* For mali_pmm_message_data and MALI_PMM_EVENT_UK_* defines */ +#include "mali_uk_types.h" +#include "mali_platform.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @defgroup pmmapi Power Management Module APIs + * + * @{ + */ + +/** OS event tester */ +#define PMM_OS_TEST 0 + +/** @brief Compile option to turn on/off tracing */ +#define MALI_PMM_TRACE 0 +#define MALI_PMM_TRACE_SENT_EVENTS 0 + +/** @brief Compile option to switch between always on or job control PMM policy */ +#define MALI_PMM_ALWAYS_ON 0 + +/** @brief Overrides hardware PMU and uses software simulation instead + * @note This even stops intialization of PMU and cores being powered on at start up + */ +#define MALI_PMM_NO_PMU 0 + +/** @brief PMM debug print to control debug message level */ +#define MALIPMM_DEBUG_PRINT(args) \ + MALI_DEBUG_PRINT(3, args) + + +/** @brief power management event message identifiers. + */ +/* These must match up with the pmm_trace_events & pmm_trace_events_internal + * arrays + */ +typedef enum mali_pmm_event_id +{ + MALI_PMM_EVENT_OS_POWER_UP = 0, /**< OS power up event */ + MALI_PMM_EVENT_OS_POWER_DOWN = 1, /**< OS power down event */ + MALI_PMM_EVENT_JOB_SCHEDULED = 2, /**< Job scheduled to run event */ + MALI_PMM_EVENT_JOB_QUEUED = 3, /**< Job queued (but not run) event */ + MALI_PMM_EVENT_JOB_FINISHED = 4, /**< Job finished event */ + MALI_PMM_EVENT_TIMEOUT = 5, /**< Time out timer has expired */ + MALI_PMM_EVENT_DVFS_PAUSE = 6, /**< Mali device pause event */ + MALI_PMM_EVENT_DVFS_RESUME = 7, /**< Mali device resume event */ + + MALI_PMM_EVENT_UKS = 200, /**< Events from the user-side start here */ + MALI_PMM_EVENT_UK_EXAMPLE = _MALI_PMM_EVENT_UK_EXAMPLE, + + MALI_PMM_EVENT_INTERNALS = 1000, + MALI_PMM_EVENT_INTERNAL_POWER_UP_ACK = 1001, /**< Internal power up acknowledgement */ + MALI_PMM_EVENT_INTERNAL_POWER_DOWN_ACK = 1002, /**< Internal power down acknowledgment */ +} mali_pmm_event_id; + + +/** @brief Use this when the power up/down callbacks do not need any OS data. */ +#define MALI_PMM_NO_OS_DATA 1 + + +/* @brief Geometry and pixel processor identifiers for the PMM + * + * @note these match the ARM Mali 400 PMU hardware definitions, apart from the "SYSTEM" + */ +typedef enum mali_pmm_core_id_tag +{ + MALI_PMM_CORE_SYSTEM = 0x00000000, /**< All of the Mali hardware */ + MALI_PMM_CORE_GP = 0x00000001, /**< Mali GP2 */ + MALI_PMM_CORE_L2 = 0x00000002, /**< Level 2 cache */ + MALI_PMM_CORE_PP0 = 0x00000004, /**< Mali 200 pixel processor 0 */ + MALI_PMM_CORE_PP1 = 0x00000008, /**< Mali 200 pixel processor 1 */ + MALI_PMM_CORE_PP2 = 0x00000010, /**< Mali 200 pixel processor 2 */ + MALI_PMM_CORE_PP3 = 0x00000020, /**< Mali 200 pixel processor 3 */ + MALI_PMM_CORE_PP_ALL = 0x0000003C /**< Mali 200 pixel processors 0-3 */ +} mali_pmm_core_id; + + +/* @brief PMM bitmask of mali_pmm_core_ids + */ +typedef u32 mali_pmm_core_mask; + +/* @brief PMM event timestamp type + */ +typedef u32 mali_pmm_timestamp; + +/** @brief power management event message struct + */ +typedef struct _mali_pmm_message +{ + mali_pmm_event_id id; /**< event id */ + mali_pmm_message_data data; /**< specific data associated with the event */ + mali_pmm_timestamp ts; /**< timestamp the event was placed in the event queue */ +} mali_pmm_message_t; + + + +/** @brief the state of the power management module. + */ +/* These must match up with the pmm_trace_state array */ +typedef enum mali_pmm_state_tag +{ + MALI_PMM_STATE_UNAVAILABLE = 0, /**< PMM is not available */ + MALI_PMM_STATE_SYSTEM_ON = 1, /**< All of the Mali hardware is on */ + MALI_PMM_STATE_SYSTEM_OFF = 2, /**< All of the Mali hardware is off */ + MALI_PMM_STATE_SYSTEM_TRANSITION = 3 /**< System is changing state */ +} mali_pmm_state; + + +/** @brief a power management policy. + */ +/* These must match up with the pmm_trace_policy array */ +typedef enum mali_pmm_policy_tag +{ + MALI_PMM_POLICY_NONE = 0, /**< No policy */ + MALI_PMM_POLICY_ALWAYS_ON = 1, /**< Always on policy */ + MALI_PMM_POLICY_JOB_CONTROL = 2, /**< Job control policy */ + MALI_PMM_POLICY_RUNTIME_JOB_CONTROL = 3 /**< Run time power management control policy */ +} mali_pmm_policy; + +/** @brief Function to power up MALI + * + * @param cores core mask to power up the cores + * + * @return error code if MALI fails to power up + */ +_mali_osk_errcode_t malipmm_powerup( u32 cores ); + +/** @brief Function to power down MALI + * + * @param cores core mask to power down the cores + * @param The power mode to which MALI transitions + * + * @return error code if MALI fails to power down + */ +_mali_osk_errcode_t malipmm_powerdown( u32 cores, mali_power_mode power_mode ); + +/** @brief Function to report to the OS when the power down has finished + * + * @param data The event message data that initiated the power down + */ +void _mali_osk_pmm_power_down_done(mali_pmm_message_data data); + +/** @brief Function to report to the OS when the power up has finished + * + * @param data The event message data that initiated the power up + */ +void _mali_osk_pmm_power_up_done(mali_pmm_message_data data); + +/** @brief Function to report that DVFS operation done + * + * @param data The event message data + */ +void _mali_osk_pmm_dvfs_operation_done(mali_pmm_message_data data); + +#if MALI_POWER_MGMT_TEST_SUITE +/** @brief Function to notify power management events + * + * @param data The event message data + */ +void _mali_osk_pmm_policy_events_notifications(mali_pmm_event_id event_id); + +#endif + +/** @brief Function to power up MALI + * + * @note powers up the MALI during MALI device driver is unloaded + */ +void malipmm_force_powerup( void ); + +/** @brief Function to report the OS that device is idle + * + * @note inform the OS that device is idle + */ +_mali_osk_errcode_t _mali_osk_pmm_dev_idle( void ); + +/** @brief Function to report the OS to activate device + * + * @note inform the os that device needs to be activated + */ +void _mali_osk_pmm_dev_activate( void ); + +/** @brief Function to report OS PMM for cleanup + * + * @note Function to report OS PMM for cleanup + */ +void _mali_osk_pmm_ospmm_cleanup( void ); + +/** @brief Queries the current state of the PMM software + * + * @note the state of the PMM can change after this call has returned + * + * @return the current PMM state value + */ +mali_pmm_state _mali_pmm_state( void ); + +/** @brief List of cores that are registered with the PMM + * + * This will return the cores that have been currently registered with the PMM, + * which is a bitwise OR of the mali_pmm_core_id_tags. A value of 0x0 means that + * there are no cores registered. + * + * @note the list of cores can change after this call has returned + * + * @return a bit mask representing all the cores that have been registered with the PMM + */ +mali_pmm_core_mask _mali_pmm_cores_list( void ); + +/** @brief List of cores that are powered up in the PMM + * + * This will return the subset of the cores that can be listed using mali_pmm_cores_ + * list, that have power. It is a bitwise OR of the mali_pmm_core_id_tags. A value of + * 0x0 means that none of the cores registered are powered. + * + * @note the list of cores can change after this call has returned + * + * @return a bit mask representing all the cores that are powered up + */ +mali_pmm_core_mask _mali_pmm_cores_powered( void ); + + +/** @brief List of power management policies that are supported by the PMM + * + * Given an empty array of policies - policy_list - which contains the number + * of entries as specified by - policy_list_size, this function will populate + * the list with the available policies. If the policy_list is too small for + * all the policies then only policy_list_size entries will be returned. If the + * policy_list is bigger than the number of available policies then, the extra + * entries will be set to MALI_PMM_POLICY_NONE. + * The function will also update available_policies with the number of policies + * that are available, even if it exceeds the policy_list_size. + * The function will succeed if all policies could be returned, else it will + * fail if none or only a subset of policies could be returned. + * The function will also fail if no policy_list is supplied, though + * available_policies is optional. + * + * @note this is a STUB function and is not yet implemented + * + * @param policy_list_size is the number of policies that can be returned in + * the policy_list argument + * @param policy_list is an array of policies that should be populated with + * the list of policies that are supported by the PMM + * @param policies_available optional argument, if non-NULL will be set to the + * number of policies available + * @return _MALI_OSK_ERR_OK if the policies could be listed, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t _mali_pmm_list_policies( + u32 policy_list_size, + mali_pmm_policy *policy_list, + u32 *policies_available ); + +/** @brief Set the power management policy in the PMM + * + * Given a valid supported policy, this function will change the PMM to use + * this new policy + * The function will fail if the policy given is invalid or unsupported. + * + * @note this is a STUB function and is not yet implemented + * + * @param policy the new policy to be set + * @return _MALI_OSK_ERR_OK if the policy could be set, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t _mali_pmm_set_policy( mali_pmm_policy policy ); + +/** @brief Get the current power management policy in the PMM + * + * Given a pointer to a policy data type, this function will return the current + * policy that is in effect for the PMM. This maybe out of date if there is a + * pending set policy call that has not been serviced. + * The function will fail if the policy given is NULL. + * + * @note the policy of the PMM can change after this call has returned + * + * @param policy a pointer to a policy that can be updated to the current + * policy + * @return _MALI_OSK_ERR_OK if the policy could be returned, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t _mali_pmm_get_policy( mali_pmm_policy *policy ); + +#if MALI_PMM_TRACE + +/** @brief Indicates when a hardware state change occurs in the PMM + * + * @param old a mask of the cores indicating the previous state of the cores + * @param newstate a mask of the cores indicating the new current state of the cores + */ +void _mali_pmm_trace_hardware_change( mali_pmm_core_mask old, mali_pmm_core_mask newstate ); + +/** @brief Indicates when a state change occurs in the PMM + * + * @param old the previous state for the PMM + * @param newstate the new current state of the PMM + */ +void _mali_pmm_trace_state_change( mali_pmm_state old, mali_pmm_state newstate ); + +/** @brief Indicates when a policy change occurs in the PMM + * + * @param old the previous policy for the PMM + * @param newpolicy the new current policy of the PMM + */ +void _mali_pmm_trace_policy_change( mali_pmm_policy old, mali_pmm_policy newpolicy ); + +/** @brief Records when an event message is read by the event system + * + * @param event the message details + * @param received MALI_TRUE when the message is received by the PMM, else it is being sent + */ +void _mali_pmm_trace_event_message( mali_pmm_message_t *event, mali_bool received ); + +#endif /* MALI_PMM_TRACE */ + +/** @brief Dumps the current state of OS PMM thread + */ +#if MALI_STATE_TRACKING +u32 mali_pmm_dump_os_thread_state( char *buf, u32 size ); +#endif /* MALI_STATE_TRACKING */ + +/** @} */ /* end group pmmapi */ + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_PMM_H__ */ diff --git a/drivers/gpu/arm/mali/common/pmm/mali_pmm_pmu.c b/drivers/gpu/arm/mali/common/pmm/mali_pmm_pmu.c new file mode 100644 index 00000000000..da8957724e8 --- /dev/null +++ b/drivers/gpu/arm/mali/common/pmm/mali_pmm_pmu.c @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2010-2011 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_pmm_pmu.c + * Mali driver functions for Mali 400 PMU hardware + */ +#include "mali_kernel_common.h" +#include "mali_osk.h" +#include "mali_platform.h" + +#if USING_MALI_PMU +#if USING_MALI_PMM + +#include "mali_pmm.h" + +/* Internal test on/off */ +#define PMU_TEST 0 + +#if MALI_POWER_MGMT_TEST_SUITE +#include "mali_platform_pmu_internal_testing.h" +#endif /* MALI_POWER_MGMT_TEST_SUITE */ + +/** @brief PMU hardware info + */ +typedef struct platform_pmu +{ + u32 reg_base_addr; /**< PMU registers base address */ + u32 reg_size; /**< PMU registers size */ + const char *name; /**< PMU name */ + u32 irq_num; /**< PMU irq number */ + + mali_io_address reg_mapped; /**< IO-mapped pointer to registers */ +} platform_pmu_t; + +static platform_pmu_t *pmu_info = NULL; + +/** @brief Register layout for hardware PMU + */ +typedef enum { + PMU_REG_ADDR_MGMT_POWER_UP = 0x00, /*< Power up register */ + PMU_REG_ADDR_MGMT_POWER_DOWN = 0x04, /*< Power down register */ + PMU_REG_ADDR_MGMT_STATUS = 0x08, /*< Core sleep status register */ + PMU_REG_ADDR_MGMT_INT_MASK = 0x0C, /*< Interrupt mask register */ + PMU_REG_ADDR_MGMT_INT_RAWSTAT = 0x10, /*< Interrupt raw status register */ + PMU_REG_ADDR_MGMT_INT_STAT = 0x14, /*< Interrupt status register */ + PMU_REG_ADDR_MGMT_INT_CLEAR = 0x18, /*< Interrupt clear register */ + PMU_REG_ADDR_MGMT_SW_DELAY = 0x1C, /*< Software delay register */ + PMU_REG_ADDR_MGMT_MASTER_PWR_UP = 0x24, /*< Master power up register */ + PMU_REGISTER_ADDRESS_SPACE_SIZE = 0x28, /*< Size of register space */ +} pmu_reg_addr_mgmt_addr; + +/* Internal functions */ +static u32 pmu_reg_read(platform_pmu_t *pmu, u32 relative_address); +static void pmu_reg_write(platform_pmu_t *pmu, u32 relative_address, u32 new_val); +static mali_pmm_core_mask pmu_translate_cores_to_pmu(mali_pmm_core_mask cores); +#if PMU_TEST +static void pmm_pmu_dump_regs( platform_pmu_t *pmu ); +static pmm_pmu_test( platform_pmu_t *pmu, u32 cores ); +#endif + +_mali_osk_errcode_t mali_pmm_pmu_init(_mali_osk_resource_t *resource) +{ + + if( resource->type == PMU ) + { + if( (resource->base == 0) || + (resource->description == NULL) ) + { + /* NOTE: We currently don't care about any other resource settings */ + MALI_PRINT_ERROR(("PLATFORM mali400-pmu: Missing PMU set up information\n")); + MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + } + pmu_info = (platform_pmu_t *)_mali_osk_malloc(sizeof(*pmu_info)); + MALI_CHECK_NON_NULL( pmu_info, _MALI_OSK_ERR_NOMEM ); + + /* All values get 0 as default */ + _mali_osk_memset(pmu_info, 0, sizeof(*pmu_info)); + + pmu_info->reg_base_addr = resource->base; + pmu_info->reg_size = (u32)PMU_REGISTER_ADDRESS_SPACE_SIZE; + pmu_info->name = resource->description; + pmu_info->irq_num = resource->irq; + + if( _MALI_OSK_ERR_OK != _mali_osk_mem_reqregion(pmu_info->reg_base_addr, pmu_info->reg_size, pmu_info->name) ) + { + MALI_PRINT_ERROR(("PLATFORM mali400-pmu: Could not request register region (0x%08X - 0x%08X) for %s\n", + pmu_info->reg_base_addr, pmu_info->reg_base_addr + pmu_info->reg_size - 1, pmu_info->name)); + goto cleanup; + } + else + { + MALI_DEBUG_PRINT( 4, ("PLATFORM mali400-pmu: Success: request_mem_region: (0x%08X - 0x%08X) for %s\n", + pmu_info->reg_base_addr, pmu_info->reg_base_addr + pmu_info->reg_size - 1, pmu_info->name)); + } + + pmu_info->reg_mapped = _mali_osk_mem_mapioregion( pmu_info->reg_base_addr, pmu_info->reg_size, pmu_info->name ); + + if( 0 == pmu_info->reg_mapped ) + { + MALI_PRINT_ERROR(("PLATFORM mali400-pmu: Could not ioremap registers for %s .\n", pmu_info->name)); + _mali_osk_mem_unreqregion( pmu_info->reg_base_addr, pmu_info->reg_size ); + goto cleanup; + } + else + { + MALI_DEBUG_PRINT( 4, ("PLATFORM mali400-pmu: Success: ioremap_nocache: Internal ptr: (0x%08X - 0x%08X) for %s\n", + (u32) pmu_info->reg_mapped, + ((u32)pmu_info->reg_mapped)+ pmu_info->reg_size - 1, + pmu_info->name)); + } + + MALI_DEBUG_PRINT( 4, ("PLATFORM mali400-pmu: Success: Mapping registers to %s\n", pmu_info->name)); + +#if PMU_TEST + pmu_test(pmu_info, (MALI_PMM_CORE_GP)); + pmu_test(pmu_info, (MALI_PMM_CORE_GP|MALI_PMM_CORE_L2|MALI_PMM_CORE_PP0)); +#endif + + MALI_DEBUG_PRINT( 4, ("PLATFORM mali400-pmu: Initialized - %s\n", pmu_info->name) ); + } + else + { + /* Didn't expect a different resource */ + MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + } + + MALI_SUCCESS; + +cleanup: + _mali_osk_free(pmu_info); + pmu_info = NULL; + MALI_ERROR(_MALI_OSK_ERR_NOMEM); +} + +_mali_osk_errcode_t mali_pmm_pmu_deinit(_mali_osk_resource_type_t *type) +{ + if (*type == PMU) + { + if( pmu_info ) + { + _mali_osk_mem_unmapioregion(pmu_info->reg_base_addr, pmu_info->reg_size, pmu_info->reg_mapped); + _mali_osk_mem_unreqregion(pmu_info->reg_base_addr, pmu_info->reg_size); + _mali_osk_free(pmu_info); + pmu_info = NULL; + MALI_DEBUG_PRINT( 4, ("PLATFORM mali400-pmu: Terminated PMU\n") ); + } + } + else + { + /* Didn't expect a different resource */ + MALI_ERROR(_MALI_OSK_ERR_INVALID_ARGS); + } + + MALI_SUCCESS; + +} + +_mali_osk_errcode_t mali_pmm_pmu_powerdown(u32 cores) +{ + u32 stat; + u32 timeout; + u32 cores_pmu; + + MALI_DEBUG_ASSERT_POINTER(pmu_info); + MALI_DEBUG_ASSERT( cores != 0 ); /* Shouldn't receive zero from PMM */ + MALI_DEBUG_PRINT( 4, ("PLATFORM mali400-pmu: power down (0x%x)\n", cores) ); + + cores_pmu = pmu_translate_cores_to_pmu(cores); + pmu_reg_write( pmu_info, (u32)PMU_REG_ADDR_MGMT_POWER_DOWN, cores_pmu ); + + /* Wait for cores to be powered down */ + timeout = 10; /* 10ms */ + do + { + /* Get status of sleeping cores */ + stat = pmu_reg_read( pmu_info, (u32)PMU_REG_ADDR_MGMT_STATUS ); + stat &= cores_pmu; + if( stat == cores_pmu ) break; /* All cores we wanted are now asleep */ + _mali_osk_time_ubusydelay(1000); /* 1ms */ + timeout--; + } while( timeout > 0 ); + + if( timeout == 0 ) MALI_ERROR(_MALI_OSK_ERR_TIMEOUT); + + MALI_SUCCESS; +} + +_mali_osk_errcode_t mali_pmm_pmu_powerup(u32 cores) +{ + u32 cores_pmu; + u32 stat; + u32 timeout; + + MALI_DEBUG_ASSERT_POINTER(pmu_info); + MALI_DEBUG_ASSERT( cores != 0 ); /* Shouldn't receive zero from PMM */ + MALI_DEBUG_PRINT( 4, ("PLATFORM mali400-pmu: power up (0x%x)\n", cores) ); + + /* Don't use interrupts - just poll status */ + pmu_reg_write( pmu_info, (u32)PMU_REG_ADDR_MGMT_INT_MASK, 0 ); + cores_pmu = pmu_translate_cores_to_pmu(cores); + pmu_reg_write( pmu_info, (u32)PMU_REG_ADDR_MGMT_POWER_UP, cores_pmu ); + + timeout = 10; /* 10ms */ + do + { + /* Get status of sleeping cores */ + stat = pmu_reg_read( pmu_info, (u32)PMU_REG_ADDR_MGMT_STATUS ); + stat &= cores_pmu; + if( stat == 0 ) break; /* All cores we wanted are now awake */ + _mali_osk_time_ubusydelay(1000); /* 1ms */ + timeout--; + } while( timeout > 0 ); + + if( timeout == 0 ) MALI_ERROR(_MALI_OSK_ERR_TIMEOUT); + + MALI_SUCCESS; +} + + +/***** INTERNAL *****/ + +/** @brief Internal PMU function to translate the cores bit mask + * into something the hardware PMU understands + * + * @param cores PMM cores bitmask + * @return PMU hardware cores bitmask + */ +static u32 pmu_translate_cores_to_pmu(mali_pmm_core_mask cores) +{ + /* For Mali 400 PMU the cores mask is already the same as what + * the hardware PMU expects. + * For other hardware, some translation can be done here, by + * translating the MALI_PMM_CORE_* bits into specific hardware + * bits + */ + return cores; +} + +/** @brief Internal PMU function to read a PMU register + * + * @param pmu handle that identifies the PMU hardware + * @param relative_address relative PMU hardware address to read from + * @return 32-bit value that was read from the address + */ +static u32 pmu_reg_read(platform_pmu_t *pmu, u32 relative_address) +{ + u32 read_val; + + MALI_DEBUG_ASSERT_POINTER(pmu); + MALI_DEBUG_ASSERT((relative_address & 0x03) == 0); + MALI_DEBUG_ASSERT(relative_address < pmu->reg_size); + + read_val = _mali_osk_mem_ioread32(pmu->reg_mapped, relative_address); + + MALI_DEBUG_PRINT( 5, ("PMU: reg_read: %s Addr:0x%04X Val:0x%08x\n", + pmu->name, relative_address, read_val)); + + return read_val; +} + +/** @brief Internal PMU function to write to a PMU register + * + * @param pmu handle that identifies the PMU hardware + * @param relative_address relative PMU hardware address to write to + * @param new_val new 32-bit value to write into the address + */ +static void pmu_reg_write(platform_pmu_t *pmu, u32 relative_address, u32 new_val) +{ + MALI_DEBUG_ASSERT_POINTER(pmu); + MALI_DEBUG_ASSERT((relative_address & 0x03) == 0); + MALI_DEBUG_ASSERT(relative_address < pmu->reg_size); + + MALI_DEBUG_PRINT( 5, ("PMU: reg_write: %s Addr:0x%04X Val:0x%08x\n", + pmu->name, relative_address, new_val)); + + _mali_osk_mem_iowrite32(pmu->reg_mapped, relative_address, new_val); +} + +#if PMU_TEST + +/***** TEST *****/ + +static void pmu_dump_regs( platform_pmu_t *pmu ) +{ + u32 addr; + for( addr = 0x0; addr < PMU_REGISTER_ADDRESS_SPACE_SIZE; addr += 0x4 ) + { + MALI_PRINT( ("PMU_REG: 0x%08x: 0x%04x\n", (addr + pmu->reg_base_addr), pmu_reg_read( pmu, addr ) ) ); + } +} + +/* This function is an internal test for the PMU without any Mali h/w interaction */ +static void pmu_test( platform_pmu_t *pmu, u32 cores ) +{ + u32 stat; + u32 timeout; + + MALI_PRINT( ("PMU_TEST: Start\n") ); + + pmu_dump_regs( pmu ); + + MALI_PRINT( ("PMU_TEST: Power down cores: 0x%x\n", cores) ); + _mali_pmm_pmu_power_down( pmu, cores, MALI_TRUE ); + + stat = pmu_reg_read( pmu, (u32)PMU_REG_ADDR_MGMT_STATUS ); + MALI_PRINT( ("PMU_TEST: %s\n", (stat & cores) == cores ? "SUCCESS" : "FAIL" ) ); + + pmu_dump_regs( pmu ); + + MALI_PRINT( ("PMU_TEST: Power up cores: 0x%x\n", cores) ); + _mali_pmm_pmu_power_up( pmu, cores, MALI_FALSE ); + + MALI_PRINT( ("PMU_TEST: Waiting for power up...\n") ); + timeout = 1000; /* 1 sec */ + while( !_mali_pmm_pmu_irq_power_up(pmu) && timeout > 0 ) + { + _mali_osk_time_ubusydelay(1000); /* 1ms */ + timeout--; + } + + MALI_PRINT( ("PMU_TEST: Waited %dms for interrupt\n", (1000-timeout)) ); + stat = pmu_reg_read( pmu, (u32)PMU_REG_ADDR_MGMT_STATUS ); + MALI_PRINT( ("PMU_TEST: %s\n", (stat & cores) == 0 ? "SUCCESS" : "FAIL" ) ); + + _mali_pmm_pmu_irq_power_up_clear(pmu); + + pmu_dump_regs( pmu ); + + MALI_PRINT( ("PMU_TEST: Finish\n") ); +} +#endif /* PMU_TEST */ + +#if MALI_POWER_MGMT_TEST_SUITE + +u32 pmu_get_power_up_down_info(void) +{ + return pmu_reg_read(pmu_info, (u32)PMU_REG_ADDR_MGMT_STATUS); +} + +#endif /* MALI_POWER_MGMT_TEST_SUITE */ +#endif /* USING_MALI_PMM */ +#endif /* USING_MALI_PMU */ diff --git a/drivers/gpu/arm/mali/common/pmm/mali_pmm_pmu.h b/drivers/gpu/arm/mali/common/pmm/mali_pmm_pmu.h new file mode 100644 index 00000000000..112074b2010 --- /dev/null +++ b/drivers/gpu/arm/mali/common/pmm/mali_pmm_pmu.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2010-2011 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_platform.h + * Platform specific Mali driver functions + */ + +#include "mali_osk.h" + +#if !USING_MALI_PMM +/* @brief System power up/down cores that can be passed into mali_platform_powerdown/up() */ +#define MALI_PLATFORM_SYSTEM 0 +#endif + +#if USING_MALI_PMM +#if USING_MALI_PMU +#include "mali_pmm.h" + +/** @brief Platform specific setup and initialisation of MALI + * + * This is called from the entrypoint of the driver to initialize the platform + * When using PMM, it is also called from the PMM start up to initialise the + * system PMU + * + * @param resource This is NULL when called on first driver start up, else it will + * be a pointer to a PMU resource + * @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error. + */ +_mali_osk_errcode_t mali_pmm_pmu_init(_mali_osk_resource_t *resource); + +/** @brief Platform specific deinitialisation of MALI + * + * This is called on the exit of the driver to terminate the platform + * When using PMM, it is also called from the PMM termination code to clean up the + * system PMU + * + * @param type This is NULL when called on driver exit, else it will + * be a pointer to a PMU resource type (not the full resource) + * @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error. + */ +_mali_osk_errcode_t mali_pmm_pmu_deinit(_mali_osk_resource_type_t *type); + +/** @brief Platform specific powerdown sequence of MALI + * + * Called as part of platform init if there is no PMM support, else the + * PMM will call it. + * + * @param cores This is MALI_PLATFORM_SYSTEM when called without PMM, else it will + * be a mask of cores to power down based on the mali_pmm_core_id enum + * @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error. + */ +_mali_osk_errcode_t mali_pmm_pmu_powerdown(u32 cores); + +/** @brief Platform specific powerup sequence of MALI + * + * Called as part of platform deinit if there is no PMM support, else the + * PMM will call it. + * + * @param cores This is MALI_PLATFORM_SYSTEM when called without PMM, else it will + * be a mask of cores to power down based on the mali_pmm_core_id enum + * @return _MALI_OSK_ERR_OK on success otherwise, a suitable _mali_osk_errcode_t error. + */ +_mali_osk_errcode_t mali_pmm_pmu_powerup(u32 cores); + +#if MALI_POWER_MGMT_TEST_SUITE +#if USING_MALI_PMM +#if USING_MALI_PMU +/** @brief function to get status of individual cores + * + * This function is used by power management test suite to get the status of powered up/down the number + * of cores + * @param utilization The workload utilization of the Mali GPU. 0 = no utilization, 256 = full utilization. + */ +u32 pmu_get_power_up_down_info(void); +#endif +#endif +#endif +#endif +#endif diff --git a/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy.c b/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy.c new file mode 100644 index 00000000000..8958ee8bd8d --- /dev/null +++ b/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy.c @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2010-2012 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_pmm_policy.c + * Implementation of the common routines for power management module + * policies + */ + +#if USING_MALI_PMM + +#include "mali_ukk.h" +#include "mali_kernel_common.h" + +#include "mali_pmm.h" +#include "mali_pmm_system.h" +#include "mali_pmm_state.h" +#include "mali_pmm_policy.h" + +#include "mali_pmm_policy_alwayson.h" +#include "mali_pmm_policy_jobcontrol.h" + +/* Call back function for timer expiration */ +static void pmm_policy_timer_callback( void *arg ); + +_mali_osk_errcode_t pmm_policy_timer_init( _pmm_policy_timer_t *pptimer, u32 timeout, mali_pmm_event_id id ) +{ + MALI_DEBUG_ASSERT_POINTER(pptimer); + + /* All values get 0 as default */ + _mali_osk_memset(pptimer, 0, sizeof(*pptimer)); + + pptimer->timer = _mali_osk_timer_init(); + if( pptimer->timer ) + { + _mali_osk_timer_setcallback( pptimer->timer, pmm_policy_timer_callback, (void *)pptimer ); + pptimer->timeout = timeout; + pptimer->event_id = id; + MALI_SUCCESS; + } + + return _MALI_OSK_ERR_FAULT; +} + +static void pmm_policy_timer_callback( void *arg ) +{ + _pmm_policy_timer_t *pptimer = (_pmm_policy_timer_t *)arg; + + MALI_DEBUG_ASSERT_POINTER(pptimer); + MALI_DEBUG_ASSERT( pptimer->set ); + + /* Set timer expired and flag there is a policy to check */ + pptimer->expired = MALI_TRUE; + malipmm_set_policy_check(); +} + + +void pmm_policy_timer_term( _pmm_policy_timer_t *pptimer ) +{ + MALI_DEBUG_ASSERT_POINTER(pptimer); + + _mali_osk_timer_del( pptimer->timer ); + _mali_osk_timer_term( pptimer->timer ); + pptimer->timer = NULL; +} + +mali_bool pmm_policy_timer_start( _pmm_policy_timer_t *pptimer ) +{ + MALI_DEBUG_ASSERT_POINTER(pptimer); + MALI_DEBUG_ASSERT_POINTER(pptimer->timer); + + if( !(pptimer->set) ) + { + pptimer->set = MALI_TRUE; + pptimer->expired = MALI_FALSE; + pptimer->start = _mali_osk_time_tickcount(); + _mali_osk_timer_add( pptimer->timer, pptimer->timeout ); + return MALI_TRUE; + } + + return MALI_FALSE; +} + +mali_bool pmm_policy_timer_stop( _pmm_policy_timer_t *pptimer ) +{ + MALI_DEBUG_ASSERT_POINTER(pptimer); + MALI_DEBUG_ASSERT_POINTER(pptimer->timer); + + if( pptimer->set ) + { + _mali_osk_timer_del( pptimer->timer ); + pptimer->set = MALI_FALSE; + pptimer->expired = MALI_FALSE; + return MALI_TRUE; + } + + return MALI_FALSE; +} + +mali_bool pmm_policy_timer_raise_event( _pmm_policy_timer_t *pptimer ) +{ + MALI_DEBUG_ASSERT_POINTER(pptimer); + + if( pptimer->expired ) + { + _mali_uk_pmm_message_s event = { + NULL, + MALI_PMM_EVENT_TIMEOUT, /* Assume timeout id, but set it below */ + 0 }; + + event.id = pptimer->event_id; + event.data = (mali_pmm_message_data)pptimer->start; + + /* Don't need to do any other notification with this timer */ + pptimer->expired = MALI_FALSE; + /* Unset timer so it is free to be set again */ + pptimer->set = MALI_FALSE; + + _mali_ukk_pmm_event_message( &event ); + + return MALI_TRUE; + } + + return MALI_FALSE; +} + +mali_bool pmm_policy_timer_valid( u32 timer_start, u32 other_start ) +{ + return (_mali_osk_time_after( other_start, timer_start ) == 0); +} + + +_mali_osk_errcode_t pmm_policy_init(_mali_pmm_internal_state_t *pmm) +{ + _mali_osk_errcode_t err; + + MALI_DEBUG_ASSERT_POINTER(pmm); + + switch( pmm->policy ) + { + case MALI_PMM_POLICY_ALWAYS_ON: + { + err = pmm_policy_init_always_on(); + } + break; + + case MALI_PMM_POLICY_JOB_CONTROL: + { + err = pmm_policy_init_job_control(pmm); + } + break; + + case MALI_PMM_POLICY_NONE: + default: + err = _MALI_OSK_ERR_FAULT; + } + + return err; +} + +void pmm_policy_term(_mali_pmm_internal_state_t *pmm) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + + switch( pmm->policy ) + { + case MALI_PMM_POLICY_ALWAYS_ON: + { + pmm_policy_term_always_on(); + } + break; + + case MALI_PMM_POLICY_JOB_CONTROL: + { + pmm_policy_term_job_control(); + } + break; + + case MALI_PMM_POLICY_NONE: + default: + MALI_PRINT_ERROR( ("PMM: Invalid policy terminated %d\n", pmm->policy) ); + } +} + + +_mali_osk_errcode_t pmm_policy_process(_mali_pmm_internal_state_t *pmm, mali_pmm_message_t *event) +{ + _mali_osk_errcode_t err; + + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_DEBUG_ASSERT_POINTER(event); + + switch( pmm->policy ) + { + case MALI_PMM_POLICY_ALWAYS_ON: + { + err = pmm_policy_process_always_on( pmm, event ); + } + break; + + case MALI_PMM_POLICY_JOB_CONTROL: + { + err = pmm_policy_process_job_control( pmm, event ); + } + break; + + case MALI_PMM_POLICY_NONE: + default: + err = _MALI_OSK_ERR_FAULT; + } + + return err; +} + + +void pmm_policy_check_policy( _mali_pmm_internal_state_t *pmm ) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + + switch( pmm->policy ) + { + case MALI_PMM_POLICY_JOB_CONTROL: + { + pmm_policy_check_job_control(); + } + break; + + default: + /* Nothing needs to be done */ + break; + } +} + + +#endif /* USING_MALI_PMM */ + diff --git a/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy.h b/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy.h new file mode 100644 index 00000000000..b3598fab158 --- /dev/null +++ b/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy.h @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2010-2012 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_pmm_policy.h + * Defines the power management module policies + */ + +#ifndef __MALI_PMM_POLICY_H__ +#define __MALI_PMM_POLICY_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @addtogroup pmmapi Power Management Module APIs + * + * @{ + * + * @defgroup pmmapi_policy Power Management Module Policies + * + * @{ + */ + +/** @brief Generic timer for use with policies + */ +typedef struct _pmm_policy_timer +{ + u32 timeout; /**< Timeout for this timer in ticks */ + mali_pmm_event_id event_id; /**< Event id that will be raised when timer expires */ + _mali_osk_timer_t *timer; /**< Timer */ + mali_bool set; /**< Timer set */ + mali_bool expired; /**< Timer expired - event needs to be raised */ + u32 start; /**< Timer start ticks */ +} _pmm_policy_timer_t; + +/** @brief Policy timer initialization + * + * This will create a timer for use in policies, but won't start it + * + * @param pptimer An empty timer structure to be initialized + * @param timeout Timeout in ticks for the timer + * @param id Event id that will be raised on timeout + * @return _MALI_OSK_ERR_OK if the policy could be initialized, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t pmm_policy_timer_init( _pmm_policy_timer_t *pptimer, u32 timeout, mali_pmm_event_id id ); + +/** @brief Policy timer termination + * + * This will clean up a timer that was previously used in policies, it + * will also stop it if started + * + * @param pptimer An initialized timer structure to be terminated + */ +void pmm_policy_timer_term( _pmm_policy_timer_t *pptimer ); + +/** @brief Policy timer start + * + * This will start a previously created timer for use in policies + * When the timer expires after the initialized timeout it will raise + * a PMM event of the event id given on initialization + * As data for the event it will pass the start time of the timer + * + * @param pptimer A previously initialized policy timer + * @return MALI_TRUE if the timer was started, MALI_FALSE if it is already started + */ +mali_bool pmm_policy_timer_start( _pmm_policy_timer_t *pptimer ); + +/** @brief Policy timer stop + * + * This will stop a previously created timer for use in policies + * + * @param pptimer A previously started policy timer + * @return MALI_TRUE if the timer was stopped, MALI_FALSE if it is already stopped + */ +mali_bool pmm_policy_timer_stop( _pmm_policy_timer_t *pptimer ); + +/** @brief Policy timer stop + * + * This raise an event for an expired timer + * + * @param pptimer An expired policy timer + * @return MALI_TRUE if an event was raised, else MALI_FALSE + */ +mali_bool pmm_policy_timer_raise_event( _pmm_policy_timer_t *pptimer ); + +/** @brief Policy timer valid checker + * + * This will check that a timer was started after a given time + * + * @param timer_start Time the timer was started + * @param other_start Time when another event or action occurred + * @return MALI_TRUE if the timer was started after the other time, else MALI_FALSE + */ +mali_bool pmm_policy_timer_valid( u32 timer_start, u32 other_start ); + + +/** @brief Common policy initialization + * + * This will initialize the current policy + * + * @note Any previously initialized policy should be terminated first + * + * @return _MALI_OSK_ERR_OK if the policy could be initialized, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t pmm_policy_init( _mali_pmm_internal_state_t *pmm ); + +/** @brief Common policy termination + * + * This will terminate the current policy. + * @note This can be called when a policy has not been initialized + */ +void pmm_policy_term( _mali_pmm_internal_state_t *pmm ); + +/** @brief Common policy state changer + * + * Given the next available event message, this routine passes it to + * the current policy for processing + * + * @param pmm internal PMM state + * @param event PMM event to process + * @return _MALI_OSK_ERR_OK if the policy state completed okay, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t pmm_policy_process( _mali_pmm_internal_state_t *pmm, mali_pmm_message_t *event ); + + +/** @brief Common policy checker + * + * If a policy timer fires then this function will be called to + * allow the policy to take the correct action + * + * @param pmm internal PMM state + */ +void pmm_policy_check_policy( _mali_pmm_internal_state_t *pmm ); + +/** @} */ /* End group pmmapi_policy */ +/** @} */ /* End group pmmapi */ + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_PMM_POLICY_H__ */ diff --git a/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy_alwayson.c b/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy_alwayson.c new file mode 100644 index 00000000000..bca5f8a8b6c --- /dev/null +++ b/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy_alwayson.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2010-2012 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_pmm_policy_alwayson.c + * Implementation of the power management module policy - always on + */ + +#if USING_MALI_PMM + +#include "mali_ukk.h" +#include "mali_kernel_common.h" + +#include "mali_pmm.h" +#include "mali_pmm_system.h" +#include "mali_pmm_state.h" +#include "mali_pmm_policy_alwayson.h" + +_mali_osk_errcode_t pmm_policy_init_always_on(void) +{ + /* Nothing to set up */ + MALI_SUCCESS; +} + +void pmm_policy_term_always_on(void) +{ + /* Nothing to tear down */ +} + +_mali_osk_errcode_t pmm_policy_process_always_on( _mali_pmm_internal_state_t *pmm, mali_pmm_message_t *event ) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_DEBUG_ASSERT_POINTER(event); + + switch( event->id ) + { + case MALI_PMM_EVENT_OS_POWER_DOWN: + /* We aren't going to do anything, but signal so we don't block the OS + * NOTE: This may adversely affect any jobs Mali is currently running + */ + _mali_osk_pmm_power_down_done( event->data ); + break; + + case MALI_PMM_EVENT_INTERNAL_POWER_UP_ACK: + case MALI_PMM_EVENT_INTERNAL_POWER_DOWN_ACK: + /* Not expected in this policy */ + MALI_DEBUG_ASSERT( MALI_FALSE ); + break; + + case MALI_PMM_EVENT_OS_POWER_UP: + /* Nothing to do */ + _mali_osk_pmm_power_up_done( event->data ); + break; + + case MALI_PMM_EVENT_JOB_SCHEDULED: + case MALI_PMM_EVENT_JOB_QUEUED: + case MALI_PMM_EVENT_JOB_FINISHED: + /* Nothing to do - we are always on */ + break; + + case MALI_PMM_EVENT_TIMEOUT: + /* Not expected in this policy */ + MALI_DEBUG_ASSERT( MALI_FALSE ); + break; + + default: + MALI_ERROR(_MALI_OSK_ERR_ITEM_NOT_FOUND); + } + + MALI_SUCCESS; +} + +#endif diff --git a/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy_alwayson.h b/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy_alwayson.h new file mode 100644 index 00000000000..9dc78a46e8a --- /dev/null +++ b/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy_alwayson.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2010, 2012 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_pmm_policy_alwayson.h + * Defines the power management module policy for always on + */ + +#ifndef __MALI_PMM_POLICY_ALWAYSON_H__ +#define __MALI_PMM_POLICY_ALWAYSON_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @addtogroup pmmapi_policy Power Management Module Policies + * + * @{ + */ + +/** @brief Always on policy initialization + * + * @return _MALI_OSK_ERR_OK if the policy could be initialized, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t pmm_policy_init_always_on(void); + +/** @brief Always on policy termination + */ +void pmm_policy_term_always_on(void); + +/** @brief Always on policy state changer + * + * Given the next available event message, this routine processes it + * for the policy and changes state as needed. + * + * Always on policy will ignore all events and keep the Mali cores on + * all the time + * + * @param pmm internal PMM state + * @param event PMM event to process + * @return _MALI_OSK_ERR_OK if the policy state completed okay, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t pmm_policy_process_always_on( _mali_pmm_internal_state_t *pmm, mali_pmm_message_t *event ); + +/** @} */ /* End group pmmapi_policies */ + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_PMM_POLICY_ALWAYSON_H__ */ diff --git a/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy_jobcontrol.c b/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy_jobcontrol.c new file mode 100644 index 00000000000..3a194f9672e --- /dev/null +++ b/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy_jobcontrol.c @@ -0,0 +1,470 @@ +/* + * Copyright (C) 2010-2012 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_pmm_policy_jobcontrol.c + * Implementation of the power management module policy - job control + */ + +#if USING_MALI_PMM + +#include "mali_ukk.h" +#include "mali_kernel_common.h" +#include "mali_platform.h" + +#include "mali_pmm.h" +#include "mali_pmm_system.h" +#include "mali_pmm_state.h" +#include "mali_pmm_policy.h" +#include "mali_pmm_policy_jobcontrol.h" + +typedef struct _pmm_policy_data_job_control +{ + _pmm_policy_timer_t latency; /**< Latency timeout timer for all cores */ + u32 core_active_start; /**< Last time a core was set to active */ + u32 timeout; /**< Timeout in ticks for latency timer */ +} _pmm_policy_data_job_control_t; + + +/* @ brief Local data for this policy + */ +static _pmm_policy_data_job_control_t *data_job_control = NULL; + +/* @brief Set up the timeout if it hasn't already been set and if there are active cores */ +static void job_control_timeout_setup( _mali_pmm_internal_state_t *pmm, _pmm_policy_timer_t *pptimer ) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_DEBUG_ASSERT_POINTER(pptimer); + + /* Do we have an inactivity time out and some powered cores? */ + if( pptimer->timeout > 0 && pmm->cores_powered != 0 ) + { + /* Is the system idle and all the powered cores are idle? */ + if( pmm->status == MALI_PMM_STATUS_IDLE && pmm->cores_idle == pmm->cores_powered ) + { + if( pmm_policy_timer_start(pptimer) ) + { + MALIPMM_DEBUG_PRINT( ("PMM policy - Job control: Setting in-activity latency timer\n") ); + } + } + else + { + /* We are not idle so there is no need for an inactivity timer + */ + if( pmm_policy_timer_stop(pptimer) ) + { + MALIPMM_DEBUG_PRINT( ("PMM policy - Job control: Removing in-activity latency timer\n") ); + } + } + } +} + +/* @brief Check the validity of the timeout - and if there is one set */ +static mali_bool job_control_timeout_valid( _mali_pmm_internal_state_t *pmm, _pmm_policy_timer_t *pptimer, u32 timer_start ) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_DEBUG_ASSERT_POINTER(pptimer); + + /* Not a valid timer! */ + if( pptimer->timeout == 0 ) return MALI_FALSE; + + /* Are some cores powered and are they all idle? */ + if( (pmm->cores_powered != 0) && (pmm->cores_idle == pmm->cores_powered) ) + { + /* Has latency timeout started after the last core was active? */ + if( pmm_policy_timer_valid( timer_start, data_job_control->core_active_start ) ) + { + return MALI_TRUE; + } + else + { + MALIPMM_DEBUG_PRINT( ("PMM: In-activity latency time out ignored - out of date\n") ); + } + } + else + { + if( pmm->cores_powered == 0 ) + { + MALIPMM_DEBUG_PRINT( ("PMM: In-activity latency time out ignored - cores already off\n") ); + } + else + { + MALIPMM_DEBUG_PRINT( ("PMM: In-activity latency time out ignored - cores active\n") ); + } + } + + return MALI_FALSE; +} + +_mali_osk_errcode_t pmm_policy_init_job_control( _mali_pmm_internal_state_t *pmm ) +{ + _mali_osk_errcode_t err; + MALI_DEBUG_ASSERT_POINTER( pmm ); + MALI_DEBUG_ASSERT( data_job_control == NULL ); + + data_job_control = (_pmm_policy_data_job_control_t *) _mali_osk_malloc(sizeof(*data_job_control)); + MALI_CHECK_NON_NULL( data_job_control, _MALI_OSK_ERR_NOMEM ); + + data_job_control->core_active_start = _mali_osk_time_tickcount(); + data_job_control->timeout = MALI_PMM_POLICY_JOBCONTROL_INACTIVITY_TIMEOUT; + + err = pmm_policy_timer_init( &data_job_control->latency, data_job_control->timeout, MALI_PMM_EVENT_TIMEOUT ); + if( err != _MALI_OSK_ERR_OK ) + { + _mali_osk_free( data_job_control ); + data_job_control = NULL; + return err; + } + + /* Start the latency timeout */ + job_control_timeout_setup( pmm, &data_job_control->latency ); + + MALI_SUCCESS; +} + +void pmm_policy_term_job_control(void) +{ + if( data_job_control != NULL ) + { + pmm_policy_timer_term( &data_job_control->latency ); + _mali_osk_free( data_job_control ); + data_job_control = NULL; + } +} + +static void pmm_policy_job_control_job_queued( _mali_pmm_internal_state_t *pmm ) +{ + mali_pmm_core_mask cores; + mali_pmm_core_mask cores_subset; + + /* Make sure that all cores are powered in this + * simple policy + */ + cores = pmm->cores_registered; + cores_subset = pmm_cores_to_power_up( pmm, cores ); + if( cores_subset != 0 ) + { + /* There are some cores that need powering up */ + if( !pmm_invoke_power_up( pmm ) ) + { + /* Need to wait until finished */ + pmm->status = MALI_PMM_STATUS_POLICY_POWER_UP; + } + } +} + +_mali_osk_errcode_t pmm_policy_process_job_control( _mali_pmm_internal_state_t *pmm, mali_pmm_message_t *event ) +{ + mali_pmm_core_mask cores; + mali_pmm_core_mask cores_subset; + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_DEBUG_ASSERT_POINTER(event); + MALI_DEBUG_ASSERT_POINTER(data_job_control); + + MALIPMM_DEBUG_PRINT( ("PMM: Job control policy process start - status=%d\n", pmm->status) ); + + /* Mainly the data is the cores */ + cores = pmm_cores_from_event_data( pmm, event ); + +#if MALI_STATE_TRACKING + pmm->mali_last_pmm_status = pmm->status; +#endif /* MALI_STATE_TRACKING */ + + switch( pmm->status ) + { + /**************** IDLE ****************/ + case MALI_PMM_STATUS_IDLE: + switch( event->id ) + { + case MALI_PMM_EVENT_OS_POWER_UP: + /* Not expected in this state */ + break; + + case MALI_PMM_EVENT_JOB_SCHEDULED: + + /* Update idle cores to indicate active - remove these! */ + pmm_cores_set_active( pmm, cores ); + /* Remember when this happened */ + data_job_control->core_active_start = event->ts; +#if MALI_POWER_MGMT_TEST_SUITE + _mali_osk_pmm_policy_events_notifications(MALI_PMM_EVENT_JOB_SCHEDULED); +#endif + + /*** FALL THROUGH to QUEUED to check POWER UP ***/ + + case MALI_PMM_EVENT_JOB_QUEUED: + + pmm_policy_job_control_job_queued( pmm ); +#if MALI_POWER_MGMT_TEST_SUITE + _mali_osk_pmm_policy_events_notifications(MALI_PMM_EVENT_JOB_QUEUED); +#endif + break; + + case MALI_PMM_EVENT_DVFS_PAUSE: + + cores_subset = pmm_cores_to_power_down( pmm, cores, MALI_FALSE ); + if ( cores_subset != 0 ) + { + if ( !pmm_power_down_okay( pmm ) ) + { + pmm->is_dvfs_active = 1; + pmm->status = MALI_PMM_STATUS_OS_POWER_DOWN; + pmm_save_os_event_data( pmm, event->data ); + break; + } + } + pmm->status = MALI_PMM_STATUS_DVFS_PAUSE; + _mali_osk_pmm_dvfs_operation_done(0); + break; + + case MALI_PMM_EVENT_OS_POWER_DOWN: + + /* Need to power down all cores even if we need to wait for them */ + cores_subset = pmm_cores_to_power_down( pmm, cores, MALI_FALSE ); + if( cores_subset != 0 ) + { + /* There are some cores that need powering down */ + if( !pmm_invoke_power_down( pmm, MALI_POWER_MODE_DEEP_SLEEP ) ) + { + /* We need to wait until they are idle */ + + pmm->status = MALI_PMM_STATUS_OS_POWER_DOWN; + /* Save the OS data to respond later */ + pmm_save_os_event_data( pmm, event->data ); + /* Exit this case - as we have to wait */ + break; + } + } + else + { + mali_platform_power_mode_change(MALI_POWER_MODE_DEEP_SLEEP); + + } + /* Set waiting status */ + pmm->status = MALI_PMM_STATUS_OS_WAITING; + /* All cores now down - respond to OS power event */ + _mali_osk_pmm_power_down_done( event->data ); + break; + + case MALI_PMM_EVENT_JOB_FINISHED: + + /* Update idle cores - add these! */ + pmm_cores_set_idle( pmm, cores ); +#if MALI_POWER_MGMT_TEST_SUITE + _mali_osk_pmm_policy_events_notifications(MALI_PMM_EVENT_JOB_FINISHED); +#endif + if( data_job_control->timeout > 0 ) + { + /* Wait for time out to fire */ + break; + } + /* For job control policy - turn off all cores */ + cores = pmm->cores_powered; + + /*** FALL THROUGH to TIMEOUT TEST as NO TIMEOUT ***/ + + case MALI_PMM_EVENT_TIMEOUT: + + /* Main job control policy - turn off cores after inactivity */ + if( job_control_timeout_valid( pmm, &data_job_control->latency, (u32)event->data ) ) + { + /* Valid timeout of inactivity - so find out if we can power down + * immedately - if we can't then this means the cores are still in fact + * active + */ + cores_subset = pmm_cores_to_power_down( pmm, cores, MALI_TRUE ); + if( cores_subset != 0 ) + { + /* Check if we can really power down, if not then we are not + * really in-active + */ + if( !pmm_invoke_power_down( pmm, MALI_POWER_MODE_LIGHT_SLEEP ) ) + { + pmm_power_down_cancel( pmm ); + } + } + /* else there are no cores powered up! */ + } +#if MALI_POWER_MGMT_TEST_SUITE + _mali_osk_pmm_policy_events_notifications(MALI_PMM_EVENT_TIMEOUT); +#endif + break; + + default: + /* Unexpected event */ + MALI_ERROR(_MALI_OSK_ERR_ITEM_NOT_FOUND); + } + break; + + /******************DVFS PAUSE**************/ + case MALI_PMM_STATUS_DVFS_PAUSE: + switch ( event->id ) + { + case MALI_PMM_EVENT_DVFS_RESUME: + + if ( pmm->cores_powered != 0 ) + { + pmm->cores_ack_down =0; + pmm_power_down_cancel( pmm ); + pmm->status = MALI_PMM_STATUS_IDLE; + } + else + { + pmm_policy_job_control_job_queued( pmm ); + } + _mali_osk_pmm_dvfs_operation_done( 0 ); + break; + + case MALI_PMM_EVENT_OS_POWER_DOWN: + /* Set waiting status */ + pmm->status = MALI_PMM_STATUS_OS_WAITING; + if ( pmm->cores_powered != 0 ) + { + if ( pmm_invoke_power_down( pmm, MALI_POWER_MODE_DEEP_SLEEP ) ) + { + _mali_osk_pmm_power_down_done( 0 ); + break; + } + } + else + { + mali_platform_power_mode_change(MALI_POWER_MODE_DEEP_SLEEP); + } + _mali_osk_pmm_power_down_done( 0 ); + break; + default: + break; + } + break; + + /**************** POWER UP ****************/ + case MALI_PMM_STATUS_OS_POWER_UP: + case MALI_PMM_STATUS_POLICY_POWER_UP: + switch( event->id ) + { + case MALI_PMM_EVENT_INTERNAL_POWER_UP_ACK: + /* Make sure cores powered off equal what we expect */ + MALI_DEBUG_ASSERT( cores == pmm->cores_pend_up ); + pmm_cores_set_up_ack( pmm, cores ); + + if( pmm_invoke_power_up( pmm ) ) + { + if( pmm->status == MALI_PMM_STATUS_OS_POWER_UP ) + { + /* Get the OS data and respond to the power up */ + _mali_osk_pmm_power_up_done( pmm_retrieve_os_event_data( pmm ) ); + } + pmm->status = MALI_PMM_STATUS_IDLE; + } + break; + + default: + /* Unexpected event */ + MALI_ERROR(_MALI_OSK_ERR_ITEM_NOT_FOUND); + } + break; + + /**************** POWER DOWN ****************/ + case MALI_PMM_STATUS_OS_POWER_DOWN: + case MALI_PMM_STATUS_POLICY_POWER_DOWN: + switch( event->id ) + { + + case MALI_PMM_EVENT_INTERNAL_POWER_DOWN_ACK: + + pmm_cores_set_down_ack( pmm, cores ); + + if ( pmm->is_dvfs_active == 1 ) + { + if( pmm_power_down_okay( pmm ) ) + { + pmm->is_dvfs_active = 0; + pmm->status = MALI_PMM_STATUS_DVFS_PAUSE; + _mali_osk_pmm_dvfs_operation_done( pmm_retrieve_os_event_data( pmm ) ); + } + break; + } + + /* Now check if we can power down */ + if( pmm_invoke_power_down( pmm, MALI_POWER_MODE_DEEP_SLEEP ) ) + { + if( pmm->status == MALI_PMM_STATUS_OS_POWER_DOWN ) + { + /* Get the OS data and respond to the power down */ + _mali_osk_pmm_power_down_done( pmm_retrieve_os_event_data( pmm ) ); + } + pmm->status = MALI_PMM_STATUS_OS_WAITING; + } + break; + + default: + /* Unexpected event */ + MALI_ERROR(_MALI_OSK_ERR_ITEM_NOT_FOUND); + } + break; + + case MALI_PMM_STATUS_OS_WAITING: + switch( event->id ) + { + case MALI_PMM_EVENT_OS_POWER_UP: + cores_subset = pmm_cores_to_power_up( pmm, cores ); + if( cores_subset != 0 ) + { + /* There are some cores that need powering up */ + if( !pmm_invoke_power_up( pmm ) ) + { + /* Need to wait until power up complete */ + pmm->status = MALI_PMM_STATUS_OS_POWER_UP; + /* Save the OS data to respond later */ + pmm_save_os_event_data( pmm, event->data ); + /* Exit this case - as we have to wait */ + break; + } + } + pmm->status = MALI_PMM_STATUS_IDLE; + /* All cores now up - respond to OS power up event */ + _mali_osk_pmm_power_up_done( event->data ); + break; + + default: + /* All other messages are ignored in this state */ + break; + } + break; + + default: + /* Unexpected state */ + MALI_ERROR(_MALI_OSK_ERR_FAULT); + } + + /* Set in-activity latency timer - if required */ + job_control_timeout_setup( pmm, &data_job_control->latency ); + + /* Update the PMM state */ + pmm_update_system_state( pmm ); +#if MALI_STATE_TRACKING + pmm->mali_new_event_status = event->id; +#endif /* MALI_STATE_TRACKING */ + + MALIPMM_DEBUG_PRINT( ("PMM: Job control policy process end - status=%d and event=%d\n", pmm->status,event->id) ); + + MALI_SUCCESS; +} + +void pmm_policy_check_job_control() +{ + MALI_DEBUG_ASSERT_POINTER(data_job_control); + + /* Latency timer must have expired raise the event */ + pmm_policy_timer_raise_event(&data_job_control->latency); +} + + +#endif /* USING_MALI_PMM */ diff --git a/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy_jobcontrol.h b/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy_jobcontrol.h new file mode 100644 index 00000000000..71af3b15d03 --- /dev/null +++ b/drivers/gpu/arm/mali/common/pmm/mali_pmm_policy_jobcontrol.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2010-2012 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_pmm_policy.h + * Defines the power management module policies + */ + +#ifndef __MALI_PMM_POLICY_JOBCONTROL_H__ +#define __MALI_PMM_POLICY_JOBCONTROL_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @addtogroup pmmapi_policy Power Management Module Policies + * + * @{ + */ + +/** @brief The jobcontrol policy inactivity latency timeout (in ticks) + * before the hardware is switched off + * + * @note Setting this low whilst tracing or producing debug output can + * cause alot of timeouts to fire which can affect the PMM behaviour + */ +#define MALI_PMM_POLICY_JOBCONTROL_INACTIVITY_TIMEOUT 50 + +/** @brief Job control policy initialization + * + * @return _MALI_OSK_ERR_OK if the policy could be initialized, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t pmm_policy_init_job_control(_mali_pmm_internal_state_t *pmm); + +/** @brief Job control policy termination + */ +void pmm_policy_term_job_control(void); + +/** @brief Job control policy state changer + * + * Given the next available event message, this routine processes it + * for the policy and changes state as needed. + * + * Job control policy depends on events from the Mali cores, and will + * power down all cores after an inactivity latency timeout. It will + * power the cores back on again when a job is scheduled to run. + * + * @param pmm internal PMM state + * @param event PMM event to process + * @return _MALI_OSK_ERR_OK if the policy state completed okay, or a suitable + * _mali_osk_errcode_t otherwise. + */ +_mali_osk_errcode_t pmm_policy_process_job_control( _mali_pmm_internal_state_t *pmm, mali_pmm_message_t *event ); + +/** @brief Job control policy checker + * + * The latency timer has fired and we need to raise the correct event to + * handle it + * + * @param pmm internal PMM state + */ +void pmm_policy_check_job_control(void); + +/** @} */ /* End group pmmapi_policy */ + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_PMM_POLICY_JOBCONTROL_H__ */ diff --git a/drivers/gpu/arm/mali/common/pmm/mali_pmm_state.c b/drivers/gpu/arm/mali/common/pmm/mali_pmm_state.c new file mode 100644 index 00000000000..c50a56799ae --- /dev/null +++ b/drivers/gpu/arm/mali/common/pmm/mali_pmm_state.c @@ -0,0 +1,721 @@ +/* + * Copyright (C) 2010-2012 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_pmm_state.c + * Implementation of the power management module internal state + */ + +#if USING_MALI_PMM + +#include "mali_ukk.h" +#include "mali_kernel_common.h" +#include "mali_kernel_subsystem.h" + +#include "mali_pmm.h" +#include "mali_pmm_state.h" +#include "mali_pmm_system.h" + +#include "mali_kernel_core.h" +#include "mali_platform.h" + +#define SIZEOF_CORES_LIST 6 + +/* NOTE: L2 *MUST* be first on the list so that it + * is correctly powered on first and powered off last + */ +static mali_pmm_core_id cores_list[] = { MALI_PMM_CORE_L2, + MALI_PMM_CORE_GP, + MALI_PMM_CORE_PP0, + MALI_PMM_CORE_PP1, + MALI_PMM_CORE_PP2, + MALI_PMM_CORE_PP3 }; + + + +void pmm_update_system_state( _mali_pmm_internal_state_t *pmm ) +{ + mali_pmm_state state; + + MALI_DEBUG_ASSERT_POINTER(pmm); + + if( pmm->cores_registered == 0 ) + { + state = MALI_PMM_STATE_UNAVAILABLE; + } + else if( pmm->cores_powered == 0 ) + { + state = MALI_PMM_STATE_SYSTEM_OFF; + } + else if( pmm->cores_powered == pmm->cores_registered ) + { + state = MALI_PMM_STATE_SYSTEM_ON; + } + else + { + /* Some other state where not everything is on or off */ + state = MALI_PMM_STATE_SYSTEM_TRANSITION; + } + +#if MALI_PMM_TRACE + _mali_pmm_trace_state_change( pmm->state, state ); +#endif + pmm->state = state; +} + +mali_pmm_core_mask pmm_cores_from_event_data( _mali_pmm_internal_state_t *pmm, mali_pmm_message_t *event ) +{ + mali_pmm_core_mask cores; + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_DEBUG_ASSERT_POINTER(event); + + switch( event->id ) + { + case MALI_PMM_EVENT_OS_POWER_UP: + case MALI_PMM_EVENT_OS_POWER_DOWN: + /* All cores - the system */ + cores = pmm->cores_registered; + break; + + case MALI_PMM_EVENT_JOB_SCHEDULED: + case MALI_PMM_EVENT_JOB_QUEUED: + case MALI_PMM_EVENT_JOB_FINISHED: + case MALI_PMM_EVENT_INTERNAL_POWER_UP_ACK: + case MALI_PMM_EVENT_INTERNAL_POWER_DOWN_ACK: + /* Currently the main event data is only the cores + * for these messages + */ + cores = (mali_pmm_core_mask)event->data; + if( cores == MALI_PMM_CORE_SYSTEM ) + { + cores = pmm->cores_registered; + } + else if( cores == MALI_PMM_CORE_PP_ALL ) + { + /* Get the subset of registered PP cores */ + cores = (pmm->cores_registered & MALI_PMM_CORE_PP_ALL); + } + MALI_PMM_DEBUG_ASSERT_CORES_SUBSET( pmm->cores_registered, cores ); + break; + + default: + /* Assume timeout messages - report cores still powered */ + cores = pmm->cores_powered; + break; + } + + return cores; +} + +mali_pmm_core_mask pmm_cores_to_power_up( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores ) +{ + mali_pmm_core_mask cores_subset; + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_PMM_DEBUG_ASSERT_CORES_SUBSET( pmm->cores_registered, cores ); + + /* Check that cores aren't pending power down when asked for power up */ + MALI_DEBUG_ASSERT( pmm->cores_pend_down == 0 ); + + cores_subset = (~(pmm->cores_powered) & cores); + if( cores_subset != 0 ) + { + /* There are some cores that need powering up */ + pmm->cores_pend_up = cores_subset; + } + + return cores_subset; +} + +mali_pmm_core_mask pmm_cores_to_power_down( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores, mali_bool immediate_only ) +{ + mali_pmm_core_mask cores_subset; + _mali_osk_errcode_t err; + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_PMM_DEBUG_ASSERT_CORES_SUBSET( pmm->cores_registered, cores ); + + /* Check that cores aren't pending power up when asked for power down */ + MALI_DEBUG_ASSERT( pmm->cores_pend_up == 0 ); + + cores_subset = (pmm->cores_powered & cores); + if( cores_subset != 0 ) + { + int n; + volatile mali_pmm_core_mask *ppowered = &(pmm->cores_powered); + + /* There are some cores that need powering up, but we may + * need to wait until they are idle + */ + for( n = SIZEOF_CORES_LIST-1; n >= 0; n-- ) + { + if( (cores_list[n] & cores_subset) != 0 ) + { + /* Core is to be powered down */ + pmm->cores_pend_down |= cores_list[n]; + + /* Can't hold the power lock, when acessing subsystem mutex via + * the core power call. + * Due to terminatation of driver requiring a subsystem mutex + * and then power lock held to unregister a core. + * This does mean that the following function could fail + * as the core is unregistered before we tell it to power + * down, but it does not matter as we are terminating + */ +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 0; +#endif /* MALI_STATE_TRACKING */ + + MALI_PMM_UNLOCK(pmm); + /* Signal the core to power down + * If it is busy (not idle) it will set a pending power down flag + * (as long as we don't want to only immediately power down). + * If it isn't busy it will move out of the idle queue right + * away + */ + err = mali_core_signal_power_down( cores_list[n], immediate_only ); + MALI_PMM_LOCK(pmm); + +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 1; +#endif /* MALI_STATE_TRACKING */ + + + /* Re-read cores_subset in case it has changed */ + cores_subset = (*ppowered & cores); + + if( err == _MALI_OSK_ERR_OK ) + { + /* We moved an idle core to the power down queue + * which means it is now acknowledged (if it is still + * registered) + */ + pmm->cores_ack_down |= (cores_list[n] & cores_subset); + } + else + { + MALI_DEBUG_PRINT(1,("PMM: In pmm_cores_to_power_down, the error and cores powered are..%x....%x",err,*ppowered)); + MALI_DEBUG_ASSERT( err == _MALI_OSK_ERR_BUSY || + (err == _MALI_OSK_ERR_FAULT && + (*ppowered & cores_list[n]) == 0) ); + /* If we didn't move a core - it must be active, so + * leave it pending, so we get an acknowledgement (when + * not in immediate only mode) + * Alternatively we are shutting down and the core has + * been unregistered + */ + } + } + } + } + + return cores_subset; +} + +void pmm_power_down_cancel( _mali_pmm_internal_state_t *pmm ) +{ + int n; + mali_pmm_core_mask pd, ad; + _mali_osk_errcode_t err; + volatile mali_pmm_core_mask *pregistered; + + MALI_DEBUG_ASSERT_POINTER(pmm); + + MALIPMM_DEBUG_PRINT( ("PMM: Cancelling power down\n") ); + + pd = pmm->cores_pend_down; + ad = pmm->cores_ack_down; + /* Clear the pending cores so that they don't move to the off + * queue if they haven't already + */ + pmm->cores_pend_down = 0; + pmm->cores_ack_down = 0; + pregistered = &(pmm->cores_registered); + + /* Power up all the pending power down cores - just so + * we make sure the system is in a known state, as a + * pending core might have sent an acknowledged message + * which hasn't been read yet. + */ + for( n = 0; n < SIZEOF_CORES_LIST; n++ ) + { + if( (cores_list[n] & pd) != 0 ) + { + /* Can't hold the power lock, when acessing subsystem mutex via + * the core power call. + * Due to terminatation of driver requiring a subsystem mutex + * and then power lock held to unregister a core. + * This does mean that the following power up function could fail + * as the core is unregistered before we tell it to power + * up, but it does not matter as we are terminating + */ +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 0; +#endif /* MALI_STATE_TRACKING */ + + MALI_PMM_UNLOCK(pmm); + /* As we are cancelling - only move the cores back to the queue - + * no reset needed + */ + err = mali_core_signal_power_up( cores_list[n], MALI_TRUE ); + MALI_PMM_LOCK(pmm); +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 1; +#endif /* MALI_STATE_TRACKING */ + + /* Update pending list with the current registered cores */ + pd &= (*pregistered); + + if( err != _MALI_OSK_ERR_OK ) + { + MALI_DEBUG_ASSERT( (err == _MALI_OSK_ERR_BUSY && + ((cores_list[n] & ad) == 0)) || + (err == _MALI_OSK_ERR_FAULT && + (*pregistered & cores_list[n]) == 0) ); + /* If we didn't power up a core - it must be active and + * hasn't actually tried to power down - this is expected + * for cores that haven't acknowledged + * Alternatively we are shutting down and the core has + * been unregistered + */ + } + } + } + /* Only used in debug builds */ + MALI_IGNORE(ad); +} + + +mali_bool pmm_power_down_okay( _mali_pmm_internal_state_t *pmm ) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + + return ( pmm->cores_pend_down == pmm->cores_ack_down ? MALI_TRUE : MALI_FALSE ); +} + +mali_bool pmm_invoke_power_down( _mali_pmm_internal_state_t *pmm, mali_power_mode power_mode ) +{ + _mali_osk_errcode_t err; + MALI_DEBUG_ASSERT_POINTER(pmm); + + /* Check that cores are pending power down during power down invoke */ + MALI_DEBUG_ASSERT( pmm->cores_pend_down != 0 ); + /* Check that cores are not pending power up during power down invoke */ + MALI_DEBUG_ASSERT( pmm->cores_pend_up == 0 ); + + if( !pmm_power_down_okay( pmm ) ) + { + MALIPMM_DEBUG_PRINT( ("PMM: Waiting for cores to go idle for power off - 0x%08x / 0x%08x\n", + pmm->cores_pend_down, pmm->cores_ack_down) ); + return MALI_FALSE; + } + else + { + pmm->cores_powered &= ~(pmm->cores_pend_down); +#if !MALI_PMM_NO_PMU + err = malipmm_powerdown( pmm->cores_pend_down, power_mode); +#else + err = _MALI_OSK_ERR_OK; +#endif + + if( err == _MALI_OSK_ERR_OK ) + { +#if MALI_PMM_TRACE + mali_pmm_core_mask old_power = pmm->cores_powered; +#endif + /* Remove powered down cores from idle and powered list */ + pmm->cores_idle &= ~(pmm->cores_pend_down); + /* Reset pending/acknowledged status */ + pmm->cores_pend_down = 0; + pmm->cores_ack_down = 0; +#if MALI_PMM_TRACE + _mali_pmm_trace_hardware_change( old_power, pmm->cores_powered ); +#endif + } + else + { + pmm->cores_powered |= pmm->cores_pend_down; + MALI_PRINT_ERROR( ("PMM: Failed to get PMU to power down cores - (0x%x) %s", + pmm->cores_pend_down, pmm_trace_get_core_name(pmm->cores_pend_down)) ); + pmm->fatal_power_err = MALI_TRUE; + } + } + + return MALI_TRUE; +} + + +mali_bool pmm_power_up_okay( _mali_pmm_internal_state_t *pmm ) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + + return ( pmm->cores_pend_up == pmm->cores_ack_up ? MALI_TRUE : MALI_FALSE ); +} + + +mali_bool pmm_invoke_power_up( _mali_pmm_internal_state_t *pmm ) +{ + _mali_osk_errcode_t err; + + MALI_DEBUG_ASSERT_POINTER(pmm); + + /* Check that cores are pending power up during power up invoke */ + MALI_DEBUG_ASSERT( pmm->cores_pend_up != 0 ); + /* Check that cores are not pending power down during power up invoke */ + MALI_DEBUG_ASSERT( pmm->cores_pend_down == 0 ); + + if( pmm_power_up_okay( pmm ) ) + { + /* Power up has completed - sort out subsystem core status */ + + int n; + /* Use volatile to access, so that it is updated if any cores are unregistered */ + volatile mali_pmm_core_mask *ppendup = &(pmm->cores_pend_up); +#if MALI_PMM_TRACE + mali_pmm_core_mask old_power = pmm->cores_powered; +#endif + /* Move cores into idle queues */ + for( n = 0; n < SIZEOF_CORES_LIST; n++ ) + { + if( (cores_list[n] & (*ppendup)) != 0 ) + { + /* Can't hold the power lock, when acessing subsystem mutex via + * the core power call. + * Due to terminatation of driver requiring a subsystem mutex + * and then power lock held to unregister a core. + * This does mean that the following function could fail + * as the core is unregistered before we tell it to power + * up, but it does not matter as we are terminating + */ +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 0; +#endif /* MALI_STATE_TRACKING */ + + MALI_PMM_UNLOCK(pmm); + err = mali_core_signal_power_up( cores_list[n], MALI_FALSE ); + MALI_PMM_LOCK(pmm); + +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 1; +#endif /* MALI_STATE_TRACKING */ + + + if( err != _MALI_OSK_ERR_OK ) + { + MALI_DEBUG_PRINT(1,("In pmm_invoke_power_up:: The error and pending cores to be powered up are...%x...%x",err,*ppendup)); + MALI_DEBUG_ASSERT( (err == _MALI_OSK_ERR_FAULT && + (*ppendup & cores_list[n]) == 0) ); + /* We only expect this to fail when we are shutting down + * and the core has been unregistered + */ + } + } + } + /* Finished power up - add cores to idle and powered list */ + pmm->cores_powered |= (*ppendup); + pmm->cores_idle |= (*ppendup); + /* Reset pending/acknowledge status */ + pmm->cores_pend_up = 0; + pmm->cores_ack_up = 0; + +#if MALI_PMM_TRACE + _mali_pmm_trace_hardware_change( old_power, pmm->cores_powered ); +#endif + return MALI_TRUE; + } + else + { +#if !MALI_PMM_NO_PMU + /* Power up must now be done */ + err = malipmm_powerup( pmm->cores_pend_up ); +#else + err = _MALI_OSK_ERR_OK; +#endif + if( err != _MALI_OSK_ERR_OK ) + { + MALI_PRINT_ERROR( ("PMM: Failed to get PMU to power up cores - (0x%x) %s", + pmm->cores_pend_up, pmm_trace_get_core_name(pmm->cores_pend_up)) ); + pmm->fatal_power_err = MALI_TRUE; + } + else + { + /* TBD - Update core status immediately rather than use event message */ + _mali_uk_pmm_message_s event = { + NULL, + MALI_PMM_EVENT_INTERNAL_POWER_UP_ACK, + 0 }; + /* All the cores that were pending power up, have now completed power up */ + event.data = pmm->cores_pend_up; + _mali_ukk_pmm_event_message( &event ); + MALIPMM_DEBUG_PRINT( ("PMM: Sending ACK to power up") ); + } + } + + /* Always return false, as we need an interrupt to acknowledge + * when power up is complete + */ + return MALI_FALSE; +} + +mali_pmm_core_mask pmm_cores_set_active( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores ) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_PMM_DEBUG_ASSERT_CORES_SUBSET( pmm->cores_registered, cores ); + + pmm->cores_idle &= (~cores); + return pmm->cores_idle; +} + +mali_pmm_core_mask pmm_cores_set_idle( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores ) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_PMM_DEBUG_ASSERT_CORES_SUBSET( pmm->cores_registered, cores ); + + pmm->cores_idle |= (cores); + return pmm->cores_idle; +} + +mali_pmm_core_mask pmm_cores_set_down_ack( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores ) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_PMM_DEBUG_ASSERT_CORES_SUBSET( pmm->cores_registered, cores ); + + /* Check core is not pending a power down */ + MALI_DEBUG_ASSERT( (pmm->cores_pend_down & cores) != 0 ); + /* Check core has not acknowledged power down more than once */ + MALI_DEBUG_ASSERT( (pmm->cores_ack_down & cores) == 0 ); + + pmm->cores_ack_down |= (cores); + + return pmm->cores_ack_down; +} + +void pmm_fatal_reset( _mali_pmm_internal_state_t *pmm ) +{ + _mali_osk_errcode_t err = _MALI_OSK_ERR_OK; + _mali_osk_notification_t *msg = NULL; + mali_pmm_status status; + MALI_DEBUG_ASSERT_POINTER(pmm); + MALIPMM_DEBUG_PRINT( ("PMM: Fatal Reset called") ); + + MALI_DEBUG_ASSERT( pmm->status != MALI_PMM_STATUS_OFF ); + + /* Reset the common status */ + pmm->waiting = 0; + pmm->missed = 0; + pmm->fatal_power_err = MALI_FALSE; + pmm->no_events = 0; + pmm->check_policy = MALI_FALSE; + pmm->cores_pend_down = 0; + pmm->cores_pend_up = 0; + pmm->cores_ack_down = 0; + pmm->cores_ack_up = 0; + pmm->is_dvfs_active = 0; +#if MALI_PMM_TRACE + pmm->messages_sent = 0; + pmm->messages_received = 0; + pmm->imessages_sent = 0; + pmm->imessages_received = 0; + MALI_PRINT( ("PMM Trace: *** Fatal reset occurred ***") ); +#endif + + /* Set that we are unavailable whilst resetting */ + pmm->state = MALI_PMM_STATE_UNAVAILABLE; + status = pmm->status; + pmm->status = MALI_PMM_STATUS_OFF; + + /* We want all cores powered */ + pmm->cores_powered = pmm->cores_registered; + /* The cores may not be idle, but this state will be rectified later */ + pmm->cores_idle = pmm->cores_registered; + + /* So power on any cores that are registered */ + if( pmm->cores_registered != 0 ) + { + int n; + volatile mali_pmm_core_mask *pregistered = &(pmm->cores_registered); +#if !MALI_PMM_NO_PMU + err = malipmm_powerup( pmm->cores_registered ); +#endif + if( err != _MALI_OSK_ERR_OK ) + { + /* This is very bad as we can't even be certain the cores are now + * powered up + */ + MALI_PRINT_ERROR( ("PMM: Failed to perform PMM reset!\n") ); + /* TBD driver exit? */ + } + + for( n = SIZEOF_CORES_LIST-1; n >= 0; n-- ) + { + if( (cores_list[n] & (*pregistered)) != 0 ) + { +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 0; +#endif /* MALI_STATE_TRACKING */ + + MALI_PMM_UNLOCK(pmm); + /* Core is now active - so try putting it in the idle queue */ + err = mali_core_signal_power_up( cores_list[n], MALI_FALSE ); + MALI_PMM_LOCK(pmm); +#if MALI_STATE_TRACKING + pmm->mali_pmm_lock_acquired = 1; +#endif /* MALI_STATE_TRACKING */ + + /* We either succeeded, or we were not off anyway, or we have + * just be deregistered + */ + MALI_DEBUG_ASSERT( (err == _MALI_OSK_ERR_OK) || + (err == _MALI_OSK_ERR_BUSY) || + (err == _MALI_OSK_ERR_FAULT && + (*pregistered & cores_list[n]) == 0) ); + } + } + } + + /* Unblock any pending OS event */ + if( status == MALI_PMM_STATUS_OS_POWER_UP ) + { + /* Get the OS data and respond to the power up */ + _mali_osk_pmm_power_up_done( pmm_retrieve_os_event_data( pmm ) ); + } + if( status == MALI_PMM_STATUS_OS_POWER_DOWN ) + { + /* Get the OS data and respond to the power down + * NOTE: We are not powered down at this point due to power problems, + * so we are lying to the system, but something bad has already + * happened and we are trying unstick things + * TBD - Add busy loop to power down cores? + */ + _mali_osk_pmm_power_down_done( pmm_retrieve_os_event_data( pmm ) ); + } + + /* Purge the event queues */ + do + { + if( _mali_osk_notification_queue_dequeue( pmm->iqueue, &msg ) == _MALI_OSK_ERR_OK ) + { + _mali_osk_notification_delete ( msg ); + break; + } + } while (MALI_TRUE); + + do + { + if( _mali_osk_notification_queue_dequeue( pmm->queue, &msg ) == _MALI_OSK_ERR_OK ) + { + _mali_osk_notification_delete ( msg ); + break; + } + } while (MALI_TRUE); + + /* Return status/state to normal */ + pmm->status = MALI_PMM_STATUS_IDLE; + pmm_update_system_state(pmm); +} + +mali_pmm_core_mask pmm_cores_set_up_ack( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores ) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + MALI_PMM_DEBUG_ASSERT_CORES_SUBSET( pmm->cores_registered, cores ); + + /* Check core is not pending a power up */ + MALI_DEBUG_ASSERT( (pmm->cores_pend_up & cores) != 0 ); + /* Check core has not acknowledged power up more than once */ + MALI_DEBUG_ASSERT( (pmm->cores_ack_up & cores) == 0 ); + + pmm->cores_ack_up |= (cores); + + return pmm->cores_ack_up; +} + +void pmm_save_os_event_data(_mali_pmm_internal_state_t *pmm, mali_pmm_message_data data) +{ + MALI_DEBUG_ASSERT_POINTER(pmm); + /* Check that there is no saved data */ + MALI_DEBUG_ASSERT( pmm->os_data == 0 ); + /* Can't store zero data - as retrieve check will fail */ + MALI_DEBUG_ASSERT( data != 0 ); + + pmm->os_data = data; +} + +mali_pmm_message_data pmm_retrieve_os_event_data(_mali_pmm_internal_state_t *pmm) +{ + mali_pmm_message_data data; + + MALI_DEBUG_ASSERT_POINTER(pmm); + /* Check that there is saved data */ + MALI_DEBUG_ASSERT( pmm->os_data != 0 ); + + /* Get data, and clear the saved version */ + data = pmm->os_data; + pmm->os_data = 0; + + return data; +} + +/* Create list of core names to look up + * We are doing it this way to overcome the need for + * either string allocation, or stack space, so we + * use constant strings instead + */ +typedef struct pmm_trace_corelist +{ + mali_pmm_core_mask id; + const char *name; +} pmm_trace_corelist_t; + +static pmm_trace_corelist_t pmm_trace_cores[] = { + { MALI_PMM_CORE_SYSTEM, "SYSTEM" }, + { MALI_PMM_CORE_GP, "GP" }, + { MALI_PMM_CORE_L2, "L2" }, + { MALI_PMM_CORE_PP0, "PP0" }, + { MALI_PMM_CORE_PP1, "PP1" }, + { MALI_PMM_CORE_PP2, "PP2" }, + { MALI_PMM_CORE_PP3, "PP3" }, + { MALI_PMM_CORE_PP_ALL, "PP (all)" }, + { (MALI_PMM_CORE_GP | MALI_PMM_CORE_L2 | MALI_PMM_CORE_PP0), + "GP+L2+PP0" }, + { (MALI_PMM_CORE_GP | MALI_PMM_CORE_PP0), + "GP+PP0" }, + { (MALI_PMM_CORE_GP | MALI_PMM_CORE_L2 | MALI_PMM_CORE_PP0 | MALI_PMM_CORE_PP1), + "GP+L2+PP0+PP1" }, + { (MALI_PMM_CORE_GP | MALI_PMM_CORE_PP0 | MALI_PMM_CORE_PP1), + "GP+PP0+PP1" }, + { 0, NULL } /* Terminator of list */ +}; + +const char *pmm_trace_get_core_name( mali_pmm_core_mask cores ) +{ + const char *dname = NULL; + int cl; + + /* Look up name in corelist */ + cl = 0; + while( pmm_trace_cores[cl].name != NULL ) + { + if( pmm_trace_cores[cl].id == cores ) + { + dname = pmm_trace_cores[cl].name; + break; + } + cl++; + } + + if( dname == NULL ) + { + /* We don't know a good short-hand for the configuration */ + dname = "[multi-core]"; + } + + return dname; +} + +#endif /* USING_MALI_PMM */ + diff --git a/drivers/gpu/arm/mali/common/pmm/mali_pmm_state.h b/drivers/gpu/arm/mali/common/pmm/mali_pmm_state.h new file mode 100644 index 00000000000..0fb62d2a642 --- /dev/null +++ b/drivers/gpu/arm/mali/common/pmm/mali_pmm_state.h @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2010-2012 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_pmm_state.h + * Defines the internal power management module state + */ + +#ifndef __MALI_PMM_STATE_H__ +#define __MALI_PMM_STATE_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @addtogroup pmmapi Power Management Module APIs + * + * @{ + * + * @defgroup pmmapi_state Power Management Module State + * + * @{ + */ + +/* Check that the subset is really a subset of cores */ +#define MALI_PMM_DEBUG_ASSERT_CORES_SUBSET( cores, subset ) \ + MALI_DEBUG_ASSERT( ((~(cores)) & (subset)) == 0 ) + + +/* Locking macros */ +#define MALI_PMM_LOCK(pmm) \ + _mali_osk_lock_wait( pmm->lock, _MALI_OSK_LOCKMODE_RW ) +#define MALI_PMM_UNLOCK(pmm) \ + _mali_osk_lock_signal( pmm->lock, _MALI_OSK_LOCKMODE_RW ) +#define MALI_PMM_LOCK_TERM(pmm) \ + _mali_osk_lock_term( pmm->lock ) + +/* Notification type for messages */ +#define MALI_PMM_NOTIFICATION_TYPE 0 + +/** @brief Status of the PMM state machine + */ +typedef enum mali_pmm_status_tag +{ + MALI_PMM_STATUS_IDLE, /**< PMM is waiting next event */ + MALI_PMM_STATUS_POLICY_POWER_DOWN, /**< Policy initiated power down */ + MALI_PMM_STATUS_POLICY_POWER_UP, /**< Policy initiated power down */ + MALI_PMM_STATUS_OS_WAITING, /**< PMM is waiting for OS power up */ + MALI_PMM_STATUS_OS_POWER_DOWN, /**< OS initiated power down */ + MALI_PMM_STATUS_DVFS_PAUSE, /**< PMM DVFS Status Pause */ + MALI_PMM_STATUS_OS_POWER_UP, /**< OS initiated power up */ + MALI_PMM_STATUS_OFF, /**< PMM is not active */ +} mali_pmm_status; + + +/** @brief Internal state of the PMM + */ +typedef struct _mali_pmm_internal_state +{ + mali_pmm_status status; /**< PMM state machine */ + mali_pmm_policy policy; /**< PMM policy */ + mali_bool check_policy; /**< PMM policy needs checking */ + mali_pmm_state state; /**< PMM state */ + mali_pmm_core_mask cores_registered; /**< Bitmask of cores registered */ + mali_pmm_core_mask cores_powered; /**< Bitmask of cores powered up */ + mali_pmm_core_mask cores_idle; /**< Bitmask of cores idle */ + mali_pmm_core_mask cores_pend_down; /**< Bitmask of cores pending power down */ + mali_pmm_core_mask cores_pend_up; /**< Bitmask of cores pending power up */ + mali_pmm_core_mask cores_ack_down; /**< Bitmask of cores acknowledged power down */ + mali_pmm_core_mask cores_ack_up; /**< Bitmask of cores acknowledged power up */ + + _mali_osk_notification_queue_t *queue; /**< PMM event queue */ + _mali_osk_notification_queue_t *iqueue; /**< PMM internal event queue */ + _mali_osk_irq_t *irq; /**< PMM irq handler */ + _mali_osk_lock_t *lock; /**< PMM lock */ + + mali_pmm_message_data os_data; /**< OS data sent via the OS events */ + + mali_bool pmu_initialized; /**< PMU initialized */ + + _mali_osk_atomic_t messages_queued; /**< PMM event messages queued */ + u32 waiting; /**< PMM waiting events - due to busy */ + u32 no_events; /**< PMM called to process when no events */ + + u32 missed; /**< PMM missed events due to OOM */ + mali_bool fatal_power_err; /**< PMM has had a fatal power error? */ + u32 is_dvfs_active; /**< PMM DVFS activity */ + +#if MALI_STATE_TRACKING + mali_pmm_status mali_last_pmm_status; /**< The previous PMM status */ + mali_pmm_event_id mali_new_event_status;/**< The type of the last PMM event */ + mali_bool mali_pmm_lock_acquired; /**< Is the PMM lock held somewhere or not */ +#endif + +#if (MALI_PMM_TRACE || MALI_STATE_TRACKING) + u32 messages_sent; /**< Total event messages sent */ + u32 messages_received; /**< Total event messages received */ + u32 imessages_sent; /**< Total event internal messages sent */ + u32 imessages_received; /**< Total event internal messages received */ +#endif +} _mali_pmm_internal_state_t; + +/** @brief Sets that a policy needs a check before processing events + * + * A timer or something has expired that needs dealing with + */ +void malipmm_set_policy_check(void); + +/** @brief Update the PMM externally viewable state depending on the current PMM internal state + * + * @param pmm internal PMM state + * @return MALI_TRUE if the timeout is valid, else MALI_FALSE + */ +void pmm_update_system_state( _mali_pmm_internal_state_t *pmm ); + +/** @brief Returns the core mask from the event data - if applicable + * + * @param pmm internal PMM state + * @param event event message to get the core mask from + * @return mask of cores that is relevant to this event message + */ +mali_pmm_core_mask pmm_cores_from_event_data( _mali_pmm_internal_state_t *pmm, mali_pmm_message_t *event ); + +/** @brief Sort out which cores need to be powered up from the given core mask + * + * All cores that can be powered up will be put into a pending state + * + * @param pmm internal PMM state + * @param cores mask of cores to check if they need to be powered up + * @return mask of cores that need to be powered up, this can be 0 if all cores + * are powered up already + */ +mali_pmm_core_mask pmm_cores_to_power_up( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores ); + +/** @brief Sort out which cores need to be powered down from the given core mask + * + * All cores that can be powered down will be put into a pending state. If they + * can be powered down immediately they will also be acknowledged that they can be + * powered down. If the immediate_only flag is set, then only those cores that + * can be acknowledged for power down will be put into a pending state. + * + * @param pmm internal PMM state + * @param cores mask of cores to check if they need to be powered down + * @param immediate_only MALI_TRUE means that only cores that can power down now will + * be put into a pending state + * @return mask of cores that need to be powered down, this can be 0 if all cores + * are powered down already + */ +mali_pmm_core_mask pmm_cores_to_power_down( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores, mali_bool immediate_only ); + +/** @brief Cancel an invokation to power down (pmm_invoke_power_down) + * + * @param pmm internal PMM state + */ +void pmm_power_down_cancel( _mali_pmm_internal_state_t *pmm ); + +/** @brief Check if a call to invoke power down should succeed, or fail + * + * This will report MALI_FALSE if some of the cores are still active and need + * to acknowledge that they are ready to power down + * + * @param pmm internal PMM state + * @return MALI_TRUE if the pending cores to power down have acknowledged they + * can power down, else MALI_FALSE + */ +mali_bool pmm_power_down_okay( _mali_pmm_internal_state_t *pmm ); + +/** @brief Try to make all the pending cores power down + * + * If all the pending cores have acknowledged they can power down, this will call the + * PMU power down function to turn them off + * + * @param pmm internal PMM state + * @return MALI_TRUE if the pending cores have been powered down, else MALI_FALSE + */ +mali_bool pmm_invoke_power_down( _mali_pmm_internal_state_t *pmm, mali_power_mode power_mode ); + +/** @brief Check if all the pending cores to power up have done so + * + * This will report MALI_FALSE if some of the cores are still powered off + * and have not acknowledged that they have powered up + * + * @param pmm internal PMM state + * @return MALI_TRUE if the pending cores to power up have acknowledged they + * are now powered up, else MALI_FALSE + */ +mali_bool pmm_power_up_okay( _mali_pmm_internal_state_t *pmm ); + +/** @brief Try to make all the pending cores power up + * + * If all the pending cores have acknowledged they have powered up, this will + * make the cores start processing jobs again, else this will call the PMU + * power up function to turn them on, and the PMM is then expected to wait for an + * interrupt to acknowledge the power up + * + * @param pmm internal PMM state + * @return MALI_TRUE if the pending cores have been powered up, else MALI_FALSE + */ +mali_bool pmm_invoke_power_up( _mali_pmm_internal_state_t *pmm ); + +/** @brief Set the cores that are now active in the system + * + * Updates which cores are active and returns which cores are still idle + * + * @param pmm internal PMM state + * @param cores mask of cores to set to active + * @return mask of all the cores that are idle + */ +mali_pmm_core_mask pmm_cores_set_active( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores ); + +/** @brief Set the cores that are now idle in the system + * + * Updates which cores are idle and returns which cores are still idle + * + * @param pmm internal PMM state + * @param cores mask of cores to set to idle + * @return mask of all the cores that are idle + */ +mali_pmm_core_mask pmm_cores_set_idle( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores ); + +/** @brief Set the cores that have acknowledged a pending power down + * + * Updates which cores have acknowledged the pending power down and are now ready + * to be turned off + * + * @param pmm internal PMM state + * @param cores mask of cores that have acknowledged the pending power down + * @return mask of all the cores that have acknowledged the power down + */ +mali_pmm_core_mask pmm_cores_set_down_ack( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores ); + +/** @brief Set the cores that have acknowledged a pending power up + * + * Updates which cores have acknowledged the pending power up and are now + * fully powered and ready to run jobs + * + * @param pmm internal PMM state + * @param cores mask of cores that have acknowledged the pending power up + * @return mask of all the cores that have acknowledged the power up + */ +mali_pmm_core_mask pmm_cores_set_up_ack( _mali_pmm_internal_state_t *pmm, mali_pmm_core_mask cores ); + + +/** @brief Tries to reset the PMM and PMU hardware to a known state after any fatal issues + * + * This will try and make all the cores powered up and reset the PMM state + * to its initial state after core registration - all cores powered but not + * pending or active. + * All events in the event queues will be thrown away. + * + * @note: Any pending power down will be cancelled including the OS calling for power down + */ +void pmm_fatal_reset( _mali_pmm_internal_state_t *pmm ); + +/** @brief Save the OS specific data for an OS power up/down event + * + * @param pmm internal PMM state + * @param data OS specific event data + */ +void pmm_save_os_event_data(_mali_pmm_internal_state_t *pmm, mali_pmm_message_data data); + +/** @brief Retrieve the OS specific data for an OS power up/down event + * + * This will clear the stored OS data, as well as return it. + * + * @param pmm internal PMM state + * @return OS specific event data that was saved previously + */ +mali_pmm_message_data pmm_retrieve_os_event_data(_mali_pmm_internal_state_t *pmm); + + +/** @brief Get a human readable name for the cores in a core mask + * + * @param core the core mask + * @return string containing a name relating to the given core mask + */ +const char *pmm_trace_get_core_name( mali_pmm_core_mask core ); + +/** @} */ /* End group pmmapi_state */ +/** @} */ /* End group pmmapi */ + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_PMM_STATE_H__ */ diff --git a/drivers/gpu/arm/mali/common/pmm/mali_pmm_system.h b/drivers/gpu/arm/mali/common/pmm/mali_pmm_system.h new file mode 100644 index 00000000000..1be5b847c88 --- /dev/null +++ b/drivers/gpu/arm/mali/common/pmm/mali_pmm_system.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2010-2012 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_pmm_system.h + * Defines the power management module system functions + */ + +#ifndef __MALI_PMM_SYSTEM_H__ +#define __MALI_PMM_SYSTEM_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @addtogroup pmmapi Power Management Module APIs + * + * @{ + * + * @defgroup pmmapi_system Power Management Module System Functions + * + * @{ + */ + +extern struct mali_kernel_subsystem mali_subsystem_pmm; + +/** @brief Register a core with the PMM, which will power up + * the core + * + * @param core the core to register with the PMM + * @return error if the core cannot be powered up + */ +_mali_osk_errcode_t malipmm_core_register( mali_pmm_core_id core ); + +/** @brief Unregister a core with the PMM + * + * @param core the core to unregister with the PMM + */ +void malipmm_core_unregister( mali_pmm_core_id core ); + +/** @brief Acknowledge that a power down is okay to happen + * + * A core should not be running a job, or be in the idle queue when this + * is called. + * + * @param core the core that can now be powered down + */ +void malipmm_core_power_down_okay( mali_pmm_core_id core ); + +/** @} */ /* End group pmmapi_system */ +/** @} */ /* End group pmmapi */ + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_PMM_H__ */ |