964 lines
26 KiB
C
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 */
|