From 8f91d0c0725e33bb4ffd69f80e758d42124f7db4 Mon Sep 17 00:00:00 2001 From: Tran Van Quy Date: Mon, 18 Nov 2024 09:35:31 +0700 Subject: [PATCH] drivers: sdhc: support SDHC driver on Renesas RA8 This is initial commit to support SDHC driver on Renesas RA8 with r_sdhi modules Signed-off-by: Tran Van Quy --- drivers/sdhc/CMakeLists.txt | 1 + drivers/sdhc/Kconfig | 1 + drivers/sdhc/Kconfig.renesas_ra | 13 + drivers/sdhc/sdhc_renesas_ra.c | 744 +++++++++++++++++++++++++ drivers/sdhc/sdhc_renesas_ra.h | 54 ++ dts/bindings/sdhc/renesas,ra-sdhc.yaml | 54 ++ modules/Kconfig.renesas_fsp | 5 + 7 files changed, 872 insertions(+) create mode 100644 drivers/sdhc/Kconfig.renesas_ra create mode 100644 drivers/sdhc/sdhc_renesas_ra.c create mode 100644 drivers/sdhc/sdhc_renesas_ra.h create mode 100644 dts/bindings/sdhc/renesas,ra-sdhc.yaml diff --git a/drivers/sdhc/CMakeLists.txt b/drivers/sdhc/CMakeLists.txt index 42985f873f1..269f1a419c0 100644 --- a/drivers/sdhc/CMakeLists.txt +++ b/drivers/sdhc/CMakeLists.txt @@ -12,4 +12,5 @@ zephyr_library_sources_ifdef(CONFIG_INTEL_EMMC_HOST intel_emmc_host.c) zephyr_library_sources_ifdef(CONFIG_SDHC_INFINEON_CAT1 ifx_cat1_sdio.c) zephyr_library_sources_ifdef(CONFIG_CDNS_SDHC sdhc_cdns_ll.c sdhc_cdns.c) zephyr_library_sources_ifdef(CONFIG_SDHC_ESP32 sdhc_esp32.c) +zephyr_library_sources_ifdef(CONFIG_SDHC_RENESAS_RA sdhc_renesas_ra.c) endif() diff --git a/drivers/sdhc/Kconfig b/drivers/sdhc/Kconfig index b6b1276207b..89fa669a5e5 100644 --- a/drivers/sdhc/Kconfig +++ b/drivers/sdhc/Kconfig @@ -17,6 +17,7 @@ source "drivers/sdhc/Kconfig.sam_hsmci" source "drivers/sdhc/Kconfig.intel" source "drivers/sdhc/Kconfig.sdhc_cdns" source "drivers/sdhc/Kconfig.esp32" +source "drivers/sdhc/Kconfig.renesas_ra" config SDHC_INIT_PRIORITY int "SDHC driver init priority" diff --git a/drivers/sdhc/Kconfig.renesas_ra b/drivers/sdhc/Kconfig.renesas_ra new file mode 100644 index 00000000000..04313f23c1f --- /dev/null +++ b/drivers/sdhc/Kconfig.renesas_ra @@ -0,0 +1,13 @@ +# Copyright (c) 2024 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +config SDHC_RENESAS_RA + bool "Renesas RA SDHI driver" + default y + depends on DT_HAS_RENESAS_RA_SDHC_ENABLED + select SDHC_SUPPORTS_NATIVE_MODE + select USE_RA_FSP_SDHI + select USE_RA_FSP_DTC + select PINCTRL + help + Enables Renesas SD Host controller driver diff --git a/drivers/sdhc/sdhc_renesas_ra.c b/drivers/sdhc/sdhc_renesas_ra.c new file mode 100644 index 00000000000..9993d5be3bf --- /dev/null +++ b/drivers/sdhc/sdhc_renesas_ra.c @@ -0,0 +1,744 @@ +/* + * Copyright (c) 2024 Renesas Electronics Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT renesas_ra_sdhc + +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 _ELC_EVENT_SDMMC_ACCS(channel) ELC_EVENT_SDHIMMC##channel##_ACCS +#define _ELC_EVENT_SDMMC_CARD(channel) ELC_EVENT_SDHIMMC##channel##_CARD +#define _ELC_EVENT_SDMMC_DMA_REQ(channel) ELC_EVENT_SDHIMMC##channel##_DMA_REQ + +#define ELC_EVENT_SDMMC_ACCS(channel) _ELC_EVENT_SDMMC_ACCS(channel) +#define ELC_EVENT_SDMMC_CARD(channel) _ELC_EVENT_SDMMC_CARD(channel) +#define ELC_EVENT_SDMMC_DMA_REQ(channel) _ELC_EVENT_SDMMC_DMA_REQ(channel) + +#define RA_SDMMC_IRQ_CONFIG_INIT(index) \ + do { \ + ARG_UNUSED(dev); \ + \ + R_ICU->IELSR[DT_INST_IRQ_BY_NAME(index, accs, irq)] = \ + ELC_EVENT_SDMMC_ACCS(DT_INST_PROP(index, channel)); \ + R_ICU->IELSR[DT_INST_IRQ_BY_NAME(index, card, irq)] = \ + ELC_EVENT_SDMMC_CARD(DT_INST_PROP(index, channel)); \ + R_ICU->IELSR[DT_INST_IRQ_BY_NAME(index, dma_req, irq)] = \ + ELC_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) diff --git a/drivers/sdhc/sdhc_renesas_ra.h b/drivers/sdhc/sdhc_renesas_ra.h new file mode 100644 index 00000000000..ada297508de --- /dev/null +++ b/drivers/sdhc/sdhc_renesas_ra.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024 Renesas Electronics Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define SDHI_PRV_ACCESS_TIMEOUT_US 100000U +#define SDHI_PRV_SD_OPTION_DEFAULT 0x40E0U +#define SDHI_PRV_SD_OPTION_WIDTH8_BIT 13 +#define SDHI_PRV_BYTES_PER_KILOBYTE 1024 +#define SDHI_PRV_SECTOR_COUNT_IN_EXT_CSD 0xFFFU +#define SDHI_TIME_OUT_MAX 0xFFFFFFFF +#define SDHI_PRV_RESPONSE_BIT 0 + +struct sdmmc_ra_event { + volatile bool transfer_completed; + struct k_sem transfer_sem; +}; + +struct sdmmc_ra_command { + uint32_t opcode; + uint32_t arg; + void *data; + unsigned int sector_count; + unsigned int sector_size; + int timeout_ms; +}; + +static ALWAYS_INLINE int err_fsp2zep(int fsp_err) +{ + int ret; + + switch (fsp_err) { + /* Treating the error codes most relevant to be individuated */ + case FSP_SUCCESS: + ret = 0; + break; + case FSP_ERR_TIMEOUT: + ret = -ETIMEDOUT; + break; + case FSP_ERR_NOT_FOUND: + ret = -ENODEV; /* SD card not inserted (requires CD signal) */ + break; + case FSP_ERR_INVALID_STATE: + ret = -EACCES; /* SD card write-protected (requires WP sinal) */ + break; + case FSP_ERR_RESPONSE: + default: + ret = -EIO; + break; + } + + return ret; +} diff --git a/dts/bindings/sdhc/renesas,ra-sdhc.yaml b/dts/bindings/sdhc/renesas,ra-sdhc.yaml new file mode 100644 index 00000000000..92d6fe75ec9 --- /dev/null +++ b/dts/bindings/sdhc/renesas,ra-sdhc.yaml @@ -0,0 +1,54 @@ +# Copyright (c) 2024 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +description: Renesas RA SDHC + +compatible: "renesas,ra-sdhc" + + +include: [sdhc.yaml, pinctrl-device.yaml] + +properties: + channel: + type: int + required: true + + bus-width: + type: int + enum: + - 1 + - 4 + default: 4 + + sd-support: + type: boolean + + mmc-support: + type: boolean + + card-detect: + type: boolean + + write-protect: + type: boolean + + reg: + required: true + + pinctrl-0: + required: true + + pinctrl-names: + required: true + + enable-gpios: + type: phandle-array + description: | + GPIO to use to enable/disable the SDHI. This GPIO is driven active when + the SDHI is enabled and inactive when the SDHI is disabled + + interrupts: + required: true + + interrupt-names: + required: true diff --git a/modules/Kconfig.renesas_fsp b/modules/Kconfig.renesas_fsp index 2a3756872ce..62221025388 100644 --- a/modules/Kconfig.renesas_fsp +++ b/modules/Kconfig.renesas_fsp @@ -137,6 +137,11 @@ config USE_RA_FSP_MIPI_DSI help Enable RA FSP MIPI DSI driver +config USE_RA_FSP_SDHI + bool + help + Enable RA FSP SDHI driver + endif # HAS_RENESAS_RA_FSP if HAS_RENESAS_RZ_FSP