Newer
Older
mbed-os / targets / TARGET_Cypress / TARGET_PSOC6 / mtb-hal-cat1 / source / cyhal_sdhc.c
@Dustin Crossman Dustin Crossman on 4 Jun 2021 107 KB Fix file modes.
/*******************************************************************************
* File Name: cyhal_sdhc.c
*
* Description:
* Provides a high level interface for interacting with the Cypress SDHC. This
* is a wrapper around the lower level PDL API.
*
********************************************************************************
* \copyright
* Copyright 2018-2021 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 <string.h> /* For memcpy */
#include <stdlib.h>
#include "cy_pdl.h"
#include "cy_utils.h"
#include "cy_result.h"
#include "cyhal_sdhc.h"
#include "cyhal_sdio.h"
#include "cyhal_gpio.h"
#include "cyhal_clock.h"
#include "cyhal_hwmgr.h"
#include "cyhal_utils.h"
#include "cyhal_system.h"
#include "cyhal_syspm.h"

/**
* \addtogroup group_hal_impl_sdhc SDHC (SD Host Controller)
* \ingroup group_hal_impl
* \{
*
* The SHDC HAL implemenation for PSoC 6 provides implementations for the following weak functions
* specified by the PDL to make their usage in SDHC HAL driver more flexible by providing user ability
* to use card detect, write protect, pwr en, and io select signals on custom pins instead of dedicated
* SDHC block pins
*   - Cy_SD_Host_IsCardConnected
*   - Cy_SD_Host_IsWpSet
*   - Cy_SD_Host_EnableCardVoltage
*   - Cy_SD_Host_DisableCardVoltage
*   - Cy_SD_Host_ChangeIoVoltage
* In order to disable these implementations, the define CYHAL_DISABLE_WEAK_FUNC_IMPL
* (DEFINES+=CYHAL_DISABLE_WEAK_FUNC_IMPL) must be set when building the application.
*
* \} group_hal_impl_sdhc
*/

#ifdef CY_IP_MXSDHC

#if defined(__cplusplus)
extern "C"
{
#endif

#define _CYHAL_SDHC_IRQ_PRIORITY                        (3UL)
#define _CYHAL_SDHC_RW_RETRY_CYCLES                     (1000u)     /* Number of cycles for read/write operation complete */
#define _CYHAL_SDHC_RETRY_TIMES                         (1000UL)    /* The number loops to make the timeout in msec */
#define _CYHAL_SDHC_FUJE_TIMEOUT_MS                     (1000U)     /* The Fuje timeout for one block */
#define _CYHAL_SDHC_RW_TIMEOUT_US                       (500U)      /* The SDHC Read/Write timeout for one block */
#define _CYHAL_SDHC_TRANSFER_TIMEOUT                    (0xCUL)     /* The transfer timeout */
#define _CYHAL_SDHC_EMMC_TRIM_DELAY_MS                  (100U)      /* The EMMC TRIM timeout */
#define _CYHAL_SDIO_ENUMERATION_TIMEOUT_MS              (500U)
#define _CYHAL_SDHC_READ_TIMEOUT_MS                     (100U)      /* The Read timeout for one block. */
#define _CYHAL_SDHC_ALL_ERR_INTERRUPTS                  (CYHAL_SDHC_CMD_TOUT_ERR | CYHAL_SDHC_CMD_CRC_ERR |\
                                                         CYHAL_SDHC_CMD_END_BIT_ERR | CYHAL_SDHC_CMD_IDX_ERR |\
                                                         CYHAL_SDHC_DATA_TOUT_ERR | CYHAL_SDHC_DATA_CRC_ERR |\
                                                         CYHAL_SDHC_DATA_END_BIT_ERR | CYHAL_SDHC_CUR_LMT_ERR |\
                                                         CYHAL_SDHC_AUTO_CMD_ERR | CYHAL_SDHC_ADMA_ERR |\
                                                         CYHAL_SDHC_TUNNING_ERR | CYHAL_SDHC_RESP_ERR |\
                                                         CYHAL_SDHC_BOOT_ACK_ERR)
#define _CYHAL_SDHC_RCA_SHIFT                           (16U)
#define _CYHAL_SDHC_1_8_REG_STABLE_TIME_MS              (30U)       /* The 1.8 voltage regulator stable time. */
#define _CYHAL_SDHC_PWR_RAMP_UP_TIME_MS                 (36U)       /* Time needed for card VDD to achieve operating
                                                                    * supply range level after power on (power on time,
                                                                    * 1 ms + pwr ramp up, max 35 ms) */
#define _CYHAL_SDHC_TOUT_TMCLK_POW_MIN                  (13U)        /* Minimal power of 2 for data timeout counter value */
#define _CYHAL_SDHC_TOUT_TMCLK_POW_MAX                  (27U)        /* Maximal power of 2 for data timeout counter value */
#define _CYHAL_SDHC_EXPECTED_BASE_CLK_FREQ_HZ           (100000000U) /* By default SDHC block is expected to be clocked by 100 MHz */

#define _CYHAL_SDIO_RW_TIMEOUT_US                       (5U)        /* The SDIO Read/Write timeout for one block */
#define _CYHAL_SDIO_CMD_CMPLT_DELAY_US                  (5U)        /* The Command complete delay */
#define _CYHAL_SDIO_HOST_CLK_400K                       (400UL * 1000UL)    /* 400 kHz clock frequency */
#define _CYHAL_SDIO_64B_BLOCK                           (64U)


#define _CYHAL_SDIO_SET_ALL_INTERRUPTS_MASK             (0x61FFUL)
#define _CYHAL_SDIO_ALL_INTERRUPTS_ENABLE_MASK          (0x61FFUL)
#define _CYHAL_SDIO_CLEAR_ALL_INTERRUPTS_MASK           (0x0UL)
#define _CYHAL_SDIO_CLEAR_ALL_INTERRUPTS_ENABLE_MASK    (0x0UL)
#define _CYHAL_SDIO_TRANSFER_TRIES                      (50U)


/* Clock frequency which is connected to SDHC divider */
#define _CYHAL_SDIO_CLK_HF_HZ                           (100000000UL)

/* Macro-function to calculate pin mapping number */
#define _CYHAL_SDHC_ELEM_COUNT(pin_mapping)    (sizeof(pin_mapping)/sizeof(cyhal_resource_pin_mapping_t))

#if (defined(SDHC_CHIP_TOP_DATA8_PRESENT) && (SDHC_CHIP_TOP_DATA8_PRESENT))   || \
    (defined(SDHC0_CHIP_TOP_DATA8_PRESENT) && (SDHC0_CHIP_TOP_DATA8_PRESENT)) || \
    (defined(SDHC1_CHIP_TOP_DATA8_PRESENT) && (SDHC1_CHIP_TOP_DATA8_PRESENT))
    #define _CYHAL_SDHC_DATA8_PRESENT   1
#else
    #define _CYHAL_SDHC_DATA8_PRESENT   0
#endif
#if (defined(SDHC_CHIP_TOP_CARD_DETECT_PRESENT) && (SDHC_CHIP_TOP_CARD_DETECT_PRESENT))   || \
    (defined(SDHC0_CHIP_TOP_CARD_DETECT_PRESENT) && (SDHC0_CHIP_TOP_CARD_DETECT_PRESENT)) || \
    (defined(SDHC1_CHIP_TOP_CARD_DETECT_PRESENT) && (SDHC1_CHIP_TOP_CARD_DETECT_PRESENT))
    #define _CYHAL_SDHC_CARD_DETECT_PRESENT   1
#else
    #define _CYHAL_SDHC_CARD_DETECT_PRESENT   0
#endif
#if (defined(SDHC_CHIP_TOP_CARD_WRITE_PROT_PRESENT) && (SDHC_CHIP_TOP_CARD_WRITE_PROT_PRESENT))   || \
    (defined(SDHC0_CHIP_TOP_CARD_WRITE_PROT_PRESENT) && (SDHC0_CHIP_TOP_CARD_WRITE_PROT_PRESENT)) || \
    (defined(SDHC1_CHIP_TOP_CARD_WRITE_PROT_PRESENT) && (SDHC1_CHIP_TOP_CARD_WRITE_PROT_PRESENT))
    #define _CYHAL_SDHC_CARD_WRITE_PROT_PRESENT   1
#else
    #define _CYHAL_SDHC_CARD_WRITE_PROT_PRESENT   0
#endif
#if (defined(SDHC_CHIP_TOP_LED_CTRL_PRESENT) && (SDHC_CHIP_TOP_LED_CTRL_PRESENT))   || \
    (defined(SDHC0_CHIP_TOP_LED_CTRL_PRESENT) && (SDHC0_CHIP_TOP_LED_CTRL_PRESENT)) || \
    (defined(SDHC1_CHIP_TOP_LED_CTRL_PRESENT) && (SDHC1_CHIP_TOP_LED_CTRL_PRESENT))
    #define _CYHAL_SDHC_LED_CTRL_PRESENT   1
#else
    #define _CYHAL_SDHC_LED_CTRL_PRESENT   0
#endif
#if (defined(SDHC_CHIP_TOP_IO_VOLT_SEL_PRESENT) && (SDHC_CHIP_TOP_IO_VOLT_SEL_PRESENT))   || \
    (defined(SDHC0_CHIP_TOP_IO_VOLT_SEL_PRESENT) && (SDHC0_CHIP_TOP_IO_VOLT_SEL_PRESENT)) || \
    (defined(SDHC1_CHIP_TOP_IO_VOLT_SEL_PRESENT) && (SDHC1_CHIP_TOP_IO_VOLT_SEL_PRESENT))
    #define _CYHAL_SDHC_IO_VOLT_SEL_PRESENT   1
#else
    #define _CYHAL_SDHC_IO_VOLT_SEL_PRESENT   0
#endif
#if (defined(SDHC_CHIP_TOP_CARD_IF_PWR_EN_PRESENT) && (SDHC_CHIP_TOP_CARD_IF_PWR_EN_PRESENT))   || \
    (defined(SDHC0_CHIP_TOP_CARD_IF_PWR_EN_PRESENT) && (SDHC0_CHIP_TOP_CARD_IF_PWR_EN_PRESENT)) || \
    (defined(SDHC1_CHIP_TOP_CARD_IF_PWR_EN_PRESENT) && (SDHC1_CHIP_TOP_CARD_IF_PWR_EN_PRESENT))
    #define _CYHAL_SDHC_CARD_IF_PWR_EN_PRESENT   1
#else
    #define _CYHAL_SDHC_CARD_IF_PWR_EN_PRESENT   0
#endif
#if (defined(SDHC_CHIP_TOP_CARD_EMMC_RESET_PRESENT) && (SDHC_CHIP_TOP_CARD_EMMC_RESET_PRESENT))   || \
    (defined(SDHC0_CHIP_TOP_CARD_EMMC_RESET_PRESENT) && (SDHC0_CHIP_TOP_CARD_EMMC_RESET_PRESENT)) || \
    (defined(SDHC1_CHIP_TOP_CARD_EMMC_RESET_PRESENT) && (SDHC1_CHIP_TOP_CARD_EMMC_RESET_PRESENT))
    #define _CYHAL_SDHC_CARD_EMMC_RESET_PRESENT   1
#else
    #define _CYHAL_SDHC_CARD_EMMC_RESET_PRESENT   0
#endif

/* Mask which indicates interface change */
#define _CYHAL_SDIO_INTERFACE_CHANGE_MASK    ((uint32_t) ((uint32_t) CYHAL_SDIO_GOING_DOWN) | ((uint32_t) CYHAL_SDIO_COMING_UP))
const uint32_t MAX_FREQUENCY = 50000000;

/* List of available SDHC instances */
static SDHC_Type * const _CYHAL_SDHC_BASE_ADDRESSES[CY_IP_MXSDHC_INSTANCES] =
{
#ifdef SDHC0
    SDHC0,
#endif /* ifdef SDHC0 */

#ifdef SDHC1
    SDHC1,
#endif /* ifdef SDHC1 */
};

/* List of available SDHC interrupt sources */
static const IRQn_Type _CYHAL_SDHC_IRQ_N[CY_IP_MXSDHC_INSTANCES] =
{
#ifdef SDHC0
    sdhc_0_interrupt_general_IRQn,
#endif /* ifdef SDHC0 */

#ifdef SDHC1
    sdhc_1_interrupt_general_IRQn,
#endif /* ifdef SDHC1 */
};

static void * stored_objects[CY_IP_MXSDHC_INSTANCES];

typedef enum
{
    _CYHAL_SDHC_NOT_RUNNING = 0x0,
    _CYHAL_SDHC_WAIT_CMD_COMPLETE = 0x1,
    _CYHAL_SDHC_WAIT_XFER_COMPLETE = 0x2,
    _CYHAL_SDHC_WAIT_BOTH = _CYHAL_SDHC_WAIT_CMD_COMPLETE | _CYHAL_SDHC_WAIT_XFER_COMPLETE
} _cyhal_sdhc_transfer_status_t;

/*  cyhal_sdhc_t object is better place for storing this data,
*   but cyhal_sdhc_write and cyhal_sdhc_read takes
*   instance as pointer to const, so there is no way to
*   update object fields inside those functions. */
static _cyhal_sdhc_transfer_status_t _cyhal_sdhc_data_transfer_status[CY_IP_MXSDHC_INSTANCES];

#if defined(CY_RTOS_AWARE) || defined(COMPONENT_RTOS_AWARE)
#include "cyabs_rtos.h"

typedef enum
{
    /* Semaphore is not initialized */
    _CYHAL_SDXX_SEMA_NOT_INITED,
    /* Semaphore is initialized, but will not be used */
    _CYHAL_SDXX_SEMA_NOT_USED,
    /* Semaphore is initialized and used (expected to be set in IRQ handler) */
    _CYHAL_SDXX_SEMA_USED,
    /* Set in irq handler */
    _CYHAL_SDXX_SEMA_SET
} _cyhal_sdxx_semaphore_status_t;

/* cyhal_sdhc_t and cyhal_sdio_t are the better places for keeping
*  these items, but cyhal_sdhc_write and cyhal_sdhc_read takes
*  instance as pointer to const, so there is no way to use
*  semaphore and initizalization flag as instance object fields
*  until we don't change interface in scope of major version
*  update. */
static cy_semaphore_t _cyhal_sdhc_smphr_xfer_done[CY_IP_MXSDHC_INSTANCES];
static _cyhal_sdxx_semaphore_status_t _cyhal_sdxx_sema_status[CY_IP_MXSDHC_INSTANCES];

static inline bool _cyhal_sdxx_is_smfr_ready_for_set(uint8_t block)
{
    return (_CYHAL_SDXX_SEMA_USED == _cyhal_sdxx_sema_status[block]);
}

static inline bool _cyhal_sdxx_is_smfr_ready_for_get(uint8_t block)
{
    return (_CYHAL_SDXX_SEMA_USED == _cyhal_sdxx_sema_status[block]) ||
                (_CYHAL_SDXX_SEMA_SET == _cyhal_sdxx_sema_status[block]);
}

#endif /* CY_RTOS_AWARE or COMPONENT_RTOS_AWARE defined */

static cy_rslt_t _cyhal_sdxx_prepare_for_transfer(SDHC_Type *base, bool is_sdio)
{
    CY_ASSERT(base != NULL);
    uint8_t block_num = (base == SDHC0) ? 0 : 1;

    uint32_t activated_cmd_complete = 0;
    if (((is_sdio) && (((cyhal_sdio_t *)stored_objects[block_num])->irq_cause & CYHAL_SDIO_CMD_COMPLETE)) ||
        ((!is_sdio) && (((cyhal_sdhc_t *)stored_objects[block_num])->irq_cause & CYHAL_SDHC_CMD_COMPLETE)))
    {
        /* Activate CY_SD_HOST_CMD_COMPLETE interrupt mask only if user enabled callback for that event */
        activated_cmd_complete = CY_SD_HOST_CMD_COMPLETE;
    }

    /* Enabling transfer complete interrupt as it takes part in in write / read processes */
    Cy_SD_Host_SetNormalInterruptMask(base, Cy_SD_Host_GetNormalInterruptMask(base) | activated_cmd_complete |
            CY_SD_HOST_XFER_COMPLETE);

    return CY_RSLT_SUCCESS;
}

typedef enum
{
    _CYHAL_SDHC_CARD_VDD                    = 0,
    _CYHAL_SDHC_CARD_IO_VOLTAGE             = 1,
    _CYHAL_SDHC_CARD_DETECT                 = 2,
    _CYHAL_SDHC_CARD_MECH_WRITE_PROTECT     = 3,
    _CYHAL_SDHC_NOT_WEAK_FUNC               = 4
} _cyhal_sdhc_weak_func_type;

