13f82d89dSTom Warren /* 23f82d89dSTom Warren * (C) Copyright 2009 SAMSUNG Electronics 33f82d89dSTom Warren * Minkyu Kang <mk7.kang@samsung.com> 43f82d89dSTom Warren * Jaehoon Chung <jh80.chung@samsung.com> 57aaa5a60STom Warren * Portions Copyright 2011-2015 NVIDIA Corporation 63f82d89dSTom Warren * 71a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 83f82d89dSTom Warren */ 93f82d89dSTom Warren 1019815399SStephen Warren #include <bouncebuf.h> 113f82d89dSTom Warren #include <common.h> 12c0493076SStephen Warren #include <dm/device.h> 13915ffa52SJaehoon Chung #include <errno.h> 143f82d89dSTom Warren #include <asm/gpio.h> 153f82d89dSTom Warren #include <asm/io.h> 1639f63332SStephen Warren #ifndef CONFIG_TEGRA186 173f82d89dSTom Warren #include <asm/arch/clock.h> 18150c2493STom Warren #include <asm/arch-tegra/clk_rst.h> 1939f63332SStephen Warren #endif 2019d7bf3dSJeroen Hofstee #include <asm/arch-tegra/mmc.h> 21150c2493STom Warren #include <asm/arch-tegra/tegra_mmc.h> 22150c2493STom Warren #include <mmc.h> 233f82d89dSTom Warren 24c0493076SStephen Warren /* 25c0493076SStephen Warren * FIXME: TODO: This driver contains a number of ifdef CONFIG_TEGRA186 that 26c0493076SStephen Warren * should not be present. These are needed because newer Tegra SoCs support 27c0493076SStephen Warren * only the standard clock/reset APIs, whereas older Tegra SoCs support only 28c0493076SStephen Warren * a custom Tegra-specific API. ASAP the older Tegra SoCs' code should be 29c0493076SStephen Warren * fixed to implement the standard APIs, and all drivers converted to solely 30c0493076SStephen Warren * use the new standard APIs, with no ifdefs. 31c0493076SStephen Warren */ 32c0493076SStephen Warren 33c9aa831eSTom Warren DECLARE_GLOBAL_DATA_PTR; 343f82d89dSTom Warren 35f175603fSStephen Warren struct mmc_host mmc_host[CONFIG_SYS_MMC_MAX_DEVICE]; 363f82d89dSTom Warren 370f925822SMasahiro Yamada #if !CONFIG_IS_ENABLED(OF_CONTROL) 38c9aa831eSTom Warren #error "Please enable device tree support to use this driver" 39c9aa831eSTom Warren #endif 403f82d89dSTom Warren 412d348a16STom Warren static void mmc_set_power(struct mmc_host *host, unsigned short power) 422d348a16STom Warren { 432d348a16STom Warren u8 pwr = 0; 442d348a16STom Warren debug("%s: power = %x\n", __func__, power); 452d348a16STom Warren 462d348a16STom Warren if (power != (unsigned short)-1) { 472d348a16STom Warren switch (1 << power) { 482d348a16STom Warren case MMC_VDD_165_195: 492d348a16STom Warren pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V1_8; 502d348a16STom Warren break; 512d348a16STom Warren case MMC_VDD_29_30: 522d348a16STom Warren case MMC_VDD_30_31: 532d348a16STom Warren pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_0; 542d348a16STom Warren break; 552d348a16STom Warren case MMC_VDD_32_33: 562d348a16STom Warren case MMC_VDD_33_34: 572d348a16STom Warren pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_3; 582d348a16STom Warren break; 592d348a16STom Warren } 602d348a16STom Warren } 612d348a16STom Warren debug("%s: pwr = %X\n", __func__, pwr); 622d348a16STom Warren 632d348a16STom Warren /* Set the bus voltage first (if any) */ 642d348a16STom Warren writeb(pwr, &host->reg->pwrcon); 652d348a16STom Warren if (pwr == 0) 662d348a16STom Warren return; 672d348a16STom Warren 682d348a16STom Warren /* Now enable bus power */ 692d348a16STom Warren pwr |= TEGRA_MMC_PWRCTL_SD_BUS_POWER; 702d348a16STom Warren writeb(pwr, &host->reg->pwrcon); 712d348a16STom Warren } 722d348a16STom Warren 7319815399SStephen Warren static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data, 7419815399SStephen Warren struct bounce_buffer *bbstate) 753f82d89dSTom Warren { 763f82d89dSTom Warren unsigned char ctrl; 773f82d89dSTom Warren 783f82d89dSTom Warren 7919815399SStephen Warren debug("buf: %p (%p), data->blocks: %u, data->blocksize: %u\n", 8019815399SStephen Warren bbstate->bounce_buffer, bbstate->user_buffer, data->blocks, 8119815399SStephen Warren data->blocksize); 8219815399SStephen Warren 83c39e2a75SThierry Reding writel((u32)(unsigned long)bbstate->bounce_buffer, &host->reg->sysad); 843f82d89dSTom Warren /* 853f82d89dSTom Warren * DMASEL[4:3] 863f82d89dSTom Warren * 00 = Selects SDMA 873f82d89dSTom Warren * 01 = Reserved 883f82d89dSTom Warren * 10 = Selects 32-bit Address ADMA2 893f82d89dSTom Warren * 11 = Selects 64-bit Address ADMA2 903f82d89dSTom Warren */ 913f82d89dSTom Warren ctrl = readb(&host->reg->hostctl); 923f82d89dSTom Warren ctrl &= ~TEGRA_MMC_HOSTCTL_DMASEL_MASK; 933f82d89dSTom Warren ctrl |= TEGRA_MMC_HOSTCTL_DMASEL_SDMA; 943f82d89dSTom Warren writeb(ctrl, &host->reg->hostctl); 953f82d89dSTom Warren 963f82d89dSTom Warren /* We do not handle DMA boundaries, so set it to max (512 KiB) */ 973f82d89dSTom Warren writew((7 << 12) | (data->blocksize & 0xFFF), &host->reg->blksize); 983f82d89dSTom Warren writew(data->blocks, &host->reg->blkcnt); 993f82d89dSTom Warren } 1003f82d89dSTom Warren 1013f82d89dSTom Warren static void mmc_set_transfer_mode(struct mmc_host *host, struct mmc_data *data) 1023f82d89dSTom Warren { 1033f82d89dSTom Warren unsigned short mode; 1043f82d89dSTom Warren debug(" mmc_set_transfer_mode called\n"); 1053f82d89dSTom Warren /* 1063f82d89dSTom Warren * TRNMOD 1073f82d89dSTom Warren * MUL1SIN0[5] : Multi/Single Block Select 1083f82d89dSTom Warren * RD1WT0[4] : Data Transfer Direction Select 1093f82d89dSTom Warren * 1 = read 1103f82d89dSTom Warren * 0 = write 1113f82d89dSTom Warren * ENACMD12[2] : Auto CMD12 Enable 1123f82d89dSTom Warren * ENBLKCNT[1] : Block Count Enable 1133f82d89dSTom Warren * ENDMA[0] : DMA Enable 1143f82d89dSTom Warren */ 1153f82d89dSTom Warren mode = (TEGRA_MMC_TRNMOD_DMA_ENABLE | 1163f82d89dSTom Warren TEGRA_MMC_TRNMOD_BLOCK_COUNT_ENABLE); 1173f82d89dSTom Warren 1183f82d89dSTom Warren if (data->blocks > 1) 1193f82d89dSTom Warren mode |= TEGRA_MMC_TRNMOD_MULTI_BLOCK_SELECT; 1203f82d89dSTom Warren 1213f82d89dSTom Warren if (data->flags & MMC_DATA_READ) 1223f82d89dSTom Warren mode |= TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ; 1233f82d89dSTom Warren 1243f82d89dSTom Warren writew(mode, &host->reg->trnmod); 1253f82d89dSTom Warren } 1263f82d89dSTom Warren 1273f82d89dSTom Warren static int mmc_wait_inhibit(struct mmc_host *host, 1283f82d89dSTom Warren struct mmc_cmd *cmd, 1293f82d89dSTom Warren struct mmc_data *data, 1303f82d89dSTom Warren unsigned int timeout) 1313f82d89dSTom Warren { 1323f82d89dSTom Warren /* 1333f82d89dSTom Warren * PRNSTS 1343f82d89dSTom Warren * CMDINHDAT[1] : Command Inhibit (DAT) 1353f82d89dSTom Warren * CMDINHCMD[0] : Command Inhibit (CMD) 1363f82d89dSTom Warren */ 1373f82d89dSTom Warren unsigned int mask = TEGRA_MMC_PRNSTS_CMD_INHIBIT_CMD; 1383f82d89dSTom Warren 1393f82d89dSTom Warren /* 1403f82d89dSTom Warren * We shouldn't wait for data inhibit for stop commands, even 1413f82d89dSTom Warren * though they might use busy signaling 1423f82d89dSTom Warren */ 1433f82d89dSTom Warren if ((data == NULL) && (cmd->resp_type & MMC_RSP_BUSY)) 1443f82d89dSTom Warren mask |= TEGRA_MMC_PRNSTS_CMD_INHIBIT_DAT; 1453f82d89dSTom Warren 1463f82d89dSTom Warren while (readl(&host->reg->prnsts) & mask) { 1473f82d89dSTom Warren if (timeout == 0) { 1483f82d89dSTom Warren printf("%s: timeout error\n", __func__); 1493f82d89dSTom Warren return -1; 1503f82d89dSTom Warren } 1513f82d89dSTom Warren timeout--; 1523f82d89dSTom Warren udelay(1000); 1533f82d89dSTom Warren } 1543f82d89dSTom Warren 1553f82d89dSTom Warren return 0; 1563f82d89dSTom Warren } 1573f82d89dSTom Warren 15819815399SStephen Warren static int mmc_send_cmd_bounced(struct mmc *mmc, struct mmc_cmd *cmd, 15919815399SStephen Warren struct mmc_data *data, struct bounce_buffer *bbstate) 1603f82d89dSTom Warren { 16193bfd616SPantelis Antoniou struct mmc_host *host = mmc->priv; 1623f82d89dSTom Warren int flags, i; 1633f82d89dSTom Warren int result; 1643f82d89dSTom Warren unsigned int mask = 0; 1653f82d89dSTom Warren unsigned int retry = 0x100000; 1663f82d89dSTom Warren debug(" mmc_send_cmd called\n"); 1673f82d89dSTom Warren 1683f82d89dSTom Warren result = mmc_wait_inhibit(host, cmd, data, 10 /* ms */); 1693f82d89dSTom Warren 1703f82d89dSTom Warren if (result < 0) 1713f82d89dSTom Warren return result; 1723f82d89dSTom Warren 1733f82d89dSTom Warren if (data) 17419815399SStephen Warren mmc_prepare_data(host, data, bbstate); 1753f82d89dSTom Warren 1763f82d89dSTom Warren debug("cmd->arg: %08x\n", cmd->cmdarg); 1773f82d89dSTom Warren writel(cmd->cmdarg, &host->reg->argument); 1783f82d89dSTom Warren 1793f82d89dSTom Warren if (data) 1803f82d89dSTom Warren mmc_set_transfer_mode(host, data); 1813f82d89dSTom Warren 1823f82d89dSTom Warren if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY)) 1833f82d89dSTom Warren return -1; 1843f82d89dSTom Warren 1853f82d89dSTom Warren /* 1863f82d89dSTom Warren * CMDREG 1873f82d89dSTom Warren * CMDIDX[13:8] : Command index 1883f82d89dSTom Warren * DATAPRNT[5] : Data Present Select 1893f82d89dSTom Warren * ENCMDIDX[4] : Command Index Check Enable 1903f82d89dSTom Warren * ENCMDCRC[3] : Command CRC Check Enable 1913f82d89dSTom Warren * RSPTYP[1:0] 1923f82d89dSTom Warren * 00 = No Response 1933f82d89dSTom Warren * 01 = Length 136 1943f82d89dSTom Warren * 10 = Length 48 1953f82d89dSTom Warren * 11 = Length 48 Check busy after response 1963f82d89dSTom Warren */ 1973f82d89dSTom Warren if (!(cmd->resp_type & MMC_RSP_PRESENT)) 1983f82d89dSTom Warren flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_NO_RESPONSE; 1993f82d89dSTom Warren else if (cmd->resp_type & MMC_RSP_136) 2003f82d89dSTom Warren flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_136; 2013f82d89dSTom Warren else if (cmd->resp_type & MMC_RSP_BUSY) 2023f82d89dSTom Warren flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48_BUSY; 2033f82d89dSTom Warren else 2043f82d89dSTom Warren flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48; 2053f82d89dSTom Warren 2063f82d89dSTom Warren if (cmd->resp_type & MMC_RSP_CRC) 2073f82d89dSTom Warren flags |= TEGRA_MMC_TRNMOD_CMD_CRC_CHECK; 2083f82d89dSTom Warren if (cmd->resp_type & MMC_RSP_OPCODE) 2093f82d89dSTom Warren flags |= TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK; 2103f82d89dSTom Warren if (data) 2113f82d89dSTom Warren flags |= TEGRA_MMC_TRNMOD_DATA_PRESENT_SELECT_DATA_TRANSFER; 2123f82d89dSTom Warren 2133f82d89dSTom Warren debug("cmd: %d\n", cmd->cmdidx); 2143f82d89dSTom Warren 2153f82d89dSTom Warren writew((cmd->cmdidx << 8) | flags, &host->reg->cmdreg); 2163f82d89dSTom Warren 2173f82d89dSTom Warren for (i = 0; i < retry; i++) { 2183f82d89dSTom Warren mask = readl(&host->reg->norintsts); 2193f82d89dSTom Warren /* Command Complete */ 2203f82d89dSTom Warren if (mask & TEGRA_MMC_NORINTSTS_CMD_COMPLETE) { 2213f82d89dSTom Warren if (!data) 2223f82d89dSTom Warren writel(mask, &host->reg->norintsts); 2233f82d89dSTom Warren break; 2243f82d89dSTom Warren } 2253f82d89dSTom Warren } 2263f82d89dSTom Warren 2273f82d89dSTom Warren if (i == retry) { 2283f82d89dSTom Warren printf("%s: waiting for status update\n", __func__); 2293f82d89dSTom Warren writel(mask, &host->reg->norintsts); 230915ffa52SJaehoon Chung return -ETIMEDOUT; 2313f82d89dSTom Warren } 2323f82d89dSTom Warren 2333f82d89dSTom Warren if (mask & TEGRA_MMC_NORINTSTS_CMD_TIMEOUT) { 2343f82d89dSTom Warren /* Timeout Error */ 2353f82d89dSTom Warren debug("timeout: %08x cmd %d\n", mask, cmd->cmdidx); 2363f82d89dSTom Warren writel(mask, &host->reg->norintsts); 237915ffa52SJaehoon Chung return -ETIMEDOUT; 2383f82d89dSTom Warren } else if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) { 2393f82d89dSTom Warren /* Error Interrupt */ 2403f82d89dSTom Warren debug("error: %08x cmd %d\n", mask, cmd->cmdidx); 2413f82d89dSTom Warren writel(mask, &host->reg->norintsts); 2423f82d89dSTom Warren return -1; 2433f82d89dSTom Warren } 2443f82d89dSTom Warren 2453f82d89dSTom Warren if (cmd->resp_type & MMC_RSP_PRESENT) { 2463f82d89dSTom Warren if (cmd->resp_type & MMC_RSP_136) { 2473f82d89dSTom Warren /* CRC is stripped so we need to do some shifting. */ 2483f82d89dSTom Warren for (i = 0; i < 4; i++) { 249c39e2a75SThierry Reding unsigned long offset = 250c39e2a75SThierry Reding (unsigned long)(&host->reg->rspreg3 - i); 2513f82d89dSTom Warren cmd->response[i] = readl(offset) << 8; 2523f82d89dSTom Warren 2533f82d89dSTom Warren if (i != 3) { 2543f82d89dSTom Warren cmd->response[i] |= 2553f82d89dSTom Warren readb(offset - 1); 2563f82d89dSTom Warren } 2573f82d89dSTom Warren debug("cmd->resp[%d]: %08x\n", 2583f82d89dSTom Warren i, cmd->response[i]); 2593f82d89dSTom Warren } 2603f82d89dSTom Warren } else if (cmd->resp_type & MMC_RSP_BUSY) { 2613f82d89dSTom Warren for (i = 0; i < retry; i++) { 2623f82d89dSTom Warren /* PRNTDATA[23:20] : DAT[3:0] Line Signal */ 2633f82d89dSTom Warren if (readl(&host->reg->prnsts) 2643f82d89dSTom Warren & (1 << 20)) /* DAT[0] */ 2653f82d89dSTom Warren break; 2663f82d89dSTom Warren } 2673f82d89dSTom Warren 2683f82d89dSTom Warren if (i == retry) { 2693f82d89dSTom Warren printf("%s: card is still busy\n", __func__); 2703f82d89dSTom Warren writel(mask, &host->reg->norintsts); 271915ffa52SJaehoon Chung return -ETIMEDOUT; 2723f82d89dSTom Warren } 2733f82d89dSTom Warren 2743f82d89dSTom Warren cmd->response[0] = readl(&host->reg->rspreg0); 2753f82d89dSTom Warren debug("cmd->resp[0]: %08x\n", cmd->response[0]); 2763f82d89dSTom Warren } else { 2773f82d89dSTom Warren cmd->response[0] = readl(&host->reg->rspreg0); 2783f82d89dSTom Warren debug("cmd->resp[0]: %08x\n", cmd->response[0]); 2793f82d89dSTom Warren } 2803f82d89dSTom Warren } 2813f82d89dSTom Warren 2823f82d89dSTom Warren if (data) { 2833f82d89dSTom Warren unsigned long start = get_timer(0); 2843f82d89dSTom Warren 2853f82d89dSTom Warren while (1) { 2863f82d89dSTom Warren mask = readl(&host->reg->norintsts); 2873f82d89dSTom Warren 2883f82d89dSTom Warren if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) { 2893f82d89dSTom Warren /* Error Interrupt */ 2903f82d89dSTom Warren writel(mask, &host->reg->norintsts); 2913f82d89dSTom Warren printf("%s: error during transfer: 0x%08x\n", 2923f82d89dSTom Warren __func__, mask); 2933f82d89dSTom Warren return -1; 2943f82d89dSTom Warren } else if (mask & TEGRA_MMC_NORINTSTS_DMA_INTERRUPT) { 2953f82d89dSTom Warren /* 2963f82d89dSTom Warren * DMA Interrupt, restart the transfer where 2973f82d89dSTom Warren * it was interrupted. 2983f82d89dSTom Warren */ 2993f82d89dSTom Warren unsigned int address = readl(&host->reg->sysad); 3003f82d89dSTom Warren 3013f82d89dSTom Warren debug("DMA end\n"); 3023f82d89dSTom Warren writel(TEGRA_MMC_NORINTSTS_DMA_INTERRUPT, 3033f82d89dSTom Warren &host->reg->norintsts); 3043f82d89dSTom Warren writel(address, &host->reg->sysad); 3053f82d89dSTom Warren } else if (mask & TEGRA_MMC_NORINTSTS_XFER_COMPLETE) { 3063f82d89dSTom Warren /* Transfer Complete */ 3073f82d89dSTom Warren debug("r/w is done\n"); 3083f82d89dSTom Warren break; 30909fb7361SMarcel Ziswiler } else if (get_timer(start) > 8000UL) { 3103f82d89dSTom Warren writel(mask, &host->reg->norintsts); 3113f82d89dSTom Warren printf("%s: MMC Timeout\n" 3123f82d89dSTom Warren " Interrupt status 0x%08x\n" 3133f82d89dSTom Warren " Interrupt status enable 0x%08x\n" 3143f82d89dSTom Warren " Interrupt signal enable 0x%08x\n" 3153f82d89dSTom Warren " Present status 0x%08x\n", 3163f82d89dSTom Warren __func__, mask, 3173f82d89dSTom Warren readl(&host->reg->norintstsen), 3183f82d89dSTom Warren readl(&host->reg->norintsigen), 3193f82d89dSTom Warren readl(&host->reg->prnsts)); 3203f82d89dSTom Warren return -1; 3213f82d89dSTom Warren } 3223f82d89dSTom Warren } 3233f82d89dSTom Warren writel(mask, &host->reg->norintsts); 3243f82d89dSTom Warren } 3253f82d89dSTom Warren 3263f82d89dSTom Warren udelay(1000); 3273f82d89dSTom Warren return 0; 3283f82d89dSTom Warren } 3293f82d89dSTom Warren 330ab769f22SPantelis Antoniou static int tegra_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, 33119815399SStephen Warren struct mmc_data *data) 33219815399SStephen Warren { 33319815399SStephen Warren void *buf; 33419815399SStephen Warren unsigned int bbflags; 33519815399SStephen Warren size_t len; 33619815399SStephen Warren struct bounce_buffer bbstate; 33719815399SStephen Warren int ret; 33819815399SStephen Warren 33919815399SStephen Warren if (data) { 34019815399SStephen Warren if (data->flags & MMC_DATA_READ) { 34119815399SStephen Warren buf = data->dest; 34219815399SStephen Warren bbflags = GEN_BB_WRITE; 34319815399SStephen Warren } else { 34419815399SStephen Warren buf = (void *)data->src; 34519815399SStephen Warren bbflags = GEN_BB_READ; 34619815399SStephen Warren } 34719815399SStephen Warren len = data->blocks * data->blocksize; 34819815399SStephen Warren 34919815399SStephen Warren bounce_buffer_start(&bbstate, buf, len, bbflags); 35019815399SStephen Warren } 35119815399SStephen Warren 35219815399SStephen Warren ret = mmc_send_cmd_bounced(mmc, cmd, data, &bbstate); 35319815399SStephen Warren 35419815399SStephen Warren if (data) 35519815399SStephen Warren bounce_buffer_stop(&bbstate); 35619815399SStephen Warren 35719815399SStephen Warren return ret; 35819815399SStephen Warren } 35919815399SStephen Warren 3603f82d89dSTom Warren static void mmc_change_clock(struct mmc_host *host, uint clock) 3613f82d89dSTom Warren { 3623f82d89dSTom Warren int div; 3633f82d89dSTom Warren unsigned short clk; 3643f82d89dSTom Warren unsigned long timeout; 3653f82d89dSTom Warren 3663f82d89dSTom Warren debug(" mmc_change_clock called\n"); 3673f82d89dSTom Warren 3683f82d89dSTom Warren /* 3692d348a16STom Warren * Change Tegra SDMMCx clock divisor here. Source is PLLP_OUT0 3703f82d89dSTom Warren */ 3713f82d89dSTom Warren if (clock == 0) 3723f82d89dSTom Warren goto out; 373c0493076SStephen Warren #ifdef CONFIG_TEGRA186 374c0493076SStephen Warren { 375c0493076SStephen Warren ulong rate = clk_set_rate(&host->clk, clock); 376c0493076SStephen Warren div = (rate + clock - 1) / clock; 377c0493076SStephen Warren } 378c0493076SStephen Warren #else 3793f82d89dSTom Warren clock_adjust_periph_pll_div(host->mmc_id, CLOCK_ID_PERIPH, clock, 3803f82d89dSTom Warren &div); 38139f63332SStephen Warren #endif 3823f82d89dSTom Warren debug("div = %d\n", div); 3833f82d89dSTom Warren 3843f82d89dSTom Warren writew(0, &host->reg->clkcon); 3853f82d89dSTom Warren 3863f82d89dSTom Warren /* 3873f82d89dSTom Warren * CLKCON 3883f82d89dSTom Warren * SELFREQ[15:8] : base clock divided by value 3893f82d89dSTom Warren * ENSDCLK[2] : SD Clock Enable 3903f82d89dSTom Warren * STBLINTCLK[1] : Internal Clock Stable 3913f82d89dSTom Warren * ENINTCLK[0] : Internal Clock Enable 3923f82d89dSTom Warren */ 3933f82d89dSTom Warren div >>= 1; 3943f82d89dSTom Warren clk = ((div << TEGRA_MMC_CLKCON_SDCLK_FREQ_SEL_SHIFT) | 3953f82d89dSTom Warren TEGRA_MMC_CLKCON_INTERNAL_CLOCK_ENABLE); 3963f82d89dSTom Warren writew(clk, &host->reg->clkcon); 3973f82d89dSTom Warren 3983f82d89dSTom Warren /* Wait max 10 ms */ 3993f82d89dSTom Warren timeout = 10; 4003f82d89dSTom Warren while (!(readw(&host->reg->clkcon) & 4013f82d89dSTom Warren TEGRA_MMC_CLKCON_INTERNAL_CLOCK_STABLE)) { 4023f82d89dSTom Warren if (timeout == 0) { 4033f82d89dSTom Warren printf("%s: timeout error\n", __func__); 4043f82d89dSTom Warren return; 4053f82d89dSTom Warren } 4063f82d89dSTom Warren timeout--; 4073f82d89dSTom Warren udelay(1000); 4083f82d89dSTom Warren } 4093f82d89dSTom Warren 4103f82d89dSTom Warren clk |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; 4113f82d89dSTom Warren writew(clk, &host->reg->clkcon); 4123f82d89dSTom Warren 4133f82d89dSTom Warren debug("mmc_change_clock: clkcon = %08X\n", clk); 4143f82d89dSTom Warren 4153f82d89dSTom Warren out: 4163f82d89dSTom Warren host->clock = clock; 4173f82d89dSTom Warren } 4183f82d89dSTom Warren 419ab769f22SPantelis Antoniou static void tegra_mmc_set_ios(struct mmc *mmc) 4203f82d89dSTom Warren { 4213f82d89dSTom Warren struct mmc_host *host = mmc->priv; 4223f82d89dSTom Warren unsigned char ctrl; 4233f82d89dSTom Warren debug(" mmc_set_ios called\n"); 4243f82d89dSTom Warren 4253f82d89dSTom Warren debug("bus_width: %x, clock: %d\n", mmc->bus_width, mmc->clock); 4263f82d89dSTom Warren 4273f82d89dSTom Warren /* Change clock first */ 4283f82d89dSTom Warren mmc_change_clock(host, mmc->clock); 4293f82d89dSTom Warren 4303f82d89dSTom Warren ctrl = readb(&host->reg->hostctl); 4313f82d89dSTom Warren 4323f82d89dSTom Warren /* 4333f82d89dSTom Warren * WIDE8[5] 4343f82d89dSTom Warren * 0 = Depend on WIDE4 4353f82d89dSTom Warren * 1 = 8-bit mode 4363f82d89dSTom Warren * WIDE4[1] 4373f82d89dSTom Warren * 1 = 4-bit mode 4383f82d89dSTom Warren * 0 = 1-bit mode 4393f82d89dSTom Warren */ 4403f82d89dSTom Warren if (mmc->bus_width == 8) 4413f82d89dSTom Warren ctrl |= (1 << 5); 4423f82d89dSTom Warren else if (mmc->bus_width == 4) 4433f82d89dSTom Warren ctrl |= (1 << 1); 4443f82d89dSTom Warren else 4453f82d89dSTom Warren ctrl &= ~(1 << 1); 4463f82d89dSTom Warren 4473f82d89dSTom Warren writeb(ctrl, &host->reg->hostctl); 4483f82d89dSTom Warren debug("mmc_set_ios: hostctl = %08X\n", ctrl); 4493f82d89dSTom Warren } 4503f82d89dSTom Warren 4512d348a16STom Warren static void mmc_reset(struct mmc_host *host, struct mmc *mmc) 4523f82d89dSTom Warren { 4533f82d89dSTom Warren unsigned int timeout; 4543f82d89dSTom Warren debug(" mmc_reset called\n"); 4553f82d89dSTom Warren 4563f82d89dSTom Warren /* 4573f82d89dSTom Warren * RSTALL[0] : Software reset for all 4583f82d89dSTom Warren * 1 = reset 4593f82d89dSTom Warren * 0 = work 4603f82d89dSTom Warren */ 4613f82d89dSTom Warren writeb(TEGRA_MMC_SWRST_SW_RESET_FOR_ALL, &host->reg->swrst); 4623f82d89dSTom Warren 4633f82d89dSTom Warren host->clock = 0; 4643f82d89dSTom Warren 4653f82d89dSTom Warren /* Wait max 100 ms */ 4663f82d89dSTom Warren timeout = 100; 4673f82d89dSTom Warren 4683f82d89dSTom Warren /* hw clears the bit when it's done */ 4693f82d89dSTom Warren while (readb(&host->reg->swrst) & TEGRA_MMC_SWRST_SW_RESET_FOR_ALL) { 4703f82d89dSTom Warren if (timeout == 0) { 4713f82d89dSTom Warren printf("%s: timeout error\n", __func__); 4723f82d89dSTom Warren return; 4733f82d89dSTom Warren } 4743f82d89dSTom Warren timeout--; 4753f82d89dSTom Warren udelay(1000); 4763f82d89dSTom Warren } 4772d348a16STom Warren 4782d348a16STom Warren /* Set SD bus voltage & enable bus power */ 47993bfd616SPantelis Antoniou mmc_set_power(host, fls(mmc->cfg->voltages) - 1); 4802d348a16STom Warren debug("%s: power control = %02X, host control = %02X\n", __func__, 4812d348a16STom Warren readb(&host->reg->pwrcon), readb(&host->reg->hostctl)); 4822d348a16STom Warren 4832d348a16STom Warren /* Make sure SDIO pads are set up */ 4842d348a16STom Warren pad_init_mmc(host); 4853f82d89dSTom Warren } 4863f82d89dSTom Warren 487ab769f22SPantelis Antoniou static int tegra_mmc_core_init(struct mmc *mmc) 4883f82d89dSTom Warren { 48993bfd616SPantelis Antoniou struct mmc_host *host = mmc->priv; 4903f82d89dSTom Warren unsigned int mask; 4913f82d89dSTom Warren debug(" mmc_core_init called\n"); 4923f82d89dSTom Warren 4932d348a16STom Warren mmc_reset(host, mmc); 4943f82d89dSTom Warren 4953f82d89dSTom Warren host->version = readw(&host->reg->hcver); 4963f82d89dSTom Warren debug("host version = %x\n", host->version); 4973f82d89dSTom Warren 4983f82d89dSTom Warren /* mask all */ 4993f82d89dSTom Warren writel(0xffffffff, &host->reg->norintstsen); 5003f82d89dSTom Warren writel(0xffffffff, &host->reg->norintsigen); 5013f82d89dSTom Warren 5023f82d89dSTom Warren writeb(0xe, &host->reg->timeoutcon); /* TMCLK * 2^27 */ 5033f82d89dSTom Warren /* 5043f82d89dSTom Warren * NORMAL Interrupt Status Enable Register init 5053f82d89dSTom Warren * [5] ENSTABUFRDRDY : Buffer Read Ready Status Enable 5063f82d89dSTom Warren * [4] ENSTABUFWTRDY : Buffer write Ready Status Enable 5073f82d89dSTom Warren * [3] ENSTADMAINT : DMA boundary interrupt 5083f82d89dSTom Warren * [1] ENSTASTANSCMPLT : Transfre Complete Status Enable 5093f82d89dSTom Warren * [0] ENSTACMDCMPLT : Command Complete Status Enable 5103f82d89dSTom Warren */ 5113f82d89dSTom Warren mask = readl(&host->reg->norintstsen); 5123f82d89dSTom Warren mask &= ~(0xffff); 5133f82d89dSTom Warren mask |= (TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | 5143f82d89dSTom Warren TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | 5153f82d89dSTom Warren TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT | 5163f82d89dSTom Warren TEGRA_MMC_NORINTSTSEN_BUFFER_WRITE_READY | 5173f82d89dSTom Warren TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY); 5183f82d89dSTom Warren writel(mask, &host->reg->norintstsen); 5193f82d89dSTom Warren 5203f82d89dSTom Warren /* 5213f82d89dSTom Warren * NORMAL Interrupt Signal Enable Register init 5223f82d89dSTom Warren * [1] ENSTACMDCMPLT : Transfer Complete Signal Enable 5233f82d89dSTom Warren */ 5243f82d89dSTom Warren mask = readl(&host->reg->norintsigen); 5253f82d89dSTom Warren mask &= ~(0xffff); 5263f82d89dSTom Warren mask |= TEGRA_MMC_NORINTSIGEN_XFER_COMPLETE; 5273f82d89dSTom Warren writel(mask, &host->reg->norintsigen); 5283f82d89dSTom Warren 5293f82d89dSTom Warren return 0; 5303f82d89dSTom Warren } 5313f82d89dSTom Warren 53219d7bf3dSJeroen Hofstee static int tegra_mmc_getcd(struct mmc *mmc) 5333f82d89dSTom Warren { 53493bfd616SPantelis Antoniou struct mmc_host *host = mmc->priv; 5353f82d89dSTom Warren 53629f3e3f2STom Warren debug("tegra_mmc_getcd called\n"); 5373f82d89dSTom Warren 5380347960bSSimon Glass if (dm_gpio_is_valid(&host->cd_gpio)) 5390347960bSSimon Glass return dm_gpio_get_value(&host->cd_gpio); 5403f82d89dSTom Warren 5413f82d89dSTom Warren return 1; 5423f82d89dSTom Warren } 5433f82d89dSTom Warren 544ab769f22SPantelis Antoniou static const struct mmc_ops tegra_mmc_ops = { 545ab769f22SPantelis Antoniou .send_cmd = tegra_mmc_send_cmd, 546ab769f22SPantelis Antoniou .set_ios = tegra_mmc_set_ios, 547ab769f22SPantelis Antoniou .init = tegra_mmc_core_init, 548ab769f22SPantelis Antoniou .getcd = tegra_mmc_getcd, 549ab769f22SPantelis Antoniou }; 550ab769f22SPantelis Antoniou 551707ac1adSSimon Glass static int do_mmc_init(int dev_index, bool removable) 5523f82d89dSTom Warren { 5533f82d89dSTom Warren struct mmc_host *host; 5543f82d89dSTom Warren struct mmc *mmc; 555c0493076SStephen Warren #ifdef CONFIG_TEGRA186 556c0493076SStephen Warren int ret; 557c0493076SStephen Warren #endif 5583f82d89dSTom Warren 559c9aa831eSTom Warren /* DT should have been read & host config filled in */ 5603f82d89dSTom Warren host = &mmc_host[dev_index]; 561c9aa831eSTom Warren if (!host->enabled) 562c9aa831eSTom Warren return -1; 563c9aa831eSTom Warren 5640347960bSSimon Glass debug(" do_mmc_init: index %d, bus width %d pwr_gpio %d cd_gpio %d\n", 5650347960bSSimon Glass dev_index, host->width, gpio_get_number(&host->pwr_gpio), 5660347960bSSimon Glass gpio_get_number(&host->cd_gpio)); 5673f82d89dSTom Warren 5683f82d89dSTom Warren host->clock = 0; 569c0493076SStephen Warren 570c0493076SStephen Warren #ifdef CONFIG_TEGRA186 571c0493076SStephen Warren ret = reset_assert(&host->reset_ctl); 572c0493076SStephen Warren if (ret) 573c0493076SStephen Warren return ret; 574c0493076SStephen Warren ret = clk_enable(&host->clk); 575c0493076SStephen Warren if (ret) 576c0493076SStephen Warren return ret; 577c0493076SStephen Warren ret = clk_set_rate(&host->clk, 20000000); 578c0493076SStephen Warren if (IS_ERR_VALUE(ret)) 579c0493076SStephen Warren return ret; 580c0493076SStephen Warren ret = reset_deassert(&host->reset_ctl); 581c0493076SStephen Warren if (ret) 582c0493076SStephen Warren return ret; 583c0493076SStephen Warren #else 5843f82d89dSTom Warren clock_start_periph_pll(host->mmc_id, CLOCK_ID_PERIPH, 20000000); 58539f63332SStephen Warren #endif 5863f82d89dSTom Warren 5870347960bSSimon Glass if (dm_gpio_is_valid(&host->pwr_gpio)) 5880347960bSSimon Glass dm_gpio_set_value(&host->pwr_gpio, 1); 5893f82d89dSTom Warren 59093bfd616SPantelis Antoniou memset(&host->cfg, 0, sizeof(host->cfg)); 5913f82d89dSTom Warren 59293bfd616SPantelis Antoniou host->cfg.name = "Tegra SD/MMC"; 59393bfd616SPantelis Antoniou host->cfg.ops = &tegra_mmc_ops; 5943f82d89dSTom Warren 59593bfd616SPantelis Antoniou host->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; 59693bfd616SPantelis Antoniou host->cfg.host_caps = 0; 597c9aa831eSTom Warren if (host->width == 8) 59893bfd616SPantelis Antoniou host->cfg.host_caps |= MMC_MODE_8BIT; 599c9aa831eSTom Warren if (host->width >= 4) 60093bfd616SPantelis Antoniou host->cfg.host_caps |= MMC_MODE_4BIT; 6015a20397bSRob Herring host->cfg.host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; 6023f82d89dSTom Warren 6033f82d89dSTom Warren /* 6043f82d89dSTom Warren * min freq is for card identification, and is the highest 6053f82d89dSTom Warren * low-speed SDIO card frequency (actually 400KHz) 6063f82d89dSTom Warren * max freq is highest HS eMMC clock as per the SD/MMC spec 6073f82d89dSTom Warren * (actually 52MHz) 6083f82d89dSTom Warren */ 60993bfd616SPantelis Antoniou host->cfg.f_min = 375000; 61093bfd616SPantelis Antoniou host->cfg.f_max = 48000000; 6113f82d89dSTom Warren 61293bfd616SPantelis Antoniou host->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; 61393bfd616SPantelis Antoniou 61493bfd616SPantelis Antoniou mmc = mmc_create(&host->cfg, host); 615707ac1adSSimon Glass mmc->block_dev.removable = removable; 61693bfd616SPantelis Antoniou if (mmc == NULL) 61793bfd616SPantelis Antoniou return -1; 6183f82d89dSTom Warren 6193f82d89dSTom Warren return 0; 6203f82d89dSTom Warren } 621c9aa831eSTom Warren 622c9aa831eSTom Warren /** 623c9aa831eSTom Warren * Get the host address and peripheral ID for a node. 624c9aa831eSTom Warren * 625c9aa831eSTom Warren * @param blob fdt blob 626c9aa831eSTom Warren * @param node Device index (0-3) 627c9aa831eSTom Warren * @param host Structure to fill in (reg, width, mmc_id) 628c9aa831eSTom Warren */ 629707ac1adSSimon Glass static int mmc_get_config(const void *blob, int node, struct mmc_host *host, 630707ac1adSSimon Glass bool *removablep) 631c9aa831eSTom Warren { 632c9aa831eSTom Warren debug("%s: node = %d\n", __func__, node); 633c9aa831eSTom Warren 634c9aa831eSTom Warren host->enabled = fdtdec_get_is_enabled(blob, node); 635c9aa831eSTom Warren 636c9aa831eSTom Warren host->reg = (struct tegra_mmc *)fdtdec_get_addr(blob, node, "reg"); 637c9aa831eSTom Warren if ((fdt_addr_t)host->reg == FDT_ADDR_T_NONE) { 638c9aa831eSTom Warren debug("%s: no sdmmc base reg info found\n", __func__); 639c9aa831eSTom Warren return -FDT_ERR_NOTFOUND; 640c9aa831eSTom Warren } 641c9aa831eSTom Warren 642c0493076SStephen Warren #ifdef CONFIG_TEGRA186 643c0493076SStephen Warren { 644c0493076SStephen Warren /* 645c0493076SStephen Warren * FIXME: This variable should go away when the MMC device 646c0493076SStephen Warren * actually is a udevice. 647c0493076SStephen Warren */ 648c0493076SStephen Warren struct udevice dev; 649c0493076SStephen Warren int ret; 650c0493076SStephen Warren dev.of_offset = node; 651*eb3f68afSStephen Warren ret = reset_get_by_name(&dev, "sdhci", &host->reset_ctl); 652c0493076SStephen Warren if (ret) { 653*eb3f68afSStephen Warren debug("reset_get_by_name() failed: %d\n", ret); 654c0493076SStephen Warren return ret; 655c0493076SStephen Warren } 656*eb3f68afSStephen Warren ret = clk_get_by_index(&dev, 0, &host->clk); 657c0493076SStephen Warren if (ret) { 658c0493076SStephen Warren debug("clk_get_by_index() failed: %d\n", ret); 659c0493076SStephen Warren return ret; 660c0493076SStephen Warren } 661c0493076SStephen Warren } 662c0493076SStephen Warren #else 663c9aa831eSTom Warren host->mmc_id = clock_decode_periph_id(blob, node); 664c9aa831eSTom Warren if (host->mmc_id == PERIPH_ID_NONE) { 665c9aa831eSTom Warren debug("%s: could not decode periph id\n", __func__); 666c9aa831eSTom Warren return -FDT_ERR_NOTFOUND; 667c9aa831eSTom Warren } 66839f63332SStephen Warren #endif 669c9aa831eSTom Warren 670c9aa831eSTom Warren /* 671c9aa831eSTom Warren * NOTE: mmc->bus_width is determined by mmc.c dynamically. 672c9aa831eSTom Warren * TBD: Override it with this value? 673c9aa831eSTom Warren */ 674c9aa831eSTom Warren host->width = fdtdec_get_int(blob, node, "bus-width", 0); 675c9aa831eSTom Warren if (!host->width) 676c9aa831eSTom Warren debug("%s: no sdmmc width found\n", __func__); 677c9aa831eSTom Warren 678c9aa831eSTom Warren /* These GPIOs are optional */ 6790347960bSSimon Glass gpio_request_by_name_nodev(blob, node, "cd-gpios", 0, &host->cd_gpio, 6800347960bSSimon Glass GPIOD_IS_IN); 6810347960bSSimon Glass gpio_request_by_name_nodev(blob, node, "wp-gpios", 0, &host->wp_gpio, 6820347960bSSimon Glass GPIOD_IS_IN); 6830347960bSSimon Glass gpio_request_by_name_nodev(blob, node, "power-gpios", 0, 6840347960bSSimon Glass &host->pwr_gpio, GPIOD_IS_OUT); 685707ac1adSSimon Glass *removablep = !fdtdec_get_bool(blob, node, "non-removable"); 686c9aa831eSTom Warren 687c9aa831eSTom Warren debug("%s: found controller at %p, width = %d, periph_id = %d\n", 68839f63332SStephen Warren __func__, host->reg, host->width, 68939f63332SStephen Warren #ifndef CONFIG_TEGRA186 69039f63332SStephen Warren host->mmc_id 69139f63332SStephen Warren #else 69239f63332SStephen Warren -1 69339f63332SStephen Warren #endif 69439f63332SStephen Warren ); 695c9aa831eSTom Warren return 0; 696c9aa831eSTom Warren } 697c9aa831eSTom Warren 698c9aa831eSTom Warren /* 699c9aa831eSTom Warren * Process a list of nodes, adding them to our list of SDMMC ports. 700c9aa831eSTom Warren * 701c9aa831eSTom Warren * @param blob fdt blob 702c9aa831eSTom Warren * @param node_list list of nodes to process (any <=0 are ignored) 703c9aa831eSTom Warren * @param count number of nodes to process 704c9aa831eSTom Warren * @return 0 if ok, -1 on error 705c9aa831eSTom Warren */ 706c9aa831eSTom Warren static int process_nodes(const void *blob, int node_list[], int count) 707c9aa831eSTom Warren { 708c9aa831eSTom Warren struct mmc_host *host; 709707ac1adSSimon Glass bool removable; 710c9aa831eSTom Warren int i, node; 711c9aa831eSTom Warren 712c9aa831eSTom Warren debug("%s: count = %d\n", __func__, count); 713c9aa831eSTom Warren 714c9aa831eSTom Warren /* build mmc_host[] for each controller */ 715c9aa831eSTom Warren for (i = 0; i < count; i++) { 716c9aa831eSTom Warren node = node_list[i]; 717c9aa831eSTom Warren if (node <= 0) 718c9aa831eSTom Warren continue; 719c9aa831eSTom Warren 720c9aa831eSTom Warren host = &mmc_host[i]; 721c9aa831eSTom Warren host->id = i; 722c9aa831eSTom Warren 723707ac1adSSimon Glass if (mmc_get_config(blob, node, host, &removable)) { 724c9aa831eSTom Warren printf("%s: failed to decode dev %d\n", __func__, i); 725c9aa831eSTom Warren return -1; 726c9aa831eSTom Warren } 727707ac1adSSimon Glass do_mmc_init(i, removable); 728c9aa831eSTom Warren } 729c9aa831eSTom Warren return 0; 730c9aa831eSTom Warren } 731c9aa831eSTom Warren 732c9aa831eSTom Warren void tegra_mmc_init(void) 733c9aa831eSTom Warren { 734f175603fSStephen Warren int node_list[CONFIG_SYS_MMC_MAX_DEVICE], count; 735c9aa831eSTom Warren const void *blob = gd->fdt_blob; 736c9aa831eSTom Warren debug("%s entry\n", __func__); 737c9aa831eSTom Warren 73839f63332SStephen Warren /* See if any Tegra186 MMC controllers are present */ 73939f63332SStephen Warren count = fdtdec_find_aliases_for_id(blob, "sdhci", 74039f63332SStephen Warren COMPAT_NVIDIA_TEGRA186_SDMMC, node_list, 74139f63332SStephen Warren CONFIG_SYS_MMC_MAX_DEVICE); 74239f63332SStephen Warren debug("%s: count of Tegra186 sdhci nodes is %d\n", __func__, count); 74339f63332SStephen Warren if (process_nodes(blob, node_list, count)) { 74439f63332SStephen Warren printf("%s: Error processing T186 mmc node(s)!\n", __func__); 74539f63332SStephen Warren return; 74639f63332SStephen Warren } 74739f63332SStephen Warren 7487aaa5a60STom Warren /* See if any Tegra210 MMC controllers are present */ 7497aaa5a60STom Warren count = fdtdec_find_aliases_for_id(blob, "sdhci", 7507aaa5a60STom Warren COMPAT_NVIDIA_TEGRA210_SDMMC, node_list, 7517aaa5a60STom Warren CONFIG_SYS_MMC_MAX_DEVICE); 7527aaa5a60STom Warren debug("%s: count of Tegra210 sdhci nodes is %d\n", __func__, count); 7537aaa5a60STom Warren if (process_nodes(blob, node_list, count)) { 754e05ab0daSSimon Glass printf("%s: Error processing T210 mmc node(s)!\n", __func__); 7557aaa5a60STom Warren return; 7567aaa5a60STom Warren } 7577aaa5a60STom Warren 758a73ca478SStephen Warren /* See if any Tegra124 MMC controllers are present */ 759a73ca478SStephen Warren count = fdtdec_find_aliases_for_id(blob, "sdhci", 760f175603fSStephen Warren COMPAT_NVIDIA_TEGRA124_SDMMC, node_list, 761f175603fSStephen Warren CONFIG_SYS_MMC_MAX_DEVICE); 762a73ca478SStephen Warren debug("%s: count of Tegra124 sdhci nodes is %d\n", __func__, count); 763a73ca478SStephen Warren if (process_nodes(blob, node_list, count)) { 764e05ab0daSSimon Glass printf("%s: Error processing T124 mmc node(s)!\n", __func__); 765a73ca478SStephen Warren return; 766a73ca478SStephen Warren } 767a73ca478SStephen Warren 7682d348a16STom Warren /* See if any Tegra30 MMC controllers are present */ 7692d348a16STom Warren count = fdtdec_find_aliases_for_id(blob, "sdhci", 770f175603fSStephen Warren COMPAT_NVIDIA_TEGRA30_SDMMC, node_list, 771f175603fSStephen Warren CONFIG_SYS_MMC_MAX_DEVICE); 7722d348a16STom Warren debug("%s: count of T30 sdhci nodes is %d\n", __func__, count); 7732d348a16STom Warren if (process_nodes(blob, node_list, count)) { 7742d348a16STom Warren printf("%s: Error processing T30 mmc node(s)!\n", __func__); 7752d348a16STom Warren return; 7762d348a16STom Warren } 7772d348a16STom Warren 7782d348a16STom Warren /* Now look for any Tegra20 MMC controllers */ 779c9aa831eSTom Warren count = fdtdec_find_aliases_for_id(blob, "sdhci", 780f175603fSStephen Warren COMPAT_NVIDIA_TEGRA20_SDMMC, node_list, 781f175603fSStephen Warren CONFIG_SYS_MMC_MAX_DEVICE); 7822d348a16STom Warren debug("%s: count of T20 sdhci nodes is %d\n", __func__, count); 783c9aa831eSTom Warren if (process_nodes(blob, node_list, count)) { 7842d348a16STom Warren printf("%s: Error processing T20 mmc node(s)!\n", __func__); 785c9aa831eSTom Warren return; 786c9aa831eSTom Warren } 787c9aa831eSTom Warren } 788