u-boot/drivers/mmc/tegra_mmc.c
Peter Geis 34aeb386af mmc: tegra: use max-frequency from device tree if provided
The driver currently hard-codes the max freqency for the sdhci
controllers. If the controller is unable to operate at the max
frequency, the mmc card will not be available on the first scan.
Subsequent scans will eventually find a working combination.

Fix this by allowing the driver to check for the max-frequency
property and default to the original value if it doesn't exist.

Signed-off-by: Peter Geis <pgwipeout@gmail.com>
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
2023-12-19 21:24:12 +02:00

778 lines
20 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2009 SAMSUNG Electronics
* Minkyu Kang <mk7.kang@samsung.com>
* Jaehoon Chung <jh80.chung@samsung.com>
* Portions Copyright 2011-2019 NVIDIA Corporation
*/
#include <bouncebuf.h>
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <log.h>
#include <mmc.h>
#include <asm/gpio.h>
#include <asm/io.h>
#include <asm/arch-tegra/tegra_mmc.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/err.h>
#if defined(CONFIG_TEGRA30) || defined(CONFIG_TEGRA210)
#include <asm/arch/clock.h>
#endif
struct tegra_mmc_plat {
struct mmc_config cfg;
struct mmc mmc;
};
struct tegra_mmc_priv {
struct tegra_mmc *reg;
struct reset_ctl reset_ctl;
struct clk clk;
struct gpio_desc cd_gpio; /* Change Detect GPIO */
struct gpio_desc pwr_gpio; /* Power GPIO */
struct gpio_desc wp_gpio; /* Write Protect GPIO */
unsigned int version; /* SDHCI spec. version */
unsigned int clock; /* Current clock (MHz) */
int mmc_id; /* peripheral id */
int tap_value;
int trim_value;
};
static void tegra_mmc_set_power(struct tegra_mmc_priv *priv,
unsigned short power)
{
u8 pwr = 0;
debug("%s: power = %x\n", __func__, power);
if (power != (unsigned short)-1) {
switch (1 << power) {
case MMC_VDD_165_195:
pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V1_8;
break;
case MMC_VDD_29_30:
case MMC_VDD_30_31:
pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_0;
break;
case MMC_VDD_32_33:
case MMC_VDD_33_34:
pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_3;
break;
}
}
debug("%s: pwr = %X\n", __func__, pwr);
/* Set the bus voltage first (if any) */
writeb(pwr, &priv->reg->pwrcon);
if (pwr == 0)
return;
/* Now enable bus power */
pwr |= TEGRA_MMC_PWRCTL_SD_BUS_POWER;
writeb(pwr, &priv->reg->pwrcon);
}
static void tegra_mmc_prepare_data(struct tegra_mmc_priv *priv,
struct mmc_data *data,
struct bounce_buffer *bbstate)
{
unsigned char ctrl;
debug("buf: %p (%p), data->blocks: %u, data->blocksize: %u\n",
bbstate->bounce_buffer, bbstate->user_buffer, data->blocks,
data->blocksize);
writel((u32)(unsigned long)bbstate->bounce_buffer, &priv->reg->sysad);
/*
* DMASEL[4:3]
* 00 = Selects SDMA
* 01 = Reserved
* 10 = Selects 32-bit Address ADMA2
* 11 = Selects 64-bit Address ADMA2
*/
ctrl = readb(&priv->reg->hostctl);
ctrl &= ~TEGRA_MMC_HOSTCTL_DMASEL_MASK;
ctrl |= TEGRA_MMC_HOSTCTL_DMASEL_SDMA;
writeb(ctrl, &priv->reg->hostctl);
/* We do not handle DMA boundaries, so set it to max (512 KiB) */
writew((7 << 12) | (data->blocksize & 0xFFF), &priv->reg->blksize);
writew(data->blocks, &priv->reg->blkcnt);
}
static void tegra_mmc_set_transfer_mode(struct tegra_mmc_priv *priv,
struct mmc_data *data)
{
unsigned short mode;
debug(" mmc_set_transfer_mode called\n");
/*
* TRNMOD
* MUL1SIN0[5] : Multi/Single Block Select
* RD1WT0[4] : Data Transfer Direction Select
* 1 = read
* 0 = write
* ENACMD12[2] : Auto CMD12 Enable
* ENBLKCNT[1] : Block Count Enable
* ENDMA[0] : DMA Enable
*/
mode = (TEGRA_MMC_TRNMOD_DMA_ENABLE |
TEGRA_MMC_TRNMOD_BLOCK_COUNT_ENABLE);
if (data->blocks > 1)
mode |= TEGRA_MMC_TRNMOD_MULTI_BLOCK_SELECT;
if (data->flags & MMC_DATA_READ)
mode |= TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ;
writew(mode, &priv->reg->trnmod);
}
static int tegra_mmc_wait_inhibit(struct tegra_mmc_priv *priv,
struct mmc_cmd *cmd,
struct mmc_data *data,
unsigned int timeout)
{
/*
* PRNSTS
* CMDINHDAT[1] : Command Inhibit (DAT)
* CMDINHCMD[0] : Command Inhibit (CMD)
*/
unsigned int mask = TEGRA_MMC_PRNSTS_CMD_INHIBIT_CMD;
/*
* We shouldn't wait for data inhibit for stop commands, even
* though they might use busy signaling
*/
if ((data == NULL) && (cmd->resp_type & MMC_RSP_BUSY))
mask |= TEGRA_MMC_PRNSTS_CMD_INHIBIT_DAT;
while (readl(&priv->reg->prnsts) & mask) {
if (timeout == 0) {
printf("%s: timeout error\n", __func__);
return -1;
}
timeout--;
udelay(1000);
}
return 0;
}
static int tegra_mmc_send_cmd_bounced(struct udevice *dev, struct mmc_cmd *cmd,
struct mmc_data *data,
struct bounce_buffer *bbstate)
{
struct tegra_mmc_priv *priv = dev_get_priv(dev);
int flags, i;
int result;
unsigned int mask = 0;
unsigned int retry = 0x100000;
debug(" mmc_send_cmd called\n");
result = tegra_mmc_wait_inhibit(priv, cmd, data, 10 /* ms */);
if (result < 0)
return result;
if (data)
tegra_mmc_prepare_data(priv, data, bbstate);
debug("cmd->arg: %08x\n", cmd->cmdarg);
writel(cmd->cmdarg, &priv->reg->argument);
if (data)
tegra_mmc_set_transfer_mode(priv, data);
if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY))
return -1;
/*
* CMDREG
* CMDIDX[13:8] : Command index
* DATAPRNT[5] : Data Present Select
* ENCMDIDX[4] : Command Index Check Enable
* ENCMDCRC[3] : Command CRC Check Enable
* RSPTYP[1:0]
* 00 = No Response
* 01 = Length 136
* 10 = Length 48
* 11 = Length 48 Check busy after response
*/
if (!(cmd->resp_type & MMC_RSP_PRESENT))
flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_NO_RESPONSE;
else if (cmd->resp_type & MMC_RSP_136)
flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_136;
else if (cmd->resp_type & MMC_RSP_BUSY)
flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48_BUSY;
else
flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48;
if (cmd->resp_type & MMC_RSP_CRC)
flags |= TEGRA_MMC_TRNMOD_CMD_CRC_CHECK;
if (cmd->resp_type & MMC_RSP_OPCODE)
flags |= TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK;
if (data)
flags |= TEGRA_MMC_TRNMOD_DATA_PRESENT_SELECT_DATA_TRANSFER;
debug("cmd: %d\n", cmd->cmdidx);
writew((cmd->cmdidx << 8) | flags, &priv->reg->cmdreg);
for (i = 0; i < retry; i++) {
mask = readl(&priv->reg->norintsts);
/* Command Complete */
if (mask & TEGRA_MMC_NORINTSTS_CMD_COMPLETE) {
if (!data)
writel(mask, &priv->reg->norintsts);
break;
}
}
if (i == retry) {
printf("%s: waiting for status update\n", __func__);
writel(mask, &priv->reg->norintsts);
return -ETIMEDOUT;
}
if (mask & TEGRA_MMC_NORINTSTS_CMD_TIMEOUT) {
/* Timeout Error */
debug("timeout: %08x cmd %d\n", mask, cmd->cmdidx);
writel(mask, &priv->reg->norintsts);
return -ETIMEDOUT;
} else if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) {
/* Error Interrupt */
debug("error: %08x cmd %d\n", mask, cmd->cmdidx);
writel(mask, &priv->reg->norintsts);
return -1;
}
if (cmd->resp_type & MMC_RSP_PRESENT) {
if (cmd->resp_type & MMC_RSP_136) {
/* CRC is stripped so we need to do some shifting. */
for (i = 0; i < 4; i++) {
unsigned long offset = (unsigned long)
(&priv->reg->rspreg3 - i);
cmd->response[i] = readl(offset) << 8;
if (i != 3) {
cmd->response[i] |=
readb(offset - 1);
}
debug("cmd->resp[%d]: %08x\n",
i, cmd->response[i]);
}
} else if (cmd->resp_type & MMC_RSP_BUSY) {
for (i = 0; i < retry; i++) {
/* PRNTDATA[23:20] : DAT[3:0] Line Signal */
if (readl(&priv->reg->prnsts)
& (1 << 20)) /* DAT[0] */
break;
}
if (i == retry) {
printf("%s: card is still busy\n", __func__);
writel(mask, &priv->reg->norintsts);
return -ETIMEDOUT;
}
cmd->response[0] = readl(&priv->reg->rspreg0);
debug("cmd->resp[0]: %08x\n", cmd->response[0]);
} else {
cmd->response[0] = readl(&priv->reg->rspreg0);
debug("cmd->resp[0]: %08x\n", cmd->response[0]);
}
}
if (data) {
unsigned long start = get_timer(0);
while (1) {
mask = readl(&priv->reg->norintsts);
if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) {
/* Error Interrupt */
writel(mask, &priv->reg->norintsts);
printf("%s: error during transfer: 0x%08x\n",
__func__, mask);
return -1;
} else if (mask & TEGRA_MMC_NORINTSTS_DMA_INTERRUPT) {
/*
* DMA Interrupt, restart the transfer where
* it was interrupted.
*/
unsigned int address = readl(&priv->reg->sysad);
debug("DMA end\n");
writel(TEGRA_MMC_NORINTSTS_DMA_INTERRUPT,
&priv->reg->norintsts);
writel(address, &priv->reg->sysad);
} else if (mask & TEGRA_MMC_NORINTSTS_XFER_COMPLETE) {
/* Transfer Complete */
debug("r/w is done\n");
break;
} else if (get_timer(start) > 8000UL) {
writel(mask, &priv->reg->norintsts);
printf("%s: MMC Timeout\n"
" Interrupt status 0x%08x\n"
" Interrupt status enable 0x%08x\n"
" Interrupt signal enable 0x%08x\n"
" Present status 0x%08x\n",
__func__, mask,
readl(&priv->reg->norintstsen),
readl(&priv->reg->norintsigen),
readl(&priv->reg->prnsts));
return -1;
}
}
writel(mask, &priv->reg->norintsts);
}
udelay(1000);
return 0;
}
static int tegra_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
struct mmc_data *data)
{
void *buf;
unsigned int bbflags;
size_t len;
struct bounce_buffer bbstate;
int ret;
if (data) {
if (data->flags & MMC_DATA_READ) {
buf = data->dest;
bbflags = GEN_BB_WRITE;
} else {
buf = (void *)data->src;
bbflags = GEN_BB_READ;
}
len = data->blocks * data->blocksize;
bounce_buffer_start(&bbstate, buf, len, bbflags);
}
ret = tegra_mmc_send_cmd_bounced(dev, cmd, data, &bbstate);
if (data)
bounce_buffer_stop(&bbstate);
return ret;
}
static void tegra_mmc_change_clock(struct tegra_mmc_priv *priv, uint clock)
{
ulong rate;
int div;
unsigned short clk;
unsigned long timeout;
debug(" mmc_change_clock called\n");
/*
* Change Tegra SDMMCx clock divisor here. Source is PLLP_OUT0
*/
if (clock == 0)
goto out;
rate = clk_set_rate(&priv->clk, clock);
div = (rate + clock - 1) / clock;
#if defined(CONFIG_TEGRA210)
if (priv->mmc_id == PERIPH_ID_SDMMC1 && clock <= 400000) {
/* clock_adjust_periph_pll_div() chooses a 'bad' clock
* on SDMMC1 T210, so skip it here and force a clock
* that's been spec'd in the table in the TRM for
* card-detect (400KHz).
*/
uint effective_rate = clock_adjust_periph_pll_div(priv->mmc_id,
CLOCK_ID_PERIPH, 24727273, NULL);
div = 62;
debug("%s: WAR: Using SDMMC1 clock of %u, div %d to achieve %dHz card clock ...\n",
__func__, effective_rate, div, clock);
} else {
clock_adjust_periph_pll_div(priv->mmc_id, CLOCK_ID_PERIPH,
clock, &div);
}
#endif
debug("div = %d\n", div);
writew(0, &priv->reg->clkcon);
/*
* CLKCON
* SELFREQ[15:8] : base clock divided by value
* ENSDCLK[2] : SD Clock Enable
* STBLINTCLK[1] : Internal Clock Stable
* ENINTCLK[0] : Internal Clock Enable
*/
div >>= 1;
clk = ((div << TEGRA_MMC_CLKCON_SDCLK_FREQ_SEL_SHIFT) |
TEGRA_MMC_CLKCON_INTERNAL_CLOCK_ENABLE);
writew(clk, &priv->reg->clkcon);
/* Wait max 10 ms */
timeout = 10;
while (!(readw(&priv->reg->clkcon) &
TEGRA_MMC_CLKCON_INTERNAL_CLOCK_STABLE)) {
if (timeout == 0) {
printf("%s: timeout error\n", __func__);
return;
}
timeout--;
udelay(1000);
}
clk |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE;
writew(clk, &priv->reg->clkcon);
debug("mmc_change_clock: clkcon = %08X\n", clk);
out:
priv->clock = clock;
}
static int tegra_mmc_set_ios(struct udevice *dev)
{
struct tegra_mmc_priv *priv = dev_get_priv(dev);
struct mmc *mmc = mmc_get_mmc_dev(dev);
unsigned char ctrl;
debug(" mmc_set_ios called\n");
debug("bus_width: %x, clock: %d\n", mmc->bus_width, mmc->clock);
/* Change clock first */
tegra_mmc_change_clock(priv, mmc->clock);
ctrl = readb(&priv->reg->hostctl);
/*
* WIDE8[5]
* 0 = Depend on WIDE4
* 1 = 8-bit mode
* WIDE4[1]
* 1 = 4-bit mode
* 0 = 1-bit mode
*/
if (mmc->bus_width == 8)
ctrl |= (1 << 5);
else if (mmc->bus_width == 4)
ctrl |= (1 << 1);
else
ctrl &= ~(1 << 1 | 1 << 5);
writeb(ctrl, &priv->reg->hostctl);
debug("mmc_set_ios: hostctl = %08X\n", ctrl);
return 0;
}
static void tegra_mmc_pad_init(struct tegra_mmc_priv *priv)
{
#if defined(CONFIG_TEGRA30) || defined(CONFIG_TEGRA210)
u32 val;
u16 clk_con;
int timeout;
int id = priv->mmc_id;
debug("%s: sdmmc address = %p, id = %d\n", __func__,
priv->reg, id);
/* Set the pad drive strength for SDMMC1 or 3 only */
if (id != PERIPH_ID_SDMMC1 && id != PERIPH_ID_SDMMC3) {
debug("%s: settings are only valid for SDMMC1/SDMMC3!\n",
__func__);
return;
}
val = readl(&priv->reg->sdmemcmppadctl);
val &= 0xFFFFFFF0;
val |= MEMCOMP_PADCTRL_VREF;
writel(val, &priv->reg->sdmemcmppadctl);
/* Disable SD Clock Enable before running auto-cal as per TRM */
clk_con = readw(&priv->reg->clkcon);
debug("%s: CLOCK_CONTROL = 0x%04X\n", __func__, clk_con);
clk_con &= ~TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE;
writew(clk_con, &priv->reg->clkcon);
val = readl(&priv->reg->autocalcfg);
val &= 0xFFFF0000;
val |= AUTO_CAL_PU_OFFSET | AUTO_CAL_PD_OFFSET;
writel(val, &priv->reg->autocalcfg);
val |= AUTO_CAL_START | AUTO_CAL_ENABLE;
writel(val, &priv->reg->autocalcfg);
debug("%s: AUTO_CAL_CFG = 0x%08X\n", __func__, val);
udelay(1);
timeout = 100; /* 10 mSec max (100*100uS) */
do {
val = readl(&priv->reg->autocalsts);
udelay(100);
} while ((val & AUTO_CAL_ACTIVE) && --timeout);
val = readl(&priv->reg->autocalsts);
debug("%s: Final AUTO_CAL_STATUS = 0x%08X, timeout = %d\n",
__func__, val, timeout);
/* Re-enable SD Clock Enable when auto-cal is done */
clk_con |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE;
writew(clk_con, &priv->reg->clkcon);
clk_con = readw(&priv->reg->clkcon);
debug("%s: final CLOCK_CONTROL = 0x%04X\n", __func__, clk_con);
if (timeout == 0) {
printf("%s: Warning: Autocal timed out!\n", __func__);
/* TBD: Set CFG2TMC_SDMMC1_PAD_CAL_DRV* regs here */
}
#endif /* T30/T210 */
}
static void tegra_mmc_reset(struct tegra_mmc_priv *priv, struct mmc *mmc)
{
unsigned int timeout;
debug(" mmc_reset called\n");
/*
* RSTALL[0] : Software reset for all
* 1 = reset
* 0 = work
*/
writeb(TEGRA_MMC_SWRST_SW_RESET_FOR_ALL, &priv->reg->swrst);
priv->clock = 0;
/* Wait max 100 ms */
timeout = 100;
/* hw clears the bit when it's done */
while (readb(&priv->reg->swrst) & TEGRA_MMC_SWRST_SW_RESET_FOR_ALL) {
if (timeout == 0) {
printf("%s: timeout error\n", __func__);
return;
}
timeout--;
udelay(1000);
}
/* Set SD bus voltage & enable bus power */
tegra_mmc_set_power(priv, fls(mmc->cfg->voltages) - 1);
debug("%s: power control = %02X, host control = %02X\n", __func__,
readb(&priv->reg->pwrcon), readb(&priv->reg->hostctl));
/* Make sure SDIO pads are set up */
tegra_mmc_pad_init(priv);
if (!IS_ERR_VALUE(priv->tap_value) ||
!IS_ERR_VALUE(priv->trim_value)) {
u32 val;
val = readl(&priv->reg->venclkctl);
val &= ~TRIM_VAL_MASK;
val |= (priv->trim_value << TRIM_VAL_SHIFT);
val &= ~TAP_VAL_MASK;
val |= (priv->tap_value << TAP_VAL_SHIFT);
writel(val, &priv->reg->venclkctl);
debug("%s: VENDOR_CLOCK_CNTRL = 0x%08X\n", __func__, val);
}
}
static int tegra_mmc_init(struct udevice *dev)
{
struct tegra_mmc_priv *priv = dev_get_priv(dev);
struct mmc *mmc = mmc_get_mmc_dev(dev);
unsigned int mask;
debug(" tegra_mmc_init called\n");
#if defined(CONFIG_TEGRA210)
priv->mmc_id = clock_decode_periph_id(dev);
if (priv->mmc_id == PERIPH_ID_NONE) {
printf("%s: Missing/invalid peripheral ID\n", __func__);
return -EINVAL;
}
#endif
tegra_mmc_reset(priv, mmc);
#if defined(CONFIG_TEGRA124_MMC_DISABLE_EXT_LOOPBACK)
/*
* Disable the external clock loopback and use the internal one on
* SDMMC3 as per the SDMMC_VENDOR_MISC_CNTRL_0 register's SDMMC_SPARE1
* bits being set to 0xfffd according to the TRM.
*
* TODO(marcel.ziswiler@toradex.com): Move to device tree controlled
* approach once proper kernel integration made it mainline.
*/
if (priv->reg == (void *)0x700b0400) {
mask = readl(&priv->reg->venmiscctl);
mask &= ~TEGRA_MMC_MISCON_ENABLE_EXT_LOOPBACK;
writel(mask, &priv->reg->venmiscctl);
}
#endif
priv->version = readw(&priv->reg->hcver);
debug("host version = %x\n", priv->version);
/* mask all */
writel(0xffffffff, &priv->reg->norintstsen);
writel(0xffffffff, &priv->reg->norintsigen);
writeb(0xe, &priv->reg->timeoutcon); /* TMCLK * 2^27 */
/*
* NORMAL Interrupt Status Enable Register init
* [5] ENSTABUFRDRDY : Buffer Read Ready Status Enable
* [4] ENSTABUFWTRDY : Buffer write Ready Status Enable
* [3] ENSTADMAINT : DMA boundary interrupt
* [1] ENSTASTANSCMPLT : Transfre Complete Status Enable
* [0] ENSTACMDCMPLT : Command Complete Status Enable
*/
mask = readl(&priv->reg->norintstsen);
mask &= ~(0xffff);
mask |= (TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE |
TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE |
TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT |
TEGRA_MMC_NORINTSTSEN_BUFFER_WRITE_READY |
TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY);
writel(mask, &priv->reg->norintstsen);
/*
* NORMAL Interrupt Signal Enable Register init
* [1] ENSTACMDCMPLT : Transfer Complete Signal Enable
*/
mask = readl(&priv->reg->norintsigen);
mask &= ~(0xffff);
mask |= TEGRA_MMC_NORINTSIGEN_XFER_COMPLETE;
writel(mask, &priv->reg->norintsigen);
return 0;
}
static int tegra_mmc_getcd(struct udevice *dev)
{
struct tegra_mmc_priv *priv = dev_get_priv(dev);
debug("tegra_mmc_getcd called\n");
if (dm_gpio_is_valid(&priv->cd_gpio))
return dm_gpio_get_value(&priv->cd_gpio);
return 1;
}
static const struct dm_mmc_ops tegra_mmc_ops = {
.send_cmd = tegra_mmc_send_cmd,
.set_ios = tegra_mmc_set_ios,
.get_cd = tegra_mmc_getcd,
};
static int tegra_mmc_probe(struct udevice *dev)
{
struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
struct tegra_mmc_plat *plat = dev_get_plat(dev);
struct tegra_mmc_priv *priv = dev_get_priv(dev);
struct mmc_config *cfg = &plat->cfg;
int bus_width, ret;
cfg->name = dev->name;
bus_width = dev_read_u32_default(dev, "bus-width", 1);
cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
cfg->host_caps = 0;
if (bus_width == 8)
cfg->host_caps |= MMC_MODE_8BIT;
if (bus_width >= 4)
cfg->host_caps |= MMC_MODE_4BIT;
cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
/*
* min freq is for card identification, and is the highest
* low-speed SDIO card frequency (actually 400KHz)
* max freq is highest HS eMMC clock as per the SD/MMC spec
* (actually 52MHz)
*/
cfg->f_min = 375000;
cfg->f_max = dev_read_u32_default(dev, "max-frequency", 48000000);
cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
priv->reg = dev_read_addr_ptr(dev);
ret = reset_get_by_name(dev, "sdhci", &priv->reset_ctl);
if (ret) {
debug("reset_get_by_name() failed: %d\n", ret);
return ret;
}
ret = clk_get_by_index(dev, 0, &priv->clk);
if (ret) {
debug("clk_get_by_index() failed: %d\n", ret);
return ret;
}
ret = reset_assert(&priv->reset_ctl);
if (ret)
return ret;
ret = clk_enable(&priv->clk);
if (ret)
return ret;
ret = clk_set_rate(&priv->clk, 20000000);
if (IS_ERR_VALUE(ret))
return ret;
ret = reset_deassert(&priv->reset_ctl);
if (ret)
return ret;
/* These GPIOs are optional */
gpio_request_by_name(dev, "cd-gpios", 0, &priv->cd_gpio, GPIOD_IS_IN);
gpio_request_by_name(dev, "wp-gpios", 0, &priv->wp_gpio, GPIOD_IS_IN);
gpio_request_by_name(dev, "power-gpios", 0, &priv->pwr_gpio,
GPIOD_IS_OUT);
if (dm_gpio_is_valid(&priv->pwr_gpio))
dm_gpio_set_value(&priv->pwr_gpio, 1);
ret = dev_read_u32(dev, "nvidia,default-tap", &priv->tap_value);
if (ret)
priv->tap_value = ret;
ret = dev_read_u32(dev, "nvidia,default-trim", &priv->trim_value);
if (ret)
priv->trim_value = ret;
upriv->mmc = &plat->mmc;
return tegra_mmc_init(dev);
}
static int tegra_mmc_bind(struct udevice *dev)
{
struct tegra_mmc_plat *plat = dev_get_plat(dev);
return mmc_bind(dev, &plat->mmc, &plat->cfg);
}
static const struct udevice_id tegra_mmc_ids[] = {
{ .compatible = "nvidia,tegra20-sdhci" },
{ .compatible = "nvidia,tegra30-sdhci" },
{ .compatible = "nvidia,tegra114-sdhci" },
{ .compatible = "nvidia,tegra124-sdhci" },
{ .compatible = "nvidia,tegra210-sdhci" },
{ .compatible = "nvidia,tegra186-sdhci" },
{ }
};
U_BOOT_DRIVER(tegra_mmc_drv) = {
.name = "tegra_mmc",
.id = UCLASS_MMC,
.of_match = tegra_mmc_ids,
.bind = tegra_mmc_bind,
.probe = tegra_mmc_probe,
.ops = &tegra_mmc_ops,
.plat_auto = sizeof(struct tegra_mmc_plat),
.priv_auto = sizeof(struct tegra_mmc_priv),
};