/* This callback is used when card detect pin is GPIO (not dedicated SDHC block signal) */
static void _cyhal_sdhc_gpio_card_detect_callback(void *callback_arg, cyhal_gpio_event_t event)
{
    CY_ASSERT(NULL != callback_arg);
    cyhal_sdhc_t * obj = (cyhal_sdhc_t *)callback_arg;

    if (obj->callback_data.callback != NULL)
    {
        cyhal_sdhc_event_callback_t callback = (cyhal_sdhc_event_callback_t) obj->callback_data.callback;

        /* Card is considered as inserted if card_detect pin in low state. */

        /* Card removal event */
        if ((event & CYHAL_GPIO_IRQ_RISE) && (obj->irq_cause & CYHAL_SDHC_CARD_REMOVAL))
        {
            /* Call registered callbacks here */
            (callback) (obj->callback_data.callback_arg, CYHAL_SDHC_CARD_REMOVAL);
        }
        /* Card insertion event */
        if ((event & CYHAL_GPIO_IRQ_FALL) && (obj->irq_cause & CYHAL_SDHC_CARD_INSERTION))
        {
            /* Call registered callbacks here */
            (callback) (obj->callback_data.callback_arg, CYHAL_SDHC_CARD_INSERTION);
        }
    }
}

#if !defined(CYHAL_DISABLE_WEAK_FUNC_IMPL)

/*  Function, that handles pins that stands behind SD Host PDL driver pin-related WEAK functions, such as:
*   - Cy_SD_Host_IsCardConnected
*   - Cy_SD_Host_IsWpSet
*   - Cy_SD_Host_EnableCardVoltage
*   - Cy_SD_Host_DisableCardVoltage
*   - Cy_SD_Host_ChangeIoVoltage
*   To make their usage in SDHC HAL driver more flexible by providing user ability to use card detect,
*   write protect, pwr en, and io select signals on pins they like instead of dedicated SDHC block pins */
static bool _cyhal_sdhc_handle_weak_func(const SDHC_Type *base, _cyhal_sdhc_weak_func_type weak_function, bool enable)
{
    uint8_t block_idx = (SDHC0 == base) ? 0 : 1;
    cyhal_sdhc_t * obj = (cyhal_sdhc_t *)stored_objects[block_idx];
    CY_ASSERT(obj != NULL);
    CY_ASSERT((weak_function == _CYHAL_SDHC_CARD_VDD) || (weak_function == _CYHAL_SDHC_CARD_IO_VOLTAGE) ||
            (weak_function == _CYHAL_SDHC_CARD_DETECT) || (weak_function == _CYHAL_SDHC_CARD_MECH_WRITE_PROTECT));
    /* function index */
    uint8_t f_idx = (uint8_t)weak_function;

    /* Indexes according to _cyhal_sdhc_weak_func_type */
    static const cyhal_resource_pin_mapping_t* pin_mappings[] = {
            cyhal_pin_map_sdhc_card_if_pwr_en, cyhal_pin_map_sdhc_io_volt_sel, cyhal_pin_map_sdhc_card_detect_n,
            cyhal_pin_map_sdhc_card_mech_write_prot
        };
    static const size_t pin_mapping_sizes[] = {
            _CYHAL_SDHC_ELEM_COUNT(cyhal_pin_map_sdhc_card_if_pwr_en), _CYHAL_SDHC_ELEM_COUNT(cyhal_pin_map_sdhc_io_volt_sel),
            _CYHAL_SDHC_ELEM_COUNT(cyhal_pin_map_sdhc_card_detect_n), _CYHAL_SDHC_ELEM_COUNT(cyhal_pin_map_sdhc_card_mech_write_prot)
        };
    cyhal_gpio_t pins[] = {
        obj->pin_card_pwr_en, obj->pin_io_vol_sel, obj->pin_card_detect, obj->pin_card_mech_write_prot
        };

    /*  Per sd_host PDL documentation (documentation for Cy_SD_Host_ChangeIoVoltage),
    *   SIGNALING_EN bit of the SDHC_CORE_HOST_CTRL2_R register must be set even if
    *   GPIO used for io voltage switching. */
    if (_CYHAL_SDHC_CARD_IO_VOLTAGE == weak_function)
    {
        SDHC_CORE_HOST_CTRL2_R(base) = _CLR_SET_FLD16U(SDHC_CORE_HOST_CTRL2_R(base),
                SDHC_CORE_HOST_CTRL2_R_SIGNALING_EN, enable);
        /* enable (true) stands for 1.8V while false for 3.3V */
        obj->low_voltage_io_set = enable;
    }

    /* Pin is not provided by user */
    if (NC == pins[f_idx])
    {
        /* Return true for card detect, false for write protect and false for other (dont care) */
        return (weak_function == _CYHAL_SDHC_CARD_DETECT) ? true : false;
    }
    /* Pin is GPIO */
    else if (NULL == _cyhal_utils_get_resource(pins[f_idx], pin_mappings[f_idx], pin_mapping_sizes[f_idx], NULL))
    {
        if (weak_function == _CYHAL_SDHC_CARD_DETECT)
        {
            /* Card is inserted if signal is low */
            return !cyhal_gpio_read(pins[f_idx]);
        }
        else if (weak_function == _CYHAL_SDHC_CARD_MECH_WRITE_PROTECT)
        {
            /* Card is mech. write protected if signal is high */
            return cyhal_gpio_read(pins[f_idx]);
        }
        /* _CYHAL_SDHC_CARD_VDD or _CYHAL_SDHC_CARD_IO_VOLTAGE */
        else
        {
            cyhal_gpio_write(pins[f_idx], enable);
            /* Don't care */
            return true;
        }
    }
    /* Pin is dedicated SDHC block signal */
    else
    {
        /* Actually copies of corresponding functions from cy_sd_host.c */
        switch(weak_function)
        {
            case _CYHAL_SDHC_CARD_VDD:
                SDHC_CORE_PWR_CTRL_R(base) =
                        _CLR_SET_FLD8U(SDHC_CORE_PWR_CTRL_R(base), SDHC_CORE_PWR_CTRL_R_SD_BUS_PWR_VDD1, enable);
                break;
            case _CYHAL_SDHC_CARD_DETECT:
                while(true != _FLD2BOOL(SDHC_CORE_PSTATE_REG_CARD_STABLE, SDHC_CORE_PSTATE_REG(base)))
                {
                    /* Wait until the card is stable. */
                }
                return _FLD2BOOL(SDHC_CORE_PSTATE_REG_CARD_INSERTED, SDHC_CORE_PSTATE_REG(base));
            case _CYHAL_SDHC_CARD_MECH_WRITE_PROTECT:
                return _FLD2BOOL(SDHC_CORE_PSTATE_REG_WR_PROTECT_SW_LVL, SDHC_CORE_PSTATE_REG(base));
            default:
            case _CYHAL_SDHC_CARD_IO_VOLTAGE:
                /* io voltage already handled above */
                (void)0;
                break;
        }
        /* dont care */
        return true;
    }
}

/* Overriden Cy_SD_Host_IsCardConnected (originally part of cy_sd_host.c)
*  This version can handle both - dedicated IP block sdhc_card_detect_n pin and
*  user-provided GPIO */
bool Cy_SD_Host_IsCardConnected(SDHC_Type const *base)
{
    return _cyhal_sdhc_handle_weak_func(base, _CYHAL_SDHC_CARD_DETECT, false);
}

/* Overriden Cy_SD_Host_IsWpSet function (originally part of cy_sd_host.c).
*  This version can handle both - dedicated IP block sdhc_card_mech_write_prot pin
*  and user-provided GPIO */
bool Cy_SD_Host_IsWpSet(SDHC_Type const *base)
{
    return _cyhal_sdhc_handle_weak_func(base, _CYHAL_SDHC_CARD_MECH_WRITE_PROTECT, false);
}

/* Overriden Cy_SD_Host_EnableCardVoltage and Cy_SD_Host_DisableCardVoltage
*  functions (originally part of in cy_sd_host.c).
*  This version can handle both - dedicated IP block sdhc_card_if_pwr_en pin and
*  user-provided GPIO */
void Cy_SD_Host_EnableCardVoltage(SDHC_Type *base)
{
    (void)_cyhal_sdhc_handle_weak_func(base, _CYHAL_SDHC_CARD_VDD, true);
}
void Cy_SD_Host_DisableCardVoltage(SDHC_Type *base)
{
    (void)_cyhal_sdhc_handle_weak_func(base, _CYHAL_SDHC_CARD_VDD, false);
}

/* Overriden Cy_SD_Host_ChangeIoVoltage function (originally part of cy_sd_host.c).
*  This version can handle both - dedicated IP block sdhc_io_volt_sel pin
*  and user-provided GPIO */
void Cy_SD_Host_ChangeIoVoltage(SDHC_Type *base, cy_en_sd_host_io_voltage_t ioVoltage)
{
    (void)_cyhal_sdhc_handle_weak_func(base, _CYHAL_SDHC_CARD_IO_VOLTAGE, (CY_SD_HOST_IO_VOLT_1_8V == ioVoltage));
}

#endif /* CYHAL_DISABLE_WEAK_FUNC_IMPL */

static uint8_t _cyhal_sdhc_get_block_from_irqn(IRQn_Type irqn)
{
    switch (irqn)
    {
#if (CY_CPU_CORTEX_M4)
    #if (CY_IP_MXSDHC_INSTANCES > 0)
        case sdhc_0_interrupt_general_IRQn: return 0;
    #endif

    #if (CY_IP_MXSDHC_INSTANCES > 1)
        case sdhc_1_interrupt_general_IRQn: return 1;
    #endif

    #if (CY_IP_MXSDHC_INSTANCES > 2)
        #error "Unhandled SDHC count"
    #endif
#endif /* (CY_CPU_CORTEX_M4) */
    default:
        CY_ASSERT(false); /* Should never be called with a non-SDHC IRQn */
        return 0;
    }
}

static void *_cyhal_sdhc_config_structs[CY_IP_MXSDHC_INSTANCES];

/* Structure to map SDHC events on SDHC interrupts */
static const uint32_t _cyhal_sdhc_event_map[] =
{
    (uint32_t)CYHAL_SDHC_ERR_INTERRUPT,   // Default error if nothing is recognized
    (uint32_t)CYHAL_SDHC_CMD_COMPLETE,    // CY_SD_HOST_CMD_COMPLETE
    (uint32_t)CYHAL_SDHC_XFER_COMPLETE,   // CY_SD_HOST_XFER_COMPLETE
    (uint32_t)CYHAL_SDHC_BGAP_EVENT,      // CY_SD_HOST_BGAP
    (uint32_t)CYHAL_SDHC_DMA_INTERRUPT,   // CY_SD_HOST_DMA_INTERRUPT
    (uint32_t)CYHAL_SDHC_BUF_WR_READY,    // CY_SD_HOST_BUF_WR_READY
    (uint32_t)CYHAL_SDHC_BUF_RD_READY,    // CY_SD_HOST_BUF_RD_READY
    (uint32_t)CYHAL_SDHC_CARD_INSERTION,  // CY_SD_HOST_CARD_INSERTION
    (uint32_t)CYHAL_SDHC_CARD_REMOVAL,    // CY_SD_HOST_CARD_REMOVAL
    /*  Placeholder of removed CYHAL_SDHC_CARD_INTERRUPT.
    *   It is needed for cyhal_sdhc_enable_event and _cyhal_utils_convert_flags
    *   functions correct work. */
    0,
    (uint32_t)CYHAL_SDHC_FX_EVENT,        // CY_SD_HOST_FX_EVENT
    (uint32_t)CYHAL_SDHC_CQE_EVENT,       // CY_SD_HOST_CQE_EVENT
    (uint32_t)CYHAL_SDHC_ERR_INTERRUPT,   // CY_SD_HOST_ERR_INTERRUPT
};

static cy_rslt_t _cyhal_sdxx_setup_pin(void *sdhc_instance, cyhal_gpio_t pin, const cyhal_resource_pin_mapping_t *pinmap, size_t count,
    cyhal_gpio_t *objRef, _cyhal_sdhc_weak_func_type weak_func_pin_type)
{
    cy_rslt_t result;
    const cyhal_resource_pin_mapping_t *map = _cyhal_utils_get_resource(pin, pinmap, count, NULL);

    if (map == NULL)
    {
        result = CYHAL_SDHC_RSLT_ERR_PIN;
        if (_CYHAL_SDHC_NOT_WEAK_FUNC != weak_func_pin_type)
        {
            /* pin, provided by user is probably not dedicated SDHC signal, but GPIO */
            switch(weak_func_pin_type)
            {
                case _CYHAL_SDHC_CARD_DETECT:
                case _CYHAL_SDHC_CARD_MECH_WRITE_PROTECT:
                    result = cyhal_gpio_init(pin, CYHAL_GPIO_DIR_INPUT, CYHAL_GPIO_DRIVE_NONE, true);
                    if ((_CYHAL_SDHC_CARD_DETECT == weak_func_pin_type) && (CY_RSLT_SUCCESS == result))
                    {
                        cyhal_gpio_register_callback(pin, _cyhal_sdhc_gpio_card_detect_callback, sdhc_instance);
                        cyhal_gpio_enable_event(pin, CYHAL_GPIO_IRQ_BOTH, CYHAL_ISR_PRIORITY_DEFAULT, true);
                        ((cyhal_sdhc_t *)sdhc_instance)->cd_gpio_cb_enabled = true;
                    }
                    break;
                case _CYHAL_SDHC_CARD_VDD:
                case _CYHAL_SDHC_CARD_IO_VOLTAGE:
                    result = cyhal_gpio_init(pin, CYHAL_GPIO_DIR_OUTPUT, CYHAL_GPIO_DRIVE_STRONG, false);
                    break;
                default:
                    CY_ASSERT(false); /* Should never get here. */
            }
        }
    }
    else
    {
        result = _cyhal_utils_reserve_and_connect(pin, map);
    }

    if (result == CY_RSLT_SUCCESS)
    {
        *objRef = pin;
    }

    return result;
}

/*******************************************************************************
*       Deep Sleep Callback Service Routine
*******************************************************************************/
static bool _cyhal_sdio_syspm_callback(cyhal_syspm_callback_state_t state, cyhal_syspm_callback_mode_t mode, void *callback_arg)
{
    bool allow = true;
    cyhal_sdio_t *obj = (cyhal_sdio_t *)callback_arg;
    CY_ASSERT(obj != NULL);

    cy_stc_syspm_callback_params_t pdl_params =
    {
        .base = obj->base,
        .context = &(obj->context)
    };

    /* Check if hardware is ready to go sleep using lower level callback. */
    if (state == CYHAL_SYSPM_CB_CPU_DEEPSLEEP)
    {
        allow = (Cy_SD_Host_DeepSleepCallback(&pdl_params, _cyhal_utils_convert_haltopdl_pm_mode(mode)) == CY_SYSPM_SUCCESS);
    }

    if (allow)
    {
        switch (mode)
        {
            case CYHAL_SYSPM_CHECK_READY:
            {
                allow = !cyhal_sdio_is_busy(obj);
                if (allow)
                {
                    /* Call the event only if we are ready to go to sleep */
                    cyhal_sdio_event_callback_t callback = (cyhal_sdio_event_callback_t) obj->callback_data.callback;
                    if ((callback != NULL) && (0U != (obj->events & (uint32_t) CYHAL_SDIO_GOING_DOWN)))
                    {
                        (callback)(obj->callback_data.callback_arg, CYHAL_SDIO_GOING_DOWN);
                    }
                    /* Set transition flag to prevent any further transaction */
                    obj->pm_transition_pending = true;
                }
                break;
            }

            case CYHAL_SYSPM_BEFORE_TRANSITION:
            {
                /* Nothing to do */
                break;
            }

            case CYHAL_SYSPM_AFTER_TRANSITION:
            case CYHAL_SYSPM_CHECK_FAIL:
            {
                /* Execute this only if check ready case was executed */
                if (obj->pm_transition_pending)
                {
                    /* Execute callback to indicate that interface is coming up */
                    cyhal_sdio_event_callback_t callback = (cyhal_sdio_event_callback_t) obj->callback_data.callback;
                    if ((callback != NULL) && (0U != (obj->events & (uint32_t) CYHAL_SDIO_COMING_UP)))
                    {
                        (callback)(obj->callback_data.callback_arg, CYHAL_SDIO_COMING_UP);
                    }

                    obj->pm_transition_pending = false;
                }
                break;
            }

            default:
                CY_ASSERT(false);
                break;
        }
    }
    return allow;
}

static bool _cyhal_sdhc_syspm_callback(cyhal_syspm_callback_state_t state, cyhal_syspm_callback_mode_t mode, void *callback_arg)
{
    bool allow = true;
    cyhal_sdhc_t *obj = (cyhal_sdhc_t *)callback_arg;
    CY_ASSERT(obj != NULL);

    cy_stc_syspm_callback_params_t pdl_params =
    {
        .base = obj->base,
        .context = &(obj->context)
    };

    /* Check if hardware is ready to go sleep using lower level callback. */
    if (state == CYHAL_SYSPM_CB_CPU_DEEPSLEEP)
    {
        allow = (Cy_SD_Host_DeepSleepCallback(&pdl_params, _cyhal_utils_convert_haltopdl_pm_mode(mode)) == CY_SYSPM_SUCCESS);
    }

    if (allow)
    {
        switch (mode)
        {
            case CYHAL_SYSPM_CHECK_READY:
            {
                allow = !cyhal_sdhc_is_busy(obj);
                if (allow)
                {
                    /* Set transition flag to prevent any further transaction */
                    obj->pm_transition_pending = true;
                }
                break;
            }

            case CYHAL_SYSPM_BEFORE_TRANSITION:
            {
                /* Nothing to do */
                break;
            }

            case CYHAL_SYSPM_AFTER_TRANSITION:
            case CYHAL_SYSPM_CHECK_FAIL:
            {
                /* Execute this only if check ready case was executed */
                obj->pm_transition_pending = false;
                break;
            }

            default:
                CY_ASSERT(false);
                break;
        }
    }
    return allow;
}

