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> 123f82d89dSTom Warren #include <asm/gpio.h> 133f82d89dSTom Warren #include <asm/io.h> 143f82d89dSTom Warren #include <asm/arch/clock.h> 15150c2493STom Warren #include <asm/arch-tegra/clk_rst.h> 1619d7bf3dSJeroen Hofstee #include <asm/arch-tegra/mmc.h> 17150c2493STom Warren #include <asm/arch-tegra/tegra_mmc.h> 18150c2493STom Warren #include <mmc.h> 193f82d89dSTom Warren 20c9aa831eSTom Warren DECLARE_GLOBAL_DATA_PTR; 213f82d89dSTom Warren 22f175603fSStephen Warren struct mmc_host mmc_host[CONFIG_SYS_MMC_MAX_DEVICE]; 233f82d89dSTom Warren 240f925822SMasahiro Yamada #if !CONFIG_IS_ENABLED(OF_CONTROL) 25c9aa831eSTom Warren #error "Please enable device tree support to use this driver" 26c9aa831eSTom Warren #endif 273f82d89dSTom Warren 282d348a16STom Warren static void mmc_set_power(struct mmc_host *host, unsigned short power) 292d348a16STom Warren { 302d348a16STom Warren u8 pwr = 0; 312d348a16STom Warren debug("%s: power = %x\n", __func__, power); 322d348a16STom Warren 332d348a16STom Warren if (power != (unsigned short)-1) { 342d348a16STom Warren switch (1 << power) { 352d348a16STom Warren case MMC_VDD_165_195: 362d348a16STom Warren pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V1_8; 372d348a16STom Warren break; 382d348a16STom Warren case MMC_VDD_29_30: 392d348a16STom Warren case MMC_VDD_30_31: 402d348a16STom Warren pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_0; 412d348a16STom Warren break; 422d348a16STom Warren case MMC_VDD_32_33: 432d348a16STom Warren case MMC_VDD_33_34: 442d348a16STom Warren pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_3; 452d348a16STom Warren break; 462d348a16STom Warren } 472d348a16STom Warren } 482d348a16STom Warren debug("%s: pwr = %X\n", __func__, pwr); 492d348a16STom Warren 502d348a16STom Warren /* Set the bus voltage first (if any) */ 512d348a16STom Warren writeb(pwr, &host->reg->pwrcon); 522d348a16STom Warren if (pwr == 0) 532d348a16STom Warren return; 542d348a16STom Warren 552d348a16STom Warren /* Now enable bus power */ 562d348a16STom Warren pwr |= TEGRA_MMC_PWRCTL_SD_BUS_POWER; 572d348a16STom Warren writeb(pwr, &host->reg->pwrcon); 582d348a16STom Warren } 592d348a16STom Warren 6019815399SStephen Warren static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data, 6119815399SStephen Warren struct bounce_buffer *bbstate) 623f82d89dSTom Warren { 633f82d89dSTom Warren unsigned char ctrl; 643f82d89dSTom Warren 653f82d89dSTom Warren 6619815399SStephen Warren debug("buf: %p (%p), data->blocks: %u, data->blocksize: %u\n", 6719815399SStephen Warren bbstate->bounce_buffer, bbstate->user_buffer, data->blocks, 6819815399SStephen Warren data->blocksize); 6919815399SStephen Warren 70c39e2a75SThierry Reding writel((u32)(unsigned long)bbstate->bounce_buffer, &host->reg->sysad); 713f82d89dSTom Warren /* 723f82d89dSTom Warren * DMASEL[4:3] 733f82d89dSTom Warren * 00 = Selects SDMA 743f82d89dSTom Warren * 01 = Reserved 753f82d89dSTom Warren * 10 = Selects 32-bit Address ADMA2 763f82d89dSTom Warren * 11 = Selects 64-bit Address ADMA2 773f82d89dSTom Warren */ 783f82d89dSTom Warren ctrl = readb(&host->reg->hostctl); 793f82d89dSTom Warren ctrl &= ~TEGRA_MMC_HOSTCTL_DMASEL_MASK; 803f82d89dSTom Warren ctrl |= TEGRA_MMC_HOSTCTL_DMASEL_SDMA; 813f82d89dSTom Warren writeb(ctrl, &host->reg->hostctl); 823f82d89dSTom Warren 833f82d89dSTom Warren /* We do not handle DMA boundaries, so set it to max (512 KiB) */ 843f82d89dSTom Warren writew((7 << 12) | (data->blocksize & 0xFFF), &host->reg->blksize); 853f82d89dSTom Warren writew(data->blocks, &host->reg->blkcnt); 863f82d89dSTom Warren } 873f82d89dSTom Warren 883f82d89dSTom Warren static void mmc_set_transfer_mode(struct mmc_host *host, struct mmc_data *data) 893f82d89dSTom Warren { 903f82d89dSTom Warren unsigned short mode; 913f82d89dSTom Warren debug(" mmc_set_transfer_mode called\n"); 923f82d89dSTom Warren /* 933f82d89dSTom Warren * TRNMOD 943f82d89dSTom Warren * MUL1SIN0[5] : Multi/Single Block Select 953f82d89dSTom Warren * RD1WT0[4] : Data Transfer Direction Select 963f82d89dSTom Warren * 1 = read 973f82d89dSTom Warren * 0 = write 983f82d89dSTom Warren * ENACMD12[2] : Auto CMD12 Enable 993f82d89dSTom Warren * ENBLKCNT[1] : Block Count Enable 1003f82d89dSTom Warren * ENDMA[0] : DMA Enable 1013f82d89dSTom Warren */ 1023f82d89dSTom Warren mode = (TEGRA_MMC_TRNMOD_DMA_ENABLE | 1033f82d89dSTom Warren TEGRA_MMC_TRNMOD_BLOCK_COUNT_ENABLE); 1043f82d89dSTom Warren 1053f82d89dSTom Warren if (data->blocks > 1) 1063f82d89dSTom Warren mode |= TEGRA_MMC_TRNMOD_MULTI_BLOCK_SELECT; 1073f82d89dSTom Warren 1083f82d89dSTom Warren if (data->flags & MMC_DATA_READ) 1093f82d89dSTom Warren mode |= TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ; 1103f82d89dSTom Warren 1113f82d89dSTom Warren writew(mode, &host->reg->trnmod); 1123f82d89dSTom Warren } 1133f82d89dSTom Warren 1143f82d89dSTom Warren static int mmc_wait_inhibit(struct mmc_host *host, 1153f82d89dSTom Warren struct mmc_cmd *cmd, 1163f82d89dSTom Warren struct mmc_data *data, 1173f82d89dSTom Warren unsigned int timeout) 1183f82d89dSTom Warren { 1193f82d89dSTom Warren /* 1203f82d89dSTom Warren * PRNSTS 1213f82d89dSTom Warren * CMDINHDAT[1] : Command Inhibit (DAT) 1223f82d89dSTom Warren * CMDINHCMD[0] : Command Inhibit (CMD) 1233f82d89dSTom Warren */ 1243f82d89dSTom Warren unsigned int mask = TEGRA_MMC_PRNSTS_CMD_INHIBIT_CMD; 1253f82d89dSTom Warren 1263f82d89dSTom Warren /* 1273f82d89dSTom Warren * We shouldn't wait for data inhibit for stop commands, even 1283f82d89dSTom Warren * though they might use busy signaling 1293f82d89dSTom Warren */ 1303f82d89dSTom Warren if ((data == NULL) && (cmd->resp_type & MMC_RSP_BUSY)) 1313f82d89dSTom Warren mask |= TEGRA_MMC_PRNSTS_CMD_INHIBIT_DAT; 1323f82d89dSTom Warren 1333f82d89dSTom Warren while (readl(&host->reg->prnsts) & mask) { 1343f82d89dSTom Warren if (timeout == 0) { 1353f82d89dSTom Warren printf("%s: timeout error\n", __func__); 1363f82d89dSTom Warren return -1; 1373f82d89dSTom Warren } 1383f82d89dSTom Warren timeout--; 1393f82d89dSTom Warren udelay(1000); 1403f82d89dSTom Warren } 1413f82d89dSTom Warren 1423f82d89dSTom Warren return 0; 1433f82d89dSTom Warren } 1443f82d89dSTom Warren 14519815399SStephen Warren static int mmc_send_cmd_bounced(struct mmc *mmc, struct mmc_cmd *cmd, 14619815399SStephen Warren struct mmc_data *data, struct bounce_buffer *bbstate) 1473f82d89dSTom Warren { 14893bfd616SPantelis Antoniou struct mmc_host *host = mmc->priv; 1493f82d89dSTom Warren int flags, i; 1503f82d89dSTom Warren int result; 1513f82d89dSTom Warren unsigned int mask = 0; 1523f82d89dSTom Warren unsigned int retry = 0x100000; 1533f82d89dSTom Warren debug(" mmc_send_cmd called\n"); 1543f82d89dSTom Warren 1553f82d89dSTom Warren result = mmc_wait_inhibit(host, cmd, data, 10 /* ms */); 1563f82d89dSTom Warren 1573f82d89dSTom Warren if (result < 0) 1583f82d89dSTom Warren return result; 1593f82d89dSTom Warren 1603f82d89dSTom Warren if (data) 16119815399SStephen Warren mmc_prepare_data(host, data, bbstate); 1623f82d89dSTom Warren 1633f82d89dSTom Warren debug("cmd->arg: %08x\n", cmd->cmdarg); 1643f82d89dSTom Warren writel(cmd->cmdarg, &host->reg->argument); 1653f82d89dSTom Warren 1663f82d89dSTom Warren if (data) 1673f82d89dSTom Warren mmc_set_transfer_mode(host, data); 1683f82d89dSTom Warren 1693f82d89dSTom Warren if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY)) 1703f82d89dSTom Warren return -1; 1713f82d89dSTom Warren 1723f82d89dSTom Warren /* 1733f82d89dSTom Warren * CMDREG 1743f82d89dSTom Warren * CMDIDX[13:8] : Command index 1753f82d89dSTom Warren * DATAPRNT[5] : Data Present Select 1763f82d89dSTom Warren * ENCMDIDX[4] : Command Index Check Enable 1773f82d89dSTom Warren * ENCMDCRC[3] : Command CRC Check Enable 1783f82d89dSTom Warren * RSPTYP[1:0] 1793f82d89dSTom Warren * 00 = No Response 1803f82d89dSTom Warren * 01 = Length 136 1813f82d89dSTom Warren * 10 = Length 48 1823f82d89dSTom Warren * 11 = Length 48 Check busy after response 1833f82d89dSTom Warren */ 1843f82d89dSTom Warren if (!(cmd->resp_type & MMC_RSP_PRESENT)) 1853f82d89dSTom Warren flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_NO_RESPONSE; 1863f82d89dSTom Warren else if (cmd->resp_type & MMC_RSP_136) 1873f82d89dSTom Warren flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_136; 1883f82d89dSTom Warren else if (cmd->resp_type & MMC_RSP_BUSY) 1893f82d89dSTom Warren flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48_BUSY; 1903f82d89dSTom Warren else 1913f82d89dSTom Warren flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48; 1923f82d89dSTom Warren 1933f82d89dSTom Warren if (cmd->resp_type & MMC_RSP_CRC) 1943f82d89dSTom Warren flags |= TEGRA_MMC_TRNMOD_CMD_CRC_CHECK; 1953f82d89dSTom Warren if (cmd->resp_type & MMC_RSP_OPCODE) 1963f82d89dSTom Warren flags |= TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK; 1973f82d89dSTom Warren if (data) 1983f82d89dSTom Warren flags |= TEGRA_MMC_TRNMOD_DATA_PRESENT_SELECT_DATA_TRANSFER; 1993f82d89dSTom Warren 2003f82d89dSTom Warren debug("cmd: %d\n", cmd->cmdidx); 2013f82d89dSTom Warren 2023f82d89dSTom Warren writew((cmd->cmdidx << 8) | flags, &host->reg->cmdreg); 2033f82d89dSTom Warren 2043f82d89dSTom Warren for (i = 0; i < retry; i++) { 2053f82d89dSTom Warren mask = readl(&host->reg->norintsts); 2063f82d89dSTom Warren /* Command Complete */ 2073f82d89dSTom Warren if (mask & TEGRA_MMC_NORINTSTS_CMD_COMPLETE) { 2083f82d89dSTom Warren if (!data) 2093f82d89dSTom Warren writel(mask, &host->reg->norintsts); 2103f82d89dSTom Warren break; 2113f82d89dSTom Warren } 2123f82d89dSTom Warren } 2133f82d89dSTom Warren 2143f82d89dSTom Warren if (i == retry) { 2153f82d89dSTom Warren printf("%s: waiting for status update\n", __func__); 2163f82d89dSTom Warren writel(mask, &host->reg->norintsts); 2173f82d89dSTom Warren return TIMEOUT; 2183f82d89dSTom Warren } 2193f82d89dSTom Warren 2203f82d89dSTom Warren if (mask & TEGRA_MMC_NORINTSTS_CMD_TIMEOUT) { 2213f82d89dSTom Warren /* Timeout Error */ 2223f82d89dSTom Warren debug("timeout: %08x cmd %d\n", mask, cmd->cmdidx); 2233f82d89dSTom Warren writel(mask, &host->reg->norintsts); 2243f82d89dSTom Warren return TIMEOUT; 2253f82d89dSTom Warren } else if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) { 2263f82d89dSTom Warren /* Error Interrupt */ 2273f82d89dSTom Warren debug("error: %08x cmd %d\n", mask, cmd->cmdidx); 2283f82d89dSTom Warren writel(mask, &host->reg->norintsts); 2293f82d89dSTom Warren return -1; 2303f82d89dSTom Warren } 2313f82d89dSTom Warren 2323f82d89dSTom Warren if (cmd->resp_type & MMC_RSP_PRESENT) { 2333f82d89dSTom Warren if (cmd->resp_type & MMC_RSP_136) { 2343f82d89dSTom Warren /* CRC is stripped so we need to do some shifting. */ 2353f82d89dSTom Warren for (i = 0; i < 4; i++) { 236c39e2a75SThierry Reding unsigned long offset = 237c39e2a75SThierry Reding (unsigned long)(&host->reg->rspreg3 - i); 2383f82d89dSTom Warren cmd->response[i] = readl(offset) << 8; 2393f82d89dSTom Warren 2403f82d89dSTom Warren if (i != 3) { 2413f82d89dSTom Warren cmd->response[i] |= 2423f82d89dSTom Warren readb(offset - 1); 2433f82d89dSTom Warren } 2443f82d89dSTom Warren debug("cmd->resp[%d]: %08x\n", 2453f82d89dSTom Warren i, cmd->response[i]); 2463f82d89dSTom Warren } 2473f82d89dSTom Warren } else if (cmd->resp_type & MMC_RSP_BUSY) { 2483f82d89dSTom Warren for (i = 0; i < retry; i++) { 2493f82d89dSTom Warren /* PRNTDATA[23:20] : DAT[3:0] Line Signal */ 2503f82d89dSTom Warren if (readl(&host->reg->prnsts) 2513f82d89dSTom Warren & (1 << 20)) /* DAT[0] */ 2523f82d89dSTom Warren break; 2533f82d89dSTom Warren } 2543f82d89dSTom Warren 2553f82d89dSTom Warren if (i == retry) { 2563f82d89dSTom Warren printf("%s: card is still busy\n", __func__); 2573f82d89dSTom Warren writel(mask, &host->reg->norintsts); 2583f82d89dSTom Warren return TIMEOUT; 2593f82d89dSTom Warren } 2603f82d89dSTom Warren 2613f82d89dSTom Warren cmd->response[0] = readl(&host->reg->rspreg0); 2623f82d89dSTom Warren debug("cmd->resp[0]: %08x\n", cmd->response[0]); 2633f82d89dSTom Warren } else { 2643f82d89dSTom Warren cmd->response[0] = readl(&host->reg->rspreg0); 2653f82d89dSTom Warren debug("cmd->resp[0]: %08x\n", cmd->response[0]); 2663f82d89dSTom Warren } 2673f82d89dSTom Warren } 2683f82d89dSTom Warren 2693f82d89dSTom Warren if (data) { 2703f82d89dSTom Warren unsigned long start = get_timer(0); 2713f82d89dSTom Warren 2723f82d89dSTom Warren while (1) { 2733f82d89dSTom Warren mask = readl(&host->reg->norintsts); 2743f82d89dSTom Warren 2753f82d89dSTom Warren if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) { 2763f82d89dSTom Warren /* Error Interrupt */ 2773f82d89dSTom Warren writel(mask, &host->reg->norintsts); 2783f82d89dSTom Warren printf("%s: error during transfer: 0x%08x\n", 2793f82d89dSTom Warren __func__, mask); 2803f82d89dSTom Warren return -1; 2813f82d89dSTom Warren } else if (mask & TEGRA_MMC_NORINTSTS_DMA_INTERRUPT) { 2823f82d89dSTom Warren /* 2833f82d89dSTom Warren * DMA Interrupt, restart the transfer where 2843f82d89dSTom Warren * it was interrupted. 2853f82d89dSTom Warren */ 2863f82d89dSTom Warren unsigned int address = readl(&host->reg->sysad); 2873f82d89dSTom Warren 2883f82d89dSTom Warren debug("DMA end\n"); 2893f82d89dSTom Warren writel(TEGRA_MMC_NORINTSTS_DMA_INTERRUPT, 2903f82d89dSTom Warren &host->reg->norintsts); 2913f82d89dSTom Warren writel(address, &host->reg->sysad); 2923f82d89dSTom Warren } else if (mask & TEGRA_MMC_NORINTSTS_XFER_COMPLETE) { 2933f82d89dSTom Warren /* Transfer Complete */ 2943f82d89dSTom Warren debug("r/w is done\n"); 2953f82d89dSTom Warren break; 29609fb7361SMarcel Ziswiler } else if (get_timer(start) > 8000UL) { 2973f82d89dSTom Warren writel(mask, &host->reg->norintsts); 2983f82d89dSTom Warren printf("%s: MMC Timeout\n" 2993f82d89dSTom Warren " Interrupt status 0x%08x\n" 3003f82d89dSTom Warren " Interrupt status enable 0x%08x\n" 3013f82d89dSTom Warren " Interrupt signal enable 0x%08x\n" 3023f82d89dSTom Warren " Present status 0x%08x\n", 3033f82d89dSTom Warren __func__, mask, 3043f82d89dSTom Warren readl(&host->reg->norintstsen), 3053f82d89dSTom Warren readl(&host->reg->norintsigen), 3063f82d89dSTom Warren readl(&host->reg->prnsts)); 3073f82d89dSTom Warren return -1; 3083f82d89dSTom Warren } 3093f82d89dSTom Warren } 3103f82d89dSTom Warren writel(mask, &host->reg->norintsts); 3113f82d89dSTom Warren } 3123f82d89dSTom Warren 3133f82d89dSTom Warren udelay(1000); 3143f82d89dSTom Warren return 0; 3153f82d89dSTom Warren } 3163f82d89dSTom Warren 317ab769f22SPantelis Antoniou static int tegra_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, 31819815399SStephen Warren struct mmc_data *data) 31919815399SStephen Warren { 32019815399SStephen Warren void *buf; 32119815399SStephen Warren unsigned int bbflags; 32219815399SStephen Warren size_t len; 32319815399SStephen Warren struct bounce_buffer bbstate; 32419815399SStephen Warren int ret; 32519815399SStephen Warren 32619815399SStephen Warren if (data) { 32719815399SStephen Warren if (data->flags & MMC_DATA_READ) { 32819815399SStephen Warren buf = data->dest; 32919815399SStephen Warren bbflags = GEN_BB_WRITE; 33019815399SStephen Warren } else { 33119815399SStephen Warren buf = (void *)data->src; 33219815399SStephen Warren bbflags = GEN_BB_READ; 33319815399SStephen Warren } 33419815399SStephen Warren len = data->blocks * data->blocksize; 33519815399SStephen Warren 33619815399SStephen Warren bounce_buffer_start(&bbstate, buf, len, bbflags); 33719815399SStephen Warren } 33819815399SStephen Warren 33919815399SStephen Warren ret = mmc_send_cmd_bounced(mmc, cmd, data, &bbstate); 34019815399SStephen Warren 34119815399SStephen Warren if (data) 34219815399SStephen Warren bounce_buffer_stop(&bbstate); 34319815399SStephen Warren 34419815399SStephen Warren return ret; 34519815399SStephen Warren } 34619815399SStephen Warren 3473f82d89dSTom Warren static void mmc_change_clock(struct mmc_host *host, uint clock) 3483f82d89dSTom Warren { 3493f82d89dSTom Warren int div; 3503f82d89dSTom Warren unsigned short clk; 3513f82d89dSTom Warren unsigned long timeout; 3523f82d89dSTom Warren 3533f82d89dSTom Warren debug(" mmc_change_clock called\n"); 3543f82d89dSTom Warren 3553f82d89dSTom Warren /* 3562d348a16STom Warren * Change Tegra SDMMCx clock divisor here. Source is PLLP_OUT0 3573f82d89dSTom Warren */ 3583f82d89dSTom Warren if (clock == 0) 3593f82d89dSTom Warren goto out; 3603f82d89dSTom Warren clock_adjust_periph_pll_div(host->mmc_id, CLOCK_ID_PERIPH, clock, 3613f82d89dSTom Warren &div); 3623f82d89dSTom Warren debug("div = %d\n", div); 3633f82d89dSTom Warren 3643f82d89dSTom Warren writew(0, &host->reg->clkcon); 3653f82d89dSTom Warren 3663f82d89dSTom Warren /* 3673f82d89dSTom Warren * CLKCON 3683f82d89dSTom Warren * SELFREQ[15:8] : base clock divided by value 3693f82d89dSTom Warren * ENSDCLK[2] : SD Clock Enable 3703f82d89dSTom Warren * STBLINTCLK[1] : Internal Clock Stable 3713f82d89dSTom Warren * ENINTCLK[0] : Internal Clock Enable 3723f82d89dSTom Warren */ 3733f82d89dSTom Warren div >>= 1; 3743f82d89dSTom Warren clk = ((div << TEGRA_MMC_CLKCON_SDCLK_FREQ_SEL_SHIFT) | 3753f82d89dSTom Warren TEGRA_MMC_CLKCON_INTERNAL_CLOCK_ENABLE); 3763f82d89dSTom Warren writew(clk, &host->reg->clkcon); 3773f82d89dSTom Warren 3783f82d89dSTom Warren /* Wait max 10 ms */ 3793f82d89dSTom Warren timeout = 10; 3803f82d89dSTom Warren while (!(readw(&host->reg->clkcon) & 3813f82d89dSTom Warren TEGRA_MMC_CLKCON_INTERNAL_CLOCK_STABLE)) { 3823f82d89dSTom Warren if (timeout == 0) { 3833f82d89dSTom Warren printf("%s: timeout error\n", __func__); 3843f82d89dSTom Warren return; 3853f82d89dSTom Warren } 3863f82d89dSTom Warren timeout--; 3873f82d89dSTom Warren udelay(1000); 3883f82d89dSTom Warren } 3893f82d89dSTom Warren 3903f82d89dSTom Warren clk |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; 3913f82d89dSTom Warren writew(clk, &host->reg->clkcon); 3923f82d89dSTom Warren 3933f82d89dSTom Warren debug("mmc_change_clock: clkcon = %08X\n", clk); 3943f82d89dSTom Warren 3953f82d89dSTom Warren out: 3963f82d89dSTom Warren host->clock = clock; 3973f82d89dSTom Warren } 3983f82d89dSTom Warren 399ab769f22SPantelis Antoniou static void tegra_mmc_set_ios(struct mmc *mmc) 4003f82d89dSTom Warren { 4013f82d89dSTom Warren struct mmc_host *host = mmc->priv; 4023f82d89dSTom Warren unsigned char ctrl; 4033f82d89dSTom Warren debug(" mmc_set_ios called\n"); 4043f82d89dSTom Warren 4053f82d89dSTom Warren debug("bus_width: %x, clock: %d\n", mmc->bus_width, mmc->clock); 4063f82d89dSTom Warren 4073f82d89dSTom Warren /* Change clock first */ 4083f82d89dSTom Warren mmc_change_clock(host, mmc->clock); 4093f82d89dSTom Warren 4103f82d89dSTom Warren ctrl = readb(&host->reg->hostctl); 4113f82d89dSTom Warren 4123f82d89dSTom Warren /* 4133f82d89dSTom Warren * WIDE8[5] 4143f82d89dSTom Warren * 0 = Depend on WIDE4 4153f82d89dSTom Warren * 1 = 8-bit mode 4163f82d89dSTom Warren * WIDE4[1] 4173f82d89dSTom Warren * 1 = 4-bit mode 4183f82d89dSTom Warren * 0 = 1-bit mode 4193f82d89dSTom Warren */ 4203f82d89dSTom Warren if (mmc->bus_width == 8) 4213f82d89dSTom Warren ctrl |= (1 << 5); 4223f82d89dSTom Warren else if (mmc->bus_width == 4) 4233f82d89dSTom Warren ctrl |= (1 << 1); 4243f82d89dSTom Warren else 4253f82d89dSTom Warren ctrl &= ~(1 << 1); 4263f82d89dSTom Warren 4273f82d89dSTom Warren writeb(ctrl, &host->reg->hostctl); 4283f82d89dSTom Warren debug("mmc_set_ios: hostctl = %08X\n", ctrl); 4293f82d89dSTom Warren } 4303f82d89dSTom Warren 4312d348a16STom Warren static void mmc_reset(struct mmc_host *host, struct mmc *mmc) 4323f82d89dSTom Warren { 4333f82d89dSTom Warren unsigned int timeout; 4343f82d89dSTom Warren debug(" mmc_reset called\n"); 4353f82d89dSTom Warren 4363f82d89dSTom Warren /* 4373f82d89dSTom Warren * RSTALL[0] : Software reset for all 4383f82d89dSTom Warren * 1 = reset 4393f82d89dSTom Warren * 0 = work 4403f82d89dSTom Warren */ 4413f82d89dSTom Warren writeb(TEGRA_MMC_SWRST_SW_RESET_FOR_ALL, &host->reg->swrst); 4423f82d89dSTom Warren 4433f82d89dSTom Warren host->clock = 0; 4443f82d89dSTom Warren 4453f82d89dSTom Warren /* Wait max 100 ms */ 4463f82d89dSTom Warren timeout = 100; 4473f82d89dSTom Warren 4483f82d89dSTom Warren /* hw clears the bit when it's done */ 4493f82d89dSTom Warren while (readb(&host->reg->swrst) & TEGRA_MMC_SWRST_SW_RESET_FOR_ALL) { 4503f82d89dSTom Warren if (timeout == 0) { 4513f82d89dSTom Warren printf("%s: timeout error\n", __func__); 4523f82d89dSTom Warren return; 4533f82d89dSTom Warren } 4543f82d89dSTom Warren timeout--; 4553f82d89dSTom Warren udelay(1000); 4563f82d89dSTom Warren } 4572d348a16STom Warren 4582d348a16STom Warren /* Set SD bus voltage & enable bus power */ 45993bfd616SPantelis Antoniou mmc_set_power(host, fls(mmc->cfg->voltages) - 1); 4602d348a16STom Warren debug("%s: power control = %02X, host control = %02X\n", __func__, 4612d348a16STom Warren readb(&host->reg->pwrcon), readb(&host->reg->hostctl)); 4622d348a16STom Warren 4632d348a16STom Warren /* Make sure SDIO pads are set up */ 4642d348a16STom Warren pad_init_mmc(host); 4653f82d89dSTom Warren } 4663f82d89dSTom Warren 467ab769f22SPantelis Antoniou static int tegra_mmc_core_init(struct mmc *mmc) 4683f82d89dSTom Warren { 46993bfd616SPantelis Antoniou struct mmc_host *host = mmc->priv; 4703f82d89dSTom Warren unsigned int mask; 4713f82d89dSTom Warren debug(" mmc_core_init called\n"); 4723f82d89dSTom Warren 4732d348a16STom Warren mmc_reset(host, mmc); 4743f82d89dSTom Warren 4753f82d89dSTom Warren host->version = readw(&host->reg->hcver); 4763f82d89dSTom Warren debug("host version = %x\n", host->version); 4773f82d89dSTom Warren 4783f82d89dSTom Warren /* mask all */ 4793f82d89dSTom Warren writel(0xffffffff, &host->reg->norintstsen); 4803f82d89dSTom Warren writel(0xffffffff, &host->reg->norintsigen); 4813f82d89dSTom Warren 4823f82d89dSTom Warren writeb(0xe, &host->reg->timeoutcon); /* TMCLK * 2^27 */ 4833f82d89dSTom Warren /* 4843f82d89dSTom Warren * NORMAL Interrupt Status Enable Register init 4853f82d89dSTom Warren * [5] ENSTABUFRDRDY : Buffer Read Ready Status Enable 4863f82d89dSTom Warren * [4] ENSTABUFWTRDY : Buffer write Ready Status Enable 4873f82d89dSTom Warren * [3] ENSTADMAINT : DMA boundary interrupt 4883f82d89dSTom Warren * [1] ENSTASTANSCMPLT : Transfre Complete Status Enable 4893f82d89dSTom Warren * [0] ENSTACMDCMPLT : Command Complete Status Enable 4903f82d89dSTom Warren */ 4913f82d89dSTom Warren mask = readl(&host->reg->norintstsen); 4923f82d89dSTom Warren mask &= ~(0xffff); 4933f82d89dSTom Warren mask |= (TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | 4943f82d89dSTom Warren TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | 4953f82d89dSTom Warren TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT | 4963f82d89dSTom Warren TEGRA_MMC_NORINTSTSEN_BUFFER_WRITE_READY | 4973f82d89dSTom Warren TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY); 4983f82d89dSTom Warren writel(mask, &host->reg->norintstsen); 4993f82d89dSTom Warren 5003f82d89dSTom Warren /* 5013f82d89dSTom Warren * NORMAL Interrupt Signal Enable Register init 5023f82d89dSTom Warren * [1] ENSTACMDCMPLT : Transfer Complete Signal Enable 5033f82d89dSTom Warren */ 5043f82d89dSTom Warren mask = readl(&host->reg->norintsigen); 5053f82d89dSTom Warren mask &= ~(0xffff); 5063f82d89dSTom Warren mask |= TEGRA_MMC_NORINTSIGEN_XFER_COMPLETE; 5073f82d89dSTom Warren writel(mask, &host->reg->norintsigen); 5083f82d89dSTom Warren 5093f82d89dSTom Warren return 0; 5103f82d89dSTom Warren } 5113f82d89dSTom Warren 51219d7bf3dSJeroen Hofstee static int tegra_mmc_getcd(struct mmc *mmc) 5133f82d89dSTom Warren { 51493bfd616SPantelis Antoniou struct mmc_host *host = mmc->priv; 5153f82d89dSTom Warren 51629f3e3f2STom Warren debug("tegra_mmc_getcd called\n"); 5173f82d89dSTom Warren 5180347960bSSimon Glass if (dm_gpio_is_valid(&host->cd_gpio)) 5190347960bSSimon Glass return dm_gpio_get_value(&host->cd_gpio); 5203f82d89dSTom Warren 5213f82d89dSTom Warren return 1; 5223f82d89dSTom Warren } 5233f82d89dSTom Warren 524ab769f22SPantelis Antoniou static const struct mmc_ops tegra_mmc_ops = { 525ab769f22SPantelis Antoniou .send_cmd = tegra_mmc_send_cmd, 526ab769f22SPantelis Antoniou .set_ios = tegra_mmc_set_ios, 527ab769f22SPantelis Antoniou .init = tegra_mmc_core_init, 528ab769f22SPantelis Antoniou .getcd = tegra_mmc_getcd, 529ab769f22SPantelis Antoniou }; 530ab769f22SPantelis Antoniou 531707ac1adSSimon Glass static int do_mmc_init(int dev_index, bool removable) 5323f82d89dSTom Warren { 5333f82d89dSTom Warren struct mmc_host *host; 5343f82d89dSTom Warren struct mmc *mmc; 5353f82d89dSTom Warren 536c9aa831eSTom Warren /* DT should have been read & host config filled in */ 5373f82d89dSTom Warren host = &mmc_host[dev_index]; 538c9aa831eSTom Warren if (!host->enabled) 539c9aa831eSTom Warren return -1; 540c9aa831eSTom Warren 5410347960bSSimon Glass debug(" do_mmc_init: index %d, bus width %d pwr_gpio %d cd_gpio %d\n", 5420347960bSSimon Glass dev_index, host->width, gpio_get_number(&host->pwr_gpio), 5430347960bSSimon Glass gpio_get_number(&host->cd_gpio)); 5443f82d89dSTom Warren 5453f82d89dSTom Warren host->clock = 0; 5463f82d89dSTom Warren clock_start_periph_pll(host->mmc_id, CLOCK_ID_PERIPH, 20000000); 5473f82d89dSTom Warren 5480347960bSSimon Glass if (dm_gpio_is_valid(&host->pwr_gpio)) 5490347960bSSimon Glass dm_gpio_set_value(&host->pwr_gpio, 1); 5503f82d89dSTom Warren 55193bfd616SPantelis Antoniou memset(&host->cfg, 0, sizeof(host->cfg)); 5523f82d89dSTom Warren 55393bfd616SPantelis Antoniou host->cfg.name = "Tegra SD/MMC"; 55493bfd616SPantelis Antoniou host->cfg.ops = &tegra_mmc_ops; 5553f82d89dSTom Warren 55693bfd616SPantelis Antoniou host->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; 55793bfd616SPantelis Antoniou host->cfg.host_caps = 0; 558c9aa831eSTom Warren if (host->width == 8) 55993bfd616SPantelis Antoniou host->cfg.host_caps |= MMC_MODE_8BIT; 560c9aa831eSTom Warren if (host->width >= 4) 56193bfd616SPantelis Antoniou host->cfg.host_caps |= MMC_MODE_4BIT; 5625a20397bSRob Herring host->cfg.host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; 5633f82d89dSTom Warren 5643f82d89dSTom Warren /* 5653f82d89dSTom Warren * min freq is for card identification, and is the highest 5663f82d89dSTom Warren * low-speed SDIO card frequency (actually 400KHz) 5673f82d89dSTom Warren * max freq is highest HS eMMC clock as per the SD/MMC spec 5683f82d89dSTom Warren * (actually 52MHz) 5693f82d89dSTom Warren */ 57093bfd616SPantelis Antoniou host->cfg.f_min = 375000; 57193bfd616SPantelis Antoniou host->cfg.f_max = 48000000; 5723f82d89dSTom Warren 57393bfd616SPantelis Antoniou host->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; 57493bfd616SPantelis Antoniou 57593bfd616SPantelis Antoniou mmc = mmc_create(&host->cfg, host); 576707ac1adSSimon Glass mmc->block_dev.removable = removable; 57793bfd616SPantelis Antoniou if (mmc == NULL) 57893bfd616SPantelis Antoniou return -1; 5793f82d89dSTom Warren 5803f82d89dSTom Warren return 0; 5813f82d89dSTom Warren } 582c9aa831eSTom Warren 583c9aa831eSTom Warren /** 584c9aa831eSTom Warren * Get the host address and peripheral ID for a node. 585c9aa831eSTom Warren * 586c9aa831eSTom Warren * @param blob fdt blob 587c9aa831eSTom Warren * @param node Device index (0-3) 588c9aa831eSTom Warren * @param host Structure to fill in (reg, width, mmc_id) 589c9aa831eSTom Warren */ 590707ac1adSSimon Glass static int mmc_get_config(const void *blob, int node, struct mmc_host *host, 591707ac1adSSimon Glass bool *removablep) 592c9aa831eSTom Warren { 593c9aa831eSTom Warren debug("%s: node = %d\n", __func__, node); 594c9aa831eSTom Warren 595c9aa831eSTom Warren host->enabled = fdtdec_get_is_enabled(blob, node); 596c9aa831eSTom Warren 597c9aa831eSTom Warren host->reg = (struct tegra_mmc *)fdtdec_get_addr(blob, node, "reg"); 598c9aa831eSTom Warren if ((fdt_addr_t)host->reg == FDT_ADDR_T_NONE) { 599c9aa831eSTom Warren debug("%s: no sdmmc base reg info found\n", __func__); 600c9aa831eSTom Warren return -FDT_ERR_NOTFOUND; 601c9aa831eSTom Warren } 602c9aa831eSTom Warren 603c9aa831eSTom Warren host->mmc_id = clock_decode_periph_id(blob, node); 604c9aa831eSTom Warren if (host->mmc_id == PERIPH_ID_NONE) { 605c9aa831eSTom Warren debug("%s: could not decode periph id\n", __func__); 606c9aa831eSTom Warren return -FDT_ERR_NOTFOUND; 607c9aa831eSTom Warren } 608c9aa831eSTom Warren 609c9aa831eSTom Warren /* 610c9aa831eSTom Warren * NOTE: mmc->bus_width is determined by mmc.c dynamically. 611c9aa831eSTom Warren * TBD: Override it with this value? 612c9aa831eSTom Warren */ 613c9aa831eSTom Warren host->width = fdtdec_get_int(blob, node, "bus-width", 0); 614c9aa831eSTom Warren if (!host->width) 615c9aa831eSTom Warren debug("%s: no sdmmc width found\n", __func__); 616c9aa831eSTom Warren 617c9aa831eSTom Warren /* These GPIOs are optional */ 6180347960bSSimon Glass gpio_request_by_name_nodev(blob, node, "cd-gpios", 0, &host->cd_gpio, 6190347960bSSimon Glass GPIOD_IS_IN); 6200347960bSSimon Glass gpio_request_by_name_nodev(blob, node, "wp-gpios", 0, &host->wp_gpio, 6210347960bSSimon Glass GPIOD_IS_IN); 6220347960bSSimon Glass gpio_request_by_name_nodev(blob, node, "power-gpios", 0, 6230347960bSSimon Glass &host->pwr_gpio, GPIOD_IS_OUT); 624707ac1adSSimon Glass *removablep = !fdtdec_get_bool(blob, node, "non-removable"); 625c9aa831eSTom Warren 626c9aa831eSTom Warren debug("%s: found controller at %p, width = %d, periph_id = %d\n", 627c9aa831eSTom Warren __func__, host->reg, host->width, host->mmc_id); 628c9aa831eSTom Warren return 0; 629c9aa831eSTom Warren } 630c9aa831eSTom Warren 631c9aa831eSTom Warren /* 632c9aa831eSTom Warren * Process a list of nodes, adding them to our list of SDMMC ports. 633c9aa831eSTom Warren * 634c9aa831eSTom Warren * @param blob fdt blob 635c9aa831eSTom Warren * @param node_list list of nodes to process (any <=0 are ignored) 636c9aa831eSTom Warren * @param count number of nodes to process 637c9aa831eSTom Warren * @return 0 if ok, -1 on error 638c9aa831eSTom Warren */ 639c9aa831eSTom Warren static int process_nodes(const void *blob, int node_list[], int count) 640c9aa831eSTom Warren { 641c9aa831eSTom Warren struct mmc_host *host; 642707ac1adSSimon Glass bool removable; 643c9aa831eSTom Warren int i, node; 644c9aa831eSTom Warren 645c9aa831eSTom Warren debug("%s: count = %d\n", __func__, count); 646c9aa831eSTom Warren 647c9aa831eSTom Warren /* build mmc_host[] for each controller */ 648c9aa831eSTom Warren for (i = 0; i < count; i++) { 649c9aa831eSTom Warren node = node_list[i]; 650c9aa831eSTom Warren if (node <= 0) 651c9aa831eSTom Warren continue; 652c9aa831eSTom Warren 653c9aa831eSTom Warren host = &mmc_host[i]; 654c9aa831eSTom Warren host->id = i; 655c9aa831eSTom Warren 656707ac1adSSimon Glass if (mmc_get_config(blob, node, host, &removable)) { 657c9aa831eSTom Warren printf("%s: failed to decode dev %d\n", __func__, i); 658c9aa831eSTom Warren return -1; 659c9aa831eSTom Warren } 660707ac1adSSimon Glass do_mmc_init(i, removable); 661c9aa831eSTom Warren } 662c9aa831eSTom Warren return 0; 663c9aa831eSTom Warren } 664c9aa831eSTom Warren 665c9aa831eSTom Warren void tegra_mmc_init(void) 666c9aa831eSTom Warren { 667f175603fSStephen Warren int node_list[CONFIG_SYS_MMC_MAX_DEVICE], count; 668c9aa831eSTom Warren const void *blob = gd->fdt_blob; 669c9aa831eSTom Warren debug("%s entry\n", __func__); 670c9aa831eSTom Warren 6717aaa5a60STom Warren /* See if any Tegra210 MMC controllers are present */ 6727aaa5a60STom Warren count = fdtdec_find_aliases_for_id(blob, "sdhci", 6737aaa5a60STom Warren COMPAT_NVIDIA_TEGRA210_SDMMC, node_list, 6747aaa5a60STom Warren CONFIG_SYS_MMC_MAX_DEVICE); 6757aaa5a60STom Warren debug("%s: count of Tegra210 sdhci nodes is %d\n", __func__, count); 6767aaa5a60STom Warren if (process_nodes(blob, node_list, count)) { 677*e05ab0daSSimon Glass printf("%s: Error processing T210 mmc node(s)!\n", __func__); 6787aaa5a60STom Warren return; 6797aaa5a60STom Warren } 6807aaa5a60STom Warren 681a73ca478SStephen Warren /* See if any Tegra124 MMC controllers are present */ 682a73ca478SStephen Warren count = fdtdec_find_aliases_for_id(blob, "sdhci", 683f175603fSStephen Warren COMPAT_NVIDIA_TEGRA124_SDMMC, node_list, 684f175603fSStephen Warren CONFIG_SYS_MMC_MAX_DEVICE); 685a73ca478SStephen Warren debug("%s: count of Tegra124 sdhci nodes is %d\n", __func__, count); 686a73ca478SStephen Warren if (process_nodes(blob, node_list, count)) { 687*e05ab0daSSimon Glass printf("%s: Error processing T124 mmc node(s)!\n", __func__); 688a73ca478SStephen Warren return; 689a73ca478SStephen Warren } 690a73ca478SStephen Warren 6912d348a16STom Warren /* See if any Tegra30 MMC controllers are present */ 6922d348a16STom Warren count = fdtdec_find_aliases_for_id(blob, "sdhci", 693f175603fSStephen Warren COMPAT_NVIDIA_TEGRA30_SDMMC, node_list, 694f175603fSStephen Warren CONFIG_SYS_MMC_MAX_DEVICE); 6952d348a16STom Warren debug("%s: count of T30 sdhci nodes is %d\n", __func__, count); 6962d348a16STom Warren if (process_nodes(blob, node_list, count)) { 6972d348a16STom Warren printf("%s: Error processing T30 mmc node(s)!\n", __func__); 6982d348a16STom Warren return; 6992d348a16STom Warren } 7002d348a16STom Warren 7012d348a16STom Warren /* Now look for any Tegra20 MMC controllers */ 702c9aa831eSTom Warren count = fdtdec_find_aliases_for_id(blob, "sdhci", 703f175603fSStephen Warren COMPAT_NVIDIA_TEGRA20_SDMMC, node_list, 704f175603fSStephen Warren CONFIG_SYS_MMC_MAX_DEVICE); 7052d348a16STom Warren debug("%s: count of T20 sdhci nodes is %d\n", __func__, count); 706c9aa831eSTom Warren if (process_nodes(blob, node_list, count)) { 7072d348a16STom Warren printf("%s: Error processing T20 mmc node(s)!\n", __func__); 708c9aa831eSTom Warren return; 709c9aa831eSTom Warren } 710c9aa831eSTom Warren } 711