/******************************************************************************* ** This file is provided under a dual BSD/GPLv2 license. When using or ** redistributing this file, you may do so under either license. ** ** GPL LICENSE SUMMARY ** ** Copyright (c) 2013 Intel Corporation All Rights Reserved ** ** This program is free software; you can redistribute it and/or modify it under ** the terms of version 2 of the GNU General Public License as published by the ** Free Software Foundation. ** ** This program is distributed in the hope that it will be useful, but WITHOUT ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ** FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ** details. ** ** You should have received a copy of the GNU General Public License along with ** this program; if not, write to the Free Software Foundation, Inc., ** 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ** The full GNU General Public License is included in this distribution in the ** file called LICENSE.GPL. ** ** BSD LICENSE ** ** Copyright (c) 2013 Intel Corporation All Rights Reserved ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are met: ** ** * Redistributions of source code must retain the above copyright notice, this ** list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright notice, ** this list of conditions and the following disclaimer in the documentation ** and/or other materials provided with the distribution. ** * Neither the name of Intel Corporation nor the names of its contributors may ** be used to endorse or promote products derived from this software without ** specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ** POSSIBILITY OF SUCH DAMAGE. ** *******************************************************************************/ #include "esif_lf_poll.h" #ifdef ESIF_ATTR_OS_WINDOWS /* * The Windows banned-API check header must be included after all other headers, * or issues can be identified against Windows SDK/DDK included headers which * we have no control over. */ #define _SDL_BANNED_RECOMMENDED #include "win\banned.h" #endif #define INIT_DEBUG 0 #define POLL_DEBUG 1 #define RAPL_DEBUG 2 #define ENERGY_DEBUG 3 #define ESIF_TRACE_DYN_INIT(format, ...) \ ESIF_TRACE_DYN(ESIF_DEBUG_MOD_POL, INIT_DEBUG, format, ##__VA_ARGS__) #define ESIF_TRACE_DYN_POLL(format, ...) \ ESIF_TRACE_DYN(ESIF_DEBUG_MOD_POL, POLL_DEBUG, format, ##__VA_ARGS__) #define ESIF_TRACE_DYN_RAPL(format, ...) \ ESIF_TRACE_DYN(ESIF_DEBUG_MOD_POL, RAPL_DEBUG, format, ##__VA_ARGS__) #define ESIF_TRACE_DYN_ENERGY(format, ...) \ ESIF_TRACE_DYN(ESIF_DEBUG_MOD_POL, ENERGY_DEBUG, format, ##__VA_ARGS__) /* TODO: Fix This */ int g_background = 1000;/* 1 Seconds */ /* Poll For Power and Send Event If Threshold Crossed */ static enum esif_rc esif_poll_power( struct esif_lp_domain *lpd_ptr ) { enum esif_rc rc = ESIF_OK; u32 energy_units = 0; u32 energy_units_used = 0; struct esif_primitive_tuple tuple = {GET_RAPL_ENERGY, lpd_ptr->id, 255}; struct esif_data req_data = {ESIF_DATA_VOID, NULL, 0, 0}; struct esif_data rsp_data = {ESIF_DATA_UINT32, &energy_units, sizeof(energy_units), 0}; rc = esif_execute_primitive(lpd_ptr->lp_ptr, &tuple, &req_data, &rsp_data, NULL); if (ESIF_OK != rc) goto exit; ESIF_TRACE_DYN_ENERGY( "GET_RAPL_ENERGY for %s energy_unit 0x%x rc %s(%d)\n", lpd_ptr->name_ptr, energy_units, esif_rc_str(rc), rc); /* Save the current and last RAPL energy counters */ lpd_ptr->rapl_energy_units_last = lpd_ptr->rapl_energy_units_current; lpd_ptr->rapl_energy_units_current = energy_units; /* Check For Wrap? */ if (lpd_ptr->rapl_energy_units_last > energy_units) { /* force I_AGAIN */ lpd_ptr->rapl_energy_units_last = 0; } else { energy_units_used = energy_units - lpd_ptr->rapl_energy_units_last; } /* TODO: Use a timestamp to make this as acurate as possible */ /* should be within a few usecs so good enough for now */ lpd_ptr->rapl_energy_units_per_sec = energy_units_used / (lpd_ptr->timer_period_msec / 1000); ESIF_TRACE_DYN_ENERGY( "id %s ENERGY units current %08X, last %08X, used %u, used/sec %d\n", lpd_ptr->name_ptr, lpd_ptr->rapl_energy_units_last, lpd_ptr->rapl_energy_units_current, energy_units_used, lpd_ptr->rapl_energy_units_per_sec); /* Take two samples to get the right rapl_power and send_event. */ if (lpd_ptr->rapl_energy_units_last == 0) goto exit; /* * Now Calculate Power */ if (lpd_ptr->rapl_energy_units_per_sec > 0) { u32 energy_joules = 0;/* default micro joules unit */ u32 power = 0; /* default power */ struct esif_lp_domain *lpd_d0_ptr = NULL; /* For Domain 0 */ /* * Don't HAVE Action Type Assume MSR since all processors * have this */ struct esif_cpc_algorithm *algo_ptr = lpd_ptr->lp_ptr->dsp_ptr->get_algorithm( lpd_ptr->lp_ptr->dsp_ptr, ESIF_ACTION_MSR); /* Power Unit Data Only Lives in Domain 0 (D0) */ lpd_d0_ptr = &lpd_ptr->lp_ptr->domains[0]; if (algo_ptr && ESIF_ALGORITHM_TYPE_POWER_UNIT_CORE == algo_ptr->power_xform) { if (lpd_d0_ptr->unit_energy) { energy_joules = 1000000 / (1 << lpd_d0_ptr->unit_energy); } else { /* 1000000uj / (2 ^ 14) = 61uj */ energy_joules = 61; } } if (algo_ptr && ESIF_ALGORITHM_TYPE_POWER_UNIT_ATOM == algo_ptr->power_xform) { if (lpd_d0_ptr->unit_energy) { energy_joules = (1 << lpd_d0_ptr->unit_energy); } else { /* 1uj * (2 ^ 5) = 32 uj */ energy_joules = 32; } } power = (lpd_ptr->rapl_energy_units_per_sec * energy_joules) / 1000; /* .001 of watt accuracy */ /* Normalized from DeciW */ esif_convert_power(ESIF_POWER_MILLIW, NORMALIZE_POWER_UNIT_TYPE, &power); ESIF_TRACE_DYN_RAPL("POWER %d %s(%d)\n", power, esif_power_unit_desc( NORMALIZE_POWER_UNIT_TYPE), NORMALIZE_POWER_UNIT_TYPE); lpd_ptr->rapl_power = power; /* * Now Check For Threshold */ ESIF_TRACE_DYN_RAPL( "THRESHOLD_CHECK hyst = %d aux0 = %d " "power = %d aux1 = %d units %s(%d)\n", lpd_ptr->power_hysteresis, lpd_ptr->power_aux0, power, lpd_ptr->power_aux1, esif_power_unit_desc( NORMALIZE_POWER_UNIT_TYPE), NORMALIZE_POWER_UNIT_TYPE); if (0 == lpd_ptr->power_aux0 && 0 == lpd_ptr->power_aux1) { /* Do Nothing */ } else { if (0 == lpd_ptr->power_aux0) { if (power > lpd_ptr->power_aux1) { lpd_ptr->lp_ptr->pi_ptr->send_event( lpd_ptr->lp_ptr->pi_ptr, ESIF_EVENT_DOMAIN_POWER_THRESHOLD_CROSSED, lpd_ptr->id, NULL); } else if (power < lpd_ptr->power_aux0 || power > lpd_ptr->power_aux1) { lpd_ptr->lp_ptr->pi_ptr->send_event( lpd_ptr->lp_ptr->pi_ptr, ESIF_EVENT_DOMAIN_POWER_THRESHOLD_CROSSED, lpd_ptr->id, NULL); } } } } exit: return rc; } /* Poll For Temperature and Send Event If Threshold Crossed */ static enum esif_rc esif_poll_temperature( struct esif_lp_domain *lpd_ptr ) { enum esif_rc rc = ESIF_OK; u32 temp = 0; struct esif_primitive_tuple tuple = {GET_TEMPERATURE, lpd_ptr->id, 255}; struct esif_data req_data = {ESIF_DATA_VOID, NULL, 0, 0}; struct esif_data rsp_data = {ESIF_DATA_TEMPERATURE, &temp, sizeof(temp), 0}; rc = esif_execute_primitive(lpd_ptr->lp_ptr, &tuple, &req_data, &rsp_data, NULL); if (ESIF_OK != rc) goto exit; ESIF_TRACE_DYN_TEMP("TEMPERATURE %d %s(%d)\n", temp, esif_temperature_type_desc(NORMALIZE_TEMP_TYPE), NORMALIZE_TEMP_TYPE); ESIF_TRACE_DYN_TEMP( "THRESHOLD_CHECK hyst = %d aux0 = %d temp = %d aux1 = %d units %s(%d)\n", lpd_ptr->temp_hysteresis, lpd_ptr->temp_aux0, temp, lpd_ptr->temp_aux1, esif_temperature_type_desc(NORMALIZE_TEMP_TYPE), NORMALIZE_TEMP_TYPE); if (((lpd_ptr->temp_aux0 != ESIF_DOMAIN_TEMP_INVALID) && (temp < lpd_ptr->temp_aux0 - lpd_ptr->temp_hysteresis)) || ((lpd_ptr->temp_aux1 != ESIF_DOMAIN_TEMP_INVALID) && (temp >= lpd_ptr->temp_aux1))) { lpd_ptr->lp_ptr->pi_ptr->send_event(lpd_ptr->lp_ptr->pi_ptr, ESIF_EVENT_DOMAIN_TEMP_THRESHOLD_CROSSED, lpd_ptr->id, NULL); ESIF_TRACE_DYN_TEMP("ESIF_EVENT_TEMP_THRESHOLD_CROSSED sent\n"); } exit: return rc; } void esif_poll( void *context_ptr ) { struct esif_lp_domain *lpd_ptr = (struct esif_lp_domain *)context_ptr; if (NULL == lpd_ptr) return; ESIF_TRACE_DYN_POLL("Timer %s:%s\n", lpd_ptr->lp_ptr->pi_name, lpd_ptr->name_ptr); /* No DSP No Work */ if (NULL != lpd_ptr->lp_ptr->dsp_ptr) { /* Do Work By Capability In Prioritized Order */ if (lpd_ptr->poll_mask & ESIF_POLL_POWER) esif_poll_power(lpd_ptr); if (lpd_ptr->poll_mask & ESIF_POLL_TEMPERATURE) esif_poll_temperature(lpd_ptr); } else { ESIF_TRACE_DYN_POLL("no DSP can't do work\n"); } } /* Start Poll */ void esif_poll_start( struct esif_lp_domain *lpd_ptr ) { enum esif_rc rc = ESIF_OK; if (ESIF_TRUE == lpd_ptr->poll || 0 == g_background) return; lpd_ptr->timer_period_msec = g_background; rc = esif_ccb_timer_init(&lpd_ptr->timer); if (ESIF_OK != rc) goto exit; rc = esif_ccb_timer_set_msec(&lpd_ptr->timer, lpd_ptr->timer_period_msec, ESIF_TRUE, esif_poll, lpd_ptr); if (ESIF_OK != rc) goto exit; ESIF_TRACE_DYN_POLL("Timer started for %s period %d\n", lpd_ptr->name_ptr, g_background); lpd_ptr->poll = ESIF_TRUE; exit: return; } /* Start All Poll Domain For Participants Instance */ void esif_poll_start_all( struct esif_lp *lp_ptr ) { u8 domain_index = 0; for (domain_index = 0; domain_index < lp_ptr->domain_count; domain_index++) esif_poll_start(&lp_ptr->domains[domain_index]); } /* Stop Poll */ void esif_poll_stop( struct esif_lp_domain *lpd_ptr ) { if (ESIF_FALSE == lpd_ptr->poll) return; esif_ccb_timer_kill(&lpd_ptr->timer); ESIF_TRACE_DYN_POLL("Timer stopped for %s period %d\n", lpd_ptr->name_ptr, g_background); /* Reset Power History */ lpd_ptr->rapl_energy_units_last = 0; lpd_ptr->rapl_energy_units_current = 0; lpd_ptr->rapl_power = 0; lpd_ptr->poll = ESIF_FALSE; } /* Initialize Poll Manager */ enum esif_rc esif_poll_init(void) { return ESIF_OK; } /* Exit Poll Manager */ void esif_poll_exit(void) { ESIF_TRACE_DYN_INIT("Exit Polling\n"); } /******************************************************************************/ /******************************************************************************/ /******************************************************************************/