zephyr/drivers/sdhc/rcar_mmc.c
Mykola Kvach b8f7fc3cc3 drivers: sdhc: add support of Renesas MMC driver
Add basic functionality of Renesas SD/MMC driver. It can be used
for both gen3 and gen4 R-car boards, but tested only with H3ULCB,
Salvator XS M3 and Spider boards. This driver working with SDHC
subsystem.

The driver supports regularal reading/writing throught SD/MMC
controller buffer, DMA mode w/o interrupts and timing tuning.

Add gpio5 and sd0 nodes to h3ulcb and salvator xs which are needed
for working with SD cards. The GPIO node is needed for switching
voltage on SD card through gpio regulator driver.

Notes:
    * the driver doesn't support SPI mode;
    * SCC tuning and DMA mode based on IRQs are enabled by default;
    * an address of a data buffer has to be aligned to 128 bytes if it
      is not, driver will use non-DMA mode automatically;
    * Renesas MMC DMAC doesn't support 64-bit DMA addresses, so for
      case when we have 64-bit xref data address we use non-DMA mode;
    * SD/MMC controller supports block size between 512 and 1 with
      a lot of restrictions, more details you can find in code;
    * support of HS400 mode isn't implemented inside driver.

Signed-off-by: Mykola Kvach <mykola_kvach@epam.com>
2024-05-01 10:55:11 -04:00

2224 lines
64 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2023 EPAM Systems
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT renesas_rcar_mmc
#include <zephyr/devicetree.h>
#include <zephyr/drivers/disk.h>
#include <zephyr/drivers/sdhc.h>
#include <zephyr/drivers/clock_control/renesas_cpg_mssr.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/logging/log.h>
#include <zephyr/cache.h>
#include <zephyr/drivers/regulator.h>
#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 "Users 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)