diff options
Diffstat (limited to 'src/lib/vrm.c')
-rwxr-xr-x | src/lib/vrm.c | 394 |
1 files changed, 394 insertions, 0 deletions
diff --git a/src/lib/vrm.c b/src/lib/vrm.c new file mode 100755 index 0000000..57f19fa --- /dev/null +++ b/src/lib/vrm.c @@ -0,0 +1,394 @@ +// $Id: vrm.c,v 1.2 2014/02/03 01:30:26 daviddu Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/vrm.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file vrm.c +/// \brief PgP SPIVRM procedures + +#include "vrm.h" + +// The semaphore used to block threads waiting for o2s operations to complete + +static SsxSemaphore o2s_protocol_semaphore; + + +/// o2s_initialize follows the steps for setting up the o2s bridge as outlined +/// In the Energy Management spec (PMC section) +int +o2s_initialize() +{ + pmc_o2s_ctrl_reg0a_t pocr0a; + pmc_o2s_ctrl_reg0b_t pocr0b; + pmc_o2s_ctrl_reg1_t pocr1; + + + ssx_semaphore_create(&o2s_protocol_semaphore, 0, 1); + + ssx_irq_disable(PGP_IRQ_OCI2SPIVID_ONGOING); + + ssx_irq_setup(PGP_IRQ_OCI2SPIVID_ONGOING, + SSX_IRQ_POLARITY_ACTIVE_LOW, + SSX_IRQ_TRIGGER_LEVEL_SENSITIVE); + + ssx_irq_handler_set(PGP_IRQ_OCI2SPIVID_ONGOING, + ssx_semaphore_post_handler, + (void *)(&o2s_protocol_semaphore), + SSX_NONCRITICAL); + + pocr0a.value = in32(PMC_O2S_CTRL_REG0A); + pocr0a.fields.o2s_frame_size = 32; + pocr0a.fields.o2s_out_count1 = 32; + pocr0a.fields.o2s_in_delay1 = 0x3F; + pocr0a.fields.o2s_in_count1 = 0; + out32(PMC_O2S_CTRL_REG0A, pocr0a.value); + + pocr0b.value = in32(PMC_O2S_CTRL_REG0B); + pocr0b.fields.o2s_out_count2 = 0; + pocr0b.fields.o2s_in_delay2 = 0; + pocr0b.fields.o2s_in_count2 = 32; + out32(PMC_O2S_CTRL_REG0B, pocr0b.value); + + pocr1.value = in32(PMC_O2S_CTRL_REG1); + pocr1.fields.o2s_bridge_enable = 1; + pocr1.fields.o2s_cpol = 0; + pocr1.fields.o2s_cpha = 0; + pocr1.fields.o2s_clock_divider = 29; // 10 MHz(?) + pocr1.fields.o2s_nr_of_frames = 1; // 2 frames + out32(PMC_O2S_CTRL_REG1, pocr1.value); + + return 0; + +} + +/// similar to o2s_intialize, but for the automated spivid fsm +/// \param vrm_select A 3-bit vector of VRM selected for the operation. +/// +/// NOTE: The spivid is normally initialized by Host Boot +/// +/// \retval 0 Success +int +spivid_initialize(int vrm_select) +{ + pmc_spiv_ctrl_reg0a_t pocr0a; + pmc_spiv_ctrl_reg0b_t pocr0b; + pmc_spiv_ctrl_reg1_t pocr1; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF((vrm_select <= 0) || + (vrm_select > 0x7), + VRM_INVALID_ARGUMENT_INIT); + } + + + pocr0a.value = in32(PMC_SPIV_CTRL_REG0A); + pocr0a.fields.spivid_frame_size = 32; + pocr0a.fields.spivid_out_count1 = 32; + pocr0a.fields.spivid_in_delay1 = 0x3F; + pocr0a.fields.spivid_in_count1 = 0; + out32(PMC_SPIV_CTRL_REG0A, pocr0a.value); + + pocr0b.value = in32(PMC_SPIV_CTRL_REG0B); + pocr0b.fields.spivid_out_count2 = 0; + pocr0b.fields.spivid_in_delay2 = 0; + pocr0b.fields.spivid_in_count2 = 32; + out32(PMC_SPIV_CTRL_REG0B, pocr0b.value); + + pocr1.value = in32(PMC_SPIV_CTRL_REG1); + pocr1.fields.spivid_fsm_enable = 1; + pocr1.fields.spivid_cpol = 0; + pocr1.fields.spivid_cpha = 0; + pocr1.fields.spivid_clock_divider = 29; // 10 MHz(?) + pocr1.fields.spivid_port_enable = vrm_select; + out32(PMC_SPIV_CTRL_REG1, pocr1.value); + + return 0; + +} + + +// Start an O2S transaction and poll for completion. Optionally return the +// input data. + +static void +o2s_start_poll(uint64_t out, uint64_t *in) +{ + + out32(PMC_O2S_WDATA_REG, out >> 32); + + if (!ssx_irq_status_get(PGP_IRQ_OCI2SPIVID_ONGOING)) { + ssx_irq_enable(PGP_IRQ_OCI2SPIVID_ONGOING); + ssx_semaphore_pend(&o2s_protocol_semaphore, SSX_WAIT_FOREVER); + } + + + if (in != 0) { + *in = in32(PMC_O2S_RDATA_REG); + } +} + + +/// Write a voltage using the O2S bridge +/// +/// \param vrm_select A 3-bit vector of VRM selected for the operation. +/// +/// \param vdd_vid The VRM-11 VID code for Vdd. +/// +/// \param vcs_offset The signed offset (Vdiff) equal to Vcs - Vdd expressed +/// in VRM-11 VID units. +/// +/// \param phases The number of phases enabled. +/// +/// This is a polling CPU procedure that writes a new voltage to a set of one +/// or more VRM then does a status read to make sure it succeeded. +/// +/// \retval 0 Success +/// +/// \retval -VRM_INVALID_ARGUMENT_VWRITE One of the arguments was invalid in +/// some way. +/// +/// \retval -O2S_BUSY_VRM_VOLTAGE_WRITE The O2S bridge is currently busy +/// +/// \retval -O2S_READ_NOT_READY A 'read not ready' condition occurred on the +/// status read. +/// +/// \retval -O2S_WRITE_NOT_VALID The voltage write was invalid +/// +/// \retval -O2S_ECC_ERROR An ECC error occurred +/// +/// \todo We need to understand what the firmware is expected to do when the +/// 'read not ready' or other error responses come back. Here they will +/// likely panic. + +int +vrm_voltage_write(int vrm_select, + uint8_t vdd_vid, + int8_t vcs_offset, + int phases) +{ + int i, port; + uint64_t result; + pmc_o2s_ctrl_reg1_t pocr; + pmc_o2s_status_reg_t posr; + vrm_write_transaction_t vwt; + vrm_write_resp_t vwr; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF((vrm_select <= 0) || + (vrm_select > 0x7) || + (phases < 0) || + (phases > 0xf), + VRM_INVALID_ARGUMENT_VWRITE); + } + + // Check for O2S busy + + posr.value = in32(PMC_O2S_STATUS_REG); + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(posr.fields.o2s_ongoing, O2S_BUSY_VRM_VOLTAGE_WRITE); + } + + // Use O2S to set voltage and read status, one port at a time. + + for (i = 0; i < SPIVRM_NPORTS; i++) { + + port = vrm_select & SPIVRM_PORT(i); + if (port != 0) { + + // Set the (singular) O2S port + + pocr.value = in32(PMC_O2S_CTRL_REG1); + pocr.fields.o2s_port_enable = port; + out32(PMC_O2S_CTRL_REG1, pocr.value); + + // Create and initiate a voltage write command + + vwt.value = 0; + vwt.fields.command = VRM_WRITE_VOLTAGE; + vwt.fields.vdd_vid = vdd_vid; + vwt.fields.vcs_offset = vcs_offset; + vwt.fields.phase_enable = phases; + + + o2s_start_poll(vwt.value, &result); + // Check the status + vwr.value = result << 32; + + // results are duplicated 3x, using first byte for checking + + SSX_ERROR_IF(vwr.fields.write_status0 == 0x00, O2S_READ_NOT_READY); + SSX_ERROR_IF(vwr.fields.write_status0 == 0x55, O2S_WRITE_ECC_ERROR); + SSX_ERROR_IF(vwr.fields.write_status0 != 0xAA, O2S_WRITE_NOT_VALID); + } + } + return 0; +} + +/// Read VRM state using the O2S bridge +/// +/// \param vrm_select A 3-bit vector of VRM selected for the operation. +/// This procedure only allows 1 VRM to be selected. +/// +/// \param vrail A 4-bit value for selecting a voltage rail +/// +/// \param[out] o_vid The resulting 8-bit VRM-11 encoded voltage ID +/// +/// +/// \retval 0 Success +/// +/// \retval -VRM_INVALID_ARGUMENT_SREAD One of the arguments was invalid in some +/// way. +/// +/// \retval -O2S_BUSY_VRM_READ_STATE The O2S bridge is currently busy +/// + +int +vrm_read_state(int vrm_select, + int *mnp1, + int *mn, + int *vfan, + int *vovertmp) +{ + int i, port; + uint64_t result; + pmc_o2s_status_reg_t posr; + pmc_o2s_ctrl_reg1_t pocr; + vrm_read_state_t vrs; + vrm_read_state_resp_t vrsr; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF((!((vrm_select == SPIVRM_PORT(0)) || + (vrm_select == SPIVRM_PORT(1)) || + (vrm_select == SPIVRM_PORT(2)))), + VRM_INVALID_ARGUMENT_SREAD); + } + + + // Check for O2S busy + + posr.value = in32(PMC_O2S_STATUS_REG); + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(posr.fields.o2s_ongoing, O2S_BUSY_VRM_READ_STATE); + } + + // Use O2S to read voltage for selected rail. + + for (i = 0; i < SPIVRM_NPORTS; i++) { + + port = vrm_select & SPIVRM_PORT(i); + if (port != 0) { + + // Set the (singular) O2S port + + pocr.value = in32(PMC_O2S_CTRL_REG1); + pocr.fields.o2s_port_enable = port; + out32(PMC_O2S_CTRL_REG1, pocr.value); + + // Create and initiate a voltage read command + + vrs.value = 0; + vrs.fields.command = VRM_READ_STATE; + + + o2s_start_poll(vrs.value, &result); + // Check the status + vrsr.value = result << 32; + + // results are duplicated 3x, returning first byte + *mnp1 = vrsr.fields.minus_nplus1_0; + *mn = vrsr.fields.minus_n0; + *vfan = vrsr.fields.vrm_fan0; + *vovertmp = vrsr.fields.vrm_overtemp0; + + } + } + return 0; +} + + +/// Read a voltage using the O2S bridge +/// +/// \param vrm_select A 3-bit vector of VRM selected for the operation. +/// This procedure only allows 1 VRM to be selected. +/// +/// \param vrail A 4-bit value for selecting a voltage rail +/// +/// \param[out] o_vid The resulting 8-bit VRM-11 encoded voltage ID +/// +/// +/// \retval 0 Success +/// +/// \retval -VRM_INVALID_ARGUMENT_VREAD One of the arguments was invalid in some +/// way. +/// +/// \retval -O2S_BUSY_VRM_VOLTAGE_READ The O2S bridge is currently busy +/// + +int +vrm_voltage_read(int vrm_select, + uint8_t vrail, + uint8_t *o_vid) +{ + int i, port; + uint64_t result; + pmc_o2s_status_reg_t posr; + pmc_o2s_ctrl_reg1_t pocr; + vrm_read_voltage_t vrv; + vrm_read_voltage_resp_t vrvr; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF((!((vrm_select == SPIVRM_PORT(0)) || + (vrm_select == SPIVRM_PORT(1)) || + (vrm_select == SPIVRM_PORT(2)))) || + (vrail > (SPIVRM_NRAILS - 1)), + VRM_INVALID_ARGUMENT_VREAD); + } + + + // Check for O2S busy + + posr.value = in32(PMC_O2S_STATUS_REG); + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(posr.fields.o2s_ongoing, O2S_BUSY_VRM_VOLTAGE_READ); + } + + // Use O2S to read voltage for selected rail. + + for (i = 0; i < SPIVRM_NPORTS; i++) { + + port = vrm_select & SPIVRM_PORT(i); + if (port != 0) { + + // Set the (singular) O2S port + + pocr.value = in32(PMC_O2S_CTRL_REG1); + pocr.fields.o2s_port_enable = port; + out32(PMC_O2S_CTRL_REG1, pocr.value); + + // Create and initiate a voltage read command + + vrv.value = 0; + vrv.fields.command = VRM_READ_VOLTAGE; + vrv.fields.rail = vrail; + + + o2s_start_poll(vrv.value, &result); + // Check the status + vrvr.value = result << 32; + + // results are duplicated 3x, returning first byte + *o_vid = vrvr.fields.vid0; + } + } + return 0; +} + + + + |