//************************************************************************* // Functions //************************************************************************* int traceSemTest(void) { UINT l_rc = 0; // lock trace semaphore first l_rc = ssx_semaphore_pend(&g_trac_mutex, TRAC_INTF_MUTEX_TIMEOUT); if(SSX_OK == l_rc) { // create one trace TRAC_INFO("traceTest Applet: test trace semaphore"); // Unlock trace semaphore ssx_semaphore_post(&g_trac_mutex); } else { l_rc = 1; } return l_rc; }
// 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: SMGR_set_mode // // Description: // // End Function Specification errlHndl_t SMGR_set_mode(const OCC_MODE i_mode, const uint8_t i_sms_type) { errlHndl_t l_errlHndl = NULL; int jj=0; OCC_MODE l_mode = i_mode; do { // Get lock for critical section if(ssx_semaphore_pend(&G_smgrModeChangeSem,SSX_WAIT_FOREVER)) { /* @ * @errortype * @moduleid MAIN_MODE_TRANSITION_MID * @reasoncode SSX_GENERIC_FAILURE * @userdata1 none * @userdata4 ERC_RUNNING_SEM_PENDING_FAILURE * @devdesc SSX semaphore related failure */ l_errlHndl = createErrl(MAIN_MODE_TRANSITION_MID, //modId SSX_GENERIC_FAILURE, //reasoncode ERC_RUNNING_SEM_PENDING_FAILURE,//Extended reason code ERRL_SEV_UNRECOVERABLE, //Severity NULL, //Trace Buf DEFAULT_TRACE_SIZE, //Trace Size 0, //userdata1 0); //userdata2 // Callout firmware addCalloutToErrl(l_errlHndl, ERRL_CALLOUT_TYPE_COMPONENT_ID, ERRL_COMPONENT_ID_FIRMWARE, ERRL_CALLOUT_PRIORITY_HIGH); break; } //Check to see if we need to make a change if(l_mode == OCC_MODE_NOCHANGE) { break; } // SAPPHIRE only accepts DPS-FE mode. In case OCC gets other modes, it should accept the request // and keep reporting back that it is in that mode. However, internally we should not // initiate any mode transition, i.e., OCC should remain internally in DPS-FE mode. if(G_sysConfigData.system_type.kvm) { G_occ_external_req_mode_kvm = l_mode; if (l_mode != OCC_MODE_DYN_POWER_SAVE) { TRAC_ERR("SAPPHIRE only accepts DPS-FE mode(6) but requested mode is : %d", l_mode); l_mode = OCC_MODE_DYN_POWER_SAVE; } } switch (l_mode) { case OCC_MODE_NOMINAL: // FALL THROUGH case OCC_MODE_PWRSAVE: // FALL THROUGH case OCC_MODE_DYN_POWER_SAVE: // FALL THROUGH case OCC_MODE_DYN_POWER_SAVE_FP: // FALL THROUGH case OCC_MODE_TURBO: // FALL THROUGH case OCC_MODE_STURBO: // FALL THROUGH case OCC_MODE_FFO: // FALL THROUGH // Notify AMEC of mode change // Change Mode via Transition Function do { // Loop through mode transition table, and find the state // transition function that matches the transition we need to do. for(jj=0; jj<G_smgr_mode_trans_count; jj++) { if( ((G_smgr_mode_trans[jj].old_state == G_occ_internal_mode) || (G_smgr_mode_trans[jj].old_state == OCC_MODE_ALL) ) && (G_smgr_mode_trans[jj].new_state == l_mode) ) { // We found the transtion that matches, now run the function // that is associated with that state transition. if(NULL != G_smgr_mode_trans[jj].trans_func_ptr) { // Signal that we are now in a mode transition G_mode_transition_occuring = TRUE; // Run transition function l_errlHndl = (G_smgr_mode_trans[jj].trans_func_ptr)(); // Signal that we are done with the transition G_mode_transition_occuring = FALSE; break; } } } // Check if we hit the end of the table without finding a valid // mode transition. If we did, log an internal error. if(G_smgr_mode_trans_count == jj) { TRAC_ERR("No transition (or NULL) found for the mode change\n"); l_errlHndl = NULL; //TODO: Create Error break; } // Update the power mode for all core groups that are following system mode AMEC_part_update_sysmode_policy(CURRENT_MODE()); } while(0); break; default: //unsupported mode break; } if(l_errlHndl) { // Punt !!! :-) break; } // Load correct thermal thresholds based on the current mode l_errlHndl = AMEC_data_write_thrm_thresholds(CURRENT_MODE()); // Update the CPU speed in AME? // Register the New Mode? // Update Power Policy Requirements? // Update CPM Calibration }while(0); // If we have a mode change failure, Mode change flag needs to be set, // otherwise, it needs be be cleared/unset. if(l_errlHndl) { } // Unlock critical section ssx_semaphore_post(&G_smgrModeChangeSem); return l_errlHndl; }
// 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: 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: task_dcom_parse_occfwmsg // // Description: Purpose of this task is to handle and acknowledge // fw messages passed from Master to Slave and vice versa. // // End Function Specification void task_dcom_parse_occfwmsg(task_t *i_self) { if(G_occ_role == OCC_MASTER) { // Local slave index counter uint32_t l_slv_idx = 0; // Loop and collect occ data for each slave occ for(; l_slv_idx < MAX_OCCS; l_slv_idx++) { // Verify all slave are in correct state and mode G_dcom_slv_outbox_rx[l_slv_idx].occ_fw_mailbox[0] = CURRENT_STATE(); if(G_sysConfigData.system_type.kvm ) { G_dcom_slv_outbox_rx[l_slv_idx].occ_fw_mailbox[1] = G_occ_external_req_mode_kvm; } else { G_dcom_slv_outbox_rx[l_slv_idx].occ_fw_mailbox[1] = CURRENT_MODE(); } // Acknowledge all slave event flags G_slave_event_flags_ack[l_slv_idx] = G_dcom_slv_outbox_rx[l_slv_idx].occ_fw_mailbox[3]; // Clear master event flags if slave has acknowledged them and the event has cleared G_master_event_flags &= ~G_dcom_slv_outbox_rx[l_slv_idx].occ_fw_mailbox[2]; } }//End master role check // Check if master has changed state and mode and update if changed // so that we can handle it in a thread. if( (G_occ_master_state != G_dcom_slv_inbox_rx.occ_fw_mailbox[0]) || (G_occ_master_mode != G_dcom_slv_inbox_rx.occ_fw_mailbox[1]) ) { if( ! isSafeStateRequested() ) { G_occ_master_state = G_dcom_slv_inbox_rx.occ_fw_mailbox[0]; G_occ_master_mode = G_dcom_slv_inbox_rx.occ_fw_mailbox[1]; ssx_semaphore_post(&G_dcomThreadWakeupSem); } } // If we are master, we don't want to update based on // the data sent to us, because it corrupts the 'golden' data // If we are in standby, we don't want to update because // the data may not have been set up yet, and would be set to zero. if(OCC_MASTER != G_occ_role ) { // Update the system mode frequencies if they have changed int l_mode = 0; bool l_change = FALSE; bool l_all_zero = TRUE; // Check if all values are zero for(l_mode = 0; l_mode<OCC_MODE_COUNT; l_mode++) { if( (0 != G_dcom_slv_inbox_rx.sys_mode_freq.table[l_mode]) ) { l_all_zero = FALSE; break; } } extern data_cnfg_t * G_data_cnfg; if( l_all_zero == FALSE) { for(l_mode =0; l_mode<OCC_MODE_COUNT; l_mode++) { // Don't trust a frequency of 0x0000 if( (0 != G_dcom_slv_inbox_rx.sys_mode_freq.table[l_mode]) ) { if(G_sysConfigData.sys_mode_freq.table[l_mode] != G_dcom_slv_inbox_rx.sys_mode_freq.table[l_mode]) { TRAC_INFO("New Frequency for Mode %d: Old: %d MHz -> New: %d MHz",l_mode, G_sysConfigData.sys_mode_freq.table[l_mode], G_dcom_slv_inbox_rx.sys_mode_freq.table[l_mode]); // Update mode frequency G_sysConfigData.sys_mode_freq.table[l_mode] = G_dcom_slv_inbox_rx.sys_mode_freq.table[l_mode]; l_change = TRUE; } } } if(l_change) { // Update "update count" for debug purposes G_sysConfigData.sys_mode_freq.update_count = G_dcom_slv_inbox_rx.sys_mode_freq.update_count; // Change Data Request Mask to indicate we got this data extern data_cnfg_t * G_data_cnfg; G_data_cnfg->data_mask |= DATA_MASK_FREQ_PRESENT; // Notify AMEC that the frequencies have changed AMEC_data_change(DATA_MASK_FREQ_PRESENT); } } else { // Clear Data Request Mask and data G_data_cnfg->data_mask &= (~DATA_MASK_FREQ_PRESENT); memset(&G_sysConfigData.sys_mode_freq.table[0], 0, sizeof(G_sysConfigData.sys_mode_freq.table)); } } // Copy mnfg parameters into g_amec structure g_amec->foverride_enable = G_dcom_slv_inbox_rx.foverride_enable; g_amec->foverride = G_dcom_slv_inbox_rx.foverride; // Copy IPS parameters sent by Master OCC g_amec->slv_ips_freq_request = G_dcom_slv_inbox_rx.ips_freq_request; // Copy DPS tunable parameters sent by Master OCC if required if(G_dcom_slv_inbox_rx.tunable_param_overwrite) { AMEC_part_overwrite_dps_parameters(); if(G_dcom_slv_inbox_rx.tunable_param_overwrite == 1) { g_amec->slv_dps_param_overwrite = TRUE; } else { g_amec->slv_dps_param_overwrite = FALSE; } } // Copy soft frequency boundaries sent by Master OCC g_amec->part_config.part_list[0].soft_fmin = G_dcom_slv_inbox_rx.soft_fmin; g_amec->part_config.part_list[0].soft_fmax = G_dcom_slv_inbox_rx.soft_fmax; // acknowledge all masters event flags G_master_event_flags_ack = G_dcom_slv_inbox_rx.occ_fw_mailbox[2]; // clear slave event flags if master has acknowledged them and the event has cleared G_slave_event_flags = (G_slave_event_flags & (~(G_dcom_slv_inbox_rx.occ_fw_mailbox[3]))); }
// Function Specification // // Name: amec_slv_mem_voting_box // // 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; amec_mem_voting_reason_t l_reason; opal_mem_voting_reason_t kvm_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; kvm_reason = NO_THROTTLE; // Memory throttled due to power cap. if also throttled due to // over temp, report over-temp as the reason to OPAL. if (g_amec->pcap.active_mem_level != 0) { kvm_reason = POWER_CAP; } // 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; kvm_reason = MEMORY_OVER_TEMP; } // 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; kvm_reason = MEMORY_OVER_TEMP; } // 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%08x%08x]", l_reason, l_vote, G_cent_temp_expired_bitmap, G_dimm_temp_expired_bitmap.words[0], G_dimm_temp_expired_bitmap.words[1]); } } else { if(L_throttle_traced) { L_throttle_traced = FALSE; TRAC_INFO("Memory is no longer being throttled"); } } //check if we need to update the throttle reason in OPAL table if(G_sysConfigData.system_type.kvm && (kvm_reason != G_amec_opal_mem_throt_reason)) { //Notify dcom thread to update the table G_amec_opal_mem_throt_reason = kvm_reason; ssx_semaphore_post(&G_dcomThreadWakeupSem); } return; }
// 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; } }