summaryrefslogtreecommitdiffstats
path: root/src/lib/gpsm.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/gpsm.c')
-rwxr-xr-xsrc/lib/gpsm.c600
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;
+}
+
+
OpenPOWER on IntegriCloud