add idl4k kernel firmware version 1.13.0.105

This commit is contained in:
Jaroslav Kysela
2015-03-26 17:22:37 +01:00
parent 5194d2792e
commit e9070cdc77
31064 changed files with 12769984 additions and 0 deletions

View File

@@ -0,0 +1,963 @@
/*
* 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.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_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;
static u32 pmm_cores_registered_mask = 0;
#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 !MALI_PMM_NO_PMU
if( mali_platform_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 )
{
_mali_osk_resource_type_t t = PMU;
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( pmm_state->pmu_initialized ) ( mali_platform_deinit(&t) );
_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;
pmm_cores_registered_mask = pmm->cores_registered;
if( ( pmm->status == MALI_PMM_STATUS_OS_POWER_DOWN ) && ( pmm->is_dvfs_active == 1 ) )
{
_mali_osk_pmm_dvfs_operation_done(0);
}
MALI_PMM_UNLOCK(pmm);
/* flush PMM workqueue */
_mali_osk_flush_workqueue( pmm->irq );
if (pmm->cores_powered == 0)
{
mali_platform_powerup(pmm_cores_registered_mask);
}
}
void malipmm_force_powerdown( void )
{
mali_platform_powerdown(pmm_cores_registered_mask);
}
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 )
{
_mali_osk_resource_type_t t = PMU;
#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);
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->pmu_initialized ) mali_platform_deinit(&t);
_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_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 = mali_platform_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_registered &= (~core);
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 */

View File

@@ -0,0 +1,329 @@
/*
* 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.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"
#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 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 power down MALI
*
* @note powers down the MALI during MALI device driver is unloaded
*/
void malipmm_force_powerdown( 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 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__ */

View File

@@ -0,0 +1,243 @@
/*
* 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_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 */

View File

@@ -0,0 +1,155 @@
/*
* 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_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__ */

View File

@@ -0,0 +1,81 @@
/*
* 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_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.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

View File

@@ -0,0 +1,62 @@
/*
* 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_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__ */

View File

@@ -0,0 +1,461 @@
/*
* 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_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 ) )
{
/* 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;
}
}
/* 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 ) )
{
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_osk_pmm_power_down_done( 0 );
break;
}
}
_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 ) )
{
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 */

View File

@@ -0,0 +1,80 @@
/*
* 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_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__ */

View File

@@ -0,0 +1,718 @@
/*
* 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_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_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_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
{
#if !MALI_PMM_NO_PMU
err = mali_platform_powerdown( pmm->cores_pend_down );
#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_powered &= ~(pmm->cores_pend_down);
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
{
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_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 = mali_platform_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 = mali_platform_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 */

View File

@@ -0,0 +1,295 @@
/*
* 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_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 );
/** @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__ */

View File

@@ -0,0 +1,66 @@
/*
* 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_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__ */