static cy_en_sd_host_bus_width_t _cyhal_sdhc_convert_buswidth(uint8_t sd_data_bits)
{
    switch (sd_data_bits)
    {
        case 1:
            return CY_SD_HOST_BUS_WIDTH_1_BIT;
        case 4:
            return CY_SD_HOST_BUS_WIDTH_4_BIT;
        case 8:
            return CY_SD_HOST_BUS_WIDTH_8_BIT;
        default:
            CY_ASSERT(false);
            return CY_SD_HOST_BUS_WIDTH_1_BIT;
    }
}

/***********************************************************************************************************************
*
* Finds SDHC internal divider value according to source clock frequency and desired frequency.
*
* hz_src                - Source clock frequency, that needs to be divided
* desired_hz            - Desired clock frequency
* tolerance             - (Not used)
* only_below_desired    - (Not used)
* div                   - Calculated divider value will be placed by this pointer
*
***********************************************************************************************************************/
static cy_rslt_t _cyhal_sdxx_find_best_div(uint32_t hz_src, uint32_t desired_hz,
            const cyhal_clock_tolerance_t *tolerance, bool only_below_desired, uint32_t *div)
{
    CY_UNUSED_PARAMETER(tolerance);
    CY_UNUSED_PARAMETER(only_below_desired);

    /* Rounding up for correcting the error in integer division
    * to ensure the actual frequency is less than or equal to
    * the requested frequency.
    * Ensure computed divider is no more than 10-bit.
    */

    if (hz_src > desired_hz)
    {
        uint32_t freq = (desired_hz << 1);
        uint32_t calculated_divider = ((hz_src + freq - 1) / freq) & 0x3FF;
        /* Real divider is 2 x calculated_divider */
        *div = calculated_divider << 1;
    }
    else
    {
        *div = 1;
    }

    return CY_RSLT_SUCCESS;
}

/***********************************************************************************************************************
*
* Changes the Host controller SD clock.
*
* obj                   - Void pointer to cyhal_sdhc_t or cyhal_sdio_t instance
* is_sdio               - Function is called to switch SDIO clock if true, and SDHC if false
* frequency             - The frequency in Hz (pointer). This variable will be updated with actual frequency achieved
*   upon function return.
* lowVoltageSignaling   - Support 1.8V
* negotiate             - Whether new frequency value needs to be negotiated with the card
*
***********************************************************************************************************************/
static cy_rslt_t _cyhal_sdxx_sdcardchangeclock(void *obj, bool is_sdio, uint32_t *frequency, bool lowVoltageSignaling,
            bool negotiate)
{
    CY_ASSERT(NULL != obj);
    CY_ASSERT(NULL != frequency);

    SDHC_Type                       *base;
    cyhal_clock_t                   *clock;
    cy_stc_sd_host_context_t        *context;
    cy_en_sd_host_bus_speed_mode_t  busSpeed;

    if (is_sdio)
    {
        base = ((cyhal_sdio_t *)obj)->base;
        clock = &(((cyhal_sdio_t *)obj)->clock);
        context = &(((cyhal_sdio_t *)obj)->context);
    }
    else
    {
        base = ((cyhal_sdhc_t *)obj)->base;
        clock = &(((cyhal_sdhc_t *)obj)->clock);
        context = &(((cyhal_sdhc_t *)obj)->context);
    }

    CY_ASSERT(NULL != base);

    cyhal_clock_t most_suitable_hf_source;
    uint32_t most_suitable_div = 0;
    uint32_t actualFreq = 0;
    uint32_t hf_source_freq = 0;

    /*  Find most suitable HF clock source and divider for it to achieve closest frequency to desired.
    *   No clock settings are being changed here. */
    cy_rslt_t ret = _cyhal_utils_find_hf_source_n_divider(clock, *frequency, NULL, _cyhal_sdxx_find_best_div,
                                &most_suitable_hf_source, &most_suitable_div);
    if (CY_RSLT_SUCCESS == ret)
    {
        hf_source_freq = cyhal_clock_get_frequency(&most_suitable_hf_source);
        actualFreq = hf_source_freq / most_suitable_div;
        if (actualFreq <= CY_SD_HOST_CLK_25M)
        {
            busSpeed = (lowVoltageSignaling)
                ? CY_SD_HOST_BUS_SPEED_SDR12_5
                : CY_SD_HOST_BUS_SPEED_DEFAULT;
        }
        else if (actualFreq <= CY_SD_HOST_CLK_50M)
        {
            busSpeed = (lowVoltageSignaling)
                ? CY_SD_HOST_BUS_SPEED_SDR25
                : CY_SD_HOST_BUS_SPEED_HIGHSPEED;
        }
        else
        {
            busSpeed = (lowVoltageSignaling)
                ? CY_SD_HOST_BUS_SPEED_SDR50
                : CY_SD_HOST_BUS_SPEED_HIGHSPEED;
        }

        /*  Currently can only negotiate with SD Card. SDIO negotiation proccess is tracked in BSP-2643. */
        if (negotiate && !is_sdio)
        {
            ret = (cy_rslt_t)Cy_SD_Host_SetBusSpeedMode(base, busSpeed, context);

            /* Delay to be removed after DRIVERS-5140 resolvation */
            if (CY_RSLT_SUCCESS == ret)
            {
                cyhal_system_delay_ms(_CYHAL_SDHC_READ_TIMEOUT_MS);
            }
        }
        else
        {
            ret = (cy_rslt_t)Cy_SD_Host_SetHostSpeedMode(base, busSpeed);
        }
    }

    if (CY_RSLT_SUCCESS == ret)
    {
        /* Switch to most suitable HF clock source */
        ret = cyhal_clock_set_source(clock, &most_suitable_hf_source);
    }

    if (CY_RSLT_SUCCESS == ret)
    {
        if (!is_sdio)
        {
            ((cyhal_sdhc_t *)obj)->block_source_freq_hz = hf_source_freq;
        }
        /* SDHC internal divider will be configured instead. */
        ret = cyhal_clock_set_divider(clock, 1);
    }

    if (CY_RSLT_SUCCESS == ret)
    {
        Cy_SD_Host_DisableSdClk(base);
        ret = (cy_rslt_t)Cy_SD_Host_SetSdClkDiv(base, most_suitable_div >> 1);
        Cy_SD_Host_EnableSdClk(base);
    }

    if (CY_RSLT_SUCCESS == ret)
    {
        *frequency = actualFreq;
    }

    return ret;
}

/*******************************************************************************
*
* Waits for the command complete event.
*
* base - The SD host registers structure pointer.
*
*******************************************************************************/
static cy_en_sd_host_status_t _cyhal_sdxx_pollcmdcomplete(SDHC_Type *base, uint8_t block)
{
    cy_en_sd_host_status_t ret = CY_SD_HOST_ERROR_TIMEOUT;
    uint32_t retry  = _CYHAL_SDHC_RETRY_TIMES;

    while (retry > 0UL)
    {
        /* Command complete */
        if (CY_SD_HOST_CMD_COMPLETE == (CY_SD_HOST_CMD_COMPLETE & Cy_SD_Host_GetNormalInterruptStatus(base)))
        {
            _cyhal_sdhc_data_transfer_status[block] &= ~_CYHAL_SDHC_WAIT_CMD_COMPLETE;

            /* Clear interrupt flag */
            Cy_SD_Host_ClearNormalInterruptStatus(base, CY_SD_HOST_CMD_COMPLETE);

            ret = CY_SD_HOST_SUCCESS;
            break;
        }

        cyhal_system_delay_us(_CYHAL_SDIO_CMD_CMPLT_DELAY_US);
        retry--;
    }

    return ret;
}


/*******************************************************************************
*
* Waits for the transfer complete event.
*
* base  - The SD host registers structure pointer.
* delay - The delay timeout for one block transfer.
*
* return If the base pointer is NULL, returns error.
*
*******************************************************************************/
static cy_en_sd_host_status_t _cyhal_sdxx_polltransfercomplete(SDHC_Type *base, const uint16_t delay, bool is_sdio)
{
    cy_en_sd_host_status_t ret = CY_SD_HOST_ERROR_TIMEOUT;
    uint32_t               retry = _CYHAL_SDHC_RW_RETRY_CYCLES;
    uint8_t                block_idx = (SDHC0 == base) ? 0 : 1;
    uint32_t               status = 0UL;

    while ((CY_SD_HOST_ERROR_TIMEOUT == ret) && (retry-- > 0U))
    {
        /* We check for either the interrupt register or the byte set in the _cyhal_sdxx_irq_handler
         * to avoid a deadlock in the case where if an API that is polling is called from an ISR
         * and its priority is higher than the priority of the _cyhal_sdxx_irq_handler thus not allowing
         * the signalling byte to be set.
         */
        status = Cy_SD_Host_GetNormalInterruptStatus(base);
        if (CY_SD_HOST_XFER_COMPLETE == (CY_SD_HOST_XFER_COMPLETE & status) ||
            ((is_sdio) && (!cyhal_sdio_is_busy((cyhal_sdio_t *)stored_objects[block_idx]))) ||
            ((!is_sdio) && (!cyhal_sdhc_is_busy((cyhal_sdhc_t *)stored_objects[block_idx]))))
        {
            /* Transfer complete */
            ret = CY_SD_HOST_SUCCESS;
            break;
        }
        cyhal_system_delay_us(delay);
    }

    return ret;
}

// Indicates that a sync transfer is in process
static void _cyhal_sdxx_setup_smphr(uint32_t block_num)
{
#if defined(CY_RTOS_AWARE) || defined(COMPONENT_RTOS_AWARE)
    bool in_isr = (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) != 0;
    if (!in_isr)
    {
        cy_rslt_t ret = CY_RSLT_SUCCESS;
        if (_CYHAL_SDXX_SEMA_NOT_INITED == _cyhal_sdxx_sema_status[block_num])
        {
            /* Semaphore is used to make transfer complete event wait process be more RTOS-friendly.
            *  It cannot be initialized in ISR context (some of mbed layers are initializing sdio in ISR conext),
            *  so we have to do it in scope of transfer functions. */
            /* Assert that we are not in an ISR */
            CY_ASSERT((SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) == 0);
            ret = cy_rtos_init_semaphore(&_cyhal_sdhc_smphr_xfer_done[block_num], 1, 0);
        }
        else if (_CYHAL_SDXX_SEMA_SET == _cyhal_sdxx_sema_status[block_num])
        {
            /*  Situation, when semaphore was set, but _cyhal_sdxx_waitfor_transfer_complete function was not
            *   used for the transfer to get the semaphore (async transfer). Clearing the semaphore in order
            *   to prepare it for another transfer.  */
            ret = cy_rtos_get_semaphore(&_cyhal_sdhc_smphr_xfer_done[block_num], 500, in_isr);
        }
        if (CY_RSLT_SUCCESS == ret)
        {
            _cyhal_sdxx_sema_status[block_num] = _CYHAL_SDXX_SEMA_USED;
        }
    }
    else if (_CYHAL_SDXX_SEMA_NOT_INITED != _cyhal_sdxx_sema_status[block_num])
    {
        _cyhal_sdxx_sema_status[block_num] = _CYHAL_SDXX_SEMA_NOT_USED;
    }
#else
    // We don't need to do anything special if we're not in an RTOS context
    CY_UNUSED_PARAMETER(block_num);
#endif
}

static cy_rslt_t _cyhal_sdxx_waitfor_transfer_complete(uint32_t block_num, SDHC_Type *base, bool is_sdio)
{
    /* When using an RTOS if a sdxx api (read or write) is called from and ISR in
     * in certain RTOSes cy_rtos_get_semaphore returns immediately without blocking. So we can
     * either busy wait around the semaphore being set in the ISR or use the normal polling method
     * we use in the non-RTOS case. For simplicity and to avoid the calling ISR from depending on
     * the SDXX ISR priority we use the normal polling method.
     */
    cy_rslt_t ret = CY_RSLT_SUCCESS;
    #if defined(CY_RTOS_AWARE) || defined(COMPONENT_RTOS_AWARE)
    bool in_isr = (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) != 0;
    if ((!in_isr) && _cyhal_sdxx_is_smfr_ready_for_get(block_num))
    {
        ret = cy_rtos_get_semaphore(&_cyhal_sdhc_smphr_xfer_done[block_num], 500, in_isr);
        if (CY_RSLT_SUCCESS == ret)
        {
            _cyhal_sdxx_sema_status[block_num] = _CYHAL_SDXX_SEMA_NOT_USED;
        }
    }
    else
    {
    #endif
        CY_UNUSED_PARAMETER(block_num);
        ret = (cy_rslt_t)_cyhal_sdxx_polltransfercomplete(base, _CYHAL_SDHC_RW_TIMEOUT_US, is_sdio);
    #if defined(CY_RTOS_AWARE) || defined(COMPONENT_RTOS_AWARE)
    }
    #endif

    return ret;
}

static void _cyhal_sdhc_irq_handler(void)
{
    IRQn_Type irqn = _CYHAL_UTILS_GET_CURRENT_IRQN();
    uint8_t block = _cyhal_sdhc_get_block_from_irqn(irqn);
    SDHC_Type *blockAddr = _CYHAL_SDHC_BASE_ADDRESSES[block];
    cyhal_sdhc_t *obj = (cyhal_sdhc_t*) _cyhal_sdhc_config_structs[block];

    uint32_t interruptStatus = Cy_SD_Host_GetNormalInterruptStatus(blockAddr);
    uint32_t userInterruptStatus = interruptStatus & obj->irq_cause;
    cyhal_sdhc_event_t user_events = (cyhal_sdhc_event_t)_cyhal_utils_convert_flags(
        _cyhal_sdhc_event_map, sizeof(_cyhal_sdhc_event_map) / sizeof(uint32_t), userInterruptStatus);

    /*  Some parts of SDHost PDL and SDHC HAL drivers are sending SD commands and polling interrupt status
    *   until CY_SD_HOST_CMD_COMPLETE occurs. Thats why we can't clear CY_SD_HOST_CMD_COMPLETE interrupt status
    *   and code below prevents _cyhal_sdhc_irq_handler from being continuosly called because of uncleared
    *   CY_SD_HOST_CMD_COMPLETE event. */

    if (interruptStatus & Cy_SD_Host_GetNormalInterruptMask(blockAddr) & CY_SD_HOST_CMD_COMPLETE)
    {
        /* Disabling command complete interrupt mask */
        Cy_SD_Host_SetNormalInterruptMask(obj->base,
                Cy_SD_Host_GetNormalInterruptMask(obj->base) & (uint32_t) ~CY_SD_HOST_CMD_COMPLETE);
        _cyhal_sdhc_data_transfer_status[block] &= ~_CYHAL_SDHC_WAIT_CMD_COMPLETE;
    }

    /*  During SDHost PDL driver operation, CY_SD_HOST_XFER_COMPLETE status can occur and driver
    *   is polling Cy_SD_Host_GetNormalInterruptStatus while waiting for it. Because of this
    *   it is critical to have CY_SD_HOST_XFER_COMPLETE event mask disabled and only enabled during
    *   transfers. Write / Read SDHC/SDIO HAL functions are taking care of enabling CY_SD_HOST_XFER_COMPLETE
    *   mask before transfer while code below disables it. */

    /* CY_SD_HOST_XFER_COMPLETE occured and appropriate bit in interrupt mask is enabled */
    if (interruptStatus & Cy_SD_Host_GetNormalInterruptMask(blockAddr) & CY_SD_HOST_XFER_COMPLETE)
    {
        /* Clearing transfer complete status */
        Cy_SD_Host_ClearNormalInterruptStatus(blockAddr, CY_SD_HOST_XFER_COMPLETE);

        _cyhal_sdhc_data_transfer_status[block] &= ~_CYHAL_SDHC_WAIT_XFER_COMPLETE;

        #if defined(CY_RTOS_AWARE) || defined(COMPONENT_RTOS_AWARE)
        if (_cyhal_sdxx_is_smfr_ready_for_set(block))
        {
            cy_rtos_set_semaphore(&_cyhal_sdhc_smphr_xfer_done[block], true);
            _cyhal_sdxx_sema_status[block] = _CYHAL_SDXX_SEMA_SET;
        }
        #endif /* CY_RTOS_AWARE or COMPONENT_RTOS_AWARE defined */

        /* Disabling transfer complete interrupt mask */
        Cy_SD_Host_SetNormalInterruptMask(obj->base,
                Cy_SD_Host_GetNormalInterruptMask(obj->base) & (uint32_t) ~CY_SD_HOST_XFER_COMPLETE);
    }

    if (obj->callback_data.callback != NULL)
    {
        cyhal_sdhc_event_callback_t callback = (cyhal_sdhc_event_callback_t) obj->callback_data.callback;

        /* Call registered callbacks here */
        (callback) (obj->callback_data.callback_arg, user_events);
    }

    /*  Cannot clear cmd complete interrupt, as it is being polling-waited by many SD Host functions.
    *   It is expected to be cleared by mentioned polling functions. */
    userInterruptStatus &= (uint32_t) ~CY_SD_HOST_CMD_COMPLETE;

    /* Clear only handled events */
    Cy_SD_Host_ClearNormalInterruptStatus(blockAddr, userInterruptStatus);
}

