diff options
Diffstat (limited to 'src/lib/gpsm.c')
-rwxr-xr-x | src/lib/gpsm.c | 600 |
1 files changed, 600 insertions, 0 deletions
diff --git a/src/lib/gpsm.c b/src/lib/gpsm.c new file mode 100755 index 0000000..e2b71d6 --- /dev/null +++ b/src/lib/gpsm.c @@ -0,0 +1,600 @@ +// $Id: gpsm.c,v 1.2 2014/02/03 01:30:24 daviddu Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/gpsm.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file gpsm.c +/// \brief Global Pstate Mechanism procedures +/// +/// \todo : Should we initialize any/all iVRM delays in gpsm_lpsa_install()? + +#include "ssx.h" +#include "pstates.h" +#include "gpe_control.h" +#include "gpsm.h" +#include "vrm.h" + +/// The semaphore used to block threads waiting for GPSM protocol actions + +SsxSemaphore G_gpsm_protocol_semaphore; + + +//////////////////////////////////////////////////////////////////////////// +// Private Utilities +//////////////////////////////////////////////////////////////////////////// + +// The mechanical transition to Firmware Pstate Mode - Not a procedure + +static void +_gpsm_fw_mode(void) +{ + pmc_mode_reg_t pmr; + + pmr.value = in32(PMC_MODE_REG); + pmr.fields.enable_hw_pstate_mode = 0; + pmr.fields.enable_fw_auction_pstate_mode = 0; + pmr.fields.enable_fw_pstate_mode = 1; + out32(PMC_MODE_REG, pmr.value); +} + + +// The mechanical transition to Firmware Auction Pstate Mode - Not a procedure + +static void +_gpsm_fw_auction_mode(void) +{ + pmc_mode_reg_t pmr; + + pmr.value = in32(PMC_MODE_REG); + pmr.fields.enable_hw_pstate_mode = 0; + pmr.fields.enable_fw_auction_pstate_mode = 1; + pmr.fields.enable_fw_pstate_mode = 0; + out32(PMC_MODE_REG, pmr.value); +} + + +// The mechanical transition to Hardware Pstate Mode - Not a procedure. +// Disable voltage change via safe_mode_without_spivid +// before enter hw mode to prevent possible glitch that +// hw momentarily flush turbo to pstate actual, +// After enter hw mode, we will enable back the spivid + +static void +_gpsm_hw_mode(void) +{ + pmc_mode_reg_t pmr; + + if (!gpsm_dcm_slave_p()) { + pmr.value = in32(PMC_MODE_REG); + pmr.fields.safe_mode_without_spivid = 1; + out32(PMC_MODE_REG, pmr.value); + } + + pmr.value = in32(PMC_MODE_REG); + pmr.fields.enable_hw_pstate_mode = 1; + pmr.fields.enable_fw_auction_pstate_mode = 0; + pmr.fields.enable_fw_pstate_mode = 0; + out32(PMC_MODE_REG, pmr.value); + + if (!gpsm_dcm_slave_p()) { + pmr.value = in32(PMC_MODE_REG); + pmr.fields.safe_mode_without_spivid = 0; + out32(PMC_MODE_REG, pmr.value); + } + +} + + +//////////////////////////////////////////////////////////////////////////// +// Private Sub-Procedures +//////////////////////////////////////////////////////////////////////////// + +// By definition, quiescing the GPSM always leaves the system in firmware +// Pstate mode. This is necessary for a consistent specification due to the +// fact that in general, Hardware Pstate mode can not be quiesced without +// leaving that mode. + +// To quiesce the GPSM in firmware or firmware auction mode requires waiting +// for both the voltage and frequency changes to be complete. Note that they +// will never be ongoing simultaneously. This predicate is used for all +// firmware-mode quiesce, even though normally only one part or the other +// (voltage/protocol) is active. +// +// Recall that PMC interrupts are level-low, so if ongoing status is clear, +// the operation is ongoing. + +static int +gpsm_fw_quiesce(void) +{ + int rc = 0; + + if (!ssx_irq_status_get(PGP_IRQ_PMC_PROTOCOL_ONGOING)) { + ssx_irq_enable(PGP_IRQ_PMC_PROTOCOL_ONGOING); + rc = ssx_semaphore_pend(&G_gpsm_protocol_semaphore, SSX_WAIT_FOREVER); + } + + if ((!rc) && !ssx_irq_status_get(PGP_IRQ_PMC_VOLTAGE_CHANGE_ONGOING)) { + ssx_irq_enable(PGP_IRQ_PMC_VOLTAGE_CHANGE_ONGOING); + rc = ssx_semaphore_pend(&G_gpsm_protocol_semaphore, SSX_WAIT_FOREVER); + } + + if (!rc) { + _gpsm_fw_mode(); + } + + return rc; +} + + +// To quiesce the GPSM in hardware mode requires waiting for any ongoing +// Pstate change to be complete. Note that there is no guarantee that this +// condition will ever be true in general unless something external to PMC +// ensures that Global bids stop coming in to the GPSM. An alternative used +// here is to 'lock' the GPSM temporarily by setting the rail bounds min and +// max to the current Global Pstate Actual. The GPSM will eventually quiesce +// at the global actual, and we can safely move to Firmware Pstate mode and +// release the lock. +// +// Recall that PMC 'ongoing' interrupts are level-low, so if ongoing status is +// clear, the operation is ongoing. + +static int +gpsm_hw_quiesce(void) +{ + int rc = 0; + pmc_rail_bounds_register_t prbr, original_prbr; + pmc_pstate_monitor_and_ctrl_reg_t ppmacr; + + ppmacr.value = in32(PMC_PSTATE_MONITOR_AND_CTRL_REG); + + original_prbr.value = prbr.value = in32(PMC_RAIL_BOUNDS_REGISTER); + prbr.fields.pmin_rail = ppmacr.fields.gpsa; + prbr.fields.pmax_rail = ppmacr.fields.gpsa; + out32(PMC_RAIL_BOUNDS_REGISTER, prbr.value); + + rc = _gpsm_hw_quiesce(); + + if (!rc) { + _gpsm_fw_mode(); + out32(PMC_RAIL_BOUNDS_REGISTER, original_prbr.value); + } + + return rc; +} + +//////////////////////////////////////////////////////////////////////////// +// Public Predicates +//////////////////////////////////////////////////////////////////////////// + +/// Is the Global Pstate Mechanism quiesced? +/// +/// This predicate can only truly be answered 'true' if we are not in +/// hardware Pstate mode. +/// +/// \retval 0 Either we're in Hardware Pstate Mode, or a Voltage/Frequency +/// operation is ongoing. +/// +/// \retval 1 We're not in Hardware Pstate Mode and no Voltage/Frequency +/// operation is ongoing. + +int +gpsm_quiesced_p(void) +{ + return !(gpsm_hw_mode_p() || + !ssx_irq_status_get(PGP_IRQ_PMC_PROTOCOL_ONGOING) || + !ssx_irq_status_get(PGP_IRQ_PMC_VOLTAGE_CHANGE_ONGOING)); +} + +/// Predicate: Is the PMC in hardware Pstate mode? +/// +/// \returns 0/1 + +int +gpsm_hw_mode_p(void) +{ + pmc_mode_reg_t pmr; + + pmr.value = in32(PMC_MODE_REG); + return (pmr.fields.enable_hw_pstate_mode != 0); +} + + +/// Predicate: Is the PMC in firmware auction Pstate mode? +/// +/// \returns 0/1 + +int +gpsm_fw_auction_mode_p(void) +{ + pmc_mode_reg_t pmr; + + pmr.value = in32(PMC_MODE_REG); + return (pmr.fields.enable_fw_auction_pstate_mode != 0); +} + + +/// Predicate: Is the PMC in firmware Pstate mode? +/// +/// \returns 0/1 + +int +gpsm_fw_mode_p(void) +{ + pmc_mode_reg_t pmr; + + pmr.value = in32(PMC_MODE_REG); + return (pmr.fields.enable_fw_pstate_mode != 0); +} + + +/// Predicate: Is the chip configured as a DCM? +/// +/// \returns 0/1 + +int +gpsm_dcm_mode_p(void) +{ + pmc_mode_reg_t pmc_mode_reg; + pmc_mode_reg.value = in32(PMC_MODE_REG); + return pmc_mode_reg.fields.enable_interchip_interface; +} + + +/// Predicate: Is the chip configured as a DCM Slave? +/// +/// \returns 0/1 + +int +gpsm_dcm_master_p(void) +{ + pmc_mode_reg_t pmc_mode_reg; + pmc_mode_reg.value = in32(PMC_MODE_REG); + return + pmc_mode_reg.fields.enable_interchip_interface && + pmc_mode_reg.fields.interchip_mode; +} + + +/// Predicate: Is the chip configured as a DCM Slave? +/// +/// \returns 0/1 + +int +gpsm_dcm_slave_p(void) +{ + pmc_mode_reg_t pmc_mode_reg; + pmc_mode_reg.value = in32(PMC_MODE_REG); + return + pmc_mode_reg.fields.enable_interchip_interface && + (pmc_mode_reg.fields.interchip_mode == 0); +} + + + + +//////////////////////////////////////////////////////////////////////////// +// Procedures +//////////////////////////////////////////////////////////////////////////// + +/// Recover the GlobalPstateTable object from the PMC +/// +/// \note It is assumed that the pointer to the Global Pstate table installed +/// in the PMC is actually a pointer to a complete GlobalPstateTable object +/// (which contains a Global Pstate table as its first element). +/// +/// \returns A pointer to the currently active GlobalPstateTable object. + +GlobalPstateTable* +gpsm_gpst(void) +{ + pmc_parameter_reg1_t ppr1; + + ppr1.value = in32(PMC_PARAMETER_REG1); + return (GlobalPstateTable*) + (ppr1.fields.ba_sram_pstate_table << GLOBAL_PSTATE_TABLE_ALIGNMENT); +} + + +/// Quiesce the GPSM to firmware mode from any other mode +/// +/// At the exit of this procedure, the PMC will be in Firmware Pstate Mode and +/// there will be no ongoing voltage or frequency transitions. +int +gpsm_quiesce(void) +{ + int rc; + + if (gpsm_hw_mode_p()) { + rc = gpsm_hw_quiesce(); + } else { + rc = gpsm_fw_quiesce(); + } + + return rc; +} + +/// Quiesce the GPSM in Hardware Pstate Mode +/// +/// In general there is no guarantee that the GPSM will ever quiesce, or +/// remain quiesced in Hardware Pstate Mode unless something like the +/// procedure in gpsm_hw_quiesce() is used. This procedure is provided for +/// the benefit of applications that are in complete control of Pstates +/// (including idle state Pstates) to simply wait for the Pstate protocol to +/// quiesce, without quiescing and entering Firmware Pstate mode like +/// gpsm_hw_quiesce(). + +int +_gpsm_hw_quiesce(void) +{ + int rc; + + do { + rc = 0; + + if (!gpsm_hw_mode_p()) { + rc = -GPSM_ILLEGAL_MODE_HW_QUIESCE; + break; + } + + if (!ssx_irq_status_get(PGP_IRQ_PMC_PROTOCOL_ONGOING)) { + ssx_irq_enable(PGP_IRQ_PMC_PROTOCOL_ONGOING); + rc = ssx_semaphore_pend(&G_gpsm_protocol_semaphore, + SSX_WAIT_FOREVER); + if (rc) break; + } + } while (0); + + return rc; +} + + +/// Change to GPSM firmware mode from any mode + +int +gpsm_fw_mode(void) +{ + return gpsm_quiesce(); +} + + +/// Change to GPSM firmware auction mode from any mode + +int +gpsm_fw_auction_mode(void) +{ + int rc; + + rc = gpsm_quiesce(); + if (!rc) { + _gpsm_fw_auction_mode(); + } + return rc; +} + + +/// Change to Hardware Pstate Mode +/// +/// The (unchecked) assumption behind this procedure is that the caller has +/// run through Pstate intialization and enablement, and the system is in a +/// state where the entry to Hardware Pstate Mode is safe once the GPSM is +/// quiesced. + +int +gpsm_hw_mode(void) +{ + int rc; + + TRACE_GPSM(TRACE_GPSM_HW_MODE); + + rc = gpsm_quiesce(); + if (!rc) { + _gpsm_hw_mode(); + } + return rc; +} + + +/// The default GPSM auction procedure +/// +/// The default auction returns the value of +/// PMC_HARDWARE_AUCTION_PSTATE_REG.haps. + +Pstate +gpsm_default_auction(void) +{ + pmc_hardware_auction_pstate_reg_t phapr; + + phapr.value = in32(PMC_HARDWARE_AUCTION_PSTATE_REG); + return phapr.fields.haps; +} + + +/// Update a user-supplied vector of Pstates with the current Global bid of +/// each core. +/// +/// \param[out] o_bids A vector of Pstates; The vector must be large enough to +/// hold the bid of every possible core. +/// +/// This routine is provided for use by non-default Global Pstate auction +/// procedures. + +void +gpsm_get_global_bids(Pstate *o_bids) +{ + // This takes advantage of the implicit layout of the + // PMC_CORE_PSTATE_REG<n>. + + uint32_t *bids32 = (uint32_t *)o_bids; + + bids32[0] = in32(PMC_CORE_PSTATE_REG0); + bids32[1] = in32(PMC_CORE_PSTATE_REG1); + bids32[2] = in32(PMC_CORE_PSTATE_REG2); + bids32[3] = in32(PMC_CORE_PSTATE_REG3); +} + + +/// Update a current Global bid of each core from a user supplied vector. +/// +/// \param[in] i_bids An array of Global Pstate bids. +/// +/// This routine is provided for use by test procedures; there is likely no +/// product-level energy management application for this procedure. + +void +gpsm_set_global_bids(const Pstate *i_bids) +{ + // This takes advantage of the implicit layout of the + // PMC_CORE_PSTATE_REG<n>. + + uint32_t *bids32 = (uint32_t *)i_bids; + + out32(PMC_CORE_PSTATE_REG0, bids32[0]); + out32(PMC_CORE_PSTATE_REG1, bids32[1]); + out32(PMC_CORE_PSTATE_REG2, bids32[2]); + out32(PMC_CORE_PSTATE_REG3, bids32[3]); +} + + +/// Application-controlled Global Actual Broadcast +/// +/// \param[in] i_pstate The Global Actual Pstate to broadcast +/// +/// \param[in] i_entry A gpst_entry_t containing the information to be used by +/// the iVRM. If iVRM are not enabled then \a entry can be initialized to 0. +/// +/// This API is provided for advanced applications to have complete control +/// over a firmware-mode Global Actual broadcast. There is no error +/// checking. Most applications in Firware Pstate mode will use the +/// higher-level gpsm_broadcast_global_actual() API. + +void +_gpsm_broadcast_global_actual(const Pstate i_pstate, + const gpst_entry_t i_entry) +{ + pmc_pstate_monitor_and_ctrl_reg_t ppmacr; + pmc_eff_global_actual_voltage_reg_t pegavr; + + ppmacr.value = 0; + ppmacr.fields.gpsa = i_pstate; + out32(PMC_PSTATE_MONITOR_AND_CTRL_REG, ppmacr.value); + + pegavr.value = 0; + pegavr.fields.maxreg_vdd = i_entry.fields.maxreg_vdd; + pegavr.fields.maxreg_vcs = i_entry.fields.maxreg_vcs; + pegavr.fields.eff_evid_vdd = i_entry.fields.evid_vdd_eff; + pegavr.fields.eff_evid_vcs = i_entry.fields.evid_vcs_eff; + out32(PMC_EFF_GLOBAL_ACTUAL_VOLTAGE_REG, pegavr.value); + + TRACE_GPSM(TRACE_GPSM_BROADCAST_GLOBAL_ACTUAL); +} + + +/// Broadcast the Global Actual Pstate in firmware Pstate mode. +/// +/// \param[in] i_gpst An initialized GlobalPstateTable structure used to +/// define the legal Pstate range, and to provide the voltage settings +/// (maxreg_vxx and eff_evid_vxx) for the internal VRM. +/// +/// \param[in] i_pstate The pstate specfiying the Global Actual Pstate to be +/// broadast to the core chiplets. +/// +/// \param[in] i_bias This is a signed bias used to obtain the voltage Pstate, +/// <em> in addition to the \a undervolting_bias already built into the Pstate +/// table </em>. The iVRM information sent with the Global Actual Pstate comes +/// from the \a pstate - \a undervolting_bias + \a bias entry of the Pstate +/// table. +/// +/// This API can be used in firware Pstate mode to broadcast a Global Actual +/// Pstate and iVRM settings to the core chiplets. The API also supports +/// optional under/over-volting. The requested Pstate will be broadcast along +/// with the voltage information from the associated Pstate table entry. +/// +/// Under/over-volting is specified by setting the \a bias to a non-0 +/// (signed) value. For example, to undervfolt by one Pstate (if possible), +/// call the API with \a bias = -1. +/// +/// This API always waits for the Global Pstate Machine to quiesce before +/// proceeding with the Global Actual broadcast. Therefore it can only be +/// called from thread mode, or from a non-thread mode guaranteed by the +/// caller to have quiesced. +/// +/// \note The application can use the _gpsm_broadcast_global_actual() API for +/// complete control over the information transmitted to the cores. +/// +/// The following return codes are not considered errors: +/// +/// \retval 0 Success +/// +/// \retval -GPST_PSTATE_CLIPPED_HIGH_GPSM_BGA The requested Pstate does not +/// exist in the table. The maximum Pstate entry in the table has been +/// broadcast as the voltage Pstate. +/// +/// \retval -GPST_PSTATE_CLIPPED_LOW_GPSM_BGA The requested Pstate does not +/// exist in the table. The minimum Pstate entry in the table has been +/// broadcast as the voltage Pstate. +/// +/// The following return codes are considered errors: +/// +/// \retval -GPSM_INVALID_OBJECT The Global Pstate Table is either null (0) or +/// otherwise invalid. +/// +/// \retval -GPSM_INVALID_ARGUMENT One or more arguments are invalid or +/// inconsistent in some way. +/// +/// \retval -GPSM_ILLEGAL_MODE_BGA The PMC is not in firmware pstate mode. +/// +/// This API may also return errors from the SSX semaphore operations that +/// implement the wait for quiescence. + +int +gpsm_broadcast_global_actual(const GlobalPstateTable* i_gpst, + const Pstate i_pstate, + const int i_bias) +{ + int rc, bias_rc, entry_rc; + gpst_entry_t entry; + Pstate voltage_pstate; + + do { + + if (!gpsm_fw_mode_p()) { + rc = GPSM_ILLEGAL_MODE_BGA; + break; + } + + // Bias the pstate, fetch the Pstate entry, quiesce and broadcast. + // bias_pstate() only returns saturation warnings. These are turned + // into bounds warnings if necessary (indicating that the Pstate + // saturated but the PMAX or PMIN was also a legal entry in the + // table). + + bias_rc = bias_pstate(i_pstate, i_bias, &voltage_pstate); + entry_rc = gpst_entry(i_gpst, voltage_pstate, 0, &entry); + if (entry_rc && + (entry_rc != -GPST_PSTATE_CLIPPED_LOW_GPST_ENTRY) && + (entry_rc != -GPST_PSTATE_CLIPPED_HIGH_GPST_ENTRY)) { + rc = entry_rc; + break; + } + + rc = gpsm_quiesce(); + if (rc) break; + + _gpsm_broadcast_global_actual(i_pstate, entry); + + if (entry_rc != 0) { + rc = entry_rc; + } else if (bias_rc == -PSTATE_OVERFLOW_BIAS_PS) { + rc = -GPST_PSTATE_CLIPPED_HIGH_GPSM_BGA; + } else if (bias_rc == -PSTATE_UNDERFLOW_BIAS_PS) { + rc = -GPST_PSTATE_CLIPPED_LOW_GPSM_BGA; + } + } while (0); + + return rc; +} + + |