| /* |
| * Copyright (c) 2024-2025 Renesas Electronics Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT renesas_ra_sdhc |
| |
| #include <zephyr/kernel.h> |
| #include <zephyr/devicetree.h> |
| #include <zephyr/drivers/pinctrl.h> |
| #include <zephyr/drivers/gpio.h> |
| #include <zephyr/irq.h> |
| #include <soc.h> |
| #include <zephyr/logging/log.h> |
| #include <zephyr/drivers/sdhc.h> |
| |
| /* Renesas include */ |
| #include "sdhc_renesas_ra.h" |
| #include "r_sdhi.h" |
| #include "r_dtc.h" |
| #include "r_sdhi_private.h" |
| |
| LOG_MODULE_REGISTER(sdhc_renesas_ra, CONFIG_SDHC_LOG_LEVEL); |
| |
| /* |
| * The extern functions below are implemented in the r_sdhi.c source file. |
| * For more information, please refer to r_sdhi.c in HAL Renesas |
| */ |
| extern fsp_err_t r_sdhi_transfer_write(sdhi_instance_ctrl_t *const p_ctrl, uint32_t block_count, |
| uint32_t bytes, const uint8_t *p_data); |
| extern fsp_err_t r_sdhi_transfer_read(sdhi_instance_ctrl_t *const p_ctrl, uint32_t block_count, |
| uint32_t bytes, void *p_data); |
| extern fsp_err_t r_sdhi_max_clock_rate_set(sdhi_instance_ctrl_t *p_ctrl, uint32_t max_rate); |
| extern fsp_err_t r_sdhi_hw_cfg(sdhi_instance_ctrl_t *const p_ctrl); |
| extern fsp_err_t r_sdhi_read_and_block(sdhi_instance_ctrl_t *const p_ctrl, uint32_t command, |
| uint32_t argument, uint32_t byte_count); |
| extern fsp_err_t r_sdhi_wait_for_device(sdhi_instance_ctrl_t *const p_ctrl); |
| extern fsp_err_t r_sdhi_wait_for_event(sdhi_instance_ctrl_t *const p_ctrl, uint32_t bit, |
| uint32_t timeout); |
| extern void r_sdhi_command_send_no_wait(sdhi_instance_ctrl_t *p_ctrl, uint32_t command, |
| uint32_t argument); |
| extern void r_sdhi_read_write_common(sdhi_instance_ctrl_t *const p_ctrl, uint32_t sector_count, |
| uint32_t sector_size, uint32_t command, uint32_t argument); |
| |
| struct sdhc_ra_config { |
| const struct pinctrl_dev_config *pcfg; |
| void *const regs; |
| }; |
| |
| struct sdhc_ra_priv { |
| struct st_sdmmc_instance_ctrl sdmmc_ctrl; |
| struct st_sdmmc_cfg fsp_config; |
| struct gpio_dt_spec sdhi_en; |
| struct sdmmc_ra_event sdmmc_event; |
| uint8_t channel; |
| bool app_cmd; |
| uint32_t bus_clock; |
| uint8_t bus_width; |
| enum sdhc_timing_mode timing; |
| enum sdhc_power power_mode; |
| struct k_sem thread_lock; |
| uint8_t status; |
| struct sdhc_host_props props; |
| /* Transfer DTC */ |
| struct st_transfer_instance transfer; |
| struct st_dtc_instance_ctrl transfer_ctrl; |
| struct st_transfer_info transfer_info DTC_TRANSFER_INFO_ALIGNMENT; |
| struct st_transfer_cfg transfer_cfg; |
| struct st_dtc_extended_cfg transfer_cfg_extend; |
| }; |
| |
| void sdhimmc_accs_isr(void); |
| void sdhimmc_card_isr(void); |
| void sdhimmc_dma_req_isr(void); |
| |
| static void ra_sdmmc_accs_isr(const void *parameter) |
| { |
| ARG_UNUSED(parameter); |
| sdhimmc_accs_isr(); |
| } |
| |
| static void ra_sdmmc_card_isr(const void *parameter) |
| { |
| ARG_UNUSED(parameter); |
| sdhimmc_card_isr(); |
| } |
| |
| static void ra_sdmmc_dma_req_isr(const void *parameter) |
| { |
| ARG_UNUSED(parameter); |
| sdhimmc_dma_req_isr(); |
| } |
| |
| static int sdhc_ra_get_card_present(const struct device *dev) |
| { |
| struct sdhc_ra_priv *priv = dev->data; |
| fsp_err_t fsp_err; |
| int ret; |
| sdmmc_status_t status; |
| |
| /* SDMMC_CARD_DETECT_CD must be configured as true to check here */ |
| fsp_err = R_SDHI_StatusGet(&priv->sdmmc_ctrl, &status); |
| ret = err_fsp2zep(fsp_err); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| return (status.card_inserted); |
| } |
| |
| static int sdhc_ra_card_busy(const struct device *dev) |
| { |
| struct sdhc_ra_priv *priv = dev->data; |
| fsp_err_t fsp_err; |
| int ret; |
| sdmmc_status_t status; |
| |
| fsp_err = R_SDHI_StatusGet(&priv->sdmmc_ctrl, &status); |
| ret = err_fsp2zep(fsp_err); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| return (status.transfer_in_progress); |
| } |
| |
| static int sdhi_command_send_wait(sdhi_instance_ctrl_t *p_ctrl, uint32_t command, uint32_t argument, |
| uint32_t timeout_ms) |
| { |
| /* Verify the device is not busy. */ |
| r_sdhi_wait_for_device(p_ctrl); |
| |
| /* Convert timeout to us */ |
| uint32_t timeout_us = timeout_ms * 1000U; |
| |
| /* Send the command. */ |
| r_sdhi_command_send_no_wait(p_ctrl, command, argument); |
| |
| /* Wait for end of response, error or timeout */ |
| return r_sdhi_wait_for_event(p_ctrl, SDHI_PRV_RESPONSE_BIT, timeout_us); |
| } |
| |
| static int sdhc_ra_send_cmd(struct sdhc_ra_priv *priv, struct sdmmc_ra_command *ra_cmd, int retries) |
| { |
| int fsp_err = 0; |
| |
| while (retries > 0) { |
| fsp_err = sdhi_command_send_wait(&priv->sdmmc_ctrl, ra_cmd->opcode, ra_cmd->arg, |
| ra_cmd->timeout_ms); |
| if (fsp_err != 0) { |
| retries--; /* error, retry */ |
| } else { |
| break; |
| } |
| } |
| return err_fsp2zep(fsp_err); |
| } |
| |
| /* |
| * Send CMD or CMD/DATA via SDHC |
| */ |
| static int sdhc_ra_request(const struct device *dev, struct sdhc_command *cmd, |
| struct sdhc_data *data) |
| { |
| struct sdhc_ra_priv *priv = dev->data; |
| int retries = (int)(cmd->retries + 1); /* first try plus retries */ |
| uint32_t timeout_cfg = 0; |
| fsp_err_t fsp_err = 0; |
| int ret = 0; |
| sdmmc_priv_csd_reg_t p_csd_reg; |
| |
| struct sdmmc_ra_command ra_cmd = { |
| .opcode = cmd->opcode, |
| .arg = cmd->arg, |
| }; |
| |
| if (data) { |
| ra_cmd.data = (uint8_t *)data->data; |
| ra_cmd.sector_count = data->blocks; |
| ra_cmd.sector_size = data->block_size; |
| timeout_cfg = data->timeout_ms; |
| } else { |
| timeout_cfg = cmd->timeout_ms; |
| } |
| |
| if (cmd->timeout_ms == SDHC_TIMEOUT_FOREVER) { |
| ra_cmd.timeout_ms = SDHI_TIME_OUT_MAX; |
| } else { |
| ra_cmd.timeout_ms = timeout_cfg; |
| } |
| |
| /* Reset semaphore */ |
| k_sem_reset(&priv->sdmmc_event.transfer_sem); |
| k_sem_take(&priv->thread_lock, K_FOREVER); |
| if (ret < 0) { |
| LOG_ERR("Can not take sem!"); |
| goto end; |
| } |
| |
| /* |
| * Handle opcode with RA specifics |
| */ |
| switch (cmd->opcode) { |
| case SD_GO_IDLE_STATE: |
| case SD_ALL_SEND_CID: |
| case SD_SEND_RELATIVE_ADDR: |
| case SD_SELECT_CARD: |
| case SD_SEND_IF_COND: |
| case SD_SET_BLOCK_SIZE: |
| case SD_ERASE_BLOCK_START: |
| case SD_ERASE_BLOCK_END: |
| case SD_ERASE_BLOCK_OPERATION: |
| case SD_APP_CMD: |
| case SD_SEND_STATUS: |
| /* Send command with argument */ |
| ret = sdhc_ra_send_cmd(priv, &ra_cmd, retries); |
| if (ret < 0) { |
| goto end; |
| } |
| break; |
| case SD_SEND_CSD: |
| /* Read card specific data register */ |
| ret = sdhc_ra_send_cmd(priv, &ra_cmd, retries); |
| if (ret < 0) { |
| goto end; |
| } |
| /* SDResponseR2 are bits from 8-127, first 8 MSBs are reserved */ |
| p_csd_reg.reg.sdrsp10 = priv->sdmmc_ctrl.p_reg->SD_RSP10; |
| p_csd_reg.reg.sdrsp32 = priv->sdmmc_ctrl.p_reg->SD_RSP32; |
| p_csd_reg.reg.sdrsp54 = priv->sdmmc_ctrl.p_reg->SD_RSP54; |
| p_csd_reg.reg.sdrsp76 = priv->sdmmc_ctrl.p_reg->SD_RSP76; |
| |
| /* Get the CSD version. */ |
| uint32_t csd_version = p_csd_reg.csd_v1_b.csd_structure; |
| uint32_t mult; |
| |
| if ((SDHI_PRV_CSD_VERSION_1_0 == csd_version) || |
| (SDMMC_CARD_TYPE_MMC == priv->sdmmc_ctrl.device.card_type)) { |
| mult = (1U << (p_csd_reg.csd_v1_b.c_size_mult + 2)); |
| priv->sdmmc_ctrl.device.sector_count = |
| ((p_csd_reg.csd_v1_b.c_size + 1U) * mult); |
| |
| /* Scale the sector count by the actual block size. */ |
| uint32_t read_sector_size = 1U << p_csd_reg.csd_v1_b.read_bl_len; |
| |
| priv->sdmmc_ctrl.device.sector_count = |
| priv->sdmmc_ctrl.device.sector_count * |
| (read_sector_size / SDHI_MAX_BLOCK_SIZE); |
| |
| if (SDMMC_CARD_TYPE_MMC == priv->sdmmc_ctrl.device.card_type) { |
| /* |
| * If c_size is 0xFFF, then sector_count should be obtained from the |
| * extended CSD. Set it to 0 to indicate it should come from the |
| * extended CSD later. |
| */ |
| if (SDHI_PRV_SECTOR_COUNT_IN_EXT_CSD == p_csd_reg.csd_v1_b.c_size) { |
| priv->sdmmc_ctrl.device.sector_count = 0U; |
| } |
| } |
| } |
| |
| #if SDHI_CFG_SD_SUPPORT_ENABLE |
| else if (SDHI_PRV_CSD_VERSION_2_0 == csd_version) { |
| priv->sdmmc_ctrl.device.sector_count = |
| (p_csd_reg.csd_v2_b.c_size + 1U) * SDHI_PRV_BYTES_PER_KILOBYTE; |
| } else { |
| /* Do Nothing */ |
| } |
| |
| if (SDHI_PRV_CSD_VERSION_1_0 == csd_version) { |
| /* Get the minimum erasable unit (in 512 byte sectors). */ |
| priv->sdmmc_ctrl.device.erase_sector_count = |
| p_csd_reg.csd_v1_b.sector_size + 1U; |
| } else |
| #endif |
| { |
| /* |
| * For SDHC and SDXC cards, there are no erase group restrictions. |
| * Using the eMMC TRIM operation, there are no erase group restrictions. |
| */ |
| priv->sdmmc_ctrl.device.erase_sector_count = 1U; |
| } |
| break; |
| case SD_APP_SEND_OP_COND: |
| ra_cmd.opcode |= SDHI_PRV_CMD_C_ACMD; |
| ret = sdhc_ra_send_cmd(priv, &ra_cmd, retries); |
| if (ret < 0) { |
| goto end; |
| } |
| sdmmc_response_t response; |
| /* get response of ACMD41 (R3) */ |
| response.status = priv->sdmmc_ctrl.p_reg->SD_RSP10; |
| /* Initialization complete? */ |
| if (response.r3.power_up_status) { |
| /* High capacity card ? */ |
| /* 0 = SDSC, 1 = SDHC or SDXC */ |
| priv->sdmmc_ctrl.sector_addressing = |
| (response.r3.card_capacity_status > 0U); |
| priv->sdmmc_ctrl.device.card_type = SDMMC_CARD_TYPE_SD; |
| } |
| priv->sdmmc_ctrl.initialized = true; |
| break; |
| case SD_SWITCH: |
| /* Check app cmd */ |
| if (priv->app_cmd && cmd->opcode == SD_APP_SET_BUS_WIDTH) { |
| /* ACMD41*/ |
| ra_cmd.opcode |= SDHI_PRV_CMD_C_ACMD; |
| ret = sdhc_ra_send_cmd(priv, &ra_cmd, retries); |
| if (ret < 0) { |
| goto end; |
| } |
| } else { |
| /* SD SWITCH CMD6*/ |
| fsp_err = r_sdhi_read_and_block(&priv->sdmmc_ctrl, ra_cmd.opcode, |
| ra_cmd.arg, ra_cmd.sector_size); |
| ret = err_fsp2zep(fsp_err); |
| if (ret < 0) { |
| goto end; |
| } |
| memcpy(ra_cmd.data, priv->sdmmc_ctrl.aligned_buff, 8); |
| priv->sdmmc_event.transfer_completed = false; |
| break; |
| } |
| break; |
| |
| /* Read write with data */ |
| case SD_APP_SEND_SCR: |
| ra_cmd.opcode = cmd->opcode | SDHI_PRV_CMD_C_ACMD; |
| fsp_err = r_sdhi_read_and_block(&priv->sdmmc_ctrl, ra_cmd.opcode, ra_cmd.arg, |
| ra_cmd.sector_size); |
| |
| if (fsp_err != 0) { |
| ret = -ETIMEDOUT; |
| goto end; |
| } |
| memcpy(ra_cmd.data, priv->sdmmc_ctrl.aligned_buff, 8); |
| priv->sdmmc_event.transfer_completed = false; |
| break; |
| case SD_READ_SINGLE_BLOCK: |
| case SD_READ_MULTIPLE_BLOCK: |
| /* Configure the transfer interface for reading.*/ |
| fsp_err = r_sdhi_transfer_read(&priv->sdmmc_ctrl, ra_cmd.sector_count, |
| ra_cmd.sector_size, ra_cmd.data); |
| ret = err_fsp2zep(fsp_err); |
| if (ret < 0) { |
| goto end; |
| } |
| |
| r_sdhi_read_write_common(&priv->sdmmc_ctrl, ra_cmd.sector_count, ra_cmd.sector_size, |
| ra_cmd.opcode, ra_cmd.arg); |
| |
| /* Verify card is back in transfer state after write */ |
| ret = k_sem_take(&priv->sdmmc_event.transfer_sem, K_MSEC(ra_cmd.timeout_ms)); |
| if (ret < 0) { |
| LOG_ERR("Can not take sem!"); |
| goto end; |
| } |
| |
| if (!priv->sdmmc_event.transfer_completed) { |
| ret = -EIO; |
| goto end; |
| } |
| |
| priv->sdmmc_event.transfer_completed = false; |
| break; |
| |
| case SD_WRITE_SINGLE_BLOCK: |
| case SD_WRITE_MULTIPLE_BLOCK: |
| |
| fsp_err = r_sdhi_transfer_write(&priv->sdmmc_ctrl, ra_cmd.sector_count, |
| ra_cmd.sector_size, ra_cmd.data); |
| ret = err_fsp2zep(fsp_err); |
| if (ret < 0) { |
| goto end; |
| } |
| /* Send command with data for reading */ |
| r_sdhi_read_write_common(&priv->sdmmc_ctrl, ra_cmd.sector_count, ra_cmd.sector_size, |
| ra_cmd.opcode, ra_cmd.arg); |
| |
| /* Verify card is back in transfer state after write */ |
| ret = k_sem_take(&priv->sdmmc_event.transfer_sem, K_MSEC(ra_cmd.timeout_ms)); |
| if (ret < 0) { |
| LOG_ERR("Can not take sem!"); |
| goto end; |
| } |
| |
| if (!priv->sdmmc_event.transfer_completed) { |
| ret = -EIO; |
| goto end; |
| } |
| |
| priv->sdmmc_event.transfer_completed = false; |
| break; |
| |
| default: |
| LOG_INF("SDHC driver: command %u not supported", cmd->opcode); |
| ret = -ENOTSUP; |
| } |
| |
| if (ra_cmd.opcode == SD_ALL_SEND_CID || ra_cmd.opcode == SD_SEND_CSD) { |
| /* SDResponseR2 are bits from 8-127, first 8 MSBs are reserved */ |
| p_csd_reg.reg.sdrsp10 = (uint32_t)priv->sdmmc_ctrl.p_reg->SD_RSP10 << 8; |
| p_csd_reg.reg.sdrsp32 = (uint32_t)priv->sdmmc_ctrl.p_reg->SD_RSP32 << 8; |
| p_csd_reg.reg.sdrsp54 = (uint32_t)priv->sdmmc_ctrl.p_reg->SD_RSP54 << 8; |
| p_csd_reg.reg.sdrsp76 = (uint32_t)priv->sdmmc_ctrl.p_reg->SD_RSP76 << 8; |
| |
| memcpy(cmd->response, &p_csd_reg.reg, sizeof(cmd->response)); |
| } else { |
| /* Fill response buffer */ |
| p_csd_reg.reg.sdrsp10 = (uint32_t)priv->sdmmc_ctrl.p_reg->SD_RSP10; |
| p_csd_reg.reg.sdrsp32 = (uint32_t)priv->sdmmc_ctrl.p_reg->SD_RSP32; |
| p_csd_reg.reg.sdrsp54 = (uint32_t)priv->sdmmc_ctrl.p_reg->SD_RSP54; |
| p_csd_reg.reg.sdrsp76 = (uint32_t)priv->sdmmc_ctrl.p_reg->SD_RSP76; |
| |
| memcpy(cmd->response, &p_csd_reg.reg, sizeof(cmd->response)); |
| } |
| end: |
| if (cmd->opcode == SD_APP_CMD) { |
| priv->app_cmd = true; |
| } else { |
| priv->app_cmd = false; |
| } |
| |
| k_sem_give(&priv->thread_lock); |
| |
| return ret; |
| } |
| |
| static int sdhc_ra_reset(const struct device *dev) |
| { |
| struct sdhc_ra_priv *priv = dev->data; |
| const struct sdhc_ra_config *cfg = dev->config; |
| |
| k_sem_take(&priv->thread_lock, K_USEC(50)); |
| |
| /* Reset SDHI. */ |
| ((R_SDHI0_Type *)cfg->regs)->SOFT_RST = 0x0U; |
| ((R_SDHI0_Type *)cfg->regs)->SOFT_RST = 0x1U; |
| |
| k_sem_give(&priv->thread_lock); |
| |
| return 0; |
| } |
| |
| /* |
| * Set SDHC io properties |
| */ |
| static int sdhc_ra_set_io(const struct device *dev, struct sdhc_io *ios) |
| { |
| struct sdhc_ra_priv *priv = dev->data; |
| const struct sdhc_ra_config *cfg = dev->config; |
| struct st_sdmmc_instance_ctrl *p_ctrl = &priv->sdmmc_ctrl; |
| int fsp_err; |
| int ret = 0; |
| |
| uint8_t bus_width; |
| uint32_t bus_width_reg; |
| |
| if (ios->bus_width > 0) { |
| bus_width_reg = 0; |
| /* Set bus width, SD bus interface doesn't support 8BIT */ |
| switch (ios->bus_width) { |
| case SDHC_BUS_WIDTH1BIT: |
| bus_width = 1; |
| bus_width_reg = 4; |
| break; |
| case SDHC_BUS_WIDTH4BIT: |
| bus_width = 4; |
| break; |
| default: |
| ret = -ENOTSUP; |
| goto end; |
| } |
| |
| if (priv->bus_width != bus_width) { |
| /* Set the bus width in the SDHI peripheral. */ |
| ((R_SDHI0_Type *)cfg->regs)->SD_OPTION = |
| SDHI_PRV_SD_OPTION_DEFAULT | |
| (bus_width_reg << SDHI_PRV_SD_OPTION_WIDTH8_BIT); |
| priv->bus_width = bus_width; |
| } |
| } |
| |
| if (ios->clock) { |
| if (ios->clock > priv->props.f_max || ios->clock < priv->props.f_min) { |
| LOG_ERR("Proposed clock outside supported host range"); |
| return -EINVAL; |
| } |
| |
| if (priv->bus_clock != (uint32_t)ios->clock) { |
| fsp_err = r_sdhi_max_clock_rate_set(p_ctrl, ios->clock); |
| ret = err_fsp2zep(fsp_err); |
| if (ret < 0) { |
| goto end; |
| } |
| priv->bus_clock = ios->clock; |
| } |
| } |
| |
| if (ios->timing > 0) { |
| /* Set I/O timing */ |
| if (priv->timing != ios->timing) { |
| switch (ios->timing) { |
| case SDHC_TIMING_LEGACY: |
| case SDHC_TIMING_HS: |
| case SDHC_TIMING_SDR12: |
| case SDHC_TIMING_SDR25: |
| break; |
| default: |
| LOG_ERR("Timing mode not supported for this device"); |
| ret = -ENOTSUP; |
| break; |
| } |
| |
| priv->timing = ios->timing; |
| } |
| } |
| end: |
| |
| return ret; |
| } |
| |
| /* |
| * Get host properties |
| */ |
| static int sdhc_ra_get_host_props(const struct device *dev, struct sdhc_host_props *props) |
| { |
| struct sdhc_ra_priv *priv = dev->data; |
| |
| memcpy(props, &priv->props, sizeof(struct sdhc_host_props)); |
| return 0; |
| } |
| |
| static int sdhc_ra_init(const struct device *dev) |
| { |
| const struct sdhc_ra_config *config = dev->config; |
| struct sdhc_ra_priv *priv = dev->data; |
| fsp_err_t fsp_err; |
| int timeout = SDHI_PRV_ACCESS_TIMEOUT_US; |
| int ret = 0; |
| |
| priv->sdmmc_event.transfer_completed = false; |
| k_sem_init(&priv->sdmmc_event.transfer_sem, 1, 1); |
| |
| /* Configure dt provided device signals when available */ |
| ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); |
| if (ret < 0) { |
| return ret; |
| } |
| if (priv->sdhi_en.port != NULL) { |
| int err = gpio_pin_configure_dt(&priv->sdhi_en, GPIO_OUTPUT_HIGH); |
| |
| if (err) { |
| return err; |
| } |
| k_sleep(K_MSEC(50)); |
| } |
| |
| k_sem_init(&priv->thread_lock, 1, 1); |
| fsp_err = R_SDHI_Open(&priv->sdmmc_ctrl, &priv->fsp_config); |
| ret = err_fsp2zep(fsp_err); |
| |
| if (ret < 0) { |
| LOG_INF("R_SDHI_Open error: %d", fsp_err); |
| return ret; /* I/O error*/ |
| } |
| |
| k_busy_wait(100); |
| |
| k_sem_take(&priv->thread_lock, K_USEC(timeout)); |
| |
| fsp_err = r_sdhi_hw_cfg(&priv->sdmmc_ctrl); |
| ret = err_fsp2zep(fsp_err); |
| if (ret < 0) { |
| LOG_ERR("failed to init sdmmc media"); |
| goto end; |
| } |
| priv->bus_width = SDMMC_BUS_WIDTH_1_BIT; |
| priv->timing = SDHC_TIMING_LEGACY; |
| priv->bus_clock = SDMMC_CLOCK_400KHZ; |
| |
| end: |
| k_sem_give(&priv->thread_lock); |
| return ret; |
| } |
| |
| static DEVICE_API(sdhc, sdhc_api) = { |
| .reset = sdhc_ra_reset, |
| .request = sdhc_ra_request, |
| .set_io = sdhc_ra_set_io, |
| .get_card_present = sdhc_ra_get_card_present, |
| .card_busy = sdhc_ra_card_busy, |
| .get_host_props = sdhc_ra_get_host_props, |
| }; |
| |
| #define EVENT_SDMMC_ACCS(channel) BSP_PRV_IELS_ENUM(CONCAT(EVENT_SDHIMMC, channel, _ACCS)) |
| #define EVENT_SDMMC_CARD(channel) BSP_PRV_IELS_ENUM(CONCAT(EVENT_SDHIMMC, channel, _CARD)) |
| #define EVENT_SDMMC_DMA_REQ(channel) BSP_PRV_IELS_ENUM(CONCAT(EVENT_SDHIMMC, channel, _DMA_REQ)) |
| |
| #define RA_SDMMC_IRQ_CONFIG_INIT(index) \ |
| do { \ |
| ARG_UNUSED(dev); \ |
| \ |
| R_ICU->IELSR[DT_INST_IRQ_BY_NAME(index, accs, irq)] = \ |
| EVENT_SDMMC_ACCS(DT_INST_PROP(index, channel)); \ |
| R_ICU->IELSR[DT_INST_IRQ_BY_NAME(index, card, irq)] = \ |
| EVENT_SDMMC_CARD(DT_INST_PROP(index, channel)); \ |
| R_ICU->IELSR[DT_INST_IRQ_BY_NAME(index, dma_req, irq)] = \ |
| EVENT_SDMMC_DMA_REQ(DT_INST_PROP(index, channel)); \ |
| \ |
| BSP_ASSIGN_EVENT_TO_CURRENT_CORE(EVENT_SDMMC_ACCS(DT_INST_PROP(index, channel))); \ |
| BSP_ASSIGN_EVENT_TO_CURRENT_CORE(EVENT_SDMMC_CARD(DT_INST_PROP(index, channel))); \ |
| BSP_ASSIGN_EVENT_TO_CURRENT_CORE( \ |
| EVENT_SDMMC_DMA_REQ(DT_INST_PROP(index, channel))); \ |
| \ |
| IRQ_CONNECT(DT_INST_IRQ_BY_NAME(index, accs, irq), \ |
| DT_INST_IRQ_BY_NAME(index, accs, priority), ra_sdmmc_accs_isr, \ |
| DEVICE_DT_INST_GET(index), 0); \ |
| IRQ_CONNECT(DT_INST_IRQ_BY_NAME(index, card, irq), \ |
| DT_INST_IRQ_BY_NAME(index, card, priority), ra_sdmmc_card_isr, \ |
| DEVICE_DT_INST_GET(index), 0); \ |
| IRQ_CONNECT(DT_INST_IRQ_BY_NAME(index, dma_req, irq), \ |
| DT_INST_IRQ_BY_NAME(index, dma_req, priority), ra_sdmmc_dma_req_isr, \ |
| DEVICE_DT_INST_GET(index), 0); \ |
| \ |
| irq_enable(DT_INST_IRQ_BY_NAME(index, accs, irq)); \ |
| irq_enable(DT_INST_IRQ_BY_NAME(index, card, irq)); \ |
| irq_enable(DT_INST_IRQ_BY_NAME(index, dma_req, irq)); \ |
| } while (0) |
| |
| #define RA_SDHI_EN(index) .sdhi_en = GPIO_DT_SPEC_INST_GET_OR(index, enable_gpios, {0}) |
| |
| #define RA_SDMMC_DTC_INIT(index) \ |
| sdhc_ra_priv_##index.fsp_config.p_lower_lvl_transfer = &sdhc_ra_priv_##index.transfer; |
| |
| #define RA_SDMMC_DTC_STRUCT_INIT(index) \ |
| .transfer_info = \ |
| { \ |
| .transfer_settings_word_b.dest_addr_mode = TRANSFER_ADDR_MODE_FIXED, \ |
| .transfer_settings_word_b.repeat_area = TRANSFER_REPEAT_AREA_SOURCE, \ |
| .transfer_settings_word_b.irq = TRANSFER_IRQ_END, \ |
| .transfer_settings_word_b.chain_mode = TRANSFER_CHAIN_MODE_DISABLED, \ |
| .transfer_settings_word_b.src_addr_mode = TRANSFER_ADDR_MODE_INCREMENTED, \ |
| .transfer_settings_word_b.size = TRANSFER_SIZE_4_BYTE, \ |
| .transfer_settings_word_b.mode = TRANSFER_MODE_NORMAL, \ |
| .p_dest = (void *)NULL, \ |
| .p_src = (void const *)NULL, \ |
| .num_blocks = 0, \ |
| .length = 128, \ |
| }, \ |
| .transfer_cfg_extend = {.activation_source = DT_INST_IRQ_BY_NAME(index, dma_req, irq)}, \ |
| .transfer_cfg = \ |
| { \ |
| .p_info = &sdhc_ra_priv_##index.transfer_info, \ |
| .p_extend = &sdhc_ra_priv_##index.transfer_cfg_extend, \ |
| }, \ |
| .transfer = { \ |
| .p_ctrl = &sdhc_ra_priv_##index.transfer_ctrl, \ |
| .p_cfg = &sdhc_ra_priv_##index.transfer_cfg, \ |
| .p_api = &g_transfer_on_dtc, \ |
| }, |
| |
| #define RA_SDHC_INIT(index) \ |
| \ |
| PINCTRL_DT_INST_DEFINE(index); \ |
| \ |
| static const struct sdhc_ra_config sdhc_ra_config_##index = { \ |
| .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(index), \ |
| .regs = (R_SDHI0_Type *)DT_INST_REG_ADDR(index), \ |
| }; \ |
| void r_sdhi_callback_##index(sdmmc_callback_args_t *p_args) \ |
| { \ |
| const struct device *dev = DEVICE_DT_INST_GET(index); \ |
| struct sdhc_ra_priv *priv = dev->data; \ |
| if (p_args->event == SDMMC_EVENT_TRANSFER_COMPLETE) { \ |
| priv->sdmmc_event.transfer_completed = true; \ |
| k_sem_give(&priv->sdmmc_event.transfer_sem); \ |
| } else if (p_args->event == SDMMC_EVENT_TRANSFER_ERROR) { \ |
| priv->sdmmc_event.transfer_completed = false; \ |
| k_sem_give(&priv->sdmmc_event.transfer_sem); \ |
| } \ |
| } \ |
| \ |
| static struct sdhc_ra_priv sdhc_ra_priv_##index = { \ |
| .power_mode = SDHC_POWER_ON, \ |
| .timing = SDHC_TIMING_LEGACY, \ |
| .fsp_config = \ |
| { \ |
| .channel = DT_INST_PROP(index, channel), \ |
| .bus_width = DT_INST_PROP(index, bus_width), \ |
| .access_ipl = DT_INST_IRQ_BY_NAME(index, accs, priority), \ |
| .access_irq = DT_INST_IRQ_BY_NAME(index, accs, irq), \ |
| .card_ipl = DT_INST_IRQ_BY_NAME(index, card, priority), \ |
| .card_irq = DT_INST_IRQ_BY_NAME(index, card, irq), \ |
| .dma_req_ipl = DT_INST_IRQ_BY_NAME(index, dma_req, priority), \ |
| .dma_req_irq = DT_INST_IRQ_BY_NAME(index, dma_req, irq), \ |
| .p_context = NULL, \ |
| .p_callback = r_sdhi_callback_##index, \ |
| .card_detect = DT_INST_PROP(index, card_detect), \ |
| .write_protect = DT_INST_PROP(index, write_protect), \ |
| .p_extend = NULL, \ |
| .p_lower_lvl_transfer = &sdhc_ra_priv_##index.transfer, \ |
| }, \ |
| .props = {.is_spi = false, \ |
| .f_max = DT_INST_PROP(index, max_bus_freq), \ |
| .f_min = DT_INST_PROP(index, min_bus_freq), \ |
| .max_current_330 = DT_INST_PROP(index, max_current_330), \ |
| .max_current_180 = DT_INST_PROP(index, max_current_180), \ |
| .power_delay = DT_INST_PROP_OR(index, power_delay_ms, 0), \ |
| .host_caps = {.vol_180_support = false, \ |
| .vol_300_support = false, \ |
| .vol_330_support = true, \ |
| .suspend_res_support = false, \ |
| .sdma_support = true, \ |
| .high_spd_support = (DT_INST_PROP(index, bus_width) == 4) \ |
| ? true \ |
| : false, \ |
| .adma_2_support = false, \ |
| .max_blk_len = 0, \ |
| .ddr50_support = false, \ |
| .sdr104_support = false, \ |
| .sdr50_support = false, \ |
| .bus_8_bit_support = false, \ |
| .bus_4_bit_support = (DT_INST_PROP(index, bus_width) == 4) \ |
| ? true \ |
| : false, \ |
| .hs200_support = false, \ |
| .hs400_support = false}}, \ |
| RA_SDHI_EN(index), \ |
| RA_SDMMC_DTC_STRUCT_INIT(index)}; \ |
| \ |
| static int sdhc_ra_init##index(const struct device *dev) \ |
| { \ |
| RA_SDMMC_DTC_INIT(index); \ |
| RA_SDMMC_IRQ_CONFIG_INIT(index); \ |
| int err = sdhc_ra_init(dev); \ |
| if (err != 0) { \ |
| return err; \ |
| } \ |
| return 0; \ |
| } \ |
| \ |
| DEVICE_DT_INST_DEFINE(index, sdhc_ra_init##index, NULL, &sdhc_ra_priv_##index, \ |
| &sdhc_ra_config_##index, POST_KERNEL, CONFIG_SDHC_INIT_PRIORITY, \ |
| &sdhc_api); |
| |
| DT_INST_FOREACH_STATUS_OKAY(RA_SDHC_INIT) |