static void _cyhal_sdio_irq_handler(void)
{
    IRQn_Type irqn = _CYHAL_UTILS_GET_CURRENT_IRQN();
    uint8_t block = _cyhal_sdhc_get_block_from_irqn(irqn);
    SDHC_Type *blockAddr = _CYHAL_SDHC_BASE_ADDRESSES[block];
    cyhal_sdio_t *obj = (cyhal_sdio_t*) _cyhal_sdhc_config_structs[block];

    uint32_t interruptStatus = Cy_SD_Host_GetNormalInterruptStatus(blockAddr);
    uint32_t userInterruptStatus = interruptStatus & obj->irq_cause;
    uint32_t normalInterruptMask = Cy_SD_Host_GetNormalInterruptMask(blockAddr);

    /*  Some parts of SDHost PDL and SDIO HAL drivers are sending SD commands and polling interrupt status
    *   until CY_SD_HOST_CMD_COMPLETE occurs. Thats why we can't clear CY_SD_HOST_CMD_COMPLETE interrupt status
    *   and code below prevents _cyhal_sdhc_irq_handler from being continuosly called because of uncleared
    *   CY_SD_HOST_CMD_COMPLETE event. */

    if (interruptStatus & normalInterruptMask & CY_SD_HOST_CMD_COMPLETE)
    {
        /* Disabling command complete interrupt mask */
        Cy_SD_Host_SetNormalInterruptMask(obj->base,
                Cy_SD_Host_GetNormalInterruptMask(obj->base) & (uint32_t) ~CY_SD_HOST_CMD_COMPLETE);
        _cyhal_sdhc_data_transfer_status[block] &= ~_CYHAL_SDHC_WAIT_CMD_COMPLETE;
    }

    /*  During SDHost PDL driver operation, CY_SD_HOST_XFER_COMPLETE status can occur and driver
    *   is polling Cy_SD_Host_GetNormalInterruptStatus while waiting for it. Because of this
    *   it is critical to have CY_SD_HOST_XFER_COMPLETE event mask disabled and only enabled during
    *   transfers. Write / Read SDHC/SDIO HAL functions are taking care of enabling CY_SD_HOST_XFER_COMPLETE
    *   mask before transfer while code below disables it. */

    /*  CY_SD_HOST_XFER_COMPLETE occured and appropriate bit in interrupt mask is enabled */
    if (interruptStatus & normalInterruptMask & CY_SD_HOST_XFER_COMPLETE)
    {
        _cyhal_sdhc_data_transfer_status[block] &= ~_CYHAL_SDHC_WAIT_XFER_COMPLETE ;
        Cy_SD_Host_ClearNormalInterruptStatus(blockAddr, CY_SD_HOST_XFER_COMPLETE);
        #if defined(CY_RTOS_AWARE) || defined(COMPONENT_RTOS_AWARE)
        if (_cyhal_sdxx_is_smfr_ready_for_set(block))
        {
            cy_rtos_set_semaphore(&_cyhal_sdhc_smphr_xfer_done[block], true);
            _cyhal_sdxx_sema_status[block] = _CYHAL_SDXX_SEMA_SET;
        }
        #endif /* CY_RTOS_AWARE or COMPONENT_RTOS_AWARE defined */

        /* Disabling transfer complete interrupt mask */
        Cy_SD_Host_SetNormalInterruptMask(obj->base,
                Cy_SD_Host_GetNormalInterruptMask(obj->base) & (uint32_t) ~CY_SD_HOST_XFER_COMPLETE);

        /* Transfer is no more active. If card interrupt was not yet enabled after it was disabled in
        *   interrupt handler, enable it.
        */
        uint32_t interrupt_enable_status = Cy_SD_Host_GetNormalInterruptEnable(obj->base);
        if (((interrupt_enable_status & CY_SD_HOST_CARD_INTERRUPT) == 0) && (0U != (CY_SD_HOST_CARD_INTERRUPT & obj->irq_cause)))
        {
            Cy_SD_Host_SetNormalInterruptEnable(obj->base, (interrupt_enable_status | CY_SD_HOST_CARD_INTERRUPT));
        }
    }

    if (obj->callback_data.callback != NULL && userInterruptStatus > 0)
    {
        cyhal_sdio_event_callback_t callback = (cyhal_sdio_event_callback_t) obj->callback_data.callback;
        /* Call registered callbacks here */
        (callback)(obj->callback_data.callback_arg, (cyhal_sdio_irq_event_t) userInterruptStatus);
    }

    /*  Cannot clear cmd complete interrupt, as it is being polling-waited by many SD Host functions.
    *   It is expected to be cleared by mentioned polling functions. */
    userInterruptStatus &= (uint32_t) ~CY_SD_HOST_CMD_COMPLETE;

    /* Clear only handled events */
    Cy_SD_Host_ClearNormalInterruptStatus(blockAddr, userInterruptStatus);

    /* To clear Card Interrupt need to disable Card Interrupt Enable bit.
    *  The Card Interrupt is enabled after the current transfer is complete
    */
    if (0U != (obj->irq_cause & CY_SD_HOST_CARD_INTERRUPT))
    {
        if (0U != (interruptStatus & CY_SD_HOST_CARD_INTERRUPT))
        {
            uint32_t interruptMask = Cy_SD_Host_GetNormalInterruptEnable(blockAddr);
            interruptMask &= (uint32_t) ~CY_SD_HOST_CARD_INTERRUPT;

            /* Disable Card Interrupt */
            Cy_SD_Host_SetNormalInterruptEnable(blockAddr, interruptMask);
        }
    }
}

/* Software reset of SDHC block data and command circuits */
static void _cyhal_sdxx_reset(SDHC_Type *base)
{
    uint8_t block_num = (base == SDHC0) ? 0 : 1;
    _cyhal_sdhc_data_transfer_status[block_num] = _CYHAL_SDHC_NOT_RUNNING;;
    Cy_SD_Host_SoftwareReset(base, CY_SD_HOST_RESET_CMD_LINE);
    Cy_SD_Host_SoftwareReset(base, CY_SD_HOST_RESET_DATALINE);
    cyhal_system_delay_us(1);
    base->CORE.SW_RST_R = 0U;
}

cy_rslt_t cyhal_sdhc_init_hw(cyhal_sdhc_t *obj,
                        const cyhal_sdhc_config_t *config,
                        cyhal_gpio_t cmd,
                        cyhal_gpio_t clk,
                        cyhal_gpio_t data0,
                        cyhal_gpio_t data1,
                        cyhal_gpio_t data2,
                        cyhal_gpio_t data3,
                        cyhal_gpio_t data4,
                        cyhal_gpio_t data5,
                        cyhal_gpio_t data6,
                        cyhal_gpio_t data7,
                        cyhal_gpio_t card_detect,
                        cyhal_gpio_t io_volt_sel,
                        cyhal_gpio_t card_pwr_en,
                        cyhal_gpio_t card_mech_write_prot,
                        cyhal_gpio_t led_ctrl,
                        cyhal_gpio_t emmc_reset)
{
    CY_ASSERT(NULL != obj);
    cy_rslt_t result = CY_RSLT_SUCCESS;

    obj->base = NULL;
    obj->resource.type = CYHAL_RSC_INVALID;

    obj->pin_clk = CYHAL_NC_PIN_VALUE;
    obj->pin_cmd = CYHAL_NC_PIN_VALUE;
    obj->pin_data[0] = CYHAL_NC_PIN_VALUE;
    obj->pin_data[1] = CYHAL_NC_PIN_VALUE;
    obj->pin_data[2] = CYHAL_NC_PIN_VALUE;
    obj->pin_data[3] = CYHAL_NC_PIN_VALUE;
    obj->pin_data[4] = CYHAL_NC_PIN_VALUE;
    obj->pin_data[5] = CYHAL_NC_PIN_VALUE;
    obj->pin_data[6] = CYHAL_NC_PIN_VALUE;
    obj->pin_data[7] = CYHAL_NC_PIN_VALUE;
    obj->pin_card_detect = CYHAL_NC_PIN_VALUE;
    obj->pin_io_vol_sel = CYHAL_NC_PIN_VALUE;
    obj->pin_card_pwr_en = CYHAL_NC_PIN_VALUE;
    obj->pin_card_mech_write_prot = CYHAL_NC_PIN_VALUE;
    obj->pin_led_ctrl = CYHAL_NC_PIN_VALUE;
    obj->pin_emmc_reset = CYHAL_NC_PIN_VALUE;

    obj->cd_gpio_cb_enabled = false;

    cyhal_gpio_t data[8];
    data[0] = data0;
    data[1] = data1;
    data[2] = data2;
    data[3] = data3;
    data[4] = data4;
    data[5] = data5;
    data[6] = data6;
    data[7] = data7;

    obj->data_timeout_tout = _CYHAL_SDHC_TRANSFER_TIMEOUT;
    obj->data_timeout_card_clocks_user = 0;
    obj->data_timeout_auto_reconfig = false;

    /* Reserve SDHC */
    const cyhal_resource_pin_mapping_t *map = _CYHAL_UTILS_GET_RESOURCE(cmd, cyhal_pin_map_sdhc_card_cmd);

    if (NULL == map || NC == data0)
    {
        result = CYHAL_SDHC_RSLT_ERR_PIN;
    }

    /* Reserve and configure GPIO pins */
    if (result == CY_RSLT_SUCCESS)
    {
        result = _cyhal_sdxx_setup_pin(
            obj, cmd, cyhal_pin_map_sdhc_card_cmd, _CYHAL_SDHC_ELEM_COUNT(cyhal_pin_map_sdhc_card_cmd),
            &(obj->pin_cmd), _CYHAL_SDHC_NOT_WEAK_FUNC);
    }

    if (result == CY_RSLT_SUCCESS)
    {
        result = _cyhal_sdxx_setup_pin(
            obj, clk, cyhal_pin_map_sdhc_clk_card, _CYHAL_SDHC_ELEM_COUNT(cyhal_pin_map_sdhc_clk_card),
            &(obj->pin_clk), _CYHAL_SDHC_NOT_WEAK_FUNC);
    }

    for (uint8_t i = 0; (i < 4) && (CY_RSLT_SUCCESS == result); i++)
    {
        if (NC != data[i])
        {
            result = _cyhal_sdxx_setup_pin(obj, data[i], cyhal_pin_map_sdhc_card_dat_3to0,
                    _CYHAL_SDHC_ELEM_COUNT(cyhal_pin_map_sdhc_card_dat_3to0), &(obj->pin_data[i]),
                    _CYHAL_SDHC_NOT_WEAK_FUNC);
        }
    }

#if _CYHAL_SDHC_DATA8_PRESENT
    for (uint8_t i = 4; (i < 8) && (CY_RSLT_SUCCESS == result); i++)
    {
        if (NC != data[i])
        {
            result = _cyhal_sdxx_setup_pin(obj, data[i], cyhal_pin_map_sdhc_card_dat_7to4,
                    _CYHAL_SDHC_ELEM_COUNT(cyhal_pin_map_sdhc_card_dat_7to4), &(obj->pin_data[i]),
                    _CYHAL_SDHC_NOT_WEAK_FUNC);
        }
    }
#else
    if ((NC != data4 || NC != data5 || NC != data6 || NC != data7) && (CY_RSLT_SUCCESS == result))
    {
        result = CYHAL_SDHC_RSLT_ERR_PIN;
    }
#endif

    if ((NC != card_detect) && (CY_RSLT_SUCCESS == result))
    {
#if _CYHAL_SDHC_CARD_DETECT_PRESENT
        result = _cyhal_sdxx_setup_pin(obj, card_detect, cyhal_pin_map_sdhc_card_detect_n,
                _CYHAL_SDHC_ELEM_COUNT(cyhal_pin_map_sdhc_card_detect_n), &(obj->pin_card_detect),
                _CYHAL_SDHC_CARD_DETECT);
#else
        result = CYHAL_SDHC_RSLT_ERR_PIN;
#endif
    }

    if ((NC != io_volt_sel) && (CY_RSLT_SUCCESS == result))
    {
#if _CYHAL_SDHC_IO_VOLT_SEL_PRESENT
        result = _cyhal_sdxx_setup_pin(obj, io_volt_sel, cyhal_pin_map_sdhc_io_volt_sel,
            _CYHAL_SDHC_ELEM_COUNT(cyhal_pin_map_sdhc_io_volt_sel), &(obj->pin_io_vol_sel),
            _CYHAL_SDHC_CARD_IO_VOLTAGE);
#else
        result = CYHAL_SDHC_RSLT_ERR_PIN;
#endif
    }

    if ((NC != card_pwr_en) && (CY_RSLT_SUCCESS == result))
    {
#if _CYHAL_SDHC_CARD_IF_PWR_EN_PRESENT
        result = _cyhal_sdxx_setup_pin(obj, card_pwr_en, cyhal_pin_map_sdhc_card_if_pwr_en,
            _CYHAL_SDHC_ELEM_COUNT(cyhal_pin_map_sdhc_card_if_pwr_en), &(obj->pin_card_pwr_en), _CYHAL_SDHC_CARD_VDD);
#else
        result = CYHAL_SDHC_RSLT_ERR_PIN;
#endif
    }

    if ((NC != card_mech_write_prot) && (CY_RSLT_SUCCESS == result))
    {
#if _CYHAL_SDHC_CARD_WRITE_PROT_PRESENT
        result = _cyhal_sdxx_setup_pin(obj, card_mech_write_prot, cyhal_pin_map_sdhc_card_mech_write_prot,
            _CYHAL_SDHC_ELEM_COUNT(cyhal_pin_map_sdhc_card_mech_write_prot), &(obj->pin_card_mech_write_prot),
            _CYHAL_SDHC_CARD_MECH_WRITE_PROTECT);
#else
        result = CYHAL_SDHC_RSLT_ERR_PIN;
#endif
    }

    if ((NC != led_ctrl) && (CY_RSLT_SUCCESS == result))
    {
#if _CYHAL_SDHC_LED_CTRL_PRESENT
        result = _cyhal_sdxx_setup_pin(obj, led_ctrl, cyhal_pin_map_sdhc_led_ctrl,
            _CYHAL_SDHC_ELEM_COUNT(cyhal_pin_map_sdhc_led_ctrl), &(obj->pin_led_ctrl), _CYHAL_SDHC_NOT_WEAK_FUNC);
#else
        result = CYHAL_SDHC_RSLT_ERR_PIN;
#endif
    }

    if ((NC != emmc_reset) && (CY_RSLT_SUCCESS == result))
    {
#if _CYHAL_SDHC_CARD_EMMC_RESET_PRESENT
        result = _cyhal_sdxx_setup_pin(obj, emmc_reset, cyhal_pin_map_sdhc_card_emmc_reset_n,
            _CYHAL_SDHC_ELEM_COUNT(cyhal_pin_map_sdhc_card_emmc_reset_n), &(obj->pin_emmc_reset),
            _CYHAL_SDHC_NOT_WEAK_FUNC);
#else
        result = CYHAL_SDHC_RSLT_ERR_PIN;
#endif
    }

    if (CY_RSLT_SUCCESS == result)
    {
        result = _cyhal_utils_allocate_clock(&(obj->clock), map->inst, CYHAL_CLOCK_BLOCK_PERIPHERAL_8BIT, true);

        if (CY_RSLT_SUCCESS == result)
        {
            result = _cyhal_utils_set_clock_frequency2(&(obj->clock), MAX_FREQUENCY, &CYHAL_CLOCK_TOLERANCE_5_P);
        }

        if (CY_RSLT_SUCCESS == result && !cyhal_clock_is_enabled(&(obj->clock)))
        {
            result = cyhal_clock_set_enabled(&(obj->clock), true, true);
        }
    }

    if (CY_RSLT_SUCCESS == result)
    {
        obj->resource = *map->inst;
        result = cyhal_hwmgr_reserve(&obj->resource);
    }

        if (CY_RSLT_SUCCESS == result)
        {
            obj->base = _CYHAL_SDHC_BASE_ADDRESSES[obj->resource.block_num];
            stored_objects[obj->resource.block_num] = (void *)obj;
            _cyhal_sdhc_data_transfer_status[obj->resource.block_num] = _CYHAL_SDHC_NOT_RUNNING;
            #if defined(CY_RTOS_AWARE) || defined(COMPONENT_RTOS_AWARE)
            _cyhal_sdxx_sema_status[obj->resource.block_num] = _CYHAL_SDXX_SEMA_NOT_INITED;
            #endif /* defined(CY_RTOS_AWARE) || defined(COMPONENT_RTOS_AWARE) */

        /* Enable the SDHC block */
        Cy_SD_Host_Enable(obj->base);

        /* Do not support eMMC card */
        obj->emmc = config->isEmmc;
        obj->dma_type = CY_SD_HOST_DMA_ADMA2;

        /* Configure SD Host to operate */
        cy_stc_sd_host_init_config_t hostConfig;
        hostConfig.dmaType = obj->dma_type;
        hostConfig.emmc = obj->emmc;

    #if _CYHAL_SDHC_LED_CTRL_PRESENT
        hostConfig.enableLedControl = (NC != led_ctrl);
    #else
        hostConfig.enableLedControl = false;
    #endif

        obj->context.cardType = CY_SD_HOST_NOT_EMMC;

        obj->irq_cause = 0UL;
        obj->callback_data.callback = NULL;
        obj->callback_data.callback_arg = NULL;
        _cyhal_sdhc_config_structs[obj->resource.block_num] = obj;

        IRQn_Type irqn = _CYHAL_SDHC_IRQ_N[obj->resource.block_num];
        cy_stc_sysint_t irqCfg = { irqn, CYHAL_ISR_PRIORITY_DEFAULT };
        Cy_SysInt_Init(&irqCfg, _cyhal_sdhc_irq_handler);
        NVIC_EnableIRQ(irqn);

        result = (cy_rslt_t)Cy_SD_Host_Init(obj->base, &hostConfig, &obj->context);
    }

    if(result == CY_RSLT_SUCCESS)
    {
        obj->pm_transition_pending = false;
        obj->pm_callback_data.callback = &_cyhal_sdhc_syspm_callback,
        obj->pm_callback_data.states =
                (cyhal_syspm_callback_state_t)(CYHAL_SYSPM_CB_CPU_DEEPSLEEP | CYHAL_SYSPM_CB_SYSTEM_HIBERNATE);
        obj->pm_callback_data.next = NULL;
        obj->pm_callback_data.args = obj;
        /* The CYHAL_SYSPM_BEFORE_TRANSITION mode cannot be ignored because the PM handler
            * calls the PDL deep-sleep callback that disables the block in this mode before transitioning.
            */
        obj->pm_callback_data.ignore_modes = (cyhal_syspm_callback_mode_t)0,

        _cyhal_syspm_register_peripheral_callback(&obj->pm_callback_data);

        obj->low_voltage_io_desired = config->lowVoltageSignaling;
        obj->bus_width = config->busWidth;
    }

    if (result != CY_RSLT_SUCCESS)
    {
        cyhal_sdhc_free(obj);
    }

    return result;
}

