/* * Copyright (C) 2022-2024, Xiaohua Semiconductor Co., Ltd. * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2023-02-14 CDT first version */ /******************************************************************************* * Include files ******************************************************************************/ #include #include #if defined(RT_USING_SDIO) #if defined(BSP_USING_SDIO1) || defined(BSP_USING_SDIO2) #include "drv_sdio.h" #include "board_config.h" /******************************************************************************* * Local type definitions ('typedef') ******************************************************************************/ /* rthw sdio */ struct rthw_sdio { struct rt_mmcsd_host *host; struct hc32_sdio_config *config; struct hc32_sdio_des des; struct rt_event event; struct rt_mutex mutex; struct sdio_pkg *pkg; uint8_t *cache_buf; }; /******************************************************************************* * Local pre-processor symbols/macros ('#define') ******************************************************************************/ //#define DRV_DEBUG #define LOG_TAG "drv.sdio" #include #ifndef SDIO_BUFF_SIZE #define SDIO_BUFF_SIZE (4096) #endif #ifndef SDIO_ALIGN_LEN #define SDIO_ALIGN_LEN (4) #endif #ifndef SDIO_MAX_FREQ #define SDIO_MAX_FREQ (50*1000*1000) #endif #define RTHW_SDIO_LOCK(_sdio) rt_mutex_take(&(_sdio)->mutex, RT_WAITING_FOREVER) #define RTHW_SDIO_UNLOCK(_sdio) rt_mutex_release(&(_sdio)->mutex); /******************************************************************************* * Global variable definitions (declared in header file with 'extern') ******************************************************************************/ extern rt_err_t rt_hw_board_sdio_init(CM_SDIOC_TypeDef *SDIOCx); /******************************************************************************* * Local function prototypes ('static') ******************************************************************************/ #ifdef BSP_USING_SDIO1 static void _sdio1_handler(void); #endif #ifdef BSP_USING_SDIO2 static void _sdio2_handler(void); #endif /******************************************************************************* * Local variable definitions ('static') ******************************************************************************/ enum { #ifdef BSP_USING_SDIO1 SDIO1_INDEX, #endif #ifdef BSP_USING_SDIO2 SDIO2_INDEX, #endif }; static struct hc32_sdio_config _sdio_config[] = { #ifdef BSP_USING_SDIO1 SDIO1_BUS_CONFIG, #endif /* BSP_USING_SDIO1 */ #ifdef BSP_USING_SDIO2 SDIO2_BUS_CONFIG, #endif /* BSP_USING_SDIO2 */ }; static const func_ptr_t _sdio_irq_handler[] = { #ifdef BSP_USING_SDIO1 _sdio1_handler, #endif /* BSP_USING_SDIO1 */ #ifdef BSP_USING_SDIO2 _sdio2_handler, #endif /* BSP_USING_SDIO2 */ }; #ifdef BSP_USING_SDIO1 rt_align(SDIO_ALIGN_LEN) static rt_uint8_t _sdio1_cache_buf[SDIO_BUFF_SIZE]; #endif #ifdef BSP_USING_SDIO2 rt_align(SDIO_ALIGN_LEN) static rt_uint8_t _sdio2_cache_buf[SDIO_BUFF_SIZE]; #endif static rt_uint8_t *const _sdio_cache_buf[] = { #ifdef BSP_USING_SDIO1 _sdio1_cache_buf, #endif /* BSP_USING_SDIO1 */ #ifdef BSP_USING_SDIO2 _sdio2_cache_buf, #endif /* BSP_USING_SDIO2 */ }; static struct rt_mmcsd_host *_sdio_host[sizeof(_sdio_config) / sizeof(_sdio_config[0])] = {0}; /******************************************************************************* * Function implementation - global ('extern') and local ('static') ******************************************************************************/ /** * @brief Get the response type of hc32 sdioc driver. * @param [in] rt_resp_type SDIO command response type defined in mmcs_core.h * @retval The response type of hc32 sdioc driver */ static rt_uint32_t _sdio_get_cmd_resptype(rt_uint32_t rt_resptype) { rt_uint32_t sdioc_resptype; switch (rt_resptype) { case RESP_NONE: sdioc_resptype = SDIOC_RESP_TYPE_NO; break; case RESP_R1: sdioc_resptype = SDIOC_RESP_TYPE_R1_R5_R6_R7; break; case RESP_R2: sdioc_resptype = SDIOC_RESP_TYPE_R2; break; case RESP_R3: case RESP_R4: sdioc_resptype = SDIOC_RESP_TYPE_R3_R4; break; case RESP_R5: case RESP_R6: case RESP_R7: sdioc_resptype = SDIOC_RESP_TYPE_R1_R5_R6_R7; break; case RESP_R1B: sdioc_resptype = SDIOC_RESP_TYPE_R1B_R5B; break; default: sdioc_resptype = SDIOC_RESP_TYPE_NO; LOG_E("unknown response type: %d", rt_resptype); break; } return sdioc_resptype; } /** * @brief This function wait sdio completed. * @param [in] sdio Pointer to a @ref rthw_sdio structure * @retval None */ static void _sdio_wait_completed(struct rthw_sdio *sdio) { rt_uint32_t status; rt_uint32_t response[4]; __IO rt_uint32_t to_count; struct rt_mmcsd_cmd *cmd = sdio->pkg->cmd; struct rt_mmcsd_data *data = cmd->data; CM_SDIOC_TypeDef *instance = sdio->config->instance; if (rt_event_recv(&sdio->event, 0xFFFFFFFF, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, rt_tick_from_millisecond(5000), &status) != RT_EOK) { LOG_E("[%s timeout] sta=0x%08X, cmd %d, arg:0x%08X", __func__, status, cmd->cmd_code, cmd->arg); cmd->err = -RT_ETIMEOUT; return; } if (sdio->pkg == RT_NULL) { return; } if (resp_type(cmd) == RESP_NONE) { ; } else if (resp_type(cmd) == RESP_R2) { LOG_D("R2"); (void)SDIOC_GetResponse(instance, SDIOC_RESP_REG_BIT0_31, &response[0]); (void)SDIOC_GetResponse(instance, SDIOC_RESP_REG_BIT32_63, &response[1]); (void)SDIOC_GetResponse(instance, SDIOC_RESP_REG_BIT64_95, &response[2]); (void)SDIOC_GetResponse(instance, SDIOC_RESP_REG_BIT96_127, &response[3]); cmd->resp[0] = (response[3] << 8) + ((response[2] >> 24) & 0xFF); cmd->resp[1] = (response[2] << 8) + ((response[1] >> 24) & 0xFF); cmd->resp[2] = (response[1] << 8) + ((response[0] >> 24) & 0xFF); cmd->resp[3] = (response[0] << 8) + 0x00; } else { (void)SDIOC_GetResponse(instance, SDIOC_RESP_REG_BIT0_31, &response[0]); cmd->resp[0] = response[0]; } if (status & SDIOC_INT_FLAG_EI) { if (status & (SDIOC_INT_FLAG_CTOE | SDIOC_INT_FLAG_CCE | SDIOC_INT_FLAG_CEBE | SDIOC_INT_FLAG_CIE)) { SDIOC_SWReset(instance, SDIOC_SW_RST_CMD_LINE); cmd->err = -RT_ERROR; LOG_D("[%s cmd err] sta=0x%08X, %s%s%s%s cmd %d arg:0x%08X", __func__, status, status & SDIOC_INT_FLAG_CCE ? "Command CRC Error " : "", status & SDIOC_INT_FLAG_CEBE ? "Command End Bit Error" : "", status & SDIOC_INT_FLAG_CTOE ? "Command Timeout Error" : "", status == 0 ? "NULL" : "", cmd->cmd_code, cmd->arg); } if (status & (SDIOC_INT_FLAG_DTOE | SDIOC_INT_FLAG_DCE | SDIOC_INT_FLAG_DEBE)) { SDIOC_SWReset(instance, SDIOC_SW_RST_DATA_LINE); if (data != NULL) { data->err = -RT_ERROR; LOG_D("[%s dat err] sta=0x%08X, %s%s%s%s cmd %d arg:0x%08X rw:%c len:%d blksize:%d", __func__, status, status & SDIOC_INT_FLAG_DCE ? "Data CRC Error " : "", status & SDIOC_INT_FLAG_DEBE ? "Data End Bit Error" : "", status & SDIOC_INT_FLAG_DTOE ? "Data Timeout Error" : "", status == 0 ? "NULL" : "", cmd->cmd_code, cmd->arg, data ? (data->flags & DATA_DIR_WRITE ? 'w' : 'r') : '-', data ? data->blks * data->blksize : 0, data ? data->blksize : 0); } } } else { cmd->err = RT_EOK; LOG_D("[%s xfer ok] sta=0x%08X, cmd %d, arg:0x%08X, resp[%08X %08X %08X %08X]", __func__, status, cmd->cmd_code, cmd->arg, cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]); } } /** * @brief Transfer data by dma. * @param [in] sdio Pointer to a @ref rthw_sdio structure * @param [in] pkg Pointer to a @ref sdio_pkg structure * @retval None */ static void _sdio_transfer_by_dma(struct rthw_sdio *sdio, struct sdio_pkg *pkg) { struct rt_mmcsd_data *data = pkg->cmd->data; if ((NULL == sdio) || (NULL == pkg) || (NULL == pkg->buf) || (NULL == pkg->cmd) || (NULL == pkg->cmd->data)) { LOG_E("%s function arguments error: %s %s %s %s %s", __func__, (sdio == RT_NULL ? "sdio is NULL" : ""), (pkg == RT_NULL ? "pkg is NULL" : ""), (sdio ? (pkg->buf == RT_NULL ? "pkg->buf is NULL" : "") : ""), (sdio ? (pkg->cmd == RT_NULL ? "pkg->cmd is NULL" : "") : ""), (sdio ? (pkg->cmd->data == RT_NULL ? "pkg->cmd->data is NULL" : "") : "") ); return; } if (data->flags & DATA_DIR_WRITE) { sdio->des.txconfig(sdio->config->dma_tx.Instance, sdio->config->dma_tx.channel, pkg); DMA_ChCmd(sdio->config->dma_tx.Instance, sdio->config->dma_tx.channel, ENABLE); } else if (data->flags & DATA_DIR_READ) { sdio->des.rxconfig(sdio->config->dma_rx.Instance, sdio->config->dma_rx.channel, pkg); DMA_ChCmd(sdio->config->dma_rx.Instance, sdio->config->dma_rx.channel, ENABLE); } } /** * @brief Send command. * @param [in] sdio Pointer to a @ref rthw_sdio structure * @param [in] pkg Pointer to a @ref sdio_pkg structure * @retval None */ static void _sdio_send_command(struct rthw_sdio *sdio, struct sdio_pkg *pkg) { rt_int32_t ret; struct rt_mmcsd_cmd *cmd = pkg->cmd; struct rt_mmcsd_data *data = cmd->data; CM_SDIOC_TypeDef *instance = sdio->config->instance; stc_sdioc_cmd_config_t stcCmdConfig; stc_sdioc_data_config_t stcDataConfig; /* save pkg */ sdio->pkg = pkg; LOG_D("CMD:%d ARG:0x%08x RES:%s%s%s%s%s%s%s%s%s rw:%c len:%d blksize:%d", cmd->cmd_code, cmd->arg, resp_type(cmd) == RESP_NONE ? "NONE" : "", resp_type(cmd) == RESP_R1 ? "R1" : "", resp_type(cmd) == RESP_R1B ? "R1B" : "", resp_type(cmd) == RESP_R2 ? "R2" : "", resp_type(cmd) == RESP_R3 ? "R3" : "", resp_type(cmd) == RESP_R4 ? "R4" : "", resp_type(cmd) == RESP_R5 ? "R5" : "", resp_type(cmd) == RESP_R6 ? "R6" : "", resp_type(cmd) == RESP_R7 ? "R7" : "", data ? (data->flags & DATA_DIR_WRITE ? 'w' : 'r') : '-', data ? data->blks * data->blksize : 0, data ? data->blksize : 0); /* config command */ stcCmdConfig.u16CmdIndex = cmd->cmd_code; /* config command argument */ stcCmdConfig.u32Argument = cmd->arg; /* config command type */ stcCmdConfig.u16CmdType = SDIOC_CMD_TYPE_NORMAL; /* config response type */ stcCmdConfig.u16ResponseType = _sdio_get_cmd_resptype(resp_type(cmd)); if (data != RT_NULL) { /* config data */ stcDataConfig.u16BlockSize = data->blksize; stcDataConfig.u16BlockCount = data->blks; stcDataConfig.u16TransDir = (data->flags & DATA_DIR_READ) ? SDIOC_TRANS_DIR_TO_HOST : SDIOC_TRANS_DIR_TO_CARD; stcDataConfig.u16AutoCmd12 = SDIOC_AUTO_SEND_CMD12_DISABLE; stcDataConfig.u16DataTimeout = SDIOC_DATA_TIMEOUT_CLK_2E27; stcDataConfig.u16TransMode = (data->blks > 1U) ? SDIOC_TRANS_MD_MULTI : SDIOC_TRANS_MD_SINGLE; ret = SDIOC_ConfigData(instance, &stcDataConfig); if (ret != 0) { LOG_E("configure data error : %d", ret); } /* transfer config */ _sdio_transfer_by_dma(sdio, pkg); stcCmdConfig.u16DataLine = SDIOC_DATA_LINE_ENABLE;; } else { stcCmdConfig.u16DataLine = SDIOC_DATA_LINE_DISABLE; } /* send cmd */ ret = SDIOC_SendCommand(instance, &stcCmdConfig); if (ret != 0) { LOG_E("send command error : %d", ret); } /* wait completed */ _sdio_wait_completed(sdio); /* clear pkg */ sdio->pkg = RT_NULL; } /** * @brief Send sdio request. * @param [in] host Pointer to a @ref rt_mmcsd_host structure * @param [in] req Pointer to a @ref rt_mmcsd_req structure * @retval None */ static void _sdio_request(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req) { rt_uint32_t mask; struct sdio_pkg pkg; struct rt_mmcsd_data *data; struct rthw_sdio *sdio = host->private_data; if ((NULL == host) || (NULL == req) || (NULL == sdio) || (NULL == sdio->config)) { LOG_E("%s function arguments error: %s %s %s %s", __func__, (host == RT_NULL ? "host is NULL" : ""), (req == RT_NULL ? "req is NULL" : ""), (sdio == RT_NULL ? "sdio is NULL" : ""), (sdio ? (sdio->config == RT_NULL ? "sdio->config is NULL" : "") : "") ); return; } RTHW_SDIO_LOCK(sdio); if (req->cmd != RT_NULL) { rt_memset(&pkg, 0, sizeof(pkg)); data = req->cmd->data; pkg.cmd = req->cmd; if (SD_SEND_IF_COND == pkg.cmd->cmd_code) { mask = (CM_SDIOC1 == sdio->config->instance) ? PERIC_SDIOC_SYCTLREG_SELMMC1 : PERIC_SDIOC_SYCTLREG_SELMMC2; if (data == RT_NULL) { CM_PERIC->SDIOC_SYCTLREG &= ~mask; } else { CM_PERIC->SDIOC_SYCTLREG |= mask; } } if (data != RT_NULL) { rt_uint32_t size = data->blks * data->blksize; RT_ASSERT(size <= SDIO_BUFF_SIZE); /* buffer unaligned */ if ((rt_uint32_t)data->buf & (SDIO_ALIGN_LEN - 1)) { if (data->flags & DATA_DIR_WRITE) { rt_memcpy(sdio->cache_buf, data->buf, size); } pkg.buf = sdio->cache_buf; } else { pkg.buf = data->buf; } } _sdio_send_command(sdio, &pkg); if ((data != RT_NULL) && (data->flags & DATA_DIR_READ) && ((rt_uint32_t)data->buf & (SDIO_ALIGN_LEN - 1))) { rt_memcpy(data->buf, sdio->cache_buf, data->blksize * data->blks); } } if (req->stop != RT_NULL) { rt_memset(&pkg, 0, sizeof(pkg)); pkg.cmd = req->stop; _sdio_send_command(sdio, &pkg); } RTHW_SDIO_UNLOCK(sdio); mmcsd_req_complete(sdio->host); } /** * @brief Config sdio. * @param [in] host Pointer to a @ref rt_mmcsd_host structure * @param [in] io_cfg Pointer to a @ref rt_mmcsd_io_cfg structure * @retval None */ static void _sdio_iocfg(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *io_cfg) { rt_int32_t ret; rt_uint32_t clk; rt_uint16_t clk_div; rt_uint32_t clk_src; struct rthw_sdio *sdio = host->private_data; CM_SDIOC_TypeDef *instance; if ((NULL == host) || (NULL == io_cfg) || (NULL == sdio) || (NULL == sdio->config)) { LOG_E("%s function arguments error: %s %s %s %s", __func__, (host == RT_NULL ? "host is NULL" : ""), (io_cfg == RT_NULL ? "io_cfg is NULL" : ""), (sdio == RT_NULL ? "sdio_des is NULL" : ""), (sdio ? (sdio->config == RT_NULL ? "sdio->config is NULL" : "") : "") ); return; } instance = sdio->config->instance; clk_src = sdio->des.clk_get(instance); if (clk_src < 400 * 1000) { LOG_E("clock rate is too low! rate:%d", clk_src); return; } clk = io_cfg->clock; if (clk > host->freq_max) { clk = host->freq_max; } if (clk > clk_src) { LOG_W("setting rate is greater than clock source rate."); clk = clk_src; } LOG_D("clk:%d width:%s%s%s power:%s%s%s", clk, io_cfg->bus_width == MMCSD_BUS_WIDTH_8 ? "8" : "", io_cfg->bus_width == MMCSD_BUS_WIDTH_4 ? "4" : "", io_cfg->bus_width == MMCSD_BUS_WIDTH_1 ? "1" : "", io_cfg->power_mode == MMCSD_POWER_OFF ? "OFF" : "", io_cfg->power_mode == MMCSD_POWER_UP ? "UP" : "", io_cfg->power_mode == MMCSD_POWER_ON ? "ON" : ""); RTHW_SDIO_LOCK(sdio); switch (io_cfg->bus_width) { case MMCSD_BUS_WIDTH_1: SDIOC_SetBusWidth(instance, SDIOC_BUS_WIDTH_1BIT); break; case MMCSD_BUS_WIDTH_4: SDIOC_SetBusWidth(instance, SDIOC_BUS_WIDTH_4BIT); break; case MMCSD_BUS_WIDTH_8: SDIOC_SetBusWidth(instance, SDIOC_BUS_WIDTH_8BIT); break; default: LOG_E("unknown bus width: %d", io_cfg->bus_width); break; } switch (io_cfg->power_mode) { case MMCSD_POWER_OFF: SDIOC_PowerCmd(instance, DISABLE); break; case MMCSD_POWER_UP: case MMCSD_POWER_ON: SDIOC_PowerCmd(instance, ENABLE); break; default: LOG_W("unknown power_mode %d", io_cfg->power_mode); break; } instance->CLKCON = 0; if (clk > 0) { ret = SDIOC_GetOptimumClockDiv(clk, &clk_div); if (ret != LL_OK) { LOG_E("clock division error"); } else { SDIOC_SetClockDiv(instance, clk_div); if ((clk << 1) <= host->freq_max) { SDIOC_SetSpeedMode(instance, SDIOC_SPEED_MD_NORMAL); } else { SDIOC_SetSpeedMode(instance, SDIOC_SPEED_MD_HIGH); } instance->CLKCON = (clk_div | SDIOC_CLKCON_ICE | SDIOC_CLKCON_CE); } } RTHW_SDIO_UNLOCK(sdio); } /** * @brief Update the sdio interrupt. * @param [in] host Pointer to a @ref rt_mmcsd_host structure * @param [in] enable Enable interrupt when value is non-zero * @retval None */ static void _sdio_enable_sdio_irq(struct rt_mmcsd_host *host, rt_int32_t enable) { struct rthw_sdio *sdio = host->private_data; CM_SDIOC_TypeDef *instance = sdio->config->instance; if (enable) { LOG_D("enable sdio interrupt"); SDIOC_IntStatusCmd(instance, SDIOC_INT_CINTSEN, ENABLE); SDIOC_IntCmd(instance, SDIOC_INT_CINTSEN, ENABLE); } else { LOG_D("disable sdio interrupt"); SDIOC_IntStatusCmd(instance, SDIOC_INT_CINTSEN, DISABLE); SDIOC_IntCmd(instance, SDIOC_INT_CINTSEN, DISABLE); } } /** * @brief update all of the using interrupt. * @param [in] host Pointer to a @ref rt_mmcsd_host structure * @param [in] enable Enable interrupt when value is non-zero * @retval None */ static void _sdio_update_irq(struct rt_mmcsd_host *host, rt_int32_t enable) { struct rthw_sdio *sdio = host->private_data; CM_SDIOC_TypeDef *instance = sdio->config->instance; const rt_uint32_t int_type = (SDIOC_INT_CCSEN | SDIOC_INT_TCSEN | SDIOC_ERR_INT_ALL); if (enable) { LOG_D("enable all of the using interrupt"); SDIOC_IntStatusCmd(instance, (int_type | SDIOC_INT_BRRSEN | SDIOC_INT_BWRSEN), ENABLE); SDIOC_IntCmd(instance, int_type, ENABLE); } else { LOG_D("disable all of the using interrupt"); SDIOC_IntStatusCmd(instance, (int_type | SDIOC_INT_BRRSEN | SDIOC_INT_BWRSEN), DISABLE); SDIOC_IntCmd(instance, int_type, DISABLE); } } /** * @brief Get card status. * @param [in] host Pointer to a @ref rt_mmcsd_host structure * @retval rt_int32_t: * - 0: No card * - 1: Card inserted */ static rt_int32_t _sdio_get_card_status(struct rt_mmcsd_host *host) { struct rthw_sdio *sdio = host->private_data; LOG_D("try to detect device"); return (rt_int32_t)SDIOC_GetHostStatus(sdio->config->instance, SDIOC_HOST_FLAG_CIN); } static const struct rt_mmcsd_host_ops _mmcsd_host_ops = { _sdio_request, _sdio_iocfg, _sdio_get_card_status, _sdio_enable_sdio_irq, }; /** * @brief Get SDIO clock source frequency. * @param [in] SDIOCx Pointer to SDIOC unit instance * @retval SDIO clock source frequency */ static rt_uint32_t _sdio_clock_get(CM_SDIOC_TypeDef *SDIOCx) { rt_uint32_t clk; (void)SDIOCx; #if defined (HC32F4A0) clk = CLK_GetBusClockFreq(CLK_BUS_PCLK1); #elif defined (HC32F460) clk = CLK_GetBusClockFreq(CLK_BUS_EXCLK); #endif return clk; } /** * @brief Initialize DMA for SDIO. * @param [in] config Pointer to hc32_sdio_config structure * @retval None */ static void _sdio_dma_init(struct hc32_sdio_config *config) { stc_dma_init_t stcDmaInit; /* Enable DMA and AOS clock */ FCG_Fcg0PeriphClockCmd(FCG0_PERIPH_AOS, ENABLE); FCG_Fcg0PeriphClockCmd(config->dma_rx.clock, ENABLE); FCG_Fcg0PeriphClockCmd(config->dma_tx.clock, ENABLE); /* Initialize DMA config structure */ (void)DMA_StructInit(&stcDmaInit); stcDmaInit.u32BlockSize = 512UL; stcDmaInit.u32DataWidth = DMA_DATAWIDTH_32BIT; /* Configure DMA_RX Transfer */ stcDmaInit.u32SrcAddr = (uint32_t)(&config->instance->BUF0); stcDmaInit.u32DestAddr = 0UL; stcDmaInit.u32SrcAddrInc = DMA_SRC_ADDR_FIX; stcDmaInit.u32DestAddrInc = DMA_DEST_ADDR_INC; if (LL_OK != DMA_Init(config->dma_rx.Instance, config->dma_rx.channel, &stcDmaInit)) { LOG_E("DMA_RX initialization fail"); } AOS_SetTriggerEventSrc(config->dma_rx.trigger_select, config->dma_rx.trigger_event); /* Configure DMA_TX Transfer */ stcDmaInit.u32SrcAddr = 0UL; stcDmaInit.u32DestAddr = (uint32_t)(&config->instance->BUF0); stcDmaInit.u32SrcAddrInc = DMA_SRC_ADDR_INC; stcDmaInit.u32DestAddrInc = DMA_DEST_ADDR_FIX; if (LL_OK != DMA_Init(config->dma_tx.Instance, config->dma_tx.channel, &stcDmaInit)) { LOG_E("DMA_TX initialization fail"); } AOS_SetTriggerEventSrc(config->dma_tx.trigger_select, config->dma_tx.trigger_event); /* Enable DMA */ DMA_Cmd(config->dma_rx.Instance, ENABLE); DMA_Cmd(config->dma_tx.Instance, ENABLE); } /** * @brief Configure DMA for SDIO receiving. * @param [in] instance Pointer to DMA instance register base * @param [in] ch DMA channel * @param [in] pkg Pointer to sdio_pkg structure * @retval RT_EOK */ static rt_err_t _sdio_dma_rxconfig(CM_DMA_TypeDef *instance, rt_uint8_t ch, struct sdio_pkg *pkg) { struct rt_mmcsd_cmd *cmd = pkg->cmd; struct rt_mmcsd_data *data = cmd->data; DMA_ClearTransCompleteStatus(instance, (DMA_INTSTAT1_BTC_0 | DMA_INTSTAT1_TC_0) << ch); DMA_SetDestAddr(instance, ch, (rt_uint32_t)pkg->buf); DMA_SetBlockSize(instance, ch, (rt_uint16_t)(data->blksize >> 2)); DMA_SetTransCount(instance, ch, (rt_uint16_t)data->blks); return RT_EOK; } /** * @brief Configure DMA for SDIO transmitting. * @param [in] instance Pointer to DMA instance register base * @param [in] ch DMA channel * @param [in] pkg Pointer to sdio_pkg structure * @retval RT_EOK */ static rt_err_t _sdio_dma_txconfig(CM_DMA_TypeDef *instance, rt_uint8_t ch, struct sdio_pkg *pkg) { struct rt_mmcsd_cmd *cmd = pkg->cmd; struct rt_mmcsd_data *data = cmd->data; DMA_ClearTransCompleteStatus(instance, (DMA_INTSTAT1_BTC_0 | DMA_INTSTAT1_TC_0) << ch); DMA_SetSrcAddr(instance, ch, (rt_uint32_t)pkg->buf); DMA_SetBlockSize(instance, ch, (rt_uint16_t)(data->blksize >> 2)); DMA_SetTransCount(instance, ch, (rt_uint16_t)data->blks); return RT_EOK; } /** * @brief This function interrupt process function. * @param [in] host Pointer to rt_mmcsd_host structure * @retval None */ static void _sdio_irq_process(struct rt_mmcsd_host *host) { int complete = 0; struct rthw_sdio *sdio = host->private_data; CM_SDIOC_TypeDef *instance = sdio->config->instance; rt_uint32_t norint_status = (rt_uint32_t)instance->NORINTST; rt_uint32_t errint_status = (rt_uint32_t)instance->ERRINTST; rt_uint32_t status = (errint_status << 16) | norint_status; LOG_D("[%s] sta=0x%08X, cmd %d, arg=0x%08X", __func__, status, sdio->pkg->cmd->cmd_code, sdio->pkg->cmd->arg); if (norint_status & (SDIOC_INT_FLAG_CRM | SDIOC_INT_FLAG_CIST)) { SDIOC_ClearIntStatus(instance, SDIOC_INT_FLAG_CLR_ALL); /* ready to change */ mmcsd_change(host); LOG_D("[%s] card insert or remove", __func__); complete = 1; } else { if (norint_status & SDIOC_INT_FLAG_EI) { SDIOC_ClearIntStatus(instance, SDIOC_INT_FLAG_CLR_ALL); complete = 1; LOG_D("[%s] error", __func__); } else { if (norint_status & SDIOC_INT_FLAG_CC) { SDIOC_ClearIntStatus(instance, SDIOC_INT_FLAG_CC); if (sdio->pkg != RT_NULL) { if ((!sdio->pkg->cmd->data) && (resp_type(sdio->pkg->cmd) != RESP_R1B)) { complete = 1; } } } if (norint_status & SDIOC_INT_FLAG_TC) { SDIOC_ClearIntStatus(instance, SDIOC_INT_FLAG_TC); complete = 1; } } } if (norint_status & SDIOC_INT_FLAG_CINT) { SDIOC_ClearIntStatus(instance, SDIOC_INT_FLAG_CINT); sdio_irq_wakeup(host); } if (complete) { rt_event_send(&sdio->event, status); LOG_D("[%s] complete", __func__); } } #ifdef BSP_USING_SDIO1 /** * @brief SDIOC1 irq handler. * @param None * @retval None */ static void _sdio1_handler(void) { /* enter interrupt */ rt_interrupt_enter(); /* process all SDIO interrupt sources */ _sdio_irq_process(_sdio_host[0]); /* leave interrupt */ rt_interrupt_leave(); } #endif #ifdef BSP_USING_SDIO2 /** * @brief SDIOC2 irq handler. * @param None * @retval None */ static void _sdio2_handler(void) { /* enter interrupt */ rt_interrupt_enter(); /* process all SDIO interrupt sources */ _sdio_irq_process(_sdio_host[1]); /* leave interrupt */ rt_interrupt_leave(); } #endif /** * @brief verify bus clock frequency. * @param [in] config Pointer to hc32_sdio_config structure * @retval RT_EOK pass to verify * -RT_ERROR fail to verify */ static rt_err_t _sdio_verify_bus_clock_frequency(struct hc32_sdio_config *config) { rt_err_t ret = RT_EOK; #if defined (HC32F4A0) rt_uint32_t pclk1; rt_uint32_t exlck; (void)config; /* ensure bus frequency condition: EXCLK >= PCLK1 */ pclk1 = CLK_GetBusClockFreq(CLK_BUS_PCLK1); exlck = CLK_GetBusClockFreq(CLK_BUS_EXCLK); if (exlck < pclk1) { LOG_E("bus frequency error: EXCLK < PCLK1. Please meet the bus frequency condition: EXCLK >= PCLK1"); ret = -RT_ERROR; } #endif return ret; } /** * @brief Enable SDIO clock. * @param [in] config Pointer to hc32_sdio_config structure * @retval RT_EOK pass to enable SDIO clock * -RT_ERROR fail to enable SDIO clock */ static rt_err_t _sdio_clock_enable(struct hc32_sdio_config *config) { /* verify bus clock frequency */ if (_sdio_verify_bus_clock_frequency(config) != RT_EOK) { LOG_E("[%s] fail to verify bus clock frequency", __func__); return -RT_ERROR; } FCG_Fcg1PeriphClockCmd(config->clock, ENABLE); return RT_EOK; } /** * @brief Create mmcsd host. * @param [in] config Pointer to a @ref hc32_sdio_config structure * @param [in] cache_buf Pointer to cache buffer * @param [in] sdio_des Pointer to a @ref hc32_sdio_des structure * @retval rt_mmcsd_host */ static struct rt_mmcsd_host *_sdio_host_create(struct hc32_sdio_config *config, uint8_t *cache_buf, const struct hc32_sdio_des *sdio_des) { struct rt_mmcsd_host *host; struct rthw_sdio *sdio = RT_NULL; if ((config == RT_NULL) || (cache_buf == RT_NULL) || \ (sdio_des == RT_NULL) || (sdio_des->txconfig == RT_NULL) || (sdio_des->rxconfig == RT_NULL)) { LOG_E("function arguments error: %s %s %s %s %s", (config == RT_NULL ? "config is NULL" : ""), (cache_buf == RT_NULL ? "cache_buf is NULL" : ""), (sdio_des == RT_NULL ? "sdio_des is NULL" : ""), (sdio_des ? (sdio_des->txconfig ? "txconfig is NULL" : "") : ""), (sdio_des ? (sdio_des->rxconfig ? "rxconfig is NULL" : "") : "") ); return RT_NULL; } /* malloc rthw_sdio */ sdio = rt_malloc(sizeof(struct rthw_sdio)); if (sdio == RT_NULL) { LOG_E("malloc rthw_sdio fail"); return RT_NULL; } rt_memset(sdio, 0, sizeof(struct rthw_sdio)); /* malloc mmcsd_alloc_host */ host = mmcsd_alloc_host(); if (host == RT_NULL) { LOG_E("mmcsd_alloc_host fail"); rt_free(sdio); return RT_NULL; } rt_memcpy(&sdio->des, sdio_des, sizeof(struct hc32_sdio_des)); rt_event_init(&sdio->event, "sdio", RT_IPC_FLAG_FIFO); rt_mutex_init(&sdio->mutex, "sdio", RT_IPC_FLAG_FIFO); /* set host default attributes */ host->ops = &_mmcsd_host_ops; host->freq_min = 400 * 1000; host->freq_max = SDIO_MAX_FREQ; host->valid_ocr = VDD_26_27 | VDD_27_28 | VDD_28_29 | VDD_29_30 | VDD_30_31 | VDD_31_32 | VDD_32_33 | VDD_33_34;/* The voltage range supported is 2.6v-3.4v */ host->valid_ocr = VDD_32_33 | VDD_33_34; #ifndef SDIO_USING_1_BIT host->flags = MMCSD_BUSWIDTH_4 | MMCSD_MUTBLKWRITE | MMCSD_SUP_SDIO_IRQ; #else host->flags = MMCSD_MUTBLKWRITE | MMCSD_SUP_SDIO_IRQ; #endif host->max_seg_size = SDIO_BUFF_SIZE; host->max_dma_segs = 1; host->max_blk_size = 512; host->max_blk_count = (SDIO_BUFF_SIZE / host->max_blk_size); /* link up host, config, cache_buf and sdio */ sdio->host = host; sdio->config = config; sdio->cache_buf = cache_buf; host->private_data = sdio; /* enable interrupt */ _sdio_update_irq(host, 1); /* ready to change */ mmcsd_change(host); return host; } int rt_hw_sdio_init(void) { struct rt_mmcsd_host *host; struct hc32_sdio_config *sdio_config; rt_size_t obj_num = sizeof(_sdio_config) / sizeof(struct hc32_sdio_config); const struct hc32_sdio_des sdio_des = { .clk_get = _sdio_clock_get, .rxconfig = _sdio_dma_rxconfig, .txconfig = _sdio_dma_txconfig, }; for (rt_size_t i = 0; i < obj_num; i++) { sdio_config = &_sdio_config[i]; if (_sdio_clock_enable(sdio_config) != RT_EOK) { LOG_E("clock enable fail"); return -1; } host = _sdio_host_create(sdio_config, _sdio_cache_buf[i], &sdio_des); if (host == RT_NULL) { LOG_E("host create fail"); return -1; } else { /* link host */ _sdio_host[i] = host; /* init board pin */ rt_hw_board_sdio_init(sdio_config->instance); /* init DMA */ _sdio_dma_init(sdio_config); /* register the irq handler */ hc32_install_irq_handler(&sdio_config->irq_config, _sdio_irq_handler[i], RT_TRUE); } } return RT_EOK; } INIT_DEVICE_EXPORT(rt_hw_sdio_init); #endif /* BSP_USING_SDIO */ #endif /* RT_USING_SDIO */