satip-axe/kernel/drivers/stm/mali/common/pmm/mali_pmm.c
2015-03-26 17:24:57 +01:00

964 lines
26 KiB
C

/*
* 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 */