511 lines
18 KiB
C
511 lines
18 KiB
C
/*
|
|
* Copyright (c) 2006-2021, RT-Thread Development Team
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Change Logs:
|
|
* Date Author Notes
|
|
* 2021-11-03 mazhiyuan first version
|
|
*/
|
|
|
|
#include <drv_sdhi.h>
|
|
struct ra_sdhi sdhi;
|
|
|
|
#define RTHW_SDIO_LOCK(_sdio) rt_mutex_take(&_sdio->mutex, RT_WAITING_FOREVER)
|
|
#define RTHW_SDIO_UNLOCK(_sdio) rt_mutex_release(&_sdio->mutex);
|
|
|
|
struct rthw_sdio
|
|
{
|
|
struct rt_mmcsd_host *host;
|
|
struct ra_sdhi sdhi_des;
|
|
struct rt_event event;
|
|
struct rt_mutex mutex;
|
|
};
|
|
|
|
static struct rt_mmcsd_host *host;
|
|
|
|
ALIGN(SDIO_ALIGN_LEN)
|
|
static rt_uint8_t cache_buf[SDIO_BUFF_SIZE];
|
|
|
|
rt_err_t command_send(sdhi_instance_ctrl_t *p_ctrl, struct rt_mmcsd_cmd *cmd)
|
|
{
|
|
uint32_t wait_bit;
|
|
uint32_t timeout = BUSY_TIMEOUT_US;
|
|
volatile sdhi_event_t event;
|
|
struct rt_mmcsd_data *data = cmd->data;
|
|
while (SD_INFO2_CBSY_SDD0MON_IDLE_VAL !=
|
|
(p_ctrl->p_reg->SD_INFO2 & SD_INFO2_CBSY_SDD0MON_IDLE_MASK))
|
|
{
|
|
if (timeout == 0)
|
|
{
|
|
return RT_ETIMEOUT;
|
|
}
|
|
R_BSP_SoftwareDelay(1U, BSP_DELAY_UNITS_MICROSECONDS);
|
|
timeout--;
|
|
}
|
|
p_ctrl->p_reg->SD_INFO1 = 0U;
|
|
p_ctrl->p_reg->SD_INFO2 = 0U;
|
|
p_ctrl->sdhi_event.word = 0U;
|
|
|
|
/* Enable response end interrupt. */
|
|
/* Disable access end interrupt and enable response end interrupt. */
|
|
uint32_t mask = p_ctrl->p_reg->SD_INFO1_MASK;
|
|
mask &= (~SDHI_INFO1_RESPONSE_END);
|
|
mask |= SDHI_INFO1_ACCESS_END;
|
|
p_ctrl->p_reg->SD_INFO1_MASK = mask;
|
|
p_ctrl->p_reg->SD_INFO2_MASK = SDHI_INFO2_MASK_CMD_SEND;
|
|
/* Write argument, then command to the SDHI peripheral. */
|
|
p_ctrl->p_reg->SD_ARG = cmd->arg & UINT16_MAX;
|
|
p_ctrl->p_reg->SD_ARG1 = cmd->arg >> 16;
|
|
|
|
if ((cmd->flags & CMD_MASK) == CMD_ADTC)
|
|
{
|
|
cmd->cmd_code |= SDHI_CMD_ADTC_EN;
|
|
switch (cmd->flags & RESP_MASK)
|
|
{
|
|
case RESP_R1:
|
|
case RESP_R5:
|
|
case RESP_R6:
|
|
case RESP_R7:
|
|
cmd->cmd_code |= SDHI_CMD_RESP_TYPE_EXT_R1_R5_R6_R7;
|
|
break;
|
|
case RESP_R1B:
|
|
cmd->cmd_code |= SDHI_CMD_RESP_TYPE_EXT_R1B;
|
|
break;
|
|
case RESP_R2:
|
|
cmd->cmd_code |= SDHI_CMD_RESP_TYPE_EXT_R2;
|
|
break;
|
|
case RESP_R3:
|
|
case RESP_R4:
|
|
cmd->cmd_code |= SDHI_CMD_RESP_TYPE_EXT_R3_R4;
|
|
break;
|
|
case RESP_NONE:
|
|
cmd->cmd_code |= SDHI_CMD_RESP_TYPE_EXT_NONE;
|
|
break;
|
|
}
|
|
if (data != RT_NULL)
|
|
{
|
|
if ((data->flags & 7) == DATA_DIR_WRITE)
|
|
{
|
|
cmd->cmd_code &= ~SDHI_CMD_DATA_DIR_READ;
|
|
}
|
|
else if ((data->flags & 7) == DATA_DIR_READ)
|
|
{
|
|
cmd->cmd_code |= SDHI_CMD_DATA_DIR_READ;
|
|
}
|
|
}
|
|
}
|
|
p_ctrl->p_reg->SD_CMD = cmd->cmd_code;
|
|
|
|
timeout = 100000;
|
|
while (true)
|
|
{
|
|
/* Check for updates to the event status. */
|
|
event.word = p_ctrl->sdhi_event.word;
|
|
|
|
/* Return an error if a hardware error occurred. */
|
|
if (event.bit.event_error)
|
|
{
|
|
cmd->err = -RT_ERROR;
|
|
if ((event.word & HW_SDHI_ERR_CRCE) && (resp_type(cmd) & (RESP_R3 | RESP_R4)))
|
|
{
|
|
if ((cmd->flags & RESP_MASK) == RESP_R2)
|
|
{
|
|
cmd->resp[0] = (p_ctrl->p_reg->SD_RSP76 << 8) | (p_ctrl->p_reg->SD_RSP54 >> 24);
|
|
cmd->resp[1] = (p_ctrl->p_reg->SD_RSP54 << 8) | (p_ctrl->p_reg->SD_RSP32 >> 24);
|
|
cmd->resp[2] = (p_ctrl->p_reg->SD_RSP32 << 8) | (p_ctrl->p_reg->SD_RSP10 >> 24);
|
|
cmd->resp[3] = (p_ctrl->p_reg->SD_RSP10 << 8);
|
|
}
|
|
else
|
|
{
|
|
cmd->resp[0] = p_ctrl->p_reg->SD_RSP10;
|
|
}
|
|
cmd->err = RT_EOK;
|
|
}
|
|
if (event.word & HW_SDHI_ERR_RTIMEOUT)
|
|
{
|
|
cmd->err = -RT_ETIMEOUT;
|
|
}
|
|
if (event.word & HW_SDHI_ERR_DTIMEOUT)
|
|
{
|
|
data->err = -RT_ETIMEOUT;
|
|
}
|
|
return -RT_ERROR;
|
|
}
|
|
if (data != RT_NULL)
|
|
{
|
|
wait_bit = SDHI_WAIT_ACCESS_BIT;
|
|
}
|
|
else
|
|
{
|
|
wait_bit = SDHI_WAIT_RESPONSE_BIT;
|
|
}
|
|
|
|
/* If the requested bit is set, return success. */
|
|
if (event.word & (1U << wait_bit))
|
|
{
|
|
cmd->err = RT_EOK;
|
|
if ((cmd->flags & RESP_MASK) == RESP_R2)
|
|
{
|
|
cmd->resp[0] = (p_ctrl->p_reg->SD_RSP76 << 8) | (p_ctrl->p_reg->SD_RSP54 >> 24);
|
|
cmd->resp[1] = (p_ctrl->p_reg->SD_RSP54 << 8) | (p_ctrl->p_reg->SD_RSP32 >> 24);
|
|
cmd->resp[2] = (p_ctrl->p_reg->SD_RSP32 << 8) | (p_ctrl->p_reg->SD_RSP10 >> 24);
|
|
cmd->resp[3] = (p_ctrl->p_reg->SD_RSP10 << 8);
|
|
}
|
|
else
|
|
{
|
|
cmd->resp[0] = p_ctrl->p_reg->SD_RSP10;
|
|
}
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
/* Check for timeout. */
|
|
timeout--;
|
|
if (0U == timeout)
|
|
{
|
|
cmd->err = -RT_ETIMEOUT;
|
|
return RT_ERROR;
|
|
}
|
|
|
|
/* Wait 1 us for consistent loop timing. */
|
|
R_BSP_SoftwareDelay(1U, BSP_DELAY_UNITS_MICROSECONDS);
|
|
}
|
|
}
|
|
|
|
rt_err_t transfer_write(sdhi_instance_ctrl_t *const p_ctrl,
|
|
uint32_t block_count,
|
|
uint32_t bytes,
|
|
const uint8_t *p_data)
|
|
{
|
|
transfer_info_t *p_info = p_ctrl->p_cfg->p_lower_lvl_transfer->p_cfg->p_info;
|
|
|
|
/* When the SD_DMAEN.DMAEN bit is 1, set the SD_INFO2_MASK.BWEM bit to 1 and the SD_INFO2_MASK.BREM bit to 1. */
|
|
p_ctrl->p_reg->SD_INFO2_MASK |= 0x300U;
|
|
p_ctrl->p_reg->SD_DMAEN = 0x2U;
|
|
|
|
uint32_t transfer_settings = (uint32_t)TRANSFER_MODE_BLOCK << TRANSFER_SETTINGS_MODE_BITS;
|
|
transfer_settings |= TRANSFER_ADDR_MODE_INCREMENTED << TRANSFER_SETTINGS_SRC_ADDR_BITS;
|
|
transfer_settings |= TRANSFER_SIZE_4_BYTE << TRANSFER_SETTINGS_SIZE_BITS;
|
|
|
|
#if SDMMC_CFG_UNALIGNED_ACCESS_ENABLE
|
|
if ((0U != ((uint32_t)p_data & 0x3U)) || (0U != (bytes & 3U)))
|
|
{
|
|
transfer_settings |= TRANSFER_IRQ_EACH << TRANSFER_SETTINGS_IRQ_BITS;
|
|
transfer_settings |= TRANSFER_REPEAT_AREA_SOURCE << TRANSFER_SETTINGS_REPEAT_AREA_BITS;
|
|
|
|
/* If the pointer is not 4-byte aligned or the number of bytes is not a multiple of 4, use a temporary buffer.
|
|
* Transfer the first block to the temporary buffer before enabling the transfer. Subsequent blocks will be
|
|
* transferred from the user buffer to the temporary buffer in an interrupt after each block transfer. */
|
|
rt_memcpy((void *)&p_ctrl->aligned_buff[0], p_data, bytes);
|
|
p_info->p_src = &p_ctrl->aligned_buff[0];
|
|
|
|
p_ctrl->transfer_block_current = 1U;
|
|
p_ctrl->transfer_blocks_total = block_count;
|
|
p_ctrl->p_transfer_data = (uint8_t *)&p_data[bytes];
|
|
p_ctrl->transfer_dir = SDHI_TRANSFER_DIR_WRITE;
|
|
p_ctrl->transfer_block_size = bytes;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
p_info->p_src = p_data;
|
|
}
|
|
|
|
p_info->transfer_settings_word = transfer_settings;
|
|
p_info->p_dest = (uint32_t *)(&p_ctrl->p_reg->SD_BUF0);
|
|
p_info->num_blocks = (uint16_t)block_count;
|
|
|
|
/* Round up to the nearest multiple of 4 bytes for the transfer. */
|
|
uint32_t words = (bytes + (sizeof(uint32_t) - 1U)) / sizeof(uint32_t);
|
|
p_info->length = (uint16_t)words;
|
|
/* Configure the transfer driver to write to the SD buffer. */
|
|
fsp_err_t err = p_ctrl->p_cfg->p_lower_lvl_transfer->p_api->reconfigure(p_ctrl->p_cfg->p_lower_lvl_transfer->p_ctrl,
|
|
p_ctrl->p_cfg->p_lower_lvl_transfer->p_cfg->p_info);
|
|
if (FSP_SUCCESS != err)
|
|
return RT_ERROR;
|
|
return RT_EOK;
|
|
}
|
|
|
|
rt_err_t transfer_read(sdhi_instance_ctrl_t *const p_ctrl,
|
|
uint32_t block_count,
|
|
uint32_t bytes,
|
|
void *p_data)
|
|
{
|
|
transfer_info_t *p_info = p_ctrl->p_cfg->p_lower_lvl_transfer->p_cfg->p_info;
|
|
|
|
/* When the SD_DMAEN.DMAEN bit is 1, set the SD_INFO2_MASK.BWEM bit to 1 and the SD_INFO2_MASK.BREM bit to 1. */
|
|
p_ctrl->p_reg->SD_INFO2_MASK |= 0X300U;
|
|
p_ctrl->p_reg->SD_DMAEN = 0x2U;
|
|
|
|
uint32_t transfer_settings = (uint32_t)TRANSFER_MODE_BLOCK << TRANSFER_SETTINGS_MODE_BITS;
|
|
transfer_settings |= TRANSFER_ADDR_MODE_INCREMENTED << TRANSFER_SETTINGS_DEST_ADDR_BITS;
|
|
transfer_settings |= TRANSFER_SIZE_4_BYTE << TRANSFER_SETTINGS_SIZE_BITS;
|
|
|
|
#if SDMMC_CFG_UNALIGNED_ACCESS_ENABLE
|
|
|
|
/* If the pointer is not 4-byte aligned or the number of bytes is not a multiple of 4, use a temporary buffer.
|
|
* Data will be transferred from the temporary buffer into the user buffer in an interrupt after each block transfer. */
|
|
if ((0U != ((uint32_t)p_data & 0x3U)) || (0U != (bytes & 3U)))
|
|
{
|
|
transfer_settings |= TRANSFER_IRQ_EACH << TRANSFER_SETTINGS_IRQ_BITS;
|
|
p_info->p_dest = &p_ctrl->aligned_buff[0];
|
|
|
|
p_ctrl->transfer_block_current = 0U;
|
|
p_ctrl->transfer_blocks_total = block_count;
|
|
p_ctrl->p_transfer_data = (uint8_t *)p_data;
|
|
p_ctrl->transfer_dir = SDHI_TRANSFER_DIR_READ;
|
|
p_ctrl->transfer_block_size = bytes;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
transfer_settings |= TRANSFER_REPEAT_AREA_SOURCE << TRANSFER_SETTINGS_REPEAT_AREA_BITS;
|
|
p_info->p_dest = p_data;
|
|
}
|
|
|
|
p_info->transfer_settings_word = transfer_settings;
|
|
p_info->p_src = (uint32_t *)(&p_ctrl->p_reg->SD_BUF0);
|
|
p_info->num_blocks = (uint16_t)block_count;
|
|
|
|
/* Round up to the nearest multiple of 4 bytes for the transfer. */
|
|
uint32_t words = (bytes + (sizeof(uint32_t) - 1U)) / sizeof(uint32_t);
|
|
p_info->length = (uint16_t)words;
|
|
|
|
/* Configure the transfer driver to read from the SD buffer. */
|
|
fsp_err_t err = p_ctrl->p_cfg->p_lower_lvl_transfer->p_api->reconfigure(p_ctrl->p_cfg->p_lower_lvl_transfer->p_ctrl,
|
|
p_ctrl->p_cfg->p_lower_lvl_transfer->p_cfg->p_info);
|
|
if (err != FSP_SUCCESS)
|
|
return RT_ERROR;
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
void ra_sdhi_request(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req)
|
|
{
|
|
struct rthw_sdio *sdio = host->private_data;
|
|
struct rt_mmcsd_data *data;
|
|
static rt_uint8_t *buffer;
|
|
|
|
RTHW_SDIO_LOCK(sdio);
|
|
|
|
if (req->cmd != RT_NULL)
|
|
{
|
|
data = req->cmd->data;
|
|
if (data != RT_NULL)
|
|
{
|
|
rt_uint32_t size = data->blks * data->blksize;
|
|
|
|
RT_ASSERT(size <= SDIO_BUFF_SIZE);
|
|
|
|
buffer = (rt_uint8_t *)data->buf;
|
|
if ((rt_uint32_t)data->buf & (SDIO_ALIGN_LEN - 1))
|
|
{
|
|
buffer = cache_buf;
|
|
if (data->flags & DATA_DIR_WRITE)
|
|
{
|
|
rt_memcpy(cache_buf, data->buf, size);
|
|
}
|
|
}
|
|
if (data->flags & DATA_DIR_WRITE)
|
|
{
|
|
transfer_write(sdio->sdhi_des.instance->p_ctrl, data->blks, data->blksize, buffer);
|
|
}
|
|
else if (data->flags & DATA_DIR_READ)
|
|
{
|
|
transfer_read(sdio->sdhi_des.instance->p_ctrl, data->blks, data->blksize, buffer);
|
|
}
|
|
/* Set the sector count. */
|
|
if (data->blks > 1U)
|
|
{
|
|
((sdhi_instance_ctrl_t *)sdio->sdhi_des.instance->p_ctrl)->p_reg->SD_STOP = 0x100U;
|
|
((sdhi_instance_ctrl_t *)sdio->sdhi_des.instance->p_ctrl)->p_reg->SD_SECCNT = data->blks;
|
|
}
|
|
else
|
|
{
|
|
((sdhi_instance_ctrl_t *)sdio->sdhi_des.instance->p_ctrl)->p_reg->SD_STOP = 0U;
|
|
}
|
|
((sdhi_instance_ctrl_t *)sdio->sdhi_des.instance->p_ctrl)->p_reg->SD_SIZE = data->blksize;
|
|
}
|
|
rt_enter_critical();
|
|
command_send(sdio->sdhi_des.instance->p_ctrl, req->cmd);
|
|
rt_exit_critical();
|
|
if ((data != RT_NULL) && (data->flags & DATA_DIR_READ) && ((rt_uint32_t)data->buf & (SDIO_ALIGN_LEN - 1)))
|
|
{
|
|
rt_memcpy(data->buf, cache_buf, data->blksize * data->blks);
|
|
}
|
|
}
|
|
|
|
if (req->stop != RT_NULL)
|
|
{
|
|
rt_enter_critical();
|
|
command_send(sdio->sdhi_des.instance->p_ctrl, req->stop);
|
|
rt_exit_critical();
|
|
}
|
|
RTHW_SDIO_UNLOCK(sdio);
|
|
mmcsd_req_complete(sdio->host);
|
|
}
|
|
|
|
static rt_err_t clock_rate_set(sdhi_instance_ctrl_t *p_ctrl, uint32_t max_rate)
|
|
{
|
|
uint32_t setting = 0xFFU;
|
|
|
|
/* Get the runtime frequency of the source of the SD clock */
|
|
uint32_t frequency = R_FSP_SystemClockHzGet(BSP_FEATURE_SDHI_CLOCK);
|
|
|
|
/* Iterate over all possible divisors, starting with the smallest, until the resulting clock rate is less than
|
|
* or equal to the requested maximum rate. */
|
|
for (uint32_t divisor_shift = BSP_FEATURE_SDHI_MIN_CLOCK_DIVISION_SHIFT;
|
|
divisor_shift <= 9U;
|
|
divisor_shift++)
|
|
{
|
|
if ((frequency >> divisor_shift) <= max_rate)
|
|
{
|
|
/* If the calculated frequency is less than or equal to the maximum supported by the device,
|
|
* select this frequency. The register setting is the divisor value divided by 4, or 0xFF for no divider. */
|
|
setting = divisor_shift ? ((1U << divisor_shift) >> 2U) : UINT8_MAX;
|
|
|
|
/* Set the clock setting. */
|
|
|
|
/* The clock register is accessible 8 SD clock counts after the last command completes. Each register access
|
|
* requires at least one PCLK count, so check the register up to 8 times the maximum PCLK divisor value (512). */
|
|
uint32_t timeout = 8U * 512U;
|
|
|
|
while (timeout > 0U)
|
|
{
|
|
/* Do not write to clock control register until this bit is set. */
|
|
if (p_ctrl->p_reg->SD_INFO2_b.SD_CLK_CTRLEN)
|
|
{
|
|
/* Set the calculated divider and enable clock output to start the 74 clocks required before
|
|
* initialization. Do not change the automatic clock control setting. */
|
|
uint32_t clkctrlen = p_ctrl->p_reg->SD_CLK_CTRL & (1U << 9);
|
|
p_ctrl->p_reg->SD_CLK_CTRL = setting | clkctrlen | (1U << 8);
|
|
p_ctrl->device.clock_rate = frequency >> divisor_shift;
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
timeout--;
|
|
}
|
|
|
|
/* Valid setting already found, stop looking. */
|
|
break;
|
|
}
|
|
}
|
|
|
|
return RT_ERROR;
|
|
}
|
|
|
|
void ra_sdhi_set_iocfg(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *io_cfg)
|
|
{
|
|
struct rthw_sdio *sdio = host->private_data;
|
|
RTHW_SDIO_LOCK(sdio);
|
|
if (io_cfg->bus_width == MMCSD_BUS_WIDTH_1)
|
|
{
|
|
((sdhi_instance_ctrl_t *)sdio->sdhi_des.instance->p_ctrl)->p_reg->SD_OPTION_b.WIDTH = 1;
|
|
}
|
|
else if (io_cfg->bus_width == MMCSD_BUS_WIDTH_4)
|
|
{
|
|
((sdhi_instance_ctrl_t *)sdio->sdhi_des.instance->p_ctrl)->p_reg->SD_OPTION_b.WIDTH = 0;
|
|
((sdhi_instance_ctrl_t *)sdio->sdhi_des.instance->p_ctrl)->p_reg->SD_OPTION_b.WIDTH8 = 0;
|
|
}
|
|
else if (io_cfg->bus_width == MMCSD_BUS_WIDTH_8)
|
|
{
|
|
((sdhi_instance_ctrl_t *)sdio->sdhi_des.instance->p_ctrl)->p_reg->SD_OPTION_b.WIDTH = 0;
|
|
((sdhi_instance_ctrl_t *)sdio->sdhi_des.instance->p_ctrl)->p_reg->SD_OPTION_b.WIDTH8 = 1;
|
|
}
|
|
clock_rate_set(sdio->sdhi_des.instance->p_ctrl, io_cfg->clock);
|
|
RTHW_SDIO_UNLOCK(sdio);
|
|
}
|
|
|
|
rt_int32_t ra_sdhi_get_card_status(struct rt_mmcsd_host *host)
|
|
{
|
|
sdmmc_status_t status;
|
|
struct rthw_sdio *sdio = host->private_data;
|
|
sdio->sdhi_des.instance->p_api->statusGet(sdio->sdhi_des.instance->p_ctrl, &status);
|
|
return status.card_inserted;
|
|
}
|
|
|
|
void ra_sdhi_enable_sdio_irq(struct rt_mmcsd_host *host, rt_int32_t en)
|
|
{
|
|
struct rthw_sdio *sdio = host->private_data;
|
|
sdio->sdhi_des.instance->p_api->ioIntEnable(sdio->sdhi_des.instance->p_ctrl, en);
|
|
}
|
|
|
|
struct rt_mmcsd_host_ops ra_sdhi_ops =
|
|
{
|
|
.request = ra_sdhi_request,
|
|
.set_iocfg = ra_sdhi_set_iocfg,
|
|
.get_card_status = ra_sdhi_get_card_status,
|
|
.enable_sdio_irq = ra_sdhi_enable_sdio_irq
|
|
};
|
|
|
|
void sdhi_callback(sdmmc_callback_args_t *p_args)
|
|
{
|
|
}
|
|
|
|
struct rt_mmcsd_host *sdio_host_create(struct ra_sdhi *sdhi_des)
|
|
{
|
|
struct rt_mmcsd_host *host;
|
|
struct rthw_sdio *sdio = RT_NULL;
|
|
|
|
if (sdhi_des == RT_NULL)
|
|
return RT_NULL;
|
|
|
|
sdio = rt_malloc(sizeof(struct rthw_sdio));
|
|
if (sdio == RT_NULL)
|
|
return RT_NULL;
|
|
rt_memset(sdio, 0, sizeof(struct rthw_sdio));
|
|
|
|
host = mmcsd_alloc_host();
|
|
if (host == RT_NULL)
|
|
{
|
|
rt_free(sdio);
|
|
return RT_NULL;
|
|
}
|
|
|
|
rt_memcpy(&sdio->sdhi_des, sdhi_des, sizeof(struct ra_sdhi));
|
|
|
|
rt_event_init(&sdio->event, "sdio", RT_IPC_FLAG_FIFO);
|
|
rt_mutex_init(&sdio->mutex, "sdio", RT_IPC_FLAG_FIFO);
|
|
|
|
/* set host defautl attributes */
|
|
host->ops = &ra_sdhi_ops;
|
|
host->freq_min = 400 * 1000;
|
|
host->freq_max = SDIO_MAX_FREQ;
|
|
host->valid_ocr = 0X00FFFF80; /* The voltage range supported is 1.65v-3.6v */
|
|
#ifndef SDHI_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 = 512;
|
|
|
|
/* link up host and sdio */
|
|
sdio->host = host;
|
|
host->private_data = sdio;
|
|
|
|
ra_sdhi_enable_sdio_irq(host, 1);
|
|
|
|
/* ready to change */
|
|
mmcsd_change(host);
|
|
|
|
return host;
|
|
}
|
|
|
|
int rt_hw_sdhi_init(void)
|
|
{
|
|
sdhi.instance = &g_sdmmc0;
|
|
sdhi.instance->p_api->open(sdhi.instance->p_ctrl, sdhi.instance->p_cfg);
|
|
host = sdio_host_create(&sdhi);
|
|
if (host == RT_NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
INIT_DEVICE_EXPORT(rt_hw_sdhi_init);
|