diff options
Diffstat (limited to 'src/usr/xscom/xscom.C')
-rw-r--r-- | src/usr/xscom/xscom.C | 300 |
1 files changed, 270 insertions, 30 deletions
diff --git a/src/usr/xscom/xscom.C b/src/usr/xscom/xscom.C index fb962cb24..179e8dc31 100644 --- a/src/usr/xscom/xscom.C +++ b/src/usr/xscom/xscom.C @@ -32,6 +32,7 @@ #include <sys/mmio.h> #include <sys/task.h> #include <sys/sync.h> +#include <sys/misc.h> #include <string.h> #include <devicefw/driverif.H> #include <trace/interface.H> @@ -40,6 +41,7 @@ #include <targeting/targetservice.H> #include <xscom/xscomreasoncodes.H> #include "xscom.H" +#include <assert.h> // Trace definition trace_desc_t* g_trac_xscom = NULL; @@ -51,6 +53,10 @@ namespace XSCOM // Master processor virtual address uint64_t* g_masterProcVirtAddr = NULL; +// Max chip per node in this system +extern uint8_t getMaxChipsPerNode(); +static uint8_t g_xscomMaxChipsPerNode = getMaxChipsPerNode(); + // Register XSCcom access functions to DD framework DEVICE_REGISTER_ROUTE(DeviceFW::WILDCARD, DeviceFW::XSCOM, @@ -194,6 +200,218 @@ errlHndl_t xscomOpSanityCheck(const DeviceFW::OperationType i_opType, return l_err; } +/** + * @brief Returns maximum processors chip per node + * base on system type + * + * @return uint8_t + */ +uint8_t getMaxChipsPerNode() +{ + uint8_t l_numOfChips = 0; + + ProcessorCoreType l_coreType = cpu_core_type(); + //@todo - Need to verify if this number is correct + // for both Salerno and Venice + switch (l_coreType) + { + case CORE_POWER8_SALERNO: + case CORE_POWER8_VENICE: + case CORE_UNKNOWN: + default: + l_numOfChips = 8; + break; + } + return l_numOfChips; +} + +/** + * @brief Get the virtual address of the input target + * for an XSCOM access. + * + * Logic: + * + * If sentinel: + * If never XSCOM to sentinel + * Calculate virtual addr for sentinel + * Save it to g_masterProcVirtAddr for future XSCOM to sentinel + * Else + * Use virtual addr stored in g_masterProcVirtAddr + * End if + * Else (not sentinel) + * If never XSCOM to this chip: + * If this is a master processor object + * Use virtual addr stored for sentinel (g_masterProcVirtAddr) + * Else + * Call mmio_dev_map() to get virtual addr for this slave proc + * @todo: + * Currently virt addr attribute is not supported, so we + * must call unmap in xscomPerfomOp function once the + * xscom operation is done. + * When virt addr attribute is supported, the code that saves + * virt addr code in this function will be uncommented, + * and the mmio_dev_unmap() call in xscomPerformOp() + * function must be removed. + * End if + * Save virtual addr used to this chip's attribute + * Else + * Use virtual address stored in this chip's attributes. + * End if + * End if + * + * @param[in] i_target XSCom target + * @param[out] o_virtAddr Target's virtual address + * + * @return errlHndl_t + */ +errlHndl_t getTargetVirtualAddress(const TARGETING::Target* i_target, + uint64_t*& o_virtAddr) +{ + errlHndl_t l_err = NULL; + o_virtAddr = NULL; + XSComBase_t l_XSComBaseAddr = 0; + + do + { + // Sentinel + if (i_target == TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL) + { + // If never XSCOM to sentinel before, calculate + // sentinel's virtAddr and store it + + // Use atomic update instructions here to avoid + // race condition between different threads. + // Keep in mind that the mutex used in XSCOM is hardware mutex, + // not a mutex for the whole XSCOM logic. + if (__sync_bool_compare_and_swap(&g_masterProcVirtAddr, + NULL, NULL)) + { + l_XSComBaseAddr = MASTER_PROC_XSCOM_BASE_ADDR; + uint64_t* l_tempVirtAddr = static_cast<uint64_t*> + (mmio_dev_map(reinterpret_cast<void*>(l_XSComBaseAddr), + THIRTYTWO_GB)); + if (!__sync_bool_compare_and_swap(&g_masterProcVirtAddr, + NULL, l_tempVirtAddr)) + { + // If g_masterProcVirtAddr has already been updated by + // another thread, we need to unmap the dev_map we just + // called above. + int rc = 0; + rc = mmio_dev_unmap(reinterpret_cast<void*> + (l_tempVirtAddr)); + if (rc != 0) + { + /*@ + * @errortype + * @moduleid XSCOM_GET_TARGET_VIRT_ADDR + * @reasoncode XSCOM_MMIO_UNMAP_ERR + * @userdata1 Return Code + * @userdata2 Unmap address + * @devdesc mmio_dev_unmap() returns error + */ + l_err = new ERRORLOG::ErrlEntry( + ERRORLOG::ERRL_SEV_UNRECOVERABLE, + XSCOM_GET_TARGET_VIRT_ADDR, + XSCOM_MMIO_UNMAP_ERR, + rc, + reinterpret_cast<uint64_t>(l_tempVirtAddr)); + break; + } + } + } + + // Set virtual address to sentinel's value + o_virtAddr = g_masterProcVirtAddr; + } + + // Non-sentinel + else + { + // @todo: + // We (Nick/Patrick/Thi) agree to review the performance cost of + // map/unmap calls for each xscom to determine if it's justified + // to add virtual address as one of the chip's attributes. + // For now, call map/unmap to get virtual address. + // If virtual address attribute is implemented, call the target + // to get it + // Get the virtual addr value of the chip + // l_virtAddr = i_target->getAttr<TARGETING::<ATTR_VIRTUAL_ADDR>(); + + // If virtual addr value is NULL, need to calculate it + if (o_virtAddr == NULL) + { + // Get the target chip info + TARGETING::XscomChipInfo l_xscomChipInfo = {0}; + l_xscomChipInfo = + i_target->getAttr<TARGETING::ATTR_XSCOM_CHIP_INFO>(); + //@todo + // Save the node id of the master chip in a global as well and + // update it. For Rainer systems the node id of the master chip may + // not be 0 if it is on a second node. + + // Is this the master proc? + TARGETING::Target* l_masterProcTarget = NULL; + TARGETING::TargetService& l_targetService = + TARGETING::targetService(); + l_targetService.masterProcChipTargetHandle( l_masterProcTarget ); + + // Use sentinel's virtual address for Master processor + if (l_masterProcTarget == i_target) + { + // There's no way g_masterProcVirtAddr is NULL + // at this point. For safety + assert(g_masterProcVirtAddr != NULL); + o_virtAddr = g_masterProcVirtAddr; + } + + // Calculate virtual address for slave processors + else + { + // Get system XSCOM base address + // Note: can't call TARGETING code prior to PNOR being + // brought up. + TARGETING::TargetService& l_targetService = + TARGETING::targetService(); + TARGETING::Target* l_pTopLevel = NULL; + (void) l_targetService.getTopLevelTarget(l_pTopLevel); + assert(l_pTopLevel != NULL); + XSComBase_t l_systemBaseAddr = + l_pTopLevel->getAttr<TARGETING::ATTR_XSCOM_BASE_ADDRESS>(); + + // Target's XSCOM Base address + l_XSComBaseAddr = l_systemBaseAddr + + ( ( (g_xscomMaxChipsPerNode * l_xscomChipInfo.nodeId) + + l_xscomChipInfo.chipId ) * THIRTYTWO_GB); + + // Target's virtual address + o_virtAddr = static_cast<uint64_t*> + (mmio_dev_map(reinterpret_cast<void*>(l_XSComBaseAddr), + THIRTYTWO_GB)); + } + + // @todo - Save as an attribute if Virtual address attribute + // is implemented, + // Technically there is a race condition here. The mutex is + // a per-hardware thread mutex, not a mutex for the whole XSCOM + // logic. So there is possibility that this same thread is running + // on another thread at the exact same time. We can use atomic + // update instructions here. + // Comment for Nick: This is a good candidate for having a way + // to return a reference to the attribute instead of requiring + // to call setAttr. We currently have no way to SMP-safely update + // this attribute, where as if we had a reference to it we could use + // the atomic update functions (_sync_bool_compare_and_swap in + // this case. + // i_target->setAttr<ATTR_VIRTUAL_ADDR>(l_virtAddr); + } + } + + } while (0); + + return l_err; +} + + /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// errlHndl_t xscomPerformOp(DeviceFW::OperationType i_opType, @@ -224,24 +442,6 @@ errlHndl_t xscomPerformOp(DeviceFW::OperationType i_opType, // Set to buffer len to 0 until successfully access io_buflen = 0; - // Setup the address - - // Init values are for master processor, as PNOR may not - // yet available - XSComBase_t l_XSComBaseAddr = MASTER_PROC_XSCOM_BASE_ADDR; - TARGETING::XscomChipInfo l_xscomChipInfo = {0}; - if (i_target != TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL) - { - l_XSComBaseAddr = - i_target->getAttr<TARGETING::ATTR_XSCOM_BASE_ADDRESS>(); - l_xscomChipInfo = - i_target->getAttr<TARGETING::ATTR_XSCOM_CHIP_INFO>(); - } - - // Build the XSCom address - XSComP8Address l_mmioAddr(l_addr, l_xscomChipInfo.nodeId, - l_xscomChipInfo.chipId, l_XSComBaseAddr); - // Re-init l_retry for loop l_retry = false; @@ -252,23 +452,23 @@ errlHndl_t xscomPerformOp(DeviceFW::OperationType i_opType, l_XSComMutex = mmio_xscom_mutex(); mutex_lock(l_XSComMutex); + // Get the target chip's virtual address uint64_t* l_virtAddr = NULL; - if (i_target == TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL) - { - if (g_masterProcVirtAddr == NULL) - { - g_masterProcVirtAddr = static_cast<uint64_t*> - (mmio_dev_map(reinterpret_cast<void*>(l_XSComBaseAddr), THIRTYTWO_GB)); - } - l_virtAddr = g_masterProcVirtAddr; - } - else + l_err = getTargetVirtualAddress(i_target, l_virtAddr); + if (l_err) { - //@todo - handle slave processors here + // Unlock + mutex_unlock(l_XSComMutex); + // Done, un-pin + task_affinity_unpin(); + break; } + // Build the XSCom address (relative to node 0, chip 0) + XSComP8Address l_mmioAddr(l_addr); + // Get the offset - uint64_t l_offset = l_mmioAddr.offset(l_XSComBaseAddr); + uint64_t l_offset = l_mmioAddr.offset(); // Keep MMIO access until XSCOM successfully done or error uint64_t l_data = 0; @@ -296,6 +496,46 @@ errlHndl_t xscomPerformOp(DeviceFW::OperationType i_opType, } while (l_hmer.mXSComStatus == HMER::XSCOM_BLOCKED); + + // @todo: this block of code is to un-map the slave devices. + // It should be removed if Virtual Addr attribute + // is supported (since we only map it once then cache + // the virtual addr value + if (i_target != TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL) + { + TARGETING::Target* l_masterProcTarget = NULL; + TARGETING::TargetService& l_targetService = + TARGETING::targetService(); + l_targetService.masterProcChipTargetHandle( l_masterProcTarget ); + if (l_masterProcTarget != i_target) + { + int rc = 0; + rc = mmio_dev_unmap(reinterpret_cast<void*>(l_virtAddr)); + if (rc != 0) + { + /*@ + * @errortype + * @moduleid XSCOM_PERFORM_OP + * @reasoncode XSCOM_MMIO_UNMAP_ERR + * @userdata1 Return Code + * @userdata2 Unmap address + * @devdesc mmio_dev_unmap() returns error + */ + l_err = new ERRORLOG::ErrlEntry(ERRORLOG::ERRL_SEV_UNRECOVERABLE, + XSCOM_PERFORM_OP, + XSCOM_MMIO_UNMAP_ERR, + rc, + reinterpret_cast<uint64_t>(l_virtAddr)); + + // Unlock + mutex_unlock(l_XSComMutex); + // Done, un-pin + task_affinity_unpin(); + break; + } + } + } + // Unlock mutex_unlock(l_XSComMutex); |