// Function Specification // // Name: amec_slv_freq_smh // // Description: Slave OCC's frequency state machine. // This function will run every tick. // // Thread: RealTime Loop // // Task Flags: // // End Function Specification void amec_slv_freq_smh(void) { /*------------------------------------------------------------------------*/ /* Local Variables */ /*------------------------------------------------------------------------*/ uint16_t k = 0; int8_t l_pstate = 0; /*------------------------------------------------------------------------*/ /* Code */ /*------------------------------------------------------------------------*/ for (k=0; k<MAX_NUM_CORES; k++) { switch (g_amec->proc[0].core[k].f_sms) { case AMEC_CORE_FREQ_IDLE_STATE: // Translate frequency request into a Pstate l_pstate = proc_freq2pstate(g_amec->proc[0].core[k].f_request); // Fall through case AMEC_CORE_FREQ_PROCESS_STATE: if(G_sysConfigData.system_type.kvm) { // update core bounds on kvm systems proc_set_core_bounds(gpst_pmin(&G_global_pstate_table) + 1, (Pstate) l_pstate, k); } else { // update core pstate request on non-kvm systems proc_set_core_pstate((Pstate) l_pstate, k); } break; } } }
// Function Specification // // Name: cmdh_mnfg_request_quad_pstate // // Description: This function handles the manufacturing command to request // a Pstate per Quad. // // End Function Specification uint8_t cmdh_mnfg_request_quad_pstate(const cmdh_fsp_cmd_t * i_cmd_ptr, cmdh_fsp_rsp_t * o_rsp_ptr) { uint8_t l_rc = ERRL_RC_SUCCESS; uint16_t l_datalength = 0; uint16_t l_resp_data_length = 0; uint8_t l_pmin = 0xFF; uint8_t l_pmax = 0xFF; uint8_t l_pstate_request = 0xFF; uint8_t l_quad = 0; mnfg_quad_pstate_cmd_t *l_cmd_ptr = (mnfg_quad_pstate_cmd_t*) i_cmd_ptr; mnfg_quad_pstate_rsp_t *l_rsp_ptr = (mnfg_quad_pstate_rsp_t*) o_rsp_ptr; do { if(!IS_OCC_STATE_ACTIVE()) { TRAC_ERR("cmdh_mnfg_request_quad_pstate: OCC must be active to request pstate"); l_rc = ERRL_RC_INVALID_STATE; break; } if(G_sysConfigData.system_type.kvm) { TRAC_ERR("cmdh_mnfg_request_quad_pstate: Must be PowerVM to request pstate"); l_rc = ERRL_RC_INVALID_CMD; break; } // Check command packet data length l_datalength = CMDH_DATALEN_FIELD_UINT16(i_cmd_ptr); if(l_datalength != (sizeof(mnfg_quad_pstate_cmd_t) - sizeof(cmdh_fsp_cmd_header_t))) { TRAC_ERR("cmdh_mnfg_request_quad_pstate: incorrect data length. exp[%d] act[%d]", (sizeof(mnfg_quad_pstate_cmd_t) - sizeof(cmdh_fsp_cmd_header_t)), l_datalength); l_rc = ERRL_RC_INVALID_CMD_LEN; break; } // Check version if(l_cmd_ptr->version != MFG_QUAD_PSTATE_VERSION) { TRAC_ERR("cmdh_mnfg_request_quad_pstate: incorrect version. exp[%d] act[%d]", MFG_QUAD_PSTATE_VERSION, l_cmd_ptr->version); l_rc = ERRL_RC_INVALID_DATA; break; } // only allow a Pstate within the current range based on mode l_pmin = proc_freq2pstate(g_amec->sys.fmin); l_pmax = proc_freq2pstate(g_amec->sys.fmax); // Process each quad Pstate request, clip any request to min/max // 0xFF has special meaning that OCC is in control for(l_quad = 0; l_quad < MAXIMUM_QUADS; l_quad++) { l_pstate_request = l_cmd_ptr->quad_pstate_in[l_quad]; if(l_pstate_request != 0xFF) { // pmin is lowest frequency corresponding to highest pState value if(l_pstate_request > l_pmin) l_pstate_request = l_pmin; // pmax is highest frequency corresponding to lowest pState value else if(l_pstate_request < l_pmax) l_pstate_request = l_pmax; } // save the quad pState request for amec and return in rsp data g_amec->mnfg_parms.quad_pstate[l_quad] = l_pstate_request; l_rsp_ptr->quad_pstate_out[l_quad] = l_pstate_request; TRAC_INFO("cmdh_mnfg_request_quad_pstate: Quad %d Pstate in = 0x%02x Pstate out = 0x%02x", l_quad, l_cmd_ptr->quad_pstate_in[l_quad], l_rsp_ptr->quad_pstate_out[l_quad]); } }while(0); // Populate the response data header G_rsp_status = l_rc; l_resp_data_length = sizeof(mnfg_quad_pstate_rsp_t) - sizeof(cmdh_fsp_rsp_header_t); l_rsp_ptr->data_length[0] = ((uint8_t *)&l_resp_data_length)[0]; l_rsp_ptr->data_length[1] = ((uint8_t *)&l_resp_data_length)[1]; return l_rc; }
// Verifies that each core is at the correct frequency after they have had // time to stabilize void amec_verify_pstate() { uint8_t l_core = 0; int8_t l_pstate_from_fmax = 0; gpe_bulk_core_data_t * l_core_data_ptr; pmc_pmsr_ffcdc_data_t l_pmc_pmsr_ffdc; errlHndl_t l_err = NULL; if ( (G_time_until_freq_check == 0) && ( CURRENT_MODE() != OCC_MODE_DYN_POWER_SAVE ) && ( CURRENT_MODE() != OCC_MODE_DYN_POWER_SAVE_FP ) && (!G_sysConfigData.system_type.kvm)) { // Reset the counter G_time_until_freq_check = FREQ_CHG_CHECK_TIME; // Convert fmax to the corresponding pstate l_pstate_from_fmax = proc_freq2pstate(g_amec->sys.fmax); for( l_core = 0; l_core < MAX_NUM_CORES; l_core++ ) { // If the core isn't present, skip it if(!CORE_PRESENT(l_core)) { l_pmc_pmsr_ffdc.pmsr_ffdc_data.data[l_core].value = 0; continue; } // Get pointer to core data l_core_data_ptr = proc_get_bulk_core_data_ptr(l_core); // Get the core's pmsr data l_pmc_pmsr_ffdc.pmsr_ffdc_data.data[l_core] = l_core_data_ptr->pcb_slave.pmsr; // Verify that the core is running at the correct frequency // If not, log an error if( (l_pstate_from_fmax != l_pmc_pmsr_ffdc.pmsr_ffdc_data.data[l_core].fields.local_pstate_actual) && (l_pstate_from_fmax > l_pmc_pmsr_ffdc.pmsr_ffdc_data.data[l_core].fields.pv_min) && (l_err == NULL) ) { TRAC_ERR("Frequency mismatch in core %d: actual_ps[%d] req_ps[%d] fmax[%d] mode[%d].", l_core, l_pmc_pmsr_ffdc.pmsr_ffdc_data.data[l_core].fields.local_pstate_actual, l_pstate_from_fmax, g_amec->sys.fmax, CURRENT_MODE()); fill_pmc_ffdc_buffer(&l_pmc_pmsr_ffdc.pmc_ffcdc_data); /* @ * @moduleid AMEC_VERIFY_FREQ_MID * @reasonCode TARGET_FREQ_FAILURE * @severity ERRL_SEV_PREDICTIVE * @userdata1 0 * @userdata2 0 * @userdata4 OCC_NO_EXTENDED_RC * @devdesc A core is not running at the expected frequency */ l_err = createErrl( AMEC_VERIFY_FREQ_MID, // i_modId, TARGET_FREQ_FAILURE, // i_reasonCode, OCC_NO_EXTENDED_RC, ERRL_SEV_UNRECOVERABLE, NULL, // i_trace, DEFAULT_TRACE_SIZE, // i_traceSz, 0, // i_userData1, 0); // i_userData2 //Add firmware callout addCalloutToErrl(l_err, ERRL_CALLOUT_TYPE_COMPONENT_ID, ERRL_COMPONENT_ID_FIRMWARE, ERRL_CALLOUT_PRIORITY_HIGH); //Add processor callout addCalloutToErrl(l_err, ERRL_CALLOUT_TYPE_HUID, G_sysConfigData.proc_huid, ERRL_CALLOUT_PRIORITY_MED); } } if( l_err != NULL) { //Add our register dump to the error log addUsrDtlsToErrl(l_err, (uint8_t*) &l_pmc_pmsr_ffdc, sizeof(l_pmc_pmsr_ffdc), ERRL_USR_DTL_STRUCT_VERSION_1, ERRL_USR_DTL_BINARY_DATA); REQUEST_RESET(l_err); } } }
// Function Specification // // Name: populate_pstate_to_sapphire_tbl // // Description: // // End Function Specification void populate_pstate_to_sapphire_tbl() { uint8_t i = 0; GlobalPstateTable *l_gpst_ptr = NULL; uint16_t l_turboFreq = 0; uint16_t l_ultraturboFreq = 0; if(G_sysConfigData.sys_mode_freq.table[OCC_MODE_STURBO] == 0) { // In this case, there is no UltraTurbo frequency defined. For OPAL-OCC // interface, make UltraTurbo = Turbo frequency l_turboFreq = G_sysConfigData.sys_mode_freq.table[OCC_MODE_TURBO]; l_ultraturboFreq = G_sysConfigData.sys_mode_freq.table[OCC_MODE_TURBO]; } else { l_turboFreq = G_sysConfigData.sys_mode_freq.table[OCC_MODE_STURBO]; l_ultraturboFreq = G_sysConfigData.sys_mode_freq.table[OCC_MODE_TURBO]; } memset(&G_sapphire_table, 0, sizeof(sapphire_table_t)); l_gpst_ptr = gpsm_gpst(); const int8_t l_pmax = (int8_t) l_gpst_ptr->pmin + l_gpst_ptr->entries - 1; G_sapphire_table.config.valid = 0x01; // default 0x01 G_sapphire_table.config.version = 0x02; // default 0x02 G_sapphire_table.config.throttle = NO_THROTTLE; // default 0x00 G_sapphire_table.config.pmin = gpst_pmin(&G_global_pstate_table)+1; //Per David Du, we must use pmin+1 to avoid gpsa hang G_sapphire_table.config.pnominal = (int8_t)proc_freq2pstate(G_sysConfigData.sys_mode_freq.table[OCC_MODE_NOMINAL]); G_sapphire_table.config.turbo = (int8_t) proc_freq2pstate(l_turboFreq); G_sapphire_table.config.ultraTurbo = (int8_t) proc_freq2pstate(l_ultraturboFreq); int8_t l_tempPmax = gpst_pmax(&G_global_pstate_table); const uint16_t l_entries = l_tempPmax - G_sapphire_table.config.pmin + 1; const uint8_t l_idx = l_gpst_ptr->entries-1; for (i = 0; i < l_entries; i++) { G_sapphire_table.data[i].pstate = (int8_t) l_pmax - i; G_sapphire_table.data[i].flag = 0; // default 0x00 if (i < l_gpst_ptr->entries) { G_sapphire_table.data[i].evid_vdd = l_gpst_ptr->pstate[i].fields.evid_vdd; G_sapphire_table.data[i].evid_vcs = l_gpst_ptr->pstate[i].fields.evid_vcs; } else { // leave the VDD & VCS Vids the same as the "Pstate Table Pmin" G_sapphire_table.data[i].evid_vdd = l_gpst_ptr->pstate[l_idx].fields.evid_vdd; G_sapphire_table.data[i].evid_vcs = l_gpst_ptr->pstate[l_idx].fields.evid_vcs; } // extrapolate the frequency G_sapphire_table.data[i].freq_khz = l_gpst_ptr->pstate0_frequency_khz + (G_sapphire_table.data[i].pstate * l_gpst_ptr->frequency_step_khz); } uint8_t l_core = 0; uint16_t l_maxFreq = l_turboFreq; //Write the max pstate for each possible number of active cores. for (l_core = 1; l_core <= MAX_CORES; l_core++) { //If wof is enabled, then use the wof uplift table to find the max freq for given # of active cores. if (g_amec->wof.enable_parm != 0) { l_maxFreq = amec_wof_get_max_freq(l_core); } //If the l_core (# of Active cores) is greater than G_wof_max_cores_per_chip //then return 0x01 for Pstate. if (l_core > G_wof_max_cores_per_chip) { G_sapphire_table.activeCore_max_pstate[l_core - 1] = 0x01; } else { G_sapphire_table.activeCore_max_pstate[l_core - 1] = (int8_t)proc_freq2pstate((uint32_t)l_maxFreq); } } //For Version 0x02 of the interface, we need to make the first four reserved bytes //equal to 0x01 to prevent confusion for(i=0; i<4; i++) { G_sapphire_table.pad[i] = 0x01; } }
// 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 }
// Function Specification // // Name: amec_set_freq_range // // Description: Set the frequency range for AMEC based on mode only // NOTE: Any other clipping of frequency should be done in // amec_slv_proc_voting_box() (called every tick) // so the CLIP history in poll response will accurately // show all clipping for the given mode the system is in // This function will run on mode changes and cnfg_data changes // // Thread: RealTime Loop // // Task Flags: // // End Function Specification errlHndl_t amec_set_freq_range(const OCC_MODE i_mode) { /*------------------------------------------------------------------------*/ /* Local Variables */ /*------------------------------------------------------------------------*/ errlHndl_t l_err = NULL; uint16_t l_freq_min = 0; uint16_t l_freq_max = 0; uint32_t l_temp = 0; amec_mode_freq_t l_ppm_freq[OCC_INTERNAL_MODE_MAX_NUM] = {{0}}; /*------------------------------------------------------------------------*/ /* Code */ /*------------------------------------------------------------------------*/ // First set to Max Freq Range for this mode // if no mode set yet default to the full range if(i_mode == OCC_MODE_NOCHANGE) { l_freq_min = G_sysConfigData.sys_mode_freq.table[OCC_MODE_MIN_FREQUENCY]; // Set Max frequency (turbo if wof off, otherwise max possible (ultra turbo) if( g_amec->wof.wof_disabled || (g_amec->wof.wof_init_state != WOF_ENABLED)) { l_freq_max = G_sysConfigData.sys_mode_freq.table[OCC_MODE_TURBO]; } else { l_freq_max = G_proc_fmax_mhz; } } else if( VALID_MODE(i_mode) ) // Set to Max Freq Range for this mode { l_freq_min = G_sysConfigData.sys_mode_freq.table[OCC_MODE_MIN_FREQUENCY]; // Use max frequency for performance modes and FMF if( (i_mode == OCC_MODE_NOM_PERFORMANCE) || (i_mode == OCC_MODE_MAX_PERFORMANCE) || (i_mode == OCC_MODE_FMF) || (i_mode == OCC_MODE_DYN_POWER_SAVE) || (i_mode == OCC_MODE_DYN_POWER_SAVE_FP) ) { // clip to turbo if WOF is disabled if( g_amec->wof.wof_disabled || (g_amec->wof.wof_init_state != WOF_ENABLED)) { l_freq_max = G_sysConfigData.sys_mode_freq.table[OCC_MODE_TURBO]; } else { l_freq_max = G_proc_fmax_mhz; } } else { l_freq_max = G_sysConfigData.sys_mode_freq.table[i_mode]; } } if( (l_freq_min == 0) || (l_freq_max == 0) ) { // Do not update amec vars with a 0 frequency. // The frequency limit for each mode should have been set prior // to calling or the mode passed was invalid TRAC_ERR("amec_set_freq_range: Freq of 0 found! mode[0x%02x] Fmin[%u] Fmax[%u]", i_mode, l_freq_min, l_freq_max); // Log an error if this is PowerVM as this should never happen when OCC // supports modes if(!G_sysConfigData.system_type.kvm) { /* @ * @errortype * @moduleid AMEC_SET_FREQ_RANGE * @reasoncode INTERNAL_FW_FAILURE * @userdata1 Mode * @userdata2 0 * @userdata4 ERC_FW_ZERO_FREQ_LIMIT * @devdesc Fmin or Fmax of 0 found for mode */ errlHndl_t l_err = createErrl(AMEC_SET_FREQ_RANGE, //modId INTERNAL_FW_FAILURE, //reasoncode ERC_FW_ZERO_FREQ_LIMIT, //Extended reason code ERRL_SEV_PREDICTIVE, //Severity NULL, //Trace Buf DEFAULT_TRACE_SIZE, //Trace Size i_mode, //userdata1 0); //userdata2 // Callout Firmware addCalloutToErrl(l_err, ERRL_CALLOUT_TYPE_COMPONENT_ID, ERRL_COMPONENT_ID_FIRMWARE, ERRL_CALLOUT_PRIORITY_LOW ); } } else { g_amec->sys.fmin = l_freq_min; g_amec->sys.fmax = l_freq_max; TRAC_INFO("amec_set_freq_range: Mode[0x%02x] Fmin[%u] (Pmin 0x%02x) Fmax[%u] (Pmax 0x%02x)", i_mode, l_freq_min, proc_freq2pstate(g_amec->sys.fmin), l_freq_max, proc_freq2pstate(g_amec->sys.fmax)); // Now determine the max frequency for the PPM structure l_ppm_freq[OCC_INTERNAL_MODE_NOM].fmax = G_sysConfigData.sys_mode_freq.table[OCC_MODE_NOMINAL]; l_ppm_freq[OCC_INTERNAL_MODE_DPS].fmax = G_sysConfigData.sys_mode_freq.table[OCC_MODE_DYN_POWER_SAVE]; l_ppm_freq[OCC_INTERNAL_MODE_DPS_MP].fmax = G_sysConfigData.sys_mode_freq.table[OCC_MODE_DYN_POWER_SAVE_FP]; // Determine the min frequency for the PPM structure. This Fmin should // always be set to the system Fmin l_ppm_freq[OCC_INTERNAL_MODE_NOM].fmin = G_sysConfigData.sys_mode_freq.table[OCC_MODE_MIN_FREQUENCY]; l_ppm_freq[OCC_INTERNAL_MODE_DPS].fmin = G_sysConfigData.sys_mode_freq.table[OCC_MODE_MIN_FREQUENCY]; l_ppm_freq[OCC_INTERNAL_MODE_DPS_MP].fmin = G_sysConfigData.sys_mode_freq.table[OCC_MODE_MIN_FREQUENCY]; // Determine the min speed allowed for DPS power policies (this is needed // by the DPS algorithms) l_temp = (l_ppm_freq[OCC_INTERNAL_MODE_DPS].fmin * 1000)/l_ppm_freq[OCC_INTERNAL_MODE_DPS].fmax; l_ppm_freq[OCC_INTERNAL_MODE_DPS].min_speed = l_temp; l_temp = (l_ppm_freq[OCC_INTERNAL_MODE_DPS_MP].fmin * 1000)/l_ppm_freq[OCC_INTERNAL_MODE_DPS_MP].fmax; l_ppm_freq[OCC_INTERNAL_MODE_DPS_MP].min_speed = l_temp; // Copy the PPM frequency information into g_amec memcpy(g_amec->part_mode_freq, l_ppm_freq, sizeof(l_ppm_freq)); } return l_err; }