/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* $Source: src/occ_405/proc/proc_pstate.c $ */ /* */ /* OpenPOWER OnChipController Project */ /* */ /* Contributors Listed Below - COPYRIGHT 2011,2017 */ /* [+] International Business Machines Corp. */ /* */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ /* You may obtain a copy of the License at */ /* */ /* http://www.apache.org/licenses/LICENSE-2.0 */ /* */ /* Unless required by applicable law or agreed to in writing, software */ /* distributed under the License is distributed on an "AS IS" BASIS, */ /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ /* implied. See the License for the specific language governing */ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ #include "ssx.h" #include "proc_data_service_codes.h" #include "errl.h" #include "trac.h" #include "rtls.h" #include "dcom.h" #include "occ_common.h" #include "state.h" #include "cmdh_fsp_cmds.h" #include "proc_data.h" #include "proc_pstate.h" #include "scom.h" #include "homer.h" #include #include #include #include #include #include //OPAL processor and memory throttle reason coming from the frequency voting boxes. extern opal_proc_voting_reason_t G_amec_opal_proc_throt_reason; extern opal_mem_voting_reason_t G_amec_opal_mem_throt_reason; //Global OCC Pstate Parameters Block Structure extern OCCPstateParmBlock G_oppb; //Holds Fmax for ease of proc_freq2pstate calculation = max(fturbo,futurbo) uint16_t G_proc_fmax_mhz; uint8_t G_desired_pstate[MAXIMUM_QUADS]; // A global variable indicating whether the pstates have been enabled. // initialized to PSTATES_DISABLED, turns to PSTATES_ENABLED only after // the PGPE IPC that enable pstates completes successfully. While the IPC // task is still running, this variable be set to PSTATES_IN_TRANSITION pstateStatus G_proc_pstate_status = PSTATES_DISABLED; // A Global parameter indicating the owner of the PMCR. PMCR_OWNER G_proc_pmcr_owner = PMCR_OWNER_HOST; // OPAL Dynamic data, updated whenever any OCC G_opal_table.dynamic parameter change // Since this is happening multiple times need to keep track of it being scheduled DMA_BUFFER( opal_dynamic_table_t G_opal_dynamic_table ) = {{0}}; bool G_opal_dynamic_bce_req_scheduled = false; BceRequest G_opal_dynamic_bce_req; #define OPAL_DYNAMIC_UPDATE_BCE_RETRIES 2 // OPAL Static data, updated once at transition to active state DMA_BUFFER( opal_static_table_t G_opal_static_table ) = {{0}}; BceRequest G_opal_static_bce_req; volatile uint8_t G_opal_table_update_state = OPAL_TABLE_UPDATE_IDLE; // Function Specification // // Name: proc_is_hwpstate_enabled // // Description: Checks OCC to see if Pstate HW Mode is enabled. // // End Function Specification bool proc_is_hwpstate_enabled(void) { return ( G_proc_pstate_status == PSTATES_ENABLED ? TRUE : FALSE); } // Function Specification // // Name: proc_pstate2freq // // Description: Convert Pstate to Frequency in kHz // // End Function Specification uint32_t proc_pstate2freq(Pstate i_pstate) { // The higher the pstate number, the lower the frequency: // If passed in Pstate is lower than Pmin (higher pstate value), // just use Pmin. if(i_pstate > G_oppb.pstate_min) { i_pstate = G_oppb.pstate_min; } // Calculate Frequency in kHz based on Pstate return ( G_oppb.frequency_max_khz - (i_pstate * G_oppb.frequency_step_khz)); } // Function Specification // // Name: proc_freq2pstate // // Description: Convert Frequency to Nearest Pstate // // End Function Specification Pstate proc_freq2pstate(uint32_t i_freq_mhz) { int8_t l_pstate = 0; int8_t l_temp_pstate = 0; int32_t l_temp_freq = 0; uint32_t l_freq_khz = 0; do { // Freq Units need to be in kHz, not Mhz for the following calculations l_freq_khz = i_freq_mhz * 1000; // Make sure that we don't ever get a frequency below the min Freq from // def file if(i_freq_mhz < G_sysConfigData.sys_mode_freq.table[OCC_MODE_MIN_FREQUENCY]) { l_freq_khz = G_sysConfigData.sys_mode_freq.table[OCC_MODE_MIN_FREQUENCY] * 1000; } if(l_freq_khz < G_proc_fmax_mhz * 1000) { // First, calculate the delta between passed in freq, and Pmin l_temp_freq = l_freq_khz - G_oppb.frequency_min_khz; // Next, calculate how many Pstate steps there are in that delta l_temp_pstate = l_temp_freq / (int32_t) G_oppb.frequency_step_khz; // Lastly, calculate Pstate, by adding delta Pstate steps to Pmin l_pstate = G_oppb.pstate_min - l_temp_pstate; } else { // Freq is higher than maximum frequency -- return Pmax l_pstate = PMAX + (G_oppb.frequency_max_khz - G_proc_fmax_mhz*1000)/G_oppb.frequency_step_khz; } } while(0); return (Pstate) l_pstate; } // Function Specification // // Name: proc_pstate_kvm_setup // // Description: Copy Pstate table to OPAL shared memory this should only be called once // when going to active state // // End Function Specification void proc_pstate_kvm_setup() { TRAC_IMP("proc_pstate_kvm_setup: populate static OPAL data"); // Initialize the opal table in SRAM (sets valid bit) populate_opal_static_data(); // copy sram image into mainstore HOMER populate_opal_tbl_to_mem(OPAL_STATIC); } // Function Specification // // Name: opal_table_bce_callback // // Description: Callback function for populate_opal_tbl_to_mem() BCE request // NO TRACING OR CALLING FUNCTIONS THAT TRACE ALLOWED // // End Function Specification void opal_table_bce_callback( void ) { // If the BCE that just finished was for a dynamic table update notify host if(G_opal_table_update_state == OPAL_TABLE_UPDATE_DYNAMIC_COPY) { G_opal_table_update_state = OPAL_TABLE_UPDATE_NOTIFY_HOST; } } // Function Specification // // Name: populate_opal_dynamic_data // // Description: populate the dynamic data entries in the OPAL table // // End Function Specification void populate_opal_dynamic_data() { memset(&G_opal_dynamic_table, 0, sizeof(G_opal_dynamic_table)); // Dynamic OPAL runtime data G_opal_dynamic_table.dynamic.occ_state = CURRENT_STATE(); //If safe state is requested then that overrides anything from amec if(isSafeStateRequested()) { G_opal_dynamic_table.dynamic.proc_throt_status = OCC_RESET; } else { G_opal_dynamic_table.dynamic.proc_throt_status = G_amec_opal_proc_throt_reason; } G_opal_dynamic_table.dynamic.mem_throt_status = G_amec_opal_mem_throt_reason; G_opal_dynamic_table.dynamic.quick_power_drop = AMEC_INTF_GET_OVERSUBSCRIPTION(); G_opal_dynamic_table.dynamic.power_shift_ratio = G_sysConfigData.psr; G_opal_dynamic_table.dynamic.power_cap_type = G_sysConfigData.pcap.source; G_opal_dynamic_table.dynamic.min_power_cap = G_sysConfigData.pcap.hard_min_pcap; G_opal_dynamic_table.dynamic.max_power_cap = G_sysConfigData.pcap.max_pcap; G_opal_dynamic_table.dynamic.current_power_cap = g_amec->pcap.active_node_pcap; G_opal_dynamic_table.dynamic.soft_min_power_cap = G_sysConfigData.pcap.soft_min_pcap; } // Function Specification // // Name: populate_opal_static_data // // Description: populate the static configuration entries, // the generated pstates table, and maximum pstates // for all possible number of active cores. // // End Function Specification void populate_opal_static_data() { // clear all entries of the OPAL static table memset(&G_opal_static_table, 0, sizeof(G_opal_static_table)); populate_opal_static_config_data(); populate_opal_static_pstates_data(); } // Function Specification // // Name: populate_opal_static_config_data // // Description: populate the static configuration entries, // // End Function Specification void populate_opal_static_config_data(void) { // Static OPAL configuration data G_opal_static_table.config.valid = 1; G_opal_static_table.config.version = 0x90; G_opal_static_table.config.occ_role = G_occ_role; G_opal_static_table.config.pmin = proc_freq2pstate(g_amec->sys.fmin); G_opal_static_table.config.pnominal = proc_freq2pstate(G_sysConfigData.sys_mode_freq.table[OCC_MODE_NOMINAL]); G_opal_static_table.config.pturbo = proc_freq2pstate(G_sysConfigData.sys_mode_freq.table[OCC_MODE_TURBO]); G_opal_static_table.config.puturbo = proc_freq2pstate(G_proc_fmax_mhz); } // Function Specification // // Name: populate_opal_static_data // // Description: populate the generated pstates table, and maximum // pstates for all possible number of active cores. // // End Function Specification void populate_opal_static_pstates_data(void) { uint16_t i; // loop variable for (i=0; i <= G_oppb.pstate_min; i++) { G_opal_static_table.pstates[i].pstate = i; // pstate number G_opal_static_table.pstates[i].flag = 0; // flag is reserved for future use G_opal_static_table.pstates[i].freq_khz = proc_pstate2freq(i); // pstate's frequency } for (i=0; i0x%02X", G_opal_dynamic_table.dynamic.proc_throt_status, G_amec_opal_proc_throt_reason); } } else if(G_opal_dynamic_table.dynamic.proc_throt_status != G_amec_opal_proc_throt_reason) { dynamic_data_change = true; TRAC_INFO("check_for_opal_updates: processor throttle status change - 0x%02X->0x%02X", G_opal_dynamic_table.dynamic.proc_throt_status, G_amec_opal_proc_throt_reason); } // check if memory throttle status or Quick Power Drop changed if( (G_opal_dynamic_table.dynamic.mem_throt_status != G_amec_opal_mem_throt_reason) || (G_opal_dynamic_table.dynamic.quick_power_drop != AMEC_INTF_GET_OVERSUBSCRIPTION()) ) { dynamic_data_change = true; TRAC_INFO("check_for_opal_updates: memory throttle status - 0x%02X->0x%02X QPD - 0x%02X->0x%02X", G_opal_dynamic_table.dynamic.mem_throt_status, G_amec_opal_mem_throt_reason, G_opal_dynamic_table.dynamic.quick_power_drop, AMEC_INTF_GET_OVERSUBSCRIPTION()); } // check for OCC state change if(G_opal_dynamic_table.dynamic.occ_state != CURRENT_STATE()) { dynamic_data_change = true; TRAC_INFO("check_for_opal_updates: OCC state change 0x%02X->0x%02X", G_opal_dynamic_table.dynamic.occ_state, CURRENT_STATE()); } // check for change in power cap data must look at slave copy // do NOT use G_master_pcap_data as that is not populated on slaves if( (G_opal_dynamic_table.dynamic.min_power_cap != G_sysConfigData.pcap.hard_min_pcap) || (G_opal_dynamic_table.dynamic.max_power_cap != G_sysConfigData.pcap.max_pcap) || (G_opal_dynamic_table.dynamic.soft_min_power_cap != G_sysConfigData.pcap.soft_min_pcap) || (G_opal_dynamic_table.dynamic.power_shift_ratio != G_sysConfigData.psr) || (G_opal_dynamic_table.dynamic.power_cap_type != G_sysConfigData.pcap.source) || (G_opal_dynamic_table.dynamic.current_power_cap != g_amec->pcap.active_node_pcap) ) { dynamic_data_change = true; TRAC_INFO("check_for_opal_updates: soft min Pcap = 0x%04X->0x%04X hard min Pcap = 0x%04X->0x%04X", G_opal_dynamic_table.dynamic.soft_min_power_cap, G_sysConfigData.pcap.soft_min_pcap, G_opal_dynamic_table.dynamic.min_power_cap, G_sysConfigData.pcap.hard_min_pcap); TRAC_INFO("check_for_opal_updates: max Pcap = 0x%04X->0x%04X active Pcap = 0x%04X->0x%04X", G_opal_dynamic_table.dynamic.max_power_cap, G_sysConfigData.pcap.max_pcap, G_opal_dynamic_table.dynamic.current_power_cap, g_amec->pcap.active_node_pcap); TRAC_INFO("check_for_opal_updates: Pcap PSR = %u->%u Pcap source = %u->%u", G_opal_dynamic_table.dynamic.power_shift_ratio, G_sysConfigData.psr, G_opal_dynamic_table.dynamic.power_cap_type, G_sysConfigData.pcap.source); } } // else check for changes // If there was a change copy to main memory and notify host when BCE finishes if(dynamic_data_change) { G_opal_table_update_state = OPAL_TABLE_UPDATE_DYNAMIC_COPY; update_dynamic_opal_data(); // if the BCE schedule fails the state will go back to IDLE, retry next time called if(G_opal_table_update_state == OPAL_TABLE_UPDATE_IDLE) { G_opal_table_update_state = OPAL_TABLE_UPDATE_BCE_FAIL; L_num_bce_checks++; } else { L_num_bce_checks = 0; } } else if(l_log_crit_error) { // stop trying to update dynamic data, this only really matters on OPAL systems so // only log the error if OPAL G_opal_table_update_state = OPAL_TABLE_UPDATE_CRITICAL_ERROR; if(G_sysConfigData.system_type.kvm) { // Create and commit error /* @ * @errortype * @moduleid PROC_CHECK_FOR_OPAL_UPDATES_MOD * @reasoncode OPAL_TABLE_UPDATE_ERROR * @userdata1 0 * @userdata2 0 * @userdata4 ERC_GENERIC_TIMEOUT * @devdesc BCE request failure to update dynamic opal table */ errlHndl_t l_errl = createErrl(PROC_CHECK_FOR_OPAL_UPDATES_MOD, // Module ID OPAL_TABLE_UPDATE_ERROR, // Reason code ERC_GENERIC_TIMEOUT, // Extended reason code ERRL_SEV_UNRECOVERABLE, // Severity NULL, // Trace Buffers DEFAULT_TRACE_SIZE, // Trace Size 0, // Userdata1 0); // Userdata2 // Callout firmware addCalloutToErrl(l_errl, ERRL_CALLOUT_TYPE_COMPONENT_ID, ERRL_COMPONENT_ID_FIRMWARE, ERRL_CALLOUT_PRIORITY_HIGH); // Callout processor addCalloutToErrl(l_errl, ERRL_CALLOUT_TYPE_HUID, G_sysConfigData.proc_huid, ERRL_CALLOUT_PRIORITY_MED); commitErrl(&l_errl); } } } // Function Specification // // Name: update_dynamic_opal_data // // Description: update dynamic opal data in SRAM and // copy it over to OPAL space in main memory. // // End Function Specification void update_dynamic_opal_data (void) { // Initialize the dynamic opal table in SRAM populate_opal_dynamic_data(); // copy sram image into mainstore HOMER populate_opal_tbl_to_mem(OPAL_DYNAMIC); }