diff options
Diffstat (limited to 'src/occ/amec/amec_sensors_power.c')
-rwxr-xr-x | src/occ/amec/amec_sensors_power.c | 574 |
1 files changed, 574 insertions, 0 deletions
diff --git a/src/occ/amec/amec_sensors_power.c b/src/occ/amec/amec_sensors_power.c new file mode 100755 index 0000000..5c4923f --- /dev/null +++ b/src/occ/amec/amec_sensors_power.c @@ -0,0 +1,574 @@ +/****************************************************************************** +// @file amec_sensors_power.c +// @brief AMEC Power Sensor Calculations +*/ +/** + * @page ChangeLogs Change Logs + * @section _amec_sensors_power_c amec_sensors_power.c + * @verbatim + * + * Flag Def/Fea Userid Date Description + * -------- ---------- -------- -------- -------------------------------------- + * thallet 02/24/2012 New file + * @pb00E pbavari 03/11/2012 Added correct include file + * @th019 835007 thallet 09/11/2012 Added power sensors + * @th044 892742 thallet 07/24/2013 Added module ID + * @ly008 894646 lychen 08/08/2013 Fix bugs in OCC handling of APSS tables for Brazos/Orlena + * @at016 891144 alvinwan 06/10/2013 OCC Power Cap Testing + * @sb000 905048 sbroyles 10/28/2013 Add tags for code cleanup, + * see RTC task 73327. + * @rt003 905677 tapiar 11/07/2013 save of proc/mem sensor data + * @sb001 906360 sbroyles 11/12/2013 Resolve fix tags + * @fk004 907588 fmkassem 11/27/2013 Add support for snapshot buffer. + * @gs020 909320 gjsilva 12/12/2013 Support for VR_FAN thermal control + * @gs021 909855 gjsilva 12/18/2013 Support for processor OT condition + * @gs023 912003 gjsilva 01/16/2014 Generate VRHOT signal and control loop + * @mw634 mware 02/09/2014 Added in call to amec_controller_centaur for stream recording + * @gs026 915840 gjsilva 02/13/2014 Support for Nvidia GPU power measurement + * @gm026 916029 milesg 02/17/2014 revert back to auto2 mode without reading GPIO's + * @gs027 918066 gjsilva 03/12/2014 Misc functions from ARL + * @gm034 920562 milesg 03/28/2014 fix voltage sensors + * @endverbatim + */ + +/******************************************************************************/ +/* Includes */ +/******************************************************************************/ +//@pb00Ec - changed from common.h to occ_common.h for ODE support +#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> //@fk004a +#include <vrm.h> +#include "amec_oversub.h" + +// 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]; + +#define ADC_CONVERTED_VALUE(i_chan) \ + ((i_chan < MAX_APSS_ADC_CHANNELS) ? G_lastValidAdcValue[i_chan] : 0) + +// Function Specification +// +// Name: amec_sensor_from_apss_adc +// +// Description: Calculates sensor from raw ADC value +// +// Flow: FN= +// +// Thread: RealTime Loop +// +// Task Flags: +// +// 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) + { + /* + * @sb001 Several changes made under this tag, see occ810 version 1.9 + * for previous version. + * 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; + } + + 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; +} + + +// Function Specification +// +// Name: amec_update_apss_sensors +// +// Description: Calculates sensor from raw ADC value +// +// Flow: FN= +// +// Thread: RealTime Loop +// +// Task Flags: +// +// End Function Specification +void amec_update_apss_sensors(void) +{ + /* + * @sb001 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. See occ810 version 1.9 of this file for the hardcoded values that + * were removed. + * Code is in place to receive command code 0x21 SET CONFIG DATA from the + * FSP which should popluate the ADC and GPIO maps as well as the APSS + * calibration data for all 16 ADC channels. + */ + + // ------------------------------------------------------ + // APSS Data Collection & Sensorization + // ------------------------------------------------------ + + // 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]; + } + + // ---------------------------------------------------- + // 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) + #define ADCMULT_TO_UNITS 1000000 // @at016a + #define ADCMULT_ROUND ADCMULT_TO_UNITS/2 // @mw580 + 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; // @at016c + sensor_update(AMECSENSOR_PTR(PWR250USP0), (uint16_t) temp32); + // Save off the combined power from all modules + //@rt003a + 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) + #define ADCSINGLE_TO_CENTIUNITS 100 // @mw580 + // 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; // @at016c @mw580 + sensor_update( AMECSENSOR_PTR(PWR250USVDD0), (uint16_t)temp32); + temp32 = (l_vdd)/ADCSINGLE_TO_CENTIUNITS; // Current is in 0.01 Amps, + sensor_update( AMECSENSOR_PTR(CUR250USVDD0), l_vdd); + temp32 = ((l_vcs_vio_vpcie * l_bulk_voltage)+ADCMULT_ROUND)/ADCMULT_TO_UNITS; // @at016c @mw580 + sensor_update( AMECSENSOR_PTR(PWR250USVCS0), (uint16_t)temp32); + + // ---------------------------------------------------- + // Convert Other Raw Misc Power from APSS into sensors + // ---------------------------------------------------- + + // Fans + 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; // @at016c @mw580 + sensor_update( AMECSENSOR_PTR(PWR250USFAN), (uint16_t)temp32); + + // I/O + 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; // @at016c @mw580 + sensor_update( AMECSENSOR_PTR(PWR250USIO), (uint16_t)temp32); + + // Memory + temp32 = ADC_CONVERTED_VALUE(G_sysConfigData.apss_adc_map.memory[l_proc]); + temp32 = ((temp32 * l_bulk_voltage)+ADCMULT_ROUND)/ADCMULT_TO_UNITS; // @at016c @mw580 + sensor_update( AMECSENSOR_PTR(PWR250USMEM0), (uint16_t)temp32); + // Save off the combined power from all memory + //@rt003a + 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]); + g_amec->mem_snr_pwr[l_idx] = ((l_temp * l_bulk_voltage)+ADCMULT_ROUND)/ADCMULT_TO_UNITS; // @mw580 + } + + // 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; // @at016c @mw580 + 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 Tuleta, 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; // @at016c @mw580 + + // 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; // @at016c @mw580 + } + sensor_update(AMECSENSOR_PTR(PWR250US), (uint16_t)temp32); + + //@fk004a - 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; + + // @fk004a + // 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). +// +// Flow: FN= +// +// Thread: RealTime Loop +// +// Task Flags: +// +// 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 = 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; + + // Obtain the 'fan_full_speed' GPIO from APSS + l_pin = G_sysConfigData.apss_gpio_map.fans_full_speed; + + // No longer reading gpio from APSS in GA1 due to instability in APSS composite mode -- gm026 + //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 +// +// +// Flow: FN= +// +// Thread: RealTime Loop +// +// Task Flags: +// +// 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); //gm034 (at malcolm's request) + + // 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); +} |