From mboxrd@z Thu Jan 1 00:00:00 1970 From: Jaehoon Chung Date: Wed, 19 Jul 2017 19:11:25 +0900 Subject: [U-Boot] [PATCH v2 1/1] mmc: Add MMC support for stm32h7 Socs In-Reply-To: <1500449238-17508-1-git-send-email-patrice.chotard@st.com> References: <1500449238-17508-1-git-send-email-patrice.chotard@st.com> Message-ID: <7554f690-c08a-8c0b-4340-b74483131997@samsung.com> List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de Hi, On 07/19/2017 04:27 PM, patrice.chotard at st.com wrote: > From: Patrice Chotard > > This patch adds SD/MMC support for STM32H7 SoCs. > > Here is an extraction of SDMMC main features, embedded in > STM32H7 SoCs. > The SD/MMC block include the following: > _ Full compliance with MultiMediaCard System Specification > Version 4.51. Card support for three different databus modes: > 1-bit (default), 4-bit and 8-bit. > _ Full compatibility with previous versions of MultiMediaCards > (backward compatibility). > _ Full compliance with SD memory card specifications version 4.1. > (SDR104 SDMMC_CK speed limited to maximum allowed IO speed, > SPI mode and UHS-II mode not supported). > _ Full compliance with SDIO card specification version 4.0. > Card support for two different databus modes: 1-bit (default) > and 4-bit. (SDR104 SDMMC_CK speed limited to maximum allowed IO > speed, SPI mode and UHS-II mode not supported). > _ Data transfer up to 208 Mbyte/s for the 8 bit mode. > (depending maximum allowed IO speed). > _ Data and command output enable signals to control external > bidirectional drivers. > > The current version of the SDMMC supports only one SD/SDIO/MMC card > at any one time and a stack of MMC Version 4.51 or previous. > > Signed-off-by: Christophe Kerello > Signed-off-by: Patrice Chotard > --- > > v2: _ add .get_cd() callback support > > drivers/mmc/Kconfig | 8 + > drivers/mmc/Makefile | 1 + > drivers/mmc/stm32_sdmmc2.c | 619 +++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 628 insertions(+) > create mode 100644 drivers/mmc/stm32_sdmmc2.c > > diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig > index 82b8d75..f2e4c26 100644 > --- a/drivers/mmc/Kconfig > +++ b/drivers/mmc/Kconfig > @@ -377,6 +377,14 @@ config GENERIC_ATMEL_MCI > the SD Memory Card Specification V2.0, the SDIO V2.0 specification > and CE-ATA V1.1. > > +config STM32_SDMMC2 Why add the SDMMC2? not SDMMC? Is there a special reason? > + bool "STMicroelectronics STM32H7 SD/MMC Host Controller support" > + depends on DM_MMC && OF_CONTROL && DM_MMC_OPS > + help > + This selects support for the SD/MMC controller on STM32H7 SoCs. > + If you have a board based on such a SoC and with a SD/MMC slot, > + say Y or M here. > + > endif > > config TEGRA124_MMC_DISABLE_EXT_LOOPBACK > diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile > index 2d781c3..2584663 100644 > --- a/drivers/mmc/Makefile > +++ b/drivers/mmc/Makefile > @@ -43,6 +43,7 @@ obj-$(CONFIG_SUPPORT_EMMC_RPMB) += rpmb.o > obj-$(CONFIG_MMC_SANDBOX) += sandbox_mmc.o > obj-$(CONFIG_SH_MMCIF) += sh_mmcif.o > obj-$(CONFIG_SH_SDHI) += sh_sdhi.o > +obj-$(CONFIG_STM32_SDMMC2) += stm32_sdmmc2.o > > # SDHCI > obj-$(CONFIG_MMC_SDHCI) += sdhci.o > diff --git a/drivers/mmc/stm32_sdmmc2.c b/drivers/mmc/stm32_sdmmc2.c > new file mode 100644 > index 0000000..84f96e5 > --- /dev/null > +++ b/drivers/mmc/stm32_sdmmc2.c > @@ -0,0 +1,619 @@ > +/* > + * Copyright (c) 2017 STMicrelectronics > + * > + * SPDX-License-Identifier: GPL-2.0 > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +struct stm32_sdmmc2 { > + u32 power; /* SDMMC power control [0x00] */ > + u32 clkcr; /* SDMMC clock control [0x04] */ > + u32 arg; /* SDMMC argument [0x08] */ > + u32 cmd; /* SDMMC command [0x0C] */ > + u32 respcmd; /* SDMMC command response [0x10] */ > + u32 resp1; /* SDMMC response 1 [0x14] */ > + u32 resp2; /* SDMMC response 2 [0x18] */ > + u32 resp3; /* SDMMC response 3 [0x1C] */ > + u32 resp4; /* SDMMC response 4 [0x20] */ > + u32 dtimer; /* SDMMC data timer [0x24] */ > + u32 dlen; /* SDMMC data length [0x28] */ > + u32 dctrl; /* SDMMC data control [0x2C] */ > + u32 dcount; /* SDMMC data counter [0x30] */ > + u32 sta; /* SDMMC status [0x34] */ > + u32 icr; /* SDMMC interrupt clear [0x38] */ > + u32 mask; /* SDMMC mask [0x3C] */ > + u32 acktime; /* SDMMC Acknowledgment timer [0x40] */ > + u32 reserved0[3]; /* Reserved, 0x44 - 0x4C - 0x4C */ > + u32 idmactrl; /* SDMMC DMA control [0x50] */ > + u32 idmabsize; /* SDMMC DMA buffer size [0x54] */ > + u32 idmabase0; /* SDMMC DMA buffer 0 base address [0x58] */ > + u32 idmabase1; /* SDMMC DMA buffer 1 base address [0x5C] */ > + u32 reserved1[8]; /* Reserved, 0x4C-0x7C */ > + u32 fifo; /* SDMMC data FIFO [0x80] */ > +}; This is for register offset, right? I want to use the defined value..likes "#define SDMMC_POWER_CONTROL 0x00" (It's my preference.) I'm not sure but i have remembered that was difficult to debug. > + > +struct stm32_sdmmc2_host { > + struct stm32_sdmmc2 *base; > + struct mmc_config cfg; > + struct clk clk; > + struct reset_ctl reset_ctl; > + struct gpio_desc cd_gpio; > + u32 clk_reg_add; > + u32 pwr_reg_add; > +}; > + > +struct stm32_sdmmc2_ctx { > + u32 cache_start; > + u32 cache_end; > + u32 data_length; > + bool dpsm_abort; > +}; > + > +/* SDMMC_POWER register */ > +#define SDMMC_POWER_PWRCTRL GENMASK(1, 0) > +#define SDMMC_POWER_VSWITCH BIT(2) > +#define SDMMC_POWER_VSWITCHEN BIT(3) > +#define SDMMC_POWER_DIRPOL BIT(4) > + > +/* SDMMC_CLKCR register */ > +#define SDMMC_CLKCR_CLKDIV GENMASK(9, 0) > +#define SDMMC_CLKCR_CLKDIV_MAX SDMMC_CLKCR_CLKDIV > +#define SDMMC_CLKCR_PWRSAV BIT(12) > +#define SDMMC_CLKCR_WIDBUS_4 BIT(14) > +#define SDMMC_CLKCR_WIDBUS_8 BIT(15) > +#define SDMMC_CLKCR_NEGEDGE BIT(16) > +#define SDMMC_CLKCR_HWFC_EN BIT(17) > +#define SDMMC_CLKCR_DDR BIT(18) > +#define SDMMC_CLKCR_BUSSPEED BIT(19) > +#define SDMMC_CLKCR_SELCLKRX GENMASK(21, 20) > + > +/* SDMMC_CMD register */ > +#define SDMMC_CMD_CMDINDEX GENMASK(5, 0) > +#define SDMMC_CMD_CMDTRANS BIT(6) > +#define SDMMC_CMD_CMDSTOP BIT(7) > +#define SDMMC_CMD_WAITRESP GENMASK(9, 8) > +#define SDMMC_CMD_WAITRESP_0 BIT(8) > +#define SDMMC_CMD_WAITRESP_1 BIT(9) > +#define SDMMC_CMD_WAITINT BIT(10) > +#define SDMMC_CMD_WAITPEND BIT(11) > +#define SDMMC_CMD_CPSMEN BIT(12) > +#define SDMMC_CMD_DTHOLD BIT(13) > +#define SDMMC_CMD_BOOTMODE BIT(14) > +#define SDMMC_CMD_BOOTEN BIT(15) > +#define SDMMC_CMD_CMDSUSPEND BIT(16) > + > +/* SDMMC_DCTRL register */ > +#define SDMMC_DCTRL_DTEN BIT(0) > +#define SDMMC_DCTRL_DTDIR BIT(1) > +#define SDMMC_DCTRL_DTMODE GENMASK(3, 2) > +#define SDMMC_DCTRL_DBLOCKSIZE GENMASK(7, 4) > +#define SDMMC_DCTRL_DBLOCKSIZE_SHIFT 4 > +#define SDMMC_DCTRL_RWSTART BIT(8) > +#define SDMMC_DCTRL_RWSTOP BIT(9) > +#define SDMMC_DCTRL_RWMOD BIT(10) > +#define SDMMC_DCTRL_SDMMCEN BIT(11) > +#define SDMMC_DCTRL_BOOTACKEN BIT(12) > +#define SDMMC_DCTRL_FIFORST BIT(13) > + > +/* SDMMC_STA register */ > +#define SDMMC_STA_CCRCFAIL BIT(0) > +#define SDMMC_STA_DCRCFAIL BIT(1) > +#define SDMMC_STA_CTIMEOUT BIT(2) > +#define SDMMC_STA_DTIMEOUT BIT(3) > +#define SDMMC_STA_TXUNDERR BIT(4) > +#define SDMMC_STA_RXOVERR BIT(5) > +#define SDMMC_STA_CMDREND BIT(6) > +#define SDMMC_STA_CMDSENT BIT(7) > +#define SDMMC_STA_DATAEND BIT(8) > +#define SDMMC_STA_DHOLD BIT(9) > +#define SDMMC_STA_DBCKEND BIT(10) > +#define SDMMC_STA_DABORT BIT(11) > +#define SDMMC_STA_DPSMACT BIT(12) > +#define SDMMC_STA_CPSMACT BIT(13) > +#define SDMMC_STA_TXFIFOHE BIT(14) > +#define SDMMC_STA_RXFIFOHF BIT(15) > +#define SDMMC_STA_TXFIFOF BIT(16) > +#define SDMMC_STA_RXFIFOF BIT(17) > +#define SDMMC_STA_TXFIFOE BIT(18) > +#define SDMMC_STA_RXFIFOE BIT(19) > +#define SDMMC_STA_BUSYD0 BIT(20) > +#define SDMMC_STA_BUSYD0END BIT(21) > +#define SDMMC_STA_SDMMCIT BIT(22) > +#define SDMMC_STA_ACKFAIL BIT(23) > +#define SDMMC_STA_ACKTIMEOUT BIT(24) > +#define SDMMC_STA_VSWEND BIT(25) > +#define SDMMC_STA_CKSTOP BIT(26) > +#define SDMMC_STA_IDMATE BIT(27) > +#define SDMMC_STA_IDMABTC BIT(28) > + > +/* SDMMC_ICR register */ > +#define SDMMC_ICR_CCRCFAILC BIT(0) > +#define SDMMC_ICR_DCRCFAILC BIT(1) > +#define SDMMC_ICR_CTIMEOUTC BIT(2) > +#define SDMMC_ICR_DTIMEOUTC BIT(3) > +#define SDMMC_ICR_TXUNDERRC BIT(4) > +#define SDMMC_ICR_RXOVERRC BIT(5) > +#define SDMMC_ICR_CMDRENDC BIT(6) > +#define SDMMC_ICR_CMDSENTC BIT(7) > +#define SDMMC_ICR_DATAENDC BIT(8) > +#define SDMMC_ICR_DHOLDC BIT(9) > +#define SDMMC_ICR_DBCKENDC BIT(10) > +#define SDMMC_ICR_DABORTC BIT(11) > +#define SDMMC_ICR_BUSYD0ENDC BIT(21) > +#define SDMMC_ICR_SDMMCITC BIT(22) > +#define SDMMC_ICR_ACKFAILC BIT(23) > +#define SDMMC_ICR_ACKTIMEOUTC BIT(24) > +#define SDMMC_ICR_VSWENDC BIT(25) > +#define SDMMC_ICR_CKSTOPC BIT(26) > +#define SDMMC_ICR_IDMATEC BIT(27) > +#define SDMMC_ICR_IDMABTCC BIT(28) > +#define SDMMC_ICR_STATIC_FLAGS ((GENMASK(28, 21)) | (GENMASK(11, 0))) > + > +/* SDMMC_MASK register */ > +#define SDMMC_MASK_CCRCFAILIE BIT(0) > +#define SDMMC_MASK_DCRCFAILIE BIT(1) > +#define SDMMC_MASK_CTIMEOUTIE BIT(2) > +#define SDMMC_MASK_DTIMEOUTIE BIT(3) > +#define SDMMC_MASK_TXUNDERRIE BIT(4) > +#define SDMMC_MASK_RXOVERRIE BIT(5) > +#define SDMMC_MASK_CMDRENDIE BIT(6) > +#define SDMMC_MASK_CMDSENTIE BIT(7) > +#define SDMMC_MASK_DATAENDIE BIT(8) > +#define SDMMC_MASK_DHOLDIE BIT(9) > +#define SDMMC_MASK_DBCKENDIE BIT(10) > +#define SDMMC_MASK_DABORTIE BIT(11) > +#define SDMMC_MASK_TXFIFOHEIE BIT(14) > +#define SDMMC_MASK_RXFIFOHFIE BIT(15) > +#define SDMMC_MASK_RXFIFOFIE BIT(17) > +#define SDMMC_MASK_TXFIFOEIE BIT(18) > +#define SDMMC_MASK_BUSYD0ENDIE BIT(21) > +#define SDMMC_MASK_SDMMCITIE BIT(22) > +#define SDMMC_MASK_ACKFAILIE BIT(23) > +#define SDMMC_MASK_ACKTIMEOUTIE BIT(24) > +#define SDMMC_MASK_VSWENDIE BIT(25) > +#define SDMMC_MASK_CKSTOPIE BIT(26) > +#define SDMMC_MASK_IDMABTCIE BIT(28) > + > +/* SDMMC_IDMACTRL register */ > +#define SDMMC_IDMACTRL_IDMAEN BIT(0) > + > +#define SDMMC_CMD_TIMEOUT 0xFFFFFFFF > + > +DECLARE_GLOBAL_DATA_PTR; > + > +static void stm32_sdmmc2_start_data(struct mmc *mmc, struct mmc_data *data, > + struct stm32_sdmmc2_ctx *ctx) > +{ > + struct stm32_sdmmc2_host *host = mmc->priv; > + struct stm32_sdmmc2 *sdmmc_handler = host->base; > + u32 data_ctrl, idmabase0; > + > + /* Configure the SDMMC DPSM (Data Path State Machine) */ > + data_ctrl = (__ilog2(data->blocksize) << > + SDMMC_DCTRL_DBLOCKSIZE_SHIFT) & > + SDMMC_DCTRL_DBLOCKSIZE; > + > + if (data->flags & MMC_DATA_READ) { > + data_ctrl |= SDMMC_DCTRL_DTDIR; > + idmabase0 = (u32)data->dest; > + } else { > + idmabase0 = (u32)data->src; > + } > + > + /* Set the SDMMC Data TimeOut value */ > + writel(SDMMC_CMD_TIMEOUT, &sdmmc_handler->dtimer); > + > + /* Set the SDMMC DataLength value */ > + writel(ctx->data_length, &sdmmc_handler->dlen); > + > + /* Write to SDMMC DCTRL */ > + writel(data_ctrl, &sdmmc_handler->dctrl); > + > + /* Cache align */ > + ctx->cache_start = rounddown(idmabase0, ARCH_DMA_MINALIGN); > + ctx->cache_end = roundup(idmabase0 + ctx->data_length, > + ARCH_DMA_MINALIGN); > + > + /* > + * Flush data cache before DMA start (clean and invalidate) > + * Clean also needed for read > + * Avoid issue on buffer not cached-aligned > + */ > + flush_dcache_range(ctx->cache_start, ctx->cache_end); > + > + /* Enable internal DMA */ > + writel(idmabase0, &sdmmc_handler->idmabase0); > + writel(SDMMC_IDMACTRL_IDMAEN, &sdmmc_handler->idmactrl); > +} > + > +static void stm32_sdmmc2_start_cmd(struct mmc *mmc, struct mmc_cmd *cmd, u32 c) > +{ > + struct stm32_sdmmc2_host *host = mmc->priv; > + struct stm32_sdmmc2 *sdmmc_handler = host->base; > + > + if (readl(&sdmmc_handler->arg) & SDMMC_CMD_CPSMEN) > + writel(0, &sdmmc_handler->arg); > + > + c |= cmd->cmdidx | SDMMC_CMD_CPSMEN; > + if (cmd->resp_type & MMC_RSP_PRESENT) { > + if (cmd->resp_type & MMC_RSP_136) > + c |= SDMMC_CMD_WAITRESP; > + else if (cmd->resp_type & MMC_RSP_CRC) > + c |= SDMMC_CMD_WAITRESP_0; > + else > + c |= SDMMC_CMD_WAITRESP_1; > + } > + > + /* Clear flags */ > + writel(SDMMC_ICR_STATIC_FLAGS, &sdmmc_handler->icr); > + > + /* Set SDMMC argument value */ > + writel(cmd->cmdarg, &sdmmc_handler->arg); > + > + /* Set SDMMC command parameters */ > + writel(c, &sdmmc_handler->cmd); > +} > + > +static int stm32_sdmmc2_end_cmd(struct mmc *mmc, struct mmc_cmd *cmd, > + struct stm32_sdmmc2_ctx *ctx) > +{ > + struct stm32_sdmmc2_host *host = mmc->priv; > + struct stm32_sdmmc2 *sdmmc_handler = host->base; > + u32 mask = SDMMC_STA_CTIMEOUT; > + u32 status; > + > + if (cmd->resp_type & MMC_RSP_PRESENT) { > + mask |= SDMMC_STA_CMDREND; > + if (cmd->resp_type & MMC_RSP_CRC) > + mask |= SDMMC_STA_CCRCFAIL; > + } else { > + mask |= SDMMC_STA_CMDSENT; > + } > + > + /* Polling status register */ > + status = readl(&sdmmc_handler->sta); > + while (!(status & mask)) > + status = readl(&sdmmc_handler->sta); > + > + /* Check status */ > + if (status & SDMMC_STA_CTIMEOUT) { > + debug("%s: error SDMMC_STA_CTIMEOUT (0x%x) for cmd %d\n", > + __func__, status, cmd->cmdidx); > + ctx->dpsm_abort = true; > + return -ETIMEDOUT; > + } > + > + if (status & SDMMC_STA_CCRCFAIL && cmd->resp_type & MMC_RSP_CRC) { > + debug("%s: error SDMMC_STA_CCRCFAIL (0x%x) for cmd %d\n", > + __func__, status, cmd->cmdidx); > + ctx->dpsm_abort = true; > + return -EILSEQ; > + } > + > + if (status & SDMMC_STA_CMDREND && cmd->resp_type & MMC_RSP_PRESENT) { > + cmd->response[0] = readl(&sdmmc_handler->resp1); > + if (cmd->resp_type & MMC_RSP_136) { > + cmd->response[1] = readl(&sdmmc_handler->resp2); > + cmd->response[2] = readl(&sdmmc_handler->resp3); > + cmd->response[3] = readl(&sdmmc_handler->resp4); > + } > + } > + > + return 0; > +} > + > +static int stm32_sdmmc2_end_data(struct mmc *mmc, struct mmc_cmd *cmd, > + struct mmc_data *data, > + struct stm32_sdmmc2_ctx *ctx) > +{ > + struct stm32_sdmmc2_host *host = mmc->priv; > + struct stm32_sdmmc2 *sdmmc_handler = host->base; > + u32 mask = SDMMC_STA_DCRCFAIL | SDMMC_STA_DTIMEOUT | > + SDMMC_STA_IDMATE | SDMMC_STA_DATAEND; > + u32 status; > + > + if (data->flags & MMC_DATA_READ) > + mask |= SDMMC_STA_RXOVERR; > + else > + mask |= SDMMC_STA_TXUNDERR; > + > + status = readl(&sdmmc_handler->sta); > + while (!(status & mask)) > + status = readl(&sdmmc_handler->sta); > + > + /* > + * Need invalidate the dcache again to avoid any > + * cache-refill during the DMA operations (pre-fetching) > + */ > + if (data->flags & MMC_DATA_READ) > + invalidate_dcache_range(ctx->cache_start, ctx->cache_end); > + > + if (status & SDMMC_STA_DCRCFAIL) { > + debug("%s: error SDMMC_STA_DCRCFAIL (0x%x) for cmd %d\n", > + __func__, status, cmd->cmdidx); > + if (readl(&sdmmc_handler->dcount)) > + ctx->dpsm_abort = true; > + return -EILSEQ; > + } > + > + if (status & SDMMC_STA_DTIMEOUT) { > + debug("%s: error SDMMC_STA_DTIMEOUT (0x%x) for cmd %d\n", > + __func__, status, cmd->cmdidx); > + ctx->dpsm_abort = true; > + return -ETIMEDOUT; > + } > + > + if (status & SDMMC_STA_TXUNDERR) { > + debug("%s: error SDMMC_STA_TXUNDERR (0x%x) for cmd %d\n", > + __func__, status, cmd->cmdidx); > + ctx->dpsm_abort = true; > + return -EIO; > + } > + > + if (status & SDMMC_STA_RXOVERR) { > + debug("%s: error SDMMC_STA_RXOVERR (0x%x) for cmd %d\n", > + __func__, status, cmd->cmdidx); > + ctx->dpsm_abort = true; > + return -EIO; > + } > + > + if (status & SDMMC_STA_IDMATE) { > + debug("%s: error SDMMC_STA_IDMATE (0x%x) for cmd %d\n", > + __func__, status, cmd->cmdidx); > + ctx->dpsm_abort = true; > + return -EIO; > + } > + > + return 0; > +} > + > +static int stm32_sdmmc2_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, > + struct mmc_data *data) > +{ > + struct mmc *mmc = mmc_get_mmc_dev(dev); > + struct stm32_sdmmc2_host *host = mmc->priv; > + struct stm32_sdmmc2 *sdmmc_handler = host->base; > + struct stm32_sdmmc2_ctx ctx; > + u32 cmdat = data ? SDMMC_CMD_CMDTRANS : 0; > + int ret, retry = 3; > + > +retry_cmd: > + ctx.data_length = 0; > + ctx.dpsm_abort = false; > + > + if (data) { > + ctx.data_length = data->blocks * data->blocksize; > + stm32_sdmmc2_start_data(mmc, data, &ctx); > + } > + > + stm32_sdmmc2_start_cmd(mmc, cmd, cmdat); > + > + debug("%s: send cmd %d data: 0x%x @ 0x%x\n", > + __func__, cmd->cmdidx, > + data ? ctx.data_length : 0, (unsigned int)data); > + > + ret = stm32_sdmmc2_end_cmd(mmc, cmd, &ctx); > + > + if (data && !ret) > + ret = stm32_sdmmc2_end_data(mmc, cmd, data, &ctx); > + > + /* Clear flags */ > + writel(SDMMC_ICR_STATIC_FLAGS, &sdmmc_handler->icr); > + if (data) > + writel(0x0, &sdmmc_handler->idmactrl); > + > + /* > + * To stop Data Path State Machine, a stop_transmission command > + * shall be send on cmd or data errors. > + */ > + if (ctx.dpsm_abort && (cmd->cmdidx != MMC_CMD_STOP_TRANSMISSION)) { > + struct mmc_cmd stop_cmd; > + > + stop_cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; > + stop_cmd.cmdarg = 0; > + stop_cmd.resp_type = MMC_RSP_R1b; > + > + debug("%s: send STOP command to abort dpsm treatments\n", > + __func__); > + > + stm32_sdmmc2_start_cmd(mmc, &stop_cmd, SDMMC_CMD_CMDSTOP); > + stm32_sdmmc2_end_cmd(mmc, &stop_cmd, &ctx); > + > + writel(SDMMC_ICR_STATIC_FLAGS, &sdmmc_handler->icr); > + } > + > + if ((ret != -ETIMEDOUT) && (ret != 0) && retry) { > + printf("%s: cmd %d failed, retrying ...\n", > + __func__, cmd->cmdidx); > + retry--; > + goto retry_cmd; > + } > + > + debug("%s: end for CMD %d, ret = %d\n", __func__, cmd->cmdidx, ret); > + > + return ret; > +} > + > +static void stm32_sdmmc2_pwron(struct stm32_sdmmc2_host *host) > +{ > + struct stm32_sdmmc2 *sdmmc_handler = host->base; > + > + /* Reset */ > + reset_assert(&host->reset_ctl); > + udelay(2); > + reset_deassert(&host->reset_ctl); > + > + udelay(1000); > + > + /* Set Power State to ON */ > + writel(SDMMC_POWER_PWRCTRL | host->pwr_reg_add, &sdmmc_handler->power); > + > + /* > + * 1ms: required power up waiting time before starting the > + * SD initialization sequence > + */ > + udelay(1000); > +} > + > +#define IS_RISING_EDGE(reg) (reg & SDMMC_CLKCR_NEGEDGE ? 0 : 1) > +static int stm32_sdmmc2_set_ios(struct udevice *dev) > +{ > + struct mmc *mmc = mmc_get_mmc_dev(dev); > + struct stm32_sdmmc2_host *host = mmc->priv; > + struct stm32_sdmmc2 *sdmmc_handler = host->base; > + struct mmc_config *cfg = &host->cfg; > + u32 desired = mmc->clock; > + u32 sys_clock = clk_get_rate(&host->clk); > + u32 clk = 0; > + > + debug("%s: bus_with = %d, clock = %d\n", __func__, > + mmc->bus_width, mmc->clock); > + > + if ((mmc->bus_width == 1) && (desired == cfg->f_min)) > + stm32_sdmmc2_pwron(host); > + > + /* > + * clk_div = 0 => command and data generated on SDMMCCLK falling edge > + * clk_div > 0 and NEGEDGE = 0 => command and data generated on > + * SDMMCCLK rising edge > + * clk_div > 0 and NEGEDGE = 1 => command and data generated on > + * SDMMCCLK falling edge > + */ > + if (desired && ((sys_clock > desired) || > + IS_RISING_EDGE(host->clk_reg_add))) { > + clk = DIV_ROUND_UP(sys_clock, 2 * desired); > + if (clk > SDMMC_CLKCR_CLKDIV_MAX) > + clk = SDMMC_CLKCR_CLKDIV_MAX; > + } > + > + if (mmc->bus_width == 4) > + clk |= SDMMC_CLKCR_WIDBUS_4; > + if (mmc->bus_width == 8) > + clk |= SDMMC_CLKCR_WIDBUS_8; > + > + writel(clk | host->clk_reg_add, &sdmmc_handler->clkcr); > + > + return 0; > +} > + > +static int stm32_sdmmc2_getcd(struct udevice *dev) > +{ > + struct stm32_sdmmc2_host *host = dev_get_priv(dev); > + > + debug("stm32_sdmmc2_getcd called\n"); > + > + if (dm_gpio_is_valid(&host->cd_gpio)) > + return dm_gpio_get_value(&host->cd_gpio); > + > + return 1; > +} > + > +static const struct dm_mmc_ops stm32_sdmmc2_ops = { > + .send_cmd = stm32_sdmmc2_send_cmd, > + .set_ios = stm32_sdmmc2_set_ios, > + .get_cd = stm32_sdmmc2_getcd, > +}; > + > +static int stm32_sdmmc2_probe(struct udevice *dev) > +{ > + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); > + struct stm32_sdmmc2_host *host = dev_get_priv(dev); > + struct mmc_config *cfg = &host->cfg; > + struct mmc *mmc; > + fdt_addr_t addr; > + int ret; > + > + addr = devfdt_get_addr(dev); > + if (addr == FDT_ADDR_T_NONE) > + return -EINVAL; > + > + host->base = (struct stm32_sdmmc2 *)addr; > + > + if (dev_read_bool(dev, "st,negedge")) > + host->clk_reg_add |= SDMMC_CLKCR_NEGEDGE; why did "or" with host->clk_reg_add? > + if (dev_read_bool(dev, "st,dirpol")) > + host->pwr_reg_add |= SDMMC_POWER_DIRPOL; ditto > + > + ret = clk_get_by_index(dev, 0, &host->clk); > + if (ret) > + return ret; > + > + ret = clk_enable(&host->clk); > + if (ret) > + goto clk_free; > + > + ret = reset_get_by_index(dev, 0, &host->reset_ctl); > + if (ret) > + goto clk_disable; > + > + gpio_request_by_name(dev, "cd-gpios", 0, &host->cd_gpio, > + GPIOD_IS_IN); > + > + cfg->f_min = 400000; > + cfg->f_max = dev_read_u32_default(dev, "max-frequency", 52000000); > + cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; > + cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; > + cfg->name = "STM32 SD/MMCv2"; > + > + cfg->host_caps = 0; > + if (cfg->f_max > 25000000) > + cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; > + > + switch (dev_read_u32_default(dev, "bus-width", 1)) { > + case 8: > + cfg->host_caps |= MMC_MODE_8BIT; > + case 4: > + cfg->host_caps |= MMC_MODE_4BIT; > + break; > + case 1: > + break; > + default: > + printf("%s: invalid \"bus-width\" property\n", __func__); > + ret = -ENOENT; > + goto reset_free; Maybe default value can be 1.. > + } > + > + mmc = mmc_create(cfg, host); > + if (!mmc) { > + ret = -ENOMEM; > + goto reset_free; > + } > + > + mmc->block_dev.removable = !dev_read_bool(dev, "non-removable"); > + mmc->dev = dev; > + upriv->mmc = mmc; > + > + return 0; > + > +reset_free: > + reset_free(&host->reset_ctl); > +clk_disable: > + clk_disable(&host->clk); > +clk_free: > + clk_free(&host->clk); > + > + return ret; > +} > + > +static const struct udevice_id stm32_sdmmc2_ids[] = { > + { .compatible = "st,stm32-sdmmc2" }, > + { } > +}; > + > +U_BOOT_DRIVER(stm32_sdmmc2) = { > + .name = "stm32_sdmmc2", > + .id = UCLASS_MMC, > + .of_match = stm32_sdmmc2_ids, > + .ops = &stm32_sdmmc2_ops, > + .probe = stm32_sdmmc2_probe, > + .priv_auto_alloc_size = sizeof(struct stm32_sdmmc2_host), > +}; >