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> 5c9aa831eSTom Warren * Portions Copyright 2011-2013 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> 16150c2493STom Warren #include <asm/arch-tegra/tegra_mmc.h> 17150c2493STom Warren #include <mmc.h> 183f82d89dSTom Warren 19c9aa831eSTom Warren DECLARE_GLOBAL_DATA_PTR; 203f82d89dSTom Warren 21*f175603fSStephen Warren struct mmc_host mmc_host[CONFIG_SYS_MMC_MAX_DEVICE]; 223f82d89dSTom Warren 23c9aa831eSTom Warren #ifndef CONFIG_OF_CONTROL 24c9aa831eSTom Warren #error "Please enable device tree support to use this driver" 25c9aa831eSTom Warren #endif 263f82d89dSTom Warren 272d348a16STom Warren static void mmc_set_power(struct mmc_host *host, unsigned short power) 282d348a16STom Warren { 292d348a16STom Warren u8 pwr = 0; 302d348a16STom Warren debug("%s: power = %x\n", __func__, power); 312d348a16STom Warren 322d348a16STom Warren if (power != (unsigned short)-1) { 332d348a16STom Warren switch (1 << power) { 342d348a16STom Warren case MMC_VDD_165_195: 352d348a16STom Warren pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V1_8; 362d348a16STom Warren break; 372d348a16STom Warren case MMC_VDD_29_30: 382d348a16STom Warren case MMC_VDD_30_31: 392d348a16STom Warren pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_0; 402d348a16STom Warren break; 412d348a16STom Warren case MMC_VDD_32_33: 422d348a16STom Warren case MMC_VDD_33_34: 432d348a16STom Warren pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_3; 442d348a16STom Warren break; 452d348a16STom Warren } 462d348a16STom Warren } 472d348a16STom Warren debug("%s: pwr = %X\n", __func__, pwr); 482d348a16STom Warren 492d348a16STom Warren /* Set the bus voltage first (if any) */ 502d348a16STom Warren writeb(pwr, &host->reg->pwrcon); 512d348a16STom Warren if (pwr == 0) 522d348a16STom Warren return; 532d348a16STom Warren 542d348a16STom Warren /* Now enable bus power */ 552d348a16STom Warren pwr |= TEGRA_MMC_PWRCTL_SD_BUS_POWER; 562d348a16STom Warren writeb(pwr, &host->reg->pwrcon); 572d348a16STom Warren } 582d348a16STom Warren 5919815399SStephen Warren static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data, 6019815399SStephen Warren struct bounce_buffer *bbstate) 613f82d89dSTom Warren { 623f82d89dSTom Warren unsigned char ctrl; 633f82d89dSTom Warren 643f82d89dSTom Warren 6519815399SStephen Warren debug("buf: %p (%p), data->blocks: %u, data->blocksize: %u\n", 6619815399SStephen Warren bbstate->bounce_buffer, bbstate->user_buffer, data->blocks, 6719815399SStephen Warren data->blocksize); 6819815399SStephen Warren 6919815399SStephen Warren writel((u32)bbstate->bounce_buffer, &host->reg->sysad); 703f82d89dSTom Warren /* 713f82d89dSTom Warren * DMASEL[4:3] 723f82d89dSTom Warren * 00 = Selects SDMA 733f82d89dSTom Warren * 01 = Reserved 743f82d89dSTom Warren * 10 = Selects 32-bit Address ADMA2 753f82d89dSTom Warren * 11 = Selects 64-bit Address ADMA2 763f82d89dSTom Warren */ 773f82d89dSTom Warren ctrl = readb(&host->reg->hostctl); 783f82d89dSTom Warren ctrl &= ~TEGRA_MMC_HOSTCTL_DMASEL_MASK; 793f82d89dSTom Warren ctrl |= TEGRA_MMC_HOSTCTL_DMASEL_SDMA; 803f82d89dSTom Warren writeb(ctrl, &host->reg->hostctl); 813f82d89dSTom Warren 823f82d89dSTom Warren /* We do not handle DMA boundaries, so set it to max (512 KiB) */ 833f82d89dSTom Warren writew((7 << 12) | (data->blocksize & 0xFFF), &host->reg->blksize); 843f82d89dSTom Warren writew(data->blocks, &host->reg->blkcnt); 853f82d89dSTom Warren } 863f82d89dSTom Warren 873f82d89dSTom Warren static void mmc_set_transfer_mode(struct mmc_host *host, struct mmc_data *data) 883f82d89dSTom Warren { 893f82d89dSTom Warren unsigned short mode; 903f82d89dSTom Warren debug(" mmc_set_transfer_mode called\n"); 913f82d89dSTom Warren /* 923f82d89dSTom Warren * TRNMOD 933f82d89dSTom Warren * MUL1SIN0[5] : Multi/Single Block Select 943f82d89dSTom Warren * RD1WT0[4] : Data Transfer Direction Select 953f82d89dSTom Warren * 1 = read 963f82d89dSTom Warren * 0 = write 973f82d89dSTom Warren * ENACMD12[2] : Auto CMD12 Enable 983f82d89dSTom Warren * ENBLKCNT[1] : Block Count Enable 993f82d89dSTom Warren * ENDMA[0] : DMA Enable 1003f82d89dSTom Warren */ 1013f82d89dSTom Warren mode = (TEGRA_MMC_TRNMOD_DMA_ENABLE | 1023f82d89dSTom Warren TEGRA_MMC_TRNMOD_BLOCK_COUNT_ENABLE); 1033f82d89dSTom Warren 1043f82d89dSTom Warren if (data->blocks > 1) 1053f82d89dSTom Warren mode |= TEGRA_MMC_TRNMOD_MULTI_BLOCK_SELECT; 1063f82d89dSTom Warren 1073f82d89dSTom Warren if (data->flags & MMC_DATA_READ) 1083f82d89dSTom Warren mode |= TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ; 1093f82d89dSTom Warren 1103f82d89dSTom Warren writew(mode, &host->reg->trnmod); 1113f82d89dSTom Warren } 1123f82d89dSTom Warren 1133f82d89dSTom Warren static int mmc_wait_inhibit(struct mmc_host *host, 1143f82d89dSTom Warren struct mmc_cmd *cmd, 1153f82d89dSTom Warren struct mmc_data *data, 1163f82d89dSTom Warren unsigned int timeout) 1173f82d89dSTom Warren { 1183f82d89dSTom Warren /* 1193f82d89dSTom Warren * PRNSTS 1203f82d89dSTom Warren * CMDINHDAT[1] : Command Inhibit (DAT) 1213f82d89dSTom Warren * CMDINHCMD[0] : Command Inhibit (CMD) 1223f82d89dSTom Warren */ 1233f82d89dSTom Warren unsigned int mask = TEGRA_MMC_PRNSTS_CMD_INHIBIT_CMD; 1243f82d89dSTom Warren 1253f82d89dSTom Warren /* 1263f82d89dSTom Warren * We shouldn't wait for data inhibit for stop commands, even 1273f82d89dSTom Warren * though they might use busy signaling 1283f82d89dSTom Warren */ 1293f82d89dSTom Warren if ((data == NULL) && (cmd->resp_type & MMC_RSP_BUSY)) 1303f82d89dSTom Warren mask |= TEGRA_MMC_PRNSTS_CMD_INHIBIT_DAT; 1313f82d89dSTom Warren 1323f82d89dSTom Warren while (readl(&host->reg->prnsts) & mask) { 1333f82d89dSTom Warren if (timeout == 0) { 1343f82d89dSTom Warren printf("%s: timeout error\n", __func__); 1353f82d89dSTom Warren return -1; 1363f82d89dSTom Warren } 1373f82d89dSTom Warren timeout--; 1383f82d89dSTom Warren udelay(1000); 1393f82d89dSTom Warren } 1403f82d89dSTom Warren 1413f82d89dSTom Warren return 0; 1423f82d89dSTom Warren } 1433f82d89dSTom Warren 14419815399SStephen Warren static int mmc_send_cmd_bounced(struct mmc *mmc, struct mmc_cmd *cmd, 14519815399SStephen Warren struct mmc_data *data, struct bounce_buffer *bbstate) 1463f82d89dSTom Warren { 14793bfd616SPantelis Antoniou struct mmc_host *host = mmc->priv; 1483f82d89dSTom Warren int flags, i; 1493f82d89dSTom Warren int result; 1503f82d89dSTom Warren unsigned int mask = 0; 1513f82d89dSTom Warren unsigned int retry = 0x100000; 1523f82d89dSTom Warren debug(" mmc_send_cmd called\n"); 1533f82d89dSTom Warren 1543f82d89dSTom Warren result = mmc_wait_inhibit(host, cmd, data, 10 /* ms */); 1553f82d89dSTom Warren 1563f82d89dSTom Warren if (result < 0) 1573f82d89dSTom Warren return result; 1583f82d89dSTom Warren 1593f82d89dSTom Warren if (data) 16019815399SStephen Warren mmc_prepare_data(host, data, bbstate); 1613f82d89dSTom Warren 1623f82d89dSTom Warren debug("cmd->arg: %08x\n", cmd->cmdarg); 1633f82d89dSTom Warren writel(cmd->cmdarg, &host->reg->argument); 1643f82d89dSTom Warren 1653f82d89dSTom Warren if (data) 1663f82d89dSTom Warren mmc_set_transfer_mode(host, data); 1673f82d89dSTom Warren 1683f82d89dSTom Warren if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY)) 1693f82d89dSTom Warren return -1; 1703f82d89dSTom Warren 1713f82d89dSTom Warren /* 1723f82d89dSTom Warren * CMDREG 1733f82d89dSTom Warren * CMDIDX[13:8] : Command index 1743f82d89dSTom Warren * DATAPRNT[5] : Data Present Select 1753f82d89dSTom Warren * ENCMDIDX[4] : Command Index Check Enable 1763f82d89dSTom Warren * ENCMDCRC[3] : Command CRC Check Enable 1773f82d89dSTom Warren * RSPTYP[1:0] 1783f82d89dSTom Warren * 00 = No Response 1793f82d89dSTom Warren * 01 = Length 136 1803f82d89dSTom Warren * 10 = Length 48 1813f82d89dSTom Warren * 11 = Length 48 Check busy after response 1823f82d89dSTom Warren */ 1833f82d89dSTom Warren if (!(cmd->resp_type & MMC_RSP_PRESENT)) 1843f82d89dSTom Warren flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_NO_RESPONSE; 1853f82d89dSTom Warren else if (cmd->resp_type & MMC_RSP_136) 1863f82d89dSTom Warren flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_136; 1873f82d89dSTom Warren else if (cmd->resp_type & MMC_RSP_BUSY) 1883f82d89dSTom Warren flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48_BUSY; 1893f82d89dSTom Warren else 1903f82d89dSTom Warren flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48; 1913f82d89dSTom Warren 1923f82d89dSTom Warren if (cmd->resp_type & MMC_RSP_CRC) 1933f82d89dSTom Warren flags |= TEGRA_MMC_TRNMOD_CMD_CRC_CHECK; 1943f82d89dSTom Warren if (cmd->resp_type & MMC_RSP_OPCODE) 1953f82d89dSTom Warren flags |= TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK; 1963f82d89dSTom Warren if (data) 1973f82d89dSTom Warren flags |= TEGRA_MMC_TRNMOD_DATA_PRESENT_SELECT_DATA_TRANSFER; 1983f82d89dSTom Warren 1993f82d89dSTom Warren debug("cmd: %d\n", cmd->cmdidx); 2003f82d89dSTom Warren 2013f82d89dSTom Warren writew((cmd->cmdidx << 8) | flags, &host->reg->cmdreg); 2023f82d89dSTom Warren 2033f82d89dSTom Warren for (i = 0; i < retry; i++) { 2043f82d89dSTom Warren mask = readl(&host->reg->norintsts); 2053f82d89dSTom Warren /* Command Complete */ 2063f82d89dSTom Warren if (mask & TEGRA_MMC_NORINTSTS_CMD_COMPLETE) { 2073f82d89dSTom Warren if (!data) 2083f82d89dSTom Warren writel(mask, &host->reg->norintsts); 2093f82d89dSTom Warren break; 2103f82d89dSTom Warren } 2113f82d89dSTom Warren } 2123f82d89dSTom Warren 2133f82d89dSTom Warren if (i == retry) { 2143f82d89dSTom Warren printf("%s: waiting for status update\n", __func__); 2153f82d89dSTom Warren writel(mask, &host->reg->norintsts); 2163f82d89dSTom Warren return TIMEOUT; 2173f82d89dSTom Warren } 2183f82d89dSTom Warren 2193f82d89dSTom Warren if (mask & TEGRA_MMC_NORINTSTS_CMD_TIMEOUT) { 2203f82d89dSTom Warren /* Timeout Error */ 2213f82d89dSTom Warren debug("timeout: %08x cmd %d\n", mask, cmd->cmdidx); 2223f82d89dSTom Warren writel(mask, &host->reg->norintsts); 2233f82d89dSTom Warren return TIMEOUT; 2243f82d89dSTom Warren } else if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) { 2253f82d89dSTom Warren /* Error Interrupt */ 2263f82d89dSTom Warren debug("error: %08x cmd %d\n", mask, cmd->cmdidx); 2273f82d89dSTom Warren writel(mask, &host->reg->norintsts); 2283f82d89dSTom Warren return -1; 2293f82d89dSTom Warren } 2303f82d89dSTom Warren 2313f82d89dSTom Warren if (cmd->resp_type & MMC_RSP_PRESENT) { 2323f82d89dSTom Warren if (cmd->resp_type & MMC_RSP_136) { 2333f82d89dSTom Warren /* CRC is stripped so we need to do some shifting. */ 2343f82d89dSTom Warren for (i = 0; i < 4; i++) { 2353f82d89dSTom Warren unsigned int offset = 2363f82d89dSTom Warren (unsigned int)(&host->reg->rspreg3 - i); 2373f82d89dSTom Warren cmd->response[i] = readl(offset) << 8; 2383f82d89dSTom Warren 2393f82d89dSTom Warren if (i != 3) { 2403f82d89dSTom Warren cmd->response[i] |= 2413f82d89dSTom Warren readb(offset - 1); 2423f82d89dSTom Warren } 2433f82d89dSTom Warren debug("cmd->resp[%d]: %08x\n", 2443f82d89dSTom Warren i, cmd->response[i]); 2453f82d89dSTom Warren } 2463f82d89dSTom Warren } else if (cmd->resp_type & MMC_RSP_BUSY) { 2473f82d89dSTom Warren for (i = 0; i < retry; i++) { 2483f82d89dSTom Warren /* PRNTDATA[23:20] : DAT[3:0] Line Signal */ 2493f82d89dSTom Warren if (readl(&host->reg->prnsts) 2503f82d89dSTom Warren & (1 << 20)) /* DAT[0] */ 2513f82d89dSTom Warren break; 2523f82d89dSTom Warren } 2533f82d89dSTom Warren 2543f82d89dSTom Warren if (i == retry) { 2553f82d89dSTom Warren printf("%s: card is still busy\n", __func__); 2563f82d89dSTom Warren writel(mask, &host->reg->norintsts); 2573f82d89dSTom Warren return TIMEOUT; 2583f82d89dSTom Warren } 2593f82d89dSTom Warren 2603f82d89dSTom Warren cmd->response[0] = readl(&host->reg->rspreg0); 2613f82d89dSTom Warren debug("cmd->resp[0]: %08x\n", cmd->response[0]); 2623f82d89dSTom Warren } else { 2633f82d89dSTom Warren cmd->response[0] = readl(&host->reg->rspreg0); 2643f82d89dSTom Warren debug("cmd->resp[0]: %08x\n", cmd->response[0]); 2653f82d89dSTom Warren } 2663f82d89dSTom Warren } 2673f82d89dSTom Warren 2683f82d89dSTom Warren if (data) { 2693f82d89dSTom Warren unsigned long start = get_timer(0); 2703f82d89dSTom Warren 2713f82d89dSTom Warren while (1) { 2723f82d89dSTom Warren mask = readl(&host->reg->norintsts); 2733f82d89dSTom Warren 2743f82d89dSTom Warren if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) { 2753f82d89dSTom Warren /* Error Interrupt */ 2763f82d89dSTom Warren writel(mask, &host->reg->norintsts); 2773f82d89dSTom Warren printf("%s: error during transfer: 0x%08x\n", 2783f82d89dSTom Warren __func__, mask); 2793f82d89dSTom Warren return -1; 2803f82d89dSTom Warren } else if (mask & TEGRA_MMC_NORINTSTS_DMA_INTERRUPT) { 2813f82d89dSTom Warren /* 2823f82d89dSTom Warren * DMA Interrupt, restart the transfer where 2833f82d89dSTom Warren * it was interrupted. 2843f82d89dSTom Warren */ 2853f82d89dSTom Warren unsigned int address = readl(&host->reg->sysad); 2863f82d89dSTom Warren 2873f82d89dSTom Warren debug("DMA end\n"); 2883f82d89dSTom Warren writel(TEGRA_MMC_NORINTSTS_DMA_INTERRUPT, 2893f82d89dSTom Warren &host->reg->norintsts); 2903f82d89dSTom Warren writel(address, &host->reg->sysad); 2913f82d89dSTom Warren } else if (mask & TEGRA_MMC_NORINTSTS_XFER_COMPLETE) { 2923f82d89dSTom Warren /* Transfer Complete */ 2933f82d89dSTom Warren debug("r/w is done\n"); 2943f82d89dSTom Warren break; 2953f82d89dSTom Warren } else if (get_timer(start) > 2000UL) { 2963f82d89dSTom Warren writel(mask, &host->reg->norintsts); 2973f82d89dSTom Warren printf("%s: MMC Timeout\n" 2983f82d89dSTom Warren " Interrupt status 0x%08x\n" 2993f82d89dSTom Warren " Interrupt status enable 0x%08x\n" 3003f82d89dSTom Warren " Interrupt signal enable 0x%08x\n" 3013f82d89dSTom Warren " Present status 0x%08x\n", 3023f82d89dSTom Warren __func__, mask, 3033f82d89dSTom Warren readl(&host->reg->norintstsen), 3043f82d89dSTom Warren readl(&host->reg->norintsigen), 3053f82d89dSTom Warren readl(&host->reg->prnsts)); 3063f82d89dSTom Warren return -1; 3073f82d89dSTom Warren } 3083f82d89dSTom Warren } 3093f82d89dSTom Warren writel(mask, &host->reg->norintsts); 3103f82d89dSTom Warren } 3113f82d89dSTom Warren 3123f82d89dSTom Warren udelay(1000); 3133f82d89dSTom Warren return 0; 3143f82d89dSTom Warren } 3153f82d89dSTom Warren 316ab769f22SPantelis Antoniou static int tegra_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, 31719815399SStephen Warren struct mmc_data *data) 31819815399SStephen Warren { 31919815399SStephen Warren void *buf; 32019815399SStephen Warren unsigned int bbflags; 32119815399SStephen Warren size_t len; 32219815399SStephen Warren struct bounce_buffer bbstate; 32319815399SStephen Warren int ret; 32419815399SStephen Warren 32519815399SStephen Warren if (data) { 32619815399SStephen Warren if (data->flags & MMC_DATA_READ) { 32719815399SStephen Warren buf = data->dest; 32819815399SStephen Warren bbflags = GEN_BB_WRITE; 32919815399SStephen Warren } else { 33019815399SStephen Warren buf = (void *)data->src; 33119815399SStephen Warren bbflags = GEN_BB_READ; 33219815399SStephen Warren } 33319815399SStephen Warren len = data->blocks * data->blocksize; 33419815399SStephen Warren 33519815399SStephen Warren bounce_buffer_start(&bbstate, buf, len, bbflags); 33619815399SStephen Warren } 33719815399SStephen Warren 33819815399SStephen Warren ret = mmc_send_cmd_bounced(mmc, cmd, data, &bbstate); 33919815399SStephen Warren 34019815399SStephen Warren if (data) 34119815399SStephen Warren bounce_buffer_stop(&bbstate); 34219815399SStephen Warren 34319815399SStephen Warren return ret; 34419815399SStephen Warren } 34519815399SStephen Warren 3463f82d89dSTom Warren static void mmc_change_clock(struct mmc_host *host, uint clock) 3473f82d89dSTom Warren { 3483f82d89dSTom Warren int div; 3493f82d89dSTom Warren unsigned short clk; 3503f82d89dSTom Warren unsigned long timeout; 3513f82d89dSTom Warren 3523f82d89dSTom Warren debug(" mmc_change_clock called\n"); 3533f82d89dSTom Warren 3543f82d89dSTom Warren /* 3552d348a16STom Warren * Change Tegra SDMMCx clock divisor here. Source is PLLP_OUT0 3563f82d89dSTom Warren */ 3573f82d89dSTom Warren if (clock == 0) 3583f82d89dSTom Warren goto out; 3593f82d89dSTom Warren clock_adjust_periph_pll_div(host->mmc_id, CLOCK_ID_PERIPH, clock, 3603f82d89dSTom Warren &div); 3613f82d89dSTom Warren debug("div = %d\n", div); 3623f82d89dSTom Warren 3633f82d89dSTom Warren writew(0, &host->reg->clkcon); 3643f82d89dSTom Warren 3653f82d89dSTom Warren /* 3663f82d89dSTom Warren * CLKCON 3673f82d89dSTom Warren * SELFREQ[15:8] : base clock divided by value 3683f82d89dSTom Warren * ENSDCLK[2] : SD Clock Enable 3693f82d89dSTom Warren * STBLINTCLK[1] : Internal Clock Stable 3703f82d89dSTom Warren * ENINTCLK[0] : Internal Clock Enable 3713f82d89dSTom Warren */ 3723f82d89dSTom Warren div >>= 1; 3733f82d89dSTom Warren clk = ((div << TEGRA_MMC_CLKCON_SDCLK_FREQ_SEL_SHIFT) | 3743f82d89dSTom Warren TEGRA_MMC_CLKCON_INTERNAL_CLOCK_ENABLE); 3753f82d89dSTom Warren writew(clk, &host->reg->clkcon); 3763f82d89dSTom Warren 3773f82d89dSTom Warren /* Wait max 10 ms */ 3783f82d89dSTom Warren timeout = 10; 3793f82d89dSTom Warren while (!(readw(&host->reg->clkcon) & 3803f82d89dSTom Warren TEGRA_MMC_CLKCON_INTERNAL_CLOCK_STABLE)) { 3813f82d89dSTom Warren if (timeout == 0) { 3823f82d89dSTom Warren printf("%s: timeout error\n", __func__); 3833f82d89dSTom Warren return; 3843f82d89dSTom Warren } 3853f82d89dSTom Warren timeout--; 3863f82d89dSTom Warren udelay(1000); 3873f82d89dSTom Warren } 3883f82d89dSTom Warren 3893f82d89dSTom Warren clk |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; 3903f82d89dSTom Warren writew(clk, &host->reg->clkcon); 3913f82d89dSTom Warren 3923f82d89dSTom Warren debug("mmc_change_clock: clkcon = %08X\n", clk); 3933f82d89dSTom Warren 3943f82d89dSTom Warren out: 3953f82d89dSTom Warren host->clock = clock; 3963f82d89dSTom Warren } 3973f82d89dSTom Warren 398ab769f22SPantelis Antoniou static void tegra_mmc_set_ios(struct mmc *mmc) 3993f82d89dSTom Warren { 4003f82d89dSTom Warren struct mmc_host *host = mmc->priv; 4013f82d89dSTom Warren unsigned char ctrl; 4023f82d89dSTom Warren debug(" mmc_set_ios called\n"); 4033f82d89dSTom Warren 4043f82d89dSTom Warren debug("bus_width: %x, clock: %d\n", mmc->bus_width, mmc->clock); 4053f82d89dSTom Warren 4063f82d89dSTom Warren /* Change clock first */ 4073f82d89dSTom Warren mmc_change_clock(host, mmc->clock); 4083f82d89dSTom Warren 4093f82d89dSTom Warren ctrl = readb(&host->reg->hostctl); 4103f82d89dSTom Warren 4113f82d89dSTom Warren /* 4123f82d89dSTom Warren * WIDE8[5] 4133f82d89dSTom Warren * 0 = Depend on WIDE4 4143f82d89dSTom Warren * 1 = 8-bit mode 4153f82d89dSTom Warren * WIDE4[1] 4163f82d89dSTom Warren * 1 = 4-bit mode 4173f82d89dSTom Warren * 0 = 1-bit mode 4183f82d89dSTom Warren */ 4193f82d89dSTom Warren if (mmc->bus_width == 8) 4203f82d89dSTom Warren ctrl |= (1 << 5); 4213f82d89dSTom Warren else if (mmc->bus_width == 4) 4223f82d89dSTom Warren ctrl |= (1 << 1); 4233f82d89dSTom Warren else 4243f82d89dSTom Warren ctrl &= ~(1 << 1); 4253f82d89dSTom Warren 4263f82d89dSTom Warren writeb(ctrl, &host->reg->hostctl); 4273f82d89dSTom Warren debug("mmc_set_ios: hostctl = %08X\n", ctrl); 4283f82d89dSTom Warren } 4293f82d89dSTom Warren 4302d348a16STom Warren static void mmc_reset(struct mmc_host *host, struct mmc *mmc) 4313f82d89dSTom Warren { 4323f82d89dSTom Warren unsigned int timeout; 4333f82d89dSTom Warren debug(" mmc_reset called\n"); 4343f82d89dSTom Warren 4353f82d89dSTom Warren /* 4363f82d89dSTom Warren * RSTALL[0] : Software reset for all 4373f82d89dSTom Warren * 1 = reset 4383f82d89dSTom Warren * 0 = work 4393f82d89dSTom Warren */ 4403f82d89dSTom Warren writeb(TEGRA_MMC_SWRST_SW_RESET_FOR_ALL, &host->reg->swrst); 4413f82d89dSTom Warren 4423f82d89dSTom Warren host->clock = 0; 4433f82d89dSTom Warren 4443f82d89dSTom Warren /* Wait max 100 ms */ 4453f82d89dSTom Warren timeout = 100; 4463f82d89dSTom Warren 4473f82d89dSTom Warren /* hw clears the bit when it's done */ 4483f82d89dSTom Warren while (readb(&host->reg->swrst) & TEGRA_MMC_SWRST_SW_RESET_FOR_ALL) { 4493f82d89dSTom Warren if (timeout == 0) { 4503f82d89dSTom Warren printf("%s: timeout error\n", __func__); 4513f82d89dSTom Warren return; 4523f82d89dSTom Warren } 4533f82d89dSTom Warren timeout--; 4543f82d89dSTom Warren udelay(1000); 4553f82d89dSTom Warren } 4562d348a16STom Warren 4572d348a16STom Warren /* Set SD bus voltage & enable bus power */ 45893bfd616SPantelis Antoniou mmc_set_power(host, fls(mmc->cfg->voltages) - 1); 4592d348a16STom Warren debug("%s: power control = %02X, host control = %02X\n", __func__, 4602d348a16STom Warren readb(&host->reg->pwrcon), readb(&host->reg->hostctl)); 4612d348a16STom Warren 4622d348a16STom Warren /* Make sure SDIO pads are set up */ 4632d348a16STom Warren pad_init_mmc(host); 4643f82d89dSTom Warren } 4653f82d89dSTom Warren 466ab769f22SPantelis Antoniou static int tegra_mmc_core_init(struct mmc *mmc) 4673f82d89dSTom Warren { 46893bfd616SPantelis Antoniou struct mmc_host *host = mmc->priv; 4693f82d89dSTom Warren unsigned int mask; 4703f82d89dSTom Warren debug(" mmc_core_init called\n"); 4713f82d89dSTom Warren 4722d348a16STom Warren mmc_reset(host, mmc); 4733f82d89dSTom Warren 4743f82d89dSTom Warren host->version = readw(&host->reg->hcver); 4753f82d89dSTom Warren debug("host version = %x\n", host->version); 4763f82d89dSTom Warren 4773f82d89dSTom Warren /* mask all */ 4783f82d89dSTom Warren writel(0xffffffff, &host->reg->norintstsen); 4793f82d89dSTom Warren writel(0xffffffff, &host->reg->norintsigen); 4803f82d89dSTom Warren 4813f82d89dSTom Warren writeb(0xe, &host->reg->timeoutcon); /* TMCLK * 2^27 */ 4823f82d89dSTom Warren /* 4833f82d89dSTom Warren * NORMAL Interrupt Status Enable Register init 4843f82d89dSTom Warren * [5] ENSTABUFRDRDY : Buffer Read Ready Status Enable 4853f82d89dSTom Warren * [4] ENSTABUFWTRDY : Buffer write Ready Status Enable 4863f82d89dSTom Warren * [3] ENSTADMAINT : DMA boundary interrupt 4873f82d89dSTom Warren * [1] ENSTASTANSCMPLT : Transfre Complete Status Enable 4883f82d89dSTom Warren * [0] ENSTACMDCMPLT : Command Complete Status Enable 4893f82d89dSTom Warren */ 4903f82d89dSTom Warren mask = readl(&host->reg->norintstsen); 4913f82d89dSTom Warren mask &= ~(0xffff); 4923f82d89dSTom Warren mask |= (TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | 4933f82d89dSTom Warren TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | 4943f82d89dSTom Warren TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT | 4953f82d89dSTom Warren TEGRA_MMC_NORINTSTSEN_BUFFER_WRITE_READY | 4963f82d89dSTom Warren TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY); 4973f82d89dSTom Warren writel(mask, &host->reg->norintstsen); 4983f82d89dSTom Warren 4993f82d89dSTom Warren /* 5003f82d89dSTom Warren * NORMAL Interrupt Signal Enable Register init 5013f82d89dSTom Warren * [1] ENSTACMDCMPLT : Transfer Complete Signal Enable 5023f82d89dSTom Warren */ 5033f82d89dSTom Warren mask = readl(&host->reg->norintsigen); 5043f82d89dSTom Warren mask &= ~(0xffff); 5053f82d89dSTom Warren mask |= TEGRA_MMC_NORINTSIGEN_XFER_COMPLETE; 5063f82d89dSTom Warren writel(mask, &host->reg->norintsigen); 5073f82d89dSTom Warren 5083f82d89dSTom Warren return 0; 5093f82d89dSTom Warren } 5103f82d89dSTom Warren 51129f3e3f2STom Warren int tegra_mmc_getcd(struct mmc *mmc) 5123f82d89dSTom Warren { 51393bfd616SPantelis Antoniou struct mmc_host *host = mmc->priv; 5143f82d89dSTom Warren 51529f3e3f2STom Warren debug("tegra_mmc_getcd called\n"); 5163f82d89dSTom Warren 517c9aa831eSTom Warren if (fdt_gpio_isvalid(&host->cd_gpio)) 518c9aa831eSTom Warren return fdtdec_get_gpio(&host->cd_gpio); 5193f82d89dSTom Warren 5203f82d89dSTom Warren return 1; 5213f82d89dSTom Warren } 5223f82d89dSTom Warren 523ab769f22SPantelis Antoniou static const struct mmc_ops tegra_mmc_ops = { 524ab769f22SPantelis Antoniou .send_cmd = tegra_mmc_send_cmd, 525ab769f22SPantelis Antoniou .set_ios = tegra_mmc_set_ios, 526ab769f22SPantelis Antoniou .init = tegra_mmc_core_init, 527ab769f22SPantelis Antoniou .getcd = tegra_mmc_getcd, 528ab769f22SPantelis Antoniou }; 529ab769f22SPantelis Antoniou 530c9aa831eSTom Warren static int do_mmc_init(int dev_index) 5313f82d89dSTom Warren { 5323f82d89dSTom Warren struct mmc_host *host; 5333f82d89dSTom Warren char gpusage[12]; /* "SD/MMCn PWR" or "SD/MMCn CD" */ 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 541c9aa831eSTom Warren debug(" do_mmc_init: index %d, bus width %d " 542c9aa831eSTom Warren "pwr_gpio %d cd_gpio %d\n", 543c9aa831eSTom Warren dev_index, host->width, 544c9aa831eSTom Warren host->pwr_gpio.gpio, host->cd_gpio.gpio); 5453f82d89dSTom Warren 5463f82d89dSTom Warren host->clock = 0; 5473f82d89dSTom Warren clock_start_periph_pll(host->mmc_id, CLOCK_ID_PERIPH, 20000000); 5483f82d89dSTom Warren 549c9aa831eSTom Warren if (fdt_gpio_isvalid(&host->pwr_gpio)) { 5503f82d89dSTom Warren sprintf(gpusage, "SD/MMC%d PWR", dev_index); 551c9aa831eSTom Warren gpio_request(host->pwr_gpio.gpio, gpusage); 552c9aa831eSTom Warren gpio_direction_output(host->pwr_gpio.gpio, 1); 553c9aa831eSTom Warren debug(" Power GPIO name = %s\n", host->pwr_gpio.name); 5543f82d89dSTom Warren } 5553f82d89dSTom Warren 556c9aa831eSTom Warren if (fdt_gpio_isvalid(&host->cd_gpio)) { 5573f82d89dSTom Warren sprintf(gpusage, "SD/MMC%d CD", dev_index); 558c9aa831eSTom Warren gpio_request(host->cd_gpio.gpio, gpusage); 559c9aa831eSTom Warren gpio_direction_input(host->cd_gpio.gpio); 560c9aa831eSTom Warren debug(" CD GPIO name = %s\n", host->cd_gpio.name); 5613f82d89dSTom Warren } 5623f82d89dSTom Warren 56393bfd616SPantelis Antoniou memset(&host->cfg, 0, sizeof(host->cfg)); 5643f82d89dSTom Warren 56593bfd616SPantelis Antoniou host->cfg.name = "Tegra SD/MMC"; 56693bfd616SPantelis Antoniou host->cfg.ops = &tegra_mmc_ops; 5673f82d89dSTom Warren 56893bfd616SPantelis Antoniou host->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; 56993bfd616SPantelis Antoniou host->cfg.host_caps = 0; 570c9aa831eSTom Warren if (host->width == 8) 57193bfd616SPantelis Antoniou host->cfg.host_caps |= MMC_MODE_8BIT; 572c9aa831eSTom Warren if (host->width >= 4) 57393bfd616SPantelis Antoniou host->cfg.host_caps |= MMC_MODE_4BIT; 57493bfd616SPantelis Antoniou host->cfg.host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS | MMC_MODE_HC; 5753f82d89dSTom Warren 5763f82d89dSTom Warren /* 5773f82d89dSTom Warren * min freq is for card identification, and is the highest 5783f82d89dSTom Warren * low-speed SDIO card frequency (actually 400KHz) 5793f82d89dSTom Warren * max freq is highest HS eMMC clock as per the SD/MMC spec 5803f82d89dSTom Warren * (actually 52MHz) 5813f82d89dSTom Warren */ 58293bfd616SPantelis Antoniou host->cfg.f_min = 375000; 58393bfd616SPantelis Antoniou host->cfg.f_max = 48000000; 5843f82d89dSTom Warren 58593bfd616SPantelis Antoniou host->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; 58693bfd616SPantelis Antoniou 58793bfd616SPantelis Antoniou mmc = mmc_create(&host->cfg, host); 58893bfd616SPantelis Antoniou if (mmc == NULL) 58993bfd616SPantelis Antoniou return -1; 5903f82d89dSTom Warren 5913f82d89dSTom Warren return 0; 5923f82d89dSTom Warren } 593c9aa831eSTom Warren 594c9aa831eSTom Warren /** 595c9aa831eSTom Warren * Get the host address and peripheral ID for a node. 596c9aa831eSTom Warren * 597c9aa831eSTom Warren * @param blob fdt blob 598c9aa831eSTom Warren * @param node Device index (0-3) 599c9aa831eSTom Warren * @param host Structure to fill in (reg, width, mmc_id) 600c9aa831eSTom Warren */ 601c9aa831eSTom Warren static int mmc_get_config(const void *blob, int node, struct mmc_host *host) 602c9aa831eSTom Warren { 603c9aa831eSTom Warren debug("%s: node = %d\n", __func__, node); 604c9aa831eSTom Warren 605c9aa831eSTom Warren host->enabled = fdtdec_get_is_enabled(blob, node); 606c9aa831eSTom Warren 607c9aa831eSTom Warren host->reg = (struct tegra_mmc *)fdtdec_get_addr(blob, node, "reg"); 608c9aa831eSTom Warren if ((fdt_addr_t)host->reg == FDT_ADDR_T_NONE) { 609c9aa831eSTom Warren debug("%s: no sdmmc base reg info found\n", __func__); 610c9aa831eSTom Warren return -FDT_ERR_NOTFOUND; 611c9aa831eSTom Warren } 612c9aa831eSTom Warren 613c9aa831eSTom Warren host->mmc_id = clock_decode_periph_id(blob, node); 614c9aa831eSTom Warren if (host->mmc_id == PERIPH_ID_NONE) { 615c9aa831eSTom Warren debug("%s: could not decode periph id\n", __func__); 616c9aa831eSTom Warren return -FDT_ERR_NOTFOUND; 617c9aa831eSTom Warren } 618c9aa831eSTom Warren 619c9aa831eSTom Warren /* 620c9aa831eSTom Warren * NOTE: mmc->bus_width is determined by mmc.c dynamically. 621c9aa831eSTom Warren * TBD: Override it with this value? 622c9aa831eSTom Warren */ 623c9aa831eSTom Warren host->width = fdtdec_get_int(blob, node, "bus-width", 0); 624c9aa831eSTom Warren if (!host->width) 625c9aa831eSTom Warren debug("%s: no sdmmc width found\n", __func__); 626c9aa831eSTom Warren 627c9aa831eSTom Warren /* These GPIOs are optional */ 628c9aa831eSTom Warren fdtdec_decode_gpio(blob, node, "cd-gpios", &host->cd_gpio); 629c9aa831eSTom Warren fdtdec_decode_gpio(blob, node, "wp-gpios", &host->wp_gpio); 630c9aa831eSTom Warren fdtdec_decode_gpio(blob, node, "power-gpios", &host->pwr_gpio); 631c9aa831eSTom Warren 632c9aa831eSTom Warren debug("%s: found controller at %p, width = %d, periph_id = %d\n", 633c9aa831eSTom Warren __func__, host->reg, host->width, host->mmc_id); 634c9aa831eSTom Warren return 0; 635c9aa831eSTom Warren } 636c9aa831eSTom Warren 637c9aa831eSTom Warren /* 638c9aa831eSTom Warren * Process a list of nodes, adding them to our list of SDMMC ports. 639c9aa831eSTom Warren * 640c9aa831eSTom Warren * @param blob fdt blob 641c9aa831eSTom Warren * @param node_list list of nodes to process (any <=0 are ignored) 642c9aa831eSTom Warren * @param count number of nodes to process 643c9aa831eSTom Warren * @return 0 if ok, -1 on error 644c9aa831eSTom Warren */ 645c9aa831eSTom Warren static int process_nodes(const void *blob, int node_list[], int count) 646c9aa831eSTom Warren { 647c9aa831eSTom Warren struct mmc_host *host; 648c9aa831eSTom Warren int i, node; 649c9aa831eSTom Warren 650c9aa831eSTom Warren debug("%s: count = %d\n", __func__, count); 651c9aa831eSTom Warren 652c9aa831eSTom Warren /* build mmc_host[] for each controller */ 653c9aa831eSTom Warren for (i = 0; i < count; i++) { 654c9aa831eSTom Warren node = node_list[i]; 655c9aa831eSTom Warren if (node <= 0) 656c9aa831eSTom Warren continue; 657c9aa831eSTom Warren 658c9aa831eSTom Warren host = &mmc_host[i]; 659c9aa831eSTom Warren host->id = i; 660c9aa831eSTom Warren 661c9aa831eSTom Warren if (mmc_get_config(blob, node, host)) { 662c9aa831eSTom Warren printf("%s: failed to decode dev %d\n", __func__, i); 663c9aa831eSTom Warren return -1; 664c9aa831eSTom Warren } 665c9aa831eSTom Warren do_mmc_init(i); 666c9aa831eSTom Warren } 667c9aa831eSTom Warren return 0; 668c9aa831eSTom Warren } 669c9aa831eSTom Warren 670c9aa831eSTom Warren void tegra_mmc_init(void) 671c9aa831eSTom Warren { 672*f175603fSStephen Warren int node_list[CONFIG_SYS_MMC_MAX_DEVICE], count; 673c9aa831eSTom Warren const void *blob = gd->fdt_blob; 674c9aa831eSTom Warren debug("%s entry\n", __func__); 675c9aa831eSTom Warren 676a73ca478SStephen Warren /* See if any Tegra124 MMC controllers are present */ 677a73ca478SStephen Warren count = fdtdec_find_aliases_for_id(blob, "sdhci", 678*f175603fSStephen Warren COMPAT_NVIDIA_TEGRA124_SDMMC, node_list, 679*f175603fSStephen Warren CONFIG_SYS_MMC_MAX_DEVICE); 680a73ca478SStephen Warren debug("%s: count of Tegra124 sdhci nodes is %d\n", __func__, count); 681a73ca478SStephen Warren if (process_nodes(blob, node_list, count)) { 682a73ca478SStephen Warren printf("%s: Error processing T30 mmc node(s)!\n", __func__); 683a73ca478SStephen Warren return; 684a73ca478SStephen Warren } 685a73ca478SStephen Warren 6862d348a16STom Warren /* See if any Tegra30 MMC controllers are present */ 6872d348a16STom Warren count = fdtdec_find_aliases_for_id(blob, "sdhci", 688*f175603fSStephen Warren COMPAT_NVIDIA_TEGRA30_SDMMC, node_list, 689*f175603fSStephen Warren CONFIG_SYS_MMC_MAX_DEVICE); 6902d348a16STom Warren debug("%s: count of T30 sdhci nodes is %d\n", __func__, count); 6912d348a16STom Warren if (process_nodes(blob, node_list, count)) { 6922d348a16STom Warren printf("%s: Error processing T30 mmc node(s)!\n", __func__); 6932d348a16STom Warren return; 6942d348a16STom Warren } 6952d348a16STom Warren 6962d348a16STom Warren /* Now look for any Tegra20 MMC controllers */ 697c9aa831eSTom Warren count = fdtdec_find_aliases_for_id(blob, "sdhci", 698*f175603fSStephen Warren COMPAT_NVIDIA_TEGRA20_SDMMC, node_list, 699*f175603fSStephen Warren CONFIG_SYS_MMC_MAX_DEVICE); 7002d348a16STom Warren debug("%s: count of T20 sdhci nodes is %d\n", __func__, count); 701c9aa831eSTom Warren if (process_nodes(blob, node_list, count)) { 7022d348a16STom Warren printf("%s: Error processing T20 mmc node(s)!\n", __func__); 703c9aa831eSTom Warren return; 704c9aa831eSTom Warren } 705c9aa831eSTom Warren } 706