diff options
author | William Bryan <wilbryan@us.ibm.com> | 2015-08-03 12:38:58 -0500 |
---|---|---|
committer | William A. Bryan <wilbryan@us.ibm.com> | 2015-08-03 15:32:27 -0500 |
commit | 420e6d248cc6d2b3c39bc3970e3bb6747b3bddc3 (patch) | |
tree | c9f6691eddba39193e39aa769367e1267fb9fc86 /src/occ_405/amec | |
parent | adade8c8ef30ed519322674c762d95663009c5d4 (diff) | |
download | talos-occ-420e6d248cc6d2b3c39bc3970e3bb6747b3bddc3.tar.gz talos-occ-420e6d248cc6d2b3c39bc3970e3bb6747b3bddc3.zip |
new ssx and lib files
Change-Id: I2328b1e86d59e3788910687d762fb70ec680058f
Reviewed-on: http://gfw160.aus.stglabs.ibm.com:8080/gerrit/19503
Reviewed-by: William A. Bryan <wilbryan@us.ibm.com>
Tested-by: William A. Bryan <wilbryan@us.ibm.com>
Diffstat (limited to 'src/occ_405/amec')
43 files changed, 15394 insertions, 0 deletions
diff --git a/src/occ_405/amec/amec_amester.c b/src/occ_405/amec/amec_amester.c new file mode 100755 index 0000000..c565912 --- /dev/null +++ b/src/occ_405/amec/amec_amester.c @@ -0,0 +1,1564 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/amec/amec_amester.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +//************************************************************************* +// Includes +//************************************************************************* +#include <occ_common.h> +#include <ssx.h> +#include <errl.h> // Error logging +#include <rtls.h> +#include <occ_service_codes.h> // for SSX_GENERIC_FAILURE +#include <sensor.h> +#include <amec_smh.h> +#include <amec_master_smh.h> +#include <amec_amester.h> +#include <amec_sys.h> +#include <trac.h> // For traces +#include <appletManager.h> +#include <sensorQueryList.h> +#include <proc_data.h> +#include <amec_parm.h> +#include <string.h> +#include <occ_sys_config.h> +#include <dcom.h> + +//************************************************************************* +// Externs +//************************************************************************* + +//************************************************************************* +// Macros +//************************************************************************* + +//************************************************************************* +// Defines/Enums +//************************************************************************* + +///Maximum size of trace buffer +#define AMEC_TB_2MS_SIZE_BYTES 8192 +#define AMEC_TB_250US_SIZE_BYTES 8192 +#define AMEC_TB_SIZE_BYTES (AMEC_TB_250US_SIZE_BYTES + AMEC_TB_2MS_SIZE_BYTES) + +///Maximum number of trace buffers we will support +#define AMEC_MAX_NUM_TB 2 + +///Maximum size of config info for 1 trace buffer +#define AMEC_TB_CONFIG_SIZE (MAX_SENSOR_NAME_SZ + 4) +#define MAX_NUM_CHIPS MAX_NUM_OCC + +//************************************************************************* +// Structures +//************************************************************************* + +//************************************************************************* +// Globals +//************************************************************************* + +// Each trace buffer should be aligned to 128 bytes in main memory because the +// block copy engine only copies multiples of 128 byte units. +// Make this a power of 2 (bytes) in size and aligned to 4 bytes. +DMA_BUFFER(UINT8 g_amec_tb_bytes[AMEC_TB_SIZE_BYTES]); + +// Array that maintains a list of all trace buffers built. +// NOTE: Must be in same order as AMEC_TB_ENUM +DMA_BUFFER(amec_tb_t g_amec_tb_list[AMEC_MAX_NUM_TB]) = { + //trace 2ms + [AMEC_TB_2MS] = { + "trace2ms", // name + AMEFP(500,0), // freq + (UINT8*)(UINT32)g_amec_tb_bytes, // bytes + AMEC_TB_2MS_SIZE_BYTES, // size + 0, // entry_size + 0, // entry_n + 0, // write + 0, // read + 0, // sensors_n + 0, // parm_n + {0}, // sensors_field[] + {0}, // sensors_num[] + {0} // parms_num[] + }, + // trace 250us + [AMEC_TB_250US] = { + "trace250us", // name + AMEFP(4000,0), // freq + (UINT8*)(UINT32)g_amec_tb_bytes+AMEC_TB_2MS_SIZE_BYTES, // bytes + AMEC_TB_250US_SIZE_BYTES, //size + 0, // entry_size + 0, // entry_n + 0, // write + 0, // read + 0, // sensors_n + 0, // parm_n + {0}, // sensors_field[] + {0}, // sensors_num[] + {0} // parms_num[] + } +}; + +//Throw a compiler error when the enum and array are not both updated +STATIC_ASSERT((AMEC_TB_NUMBER_OF_TRACES != (sizeof(g_amec_tb_list)/sizeof(amec_tb_t)))); + +///=1 signals a trace is being taken +UINT8 g_amec_tb_record=0; +///=1 signals continuous tracing +UINT8 g_amec_tb_continuous=0; //CL273 + +//************************************************************************* +// Function Prototypes +//************************************************************************* + +//************************************************************************* +// Functions +//************************************************************************* +// Function Specification +// +// Name: amester_get_sensor_info +// +// Description: Returns name, units, update frequency, and scalefactor for a sensor +// +// Task Flags: +// +// End Function Specification +static uint8_t amester_get_sensor_info( uint8_t* o_resp, uint16_t* io_resp_length, const uint8_t i_type, const uint16_t i_sensor) +{ + uint8_t l_rc = COMPCODE_NORMAL; // assume no error + sensor_t * l_sensor_ptr = NULL; + uint16_t l_numOfSensors = 1; + sensor_info_t l_sensorInfo; + errlHndl_t l_err = NULL; + OCC_APLT_STATUS_CODES l_status = 0; + uint16_t l_resp_length = 0; + + do + { + // Check o_resp and io_resp_length pointers + if( (o_resp == NULL) || + (io_resp_length == NULL) ) + { + l_rc = COMPCODE_UNSPECIFIED; + break; + } + + l_resp_length = *io_resp_length; + *io_resp_length = 0; + + if (i_sensor >= MAX_AMEC_SENSORS ) + { + l_rc = COMPCODE_PARAM_OUT_OF_RANGE; + break; + } + + l_sensor_ptr = getSensorByGsid(i_sensor); + if(l_sensor_ptr == NULL) + { + // Didn't find it + l_rc = COMPCODE_PARAM_OUT_OF_RANGE; + break; + } + + querySensorListAppletArg_t l_querySensorListAppletArg = { + i_sensor, // i_startGsid + 0, // i_present + SENSOR_TYPE_ALL, // i_type + SENSOR_LOC_ALL, // i_loc + &l_numOfSensors, // io_numOfSensors + NULL, // o_sensors + &l_sensorInfo // o_sensorInfoPtr + }; + + + //Call sensor query list applet + runApplet(OCC_APLT_SNSR_QUERY, // Applet enum Name + &l_querySensorListAppletArg, // Applet arguments + TRUE, // Blocking call? + NULL, // Applet finished semaphore + &l_err, // Error log handle + &l_status); // Error status + + if( NULL != l_err) + { + // Query failure, it should not happens + TRAC_ERR("Failed to run OCC_APLT_SNSR_QUERY applet. Error status is : 0x%x", l_status); + + // commit error log + commitErrl( &l_err ); + + l_rc = COMPCODE_UNSPECIFIED; + break; + } + + switch (i_type) + { + case AME_INFO_NAME: + { + char *src = l_sensorInfo.name; + char *dest = (char*)o_resp; + uint16_t l_length = strlen(src)+1; // add string terminator + + // Check length + if(l_resp_length < l_length) + { + l_rc = COMPCODE_PARAM_OUT_OF_RANGE; + break; + } + // Copy string + strcpy( dest, src ); + *io_resp_length = l_length; + break; + } + + case AME_INFO_UNITS: + { + char *src = l_sensorInfo.sensor.units; + char *dest = (char*)o_resp; + uint16_t l_length = strlen(src)+1; // add string terminator + + // Check length + if(l_resp_length < l_length) + { + l_rc = COMPCODE_PARAM_OUT_OF_RANGE; + break; + } + + // Copy string + strcpy( dest, src ); + *io_resp_length = l_length; + break; + } + + case AME_INFO_FREQ: + { + uint16_t l_length = sizeof( uint32_t); + + // Check length + if(l_resp_length < l_length) + { + l_rc = COMPCODE_PARAM_OUT_OF_RANGE; + break; + } + + *((uint32_t *)o_resp) = l_sensorInfo.sensor.freq; + *io_resp_length = l_length; + break; + } + + case AME_INFO_SCALE: + { + uint16_t l_length = sizeof( uint32_t); + + // Check length + if(l_resp_length < l_length) + { + l_rc = COMPCODE_PARAM_OUT_OF_RANGE; + break; + } + + *((uint32_t *)o_resp) = l_sensorInfo.sensor.scalefactor; + *io_resp_length = l_length; + break; + } + + case AME_INFO_ALL: //Added for AME API 2.16 + { + char *src = NULL; + char *dest = (char*)o_resp; + uint16_t l_length = strlen(l_sensorInfo.name) + 1 +\ + strlen(l_sensorInfo.sensor.units) + 1 + \ + sizeof(uint32_t) + sizeof(uint32_t); + // Check length + if(l_resp_length < l_length) + { + l_rc = COMPCODE_PARAM_OUT_OF_RANGE; + break; + } + + src = l_sensorInfo.name; + // Copy string + strcpy( dest, src ); + dest += strlen(src)+1; // add string terminator + + src = l_sensorInfo.sensor.units; + // Copy string + strcpy( dest, src ); + dest += strlen(src)+1; // add string terminator + + *((uint32_t *)dest) = l_sensorInfo.sensor.freq; + dest+= 4; + + *((uint32_t *)dest) = l_sensorInfo.sensor.scalefactor; + dest+= 4; + + *io_resp_length = (uint8_t) ((uint32_t)dest - (uint32_t)o_resp); + break; + } + + default: + l_rc = COMPCODE_PARAM_OUT_OF_RANGE; + } // End of switch case + } while (0); + return l_rc; +} + +// Function Specification +// +// Name: amester_api +// +// Description: amester entry point for ipmicommand 0x3C +// +// Task Flags: +// +// End Function Specification +uint8_t amester_api( const IPMIMsg_t * i_msg, + uint16_t * io_resp_length, + uint8_t * o_resp ) +{ + uint8_t l_rc = COMPCODE_NORMAL; + uint8_t l_temp_buffer[ sizeof(sensor_info_t) ]; + sensor_t *l_sensor_ptr = NULL; + uint16_t l_sensor_id = 0; // sensor id + uint16_t l_sensor_count = 0; // sensor count + uint8_t l_sensor_type = 0; // sensor type + uint16_t l_maxlen = 0, l_retlen = 0; // for echo command and 0xff command + uint16_t l_resp_length = *io_resp_length; + sensorrec_t SensorInfo; + + switch( i_msg->au8CmdData_ptr[0] ) + { + // commands 0x01 ~ 0x1B are DEPRECATED except 0x07, 0x0A + + case 0x07: // Get Multiple Sensor Data + { + uint16_t l_in; // input pointer + uint16_t l_out=0; // output pointer + char *t; + int k; + l_rc = COMPCODE_NORMAL;; // assume no error + + // Process each sensor in turn + for (l_in = 1; l_in + 1 < i_msg->u8CmdDataLen; l_in=l_in+2) + { + // exit when a return message is filled. -1 is for IPMI return code + if (l_out + AME_SDRS > IPMI_MAX_MSG_SIZE - 1) break; + + // Get the next sensor + l_sensor_id = CONVERT_UINT8_ARRAY_UINT16(i_msg->au8CmdData_ptr[l_in], + i_msg->au8CmdData_ptr[l_in+1]); + l_sensor_ptr = getSensorByGsid(l_sensor_id); + if(l_sensor_ptr == NULL) + { + // Mark which sensor number does not exist + o_resp[0] = (uint8_t)(l_in >> 8); + o_resp[1] = (uint8_t)(l_in); + *io_resp_length = 2; + l_rc = COMPCODE_DEST_UNAVAILABLE; + break; + } + + /* Get a snap-shot of this sensors registers */ + /* This copy is required so the bytes in each field are self-consistent + since the AME interrupt can modify them at any time. Note that it is + possible that the fields are not consistent with each other, but this + is how Amester has always been. Use traces to get a consistent view.*/ + SensorInfo.timestamp=G_current_tick; + SensorInfo.updates=l_sensor_ptr->update_tag; + SensorInfo.accumulated_value=l_sensor_ptr->accumulator; + SensorInfo.value=l_sensor_ptr->sample; + SensorInfo.value_min=l_sensor_ptr->sample_min; + SensorInfo.value_max=l_sensor_ptr->sample_max; + memcpy(&SensorInfo.status,&l_sensor_ptr->status,sizeof(uint16_t)); + SensorInfo.test=0; //TODO: This field is not supported for now. Maybe need to change in the future. + + // Copy to output buffer. + t=(char *)&SensorInfo; + for (k=0;k<AME_SDRS;k++) o_resp[l_out++]=*t++; + + *io_resp_length = l_out; + + } // for each sensor + + break; + }; + + case 0x0a: // Get API version + o_resp[0] = AME_API_MAJ; + o_resp[1] = AME_API_MIN; + *io_resp_length = 2; + l_rc = COMPCODE_NORMAL; + break; + + //---------------------------------------------------------------------- + // AME 2.16 commands + //---------------------------------------------------------------------- + case 0x1c: // AME component level constants + // Goal is to allow 1 IPMI to get a bunch of important data at startup. + // Dramatically speedup Amester initialization + + // Check length + if(l_resp_length < AME_COMPONENT_LEVEL_RSPCMD_LEN) + { + l_rc = COMPCODE_PARAM_OUT_OF_RANGE; + break; + } + o_resp[0] = AME_API_MAJ; + o_resp[1] = AME_API_MIN; + o_resp[2] = AME_VERSION_MAJ; + o_resp[3] = AME_VERSION_MIN; + o_resp[4] = CONVERT_UINT16_UINT8_HIGH(AME_YEAR); + o_resp[5] = CONVERT_UINT16_UINT8_LOW(AME_YEAR); + o_resp[6] = AME_MONTH; + o_resp[7] = AME_DAY; + o_resp[8] = CONVERT_UINT16_UINT8_HIGH(G_amec_sensor_count); + o_resp[9] = CONVERT_UINT16_UINT8_LOW(G_amec_sensor_count); + o_resp[10] = AMEC_TB_NUMBER_OF_TRACES; + *io_resp_length = AME_COMPONENT_LEVEL_RSPCMD_LEN; + l_rc = COMPCODE_NORMAL; + break; + + case 0x1d: + + // Check length + if(l_resp_length < 2) + { + l_rc = COMPCODE_PARAM_OUT_OF_RANGE; + break; + } + o_resp[0] = 0; + o_resp[1] = 0; + *io_resp_length = 2; + l_rc = COMPCODE_NORMAL; + break; + + //Clear min/max fields in all sensors. + case 0x21: + l_sensor_ptr = NULL; + l_sensor_count = G_amec_sensor_count; + + // clear min/max + uint16_t i = 0; + for( i = 0;i < l_sensor_count; i++) + { + l_sensor_ptr = getSensorByGsid(i); + sensor_clear_minmax(l_sensor_ptr); + } + *io_resp_length = 0; + l_rc = COMPCODE_NORMAL; + break; + + case 0x25: // Get sensors info + l_rc = COMPCODE_NORMAL; + l_sensor_type = i_msg->au8CmdData_ptr[3]; + l_sensor_count = G_amec_sensor_count; + l_sensor_id = CONVERT_UINT8_ARRAY_UINT16( i_msg->au8CmdData_ptr[1], + i_msg->au8CmdData_ptr[2] ); + uint16_t j = 0; + uint16_t l_final_length = 0; + + for( j = l_sensor_id; j < l_sensor_count; j++) + { + *io_resp_length = sizeof(sensor_info_t); + l_rc = amester_get_sensor_info(l_temp_buffer,io_resp_length,l_sensor_type,j); + if(l_rc != COMPCODE_NORMAL) + { + l_final_length = 0; + break; + } + + // max response length is IPMI_MAX_MSG_SIZE + if( ((l_final_length+(*io_resp_length)) < IPMI_MAX_MSG_SIZE) && + ((l_final_length+(*io_resp_length)) < l_resp_length ) ) + { + memcpy( o_resp, l_temp_buffer, *io_resp_length); // Copy to final output buffer + o_resp += (*io_resp_length); + l_final_length = l_final_length+(*io_resp_length); + } + else + { + break; + } + } + *io_resp_length = l_final_length; + break; + + // Trace buffer commands + // Get trace buffer configuration + case 0x30: + amec_tb_cmd_info(i_msg,o_resp,io_resp_length,&l_rc); + break; + + // Configure TB + case 0x31: + amec_tb_cmd_set_config(i_msg,o_resp,io_resp_length,&l_rc); + break; + + // Read TB + // Input is an index into the buffer. + // Output is a full-sized response, possibly wrapping around at end of buffer. + case 0x32: + amec_tb_cmd_read(i_msg,o_resp,io_resp_length,&l_rc); + break; + + // Start recording TB + case 0x33: + amec_tb_cmd_start_recording(i_msg,o_resp,io_resp_length,&l_rc); + break; + + // Stop recording TB + case 0x34: + amec_tb_cmd_stop_recording(i_msg,o_resp,io_resp_length,&l_rc); + break; + + // Get sensor table, not support + case 0x35: //No support + *io_resp_length = 0; + l_rc = COMPCODE_PARAM_OUT_OF_RANGE; + break; + + // Get SCOM table, not support + case 0x36: //No support + *io_resp_length = 0; + l_rc = COMPCODE_PARAM_OUT_OF_RANGE; + break; + + // Return all configurable parameters for a trace + case 0x3f: + amec_tb_cmd_get_config(i_msg,o_resp,io_resp_length,&l_rc); + break; + + // Get number of parameters + case 0x40: + amec_parm_get_number(i_msg,o_resp,io_resp_length,&l_rc); + break; + + // Return configuration of parameters starting with a given guid + case 0x41: + amec_parm_get_config(i_msg,o_resp,io_resp_length,&l_rc); + break; + + // Read parameter + case 0x42: + amec_parm_read(i_msg,o_resp,io_resp_length,&l_rc); + break; + + // Write parameter + case 0x43: + amec_parm_write(i_msg,o_resp,io_resp_length,&l_rc); + break; + + // Partition management + case 0x50: //No support + *io_resp_length = 0; + l_rc = COMPCODE_PARAM_OUT_OF_RANGE; + break; + + // Note: Amester uses the echo command to figure out how much data it is + // allowed to send in 1 message to OCC. + case 0xfe: //echo + l_maxlen = IPMI_MAX_MSG_SIZE - 1; // -1 for completion code + l_retlen = l_maxlen; + + // Pick the smaller of the input length and max output length. + if (i_msg->u8CmdDataLen < l_maxlen) + { + l_retlen = i_msg->u8CmdDataLen; + } + + // Check length + if(l_resp_length < l_retlen) + { + l_rc = COMPCODE_PARAM_OUT_OF_RANGE; + break; + } + + l_rc = COMPCODE_NORMAL; /* assume no error */ + // Copy back as much of the input message as possible. + memcpy( o_resp, i_msg->au8CmdData_ptr, l_retlen); + *io_resp_length = l_retlen; + break; + + // Note: Amester uses this command to find out the maximum length output + // message OCC supports. + case 0xff: + l_maxlen = IPMI_MAX_MSG_SIZE - 1; // -1 for completion code + + if (i_msg->u8CmdDataLen == 3) + { + l_maxlen = CONVERT_UINT8_ARRAY_UINT16( i_msg->au8CmdData_ptr[1], + i_msg->au8CmdData_ptr[2]); + } + + if (l_maxlen > IPMI_MAX_MSG_SIZE -1) + { + l_maxlen = IPMI_MAX_MSG_SIZE -1; + } + + // Check length + if(l_resp_length < l_maxlen) + { + l_rc = COMPCODE_PARAM_OUT_OF_RANGE; + break; + } + + l_rc = COMPCODE_NORMAL; /* assume no error */ + for (i = 0; i<l_maxlen; i++) + { + o_resp[i] = i; + } + *io_resp_length = l_maxlen; + break; + + default: + *io_resp_length = 0; + l_rc = COMPCODE_PARAM_OUT_OF_RANGE; + } + + return l_rc; +} + + +// Function Specification +// +// Name: amester_manual_throttle +// +// Description: Amester interface entry point for ipmicommand 0x3B +// +// Task Flags: +// +// End Function Specification +uint8_t amester_manual_throttle( const IPMIMsg_t * i_msg, + uint16_t * io_resp_length, + uint8_t * o_resp ) + +{ + /*------------------------------------------------------------------------*/ + /* Local variables */ + /*------------------------------------------------------------------------*/ + uint8_t l_rc,temp1,temp2; + uint16_t l_resp_length = *io_resp_length; + uint16_t i,j,cc,idx,temp16; + uint16_t k; + uint32_t temp32a; + uint32_t *temp32; + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + switch (i_msg->au8CmdData_ptr[0]) + { + case 0x03: // CPU(s) Present Bit Mask + // The CPU Present Bit Mask is now being generated by the + // PROC component of OCC. + + // Check length + if(l_resp_length < 2) + { + l_rc = COMPCODE_PARAM_OUT_OF_RANGE; + break; + } + + o_resp[0] = CONVERT_UINT32_UINT8_UPPER_HIGH( G_present_hw_cores); + o_resp[1] = CONVERT_UINT32_UINT8_UPPER_LOW( G_present_hw_cores); + *io_resp_length = 2; + l_rc = COMPCODE_NORMAL; + break; + + case 0x04: // Get last throttle value sent to CPU 0. DEPRECATED. + *io_resp_length = 0; + l_rc = COMPCODE_PARAM_OUT_OF_RANGE; + break; + + case 0x05: // Get AME enable/disable flag (old style interface...do not use), no support + *io_resp_length = 0; + l_rc = COMPCODE_PARAM_OUT_OF_RANGE; + break; + + case 0x06: // Get new PTVR (Power Threshold Vector Request), no support + *io_resp_length = 0; + l_rc = COMPCODE_PARAM_OUT_OF_RANGE; + break; + + case 0x07: // Write individual AME parameters + switch (i_msg->au8CmdData_ptr[1]) + { + + case 20: // parameter 20: Set Probe Parameters + { + if (i_msg->au8CmdData_ptr[2]> (NUM_AMEC_FW_PROBES-1)) + { + o_resp[0]=i_msg->au8CmdData_ptr[2]; + *io_resp_length=1; + l_rc=COMPCODE_PARAM_OUT_OF_RANGE; + break; + } + if (i_msg->au8CmdData_ptr[3] < 1) + { + o_resp[0]=i_msg->au8CmdData_ptr[2]; + *io_resp_length=1; + l_rc=COMPCODE_PARAM_OUT_OF_RANGE; + break; + } + + temp32a=((uint32_t)i_msg->au8CmdData_ptr[4]<<24)+((uint32_t)i_msg->au8CmdData_ptr[5]<<16); + temp32a=temp32a+((uint32_t)i_msg->au8CmdData_ptr[6]<<8)+((uint32_t)i_msg->au8CmdData_ptr[7]); + temp32=(uint32_t*)temp32a; + + g_amec->ptr_probe250us[i_msg->au8CmdData_ptr[2]]=temp32; + g_amec->size_probe250us[i_msg->au8CmdData_ptr[2]]=i_msg->au8CmdData_ptr[3]; + g_amec->index_probe250us[i_msg->au8CmdData_ptr[2]]=0; // Reset index + + o_resp[0]=i_msg->au8CmdData_ptr[2]; // Return probe # + *io_resp_length=1; + l_rc = COMPCODE_NORMAL; + break; + }; + + case 22: // parameter 22: Analytics parameters + { + g_amec->analytics_group=i_msg->au8CmdData_ptr[2]; // Set group + g_amec->analytics_chip=i_msg->au8CmdData_ptr[3]; // Select which chip to analyze + g_amec->analytics_option=i_msg->au8CmdData_ptr[4]; // Select which option + g_amec->analytics_total_chips=i_msg->au8CmdData_ptr[5]; // Select total number of chips + g_amec->analytics_slot=i_msg->au8CmdData_ptr[6]; // Select time slot to read data + o_resp[0]=i_msg->au8CmdData_ptr[2]; + o_resp[1]=i_msg->au8CmdData_ptr[3]; + o_resp[2]=i_msg->au8CmdData_ptr[4]; + o_resp[3]=i_msg->au8CmdData_ptr[5]; + o_resp[4]=i_msg->au8CmdData_ptr[6]; + *io_resp_length=5; + l_rc = COMPCODE_NORMAL; + break; + } + + case 23: // parameter 23: CPM calibration parameters + { + // g_amec->cpms_enabled=i_msg->au8CmdData_ptr[2]; // Enable CPMs + o_resp[0]=i_msg->au8CmdData_ptr[2]; + *io_resp_length=1; + l_rc = COMPCODE_NORMAL; + break; + } + + + case 24: // parameter 24: set ambient and fan speed sensors + { + // Set ambient temperature (8 bits) + temp16=(uint16_t)i_msg->au8CmdData_ptr[2]; + sensor_update(AMECSENSOR_PTR(TEMPAMBIENT), temp16); + // Set average fan speed (16 bits) + temp16=((uint16_t)i_msg->au8CmdData_ptr[3]<<8)+(uint16_t)i_msg->au8CmdData_ptr[4]; + sensor_update(AMECSENSOR_PTR(FANSPEEDAVG), temp16); + o_resp[0]=i_msg->au8CmdData_ptr[2]; + o_resp[1]=i_msg->au8CmdData_ptr[3]; + o_resp[2]=i_msg->au8CmdData_ptr[3]; + *io_resp_length=3; + l_rc = COMPCODE_NORMAL; + break; + } + + + case 29: // parameter 29: Control vector recording modes and stream rates. + { + g_amec->stream_vector_rate=255; // First step is to set an invalid rate so no recording done at all + g_amec->stream_vector_mode=0; // Also is to assure NO recording during parameter changes + g_amec->stream_vector_group=i_msg->au8CmdData_ptr[4]; // Choose group # + g_amec->write_stream_index=(uint32_t)CONVERT_UINT8_ARRAY_UINT16(i_msg->au8CmdData_ptr[5],i_msg->au8CmdData_ptr[6]); + g_amec->stream_vector_delay=(uint32_t)CONVERT_UINT8_ARRAY_UINT16(i_msg->au8CmdData_ptr[7],i_msg->au8CmdData_ptr[8]); + g_amec->stream_vector_mode=i_msg->au8CmdData_ptr[2]; // Choose mode + + switch (g_amec->stream_vector_group) + { + case 45: //group 45 decimal (amec_analytics support) + g_amec->stream_vector_map[0]=0; // Leave space for 250usec time stamp + k = 1; + for (i=0; i<=(STREAM_VECTOR_SIZE_EX-2); i++) + { + g_amec->stream_vector_map[k++] = &g_amec->analytics_array[i]; + } + //gpEMP->stream_vector_map[64]=(void *) 0xffffffff; // Termination of partial vector + g_amec->analytics_group=45; + g_amec->analytics_bad_output_count=2; // drop first 2 frames of output + break; + + default: + break; + } + + // Final step is to set a valid rate to begin recording at + g_amec->stream_vector_rate=i_msg->au8CmdData_ptr[3]; // Choose stream rate + g_amec->recordflag=1; // Recording is now valid + *io_resp_length = 1; + l_rc = COMPCODE_NORMAL; + break; + } + + case 64: // support for THREADMODE group 44 recording + g_amec->analytics_threadmode=i_msg->au8CmdData_ptr[2]; + g_amec->analytics_threadcountmax=i_msg->au8CmdData_ptr[3]; + o_resp[0]=i_msg->au8CmdData_ptr[2]; + o_resp[1]=i_msg->au8CmdData_ptr[3]; + *io_resp_length=2; + l_rc = COMPCODE_NORMAL; + break; + + default: + *io_resp_length = 0; + l_rc = COMPCODE_PARAM_OUT_OF_RANGE; + break; + } + + break; + + case 0x08: // Read individual AME parameters + switch (i_msg->au8CmdData_ptr[1]) + { + case 0x08: // parameter 8: Set histogram copy interval in msec (4 bytes) + o_resp[0] = (uint8_t)(AME_HISTOGRAM_COPY_INTERVAL>>24); + o_resp[1] = (uint8_t)(AME_HISTOGRAM_COPY_INTERVAL>>16); + o_resp[2] = (uint8_t)(AME_HISTOGRAM_COPY_INTERVAL>>8); + o_resp[3] = (uint8_t)(AME_HISTOGRAM_COPY_INTERVAL); + *io_resp_length = 4; + l_rc = COMPCODE_NORMAL; + break; + + case 20: // parameter 20: Read Probe Parameters + { + if (i_msg->au8CmdData_ptr[2]> (NUM_AMEC_FW_PROBES-1)) + { + o_resp[0]=i_msg->au8CmdData_ptr[2]; + *io_resp_length=1; + l_rc=COMPCODE_PARAM_OUT_OF_RANGE; + break; + } + o_resp[1]=g_amec->size_probe250us[i_msg->au8CmdData_ptr[2]]; // Return size of object read by probe in bytes + temp32=g_amec->ptr_probe250us[i_msg->au8CmdData_ptr[2]]; // Get copy of 32 bit probe ptr + temp32a=(uint32_t)temp32; + o_resp[5]=(uint8_t)temp32a; + o_resp[4]=(uint8_t)((uint32_t)temp32a>>8); + o_resp[3]=(uint8_t)((uint32_t)temp32a>>16); + o_resp[2]=(uint8_t)((uint32_t)temp32a>>24); + o_resp[0]=i_msg->au8CmdData_ptr[2]; // Return probe # + *io_resp_length=6; + l_rc=COMPCODE_NORMAL; + break; + }; + + case 22: // parameter 22: Analytics parameters + o_resp[0]=g_amec->analytics_group; + o_resp[1]=g_amec->analytics_chip; + o_resp[2]=g_amec->analytics_option; + o_resp[3]=g_amec->analytics_total_chips; + o_resp[4]=g_amec->analytics_slot; + *io_resp_length=5; + l_rc = COMPCODE_NORMAL; + break; + + case 23: // parameter 23: CPM parameters + // o_resp[0]=g_amec->cpms_enabled; + // o_resp[1]=g_amec->cpm_active_core; + // o_resp[2]=g_amec->cpm_cal_state; + // o_resp[3]=g_amec->cpm_core_state; + // o_resp[4]=g_amec->cpm_measure_state; + // o_resp[5]=g_amec->cpm_cal_count; + *io_resp_length=6; + l_rc = COMPCODE_NORMAL; + break; + + case 29: // parameter 29: Stream recording control parameters + o_resp[0]=(uint8_t)(g_amec->stream_vector_mode); + o_resp[1]=(uint8_t)(g_amec->stream_vector_rate); + o_resp[2]=(uint8_t)(g_amec->write_stream_index>>8); + o_resp[3]=(uint8_t)(g_amec->write_stream_index & 0xff); + o_resp[4]=(uint8_t)(g_amec->stream_vector_delay>>8); + o_resp[5]=(uint8_t)(g_amec->stream_vector_delay & 0xff); + *io_resp_length=6; + l_rc=COMPCODE_NORMAL; + break; + + case 37: // parameter 37: Read out (IPMI_MAX_MSG_SIZE-2*STREAM_VECTOR_SIZE) byte vector from + // streaming buffer + g_amec->read_stream_index=(uint32_t)((i_msg->au8CmdData_ptr[2]<<8)+i_msg->au8CmdData_ptr[3]); + temp1=i_msg->au8CmdData_ptr[4]; + temp2=i_msg->au8CmdData_ptr[5]; + if (g_amec->read_stream_index > (STREAM_BUFFER_SIZE-1*STREAM_VECTOR_SIZE_EX)) + { + o_resp[0]=i_msg->au8CmdData_ptr[2]; + *io_resp_length=1; + l_rc=COMPCODE_PARAM_OUT_OF_RANGE; + break; + } + if (temp1 > 1) // No averaging is allowed when using large read sizes + { + o_resp[0]=i_msg->au8CmdData_ptr[4]; + *io_resp_length=1; + l_rc=COMPCODE_PARAM_OUT_OF_RANGE; + break; + } + if (temp2 > 0) + { + o_resp[0]=i_msg->au8CmdData_ptr[5]; + *io_resp_length=1; + l_rc=COMPCODE_PARAM_OUT_OF_RANGE; + break; + } + if (g_amec->write_stream_index >= g_amec->read_stream_index) + { + temp32a=g_amec->write_stream_index-g_amec->read_stream_index; + } + else + { + temp32a=STREAM_BUFFER_SIZE+g_amec->write_stream_index-g_amec->read_stream_index; + } + if (temp32a < 1*STREAM_VECTOR_SIZE_EX) + { + o_resp[0]=1; // Indicate insufficient data, but return a zero return code + *io_resp_length=STREAM_VECTOR_SIZE_EX+3; // # of bytes (STREAM_VECTOR_SIZE is in 16 bit words) + l_rc=COMPCODE_NORMAL; + break; + } + o_resp[0]=0; // Indicate sufficient data + i=0; + j=1*STREAM_VECTOR_SIZE_EX; // used to be 10*STREAM_VECTOR_SIZE_EX + cc=3; // Begin just past return code and time stamp + + for(idx = i; idx < j; idx++) // Skip first 1 entry: either write_index and time stamp + { + temp16 = (uint16_t)g_amec->ptr_stream_buffer[g_amec->read_stream_index + idx]; + o_resp[cc] = (temp16 >> 8); + o_resp[cc + 1] = (temp16 & 0xff); + cc = cc + 2; // output index + } + if(i_msg->au8CmdData_ptr[7] == 0) + { + temp16 = g_amec->ptr_stream_buffer[g_amec->read_stream_index]; // Send back time stamp + } else + { + temp16 = g_amec->write_stream_index; // Send back write stream index + } + o_resp[1] = (uint8_t)(temp16 >> 8); + o_resp[2] = (uint8_t)(temp16 & 0xff); + *io_resp_length = 3 + 2 * (1 * STREAM_VECTOR_SIZE_EX); // # of bytes (STREAM_VECTOR_SIZE_EX is in 16 bit words) + l_rc = COMPCODE_NORMAL; + break; + + case 64: // support for THREADMODE group 45 recording + o_resp[0]=(uint8_t)(g_amec->analytics_threadmode); + o_resp[1]=(uint8_t)(g_amec->analytics_threadcountmax); + *io_resp_length=2; + l_rc=COMPCODE_NORMAL; + break; + + default: + *io_resp_length = 0; + l_rc = COMPCODE_PARAM_OUT_OF_RANGE; + break; + } + break; + + default: + *io_resp_length = 0; + l_rc = COMPCODE_PARAM_OUT_OF_RANGE; + break; + } // end of switch + + return l_rc; +} + +// Function Specification +// +// Name: AMEC_entry_point +// +// Description: Amester interface entry point +// +// Task Flags: +// +// End Function Specification +uint8_t amester_entry_point( const IPMIMsg_t * i_msg, + uint16_t * io_resp_length, + uint8_t * o_resp) + +{ + uint8_t l_rc = COMPCODE_NORMAL; + + do + { + if( (i_msg == NULL) || + (io_resp_length == NULL) || + (o_resp == NULL) ) + { + l_rc = COMPCODE_UNSPECIFIED; + break; + } + + switch (i_msg->u8Cmd) + { + case 0x3C: + l_rc = amester_api( i_msg, io_resp_length, o_resp); + break; + + case 0x3B: + l_rc = amester_manual_throttle( i_msg, io_resp_length, o_resp); + break; + + default: + l_rc = COMPCODE_CMD_UNKNOWN; + break; + } + } while (0); + + return l_rc; +} + +amec_tb_t* AMEC_tb_get_by_guid(const AMEC_TB_GUID i_guid) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + amec_tb_t* l_tb_ptr = NULL; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + do + { + if(i_guid > AMEC_TB_NUMBER_OF_TRACES) + { + //TRACE(g_trac_amec,"E>amec_tb_get_by_guid:Invalid GUID (%d) max GUID supported at the moment is (%d)",i_guid,g_amec_tb_count); + break; + } + //traces are arranged in an array by GUID + l_tb_ptr = &g_amec_tb_list[i_guid]; + + } while( 0 ); + + return l_tb_ptr; +} + +// +// Commands that run during AME interrupt time +// +void amec_tb_record(const AMEC_TB_GUID i_guid) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + UINT8 *l_w; // pointer for tracebuffer writing + UINT16 l_i; + sensor_t *l_s; + UINT32 l_totalbytes; + amec_parm_t *l_parm; + UINT8 *l_src_ptr; + AMEC_PARM_GUID l_parm_guid; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + // Record OCA from last 32 ms + if(G_dcom_slv_inbox_rx.tb_record) + { + // Check for valid tb write entry index + if(g_amec_tb_list[i_guid].write < g_amec_tb_list[i_guid].entry_n) + { + l_w = g_amec_tb_list[i_guid].bytes + g_amec_tb_list[i_guid].write * g_amec_tb_list[i_guid].entry_size; + // Write the tracked sensors + for(l_i = 0; l_i < g_amec_tb_list[i_guid].sensors_n; l_i++) + { + l_s = getSensorByGsid(g_amec_tb_list[i_guid].sensors_num[l_i]); + switch(g_amec_tb_list[i_guid].sensors_field[l_i]) + { + case 0: + { //value + *l_w++ = (UINT8)(l_s->sample >> 8); + *l_w++ = (UINT8)(l_s->sample); + break; + } + case 1: + { //min + *l_w++ = (UINT8)(l_s->sample_min >> 8); + *l_w++ = (UINT8)(l_s->sample_min); + break; + } + case 2: + { //max + *l_w++ = (UINT8)(l_s->sample_max >> 8); + *l_w++ = (UINT8)(l_s->sample_max); + break; + } + case 3: + { //accumulator + *l_w++ = (UINT8)(l_s->accumulator >> 24); + *l_w++ = (UINT8)(l_s->accumulator >> 16); + *l_w++ = (UINT8)(l_s->accumulator >> 8); + *l_w++ = (UINT8)(l_s->accumulator); + break; + } + case 4: + { //update tag + *l_w++ = (UINT8)(l_s->update_tag >> 24); + *l_w++ = (UINT8)(l_s->update_tag >> 16); + *l_w++ = (UINT8)(l_s->update_tag >> 8); + *l_w++ = (UINT8)(l_s->update_tag); + break; + } + case 5: + { //test (not available in POWER8) + *l_w++ = (UINT8)0; + *l_w++ = (UINT8)0; + break; + } + case 6: + { //rcnt + *l_w++ = (UINT8)(g_amec->r_cnt >> 24); + *l_w++ = (UINT8)(g_amec->r_cnt >> 16); + *l_w++ = (UINT8)(g_amec->r_cnt >> 8); + *l_w++ = (UINT8)(g_amec->r_cnt); + break; + } + default: + break; + } + } // write sensors + + // Write the tracked parameters + for(l_i = 0; l_i < g_amec_tb_list[i_guid].parm_n; l_i++) + { + l_parm_guid = g_amec_tb_list[i_guid].parm_num[l_i]; + if(g_amec_parm_list[l_parm_guid].preread) amec_parm_preread(l_parm_guid); // get latest version + l_parm = &g_amec_parm_list[l_parm_guid]; + l_totalbytes = l_parm->length * l_parm->vector_length; + l_src_ptr = l_parm->value_ptr; + while(l_totalbytes--) + { + *l_w++ = *l_src_ptr++; + } + } + + // Advance the write pointer and decide if recording should stop + g_amec_tb_list[i_guid].write++; // Signal this entry is ready for reading + if(g_amec_tb_list[i_guid].write >= g_amec_tb_list[i_guid].entry_n) + { + if(g_amec_tb_continuous) + { + // Start at beginning of trace + g_amec_tb_list[i_guid].write = 0; + } else + { + // Stop recording if this trace is configured + if(g_amec_tb_list[i_guid].entry_n) g_amec_tb_record = 0; + } + } + } // valid write index + } // end if(record)} +} + + +// Get tb config. Pack as many traces as possible +void amec_tb_cmd_info(const IPMIMsg_t *i_psMsg, + UINT8 *o_pu8Resp, + UINT16 *o_pu16RespLength, + UINT8 *o_retval) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + AMEC_TB_GUID l_id; // trace id + UINT16 l_j; // size of return message + CHAR *l_src; //pointer for copying name + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + l_id = (AMEC_TB_GUID) i_psMsg->au8CmdData_ptr[1]; + l_j = 0; // write index byte for response + + for(; l_id < AMEC_TB_NUMBER_OF_TRACES; l_id++) + { + if(l_j + AMEC_TB_CONFIG_SIZE >= IPMI_MAX_MSG_SIZE) break; // end of response buffer + + l_src = g_amec_tb_list[l_id].name; + while(*l_src != 0) + { + o_pu8Resp[l_j++] = *l_src++; + } /* copy string up until \0 */ + o_pu8Resp[l_j++] = '\0'; /* add string terminator */ + + o_pu8Resp[l_j++] = (UINT8)(g_amec_tb_list[l_id].freq >> 24); + o_pu8Resp[l_j++] = (UINT8)(g_amec_tb_list[l_id].freq >> 16); + o_pu8Resp[l_j++] = (UINT8)(g_amec_tb_list[l_id].freq >> 8); + o_pu8Resp[l_j++] = (UINT8)(g_amec_tb_list[l_id].freq); + // has_scoms field from POWER7 is always 0 on POWER8 + o_pu8Resp[l_j++] = 0; + } + *o_pu16RespLength=l_j; + *o_retval=COMPCODE_NORMAL; + return; +} + + +void amec_tb_cmd_set_config(const IPMIMsg_t *i_psMsg, + UINT8 *o_pu8Resp, + UINT16 *o_pu16RespLength, + UINT8 *o_retval) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + UINT8 l_i; + UINT16 l_sensors_n; + UINT16 l_oca_n; + UINT16 l_num_index; + UINT16 l_field_index; + UINT8 l_valid_sockets = 0; + UINT32 l_socket_bitmap = 0; + UINT16 l_parm_n; + UINT16 l_parm_index; + amec_parm_t *l_parm; + UINT8 l_count; + amec_tb_t *l_trace; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + do + { + *o_retval = COMPCODE_NORMAL; + + //Stop recording while setting up a trace. + g_amec_tb_record = 0; + + // 0. Parse input + l_trace = AMEC_tb_get_by_guid(i_psMsg->au8CmdData_ptr[1]); + l_sensors_n = CONVERT_UINT8_ARRAY_UINT16( + i_psMsg->au8CmdData_ptr[2], + i_psMsg->au8CmdData_ptr[3] + ); + l_parm_n = CONVERT_UINT8_ARRAY_UINT16( + i_psMsg->au8CmdData_ptr[4], + i_psMsg->au8CmdData_ptr[5] + ); + l_oca_n = CONVERT_UINT8_ARRAY_UINT16( + i_psMsg->au8CmdData_ptr[6], + i_psMsg->au8CmdData_ptr[7] + ); + l_socket_bitmap = CONVERT_UINT8_ARRAY_UINT32( + i_psMsg->au8CmdData_ptr[8], + i_psMsg->au8CmdData_ptr[9], + i_psMsg->au8CmdData_ptr[10], + i_psMsg->au8CmdData_ptr[11] + ); + + if(l_sensors_n > AMEC_TB_SENSORS_MAX) + { + *o_retval = COMPCODE_PARAM_OUT_OF_RANGE; + o_pu8Resp[0] = 0; // mark second byte is bad. + o_pu8Resp[1] = 2; // mark second byte is bad. + *o_pu16RespLength = 2; + break; + } + + if(l_parm_n > AMEC_TB_PARM_MAX) + { + *o_retval = COMPCODE_PARAM_OUT_OF_RANGE; + o_pu8Resp[0] = 0; // mark fourth byte is bad. + o_pu8Resp[1] = 4; // + *o_pu16RespLength = 2; + } + + if(l_oca_n > OCA_MAX_ENTRIES) + { + *o_retval = COMPCODE_PARAM_OUT_OF_RANGE; + o_pu8Resp[0] = 0; // mark sixth byte is bad + o_pu8Resp[1] = 6; + *o_pu16RespLength = 2; + break; + } + + // Count valid sockets + l_count = l_socket_bitmap; + while(l_count) + { + if(l_count & 0x01) + {l_valid_sockets++;} + l_count >>= 1; + } + if(l_valid_sockets > MAX_NUM_CHIPS) + { + l_valid_sockets = MAX_NUM_CHIPS; + } + + l_trace->entry_size = 0; + + // Set pointers to input + l_num_index = 12; // start of sensor numbers + l_field_index = 12 + 2 * l_sensors_n; // start of sensor fields + l_parm_index = l_field_index + l_sensors_n; // start of parameters + + // Read sensor configuration + for(l_i = 0; l_i < l_sensors_n; l_i++) + { + l_trace->sensors_num[l_i] = CONVERT_UINT8_ARRAY_UINT16( + i_psMsg->au8CmdData_ptr[l_num_index], + i_psMsg->au8CmdData_ptr[l_num_index + 1] + ); + l_trace->sensors_field[l_i] = i_psMsg->au8CmdData_ptr[l_field_index]; + + if(l_trace->sensors_num[l_i] >= G_amec_sensor_count) + { + *o_retval = COMPCODE_PARAM_OUT_OF_RANGE; + o_pu8Resp[0] = (UINT8)(l_num_index >> 8); // mark which byte input is bad + o_pu8Resp[1] = (UINT8)(l_num_index); // mark which byte input is bad + *o_pu16RespLength = 2; + break; + } + + if(l_trace->sensors_field[l_i] > 6) + { + *o_retval = COMPCODE_PARAM_OUT_OF_RANGE; + o_pu8Resp[0] = (UINT8)(l_field_index >> 8); // mark which byte input is bad + o_pu8Resp[1] = (UINT8)(l_field_index); // mark which byte input is bad + *o_pu16RespLength = 2; + return; + } + + switch(l_trace->sensors_field[l_i]) + { + case 0: // value + case 1: // min + case 2: // max + l_trace->entry_size += 2; + break; + case 3: //acc + l_trace->entry_size += 4; + break; + case 4: //updates + l_trace->entry_size += 4; + break; + case 5: //test + l_trace->entry_size += 2; + break; + case 6: //rcnt + l_trace->entry_size += 4; + break; + default: + break; + } + + l_num_index += 2; + l_field_index++; + } + + if(*o_retval) break; + + // Record number of sensors in this trace + l_trace->sensors_n = l_sensors_n; + + // Read Parameter configuration + for(l_i = 0; l_i < l_parm_n; l_i++) + { + l_trace->parm_num[l_i] = CONVERT_UINT8_ARRAY_UINT16( + i_psMsg->au8CmdData_ptr[l_parm_index], + i_psMsg->au8CmdData_ptr[l_parm_index + 1] + ); + + if(l_trace->parm_num[l_i] >= AMEC_PARM_NUMBER_OF_PARAMETERS) + { + *o_retval = COMPCODE_PARAM_OUT_OF_RANGE; + o_pu8Resp[0] = (UINT8)(l_parm_index >> 8); // mark which byte input is bad + o_pu8Resp[1] = (UINT8)(l_parm_index); // mark which byte input is bad + *o_pu16RespLength = 2; + break; + } + l_parm = &g_amec_parm_list[l_trace->parm_num[l_i]]; + l_trace->entry_size += l_parm->length * l_parm->vector_length; + l_parm_index += 2; + } + + if(*o_retval) break; + + // Record this number of parameters in the trace + l_trace->parm_n = l_parm_n; + + l_trace->entry_n = l_trace->size / l_trace->entry_size; + l_trace->read = 0; + l_trace->write = 0; + + *o_pu16RespLength = 0; + } while(0); + + return; +} + +void amec_tb_cmd_start_recording(const IPMIMsg_t *i_psMsg, + UINT8 *o_pu8Resp, + UINT16 *o_pu16RespLength, + UINT8 *o_retval) +{ + g_amec_tb_record = 1; + *o_pu16RespLength = 0; + *o_retval=COMPCODE_NORMAL; +} + +void amec_tb_cmd_stop_recording(const IPMIMsg_t *i_psMsg, + UINT8 *o_pu8Resp, + UINT16 *o_pu16RespLength, + UINT8 *o_retval) +{ + g_amec_tb_record = 0; + *o_pu16RespLength = 0; + *o_retval=COMPCODE_NORMAL; +} + +void amec_tb_cmd_read(const IPMIMsg_t *i_psMsg, + UINT8 *o_pu8Resp, + UINT16 *o_pu16RespLength, + UINT8 *o_retval) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + amec_tb_t *l_trace; + UINT16 l_i=0; // output index + UINT16 l_maxresponse = IPMI_MAX_MSG_SIZE - 1; // -1 since return code is 1B + UINT32 l_j; // index to copy from + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + do + { + *o_retval = COMPCODE_NORMAL; /* assume no error */ + + // Parse input command + l_trace = AMEC_tb_get_by_guid(i_psMsg->au8CmdData_ptr[1]); + if(l_trace == NULL) + { + *o_retval = COMPCODE_PARAM_OUT_OF_RANGE; + *o_pu16RespLength = 0; + break; + } + l_j = CONVERT_UINT8_ARRAY_UINT32( + i_psMsg->au8CmdData_ptr[2], + i_psMsg->au8CmdData_ptr[3], + i_psMsg->au8CmdData_ptr[4], + i_psMsg->au8CmdData_ptr[5] + ); + + // Copy bytes to be read into response buffer + for(l_i = 0; l_i < l_maxresponse; l_i++, l_j++) + { + if(l_j >= l_trace->size) // wrap around to beginning of buffer. + { + l_j = 0; + } + o_pu8Resp[l_i] = l_trace->bytes[l_j]; + } + *o_pu16RespLength = l_i; + } while(0); +} + + +void amec_tb_cmd_get_config(const IPMIMsg_t *i_psMsg, + UINT8 *o_pu8Resp, + UINT16 *o_pu16RespLength, + UINT8 *o_retval) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + amec_tb_t *l_trace; + UINT8 l_i = 0; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + do + { + *o_retval = COMPCODE_NORMAL; + + l_trace = AMEC_tb_get_by_guid(i_psMsg->au8CmdData_ptr[1]); + if(l_trace == NULL) + { + *o_retval = COMPCODE_PARAM_OUT_OF_RANGE; + *o_pu16RespLength = 0; + break; + } + + o_pu8Resp[l_i++] = (UINT8)((UINT32)l_trace->bytes >> 24); + o_pu8Resp[l_i++] = (UINT8)((UINT32)l_trace->bytes >> 16); + o_pu8Resp[l_i++] = (UINT8)((UINT32)l_trace->bytes >> 8); + o_pu8Resp[l_i++] = (UINT8)((UINT32)l_trace->bytes); + o_pu8Resp[l_i++] = (UINT8)(l_trace->size >> 24); + o_pu8Resp[l_i++] = (UINT8)(l_trace->size >> 16); + o_pu8Resp[l_i++] = (UINT8)(l_trace->size >> 8); + o_pu8Resp[l_i++] = (UINT8)(l_trace->size); + o_pu8Resp[l_i++] = (UINT8)(l_trace->entry_size >> 24); + o_pu8Resp[l_i++] = (UINT8)(l_trace->entry_size >> 16); + o_pu8Resp[l_i++] = (UINT8)(l_trace->entry_size >> 8); + o_pu8Resp[l_i++] = (UINT8)(l_trace->entry_size); + o_pu8Resp[l_i++] = (UINT8)(l_trace->entry_n >> 24); + o_pu8Resp[l_i++] = (UINT8)(l_trace->entry_n >> 16); + o_pu8Resp[l_i++] = (UINT8)(l_trace->entry_n >> 8); + o_pu8Resp[l_i++] = (UINT8)(l_trace->entry_n); + // 32-bit oca_offset field is always 0 on POWER8 + o_pu8Resp[l_i++] = 0; + o_pu8Resp[l_i++] = 0; + o_pu8Resp[l_i++] = 0; + o_pu8Resp[l_i++] = 0; + o_pu8Resp[l_i++] = (UINT8)((UINT32)l_trace->write >> 24); + o_pu8Resp[l_i++] = (UINT8)((UINT32)l_trace->write >> 16); + o_pu8Resp[l_i++] = (UINT8)((UINT32)l_trace->write >> 8); + o_pu8Resp[l_i++] = (UINT8)((UINT32)l_trace->write); + // 32-bit write_oca field is always 0 on POWER8 + o_pu8Resp[l_i++] = 0; + o_pu8Resp[l_i++] = 0; + o_pu8Resp[l_i++] = 0; + o_pu8Resp[l_i++] = 0; + o_pu8Resp[l_i++] = (UINT8)((UINT32)l_trace->read >> 24); + o_pu8Resp[l_i++] = (UINT8)((UINT32)l_trace->read >> 16); + o_pu8Resp[l_i++] = (UINT8)((UINT32)l_trace->read >> 8); + o_pu8Resp[l_i++] = (UINT8)((UINT32)l_trace->read); + o_pu8Resp[l_i++] = l_trace->sensors_n; + o_pu8Resp[l_i++] = l_trace->parm_n; + // oca_n field from POWER7 is always 0 on POWER8 + o_pu8Resp[l_i++] = 0; + + // TODO: this should move to another command + o_pu8Resp[l_i++] = g_amec_tb_record; + o_pu8Resp[l_i++] = g_amec_tb_continuous; + o_pu8Resp[l_i++] = (UINT8)((UINT16)AMEC_TB_SENSORS_MAX); + o_pu8Resp[l_i++] = (UINT8)((UINT16)OCA_MAX_ENTRIES); + o_pu8Resp[l_i++] = (UINT8)(MAX_NUM_CHIPS); + o_pu8Resp[l_i++] = (UINT8)(AMEC_TB_PARM_MAX); + *o_pu16RespLength = l_i; + + } while(0); +} + +/*----------------------------------------------------------------------------*/ +/* End */ +/*----------------------------------------------------------------------------*/ diff --git a/src/occ_405/amec/amec_amester.h b/src/occ_405/amec/amec_amester.h new file mode 100644 index 0000000..8a3510f --- /dev/null +++ b/src/occ_405/amec/amec_amester.h @@ -0,0 +1,327 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/amec/amec_amester.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#ifndef _AMEC_AMESTER_H +#define _AMEC_AMESTER_H + +//************************************************************************* +// Includes +//************************************************************************* +#include <occ_common.h> + +//************************************************************************* +// Externs +//************************************************************************* + +//************************************************************************* +// Macros +//************************************************************************* + +//************************************************************************* +// Defines/Enums +//************************************************************************* +/* Define Completion Codes (IPMI 1.5 Table 5-2) */ +#define COMPCODE_NORMAL 0x00 +#define COMPCODE_NODE_BUSY 0xC0 +#define COMPCODE_CMD_UNKNOWN 0xC1 +#define COMPCODE_CMD_INVALID_FOR_LUN 0xC2 +#define COMPCODE_TIMEOUT 0xC3 +#define COMPCODE_OUT_OF_SPACE 0xC4 +#define COMPCODE_RESERVATION_CANCELLED 0xC5 +#define COMPCODE_REQ_DATA_TRUNCATED 0xC6 +#define COMPCODE_REQ_DATA_LEN_INVALID 0xC7 +#define COMPCODE_REQ_LEN_LIMIT_EXCEEDED 0xC8 +#define COMPCODE_PARAM_OUT_OF_RANGE 0xC9 +#define COMPCODE_REQ_BYTE_CNT_ERR 0xCA +#define COMPCODE_OBJ_ABSENT 0xCB +#define COMPCODE_REQ_FIELD_INVALID 0xCC +#define COMPCODE_CMD_ILL_FOR_SEN 0xCD +#define COMPCODE_RESP_UNAVAILABLE 0xCE +#define COMPCODE_REQ_DUPLICATE 0xCF +#define COMPCODE_SDR_REPO_IN_UPD 0xD0 +#define COMPCODE_FW_IN_UPD 0xD1 +#define COMPCODE_INIT_IN_PROGRESS 0xD2 +#define COMPCODE_DEST_UNAVAILABLE 0xD3 +#define COMPCODE_WRONG_PRIV 0xD4 +#define COMPCODE_CUR_NOT_SUPPORT 0xD5 +#define COMPCODE_UNSPECIFIED 0xFF + +//Habanero uses IPMI, which is constrained to 256 Byte IPMI response. +//This will slow down the Amester connection on FSP-based systems +#define IPMI_MAX_MSG_SIZE 246 // BMC size +#define AMEC_AME_CMD_HEADER_SZ 2 + +// Autonomic Management of Energy (AME) Parameters +#define AME_API_MAJ 2 // API version major +#define AME_API_MIN 26 // API version minor + +#define AME_VERSION_MAJ 7 // Major Version (e.g. Ver. 1.4 has MAJ=1) +#define AME_VERSION_MIN 27 // Minor Version (e.g. Ver. 1.4 has MIN=4) + +#define AME_YEAR 2015 // Year of Release (e.g. 2006) +#define AME_MONTH 4 // Month of Release (e.g. September=9) +#define AME_DAY 2 // Day of Release + +#define AME_SDRS 22 // AME Sensor Data Record Size: 18 bytes + +// AME data types for AME_GetInfo_*() functions +#define AME_INFO_NAME 0 +#define AME_INFO_FREQ 1 +#define AME_INFO_UNITS 2 +#define AME_INFO_SCALE 3 +#define AME_INFO_KIND 4 +#define AME_INFO_ALL 5 + +#define COMMON_MAX_MTU_SIZE 2056 + +// The command response length +#define AME_COMPONENT_LEVEL_RSPCMD_LEN 11 + +// Histogram copy interval in milliseconds (default to 8 seconds) +#define AME_HISTOGRAM_COPY_INTERVAL 8000 + +// WARNING -> STREAM_BUFFER_SIZE must be a perfect multiple of the vector size. +#define STREAM_VECTOR_SIZE 32 // # of 16 bit elements in a stream vector-> must be a power of 2 +#define SHIFT_VECTOR_SIZE 5 // Log base 2 of STREAM_VECTOR_SIZE for shifting +#define INJECTION_BUFFER_SIZE 32 // Size of injection buffer (must be a power of 2) +#define STREAM_VECTOR_SIZE_EX 74 // # of 16 bit elements in a stream vector +#define STREAM_BUFFER_SIZE (40*1*STREAM_VECTOR_SIZE_EX) // Stream buffer size in 16 bit samples for recording real time data to stream to Amester +#define MAX_SENSORS_ANALYTICS 134 // Maximum sensors making up the analytics group 45 (includes all Centaur data & L4 data & supports 12 cores) +#define MSA MAX_SENSORS_ANALYTICS + +#define OCA_MAX_ENTRIES 0 // no POWER7 OCA on POWER8 +#define AMEC_TB_SENSORS_MAX 40 +#define AMEC_TB_PARM_MAX 40 + +// Macro for dividing two UINT32 +// Returns a 32-bit value with the quotient of the division. +#define UTIL_DIV32(I_NUM, I_DEN) (((UINT32)(I_NUM)) / ((UINT32)(I_DEN))) + +//************************************************************************* +// Structures +//************************************************************************* +typedef struct +{ + /** The IPMI command ID */ + uint8_t u8Cmd; + /** The IPMI command data length */ + uint16_t u8CmdDataLen; + /** the IPMI command data (including completion code) */ + uint8_t *au8CmdData_ptr; +} IPMIMsg_t; + +typedef struct sensorrec +{ + uint32_t timestamp; + uint32_t updates; + uint32_t accumulated_value; + uint16_t value; + uint16_t value_min; + uint16_t value_max; + uint16_t status; // bit 0, LSB: calibration complete on this sensor (=1); + // no calibration complete (=0) + // bit 1: histogram gets updated (=1); + // histogram is frozen (=0) + // bit 2: histogram gets reset (=1); + // histogram not being reset (=0) + // WARNING -> bit 2 only takes effect when bit 1 + // is toggled high. As soon as the histogram of + // interest is reset, bit 2 is cleared to a 0 + // automatically. It is expected that the + // external entity will carry out a reset, first + // by setting bit 1 to a 0 to freeze updating, + // then set bit 2 to a 1 to enable a reset, and + // then set bit 1 to a 1 to enable a reset and + // immediate updating again from a known state + // of 0 in all counters. + // bit 3: select buffering area to be a time series + // trace (=1) or a histogram (=0) (msw324) + // bit 4: if set to 1, time series trigger is armed; + // if =0, trigger happened (msw354) + // bit 5: if set to 1, then time series buffer trace + // ptr was reset to 0; this bit is set to 0 + // whenever the next histogram snapshot interval + // arrives. If the bit stays at 0, and the + // next histogram snapshot interval arrives, + // then the update tag is not incremented. This + // allows for much shorter intervals when using + // exclusively time series modes for all the + // sensors with a buffer because the Amester + // never sees the update tag keep getting + // incremented once the recording of each time + // trace is completed. msw357 + // bit 6: if set to 1, this forces all sensor snapshots + // to occur simultaneously in time msw366 + // This is important for histograms to be + // compared. if set to 0, snapshots will be + // spreadout with one done every 64msec once + // the snap-shot interval is finished. This + // flattens out the cycles for a large number + // of sensors being monitored for time-tracing + // where the contents of the histogram/time + // trace buffer aren't changing once the trace + // has finished recording, so snapshotting all + // these sensors at the same time instant + // is not essential. Normally, if all histograms + // are in use, set this bit to 1. If all + // time-series are in used, set this to 0. If + // mixed, be very careful about the choice! + // bits 11 to 13: + // encodes function to be performed in creating a vector + // sensor's sample_reg output. + // 000 -> find max of vector elements + // 001 -> find min of vector elements + // 010 -> find average of vector elements + // 011 to 111 are available for new functions. + // bit 14:if set to 1, high resolution mode is used for + // histograms and tracing on all vector + // sensors. All samples of the vector pass + // through add_histogram routine every time + // the vector is updated. if set to 0, low + // resolution mode is used, and only sample_reg + // (latest max or min of the vector) is passed + // through add_histogram every time. + // bit 15:if set to 1, this is an AME vector sensor. + // if set to 0, this is a normal AME scalar + // sensor. + + uint16_t test; + // if bit 15 is set to 1 in the status register, the + // test register can only be used to support + // the vector sensor. In this mode, the high 8 + // bits are used to hold two 4 bit + // indices of minimum values. The low 8 bits are + // used to hold two 4 bit indices of + // maximum values. Vector sensors can only hold + // up to 12 elements, so each 4 bit index + // can point to the vector index of interest. + // Here is a summary of what each 4 bit field + // shows: + // bits 15-12: index of minimum vector sensor + // value seen for all time (since + // last reset) + // bits 11-8: index of minimum vector sensor + // value seen for the latest time + // instant + // bits 7-4: index of maximum vector sensor + // value seen for all time (since + // last reset) + // bits 3-0: index of maximum vector sensor + // value seen for the latest time + // instant + // Vector sensors should not be longer than 12 + // elements because one can only guard out + // up to 12 elements with the vector sensor + // enable mask and to avoid extreme cycle spikes. + // A RESET of this sensor will cause the test + // register (high and low bytes) to be set to + // 0xFF. + // if bit 15 is set to 0 in the status register, this + // register can be used to store anything. +} sensorrec_t; + +typedef UINT8 AMEC_TB_GUID; +typedef UINT16 AMEC_SENSOR_GUID; + +typedef enum +{ + AMEC_TB_2MS, // GUID for 2ms trace + AMEC_TB_250US, // GUID for 250us trace + AMEC_TB_NUMBER_OF_TRACES // Number of trace buffers supported +} AMEC_TB_ENUM; + +typedef struct amec_tb +{ + // Constant properties + + ///Trace name + CHAR* name; + ///Update Frequency + UINT32 freq; + + /* Configurable properties */ + ///Pointer to raw bytes for trace buffer + UINT8* bytes; + ///Number of raw bytes + UINT32 size; + ///Number of bytes in 1 trace record. Used to increment write pointer. + UINT32 entry_size; + ///Number of records that fit in the trace buffer. (0 to n-1 can be used) + UINT32 entry_n; + ///Record number to write next for sensor data + UINT32 write; + ///Record number for next read (not used yet) + UINT32 read; + ///Number of sensors tracked in tb + UINT8 sensors_n; + ///Number of paramters tracked in tb + UINT8 parm_n; + ///List of corresponding sensor fields tracked in this trace buffer + UINT8 sensors_field[AMEC_TB_SENSORS_MAX]; + ///List of sensors tracked in this trace buffer + AMEC_SENSOR_GUID sensors_num[AMEC_TB_SENSORS_MAX]; + ///List of parameters tracked + uint16_t parm_num[AMEC_TB_PARM_MAX]; + +} amec_tb_t; + +//************************************************************************* +// Globals +//************************************************************************* + +//************************************************************************* +// Function Prototypes +//************************************************************************* +//Amester interface entry point +uint8_t amester_entry_point( const IPMIMsg_t * i_msg, + uint16_t * o_resp_length, + uint8_t * o_resp); + +// Write sensor data to trace record +// Called periodically to write next trace record with sensor data. +void amec_tb_record(const AMEC_TB_GUID i_guid); + +// Get global information on traces (names, frequencies) +// Get a list of all available trace buffers in OCC and their frequencies. +void amec_tb_cmd_info(const IPMIMsg_t * i_psMsg, UINT8 *o_pu8Resp,UINT16 *o_pu16RespLength,UINT8 *o_retval); + +// Set the configuration of a trace (which sensors to trace) +// Choose which sensors and SCOMs to trace. Choose size of trace buffer memory. +void amec_tb_cmd_set_config(const IPMIMsg_t *i_psMsg, UINT8 *o_pu8Resp,UINT16 *o_pu16RespLength,UINT8 *o_retval); + +// Begin recording all configured traces +void amec_tb_cmd_start_recording(const IPMIMsg_t *i_psMsg,UINT8 *o_pu8Resp,UINT16 *o_pu16RespLength,UINT8 *o_retval); + +// Stop recording all traces +void amec_tb_cmd_stop_recording(const IPMIMsg_t *i_psMsg,UINT8 *o_pu8Resp,UINT16 *o_pu16RespLength,UINT8 *o_retval); + +// Get bytes from trace buffer memory +// Returns a maximum size packet starting at a given index +void amec_tb_cmd_read(const IPMIMsg_t *i_psMsg,UINT8 *o_pu8Resp,UINT16 *o_pu16RespLength,UINT8 *o_retval); + +// Returns configuration of a trace +void amec_tb_cmd_get_config(const IPMIMsg_t *i_psMsg,UINT8 *o_pu8Resp,UINT16 *o_pu16RespLength,UINT8 *o_retval); + +#endif diff --git a/src/occ_405/amec/amec_analytics.c b/src/occ_405/amec/amec_analytics.c new file mode 100755 index 0000000..8b65f6d --- /dev/null +++ b/src/occ_405/amec/amec_analytics.c @@ -0,0 +1,536 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/amec/amec_analytics.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + + +//************************************************************************* +// Includes +//************************************************************************* +#include <amec_amester.h> +#include <amec_sys.h> +#include <proc_data.h> +#include <ssx.h> + +//************************************************************************* +// Externs +//************************************************************************* + +//************************************************************************* +// Macros +//************************************************************************* + +//************************************************************************* +// Defines/Enums +//************************************************************************* + +//************************************************************************* +// Structures +//************************************************************************* + +//************************************************************************* +// Globals +//************************************************************************* +extern uint8_t G_occ_interrupt_type; + +//************************************************************************* +// Function Prototypes +//************************************************************************* + +//************************************************************************* +// Functions +//************************************************************************* + +void amec_analytics_sb_recording(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + uint16_t temp16 = 0; + uint8_t k = 0; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + // Increment the internal counter here per 250us tick + g_amec->r_cnt++; + + // Need to have reached modulo time interval to record output and also just + // after the analytics_slot was reached. + temp16 = g_amec->r_cnt - g_amec->analytics_slot; + temp16 = ((1<<g_amec->stream_vector_rate)-1) & temp16; + + if ((temp16 == 0) && (g_amec->recordflag == 1)) + { + if (g_amec->stream_vector_mode == 0) // If zero, see if timer running prior to beginning a capture + { + if (g_amec->stream_vector_delay > 1) + { + g_amec->stream_vector_delay = g_amec->stream_vector_delay - 1; + } else + { + if (g_amec->stream_vector_delay == 1) + { + g_amec->stream_vector_mode = 1; // Turn on 1 shot recording + g_amec->write_stream_index = 0; // Reset to start of buffer + g_amec->stream_vector_delay = 0; // Disable any further delays + + // support L4 state machine and tracing being synchronized + if (g_amec->reset_prep != 0) + { + g_amec->cent_l4_state[g_amec->probe_l4_centaur] = 0; // Start with L4 state machine set to first state (L4_S0) + g_amec->cent_l4_ipl_state[g_amec->probe_l4_centaur] = 0; // Start with L4 IPL state machine set to first state (IPL_L4_S0) + g_amec->reset_prep = 0; // Turn off indicator of TMGT wanting to reset the OCC, which will start L4 state machine + g_amec->l4_powerdown_requestm = 1; // Raise indicator that the master OCC wants to carry out an L4 power down + } + } + } + } else + { + // Check is discarding initial frames due to analytics data getting averaged + if (g_amec->analytics_bad_output_count == 0) + { + // Stream buffer recording function done every 250usec * 2^(stream_vector_rate) + g_amec->ptr_stream_buffer[g_amec->write_stream_index] = (uint16_t)g_amec->r_cnt; + g_amec->write_stream_index++; + // WARNING -> The size of the vector recorded must be a precise multiple + // of the size of the entire stream buffer. + for (k = 1; k < STREAM_VECTOR_SIZE_EX; k++) + { + if (g_amec->stream_vector_map[k] == (void *)0xffffffff) + { + k = STREAM_VECTOR_SIZE_EX; // Terminate as partial vector complete + } else + { + temp16 = *((uint16_t * )(g_amec->stream_vector_map[k])); + g_amec->ptr_stream_buffer[g_amec->write_stream_index] = (uint16_t)temp16; + g_amec->write_stream_index++; + } + } + if (g_amec->write_stream_index >= STREAM_BUFFER_SIZE) + { + g_amec->write_stream_index = 0; // Reset to start of buffer + if (g_amec->stream_vector_mode == 1) + { + // If single shot, just rotate write ptr in last record + g_amec->write_stream_index = STREAM_BUFFER_SIZE - STREAM_VECTOR_SIZE_EX; + } + } + } else + { + g_amec->analytics_bad_output_count--; // decrement bad output counter + } + } + } +} + +void amec_analytics_main(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + uint8_t i = 0; + uint8_t j = 0; + uint8_t k = 0; + uint8_t l = 0; + uint8_t m = 0; + uint16_t temp16 = 0; + uint16_t tempreg = 0; + uint32_t temp32 = 0; + uint32_t tempaccum = 0; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + // This functions is disabled by default. Need to enable analytics via + // Amester. + if (g_amec->stream_vector_rate == 0xFF) + { + return; + } + + g_amec->packednapsleep[0] = (g_amec->proc[0].winkcnt2ms.sample<<8) + + g_amec->proc[0].sleepcnt2ms.sample; + // There are no other elements in proc[] array other than element 0 + g_amec->packednapsleep[1] = 0; + g_amec->packednapsleep[2] = 0; + g_amec->packednapsleep[3] = 0; + + switch (g_amec->analytics_group) + { + case 45: // Group 45 + + // Every 2msec (250usec * 2^stream_rate, default stream_rate=3), perform averaging of sensors. + // Averaging is required because many sensors are updated every + // 2msec and if they aren't properly averaged, those updates + // are lost in the final analytics output. + // The analytics group should be a correct average of the higher + // frequency sensor updates. + // (wait until OCC master collects all chips data) + for (i=0; i<1; i++) + { + g_amec->g44_avg[(i*MSA)+0] = (UINT32)g_amec->sys.todclock0.sample; // ptr to high 16 bits of 48bit TOD clock + g_amec->g44_avg[(i*MSA)+2] = (UINT32)g_amec->sys.todclock1.sample; // ptr to middle 16 bits of 48 bit TOD clock + g_amec->g44_avg[(i*MSA)+4] = (UINT32)g_amec->sys.todclock2.sample; // ptr to low 16 bits of 48 bit TOD clock + + tempaccum=g_amec->sys.pwr250us.src_accum_snapshot; // load pwr250us accum from last 2msec + g_amec->sys.pwr250us.src_accum_snapshot = g_amec->sys.pwr250us.accumulator; // save current accum state for next 2msec + tempaccum=g_amec->sys.pwr250us.accumulator - tempaccum; // total accumulation over 2msec + tempaccum=tempaccum>>3; // divide by 8 + g_amec->g44_avg[(i*MSA)+6] = g_amec->g44_avg[(i*MSA)+6] + + tempaccum; + + tempaccum=g_amec->sys.pwr250usgpu.src_accum_snapshot; // load pwr250usgpu accum from last 2msec + g_amec->sys.pwr250usgpu.src_accum_snapshot = g_amec->sys.pwr250usgpu.accumulator; // save current accum state for next 2msec + tempaccum=g_amec->sys.pwr250usgpu.accumulator - tempaccum; // total accumulation over 2msec + tempaccum=tempaccum>>3; // divide by 8 + g_amec->g44_avg[(i*MSA)+8] = g_amec->g44_avg[(i*MSA)+8] + + tempaccum; + + tempaccum=g_amec->proc[i].pwr250us.src_accum_snapshot; // load accumulator from last 2msec + g_amec->proc[i].pwr250us.src_accum_snapshot = g_amec->proc[i].pwr250us.accumulator; // save current accum state for next 2msec + tempaccum=g_amec->proc[i].pwr250us.accumulator - tempaccum; // total accumulation over 2msec + tempaccum=tempaccum>>3; // divide by 8 + g_amec->g44_avg[(i*MSA)+10] = g_amec->g44_avg[(i*MSA)+10] + + tempaccum; + + tempaccum=g_amec->proc[i].pwr250usvdd.src_accum_snapshot; // load accumulator from last 2msec + g_amec->proc[i].pwr250usvdd.src_accum_snapshot = g_amec->proc[i].pwr250usvdd.accumulator; // save current accum state for next 2msec + tempaccum=g_amec->proc[i].pwr250usvdd.accumulator - tempaccum; // total accumulation over 2msec + tempaccum=tempaccum>>3; + g_amec->g44_avg[(i*MSA)+11] = g_amec->g44_avg[(i*MSA)+11] + + tempaccum; + + tempaccum=g_amec->proc[i].vrm[0].volt250us.src_accum_snapshot; // load accumulator from last 2msec + g_amec->proc[i].vrm[0].volt250us.src_accum_snapshot = g_amec->proc[i].vrm[0].volt250us.accumulator; // save current accum state for next 2msec + tempaccum=g_amec->proc[i].vrm[0].volt250us.accumulator - tempaccum; // total accumulation over 2msec + temp32 = tempaccum<<3; // Pi, Vdd + tempreg = 4000; + // Convert voltage from 100uV resolution to 6.25mV resolution + tempreg = (UINT16)(UTIL_DIV32(temp32, tempreg)); + g_amec->g44_avg[(i*MSA)+12] = g_amec->g44_avg[(i*MSA)+12] + (UINT32)tempreg; + + tempaccum=g_amec->proc[i].vrm[1].volt250us.src_accum_snapshot; // load accumulator from last 2msec + g_amec->proc[i].vrm[1].volt250us.src_accum_snapshot = g_amec->proc[i].vrm[1].volt250us.accumulator; // save current accum state for next 2msec + tempaccum=g_amec->proc[i].vrm[1].volt250us.accumulator - tempaccum; // total accumulation over 2msec + temp32 = tempaccum<<3; // Pi, Vcs + tempreg = 4000; + // Convert voltage from 100uV resolution to 6.25mV resolution + tempreg = (UINT16)(UTIL_DIV32(temp32, tempreg)); + g_amec->g44_avg[(i*MSA)+13] = g_amec->g44_avg[(i*MSA)+13] + + (UINT32)tempreg; + + tempaccum=g_amec->proc[i].cur250usvdd.src_accum_snapshot; // load accumulator from last 2msec + g_amec->proc[i].cur250usvdd.src_accum_snapshot = g_amec->proc[i].cur250usvdd.accumulator; // save current accum state for next 2msec + tempaccum=g_amec->proc[i].cur250usvdd.accumulator - tempaccum; // total accumulation over 2msec + tempaccum=tempaccum>>3; + g_amec->g44_avg[(i*MSA)+14] = g_amec->g44_avg[(i*MSA)+14] + + tempaccum/100; + g_amec->g44_avg[(i*MSA)+15] = g_amec->g44_avg[(i*MSA)+15] + + (UINT32)g_amec->proc[i].temp2ms.sample; // hottest processor core temperature (average??) + +// major changes below to accommodate Group 45 + + l=16; // l = index offset + for (j=0; j<8; j++) // Group 45 supports all 8 Centaurs per OCC + { + g_amec->g44_avg[(i*MSA)+l] = g_amec->g44_avg[(i*MSA)+l] + + (UINT32)(g_amec->proc[i].memctl[j].mrd2ms.sample/78); // memory read bandwidth + l=l+1; + } + for (j=0; j<8; j++) // Group 45 supports all 8 Centaurs per OCC + { + g_amec->g44_avg[(i*MSA)+l] = g_amec->g44_avg[(i*MSA)+l] + + (UINT32)(g_amec->proc[i].memctl[j].mwr2ms.sample/78); // memory write bandwidth + l=l+1; + } + + for (j=0; j<8; j++) // Group 45 supports all 8 L4 caches per OCC + { + temp16 = g_amec->proc[i].memctl[j].centaur.portpair[0].perf.l4rd2ms; + temp16 = temp16 + g_amec->proc[i].memctl[j].centaur.portpair[1].perf.l4rd2ms; + g_amec->g44_avg[(i*MSA)+l] = g_amec->g44_avg[(i*MSA)+l] + + (UINT32)(temp16/156); // L4 read bandwidth (/156 because two portpairs added together) + l=l+1; + } + + for (j=0; j<8; j++) // Group 45 supports all 8 L4 caches per OCC + { + temp16 = g_amec->proc[i].memctl[j].centaur.portpair[0].perf.l4wr2ms; + temp16 = temp16 + g_amec->proc[i].memctl[j].centaur.portpair[1].perf.l4wr2ms; + g_amec->g44_avg[(i*MSA)+l] = g_amec->g44_avg[(i*MSA)+l] + + (UINT32)(temp16/156); // L4 write bandwidth (/156 because two portpairs added together) + l=l+1; + } + + g_amec->g44_avg[(i*MSA)+48] = g_amec->g44_avg[(i*MSA)+48] + + (UINT32)(g_amec->proc[i].winkcnt2ms.sample<<3); // counter of cores that entered winkle for at least part of the 2msec interval: 1/8th resolution + g_amec->g44_avg[(i*MSA)+49] = g_amec->g44_avg[(i*MSA)+49] + + (UINT32)(g_amec->proc[i].sleepcnt2ms.sample<<3); // counter of cores that entered sleep for at least part of the 2msec interval: 1/8th resolution + + + m=0; // counter for actual configured # of cores - 1. + for (j=0; j<12; j++) // Group 45 supports up to 12 cores to be configured per OCC chip + { + if (CORE_PRESENT(j)) + { + //average frequency for this core (apply rounding for frequency for maximum 8 bit resolution): 20MHz resolution (Power8 is actually 33.25MHz steps) + temp32 = (UINT32)g_amec->proc[i].core[j].freqa2ms.sample/10; // 10MHz resolution + temp16 = (UINT16)temp32; + temp32 = temp32 >>1; // convert to 20MHz resolution + if (temp16 & 1) temp32 = temp32+1; // if LSBit of 10MHz resolution value is a 1, then round the 20MHz resolution value up by 1 + + g_amec->g44_avg[(i*MSA)+50+m] = g_amec->g44_avg[(i*MSA)+50+m] + temp32; + + m++; // increment configured core counter + if (m > 11) j=12; // safeguard in case more than 12 configured cores. + } + } + + m=0; // counter for actual configured # of cores - 1. + for (j=0; j<12; j++) // Group 45 supports up to 12 cores to be configured per OCC chip + { + if (CORE_PRESENT(j)) + { + tempreg = 0; // keeps track of maximum thread utilization for this core + temp32 = 0; // keeps track of average thread utilization for this core for non-zero threads (threadmode=0) or all threads (threadmode=1) or no average (threadmode=2) + temp16 = 0; // keeps track of non-zero threads + for (k=0; k < g_amec->analytics_threadcountmax; k++) + { + if (tempreg < g_amec->proc[i].core[j].thread[k].util2ms_thread) + { + tempreg = g_amec->proc[i].core[j].thread[k].util2ms_thread; + } + if ((0 < g_amec->proc[i].core[j].thread[k].util2ms_thread) || + (g_amec->analytics_threadmode != 0)) + { + // accumulate for computing average + temp32 = temp32 + g_amec->proc[i].core[j].thread[k].util2ms_thread; + // increment counter of threads + temp16 = temp16 + 1; + } + } + g_amec->g44_avg[(i*MSA)+62+m] = g_amec->g44_avg[(i*MSA)+62+m] + + (UINT32)(g_amec->proc[i].core[j].util2ms.sample/50); // accumulate util sensor that feeds IPS and DPS algorithms for this core + + if (g_amec->analytics_threadmode == 2) + { + temp16 = tempreg; // Store maximum of all the threads on this core + } + if (g_amec->analytics_threadmode < 2) + { + if (temp16 > 0) + { + temp16 = (UINT16)(UTIL_DIV32(temp32, temp16)); // compute average utilization of all non-zero threads (threadmode=0) or all threads (threadmode=1) + } + } + if (g_amec->analytics_threadmode == 3) + { + // accumulate average finish latency counter for this core + temp16 = ((g_amec->proc[i].core[j].mcpifi2ms.sample) >>1); + } + + temp32 = (UINT32)(temp16/25); // 0.25% utilization resolution + temp32 = temp32 >>1; // convert to 0.5% utilization resolution + if (temp16 & 1) temp32 = temp32+1; // if LSBit of 0.25% utilization resolution value is a 1, then round the 0.5% utilization resolution value up by 1 + g_amec->g44_avg[(i * MSA) + 74 + m] = g_amec->g44_avg[(i * MSA) + 74 + m] + + (UINT32)(temp32); // accumulate average utilization or individual threads for this core or finish latency counter + g_amec->g44_avg[(i * MSA) + 86 + m] = g_amec->g44_avg[(i * MSA) + 86 + m] + + (UINT32)(g_amec->proc[i].core[j].ips2ms.sample / 50); // accumulate average MIPS for this core + g_amec->g44_avg[(i * MSA) + 98 + m] = g_amec->g44_avg[(i * MSA) + 98 + m] + + (UINT32)g_amec->proc[i].core[j].temp2ms.sample; // accumulate average temperature for this core + g_amec->g44_avg[(i * MSA) + 110 + m] = g_amec->g44_avg[(i * MSA) + 110 + m] + + (UINT32)((g_amec->proc[i].core[j].cmbw2ms.sample) / 156); // accumulate average memory bandwidth for this core //@mw713 /156, was /78 (overflow issues) + temp16 = ((g_amec->proc[i].core[j].mcpifd2ms.sample) / 100); // accumulate average busy latency counter for this core + g_amec->g44_avg[(i * MSA) + 122 + m] = g_amec->g44_avg[(i * MSA) + 122 + m] + (UINT32)temp16; + m++; // increment configured core counter + if (m > 11) j = 12; // safeguard in case more than 12 configured cores. + } + } // End loop processing each core + } // End loop processing each chip + + // Determine when to update final analytics_array + temp16 = g_amec->r_cnt - g_amec->analytics_slot; + temp16 = ((1<<g_amec->stream_vector_rate)-1) & temp16; + + // Have we completed this interval so that we can output? + if (temp16 == 0) + { + // Now, update Group 45 analytics packed array + switch (g_amec->analytics_thermal_offset) + { + case 0: + tempreg = (g_amec->sys.tempambient.sample) << 8; // upper byte + tempreg = tempreg | 0x8000; // Turn on MSBit for temporal frame sync + break; + + case 1: + if (g_amec->mst_ips_parms.active == 0) + { + tempreg = 0; // If not in IPS mode, return 0 + } + else + { + tempreg = 127; // If in IPS, return constant indicating in IPS mode + } + if (tempreg > 127) tempreg = 127; // Saturate at 7 bit limit (508 seconds) + tempreg = (tempreg) << 8; // upper byte + break; + + case 2: + tempreg=(g_amec->mst_ips_parms.active)<<8; // upper byte + break; + + case 3: + tempreg = (g_amec->fan.fanspeedavg.sample / 100) << 8; // upper byte (100 RPM resolution) + break; + + case 4: + tempreg = (g_amec->proc[0].temp2msdimm.sample) << 8; // upper byte + break; + + case 5: + tempreg = (g_amec->proc[0].temp2mscent.sample) << 8; // upper byte + break; + + case 6: + // tempreg=(g_amec->proc[2].temp2msdimm.sample)<<8; // upper byte + tempreg = 0; + break; + + case 7: + // tempreg=(g_amec->proc[2].temp2mscent.sample)<<8; // upper byte + tempreg = 0; + break; + + default: + break; + + } + g_amec->analytics_thermal_offset = 0x7 & + (g_amec->analytics_thermal_offset + 1); // modulo 8 + + tempaccum = g_amec->fan.pwr250usfan.src_accum_snapshot; // load accumulator from last 2msec + g_amec->fan.pwr250usfan.src_accum_snapshot = g_amec->fan.pwr250usfan.accumulator; // save current accum state for next 2msec + tempaccum = g_amec->fan.pwr250usfan.accumulator - tempaccum; // total accumulation over 2msec + tempaccum = tempaccum >> g_amec->stream_vector_rate; + + tempreg = tempreg | (0xff & ((UINT16)tempaccum)); + g_amec->analytics_array[5] = tempreg; + + tempaccum = g_amec->io.pwr250usio.src_accum_snapshot; // load accumulator from last 2msec + g_amec->io.pwr250usio.src_accum_snapshot = g_amec->io.pwr250usio.accumulator; // save current accum state for next 2msec + tempaccum = g_amec->io.pwr250usio.accumulator - tempaccum; // total accumulation over 2msec + tempaccum = tempaccum >> g_amec->stream_vector_rate; + + tempreg = ((UINT16)tempaccum) << 8; // upper byte + + tempaccum = g_amec->storage.pwr250usstore.src_accum_snapshot; // load accumulator from last 2msec + g_amec->storage.pwr250usstore.src_accum_snapshot = g_amec->storage.pwr250usstore.accumulator; // save current accum state for next 2msec + tempaccum = g_amec->storage.pwr250usstore.accumulator - tempaccum; // total accumulation over 2msec + tempaccum = tempaccum >> g_amec->stream_vector_rate; + + tempreg = tempreg | (0xff & ((UINT16)tempaccum)); + g_amec->analytics_array[6] = tempreg; + + tempaccum = g_amec->proc[0].pwr250usmem.src_accum_snapshot; // load accumulator from last 2msec + g_amec->proc[0].pwr250usmem.src_accum_snapshot = g_amec->proc[0].pwr250usmem.accumulator; // save current accum state for next 2msec + tempaccum = g_amec->proc[0].pwr250usmem.accumulator - tempaccum; // total accumulation over 2msec + tempaccum = tempaccum >> g_amec->stream_vector_rate; + + tempreg = ((UINT16)tempaccum) << 8; // upper byte + g_amec->analytics_array[7] = tempreg; + g_amec->analytics_array[8] = 0; + + // Now begins the per processor unique data + tempreg = (g_amec->analytics_total_chips) << 8; // upper byte + tempreg = tempreg | (0xff & (g_amec->analytics_chip)); // which chip is outputting this interval? + g_amec->analytics_array[9] = tempreg; + j = g_amec->analytics_chip; // select which chip to process + + if (g_amec->analytics_option == 0) + { + k = 0; // Default to no shift, if user didn't enter analytics_total_chips (set to 0) + if (g_amec->analytics_total_chips == 1) k = g_amec->stream_vector_rate - 3; // (2msec * 2^k) is shift for averaging interval (16msec) + if (g_amec->analytics_total_chips == 2) k = g_amec->stream_vector_rate - 2; // (2msec * 2^k) is shift for averaging interval (32msec) + if (g_amec->analytics_total_chips == 4) k = g_amec->stream_vector_rate - 1; // (2msec * 2^k) is shift for averaging interval (64msec) + if (g_amec->analytics_total_chips == 8) k = g_amec->stream_vector_rate; // (2msec * 2^k) is shift for averaging interval (128msec) + } + if (g_amec->analytics_option == 1) + { + k = g_amec->stream_vector_rate - 3; // (2msec * 2^k) is shift for averaging interval (16msec) + } + + l = 12; // index offset + + g_amec->analytics_array[0] = (UINT16)g_amec->g44_avg[(j * MSA) + 0]; // todclock1 (hi 16 bits, no averaging) + g_amec->analytics_array[1] = (UINT16)g_amec->g44_avg[(j * MSA) + 2]; // todclock1 (medium 16 bits, no averaging) + g_amec->analytics_array[2] = (UINT16)g_amec->g44_avg[(j * MSA) + 4]; // todclock1 (lo 16 bits, no averaging) + + g_amec->analytics_array[3] = (UINT16)(g_amec->g44_avg[(j * MSA) + 6] >> k); // the first two averages are 16 bits + g_amec->g44_avg[(j * MSA) + 6] = 0; // reset average for this sensor to 0 + g_amec->analytics_array[4] = (UINT16)(g_amec->g44_avg[(j * MSA) + 8] >> k); // the first two averages are 16 bits + g_amec->g44_avg[(j * MSA) + 8] = 0; // reset average for this sensor to 0 + g_amec->analytics_array[10] = (UINT16)(g_amec->g44_avg[(j * MSA) + 10] >> k); // the first two averages are 16 bits + g_amec->g44_avg[(j * MSA) + 10] = 0; // reset average for this sensor to 0 + g_amec->analytics_array[11] = (UINT16)(g_amec->g44_avg[(j * MSA) + 11] >> k); // the first two averages are 16 bits + g_amec->g44_avg[(j * MSA) + 11] = 0; // reset average for this sensor to 0 + + for (i = 12; i <= 72; i++) + { + temp16 = (UINT16)(g_amec->g44_avg[(j * MSA) + l] >> k); + tempreg = temp16 << 8; // upper byte + temp16 = (UINT16)(g_amec->g44_avg[(j * MSA) + l + 1] >> k); + tempreg = tempreg | (0xff & temp16); + g_amec->analytics_array[i] = tempreg; + g_amec->g44_avg[(j * MSA) + l] = 0; // Reset average for this sensor to 0 + g_amec->g44_avg[(j * MSA) + l + 1] = 0; // Reset average for this sensor to 0 + + l = l + 2; + } + + // Final processing for Group 45: determine if cycling through all + // chips or just monitoring one chip + if (g_amec->analytics_option == 0) + { + g_amec->analytics_chip++; + + if (g_amec->analytics_chip >= g_amec->analytics_total_chips) + { + g_amec->analytics_chip = 0; // loop back to chip 0 again + } + } + + } + break; + + default: + break; + } +} + +/*----------------------------------------------------------------------------*/ +/* End */ +/*----------------------------------------------------------------------------*/ diff --git a/src/occ_405/amec/amec_analytics.h b/src/occ_405/amec/amec_analytics.h new file mode 100755 index 0000000..b6a344e --- /dev/null +++ b/src/occ_405/amec/amec_analytics.h @@ -0,0 +1,47 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ_405/amec/amec_analytics.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#ifndef _AMEC_ANALYTICS_H +#define _AMEC_ANALYTICS_H + +/*----------------------------------------------------------------------------*/ +/* Includes */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Defines/Constants */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Function Declarations */ +/*----------------------------------------------------------------------------*/ + +// Function that enables stream buffer recording +void amec_analytics_sb_recording(void); + +// Function that is called by AMEC State Machine for analytics +void amec_analytics_main(void); + +#endif // _AMEC_ANALYTICS_H diff --git a/src/occ_405/amec/amec_controller.c b/src/occ_405/amec/amec_controller.c new file mode 100644 index 0000000..0b8b7e1 --- /dev/null +++ b/src/occ_405/amec/amec_controller.c @@ -0,0 +1,438 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ_405/amec/amec_controller.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +//************************************************************************* +// Includes +//************************************************************************* +#include <occ_common.h> +#include <sensor.h> +#include <amec_sys.h> + +//************************************************************************* +// Externs +//************************************************************************* +extern uint8_t G_dimm_temp_expired_bitmap; +extern uint8_t G_cent_temp_expired_bitmap; +//************************************************************************* +// Macros +//************************************************************************* + +//************************************************************************* +// Defines/Enums +//************************************************************************* + +//************************************************************************* +// Structures +//************************************************************************* + +//************************************************************************* +// Globals +//************************************************************************* + +//************************************************************************* +// Function Prototypes +//************************************************************************* + +//************************************************************************* +// Functions +//************************************************************************* +// Function Specification +// +// Name: amec_controller_proc_thermal +// +// Description: This function implements the Proportional Controller for the +// processor thermal control. Although it doesn't return any +// results, it populates the thermal vote in the field +// g_amec->thermalproc.speed_request. +// +// Task Flags: +// +// End Function Specification +void amec_controller_proc_thermal() +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + uint16_t l_thermal_winner = 0; + uint16_t l_residue = 0; + uint16_t l_old_residue = 0; + int16_t l_error = 0; + int16_t l_cpu_speed = 0; + int16_t l_throttle_chg = 0; + int32_t l_throttle = 0; + sensor_t * l_sensor = NULL; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + // Get TEMP2MSP0PEAK sensor, which is hottest core temperature in OCC + // processor + l_sensor = getSensorByGsid(TEMP2MSP0PEAK); + + // Use the highest temperature of all processors in 0.1 degrees C + l_thermal_winner = l_sensor->sample * 10; + + // Check if there is an error + if (g_amec->thermalproc.setpoint == l_thermal_winner) + return; + + // Calculate the thermal control error + l_error = g_amec->thermalproc.setpoint - l_thermal_winner; + + // TODO: Get with Malcolm to migrate to 16-bit multiply + // Proportional Controller for the thermal control loop + l_throttle = (int32_t) l_error * g_amec->thermalproc.Pgain; + l_residue = (uint16_t) l_throttle; + l_throttle_chg = (int16_t) (l_throttle >> 16); + + if ((int16_t) l_throttle_chg > (int16_t) g_amec->sys.speed_step_limit) + { + l_throttle_chg = g_amec->sys.speed_step_limit; + } + else + { + if ((int16_t) l_throttle_chg < ((int16_t) (-g_amec->sys.speed_step_limit))) + { + l_throttle_chg = (int16_t)(-g_amec->sys.speed_step_limit); + } + } + + // Calculate the new thermal CPU speed request + l_cpu_speed = g_amec->thermalproc.speed_request + + (int16_t) l_throttle_chg * g_amec->sys.speed_step; + + // Proceed with residue summation to correctly follow set-point + l_old_residue = g_amec->thermalproc.total_res; + g_amec->thermalproc.total_res += l_residue; + if (g_amec->thermalproc.total_res < l_old_residue) + { + l_cpu_speed += g_amec->sys.speed_step; + } + + // Enforce actuator saturation limits + if (l_cpu_speed > g_amec->sys.max_speed) + l_cpu_speed = g_amec->sys.max_speed; + if (l_cpu_speed < g_amec->sys.min_speed) + l_cpu_speed = g_amec->sys.min_speed; + + // Generate the new thermal speed request + g_amec->thermalproc.speed_request = l_cpu_speed; + // Calculate frequency request based on thermal speed request + g_amec->thermalproc.freq_request = amec_controller_speed2freq( + g_amec->thermalproc.speed_request, + g_amec->sys.fmax); +} + +//************************************************************************* +// Function Specification +// +// Name: amec_controller_dimm_thermal +// +// Description: This function implements the Proportional Controller for the +// DIMM thermal control. Although it doesn't return any +// results, it populates the thermal vote in the field +// g_amec->thermaldimm.speed_request. +// +// Task Flags: +// +// End Function Specification +void amec_controller_dimm_thermal() +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + uint16_t l_thermal_winner = 0; + uint16_t l_residue = 0; + uint16_t l_old_residue = 0; + int16_t l_error = 0; + int16_t l_mem_speed = 0; + int16_t l_throttle_chg = 0; + int32_t l_throttle = 0; + sensor_t * l_sensor = NULL; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + // Get TEMP2MSDIMM sensor value + l_sensor = getSensorByGsid(TEMP2MSDIMM); + + if(G_dimm_temp_expired_bitmap) + { + //we were not able to read one or more dimm temperatures. + //Assume temperature is at the setpoint plus 1 degree C. + l_thermal_winner = g_amec->thermaldimm.setpoint + 10; + } + else + { + // Use the highest temperature of all DIMMs in 0.1 degrees C + l_thermal_winner = l_sensor->sample * 10; + } + + // Check if there is an error + if (g_amec->thermaldimm.setpoint == l_thermal_winner) + return; + + // Calculate the thermal control error + l_error = g_amec->thermaldimm.setpoint - l_thermal_winner; + + // Proportional Controller for the thermal control loop based on DIMM + // temperatures + l_throttle = (int32_t) l_error * g_amec->thermaldimm.Pgain; + l_residue = (uint16_t) l_throttle; + l_throttle_chg = (int16_t) (l_throttle >> 16); + + if ((int16_t) l_throttle_chg > AMEC_MEMORY_SPEED_CHANGE_LIMIT) + { + l_throttle_chg = AMEC_MEMORY_SPEED_CHANGE_LIMIT; + } + else + { + if ((int16_t) l_throttle_chg < (-AMEC_MEMORY_SPEED_CHANGE_LIMIT)) + { + l_throttle_chg = -AMEC_MEMORY_SPEED_CHANGE_LIMIT; + } + } + + // Calculate the new thermal speed request for DIMMs + l_mem_speed = g_amec->thermaldimm.speed_request + + (int16_t) l_throttle_chg * AMEC_MEMORY_STEP_SIZE; + + // Proceed with residue summation to correctly follow set-point + l_old_residue = g_amec->thermaldimm.total_res; + g_amec->thermaldimm.total_res += l_residue; + if (g_amec->thermaldimm.total_res < l_old_residue) + { + l_mem_speed += AMEC_MEMORY_STEP_SIZE; + } + + // Enforce actuator saturation limits + if (l_mem_speed > AMEC_MEMORY_MAX_STEP) + l_mem_speed = AMEC_MEMORY_MAX_STEP; + if (l_mem_speed < AMEC_MEMORY_MIN_STEP) + l_mem_speed = AMEC_MEMORY_MIN_STEP; + + // Generate the new thermal speed request + g_amec->thermaldimm.speed_request = (uint16_t) l_mem_speed; +} + + +//************************************************************************* +// Function Specification +// +// Name: amec_controller_centaur_thermal +// +// Description: This function implements the Proportional Controller for the +// centaur thermal control. Although it doesn't return any +// results, it populates the thermal vote in the field +// g_amec->thermalcent.speed_request. +// +// Task Flags: +// +// End Function Specification +void amec_controller_centaur_thermal() +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + uint16_t l_thermal_winner = 0; + uint16_t l_residue = 0; + uint16_t l_old_residue = 0; + int16_t l_error = 0; + int16_t l_mem_speed = 0; + int16_t l_throttle_chg = 0; + int32_t l_throttle = 0; + sensor_t * l_sensor = NULL; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + // Get TEMP2MSCENT sensor value + l_sensor = getSensorByGsid(TEMP2MSCENT); + + if(G_cent_temp_expired_bitmap) + { + //we were not able to get a valid temperature. Assume it is 1 degree + //over the setpoint. + l_thermal_winner = g_amec->thermalcent.setpoint + 10; + } + else + { + // Use the highest temperature of all Centaur in 0.1 degrees C + l_thermal_winner = l_sensor->sample * 10; + } + + // Check if there is an error + if (g_amec->thermalcent.setpoint == l_thermal_winner) + return; + + // Calculate the thermal control error + l_error = g_amec->thermalcent.setpoint - l_thermal_winner; + + // Proportional Controller for the thermal control loop based on CENTAUR + // temperatures + l_throttle = (int32_t) l_error * g_amec->thermalcent.Pgain; + l_residue = (uint16_t) l_throttle; + l_throttle_chg = (int16_t) (l_throttle >> 16); + + if ((int16_t) l_throttle_chg > AMEC_MEMORY_SPEED_CHANGE_LIMIT) + { + l_throttle_chg = AMEC_MEMORY_SPEED_CHANGE_LIMIT; + } + else + { + if ((int16_t) l_throttle_chg < (-AMEC_MEMORY_SPEED_CHANGE_LIMIT)) + { + l_throttle_chg = -AMEC_MEMORY_SPEED_CHANGE_LIMIT; + } + } + + // Calculate the new thermal speed request for Centaurs + l_mem_speed = g_amec->thermalcent.speed_request + + (int16_t) l_throttle_chg * AMEC_MEMORY_STEP_SIZE; + + // Proceed with residue summation to correctly follow set-point + l_old_residue = g_amec->thermalcent.total_res; + g_amec->thermalcent.total_res += l_residue; + if (g_amec->thermalcent.total_res < l_old_residue) + { + l_mem_speed += AMEC_MEMORY_STEP_SIZE; + } + + // Enforce actuator saturation limits + if (l_mem_speed > AMEC_MEMORY_MAX_STEP) + l_mem_speed = AMEC_MEMORY_MAX_STEP; + if (l_mem_speed < AMEC_MEMORY_MIN_STEP) + l_mem_speed = AMEC_MEMORY_MIN_STEP; + + // Generate the new thermal speed request + g_amec->thermalcent.speed_request = (uint16_t) l_mem_speed; +} + +//************************************************************************* +// Function Specification +// +// Name: amec_controller_vrhotproc +// +// Description: This function implements a Single-step Controller for the +// processor VRHOT signal. Although it doesn't return any +// results, it populates the frequency vote in the field +// g_amec->vrhotproc.freq_request +// +// +// End Function Specification +void amec_controller_vrhotproc() +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + int16_t l_cpu_speed = 0; + int16_t l_throttle_chg = 0; + sensor_t *l_sensor = NULL; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + // Get VRHOT sensor, which is really a counter of consecutive times the + // VRHOT signal has been asserted + l_sensor = getSensorByGsid(VRHOT250USPROC); + + // Single-step controller: + // If sensor VRHOT250USPROC is above the set-point, we need to step down. + // Else, do a single step up. + if (l_sensor->sample >= g_amec->vrhotproc.setpoint) + { + l_throttle_chg = (int16_t)(-1); + } + else + { + l_throttle_chg = (int16_t)(1); + } + + // Calculate the new VRHOTPROC speed request + l_cpu_speed = g_amec->vrhotproc.speed_request + + (int16_t) l_throttle_chg * g_amec->sys.speed_step; + + // Never allow negative speed requests + if (l_cpu_speed < 0) + { + l_cpu_speed = 0; + } + + // Enforce actuator saturation limits + if (l_cpu_speed > g_amec->sys.max_speed) + { + l_cpu_speed = g_amec->sys.max_speed; + } + if (l_cpu_speed < g_amec->sys.min_speed) + { + l_cpu_speed = g_amec->sys.min_speed; + } + + // Generate the new VRHOTPROC frequency request + g_amec->vrhotproc.speed_request = l_cpu_speed; + g_amec->vrhotproc.freq_request = + amec_controller_speed2freq(g_amec->vrhotproc.speed_request, + g_amec->sys.fmax); + +} + +//************************************************************************* +// Function Specification +// +// Name: amec_controller_speed2freq +// +// Description: Helper function to convert speed to MHz. +// +// End Function Specification +uint16_t amec_controller_speed2freq (const uint16_t i_speed, const uint16_t i_fmax) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + uint16_t l_tempreg = 0; + uint16_t l_temp16 = 0; + uint32_t l_temp32 = 0; + uint16_t l_freq = 0; + uint32_t l_divide32[2] = {0, 0}; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + l_temp16 = i_fmax; + l_tempreg = (uint16_t)i_speed; + l_temp32 = ((uint32_t)l_tempreg)*((uint32_t)l_temp16); + l_temp16 = (uint16_t)1000; + l_divide32[1] = (uint32_t)l_temp16; + l_divide32[0] = (uint32_t)l_temp32; + l_divide32[0] /= l_divide32[1]; + l_temp32 = l_divide32[0]; + l_freq = (uint16_t)l_temp32; /* freq will always fit in 16 bits */ + + return l_freq; +} + +/*----------------------------------------------------------------------------*/ +/* End */ +/*----------------------------------------------------------------------------*/ diff --git a/src/occ_405/amec/amec_controller.h b/src/occ_405/amec/amec_controller.h new file mode 100644 index 0000000..8b2c1b9 --- /dev/null +++ b/src/occ_405/amec/amec_controller.h @@ -0,0 +1,132 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ_405/amec/amec_controller.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#ifndef _AMEC_CONTROLLER_H +#define _AMEC_CONTROLLER_H +/*----------------------------------------------------------------------------*/ +/* Includes */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Constants */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Globals */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Defines */ +/*----------------------------------------------------------------------------*/ +// Maximum speed supported for memory controllers (100.0%) +#define AMEC_MEMORY_MAX_STEP 1000 + +// Minimum speed supported for memory controllers (1.0%) +#define AMEC_MEMORY_MIN_STEP 10 + +// Step size for increasing or decreasing memory speed (1.0%) +#define AMEC_MEMORY_STEP_SIZE 10 + +// Limit value on speed change to avoid overflow of the controller +#define AMEC_MEMORY_SPEED_CHANGE_LIMIT (UINT16_MAX/4)/AMEC_MEMORY_STEP_SIZE + + +/*----------------------------------------------------------------------------*/ +/* Typedef / Enum */ +/*----------------------------------------------------------------------------*/ +///Controller Model +typedef struct amec_controller +{ + ///Over-temperature error for FRU callout (in degrees C) + uint8_t ot_error; + ///Setpoint or reference for the controller + uint16_t setpoint; + ///Proportional gain of the controller + uint16_t Pgain; + ///Theoretical controller residue summation + uint16_t total_res; + ///Final speed request of the controller + uint16_t speed_request; + ///Final frequency request of the controller + uint16_t freq_request; + ///Expiration time for a temperature reading (in seconds) + uint8_t temp_timeout; +}amec_controller_t; + + +/*----------------------------------------------------------------------------*/ +/* Function Prototypes */ +/*----------------------------------------------------------------------------*/ + +/** + * Thermal Control Loop based on processor temperatures. + * + * This function implements the Proportional Controller for the thermal + * control loop. Although it doesn't return any results, it populates the + * thermal vote in the field g_amec->thermalproc.speed_request. + * + */ +void amec_controller_proc_thermal(); + +/** + * Thermal Control Loop based on Centaur temperatures. + * + * This function implements a Proportional Controller for the + * thermal control loop based on Centaur temperatures. + * Although it doesn't return any results, it populates the + * thermal vote in the field g_amec->thermalcent.speed_request. + * + */ +void amec_controller_centaur_thermal(); + +/** + * Thermal Control Loop based on DIMM temperatures. + * + * This function implements a Proportional Controller for the + * thermal control loop based on DIMM temperatures. Although it + * doesn't return any results, it populates the thermal vote in + * the field g_amec->thermaldimm.speed_request. + * + */ +void amec_controller_dimm_thermal(); + +/** + * Helper function to convert speed to MHz + * + * This function converts a given speed in percentage (%) to a + * frequency in MHz. + * + */ +uint16_t amec_controller_speed2freq (const uint16_t i_speed, const uint16_t i_fmax); + +/** + * Thermal Control Loop based on VRHOT signal from processor + * regulators. + * + */ +void amec_controller_vrhotproc(); + + +#endif //_AMEC_CONTROLLER_H diff --git a/src/occ_405/amec/amec_data.c b/src/occ_405/amec/amec_data.c new file mode 100755 index 0000000..33142e1 --- /dev/null +++ b/src/occ_405/amec/amec_data.c @@ -0,0 +1,477 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/amec/amec_data.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +//************************************************************************* +// Includes +//************************************************************************* +#include <occ_common.h> +#include <ssx.h> +#include <errl.h> +#include "sensor.h" +#include "rtls.h" +#include "occ_sys_config.h" +#include "occ_service_codes.h" // for SSX_GENERIC_FAILURE +#include "dcom.h" +#include "proc_data.h" +#include "proc_data_control.h" +#include "amec_smh.h" +#include "amec_slave_smh.h" +#include <trac.h> +#include "amec_sys.h" +#include "sensor_enum.h" +#include "amec_service_codes.h" +#include <amec_sensors_core.h> +#include <amec_sensors_power.h> +#include <amec_sensors_fw.h> +#include <amec_data.h> +#include <amec_freq.h> +#include <thrm_thread.h> + +//************************************************************************* +// Externs +//************************************************************************* + +//************************************************************************* +// Defines/Enums +//************************************************************************* + +//************************************************************************* +// Structures +//************************************************************************* + +//************************************************************************* +// Globals +//************************************************************************* +extern uint8_t G_occ_interrupt_type; + +//************************************************************************* +// Function Prototypes +//************************************************************************* + +//************************************************************************* +// Functions +//************************************************************************* + + +// Function Specification +// +// Name: AMEC_data_write_fcurr +// +// Description: +// +// Thread: RealTime Loop +// +// Task Flags: +// +// End Function Specification +errlHndl_t AMEC_data_write_fcurr(const OCC_MODE i_mode) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + errlHndl_t l_err = NULL; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + // If we're active we need to load this new range into DVFS MIN/MAX + if(CURRENT_STATE() == OCC_STATE_ACTIVE) + { + // Use i_mode here since this function understands turbo + l_err = amec_set_freq_range(i_mode); + + if(l_err) + { + //break; + } + } + + // If we are in OpenPower environment, load this new range into DVFS + // min/max for AMEC component + if(G_occ_interrupt_type != FSP_SUPPORTED_OCC) + { + g_amec->sys.fmax = G_sysConfigData.sys_mode_freq.table[OCC_MODE_TURBO]; + g_amec->sys.fmin = G_sysConfigData.sys_mode_freq.table[OCC_MODE_MIN_FREQUENCY]; + + TRAC_INFO("AMEC_data_write_fcurr: New frequency range Fmin[%u] Fmax[%u]", + g_amec->sys.fmin, + g_amec->sys.fmax); + } + + return l_err; +} + +// Function Specification +// +// Name: AMEC_data_write_thrm_thresholds +// +// Description: This function loads data from the Thermal Control Threshold +// data packet (format 0x13) into g_amec structure. This function should be +// called when OCC goes active or changes modes or goes in/out of Acoustic +// mode (ITE-only mode). +// +// Thread: RealTime Loop +// +// Task Flags: +// +// End Function Specification +errlHndl_t AMEC_data_write_thrm_thresholds(const OCC_MODE i_mode) +{ + + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + errlHndl_t l_err = NULL; + cmdh_thrm_thresholds_t *l_data = NULL; + cmdh_thrm_thresholds_set_t *l_frudata = NULL; + uint8_t l_dvfs_temp = 0; + uint8_t l_error = 0; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + do + { + // Retrieve the thermal control threshold data + l_err = DATA_get_thrm_thresholds(&l_data); + if(l_err) + { + TRAC_ERR("AMEC_data_write_thrm_thresholds: Error retrieving thermal control threshold data"); + break; + } + + // Notify thermal thread to update its local copy of the thermal thresholds + THRM_thread_update_thresholds(); + + l_frudata = l_data->data; + + // TODO: Need to check if acoustic mode has been enabled (ITE-only mode) + + // Store the processor thermal data + if(i_mode == OCC_MODE_NOMINAL) + { + l_dvfs_temp = l_frudata[DATA_FRU_PROC].dvfs; + l_error = l_frudata[DATA_FRU_PROC].error; + } + else + { + l_dvfs_temp = l_frudata[DATA_FRU_PROC].pm_dvfs; + if(i_mode == OCC_MODE_TURBO) + { + //Need to log an error if we dvfs in static turbo mode (for mfg) + l_error = l_dvfs_temp; + } + else + { + l_error = l_frudata[DATA_FRU_PROC].pm_error; + } + } + // Store the DVFS thermal setpoint in 0.1 degrees C + g_amec->thermalproc.setpoint = l_dvfs_temp * 10; + // Store the error temperature for OT detection + g_amec->thermalproc.ot_error = l_error; + // Store the temperature timeout value + g_amec->thermalproc.temp_timeout = l_frudata[DATA_FRU_PROC].max_read_timeout; + + TRAC_INFO("AMEC_data_write_thrm_thresholds: Setting %u as DVFS setpoint for processor", + l_dvfs_temp); + + // Store the Centaur thermal data + if(i_mode == OCC_MODE_NOMINAL) + { + l_dvfs_temp = l_frudata[DATA_FRU_CENTAUR].dvfs; + l_error = l_frudata[DATA_FRU_CENTAUR].error; + } + else + { + l_dvfs_temp = l_frudata[DATA_FRU_CENTAUR].pm_dvfs; + if(i_mode == OCC_MODE_TURBO) + { + //Need to log an error if we throttle in static turbo mode (for mfg) + l_error = l_dvfs_temp; + } + else + { + l_error = l_frudata[DATA_FRU_CENTAUR].pm_error; + } + } + + // Store the DVFS thermal setpoint in 0.1 degrees C + g_amec->thermalcent.setpoint = l_dvfs_temp * 10; + // Store the error temperature for OT detection + g_amec->thermalcent.ot_error = l_error; + // Store the temperature timeout value + g_amec->thermalcent.temp_timeout = l_frudata[DATA_FRU_CENTAUR].max_read_timeout; + + TRAC_INFO("AMEC_data_write_thrm_thresholds: Setting %u as DVFS setpoint for Centaur", + l_dvfs_temp); + + // Store the DIMM thermal data + if(i_mode == OCC_MODE_NOMINAL) + { + l_dvfs_temp = l_frudata[DATA_FRU_DIMM].dvfs; + l_error = l_frudata[DATA_FRU_DIMM].error; + } + else + { + l_dvfs_temp = l_frudata[DATA_FRU_DIMM].pm_dvfs; + if(i_mode == OCC_MODE_TURBO) + { + //Need to log an error if we throttle in static turbo mode (for mfg) + l_error = l_dvfs_temp; + } + else + { + l_error = l_frudata[DATA_FRU_DIMM].pm_error; + } + } + // Store the DVFS thermal setpoint in 0.1 degrees C + g_amec->thermaldimm.setpoint = l_dvfs_temp * 10; + // Store the error temperature for OT detection + g_amec->thermaldimm.ot_error = l_error; + // Store the temperature timeout value + g_amec->thermaldimm.temp_timeout = l_frudata[DATA_FRU_DIMM].max_read_timeout; + + TRAC_INFO("AMEC_data_write_thrm_thresholds: Setting %u as DVFS setpoint for DIMM", + l_dvfs_temp); + + // Store the VRM thermal data + g_amec->proc[0].vrfan_error_count = l_frudata[DATA_FRU_VRM].sample_error_count; + g_amec->vrhotproc.setpoint = l_frudata[DATA_FRU_VRM].error_count; + + TRAC_INFO("AMEC_data_write_thrm_thresholds: Setting %u as DVFS setpoint for VRHOT", + g_amec->vrhotproc.setpoint); + + } while(0); + + return l_err; +} + +// Function Specification +// +// Name: AMEC_data_write_ips_config +// +// Description: This function loads data from the IPS Config data packet +// (format 0x11) into g_amec structure. This function should only by called by +// Master OCC firmware. +// +// Thread: RealTime Loop +// +// End Function Specification +errlHndl_t AMEC_data_write_ips_cnfg(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + errlHndl_t l_err = NULL; + cmdh_ips_config_data_t *l_data = NULL; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + do + { + // Retrieve the thermal control threshold data + l_err = DATA_get_ips_cnfg(&l_data); + if(l_err) + { + TRAC_ERR("AMEC_data_write_ips_cnfg: Error retrieving IPS config data"); + break; + } + + // Store Idle Power Saver settings for AMEC + // Delay times should be stored in number of samples + g_amec->mst_ips_parms.entry_delay = l_data->iv_delayTimeforEntry * + AMEC_DPS_SAMPLING_RATE; + g_amec->mst_ips_parms.exit_delay = l_data->iv_delayTimeforExit * + AMEC_DPS_SAMPLING_RATE; + + // Utilization thresholds should be stored in hundredths of a percent + g_amec->mst_ips_parms.entry_threshold = l_data->iv_utilizationForEntry * 100; + g_amec->mst_ips_parms.exit_threshold = l_data->iv_utilizationForExit * 100; + + // Enable/disable Idle Power Saver + g_amec->mst_ips_parms.enable = l_data->iv_ipsEnabled; + + } while(0); + + return l_err; +} + +// Function Specification +// +// Name: AMEC_data_change +// +// Description: +// +// Thread: RealTime Loop +// +// Task Flags: +// +// End Function Specification +errlHndl_t AMEC_data_change(const uint32_t i_data_mask) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + errlHndl_t l_err = NULL; + OCC_MODE l_cur_mode = OCC_MODE_NOCHANGE; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + //l_cur_state = CURRENT_STATE(); + l_cur_mode = CURRENT_MODE(); + + //If we received the frequency from TMGT + if(i_data_mask & DATA_MASK_FREQ_PRESENT) + { + l_err = AMEC_data_write_fcurr(l_cur_mode); + } + else if(i_data_mask & DATA_MASK_THRM_THRESHOLDS) + { + l_err = AMEC_data_write_thrm_thresholds(l_cur_mode); + + if(l_err) + { + TRAC_ERR("AMEC_data_change: Error writing thermal threshold data!"); + } + } + else if(i_data_mask & DATA_MASK_IPS_CNFG) + { + l_err = AMEC_data_write_ips_cnfg(); + + if(l_err) + { + TRAC_ERR("AMEC_data_change: Error writing IPS config data!"); + } + } + + return l_err; +} + +// Function Specification +// +// Name: amec_data_write_pcap +// +// Description: Function called by slave interrupt handler to collect pcap +// data sent by Master OCC. +// If a new packet is received from Master, it will be written to +// G_sysConfigData.pcap, and g_amec->pcap will be updated too. +// Otherwise nothing happens. +// +// Thread: Interrupt Handler +// +// End Function Specification +void amec_data_write_pcap(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + static uint8_t L_pcap_data_count = 0; + uint16_t l_customer = 0; + uint16_t l_system = 0; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + //Check if Master sent a new packet of data. + if(L_pcap_data_count != G_dcom_slv_inbox_doorbell_rx.pcap.pcap_data_count) + { + //Update counter + L_pcap_data_count = G_dcom_slv_inbox_doorbell_rx.pcap.pcap_data_count; + + //Copy pcap data received from Master OCC to G_sysConfigData + memcpy(&(G_sysConfigData.pcap),&(G_dcom_slv_inbox_doorbell_rx.pcap), + sizeof(pcap_config_data_t)); + + //Affects ITE ONLY: Check if it's ok (1) to exit the oversubscribed state + if(1 == G_sysConfigData.pcap.unthrottle) + { + //Clear throttle flag + g_amec->oversub_status.cmeThrottleLatchAmec = 0; + } + + //Check node power cap requested by customer/system. + // 0 means there is no pcap for that parameter. + if(0 == G_sysConfigData.pcap.current_pcap) + { + l_customer = 0xFFFF; + } + else + { + l_customer = G_sysConfigData.pcap.current_pcap; + } + + //Check fixed node power cap required by the system. + // 0 means there is no pcap for that parameter. + if(0 == G_sysConfigData.pcap.system_pcap) + { + l_system = 0xFFFF; + } + else + { + l_system = G_sysConfigData.pcap.system_pcap; + } + + //Set the normal node pcap to the least of both system and customer pcap. + if(l_customer < l_system) + { + g_amec->pcap.norm_node_pcap = l_customer; + } + else + { + g_amec->pcap.norm_node_pcap = l_system; + } + + /////////////////////////////// + //Set the oversubscription pcap + if(0 != G_sysConfigData.pcap.oversub_pcap) + { + g_amec->pcap.ovs_node_pcap = G_sysConfigData.pcap.oversub_pcap; + } + else + { + g_amec->pcap.ovs_node_pcap = G_sysConfigData.pcap.hard_min_pcap; + } + + //Oversubscription pcap can NOT be higher than a customer set pcap. + if(g_amec->pcap.ovs_node_pcap > l_customer) + { + g_amec->pcap.ovs_node_pcap = l_customer; + } + + // update data mask notifying we got pcap information + extern data_cnfg_t * G_data_cnfg; + G_data_cnfg->data_mask |= DATA_MASK_PCAP_PRESENT; + TRAC_IMP("amec_data_write: PCAP Config data: pcap[%d]: data_mask[%x]", g_amec->pcap.norm_node_pcap, G_data_cnfg->data_mask); + } +} + +/*----------------------------------------------------------------------------*/ +/* End */ +/*----------------------------------------------------------------------------*/ diff --git a/src/occ_405/amec/amec_data.h b/src/occ_405/amec/amec_data.h new file mode 100755 index 0000000..ee7fccf --- /dev/null +++ b/src/occ_405/amec/amec_data.h @@ -0,0 +1,82 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ_405/amec/amec_data.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#ifndef _AMEC_DATA_H +#define _AMEC_DATA_H + +//************************************************************************* +// Includes +//************************************************************************* +#include <occ_common.h> +#include <ssx.h> +#include <ssx_app_cfg.h> +#include <amec_smh.h> +#include <errl.h> +#include <mode.h> + +//************************************************************************* +// Externs +//************************************************************************* + +//************************************************************************* +// Macros +//************************************************************************* + +//************************************************************************* +// Defines/Enums +//************************************************************************* + +//************************************************************************* +// Structures +//************************************************************************* + +//************************************************************************* +// Globals +//************************************************************************* + +//************************************************************************* +// Function Prototypes +//************************************************************************* + +//************************************************************************* +// Functions +//************************************************************************* + +// This is used to change the current Freq data AMEC is using +errlHndl_t AMEC_data_write_fcurr(const OCC_MODE i_mode); + +// This is used to store the thermal thresholds AMEC is using +errlHndl_t AMEC_data_write_thrm_thresholds(const OCC_MODE i_mode); + +// This is used to store the IPS config data AMEC is using +errlHndl_t AMEC_data_write_ips_cnfg(void); + +// This is used to notify AMEC that there is a change to the configuration data +errlHndl_t AMEC_data_change(const uint32_t i_data_mask); + +// Writes pcap data sent by master to slave accessable structure. +void amec_data_write_pcap(); + +#endif diff --git a/src/occ_405/amec/amec_dps.c b/src/occ_405/amec/amec_dps.c new file mode 100755 index 0000000..ac6aff7 --- /dev/null +++ b/src/occ_405/amec/amec_dps.c @@ -0,0 +1,365 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ_405/amec/amec_dps.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/*----------------------------------------------------------------------------*/ +/* Includes */ +/*----------------------------------------------------------------------------*/ +#include <common_types.h> +#include <occ_sys_config.h> +#include <proc_data.h> +#include <amec_sys.h> +#include <amec_perfcount.h> +#include <amec_part.h> +#include <amec_dps.h> +#include <sensor.h> +#include <amec_controller.h> + +/*----------------------------------------------------------------------------*/ +/* Constants */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Globals */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Typedef / Enum */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Internal Function Prototypes */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Code */ +/*----------------------------------------------------------------------------*/ + +// Function Specification +// +// Name: amec_dps_update_core_util +// +// Description: Update per-core utilization variables. +// +// End Function Specification +void amec_dps_update_core_util(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + uint16_t l_temp16 = 0; + uint8_t l_tempreg = 0; + uint8_t l_idx = 0; + amec_core_perf_counter_t* l_perf = NULL; + amec_part_t *l_part = NULL; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + // If g_amec->fw.dps_no_update_flag=1, no updating is allowed + if (!g_amec->fw.dps_no_update_flag) + { + // Update moving average of util_slack and util_active for all cores + for(l_idx=0; l_idx<MAX_NUM_CORES; l_idx++) + { + if (!CORE_PRESENT(l_idx)) + { + continue; //nothing to do if the core's disabled + } + + l_part = amec_part_find_by_core(&g_amec->part_config, l_idx); + + // If this core doesn't belong to a core group, then do nothing + if (l_part == NULL) + { + continue; + } + + // Get pointer to the perf struc of this core + l_perf = &g_amec->proc[0].core[l_idx].core_perf; + + l_perf->ptr_putUtilslack--; // Decrement put pointer + if (l_perf->ptr_putUtilslack > TWO_TO_THE_POWER_OF_FIFTEEN) + { + // Wrap circular pointer. WARNING -> buffer size must be a + // power of 2 + l_perf->ptr_putUtilslack &= (MAX_UTIL_SLACK_AVG_LEN-1); + } + + // Locate oldest sample associated with 32-bit moving average + // WARNING -> we need to read this sample first in case entire + // buffer is used which would result in the write overwriting the + // oldest sample we need to read. + l_temp16=(uint16_t)(l_perf->ptr_putUtilslack+l_part->dpsalg.sample_count_util) & (MAX_UTIL_SLACK_AVG_LEN-1); + l_tempreg=l_perf->ptr_util_slack_avg_buffer[l_temp16]; + // Bleed off oldest sample from moving average + l_perf->util_slack_accumulator = l_perf->util_slack_accumulator-(uint32_t)l_tempreg; + l_tempreg=l_perf->ptr_util_active_avg_buffer[l_temp16]; + // Bleed off oldest sample from moving average + l_perf->util_active_accumulator = l_perf->util_active_accumulator-(uint32_t)l_tempreg; + // Add in newest sample into moving average + l_tempreg = l_perf->util_slack_core_counter; + l_perf->util_slack_accumulator = l_perf->util_slack_accumulator+(uint32_t)l_tempreg; + // Write new sample into buffer. + l_perf->ptr_util_slack_avg_buffer[l_perf->ptr_putUtilslack]=l_tempreg; + // Add in newest sample into moving average + l_tempreg = l_perf->util_active_core_counter; + l_perf->util_active_accumulator = l_perf->util_active_accumulator+(uint32_t)l_tempreg; + // Write new sample into buffer. + l_perf->ptr_util_active_avg_buffer[l_perf->ptr_putUtilslack]=l_tempreg; + + // Reset counters every 2msec + l_perf->util_active_core_counter=0; + l_perf->util_slack_core_counter=0; + } + } +} + +// Function Specification +// +// Name: amec_dps_partition_update_sensors +// +// Description: Update utilization sensors for a core group. +// +// End Function Specification +void amec_dps_partition_update_sensors(const uint16_t i_part_id) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + amec_core_perf_counter_t* l_perf = NULL; + uint16_t l_core_index = 0; + uint8_t l_idx = 0; + uint32_t l_cg_active_accumulator = 0; + uint32_t l_cg_slack_accumulator = 0; + uint16_t l_cg_util_slack_perc = 0; + uint32_t l_divide32[2] = {0, 0}; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + for (l_idx=0; l_idx<g_amec->part_config.part_list[i_part_id].ncores; l_idx++) + { + l_core_index = g_amec->part_config.part_list[i_part_id].core_list[l_idx]; + l_perf = &g_amec->proc[0].core[l_core_index % MAX_NUM_CORES].core_perf; + + // Sum accumulators for core group + l_cg_active_accumulator += l_perf->util_active_accumulator; + l_cg_slack_accumulator += l_perf->util_slack_accumulator; + } + + if (l_cg_active_accumulator == 0) + { + // Check for divide by zero + l_cg_util_slack_perc=16384; // if no active cores, set slack=100% + } + else + { + l_divide32[1]=(uint32_t)l_cg_active_accumulator; + l_divide32[0]=(uint32_t)l_cg_slack_accumulator<<14; // *16384 because 16384=1.0 + l_divide32[0] /= l_divide32[1]; + l_cg_util_slack_perc=(uint16_t)(l_divide32[0]); + } + + // Update the sensor of the utilization slack + sensor_update(&g_amec->part_config.part_list[i_part_id].util2msslack, l_cg_util_slack_perc); +} + +// Function Specification +// +// Name: amec_dps_partition_alg +// +// Description: DPS algorithm function. +// +// End Function Specification +void amec_dps_partition_alg(const uint16_t i_part_id) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + amec_core_perf_counter_t* l_perf = NULL; + uint16_t l_freq = 0; + uint16_t l_core_index = 0; + uint16_t l_idx = 0; + uint16_t l_tempreg = 0; + uint16_t l_temp16 = 0; + uint32_t l_divide32[2] = {0, 0}; + OCC_INTERNAL_MODE l_part_policy = 0xFF; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + // Switch on dps_type of the core group + switch (g_amec->part_config.part_list[i_part_id].dpsalg.type) + { + case 0x00: + // Type 0 algorithm: no dynamic power savings enabled + for (l_idx=0; l_idx<g_amec->part_config.part_list[i_part_id].ncores; l_idx++) + { + l_core_index = g_amec->part_config.part_list[i_part_id].core_list[l_idx]; + l_perf = &g_amec->proc[0].core[l_core_index % MAX_NUM_CORES].core_perf; + l_perf->dps_freq_request = UINT16_MAX; + } + break; + + case 41: + // Type 41 algorithm + // l_tempreg=measure of slack over gamma time interval on a per core basis + l_tempreg = (uint16_t)g_amec->part_config.part_list[i_part_id].util2msslack.sample; + // Convert to 10000 is equivalent to 100.00% utilization. + l_divide32[1]=(uint32_t)53687; + l_divide32[0]=(uint32_t)l_tempreg<<15; + l_divide32[0] /= l_divide32[1]; + // l_tempreg is level of utilization using per core info + l_tempreg=10000-(uint16_t)(l_divide32[0]); + + l_temp16 = g_amec->part_config.part_list[i_part_id].dpsalg.util_speed_request; + if (l_tempreg > g_amec->part_config.part_list[i_part_id].dpsalg.alpha_up) + { + l_temp16 = g_amec->part_config.part_list[i_part_id].dpsalg.util_speed_request + + g_amec->part_config.part_list[i_part_id].dpsalg.step_up; + } + else + { + if (l_tempreg < g_amec->part_config.part_list[i_part_id].dpsalg.alpha_down) + { + l_temp16 = g_amec->part_config.part_list[i_part_id].dpsalg.util_speed_request - + g_amec->part_config.part_list[i_part_id].dpsalg.step_down; + } + } + + // Now limit final speed in l_temp16 to be between allowed speeds. + if (l_temp16 > g_amec->sys.max_speed) + { + l_temp16 = g_amec->sys.max_speed; + } + // Use the lower boundary based on the power policy of the core group + l_part_policy = g_amec->part_config.part_list[i_part_id].es_policy; + if (l_temp16 < g_amec->part_mode_freq[l_part_policy].min_speed) + { + l_temp16 = g_amec->part_mode_freq[l_part_policy].min_speed; + } + + // Generate vote for utilization + g_amec->part_config.part_list[i_part_id].dpsalg.util_speed_request = l_temp16; + l_freq = amec_controller_speed2freq(l_temp16, g_amec->part_mode_freq[l_part_policy].fmax); + g_amec->part_config.part_list[i_part_id].dpsalg.freq_request = l_freq; + + for (l_idx=0; l_idx<g_amec->part_config.part_list[i_part_id].ncores; l_idx++) + { + l_core_index = g_amec->part_config.part_list[i_part_id].core_list[l_idx]; + l_perf = &g_amec->proc[0].core[l_core_index % MAX_NUM_CORES].core_perf; + l_perf->dps_freq_request = l_freq; + } + // End algorithm type 41 + break; + + default: + break; + } +} + +// Function Specification +// +// Name: amec_dps_main +// +// Description: Main DPS function. +// +// End Function Specification +void amec_dps_main(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + uint16_t l_idx = 0; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + // First, update the utilization variables for all cores + amec_dps_update_core_util(); + + // Loop through all core groups and apply energy-savings policy + for (l_idx=0; l_idx<AMEC_PART_MAX_PART; l_idx++) + { + if (!g_amec->part_config.part_list[l_idx].valid) + { + continue; + } + + if (g_amec->part_config.part_list[l_idx].ncores == 0) + { + continue; + } + + switch (g_amec->part_config.part_list[l_idx].es_policy) + { + case OCC_INTERNAL_MODE_DPS: + case OCC_INTERNAL_MODE_DPS_MP: + amec_dps_partition_update_sensors(l_idx); + amec_dps_partition_alg(l_idx); + break; + + default: + // No energy-savings policy: DPS vote is already disabled when + // policy first selected for partition or core ownership + // changes (amec_part.c) + break; + } + } + + // For GA1, we need to send the Fwish to the Master OCC + if ((g_amec->part_config.part_list[0].es_policy == OCC_INTERNAL_MODE_DPS) || + (g_amec->part_config.part_list[0].es_policy == OCC_INTERNAL_MODE_DPS_MP)) + { + // If this core group policy is one of the DPS modes, then send the + // frequency request from the DPS algorithm + G_dcom_slv_outbox_tx.fwish = + g_amec->part_config.part_list[0].dpsalg.freq_request; + } + else + { + // Else, send the nominal frequency of the system + G_dcom_slv_outbox_tx.fwish = + g_amec->part_mode_freq[OCC_INTERNAL_MODE_NOM].fmax; + } + // We also need to send the Factual to the Master OCC + for (l_idx=0; l_idx<MAX_NUM_CORES; l_idx++) + { + // Find the first valid core and send its frequency + if (CORE_PRESENT(l_idx)) + { + G_dcom_slv_outbox_tx.factual = + AMECSENSOR_ARRAY_PTR(FREQ250USP0C0,l_idx)->sample; + break; + } + } +} + +/*----------------------------------------------------------------------------*/ +/* End */ +/*----------------------------------------------------------------------------*/ diff --git a/src/occ_405/amec/amec_dps.h b/src/occ_405/amec/amec_dps.h new file mode 100755 index 0000000..c342139 --- /dev/null +++ b/src/occ_405/amec/amec_dps.h @@ -0,0 +1,114 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ_405/amec/amec_dps.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#ifndef _AMEC_DPS_H +#define _AMEC_DPS_H + +/*----------------------------------------------------------------------------*/ +/* Includes */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Constants */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Globals */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Defines */ +/*----------------------------------------------------------------------------*/ +#define TWO_TO_THE_POWER_OF_FIFTEEN 32768 + +/*----------------------------------------------------------------------------*/ +/* Typedef / Enum */ +/*----------------------------------------------------------------------------*/ +///DPS Algorithm Model +typedef struct amec_dps +{ + ///Frequency request for core voting box + uint16_t freq_request; + ///Utilization speed request + uint16_t util_speed_request; + ///Utilization threshold for moving down in frequency (low side) + uint16_t tlutil; + ///Step size for going up in speed + uint16_t step_up; + ///Step size for going down in speed + uint16_t step_down; + ///Number of utilization samples in sliding window + uint16_t sample_count_util; + ///Epsilon used for determining if a core is active (units of 0.01%) + uint16_t epsilon_perc; + ///Threshold for going up in frequency + uint16_t alpha_up; + ///Threshold for going down in frequency + uint16_t alpha_down; + ///8-bit mask for dynamic power save type (=0:none active) + uint8_t type; +}amec_dps_t; + +/*----------------------------------------------------------------------------*/ +/* Function Prototypes */ +/*----------------------------------------------------------------------------*/ + +/** + * Update per-core utilization variables. + * + * This function updates all the per-core utilization variables. These + * variables are used to populate the slack sensors. + * + */ +void amec_dps_update_core_util(void); + +/** + * Update utilization sensors for a core group. + * + * This function updates the utilization (slack) sensors for a + * given core group. + * + */ +void amec_dps_partition_update_sensors(const uint16_t i_part_id); + +/** + * DPS algorithm function. + * + * This function implements the different DPS algorithms for a + * given core group. + * + */ +void amec_dps_partition_alg(const uint16_t i_part_id); + +/** + * Main DPS function. + * + * This function is the entry-point for running DPS algorithms + * and is aware of the different core groups defined. + * + */ +void amec_dps_main(void); + +#endif /* #ifndef _AMEC_DPS_H */ diff --git a/src/occ_405/amec/amec_external.h b/src/occ_405/amec/amec_external.h new file mode 100755 index 0000000..17fcd57 --- /dev/null +++ b/src/occ_405/amec/amec_external.h @@ -0,0 +1,69 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ_405/amec/amec_external.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#ifndef _AMEC_EXTERNAL_H +#define _AMEC_EXTERNAL_H + +//************************************************************************* +// Includes +//************************************************************************* +#include <occ_common.h> +#include <ssx.h> +#include <ssx_app_cfg.h> + +//************************************************************************* +// Externs +//************************************************************************* + +//************************************************************************* +// Macros +//************************************************************************* + +//************************************************************************* +// Defines/Enums +//************************************************************************* + +//************************************************************************* +// Structures +//************************************************************************* + +//************************************************************************* +// Globals +//************************************************************************* + +//************************************************************************* +// Function Prototypes +//************************************************************************* +// task for amec master code +void task_amec_master( task_t *i_self); + +// task for amec slave code +void task_amec_slave( task_t *i_self); + +//************************************************************************* +// Functions +//************************************************************************* + +#endif //_AMEC_EXTERNAL_H diff --git a/src/occ_405/amec/amec_freq.c b/src/occ_405/amec/amec_freq.c new file mode 100755 index 0000000..953bf27 --- /dev/null +++ b/src/occ_405/amec/amec_freq.c @@ -0,0 +1,1069 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/amec/amec_freq.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +//************************************************************************* +// Includes +//************************************************************************* +#include <occ_common.h> +#include <ssx.h> +#include <errl.h> +#include "sensor.h" +#include "rtls.h" +#include "occ_sys_config.h" +#include "occ_service_codes.h" // for SSX_GENERIC_FAILURE +#include "dcom.h" +#include "proc_data.h" +#include "proc_data_control.h" +#include "amec_smh.h" +#include "amec_slave_smh.h" +#include <trac.h> +#include "amec_sys.h" +#include "sensor_enum.h" +#include "amec_service_codes.h" +#include <amec_sensors_core.h> +#include <amec_sensors_power.h> +#include <amec_sensors_fw.h> +#include <amec_data.h> +#include <amec_freq.h> + +//************************************************************************* +// Externs +//************************************************************************* +extern uint8_t G_cent_temp_expired_bitmap; +extern uint8_t G_dimm_temp_expired_bitmap; + +//************************************************************************* +// Defines/Enums +//************************************************************************* + +//************************************************************************* +// Structures +//************************************************************************* + +//************************************************************************* +// Globals +//************************************************************************* +BOOLEAN G_non_dps_power_limited = FALSE; +uint8_t G_amec_kvm_throt_reason = NO_THROTTLE; +uint16_t G_time_until_freq_check = FREQ_CHG_CHECK_TIME; + +//FFDC SCOM addresses as requested by Greg Still in defect SW247927 +//If new SCOM addresses are added, update the size of the array. +const uint32_t G_pmc_ffdc_scom_addrs[PMC_FFDC_SCOM_ADDRS_SIZE] = +{ + PMC_LFIR_ERR_REG, + PMC_LFIR_ERR_MASK_REG, + OCB_OCCLFIR, + PBA_FIR, + TOD_VALUE_REG +}; + +//FFDC OCI addresses as requested by Greg Still in defect SW247927 +//If new OCI addresses are added, update the size of the array. +const uint32_t G_pmc_ffdc_oci_addrs[PMC_FFDC_OCI_ADDRS_SIZE] = +{ + PMC_MODE_REG, + PMC_PSTATE_MONITOR_AND_CTRL_REG, + PMC_RAIL_BOUNDS_REGISTER, + PMC_PARAMETER_REG0, + PMC_PARAMETER_REG1, + PMC_EFF_GLOBAL_ACTUAL_VOLTAGE_REG, + PMC_STATUS_REG, + PMC_INTCHP_CTRL_REG1, + PMC_INTCHP_CTRL_REG4, + PMC_INTCHP_STATUS_REG, + PMC_INTCHP_COMMAND_REG, + PMC_INTCHP_PSTATE_REG, + PMC_SPIV_CTRL_REG0A, + PMC_SPIV_CTRL_REG0B, + PMC_SPIV_CTRL_REG1, + PMC_SPIV_CTRL_REG2, + PMC_SPIV_CTRL_REG3, + PMC_SPIV_CTRL_REG4, + PMC_SPIV_STATUS_REG, + PMC_SPIV_COMMAND_REG, + PMC_O2S_CTRL_REG0A, + PMC_O2S_CTRL_REG0B, + PMC_O2S_CTRL_REG1, + PMC_O2S_CTRL_REG2, + PMC_O2S_CTRL_REG4, + PMC_O2S_STATUS_REG, + PMC_O2S_COMMAND_REG, + PMC_O2S_WDATA_REG, + PMC_CORE_DECONFIGURATION_REG, + PMC_FSMSTATE_STATUS_REG, + PMC_GPSA_ACK_COLLECTION_REG, + PMC_GPSA_ACK_COLLECTION_MASK_REG, + PMC_OCC_HEARTBEAT_REG, + 0 //0 marks last OCI address +}; + +//************************************************************************* +// Function Prototypes +//************************************************************************* + +//************************************************************************* +// Functions +//************************************************************************* + + +// Function Specification +// +// Name: amec_set_freq_range +// +// Description: Set the frequency range for AMEC +// This function will run on mode changes and cnfg_data changes +// +// Thread: RealTime Loop +// +// Task Flags: +// +// End Function Specification +errlHndl_t amec_set_freq_range(const OCC_MODE i_mode) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + errlHndl_t l_err = NULL; + uint16_t l_freq_min = 0; + uint16_t l_freq_max = 0; + uint32_t l_temp = 0; + amec_mode_freq_t l_ppm_freq[OCC_INTERNAL_MODE_MAX_NUM] = {{0}}; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + // First set to Max Freq Range for this mode + if( VALID_MODE(i_mode) ) + { + l_freq_min = G_sysConfigData.sys_mode_freq.table[OCC_MODE_MIN_FREQUENCY]; + l_freq_max = G_sysConfigData.sys_mode_freq.table[i_mode]; + } + + // If SMS is set then TMGT wants us to pin to frequency which + // corresponds to input mode. They will use this function + // when powering off and they wish to have us bring the system + // back up to real nominal frequency (without being impacted + // by power caps or thermal actuations) + if(CURRENT_SMS() == SMGR_SMS_STATIC_VF_CHANGE_REQ) + { + l_freq_min = l_freq_max; + } + + g_amec->sys.fmin = l_freq_min; + g_amec->sys.fmax = l_freq_max; + + TRAC_INFO("amec_set_freq_range: Mode[0x%02x] Fmin[%u] Fmax[%u]", + i_mode, + l_freq_min, + l_freq_max); + + // Now determine the max frequency for the PPM structure + l_ppm_freq[OCC_INTERNAL_MODE_NOM].fmax = G_sysConfigData.sys_mode_freq.table[OCC_MODE_NOMINAL]; + l_ppm_freq[OCC_INTERNAL_MODE_DPS].fmax = G_sysConfigData.sys_mode_freq.table[OCC_MODE_DYN_POWER_SAVE]; + l_ppm_freq[OCC_INTERNAL_MODE_DPS_MP].fmax = G_sysConfigData.sys_mode_freq.table[OCC_MODE_DYN_POWER_SAVE_FP]; + + // Determine the min frequency for the PPM structure. This Fmin should + // always be set to the system Fmin + l_ppm_freq[OCC_INTERNAL_MODE_NOM].fmin = G_sysConfigData.sys_mode_freq.table[OCC_MODE_MIN_FREQUENCY]; + l_ppm_freq[OCC_INTERNAL_MODE_DPS].fmin = G_sysConfigData.sys_mode_freq.table[OCC_MODE_MIN_FREQUENCY]; + l_ppm_freq[OCC_INTERNAL_MODE_DPS_MP].fmin = G_sysConfigData.sys_mode_freq.table[OCC_MODE_MIN_FREQUENCY]; + + // Determine the min speed allowed for DPS power policies (this is needed + // by the DPS algorithms) + l_temp = (l_ppm_freq[OCC_INTERNAL_MODE_DPS].fmin * 1000)/l_ppm_freq[OCC_INTERNAL_MODE_DPS].fmax; + l_ppm_freq[OCC_INTERNAL_MODE_DPS].min_speed = l_temp; + + l_temp = (l_ppm_freq[OCC_INTERNAL_MODE_DPS_MP].fmin * 1000)/l_ppm_freq[OCC_INTERNAL_MODE_DPS_MP].fmax; + l_ppm_freq[OCC_INTERNAL_MODE_DPS_MP].min_speed = l_temp; + + // Copy the PPM frequency information into g_amec + memcpy(g_amec->part_mode_freq, l_ppm_freq, sizeof(l_ppm_freq)); + + TRAC_INFO("amec_set_freq_range: PPM Fmin[%u] Fnom[%u] Fmax[%u] min_speed[%u]", + l_ppm_freq[OCC_INTERNAL_MODE_NOM].fmin, + l_ppm_freq[OCC_INTERNAL_MODE_NOM].fmax, + l_ppm_freq[OCC_INTERNAL_MODE_DPS].fmax, + l_ppm_freq[OCC_INTERNAL_MODE_DPS_MP].min_speed); + + return l_err; +} + +// Function Specification +// +// Name: amec_slv_voting_box +// +// Description: Slave OCC's voting box that decides the frequency request. +// This function will run every tick. +// +// Thread: RealTime Loop +// +// Task Flags: +// +// End Function Specification +void amec_slv_voting_box(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + uint16_t k = 0; + uint16_t l_chip_fmax = g_amec->sys.fmax; + uint16_t l_core_freq = 0; + uint32_t l_chip_reason = 0; + uint32_t l_core_reason = 0; + uint8_t l_kvm_throt_reason = NO_THROTTLE; + amec_part_t *l_part = NULL; + bool l_freq_req_changed = FALSE; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + // Voting Box for CPU speed. + // This function implements the voting box to decide which input gets the right + // to actuate the system. + + //Reset the maximum core frequency requested prior to recalculation. + g_amec->proc[0].core_max_freq = 0; + + // PPB_FMAX + if(g_amec->proc[0].pwr_votes.ppb_fmax < l_chip_fmax) + { + l_chip_fmax = g_amec->proc[0].pwr_votes.ppb_fmax; + l_chip_reason = AMEC_VOTING_REASON_PPB; + l_kvm_throt_reason = POWERCAP; + } + + // PMAX_CLIP_FREQ + if(g_amec->proc[0].pwr_votes.pmax_clip_freq < l_chip_fmax) + { + l_chip_fmax = g_amec->proc[0].pwr_votes.pmax_clip_freq; + l_chip_reason = AMEC_VOTING_REASON_PMAX; + l_kvm_throt_reason = POWER_SUPPLY_FAILURE; + } + + // Pmax_clip frequency request if there is an APSS failure + if(g_amec->proc[0].pwr_votes.apss_pmax_clip_freq < l_chip_fmax) + { + l_chip_fmax = g_amec->proc[0].pwr_votes.apss_pmax_clip_freq; + l_chip_reason = AMEC_VOTING_REASON_APSS_PMAX; + l_kvm_throt_reason = POWER_SUPPLY_FAILURE; + } + + //THERMALPROC.FREQ_REQUEST + //Thermal controller input based on processor temperature + if(g_amec->thermalproc.freq_request < l_chip_fmax) + { + l_chip_fmax = g_amec->thermalproc.freq_request; + l_chip_reason = AMEC_VOTING_REASON_PROC_THRM; + l_kvm_throt_reason = CPU_OVERTEMP; + } + + // Controller request based on VRHOT signal from processor regulator + if(g_amec->vrhotproc.freq_request < l_chip_fmax) + { + l_chip_fmax = g_amec->vrhotproc.freq_request; + l_chip_reason = AMEC_VOTING_REASON_VRHOT_THRM; + l_kvm_throt_reason = CPU_OVERTEMP; + } + + // CONN_OC_VOTE + if(g_amec->proc[0].pwr_votes.conn_oc_vote < l_chip_fmax) + { + l_chip_fmax = g_amec->proc[0].pwr_votes.conn_oc_vote; + l_chip_reason = AMEC_VOTING_REASON_CONN_OC; + l_kvm_throt_reason = OVERCURRENT; + } + + for (k=0; k<MAX_NUM_CORES; k++) + { + if(CORE_PRESENT(k)) + { + l_core_freq = l_chip_fmax; + l_core_reason = l_chip_reason; + + // Disable DPS in KVM + if(!G_sysConfigData.system_type.kvm) + { + l_part = amec_part_find_by_core(&g_amec->part_config, k); + + // Check frequency request generated by DPS algorithms + if(g_amec->proc[0].core[k].core_perf.dps_freq_request < l_core_freq) + { + l_core_freq = g_amec->proc[0].core[k].core_perf.dps_freq_request; + l_core_reason = AMEC_VOTING_REASON_UTIL; + } + + // Adjust frequency based on soft frequency boundaries + if(l_part != NULL) + { + if(l_core_freq < l_part->soft_fmin) + { + // Before enforcing a soft Fmin, make sure we don't + // have a thermal or power emergency + if(!(l_chip_reason & (AMEC_VOTING_REASON_PROC_THRM | + AMEC_VOTING_REASON_VRHOT_THRM | + AMEC_VOTING_REASON_PPB | + AMEC_VOTING_REASON_PMAX | + AMEC_VOTING_REASON_CONN_OC))) + { + l_core_freq = l_part->soft_fmin; + l_core_reason = AMEC_VOTING_REASON_SOFT_MIN; + } + } + else if(l_core_freq > l_part->soft_fmax) + { + l_core_freq = l_part->soft_fmax; + l_core_reason = AMEC_VOTING_REASON_SOFT_MAX; + } + } + } + + if(CURRENT_MODE() == OCC_MODE_NOMINAL) + { + // PROC_PCAP_NOM_VOTE + if(g_amec->proc[0].pwr_votes.proc_pcap_nom_vote < l_core_freq) + { + l_core_freq = g_amec->proc[0].pwr_votes.proc_pcap_nom_vote; + l_core_reason = AMEC_VOTING_REASON_PWR; + l_kvm_throt_reason = POWERCAP; + } + } + else + { + // PROC_PCAP_VOTE + if(g_amec->proc[0].pwr_votes.proc_pcap_vote < l_core_freq) + { + l_core_freq = g_amec->proc[0].pwr_votes.proc_pcap_vote; + l_core_reason = AMEC_VOTING_REASON_PWR; + l_kvm_throt_reason = POWERCAP; + } + } + + // Check IPS frequency request sent by Master OCC + if(g_amec->slv_ips_freq_request != 0) + { + if(g_amec->slv_ips_freq_request < l_core_freq) + { + l_core_freq = g_amec->slv_ips_freq_request; + l_core_reason = AMEC_VOTING_REASON_IPS; + } + } + + // Override frequency with request from Master OCC + if(g_amec->foverride_enable) + { + if(g_amec->foverride != 0) + { + // Override the frequency on all cores if Master OCC sends + // a non-zero request + l_core_freq = g_amec->foverride; + l_core_reason = AMEC_VOTING_REASON_OVERRIDE; + } + } + + if(g_amec->pstate_foverride_enable) + { + if(g_amec->pstate_foverride != 0) + { + // Override the frequency on all cores if the Global Pstate + // table has been modified + l_core_freq = g_amec->pstate_foverride; + l_core_reason = AMEC_VOTING_REASON_OVERRIDE; + } + } + + //Make sure the frequency is not less then the system min + if(l_core_freq < g_amec->sys.fmin) + { + l_core_freq = g_amec->sys.fmin; + } + + // Override frequency via Amester parameter interface + if (g_amec->proc[0].parm_f_override_enable && + g_amec->proc[0].parm_f_override[k] > 0) + { + l_core_freq = g_amec->proc[0].parm_f_override[k]; + l_core_reason = AMEC_VOTING_REASON_OVERRIDE_CORE; + } + + // If frequency has changed, set the flag + if ( (l_core_freq != g_amec->proc[0].core[k].f_request) || + (l_core_freq != g_amec->sys.fmax)) + { + l_freq_req_changed = TRUE; + } + + //STORE core frequency and reason + g_amec->proc[0].core[k].f_request = l_core_freq; + g_amec->proc[0].core[k].f_reason = l_core_reason; + + // Update the Amester parameter telling us the reason. Needed for + // parameter array. + g_amec->proc[0].parm_f_reason[k] = l_core_reason; + + //CURRENT_MODE() may be OCC_MODE_NOCHANGE because STATE change is processed + //before MODE change + if ((CURRENT_MODE() != OCC_MODE_DYN_POWER_SAVE) && + (CURRENT_MODE() != OCC_MODE_DYN_POWER_SAVE_FP) && + (CURRENT_MODE() != OCC_MODE_NOCHANGE) && + (l_core_reason & NON_DPS_POWER_LIMITED)) + { + G_non_dps_power_limited = TRUE; + } + else + { + G_non_dps_power_limited = FALSE; + } + + // Update the sensor telling us what the requested frequency is + sensor_update( AMECSENSOR_ARRAY_PTR(FREQ250USP0C0,k), + (uint16_t) g_amec->proc[0].core[k].f_request); + +#if 0 + /// TODO: This can be deleted if deemed useless + /// This trace that can be used to debug the voting + /// box an control loops. It will trace the reason why a + /// controller is lowering the freq, but will only do it once in a + /// row for the specific freq it wants to control to. It assumes + /// that all cores will be controlled to same freq. + if(l_chip_fmax != g_amec->sys.fmax){ + static uint16_t L_trace = 0; + if(l_chip_fmax != L_trace){ + L_trace = l_chip_fmax; + TRAC_INFO("Core: %d, Freq: %d, Reason: %d",k,l_core_freq,l_core_reason); + } + } +#endif + + if(l_core_freq > g_amec->proc[0].core_max_freq) + { + g_amec->proc[0].core_max_freq = l_core_freq; + } + } + else + { + l_core_freq = 0; + l_core_reason = 0; + } + }//End of for loop + + // Check if the frequency is going to be changing + if( l_freq_req_changed == TRUE ) + { + G_time_until_freq_check = FREQ_CHG_CHECK_TIME; + } + else if (G_time_until_freq_check != 0) + { + G_time_until_freq_check--; + } + + //convert POWERCAP reason to POWER_SUPPLY_FAILURE if ovs/failsafe is asserted + if((l_kvm_throt_reason == POWERCAP) && + (AMEC_INTF_GET_FAILSAFE() || AMEC_INTF_GET_OVERSUBSCRIPTION())) + { + l_kvm_throt_reason = POWER_SUPPLY_FAILURE; + } + + //check if we need to update the throttle reason in homer + if(G_sysConfigData.system_type.kvm && + (l_kvm_throt_reason != G_amec_kvm_throt_reason)) + { + //Notify dcom thread to update the table + G_amec_kvm_throt_reason = l_kvm_throt_reason; + ssx_semaphore_post(&G_dcomThreadWakeupSem); + } +} + +// Function Specification +// +// Name: amec_slv_freq_smh +// +// Description: Slave OCC's frequency state machine. +// This function will run every tick. +// +// Thread: RealTime Loop +// +// Task Flags: +// +// End Function Specification +void amec_slv_freq_smh(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + uint16_t k = 0; + int8_t l_pstate = 0; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + for (k=0; k<MAX_NUM_CORES; k++) + { + switch (g_amec->proc[0].core[k].f_sms) + { + case AMEC_CORE_FREQ_IDLE_STATE: + // Translate frequency request into a Pstate + l_pstate = proc_freq2pstate(g_amec->proc[0].core[k].f_request); + + // Fall through + case AMEC_CORE_FREQ_PROCESS_STATE: + if(G_sysConfigData.system_type.kvm) + { + // update core bounds on kvm systems + proc_set_core_bounds(gpst_pmin(&G_global_pstate_table) + 1, (Pstate) l_pstate, k); + } + else + { + // update core pstate request on non-kvm systems + proc_set_core_pstate((Pstate) l_pstate, k); + } + break; + } + } +} + +// Function Specification +// +// Name: amec_slv_freq_smh +// +// Description: Slave OCC's voting box that decides the memory speed request. +// This function will run every tick. +// +// Thread: RealTime Loop +// +// Task Flags: +// +// End Function Specification +void amec_slv_mem_voting_box(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + UINT16 l_vote; + UINT8 l_reason; + static INT16 l_slew_step = AMEC_MEMORY_STEP_SIZE; + static bool L_throttle_traced = FALSE; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + // Start with max allowed speed + l_vote = AMEC_MEMORY_MAX_STEP; + l_reason = AMEC_MEM_VOTING_REASON_INIT; + + // Check vote from Centaur thermal control loop + if (l_vote > g_amec->thermalcent.speed_request) + { + l_vote = g_amec->thermalcent.speed_request; + l_reason = AMEC_MEM_VOTING_REASON_CENT; + } + + // Check vote from DIMM thermal control loop + if (l_vote > g_amec->thermaldimm.speed_request) + { + l_vote = g_amec->thermaldimm.speed_request; + l_reason = AMEC_MEM_VOTING_REASON_DIMM; + } + + // Check if memory autoslewing is enabled + if (g_amec->mnfg_parms.mem_autoslew) + { + //check if we've reached the max setting and need to start going down + if(g_amec->mem_speed_request >= AMEC_MEMORY_MAX_STEP) + { + g_amec->mnfg_parms.mem_slew_counter++; + l_slew_step = -AMEC_MEMORY_STEP_SIZE; + } + + //check if we've reached the min setting and need to start going up + else if(g_amec->mem_speed_request <= AMEC_MEMORY_MIN_STEP) + { + g_amec->mnfg_parms.mem_slew_counter++; + l_slew_step = AMEC_MEMORY_STEP_SIZE; + } + + l_vote = g_amec->mem_speed_request + l_slew_step; + l_reason = AMEC_MEM_VOTING_REASON_SLEW; + } + + // Store final vote and vote reason in g_amec + g_amec->mem_throttle_reason = l_reason; + g_amec->mem_speed_request = l_vote; + + //trace changes in memory throttling + if(l_reason != AMEC_MEM_VOTING_REASON_INIT) + { + if(!L_throttle_traced) + { + L_throttle_traced = TRUE; + TRAC_INFO("Memory is being throttled. reason[%d] vote[%d] cent_expired[0x%02x] dimm_expired[0x%02x]", + l_reason, + l_vote, + G_cent_temp_expired_bitmap, + G_dimm_temp_expired_bitmap); + } + } + else + { + if(L_throttle_traced) + { + L_throttle_traced = FALSE; + TRAC_INFO("Memory is no longer being throttled"); + } + } + return; +} + +// Function Specification +// +// Name: amec_slv_check_perf +// +// Description: Slave OCC's Detect and log degraded performance errors +// This function will run every tick. +// +// Thread: RealTime Loop +// +// Task Flags: +// +// End Function Specification +void amec_slv_check_perf(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + static BOOLEAN l_prev_failsafe_state = FALSE; + static BOOLEAN l_prev_ovs_state = FALSE; + static BOOLEAN l_prev_pcap_state = FALSE; + static ERRL_SEVERITY l_pcap_sev = ERRL_SEV_PREDICTIVE; + static BOOLEAN l_throttle_traced = FALSE; + static uint64_t l_time = 0; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + // Verify that cores are at proper frequency + amec_verify_pstate(); + + do + { + // was frequency limited by power ? + if ( G_non_dps_power_limited != TRUE ) + { + if(l_throttle_traced) + { + TRAC_INFO("Frequency not limited by power algorithms anymore"); + l_throttle_traced = FALSE; + } + // we are done break and return + break; + } + + // frequency limited due to failsafe condition ? + if ( AMEC_INTF_GET_FAILSAFE() == TRUE ) + { + if ( l_prev_failsafe_state == TRUE) + { + // we are done break and return + break; + } + else + { + // log this error ONLY ONCE per IPL + l_prev_failsafe_state = TRUE; + + TRAC_ERR("Frequency limited due to failsafe condition(mode:%d, state:%d)", + CURRENT_MODE(), CURRENT_STATE()); + l_throttle_traced = TRUE; + l_time = ssx_timebase_get(); + + // log error that calls out OVS procedure + // set error severity to RRL_SEV_PREDICTIVE + + /* @ + * @errortype + * @moduleid AMEC_SLAVE_CHECK_PERFORMANCE + * @reasoncode INTERNAL_FAILURE + * @userdata1 Previous FailSafe State + * @userdata4 ERC_AMEC_SLAVE_FAILSAFE_STATE + * @devdesc Frequency limited due to failsafe condition + */ + errlHndl_t l_errl = createErrl(AMEC_SLAVE_CHECK_PERFORMANCE, //modId + INTERNAL_FAILURE, //reasoncode + ERC_AMEC_SLAVE_FAILSAFE_STATE,//Extended reason code + ERRL_SEV_PREDICTIVE, //Severity + NULL, //Trace Buf + DEFAULT_TRACE_SIZE, //Trace Size + l_prev_failsafe_state, //userdata1 + 0); //userdata2 + + addCalloutToErrl( l_errl, + ERRL_CALLOUT_TYPE_COMPONENT_ID, + ERRL_COMPONENT_ID_OVERSUBSCRIPTION, + ERRL_CALLOUT_PRIORITY_HIGH + ); + + // and sets the consolidate action flag + setErrlActions( l_errl, ERRL_ACTIONS_CONSOLIDATE_ERRORS ); + + // Commit Error + commitErrl(&l_errl); + + // we are done lets break + break; + } + } + + // frequency limited due to oversubscription condition ? + if ( AMEC_INTF_GET_OVERSUBSCRIPTION() == TRUE ) + { + if ( l_prev_ovs_state == TRUE) + { + // we are done break and return + break; + } + else + { + // log this error ONLY ONCE per IPL + l_prev_ovs_state = TRUE; + + TRAC_ERR("Frequency limited due to oversubscription condition(mode:%d, state:%d)", + CURRENT_MODE(), CURRENT_STATE()); + l_throttle_traced = TRUE; + l_time = ssx_timebase_get(); + + // log error that calls out OVS procedure + // set error severity to RRL_SEV_PREDICTIVE + + // Updated the RC to match the actual RC passed to createErrl() + /* @ + * @errortype + * @moduleid AMEC_SLAVE_CHECK_PERFORMANCE + * @reasoncode OVERSUB_LIMIT_ALERT + * @userdata1 Previous OVS State + * @userdata4 ERC_AMEC_SLAVE_OVS_STATE + * @devdesc Frequency limited due to oversubscription condition + */ + errlHndl_t l_errl = createErrl(AMEC_SLAVE_CHECK_PERFORMANCE, //modId + OVERSUB_LIMIT_ALERT, //reasoncode + ERC_AMEC_SLAVE_OVS_STATE, //Extended reason code + ERRL_SEV_PREDICTIVE, //Severity + NULL, //Trace Buf + DEFAULT_TRACE_SIZE, //Trace Size + l_prev_ovs_state, //userdata1 + 0); //userdata2 + + // Callout to Oversubscription + addCalloutToErrl( l_errl, + ERRL_CALLOUT_TYPE_COMPONENT_ID, + ERRL_COMPONENT_ID_OVERSUBSCRIPTION, + ERRL_CALLOUT_PRIORITY_HIGH + ); + + // Callout to APSS + addCalloutToErrl( l_errl, + ERRL_CALLOUT_TYPE_HUID, + G_sysConfigData.apss_huid, + ERRL_CALLOUT_PRIORITY_MED + ); + + // Callout to Firmware + addCalloutToErrl( l_errl, + ERRL_CALLOUT_TYPE_COMPONENT_ID, + ERRL_COMPONENT_ID_FIRMWARE, + ERRL_CALLOUT_PRIORITY_LOW + ); + + // and sets the consolidate action flag + setErrlActions( l_errl, ERRL_ACTIONS_CONSOLIDATE_ERRORS ); + + // Commit Error + commitErrl(&l_errl); + + // we are done lets break + break; + } + } + + uint16_t l_snrBulkPwr = AMECSENSOR_PTR(PWR250US)->sample; + + // frequency limited due to system power cap condition ? + if (( l_snrBulkPwr > (G_sysConfigData.pcap.system_pcap - PDROP_THRESH) ) + && + ( G_sysConfigData.pcap.current_pcap == 0 )) + { + if ( l_prev_pcap_state == TRUE) + { + // we are done break and return + break; + } + else + { + //log this error ONLY ONCE per IPL + l_prev_pcap_state = TRUE; + + TRAC_ERR("Frequency limited due to power cap condition(mode:%d, state:%d)", + CURRENT_MODE(), CURRENT_STATE()); + + TRAC_ERR("SnrBulkPwr %d > Sys Pcap %d ",l_snrBulkPwr, + G_sysConfigData.pcap.system_pcap ); + + TRAC_ERR("SnrFanPwr %d, SnrIOPwr %d, SnrStoragePwr %d, SnrGpuPrw %d ", + AMECSENSOR_PTR(PWR250USFAN)->sample, + AMECSENSOR_PTR(PWR250USIO)->sample, + AMECSENSOR_PTR(PWR250USSTORE)->sample, + AMECSENSOR_PTR(PWR250USGPU)->sample ); + + TRAC_ERR("SnrProcPwr 0 %d, SnrProcPwr 1 %d, SnrProcPwr 2 %d, SnrProcPwr 3 %d", + g_amec->proc_snr_pwr[0], + g_amec->proc_snr_pwr[1], + g_amec->proc_snr_pwr[2], + g_amec->proc_snr_pwr[3] ); + + TRAC_ERR("SnrMemPwr 0 %d, SnrMemPwr 1 %d, SnrMemPwr 2 %d, SnrMemPwr 3 %d", + g_amec->mem_snr_pwr[0], + g_amec->mem_snr_pwr[1], + g_amec->mem_snr_pwr[2], + g_amec->mem_snr_pwr[3] ); + + + l_throttle_traced = TRUE; + l_time = ssx_timebase_get(); + + // log error that calls out firmware and APSS procedure + // set error severity to l_pcap_sev + + /* @ + * @errortype + * @moduleid AMEC_SLAVE_CHECK_PERFORMANCE + * @reasoncode PCAP_THROTTLE_POWER_LIMIT + * @userdata1 Current Sensor Bulk Power + * @userdata2 System PCAP + * @userdata4 ERC_AMEC_SLAVE_POWERCAP + * @devdesc Frequency limited due to PowerCap condition + */ + errlHndl_t l_errl = createErrl(AMEC_SLAVE_CHECK_PERFORMANCE, //modId + PCAP_THROTTLE_POWER_LIMIT, //reasoncode + ERC_AMEC_SLAVE_POWERCAP, //Extended reason code + l_pcap_sev, //Severity + NULL, //Trace Buf + DEFAULT_TRACE_SIZE, //Trace Size + l_snrBulkPwr, //userdata1 + G_sysConfigData.pcap.system_pcap);//userdata2 + + addCalloutToErrl( l_errl, + ERRL_CALLOUT_TYPE_COMPONENT_ID, + ERRL_COMPONENT_ID_FIRMWARE, + ERRL_CALLOUT_PRIORITY_HIGH + ); + + addCalloutToErrl( l_errl, + ERRL_CALLOUT_TYPE_HUID, + G_sysConfigData.apss_huid, + ERRL_CALLOUT_PRIORITY_HIGH + ); + + // and sets the consolidate action flag + setErrlActions( l_errl, ERRL_ACTIONS_CONSOLIDATE_ERRORS ); + + // then l_pcap_sev to informational + l_pcap_sev = ERRL_SEV_INFORMATIONAL; + + // Commit Error + commitErrl(&l_errl); + + // we are done lets break + break; + } + } + + // trottle trace to every 3600 seconds (1hr = 3600000) + if(!l_throttle_traced && ( DURATION_IN_MS_UNTIL_NOW_FROM(l_time) > 3600000 ) ) + { + TRAC_INFO("Frequency power limited due to transient condition: PowerLimited=%x, FailSafe=%x, OverSubScription=%x CurrentBulkPwr=%x", + G_non_dps_power_limited, AMEC_INTF_GET_FAILSAFE(), AMEC_INTF_GET_OVERSUBSCRIPTION(), l_snrBulkPwr ); + l_throttle_traced = TRUE; + + l_time = ssx_timebase_get(); + } + } + while( 0 ); + + return; +} + +// Verifies that each core is at the correct frequency after they have had +// time to stabilize +void amec_verify_pstate() +{ + uint8_t l_core = 0; + int8_t l_pstate_from_fmax = 0; + gpe_bulk_core_data_t * l_core_data_ptr; + pmc_pmsr_ffcdc_data_t l_pmc_pmsr_ffdc; + errlHndl_t l_err = NULL; + + if ( (G_time_until_freq_check == 0) && + ( CURRENT_MODE() != OCC_MODE_DYN_POWER_SAVE ) && + ( CURRENT_MODE() != OCC_MODE_DYN_POWER_SAVE_FP ) && + (!G_sysConfigData.system_type.kvm)) + { + // Reset the counter + G_time_until_freq_check = FREQ_CHG_CHECK_TIME; + + // Convert fmax to the corresponding pstate + l_pstate_from_fmax = proc_freq2pstate(g_amec->sys.fmax); + + for( l_core = 0; l_core < MAX_NUM_CORES; l_core++ ) + { + // If the core isn't present, skip it + if(!CORE_PRESENT(l_core)) + { + l_pmc_pmsr_ffdc.pmsr_ffdc_data.data[l_core].value = 0; + continue; + } + + // Get pointer to core data + l_core_data_ptr = proc_get_bulk_core_data_ptr(l_core); + + // Get the core's pmsr data + l_pmc_pmsr_ffdc.pmsr_ffdc_data.data[l_core] = l_core_data_ptr->pcb_slave.pmsr; + + // Verify that the core is running at the correct frequency + // If not, log an error + if( (l_pstate_from_fmax != l_pmc_pmsr_ffdc.pmsr_ffdc_data.data[l_core].fields.local_pstate_actual) && + (l_pstate_from_fmax > l_pmc_pmsr_ffdc.pmsr_ffdc_data.data[l_core].fields.pv_min) && + (l_err == NULL) ) + { + TRAC_ERR("Frequency mismatch in core %d: actual_ps[%d] req_ps[%d] fmax[%d] mode[%d].", + l_core, + l_pmc_pmsr_ffdc.pmsr_ffdc_data.data[l_core].fields.local_pstate_actual, + l_pstate_from_fmax, + g_amec->sys.fmax, + CURRENT_MODE()); + + fill_pmc_ffdc_buffer(&l_pmc_pmsr_ffdc.pmc_ffcdc_data); + + /* @ + * @moduleid AMEC_VERIFY_FREQ_MID + * @reasonCode TARGET_FREQ_FAILURE + * @severity ERRL_SEV_PREDICTIVE + * @userdata1 0 + * @userdata2 0 + * @userdata4 OCC_NO_EXTENDED_RC + * @devdesc A core is not running at the expected frequency + */ + l_err = createErrl( AMEC_VERIFY_FREQ_MID, // i_modId, + TARGET_FREQ_FAILURE, // i_reasonCode, + OCC_NO_EXTENDED_RC, + ERRL_SEV_UNRECOVERABLE, + NULL, // i_trace, + DEFAULT_TRACE_SIZE, // i_traceSz, + 0, // i_userData1, + 0); // i_userData2 + + //Add firmware callout + addCalloutToErrl(l_err, + ERRL_CALLOUT_TYPE_COMPONENT_ID, + ERRL_COMPONENT_ID_FIRMWARE, + ERRL_CALLOUT_PRIORITY_HIGH); + + //Add processor callout + addCalloutToErrl(l_err, + ERRL_CALLOUT_TYPE_HUID, + G_sysConfigData.proc_huid, + ERRL_CALLOUT_PRIORITY_MED); + } + } + + if( l_err != NULL) + { + //Add our register dump to the error log + addUsrDtlsToErrl(l_err, + (uint8_t*) &l_pmc_pmsr_ffdc, + sizeof(l_pmc_pmsr_ffdc), + ERRL_USR_DTL_STRUCT_VERSION_1, + ERRL_USR_DTL_BINARY_DATA); + + REQUEST_RESET(l_err); + } + } +} + +// Fills in a pmc ffdc buffer with lots of PMC related OCI and SCOM registers +void fill_pmc_ffdc_buffer(pmc_ffdc_data_t* i_ffdc_ptr) +{ + int i; + uint32_t l_rc, l_addr, l_data32; + uint64_t l_data64; + + //clear out the entire buffer + memset(i_ffdc_ptr, 0, sizeof(pmc_ffdc_data_t)); + + //first get the OCI accessible FFDC data + for(i = 0; i < PMC_FFDC_OCI_ADDRS_SIZE; i++) + { + l_addr = G_pmc_ffdc_oci_addrs[i]; + if(l_addr) + { + l_data32 = in32(l_addr); + } + else + { + //leave an entry with all zero address and data for eye catcher + break; + } + + //store address along with data for easier parsing + i_ffdc_ptr->oci_regs[i].addr = l_addr; + i_ffdc_ptr->oci_regs[i].data = l_data32; + } + + //then get the SCOM accessible FFDC data + for(i = 0; i < PMC_FFDC_SCOM_ADDRS_SIZE; i++) + { + l_addr = G_pmc_ffdc_scom_addrs[i]; + l_rc = (uint32_t)_getscom(l_addr, &l_data64, SCOM_TIMEOUT); + if(l_rc) + { + //indicate there was a scom failure in collecting the data + l_data64 = 0xFEEDB0B000000000ull; + + //store rc in lower word + l_data64 |= l_rc; + } + + //store address along with data for easier parsing + i_ffdc_ptr->scom_regs[i].addr = l_addr; + i_ffdc_ptr->scom_regs[i].data = l_data64; + } +} + +/*----------------------------------------------------------------------------*/ +/* End */ +/*----------------------------------------------------------------------------*/ diff --git a/src/occ_405/amec/amec_freq.h b/src/occ_405/amec/amec_freq.h new file mode 100644 index 0000000..b05277d --- /dev/null +++ b/src/occ_405/amec/amec_freq.h @@ -0,0 +1,170 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ_405/amec/amec_freq.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#ifndef _AMEC_FREQ_H +#define _AMEC_FREQ_H + +//************************************************************************* +// Includes +//************************************************************************* +#include <occ_common.h> +#include <ssx.h> +#include "occ_sys_config.h" +#include <ssx_app_cfg.h> +#include <amec_smh.h> +#include <mode.h> +#include <errl.h> + +//************************************************************************* +// Externs +//************************************************************************* + +//************************************************************************* +// Macros +//************************************************************************* + +//************************************************************************* +// Defines/Enums +//************************************************************************* +#define FREQ_CHG_CHECK_TIME 4000 + +// This is used by the Frequency State Machine +typedef enum +{ + AMEC_CORE_FREQ_IDLE_STATE = 0x00, + AMEC_CORE_FREQ_PROCESS_STATE = 0x01, +}amec_freq_write_state_t; + +// This is reason code used by Voting box amec_slv_voting_box +typedef enum +{ + AMEC_VOTING_REASON_INIT = 0x00000000, + AMEC_VOTING_REASON_PHYP = 0x00000001, + AMEC_VOTING_REASON_PLPM = 0x00000002, + AMEC_VOTING_REASON_LEGACY = 0x00000004, + AMEC_VOTING_REASON_SOFT_MIN = 0x00000008, + AMEC_VOTING_REASON_SOFT_MAX = 0x00000010, + AMEC_VOTING_REASON_CORE_CAP = 0x00000020, + AMEC_VOTING_REASON_PROC_THRM = 0x00000040, + AMEC_VOTING_REASON_GXHB_THRM = 0x00000080, + AMEC_VOTING_REASON_VRHOT_THRM = 0x00000100, + AMEC_VOTING_REASON_OVER_CURRENT = 0x00000200, + AMEC_VOTING_REASON_OVERRIDE = 0x00000400, + AMEC_VOTING_REASON_CORE_GRP_MIN = 0x00000800, + AMEC_VOTING_REASON_PWR = 0x00001000, + AMEC_VOTING_REASON_PPB = 0x00002000, + AMEC_VOTING_REASON_PMAX = 0x00004000, + AMEC_VOTING_REASON_UTIL = 0x00008000, + AMEC_VOTING_REASON_CONN_OC = 0x00010000, + AMEC_VOTING_REASON_OVERRIDE_CORE = 0x00020000, + AMEC_VOTING_REASON_IPS = 0x00040000, + AMEC_VOTING_REASON_APSS_PMAX = 0x00080000, +}amec_freq_voting_reason_t; + + +#define NON_DPS_POWER_LIMITED ( AMEC_VOTING_REASON_PWR | \ + AMEC_VOTING_REASON_PPB | \ + AMEC_VOTING_REASON_PMAX \ + ) + +extern BOOLEAN G_non_dps_power_limited; + +// This is reason code used by Voting box amec_slv_mem_voting_box +typedef enum +{ + AMEC_MEM_VOTING_REASON_INIT = 0, + AMEC_MEM_VOTING_REASON_CENT = 1, + AMEC_MEM_VOTING_REASON_DIMM = 2, + AMEC_MEM_VOTING_REASON_SLEW = 3, +}amec_mem_voting_reason_t; + +//************************************************************************* +// Structures +//************************************************************************* + +#define PMC_FFDC_OCI_ADDRS_SIZE 34 +#define PMC_FFDC_SCOM_ADDRS_SIZE 5 + +// scom ffdc format +typedef struct __attribute__ ((packed)) +{ + uint32_t addr; + uint64_t data; +} pmc_ffdc_scom_entry_t; + +// OCI ffdc format +typedef struct +{ + uint32_t addr; + uint32_t data; +} pmc_ffdc_oci_entry_t; + +//PMC FFDC format for user detail section of error log +typedef struct +{ + pmc_ffdc_oci_entry_t oci_regs[PMC_FFDC_OCI_ADDRS_SIZE]; + pmc_ffdc_scom_entry_t scom_regs[PMC_FFDC_SCOM_ADDRS_SIZE]; +} pmc_ffdc_data_t; + +typedef struct +{ + pcbs_power_management_status_reg_t data[MAX_NUM_CORES]; +} pmsr_ffdc_data_t; + +typedef struct +{ + pmc_ffdc_data_t pmc_ffcdc_data; + pmsr_ffdc_data_t pmsr_ffdc_data; +} pmc_pmsr_ffcdc_data_t; + +//************************************************************************* +// Globals +//************************************************************************* + +//************************************************************************* +// Function Prototypes +//************************************************************************* +void fill_pmc_ffdc_buffer(pmc_ffdc_data_t* i_ffdc_ptr); + +// Used to set the freq range that amec can control between. +errlHndl_t amec_set_freq_range(const OCC_MODE i_mode); + +// Voting box for handling slave freq votes +void amec_slv_voting_box(void); + +// Amec Frequency State Machine +void amec_slv_freq_smh(void); + +// Voting box for handling slave memory freq votes +void amec_slv_mem_voting_box(void); + +// Amec Detect and log degraded performance errors +void amec_slv_check_perf(void); + +// Verifies that each core is at the correct frequency +void amec_verify_pstate(); + +#endif + diff --git a/src/occ_405/amec/amec_health.c b/src/occ_405/amec/amec_health.c new file mode 100755 index 0000000..4efad15 --- /dev/null +++ b/src/occ_405/amec/amec_health.c @@ -0,0 +1,1034 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ_405/amec/amec_health.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +//************************************************************************* +// Includes +//************************************************************************* +#include "amec_health.h" +#include "amec_sys.h" +#include "amec_service_codes.h" +#include "occ_service_codes.h" +#include <centaur_data.h> +#include <thrm_thread.h> +#include <proc_data.h> + +//************************************************************************* +// Externs +//************************************************************************* +extern thrm_fru_data_t G_thrm_fru_data[DATA_FRU_MAX]; + +//************************************************************************* +// Defines/Enums +//************************************************************************* + +//************************************************************************* +// Globals +//************************************************************************* + +// Have we already called out the dimm for overtemp (bitmap of dimms)? +cent_sensor_flags_t G_dimm_overtemp_logged_bitmap = {0}; + +// Have we already called out the dimm for timeout (bitmap of dimms)? +cent_sensor_flags_t G_dimm_timeout_logged_bitmap = {0}; + +// Are any dimms currently in the timedout state (bitmap of centaurs)? +// Note: this only tells you which centaur, not which dimm. +uint8_t G_dimm_temp_expired_bitmap = 0; + +// Have we already called out the centaur for timeout (bitmap of centaurs)? +uint8_t G_cent_timeout_logged_bitmap = 0; + +// Have we already called out the centaur for overtemp (bitmap of centaurs)? +uint8_t G_cent_overtemp_logged_bitmap = 0; + +// Are any dimms currently in the timedout state (bitmap of centaurs)? +uint8_t G_cent_temp_expired_bitmap = 0; + +// Array to store the update tag of each core's temperature sensor +uint32_t G_core_temp_update_tag[MAX_NUM_CORES] = {0}; + +//************************************************************************* +// Function Declarations +//************************************************************************* + +//************************************************************************* +// Functions +//************************************************************************* +uint64_t amec_mem_get_huid(uint8_t i_cent, uint8_t i_dimm) +{ + uint64_t l_huid; + + if(i_dimm == 0xff) + { + //we're being asked for a centaur huid + l_huid = G_sysConfigData.centaur_huids[i_cent]; + } + else + { + //we're being asked for a dimm huid + l_huid = G_sysConfigData.dimm_huids[i_cent][i_dimm]; + if(!l_huid) + { + //if we don't have a valid dimm huid, use the + //centaur huid. + //TODO: this will not work for ISDIMMS. + l_huid = G_sysConfigData.centaur_huids[i_cent]; + } + } + return l_huid; +} + +//If i_dimm is 0xff it is assumed that the caller wishes to +//mark the centaur as being logged. Otherwise, it is assumed +//that the dimm should be marked. +void amec_mem_mark_logged(uint8_t i_cent, + uint8_t i_dimm, + uint8_t* i_clog_bitmap, + uint8_t* i_dlog_bitmap) +{ + if(i_dimm == 0xff) + { + //mark the centaur as being called out. + *i_clog_bitmap |= CENTAUR0_PRESENT_MASK >> i_cent; + } + else + { + //mark the dimm as being called out. + *i_dlog_bitmap |= DIMM_SENSOR0 >> i_dimm; + } +} + +void amec_health_check_dimm_temp() +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + uint16_t l_ot_error, l_cur_temp, l_max_temp; + sensor_t *l_sensor; + uint32_t l_cent, l_dimm; + uint32_t l_callouts_count = 0; + uint8_t l_new_callouts; + uint64_t l_huid; + errlHndl_t l_err = NULL; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + // Check to see if any dimms have reached the error temperature that + // haven't been called out already + if(G_dimm_overtemp_bitmap.bigword == G_dimm_overtemp_logged_bitmap.bigword) + { + return; + } + + l_ot_error = g_amec->thermaldimm.ot_error; + l_sensor = getSensorByGsid(TEMP2MSDIMM); + l_cur_temp = l_sensor->sample; + l_max_temp = l_sensor->sample_max; + TRAC_ERR("amec_health_check_dimm_temp: DIMM reached error temp[%d]. cur_max[%d], hist_max[%d]", + l_ot_error, + l_cur_temp, + l_max_temp); + + //iterate over all centaurs + for(l_cent = 0; l_cent < MAX_NUM_CENTAURS; l_cent++) + { + //only callout a dimm if it hasn't been called out already + l_new_callouts = G_dimm_overtemp_bitmap.bytes[l_cent] ^ + G_dimm_overtemp_logged_bitmap.bytes[l_cent]; + + //skip to next centaur if no new callouts for this one + if(!l_new_callouts) + { + continue; + } + + //find the dimm(s) that need to be called out behind this centaur + for(l_dimm = 0; l_dimm < NUM_DIMMS_PER_CENTAUR; l_dimm++) + { + if(!(l_new_callouts & (DIMM_SENSOR0 >> l_dimm))) + { + continue; + } + + l_huid = amec_mem_get_huid(l_cent, l_dimm); + + amec_mem_mark_logged(l_cent, + l_dimm, + &G_cent_overtemp_logged_bitmap, + &G_dimm_overtemp_logged_bitmap.bytes[l_cent]); + + //If we don't have an error log for the callout, create one + if(!l_err) + { + /* @ + * @errortype + * @moduleid AMEC_HEALTH_CHECK_DIMM_TEMP + * @reasoncode DIMM_ERROR_TEMP + * @userdata1 Maximum dimm temperature + * @userdata2 Dimm temperature threshold + * @userdata4 OCC_NO_EXTENDED_RC + * @devdesc Memory DIMM(s) exceeded maximum safe + * temperature. + */ + l_err = createErrl(AMEC_HEALTH_CHECK_DIMM_TEMP, //modId + DIMM_ERROR_TEMP, //reasoncode + OCC_NO_EXTENDED_RC, //Extended reason code + ERRL_SEV_PREDICTIVE, //Severity + NULL, //Trace Buf + DEFAULT_TRACE_SIZE, //Trace Size + l_max_temp, //userdata1 + l_ot_error); //userdata2 + + // Callout the "over temperature" procedure + addCalloutToErrl(l_err, + ERRL_CALLOUT_TYPE_COMPONENT_ID, + ERRL_COMPONENT_ID_OVER_TEMPERATURE, + ERRL_CALLOUT_PRIORITY_HIGH); + l_callouts_count = 1; + } + + // Callout dimm + addCalloutToErrl(l_err, + ERRL_CALLOUT_TYPE_HUID, + l_huid, + ERRL_CALLOUT_PRIORITY_MED); + + l_callouts_count++; + + //If we've reached the max # of callouts for an error log + //commit the error log + if(l_callouts_count == ERRL_MAX_CALLOUTS) + { + commitErrl(&l_err); + } + + //If we found all of the callouts for this centaur, go to the next one + if(!l_new_callouts) + { + break; + } + }//iterate over dimms + }//iterate over centaurs + + if(l_err) + { + commitErrl(&l_err); + } +} + +void amec_health_check_cent_temp() +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + uint16_t l_ot_error, l_cur_temp, l_max_temp; + sensor_t *l_sensor; + uint32_t l_cent; + uint32_t l_callouts_count = 0; + uint8_t l_new_callouts; + uint64_t l_huid; + errlHndl_t l_err = NULL; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + // Check to see if any centaurs have reached the error temperature that + // haven't been called out already + l_new_callouts = G_cent_overtemp_bitmap ^ G_cent_overtemp_logged_bitmap; + if(!l_new_callouts) + { + return; + } + + l_ot_error = g_amec->thermalcent.ot_error; + l_sensor = getSensorByGsid(TEMP2MSCENT); + l_cur_temp = l_sensor->sample; + l_max_temp = l_sensor->sample_max; + TRAC_ERR("amec_health_check_cent_temp: Centaur reached error temp[%d]. cur_max[%d], hist_max[%d] bitmap[0x%02X]", + l_ot_error, + l_cur_temp, + l_max_temp, + l_new_callouts); + + //find the centaur(s) that need to be called out + for(l_cent = 0; l_cent < MAX_NUM_CENTAURS; l_cent++) + { + if(!(l_new_callouts & (CENTAUR0_PRESENT_MASK >> l_cent))) + { + continue; + } + + l_huid = amec_mem_get_huid(l_cent, 0xff); + + amec_mem_mark_logged(l_cent, + 0xff, + &G_cent_overtemp_logged_bitmap, + &G_dimm_overtemp_logged_bitmap.bytes[l_cent]); + + //If we don't have an error log for the callout, create one + if(!l_err) + { + /* @ + * @errortype + * @moduleid AMEC_HEALTH_CHECK_CENT_TEMP + * @reasoncode CENT_ERROR_TEMP + * @userdata1 Maximum centaur temperature + * @userdata2 Centaur temperature threshold + * @userdata4 OCC_NO_EXTENDED_RC + * @devdesc Centaur memory controller(s) exceeded maximum safe + * temperature. + */ + l_err = createErrl(AMEC_HEALTH_CHECK_CENT_TEMP, //modId + CENT_ERROR_TEMP, //reasoncode + OCC_NO_EXTENDED_RC, //Extended reason code + ERRL_SEV_PREDICTIVE, //Severity + NULL, //Trace Buf + DEFAULT_TRACE_SIZE, //Trace Size + l_max_temp, //userdata1 + l_ot_error); //userdata2 + + // Callout the "over temperature" procedure + addCalloutToErrl(l_err, + ERRL_CALLOUT_TYPE_COMPONENT_ID, + ERRL_COMPONENT_ID_OVER_TEMPERATURE, + ERRL_CALLOUT_PRIORITY_HIGH); + l_callouts_count = 1; + } + + // Callout centaur + addCalloutToErrl(l_err, + ERRL_CALLOUT_TYPE_HUID, + l_huid, + ERRL_CALLOUT_PRIORITY_MED); + + l_callouts_count++; + + //If we've reached the max # of callouts for an error log + //commit the error log + if(l_callouts_count == ERRL_MAX_CALLOUTS) + { + commitErrl(&l_err); + } + + }//iterate over centaurs + + if(l_err) + { + commitErrl(&l_err); + } +} + +void amec_health_check_dimm_timeout() +{ + static cent_sensor_flags_t L_temp_update_bitmap_prev = {0}; + cent_sensor_flags_t l_need_inc, l_need_clr, l_temp_update_bitmap; + uint8_t l_dimm, l_cent; + fru_temp_t* l_fru; + errlHndl_t l_err = NULL; + uint32_t l_callouts_count = 0; + uint64_t l_huid; + static bool L_ran_once = FALSE; + + do + { + //For every dimm sensor there are 3 cases to consider + // + //1) sensor is enabled and not updated (need to increment timer and check for timeout) + //2) sensor is enabled and updated but wasn't updated on previous check (need to clear timer) + //3) sensor is enabled and updated and was updated on previous check (do nothing) + + //Grab snapshot of G_dimm_temp_updated_bitmap and clear it + l_temp_update_bitmap.bigword = G_dimm_temp_updated_bitmap.bigword; + G_dimm_temp_updated_bitmap.bigword = 0; + + //check if we need to increment any timers (haven't been updated in the last second) + l_need_inc.bigword = G_cent_enabled_sensors.bigword & ~l_temp_update_bitmap.bigword; + + //check if we need to clear any timers (updated now but not updated previously) + l_need_clr.bigword = l_temp_update_bitmap.bigword & ~L_temp_update_bitmap_prev.bigword; + + //save off the previous bitmap of updated sensors for next time + L_temp_update_bitmap_prev.bigword = l_temp_update_bitmap.bigword; + + //only go further if we actually have work to do here. + if(!l_need_inc.bigword && !l_need_clr.bigword) + { + //nothing to do + break; + } + + //iterate across all centaurs incrementing dimm sensor timers as needed + for(l_cent = 0; l_cent < MAX_NUM_CENTAURS; l_cent++) + { + //any dimm timers behind this centaur need incrementing? + if(!l_need_inc.bytes[l_cent]) + { + //all dimm sensors were updated for this centaur. Clear the dimm timeout bit for this centaur. + if(G_dimm_temp_expired_bitmap & (CENTAUR0_PRESENT_MASK >> l_cent)) + { + G_dimm_temp_expired_bitmap &= ~(CENTAUR0_PRESENT_MASK >> l_cent); + TRAC_INFO("All dimm sensors for centaur %d have been updated", l_cent); + } + continue; + } + + //There's at least one dimm requiring an increment, find the dimm + for(l_dimm = 0; l_dimm < NUM_DIMMS_PER_CENTAUR; l_dimm++) + { + //not this one, go to next one + if(!(l_need_inc.bytes[l_cent] & (DIMM_SENSOR0 >> l_dimm))) + { + continue; + } + + //we found one. + l_fru = &g_amec->proc[0].memctl[l_cent].centaur.dimm_temps[l_dimm]; + + //increment timer + l_fru->sample_age++; + + //handle wrapping + if(!l_fru->sample_age) + { + l_fru->sample_age = -1; + } + + //info trace each transition to not having a new temperature + if(l_fru->sample_age == 1) + { + TRAC_INFO("Failed to read dimm temperature on cent[%d] dimm[%d] temp[%d] flags[0x%02X]", + l_cent, l_dimm, l_fru->cur_temp, l_fru->flags); + } + + //check if the temperature reading is still useable + if(g_amec->thermaldimm.temp_timeout == 0xff || + l_fru->sample_age < g_amec->thermaldimm.temp_timeout) + { + continue; + } + + //temperature has expired. Notify control algorithms which centaur. + if(!(G_dimm_temp_expired_bitmap & (CENTAUR0_PRESENT_MASK >> l_cent))) + { + G_dimm_temp_expired_bitmap |= CENTAUR0_PRESENT_MASK >> l_cent; + TRAC_ERR("Timed out reading dimm temperature sensor on cent %d.", + l_cent); + } + + //If we've already logged an error for this FRU go to the next one. + if(G_dimm_timeout_logged_bitmap.bytes[l_cent] & (DIMM_SENSOR0 >> l_dimm)) + { + continue; + } + + TRAC_ERR("Timed out reading dimm temperature on cent[%d] dimm[%d] temp[%d] flags[0x%02X]", + l_cent, l_dimm, l_fru->cur_temp, l_fru->flags); + + if(!l_err) + { + /* @ + * @errortype + * @moduleid AMEC_HEALTH_CHECK_DIMM_TIMEOUT + * @reasoncode FRU_TEMP_TIMEOUT + * @userdata1 timeout value in seconds + * @userdata2 0 + * @userdata4 OCC_NO_EXTENDED_RC + * @devdesc Failed to read a memory DIMM temperature + * + */ + l_err = createErrl(AMEC_HEALTH_CHECK_DIMM_TIMEOUT, //modId + FRU_TEMP_TIMEOUT, //reasoncode + OCC_NO_EXTENDED_RC, //Extended reason code + ERRL_SEV_PREDICTIVE, //Severity + NULL, //Trace Buf + DEFAULT_TRACE_SIZE, //Trace Size + g_amec->thermaldimm.temp_timeout, //userdata1 + 0); //userdata2 + + l_callouts_count = 0; + } + + //Get the HUID for the dimm + l_huid = amec_mem_get_huid(l_cent, l_dimm); + + // Callout dimm + addCalloutToErrl(l_err, + ERRL_CALLOUT_TYPE_HUID, + l_huid, + ERRL_CALLOUT_PRIORITY_MED); + + l_callouts_count++; + + //If we've reached the max # of callouts for an error log + //commit the error log + if(l_callouts_count == ERRL_MAX_CALLOUTS) + { + commitErrl(&l_err); + } + + //Mark dimm as logged so we don't log it more than once + amec_mem_mark_logged(l_cent, + l_dimm, + &G_cent_timeout_logged_bitmap, + &G_dimm_timeout_logged_bitmap.bytes[l_cent]); + } //iterate over all dimms + + } //iterate over all centaurs + + if(l_err) + { + commitErrl(&l_err); + } + + //skip clearing if no dimms need it + if(!l_need_clr.bigword) + { + break; + } + + //iterate across all centaurs clearing dimm sensor timers as needed + for(l_cent = 0; l_cent < MAX_NUM_CENTAURS; l_cent++) + { + + if(!l_need_clr.bytes[l_cent]) + { + continue; + } + + //iterate over all dimms + for(l_dimm = 0; l_dimm < NUM_DIMMS_PER_CENTAUR; l_dimm++) + { + //not this one, go to next one + if(!(l_need_clr.bytes[l_cent] & (DIMM_SENSOR0 >> l_dimm))) + { + continue; + } + + //we found one. + l_fru = &g_amec->proc[0].memctl[l_cent].centaur.dimm_temps[l_dimm]; + + //clear timer + l_fru->sample_age = 0; + + //info trace each time we recover + if(L_ran_once) + { + TRAC_INFO("DIMM temperature collection has resumed on cent[%d] dimm[%d] temp[%d]", + l_cent, l_dimm, l_fru->cur_temp); + } + + }//iterate over all dimms + }//iterate over all centaurs + }while(0); + L_ran_once = TRUE; + G_thrm_fru_data[DATA_FRU_DIMM].read_failure = G_dimm_temp_expired_bitmap; +} + +void amec_health_check_cent_timeout() +{ + static uint8_t L_temp_update_bitmap_prev = 0; + uint8_t l_need_inc, l_need_clr, l_temp_update_bitmap; + uint8_t l_cent; + fru_temp_t* l_fru; + errlHndl_t l_err = NULL; + uint32_t l_callouts_count = 0; + uint64_t l_huid; + static bool L_ran_once = FALSE; + + do + { + //For every centaur sensor there are 3 cases to consider + // + //1) centaur is present and not updated (need to increment timer and check for timeout) + //2) centaur is present and updated but wasn't updated on previous check (need to clear timer) + //3) centaur is present and updated and was updated on previous check (do nothing) + + //Grab snapshot of G_cent_temp_update_bitmap and clear it + l_temp_update_bitmap = G_cent_temp_updated_bitmap; + G_cent_temp_updated_bitmap = 0; + + //check if we need to increment any timers + l_need_inc = G_present_centaurs & ~l_temp_update_bitmap; + + //check if we need to clear any timers + l_need_clr = l_temp_update_bitmap & ~L_temp_update_bitmap_prev; + + //only go further if we actually have work to do here. + if(!l_need_inc && !l_need_clr) + { + //nothing to do + break; + } + + //save off the previous bitmap of updated sensors + L_temp_update_bitmap_prev = l_temp_update_bitmap; + + //iterate across all centaurs incrementing timers as needed + for(l_cent = 0; l_cent < MAX_NUM_CENTAURS; l_cent++) + { + //does this centaur timer need incrementing? + if(!(l_need_inc & (CENTAUR0_PRESENT_MASK >> l_cent))) + { + //temperature was updated for this centaur. Clear the timeout bit for this centaur. + if(G_cent_temp_expired_bitmap & (CENTAUR0_PRESENT_MASK >> l_cent)) + { + G_cent_temp_expired_bitmap &= ~(CENTAUR0_PRESENT_MASK >> l_cent); + TRAC_INFO("centaur %d temps have been updated", l_cent); + } + continue; + } + + //This centaur requires an increment + l_fru = &g_amec->proc[0].memctl[l_cent].centaur.centaur_hottest; + + //increment timer + l_fru->sample_age++; + + //handle wrapping + if(!l_fru->sample_age) + { + l_fru->sample_age = -1; + } + + //info trace each transition to not having a new temperature + if(l_fru->sample_age == 1) + { + TRAC_INFO("Failed to read centaur temperature on cent[%d] temp[%d] flags[0x%02X]", + l_cent, l_fru->cur_temp, l_fru->flags); + } + + //check if the temperature reading is still useable + if(g_amec->thermalcent.temp_timeout == 0xff || + l_fru->sample_age < g_amec->thermalcent.temp_timeout) + { + continue; + } + + //temperature has expired. Notify control algorithms which centaur. + if(!(G_cent_temp_expired_bitmap & (CENTAUR0_PRESENT_MASK >> l_cent))) + { + G_cent_temp_expired_bitmap |= CENTAUR0_PRESENT_MASK >> l_cent; + TRAC_ERR("Timed out reading centaur temperature sensor on cent %d", + l_cent); + } + + //If we've already logged an error for this FRU go to the next one. + if(G_cent_timeout_logged_bitmap & (CENTAUR0_PRESENT_MASK >> l_cent)) + { + continue; + } + + TRAC_ERR("Timed out reading centaur temperature on cent[%d] temp[%d] flags[0x%02X]", + l_cent, l_fru->cur_temp, l_fru->flags); + + if(!l_err) + { + /* @ + * @errortype + * @moduleid AMEC_HEALTH_CHECK_CENT_TIMEOUT + * @reasoncode FRU_TEMP_TIMEOUT + * @userdata1 timeout value in seconds + * @userdata2 0 + * @userdata4 OCC_NO_EXTENDED_RC + * @devdesc Failed to read a centaur memory controller + * temperature + * + */ + l_err = createErrl(AMEC_HEALTH_CHECK_CENT_TIMEOUT, //modId + FRU_TEMP_TIMEOUT, //reasoncode + OCC_NO_EXTENDED_RC, //Extended reason code + ERRL_SEV_PREDICTIVE, //Severity + NULL, //Trace Buf + DEFAULT_TRACE_SIZE, //Trace Size + g_amec->thermalcent.temp_timeout, //userdata1 + 0); //userdata2 + + l_callouts_count = 0; + } + + //Get the HUID for the centaur + l_huid = amec_mem_get_huid(l_cent, 0xff); + + // Callout centaur + addCalloutToErrl(l_err, + ERRL_CALLOUT_TYPE_HUID, + l_huid, + ERRL_CALLOUT_PRIORITY_MED); + + l_callouts_count++; + + //If we've reached the max # of callouts for an error log + //commit the error log + if(l_callouts_count == ERRL_MAX_CALLOUTS) + { + commitErrl(&l_err); + } + + //Mark centaur as logged so we don't log it more than once + amec_mem_mark_logged(l_cent, + 0xff, + &G_cent_timeout_logged_bitmap, + &G_dimm_timeout_logged_bitmap.bytes[l_cent]); + } //iterate over all centaurs + + if(l_err) + { + commitErrl(&l_err); + } + + //skip clearing timers if no centaurs need it + if(!l_need_clr) + { + break; + } + + //iterate across all centaurs clearing timers as needed + for(l_cent = 0; l_cent < MAX_NUM_CENTAURS; l_cent++) + { + //not this one, go to next one + if(!(l_need_clr & (CENTAUR0_PRESENT_MASK >> l_cent))) + { + continue; + } + + //we found one. + l_fru = &g_amec->proc[0].memctl[l_cent].centaur.centaur_hottest; + + //clear timer + l_fru->sample_age = 0; + + //info trace each time we recover + if(L_ran_once) + { + TRAC_INFO("centaur temperature collection has resumed on cent[%d] temp[%d]", + l_cent, l_fru->cur_temp); + } + + }//iterate over all centaurs + }while(0); + L_ran_once = TRUE; + G_thrm_fru_data[DATA_FRU_CENTAUR].read_failure = G_cent_temp_expired_bitmap; +} + + +// Function Specification +// +// Name: amec_health_check_proc_temp +// +// Description: This function checks if the proc temperature has +// exceeded the error temperature as define in data format 0x13. +// +// End Function Specification +void amec_health_check_proc_temp() +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + uint16_t l_ot_error; + static uint32_t l_error_count = 0; + static BOOLEAN l_ot_error_logged = FALSE; + sensor_t *l_sensor; + errlHndl_t l_err = NULL; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + do + { + // Get TEMP2MSP0PEAK sensor, which is hottest core temperature + // in OCC processor + l_sensor = getSensorByGsid(TEMP2MSP0PEAK); + l_ot_error = g_amec->thermalproc.ot_error; + + // Check to see if we exceeded our error temperature + if (l_sensor->sample > l_ot_error) + { + // Increment the error counter for this FRU + l_error_count++; + + // Trace and log error the first time this occurs + if (l_error_count == AMEC_HEALTH_ERROR_TIMER) + { + // Have we logged an OT error for this FRU already? + if (l_ot_error_logged == TRUE) + { + break; + } + + l_ot_error_logged = TRUE; + + TRAC_ERR("amec_health_check_error_temp: processor has exceeded OT error! temp[%u] ot_error[%u]", + l_sensor->sample, + l_ot_error); + + // Log an OT error + /* @ + * @errortype + * @moduleid AMEC_HEALTH_CHECK_PROC_TEMP + * @reasoncode PROC_ERROR_TEMP + * @userdata1 0 + * @userdata2 Fru peak temperature sensor + * @devdesc Processor FRU has reached error temperature + * threshold and is called out in this error log. + * + */ + l_err = createErrl(AMEC_HEALTH_CHECK_PROC_TEMP, + PROC_ERROR_TEMP, + ERC_AMEC_PROC_ERROR_OVER_TEMPERATURE, + ERRL_SEV_PREDICTIVE, + NULL, + DEFAULT_TRACE_SIZE, + 0, + l_sensor->sample_max); + + // Callout the Ambient procedure + addCalloutToErrl(l_err, + ERRL_CALLOUT_TYPE_COMPONENT_ID, + ERRL_COMPONENT_ID_OVER_TEMPERATURE, + ERRL_CALLOUT_PRIORITY_HIGH); + + // Callout to processor + addCalloutToErrl(l_err, + ERRL_CALLOUT_TYPE_HUID, + G_sysConfigData.proc_huid, + ERRL_CALLOUT_PRIORITY_MED); + + // Commit Error + commitErrl(&l_err); + } + } + else + { + // Trace that we have now dropped below the error threshold + if (l_error_count >= AMEC_HEALTH_ERROR_TIMER) + { + TRAC_INFO("amec_health_check_proc_temp: We have dropped below error threshold for processors. error_count[%u]", + l_error_count); + } + + // Reset the error counter for this FRU + l_error_count = 0; + } + }while (0); + +} + +// Function Specification +// +// Name: amec_health_check_proc_temp_timeout +// +// Description: This function checks if OCC has failed to read the processor +// temperature and if it has exceeded the maximum allowed number of retries. +// +// End Function Specification +void amec_health_check_proc_timeout() +{ + + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + errlHndl_t l_err = NULL; + sensor_t *l_sensor = NULL; + BOOLEAN l_core_fail_detected = FALSE; + static uint32_t L_read_fail_cnt = 0; + uint8_t i = 0; + uint8_t l_bad_core_index = 0; + gpe_bulk_core_data_t *l_core_data_ptr = NULL; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + do + { + for(i=0; i<MAX_NUM_CORES; i++) + { + if(!CORE_PRESENT(i)) + { + // If this core is not present, move on + continue; + } + + // Check if this core's temperature sensor has been updated + l_sensor = AMECSENSOR_ARRAY_PTR(TEMP2MSP0C0,i); + if (l_sensor->update_tag == G_core_temp_update_tag[i]) + { + // If the update tag is not changing, then this core's + // temperature sensor is not being updated. + l_core_fail_detected = TRUE; + l_bad_core_index = i; + } + + // Take a snapshot of the update tag + G_core_temp_update_tag[i] = l_sensor->update_tag; + } + + // Have we found at least one core that has reading failures? + if(!l_core_fail_detected) + { + // We were able to read all cores' temperature sensors so clear our + // counter + L_read_fail_cnt = 0; + } + else + { + // We've failed to read a core's temperature sensor so increment + // our counter + L_read_fail_cnt++; + + // Check if we have reached the maximum read time allowed + if((L_read_fail_cnt == g_amec->thermalproc.temp_timeout) && + (g_amec->thermalproc.temp_timeout != 0xFF)) + { + TRAC_ERR("Timed out reading processor temperature on core_index[%u]", + l_bad_core_index); + + // Get pointer to core data + l_core_data_ptr = proc_get_bulk_core_data_ptr(l_bad_core_index); + + // Trace some critical registers to understand this error better + TRAC_ERR("OHA_Status_Reg[0x%08X] PM_State_Hist_Reg[0x%08X]", + l_core_data_ptr->oha.oha_ro_status_reg.words.low_order, + l_core_data_ptr->pcb_slave.pm_history.words.high_order); + + TRAC_ERR("SensorV0[0x%08X%08X] SensorV1[0x%08X%08X]", + (uint32_t)(l_core_data_ptr->dts_cpm.sensors_v0.value >> 32), + (uint32_t)(l_core_data_ptr->dts_cpm.sensors_v0.value & 0x00000000ffffffffull), + (uint32_t)(l_core_data_ptr->dts_cpm.sensors_v1.value >> 32), + (uint32_t)(l_core_data_ptr->dts_cpm.sensors_v1.value & 0x00000000ffffffffull)); + + /* @ + * @errortype + * @moduleid AMEC_HEALTH_CHECK_PROC_TIMEOUT + * @reasoncode PROC_TEMP_TIMEOUT + * @userdata1 timeout value in seconds + * @userdata2 0 + * @userdata4 OCC_NO_EXTENDED_RC + * @devdesc Failed to read processor temperature. + * + */ + l_err = createErrl(AMEC_HEALTH_CHECK_PROC_TIMEOUT, //modId + PROC_TEMP_TIMEOUT, //reasoncode + OCC_NO_EXTENDED_RC, //Extended reason code + ERRL_SEV_PREDICTIVE, //Severity + NULL, //Trace Buf + DEFAULT_TRACE_SIZE, //Trace Size + g_amec->thermalproc.temp_timeout, //userdata1 + 0); //userdata2 + + // Callout the processor + addCalloutToErrl(l_err, + ERRL_CALLOUT_TYPE_HUID, + G_sysConfigData.proc_huid, + ERRL_CALLOUT_PRIORITY_MED); + + // Commit error log and request reset + REQUEST_RESET(l_err); + } + } + }while(0); +} + +// Function Specification +// +// Name: amec_health_check_proc_vrhot +// +// Description: This function checks if VRHOT signal from processor regulator +// has been asserted. The VRHOT signal is actually derived in firmware: if +// VR_FAN signal is assserted and the 'fans_full_speed' GPIO is ON, then OCC +// will considered VR_HOT as being asserted. +// +// End Function Specification +void amec_health_check_proc_vrhot() +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + static BOOLEAN L_error_logged = FALSE; + sensor_t *l_sensor; + errlHndl_t l_err = NULL; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + // Get VRHOT50USPROC sensor + l_sensor = getSensorByGsid(VRHOT250USPROC); + + // Check to see if we have exceeded our ERROR_COUNT + if(l_sensor->sample >= g_amec->vrhotproc.setpoint) + { + // We have reached the number of successive VRHOT samples allowed. Need + // to log an error (only once per OCC reset). + if(!L_error_logged) + { + L_error_logged = TRUE; + + TRAC_ERR("amec_health_check_proc_vrhot: VRHOT has been asserted! num_samples[%u]", + l_sensor->sample); + + /* @ + * @errortype + * @moduleid AMEC_HEALTH_CHECK_PROC_VRHOT + * @reasoncode VRM_ERROR_TEMP + * @userdata1 VRHOT error threshold + * @userdata2 0 + * @userdata4 OCC_NO_EXTENDED_RC + * @devdesc VRHOT signal has been asserted long enough to + * exceed its error threshold. + * + */ + l_err = createErrl(AMEC_HEALTH_CHECK_PROC_VRHOT, + VRM_ERROR_TEMP, + OCC_NO_EXTENDED_RC, + ERRL_SEV_PREDICTIVE, + NULL, + DEFAULT_TRACE_SIZE, + g_amec->vrhotproc.setpoint, + 0); + + // Callout the Ambient procedure + addCalloutToErrl(l_err, + ERRL_CALLOUT_TYPE_COMPONENT_ID, + ERRL_COMPONENT_ID_OVER_TEMPERATURE, + ERRL_CALLOUT_PRIORITY_HIGH); + + // Callout backplane + addCalloutToErrl(l_err, + ERRL_CALLOUT_TYPE_HUID, + G_sysConfigData.backplane_huid, + ERRL_CALLOUT_PRIORITY_MED); + + // Commit Error + commitErrl(&l_err); + } + } +} + +/*----------------------------------------------------------------------------*/ +/* End */ +/*----------------------------------------------------------------------------*/ diff --git a/src/occ_405/amec/amec_health.h b/src/occ_405/amec/amec_health.h new file mode 100755 index 0000000..35271c0 --- /dev/null +++ b/src/occ_405/amec/amec_health.h @@ -0,0 +1,52 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ_405/amec/amec_health.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#ifndef AMEC_HEALTH_H +#define AMEC_HEALTH_H + +//************************************************************************* +// Includes +//************************************************************************* +#include "cmdh_fsp_cmds_datacnfg.h" + +//************************************************************************* +// Defines/Enums +//************************************************************************* +// Error threshold check is done every 16msec. Error timer is 5x16 = 80msec +#define AMEC_HEALTH_ERROR_TIMER 5 + +/*******************************************************************/ +/* Function Definitions */ +/*******************************************************************/ + +void amec_health_check_proc_temp(void); +void amec_health_check_proc_timeout(void); +void amec_health_check_proc_vrhot(); +void amec_health_check_cent_temp(void); +void amec_health_check_cent_timeout(void); +void amec_health_check_dimm_temp(void); +void amec_health_check_dimm_timeout(void); + +#endif diff --git a/src/occ_405/amec/amec_init.c b/src/occ_405/amec/amec_init.c new file mode 100644 index 0000000..487c9ef --- /dev/null +++ b/src/occ_405/amec/amec_init.c @@ -0,0 +1,436 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/amec/amec_init.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + + +//************************************************************************* +// Includes +//************************************************************************* +#include <occ_common.h> +#include <amec_sys.h> +#include <ssx.h> +#include <errl.h> // Error logging +#include <rtls.h> +#include <occ_sys_config.h> +#include <occ_service_codes.h> // for SSX_GENERIC_FAILURE +#include <trac.h> +#include "state.h" +#include "amec_service_codes.h" +#include <amec_sys.h> +#include <proc_data.h> +#include <sensor.h> + +//************************************************************************* +// Externs +//************************************************************************* + +//************************************************************************* +// Macros +//************************************************************************* + +//************************************************************************* +// Defines/Enums +//************************************************************************* + +//************************************************************************* +// Structures +//************************************************************************* + +//************************************************************************* +// Globals +//************************************************************************* +// We can initialize amec system structure to all zeros +amec_sys_t g_amec_sys = {0}; + +// Initialize g_amec to point to g_amec_sys +// We use this pointer to keep the amec code as similar to previous projects +amec_sys_t * g_amec = &g_amec_sys; + +// GPE Request Structure that is used to measure the worst case GPE timings +PoreFlex G_gpe_nop_request[NUM_GPE_ENGINES]; + +extern PoreEntryPoint GPE_pore_nop; +extern void amec_slv_update_gpe_sensors(uint8_t i_gpe_engine); +extern void amec_slv_update_gpe_sensors(uint8_t i_gpe_engine); + +//************************************************************************* +// Function Prototypes +//************************************************************************* + +//************************************************************************* +// Functions +//************************************************************************* +void amec_vectorize_core_sensor(sensor_t * l_sensor, + vectorSensor_t * l_vector, + const VECTOR_SENSOR_OP l_op, + uint16_t l_sensor_elem_array_gsid) +{ +#define VECTOR_CREATE_FAILURE 1 +#define VECTOR_ADD_ELEM_FAILURE 2 + + int l_idx = 0; // Used to index the for loops for vector create + int l_rc = 0; // Indicates failure to add a sensor to vector + uint16_t l_gsid = 0xFFFF; + errlHndl_t l_err = NULL; + + do + { + // Grab GSID for errl in case of failure + l_gsid = l_sensor->gsid; + + // Vectorize the sensor + sensor_vectorize(l_sensor, + l_vector, + l_op); + + // If vectorize worked, add elements to the vector sensor + if(NULL != l_sensor->vector) + { + // Loop through cores + for(l_idx = 0; l_idx < MAX_NUM_CORES; l_idx++) + { + // Add elements to the vector sensor + sensor_vector_elem_add(l_sensor->vector, + l_idx, + AMECSENSOR_ARRAY_PTR(l_sensor_elem_array_gsid, l_idx)); + // If core is not present, disable this vector element + if(!CORE_PRESENT(l_idx)) + { + sensor_vector_elem_enable(l_sensor->vector, + l_idx, + 0 /* Disable */); + } + } + + // Sanity check, we should have MAX_NUM_CORES entries in + // vector sensor + if(l_sensor->vector->size != MAX_NUM_CORES) + { + // Set l_rc and break out so that we can create an errl + l_rc = VECTOR_ADD_ELEM_FAILURE; + break; + } + } + else + { + // Set l_rc and break out so that we can create an errl + l_rc = VECTOR_CREATE_FAILURE; + break; + } + }while(0); + + if(l_rc) + { + //If fail to create pore flex object then there is a problem. + TRAC_ERR("Failed to vectorize sensor[0x%x, 0x%x]", l_gsid, l_rc ); + + /* @ + * @errortype + * @moduleid AMEC_VECTORIZE_FW_SENSORS + * @reasoncode SSX_GENERIC_FAILURE + * @userdata1 return code + * @userdata2 gsid of failed sensor + * @userdata4 OCC_NO_EXTENDED_RC + * @devdesc Firmware failure in call to vectorize sensor + */ + l_err = createErrl( + AMEC_VECTORIZE_FW_SENSORS, //modId + SSX_GENERIC_FAILURE, //reasoncode + OCC_NO_EXTENDED_RC, //Extended reason code + ERRL_SEV_UNRECOVERABLE, //Severity + NULL,//TODO: create trace //Trace Buf + DEFAULT_TRACE_SIZE, //Trace Size + l_rc, //userdata1 + l_gsid //userdata2 + ); + + REQUEST_RESET(l_err); + } +} + +void amec_init_vector_sensors(void) +{ + +#define VECTOR_CREATE_FAILURE 1 +#define VECTOR_ADD_ELEM_FAILURE 2 + + //----------------------------------------------------- + // TEMP2MSP0 Vector Sensor + //----------------------------------------------------- + amec_vectorize_core_sensor(AMECSENSOR_PTR(TEMP2MSP0), + &g_amec_sys.proc[0].temp2ms_vector, + VECTOR_OP_AVG, + TEMP2MSP0C0); + + //----------------------------------------------------- + // FREQA2MSP0 Vector Sensor + //----------------------------------------------------- + amec_vectorize_core_sensor(AMECSENSOR_PTR(FREQA2MSP0), + &g_amec_sys.proc[0].freqa2ms_vector, + VECTOR_OP_AVG, + FREQA2MSP0C0); + + //----------------------------------------------------- + // IPS2MSP0 Vector Sensor + //----------------------------------------------------- + amec_vectorize_core_sensor(AMECSENSOR_PTR(IPS2MSP0), + &g_amec_sys.proc[0].ips2ms_vector, + VECTOR_OP_AVG, + IPS2MSP0C0); + + //----------------------------------------------------- + // TEMP2MSP0PEAK Vector Sensor + //----------------------------------------------------- + amec_vectorize_core_sensor(AMECSENSOR_PTR(TEMP2MSP0PEAK), + &g_amec_sys.proc[0].temp2mspeak_vector, + VECTOR_OP_MAX, + TEMP2MSP0C0); + + //----------------------------------------------------- + // UTIL2MSP0 Vector Sensor + //----------------------------------------------------- + amec_vectorize_core_sensor(AMECSENSOR_PTR(UTIL2MSP0), + &g_amec_sys.proc[0].util2ms_vector, + VECTOR_OP_AVG, + UTIL2MSP0C0); + +//TODO: Re-enable with error checking when centaur support is added +#if 0 + int l_rc = 0, l_idx = 0, l_idx2 = 0; // Used to index the for loops for vector create + //----------------------------------------------------- + // MEMSP2MSP0 Vector Sensor + //----------------------------------------------------- + sensor_vectorize(AMECSENSOR_PTR(MEMSP2MSP0), + &g_amec_sys.proc[0].memsp2ms_vector, + VECTOR_OP_MIN); + + for(l_idx=0; l_idx<MAX_NUM_MEM_CONTROLLERS; l_idx++) + { + for(l_idx2=0; l_idx2<NUM_PORT_PAIRS_PER_CENTAUR; l_idx2++) + { + sensor_vector_elem_add(AMECSENSOR_PTR(MEMSP2MSP0)->vector, + l_idx, + AMECSENSOR_2D_ARRAY_PTR(MEMSP2MSPM0C0P0,l_idx, l_idx2)); + } + } +#endif +} + +// Function Specification +// +// Name: amec_init_gamec_struct +// +// Description: Perform initialization of g_amec structure +// +// End Function Specification +void amec_init_gamec_struct(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + uint16_t l_idx = 0; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + // Defaul the frequency range to something safe + g_amec->sys.fmin = 2000; + g_amec->sys.fmax = 2000; + g_amec->sys.max_speed = 1000; + + g_amec->sys.min_speed = 400; + g_amec->sys.speed_step = 10; + g_amec->sys.speed_step_limit = (uint16_t)((65535/4)/(g_amec->sys.speed_step)); + + // Initialize thermal controller for processor + g_amec->thermalproc.setpoint = 850; // change to 850 = 85.0 C + g_amec->thermalproc.Pgain = 1000; + g_amec->thermalproc.speed_request = 1000; + g_amec->thermalproc.freq_request = -1; //unconstrained frequency vote + g_amec->thermalproc.total_res = 0; + + // Initialize thermal controller based on DIMM temperatures + g_amec->thermaldimm.setpoint = 850; //In 0.1 degrees C -> 850 = 85.0 C + g_amec->thermaldimm.Pgain = 30000; + g_amec->thermaldimm.speed_request = AMEC_MEMORY_MAX_STEP; + + // Initialize thermal controller based on Centaur temperatures + g_amec->thermalcent.setpoint = 850; //In 0.1 degrees C -> 850 = 85.0 C + g_amec->thermalcent.Pgain = 30000; + g_amec->thermalcent.speed_request = AMEC_MEMORY_MAX_STEP; + + // Initialize controler based on VRHOT signal from processor regulator + g_amec->vrhotproc.setpoint = 100; + g_amec->vrhotproc.freq_request = -1; + g_amec->vrhotproc.speed_request = 1000; + + // Initialize partition information + amec_part_init(); + + // Initialize performace counter + for (l_idx=0; l_idx<MAX_NUM_CORES; l_idx++) + { + amec_core_perf_counter_ctor(&g_amec->proc[0].core[l_idx].core_perf, 0, l_idx); + } + + //Initialize processor fields + g_amec->proc[0].core_max_freq = G_sysConfigData.sys_mode_freq.table[OCC_MODE_TURBO]; + + //Initialize processor power votes + g_amec->proc[0].pwr_votes.pmax_clip_freq = G_sysConfigData.sys_mode_freq.table[OCC_MODE_TURBO]; + g_amec->proc[0].pwr_votes.apss_pmax_clip_freq = 0xFFFF; + + //Initialize stream buffer recording parameters + g_amec->recordflag=0; // Never enable recording until requested via Amester API call + g_amec->r_cnt=0; // Reset counter of 250us ticks + g_amec->ptr_stream_buffer = &g_amec->stream_buffer[0]; + g_amec->stream_vector_mode=0; // No recording yet + g_amec->stream_vector_delay=0; // Delay in msec before recording can begin + g_amec->stream_vector_rate=0xff; // Invalid setting: requires IPMI command to select initial rate + + //Initialize analytics parameters + g_amec->analytics_group=45; // Default to analytics Group 45 + g_amec->analytics_chip=0; // Default to which chip to perform analytics on + g_amec->analytics_bad_output_count=0; // Number of frames to discard before recording analytics output + g_amec->analytics_total_chips=MAX_NUM_CHIP_MODULES; // Default to do all chips in the system + g_amec->analytics_threadmode=1; // Default is average of all N threads (may be altered with IPMI command) + g_amec->analytics_threadcountmax=8;// Default is 8 threads per core (may be altered with IPMI command) + g_amec->analytics_total_chips=4; // For Tuleta force to only 2 DCM sockets, 4 chips + g_amec->analytics_option=1; // =0 means cycle through all chips, =1 means only work with analytics_chip + g_amec->analytics_thermal_offset=0;// Reset offset to 0 for thermal output group + g_amec->analytics_slot=4; // Time slot associated with when the amec_analytics function is called (out of 8 slots) + // Set entire averaging buffer to zero + memset (&g_amec->g44_avg, 0, 4*(MAX_SENSORS_ANALYTICS*MAX_NUM_CHIP_MODULES)); + for(l_idx=0; l_idx<NUM_AMEC_FW_PROBES; l_idx++) + { + g_amec->ptr_probe250us[l_idx] = &g_amec->sys.pwr250us.sample; + g_amec->size_probe250us[l_idx] = 2; // Size of object pointed to by probe is 2 bytes + g_amec->index_probe250us[l_idx] = 0; // Initialize all offsets to 0 (used only if size > 2) + } + +// g_amec->ptr_probe250us[1] = g_amec->proc[0].sleepcnt2ms.sample; +// g_amec->ptr_probe250us[2] = &g_amec->g44_avg[(0*MSA)+49]; +// g_amec->ptr_probe250us[2] = g_amec->ptr_probe250us[2]+2; // Point to low 16 bits of g44_avg +// g_amec->ptr_probe250us[3] = &g_amec->proc[0].core[0].thread[0].util2ms_thread; + g_amec->ptr_probe250us[1] = &g_amec->sys.pwr250us.sample; + g_amec->ptr_probe250us[2] = &g_amec->r_cnt; + g_amec->ptr_probe250us[2] = g_amec->ptr_probe250us[2]+2; // Point to low 16 bits of r_cnt + g_amec->ptr_probe250us[3] = &g_amec->r_cnt; +// g_amec->ptr_probe250us[4] = &g_amec->testscom1; +// g_amec->ptr_probe250us[5] = &g_amec->traffic_delay; // holds loop delay for holding up memory traffic +// g_amec->ptr_probe250us[6] = &g_amec->testscom1; +// g_amec->ptr_probe250us[6] = g_amec->ptr_probe250us[6]+2; // Point to low 16 bits of testscom1 +// g_amec->ptr_probe250us[7] = &g_amec->task_centaur_data_count; + +} + +// Function Specification +// +// Name: amec_slave_init +// +// Description: Perform initialization of any/all AMEC Slave Functions +// +// End Function Specification +void amec_slave_init() +{ + errlHndl_t l_err = NULL; // Error handler + int rc = 0; // Return code + int rc2 = 0; // Return code + + // Set the GPE Request Pointers to NULL in case the create fails. + G_fw_timing.gpe0_timing_request = NULL; + G_fw_timing.gpe1_timing_request = NULL; + + // Initializes the GPE routine that will be used to measure the worst case + // timings for GPE0 + rc = pore_flex_create( &G_gpe_nop_request[0], //gpe_req for the task + &G_pore_gpe0_queue, //queue + (void *) GPE_pore_nop, //entry point + (uint32_t) NULL, //parm for the task + SSX_WAIT_FOREVER, //no timeout + (AsyncRequestCallback) amec_slv_update_gpe_sensors, //callback + (void *) GPE_ENGINE_0, //callback argument + ASYNC_CALLBACK_IMMEDIATE ); //options + + // Initializes the GPE routine that will be used to measure the worst case + // timings for GPE1 + rc2 = pore_flex_create( &G_gpe_nop_request[1], //gpe_req for the task + &G_pore_gpe1_queue, //queue + (void *)GPE_pore_nop, //entry point + (uint32_t) NULL, //parm for the task + SSX_WAIT_FOREVER, //no timeout + (AsyncRequestCallback) amec_slv_update_gpe_sensors, //callback + (void *) GPE_ENGINE_1, //callback argument + ASYNC_CALLBACK_IMMEDIATE ); //options + + // If we couldn't create the poreFlex objects, there must be a major problem + // so we will log an error and halt OCC. + if( rc || rc2 ) + { + //If fail to create pore flex object then there is a problem. + TRAC_ERR("Failed to create GPE duration poreFlex object[0x%x, 0x%x]", rc, rc2 ); + + /* @ + * @errortype + * @moduleid AMEC_INITIALIZE_FW_SENSORS + * @reasoncode SSX_GENERIC_FAILURE + * @userdata1 return code - gpe0 + * @userdata2 return code - gpe1 + * @userdata4 OCC_NO_EXTENDED_RC + * @devdesc Failure to create PORE-GPE poreFlex object for FW timing + * analysis. + * + */ + l_err = createErrl( + AMEC_INITIALIZE_FW_SENSORS, //modId + SSX_GENERIC_FAILURE, //reasoncode + OCC_NO_EXTENDED_RC, //Extended reason code + ERRL_SEV_PREDICTIVE, //Severity + NULL, //TODO: create trace //Trace Buf + DEFAULT_TRACE_SIZE, //Trace Size + rc, //userdata1 + rc2 //userdata2 + ); + + REQUEST_RESET(l_err); + } + else + { + // Everything was successful, so set FW timing pointers to these + // GPE Request objects + G_fw_timing.gpe0_timing_request = &G_gpe_nop_request[0]; + G_fw_timing.gpe1_timing_request = &G_gpe_nop_request[1]; + } + + // Initialize Vector Sensors for AMEC use + amec_init_vector_sensors(); + + // Initialize AMEC internal parameters + amec_init_gamec_struct(); +} + +/*----------------------------------------------------------------------------*/ +/* End */ +/*----------------------------------------------------------------------------*/ diff --git a/src/occ_405/amec/amec_master_smh.c b/src/occ_405/amec/amec_master_smh.c new file mode 100755 index 0000000..c49ca06 --- /dev/null +++ b/src/occ_405/amec/amec_master_smh.c @@ -0,0 +1,1076 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ_405/amec/amec_master_smh.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +//************************************************************************* +// Includes +//************************************************************************* +#include <occ_common.h> +#include <ssx.h> +#include <errl.h> // Error logging +#include "rtls.h" +#include "occ_service_codes.h" // for SSX_GENERIC_FAILURE +#include "sensor.h" +#include "amec_smh.h" +#include "amec_master_smh.h" +#include <trac.h> // For traces +#include "amec_sys.h" +#include "amec_service_codes.h" //For AMEC_MST_CHECK_PCAPS_MATCH +#include "dcom.h" + +//************************************************************************* +// Externs +//************************************************************************* + +//************************************************************************* +// Macros +//************************************************************************* + +//************************************************************************* +// Defines/Enums +//************************************************************************* + +//Power cap mismatch threshold set to 8 ticks (2 milliseconds) +#define PCAPS_MISMATCH_THRESHOLD 8 + +//Power cap failure threshold set to 32 (ticks) +#define PCAP_FAILURE_THRESHOLD 32 + +//************************************************************************* +// Structures +//************************************************************************* + +//************************************************************************* +// Globals +//************************************************************************* +smh_state_t G_amec_mst_state = {AMEC_INITIAL_STATE, + AMEC_INITIAL_STATE, + AMEC_INITIAL_STATE}; + +//Array that stores active power cap values of all OCCs +slave_pcap_info_t G_slave_active_pcaps[MAX_OCCS] = {{0}}; + +//OCC Power cap mismatch count +uint8_t G_pcaps_mismatch_count = 0; + +//OCC over power cap count +uint8_t G_over_cap_count = 0; + +//Array that stores the exit counts for the IPS algorithm +uint32_t G_ips_exit_count[MAX_OCCS][MAX_CORES] = {{0}}; +//Array that stores the entry counts for the IPS algorithm +uint32_t G_ips_entry_count = 0; + +//Soft Fmin to be sent to Slave OCCs +uint16_t G_mst_soft_fmin = 0; +//Soft Fmax to be sent to Slave OCCs +uint16_t G_mst_soft_fmax = 0xFFFF; +//Counter of committed violations by the Slave OCCs +uint8_t G_mst_violation_cnt[MAX_OCCS] = {0}; + +// -------------------------------------------------------- +// AMEC Master State 6.1 Substate Table +// -------------------------------------------------------- +// Each function inside this state table runs once every 128ms. +// +const smh_tbl_t amec_mst_state_6_1_sub_substate_table[AMEC_SMH_STATES_PER_LVL] = +{ + {amec_mst_sub_substate_6_1_0, NULL}, + {amec_mst_sub_substate_6_1_1, NULL}, + {amec_mst_sub_substate_6_1_2, NULL}, + {amec_mst_sub_substate_6_1_3, NULL}, + {amec_mst_sub_substate_6_1_4, NULL}, + {amec_mst_sub_substate_6_1_5, NULL}, + {amec_mst_sub_substate_6_1_6, NULL}, + {amec_mst_sub_substate_6_1_7, NULL}, +}; + +// -------------------------------------------------------- +// AMEC Master State 0 Substate Table +// -------------------------------------------------------- +// Each function inside this state table runs once every 16ms. +// +// The 2 States are interleaved so each one runs every 4ms. +// +const smh_tbl_t amec_mst_state_0_substate_table[AMEC_SMH_STATES_PER_LVL] = +{ + {amec_mst_substate_0_0, NULL}, + {amec_mst_substate_0_1, NULL}, + {amec_mst_substate_0_0, NULL}, + {amec_mst_substate_0_1, NULL}, + {amec_mst_substate_0_0, NULL}, + {amec_mst_substate_0_1, NULL}, + {amec_mst_substate_0_0, NULL}, + {amec_mst_substate_0_1, NULL}, +}; + +// -------------------------------------------------------- +// AMEC Master State 3 Substate Table +// -------------------------------------------------------- +// Each function inside this state table runs once every 16ms. +// +// The 2 States are interleaved so each one runs every 4ms. +// +const smh_tbl_t amec_mst_state_3_substate_table[AMEC_SMH_STATES_PER_LVL] = + +{ + {amec_mst_substate_3_0, NULL}, + {amec_mst_substate_3_1, NULL}, + {amec_mst_substate_3_0, NULL}, + {amec_mst_substate_3_1, NULL}, + {amec_mst_substate_3_0, NULL}, + {amec_mst_substate_3_1, NULL}, + {amec_mst_substate_3_0, NULL}, + {amec_mst_substate_3_1, NULL}, +}; + +// -------------------------------------------------------- +// AMEC Master State 6 Substate Table +// -------------------------------------------------------- +// Each function inside this state table runs once every 16ms. +// +// SubState1: 8 Sub-substates (128ms/sub-substate) +// +const smh_tbl_t amec_mst_state_6_substate_table[AMEC_SMH_STATES_PER_LVL] = +{ + {amec_mst_substate_6_0, NULL}, + {amec_mst_substate_6_1, amec_mst_state_6_1_sub_substate_table}, + {amec_mst_substate_6_2, NULL}, + {amec_mst_substate_6_3, NULL}, + {amec_mst_substate_6_4, NULL}, + {amec_mst_substate_6_5, NULL}, + {amec_mst_substate_6_6, NULL}, + {amec_mst_substate_6_7, NULL}, +}; + +// -------------------------------------------------------- +// Main AMEC Master State Table +// -------------------------------------------------------- +// Each function inside this state table runs once every 2ms. +// +// State0: 2 Substates (4ms/substate) +// State3: 2 Substates (4ms/substate) +// State6: 8 Substates (16ms/substate) +// +const smh_tbl_t amec_mst_state_table[AMEC_SMH_STATES_PER_LVL] = +{ + {amec_mst_state_0, amec_mst_state_0_substate_table}, + {amec_mst_state_1, NULL}, + {amec_mst_state_2, NULL}, + {amec_mst_state_3, amec_mst_state_3_substate_table}, + {amec_mst_state_4, NULL}, + {amec_mst_state_5, NULL}, + {amec_mst_state_6, amec_mst_state_6_substate_table}, + {amec_mst_state_7, NULL}, +}; + +// This sets up the function pointer that will be called to update the +// fw timings when the AMEC master State Machine finishes. +smh_state_timing_t G_amec_mst_state_timings = {amec_mst_update_smh_sensors}; + +//************************************************************************* +// Function Prototypes +//************************************************************************* + +//************************************************************************* +// Functions +//************************************************************************* + +// Function Specification +// +// Name: amec_mst_update_smh_sensors +// +// Description: Update the sensor +// +// End Function Specification +void amec_mst_update_smh_sensors(int i_smh_state, uint32_t i_duration) +{ + // Update the duration in the fw timing table + if(G_fw_timing.amess_state != i_smh_state) + { + AMEC_DBG("Mismatch between Master and Slave AMEC states\n"); + } + + G_fw_timing.amess_dur += i_duration; +} + +// Function Specification +// +// Name: amec_master_auto_slew +// +// Description: This function executes the auto-slewing of frequency based on +// manufacturing parameters. +// +// Task Flags: +// +// End Function Specification +void amec_master_auto_slew(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + static int8_t l_direction = 1; + uint16_t l_freq = 0; + static uint16_t l_step_delay = 0; + static bool l_first_time_enable = TRUE; + static bool l_first_time_disable = FALSE; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + do + { + // Check if auto-slewing has been enabled + if (g_amec->mnfg_parms.auto_slew) + { + // Is this the first time we are enabling auto-slewing? + if (l_first_time_enable) + { + // Need to start the auto-slew from fmax + g_amec->mnfg_parms.foverride = g_amec->mnfg_parms.fmax; + + // Reset the delay counter and reset the direction + l_step_delay = g_amec->mnfg_parms.delay; + l_direction = 1; + + l_first_time_enable = FALSE; + l_first_time_disable = TRUE; + + // Break out now to give it some time to get to fmax + break; + } + } + else + { + // Is this the first time we are disabling auto-slewing? + if (l_first_time_disable) + { + // Clear the frequency override parameter + g_amec->mnfg_parms.foverride = 0; + + l_first_time_enable = TRUE; + l_first_time_disable = FALSE; + } + + // We are done, break + break; + } + + // If we've made it this far, then we are running auto-slew. First, + // check if our delay counter has expired. + if (l_step_delay != 0) + { + // Decrement our delay counter and exit + l_step_delay--; + break; + } + + // Our delay counter has expired, reset it to its original value + l_step_delay = g_amec->mnfg_parms.delay; + + // Now, generate a new frequency override + l_freq = g_amec->mnfg_parms.foverride - + (l_direction * g_amec->mnfg_parms.fstep); + + if (l_freq <= g_amec->mnfg_parms.fmin) + { + // We've reached an edge, increment our counter + g_amec->mnfg_parms.slew_counter++; + + // Change direction of the auto-slew + l_direction = -l_direction; + + // Clip frequency to fmin + l_freq = g_amec->mnfg_parms.fmin; + } + else if (l_freq >= g_amec->mnfg_parms.fmax) + { + // We've reached an edge, increment our counter + g_amec->mnfg_parms.slew_counter++; + + // Change direction of the auto-slew + l_direction = -l_direction; + + // Clip frequency to fmax + l_freq = g_amec->mnfg_parms.fmax; + } + g_amec->mnfg_parms.foverride = l_freq; + + }while(0); + + return; +} + +// Function Specification +// +// Name: amec_mst_check_pcaps_match +// +// Description: This function checks for mismatch in power caps between +// occs for 8 consecutive ticks. +// +// End Function Specification +void amec_mst_check_pcaps_match(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + uint8_t l_chip_id; + bool l_prev_pcap_valid = FALSE; + uint16_t l_prev_pcap = 0; + bool l_pcap_mismatch = FALSE; + errlHndl_t l_err = NULL; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + //Loop through all occs + for(l_chip_id = 0; l_chip_id < MAX_OCCS; l_chip_id++) + { + //if occ is present && its pcap data is considered valid + if((G_sysConfigData.is_occ_present & (1<< l_chip_id)) && + (G_slave_active_pcaps[l_chip_id].pcap_valid != 0)) + { + //TRAC_INFO(" occ[%d]: pcap[%d] valid(%d)", + // l_chip_id, G_slave_active_pcaps[l_chip_id].active_pcap, G_slave_active_pcaps[l_chip_id].pcap_valid ); + + //Initialize l_prev_pcap to the first valid/present occ's power cap + if(!l_prev_pcap_valid) + { + //TRAC_INFO("First present occ - power cap info[%d]=%d(%d)", + // l_chip_id, G_slave_active_pcaps[l_chip_id].active_pcap, G_slave_active_pcaps[l_chip_id].pcap_valid ); + + l_prev_pcap = G_slave_active_pcaps[l_chip_id].active_pcap; + l_prev_pcap_valid = TRUE; + } + else + { + //If there is mismatch between OCCs power caps, increment mismatch + // count + if(l_prev_pcap != G_slave_active_pcaps[l_chip_id].active_pcap) + { + G_pcaps_mismatch_count++; + l_pcap_mismatch = TRUE; + + TRAC_INFO("Mismatch in OCC power cap values: mismatch cnt=%d pcap=%d vs compared pcap[%d]=%d(%d)", + G_pcaps_mismatch_count, l_prev_pcap, l_chip_id, G_slave_active_pcaps[l_chip_id].active_pcap, + G_slave_active_pcaps[l_chip_id].pcap_valid); + + //If mismatch occurs for 8 consecutive ticks + //i.e 8 * 250 microsecs = 2 milliseconds,then reset occ + if(G_pcaps_mismatch_count >= PCAPS_MISMATCH_THRESHOLD) + { + TRAC_ERR("Mismatch in OCC power cap values: pcap=%d, slave_active_pcap[%d]=%d(%d)", + l_prev_pcap, l_chip_id, G_slave_active_pcaps[l_chip_id].active_pcap, + G_slave_active_pcaps[l_chip_id].pcap_valid); + + /* @ + * @errortype + * @moduleid AMEC_MST_CHECK_PCAPS_MATCH + * @reasoncode INTERNAL_FAILURE + * @userdata1 First OCC Power cap + * @userdata2 Mismatch OCC Power cap + * @devdesc Internal max power limits mismatched + * + */ + l_err = createErrl( AMEC_MST_CHECK_PCAPS_MATCH, + INTERNAL_FAILURE, + ERC_AMEC_PCAPS_MISMATCH_FAILURE, + ERRL_SEV_PREDICTIVE, + NULL, + DEFAULT_TRACE_SIZE, + l_prev_pcap, + G_slave_active_pcaps[l_chip_id].active_pcap); + + //Callout to OVS + addCalloutToErrl(l_err, + ERRL_CALLOUT_TYPE_COMPONENT_ID, + ERRL_COMPONENT_ID_FIRMWARE, + ERRL_CALLOUT_PRIORITY_HIGH); + + //Callout to APSS + addCalloutToErrl(l_err, + ERRL_CALLOUT_TYPE_HUID, + G_sysConfigData.apss_huid, + ERRL_CALLOUT_PRIORITY_HIGH); + + //Callout to DPSS + addCalloutToErrl(l_err, + ERRL_CALLOUT_TYPE_HUID, + G_sysConfigData.dpss_huid, + ERRL_CALLOUT_PRIORITY_HIGH); + //Reset OCC + REQUEST_RESET(l_err); + } + break; + } + } + } + } + + //If there was no power cap mismatch between occ's,then reset count to zero + if(!l_pcap_mismatch) + { + G_pcaps_mismatch_count = 0; + } +} + + +// Function Specification +// +// Name: amec_mst_check_under_pcap +// +// Description: This function checks if there is failure in maintaining power +// cap value. +// +// End Function Specification + +void amec_mst_check_under_pcap(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + errlHndl_t l_err = NULL; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + // Check if ppb_fmax = Fmin and PWR250US > Node power cap and + // Node power cap >= hard_min_pcap + if((g_amec->proc[0].pwr_votes.ppb_fmax == g_amec->sys.fmin) && + (AMECSENSOR_PTR(PWR250US)->sample > g_amec->pcap.active_node_pcap) && + (g_amec->pcap.active_node_pcap >= G_sysConfigData.pcap.hard_min_pcap)) + { + + G_over_cap_count++; + + //Log error and reset OCC if count >= 32 (ticks) + if(G_over_cap_count >= PCAP_FAILURE_THRESHOLD) + { + TRAC_ERR("Failure to maintain power cap: Power Cap = %d ," + "PWR250US = %d ,PWR250USP0 = %d ,PWR250USFAN = %d ," + "PWR250USMEM0 = %d",g_amec->pcap.active_node_pcap, + AMECSENSOR_PTR(PWR250US)->sample, + AMECSENSOR_PTR(PWR250USP0)->sample, + AMECSENSOR_PTR(PWR250USFAN)->sample, + AMECSENSOR_PTR(PWR250USMEM0)->sample); + + TRAC_ERR("PWR250USIO = %d , PWR250USSTORE = %d, PWR250USGPU = %d", + AMECSENSOR_PTR(PWR250USIO)->sample, + AMECSENSOR_PTR(PWR250USSTORE)->sample, + AMECSENSOR_PTR(PWR250USGPU)->sample); + + /* @ + * @errortype + * @moduleid AMEC_MST_CHECK_UNDER_PCAP + * @reasoncode POWER_CAP_FAILURE + * @userdata1 Power Cap + * @userdata2 PWR250US (Node Power) + * @devdesc Failure to maintain max power limits + * + */ + l_err = createErrl( AMEC_MST_CHECK_UNDER_PCAP, + POWER_CAP_FAILURE, + ERC_AMEC_UNDER_PCAP_FAILURE, + ERRL_SEV_PREDICTIVE, + NULL, + DEFAULT_TRACE_SIZE, + g_amec->pcap.active_node_pcap, + AMECSENSOR_PTR(PWR250US)->sample); + + //Callout to firmware + addCalloutToErrl(l_err, + ERRL_CALLOUT_TYPE_COMPONENT_ID, + ERRL_COMPONENT_ID_FIRMWARE, + ERRL_CALLOUT_PRIORITY_HIGH); + + //Callout to APSS + addCalloutToErrl(l_err, + ERRL_CALLOUT_TYPE_HUID, + G_sysConfigData.apss_huid, + ERRL_CALLOUT_PRIORITY_HIGH); + + //Reset OCC + REQUEST_RESET(l_err); + } + } + else + { + //Decrement count if node power under power cap value + if(G_over_cap_count > 0) + { + G_over_cap_count--; + } + } + + return; +} + +// Function Specification +// +// Name: amec_mst_ips_main +// +// Description: This function executes the Idle Power Saver (IPS) +// algorithm. +// +// End Function Specification +void amec_mst_ips_main(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + uint16_t i = 0; + uint16_t j = 0; + uint16_t l_time_interval = 0; + uint16_t l_core_count = 0; + uint16_t l_entry_core_count = 0; + BOOLEAN l_exit = FALSE; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + do + { + // If IPS is disabled, no need to execute the algorithm + if (g_amec->mst_ips_parms.enable == 0) + { + // Reset the following parameters + g_amec->mst_ips_parms.active = 0; + G_ips_entry_count = 0; + g_amec->mst_ips_parms.freq_request = g_amec->sys.fmax; + + break; + } + + // We made it here, so that means IPS is enabled. + l_time_interval = AMEC_DPS_SAMPLING_RATE * AMEC_IPS_AVRG_INTERVAL; + + // If IPS is active, check if the exit criteria has been met + if (g_amec->mst_ips_parms.active == 1) + { + // Useful trace for debugging + //TRAC_INFO("DBG_IPS: IPS is active! exit_countp0c5[%u]", + // G_ips_exit_count[0][5]); + + for (i=0; i<MAX_OCCS; i++) + { + if (!(G_sysConfigData.is_occ_present & (1<<i))) + { + // This OCC is not present, just move along + continue; + } + + for (j=0; j<MAX_CORES; j++) + { + // Check if this core's normalized utilization has exceeded + // the exit threshold + if (G_dcom_slv_outbox_rx[i].nutil3sp0cy[j] >= + g_amec->mst_ips_parms.exit_threshold) + { + // Increment our exit count by the number of samples + // used to average utilization (default is 3 seconds) + G_ips_exit_count[i][j] += l_time_interval; + + if (G_ips_exit_count[i][j] >= + g_amec->mst_ips_parms.exit_delay) + { + // Found one core that meets the exit criteria! + l_exit = TRUE; + break; + } + } + else + { + // If the normalized utilization of this core is below + // the exit threshold, reset its exit count + G_ips_exit_count[i][j] = 0; + } + } + + if (l_exit) + { + break; + } + } + + // If we found a core that meets the exit criteria, then exit IPS + // active state + if (l_exit) + { + g_amec->mst_ips_parms.active = 0; + G_ips_entry_count = 0; + // A special request of 0 frequency will inform the slaves to + // ignore the IPS request from the master + g_amec->mst_ips_parms.freq_request = 0; + + TRAC_INFO("amec_mst_ips_main: We have exited IPS active state! exit_count[%u] freq_request[%u]", + G_ips_exit_count[i][j], g_amec->mst_ips_parms.freq_request); + } + } + else //IPS is inactive, check if entry criteria has been met + { + // Useful trace for debugging + //TRAC_INFO("DBG_IPS: IPS is inactive! entry_count[%u]", + // G_ips_entry_count); + + for (i=0; i<MAX_OCCS; i++) + { + if (!(G_sysConfigData.is_occ_present & (1<<i))) + { + // This OCC is not present, just move along + continue; + } + + for (j=0; j<MAX_CORES; j++) + { + l_core_count++; + + // Note: cores that are not present will return a zero + // utilization, thus fulfilling the check below + if (G_dcom_slv_outbox_rx[i].nutil3sp0cy[j] < + g_amec->mst_ips_parms.entry_threshold) + { + // Count how many cores have a normalized utilization + // below the entry threshold + l_entry_core_count++; + } + else + { + // Reset the entry count since we found a core that + // has a utilization >= entry threshold + G_ips_entry_count = 0; + } + } + } + + // All cores have a utilization lower than the entry threshold + if (l_entry_core_count == l_core_count) + { + // Increment the entry count by the number of samples used to + // average utilization (default is 3 seconds) + G_ips_entry_count += l_time_interval; + + if (G_ips_entry_count >= g_amec->mst_ips_parms.entry_delay) + { + // We have met the entry criteria, move IPS state to active + g_amec->mst_ips_parms.active = 1; + + for (i=0; i<MAX_OCCS; i++) + { + if (!(G_sysConfigData.is_occ_present & (1<<i))) + { + continue; + } + for (j=0; j<MAX_CORES; j++) + { + G_ips_exit_count[i][j] = 0; + } + } + + g_amec->mst_ips_parms.freq_request = + G_sysConfigData.sys_mode_freq.table[OCC_MODE_MIN_FREQUENCY]; + + TRAC_INFO("amec_mst_ips_main: We have entered IPS active state! entry_count[%u] freq_request[%u]", + G_ips_entry_count, g_amec->mst_ips_parms.freq_request); + } + } + } + }while(0); +} + +uint8_t AMEC_mst_get_ips_active_status() +{ + return g_amec->mst_ips_parms.active; +} + +// Function Specification +// +// Name: amec_mst_gen_soft_freq +// +// Description: This function executes the algorithm that generates the soft +// frequency boundaries to be sent to the Slave OCCs. This is the strategy +// to be used for P8 GA1. +// +// End Function Specification +void amec_mst_gen_soft_freq(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + uint8_t i = 0; + uint16_t l_ftarget = 0; + uint16_t l_fdelta = 0; + uint16_t l_lowest_fwish = 0; + uint16_t l_highest_fwish = 0; + BOOLEAN l_need_reset = FALSE; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + do + { + // First, check if frequency delta has been enabled + if (G_mst_tunable_parameter_table_ext[7].adj_value == 0) + { + // If it hasn't been enabled, then use the full frequency range and + // break + G_mst_soft_fmin = 0x0000; + G_mst_soft_fmax = 0xFFFF; + + break; + } + + // Use the frequency delta sent by the customer + l_fdelta = (G_sysConfigData.sys_mode_freq.table[OCC_MODE_NOMINAL] * + G_mst_tunable_parameter_table_ext[8].adj_value/2) / 100; + + // Check if all OCCs are operating inside the soft frequency boundaries + for (i=0; i<MAX_OCCS; i++) + { + // Ignore OCCs that are sending zeros for factual or fwish + if ((G_dcom_slv_outbox_rx[i].factual == 0) || + (G_dcom_slv_outbox_rx[i].fwish == 0)) + { + continue; + } + + // Compare factual against the soft frequency boundaries + if ((G_dcom_slv_outbox_rx[i].factual < G_mst_soft_fmin) || + (G_dcom_slv_outbox_rx[i].factual > G_mst_soft_fmax)) + { + G_mst_violation_cnt[i]++; + } + else + { + G_mst_violation_cnt[i] = 0; + } + + // Now check if the violation count has exceeded the threshold + if (G_mst_violation_cnt[i] > 3) + { + // The new target frequency is the violator's factual + l_need_reset = TRUE; + l_ftarget = G_dcom_slv_outbox_rx[i].factual; + } + } + + // If all OCCs are inside the soft frequency boundaries, select the + // target frequeny based on the power mode + if (!l_need_reset) + { + if ((CURRENT_MODE() == OCC_MODE_DYN_POWER_SAVE) || + (CURRENT_MODE() == OCC_MODE_DYN_POWER_SAVE_FP)) + { + // For DPS and DPS-FP modes, calculate the highest Fwish sent + // by all slave OCCs + l_highest_fwish = 0; + for (i=0; i<MAX_OCCS; i++) + { + // Ignore OCCs that are sending zeros for factual or fwish + if ((G_dcom_slv_outbox_rx[i].factual == 0) || + (G_dcom_slv_outbox_rx[i].fwish == 0)) + { + continue; + } + if (G_dcom_slv_outbox_rx[i].fwish > l_highest_fwish) + { + l_highest_fwish = G_dcom_slv_outbox_rx[i].fwish; + } + } + + // If the highest Fwish is in range, go for it. Otherwise, step + // towards it in small steps + if ((l_highest_fwish >= G_mst_soft_fmin) && + (l_highest_fwish <= G_mst_soft_fmax)) + { + l_ftarget = l_highest_fwish; + } + else if (l_highest_fwish > G_mst_soft_fmax) + { + // Fwish is too high for range, step up (new target is the + // old soft Fmax) + l_ftarget = G_mst_soft_fmax; + } + else + { + // Fwish is too low for range, step down (new target is the + // old soft Fmin) + l_ftarget = G_mst_soft_fmin; + } + } + else + { + // For other system power modes, calculate the lowest Fwish sent + // by all slave OCCs + l_lowest_fwish = 0xFFFF; + for (i=0; i<MAX_OCCS; i++) + { + // Ignore OCCs that are sending zeros for factual or fwish + if ((G_dcom_slv_outbox_rx[i].factual == 0) || + (G_dcom_slv_outbox_rx[i].fwish == 0)) + { + continue; + } + if ((G_dcom_slv_outbox_rx[i].fwish < l_lowest_fwish) && + (G_dcom_slv_outbox_rx[i].fwish != 0)) + { + l_lowest_fwish = G_dcom_slv_outbox_rx[i].fwish; + } + } + + // If the lowest Fwish is in range, go for it. Otherwise, step + // towards it in small steps + if ((l_lowest_fwish >= G_mst_soft_fmin) && + (l_lowest_fwish <= G_mst_soft_fmax)) + { + l_ftarget = l_lowest_fwish; + } + else if (l_lowest_fwish > G_mst_soft_fmax) + { + // Fwish is too high for range, step up (new target is the + // old soft Fmax) + l_ftarget = G_mst_soft_fmax; + } + else + { + // Fwish is too low for range, step down (new target is the + // old soft Fmin) + l_ftarget = G_mst_soft_fmin; + } + } + } + + // Calculate the new soft frequency boundaries + G_mst_soft_fmin = l_ftarget - l_fdelta; + G_mst_soft_fmax = l_ftarget + l_fdelta; + + } while(0); +} + + +// Function Specification +// +// Name: amec_mst_cmmon_tasks_pre +// +// Description: master common tasks pre function +// +// Task Flags: +// +// End Function Specification +void amec_mst_common_tasks_pre(void) +{ + AMEC_DBG("\tAMEC Master Pre-State Common\n"); + + // ------------------------------------------------------ + // + // ------------------------------------------------------ + + + // ------------------------------------------------------ + // + // ------------------------------------------------------ +} + + +// Function Specification +// +// Name: amec_mst_cmmon_tasks_post +// +// Description: master common tasks post function +// +// End Function Specification +void amec_mst_common_tasks_post(void) +{ + AMEC_DBG("\tAMEC Master Post-State Common\n"); + + // Only execute if OCC is in the active state + if ( IS_OCC_STATE_ACTIVE() ) + { + // Call the OCC auto-slew function + amec_master_auto_slew(); + + //Call OCC pcaps mismatch function + amec_mst_check_pcaps_match(); + + //Call check under power cap function + amec_mst_check_under_pcap(); + } +} + + +// Function Specification +// +// Name: amec_mst_state_0 ~ amec_mst_state_7 +// +// Description: master state 0 ~ 7 functions +// +// End Function Specification +void amec_mst_state_0(void){AMEC_DBG("\tAMEC Master State 0\n");} +void amec_mst_state_1(void){AMEC_DBG("\tAMEC Master State 1\n");} +void amec_mst_state_2(void){AMEC_DBG("\tAMEC Master State 2\n");} +void amec_mst_state_3(void){AMEC_DBG("\tAMEC Master State 3\n");} +void amec_mst_state_4(void){AMEC_DBG("\tAMEC Master State 4\n");} + +void amec_mst_state_5(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + static uint16_t l_counter = 0; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + AMEC_DBG("\tAMEC Master State 5\n"); + + // Only execute if OCC is in the active state + if ( IS_OCC_STATE_ACTIVE() ) + { + // Increment our local counter + l_counter++; + + // Is it time to run the Idle Power Saver algorithm? (default is 3sec) + if (l_counter == AMEC_DPS_SAMPLING_RATE * AMEC_IPS_AVRG_INTERVAL) + { + l_counter = 0; + amec_mst_ips_main(); + } + } +} + +void amec_mst_state_6(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + AMEC_DBG("\tAMEC Master State 6\n"); + + if ( IS_OCC_STATE_ACTIVE() ) + { + amec_mst_gen_soft_freq(); + } +} + +void amec_mst_state_7(void){AMEC_DBG("\tAMEC Master State 7\n");} + +// Function Specification +// +// Name: amec_mst_substate_0_0 +// amec_mst_substate_0_1 +// +// Description: master substate amec_mst_substate_0_0 +// master substate amec_mst_substate_0_1 +// +// End Function Specification +void amec_mst_substate_0_0(void){AMEC_DBG("\tAMEC Master SubState 0.0\n");} +void amec_mst_substate_0_1(void){AMEC_DBG("\tAMEC Master SubState 0.1\n");} + +// Function Specification +// +// Name: amec_mst_substate_6_0 +// amec_mst_substate_6_1 +// amec_mst_substate_6_2 +// amec_mst_substate_6_3 +// amec_mst_substate_6_4 +// amec_mst_substate_6_5 +// amec_mst_substate_6_6 +// amec_mst_substate_6_7 +// +// Description: master substate amec_mst_substate_6_0 +// master substate amec_mst_substate_6_1 +// master substate amec_mst_substate_6_2 +// master substate amec_mst_substate_6_3 +// master substate amec_mst_substate_6_4 +// master substate amec_mst_substate_6_5 +// master substate amec_mst_substate_6_6 +// master substate amec_mst_substate_6_7 +// +// Flow: FN= None +// +// End Function Specification +void amec_mst_substate_6_0(void){AMEC_DBG("\tAMEC Master State 6.0\n");} +void amec_mst_substate_6_1(void){AMEC_DBG("\tAMEC Master State 6.1\n");} +void amec_mst_substate_6_2(void){AMEC_DBG("\tAMEC Master State 6.2\n");} +void amec_mst_substate_6_3(void){AMEC_DBG("\tAMEC Master State 6.3\n");} +void amec_mst_substate_6_4(void){AMEC_DBG("\tAMEC Master State 6.4\n");} +void amec_mst_substate_6_5(void){AMEC_DBG("\tAMEC Master State 6.5\n");} +void amec_mst_substate_6_6(void){AMEC_DBG("\tAMEC Master State 6.6\n");} +void amec_mst_substate_6_7(void){AMEC_DBG("\tAMEC Master State 6.7\n");} + +// Function Specification +// +// Name: amec_mst_substate_3_0 +// amec_mst_substate_3_1 +// +// Description: master substate amec_mst_substate_3_0 +// master substate amec_mst_substate_3_1 +// +// Flow: FN= None +// +// End Function Specification +void amec_mst_substate_3_0(void){AMEC_DBG("\tAMEC Master State 3.0\n");} +void amec_mst_substate_3_1(void){AMEC_DBG("\tAMEC Master State 3.1\n");} + + +// Function Specification +// +// Name: amec_mst_substate_6_1_0 +// amec_mst_substate_6_1_1 +// amec_mst_substate_6_1_2 +// amec_mst_substate_6_1_3 +// amec_mst_substate_6_1_4 +// amec_mst_substate_6_1_5 +// amec_mst_substate_6_1_6 +// amec_mst_substate_6_1_7 +// +// Description: master substate amec_mst_substate_6_1_0 +// master substate amec_mst_substate_6_1_1 +// master substate amec_mst_substate_6_1_2 +// master substate amec_mst_substate_6_1_3 +// master substate amec_mst_substate_6_1_4 +// master substate amec_mst_substate_6_1_5 +// master substate amec_mst_substate_6_1_6 +// master substate amec_mst_substate_6_1_7 +// +// Flow: FN= None +// +// End Function Specification +void amec_mst_sub_substate_6_1_0(void){AMEC_DBG("\tAMEC Master State 6.1.0\n");} +void amec_mst_sub_substate_6_1_1(void){AMEC_DBG("\tAMEC Master State 6.1.1\n");} +void amec_mst_sub_substate_6_1_2(void){AMEC_DBG("\tAMEC Master State 6.1.2\n");} +void amec_mst_sub_substate_6_1_3(void){AMEC_DBG("\tAMEC Master State 6.1.3\n");} +void amec_mst_sub_substate_6_1_4(void){AMEC_DBG("\tAMEC Master State 6.1.4\n");} +void amec_mst_sub_substate_6_1_5(void){AMEC_DBG("\tAMEC Master State 6.1.5\n");} +void amec_mst_sub_substate_6_1_6(void){AMEC_DBG("\tAMEC Master State 6.1.6\n");} +void amec_mst_sub_substate_6_1_7(void){AMEC_DBG("\tAMEC Master State 6.1.7\n");} + +/*----------------------------------------------------------------------------*/ +/* End */ +/*----------------------------------------------------------------------------*/ diff --git a/src/occ_405/amec/amec_master_smh.h b/src/occ_405/amec/amec_master_smh.h new file mode 100755 index 0000000..59563f6 --- /dev/null +++ b/src/occ_405/amec/amec_master_smh.h @@ -0,0 +1,162 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ_405/amec/amec_master_smh.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#ifndef _AMEC_MASTER_SMH_H +#define _AMEC_MASTER_SMH_H + +//************************************************************************* +// Includes +//************************************************************************* +#include <occ_common.h> +#include <ssx.h> +#include <ssx_app_cfg.h> +#include <amec_smh.h> + +//************************************************************************* +// Externs +//************************************************************************* + +//************************************************************************* +// Macros +//************************************************************************* + +//************************************************************************* +// Defines/Enums +//************************************************************************* +#define AMEC_MST_STATE() AMEC_STATE(&G_amec_mst_state); +#define AMEC_MST_SUBSTATE() AMEC_SUBSTATE(&G_amec_mst_state); +#define AMEC_MST_SUB_SUBSTATE() AMEC_SUB_SUBSTATE(&G_amec_mst_state); + +#define AMEC_MST_STATE_NEXT() AMEC_STATE_NEXT(&G_amec_mst_state); +#define AMEC_MST_SUBSTATE_NEXT() AMEC_SUBSTATE_NEXT(&G_amec_mst_state); +#define AMEC_MST_SUB_SUBSTATE_NEXT() AMEC_SUB_SUBSTATE_NEXT(&G_amec_mst_state); + +#define AMEC_MST_SET_MNFG_FMIN(a) AMEC_MST_CUR_MNFG_FMIN() = a +#define AMEC_MST_SET_MNFG_FMAX(a) AMEC_MST_CUR_MNFG_FMAX() = a +#define AMEC_MST_SET_MNFG_FSTEP(a) g_amec->mnfg_parms.fstep = a +#define AMEC_MST_SET_MNFG_DELAY(a) g_amec->mnfg_parms.delay = a +#define AMEC_MST_START_AUTO_SLEW() g_amec->mnfg_parms.auto_slew = 1 +#define AMEC_MST_STOP_AUTO_SLEW() g_amec->mnfg_parms.auto_slew = 0 +#define AMEC_MST_CUR_SLEW_COUNT() g_amec->mnfg_parms.slew_counter +#define AMEC_MST_CUR_MNFG_FMIN() g_amec->mnfg_parms.fmin +#define AMEC_MST_CUR_MNFG_FMAX() g_amec->mnfg_parms.fmax + +//************************************************************************* +// Structures +//************************************************************************* + +//************************************************************************* +// Globals +//************************************************************************* +extern const smh_tbl_t amec_mst_state_table[AMEC_SMH_STATES_PER_LVL]; +extern smh_state_t G_amec_mst_state; +extern smh_state_timing_t G_amec_mst_state_timings; + +typedef struct +{ + uint16_t active_pcap; + uint8_t pcap_valid; + uint8_t reserved; +}slave_pcap_info_t; + +extern slave_pcap_info_t G_slave_active_pcaps[MAX_OCCS]; +extern uint8_t G_pcaps_mismatch_count; +extern uint8_t G_over_cap_count; +extern uint16_t G_mst_soft_fmin; +extern uint16_t G_mst_soft_fmax; + +//************************************************************************* +// Function Prototypes +//************************************************************************* +void amec_mst_update_smh_sensors(int i_smh_state, uint32_t i_duration); + +// PRE: master common tasks +void amec_mst_common_tasks_pre(void); +// POST: master common tasks +void amec_mst_common_tasks_post(void); + +// Master auto-slewing function +void amec_master_auto_slew(void); +// OCC Power cap mismatch function +void amec_mst_check_pcaps_match(void); +// Check if under power cap function +void amex_mst_check_under_pcap(void); +// Idle Power Saver main algorithm +void amec_mst_ips_main(void); +// Get the current Idle Power Saver active status +uint8_t AMEC_mst_get_ips_active_status(); +void amec_mst_gen_soft_freq(void); + +// Master States +void amec_mst_state_0(void); +void amec_mst_state_1(void); +void amec_mst_state_2(void); +void amec_mst_state_3(void); +void amec_mst_state_4(void); +void amec_mst_state_5(void); +void amec_mst_state_6(void); +void amec_mst_state_7(void); + +// Master SubState 0 +void amec_mst_substate_0_0(void); +void amec_mst_substate_0_1(void); +void amec_mst_substate_0_4(void); +void amec_mst_substate_0_7(void); + +// Master SubState 3 +void amec_mst_substate_3_0(void); +void amec_mst_substate_3_1(void); +void amec_mst_substate_3_2(void); +void amec_mst_substate_3_6(void); +void amec_mst_substate_3_7(void); + +// Master Sub-SubState 3 +void amec_mst_sub_substate_3_0_0(void); +void amec_mst_sub_substate_3_0_1(void); +void amec_mst_sub_substate_3_0_7(void); + +// Master SubState 6 +void amec_mst_substate_6_0(void); +void amec_mst_substate_6_1(void); +void amec_mst_substate_6_2(void); +void amec_mst_substate_6_3(void); +void amec_mst_substate_6_4(void); +void amec_mst_substate_6_5(void); +void amec_mst_substate_6_6(void); +void amec_mst_substate_6_7(void); + +void amec_mst_substate_3_0(void); +void amec_mst_substate_3_1(void); + +void amec_mst_sub_substate_6_1_0(void); +void amec_mst_sub_substate_6_1_1(void); +void amec_mst_sub_substate_6_1_2(void); +void amec_mst_sub_substate_6_1_3(void); +void amec_mst_sub_substate_6_1_4(void); +void amec_mst_sub_substate_6_1_5(void); +void amec_mst_sub_substate_6_1_6(void); +void amec_mst_sub_substate_6_1_7(void); + +#endif diff --git a/src/occ_405/amec/amec_oversub.c b/src/occ_405/amec/amec_oversub.c new file mode 100755 index 0000000..beff792 --- /dev/null +++ b/src/occ_405/amec/amec_oversub.c @@ -0,0 +1,252 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ_405/amec/amec_oversub.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +//************************************************************************* +// Includes +//************************************************************************* +#include "ssx.h" +#include <occ_common.h> +#include <trac.h> +#include <amec_sys.h> +#include <proc_pstate.h> +#include <dcom.h> +#include <occ_sys_config.h> +#include <amec_service_codes.h> + +//************************************************************************* +// Externs +//************************************************************************* + +//************************************************************************* +// Macros +//************************************************************************* + +//************************************************************************* +// Defines/Enums +//************************************************************************* +#define OVERSUB_REASON_DELAY_4MS 16 // 4ms (unit is 250us) +#define OVERSUB_REASON_COUNT_TIMEOUT 2 +#define OVERSUB_REASON_DETERMINED 1 + +//************************************************************************* +// Structures +//************************************************************************* + +//************************************************************************* +// Globals +//************************************************************************* + +//************************************************************************* +// Function Prototypes +//************************************************************************* + +//************************************************************************* +// Functions +//************************************************************************* + +// Function Specification +// +// Name: amec_oversub_pmax_clip +// +// Description: Set Pmax_Clip in PMC to lowest Pstate +// +// Task Flags: +// +// End Function Specification +void amec_oversub_pmax_clip(Pstate i_pstate) +{ + pmc_rail_bounds_register_t prbr; + + // Set Pmax_Clip in PMC to lowest Pstate + prbr.value = in32(PMC_RAIL_BOUNDS_REGISTER); + prbr.fields.pmax_rail = i_pstate; + out32(PMC_RAIL_BOUNDS_REGISTER, prbr.value); +} + +// Function Specification +// +// Name: amec_oversub_isr +// +// Description: Oversubscription ISR +// +// Task Flags: +// +// Note: This function is only ever called from Critical interrupt context +// +// End Function Specification +void amec_oversub_isr(void) +{ + static uint32_t l_isr_count = 0; + static uint8_t l_polarity = SSX_IRQ_POLARITY_ACTIVE_LOW; // Default is low + uint8_t l_cur_polarity = l_polarity; + + l_isr_count++; + + // SSX_IRQ_POLARITY_ACTIVE_LOW means over-subscription is active + if(l_polarity == SSX_IRQ_POLARITY_ACTIVE_LOW) + { + // If RTL doesn't control it, do it here + if(g_amec->oversub_status.oversubLatchAmec == 0) + { + // Set PMC Pmax_clip to Pmin and throttle all Cores via OCI write to PMC + amec_oversub_pmax_clip(gpst_pmin(&G_global_pstate_table)); + + // TODO: Throttle all Centaurs via PORE-GPE by setting 'Emergency Throttle' + + g_amec->oversub_status.oversubReasonLatchCount = OVERSUB_REASON_DELAY_4MS; + } + + // Set oversubPinLive and oversubActiveTime + g_amec->oversub_status.oversubPinLive = 1; + g_amec->oversub_status.oversubActiveTime = ssx_timebase_get(); + + // Setup the IRQ + ssx_irq_setup(PGP_IRQ_EXTERNAL_TRAP, + SSX_IRQ_POLARITY_ACTIVE_HIGH, + SSX_IRQ_TRIGGER_LEVEL_SENSITIVE); + l_polarity = SSX_IRQ_POLARITY_ACTIVE_HIGH; + + } + else // over-subscription is inactive + { + // Clear oversubPinLive + g_amec->oversub_status.oversubPinLive = 0; + + // Set oversubInActiveTime + g_amec->oversub_status.oversubInactiveTime = ssx_timebase_get(); + + // Setup the IRQ + ssx_irq_setup(PGP_IRQ_EXTERNAL_TRAP, + SSX_IRQ_POLARITY_ACTIVE_LOW, + SSX_IRQ_TRIGGER_LEVEL_SENSITIVE); + + l_polarity = SSX_IRQ_POLARITY_ACTIVE_LOW; + } + + // Trace Oversub Event, Polarity and ISR count + TRAC_INFO("Oversub IRQ - Polarity (low active):%d, oversubPinLive: %d, count: %d)", l_cur_polarity, g_amec->oversub_status.oversubPinLive, l_isr_count); + +} + +// Function Specification +// +// Name: amec_oversub_check +// +// Description: Oversubscription check called in amec_slv_common_tasks_pre() +// +// Task Flags: +// +// End Function Specification +void amec_oversub_check(void) +{ + uint8_t l_cme_pin_value = 1; // low active, so set default to high + static BOOLEAN l_prev_ovs_state = FALSE; // oversub happened + + // Get CME Pin state + // No longer reading gpio from APSS in GA1 due to instability in APSS composite mode + //apss_gpio_get(l_cme_pin, &l_cme_pin_value); + + // Check CME Pin? OR CME Oversub Mnfg Active + if( (l_cme_pin_value == 0) || + (g_amec->oversub_status.cmeThrottlePinMnfg == 1) ) + { + g_amec->oversub_status.cmeThrottlePinLive = 1; + g_amec->oversub_status.cmeThrottleLatchAmec = 1; + } + else + { + // Do not clear cmeThrottleLatchAmec. + // That will only be done via the PowerCa command from TMGT. + g_amec->oversub_status.cmeThrottlePinLive = 0; + } + + // oversubscription condition happened? + if ( AMEC_INTF_GET_OVERSUBSCRIPTION() == TRUE ) + { + if ( l_prev_ovs_state != TRUE) + { + l_prev_ovs_state = TRUE; + + TRAC_ERR("Oversubscription condition happened"); + /* @ + * @errortype + * @moduleid AMEC_SLAVE_CHECK_PERFORMANCE + * @reasoncode OVERSUB_ALERT + * @userdata1 Previous OVS State + * @userdata4 ERC_AMEC_SLAVE_OVS_STATE + * @devdesc Oversubscription condition happened + */ + errlHndl_t l_errl = createErrl(AMEC_SLAVE_CHECK_PERFORMANCE,//modId + OVERSUB_ALERT, //reasoncode + ERC_AMEC_SLAVE_OVS_STATE, //Extended reason code + ERRL_SEV_INFORMATIONAL, //Severity + NULL, //Trace Buf + DEFAULT_TRACE_SIZE, //Trace Size + l_prev_ovs_state, //userdata1 + 0); //userdata2 + + // set the mfg action flag (allows callout to be added to info error) + setErrlActions(l_errl, ERRL_ACTIONS_MANUFACTURING_ERROR); + + // add the oversubscription symbolic callout + addCalloutToErrl(l_errl, + ERRL_CALLOUT_TYPE_COMPONENT_ID, + ERRL_COMPONENT_ID_OVERSUBSCRIPTION, + ERRL_CALLOUT_PRIORITY_HIGH); + + // Commit Error + commitErrl(&l_errl); + } + } + else + { + l_prev_ovs_state = FALSE; + } + + // Figure out the over-subscription reason + if(g_amec->oversub_status.oversubReasonLatchCount > 1) + { + // Try to figure out why we throttled based on APSS GPIO pins + if( g_amec->oversub_status.oversubReasonLatchCount == OVERSUB_REASON_COUNT_TIMEOUT) + { + g_amec->oversub_status.oversubReason = INDETERMINATE; + g_amec->oversub_status.oversubReasonLatchCount = OVERSUB_REASON_DETERMINED; + + TRAC_INFO("Oversub (oversubReason: %d)", g_amec->oversub_status.oversubReason ); + } + else + { + // If we can figure out why oversub happened, set oversubReason to valide enum value + // then set oversubReasonLatchCount = 1 + + // If we can't figure it out, decrease oversubReasonLatchCount and we will try again in 250us + g_amec->oversub_status.oversubReasonLatchCount--; // The unit of oversubReasonLatchCount is 250us + } + } +} + +/*----------------------------------------------------------------------------*/ +/* End */ +/*----------------------------------------------------------------------------*/ diff --git a/src/occ_405/amec/amec_oversub.h b/src/occ_405/amec/amec_oversub.h new file mode 100755 index 0000000..238599a --- /dev/null +++ b/src/occ_405/amec/amec_oversub.h @@ -0,0 +1,117 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ_405/amec/amec_oversub.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#ifndef _AMEC_OVERSUB_H +#define _AMEC_OVERSUB_H + +/*----------------------------------------------------------------------------*/ +/* Includes */ +/*----------------------------------------------------------------------------*/ +#include <occ_sys_config.h> // Added for sys config access +#include <dcom.h> + +/*----------------------------------------------------------------------------*/ +/* Constants */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Globals */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Defines */ +/*----------------------------------------------------------------------------*/ +#define AMEC_INTF_GET_OVERSUBSCRIPTION() \ +(G_sysConfigData.failsafe_enabled ? g_amec->oversub_status.cmeThrottlePinLive : \ + (g_amec->oversub_status.oversubPinLive || G_dcom_slv_inbox_rx.emulate_oversub)) + +#define AMEC_INTF_GET_OVERSUBSCRIPTION_EMULATION() g_amec->oversub_status.oversubPinMnfg + +#define AMEC_INTF_GET_FAILSAFE() \ +(G_sysConfigData.failsafe_enabled ? g_amec->oversub_status.oversubPinLive : 0) + +/*----------------------------------------------------------------------------*/ +/* Typedef / Enum */ +/*----------------------------------------------------------------------------*/ +typedef enum oversub_reason +{ + PENDING_OR_INVALID = 0x00, + FANS_FULL_SPEED = 0x01, + FAN_ERRROR = 0x02, + FAN_WARNING = 0x03, + ITE_FAILSAFE = 0x04, + + INDETERMINATE = 0xFF +}oversub_reason_t; + +typedef struct oversub_status +{ + // Way to emulate oversub for MNFG or Developers + uint32_t oversubPinMnfg : 1; + + // Live Status of oversub Pin + uint32_t oversubPinLive : 1; + + // AMEC status of oversub pin, so it doesn't + // change mid-RTL + uint32_t oversubLatchAmec : 1; + + // Used for SRC logging of performance loss, + // need to have countdown b/c we don't get + // APSS gpio signals as quick as we get + // oversub. + uint8_t oversubReasonLatchCount; + oversub_reason_t oversubReason; + + // For debug, tracks time oversub last went inactive + SsxTimebase oversubInactiveTime; + + // For debug, tracks time oversub last went active + SsxTimebase oversubActiveTime; + + // Live status of CME throttle pin, doesn't change mid-RTL + uint32_t cmeThrottlePinLive :1; + + // Status of CME Throttle, doesn't get cleared until MM/TMGT + // tells us to clear it/un-throttle. + uint32_t cmeThrottleLatchAmec :1; + + // Way to emulate cmeThrottle for MNFG or Developers + uint32_t cmeThrottlePinMnfg :1; +}oversub_status_t; + +/*----------------------------------------------------------------------------*/ +/* Function Prototypes */ +/*----------------------------------------------------------------------------*/ + +void amec_oversub_isr(void); + +void amec_oversub_check(void); + +void amec_oversub_pmax_clip(Pstate i_pstate); + +bool apss_gpio_get(uint8_t i_pin_number, uint8_t *o_pin_value); + +#endif //_AMEC_OVERSUB_H diff --git a/src/occ_405/amec/amec_parm.c b/src/occ_405/amec/amec_parm.c new file mode 100755 index 0000000..a3dafd6 --- /dev/null +++ b/src/occ_405/amec/amec_parm.c @@ -0,0 +1,306 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ_405/amec/amec_parm.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +//************************************************************************* +// Includes +//************************************************************************* +#include <common_types.h> +#include <amec_parm.h> +#include <string.h> +#include <stdlib.h> +#include <occ_common.h> +#include <amec_amester.h> + +//************************************************************************* +// Externs +//************************************************************************* + +//************************************************************************* +// Defines/Enums +//************************************************************************* + +//************************************************************************* +// Globals +//************************************************************************* + +///Array that maintains a list of all parameters built +extern amec_parm_t g_amec_parm_list[]; + +//************************************************************************* +// Function Declarations +//************************************************************************* + +//************************************************************************* +// Functions +//************************************************************************* + +void amec_parm_get_number(const IPMIMsg_t *i_psMsg, + UINT8 *o_pu8Resp, + UINT16 *o_pu16RespLength, + UINT8 *o_retval) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + o_pu8Resp[0] = (UINT8)(AMEC_PARM_NUMBER_OF_PARAMETERS>>8); + o_pu8Resp[1] = (UINT8)(AMEC_PARM_NUMBER_OF_PARAMETERS); + + *o_pu16RespLength=2; + *o_retval=COMPCODE_NORMAL; + + return; +} + + +void amec_parm_get_config(const IPMIMsg_t *i_psMsg, + UINT8 *o_pu8Resp, + UINT16 *o_pu16RespLength, + UINT8 *o_retval) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + AMEC_PARM_GUID l_id; // parameter id + UINT16 l_j; // index into return message + UINT16 l_length = 0; // response length + CHAR *l_src; //pointer for copying name + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + l_id = (AMEC_PARM_GUID) CONVERT_UINT8_ARRAY_UINT16( + i_psMsg->au8CmdData_ptr[1], + i_psMsg->au8CmdData_ptr[2]); + l_j = 0; // write index byte for response + + for (; l_id < AMEC_PARM_NUMBER_OF_PARAMETERS; l_id++) + { + if (l_j + strlen(g_amec_parm_list[l_id].name) + 1 + 10 >= IPMI_MAX_MSG_SIZE) + { + // +1 = null terminator in name. + // +10 = type, mode, vector_length, length (optional) + break; // hit end of response buffer + } + + // Copy name into output buffer + l_src = g_amec_parm_list[l_id].name; + do + { + o_pu8Resp[l_j++] = *l_src; + } while (*l_src++ != 0); /* copy string until \0 */ + + o_pu8Resp[l_j++] = (UINT8)(g_amec_parm_list[l_id].type); + o_pu8Resp[l_j++] = (UINT8)(g_amec_parm_list[l_id].mode); + o_pu8Resp[l_j++] = (UINT8)(g_amec_parm_list[l_id].vector_length>>24); + o_pu8Resp[l_j++] = (UINT8)(g_amec_parm_list[l_id].vector_length>>16); + o_pu8Resp[l_j++] = (UINT8)(g_amec_parm_list[l_id].vector_length>>8); + o_pu8Resp[l_j++] = (UINT8)(g_amec_parm_list[l_id].vector_length); + + // If base type is unstructured data or string, send length + if (g_amec_parm_list[l_id].type == AMEC_PARM_TYPE_STRING || + g_amec_parm_list[l_id].type == AMEC_PARM_TYPE_RAW) + { + o_pu8Resp[l_j++] = (UINT8)(g_amec_parm_list[l_id].length>>24); + o_pu8Resp[l_j++] = (UINT8)(g_amec_parm_list[l_id].length>>16); + o_pu8Resp[l_j++] = (UINT8)(g_amec_parm_list[l_id].length>>8); + o_pu8Resp[l_j++] = (UINT8)(g_amec_parm_list[l_id].length); + } + + // update length of response parameter just copied + l_length = l_j; + } + *o_pu16RespLength=l_length; + *o_retval=COMPCODE_NORMAL; + return; +} + + +void amec_parm_read(const IPMIMsg_t *const i_psMsg, + UINT8 *const o_pu8Resp, + UINT16 *const o_pu16RespLength, + UINT8 *const o_retval) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + AMEC_PARM_GUID l_id; + UINT16 i=0; // output index + UINT16 l_maxresponse = IPMI_MAX_MSG_SIZE - 1; // -1 since return code is 1B + UINT8 *l_src_ptr; // pointer to first byte of data + UINT8 *l_end_ptr; // mark end of data + UINT32 b; // start byte + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + do + { + *o_retval = COMPCODE_NORMAL; /* assume no error */ + + // Parse input command + // Get the byte offset + b = CONVERT_UINT8_ARRAY_UINT32( + i_psMsg->au8CmdData_ptr[1], + i_psMsg->au8CmdData_ptr[2], + i_psMsg->au8CmdData_ptr[3], + i_psMsg->au8CmdData_ptr[4]); + + // Get parameter id + l_id = CONVERT_UINT8_ARRAY_UINT16( + i_psMsg->au8CmdData_ptr[5], + i_psMsg->au8CmdData_ptr[6]); + + if (l_id >= AMEC_PARM_NUMBER_OF_PARAMETERS) + { + *o_retval = COMPCODE_PARAM_OUT_OF_RANGE; + *o_pu16RespLength = 0; + break; + } + + if (g_amec_parm_list[l_id].preread) + { + amec_parm_preread(l_id); + } + + // Copy value to output buffer + // Set src to first byte to send back + l_src_ptr = g_amec_parm_list[l_id].value_ptr + b; + + // Set end pointer 1 beyond last byte to send. It is limited either + // on the value size, or the IPMI message size. + l_end_ptr = g_amec_parm_list[l_id].value_ptr + + (g_amec_parm_list[l_id].vector_length * g_amec_parm_list[l_id].length); + if (l_src_ptr + l_maxresponse < l_end_ptr) + { + l_end_ptr = l_src_ptr + l_maxresponse; + } + + while ((UINT32)l_src_ptr < (UINT32)l_end_ptr) + { + //Copy next byte to output + o_pu8Resp[i++] = (UINT8)*l_src_ptr++; + } + + *o_pu16RespLength = i; + + } while (FALSE); +} + + +void amec_parm_write(const IPMIMsg_t *const i_psMsg, + UINT8 *const o_pu8Resp, + UINT16 *const o_pu16RespLength, + UINT8 *const o_retval) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + AMEC_PARM_GUID l_id; + UINT16 i=0; // output index + UINT8 *l_dest_ptr = NULL; // pointer to first byte of data + UINT8 *l_start_ptr; // mark end of data + UINT8 *l_end_ptr = NULL; // mark end of data + UINT32 b; // start byte + UINT32 l_bytes = 0; // number of bytes written + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + do + { + *o_retval = COMPCODE_NORMAL; /* assume no error */ + + // Parse input command + // Get parameter id + l_id = CONVERT_UINT8_ARRAY_UINT16( + i_psMsg->au8CmdData_ptr[1], + i_psMsg->au8CmdData_ptr[2]); + + // Get the starting byte of element + b = CONVERT_UINT8_ARRAY_UINT32( + i_psMsg->au8CmdData_ptr[3], + i_psMsg->au8CmdData_ptr[4], + i_psMsg->au8CmdData_ptr[5], + i_psMsg->au8CmdData_ptr[6]); + + if (l_id >= AMEC_PARM_NUMBER_OF_PARAMETERS) + { + *o_retval = COMPCODE_PARAM_OUT_OF_RANGE; + *o_pu16RespLength = 0; + break; + } + + i = 7; // start of data to write in input buffer + + // Check if read-only + if (g_amec_parm_list[l_id].mode & AMEC_PARM_MODE_READONLY) + { + *o_retval = COMPCODE_WRONG_PRIV; + *o_pu16RespLength = 0; + break; + } + + l_start_ptr = g_amec_parm_list[l_id].value_ptr + b; + l_dest_ptr = l_start_ptr; + // Set end pointer 1 beyond last byte to send. It is limited either + // on the value size, or the IPMI message size. + l_end_ptr = g_amec_parm_list[l_id].value_ptr + + (g_amec_parm_list[l_id].vector_length * g_amec_parm_list[l_id].length); + + // Copy value from input buffer + while ((UINT32)l_dest_ptr < (UINT32)l_end_ptr + && i < i_psMsg->u8CmdDataLen) + { + *l_dest_ptr++ = i_psMsg->au8CmdData_ptr[i++]; + } + + l_bytes = l_dest_ptr - l_start_ptr; + + // Return number of bytes written + *o_pu16RespLength = 4; + o_pu8Resp[0] = (UINT8)(l_bytes >> 24); + o_pu8Resp[1] = (UINT8)(l_bytes >> 16); + o_pu8Resp[2] = (UINT8)(l_bytes >> 8); + o_pu8Resp[3] = (UINT8)(l_bytes); + + // Run post-write routine only if last byte of parameter was written + // Some long parameters require multiple write calls due to IPMI + // message limits, so we only call the postwrite routine when the + // last byte of the parameter is written. + if (l_dest_ptr == l_end_ptr && g_amec_parm_list[l_id].postwrite) + { + amec_parm_postwrite(l_id); + } + + } while (FALSE); +} + +/*----------------------------------------------------------------------------*/ +/* End */ +/*----------------------------------------------------------------------------*/ diff --git a/src/occ_405/amec/amec_parm.h b/src/occ_405/amec/amec_parm.h new file mode 100755 index 0000000..83104f9 --- /dev/null +++ b/src/occ_405/amec/amec_parm.h @@ -0,0 +1,181 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ_405/amec/amec_parm.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/* + This interface takes named memory objects (such as global variables) + and makes them accessible to Amester parameter for reading and + writing. Any memory location may become an Amester parameter. + + To add a parameter, + 1. Add a new parameter id number in the AMEC_PARM_ENUM below. + 2. In the same position, add the parameter to g_amec_parm_list in + amec_parm_table.c + There are macros that help in adding a parameter to the table. + The macro typically takes a) the parameter id, b) a string name, + and c) a pointer to the memory location. +*/ + +#ifndef _AMEC_PARM_H +#define _AMEC_PARM_H + +//************************************************************************* +// Includes +//************************************************************************* +#include <amec_amester.h> + +//************************************************************************* +// Defines/Enums +//************************************************************************* + +// List of all parameters +// NOTE: The parameters must be in the same order as g_amec_parm_list[] in +// amec_parm_table.c +typedef enum +{ + PARM_SYS_FMAX, + PARM_SYS_FMIN, + PARM_GPST, + PARM_PSTATE_MHZ, + PARM_FREQ_REASON, + PARM_FREQ_OR, + PARM_FREQ_OR_EN, + PARM_SYS_THRM_SP, + PARM_SYS_THRM_GAIN, + PARM_SYS_THRM_RES, + PARM_SYS_THRM_SPEED, + PARM_SYS_THRM_FREQ, + PARM_SOFT_FMIN, + PARM_SOFT_FMAX, + PARM_TOD, + AMEC_PARM_NUMBER_OF_PARAMETERS +} AMEC_PARM_ENUM; + +typedef enum +{ + AMEC_PARM_TYPE_UINT8 = 0, + AMEC_PARM_TYPE_UINT16, + AMEC_PARM_TYPE_UINT32, + AMEC_PARM_TYPE_UINT64, + AMEC_PARM_TYPE_INT8, + AMEC_PARM_TYPE_INT16, + AMEC_PARM_TYPE_INT32, + AMEC_PARM_TYPE_INT64, + AMEC_PARM_TYPE_STRING, + AMEC_PARM_TYPE_RAW + +} AMEC_PARM_TYPE_ENUM; + +#define AMEC_PARM_MODE_NORMAL (0x00) +#define AMEC_PARM_MODE_READONLY (0x01) + +// Length includes null byte terminator. 15 readable characters are allowed. +#define AMEC_PARM_NAME_LENGTH 16 + +typedef struct amec_parm_s +{ + /// name of parameter + CHAR name[AMEC_PARM_NAME_LENGTH]; + /// value_ptr: pointer to data + UINT8 *value_ptr; + /// number of bytes in base data + UINT32 length; + /// vector_length is the number of items in the array pointed by value ptr. + UINT32 vector_length; + /// Type of data + UINT8 type : 4; + /// Mode of data (read-write, read-only, etc.) + UINT8 mode : 1; + /// If preread is 1, call amec_parm_preread(GUID) before reading parameter value. + UINT8 preread : 1; + /// If postwrite is 1, call amec_parm_postwrite(GUID) before reading parameter value. + UINT8 postwrite : 1; +} amec_parm_t; + +typedef UINT16 AMEC_PARM_GUID; + +extern amec_parm_t g_amec_parm_list[]; + +/*******************************************************************/ +/* Function Definitions */ +/*******************************************************************/ + +/** + * Get number of parameters tracked by OCC + * + */ +void amec_parm_get_number(const IPMIMsg_t *i_psMsg, + UINT8 *o_pu8Resp, + UINT16 *o_pu16RespLength, + UINT8 *o_retval); + +/** + * Get parameter configuration (names, types, size, etc.) + * + */ +void amec_parm_get_config(const IPMIMsg_t *i_psMsg, + UINT8 *o_pu8Resp, + UINT16 *o_pu16RespLength, + UINT8 *o_retval); + +/** + * Read a parameter value + * + */ +void amec_parm_read(const IPMIMsg_t *const i_psMsg, + UINT8 *const o_pu8Resp, + UINT16 *const o_pu16RespLength, + UINT8 *const o_retval); + +/** + * Write a value to a parameter + * + */ +void amec_parm_write(const IPMIMsg_t *const i_psMsg, + UINT8 *const o_pu8Resp, + UINT16 *const o_pu16RespLength, + UINT8 *const o_retval); + +/** + * Update parameter value before reading + * + * Some parameters need to be updated before reading. + * For example, a parameter that points to double-buffered + * that may be at a new memory location each time the + * parameter is examined. + * This routine only needs to be called when the parameter + * has a 'preread' field with a value of 1. + */ +void amec_parm_preread(AMEC_PARM_GUID i_parm_guid); + +/** + * Update parameter value after writing + * + * Some parameters trigger actions after writing. + * This routine only needs to be called when the parameter + * has been written and has a 'postwrite' field with a value of 1. + */ +void amec_parm_postwrite(AMEC_PARM_GUID i_parm_guid); + +#endif diff --git a/src/occ_405/amec/amec_parm_table.c b/src/occ_405/amec/amec_parm_table.c new file mode 100755 index 0000000..c485484 --- /dev/null +++ b/src/occ_405/amec/amec_parm_table.c @@ -0,0 +1,168 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ_405/amec/amec_parm_table.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +//************************************************************************* +// Includes +//************************************************************************* +#include <occ_common.h> //STATIC_ASSERT macro +#include <amec_parm.h> +#include <amec_sys.h> +#include <proc_pstate.h> //global pstate table parameter + +//************************************************************************* +// Externs +//************************************************************************* + +//************************************************************************* +// Macros +//************************************************************************* + +// Macros for parameters based on simple variables (read-write, not vectors) +/** + * Create or update a parameter + * + * i_name: Name of parameter. If this name is the name of an existing + * parameter, then the existing parameter fields will be updated + * i_value: A pointer to the bytes representing the value of the parameter + * i_length: The length in bytes of the parameter value + * n: The number of elements if this parameter is a vector, otherwise = 1. + */ +#define AMEC_PARM_UINT8(i_num, i_name, i_value) \ + [i_num] = {i_name,(void*)i_value,1,1,AMEC_PARM_TYPE_UINT8,0} +#define AMEC_PARM_UINT16(i_num, i_name, i_value) \ + [i_num] = {i_name,(void*)i_value,2,1,AMEC_PARM_TYPE_UINT16,0} +#define AMEC_PARM_UINT32(i_num, i_name, i_value) \ + [i_num] = {i_name,(void*)i_value,4,1,AMEC_PARM_TYPE_UINT32,0} +#define AMEC_PARM_UINT64(i_num, i_name, i_value) \ + [i_num] = {i_name,(void*)i_value,8,1,AMEC_PARM_TYPE_UINT64,0} +#define AMEC_PARM_INT8(i_num, i_name, i_value) \ + [i_num] = {i_name,(void*)i_value,1,1,AMEC_PARM_TYPE_INT8,0} +#define AMEC_PARM_INT16(i_num, i_name, i_value) \ + [i_num] = {i_name,(void*)i_value,2,1,AMEC_PARM_TYPE_INT16,0} +#define AMEC_PARM_INT32(i_num, i_name, i_value) \ + [i_num] = {i_name,(void*)i_value,4,1,AMEC_PARM_TYPE_INT32,0} +#define AMEC_PARM_INT64(i_num, i_name, i_value) \ + [i_num] = {i_name,(void*)i_value,8,1,AMEC_PARM_TYPE_IN64,0} +#define AMEC_PARM_STRING(i_num, i_name, i_value, i_length) \ + [i_num] = {i_name,(void*)i_value,i_length,1,AMEC_PARM_TYPE_STRING,0} +#define AMEC_PARM_RAW(i_num, i_name, i_value, i_length) \ + [i_num] = {i_name,(void*)i_value,i_length,1,AMEC_PARM_TYPE_RAW,0} + +//Use these macros when the parameter is an array of values. +#define AMEC_PARM_UINT8_ARRAY(i_num, i_name, i_value, n) \ + [i_num] = {i_name,(void*)i_value,1,n,AMEC_PARM_TYPE_UINT8,0} +#define AMEC_PARM_UINT16_ARRAY(i_num, i_name, i_value, n) \ + [i_num] = {i_name,(void*)i_value,2,n,AMEC_PARM_TYPE_UINT16,0} +#define AMEC_PARM_UINT32_ARRAY(i_num, i_name, i_value, n) \ + [i_num] = {i_name,(void*)i_value,4,n,AMEC_PARM_TYPE_UINT32,0} +#define AMEC_PARM_UINT64_ARRAY(i_num, i_name, i_value, n) \ + [i_num] = {i_name,(void*)i_value,8,n,AMEC_PARM_TYPE_UINT64,0} +#define AMEC_PARM_INT8_ARRAY(i_num, i_name, i_value, n) \ + [i_num] = {i_name,(void*)i_value,1,n,AMEC_PARM_TYPE_INT8,0} +#define AMEC_PARM_INT16_ARRAY(i_num, i_name, i_value, n) \ + [i_num] = {i_name,(void*)i_value,2,n,AMEC_PARM_TYPE_INT16,0} +#define AMEC_PARM_INT32_ARRAY(i_num, i_name, i_value, n) \ + [i_num] = {i_name,(void*)i_value,4,n,AMEC_PARM_TYPE_INT32,0} +#define AMEC_PARM_INT64_ARRAY(i_num, i_name, i_value, n) \ + [i_num] = {i_name,(void*)i_value,8,n,AMEC_PARM_TYPE_IN64,0} +#define AMEC_PARM_STRING_ARRAY(i_num, i_name, i_value, i_length, n) \ + [i_num] = {i_name,(void*)i_value,i_length,n,AMEC_PARM_TYPE_STRING,0} +#define AMEC_PARM_RAW_ARRAY(i_num, i_name, i_value, i_length, n) \ + [i_num] = {i_name,(void*)i_value,i_length,n,AMEC_PARM_TYPE_RAW,0} + +//************************************************************************* +// Defines/Enums +//************************************************************************* + +//************************************************************************* +// Structures +//************************************************************************* + +extern amec_sys_t g_amec_sys; + +//************************************************************************* +// Globals +//************************************************************************* + +// This is the list of all parameters seen by Amester +// +// Note: The parameters must be in the same order as in AMEC_PARM_ENUM +// in amec_parm.h +// +// Future optimization: This table could be placed in main memory, not +// the SRAM tank, since slow access to it is OK. +amec_parm_t g_amec_parm_list[] = { + // System fmin and fmax + AMEC_PARM_UINT16(PARM_SYS_FMAX,"sys_fmax",&g_amec_sys.sys.fmax), + AMEC_PARM_UINT16(PARM_SYS_FMIN,"sys_fmin",&g_amec_sys.sys.fmin), + + // Global Pstate table + AMEC_PARM_RAW(PARM_GPST,"gpst",&G_global_pstate_table,sizeof(GlobalPstateTable)), + // MHz per pstate + AMEC_PARM_UINT32(PARM_PSTATE_MHZ,"pstate_mhz",&G_mhz_per_pstate), + // frequency reason code per-core + AMEC_PARM_UINT32_ARRAY(PARM_FREQ_REASON,"freq_reason",g_amec_sys.proc[0].parm_f_reason,MAX_NUM_CORES), + // frequency override speed in MHz per-core + AMEC_PARM_UINT16_ARRAY(PARM_FREQ_OR,"freq_or",g_amec_sys.proc[0].parm_f_override,MAX_NUM_CORES), + // frequency override enable bit (1=active) + AMEC_PARM_UINT8(PARM_FREQ_OR_EN,"freq_or_en",&g_amec_sys.proc[0].parm_f_override_enable), + + // Thermal controller parameters + AMEC_PARM_UINT16(PARM_SYS_THRM_SP,"sys_thrm_sp",&g_amec_sys.thermalproc.setpoint), + AMEC_PARM_UINT16(PARM_SYS_THRM_GAIN,"sys_thrm_gain",&g_amec_sys.thermalproc.Pgain), + AMEC_PARM_UINT16(PARM_SYS_THRM_RES,"sys_thrm_res",&g_amec_sys.thermalproc.total_res), + AMEC_PARM_UINT16(PARM_SYS_THRM_SPEED,"sys_thrm_speed",&g_amec_sys.thermalproc.speed_request), + AMEC_PARM_UINT16(PARM_SYS_THRM_FREQ,"sys_thrm_freq",&g_amec_sys.thermalproc.freq_request), + + // Partition related parameters + AMEC_PARM_UINT16(PARM_SOFT_FMIN,"part_soft_fmin",&g_amec_sys.part_config.part_list[0].soft_fmin), + AMEC_PARM_UINT16(PARM_SOFT_FMAX,"part_soft_fmax",&g_amec_sys.part_config.part_list[0].soft_fmax), + AMEC_PARM_RAW(PARM_TOD,"apss_tod",&G_dcom_slv_inbox_doorbell_rx.tod,8), +}; + +//Throw a compiler error when the enum and array are not both updated +STATIC_ASSERT((AMEC_PARM_NUMBER_OF_PARAMETERS != (sizeof(g_amec_parm_list)/sizeof(amec_parm_t)))); + +void amec_parm_preread(AMEC_PARM_GUID i_parm_guid) +{ + switch (i_parm_guid) + { + default: + break; + } +} + +void amec_parm_postwrite(AMEC_PARM_GUID i_parm_guid) +{ + switch (i_parm_guid) + { + default: + break; + } +} + +/*----------------------------------------------------------------------------*/ +/* End */ +/*----------------------------------------------------------------------------*/ diff --git a/src/occ_405/amec/amec_part.c b/src/occ_405/amec/amec_part.c new file mode 100755 index 0000000..86f1b7e --- /dev/null +++ b/src/occ_405/amec/amec_part.c @@ -0,0 +1,468 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ_405/amec/amec_part.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/*----------------------------------------------------------------------------*/ +/* Includes */ +/*----------------------------------------------------------------------------*/ +#include <common_types.h> +#include <mode.h> +#include <amec_sys.h> +#include <amec_part.h> +#include <trac.h> +#include <dcom.h> + +/*----------------------------------------------------------------------------*/ +/* Constants */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Globals */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Typedef / Enum */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Code */ +/*----------------------------------------------------------------------------*/ + +// Given partition id, return pointer to existing partition or NULL +static amec_part_t* amec_part_find_by_id(amec_part_config_t* i_config, + const uint8_t i_id) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + amec_part_t *l_part = NULL; + uint16_t l_idx = 0; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + for (l_idx=0; l_idx<AMEC_PART_MAX_PART; l_idx++) + { + if (i_config->part_list[l_idx].id == i_id && i_config->part_list[l_idx].valid) + { + l_part = &(i_config->part_list[l_idx]); + break; + } + } + + return l_part; +} + +// Function Specification +// +// Name: amec_isr_speed_to_freq +// +// Description: Given a core, return a valid partition that owns it, or NULL. +// +// End Function Specification +amec_part_t* amec_part_find_by_core(amec_part_config_t* i_config, + const uint16_t i_core_index) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + amec_part_t *l_part = NULL; + uint16_t l_part_index = 0; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + do + { + if (i_core_index >= MAX_NUM_CORES) + { + break; // illegal core number + } + l_part_index = i_config->core2part[i_core_index]; + if (l_part_index >= AMEC_PART_MAX_PART) + { + break; // illegal partition number + } + if (i_config->part_list[l_part_index].valid) + { + l_part = (amec_part_t*) &(i_config->part_list[l_part_index]); + } + } while(0); + + return l_part; +} + +// Function Specification +// +// Name: amec_part_add +// +// Description: Add a core group. +// +// End Function Specification +void amec_part_add(uint8_t i_id) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + amec_part_t *l_part; + uint16_t l_idx = 0; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + do + { + l_part = (amec_part_t*) amec_part_find_by_id(&g_amec->part_config, + i_id); + if (l_part != NULL) + { + break; // Partition already exists + } + + // Find new slot + for (l_idx=0; l_idx<AMEC_PART_MAX_PART; l_idx++) + { + if (!g_amec->part_config.part_list[l_idx].valid) + { + l_part = &g_amec->part_config.part_list[l_idx]; + break; + } + } + + if (l_part == NULL) + { + // This should never happen, since table should be large enough + // to hold as many partitions as can be created on system. + break; + } + + // Enter new partition into table with default values + l_part->id = i_id; + l_part->valid = 1; //Mark them as valid + l_part->ncores = 0; //No cores + for (l_idx=0; l_idx<AMEC_PART_NUM_CORES; l_idx++) + { + l_part->core_list[l_idx] = AMEC_PART_NUM_CORES; + } + + // For now, create a single partition with all cores assigned to it + // until we write the interface to talk to PHYP + if (i_id == 0) + { + l_part->ncores = MAX_NUM_CORES; + for (l_idx=0; l_idx<MAX_NUM_CORES; l_idx++) + { + l_part->core_list[l_idx] = l_idx; + } + } + + // Set default soft frequency boundaries to use full frequency range + l_part->soft_fmin = 0x0000; + l_part->soft_fmax = 0xFFFF; + + // Set default values for DPS parameters (Favor Energy) + l_part->dpsalg.step_up = 8; + l_part->dpsalg.step_down = 8; + l_part->dpsalg.tlutil = 9800; + l_part->dpsalg.sample_count_util = 16; + l_part->dpsalg.epsilon_perc = 1800; + l_part->dpsalg.alpha_up = 9800; + l_part->dpsalg.alpha_down = 9800; + l_part->dpsalg.type = 0; //No algorithm is selected yet + l_part->dpsalg.freq_request = UINT16_MAX; + + l_part->es_policy = OCC_INTERNAL_MODE_MAX_NUM; + l_part->follow_sysmode = TRUE; + } + while (0); + + return; +} + +void amec_part_init() +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + uint16_t l_idx = 0; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + // Initial core to partition mapping. Cores point to an invalid core group + // id. + // For now, we assume a single partition (id 0) with all cores assigned to + // it. Once the PHYP interface has been written, we can remove this + // assumption. + for (l_idx=0; l_idx<MAX_NUM_CORES; l_idx++) + { + g_amec->part_config.core2part[l_idx] = 0; // AMEC_PART_INVALID_ID; + } + + for (l_idx=0; l_idx<AMEC_PART_MAX_PART; l_idx++) + { + // Creating all possible partitions + amec_part_add(l_idx); + + g_amec->part_config.part_list[l_idx].dpsalg.util_speed_request = g_amec->sys.fmax; + } +} + +void amec_part_update_dps_parameters(amec_part_t* io_part) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + // If partition doesn't exist, just exit + if (io_part == NULL) + { + return; + } + + // Check to see if parameter values have been overwritten by user + if (g_amec->slv_dps_param_overwrite == TRUE) + { + // Then, exit right away + return; + } + + // By default, let's use the DPS-Favor Energy settings + io_part->dpsalg.step_up = 8; + io_part->dpsalg.step_down = 8; + io_part->dpsalg.tlutil = 9800; + io_part->dpsalg.sample_count_util = 16; + io_part->dpsalg.epsilon_perc = 1800; + io_part->dpsalg.alpha_up = 9800; + io_part->dpsalg.alpha_down = 9800; + io_part->dpsalg.type = 41; + + // If core group policy is DPS-Favor Performance, then write those settings + if (io_part->es_policy == OCC_INTERNAL_MODE_DPS_MP) + { + io_part->dpsalg.step_up = 1000; + io_part->dpsalg.step_down = 8; + io_part->dpsalg.tlutil = 1000; + io_part->dpsalg.sample_count_util = 1; + io_part->dpsalg.epsilon_perc = 0; + io_part->dpsalg.alpha_up = 9990; + io_part->dpsalg.alpha_down = 9990; + io_part->dpsalg.type = 41; + + // Check if this is a multi-node system + if (G_sysConfigData.system_type.single == 0) + { + // These parameter values will result in static turbo frequency + io_part->dpsalg.alpha_down = 0; + io_part->dpsalg.tlutil = 0; + + TRAC_IMP("Multi-node system found! Updating DPS-FP parameters: single_node_bit[%u] alpha_down[%u] tlutil[%u]", + G_sysConfigData.system_type.single, + io_part->dpsalg.alpha_down, + io_part->dpsalg.tlutil); + } + } +} + +void amec_part_update_perf_settings(amec_part_t* io_part) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + uint16_t j = 0; + uint16_t l_core_index = 0; + amec_core_perf_counter_t* l_perf; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + // If partition doesn't exist, just exit + if (io_part == NULL) + { + return; + } + + switch (io_part->es_policy) + { + case OCC_INTERNAL_MODE_DPS: + case OCC_INTERNAL_MODE_DPS_MP: + // These policies require that we reset the internal performance + // settings of the cores in the partition + for (j=0; j<io_part->ncores; j++) + { + l_core_index = io_part->core_list[j]; + l_perf = &g_amec->proc[0].core[l_core_index % MAX_NUM_CORES].core_perf; + + memset(l_perf->ptr_util_slack_avg_buffer, 0, 2*MAX_UTIL_SLACK_AVG_LEN); + memset(l_perf->ptr_util_active_avg_buffer, 0, 2*MAX_UTIL_SLACK_AVG_LEN); + l_perf->util_active_core_counter = 0; + l_perf->util_slack_core_counter = 0; + l_perf->util_slack_accumulator = 0; + l_perf->util_active_accumulator = 0; + l_perf->ptr_putUtilslack = 0; + } + break; + + default: + // For all other policies, reset the DPS frequency request + for (j=0; j<io_part->ncores; j++) + { + l_core_index = io_part->core_list[j]; + g_amec->proc[0].core[l_core_index % MAX_NUM_CORES] + .core_perf.dps_freq_request = UINT16_MAX; + } + break; + } +} + +void AMEC_part_update_sysmode_policy(OCC_MODE i_occ_internal_mode) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + uint16_t i = 0; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + for (i=0; i<AMEC_PART_MAX_PART; i++) + { + // If a core group is not valid, skip it + if (!g_amec->part_config.part_list[i].valid) + { + continue; + } + // If a core group has no cores assigned, skip it + if (g_amec->part_config.part_list[i].ncores == 0) + { + continue; + } + + // If a core group is not following the system power mode, skip it + if (!g_amec->part_config.part_list[i].follow_sysmode) + { + continue; + } + + // Set power mode for this core group to the system power mode + switch (i_occ_internal_mode) + { + case OCC_MODE_NOMINAL: + g_amec->part_config.part_list[i].es_policy = OCC_INTERNAL_MODE_NOM; + break; + + case OCC_MODE_DYN_POWER_SAVE: + g_amec->part_config.part_list[i].es_policy = OCC_INTERNAL_MODE_DPS; + break; + + case OCC_MODE_DYN_POWER_SAVE_FP: + g_amec->part_config.part_list[i].es_policy = OCC_INTERNAL_MODE_DPS_MP; + break; + + default: + g_amec->part_config.part_list[i].es_policy = OCC_INTERNAL_MODE_UNDEFINED; + break; + } + + // Update the DPS parameters based on the power policy + if ((g_amec->part_config.part_list[i].es_policy == OCC_INTERNAL_MODE_DPS) || + (g_amec->part_config.part_list[i].es_policy == OCC_INTERNAL_MODE_DPS_MP)) + { + amec_part_update_dps_parameters(&(g_amec->part_config.part_list[i])); + } + + // Update internal performance settings for this partition + amec_part_update_perf_settings(&(g_amec->part_config.part_list[i])); + + // Useful trace for debug + //TRAC_INFO("AMEC_part_update_sysmode_policy: Core_group[%u] Num_cores[%u] ES_policy[0x%02x]", + // i, g_amec->part_config.part_list[i].ncores, + // g_amec->part_config.part_list[i].es_policy); + } +} + +void AMEC_part_overwrite_dps_parameters(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + uint16_t i = 0; + uint16_t j = 0; + uint16_t l_core_index = 0; + amec_part_t* l_part = NULL; + amec_core_perf_counter_t* l_perf = NULL; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + for (i=0; i<AMEC_PART_MAX_PART; i++) + { + // If a core group is not valid, skip it + if (!g_amec->part_config.part_list[i].valid) + { + continue; + } + // If a core group has no cores assigned, skip it + if (g_amec->part_config.part_list[i].ncores == 0) + { + continue; + } + + // Overwrite the DPS parameters based on values sent by Master OCC + l_part = &(g_amec->part_config.part_list[i]); + l_part->dpsalg.alpha_up = G_dcom_slv_inbox_rx.alpha_up; + l_part->dpsalg.alpha_down = G_dcom_slv_inbox_rx.alpha_down; + l_part->dpsalg.sample_count_util = G_dcom_slv_inbox_rx.sample_count_util; + l_part->dpsalg.step_up = G_dcom_slv_inbox_rx.step_up; + l_part->dpsalg.step_down = G_dcom_slv_inbox_rx.step_down; + l_part->dpsalg.epsilon_perc = G_dcom_slv_inbox_rx.epsilon_perc; + l_part->dpsalg.tlutil = G_dcom_slv_inbox_rx.tlutil; + // The algorithm type cannot be selected by customer, hardcoded here. + l_part->dpsalg.type = 41; + + // Update internal performance settings for each core on this partition + for (j=0; j<l_part->ncores; j++) + { + l_core_index = l_part->core_list[j]; + l_perf = &g_amec->proc[0].core[l_core_index % MAX_NUM_CORES].core_perf; + + memset(l_perf->ptr_util_slack_avg_buffer, 0, 2*MAX_UTIL_SLACK_AVG_LEN); + memset(l_perf->ptr_util_active_avg_buffer, 0, 2*MAX_UTIL_SLACK_AVG_LEN); + l_perf->util_active_core_counter = 0; + l_perf->util_slack_core_counter = 0; + l_perf->util_slack_accumulator = 0; + l_perf->util_active_accumulator = 0; + l_perf->ptr_putUtilslack = 0; + } + } +} + +/*----------------------------------------------------------------------------*/ +/* End */ +/*----------------------------------------------------------------------------*/ diff --git a/src/occ_405/amec/amec_part.h b/src/occ_405/amec/amec_part.h new file mode 100755 index 0000000..b95909c --- /dev/null +++ b/src/occ_405/amec/amec_part.h @@ -0,0 +1,151 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ_405/amec/amec_part.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#ifndef _AMEC_PART_H +#define _AMEC_PART_H + +/*----------------------------------------------------------------------------*/ +/* Includes */ +/*----------------------------------------------------------------------------*/ +#include <mode.h> +#include <sensor.h> +#include <occ_sys_config.h> +#include <amec_dps.h> + +/*----------------------------------------------------------------------------*/ +/* Constants */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Globals */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Defines */ +/*----------------------------------------------------------------------------*/ + +// Number of cores in system +#define AMEC_PART_NUM_CORES (MAX_NUM_OCC * MAX_NUM_CORES) + +// On a given OCC, only 12 partitions can be defined +#define AMEC_PART_MAX_PART MAX_NUM_CORES + +// Invalid core group ID +#define AMEC_PART_INVALID_ID 0xFF + +/*----------------------------------------------------------------------------*/ +/* Typedef / Enum */ +/*----------------------------------------------------------------------------*/ + +typedef struct amec_part +{ + ///Flag to indicate if the core group should follow the system power policy + BOOLEAN follow_sysmode; + ///EnergyScale power savings policy + OCC_INTERNAL_MODE es_policy; + ///Total number of cores in this core group. + uint8_t ncores; + ///List of cores group. Indices 0 to ncores-1 are valid. Valid values: 0 to + ///AMEC_PART_NUM_CORES-1. + uint8_t core_list[AMEC_PART_NUM_CORES]; + ///Partition ID + uint8_t id; + ///Valid bit (=1 in use, =0 not in use) + uint8_t valid; + ///Soft min frequency boundary sent by PHYP + uint16_t soft_fmin; + ///Soft max frequency boundary sent by PHYP + uint16_t soft_fmax; + ///Power saving state + amec_dps_t dpsalg; + ///slack utilization sensor + sensor_t util2msslack; +} amec_part_t; + +// Main structure that contains the partition configuration for PLPM work. +typedef struct amec_part_config +{ + ///Data structure holding core to partition mapping. The value + ///AMEC_PART_INVALID_ID means that a core is not mapped to a partition. A + ///value less than that means core is mapped to that partition index. + uint16_t core2part[MAX_NUM_CORES]; + + ///Data structure holding all active partitions (core groups) + amec_part_t part_list[AMEC_PART_MAX_PART]; +} amec_part_config_t; + +/*----------------------------------------------------------------------------*/ +/* Function Prototypes */ +/*----------------------------------------------------------------------------*/ + +/** + * Given a core, return a valid partition that owns it, or NULL. + * + */ +amec_part_t* amec_part_find_by_core(amec_part_config_t* i_config, + const uint16_t i_core_index); + +/** + * This function adds a new core group and should only be called within the + * AMEC ISR. + * + */ +void amec_part_add(uint8_t i_id); + +/** + * Add a core group. + * + */ +void amec_part_init(void); + +/** + * Update the parameter values depending on the DPS mode that + * has been selected (Favor Energy or Favor Performance). + * + */ +void amec_part_update_dps_parameter(amec_part_t* io_part); + +/** + * Update the internal performance settings for those cores that + * belong to the input partition. + * + */ +void amec_part_update_perf_settings(amec_part_t* io_part); + +/** + * Update the power mode on all core groups that are following + * the system mode. + * + */ +void AMEC_part_update_sysmode_policy(OCC_MODE i_occ_internal_mode); + +/** + * Overwrite the tunable parameters used by the DPS algorithms + * whenever the Master OCC sends them. + * + */ +void AMEC_part_overwrite_dps_parameters(void); + +#endif //_AMEC_PART_H diff --git a/src/occ_405/amec/amec_pcap.c b/src/occ_405/amec/amec_pcap.c new file mode 100755 index 0000000..c742851 --- /dev/null +++ b/src/occ_405/amec/amec_pcap.c @@ -0,0 +1,538 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/amec/amec_pcap.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +//************************************************************************* +// Includes +//************************************************************************* +#include "amec_pcap.h" +#include "amec_sys.h" +#include "amec_service_codes.h" +#include <occ_common.h> +#include <occ_sys_config.h> +#include <dcom.h> +#include <trac.h> + +//************************************************************************* +// Externs +//************************************************************************* + +//************************************************************************* +// Defines/Enums +//************************************************************************* +#define PPB_NOM_DROP_DELAY 4 //ticks + +//************************************************************************* +// Structures +//************************************************************************* + +//************************************************************************* +// Globals +//************************************************************************* + +//Number of ticks to wait before dropping below nominal frequency +#define PWR_SETTLED_TICKS 4 +//Number of watts power must be below the node power cap before raising +//ppb_fmax +#define PDROP_THRESH 0 //TODO: need better value. +//Number of MHz to raise the proc_pcap_vote for every watt of available power +//(DCM value should be less than SCM) +#define PROC_MHZ_PER_WATT 28 //TODO: need better value. +//Number of MHz to raise ppb_fmax per watt of available power. Depends on +//number of procs in node. +#define NODE_MHZ_PER_WATT() \ + (G_sysConfigData.sys_num_proc_present == 0? \ + 1: \ + ((PROC_MHZ_PER_WATT/G_sysConfigData.sys_num_proc_present) == 0? \ + 1: \ + PROC_MHZ_PER_WATT/G_sysConfigData.sys_num_proc_present)) + +//Frequency_step_khz (from global pstate table)/1000 +uint32_t G_mhz_per_pstate=0; //TODO: Maybe there's a better value to initilize it to. + +uint8_t G_over_pcap_count=0; + +//************************************************************************* +// Function Prototypes +//************************************************************************* + +//************************************************************************* +// Functions +//************************************************************************* + +////////////////////////// +// Function Specification +// +// Name: amec_pmax_clip_controller +// +// Description: Calculate the pmax_clip_freq vote. Initialized to Turbo. +// +// Thread: Real Time Loop +// +// End Function Specification +void amec_pmax_clip_controller(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + uint16_t l_fturbo = G_sysConfigData.sys_mode_freq.table[OCC_MODE_TURBO]; + uint32_t l_pmax_clip_freq = g_amec->proc[0].pwr_votes.pmax_clip_freq; + Pstate l_pstate = 0; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + //Note: quickPowerDrop interrupts will not preempt the real time loop + // interrupt. No locking is needed between the two interrupts. + //Note: quickPowerDropLatchAmec represents the failsafe signal on ITEs and + // oversubscription signal on non-ITEs. + + // See the oversub event and control oversub in AMEC + if(AMEC_INTF_GET_OVERSUBSCRIPTION()&& + (g_amec->oversub_status.oversubLatchAmec==FALSE) ) + { + // ISR already did it but still need to do it again here due to + // l_pmax_clip_freq is incorrect + l_pmax_clip_freq = G_sysConfigData.sys_mode_freq.table[OCC_MODE_MIN_FREQUENCY]; + g_amec->oversub_status.oversubLatchAmec = TRUE; + } + else if( !AMEC_INTF_GET_OVERSUBSCRIPTION() ) + { + // AMEC doesn't control it and let ISR do it + g_amec->oversub_status.oversubLatchAmec = FALSE; + } + + if(l_pmax_clip_freq < l_fturbo) + { + l_pmax_clip_freq += G_mhz_per_pstate; + + if(l_pmax_clip_freq > l_fturbo) + { + l_pmax_clip_freq = l_fturbo; + } + + //call proc_freq2pstate + l_pstate = proc_freq2pstate(l_pmax_clip_freq); + + //Set the pmax_clip register via OCI write. + amec_oversub_pmax_clip(l_pstate); + } + + g_amec->proc[0].pwr_votes.pmax_clip_freq = l_pmax_clip_freq; +} + +////////////////////////// +// Function Specification +// +// Name: amec_pcap_calc +// +// Description: Calculate the node power cap and the processor power cap. +// +// Thread: Real Time Loop +// +// End Function Specification +void amec_pcap_calc(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + bool l_failsafe_state = AMEC_INTF_GET_FAILSAFE(); + bool l_oversub_state = 0; + uint16_t l_node_pwr = AMECSENSOR_PTR(PWR250US)->sample; + uint16_t l_p0_pwr = AMECSENSOR_PTR(PWR250USP0)->sample; + int32_t l_avail_power = 0; + uint32_t l_proc_fraction = 0; + static uint32_t L_prev_node_pcap = 0; + static bool l_apss_error_traced = FALSE; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + //TRAC_INFO("amec_pcap_calc: Calculate active_node_pcap, and nom_pcap_fmin."); + l_oversub_state = AMEC_INTF_GET_OVERSUBSCRIPTION(); + + //Set the active_node_pcap in g_amec. + if(TRUE == l_failsafe_state) + { + //Set active_node_pcap to soft min pcap since failsafe is on + g_amec->pcap.active_node_pcap = G_sysConfigData.pcap.soft_min_pcap; + } + else if(TRUE == l_oversub_state) + { + g_amec->pcap.active_node_pcap = g_amec->pcap.ovs_node_pcap; + } + else + { + g_amec->pcap.active_node_pcap = g_amec->pcap.norm_node_pcap; + } + + //Trace whenever the node pcap changes + if(L_prev_node_pcap != g_amec->pcap.active_node_pcap) + { + TRAC_IMP("amec_pcap_calc: Node pcap set to %d watts.", + g_amec->pcap.active_node_pcap); + L_prev_node_pcap = g_amec->pcap.active_node_pcap; + + // set this pcap as valid (needed by master for comparison) + g_amec->pcap_valid = 1; + } + + l_avail_power = g_amec->pcap.active_node_pcap - l_node_pwr; + if(l_node_pwr != 0) + { + l_proc_fraction = ((uint32_t)(l_p0_pwr) << 16)/l_node_pwr; + if(l_apss_error_traced) + { + TRAC_ERR("PCAP: PWR250US sensor is no longer 0."); + l_apss_error_traced = FALSE; + } + } + else + { + if(!l_apss_error_traced) + { + TRAC_ERR("PCAP: PWR250US sensor is showing a value of 0."); + l_apss_error_traced = TRUE; + } + } + + g_amec->pcap.active_proc_pcap = l_p0_pwr + ((l_proc_fraction * l_avail_power) >> 16); + + //TRAC_INFO("PCAP: calculated active proc pcap: avail_power[0x%X],proc_fraction[0x%X]," + // "active_proc_pcap[0x%X].",l_avail_power,l_proc_fraction,g_amec->pcap.active_proc_pcap); + + //NOTE: Power capping will not affect nominal cores unless a customer power cap is set below the + // max pcap or oversubscription occurs. + // However, nominal cores will drop below nominal if ppb_fmax drops below nominal. + if(g_amec->pcap.active_node_pcap < G_sysConfigData.pcap.max_pcap) + { + g_amec->proc[0].pwr_votes.nom_pcap_fmin = G_sysConfigData.sys_mode_freq.table[OCC_MODE_MIN_FREQUENCY]; + } + else + { + g_amec->proc[0].pwr_votes.nom_pcap_fmin = G_sysConfigData.sys_mode_freq.table[OCC_MODE_NOMINAL]; + } +} + +////////////////////////// +// Function Specification +// +// Name: amec_pcap_controller +// +// Description: Execute the processor Pcap control loop. +// +// Thread: Real Time Loop +// +// End Function Specification +void amec_pcap_controller(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + int32_t l_power_avail = 0; + int32_t l_proc_pcap_vote = g_amec->proc[0].pwr_votes.proc_pcap_vote; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + l_power_avail = g_amec->pcap.active_proc_pcap - AMECSENSOR_PTR(PWR250USP0)->sample; + + if(l_proc_pcap_vote > g_amec->proc[0].pwr_votes.nom_pcap_fmin) + { + l_proc_pcap_vote = g_amec->proc[0].core_max_freq + + (PROC_MHZ_PER_WATT * l_power_avail); + } + else + { + l_proc_pcap_vote += (PROC_MHZ_PER_WATT * l_power_avail); + } + + if(l_proc_pcap_vote > G_sysConfigData.sys_mode_freq.table[OCC_MODE_TURBO]) + { + l_proc_pcap_vote = G_sysConfigData.sys_mode_freq.table[OCC_MODE_TURBO]; + } + + if(l_proc_pcap_vote < G_sysConfigData.sys_mode_freq.table[OCC_MODE_MIN_FREQUENCY]) + { + l_proc_pcap_vote = G_sysConfigData.sys_mode_freq.table[OCC_MODE_MIN_FREQUENCY]; + } + + //Power capping for nominal cores is not allowed to drop frequency below nom_pcap_fmin + if(l_proc_pcap_vote < g_amec->proc[0].pwr_votes.nom_pcap_fmin) + { + g_amec->proc[0].pwr_votes.proc_pcap_nom_vote = g_amec->proc[0].pwr_votes.nom_pcap_fmin; + } + else + { + g_amec->proc[0].pwr_votes.proc_pcap_nom_vote = l_proc_pcap_vote; + } + + g_amec->proc[0].pwr_votes.proc_pcap_vote = l_proc_pcap_vote; +} + +////////////////////////// +// Function Specification +// +// Name: amec_ppb_fmax_calc +// +// Description: Calculate the Performance Preserving Bounds (PPB) vote. +// +// Thread: Real Time Loop +// +// End Function Specification +void amec_ppb_fmax_calc(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + int32_t l_power_avail = 0; + bool l_continue = TRUE; //Used to break from code if needed. + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + //Set the slaves local copy of ppb_fmax to that received from Master OCC. + g_amec->proc[0].pwr_votes.ppb_fmax = G_dcom_slv_inbox_doorbell_rx.ppb_fmax; + // For debug + sensor_update( AMECSENSOR_PTR(PROBE250US0), g_amec->proc[0].pwr_votes.ppb_fmax); + + //CALCULATION done by MASTER OCC only. + if(OCC_MASTER == G_occ_role) + { + //Power available is the ActiveNodePower - PowerDropThreshold - ActualPwr + l_power_avail = g_amec->pcap.active_node_pcap - PDROP_THRESH - AMECSENSOR_PTR(PWR250US)->sample; + + //Note: The PWR250US value is read over the SPI bus, which has no error + //detection. In order to prevent a single bad SPI transfer from causing + //OCC to lower nominal core frequencies, we require the power to be over + //the pcap for PPB_NOM_DROP_DELAY ticks before lowering PPB Fmax below + //Fnom. + if((g_amec->proc[0].pwr_votes.ppb_fmax == G_sysConfigData.sys_mode_freq.table[OCC_MODE_NOMINAL]) + && (l_power_avail <=0)) + { + if(G_over_pcap_count < PPB_NOM_DROP_DELAY) + { + G_over_pcap_count++; + l_continue = FALSE; + } + } + else + { + G_over_pcap_count = 0; + } + + //Only run once every 4 ticks (1ms) to allow time for power hogging + //chips to drop power and power starved chips to raise power. + if(l_continue && (0 == (G_current_tick & 0x3))) + { + if(l_power_avail <= 0) + { + G_sysConfigData.master_ppb_fmax -= G_mhz_per_pstate; + } + else + { + G_sysConfigData.master_ppb_fmax += NODE_MHZ_PER_WATT() * l_power_avail; + } + + if(G_sysConfigData.master_ppb_fmax > G_sysConfigData.sys_mode_freq.table[OCC_MODE_TURBO]) + { + G_sysConfigData.master_ppb_fmax = G_sysConfigData.sys_mode_freq.table[OCC_MODE_TURBO]; + } + + if(G_sysConfigData.master_ppb_fmax < G_sysConfigData.sys_mode_freq.table[OCC_MODE_MIN_FREQUENCY]) + { + G_sysConfigData.master_ppb_fmax = G_sysConfigData.sys_mode_freq.table[OCC_MODE_MIN_FREQUENCY]; + } + } + }//End of Master code +} + +////////////////////////// +// Function Specification +// +// Name: amec_conn_oc_controller +// +// Description: Handle Over Current. Useful on ITE system only. +// Design uses 2 step procedure when OC is detected. +// 1) Lower all cores to Fnom +// 2) If still asserted after PWR_SETTLED_TICKS, lower all cores to Fmin +// +// Thread: Real Time Loop +// +// End Function Specification +void amec_conn_oc_controller(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + errlHndl_t l_err = NULL; + static uint8_t L_asserted_count = 0; + static bool L_error_logged = FALSE; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + //If no overcurrent pins are set, then set vote to Turbo + if(G_conn_oc_pins_bitmap == 0) + { + g_amec->proc[0].pwr_votes.conn_oc_vote = G_sysConfigData.sys_mode_freq.table[OCC_MODE_TURBO]; + } + else + { + //Check if any of the OC gpio pins are set. + //TODO: G_conn_oc_pins_bitmap should be populated with data sent by TMGT. + if((G_conn_oc_pins_bitmap & ~(G_apss_pwr_meas.gpio[0])) == 0) + { + //Reset counter + L_asserted_count = 0; + + g_amec->proc[0].pwr_votes.conn_oc_vote += G_mhz_per_pstate; + + //Verify that vote is not greater than turbo. + if(g_amec->proc[0].pwr_votes.conn_oc_vote > G_sysConfigData.sys_mode_freq.table[OCC_MODE_TURBO]) + { + g_amec->proc[0].pwr_votes.conn_oc_vote = G_sysConfigData.sys_mode_freq.table[OCC_MODE_TURBO]; + } + } + else if(L_asserted_count < PWR_SETTLED_TICKS) + { + g_amec->proc[0].pwr_votes.conn_oc_vote = G_sysConfigData.sys_mode_freq.table[OCC_MODE_NOMINAL]; + L_asserted_count++; + + //Log an informational error + TRAC_ERR("Connector overcurrent pins still asserted. Count=%i.",L_asserted_count); + + /* @ + * @errortype + * @moduleid AMEC_PCAP_CONN_OC_CONTROLLER + * @reasoncode CONNECTOR_OC_PINS_WARNING + * @userdata1 OC pin bitmap + * @userdata2 APSS power measure gpio state + * @devdesc The connector overcurrent pins are still asserted. + * + */ + + //TODO: ADD ACTION FLAG for manufacturing error to log it as predictive. + if(!L_error_logged) //only log this error once + { + L_error_logged = TRUE; + l_err = createErrl( + AMEC_PCAP_CONN_OC_CONTROLLER, //modId + CONNECTOR_OC_PINS_WARNING, //reasoncode + OCC_NO_EXTENDED_RC, //Extended reason code + ERRL_SEV_INFORMATIONAL, //Severity + NULL, //Trace Buf + DEFAULT_TRACE_SIZE, //Trace Size + G_conn_oc_pins_bitmap, //userdata1 + G_apss_pwr_meas.gpio[0] //userdata2 + ); + + commitErrl( &l_err); + } + } + else //Asserted count reached or exceeded PWR_SETTLED_TICKS. + { + g_amec->proc[0].pwr_votes.conn_oc_vote = G_sysConfigData.sys_mode_freq.table[OCC_MODE_MIN_FREQUENCY]; + + if(CURRENT_MODE() == OCC_MODE_NOMINAL) + { + //Log a predictive error + TRAC_ERR("Connector overcurrent pins still asserted after max ticks. Logging error."); + + /* @ + * @errortype + * @moduleid AMEC_PCAP_CONN_OC_CONTROLLER + * @reasoncode CONNECTOR_OC_PINS_FAILURE + * @userdata1 OC pin bitmap + * @userdata2 APSS power measure gpio state + * @devdesc The connector overcurrent pins are asserted for too long. + * + */ + + l_err = createErrl( + AMEC_PCAP_CONN_OC_CONTROLLER, //modId + CONNECTOR_OC_PINS_FAILURE, //reasoncode + OCC_NO_EXTENDED_RC, //Extended reason code + ERRL_SEV_PREDICTIVE, //Severity + NULL, //Trace Buf + DEFAULT_TRACE_SIZE, //Trace Size + G_conn_oc_pins_bitmap, //userdata1 + G_apss_pwr_meas.gpio[0] //userdata2 + ); + + commitErrl( &l_err); + + //Request safe mode without retries. + //TODO: add code to request safe mode with no retries. + } + } + } +} + +////////////////////////// +// Function Specification +// +// Name: amec_power_control +// +// Description: Main function for power control loop. +// +// Thread: Real Time Loop +// +// End Function Specification +void amec_power_control(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + // Call amec pmax clip controller to control the Pmax_clip register setting + // and voting box input. + amec_pmax_clip_controller(); + + // Calculate the power cap for the processor and the power capping limit + // for nominal cores. + amec_pcap_calc(); + + // Calculate the voting box input frequency for staying with the current pcap + amec_pcap_controller(); + + // Calculate the performance preserving bounds voting box input frequency. + amec_ppb_fmax_calc(); + + // Check for connector overcurrent condition and calculate voting box input frequency. + amec_conn_oc_controller(); +} + +/*----------------------------------------------------------------------------*/ +/* End */ +/*----------------------------------------------------------------------------*/ diff --git a/src/occ_405/amec/amec_pcap.h b/src/occ_405/amec/amec_pcap.h new file mode 100755 index 0000000..47a065b --- /dev/null +++ b/src/occ_405/amec/amec_pcap.h @@ -0,0 +1,80 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ_405/amec/amec_pcap.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#ifndef _AMEC_PCAP_H +#define _AMEC_PCAP_H + +//************************************************************************* +// Includes +//************************************************************************* +#include <occ_common.h> +#include <errl.h> + +//************************************************************************* +// Externs +//************************************************************************* + +//************************************************************************* +// Macros +//************************************************************************* + +//************************************************************************* +// Defines/Enums +//************************************************************************* + +//Number of watts power must be below the node power cap before raising +//ppb_fmax +#define PDROP_THRESH 0 //TODO: need better value. + +//Structure used in g_amec +typedef struct amec_pcap +{ + uint16_t ovs_node_pcap; //Oversub node power cap in 1W units + uint16_t norm_node_pcap; //Normal node power cap in 1W units + uint16_t active_node_pcap; //Currently active node power cap in 1W units + uint16_t active_proc_pcap; //Currently active proc power cap in 1W units +} amec_pcap_t; + +//************************************************************************* +// Structures +//************************************************************************* + +//************************************************************************* +// Globals +//************************************************************************* + +//************************************************************************* +// Function Prototypes +//************************************************************************* + +//************************************************************************* +// Functions +//************************************************************************* + +// Calls all power control algorithms +void amec_power_control(); + +#endif + diff --git a/src/occ_405/amec/amec_perfcount.c b/src/occ_405/amec/amec_perfcount.c new file mode 100755 index 0000000..da37d40 --- /dev/null +++ b/src/occ_405/amec/amec_perfcount.c @@ -0,0 +1,134 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ_405/amec/amec_perfcount.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/*----------------------------------------------------------------------------*/ +/* Includes */ +/*----------------------------------------------------------------------------*/ +#include <string.h> +#include <common_types.h> +#include <sensor.h> +#include <amec_sys.h> +#include <amec_part.h> +#include <amec_perfcount.h> + +/*----------------------------------------------------------------------------*/ +/* Constants */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Globals */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Typedef / Enum */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Code */ +/*----------------------------------------------------------------------------*/ + +// Function Specification +// +// Name: amec_calc_dps_util_counters +// +// Description: Calculate the performance counter for a core. +// +// End Function Specification +void amec_calc_dps_util_counters(const uint8_t i_core_id) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + amec_part_t *l_part = NULL; + amec_core_perf_counter_t *l_perf = NULL; + sensor_ptr_t l_sensor = NULL; + uint16_t l_utilization = 0; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + l_perf = &g_amec->proc[0].core[i_core_id].core_perf; + // Read sensor for this core + l_sensor = AMECSENSOR_ARRAY_PTR(UTIL2MSP0C0, i_core_id); + l_utilization = l_sensor->sample; + l_part = amec_part_find_by_core(&g_amec->part_config, i_core_id); + + // Type 41 input: Check if core's utilization is within + // epsilon of the slack threshold: if yes declare the core + // active + if (l_part != NULL) + { + if (l_utilization > (l_part->dpsalg.tlutil - l_part->dpsalg.epsilon_perc)) + { + // indicate core is active + l_perf->util_active_core_counter++; + + if (l_utilization < l_part->dpsalg.tlutil) + { + // indicate core has some slack + l_perf->util_slack_core_counter++; + } + } + } +} + +// Function Specification +// +// Name: amec_core_perf_counter_ctor +// +// Description: Build the performance counter for a core. +// +// End Function Specification +amec_core_perf_counter_t* amec_core_perf_counter_ctor(amec_core_perf_counter_t* i_this_ptr, + const uint8_t i_proc_id, + const uint8_t i_core_id) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + //zero everything out, regardless of when we were called + memset(i_this_ptr, 0x00, sizeof(amec_core_perf_counter_t)); + + // Create space for per-core active and slack counts (for DPS #41 algorithm) + memset(i_this_ptr->ptr_util_slack_avg_buffer, 0, + (uint32_t)(2*MAX_UTIL_SLACK_AVG_LEN)); + memset(i_this_ptr->ptr_util_active_avg_buffer, 0, + (uint32_t)(2*MAX_UTIL_SLACK_AVG_LEN)); + + // DPS frequency request. Choose highest performance by default. + i_this_ptr->dps_freq_request = UINT16_MAX; // input to core freq voting box + + //pass out i_this_ptr in case we're allocating this dynamically + return i_this_ptr; +} + +/*----------------------------------------------------------------------------*/ +/* End */ +/*----------------------------------------------------------------------------*/ diff --git a/src/occ_405/amec/amec_perfcount.h b/src/occ_405/amec/amec_perfcount.h new file mode 100755 index 0000000..49eb20c --- /dev/null +++ b/src/occ_405/amec/amec_perfcount.h @@ -0,0 +1,89 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ_405/amec/amec_perfcount.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#ifndef _AMEC_PERFCOUNT_H +#define _AMEC_PERFCOUNT_H + +/*----------------------------------------------------------------------------*/ +/* Includes */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Constants */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Globals */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Defines */ +/*----------------------------------------------------------------------------*/ +#define MAX_UTIL_SLACK_AVG_LEN 64 // Max # of samples in utilization slack averaging buffer + +/*----------------------------------------------------------------------------*/ +/* Typedef / Enum */ +/*----------------------------------------------------------------------------*/ + +/// Core Performance Counter Model +typedef struct amec_core_perf_counter +{ + ///32-bit accumulator of util_slack counter + uint32_t util_slack_accumulator; + ///32-bit accumulator of util_active counter + uint32_t util_active_accumulator; + ///Circular buffer pointer to put Utilslack signal + uint16_t ptr_putUtilslack; + ///Frequency request + uint16_t dps_freq_request; + ///32-bit pointer to utilization slack averaging buffer + uint8_t ptr_util_slack_avg_buffer[2*MAX_UTIL_SLACK_AVG_LEN]; + ///32-bit pointer to utilization active averaging buffer + uint8_t ptr_util_active_avg_buffer[2*MAX_UTIL_SLACK_AVG_LEN]; + ///8-bit counter of cores that are active (utilization>CPU_utilization_threshold) + uint8_t util_active_core_counter; + ///8-bit counter of cores with slack (utilization<type 1 alg UTIL tlutil) + uint8_t util_slack_core_counter; +}amec_core_perf_counter_t; + +/*----------------------------------------------------------------------------*/ +/* Function Prototypes */ +/*----------------------------------------------------------------------------*/ + +/** + * Calculate the performance counter for a core + * + */ +void amec_calc_dps_util_counters(const uint8_t i_core_id); + +/** + * Build the performance counter for a core + * + */ +amec_core_perf_counter_t* amec_core_perf_counter_ctor(amec_core_perf_counter_t* i_this_ptr, + const uint8_t i_proc_id, + const uint8_t i_core_id); + +#endif diff --git a/src/occ_405/amec/amec_sensors_centaur.c b/src/occ_405/amec/amec_sensors_centaur.c new file mode 100644 index 0000000..2679bc9 --- /dev/null +++ b/src/occ_405/amec/amec_sensors_centaur.c @@ -0,0 +1,686 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ_405/amec/amec_sensors_centaur.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/******************************************************************************/ +/* includes */ +/******************************************************************************/ +#include <occ_common.h> +#include <ssx.h> +#include <errl.h> // Error logging +#include "sensor.h" +#include "rtls.h" +#include "occ_sys_config.h" +#include "occ_service_codes.h" // for SSX_GENERIC_FAILURE +#include "dcom.h" +#include "centaur_data.h" +#include "amec_smh.h" +#include "amec_slave_smh.h" +#include <trac.h> +#include "amec_sys.h" +#include "sensor_enum.h" +#include "amec_service_codes.h" +#include <amec_sensors_centaur.h> + +/******************************************************************************/ +/* Globals */ +/******************************************************************************/ +cent_sensor_flags_t G_dimm_overtemp_bitmap = {0}; +cent_sensor_flags_t G_dimm_temp_updated_bitmap = {0}; +uint8_t G_cent_overtemp_bitmap = 0; +uint8_t G_cent_temp_updated_bitmap = 0; +extern uint8_t G_centaur_needs_recovery; +extern uint8_t G_centaur_nest_lfir6; + +/******************************************************************************/ +/* Forward Declarations */ +/******************************************************************************/ +void amec_update_dimm_dts_sensors(MemData * i_sensor_cache, uint8_t i_centaur); +void amec_update_centaur_dts_sensors(MemData * i_sensor_cache, uint8_t i_centaur); +void amec_perfcount_getmc( MemData * i_sensor_cache, uint8_t i_centaur); + +/******************************************************************************/ +/* Code */ +/******************************************************************************/ + +// Function Specification +// +// Name: amec_update_dimm_dts_sensors +// +// Description: Updates sensors that have data grabbed by the fast core data +// task. +// +// Thread: RealTime Loop +// +// End Function Specification +void amec_update_centaur_sensors(uint8_t i_centaur) +{ + if(CENTAUR_PRESENT(i_centaur)) + { + MemData * l_sensor_cache = cent_get_centaur_data_ptr(i_centaur); + if(CENTAUR_UPDATED(i_centaur)) + { + amec_update_dimm_dts_sensors(l_sensor_cache, i_centaur); + amec_update_centaur_dts_sensors(l_sensor_cache, i_centaur); + } + amec_perfcount_getmc(l_sensor_cache, i_centaur); + CLEAR_CENTAUR_UPDATED(i_centaur); + } +} + +// Function Specification +// +// Name: amec_update_dimm_dts_sensors +// +// Description: Updates sensors that have data grabbed by the fast core data +// task. +// +// Thread: RealTime Loop +// +// End Function Specification +void amec_update_dimm_dts_sensors(MemData * i_sensor_cache, uint8_t i_centaur) +{ +#define MIN_VALID_DIMM_TEMP 1 +#define MAX_VALID_DIMM_TEMP 125 //according to Mike Pardiek +#define MAX_MEM_TEMP_CHANGE 2 + + uint32_t k, l_hottest_dimm_temp; + uint16_t l_dts[NUM_DIMMS_PER_CENTAUR] = {0}; + uint32_t l_hottest_dimm_loc = NUM_DIMMS_PER_CENTAUR; + uint32_t l_sens_status; + int32_t l_dimm_temp, l_prev_temp; + static uint8_t L_ran_once[MAX_NUM_CENTAURS] = {FALSE}; + + // Harvest thermal data for all dimms + for(k=0; k < NUM_DIMMS_PER_CENTAUR; k++) + { + if(!CENTAUR_SENSOR_ENABLED(i_centaur, k)) + { + continue; + } + + l_sens_status = i_sensor_cache->scache.dimm_thermal_sensor[k].fields.status; + fru_temp_t* l_fru = &g_amec->proc[0].memctl[i_centaur].centaur.dimm_temps[k]; + + l_dimm_temp = i_sensor_cache->scache.dimm_thermal_sensor[k].fields.temperature; + l_prev_temp = l_fru->cur_temp; + if(!l_prev_temp) + { + l_prev_temp = l_dimm_temp; + } + + //Check DTS status bits. + if(l_sens_status == DIMM_SENSOR_STATUS_VALID_NEW) + { + //make sure temperature is within a 'reasonable' range. + if(l_dimm_temp < MIN_VALID_DIMM_TEMP || + l_dimm_temp > MAX_VALID_DIMM_TEMP) + { + //set a flag so that if we end up logging an error we have something to debug why + l_fru->flags |= FRU_TEMP_OUT_OF_RANGE; + l_dts[k] = l_prev_temp; + } + else + { + //don't allow temp to change more than is reasonable for 2ms + if(l_dimm_temp > (l_prev_temp + MAX_MEM_TEMP_CHANGE)) + { + l_dts[k] = l_prev_temp + MAX_MEM_TEMP_CHANGE; + if(!l_fru->flags) + { + TRAC_INFO("dimm temp rose faster than reasonable: cent[%d] dimm[%d] prev[%d] cur[%d]", + i_centaur, k, l_prev_temp, l_dimm_temp); + l_fru->flags |= FRU_TEMP_FAST_CHANGE; + } + } + else if (l_dimm_temp < (l_prev_temp - MAX_MEM_TEMP_CHANGE)) + { + l_dts[k] = l_prev_temp - MAX_MEM_TEMP_CHANGE; + if(!l_fru->flags) + { + TRAC_INFO("dimm temp fell faster than reasonable: cent[%d] dimm[%d] prev[%d] cur[%d]", + i_centaur, k, l_prev_temp, l_dimm_temp); + l_fru->flags |= FRU_TEMP_FAST_CHANGE; + } + } + else //reasonable amount of change occurred + { + l_dts[k] = l_dimm_temp; + l_fru->flags &= ~FRU_TEMP_FAST_CHANGE; + } + + //Notify thermal thread that temperature has been updated + G_dimm_temp_updated_bitmap.bytes[i_centaur] |= DIMM_SENSOR0 >> k; + + //clear error flags + l_fru->flags &= FRU_TEMP_FAST_CHANGE; + } + } + else //status was VALID_OLD, ERROR, or STALLED + { + //convert status number to a flag + uint8_t l_status_flag = 1 << l_sens_status; + + if(L_ran_once[i_centaur]) + { + //Trace the error if we haven't traced it already for this sensor + if((l_sens_status != DIMM_SENSOR_STATUS_VALID_OLD) && + !(l_status_flag & l_fru->flags)) + { + TRAC_INFO("Centaur%d sensor%d error: %d", i_centaur, k, l_sens_status); + } + + l_fru->flags |= l_status_flag; + } + + //use last temperature + l_dts[k] = l_prev_temp; + + //request recovery (disable and re-enable sensor cache collection) + if(l_sens_status == DIMM_SENSOR_STATUS_ERROR) + { + G_centaur_needs_recovery |= CENTAUR0_PRESENT_MASK >> i_centaur; + } + } + + //Check if at or above the error temperature + if(l_dts[k] >= g_amec->thermaldimm.ot_error) + { + //Set a bit so that this dimm can be called out by the thermal thread + G_dimm_overtemp_bitmap.bytes[i_centaur] |= 1 << k; + } + } + + // Find hottest temperature from all DIMMs for this centaur + for(l_hottest_dimm_temp = 0, k = 0; k < NUM_DIMMS_PER_CENTAUR; k++) + { + if(l_dts[k] > l_hottest_dimm_temp) + { + l_hottest_dimm_temp = l_dts[k]; + l_hottest_dimm_loc = k; + } + g_amec->proc[0].memctl[i_centaur].centaur.dimm_temps[k].cur_temp = l_dts[k]; + } + + amec_centaur_t* l_centaur_ptr = &g_amec->proc[0].memctl[i_centaur].centaur; + + //only update location if hottest dimm temp is greater than previous maximum + if(l_hottest_dimm_temp > l_centaur_ptr->tempdimmax.sample_max) + { + sensor_update(&l_centaur_ptr->locdimmax, l_hottest_dimm_loc); + } + + //update the max dimm temperature sensor for this centaur + sensor_update(&l_centaur_ptr->tempdimmax, l_hottest_dimm_temp); + + L_ran_once[i_centaur] = TRUE; + AMEC_DBG("Centaur[%d]: HotDimm=%d\n",i_centaur,l_hottest_dimm_temp); +} + + +// Function Specification +// +// Name: amec_update_centaur_dts_sensors +// +// Description: Updates sensors taht have data grabbed by the fast core data +// +// Thread: RealTime Loop +// +// End Function Specification +void amec_update_centaur_dts_sensors(MemData * i_sensor_cache, uint8_t i_centaur) +{ +#define MIN_VALID_CENT_TEMP 1 +#define MAX_VALID_CENT_TEMP 125 //according to Mike Pardiek +#define CENT_SENSOR_STATUS_VALID 0x0001 +#define CENT_SENSOR_TEMP_MASK 0xfff0 + + uint16_t l_dts, l_sens_value0, l_sens_value1; + uint16_t l_sens_status0, l_sens_status1, l_sens_status; + uint16_t l_sens_temp0, l_sens_temp1, l_cent_temp; + int32_t l_prev_temp; + static uint8_t L_ran_once[MAX_NUM_CENTAURS] = {FALSE}; + + l_sens_value0 = i_sensor_cache->scache.centaur_thermal_sensor[0].value; + l_sens_value1 = i_sensor_cache->scache.centaur_thermal_sensor[1].value; + l_sens_status0 = l_sens_value0 & CENT_SENSOR_STATUS_VALID; + l_sens_status1 = l_sens_value1 & CENT_SENSOR_STATUS_VALID; + l_sens_temp0 = (l_sens_status0)? (l_sens_value0 & CENT_SENSOR_TEMP_MASK) >> 4: + 0; + l_sens_temp1 = (l_sens_status1)? (l_sens_value1 & CENT_SENSOR_TEMP_MASK) >> 4: + 0; + l_sens_status = l_sens_status0 | l_sens_status1; + l_cent_temp = (l_sens_temp0 > l_sens_temp1)? l_sens_temp0: l_sens_temp1; + + fru_temp_t* l_fru = &g_amec->proc[0].memctl[i_centaur].centaur.centaur_hottest; + + l_prev_temp = l_fru->cur_temp; + if(!l_prev_temp) + { + l_prev_temp = l_cent_temp; + } + + //Check DTS status bits and NEST LFIR bit 6 for a valid temperature. + if(l_sens_status == CENT_SENSOR_STATUS_VALID && + !(G_centaur_nest_lfir6 & (CENTAUR0_PRESENT_MASK >> i_centaur))) + { + //make sure temperature is within a 'reasonable' range. + if(l_cent_temp < MIN_VALID_CENT_TEMP || + l_cent_temp > MAX_VALID_CENT_TEMP) + { + //set a flag so that if we end up logging an error we have something to debug why + l_fru->flags |= FRU_TEMP_OUT_OF_RANGE; + l_dts = l_prev_temp; + } + else + { + //don't allow temp to change more than is reasonable for 2ms + if(l_cent_temp > (l_prev_temp + MAX_MEM_TEMP_CHANGE)) + { + l_dts = l_prev_temp + MAX_MEM_TEMP_CHANGE; + if(!l_fru->flags) + { + TRAC_INFO("centaur temp rose faster than reasonable: cent[%d] prev[%d] cur[%d]", + i_centaur, l_prev_temp, l_cent_temp); + l_fru->flags |= FRU_TEMP_FAST_CHANGE; + } + } + else if (l_cent_temp < (l_prev_temp - MAX_MEM_TEMP_CHANGE)) + { + l_dts = l_prev_temp - MAX_MEM_TEMP_CHANGE; + if(!l_fru->flags) + { + TRAC_INFO("centaur temp fell faster than reasonable: cent[%d] prev[%d] cur[%d]", + i_centaur, l_prev_temp, l_cent_temp); + l_fru->flags |= FRU_TEMP_FAST_CHANGE; + } + } + else //reasonable amount of change occurred + { + l_dts = l_cent_temp; + l_fru->flags &= ~FRU_TEMP_FAST_CHANGE; + } + + //Notify thermal thread that temperature has been updated + G_cent_temp_updated_bitmap |= CENTAUR0_PRESENT_MASK >> i_centaur; + + //clear error flags + l_fru->flags &= FRU_TEMP_FAST_CHANGE; + } + } + else //status was INVALID + { + if(L_ran_once[i_centaur]) + { + //Trace the error if we haven't traced it already for this sensor + //and this wasn't only caused by LFIR[6] (which is traced elsewhere). + if(!(l_fru->flags & FRU_SENSOR_STATUS_INVALID) && + !(l_sens_status == CENT_SENSOR_STATUS_VALID)) + { + TRAC_INFO("Centaur%d temp invalid. nest_lfir6=0x%02x", i_centaur, G_centaur_nest_lfir6); + } + + l_fru->flags |= FRU_SENSOR_STATUS_INVALID; + + if(G_centaur_nest_lfir6 & (CENTAUR0_PRESENT_MASK >> i_centaur)) + { + l_fru->flags |= FRU_SENSOR_CENT_NEST_FIR6; + } + } + + //use last temperature + l_dts = l_prev_temp; + } + + L_ran_once[i_centaur] = TRUE; + + //Check if at or above the error temperature + if(l_dts >= g_amec->thermalcent.ot_error) + { + //Set a bit so that this dimm can be called out by the thermal thread + G_cent_overtemp_bitmap |= (CENTAUR0_PRESENT_MASK >> i_centaur); + } + + // Update Interim Data - later this will get picked up to form centaur sensor + g_amec->proc[0].memctl[i_centaur].centaur.centaur_hottest.cur_temp = l_dts; + + AMEC_DBG("Centaur[%d]: HotCentaur=%d\n",i_centaur,l_dts); +} + + +// Function Specification +// +// Name: amec_update_centaur_temp_sensors +// +// Description: Updates thermal sensors that have data grabbed by the centaur. +// +// Thread: RealTime Loop +// +// End Function Specification +void amec_update_centaur_temp_sensors(void) +{ + uint32_t k, l_hot; + + // ----------------------------------------------------------- + // Find hottest temperature from all centaurs for this P8 chip + // ----------------------------------------------------------- + for(l_hot = 0, k=0; k < MAX_NUM_CENTAURS; k++) + { + if(g_amec->proc[0].memctl[k].centaur.centaur_hottest.cur_temp > l_hot) + { + l_hot = g_amec->proc[0].memctl[k].centaur.centaur_hottest.cur_temp; + } + } + sensor_update(&g_amec->proc[0].temp2mscent,l_hot); + AMEC_DBG("HotCentaur=%d\n",l_hot); + + // -------------------------------------------------------- + // Find hottest temperature from all DIMMs for this P8 chip + // -------------------------------------------------------- + for(l_hot = 0, k=0; k < MAX_NUM_CENTAURS; k++) + { + if(g_amec->proc[0].memctl[k].centaur.tempdimmax.sample > l_hot) + { + l_hot = g_amec->proc[0].memctl[k].centaur.tempdimmax.sample; + } + } + sensor_update(&g_amec->proc[0].temp2msdimm,l_hot); + AMEC_DBG("HotDimm=%d\n",l_hot); +} + + +// Function Specification +// +// Name: amec_perfcount_getmc +// +// Description: Updates performance sensors that have data grabbed by the +// centaur. +// +// Thread: RealTime Loop +// +// End Function Specification +void amec_perfcount_getmc( MemData * i_sensor_cache, + uint8_t i_centaur) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + UINT32 tempu = 0; + UINT32 templ = 0; + UINT32 temp32new = 0; + UINT32 temp32 = 0; + UINT16 tempreg = 0; + UINT16 tempreg2 = 0; + uint8_t i_mc_id = 0; + #define AMECSENSOR_PORTPAIR_PTR(sensor_base,idx,idx2) \ + (&(g_amec->proc[0].memctl[idx].centaur.portpair[idx2].sensor_base)) + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + MemData * l_sensor_cache = i_sensor_cache; + + for(i_mc_id=0; i_mc_id<2; i_mc_id++) + { + if (i_mc_id == 0) + { + tempu = l_sensor_cache->scache.mba01_rd; + templ = l_sensor_cache->scache.mba01_wr; + } + else + { + tempu = l_sensor_cache->scache.mba23_rd; + templ = l_sensor_cache->scache.mba23_wr; + } + + // --------------------------------------------------------------------------- + // Interim Calculation: MWR2MSP0Mx (0.01 Mrps) Memory write requests per sec + // --------------------------------------------------------------------------- + + // Extract write bandwidth + temp32new = (templ); // left shift into top 20 bits of 32 bits + + temp32 = temp32new - g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.wr_cnt_accum; + g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.wr_cnt_accum = temp32new; // Save latest accumulator away for next time + + // Read every 2 ms....to convert to 0.01 Mrps = ((2ms read * 500)/10000) + tempreg = ((temp32*5)/100); + + g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.memwrite2ms = tempreg; + + // ------------------------------------------------------------------------- + // Interim Calculation: MRD2MSP0Mx (0.01 Mrps) Memory read requests per sec + // ------------------------------------------------------------------------- + + // Extract read bandwidth + temp32new = (tempu); // left shift into top 20 bits of 32 bits + + temp32 = temp32new - g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.rd_cnt_accum; + g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.rd_cnt_accum = temp32new; // Save latest accumulator away for next time + + // Read every 2 ms....to convert to 0.01 Mrps = ((2ms read * 500)/10000) + tempreg = ((temp32*5)/100); + + g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.memread2ms = tempreg; + + // Go after second MC performance counter (power ups and activations) + if (i_mc_id == 0) + { + tempu = l_sensor_cache->scache.mba01_act; + templ = l_sensor_cache->scache.mba01_powerups; + } + else + { + tempu = l_sensor_cache->scache.mba23_act; + templ = l_sensor_cache->scache.mba23_powerups; + } + + // ---------------------------------------------------------------- + // Sensor: MPU2MSP0Mx (0.01 Mrps) Memory power-up requests per sec + // ---------------------------------------------------------------- + // Extract power up count + temp32new = (templ); // left shift into top 20 bits of 32 bits + + // For DD1.0, we only have 1 channel field that we use; DD2.0 we need to add another channel + temp32 = temp32new - g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.pwrup_cnt_accum; + g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.pwrup_cnt_accum = temp32new; // Save latest accumulator away for next time + tempreg=(UINT16)(temp32>>12); // Select upper 20 bits + g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.pwrup_cnt=(UINT16)tempreg; + sensor_update(AMECSENSOR_PORTPAIR_PTR(mpu2ms,i_centaur,i_mc_id), tempreg); + + // ------------------------------------------------------------------- + // Sensor: MAC2MSP0Mx (0.01 Mrps) Memory activation requests per sec + // ------------------------------------------------------------------- + // Extract activation count + temp32 = templ; + temp32 += tempu; + + temp32new = (temp32); // left shift into top 20 bits of 32 bits + // For DD1.0, we only have 1 channel field that we use; DD2.0 we need to add another channel + temp32 = temp32new - g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.act_cnt_accum; + g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.act_cnt_accum = temp32new; // Save latest accumulator away for next time + tempreg=(UINT16)(temp32>>12); // Select lower 16 of 20 bits + g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.act_cnt=(UINT16)tempreg; + sensor_update(AMECSENSOR_PORTPAIR_PTR(mac2ms,i_centaur,i_mc_id), tempreg); + + // -------------------------------------------------------------------------- + // Sensor: MTS2MS (count) Last received Timestamp (frame count) from Centaur + // -------------------------------------------------------------------------- + // Extract framecount (clock is 266.6666666MHz * 0.032 / 4096)=2083. + temp32new = l_sensor_cache->scache.frame_count; + + // For DD1.0, we only have 1 channel field that we use; DD2.0 we need to add another channel + temp32 = temp32new - g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.fr2_cnt_accum; + g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.fr2_cnt_accum = temp32new; // Save latest accumulator away for next time + tempreg=(UINT16)(temp32>>12); // Select upper 20 bits + + g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.fr2_cnt=(UINT16)tempreg; + sensor_update(AMECSENSOR_PORTPAIR_PTR(mts2ms,i_centaur,i_mc_id), tempreg); + + if (i_mc_id == 0) + { + tempu = l_sensor_cache->scache.mba01_cache_hits_rd; + templ = l_sensor_cache->scache.mba01_cache_hits_wr; + } + else + { + tempu = l_sensor_cache->scache.mba23_cache_hits_rd; + templ = l_sensor_cache->scache.mba23_cache_hits_wr; + } + // ---------------------------------------------------------------------- + // Sensor: M4RD2MS (0.01 Mrps) Memory cached (L4) read requests per sec + // ---------------------------------------------------------------------- + temp32new = (tempu); // left shift into top 20 bits of 32 bits + temp32 = temp32new - g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.l4_rd_cnt_accum; + g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.l4_rd_cnt_accum = temp32new; // Save latest accumulator away for next time + + // Read every 2 ms....to convert to 0.01 Mrps = ((2ms read * 500)/10000) + tempreg = ((temp32*5)/100); + tempreg2 = g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.memread2ms; + // Firmware workaround for hardware bug: hits - memreads ~= hits + tempreg = tempreg - tempreg2; + // Deal with maintenance commands or quantization in counters being off by 12 and force to 0 + if ((tempreg > 32767) || (tempreg <= 12)) + { + tempreg=0; + } + + g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.l4rd2ms = tempreg; + sensor_update(AMECSENSOR_PORTPAIR_PTR(m4rd2ms,i_centaur,i_mc_id), tempreg); + + // ----------------------------------------------------------------------- + // Sensor: M4WR2MS (0.01 Mrps) Memory cached (L4) write requests per sec + // ----------------------------------------------------------------------- + temp32new = (templ); // left shift into top 20 bits of 32 bits + temp32 = temp32new - g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.l4_wr_cnt_accum; + g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.l4_wr_cnt_accum = temp32new; // Save latest accumulator away for next time + + // Read every 2 ms....to convert to 0.01 Mrps = ((2ms read * 500)/10000) + tempreg = ((temp32*5)/100); + + tempreg2 = g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.memwrite2ms; + // Firmware workaround for hardware bug: hits - memwrites ~= hits + tempreg = tempreg - tempreg2; + // Deal with maintenance commands or quantization in counters being off by 12 and force to 0 + if ((tempreg > 32767) || (tempreg <= 12)) + { + tempreg=0; + } + + g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.l4wr2ms = tempreg; + sensor_update(AMECSENSOR_PORTPAIR_PTR(m4wr2ms,i_centaur,i_mc_id), tempreg); + + // ------------------------------------------------------------------------------ + // Sensor: MIRB2MS (0.01 Mevents/s) Memory Inter-request arrival idle intervals + // ------------------------------------------------------------------------------ + temp32new = (i_mc_id == 0) ? l_sensor_cache->scache.mba01_intreq_arr_cnt_base : l_sensor_cache->scache.mba23_intreq_arr_cnt_base; + temp32 = temp32new - g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.intreq_base_accum; + g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.intreq_base_accum = temp32new; // Save latest accumulator away for next time + + // Read every 2 ms....to convert to 0.01 Mrps = ((2ms read * 500)/10000) + tempreg = ((temp32*5)/100); + g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.mirb2ms = tempreg; + sensor_update(AMECSENSOR_PORTPAIR_PTR(mirb2ms,i_centaur,i_mc_id), tempreg); + + // -------------------------------------------------------------------------------------------------------- + // Sensor: MIRL2MS (0.01 Mevents/s) Memory Inter-request arrival idle intervals longer than low threshold + // -------------------------------------------------------------------------------------------------------- + temp32new = (i_mc_id == 0) ? l_sensor_cache->scache.mba01_intreq_arr_cnt_low : l_sensor_cache->scache.mba23_intreq_arr_cnt_low; + temp32 = temp32new - g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.intreq_low_accum; + g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.intreq_low_accum = temp32new; // Save latest accumulator away for next time + + // Read every 2 ms....to convert to 0.01 Mrps = ((2ms read * 500)/10000) + tempreg = ((temp32*5)/100); + g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.mirl2ms = tempreg; + sensor_update(AMECSENSOR_PORTPAIR_PTR(mirl2ms,i_centaur,i_mc_id), tempreg); + + // ----------------------------------------------------------------------------------------------------------- + // Sensor: MIRM2MS (0.01 Mevents/s) Memory Inter-request arrival idle intervals longer than medium threshold + // ----------------------------------------------------------------------------------------------------------- + temp32new = (i_mc_id == 0) ? l_sensor_cache->scache.mba01_intreq_arr_cnt_med : l_sensor_cache->scache.mba23_intreq_arr_cnt_med; + temp32 = temp32new - g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.intreq_med_accum; + g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.intreq_med_accum = temp32new; // Save latest accumulator away for next time + + // Read every 2 ms....to convert to 0.01 Mrps = ((2ms read * 500)/10000) + tempreg = ((temp32*5)/100); + g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.mirm2ms = tempreg; + sensor_update(AMECSENSOR_PORTPAIR_PTR(mirm2ms,i_centaur,i_mc_id), tempreg); + + // --------------------------------------------------------------------------------------------------------- + // Sensor: MIRH2MS (0.01 Mevents/s) Memory Inter-request arrival idle intervals longer than high threshold + // --------------------------------------------------------------------------------------------------------- + temp32new = (i_mc_id == 0) ? l_sensor_cache->scache.mba01_intreq_arr_cnt_high : l_sensor_cache->scache.mba23_intreq_arr_cnt_high; + temp32 = temp32new - g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.intreq_high_accum; + g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.intreq_high_accum = temp32new; // Save latest accumulator away for next time + + // Read every 2 ms....to convert to 0.01 Mrps = ((2ms read * 500)/10000) + tempreg = ((temp32*5)/100); + g_amec->proc[0].memctl[i_centaur].centaur.portpair[i_mc_id].perf.mirh2ms = tempreg; + sensor_update(AMECSENSOR_PORTPAIR_PTR(mirh2ms,i_centaur,i_mc_id), tempreg); + } + + // -------------------------------------------------------------------------------------------------------------- + // Sensor: MIRC2MS (0.01 Mevents/s) Memory Inter-request arrival idle interval longer than programmed threshold + // -------------------------------------------------------------------------------------------------------------- + temp32new = l_sensor_cache->scache.intreq_arr_cnt_high_latency; + temp32 = temp32new - g_amec->proc[0].memctl[i_centaur].centaur.perf.intreq_highlatency_accum; + g_amec->proc[0].memctl[i_centaur].centaur.perf.intreq_highlatency_accum = temp32new; // Save latest accumulator away for next time + + // Read every 2 ms....to convert to 0.01 Mrps = ((2ms read * 500)/10000) + tempreg = ((temp32*5)/100); + g_amec->proc[0].memctl[i_centaur].centaur.perf.mirc2ms = tempreg; + sensor_update((&(g_amec->proc[0].memctl[i_centaur].centaur.mirc2ms)), tempreg); + + // ---------------------------------------------------- + // Sensor: MLP2MS (events/s) Number of LP2 exits + // ---------------------------------------------------- + temp32new = l_sensor_cache->scache.lp2_exits; + temp32 = temp32new - g_amec->proc[0].memctl[i_centaur].centaur.perf.lp2exit_accum; + g_amec->proc[0].memctl[i_centaur].centaur.perf.lp2exit_accum = temp32new; // Save latest accumulator away for next time + + // Read every 2 ms....to convert to 0.01 Mrps = ((2ms read * 500)/10000) + tempreg = ((temp32*5)/100); + g_amec->proc[0].memctl[i_centaur].centaur.perf.mlp2_2ms = tempreg; + sensor_update((&(g_amec->proc[0].memctl[i_centaur].centaur.mlp2ms)), tempreg); + + // ------------------------------------------------------------ + // Sensor: MRD2MSP0Mx (0.01 Mrps) Memory read requests per sec + // ------------------------------------------------------------ + tempreg = g_amec->proc[0].memctl[i_centaur].centaur.portpair[0].perf.memread2ms; + tempreg += g_amec->proc[0].memctl[i_centaur].centaur.portpair[1].perf.memread2ms; + sensor_update( (&(g_amec->proc[0].memctl[i_centaur].mrd2ms)), tempreg); + + // ------------------------------------------------------------- + // Sensor: MWR2MSP0Mx (0.01 Mrps) Memory write requests per sec + // ------------------------------------------------------------- + tempreg = g_amec->proc[0].memctl[i_centaur].centaur.portpair[0].perf.memwrite2ms; + tempreg += g_amec->proc[0].memctl[i_centaur].centaur.portpair[1].perf.memwrite2ms; + sensor_update( (&(g_amec->proc[0].memctl[i_centaur].mwr2ms)), tempreg); + + return; +} + +/*----------------------------------------------------------------------------*/ +/* End */ +/*----------------------------------------------------------------------------*/ diff --git a/src/occ_405/amec/amec_sensors_centaur.h b/src/occ_405/amec/amec_sensors_centaur.h new file mode 100644 index 0000000..ddc3132 --- /dev/null +++ b/src/occ_405/amec/amec_sensors_centaur.h @@ -0,0 +1,36 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ_405/amec/amec_sensors_centaur.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#ifndef _AMEC_SENSORS_CENTAUR_H +#define _AMEC_SENSORS_CENTAUR_H + +//************************************************************************* +// Function Prototypes +//************************************************************************* +void amec_update_centaur_sensors(uint8_t i_centaur); +void amec_update_centaur_temp_sensors(void); + +#endif + diff --git a/src/occ_405/amec/amec_sensors_core.c b/src/occ_405/amec/amec_sensors_core.c new file mode 100755 index 0000000..bdea518 --- /dev/null +++ b/src/occ_405/amec/amec_sensors_core.c @@ -0,0 +1,882 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/amec/amec_sensors_core.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/******************************************************************************/ +/* Includes */ +/******************************************************************************/ +#include <occ_common.h> +#include <ssx.h> +#include <errl.h> // Error logging +#include "sensor.h" +#include "rtls.h" +#include "occ_sys_config.h" +#include "occ_service_codes.h" // for SSX_GENERIC_FAILURE +#include "dcom.h" +#include "proc_data.h" +#include "amec_smh.h" +#include "amec_slave_smh.h" +#include <trac.h> +#include "amec_sys.h" +#include "sensor_enum.h" +#include "amec_service_codes.h" +#include <amec_sensors_core.h> +#include "amec_perfcount.h" + +/******************************************************************************/ +/* Globals */ +/******************************************************************************/ + +/******************************************************************************/ +/* Forward Declarations */ +/******************************************************************************/ +void amec_calc_dts_sensors(gpe_bulk_core_data_t * i_core_data_ptr, uint8_t i_core); +//void amec_calc_cpm_sensors(gpe_bulk_core_data_t * i_core_data_ptr, uint8_t i_core); //CPM - Commented out as requested by Malcolm +void amec_calc_freq_and_util_sensors(gpe_bulk_core_data_t * i_core_data_ptr, uint8_t i_core); +void amec_calc_ips_sensors(gpe_bulk_core_data_t * i_core_data_ptr, uint8_t i_core); +void amec_calc_spurr(uint8_t i_core); + +//************************************************************************* +// Code +//************************************************************************* + +// Function Specification +// +// Name: amec_update_fast_core_data_sensors +// +// Description: Updates sensors that have data grabbed by the fast core data +// task. +// +// Thread: RealTime Loop +// +// End Function Specification +void amec_update_fast_core_data_sensors(void) +{ + // ------------------------------------------------------ + // Update Fast Core Data Sensors + // ------------------------------------------------------ + // SensorNameCx = PCBS Local Pstate Freq Target (per core) + // TODclock = TOD Clock? + + // Need to comment this out because l_tod is always zero, which messes + // up proper sensor updates. Proper updating is done below in the core level + // sensor updates. + + //gpe_fast_core_data_t * l_core = proc_get_fast_core_data_ptr(); + // uint32_t l_tod = l_core->tod; + + //if( l_core != NULL) + //{ + // GPEtickdur0 = duration of last tick's PORE-GPE0 duration + // sensor_update( AMECSENSOR_PTR(TODclock0), CONVERT_UINT32_UINT8_UPPER_HIGH(l_tod) ); + // sensor_update( AMECSENSOR_PTR(TODclock1), CONVERT_UINT32_UINT16_MIDDLE(l_tod) ); + // sensor_update( AMECSENSOR_PTR(TODclock2), ((uint16_t) (CONVERT_UINT32_UINT8_LOWER_LOW(l_tod))) << 8); + //} + + // TODO: Don't know what to update from the PCBS LPstate Target Freq Status Reg + //for(int i=0; i++; i<MAX_NUM_HW_CORES) + //{ + // sensor_update(&sensor, + // G_read_fast_core_data_ptr->core_data[i].pcbs_lpstate_freq_target_sr); + //} +} + + +// Function Specification +// +// Name: amec_update_proc_core_sensors +// +// Description: Update all the sensors for a given proc +// +// Thread: RealTime Loop +// +// End Function Specification +void amec_update_proc_core_sensors(uint8_t i_core) +{ + gpe_bulk_core_data_t * l_core_data_ptr; + int i; + uint16_t l_temp16 = 0; + uint32_t l_temp32 = 0; + + // Make sure the core is present, and that it has updated data. + if(CORE_PRESENT(i_core) && CORE_UPDATED(i_core)) + { + // Clear flag indicating core was updated by proc task + CLEAR_CORE_UPDATED(i_core); + + // Get pointer to core data + l_core_data_ptr = proc_get_bulk_core_data_ptr(i_core); + + //------------------------------------------------------- + // Thermal Sensors & Calc + //------------------------------------------------------- + amec_calc_dts_sensors(l_core_data_ptr, i_core); + + //------------------------------------------------------- + //CPM - Commented out as requested by Malcolm + // ------------------------------------------------------ + // amec_calc_cpm_sensors(l_core_data_ptr, i_core); + + //------------------------------------------------------- + // Util / Freq + //------------------------------------------------------- + // Skip this update if there was an empath collection error + if (!CORE_EMPATH_ERROR(i_core)) + { + amec_calc_freq_and_util_sensors(l_core_data_ptr,i_core); + } + + //------------------------------------------------------- + // Performance counter - This function should be called + // after amec_calc_freq_and_util_sensors(). + //------------------------------------------------------- + amec_calc_dps_util_counters(i_core); + + //------------------------------------------------------- + // IPS + //------------------------------------------------------- + // Skip this update if there was an empath collection error + if (!CORE_EMPATH_ERROR(i_core)) + { + amec_calc_ips_sensors(l_core_data_ptr,i_core); + } + + //------------------------------------------------------- + // SPURR + //------------------------------------------------------- + amec_calc_spurr(i_core); + + // ------------------------------------------------------ + // Update PREVIOUS values for next time + // ------------------------------------------------------ + g_amec->proc[0].core[i_core].prev_PC_RAW_Th_CYCLES = l_core_data_ptr->per_thread[0].raw_cycles; + + // Skip empath updates if there was an empath collection error on this core + if (!CORE_EMPATH_ERROR(i_core)) + { + g_amec->proc[0].core[i_core].prev_PC_RAW_CYCLES = l_core_data_ptr->empath.raw_cycles; + g_amec->proc[0].core[i_core].prev_PC_RUN_CYCLES = l_core_data_ptr->empath.run_cycles; + g_amec->proc[0].core[i_core].prev_PC_COMPLETED = l_core_data_ptr->empath.completion; + g_amec->proc[0].core[i_core].prev_PC_DISPATCH = l_core_data_ptr->empath.dispatch; + g_amec->proc[0].core[i_core].prev_tod_2mhz = l_core_data_ptr->empath.tod_2mhz; + g_amec->proc[0].core[i_core].prev_FREQ_SENS_BUSY = l_core_data_ptr->empath.freq_sens_busy; + g_amec->proc[0].core[i_core].prev_FREQ_SENS_FINISH = l_core_data_ptr->empath.freq_sens_finish; + } + + for(i=0; i<MAX_THREADS_PER_CORE; i++) + { + g_amec->proc[0].core[i_core].thread[i].prev_PC_RUN_Th_CYCLES = l_core_data_ptr->per_thread[i].run_cycles; + } + + // Final step is to update TOD sensors + // Extract 32 bits with 16usec resolution + l_temp32 = (uint32_t)(G_dcom_slv_inbox_doorbell_rx.tod>>13); + l_temp16 = (uint16_t)(l_temp32); + // low 16 bits is 16usec resolution with 512MHz TOD clock + sensor_update( AMECSENSOR_PTR(TODclock0), l_temp16); + l_temp16 = (uint16_t)(l_temp32>>16); + // mid 16 bits is 1.05sec resolution with 512MHz TOD clock + sensor_update( AMECSENSOR_PTR(TODclock1), l_temp16); + l_temp16 = (uint16_t)(G_dcom_slv_inbox_doorbell_rx.tod>>45); + // hi 3 bits in 0.796 day resolution with 512MHz TOD clock + sensor_update( AMECSENSOR_PTR(TODclock2), l_temp16); + } +} + + +// Function Specification +// +// Name: amec_calc_dts_sensors +// +// Description: Compute core temperature. This function is called every +// 2ms/core. +// +// Thread: RealTime Loop +// +// End Function Specification +void amec_calc_dts_sensors(gpe_bulk_core_data_t * i_core_data_ptr, uint8_t i_core) +{ +#define DTS_PER_CORE 4 +#define DTS_INVALID_MASK 0x0C00 + + uint32_t k, l_core_avg, l_core_hot, l_sensor_count; + uint32_t l_oha_status_reg = 0; + uint32_t l_pm_state_hist_reg = 0; + uint16_t l_dts[DTS_PER_CORE]; + BOOLEAN l_update_sensor = FALSE; + + // Build up array of DTS values + // DTS sensors are in the format of uint64_t dts0 : 12; + // uint64_t thermal_trip0 : 2; + // uint64_t spare0 : 1; + // uint64_t valid0 : 1; + // + // so we will need to convert them before they are used in loop below. + l_dts[0] = i_core_data_ptr->dts_cpm.sensors_v0.fields.dts0; + l_dts[1] = i_core_data_ptr->dts_cpm.sensors_v0.fields.dts1; + l_dts[2] = i_core_data_ptr->dts_cpm.sensors_v0.fields.dts2; + l_dts[3] = i_core_data_ptr->dts_cpm.sensors_v1.fields.dts4; + + // Read the low-order bytes of the OHA Status register + l_oha_status_reg = i_core_data_ptr->oha.oha_ro_status_reg.words.low_order; + + // Read the high-order bytes of PM State History register for this core + l_pm_state_hist_reg = i_core_data_ptr->pcb_slave.pm_history.words.high_order; + + // Check if we were able to collect core data + if(l_oha_status_reg & CORE_DATA_CORE_SENSORS_COLLECTED) + { + // Check if all DTS readings in the core are valid. The field sensors_v0 + // contains core-related data + if(i_core_data_ptr->dts_cpm.sensors_v0.fields.valid0 || + i_core_data_ptr->dts_cpm.sensors_v0.fields.valid1 || + i_core_data_ptr->dts_cpm.sensors_v0.fields.valid2) + { + l_update_sensor = TRUE; + } + } + // Check if we were able to collect L3 data + if(l_oha_status_reg & CORE_DATA_L3_SENSORS_COLLECTED) + { + // Check if DTS reading in the L3 is valid. The field sensors_v1 contains + // L3-related data + if(i_core_data_ptr->dts_cpm.sensors_v1.fields.valid4) + { + l_update_sensor = TRUE; + } + } + // Check if this core has been in fast winkle OR deep winkle + if(((l_pm_state_hist_reg & OCC_PM_STATE_MASK) == OCC_PAST_FAST_WINKLE) || + ((l_pm_state_hist_reg & OCC_PM_STATE_MASK) == OCC_PAST_DEEP_WINKLE)) + { + l_update_sensor = TRUE; + } + + // Update the thermal sensor associated with this core + if(l_update_sensor) + { + //calculate average temperature from all DTS's for this core + for(l_sensor_count = DTS_PER_CORE, l_core_hot = 0, + l_core_avg = 0, k = 0; + k < DTS_PER_CORE; k++) + { + //Hardware bug workaround: Temperatures reaching 0 degrees C + //can show up as negative numbers. To fix this, we discount + //values that have the 2 MSB's set. + if((l_dts[k] & DTS_INVALID_MASK) == DTS_INVALID_MASK) + { + l_dts[k] = 0; + } + + l_core_avg += l_dts[k]; + if(l_dts[k] > l_core_hot) + { + l_core_hot = l_dts[k]; + } + // Assume 0 degrees to mean bad sensor and don't let it bring the + // average reading down. + else if(l_dts[k] == 0) + { + l_sensor_count--; + } + } + + if(l_sensor_count == DTS_PER_CORE) + { + //For the common case, compiler converts this to a fast multiplication + //operation when one of the operands is a constant. + l_core_avg /= DTS_PER_CORE; + } + else if(l_sensor_count) //prevent div by 0 if all sensors are zero + { + //otherwise, use the slower division routine when both operands are + //unknown at compile time. + l_core_avg /= l_sensor_count; + } + + // Update sensors & Interim Data + sensor_update( AMECSENSOR_ARRAY_PTR(TEMP2MSP0C0,i_core), l_core_avg); + g_amec->proc[0].core[i_core].dts_hottest = l_core_hot; + } +} + + +//CPM - Commented out as requested by Malcolm +/* void amec_calc_cpm_sensors(gpe_bulk_core_data_t * i_core_data_ptr, uint8_t i_core) +{ +#define CPM_PER_CORE 4 + + uint32_t k, l_cpm_min = 0xffffffff; + uint16_t l_cpm[CPM_PER_CORE]; + + l_cpm[0] = i_core_data_ptr->dts_cpm.sensors_v8.fields.encoded_cpm0; + l_cpm[1] = i_core_data_ptr->dts_cpm.sensors_v8.fields.encoded_cpm1; + l_cpm[2] = i_core_data_ptr->dts_cpm.sensors_v8.fields.encoded_cpm2; + l_cpm[3] = i_core_data_ptr->dts_cpm.sensors_v9.fields.encoded_cpm4; + + //calculate min CPM from all CPM's for this core + for(k = 0; k < CPM_PER_CORE; k++) + { + if(l_cpm[k] < l_cpm_min) + { + l_cpm_min = l_cpm[k]; + } + } + + sensor_update( AMECSENSOR_ARRAY_PTR(CPM2MSP0C0,i_core), l_cpm_min); +} +*/ + +// Function Specification +// +// Name: amec_calc_freq_and_util_sensors +// +// Description: Compute the frequency and utilization sensors for a given core. +// This function is called every 2ms/core. +// +// Thread: RealTime Loop +// +// End Function Specification +void amec_calc_freq_and_util_sensors(gpe_bulk_core_data_t * i_core_data_ptr, uint8_t i_core) +{ + BOOLEAN l_core_sleep_winkle = FALSE; + uint32_t l_pm_state_hist_reg = 0; + uint32_t temp32 = 0; + uint32_t temp32a = 0; + uint16_t temp16 = 0; + uint16_t temp16a = 0; + uint16_t l_core_util = 0; + uint16_t l_core_freq = 0; + uint16_t l_time_interval = 0; + uint32_t l_cycles2ms = 0; + int i; + + // Read the high-order bytes of PM State History register for this core + l_pm_state_hist_reg = i_core_data_ptr->pcb_slave.pm_history.words.high_order; + + // If core is in fast/deep sleep mode or fast/winkle mode, then set a flag + // indicating this + if(l_pm_state_hist_reg & OCC_PAST_CORE_CLK_STOP) + { + l_core_sleep_winkle = TRUE; + } + + // ------------------------------------------------------ + // Per Core Frequency + // ------------------------------------------------------ + // <amec_formula> + // Result: Calculated Core Frequency + // Sensor: FREQA2MSP0C0 + // Timescale: 2ms + // Units: MHz + // Min/Max: 0/6000 (UPPER_LIMIT_PROC_FREQ_MHZ=6000) + // Formula: cyc_delta = (RAW_CYCLES[t=now] - RAW_CYCLES[t=-2ms]) + // time_delta = (TOD[t=now] - TOD[t=-2ms]) + // frequency(MHz) = (cyc_delta / time_delta) * (2M TOD ticks / 1 second) + // = (2 * cyc_delta) / time_delta + // </amec_formula> + + // Compute Delta in PC_RAW_CYCLES + temp32 = i_core_data_ptr->empath.raw_cycles; + temp32a = g_amec->proc[0].core[i_core].prev_PC_RAW_CYCLES; + temp32 = l_cycles2ms = temp32 - temp32a; + + if( (cfam_id() == CFAM_CHIP_ID_MURANO_10) + || (cfam_id() == CFAM_CHIP_ID_MURANO_11) + || (cfam_id() == CFAM_CHIP_ID_MURANO_12) ) + { + temp32a = AMEC_US_PER_SMH_PERIOD; // using fixed 2000us is showing 3% error. + temp32 = temp32 / temp32a; + } + else + { + temp32a = (i_core_data_ptr->empath.tod_2mhz - + g_amec->proc[0].core[i_core].prev_tod_2mhz); + temp32 = (2 * temp32) / temp32a; + } + + // TODO: Remove this once we have the OHA Power Proxy legacy mode stuff working. + if(temp32 < UPPER_LIMIT_PROC_FREQ_MHZ) + { + // Update Sensor for this core + if(l_core_sleep_winkle) + { + l_core_freq = 0; + } + else + { + l_core_freq = (uint16_t) temp32; + } + sensor_update( AMECSENSOR_ARRAY_PTR(FREQA2MSP0C0,i_core), l_core_freq); + } + + // ------------------------------------------------------ + // Per Core Utilization + // ------------------------------------------------------ + // <amec_formula> + // Result: Calculated Core Utilization + // Sensor: UTIL2MSP0C0 + // Timescale: 2ms + // Units: 0.01 % + // Min/Max: 0/10000 (0/100%) + // Formula: cyc_delta = (RAW_CYCLES[t=now] - RAW_CYCLES[t=-2ms]) + // run_delta = (RUN_CYCLES[t=now] - RUN_CYCLES[t=-2ms]) + // + // UTIL(in %) = run_delta / cyc_delta + // </amec_formula> + + // Compute Delta in PC_RUN_CYCLES + temp32 = i_core_data_ptr->empath.run_cycles; + temp32a = g_amec->proc[0].core[i_core].prev_PC_RUN_CYCLES; + temp32 = temp32 - temp32a; + + temp32 = temp32 >> 8; // Drop non-significant bits + temp16 = (uint16_t) temp32; // Cast to uint16 for mult below + temp16a = 10000; // Mult * 10000 to get finer resolution for 0.01% + temp32 = ((uint32_t)temp16a)*((uint32_t)temp16); + + temp32a = l_cycles2ms; // Get Raw cycles + temp32a = temp32a >> 8; // Drop non-significant bits + + // Calculate Utilization + temp32 = temp32 / temp32a; + if(temp32a == 0) // Prevent a divide by zero + { + temp32 = 0; + } + + // Update Sensor for this core + if(l_core_sleep_winkle) + { + l_core_util = 0; + } + else + { + l_core_util = (uint16_t) temp32; + } + sensor_update(AMECSENSOR_ARRAY_PTR(UTIL2MSP0C0, i_core), l_core_util); + + + // ------------------------------------------------------ + // Per Thread Utilization + // ------------------------------------------------------ + // <amec_formula> + // Result: Calculated Core Utilization + // Sensor: None + // Timescale: 2ms + // Units: 0.01 % + // Min/Max: 0/10000 (0/100%) + // Formula: cyc_delta = (RAW_CYCLES[t=now] - RAW_CYCLES[t=-2ms]) + // run_delta = (RUN_CYCLES[t=now] - RUN_CYCLES[t=-2ms]) + // + // UTIL(in %) = run_delta / cyc_delta + // </amec_formula> + + // Get RAW CYCLES for Thread + temp32 = i_core_data_ptr->per_thread[0].raw_cycles; + temp32a = g_amec->proc[0].core[i_core].prev_PC_RAW_Th_CYCLES; + temp32 = l_cycles2ms = temp32 - temp32a; + + for(i=0; i<MAX_THREADS_PER_CORE; i++) + { + // Get Run Counters for Thread + temp32 = i_core_data_ptr->per_thread[i].run_cycles; + temp32a = g_amec->proc[0].core[i_core].thread[i].prev_PC_RUN_Th_CYCLES; + temp32 = temp32 - temp32a; + + temp32 = temp32 >> 8; // Drop non-significant bits + temp16 = (uint16_t) temp32; // Cast to uint16 for mult below + temp16a = 10000; // Mult * 10000 to get finer resolution for 0.01% + temp32 = ((uint32_t)temp16a)*((uint32_t)temp16); + + temp32a = l_cycles2ms; + temp32a = temp32a >> 8; // Drop non-significant bits + + // Calculate Utilization + temp32 = temp32 / temp32a; + + // Update per thread value for this core + if(l_core_sleep_winkle) + { + temp32 = 0; + } + g_amec->proc[0].core[i_core].thread[i].util2ms_thread = (uint16_t) temp32; + } + + // No sensors to update for perThread Util + + // ------------------------------------------------------ + // Per Core Sleep/Winkle Count + // ------------------------------------------------------ + + // Get Current Idle State of Chiplet + // The SLEEPCNT and WINKLECNT sensors are updated in amec_slv_state_0() function + temp16 = CONVERT_UINT64_UINT16_UPPER(i_core_data_ptr->pcb_slave.pm_history.value); + temp16 = temp16 & 0xE000; + temp16 = temp16 >> 13; + switch(temp16) + { + case 0: break; // Run State + case 1: break; // Special Wakeup + case 2: break; // Nap + case 3: SETBIT(g_amec->proc[0].sleep_cnt,i_core); break; // Legacy Sleep + case 4: SETBIT(g_amec->proc[0].sleep_cnt,i_core); break; // Fast Sleep + case 5: SETBIT(g_amec->proc[0].sleep_cnt,i_core); break; // Deep Sleep + case 6: SETBIT(g_amec->proc[0].winkle_cnt,i_core); break; // Fast Winkle + case 7: SETBIT(g_amec->proc[0].winkle_cnt,i_core); break; // Deep Winkle + } + + // ------------------------------------------------------ + // Core Memory Hierarchy C LPARx Utilization counters + // ------------------------------------------------------ + for(i=0; i<4; i++) + { + // Extract the utilization counter + temp32 = i_core_data_ptr->per_partition_memory.count[i]; + + // Convert counter to 0.01 Mrps resolution. Since we access every 2 ms: + // ((2ms read * 500) / 10000) + temp32a = temp32 - g_amec->proc[0].core[i_core].prev_lpar_mem_cnt[i]; + g_amec->proc[0].core[i_core].prev_lpar_mem_cnt[i] = temp32; + temp32 = (temp32a * 5) / 100; + + // Store the bandwidth for this LPAR + g_amec->proc[0].core[i_core].membw[i] = (uint16_t)temp32; + } + + // Sum up all the memory bandwidth data from the LPARs + temp32 = g_amec->proc[0].core[i_core].membw[0] + + g_amec->proc[0].core[i_core].membw[1] + + g_amec->proc[0].core[i_core].membw[2] + + g_amec->proc[0].core[i_core].membw[3]; + + // Divide by two due to a bug in the hardware + temp32 = temp32/2; + + // See if core is sleeping/winkled + if(l_core_sleep_winkle) + { + temp32 = 0; + } + // Update Sensor for this core + sensor_update( AMECSENSOR_ARRAY_PTR(CMBW2MSP0C0,i_core), (uint16_t) temp32); + + // ------------------------------------------------------ + // Core Stall counters + // ------------------------------------------------------ + temp32 = i_core_data_ptr->empath.freq_sens_busy; + temp32a = g_amec->proc[0].core[i_core].prev_FREQ_SENS_BUSY; + temp32 = temp32 - temp32a; + temp32 = temp32 >> 8; + + // See if core is sleeping/winkled + if(l_core_sleep_winkle) + { + temp32 = 0; + } + + // Update Sensor for this core + sensor_update( AMECSENSOR_ARRAY_PTR(NOTBZE2MSP0C0,i_core), (uint16_t) temp32); + + temp32 = i_core_data_ptr->empath.freq_sens_finish; + temp32a = g_amec->proc[0].core[i_core].prev_FREQ_SENS_FINISH; + temp32 = temp32 - temp32a; + temp32 = temp32 >> 8; + + // See if core is sleeping/winkled + if(l_core_sleep_winkle) + { + temp32 = 0; + } + + // Update Sensor for this core + sensor_update( AMECSENSOR_ARRAY_PTR(NOTFIN2MSP0C0,i_core), (uint16_t) temp32); + + // ------------------------------------------------------ + // Per Core Normalized Average Utilization + // ------------------------------------------------------ + // <amec_formula> + // Result: Calculated Normalized Average Core Utilization + // Sensor: NUTIL3SP0C0 + // Timescale: 2ms (3s rolling average) + // Units: 0.01 % + // Min/Max: 0/10000 (0/100%) + // </amec_formula> + + // Determine the time interval for the rolling average calculation + l_time_interval = AMEC_DPS_SAMPLING_RATE * AMEC_IPS_AVRG_INTERVAL; + + // Increment our sample count but prevent it from wrapping + if(g_amec->proc[0].core[i_core].sample_count < UINT16_MAX) + { + g_amec->proc[0].core[i_core].sample_count++; + } + + if(g_amec->proc[0].core[i_core].sample_count == l_time_interval) + { + // Increase resolution of the UTIL accumulator by two decimal places + temp32 = AMECSENSOR_ARRAY_PTR(UTIL2MSP0C0,i_core)->accumulator * 100; + // Calculate average utilization of this core + temp32 = temp32 / g_amec->proc[0].core[i_core].sample_count; + g_amec->proc[0].core[i_core].avg_util = temp32; + + // Increase resolution of the FREQA accumulator by two decimal places + temp32 = AMECSENSOR_ARRAY_PTR(FREQA2MSP0C0,i_core)->accumulator * 100; + // Calculate average frequency of this core + temp32 = temp32 / g_amec->proc[0].core[i_core].sample_count; + g_amec->proc[0].core[i_core].avg_freq = temp32; + } + + if(g_amec->proc[0].core[i_core].sample_count > l_time_interval) + { + // Calculate average utilization for this core + temp32 = (uint32_t) g_amec->proc[0].core[i_core].avg_util; + temp32 = temp32 * (l_time_interval-1); + temp32 = temp32 + l_core_util*100; + g_amec->proc[0].core[i_core].avg_util = temp32 / l_time_interval; + + // Could be needed for increase accuracy + //if(g_amec->proc[0].core[i_core].avg_util > 9000) + //{ This rounds up only! + //g_amec->proc[0].core[i_core].avg_util = (temp32+ (1500-1)) / 1500; + //} + + // Calculate average frequency for this core + temp32 = (uint32_t) g_amec->proc[0].core[i_core].avg_freq; + temp32 = temp32 * (l_time_interval-1); + temp32 = temp32 + l_core_freq*100; + g_amec->proc[0].core[i_core].avg_freq = temp32 / l_time_interval; + } + + // Calculate the normalized utilization for this core + if(g_amec->proc[0].core[i_core].avg_freq != 0) + { + // First, revert back to the original resolution of the sensors + temp32 = g_amec->proc[0].core[i_core].avg_util / 100; + temp32a = g_amec->proc[0].core[i_core].avg_freq / 100; + + // Compute now the normalized utilization as follows: + // Normalized utilization = (Average_utilization)/(Average_frequency) * Fnom + // Note: The 100000 constant is to increase the precision of our division + temp32 = (temp32 * 100000) / temp32a; + temp32 = (temp32 * G_sysConfigData.sys_mode_freq.table[OCC_MODE_NOMINAL]) / 100000; + + // Update sensor for this core + if(l_core_sleep_winkle) + { + sensor_update(AMECSENSOR_ARRAY_PTR(NUTIL3SP0C0, i_core), 0); + } + else + { + sensor_update(AMECSENSOR_ARRAY_PTR(NUTIL3SP0C0, i_core), (uint16_t)temp32); + } + } +} + + +void amec_calc_ips_sensors(gpe_bulk_core_data_t * i_core_data_ptr, uint8_t i_core) +{ +#define TWO_PWR_24_MASK 0x00FFFFFF +#define TWO_PWR_20_MASK 0x000FFFFF + + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + INT32 cyc1 = 0; //cycle counts + INT32 cyc2 = 0; + UINT32 fin1 = 0; //finished instruction counts + UINT32 fin2 = 0; + INT32 disp1 = 0; //dispatched instruction counts + INT32 disp2 = 0; + UINT32 temp32 = 0; + UINT32 ticks_2mhz = 0; // IPS sensor interval in 2mhz ticks + BOOLEAN l_core_sleep_winkle = FALSE; + uint32_t l_pm_state_hist_reg = 0; + + + // Read the high-order bytes of PM State History register for this core + l_pm_state_hist_reg = i_core_data_ptr->pcb_slave.pm_history.words.high_order; + + // If core is in fast/deep sleep mode or fast/winkle mode, then set a flag + // indicating this + if(l_pm_state_hist_reg & OCC_PAST_CORE_CLK_STOP) + { + l_core_sleep_winkle = TRUE; + } + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + // Get Run Cycles + cyc1 = i_core_data_ptr->empath.run_cycles; + cyc2 = g_amec->proc[0].core[i_core].prev_PC_RUN_CYCLES; + cyc2 = cyc1 - cyc2; + + // Following lines look bogus...the counters are supposed to be 32-bit + // since we are doing 24-bit unsigned math, we need to account for the + // overflow case. If this occurs, we mask off the "overflow" to make it behave + // like a 32-bit subtraction overflow would. Commenting them out. + //if ( cyc2 < 0 ) + //{ + // cyc2 &= TWO_PWR_24_MASK; + //} + + fin1 = i_core_data_ptr->empath.completion; + fin2 = g_amec->proc[0].core[i_core].prev_PC_COMPLETED; + fin2 = fin1 - fin2; + + // Is this counting every completed instruction or 1 of every 16? + // Why are we masking 20 bits of a 32-bit counter? Commenting these lines out. + //if ( fin2 < 0 ) + //{ + // fin2 &= TWO_PWR_20_MASK; + //} + + disp1 = i_core_data_ptr->empath.dispatch; + disp2 = g_amec->proc[0].core[i_core].prev_PC_DISPATCH; + disp2 = disp1 - disp2; + + if ( disp2 < 0 ) + { + disp2 &= TWO_PWR_20_MASK; + } + + // ------------------------------------------------------ + // Per Core IPC Calculation + // ------------------------------------------------------ + // <amec_formula> + // Result: Calculated Instructions per Cycle + // Sensor: None + // Timescale: 2ms + // Units: 0.01 IPC + // Min/Max: ? + // Formula: ipc_delta = (INST_COMPLETE[t=now] - INST_COMPLETE[t=-2ms]) + // run_cycles = (RUN_CYCLES[t=now] - RUN_CYCLES[t=-2ms]) + // 100 = Convert 0.01 DPC + // + // IPC(in 0.01 IPC) = (ipc_delta * 100) / run_cycles + // </amec_formula> + temp32 = (fin2 * 100); // In units of IPS + temp32 = temp32 / cyc2; // In units of 0.01 DPC + g_amec->proc[0].core[i_core].ipc = temp32; + + + // ------------------------------------------------------ + // Per Core DPC Calculation + // ------------------------------------------------------ + // <amec_formula> + // Result: Calculated dispatched Instructions per Cycle + // Sensor: None + // Timescale: 2ms + // Units: 0.2Mips + // Min/Max: ? + // Formula: dpc_delta = (INST_DISPATCH[t=now] - INST_DISPATCH[t=-2ms]) + // run_cycles = (RUN_CYCLES[t=now] - RUN_CYCLES[t=-2ms]) + // 100 = Convert 0.01 DPC + // + // DPC(in 0.01DPC) = (dpc_delta * 100) / run_cycles + // </amec_formula> + temp32 = (disp2 * 100); // In units of IPS + temp32 = temp32 / cyc2; // In units of 0.01 DPC + g_amec->proc[0].core[i_core].dpc = temp32; + + // ------------------------------------------------------ + // Per Core DPS Calculation + // ------------------------------------------------------ + // <amec_formula> + // Result: Calculated dispatched Instructions per Second + // Sensor: None + // Timescale: 2ms + // Units: 0.2Mips + // Min/Max: ? + // Formula: dps_delta = (INST_DISPATCH[t=now] - INST_DISPATCH[t=-2ms]) + // 500 = # of 2ms periods in 1 second + // 50,000 = Convert IPS to 0.2MIPS + // + // DPS(in 0.2Mips) = (dps_delta * 500) / 50,000 + // </amec_formula> + temp32 = (disp2 * AMEC_SMH_PERIODS_IN_1SEC); // In untis of IPS + temp32 = temp32 / 50000; // In units of 0.2Mips (max 327675 Mips for uint16_t) + g_amec->proc[0].core[i_core].dps = temp32; + + // ------------------------------------------------------ + // Per Core IPS Calculation + // ------------------------------------------------------ + // <amec_formula> + // Result: Calculated Instructions per Second + // Sensor: IPS2MSP0C0 + // Timescale: 2ms + // Units: 0.2Mips + // Min/Max: ? + // Formula: + // comp_delta = (INST_COMPLETE[t=now] - INST_COMPLETE[t=-2ms]) + // ticks_delta = (TOD[t=now] - TOD[t=-2ms]) + // MIPS = comp_delta (insns/interval) * (1 interval per ticks_delta 2mhz ticks) * (2M 2mhz ticks / s) / 1M + // = (2* fin2) / ticks_2mhz + // Note: For best resolution do multiply first and division last. + // </amec_formula> + + ticks_2mhz = i_core_data_ptr->empath.tod_2mhz - + g_amec->proc[0].core[i_core].prev_tod_2mhz; + temp32 = (fin2 << 1) / ticks_2mhz; + // See if core is sleeping/winkled + if(l_core_sleep_winkle) + { + temp32 = 0; + } + sensor_update( AMECSENSOR_ARRAY_PTR(IPS2MSP0C0,i_core), (uint16_t) temp32); +} + + +// Function Specification +// +// Name: amec_calc_spurr +// +// Description: Do SPURR calculation. Must run after FreqA is calculated. +// +// Thread: RealTime Loop +// +// End Function Specification +void amec_calc_spurr(uint8_t i_core) +{ + uint16_t l_actual_freq = AMECSENSOR_ARRAY_PTR(FREQA2MSP0C0, i_core)->sample; + uint16_t l_nominal = 2790; + uint32_t temp32; + + // Sanity Check on Freq + if(l_actual_freq < UPPER_LIMIT_PROC_FREQ_MHZ) + { + temp32 = ((uint32_t) (l_actual_freq * 1000) / l_nominal); + + // Scale for SPURR Register (64 = Nominal, 32 = Nom-50%) + temp32 = (temp32 * 64) / 1000; + + sensor_update( AMECSENSOR_ARRAY_PTR(SPURR2MSP0C0,i_core), (uint16_t) temp32); + } +} + +/*----------------------------------------------------------------------------*/ +/* End */ +/*----------------------------------------------------------------------------*/ diff --git a/src/occ_405/amec/amec_sensors_core.h b/src/occ_405/amec/amec_sensors_core.h new file mode 100755 index 0000000..e018925 --- /dev/null +++ b/src/occ_405/amec/amec_sensors_core.h @@ -0,0 +1,60 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ_405/amec/amec_sensors_core.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#ifndef _AMEC_SENSORS_CORE_H +#define _AMEC_SENSORS_CORE_H + +/*----------------------------------------------------------------------------*/ +/* Includes */ +/*----------------------------------------------------------------------------*/ +#include <occ_common.h> +#include <ssx.h> +#include <ssx_app_cfg.h> +#include "amec_external.h" +#include <occ_sys_config.h> + +/*----------------------------------------------------------------------------*/ +/* Defines/Constants */ +/*----------------------------------------------------------------------------*/ +// See bit definition of PM State History Register for OCC +#define OCC_PM_STATE_MASK 0x1F000000 //Mask bits 0:2 of the register +#define OCC_PAST_FAST_SLEEP 0x18000000 //Core has been in fast sleep +#define OCC_PAST_DEEP_SLEEP 0x1C000000 //Core has been in deep sleep +#define OCC_PAST_FAST_WINKLE 0x1E000000 //Core has been in fast winkle +#define OCC_PAST_DEEP_WINKLE 0x1F000000 //Core has been in deep winkle +#define OCC_PAST_CORE_CLK_STOP 0x08000000 //Core has been in an idle state with core clocks stopped + +/*----------------------------------------------------------------------------*/ +/* Structures */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Function Declarations */ +/*----------------------------------------------------------------------------*/ +void amec_update_fast_core_data_sensors(void); + +void amec_update_proc_core_sensors(uint8_t i_core); + +#endif // _AMEC_SENSORS_CORE_H diff --git a/src/occ_405/amec/amec_sensors_fw.c b/src/occ_405/amec/amec_sensors_fw.c new file mode 100644 index 0000000..1bc3811 --- /dev/null +++ b/src/occ_405/amec/amec_sensors_fw.c @@ -0,0 +1,242 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ_405/amec/amec_sensors_fw.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/******************************************************************************/ +/* Includes */ +/******************************************************************************/ +#include <occ_common.h> +#include <ssx.h> +#include <errl.h> // Error logging +#include "sensor.h" +#include "rtls.h" +#include "occ_sys_config.h" +#include "occ_service_codes.h" // for SSX_GENERIC_FAILURE +#include "dcom.h" +#include "proc_data.h" +#include "amec_smh.h" +#include "amec_slave_smh.h" +#include <trac.h> +#include "amec_sys.h" +#include "sensor_enum.h" +#include "amec_service_codes.h" +#include <amec_sensors_fw.h> + +/******************************************************************************/ +/* Globals */ +/******************************************************************************/ + +//************************************************************************* +// Code +//************************************************************************* + +// Function Specification +// +// Name: amec_slv_update_smh_sensors +// +// Description: Update FW Sensors with Amec Slave Timings. +// +// End Function Specification +void amec_slv_update_smh_sensors(int i_smh_state, uint32_t i_duration) +{ + // Update the duration in the fw timing table + G_fw_timing.amess_state = i_smh_state; + G_fw_timing.amess_dur = i_duration; +} + + +// Function Specification +// +// Name: amec_slv_update_gpe_sensors +// +// Description: Update FW Sensors with GPE Engine Timings. Called from +// callback on GPE routine completion. +// +// End Function Specification +void amec_slv_update_gpe_sensors(uint8_t i_gpe_engine) +{ + // Update the duration in the fw timing table + G_fw_timing.gpe_dur[i_gpe_engine] = DURATION_IN_US_UNTIL_NOW_FROM(G_fw_timing.rtl_start_gpe); +} + + +// Function Specification +// +// Name: amec_update_fw_sensors +// +// Description: Updates sensors related to the OCC FW Timings +// +// Thread: RealTime Loop +// +// End Function Specification +#define MAX_CONSEC_TRACE 4 +void amec_update_fw_sensors(void) +{ + errlHndl_t l_err = NULL; + int rc = 0; + int rc2 = 0; + static bool l_first_call = TRUE; + bool l_gpe0_idle, l_gpe1_idle; + static int L_consec_trace_count = 0; + + // ------------------------------------------------------ + // Update OCC Firmware Sensors from last tick + // ------------------------------------------------------ + int l_last_state = G_fw_timing.amess_state; + // RTLtickdur = duration of last tick's RTL ISR (max = 250us) + sensor_update( AMECSENSOR_PTR(RTLtickdur), G_fw_timing.rtl_dur); + // AMEintdur = duration of last tick's AMEC portion of RTL ISR + sensor_update( AMECSENSOR_PTR(AMEintdur), G_fw_timing.ameint_dur); + // AMESSdurX = duration of last tick's AMEC state + if(l_last_state >= NUM_AMEC_SMH_STATES) + { + // Sanity check. Trace this out, even though it should never happen. + TRAC_INFO("AMEC State Invalid, Sensor Not Updated"); + } + else + { + // AMESSdurX = duration of last tick's AMEC state + sensor_update( AMECSENSOR_ARRAY_PTR(AMESSdur0, l_last_state), G_fw_timing.amess_dur); + } + + // ------------------------------------------------------ + // Kick off GPE programs to track WorstCase time in GPE + // and update the sensors. + // ------------------------------------------------------ + if( (NULL != G_fw_timing.gpe0_timing_request) + && (NULL != G_fw_timing.gpe1_timing_request) ) + { + //Check if both GPE engines were able to complete the last GPE job on + //the queue within 1 tick. + l_gpe0_idle = async_request_is_idle(&G_fw_timing.gpe0_timing_request->request); + l_gpe1_idle = async_request_is_idle(&G_fw_timing.gpe1_timing_request->request); + if(l_gpe0_idle && l_gpe1_idle) + { + //reset the consecutive trace count + L_consec_trace_count = 0; + + //Both GPE engines finished on time. Now check if they were + //successful too. + if( async_request_completed(&(G_fw_timing.gpe0_timing_request->request)) + && async_request_completed(&(G_fw_timing.gpe1_timing_request->request)) ) + { + // GPEtickdur0 = duration of last tick's PORE-GPE0 duration + sensor_update( AMECSENSOR_PTR(GPEtickdur0), G_fw_timing.gpe_dur[0]); + // GPEtickdur1 = duration of last tick's PORE-GPE1 duration + sensor_update( AMECSENSOR_PTR(GPEtickdur1), G_fw_timing.gpe_dur[1]); + } + else + { + //This case is expected on the first call of the function. + //After that, this should not happen. + if(!l_first_call) + { + //Note: FFDC for this case is gathered by each task + //responsible for a GPE job. + TRAC_INFO("GPE task idle but GPE task did not complete"); + } + l_first_call = FALSE; + } + + // Update Time used to measure GPE duration. + G_fw_timing.rtl_start_gpe = G_fw_timing.rtl_start; + + // Schedule the GPE Routines that will run and update the worst + // case timings (via callback) after they complete. These GPE + // routines are the last GPE routines added to the queue + // during the RTL tick. + rc = pore_flex_schedule(G_fw_timing.gpe0_timing_request); + rc2 = pore_flex_schedule(G_fw_timing.gpe1_timing_request); + + if(rc || rc2) + { + /* @ + * @errortype + * @moduleid AMEC_UPDATE_FW_SENSORS + * @reasoncode SSX_GENERIC_FAILURE + * @userdata1 return code - gpe0 + * @userdata2 return code - gpe1 + * @userdata4 OCC_NO_EXTENDED_RC + * @devdesc Failure to schedule PORE-GPE poreFlex object for FW timing + * analysis. + */ + l_err = createErrl( + AMEC_UPDATE_FW_SENSORS, //modId + SSX_GENERIC_FAILURE, //reasoncode + OCC_NO_EXTENDED_RC, //Extended reason code + ERRL_SEV_INFORMATIONAL, //Severity + NULL, //Trace Buf + DEFAULT_TRACE_SIZE, //Trace Size + rc, //userdata1 + rc2); //userdata2 + + // commit error log + commitErrl( &l_err ); + } + } + else if(L_consec_trace_count < MAX_CONSEC_TRACE) + { + uint64_t l_dbg0; + uint64_t l_dbg1; + uint64_t l_status; + + // Reset will eventually be requested due to not having power measurement + // data after X ticks, but add some additional FFDC to the trace that + // will tell us what GPE job is currently executing. + if(!l_gpe0_idle) + { + l_dbg1 = in64(PORE_GPE0_DBG1); + l_dbg0 = in64(PORE_GPE0_DBG0); + l_status = in64(PORE_GPE0_STATUS); + TRAC_ERR("GPE0 programs did not complete within one tick. DBG0[0x%08x%08x] DBG1[0x%08x%08x]", + (uint32_t)(l_dbg0 >> 32), + (uint32_t)(l_dbg0 & 0x00000000ffffffffull), + (uint32_t)(l_dbg1 >> 32), + (uint32_t)(l_dbg1 & 0x00000000ffffffffull)); + TRAC_ERR("Additional GPE0 debug data: STATUS[0x%08x%08x]", + (uint32_t)(l_status >> 32), + (uint32_t)(l_status & 0x00000000ffffffffull)); + } + if(!l_gpe1_idle) + { + l_dbg1 = in64(PORE_GPE1_DBG1); + l_dbg0 = in64(PORE_GPE1_DBG0); + l_status = in64(PORE_GPE1_STATUS); + TRAC_ERR("GPE1 programs did not complete within one tick. DBG0[0x%08x%08x] DBG1[0x%08x%08x]", + (uint32_t)(l_dbg0 >> 32), + (uint32_t)(l_dbg0 & 0x00000000ffffffffull), + (uint32_t)(l_dbg1 >> 32), + (uint32_t)(l_dbg1 & 0x00000000ffffffffull)); + TRAC_ERR("Additional GPE1 debug data: STATUS[0x%08x%08x]", + (uint32_t)(l_status >> 32), + (uint32_t)(l_status & 0x00000000ffffffffull)); + } + L_consec_trace_count++; + } + } +} + +/*----------------------------------------------------------------------------*/ +/* End */ +/*----------------------------------------------------------------------------*/ diff --git a/src/occ_405/amec/amec_sensors_fw.h b/src/occ_405/amec/amec_sensors_fw.h new file mode 100755 index 0000000..015cc01 --- /dev/null +++ b/src/occ_405/amec/amec_sensors_fw.h @@ -0,0 +1,53 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ_405/amec/amec_sensors_fw.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#ifndef _AMEC_SENSORS_FW_H +#define _AMEC_SENSORS_FW_H + +/*----------------------------------------------------------------------------*/ +/* Includes */ +/*----------------------------------------------------------------------------*/ +#include <occ_common.h> +#include <ssx.h> +#include <ssx_app_cfg.h> +#include "amec_external.h" + +/*----------------------------------------------------------------------------*/ +/* Defines/Constants */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Function Declarations */ +/*----------------------------------------------------------------------------*/ +// Function that updates the AMEC interim structures with AMEC State Durations +void amec_slv_update_smh_sensors(int i_smh_state, uint32_t i_duration); + +// Function that updates the AMEC interim structures with GPE Engine Durations +void amec_slv_update_gpe_sensors(uint8_t i_gpe_engine); + +// Function that updates the AMEC FW sensors +void amec_update_fw_sensors(void); + +#endif // _AMEC_SENSORS_FW_H diff --git a/src/occ_405/amec/amec_sensors_power.c b/src/occ_405/amec/amec_sensors_power.c new file mode 100755 index 0000000..8d08db3 --- /dev/null +++ b/src/occ_405/amec/amec_sensors_power.c @@ -0,0 +1,668 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/amec/amec_sensors_power.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +/******************************************************************************/ +/* Includes */ +/******************************************************************************/ +#include <occ_common.h> +#include <ssx.h> +#include <errl.h> // Error logging +#include "sensor.h" +#include "rtls.h" +#include "occ_sys_config.h" +#include "occ_service_codes.h" // for SSX_GENERIC_FAILURE +#include "dcom.h" +#include "proc_data.h" +#include "amec_smh.h" +#include "amec_slave_smh.h" +#include <trac.h> +#include "amec_sys.h" +#include "sensor_enum.h" +#include "amec_service_codes.h" +#include <amec_sensors_power.h> +#include <cmdh_snapshot.h> +#include <vrm.h> +#include "amec_oversub.h" + +/******************************************************************************/ +/* Globals */ +/******************************************************************************/ +// This holds the converted ADC Reads +uint32_t G_lastValidAdcValue[MAX_APSS_ADC_CHANNELS] = {0}; + +extern thrm_fru_data_t G_thrm_fru_data[DATA_FRU_MAX]; + +// There are only MAX_APSS_ADC_CHANNELS channels. Therefore if the channel value +// is greater then the MAX, then there was no channel associated with the function id. +#define ADC_CONVERTED_VALUE(i_chan) \ + ((i_chan < MAX_APSS_ADC_CHANNELS) ? G_lastValidAdcValue[i_chan] : 0) + +extern uint8_t G_occ_interrupt_type; + +//************************************************************************* +// Code +//************************************************************************* + +// Function Specification +// +// Name: amec_sensor_from_apss_adc +// +// Description: Calculates sensor from raw ADC value +// +// Thread: RealTime Loop +// +// End Function Specification +uint32_t amec_value_from_apss_adc(uint8_t i_chan) +{ + uint16_t l_raw = 0; + uint32_t l_temp = 0; + uint32_t l_gain = 0; + uint32_t l_offset = 0; + + if(i_chan != SYSCFG_INVALID_ADC_CHAN) + { + /* + * The APSS value is in mV or mA depending on the channel and the raw + * reading from the APSS must be decoded using the following info: + * + * APSS LSB: Vref/4096 + * Full scale: (Vref - LSB) = 0xFFF + * Midscale: Vref/2 = 0x800 + * Midscale - 1 LSB: Vref/2 - 1 LSB, 0x7FF + * Zero: 0V, 0x000 + * Any voltage at or above Vref will will result in an ADC channel value + * of 0xFFF + * + * Our APSS has Vref pinned to 2.048 V + * LSB: 0.5 mV (minimum ADC resolution) + * Full scale: 2047.5 mV (0xFFF) + * Midscale: 1024 mV (0x800) + * Zero: 0V, 0x000 + * + * To get the right mV reading from the raw APSS data all we need to do + * is divide the raw 12 bit ADC code by 2. The same logic applies if + * the channel is measuring power in mA. + * + * If there is an offset it will bein mV or mA depending upon the sensor + * and it needs to be added or subtracted from the raw value depending + * on it's signedness. Negative offsets are stored in 2's complement + * form. + * + * The gain values will be multiplied by 1000 in TMGT before being sent + * in the sysconfig packet. Raw gain from the MRW values are in A/V and + * TMGT will multiply this by 1000 before sending in the sysconfig + * packet to preserve precision. That makes the gain units mA/V. + * + * To apply the gain multiply the sysconfig value against the converted + * APSS voltage. + * + * Applying the gain to the converted APSS data gives (mV . mA)/V so we + * divide by 1000 to reduce the result to mA. This is the unit that + * is returned to the caller. For example: + * raw APSS value: 0x800 + * converted value: raw/2 = 1024 mV + * gain from MRW: 10.00 A/V + * Converted gain: 10,000 mA/V + * gain adjusted output: 1024mV * 10,000 mA/V = 10,240,000 (mV . mA)/V + * Reduced value: adjusted/1000 = 10,240 mA + * + * Note that in the case of the remote ground and 12V sense the gain + * values are in V/V so the returned value is actually in mVs. + * + * Max returnable value is 4,294,967,295 mA or approx. 4.3 MA + */ + + // Get ADC Mapping calibration info for this entity. + l_gain = G_sysConfigData.apss_cal[i_chan].gain; + l_offset = G_sysConfigData.apss_cal[i_chan].offset; + + // Read Raw Value in mA (divide masked channel data by 2) + l_raw = (G_dcom_slv_inbox_rx.adc[i_chan] & APSS_12BIT_ADC_MASK)/2; + // Apply offset and gain + if (l_offset & 0x80000000) + { + // Negative offset + l_raw -= (~l_offset + 1); + } + else + { + l_raw += l_offset; + } + //Check to see if l_raw is negative. If so, set raw to 0 + if (l_raw & 0x8000) + { + l_raw = 0; + } + + l_temp = ((uint32_t)l_raw * l_gain); + // Reduce value back to mA or mV + l_temp /= 1000; + } + + AMEC_DBG("APSS ADC info: chan=%d, raw=0x%04x, offset=%d, gain=%d calibrated output=%d\n", + i_chan, l_raw, l_offset, l_gain, l_temp); + + return l_temp; +} + +#define ADCMULT_TO_UNITS 1000000 +#define ADCMULT_ROUND ADCMULT_TO_UNITS/2 +// Function Specification +// +// Name: amec_update_channel_sensor +// +// Description: Used to calculate power based on raw data obtained from APSS +// +// End Function Specification +void amec_update_channel_sensor(const uint8_t i_channel) +{ + if (i_channel < MAX_APSS_ADC_CHANNELS) + { + if(AMECSENSOR_PTR(PWRAPSSCH0 + i_channel)->ipmi_sid != 0) + { + uint32_t l_bulk_voltage = ADC_CONVERTED_VALUE(G_sysConfigData.apss_adc_map.sense_12v); + + uint32_t l_temp32 = ADC_CONVERTED_VALUE(i_channel); + l_temp32 = ((l_temp32 * l_bulk_voltage)+ADCMULT_ROUND)/ADCMULT_TO_UNITS; + sensor_update(AMECSENSOR_PTR(PWRAPSSCH0 + i_channel), (uint16_t) l_temp32); + } + } +} + + +// Function Specification +// +// Name: amec_update_apss_sensors +// +// Description: Calculates sensor from raw ADC values obtained from APSS +// +// Thread: RealTime Loop +// +// End Function Specification +void amec_update_apss_sensors(void) +{ + /* + * Removed fake apss config data, do not reuse, the old hardcoded + * values will not work with the new code used in processing APSS channel + * data. + * Code is in place to receive command code 0x21 SET CONFIG DATA + * which should popluate the ADC and GPIO maps as well as the APSS + * calibration data for all 16 ADC channels. + */ + // Need to check to make sure APSS data has been received + // via slave inbox first + if (G_slv_inbox_received) + { + uint8_t l_proc = G_pob_id.module_id; + uint32_t temp32 = 0; + uint8_t l_idx = 0; + uint32_t l_bulk_current_sum = 0; + + // ---------------------------------------------------- + // Convert all ADC Channels immediately + // ---------------------------------------------------- + for (l_idx = 0; l_idx < MAX_APSS_ADC_CHANNELS; l_idx++) + { + // These values returned are gain adjusted. The APSS readings for + // the remote ground and 12V sense are returned in mVs, all other + // readings are treated as mAs. + G_lastValidAdcValue[l_idx] = amec_value_from_apss_adc(l_idx); + + // Add up all channels now, we will subtract ones later that don't + // count towards the system power + l_bulk_current_sum += G_lastValidAdcValue[l_idx]; + } + + //Only for FSP-LESS systems do we update channel sensors. + if (FSP_SUPPORTED_OCC != G_occ_interrupt_type) + { + amec_update_channel_sensor(G_sysConfigData.apss_adc_map.memory[l_proc][0]); + for (l_idx = 1; l_idx < MAX_PROC_CENT_CH; l_idx++) + { + amec_update_channel_sensor(G_sysConfigData.apss_adc_map.memory[l_proc][l_idx]); + } + amec_update_channel_sensor(G_sysConfigData.apss_adc_map.vdd[l_proc]); + amec_update_channel_sensor(G_sysConfigData.apss_adc_map.vcs_vio_vpcie[l_proc]); + + if (OCC_MASTER == G_occ_role) + { + amec_update_channel_sensor(G_sysConfigData.apss_adc_map.io[0]); + amec_update_channel_sensor(G_sysConfigData.apss_adc_map.io[1]); + amec_update_channel_sensor(G_sysConfigData.apss_adc_map.io[2]); + amec_update_channel_sensor(G_sysConfigData.apss_adc_map.fans[0]); + amec_update_channel_sensor(G_sysConfigData.apss_adc_map.fans[1]); + amec_update_channel_sensor(G_sysConfigData.apss_adc_map.storage_media[0]); + amec_update_channel_sensor(G_sysConfigData.apss_adc_map.storage_media[1]); + amec_update_channel_sensor(G_sysConfigData.apss_adc_map.total_current_12v); + amec_update_channel_sensor(G_sysConfigData.apss_adc_map.mem_cache); + amec_update_channel_sensor(G_sysConfigData.apss_adc_map.gpu); + } + } + + // -------------------------------------------------------------- + // Convert 12Vsense into interim value - this has to happen first + // -------------------------------------------------------------- + uint32_t l_bulk_voltage = ADC_CONVERTED_VALUE(G_sysConfigData.apss_adc_map.sense_12v); + + // ---------------------------------------------------------- + // Convert Raw Vdd/Vcs/Vio/Vpcie Power from APSS into sensors + // ---------------------------------------------------------- + // Some sensor values are in Watts so after getting the mA readings we + // multiply by the bulk voltage (mVs) which requires us to then divide + // by 1000000 to get W (A.V), ie. + // divide by 1000 to get it back to milliUnits (0.001) + // divide by 10000 to get it to centiUnits (0.01) + // divide by 100000 to get it to deciUnits (0.1) + // divide by 1000000 to get it to Units (1) + + //Update channel specific sensors based on saved pairing between function Ids and Channels. + + uint32_t l_vdd = ADC_CONVERTED_VALUE(G_sysConfigData.apss_adc_map.vdd[l_proc]); + uint32_t l_vcs_vio_vpcie = ADC_CONVERTED_VALUE(G_sysConfigData.apss_adc_map.vcs_vio_vpcie[l_proc]); + temp32 = ((l_vcs_vio_vpcie + l_vdd) * l_bulk_voltage)/ADCMULT_TO_UNITS; + sensor_update(AMECSENSOR_PTR(PWR250USP0), (uint16_t) temp32); + + // Save off the combined power from all modules + for (l_idx=0; l_idx < MAX_NUM_CHIP_MODULES; l_idx++) + { + uint32_t l_vd = ADC_CONVERTED_VALUE(G_sysConfigData.apss_adc_map.vdd[l_idx]); + uint32_t l_vpcie = ADC_CONVERTED_VALUE(G_sysConfigData.apss_adc_map.vcs_vio_vpcie[l_idx]); + g_amec->proc_snr_pwr[l_idx] = ((l_vpcie + l_vd) * l_bulk_voltage)/ADCMULT_TO_UNITS; + } + + // All readings from APSS come back as milliUnits, so if we want + // to convert one, we need to + // divide by 1 to get it back to milliUnits (0.001) + // divide by 10 to get it to centiUnits (0.01) + // divide by 100 to get it to deciUnits (0.1) + // divide by 1000 to get it to Units (1) + // Vdd has both a power and a current sensor, we convert the Vdd power + // to Watts and the current to centiAmps + temp32 = ((l_vdd * l_bulk_voltage)+ADCMULT_ROUND)/ADCMULT_TO_UNITS; + sensor_update( AMECSENSOR_PTR(PWR250USVDD0), (uint16_t)temp32); + temp32 = ((l_vcs_vio_vpcie * l_bulk_voltage)+ADCMULT_ROUND)/ADCMULT_TO_UNITS; + sensor_update( AMECSENSOR_PTR(PWR250USVCS0), (uint16_t)temp32); + + // ---------------------------------------------------- + // Convert Other Raw Misc Power from APSS into sensors + // ---------------------------------------------------- + + // Fans: Add up all Fan channels + temp32 = ADC_CONVERTED_VALUE(G_sysConfigData.apss_adc_map.fans[0]); + temp32 += ADC_CONVERTED_VALUE(G_sysConfigData.apss_adc_map.fans[1]); + temp32 = ((temp32 * l_bulk_voltage)+ADCMULT_ROUND)/ADCMULT_TO_UNITS; + sensor_update( AMECSENSOR_PTR(PWR250USFAN), (uint16_t)temp32); + + // I/O: Add up all I/O channels + temp32 = ADC_CONVERTED_VALUE(G_sysConfigData.apss_adc_map.io[0]); + temp32 += ADC_CONVERTED_VALUE(G_sysConfigData.apss_adc_map.io[1]); + temp32 += ADC_CONVERTED_VALUE(G_sysConfigData.apss_adc_map.io[2]); + temp32 = ((temp32 * l_bulk_voltage)+ADCMULT_ROUND)/ADCMULT_TO_UNITS; + sensor_update( AMECSENSOR_PTR(PWR250USIO), (uint16_t)temp32); + + // Memory: Add up all channels for the same processor. + temp32 = ADC_CONVERTED_VALUE(G_sysConfigData.apss_adc_map.memory[l_proc][0]); + temp32 += ADC_CONVERTED_VALUE(G_sysConfigData.apss_adc_map.memory[l_proc][1]); + temp32 += ADC_CONVERTED_VALUE(G_sysConfigData.apss_adc_map.memory[l_proc][2]); + temp32 += ADC_CONVERTED_VALUE(G_sysConfigData.apss_adc_map.memory[l_proc][3]); + //Only for FSP-LESS systems do we add in Centaur power because it is measured on its own A/D channel, but is part of memory power + if (FSP_SUPPORTED_OCC != G_occ_interrupt_type) + { + temp32 += ADC_CONVERTED_VALUE(G_sysConfigData.apss_adc_map.mem_cache); + } + temp32 = ((temp32 * l_bulk_voltage)+ADCMULT_ROUND)/ADCMULT_TO_UNITS; + sensor_update( AMECSENSOR_PTR(PWR250USMEM0), (uint16_t)temp32); + + // Save off the combined power from all memory + for (l_idx=0; l_idx < MAX_NUM_CHIP_MODULES; l_idx++) + { + uint32_t l_temp = ADC_CONVERTED_VALUE(G_sysConfigData.apss_adc_map.memory[l_idx][0]); + l_temp += ADC_CONVERTED_VALUE(G_sysConfigData.apss_adc_map.memory[l_idx][1]); + l_temp += ADC_CONVERTED_VALUE(G_sysConfigData.apss_adc_map.memory[l_idx][2]); + l_temp += ADC_CONVERTED_VALUE(G_sysConfigData.apss_adc_map.memory[l_idx][3]); + g_amec->mem_snr_pwr[l_idx] = ((l_temp * l_bulk_voltage)+ADCMULT_ROUND)/ADCMULT_TO_UNITS; + } + + // Storage/Media + temp32 = ADC_CONVERTED_VALUE(G_sysConfigData.apss_adc_map.storage_media[0]); + temp32 += ADC_CONVERTED_VALUE(G_sysConfigData.apss_adc_map.storage_media[1]); + temp32 = ((temp32 * l_bulk_voltage)+ADCMULT_ROUND)/ADCMULT_TO_UNITS; + sensor_update( AMECSENSOR_PTR(PWR250USSTORE), (uint16_t)temp32); + + // GPU adapter + temp32 = ADC_CONVERTED_VALUE(G_sysConfigData.apss_adc_map.gpu); + temp32 = ((temp32 * l_bulk_voltage)+ADCMULT_ROUND)/ADCMULT_TO_UNITS; + sensor_update( AMECSENSOR_PTR(PWR250USGPU), (uint16_t)temp32); + + // ---------------------------------------------------- + // Convert Raw Bulk Power from APSS into sensors + // ---------------------------------------------------- + // We don't get this adc channel in some systems, we have to add it manually. + // With valid sysconfig data the code here should automatically use what + // is provided by the APSS if it is available, or manually sum it up if not. + temp32 = ADC_CONVERTED_VALUE(G_sysConfigData.apss_adc_map.total_current_12v); + temp32 = ((temp32 * l_bulk_voltage)+ADCMULT_ROUND)/ADCMULT_TO_UNITS; + + // To calculated the total 12V current based on a sum of all ADC channels, + // Subract adc channels that don't measure power + l_bulk_current_sum -= ADC_CONVERTED_VALUE(G_sysConfigData.apss_adc_map.sense_12v); + l_bulk_current_sum -= ADC_CONVERTED_VALUE(G_sysConfigData.apss_adc_map.remote_gnd); + + // If we don't have a ADC channel that measures the bulk 12v power, use + // the ADC sum instead + if(0 == temp32) + { + temp32 = ((l_bulk_current_sum * l_bulk_voltage)+ADCMULT_ROUND)/ADCMULT_TO_UNITS; + } + sensor_update(AMECSENSOR_PTR(PWR250US), (uint16_t)temp32); + + // Calculate average frequency of all OCCs. + uint32_t l_allOccAvgFreqOver250us = 0; + uint8_t l_presentOCCs = 0; + uint8_t l_occCount = 0; + + // Add up the average freq from all OCCs. + for (l_occCount = 0; l_occCount < MAX_OCCS; l_occCount++) + { + if (G_sysConfigData.is_occ_present & (1<< l_occCount)) + { + l_allOccAvgFreqOver250us += G_dcom_slv_outbox_rx[l_occCount].freqa2msp0; + l_presentOCCs++; + } + } + //Calculate average of all the OCCs. + l_allOccAvgFreqOver250us /= l_presentOCCs; + + // Save the max and min pwr250us sensors and keep an accumulator of the + // average frequency over 30 seconds. + if (g_pwr250us_over30sec.count == 0) + { + //The counter has been reset, therefore initialize the stored values. + g_pwr250us_over30sec.max = (uint16_t) temp32; + g_pwr250us_over30sec.min = (uint16_t) temp32; + g_pwr250us_over30sec.freqaAccum = l_allOccAvgFreqOver250us; + } + else + { + //Check for max. + if (temp32 > g_pwr250us_over30sec.max) + { + g_pwr250us_over30sec.max = (uint16_t) temp32; + } + //Check for min. + if (temp32 < g_pwr250us_over30sec.min) + { + g_pwr250us_over30sec.min = (uint16_t) temp32; + } + //Average frequency accumulator. + g_pwr250us_over30sec.freqaAccum += l_allOccAvgFreqOver250us; + } + + //Count of number of updates. + g_pwr250us_over30sec.count++; + + // ---------------------------------------------------- + // Clear Flag to indicate that AMEC has received the data. + // ---------------------------------------------------- + G_slv_inbox_received = FALSE; + } + else + { + // Skip it...AMEC Health Monitor will figure out we didn't + // update this sensor. + } +} + + +// Function Specification +// +// Name: amec_update_vrm_sensors +// +// Description: Updates sensors that use data from the VRMs +// (e.g., VR_FAN, FANS_FULL_SPEED, VR_HOT). +// +// Thread: RealTime Loop +// +// End Function Specification +void amec_update_vrm_sensors(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + int l_rc = 0; + int l_vrfan = 0; + int l_softoc = 0; + int l_minus_np1_regmode = 0; + int l_minus_n_regmode = 0; + static uint8_t L_error_count = 0; + uint8_t l_pin_value = 1; // active low, so set default to high + uint8_t l_vrhot_count = 0; + errlHndl_t l_err = NULL; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + // Check if we have access to SPIVID. In DCMs only Master OCC has access to + // the SPIVID. + if (G_dcm_occ_role == OCC_DCM_MASTER) + { + // VR_FAN and SOFT_OC come from SPIVID + l_rc = vrm_read_state(SPIVRM_PORT(0), + &l_minus_np1_regmode, + &l_minus_n_regmode, + &l_vrfan, + &l_softoc); + + if (l_rc == 0) + { + // Update the VR_FAN sensor + sensor_update( AMECSENSOR_PTR(VRFAN250USPROC), (uint16_t)l_vrfan ); + + // Clear our error count and the 'read failure' flag (since we can + // read VR_FAN signal) + L_error_count = 0; + G_thrm_fru_data[DATA_FRU_VRM].read_failure = 0; + + // No longer reading gpio from APSS in GA1 due to instability in + // APSS composite mode + //apss_gpio_get(l_pin, &l_pin_value); + + // VR_HOT sensor is a counter of number of times the VRHOT signal + // has been asserted + l_vrhot_count = AMECSENSOR_PTR(VRHOT250USPROC)->sample; + + // Check if VR_FAN is asserted AND if 'fans_full_speed' GPIO is ON. + // Note that this GPIO is active low. + if (AMECSENSOR_PTR(VRFAN250USPROC)->sample && !(l_pin_value)) + { + // VR_FAN is asserted and 'fans_full_speed' GPIO is ON, + // then increment our VR_HOT counter + if (l_vrhot_count < g_amec->vrhotproc.setpoint) + { + l_vrhot_count++; + } + } + else + { + // Reset our VR_HOT counter + l_vrhot_count = 0; + } + sensor_update(AMECSENSOR_PTR(VRHOT250USPROC), l_vrhot_count); + } + else + { + // Increment our error count + L_error_count++; + + // Don't allow the error count to wrap + if (L_error_count == 0) + { + L_error_count = 0xFF; + } + + // Log an error if we exceeded our number of fail-to-read sensor + if ((L_error_count == g_amec->proc[0].vrfan_error_count) && + (g_amec->proc[0].vrfan_error_count != 0xFF)) + { + TRAC_ERR("amec_update_vrm_sensors: Failed to read VR_FAN for %u consecutive times!", + L_error_count); + + // Also, inform the thermal thread to send a cooling request + G_thrm_fru_data[DATA_FRU_VRM].read_failure = 1; + + /* @ + * @errortype + * @moduleid AMEC_HEALTH_CHECK_VRFAN_TIMEOUT + * @reasoncode VRM_VRFAN_TIMEOUT + * @userdata1 timeout value + * @userdata2 0 + * @userdata4 OCC_NO_EXTENDED_RC + * @devdesc Failed to read VR_FAN signal from regulator. + * + */ + l_err = createErrl(AMEC_HEALTH_CHECK_VRFAN_TIMEOUT, //modId + VRM_VRFAN_TIMEOUT, //reasoncode + OCC_NO_EXTENDED_RC, //Extended reason code + ERRL_SEV_PREDICTIVE, //Severity + NULL, //Trace Buf + DEFAULT_TRACE_SIZE, //Trace Size + g_amec->thermaldimm.temp_timeout, //userdata1 + 0); //userdata2 + + // Callout backplane for this VRM error + addCalloutToErrl(l_err, + ERRL_CALLOUT_TYPE_HUID, + G_sysConfigData.backplane_huid, + ERRL_CALLOUT_PRIORITY_MED); + + // Commit the error + commitErrl(&l_err); + } + } + } + + if( 1 ) + { + sensor_update( AMECSENSOR_PTR(VRFAN250USMEM), 0 ); + sensor_update( AMECSENSOR_PTR(VRHOT250USMEM), 0 ); + } +} + +// Function Specification +// +// Name: amec_update_external_voltage +// +// Description: Measure actual external voltage +// +// Thread: RealTime Loop +// +// End Function Specification +void amec_update_external_voltage() +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + uint32_t l_data = 0; + uint16_t l_temp = 0; + uint16_t l_vdd = 0; + uint16_t l_vcs = 0; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + // Collect the external voltage data + l_data = in32(PMC_GLOBAL_ACTUAL_VOLTAGE_REG); + + // Extract the Vdd vid code and convert to voltage + l_temp = (l_data & 0xFF000000) >>24; + l_vdd = 16125 - ((uint32_t)l_temp * 625)/10; + + // Extract the Vcs vid code and convert to voltage + l_temp = (l_data & 0x00FF0000) >>16; + l_vcs = 16125 - ((uint32_t)l_temp * 625)/10; + + sensor_update( AMECSENSOR_PTR(VOLT250USP0V0), (uint16_t) l_vdd); + sensor_update( AMECSENSOR_PTR(VOLT250USP0V1), (uint16_t) l_vcs); +} + +// Function Specification +// +// Name: amec_update_current_sensor +// +// Description: Estimates Vdd output current based on input power and Vdd voltage setting. +// Compute CUR250USVDD0 (current out of Vdd regulator) +// +// Flow: +// +// Thread: RealTime Loop +// +// Changedby: +// +// Task Flags: +// +// End Function Specification +void amec_update_current_sensor(void) +{ + uint32_t result32; //temporary result + uint16_t l_pow_reg_input_dW = AMECSENSOR_PTR(PWR250USVDD0)->sample * 10; // convert to dW by *10. + uint16_t l_vdd_reg = AMECSENSOR_PTR(VOLT250USP0V0)->sample; + uint32_t l_pow_reg_output_mW; + uint32_t l_curr_output; + + + /* Step 1 */ + + // 1. Get PWR250USVDD0 (the input power to regulator) + // 2. Look up efficiency using PWR250USVDD0 as index (and interpolate) + // 3. Calculate output power = PWR250USVDD0 * efficiency + // 4. Calculate output current = output power / Vdd set point + + /* Determine regulator efficiency */ + // use 85% efficiency all the time + result32 = 8500; + + // Compute regulator output power. out = in * efficiency + // in: min=0W max=300W = 3000dW + // eff: min=0 max=10000=100% (.01% units) + // p_out: max=3000dW * 10000 = 30,000,000 (dW*0.0001) < 2^25, fits in 25 bits + l_pow_reg_output_mW = (uint32_t)l_pow_reg_input_dW * (uint32_t)result32; + // Scale up p_out by 10x to give better resolution for the following division step + // p_out: max=30M (dW*0.0001) in 25 bits + // * 10 = 300M (dW*0.00001) in 29 bits + l_pow_reg_output_mW *= 10; + // Compute current out of regulator. curr_out = power_out (*10 scaling factor) / voltage_out + // p_out: max=300M (dW*0.00001) in 29 bits + // v_out: min=5000 (0.0001 V) max=16000(0.0001 V) in 14 bits + // i_out: max = 300M/5000 = 60000 (dW*0.00001/(0.0001V)= 0.01A), in 16 bits. + // VOLT250USP0V0 in units of 0.0001 V = 0.1 mV. (multiply by 0.1 to get mV) + l_curr_output = l_pow_reg_output_mW / l_vdd_reg; + sensor_update(AMECSENSOR_PTR(CUR250USVDD0), l_curr_output); + +} + +/*----------------------------------------------------------------------------*/ +/* End */ +/*----------------------------------------------------------------------------*/ diff --git a/src/occ_405/amec/amec_sensors_power.h b/src/occ_405/amec/amec_sensors_power.h new file mode 100755 index 0000000..fd69ce8 --- /dev/null +++ b/src/occ_405/amec/amec_sensors_power.h @@ -0,0 +1,61 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/amec/amec_sensors_power.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#ifndef _AMEC_SENSORS_POWER_H +#define _AMEC_SENSORS_POWER_H + +/*----------------------------------------------------------------------------*/ +/* Includes */ +/*----------------------------------------------------------------------------*/ +#include <occ_common.h> +#include <ssx.h> +#include <ssx_app_cfg.h> +#include "amec_external.h" + +/*----------------------------------------------------------------------------*/ +/* Defines/Constants */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Function Declarations */ +/*----------------------------------------------------------------------------*/ + +// Function that is called by AMEC State Machine that will update the AMEC +// sensors for data that comes from the APSS (Power Data from APSS ADCs) +void amec_update_apss_sensors(void); + +// Function that is called by AMEC State Machine that will update the AMEC +// sensors for data that comes from the SPIVID chip (VR_FAN, SoftOC) +void amec_update_vrm_sensors(void); + +// Function that is called by AMEC State Machine that will update the AMEC +// sensors for external voltage measurement +void amec_update_external_voltage(void); + +// Function that is called by AMEC State Machine that will update the AMEC +// sensor for the Vdd output current estimation. +void amec_update_current_sensor(void); + +#endif // _AMEC_SENSORS_POWER_H diff --git a/src/occ_405/amec/amec_service_codes.h b/src/occ_405/amec/amec_service_codes.h new file mode 100755 index 0000000..c93cc25 --- /dev/null +++ b/src/occ_405/amec/amec_service_codes.h @@ -0,0 +1,73 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ_405/amec/amec_service_codes.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#ifndef _AMEC_SERVICE_CODES_H_ +#define _AMEC_SERVICE_CODES_H_ + +/*----------------------------------------------------------------------------*/ +/* Includes */ +/*----------------------------------------------------------------------------*/ +#include <comp_ids.h> + +/*----------------------------------------------------------------------------*/ +/* Constants */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Globals */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Defines */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +/* Typedef / Enum */ +/*----------------------------------------------------------------------------*/ +enum occAmecModuleId +{ + AMEC_INITIALIZE_FW_SENSORS = AMEC_COMP_ID | 0x00, + AMEC_UPDATE_FW_SENSORS = AMEC_COMP_ID | 0x01, + AMEC_VECTORIZE_FW_SENSORS = AMEC_COMP_ID | 0x02, + AMEC_AMESTER_INTERFACE = AMEC_COMP_ID | 0x03, + AMEC_PCAP_CONN_OC_CONTROLLER = AMEC_COMP_ID | 0x04, + AMEC_MST_CHECK_PCAPS_MATCH = AMEC_COMP_ID | 0x05, + AMEC_MST_CHECK_UNDER_PCAP = AMEC_COMP_ID | 0x06, + AMEC_SLAVE_CHECK_PERFORMANCE = AMEC_COMP_ID | 0x07, + AMEC_HEALTH_CHECK_PROC_TEMP = AMEC_COMP_ID | 0x08, + AMEC_HEALTH_CHECK_DIMM_TEMP = AMEC_COMP_ID | 0x09, + AMEC_HEALTH_CHECK_CENT_TEMP = AMEC_COMP_ID | 0x10, + AMEC_HEALTH_CHECK_DIMM_TIMEOUT = AMEC_COMP_ID | 0x11, + AMEC_HEALTH_CHECK_CENT_TIMEOUT = AMEC_COMP_ID | 0x12, + AMEC_HEALTH_CHECK_VRFAN_TIMEOUT = AMEC_COMP_ID | 0x13, + AMEC_HEALTH_CHECK_PROC_TIMEOUT = AMEC_COMP_ID | 0x14, + AMEC_HEALTH_CHECK_PROC_VRHOT = AMEC_COMP_ID | 0x15, +}; + +/*----------------------------------------------------------------------------*/ +/* Function Prototypes */ +/*----------------------------------------------------------------------------*/ + +#endif /* #ifndef _AMEC_SERVICE_CODES_H_ */ diff --git a/src/occ_405/amec/amec_slave_smh.c b/src/occ_405/amec/amec_slave_smh.c new file mode 100755 index 0000000..1b5dbc0 --- /dev/null +++ b/src/occ_405/amec/amec_slave_smh.c @@ -0,0 +1,768 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/amec/amec_slave_smh.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +//************************************************************************* +// Includes +//************************************************************************* +#include <occ_common.h> +#include <ssx.h> +#include <errl.h> // Error logging +#include "sensor.h" +#include "rtls.h" +#include "occ_sys_config.h" +#include "occ_service_codes.h" // for SSX_GENERIC_FAILURE +#include "dcom.h" +#include "proc_data.h" +#include "proc_data_control.h" +#include "amec_smh.h" +#include "amec_slave_smh.h" +#include <trac.h> +#include "amec_sys.h" +#include "sensor_enum.h" +#include "amec_service_codes.h" +#include <amec_sensors_core.h> +#include <amec_sensors_power.h> +#include <amec_sensors_centaur.h> +#include <amec_sensors_fw.h> +#include <amec_freq.h> +#include <amec_data.h> +#include <centaur_data.h> +#include <amec_amester.h> +#include <amec_oversub.h> +#include <amec_health.h> +#include <amec_analytics.h> +#include <common.h> + +//************************************************************************* +// Externs +//************************************************************************* +extern dcom_slv_inbox_t G_dcom_slv_inbox_rx; +extern uint8_t G_vrm_present; + +//************************************************************************* +// Macros +//************************************************************************* + +//************************************************************************* +// Defines/Enums +//************************************************************************* +smh_state_t G_amec_slv_state = {AMEC_INITIAL_STATE, + AMEC_INITIAL_STATE, + AMEC_INITIAL_STATE}; + +// Number of ticks for periodically updating VRM-related data +#define AMEC_UPDATE_VRM_TICKS 4000 + +// -------------------------------------------------------- +// AMEC Slave State 6 Substate Table +// -------------------------------------------------------- +// Each function inside this state table runs once every 16ms. +// +// No Substates +// +const smh_tbl_t amec_slv_state_6_substate_table[AMEC_SMH_STATES_PER_LVL] = +{ + {amec_slv_substate_6_0, NULL}, + {amec_slv_substate_6_1, NULL}, + {amec_slv_substate_6_2, NULL}, + {amec_slv_substate_6_3, NULL}, + {amec_slv_substate_6_4, NULL}, + {amec_slv_substate_6_5, NULL}, + {amec_slv_substate_6_6, NULL}, + {amec_slv_substate_6_7, NULL}, +}; + +// -------------------------------------------------------- +// AMEC Slave State 7 Substate Table +// -------------------------------------------------------- +// Each function inside this state table runs once every 16ms. +// +// No Substates +// +const smh_tbl_t amec_slv_state_7_substate_table[AMEC_SMH_STATES_PER_LVL] = +{ + {amec_slv_substate_7_0, NULL}, + {amec_slv_substate_7_1, NULL}, + {amec_slv_substate_7_2, NULL}, + {amec_slv_substate_7_3, NULL}, + {amec_slv_substate_7_4, NULL}, + {amec_slv_substate_7_5, NULL}, + {amec_slv_substate_7_6, NULL}, + {amec_slv_substate_7_7, NULL}, +}; + +// -------------------------------------------------------- +// Main AMEC Slave State Table +// -------------------------------------------------------- +// Each function inside this state table runs once every 2ms. +// +// No Substates +// +const smh_tbl_t amec_slv_state_table[AMEC_SMH_STATES_PER_LVL] = +{ + {amec_slv_state_0, NULL}, + {amec_slv_state_1, NULL}, + {amec_slv_state_2, NULL}, + {amec_slv_state_3, NULL}, + {amec_slv_state_4, NULL}, + {amec_slv_state_5, NULL}, + {amec_slv_state_6, amec_slv_state_6_substate_table}, + {amec_slv_state_7, amec_slv_state_7_substate_table}, +}; + +// This sets up the function pointer that will be called to update the +// fw timings when the AMEC Slave State Machine finishes. +smh_state_timing_t G_amec_slv_state_timings = {amec_slv_update_smh_sensors}; + +//************************************************************************* +// Structures +//************************************************************************* + +//************************************************************************* +// Globals +//************************************************************************* + +//************************************************************************* +// Function Prototypes +//************************************************************************* + +//************************************************************************* +// Functions +//************************************************************************* + +// Function Specification +// +// Name: amec_slv_check_apss_fail +// +// Description: This function checks if there are APSS failures and takes +// action accordingly. If there are APSS failures, it will lower the Pmax_rail +// to nominal. Else, if will release the Pmax_rail. +// +// End Function Specification +void amec_slv_check_apss_fail(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + uint32_t l_pmax_rail_freq = g_amec->proc[0].pwr_votes.apss_pmax_clip_freq; + Pstate l_pstate = 0; + static bool L_lower_pmax_rail = FALSE; + static bool L_raise_pmax_rail = TRUE; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + if (G_apss_lower_pmax_rail == TRUE) + { + if (L_lower_pmax_rail == FALSE) + { + // Lower the Pmax_rail to nominal + l_pmax_rail_freq = G_sysConfigData.sys_mode_freq.table[OCC_MODE_NOMINAL]; + l_pstate = proc_freq2pstate(l_pmax_rail_freq); + + // Set the Pmax_rail register via OCI write + amec_oversub_pmax_clip(l_pstate); + + L_lower_pmax_rail = TRUE; + L_raise_pmax_rail = FALSE; + } + } + else + { + if (L_raise_pmax_rail == FALSE) + { + // Raise the Pmax rail back + l_pmax_rail_freq = G_sysConfigData.sys_mode_freq.table[OCC_MODE_TURBO]; + l_pstate = proc_freq2pstate(l_pmax_rail_freq); + + // Set the Pmax_rail register via OCI write + amec_oversub_pmax_clip(l_pstate); + + L_lower_pmax_rail = FALSE; + L_raise_pmax_rail = TRUE; + } + } + + // Store the frequency vote for the voting box + g_amec->proc[0].pwr_votes.apss_pmax_clip_freq = l_pmax_rail_freq; +} + +// Function Specification +// +// Name: amec_slv_pstate_uplift_check +// +// Description: This function checks if the Global Pstate table needs to be +// modified with a voltage uplift. If an uplift has been requested, it will +// proceed to update every entry of the table. +// +// End Function Specification +void amec_slv_pstate_uplift_check(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + Pstate l_pmin = 0; + Pstate l_pmax = 0; + uint16_t i = 0; + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + + // Check if a new uplift request for Vdd has been made + if ((G_sysConfigData.vdd_vid_delta != 0) || + (G_sysConfigData.vcs_vid_delta != 0)) + { + TRAC_INFO("Updating Global Pstate table with requested uplift values!"); + + // STEP 1: + // Prevent any Pstate changes by locking the PMC Rail so that + // Pmax_rail = Pmin_rail + 1 + l_pmin = gpst_pmin(&G_global_pstate_table) + 1; + + // Set the Pmax_rail register via OCI write + amec_oversub_pmax_clip(l_pmin); + + // STEP 2: + // Update all entries of the Global Pstate table using uplift provided + for (i=0; i<G_global_pstate_table.entries; i++) + { + // Modify the fields associated with Vdd. Per HW procedure team, + // the evid_vdd_eff and max_reg_vdd should get decremented + G_global_pstate_table.pstate[i].fields.evid_vdd += G_sysConfigData.vdd_vid_delta; + G_global_pstate_table.pstate[i].fields.evid_vdd_eff -= G_sysConfigData.vdd_vid_delta; + G_global_pstate_table.pstate[i].fields.maxreg_vdd -= G_sysConfigData.vdd_vid_delta; + + // Modify the fields associated with Vcs. Per HW procedure team, + // the evid_vcs_eff and max_reg_vcs should get decremented + G_global_pstate_table.pstate[i].fields.evid_vcs += G_sysConfigData.vcs_vid_delta; + G_global_pstate_table.pstate[i].fields.evid_vcs_eff -= G_sysConfigData.vcs_vid_delta; + G_global_pstate_table.pstate[i].fields.maxreg_vcs -= G_sysConfigData.vcs_vid_delta; + + // Compute the ECC for this entry + G_global_pstate_table.pstate[i].fields.ecc = + gpstCheckByte(G_global_pstate_table.pstate[i].value); + } + + // STEP 3: + // Release the lock on the PMC Rail from Step 1 + l_pmax = gpst_pmax(&G_global_pstate_table); + + // Set the Pmax_rail register via OCI write + amec_oversub_pmax_clip(l_pmax); + + // STEP 4: + // In order to inform the HW about the new Global Pstate table, perform + // a single +1 Pstate jump + g_amec->pstate_foverride_enable = 1; + if (g_amec->proc[0].core_max_freq == + G_sysConfigData.sys_mode_freq.table[OCC_MODE_TURBO]) + { + // If we are at turbo frequency, then perform a single -1 Pstate + // jump instead + g_amec->pstate_foverride = g_amec->proc[0].core_max_freq - + (uint16_t)(G_global_pstate_table.frequency_step_khz / 1000); + } + else + { + g_amec->pstate_foverride = g_amec->proc[0].core_max_freq + + (uint16_t)(G_global_pstate_table.frequency_step_khz / 1000); + } + + // After updating Global Pstate table, reset the delta to 0 + G_sysConfigData.vdd_vid_delta = 0; + G_sysConfigData.vcs_vid_delta = 0; + } + else + { + // Check if we updated the Global Pstate table + if (g_amec->pstate_foverride_enable) + { + // STEP 5: + // Go back to the initial Pstate by disabling the override enable + // and maxing out the frequency request so it doesn't influence + // the final vote in the voting box + g_amec->pstate_foverride_enable = 0; + g_amec->pstate_foverride = 0xFFFF; + } + } +} + +// Function Specification +// +// Name: amec_slv_common_tasks_pre +// +// Description: Runs all the functions that need to run pre-AMEC-State-Machine +// This function will run every tick. +// +// Thread: RealTime Loop +// +// End Function Specification +void amec_slv_common_tasks_pre(void) +{ + static uint16_t L_counter = 0; + + AMEC_DBG("\tAMEC Slave Pre-State Common\n"); + + // Check if we need to apply a voltage uplift to the Global Pstate table + amec_slv_pstate_uplift_check(); + + // Update the FW Worst Case sensors every tick + amec_update_fw_sensors(); + + // Update the fast core data sensors every tick + amec_update_fast_core_data_sensors(); + + // Update the sensors that come from the APSS every tick + amec_update_apss_sensors(); + + // Call the stream buffer recording function + amec_analytics_sb_recording(); + + // Update the sensors that come from the VRM + L_counter++; + if (L_counter == AMEC_UPDATE_VRM_TICKS) + { + if (G_vrm_present) + { + amec_update_vrm_sensors(); + } + L_counter = 0; + } + + // Update the external voltage sensors + amec_update_external_voltage(); + + // Update estimate of Vdd regulator output current + + amec_update_current_sensor(); // Compute estimate for Vdd output current + + // Over-subscription check + amec_oversub_check(); +} + + +// Function Specification +// +// Name: amec_slv_cmmon_tasks_post +// +// Description: Runs all the functions that need to run post-AMEC-State-Machine +// This function will run every tick. +// +// Thread: RealTime Loop +// +// End Function Specification +void amec_slv_common_tasks_post(void) +{ + AMEC_DBG("\tAMEC Slave Post-State Common\n"); + + // Only execute if OCC is in the active state + if ( IS_OCC_STATE_ACTIVE() ) + { + // Check if we need to change Pmax_clip register setting due to not + // getting any APSS data from Master + amec_slv_check_apss_fail(); + + // Call amec_power_control + amec_power_control(); + + // Call the OCC slave's voting box + amec_slv_voting_box(); + + // Call the frequency state machine + amec_slv_freq_smh(); + + // Call the OCC slave's memory voting box + amec_slv_mem_voting_box(); + + // Call the OCC slave's performance check + amec_slv_check_perf(); + + // Call the 250us trace recording if it has been configured via Amester. + // If not configured, this call will return immediately. + amec_tb_record(AMEC_TB_250US); + } +} + + +// Function Specification +// +// Name: amec_slv_state_0 +// +// Description: AMEC Slave State Machine State +// +// End Function Specification +void amec_slv_state_0(void) +{ + AMEC_DBG("\tAMEC Slave State 0\n"); + + //------------------------------------------------------- + // Update Proc Core sensors (for this tick) + //------------------------------------------------------- + amec_update_proc_core_sensors(CORE_0); + amec_update_proc_core_sensors(CORE_8); + + //------------------------------------------------------- + // Update Centaur sensors (for this tick) + //------------------------------------------------------- + amec_update_centaur_sensors(CENTAUR_0); + + //------------------------------------------------------- + // Update Sleep Count & Winkle Count Sensors + //------------------------------------------------------- + sensor_update(AMECSENSOR_PTR(SLEEPCNT2MSP0), + __builtin_popcount( g_amec->proc[0].sleep_cnt)); + g_amec->proc[0].sleep_cnt = 0; + + sensor_update(AMECSENSOR_PTR(WINKCNT2MSP0), + __builtin_popcount(g_amec->proc[0].winkle_cnt)); + g_amec->proc[0].winkle_cnt = 0; + + //------------------------------------------------------- + // Update vector sensors + //------------------------------------------------------- + sensor_vector_update(AMECSENSOR_PTR(TEMP2MSP0), 1); + sensor_vector_update(AMECSENSOR_PTR(TEMP2MSP0PEAK),1); + sensor_vector_update(AMECSENSOR_PTR(FREQA2MSP0), 1); + sensor_vector_update(AMECSENSOR_PTR(IPS2MSP0), 1); + sensor_vector_update(AMECSENSOR_PTR(UTIL2MSP0), 1); + + // Call the trace function for 2ms tracing if it has been configured via + // Amester. If not configured, this call will return immediately. + amec_tb_record(AMEC_TB_2MS); +} + + +// Function Specification +// +// Name: amec_slv_state_1 +// +// Description: AMEC Slave State Machine State +// +// Thread: RealTime Loop +// +// End Function Specification +void amec_slv_state_1(void) +{ + AMEC_DBG("\tAMEC Slave State 1\n"); + + //------------------------------------------------------- + // Update Proc Core sensors (for this tick) + //------------------------------------------------------- + amec_update_proc_core_sensors(CORE_1); + amec_update_proc_core_sensors(CORE_9); + + //------------------------------------------------------- + // Update Centaur sensors (for this tick) + //------------------------------------------------------- + amec_update_centaur_sensors(CENTAUR_1); + + //------------------------------------------------------- + // Update Proc Level Centaur/DIMM Temperature sensors + //------------------------------------------------------- + amec_update_centaur_temp_sensors(); +} + + +// Function Specification +// +// Name: amec_slv_state_2 +// +// Description: AMEC Slave State Machine State +// +// End Function Specification +void amec_slv_state_2(void) +{ + AMEC_DBG("\tAMEC Slave State 2\n"); + + //------------------------------------------------------- + // Update Proc Core sensors (for this tick) + //------------------------------------------------------- + amec_update_proc_core_sensors(CORE_2); + amec_update_proc_core_sensors(CORE_10); + + //------------------------------------------------------- + // Update Centaur sensors (for this tick) + //------------------------------------------------------- + amec_update_centaur_sensors(CENTAUR_2); +} + + +// Function Specification +// +// Name: amec_slv_state_3 +// +// Description: AMEC Slave State Machine State +// +// Thread: RealTime Loop +// +// End Function Specification +void amec_slv_state_3(void) +{ + AMEC_DBG("\tAMEC Slave State 3\n"); + + //------------------------------------------------------- + // Update Proc Core sensors (for this tick) + //------------------------------------------------------- + amec_update_proc_core_sensors(CORE_3); + amec_update_proc_core_sensors(CORE_11); + + //------------------------------------------------------- + // Update Centaur sensors (for this tick) + //------------------------------------------------------- + amec_update_centaur_sensors(CENTAUR_3); + + //------------------------------------------------------- + // Perform amec_analytics (set amec_analytics_slot to 3) + //------------------------------------------------------- + amec_analytics_main(); +} + + +// Function Specification +// +// Name: amec_slv_state_4 +// +// Description: AMEC Slave State Machine State +// +// Thread: RealTime Loop +// +// End Function Specification +void amec_slv_state_4(void) +{ + AMEC_DBG("\tAMEC Slave State 4\n"); + + //------------------------------------------------------- + // Update Proc Core sensors (for this tick) + //------------------------------------------------------- + amec_update_proc_core_sensors(CORE_4); + + //------------------------------------------------------- + // Update Centaur sensors (for this tick) + //------------------------------------------------------- + amec_update_centaur_sensors(CENTAUR_4); +} + + +// Function Specification +// +// Name: amec_slv_state_5 +// +// Description: AMEC Slave State Machine State +// +// Thread: RealTime Loop +// +// End Function Specification +void amec_slv_state_5(void) +{ + AMEC_DBG("\tAMEC Slave State 5\n"); + + //------------------------------------------------------- + // Update Proc Core sensors (for this tick) + //------------------------------------------------------- + amec_update_proc_core_sensors(CORE_5); + + //------------------------------------------------------- + // Update Centaur sensors (for this tick) + //------------------------------------------------------- + amec_update_centaur_sensors(CENTAUR_5); + + //------------------------------------------------------- + // Update partition sensors for DPS algorithms (for this tick) + //------------------------------------------------------- + amec_dps_main(); +} + + +// Function Specification +// +// Name: amec_slv_state_6 +// +// Description: AMEC Slave State Machine State +// +// Thread: RealTime Loop +// +// End Function Specification +void amec_slv_state_6(void) +{ + AMEC_DBG("\tAMEC Slave State 6\n"); + + //------------------------------------------------------- + // Update Proc Core sensors (for this tick) + //------------------------------------------------------- + amec_update_proc_core_sensors(CORE_6); + + //------------------------------------------------------- + // Update Centaur sensors (for this tick) + //------------------------------------------------------- + amec_update_centaur_sensors(CENTAUR_6); +} + + +// Function Specification +// +// Name: amec_slv_state_7 +// +// Description: AMEC Slave State Machine State +// +// End Function Specification +void amec_slv_state_7(void) +{ + AMEC_DBG("\tAMEC Slave State 7\n"); + + //------------------------------------------------------- + // Update Proc Core sensors (for this tick) + //------------------------------------------------------- + amec_update_proc_core_sensors(CORE_7); + + //------------------------------------------------------- + // Update Centaur sensors (for this tick) + //------------------------------------------------------- + amec_update_centaur_sensors(CENTAUR_7); +} + +// Function Specification +// +// Name: amec_slv_substate_6_0 +// amec_slv_substate_6_1 +// amec_slv_substate_6_2 +// amec_slv_substate_6_3 +// amec_slv_substate_6_4 +// amec_slv_substate_6_5 +// amec_slv_substate_6_6 +// amec_slv_substate_6_7 +// +// Description: slave substate amec_slv_substate_6_0 +// slave substate amec_slv_substate_6_1 +// slave substate amec_slv_substate_6_2 +// slave substate amec_slv_substate_6_3 +// slave substate amec_slv_substate_6_4 +// slave substate amec_slv_substate_6_5 +// slave substate amec_slv_substate_6_6 +// slave substate amec_slv_substate_6_7 +// +// End Function Specification +void amec_slv_substate_6_0(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + AMEC_DBG("\tAMEC Slave State 6.0\n"); + + // Call processor-based thermal controller + amec_controller_proc_thermal(); +} + +void amec_slv_substate_6_1(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + AMEC_DBG("\tAMEC Slave State 6.1\n"); + + // Call controller on VRHOT signal from processor regulator + amec_controller_vrhotproc(); +} + +void amec_slv_substate_6_2(void){AMEC_DBG("\tAMEC Slave State 6.2\n");} +void amec_slv_substate_6_3(void){AMEC_DBG("\tAMEC Slave State 6.3\n");} +void amec_slv_substate_6_4(void){AMEC_DBG("\tAMEC Slave State 6.4\n");} +void amec_slv_substate_6_5(void){AMEC_DBG("\tAMEC Slave State 6.5\n");} +void amec_slv_substate_6_6(void){AMEC_DBG("\tAMEC Slave State 6.6\n");} +void amec_slv_substate_6_7(void){AMEC_DBG("\tAMEC Slave State 6.7\n");} + +// Function Specification +// +// Name: amec_slv_substate_7_0 +// amec_slv_substate_7_1 +// amec_slv_substate_7_2 +// amec_slv_substate_7_3 +// amec_slv_substate_7_4 +// amec_slv_substate_7_5 +// amec_slv_substate_7_6 +// amec_slv_substate_7_7 +// +// Description: slave substate amec_slv_substate_7_0 +// slave substate amec_slv_substate_7_1 +// slave substate amec_slv_substate_7_2 +// slave substate amec_slv_substate_7_3 +// slave substate amec_slv_substate_7_4 +// slave substate amec_slv_substate_7_5 +// slave substate amec_slv_substate_7_6 +// slave substate amec_slv_substate_7_7 +// +// End Function Specification +void amec_slv_substate_7_0(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + AMEC_DBG("\tAMEC Slave State 7.0\n"); + + // Call memory thermal controller based on DIMM temperature + amec_controller_dimm_thermal(); + + // Call memory thermal controller based on Centaur temperature + amec_controller_centaur_thermal(); +} + +void amec_slv_substate_7_1(void){AMEC_DBG("\tAMEC Slave State 7.1\n");} +void amec_slv_substate_7_2(void){AMEC_DBG("\tAMEC Slave State 7.2\n");} +void amec_slv_substate_7_3(void){AMEC_DBG("\tAMEC Slave State 7.3\n");} +void amec_slv_substate_7_4(void){AMEC_DBG("\tAMEC Slave State 7.4\n");} +void amec_slv_substate_7_5(void){AMEC_DBG("\tAMEC Slave State 7.5\n");} +void amec_slv_substate_7_6(void){AMEC_DBG("\tAMEC Slave State 7.6\n");} + +void amec_slv_substate_7_7(void) +{ + /*------------------------------------------------------------------------*/ + /* Local Variables */ + /*------------------------------------------------------------------------*/ + + /*------------------------------------------------------------------------*/ + /* Code */ + /*------------------------------------------------------------------------*/ + AMEC_DBG("\tAMEC Slave State 7.7\n"); + + // Call health monitor to check for processor error temperature conditions + amec_health_check_proc_temp(); +} + +/*----------------------------------------------------------------------------*/ +/* End */ +/*----------------------------------------------------------------------------*/ diff --git a/src/occ_405/amec/amec_slave_smh.h b/src/occ_405/amec/amec_slave_smh.h new file mode 100755 index 0000000..884d480 --- /dev/null +++ b/src/occ_405/amec/amec_slave_smh.h @@ -0,0 +1,110 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/amec/amec_slave_smh.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#ifndef _AMEC_SLAVE_SMH_H +#define _AMEC_SLAVE_SMH_H + +//************************************************************************* +// Includes +//************************************************************************* +#include <occ_common.h> +#include <ssx.h> +#include <ssx_app_cfg.h> +#include <amec_smh.h> +#include <amec_amester.h> + +//************************************************************************* +// Externs +//************************************************************************* + +//************************************************************************* +// Macros +//************************************************************************* + +//************************************************************************* +// Defines/Enums +//************************************************************************* +#define AMEC_SLV_STATE() AMEC_STATE(&G_amec_slv_state); +#define AMEC_SLV_SUBSTATE() AMEC_SUBSTATE(&G_amec_slv_state); +#define AMEC_SLV_SUB_SUBSTATE() AMEC_SUB_SUBSTATE(&G_amec_slv_state); + +#define AMEC_SLV_STATE_NEXT() AMEC_STATE_NEXT(&G_amec_slv_state); +#define AMEC_SLV_SUBSTATE_NEXT() AMEC_SUBSTATE_NEXT(&G_amec_slv_state); +#define AMEC_SLV_SUB_SUBSTATE_NEXT() AMEC_SUB_SUBSTATE_NEXT(&G_amec_slv_state); + +//************************************************************************* +// Structures +//************************************************************************* + +//************************************************************************* +// Globals +//************************************************************************* +extern const smh_tbl_t amec_slv_state_table[AMEC_SMH_STATES_PER_LVL]; +extern smh_state_t G_amec_slv_state; +extern smh_state_timing_t G_amec_slv_state_timings; +extern bool G_apss_lower_pmax_rail; + +//************************************************************************* +// Function Prototypes +//************************************************************************* +void amec_slv_check_apss_fail(void); +void amec_slv_pstate_uplift_check(void); + +// PRE: slave common tasks +void amec_slv_common_tasks_pre(void); +// POST: slave common tasks +void amec_slv_common_tasks_post(void); + +// Slave States +void amec_slv_state_0(void); +void amec_slv_state_1(void); +void amec_slv_state_2(void); +void amec_slv_state_3(void); +void amec_slv_state_4(void); +void amec_slv_state_5(void); +void amec_slv_state_6(void); +void amec_slv_state_7(void); + +// Slave SubState 6 +void amec_slv_substate_6_0(void); +void amec_slv_substate_6_1(void); +void amec_slv_substate_6_2(void); +void amec_slv_substate_6_3(void); +void amec_slv_substate_6_4(void); +void amec_slv_substate_6_5(void); +void amec_slv_substate_6_6(void); +void amec_slv_substate_6_7(void); + +// Slave SubState 7 +void amec_slv_substate_7_0(void); +void amec_slv_substate_7_1(void); +void amec_slv_substate_7_2(void); +void amec_slv_substate_7_3(void); +void amec_slv_substate_7_4(void); +void amec_slv_substate_7_5(void); +void amec_slv_substate_7_6(void); +void amec_slv_substate_7_7(void); + +#endif diff --git a/src/occ_405/amec/amec_smh.h b/src/occ_405/amec/amec_smh.h new file mode 100755 index 0000000..e66c339 --- /dev/null +++ b/src/occ_405/amec/amec_smh.h @@ -0,0 +1,100 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ_405/amec/amec_smh.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#ifndef _AMEC_SMH_H +#define _AMEC_SMH_H + +//************************************************************************* +// Includes +//************************************************************************* +#include <occ_common.h> +#include <ssx.h> +#include <ssx_app_cfg.h> +#include "amec_external.h" + +//************************************************************************* +// Externs +//************************************************************************* + +//************************************************************************* +// Macros +//************************************************************************* + +//************************************************************************* +// Defines/Enums +//************************************************************************* + +#define AMEC_STATE(i_state) (i_state->state); +#define AMEC_SUBSTATE(i_state) (i_state->substate); +#define AMEC_SUB_SUBSTATE(i_state) (i_state->sub_substate); + +#define AMEC_STATE_NEXT(i_state) i_state->state++; i_state->state %= AMEC_SMH_STATES_PER_LVL; +#define AMEC_SUBSTATE_NEXT(i_state) i_state->substate++; i_state->substate %= AMEC_SMH_STATES_PER_LVL; +#define AMEC_SUB_SUBSTATE_NEXT(i_state) i_state->sub_substate++; i_state->sub_substate %= AMEC_SMH_STATES_PER_LVL; + +#define AMEC_INITIAL_STATE 0 + +#define AMEC_SMH_STATES_PER_LVL 8 + +// Number of uS in 1 RTL tick (250=250us) +#define AMEC_US_PER_TICK MICS_PER_TICK +// Number of uS in 1 full period of the AMEC State Machine (2000=2mS, 8 RTL ticks) +#define AMEC_US_PER_SMH_PERIOD (AMEC_SMH_STATES_PER_LVL * MICS_PER_TICK) +// Number of <AMEC_US_PER_SMH_PERIOD> that happen in 1 second +#define AMEC_SMH_PERIODS_IN_1SEC (10000000 / AMEC_US_PER_SMH_PERIOD) + +//************************************************************************* +// Structures +//************************************************************************* +// Each State table (including Substates) will take up 64 bytes +// of SRAM space. +typedef struct smh_tbl +{ + void (*state)(); + const struct smh_tbl * substate; +} smh_tbl_t; + +typedef struct +{ + uint8_t state; + uint8_t substate; + uint8_t sub_substate; +} smh_state_t; + +typedef struct +{ + void (*update_sensor)(int, uint32_t); +} smh_state_timing_t; + +//************************************************************************* +// Globals +//************************************************************************* + +//************************************************************************* +// Function Prototypes +//************************************************************************* +void amec_generic_smh(const smh_tbl_t * i_smh_tbl, smh_state_t * i_smh_state, smh_state_timing_t * i_smh_timing); + +#endif //_AMEC_SMH_H diff --git a/src/occ_405/amec/amec_sys.h b/src/occ_405/amec/amec_sys.h new file mode 100755 index 0000000..0383701 --- /dev/null +++ b/src/occ_405/amec/amec_sys.h @@ -0,0 +1,729 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ/amec/amec_sys.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +#ifndef _AMEC_SYS_H +#define _AMEC_SYS_H + +//************************************************************************* +// Includes +//************************************************************************* +#include <occ_common.h> +#include <sensor.h> +#include <occ_sys_config.h> +#include <amec_part.h> +#include <amec_perfcount.h> +#include <mode.h> +#include <amec_controller.h> +#include <amec_oversub.h> +#include <amec_amester.h> +#include <amec_pcap.h> + +//************************************************************************* +// Externs +//************************************************************************* + +//************************************************************************* +// Macros +//************************************************************************* + +//************************************************************************* +// Defines/Enums +//************************************************************************* +// This is an arbitrary number of FW probes for use internally. +#define NUM_AMEC_FW_PROBES 8 + +// Number of States in the AMEC State Machine (should always be 8) +#define NUM_AMEC_SMH_STATES 8 + +// Number of PORE-GPE Engines +#define NUM_GPE_ENGINES 2 +#define GPE_ENGINE_0 0 +#define GPE_ENGINE_1 1 + +//************************************************************************* +// Structures +//************************************************************************* + +//------------------------------------------------------------- +// FW Sub-structure +//------------------------------------------------------------- +typedef struct +{ + // Sensors + sensor_t ameintdur; + sensor_t amessdur[NUM_AMEC_SMH_STATES]; + sensor_t gpetickdur[NUM_GPE_ENGINES]; + sensor_t prcdupdatedur; + sensor_t probe250us[NUM_AMEC_FW_PROBES]; + + // DPS update flag + // 8 bit flag: =1, no updating allowed; =0, updating is allowed + uint8_t dps_no_update_flag; + +} amec_fw_t; + +//------------------------------------------------------------- +// Fan Sub-structure +//------------------------------------------------------------- +typedef struct +{ + // Sensors + sensor_t fanspeedavg; + sensor_t pwr250usfan; + +} amec_fans_t; + +//------------------------------------------------------------- +// IO Sub-structure +//------------------------------------------------------------- +typedef struct +{ + // Sensors + sensor_t pwr250usio; + +} amec_io_t; + +//------------------------------------------------------------- +// Storage Sub-structure +//------------------------------------------------------------- +typedef struct +{ + // Sensors + sensor_t pwr250usstore; + +} amec_store_t; + +//------------------------------------------------------------- +// Proc Sub-structure +//------------------------------------------------------------- + +typedef struct +{ + // Sensors + sensor_t uvolt250us; + sensor_t volt250us; + +} amec_vrm_t; + + +typedef struct +{ + uint32_t wr_cnt_accum; + uint32_t rd_cnt_accum; + uint32_t pwrup_cnt_accum; + uint32_t act_cnt_accum; + uint32_t fr2_cnt_accum; + uint32_t l4_rd_cnt_accum; + uint32_t l4_wr_cnt_accum; + uint32_t intreq_base_accum; + uint32_t intreq_low_accum; + uint32_t intreq_med_accum; + uint32_t intreq_high_accum; + + uint16_t fr2_cnt; + uint16_t act_cnt; + uint16_t pwrup_cnt; + uint16_t memwrite2ms; + uint16_t memread2ms; + uint16_t l4wr2ms; + uint16_t l4rd2ms; + uint16_t mirb2ms; + uint16_t mirl2ms; + uint16_t mirm2ms; + uint16_t mirh2ms; + +} amec_chpair_perf_counter_t; + +//convenient format for storing throttle settings +typedef union +{ + uint32_t word32; + struct + { + uint32_t mba_n: 16; + uint32_t chip_n: 16; + }; +} amec_cent_mem_speed_t; + +typedef struct +{ + // Sensors + sensor_t mac2ms; + sensor_t mpu2ms; + sensor_t mirb2ms; + sensor_t mirl2ms; + sensor_t mirm2ms; + sensor_t mirh2ms; + sensor_t mts2ms; + sensor_t memsp2ms; + sensor_t m4rd2ms; + sensor_t m4wr2ms; + + amec_chpair_perf_counter_t perf; + + // The most recent throttle value sent to this MBA + // This is used to only send values to the centaur when it changes. + amec_cent_mem_speed_t last_mem_speed_sent; +} amec_portpair_t; + +typedef struct +{ + uint32_t intreq_highlatency_accum; + uint32_t lp2exit_accum; + + uint16_t mirc2ms; + uint16_t mlp2_2ms; +} amec_centaur_perf_counter_t; + +#define FRU_SENSOR_STATUS_STALLED 0x01 +#define FRU_SENSOR_STATUS_ERROR 0x02 +#define FRU_SENSOR_STATUS_VALID_OLD 0x04 +#define FRU_TEMP_OUT_OF_RANGE 0x08 +#define FRU_SENSOR_STATUS_INVALID 0x10 //centaur only +#define FRU_TEMP_FAST_CHANGE 0x20 +#define FRU_SENSOR_CENT_NEST_FIR6 0x40 //centaur only + +typedef struct +{ + uint8_t cur_temp; + uint8_t sample_age; + uint8_t flags; + // Sensor ID for reporting temperature to BMC + uint16_t temp_sid; +}fru_temp_t; + +typedef struct +{ + // Sub-structures under Centaur + union + { + amec_portpair_t portpair[NUM_PORT_PAIRS_PER_CENTAUR]; + amec_portpair_t mba[NUM_PORT_PAIRS_PER_CENTAUR]; + }; // Just a different name to refer to same thing + + // Sensors + sensor_t mlp2ms; + sensor_t mirc2ms; + + //hottest dimm temperature behind this centaur + sensor_t tempdimmax; + + //which of the 8 dimm temperatures was the hottest temperature + //(only changes when the max of tempdimmax changes) + sensor_t locdimmax; + + // Current dimm tempuratures + fru_temp_t dimm_temps[NUM_DIMMS_PER_CENTAUR]; + + // Hottest centaur temperature for this centaur + fru_temp_t centaur_hottest; + + // Sensor ID for reporting temperature to BMC + uint16_t temp_sid; + + amec_centaur_perf_counter_t perf; + +} amec_centaur_t; + +typedef struct +{ + // Sub-structures under MemCtl + amec_centaur_t centaur; + + // Sensors + sensor_t mrd2ms; + sensor_t mwr2ms; + +} amec_memctl_t; + +typedef struct +{ + //----------------------------------- + // Previous Tick Data + //----------------------------------- + uint32_t prev_PC_RUN_Th_CYCLES; + + //----------------------------------- + // Calculations & Interim Data + //----------------------------------- + uint16_t util2ms_thread; + +} amec_core_thread_t; + +typedef struct +{ + // Sub-structures under Core + amec_core_perf_counter_t core_perf; + amec_core_thread_t thread[MAX_THREADS_PER_CORE]; + + //----------------------------------- + // Sensors + //----------------------------------- +// sensor_t cpm2ms; //CPM - Commented out as requested by Malcolm + sensor_t freq250us; + sensor_t freqa2ms; + sensor_t ips2ms; + sensor_t mcpifd2ms; + sensor_t mcpifi2ms; + sensor_t spurr2ms; + sensor_t temp2ms; + sensor_t util2ms; + sensor_t nutil3s; + sensor_t mstl2ms; + sensor_t cmt2ms; + sensor_t cmbw2ms; + sensor_t ppic; + sensor_t pwrpx250us; + + //----------------------------------- + // Previous Tick Data + //----------------------------------- + uint32_t prev_PC_RAW_CYCLES; + uint32_t prev_PC_RUN_CYCLES; + uint32_t prev_PC_DISPATCH; + uint32_t prev_PC_COMPLETED; + uint32_t prev_PC_RAW_Th_CYCLES; + uint32_t prev_tod_2mhz; + uint32_t prev_lpar_mem_cnt[4]; + uint32_t prev_FREQ_SENS_BUSY; + uint32_t prev_FREQ_SENS_FINISH; + + //----------------------------------- + // Calculations & Interim Data + //----------------------------------- + // Dispatched Instructions per Second + uint16_t dps; + // Dispatched Instruction per Cycle + uint16_t dpc; + // Instructions per Cycle + uint16_t ipc; + // Hottest DTS sensor per core + uint16_t dts_hottest; + // Counter of number of samples for calculating average utilization & frequency + uint16_t sample_count; + // Array of memory bandwidth for each LPAR + uint16_t membw[4]; + + // Average utilization over a fixed time interval + uint32_t avg_util; + // Average frequency over a fixed time interval + uint32_t avg_freq; + + // --------------------------------- + // Frequency State Machine variables + // --------------------------------- + // Frequency request generated by the voting box + uint16_t f_request; + // Reason for the frequency request generated by the voting box + uint32_t f_reason; + // Current state of this core frequency state machine + uint8_t f_sms; + +} amec_core_t; + +//------------------------------------------------------------- +// System-wide Sub-structure +//------------------------------------------------------------- +typedef struct +{ + // Sensors + //sensor_t fake_sensor[NUM_OCCS]; + // +} amec_master_t; + + +//------------------------------------------------------------- +// System-wide Sub-structure +//------------------------------------------------------------- +typedef struct +{ + // System Sensors + sensor_t tempambient; + sensor_t altitude; + sensor_t pwr250us; + sensor_t pwr250usgpu; + sensor_t pwrapssch[MAX_APSS_ADC_CHANNELS]; + + sensor_t vrfan250usmem; + sensor_t vrhot250usmem; + sensor_t vrfan250usproc; + sensor_t vrhot250usproc; + + // Chip Sensors + sensor_t todclock0; + sensor_t todclock1; + sensor_t todclock2; + + // Minimum Frequency that can be set in the current policy + uint16_t fmin; + + // Maximum Frequency that can be set in the current policy + uint16_t fmax; + + // Maximum speed in current policy + uint16_t max_speed; + + // Minimum speed in current policy + uint16_t min_speed; + + // Speed step size + uint16_t speed_step; + + // Speed step limit + uint16_t speed_step_limit; +} amec_systemwide_t; + + +typedef struct +{ + //Maximum frequency allowed on this chip by the + //performance preserving boundary algorithm. Set by amec_ppb_fmax_calc + uint16_t ppb_fmax; + + //Maximum frequency allowed on this chip by the Pmax_clip register. + //Set by amec_pmax_clip_controller. + uint16_t pmax_clip_freq; + + //Maximum frequency allowed on this chip by the power capping algorithm for + //non-nominal cores. Set by amec_pcap_controller. + uint16_t proc_pcap_vote; + + //Minimum frequency that power capping is allowed to lower a nominal + //core to. Set by amec_pcap_calc. + uint16_t nom_pcap_fmin; + + //Maximum frequency allowed on this chip by the power capping algorithm for + //nominal cores. Set by amec_pcpa_controller. + uint16_t proc_pcap_nom_vote; + + //Maximum frequency allowed on this chip by the connector overcurrent algorithm. + //Set by amec_conn_oc_controller. + uint16_t conn_oc_vote; + + //Maximum frequency allowed on this chip by the Pmax_clip register. + //Set by amec_slv_check_apss_fail + uint16_t apss_pmax_clip_freq; + +} amec_proc_pwr_votes_t; + +//------------------------------------------------------------- +// Proc Structure +//------------------------------------------------------------- +typedef struct +{ + // Sub-structures under Proc + amec_core_t core[MAX_NUM_CORES]; + amec_memctl_t memctl[MAX_NUM_MEM_CONTROLLERS]; + amec_vrm_t vrm[NUM_PROC_VRMS]; + amec_proc_pwr_votes_t pwr_votes; + + // Processor Sensors + sensor_t freqa2ms; + vectorSensor_t freqa2ms_vector; + sensor_t ips2ms; + vectorSensor_t ips2ms_vector; + sensor_t memsp2ms; + vectorSensor_t memsp2ms_vector; + sensor_t pwr250us; + sensor_t pwr250usvdd; + sensor_t cur250usvdd; + sensor_t pwr250usvcs; + sensor_t pwr250usmem; + sensor_t sleepcnt2ms; + sensor_t winkcnt2ms; + sensor_t sp250us; + sensor_t temp2ms; + vectorSensor_t temp2ms_vector; + sensor_t temp2mspeak; + vectorSensor_t temp2mspeak_vector; + sensor_t util2ms; + vectorSensor_t util2ms_vector; + + // Memory Summary Sensors + sensor_t temp2mscent; + sensor_t temp2msdimm; + sensor_t memsp2ms_tls; + + // Error count for failing to read VR_FAN signal + uint8_t vrfan_error_count; + + // Calculations & Interim Data + uint16_t sleep_cnt; + uint16_t winkle_cnt; + + uint16_t core_max_freq; // Maximum requested freq for all cores on chip. + + // Parameters used through Amester interface + // Note: keep core arrays here, not in per-cores structure so one parameter + // can be used to pass array. + uint32_t parm_f_reason[MAX_NUM_CORES]; // per-core frequency reason + uint16_t parm_f_override[MAX_NUM_CORES]; // per-core frequency override in MHz + uint8_t parm_f_override_enable; // enable using the frequency override + +} amec_proc_t; + + + +//------------------------------------------------------------- +// Mode Freq Structure +//------------------------------------------------------------- +typedef struct amec_mode_freq +{ + uint16_t fmin; + uint16_t fmax; + ///Minimum speed allowed based on fmin/fmax ratio + uint16_t min_speed; +} amec_mode_freq_t; + + +//------------------------------------------------------------- +// Parameters for manufacturing commands +//------------------------------------------------------------- +typedef struct amec_mnfg +{ + ///Auto-slewing flag: enable=1, disable=0 + uint8_t auto_slew; + ///Minimum frequency in MHz for auto-slewing + uint16_t fmin; + ///Maximum frequency in MHz for auto-slewing + uint16_t fmax; + ///Step size in MHz for auto-slewing + uint16_t fstep; + ///Additional delay in ticks for auto-slewing + uint16_t delay; + ///Frequency override to be sent to all slave OCCs + uint16_t foverride; + ///Counter of times we reached fmin or fmax + uint16_t slew_counter; + ///memory auto-slewing flag: enable=1, disable=0 + bool mem_autoslew; + ///memory slewing count + uint32_t mem_slew_counter; +} amec_mnfg_t; + +//------------------------------------------------------------- +// Parameters for Idle Power Save (IPS) mode +//------------------------------------------------------------- +typedef struct amec_ips +{ + ///Enable/Disable IPS (=0:disable; =1:enable) + uint8_t enable; + ///Current 'active' state of IPS (=0:inactive; =1:active) + uint8_t active; + ///IPS frequency request to be sent to all OCC Slaves + uint16_t freq_request; + ///Utilization threshold to enter idle condition (in hundreth of a percent) + uint16_t entry_threshold; + ///Utilization threshold to exit idle condition (in hundreth of a percent) + uint16_t exit_threshold; + ///Delay time to enter idle condition (in number of samples) + uint32_t entry_delay; + ///Delay time to exit idle condition (in number of samples) + uint32_t exit_delay; +}amec_ips_t; + + +//------------------------------------------------------------- +// +// AMEC/OCC Overall System Structure -- g_amec +// +//------------------------------------------------------------- +typedef struct +{ + //--------------------------------------------------------- + // + // System Management Settings + // + //--------------------------------------------------------- + // Global memory throttle reason + uint8_t mem_throttle_reason; + // Global memory speed request + uint16_t mem_speed_request; + + // Flag to enable frequency override in the voting box due to Master OCC request + uint8_t foverride_enable; + // Override frequency to be used by the voting box due to Master OCC request + uint16_t foverride; + // Flag to enable frequency override in the voting box due to a Pstate table update + uint8_t pstate_foverride_enable; + // Override frequency to be used by the voting box due to a Pstate table update + uint16_t pstate_foverride; + + // Idle Power Saver frequency request sent by Master OCC + uint16_t slv_ips_freq_request; + // Flag to indicate that the DPS parameters were overwritten by user + BOOLEAN slv_dps_param_overwrite; + + //--------------------------------------------------------- + // + // Physical Structure + // + //--------------------------------------------------------- + // IO Data + amec_io_t io; + + // Storage Data + amec_store_t storage; + + // Fan Data + amec_fans_t fan; + + // Overall System Data + amec_systemwide_t sys; + + // Processor Card Data + // - This is an array of 1. This was initialized this way + // in the hopes of perhaps reusing some code from previous projects. + amec_proc_t proc[NUM_PROC_CHIPS_PER_OCC]; + + // OCC Firmware Data + amec_fw_t fw; + + // Sensors on master for calculations across multiple OCCs + //amec_master_t mstr; + + // Partition Information + amec_part_config_t part_config; + // Mode frequency table indexed by mode + amec_mode_freq_t part_mode_freq[OCC_INTERNAL_MODE_MAX_NUM]; + + //--------------------------------------------------------- + // + // Control Systems + // + //--------------------------------------------------------- + // Thermal Controller based on processor temperatures + amec_controller_t thermalproc; + // Thermal Controller based on Centaur temperatures + amec_controller_t thermalcent; + // Thermal Controller based on DIMM temperatures + amec_controller_t thermaldimm; + // Thermal Controller based on VRHOT signal from processor VRM + amec_controller_t vrhotproc; + + // Oversubscription Status + oversub_status_t oversub_status; + + // Parameters for manufacturing commands + amec_mnfg_t mnfg_parms; + + // Parameters for Idle Power Save (IPS) mode + amec_ips_t mst_ips_parms; + + // PowerCap Data + amec_pcap_t pcap; + + // Save off proc and mem sensor data for debug usage + uint16_t proc_snr_pwr[MAX_NUM_CHIP_MODULES]; + uint16_t mem_snr_pwr[MAX_NUM_CHIP_MODULES]; + + // save off when pcap is considered valid + uint8_t pcap_valid; + + //--------------------------------------------------------- + // + // Parameters for analytics function + // + //--------------------------------------------------------- + // 32 bit counter of 250usec ticks + uint32_t r_cnt; + // array holding sensor ptrs for writing to stream vector + void *stream_vector_map[STREAM_VECTOR_SIZE_EX]; + void * ptr_probe250us[NUM_AMEC_FW_PROBES]; // array holding ptrs to data that is read by probe250us sensors + // 32-bit ptr to streaming buffer which contains 16 bit elements + uint16_t *ptr_stream_buffer; + // 32-bit index for next write into streaming buffer + uint32_t write_stream_index; + // 32-bit index for next read from streaming buffer + uint32_t read_stream_index; + // stream buffer for vector recordings + uint16_t stream_buffer[STREAM_BUFFER_SIZE]; + // initially 0 until recording is valid + uint8_t recordflag; + // 16-bit delay in msec before stream vector records (set to 0 to avoid delay) + uint16_t stream_vector_delay; + // 8-bit mode control for stream vector mode: + uint8_t stream_vector_mode; // 0=stop recording + // 1=record unconditionally from begin to end of buffer, then stop + // 2=record unconditionally forever + // 3=record until a checkstop event is detected + // 8-bit mode control for stream vector recording: + // 0=fastest sampling on platform: (250usec on OCC); 7=32msec + uint8_t stream_vector_rate; + // 8-bit group # that selects which group of sensors to record as a vector + uint8_t stream_vector_group; + // input from TMGT to signal a reset of the OCC is desired (!=0) + uint8_t reset_prep; + // holds current state of L4 state machine for Centaur k + uint16_t cent_l4_state[MAX_NUM_CENTAURS]; + // holds current state of L4 IPL state machine for Centaur k + uint16_t cent_l4_ipl_state[MAX_NUM_CENTAURS]; + // input from OCC master to signal a desire to power down the L4s (!=0) + uint8_t l4_powerdown_requestm; + // indicates which of the L4 Centaurs is being monitored by probe. + uint16_t probe_l4_centaur; + uint32_t g44_avg[MAX_NUM_CHIP_MODULES*MAX_SENSORS_ANALYTICS]; + // parameter driven selection of analytics group + uint16_t analytics_group; + // parameter to select which chip to monitor analytics on + uint8_t analytics_chip; + // parameter to select which analytics options (=0 just selected chip) + uint8_t analytics_option; + // 8-bit value used to throw away frames until good output has been averaged in amec_analytics buffer outputs + uint8_t analytics_bad_output_count; + // Total number of chips used in analytics sensor capture + uint8_t analytics_total_chips; + // Current offset in cyclic thermal group output (8 in cycle) + uint8_t analytics_thermal_offset; + // Selects which type of Group 44 averaging is done on per thread data: + // default=0 (average of non-zero thread utilizations), =1 (average of N), =2 (max of N) + uint8_t analytics_threadmode; + // Has the maximum number of threads per core for this processor architecture or for SMT modes. Default=4 on P7+. + uint8_t analytics_threadcountmax; + // Which of 8 time slots that amec_analytics is called in + uint8_t analytics_slot; + // Used to hold selected analytics group + uint16_t analytics_array[STREAM_VECTOR_SIZE_EX]; + // for group 44 support core bit maps of their napping cores (upper byte) and sleeping cores (lower byte) + uint16_t packednapsleep[MAX_NUM_CHIP_MODULES]; + // holds the sum of all the memory power sensors (32msec) + uint16_t total_memory_power; + uint16_t probetemp[NUM_AMEC_FW_PROBES]; // array holding temporary probe data + uint8_t size_probe250us[NUM_AMEC_FW_PROBES]; // size of object pointed at by each probe (1 byte, 2 bytes, or 4 bytes) + uint8_t index_probe250us[NUM_AMEC_FW_PROBES]; // index offset to read object pointed to by each probe (only valid for size > 2) + +} amec_sys_t; + +//************************************************************************* +// Globals +//************************************************************************* +extern amec_sys_t * g_amec; + +//************************************************************************* +// Function Prototypes +//************************************************************************* +void amec_slave_init(void) INIT_SECTION; + +#endif diff --git a/src/occ_405/amec/amec_tasks.c b/src/occ_405/amec/amec_tasks.c new file mode 100755 index 0000000..ce306a9 --- /dev/null +++ b/src/occ_405/amec/amec_tasks.c @@ -0,0 +1,292 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/occ_405/amec/amec_tasks.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2011,2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ + +//************************************************************************* +// Includes +//************************************************************************* +#include <occ_common.h> +#include <ssx.h> +#include <errl.h> // Error logging +#include <trac.h> // Error logging +#include "rtls.h" +#include "occ_service_codes.h" // for SSX_GENERIC_FAILURE +#include "amec_smh.h" +#include "amec_master_smh.h" +#include "amec_slave_smh.h" +#include "amec_sys.h" + +//************************************************************************* +// Externs +//************************************************************************* + +//************************************************************************* +// Macros +//************************************************************************* +/* rotate x to the left by s bits */ +uint8_t _rotl8(uint8_t val, uint8_t shift) +{ + return (val << shift) | (val >> (8 - shift)); +} + +//************************************************************************* +// Defines/Enums +//************************************************************************* + +//************************************************************************* +// Structures +//************************************************************************* + +//************************************************************************* +// Globals +//************************************************************************* + +//************************************************************************* +// Function Prototypes +//************************************************************************* + +//************************************************************************* +// Functions +//************************************************************************* + +// Function Specification +// +// Name: amec_generic_smh +// +// Description: Generic State Machine that allows for 8 states per level, +// with 3 levels. +// If State Machine gets called/incremented every 250us, +// - Each function entry in Level 0 table runs every 2ms, +// - Each function entry in Level 1 table will run every 16ms, +// - Each function entry in Level 2 table will run every 128ms. +// +// Anything longer than that will need to be taken care of +// inside of the states themselves. +// +// NULL entries are allowed, for both state functions and +// for pointers to substate tables. +// - If a state does not have substates, it should have NULL +// in the substate table for the substate table pointer. +// - If a state does not wish to run a function, it should have +// NULL in the substate table for the function pointer. +// - A state can have substates without having a function that +// runs (i.e. NULL in function pointer, but valid in substate +// table pointer) +// +// In addition, if i_smh_timing is not NULL and points to a +// smh_state_timing_t structure, this state machine will grab +// the SSX timings of each "Level 0" state and store the longest +// time in each state in that structure. +// +// End Function Specification +void amec_generic_smh(const smh_tbl_t * i_smh_tbl, + smh_state_t * i_smh_state, + smh_state_timing_t * i_smh_timing) +{ + uint64_t l_start = ssx_timebase_get(); + uint8_t l_state = 0; + + // ---------------------------------------------------- + // AMEC State Machine States + // ---------------------------------------------------- + if(NULL != i_smh_tbl) + { + // Read AMEC State Machine "State" + l_state = AMEC_STATE(i_smh_state); + + AMEC_DBG("SMH: AMEC State: [%d] Mask: [%02x]\n",l_state,i_smh_state->state); + + // check if state value makes sense + if ( l_state >= NUM_AMEC_SMH_STATES ) + { + TRAC_ERR("SMH: Invalid AMEC State: [%d] Mask: [%02x]\n",l_state,i_smh_state->state); + + // TODO: how to handle this situation? + // for now exiting function + return; + } + + // If there is a function to run for this state (!NULL), run it + if(NULL != i_smh_tbl[(l_state)].state) + { + // Call into AMEC State (i.e State 6) + (*(i_smh_tbl[(l_state)].state))(); + } + + // ---------------------------------------------------- + // AMEC State Machine Substates + // ---------------------------------------------------- + if(NULL != i_smh_tbl[l_state].substate) + { + // Read AMEC State Machine "Substate" + uint8_t l_substate = AMEC_SUBSTATE(i_smh_state); + + AMEC_DBG("\tSMH: AMEC Sub State: %d Mask: %02x\n",l_substate,i_smh_state->substate); + + // check if sub state value makes sense + if ( l_substate >= NUM_AMEC_SMH_STATES ) + { + TRAC_ERR("SMH: Invalid AMEC Sub State: [%d] Mask: [%02x]\n",l_substate,i_smh_state->substate); + + // TODO: how to handle this situation? + // for now exiting function + return; + } + + // Set up pointer to sub-state table + const smh_tbl_t * l_sub_state_tbl = i_smh_tbl[l_state].substate; + + // If there is a function to run for this substate (!NULL), run it + if(NULL != (l_sub_state_tbl+l_substate)->state) + { + // Call into AMEC Substate (i.e State 6.1) + (*((l_sub_state_tbl+l_substate)->state))(); + } + + // ---------------------------------------------------- + // AMEC State Machine Sub-substates + // ---------------------------------------------------- + if(NULL != (l_sub_state_tbl+l_substate)->substate) + { + // Read AMEC State Machine "Sub-Substate" + uint8_t l_sub_substate = AMEC_SUB_SUBSTATE(i_smh_state); + + AMEC_DBG("\t\tSMH: AMEC Sub Sub State: %d Mask: %02x\n",l_sub_substate,i_smh_state->sub_substate); + + // check if sub sub state value makes sense + if ( l_sub_substate >= NUM_AMEC_SMH_STATES ) + { + TRAC_ERR("SMH: Invalid AMEC Sub State: [%d] Mask: [%02x]\n",l_sub_substate,i_smh_state->substate); + + // TODO: how to handle this situation? + // for now exiting function + return; + } + + // Set up pointer to sub-state table + const smh_tbl_t * l_sub_substate_tbl = (l_sub_state_tbl+l_substate)->substate; + + // If there is a function to run for this sub-substate (!NULL), run it + if(NULL != (l_sub_substate_tbl+l_sub_substate)->state) + { + // Call into AMEC Sub-Substate (i.e State 6.1.3) + (*((l_sub_substate_tbl+l_sub_substate)->state))(); + } + } + } + } + + // ------------------------------------------------------ + // Increment the state(s) of the AMEC State Machine + // ------------------------------------------------------ + + // Bump the state as final undertaking before task ends + AMEC_STATE_NEXT(i_smh_state); + if(AMEC_INITIAL_STATE == i_smh_state->state) + { + // Only bump the substate when "state" has wrapped + AMEC_SUBSTATE_NEXT(i_smh_state); + if(AMEC_INITIAL_STATE == i_smh_state->substate) + { + // Only bump the substate when "state" & "substate" have wrapped + AMEC_SUB_SUBSTATE_NEXT(i_smh_state); + } + } + + // ------------------------------------------------------ + // Update the max timing of this state(s) of the AMEC State Machine + // ------------------------------------------------------ + if((NULL != i_smh_timing) && (NULL != i_smh_timing->update_sensor)) + { + // Calculate the duration of the state + uint64_t l_state_duration = DURATION_IN_US_UNTIL_NOW_FROM(l_start); + + // Make a function call to update sensor with duration + (*((i_smh_timing)->update_sensor))(l_state,(uint32_t)l_state_duration); + } +} + + +// Function Specification +// +// Name: task_amec_master +// +// Description: Purpose of this function is to run all the AMEC "master-only" code +// +// Task Flags: RTL_FLAG_MSTR +// RTL_FLAG_OBS +// RTL_FLAG_ACTIVE +// RTL_FLAG_MSTR_READY +// RTL_FLAG_NO_APSS +// RTL_FLAG_RUN +// +// End Function Specification +void task_amec_master( task_t *i_self) +{ + uint64_t l_start = ssx_timebase_get(); + + amec_mst_common_tasks_pre(); + + amec_generic_smh( amec_mst_state_table, &G_amec_mst_state, &G_amec_mst_state_timings ); + + amec_mst_common_tasks_post(); + + // Add the time that this master task took to the total AMEC int task time, + // which already contains the slave task duration + G_fw_timing.ameint_dur += DURATION_IN_US_UNTIL_NOW_FROM(l_start); +} + + +// Function Specification +// +// Name: task_amec_slave +// +// Description: Purpose of this function is to run all the AMEC "slave-only" code +// +// Task Flags: RTL_FLAG_MSTR +// RTL_FLAG_NONMSTR +// RTL_FLAG_OBS +// RTL_FLAG_ACTIVE +// RTL_FLAG_MSTR_READY +// RTL_FLAG_NO_APSS +// RTL_FLAG_RUN +// +// End Function Specification +void task_amec_slave( task_t *i_self) +{ + uint64_t l_start = ssx_timebase_get(); + + amec_slv_common_tasks_pre(); + + amec_generic_smh( amec_slv_state_table, &G_amec_slv_state, &G_amec_slv_state_timings ); + + amec_slv_common_tasks_post(); + + // Set the total AMEC int task time for this tick, to the duration of the slave tasks. + G_fw_timing.ameint_dur = DURATION_IN_US_UNTIL_NOW_FROM(l_start); +} + +/*----------------------------------------------------------------------------*/ +/* End */ +/*----------------------------------------------------------------------------*/ |