Add support for all UHS modes of SD3.0. This patch defines the routines to switch volatge, setting uhs modes and execute tuning as these are needed for SD3.0 support
Signed-off-by: Siva Durga Prasad Paladugu <siva...@xilinx.com> --- Changes from v1: - Split the patch with only sdhci changes as per comment --- drivers/mmc/sdhci.c | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++- include/sdhci.h | 8 +++ 2 files changed, 163 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 884b6a6..bba2c30 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -13,6 +13,7 @@ #include <malloc.h> #include <mmc.h> #include <sdhci.h> +#include <wait_bit.h> #if defined(CONFIG_FIXED_SDHCI_ALIGNED_BUFFER) void *aligned_buffer = (void *)CONFIG_FIXED_SDHCI_ALIGNED_BUFFER; @@ -155,7 +156,8 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, /* We shouldn't wait for data inihibit for stop commands, even though they might use busy signaling */ - if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) + if ((cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) || + (cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK)) mask &= ~SDHCI_DATA_INHIBIT; while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) { @@ -175,6 +177,11 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, } mask = SDHCI_INT_RESPONSE; + + /* only buffer read ready interrupt whil tuning */ + if (cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK) + mask = SDHCI_INT_DATA_AVAIL; + if (!(cmd->resp_type & MMC_RSP_PRESENT)) flags = SDHCI_CMD_RESP_NONE; else if (cmd->resp_type & MMC_RSP_136) @@ -190,7 +197,7 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, flags |= SDHCI_CMD_CRC; if (cmd->resp_type & MMC_RSP_OPCODE) flags |= SDHCI_CMD_INDEX; - if (data) + if (data || (cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK)) flags |= SDHCI_CMD_DATA; /* Set Transfer mode regarding to data flag */ @@ -291,6 +298,80 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, else return -ECOMM; } +#ifdef CONFIG_DM_MMC_OPS +static int sdhci_execute_tuning(struct udevice *dev, u8 opcode) +{ + struct mmc *mmc = mmc_get_mmc_dev(dev); +#else +static int sdhci_execute_tuning(struct mmc *mmc, u8 opcode) +{ +#endif + struct mmc_cmd cmd; + struct mmc_data data; + u32 ctrl; + u8 tuning_loop_counter = 40; + struct sdhci_host *host = mmc->priv; + + debug("%s\n", __func__); + + ctrl = sdhci_readw(host, SDHCI_HOST_CTRL2); + ctrl |= SDHCI_CTRL_EXEC_TUNING; + sdhci_writew(host, ctrl, SDHCI_HOST_CTRL2); + + sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_INT_ENABLE); + sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_SIGNAL_ENABLE); + + do { + cmd.cmdidx = opcode; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = 0; + + data.blocksize = 64; + data.blocks = 1; + data.flags = MMC_DATA_READ; + + if (tuning_loop_counter == 0) + break; + + tuning_loop_counter--; + + if (cmd.cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200 && + mmc->bus_width == 8) { + data.blocksize = 128; + } + + sdhci_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, + data.blocksize), + SDHCI_BLOCK_SIZE); + sdhci_writew(host, data.blocks, SDHCI_BLOCK_COUNT); + sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE); + + sdhci_send_command(dev, &cmd, &data); + ctrl = sdhci_readw(host, SDHCI_HOST_CTRL2); + + if (cmd.cmdidx == MMC_CMD_SEND_TUNING_BLOCK) + udelay(1); + + } while (ctrl & SDHCI_CTRL_EXEC_TUNING); + + if (tuning_loop_counter < 0) { + ctrl &= ~SDHCI_CTRL_TUNED_CLK; + sdhci_writel(host, ctrl, SDHCI_HOST_CTRL2); + } + + if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) { + debug("%s:Tuning failed\n", __func__); + return -1; + } + + /* Enable only interrupts served by the SD controller */ + sdhci_writel(host, SDHCI_INT_DATA_MASK | SDHCI_INT_CMD_MASK, + SDHCI_INT_ENABLE); + /* Mask all sdhci interrupt sources */ + sdhci_writel(host, 0x0, SDHCI_SIGNAL_ENABLE); + + return 0; +} static int sdhci_set_clock(struct mmc *mmc, unsigned int clock) { @@ -384,6 +465,72 @@ static int sdhci_set_clock(struct mmc *mmc, unsigned int clock) return 0; } +#ifdef CONFIG_DM_MMC_OPS +static int sdhci_set_voltage(struct udevice *dev) +{ + struct mmc *mmc = mmc_get_mmc_dev(dev); +#else +static int sdhci_set_voltage(struct mmc *mmc) +{ +#endif + struct sdhci_host *host = mmc->priv; + u32 reg; + int err; + + debug("%s\n", __func__); + + reg = (unsigned long)host->ioaddr + SDHCI_PRESENT_STATE; + /* Wait max 20ms for the bits to clear*/ + err = wait_for_bit(__func__, (const u32 *)(uintptr_t)reg, + (SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT), + false, 20, false); + if (err < 0) + return err; + + reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + reg &= ~(SDHCI_CLOCK_CARD_EN | SDHCI_CLOCK_INT_EN); + sdhci_writew(host, reg, SDHCI_CLOCK_CONTROL); + + /* keep clock gated for 5 msec as per spec */ + udelay(5000); + + reg = sdhci_readw(host, SDHCI_HOST_CTRL2); + reg |= SDHCI_18V_SIGNAL; + sdhci_writew(host, reg, SDHCI_HOST_CTRL2); + + sdhci_set_clock(mmc, mmc->cfg->f_min); + + reg = (unsigned long)host->ioaddr + SDHCI_PRESENT_STATE; + /* Wait max 20ms for bits to be clear */ + err = wait_for_bit(__func__, (const u32 *)(uintptr_t)reg, + (SDHCI_CMD_BUSY | SDHCI_DATA_BUSY), + true, 20, false); + if (err < 0) + return err; + + return 0; +} + +#ifdef CONFIG_DM_MMC_OPS +static int sdhci_set_uhs(struct udevice *dev) +{ + struct mmc *mmc = mmc_get_mmc_dev(dev); +#else +static int sdhci_set_uhs(struct mmc *mmc) +{ +#endif + struct sdhci_host *host = mmc->priv; + u32 reg; + + debug("%s\n", __func__); + reg = sdhci_readw(host, SDHCI_HOST_CTRL2); + reg &= ~SDHCI_CTRL2_MODE_MASK; + reg |= mmc->uhsmode; + sdhci_writew(host, reg, SDHCI_HOST_CTRL2); + + return 0; +} + static void sdhci_set_power(struct sdhci_host *host, unsigned short power) { u8 pwr = 0; @@ -505,12 +652,18 @@ int sdhci_probe(struct udevice *dev) const struct dm_mmc_ops sdhci_ops = { .send_cmd = sdhci_send_command, .set_ios = sdhci_set_ios, + .set_voltage = sdhci_set_voltage, + .set_uhs = sdhci_set_uhs, + .execute_tuning = sdhci_execute_tuning, }; #else static const struct mmc_ops sdhci_ops = { .send_cmd = sdhci_send_command, .set_ios = sdhci_set_ios, .init = sdhci_init, + .set_voltage = sdhci_set_voltage, + .set_uhs = sdhci_set_uhs, + .execute_tuning = sdhci_execute_tuning, }; #endif diff --git a/include/sdhci.h b/include/sdhci.h index 685bcf2..fc0708c 100644 --- a/include/sdhci.h +++ b/include/sdhci.h @@ -64,6 +64,8 @@ #define SDHCI_CARD_STATE_STABLE BIT(17) #define SDHCI_CARD_DETECT_PIN_LEVEL BIT(18) #define SDHCI_WRITE_PROTECT BIT(19) +#define SDHCI_DATA_BUSY 0xF00000 +#define SDHCI_CMD_BUSY 0x1000000 #define SDHCI_HOST_CONTROL 0x28 #define SDHCI_CTRL_LED BIT(0) @@ -146,6 +148,12 @@ #define SDHCI_ACMD12_ERR 0x3C /* 3E-3F reserved */ +#define SDHCI_HOST_CTRL2 0x3E +#define SDHCI_CTRL2_MODE_MASK 0x7 + +#define SDHCI_18V_SIGNAL 0x8 +#define SDHCI_CTRL_EXEC_TUNING 0x0040 +#define SDHCI_CTRL_TUNED_CLK 0x80 #define SDHCI_CAPABILITIES 0x40 #define SDHCI_TIMEOUT_CLK_MASK 0x0000003F -- 2.7.4 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot