summaryrefslogtreecommitdiffstats
path: root/src/lib/pmc_dcm.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/pmc_dcm.c')
-rwxr-xr-xsrc/lib/pmc_dcm.c425
1 files changed, 425 insertions, 0 deletions
diff --git a/src/lib/pmc_dcm.c b/src/lib/pmc_dcm.c
new file mode 100755
index 0000000..a0f7d82
--- /dev/null
+++ b/src/lib/pmc_dcm.c
@@ -0,0 +1,425 @@
+// $Id: pmc_dcm.c,v 1.2 2014/02/03 01:30:25 daviddu Exp $
+// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/pmc_dcm.c,v $
+//-----------------------------------------------------------------------------
+// *! (C) Copyright International Business Machines Corp. 2013
+// *! All Rights Reserved -- Property of IBM
+// *! *** IBM Confidential ***
+//-----------------------------------------------------------------------------
+
+/// \file pmc_dcm.c
+/// \brief Genertic PMC Interhchip Communication Mechanism
+
+#include "ssx.h"
+#include "pmc_dcm.h"
+
+////////////////////////////////////////////////////////////////////////////
+// Low-level PMC-DCM Interfaces
+////////////////////////////////////////////////////////////////////////////
+
+/// Non-blocking transmission of a packet over the PMC-DCM interface
+///
+/// Note: Locking/synchronization of the PMC-DCM interface is the
+/// responsibility of the application.
+///
+/// \param hwPacket A PmcDcmPacket structure to be sent via PMC interchip link
+/// This argument is provided by the caller and passed in as reference.
+///
+/// \code
+/// hwPacket:
+///
+/// cmd_code | cmd_ext | payload 0 | payload 1 | ECC
+/// [0:3] | [4:7] | [8:15] | [16:23] | [24:31]
+///
+/// cmd_code:
+/// PMC_IC_GPA_CC | 1 | 0b0001 | Global PState Actual | Master to Slave
+/// PMC_IC_GPA_ACK_CC | 2 | 0b0010 | Global PState Ack | Slave to Master
+/// PMC_IC_GAR_CC | 3 | 0b0011 | Global Actual Request | Slave to Master
+/// PMC_IC_PING_CC | 4 | 0b0100 | Ping | Master to Slave
+/// PMC_IC_PING_ACK_CC | 6 | 0b0110 | Ping Acknowledge | Slave to Master
+/// PMC_IC_MSG_CC | 8 | 0b1000 | Message | Bidirectional
+/// PMC_IC_MSG_NACK_CC | 10 | 0b1010 | Message NACK | Bidirectional
+/// PMC_IC_MSG_ACK_CC | 11 | 0b1011 | Message ACK | Bidirectional
+/// PMC_IC_ERROR_CC | 12 | 0b1111 | Error | Slave to Master
+/// \endcode
+///
+/// This API sends command to another chip through the PMC interchip wire
+/// in DCM setup. The message will be sent out by writing the packet value
+/// to the regisiter: PMC_INTCHP_MSG_WDATA
+///
+/// It also checks the interchip_ga_ongoing and interchip_msg_send_ongoing
+/// bits of PMC_INTCHP_STATUS_REG to detect if the channel is free
+/// If the channel is busy, the function will exit with returning
+/// an error code \a PMC_DCM_OUTSTANDING_TRANSFER
+///
+/// Prerequisite: The enable_interchip_interface bit of PMC_MODE_REG
+/// must be set to enable the PMC interchip transfer
+/// Also, the hardware must be in DCM setup
+///
+/// Note: This API can be used to send any valid command over the link
+/// however, both command code and corresponding direction of transfer
+/// will be checked to ensure correctness of the protocol. Upon
+/// attempt to send an invalid command or an unexpected direction
+/// of transfering certain command will cause this API to abort
+/// which indicates a HW/FW bug
+///
+/// \retval PMC_DCM_SUCCESS
+///
+/// \retval PMC_DCM_ARG_NULL_OBJECT_SEND
+///
+/// \retval PMC_DCM_INTCHP_DISABLED_SEND
+///
+/// \retval PMC_DCM_OUTSTANDING_TRANSFER
+///
+/// \retval PMC_DCM_INVALID_COMMAND_CODE
+///
+
+int
+pmc_dcm_send(PmcDcmPacket* hwPacket)
+{
+ int rc = PMC_DCM_SUCCESS;
+
+ do {
+
+ // check if reference packet is null
+ SSX_ERROR_IF_CHECK_API(
+ (hwPacket == 0),
+ PMC_DCM_ARG_NULL_OBJECT_SEND);
+
+ // check if interchip transfer is enabled on this chip
+ SSX_ERROR_IF_CHECK_API(
+ (pmc_dcm_if_interchip_interface_enabled() == 0),
+ PMC_DCM_INTCHP_DISABLED_SEND);
+
+ //check if command code is valid and direction of transfer is valid
+ rc = pmc_dcm_check_ic_command((int)hwPacket->fields.cmd_code);
+ if( rc ) break;
+
+ // check if the interchip channel is busy
+ if( pmc_dcm_if_channel_busy() ) {
+ rc = PMC_DCM_OUTSTANDING_TRANSFER;
+ break;
+ }
+
+ // send out the command
+ _pmc_dcm_send(&hwPacket->value);
+
+ } while (0);
+
+ return rc;
+}
+
+/// Non-blocking reception of a packet from the PMC-DCM interface
+///
+/// Note: Locking/synchronization of the PMC-DCM interface is the
+/// responsibility of the application.
+///
+/// \param hwPacket A PmcDcmPacket structure passed by the caller
+/// as reference to receive the message sent from PMC interchip link
+///
+/// This API receives the message from the PMC interchip wire
+/// by reading the register: PMC_INTCHP_MSG_RDATA
+///
+/// It checks the interchip_msg_recv_detected bit of
+/// PMC_INTCHP_STATUS_REG to know if there is a new message
+/// If no new message is detected, the receive function will
+/// exit with returning an error code \a PMC_DCM_RECEIVE_NOT_DETECTRD
+///
+/// Prerequisite: The enable_interchip_interface bit of PMC_MODE_REG
+/// must be set to enable the PMC interchip transfer
+/// Also, the hardware must be in DCM setup
+///
+/// Note: only MSG type message or cmd code should be received
+/// otherwise function aborts and indicates a hardware bug
+///
+/// \retval PMC_DCM_SUCCESS
+///
+/// \retval PMC_DCM_ARG_NULL_OBJECT_RECV
+///
+/// \retval PMC_DCM_INTCHP_DISABLED_RECV
+///
+/// \retval PMC_DCM_RECEIVE_NOT_DETECTRD
+///
+/// \retval PMC_DCM_RECEIVE_NOT_MSG_TYPE
+///
+
+int
+pmc_dcm_receive(PmcDcmPacket* hwPacket)
+{
+ int rc = PMC_DCM_SUCCESS;
+
+ do {
+
+ //check if reference packet is null
+ SSX_ERROR_IF_CHECK_API(
+ (hwPacket == 0),
+ PMC_DCM_ARG_NULL_OBJECT_RECV);
+
+ // check if interchip transfer is enabled on this chip
+ SSX_ERROR_IF_CHECK_API(
+ (!pmc_dcm_if_interchip_interface_enabled()),
+ PMC_DCM_INTCHP_DISABLED_RECV);
+
+ // check if there is a new incoming message
+ if( !pmc_dcm_if_new_message() ) {
+ rc = PMC_DCM_RECEIVE_NOT_DETECTED;
+ break;
+ }
+
+ // receive the new message
+ _pmc_dcm_receive(&hwPacket->value);
+
+ // check if the command is MSG type
+ SSX_ERROR_IF_CHECK_API(
+ (hwPacket->fields.cmd_code != PMC_IC_MSG_CC),
+ PMC_DCM_RECEIVE_NOT_MSG_TYPE);
+
+ } while (0);
+
+ return rc;
+}
+
+
+
+/// Internal API : Send data without error checking
+///
+/// \param value 32 bits data to be sent
+///
+/// This API send the interchip data by writing register:
+/// PMC_INTCHP_MSG_WDATA
+///
+/// \retval NONE
+///
+
+void
+_pmc_dcm_send(uint32_t *value)
+{
+ out32(PMC_INTCHP_MSG_WDATA, *value);
+}
+
+
+/// Internal API : Receive data without error checking
+///
+/// \param value 32 bits data to be received
+///
+/// This API receive the interchip data by reading register:
+/// PMC_INTCHP_MSG_RDATA
+///
+/// \retval NONE
+///
+
+void
+_pmc_dcm_receive(uint32_t *value)
+{
+ *value = in32(PMC_INTCHP_MSG_RDATA);
+}
+
+/// This API tells if the given command is a valid pmc interchip command
+/// and if the command is given by the designated source
+///
+/// \param cmd_code the command code
+///
+/// \param rc the return code back to caller
+///
+/// \code
+/// cmd_code:
+///
+/// PMC_IC_GPA_CC | 1 | 0b0001 | Global PState Actual | Master to Slave
+/// PMC_IC_GPA_ACK_CC | 2 | 0b0010 | Global PState Ack | Slave to Master
+/// PMC_IC_GAR_CC | 3 | 0b0011 | Global Actual Request | Slave to Master
+/// PMC_IC_PING_CC | 4 | 0b0100 | Ping | Master to Slave
+/// PMC_IC_PING_ACK_CC | 6 | 0b0110 | Ping Acknowledge | Slave to Master
+/// PMC_IC_MSG_CC | 8 | 0b1000 | Message | Bidirectional
+/// PMC_IC_MSG_NACK_CC | 10 | 0b1010 | Message NACK | Bidirectional
+/// PMC_IC_MSG_ACK_CC | 11 | 0b1011 | Message ACK | Bidirectional
+/// PMC_IC_ERROR_CC | 12 | 0b1111 | Error | Slave to Master
+/// \endcode
+///
+/// \retval PMC_DCM_INTCHP_CMD_ONLY_MTOS
+///
+/// \retval PMC_DCM_INTCHP_CMD_ONLY_STOM
+///
+/// \retval PMC_DCM_INVALID_COMMAND_CODE
+///
+
+int
+pmc_dcm_check_ic_command(int cmd_code)
+{
+ //note:certain command can only be transferred from master to slave
+ // or from slave to master or bidirectional.
+ if( cmd_code == PMC_IC_GPA_CC ||
+ cmd_code == PMC_IC_PING_CC ) {
+ //those commands can only be sent from master to slave
+ SSX_ERROR_IF_CHECK_API(
+ (!pmc_dcm_if_dcm_master()),
+ PMC_DCM_INTCHP_CMD_ONLY_MTOS);
+ } else if( cmd_code == PMC_IC_GPA_ACK_CC ||
+ cmd_code == PMC_IC_GAR_CC ||
+ cmd_code == PMC_IC_PING_ACK_CC ||
+ cmd_code == PMC_IC_ERROR_CC ) {
+ //those commands can only be sent from slave to master
+ SSX_ERROR_IF_CHECK_API(
+ (pmc_dcm_if_dcm_master()),
+ PMC_DCM_INTCHP_CMD_ONLY_STOM);
+ } else if( !(cmd_code == PMC_IC_MSG_CC ||
+ cmd_code == PMC_IC_MSG_NACK_CC ||
+ cmd_code == PMC_IC_MSG_ACK_CC) ) {
+ //those commands are bidirectional
+ //none of above, invalid command
+ SSX_ERROR_IF_CHECK_API(
+ 0,
+ PMC_DCM_INVALID_COMMAND_CODE);
+ }
+ return PMC_DCM_SUCCESS;
+}
+
+
+
+/// This API tells if the current chip is the DCM master or slave
+///
+/// \param NONE
+///
+/// The DCM master/slave is configured as the interchip_mode bit in register:
+/// PMC_MODE_REG
+///
+/// \retval 1 Master
+///
+/// \retval 0 Slave
+///
+
+int
+pmc_dcm_if_dcm_master()
+{
+ 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;
+}
+
+/// This API sets the current chip to be the DCM master or slave
+///
+/// \param master if 1 then set to master otherwise slave
+///
+/// The DCM master/slave is configured as the interchip_mode bit in register:
+/// PMC_MODE_REG
+///
+/// \retval NONE
+///
+
+void
+pmc_dcm_set_interchip_mode(int master)
+{
+ pmc_mode_reg_t pmc_mode_reg;
+ pmc_mode_reg.value = in32(PMC_MODE_REG);
+ pmc_mode_reg.fields.enable_interchip_interface = 1;
+ if( master == 0 )
+ pmc_mode_reg.fields.interchip_mode = 0;
+ else
+ pmc_mode_reg.fields.interchip_mode = 1;
+ out32(PMC_MODE_REG, pmc_mode_reg.value);
+}
+
+/// This API tells if the current chip is enabled with interchip interface
+///
+/// \param NONE
+///
+/// The DCM master/slave is configured as the enable_interchip_interface bit
+/// in register: PMC_MODE_REG
+///
+/// Note: set this bit is required for any interchip communication
+///
+/// \retval 1 Enabled
+///
+/// \retval 0 Disabled
+///
+
+int
+pmc_dcm_if_interchip_interface_enabled()
+{
+ pmc_mode_reg_t pmc_mode_reg;
+ pmc_mode_reg.value = in32(PMC_MODE_REG);
+ return pmc_mode_reg.fields.enable_interchip_interface;
+}
+
+/// This API sets the current chip to be enabled with interchip interface
+///
+/// \param enable if 1 then interface enabled otherwise disabled
+///
+/// The DCM master/slave is configured as the enable_interchip_interface bit
+/// in register: PMC_MODE_REG
+///
+/// \retval NONE
+///
+
+void
+pmc_dcm_set_interchip_interface(int enable)
+{
+ pmc_mode_reg_t pmc_mode_reg;
+ pmc_mode_reg.value = in32(PMC_MODE_REG);
+ if( enable == 0 )
+ pmc_mode_reg.fields.enable_interchip_interface = 0;
+ else
+ pmc_mode_reg.fields.enable_interchip_interface = 1;
+ out32(PMC_MODE_REG, pmc_mode_reg.value);
+}
+
+
+/// This API tells if the interchip channel is busy for outgoing communication
+///
+/// \param NONE
+///
+/// depends on bits: interchip_ga_ongoing and interchip_msg_send_ongoing
+/// in register: PMC_INTCHP_STATUS_REG
+///
+/// \retval 1 Busy
+///
+/// \retval 0 Free
+///
+
+int
+pmc_dcm_if_channel_busy()
+{
+ pmc_intchp_status_reg_t pmc_intchp_status_reg;
+ pmc_intchp_status_reg.value = in32(PMC_INTCHP_STATUS_REG);
+ return pmc_intchp_status_reg.fields.interchip_msg_send_ongoing;
+ //return (pmc_intchp_status_reg.fields.interchip_ga_ongoing ||
+ // pmc_intchp_status_reg.fields.interchip_msg_send_ongoing);
+}
+
+/// This API tells if there is a new message arrived from the interchip wire
+///
+/// \param NONE
+///
+/// if bit interchip_msg_recv_detected in register: PMC_INTCHP_STATUS_REG
+/// is set then new message otherwise none
+///
+/// \retval 1 New Message
+///
+/// \retval 0 NO New Message
+///
+
+int
+pmc_dcm_if_new_message()
+{
+ pmc_intchp_status_reg_t pmc_intchp_status_reg;
+ pmc_intchp_status_reg.value = in32(PMC_INTCHP_STATUS_REG);
+ return pmc_intchp_status_reg.fields.interchip_msg_recv_detected;
+}
+
+/// This API initializes the DCM setup
+///
+/// \param master_or_slave configure the current chip to be master or slave
+///
+/// for current chip
+/// 1) enable interchip interface
+/// 2) configure to be DCM master or slave
+///
+/// Note: one chip has to be master and one chip has to be slave
+///
+/// \retval NONE
+///
+
+void
+pmc_dcm_init(int master_or_slave)
+{
+ pmc_dcm_set_interchip_mode(master_or_slave);
+}
OpenPOWER on IntegriCloud