mbed-os/targets/TARGET_Cypress/TARGET_PSOC6/udb-sdio-whd/SDIO_HOST.c

1559 lines
57 KiB
C

/***********************************************************************************************//**
* \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)