// $Id: gpsm_init.c,v 1.10 2015/05/16 17:43:12 daviddu Exp $ // $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/gpsm_init.c,v $ //----------------------------------------------------------------------------- // *! (C) Copyright International Business Machines Corp. 2013 // *! All Rights Reserved -- Property of IBM // *! *** IBM Confidential *** //----------------------------------------------------------------------------- /// \file gpsm_init.c /// \brief Global Pstate Machine procedures only required to initialize Pstate /// and Pstate modes. /// /// In OCC product firmware this code is only needed immediately after IPL to /// set up Pstate tables and enable Pstates. This code could be run from an /// applet or otherwise removed once Pstates are initialized. Note that this /// file cointains code only; some global variables referenced here are /// defined in gpsm.c, along with the run-time APIs for GPSM. /// /// The following sequence of procedures is required to initialize the GPSM /// mechanism, enable Pstate mode, and further enable Hardware Pstate Mode. /// /// \code /// /// PstateSuperStructure* pss; /// GlobalPstateTable *gpst; /// GpsmEnablePstatesMasterInfo info; /// Pstate voltage_pstate, frequency_pstate; /// /// gpsm_initialize(pss, gpst); /// gpsm_enable_pstates_master(&info, &voltage_pstate, &frequency_pstate); /// gpsm_enable_pstates_slave(&info, voltage_pstate, frequency_pstate); /// gpsm_hw_mode(); /// /// \endcode /// /// Executing these procedures enables Pstate control of voltage and /// frequency, and leaves the chip quiesced in Firmware Pstate mode. These /// procedures initializes both central (PMC) and remote (PCB Slave) Pstate /// hardware to fully enable Pstate control of voltage and frequency. /// Carefully note the procedure sequence preconditions and postconditions. /// /// In the case of a DCM environment, the DCM master must execute the sequence /// as follows: /// /// \code /// /// PstateSuperStructure* pss; /// GlobalPstateTable *gpst; /// GpsmEnablePstatesMasterInfo info; /// Pstate voltage_pstate, frequency_pstate; /// /// gpsm_initialize(pss, gpst); /// gpsm_enable_pstates_master(&info, &voltage_pstate, &frequency_pstate); /// /// Send voltage_pstate and frequency_pstate to the slave and wait for /// confirmation that the procedure has completed. /// /// gpsm_enable_pstates_slave(&info, voltage_pstate, frequency_pstate); /// gpsm_hw_mode(); /// /// Send command to the slave to execute gpsm_hw_mode() /// /// \endcode /// /// The DCM slave executes the following sequence /// \code /// PstateSuperStructure* pss; /// GlobalPstateTable *gpst; /// Pstate voltage_pstate, frequency_pstate; /// /// gpsm_initialize(pss, gpst); /// /// Receive voltage_pstate and frequency_pstate from the masterand wait for /// confirmation that the procedure has completed. /// /// gpsm_enable_pstates_slave(0, voltage_pstate, frequency_pstate); /// /// Wait for a command from the master to execute gpsm_hw_mode(). /// /// \endcode /// /// Preconditions /// /// - This sequence must be called from a thread as it must be able to block /// for the completion of GPSM events. /// /// - The fundamental precondition is the assumption that any snapshot of the /// voltage/frequency state of the system yields a legal state. As long as /// this is true, then this sequence should never take the system to an /// illegal V/F state. /// /// - CPM-DPLL mode should be disabled, and all undervolting controls are /// assumed to be at their nominal values. Correctness can not guaranteed /// otherwise. /// /// - iVRM mode should be disabled prior to calling this procedure. /// Correctness can not be guaranteed otherwise. /// /// /// Standard/Benign Postconditions after executing /// gpsm_enable_pstates_slave() /// /// - The system will be in Firmware Pstate Mode, using the Local and Global /// Pstate tables installed by gpsm_initialize(). Pstate 0 will be mapped to /// the nominal frequency (modulo rounding down) of the /// 'nominal_frequency_khz' field of the Global Pstate table. /// /// - All core chiplet frequencies will be under the control of the PMCR Local /// Pstate. /// /// - The Global Actual Pstate immediately prior to the final entry of Pstate /// mode is the Pstate that most closely matches an arbitrary snapshot of the /// system voltage during the execution of this procedure. /// /// - The PMCR and PMICR are not modified, therefore the Global Pstate /// subsequent to the release of Hardware Pstate mode is arbitrary. /// /// - The PMC_RAIL_BOUNDS register is set to the maximum legal bounds allowed /// by Global Pstate Table /// /// - IVRM is setup and enabled if Local Pstate Table is installed. /// /// - Resonant Clock is setup and enabled if Resonant Clock is installed. /// /// Side-Effects /// /// - The dpll_fmax and dpll_fmin fields of the FREQ_CTRL_REG in each core are /// set to an arbitrary value. The dpll_fmax_bias is set to the value of the /// 'dpll_fmax_bias' field of the \a gpst. /// /// - The core-level PCBS_POWER_MANAGEMENT_BOUNDS_REG registers are /// set to the maximum legal bounds. This is required due to Pstate table /// constraints. /// /// \todo - How to handle redundant SPIVID interfaces? Here we get the /// current volatge from interface 0. /// /// \bug Check to make sure that the PMC_CORE_DECONFIGURATION_REG matches /// multicast group 1. /// /// \bug Code marked with ** VBU ** is necessary for VBU/EPO simulation, needs /// to be scrubbed once this is working in VBU. #include "ssx.h" #include "ssx_io.h" #include "gpsm.h" #include "vrm.h" #include "heartbeat.h" #include "special_wakeup.h" // Debugging support #if 0 #define _BREAK \ { \ fprintf(stderr, "%s:%d: _BREAK trapped error rc = 0x%08x (-0x%08x)\n", \ __FILE__, __LINE__, rc, -rc); \ SSX_PANIC(GPSM_ERROR_BREAK); \ } #else #define _BREAK break; #endif /// Flag set once gpsm_initialize() has been successfully completed. uint8_t G_gpsm_initialized = 0; /// Install a (new) Global Pstate Table /// /// \param[out] o_gpst A pointer to a properly-aligned GlobalPstateTable /// /// \param[in] i_source A pointer to an initialized source GlobalPstateTable /// that will be copied to \a gpst (if not in fact the same as \a gpst). /// /// This procedure will likely only be called once, at initialization, and /// then only as part of the gpsm_initialize() procedure. The procedure: /// /// - Copies the \a source to the \a gpst if required. /// /// - Installs a pointer to the Global Pstate Table in the PMC hardware. /// /// - Sets up the PMC Pstate clipping bounds. /// /// - Sets up the Pstate stepping parameters from the GlobalPstateTable /// /// - Clears the local undervolting register (via multicast) and sets the /// default undervolting bounds to the entire Local Pstate Table. /// /// - Broadcasts the safe mode Pstate Psafe to the cores and clears the other /// fields of the PCBS_OCC_HEARTBEAT_REG, disabling the heartbeat timer. /// However the heartbeat timer must not have been active anyway. /// /// \note This procedure does modify the rail bounds. /// /// \note The caller is responsible for the mode-correctness of this /// procedure. This procedure must only be called when the PMC is in Firmware /// Pstate Mode. /// /// \retval 0 Success /// /// \retval -GPSM_INVALID_ARGUMENT_GPST_INSTALL The Global Pstate table argument /// was either NULL (0) or improperly aligned, or the \a source was NULL (0). /// /// \retval -GPSM_ILLEGAL_MODE_GPST_INSTALL The PMC does not indicate that the // system is in Firmware Pstate Mode, or the heartbeat is enabled. int gpsm_gpst_install(GlobalPstateTable* o_gpst, const GlobalPstateTable* i_source) { pmc_occ_heartbeat_reg_t pohr; pmc_parameter_reg0_t ppr0; pmc_parameter_reg1_t ppr1; pmc_global_pstate_bounds_reg_t pgpbr; pmc_rail_bounds_register_t prbr; pmc_undervolting_reg_t pur; pcbs_occ_heartbeat_reg_t pcbsohr; int rc; TRACE_GPSM(TRACE_GPSM_GPST_INSTALL); do { // Optional bypass of the procedure if (i_source->options.options & PSTATE_NO_INSTALL_GPST) { rc = 0; break; } // Check presence and alignment of the Pstate table, and proper Pstate // and heartbeat modes. if ((o_gpst == 0) || ((unsigned long)o_gpst % POW2_32(GLOBAL_PSTATE_TABLE_ALIGNMENT))) { rc = -GPSM_INVALID_ARGUMENT_GPST_INSTALL; _BREAK; } pohr.value = in32(PMC_OCC_HEARTBEAT_REG); rc = getscom(MC_ADDRESS(PCBS_OCC_HEARTBEAT_REG, MC_GROUP_EX, PCB_MULTICAST_OR), &(pcbsohr.value)); if (rc) _BREAK; if (!gpsm_fw_mode_p() || pohr.fields.pmc_occ_heartbeat_en || pcbsohr.fields.occ_heartbeat_enable) { rc = -GPSM_ILLEGAL_MODE_GPST_INSTALL; _BREAK; } // Copy \a source to \a gpst if required, then install the Pstate // table, Pvsafe and Pstate stepping parameters, and set the clipping // bounds as well as the rail bounds if ((o_gpst != i_source) && !(i_source->options.options & PSTATE_NO_COPY_GPST)) { memcpy(o_gpst, i_source, sizeof(*o_gpst)); } ppr1.value = in32(PMC_PARAMETER_REG1); ppr1.fields.ba_sram_pstate_table = (unsigned long)o_gpst >> GLOBAL_PSTATE_TABLE_ALIGNMENT; ppr1.fields.pvsafe = i_source->pvsafe; // This fix is added per SW260911 // Minimum Frequency in the system is given by MRW attribute // PState Datablock procedure will read the attribute then // convert it into pstate _pfloor_ and put it into // Global Pstate Table. GPSM here consumes the value // and set both lower bounds: pmin_rail(PMC) and pmin_clip(PCBS) // and two safe pstates: pvsafe(PMc) and psafe(PCBS) to be // _pfloor_ if _pfloor_ is higher than their default(gpst_pmin) // so that we should never run with frequency below the floor // even in safe mode if (ppr1.fields.pvsafe < i_source->pfloor && i_source->pfloor != 0) ppr1.fields.pvsafe = i_source->pfloor; out32(PMC_PARAMETER_REG1, ppr1.value); pgpbr.value = 0; pgpbr.fields.gpsi_min = gpst_pmin(i_source) - PSTATE_MIN; pgpbr.fields.gpst_number_of_entries_minus_one = i_source->entries - 1; out32(PMC_GLOBAL_PSTATE_BOUNDS_REG, pgpbr.value); ppr0.value = in32(PMC_PARAMETER_REG0); ppr0.fields.pstate_stepsize = i_source->pstate_stepsize; ppr0.fields.vrm_stepdelay_range = i_source->vrm_stepdelay_range; ppr0.fields.vrm_stepdelay_value = i_source->vrm_stepdelay_value; ppr0.fields.gpsa_timeout_value_sel = 1; out32(PMC_PARAMETER_REG0, ppr0.value); prbr.value = 0; prbr.fields.pmin_rail = gpst_pmin(i_source)+1; prbr.fields.pmax_rail = gpst_pmax(i_source); // This fix is added per SW260911 // Minimum Frequency in the system is given by MRW attribute // PState Datablock procedure will read the attribute then // convert it into pstate _pfloor_ and put it into // Global Pstate Table. GPSM here consumes the value // and set both lower bounds: pmin_rail(PMC) and pmin_clip(PCBS) // and two safe pstates: pvsafe(PMc) and psafe(PCBS) to be // _pfloor_ if _pfloor_ is higher than their default(gpst_pmin) // so that we should never run with frequency below the floor // even in safe mode if (prbr.fields.pmin_rail < i_source->pfloor && i_source->pfloor != 0) prbr.fields.pmin_rail = i_source->pfloor; out32(PMC_RAIL_BOUNDS_REGISTER, prbr.value); // Clear the undervolting control, and set the undervolting range to // the entire Global Pstate Table range. pur.value = 0; pur.fields.puv_min = gpst_pmin(i_source); pur.fields.puv_max = gpst_pmax(i_source); pur.fields.kuv_request = 0; out32(PMC_UNDERVOLTING_REG, pur.value); // Broadcast the safe mode Pstate Psafe to the cores, disabling the // heartbeat (which must have been disabled anyway) and clearing any // other heartbeat setup. pcbsohr.value = 0; pcbsohr.fields.psafe = i_source->psafe; // This fix is added per SW260911 // Minimum Frequency in the system is given by MRW attribute // PState Datablock procedure will read the attribute then // convert it into pstate _pfloor_ and put it into // Global Pstate Table. GPSM here consumes the value // and set both lower bounds: pmin_rail(PMC) and pmin_clip(PCBS) // and two safe pstates: pvsafe(PMc) and psafe(PCBS) to be // _pfloor_ if _pfloor_ is higher than their default(gpst_pmin) // so that we should never run with frequency below the floor // even in safe mode if (pcbsohr.fields.psafe < i_source->pfloor && i_source->pfloor != 0) pcbsohr.fields.psafe = i_source->pfloor; rc = putscom(MC_ADDRESS(PCBS_OCC_HEARTBEAT_REG, MC_GROUP_EX, PCB_MULTICAST_WRITE), pcbsohr.value); if (rc) _BREAK; rc = 0; } while(0); return rc; } /// Install a (new) Local Pstate Array /// /// \param[in] i_lpsa A pointer to a LocalPstateArray to install in every /// configured core. /// /// \param[in] i_options Options controlling the installation, or a NULL (0) /// pointer to indicate fully default behavior. /// /// This procedure will likely only be called once, at initialization, and /// then only as part of the gpsm_initialize() procedure. The procedure: /// /// - Power on PFET Voltage Reference Circuit /// /// - Perform the binary search for IVRM Calibration /// /// - Uploads the LocalPstateArray to every core using multicast. /// /// - Sets up the Local Pstate table bounds in every core using multicast. /// /// - Sets the step delay parameters for every core using multicast. /// /// - Clears the local undervolting register (via multicast) and sets the /// default undervolting bounds to the entire Local Pstate Table. /// /// - Setup IVRM delay parameters /// /// - Enable IVRM /// /// \note This procedure \e does \e not modify the rail bounds. /// /// \note The caller is responsible for the mode-correctness of this /// procedure. This procedure must only be called when the iVRM are /// disabled, the DPLL is in "normal" (not CPM-DPLL) mode, and core heartbeats /// are disabled. /// /// \retval 0 Success /// /// \retval -GPSM_INVALID_ARGUMENT_LPST_INSTALL The Local Pstate array argument /// was NULL (0). /// /// \retval -GPSM_ILLEGAL_MODE_LPST_INSTALL iVRM mode, CPM-DPLL mode, or the /// local heartbeat appears to be enabled in at least one core. /// /// \retval -GPSM_IVRM_CALIBRATION_TIMEOUT, if IVRM Calibration does not /// complete in time. /// /// \retval -GPSM_IVRM_GROSS_OR_FINE, if ivrm_gross_or_fine_err is set /// /// \retval -GPSM_PSTATE_ENABLED, if pstate is enabled before enabling IVRM /// /// \retval others This API may also return non-0 codes from /// getscom()/putscom() int gpsm_lpsa_install(const LocalPstateArray* i_lpsa, const PstateOptions* i_options) { pcbs_ivrm_control_status_reg_t picsr; pcbs_dpll_cpm_parm_reg_t pdcpr; pcbs_pstate_index_bound_reg_t ppibr; pcbs_ivrm_vid_control_reg0_t pivcr0; pcbs_ivrm_vid_control_reg1_t pivcr1; pcbs_undervolting_reg_t pur; pcbs_pmerr_reg_t ppr; pcbs_pcbspm_mode_reg_t ppmr; pmc_core_deconfiguration_reg_t pcdr; ChipConfigCores cores, wakedup; SsxTimebase timeout; int i, rc, timeout_rc = 0; uint32_t configured_cores; int flag, core; TRACE_GPSM(TRACE_GPSM_LPSA_INSTALL); do { // Optional bypass of this procedure if ((i_options != 0) && (i_options->options & PSTATE_NO_INSTALL_LPSA)) { rc = 0; break; } // No LPST Install and IVRM Enable if there is no configued cores configured_cores = ~in32(PMC_CORE_DECONFIGURATION_REG); flag = 1; for (core = 0; core < PGP_NCORES; core++, configured_cores <<= 1) { if (!(configured_cores & 0x80000000)) continue; flag = 0; } if (flag == 1) { rc = 0; break; } // Check the array for existence. Do an OR-combining multicast read // to see if any of the cores have iVRM enabled, have the heartbeat // enabled, or any cores are running in CPM-DPLL mode. if (i_lpsa == 0) { rc = -GPSM_INVALID_ARGUMENT_LPST_INSTALL; _BREAK; } rc = getscom(MC_ADDRESS(PCBS_IVRM_CONTROL_STATUS_REG, MC_GROUP_EX, PCB_MULTICAST_OR), &(picsr.value)); if (rc) _BREAK; rc = getscom(MC_ADDRESS(PCBS_DPLL_CPM_PARM_REG, MC_GROUP_EX, PCB_MULTICAST_OR), &(pdcpr.value)); if (rc) _BREAK; if (picsr.fields.ivrm_fsm_enable || pdcpr.fields.cpm_filter_enable) { rc = -GPSM_ILLEGAL_MODE_LPST_INSTALL; _BREAK; } // In case cores are in deep winkle so that ivrm caliburation // will fail, insert special wakeup first pcdr.value = in32(PMC_CORE_DECONFIGURATION_REG); cores = ~pcdr.fields.core_chiplet_deconf_vector; rc = occ_special_wakeup(1, cores, 25, &wakedup); if (rc) _BREAK; // Power on PFET Voltage Reference Circuit picsr.fields.pvref_en = 1; rc = putscom(MC_ADDRESS(PCBS_IVRM_CONTROL_STATUS_REG, MC_GROUP_EX, PCB_MULTICAST_WRITE), picsr.value); if (rc) _BREAK; // Wait 10us for circuit to power on timeout = ssx_timebase_get() + SSX_MICROSECONDS(10); while (ssx_timebase_get() < timeout) {;} // Perform the binary search picsr.fields.binsearch_cal_ena = 1; rc = putscom(MC_ADDRESS(PCBS_IVRM_CONTROL_STATUS_REG, MC_GROUP_EX, PCB_MULTICAST_WRITE), picsr.value); if (rc) _BREAK; // Check IVRM Calibration is completed // Poll for up to 100us for done before erroring out timeout_rc = -GPSM_IVRM_CALIBRATION_TIMEOUT; timeout = ssx_timebase_get() + SSX_MICROSECONDS(100); while (ssx_timebase_get() < timeout) { rc = getscom(MC_ADDRESS(PCBS_IVRM_CONTROL_STATUS_REG, MC_GROUP_EX, PCB_MULTICAST_AND), &(picsr.value)); if (rc) _BREAK; if (picsr.fields.binsearch_cal_done) { timeout_rc=0; break; } } if (timeout_rc||rc) _BREAK; // IVRM Calibration complete, Clear binary search enable picsr.fields.binsearch_cal_ena = 0; rc = putscom(MC_ADDRESS(PCBS_IVRM_CONTROL_STATUS_REG, MC_GROUP_EX, PCB_MULTICAST_WRITE), picsr.value); if (rc) _BREAK; // Check if IVRM Gross or Fine Error is set after calibration! rc = getscom(MC_ADDRESS(PCBS_PMERR_REG, MC_GROUP_EX, PCB_MULTICAST_OR), &(ppr.value)); if (rc) _BREAK; if (ppr.fields.pcbs_ivrm_gross_or_fine_err) { rc = -GPSM_IVRM_GROSS_OR_FINE; _BREAK; } // Deassert Special Wakeup rc = occ_special_wakeup(0, cores, 25, &wakedup); if (rc) _BREAK; // Upload the Local Pstate Array. The array is loaded via multicast, // using the built-in auto-increment mechanism. Then upload the // Pstate bounds register via multicast. Pstate clipping is not // modified. rc = putscom(MC_ADDRESS(PCBS_PSTATE_TABLE_CTRL_REG, MC_GROUP_EX, PCB_MULTICAST_WRITE), 0); if (rc) _BREAK; for (i = 0; i < LOCAL_PSTATE_ARRAY_ENTRIES+VDSVIN_ARRAY_ENTRIES; i++) { if (i < LOCAL_PSTATE_ARRAY_ENTRIES) { rc = putscom(MC_ADDRESS(PCBS_PSTATE_TABLE_REG, MC_GROUP_EX, PCB_MULTICAST_WRITE), i_lpsa->pstate[i].value); if (rc) _BREAK; } else { rc = putscom(MC_ADDRESS(PCBS_PSTATE_TABLE_REG, MC_GROUP_EX, PCB_MULTICAST_WRITE), i_lpsa->vdsvin[i-LOCAL_PSTATE_ARRAY_ENTRIES].value); if (rc) _BREAK; } } ppibr.value = 0; ppibr.fields.lpsi_min = lpst_pmin(i_lpsa) - PSTATE_MIN; ppibr.fields.lpsi_entries_minus_1 = i_lpsa->entries - 1; rc = putscom(MC_ADDRESS(PCBS_PSTATE_INDEX_BOUND_REG, MC_GROUP_EX, PCB_MULTICAST_WRITE), ppibr.value); if (rc) _BREAK; // Install the step delay parameters, then clear the undervolting // control (applicable to the entire range) via multicast. pivcr0.value = 0; if (i_lpsa->stepdelay_rising) pivcr0.fields.ivrm_req_pstate_stepdelay_rising = i_lpsa->stepdelay_rising; else pivcr0.fields.ivrm_req_pstate_stepdelay_rising = 0xFF; if (i_lpsa->stepdelay_lowering) pivcr0.fields.ivrm_req_pstate_stepdelay_lowering = i_lpsa->stepdelay_lowering; else pivcr0.fields.ivrm_req_pstate_stepdelay_lowering = 0XFF; rc = putscom(MC_ADDRESS(PCBS_IVRM_VID_CONTROL_REG0, MC_GROUP_EX, PCB_MULTICAST_WRITE), pivcr0.value); if (rc) _BREAK; ///bug need to determine where these values come from pivcr1.value = 0; pivcr1.fields.ivrm_stabilize_delay_run = 0x40; pivcr1.fields.ivrm_stabilize_delay_idle = 0x40; pivcr1.fields.ivrm_pfstr_prop_delay = 0x1E; pivcr1.fields.ivrm_pfstrvalid_prop_delay = 0xFF; pivcr1.fields.ivrm_vpump_poweron_time = 0xFF; pivcr1.fields.ivrm_bypass_delay = 0x1E; pivcr1.fields.pfet_vpump_enable_delay = 0x4E; pivcr1.fields.ivrm_vid_vout_threshold = 0x00; rc = putscom(MC_ADDRESS(PCBS_IVRM_VID_CONTROL_REG1, MC_GROUP_EX, PCB_MULTICAST_WRITE), pivcr1.value); if (rc) _BREAK; pur.value = 0; pur.fields.puv_min = lpst_pmin(i_lpsa); pur.fields.puv_max = lpst_pmax(i_lpsa); pur.fields.kuv = 0; rc = putscom(MC_ADDRESS(PCBS_UNDERVOLTING_REG, MC_GROUP_EX, PCB_MULTICAST_WRITE), pur.value); if (rc) _BREAK; // Set pre_vret_pstate to Non-Functional Pstate // \bug currently set to pmin, is it always same as psafe? rc = getscom(MC_ADDRESS(PCBS_DPLL_CPM_PARM_REG, MC_GROUP_EX, PCB_MULTICAST_OR), &(pdcpr.value)); if (rc) _BREAK; pdcpr.fields.pre_vret_pstate = lpst_pmin(i_lpsa); rc = putscom(MC_ADDRESS(PCBS_DPLL_CPM_PARM_REG, MC_GROUP_EX, PCB_MULTICAST_WRITE), pdcpr.value); if (rc) _BREAK; // Checking that PStates are NOT enabled rc = getscom(MC_ADDRESS(PCBS_PCBSPM_MODE_REG, MC_GROUP_EX, PCB_MULTICAST_OR), &(ppmr.value)); if (rc) _BREAK; if (ppmr.fields.enable_pstate_mode) { rc = -GPSM_PSTATE_ENABLED; _BREAK; } // Enable I-VRM FSM rc = getscom(MC_ADDRESS(PCBS_IVRM_CONTROL_STATUS_REG, MC_GROUP_EX, PCB_MULTICAST_OR), &(picsr.value)); if (rc) _BREAK; picsr.fields.ivrm_fsm_enable = 1; rc = putscom(MC_ADDRESS(PCBS_IVRM_CONTROL_STATUS_REG, MC_GROUP_EX, PCB_MULTICAST_WRITE), picsr.value); if (rc) _BREAK; } while (0); if (timeout_rc && !rc) return timeout_rc; else return rc; } /// Install (new) Resonant Clocking Setup /// /// \param[in] i_resclk A pointer to a ResonantClockingSetup to install in /// every configured core. /// /// \param[in] i_options Options controlling the installation, or a NULL (0) /// pointer to indicate fully default behavior. /// /// This procedure will likely only be called once, at initialization, and /// then only as part of the gpsm_initialize() procedure. The procedure: /// /// - Initializes the Pstate resonance range limits in the register /// PCBS_RESONANT_CLOCK_CONTROL_REG1 by multicast. /// /// - Setup parameters in PCBS_RESONANT_CLOCK_CONTROL_REG0 and turn on /// Resonant Clock in Hardware Pstate Mode. /// /// \note The caller is responsible for the mode-correctness of this /// procedure. This procedure must only be called when resonant clocking is /// disabled and the controls are set for manual mode. Because of the way the /// resonant clocking controls are designed we must enable resonant clocking /// to update the Pstate bounds! After the bounds are updated resonant /// clocking is disabled again. /// /// \retval 0 Success /// /// \retval -GPSM_INVALID_ARGUMENT_RCLK_INSTALL The ResonantClockingSetup /// argument was NULL (0). /// /// \retval -GPSM_ILLEGAL_MODE_RCLK_INSTALL Resonant clocking appears to be /// enabled or not in manual mode in at least one configured core. /// /// \retval others This API may also return non-0 codes from /// getscom()/putscom() int gpsm_resclk_install(const ResonantClockingSetup* i_resclk, const GlobalPstateTable* i_gpst, const PstateOptions* i_options) { ResonantClockingSetup d_resclk; pcbs_resonant_clock_control_reg0_t prccr0; pcbs_resonant_clock_control_reg1_t prccr1; int rc; uint32_t configured_cores; int flag, core; TRACE_GPSM(TRACE_GPSM_RESCLK_INSTALL); do { // Optional bypass of this procedure if ((i_options != 0) && (i_options->options & PSTATE_NO_INSTALL_RESCLK)) { rc = 0; break; } // No Resonant Clock Install and Enable if there is no configued cores configured_cores = ~in32(PMC_CORE_DECONFIGURATION_REG); flag = 1; for (core = 0; core < PGP_NCORES; core++, configured_cores <<= 1) { if (!(configured_cores & 0x80000000)) continue; flag = 0; } if (flag == 1) { rc = 0; break; } // Check the setup for existence. Do an AND-combining multicast read // to see if any of the cores have resonant clocking enabled, or are // not in manual mode. if (i_resclk == 0) { rc = -GPSM_INVALID_ARGUMENT_RCLK_INSTALL; _BREAK; } rc = getscom(MC_ADDRESS(PCBS_RESONANT_CLOCK_CONTROL_REG0, MC_GROUP_EX, PCB_MULTICAST_AND), &(prccr0.value)); if (rc) _BREAK; if (!prccr0.fields.resclk_dis || !prccr0.fields.resclk_control_mode) { rc = -GPSM_ILLEGAL_MODE_RCLK_INSTALL; _BREAK; } // Resonant clocking is specified such that it must be enabled (in a // benign manual mode) in order to be set up. // Enable resonant clocking in the GP3 register (AND), bit 22. Our // Simics environment does not model the GP3->PRCCR0 connection // currently, and does not enforce the register locks. if (!SIMICS_ENVIRONMENT) { rc = putscom(MC_ADDRESS(0x100f0013, MC_GROUP_EX, PCB_MULTICAST_WRITE), ~0x0000020000000000ull); if (rc) _BREAK; } // Write the PCBS_RESONANT_CLOCK_CONTROL_REG1 with the // Pstate setup, clearing all manual fields. // If at least one resonant clocking parm was 0 in PstateSuperStructure // write the register with default values // Low Band : 2 GHZ to 3.2 GHz // High Band : 3.2 GHZ - Up gpst_frequency2pstate(i_gpst, 0, &(d_resclk.full_csb_ps)); gpst_frequency2pstate(i_gpst, 2000000, &(d_resclk.res_low_lower_ps)); gpst_frequency2pstate(i_gpst, 3200000, &(d_resclk.res_low_upper_ps)); gpst_frequency2pstate(i_gpst, 3200000, &(d_resclk.res_high_lower_ps)); gpst_frequency2pstate(i_gpst, 9999999, &(d_resclk.res_high_upper_ps)); prccr1.value = 0; if (!(i_resclk->full_csb_ps && i_resclk->res_low_lower_ps && i_resclk->res_low_upper_ps && i_resclk->res_high_lower_ps && i_resclk->res_high_upper_ps)) { prccr1.fields.full_csb_ps = d_resclk.full_csb_ps; prccr1.fields.res_low_lower_ps = d_resclk.res_low_lower_ps; prccr1.fields.res_low_upper_ps = d_resclk.res_low_upper_ps; prccr1.fields.res_high_lower_ps = d_resclk.res_high_lower_ps; prccr1.fields.res_high_upper_ps = d_resclk.res_high_upper_ps; } else { prccr1.fields.full_csb_ps = i_resclk->full_csb_ps; prccr1.fields.res_low_lower_ps = i_resclk->res_low_lower_ps; prccr1.fields.res_low_upper_ps = i_resclk->res_low_upper_ps; prccr1.fields.res_high_lower_ps = i_resclk->res_high_lower_ps; prccr1.fields.res_high_upper_ps = i_resclk->res_high_upper_ps; } ///bug need to determine where these values come from prccr1.fields.nonres_csb_value_ti = 0xC; prccr1.fields.full_csb_value_ti = 0xF; rc = putscom(MC_ADDRESS(PCBS_RESONANT_CLOCK_CONTROL_REG1, MC_GROUP_EX, PCB_MULTICAST_WRITE), prccr1.value); if (rc) _BREAK; // Disable resonant clocking in the GP3 register (OR), bit 22. if (!SIMICS_ENVIRONMENT) { rc = putscom(MC_ADDRESS(0x100f0014, MC_GROUP_EX, PCB_MULTICAST_WRITE), 0x0000020000000000ull); if (rc) _BREAK; } // Enable resonant clock pstate hardware mode(control_mode = 0) // Sync Pulse Width maximum value : 0x7 of nest/4 cycles // Sync Delay maximum value : 0x7F of nest/4 cycles // Sector Buffer Strength Instruction . Low Band : 0xAAA // Sector Buffer Strength Instruction . High Band : 0xAAA prccr0.fields.resclk_control_mode = 0; prccr0.fields.resclk_sync_pw = 0x7; prccr0.fields.res_sync_delay_cnt = 0x7F; prccr0.fields.res_csb_str_instr_lo = 0xAAA; prccr0.fields.res_csb_str_instr_hi = 0x1FF; rc = putscom(MC_ADDRESS(PCBS_RESONANT_CLOCK_CONTROL_REG0, MC_GROUP_EX, PCB_MULTICAST_WRITE), prccr0.value); if (rc) break; // Enable resonant clocking in the GP3 register (AND), bit 22. if (!SIMICS_ENVIRONMENT) { rc = putscom(MC_ADDRESS(0x100f0013, MC_GROUP_EX, PCB_MULTICAST_WRITE), ~0x0000020000000000ull); if (rc) break; } } while (0); return rc; } /// Initialize the GPSM procedure mechanism /// /// \param[in] i_pss A pointer to the PstateSuperStructure containing the /// Global and Local Pstate tables, plus resonant clocking setup and other /// options. /// /// \param[out] o_gpst A pointer to a 1-KB aligned GlobalPstateTable which /// will be updated with a copy of the GlobalPstateTable from the /// PstateSuperStructure. /// /// This API is designed to be called once at system initialization, to set up /// GPSM mechanisms, install the Global and Local Pstate tables, and set up /// resonant clocking from the PstateSuperStructure. At the entry of this /// procedure it is assumed that the system firmware has initialized the PMC /// mode register to either no mode, or to indicate Firmware Pstate Mode. It /// is further assumed that the core chiplets are in a state which will allow /// the Local Pstate tables and resonant clocking setup to be installed /// without affecting system stability. Such a state must be guaranteed at /// system initialization and after any OCC reset. If called from any other /// context the caller is responsible for ensuring that the system is in a /// state that will allow the procedure to run correctly. /// /// This procedure does not enable Pstates or enter any Pstate mode, and does /// not alter any voltage or frequency settings. After the Pstate tables have /// been installed, Pstate mode is enabled by calls of /// gpsm_enable_pstates_master() and gpsm_enable_pstates_slave() as described /// in the commenst for gpsm_init.c. /// /// The initialization of Pstates was split up into these three steps to best /// handle the initialization of the slave chip in a DCM. This procedure /// (gpsm_initialize()) can be called by the DCM slave whenever a /// PstateSuperStructure is available. By requirememt and convention this /// Pstate SuperStructure will be identical with the one installed by the DCM /// master. /// /// The GPSM driver makes few assumptions about how the system firmware has /// set up the PMC, but does require some critical setup. /// /// - It is assumed that for DCM configurations the system firmware will have /// set the PMC_MODE_REG.enable_interchip_interface (to indicate a DCM /// configuration), and set the PMC_MODE_REG.interchip_mode appropriately for /// the master and the slave. /// /// - It is assumed that the PMC Core Deconfiguration register implies the /// same set of configured cores as the set included in the PCB multicast /// group covering all cores. /// /// All GPSM procedures use the same semaphore, which is set by the interrupt /// handler for all GPSM interrupts. The GPSM driver claims the PMC protocol /// ongoing, voltage change ongoing, and PMC Sync interrupts. /// /// Once the interrupts are set up, the GlobalPstateTable is copied from the /// PstateSuperStructure to its proper location and installed. Next, the /// Local Pstate tables and resonant clocking setup are installed into all /// cores by multicast SCOM. /// /// \retval 0 Success /// /// \retval -GPSM_INVALID_OBJECT Either the \a i_pss or \a o_gpst are NULL (0). /// /// \retval -GPSM_INVALID_MAGIC The 'magic number' of the PstateSuperStructure /// is different from that expected. /// /// \retval -GPSM_ILLEGAL_MODE_GPSM_INIT Either the PMC indicates a Pstate mode /// is active, one or more cores appear to have iVRM enabled, or one or more /// cores appear to have resonant clocking enabled. /// /// \retval others This API may also return codes from gpsm_gpst_install(), /// gpsm_lpsa_install() and gpsm_resclk_install(). int gpsm_initialize(const PstateSuperStructure* i_pss, GlobalPstateTable* o_gpst) { pmc_mode_reg_t pmr; int rc; TRACE_GPSM(TRACE_GPSM_INITIALIZE); do { // Check for a valid PstateSuperStructure and GlobalPstateTable if ((i_pss == 0) || (o_gpst == 0)) { rc = -GPSM_INVALID_OBJECT; _BREAK; } if (i_pss->magic != PSTATE_SUPERSTRUCTURE_GOOD1 && i_pss->magic != PSTATE_SUPERSTRUCTURE_GOOD2 && i_pss->magic != PSTATE_SUPERSTRUCTURE_GOOD3 && i_pss->magic != PSTATE_SUPERSTRUCTURE_GOOD4) { rc = -GPSM_INVALID_MAGIC; _BREAK; } // Check/set up the PMC mode register pmr.value = in32(PMC_MODE_REG); if (pmr.fields.enable_hw_pstate_mode || pmr.fields.enable_fw_auction_pstate_mode) { rc = -GPSM_ILLEGAL_MODE_GPSM_INIT; _BREAK; } if (!pmr.fields.enable_fw_pstate_mode) { pmr.fields.enable_fw_pstate_mode = 1; out32(PMC_MODE_REG, pmr.value); } // ** VBU ** pmr.fields.halt_pstate_master_fsm = 0; out32(PMC_MODE_REG, pmr.value); // Initialize interrupt handling ssx_semaphore_create(&G_gpsm_protocol_semaphore, 0, 1); ssx_irq_disable(PGP_IRQ_PMC_PROTOCOL_ONGOING); ssx_irq_disable(PGP_IRQ_PMC_VOLTAGE_CHANGE_ONGOING); ssx_irq_disable(PGP_IRQ_PMC_SYNC); ssx_irq_setup(PGP_IRQ_PMC_PROTOCOL_ONGOING, SSX_IRQ_POLARITY_ACTIVE_LOW, SSX_IRQ_TRIGGER_LEVEL_SENSITIVE); ssx_irq_setup(PGP_IRQ_PMC_VOLTAGE_CHANGE_ONGOING, SSX_IRQ_POLARITY_ACTIVE_LOW, SSX_IRQ_TRIGGER_LEVEL_SENSITIVE); ssx_irq_setup(PGP_IRQ_PMC_SYNC, SSX_IRQ_POLARITY_ACTIVE_HIGH, SSX_IRQ_TRIGGER_LEVEL_SENSITIVE); ssx_irq_handler_set(PGP_IRQ_PMC_PROTOCOL_ONGOING, ssx_semaphore_post_handler, (void *)(&G_gpsm_protocol_semaphore), SSX_NONCRITICAL); ssx_irq_handler_set(PGP_IRQ_PMC_VOLTAGE_CHANGE_ONGOING, ssx_semaphore_post_handler, (void *)(&G_gpsm_protocol_semaphore), SSX_NONCRITICAL); ssx_irq_handler_set(PGP_IRQ_PMC_SYNC, ssx_semaphore_post_handler, (void *)(&G_gpsm_protocol_semaphore), SSX_NONCRITICAL); // Install the Global Pstate table, Local Pstate Array and resonant // clocking setup, using options contained in the PstateSuperStructure. rc = gpsm_gpst_install(o_gpst, &(i_pss->gpst)); if (rc) _BREAK; rc = gpsm_lpsa_install(&i_pss->lpsa, &(i_pss->gpst.options)); if (rc) _BREAK; rc = gpsm_resclk_install(&i_pss->resclk, &(i_pss->gpst), &(i_pss->gpst.options)); if (rc) _BREAK; G_gpsm_initialized = 1; } while (0); return rc; } // Step voltage manually // // This API is only (?) used by gpsm_enable_pstates_master[slave](). It is // used to make a (hopefully) minor adjustment between the current voltage and // the target voltage associated with the initial Global Actual Pstate. In // cases where the current voltage is not represented in the new Pstate table, // this routine may take a long time as it will do many single-VID-code steps // as it gradually moves between the current and target voltages. // // Voltage change direction is determined by the difference in the Vdd VIDs, // and the alogorithm mimics the P7 PVID stepping protocol. If voltage is // going up, Vdd and Vcs slew together. If voltage is going down, Vdd slews // twice for every change in Vcs. Note that given a Vdd differential we can't // assume which way Vcs is moving. // // The use of Vcs offsets instead of straight-up VID codes in the hardware is // extremely confusing, especially since the offsets are defined in normal // order as opposed to VID codes which decrease as voltage increases. // Racall that the inputs are VID codes (lower VID --> higher voltage) static int _manual_step_voltage(const uint8_t i_currentVdd, const uint8_t i_currentVcs, const uint8_t i_targetVdd, const uint8_t i_targetVcs) { int rc, parity; pmc_global_actual_voltage_reg_t pgavr; uint8_t currentVdd, currentVcs; TRACE_GPSM(TRACE_MANUAL_STEP_VOLTAGE); do { rc = 0; currentVdd = i_currentVdd; currentVcs = i_currentVcs; parity = 1; while ((currentVdd != i_targetVdd) && (currentVcs != i_targetVcs)) { if (currentVdd > i_targetVdd) { // Voltage going up, slew Vdd and Vcs together. Parity remains // 1. currentVdd--; } else if (currentVdd > i_targetVdd) { // Voltage going down, only slew Vcs every other time. Parity // is inverted. currentVdd++; parity = 1 - parity; } else { // Vdd not moving, set parity so Vcs will move every time. parity = 1; } if (parity) { if (currentVcs < i_targetVcs) { currentVcs++; } else if (currentVcs > i_targetVcs) { currentVcs--; } } rc = gpsm_quiesce(); if (rc) _BREAK; pgavr.value = 0; pgavr.fields.evid_vdd = currentVdd; pgavr.fields.evid_vcs = -((int)currentVcs - (int)currentVdd); out32(PMC_GLOBAL_ACTUAL_VOLTAGE_REG, pgavr.value); } if (rc) _BREAK; rc = gpsm_quiesce(); if (rc) _BREAK; } while (0); return rc; } // This is a 'prologue' sequence executed in each core chiplet during the // initialization of Pstates. The set of cores to operate on is taken from // the current value of the PMC_CORE_DECONFIGURATION_REG. // // At entry it assumed that iVRM and CPM-DPLL are disabled. It also clears // possible safe mode dails before enable pstate. // // At exit, the following will be true for all configured cores: // // - The core will be in DPLL frequency override mode with Fmin and Fmax set // to the frequency implied by the given Pstate in the given Pstate table with // 0 undervolting. // // - The Fmax bias of the core is set from the Pstate table. // // - The Fnom of the core is set from the Pstate table. // // - Pstate mode is enabled in the core and global requests are enabled. // // - The Local Actual Pstate is being controlled by the Pstate mechanism. // // - The PMCR will have been updated to the \a frequencyPstate (both global // and local) and the global bids (should be) consistent. Auto-override modes // in the PMCR are not modified. static int _enable_pstates_core_prologue(const GlobalPstateTable* i_gpst, const Pstate i_frequency_pstate, const gpst_entry_t i_entry) { int rc, core; unsigned int bogus; uint32_t configured_cores; DpllCode fNom, fPstate; pcbs_pmgp1_reg_t pmgp1; pcbs_freq_ctrl_reg_t pfcr; pcbs_pcbspm_mode_reg_t ppmr; pcbs_power_management_control_reg_t pmcr; pcbs_power_management_bounds_reg_t ppmbr; pcbs_pmc_vf_ctrl_reg_t ppvcr; TRACE_GPSM(TRACE_ENABLE_PSTATES_CORE_PROLOGUE); do { /* In the event of no configured cores, FW requested to not error out */ //rc = -GPSM_CONFIGURATION_ERROR; rc = 0; // Do for each core chiplet... configured_cores = ~in32(PMC_CORE_DECONFIGURATION_REG); // Turn off possible safe mode so we can move pstate pcbs_hb_config(0, configured_cores, 0, 0, 0, &bogus); pmc_hb_config(0, 0, 0, &bogus); for (core = 0; core < PGP_NCORES; core++, configured_cores <<= 1) { if (!(configured_cores & 0x80000000)) continue; // The 'nominal' frequency code may be biased per core. This // should not under/over-flow. fNom = i_gpst->pstate0_frequency_code[core]; rc = bias_frequency(fNom, i_frequency_pstate, &fPstate); if (rc) _BREAK; /// \bug HW Bug: Chicken-and-egg problem with frequency override /// mode. We need a different HW control structure here. This /// may glitch frequency. // Initial PMGP1_REG setup // // - Force OCC control of the PM SPRS. This may have to be // rethought if PHYP ever controls Pstates. // // - Enable DPLL frequency overrides pmgp1.value = 0; pmgp1.fields.pm_spr_override_en = 1; pmgp1.fields.dpll_freq_override_enable = 1; rc = putscom(CORE_CHIPLET_ADDRESS(PCBS_PMGP1_REG_OR, core), pmgp1.value); if (rc) _BREAK; // Update Fmin, Fmax, Fmax bias and Pstate0 frequency. pfcr.value = 0; pfcr.fields.dpll_fmin = fPstate; pfcr.fields.dpll_fmax = fPstate; pfcr.fields.dpll_fmax_bias = i_gpst->dpll_fmax_bias[core]; pfcr.fields.frequ_at_pstate0 = fNom; rc = putscom(CORE_CHIPLET_ADDRESS(PCBS_FREQ_CTRL_REG, core), pfcr.value); if (rc) _BREAK; /// \bug HW BUG : PCBS_Power_Management_Bounds_reg hardware reset /// is whack, violating the Pstate constraints. (HW216565). /// Deferred to Venice. Not a problem for us, we always set up /// this register. // The PCBS clipping is initialized to the limits present in the // _global_ Pstate table. This is necessary for correctness of // the PCBS state machines. If fast-idle modes with retention are // enabled this is also necessary to protect against trying to // drop into non-functional Pstates required to be present in the // _local_ pstate table. // \bug Workaround, since pre_vret_pstate is set to pmin currently // until pstate super structure and pstate data block procedure // support an entry as non-functional pstate, need to set lower // clip bound to be the pstate one above pmin to make pmin // essentially a non-functional pstate for now ppmbr.value = 0; ppmbr.fields.pmin_clip = gpst_pmin(i_gpst)+1; ppmbr.fields.pmax_clip = gpst_pmax(i_gpst); // This fix is added per SW260911 // Minimum Frequency in the system is given by MRW attribute // PState Datablock procedure will read the attribute then // convert it into pstate _pfloor_ and put it into // Global Pstate Table. GPSM here consumes the value // and set both lower bounds: pmin_rail(PMC) and pmin_clip(PCBS) // and two safe pstates: pvsafe(PMc) and psafe(PCBS) to be // _pfloor_ if _pfloor_ is higher than their default(gpst_pmin) // so that we should never run with frequency below the floor // even in safe mode if (ppmbr.fields.pmin_clip < i_gpst->pfloor && i_gpst->pfloor != 0) ppmbr.fields.pmin_clip = i_gpst->pfloor; rc = putscom(CORE_CHIPLET_ADDRESS(PCBS_POWER_MANAGEMENT_BOUNDS_REG, core), ppmbr.value); if (rc) _BREAK; // Now that we've locked the frequency and set valid clipping // bounds, disable the local Pstate override and allow Global Acks // and Pmax-Sync to propogate. rc = getscom(CORE_CHIPLET_ADDRESS(PCBS_PMGP1_REG, core), &(pmgp1.value)); if (rc) _BREAK; pmgp1.value = 0; pmgp1.fields.enable_occ_ctrl_for_local_pstate_eff_req = 1; rc = putscom(CORE_CHIPLET_ADDRESS(PCBS_PMGP1_REG_AND, core), ~pmgp1.value); if (rc) _BREAK; // Setup PCBS_PMC_VF_CTRL_REG before enable Pstate rc = getscom(CORE_CHIPLET_ADDRESS(PCBS_PMC_VF_CTRL_REG, core), &(ppvcr.value)); if (rc) _BREAK; ppvcr.fields.pglobal_actual = i_frequency_pstate; ppvcr.fields.maxregvcs = i_entry.fields.maxreg_vdd; ppvcr.fields.maxregvdd = i_entry.fields.maxreg_vcs; ppvcr.fields.evidvcs_eff = i_entry.fields.evid_vdd_eff; ppvcr.fields.evidvdd_eff = i_entry.fields.evid_vcs_eff; rc = putscom(CORE_CHIPLET_ADDRESS(PCBS_PMC_VF_CTRL_REG, core), ppvcr.value); if (rc) _BREAK; // Enable Pstate in PCB Slave rc = getscom(CORE_CHIPLET_ADDRESS(PCBS_PCBSPM_MODE_REG, core), &(ppmr.value)); if (rc) _BREAK; ppmr.fields.enable_pstate_mode = 1; ppmr.fields.enable_global_pstate_req = 1; ppmr.fields.enable_pmc_pmax_sync_notification = 1; // ** VBU ** ppmr.fields.dpll_lock_replacement_timer_mode_en = 1; rc = putscom(CORE_CHIPLET_ADDRESS(PCBS_PCBSPM_MODE_REG, core), ppmr.value); if (rc) _BREAK; // Update the PMCR to propagate the global bids rc = getscom(CORE_CHIPLET_ADDRESS(PCBS_POWER_MANAGEMENT_CONTROL_REG, core), &(pmcr.value)); if (rc) _BREAK; pmcr.fields.global_pstate_req = i_frequency_pstate; pmcr.fields.local_pstate_req = i_frequency_pstate; rc = putscom(CORE_CHIPLET_ADDRESS(PCBS_POWER_MANAGEMENT_CONTROL_REG, core), pmcr.value); if (rc) _BREAK; } if (rc) _BREAK; } while (0); return rc; } // This is an 'epilogue' sequence executed in each core chiplet during the // enablement of Pstate mode. When this code is executed, the core is in // frequency override mode at (or below) the frequency of the Global Pstate // Actual. This procedure releases frequency override mode and core-level // Pstate operations commence. // // retval -GPSM_BABYSTEPPER_SYNC_TIMEOUT, if baby stepper sync // local_pstate_actual times out // static int _enable_pstates_core_epilogue(void) { int rc = 0, timeout_rc = 0, core; uint32_t configured_cores; pcbs_pmgp1_reg_t pmgp1; pcbs_power_management_status_reg_t ppmsr; SsxTimebase timeout; TRACE_GPSM(TRACE_ENABLE_PSTATES_CORE_EPILOGUE); do { configured_cores = ~in32(PMC_CORE_DECONFIGURATION_REG); for (core = 0; core < PGP_NCORES; core++, configured_cores <<= 1) { if (!(configured_cores & 0x80000000)) continue; pmgp1.value = 0; pmgp1.fields.dpll_freq_override_enable = 1; rc = putscom(CORE_CHIPLET_ADDRESS(PCBS_PMGP1_REG_AND, core), ~pmgp1.value); if (rc) _BREAK; } if (rc) _BREAK; // For Babystepper to catch up sync the local_pstate_actual // Poll for up to 300us for done before erroring out timeout_rc = -GPSM_BABYSTEPPER_SYNC_TIMEOUT; timeout = ssx_timebase_get() + SSX_MICROSECONDS(300); while (ssx_timebase_get() < timeout) { rc = getscom(MC_ADDRESS(PCBS_POWER_MANAGEMENT_STATUS_REG, MC_GROUP_EX, PCB_MULTICAST_AND), &(ppmsr.value)); if (rc) _BREAK; if (ppmsr.fields.local_pstate_actual == ppmsr.fields.global_pstate_actual) { timeout_rc = 0; break; } } if (timeout_rc||rc) _BREAK; } while(0); if (timeout_rc && !rc) return timeout_rc; else return rc; } /// Enable Pstates in Firmware Mode, initial Master-only phase /// /// \param[out] o_info This structure is populated by this API for use /// by a DCM master in gpsm_enable_pstates_slave. The DCM slave does not /// require this information. /// /// \param[out] o_voltage_pstate This parameter returns the Pstate /// corresponding to the current system voltage (or the closest safe /// approximation). This parameter must be communicated to the slave before /// the slave can call gpsm_enable_pstates_slave(). /// /// \param[out] o_frequency_pstate *DEPRECATED* This parameter returns the /// Pstate corresponding to the current system voltage (or the closest safe /// approximation). This parameter must be communicated to the slave before /// the slave can call gpsm_enable_pstates_slave(). /// /// \note This procedure is only called on an SCM or a DCM master. It will /// fail if called on a DCM slave. /// /// \retval 0 Success /// /// \returns All other return codes indicate an error. int gpsm_enable_pstates_master(GpsmEnablePstatesMasterInfo* o_info, Pstate* o_voltage_pstate, Pstate* o_frequency_pstate) { int rc, search_rc; GlobalPstateTable* gpst; gpst_entry_t voltage_entry; TRACE_GPSM(TRACE_GPSM_ENABLE_PSTATES_MASTER); do { if (gpsm_dcm_slave_p()) { rc = -GPSM_ILLEGAL_MODE_EPSM; _BREAK; } // Enter Firmware Pstate Mode. The gpsm_fw_mode() procedure // guarantees that the GPSM is quiesced at this point. Recover a // pointer to the Pstate table from PMC. rc = gpsm_fw_mode(); if (rc) _BREAK; gpst = gpsm_gpst(); // Map the current Vdd VID to a pstate in the new Pstate table. // // As an option (workaround, simulation hack), force the assumption // that the current voltage corresponds to PMIN. This will not move // the external voltage, however it will force the frequency down to // the PMIN frequency prior to starting Pstate operations. It is // always safe to change the Pstate from "PMIN", regardless of the // actual external voltage, since the PMIN frequency is safe at any // voltage. if (!(gpst->options.options & PSTATE_FORCE_INITIAL_PMIN)) { rc = vrm_voltage_read(SPIVRM_PORT(0), VRM_RD_VDD_RAIL, &(o_info->currentVdd)); if (rc) _BREAK; rc = vrm_voltage_read(SPIVRM_PORT(0), VRM_RD_VCS_RAIL, &(o_info->currentVcs)); if (rc) _BREAK; } else { rc = gpst_entry(gpst, gpst_pmin(gpst), 0, &voltage_entry); if (rc) { SSX_PANIC(GPSM_BUG); /* This can't happen */ } o_info->currentVdd = voltage_entry.fields.evid_vdd; o_info->currentVcs = voltage_entry.fields.evid_vcs; } search_rc = gpst_vdd2pstate(gpst, o_info->currentVdd, o_voltage_pstate, &voltage_entry); if (search_rc && (search_rc != -GPST_PSTATE_CLIPPED_LOW_GPST_V2P) && (search_rc != -GPST_PSTATE_CLIPPED_HIGH_GPST_V2P)) { rc = search_rc; break; } o_info->targetVdd = voltage_entry.fields.evid_vdd; o_info->targetVcs = voltage_entry.fields.evid_vcs; // If the Pstate was 'clipped low', it indicates that the current // voltage is lower than the lowest new Pstate. Therefore we need to // manually step voltage up before locking in the Pmin frequency. If // the Pstate was 'clipped high' it means that the current voltage is // higher than the highest Pstate, and we need to lock frequency at // the Pmax frequency prior to stepping voltage down. The unclipped // case is lumped with the 'clipped low' case as this case might // entail a slight rise of voltage. V/F stepping must be split across // the calls of gpsm_enable_pstates_master[slave]. if ((search_rc == 0)||(search_rc = -GPST_PSTATE_CLIPPED_LOW_GPST_V2P)) { rc = _manual_step_voltage(o_info->currentVdd, o_info->currentVcs, o_info->targetVdd, o_info->targetVcs); if (rc) _BREAK; o_info->move_voltage = 0; } else { o_info->move_voltage = 1; } } while (0); /// \todo The o_frequency_pstate parameter is no longer needed. It was /// originally needed when the Pstate table had an undervolting bias. *o_frequency_pstate = *o_voltage_pstate; return rc; } /// Enable Pstates in Firmware Pstate Mode, final Master/Slave phase /// /// \param[in] i_info This structure is populated by /// gpsm_enable_pstates_master(), and only required in an SCM or DCM master. /// When this API is called on a DCM slave the parameter may be passed as NULL /// (0). /// /// \param[in] i_voltage_pstate This parameter is computed by /// gpsm_enable_pstates_master(), and is required in every case. /// /// \param[in] i_frequency_pstate This parameter is computed by /// gpsm_enable_pstates_master(), and is required in every case. /// /// \note This procedure is called in all cases as the final step in enabling /// Pstate mode: SCM, DCM master, DCM slave. /// /// \retval 0 Success /// /// \returns All other return codes indicate an error. int gpsm_enable_pstates_slave(const GpsmEnablePstatesMasterInfo* i_info, const Pstate i_voltage_pstate, const Pstate i_frequency_pstate) { int rc; GlobalPstateTable* gpst; pmc_mode_reg_t pmr; gpst_entry_t voltage_entry; TRACE_GPSM(TRACE_GPSM_ENABLE_PSTATES_SLAVE); do { // Enter Firmware Pstate Mode. The gpsm_fw_mode() procedure // guarantees that the GPSM is quiesced at this point for the slave; // the master must already be quiesced. Recover a pointer to the // Pstate table from PMC. if (gpsm_dcm_slave_p()) { rc = gpsm_fw_mode(); if (rc) _BREAK; } else { if (!i_info) { rc = -GPSM_INVALID_ARGUMENT_EPSS; _BREAK; } else if (!gpsm_fw_mode_p() || !gpsm_quiesced_p()) { rc = -GPSM_ILLEGAL_MODE_EPSS; _BREAK; } } gpst = gpsm_gpst(); gpst_entry(gpst, i_voltage_pstate, 0, &voltage_entry); // Execute the core prologue. An SCM or DCM master may need to move // voltage after the frequency move. Since this is guaranteed to be a // safe downward move (otherwise we would have moved voltage already), // it is safe for the DCM slave to go ahead and finish its Pstate // setup before the master has moved the voltage. rc = _enable_pstates_core_prologue(gpst, i_frequency_pstate, voltage_entry); if (rc) _BREAK; if (!gpsm_dcm_slave_p()) { rc = _manual_step_voltage(i_info->currentVdd, i_info->currentVcs, i_info->targetVdd, i_info->targetVcs); if (rc) _BREAK; } // The Voltage and Frequency state is now consistent in the cores and // in PMC. Make sure that PMC modes are set correctly for Hardware // Pstate Mode. pmr.value = in32(PMC_MODE_REG); pmr.fields.enable_pstate_voltage_changes = 1; pmr.fields.enable_global_actual_pstate_forwarding = 1; //pmr.fields.enable_pstate_stepping = 1; out32(PMC_MODE_REG, pmr.value); // Since we're in Firmware Pstate mode and all cores are // frequency-locked, we can set the Global Actual without stepping - // under the assumption that the caller has disabled iVRM prior to the // call. The master has already computed the volatge_pstate. We // allow the GPSM to quiesce before unlocking the core frequencies. _gpsm_broadcast_global_actual(i_frequency_pstate, voltage_entry); rc = gpsm_quiesce(); if (rc) _BREAK; rc = _enable_pstates_core_epilogue(); if (rc) _BREAK; } while (0); return rc; }