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