cy_rslt_t cyhal_sdhc_init_card(cyhal_sdhc_t *obj)
{
    CY_ASSERT(NULL != obj);
    CY_ASSERT(NULL != obj->base);

    cy_stc_sd_host_sd_card_config_t stcSdcardCfg;
    cy_en_sd_host_card_capacity_t cardCapacity;
    uint32_t rca = 0UL;
    cy_en_sd_host_card_type_t cardType = CY_SD_HOST_UNUSABLE;

    /* Initialize the SDIO/SD/eMMC card configuration structure */
    stcSdcardCfg.lowVoltageSignaling = obj->low_voltage_io_desired;
    stcSdcardCfg.busWidth = _cyhal_sdhc_convert_buswidth(obj->bus_width);
    stcSdcardCfg.cardType = &cardType;
    stcSdcardCfg.rca = &rca;
    stcSdcardCfg.cardCapacity = &cardCapacity;

    /* Initialize the card */
    cy_rslt_t result = (cy_rslt_t)Cy_SD_Host_InitCard(obj->base, &stcSdcardCfg, &obj->context);

    /* Update SD Card frequency to be 25 Mhz */
    if (CY_RSLT_SUCCESS == result)
    {
        result = cyhal_sdhc_set_frequency(obj, CY_SD_HOST_CLK_25M, true);
    }

    return result;
}

cy_rslt_t cyhal_sdhc_init(cyhal_sdhc_t *obj,
                          const cyhal_sdhc_config_t *config,
                          cyhal_gpio_t cmd,
                          cyhal_gpio_t clk,
                          cyhal_gpio_t data0,
                          cyhal_gpio_t data1,
                          cyhal_gpio_t data2,
                          cyhal_gpio_t data3,
                          cyhal_gpio_t data4,
                          cyhal_gpio_t data5,
                          cyhal_gpio_t data6,
                          cyhal_gpio_t data7,
                          cyhal_gpio_t card_detect,
                          cyhal_gpio_t io_volt_sel,
                          cyhal_gpio_t card_pwr_en,
                          cyhal_gpio_t card_mech_write_prot,
                          cyhal_gpio_t led_ctrl,
                          cyhal_gpio_t emmc_reset)
{
    cy_rslt_t result = cyhal_sdhc_init_hw(obj, config, cmd, clk, data0, data1, data2, data3, data4, data5, data6,
            data7, card_detect, io_volt_sel, card_pwr_en, card_mech_write_prot, led_ctrl, emmc_reset);

    /* Initialize card */
    if (result == CY_RSLT_SUCCESS)
    {
        result = cyhal_sdhc_init_card(obj);
    }

    return result;
}

void cyhal_sdhc_free(cyhal_sdhc_t *obj)
{
    CY_ASSERT(NULL != obj);

    if (NULL != obj->base)
    {
        IRQn_Type irqn = _CYHAL_SDHC_IRQ_N[obj->resource.block_num];
        NVIC_DisableIRQ(irqn);
        #if defined(CY_RTOS_AWARE) || defined(COMPONENT_RTOS_AWARE)
        if (_CYHAL_SDXX_SEMA_NOT_INITED != _cyhal_sdxx_sema_status[obj->resource.block_num])
        {
            cy_rtos_deinit_semaphore(&_cyhal_sdhc_smphr_xfer_done[obj->resource.block_num]);
            _cyhal_sdxx_sema_status[obj->resource.block_num] = _CYHAL_SDXX_SEMA_NOT_INITED;
        }
        #endif /* CY_RTOS_AWARE or COMPONENT_RTOS_AWARE defined */

        Cy_SD_Host_DeInit(obj->base);
        stored_objects[obj->resource.block_num] = NULL;
        _cyhal_sdhc_data_transfer_status[obj->resource.block_num] = _CYHAL_SDHC_NOT_RUNNING;

        cyhal_hwmgr_free(&(obj->resource));
        obj->base = NULL;

        _cyhal_syspm_unregister_peripheral_callback(&obj->pm_callback_data);
    }

    if (obj->resource.type != CYHAL_RSC_INVALID)
    {
        cyhal_clock_free(&(obj->clock));
        obj->resource.type = CYHAL_RSC_INVALID;
    }

    /* Free pins */
    _cyhal_utils_release_if_used(&(obj->pin_cmd));
    _cyhal_utils_release_if_used(&(obj->pin_clk));

#if _CYHAL_SDHC_DATA8_PRESENT
    const uint8_t max_idx = 8;
#else
    const uint8_t max_idx = 4;
#endif
    for (uint8_t i = 0; i < max_idx; i++)
    {
        _cyhal_utils_release_if_used(&(obj->pin_data[i]));
    }

#if _CYHAL_SDHC_CARD_DETECT_PRESENT
    if ((obj->cd_gpio_cb_enabled) && (CYHAL_NC_PIN_VALUE != obj->pin_card_detect))
    {
        cyhal_gpio_enable_event(obj->pin_card_detect, CYHAL_GPIO_IRQ_BOTH, CYHAL_ISR_PRIORITY_DEFAULT, false);
        obj->cd_gpio_cb_enabled = false;
    }
    _cyhal_utils_release_if_used(&(obj->pin_card_detect));
#endif

#if _CYHAL_SDHC_IO_VOLT_SEL_PRESENT
    _cyhal_utils_release_if_used(&(obj->pin_io_vol_sel));
#endif

#if _CYHAL_SDHC_CARD_IF_PWR_EN_PRESENT
    _cyhal_utils_release_if_used(&(obj->pin_card_pwr_en));
#endif

#if _CYHAL_SDHC_CARD_WRITE_PROT_PRESENT
    _cyhal_utils_release_if_used(&(obj->pin_card_mech_write_prot));
#endif

#if _CYHAL_SDHC_LED_CTRL_PRESENT
    _cyhal_utils_release_if_used(&(obj->pin_led_ctrl));
#endif

#if _CYHAL_SDHC_CARD_EMMC_RESET_PRESENT
    _cyhal_utils_release_if_used(&(obj->pin_emmc_reset));
#endif
}

cy_rslt_t cyhal_sdhc_read(const cyhal_sdhc_t *obj, uint32_t address, uint8_t *data, size_t *length)
{
    CY_ASSERT(_CYHAL_SDHC_NOT_RUNNING == _cyhal_sdhc_data_transfer_status[obj->resource.block_num]);
    _cyhal_sdxx_setup_smphr(obj->resource.block_num);
    cy_rslt_t ret = cyhal_sdhc_read_async(obj, address, data, length);
    /* Waiting for async operation to end */
    if (CY_RSLT_SUCCESS == ret)
    {
        ret = _cyhal_sdxx_waitfor_transfer_complete(obj->resource.block_num, obj->base, false);
    }
    if(CY_RSLT_SUCCESS != ret)
    {
        _cyhal_sdhc_data_transfer_status[obj->resource.block_num] = _CYHAL_SDHC_NOT_RUNNING;
    }
    return ret;
}

cy_rslt_t cyhal_sdhc_write(const cyhal_sdhc_t *obj, uint32_t address, const uint8_t *data, size_t *length)
{
    CY_ASSERT(_CYHAL_SDHC_NOT_RUNNING == _cyhal_sdhc_data_transfer_status[obj->resource.block_num]);
    _cyhal_sdxx_setup_smphr(obj->resource.block_num);
    cy_rslt_t ret = cyhal_sdhc_write_async(obj, address, data, length);
    /* Waiting for async operation to end */
    if (CY_RSLT_SUCCESS == ret)
    {
        ret = _cyhal_sdxx_waitfor_transfer_complete(obj->resource.block_num, obj->base, false);
    }
    if(CY_RSLT_SUCCESS != ret)
    {
        _cyhal_sdhc_data_transfer_status[obj->resource.block_num] = _CYHAL_SDHC_NOT_RUNNING;
    }
    return ret;
}

cy_rslt_t cyhal_sdhc_erase(const cyhal_sdhc_t *obj, uint32_t start_addr, size_t length)
{
    if (obj->pm_transition_pending)
    {
        return CYHAL_SYSPM_RSLT_ERR_PM_PENDING;
    }
    if (0 == length)
    {
        return CYHAL_SDHC_RSLT_ERR_WRONG_PARAM;
    }

    cy_rslt_t ret = CY_RSLT_SUCCESS;
    cy_en_sd_host_erase_type_t eraseType = CY_SD_HOST_ERASE_ERASE;
    uint32_t i = _CYHAL_SDHC_RETRY_TIMES;
    uint32_t cardStatus;

    if (obj->emmc)
    {
         eraseType = CY_SD_HOST_ERASE_TRIM;
    }

    /* First clear out the transfer and command complete statuses */
    Cy_SD_Host_ClearNormalInterruptStatus(obj->base, (CY_SD_HOST_XFER_COMPLETE | CY_SD_HOST_CMD_COMPLETE));

    if (obj->irq_cause & CYHAL_SDHC_CMD_COMPLETE)
    {
        /*  Enabling command complete interrupt mask if corresponding event was enabled by user
        *   _cyhal_sdhc_irq_handler will disable CY_SD_HOST_CMD_COMPLETE mask once interrupt
        *   is generated. */
        Cy_SD_Host_SetNormalInterruptMask(obj->base, Cy_SD_Host_GetNormalInterruptMask(obj->base) |
                CY_SD_HOST_CMD_COMPLETE);
    }

    ret = (cy_rslt_t)Cy_SD_Host_Erase(obj->base, start_addr, (start_addr + length - 1), eraseType, &obj->context);

    if (CY_RSLT_SUCCESS == ret)
    {
        ret =  (cy_rslt_t)_cyhal_sdxx_pollcmdcomplete(obj->base, obj->resource.block_num);
    }

    if (CY_RSLT_SUCCESS == ret)
    {
        if (false == obj->emmc)
        {
            /* polling result */
            ret = CYHAL_SDHC_RSLT_ERR_ERASE_CMPLT_TIMEOUT;

            while (i-- > 0UL)
            {
                cardStatus = Cy_SD_Host_GetCardStatus(obj->base, &obj->context);
                if (((CY_SD_HOST_CARD_TRAN << CY_SD_HOST_CMD13_CURRENT_STATE) |
                    (1UL << CY_SD_HOST_CMD13_READY_FOR_DATA)) == cardStatus)
                {
                    ret = CY_RSLT_SUCCESS;
                    break;
                }
                cyhal_system_delay_us(_CYHAL_SDHC_FUJE_TIMEOUT_MS); /* The Fuje timeout for one block. */
            }
        }
        else
        {
            cyhal_system_delay_ms(_CYHAL_SDHC_EMMC_TRIM_DELAY_MS);
        }
    }

    return ret;
}

cy_rslt_t cyhal_sdhc_read_async(const cyhal_sdhc_t *obj, uint32_t address, uint8_t *data, size_t *length)
{
    if (obj->pm_transition_pending)
    {
        return CYHAL_SYSPM_RSLT_ERR_PM_PENDING;
    }

    cy_rslt_t ret;
    cy_stc_sd_host_write_read_config_t dataConfig;

    /* The pointer to data. */
    dataConfig.data = (uint32_t*)data;
    /* The address to write/read data on the card or eMMC. */
    dataConfig.address = address;
    /* The number of blocks to write/read. */
    dataConfig.numberOfBlocks = (uint32_t)*length;
    dataConfig.autoCommand = (1UL == (uint32_t)*length)
        ? CY_SD_HOST_AUTO_CMD_NONE
        : CY_SD_HOST_AUTO_CMD_AUTO;
    /* The timeout value for the transfer. */
    dataConfig.dataTimeout = obj->data_timeout_tout;
    /* For EMMC cards enable reliable write. */
    dataConfig.enReliableWrite = false;
    dataConfig.enableDma = true;

    /* First clear out the transfer and command complete statuses */
    Cy_SD_Host_ClearNormalInterruptStatus(obj->base, (CY_SD_HOST_XFER_COMPLETE | CY_SD_HOST_CMD_COMPLETE));

    ret = _cyhal_sdxx_prepare_for_transfer(obj->base, false);

    if (CY_RSLT_SUCCESS == ret)
    {
        // The PDL takes care of the cmd complete stage in sdhc mode so we can jump straight
        // to waiting for the xfer complete
        _cyhal_sdhc_data_transfer_status[obj->resource.block_num] = _CYHAL_SDHC_WAIT_XFER_COMPLETE;
        ret = (cy_rslt_t)Cy_SD_Host_Read(obj->base, &dataConfig, &obj->context);
        /* Read operation failed */
        if (CY_RSLT_SUCCESS != ret)
        {
            _cyhal_sdhc_data_transfer_status[obj->resource.block_num] = _CYHAL_SDHC_NOT_RUNNING;
        }
    }

    return ret;
}

cy_rslt_t cyhal_sdhc_write_async(const cyhal_sdhc_t *obj, uint32_t address, const uint8_t *data, size_t *length)
{
    if (obj->pm_transition_pending)
    {
        return CYHAL_SYSPM_RSLT_ERR_PM_PENDING;
    }

    cy_rslt_t ret;
    cy_stc_sd_host_write_read_config_t dataConfig;

    /* The pointer to data. */
    dataConfig.data = (uint32_t*)data;
    /* The address to write/read data on the card or eMMC. */
    dataConfig.address = address;
    /* The number of blocks to write/read. */
    dataConfig.numberOfBlocks = (uint32_t)*length;
    dataConfig.autoCommand = (1UL == (uint32_t)*length)
        ? CY_SD_HOST_AUTO_CMD_NONE
        : CY_SD_HOST_AUTO_CMD_AUTO;
    /* The timeout value for the transfer. */
    dataConfig.dataTimeout = obj->data_timeout_tout;
    /* For EMMC cards enable reliable write. */
    dataConfig.enReliableWrite = false;
    dataConfig.enableDma = true;

    /* First clear out the transfer and command complete statuses */
    Cy_SD_Host_ClearNormalInterruptStatus(obj->base, (CY_SD_HOST_XFER_COMPLETE | CY_SD_HOST_CMD_COMPLETE));

    ret = _cyhal_sdxx_prepare_for_transfer(obj->base, false);

    if (CY_RSLT_SUCCESS == ret)
    {
        // The PDL takes care of the cmd complete stage in sdhc mode so we can jump straight
        // to waiting for the xfer complete
        _cyhal_sdhc_data_transfer_status[obj->resource.block_num] = _CYHAL_SDHC_WAIT_XFER_COMPLETE;
        ret = (cy_rslt_t)Cy_SD_Host_Write(obj->base, &dataConfig, &obj->context);
        /* Write operation failed */
        if (CY_RSLT_SUCCESS != ret)
        {
            _cyhal_sdhc_data_transfer_status[obj->resource.block_num] = _CYHAL_SDHC_NOT_RUNNING;
        }
    }

    return ret;
}

bool cyhal_sdhc_is_busy(const cyhal_sdhc_t *obj)
{
    bool busy_status = true;
    /* Check DAT Line Active */
    uint32_t pState = Cy_SD_Host_GetPresentState(obj->base);
    if ((CY_SD_HOST_DAT_LINE_ACTIVE != (pState & CY_SD_HOST_DAT_LINE_ACTIVE)) &&
        (CY_SD_HOST_CMD_CMD_INHIBIT_DAT != (pState & CY_SD_HOST_CMD_CMD_INHIBIT_DAT)))
    {
        busy_status = false;
    }
    return busy_status || (_CYHAL_SDHC_NOT_RUNNING != _cyhal_sdhc_data_transfer_status[obj->resource.block_num]);
}

