/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* $Source: src/occ_405/dcom/dcom.c $ */ /* */ /* OpenPOWER OnChipController Project */ /* */ /* Contributors Listed Below - COPYRIGHT 2011,2016 */ /* [+] International Business Machines Corp. */ /* */ /* */ /* Licensed under the Apache License, Version 2.0 (the "License"); */ /* you may not use this file except in compliance with the License. */ /* You may obtain a copy of the License at */ /* */ /* http://www.apache.org/licenses/LICENSE-2.0 */ /* */ /* Unless required by applicable law or agreed to in writing, software */ /* distributed under the License is distributed on an "AS IS" BASIS, */ /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ /* implied. See the License for the specific language governing */ /* permissions and limitations under the License. */ /* */ /* IBM_PROLOG_END_TAG */ #ifndef _DCOM_C #define _DCOM_C //#include #include "ssx.h" #include "occhw_pba.h" #include #include #include #include #include #include #include #include #include #include #include "scom.h" #include "pss_constants.h" // @TODO: move with HW registers? extern uint8_t G_occ_interrupt_type; dcom_timing_t G_dcomTime; DMA_BUFFER( dcom_slv_inbox_t G_dcom_slv_inbox_tx[MAX_OCCS]) = {{0}}; DMA_BUFFER( dcom_slv_outbox_t G_dcom_slv_outbox_tx) = {0}; DMA_BUFFER( dcom_slv_outbox_t G_dcom_slv_outbox_rx[MAX_OCCS]) = {{0}}; DMA_BUFFER( dcom_slv_inbox_t G_dcom_slv_inbox_rx) = {0}; // ========================================================= // Master & Slave // ========================================================= // PBAX Circ Queue buffers (where PBAX will put the data in OCC SRAM, so that OCC // can grab it. PBAX_CQ_READ_BUFFER(G_pbax_queue_rx1_buffer,NUM_ENTRIES_PBAX_QUEUE1); PBAX_CQ_READ_BUFFER(G_pbax_queue_rx0_buffer,NUM_ENTRIES_PBAX_QUEUE0); // Initialize Globals // Indicate that Slave OCC got an inbox from master bool G_slv_inbox_received = FALSE; // Counters to debug Master/Slave communication errors dcom_fail_count_t G_dcomSlvInboxCounter = {0}; uint8_t G_occ_role = OCC_SLAVE; // PBAX ID of this OCC is also its PowerBus ID. Contains ChipId & NodeId. pob_id_t G_pbax_id = {0}; // PBAX 'Target' Structure (Register Abstraction) that has the data needed for // a multicast operation. PbaxTarget G_pbax_multicast_target; // PBAX 'Target' Structure (Register Abstraction) that has the data needed for // a unicast operation from the OCC Slave to the OCC Master. PbaxTarget G_pbax_unicast_target; // Number of occ's that *should* be present uint8_t G_occ_num_present; // Master/slave event flags uint32_t G_master_event_flags = 0; uint32_t G_slave_event_flags = 0; uint32_t G_master_event_flags_ack = 0; uint32_t G_slave_event_flags_ack[MAX_OCCS] = {0}; // Helper function to determine if slave inboxes are valid bool isDcomSlvInboxValid(void) { return (G_dcomSlvInboxCounter.currentFailCount ? FALSE : TRUE); } // Function Specification // // Name: dcom_initialize_roles // // Description: Initialize roles so we know if we are master or slave // // End Function Specification void dcom_initialize_roles(void) { G_occ_role = OCC_SLAVE; // Locals pba_xcfg_t pbax_cfg_reg; // Used as a debug tool to correlate time between OCCs & System Time // getscom_ffdc(OCB_OTBR, &G_dcomTime.tod, NULL); // Commits errors internally G_dcomTime.tod = in64(OCB_OTBR) >> 4; G_dcomTime.base = ssx_timebase_get(); pbax_cfg_reg.value = in64(PBA_XCFG); if(pbax_cfg_reg.fields.rcv_groupid < MAX_NUM_NODES && pbax_cfg_reg.fields.rcv_chipid < MAX_NUM_OCC) { TRAC_IMP("Proc ChipId (%d) NodeId (%d)", pbax_cfg_reg.fields.rcv_chipid, pbax_cfg_reg.fields.rcv_groupid); G_pbax_id.valid = 1; G_pbax_id.node_id = pbax_cfg_reg.fields.rcv_groupid; G_pbax_id.chip_id = pbax_cfg_reg.fields.rcv_chipid; G_pbax_id.module_id = G_pbax_id.chip_id; // Always start as OCC Slave G_occ_role = OCC_SLAVE; rtl_set_run_mask(RTL_FLAG_NOTMSTR); // Set the initial presence mask, and count the number of occ's present G_sysConfigData.is_occ_present |= (0x01 << G_pbax_id.chip_id); G_occ_num_present = __builtin_popcount(G_sysConfigData.is_occ_present); } else // Invalid chip/node ID(s) { TRAC_ERR("Proc ChipId (%d) and/or NodeId (%d) too high: request reset", pbax_cfg_reg.fields.rcv_chipid, pbax_cfg_reg.fields.rcv_groupid); /* @ * @errortype * @moduleid DCOM_MID_INIT_ROLES * @reasoncode INVALID_CONFIG_DATA * @userdata1 PBAXCFG (upper) * @userdata2 PBAXCFG (lower) * @userdata4 ERC_CHIP_IDS_INVALID * @devdesc Failure determining OCC role */ errlHndl_t l_errl = createErrl( DCOM_MID_INIT_ROLES, //ModId INVALID_CONFIG_DATA, //Reasoncode ERC_CHIP_IDS_INVALID, //Extended reasoncode ERRL_SEV_UNRECOVERABLE, //Severity NULL, //Trace Buf DEFAULT_TRACE_SIZE, //Trace Size pbax_cfg_reg.words.high_order, //Userdata1 pbax_cfg_reg.words.low_order //Userdata2 ); // Callout firmware addCalloutToErrl(l_errl, ERRL_CALLOUT_TYPE_COMPONENT_ID, ERRL_COMPONENT_ID_FIRMWARE, ERRL_CALLOUT_PRIORITY_HIGH); //Add processor callout addCalloutToErrl(l_errl, ERRL_CALLOUT_TYPE_HUID, G_sysConfigData.proc_huid, ERRL_CALLOUT_PRIORITY_LOW); G_pbax_id.valid = 0; // Invalid Chip/Node ID CHECKPOINT_FAIL_AND_HALT(l_errl); } // Initialize DCOM Thread Sem ssx_semaphore_create( &G_dcomThreadWakeupSem, // Semaphore 1, // Initial Count 0); // No Max Count } // Function Specification // // Name: dcom_initialize_pbax_queues // // Description: Initialize the PBAX Queues for sending doorbells // // End Function Specification void dcom_initialize_pbax_queues(void) { // SSX return codes int l_rc = 0; do { //disabled pbax send before configuring PBAX pbax_send_disable(); // TODO: With the new design, PBAX node and chip IDs are set by hostboot // Remove these ID parameters from the pbax_configure function? l_rc = pbax_configure(G_occ_role, // master G_pbax_id.node_id, // node id G_pbax_id.chip_id, // chipd id PBAX_CONFIGURE_RCV_GROUP_MASK); // group_mask if(l_rc != 0) { TRAC_ERR("Error configuring the pbax rc[%x]",l_rc); break; } //enabled pbax send does not return errors pbax_send_enable(); if(G_occ_role == OCC_SLAVE) { // create pbax rx queue 1 l_rc = pbax_queue_create( &G_pbax_read_queue[1], //queue ASYNC_ENGINE_PBAX_PUSH1, //engine G_pbax_queue_rx1_buffer, //cq base NUM_ENTRIES_PBAX_QUEUE1, //cq entries PBAX_INTERRUPT_PROTOCOL_AGGRESSIVE //protocol ); if(l_rc != 0) { TRAC_ERR("Error creating pbax queue 1 rc[%x]",l_rc); break; } // create pbax rx queue 0 l_rc = pbax_queue_create( &G_pbax_read_queue[0],//queue ASYNC_ENGINE_PBAX_PUSH0, //engine G_pbax_queue_rx0_buffer, //cq base NUM_ENTRIES_PBAX_QUEUE0, //cq entries PBAX_INTERRUPT_PROTOCOL_AGGRESSIVE //protocol ); if(l_rc != 0) { TRAC_ERR("Error creating pbax queue 0 rc[%x]",l_rc); break; } // enable the read 1 queue l_rc = pbax_queue_enable(&G_pbax_read_queue[1]); if(l_rc != 0) { TRAC_ERR("Error enabling queue 1 rc[%x]",l_rc); break; } } if(G_occ_role == OCC_MASTER) { l_rc = pbax_target_create( &G_pbax_multicast_target, // target, PBAX_BROADCAST, // type PBAX_SYSTEM, // scope TODO 0, // queue G_pbax_id.node_id, // node PBAX_BROADCAST_GROUP); // chip_or_group if(l_rc != 0) { TRAC_ERR("Error creating pbax target for master TX operations SSXrc[%x]",l_rc); break; } } }while(0); if(l_rc) { /* @ * @errortype * @moduleid DCOM_MID_INIT_PBAX_QUEUES * @reasoncode SSX_GENERIC_FAILURE * @userdata1 SSX RC * @userdata4 OCC_NO_EXTENDED_RC * @devdesc Failure initializing the PBAX queues */ errlHndl_t l_errl = createErrl( DCOM_MID_INIT_PBAX_QUEUES, //ModId SSX_GENERIC_FAILURE, //Reasoncode OCC_NO_EXTENDED_RC, //Extended reasoncode ERRL_SEV_UNRECOVERABLE, //Severity NULL, //Trace Buf DEFAULT_TRACE_SIZE, //Trace Size l_rc, //Userdata1 0 //Userdata2 ); CHECKPOINT_FAIL_AND_HALT(l_errl); } } // Function Specification // // Name: dcom_error_check // // Description: keep track of failure counts // // End Function Specification void dcom_error_check( const dcom_error_type_t i_error_type, const bool i_clear_error, const uint32_t i_orc, const uint32_t i_orc_ext) { static uint16_t L_rx_slv_outbox_fail_count = 0; uint16_t l_modId = 0; uint16_t *l_count_ptr = NULL; if ( i_error_type == SLAVE_INBOX ) { l_count_ptr = &G_dcomSlvInboxCounter.currentFailCount; l_modId = DCOM_MID_TASK_RX_SLV_INBOX; } // if the i_error_type == SLAVE_OUTBOX then set the outbox count else { l_count_ptr = &L_rx_slv_outbox_fail_count; l_modId = DCOM_MID_TASK_RX_SLV_OUTBOX; } if ( i_clear_error ) { *l_count_ptr = 0; } else { (*l_count_ptr)++; if ( *l_count_ptr == DCOM_250us_GAP ) { // Trace an imp trace log TRAC_IMP("l_count_ptr[%d], L_outbox[%d], L_inbox[%d]", *l_count_ptr, L_rx_slv_outbox_fail_count, G_dcomSlvInboxCounter.currentFailCount ); } else if ( *l_count_ptr == DCOM_4MS_GAP ) { // Create and commit error log // NOTE: SRC tags are NOT needed here, they are // taken care of by the caller errlHndl_t l_errl = createErrl( l_modId, //ModId i_orc, //Reasoncode i_orc_ext, //Extended reasoncode ERRL_SEV_UNRECOVERABLE, //Severity NULL, //Trace Buf DEFAULT_TRACE_SIZE, //Trace Size *l_count_ptr, //Userdata1 0 //Userdata2 ); // Commit log commitErrl( &l_errl ); // Call request nominal macro to change state REQUEST_NOMINAL(); } else if ( *l_count_ptr == DCOM_1S_GAP ) { // Create and commit error log // NOTE: SRC tags are NOT needed here, they are // taken care of by the caller errlHndl_t l_errl = createErrl( l_modId, //ModId i_orc, //Reasoncode i_orc_ext, //Extended reasoncode ERRL_SEV_UNRECOVERABLE, //Severity NULL, //Trace Buf DEFAULT_TRACE_SIZE, //Trace Size *l_count_ptr, //Userdata1 0 //Userdata2 ); // Commit log // Call request reset macro REQUEST_RESET(l_errl); } } } // Function Specification // // Name: dcom_build_occfw_msg // // Description: Copy data into occ fw msg portion // // End Function Specification void dcom_build_occfw_msg( const dcom_error_type_t i_which_msg ) { if ( i_which_msg == SLAVE_INBOX ) { uint32_t l_slv_idx = 0; // For each occ slave for(; l_slv_idx < MAX_OCCS; l_slv_idx++) { G_dcom_slv_inbox_tx[l_slv_idx].occ_fw_mailbox[0] = G_occ_external_req_state; G_dcom_slv_inbox_tx[l_slv_idx].occ_fw_mailbox[1] = G_occ_external_req_mode; G_dcom_slv_inbox_tx[l_slv_idx].occ_fw_mailbox[2] = G_master_event_flags; G_dcom_slv_inbox_tx[l_slv_idx].occ_fw_mailbox[3] = G_slave_event_flags_ack[l_slv_idx]; G_dcom_slv_inbox_tx[l_slv_idx].occ_fw_mailbox[4] = 0; } } else if ( i_which_msg == SLAVE_OUTBOX ) { G_dcom_slv_outbox_tx.occ_fw_mailbox[0] = CURRENT_STATE(); if(G_sysConfigData.system_type.kvm ) { G_dcom_slv_outbox_tx.occ_fw_mailbox[1] = G_occ_external_req_mode_kvm; } else { G_dcom_slv_outbox_tx.occ_fw_mailbox[1] = CURRENT_MODE(); } G_dcom_slv_outbox_tx.occ_fw_mailbox[2] = G_master_event_flags_ack; G_dcom_slv_outbox_tx.occ_fw_mailbox[3] = G_slave_event_flags; G_dcom_slv_outbox_tx.occ_fw_mailbox[4] = SMGR_validate_get_valid_states(); } } // Function Specification // // Name: task_dcom_parse_occfwmsg // // Description: Purpose of this task is to handle and acknowledge // fw messages passed from Master to Slave and vice versa. // // End Function Specification void task_dcom_parse_occfwmsg(task_t *i_self) { errlHndl_t l_errl = NULL; if(G_occ_role == OCC_MASTER) { // Local slave index counter uint32_t l_slv_idx = 0; // Loop and collect occ data for each slave occ for(; l_slv_idx < MAX_OCCS; l_slv_idx++) { // Verify all slave are in correct state and mode G_dcom_slv_outbox_rx[l_slv_idx].occ_fw_mailbox[0] = CURRENT_STATE(); if(G_sysConfigData.system_type.kvm ) { G_dcom_slv_outbox_rx[l_slv_idx].occ_fw_mailbox[1] = G_occ_external_req_mode_kvm; } else { G_dcom_slv_outbox_rx[l_slv_idx].occ_fw_mailbox[1] = CURRENT_MODE(); } // Acknowledge all slave event flags G_slave_event_flags_ack[l_slv_idx] = G_dcom_slv_outbox_rx[l_slv_idx].occ_fw_mailbox[3]; // Clear master event flags if slave has acknowledged them and the event has cleared G_master_event_flags &= ~G_dcom_slv_outbox_rx[l_slv_idx].occ_fw_mailbox[2]; } }//End master role check // Check if master has changed state and mode and update if changed // so that we can handle it in a thread. if( (G_occ_master_state != G_dcom_slv_inbox_rx.occ_fw_mailbox[0]) || (G_occ_master_mode != G_dcom_slv_inbox_rx.occ_fw_mailbox[1]) ) { if( ! isSafeStateRequested() ) { G_occ_master_state = G_dcom_slv_inbox_rx.occ_fw_mailbox[0]; G_occ_master_mode = G_dcom_slv_inbox_rx.occ_fw_mailbox[1]; ssx_semaphore_post(&G_dcomThreadWakeupSem); } } // If we are master, we don't want to update based on // the data sent to us, because it corrupts the 'golden' data // If we are in standby, we don't want to update because // the data may not have been set up yet, and would be set to zero. if(OCC_MASTER != G_occ_role ) { // Update the system mode frequencies if they have changed int l_mode = 0; bool l_change = FALSE; bool l_all_zero = TRUE; // Check if all values are zero for(l_mode = 0; l_mode New: %d MHz",l_mode, G_sysConfigData.sys_mode_freq.table[l_mode], G_dcom_slv_inbox_rx.sys_mode_freq.table[l_mode]); // Update mode frequency G_sysConfigData.sys_mode_freq.table[l_mode] = G_dcom_slv_inbox_rx.sys_mode_freq.table[l_mode]; l_change = TRUE; } } } if(l_change) { // Update "update count" for debug purposes G_sysConfigData.sys_mode_freq.update_count = G_dcom_slv_inbox_rx.sys_mode_freq.update_count; // Change Data Request Mask to indicate we got this data extern data_cnfg_t * G_data_cnfg; G_data_cnfg->data_mask |= DATA_MASK_FREQ_PRESENT; // Notify AMEC that the frequencies have changed l_errl = AMEC_data_change(DATA_MASK_FREQ_PRESENT); if(l_errl) { // Commit log commitErrl(&l_errl); } } } else { // Clear Data Request Mask and data G_data_cnfg->data_mask &= (~DATA_MASK_FREQ_PRESENT); memset(&G_sysConfigData.sys_mode_freq.table[0], 0, sizeof(G_sysConfigData.sys_mode_freq.table)); } } // Copy mnfg parameters into g_amec structure g_amec->foverride_enable = G_dcom_slv_inbox_rx.foverride_enable; g_amec->foverride = G_dcom_slv_inbox_rx.foverride; // Copy IPS parameters sent by Master OCC g_amec->slv_ips_freq_request = G_dcom_slv_inbox_rx.ips_freq_request; // Copy DPS tunable parameters sent by Master OCC if required if(G_dcom_slv_inbox_rx.tunable_param_overwrite) { AMEC_part_overwrite_dps_parameters(); if(G_dcom_slv_inbox_rx.tunable_param_overwrite == 1) { g_amec->slv_dps_param_overwrite = TRUE; } else { g_amec->slv_dps_param_overwrite = FALSE; } } // Copy soft frequency boundaries sent by Master OCC g_amec->part_config.part_list[0].soft_fmin = G_dcom_slv_inbox_rx.soft_fmin; g_amec->part_config.part_list[0].soft_fmax = G_dcom_slv_inbox_rx.soft_fmax; // acknowledge all masters event flags G_master_event_flags_ack = G_dcom_slv_inbox_rx.occ_fw_mailbox[2]; // clear slave event flags if master has acknowledged them and the event has cleared G_slave_event_flags = (G_slave_event_flags & (~(G_dcom_slv_inbox_rx.occ_fw_mailbox[3]))); } #endif //_DCOM_C