123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510 |
- /*
- * 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);
|