diff options
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/tegra_mmc.c | 522 |
1 files changed, 199 insertions, 323 deletions
diff --git a/drivers/mmc/tegra_mmc.c b/drivers/mmc/tegra_mmc.c index 3d0845e317..97b1154595 100644 --- a/drivers/mmc/tegra_mmc.c +++ b/drivers/mmc/tegra_mmc.c @@ -2,7 +2,7 @@ * (C) Copyright 2009 SAMSUNG Electronics * Minkyu Kang <mk7.kang@samsung.com> * Jaehoon Chung <jh80.chung@samsung.com> - * Portions Copyright 2011-2015 NVIDIA Corporation + * Portions Copyright 2011-2016 NVIDIA Corporation * * SPDX-License-Identifier: GPL-2.0+ */ @@ -13,32 +13,26 @@ #include <errno.h> #include <asm/gpio.h> #include <asm/io.h> -#ifndef CONFIG_TEGRA186 -#include <asm/arch/clock.h> -#include <asm/arch-tegra/clk_rst.h> -#endif -#include <asm/arch-tegra/mmc.h> #include <asm/arch-tegra/tegra_mmc.h> #include <mmc.h> -/* - * FIXME: TODO: This driver contains a number of ifdef CONFIG_TEGRA186 that - * should not be present. These are needed because newer Tegra SoCs support - * only the standard clock/reset APIs, whereas older Tegra SoCs support only - * a custom Tegra-specific API. ASAP the older Tegra SoCs' code should be - * fixed to implement the standard APIs, and all drivers converted to solely - * use the new standard APIs, with no ifdefs. - */ - DECLARE_GLOBAL_DATA_PTR; -struct mmc_host mmc_host[CONFIG_SYS_MMC_MAX_DEVICE]; - -#if !CONFIG_IS_ENABLED(OF_CONTROL) -#error "Please enable device tree support to use this driver" -#endif +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) */ + struct mmc_config cfg; /* mmc configuration */ + struct mmc *mmc; +}; -static void mmc_set_power(struct mmc_host *host, unsigned short power) +static void tegra_mmc_set_power(struct tegra_mmc_priv *priv, + unsigned short power) { u8 pwr = 0; debug("%s: power = %x\n", __func__, power); @@ -61,17 +55,18 @@ static void mmc_set_power(struct mmc_host *host, unsigned short power) debug("%s: pwr = %X\n", __func__, pwr); /* Set the bus voltage first (if any) */ - writeb(pwr, &host->reg->pwrcon); + writeb(pwr, &priv->reg->pwrcon); if (pwr == 0) return; /* Now enable bus power */ pwr |= TEGRA_MMC_PWRCTL_SD_BUS_POWER; - writeb(pwr, &host->reg->pwrcon); + writeb(pwr, &priv->reg->pwrcon); } -static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data, - struct bounce_buffer *bbstate) +static void tegra_mmc_prepare_data(struct tegra_mmc_priv *priv, + struct mmc_data *data, + struct bounce_buffer *bbstate) { unsigned char ctrl; @@ -80,7 +75,7 @@ static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data, bbstate->bounce_buffer, bbstate->user_buffer, data->blocks, data->blocksize); - writel((u32)(unsigned long)bbstate->bounce_buffer, &host->reg->sysad); + writel((u32)(unsigned long)bbstate->bounce_buffer, &priv->reg->sysad); /* * DMASEL[4:3] * 00 = Selects SDMA @@ -88,17 +83,18 @@ static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data, * 10 = Selects 32-bit Address ADMA2 * 11 = Selects 64-bit Address ADMA2 */ - ctrl = readb(&host->reg->hostctl); + ctrl = readb(&priv->reg->hostctl); ctrl &= ~TEGRA_MMC_HOSTCTL_DMASEL_MASK; ctrl |= TEGRA_MMC_HOSTCTL_DMASEL_SDMA; - writeb(ctrl, &host->reg->hostctl); + writeb(ctrl, &priv->reg->hostctl); /* We do not handle DMA boundaries, so set it to max (512 KiB) */ - writew((7 << 12) | (data->blocksize & 0xFFF), &host->reg->blksize); - writew(data->blocks, &host->reg->blkcnt); + writew((7 << 12) | (data->blocksize & 0xFFF), &priv->reg->blksize); + writew(data->blocks, &priv->reg->blkcnt); } -static void mmc_set_transfer_mode(struct mmc_host *host, struct mmc_data *data) +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"); @@ -121,13 +117,13 @@ static void mmc_set_transfer_mode(struct mmc_host *host, struct mmc_data *data) if (data->flags & MMC_DATA_READ) mode |= TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ; - writew(mode, &host->reg->trnmod); + writew(mode, &priv->reg->trnmod); } -static int mmc_wait_inhibit(struct mmc_host *host, - struct mmc_cmd *cmd, - struct mmc_data *data, - unsigned int timeout) +static int tegra_mmc_wait_inhibit(struct tegra_mmc_priv *priv, + struct mmc_cmd *cmd, + struct mmc_data *data, + unsigned int timeout) { /* * PRNSTS @@ -143,7 +139,7 @@ static int mmc_wait_inhibit(struct mmc_host *host, if ((data == NULL) && (cmd->resp_type & MMC_RSP_BUSY)) mask |= TEGRA_MMC_PRNSTS_CMD_INHIBIT_DAT; - while (readl(&host->reg->prnsts) & mask) { + while (readl(&priv->reg->prnsts) & mask) { if (timeout == 0) { printf("%s: timeout error\n", __func__); return -1; @@ -155,29 +151,30 @@ static int mmc_wait_inhibit(struct mmc_host *host, return 0; } -static int mmc_send_cmd_bounced(struct mmc *mmc, struct mmc_cmd *cmd, - struct mmc_data *data, struct bounce_buffer *bbstate) +static int tegra_mmc_send_cmd_bounced(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data, + struct bounce_buffer *bbstate) { - struct mmc_host *host = mmc->priv; + struct tegra_mmc_priv *priv = mmc->priv; int flags, i; int result; unsigned int mask = 0; unsigned int retry = 0x100000; debug(" mmc_send_cmd called\n"); - result = mmc_wait_inhibit(host, cmd, data, 10 /* ms */); + result = tegra_mmc_wait_inhibit(priv, cmd, data, 10 /* ms */); if (result < 0) return result; if (data) - mmc_prepare_data(host, data, bbstate); + tegra_mmc_prepare_data(priv, data, bbstate); debug("cmd->arg: %08x\n", cmd->cmdarg); - writel(cmd->cmdarg, &host->reg->argument); + writel(cmd->cmdarg, &priv->reg->argument); if (data) - mmc_set_transfer_mode(host, data); + tegra_mmc_set_transfer_mode(priv, data); if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY)) return -1; @@ -212,33 +209,33 @@ static int mmc_send_cmd_bounced(struct mmc *mmc, struct mmc_cmd *cmd, debug("cmd: %d\n", cmd->cmdidx); - writew((cmd->cmdidx << 8) | flags, &host->reg->cmdreg); + writew((cmd->cmdidx << 8) | flags, &priv->reg->cmdreg); for (i = 0; i < retry; i++) { - mask = readl(&host->reg->norintsts); + mask = readl(&priv->reg->norintsts); /* Command Complete */ if (mask & TEGRA_MMC_NORINTSTS_CMD_COMPLETE) { if (!data) - writel(mask, &host->reg->norintsts); + writel(mask, &priv->reg->norintsts); break; } } if (i == retry) { printf("%s: waiting for status update\n", __func__); - writel(mask, &host->reg->norintsts); + 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, &host->reg->norintsts); + 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, &host->reg->norintsts); + writel(mask, &priv->reg->norintsts); return -1; } @@ -246,8 +243,8 @@ static int mmc_send_cmd_bounced(struct mmc *mmc, struct mmc_cmd *cmd, 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)(&host->reg->rspreg3 - i); + unsigned long offset = (unsigned long) + (&priv->reg->rspreg3 - i); cmd->response[i] = readl(offset) << 8; if (i != 3) { @@ -260,21 +257,21 @@ static int mmc_send_cmd_bounced(struct mmc *mmc, struct mmc_cmd *cmd, } else if (cmd->resp_type & MMC_RSP_BUSY) { for (i = 0; i < retry; i++) { /* PRNTDATA[23:20] : DAT[3:0] Line Signal */ - if (readl(&host->reg->prnsts) + if (readl(&priv->reg->prnsts) & (1 << 20)) /* DAT[0] */ break; } if (i == retry) { printf("%s: card is still busy\n", __func__); - writel(mask, &host->reg->norintsts); + writel(mask, &priv->reg->norintsts); return -ETIMEDOUT; } - cmd->response[0] = readl(&host->reg->rspreg0); + cmd->response[0] = readl(&priv->reg->rspreg0); debug("cmd->resp[0]: %08x\n", cmd->response[0]); } else { - cmd->response[0] = readl(&host->reg->rspreg0); + cmd->response[0] = readl(&priv->reg->rspreg0); debug("cmd->resp[0]: %08x\n", cmd->response[0]); } } @@ -283,11 +280,11 @@ static int mmc_send_cmd_bounced(struct mmc *mmc, struct mmc_cmd *cmd, unsigned long start = get_timer(0); while (1) { - mask = readl(&host->reg->norintsts); + mask = readl(&priv->reg->norintsts); if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) { /* Error Interrupt */ - writel(mask, &host->reg->norintsts); + writel(mask, &priv->reg->norintsts); printf("%s: error during transfer: 0x%08x\n", __func__, mask); return -1; @@ -296,31 +293,31 @@ static int mmc_send_cmd_bounced(struct mmc *mmc, struct mmc_cmd *cmd, * DMA Interrupt, restart the transfer where * it was interrupted. */ - unsigned int address = readl(&host->reg->sysad); + unsigned int address = readl(&priv->reg->sysad); debug("DMA end\n"); writel(TEGRA_MMC_NORINTSTS_DMA_INTERRUPT, - &host->reg->norintsts); - writel(address, &host->reg->sysad); + &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, &host->reg->norintsts); + 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(&host->reg->norintstsen), - readl(&host->reg->norintsigen), - readl(&host->reg->prnsts)); + readl(&priv->reg->norintstsen), + readl(&priv->reg->norintsigen), + readl(&priv->reg->prnsts)); return -1; } } - writel(mask, &host->reg->norintsts); + writel(mask, &priv->reg->norintsts); } udelay(1000); @@ -328,7 +325,7 @@ static int mmc_send_cmd_bounced(struct mmc *mmc, struct mmc_cmd *cmd, } static int tegra_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, - struct mmc_data *data) + struct mmc_data *data) { void *buf; unsigned int bbflags; @@ -349,7 +346,7 @@ static int tegra_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, bounce_buffer_start(&bbstate, buf, len, bbflags); } - ret = mmc_send_cmd_bounced(mmc, cmd, data, &bbstate); + ret = tegra_mmc_send_cmd_bounced(mmc, cmd, data, &bbstate); if (data) bounce_buffer_stop(&bbstate); @@ -357,8 +354,9 @@ static int tegra_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, return ret; } -static void mmc_change_clock(struct mmc_host *host, uint clock) +static void tegra_mmc_change_clock(struct tegra_mmc_priv *priv, uint clock) { + ulong rate; int div; unsigned short clk; unsigned long timeout; @@ -370,18 +368,12 @@ static void mmc_change_clock(struct mmc_host *host, uint clock) */ if (clock == 0) goto out; -#ifdef CONFIG_TEGRA186 - { - ulong rate = clk_set_rate(&host->clk, clock); - div = (rate + clock - 1) / clock; - } -#else - clock_adjust_periph_pll_div(host->mmc_id, CLOCK_ID_PERIPH, clock, - &div); -#endif + + rate = clk_set_rate(&priv->clk, clock); + div = (rate + clock - 1) / clock; debug("div = %d\n", div); - writew(0, &host->reg->clkcon); + writew(0, &priv->reg->clkcon); /* * CLKCON @@ -393,11 +385,11 @@ static void mmc_change_clock(struct mmc_host *host, uint clock) div >>= 1; clk = ((div << TEGRA_MMC_CLKCON_SDCLK_FREQ_SEL_SHIFT) | TEGRA_MMC_CLKCON_INTERNAL_CLOCK_ENABLE); - writew(clk, &host->reg->clkcon); + writew(clk, &priv->reg->clkcon); /* Wait max 10 ms */ timeout = 10; - while (!(readw(&host->reg->clkcon) & + while (!(readw(&priv->reg->clkcon) & TEGRA_MMC_CLKCON_INTERNAL_CLOCK_STABLE)) { if (timeout == 0) { printf("%s: timeout error\n", __func__); @@ -408,26 +400,26 @@ static void mmc_change_clock(struct mmc_host *host, uint clock) } clk |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; - writew(clk, &host->reg->clkcon); + writew(clk, &priv->reg->clkcon); debug("mmc_change_clock: clkcon = %08X\n", clk); out: - host->clock = clock; + priv->clock = clock; } static void tegra_mmc_set_ios(struct mmc *mmc) { - struct mmc_host *host = mmc->priv; + struct tegra_mmc_priv *priv = mmc->priv; unsigned char ctrl; debug(" mmc_set_ios called\n"); debug("bus_width: %x, clock: %d\n", mmc->bus_width, mmc->clock); /* Change clock first */ - mmc_change_clock(host, mmc->clock); + tegra_mmc_change_clock(priv, mmc->clock); - ctrl = readb(&host->reg->hostctl); + ctrl = readb(&priv->reg->hostctl); /* * WIDE8[5] @@ -444,11 +436,38 @@ static void tegra_mmc_set_ios(struct mmc *mmc) else ctrl &= ~(1 << 1); - writeb(ctrl, &host->reg->hostctl); + writeb(ctrl, &priv->reg->hostctl); debug("mmc_set_ios: hostctl = %08X\n", ctrl); } -static void mmc_reset(struct mmc_host *host, struct mmc *mmc) +static void tegra_mmc_pad_init(struct tegra_mmc_priv *priv) +{ +#if defined(CONFIG_TEGRA30) + u32 val; + + debug("%s: sdmmc address = %08x\n", __func__, (unsigned int)priv->reg); + + /* Set the pad drive strength for SDMMC1 or 3 only */ + if (priv->reg != (void *)0x78000000 && + priv->reg != (void *)0x78000400) { + 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); + + val = readl(&priv->reg->autocalcfg); + val &= 0xFFFF0000; + val |= AUTO_CAL_PU_OFFSET | AUTO_CAL_PD_OFFSET | AUTO_CAL_ENABLED; + writel(val, &priv->reg->autocalcfg); +#endif +} + +static void tegra_mmc_reset(struct tegra_mmc_priv *priv, struct mmc *mmc) { unsigned int timeout; debug(" mmc_reset called\n"); @@ -458,15 +477,15 @@ static void mmc_reset(struct mmc_host *host, struct mmc *mmc) * 1 = reset * 0 = work */ - writeb(TEGRA_MMC_SWRST_SW_RESET_FOR_ALL, &host->reg->swrst); + writeb(TEGRA_MMC_SWRST_SW_RESET_FOR_ALL, &priv->reg->swrst); - host->clock = 0; + priv->clock = 0; /* Wait max 100 ms */ timeout = 100; /* hw clears the bit when it's done */ - while (readb(&host->reg->swrst) & TEGRA_MMC_SWRST_SW_RESET_FOR_ALL) { + while (readb(&priv->reg->swrst) & TEGRA_MMC_SWRST_SW_RESET_FOR_ALL) { if (timeout == 0) { printf("%s: timeout error\n", __func__); return; @@ -476,30 +495,30 @@ static void mmc_reset(struct mmc_host *host, struct mmc *mmc) } /* Set SD bus voltage & enable bus power */ - mmc_set_power(host, fls(mmc->cfg->voltages) - 1); + tegra_mmc_set_power(priv, fls(mmc->cfg->voltages) - 1); debug("%s: power control = %02X, host control = %02X\n", __func__, - readb(&host->reg->pwrcon), readb(&host->reg->hostctl)); + readb(&priv->reg->pwrcon), readb(&priv->reg->hostctl)); /* Make sure SDIO pads are set up */ - pad_init_mmc(host); + tegra_mmc_pad_init(priv); } -static int tegra_mmc_core_init(struct mmc *mmc) +static int tegra_mmc_init(struct mmc *mmc) { - struct mmc_host *host = mmc->priv; + struct tegra_mmc_priv *priv = mmc->priv; unsigned int mask; - debug(" mmc_core_init called\n"); + debug(" tegra_mmc_init called\n"); - mmc_reset(host, mmc); + tegra_mmc_reset(priv, mmc); - host->version = readw(&host->reg->hcver); - debug("host version = %x\n", host->version); + priv->version = readw(&priv->reg->hcver); + debug("host version = %x\n", priv->version); /* mask all */ - writel(0xffffffff, &host->reg->norintstsen); - writel(0xffffffff, &host->reg->norintsigen); + writel(0xffffffff, &priv->reg->norintstsen); + writel(0xffffffff, &priv->reg->norintsigen); - writeb(0xe, &host->reg->timeoutcon); /* TMCLK * 2^27 */ + writeb(0xe, &priv->reg->timeoutcon); /* TMCLK * 2^27 */ /* * NORMAL Interrupt Status Enable Register init * [5] ENSTABUFRDRDY : Buffer Read Ready Status Enable @@ -508,35 +527,35 @@ static int tegra_mmc_core_init(struct mmc *mmc) * [1] ENSTASTANSCMPLT : Transfre Complete Status Enable * [0] ENSTACMDCMPLT : Command Complete Status Enable */ - mask = readl(&host->reg->norintstsen); + 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, &host->reg->norintstsen); + writel(mask, &priv->reg->norintstsen); /* * NORMAL Interrupt Signal Enable Register init * [1] ENSTACMDCMPLT : Transfer Complete Signal Enable */ - mask = readl(&host->reg->norintsigen); + mask = readl(&priv->reg->norintsigen); mask &= ~(0xffff); mask |= TEGRA_MMC_NORINTSIGEN_XFER_COMPLETE; - writel(mask, &host->reg->norintsigen); + writel(mask, &priv->reg->norintsigen); return 0; } static int tegra_mmc_getcd(struct mmc *mmc) { - struct mmc_host *host = mmc->priv; + struct tegra_mmc_priv *priv = mmc->priv; debug("tegra_mmc_getcd called\n"); - if (dm_gpio_is_valid(&host->cd_gpio)) - return dm_gpio_get_value(&host->cd_gpio); + if (dm_gpio_is_valid(&priv->cd_gpio)) + return dm_gpio_get_value(&priv->cd_gpio); return 1; } @@ -544,61 +563,29 @@ static int tegra_mmc_getcd(struct mmc *mmc) static const struct mmc_ops tegra_mmc_ops = { .send_cmd = tegra_mmc_send_cmd, .set_ios = tegra_mmc_set_ios, - .init = tegra_mmc_core_init, + .init = tegra_mmc_init, .getcd = tegra_mmc_getcd, }; -static int do_mmc_init(int dev_index, bool removable) +static int tegra_mmc_probe(struct udevice *dev) { - struct mmc_host *host; - struct mmc *mmc; -#ifdef CONFIG_TEGRA186 - int ret; -#endif + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct tegra_mmc_priv *priv = dev_get_priv(dev); + int bus_width, ret; - /* DT should have been read & host config filled in */ - host = &mmc_host[dev_index]; - if (!host->enabled) - return -1; - - debug(" do_mmc_init: index %d, bus width %d pwr_gpio %d cd_gpio %d\n", - dev_index, host->width, gpio_get_number(&host->pwr_gpio), - gpio_get_number(&host->cd_gpio)); - - host->clock = 0; - -#ifdef CONFIG_TEGRA186 - ret = reset_assert(&host->reset_ctl); - if (ret) - return ret; - ret = clk_enable(&host->clk); - if (ret) - return ret; - ret = clk_set_rate(&host->clk, 20000000); - if (IS_ERR_VALUE(ret)) - return ret; - ret = reset_deassert(&host->reset_ctl); - if (ret) - return ret; -#else - clock_start_periph_pll(host->mmc_id, CLOCK_ID_PERIPH, 20000000); -#endif - - if (dm_gpio_is_valid(&host->pwr_gpio)) - dm_gpio_set_value(&host->pwr_gpio, 1); - - memset(&host->cfg, 0, sizeof(host->cfg)); + priv->cfg.name = "Tegra SD/MMC"; + priv->cfg.ops = &tegra_mmc_ops; - host->cfg.name = "Tegra SD/MMC"; - host->cfg.ops = &tegra_mmc_ops; + bus_width = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "bus-width", + 1); - host->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; - host->cfg.host_caps = 0; - if (host->width == 8) - host->cfg.host_caps |= MMC_MODE_8BIT; - if (host->width >= 4) - host->cfg.host_caps |= MMC_MODE_4BIT; - host->cfg.host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; + priv->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; + priv->cfg.host_caps = 0; + if (bus_width == 8) + priv->cfg.host_caps |= MMC_MODE_8BIT; + if (bus_width >= 4) + priv->cfg.host_caps |= MMC_MODE_4BIT; + priv->cfg.host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; /* * min freq is for card identification, and is the highest @@ -606,182 +593,71 @@ static int do_mmc_init(int dev_index, bool removable) * max freq is highest HS eMMC clock as per the SD/MMC spec * (actually 52MHz) */ - host->cfg.f_min = 375000; - host->cfg.f_max = 48000000; - - host->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; - - mmc = mmc_create(&host->cfg, host); - mmc->block_dev.removable = removable; - if (mmc == NULL) - return -1; - - return 0; -} - -/** - * Get the host address and peripheral ID for a node. - * - * @param blob fdt blob - * @param node Device index (0-3) - * @param host Structure to fill in (reg, width, mmc_id) - */ -static int mmc_get_config(const void *blob, int node, struct mmc_host *host, - bool *removablep) -{ - debug("%s: node = %d\n", __func__, node); + priv->cfg.f_min = 375000; + priv->cfg.f_max = 48000000; - host->enabled = fdtdec_get_is_enabled(blob, node); + priv->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; - host->reg = (struct tegra_mmc *)fdtdec_get_addr(blob, node, "reg"); - if ((fdt_addr_t)host->reg == FDT_ADDR_T_NONE) { - debug("%s: no sdmmc base reg info found\n", __func__); - return -FDT_ERR_NOTFOUND; - } + priv->reg = (void *)dev_get_addr(dev); -#ifdef CONFIG_TEGRA186 - { - /* - * FIXME: This variable should go away when the MMC device - * actually is a udevice. - */ - struct udevice dev; - int ret; - dev.of_offset = node; - ret = reset_get_by_name(&dev, "sdhci", &host->reset_ctl); - if (ret) { - debug("reset_get_by_name() failed: %d\n", ret); - return ret; - } - ret = clk_get_by_index(&dev, 0, &host->clk); - if (ret) { - debug("clk_get_by_index() failed: %d\n", ret); - return ret; - } + ret = reset_get_by_name(dev, "sdhci", &priv->reset_ctl); + if (ret) { + debug("reset_get_by_name() failed: %d\n", ret); + return ret; } -#else - host->mmc_id = clock_decode_periph_id(blob, node); - if (host->mmc_id == PERIPH_ID_NONE) { - debug("%s: could not decode periph id\n", __func__); - return -FDT_ERR_NOTFOUND; + ret = clk_get_by_index(dev, 0, &priv->clk); + if (ret) { + debug("clk_get_by_index() failed: %d\n", ret); + return ret; } -#endif - /* - * NOTE: mmc->bus_width is determined by mmc.c dynamically. - * TBD: Override it with this value? - */ - host->width = fdtdec_get_int(blob, node, "bus-width", 0); - if (!host->width) - debug("%s: no sdmmc width found\n", __func__); + 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_nodev(blob, node, "cd-gpios", 0, &host->cd_gpio, - GPIOD_IS_IN); - gpio_request_by_name_nodev(blob, node, "wp-gpios", 0, &host->wp_gpio, - GPIOD_IS_IN); - gpio_request_by_name_nodev(blob, node, "power-gpios", 0, - &host->pwr_gpio, GPIOD_IS_OUT); - *removablep = !fdtdec_get_bool(blob, node, "non-removable"); - - debug("%s: found controller at %p, width = %d, periph_id = %d\n", - __func__, host->reg, host->width, -#ifndef CONFIG_TEGRA186 - host->mmc_id -#else - -1 -#endif - ); - return 0; -} - -/* - * Process a list of nodes, adding them to our list of SDMMC ports. - * - * @param blob fdt blob - * @param node_list list of nodes to process (any <=0 are ignored) - * @param count number of nodes to process - * @return 0 if ok, -1 on error - */ -static int process_nodes(const void *blob, int node_list[], int count) -{ - struct mmc_host *host; - bool removable; - int i, node; - - debug("%s: count = %d\n", __func__, count); - - /* build mmc_host[] for each controller */ - for (i = 0; i < count; i++) { - node = node_list[i]; - if (node <= 0) - continue; + 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); + + priv->mmc = mmc_create(&priv->cfg, priv); + if (priv->mmc == NULL) + return -1; - host = &mmc_host[i]; - host->id = i; + priv->mmc->dev = dev; + upriv->mmc = priv->mmc; - if (mmc_get_config(blob, node, host, &removable)) { - printf("%s: failed to decode dev %d\n", __func__, i); - return -1; - } - do_mmc_init(i, removable); - } return 0; } -void tegra_mmc_init(void) -{ - int node_list[CONFIG_SYS_MMC_MAX_DEVICE], count; - const void *blob = gd->fdt_blob; - debug("%s entry\n", __func__); - - /* See if any Tegra186 MMC controllers are present */ - count = fdtdec_find_aliases_for_id(blob, "sdhci", - COMPAT_NVIDIA_TEGRA186_SDMMC, node_list, - CONFIG_SYS_MMC_MAX_DEVICE); - debug("%s: count of Tegra186 sdhci nodes is %d\n", __func__, count); - if (process_nodes(blob, node_list, count)) { - printf("%s: Error processing T186 mmc node(s)!\n", __func__); - return; - } - - /* See if any Tegra210 MMC controllers are present */ - count = fdtdec_find_aliases_for_id(blob, "sdhci", - COMPAT_NVIDIA_TEGRA210_SDMMC, node_list, - CONFIG_SYS_MMC_MAX_DEVICE); - debug("%s: count of Tegra210 sdhci nodes is %d\n", __func__, count); - if (process_nodes(blob, node_list, count)) { - printf("%s: Error processing T210 mmc node(s)!\n", __func__); - return; - } - - /* See if any Tegra124 MMC controllers are present */ - count = fdtdec_find_aliases_for_id(blob, "sdhci", - COMPAT_NVIDIA_TEGRA124_SDMMC, node_list, - CONFIG_SYS_MMC_MAX_DEVICE); - debug("%s: count of Tegra124 sdhci nodes is %d\n", __func__, count); - if (process_nodes(blob, node_list, count)) { - printf("%s: Error processing T124 mmc node(s)!\n", __func__); - return; - } - - /* See if any Tegra30 MMC controllers are present */ - count = fdtdec_find_aliases_for_id(blob, "sdhci", - COMPAT_NVIDIA_TEGRA30_SDMMC, node_list, - CONFIG_SYS_MMC_MAX_DEVICE); - debug("%s: count of T30 sdhci nodes is %d\n", __func__, count); - if (process_nodes(blob, node_list, count)) { - printf("%s: Error processing T30 mmc node(s)!\n", __func__); - return; - } +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" }, + { } +}; - /* Now look for any Tegra20 MMC controllers */ - count = fdtdec_find_aliases_for_id(blob, "sdhci", - COMPAT_NVIDIA_TEGRA20_SDMMC, node_list, - CONFIG_SYS_MMC_MAX_DEVICE); - debug("%s: count of T20 sdhci nodes is %d\n", __func__, count); - if (process_nodes(blob, node_list, count)) { - printf("%s: Error processing T20 mmc node(s)!\n", __func__); - return; - } -} +U_BOOT_DRIVER(tegra_mmc_drv) = { + .name = "tegra_mmc", + .id = UCLASS_MMC, + .of_match = tegra_mmc_ids, + .probe = tegra_mmc_probe, + .priv_auto_alloc_size = sizeof(struct tegra_mmc_priv), +}; |