/***********************************************************************************************//** * \file SDIO_HOST.c * * \brief * This file provides the source code to the API for the UDB based SDIO driver. * *************************************************************************************************** * \copyright * Copyright 2016-2020 Cypress Semiconductor Corporation * SPDX-License-Identifier: Apache-2.0 * * 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. **************************************************************************************************/ #include "SDIO_HOST.h" #include "cy_utils.h" #include "cy_gpio.h" #include "cybsp.h" #if defined(CYHAL_UDB_SDIO) #if defined(__cplusplus) extern "C" { #endif #if defined(CY_RTOS_AWARE) || defined(COMPONENT_RTOS_AWARE) #include "cyabs_rtos.h" #define NEVER_TIMEOUT ( (uint32_t)0xffffffffUL ) static cy_semaphore_t sdio_transfer_finished_semaphore; static bool sema_initialized = false; #endif // Backup struct used to store and restore non retention UDB registers typedef struct { uint32_t CY_SDIO_UDB_WRKMULT_CTL_0; uint32_t CY_SDIO_UDB_WRKMULT_CTL_1; uint32_t CY_SDIO_UDB_WRKMULT_CTL_2; uint32_t CY_SDIO_UDB_WRKMULT_CTL_3; } stc_sdio_backup_regs_t; // Globals Needed for DMA // DMA channel structures cy_stc_dma_channel_config_t respChannelConfig; cy_stc_dma_channel_config_t cmdChannelConfig; cy_stc_dma_channel_config_t writeChannelConfig; cy_stc_dma_channel_config_t readChannelConfig; // DMA Descriptor structures cy_stc_dma_descriptor_t respDesr; cy_stc_dma_descriptor_t cmdDesr; cy_stc_dma_descriptor_t readDesr0; cy_stc_dma_descriptor_t readDesr1; cy_stc_dma_descriptor_t writeDesr0; cy_stc_dma_descriptor_t writeDesr1; // Global structure used for data keeping stc_sdio_gInternalData_t gstcInternalData; // Global CRC table static uint8_t crcTable[256]; // Global values used for DMA interrupt static uint32_t yCountRemainder; static uint32_t yCounts; // Global value for card interrupt static uint8_t pfnCardInt_count = 0; // Global structure to store UDB registers static stc_sdio_backup_regs_t regs; static uint32_t udb_initialized = 0; cy_stc_syspm_callback_params_t sdio_pm_callback_params; cy_stc_syspm_callback_t sdio_pm_callback_handler; // Deep Sleep Mode API Support static void SDIO_SaveConfig(void); static void SDIO_RestoreConfig(void); /*************************************************************************************************** * Function Name: SDIO_DeepSleepCallback ***********************************************************************************************//** * * Callback executed during Deep Sleep entry/exit * * \param params * Pointer to structure that holds callback parameters for this driver. * * \param mode * The state transition mode that is currently happening. * * \note * Saves/Restores SDIO UDB registers * * \return * CY_SYSPM_SUCCESS if the transition was successful, otherwise CY_SYSPM_FAIL * **************************************************************************************************/ cy_en_syspm_status_t SDIO_DeepSleepCallback(cy_stc_syspm_callback_params_t* params, cy_en_syspm_callback_mode_t mode) { CY_UNUSED_PARAMETER(params); cy_en_syspm_status_t status = CY_SYSPM_FAIL; switch (mode) { case CY_SYSPM_CHECK_READY: case CY_SYSPM_CHECK_FAIL: status = CY_SYSPM_SUCCESS; break; case CY_SYSPM_BEFORE_TRANSITION: SDIO_SaveConfig(); status = CY_SYSPM_SUCCESS; break; case CY_SYSPM_AFTER_TRANSITION: SDIO_RestoreConfig(); status = CY_SYSPM_SUCCESS; break; default: break; } return status; } /*************************************************************************************************** * Function Name: SDIO_Init ***********************************************************************************************//** * * Initializes the SDIO hardware * * \param pfuCb * Pointer to structure that holds pointers to callback function * see \ref stc_sdio_irq_cb_t. * * \note * Sets SD Clock Frequency to 400 kHz **************************************************************************************************/ void SDIO_Init(stc_sdio_irq_cb_t* pfuCb) { if (!udb_initialized) { udb_initialized = 1; SDIO_Host_Config_TriggerMuxes(); SDIO_Host_Config_UDBs(); } // Set Number of Blocks to 1 initially, this will be updated later SDIO_SetNumBlocks(1); // Enable SDIO ISR NVIC_EnableIRQ((IRQn_Type)SDIO_HOST_sdio_int__INTC_NUMBER); // Enable the Status Reg to generate an interrupt SDIO_STATUS_AUX_CTL |= (0x10); // Set the priority of DW0, DW1, M4 and M0. DW1 should have highest // First clear priority of all (*(reg32*)CYREG_PROT_SMPU_MS0_CTL) &= ~0x0300; (*(reg32*)CYREG_PROT_SMPU_MS2_CTL) &= ~0x0300; (*(reg32*)CYREG_PROT_SMPU_MS3_CTL) &= ~0x0300; (*(reg32*)CYREG_PROT_SMPU_MS14_CTL) &= ~0x0300; // Next set priority DW1 = 0, DW0 = 1, M4 = 2, M0 =3 (*(reg32*)CYREG_PROT_SMPU_MS2_CTL) |= 0x0100; (*(reg32*)CYREG_PROT_SMPU_MS0_CTL) |= 0x0200; (*(reg32*)CYREG_PROT_SMPU_MS14_CTL) |= 0x0200; // Setup callback for card interrupt gstcInternalData.pstcCallBacks.pfnCardIntCb = pfuCb->pfnCardIntCb; // Setup the DMA channels SDIO_SetupDMA(); // Initialize CRC SDIO_Crc7Init(); // Enable all the bit counters SDIO_CMD_BIT_CNT_CONTROL_REG |= SDIO_ENABLE_CNT; SDIO_WRITE_CRC_CNT_CONTROL_REG |= SDIO_ENABLE_CNT; SDIO_CRC_BIT_CNT_CONTROL_REG |= SDIO_ENABLE_CNT; SDIO_BYTE_CNT_CONTROL_REG |= SDIO_ENABLE_CNT; // Set block byte count to 64, this will be changed later SDIO_SetBlockSize(64); // Set the read and write FIFOs to use the half full status (*(reg32*)SDIO_HOST_bSDIO_Write_DP__DP_AUX_CTL_REG) |= 0x0c; (*(reg32*)SDIO_HOST_bSDIO_Read_DP__DP_AUX_CTL_REG) |= 0x0c; // Set clock to 400k, and enable it SDIO_SetSdClkFrequency(400000); SDIO_EnableIntClock(); SDIO_EnableSdClk(); } /*************************************************************************************************** * Function Name: SDIO_SendCommand ***********************************************************************************************//** * * Send an SDIO command, don't wait for it to finish. * * \param pstcCmdConfig * Command configuration structure. See \ref stc_sdio_cmd_config_t. * **************************************************************************************************/ void SDIO_SendCommand(stc_sdio_cmd_config_t* pstcCmdConfig) { // buffer to hold command data static uint8_t u8cmdBuf[6]; // Populate buffer // Element 0 is the Most Significant Byte u8cmdBuf[0] = SDIO_HOST_DIR | pstcCmdConfig->u8CmdIndex; u8cmdBuf[1] = (uint8_t)((pstcCmdConfig->u32Argument & 0xff000000)>>24); u8cmdBuf[2] = (uint8_t)((pstcCmdConfig->u32Argument & 0x00ff0000)>>16); u8cmdBuf[3] = (uint8_t)((pstcCmdConfig->u32Argument & 0x0000ff00)>>8); u8cmdBuf[4] = (uint8_t)((pstcCmdConfig->u32Argument & 0x000000ff)); // calculate the CRC of above data u8cmdBuf[5] = SDIO_CalculateCrc7(u8cmdBuf, 5); // Shift it up by 1 as the CRC takes the upper 7 bits of the last byte of the cmd u8cmdBuf[5] = u8cmdBuf[5] << 1; // Add on the end bit u8cmdBuf[5] = u8cmdBuf[5] | SDIO_CMD_END_BIT; // Load the first byte into A0 SDIO_CMD_COMMAND_A0_REG = u8cmdBuf[0]; // If a response is expected setup DMA to receive the response if (pstcCmdConfig->bResponseRequired == true) { // Clear the flag in hardware that says skip response SDIO_CONTROL_REG &= ~SDIO_CTRL_SKIP_RESPONSE; // Set the destination address respDesr.dst = (uint32_t)(pstcCmdConfig->pu8ResponseBuf); // Initialize the channel with the descriptor Cy_DMA_Channel_SetDescriptor(SDIO_HOST_Resp_DMA_HW, SDIO_HOST_Resp_DMA_DW_CHANNEL, &respDesr); // Enable the channel Cy_DMA_Channel_Enable(SDIO_HOST_Resp_DMA_HW, SDIO_HOST_Resp_DMA_DW_CHANNEL); } else { // Set the skip flag SDIO_CONTROL_REG |= SDIO_CTRL_SKIP_RESPONSE; } // Setup the Command DMA // Set the source address cmdDesr.src = (uint32_t)(&u8cmdBuf[1]); // Initialize the channel with the descriptor Cy_DMA_Channel_SetDescriptor(SDIO_HOST_CMD_DMA_HW, SDIO_HOST_CMD_DMA_DW_CHANNEL, &cmdDesr); // Enable the channel Cy_DMA_Channel_Enable(SDIO_HOST_CMD_DMA_HW, SDIO_HOST_CMD_DMA_DW_CHANNEL); } /*************************************************************************************************** * Function Name: SDIO_GetResponse ***********************************************************************************************//** * * Takes a 6 byte response buffer, and extracts the 32 bit response, also checks * for index errors, CRC errors, and end bit errors. * * \param bCmdIndexCheck * If True check for index errors * * \param bCmdCrcCheck * If True check for CRC errors * * \param u8cmdIdx * Command index, used for checking the index error * * \param pu32Response * location to store 32 bit response * * \param pu8ResponseBuf * buffer that holds the 6 bytes of response data * * \return * \ref en_sdio_result_t * **************************************************************************************************/ en_sdio_result_t SDIO_GetResponse(uint8_t bCmdIndexCheck, uint8_t bCmdCrcCheck, uint8_t u8cmdIdx, uint32_t* pu32Response, uint8_t* pu8ResponseBuf) { // Function return en_sdio_result_t enRet = Error; // variable to hold temporary CRC uint8_t u8TmpCrc; // temporary response uint32_t u32TmpResponse; // Zero out the pu32Response *pu32Response = 0; // Check if the CRC needs to be checked if (bCmdCrcCheck) { // Calculate the CRC u8TmpCrc = SDIO_CalculateCrc7(pu8ResponseBuf, 5); // Shift calculated CRC up by one bit to match bit position of CRC u8TmpCrc = u8TmpCrc << 1; // Compare calculated CRC with received CRC if ((u8TmpCrc & 0xfe) != (pu8ResponseBuf[5] & 0xfe)) { enRet |= CommandCrcError; } } // Check if the index needs to be checked if (bCmdIndexCheck) { // The index resides in the lower 6 bits of the 1st byte of the response if ((u8cmdIdx != (pu8ResponseBuf[0] & 0x3f))) { enRet |= CommandIdxError; } } // Check the end bit if (!(pu8ResponseBuf[5] & 0x01)) { enRet |= CommandEndError; } if (enRet == Error) { // If we get here then there were no errors with the command populate the response u32TmpResponse = pu8ResponseBuf[1]; u32TmpResponse = u32TmpResponse << 8; u32TmpResponse |= pu8ResponseBuf[2]; u32TmpResponse = u32TmpResponse << 8; u32TmpResponse |= pu8ResponseBuf[3]; u32TmpResponse = u32TmpResponse << 8; u32TmpResponse |= pu8ResponseBuf[4]; *pu32Response = u32TmpResponse; enRet = Ok; } return enRet; } /*************************************************************************************************** * Function Name: SDIO_InitDataTransfer ***********************************************************************************************//** * * Configure the data channel for a data transfer. For a write this doesn't start * the write, that must be done separately after the response is received. * * \param pstcDataConfig * Data configuration structure. See \ref stc_sdio_data_config_t * * **************************************************************************************************/ void SDIO_InitDataTransfer(stc_sdio_data_config_t* pstcDataConfig) { // hold size of entire transfer uint32_t dataSize; // calculate how many bytes are going to be sent dataSize = pstcDataConfig->u16BlockSize * pstcDataConfig->u16BlockCount; // Set the block size and number of blocks SDIO_SetBlockSize(pstcDataConfig->u16BlockSize); SDIO_SetNumBlocks((pstcDataConfig->u16BlockCount) - 1); // If we are reading data setup the DMA to receive read data if (pstcDataConfig->bRead == true) { // First disable the write channel Cy_DMA_Channel_Disable(SDIO_HOST_Write_DMA_HW, SDIO_HOST_Write_DMA_DW_CHANNEL); // Clear any pending interrupts in the DMA Cy_DMA_Channel_ClearInterrupt(SDIO_HOST_Read_DMA_HW, SDIO_HOST_Read_DMA_DW_CHANNEL); NVIC_ClearPendingIRQ((IRQn_Type)SDIO_HOST_Read_Int_INTC_NUMBER); // setup the destination addresses readDesr0.dst = (uint32_t)(pstcDataConfig->pu8Data); readDesr1.dst = (uint32_t)((pstcDataConfig->pu8Data) + 1024); // Setup the X control to transfer two 16 bit elements per transfer for a total of 4 bytes // Remember X increment is in terms of data element size which is 16, thus why it is 1 readDesr0.xCtl = _VAL2FLD(CY_DMA_CTL_COUNT, 1) | _VAL2FLD(CY_DMA_CTL_DST_INCR, 1); readDesr1.xCtl = _VAL2FLD(CY_DMA_CTL_COUNT, 1) | _VAL2FLD(CY_DMA_CTL_DST_INCR, 1); // The X Loop will always transfer 4 bytes. The FIFO will only trigger the // DMA when it has 4 bytes to send (2 in each F0 and F1). There is a possibility // that there could be 3,2,or 1 bytes still in the FIFOs. To solve this the DMA // will be SW triggered when hardware indicates all bytes have been received. // This leads to an extra 1, 2 or 3 bytes being received. So the RX buffer needs to // be at least 3 bytes bigger than the data size. // // Since the X loop is setup to 4, the maximum number of Y loop is 256 so one // descriptor can transfer 1024 bytes. Two descriptors can transfer 2048 bytes. // Since we don't know the maximum number of bytes to read only two descriptors will // be used. If more than 2048 bytes need to be read then and interrupt will be enabled // The descriptor that is not currently running will be updated in the ISR to receive // more data. // // So there are three conditions to check: // 1) Are we sending less than or equal to 1024 bytes if so use one descriptor // 2) Are we sending greater than 1024, but less than or equal to 2048, use two descriptors // 3) Greater than 2048, use two descriptors and the ISR if (dataSize <= 1024) { // Setup one descriptor // Y Increment is 2 because the X is transfer 2 data elements (which are 16 bits) readDesr0.yCtl = _VAL2FLD(CY_DMA_CTL_COUNT, (dataSize - 1) / 4) | _VAL2FLD(CY_DMA_CTL_DST_INCR, 2); // Setup descriptor 0 to point to nothing and disable readDesr0.nextPtr = 0; readDesr0.ctl |= 0x01000000; // Disable Interrupt NVIC_DisableIRQ((IRQn_Type)SDIO_HOST_Read_Int_INTC_NUMBER); } else if (dataSize <= 2048) { // setup the first descriptor for 1024, then setup 2nd descriptor for remainder readDesr0.yCtl = _VAL2FLD(CY_DMA_CTL_COUNT, 255) | _VAL2FLD(CY_DMA_CTL_DST_INCR, 2); readDesr1.yCtl = _VAL2FLD(CY_DMA_CTL_COUNT, (dataSize - 1025) / 4) | _VAL2FLD(CY_DMA_CTL_DST_INCR, 2); // Setup descriptor 0 to point to descriptor 1 readDesr0.nextPtr = (uint32_t)(&readDesr1); // Setup descriptor 1 to point to nothing and disable readDesr1.nextPtr = 0; // Don't disable after first descriptor readDesr0.ctl &= ~0x01000000; // Disable after second descriptor readDesr1.ctl |= 0x01000000; // Disable Interrupt NVIC_DisableIRQ((IRQn_Type)SDIO_HOST_Read_Int_INTC_NUMBER); } else // dataSize must be greater than 2048 { // These are for the ISR, Need to figure out how many "descriptors" // need to run, and the yCount for last descriptor. // Example: dataSize = 2080 // yCounts = 2, yCountRemainder = 7 (send 8 more set of 4) yCounts = (dataSize / 1024); // the Ycount register is a +1 register meaning 0 = 1. I However, need to know when // there is no remainder so I increase the value to make sure there is a remainder and // decrement in the ISR yCountRemainder = (((dataSize - (yCounts * 1024)) + 3) / 4); // Setup the Y Ctrl for both descriptors readDesr0.yCtl = _VAL2FLD(CY_DMA_CTL_COUNT, 255) | _VAL2FLD(CY_DMA_CTL_DST_INCR, 2); readDesr1.yCtl = _VAL2FLD(CY_DMA_CTL_COUNT, 255) | _VAL2FLD(CY_DMA_CTL_DST_INCR, 2); // Setup descriptor 0 to point to descriptor 1 readDesr0.nextPtr = (uint32_t)(&readDesr1); // Setup descriptor 1 to point to descriptor 0 readDesr1.nextPtr = (uint32_t)(&readDesr0); // Don't disable the channel on completion of descriptor readDesr0.ctl &= ~0x01000000; readDesr1.ctl &= ~0x01000000; // Decrement yCounts by 2 since we already have 2 descriptors setup yCounts -= 2; // Enable DMA interrupt NVIC_EnableIRQ((IRQn_Type)SDIO_HOST_Read_Int_INTC_NUMBER); } // Initialize the channel with the first descriptor Cy_DMA_Channel_SetDescriptor(SDIO_HOST_Read_DMA_HW, SDIO_HOST_Read_DMA_DW_CHANNEL, &readDesr0); // Enable the channel Cy_DMA_Channel_Enable(SDIO_HOST_Read_DMA_HW, SDIO_HOST_Read_DMA_DW_CHANNEL); // Set the flag in the control register to enable the read SDIO_CONTROL_REG |= SDIO_CTRL_ENABLE_READ; } // Otherwise it is a write else { // First disable the Read channel Cy_DMA_Channel_Disable(SDIO_HOST_Read_DMA_HW, SDIO_HOST_Read_DMA_DW_CHANNEL); // Clear any pending interrupts in the DMA Cy_DMA_Channel_ClearInterrupt(SDIO_HOST_Write_DMA_HW, SDIO_HOST_Write_DMA_DW_CHANNEL); NVIC_ClearPendingIRQ((IRQn_Type)SDIO_HOST_Write_Int_INTC_NUMBER); // setup the SRC addresses writeDesr0.src = (uint32_t)(pstcDataConfig->pu8Data); writeDesr1.src = (uint32_t)((pstcDataConfig->pu8Data) + 1024); // Setup the X control to transfer two 16 bit elements per transfer for a total of 4 bytes // Remember X increment is in terms of data element size which is 16, thus why it is 1 writeDesr0.xCtl = _VAL2FLD(CY_DMA_CTL_COUNT, 1) | _VAL2FLD(CY_DMA_CTL_SRC_INCR, 1); writeDesr1.xCtl = _VAL2FLD(CY_DMA_CTL_COUNT, 1) | _VAL2FLD(CY_DMA_CTL_SRC_INCR, 1); if (dataSize <= 1024) { // Setup one descriptor // Y Increment is 2 because the X is transfer 2 data elements (which are 16 bits) writeDesr0.yCtl = _VAL2FLD(CY_DMA_CTL_COUNT, (dataSize - 1) / 4) | _VAL2FLD(CY_DMA_CTL_SRC_INCR, 2); // Setup descriptor 0 to point to nothing and disable writeDesr0.nextPtr = 0; writeDesr0.ctl |= 0x01000000; // Disable Interrupt NVIC_DisableIRQ((IRQn_Type)SDIO_HOST_Write_Int_INTC_NUMBER); } else if (dataSize <= 2048) { // setup the first descriptor for 1024, then setup 2nd descriptor for remainder writeDesr0.yCtl = _VAL2FLD(CY_DMA_CTL_COUNT, 255) | _VAL2FLD(CY_DMA_CTL_SRC_INCR, 2); writeDesr1.yCtl = _VAL2FLD(CY_DMA_CTL_COUNT, (dataSize - 1025) / 4) | _VAL2FLD(CY_DMA_CTL_SRC_INCR, 2); // Setup descriptor 0 to point to descriptor 1 writeDesr0.nextPtr = (uint32_t)(&writeDesr1); // Setup descriptor 1 to point to nothing and disable writeDesr1.nextPtr = 0; // Don't disable after first descriptor writeDesr0.ctl &= ~0x01000000; // Disable after second descriptor writeDesr1.ctl |= 0x01000000; // Disable Interrupt NVIC_DisableIRQ((IRQn_Type)SDIO_HOST_Write_Int_INTC_NUMBER); } else // dataSize must be greater than 2048 { // These are for the ISR, Need to figure out how many "descriptors" // need to run, and the yCount for last descriptor. // Example: dataSize = 2080 // yCounts = 2, yCountRemainder = 7 (send 8 more set of 4) yCounts = (dataSize / 1024); // the Ycount register is a +1 register meaning 0 = 1. I However, need to know when // there is no remainder so I increase the value to make sure there is a remainder and // decrement in the ISR yCountRemainder = (((dataSize - (yCounts * 1024)) + 3) / 4); // Setup the Y Ctrl for both descriptors writeDesr0.yCtl = _VAL2FLD(CY_DMA_CTL_COUNT, 255) | _VAL2FLD(CY_DMA_CTL_SRC_INCR, 2); writeDesr1.yCtl = _VAL2FLD(CY_DMA_CTL_COUNT, 255) | _VAL2FLD(CY_DMA_CTL_SRC_INCR, 2); // Setup descriptor 0 to point to descriptor 1 writeDesr0.nextPtr = (uint32_t)(&writeDesr1); // Setup descriptor 1 to point to descriptor 0 writeDesr1.nextPtr = (uint32_t)(&writeDesr0); // Don't disable the channel on completion of descriptor writeDesr0.ctl &= ~0x01000000; writeDesr1.ctl &= ~0x01000000; // Decrement yCounts by 2 since we already have 2 descriptors setup yCounts -= 2; // Enable DMA interrupt NVIC_EnableIRQ((IRQn_Type)SDIO_HOST_Write_Int_INTC_NUMBER); } // Initialize the channel with the first descriptor Cy_DMA_Channel_SetDescriptor(SDIO_HOST_Write_DMA_HW, SDIO_HOST_Write_DMA_DW_CHANNEL, &writeDesr0); } } /*************************************************************************************************** * Function Name: SDIO_SendCommandAndWait ***********************************************************************************************//** * * This function sends a command on the command channel and waits for that * command to finish before returning. If a Command 53 is issued this function * will handle all of the data transfer and wait to return until it is done. * * \param pstcCmd * Pointer command configuration structure see \ref stc_sdio_cmd_t. * * \return * \ref en_sdio_result_t * **************************************************************************************************/ en_sdio_result_t SDIO_SendCommandAndWait(stc_sdio_cmd_t* pstcCmd) { // Store the command and data configurations stc_sdio_cmd_config_t stcCmdConfig; stc_sdio_data_config_t stcDataConfig; uint32_t u32CmdTimeout = 0; // Returns from various function calls en_sdio_result_t enRet = Ok; en_sdio_result_t enRetTmp = Ok; // Hold value of if these checks are needed uint8_t bCmdIndexCheck; uint8_t bCmdCrcCheck; static uint8_t u8responseBuf[6]; // Clear statuses gstcInternalData.stcEvents.u8CmdComplete = 0; gstcInternalData.stcEvents.u8TransComplete = 0; gstcInternalData.stcEvents.u8CRCError = 0; // Setup the command configuration stcCmdConfig.u8CmdIndex = (uint8_t)pstcCmd->u32CmdIdx; stcCmdConfig.u32Argument = pstcCmd->u32Arg; #if defined(CY_RTOS_AWARE) || defined(COMPONENT_RTOS_AWARE) cy_rslt_t result; // Initialize the semaphore. This is not done in init because init is called * in interrupt // thread. cy_rtos_init_semaphore call is prohibited in * interrupt thread. if (!sema_initialized) { cy_rtos_init_semaphore(&sdio_transfer_finished_semaphore, 1, 0); sema_initialized = true; } #else // if defined(CY_RTOS_AWARE) || defined(COMPONENT_RTOS_AWARE) // Variable used for holding timeout value uint32_t u32Timeout = 0; #endif // if defined(CY_RTOS_AWARE) || defined(COMPONENT_RTOS_AWARE) // Determine the type of response and if we need to do any checks // Command 0 and 8 have no response, so don't wait for one if ((pstcCmd->u32CmdIdx == 0) || (pstcCmd->u32CmdIdx == 8)) { bCmdIndexCheck = false; bCmdCrcCheck = false; stcCmdConfig.bResponseRequired = false; stcCmdConfig.pu8ResponseBuf = NULL; } // Command 5's response doesn't have a CRC or index, so don't check else if (pstcCmd->u32CmdIdx == 5) { bCmdIndexCheck = false; bCmdCrcCheck = false; stcCmdConfig.bResponseRequired = true; stcCmdConfig.pu8ResponseBuf = u8responseBuf; } // Otherwise check everything else { bCmdIndexCheck = true; bCmdCrcCheck = true; stcCmdConfig.bResponseRequired = true; stcCmdConfig.pu8ResponseBuf = u8responseBuf; } // Check if the command is 53, if it is then setup the data transfer if (pstcCmd->u32CmdIdx == 53) { // Set the number of blocks in the global struct stcDataConfig.u16BlockCount = (uint16_t)pstcCmd->u16BlockCnt; // Set the size of the data transfer stcDataConfig.u16BlockSize = (uint16_t)pstcCmd->u16BlockSize; // Set the direction are we reading or writing stcDataConfig.bRead = pstcCmd->bRead; // Set the pointer for the data stcDataConfig.pu8Data = pstcCmd->pu8Data; // Check DAT[0] to ensure it isn't low, if it is wait uint32_t count = 0; while (0UL == Cy_GPIO_Read(Cy_GPIO_PortToAddr(CYHAL_GET_PORT(CYBSP_WIFI_SDIO_D0)), CYHAL_GET_PIN(CYBSP_WIFI_SDIO_D0)) && count < SDIO_DAT_BUSY_TIMEOUT_MS) { #if defined(CY_RTOS_AWARE) || defined(COMPONENT_RTOS_AWARE) cy_rtos_delay_milliseconds(1); #else Cy_SysLib_Delay(1); #endif count++; } if (count >= SDIO_DAT_BUSY_TIMEOUT_MS) { enRet |= DataTimeout; } else { // Get the data Transfer Ready SDIO_InitDataTransfer(&stcDataConfig); // Set bit saying this was a CMD_53 SDIO_CONTROL_REG |= SDIO_CTRL_ENABLE_INT; } } if (enRet == Ok) { // Send the command SDIO_SendCommand(&stcCmdConfig); // Wait for the command to finish do { u32CmdTimeout++; enRetTmp = SDIO_CheckForEvent(SdCmdEventCmdDone); } while ((enRetTmp != Ok) && (u32CmdTimeout < SDIO_CMD_TIMEOUT)); if (u32CmdTimeout == SDIO_CMD_TIMEOUT) { enRet |= CMDTimeout; } else // CMD Passed { // If a response is expected check it if (stcCmdConfig.bResponseRequired == true) { enRetTmp = SDIO_GetResponse(bCmdCrcCheck, bCmdIndexCheck, (uint8_t)pstcCmd->u32CmdIdx, pstcCmd->pu32Response, u8responseBuf); if (enRetTmp != Ok) { enRet |= enRetTmp; } else // Response good { // if it was command 53, check the response to ensure there was no error if ((pstcCmd->u32CmdIdx) == 53) { // Make sure none of the error bits are set if (*(pstcCmd->pu32Response) & 0x0000cf00) { enRet |= ResponseFlagError; } else // CMD53 Response good { // If it was command 53 and it was a write enable the write if ((pstcCmd->bRead == false) && (enRet == Ok)) { Cy_DMA_Channel_Disable(SDIO_HOST_Resp_DMA_HW, SDIO_HOST_Resp_DMA_DW_CHANNEL); Cy_DMA_Channel_Disable(SDIO_HOST_CMD_DMA_HW, SDIO_HOST_CMD_DMA_DW_CHANNEL); Cy_DMA_Channel_Disable(SDIO_HOST_Read_DMA_HW, SDIO_HOST_Read_DMA_DW_CHANNEL); // Set the flag in the control register to enable the write Cy_DMA_Channel_Enable(SDIO_HOST_Write_DMA_HW, SDIO_HOST_Write_DMA_DW_CHANNEL); // Enable the channel Cy_SysLib_DelayCycles(35); SDIO_CONTROL_REG |= SDIO_CTRL_ENABLE_WRITE; } #if defined(CY_RTOS_AWARE) || defined(COMPONENT_RTOS_AWARE) // Wait for the transfer to finish. // Acquire semaphore and wait until it will be released // in SDIO_IRQ: // 1. sdio_transfer_finished_semaphore count is equal to // zero. cy_rtos_get_semaphore waits until semaphore // count is increased by cy_rtos_set_semaphore() in // SDIO_IRQ. // 2. The cy_rtos_set_semaphore() increases // sdio_transfer_finished_semaphore count. // 3. The cy_rtos_get_semaphore() function decreases // sdio_transfer_finished_semaphore back to zero // and exit. Or timeout occurs result = cy_rtos_get_semaphore(&sdio_transfer_finished_semaphore, 10, false); enRetTmp = SDIO_CheckForEvent(SdCmdEventTransferDone); if (result != CY_RSLT_SUCCESS) #else // if defined(CY_RTOS_AWARE) || defined(COMPONENT_RTOS_AWARE) // Wait for the transfer to finish do { u32Timeout++; enRetTmp = SDIO_CheckForEvent(SdCmdEventTransferDone); } while (!((enRetTmp == Ok) || (enRetTmp == DataCrcError) || (u32Timeout >= SDIO_DAT_TIMEOUT))); if (u32Timeout == SDIO_DAT_TIMEOUT) #endif // if defined(CY_RTOS_AWARE) || defined(COMPONENT_RTOS_AWARE) { enRet |= DataTimeout; } // if it was a read it is possible there is still extra data hanging // out, trigger the DMA again. This can result in extra data being // transferred so the read buffer should be 3 bytes bigger than needed if (pstcCmd->bRead == true) { Cy_TrigMux_SwTrigger((uint32_t)SDIO_HOST_Read_DMA_DW__TR_IN, 2); } if (enRetTmp == DataCrcError) { enRet |= DataCrcError; } }// CMD53 response good }// Not a CMD53 } // Response Good } // No Response Required, thus no CMD53 } // CMD Passed } // Timeout error #if !defined(CY_RTOS_AWARE) && !defined(COMPONENT_RTOS_AWARE) u32Timeout = 0; #endif // If there were any errors then set general error flag if (enRet != Ok) { enRet |= Error; } // reset CmdTimeout value u32CmdTimeout = 0; // Always Reset on exit to clean up Cy_DMA_Channel_Disable(SDIO_HOST_Resp_DMA_HW, SDIO_HOST_Resp_DMA_DW_CHANNEL); Cy_DMA_Channel_Disable(SDIO_HOST_CMD_DMA_HW, SDIO_HOST_CMD_DMA_DW_CHANNEL); Cy_DMA_Channel_Disable(SDIO_HOST_Write_DMA_HW, SDIO_HOST_Write_DMA_DW_CHANNEL); Cy_DMA_Channel_Disable(SDIO_HOST_Read_DMA_HW, SDIO_HOST_Read_DMA_DW_CHANNEL); // No longer a CMD_53 SDIO_CONTROL_REG &= ~(SDIO_CTRL_ENABLE_INT | SDIO_CTRL_ENABLE_WRITE | SDIO_CTRL_ENABLE_READ); SDIO_Reset(); return enRet; } /*************************************************************************************************** * Function Name: SDIO_CheckForEvent ***********************************************************************************************//** * * Checks to see if a specific event has occurred such a command complete or * transfer complete. * * \param enEventType * The type of event to check for. See \ref en_sdio_event_t. * * \return * \ref en_sdio_result_t * **************************************************************************************************/ en_sdio_result_t SDIO_CheckForEvent(en_sdio_event_t enEventType) { en_sdio_result_t enRet = Error; // Disable Interrupts while modifying the global NVIC_DisableIRQ((IRQn_Type)SDIO_HOST_sdio_int__INTC_NUMBER); // Switch the event to check switch (enEventType) { // If the command is done clear the flag case SdCmdEventCmdDone: if (gstcInternalData.stcEvents.u8CmdComplete > 0) { gstcInternalData.stcEvents.u8CmdComplete = 0; enRet = Ok; } break; // If the transfer is done check for CRC Error and clear the flag case SdCmdEventTransferDone: if (gstcInternalData.stcEvents.u8TransComplete > 0) { gstcInternalData.stcEvents.u8TransComplete = 0; enRet = Ok; } // Check for CRC error and set flags if (gstcInternalData.stcEvents.u8CRCError > 0) { enRet = DataCrcError; gstcInternalData.stcEvents.u8CRCError = 0; } break; } // Re-enable Interrupts NVIC_EnableIRQ((IRQn_Type)SDIO_HOST_sdio_int__INTC_NUMBER); return enRet; } /*************************************************************************************************** * Function Name: SDIO_CalculateCrc7 ***********************************************************************************************//** * * Calculate the 7 bit CRC for the command channel * * \param pu8Data * Data to calculate CRC on * * \param u8Size * Number of bytes to calculate CRC on * * \return * CRC * * \note * This code was copied from * http://www.barrgroup.com/Embedded-Systems/How-To/CRC-Calculation-C-Code * **************************************************************************************************/ uint8_t SDIO_CalculateCrc7(uint8_t* pu8Data, uint8_t u8Size) { uint8_t data; uint8_t remainder = 0; uint32_t byte; for (byte = 0; byte < u8Size; ++byte) { data = pu8Data[byte] ^ remainder; remainder = crcTable[data] ^ (remainder << 8); } return (remainder>>1); } /*************************************************************************************************** * Function Name: SDIO_Crc7Init ***********************************************************************************************//** * * Initialize 7-bit CRC Table * * \note * This code was copied from * http://www.barrgroup.com/Embedded-Systems/How-To/CRC-Calculation-C-Code * **************************************************************************************************/ void SDIO_Crc7Init(void) { uint8_t remainder; uint8_t bit; uint32_t dividend; for (dividend = 0; dividend < 256; ++dividend) { remainder = dividend; for (bit = 8; bit > 0; --bit) { if (remainder & SDIO_CRC_UPPER_BIT) { remainder = (remainder << 1) ^ SDIO_CRC7_POLY; } else { remainder = (remainder << 1); } } crcTable[dividend] = (remainder); } } /*************************************************************************************************** * Function Name: SDIO_SetBlockSize ***********************************************************************************************//** * * Sets the size of each block * * \param u8ByteCount * Size of the block * **************************************************************************************************/ void SDIO_SetBlockSize(uint8_t u8ByteCount) { SDIO_BYTE_COUNT_REG = u8ByteCount; } /*************************************************************************************************** * Function Name: SDIO_SetNumBlocks ***********************************************************************************************//** * * Sets the number of blocks to send * * \param u8BlockCount * Size of the block * **************************************************************************************************/ void SDIO_SetNumBlocks(uint8_t u8BlockCount) { SDIO_DATA_BLOCK_COUNTER_A0_REG = u8BlockCount; SDIO_DATA_BLOCK_COUNTER_D0_REG = u8BlockCount; // The one is used so that we can do 256 bytes SDIO_DATA_BLOCK_COUNTER_A1_REG = 1; SDIO_DATA_BLOCK_COUNTER_D1_REG = 1; } /*************************************************************************************************** * Function Name: SDIO_EnableIntClock ***********************************************************************************************//** * * Enable Internal clock for the block * **************************************************************************************************/ void SDIO_EnableIntClock(void) { SDIO_CONTROL_REG |= SDIO_CTRL_INT_CLK; Cy_SysClk_PeriphEnableDivider(SDIO_HOST_Internal_Clock_DIV_TYPE, SDIO_HOST_Internal_Clock_DIV_NUM); } /*************************************************************************************************** * Function Name: SDIO_DisableIntClock ***********************************************************************************************//** * * Enable Disable clock for the block * **************************************************************************************************/ void SDIO_DisableIntClock(void) { SDIO_CONTROL_REG &= ~SDIO_CTRL_INT_CLK; Cy_SysClk_PeriphDisableDivider(SDIO_HOST_Internal_Clock_DIV_TYPE, SDIO_HOST_Internal_Clock_DIV_NUM); } /*************************************************************************************************** * Function Name: SDIO_EnableSdClk ***********************************************************************************************//** * * Enable SD Clock out to pin * **************************************************************************************************/ void SDIO_EnableSdClk(void) { SDIO_CONTROL_REG |= SDIO_CTRL_SD_CLK; } /*************************************************************************************************** * Function Name: SDIO_DisableSdClk ***********************************************************************************************//** * * Disable SD Clock out to the pin * **************************************************************************************************/ void SDIO_DisableSdClk(void) { SDIO_CONTROL_REG &= ~SDIO_CTRL_SD_CLK; } /*************************************************************************************************** * Function Name: SDIO_SetSdClkFrequency ***********************************************************************************************//** * * Sets the frequency of the SD Clock * * \param u32SdClkFreqHz * Frequency of SD Clock in Hz. * * \note * Only an integer divider is used, so the desired frequency may not be meet **************************************************************************************************/ void SDIO_SetSdClkFrequency(uint32_t u32SdClkFreqHz) { uint16_t u16Div; /* * The UDB SDIO implemenation has a extra divider internally that divides the input clock to the * UDB * by 2. The desired clock frequency is hence intentionally multiplied by 2 in order to get the * required * SDIO operating frequency. */ u16Div = Cy_SysClk_ClkPeriGetFrequency() / (2 * u32SdClkFreqHz); Cy_SysClk_PeriphSetDivider(SDIO_HOST_Internal_Clock_DIV_TYPE, SDIO_HOST_Internal_Clock_DIV_NUM, (u16Div-1)); } /*************************************************************************************************** * Function Name: SDIO_SetupDMA ***********************************************************************************************//** * * Configures the DMA for the SDIO block * **************************************************************************************************/ void SDIO_SetupDMA(void) { // Set the number of bytes to send SDIO_HOST_CMD_DMA_CMD_DMA_Desc_config.xCount = (SDIO_NUM_RESP_BYTES - 1); // Set the destination address SDIO_HOST_CMD_DMA_CMD_DMA_Desc_config.dstAddress = (void*)SDIO_CMD_COMMAND_PTR; // Initialize descriptor for cmd channel Cy_DMA_Descriptor_Init(&cmdDesr, &SDIO_HOST_CMD_DMA_CMD_DMA_Desc_config); // Set flag to disable descriptor when done cmdDesr.ctl |= 0x01000000; // Configure channel // CMD channel can be preempted, and has lower priority cmdChannelConfig.descriptor = &cmdDesr; cmdChannelConfig.preemptable = 1; cmdChannelConfig.priority = 1; cmdChannelConfig.enable = 0u; // Configure Channel with initial Settings Cy_DMA_Channel_Init(SDIO_HOST_CMD_DMA_HW, SDIO_HOST_CMD_DMA_DW_CHANNEL, &cmdChannelConfig); // Enable DMA block Cy_DMA_Enable(SDIO_HOST_CMD_DMA_HW); // Set the number of bytes to receive SDIO_HOST_Resp_DMA_Resp_DMA_Desc_config.xCount = SDIO_NUM_RESP_BYTES; // Set the source address SDIO_HOST_Resp_DMA_Resp_DMA_Desc_config.srcAddress = (void*)SDIO_CMD_RESPONSE_PTR; // Initialize descriptor for response channel Cy_DMA_Descriptor_Init(&respDesr, &SDIO_HOST_Resp_DMA_Resp_DMA_Desc_config); // Set flag to disable descriptor when done respDesr.ctl |= 0x01000000; // Configure channel // response channel can be preempted, and has lower priority respChannelConfig.descriptor = &respDesr; respChannelConfig.preemptable = 1; respChannelConfig.priority = 1; respChannelConfig.enable = 0u; // Configure Channel with initial Settings Cy_DMA_Channel_Init(SDIO_HOST_Resp_DMA_HW, SDIO_HOST_Resp_DMA_DW_CHANNEL, &respChannelConfig); // Enable DMA block Cy_DMA_Enable(SDIO_HOST_Resp_DMA_HW); // Set the destination address SDIO_HOST_Write_DMA_Write_DMA_Desc_config.dstAddress = (void*)SDIO_DAT_WRITE_PTR; // Initialize descriptor for write channel Cy_DMA_Descriptor_Init(&writeDesr0, &SDIO_HOST_Write_DMA_Write_DMA_Desc_config); Cy_DMA_Descriptor_Init(&writeDesr1, &SDIO_HOST_Write_DMA_Write_DMA_Desc_config); // Configure channel // write channel cannot be preempted, and has highest priority writeChannelConfig.descriptor = &writeDesr0; writeChannelConfig.preemptable = 0; writeChannelConfig.priority = 0; writeChannelConfig.enable = 0u; // Configure Channel with initial Settings Cy_DMA_Channel_Init(SDIO_HOST_Write_DMA_HW, SDIO_HOST_Write_DMA_DW_CHANNEL, &writeChannelConfig); // Enable the interrupt Cy_DMA_Channel_SetInterruptMask(SDIO_HOST_Write_DMA_HW, SDIO_HOST_Write_DMA_DW_CHANNEL, CY_DMA_INTR_MASK); // Enable DMA block Cy_DMA_Enable(SDIO_HOST_Write_DMA_HW); // Set the source address SDIO_HOST_Read_DMA_Read_DMA_Desc_config.srcAddress = (void*)SDIO_DAT_READ_PTR; // Initialize descriptor for read channel Cy_DMA_Descriptor_Init(&readDesr0, &SDIO_HOST_Read_DMA_Read_DMA_Desc_config); Cy_DMA_Descriptor_Init(&readDesr1, &SDIO_HOST_Read_DMA_Read_DMA_Desc_config); // Configure channel // read channel cannot be preempted, and has highest priority readChannelConfig.descriptor = &readDesr0; readChannelConfig.preemptable = 0; readChannelConfig.priority = 0; readChannelConfig.enable = 0u; // Configure Channel with initial Settings Cy_DMA_Channel_Init(SDIO_HOST_Read_DMA_HW, SDIO_HOST_Read_DMA_DW_CHANNEL, &readChannelConfig); // Enable the interrupt Cy_DMA_Channel_SetInterruptMask(SDIO_HOST_Read_DMA_HW, SDIO_HOST_Read_DMA_DW_CHANNEL, CY_DMA_INTR_MASK); // Enable DMA block Cy_DMA_Enable(SDIO_HOST_Read_DMA_HW); } /*************************************************************************************************** * Function Name: SDIO_Reset ***********************************************************************************************//** * * Reset the SDIO interface * **************************************************************************************************/ void SDIO_Reset(void) { // Control register is in pulse mode, so this just pulses the reset SDIO_CONTROL_REG |= (SDIO_CTRL_RESET_DP); } /*************************************************************************************************** * Function Name: SDIO_EnableChipInt ***********************************************************************************************//** * * Enables the SDIO Chip Int by setting the mask bit * **************************************************************************************************/ void SDIO_EnableChipInt(void) { SDIO_STATUS_INT_MSK |= SDIO_STS_CARD_INT; } /*************************************************************************************************** * Function Name: SDIO_DisableChipInt ***********************************************************************************************//** * * Enables the SDIO Chip Int by setting the mask bit * **************************************************************************************************/ void SDIO_DisableChipInt(void) { SDIO_STATUS_INT_MSK &= ~SDIO_STS_CARD_INT; } /*************************************************************************************************** * Function Name: SDIO_IRQ ***********************************************************************************************//** * * SDIO interrupt, checks for events, and calls callbacks * **************************************************************************************************/ void SDIO_IRQ(void) { uint8_t u8Status; // First read the status register u8Status = SDIO_STATUS_REG; // Check card interrupt if (u8Status & SDIO_STS_CARD_INT) { pfnCardInt_count++; } // Execute card interrupt callback if neccesary if (0 != pfnCardInt_count) { if (NULL != gstcInternalData.pstcCallBacks.pfnCardIntCb) { gstcInternalData.pstcCallBacks.pfnCardIntCb(); } pfnCardInt_count--; } // If the command is complete set the flag if (u8Status & SDIO_STS_CMD_DONE) { gstcInternalData.stcEvents.u8CmdComplete++; } // Check if a write is complete if (u8Status & SDIO_STS_WRITE_DONE) { // Clear the Write flag and CMD53 flag SDIO_CONTROL_REG &= ~(SDIO_CTRL_ENABLE_WRITE | SDIO_CTRL_ENABLE_INT); // Check if the CRC status return was bad if (u8Status & SDIO_STS_CRC_ERR) { // CRC was bad, set the flag gstcInternalData.stcEvents.u8CRCError++; } // Set the done flag #if defined(CY_RTOS_AWARE) || defined(COMPONENT_RTOS_AWARE) cy_rtos_set_semaphore(&sdio_transfer_finished_semaphore, true); #else gstcInternalData.stcEvents.u8TransComplete++; #endif } // Check if a read is complete if (u8Status & SDIO_STS_READ_DONE) { // Clear the read flag SDIO_CONTROL_REG &= ~(SDIO_CTRL_ENABLE_READ| SDIO_CTRL_ENABLE_INT); // Check the CRC if (u8Status & SDIO_STS_CRC_ERR) { // CRC was bad, set the flag gstcInternalData.stcEvents.u8CRCError++; } // Okay we're done so set the done flag #if defined(CY_RTOS_AWARE) || defined(COMPONENT_RTOS_AWARE) cy_rtos_set_semaphore(&sdio_transfer_finished_semaphore, true); #else gstcInternalData.stcEvents.u8TransComplete++; #endif } NVIC_ClearPendingIRQ((IRQn_Type)SDIO_HOST_sdio_int__INTC_NUMBER); } /*************************************************************************************************** * Function Name: SDIO_READ_DMA_IRQ ***********************************************************************************************//** * * SDIO DMA Read interrupt, checks counts and toggles to other descriptor if * needed * **************************************************************************************************/ void SDIO_READ_DMA_IRQ(void) { // Shouldn't have to change anything unless it is the last descriptor // If the current descriptor is 0, then change descriptor 1 if (Cy_DMA_Channel_GetCurrentDescriptor(SDIO_HOST_Read_DMA_HW, SDIO_HOST_Read_DMA_DW_CHANNEL) == &readDesr0) { // We need to increment the destination address every time readDesr1.dst += 2048; // If this is the last descriptor if ((yCounts == 1) && (yCountRemainder == 0)) { // In this case all we need to change is the next descriptor and disable readDesr1.nextPtr = 0; readDesr1.ctl |= 0x01000000; NVIC_DisableIRQ((IRQn_Type)SDIO_HOST_Read_Int_INTC_NUMBER); } else if ((yCounts == 0) && (yCountRemainder > 0)) { // change next descriptor, and disable readDesr1.nextPtr = 0; readDesr1.ctl |= 0x01000000; // Also change the yCount readDesr1.yCtl = _VAL2FLD(CY_DMA_CTL_COUNT, (yCountRemainder-1)) | _VAL2FLD(CY_DMA_CTL_DST_INCR, 2); NVIC_DisableIRQ((IRQn_Type)SDIO_HOST_Read_Int_INTC_NUMBER); } } // If the current descriptor is 1, then change descriptor 0 if (Cy_DMA_Channel_GetCurrentDescriptor(SDIO_HOST_Read_DMA_HW, SDIO_HOST_Read_DMA_DW_CHANNEL) == &readDesr1) { // We need to increment the destination address everytime readDesr0.dst += 2048; // If this is the last descriptor if ((yCounts == 1) && (yCountRemainder == 0)) { // In this case all we need to change is the next descriptor and disable readDesr0.nextPtr = 0; readDesr0.ctl |= 0x01000000; NVIC_DisableIRQ((IRQn_Type)SDIO_HOST_Read_Int_INTC_NUMBER); } else if ((yCounts == 0) && (yCountRemainder > 0)) { // change next descriptor, and disable readDesr0.nextPtr = 0; readDesr0.ctl |= 0x01000000; // Also change the yCount readDesr0.yCtl = _VAL2FLD(CY_DMA_CTL_COUNT, (yCountRemainder-1)) | _VAL2FLD(CY_DMA_CTL_DST_INCR, 2); NVIC_DisableIRQ((IRQn_Type)SDIO_HOST_Read_Int_INTC_NUMBER); } } // Clear the interrupt Cy_DMA_Channel_ClearInterrupt(SDIO_HOST_Read_DMA_HW, SDIO_HOST_Read_DMA_DW_CHANNEL); // decrement y counts yCounts--; } /*************************************************************************************************** * Function Name: SDIO_WRITE_DMA_IRQ ***********************************************************************************************//** * * SDIO DMA Write interrupt, checks counts and toggles to other descriptor if * needed * **************************************************************************************************/ void SDIO_WRITE_DMA_IRQ(void) { // We shouldn't have to change anything unless it is the last descriptor // If the current descriptor is 0, then change descriptor 1 if (Cy_DMA_Channel_GetCurrentDescriptor(SDIO_HOST_Write_DMA_HW, SDIO_HOST_Write_DMA_DW_CHANNEL) == &writeDesr0) { // We also need to increment the destination address every-time writeDesr1.src += 2048; // If this is the last descriptor if ((yCounts == 1) && (yCountRemainder == 0)) { // In this case all we need to change is the next descriptor and disable writeDesr1.nextPtr = 0; writeDesr1.ctl |= 0x01000000; NVIC_DisableIRQ((IRQn_Type)SDIO_HOST_Write_Int_INTC_NUMBER); } else if ((yCounts == 0) && (yCountRemainder > 0)) { // change next descriptor, and disable writeDesr1.nextPtr = 0; writeDesr1.ctl |= 0x01000000; // Also change the yCount writeDesr1.yCtl = _VAL2FLD(CY_DMA_CTL_COUNT, (yCountRemainder -1)) | _VAL2FLD(CY_DMA_CTL_SRC_INCR, 2); NVIC_DisableIRQ((IRQn_Type)SDIO_HOST_Write_Int_INTC_NUMBER); } } // If the current descriptor is 1, then change descriptor 0 if (Cy_DMA_Channel_GetCurrentDescriptor(SDIO_HOST_Write_DMA_HW, SDIO_HOST_Write_DMA_DW_CHANNEL) == &writeDesr1) { // We also need to increment the destination address writeDesr0.src += 2048; // If this is the last descriptor if ((yCounts == 1) && (yCountRemainder == 0)) { // In this case all we need to change is the next descriptor and disable writeDesr0.nextPtr = 0; writeDesr0.ctl |= 0x01000000; NVIC_DisableIRQ((IRQn_Type)SDIO_HOST_Write_Int_INTC_NUMBER); } else if ((yCounts == 0) && (yCountRemainder > 0)) { // change next descriptor, and disable writeDesr0.nextPtr = 0; writeDesr0.ctl |= 0x01000000; // Also change the yCount writeDesr0.yCtl = _VAL2FLD(CY_DMA_CTL_COUNT, (yCountRemainder -1)) | _VAL2FLD(CY_DMA_CTL_SRC_INCR, 2); NVIC_DisableIRQ((IRQn_Type)SDIO_HOST_Write_Int_INTC_NUMBER); } } // Clear the interrupt Cy_DMA_Channel_ClearInterrupt(SDIO_HOST_Write_DMA_HW, SDIO_HOST_Write_DMA_DW_CHANNEL); yCounts--; } /*************************************************************************************************** * Function Name: SDIO_Free ***********************************************************************************************//** * * Frees any system resources that were allocated by the SDIO driver. * **************************************************************************************************/ void SDIO_Free(void) { #if defined(CY_RTOS_AWARE) || defined(COMPONENT_RTOS_AWARE) cy_rtos_deinit_semaphore(&sdio_transfer_finished_semaphore); #endif } /******************************************************************************* * Function Name: SDIO_SaveConfig ******************************************************************************** * * Saves the user configuration of the SDIO UDB non-retention registers. Call the * SDIO_SaveConfig() function before the Cy_SysPm_CpuEnterDeepSleep() function. * *******************************************************************************/ static void SDIO_SaveConfig(void) { regs.CY_SDIO_UDB_WRKMULT_CTL_0 = UDB->WRKMULT.CTL[0]; regs.CY_SDIO_UDB_WRKMULT_CTL_1 = UDB->WRKMULT.CTL[1]; regs.CY_SDIO_UDB_WRKMULT_CTL_2 = UDB->WRKMULT.CTL[2]; regs.CY_SDIO_UDB_WRKMULT_CTL_3 = UDB->WRKMULT.CTL[3]; } /******************************************************************************* * Function Name: SDIO_RestoreConfig ******************************************************************************** * * Restores the user configuration of the SDIO UDB non-retention registers. Call * the SDIO_Wakeup() function after the Cy_SysPm_CpuEnterDeepSleep() function. * *******************************************************************************/ static void SDIO_RestoreConfig(void) { UDB->WRKMULT.CTL[0] = regs.CY_SDIO_UDB_WRKMULT_CTL_0; UDB->WRKMULT.CTL[1] = regs.CY_SDIO_UDB_WRKMULT_CTL_1; UDB->WRKMULT.CTL[2] = regs.CY_SDIO_UDB_WRKMULT_CTL_2; UDB->WRKMULT.CTL[3] = regs.CY_SDIO_UDB_WRKMULT_CTL_3; } #if defined(__cplusplus) } #endif #endif // defined(CYHAL_UDB_SDIO)