|  | /* | 
|  | * 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; | 
|  | 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) | 
|  | { | 
|  | /* Verify the device is not busy. */ | 
|  | r_sdhi_wait_for_device(p_ctrl); | 
|  |  | 
|  | /* 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); | 
|  | } | 
|  |  | 
|  | 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));                         \ | 
|  | \ | 
|  | 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) |