11592ef85SReinhard Meyer /* 21592ef85SReinhard Meyer * Copyright (C) 2010 31592ef85SReinhard Meyer * Rob Emanuele <rob@emanuele.us> 41592ef85SReinhard Meyer * Reinhard Meyer, EMK Elektronik <reinhard.meyer@emk-elektronik.de> 51592ef85SReinhard Meyer * 61592ef85SReinhard Meyer * Original Driver: 71592ef85SReinhard Meyer * Copyright (C) 2004-2006 Atmel Corporation 81592ef85SReinhard Meyer * 91a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 101592ef85SReinhard Meyer */ 111592ef85SReinhard Meyer 121592ef85SReinhard Meyer #include <common.h> 13*c86c0155SWenyou Yang #include <clk.h> 141592ef85SReinhard Meyer #include <mmc.h> 151592ef85SReinhard Meyer #include <part.h> 161592ef85SReinhard Meyer #include <malloc.h> 171592ef85SReinhard Meyer #include <asm/io.h> 181221ce45SMasahiro Yamada #include <linux/errno.h> 191592ef85SReinhard Meyer #include <asm/byteorder.h> 201592ef85SReinhard Meyer #include <asm/arch/clk.h> 21329f0f52SReinhard Meyer #include <asm/arch/hardware.h> 22*c86c0155SWenyou Yang #include <dm/device.h> 231592ef85SReinhard Meyer #include "atmel_mci.h" 241592ef85SReinhard Meyer 25*c86c0155SWenyou Yang DECLARE_GLOBAL_DATA_PTR; 26*c86c0155SWenyou Yang 271592ef85SReinhard Meyer #ifndef CONFIG_SYS_MMC_CLK_OD 281592ef85SReinhard Meyer # define CONFIG_SYS_MMC_CLK_OD 150000 291592ef85SReinhard Meyer #endif 301592ef85SReinhard Meyer 311592ef85SReinhard Meyer #define MMC_DEFAULT_BLKLEN 512 321592ef85SReinhard Meyer 331592ef85SReinhard Meyer #if defined(CONFIG_ATMEL_MCI_PORTB) 341592ef85SReinhard Meyer # define MCI_BUS 1 351592ef85SReinhard Meyer #else 361592ef85SReinhard Meyer # define MCI_BUS 0 371592ef85SReinhard Meyer #endif 381592ef85SReinhard Meyer 396b75d359SMarek Vasut struct atmel_mci_priv { 406b75d359SMarek Vasut struct mmc_config cfg; 416b75d359SMarek Vasut struct atmel_mci *mci; 42877807e1SMarek Vasut unsigned int initialized:1; 43b4670a0cSGregory CLEMENT unsigned int curr_clk; 44*c86c0155SWenyou Yang #ifdef CONFIG_DM_MMC 45*c86c0155SWenyou Yang struct mmc mmc; 46*c86c0155SWenyou Yang ulong bus_clk_rate; 47*c86c0155SWenyou Yang #endif 486b75d359SMarek Vasut }; 496b75d359SMarek Vasut 50aac4b69bSBo Shen /* Read Atmel MCI IP version */ 51aac4b69bSBo Shen static unsigned int atmel_mci_get_version(struct atmel_mci *mci) 52aac4b69bSBo Shen { 53aac4b69bSBo Shen return readl(&mci->version) & 0x00000fff; 54aac4b69bSBo Shen } 55aac4b69bSBo Shen 561592ef85SReinhard Meyer /* 571592ef85SReinhard Meyer * Print command and status: 581592ef85SReinhard Meyer * 591592ef85SReinhard Meyer * - always when DEBUG is defined 601592ef85SReinhard Meyer * - on command errors 611592ef85SReinhard Meyer */ 621592ef85SReinhard Meyer static void dump_cmd(u32 cmdr, u32 arg, u32 status, const char* msg) 631592ef85SReinhard Meyer { 64b84c9c9aSMarek Vasut debug("gen_atmel_mci: CMDR %08x (%2u) ARGR %08x (SR: %08x) %s\n", 651592ef85SReinhard Meyer cmdr, cmdr & 0x3F, arg, status, msg); 661592ef85SReinhard Meyer } 671592ef85SReinhard Meyer 681592ef85SReinhard Meyer /* Setup for MCI Clock and Block Size */ 69*c86c0155SWenyou Yang #ifdef CONFIG_DM_MMC 70*c86c0155SWenyou Yang static void mci_set_mode(struct atmel_mci_priv *priv, u32 hz, u32 blklen) 71*c86c0155SWenyou Yang { 72*c86c0155SWenyou Yang struct mmc *mmc = &priv->mmc; 73*c86c0155SWenyou Yang u32 bus_hz = priv->bus_clk_rate; 74*c86c0155SWenyou Yang #else 751592ef85SReinhard Meyer static void mci_set_mode(struct mmc *mmc, u32 hz, u32 blklen) 761592ef85SReinhard Meyer { 776b75d359SMarek Vasut struct atmel_mci_priv *priv = mmc->priv; 781592ef85SReinhard Meyer u32 bus_hz = get_mci_clk_rate(); 79*c86c0155SWenyou Yang #endif 80*c86c0155SWenyou Yang 81*c86c0155SWenyou Yang atmel_mci_t *mci = priv->mci; 821592ef85SReinhard Meyer u32 clkdiv = 255; 83cd60ebd4SBo Shen unsigned int version = atmel_mci_get_version(mci); 84cd60ebd4SBo Shen u32 clkodd = 0; 85cd60ebd4SBo Shen u32 mr; 861592ef85SReinhard Meyer 871592ef85SReinhard Meyer debug("mci: bus_hz is %u, setting clock %u Hz, block size %u\n", 881592ef85SReinhard Meyer bus_hz, hz, blklen); 891592ef85SReinhard Meyer if (hz > 0) { 90cd60ebd4SBo Shen if (version >= 0x500) { 91cd60ebd4SBo Shen clkdiv = DIV_ROUND_UP(bus_hz, hz) - 2; 92cd60ebd4SBo Shen if (clkdiv > 511) 93cd60ebd4SBo Shen clkdiv = 511; 94cd60ebd4SBo Shen 95cd60ebd4SBo Shen clkodd = clkdiv & 1; 96cd60ebd4SBo Shen clkdiv >>= 1; 97cd60ebd4SBo Shen 98b84c9c9aSMarek Vasut debug("mci: setting clock %u Hz, block size %u\n", 99cd60ebd4SBo Shen bus_hz / (clkdiv * 2 + clkodd + 2), blklen); 100cd60ebd4SBo Shen } else { 101cd60ebd4SBo Shen /* find clkdiv yielding a rate <= than requested */ 1021592ef85SReinhard Meyer for (clkdiv = 0; clkdiv < 255; clkdiv++) { 1031592ef85SReinhard Meyer if ((bus_hz / (clkdiv + 1) / 2) <= hz) 1041592ef85SReinhard Meyer break; 1051592ef85SReinhard Meyer } 106b84c9c9aSMarek Vasut debug("mci: setting clock %u Hz, block size %u\n", 1071592ef85SReinhard Meyer (bus_hz / (clkdiv + 1)) / 2, blklen); 1081592ef85SReinhard Meyer 109cd60ebd4SBo Shen } 110cd60ebd4SBo Shen } 111b4670a0cSGregory CLEMENT if (version >= 0x500) 112b4670a0cSGregory CLEMENT priv->curr_clk = bus_hz / (clkdiv * 2 + clkodd + 2); 113b4670a0cSGregory CLEMENT else 114b4670a0cSGregory CLEMENT priv->curr_clk = (bus_hz / (clkdiv + 1)) / 2; 1151592ef85SReinhard Meyer blklen &= 0xfffc; 116cd60ebd4SBo Shen 117cd60ebd4SBo Shen mr = MMCI_BF(CLKDIV, clkdiv); 118cd60ebd4SBo Shen 119cd60ebd4SBo Shen /* MCI IP version >= 0x200 has R/WPROOF */ 120cd60ebd4SBo Shen if (version >= 0x200) 121cd60ebd4SBo Shen mr |= MMCI_BIT(RDPROOF) | MMCI_BIT(WRPROOF); 122cd60ebd4SBo Shen 1231db7377aSWu, Josh /* 124cd60ebd4SBo Shen * MCI IP version >= 0x500 use bit 16 as clkodd. 125cd60ebd4SBo Shen * MCI IP version < 0x500 use upper 16 bits for blklen. 1261db7377aSWu, Josh */ 127cd60ebd4SBo Shen if (version >= 0x500) 128cd60ebd4SBo Shen mr |= MMCI_BF(CLKODD, clkodd); 129cd60ebd4SBo Shen else 130cd60ebd4SBo Shen mr |= MMCI_BF(BLKLEN, blklen); 131cd60ebd4SBo Shen 132cd60ebd4SBo Shen writel(mr, &mci->mr); 133cd60ebd4SBo Shen 134cd60ebd4SBo Shen /* MCI IP version >= 0x200 has blkr */ 135cd60ebd4SBo Shen if (version >= 0x200) 1361db7377aSWu, Josh writel(MMCI_BF(BLKLEN, blklen), &mci->blkr); 137cd60ebd4SBo Shen 138da55c66eSBo Shen if (mmc->card_caps & mmc->cfg->host_caps & MMC_MODE_HS) 139da55c66eSBo Shen writel(MMCI_BIT(HSMODE), &mci->cfg); 140da55c66eSBo Shen 141877807e1SMarek Vasut priv->initialized = 1; 1421592ef85SReinhard Meyer } 1431592ef85SReinhard Meyer 1441592ef85SReinhard Meyer /* Return the CMDR with flags for a given command and data packet */ 1451592ef85SReinhard Meyer static u32 mci_encode_cmd( 1461592ef85SReinhard Meyer struct mmc_cmd *cmd, struct mmc_data *data, u32* error_flags) 1471592ef85SReinhard Meyer { 1481592ef85SReinhard Meyer u32 cmdr = 0; 1491592ef85SReinhard Meyer 1501592ef85SReinhard Meyer /* Default Flags for Errors */ 1511592ef85SReinhard Meyer *error_flags |= (MMCI_BIT(DTOE) | MMCI_BIT(RDIRE) | MMCI_BIT(RENDE) | 1521592ef85SReinhard Meyer MMCI_BIT(RINDE) | MMCI_BIT(RTOE)); 1531592ef85SReinhard Meyer 1541592ef85SReinhard Meyer /* Default Flags for the Command */ 1551592ef85SReinhard Meyer cmdr |= MMCI_BIT(MAXLAT); 1561592ef85SReinhard Meyer 1571592ef85SReinhard Meyer if (data) { 1581592ef85SReinhard Meyer cmdr |= MMCI_BF(TRCMD, 1); 1591592ef85SReinhard Meyer if (data->blocks > 1) 1601592ef85SReinhard Meyer cmdr |= MMCI_BF(TRTYP, 1); 1611592ef85SReinhard Meyer if (data->flags & MMC_DATA_READ) 1621592ef85SReinhard Meyer cmdr |= MMCI_BIT(TRDIR); 1631592ef85SReinhard Meyer } 1641592ef85SReinhard Meyer 1651592ef85SReinhard Meyer if (cmd->resp_type & MMC_RSP_CRC) 1661592ef85SReinhard Meyer *error_flags |= MMCI_BIT(RCRCE); 1671592ef85SReinhard Meyer if (cmd->resp_type & MMC_RSP_136) 1681592ef85SReinhard Meyer cmdr |= MMCI_BF(RSPTYP, 2); 1691592ef85SReinhard Meyer else if (cmd->resp_type & MMC_RSP_BUSY) 1701592ef85SReinhard Meyer cmdr |= MMCI_BF(RSPTYP, 3); 1711592ef85SReinhard Meyer else if (cmd->resp_type & MMC_RSP_PRESENT) 1721592ef85SReinhard Meyer cmdr |= MMCI_BF(RSPTYP, 1); 1731592ef85SReinhard Meyer 1741592ef85SReinhard Meyer return cmdr | MMCI_BF(CMDNB, cmd->cmdidx); 1751592ef85SReinhard Meyer } 1761592ef85SReinhard Meyer 1771592ef85SReinhard Meyer /* Entered into function pointer in mci_send_cmd */ 1781592ef85SReinhard Meyer static u32 mci_data_read(atmel_mci_t *mci, u32* data, u32 error_flags) 1791592ef85SReinhard Meyer { 1801592ef85SReinhard Meyer u32 status; 1811592ef85SReinhard Meyer 1821592ef85SReinhard Meyer do { 1831592ef85SReinhard Meyer status = readl(&mci->sr); 1841592ef85SReinhard Meyer if (status & (error_flags | MMCI_BIT(OVRE))) 1851592ef85SReinhard Meyer goto io_fail; 1861592ef85SReinhard Meyer } while (!(status & MMCI_BIT(RXRDY))); 1871592ef85SReinhard Meyer 1881592ef85SReinhard Meyer if (status & MMCI_BIT(RXRDY)) { 1891592ef85SReinhard Meyer *data = readl(&mci->rdr); 1901592ef85SReinhard Meyer status = 0; 1911592ef85SReinhard Meyer } 1921592ef85SReinhard Meyer io_fail: 1931592ef85SReinhard Meyer return status; 1941592ef85SReinhard Meyer } 1951592ef85SReinhard Meyer 1961592ef85SReinhard Meyer /* Entered into function pointer in mci_send_cmd */ 1971592ef85SReinhard Meyer static u32 mci_data_write(atmel_mci_t *mci, u32* data, u32 error_flags) 1981592ef85SReinhard Meyer { 1991592ef85SReinhard Meyer u32 status; 2001592ef85SReinhard Meyer 2011592ef85SReinhard Meyer do { 2021592ef85SReinhard Meyer status = readl(&mci->sr); 2031592ef85SReinhard Meyer if (status & (error_flags | MMCI_BIT(UNRE))) 2041592ef85SReinhard Meyer goto io_fail; 2051592ef85SReinhard Meyer } while (!(status & MMCI_BIT(TXRDY))); 2061592ef85SReinhard Meyer 2071592ef85SReinhard Meyer if (status & MMCI_BIT(TXRDY)) { 2081592ef85SReinhard Meyer writel(*data, &mci->tdr); 2091592ef85SReinhard Meyer status = 0; 2101592ef85SReinhard Meyer } 2111592ef85SReinhard Meyer io_fail: 2121592ef85SReinhard Meyer return status; 2131592ef85SReinhard Meyer } 2141592ef85SReinhard Meyer 2151592ef85SReinhard Meyer /* 2161592ef85SReinhard Meyer * Entered into mmc structure during driver init 2171592ef85SReinhard Meyer * 2181592ef85SReinhard Meyer * Sends a command out on the bus and deals with the block data. 2191592ef85SReinhard Meyer * Takes the mmc pointer, a command pointer, and an optional data pointer. 2201592ef85SReinhard Meyer */ 221*c86c0155SWenyou Yang #ifdef CONFIG_DM_MMC 222*c86c0155SWenyou Yang static int atmel_mci_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, 223*c86c0155SWenyou Yang struct mmc_data *data) 224*c86c0155SWenyou Yang { 225*c86c0155SWenyou Yang struct atmel_mci_priv *priv = dev_get_priv(dev); 226*c86c0155SWenyou Yang struct mmc *mmc = mmc_get_mmc_dev(dev); 227*c86c0155SWenyou Yang #else 2281592ef85SReinhard Meyer static int 2291592ef85SReinhard Meyer mci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) 2301592ef85SReinhard Meyer { 2316b75d359SMarek Vasut struct atmel_mci_priv *priv = mmc->priv; 232*c86c0155SWenyou Yang #endif 2336b75d359SMarek Vasut atmel_mci_t *mci = priv->mci; 2341592ef85SReinhard Meyer u32 cmdr; 2351592ef85SReinhard Meyer u32 error_flags = 0; 2361592ef85SReinhard Meyer u32 status; 2371592ef85SReinhard Meyer 238877807e1SMarek Vasut if (!priv->initialized) { 2391592ef85SReinhard Meyer puts ("MCI not initialized!\n"); 240915ffa52SJaehoon Chung return -ECOMM; 2411592ef85SReinhard Meyer } 2421592ef85SReinhard Meyer 2431592ef85SReinhard Meyer /* Figure out the transfer arguments */ 2441592ef85SReinhard Meyer cmdr = mci_encode_cmd(cmd, data, &error_flags); 2451592ef85SReinhard Meyer 2461db7377aSWu, Josh /* For multi blocks read/write, set the block register */ 2471db7377aSWu, Josh if ((cmd->cmdidx == MMC_CMD_READ_MULTIPLE_BLOCK) 2481db7377aSWu, Josh || (cmd->cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK)) 2491db7377aSWu, Josh writel(data->blocks | MMCI_BF(BLKLEN, mmc->read_bl_len), 2501db7377aSWu, Josh &mci->blkr); 2511db7377aSWu, Josh 2521592ef85SReinhard Meyer /* Send the command */ 2531592ef85SReinhard Meyer writel(cmd->cmdarg, &mci->argr); 2541592ef85SReinhard Meyer writel(cmdr, &mci->cmdr); 2551592ef85SReinhard Meyer 2561592ef85SReinhard Meyer #ifdef DEBUG 2571592ef85SReinhard Meyer dump_cmd(cmdr, cmd->cmdarg, 0, "DEBUG"); 2581592ef85SReinhard Meyer #endif 2591592ef85SReinhard Meyer 2601592ef85SReinhard Meyer /* Wait for the command to complete */ 2611592ef85SReinhard Meyer while (!((status = readl(&mci->sr)) & MMCI_BIT(CMDRDY))); 2621592ef85SReinhard Meyer 26393e3236cSBo Shen if ((status & error_flags) & MMCI_BIT(RTOE)) { 26493e3236cSBo Shen dump_cmd(cmdr, cmd->cmdarg, status, "Command Time Out"); 265915ffa52SJaehoon Chung return -ETIMEDOUT; 26693e3236cSBo Shen } else if (status & error_flags) { 2671592ef85SReinhard Meyer dump_cmd(cmdr, cmd->cmdarg, status, "Command Failed"); 268915ffa52SJaehoon Chung return -ECOMM; 2691592ef85SReinhard Meyer } 2701592ef85SReinhard Meyer 2711592ef85SReinhard Meyer /* Copy the response to the response buffer */ 2721592ef85SReinhard Meyer if (cmd->resp_type & MMC_RSP_136) { 2731592ef85SReinhard Meyer cmd->response[0] = readl(&mci->rspr); 2741592ef85SReinhard Meyer cmd->response[1] = readl(&mci->rspr1); 2751592ef85SReinhard Meyer cmd->response[2] = readl(&mci->rspr2); 2761592ef85SReinhard Meyer cmd->response[3] = readl(&mci->rspr3); 2771592ef85SReinhard Meyer } else 2781592ef85SReinhard Meyer cmd->response[0] = readl(&mci->rspr); 2791592ef85SReinhard Meyer 2801592ef85SReinhard Meyer /* transfer all of the blocks */ 2811592ef85SReinhard Meyer if (data) { 2821592ef85SReinhard Meyer u32 word_count, block_count; 2831592ef85SReinhard Meyer u32* ioptr; 2841592ef85SReinhard Meyer u32 sys_blocksize, dummy, i; 2851592ef85SReinhard Meyer u32 (*mci_data_op) 2861592ef85SReinhard Meyer (atmel_mci_t *mci, u32* data, u32 error_flags); 2871592ef85SReinhard Meyer 2881592ef85SReinhard Meyer if (data->flags & MMC_DATA_READ) { 2891592ef85SReinhard Meyer mci_data_op = mci_data_read; 2901592ef85SReinhard Meyer sys_blocksize = mmc->read_bl_len; 2911592ef85SReinhard Meyer ioptr = (u32*)data->dest; 2921592ef85SReinhard Meyer } else { 2931592ef85SReinhard Meyer mci_data_op = mci_data_write; 2941592ef85SReinhard Meyer sys_blocksize = mmc->write_bl_len; 2951592ef85SReinhard Meyer ioptr = (u32*)data->src; 2961592ef85SReinhard Meyer } 2971592ef85SReinhard Meyer 2981592ef85SReinhard Meyer status = 0; 2991592ef85SReinhard Meyer for (block_count = 0; 3001592ef85SReinhard Meyer block_count < data->blocks && !status; 3011592ef85SReinhard Meyer block_count++) { 3021592ef85SReinhard Meyer word_count = 0; 3031592ef85SReinhard Meyer do { 3041592ef85SReinhard Meyer status = mci_data_op(mci, ioptr, error_flags); 3051592ef85SReinhard Meyer word_count++; 3061592ef85SReinhard Meyer ioptr++; 3071592ef85SReinhard Meyer } while (!status && word_count < (data->blocksize/4)); 3081592ef85SReinhard Meyer #ifdef DEBUG 3091592ef85SReinhard Meyer if (data->flags & MMC_DATA_READ) 3101592ef85SReinhard Meyer { 3119902c7b6SWu, Josh u32 cnt = word_count * 4; 3121592ef85SReinhard Meyer printf("Read Data:\n"); 3139902c7b6SWu, Josh print_buffer(0, data->dest + cnt * block_count, 3149902c7b6SWu, Josh 1, cnt, 0); 3151592ef85SReinhard Meyer } 3161592ef85SReinhard Meyer #endif 3171592ef85SReinhard Meyer #ifdef DEBUG 3181592ef85SReinhard Meyer if (!status && word_count < (sys_blocksize / 4)) 3191592ef85SReinhard Meyer printf("filling rest of block...\n"); 3201592ef85SReinhard Meyer #endif 3211592ef85SReinhard Meyer /* fill the rest of a full block */ 3221592ef85SReinhard Meyer while (!status && word_count < (sys_blocksize / 4)) { 3231592ef85SReinhard Meyer status = mci_data_op(mci, &dummy, 3241592ef85SReinhard Meyer error_flags); 3251592ef85SReinhard Meyer word_count++; 3261592ef85SReinhard Meyer } 3271592ef85SReinhard Meyer if (status) { 3281592ef85SReinhard Meyer dump_cmd(cmdr, cmd->cmdarg, status, 3291592ef85SReinhard Meyer "Data Transfer Failed"); 330915ffa52SJaehoon Chung return -ECOMM; 3311592ef85SReinhard Meyer } 3321592ef85SReinhard Meyer } 3331592ef85SReinhard Meyer 3341592ef85SReinhard Meyer /* Wait for Transfer End */ 3351592ef85SReinhard Meyer i = 0; 3361592ef85SReinhard Meyer do { 3371592ef85SReinhard Meyer status = readl(&mci->sr); 3381592ef85SReinhard Meyer 3391592ef85SReinhard Meyer if (status & error_flags) { 3401592ef85SReinhard Meyer dump_cmd(cmdr, cmd->cmdarg, status, 3411592ef85SReinhard Meyer "DTIP Wait Failed"); 342915ffa52SJaehoon Chung return -ECOMM; 3431592ef85SReinhard Meyer } 3441592ef85SReinhard Meyer i++; 3451592ef85SReinhard Meyer } while ((status & MMCI_BIT(DTIP)) && i < 10000); 3461592ef85SReinhard Meyer if (status & MMCI_BIT(DTIP)) { 3471592ef85SReinhard Meyer dump_cmd(cmdr, cmd->cmdarg, status, 3481592ef85SReinhard Meyer "XFER DTIP never unset, ignoring"); 3491592ef85SReinhard Meyer } 3501592ef85SReinhard Meyer } 3511592ef85SReinhard Meyer 352b4670a0cSGregory CLEMENT /* 353b4670a0cSGregory CLEMENT * After the switch command, wait for 8 clocks before the next 354b4670a0cSGregory CLEMENT * command 355b4670a0cSGregory CLEMENT */ 356b4670a0cSGregory CLEMENT if (cmd->cmdidx == MMC_CMD_SWITCH) 357b4670a0cSGregory CLEMENT udelay(8*1000000 / priv->curr_clk); /* 8 clk in us */ 358b4670a0cSGregory CLEMENT 3591592ef85SReinhard Meyer return 0; 3601592ef85SReinhard Meyer } 3611592ef85SReinhard Meyer 362*c86c0155SWenyou Yang #ifdef CONFIG_DM_MMC 363*c86c0155SWenyou Yang static int atmel_mci_set_ios(struct udevice *dev) 364*c86c0155SWenyou Yang { 365*c86c0155SWenyou Yang struct atmel_mci_priv *priv = dev_get_priv(dev); 366*c86c0155SWenyou Yang struct mmc *mmc = mmc_get_mmc_dev(dev); 367*c86c0155SWenyou Yang #else 3681592ef85SReinhard Meyer /* Entered into mmc structure during driver init */ 36907b0b9c0SJaehoon Chung static int mci_set_ios(struct mmc *mmc) 3701592ef85SReinhard Meyer { 3716b75d359SMarek Vasut struct atmel_mci_priv *priv = mmc->priv; 372*c86c0155SWenyou Yang #endif 3736b75d359SMarek Vasut atmel_mci_t *mci = priv->mci; 374aac4b69bSBo Shen int bus_width = mmc->bus_width; 375aac4b69bSBo Shen unsigned int version = atmel_mci_get_version(mci); 376aac4b69bSBo Shen int busw; 3771592ef85SReinhard Meyer 3781592ef85SReinhard Meyer /* Set the clock speed */ 379*c86c0155SWenyou Yang #ifdef CONFIG_DM_MMC 380*c86c0155SWenyou Yang mci_set_mode(priv, mmc->clock, MMC_DEFAULT_BLKLEN); 381*c86c0155SWenyou Yang #else 3821592ef85SReinhard Meyer mci_set_mode(mmc, mmc->clock, MMC_DEFAULT_BLKLEN); 383*c86c0155SWenyou Yang #endif 3841592ef85SReinhard Meyer 3851592ef85SReinhard Meyer /* 3861592ef85SReinhard Meyer * set the bus width and select slot for this interface 3871592ef85SReinhard Meyer * there is no capability for multiple slots on the same interface yet 3881592ef85SReinhard Meyer */ 389aac4b69bSBo Shen if ((version & 0xf00) >= 0x300) { 390aac4b69bSBo Shen switch (bus_width) { 391aac4b69bSBo Shen case 8: 392aac4b69bSBo Shen busw = 3; 393aac4b69bSBo Shen break; 394aac4b69bSBo Shen case 4: 395aac4b69bSBo Shen busw = 2; 396aac4b69bSBo Shen break; 397aac4b69bSBo Shen default: 398aac4b69bSBo Shen busw = 0; 399aac4b69bSBo Shen break; 400aac4b69bSBo Shen } 401aac4b69bSBo Shen 402aac4b69bSBo Shen writel(busw << 6 | MMCI_BF(SCDSEL, MCI_BUS), &mci->sdcr); 403aac4b69bSBo Shen } else { 404aac4b69bSBo Shen busw = (bus_width == 4) ? 1 : 0; 405aac4b69bSBo Shen 406aac4b69bSBo Shen writel(busw << 7 | MMCI_BF(SCDSEL, MCI_BUS), &mci->sdcr); 407aac4b69bSBo Shen } 40807b0b9c0SJaehoon Chung 40907b0b9c0SJaehoon Chung return 0; 4101592ef85SReinhard Meyer } 4111592ef85SReinhard Meyer 412*c86c0155SWenyou Yang #ifdef CONFIG_DM_MMC 413*c86c0155SWenyou Yang static int atmel_mci_hw_init(struct atmel_mci_priv *priv) 414*c86c0155SWenyou Yang { 415*c86c0155SWenyou Yang #else 4161592ef85SReinhard Meyer /* Entered into mmc structure during driver init */ 4171592ef85SReinhard Meyer static int mci_init(struct mmc *mmc) 4181592ef85SReinhard Meyer { 4196b75d359SMarek Vasut struct atmel_mci_priv *priv = mmc->priv; 420*c86c0155SWenyou Yang #endif 4216b75d359SMarek Vasut atmel_mci_t *mci = priv->mci; 4221592ef85SReinhard Meyer 4231592ef85SReinhard Meyer /* Initialize controller */ 4241592ef85SReinhard Meyer writel(MMCI_BIT(SWRST), &mci->cr); /* soft reset */ 4251592ef85SReinhard Meyer writel(MMCI_BIT(PWSDIS), &mci->cr); /* disable power save */ 4261592ef85SReinhard Meyer writel(MMCI_BIT(MCIEN), &mci->cr); /* enable mci */ 4272aed9d14SReinhard Meyer writel(MMCI_BF(SCDSEL, MCI_BUS), &mci->sdcr); /* select port */ 4281592ef85SReinhard Meyer 4299924ca6eSWu, Josh /* This delay can be optimized, but stick with max value */ 4309924ca6eSWu, Josh writel(0x7f, &mci->dtor); 4311592ef85SReinhard Meyer /* Disable Interrupts */ 4321592ef85SReinhard Meyer writel(~0UL, &mci->idr); 4331592ef85SReinhard Meyer 4341592ef85SReinhard Meyer /* Set default clocks and blocklen */ 435*c86c0155SWenyou Yang #ifdef CONFIG_DM_MMC 436*c86c0155SWenyou Yang mci_set_mode(priv, CONFIG_SYS_MMC_CLK_OD, MMC_DEFAULT_BLKLEN); 437*c86c0155SWenyou Yang #else 4381592ef85SReinhard Meyer mci_set_mode(mmc, CONFIG_SYS_MMC_CLK_OD, MMC_DEFAULT_BLKLEN); 439*c86c0155SWenyou Yang #endif 4401592ef85SReinhard Meyer 4411592ef85SReinhard Meyer return 0; 4421592ef85SReinhard Meyer } 4431592ef85SReinhard Meyer 444*c86c0155SWenyou Yang #ifndef CONFIG_DM_MMC 445ab769f22SPantelis Antoniou static const struct mmc_ops atmel_mci_ops = { 446ab769f22SPantelis Antoniou .send_cmd = mci_send_cmd, 447ab769f22SPantelis Antoniou .set_ios = mci_set_ios, 448ab769f22SPantelis Antoniou .init = mci_init, 449ab769f22SPantelis Antoniou }; 450ab769f22SPantelis Antoniou 4511592ef85SReinhard Meyer /* 4521592ef85SReinhard Meyer * This is the only exported function 4531592ef85SReinhard Meyer * 4541592ef85SReinhard Meyer * Call it with the MCI register base address 4551592ef85SReinhard Meyer */ 4561592ef85SReinhard Meyer int atmel_mci_init(void *regs) 4571592ef85SReinhard Meyer { 45893bfd616SPantelis Antoniou struct mmc *mmc; 45993bfd616SPantelis Antoniou struct mmc_config *cfg; 4606b75d359SMarek Vasut struct atmel_mci_priv *priv; 461aac4b69bSBo Shen unsigned int version; 4621592ef85SReinhard Meyer 4636b75d359SMarek Vasut priv = calloc(1, sizeof(*priv)); 4646b75d359SMarek Vasut if (!priv) 4656b75d359SMarek Vasut return -ENOMEM; 466aac4b69bSBo Shen 4676b75d359SMarek Vasut cfg = &priv->cfg; 46893bfd616SPantelis Antoniou 46993bfd616SPantelis Antoniou cfg->name = "mci"; 47093bfd616SPantelis Antoniou cfg->ops = &atmel_mci_ops; 4711592ef85SReinhard Meyer 4726b75d359SMarek Vasut priv->mci = (struct atmel_mci *)regs; 473877807e1SMarek Vasut priv->initialized = 0; 4746b75d359SMarek Vasut 4751592ef85SReinhard Meyer /* need to be able to pass these in on a board by board basis */ 47693bfd616SPantelis Antoniou cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; 4776b75d359SMarek Vasut version = atmel_mci_get_version(priv->mci); 478da55c66eSBo Shen if ((version & 0xf00) >= 0x300) { 47993bfd616SPantelis Antoniou cfg->host_caps = MMC_MODE_8BIT; 480da55c66eSBo Shen cfg->host_caps |= MMC_MODE_HS | MMC_MODE_HS_52MHz; 481da55c66eSBo Shen } 482aac4b69bSBo Shen 48393bfd616SPantelis Antoniou cfg->host_caps |= MMC_MODE_4BIT; 484aac4b69bSBo Shen 4851592ef85SReinhard Meyer /* 4861592ef85SReinhard Meyer * min and max frequencies determined by 4871592ef85SReinhard Meyer * max and min of clock divider 4881592ef85SReinhard Meyer */ 48993bfd616SPantelis Antoniou cfg->f_min = get_mci_clk_rate() / (2*256); 49093bfd616SPantelis Antoniou cfg->f_max = get_mci_clk_rate() / (2*1); 4911592ef85SReinhard Meyer 49293bfd616SPantelis Antoniou cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; 4938feafcc4SJohn Rigby 4946b75d359SMarek Vasut mmc = mmc_create(cfg, priv); 49593bfd616SPantelis Antoniou 49693bfd616SPantelis Antoniou if (mmc == NULL) { 4976b75d359SMarek Vasut free(priv); 4986b75d359SMarek Vasut return -ENODEV; 49993bfd616SPantelis Antoniou } 5006b75d359SMarek Vasut /* NOTE: possibly leaking the priv structure */ 5011592ef85SReinhard Meyer 5021592ef85SReinhard Meyer return 0; 5031592ef85SReinhard Meyer } 504*c86c0155SWenyou Yang #endif 505*c86c0155SWenyou Yang 506*c86c0155SWenyou Yang #ifdef CONFIG_DM_MMC 507*c86c0155SWenyou Yang static const struct dm_mmc_ops atmel_mci_mmc_ops = { 508*c86c0155SWenyou Yang .send_cmd = atmel_mci_send_cmd, 509*c86c0155SWenyou Yang .set_ios = atmel_mci_set_ios, 510*c86c0155SWenyou Yang }; 511*c86c0155SWenyou Yang 512*c86c0155SWenyou Yang static void atmel_mci_setup_cfg(struct atmel_mci_priv *priv) 513*c86c0155SWenyou Yang { 514*c86c0155SWenyou Yang struct mmc_config *cfg; 515*c86c0155SWenyou Yang u32 version; 516*c86c0155SWenyou Yang 517*c86c0155SWenyou Yang cfg = &priv->cfg; 518*c86c0155SWenyou Yang cfg->name = "Atmel mci"; 519*c86c0155SWenyou Yang cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; 520*c86c0155SWenyou Yang 521*c86c0155SWenyou Yang /* 522*c86c0155SWenyou Yang * If the version is above 3.0, the capabilities of the 8-bit 523*c86c0155SWenyou Yang * bus width and high speed are supported. 524*c86c0155SWenyou Yang */ 525*c86c0155SWenyou Yang version = atmel_mci_get_version(priv->mci); 526*c86c0155SWenyou Yang if ((version & 0xf00) >= 0x300) { 527*c86c0155SWenyou Yang cfg->host_caps = MMC_MODE_8BIT | 528*c86c0155SWenyou Yang MMC_MODE_HS | MMC_MODE_HS_52MHz; 529*c86c0155SWenyou Yang } 530*c86c0155SWenyou Yang 531*c86c0155SWenyou Yang cfg->host_caps |= MMC_MODE_4BIT; 532*c86c0155SWenyou Yang cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; 533*c86c0155SWenyou Yang cfg->f_min = priv->bus_clk_rate / (2 * 256); 534*c86c0155SWenyou Yang cfg->f_max = priv->bus_clk_rate / 2; 535*c86c0155SWenyou Yang } 536*c86c0155SWenyou Yang 537*c86c0155SWenyou Yang static int atmel_mci_enable_clk(struct udevice *dev) 538*c86c0155SWenyou Yang { 539*c86c0155SWenyou Yang struct atmel_mci_priv *priv = dev_get_priv(dev); 540*c86c0155SWenyou Yang struct clk clk; 541*c86c0155SWenyou Yang ulong clk_rate; 542*c86c0155SWenyou Yang int ret = 0; 543*c86c0155SWenyou Yang 544*c86c0155SWenyou Yang ret = clk_get_by_index(dev, 0, &clk); 545*c86c0155SWenyou Yang if (ret) { 546*c86c0155SWenyou Yang ret = -EINVAL; 547*c86c0155SWenyou Yang goto failed; 548*c86c0155SWenyou Yang } 549*c86c0155SWenyou Yang 550*c86c0155SWenyou Yang ret = clk_enable(&clk); 551*c86c0155SWenyou Yang if (ret) 552*c86c0155SWenyou Yang goto failed; 553*c86c0155SWenyou Yang 554*c86c0155SWenyou Yang clk_rate = clk_get_rate(&clk); 555*c86c0155SWenyou Yang if (!clk_rate) { 556*c86c0155SWenyou Yang ret = -EINVAL; 557*c86c0155SWenyou Yang goto failed; 558*c86c0155SWenyou Yang } 559*c86c0155SWenyou Yang 560*c86c0155SWenyou Yang priv->bus_clk_rate = clk_rate; 561*c86c0155SWenyou Yang 562*c86c0155SWenyou Yang failed: 563*c86c0155SWenyou Yang clk_free(&clk); 564*c86c0155SWenyou Yang 565*c86c0155SWenyou Yang return ret; 566*c86c0155SWenyou Yang } 567*c86c0155SWenyou Yang 568*c86c0155SWenyou Yang static int atmel_mci_probe(struct udevice *dev) 569*c86c0155SWenyou Yang { 570*c86c0155SWenyou Yang struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); 571*c86c0155SWenyou Yang struct atmel_mci_priv *priv = dev_get_priv(dev); 572*c86c0155SWenyou Yang struct mmc *mmc; 573*c86c0155SWenyou Yang int ret; 574*c86c0155SWenyou Yang 575*c86c0155SWenyou Yang ret = atmel_mci_enable_clk(dev); 576*c86c0155SWenyou Yang if (ret) 577*c86c0155SWenyou Yang return ret; 578*c86c0155SWenyou Yang 579*c86c0155SWenyou Yang priv->mci = (struct atmel_mci *)dev_get_addr_ptr(dev); 580*c86c0155SWenyou Yang 581*c86c0155SWenyou Yang atmel_mci_setup_cfg(priv); 582*c86c0155SWenyou Yang 583*c86c0155SWenyou Yang mmc = &priv->mmc; 584*c86c0155SWenyou Yang mmc->cfg = &priv->cfg; 585*c86c0155SWenyou Yang mmc->dev = dev; 586*c86c0155SWenyou Yang upriv->mmc = mmc; 587*c86c0155SWenyou Yang 588*c86c0155SWenyou Yang atmel_mci_hw_init(priv); 589*c86c0155SWenyou Yang 590*c86c0155SWenyou Yang return 0; 591*c86c0155SWenyou Yang } 592*c86c0155SWenyou Yang 593*c86c0155SWenyou Yang static int atmel_mci_bind(struct udevice *dev) 594*c86c0155SWenyou Yang { 595*c86c0155SWenyou Yang struct atmel_mci_priv *priv = dev_get_priv(dev); 596*c86c0155SWenyou Yang 597*c86c0155SWenyou Yang return mmc_bind(dev, &priv->mmc, &priv->cfg); 598*c86c0155SWenyou Yang } 599*c86c0155SWenyou Yang 600*c86c0155SWenyou Yang static const struct udevice_id atmel_mci_ids[] = { 601*c86c0155SWenyou Yang { .compatible = "atmel,hsmci" }, 602*c86c0155SWenyou Yang { } 603*c86c0155SWenyou Yang }; 604*c86c0155SWenyou Yang 605*c86c0155SWenyou Yang U_BOOT_DRIVER(atmel_mci) = { 606*c86c0155SWenyou Yang .name = "atmel-mci", 607*c86c0155SWenyou Yang .id = UCLASS_MMC, 608*c86c0155SWenyou Yang .of_match = atmel_mci_ids, 609*c86c0155SWenyou Yang .bind = atmel_mci_bind, 610*c86c0155SWenyou Yang .probe = atmel_mci_probe, 611*c86c0155SWenyou Yang .priv_auto_alloc_size = sizeof(struct atmel_mci_priv), 612*c86c0155SWenyou Yang .ops = &atmel_mci_mmc_ops, 613*c86c0155SWenyou Yang }; 614*c86c0155SWenyou Yang #endif 615