diff --git a/drivers/sdhc/CMakeLists.txt b/drivers/sdhc/CMakeLists.txt index b8d7e21bfb..42985f873f 100644 --- a/drivers/sdhc/CMakeLists.txt +++ b/drivers/sdhc/CMakeLists.txt @@ -6,6 +6,7 @@ zephyr_library() zephyr_library_sources_ifdef(CONFIG_IMX_USDHC imx_usdhc.c) zephyr_library_sources_ifdef(CONFIG_SPI_SDHC sdhc_spi.c) zephyr_library_sources_ifdef(CONFIG_MCUX_SDIF mcux_sdif.c) +zephyr_library_sources_ifdef(CONFIG_RCAR_MMC rcar_mmc.c) zephyr_library_sources_ifdef(CONFIG_SAM_HSMCI sam_hsmci.c) zephyr_library_sources_ifdef(CONFIG_INTEL_EMMC_HOST intel_emmc_host.c) zephyr_library_sources_ifdef(CONFIG_SDHC_INFINEON_CAT1 ifx_cat1_sdio.c) diff --git a/drivers/sdhc/Kconfig b/drivers/sdhc/Kconfig index 2a121839cd..b6b1276207 100644 --- a/drivers/sdhc/Kconfig +++ b/drivers/sdhc/Kconfig @@ -12,6 +12,7 @@ source "drivers/sdhc/Kconfig.ifx_cat1" source "drivers/sdhc/Kconfig.imx" source "drivers/sdhc/Kconfig.spi" source "drivers/sdhc/Kconfig.mcux_sdif" +source "drivers/sdhc/Kconfig.rcar" source "drivers/sdhc/Kconfig.sam_hsmci" source "drivers/sdhc/Kconfig.intel" source "drivers/sdhc/Kconfig.sdhc_cdns" diff --git a/drivers/sdhc/Kconfig.rcar b/drivers/sdhc/Kconfig.rcar new file mode 100644 index 0000000000..f5d57a4bb0 --- /dev/null +++ b/drivers/sdhc/Kconfig.rcar @@ -0,0 +1,42 @@ +# Copyright (c) 2023 EPAM Systems +# SPDX-License-Identifier: Apache-2.0 + +config RCAR_MMC + bool "Renesas Rcar MMC driver" + default y + depends on DT_HAS_RENESAS_RCAR_MMC_ENABLED + select SDHC_SUPPORTS_NATIVE_MODE + select REGULATOR + select GPIO + select SDHC_SUPPORTS_UHS if SDMMC_STACK + help + Renesas Rcar MMC driver. + +if RCAR_MMC + +config RCAR_MMC_DMA_SUPPORT + bool "Internal DMA support for Renesas Rcar MMC driver" + select CACHE_MANAGEMENT + select DCACHE + default y + help + Internal DMA support for Renesas Rcar MMC driver. + +config RCAR_MMC_SCC_SUPPORT + bool "Support of SCC" + default y + help + Enable support of Sampling Clock Controller for Renesas Rcar MMC driver. + +if RCAR_MMC_DMA_SUPPORT + +config SDHC_BUFFER_ALIGNMENT + default 128 + +config RCAR_MMC_DMA_IRQ_DRIVEN_SUPPORT + bool "Internal DMA IRQ driven support for Renesas Rcar MMC driver" + default y + +endif # RCAR_MMC_DMA_SUPPORT + +endif # RCAR_MMC diff --git a/drivers/sdhc/rcar_mmc.c b/drivers/sdhc/rcar_mmc.c new file mode 100644 index 0000000000..2e5d20ee86 --- /dev/null +++ b/drivers/sdhc/rcar_mmc.c @@ -0,0 +1,2223 @@ +/* + * Copyright (c) 2023 EPAM Systems + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT renesas_rcar_mmc + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rcar_mmc_registers.h" + +#define PINCTRL_STATE_UHS PINCTRL_STATE_PRIV_START + +/** + * @note we don't need any locks here, because SDHC subsystem cares about it + */ + +LOG_MODULE_REGISTER(rcar_mmc, CONFIG_LOG_DEFAULT_LEVEL); + +#define MMC_POLL_FLAGS_TIMEOUT_US 100000 +#define MMC_POLL_FLAGS_ONE_CYCLE_TIMEOUT_US 1 +#define MMC_BUS_CLOCK_FREQ 800000000 +/* + * SD/MMC clock for Gen3/Gen4 R-car boards can't be equal to 208 MHz, + * but we can run SDR104 on lower frequencies: + * "SDR104: UHS-I 1.8V signaling, Frequency up to 208 MHz" + * so according to SD card standard it is possible to use lower frequencies, + * and we need to pass check of frequency in sdmmc in order to use sdr104 mode. + * This is the reason why it is needed this correction. + */ +#define MMC_MAX_FREQ_CORRECTION 8000000 + +#ifdef CONFIG_RCAR_MMC_DMA_SUPPORT +#define ALIGN_BUF_DMA __aligned(CONFIG_SDHC_BUFFER_ALIGNMENT) +#else +#define ALIGN_BUF_DMA +#endif + +/** + * @brief Renesas MMC host controller driver data + * + */ +struct mmc_rcar_data { + DEVICE_MMIO_RAM; /* Must be first */ + struct sdhc_io host_io; + struct sdhc_host_props props; +#ifdef CONFIG_RCAR_MMC_DMA_IRQ_DRIVEN_SUPPORT + struct k_sem irq_xref_fin; +#endif + + uint8_t ver; + /* in bytes, possible values are 2, 4 or 8 */ + uint8_t width_access_sd_buf0; + uint8_t ddr_mode; + uint8_t restore_cfg_after_reset; + uint8_t is_last_cmd_app_cmd; /* ACMD55 */ + +#ifdef CONFIG_RCAR_MMC_SCC_SUPPORT + uint8_t manual_retuning; + uint8_t tuning_buf[128] ALIGN_BUF_DMA; +#endif /* CONFIG_RCAR_MMC_SCC_SUPPORT */ + uint8_t can_retune; +}; + +/** + * @brief Renesas MMC host controller driver configuration + */ +struct mmc_rcar_cfg { + DEVICE_MMIO_ROM; /* Must be first */ + struct rcar_cpg_clk cpg_clk; + struct rcar_cpg_clk bus_clk; + const struct device *cpg_dev; + const struct pinctrl_dev_config *pcfg; + const struct device *regulator_vqmmc; + const struct device *regulator_vmmc; + + uint32_t max_frequency; + +#ifdef CONFIG_RCAR_MMC_DMA_IRQ_DRIVEN_SUPPORT + void (*irq_config_func)(const struct device *dev); +#endif + + uint8_t non_removable; + uint8_t uhs_support; + uint8_t mmc_hs200_1_8v; + uint8_t mmc_hs400_1_8v; + uint8_t bus_width; + uint8_t mmc_sdr104_support; +}; + +#ifdef CONFIG_RCAR_MMC_SCC_SUPPORT +static int rcar_mmc_execute_tuning(const struct device *dev); +static int rcar_mmc_retune_if_needed(const struct device *dev, bool request_retune); +#endif +static int rcar_mmc_disable_scc(const struct device *dev); + +static uint32_t rcar_mmc_read_reg32(const struct device *dev, uint32_t reg) +{ + return sys_read32(DEVICE_MMIO_GET(dev) + reg); +} + +static void rcar_mmc_write_reg32(const struct device *dev, uint32_t reg, uint32_t val) +{ + sys_write32(val, DEVICE_MMIO_GET(dev) + reg); +} + +/* cleanup SD card interrupt flag register and mask their interrupts */ +static inline void rcar_mmc_reset_and_mask_irqs(const struct device *dev) +{ + struct mmc_rcar_data *data = dev->data; + + rcar_mmc_write_reg32(dev, RCAR_MMC_INFO1, 0); + rcar_mmc_write_reg32(dev, RCAR_MMC_INFO1_MASK, ~0); + + rcar_mmc_write_reg32(dev, RCAR_MMC_INFO2, RCAR_MMC_INFO2_CLEAR); + rcar_mmc_write_reg32(dev, RCAR_MMC_INFO2_MASK, ~0); + +#ifdef CONFIG_RCAR_MMC_DMA_SUPPORT + /* default value of Seq suspend should be 0 */ + rcar_mmc_write_reg32(dev, RCAR_MMC_DMA_INFO1_MASK, 0xfffffeff); + rcar_mmc_write_reg32(dev, RCAR_MMC_DMA_INFO1, 0x0); + rcar_mmc_write_reg32(dev, RCAR_MMC_DMA_INFO2_MASK, 0xffffffff); + rcar_mmc_write_reg32(dev, RCAR_MMC_DMA_INFO2, 0x0); +#ifdef CONFIG_RCAR_MMC_DMA_IRQ_DRIVEN_SUPPORT + k_sem_reset(&data->irq_xref_fin); +#endif +#endif /* CONFIG_RCAR_MMC_DMA_SUPPORT */ +} + +/** + * @brief check if MMC is busy + * + * This check should generally be implemented as checking the controller + * state. No MMC commands need to be sent. + * + * @param dev MMC device + * @retval 0 card is not busy + * @retval 1 card is busy + * @retval -EINVAL: the dev pointer is NULL + */ +static int rcar_mmc_card_busy(const struct device *dev) +{ + uint32_t reg; + + if (!dev) { + return -EINVAL; + } + + reg = rcar_mmc_read_reg32(dev, RCAR_MMC_INFO2); + return (reg & RCAR_MMC_INFO2_DAT0) ? 0 : 1; +} + +/** + * @brief Check error flags inside INFO2 MMC register + * + * @note in/out parameters should be checked by a caller function + * + * @param dev MMC device + * + * @retval 0 INFO2 register hasn't errors + * @retval -ETIMEDOUT: timed out while tx/rx + * @retval -EIO: I/O error + * @retval -EILSEQ: communication out of sync + */ +static int rcar_mmc_check_errors(const struct device *dev) +{ + uint32_t info2 = rcar_mmc_read_reg32(dev, RCAR_MMC_INFO2); + + if (info2 & (RCAR_MMC_INFO2_ERR_TO | RCAR_MMC_INFO2_ERR_RTO)) { + LOG_DBG("timeout error 0x%08x", info2); + return -ETIMEDOUT; + } + + if (info2 & (RCAR_MMC_INFO2_ERR_END | RCAR_MMC_INFO2_ERR_CRC | RCAR_MMC_INFO2_ERR_IDX)) { + LOG_DBG("communication out of sync 0x%08x", info2); + return -EILSEQ; + } + + if (info2 & (RCAR_MMC_INFO2_ERR_ILA | RCAR_MMC_INFO2_ERR_ILR | RCAR_MMC_INFO2_ERR_ILW)) { + LOG_DBG("illegal access 0x%08x", info2); + return -EIO; + } + + return 0; +} + +/** + * @brief Poll flag(s) in MMC register and check errors + * + * @note in/out parameters should be checked by a caller function + * + * @param dev MMC device + * @param reg register offset relative to the base address + * @param flag polling flag(s) + * @param state state of flag(s) when we should stop polling + * @param check_errors call @ref rcar_mmc_check_errors function or not + * @param check_dma_errors check if there are DMA errors inside info2 + * @param timeout_us timeout in microseconds how long we should poll flag(s) + * + * @retval 0 poll of flag(s) was successful + * @retval -ETIMEDOUT: timed out while tx/rx + * @retval -EIO: I/O error + * @retval -EILSEQ: communication out of sync + */ +static int rcar_mmc_poll_reg_flags_check_err(const struct device *dev, unsigned int reg, + uint32_t flag, uint32_t state, bool check_errors, + bool check_dma_errors, int64_t timeout_us) +{ + int ret; + + while ((rcar_mmc_read_reg32(dev, reg) & flag) != state) { + if (timeout_us < 0) { + LOG_DBG("timeout error during polling flag(s) 0x%08x in reg 0x%08x", flag, + reg); + return -ETIMEDOUT; + } + + if (check_errors) { + ret = rcar_mmc_check_errors(dev); + if (ret) { + return ret; + } + } + + if (check_dma_errors && rcar_mmc_read_reg32(dev, RCAR_MMC_DMA_INFO2)) { + LOG_DBG("%s: an error occurs on the DMAC channel #%u", dev->name, + (reg & RCAR_MMC_DMA_INFO2_ERR_RD) ? 1U : 0U); + return -EIO; + } + + k_usleep(MMC_POLL_FLAGS_ONE_CYCLE_TIMEOUT_US); + timeout_us -= MMC_POLL_FLAGS_ONE_CYCLE_TIMEOUT_US; + } + + return 0; +} + +/* reset DMA MMC controller */ +static inline void rcar_mmc_reset_dma(const struct device *dev) +{ + uint32_t reg = RCAR_MMC_DMA_RST_DTRAN0 | RCAR_MMC_DMA_RST_DTRAN1; + + rcar_mmc_write_reg32(dev, RCAR_MMC_EXTMODE, 0); + rcar_mmc_write_reg32(dev, RCAR_MMC_DMA_RST, ~reg); + rcar_mmc_write_reg32(dev, RCAR_MMC_DMA_RST, ~0); + rcar_mmc_write_reg32(dev, RCAR_MMC_EXTMODE, 1); +} + +/** + * @brief reset MMC controller state + * + * Used when the MMC has encountered an error. Resetting the MMC controller + * should clear all errors on the MMC, but does not necessarily reset I/O + * settings to boot (this can be done with @ref sdhc_set_io) + * + * @note during reset the clock input is disabled, also this call changes rate + * + * @param dev MMC controller device + * @retval 0 reset succeeded + * @retval -ETIMEDOUT: controller reset timed out + * @retval -EINVAL: the dev pointer is NULL + * @retval -EILSEQ: communication out of sync + * @retval -ENOTSUP: controller does not support I/O + * + * @details List of affected registers and their bits during the soft reset trigger: + * * RCAR_MMC_STOP all bits reset to default (0x0); + * * RCAR_MMC_INFO1 affected bits: + * * RCAR_MMC_INFO1_CMP default state 0; + * * RCAR_MMC_INFO1_RSP default state 0; + * * HPIRES Response Reception Completion (16), default state 0; + * * RCAR_MMC_INFO2 all bits reset 0, except the next: + * * RCAR_MMC_INFO2_DAT0 state unknown after reset; + * * RCAR_MMC_INFO2_SCLKDIVEN default state 1; + * * RCAR_MMC_CLKCTL affected bit(s): + * * RCAR_MMC_CLKCTL_SCLKEN default state 0; + * * RCAR_MMC_OPTION affected bits: + * * WIDTH (15) and WIDTH8 (13) set to 0, which equal to 4-bits bus; + * * Timeout Mode Select (EXTOP - 9) is set to 0; + * * Timeout Mask (TOUTMASK - 8) is set to 0; + * * Timeout Counter (TOP27-TOP24 bits 7-4) is equal to 0b1110; + * * Card Detect Time Counter (CTOP24-CTOP21 bits 3-0) is equal to 0b1110; + * * RCAR_MMC_ERR_STS1 all bits after reset 0, except the next: + * * E13 default state 1 (E12-E14 it is CRC status 0b010); + * * RCAR_MMC_ERR_STS2 all bits after reset 0; + * * IO_INFO1 all bits after reset 0; + * * RCAR_MMC_IF_MODE all bits after reset 0. + */ +static int rcar_mmc_reset(const struct device *dev) +{ + int ret = 0; + uint32_t reg; + struct mmc_rcar_data *data; + uint8_t can_retune; + + if (!dev) { + return -EINVAL; + } + + data = dev->data; + + /* + * soft reset of the host + */ + reg = rcar_mmc_read_reg32(dev, RCAR_MMC_SOFT_RST); + reg &= ~RCAR_MMC_SOFT_RST_RSTX; + rcar_mmc_write_reg32(dev, RCAR_MMC_SOFT_RST, reg); + reg |= RCAR_MMC_SOFT_RST_RSTX; + rcar_mmc_write_reg32(dev, RCAR_MMC_SOFT_RST, reg); + + rcar_mmc_reset_and_mask_irqs(dev); + + /* + * note: DMA reset can be triggered only in case of error in + * DMA Info2 otherwise the SDIP will not accurately operate + */ +#ifdef CONFIG_RCAR_MMC_DMA_SUPPORT + rcar_mmc_reset_dma(dev); +#endif + + can_retune = data->can_retune; + if (can_retune) { + rcar_mmc_disable_scc(dev); + } + + /* note: be careful soft reset stops SDCLK */ + if (data->restore_cfg_after_reset) { + struct sdhc_io ios; + + memcpy(&ios, &data->host_io, sizeof(ios)); + memset(&data->host_io, 0, sizeof(ios)); + + data->host_io.power_mode = ios.power_mode; + + ret = sdhc_set_io(dev, &ios); + + rcar_mmc_write_reg32(dev, RCAR_MMC_STOP, RCAR_MMC_STOP_SEC); + +#ifdef CONFIG_RCAR_MMC_SCC_SUPPORT + /* tune if this reset isn't invoked during tuning */ + if (can_retune && (ios.timing == SDHC_TIMING_SDR50 || + ios.timing == SDHC_TIMING_SDR104 || + ios.timing == SDHC_TIMING_HS200)) { + ret = rcar_mmc_execute_tuning(dev); + } +#endif + + return ret; + } + + data->ddr_mode = 0; + data->host_io.bus_width = SDHC_BUS_WIDTH4BIT; + data->host_io.timing = SDHC_TIMING_LEGACY; + data->is_last_cmd_app_cmd = 0; + + return 0; +} + +/** + * @brief SD Clock (SD_CLK) Output Control Enable + * + * @note in/out parameters should be checked by a caller function. + * + * @param dev MMC device + * @param enable + * false: SD_CLK output is disabled. The SD_CLK signal is fixed 0. + * true: SD_CLK output is enabled. + * + * @retval 0 I/O was configured correctly + * @retval -ETIMEDOUT: card busy flag is set during long time + */ +static int rcar_mmc_enable_clock(const struct device *dev, bool enable) +{ + int ret; + uint32_t mmc_clk_ctl = rcar_mmc_read_reg32(dev, RCAR_MMC_CLKCTL); + + if (enable == true) { + mmc_clk_ctl &= ~RCAR_MMC_CLKCTL_OFFEN; + mmc_clk_ctl |= RCAR_MMC_CLKCTL_SCLKEN; + } else { + mmc_clk_ctl |= RCAR_MMC_CLKCTL_OFFEN; + mmc_clk_ctl &= ~RCAR_MMC_CLKCTL_SCLKEN; + } + + /* + * Do not change the values of these bits + * when the CBSY bit in SD_INFO2 is 1 + */ + ret = rcar_mmc_poll_reg_flags_check_err(dev, RCAR_MMC_INFO2, RCAR_MMC_INFO2_CBSY, 0, false, + false, MMC_POLL_FLAGS_TIMEOUT_US); + if (ret) { + return -ETIMEDOUT; + } + rcar_mmc_write_reg32(dev, RCAR_MMC_CLKCTL, mmc_clk_ctl); + + /* SD spec recommends at least 1 ms of delay */ + k_msleep(1); + + return 0; +} + +/** + * @brief Convert SDHC response to Renesas MMC response + * + * Function performs a conversion from SDHC response to Renesas MMC + * CMD register response. + * + * @note in/out parameters should be checked by a caller function. + * + * @param response_type SDHC response type without SPI flags + * + * @retval positiv number (partial configuration of CMD register) on + * success, negative errno code otherwise + */ +static int32_t rcar_mmc_convert_sd_to_mmc_resp(uint32_t response_type) +{ + uint32_t mmc_resp = 0U; + + switch (response_type) { + case SD_RSP_TYPE_NONE: + mmc_resp = RCAR_MMC_CMD_RSP_NONE; + break; + case SD_RSP_TYPE_R1: + case SD_RSP_TYPE_R5: + case SD_RSP_TYPE_R6: + case SD_RSP_TYPE_R7: + mmc_resp = RCAR_MMC_CMD_RSP_R1; + break; + case SD_RSP_TYPE_R1b: + case SD_RSP_TYPE_R5b: + mmc_resp = RCAR_MMC_CMD_RSP_R1B; + break; + case SD_RSP_TYPE_R2: + mmc_resp = RCAR_MMC_CMD_RSP_R2; + break; + case SD_RSP_TYPE_R3: + case SD_RSP_TYPE_R4: + mmc_resp = RCAR_MMC_CMD_RSP_R3; + break; + default: + LOG_ERR("unknown response type 0x%08x", response_type); + return -EINVAL; + } + + __ASSERT((int32_t)mmc_resp >= 0, "%s: converted response shouldn't be negative", __func__); + + return mmc_resp; +} + +/** + * @brief Convert response from Renesas MMC to SDHC + * + * Function writes a response to response array of @ref sdhc_command structure + * + * @note in/out parameters should be checked by a caller function. + * + * @param dev MMC device + * @param cmd MMC command + * @param response_type SDHC response type without SPI flags + * + * @retval none + */ +static void rcar_mmc_extract_resp(const struct device *dev, struct sdhc_command *cmd, + uint32_t response_type) +{ + if (response_type == SD_RSP_TYPE_R2) { + uint32_t rsp_127_104 = rcar_mmc_read_reg32(dev, RCAR_MMC_RSP76); + uint32_t rsp_103_72 = rcar_mmc_read_reg32(dev, RCAR_MMC_RSP54); + uint32_t rsp_71_40 = rcar_mmc_read_reg32(dev, RCAR_MMC_RSP32); + uint32_t rsp_39_8 = rcar_mmc_read_reg32(dev, RCAR_MMC_RSP10); + + cmd->response[0] = (rsp_39_8 & 0xffffff) << 8; + cmd->response[1] = + ((rsp_71_40 & 0x00ffffff) << 8) | ((rsp_39_8 & 0xff000000) >> 24); + cmd->response[2] = + ((rsp_103_72 & 0x00ffffff) << 8) | ((rsp_71_40 & 0xff000000) >> 24); + cmd->response[3] = + ((rsp_127_104 & 0x00ffffff) << 8) | ((rsp_103_72 & 0xff000000) >> 24); + + LOG_DBG("Response 2\n\t[0]: 0x%08x\n\t[1]: 0x%08x" + "\n\t[2]: 0x%08x\n\t[3]: 0x%08x", + cmd->response[0], cmd->response[1], cmd->response[2], cmd->response[3]); + } else { + cmd->response[0] = rcar_mmc_read_reg32(dev, RCAR_MMC_RSP10); + LOG_DBG("Response %u\n\t[0]: 0x%08x", response_type, cmd->response[0]); + } +} + +/* configure CMD register for tx/rx data */ +static uint32_t rcar_mmc_gen_data_cmd(struct sdhc_command *cmd, struct sdhc_data *data) +{ + uint32_t cmd_reg = RCAR_MMC_CMD_DATA; + + switch (cmd->opcode) { + case MMC_SEND_EXT_CSD: + case SD_READ_SINGLE_BLOCK: + case MMC_SEND_TUNING_BLOCK: + case SD_SEND_TUNING_BLOCK: + case SD_SWITCH: + case SD_APP_SEND_NUM_WRITTEN_BLK: + case SD_APP_SEND_SCR: + cmd_reg |= RCAR_MMC_CMD_RD; + break; + case SD_READ_MULTIPLE_BLOCK: + cmd_reg |= RCAR_MMC_CMD_RD; + cmd_reg |= RCAR_MMC_CMD_MULTI; + break; + case SD_WRITE_MULTIPLE_BLOCK: + cmd_reg |= RCAR_MMC_CMD_MULTI; + break; + case SD_WRITE_SINGLE_BLOCK: + /* fall through */ + default: + break; + } + + if (data->blocks > 1) { + cmd_reg |= RCAR_MMC_CMD_MULTI; + } + + return cmd_reg; +} + +/** + * @brief Transmit/Receive data to/from MMC using DMA + * + * Sends/Receives data to/from the MMC controller. + * + * @note in/out parameters should be checked by a caller function. + * + * @param dev MMC device + * @param data MMC data buffer for tx/rx + * @param is_read it is read or write operation + * + * @retval 0 tx/rx was successful + * @retval -ENOTSUP: cache flush/invalidate aren't supported + * @retval -ETIMEDOUT: timed out while tx/rx + * @retval -EIO: I/O error + * @retval -EILSEQ: communication out of sync + */ +static int rcar_mmc_dma_rx_tx_data(const struct device *dev, struct sdhc_data *data, bool is_read) +{ + uintptr_t dma_addr; + uint32_t reg; + int ret = 0; + uint32_t dma_info1_poll_flag; +#ifdef CONFIG_RCAR_MMC_DMA_IRQ_DRIVEN_SUPPORT + struct mmc_rcar_data *dev_data = dev->data; +#endif + + ret = sys_cache_data_flush_range(data->data, data->blocks * data->block_size); + if (ret < 0) { + LOG_ERR("%s: can't invalidate data cache before write", dev->name); + return ret; + } + + reg = rcar_mmc_read_reg32(dev, RCAR_MMC_DMA_MODE); + if (is_read) { + dma_info1_poll_flag = RCAR_MMC_DMA_INFO1_END_RD2; + reg |= RCAR_MMC_DMA_MODE_DIR_RD; + } else { + dma_info1_poll_flag = RCAR_MMC_DMA_INFO1_END_WR; + reg &= ~RCAR_MMC_DMA_MODE_DIR_RD; + } + rcar_mmc_write_reg32(dev, RCAR_MMC_DMA_MODE, reg); + + reg = rcar_mmc_read_reg32(dev, RCAR_MMC_EXTMODE); + reg |= RCAR_MMC_EXTMODE_DMA_EN; + rcar_mmc_write_reg32(dev, RCAR_MMC_EXTMODE, reg); + + dma_addr = z_mem_phys_addr(data->data); + + rcar_mmc_write_reg32(dev, RCAR_MMC_DMA_ADDR_L, dma_addr); + rcar_mmc_write_reg32(dev, RCAR_MMC_DMA_ADDR_H, 0); + +#ifdef CONFIG_RCAR_MMC_DMA_IRQ_DRIVEN_SUPPORT + rcar_mmc_write_reg32( + dev, RCAR_MMC_DMA_INFO2_MASK, + (uint32_t)(is_read ? (~RCAR_MMC_DMA_INFO2_ERR_RD) : (~RCAR_MMC_DMA_INFO2_ERR_WR))); + + reg = rcar_mmc_read_reg32(dev, RCAR_MMC_DMA_INFO1_MASK); + reg &= ~dma_info1_poll_flag; + rcar_mmc_write_reg32(dev, RCAR_MMC_DMA_INFO1_MASK, reg); + rcar_mmc_write_reg32(dev, RCAR_MMC_DMA_CTL, RCAR_MMC_DMA_CTL_START); + + ret = k_sem_take(&dev_data->irq_xref_fin, K_MSEC(data->timeout_ms)); + if (ret < 0) { + LOG_ERR("%s: interrupt signal timeout error %d", dev->name, ret); + } + + reg = rcar_mmc_read_reg32(dev, RCAR_MMC_DMA_INFO2); + if (reg) { + LOG_ERR("%s: an error occurs on the DMAC channel #%u", dev->name, + (reg & RCAR_MMC_DMA_INFO2_ERR_RD) ? 1U : 0U); + ret = -EIO; + } +#else + rcar_mmc_write_reg32(dev, RCAR_MMC_DMA_CTL, RCAR_MMC_DMA_CTL_START); + ret = rcar_mmc_poll_reg_flags_check_err(dev, RCAR_MMC_DMA_INFO1, dma_info1_poll_flag, + dma_info1_poll_flag, false, true, + data->timeout_ms * 1000LL); +#endif + + if (is_read) { + if (sys_cache_data_invd_range(data->data, data->blocks * data->block_size) < 0) { + LOG_ERR("%s: can't invalidate data cache after read", dev->name); + } + } + + /* in case when we get to here and there wasn't IRQ trigger */ + rcar_mmc_write_reg32(dev, RCAR_MMC_DMA_INFO1_MASK, 0xfffffeff); + rcar_mmc_write_reg32(dev, RCAR_MMC_DMA_INFO2_MASK, ~0); + + if (ret == -EIO) { + rcar_mmc_reset_dma(dev); + } + + reg = rcar_mmc_read_reg32(dev, RCAR_MMC_EXTMODE); + reg &= ~RCAR_MMC_EXTMODE_DMA_EN; + rcar_mmc_write_reg32(dev, RCAR_MMC_EXTMODE, reg); + + return ret; +} + +/* read from SD/MMC controller buf0 register */ +static inline uint64_t rcar_mmc_read_buf0(const struct device *dev) +{ + uint64_t buf0 = 0ULL; + struct mmc_rcar_data *dev_data = dev->data; + uint8_t sd_buf0_size = dev_data->width_access_sd_buf0; + mm_reg_t buf0_addr = DEVICE_MMIO_GET(dev) + RCAR_MMC_BUF0; + + switch (sd_buf0_size) { + case 8: + buf0 = sys_read64(buf0_addr); + break; + case 4: + buf0 = sys_read32(buf0_addr); + break; + case 2: + buf0 = sys_read16(buf0_addr); + break; + default: + k_panic(); + break; + } + + return buf0; +} + +/* write to SD/MMC controller buf0 register */ +static inline void rcar_mmc_write_buf0(const struct device *dev, uint64_t val) +{ + struct mmc_rcar_data *dev_data = dev->data; + uint8_t sd_buf0_size = dev_data->width_access_sd_buf0; + mm_reg_t buf0_addr = DEVICE_MMIO_GET(dev) + RCAR_MMC_BUF0; + + switch (sd_buf0_size) { + case 8: + sys_write64(val, buf0_addr); + break; + case 4: + sys_write32(val, buf0_addr); + break; + case 2: + sys_write16(val, buf0_addr); + break; + default: + k_panic(); + break; + } +} + +/** + * @brief Transmit/Receive data to/from MMC without DMA + * + * Sends/Receives data to/from the MMC controller. + * + * @note in/out parameters should be checked by a caller function. + * + * @param dev MMC device + * @param data MMC data buffer for tx/rx + * @param is_read it is read or write operation + * + * @retval 0 tx/rx was successful + * @retval -EINVAL: invalid block size + * @retval -ETIMEDOUT: timed out while tx/rx + * @retval -EIO: I/O error + * @retval -EILSEQ: communication out of sync + */ +static int rcar_mmc_sd_buf_rx_tx_data(const struct device *dev, struct sdhc_data *data, + bool is_read) +{ + struct mmc_rcar_data *dev_data = dev->data; + uint32_t block; + int ret = 0; + uint32_t info2_poll_flag = is_read ? RCAR_MMC_INFO2_BRE : RCAR_MMC_INFO2_BWE; + uint8_t sd_buf0_size = dev_data->width_access_sd_buf0; + uint16_t aligned_block_size = ROUND_UP(data->block_size, sd_buf0_size); + uint32_t cmd_reg = 0; + int64_t remaining_timeout_us = data->timeout_ms * 1000LL; + + /* + * note: below code should work for all possible block sizes, but + * we need below check, because code isn't tested with smaller + * block sizes. + */ + if ((data->block_size % dev_data->width_access_sd_buf0) || + (data->block_size < dev_data->width_access_sd_buf0)) { + LOG_ERR("%s: block size (%u) less or not align on SD BUF0 access width (%hhu)", + dev->name, data->block_size, dev_data->width_access_sd_buf0); + return -EINVAL; + } + + /* + * JEDEC Standard No. 84-B51 + * 6.6.24 Dual Data Rate mode operation: + * Therefore, all single or multiple block data transfer read or write will operate on + * a fixed block size of 512 bytes while the Device remains in dual data rate. + * + * Physical Layer Specification Version 3.01 + * 4.12.6 Timing Changes in DDR50 Mode + * 4.12.6.2 Protocol Principles + * * Read and Write data block length size is always 512 bytes (same as SDHC). + */ + if (dev_data->ddr_mode && data->block_size != 512) { + LOG_ERR("%s: block size (%u) isn't equal to 512 in DDR mode", dev->name, + data->block_size); + return -EINVAL; + } + + /* + * note: the next restrictions we have according to description of + * transfer data length register from R-Car S4 series User's Manual + */ + if (data->block_size > 512 || data->block_size == 0) { + LOG_ERR("%s: block size (%u) must not be bigger than 512 bytes and equal to zero", + dev->name, data->block_size); + return -EINVAL; + } + + cmd_reg = rcar_mmc_read_reg32(dev, RCAR_MMC_CMD); + if (cmd_reg & RCAR_MMC_CMD_MULTI) { + /* CMD12 is automatically issued at multiple block transfer */ + if (!(cmd_reg & RCAR_MMC_CMD_NOSTOP) && data->block_size != 512) { + LOG_ERR("%s: illegal block size (%u) for multi-block xref with CMD12", + dev->name, data->block_size); + return -EINVAL; + } + + switch (data->block_size) { + case 32: + case 64: + case 128: + case 256: + case 512: + break; + default: + LOG_ERR("%s: illegal block size (%u) for multi-block xref without CMD12", + dev->name, data->block_size); + return -EINVAL; + } + } + + if (data->block_size == 1 && dev_data->host_io.bus_width == SDHC_BUS_WIDTH8BIT) { + LOG_ERR("%s: block size can't be equal to 1 with 8-bits bus width", dev->name); + return -EINVAL; + } + + for (block = 0; block < data->blocks; block++) { + uint8_t *buf = (uint8_t *)data->data + (block * data->block_size); + uint32_t info2_reg; + uint16_t w_off; /* word offset in a block */ + uint64_t start_block_xref_us = k_ticks_to_us_ceil64(k_uptime_ticks()); + + /* wait until the buffer is filled with data */ + ret = rcar_mmc_poll_reg_flags_check_err(dev, RCAR_MMC_INFO2, info2_poll_flag, + info2_poll_flag, true, false, + remaining_timeout_us); + if (ret) { + return ret; + } + + /* clear write/read buffer ready flag */ + info2_reg = rcar_mmc_read_reg32(dev, RCAR_MMC_INFO2); + info2_reg &= ~info2_poll_flag; + rcar_mmc_write_reg32(dev, RCAR_MMC_INFO2, info2_reg); + + for (w_off = 0; w_off < aligned_block_size; w_off += sd_buf0_size) { + uint64_t buf0 = 0ULL; + uint8_t copy_size = MIN(sd_buf0_size, data->block_size - w_off); + + if (is_read) { + buf0 = rcar_mmc_read_buf0(dev); + memcpy(buf + w_off, &buf0, copy_size); + } else { + memcpy(&buf0, buf + w_off, copy_size); + rcar_mmc_write_buf0(dev, buf0); + } + } + + remaining_timeout_us -= + k_ticks_to_us_ceil64(k_uptime_ticks()) - start_block_xref_us; + if (remaining_timeout_us < 0) { + return -ETIMEDOUT; + } + } + + return ret; +} + +/** + * @brief Transmit/Receive data to/from MMC + * + * Sends/Receives data to/from the MMC controller. + * + * @note in/out parameters should be checked by a caller function. + * + * @param dev MMC device + * @param data MMC data buffer for tx/rx + * @param is_read it is read or write operation + * + * @retval 0 tx/rx was successful + * @retval -EINVAL: invalid block size + * @retval -ETIMEDOUT: timed out while tx/rx + * @retval -EIO: I/O error + * @retval -EILSEQ: communication out of sync + */ +static int rcar_mmc_rx_tx_data(const struct device *dev, struct sdhc_data *data, bool is_read) +{ + uint32_t info1_reg; + int ret = 0; + +#ifdef CONFIG_RCAR_MMC_DMA_SUPPORT + if (!(z_mem_phys_addr(data->data) >> 32)) { + ret = rcar_mmc_dma_rx_tx_data(dev, data, is_read); + } else +#endif + { + ret = rcar_mmc_sd_buf_rx_tx_data(dev, data, is_read); + } + + if (ret < 0) { + return ret; + } + + ret = rcar_mmc_poll_reg_flags_check_err(dev, RCAR_MMC_INFO1, RCAR_MMC_INFO1_CMP, + RCAR_MMC_INFO1_CMP, true, false, + MMC_POLL_FLAGS_TIMEOUT_US); + if (ret) { + return ret; + } + + /* clear access end flag */ + info1_reg = rcar_mmc_read_reg32(dev, RCAR_MMC_INFO1); + info1_reg &= ~RCAR_MMC_INFO1_CMP; + rcar_mmc_write_reg32(dev, RCAR_MMC_INFO1, info1_reg); + + return ret; +} + +/** + * @brief Send command to MMC + * + * Sends a command to the MMC controller. + * + * @param dev MMC device + * @param cmd MMC command + * @param data MMC data. Leave NULL to send SD command without data. + * + * @retval 0 command was sent successfully + * @retval -ETIMEDOUT: command timed out while sending + * @retval -ENOTSUP: host controller does not support command + * @retval -EIO: I/O error + * @retval -EILSEQ: communication out of sync + */ +static int rcar_mmc_request(const struct device *dev, struct sdhc_command *cmd, + struct sdhc_data *data) +{ + int ret = -ENOTSUP; + uint32_t reg; + uint32_t response_type; + bool is_read = true; + int attempts; + struct mmc_rcar_data *dev_data; + + if (!dev || !cmd) { + return -EINVAL; + } + + dev_data = dev->data; + response_type = cmd->response_type & SDHC_NATIVE_RESPONSE_MASK; + attempts = cmd->retries + 1; + + while (ret && attempts-- > 0) { + if (ret != -ENOTSUP) { + rcar_mmc_reset(dev); +#ifdef CONFIG_RCAR_MMC_SCC_SUPPORT + rcar_mmc_retune_if_needed(dev, true); +#endif + } + + ret = rcar_mmc_poll_reg_flags_check_err(dev, RCAR_MMC_INFO2, RCAR_MMC_INFO2_CBSY, 0, + false, false, MMC_POLL_FLAGS_TIMEOUT_US); + if (ret) { + ret = -EBUSY; + continue; + } + + rcar_mmc_reset_and_mask_irqs(dev); + + rcar_mmc_write_reg32(dev, RCAR_MMC_ARG, cmd->arg); + + reg = cmd->opcode; + + if (data) { + rcar_mmc_write_reg32(dev, RCAR_MMC_SIZE, data->block_size); + rcar_mmc_write_reg32(dev, RCAR_MMC_SECCNT, data->blocks); + reg |= rcar_mmc_gen_data_cmd(cmd, data); + is_read = (reg & RCAR_MMC_CMD_RD) ? true : false; + } + + /* CMD55 is always sended before ACMD */ + if (dev_data->is_last_cmd_app_cmd) { + reg |= RCAR_MMC_CMD_APP; + } + + ret = rcar_mmc_convert_sd_to_mmc_resp(response_type); + if (ret < 0) { + /* don't need to retry we will always have the same result */ + return -EINVAL; + } + + reg |= ret; + + LOG_DBG("(SD_CMD=%08x, SD_ARG=%08x)", cmd->opcode, cmd->arg); + rcar_mmc_write_reg32(dev, RCAR_MMC_CMD, reg); + + /* wait until response end flag is set or errors occur */ + ret = rcar_mmc_poll_reg_flags_check_err(dev, RCAR_MMC_INFO1, RCAR_MMC_INFO1_RSP, + RCAR_MMC_INFO1_RSP, true, false, + cmd->timeout_ms * 1000LL); + if (ret) { + continue; + } + + /* clear response end flag */ + reg = rcar_mmc_read_reg32(dev, RCAR_MMC_INFO1); + reg &= ~RCAR_MMC_INFO1_RSP; + rcar_mmc_write_reg32(dev, RCAR_MMC_INFO1, reg); + + rcar_mmc_extract_resp(dev, cmd, response_type); + + if (data) { + ret = rcar_mmc_rx_tx_data(dev, data, is_read); + if (ret) { + continue; + } + } + + /* wait until the SD bus (CMD, DAT) is free or errors occur */ + ret = rcar_mmc_poll_reg_flags_check_err( + dev, RCAR_MMC_INFO2, RCAR_MMC_INFO2_SCLKDIVEN, RCAR_MMC_INFO2_SCLKDIVEN, + true, false, MMC_POLL_FLAGS_TIMEOUT_US); + } + + if (ret) { + rcar_mmc_reset(dev); +#ifdef CONFIG_RCAR_MMC_SCC_SUPPORT + rcar_mmc_retune_if_needed(dev, true); +#endif + } + + dev_data->is_last_cmd_app_cmd = (cmd->opcode == SD_APP_CMD); + + return ret; +} + +/* convert sd_voltage to string */ +static inline const char *const rcar_mmc_get_signal_voltage_str(enum sd_voltage voltage) +{ + static const char *const sig_vol_str[] = { + [0] = "Unset", [SD_VOL_3_3_V] = "3.3V", [SD_VOL_3_0_V] = "3.0V", + [SD_VOL_1_8_V] = "1.8V", [SD_VOL_1_2_V] = "1.2V", + }; + + if (voltage >= 0 && voltage < ARRAY_SIZE(sig_vol_str)) { + return sig_vol_str[voltage]; + } else { + return "Unknown"; + } +} + +/* convert sdhc_timing_mode to string */ +static inline const char *const rcar_mmc_get_timing_str(enum sdhc_timing_mode timing) +{ + static const char *const timing_str[] = { + [0] = "Unset", + [SDHC_TIMING_LEGACY] = "LEGACY", + [SDHC_TIMING_HS] = "HS", + [SDHC_TIMING_SDR12] = "SDR12", + [SDHC_TIMING_SDR25] = "SDR25", + [SDHC_TIMING_SDR50] = "SDR50", + [SDHC_TIMING_SDR104] = "SDR104", + [SDHC_TIMING_DDR50] = "DDR50", + [SDHC_TIMING_DDR52] = "DDR52", + [SDHC_TIMING_HS200] = "HS200", + [SDHC_TIMING_HS400] = "HS400", + }; + + if (timing >= 0 && timing < ARRAY_SIZE(timing_str)) { + return timing_str[timing]; + } else { + return "Unknown"; + } +} + +/* change voltage of MMC */ +static int rcar_mmc_change_voltage(const struct mmc_rcar_cfg *cfg, struct sdhc_io *host_io, + struct sdhc_io *ios) +{ + int ret = 0; + + /* Set host signal voltage */ + if (!ios->signal_voltage || ios->signal_voltage == host_io->signal_voltage) { + return 0; + } + + switch (ios->signal_voltage) { + case SD_VOL_3_3_V: + ret = regulator_set_voltage(cfg->regulator_vqmmc, 3300000, 3300000); + if (ret && ret != -ENOSYS) { + break; + } + + ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT); + break; + case SD_VOL_1_8_V: + ret = regulator_set_voltage(cfg->regulator_vqmmc, 1800000, 1800000); + if (ret && ret != -ENOSYS) { + break; + } + + ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_UHS); + break; + case SD_VOL_3_0_V: + case SD_VOL_1_2_V: + /* fall through */ + default: + ret = -ENOTSUP; + return ret; + } + + if (!ret) { + host_io->signal_voltage = ios->signal_voltage; + } + + return ret; +} + +/* note: for zero val function returns zero */ +static inline uint32_t round_up_next_pwr_of_2(uint32_t val) +{ + __ASSERT(val, "Zero val passed to %s", __func__); + + val--; + val |= val >> 1; + val |= val >> 2; + val |= val >> 4; + val |= val >> 8; + val |= val >> 16; + return ++val; +} + +/** + * @brief configure clock divider on MMC controller + * + * @note In/out parameters should be checked by a caller function. + * @note In the case of data transfer in HS400 mode (HS400 bit in + * SDIF_MODE = 1), do not set this width equal to 1. + * @note In the case of writing of one-byte block, 8-bit width cannot + * be specified for the bus width. Change the bus width to 4 bits + * or 1 bit before writing one-byte block. + * + * @param dev MMC device + * @param io I/O properties + * + * @retval 0 I/O was configured correctly + * @retval -ENOTSUP: controller does not support these I/O settings + * @retval -ETIMEDOUT: card busy flag is set during long time + */ +static int rcar_mmc_set_clk_rate(const struct device *dev, struct sdhc_io *ios) +{ + int ret = 0; + uint32_t divisor; + uint32_t mmc_clk_ctl; + struct mmc_rcar_data *data = dev->data; + const struct mmc_rcar_cfg *cfg = dev->config; + struct sdhc_io *host_io = &data->host_io; + + if (host_io->clock == ios->clock) { + return 0; + } + + if (ios->clock == 0) { + host_io->clock = 0; + return rcar_mmc_enable_clock(dev, false); + } + + if (ios->clock > data->props.f_max || ios->clock < data->props.f_min) { + LOG_ERR("SDHC I/O: clock (%d) isn't in range %d - %d Hz", ios->clock, + data->props.f_min, data->props.f_max); + return -EINVAL; + } + + divisor = DIV_ROUND_UP(cfg->max_frequency, ios->clock); + + /* Do not set divider to 0xff in DDR mode */ + if (data->ddr_mode && (divisor == 1)) { + divisor = 2; + } + + divisor = round_up_next_pwr_of_2(divisor); + if (divisor == 1) { + divisor = RCAR_MMC_CLKCTL_RCAR_DIV1; + } else { + divisor >>= 2; + } + + /* + * Stop the clock before changing its rate + * to avoid a glitch signal + */ + ret = rcar_mmc_enable_clock(dev, false); + if (ret) { + return ret; + } + + mmc_clk_ctl = rcar_mmc_read_reg32(dev, RCAR_MMC_CLKCTL); + if ((mmc_clk_ctl & RCAR_MMC_CLKCTL_SCLKEN) && + (mmc_clk_ctl & RCAR_MMC_CLKCTL_DIV_MASK) == divisor) { + host_io->clock = ios->clock; + return rcar_mmc_enable_clock(dev, false); + } + + /* + * Do not change the values of these bits + * when the CBSY bit in SD_INFO2 is 1 + */ + ret = rcar_mmc_poll_reg_flags_check_err(dev, RCAR_MMC_INFO2, RCAR_MMC_INFO2_CBSY, 0, false, + false, MMC_POLL_FLAGS_TIMEOUT_US); + if (ret) { + return -ETIMEDOUT; + } + + mmc_clk_ctl &= ~RCAR_MMC_CLKCTL_DIV_MASK; + mmc_clk_ctl |= divisor; + + rcar_mmc_write_reg32(dev, RCAR_MMC_CLKCTL, mmc_clk_ctl); + ret = rcar_mmc_enable_clock(dev, true); + if (ret) { + return ret; + } + + host_io->clock = ios->clock; + + LOG_DBG("%s: set clock rate to %d", dev->name, ios->clock); + + return 0; +} + +/** + * @brief set bus width of MMC + * + * @note In/out parameters should be checked by a caller function. + * @note In the case of data transfer in HS400 mode (HS400 bit in + * SDIF_MODE = 1), do not set this width equal to 1. + * @note In the case of writing of one-byte block, 8-bit width cannot + * be specified for the bus width. Change the bus width to 4 bits + * or 1 bit before writing one-byte block. + * + * @param dev MMC device + * @param io I/O properties + * + * @retval 0 I/O was configured correctly + * @retval -ENOTSUP: controller does not support these I/O settings + * @retval -ETIMEDOUT: card busy flag is set during long time + */ +static int rcar_mmc_set_bus_width(const struct device *dev, struct sdhc_io *ios) +{ + int ret = 0; + uint32_t mmc_option_reg; + uint32_t reg_width; + struct mmc_rcar_data *data = dev->data; + struct sdhc_io *host_io = &data->host_io; + + /* Set bus width */ + if (host_io->bus_width == ios->bus_width) { + return 0; + } + + if (!ios->bus_width) { + return 0; + } + + switch (ios->bus_width) { + case SDHC_BUS_WIDTH1BIT: + reg_width = RCAR_MMC_OPTION_WIDTH_1; + break; + case SDHC_BUS_WIDTH4BIT: + if (data->props.host_caps.bus_4_bit_support) { + reg_width = RCAR_MMC_OPTION_WIDTH_4; + } else { + LOG_ERR("SDHC I/O: 4-bits bus width isn't supported"); + return -ENOTSUP; + } + break; + case SDHC_BUS_WIDTH8BIT: + if (data->props.host_caps.bus_8_bit_support) { + reg_width = RCAR_MMC_OPTION_WIDTH_8; + } else { + LOG_ERR("SDHC I/O: 8-bits bus width isn't supported"); + return -ENOTSUP; + } + break; + default: + return -ENOTSUP; + } + + /* + * Do not change the values of these bits + * when the CBSY bit in SD_INFO2 is 1 + */ + ret = rcar_mmc_poll_reg_flags_check_err(dev, RCAR_MMC_INFO2, RCAR_MMC_INFO2_CBSY, 0, false, + false, MMC_POLL_FLAGS_TIMEOUT_US); + if (ret) { + return -ETIMEDOUT; + } + + mmc_option_reg = rcar_mmc_read_reg32(dev, RCAR_MMC_OPTION); + mmc_option_reg &= ~RCAR_MMC_OPTION_WIDTH_MASK; + mmc_option_reg |= reg_width; + rcar_mmc_write_reg32(dev, RCAR_MMC_OPTION, mmc_option_reg); + + host_io->bus_width = ios->bus_width; + + LOG_DBG("%s: set bus-width to %d", dev->name, host_io->bus_width); + return 0; +} + +/** + * set DDR mode on MMC controller according to value inside + * ddr_mode field from @ref mmc_rcar_data structure. + */ +static int rcar_mmc_set_ddr_mode(const struct device *dev) +{ + int ret = 0; + uint32_t if_mode_reg; + struct mmc_rcar_data *data = dev->data; + + /* + * Do not change the values of these bits + * when the CBSY bit in SD_INFO2 is 1 + */ + ret = rcar_mmc_poll_reg_flags_check_err(dev, RCAR_MMC_INFO2, RCAR_MMC_INFO2_CBSY, 0, false, + false, MMC_POLL_FLAGS_TIMEOUT_US); + if (ret) { + return -ETIMEDOUT; + } + + if_mode_reg = rcar_mmc_read_reg32(dev, RCAR_MMC_IF_MODE); + if (data->ddr_mode) { + /* HS400 mode (DDR mode) */ + if_mode_reg |= RCAR_MMC_IF_MODE_DDR; + } else { + /* Normal mode (default, high speed, or SDR) */ + if_mode_reg &= ~RCAR_MMC_IF_MODE_DDR; + } + rcar_mmc_write_reg32(dev, RCAR_MMC_IF_MODE, if_mode_reg); + + return 0; +} + +/** + * @brief set timing property of MMC + * + * For now function only can enable DDR mode and call the function for + * changing voltage. It is expectable that we change clock using another + * I/O option. + * @note In/out parameters should be checked by a caller function. + * + * @param dev MMC device + * @param io I/O properties + * + * @retval 0 I/O was configured correctly + * @retval -ENOTSUP: controller does not support these I/O settings + * @retval -ETIMEDOUT: card busy flag is set during long time + */ +static int rcar_mmc_set_timings(const struct device *dev, struct sdhc_io *ios) +{ + int ret; + struct mmc_rcar_data *data = dev->data; + struct sdhc_io *host_io = &data->host_io; + enum sd_voltage new_voltage = host_io->signal_voltage; + + if (host_io->timing == ios->timing) { + return 0; + } + + if (!host_io->timing) { + return 0; + } + + data->ddr_mode = 0; + + switch (ios->timing) { + case SDHC_TIMING_LEGACY: + break; + case SDHC_TIMING_HS: + if (!data->props.host_caps.high_spd_support) { + LOG_ERR("SDHC I/O: HS timing isn't supported"); + return -ENOTSUP; + } + break; + case SDHC_TIMING_SDR12: + case SDHC_TIMING_SDR25: + case SDHC_TIMING_SDR50: + break; + case SDHC_TIMING_SDR104: + if (!data->props.host_caps.sdr104_support) { + LOG_ERR("SDHC I/O: SDR104 timing isn't supported"); + return -ENOTSUP; + } + break; + case SDHC_TIMING_HS400: + if (!data->props.host_caps.hs400_support) { + LOG_ERR("SDHC I/O: HS400 timing isn't supported"); + return -ENOTSUP; + } + new_voltage = SD_VOL_1_8_V; + data->ddr_mode = 1; + break; + case SDHC_TIMING_DDR50: + case SDHC_TIMING_DDR52: + if (!data->props.host_caps.ddr50_support) { + LOG_ERR("SDHC I/O: DDR50/DDR52 timing isn't supported"); + return -ENOTSUP; + } + data->ddr_mode = 1; + break; + case SDHC_TIMING_HS200: + if (!data->props.host_caps.hs200_support) { + LOG_ERR("SDHC I/O: HS200 timing isn't supported"); + return -ENOTSUP; + } + new_voltage = SD_VOL_1_8_V; + break; + default: + return -ENOTSUP; + } + + ios->signal_voltage = new_voltage; + if (rcar_mmc_change_voltage(dev->config, host_io, ios)) { + return -ENOTSUP; + } + + ret = rcar_mmc_set_ddr_mode(dev); + if (ret) { + return ret; + } + + host_io->timing = ios->timing; + return 0; +} + +/** + * @brief set I/O properties of MMC + * + * I/O properties should be reconfigured when the card has been sent a command + * to change its own MMC settings. This function can also be used to toggle + * power to the SD card. + * + * @param dev MMC device + * @param io I/O properties + * + * @retval 0 I/O was configured correctly + * @retval -ENOTSUP: controller does not support these I/O settings + * @retval -EINVAL: some of pointers provided to the function are NULL + * @retval -ETIMEDOUT: card busy flag is set during long time + */ +static int rcar_mmc_set_io(const struct device *dev, struct sdhc_io *ios) +{ + int ret = 0; + struct mmc_rcar_data *data; + struct sdhc_io *host_io; + + if (!dev || !ios || !dev->data || !dev->config) { + return -EINVAL; + } + + data = dev->data; + host_io = &data->host_io; + + LOG_DBG("SDHC I/O: bus width %d, clock %dHz, card power %s, " + "timing %s, voltage %s", + ios->bus_width, ios->clock, ios->power_mode == SDHC_POWER_ON ? "ON" : "OFF", + rcar_mmc_get_timing_str(ios->timing), + rcar_mmc_get_signal_voltage_str(ios->signal_voltage)); + + /* Set host clock */ + ret = rcar_mmc_set_clk_rate(dev, ios); + if (ret) { + LOG_ERR("SDHC I/O: can't change clock rate error %d old %d new %d", ret, + host_io->clock, ios->clock); + return ret; + } + + /* + * Set card bus mode + * + * SD Specifications Part 1 Physical Layer Simplified Specification Version 9.00 + * 4.7.1 Command Types: "... there is no Open Drain mode in SD Memory Card" + * + * The use of open-drain mode is not possible in SD memory cards because the SD bus uses + * push-pull signaling, where both the host and the card can actively drive the data lines + * high or low. + * In an SD card, the command and response signaling needs to be bidirectional, and each + * signal line needs to be actively driven high or low. The use of open-drain mode in this + * scenario would not allow for the necessary bidirectional signaling and could result in + * communication errors. + * + * JEDEC Standard No. 84-B51, 10 The eMMC bus: + * "The e•MMC bus has eleven communication lines: + * - CMD: Command is a bidirectional signal. The host and Device drivers are operating in + * two modes, open drain and push/pull. + * - DAT0-7: Data lines are bidirectional signals. Host and Device drivers are operating + * in push-pull mode. + * - CLK: Clock is a host to Device signal. CLK operates in push-pull mode. + * - Data Strobe: Data Strobe is a Device to host signal. Data Strobe operates in + * push-pull mode." + * + * So, open-drain mode signaling is supported in eMMC as one of the signaling modes for + * the CMD line. But Gen3 and Gen4 boards has MMC/SD controller which is a specialized + * component designed specifically for managing communication with MMC/SD devices. It + * handles low-level operations such as protocol handling, data transfer, and error + * checking and should take care of the low-level details of communicating with the + * MMC/SD card, including setting the bus mode. Moreover, we can use only MMIO mode, the + * processor communicates with the MMC/SD controller through memory read and write + * operations, rather than through dedicated I/O instructions or specialized data transfer + * protocols like SPI or SDIO. Finally, R-Car Gen3 and Gen4 "User’s manuals: Hardware" + * don't have direct configurations for open-drain mode for both PFC and GPIO and Zephyr + * SDHC subsystem doesn't support any bus mode except push-pull. + */ + if (ios->bus_mode != SDHC_BUSMODE_PUSHPULL) { + LOG_ERR("SDHC I/O: not supported bus mode %d", ios->bus_mode); + return -ENOTSUP; + } + host_io->bus_mode = ios->bus_mode; + + /* Set card power */ + if (ios->power_mode && host_io->power_mode != ios->power_mode) { + const struct mmc_rcar_cfg *cfg = dev->config; + + switch (ios->power_mode) { + case SDHC_POWER_ON: + ret = regulator_enable(cfg->regulator_vmmc); + if (ret) { + break; + } + + k_msleep(data->props.power_delay); + + ret = regulator_enable(cfg->regulator_vqmmc); + if (ret) { + break; + } + + k_msleep(data->props.power_delay); + ret = rcar_mmc_enable_clock(dev, true); + break; + case SDHC_POWER_OFF: + if (regulator_is_enabled(cfg->regulator_vqmmc)) { + ret = regulator_disable(cfg->regulator_vqmmc); + if (ret) { + break; + } + } + + if (regulator_is_enabled(cfg->regulator_vmmc)) { + ret = regulator_disable(cfg->regulator_vmmc); + if (ret) { + break; + } + } + + ret = rcar_mmc_enable_clock(dev, false); + break; + default: + LOG_ERR("SDHC I/O: not supported power mode %d", ios->power_mode); + return -ENOTSUP; + } + + if (ret) { + return ret; + } + host_io->power_mode = ios->power_mode; + } + + ret = rcar_mmc_set_bus_width(dev, ios); + if (ret) { + LOG_ERR("SDHC I/O: can't change bus width error %d old %d new %d", ret, + host_io->bus_width, ios->bus_width); + return ret; + } + + ret = rcar_mmc_set_timings(dev, ios); + if (ret) { + LOG_ERR("SDHC I/O: can't change timing error %d old %d new %d", ret, + host_io->timing, ios->timing); + return ret; + } + + ret = rcar_mmc_change_voltage(dev->config, host_io, ios); + if (ret) { + LOG_ERR("SDHC I/O: can't change voltage! error %d old %d new %d", ret, + host_io->signal_voltage, ios->signal_voltage); + return ret; + } + + return 0; +} + +/** + * @brief check for MMC card presence + * + * Checks if card is present on the bus. + * + * @param dev MMC device + * + * @retval 1 card is present + * @retval 0 card is not present + * @retval -EINVAL: some of pointers provided to the function are NULL + */ +static int rcar_mmc_get_card_present(const struct device *dev) +{ + const struct mmc_rcar_cfg *cfg; + + if (!dev || !dev->config) { + return -EINVAL; + } + + cfg = dev->config; + if (cfg->non_removable) { + return 1; + } + + return !!(rcar_mmc_read_reg32(dev, RCAR_MMC_INFO1) & RCAR_MMC_INFO1_CD); +} + +#ifdef CONFIG_RCAR_MMC_SCC_SUPPORT + +/* JESD84-B51, 6.6.5.1 Sampling Tuning Sequence for HS200 */ +static const uint8_t tun_block_8_bits_bus[] = { + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc, + 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, + 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff, + 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, + 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, + 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, + 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, + 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, + 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, + 0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, + 0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, + 0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, + 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, +}; + +/* + * In 4 bit mode the same pattern is used as shown above, + * but only first 4 bits least significant from every byte is used, examle: + * 8-bits pattern: 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00 ... + * f f 0 f f f 0 0 ... + * 4-bits pattern: 0xff 0x0f 0xff 0x00 ... + */ +static const uint8_t tun_block_4_bits_bus[] = { + 0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc, + 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef, + 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, + 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef, + 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c, + 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee, + 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff, + 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde, +}; + +#define RENESAS_TAPNUM 8 + +/** + * @brief run MMC tuning + * + * MMC cards require signal tuning for UHS modes SDR104, HS200 or HS400. + * This function allows an application to request the SD host controller + * to tune the card. + * + * @param dev MMC device + * + * @retval 0 tuning succeeded (card is ready for commands), otherwise negative number is returned + */ +static int rcar_mmc_execute_tuning(const struct device *dev) +{ + int ret = -ENOTSUP; + const uint8_t *tun_block_ptr; + uint8_t tap_idx; + uint8_t is_mmc_cmd = false; + struct sdhc_command cmd = {0}; + struct sdhc_data data = {0}; + struct mmc_rcar_data *dev_data; + uint16_t valid_taps = 0; + uint16_t smpcmp_bitmask = 0; + + BUILD_ASSERT(sizeof(valid_taps) * 8 >= 2 * RENESAS_TAPNUM); + BUILD_ASSERT(sizeof(smpcmp_bitmask) * 8 >= 2 * RENESAS_TAPNUM); + + if (!dev) { + return -EINVAL; + } + + dev_data = dev->data; + dev_data->can_retune = 0; + + if (dev_data->host_io.timing == SDHC_TIMING_HS200) { + cmd.opcode = MMC_SEND_TUNING_BLOCK; + is_mmc_cmd = true; + } else if (dev_data->host_io.timing != SDHC_TIMING_HS400) { + cmd.opcode = SD_SEND_TUNING_BLOCK; + } else { + LOG_ERR("%s: tuning isn't possible in HS400 mode, it should be done in HS200", + dev->name); + return -EINVAL; + } + + cmd.response_type = SD_RSP_TYPE_R1; + cmd.timeout_ms = CONFIG_SD_CMD_TIMEOUT; + + data.blocks = 1; + data.data = dev_data->tuning_buf; + data.timeout_ms = CONFIG_SD_DATA_TIMEOUT; + if (dev_data->host_io.bus_width == SDHC_BUS_WIDTH4BIT) { + data.block_size = sizeof(tun_block_4_bits_bus); + tun_block_ptr = tun_block_4_bits_bus; + } else if (dev_data->host_io.bus_width == SDHC_BUS_WIDTH8BIT) { + data.block_size = sizeof(tun_block_8_bits_bus); + tun_block_ptr = tun_block_8_bits_bus; + } else { + LOG_ERR("%s: don't support tuning for 1-bit bus width", dev->name); + return -EINVAL; + } + + ret = rcar_mmc_enable_clock(dev, false); + if (ret) { + return ret; + } + + /* enable modes SDR104/HS200/HS400 */ + rcar_mmc_write_reg32(dev, RENESAS_SDHI_SCC_DT2FF, 0x300); + /* SCC sampling clock operation is enabled */ + rcar_mmc_write_reg32(dev, RENESAS_SDHI_SCC_DTCNTL, + RENESAS_SDHI_SCC_DTCNTL_TAPEN | RENESAS_TAPNUM << 16); + /* SCC sampling clock is used */ + rcar_mmc_write_reg32(dev, RENESAS_SDHI_SCC_CKSEL, RENESAS_SDHI_SCC_CKSEL_DTSEL); + /* SCC sampling clock position correction is disabled */ + rcar_mmc_write_reg32(dev, RENESAS_SDHI_SCC_RVSCNTL, 0); + /* cleanup errors */ + rcar_mmc_write_reg32(dev, RENESAS_SDHI_SCC_RVSREQ, 0); + + ret = rcar_mmc_enable_clock(dev, true); + if (ret) { + return ret; + } + + /* + * two runs is better for detecting TAP ok cases like next: + * - one burn: 0b10000011 + * - two burns: 0b1000001110000011 + * it is more easly to detect 3 OK taps in a row + */ + for (tap_idx = 0; tap_idx < 2 * RENESAS_TAPNUM; tap_idx++) { + /* clear flags */ + rcar_mmc_reset_and_mask_irqs(dev); + rcar_mmc_write_reg32(dev, RENESAS_SDHI_SCC_TAPSET, tap_idx % RENESAS_TAPNUM); + memset(dev_data->tuning_buf, 0, data.block_size); + ret = rcar_mmc_request(dev, &cmd, &data); + if (ret) { + LOG_DBG("%s: received an error (%d) during tuning request", dev->name, ret); + + if (is_mmc_cmd) { + struct sdhc_command stop_cmd = { + .opcode = SD_STOP_TRANSMISSION, + .response_type = SD_RSP_TYPE_R1b, + .timeout_ms = CONFIG_SD_CMD_TIMEOUT, + }; + + rcar_mmc_request(dev, &stop_cmd, NULL); + } + continue; + } + + smpcmp_bitmask |= !rcar_mmc_read_reg32(dev, RENESAS_SDHI_SCC_SMPCMP) << tap_idx; + + if (memcmp(tun_block_ptr, dev_data->tuning_buf, data.block_size)) { + LOG_DBG("%s: received tuning block doesn't equal to pattert TAP index %u", + dev->name, tap_idx); + continue; + } + + valid_taps |= BIT(tap_idx); + + LOG_DBG("%s: smpcmp_bitmask[%u] 0x%08x", dev->name, tap_idx, smpcmp_bitmask); + } + + /* both parts of bitmasks have to be the same */ + valid_taps &= (valid_taps >> RENESAS_TAPNUM); + valid_taps |= (valid_taps << RENESAS_TAPNUM); + + smpcmp_bitmask &= (smpcmp_bitmask >> RENESAS_TAPNUM); + smpcmp_bitmask |= (smpcmp_bitmask << RENESAS_TAPNUM); + + rcar_mmc_write_reg32(dev, RENESAS_SDHI_SCC_RVSREQ, 0); + + if (!valid_taps) { + LOG_ERR("%s: there isn't any valid tap during tuning", dev->name); + goto reset_scc; + } + + /* + * If all of the taps[i] is OK, the sampling clock position is selected by identifying + * the change point of data. Change point of the data can be found in the value of + * SCC_SMPCMP register + */ + if ((valid_taps >> RENESAS_TAPNUM) == (1 << RENESAS_TAPNUM) - 1) { + valid_taps = smpcmp_bitmask; + } + + /* do we have 3 set bits in a row at least */ + if (valid_taps & (valid_taps >> 1) & (valid_taps >> 2)) { + uint32_t max_len_range_pos = 0; + uint32_t max_bits_in_range = 0; + uint32_t pos_of_lsb_set = 0; + + /* all bits are set */ + if ((valid_taps >> RENESAS_TAPNUM) == (1 << RENESAS_TAPNUM) - 1) { + rcar_mmc_write_reg32(dev, RENESAS_SDHI_SCC_TAPSET, 0); + + if (!dev_data->manual_retuning) { + rcar_mmc_write_reg32(dev, RENESAS_SDHI_SCC_RVSCNTL, 1); + } + dev_data->can_retune = 1; + return 0; + } + + /* searching the longest range of set bits */ + while (valid_taps) { + uint32_t num_bits_in_range; + uint32_t rsh = 0; + + rsh = find_lsb_set(valid_taps) - 1; + pos_of_lsb_set += rsh; + + /* shift all leading zeros */ + valid_taps >>= rsh; + + num_bits_in_range = find_lsb_set(~valid_taps) - 1; + + /* shift all leading ones */ + valid_taps >>= num_bits_in_range; + + if (max_bits_in_range < num_bits_in_range) { + max_bits_in_range = num_bits_in_range; + max_len_range_pos = pos_of_lsb_set; + } + pos_of_lsb_set += num_bits_in_range; + } + + tap_idx = (max_len_range_pos + max_bits_in_range / 2) % RENESAS_TAPNUM; + rcar_mmc_write_reg32(dev, RENESAS_SDHI_SCC_TAPSET, tap_idx); + + LOG_DBG("%s: valid_taps %08x smpcmp_bitmask %08x tap_idx %u", dev->name, valid_taps, + smpcmp_bitmask, tap_idx); + + if (!dev_data->manual_retuning) { + rcar_mmc_write_reg32(dev, RENESAS_SDHI_SCC_RVSCNTL, 1); + } + dev_data->can_retune = 1; + return 0; + } + +reset_scc: + rcar_mmc_disable_scc(dev); + return ret; +} + +/* retune SCC in case of error during xref */ +static int rcar_mmc_retune_if_needed(const struct device *dev, bool request_retune) +{ + struct mmc_rcar_data *dev_data = dev->data; + int ret = 0; + uint32_t reg; + bool scc_pos_err = false; + uint8_t scc_tapset; + + if (!dev_data->can_retune) { + return 0; + } + + reg = rcar_mmc_read_reg32(dev, RENESAS_SDHI_SCC_RVSREQ); + if (reg & RENESAS_SDHI_SCC_RVSREQ_ERR) { + scc_pos_err = true; + } + + scc_tapset = rcar_mmc_read_reg32(dev, RENESAS_SDHI_SCC_TAPSET); + + LOG_DBG("%s: scc_tapset %08x scc_rvsreq %08x request %d is manual tuning %d", dev->name, + scc_tapset, reg, request_retune, dev_data->manual_retuning); + + if (request_retune || (scc_pos_err && !dev_data->manual_retuning)) { + return rcar_mmc_execute_tuning(dev); + } + + rcar_mmc_write_reg32(dev, RENESAS_SDHI_SCC_RVSREQ, 0); + + switch (reg & RENESAS_SDHI_SCC_RVSREQ_REQTAP_MASK) { + case RENESAS_SDHI_SCC_RVSREQ_REQTAPDOWN: + scc_tapset = (scc_tapset - 1) % RENESAS_TAPNUM; + break; + case RENESAS_SDHI_SCC_RVSREQ_REQTAPUP: + scc_tapset = (scc_tapset + 1) % RENESAS_TAPNUM; + break; + default: + ret = -EINVAL; + LOG_ERR("%s: can't perform manual tuning SCC_RVSREQ %08x", dev->name, reg); + break; + } + + if (!ret) { + rcar_mmc_write_reg32(dev, RENESAS_SDHI_SCC_TAPSET, scc_tapset); + } + + return ret; +} + +#endif /* CONFIG_RCAR_MMC_SCC_SUPPORT */ + +/** + * @brief Get MMC controller properties + * + * Gets host properties from the host controller. Host controller should + * initialize all values in the @ref sdhc_host_props structure provided. + * + * @param dev Renesas MMC device + * @param props property structure to be filled by MMC driver + * + * @retval 0 function succeeded. + * @retval -EINVAL: some of pointers provided to the function are NULL + */ +static int rcar_mmc_get_host_props(const struct device *dev, struct sdhc_host_props *props) +{ + struct mmc_rcar_data *data; + + if (!props || !dev || !dev->data) { + return -EINVAL; + } + + data = dev->data; + memcpy(props, &data->props, sizeof(*props)); + return 0; +} + +static const struct sdhc_driver_api rcar_sdhc_api = { + .card_busy = rcar_mmc_card_busy, +#ifdef CONFIG_RCAR_MMC_SCC_SUPPORT + .execute_tuning = rcar_mmc_execute_tuning, +#endif + .get_card_present = rcar_mmc_get_card_present, + .get_host_props = rcar_mmc_get_host_props, + .request = rcar_mmc_request, + .reset = rcar_mmc_reset, + .set_io = rcar_mmc_set_io, +}; + +/* start SD-IF clock at max frequency configured in dts */ +static int rcar_mmc_init_start_clk(const struct mmc_rcar_cfg *cfg) +{ + int ret = 0; + const struct device *cpg_dev = cfg->cpg_dev; + uintptr_t rate = cfg->max_frequency; + + ret = clock_control_on(cpg_dev, (clock_control_subsys_t *)&cfg->bus_clk); + if (ret < 0) { + return ret; + } + + ret = clock_control_on(cpg_dev, (clock_control_subsys_t *)&cfg->cpg_clk); + if (ret < 0) { + return ret; + } + + ret = clock_control_set_rate(cpg_dev, (clock_control_subsys_t *)&cfg->cpg_clk, + (clock_control_subsys_rate_t)rate); + if (ret < 0) { + clock_control_off(cpg_dev, (clock_control_subsys_t *)&cfg->cpg_clk); + } + + rate = MMC_BUS_CLOCK_FREQ; + ret = clock_control_set_rate(cpg_dev, (clock_control_subsys_t *)&cfg->bus_clk, + (clock_control_subsys_rate_t)rate); + /* SD spec recommends at least 1 ms of delay after start of clock */ + k_msleep(1); + + return ret; +} + +static void rcar_mmc_init_host_props(const struct device *dev) +{ + struct mmc_rcar_data *data = dev->data; + const struct mmc_rcar_cfg *cfg = dev->config; + struct sdhc_host_props *props = &data->props; + struct sdhc_host_caps *host_caps = &props->host_caps; + + memset(props, 0, sizeof(*props)); + + /* Note: init only properties that are used for mmc/sdhc */ + + props->f_max = cfg->max_frequency + MMC_MAX_FREQ_CORRECTION; + /* + * note: actually, it's possible to get lower frequency + * if we use divider from cpg too + */ + props->f_min = (cfg->max_frequency >> 9); + + props->power_delay = 100; /* ms */ + + props->is_spi = 0; + + switch (cfg->bus_width) { + case SDHC_BUS_WIDTH8BIT: + host_caps->bus_8_bit_support = 1; + case SDHC_BUS_WIDTH4BIT: + host_caps->bus_4_bit_support = 1; + default: + break; + } + + host_caps->high_spd_support = 1; +#ifdef CONFIG_RCAR_MMC_SCC_SUPPORT + host_caps->sdr104_support = cfg->mmc_sdr104_support; + host_caps->sdr50_support = cfg->uhs_support; + /* neither Linux nor U-boot support DDR50 mode, that's why we don't support it too */ + host_caps->ddr50_support = 0; + host_caps->hs200_support = cfg->mmc_hs200_1_8v; + /* TODO: add support */ + host_caps->hs400_support = 0; +#endif + + host_caps->vol_330_support = + regulator_is_supported_voltage(cfg->regulator_vqmmc, 3300000, 3300000); + host_caps->vol_300_support = + regulator_is_supported_voltage(cfg->regulator_vqmmc, 3000000, 3000000); + host_caps->vol_180_support = + regulator_is_supported_voltage(cfg->regulator_vqmmc, 1800000, 1800000); +} + +/* reset sampling clock controller registers */ +static int rcar_mmc_disable_scc(const struct device *dev) +{ + int ret; + uint32_t reg; + struct mmc_rcar_data *data = dev->data; + uint32_t mmc_clk_ctl = rcar_mmc_read_reg32(dev, RCAR_MMC_CLKCTL); + + /* just to be to be sure that the SD clock is disabled */ + ret = rcar_mmc_enable_clock(dev, false); + if (ret) { + return ret; + } + + /* + * Reset SCC registers, need to disable and enable clock + * before and after reset + */ + + /* Disable SCC sampling clock */ + reg = rcar_mmc_read_reg32(dev, RENESAS_SDHI_SCC_CKSEL); + reg &= ~RENESAS_SDHI_SCC_CKSEL_DTSEL; + rcar_mmc_write_reg32(dev, RENESAS_SDHI_SCC_CKSEL, reg); + + /* disable hs400 mode & data output timing */ + reg = rcar_mmc_read_reg32(dev, RENESAS_SDHI_SCC_TMPPORT2); + reg &= ~(RENESAS_SDHI_SCC_TMPPORT2_HS400EN | RENESAS_SDHI_SCC_TMPPORT2_HS400OSEL); + rcar_mmc_write_reg32(dev, RENESAS_SDHI_SCC_TMPPORT2, reg); + + ret = rcar_mmc_enable_clock(dev, (mmc_clk_ctl & RCAR_MMC_CLKCTL_OFFEN) ? false : true); + if (ret) { + return ret; + } + + /* disable SCC sampling clock position correction */ + reg = rcar_mmc_read_reg32(dev, RENESAS_SDHI_SCC_RVSCNTL); + reg &= ~RENESAS_SDHI_SCC_RVSCNTL_RVSEN; + rcar_mmc_write_reg32(dev, RENESAS_SDHI_SCC_RVSCNTL, reg); + + data->can_retune = 0; + + return 0; +} + +/* initialize and configure the Renesas MMC controller registers */ +static int rcar_mmc_init_controller_regs(const struct device *dev) +{ + int ret = 0; + uint32_t reg; + struct mmc_rcar_data *data = dev->data; + struct sdhc_io ios = {0}; + + rcar_mmc_reset(dev); + + /* Disable SD clock (SD_CLK) output */ + ret = rcar_mmc_enable_clock(dev, false); + if (ret) { + return ret; + } + + /* set transfer data length to 0 */ + rcar_mmc_write_reg32(dev, RCAR_MMC_SIZE, 0); + + /* disable the SD_BUF read/write DMA transfer */ + reg = rcar_mmc_read_reg32(dev, RCAR_MMC_EXTMODE); + reg &= ~RCAR_MMC_EXTMODE_DMA_EN; + rcar_mmc_write_reg32(dev, RCAR_MMC_EXTMODE, reg); + /* mask DMA irqs and clear dma irq flags */ + rcar_mmc_reset_and_mask_irqs(dev); + /* set system address increment mode selector & 64-bit bus width */ + reg = rcar_mmc_read_reg32(dev, RCAR_MMC_DMA_MODE); + reg |= RCAR_MMC_DMA_MODE_ADDR_INC | RCAR_MMC_DMA_MODE_WIDTH; + rcar_mmc_write_reg32(dev, RCAR_MMC_DMA_MODE, reg); + + /* store version of of introductory IP */ + data->ver = rcar_mmc_read_reg32(dev, RCAR_MMC_VERSION); + data->ver &= RCAR_MMC_VERSION_IP; + + /* + * set bus width to 1 + * timeout counter: SDCLK * 2^27 + * card detect time counter: SDϕ * 2^24 + */ + reg = rcar_mmc_read_reg32(dev, RCAR_MMC_OPTION); + reg |= RCAR_MMC_OPTION_WIDTH_MASK | 0xEE; + rcar_mmc_write_reg32(dev, RCAR_MMC_OPTION, reg); + + /* block count enable */ + rcar_mmc_write_reg32(dev, RCAR_MMC_STOP, RCAR_MMC_STOP_SEC); + /* number of transfer blocks */ + rcar_mmc_write_reg32(dev, RCAR_MMC_SECCNT, 0); + + /* + * SD_BUF0 data swap disabled. + * Read/write access to SD_BUF0 can be performed with the 64-bit access. + * + * Note: when using the DMA, the bus width should be fixed at 64 bits. + */ + rcar_mmc_write_reg32(dev, RCAR_MMC_HOST_MODE, 0); + data->width_access_sd_buf0 = 8; + + /* disable sampling clock controller, it is used for uhs/sdr104, hs200 and hs400 */ + ret = rcar_mmc_disable_scc(dev); + if (ret) { + return ret; + } + + /* + * configure divider inside MMC controller + * set maximum possible divider + */ + ios.clock = data->props.f_min; + rcar_mmc_set_clk_rate(dev, &ios); + + data->restore_cfg_after_reset = 1; + + return 0; +} + +#ifdef CONFIG_RCAR_MMC_DMA_IRQ_DRIVEN_SUPPORT +static void rcar_mmc_irq_handler(const void *arg) +{ + const struct device *dev = arg; + + uint32_t dma_info1 = rcar_mmc_read_reg32(dev, RCAR_MMC_DMA_INFO1); + uint32_t dma_info2 = rcar_mmc_read_reg32(dev, RCAR_MMC_DMA_INFO2); + + if (dma_info1 || dma_info2) { + struct mmc_rcar_data *data = dev->data; + + rcar_mmc_write_reg32(dev, RCAR_MMC_DMA_INFO1_MASK, 0xfffffeff); + rcar_mmc_write_reg32(dev, RCAR_MMC_DMA_INFO2_MASK, ~0); + k_sem_give(&data->irq_xref_fin); + } else { + LOG_WRN("%s: warning: non-dma event triggers irq", dev->name); + } +} +#endif /* CONFIG_RCAR_MMC_DMA_IRQ_DRIVEN_SUPPORT */ + +/* initialize and configure the Renesas MMC driver */ +static int rcar_mmc_init(const struct device *dev) +{ + int ret = 0; + struct mmc_rcar_data *data = dev->data; + const struct mmc_rcar_cfg *cfg = dev->config; + +#ifdef CONFIG_RCAR_MMC_DMA_IRQ_DRIVEN_SUPPORT + ret = k_sem_init(&data->irq_xref_fin, 0, 1); + if (ret) { + LOG_ERR("%s: can't init semaphore", dev->name); + return ret; + } +#endif + + DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE); + + /* Configure dt provided device signals when available */ + ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT); + if (ret < 0) { + LOG_ERR("%s: error can't apply pinctrl state", dev->name); + goto exit_unmap; + } + + if (!device_is_ready(cfg->cpg_dev)) { + LOG_ERR("%s: error cpg_dev isn't ready", dev->name); + ret = -ENODEV; + goto exit_unmap; + } + + ret = rcar_mmc_init_start_clk(cfg); + if (ret < 0) { + LOG_ERR("%s: error can't turn on the cpg", dev->name); + goto exit_unmap; + } + + /* it's needed for SDHC */ + rcar_mmc_init_host_props(dev); + + ret = rcar_mmc_init_controller_regs(dev); + if (ret) { + goto exit_disable_clk; + } + +#ifdef CONFIG_RCAR_MMC_DMA_IRQ_DRIVEN_SUPPORT + cfg->irq_config_func(dev); +#endif /* CONFIG_RCAR_MMC_DMA_IRQ_DRIVEN_SUPPORT */ + + LOG_INF("%s: initialize driver, MMC version 0x%hhx", dev->name, data->ver); + + return 0; + +exit_disable_clk: + clock_control_off(cfg->cpg_dev, (clock_control_subsys_t *)&cfg->cpg_clk); + +exit_unmap: +#if defined(DEVICE_MMIO_IS_IN_RAM) && defined(CONFIG_MMU) + z_phys_unmap((uint8_t *)DEVICE_MMIO_GET(dev), DEVICE_MMIO_ROM_PTR(dev)->size); +#endif + return ret; +} + +#ifdef CONFIG_RCAR_MMC_DMA_IRQ_DRIVEN_SUPPORT +#define RCAR_MMC_CONFIG_FUNC(n) \ + static void irq_config_func_##n(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), rcar_mmc_irq_handler, \ + DEVICE_DT_INST_GET(n), DT_INST_IRQ(n, flags)); \ + irq_enable(DT_INST_IRQN(n)); \ + } +#define RCAR_MMC_IRQ_CFG_FUNC_INIT(n) .irq_config_func = irq_config_func_##n, +#else +#define RCAR_MMC_IRQ_CFG_FUNC_INIT(n) +#define RCAR_MMC_CONFIG_FUNC(n) +#endif + +#define RCAR_MMC_INIT(n) \ + static struct mmc_rcar_data mmc_rcar_data_##n; \ + PINCTRL_DT_INST_DEFINE(n); \ + RCAR_MMC_CONFIG_FUNC(n); \ + static const struct mmc_rcar_cfg mmc_rcar_cfg_##n = { \ + DEVICE_MMIO_ROM_INIT(DT_DRV_INST(n)), \ + .cpg_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \ + .cpg_clk.module = DT_INST_CLOCKS_CELL_BY_IDX(n, 0, module), \ + .cpg_clk.domain = DT_INST_CLOCKS_CELL_BY_IDX(n, 0, domain), \ + .bus_clk.module = DT_INST_CLOCKS_CELL_BY_IDX(n, 1, module), \ + .bus_clk.domain = DT_INST_CLOCKS_CELL_BY_IDX(n, 1, domain), \ + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + .regulator_vqmmc = DEVICE_DT_GET(DT_PHANDLE(DT_DRV_INST(n), vqmmc_supply)), \ + .regulator_vmmc = DEVICE_DT_GET(DT_PHANDLE(DT_DRV_INST(n), vmmc_supply)), \ + .max_frequency = DT_INST_PROP(n, max_bus_freq), \ + .non_removable = DT_INST_PROP(n, non_removable), \ + .mmc_hs200_1_8v = DT_INST_PROP(n, mmc_hs200_1_8v), \ + .mmc_hs400_1_8v = DT_INST_PROP(n, mmc_hs400_1_8v), \ + .mmc_sdr104_support = DT_INST_PROP(n, mmc_sdr104_support), \ + .uhs_support = 1, \ + .bus_width = DT_INST_PROP(n, bus_width), \ + RCAR_MMC_IRQ_CFG_FUNC_INIT(n)}; \ + DEVICE_DT_INST_DEFINE(n, rcar_mmc_init, NULL, &mmc_rcar_data_##n, &mmc_rcar_cfg_##n, \ + POST_KERNEL, CONFIG_SDHC_INIT_PRIORITY, &rcar_sdhc_api); + +DT_INST_FOREACH_STATUS_OKAY(RCAR_MMC_INIT) diff --git a/drivers/sdhc/rcar_mmc_registers.h b/drivers/sdhc/rcar_mmc_registers.h new file mode 100644 index 0000000000..067ca4f112 --- /dev/null +++ b/drivers/sdhc/rcar_mmc_registers.h @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2023 EPAM Systems + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __RCAR_MMC_REGISTERS_H__ +#define __RCAR_MMC_REGISTERS_H__ + +#include /* for BIT macro */ + +/* + * The command type register is used to select the command type + * and response type + */ +#define RCAR_MMC_CMD 0x000 /* command */ +#define RCAR_MMC_CMD_NOSTOP BIT(14) /* No automatic CMD12 issue */ +#define RCAR_MMC_CMD_MULTI BIT(13) /* multiple block transfer */ +#define RCAR_MMC_CMD_RD BIT(12) /* 1: read, 0: write */ +#define RCAR_MMC_CMD_DATA BIT(11) /* data transfer */ +#define RCAR_MMC_CMD_APP BIT(6) /* ACMD preceded by CMD55 */ +#define RCAR_MMC_CMD_NORMAL (0 << 8) /* auto-detect of resp-type */ +#define RCAR_MMC_CMD_RSP_NONE (3 << 8) /* response: none */ +#define RCAR_MMC_CMD_RSP_R1 (4 << 8) /* response: R1, R5, R6, R7 */ +#define RCAR_MMC_CMD_RSP_R1B (5 << 8) /* response: R1b, R5b */ +#define RCAR_MMC_CMD_RSP_R2 (6 << 8) /* response: R2 */ +#define RCAR_MMC_CMD_RSP_R3 (7 << 8) /* response: R3, R4 */ + +/* Command arguments register for SD card */ +#define RCAR_MMC_ARG 0x010 /* command argument */ + +/* + * The data stop register is used to enable or disable block counting at + * multiple block transfer, and to control the issuing of CMD12 within + * command sequences. + */ +#define RCAR_MMC_STOP 0x020 /* stop action control */ +#define RCAR_MMC_STOP_SEC BIT(8) /* use sector count */ +#define RCAR_MMC_STOP_STP BIT(0) /* issue CMD12 */ + +/* + * The block count register is used to specify the number of + * transfer blocks at multiple block transfer. + */ +#define RCAR_MMC_SECCNT 0x028 /* sector counter */ + +/* The SD card response registers hold the response from the SD card */ +#define RCAR_MMC_RSP10 0x030 /* response[39:8] */ +#define RCAR_MMC_RSP32 0x040 /* response[71:40] */ +#define RCAR_MMC_RSP54 0x050 /* response[103:72] */ +#define RCAR_MMC_RSP76 0x060 /* response[127:104] */ + +/* + * The SD card interrupt flag register 1 indicates the response end and access + * end in the command sequence. This register also indicates the card + * detect/write protect state. + */ +#define RCAR_MMC_INFO1 0x070 /* IRQ status 1 */ +#define RCAR_MMC_INFO1_CD BIT(5) /* state of card detect */ +#define RCAR_MMC_INFO1_INSERT BIT(4) /* card inserted */ +#define RCAR_MMC_INFO1_REMOVE BIT(3) /* card removed */ +#define RCAR_MMC_INFO1_CMP BIT(2) /* data complete */ +#define RCAR_MMC_INFO1_RSP BIT(0) /* response complete */ + +/* + * The SD card interrupt flag register 2 indicates the access status of the + * SD buffer and SD card. + */ +#define RCAR_MMC_INFO2 0x078 /* IRQ status 2 */ +#define RCAR_MMC_INFO2_ERR_ILA BIT(15) /* illegal access err */ +#define RCAR_MMC_INFO2_CBSY BIT(14) /* command busy */ +#define RCAR_MMC_INFO2_SCLKDIVEN BIT(13) /* command setting reg ena */ +#define RCAR_MMC_INFO2_CLEAR BIT(11) /* the write value should always be 1 */ +#define RCAR_MMC_INFO2_BWE BIT(9) /* write buffer ready */ +#define RCAR_MMC_INFO2_BRE BIT(8) /* read buffer ready */ +#define RCAR_MMC_INFO2_DAT0 BIT(7) /* SDDAT0 */ +#define RCAR_MMC_INFO2_ERR_RTO BIT(6) /* response time out */ +#define RCAR_MMC_INFO2_ERR_ILR BIT(5) /* illegal read err */ +#define RCAR_MMC_INFO2_ERR_ILW BIT(4) /* illegal write err */ +#define RCAR_MMC_INFO2_ERR_TO BIT(3) /* time out error */ +#define RCAR_MMC_INFO2_ERR_END BIT(2) /* END bit error */ +#define RCAR_MMC_INFO2_ERR_CRC BIT(1) /* CRC error */ +#define RCAR_MMC_INFO2_ERR_IDX BIT(0) /* cmd index error */ + +#define RCAR_MMC_INFO2_ERRORS \ + (RCAR_MMC_INFO2_ERR_RTO | RCAR_MMC_INFO2_ERR_ILR | \ + RCAR_MMC_INFO2_ERR_ILW | RCAR_MMC_INFO2_ERR_TO | \ + RCAR_MMC_INFO2_ERR_END | RCAR_MMC_INFO2_ERR_CRC | \ + RCAR_MMC_INFO2_ERR_IDX | RCAR_MMC_INFO2_ERR_ILA) + +/* + * The interrupt mask 1 register is used to enable or disable + * the RCAR_MMC_INFO1 interrupt. + */ +#define RCAR_MMC_INFO1_MASK 0x080 + +/* + * The interrupt mask 2 register is used to enable or disable + * the RCAR_MMC_INFO2 interrupt. + */ +#define RCAR_MMC_INFO2_MASK 0x088 + +/* + * The SD clock control register is used to control + * the SD clock output and to set the frequency. + */ +#define RCAR_MMC_CLKCTL 0x090 +#define RCAR_MMC_CLKCTL_DIV_MASK 0x104ff +#define RCAR_MMC_CLKCTL_DIV512 BIT(7) /* SDCLK = CLK / 512 */ +#define RCAR_MMC_CLKCTL_DIV256 BIT(6) /* SDCLK = CLK / 256 */ +#define RCAR_MMC_CLKCTL_DIV128 BIT(5) /* SDCLK = CLK / 128 */ +#define RCAR_MMC_CLKCTL_DIV64 BIT(4) /* SDCLK = CLK / 64 */ +#define RCAR_MMC_CLKCTL_DIV32 BIT(3) /* SDCLK = CLK / 32 */ +#define RCAR_MMC_CLKCTL_DIV16 BIT(2) /* SDCLK = CLK / 16 */ +#define RCAR_MMC_CLKCTL_DIV8 BIT(1) /* SDCLK = CLK / 8 */ +#define RCAR_MMC_CLKCTL_DIV4 BIT(0) /* SDCLK = CLK / 4 */ +#define RCAR_MMC_CLKCTL_DIV2 0 /* SDCLK = CLK / 2 */ +#define RCAR_MMC_CLKCTL_RCAR_DIV1 0xff /* SDCLK = CLK (RCar ver.) */ +#define RCAR_MMC_CLKCTL_OFFEN BIT(9) /* stop SDCLK when unused */ +#define RCAR_MMC_CLKCTL_SCLKEN BIT(8) /* SDCLK output enable */ + +/* + * The transfer data length register is used to specify + * the transfer data size. + */ +#define RCAR_MMC_SIZE 0x098 + +/* + * The SD card access control option register is used to set + * the bus width and timeout counter. + */ +#define RCAR_MMC_OPTION 0x0A0 +#define RCAR_MMC_OPTION_WIDTH_MASK (5 << 13) +#define RCAR_MMC_OPTION_WIDTH_1 (4 << 13) +#define RCAR_MMC_OPTION_WIDTH_4 (0 << 13) +#define RCAR_MMC_OPTION_WIDTH_8 (1 << 13) + +/* + * The SD error status register 1 indicates the CRC status, CRC error, + * End error, and CMD error. + */ +#define RCAR_MMC_ERR_STS1 0x0B0 + +/* The SD error status register 2 indicates the timeout state. */ +#define RCAR_MMC_ERR_STS2 0x0B8 + +/* SD Buffer Read/Write Register */ +#define RCAR_MMC_BUF0 0x0C0 + +/* The DMA mode enable register enables the DMA transfer. */ +#define RCAR_MMC_EXTMODE 0x360 +#define RCAR_MMC_EXTMODE_DMA_EN BIT(1) /* transfer 1: DMA, 0: pio */ + +/* The software reset register sets a software reset. */ +#define RCAR_MMC_SOFT_RST 0x380 +#define RCAR_MMC_SOFT_RST_RSTX BIT(0) /* reset deassert */ + +/* The version register indicates the version of the SD host interface. */ +#define RCAR_MMC_VERSION 0x388 +#define RCAR_MMC_VERSION_IP 0xff /* IP version */ + +/* + * The host interface mode setting register selects the width for access to + * the data bus. + */ +#define RCAR_MMC_HOST_MODE 0x390 + +/* The SD interface mode setting register specifies HS400 mode. */ +#define RCAR_MMC_IF_MODE 0x398 +#define RCAR_MMC_IF_MODE_DDR BIT(0) /* DDR mode */ + +/* Set of DMAC registers */ +#define RCAR_MMC_DMA_MODE 0x820 +#define RCAR_MMC_DMA_MODE_DIR_RD BIT(16) /* 1: from device, 0: to dev */ +#define RCAR_MMC_DMA_MODE_WIDTH (BIT(4) | BIT(5)) +#define RCAR_MMC_DMA_MODE_ADDR_INC BIT(0) /* 1: address inc, 0: fixed */ +#define RCAR_MMC_DMA_CTL 0x828 +#define RCAR_MMC_DMA_CTL_START BIT(0) /* start DMA (auto cleared) */ +#define RCAR_MMC_DMA_RST 0x830 +#define RCAR_MMC_DMA_RST_DTRAN0 BIT(8) +#define RCAR_MMC_DMA_RST_DTRAN1 BIT(9) +#define RCAR_MMC_DMA_INFO1 0x840 +#define RCAR_MMC_DMA_INFO1_END_RD2 BIT(20) /* DMA from device is complete (uniphier) */ +#define RCAR_MMC_DMA_INFO1_END_RD BIT(17) /* DMA from device is complete (renesas) */ +#define RCAR_MMC_DMA_INFO1_END_WR BIT(16) /* DMA to device is complete */ +#define RCAR_MMC_DMA_INFO1_MASK 0x848 +#define RCAR_MMC_DMA_INFO2 0x850 +#define RCAR_MMC_DMA_INFO2_ERR_RD BIT(17) +#define RCAR_MMC_DMA_INFO2_ERR_WR BIT(16) +#define RCAR_MMC_DMA_INFO2_MASK 0x858 +#define RCAR_MMC_DMA_ADDR_L 0x880 +#define RCAR_MMC_DMA_ADDR_H 0x888 + +/* set of SCC registers */ + +/* Initial setting register */ +#define RENESAS_SDHI_SCC_DTCNTL 0x1000 +#define RENESAS_SDHI_SCC_DTCNTL_TAPEN BIT(0) +/* Sampling clock position setting register */ +#define RENESAS_SDHI_SCC_TAPSET 0x1008 +#define RENESAS_SDHI_SCC_DT2FF 0x1010 +/* Sampling Clock Selection Register */ +#define RENESAS_SDHI_SCC_CKSEL 0x1018 +#define RENESAS_SDHI_SCC_CKSEL_DTSEL BIT(0) +/* Sampling Clock Position Correction Register */ +#define RENESAS_SDHI_SCC_RVSCNTL 0x1020 +#define RENESAS_SDHI_SCC_RVSCNTL_RVSEN BIT(0) +/* Sampling Clock Position Correction Request Register */ +#define RENESAS_SDHI_SCC_RVSREQ 0x1028 +#define RENESAS_SDHI_SCC_RVSREQ_REQTAPDOWN BIT(0) +#define RENESAS_SDHI_SCC_RVSREQ_REQTAPUP BIT(1) +#define RENESAS_SDHI_SCC_RVSREQ_REQTAP_MASK \ + (RENESAS_SDHI_SCC_RVSREQ_REQTAPDOWN | RENESAS_SDHI_SCC_RVSREQ_REQTAPUP) +#define RENESAS_SDHI_SCC_RVSREQ_ERR BIT(2) +/* Sampling data comparison register */ +#define RENESAS_SDHI_SCC_SMPCMP 0x1030 +/* Hardware Adjustment Register 2, used for configuration HS400 mode */ +#define RENESAS_SDHI_SCC_TMPPORT2 0x1038 +#define RENESAS_SDHI_SCC_TMPPORT2_HS400EN BIT(31) +#define RENESAS_SDHI_SCC_TMPPORT2_HS400OSEL BIT(4) + +#endif /* __RCAR_MMC_REGISTERS_H__ */ diff --git a/dts/arm64/renesas/rcar_gen3_ca57.dtsi b/dts/arm64/renesas/rcar_gen3_ca57.dtsi index 5c87c8accd..d79a557a9c 100644 --- a/dts/arm64/renesas/rcar_gen3_ca57.dtsi +++ b/dts/arm64/renesas/rcar_gen3_ca57.dtsi @@ -28,6 +28,26 @@ ; }; + reg_3p3v: regulator_3p3v { + compatible = "regulator-fixed"; + regulator-name = "reg_3p3v"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + status = "okay"; + }; + + reg_1p8v: regulator_1p8v { + compatible = "regulator-fixed"; + regulator-name = "reg_1p8v"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + status = "okay"; + }; + gic: interrupt-controller@f1010000 { compatible = "arm,gic-400", "arm,gic-v2", "arm,gic" ; #interrupt-cells = <4>; @@ -53,13 +73,36 @@ #reset-cells = <1>; }; + gpio5: gpio@e6055000 { + compatible = "renesas,rcar-gpio"; + reg = <0 0xe6055000 0 0x50>; + #gpio-cells = <2>; + gpio-controller; + interrupt-parent = <&gic>; + interrupts = ; + clocks = <&cpg CPG_MOD 907>; + status = "disabled"; + }; + + sd0: mmc@ee100000 { + compatible = "renesas,rcar-mmc"; + reg = <0 0xee100000 0 0x2000>; + interrupts = ; + clocks = <&cpg CPG_MOD 314>, <&cpg CPG_CORE R8A7795_CLK_SD0H>; + max-bus-freq = <200000000>; + status = "disabled"; + }; + emmc2: mmc@ee140000 { compatible = "renesas,rcar-mmc"; reg = <0 0xee140000 0 0x2000>; + vmmc-supply = <®_3p3v>; + vqmmc-supply = <®_1p8v>; interrupts = ; - clocks = <&cpg CPG_MOD 312>; - max-frequency = <200000000>; + clocks = <&cpg CPG_MOD 312>, <&cpg CPG_CORE R8A7795_CLK_SD2H>; + max-bus-freq = <200000000>; status = "disabled"; }; diff --git a/dts/bindings/mmc/renesas,rcar-emmc.yaml b/dts/bindings/mmc/renesas,rcar-emmc.yaml new file mode 100644 index 0000000000..fe786d2e19 --- /dev/null +++ b/dts/bindings/mmc/renesas,rcar-emmc.yaml @@ -0,0 +1,63 @@ +description: Renesas R-Car eMMC + +compatible: "renesas,rcar-mmc" + +include: [sdhc.yaml, mmc.yaml, pinctrl-device.yaml, reset-device.yaml] + +properties: + clocks: + required: true + + reg: + required: true + + pinctrl-0: + required: true + + pinctrl-names: + required: true + + max-bus-freq: + required: true + + non-removable: + type: boolean + description: | + Non-removable slots (like eMMC), which are assumed to always be present, + will affect the `sdhc_card_present` call. This call will always return + true if this property exists for the node. + + mmc-sdr104-support: + type: boolean + + cd-gpios: + type: phandle-array + description: Card Detect pin + + pwr-gpios: + type: phandle-array + description: Power pin + + vmmc-supply: + type: phandle + description: | + Supply for the card power + + vqmmc-supply: + type: phandle + description: | + Supply for the bus IO line power, such as a level shifter. + If the level shifter is controlled by a GPIO line, this shall + be modeled as a "regulator-fixed" with a GPIO line for + switching the level shifter on/off. + + bus-width: + type: int + default: 1 + description: | + Bus width for SDMMC access, defaults to the minimum necessary + number of bus lines + enum: + - 1 + - 4 + - 8