cy_rslt_t cyhal_sdhc_abort_async(const cyhal_sdhc_t *obj)
{
    cy_rslt_t ret = (cy_rslt_t)Cy_SD_Host_AbortTransfer(obj->base, &obj->context);
    if (CY_RSLT_SUCCESS == ret)
    {
        _cyhal_sdhc_data_transfer_status[obj->resource.block_num] = _CYHAL_SDHC_NOT_RUNNING;
    }
    return ret;
}

void cyhal_sdhc_register_callback(cyhal_sdhc_t *obj, cyhal_sdhc_event_callback_t callback, void *callback_arg)
{
    uint32_t savedIntrStatus = cyhal_system_critical_section_enter();
    obj->callback_data.callback = (cy_israddress) callback;
    obj->callback_data.callback_arg = callback_arg;
    cyhal_system_critical_section_exit(savedIntrStatus);
}

void cyhal_sdhc_enable_event(cyhal_sdhc_t *obj, cyhal_sdhc_event_t event, uint8_t intr_priority, bool enable)
{
    uint32_t interruptMask = Cy_SD_Host_GetNormalInterruptMask(obj->base);

    IRQn_Type irqn = _CYHAL_SDHC_IRQ_N[obj->resource.block_num];
    NVIC_SetPriority(irqn, intr_priority);
    uint32_t map_count = sizeof(_cyhal_sdhc_event_map) / sizeof(uint32_t);

    /* Specific interrupt */
    if ((uint32_t) event < (uint32_t) CYHAL_SDHC_ALL_INTERRUPTS)
    {
        for (uint8_t i = 1; i < map_count; i++)
        {
            if ((_cyhal_sdhc_event_map[i] & (uint32_t) event) != 0)
            {
                if (enable)
                    interruptMask |= (1 << (i - 1));
                else
                    interruptMask &= ~(1 << (i - 1));
            }
        }
    }
    /* All interrupts */
    else
    {
        interruptMask = (enable) ? CYHAL_SDHC_ALL_INTERRUPTS : 0;
    }

    obj->irq_cause = interruptMask;
    /*  CY_SD_HOST_CMD_COMPLETE and CY_SD_HOST_XFER_COMPLETE cannot be always enabled because of SD Host driver limitations.
    *   SDHC HAL transfer APIs are taking care of enabling these statuses. CY_SD_HOST_CMD_COMPLETE is only
    *   enabled if user callback is enabled while CY_SD_HOST_XFER_COMPLETE is enabled by transfer API regardless it was enabled
    *   by user or not. */
    interruptMask &= ((uint32_t) ~CY_SD_HOST_CMD_COMPLETE) & ((uint32_t) ~CY_SD_HOST_XFER_COMPLETE);
    Cy_SD_Host_SetNormalInterruptMask(obj->base, interruptMask);
}

bool cyhal_sdhc_is_card_inserted(const cyhal_sdhc_t *obj)
{
    CY_ASSERT(NULL != obj);
    CY_ASSERT(NULL != obj->base);
    return Cy_SD_Host_IsCardConnected(obj->base);
}

bool cyhal_sdhc_is_card_mech_write_protected(const cyhal_sdhc_t *obj)
{
    CY_ASSERT(NULL != obj);
    CY_ASSERT(NULL != obj->base);
    return Cy_SD_Host_IsWpSet(obj->base);
}

cy_rslt_t cyhal_sdhc_get_block_count(cyhal_sdhc_t *obj, uint32_t * block_count)
{
    CY_ASSERT(NULL != obj);
    CY_ASSERT(NULL != block_count);

    cy_rslt_t result = CYHAL_SDHC_RSLT_ERR_BLOCK_COUNT_GET_FAILURE;

    if ((NULL != obj->base) && ((CY_SD_HOST_SD == obj->context.cardType) || (CY_SD_HOST_EMMC == obj->context.cardType)))
    {
        *block_count = obj->context.maxSectorNum;
        result = CY_RSLT_SUCCESS;
    }

    return result;
}

cy_rslt_t cyhal_sdhc_set_frequency(cyhal_sdhc_t *obj, uint32_t hz, bool negotiate)
{
    CY_ASSERT(NULL != obj);
    cy_rslt_t result = CYHAL_SDHC_RSLT_ERR_SET_FREQ;
    if (NULL != obj->base)
    {
        /*  Assigning for desired freq as for now and this variable
         *  will be updated with achieved freq. */
        uint32_t actual_freq = hz;
        result = _cyhal_sdxx_sdcardchangeclock(obj, false, &actual_freq, obj->low_voltage_io_set, negotiate);
        if (CY_RSLT_SUCCESS == result)
        {
            obj->bus_frequency_hz = actual_freq;
            if (obj->data_timeout_auto_reconfig && (0 != obj->data_timeout_card_clocks_user))
            {
                /* User have data timeout configured, we need to reconfigure it according to new card clock */
                result = cyhal_sdhc_set_data_read_timeout(obj, obj->data_timeout_card_clocks_user, true);
            }
        }
    }
    return result;
}

uint32_t cyhal_sdhc_get_frequency(cyhal_sdhc_t *obj)
{
    CY_ASSERT(NULL != obj);
    return obj->bus_frequency_hz;
}

cy_rslt_t cyhal_sdhc_set_data_read_timeout(cyhal_sdhc_t *obj, uint32_t timeout, bool auto_reconfigure)
{
    CY_ASSERT(NULL != obj);

    uint32_t current_card_clock = cyhal_sdhc_get_frequency(obj);

    /* TMCLK works on 1 MHz in current block implementation if corresponding HF clock is 100 MHz.
    *  This is defined in registers (TOUT_CLK_FREQ[5:0] = 1 and TOUT_CLK_UNIT[7] = 1).  */
    float tout_clk_period_us = _CYHAL_SDHC_EXPECTED_BASE_CLK_FREQ_HZ / (float)obj->block_source_freq_hz;

    uint32_t user_needs_us = (((uint64_t)timeout * 1000000) + current_card_clock - 1) / current_card_clock;

    /* Timeout range from 0x0 to 0xE is valid for PSoC6, which corresponds to
    * TMCLK x 2^13 for 0, TMCLK x 2^14 for 1, ..., TMCLK x 2^27 for 0xE. 0xF is reserved. */
    const uint8_t tout_clk_power_base = _CYHAL_SDHC_TOUT_TMCLK_POW_MIN;
    for (uint8_t tmclk_power = tout_clk_power_base; tmclk_power <= _CYHAL_SDHC_TOUT_TMCLK_POW_MAX; tmclk_power++)
    {
        if (tout_clk_period_us * (1 << tmclk_power) >= user_needs_us)
        {
            obj->data_timeout_tout = tmclk_power - tout_clk_power_base;
            obj->data_timeout_card_clocks_user = timeout;
            obj->data_timeout_auto_reconfig = auto_reconfigure;
            return CY_RSLT_SUCCESS;
        }
    }
    return CYHAL_SDHC_RSLT_ERR_TOUT_CFG;
}

cy_rslt_t cyhal_sdhc_config_data_transfer(cyhal_sdhc_t *obj, cyhal_sdhc_data_config_t *data_config)
{
    CY_ASSERT(NULL != obj);
    CY_ASSERT(NULL != obj->base);
    CY_ASSERT(NULL != data_config);

    if (NULL == data_config->data_ptr)
    {
        return CYHAL_SDHC_RSLT_ERR_WRONG_PARAM;
    }

    cy_stc_sd_host_data_config_t dataConfig = {
        .blockSize = data_config->block_size,
        .numberOfBlock = data_config->number_of_blocks,
        .enableDma = true,
        .autoCommand = (cy_en_sd_host_auto_cmd_t)data_config->auto_command,
        .read = data_config->is_read,
        /* .data is skipped to configure adma2 descriptor for it later */
        .dataTimeout = obj->data_timeout_tout,
        .enableIntAtBlockGap = false,
        .enReliableWrite = false
    };

    uint32_t length = data_config->block_size * data_config->number_of_blocks;

    obj->adma_descriptor_tbl[0] = (1UL << CY_SD_HOST_ADMA_ATTR_VALID_POS) | /* Attr Valid */
                            (1UL << CY_SD_HOST_ADMA_ATTR_END_POS) |   /* Attr End */
                            (0UL << CY_SD_HOST_ADMA_ATTR_INT_POS) |   /* Attr Int */
                            (CY_SD_HOST_ADMA_TRAN << CY_SD_HOST_ADMA_ACT_POS) |
                            (length << CY_SD_HOST_ADMA_LEN_POS);     /* Len */

    obj->adma_descriptor_tbl[1] = (uint32_t)data_config->data_ptr;

    /* The address of the ADMA descriptor table. */
    dataConfig.data = (uint32_t*)&obj->adma_descriptor_tbl[0];

    return (cy_rslt_t) Cy_SD_Host_InitDataTransfer(obj->base, &dataConfig);
}

cy_rslt_t cyhal_sdhc_send_cmd(cyhal_sdhc_t *obj, cyhal_sdhc_cmd_config_t *cmd_config)
{
    CY_ASSERT(NULL != obj);
    CY_ASSERT(NULL != obj->base);
    CY_ASSERT(NULL != cmd_config);

    cy_rslt_t result = CY_RSLT_SUCCESS;
    bool cmd_data_present = (NULL != cmd_config->data_config);

    cy_stc_sd_host_cmd_config_t cmd = {
        .commandIndex = cmd_config->command_index,
        .commandArgument = cmd_config->command_argument,
        .enableCrcCheck = cmd_config->enable_crc_check,
        .enableAutoResponseErrorCheck = false,
        .respType = (cy_en_sd_host_response_type_t)cmd_config->response_type,
        .enableIdxCheck = cmd_config->enable_idx_check,
        .dataPresent = cmd_data_present,
        .cmdType = (cy_en_sd_host_cmd_type_t)cmd_config->command_type
    };

    /* First clear out the transfer and command complete statuses */
    Cy_SD_Host_ClearNormalInterruptStatus(obj->base, (CY_SD_HOST_XFER_COMPLETE | CY_SD_HOST_CMD_COMPLETE));

    if (cmd_data_present)
    {
        result = _cyhal_sdxx_prepare_for_transfer(obj->base, false);
    }

    if (CY_RSLT_SUCCESS == result)
    {
        if (cmd_data_present)
        {
            /* Data will be transfered in scope of this command, so setting _CYHAL_SDHC_WAIT_BOTH (cmd and data)
            *  and seting up xfer complete semaphore */
            _cyhal_sdhc_data_transfer_status[obj->resource.block_num] = _CYHAL_SDHC_WAIT_BOTH;
            _cyhal_sdxx_setup_smphr(obj->resource.block_num);
        }
        else
        {
            /* No data is transfered in this command, just waiting for cmd to complete */
            _cyhal_sdhc_data_transfer_status[obj->resource.block_num] = _CYHAL_SDHC_WAIT_CMD_COMPLETE;
        }
        result = (cy_rslt_t)Cy_SD_Host_SendCommand(obj->base, &cmd);
    }

    if (CY_RSLT_SUCCESS == result)
    {
        result = (cy_rslt_t)_cyhal_sdxx_pollcmdcomplete(obj->base, obj->resource.block_num);
    }

    if (CY_RSLT_SUCCESS != result)
    {
        /* Transfer failed */
        _cyhal_sdhc_data_transfer_status[obj->resource.block_num] = _CYHAL_SDHC_NOT_RUNNING;
    }

    return result;
}

cy_rslt_t cyhal_sdhc_get_response(cyhal_sdhc_t *obj, uint32_t *response, bool large_response)
{
    CY_ASSERT(NULL != obj);
    CY_ASSERT(NULL != obj->base);
    CY_ASSERT(NULL != response);
    return (cy_rslt_t)Cy_SD_Host_GetResponse(obj->base, response, large_response);
}

cy_rslt_t cyhal_sdhc_wait_transfer_complete(cyhal_sdhc_t *obj)
{
    CY_ASSERT(NULL != obj);
    CY_ASSERT(NULL != obj->base);
    return _cyhal_sdxx_waitfor_transfer_complete(obj->resource.block_num, obj->base, false);
}

static cy_rslt_t _cyhal_sdhc_io_volt_switch_seq(cyhal_sdhc_t *obj)
{
    CY_ASSERT(NULL != obj);
    CY_ASSERT(NULL != obj->base);

    uint32_t p_state;
    cy_rslt_t result = CY_RSLT_SUCCESS;

    /* Disable providing the SD Clock. */
    Cy_SD_Host_DisableSdClk(obj->base);

    p_state = Cy_SD_Host_GetPresentState(obj->base) & SDHC_CORE_PSTATE_REG_DAT_3_0_Msk;

    /* Check DAT[3:0]. */
    if (0UL == p_state)
    {
        /* Switch the bus to 1.8 V (Set the IO_VOLT_SEL pin to low)*/
        Cy_SD_Host_ChangeIoVoltage(obj->base, CY_SD_HOST_IO_VOLT_1_8V);

        /* Wait 10 ms to 1.8 voltage regulator to be stable. */
        cyhal_system_delay_ms(_CYHAL_SDHC_1_8_REG_STABLE_TIME_MS);

        /* Check the 1.8V signaling enable. */
        if (true == _FLD2BOOL(SDHC_CORE_HOST_CTRL2_R_SIGNALING_EN,
                            SDHC_CORE_HOST_CTRL2_R(obj->base)))
        {
            /* Enable providing the SD Clock. */
            Cy_SD_Host_EnableSdClk(obj->base);

            /* Wait for the stable CLK */
            cyhal_system_delay_ms(CY_SD_HOST_CLK_RAMP_UP_TIME_MS);

            p_state = Cy_SD_Host_GetPresentState(obj->base) & SDHC_CORE_PSTATE_REG_DAT_3_0_Msk;

            /* Check DAT[3:0]. */
            if (SDHC_CORE_PSTATE_REG_DAT_3_0_Msk != p_state)
            {
                result = CYHAL_SDHC_RSLT_ERR_IO_VOLT_SWITCH_SEQ;
            }
        }
        else
        {
            result = CYHAL_SDHC_RSLT_ERR_IO_VOLT_SWITCH_SEQ;
        }
    }
    else
    {
        result = CYHAL_SDHC_RSLT_ERR_IO_VOLT_SWITCH_SEQ;
    }

    return result;
}

/* Ncc - is time, needed to wait after certain commands before issuing next cmd.
*  Ncc is 8 clock cycles which for 400 kHz is 20 us */
static inline void _cyhal_sdhc_wait_ncc_time_at_400khz(void)
{
    cyhal_system_delay_us(20);
}

static cy_rslt_t _cyhal_sdhc_io_volt_negotiate(cyhal_sdhc_t *obj, cyhal_sdhc_io_voltage_t io_voltage)
{
    /* GO_IDLE (CMD0) command is issued in scope of Cy_SD_Host_SelBusVoltage, which resets
    *  SD bus to 1 bit bus width and 400 khz on the SD card side. Preparing host side for that. */

    /* 1 bit data bus width */
    cy_rslt_t result = cyhal_sdhc_set_bus_width(obj, 1, true);

    if (CY_RSLT_SUCCESS == result)
    {
        /* 400 khz sd bus clock */
        result = cyhal_sdhc_set_frequency(obj, 400000, false);
    }

    if (CY_RSLT_SUCCESS == result)
    {
        bool voltage_1v8 = (io_voltage == CYHAL_SDHC_IO_VOLTAGE_1_8V);
        result = (cy_rslt_t)Cy_SD_Host_SelBusVoltage(obj->base, voltage_1v8, &obj->context);
        /* Card is expected to be in "ready" state now */
    }

    if (CY_RSLT_SUCCESS == result)
    {
        /* GetCid (CMD2) will switch card from "ready" state to "ident" */
        uint32_t cid_reg[4];  /* The Device Identification register. */
        result = (cy_rslt_t)Cy_SD_Host_GetCid(obj->base, cid_reg);
        _cyhal_sdhc_wait_ncc_time_at_400khz();
    }

    if (CY_RSLT_SUCCESS == result)
    {
        /* Cy_SD_Host_GetRca (CMD3) will switch card from "ident" state to "stand-by" state */
        obj->context.RCA = Cy_SD_Host_GetRca(obj->base);
        _cyhal_sdhc_wait_ncc_time_at_400khz();

        /* CMD7 will switch card from "stand-by" state to "transfer" state */
        cyhal_sdhc_cmd_config_t cmd = {
            .command_index = 7,
            .command_argument = obj->context.RCA << _CYHAL_SDHC_RCA_SHIFT,
            .enable_crc_check = false,
            .response_type = CYHAL_SDHC_RESPONSE_LEN_48B,
            .enable_idx_check = false,
            .command_type = CYHAL_SDHC_CMD_NORMAL,
            .data_config = NULL
        };

        result = cyhal_sdhc_send_cmd(obj, &cmd);
    }

    if (CY_RSLT_SUCCESS == result)
    {
        _cyhal_sdhc_wait_ncc_time_at_400khz();
        result = _cyhal_sdxx_waitfor_transfer_complete(obj->resource.block_num, obj->base, false);
    }

    return result;
}

