int _centaur_configuration_create(int i_bar, int i_slave, int i_setup) { CentaurConfiguration config; int i, designatedSync, diffInit; int64_t rc; /* Must be copied to global struct. */ mcfgpr_t mcfgpr; mcifir_t mcifir; mcsmode0_t mcsmode0; pba_slvctln_t slvctl; uint64_t diffMask, addrAccum, bar, mask, base; PoreFlex request; // Start by clearing the local structure and setting the error flag. memset(&config, 0, sizeof(config)); config.configRc = CENTAUR_NOT_CONFIGURED; designatedSync = -1; do { // Basic consistency checks if ((i_bar < 0) || (i_bar >= PBA_BARS) || (i_slave < 0) || (i_slave >= PBA_SLAVES)) { rc = CENTAUR_INVALID_ARGUMENT; break; } // Create the setups for the GPE procedures. The 'dataParms' are the // setup for accessing the Centaur sensor cache. The 'scomParms' are // the setup for accessing Centaur SCOMs. rc = gpe_pba_parms_create(&(config.dataParms), PBA_SLAVE_PORE_GPE, PBA_WRITE_TTYPE_CI_PR_W, PBA_WRITE_TTYPE_DC, PBA_READ_TTYPE_CL_RD_NC); if (rc) { rc = CENTAUR_DATA_SETUP_ERROR; break; } rc = gpe_pba_parms_create(&(config.scomParms), PBA_SLAVE_PORE_GPE, PBA_WRITE_TTYPE_CI_PR_W, PBA_WRITE_TTYPE_DC, PBA_READ_TTYPE_CI_PR_RD); if (rc) { rc = CENTAUR_SCOM_SETUP_ERROR; break; } // Go into each MCS on the chip, and for all enabled MCS get a couple // of SCOMs and check configuration items for correctness. If any of // the Centaur are configured, exactly one of the MCS must be // designated to receive the SYNC commands. // Note that the code uniformly treats SCOM failures of the MCFGPR // registers as an unconfigured Centaur. This works both for Murano, // which only defines the final 4 MCS, as well as for our VBU models // where some of the "valid" MCS are not in the simulation models. for (i = 0; i < PGP_NCENTAUR; i++) { // SW273928: New function added for FW820, when centaur has channel // checkstop, we consider centaur is not usable so treat it as // deconfigured. Note that the current implementation assumes when // centaur is dead, its mcs is also dead, which is wrong. However, // it only concerns when MCS happens to be the SYNC master because // the gpe procedure only tries to talk to centaurs regardless what // MCS status it knows about. In this particular case, // the procedure will turn on SYNC on a different MCS with // valid centaur. According to Eric Retter, it would be ok for // HW to have more MCS turned on as SYNC master as long as FW // only send SYNC command to one of them. rc = _getscom(MCS_ADDRESS(MCIFIR, i), &(mcifir.value), SCOM_TIMEOUT); if (rc) { rc = 0; config.baseAddress[i] = 0; continue; } if (mcifir.fields.channel_fail_signal_active) continue; rc = _getscom(MCS_ADDRESS(MCFGPR, i), &(mcfgpr.value), SCOM_TIMEOUT); if (rc) { rc = 0; config.baseAddress[i] = 0; continue; } if (!mcfgpr.fields.mcfgprq_valid) continue; rc = _getscom(MCS_ADDRESS(MCSMODE0, i), &(mcsmode0.value), SCOM_TIMEOUT); if (rc) { PRINTD("Unexpected rc = 0x%08x SCOMing MCSMODE0(%d)\n", (uint32_t)rc, i); rc = CENTAUR_MCSMODE0_SCOM_FAILURE; break; } // We require that the MCFGRP_19_IS_HO_BIT be set in the mode // register. We do not support the option of this bit not being // set, and all of our procedures will set bit 19 of the PowerBus // address to indicate that OCC is making the access. if (!mcsmode0.fields.mcfgrp_19_is_ho_bit) { PRINTD("MCSMODE0(%d).mcfgrp_19_is_ho_bit == 0\n", i); rc = CENTAUR_MCSMODE0_19_FAILURE; break; } // The 14-bit base-address is moved to begin at bit 14 in the // 64-bit PowerBus address. The low-order bit of this address (bit // 19 mentioned above which is bit 27 as an address bit) must be 0 // - otherwise there is confusion over who's controlling this // bit. config.baseAddress[i] = ((uint64_t)(mcfgpr.fields.mcfgprq_base_address)) << (64 - 14 - 14); if (config.baseAddress[i] & 0x0000001000000000ull) { PRINTD("Centaur base address %d has bit 27 set\n", i); rc = CENTAUR_ADDRESS_27_FAILURE; break; } // If this MCS is configured to be the designated SYNC unit, it // must be the only one. if (mcsmode0.fields.enable_centaur_sync) { if (designatedSync > 0) { PRINTD("Both MCS %d and %d are designated " "for Centaur Sync\n", designatedSync, i); rc = CENTAUR_MULTIPLE_DESIGNATED_SYNC; break; } else { designatedSync = i; } } // Add the Centaur to the configuration config.config |= (CHIP_CONFIG_MCS(i) | CHIP_CONFIG_CENTAUR(i)); } if (rc) break; // If Centaur are configured, make sure at least one of the MCS will // handle the SYNC. If so, convert its base address into an address // for issuing SYNC commands by setting bits 27 (OCC) 28 and 29 // (Sync), then insert this address into the extended address field of // a PBA slave control register image. gsc_scom_centaur() then merges // this extended address into the PBA slave control register (which // has been set up for Centaur SCOM) to do the SYNC. // In the override mode (i_setup > 1) we tag the first valid MCS // to recieve the sync if the firmware has not set it up correctly. if (config.config) { if (designatedSync < 0) { if (i_setup <= 1) { PRINTD("No MCS is designated for Centaur SYNC\n"); rc = CENTAUR_NO_DESIGNATED_SYNC; break; } else { designatedSync = cntlz32(left_justify_mcs_config(config.config)); rc = _getscom(MCS_ADDRESS(MCSMODE0, designatedSync), &(mcsmode0.value), SCOM_TIMEOUT); if (rc) { PRINTD("Unexpected rc = 0x%08x SCOMing MCSMODE0(%d)\n", (uint32_t)rc, designatedSync); rc = CENTAUR_MCSMODE0_SCOM_FAILURE; break; } mcsmode0.fields.enable_centaur_sync = 1; rc = _putscom(MCS_ADDRESS(MCSMODE0, designatedSync), mcsmode0.value, SCOM_TIMEOUT); if (rc) { PRINTD("Unexpected rc = 0x%08x SCOMing MCSMODE0(%d)\n", (uint32_t)rc, designatedSync); rc = CENTAUR_MCSMODE0_SCOM_FAILURE; break; } } } base = config.baseAddress[designatedSync] | 0x0000001c00000000ull; slvctl.value = 0; slvctl.fields.extaddr = (base & 0x000001fff8000000ull) >> 27; config.syncSlaveControl = slvctl.value; } // At this point we have one or more enabled MCS and they pass the // initial configuration sniff test. We can now implement the option // to configure the PBA BAR and BAR MASK correctly to allow access to // these Centaur. We do this by computing the minimum BAR mask that // covers all of the Centaur base addresses. This is done by // accumulating a difference mask of the base addresses and finding // the first set bit in the mask. // // Note that we do the configuration here on demand, but always do the // correctness checking as the next step. if (i_setup && (config.config != 0)) { diffInit = 0; diffMask = 0; /* GCC happiness */ addrAccum = 0; /* GCC happiness */ for (i = 0; i < PGP_NCENTAUR; i++) { if (config.baseAddress[i] != 0) { if (!diffInit) { diffInit = 1; diffMask = 0; addrAccum = config.baseAddress[i]; } else { diffMask |= (config.baseAddress[i] ^ addrAccum); addrAccum |= config.baseAddress[i]; } if (0) { // Debug printk("i:%d baseAddress: 0x%016llx " "diffMask: 0x%016llx, addrAccum: 0x%016llx\n", i, config.baseAddress[i], diffMask, addrAccum); } } } // The mask must cover all differences - and must also have at // least bit 27 set. The mask register contains only the mask. The // BAR is set to the accumulated address outside of the mask. The // BAR also contains a scope field which defaults to 0 (Nodal // Scope) for Centaur inband access. diffMask |= 0x0000001000000000ull; mask = ((1ull << (64 - cntlz64(diffMask))) - 1) & PBA_BARMSKN_MASK_MASK; rc = _putscom(PBA_BARMSKN(i_bar), mask, SCOM_TIMEOUT); if (rc) { PRINTD("Unexpected rc = 0x%08x SCOMing PBA_BARMSKN(%d)\n", (uint32_t)rc, i_bar); rc = CENTAUR_BARMSKN_PUTSCOM_FAILURE; break; } rc = _putscom(PBA_BARN(i_bar), addrAccum & ~mask, SCOM_TIMEOUT); if (rc) { PRINTD("Unexpected rc = 0x%08x SCOMing PBA_BARN(%d)\n", (uint32_t)rc, i_bar); rc = CENTAUR_BARN_PUTSCOM_FAILURE; break; } } // Do an independent check that every Centaur base address // can be generated by the combination of the current BAR and // BAR Mask, along with the initial requirement that the mask must // include at least bits 27:43. if (config.config != 0) { rc = _getscom(PBA_BARN(i_bar), &bar, SCOM_TIMEOUT); if (rc) { PRINTD("Unexpected rc = 0x%08x SCOMing PBA_BARN(%d)\n", (uint32_t)rc, i_bar); rc = CENTAUR_BARN_GETSCOM_FAILURE; break; } rc = _getscom(PBA_BARMSKN(i_bar), &mask, SCOM_TIMEOUT); if (rc) { PRINTD("Unexpected rc = 0x%08x SCOMing PBA_BARMSKN(%d)\n", (uint32_t)rc, i_bar); rc = CENTAUR_BARMSKN_GETSCOM_FAILURE; break; } bar = bar & PBA_BARN_ADDR_MASK; mask = mask & PBA_BARMSKN_MASK_MASK; if ((mask & 0x0000001ffff00000ull) != 0x0000001ffff00000ull) { PRINTD("PBA BAR mask (%d) does not cover bits 27:43\n", i_bar); rc = CENTAUR_MASK_ERROR; break; } for (i = 0; i < PGP_NCENTAUR; i++) { if (config.baseAddress[i] != 0) { if ((config.baseAddress[i] & ~mask) != (bar & ~mask)) { PRINTD("BAR/Mask (%d) error for MCS/Centaur %d\n" " base = 0x%016llx\n" " bar = 0x%016llx\n" " mask = 0x%016llx\n", i_bar, i, config.baseAddress[i], bar, mask); rc = CENTAUR_BAR_MASK_ERROR; break; } } } if (rc) break; } // At this point the structure is initialized well-enough that it can // be used by gpe_scom_centaur(). We run gpe_scom_centaur() to collect // the CFAM ids of the chips. Prior to this we copy our local copy // into the global read-only data structure. (Note that GPE can DMA // under the OCC TLB memory protection.) In order for // gpe_scom_centaur() to run the global configuration must be valid // (configRc == 0) - so we provisionally mark it valid (and will // invalidate it later if errors occur here). // Note however that if no Centaur are present then we're already // done. // It's assumed that this procedure is being run before threads have // started, therefore we must poll for completion of the GPE program. // Assuming no contention for GPE1 this procedure should take a few // microseconds at most to complete. if (0) { // Debug for Simics - only enable MCS 5 config.baseAddress[0] = config.baseAddress[1] = config.baseAddress[2] = config.baseAddress[3] = config.baseAddress[4] = config.baseAddress[6] = config.baseAddress[7] = 0; } config.configRc = 0; memcpy_real(&G_centaurConfiguration, &config, sizeof(config)); if (config.config == 0) break; S_scomList.scom = CENTAUR_DEVICE_ID; S_scomList.commandType = GPE_SCOM_READ_VECTOR; S_scomList.pData = G_centaurConfiguration.deviceId; S_parms.scomList = CAST_POINTER(uint64_t, &S_scomList); S_parms.entries = 1; S_parms.options = 0; pore_flex_create(&request, &G_pore_gpe1_queue, gpe_scom_centaur, (uint32_t)(&S_parms), SSX_MILLISECONDS(10), /* Timeout */ 0, 0, 0); rc = pore_flex_schedule(&request); if (rc) break; while (!async_request_is_idle((AsyncRequest*)(&request))); if (!async_request_completed((AsyncRequest*)(&request)) || (S_parms.rc != 0)) { PRINTD("gpe_scom_centaur() for CENTAUR_DEVICE_ID failed:\n" " Async state = 0x%02x\n" " gpe_scom_centaur() rc = %u\n" " gpe_scom_centaur() errorIndex = %d\n", ((AsyncRequest*)(&request))->state, S_parms.rc, S_parms.errorIndex); rc = CENTAUR_READ_TPC_ID_FAILURE; } if (0) { // Debug slvctl.value = G_gsc_lastSlaveControl; PRINTD("centaur_configuration_create:Debug\n" " Last SCOM (PowerBus) address = 0x%016llx\n" " Last Slave Control = 0x%016llx\n" " Extended Address (positioned) = 0x%016llx\n" " Last OCI Address = 0x%016llx\n", G_gsc_lastScomAddress, G_gsc_lastSlaveControl, (unsigned long long)(slvctl.fields.extaddr) << (64 - 23 - 14), G_gsc_lastOciAddress); } } while (0); // Copy the final RC into the global structure and done. memcpy_real(&(G_centaurConfiguration.configRc), &rc, sizeof(rc)); return rc; }
// Function Specification // // Name: cmdh_mnfg_run_stop_slew // // Description: This function handles the manufacturing command to start // or stop frequency autoslewing. // // End Function Specification uint8_t cmdh_mnfg_run_stop_slew(const cmdh_fsp_cmd_t * i_cmd_ptr, cmdh_fsp_rsp_t * o_rsp_ptr) { uint8_t l_rc = ERRL_RC_SUCCESS; uint16_t l_fmin = 0; uint16_t l_fmax = 0; uint16_t l_step_size = 0; uint16_t l_step_delay = 0; uint32_t l_temp = 0; mnfg_run_stop_slew_cmd_t *l_cmd_ptr = (mnfg_run_stop_slew_cmd_t*) i_cmd_ptr; mnfg_run_stop_slew_rsp_t *l_rsp_ptr = (mnfg_run_stop_slew_rsp_t*) o_rsp_ptr; do { // This command is only supported on Master OCC if (G_occ_role == OCC_SLAVE) { TRAC_ERR("cmdh_mnfg_run_stop_slew: Mnfg command not supported on Slave OCCs!"); break; } // Do some basic input verification if ((l_cmd_ptr->action > MNFG_INTF_SLEW_STOP) || (l_cmd_ptr->step_mode > MNFG_INTF_FULL_SLEW)) { // Invalid values were passed by the user! TRAC_ERR("cmdh_mnfg_run_stop_slew: Invalid values were detected! action[0x%02x] step_mode[0x%02x]", l_cmd_ptr->action, l_cmd_ptr->step_mode); l_rc = ERRL_RC_INVALID_DATA; break; } // Are we stopping the auto-slew function? if (l_cmd_ptr->action == MNFG_INTF_SLEW_STOP) { // Collect the slew count l_rsp_ptr->slew_count = AMEC_MST_CUR_SLEW_COUNT(); // Collect the frequency range used for the auto-slew l_rsp_ptr->fstart = AMEC_MST_CUR_MNFG_FMIN(); l_rsp_ptr->fstop = AMEC_MST_CUR_MNFG_FMAX(); TRAC_INFO("cmdh_mnfg_run_stop_slew: Auto-slewing has been stopped. Count[%u] fstart[%u] fstop[%u]", AMEC_MST_CUR_SLEW_COUNT(), AMEC_MST_CUR_MNFG_FMIN(), AMEC_MST_CUR_MNFG_FMAX()); // Send a signal to RTL to stop auto-slewing AMEC_MST_STOP_AUTO_SLEW(); // We are done break; } // If we made it here, that means we are starting up a slew run // First, determine the Fmax and Fmin for the slew run if (l_cmd_ptr->bottom_mode == OCC_MODE_PWRSAVE) { // If bottom mode is Static Power Save, use the min frequency // available l_fmin = G_sysConfigData.sys_mode_freq.table[OCC_MODE_MIN_FREQUENCY]; } else { l_fmin = G_sysConfigData.sys_mode_freq.table[l_cmd_ptr->bottom_mode]; } l_fmax = G_sysConfigData.sys_mode_freq.table[l_cmd_ptr->high_mode]; // Add the percentages to compute the min/max frequencies l_fmin = l_fmin + (l_fmin * l_cmd_ptr->bottom_percent)/100; l_fmax = l_fmax + (l_fmax * l_cmd_ptr->high_percent)/100; TRAC_INFO("cmdh_mnfg_run_stop_slew: We are about to start auto-slewing function"); TRAC_INFO("cmdh_mnfg_run_stop_slew: bottom_mode[0x%.2X] freq[%u] high_mode[0x%.2X] freq[%u]", l_cmd_ptr->bottom_mode, l_fmin, l_cmd_ptr->high_mode, l_fmax); // Determine the frequency step size and the step delay if (l_cmd_ptr->step_mode == MNFG_INTF_FULL_SLEW) { l_step_size = l_fmax - l_fmin; // Disable step delays if full slew mode has been selected l_step_delay = 0; TRAC_INFO("cmdh_mnfg_run_stop_slew: Enabling full-slew mode with step_size[%u] step_delay[%u]", l_step_size, l_step_delay); } else { l_step_size = (uint16_t)G_mhz_per_pstate; // Translate the step delay to internal OCC ticks l_temp = (l_cmd_ptr->step_delay * 1000) / AMEC_US_PER_TICK; l_step_delay = (uint16_t) l_temp; TRAC_INFO("cmdh_mnfg_run_stop_slew: Enabling single-step mode with step_size[%u] step_delay[%u]", l_step_size, l_step_delay); } // Now, load the values for RTL consumption AMEC_MST_SET_MNFG_FMIN(l_fmin); AMEC_MST_SET_MNFG_FMAX(l_fmax); AMEC_MST_SET_MNFG_FSTEP(l_step_size); AMEC_MST_SET_MNFG_DELAY(l_step_delay); // Reset the slew-counter before we start auto-slewing AMEC_MST_CUR_SLEW_COUNT() = 0; // Wait a little bit for RTL to process above parameters ssx_sleep(SSX_MILLISECONDS(5)); // Send a signal to RTL to start auto-slewing AMEC_MST_START_AUTO_SLEW(); // We are auto-slewing now, populate the response packet l_rsp_ptr->slew_count = 0; l_rsp_ptr->fstart = l_fmin; l_rsp_ptr->fstop = l_fmax; }while(0); // Populate the response data packet G_rsp_status = l_rc; l_rsp_ptr->data_length[0] = 0; l_rsp_ptr->data_length[1] = MNFG_INTF_RUN_STOP_SLEW_RSP_SIZE; return l_rc; }