Files
Chromebook-Device-Nyan-NVID…/drivers/platform/x86/intel_dptf/esif_lf_poll.c
2025-04-06 23:50:55 -05:00

402 lines
11 KiB
C

/*******************************************************************************
** 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");
}
/******************************************************************************/
/******************************************************************************/
/******************************************************************************/