typedef struct
{
    en_hsiom_sel_t      clk;
    en_hsiom_sel_t      cmd;
    #if _CYHAL_SDHC_DATA8_PRESENT
    en_hsiom_sel_t      data[8];
    #else
    en_hsiom_sel_t      data[4];
    #endif
} _cyhal_sdhc_saved_lines_hsiom_t;

/* power cycle config single pin */
static void _cyhal_sdhc_pc_config_single_pin(cyhal_gpio_t *gpio_ptr, en_hsiom_sel_t *hsiom_ptr, bool set_line_low)
{
    GPIO_PRT_Type* port;
    uint8_t pin;

    cyhal_gpio_t gpio = *gpio_ptr;
    if (NC != gpio)
    {
        port = CYHAL_GET_PORTADDR(gpio);
        pin = (uint8_t)CYHAL_GET_PIN(gpio);
        if(set_line_low)
        {
            /* Switching HSIOM to GPIO and set pin to low state */
            *hsiom_ptr = Cy_GPIO_GetHSIOM(port, pin);
            Cy_GPIO_Clr(port, pin);
            Cy_GPIO_SetHSIOM(port, pin, HSIOM_SEL_GPIO);
        }
        else
        {
            /* Restore pin's HSIOM configuration for SDHC block signals */
            Cy_GPIO_SetHSIOM(port, pin, *hsiom_ptr);
        }
    }
}

/* Per SD Spec, during card power down, DAT, CMD and CLK lines should be disconnected or driven to logical 0
*  by the host to avoid operating current being drawn through the signal lines.
*  Lines are set low if set_lines_low true, pins configuration resored if set_lines_low false */
static void _cyhal_sdhc_power_cycle_config_lines(cyhal_sdhc_t *obj, _cyhal_sdhc_saved_lines_hsiom_t *pins_cfg,
    bool set_lines_low)
{
    _cyhal_sdhc_pc_config_single_pin(&obj->pin_clk, &pins_cfg->clk, set_lines_low);
    _cyhal_sdhc_pc_config_single_pin(&obj->pin_cmd, &pins_cfg->cmd, set_lines_low);
    for(size_t i = 0; i < sizeof(pins_cfg->data)/sizeof(pins_cfg->data[0]); ++i)
    {
        _cyhal_sdhc_pc_config_single_pin(&obj->pin_data[i], &pins_cfg->data[i], set_lines_low);
    }
}

static cy_rslt_t _cyhal_sdhc_card_power_cycle(cyhal_sdhc_t *obj)
{
    /* To perform reliable SD card hard reset, Card VDD should drop to below 0.5V for at least 1 ms */
    if (NC == obj->pin_card_pwr_en)
    {
        /* card_pwr_en needs to be provided in order to control card VDD */
        return CYHAL_SDHC_RSLT_ERR_PIN;
    }

    _cyhal_sdhc_saved_lines_hsiom_t pins_cfg;

    /* Drive signal lines logic 0 to avoid card being powered through signal lines */
    _cyhal_sdhc_power_cycle_config_lines(obj, &pins_cfg, true);
    /* Power down the card */
    cyhal_sdhc_enable_card_power(obj, false);
    /* Waiting for 1 ms per spec */
    cyhal_system_delay_us(1000);
    /* Restore signals configuration */
    _cyhal_sdhc_power_cycle_config_lines(obj, &pins_cfg, false);
    /* Power up the card */
    cyhal_sdhc_enable_card_power(obj, true);
    cyhal_system_delay_ms(_CYHAL_SDHC_PWR_RAMP_UP_TIME_MS);

    return CY_RSLT_SUCCESS;
}

cy_rslt_t cyhal_sdhc_set_io_voltage(cyhal_sdhc_t *obj, cyhal_sdhc_io_voltage_t io_voltage, cyhal_sdhc_io_volt_action_type_t io_switch_type)
{
    CY_ASSERT(NULL != obj);
    CY_ASSERT(NULL != obj->base);

    if (NC == obj->pin_io_vol_sel)
    {
        /* Need to have io_volt_sel pin configured in order to switch io voltage */
        return CYHAL_SDHC_RSLT_ERR_PIN;
    }

    if ((CY_SD_HOST_SD != obj->context.cardType) && (CYHAL_SDHC_IO_VOLT_ACTION_NONE != io_switch_type) &&
            (CYHAL_SDHC_IO_VOLTAGE_1_8V == io_voltage))
    {
        /* Negotiation or Voltage Switch sequence can only be performed for SD card */
        return CYHAL_SDHC_RSLT_ERR_UNSUPPORTED;
    }

    cy_rslt_t result = CY_RSLT_SUCCESS;

    switch (io_switch_type)
    {
        case CYHAL_SDHC_IO_VOLT_ACTION_NEGOTIATE:
        {
            /*  Bus width and card frequency settings are initialized to default during negotiation.
            *   Saving the parameters to restore them after I/O switch is done. */
            uint8_t sd_bus_width_before_switch = cyhal_sdhc_get_bus_width(obj);
            uint32_t sd_freq_before_switch = cyhal_sdhc_get_frequency(obj);

            /*  Once the card enters 1.8V signaling mode, it cannot be switched back to 3.3V signaling without power cycle. */
            if ((obj->low_voltage_io_set) && (CYHAL_SDHC_IO_VOLTAGE_3_3V == io_voltage))
            {
                result = _cyhal_sdhc_card_power_cycle(obj);
                if (CY_RSLT_SUCCESS == result)
                {
                    obj->low_voltage_io_desired = false;
                    result = cyhal_sdhc_init_card(obj);
                }
            }
            else
            {
                result = _cyhal_sdhc_io_volt_negotiate(obj, io_voltage);
            }

            /* Return back bus width and frequency regardless of volt select change status */
            (void)cyhal_sdhc_set_bus_width(obj, sd_bus_width_before_switch, true);
            (void)cyhal_sdhc_set_frequency(obj, sd_freq_before_switch, true);
            break;
        }

        case CYHAL_SDHC_IO_VOLT_ACTION_SWITCH_SEQ_ONLY:
            if (CYHAL_SDHC_IO_VOLTAGE_1_8V == io_voltage)
            {
                result = _cyhal_sdhc_io_volt_switch_seq(obj);
                break;
            }
            /* Intentionally left no break to switch IO to 3.3V as it is done for CYHAL_SDHC_IO_VOLT_ACTION_NONE */
            // fall through

        case CYHAL_SDHC_IO_VOLT_ACTION_NONE:
            Cy_SD_Host_ChangeIoVoltage(obj->base, (cy_en_sd_host_io_voltage_t)io_voltage);
            break;

        default:
            /* Illegal io_switch_type value provided */
            CY_ASSERT(false);
    }

    return result;
}

cyhal_sdhc_io_voltage_t cyhal_sdhc_get_io_voltage(cyhal_sdhc_t *obj)
{
    CY_ASSERT(NULL != obj);
    return obj->low_voltage_io_set ? CYHAL_SDHC_IO_VOLTAGE_1_8V : CYHAL_SDHC_IO_VOLTAGE_3_3V;;
}

static inline bool _cyhal_sdhc_is_buswidth_correct(uint8_t sd_data_bits)
{
    return ((1 == sd_data_bits) || (4 == sd_data_bits) || (8 == sd_data_bits));
}

cy_rslt_t cyhal_sdhc_set_bus_width(cyhal_sdhc_t *obj, uint8_t bus_width, bool configure_card)
{
    CY_ASSERT(NULL != obj);
    CY_ASSERT(NULL != obj->base);

    if (!_cyhal_sdhc_is_buswidth_correct(bus_width))
    {
        return CYHAL_SDHC_RSLT_ERR_WRONG_PARAM;
    }

    cy_rslt_t result;

    if (configure_card)
    {
        result = (cy_rslt_t)Cy_SD_Host_SetBusWidth(obj->base, _cyhal_sdhc_convert_buswidth(bus_width), &obj->context);
    }
    else
    {
        result = (cy_rslt_t)Cy_SD_Host_SetHostBusWidth(obj->base, _cyhal_sdhc_convert_buswidth(bus_width));
    }

    if (CY_RSLT_SUCCESS == result)
    {
        obj->bus_width = bus_width;
    }

    return result;
}

uint8_t cyhal_sdhc_get_bus_width(cyhal_sdhc_t *obj)
{
    CY_ASSERT(NULL != obj);
    return obj->bus_width;
}

void cyhal_sdhc_clear_errors(cyhal_sdhc_t *obj)
{
    CY_ASSERT(NULL != obj);
    CY_ASSERT(NULL != obj->base);
    Cy_SD_Host_ClearErrorInterruptStatus(obj->base, _CYHAL_SDHC_ALL_ERR_INTERRUPTS);
}

cyhal_sdhc_error_type_t cyhal_sdhc_get_last_command_errors(cyhal_sdhc_t *obj)
{
    CY_ASSERT(NULL != obj);
    CY_ASSERT(NULL != obj->base);
    return (cyhal_sdhc_error_type_t)Cy_SD_Host_GetErrorInterruptStatus(obj->base);
}

void cyhal_sdhc_software_reset(cyhal_sdhc_t *obj)
{
    CY_ASSERT(NULL != obj);
    CY_ASSERT(NULL != obj->base);
    _cyhal_sdxx_reset(obj->base);
}

cy_rslt_t cyhal_sdhc_enable_card_power(cyhal_sdhc_t *obj, bool enable)
{
    CY_ASSERT(NULL != obj);
    CY_ASSERT(NULL != obj->base);

    if (NC == obj->pin_card_pwr_en)
    {
        /* Need to have card_pwr_en pin configured in order to switch card power state */
        return CYHAL_SDHC_RSLT_ERR_PIN;
    }

    if (enable)
    {
        Cy_SD_Host_EnableCardVoltage(obj->base);
    }
    else
    {
        Cy_SD_Host_DisableCardVoltage(obj->base);
    }
    return CY_RSLT_SUCCESS;
}

cy_rslt_t cyhal_sdio_init(cyhal_sdio_t *obj, cyhal_gpio_t cmd, cyhal_gpio_t clk, cyhal_gpio_t data0, cyhal_gpio_t data1,
        cyhal_gpio_t data2, cyhal_gpio_t data3)
{
    cy_stc_sd_host_init_config_t hostConfig;
    cy_rslt_t result;
    cy_stc_sd_host_context_t context;

    CY_ASSERT(NULL != obj);

    obj->resource.type = CYHAL_RSC_INVALID;
    obj->base = NULL;
    obj->pin_cmd = CYHAL_NC_PIN_VALUE;
    obj->pin_clk = CYHAL_NC_PIN_VALUE;
    obj->pin_data0 = CYHAL_NC_PIN_VALUE;
    obj->pin_data1 = CYHAL_NC_PIN_VALUE;
    obj->pin_data2 = CYHAL_NC_PIN_VALUE;
    obj->pin_data3 = CYHAL_NC_PIN_VALUE;

    result = _cyhal_sdxx_setup_pin(obj, cmd, cyhal_pin_map_sdhc_card_cmd,
        _CYHAL_SDHC_ELEM_COUNT(cyhal_pin_map_sdhc_card_cmd), &(obj->pin_cmd), _CYHAL_SDHC_NOT_WEAK_FUNC);

    if (CY_RSLT_SUCCESS == result)
    {
        result = _cyhal_sdxx_setup_pin(obj, clk, cyhal_pin_map_sdhc_clk_card,
            _CYHAL_SDHC_ELEM_COUNT(cyhal_pin_map_sdhc_clk_card), &(obj->pin_clk), _CYHAL_SDHC_NOT_WEAK_FUNC);
    }

    if (CY_RSLT_SUCCESS == result)
    {
        result = _cyhal_sdxx_setup_pin(obj, data0, cyhal_pin_map_sdhc_card_dat_3to0,
            _CYHAL_SDHC_ELEM_COUNT(cyhal_pin_map_sdhc_card_dat_3to0), &(obj->pin_data0), _CYHAL_SDHC_NOT_WEAK_FUNC);
    }

    if (CY_RSLT_SUCCESS == result)
    {
        result = _cyhal_sdxx_setup_pin(obj, data1, cyhal_pin_map_sdhc_card_dat_3to0,
            _CYHAL_SDHC_ELEM_COUNT(cyhal_pin_map_sdhc_card_dat_3to0), &(obj->pin_data1), _CYHAL_SDHC_NOT_WEAK_FUNC);
    }

    if (CY_RSLT_SUCCESS == result)
    {
        result = _cyhal_sdxx_setup_pin(obj, data2, cyhal_pin_map_sdhc_card_dat_3to0,
            _CYHAL_SDHC_ELEM_COUNT(cyhal_pin_map_sdhc_card_dat_3to0), &(obj->pin_data2), _CYHAL_SDHC_NOT_WEAK_FUNC);
    }

    if (CY_RSLT_SUCCESS == result)
    {
        result = _cyhal_sdxx_setup_pin(obj, data3, cyhal_pin_map_sdhc_card_dat_3to0,
            _CYHAL_SDHC_ELEM_COUNT(cyhal_pin_map_sdhc_card_dat_3to0), &(obj->pin_data3), _CYHAL_SDHC_NOT_WEAK_FUNC);
    }

    if (result == CY_RSLT_SUCCESS)
    {
        const cyhal_resource_pin_mapping_t *cmd_map = _CYHAL_UTILS_GET_RESOURCE(cmd, cyhal_pin_map_sdhc_card_cmd);
        cyhal_resource_inst_t sdhc = *cmd_map->inst;

        result = _cyhal_utils_allocate_clock(&(obj->clock), &sdhc, CYHAL_CLOCK_BLOCK_PERIPHERAL_16BIT, true);
        if (CY_RSLT_SUCCESS == result)
        {
            result = _cyhal_utils_set_clock_frequency2(&(obj->clock), MAX_FREQUENCY, &CYHAL_CLOCK_TOLERANCE_5_P);
        }

        if (CY_RSLT_SUCCESS == result && !cyhal_clock_is_enabled(&(obj->clock)))
        {
            result = cyhal_clock_set_enabled(&(obj->clock), true, true);
        }

        if (result == CY_RSLT_SUCCESS)
        {
            obj->resource = sdhc;
            result = cyhal_hwmgr_reserve(&sdhc);
        }

        if (result == CY_RSLT_SUCCESS)
        {
            obj->base = _CYHAL_SDHC_BASE_ADDRESSES[obj->resource.block_num];
            stored_objects[obj->resource.block_num] = (void *)obj;
            _cyhal_sdhc_data_transfer_status[obj->resource.block_num] = _CYHAL_SDHC_NOT_RUNNING;

            /* Enable the SDHC block */
            Cy_SD_Host_Enable(obj->base);

            hostConfig.dmaType = CY_SD_HOST_DMA_ADMA2;
            hostConfig.emmc = false;
            hostConfig.enableLedControl = false;

            /* Configure SD Host to operate */
            result = (cy_rslt_t)Cy_SD_Host_Init(obj->base, &hostConfig, &context);

            /* Register SDIO Deep Sleep Callback */
            if (CY_RSLT_SUCCESS == result)
            {
                context.cardType = CY_SD_HOST_SDIO;
                obj->context = context;
                obj->pm_transition_pending = false;
                obj->pm_callback_data.callback = &_cyhal_sdio_syspm_callback,
                obj->pm_callback_data.states = (cyhal_syspm_callback_state_t)(CYHAL_SYSPM_CB_CPU_DEEPSLEEP | CYHAL_SYSPM_CB_SYSTEM_HIBERNATE);
                obj->pm_callback_data.next = NULL;
                obj->pm_callback_data.args = obj;
                /* The CYHAL_SYSPM_BEFORE_TRANSITION mode cannot be ignored because the PM handler
                    * calls the PDL deep-sleep callback that disables the block in this mode before transitioning.
                    */
                obj->pm_callback_data.ignore_modes = (cyhal_syspm_callback_mode_t)0;

                _cyhal_syspm_register_peripheral_callback(&obj->pm_callback_data);
            }

            if (result == CY_RSLT_SUCCESS)
            {
                /* Don't enable any error interrupts for now */
                Cy_SD_Host_SetErrorInterruptMask(obj->base, 0UL);

                /* Clear all interrupts */
                Cy_SD_Host_ClearErrorInterruptStatus(obj->base, _CYHAL_SDIO_SET_ALL_INTERRUPTS_MASK);
                Cy_SD_Host_ClearNormalInterruptStatus(obj->base, _CYHAL_SDIO_SET_ALL_INTERRUPTS_MASK);

                obj->irq_cause = 0UL;
                obj->events    = 0UL;

                obj->callback_data.callback = NULL;
                obj->callback_data.callback_arg = NULL;
                _cyhal_sdhc_config_structs[obj->resource.block_num] = obj;

                #if defined(CY_RTOS_AWARE) || defined(COMPONENT_RTOS_AWARE)
                _cyhal_sdxx_sema_status[obj->resource.block_num] = _CYHAL_SDXX_SEMA_NOT_INITED;
                #endif /* defined(CY_RTOS_AWARE) || defined(COMPONENT_RTOS_AWARE) */

                IRQn_Type irqn = _CYHAL_SDHC_IRQ_N[obj->resource.block_num];
                cy_stc_sysint_t irqCfg = { irqn, CYHAL_ISR_PRIORITY_DEFAULT };
                Cy_SysInt_Init(&irqCfg, _cyhal_sdio_irq_handler);
                NVIC_EnableIRQ(irqn);

                result = (cy_rslt_t)Cy_SD_Host_SetHostBusWidth(obj->base, CY_SD_HOST_BUS_WIDTH_4_BIT);

                /* Change the host SD clock to 400 kHz */
                if (result == CY_RSLT_SUCCESS)
                {
                    uint32_t freq = _CYHAL_SDIO_HOST_CLK_400K;
                    result = _cyhal_sdxx_sdcardchangeclock(obj, true, &freq, true, false);
                    if (result == CY_RSLT_SUCCESS)
                    {
                        obj->frequencyhal_hz = freq;
                        obj->block_size = _CYHAL_SDIO_64B_BLOCK;
                    }
                }
            }
        }
    }

    if (result != CY_RSLT_SUCCESS)
    {
        cyhal_sdio_free(obj);
    }

    return result;
}

