// Function Specification // // Name: proc_trace_pstate_table_quick // // Description: Debug Function to Print portion of Pstate Table // Eventually, this should trace key elements of Pstate // table to Trace Buffer. // // End Function Specification void proc_trace_pstate_table_quick(void) { GlobalPstateTable * l_gpst_ptr = NULL; l_gpst_ptr = gpsm_gpst(); // Check the pointer since it may not have been installed on chips with 0 configured cores if(l_gpst_ptr == &G_global_pstate_table) { TRAC_IMP("GPST Installed: Pstate[0]: %d kHz, Step: %d kHz, Entries: %d, Pvsafe[%d], Psafe[%d]", l_gpst_ptr->pstate0_frequency_khz, l_gpst_ptr->frequency_step_khz, (int8_t) l_gpst_ptr->entries, (int8_t) l_gpst_ptr->pvsafe, (int8_t) l_gpst_ptr->psafe ); TRAC_IMP("Pmin[%d]: %d kHz, Pmax[%d]: %d kHz", (int8_t) l_gpst_ptr->pmin, (l_gpst_ptr->pstate0_frequency_khz + ((int8_t) l_gpst_ptr->pmin) * l_gpst_ptr->frequency_step_khz), ((int8_t) l_gpst_ptr->pmin + l_gpst_ptr->entries - 1), (l_gpst_ptr->pstate0_frequency_khz + ((int8_t) l_gpst_ptr->pmin + l_gpst_ptr->entries - 1) * l_gpst_ptr->frequency_step_khz) ); } else { //This likely means that the processor has no configured cores (may not be an error scenario) TRAC_IMP("GPST not installed. hw pointer= 0x%08x, present cores= 0x%08x", (uint32_t)l_gpst_ptr, G_present_cores); } }
// Function Specification // // Name: SMGR_mode_transition_to_nominal // // Description: // // End Function Specification errlHndl_t SMGR_mode_transition_to_nominal() { errlHndl_t l_errlHndl = NULL; TRAC_IMP("SMGR: Mode to Nominal Transition Started"); // Set Freq Mode for AMEC to use l_errlHndl = amec_set_freq_range(OCC_MODE_NOMINAL); CURRENT_MODE() = OCC_MODE_NOMINAL; TRAC_IMP("SMGR: Mode to Nominal Transition Completed"); return l_errlHndl; }
// Function Specification // // Name: SMGR_mode_transition_to_powersave // // Description: // // End Function Specification errlHndl_t SMGR_mode_transition_to_powersave() { errlHndl_t l_errlHndl = NULL; TRAC_IMP("SMGR: Mode to PowerSave Transition Started"); // Set Freq Mode for AMEC to use l_errlHndl = amec_set_freq_range(OCC_MODE_PWRSAVE); CURRENT_MODE() = OCC_MODE_PWRSAVE; TRAC_IMP("SMGR: Mode to PowerSave Transition Completed"); return l_errlHndl; }
// Function Specification // // Name: SMGR_mode_transition_to_dynpowersave_fp // // Description: // // End Function Specification errlHndl_t SMGR_mode_transition_to_dynpowersave_fp() { errlHndl_t l_errlHndl = NULL; TRAC_IMP("SMGR: Mode to Dynamic PowerSave-Favor Performance Transition Started"); // Set Freq Mode for AMEC to use l_errlHndl = amec_set_freq_range(OCC_MODE_DYN_POWER_SAVE_FP); CURRENT_MODE() = OCC_MODE_DYN_POWER_SAVE_FP; TRAC_IMP("SMGR: Mode to Dynamic PowerSave-Favor Performance Transition Completed"); return l_errlHndl; }
// Function Specification // // Name: SMGR_mode_transition_to_ffo // // Description: // // End Function Specification errlHndl_t SMGR_mode_transition_to_ffo() { errlHndl_t l_errlHndl = NULL; TRAC_IMP("SMGR: Mode to FFO Transition Started"); // Set Freq Mode for AMEC to use l_errlHndl = amec_set_freq_range(OCC_MODE_FFO); CURRENT_MODE() = OCC_MODE_FFO; TRAC_IMP("SMGR: Mode to FFO Transition Completed"); return l_errlHndl; }
// Update I2C log information for specified engine // i_op values: // LOC_ACQUIRE = OCC should take ownership of lock // LOC_RELEASE = OCC should release ownership of lock and notify host void update_i2c_lock(const lockOperation_e i_op, const uint8_t i_engine) { ocb_occflg_t occ_flags = {0}; #ifdef DEBUG_LOCK_TESTING ocb_occflg_t flag; flag.value = in32(OCB_OCCFLG); if (LOCK_RELEASE == i_op) { LOCK_DBG("update_i2c_lock: I2C engine %d RELEASE - host=%d, occ=%d, dimmTick=%d", i_engine, flag.fields.i2c_engine3_lock_host, flag.fields.i2c_engine3_lock_occ, DIMM_TICK); } else { LOCK_DBG("update_i2c_lock: I2C engine %d LOCK - host=%d, occ=%d, dimmTick=%d", i_engine, flag.fields.i2c_engine3_lock_host, flag.fields.i2c_engine3_lock_occ, DIMM_TICK); } #endif if (PIB_I2C_ENGINE_E == i_engine) { occ_flags.fields.i2c_engine3_lock_occ = 1; } else if (PIB_I2C_ENGINE_D == i_engine) { occ_flags.fields.i2c_engine2_lock_occ = 1; } else if (PIB_I2C_ENGINE_C == i_engine) { occ_flags.fields.i2c_engine1_lock_occ = 1; } if (LOCK_RELEASE == i_op) { out32(OCB_OCCFLG_CLR, occ_flags.value); // OCC had the lock and host wants it, so send interrupt to host notify_host(INTR_REASON_I2C_OWNERSHIP_CHANGE); TRAC_IMP("update_i2c_lock: OCC has released lock for I2C engine %d", i_engine); } else // LOCK_ACQUIRE { out32(OCB_OCCFLG_OR, occ_flags.value); TRAC_IMP("update_i2c_lock: OCC has acquired lock for I2C engine %d", i_engine); } } // end update_i2c_lock()
//************************************************************************* // Entry point function //************************************************************************* errlHndl_t traceTest(void * i_arg) { errlHndl_t l_err = NULL; UINT l_rc = 0; do { // function unit test l_rc = traceFuncTest(); if(l_rc) { printf("traceTest Applet: Function test failed\n"); break; } // Macro test: test basic trace macros with/without parameters // int: supported TRAC_INFO(para_int_0); TRAC_INFO(para_int_1, 1); TRAC_INFO(para_int_5, 1, 2, 3, 4, 5); TRAC_INFO(para_int_6, 1, 2, 3, 4, 5, 6); // hex: supported TRAC_ERR(para_hex_0); TRAC_ERR(para_hex_1, 0xA); TRAC_ERR(para_hex_5, 0xA, 0xB, 0xC, 0xD, 0xE); TRAC_ERR(para_hex_6, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF); // char: not supported TRAC_IMP(para_int_0); TRAC_IMP(para_chr_1, "1"); TRAC_IMP(para_chr_5, "1", "2", "3", "4", "5"); TRAC_IMP(para_chr_6, "1", "2", "3", "4", "5", "6"); #ifdef TEST_SEMAPHORE // semaphore test l_rc = traceSemTest(); if(l_rc) { printf("traceTest Applet: Semaphore test failed\n"); break; } #endif }while(0); printf("traceTest Applet: test finished\n"); return l_err; }
// Function Specification // // Name: reset_wof_clear_inhibit // // Description: This function clears the inhibit bits that are // set as part of the WOF function // // End Function Specification void reset_wof_clear_inhibit() { uint64_t l_data64 = 0; uint32_t l_rc = 0; // Do not inhibit core wakeup anymore l_data64 = 0x0000000000000000ull; l_rc = _putscom(PDEMR, l_data64, SCOM_TIMEOUT); if (l_rc != 0) { TRAC_ERR("reset_wof_clear_inhibit: Error writing to PDEMR register! addr[0x%08X] rc[0x%08X]", PDEMR, l_rc); } else { TRAC_IMP("reset_wof_clear_inhibit: PDEMR register has been successfully cleared"); } }
int traceFuncTest() { UINT l_rc = 0; UINT l_max_trace_entries = TRACE_BUFFER_SIZE / MIN_TRACE_ENTRY_SIZE; UINT l_entry_count = 0; UINT l_buffer_size = 0; tracDesc_t l_head = NULL; do { // Test target - trac_write_XXX(), TRAC_get_buffer() and TRAC_get_td() // This testcase would create l_max_trace_entries +1 trace entries // to fill trace buffer, times_wrap should be larger than zero do{ l_entry_count++; TRAC_INFO("traceTest applet INFO record: count %d", (int)l_entry_count); TRAC_ERR("traceTest applet ERR record: count %d", (int)l_entry_count); TRAC_IMP("traceTest applet IMP record: count %d", (int)l_entry_count); }while(l_max_trace_entries >= l_entry_count); // Check times_wrap in TRAC_INFO. // Because structures are all the same, skip TRAC_ERR and TRAC_IMP l_rc = TRAC_get_buffer(TRAC_get_td("INF"), G_trac_buffer); l_head = (tracDesc_t)&G_trac_buffer; if((l_rc != 0 ) || (l_head->times_wrap == 0)) { printf("Fail: times_wrap error in trace buffer: %d, %d\n", l_rc, l_head->times_wrap); break; } // Test target - TRAC_get_buffer() and TRAC_get_td() // case: invalid parameters l_rc = TRAC_get_buffer(TRAC_get_td("INF"), NULL); l_head = (tracDesc_t)&G_trac_buffer; if(l_rc == 0) { printf("TRAC_get_buffer(), reason code: %d\n", l_rc); printf("Fail: test TRAC_get_buffer() invalid 1th parameter\n"); break; } l_rc = TRAC_get_buffer(NULL, G_trac_buffer); l_head = (tracDesc_t)&G_trac_buffer; if(l_rc == 0) { printf("TRAC_get_buffer(), reason code: %d\n", l_rc); printf("Fail: test TRAC_get_buffer() invalid 2nd parameter\n"); break; } // Test target - TRAC_get_buffer_partial() and TRAC_get_td() // case: invalid parameters l_buffer_size = TRACE_BUFFER_SIZE; l_rc = TRAC_get_buffer_partial(NULL, G_trac_buffer, &l_buffer_size); if((l_rc != TRAC_INVALID_PARM) && (l_buffer_size !=0)) { printf("TRAC_get_buffer_partial(), reason code: %d\n", l_rc); printf("Fail: test TRAC_get_buffer_partial() invalid 1st parameter\n"); break; } l_rc = TRAC_get_buffer_partial(TRAC_get_td("UNKNOWN"), NULL, &l_buffer_size); if((l_rc != TRAC_INVALID_PARM) && (l_buffer_size !=0)) { printf("TRAC_get_buffer_partial(), reason code: %d\n", l_rc); printf("Fail: test TRAC_get_buffer_partial() invalid 1st parameter\n"); break; } l_rc = TRAC_get_buffer_partial(TRAC_get_td("INF"), NULL, &l_buffer_size); if((l_rc != TRAC_INVALID_PARM) && (l_buffer_size !=0)) { printf("TRAC_get_buffer_partial(), reason code: %d\n", l_rc); printf("Fail: test TRAC_get_buffer_partial() invalid 2nd parameter\n"); break; } l_rc = TRAC_get_buffer_partial(TRAC_get_td("ERR"), G_trac_buffer, NULL); if(l_rc != TRAC_INVALID_PARM) { printf("TRAC_get_buffer_partial(), reason code: %d\n", l_rc); printf("Fail: test TRAC_get_buffer_partial() invalid 3rd parameter\n"); break; } // Test target - TRAC_get_buffer_partial() // case: input buffer less then the size of trace buffer header l_buffer_size = sizeof(trace_buf_head_t) - 1; l_rc = TRAC_get_buffer_partial(TRAC_get_td("IMP"), G_trac_buffer, &l_buffer_size); if(l_rc != TRAC_DATA_SIZE_LESS_THAN_HEADER_SIZE) { printf("TRAC_get_buffer_partial(), reason code: %d\n", l_rc); printf("Fail: test TRAC_get_buffer_partial() with illegal small input buffer\n"); break; } // Test target - TRAC_get_buffer_partial() // case: input buffer is small then then trace buffer l_buffer_size = sizeof(trace_buf_head_t) + (TRACE_BUFFER_SIZE/4); l_rc = TRAC_get_buffer_partial(TRAC_get_td("INF"), G_trac_buffer, &l_buffer_size); if(l_rc) { printf("TRAC_get_buffer_partial(), reason code: %d\n", l_rc); printf("Fail: test TRAC_get_buffer_partial() with small input buffer\n"); break; } // Test target - TRAC_get_buffer_partial() // case: input buffer is larger then trace buffer l_buffer_size = sizeof(G_trac_buffer); l_rc = TRAC_get_buffer_partial(TRAC_get_td("INF"), G_trac_buffer, &l_buffer_size); if(l_rc || (l_buffer_size != TRACE_BUFFER_SIZE)) { printf("TRAC_get_buffer_partial(), reason code: %d size %d/%d\n", l_rc, l_buffer_size, TRACE_BUFFER_SIZE); printf("Fail: test TRAC_get_buffer_partial() with too large input buffer\n"); break; } // Test target - TRAC_reset_buf() and TRAC_get_buffer_partial() // case: clear trace buffer and check with buffer larger than trace buffer TRAC_reset_buf(); l_buffer_size = sizeof(G_trac_buffer); l_rc = TRAC_get_buffer_partial(TRAC_get_td("ERR"), G_trac_buffer, &l_buffer_size); if(l_rc) { printf("TRAC_get_buffer_partial(), reason code: %d\n", l_rc); printf("Fail: test TRAC_reset_buf()/TRAC_get_buffer_partial() with empty trace\n"); break; } // Test target - TRAC_reset_buf() and TRAC_get_buffer_partial() // case: clear trace buffer and check it with buffer smaller than trace buffer TRAC_reset_buf(); l_buffer_size = TRACE_BUFFER_SIZE/2; l_rc = TRAC_get_buffer_partial(TRAC_get_td("ERR"), G_trac_buffer, &l_buffer_size); if(l_rc) { printf("TRAC_get_buffer_partial(), reason code: %d\n", l_rc); printf("Fail: test TRAC_reset_buf()/TRAC_get_buffer_partial() with empty trace\n"); break; } // Test target - TRAC_get_buffer_partial() // case: create some traces and test with large input buffer l_entry_count = 0; do{ l_entry_count++; TRAC_INFO("traceTest applet INFO record: count %d", (int)l_entry_count); TRAC_ERR("traceTest applet ERR record: count %d", (int)l_entry_count); TRAC_IMP("traceTest applet IMP record: count %d", (int)l_entry_count); }while((l_max_trace_entries/4) >= l_entry_count); l_buffer_size = TRACE_BUFFER_SIZE; l_rc = TRAC_get_buffer_partial(TRAC_get_td("IMP"), G_trac_buffer, &l_buffer_size); l_head = (tracDesc_t)&G_trac_buffer; if(l_rc || (l_head->times_wrap != 0)) { printf("TRAC_get_buffer_partial(), reason code: %d\n", l_rc); printf("Fail: test TRAC_get_buffer_partial() with large input buffer\n"); break; } // Test target - TRAC_get_buffer_partial() // case: create some traces and test with small input buffer l_buffer_size = sizeof(trace_buf_head_t) + (TRACE_BUFFER_SIZE/4); l_rc = TRAC_get_buffer_partial(TRAC_get_td("INF"), G_trac_buffer, &l_buffer_size); if(l_rc) { printf("TRAC_get_buffer_partial(), reason code: %d\n", l_rc); printf("Fail: test TRAC_get_buffer_partial() with small input buffer\n"); break; } }while(0); return l_rc; }
// Function Specification // // Name: task_check_for_checkstop // // Description: Check for checkstop // // End Function Specification void task_check_for_checkstop(task_t *i_self) { pore_status_t l_gpe0_status; ocb_oisr0_t l_oisr0_status; static bool L_checkstop_traced = FALSE; uint8_t l_reason_code = 0; do { // This check is disabled once a checkstop or frozen GPE is detected if(L_checkstop_traced) { break; } // Looked for a frozen GPE, a sign that the chip has stopped working or // check-stopped. This check also looks for an interrupt status flag that // indicates if the system has check-stopped. l_gpe0_status.value = in64(PORE_GPE0_STATUS); l_oisr0_status.value = in32(OCB_OISR0); if (l_gpe0_status.fields.freeze_action || l_oisr0_status.fields.check_stop) { errlHndl_t l_err = NULL; if (l_gpe0_status.fields.freeze_action) { TRAC_IMP("Frozen GPE0 detected by RTL"); l_reason_code = OCC_GPE_HALTED; } if (l_oisr0_status.fields.check_stop) { TRAC_IMP("System checkstop detected by RTL"); l_reason_code = OCC_SYSTEM_HALTED; } L_checkstop_traced = TRUE; /* * @errortype * @moduleid MAIN_SYSTEM_HALTED_MID * @reasoncode OCC_GPE_HALTED * @userdata1 High order word of PORE_GPE0_STATUS * @userdata2 OCB_OISR0 * @devdesc OCC detected frozen GPE0 */ /* * @errortype * @moduleid MAIN_SYSTEM_HALTED_MID * @reasoncode OCC_SYSTEM_HALTED * @userdata1 High order word of PORE_GPE0_STATUS * @userdata2 OCB_OISR0 * @devdesc OCC detected system checkstop */ l_err = createErrl(MAIN_SYSTEM_HALTED_MID, l_reason_code, OCC_NO_EXTENDED_RC, ERRL_SEV_INFORMATIONAL, NULL, DEFAULT_TRACE_SIZE, l_gpe0_status.words.high_order, l_oisr0_status.value); // The commit code will check for the frozen GPE0 and system // checkstop conditions and take appropriate actions. commitErrl(&l_err); } } while(0); }
// Function Specification // // Name: dcom_error_check // // Description: keep track of failure counts // // End Function Specification void dcom_error_check( const dcom_error_type_t i_error_type, const bool i_clear_error, const uint32_t i_orc, const uint32_t i_orc_ext) { static uint16_t L_rx_slv_outbox_fail_count = 0; uint16_t l_modId = 0; uint16_t *l_count_ptr = NULL; if ( i_error_type == SLAVE_INBOX ) { l_count_ptr = &G_dcomSlvInboxCounter.currentFailCount; l_modId = DCOM_MID_TASK_RX_SLV_INBOX; } // if the i_error_type == SLAVE_OUTBOX then set the outbox count else { l_count_ptr = &L_rx_slv_outbox_fail_count; l_modId = DCOM_MID_TASK_RX_SLV_OUTBOX; } if ( i_clear_error ) { *l_count_ptr = 0; } else { (*l_count_ptr)++; if ( *l_count_ptr == DCOM_250us_GAP ) { // Trace an imp trace log TRAC_IMP("l_count_ptr[%d], L_outbox[%d], L_inbox[%d]", *l_count_ptr, L_rx_slv_outbox_fail_count, G_dcomSlvInboxCounter.currentFailCount ); } else if ( *l_count_ptr == DCOM_4MS_GAP ) { // Create and commit error log // NOTE: SRC tags are NOT needed here, they are // taken care of by the caller errlHndl_t l_errl = createErrl( l_modId, //ModId i_orc, //Reasoncode i_orc_ext, //Extended reasoncode ERRL_SEV_UNRECOVERABLE, //Severity NULL, //Trace Buf DEFAULT_TRACE_SIZE, //Trace Size *l_count_ptr, //Userdata1 0 //Userdata2 ); // Commit log commitErrl( &l_errl ); // Call request nominal macro to change state REQUEST_NOMINAL(); } else if ( *l_count_ptr == DCOM_1S_GAP ) { // Create and commit error log // NOTE: SRC tags are NOT needed here, they are // taken care of by the caller errlHndl_t l_errl = createErrl( l_modId, //ModId i_orc, //Reasoncode i_orc_ext, //Extended reasoncode ERRL_SEV_UNRECOVERABLE, //Severity NULL, //Trace Buf DEFAULT_TRACE_SIZE, //Trace Size *l_count_ptr, //Userdata1 0 //Userdata2 ); // Commit log // Call request reset macro REQUEST_RESET(l_errl); } } }
// Function Specification // // Name: dcom_initialize_roles // // Description: Initialize roles so we know if we are master or slave // // End Function Specification void dcom_initialize_roles(void) { G_occ_role = OCC_SLAVE; // Locals pba_xcfg_t pbax_cfg_reg; // Used as a debug tool to correlate time between OCCs & System Time // getscom_ffdc(OCB_OTBR, &G_dcomTime.tod, NULL); // Commits errors internally G_dcomTime.tod = in64(OCB_OTBR) >> 4; G_dcomTime.base = ssx_timebase_get(); pbax_cfg_reg.value = in64(PBA_XCFG); if(pbax_cfg_reg.fields.rcv_groupid < MAX_NUM_NODES && pbax_cfg_reg.fields.rcv_chipid < MAX_NUM_OCC) { TRAC_IMP("Proc ChipId (%d) NodeId (%d)", pbax_cfg_reg.fields.rcv_chipid, pbax_cfg_reg.fields.rcv_groupid); G_pbax_id.valid = 1; G_pbax_id.node_id = pbax_cfg_reg.fields.rcv_groupid; G_pbax_id.chip_id = pbax_cfg_reg.fields.rcv_chipid; G_pbax_id.module_id = G_pbax_id.chip_id; // Always start as OCC Slave G_occ_role = OCC_SLAVE; rtl_set_run_mask(RTL_FLAG_NOTMSTR); // Set the initial presence mask, and count the number of occ's present G_sysConfigData.is_occ_present |= (0x01 << G_pbax_id.chip_id); G_occ_num_present = __builtin_popcount(G_sysConfigData.is_occ_present); } else // Invalid chip/node ID(s) { TRAC_ERR("Proc ChipId (%d) and/or NodeId (%d) too high: request reset", pbax_cfg_reg.fields.rcv_chipid, pbax_cfg_reg.fields.rcv_groupid); /* @ * @errortype * @moduleid DCOM_MID_INIT_ROLES * @reasoncode INVALID_CONFIG_DATA * @userdata1 PBAXCFG (upper) * @userdata2 PBAXCFG (lower) * @userdata4 ERC_CHIP_IDS_INVALID * @devdesc Failure determining OCC role */ errlHndl_t l_errl = createErrl( DCOM_MID_INIT_ROLES, //ModId INVALID_CONFIG_DATA, //Reasoncode ERC_CHIP_IDS_INVALID, //Extended reasoncode ERRL_SEV_UNRECOVERABLE, //Severity NULL, //Trace Buf DEFAULT_TRACE_SIZE, //Trace Size pbax_cfg_reg.words.high_order, //Userdata1 pbax_cfg_reg.words.low_order //Userdata2 ); // Callout firmware addCalloutToErrl(l_errl, ERRL_CALLOUT_TYPE_COMPONENT_ID, ERRL_COMPONENT_ID_FIRMWARE, ERRL_CALLOUT_PRIORITY_HIGH); //Add processor callout addCalloutToErrl(l_errl, ERRL_CALLOUT_TYPE_HUID, G_sysConfigData.proc_huid, ERRL_CALLOUT_PRIORITY_LOW); G_pbax_id.valid = 0; // Invalid Chip/Node ID } // Initialize DCOM Thread Sem ssx_semaphore_create( &G_dcomThreadWakeupSem, // Semaphore 1, // Initial Count 0); // No Max Count }
// Function Specification // // Name: proc_gpsm_dcm_sync_enable_pstates_smh // // Description: Step through all the states & synch needed to enable // Pstates on both master & slave on a DCM. This also // works for a SCM, which will act as DCM master (as far // as this function is concerned.) // // End Function Specification void proc_gpsm_dcm_sync_enable_pstates_smh(void) { // Static Locals static GpsmEnablePstatesMasterInfo l_master_info; static Pstate l_voltage_pstate, l_freq_pstate; // Local Variables int l_rc = 0; errlHndl_t l_errlHndl = NULL; if(!gpsm_dcm_slave_p()) { // --------------------------------------- // SCM or DCM Master // --------------------------------------- switch( G_proc_dcm_sync_state.sync_state_master ) { case PROC_GPSM_SYNC_NO_PSTATE_TABLE: // Waiting for Pstate Table from TMGT break; case PROC_GPSM_SYNC_PSTATE_TABLE_INSTALLED: PROC_DBG("GPST DCM Master State %d\n",G_proc_dcm_sync_state.sync_state_master); // DCM SYNC (MasterWaitForSlave): Wait for slave to install Pstate table if(gpsm_dcm_mode_p()){ if( G_proc_dcm_sync_state.sync_state_slave == PROC_GPSM_SYNC_PSTATE_TABLE_INSTALLED) { // Move to next state in state machine G_proc_dcm_sync_state.sync_state_master = PROC_GPSM_SYNC_READY_TO_ENABLE_MASTER; } } else { // Move to next state in state machine G_proc_dcm_sync_state.sync_state_master = PROC_GPSM_SYNC_READY_TO_ENABLE_MASTER; } break; case PROC_GPSM_SYNC_READY_TO_ENABLE_MASTER: PROC_DBG("GPST DCM Master State %d\n",G_proc_dcm_sync_state.sync_state_master); // Pstate tables has been installed, so now Master can start to enable Pstates l_rc = gpsm_enable_pstates_master(&l_master_info, &l_voltage_pstate, &l_freq_pstate); if(l_rc) { // Error TRAC_ERR("MSTR: gpsm_enable_pstates_master failed with rc=0x%08x", l_rc); G_proc_dcm_sync_state.sync_state_master = PROC_GPSM_SYNC_PSTATE_ERROR; break; } TRAC_IMP("MSTR: Initial Pstates: V: %d, F: %d\n",l_voltage_pstate, l_freq_pstate); // DCM SYNC (Master2Slave): Send V & F Pstate to slave G_proc_dcm_sync_state.dcm_pair_id = G_pob_id.chip_id; G_proc_dcm_sync_state.pstate_v = l_voltage_pstate; G_proc_dcm_sync_state.pstate_f = l_freq_pstate; // Move to next state in state machine G_proc_dcm_sync_state.sync_state_master = PROC_GPSM_SYNC_PSTATE_MASTER_ENABLED; break; case PROC_GPSM_SYNC_PSTATE_MASTER_ENABLED: PROC_DBG("GPST DCM Master State %d\n",G_proc_dcm_sync_state.sync_state_master); // DCM SYNC (MasterWaitForSlave): Wait for slave to complete gpsm_enable_pstates_slave() if(gpsm_dcm_mode_p()){ if( G_proc_dcm_sync_state.sync_state_slave == PROC_GPSM_SYNC_PSTATE_SLAVE_ENABLED) { // Move to next state in state machine G_proc_dcm_sync_state.sync_state_master = PROC_GPSM_SYNC_READY_TO_ENABLE_SLAVE; } } else { G_proc_dcm_sync_state.sync_state_master = PROC_GPSM_SYNC_READY_TO_ENABLE_SLAVE; } break; case PROC_GPSM_SYNC_READY_TO_ENABLE_SLAVE: PROC_DBG("GPST DCM Master State %d\n",G_proc_dcm_sync_state.sync_state_master); // Master does next step of enabling Pstates, now that slave has done it's enable l_rc = gpsm_enable_pstates_slave(&l_master_info, l_voltage_pstate, l_freq_pstate); if(l_rc) { // Error TRAC_ERR("MSTR: gpsm_enable_pstates_slave failed with rc=0x%08x", l_rc); G_proc_dcm_sync_state.sync_state_master = PROC_GPSM_SYNC_PSTATE_ERROR; break; } TRAC_INFO("MSTR: Completed DCM Pstate Slave Init\n"); G_proc_dcm_sync_state.sync_state_master = PROC_GPSM_SYNC_PSTATE_SLAVE_ENABLED; break; case PROC_GPSM_SYNC_PSTATE_SLAVE_ENABLED: PROC_DBG("GPST DCM Master State %d\n",G_proc_dcm_sync_state.sync_state_master); // Master puts this chip in Pstate HW mode l_rc = gpsm_hw_mode(); if(l_rc) { // Error TRAC_ERR("MSTR: gpsm_hw_mode failed with rc=0x%08x", l_rc); G_proc_dcm_sync_state.sync_state_master = PROC_GPSM_SYNC_PSTATE_ERROR; break; } // DCM SYNC (Master2Slave): Tell Slave Master has entered HW mmode G_proc_dcm_sync_state.sync_state_master = PROC_GPSM_SYNC_PSTATE_HW_MODE; break; case PROC_GPSM_SYNC_PSTATE_HW_MODE: PROC_DBG("GPST DCM Master State %d\n",G_proc_dcm_sync_state.sync_state_master); // DCM SYNC (Master2Slave): Wait for Slave to Enter HW Mode if(gpsm_dcm_mode_p()){ if( G_proc_dcm_sync_state.sync_state_slave == PROC_GPSM_SYNC_PSTATE_HW_MODE) { TRAC_INFO("MSTR: Completed DCM Pstate Enable"); G_proc_dcm_sync_state.sync_state_master = PROC_GPSM_SYNC_PSTATE_HW_MODE_ENABLED; //do additional setup if in kvm mode proc_pstate_kvm_setup(); } } else { G_proc_dcm_sync_state.sync_state_master = PROC_GPSM_SYNC_PSTATE_HW_MODE_ENABLED; TRAC_INFO("MSTR: Completed SCM Pstate Enable"); //do additional setup if in kvm mode proc_pstate_kvm_setup(); } break; case PROC_GPSM_SYNC_PSTATE_HW_MODE_ENABLED: // Final State // Pstates Enabled on both modules in DCM break; case PROC_GPSM_SYNC_PSTATE_ERROR: // Do nothing, something will have to come and kick us out of this state break; default: G_proc_dcm_sync_state.sync_state_master = PROC_GPSM_SYNC_NO_PSTATE_TABLE; break; } } else if (gpsm_dcm_slave_p()) { // --------------------------------------- // DCM Slave // - Don't need to check if DCM, since we can't come in here unless DCM // --------------------------------------- switch( G_proc_dcm_sync_state.sync_state_slave) { case PROC_GPSM_SYNC_NO_PSTATE_TABLE: // Waiting for Pstate Table from TMGT break; case PROC_GPSM_SYNC_PSTATE_TABLE_INSTALLED: // Pstate table has been installed, but slave needs to wait // for master before it can do anything else. // DCM SYNC (SlaveWaitForMaster): Send V & F Pstate to slave // Wait for Master to complete gpsm_enable_pstates_master() // before running gpsm_enable_pstates_slave() if( G_proc_dcm_sync_state.sync_state_master == PROC_GPSM_SYNC_PSTATE_MASTER_ENABLED) { // Go to next state G_proc_dcm_sync_state.sync_state_slave = PROC_GPSM_SYNC_PSTATE_MASTER_ENABLED; } break; case PROC_GPSM_SYNC_PSTATE_MASTER_ENABLED: PROC_DBG("GPST DCM Slave State %d\n",G_proc_dcm_sync_state.sync_state_slave); // Read the initial Pstates from the data DCM master sent l_voltage_pstate = G_proc_dcm_sync_state.pstate_v; l_freq_pstate = G_proc_dcm_sync_state.pstate_f; // NULL is passed to this function when run on dcm slave l_rc = gpsm_enable_pstates_slave(NULL, l_voltage_pstate, l_freq_pstate); if(l_rc) { // Error TRAC_ERR("SLV: gpsm_enable_pstates_slave failed with rc=0x%08x", l_rc); G_proc_dcm_sync_state.sync_state_slave = PROC_GPSM_SYNC_PSTATE_ERROR; break; } TRAC_INFO("SLV: Completed DCM Pstate Slave Init\n"); // DCM SYNC (Slave2Master): // Tell Master that slave has run gpsm_enable_pstates_slave() // Go to next state G_proc_dcm_sync_state.sync_state_slave = PROC_GPSM_SYNC_PSTATE_SLAVE_ENABLED; break; case PROC_GPSM_SYNC_PSTATE_SLAVE_ENABLED: // DCM SYNC (SlaveWaitForMaster): Wait for Master to run gpsm_hw_mode if( G_proc_dcm_sync_state.sync_state_master == PROC_GPSM_SYNC_PSTATE_HW_MODE) { // Enter Pstate HW mode l_rc = gpsm_hw_mode(); if(l_rc) { // Error TRAC_ERR("SLV: gpsm_hw_mode failed with rc=0x%08x", l_rc); G_proc_dcm_sync_state.sync_state_slave = PROC_GPSM_SYNC_PSTATE_ERROR; break; } // DCM SYNC (Slave2Master): Tell master that DCM slave made it to HW mode // Go to next state G_proc_dcm_sync_state.sync_state_slave = PROC_GPSM_SYNC_PSTATE_HW_MODE; } break; case PROC_GPSM_SYNC_PSTATE_HW_MODE: // Slave & Master now both know each other has HW mode enabled if( G_proc_dcm_sync_state.sync_state_master == PROC_GPSM_SYNC_PSTATE_HW_MODE_ENABLED) { G_proc_dcm_sync_state.sync_state_slave = PROC_GPSM_SYNC_PSTATE_HW_MODE_ENABLED; TRAC_INFO("SLV: Completed DCM Pstate Enable"); //do additional setup if in kvm mode proc_pstate_kvm_setup(); } break; case PROC_GPSM_SYNC_PSTATE_HW_MODE_ENABLED: // Final State // Pstates Enabled on both modules in DCM break; case PROC_GPSM_SYNC_PSTATE_ERROR: // Do nothing, something will have to come and kick us out of this state break; default: G_proc_dcm_sync_state.sync_state_slave = PROC_GPSM_SYNC_NO_PSTATE_TABLE; break; } } // If we are in the process of running through the state machine, // we will do a sem_post to speed up the DCOM Thread and step us // through faster. if( PROC_GPSM_SYNC_NO_PSTATE_TABLE != proc_gpsm_dcm_sync_get_my_state() && !proc_is_hwpstate_enabled() ) { ssx_semaphore_post(&G_dcomThreadWakeupSem); } // If we broke out of loops above because of an error, create an // error log and return it to caller. if(l_rc) { /* @ * @errortype * @moduleid PROC_ENABLE_PSTATES_SMH_MOD * @reasoncode SSX_GENERIC_FAILURE * @userdata1 SRAM Address of the Pstate Table * @userdata2 Return Code of call that failed * @userdata4 OCC_NO_EXTENDED_RC * @devdesc Failed to install Pstate Table */ l_errlHndl = createErrl( PROC_ENABLE_PSTATES_SMH_MOD, //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 (uint32_t) &G_global_pstate_table, //userdata1 l_rc); //userdata2 addCalloutToErrl(l_errlHndl, ERRL_CALLOUT_TYPE_COMPONENT_ID, ERRL_COMPONENT_ID_FIRMWARE, ERRL_CALLOUT_PRIORITY_HIGH); addCalloutToErrl(l_errlHndl, ERRL_CALLOUT_TYPE_HUID, G_sysConfigData.proc_huid, ERRL_CALLOUT_PRIORITY_LOW); REQUEST_RESET(l_errlHndl); } return; }
// Function Specification // // Name: proc_pstate_kvm_setup // // Description: Get everything set up for KVM mode // // End Function Specification void proc_pstate_kvm_setup() { int l_core; int l_rc = 0; uint32_t l_configured_cores; pcbs_pcbspm_mode_reg_t l_ppmr; pcbs_pmgp1_reg_t l_pmgp1; pcbs_power_management_bounds_reg_t l_pmbr; errlHndl_t l_errlHndl; do { //only run this in KVM mode if(!G_sysConfigData.system_type.kvm) { break; } l_configured_cores = ~in32(PMC_CORE_DECONFIGURATION_REG); // Do per-core configuration for(l_core = 0; l_core < PGP_NCORES; l_core++, l_configured_cores <<= 1) { if(!(l_configured_cores & 0x80000000)) continue; //do read-modify-write to allow pmax clip to also clip voltage (not just frequency) l_rc = getscom_ffdc(CORE_CHIPLET_ADDRESS(PCBS_PCBSPM_MODE_REG, l_core), &(l_ppmr.value), NULL); //commit errors internally if(l_rc) { TRAC_ERR("proc_pstate_kvm_setup: getscom(PCBS_PCBSPM_MODE_REG) failed. rc=%d, hw_core=%d", l_rc, l_core); break; } l_ppmr.fields.enable_clipping_of_global_pstate_req = 1; l_rc = putscom_ffdc(CORE_CHIPLET_ADDRESS(PCBS_PCBSPM_MODE_REG, l_core), l_ppmr.value, NULL); //commit errors internally if(l_rc) { TRAC_ERR("proc_pstate_kvm_setup: putscom(PCBS_PCBSPM_MODE_REG) failed. rc=%d, hw_core=%d", l_rc, l_core); break; } //per Vaidy Srinivasan, clear bit 11 in the Power Management GP1 register l_pmgp1.value = 0; l_pmgp1.fields.pm_spr_override_en = 1; l_rc = putscom_ffdc(CORE_CHIPLET_ADDRESS(PCBS_PMGP1_REG_AND, l_core), ~l_pmgp1.value, NULL); //commit errors internally if(l_rc) { TRAC_ERR("proc_pstate_kvm_setup: putscom(PCBS_PMGB1_REG_OR) failed. rc=0x%08x, hw_core=%d", l_rc, l_core); break; } //set pmax/pmin clip initial settings l_pmbr.value = 0; l_pmbr.fields.pmin_clip = gpst_pmin(&G_global_pstate_table)+1; //Per David Du, we must use pmin+1 to avoid gpsa hang l_pmbr.fields.pmax_clip = gpst_pmax(&G_global_pstate_table); l_rc = putscom_ffdc(CORE_CHIPLET_ADDRESS(PCBS_POWER_MANAGEMENT_BOUNDS_REG, l_core), l_pmbr.value, NULL); //commit errors internally if(l_rc) { TRAC_ERR("proc_pstate_kvm_setup: putscom(PCBS_POWER_MANAGEMENT_BOUNDS_REG) failed. rc=0x%08x, hw_core=%d", l_rc, l_core); break; } }// end of per-core config if(l_rc) { break; } // Set the voltage clipping register to match the pmax/pmin clip values set above. pmc_rail_bounds_register_t prbr; prbr.value = in32(PMC_RAIL_BOUNDS_REGISTER); prbr.fields.pmin_rail = gpst_pmin(&G_global_pstate_table); prbr.fields.pmax_rail = gpst_pmax(&G_global_pstate_table); TRAC_IMP("pmin clip pstate = %d, pmax clip pstate = %d", prbr.fields.pmin_rail, prbr.fields.pmax_rail); out32(PMC_RAIL_BOUNDS_REGISTER, prbr.value); // Initialize the sapphire table in SRAM (sets valid bit) populate_pstate_to_sapphire_tbl(); // copy sram image into mainstore HOMER populate_sapphire_tbl_to_mem(); TRAC_IMP("proc_pstate_kvm_setup: RUNNING IN KVM MODE"); }while(0); if(l_rc) { // Create Error Log and request reset /* @ * @errortype * @moduleid PROC_PSTATE_KVM_SETUP_MOD * @reasoncode PROC_SCOM_ERROR * @userdata1 l_configured_cores * @userdata2 Return Code of call that failed * @userdata4 OCC_NO_EXTENDED_RC * @devdesc OCC failed to scom a core register */ l_errlHndl = createErrl( PROC_PSTATE_KVM_SETUP_MOD, //modId PROC_SCOM_ERROR, //reasoncode OCC_NO_EXTENDED_RC, //Extended reason code ERRL_SEV_PREDICTIVE, //Severity NULL, //Trace Buf DEFAULT_TRACE_SIZE, //Trace Size l_configured_cores, //userdata1 l_rc //userdata2 ); addCalloutToErrl(l_errlHndl, ERRL_CALLOUT_TYPE_HUID, G_sysConfigData.proc_huid, ERRL_CALLOUT_PRIORITY_HIGH); addCalloutToErrl(l_errlHndl, ERRL_CALLOUT_TYPE_COMPONENT_ID, ERRL_COMPONENT_ID_FIRMWARE, ERRL_CALLOUT_PRIORITY_MED); REQUEST_RESET(l_errlHndl); } }
////////////////////////// // Function Specification // // Name: amec_gpu_pcap // // Description: Determine power cap for GPUs // // Thread: Real Time Loop // // End Function Specification void amec_gpu_pcap(bool i_oversubscription, bool i_active_pcap_changed, int32_t i_avail_power) { /*------------------------------------------------------------------------*/ /* Local Variables */ /*------------------------------------------------------------------------*/ uint8_t i = 0; uint32_t l_gpu_cap_mw = 0; uint16_t l_system_gpu_total_pcap = 0; // total GPU pcap required by system based on if currently in oversub or not static uint16_t L_total_gpu_pcap = 0; // Current total GPU pcap in effect static uint16_t L_n_plus_1_mode_gpu_total_pcap = 0; // Total GPU pcap required for N+1 (not in oversubscription) static uint16_t L_n_mode_gpu_total_pcap = 0; // Total GPU pcap required for oversubscription static uint16_t L_active_psr_gpu_total_pcap = 0; // Total GPU pcap for the currently set pcap and PSR static uint16_t L_per_gpu_pcap = 0; // Amount of L_total_gpu_pcap for each GPU static uint8_t L_psr = 100; // PSR value used in L_active_psr_gpu_total_pcap calculation static bool L_first_run = TRUE; // for calculations done only 1 time static uint32_t L_last_pcap_traced[MAX_NUM_GPU_PER_DOMAIN] = {0}; /*------------------------------------------------------------------------*/ /* Code */ /*------------------------------------------------------------------------*/ // If this is the first time running calculate the total GPU power cap for system power caps (N and N+1) if(L_first_run) { // calculate total GPU power cap for oversubscription if(g_amec->pcap.ovs_node_pcap > G_sysConfigData.total_non_gpu_max_pwr_watts) { // Take all non-GPU power away from the oversubscription power cap L_n_mode_gpu_total_pcap = g_amec->pcap.ovs_node_pcap - G_sysConfigData.total_non_gpu_max_pwr_watts; // Add back in the power that will be dropped by processor DVFS and memory throttling and give to GPUs L_n_mode_gpu_total_pcap += G_sysConfigData.total_proc_mem_pwr_drop_watts; } else { // This should not happen, the total non GPU power should never be higher than the N mode cap // Log error and set GPUs to minimum power cap L_n_mode_gpu_total_pcap = 0; // this will set minimum GPU power cap TRAC_ERR("amec_gpu_pcap: non GPU max power %dW is more than N mode pwr limit %dW", G_sysConfigData.total_non_gpu_max_pwr_watts, g_amec->pcap.ovs_node_pcap); /* @ * @errortype * @moduleid AMEC_GPU_PCAP_MID * @reasoncode GPU_FAILURE * @userdata1 N mode Power Cap watts * @userdata2 Total non-GPU power watts * @userdata4 ERC_GPU_N_MODE_PCAP_CALC_FAILURE * @devdesc Total non-GPU power more than N mode power cap * */ errlHndl_t l_err = createErrl(AMEC_GPU_PCAP_MID, GPU_FAILURE, ERC_GPU_N_MODE_PCAP_CALC_FAILURE, ERRL_SEV_PREDICTIVE, NULL, DEFAULT_TRACE_SIZE, g_amec->pcap.ovs_node_pcap, G_sysConfigData.total_non_gpu_max_pwr_watts); //Callout firmware addCalloutToErrl(l_err, ERRL_CALLOUT_TYPE_COMPONENT_ID, ERRL_COMPONENT_ID_FIRMWARE, ERRL_CALLOUT_PRIORITY_HIGH); commitErrl(&l_err); } // calculate total GPU power cap for N+1 (not in oversubscription) if(G_sysConfigData.pcap.system_pcap > G_sysConfigData.total_non_gpu_max_pwr_watts) { // Take all non-GPU power away from the N+1 power cap L_n_plus_1_mode_gpu_total_pcap = G_sysConfigData.pcap.system_pcap - G_sysConfigData.total_non_gpu_max_pwr_watts; // Add back in the power that will be dropped by processor DVFS and memory throttling and give to GPUs L_n_plus_1_mode_gpu_total_pcap += G_sysConfigData.total_proc_mem_pwr_drop_watts; } else { // This should not happen, the total non GPU power should never be higher than the N+1 mode cap // Log error and set GPUs to minimum power cap L_n_plus_1_mode_gpu_total_pcap = 0; // this will set minimum GPU power cap TRAC_ERR("amec_gpu_pcap: non GPU max power %dW is more than N+1 mode pwr limit %dW", G_sysConfigData.total_non_gpu_max_pwr_watts, G_sysConfigData.pcap.system_pcap); /* @ * @errortype * @moduleid AMEC_GPU_PCAP_MID * @reasoncode GPU_FAILURE * @userdata1 N+1 mode Power Cap watts * @userdata2 Total non-GPU power watts * @userdata4 ERC_GPU_N_PLUS_1_MODE_PCAP_CALC_FAILURE * @devdesc Total non-GPU power more than N+1 mode power cap * */ errlHndl_t l_err = createErrl(AMEC_GPU_PCAP_MID, GPU_FAILURE, ERC_GPU_N_PLUS_1_MODE_PCAP_CALC_FAILURE, ERRL_SEV_PREDICTIVE, NULL, DEFAULT_TRACE_SIZE, G_sysConfigData.pcap.system_pcap, G_sysConfigData.total_non_gpu_max_pwr_watts); //Callout firmware addCalloutToErrl(l_err, ERRL_CALLOUT_TYPE_COMPONENT_ID, ERRL_COMPONENT_ID_FIRMWARE, ERRL_CALLOUT_PRIORITY_HIGH); commitErrl(&l_err); } } // if first run // Calculate the total GPU power cap for the current active limit and PSR // this only needs to be calculated if either the active limit or PSR changed if( (L_first_run) || (i_active_pcap_changed) || (L_psr != G_sysConfigData.psr) ) { L_psr = G_sysConfigData.psr; if(g_amec->pcap.active_node_pcap > G_sysConfigData.total_non_gpu_max_pwr_watts) { // Take all non-GPU power away from the active power cap L_active_psr_gpu_total_pcap = g_amec->pcap.active_node_pcap - G_sysConfigData.total_non_gpu_max_pwr_watts; // Add back in the power that will be dropped by processor DVFS and memory throttling based on the PSR // to give to GPUs L_active_psr_gpu_total_pcap += ( (L_psr / 100) * G_sysConfigData.total_proc_mem_pwr_drop_watts ); } else { // Set GPUs to minimum power cap L_active_psr_gpu_total_pcap = 0; TRAC_IMP("amec_gpu_pcap: non GPU max power %dW is more than active pwr limit %dW", G_sysConfigData.total_non_gpu_max_pwr_watts, g_amec->pcap.active_node_pcap); } // Total GPU power cap is the lower of system (N+1 or oversubscription depending on if in oversub) // and the active power limit. We do not need to always account for oversubscription since // the automatic hw power brake will assert to the GPUs if there is a problem when oversub is // entered from the time OCC can set and GPUs react to a new power limit if(i_oversubscription) { // system in oversubscription use N mode cap l_system_gpu_total_pcap = L_n_mode_gpu_total_pcap; } else { // system is not in oversubscription use N+1 mode cap l_system_gpu_total_pcap = L_n_plus_1_mode_gpu_total_pcap; } L_total_gpu_pcap = (l_system_gpu_total_pcap < L_active_psr_gpu_total_pcap) ? l_system_gpu_total_pcap : L_active_psr_gpu_total_pcap; // Divide the total equally across all GPUs in the system if(G_first_num_gpus_sys) { L_per_gpu_pcap = L_total_gpu_pcap / G_first_num_gpus_sys; } else { L_per_gpu_pcap = 0; TRAC_ERR("amec_gpu_pcap: Called with no GPUs present!"); } } // Setup to send new power limit to GPUs. The actual sending of GPU power limit will be handled by task_gpu_sm() for (i=0; i<MAX_NUM_GPU_PER_DOMAIN; i++) { // Before sending a GPU a power limit the power limits must be read from the GPU to know min/max GPU allows if( GPU_PRESENT(i) && g_amec->gpu[i].pcap.pwr_limits_read ) { l_gpu_cap_mw = L_per_gpu_pcap * 1000; // convert W to mW // GPU is present and have min/max power limits from GPU // clip the GPU power limit to min/max GPU limit if needed if(l_gpu_cap_mw < g_amec->gpu[i].pcap.gpu_min_pcap_mw) // clip to min? { l_gpu_cap_mw = g_amec->gpu[i].pcap.gpu_min_pcap_mw; } else if(l_gpu_cap_mw > g_amec->gpu[i].pcap.gpu_max_pcap_mw) // clip to max? { l_gpu_cap_mw = g_amec->gpu[i].pcap.gpu_max_pcap_mw; } // check if this is a new power limit if(g_amec->gpu[i].pcap.gpu_desired_pcap_mw != l_gpu_cap_mw) { if( (g_amec->gpu[i].pcap.gpu_desired_pcap_mw != 0) || (L_last_pcap_traced[i] != l_gpu_cap_mw) ) { L_last_pcap_traced[i] = l_gpu_cap_mw; TRAC_IMP("amec_gpu_pcap: Updating GPU%d desired pcap %dmW to %dmW", i, g_amec->gpu[i].pcap.gpu_desired_pcap_mw, l_gpu_cap_mw); } g_amec->gpu[i].pcap.gpu_desired_pcap_mw = l_gpu_cap_mw; } } } // for each GPU L_first_run = FALSE; }
// Function Specification // // Name: reset_state_request // // Description: Request Reset States // // End Function Specification void reset_state_request(uint8_t i_request) { //TODO: This needs to be changed so that G_reset_state operations are // atomic. switch(i_request) { case RESET_REQUESTED_DUE_TO_ERROR: // In case we want to just halt() if fw requests a reset, this is // the place to do it. It is disabled by default, and there is no // code to eanble it. if( G_halt_on_reset_request ) { TRAC_ERR("Halt()"); // This isn't modeled very well in simics. OCC will go into an // infinite loop, which eventually would crash Simics. HALT_WITH_FIR_SET; } // If we have TMGT comm, and we aren't already in reset, set the reset // state to reset to enter the reset state machine. if(G_reset_state < RESET_REQUESTED_DUE_TO_ERROR) { TRAC_IMP("Activating reset required state."); G_reset_state = RESET_REQUESTED_DUE_TO_ERROR; // Post the semaphore to wakeup the thread that // will put us into SAFE state. ssx_semaphore_post(&G_dcomThreadWakeupSem); // Set RTL Flags here too, depending how urgent it is that we stop // running tasks. rtl_set_run_mask(RTL_FLAG_RST_REQ); } break; case NOMINAL_REQUESTED_DUE_TO_ERROR: if(G_reset_state < NOMINAL_REQUESTED_DUE_TO_ERROR) { TRAC_ERR("Going to Nominal because of error"); // May need to add counter if multiple places request nominal G_reset_state = NOMINAL_REQUESTED_DUE_TO_ERROR; //TODO: Will need to set some flag or event here } break; case RESET_NOT_REQUESTED: if(G_reset_state == NOMINAL_REQUESTED_DUE_TO_ERROR) { TRAC_IMP("Clearing Nominal Reset State because of error"); // May need to add counter check if multiple places request nominal G_reset_state = RESET_NOT_REQUESTED; //TODO: Will need to clear some flag or event here } break; default: break; } }
// 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 */ /*------------------------------------------------------------------------*/ uint8_t quad = 0; // loop through quads uint8_t core_num = 0; // core ID uint8_t core_idx = 0; // loop through cores within each quad Pstate pmax[MAXIMUM_QUADS] = {0}; // max pstate (min frequency) within each quad Pstate pmax_chip = 0; // highest Pstate (lowest frequency) across all quads bool l_atLeast1Core[MAXIMUM_QUADS] = {FALSE}; // at least 1 core present and online in quad bool l_atLeast1Quad = FALSE; // at least 1 quad online static bool L_mfg_set_trace[MAXIMUM_QUADS] = {FALSE}; static bool L_mfg_clear_trace[MAXIMUM_QUADS] = {FALSE}; /*------------------------------------------------------------------------*/ /* Code */ /*------------------------------------------------------------------------*/ // loop through all quads, get f_requests, translate to pstates and determine pmax across chip for (quad = 0; quad < MAXIMUM_QUADS; quad++) { for (core_idx=0; core_idx<NUM_CORES_PER_QUAD; core_idx++) // loop thru all cores in quad { core_num = (quad*NUM_CORES_PER_QUAD) + core_idx; // ignore core if freq request is 0 (core not present when amec_slv_proc_voting_box ran) if(g_amec->proc[0].core[core_num].f_request != 0) { l_atLeast1Core[quad] = TRUE; l_atLeast1Quad = TRUE; // The higher the pstate number, the lower the frequency if(pmax[quad] < proc_freq2pstate(g_amec->proc[0].core[core_num].f_request)) { pmax[quad] = proc_freq2pstate(g_amec->proc[0].core[core_num].f_request); if(pmax_chip < pmax[quad]) // check if this is a new lowest freq for the chip pmax_chip = pmax[quad]; } } } } // Skip determining new frequency if all cores in all quads are offline if(l_atLeast1Quad) { // check for mfg quad Pstate request and set Pstate for each quad for (quad = 0; quad < MAXIMUM_QUADS; quad++) { // set quad with no cores present to lowest frequency for the chip if(l_atLeast1Core[quad] == FALSE) pmax[quad] = pmax_chip; // check if there is a mnfg Pstate request for this quad if(g_amec->mnfg_parms.quad_pstate[quad] != 0xFF) { // use mnfg request if it is a lower frequency (higher pState) if(g_amec->mnfg_parms.quad_pstate[quad] > pmax[quad]) pmax[quad] = g_amec->mnfg_parms.quad_pstate[quad]; if(L_mfg_clear_trace[quad] == FALSE) L_mfg_set_trace[quad] = TRUE; } else if(L_mfg_clear_trace[quad] == TRUE) { TRAC_INFO("amec_slv_freq_smh: mfg Quad %d Pstate request cleared. New Pstate = 0x%02x", quad, pmax[quad]); L_mfg_clear_trace[quad] = FALSE; } #ifdef PROC_DEBUG if (G_desired_pstate[quad] != pmax[quad]) { TRAC_IMP("Updating Quad %d's Pstate to %d", quad, pmax[quad]); } #endif // update quad pstate request G_desired_pstate[quad] = pmax[quad]; if(L_mfg_set_trace[quad] == TRUE) { TRAC_INFO("amec_slv_freq_smh: mfg Quad %d Pstate request set = 0x%02x", quad, pmax[quad]); L_mfg_set_trace[quad] = FALSE; L_mfg_clear_trace[quad] = TRUE; } } } // if at least 1 core online }
void cent_update_nlimits(uint32_t i_cent) { /*------------------------------------------------------------------------*/ /* Local Variables */ /*------------------------------------------------------------------------*/ static uint32_t L_trace_throttle_count = 0; uint16_t l_mba01_mba_maxn, l_mba01_chip_maxn, l_mba23_mba_maxn, l_mba23_chip_maxn; /*------------------------------------------------------------------------*/ /* Code */ /*------------------------------------------------------------------------*/ do { centaur_throttle_t* l_active_limits01 = &G_centaurThrottleLimits[i_cent][0]; centaur_throttle_t* l_active_limits23 = &G_centaurThrottleLimits[i_cent][1]; mem_throt_config_data_t* l_state_limits01 = &G_sysConfigData.mem_throt_limits[i_cent][0]; mem_throt_config_data_t* l_state_limits23 = &G_sysConfigData.mem_throt_limits[i_cent][1]; //Minimum N value is not state dependent l_active_limits01->min_n_per_mba = l_state_limits01->min_ot_n_per_mba; l_active_limits23->min_n_per_mba = l_state_limits23->min_ot_n_per_mba; //oversubscription? if(AMEC_INTF_GET_OVERSUBSCRIPTION()) { l_mba01_mba_maxn = l_state_limits01->ovs_n_per_mba; l_mba01_chip_maxn = l_state_limits01->ovs_n_per_chip; l_mba23_mba_maxn = l_state_limits23->ovs_n_per_mba; l_mba23_chip_maxn = l_state_limits23->ovs_n_per_chip; } else if(CURRENT_MODE() == OCC_MODE_NOMINAL) { l_mba01_mba_maxn = l_state_limits01->nom_n_per_mba; l_mba01_chip_maxn = l_state_limits01->nom_n_per_chip; l_mba23_mba_maxn = l_state_limits23->nom_n_per_mba; l_mba23_chip_maxn = l_state_limits23->nom_n_per_chip; } else //DPS, TURBO, FFO, and SPS modes will use these settings { l_mba01_mba_maxn = l_state_limits01->turbo_n_per_mba; l_mba01_chip_maxn = l_state_limits01->turbo_n_per_chip; l_mba23_mba_maxn = l_state_limits23->turbo_n_per_mba; l_mba23_chip_maxn = l_state_limits23->turbo_n_per_chip; } l_active_limits01->max_n_per_chip = l_mba01_chip_maxn; l_active_limits23->max_n_per_chip = l_mba23_chip_maxn; //Trace when the MBA max N value changes if((l_mba01_mba_maxn != l_active_limits01->max_n_per_mba) || (l_mba23_mba_maxn != l_active_limits23->max_n_per_mba)) { l_active_limits01->max_n_per_mba = l_mba01_mba_maxn; l_active_limits23->max_n_per_mba = l_mba23_mba_maxn; //Don't trace every MBA changing, just one if(!L_trace_throttle_count) { L_trace_throttle_count = CENT_TRACE_THROTTLE_DELAY; TRAC_IMP("New MBA throttle max|min N values: mba01[0x%08x], mba23[0x%08x]", (uint32_t)((l_mba01_mba_maxn << 16) | l_active_limits01->min_n_per_mba), (uint32_t)((l_mba23_mba_maxn << 16) | l_active_limits23->min_n_per_mba)); break; } } if(L_trace_throttle_count) { L_trace_throttle_count--; } }while(0); }
////////////////////////// // Function Specification // // Name: amec_pcap_calc // // Description: Calculate the node, memory and processor power caps. // // Thread: Real Time Loop // // End Function Specification void amec_pcap_calc(const bool i_oversub_state) { bool l_active_pcap_changed = FALSE; uint16_t l_node_pwr = AMECSENSOR_PTR(PWRSYS)->sample; uint16_t l_p0_pwr = AMECSENSOR_PTR(PWRPROC)->sample; int32_t l_avail_power = 0; uint16_t mem_pwr_diff = 0; uint32_t l_proc_fraction = 0; static uint32_t L_prev_node_pcap = 0; static bool L_apss_error_traced = FALSE; static uint32_t L_ticks_mem_pwr_available = 0; static bool L_trace_pcap_throttle = true; static bool L_trace_pcap_unthrottle = true; // Determine the active power cap. // when in oversub (N mode) only use oversub pcap if lower than user set pcap // OCC should allow N mode to be higher than N+1 (don't compare against norm_node_pcap) // N mode may be higher on some systems due to ps issue reporting higher power in N mode if( (TRUE == i_oversub_state) && (g_amec->pcap.ovs_node_pcap < G_sysConfigData.pcap.current_pcap) ) { g_amec->pcap.active_node_pcap = g_amec->pcap.ovs_node_pcap; } // norm_node_pcap is set as lowest between sys (N+1 mode) and // user in amec_data_write_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_active_pcap_changed = TRUE; } l_avail_power = g_amec->pcap.active_node_pcap - l_node_pwr; // Determine GPU power cap if there are GPUs present if(G_first_proc_gpu_config) { amec_gpu_pcap(i_oversub_state, l_active_pcap_changed, l_avail_power); } 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: PWRSYS sensor is no longer 0."); L_apss_error_traced = FALSE; } // check if allowed to increase power AND memory throttled due to pcap if((l_avail_power > 0) && (g_amec->pcap.active_mem_level != 0)) { // un-throttle memory if there is enough available power between // current and new throttles if (CURRENT_MODE() == OCC_MODE_NOMINAL) { mem_pwr_diff = g_amec->pcap.nominal_mem_pwr; } else { mem_pwr_diff = g_amec->pcap.turbo_mem_pwr; } // currently there's only 1 mem pcap throt level so must be pcap1 mem_pwr_diff -= g_amec->pcap.pcap1_mem_pwr; if(l_avail_power >= mem_pwr_diff) { L_ticks_mem_pwr_available++; if( L_ticks_mem_pwr_available == UNTHROTTLE_MEMORY_DELAY ) { if( L_trace_pcap_unthrottle || (G_allow_trace_flags & ALLOW_MEM_TRACE) ) { TRAC_IMP("PCAP: Un-Throttling memory"); L_trace_pcap_unthrottle = false; } g_amec->pcap.active_mem_level = 0; L_ticks_mem_pwr_available = 0; // don't let the proc have any available power this tick l_avail_power = 0; } } } // check if need to reduce power and frequency is already at the min else if(l_avail_power < 0) { L_ticks_mem_pwr_available = 0; // if memory is not throttled and frequency is at min shed additional power // by throttling memory if( (g_amec->pcap.active_mem_level == 0) && (g_amec->proc[0].pwr_votes.ppb_fmax == g_amec->sys.fmin) ) { if( L_trace_pcap_throttle || (G_allow_trace_flags & ALLOW_MEM_TRACE) ) { TRAC_IMP("PCAP: Throttling memory"); L_trace_pcap_throttle = false; } g_amec->pcap.active_mem_level = 1; } } else { // no changes to memory throttles due to power } } else { if(!L_apss_error_traced) { TRAC_ERR("PCAP: PWRSYS sensor is showing a value of 0."); L_apss_error_traced = TRUE; } } // skip processor changes until memory is un-capped if(!g_amec->pcap.active_mem_level) { g_amec->pcap.active_proc_pcap = l_p0_pwr + ((l_proc_fraction * l_avail_power) >> 16); //NOTE: Power capping will not affect nominal cores unless a customer pcap // 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]; } }
void task_core_data( task_t * i_task ) { errlHndl_t l_err = NULL; //Error handler tracDesc_t l_trace = NULL; //Temporary trace descriptor int rc = 0; //return code bulk_core_data_task_t * l_bulk_core_data_ptr = (bulk_core_data_task_t *)i_task->data_ptr; GpeGetCoreDataParms * l_parms = (GpeGetCoreDataParms *)(l_bulk_core_data_ptr->gpe_req.parameter); gpe_bulk_core_data_t * l_temp = NULL; do { //First, check to see if the previous GPE request still running //A request is considered idle if it is not attached to any of the //asynchronous request queues if( !(async_request_is_idle(&l_bulk_core_data_ptr->gpe_req.request)) ) { //This should not happen unless there's a problem //Trace 1 time if( !G_queue_not_idle_traced ) { TRAC_ERR("Core data GPE is still running \n"); G_queue_not_idle_traced = TRUE; } break; } //Need to complete collecting data for all assigned cores from previous interval //and tick 0 is the current tick before collect data again. if( (l_bulk_core_data_ptr->current_core == l_bulk_core_data_ptr->end_core) && ((CURRENT_TICK & (MAX_NUM_TICKS - 1)) != 0) ) { PROC_DBG("Not collect data. Need to wait for tick.\n"); break; } //Check to see if the previously GPE request has successfully completed //A request is not considered complete until both the engine job //has finished without error and any callback has run to completion. if( async_request_completed(&l_bulk_core_data_ptr->gpe_req.request) && CORE_PRESENT(l_bulk_core_data_ptr->current_core) ) { //If the previous GPE request succeeded then swap core_data_ptr //with the global one. The gpe routine will write new data into //a buffer that is not being accessed by the RTLoop code. PROC_DBG( "Swap core_data_ptr [%x] with the global one\n", l_bulk_core_data_ptr->current_core ); //debug only #ifdef PROC_DEBUG print_core_status(l_bulk_core_data_ptr->current_core); print_core_data_sensors(l_bulk_core_data_ptr->current_core); #endif l_temp = l_bulk_core_data_ptr->core_data_ptr; l_bulk_core_data_ptr->core_data_ptr = G_core_data_ptrs[l_bulk_core_data_ptr->current_core]; G_core_data_ptrs[l_bulk_core_data_ptr->current_core] = l_temp; //Core data has been collected so set the bit in global mask. //AMEC code will know which cores to update sensors for. AMEC is //responsible for clearing the bit later on. G_updated_core_mask |= CORE0_PRESENT_MASK >> (l_bulk_core_data_ptr->current_core); // Presumptively clear the empath error mask G_empath_error_core_mask &= ~(CORE0_PRESENT_MASK >> (l_bulk_core_data_ptr->current_core)); // The gpe_data collection code has to handle the workaround for // HW280375. Two new flags have been added to the OHA_RO_STATUS_REG // image to indicate whether the EMPATH collection failed, and // whether it was due to an "expected" error that we can ignore // (we can ignore the data as well), or an "unexpected" error that // we will create an informational log one time. // // The "expected" errors are very rare in practice, in fact we may // never even see them unless running a specific type of workload. // If you want to test the handling of expected errors compile the // GPE code with -DINJECT_HW280375_ERRORS which will inject an error // approximately every 1024 samples // // To determine if the expected error has occurred inspect the // CoreDataOha element of the CoreData structure written by the GPE // core data job. The OHA element contains the oha_ro_status_reg. // Inside the OHA status register is a 16 bit reserved field. // gpe_data.h defines two masks that can be applied against the // reserved field to check for these errors: // CORE_DATA_EXPECTED_EMPATH_ERROR // CORE_DATA_UNEXPECTED_EMPATH_ERROR // Also, a 4-bit PCB parity + error code is saved at bit position: // CORE_DATA_EMPATH_ERROR_LOCATION, formally the length is // specified by: CORE_DATA_EMPATH_ERROR_BITS gpe_bulk_core_data_t *l_core_data = G_core_data_ptrs[l_bulk_core_data_ptr->current_core]; // We will trace the errors, but only a certain number of // times, we will only log the unexpected error once. #define OCC_EMPATH_ERROR_THRESH 10 static uint32_t L_expected_emp_err_cnt = 0; static uint32_t L_unexpected_emp_err_cnt = 0; // Check the reserved field for the expected or the unexpected error flag if ((l_core_data->oha.oha_ro_status_reg.fields._reserved0 & CORE_DATA_EXPECTED_EMPATH_ERROR) || (l_core_data->oha.oha_ro_status_reg.fields._reserved0 & CORE_DATA_UNEXPECTED_EMPATH_ERROR)) { // Indicate empath error on current core G_empath_error_core_mask |= CORE0_PRESENT_MASK >> (l_bulk_core_data_ptr->current_core); // Save the high and low order words of the OHA status reg uint32_t l_oha_reg_high = l_core_data->oha.oha_ro_status_reg.words.high_order; uint32_t l_oha_reg_low = l_core_data->oha.oha_ro_status_reg.words.low_order; // Handle each error case if ((l_core_data->oha.oha_ro_status_reg.fields._reserved0 & CORE_DATA_EXPECTED_EMPATH_ERROR) && (L_expected_emp_err_cnt < OCC_EMPATH_ERROR_THRESH)) { L_expected_emp_err_cnt++; TRAC_IMP("Expected empath collection error occurred %d time(s)! Core = %d", L_expected_emp_err_cnt, l_bulk_core_data_ptr->current_core); TRAC_IMP("OHA status register: 0x%4.4x%4.4x", l_oha_reg_high, l_oha_reg_low); } if ((l_core_data->oha.oha_ro_status_reg.fields._reserved0 & CORE_DATA_UNEXPECTED_EMPATH_ERROR) && (L_unexpected_emp_err_cnt < OCC_EMPATH_ERROR_THRESH)) { L_unexpected_emp_err_cnt++; TRAC_ERR("Unexpected empath collection error occurred %d time(s)! Core = %d", L_unexpected_emp_err_cnt, l_bulk_core_data_ptr->current_core); TRAC_ERR("OHA status register: 0x%4.4x%4.4x", l_oha_reg_high, l_oha_reg_low); // Create and commit an informational error the first // time this occurs. if (L_unexpected_emp_err_cnt == 1) { TRAC_IMP("Logging unexpected empath collection error 1 time only."); /* * @errortype * @moduleid PROC_TASK_CORE_DATA_MOD * @reasoncode INTERNAL_HW_FAILURE * @userdata1 OHA status reg high * @userdata2 OHA status reg low * @userdata4 ERC_PROC_CORE_DATA_EMPATH_ERROR * @devdesc An unexpected error occurred while * collecting core empath data. */ l_err = createErrl( PROC_TASK_CORE_DATA_MOD, //modId INTERNAL_HW_FAILURE, //reason code ERC_PROC_CORE_DATA_EMPATH_ERROR, //Extended reason code ERRL_SEV_INFORMATIONAL, //Severity NULL, //Trace DEFAULT_TRACE_SIZE, //Trace Size l_oha_reg_high, //userdata1 l_oha_reg_low); //userdata2 commitErrl(&l_err); } } } }
// Function Specification // // Name: amec_slv_proc_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_proc_voting_box(void) { /*------------------------------------------------------------------------*/ /* Local Variables */ /*------------------------------------------------------------------------*/ uint16_t k = 0; uint16_t l_chip_fmax = g_amec->sys.fmax; uint16_t l_core_freq = 0; uint16_t l_core_freq_max = 0; // max freq across all cores uint16_t l_core_freq_min = g_amec->sys.fmax; // min freq across all cores uint32_t l_current_reason = 0; // used for debug purposes static uint32_t L_last_reason = 0; // used for debug purposes uint32_t l_chip_reason = 0; uint32_t l_core_reason = 0; amec_proc_voting_reason_t l_kvm_throt_reason = NO_THROTTLE; amec_part_t *l_part = NULL; // frequency threshold for reporting throttling uint16_t l_report_throttle_freq = G_sysConfigData.system_type.report_dvfs_nom ? G_sysConfigData.sys_mode_freq.table[OCC_MODE_NOMINAL] : G_sysConfigData.sys_mode_freq.table[OCC_MODE_TURBO]; /*------------------------------------------------------------------------*/ /* Code */ /*------------------------------------------------------------------------*/ if (!G_allowPstates) { // Don't allow pstates to be sent until after initial mode has been set if ( (CURRENT_MODE()) || (G_sysConfigData.system_type.kvm) ) { G_allowPstates = TRUE; } } // Voting Box for CPU speed. // This function implements the voting box to decide which input gets the right // to actuate the system. // check for oversubscription if redundant ps policy (oversubscription) is being enforced if (G_sysConfigData.system_type.non_redund_ps == false) { // If in oversubscription and there is a defined (non 0) OVERSUB frequency less than max then use it if( (AMEC_INTF_GET_OVERSUBSCRIPTION()) && (G_sysConfigData.sys_mode_freq.table[OCC_MODE_OVERSUB]) && (G_sysConfigData.sys_mode_freq.table[OCC_MODE_OVERSUB] < l_chip_fmax) ) { l_chip_fmax = G_sysConfigData.sys_mode_freq.table[OCC_MODE_OVERSUB]; l_chip_reason = AMEC_VOTING_REASON_OVERSUB; } } // If there is an active VRM fault and a defined (non 0) VRM N frequency less than max use it if( (g_amec->sys.vrm_fault_status) && (G_sysConfigData.sys_mode_freq.table[OCC_MODE_VRM_N]) && (G_sysConfigData.sys_mode_freq.table[OCC_MODE_VRM_N] < l_chip_fmax) ) { l_chip_fmax = G_sysConfigData.sys_mode_freq.table[OCC_MODE_VRM_N]; l_chip_reason = AMEC_VOTING_REASON_VRM_N; } // 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; if(l_report_throttle_freq <= l_chip_fmax) { l_kvm_throt_reason = PCAP_EXCEED_REPORT; } else { 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; if( l_report_throttle_freq <= l_chip_fmax) { l_kvm_throt_reason = PROC_OVERTEMP_EXCEED_REPORT; } else { l_kvm_throt_reason = CPU_OVERTEMP; } } //Thermal controller input based on VRM Vdd temperature if(g_amec->thermalvdd.freq_request < l_chip_fmax) { l_chip_fmax = g_amec->thermalvdd.freq_request; l_chip_reason = AMEC_VOTING_REASON_VDD_THRM; if( l_report_throttle_freq <= l_chip_fmax) { l_kvm_throt_reason = VDD_OVERTEMP_EXCEED_REPORT; } else { l_kvm_throt_reason = VDD_OVERTEMP; } } for (k=0; k<MAX_NUM_CORES; k++) { if( CORE_PRESENT(k) && !CORE_OFFLINE(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_VDD_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; if(l_report_throttle_freq <= l_core_freq) { l_kvm_throt_reason = PCAP_EXCEED_REPORT; } else { 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; } l_kvm_throt_reason = MANUFACTURING_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; } //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; if(l_core_freq < l_core_freq_min) { // store the new lowest frequency and reason to be used after all cores checked l_core_freq_min = l_core_freq; l_current_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_NOM_PERFORMANCE) && (CURRENT_MODE() != OCC_MODE_MAX_PERFORMANCE) && (CURRENT_MODE() != OCC_MODE_FMF) && (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(FREQREQC0,k), (uint16_t) g_amec->proc[0].core[k].f_request); #if DEBUG_PROC_VOTING_BOX /// This trace that can be used to debug the voting /// box and 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 > l_core_freq_max) { l_core_freq_max = l_core_freq; } } // if core present and not offline else { //Set f_request to 0 so this core is ignored in amec_slv_freq_smh() g_amec->proc[0].core[k].f_request = 0; g_amec->proc[0].core[k].f_reason = 0; } }//End of for loop // update max core frequency if not 0 i.e. all cores offline (stop 2 or greater) // this is used by power capping alg, updating to 0 will cause power throttling when not needed if(l_core_freq_max) { g_amec->proc[0].core_max_freq = l_core_freq_max; // update the overall reason driving frequency across all cores g_amec->proc[0].f_reason = l_current_reason; } //check if there was a throttle reason change if(l_kvm_throt_reason != G_amec_opal_proc_throt_reason) { //Always update G_amec_opal_proc_throt_reason, this is used to set poll rsp bits for all system types G_amec_opal_proc_throt_reason = l_kvm_throt_reason; // Only if running OPAL need to notify dcom thread to update the table in HOMER for OPAL if(G_sysConfigData.system_type.kvm) { ssx_semaphore_post(&G_dcomThreadWakeupSem); } } // For debug... if lower than max update vars returned in poll response to give clipping reason g_amec->proc[0].core_min_freq = l_core_freq_min; if(l_core_freq_min < g_amec->sys.fmax) { if(l_current_reason == L_last_reason) { // same reason INC counter if(g_amec->proc[0].current_clip_count != 0xFF) { g_amec->proc[0].current_clip_count++; } } else { // new reason update history and set counter to 1 L_last_reason = l_current_reason; g_amec->proc[0].current_clip_count = 1; if( (g_amec->proc[0].chip_f_reason_history & l_current_reason) == 0) { g_amec->proc[0].chip_f_reason_history |= l_current_reason; TRAC_IMP("First time throttling for reason[0x%08X] History[0x%08X] freq = %d", l_current_reason, g_amec->proc[0].chip_f_reason_history, l_core_freq_min); } } } else // no active clipping { L_last_reason = 0; g_amec->proc[0].current_clip_count = 0; } }