Files
mbed-os/targets/TARGET_Cypress/TARGET_PSOC6/mtb-hal-cat1/source/cyhal_sdhc.c
Beslan 0ef1717155
Some checks failed
Basic Checks / license-check (push) Has been cancelled
Basic Checks / include-check (push) Has been cancelled
Basic Checks / style-check (push) Has been cancelled
Basic Checks / docs-check (push) Has been cancelled
Basic Checks / python-tests (push) Has been cancelled
Basic Checks / pin-validation (push) Has been cancelled
Basic Checks / cmake-checks (push) Has been cancelled
Basic Checks / frozen-tools-check (push) Has been cancelled
Publish or Update docker image for head of branch / prepare-tags (push) Has been cancelled
Release or update docker image for a released mbed-os version / prepare-tags (push) Has been cancelled
Publish or Update docker image for head of branch / build-container (push) Has been cancelled
Publish or Update docker image for head of branch / test-container (linux/amd64) (push) Has been cancelled
Publish or Update docker image for head of branch / test-container (linux/arm64) (push) Has been cancelled
Publish or Update docker image for head of branch / deploy-container (push) Has been cancelled
Release or update docker image for a released mbed-os version / build-container (push) Has been cancelled
Release or update docker image for a released mbed-os version / test-container (linux/amd64) (push) Has been cancelled
Release or update docker image for a released mbed-os version / test-container (linux/arm64) (push) Has been cancelled
Release or update docker image for a released mbed-os version / deploy-container (push) Has been cancelled
Prune temporary docker images / prune-images (push) Has been cancelled
Mirror mbed-os-6.15.0
2026-07-10 18:42:39 +03:00

2907 lines
108 KiB
C

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