Exemple #1
0
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;
}
Exemple #2
0
// 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;
}