1019 lines
29 KiB
C
1019 lines
29 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.h"
|
|
#include "esif_lf_action.h"
|
|
#include "esif_ipc.h"
|
|
#include "esif_queue.h"
|
|
#include "esif_hash_table.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 ESIF_DEBUG_MODULE ESIF_DEBUG_MOD_ELF
|
|
|
|
#define INIT_DEBUG 0
|
|
#define LF_DEBUG 1
|
|
#define DECODE_DEBUG 2
|
|
#define EVENT_DEBUG 3
|
|
|
|
#define ESIF_TRACE_DYN_INIT(format, ...) \
|
|
ESIF_TRACE_DYN(ESIF_DEBUG_MOD_ELF, INIT_DEBUG, format, ##__VA_ARGS__)
|
|
#define ESIF_TRACE_DYN_LF(format, ...) \
|
|
ESIF_TRACE_DYN(ESIF_DEBUG_MOD_ELF, LF_DEBUG, format, ##__VA_ARGS__)
|
|
#define ESIF_TRACE_DYN_DECODE(format, ...) \
|
|
ESIF_TRACE_DYN(ESIF_DEBUG_MOD_ELF, DECODE_DEBUG, format, ##__VA_ARGS__)
|
|
#define ESIF_TRACE_DYN_EVENT(format, ...) \
|
|
ESIF_TRACE_DYN(ESIF_DEBUG_MOD_ELF, EVENT_DEBUG, format, ##__VA_ARGS__)
|
|
|
|
|
|
/* List of Init/Exit Functions to be called during esif_lf_init/exit */
|
|
typedef enum esif_rc (*esif_init_callback)(void);
|
|
typedef void (*esif_exit_callback)(void);
|
|
struct esif_init_module {
|
|
esif_init_callback init_func;
|
|
esif_exit_callback exit_func;
|
|
u32 started;
|
|
};
|
|
static struct esif_init_module esif_initializers[] = {
|
|
{esif_lf_pm_init, esif_lf_pm_exit}, /* Always First */
|
|
{esif_action_acpi_init, esif_action_acpi_exit},
|
|
{esif_action_code_init, esif_action_code_exit},
|
|
{esif_action_const_init, esif_action_const_exit},
|
|
{esif_action_mmio_init, esif_action_mmio_exit},
|
|
{esif_action_msr_init, esif_action_msr_exit},
|
|
{esif_action_systemio_init, esif_action_systemio_exit},
|
|
{esif_action_var_init, esif_action_var_exit},
|
|
{esif_action_mbi_init, esif_action_mbi_exit},
|
|
{esif_data_init, esif_data_exit},
|
|
{esif_queue_init, esif_queue_exit},
|
|
{esif_event_init, esif_event_exit},
|
|
{esif_link_list_init, esif_link_list_exit},
|
|
{esif_hash_table_init, esif_hash_table_exit},
|
|
{esif_dsp_init, esif_dsp_exit},
|
|
{esif_command_init, esif_command_exit} /* Always Last */
|
|
};
|
|
static enum esif_rc esif_start_initializers(void);
|
|
static void esif_stop_initializers(void);
|
|
|
|
/*
|
|
*******************************************************************************
|
|
** PRIVATE
|
|
*******************************************************************************
|
|
*/
|
|
#define GFX_DTS_TEMPERATURE_MAX 207
|
|
#define GFX_DTS_TEMPERATURE_MIN 127
|
|
|
|
#define GFX_DEGREE_TEMPERATURE_MAX 90
|
|
#define GFX_DEGREE_TEMPERATURE_MIN 10
|
|
|
|
/*
|
|
* DTS to Degree Celcius conversion with respect to table below.
|
|
* Example : Temperature in Degree for DTS temperature of 157 will be calcuate
|
|
* as DTSToCelcius(157) = ((GFX_DEGREE_TEMPERATRUE_MIN == 10)+
|
|
* ((GFX_DTS_TEMPERATRUE_MAX == 207)- (degreeTemp == 157))) = 60, which is
|
|
* correct in table below.
|
|
* Example :
|
|
* DTSToCelcius(180) = ((GFX_DEGREE_TEMPERATRUE_MIN == 10)+
|
|
* ((GFX_DTS_TEMPERATRUE_MAX == 207)- (degreeTemp == 180))) = 37, which is
|
|
* correct in table below.
|
|
*/
|
|
#define DTS_TO_CELCIUS(temp) ((GFX_DEGREE_TEMPERATURE_MIN) + \
|
|
((GFX_DTS_TEMPERATURE_MAX)-(temp)))
|
|
|
|
/*
|
|
* Thermistor xform example
|
|
* 1. Input EC is 586, output Kelvin is 3055
|
|
* 2. Input Kelvin in 3055, output EC is 586
|
|
*
|
|
* Lookup LPAT ACPI object = {{type, tempK}, {type, rawVal}} = {
|
|
* ...
|
|
* {{INT64, 2891}, {UINT64, 668}},
|
|
* {{INT64, 3031}, {UINT64, 612}}, <-- tempK[0], raw[0]
|
|
* {{INT64, 3081}, {UINT64, 560}}, <-- tempK[1], raw[1]
|
|
* {{INT64, 3131}, {UINT64, 508}},
|
|
* ... }
|
|
* Algorithm
|
|
* FP = (tempK[1] - tempK[0]) / (raw[0] - raw[1])
|
|
* = ( 3081 - 3031 ) / ( 612 - 560 )
|
|
* = 0.096153
|
|
*
|
|
* Kernel lacks of floating point, so make FP 1K bigger to keep precision.
|
|
* FP = FP * 1K = 961
|
|
*
|
|
* Kelvin = tempK[0] + ((raw[0] - input_EC) * FP ) / 1K
|
|
* = 3031 + (( 612 - 586 ) * 961) / 1K
|
|
* = 3031 + 24
|
|
* = 3055
|
|
#
|
|
* EC = raw[0] - ((input_K - tempK[0]) * 1K) / FP
|
|
* = 612 - ((3055 - 3031 ) * 1K) / 961
|
|
* = 615 - 26
|
|
* = 586
|
|
*
|
|
*/
|
|
static u32 esif_xform_lpat(int input, struct esif_lp_dsp *dsp_ptr,
|
|
enum esif_temperature_type input_type)
|
|
{
|
|
u8 *var_ptr = (u8 *) dsp_ptr->table;
|
|
long long temp[2] = {0, 0};
|
|
long long raw[2] = {0, 0};
|
|
long long fp = 0;
|
|
long long output = 0;
|
|
int i = 0;
|
|
int num_lpat = 0;
|
|
|
|
if (input_type != ESIF_TEMP_THERMISTOR && input_type != ESIF_TEMP_DECIK)
|
|
return 0;
|
|
/*
|
|
* VS 2012 doesn't support "typeof(((type *)0)->memb *)" generic type,
|
|
* so we can only use (long long *) instead.
|
|
*/
|
|
#define ESIF_VAR union esif_data_variant
|
|
#define var_offset(type, memb) ((size_t)&((type *)0)->memb)
|
|
#define var_sizeof(type, memb) (sizeof((type *)0)->memb)
|
|
#define val_var_offset(ptr, type, memb) *(long long *) \
|
|
(ptr + var_offset(type, memb))
|
|
#define next_val_var_offset(ptr, type, all_memb, memb) *(long long *) \
|
|
(ptr + var_offset(type, memb) + var_sizeof(type, all_memb))
|
|
|
|
if (0 == dsp_ptr->table_size)
|
|
goto exit;
|
|
|
|
num_lpat = dsp_ptr->table_size / sizeof(union esif_data_variant);
|
|
|
|
for (i=0; i < ((num_lpat/2) - 1); i++) {
|
|
temp[0] = val_var_offset(var_ptr, ESIF_VAR, integer.value);
|
|
raw[0] = next_val_var_offset(var_ptr, ESIF_VAR, integer,
|
|
integer.value);
|
|
|
|
/* Next element */
|
|
var_ptr += 2 * var_sizeof(ESIF_VAR, integer);
|
|
temp[1] = val_var_offset(var_ptr, ESIF_VAR, integer.value);
|
|
raw[1] = next_val_var_offset(var_ptr, ESIF_VAR, integer,
|
|
integer.value);
|
|
|
|
switch (input_type) {
|
|
case ESIF_TEMP_THERMISTOR:
|
|
/* Out of boundry check - too small */
|
|
if (i == 0 && input > raw[0])
|
|
goto exit;
|
|
/* LPAT entry lookup */
|
|
if (input <= raw[0] && input >= raw[1])
|
|
goto comp;
|
|
break;
|
|
case ESIF_TEMP_DECIK:
|
|
if (i == 0 && input < temp[0])
|
|
goto exit;
|
|
if (input >= temp[0] && input <= temp[1])
|
|
goto comp;
|
|
break;
|
|
default:
|
|
goto exit;
|
|
}
|
|
}
|
|
/* Out of boundry check - too large */
|
|
goto exit;
|
|
comp:
|
|
/* FP = 1K * ((temp1 - temp0) / (raw0 - raw1)) */
|
|
fp = (temp[1] - temp[0]) * 1000;
|
|
do_div(fp, (raw[0] - raw[1]));
|
|
|
|
switch (input_type) {
|
|
case ESIF_TEMP_THERMISTOR:
|
|
/* K = temp0 + ((raw0 - EC) * FP) / 1K */
|
|
output = (raw[0] - input) * fp;
|
|
do_div(output, 1000);
|
|
output = temp[0] - output;
|
|
break;
|
|
case ESIF_TEMP_DECIK:
|
|
/* EC = raw0 - ((K - temp0) * 1K) / FP */
|
|
output = (input - temp[0]) * 1000;
|
|
do_div(output, fp);
|
|
output = raw[0] - output;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
exit:
|
|
ESIF_TRACE_DYN_TEMP("%s: input %d dsp.table_size %u num_plat %d "
|
|
"LPAT {temp0 %u, raw0 %u}, {temp1 %u, raw1 %u} output %llu\n",
|
|
ESIF_FUNC, input, dsp_ptr->table_size, num_lpat,
|
|
(u32)temp[0], (u32)raw[0], (u32)temp[1], (u32)raw[1], output);
|
|
return (u32) output;
|
|
}
|
|
|
|
|
|
/* DTS Counter | Temperature
|
|
* Value | Degree Celcius
|
|
* 127 | 90
|
|
* 137 | 80
|
|
* 147 | 70
|
|
* 157 | 60
|
|
* 167 | 50
|
|
* 177 | 40
|
|
* 187 | 30
|
|
* 197 | 20
|
|
* 207 | 10
|
|
*/
|
|
|
|
static enum esif_rc esif_xform_temp(
|
|
const enum esif_temperature_type type,
|
|
esif_temp_t *temp_ptr,
|
|
const enum esif_action_type action,
|
|
const struct esif_lp_dsp *dsp_ptr,
|
|
const struct esif_lp_primitive *primitive_ptr,
|
|
const struct esif_lp *lp_ptr
|
|
)
|
|
{
|
|
enum esif_rc rc = ESIF_OK;
|
|
enum esif_temperature_type temp_in_type = type;
|
|
enum esif_temperature_type temp_out_type = type;
|
|
struct esif_cpc_algorithm *algo_ptr = dsp_ptr->get_algorithm(
|
|
dsp_ptr,
|
|
action);
|
|
enum esif_primitive_opcode opcode = primitive_ptr->opcode;
|
|
|
|
esif_temp_t temp_in;
|
|
esif_temp_t temp_out;
|
|
|
|
if (algo_ptr == NULL)
|
|
return ESIF_E_NEED_ALGORITHM;
|
|
|
|
temp_in = *temp_ptr;
|
|
temp_out = *temp_ptr;
|
|
|
|
switch (algo_ptr->temp_xform) {
|
|
|
|
case ESIF_ALGORITHM_TYPE_TEMP_DECIK:
|
|
|
|
/* Convert Temp before/after ACPI action
|
|
* For Get:
|
|
* From reading from APCI device driver in Kelvin
|
|
* To normalized temp (C) back to user response buffer
|
|
* For Set:
|
|
* From user request buffer in Kelvin or Celsius
|
|
* To Kelvin passing to ACPI device driver to set
|
|
*/
|
|
ESIF_TRACE_DYN_TEMP(
|
|
"%s: using algorithm DeciK (%s), for ACPI temp\n",
|
|
ESIF_FUNC,
|
|
esif_algorithm_type_str(algo_ptr->temp_xform));
|
|
if (opcode == ESIF_PRIMITIVE_OP_GET) {
|
|
temp_in_type = ESIF_TEMP_DECIK;
|
|
temp_out_type = type;
|
|
/* Normalized from Kelvin */
|
|
esif_convert_temp(temp_in_type, temp_out_type,
|
|
&temp_out);
|
|
} else {/* ESIF_PRIMITIVE_OP_SET */
|
|
temp_in_type = type;
|
|
temp_out_type = ESIF_TEMP_DECIK;
|
|
/* Normalized to Kelvin */
|
|
esif_convert_temp(temp_in_type, temp_out_type,
|
|
&temp_out);
|
|
}
|
|
break;
|
|
|
|
case ESIF_ALGORITHM_TYPE_TEMP_TJMAX_CORE:
|
|
{
|
|
/* LIFU Get TJMAX from GET_PROC_TJ_MAX */
|
|
const struct esif_lp_domain *lpd_ptr = NULL;
|
|
u32 tjmax = 0;
|
|
|
|
/*
|
|
* TjMax Only Lives In Doman 0 (D0) Level, If Found,
|
|
* Assign Tjmax, Or Use A Default Value (TODO:100??)
|
|
*/
|
|
lpd_ptr = &lp_ptr->domains[0];
|
|
tjmax = (lpd_ptr->temp_tjmax) ? lpd_ptr->temp_tjmax : 100;
|
|
|
|
ESIF_TRACE_DYN_TEMP(
|
|
"%s: using algorithm Tjmax %d %s, cached Tjmax %d,"
|
|
" for CORE MSR temp\n",
|
|
ESIF_FUNC,
|
|
tjmax,
|
|
esif_algorithm_type_str(algo_ptr->temp_xform),
|
|
lpd_ptr->temp_tjmax);
|
|
|
|
if (opcode == ESIF_PRIMITIVE_OP_GET) {
|
|
temp_out = (temp_out > tjmax) ? 0 : tjmax - temp_out;
|
|
temp_in_type = ESIF_TEMP_C;
|
|
temp_out_type = type;
|
|
esif_convert_temp(temp_in_type, temp_out_type,
|
|
&temp_out);
|
|
} else {
|
|
temp_in_type = type;
|
|
temp_out_type = ESIF_TEMP_C;
|
|
esif_convert_temp(temp_in_type, temp_out_type,
|
|
&temp_out);
|
|
temp_out = (temp_out > tjmax) ? 0 : tjmax - temp_out;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ESIF_ALGORITHM_TYPE_TEMP_PCH_CORE:
|
|
{
|
|
ESIF_TRACE_DYN_TEMP(
|
|
"%s: using algorithm %s, for PCH MMIO temp\n",
|
|
ESIF_FUNC,
|
|
esif_algorithm_type_str(algo_ptr->temp_xform));
|
|
|
|
if (opcode == ESIF_PRIMITIVE_OP_GET) {
|
|
temp_out = (temp_out / 2) - 50;
|
|
temp_in_type = ESIF_TEMP_C;
|
|
temp_out_type = type;
|
|
esif_convert_temp(temp_in_type, temp_out_type,
|
|
&temp_out);
|
|
} else {
|
|
temp_in_type = type;
|
|
temp_out_type = ESIF_TEMP_C;
|
|
esif_convert_temp(temp_in_type, temp_out_type,
|
|
&temp_out);
|
|
temp_out = (temp_out / 2) - 50;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ESIF_ALGORITHM_TYPE_TEMP_TJMAX_ATOM:
|
|
{
|
|
const struct esif_lp_domain *lpd_ptr = NULL;
|
|
u32 tjmax = 0;
|
|
|
|
/*
|
|
* TjMax Only Lives In Doman 0 (D0) Level, If Found,
|
|
* Assign Tjmax, Or Use A Default Value (TODO:100??)
|
|
*/
|
|
lpd_ptr = &lp_ptr->domains[0];
|
|
tjmax = (lpd_ptr->temp_tjmax) ? lpd_ptr->temp_tjmax : 100;
|
|
|
|
ESIF_TRACE_DYN_TEMP(
|
|
"%s: using algorithm %s tjmax %d, cached tjmax %d, for ATOM MSR temp\n",
|
|
ESIF_FUNC,
|
|
esif_algorithm_type_str(algo_ptr->temp_xform),
|
|
tjmax,
|
|
lpd_ptr->temp_tjmax);
|
|
|
|
if (opcode == ESIF_PRIMITIVE_OP_GET) {
|
|
u32 temp = 0;
|
|
/* Algorithm taken from 7.0 DPTF Code */
|
|
|
|
/* This will read the current temperature from MSR.
|
|
* Please refer BWG for details. Valid temp. range is
|
|
* -20 deg to 90 deg.
|
|
*/
|
|
temp = ((temp_out >> 16) & 0x007F);
|
|
|
|
/*
|
|
* Bit 4 is set OR Bit 31 is not set --- indicates above
|
|
* 90 deg temperature
|
|
*/
|
|
if ((temp_out & 0x10) ||
|
|
((temp_out & 0x80000000) == 0x0)) {
|
|
temp_out = tjmax + temp;
|
|
} else {
|
|
temp_out = tjmax - temp;
|
|
}
|
|
|
|
temp_in_type = ESIF_TEMP_C;
|
|
temp_out_type = type;
|
|
esif_convert_temp(temp_in_type, temp_out_type,
|
|
&temp_out);
|
|
} else {
|
|
temp_in_type = type;
|
|
temp_out_type = ESIF_TEMP_C;
|
|
esif_convert_temp(temp_in_type, temp_out_type,
|
|
&temp_out);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ESIF_ALGORITHM_TYPE_TEMP_DTS_ATOM:
|
|
{
|
|
u32 tjmax = dsp_ptr->get_temp_tc1(dsp_ptr, action);
|
|
ESIF_TRACE_DYN_TEMP(
|
|
"%s: using algorithm %s, tjmax %d for ATOM\n",
|
|
ESIF_FUNC,
|
|
esif_algorithm_type_str(algo_ptr->temp_xform),
|
|
tjmax);
|
|
if (opcode == ESIF_PRIMITIVE_OP_GET) {
|
|
temp_out = DTS_TO_CELCIUS(temp_out) + tjmax - 90;
|
|
temp_in_type = ESIF_TEMP_C;
|
|
temp_out_type = type;
|
|
esif_convert_temp(temp_in_type, temp_out_type,
|
|
&temp_out);
|
|
} else {/* ESIF_PRIMITIVE_OP_SET */
|
|
temp_in_type = type;
|
|
temp_out_type = ESIF_TEMP_C;
|
|
esif_convert_temp(temp_in_type, temp_out_type,
|
|
&temp_out);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ESIF_ALGORITHM_TYPE_TEMP_NONE:
|
|
ESIF_TRACE_DYN_TEMP(
|
|
"%s: using algorithm none (%s), for Code and "
|
|
"Konst temp\n",
|
|
ESIF_FUNC,
|
|
esif_algorithm_type_str(algo_ptr->temp_xform));
|
|
if (opcode == ESIF_PRIMITIVE_OP_GET) {
|
|
temp_in_type = ESIF_TEMP_C;
|
|
temp_out_type = type;
|
|
esif_convert_temp(temp_in_type, temp_out_type,
|
|
&temp_out);
|
|
} else {/* ESIF_PRIMITIVE_OP_SET */
|
|
temp_in_type = type;
|
|
temp_out_type = ESIF_TEMP_C;
|
|
esif_convert_temp(temp_in_type, temp_out_type,
|
|
&temp_out);
|
|
}
|
|
break;
|
|
|
|
case ESIF_ALGORITHM_TYPE_TEMP_LPAT:
|
|
ESIF_TRACE_DYN_TEMP(
|
|
"%s: using algorithm %s temp_in %u\n", ESIF_FUNC,
|
|
esif_algorithm_type_str(algo_ptr->temp_xform),
|
|
temp_in);
|
|
|
|
if (opcode == ESIF_PRIMITIVE_OP_GET) {
|
|
temp_out = esif_xform_lpat(temp_in, lp_ptr->dsp_ptr,
|
|
ESIF_TEMP_THERMISTOR);
|
|
|
|
temp_in_type = ESIF_TEMP_DECIK;
|
|
temp_out_type = type;
|
|
esif_convert_temp(temp_in_type, temp_out_type,
|
|
&temp_out);
|
|
} else { /*ESIF_PRIMITIVE_OP_SET */
|
|
temp_in_type = type;
|
|
temp_out_type = ESIF_TEMP_DECIK;
|
|
/* Normalized to Kelvin */
|
|
esif_convert_temp(temp_in_type, temp_out_type,
|
|
&temp_out);
|
|
|
|
temp_out = esif_xform_lpat(temp_in, lp_ptr->dsp_ptr,
|
|
ESIF_TEMP_DECIK);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ESIF_TRACE_DYN_POWER(
|
|
"%s: Unknown algorithm (%s) to xform temp\n",
|
|
ESIF_FUNC,
|
|
esif_algorithm_type_str(algo_ptr->temp_xform));
|
|
rc = ESIF_E_UNSUPPORTED_ALGORITHM;
|
|
}
|
|
|
|
ESIF_TRACE_DYN_TEMP("%s: IN temp %u %s(%d)\n", ESIF_FUNC, temp_in,
|
|
esif_temperature_type_desc(
|
|
temp_in_type), temp_in_type);
|
|
|
|
ESIF_TRACE_DYN_TEMP("%s: OUT temp %u %s(%d)\n", ESIF_FUNC, temp_out,
|
|
esif_temperature_type_desc(
|
|
temp_out_type), temp_out_type);
|
|
|
|
*temp_ptr = temp_out;
|
|
return rc;
|
|
}
|
|
|
|
|
|
/* Power Transform */
|
|
static enum esif_rc esif_xform_power(
|
|
const enum esif_power_unit_type type,
|
|
esif_power_t *power_ptr,
|
|
const enum esif_action_type action,
|
|
const struct esif_lp_dsp *dsp_ptr,
|
|
const enum esif_primitive_opcode opcode
|
|
)
|
|
{
|
|
enum esif_power_unit_type power_in_type = type;
|
|
enum esif_power_unit_type power_out_type = type;
|
|
enum esif_rc rc = ESIF_OK;
|
|
struct esif_cpc_algorithm *algo_ptr = NULL;
|
|
esif_power_t power_in;
|
|
esif_power_t power_out;
|
|
|
|
algo_ptr = dsp_ptr->get_algorithm(dsp_ptr, action);
|
|
if (algo_ptr == NULL)
|
|
return ESIF_E_NEED_ALGORITHM;
|
|
|
|
power_in = *power_ptr;
|
|
power_out = *power_ptr;
|
|
|
|
switch (algo_ptr->power_xform) {
|
|
case ESIF_ALGORITHM_TYPE_POWER_DECIW:
|
|
ESIF_TRACE_DYN_POWER(
|
|
"%s: using algorithm DeciW (%s), for ACPI power\n",
|
|
ESIF_FUNC,
|
|
esif_algorithm_type_str(algo_ptr->power_xform));
|
|
if (opcode == ESIF_PRIMITIVE_OP_GET) {
|
|
/* Tenths Of A Watt To Milli Watts */
|
|
power_in_type = ESIF_POWER_DECIW;
|
|
power_out_type = type;
|
|
/* Normalized from DeciW */
|
|
esif_convert_power(power_in_type,
|
|
power_out_type,
|
|
&power_out);
|
|
} else {
|
|
/* Milli Watts To Tenths Of A Watt */
|
|
power_in_type = type;
|
|
power_out_type = ESIF_POWER_DECIW;
|
|
/* Normalized to DeciW */
|
|
esif_convert_power(power_in_type,
|
|
power_out_type,
|
|
&power_out);
|
|
}
|
|
break;
|
|
|
|
case ESIF_ALGORITHM_TYPE_POWER_MILLIW:
|
|
ESIF_TRACE_DYN_POWER(
|
|
"%s: using algorithm MillW (%s), for Code and Konst power\n",
|
|
ESIF_FUNC,
|
|
esif_algorithm_type_str(algo_ptr->power_xform));
|
|
if (opcode == ESIF_PRIMITIVE_OP_GET) {
|
|
power_in_type = ESIF_POWER_MILLIW;
|
|
power_out_type = type;
|
|
esif_convert_power(power_in_type,
|
|
power_out_type,
|
|
&power_out);
|
|
} else {
|
|
/* Milli Watts To Tenths Of A Watt */
|
|
power_in_type = type;
|
|
power_out_type = ESIF_POWER_MILLIW;
|
|
esif_convert_power(power_in_type,
|
|
power_out_type,
|
|
&power_out);
|
|
}
|
|
break;
|
|
|
|
case ESIF_ALGORITHM_TYPE_POWER_UNIT_ATOM:
|
|
|
|
/*
|
|
* Power 1mw * (2 ^ RAPL_POWER_UNIT)
|
|
* RAPL_POWER_UNIT = 5 example 1mw * (2 ^5) = 32mw
|
|
*/
|
|
ESIF_TRACE_DYN_POWER(
|
|
"%s: using algorithm %s, for hardware power\n",
|
|
ESIF_FUNC,
|
|
esif_algorithm_type_str(algo_ptr->power_xform));
|
|
/* Hardware */
|
|
if (opcode == ESIF_PRIMITIVE_OP_GET) {
|
|
power_in_type = ESIF_POWER_UNIT_ATOM;
|
|
power_out_type = type;
|
|
/* Normalized from hardware */
|
|
esif_convert_power(power_in_type,
|
|
power_out_type,
|
|
&power_out);
|
|
} else {
|
|
power_in_type = type;
|
|
power_out_type = ESIF_POWER_UNIT_ATOM;
|
|
/* Normalized to hardware */
|
|
esif_convert_power(power_in_type,
|
|
power_out_type,
|
|
&power_out);
|
|
}
|
|
break;
|
|
|
|
case ESIF_ALGORITHM_TYPE_POWER_UNIT_CORE:
|
|
|
|
/*
|
|
* Power 1000mw / (2 ^ RAPL_POWER_UNIT)
|
|
* RAPL_POWER_LIMIT = 3 example 1000mw / (2 ^3) = 125mw
|
|
*/
|
|
ESIF_TRACE_DYN_POWER(
|
|
"%s: using algorithm %s, for hardware power\n",
|
|
ESIF_FUNC,
|
|
esif_algorithm_type_str(algo_ptr->power_xform));
|
|
/* Hardware */
|
|
if (opcode == ESIF_PRIMITIVE_OP_GET) {
|
|
power_in_type = ESIF_POWER_UNIT_CORE;
|
|
power_out_type = type;
|
|
/* Normalized from hardware */
|
|
esif_convert_power(power_in_type,
|
|
power_out_type,
|
|
&power_out);
|
|
} else {
|
|
/* Milliwatts To hardware */
|
|
power_in_type = type;
|
|
power_out_type = ESIF_POWER_UNIT_CORE;
|
|
/* Normalized to OctaW */
|
|
esif_convert_power(power_in_type,
|
|
power_out_type,
|
|
&power_out);
|
|
}
|
|
break;
|
|
|
|
case ESIF_ALGORITHM_TYPE_POWER_NONE:
|
|
/* No algorithm specified, do not perform any xform */
|
|
ESIF_TRACE_DYN_POWER(
|
|
"%s: using algorithm NONE (%s), no xform performed\n",
|
|
ESIF_FUNC,
|
|
esif_algorithm_type_str(algo_ptr->power_xform));
|
|
break;
|
|
|
|
default:
|
|
ESIF_TRACE_DYN_POWER(
|
|
"%s: Unknown algorithm (%s) to xform power\n",
|
|
ESIF_FUNC,
|
|
esif_algorithm_type_str(algo_ptr->power_xform));
|
|
rc = ESIF_E_UNSUPPORTED_ALGORITHM;
|
|
}
|
|
|
|
ESIF_TRACE_DYN_POWER("%s: IN power %u %s(%d)\n", ESIF_FUNC, power_in,
|
|
esif_power_unit_desc(power_in_type),
|
|
power_in_type);
|
|
|
|
ESIF_TRACE_DYN_POWER("%s: OUT power %u %s(%d)\n", ESIF_FUNC, power_out,
|
|
esif_power_unit_desc(
|
|
power_out_type), power_out_type);
|
|
|
|
*power_ptr = power_out;
|
|
return rc;
|
|
}
|
|
|
|
|
|
/* Execute Xform Function */
|
|
enum esif_rc esif_execute_xform_func(
|
|
const struct esif_lp *lp_ptr,
|
|
const struct esif_lp_primitive *primitive_ptr,
|
|
const enum esif_action_type action,
|
|
const enum esif_data_type data_type,
|
|
u64 *temp_ptr
|
|
)
|
|
{
|
|
enum esif_rc rc = ESIF_E_XFORM_NOT_AVAILABLE;
|
|
|
|
switch (data_type) {
|
|
case ESIF_DATA_TEMPERATURE:
|
|
if (lp_ptr->xform_temp != NULL)
|
|
rc = lp_ptr->xform_temp(NORMALIZE_TEMP_TYPE,
|
|
(esif_temp_t *) temp_ptr,
|
|
action,
|
|
lp_ptr->dsp_ptr,
|
|
primitive_ptr,
|
|
lp_ptr);
|
|
break;
|
|
case ESIF_DATA_POWER:
|
|
if (lp_ptr->xform_power != NULL)
|
|
rc = lp_ptr->xform_power(NORMALIZE_POWER_UNIT_TYPE,
|
|
(esif_power_t *) temp_ptr,
|
|
action,
|
|
lp_ptr->dsp_ptr,
|
|
primitive_ptr->opcode);
|
|
break;
|
|
default:
|
|
rc = ESIF_E_UNSUPPORTED_ACTION_TYPE;
|
|
break;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
*******************************************************************************
|
|
** PUBLIC
|
|
*******************************************************************************
|
|
*/
|
|
|
|
/* ESIF Stats */
|
|
struct esif_memory_stats g_memstat = {0};
|
|
esif_ccb_lock_t g_memstat_lock;
|
|
|
|
/* ESIF Memory */
|
|
struct esif_ccb_mempool *g_mempool[ESIF_MEMPOOL_TYPE_MAX] = {0};
|
|
esif_ccb_lock_t g_mempool_lock;
|
|
|
|
struct esif_ccb_memtype *g_memtype[ESIF_MEMTYPE_TYPE_MAX] = {0};
|
|
esif_ccb_lock_t g_memtype_lock;
|
|
|
|
/* Register Participant */
|
|
enum esif_rc esif_lf_register_participant(struct esif_participant_iface *pi_ptr)
|
|
{
|
|
enum esif_rc rc = ESIF_OK;
|
|
struct esif_lp *lp_ptr = NULL;
|
|
ESIF_TRACE_DYN_LF("Register Send_Event Handler\n");
|
|
|
|
ESIF_TRACE_DYN_DECODE(
|
|
"Version: %d\n"
|
|
"GUID: %08x.%08x.%08x.%08x\n"
|
|
"Enumerator: %s(%d)\n"
|
|
"Flags: %08x\n"
|
|
"Name: %s\n"
|
|
"Driver Name: %s\n"
|
|
"Device Name: %s\n"
|
|
"Device Path: %s\n"
|
|
"ACPI Device: %s\n"
|
|
"ACPI Scope: %s\n"
|
|
"ACPI UID: %s\n"
|
|
"ACPI Type: %x\n"
|
|
"PCI Vendor: %x\n"
|
|
"PCI Device: %x\n"
|
|
"PCI Bus: %x\n"
|
|
"PCI Bus Device: %x\n"
|
|
"PCI Function: %x\n"
|
|
"PCI Revision: %x\n"
|
|
"PCI Class: %x\n"
|
|
"PCI SubClass: %x\n"
|
|
"PCI ProgIF: %x\n",
|
|
pi_ptr->version,
|
|
*((int *)&pi_ptr->class_guid[0]),
|
|
*((int *)&pi_ptr->class_guid[4]),
|
|
*((int *)&pi_ptr->class_guid[8]),
|
|
*((int *)&pi_ptr->class_guid[12]),
|
|
esif_participant_enum_str(pi_ptr->enumerator),
|
|
pi_ptr->enumerator,
|
|
pi_ptr->flags,
|
|
pi_ptr->name,
|
|
pi_ptr->driver_name,
|
|
pi_ptr->device_name,
|
|
pi_ptr->device_path,
|
|
pi_ptr->acpi_device,
|
|
pi_ptr->acpi_scope,
|
|
pi_ptr->acpi_uid,
|
|
pi_ptr->acpi_type,
|
|
pi_ptr->pci_vendor,
|
|
pi_ptr->pci_device,
|
|
pi_ptr->pci_bus,
|
|
pi_ptr->pci_bus_device,
|
|
pi_ptr->pci_function,
|
|
pi_ptr->pci_revision,
|
|
pi_ptr->pci_class,
|
|
pi_ptr->pci_sub_class,
|
|
pi_ptr->pci_prog_if);
|
|
|
|
lp_ptr = esif_lf_pm_lp_create(pi_ptr);
|
|
if (NULL == lp_ptr) {
|
|
rc = ESIF_E_NO_CREATE;
|
|
goto exit;
|
|
}
|
|
|
|
pi_ptr->send_event = esif_lf_event;
|
|
lp_ptr->xform_temp = esif_xform_temp;
|
|
lp_ptr->xform_power = esif_xform_power;
|
|
|
|
/* Transition State To Registering Next State Need DSP */
|
|
rc = esif_lf_pm_lp_set_state(lp_ptr,
|
|
ESIF_PM_PARTICIPANT_STATE_REGISTERING);
|
|
if (ESIF_OK != rc)
|
|
goto exit;
|
|
|
|
/* Notify Upper Framework */
|
|
rc = esif_lf_event(pi_ptr, ESIF_EVENT_PARTICIPANT_CREATE, 'NA', NULL);
|
|
if (ESIF_OK != rc)
|
|
goto exit;
|
|
|
|
/* Notify Driver */
|
|
pi_ptr->recv_event(ESIF_EVENT_PARTICIPANT_CREATE, 'NA', NULL);
|
|
if (ESIF_OK != rc)
|
|
goto exit;
|
|
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
|
|
/* Unregister All Participants */
|
|
void esif_lf_unregister_all_participants()
|
|
{
|
|
u8 i = 0;
|
|
struct esif_participant_iface *pi_ptr = NULL;
|
|
|
|
for (i = 0; i < MAX_PARTICIPANT_ENTRY; i++) {
|
|
pi_ptr = esif_lf_pm_pi_get_by_instance_id(i);
|
|
if (pi_ptr != NULL)
|
|
esif_lf_unregister_participant(pi_ptr);
|
|
}
|
|
}
|
|
|
|
|
|
/* Unregister Participant */
|
|
enum esif_rc esif_lf_unregister_participant(
|
|
struct esif_participant_iface *pi_ptr)
|
|
{
|
|
enum esif_rc rc = ESIF_OK;
|
|
struct esif_lp *lp_ptr = esif_lf_pm_lp_get_by_pi(pi_ptr);
|
|
|
|
if (NULL == lp_ptr) {
|
|
rc = ESIF_E_PARTICIPANT_NOT_FOUND;
|
|
goto exit;
|
|
}
|
|
|
|
if (NULL != lp_ptr->dsp_ptr)
|
|
esif_dsp_unload(lp_ptr);
|
|
|
|
ESIF_TRACE_DYN_LF("instance %d.\n", lp_ptr->instance);
|
|
|
|
/* Notify Upper Framework */
|
|
rc = esif_lf_event(lp_ptr->pi_ptr,
|
|
ESIF_EVENT_PARTICIPANT_UNREGISTER,
|
|
'NA',
|
|
NULL);
|
|
if (ESIF_OK != rc)
|
|
goto exit;
|
|
|
|
esif_lf_pm_lp_remove(lp_ptr);
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
/* Optional Low-Level ESIF LF OS Init.
|
|
* Call before any esif_ccb_malloc, esif_lf_init, or threads created
|
|
*/
|
|
static unsigned int os_init_count = 0;
|
|
void esif_lf_os_init()
|
|
{
|
|
if (!os_init_count) {
|
|
esif_ccb_lock_init(&g_memstat_lock);
|
|
os_init_count = 1;
|
|
} else {
|
|
os_init_count++;
|
|
}
|
|
}
|
|
|
|
/* Optional Low-Level ESIF LF OS Exit.
|
|
* Call after all esif_ccb_free, esif_lf_init, and threads exit
|
|
*/
|
|
void esif_lf_os_exit()
|
|
{
|
|
if (os_init_count) {
|
|
os_init_count--;
|
|
} else {
|
|
esif_ccb_lock_uninit(&g_memstat_lock);
|
|
os_init_count = 0;
|
|
}
|
|
}
|
|
|
|
/* Start all Initializers, Stopping and aborting all on first Error */
|
|
static enum esif_rc esif_start_initializers()
|
|
{
|
|
enum esif_rc rc = ESIF_OK;
|
|
int initializer_count = sizeof(esif_initializers) / sizeof(struct esif_init_module);
|
|
int j;
|
|
|
|
for (j = 0; j < initializer_count; j++) {
|
|
rc = (*esif_initializers[j].init_func)();
|
|
if (rc != ESIF_OK) {
|
|
esif_stop_initializers();
|
|
break;
|
|
}
|
|
esif_initializers[j].started = 1;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/* Stop all Initializers that have been Started */
|
|
static void esif_stop_initializers()
|
|
{
|
|
int initializer_count = sizeof(esif_initializers) / sizeof(struct esif_init_module);
|
|
int j;
|
|
|
|
/* Stop in reverse order */
|
|
for (j = initializer_count-1; j >= 0; j--) {
|
|
if (esif_initializers[j].started) {
|
|
(*esif_initializers[j].exit_func)();
|
|
esif_initializers[j].started = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ESIF LF Init */
|
|
enum esif_rc esif_lf_init(u32 debug_mask)
|
|
{
|
|
enum esif_rc rc = ESIF_OK;
|
|
esif_lf_os_init();
|
|
esif_ccb_mempool_init_tracking();
|
|
esif_ccb_memtype_init_tracking();
|
|
|
|
/* Static Debug Table */
|
|
esif_debug_init_module_categories();
|
|
if (0 != debug_mask) {
|
|
/* Modules */
|
|
esif_debug_set_modules(debug_mask);
|
|
/* CPC */
|
|
esif_debug_set_module_category(ESIF_DEBUG_MOD_CPC,
|
|
ESIF_TRACE_CATEGORY_DEFAULT |
|
|
0xff);
|
|
/* DSP */
|
|
esif_debug_set_module_category(ESIF_DEBUG_MOD_DSP,
|
|
ESIF_TRACE_CATEGORY_DEFAULT |
|
|
0xff);
|
|
}
|
|
|
|
ESIF_TRACE_DYN_INIT("%s: Initialize Eco-System Independent Framework\n",
|
|
ESIF_FUNC);
|
|
|
|
/* Call all Init functions listed in esif_initializers. Abort on 1st Error */
|
|
rc = esif_start_initializers();
|
|
return rc;
|
|
}
|
|
|
|
|
|
/* ESIF LF Exit */
|
|
void esif_lf_exit(void)
|
|
{
|
|
esif_lf_unregister_all_participants();
|
|
|
|
/* Call all Exit functions listed in esif_initializers */
|
|
esif_stop_initializers();
|
|
|
|
esif_ccb_mempool_uninit_tracking();
|
|
esif_ccb_memtype_uninit_tracking();
|
|
esif_lf_os_exit();
|
|
|
|
ESIF_TRACE_DYN_INIT("%s: Exit Eco-System Independent Framework\n",
|
|
ESIF_FUNC);
|
|
|
|
ESIF_TRACE_DEBUG("\nDUMP Memory Stats:\n"
|
|
"-----------------------\n");
|
|
ESIF_TRACE_DEBUG("MemAllocs: %d\n", g_memstat.allocs);
|
|
ESIF_TRACE_DEBUG("MemFrees: %d\n", g_memstat.frees);
|
|
ESIF_TRACE_DEBUG("MemInuse: %d\n",
|
|
g_memstat.allocs - g_memstat.frees);
|
|
ESIF_TRACE_DEBUG("MemPoolAllocs: %d\n", g_memstat.memPoolAllocs);
|
|
ESIF_TRACE_DEBUG("MemPoolFrees: %d\n", g_memstat.memPoolFrees);
|
|
ESIF_TRACE_DEBUG("MemPoolInuse: %d\n",
|
|
g_memstat.memPoolAllocs - g_memstat.memPoolFrees);
|
|
ESIF_TRACE_DEBUG("MemPoolObjAllocs: %d\n", g_memstat.memPoolObjAllocs);
|
|
ESIF_TRACE_DEBUG("MemPoolObjFrees: %d\n", g_memstat.memPoolObjFrees);
|
|
ESIF_TRACE_DEBUG("MemPoolObjInuse: %d\n",
|
|
g_memstat.memPoolObjAllocs -
|
|
g_memstat.memPoolObjFrees);
|
|
ESIF_TRACE_DEBUG("MemTypeAllocs: %d\n", g_memstat.memTypeAllocs);
|
|
ESIF_TRACE_DEBUG("MemTypeFrees: %d\n", g_memstat.memTypeFrees);
|
|
ESIF_TRACE_DEBUG("MemTypeInuse: %d\n",
|
|
g_memstat.memTypeAllocs - g_memstat.memTypeFrees);
|
|
ESIF_TRACE_DEBUG("MemTypeObjAllocs: %d\n", g_memstat.memTypeObjAllocs);
|
|
ESIF_TRACE_DEBUG("MemTypeObjFrees: %d\n", g_memstat.memTypeObjFrees);
|
|
ESIF_TRACE_DEBUG("MemTypeObjInuse: %d\n\n",
|
|
g_memstat.memTypeObjAllocs -
|
|
g_memstat.memTypeObjFrees);
|
|
|
|
if (0 != (g_memstat.allocs - g_memstat.frees)) {
|
|
ESIF_TRACE_DEBUG(
|
|
"!!!!!!!! POTENTIAL MEMORY LEAK DETECTED !!!!!!!!\n\n");
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/******************************************************************************/
|
|
/******************************************************************************/
|