/** * @file fault_mgmt.c * * @brief * The file implements the Fault Management library. * * \par * NOTE: * (C) Copyright 2012-2015 Texas Instruments, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the * distribution. * * Neither the name of Texas Instruments Incorporated nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ /************************************************************************** *************************** Include Files ******************************** **************************************************************************/ /* Standard Include Files. */ #include #include /* BIOS/XDC Include Files. */ #include /* CSL Include Files. */ #include #include #include #include #include #include #include /* LLD Includes */ #include #include /* FM API Include */ #include /* FM Internal Include */ #include #include #include /* Fault Management OSAL File. */ #include /************************************************************************** ********************** Internal Data Structures ************************** **************************************************************************/ /* Note section name length in bytes (chars). */ #define NOTE_NAME_LEN 32 /* Structure specifying the ELF program header Note section header. * ELF Note Section: * * byte +0 +1 +2 +3 * +--------+--------+---------+--------+ * namesz | # bytes in name field excluding pad| * +--------+--------+---------+--------+ * descsz | # bytes in desc field excluding pad| * +--------+--------+---------+--------+ * type | Not Used (should be zero) | * +--------+--------+---------+--------+ * name | char 0 | char 1 | char 2 | char 3 | * +--------+--------+---------+--------+ * | char 4 | ... | char n | pad | * +--------+--------+---------+--------+ * desc | word 0 | * +--------+--------+---------+--------+ * | word 1 | * +--------+--------+---------+--------+ */ typedef struct { uint32_t namesz; uint32_t descsz; uint32_t type; } elfNoteHeader; /********************************************************************** ************************** Globals *********************************** **********************************************************************/ #pragma DATA_ALIGN (fault_mgmt_data, CACHE_L2_LINESIZE) #pragma DATA_SECTION (fault_mgmt_data, ".note"); uint8_t fault_mgmt_data[FAULT_MGMT_DATA_SIZE]; #if (!defined(DEVICE_K2H) && !defined(DEVICE_K2K) && \ !defined(DEVICE_K2L) && !defined(DEVICE_K2E)) /* Cache line aligned and padded */ #pragma DATA_ALIGN (fmCleanupStatus, CACHE_L2_LINESIZE) #pragma DATA_SECTION (fmCleanupStatus, ".fm_cleanup_status"); int32_t fmCleanupStatus[32]; #endif /* !(K2H && K2K && K2L && K2E) */ /* Heap needed to initialize CPPI prior to IO halt execution */ uint8_t tempCppiHeap[32000]; /* AIF configuration object. Defined as global because it's a large object * that may cause overflow in smaller stacks */ AIF_ConfigObj localAifObj; /********************************************************************** ******************** External Variables ****************************** **********************************************************************/ /* CPPI Global configuration parameters */ extern Cppi_GlobalConfigParams cppiGblCfgParams[]; /********************************************************************** ************************ Local Functions ***************************** **********************************************************************/ /* FUNCTION PURPOSE: Disables a given CPDMA channel *********************************************************************** * DESCRIPTION: Opens then disables a specified CPDMA channel */ static void disableDmaCh(Cppi_Handle cppiHandle, int32_t dmaNum, int32_t chNum, uint32_t isRxCh) { Cppi_RxChInitCfg rxChCfg; Cppi_TxChInitCfg txChCfg; Cppi_ChHnd chHandle; uint8_t isAllocated; Cppi_Result cppiResult; if (isRxCh) { memset((void *) &rxChCfg, 0, sizeof(rxChCfg)); rxChCfg.channelNum = chNum; if (chHandle = Cppi_rxChannelOpen(cppiHandle, &rxChCfg, &isAllocated)) { if(cppiResult = Cppi_channelDisable(chHandle) != CPPI_SOK) { Fault_Mgmt_osalLog("IO Halt: Failed to disable cppi rx ch %d " "with err %d\n", chNum, cppiResult); } } else { Fault_Mgmt_osalLog("IO Halt: " "Failed to open DMA %d, RX channel %d\n", dmaNum, chNum); } } else { memset((void *) &txChCfg, 0, sizeof(txChCfg)); txChCfg.channelNum = chNum; if (chHandle = Cppi_txChannelOpenWithHwCfg(cppiHandle, &txChCfg, &isAllocated, 0)) { if(cppiResult = Cppi_channelDisable(chHandle) != CPPI_SOK) { Fault_Mgmt_osalLog("IO Halt: Failed to disable cppi tx ch %d " "with err %d\n", chNum, cppiResult); } } else { Fault_Mgmt_osalLog("IO Halt: " "Failed to open DMA %d, TX channel %d\n", dmaNum, chNum); } } } /* FUNCTION PURPOSE: Creates the note header *********************************************************************** * DESCRIPTION: Creates the note header. Only the fault data region * size if calculated if NULL is provided for the * fault data region pointer. */ static uint32_t createNoteHeader(uint32_t **pFaultDataPtr) { elfNoteHeader noteHeader; char elfNoteName[NOTE_NAME_LEN]; uint32_t totalBytes = 0; uint32_t *locPtr = NULL; if (pFaultDataPtr) { locPtr = *pFaultDataPtr; } memset((void *)¬eHeader, 0, sizeof(noteHeader)); totalBytes = sizeof(noteHeader); sprintf(elfNoteName, "Core%d", DNUM); noteHeader.namesz = strlen(elfNoteName) + 1; totalBytes += noteHeader.namesz; if (locPtr) { *locPtr++ = noteHeader.namesz; } /* Calculate size of desc section. Subtract out size of pointer to * Exception_Context structure since that structure is added in entirety. * Add an additional uint32_t for PC value that is added. The PC is not * tracked by the Exception Status or Context*/ noteHeader.descsz = (sizeof(ti_sysbios_family_c64p_Exception_Status) - sizeof(ti_sysbios_family_c64p_Exception_Context *)) + sizeof (ti_sysbios_family_c64p_Exception_Context) + sizeof (uint32_t); totalBytes += noteHeader.descsz; /* noteHeader type should always be 0 */ noteHeader.type = 0; if (locPtr) { *locPtr++ = noteHeader.descsz; *locPtr++ = noteHeader.type; } if (locPtr) { memcpy ((void *)locPtr, (void *)&elfNoteName[0], noteHeader.namesz); locPtr += (noteHeader.namesz/sizeof(noteHeader.namesz)); } /* Account for name padding */ if (noteHeader.namesz % sizeof(noteHeader.namesz)) { totalBytes += (sizeof(noteHeader.namesz) - (noteHeader.namesz % sizeof(noteHeader.namesz))); if (locPtr) { locPtr++; } } if (locPtr) { *pFaultDataPtr = locPtr; } return(totalBytes); } /********************************************************************** ********************** FM-Visible Functions ************************** **********************************************************************/ /* FUNCTION PURPOSE: Checks if a wireless peripheral is powered on *********************************************************************** * DESCRIPTION: Checks if a wireless peripheral is powered on via the * PSC registers */ uint32_t fmIsWirelessPeriphPoweredOnForCpdma(Cppi_CpDma dmaNum) { #ifndef DEVICE_K2E uint32_t pwrDmnNum; uint32_t moduleNum; switch (dmaNum) { #if (defined(DEVICE_K2H) || defined(DEVICE_K2K)) /* K2 devices */ case Cppi_CpDma_AIF_CPDMA: pwrDmnNum = CSL_PSC_PD_AIF; moduleNum = CSL_PSC_LPSC_AIF; break; case Cppi_CpDma_FFTC_A_CPDMA: pwrDmnNum = CSL_PSC_PD_FFTC_01; moduleNum = CSL_PSC_LPSC_FFTC_0; break; case Cppi_CpDma_FFTC_B_CPDMA: pwrDmnNum = CSL_PSC_PD_FFTC_01; moduleNum = CSL_PSC_LPSC_FFTC_1; break; case Cppi_CpDma_FFTC_C_CPDMA: pwrDmnNum = CSL_PSC_PD_FFTC_2345; moduleNum = CSL_PSC_LPSC_FFTC_2; break; case Cppi_CpDma_FFTC_D_CPDMA: pwrDmnNum = CSL_PSC_PD_FFTC_2345; moduleNum = CSL_PSC_LPSC_FFTC_3; break; case Cppi_CpDma_FFTC_E_CPDMA: pwrDmnNum = CSL_PSC_PD_FFTC_2345; moduleNum = CSL_PSC_LPSC_FFTC_4; break; case Cppi_CpDma_FFTC_F_CPDMA: pwrDmnNum = CSL_PSC_PD_FFTC_2345; moduleNum = CSL_PSC_LPSC_FFTC_5; break; #elif (defined(DEVICE_K2L)) case Cppi_CpDma_FFTC_A_CPDMA: pwrDmnNum = CSL_PSC_PD_FFTC_0; moduleNum = CSL_PSC_LPSC_FFTC_0; break; #elif (!defined(DEVICE_K2E)) /* Appleton */ case Cppi_CpDma_AIF_CPDMA: pwrDmnNum = CSL_PSC_PD_AI; moduleNum = CSL_PSC_LPSC_AI; break; case Cppi_CpDma_FFTC_A_CPDMA: case Cppi_CpDma_FFTC_B_CPDMA: pwrDmnNum = CSL_PSC_PD_FFTC_AB; moduleNum = CSL_PSC_LPSC_FFTC_AB; break; #endif case Cppi_CpDma_BCP_CPDMA: pwrDmnNum = CSL_PSC_PD_BCP; moduleNum = CSL_PSC_LPSC_BCP; break; default: return (FM_TRUE); } /* Get peripheral PSC status */ if ((CSL_PSC_getPowerDomainState(pwrDmnNum) == PSC_PDSTATE_ON) && (CSL_PSC_getModuleState (moduleNum) == PSC_MODSTATE_ENABLE)) { /* On */ return (FM_TRUE); } else { /* Off */ return (FM_FALSE); } #else return (FM_TRUE); #endif } /********************************************************************** ********************** Application Visible APIs ********************** **********************************************************************/ #if (!defined(DEVICE_K2H) && !defined(DEVICE_K2K) && \ !defined(DEVICE_K2L) && !defined(DEVICE_K2E)) /* FUNCTION PURPOSE: Resets system peripherals to their power on reset state *********************************************************************** * DESCRIPTION: Can be called to reset system peripherals to their power on * reset state. After a DSP local reset an application that * uses system peripherals can be loaded without fear of a * corrupted peripheral state. */ Fm_Result Fault_Mgmt_faultCleanup(Fm_GlobalConfigParams *fmGblCfgParams, Fm_CleanupCfg *cleanupCfg) { uint32_t numEntries; int32_t validateStatus; uint32_t fullInit = FM_TRUE; Fm_Result retVal = FM_FAULT_CLEANUP_OK; /* Writeback status so that Host can view it */ fmCleanupStatus[0] = FM_OK; Fault_Mgmt_osalEndMemAccess(&fmCleanupStatus[0], sizeof(fmCleanupStatus)); /* Validate exclusion entries and return number of entries */ numEntries = fmExclusionValidateList(fmGblCfgParams, cleanupCfg->excludedResources, &validateStatus); if (validateStatus < 0) { Fault_Mgmt_osalLog("Fault Cleanup: Failed because entry %d (base 0) " "of exclusion list has invalid exResInfo field\n", numEntries); retVal = validateStatus; goto errorExit; } /* Do not do full initializtion of peripherals if there are excluded * resources. This means the peripherals have already been configured */ if (numEntries) { fullInit = FM_FALSE; } /* Clean sems first since they may be used in LLD OSAL layers */ if ((retVal = fmCleanSemaphores(cleanupCfg->excludedResources, numEntries)) != FM_FAULT_CLEANUP_OK) { goto errorExit; } /* Stop all timers */ if ((retVal = fmCleanTimers(fmGblCfgParams, cleanupCfg->excludedResources, numEntries)) != FM_FAULT_CLEANUP_OK) { goto errorExit; } /* Init CPPI, QMSS, etc data structures for cleanup */ if ((retVal = fmCleanupInit(fullInit)) != FM_FAULT_CLEANUP_OK) { goto errorExit; } /* Shut down all the wireless peripherals first (Expectation is any app * using them will power them on at init) AIF gets cleaned first since it * is the heartbeat for all wireless peripherals. The wireless peripherals * will be in an "idle" state after the AIF is cleaned */ if (cleanupCfg->cleanAif2) { if ((retVal = fmCleanAif2()) != FM_FAULT_CLEANUP_OK) { goto errorExit; } } if (cleanupCfg->cleanTcp3d) { if ((retVal = fmCleanTcp3d()) != FM_FAULT_CLEANUP_OK) { goto errorExit; } } if (cleanupCfg->cleanBcp) { if ((retVal = fmCleanBcp()) != FM_FAULT_CLEANUP_OK) { goto errorExit; } } if (cleanupCfg->cleanFftc) { if ((retVal = fmCleanFftc()) != FM_FAULT_CLEANUP_OK) { goto errorExit; } } if (cleanupCfg->cleanVcp) { if ((retVal = fmCleanVcp()) != FM_FAULT_CLEANUP_OK) { goto errorExit; } } if (cleanupCfg->cleanCpdma) { if ((retVal = fmCleanCppi(cleanupCfg->excludedResources, numEntries)) != FM_FAULT_CLEANUP_OK) { goto errorExit; } } if (cleanupCfg->cleanEdma3) { if ((retVal = fmCleanEdma3(fmGblCfgParams, cleanupCfg->excludedResources, numEntries, FM_TRUE)) != FM_FAULT_CLEANUP_OK) { goto errorExit; } } if (cleanupCfg->cleanQmss) { if ((retVal = fmCleanQmssQueue(fmGblCfgParams, cleanupCfg->excludedResources, numEntries)) != FM_FAULT_CLEANUP_OK) { goto errorExit; } } if (cleanupCfg->cleanPa) { if ((retVal = fmCleanPa(fmGblCfgParams, cleanupCfg->excludedResources, numEntries)) != FM_FAULT_CLEANUP_OK) { goto errorExit; } } if (cleanupCfg->cleanSa) { if ((retVal = fmCleanSa(cleanupCfg->excludedResources, numEntries)) != FM_FAULT_CLEANUP_OK) { goto errorExit; } } /* Clean QMSS again in case of descriptor leakage during PA and SA * cleanup */ if (cleanupCfg->cleanQmss) { if ((retVal = fmCleanQmssQueue(fmGblCfgParams, cleanupCfg->excludedResources, numEntries)) != FM_FAULT_CLEANUP_OK) { goto errorExit; } } if (cleanupCfg->cleanQmss) { if ((retVal = fmCleanQmssAccum(fmGblCfgParams, cleanupCfg->excludedResources, numEntries)) != FM_FAULT_CLEANUP_OK) { goto errorExit; } } /* Clean out all the CIC mappings after everything has been disabled */ if ((retVal = fmCleanCics(fmGblCfgParams, cleanupCfg->excludedResources, numEntries)) != FM_FAULT_CLEANUP_OK) { goto errorExit; } /* Clean sems again at end to wipe fresh for application restart */ if ((retVal = fmCleanSemaphores(cleanupCfg->excludedResources, numEntries)) != FM_FAULT_CLEANUP_OK) { goto errorExit; } errorExit: /* Writeback status so that Host can view it */ fmCleanupStatus[0] = retVal; Fault_Mgmt_osalEndMemAccess(&fmCleanupStatus[0], sizeof(fmCleanupStatus)); return(retVal); } #endif /* !(K2H && K2K && K2L && K2E) */ /* FUNCTION PURPOSE: Halts certain IO processing mechanisms *********************************************************************** * DESCRIPTION: Stops hardware processing in the following IO: * - EMDA3 DMA, QDMA, and INT channels * - AIF PE/PD channels * - SGMII switch * - CPDMA tx/rx channels */ Fm_Result Fault_Mgmt_haltIoProcessing(Fm_GlobalConfigParams *fmGblCfgParams, Fm_HaltCfg *haltCfg) { uint32_t numEntries; int32_t validateStatus; Fm_ExclusionParams exclusionParams; int32_t i, j; Cppi_Result cppiResult; uint32_t heapSize; Cppi_InitCfg cppiInitCfg; Cppi_CpDmaInitCfg dmaCfg; Cppi_Handle cppiHandle; /* Validate exclusion entries and return number of entries */ numEntries = fmExclusionValidateList(fmGblCfgParams, haltCfg->excludedResources, &validateStatus); if (validateStatus < 0) { Fault_Mgmt_osalLog("IO Halt: Failed because entry %d (base 0) of " "exclusion list has invalid exResInfo field\n", numEntries); return(validateStatus); } memset(&exclusionParams, 0, sizeof(exclusionParams)); exclusionParams.exclusionList = haltCfg->excludedResources; exclusionParams.numListEntries = numEntries; if (haltCfg->haltEdma3) { /* Reuse recovery function */ fmCleanEdma3(fmGblCfgParams, haltCfg->excludedResources, numEntries, FM_FALSE); } if (haltCfg->haltAif2) { #if (!defined(DEVICE_K2L) && !defined(DEVICE_K2E)) /* Halts AIF2 peripheral resources. Resources in the exclusion * list will not be reset */ /* Disable all the AIF2 PE (TX) and PD (RX) channels. */ exclusionParams.resType = Fm_res_AifPeCh; for (i = 0; i < fmGblCfgParams->maxAifPeCh; i++) { exclusionParams.resourceNum = i; if (!fmExclusionIsExcluded(&exclusionParams)) { AIF_disablePeCh(&localAifObj, i); } } exclusionParams.resType = Fm_res_AifPdCh; for (i = 0; i < fmGblCfgParams->maxAifPdCh; i++) { exclusionParams.resourceNum = i; if (!fmExclusionIsExcluded(&exclusionParams)) { AIF_disablePdCh(&localAifObj, i); } } /* Reset the AIF2 timers */ AIF_resetFsync(&localAifObj); #endif /* !(K2L && K2E) */ } if (haltCfg->haltSGMII) { /* Halt the RX/TX transmit logic by starting soft reset but not * completing it */ CSL_SGMII_startRxTxSoftReset(0); CSL_SGMII_startRxTxSoftReset(1); } if (haltCfg->haltCpdma) { /* Setup CPPI */ cppiResult = Cppi_getHeapReq(cppiGblCfgParams, &heapSize); if (sizeof(tempCppiHeap) < heapSize) { Fault_Mgmt_osalLog("IO Halt: Temp heap needed to initalize CPPI is " "too small"); } cppiInitCfg.heapParams.staticHeapBase = &tempCppiHeap[0]; cppiInitCfg.heapParams.staticHeapSize = heapSize; cppiInitCfg.heapParams.heapAlignPow2 = 8; cppiInitCfg.heapParams.dynamicHeapBlockSize = -1; cppiResult = Cppi_initCfg(cppiGblCfgParams, &cppiInitCfg); if (cppiResult != CPPI_SOK) { Fault_Mgmt_osalLog("IO Halt: Failed CPPI init\n"); return(FM_ERROR_CPPI_INIT_FAILED); } memset(&exclusionParams, 0, sizeof(exclusionParams)); exclusionParams.exclusionList = haltCfg->excludedResources; exclusionParams.numListEntries = numEntries; /* Disable CPPI channels */ for (i = 0; i < CPPI_MAX_CPDMA; i++) { if (fmIsWirelessPeriphPoweredOnForCpdma((Cppi_CpDma) i)) { memset ((void *) &dmaCfg, 0, sizeof(dmaCfg)); dmaCfg.dmaNum = (Cppi_CpDma) i; if (cppiHandle = Cppi_open(&dmaCfg)) { exclusionParams.u.cpdmaParams.dma = dmaCfg.dmaNum; exclusionParams.resType = Fm_res_CpdmaRxCh; for (j = 0; j < fmGetDmaMaxRxCh(dmaCfg.dmaNum); j++) { exclusionParams.resourceNum = j; if (!fmExclusionIsExcluded(&exclusionParams)) { disableDmaCh(cppiHandle, i, j, 1); } } exclusionParams.resType = Fm_res_CpdmaTxCh; for (j = 0; j < fmGetDmaMaxTxCh(dmaCfg.dmaNum); j++) { exclusionParams.resourceNum = j; if (!fmExclusionIsExcluded(&exclusionParams)) { disableDmaCh(cppiHandle, i, j, 0); } } } } } } return(FM_OK); } /* FUNCTION PURPOSE: Gets and stores the system register status *********************************************************************** * DESCRIPTION: Gets the system register status and stores it in the * fault management data region. */ void Fault_Mgmt_getLastRegStatus(void) { Exception_Status status; uint32_t *faultDataPtr = (uint32_t *)&fault_mgmt_data[0]; /* Make sure note section fits within the provided fault data region */ if (Fault_Mgmt_getSizes() > FAULT_MGMT_DATA_SIZE) { Fault_Mgmt_osalLog("Get Last Reg Status: Failed - ELF Note data " "section is %d bytes. Need %d bytes.\n", FAULT_MGMT_DATA_SIZE, Fault_Mgmt_getSizes()); return; } memset((void *)faultDataPtr, 0, FAULT_MGMT_DATA_SIZE); createNoteHeader(&faultDataPtr); /* Get the register status for the core dump. */ Exception_getLastStatus (&status); /* Copy the execution context to fault management data memory and update * number of bytes in the Note section. */ /* The very first data value in the "desc" field is the PC for the crash * dump. The PC value should correspond to the NMI return pointer that was * stored in the NRP register when the DSP exception triggered the * exception handler which runs in the NMI context */ *faultDataPtr++ = status.nrp; *faultDataPtr++ = status.efr; *faultDataPtr++ = status.nrp; *faultDataPtr++ = status.ntsr; /* Copy IERR from exception context to the status value. According to the * exception module it's tracked in two places. However, it is only * updated in one of them. To avoid confusion on the host side when the * fault is reported the valid value in the exception context is copied to * the invalid value in the status context */ status.ierr = (uint32_t) status.excContext->IERR; *faultDataPtr++ = status.ierr; memcpy((void *)faultDataPtr, (void *)status.excContext, sizeof(ti_sysbios_family_c64p_Exception_Context)); Fault_Mgmt_osalEndMemAccess(&fault_mgmt_data[0], FAULT_MGMT_DATA_SIZE); return; } /* FUNCTION PURPOSE: Notifies Host of DSP fault *********************************************************************** * DESCRIPTION: Sends a notification to the Host via the IPC registers * that a certain DSP has encountered an exception. */ void Fault_Mgmt_notify(void) { CSL_IPC_genHostInterrupt(FM_HOST_IPCGR_OFFSET + DNUM); return; } /* FUNCTION PURPOSE: Notifies remote DSP core of DSP fault *********************************************************************** * DESCRIPTION: Sends a notification to a remote DSP core via the IPC * registers that a certain DSP has encountered an exception. */ void Fault_Mgmt_notify_remote_core(uint32_t core_id) { CSL_IPC_genNMIEvent(core_id); return; } /* FUNCTION PURPOSE: Returns the required fault management data region size *********************************************************************** * DESCRIPTION: Returns the required size of the fault management data * region containing the crash dump data. */ uint32_t Fault_Mgmt_getSizes(void) { return(createNoteHeader(NULL)); } /** @} */