void cyhal_sdio_free(cyhal_sdio_t *obj)
{
    CY_ASSERT(NULL != obj);

    if (NULL != obj->base)
    {
        IRQn_Type irqn = _CYHAL_SDHC_IRQ_N[obj->resource.block_num];
        NVIC_DisableIRQ(irqn);

        #if defined(CY_RTOS_AWARE) || defined(COMPONENT_RTOS_AWARE)
        if (_CYHAL_SDXX_SEMA_NOT_INITED != _cyhal_sdxx_sema_status[obj->resource.block_num])
        {
            cy_rtos_deinit_semaphore(&_cyhal_sdhc_smphr_xfer_done[obj->resource.block_num]);
            _cyhal_sdxx_sema_status[obj->resource.block_num] = _CYHAL_SDXX_SEMA_NOT_INITED;
        }
        #endif /* CY_RTOS_AWARE or COMPONENT_RTOS_AWARE defined */

        Cy_SD_Host_DeInit(obj->base);
        stored_objects[obj->resource.block_num] = NULL;
        _cyhal_sdhc_data_transfer_status[obj->resource.block_num] = _CYHAL_SDHC_NOT_RUNNING;

        cyhal_hwmgr_free(&(obj->resource));
        obj->base = NULL;

        _cyhal_sdhc_config_structs[obj->resource.block_num] = NULL;
        _cyhal_syspm_unregister_peripheral_callback(&obj->pm_callback_data);
    }

    if (obj->resource.type != CYHAL_RSC_INVALID)
    {
        obj->resource.type = CYHAL_RSC_INVALID;
        cyhal_clock_free(&(obj->clock));
    }

    /* Free pins */
    _cyhal_utils_release_if_used(&obj->pin_clk);
    _cyhal_utils_release_if_used(&obj->pin_cmd);
    _cyhal_utils_release_if_used(&obj->pin_data0);
    _cyhal_utils_release_if_used(&obj->pin_data1);
    _cyhal_utils_release_if_used(&obj->pin_data2);
    _cyhal_utils_release_if_used(&obj->pin_data3);
}

cy_rslt_t cyhal_sdio_configure(cyhal_sdio_t *obj, const cyhal_sdio_cfg_t *config)
{
    cy_rslt_t result = CYHAL_SDIO_RSLT_ERR_CONFIG;

    if ((NULL == obj) || (config == NULL))
    {
        return CYHAL_SDIO_RSLT_ERR_BAD_PARAM;
    }

    if (config->frequencyhal_hz != 0U)
    {
        uint32_t freq = config->frequencyhal_hz;
        result = _cyhal_sdxx_sdcardchangeclock(obj, true, &freq, true, false);
        if (CY_RSLT_SUCCESS == result)
        {
            obj->frequencyhal_hz = freq;
        }
    }

    if (config->block_size != 0U)
    {
        /* No need to change anything in HW, because it will be overwritten
        *  in cyhal_sdio_bulk_transfer()/cyhal_sdio_transfer_async() functions.
        *  The HW block size will taken based on obj->block_size, which is
        *  updated here.
        */
        obj->block_size = config->block_size;
    }

    return result;
}

cy_rslt_t cyhal_sdio_send_cmd(const cyhal_sdio_t *obj, cyhal_transfer_t direction, \
                              cyhal_sdio_command_t command, uint32_t argument, uint32_t* response)
{
    CY_UNUSED_PARAMETER(direction);
    if (NULL == obj)
    {
        return CYHAL_SDIO_RSLT_ERR_BAD_PARAM;
    }

    if (obj->pm_transition_pending)
    {
        return CYHAL_SDIO_RSLT_ERR_PM_PENDING;
    }

    cy_rslt_t                   ret;
    cy_stc_sd_host_cmd_config_t cmd;
    uint32_t                    retry = _CYHAL_SDIO_TRANSFER_TRIES;

    /* Clear out the response */
    if ( response != NULL )
    {
        *response = 0UL;
    }

    do
    {
        /* First clear out the command complete and transfer complete statuses */
        Cy_SD_Host_ClearNormalInterruptStatus(obj->base, CY_SD_HOST_CMD_COMPLETE);

        /* Check if an error occurred on any previous transactions */
        if ( Cy_SD_Host_GetNormalInterruptStatus(obj->base) & CY_SD_HOST_ERR_INTERRUPT )
        {
            /* Reset the block if there was an error. Note a full reset usually
             * requires more time, but this short version is working quite well and
             * successfully clears out the error state.
             */
            Cy_SD_Host_ClearErrorInterruptStatus(obj->base, _CYHAL_SDIO_SET_ALL_INTERRUPTS_MASK);
            _cyhal_sdxx_reset(obj->base);
        }

        cmd.commandIndex                 = (uint32_t)command;
        cmd.commandArgument              = argument;
        cmd.enableCrcCheck               = true;
        cmd.enableAutoResponseErrorCheck = false;
        cmd.respType                     = CY_SD_HOST_RESPONSE_LEN_48;
        cmd.enableIdxCheck               = true;
        cmd.dataPresent                  = false;
        cmd.cmdType                      = CY_SD_HOST_CMD_NORMAL;

        if (obj->irq_cause & CYHAL_SDIO_CMD_COMPLETE)
        {
            /*  Enabling command complete interrupt mask if corresponding event was enabled by user
            *   _cyhal_sdio_irq_handler will disable CY_SD_HOST_CMD_COMPLETE mask once interrupt
            *   is generated. */
            Cy_SD_Host_SetNormalInterruptMask(obj->base, Cy_SD_Host_GetNormalInterruptMask(obj->base) |
                    CY_SD_HOST_CMD_COMPLETE);
        }

        ret = (cy_rslt_t)Cy_SD_Host_SendCommand(obj->base, &cmd);

        if (CY_RSLT_SUCCESS == ret)
        {
            ret = (cy_rslt_t)_cyhal_sdxx_pollcmdcomplete(obj->base, obj->resource.block_num);
        }
    } while ((CY_RSLT_SUCCESS != ret) && (retry-- > 0UL));

    if (CY_RSLT_SUCCESS == ret)
    {
        ret = (cy_rslt_t)Cy_SD_Host_GetResponse(obj->base, response, false);
    }

    return ret;
}

cy_rslt_t cyhal_sdio_bulk_transfer(cyhal_sdio_t *obj, cyhal_transfer_t direction,
                                   uint32_t argument, const uint32_t* data,
                                   uint16_t length, uint32_t* response)
{
    cy_rslt_t ret = CY_RSLT_SUCCESS;
    uint32_t retry = _CYHAL_SDIO_TRANSFER_TRIES;
    _cyhal_sdxx_setup_smphr(obj->resource.block_num);

    do
    {
        ret = cyhal_sdio_transfer_async(obj, direction, argument, data, length);

        if (CY_RSLT_SUCCESS == ret)
        {
            ret = _cyhal_sdxx_waitfor_transfer_complete(obj->resource.block_num, obj->base, true);
        }

        if (CY_RSLT_SUCCESS != ret)
        {
            /*  SDIO Error Handling
            *   SDIO write timeout is expected when doing first write to register
            *   after KSO bit disable (as it goes to AOS core).
            *   This is the only time known that a write timeout occurs.
            *   Issue the reset to recover from error. */
            _cyhal_sdxx_reset(obj->base);
        }

    } while ((CY_RSLT_SUCCESS != ret) && (--retry > 0UL));

    if(CY_RSLT_SUCCESS != ret)
    {
        _cyhal_sdhc_data_transfer_status[obj->resource.block_num] = _CYHAL_SDHC_NOT_RUNNING;
    }

    if ((response != NULL) && (CY_RSLT_SUCCESS == ret))
    {
        *response = 0UL;
        ret = (cy_rslt_t)Cy_SD_Host_GetResponse(obj->base, response, false);
    }

    return ret;
}

/*******************************************************************************
*
*   The asynchronous transfer is implemented on the CY_SD_HOST_XFER_COMPLETE
*   interrupt. The function sets up data and enables the CY_SD_HOST_XFER_COMPLETE
*   interrupt mask, which causes interrupt to occur and handled by _cyhal_sdio_irq_handler
*   which take care of disabling CY_SD_HOST_XFER_COMPLETE mask. This function
*   can also activate CY_SD_HOST_CMD_COMPLETE interrupt mask if it was enabled
*   by user via cyhal_sdio_enable_event function.
*
*******************************************************************************/
cy_rslt_t cyhal_sdio_transfer_async(cyhal_sdio_t *obj, cyhal_transfer_t direction,
                                    uint32_t argument, const uint32_t* data, uint16_t length)
{
    if (NULL == obj)
    {
        return CYHAL_SDIO_RSLT_ERR_BAD_PARAM;
    }

    if (obj->pm_transition_pending)
    {
        return CYHAL_SDIO_RSLT_ERR_PM_PENDING;
    }

    cy_rslt_t                    ret;
    uint32_t                     retry = _CYHAL_SDIO_TRANSFER_TRIES;
    cy_stc_sd_host_cmd_config_t  cmd;
    cy_stc_sd_host_data_config_t dat;

    /* Initialize data constants*/
    dat.autoCommand         = CY_SD_HOST_AUTO_CMD_NONE;
    dat.dataTimeout         = 0x0dUL;
    dat.enableIntAtBlockGap = false;
    dat.enReliableWrite     = false;
    dat.enableDma           = true;

    do
    {
        /* Add SDIO Error Handling
        * SDIO write timeout is expected when doing first write to register
        * after KSO bit disable (as it goes to AOS core).
        * This timeout, however, triggers an error state in the hardware.
        * So, check for the error and then recover from it
        * as needed via reset issuance. This is the only time known that
        * a write timeout occurs.
        */

        /* First clear out the command complete and transfer complete statuses */
        Cy_SD_Host_ClearNormalInterruptStatus(obj->base, (CY_SD_HOST_XFER_COMPLETE | CY_SD_HOST_CMD_COMPLETE));

        /* Check if an error occurred on any previous transactions or reset after the first unsuccessful transfer try */
        if ((Cy_SD_Host_GetNormalInterruptStatus(obj->base) & CY_SD_HOST_ERR_INTERRUPT) ||
            (retry < _CYHAL_SDIO_TRANSFER_TRIES))
        {
            /* Reset the block if there was an error. Note a full reset usually
             * requires more time, but this short version is working quite well and
             * successfully clears out the error state.
             */
            Cy_SD_Host_ClearErrorInterruptStatus(obj->base, _CYHAL_SDIO_SET_ALL_INTERRUPTS_MASK);
            _cyhal_sdxx_reset(obj->base);
        }

        /* Prepare the data transfer register */
        cmd.commandIndex                 = (uint32_t) CYHAL_SDIO_CMD_IO_RW_EXTENDED;
        cmd.commandArgument              = argument;
        cmd.enableCrcCheck               = true;
        cmd.enableAutoResponseErrorCheck = false;
        cmd.respType                     = CY_SD_HOST_RESPONSE_LEN_48;
        cmd.enableIdxCheck               = true;
        cmd.dataPresent                  = true;
        cmd.cmdType                      = CY_SD_HOST_CMD_NORMAL;

        dat.read = ( direction == CYHAL_WRITE ) ? false : true;

        /* Block mode */
        if (length >= obj->block_size)
        {
            dat.blockSize     = obj->block_size;
            dat.numberOfBlock = ( length + obj->block_size - 1 ) / obj->block_size;
        }
        /* Byte mode */
        else
        {
            dat.blockSize     = length;
            dat.numberOfBlock = 1UL;
        }

        length = dat.blockSize * dat.numberOfBlock;

        obj->adma_descriptor_tbl[0] = (1UL << CY_SD_HOST_ADMA_ATTR_VALID_POS) | /* Attr Valid */
                                     (1UL << CY_SD_HOST_ADMA_ATTR_END_POS) |   /* Attr End */
                                     (0UL << CY_SD_HOST_ADMA_ATTR_INT_POS) |   /* Attr Int */
                                     (CY_SD_HOST_ADMA_TRAN << CY_SD_HOST_ADMA_ACT_POS) |
                                     (length << CY_SD_HOST_ADMA_LEN_POS);     /* Len */

        obj->adma_descriptor_tbl[1] = (uint32_t)data;

        /* The address of the ADMA descriptor table. */
        dat.data = (uint32_t*)&obj->adma_descriptor_tbl[0];

        ret = _cyhal_sdxx_prepare_for_transfer(obj->base, true);

        if (CY_RSLT_SUCCESS == ret)
        {
            ret = (cy_rslt_t)Cy_SD_Host_InitDataTransfer(obj->base, &dat);
        }

        if (CY_RSLT_SUCCESS == ret)
        {
            /* Indicate that async transfer in progress */
            _cyhal_sdhc_data_transfer_status[obj->resource.block_num] = _CYHAL_SDHC_WAIT_BOTH;
            ret = (cy_rslt_t)Cy_SD_Host_SendCommand(obj->base, &cmd);
        }

        if (CY_RSLT_SUCCESS == ret)
        {
            ret = (cy_rslt_t)_cyhal_sdxx_pollcmdcomplete(obj->base, obj->resource.block_num);
        }

    } while ((CY_RSLT_SUCCESS != ret) && (--retry > 0UL));

    if (CY_RSLT_SUCCESS != ret)
    {
        /* Transfer failed */
        _cyhal_sdhc_data_transfer_status[obj->resource.block_num] = _CYHAL_SDHC_NOT_RUNNING;
    }

    return ret;
}

bool cyhal_sdio_is_busy(const cyhal_sdio_t *obj)
{
    CY_ASSERT(NULL != obj);
    return (_CYHAL_SDHC_NOT_RUNNING != _cyhal_sdhc_data_transfer_status[obj->resource.block_num]);
}

cy_rslt_t cyhal_sdio_abort_async(const cyhal_sdio_t *obj)
{
    cy_rslt_t ret = CY_RSLT_SUCCESS;

    /* To abort transition reset dat and cmd lines (software reset) */
    _cyhal_sdxx_reset(obj->base);
    _cyhal_sdhc_data_transfer_status[obj->resource.block_num] = _CYHAL_SDHC_NOT_RUNNING;
    return ret;
}

void cyhal_sdio_register_callback(cyhal_sdio_t *obj, cyhal_sdio_event_callback_t callback, void *callback_arg)
{
    uint32_t savedIntrStatus = cyhal_system_critical_section_enter();
    obj->callback_data.callback = (cy_israddress) callback;
    obj->callback_data.callback_arg = callback_arg;
    cyhal_system_critical_section_exit(savedIntrStatus);
}

void cyhal_sdio_enable_event(cyhal_sdio_t *obj, cyhal_sdio_irq_event_t event, uint8_t intr_priority, bool enable)
{
    /* Configure interrupt-based event(s) */
    if (0U != ((uint32_t) event & (uint32_t) CYHAL_SDIO_ALL_INTERRUPTS))
    {
        uint32_t interruptMask   = Cy_SD_Host_GetNormalInterruptMask(obj->base);

        IRQn_Type irqn = _CYHAL_SDHC_IRQ_N[obj->resource.block_num];
        NVIC_SetPriority(irqn, intr_priority);

        if (enable)
        {
            interruptMask   |= event;
            obj->irq_cause  |= event;
        }
        else
        {
            interruptMask   &= ~(event);
            obj->irq_cause  &= ~event;
        }

        Cy_SD_Host_ClearNormalInterruptStatus(obj->base, interruptMask);

        /*  CY_SD_HOST_CMD_COMPLETE and CY_SD_HOST_XFER_COMPLETE cannot be always enabled because of SD Host driver limitations.
        *   SDHC HAL transfer APIs are taking care of enabling these statuses. CY_SD_HOST_CMD_COMPLETE is only
        *   enabled if corresponding user callback is enabled while CY_SD_HOST_XFER_COMPLETE is enabled by transfer API
        *   regardless it was enabled by user or not. */
        interruptMask &= ((uint32_t) ~CY_SD_HOST_CMD_COMPLETE) & ((uint32_t) ~CY_SD_HOST_XFER_COMPLETE);

        Cy_SD_Host_SetNormalInterruptMask(obj->base, interruptMask);
    }

    /* Configure non-interrupt based event(s) */
    if (0U != ((uint32_t) event & _CYHAL_SDIO_INTERFACE_CHANGE_MASK))
    {
        if (enable)
        {
            obj->events |= (uint32_t) event;
        }
        else
        {
            obj->events &= (uint32_t) ~((uint32_t) event);
        }
    }
}

#if defined(__cplusplus)
}
#endif

#endif /* CY_IP_MXSDHC */