1272cc70bSAndy Fleming /* 2272cc70bSAndy Fleming * Copyright 2008, Freescale Semiconductor, Inc 3272cc70bSAndy Fleming * Andy Fleming 4272cc70bSAndy Fleming * 5272cc70bSAndy Fleming * Based vaguely on the Linux code 6272cc70bSAndy Fleming * 71a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 8272cc70bSAndy Fleming */ 9272cc70bSAndy Fleming 10272cc70bSAndy Fleming #include <config.h> 11272cc70bSAndy Fleming #include <common.h> 12272cc70bSAndy Fleming #include <command.h> 138e3332e2SSjoerd Simons #include <dm.h> 148e3332e2SSjoerd Simons #include <dm/device-internal.h> 15d4622df3SStephen Warren #include <errno.h> 16272cc70bSAndy Fleming #include <mmc.h> 17272cc70bSAndy Fleming #include <part.h> 182051aefeSPeng Fan #include <power/regulator.h> 19272cc70bSAndy Fleming #include <malloc.h> 20cf92e05cSSimon Glass #include <memalign.h> 21272cc70bSAndy Fleming #include <linux/list.h> 229b1f942cSRabin Vincent #include <div64.h> 23da61fa5fSPaul Burton #include "mmc_private.h" 24272cc70bSAndy Fleming 253697e599SPeng Fan static const unsigned int sd_au_size[] = { 263697e599SPeng Fan 0, SZ_16K / 512, SZ_32K / 512, 273697e599SPeng Fan SZ_64K / 512, SZ_128K / 512, SZ_256K / 512, 283697e599SPeng Fan SZ_512K / 512, SZ_1M / 512, SZ_2M / 512, 293697e599SPeng Fan SZ_4M / 512, SZ_8M / 512, (SZ_8M + SZ_4M) / 512, 303697e599SPeng Fan SZ_16M / 512, (SZ_16M + SZ_8M) / 512, SZ_32M / 512, SZ_64M / 512, 313697e599SPeng Fan }; 323697e599SPeng Fan 33b5b838f1SMarek Vasut #if CONFIG_IS_ENABLED(MMC_TINY) 34b5b838f1SMarek Vasut static struct mmc mmc_static; 35b5b838f1SMarek Vasut struct mmc *find_mmc_device(int dev_num) 36b5b838f1SMarek Vasut { 37b5b838f1SMarek Vasut return &mmc_static; 38b5b838f1SMarek Vasut } 39b5b838f1SMarek Vasut 40b5b838f1SMarek Vasut void mmc_do_preinit(void) 41b5b838f1SMarek Vasut { 42b5b838f1SMarek Vasut struct mmc *m = &mmc_static; 43b5b838f1SMarek Vasut #ifdef CONFIG_FSL_ESDHC_ADAPTER_IDENT 44b5b838f1SMarek Vasut mmc_set_preinit(m, 1); 45b5b838f1SMarek Vasut #endif 46b5b838f1SMarek Vasut if (m->preinit) 47b5b838f1SMarek Vasut mmc_start_init(m); 48b5b838f1SMarek Vasut } 49b5b838f1SMarek Vasut 50b5b838f1SMarek Vasut struct blk_desc *mmc_get_blk_desc(struct mmc *mmc) 51b5b838f1SMarek Vasut { 52b5b838f1SMarek Vasut return &mmc->block_dev; 53b5b838f1SMarek Vasut } 54b5b838f1SMarek Vasut #endif 55b5b838f1SMarek Vasut 56e7881d85SSimon Glass #if !CONFIG_IS_ENABLED(DM_MMC) 57750121c3SJeroen Hofstee __weak int board_mmc_getwp(struct mmc *mmc) 58d23d8d7eSNikita Kiryanov { 59d23d8d7eSNikita Kiryanov return -1; 60d23d8d7eSNikita Kiryanov } 61d23d8d7eSNikita Kiryanov 62d23d8d7eSNikita Kiryanov int mmc_getwp(struct mmc *mmc) 63d23d8d7eSNikita Kiryanov { 64d23d8d7eSNikita Kiryanov int wp; 65d23d8d7eSNikita Kiryanov 66d23d8d7eSNikita Kiryanov wp = board_mmc_getwp(mmc); 67d23d8d7eSNikita Kiryanov 68d4e1da4eSPeter Korsgaard if (wp < 0) { 6993bfd616SPantelis Antoniou if (mmc->cfg->ops->getwp) 7093bfd616SPantelis Antoniou wp = mmc->cfg->ops->getwp(mmc); 71d4e1da4eSPeter Korsgaard else 72d4e1da4eSPeter Korsgaard wp = 0; 73d4e1da4eSPeter Korsgaard } 74d23d8d7eSNikita Kiryanov 75d23d8d7eSNikita Kiryanov return wp; 76d23d8d7eSNikita Kiryanov } 77d23d8d7eSNikita Kiryanov 78cee9ab7cSJeroen Hofstee __weak int board_mmc_getcd(struct mmc *mmc) 79cee9ab7cSJeroen Hofstee { 8011fdade2SStefano Babic return -1; 8111fdade2SStefano Babic } 828ca51e51SSimon Glass #endif 8311fdade2SStefano Babic 848635ff9eSMarek Vasut #ifdef CONFIG_MMC_TRACE 85c0c76ebaSSimon Glass void mmmc_trace_before_send(struct mmc *mmc, struct mmc_cmd *cmd) 86c0c76ebaSSimon Glass { 87c0c76ebaSSimon Glass printf("CMD_SEND:%d\n", cmd->cmdidx); 88c0c76ebaSSimon Glass printf("\t\tARG\t\t\t 0x%08X\n", cmd->cmdarg); 89c0c76ebaSSimon Glass } 90c0c76ebaSSimon Glass 91c0c76ebaSSimon Glass void mmmc_trace_after_send(struct mmc *mmc, struct mmc_cmd *cmd, int ret) 92c0c76ebaSSimon Glass { 935db2fe3aSRaffaele Recalcati int i; 945db2fe3aSRaffaele Recalcati u8 *ptr; 955db2fe3aSRaffaele Recalcati 967863ce58SBin Meng if (ret) { 977863ce58SBin Meng printf("\t\tRET\t\t\t %d\n", ret); 987863ce58SBin Meng } else { 995db2fe3aSRaffaele Recalcati switch (cmd->resp_type) { 1005db2fe3aSRaffaele Recalcati case MMC_RSP_NONE: 1015db2fe3aSRaffaele Recalcati printf("\t\tMMC_RSP_NONE\n"); 1025db2fe3aSRaffaele Recalcati break; 1035db2fe3aSRaffaele Recalcati case MMC_RSP_R1: 1045db2fe3aSRaffaele Recalcati printf("\t\tMMC_RSP_R1,5,6,7 \t 0x%08X \n", 1055db2fe3aSRaffaele Recalcati cmd->response[0]); 1065db2fe3aSRaffaele Recalcati break; 1075db2fe3aSRaffaele Recalcati case MMC_RSP_R1b: 1085db2fe3aSRaffaele Recalcati printf("\t\tMMC_RSP_R1b\t\t 0x%08X \n", 1095db2fe3aSRaffaele Recalcati cmd->response[0]); 1105db2fe3aSRaffaele Recalcati break; 1115db2fe3aSRaffaele Recalcati case MMC_RSP_R2: 1125db2fe3aSRaffaele Recalcati printf("\t\tMMC_RSP_R2\t\t 0x%08X \n", 1135db2fe3aSRaffaele Recalcati cmd->response[0]); 1145db2fe3aSRaffaele Recalcati printf("\t\t \t\t 0x%08X \n", 1155db2fe3aSRaffaele Recalcati cmd->response[1]); 1165db2fe3aSRaffaele Recalcati printf("\t\t \t\t 0x%08X \n", 1175db2fe3aSRaffaele Recalcati cmd->response[2]); 1185db2fe3aSRaffaele Recalcati printf("\t\t \t\t 0x%08X \n", 1195db2fe3aSRaffaele Recalcati cmd->response[3]); 1205db2fe3aSRaffaele Recalcati printf("\n"); 1215db2fe3aSRaffaele Recalcati printf("\t\t\t\t\tDUMPING DATA\n"); 1225db2fe3aSRaffaele Recalcati for (i = 0; i < 4; i++) { 1235db2fe3aSRaffaele Recalcati int j; 1245db2fe3aSRaffaele Recalcati printf("\t\t\t\t\t%03d - ", i*4); 125146bec79SDirk Behme ptr = (u8 *)&cmd->response[i]; 1265db2fe3aSRaffaele Recalcati ptr += 3; 1275db2fe3aSRaffaele Recalcati for (j = 0; j < 4; j++) 1285db2fe3aSRaffaele Recalcati printf("%02X ", *ptr--); 1295db2fe3aSRaffaele Recalcati printf("\n"); 1305db2fe3aSRaffaele Recalcati } 1315db2fe3aSRaffaele Recalcati break; 1325db2fe3aSRaffaele Recalcati case MMC_RSP_R3: 1335db2fe3aSRaffaele Recalcati printf("\t\tMMC_RSP_R3,4\t\t 0x%08X \n", 1345db2fe3aSRaffaele Recalcati cmd->response[0]); 1355db2fe3aSRaffaele Recalcati break; 1365db2fe3aSRaffaele Recalcati default: 1375db2fe3aSRaffaele Recalcati printf("\t\tERROR MMC rsp not supported\n"); 1385db2fe3aSRaffaele Recalcati break; 1395db2fe3aSRaffaele Recalcati } 1407863ce58SBin Meng } 141c0c76ebaSSimon Glass } 142c0c76ebaSSimon Glass 143c0c76ebaSSimon Glass void mmc_trace_state(struct mmc *mmc, struct mmc_cmd *cmd) 144c0c76ebaSSimon Glass { 145c0c76ebaSSimon Glass int status; 146c0c76ebaSSimon Glass 147c0c76ebaSSimon Glass status = (cmd->response[0] & MMC_STATUS_CURR_STATE) >> 9; 148c0c76ebaSSimon Glass printf("CURR STATE:%d\n", status); 149c0c76ebaSSimon Glass } 1505db2fe3aSRaffaele Recalcati #endif 151c0c76ebaSSimon Glass 152e7881d85SSimon Glass #if !CONFIG_IS_ENABLED(DM_MMC) 153c0c76ebaSSimon Glass int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) 154c0c76ebaSSimon Glass { 155c0c76ebaSSimon Glass int ret; 156c0c76ebaSSimon Glass 157c0c76ebaSSimon Glass mmmc_trace_before_send(mmc, cmd); 158c0c76ebaSSimon Glass ret = mmc->cfg->ops->send_cmd(mmc, cmd, data); 159c0c76ebaSSimon Glass mmmc_trace_after_send(mmc, cmd, ret); 160c0c76ebaSSimon Glass 1618635ff9eSMarek Vasut return ret; 162272cc70bSAndy Fleming } 1638ca51e51SSimon Glass #endif 164272cc70bSAndy Fleming 165da61fa5fSPaul Burton int mmc_send_status(struct mmc *mmc, int timeout) 1665d4fc8d9SRaffaele Recalcati { 1675d4fc8d9SRaffaele Recalcati struct mmc_cmd cmd; 168d617c426SJan Kloetzke int err, retries = 5; 1695d4fc8d9SRaffaele Recalcati 1705d4fc8d9SRaffaele Recalcati cmd.cmdidx = MMC_CMD_SEND_STATUS; 1715d4fc8d9SRaffaele Recalcati cmd.resp_type = MMC_RSP_R1; 172aaf3d41aSMarek Vasut if (!mmc_host_is_spi(mmc)) 173aaf3d41aSMarek Vasut cmd.cmdarg = mmc->rca << 16; 1745d4fc8d9SRaffaele Recalcati 1751677eef4SAndrew Gabbasov while (1) { 1765d4fc8d9SRaffaele Recalcati err = mmc_send_cmd(mmc, &cmd, NULL); 177d617c426SJan Kloetzke if (!err) { 178d617c426SJan Kloetzke if ((cmd.response[0] & MMC_STATUS_RDY_FOR_DATA) && 179d617c426SJan Kloetzke (cmd.response[0] & MMC_STATUS_CURR_STATE) != 180d617c426SJan Kloetzke MMC_STATE_PRG) 1815d4fc8d9SRaffaele Recalcati break; 182d617c426SJan Kloetzke else if (cmd.response[0] & MMC_STATUS_MASK) { 18356196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) 184d617c426SJan Kloetzke printf("Status Error: 0x%08X\n", 185d617c426SJan Kloetzke cmd.response[0]); 18656196826SPaul Burton #endif 187915ffa52SJaehoon Chung return -ECOMM; 188d617c426SJan Kloetzke } 189d617c426SJan Kloetzke } else if (--retries < 0) 190d617c426SJan Kloetzke return err; 1915d4fc8d9SRaffaele Recalcati 1921677eef4SAndrew Gabbasov if (timeout-- <= 0) 1931677eef4SAndrew Gabbasov break; 1945d4fc8d9SRaffaele Recalcati 1951677eef4SAndrew Gabbasov udelay(1000); 1961677eef4SAndrew Gabbasov } 1975d4fc8d9SRaffaele Recalcati 198c0c76ebaSSimon Glass mmc_trace_state(mmc, &cmd); 1995b0c942fSJongman Heo if (timeout <= 0) { 20056196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) 2015d4fc8d9SRaffaele Recalcati printf("Timeout waiting card ready\n"); 20256196826SPaul Burton #endif 203915ffa52SJaehoon Chung return -ETIMEDOUT; 2045d4fc8d9SRaffaele Recalcati } 2055d4fc8d9SRaffaele Recalcati 2065d4fc8d9SRaffaele Recalcati return 0; 2075d4fc8d9SRaffaele Recalcati } 2085d4fc8d9SRaffaele Recalcati 209da61fa5fSPaul Burton int mmc_set_blocklen(struct mmc *mmc, int len) 210272cc70bSAndy Fleming { 211272cc70bSAndy Fleming struct mmc_cmd cmd; 212272cc70bSAndy Fleming 213caa21a21SZiyuan Xu if (mmc_card_ddr(mmc)) 214d22e3d46SJaehoon Chung return 0; 215d22e3d46SJaehoon Chung 216272cc70bSAndy Fleming cmd.cmdidx = MMC_CMD_SET_BLOCKLEN; 217272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_R1; 218272cc70bSAndy Fleming cmd.cmdarg = len; 219272cc70bSAndy Fleming 220272cc70bSAndy Fleming return mmc_send_cmd(mmc, &cmd, NULL); 221272cc70bSAndy Fleming } 222272cc70bSAndy Fleming 223ff8fef56SSascha Silbe static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start, 224fdbb873eSKim Phillips lbaint_t blkcnt) 225272cc70bSAndy Fleming { 226272cc70bSAndy Fleming struct mmc_cmd cmd; 227272cc70bSAndy Fleming struct mmc_data data; 228272cc70bSAndy Fleming 2294a1a06bcSAlagu Sankar if (blkcnt > 1) 2304a1a06bcSAlagu Sankar cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK; 2314a1a06bcSAlagu Sankar else 232272cc70bSAndy Fleming cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK; 233272cc70bSAndy Fleming 234272cc70bSAndy Fleming if (mmc->high_capacity) 2354a1a06bcSAlagu Sankar cmd.cmdarg = start; 236272cc70bSAndy Fleming else 2374a1a06bcSAlagu Sankar cmd.cmdarg = start * mmc->read_bl_len; 238272cc70bSAndy Fleming 239272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_R1; 240272cc70bSAndy Fleming 241272cc70bSAndy Fleming data.dest = dst; 2424a1a06bcSAlagu Sankar data.blocks = blkcnt; 243272cc70bSAndy Fleming data.blocksize = mmc->read_bl_len; 244272cc70bSAndy Fleming data.flags = MMC_DATA_READ; 245272cc70bSAndy Fleming 2464a1a06bcSAlagu Sankar if (mmc_send_cmd(mmc, &cmd, &data)) 2474a1a06bcSAlagu Sankar return 0; 2484a1a06bcSAlagu Sankar 2494a1a06bcSAlagu Sankar if (blkcnt > 1) { 2504a1a06bcSAlagu Sankar cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; 2514a1a06bcSAlagu Sankar cmd.cmdarg = 0; 2524a1a06bcSAlagu Sankar cmd.resp_type = MMC_RSP_R1b; 2534a1a06bcSAlagu Sankar if (mmc_send_cmd(mmc, &cmd, NULL)) { 25456196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) 2554a1a06bcSAlagu Sankar printf("mmc fail to send stop cmd\n"); 25656196826SPaul Burton #endif 2574a1a06bcSAlagu Sankar return 0; 2584a1a06bcSAlagu Sankar } 259272cc70bSAndy Fleming } 260272cc70bSAndy Fleming 2614a1a06bcSAlagu Sankar return blkcnt; 262272cc70bSAndy Fleming } 263272cc70bSAndy Fleming 264c4d660d4SSimon Glass #if CONFIG_IS_ENABLED(BLK) 2657dba0b93SSimon Glass ulong mmc_bread(struct udevice *dev, lbaint_t start, lbaint_t blkcnt, void *dst) 26633fb211dSSimon Glass #else 2677dba0b93SSimon Glass ulong mmc_bread(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt, 2687dba0b93SSimon Glass void *dst) 26933fb211dSSimon Glass #endif 270272cc70bSAndy Fleming { 271c4d660d4SSimon Glass #if CONFIG_IS_ENABLED(BLK) 27233fb211dSSimon Glass struct blk_desc *block_dev = dev_get_uclass_platdata(dev); 27333fb211dSSimon Glass #endif 274bcce53d0SSimon Glass int dev_num = block_dev->devnum; 275873cc1d7SStephen Warren int err; 2764a1a06bcSAlagu Sankar lbaint_t cur, blocks_todo = blkcnt; 277272cc70bSAndy Fleming 2784a1a06bcSAlagu Sankar if (blkcnt == 0) 2794a1a06bcSAlagu Sankar return 0; 2804a1a06bcSAlagu Sankar 2814a1a06bcSAlagu Sankar struct mmc *mmc = find_mmc_device(dev_num); 282272cc70bSAndy Fleming if (!mmc) 283272cc70bSAndy Fleming return 0; 284272cc70bSAndy Fleming 285b5b838f1SMarek Vasut if (CONFIG_IS_ENABLED(MMC_TINY)) 286b5b838f1SMarek Vasut err = mmc_switch_part(mmc, block_dev->hwpart); 287b5b838f1SMarek Vasut else 28869f45cd5SSimon Glass err = blk_dselect_hwpart(block_dev, block_dev->hwpart); 289b5b838f1SMarek Vasut 290873cc1d7SStephen Warren if (err < 0) 291873cc1d7SStephen Warren return 0; 292873cc1d7SStephen Warren 293c40fdca6SSimon Glass if ((start + blkcnt) > block_dev->lba) { 29456196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) 295ff8fef56SSascha Silbe printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n", 296c40fdca6SSimon Glass start + blkcnt, block_dev->lba); 29756196826SPaul Burton #endif 298d2bf29e3SLei Wen return 0; 299d2bf29e3SLei Wen } 300272cc70bSAndy Fleming 30111692991SSimon Glass if (mmc_set_blocklen(mmc, mmc->read_bl_len)) { 30211692991SSimon Glass debug("%s: Failed to set blocklen\n", __func__); 303272cc70bSAndy Fleming return 0; 30411692991SSimon Glass } 305272cc70bSAndy Fleming 3064a1a06bcSAlagu Sankar do { 30793bfd616SPantelis Antoniou cur = (blocks_todo > mmc->cfg->b_max) ? 30893bfd616SPantelis Antoniou mmc->cfg->b_max : blocks_todo; 30911692991SSimon Glass if (mmc_read_blocks(mmc, dst, start, cur) != cur) { 31011692991SSimon Glass debug("%s: Failed to read blocks\n", __func__); 311c30b5115SJason Zhu int timeout = 0; 312c30b5115SJason Zhu re_init_retry: 313c30b5115SJason Zhu timeout++; 314c30b5115SJason Zhu /* 315c30b5115SJason Zhu * Try re-init seven times. 316c30b5115SJason Zhu */ 317c30b5115SJason Zhu if (timeout > 7) { 318c30b5115SJason Zhu printf("Re-init retry timeout\n"); 3194a1a06bcSAlagu Sankar return 0; 32011692991SSimon Glass } 321c30b5115SJason Zhu 322c30b5115SJason Zhu mmc->has_init = 0; 323c30b5115SJason Zhu if (mmc_init(mmc)) 324c30b5115SJason Zhu return 0; 325c30b5115SJason Zhu 326c30b5115SJason Zhu if (mmc_read_blocks(mmc, dst, start, cur) != cur) { 327c30b5115SJason Zhu printf("%s: Re-init mmc_read_blocks error\n", 328c30b5115SJason Zhu __func__); 329c30b5115SJason Zhu goto re_init_retry; 330c30b5115SJason Zhu } 331c30b5115SJason Zhu } 3324a1a06bcSAlagu Sankar blocks_todo -= cur; 3334a1a06bcSAlagu Sankar start += cur; 3344a1a06bcSAlagu Sankar dst += cur * mmc->read_bl_len; 3354a1a06bcSAlagu Sankar } while (blocks_todo > 0); 336272cc70bSAndy Fleming 337272cc70bSAndy Fleming return blkcnt; 338272cc70bSAndy Fleming } 339272cc70bSAndy Fleming 34049dba033SZiyuan Xu void mmc_set_clock(struct mmc *mmc, uint clock) 34149dba033SZiyuan Xu { 34249dba033SZiyuan Xu if (clock > mmc->cfg->f_max) 34349dba033SZiyuan Xu clock = mmc->cfg->f_max; 34449dba033SZiyuan Xu 34549dba033SZiyuan Xu if (clock < mmc->cfg->f_min) 34649dba033SZiyuan Xu clock = mmc->cfg->f_min; 34749dba033SZiyuan Xu 34849dba033SZiyuan Xu mmc->clock = clock; 34949dba033SZiyuan Xu 35049dba033SZiyuan Xu mmc_set_ios(mmc); 35149dba033SZiyuan Xu } 35249dba033SZiyuan Xu 35349dba033SZiyuan Xu static void mmc_set_bus_width(struct mmc *mmc, uint width) 35449dba033SZiyuan Xu { 35549dba033SZiyuan Xu mmc->bus_width = width; 35649dba033SZiyuan Xu 35749dba033SZiyuan Xu mmc_set_ios(mmc); 35849dba033SZiyuan Xu } 35949dba033SZiyuan Xu 36081db2d36SZiyuan Xu static void mmc_set_timing(struct mmc *mmc, uint timing) 36181db2d36SZiyuan Xu { 36281db2d36SZiyuan Xu mmc->timing = timing; 36381db2d36SZiyuan Xu mmc_set_ios(mmc); 36481db2d36SZiyuan Xu } 36581db2d36SZiyuan Xu 366fdbb873eSKim Phillips static int mmc_go_idle(struct mmc *mmc) 367272cc70bSAndy Fleming { 368272cc70bSAndy Fleming struct mmc_cmd cmd; 369272cc70bSAndy Fleming int err; 370272cc70bSAndy Fleming 371272cc70bSAndy Fleming udelay(1000); 372272cc70bSAndy Fleming 373272cc70bSAndy Fleming cmd.cmdidx = MMC_CMD_GO_IDLE_STATE; 374272cc70bSAndy Fleming cmd.cmdarg = 0; 375272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_NONE; 376272cc70bSAndy Fleming 377272cc70bSAndy Fleming err = mmc_send_cmd(mmc, &cmd, NULL); 378272cc70bSAndy Fleming 379272cc70bSAndy Fleming if (err) 380272cc70bSAndy Fleming return err; 381272cc70bSAndy Fleming 382272cc70bSAndy Fleming udelay(2000); 383272cc70bSAndy Fleming 384272cc70bSAndy Fleming return 0; 385272cc70bSAndy Fleming } 386272cc70bSAndy Fleming 387fdbb873eSKim Phillips static int sd_send_op_cond(struct mmc *mmc) 388272cc70bSAndy Fleming { 389272cc70bSAndy Fleming int timeout = 1000; 390272cc70bSAndy Fleming int err; 391272cc70bSAndy Fleming struct mmc_cmd cmd; 392272cc70bSAndy Fleming 3931677eef4SAndrew Gabbasov while (1) { 394272cc70bSAndy Fleming cmd.cmdidx = MMC_CMD_APP_CMD; 395272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_R1; 396272cc70bSAndy Fleming cmd.cmdarg = 0; 397272cc70bSAndy Fleming 398272cc70bSAndy Fleming err = mmc_send_cmd(mmc, &cmd, NULL); 399272cc70bSAndy Fleming 400272cc70bSAndy Fleming if (err) 401272cc70bSAndy Fleming return err; 402272cc70bSAndy Fleming 403272cc70bSAndy Fleming cmd.cmdidx = SD_CMD_APP_SEND_OP_COND; 404272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_R3; 405250de12bSStefano Babic 406250de12bSStefano Babic /* 407250de12bSStefano Babic * Most cards do not answer if some reserved bits 408250de12bSStefano Babic * in the ocr are set. However, Some controller 409250de12bSStefano Babic * can set bit 7 (reserved for low voltages), but 410250de12bSStefano Babic * how to manage low voltages SD card is not yet 411250de12bSStefano Babic * specified. 412250de12bSStefano Babic */ 413d52ebf10SThomas Chou cmd.cmdarg = mmc_host_is_spi(mmc) ? 0 : 41493bfd616SPantelis Antoniou (mmc->cfg->voltages & 0xff8000); 415272cc70bSAndy Fleming 416272cc70bSAndy Fleming if (mmc->version == SD_VERSION_2) 417272cc70bSAndy Fleming cmd.cmdarg |= OCR_HCS; 418272cc70bSAndy Fleming 419272cc70bSAndy Fleming err = mmc_send_cmd(mmc, &cmd, NULL); 420272cc70bSAndy Fleming 421272cc70bSAndy Fleming if (err) 422272cc70bSAndy Fleming return err; 423272cc70bSAndy Fleming 4241677eef4SAndrew Gabbasov if (cmd.response[0] & OCR_BUSY) 4251677eef4SAndrew Gabbasov break; 426272cc70bSAndy Fleming 4271677eef4SAndrew Gabbasov if (timeout-- <= 0) 428915ffa52SJaehoon Chung return -EOPNOTSUPP; 429272cc70bSAndy Fleming 4301677eef4SAndrew Gabbasov udelay(1000); 4311677eef4SAndrew Gabbasov } 4321677eef4SAndrew Gabbasov 433272cc70bSAndy Fleming if (mmc->version != SD_VERSION_2) 434272cc70bSAndy Fleming mmc->version = SD_VERSION_1_0; 435272cc70bSAndy Fleming 436d52ebf10SThomas Chou if (mmc_host_is_spi(mmc)) { /* read OCR for spi */ 437d52ebf10SThomas Chou cmd.cmdidx = MMC_CMD_SPI_READ_OCR; 438d52ebf10SThomas Chou cmd.resp_type = MMC_RSP_R3; 439d52ebf10SThomas Chou cmd.cmdarg = 0; 440d52ebf10SThomas Chou 441d52ebf10SThomas Chou err = mmc_send_cmd(mmc, &cmd, NULL); 442d52ebf10SThomas Chou 443d52ebf10SThomas Chou if (err) 444d52ebf10SThomas Chou return err; 445d52ebf10SThomas Chou } 446d52ebf10SThomas Chou 447998be3ddSRabin Vincent mmc->ocr = cmd.response[0]; 448272cc70bSAndy Fleming 449272cc70bSAndy Fleming mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS); 450272cc70bSAndy Fleming mmc->rca = 0; 451272cc70bSAndy Fleming 452272cc70bSAndy Fleming return 0; 453272cc70bSAndy Fleming } 454272cc70bSAndy Fleming 4555289b535SAndrew Gabbasov static int mmc_send_op_cond_iter(struct mmc *mmc, int use_arg) 456272cc70bSAndy Fleming { 4575289b535SAndrew Gabbasov struct mmc_cmd cmd; 458272cc70bSAndy Fleming int err; 459272cc70bSAndy Fleming 4605289b535SAndrew Gabbasov cmd.cmdidx = MMC_CMD_SEND_OP_COND; 4615289b535SAndrew Gabbasov cmd.resp_type = MMC_RSP_R3; 4625289b535SAndrew Gabbasov cmd.cmdarg = 0; 4635a20397bSRob Herring if (use_arg && !mmc_host_is_spi(mmc)) 4645a20397bSRob Herring cmd.cmdarg = OCR_HCS | 46593bfd616SPantelis Antoniou (mmc->cfg->voltages & 466a626c8d4SAndrew Gabbasov (mmc->ocr & OCR_VOLTAGE_MASK)) | 467a626c8d4SAndrew Gabbasov (mmc->ocr & OCR_ACCESS_MODE); 468e9550449SChe-Liang Chiou 4695289b535SAndrew Gabbasov err = mmc_send_cmd(mmc, &cmd, NULL); 470e9550449SChe-Liang Chiou if (err) 471e9550449SChe-Liang Chiou return err; 4725289b535SAndrew Gabbasov mmc->ocr = cmd.response[0]; 473e9550449SChe-Liang Chiou return 0; 474e9550449SChe-Liang Chiou } 475e9550449SChe-Liang Chiou 476750121c3SJeroen Hofstee static int mmc_send_op_cond(struct mmc *mmc) 477e9550449SChe-Liang Chiou { 478e9550449SChe-Liang Chiou int err, i; 479e9550449SChe-Liang Chiou 480272cc70bSAndy Fleming /* Some cards seem to need this */ 481272cc70bSAndy Fleming mmc_go_idle(mmc); 482272cc70bSAndy Fleming 48331cacbabSRaffaele Recalcati /* Asking to the card its capabilities */ 484e9550449SChe-Liang Chiou for (i = 0; i < 2; i++) { 4855289b535SAndrew Gabbasov err = mmc_send_op_cond_iter(mmc, i != 0); 48631cacbabSRaffaele Recalcati if (err) 48731cacbabSRaffaele Recalcati return err; 48831cacbabSRaffaele Recalcati 489e9550449SChe-Liang Chiou /* exit if not busy (flag seems to be inverted) */ 490a626c8d4SAndrew Gabbasov if (mmc->ocr & OCR_BUSY) 491bd47c135SAndrew Gabbasov break; 492e9550449SChe-Liang Chiou } 493bd47c135SAndrew Gabbasov mmc->op_cond_pending = 1; 494bd47c135SAndrew Gabbasov return 0; 495e9550449SChe-Liang Chiou } 49631cacbabSRaffaele Recalcati 497750121c3SJeroen Hofstee static int mmc_complete_op_cond(struct mmc *mmc) 498e9550449SChe-Liang Chiou { 499e9550449SChe-Liang Chiou struct mmc_cmd cmd; 500e9550449SChe-Liang Chiou int timeout = 1000; 501e9550449SChe-Liang Chiou uint start; 502e9550449SChe-Liang Chiou int err; 503e9550449SChe-Liang Chiou 504e9550449SChe-Liang Chiou mmc->op_cond_pending = 0; 505cc17c01fSAndrew Gabbasov if (!(mmc->ocr & OCR_BUSY)) { 506d188b113SYangbo Lu /* Some cards seem to need this */ 507d188b113SYangbo Lu mmc_go_idle(mmc); 508d188b113SYangbo Lu 509e9550449SChe-Liang Chiou start = get_timer(0); 5101677eef4SAndrew Gabbasov while (1) { 5115289b535SAndrew Gabbasov err = mmc_send_op_cond_iter(mmc, 1); 512272cc70bSAndy Fleming if (err) 513272cc70bSAndy Fleming return err; 5141677eef4SAndrew Gabbasov if (mmc->ocr & OCR_BUSY) 5151677eef4SAndrew Gabbasov break; 516e9550449SChe-Liang Chiou if (get_timer(start) > timeout) 517915ffa52SJaehoon Chung return -EOPNOTSUPP; 518e9550449SChe-Liang Chiou udelay(100); 5191677eef4SAndrew Gabbasov } 520cc17c01fSAndrew Gabbasov } 521272cc70bSAndy Fleming 522d52ebf10SThomas Chou if (mmc_host_is_spi(mmc)) { /* read OCR for spi */ 523d52ebf10SThomas Chou cmd.cmdidx = MMC_CMD_SPI_READ_OCR; 524d52ebf10SThomas Chou cmd.resp_type = MMC_RSP_R3; 525d52ebf10SThomas Chou cmd.cmdarg = 0; 526d52ebf10SThomas Chou 527d52ebf10SThomas Chou err = mmc_send_cmd(mmc, &cmd, NULL); 528d52ebf10SThomas Chou 529d52ebf10SThomas Chou if (err) 530d52ebf10SThomas Chou return err; 531a626c8d4SAndrew Gabbasov 532a626c8d4SAndrew Gabbasov mmc->ocr = cmd.response[0]; 533d52ebf10SThomas Chou } 534d52ebf10SThomas Chou 535272cc70bSAndy Fleming mmc->version = MMC_VERSION_UNKNOWN; 536272cc70bSAndy Fleming 537272cc70bSAndy Fleming mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS); 538def816a2SStephen Warren mmc->rca = 1; 539272cc70bSAndy Fleming 540272cc70bSAndy Fleming return 0; 541272cc70bSAndy Fleming } 542272cc70bSAndy Fleming 543272cc70bSAndy Fleming 544fdbb873eSKim Phillips static int mmc_send_ext_csd(struct mmc *mmc, u8 *ext_csd) 545272cc70bSAndy Fleming { 546272cc70bSAndy Fleming struct mmc_cmd cmd; 547272cc70bSAndy Fleming struct mmc_data data; 548272cc70bSAndy Fleming int err; 549272cc70bSAndy Fleming 550272cc70bSAndy Fleming /* Get the Card Status Register */ 551272cc70bSAndy Fleming cmd.cmdidx = MMC_CMD_SEND_EXT_CSD; 552272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_R1; 553272cc70bSAndy Fleming cmd.cmdarg = 0; 554272cc70bSAndy Fleming 555cdfd1ac6SYoshihiro Shimoda data.dest = (char *)ext_csd; 556272cc70bSAndy Fleming data.blocks = 1; 5578bfa195eSSimon Glass data.blocksize = MMC_MAX_BLOCK_LEN; 558272cc70bSAndy Fleming data.flags = MMC_DATA_READ; 559272cc70bSAndy Fleming 560272cc70bSAndy Fleming err = mmc_send_cmd(mmc, &cmd, &data); 561272cc70bSAndy Fleming 562272cc70bSAndy Fleming return err; 563272cc70bSAndy Fleming } 564272cc70bSAndy Fleming 5659e8ce816SZiyuan Xu static int mmc_poll_for_busy(struct mmc *mmc, u8 send_status) 566272cc70bSAndy Fleming { 567272cc70bSAndy Fleming struct mmc_cmd cmd; 56855e5defdSZiyuan Xu u8 busy = true; 56955e5defdSZiyuan Xu uint start; 57055e5defdSZiyuan Xu int ret; 5715d4fc8d9SRaffaele Recalcati int timeout = 1000; 57255e5defdSZiyuan Xu 57355e5defdSZiyuan Xu cmd.cmdidx = MMC_CMD_SEND_STATUS; 57455e5defdSZiyuan Xu cmd.resp_type = MMC_RSP_R1; 57555e5defdSZiyuan Xu cmd.cmdarg = mmc->rca << 16; 57655e5defdSZiyuan Xu 57755e5defdSZiyuan Xu start = get_timer(0); 57855e5defdSZiyuan Xu 5799e8ce816SZiyuan Xu if (!send_status && !mmc_can_card_busy(mmc)) { 5809e8ce816SZiyuan Xu mdelay(timeout); 5819e8ce816SZiyuan Xu return 0; 5829e8ce816SZiyuan Xu } 5839e8ce816SZiyuan Xu 58455e5defdSZiyuan Xu do { 5859e8ce816SZiyuan Xu if (!send_status) { 58655e5defdSZiyuan Xu busy = mmc_card_busy(mmc); 58755e5defdSZiyuan Xu } else { 58855e5defdSZiyuan Xu ret = mmc_send_cmd(mmc, &cmd, NULL); 58955e5defdSZiyuan Xu 59055e5defdSZiyuan Xu if (ret) 59155e5defdSZiyuan Xu return ret; 59255e5defdSZiyuan Xu 59355e5defdSZiyuan Xu if (cmd.response[0] & MMC_STATUS_SWITCH_ERROR) 59455e5defdSZiyuan Xu return -EBADMSG; 59555e5defdSZiyuan Xu busy = (cmd.response[0] & MMC_STATUS_CURR_STATE) == 59655e5defdSZiyuan Xu MMC_STATE_PRG; 59755e5defdSZiyuan Xu } 59855e5defdSZiyuan Xu 59955e5defdSZiyuan Xu if (get_timer(start) > timeout && busy) 60055e5defdSZiyuan Xu return -ETIMEDOUT; 60155e5defdSZiyuan Xu } while (busy); 60255e5defdSZiyuan Xu 60355e5defdSZiyuan Xu return 0; 60455e5defdSZiyuan Xu } 60555e5defdSZiyuan Xu 60655e5defdSZiyuan Xu static int __mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value, 60755e5defdSZiyuan Xu u8 send_status) 60855e5defdSZiyuan Xu { 60955e5defdSZiyuan Xu struct mmc_cmd cmd; 610a9003dc6SMaxime Ripard int retries = 3; 6115d4fc8d9SRaffaele Recalcati int ret; 612272cc70bSAndy Fleming 613272cc70bSAndy Fleming cmd.cmdidx = MMC_CMD_SWITCH; 614272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_R1b; 615272cc70bSAndy Fleming cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | 616272cc70bSAndy Fleming (index << 16) | 617272cc70bSAndy Fleming (value << 8); 618272cc70bSAndy Fleming 61955e5defdSZiyuan Xu do { 6205d4fc8d9SRaffaele Recalcati ret = mmc_send_cmd(mmc, &cmd, NULL); 6215d4fc8d9SRaffaele Recalcati 6229e8ce816SZiyuan Xu if (!ret) 6239e8ce816SZiyuan Xu return mmc_poll_for_busy(mmc, send_status); 62455e5defdSZiyuan Xu } while (--retries > 0 && ret); 62555e5defdSZiyuan Xu 626a9003dc6SMaxime Ripard return ret; 627a9003dc6SMaxime Ripard } 628a9003dc6SMaxime Ripard 62955e5defdSZiyuan Xu int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value) 63055e5defdSZiyuan Xu { 63155e5defdSZiyuan Xu return __mmc_switch(mmc, set, index, value, true); 632272cc70bSAndy Fleming } 633272cc70bSAndy Fleming 63449dba033SZiyuan Xu static int mmc_select_bus_width(struct mmc *mmc) 63549dba033SZiyuan Xu { 63649dba033SZiyuan Xu u32 ext_csd_bits[] = { 63749dba033SZiyuan Xu EXT_CSD_BUS_WIDTH_8, 63849dba033SZiyuan Xu EXT_CSD_BUS_WIDTH_4, 63949dba033SZiyuan Xu }; 64049dba033SZiyuan Xu u32 bus_widths[] = { 64149dba033SZiyuan Xu MMC_BUS_WIDTH_8BIT, 64249dba033SZiyuan Xu MMC_BUS_WIDTH_4BIT, 64349dba033SZiyuan Xu }; 64449dba033SZiyuan Xu ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); 64549dba033SZiyuan Xu ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN); 64649dba033SZiyuan Xu u32 idx, bus_width = 0; 64749dba033SZiyuan Xu int err = 0; 64849dba033SZiyuan Xu 64949dba033SZiyuan Xu if (mmc->version < MMC_VERSION_4 || 65049dba033SZiyuan Xu !(mmc->cfg->host_caps & (MMC_MODE_4BIT | MMC_MODE_8BIT))) 65149dba033SZiyuan Xu return 0; 65249dba033SZiyuan Xu 65349dba033SZiyuan Xu err = mmc_send_ext_csd(mmc, ext_csd); 65449dba033SZiyuan Xu 65549dba033SZiyuan Xu if (err) 65649dba033SZiyuan Xu return err; 65749dba033SZiyuan Xu 65849dba033SZiyuan Xu idx = (mmc->cfg->host_caps & MMC_MODE_8BIT) ? 0 : 1; 65949dba033SZiyuan Xu 66049dba033SZiyuan Xu /* 66149dba033SZiyuan Xu * Unlike SD, MMC cards dont have a configuration register to notify 66249dba033SZiyuan Xu * supported bus width. So bus test command should be run to identify 66349dba033SZiyuan Xu * the supported bus width or compare the ext csd values of current 66449dba033SZiyuan Xu * bus width and ext csd values of 1 bit mode read earlier. 66549dba033SZiyuan Xu */ 66649dba033SZiyuan Xu for (; idx < ARRAY_SIZE(bus_widths); idx++) { 66749dba033SZiyuan Xu /* 66849dba033SZiyuan Xu * Host is capable of 8bit transfer, then switch 66949dba033SZiyuan Xu * the device to work in 8bit transfer mode. If the 67049dba033SZiyuan Xu * mmc switch command returns error then switch to 67149dba033SZiyuan Xu * 4bit transfer mode. On success set the corresponding 67249dba033SZiyuan Xu * bus width on the host. 67349dba033SZiyuan Xu */ 67449dba033SZiyuan Xu err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 67549dba033SZiyuan Xu EXT_CSD_BUS_WIDTH, ext_csd_bits[idx]); 67649dba033SZiyuan Xu if (err) 67749dba033SZiyuan Xu continue; 67849dba033SZiyuan Xu 67949dba033SZiyuan Xu bus_width = bus_widths[idx]; 68049dba033SZiyuan Xu mmc_set_bus_width(mmc, bus_width); 68149dba033SZiyuan Xu 68249dba033SZiyuan Xu err = mmc_send_ext_csd(mmc, test_csd); 68349dba033SZiyuan Xu 68449dba033SZiyuan Xu if (err) 68549dba033SZiyuan Xu continue; 68649dba033SZiyuan Xu 68749dba033SZiyuan Xu /* Only compare read only fields */ 68849dba033SZiyuan Xu if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] == 68949dba033SZiyuan Xu test_csd[EXT_CSD_PARTITIONING_SUPPORT]) && 69049dba033SZiyuan Xu (ext_csd[EXT_CSD_HC_WP_GRP_SIZE] == 69149dba033SZiyuan Xu test_csd[EXT_CSD_HC_WP_GRP_SIZE]) && 69249dba033SZiyuan Xu (ext_csd[EXT_CSD_REV] == test_csd[EXT_CSD_REV]) && 69349dba033SZiyuan Xu (ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] == 69449dba033SZiyuan Xu test_csd[EXT_CSD_HC_ERASE_GRP_SIZE]) && 69549dba033SZiyuan Xu !memcmp(&ext_csd[EXT_CSD_SEC_CNT], 69649dba033SZiyuan Xu &test_csd[EXT_CSD_SEC_CNT], 4)) { 69749dba033SZiyuan Xu err = bus_width; 69849dba033SZiyuan Xu break; 69949dba033SZiyuan Xu } else { 70049dba033SZiyuan Xu err = -EBADMSG; 70149dba033SZiyuan Xu } 70249dba033SZiyuan Xu } 70349dba033SZiyuan Xu 70449dba033SZiyuan Xu return err; 70549dba033SZiyuan Xu } 70649dba033SZiyuan Xu 70749dba033SZiyuan Xu static const u8 tuning_blk_pattern_4bit[] = { 70849dba033SZiyuan Xu 0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc, 70949dba033SZiyuan Xu 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef, 71049dba033SZiyuan Xu 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, 71149dba033SZiyuan Xu 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef, 71249dba033SZiyuan Xu 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c, 71349dba033SZiyuan Xu 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee, 71449dba033SZiyuan Xu 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff, 71549dba033SZiyuan Xu 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde, 71649dba033SZiyuan Xu }; 71749dba033SZiyuan Xu 71849dba033SZiyuan Xu static const u8 tuning_blk_pattern_8bit[] = { 71949dba033SZiyuan Xu 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 72049dba033SZiyuan Xu 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc, 72149dba033SZiyuan Xu 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, 72249dba033SZiyuan Xu 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff, 72349dba033SZiyuan Xu 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, 72449dba033SZiyuan Xu 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, 72549dba033SZiyuan Xu 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, 72649dba033SZiyuan Xu 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff, 72749dba033SZiyuan Xu 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 72849dba033SZiyuan Xu 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 72949dba033SZiyuan Xu 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 73049dba033SZiyuan Xu 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 73149dba033SZiyuan Xu 0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 73249dba033SZiyuan Xu 0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 73349dba033SZiyuan Xu 0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 73449dba033SZiyuan Xu 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 73549dba033SZiyuan Xu }; 73649dba033SZiyuan Xu 73749dba033SZiyuan Xu int mmc_send_tuning(struct mmc *mmc, u32 opcode) 73849dba033SZiyuan Xu { 73949dba033SZiyuan Xu struct mmc_cmd cmd; 74049dba033SZiyuan Xu struct mmc_data data; 74149dba033SZiyuan Xu const u8 *tuning_block_pattern; 74249dba033SZiyuan Xu int size, err = 0; 74349dba033SZiyuan Xu u8 *data_buf; 74449dba033SZiyuan Xu 74549dba033SZiyuan Xu if (mmc->bus_width == MMC_BUS_WIDTH_8BIT) { 74649dba033SZiyuan Xu tuning_block_pattern = tuning_blk_pattern_8bit; 74749dba033SZiyuan Xu size = sizeof(tuning_blk_pattern_8bit); 74849dba033SZiyuan Xu } else if (mmc->bus_width == MMC_BUS_WIDTH_4BIT) { 74949dba033SZiyuan Xu tuning_block_pattern = tuning_blk_pattern_4bit; 75049dba033SZiyuan Xu size = sizeof(tuning_blk_pattern_4bit); 75149dba033SZiyuan Xu } else { 75249dba033SZiyuan Xu return -EINVAL; 75349dba033SZiyuan Xu } 75449dba033SZiyuan Xu 75549dba033SZiyuan Xu data_buf = calloc(1, size); 75649dba033SZiyuan Xu if (!data_buf) 75749dba033SZiyuan Xu return -ENOMEM; 75849dba033SZiyuan Xu 75949dba033SZiyuan Xu cmd.cmdidx = opcode; 76049dba033SZiyuan Xu cmd.resp_type = MMC_RSP_R1; 76149dba033SZiyuan Xu cmd.cmdarg = 0; 76249dba033SZiyuan Xu 76349dba033SZiyuan Xu data.dest = (char *)data_buf; 76449dba033SZiyuan Xu data.blocksize = size; 76549dba033SZiyuan Xu data.blocks = 1; 76649dba033SZiyuan Xu data.flags = MMC_DATA_READ; 76749dba033SZiyuan Xu 76849dba033SZiyuan Xu err = mmc_send_cmd(mmc, &cmd, &data); 76949dba033SZiyuan Xu if (err) 77049dba033SZiyuan Xu goto out; 77149dba033SZiyuan Xu 77249dba033SZiyuan Xu if (memcmp(data_buf, tuning_block_pattern, size)) 77349dba033SZiyuan Xu err = -EIO; 77449dba033SZiyuan Xu out: 77549dba033SZiyuan Xu free(data_buf); 77649dba033SZiyuan Xu return err; 77749dba033SZiyuan Xu } 77849dba033SZiyuan Xu 77949dba033SZiyuan Xu static int mmc_execute_tuning(struct mmc *mmc) 78049dba033SZiyuan Xu { 78149dba033SZiyuan Xu #ifdef CONFIG_DM_MMC 78249dba033SZiyuan Xu struct dm_mmc_ops *ops = mmc_get_ops(mmc->dev); 78349dba033SZiyuan Xu #endif 78449dba033SZiyuan Xu u32 opcode; 78549dba033SZiyuan Xu 78649dba033SZiyuan Xu if (IS_SD(mmc)) 78749dba033SZiyuan Xu opcode = MMC_SEND_TUNING_BLOCK; 78849dba033SZiyuan Xu else 78949dba033SZiyuan Xu opcode = MMC_SEND_TUNING_BLOCK_HS200; 79049dba033SZiyuan Xu 79149dba033SZiyuan Xu #ifndef CONFIG_DM_MMC 79249dba033SZiyuan Xu if (mmc->cfg->ops->execute_tuning) { 79349dba033SZiyuan Xu return mmc->cfg->ops->execute_tuning(mmc, opcode); 79449dba033SZiyuan Xu #else 79549dba033SZiyuan Xu if (ops->execute_tuning) { 79649dba033SZiyuan Xu return ops->execute_tuning(mmc->dev, opcode); 79749dba033SZiyuan Xu #endif 79849dba033SZiyuan Xu } else { 79949dba033SZiyuan Xu debug("Tuning feature required for HS200 mode.\n"); 80049dba033SZiyuan Xu return -EIO; 80149dba033SZiyuan Xu } 80249dba033SZiyuan Xu } 80349dba033SZiyuan Xu 80449dba033SZiyuan Xu static int mmc_hs200_tuning(struct mmc *mmc) 80549dba033SZiyuan Xu { 80649dba033SZiyuan Xu return mmc_execute_tuning(mmc); 80749dba033SZiyuan Xu } 80849dba033SZiyuan Xu 809e61cd3d7SZiyuan Xu static int mmc_select_hs(struct mmc *mmc) 810e61cd3d7SZiyuan Xu { 811e61cd3d7SZiyuan Xu int ret; 812e61cd3d7SZiyuan Xu 813e61cd3d7SZiyuan Xu ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 814e61cd3d7SZiyuan Xu EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS); 815e61cd3d7SZiyuan Xu 816e61cd3d7SZiyuan Xu if (!ret) 817e61cd3d7SZiyuan Xu mmc_set_timing(mmc, MMC_TIMING_MMC_HS); 818e61cd3d7SZiyuan Xu 819e61cd3d7SZiyuan Xu return ret; 820e61cd3d7SZiyuan Xu } 821e61cd3d7SZiyuan Xu 8225545757fSZiyuan Xu static int mmc_select_hs_ddr(struct mmc *mmc) 8235545757fSZiyuan Xu { 8245545757fSZiyuan Xu u32 ext_csd_bits; 8255545757fSZiyuan Xu int err = 0; 8265545757fSZiyuan Xu 8275545757fSZiyuan Xu if (mmc->bus_width == MMC_BUS_WIDTH_1BIT) 8285545757fSZiyuan Xu return 0; 8295545757fSZiyuan Xu 8305545757fSZiyuan Xu ext_csd_bits = (mmc->bus_width == MMC_BUS_WIDTH_8BIT) ? 8315545757fSZiyuan Xu EXT_CSD_DDR_BUS_WIDTH_8 : EXT_CSD_DDR_BUS_WIDTH_4; 8325545757fSZiyuan Xu 8335545757fSZiyuan Xu err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 8345545757fSZiyuan Xu EXT_CSD_BUS_WIDTH, ext_csd_bits); 8355545757fSZiyuan Xu if (err) 8365545757fSZiyuan Xu return err; 8375545757fSZiyuan Xu 8385545757fSZiyuan Xu mmc_set_timing(mmc, MMC_TIMING_MMC_DDR52); 8395545757fSZiyuan Xu 8405545757fSZiyuan Xu return 0; 8415545757fSZiyuan Xu } 8425545757fSZiyuan Xu 84349dba033SZiyuan Xu static int mmc_select_hs200(struct mmc *mmc) 84449dba033SZiyuan Xu { 84549dba033SZiyuan Xu int ret; 84649dba033SZiyuan Xu 84749dba033SZiyuan Xu /* 84849dba033SZiyuan Xu * Set the bus width(4 or 8) with host's support and 84949dba033SZiyuan Xu * switch to HS200 mode if bus width is set successfully. 85049dba033SZiyuan Xu */ 85149dba033SZiyuan Xu ret = mmc_select_bus_width(mmc); 85249dba033SZiyuan Xu 85349dba033SZiyuan Xu if (ret > 0) { 85449dba033SZiyuan Xu ret = __mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 85549dba033SZiyuan Xu EXT_CSD_HS_TIMING, 85649dba033SZiyuan Xu EXT_CSD_TIMING_HS200, false); 85749dba033SZiyuan Xu 85849dba033SZiyuan Xu if (ret) 85949dba033SZiyuan Xu return ret; 86049dba033SZiyuan Xu 86149dba033SZiyuan Xu mmc_set_timing(mmc, MMC_TIMING_MMC_HS200); 86249dba033SZiyuan Xu } 86349dba033SZiyuan Xu 86449dba033SZiyuan Xu return ret; 86549dba033SZiyuan Xu } 86649dba033SZiyuan Xu 867b673f29aSZiyuan Xu static int mmc_select_hs400(struct mmc *mmc) 868b673f29aSZiyuan Xu { 869b673f29aSZiyuan Xu int ret; 870b673f29aSZiyuan Xu 871b673f29aSZiyuan Xu /* Switch card to HS mode */ 872b673f29aSZiyuan Xu ret = __mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 873b673f29aSZiyuan Xu EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS, false); 874b673f29aSZiyuan Xu if (ret) 875b673f29aSZiyuan Xu return ret; 876b673f29aSZiyuan Xu 877b673f29aSZiyuan Xu /* Set host controller to HS timing */ 878b673f29aSZiyuan Xu mmc_set_timing(mmc, MMC_TIMING_MMC_HS); 879b673f29aSZiyuan Xu 880b673f29aSZiyuan Xu /* Reduce frequency to HS frequency */ 881b673f29aSZiyuan Xu mmc_set_clock(mmc, MMC_HIGH_52_MAX_DTR); 882b673f29aSZiyuan Xu 883b673f29aSZiyuan Xu ret = mmc_send_status(mmc, 1000); 884b673f29aSZiyuan Xu if (ret) 885b673f29aSZiyuan Xu return ret; 886b673f29aSZiyuan Xu 887b673f29aSZiyuan Xu /* Switch card to DDR */ 888b673f29aSZiyuan Xu ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 889b673f29aSZiyuan Xu EXT_CSD_BUS_WIDTH, 890b673f29aSZiyuan Xu EXT_CSD_DDR_BUS_WIDTH_8); 891b673f29aSZiyuan Xu if (ret) 892b673f29aSZiyuan Xu return ret; 893b673f29aSZiyuan Xu 894b673f29aSZiyuan Xu /* Switch card to HS400 */ 895b673f29aSZiyuan Xu ret = __mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 896b673f29aSZiyuan Xu EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS400, false); 897b673f29aSZiyuan Xu if (ret) 898b673f29aSZiyuan Xu return ret; 899b673f29aSZiyuan Xu 900b673f29aSZiyuan Xu /* Set host controller to HS400 timing and frequency */ 901b673f29aSZiyuan Xu mmc_set_timing(mmc, MMC_TIMING_MMC_HS400); 902b673f29aSZiyuan Xu 903b673f29aSZiyuan Xu return ret; 904b673f29aSZiyuan Xu } 905b673f29aSZiyuan Xu 906227f658eSZiyuan Xu static u32 mmc_select_card_type(struct mmc *mmc, u8 *ext_csd) 907227f658eSZiyuan Xu { 908227f658eSZiyuan Xu u8 card_type; 909227f658eSZiyuan Xu u32 host_caps, avail_type = 0; 910227f658eSZiyuan Xu 911227f658eSZiyuan Xu card_type = ext_csd[EXT_CSD_CARD_TYPE]; 912227f658eSZiyuan Xu host_caps = mmc->cfg->host_caps; 913227f658eSZiyuan Xu 914227f658eSZiyuan Xu if ((host_caps & MMC_MODE_HS) && 915227f658eSZiyuan Xu (card_type & EXT_CSD_CARD_TYPE_26)) 916227f658eSZiyuan Xu avail_type |= EXT_CSD_CARD_TYPE_26; 917227f658eSZiyuan Xu 918227f658eSZiyuan Xu if ((host_caps & MMC_MODE_HS) && 919227f658eSZiyuan Xu (card_type & EXT_CSD_CARD_TYPE_52)) 920227f658eSZiyuan Xu avail_type |= EXT_CSD_CARD_TYPE_52; 921227f658eSZiyuan Xu 922227f658eSZiyuan Xu /* 923227f658eSZiyuan Xu * For the moment, u-boot doesn't support signal voltage 924227f658eSZiyuan Xu * switch, therefor we assume that host support ddr52 925227f658eSZiyuan Xu * at 1.8v or 3.3v I/O(1.2v I/O not supported, hs200 and 926227f658eSZiyuan Xu * hs400 are the same). 927227f658eSZiyuan Xu */ 928227f658eSZiyuan Xu if ((host_caps & MMC_MODE_DDR_52MHz) && 929227f658eSZiyuan Xu (card_type & EXT_CSD_CARD_TYPE_DDR_1_8V)) 930227f658eSZiyuan Xu avail_type |= EXT_CSD_CARD_TYPE_DDR_1_8V; 931227f658eSZiyuan Xu 932227f658eSZiyuan Xu if ((host_caps & MMC_MODE_HS200) && 933227f658eSZiyuan Xu (card_type & EXT_CSD_CARD_TYPE_HS200_1_8V)) 934227f658eSZiyuan Xu avail_type |= EXT_CSD_CARD_TYPE_HS200_1_8V; 935227f658eSZiyuan Xu 936227f658eSZiyuan Xu /* 937227f658eSZiyuan Xu * If host can support HS400, it means that host can also 938227f658eSZiyuan Xu * support HS200. 939227f658eSZiyuan Xu */ 940227f658eSZiyuan Xu if ((host_caps & MMC_MODE_HS400) && 941227f658eSZiyuan Xu (host_caps & MMC_MODE_8BIT) && 942227f658eSZiyuan Xu (card_type & EXT_CSD_CARD_TYPE_HS400_1_8V)) 943227f658eSZiyuan Xu avail_type |= EXT_CSD_CARD_TYPE_HS200_1_8V | 944227f658eSZiyuan Xu EXT_CSD_CARD_TYPE_HS400_1_8V; 945227f658eSZiyuan Xu 946227f658eSZiyuan Xu if ((host_caps & MMC_MODE_HS400ES) && 947227f658eSZiyuan Xu (host_caps & MMC_MODE_8BIT) && 948227f658eSZiyuan Xu ext_csd[EXT_CSD_STROBE_SUPPORT] && 949227f658eSZiyuan Xu (avail_type & EXT_CSD_CARD_TYPE_HS400_1_8V)) 950227f658eSZiyuan Xu avail_type |= EXT_CSD_CARD_TYPE_HS200_1_8V | 951227f658eSZiyuan Xu EXT_CSD_CARD_TYPE_HS400_1_8V | 952227f658eSZiyuan Xu EXT_CSD_CARD_TYPE_HS400ES; 953227f658eSZiyuan Xu 954227f658eSZiyuan Xu return avail_type; 955227f658eSZiyuan Xu } 956227f658eSZiyuan Xu 95749dba033SZiyuan Xu static void mmc_set_bus_speed(struct mmc *mmc, u8 avail_type) 95849dba033SZiyuan Xu { 95949dba033SZiyuan Xu int clock = 0; 96049dba033SZiyuan Xu 96149dba033SZiyuan Xu if (mmc_card_hs(mmc)) 96249dba033SZiyuan Xu clock = (avail_type & EXT_CSD_CARD_TYPE_52) ? 96349dba033SZiyuan Xu MMC_HIGH_52_MAX_DTR : MMC_HIGH_26_MAX_DTR; 96449dba033SZiyuan Xu else if (mmc_card_hs200(mmc) || 96549dba033SZiyuan Xu mmc_card_hs400(mmc) || 96649dba033SZiyuan Xu mmc_card_hs400es(mmc)) 96749dba033SZiyuan Xu clock = MMC_HS200_MAX_DTR; 96849dba033SZiyuan Xu 96949dba033SZiyuan Xu mmc_set_clock(mmc, clock); 97049dba033SZiyuan Xu } 97149dba033SZiyuan Xu 972fdbb873eSKim Phillips static int mmc_change_freq(struct mmc *mmc) 973272cc70bSAndy Fleming { 9748bfa195eSSimon Glass ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); 975227f658eSZiyuan Xu u32 avail_type; 976272cc70bSAndy Fleming int err; 977272cc70bSAndy Fleming 978fc5b32fbSAndrew Gabbasov mmc->card_caps = 0; 979272cc70bSAndy Fleming 980d52ebf10SThomas Chou if (mmc_host_is_spi(mmc)) 981d52ebf10SThomas Chou return 0; 982d52ebf10SThomas Chou 983272cc70bSAndy Fleming /* Only version 4 supports high-speed */ 984272cc70bSAndy Fleming if (mmc->version < MMC_VERSION_4) 985272cc70bSAndy Fleming return 0; 986272cc70bSAndy Fleming 987fc5b32fbSAndrew Gabbasov mmc->card_caps |= MMC_MODE_4BIT | MMC_MODE_8BIT; 988fc5b32fbSAndrew Gabbasov 989272cc70bSAndy Fleming err = mmc_send_ext_csd(mmc, ext_csd); 990272cc70bSAndy Fleming 991272cc70bSAndy Fleming if (err) 992272cc70bSAndy Fleming return err; 993272cc70bSAndy Fleming 994227f658eSZiyuan Xu avail_type = mmc_select_card_type(mmc, ext_csd); 995272cc70bSAndy Fleming 99649dba033SZiyuan Xu if (avail_type & EXT_CSD_CARD_TYPE_HS200) 99749dba033SZiyuan Xu err = mmc_select_hs200(mmc); 998*1f250d0aSJason Zhu else if (avail_type & EXT_CSD_CARD_TYPE_HS) 999e61cd3d7SZiyuan Xu err = mmc_select_hs(mmc); 1000227f658eSZiyuan Xu else 1001227f658eSZiyuan Xu err = -EINVAL; 1002272cc70bSAndy Fleming 1003272cc70bSAndy Fleming if (err) 1004a5e27b41SHeiko Schocher return err; 1005272cc70bSAndy Fleming 100649dba033SZiyuan Xu mmc_set_bus_speed(mmc, avail_type); 1007272cc70bSAndy Fleming 1008b673f29aSZiyuan Xu if (mmc_card_hs200(mmc)) { 100949dba033SZiyuan Xu err = mmc_hs200_tuning(mmc); 1010b673f29aSZiyuan Xu if (avail_type & EXT_CSD_CARD_TYPE_HS400 && 1011b673f29aSZiyuan Xu mmc->bus_width == MMC_BUS_WIDTH_8BIT) { 1012b673f29aSZiyuan Xu err = mmc_select_hs400(mmc); 1013b673f29aSZiyuan Xu mmc_set_bus_speed(mmc, avail_type); 1014b673f29aSZiyuan Xu } 1015b673f29aSZiyuan Xu } else if (!mmc_card_hs400es(mmc)) { 101649dba033SZiyuan Xu err = mmc_select_bus_width(mmc) > 0 ? 0 : err; 10175545757fSZiyuan Xu if (!err && avail_type & EXT_CSD_CARD_TYPE_DDR_52) 10185545757fSZiyuan Xu err = mmc_select_hs_ddr(mmc); 10195545757fSZiyuan Xu } 102049dba033SZiyuan Xu 1021272cc70bSAndy Fleming return err; 1022272cc70bSAndy Fleming } 1023272cc70bSAndy Fleming 1024f866a46dSStephen Warren static int mmc_set_capacity(struct mmc *mmc, int part_num) 1025f866a46dSStephen Warren { 1026f866a46dSStephen Warren switch (part_num) { 1027f866a46dSStephen Warren case 0: 1028f866a46dSStephen Warren mmc->capacity = mmc->capacity_user; 1029f866a46dSStephen Warren break; 1030f866a46dSStephen Warren case 1: 1031f866a46dSStephen Warren case 2: 1032f866a46dSStephen Warren mmc->capacity = mmc->capacity_boot; 1033f866a46dSStephen Warren break; 1034f866a46dSStephen Warren case 3: 1035f866a46dSStephen Warren mmc->capacity = mmc->capacity_rpmb; 1036f866a46dSStephen Warren break; 1037f866a46dSStephen Warren case 4: 1038f866a46dSStephen Warren case 5: 1039f866a46dSStephen Warren case 6: 1040f866a46dSStephen Warren case 7: 1041f866a46dSStephen Warren mmc->capacity = mmc->capacity_gp[part_num - 4]; 1042f866a46dSStephen Warren break; 1043f866a46dSStephen Warren default: 1044f866a46dSStephen Warren return -1; 1045f866a46dSStephen Warren } 1046f866a46dSStephen Warren 1047c40fdca6SSimon Glass mmc_get_blk_desc(mmc)->lba = lldiv(mmc->capacity, mmc->read_bl_len); 1048f866a46dSStephen Warren 1049f866a46dSStephen Warren return 0; 1050f866a46dSStephen Warren } 1051f866a46dSStephen Warren 10527dba0b93SSimon Glass int mmc_switch_part(struct mmc *mmc, unsigned int part_num) 1053bc897b1dSLei Wen { 1054f866a46dSStephen Warren int ret; 1055bc897b1dSLei Wen 1056f866a46dSStephen Warren ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF, 1057bc897b1dSLei Wen (mmc->part_config & ~PART_ACCESS_MASK) 1058bc897b1dSLei Wen | (part_num & PART_ACCESS_MASK)); 1059f866a46dSStephen Warren 10606dc93e70SPeter Bigot /* 10616dc93e70SPeter Bigot * Set the capacity if the switch succeeded or was intended 10626dc93e70SPeter Bigot * to return to representing the raw device. 10636dc93e70SPeter Bigot */ 1064873cc1d7SStephen Warren if ((ret == 0) || ((ret == -ENODEV) && (part_num == 0))) { 10656dc93e70SPeter Bigot ret = mmc_set_capacity(mmc, part_num); 1066fdbb139fSSimon Glass mmc_get_blk_desc(mmc)->hwpart = part_num; 1067873cc1d7SStephen Warren } 10686dc93e70SPeter Bigot 10696dc93e70SPeter Bigot return ret; 1070bc897b1dSLei Wen } 1071bc897b1dSLei Wen 1072ac9da0e0SDiego Santa Cruz int mmc_hwpart_config(struct mmc *mmc, 1073ac9da0e0SDiego Santa Cruz const struct mmc_hwpart_conf *conf, 1074ac9da0e0SDiego Santa Cruz enum mmc_hwpart_conf_mode mode) 1075ac9da0e0SDiego Santa Cruz { 1076ac9da0e0SDiego Santa Cruz u8 part_attrs = 0; 1077ac9da0e0SDiego Santa Cruz u32 enh_size_mult; 1078ac9da0e0SDiego Santa Cruz u32 enh_start_addr; 1079ac9da0e0SDiego Santa Cruz u32 gp_size_mult[4]; 1080ac9da0e0SDiego Santa Cruz u32 max_enh_size_mult; 1081ac9da0e0SDiego Santa Cruz u32 tot_enh_size_mult = 0; 10828dda5b0eSDiego Santa Cruz u8 wr_rel_set; 1083ac9da0e0SDiego Santa Cruz int i, pidx, err; 1084ac9da0e0SDiego Santa Cruz ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); 1085ac9da0e0SDiego Santa Cruz 1086ac9da0e0SDiego Santa Cruz if (mode < MMC_HWPART_CONF_CHECK || mode > MMC_HWPART_CONF_COMPLETE) 1087ac9da0e0SDiego Santa Cruz return -EINVAL; 1088ac9da0e0SDiego Santa Cruz 1089ac9da0e0SDiego Santa Cruz if (IS_SD(mmc) || (mmc->version < MMC_VERSION_4_41)) { 1090ac9da0e0SDiego Santa Cruz printf("eMMC >= 4.4 required for enhanced user data area\n"); 1091ac9da0e0SDiego Santa Cruz return -EMEDIUMTYPE; 1092ac9da0e0SDiego Santa Cruz } 1093ac9da0e0SDiego Santa Cruz 1094ac9da0e0SDiego Santa Cruz if (!(mmc->part_support & PART_SUPPORT)) { 1095ac9da0e0SDiego Santa Cruz printf("Card does not support partitioning\n"); 1096ac9da0e0SDiego Santa Cruz return -EMEDIUMTYPE; 1097ac9da0e0SDiego Santa Cruz } 1098ac9da0e0SDiego Santa Cruz 1099ac9da0e0SDiego Santa Cruz if (!mmc->hc_wp_grp_size) { 1100ac9da0e0SDiego Santa Cruz printf("Card does not define HC WP group size\n"); 1101ac9da0e0SDiego Santa Cruz return -EMEDIUMTYPE; 1102ac9da0e0SDiego Santa Cruz } 1103ac9da0e0SDiego Santa Cruz 1104ac9da0e0SDiego Santa Cruz /* check partition alignment and total enhanced size */ 1105ac9da0e0SDiego Santa Cruz if (conf->user.enh_size) { 1106ac9da0e0SDiego Santa Cruz if (conf->user.enh_size % mmc->hc_wp_grp_size || 1107ac9da0e0SDiego Santa Cruz conf->user.enh_start % mmc->hc_wp_grp_size) { 1108ac9da0e0SDiego Santa Cruz printf("User data enhanced area not HC WP group " 1109ac9da0e0SDiego Santa Cruz "size aligned\n"); 1110ac9da0e0SDiego Santa Cruz return -EINVAL; 1111ac9da0e0SDiego Santa Cruz } 1112ac9da0e0SDiego Santa Cruz part_attrs |= EXT_CSD_ENH_USR; 1113ac9da0e0SDiego Santa Cruz enh_size_mult = conf->user.enh_size / mmc->hc_wp_grp_size; 1114ac9da0e0SDiego Santa Cruz if (mmc->high_capacity) { 1115ac9da0e0SDiego Santa Cruz enh_start_addr = conf->user.enh_start; 1116ac9da0e0SDiego Santa Cruz } else { 1117ac9da0e0SDiego Santa Cruz enh_start_addr = (conf->user.enh_start << 9); 1118ac9da0e0SDiego Santa Cruz } 1119ac9da0e0SDiego Santa Cruz } else { 1120ac9da0e0SDiego Santa Cruz enh_size_mult = 0; 1121ac9da0e0SDiego Santa Cruz enh_start_addr = 0; 1122ac9da0e0SDiego Santa Cruz } 1123ac9da0e0SDiego Santa Cruz tot_enh_size_mult += enh_size_mult; 1124ac9da0e0SDiego Santa Cruz 1125ac9da0e0SDiego Santa Cruz for (pidx = 0; pidx < 4; pidx++) { 1126ac9da0e0SDiego Santa Cruz if (conf->gp_part[pidx].size % mmc->hc_wp_grp_size) { 1127ac9da0e0SDiego Santa Cruz printf("GP%i partition not HC WP group size " 1128ac9da0e0SDiego Santa Cruz "aligned\n", pidx+1); 1129ac9da0e0SDiego Santa Cruz return -EINVAL; 1130ac9da0e0SDiego Santa Cruz } 1131ac9da0e0SDiego Santa Cruz gp_size_mult[pidx] = conf->gp_part[pidx].size / mmc->hc_wp_grp_size; 1132ac9da0e0SDiego Santa Cruz if (conf->gp_part[pidx].size && conf->gp_part[pidx].enhanced) { 1133ac9da0e0SDiego Santa Cruz part_attrs |= EXT_CSD_ENH_GP(pidx); 1134ac9da0e0SDiego Santa Cruz tot_enh_size_mult += gp_size_mult[pidx]; 1135ac9da0e0SDiego Santa Cruz } 1136ac9da0e0SDiego Santa Cruz } 1137ac9da0e0SDiego Santa Cruz 1138ac9da0e0SDiego Santa Cruz if (part_attrs && ! (mmc->part_support & ENHNCD_SUPPORT)) { 1139ac9da0e0SDiego Santa Cruz printf("Card does not support enhanced attribute\n"); 1140ac9da0e0SDiego Santa Cruz return -EMEDIUMTYPE; 1141ac9da0e0SDiego Santa Cruz } 1142ac9da0e0SDiego Santa Cruz 1143ac9da0e0SDiego Santa Cruz err = mmc_send_ext_csd(mmc, ext_csd); 1144ac9da0e0SDiego Santa Cruz if (err) 1145ac9da0e0SDiego Santa Cruz return err; 1146ac9da0e0SDiego Santa Cruz 1147ac9da0e0SDiego Santa Cruz max_enh_size_mult = 1148ac9da0e0SDiego Santa Cruz (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT+2] << 16) + 1149ac9da0e0SDiego Santa Cruz (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT+1] << 8) + 1150ac9da0e0SDiego Santa Cruz ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT]; 1151ac9da0e0SDiego Santa Cruz if (tot_enh_size_mult > max_enh_size_mult) { 1152ac9da0e0SDiego Santa Cruz printf("Total enhanced size exceeds maximum (%u > %u)\n", 1153ac9da0e0SDiego Santa Cruz tot_enh_size_mult, max_enh_size_mult); 1154ac9da0e0SDiego Santa Cruz return -EMEDIUMTYPE; 1155ac9da0e0SDiego Santa Cruz } 1156ac9da0e0SDiego Santa Cruz 11578dda5b0eSDiego Santa Cruz /* The default value of EXT_CSD_WR_REL_SET is device 11588dda5b0eSDiego Santa Cruz * dependent, the values can only be changed if the 11598dda5b0eSDiego Santa Cruz * EXT_CSD_HS_CTRL_REL bit is set. The values can be 11608dda5b0eSDiego Santa Cruz * changed only once and before partitioning is completed. */ 11618dda5b0eSDiego Santa Cruz wr_rel_set = ext_csd[EXT_CSD_WR_REL_SET]; 11628dda5b0eSDiego Santa Cruz if (conf->user.wr_rel_change) { 11638dda5b0eSDiego Santa Cruz if (conf->user.wr_rel_set) 11648dda5b0eSDiego Santa Cruz wr_rel_set |= EXT_CSD_WR_DATA_REL_USR; 11658dda5b0eSDiego Santa Cruz else 11668dda5b0eSDiego Santa Cruz wr_rel_set &= ~EXT_CSD_WR_DATA_REL_USR; 11678dda5b0eSDiego Santa Cruz } 11688dda5b0eSDiego Santa Cruz for (pidx = 0; pidx < 4; pidx++) { 11698dda5b0eSDiego Santa Cruz if (conf->gp_part[pidx].wr_rel_change) { 11708dda5b0eSDiego Santa Cruz if (conf->gp_part[pidx].wr_rel_set) 11718dda5b0eSDiego Santa Cruz wr_rel_set |= EXT_CSD_WR_DATA_REL_GP(pidx); 11728dda5b0eSDiego Santa Cruz else 11738dda5b0eSDiego Santa Cruz wr_rel_set &= ~EXT_CSD_WR_DATA_REL_GP(pidx); 11748dda5b0eSDiego Santa Cruz } 11758dda5b0eSDiego Santa Cruz } 11768dda5b0eSDiego Santa Cruz 11778dda5b0eSDiego Santa Cruz if (wr_rel_set != ext_csd[EXT_CSD_WR_REL_SET] && 11788dda5b0eSDiego Santa Cruz !(ext_csd[EXT_CSD_WR_REL_PARAM] & EXT_CSD_HS_CTRL_REL)) { 11798dda5b0eSDiego Santa Cruz puts("Card does not support host controlled partition write " 11808dda5b0eSDiego Santa Cruz "reliability settings\n"); 11818dda5b0eSDiego Santa Cruz return -EMEDIUMTYPE; 11828dda5b0eSDiego Santa Cruz } 11838dda5b0eSDiego Santa Cruz 1184ac9da0e0SDiego Santa Cruz if (ext_csd[EXT_CSD_PARTITION_SETTING] & 1185ac9da0e0SDiego Santa Cruz EXT_CSD_PARTITION_SETTING_COMPLETED) { 1186ac9da0e0SDiego Santa Cruz printf("Card already partitioned\n"); 1187ac9da0e0SDiego Santa Cruz return -EPERM; 1188ac9da0e0SDiego Santa Cruz } 1189ac9da0e0SDiego Santa Cruz 1190ac9da0e0SDiego Santa Cruz if (mode == MMC_HWPART_CONF_CHECK) 1191ac9da0e0SDiego Santa Cruz return 0; 1192ac9da0e0SDiego Santa Cruz 1193ac9da0e0SDiego Santa Cruz /* Partitioning requires high-capacity size definitions */ 1194ac9da0e0SDiego Santa Cruz if (!(ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x01)) { 1195ac9da0e0SDiego Santa Cruz err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 1196ac9da0e0SDiego Santa Cruz EXT_CSD_ERASE_GROUP_DEF, 1); 1197ac9da0e0SDiego Santa Cruz 1198ac9da0e0SDiego Santa Cruz if (err) 1199ac9da0e0SDiego Santa Cruz return err; 1200ac9da0e0SDiego Santa Cruz 1201ac9da0e0SDiego Santa Cruz ext_csd[EXT_CSD_ERASE_GROUP_DEF] = 1; 1202ac9da0e0SDiego Santa Cruz 1203ac9da0e0SDiego Santa Cruz /* update erase group size to be high-capacity */ 1204ac9da0e0SDiego Santa Cruz mmc->erase_grp_size = 1205ac9da0e0SDiego Santa Cruz ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 1024; 1206ac9da0e0SDiego Santa Cruz 1207ac9da0e0SDiego Santa Cruz } 1208ac9da0e0SDiego Santa Cruz 1209ac9da0e0SDiego Santa Cruz /* all OK, write the configuration */ 1210ac9da0e0SDiego Santa Cruz for (i = 0; i < 4; i++) { 1211ac9da0e0SDiego Santa Cruz err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 1212ac9da0e0SDiego Santa Cruz EXT_CSD_ENH_START_ADDR+i, 1213ac9da0e0SDiego Santa Cruz (enh_start_addr >> (i*8)) & 0xFF); 1214ac9da0e0SDiego Santa Cruz if (err) 1215ac9da0e0SDiego Santa Cruz return err; 1216ac9da0e0SDiego Santa Cruz } 1217ac9da0e0SDiego Santa Cruz for (i = 0; i < 3; i++) { 1218ac9da0e0SDiego Santa Cruz err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 1219ac9da0e0SDiego Santa Cruz EXT_CSD_ENH_SIZE_MULT+i, 1220ac9da0e0SDiego Santa Cruz (enh_size_mult >> (i*8)) & 0xFF); 1221ac9da0e0SDiego Santa Cruz if (err) 1222ac9da0e0SDiego Santa Cruz return err; 1223ac9da0e0SDiego Santa Cruz } 1224ac9da0e0SDiego Santa Cruz for (pidx = 0; pidx < 4; pidx++) { 1225ac9da0e0SDiego Santa Cruz for (i = 0; i < 3; i++) { 1226ac9da0e0SDiego Santa Cruz err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 1227ac9da0e0SDiego Santa Cruz EXT_CSD_GP_SIZE_MULT+pidx*3+i, 1228ac9da0e0SDiego Santa Cruz (gp_size_mult[pidx] >> (i*8)) & 0xFF); 1229ac9da0e0SDiego Santa Cruz if (err) 1230ac9da0e0SDiego Santa Cruz return err; 1231ac9da0e0SDiego Santa Cruz } 1232ac9da0e0SDiego Santa Cruz } 1233ac9da0e0SDiego Santa Cruz err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 1234ac9da0e0SDiego Santa Cruz EXT_CSD_PARTITIONS_ATTRIBUTE, part_attrs); 1235ac9da0e0SDiego Santa Cruz if (err) 1236ac9da0e0SDiego Santa Cruz return err; 1237ac9da0e0SDiego Santa Cruz 1238ac9da0e0SDiego Santa Cruz if (mode == MMC_HWPART_CONF_SET) 1239ac9da0e0SDiego Santa Cruz return 0; 1240ac9da0e0SDiego Santa Cruz 12418dda5b0eSDiego Santa Cruz /* The WR_REL_SET is a write-once register but shall be 12428dda5b0eSDiego Santa Cruz * written before setting PART_SETTING_COMPLETED. As it is 12438dda5b0eSDiego Santa Cruz * write-once we can only write it when completing the 12448dda5b0eSDiego Santa Cruz * partitioning. */ 12458dda5b0eSDiego Santa Cruz if (wr_rel_set != ext_csd[EXT_CSD_WR_REL_SET]) { 12468dda5b0eSDiego Santa Cruz err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 12478dda5b0eSDiego Santa Cruz EXT_CSD_WR_REL_SET, wr_rel_set); 12488dda5b0eSDiego Santa Cruz if (err) 12498dda5b0eSDiego Santa Cruz return err; 12508dda5b0eSDiego Santa Cruz } 12518dda5b0eSDiego Santa Cruz 1252ac9da0e0SDiego Santa Cruz /* Setting PART_SETTING_COMPLETED confirms the partition 1253ac9da0e0SDiego Santa Cruz * configuration but it only becomes effective after power 1254ac9da0e0SDiego Santa Cruz * cycle, so we do not adjust the partition related settings 1255ac9da0e0SDiego Santa Cruz * in the mmc struct. */ 1256ac9da0e0SDiego Santa Cruz 1257ac9da0e0SDiego Santa Cruz err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 1258ac9da0e0SDiego Santa Cruz EXT_CSD_PARTITION_SETTING, 1259ac9da0e0SDiego Santa Cruz EXT_CSD_PARTITION_SETTING_COMPLETED); 1260ac9da0e0SDiego Santa Cruz if (err) 1261ac9da0e0SDiego Santa Cruz return err; 1262ac9da0e0SDiego Santa Cruz 1263ac9da0e0SDiego Santa Cruz return 0; 1264ac9da0e0SDiego Santa Cruz } 1265ac9da0e0SDiego Santa Cruz 1266e7881d85SSimon Glass #if !CONFIG_IS_ENABLED(DM_MMC) 126748972d90SThierry Reding int mmc_getcd(struct mmc *mmc) 126848972d90SThierry Reding { 126948972d90SThierry Reding int cd; 127048972d90SThierry Reding 127148972d90SThierry Reding cd = board_mmc_getcd(mmc); 127248972d90SThierry Reding 1273d4e1da4eSPeter Korsgaard if (cd < 0) { 127493bfd616SPantelis Antoniou if (mmc->cfg->ops->getcd) 127593bfd616SPantelis Antoniou cd = mmc->cfg->ops->getcd(mmc); 1276d4e1da4eSPeter Korsgaard else 1277d4e1da4eSPeter Korsgaard cd = 1; 1278d4e1da4eSPeter Korsgaard } 127948972d90SThierry Reding 128048972d90SThierry Reding return cd; 128148972d90SThierry Reding } 12828ca51e51SSimon Glass #endif 128348972d90SThierry Reding 1284fdbb873eSKim Phillips static int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp) 1285272cc70bSAndy Fleming { 1286272cc70bSAndy Fleming struct mmc_cmd cmd; 1287272cc70bSAndy Fleming struct mmc_data data; 1288272cc70bSAndy Fleming 1289272cc70bSAndy Fleming /* Switch the frequency */ 1290272cc70bSAndy Fleming cmd.cmdidx = SD_CMD_SWITCH_FUNC; 1291272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_R1; 1292272cc70bSAndy Fleming cmd.cmdarg = (mode << 31) | 0xffffff; 1293272cc70bSAndy Fleming cmd.cmdarg &= ~(0xf << (group * 4)); 1294272cc70bSAndy Fleming cmd.cmdarg |= value << (group * 4); 1295272cc70bSAndy Fleming 1296272cc70bSAndy Fleming data.dest = (char *)resp; 1297272cc70bSAndy Fleming data.blocksize = 64; 1298272cc70bSAndy Fleming data.blocks = 1; 1299272cc70bSAndy Fleming data.flags = MMC_DATA_READ; 1300272cc70bSAndy Fleming 1301272cc70bSAndy Fleming return mmc_send_cmd(mmc, &cmd, &data); 1302272cc70bSAndy Fleming } 1303272cc70bSAndy Fleming 1304272cc70bSAndy Fleming 1305fdbb873eSKim Phillips static int sd_change_freq(struct mmc *mmc) 1306272cc70bSAndy Fleming { 1307272cc70bSAndy Fleming int err; 1308272cc70bSAndy Fleming struct mmc_cmd cmd; 1309f781dd38SAnton staaf ALLOC_CACHE_ALIGN_BUFFER(uint, scr, 2); 1310f781dd38SAnton staaf ALLOC_CACHE_ALIGN_BUFFER(uint, switch_status, 16); 1311272cc70bSAndy Fleming struct mmc_data data; 1312272cc70bSAndy Fleming int timeout; 1313272cc70bSAndy Fleming 1314272cc70bSAndy Fleming mmc->card_caps = 0; 1315272cc70bSAndy Fleming 1316d52ebf10SThomas Chou if (mmc_host_is_spi(mmc)) 1317d52ebf10SThomas Chou return 0; 1318d52ebf10SThomas Chou 1319272cc70bSAndy Fleming /* Read the SCR to find out if this card supports higher speeds */ 1320272cc70bSAndy Fleming cmd.cmdidx = MMC_CMD_APP_CMD; 1321272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_R1; 1322272cc70bSAndy Fleming cmd.cmdarg = mmc->rca << 16; 1323272cc70bSAndy Fleming 1324272cc70bSAndy Fleming err = mmc_send_cmd(mmc, &cmd, NULL); 1325272cc70bSAndy Fleming 1326272cc70bSAndy Fleming if (err) 1327272cc70bSAndy Fleming return err; 1328272cc70bSAndy Fleming 1329272cc70bSAndy Fleming cmd.cmdidx = SD_CMD_APP_SEND_SCR; 1330272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_R1; 1331272cc70bSAndy Fleming cmd.cmdarg = 0; 1332272cc70bSAndy Fleming 1333272cc70bSAndy Fleming timeout = 3; 1334272cc70bSAndy Fleming 1335272cc70bSAndy Fleming retry_scr: 1336f781dd38SAnton staaf data.dest = (char *)scr; 1337272cc70bSAndy Fleming data.blocksize = 8; 1338272cc70bSAndy Fleming data.blocks = 1; 1339272cc70bSAndy Fleming data.flags = MMC_DATA_READ; 1340272cc70bSAndy Fleming 1341272cc70bSAndy Fleming err = mmc_send_cmd(mmc, &cmd, &data); 1342272cc70bSAndy Fleming 1343272cc70bSAndy Fleming if (err) { 1344272cc70bSAndy Fleming if (timeout--) 1345272cc70bSAndy Fleming goto retry_scr; 1346272cc70bSAndy Fleming 1347272cc70bSAndy Fleming return err; 1348272cc70bSAndy Fleming } 1349272cc70bSAndy Fleming 13504e3d89baSYauhen Kharuzhy mmc->scr[0] = __be32_to_cpu(scr[0]); 13514e3d89baSYauhen Kharuzhy mmc->scr[1] = __be32_to_cpu(scr[1]); 1352272cc70bSAndy Fleming 1353272cc70bSAndy Fleming switch ((mmc->scr[0] >> 24) & 0xf) { 1354272cc70bSAndy Fleming case 0: 1355272cc70bSAndy Fleming mmc->version = SD_VERSION_1_0; 1356272cc70bSAndy Fleming break; 1357272cc70bSAndy Fleming case 1: 1358272cc70bSAndy Fleming mmc->version = SD_VERSION_1_10; 1359272cc70bSAndy Fleming break; 1360272cc70bSAndy Fleming case 2: 1361272cc70bSAndy Fleming mmc->version = SD_VERSION_2; 13621741c64dSJaehoon Chung if ((mmc->scr[0] >> 15) & 0x1) 13631741c64dSJaehoon Chung mmc->version = SD_VERSION_3; 1364272cc70bSAndy Fleming break; 1365272cc70bSAndy Fleming default: 1366272cc70bSAndy Fleming mmc->version = SD_VERSION_1_0; 1367272cc70bSAndy Fleming break; 1368272cc70bSAndy Fleming } 1369272cc70bSAndy Fleming 1370b44c7083SAlagu Sankar if (mmc->scr[0] & SD_DATA_4BIT) 1371b44c7083SAlagu Sankar mmc->card_caps |= MMC_MODE_4BIT; 1372b44c7083SAlagu Sankar 1373272cc70bSAndy Fleming /* Version 1.0 doesn't support switching */ 1374272cc70bSAndy Fleming if (mmc->version == SD_VERSION_1_0) 1375272cc70bSAndy Fleming return 0; 1376272cc70bSAndy Fleming 1377272cc70bSAndy Fleming timeout = 4; 1378272cc70bSAndy Fleming while (timeout--) { 1379272cc70bSAndy Fleming err = sd_switch(mmc, SD_SWITCH_CHECK, 0, 1, 1380f781dd38SAnton staaf (u8 *)switch_status); 1381272cc70bSAndy Fleming 1382272cc70bSAndy Fleming if (err) 1383272cc70bSAndy Fleming return err; 1384272cc70bSAndy Fleming 1385272cc70bSAndy Fleming /* The high-speed function is busy. Try again */ 13864e3d89baSYauhen Kharuzhy if (!(__be32_to_cpu(switch_status[7]) & SD_HIGHSPEED_BUSY)) 1387272cc70bSAndy Fleming break; 1388272cc70bSAndy Fleming } 1389272cc70bSAndy Fleming 1390272cc70bSAndy Fleming /* If high-speed isn't supported, we return */ 13914e3d89baSYauhen Kharuzhy if (!(__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED)) 1392272cc70bSAndy Fleming return 0; 1393272cc70bSAndy Fleming 13942c3fbf4cSMacpaul Lin /* 13952c3fbf4cSMacpaul Lin * If the host doesn't support SD_HIGHSPEED, do not switch card to 13962c3fbf4cSMacpaul Lin * HIGHSPEED mode even if the card support SD_HIGHSPPED. 13972c3fbf4cSMacpaul Lin * This can avoid furthur problem when the card runs in different 13982c3fbf4cSMacpaul Lin * mode between the host. 13992c3fbf4cSMacpaul Lin */ 140093bfd616SPantelis Antoniou if (!((mmc->cfg->host_caps & MMC_MODE_HS_52MHz) && 140193bfd616SPantelis Antoniou (mmc->cfg->host_caps & MMC_MODE_HS))) 14022c3fbf4cSMacpaul Lin return 0; 14032c3fbf4cSMacpaul Lin 1404f781dd38SAnton staaf err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, 1, (u8 *)switch_status); 1405272cc70bSAndy Fleming 1406272cc70bSAndy Fleming if (err) 1407272cc70bSAndy Fleming return err; 1408272cc70bSAndy Fleming 14094e3d89baSYauhen Kharuzhy if ((__be32_to_cpu(switch_status[4]) & 0x0f000000) == 0x01000000) 1410272cc70bSAndy Fleming mmc->card_caps |= MMC_MODE_HS; 1411272cc70bSAndy Fleming 1412272cc70bSAndy Fleming return 0; 1413272cc70bSAndy Fleming } 1414272cc70bSAndy Fleming 14153697e599SPeng Fan static int sd_read_ssr(struct mmc *mmc) 14163697e599SPeng Fan { 14173697e599SPeng Fan int err, i; 14183697e599SPeng Fan struct mmc_cmd cmd; 14193697e599SPeng Fan ALLOC_CACHE_ALIGN_BUFFER(uint, ssr, 16); 14203697e599SPeng Fan struct mmc_data data; 14213697e599SPeng Fan int timeout = 3; 14223697e599SPeng Fan unsigned int au, eo, et, es; 14233697e599SPeng Fan 14243697e599SPeng Fan cmd.cmdidx = MMC_CMD_APP_CMD; 14253697e599SPeng Fan cmd.resp_type = MMC_RSP_R1; 14263697e599SPeng Fan cmd.cmdarg = mmc->rca << 16; 14273697e599SPeng Fan 14283697e599SPeng Fan err = mmc_send_cmd(mmc, &cmd, NULL); 14293697e599SPeng Fan if (err) 14303697e599SPeng Fan return err; 14313697e599SPeng Fan 14323697e599SPeng Fan cmd.cmdidx = SD_CMD_APP_SD_STATUS; 14333697e599SPeng Fan cmd.resp_type = MMC_RSP_R1; 14343697e599SPeng Fan cmd.cmdarg = 0; 14353697e599SPeng Fan 14363697e599SPeng Fan retry_ssr: 14373697e599SPeng Fan data.dest = (char *)ssr; 14383697e599SPeng Fan data.blocksize = 64; 14393697e599SPeng Fan data.blocks = 1; 14403697e599SPeng Fan data.flags = MMC_DATA_READ; 14413697e599SPeng Fan 14423697e599SPeng Fan err = mmc_send_cmd(mmc, &cmd, &data); 14433697e599SPeng Fan if (err) { 14443697e599SPeng Fan if (timeout--) 14453697e599SPeng Fan goto retry_ssr; 14463697e599SPeng Fan 14473697e599SPeng Fan return err; 14483697e599SPeng Fan } 14493697e599SPeng Fan 14503697e599SPeng Fan for (i = 0; i < 16; i++) 14513697e599SPeng Fan ssr[i] = be32_to_cpu(ssr[i]); 14523697e599SPeng Fan 14533697e599SPeng Fan au = (ssr[2] >> 12) & 0xF; 14543697e599SPeng Fan if ((au <= 9) || (mmc->version == SD_VERSION_3)) { 14553697e599SPeng Fan mmc->ssr.au = sd_au_size[au]; 14563697e599SPeng Fan es = (ssr[3] >> 24) & 0xFF; 14573697e599SPeng Fan es |= (ssr[2] & 0xFF) << 8; 14583697e599SPeng Fan et = (ssr[3] >> 18) & 0x3F; 14593697e599SPeng Fan if (es && et) { 14603697e599SPeng Fan eo = (ssr[3] >> 16) & 0x3; 14613697e599SPeng Fan mmc->ssr.erase_timeout = (et * 1000) / es; 14623697e599SPeng Fan mmc->ssr.erase_offset = eo * 1000; 14633697e599SPeng Fan } 14643697e599SPeng Fan } else { 14653697e599SPeng Fan debug("Invalid Allocation Unit Size.\n"); 14663697e599SPeng Fan } 14673697e599SPeng Fan 14683697e599SPeng Fan return 0; 14693697e599SPeng Fan } 14703697e599SPeng Fan 1471272cc70bSAndy Fleming /* frequency bases */ 1472272cc70bSAndy Fleming /* divided by 10 to be nice to platforms without floating point */ 14735f837c2cSMike Frysinger static const int fbase[] = { 1474272cc70bSAndy Fleming 10000, 1475272cc70bSAndy Fleming 100000, 1476272cc70bSAndy Fleming 1000000, 1477272cc70bSAndy Fleming 10000000, 1478272cc70bSAndy Fleming }; 1479272cc70bSAndy Fleming 1480272cc70bSAndy Fleming /* Multiplier values for TRAN_SPEED. Multiplied by 10 to be nice 1481272cc70bSAndy Fleming * to platforms without floating point. 1482272cc70bSAndy Fleming */ 148361fe076fSSimon Glass static const u8 multipliers[] = { 1484272cc70bSAndy Fleming 0, /* reserved */ 1485272cc70bSAndy Fleming 10, 1486272cc70bSAndy Fleming 12, 1487272cc70bSAndy Fleming 13, 1488272cc70bSAndy Fleming 15, 1489272cc70bSAndy Fleming 20, 1490272cc70bSAndy Fleming 25, 1491272cc70bSAndy Fleming 30, 1492272cc70bSAndy Fleming 35, 1493272cc70bSAndy Fleming 40, 1494272cc70bSAndy Fleming 45, 1495272cc70bSAndy Fleming 50, 1496272cc70bSAndy Fleming 55, 1497272cc70bSAndy Fleming 60, 1498272cc70bSAndy Fleming 70, 1499272cc70bSAndy Fleming 80, 1500272cc70bSAndy Fleming }; 1501272cc70bSAndy Fleming 1502e7881d85SSimon Glass #if !CONFIG_IS_ENABLED(DM_MMC) 1503fdbb873eSKim Phillips static void mmc_set_ios(struct mmc *mmc) 1504272cc70bSAndy Fleming { 150593bfd616SPantelis Antoniou if (mmc->cfg->ops->set_ios) 150693bfd616SPantelis Antoniou mmc->cfg->ops->set_ios(mmc); 1507272cc70bSAndy Fleming } 1508ad77484aSZiyuan Xu 1509ad77484aSZiyuan Xu static bool mmc_card_busy(struct mmc *mmc) 1510ad77484aSZiyuan Xu { 1511ad77484aSZiyuan Xu if (!mmc->cfg->ops->card_busy) 1512ad77484aSZiyuan Xu return -ENOSYS; 1513ad77484aSZiyuan Xu 1514ad77484aSZiyuan Xu return mmc->cfg->ops->card_busy(mmc); 1515ad77484aSZiyuan Xu } 1516ad77484aSZiyuan Xu 1517ad77484aSZiyuan Xu static bool mmc_can_card_busy(struct mmc *) 1518ad77484aSZiyuan Xu { 1519ad77484aSZiyuan Xu return !!mmc->cfg->ops->card_busy; 1520ad77484aSZiyuan Xu } 15218ca51e51SSimon Glass #endif 1522272cc70bSAndy Fleming 1523fdbb873eSKim Phillips static int mmc_startup(struct mmc *mmc) 1524272cc70bSAndy Fleming { 1525f866a46dSStephen Warren int err, i; 15263e3ff0acSZiyuan Xu uint mult, freq, tran_speed; 1527639b7827SYoshihiro Shimoda u64 cmult, csize, capacity; 1528272cc70bSAndy Fleming struct mmc_cmd cmd; 15298bfa195eSSimon Glass ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); 15300c453bb7SDiego Santa Cruz bool has_parts = false; 15318a0cf490SDiego Santa Cruz bool part_completed; 1532c40fdca6SSimon Glass struct blk_desc *bdesc; 1533272cc70bSAndy Fleming 1534d52ebf10SThomas Chou #ifdef CONFIG_MMC_SPI_CRC_ON 1535d52ebf10SThomas Chou if (mmc_host_is_spi(mmc)) { /* enable CRC check for spi */ 1536d52ebf10SThomas Chou cmd.cmdidx = MMC_CMD_SPI_CRC_ON_OFF; 1537d52ebf10SThomas Chou cmd.resp_type = MMC_RSP_R1; 1538d52ebf10SThomas Chou cmd.cmdarg = 1; 1539d52ebf10SThomas Chou err = mmc_send_cmd(mmc, &cmd, NULL); 1540d52ebf10SThomas Chou 1541d52ebf10SThomas Chou if (err) 1542d52ebf10SThomas Chou return err; 1543d52ebf10SThomas Chou } 1544d52ebf10SThomas Chou #endif 1545d52ebf10SThomas Chou 1546272cc70bSAndy Fleming /* Put the Card in Identify Mode */ 1547d52ebf10SThomas Chou cmd.cmdidx = mmc_host_is_spi(mmc) ? MMC_CMD_SEND_CID : 1548d52ebf10SThomas Chou MMC_CMD_ALL_SEND_CID; /* cmd not supported in spi */ 1549272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_R2; 1550272cc70bSAndy Fleming cmd.cmdarg = 0; 1551272cc70bSAndy Fleming 1552272cc70bSAndy Fleming err = mmc_send_cmd(mmc, &cmd, NULL); 1553272cc70bSAndy Fleming 1554272cc70bSAndy Fleming if (err) 1555272cc70bSAndy Fleming return err; 1556272cc70bSAndy Fleming 1557272cc70bSAndy Fleming memcpy(mmc->cid, cmd.response, 16); 1558272cc70bSAndy Fleming 1559272cc70bSAndy Fleming /* 1560272cc70bSAndy Fleming * For MMC cards, set the Relative Address. 1561272cc70bSAndy Fleming * For SD cards, get the Relatvie Address. 1562272cc70bSAndy Fleming * This also puts the cards into Standby State 1563272cc70bSAndy Fleming */ 1564d52ebf10SThomas Chou if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */ 1565272cc70bSAndy Fleming cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR; 1566272cc70bSAndy Fleming cmd.cmdarg = mmc->rca << 16; 1567272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_R6; 1568272cc70bSAndy Fleming 1569272cc70bSAndy Fleming err = mmc_send_cmd(mmc, &cmd, NULL); 1570272cc70bSAndy Fleming 1571272cc70bSAndy Fleming if (err) 1572272cc70bSAndy Fleming return err; 1573272cc70bSAndy Fleming 1574272cc70bSAndy Fleming if (IS_SD(mmc)) 1575998be3ddSRabin Vincent mmc->rca = (cmd.response[0] >> 16) & 0xffff; 1576d52ebf10SThomas Chou } 1577272cc70bSAndy Fleming 1578272cc70bSAndy Fleming /* Get the Card-Specific Data */ 1579272cc70bSAndy Fleming cmd.cmdidx = MMC_CMD_SEND_CSD; 1580272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_R2; 1581272cc70bSAndy Fleming cmd.cmdarg = mmc->rca << 16; 1582272cc70bSAndy Fleming 1583272cc70bSAndy Fleming err = mmc_send_cmd(mmc, &cmd, NULL); 1584272cc70bSAndy Fleming 1585272cc70bSAndy Fleming if (err) 1586272cc70bSAndy Fleming return err; 1587272cc70bSAndy Fleming 1588998be3ddSRabin Vincent mmc->csd[0] = cmd.response[0]; 1589998be3ddSRabin Vincent mmc->csd[1] = cmd.response[1]; 1590998be3ddSRabin Vincent mmc->csd[2] = cmd.response[2]; 1591998be3ddSRabin Vincent mmc->csd[3] = cmd.response[3]; 1592272cc70bSAndy Fleming 1593272cc70bSAndy Fleming if (mmc->version == MMC_VERSION_UNKNOWN) { 15940b453ffeSRabin Vincent int version = (cmd.response[0] >> 26) & 0xf; 1595272cc70bSAndy Fleming 1596272cc70bSAndy Fleming switch (version) { 1597272cc70bSAndy Fleming case 0: 1598272cc70bSAndy Fleming mmc->version = MMC_VERSION_1_2; 1599272cc70bSAndy Fleming break; 1600272cc70bSAndy Fleming case 1: 1601272cc70bSAndy Fleming mmc->version = MMC_VERSION_1_4; 1602272cc70bSAndy Fleming break; 1603272cc70bSAndy Fleming case 2: 1604272cc70bSAndy Fleming mmc->version = MMC_VERSION_2_2; 1605272cc70bSAndy Fleming break; 1606272cc70bSAndy Fleming case 3: 1607272cc70bSAndy Fleming mmc->version = MMC_VERSION_3; 1608272cc70bSAndy Fleming break; 1609272cc70bSAndy Fleming case 4: 1610272cc70bSAndy Fleming mmc->version = MMC_VERSION_4; 1611272cc70bSAndy Fleming break; 1612272cc70bSAndy Fleming default: 1613272cc70bSAndy Fleming mmc->version = MMC_VERSION_1_2; 1614272cc70bSAndy Fleming break; 1615272cc70bSAndy Fleming } 1616272cc70bSAndy Fleming } 1617272cc70bSAndy Fleming 1618272cc70bSAndy Fleming /* divide frequency by 10, since the mults are 10x bigger */ 16190b453ffeSRabin Vincent freq = fbase[(cmd.response[0] & 0x7)]; 16200b453ffeSRabin Vincent mult = multipliers[((cmd.response[0] >> 3) & 0xf)]; 1621272cc70bSAndy Fleming 16223e3ff0acSZiyuan Xu tran_speed = freq * mult; 1623272cc70bSAndy Fleming 1624ab71188cSMarkus Niebel mmc->dsr_imp = ((cmd.response[1] >> 12) & 0x1); 1625998be3ddSRabin Vincent mmc->read_bl_len = 1 << ((cmd.response[1] >> 16) & 0xf); 1626272cc70bSAndy Fleming 1627272cc70bSAndy Fleming if (IS_SD(mmc)) 1628272cc70bSAndy Fleming mmc->write_bl_len = mmc->read_bl_len; 1629272cc70bSAndy Fleming else 1630998be3ddSRabin Vincent mmc->write_bl_len = 1 << ((cmd.response[3] >> 22) & 0xf); 1631272cc70bSAndy Fleming 1632272cc70bSAndy Fleming if (mmc->high_capacity) { 1633272cc70bSAndy Fleming csize = (mmc->csd[1] & 0x3f) << 16 1634272cc70bSAndy Fleming | (mmc->csd[2] & 0xffff0000) >> 16; 1635272cc70bSAndy Fleming cmult = 8; 1636272cc70bSAndy Fleming } else { 1637272cc70bSAndy Fleming csize = (mmc->csd[1] & 0x3ff) << 2 1638272cc70bSAndy Fleming | (mmc->csd[2] & 0xc0000000) >> 30; 1639272cc70bSAndy Fleming cmult = (mmc->csd[2] & 0x00038000) >> 15; 1640272cc70bSAndy Fleming } 1641272cc70bSAndy Fleming 1642f866a46dSStephen Warren mmc->capacity_user = (csize + 1) << (cmult + 2); 1643f866a46dSStephen Warren mmc->capacity_user *= mmc->read_bl_len; 1644f866a46dSStephen Warren mmc->capacity_boot = 0; 1645f866a46dSStephen Warren mmc->capacity_rpmb = 0; 1646f866a46dSStephen Warren for (i = 0; i < 4; i++) 1647f866a46dSStephen Warren mmc->capacity_gp[i] = 0; 1648272cc70bSAndy Fleming 16498bfa195eSSimon Glass if (mmc->read_bl_len > MMC_MAX_BLOCK_LEN) 16508bfa195eSSimon Glass mmc->read_bl_len = MMC_MAX_BLOCK_LEN; 1651272cc70bSAndy Fleming 16528bfa195eSSimon Glass if (mmc->write_bl_len > MMC_MAX_BLOCK_LEN) 16538bfa195eSSimon Glass mmc->write_bl_len = MMC_MAX_BLOCK_LEN; 1654272cc70bSAndy Fleming 1655ab71188cSMarkus Niebel if ((mmc->dsr_imp) && (0xffffffff != mmc->dsr)) { 1656ab71188cSMarkus Niebel cmd.cmdidx = MMC_CMD_SET_DSR; 1657ab71188cSMarkus Niebel cmd.cmdarg = (mmc->dsr & 0xffff) << 16; 1658ab71188cSMarkus Niebel cmd.resp_type = MMC_RSP_NONE; 1659ab71188cSMarkus Niebel if (mmc_send_cmd(mmc, &cmd, NULL)) 1660ab71188cSMarkus Niebel printf("MMC: SET_DSR failed\n"); 1661ab71188cSMarkus Niebel } 1662ab71188cSMarkus Niebel 1663272cc70bSAndy Fleming /* Select the card, and put it into Transfer Mode */ 1664d52ebf10SThomas Chou if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */ 1665272cc70bSAndy Fleming cmd.cmdidx = MMC_CMD_SELECT_CARD; 1666fe8f7066SAjay Bhargav cmd.resp_type = MMC_RSP_R1; 1667272cc70bSAndy Fleming cmd.cmdarg = mmc->rca << 16; 1668272cc70bSAndy Fleming err = mmc_send_cmd(mmc, &cmd, NULL); 1669272cc70bSAndy Fleming 1670272cc70bSAndy Fleming if (err) 1671272cc70bSAndy Fleming return err; 1672d52ebf10SThomas Chou } 1673272cc70bSAndy Fleming 1674e6f99a56SLei Wen /* 1675e6f99a56SLei Wen * For SD, its erase group is always one sector 1676e6f99a56SLei Wen */ 1677e6f99a56SLei Wen mmc->erase_grp_size = 1; 1678bc897b1dSLei Wen mmc->part_config = MMCPART_NOAVAILABLE; 1679d23e2c09SSukumar Ghorai if (!IS_SD(mmc) && (mmc->version >= MMC_VERSION_4)) { 1680d23e2c09SSukumar Ghorai /* check ext_csd version and capacity */ 1681d23e2c09SSukumar Ghorai err = mmc_send_ext_csd(mmc, ext_csd); 16829cf199ebSDiego Santa Cruz if (err) 16839cf199ebSDiego Santa Cruz return err; 16849cf199ebSDiego Santa Cruz if (ext_csd[EXT_CSD_REV] >= 2) { 1685639b7827SYoshihiro Shimoda /* 1686639b7827SYoshihiro Shimoda * According to the JEDEC Standard, the value of 1687639b7827SYoshihiro Shimoda * ext_csd's capacity is valid if the value is more 1688639b7827SYoshihiro Shimoda * than 2GB 1689639b7827SYoshihiro Shimoda */ 16900560db18SLei Wen capacity = ext_csd[EXT_CSD_SEC_CNT] << 0 16910560db18SLei Wen | ext_csd[EXT_CSD_SEC_CNT + 1] << 8 16920560db18SLei Wen | ext_csd[EXT_CSD_SEC_CNT + 2] << 16 16930560db18SLei Wen | ext_csd[EXT_CSD_SEC_CNT + 3] << 24; 16948bfa195eSSimon Glass capacity *= MMC_MAX_BLOCK_LEN; 1695b1f1e821SŁukasz Majewski if ((capacity >> 20) > 2 * 1024) 1696f866a46dSStephen Warren mmc->capacity_user = capacity; 1697d23e2c09SSukumar Ghorai } 1698bc897b1dSLei Wen 169964f4a619SJaehoon Chung switch (ext_csd[EXT_CSD_REV]) { 170064f4a619SJaehoon Chung case 1: 170164f4a619SJaehoon Chung mmc->version = MMC_VERSION_4_1; 170264f4a619SJaehoon Chung break; 170364f4a619SJaehoon Chung case 2: 170464f4a619SJaehoon Chung mmc->version = MMC_VERSION_4_2; 170564f4a619SJaehoon Chung break; 170664f4a619SJaehoon Chung case 3: 170764f4a619SJaehoon Chung mmc->version = MMC_VERSION_4_3; 170864f4a619SJaehoon Chung break; 170964f4a619SJaehoon Chung case 5: 171064f4a619SJaehoon Chung mmc->version = MMC_VERSION_4_41; 171164f4a619SJaehoon Chung break; 171264f4a619SJaehoon Chung case 6: 171364f4a619SJaehoon Chung mmc->version = MMC_VERSION_4_5; 171464f4a619SJaehoon Chung break; 1715edab723bSMarkus Niebel case 7: 1716edab723bSMarkus Niebel mmc->version = MMC_VERSION_5_0; 1717edab723bSMarkus Niebel break; 17181a3619cfSStefan Wahren case 8: 17191a3619cfSStefan Wahren mmc->version = MMC_VERSION_5_1; 17201a3619cfSStefan Wahren break; 172164f4a619SJaehoon Chung } 172264f4a619SJaehoon Chung 17238a0cf490SDiego Santa Cruz /* The partition data may be non-zero but it is only 17248a0cf490SDiego Santa Cruz * effective if PARTITION_SETTING_COMPLETED is set in 17258a0cf490SDiego Santa Cruz * EXT_CSD, so ignore any data if this bit is not set, 17268a0cf490SDiego Santa Cruz * except for enabling the high-capacity group size 17278a0cf490SDiego Santa Cruz * definition (see below). */ 17288a0cf490SDiego Santa Cruz part_completed = !!(ext_csd[EXT_CSD_PARTITION_SETTING] & 17298a0cf490SDiego Santa Cruz EXT_CSD_PARTITION_SETTING_COMPLETED); 17308a0cf490SDiego Santa Cruz 17310c453bb7SDiego Santa Cruz /* store the partition info of emmc */ 17320c453bb7SDiego Santa Cruz mmc->part_support = ext_csd[EXT_CSD_PARTITIONING_SUPPORT]; 17330c453bb7SDiego Santa Cruz if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) || 17340c453bb7SDiego Santa Cruz ext_csd[EXT_CSD_BOOT_MULT]) 17350c453bb7SDiego Santa Cruz mmc->part_config = ext_csd[EXT_CSD_PART_CONF]; 17368a0cf490SDiego Santa Cruz if (part_completed && 17378a0cf490SDiego Santa Cruz (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & ENHNCD_SUPPORT)) 17380c453bb7SDiego Santa Cruz mmc->part_attr = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE]; 1739a6a1f5f8SJason Zhu if (ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT] & EXT_CSD_SEC_GB_CL_EN) 1740a6a1f5f8SJason Zhu mmc->esr.mmc_can_trim = 1; 17410c453bb7SDiego Santa Cruz 17420c453bb7SDiego Santa Cruz mmc->capacity_boot = ext_csd[EXT_CSD_BOOT_MULT] << 17; 17430c453bb7SDiego Santa Cruz 17440c453bb7SDiego Santa Cruz mmc->capacity_rpmb = ext_csd[EXT_CSD_RPMB_MULT] << 17; 17450c453bb7SDiego Santa Cruz 17460c453bb7SDiego Santa Cruz for (i = 0; i < 4; i++) { 17470c453bb7SDiego Santa Cruz int idx = EXT_CSD_GP_SIZE_MULT + i * 3; 17488a0cf490SDiego Santa Cruz uint mult = (ext_csd[idx + 2] << 16) + 17490c453bb7SDiego Santa Cruz (ext_csd[idx + 1] << 8) + ext_csd[idx]; 17508a0cf490SDiego Santa Cruz if (mult) 17518a0cf490SDiego Santa Cruz has_parts = true; 17528a0cf490SDiego Santa Cruz if (!part_completed) 17538a0cf490SDiego Santa Cruz continue; 17548a0cf490SDiego Santa Cruz mmc->capacity_gp[i] = mult; 17550c453bb7SDiego Santa Cruz mmc->capacity_gp[i] *= 17560c453bb7SDiego Santa Cruz ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; 17570c453bb7SDiego Santa Cruz mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; 1758f8e89d67SDiego Santa Cruz mmc->capacity_gp[i] <<= 19; 17590c453bb7SDiego Santa Cruz } 17600c453bb7SDiego Santa Cruz 17618a0cf490SDiego Santa Cruz if (part_completed) { 1762a7f852b6SDiego Santa Cruz mmc->enh_user_size = 1763a7f852b6SDiego Santa Cruz (ext_csd[EXT_CSD_ENH_SIZE_MULT+2] << 16) + 1764a7f852b6SDiego Santa Cruz (ext_csd[EXT_CSD_ENH_SIZE_MULT+1] << 8) + 1765a7f852b6SDiego Santa Cruz ext_csd[EXT_CSD_ENH_SIZE_MULT]; 1766a7f852b6SDiego Santa Cruz mmc->enh_user_size *= ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; 1767a7f852b6SDiego Santa Cruz mmc->enh_user_size *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; 1768a7f852b6SDiego Santa Cruz mmc->enh_user_size <<= 19; 1769a7f852b6SDiego Santa Cruz mmc->enh_user_start = 1770a7f852b6SDiego Santa Cruz (ext_csd[EXT_CSD_ENH_START_ADDR+3] << 24) + 1771a7f852b6SDiego Santa Cruz (ext_csd[EXT_CSD_ENH_START_ADDR+2] << 16) + 1772a7f852b6SDiego Santa Cruz (ext_csd[EXT_CSD_ENH_START_ADDR+1] << 8) + 1773a7f852b6SDiego Santa Cruz ext_csd[EXT_CSD_ENH_START_ADDR]; 1774a7f852b6SDiego Santa Cruz if (mmc->high_capacity) 1775a7f852b6SDiego Santa Cruz mmc->enh_user_start <<= 9; 17768a0cf490SDiego Santa Cruz } 1777a7f852b6SDiego Santa Cruz 1778e6f99a56SLei Wen /* 17791937e5aaSOliver Metz * Host needs to enable ERASE_GRP_DEF bit if device is 17801937e5aaSOliver Metz * partitioned. This bit will be lost every time after a reset 17811937e5aaSOliver Metz * or power off. This will affect erase size. 1782e6f99a56SLei Wen */ 17838a0cf490SDiego Santa Cruz if (part_completed) 17840c453bb7SDiego Santa Cruz has_parts = true; 17851937e5aaSOliver Metz if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) && 17860c453bb7SDiego Santa Cruz (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & PART_ENH_ATTRIB)) 17870c453bb7SDiego Santa Cruz has_parts = true; 17880c453bb7SDiego Santa Cruz if (has_parts) { 17891937e5aaSOliver Metz err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 17901937e5aaSOliver Metz EXT_CSD_ERASE_GROUP_DEF, 1); 17911937e5aaSOliver Metz 17921937e5aaSOliver Metz if (err) 17931937e5aaSOliver Metz return err; 1794021a8055SHannes Petermaier else 1795021a8055SHannes Petermaier ext_csd[EXT_CSD_ERASE_GROUP_DEF] = 1; 1796037dc0abSDiego Santa Cruz } 17971937e5aaSOliver Metz 1798037dc0abSDiego Santa Cruz if (ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x01) { 17991937e5aaSOliver Metz /* Read out group size from ext_csd */ 18000560db18SLei Wen mmc->erase_grp_size = 1801a4ff9f83SDiego Santa Cruz ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 1024; 1802d7b29129SMarkus Niebel /* 1803d7b29129SMarkus Niebel * if high capacity and partition setting completed 1804d7b29129SMarkus Niebel * SEC_COUNT is valid even if it is smaller than 2 GiB 1805d7b29129SMarkus Niebel * JEDEC Standard JESD84-B45, 6.2.4 1806d7b29129SMarkus Niebel */ 18078a0cf490SDiego Santa Cruz if (mmc->high_capacity && part_completed) { 1808d7b29129SMarkus Niebel capacity = (ext_csd[EXT_CSD_SEC_CNT]) | 1809d7b29129SMarkus Niebel (ext_csd[EXT_CSD_SEC_CNT + 1] << 8) | 1810d7b29129SMarkus Niebel (ext_csd[EXT_CSD_SEC_CNT + 2] << 16) | 1811d7b29129SMarkus Niebel (ext_csd[EXT_CSD_SEC_CNT + 3] << 24); 1812d7b29129SMarkus Niebel capacity *= MMC_MAX_BLOCK_LEN; 1813d7b29129SMarkus Niebel mmc->capacity_user = capacity; 1814d7b29129SMarkus Niebel } 18158bfa195eSSimon Glass } else { 18161937e5aaSOliver Metz /* Calculate the group size from the csd value. */ 1817e6f99a56SLei Wen int erase_gsz, erase_gmul; 1818e6f99a56SLei Wen erase_gsz = (mmc->csd[2] & 0x00007c00) >> 10; 1819e6f99a56SLei Wen erase_gmul = (mmc->csd[2] & 0x000003e0) >> 5; 1820e6f99a56SLei Wen mmc->erase_grp_size = (erase_gsz + 1) 1821e6f99a56SLei Wen * (erase_gmul + 1); 1822e6f99a56SLei Wen } 1823037dc0abSDiego Santa Cruz 1824037dc0abSDiego Santa Cruz mmc->hc_wp_grp_size = 1024 1825037dc0abSDiego Santa Cruz * ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] 1826037dc0abSDiego Santa Cruz * ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; 18279e41a00bSDiego Santa Cruz 18289e41a00bSDiego Santa Cruz mmc->wr_rel_set = ext_csd[EXT_CSD_WR_REL_SET]; 1829f866a46dSStephen Warren } 1830f866a46dSStephen Warren 1831c40fdca6SSimon Glass err = mmc_set_capacity(mmc, mmc_get_blk_desc(mmc)->hwpart); 1832f866a46dSStephen Warren if (err) 1833f866a46dSStephen Warren return err; 1834d23e2c09SSukumar Ghorai 1835272cc70bSAndy Fleming if (IS_SD(mmc)) 1836272cc70bSAndy Fleming err = sd_change_freq(mmc); 1837272cc70bSAndy Fleming else 1838272cc70bSAndy Fleming err = mmc_change_freq(mmc); 1839272cc70bSAndy Fleming 1840272cc70bSAndy Fleming if (err) 1841272cc70bSAndy Fleming return err; 1842272cc70bSAndy Fleming 1843272cc70bSAndy Fleming /* Restrict card's capabilities by what the host can do */ 184493bfd616SPantelis Antoniou mmc->card_caps &= mmc->cfg->host_caps; 1845272cc70bSAndy Fleming 1846272cc70bSAndy Fleming if (IS_SD(mmc)) { 1847272cc70bSAndy Fleming if (mmc->card_caps & MMC_MODE_4BIT) { 1848272cc70bSAndy Fleming cmd.cmdidx = MMC_CMD_APP_CMD; 1849272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_R1; 1850272cc70bSAndy Fleming cmd.cmdarg = mmc->rca << 16; 1851272cc70bSAndy Fleming 1852272cc70bSAndy Fleming err = mmc_send_cmd(mmc, &cmd, NULL); 1853272cc70bSAndy Fleming if (err) 1854272cc70bSAndy Fleming return err; 1855272cc70bSAndy Fleming 1856272cc70bSAndy Fleming cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH; 1857272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_R1; 1858272cc70bSAndy Fleming cmd.cmdarg = 2; 1859272cc70bSAndy Fleming err = mmc_send_cmd(mmc, &cmd, NULL); 1860272cc70bSAndy Fleming if (err) 1861272cc70bSAndy Fleming return err; 1862272cc70bSAndy Fleming 1863272cc70bSAndy Fleming mmc_set_bus_width(mmc, 4); 1864272cc70bSAndy Fleming } 1865272cc70bSAndy Fleming 18663697e599SPeng Fan err = sd_read_ssr(mmc); 18673697e599SPeng Fan if (err) 18683697e599SPeng Fan return err; 18693697e599SPeng Fan 1870272cc70bSAndy Fleming if (mmc->card_caps & MMC_MODE_HS) 18713e3ff0acSZiyuan Xu tran_speed = 50000000; 1872272cc70bSAndy Fleming else 18733e3ff0acSZiyuan Xu tran_speed = 25000000; 1874ad5fd922SJaehoon Chung 18753e3ff0acSZiyuan Xu mmc_set_clock(mmc, tran_speed); 187649dba033SZiyuan Xu } 1877272cc70bSAndy Fleming 18785af8f45cSAndrew Gabbasov /* Fix the block length for DDR mode */ 187949dba033SZiyuan Xu if (mmc_card_ddr(mmc)) { 18805af8f45cSAndrew Gabbasov mmc->read_bl_len = MMC_MAX_BLOCK_LEN; 18815af8f45cSAndrew Gabbasov mmc->write_bl_len = MMC_MAX_BLOCK_LEN; 18825af8f45cSAndrew Gabbasov } 18835af8f45cSAndrew Gabbasov 1884272cc70bSAndy Fleming /* fill in device description */ 1885c40fdca6SSimon Glass bdesc = mmc_get_blk_desc(mmc); 1886c40fdca6SSimon Glass bdesc->lun = 0; 1887c40fdca6SSimon Glass bdesc->hwpart = 0; 1888c40fdca6SSimon Glass bdesc->type = 0; 1889c40fdca6SSimon Glass bdesc->blksz = mmc->read_bl_len; 1890c40fdca6SSimon Glass bdesc->log2blksz = LOG2(bdesc->blksz); 1891c40fdca6SSimon Glass bdesc->lba = lldiv(mmc->capacity, mmc->read_bl_len); 1892fc011f64SSjoerd Simons #if !defined(CONFIG_SPL_BUILD) || \ 1893fc011f64SSjoerd Simons (defined(CONFIG_SPL_LIBCOMMON_SUPPORT) && \ 1894fc011f64SSjoerd Simons !defined(CONFIG_USE_TINY_PRINTF)) 1895c40fdca6SSimon Glass sprintf(bdesc->vendor, "Man %06x Snr %04x%04x", 1896babce5f6STaylor Hutt mmc->cid[0] >> 24, (mmc->cid[2] & 0xffff), 1897babce5f6STaylor Hutt (mmc->cid[3] >> 16) & 0xffff); 1898c40fdca6SSimon Glass sprintf(bdesc->product, "%c%c%c%c%c%c", mmc->cid[0] & 0xff, 18990b453ffeSRabin Vincent (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff, 1900babce5f6STaylor Hutt (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff, 1901babce5f6STaylor Hutt (mmc->cid[2] >> 24) & 0xff); 1902c40fdca6SSimon Glass sprintf(bdesc->revision, "%d.%d", (mmc->cid[2] >> 20) & 0xf, 1903babce5f6STaylor Hutt (mmc->cid[2] >> 16) & 0xf); 190456196826SPaul Burton #else 1905c40fdca6SSimon Glass bdesc->vendor[0] = 0; 1906c40fdca6SSimon Glass bdesc->product[0] = 0; 1907c40fdca6SSimon Glass bdesc->revision[0] = 0; 190856196826SPaul Burton #endif 1909122efd43SMikhail Kshevetskiy #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBDISK_SUPPORT) 1910c40fdca6SSimon Glass part_init(bdesc); 1911122efd43SMikhail Kshevetskiy #endif 1912272cc70bSAndy Fleming 1913272cc70bSAndy Fleming return 0; 1914272cc70bSAndy Fleming } 1915272cc70bSAndy Fleming 1916fdbb873eSKim Phillips static int mmc_send_if_cond(struct mmc *mmc) 1917272cc70bSAndy Fleming { 1918272cc70bSAndy Fleming struct mmc_cmd cmd; 1919272cc70bSAndy Fleming int err; 1920272cc70bSAndy Fleming 1921272cc70bSAndy Fleming cmd.cmdidx = SD_CMD_SEND_IF_COND; 1922272cc70bSAndy Fleming /* We set the bit if the host supports voltages between 2.7 and 3.6 V */ 192393bfd616SPantelis Antoniou cmd.cmdarg = ((mmc->cfg->voltages & 0xff8000) != 0) << 8 | 0xaa; 1924272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_R7; 1925272cc70bSAndy Fleming 1926272cc70bSAndy Fleming err = mmc_send_cmd(mmc, &cmd, NULL); 1927272cc70bSAndy Fleming 1928272cc70bSAndy Fleming if (err) 1929272cc70bSAndy Fleming return err; 1930272cc70bSAndy Fleming 1931998be3ddSRabin Vincent if ((cmd.response[0] & 0xff) != 0xaa) 1932915ffa52SJaehoon Chung return -EOPNOTSUPP; 1933272cc70bSAndy Fleming else 1934272cc70bSAndy Fleming mmc->version = SD_VERSION_2; 1935272cc70bSAndy Fleming 1936272cc70bSAndy Fleming return 0; 1937272cc70bSAndy Fleming } 1938272cc70bSAndy Fleming 1939c4d660d4SSimon Glass #if !CONFIG_IS_ENABLED(DM_MMC) 194095de9ab2SPaul Kocialkowski /* board-specific MMC power initializations. */ 194195de9ab2SPaul Kocialkowski __weak void board_mmc_power_init(void) 194295de9ab2SPaul Kocialkowski { 194395de9ab2SPaul Kocialkowski } 194405cbeb7cSSimon Glass #endif 194595de9ab2SPaul Kocialkowski 19462051aefeSPeng Fan static int mmc_power_init(struct mmc *mmc) 19472051aefeSPeng Fan { 1948c4d660d4SSimon Glass #if CONFIG_IS_ENABLED(DM_MMC) 194905cbeb7cSSimon Glass #if defined(CONFIG_DM_REGULATOR) && !defined(CONFIG_SPL_BUILD) 19502051aefeSPeng Fan struct udevice *vmmc_supply; 19512051aefeSPeng Fan int ret; 19522051aefeSPeng Fan 19532051aefeSPeng Fan ret = device_get_supply_regulator(mmc->dev, "vmmc-supply", 19542051aefeSPeng Fan &vmmc_supply); 19552051aefeSPeng Fan if (ret) { 1956288db7c7SJaehoon Chung debug("%s: No vmmc supply\n", mmc->dev->name); 19572051aefeSPeng Fan return 0; 19582051aefeSPeng Fan } 19592051aefeSPeng Fan 19602051aefeSPeng Fan ret = regulator_set_enable(vmmc_supply, true); 19612051aefeSPeng Fan if (ret) { 19622051aefeSPeng Fan puts("Error enabling VMMC supply\n"); 19632051aefeSPeng Fan return ret; 19642051aefeSPeng Fan } 19652051aefeSPeng Fan #endif 196605cbeb7cSSimon Glass #else /* !CONFIG_DM_MMC */ 196705cbeb7cSSimon Glass /* 196805cbeb7cSSimon Glass * Driver model should use a regulator, as above, rather than calling 196905cbeb7cSSimon Glass * out to board code. 197005cbeb7cSSimon Glass */ 197105cbeb7cSSimon Glass board_mmc_power_init(); 197205cbeb7cSSimon Glass #endif 19732051aefeSPeng Fan return 0; 19742051aefeSPeng Fan } 19752051aefeSPeng Fan 1976e9550449SChe-Liang Chiou int mmc_start_init(struct mmc *mmc) 1977272cc70bSAndy Fleming { 19788ca51e51SSimon Glass bool no_card; 1979afd5932bSMacpaul Lin int err; 1980272cc70bSAndy Fleming 1981ab769f22SPantelis Antoniou /* we pretend there's no card when init is NULL */ 19828ca51e51SSimon Glass no_card = mmc_getcd(mmc) == 0; 1983e7881d85SSimon Glass #if !CONFIG_IS_ENABLED(DM_MMC) 19848ca51e51SSimon Glass no_card = no_card || (mmc->cfg->ops->init == NULL); 19858ca51e51SSimon Glass #endif 19868ca51e51SSimon Glass if (no_card) { 198748972d90SThierry Reding mmc->has_init = 0; 198856196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) 198948972d90SThierry Reding printf("MMC: no card present\n"); 199056196826SPaul Burton #endif 1991915ffa52SJaehoon Chung return -ENOMEDIUM; 199248972d90SThierry Reding } 199348972d90SThierry Reding 1994bc897b1dSLei Wen if (mmc->has_init) 1995bc897b1dSLei Wen return 0; 1996bc897b1dSLei Wen 19975a8dbdc6SYangbo Lu #ifdef CONFIG_FSL_ESDHC_ADAPTER_IDENT 19985a8dbdc6SYangbo Lu mmc_adapter_card_type_ident(); 19995a8dbdc6SYangbo Lu #endif 20002051aefeSPeng Fan err = mmc_power_init(mmc); 20012051aefeSPeng Fan if (err) 20022051aefeSPeng Fan return err; 200395de9ab2SPaul Kocialkowski 2004e7881d85SSimon Glass #if CONFIG_IS_ENABLED(DM_MMC) 20058ca51e51SSimon Glass /* The device has already been probed ready for use */ 20068ca51e51SSimon Glass #else 2007ab769f22SPantelis Antoniou /* made sure it's not NULL earlier */ 200893bfd616SPantelis Antoniou err = mmc->cfg->ops->init(mmc); 2009272cc70bSAndy Fleming if (err) 2010272cc70bSAndy Fleming return err; 20118ca51e51SSimon Glass #endif 2012b86b85e2SIlya Yanok mmc_set_bus_width(mmc, 1); 2013b86b85e2SIlya Yanok mmc_set_clock(mmc, 1); 201481db2d36SZiyuan Xu mmc_set_timing(mmc, MMC_TIMING_LEGACY); 2015b86b85e2SIlya Yanok 2016272cc70bSAndy Fleming /* Reset the Card */ 2017272cc70bSAndy Fleming err = mmc_go_idle(mmc); 2018272cc70bSAndy Fleming 2019272cc70bSAndy Fleming if (err) 2020272cc70bSAndy Fleming return err; 2021272cc70bSAndy Fleming 2022bc897b1dSLei Wen /* The internal partition reset to user partition(0) at every CMD0*/ 2023c40fdca6SSimon Glass mmc_get_blk_desc(mmc)->hwpart = 0; 2024bc897b1dSLei Wen 2025272cc70bSAndy Fleming /* Test for SD version 2 */ 2026272cc70bSAndy Fleming err = mmc_send_if_cond(mmc); 2027272cc70bSAndy Fleming 2028272cc70bSAndy Fleming /* Now try to get the SD card's operating condition */ 2029272cc70bSAndy Fleming err = sd_send_op_cond(mmc); 2030272cc70bSAndy Fleming 2031272cc70bSAndy Fleming /* If the command timed out, we check for an MMC card */ 2032915ffa52SJaehoon Chung if (err == -ETIMEDOUT) { 2033272cc70bSAndy Fleming err = mmc_send_op_cond(mmc); 2034272cc70bSAndy Fleming 2035bd47c135SAndrew Gabbasov if (err) { 203656196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) 2037272cc70bSAndy Fleming printf("Card did not respond to voltage select!\n"); 203856196826SPaul Burton #endif 2039915ffa52SJaehoon Chung return -EOPNOTSUPP; 2040272cc70bSAndy Fleming } 2041272cc70bSAndy Fleming } 2042272cc70bSAndy Fleming 2043bd47c135SAndrew Gabbasov if (!err) 2044e9550449SChe-Liang Chiou mmc->init_in_progress = 1; 2045e9550449SChe-Liang Chiou 2046e9550449SChe-Liang Chiou return err; 2047e9550449SChe-Liang Chiou } 2048e9550449SChe-Liang Chiou 2049e9550449SChe-Liang Chiou static int mmc_complete_init(struct mmc *mmc) 2050e9550449SChe-Liang Chiou { 2051e9550449SChe-Liang Chiou int err = 0; 2052e9550449SChe-Liang Chiou 2053bd47c135SAndrew Gabbasov mmc->init_in_progress = 0; 2054e9550449SChe-Liang Chiou if (mmc->op_cond_pending) 2055e9550449SChe-Liang Chiou err = mmc_complete_op_cond(mmc); 2056e9550449SChe-Liang Chiou 2057e9550449SChe-Liang Chiou if (!err) 2058bc897b1dSLei Wen err = mmc_startup(mmc); 2059bc897b1dSLei Wen if (err) 2060bc897b1dSLei Wen mmc->has_init = 0; 2061bc897b1dSLei Wen else 2062bc897b1dSLei Wen mmc->has_init = 1; 2063e9550449SChe-Liang Chiou return err; 2064e9550449SChe-Liang Chiou } 2065e9550449SChe-Liang Chiou 2066e9550449SChe-Liang Chiou int mmc_init(struct mmc *mmc) 2067e9550449SChe-Liang Chiou { 2068bd47c135SAndrew Gabbasov int err = 0; 2069ce9eca94SMarek Vasut __maybe_unused unsigned start; 2070c4d660d4SSimon Glass #if CONFIG_IS_ENABLED(DM_MMC) 207133fb211dSSimon Glass struct mmc_uclass_priv *upriv = dev_get_uclass_priv(mmc->dev); 2072e9550449SChe-Liang Chiou 207333fb211dSSimon Glass upriv->mmc = mmc; 207433fb211dSSimon Glass #endif 2075e9550449SChe-Liang Chiou if (mmc->has_init) 2076e9550449SChe-Liang Chiou return 0; 2077d803fea5SMateusz Zalega 2078d803fea5SMateusz Zalega start = get_timer(0); 2079d803fea5SMateusz Zalega 2080e9550449SChe-Liang Chiou if (!mmc->init_in_progress) 2081e9550449SChe-Liang Chiou err = mmc_start_init(mmc); 2082e9550449SChe-Liang Chiou 2083bd47c135SAndrew Gabbasov if (!err) 2084e9550449SChe-Liang Chiou err = mmc_complete_init(mmc); 2085919b4858SJagan Teki if (err) 2086919b4858SJagan Teki printf("%s: %d, time %lu\n", __func__, err, get_timer(start)); 2087919b4858SJagan Teki 2088bc897b1dSLei Wen return err; 2089272cc70bSAndy Fleming } 2090272cc70bSAndy Fleming 2091ab71188cSMarkus Niebel int mmc_set_dsr(struct mmc *mmc, u16 val) 2092ab71188cSMarkus Niebel { 2093ab71188cSMarkus Niebel mmc->dsr = val; 2094ab71188cSMarkus Niebel return 0; 2095ab71188cSMarkus Niebel } 2096ab71188cSMarkus Niebel 2097cee9ab7cSJeroen Hofstee /* CPU-specific MMC initializations */ 2098cee9ab7cSJeroen Hofstee __weak int cpu_mmc_init(bd_t *bis) 2099272cc70bSAndy Fleming { 2100272cc70bSAndy Fleming return -1; 2101272cc70bSAndy Fleming } 2102272cc70bSAndy Fleming 2103cee9ab7cSJeroen Hofstee /* board-specific MMC initializations. */ 2104cee9ab7cSJeroen Hofstee __weak int board_mmc_init(bd_t *bis) 2105cee9ab7cSJeroen Hofstee { 2106cee9ab7cSJeroen Hofstee return -1; 2107cee9ab7cSJeroen Hofstee } 2108272cc70bSAndy Fleming 2109e9550449SChe-Liang Chiou void mmc_set_preinit(struct mmc *mmc, int preinit) 2110e9550449SChe-Liang Chiou { 2111e9550449SChe-Liang Chiou mmc->preinit = preinit; 2112e9550449SChe-Liang Chiou } 2113e9550449SChe-Liang Chiou 2114c4d660d4SSimon Glass #if CONFIG_IS_ENABLED(DM_MMC) && defined(CONFIG_SPL_BUILD) 21158e3332e2SSjoerd Simons static int mmc_probe(bd_t *bis) 21168e3332e2SSjoerd Simons { 21178e3332e2SSjoerd Simons return 0; 21188e3332e2SSjoerd Simons } 2119c4d660d4SSimon Glass #elif CONFIG_IS_ENABLED(DM_MMC) 21208e3332e2SSjoerd Simons static int mmc_probe(bd_t *bis) 21218e3332e2SSjoerd Simons { 21224a1db6d8SSimon Glass int ret, i; 21238e3332e2SSjoerd Simons struct uclass *uc; 21244a1db6d8SSimon Glass struct udevice *dev; 21258e3332e2SSjoerd Simons 21268e3332e2SSjoerd Simons ret = uclass_get(UCLASS_MMC, &uc); 21278e3332e2SSjoerd Simons if (ret) 21288e3332e2SSjoerd Simons return ret; 21298e3332e2SSjoerd Simons 21304a1db6d8SSimon Glass /* 21314a1db6d8SSimon Glass * Try to add them in sequence order. Really with driver model we 21324a1db6d8SSimon Glass * should allow holes, but the current MMC list does not allow that. 21334a1db6d8SSimon Glass * So if we request 0, 1, 3 we will get 0, 1, 2. 21344a1db6d8SSimon Glass */ 21354a1db6d8SSimon Glass for (i = 0; ; i++) { 21364a1db6d8SSimon Glass ret = uclass_get_device_by_seq(UCLASS_MMC, i, &dev); 21374a1db6d8SSimon Glass if (ret == -ENODEV) 21384a1db6d8SSimon Glass break; 21394a1db6d8SSimon Glass } 21404a1db6d8SSimon Glass uclass_foreach_dev(dev, uc) { 21414a1db6d8SSimon Glass ret = device_probe(dev); 21428e3332e2SSjoerd Simons if (ret) 21434a1db6d8SSimon Glass printf("%s - probe failed: %d\n", dev->name, ret); 21448e3332e2SSjoerd Simons } 21458e3332e2SSjoerd Simons 21468e3332e2SSjoerd Simons return 0; 21478e3332e2SSjoerd Simons } 21488e3332e2SSjoerd Simons #else 21498e3332e2SSjoerd Simons static int mmc_probe(bd_t *bis) 21508e3332e2SSjoerd Simons { 21518e3332e2SSjoerd Simons if (board_mmc_init(bis) < 0) 21528e3332e2SSjoerd Simons cpu_mmc_init(bis); 21538e3332e2SSjoerd Simons 21548e3332e2SSjoerd Simons return 0; 21558e3332e2SSjoerd Simons } 21568e3332e2SSjoerd Simons #endif 2157e9550449SChe-Liang Chiou 2158272cc70bSAndy Fleming int mmc_initialize(bd_t *bis) 2159272cc70bSAndy Fleming { 21601b26bab1SDaniel Kochmański static int initialized = 0; 21618e3332e2SSjoerd Simons int ret; 21621b26bab1SDaniel Kochmański if (initialized) /* Avoid initializing mmc multiple times */ 21631b26bab1SDaniel Kochmański return 0; 21641b26bab1SDaniel Kochmański initialized = 1; 21651b26bab1SDaniel Kochmański 2166c4d660d4SSimon Glass #if !CONFIG_IS_ENABLED(BLK) 2167b5b838f1SMarek Vasut #if !CONFIG_IS_ENABLED(MMC_TINY) 2168c40fdca6SSimon Glass mmc_list_init(); 2169c40fdca6SSimon Glass #endif 2170b5b838f1SMarek Vasut #endif 21718e3332e2SSjoerd Simons ret = mmc_probe(bis); 21728e3332e2SSjoerd Simons if (ret) 21738e3332e2SSjoerd Simons return ret; 2174272cc70bSAndy Fleming 2175bb0dc108SYing Zhang #ifndef CONFIG_SPL_BUILD 2176272cc70bSAndy Fleming print_mmc_devices(','); 2177bb0dc108SYing Zhang #endif 2178272cc70bSAndy Fleming 2179c40fdca6SSimon Glass mmc_do_preinit(); 2180272cc70bSAndy Fleming return 0; 2181272cc70bSAndy Fleming } 2182cd3d4880STomas Melin 2183cd3d4880STomas Melin #ifdef CONFIG_CMD_BKOPS_ENABLE 2184cd3d4880STomas Melin int mmc_set_bkops_enable(struct mmc *mmc) 2185cd3d4880STomas Melin { 2186cd3d4880STomas Melin int err; 2187cd3d4880STomas Melin ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); 2188cd3d4880STomas Melin 2189cd3d4880STomas Melin err = mmc_send_ext_csd(mmc, ext_csd); 2190cd3d4880STomas Melin if (err) { 2191cd3d4880STomas Melin puts("Could not get ext_csd register values\n"); 2192cd3d4880STomas Melin return err; 2193cd3d4880STomas Melin } 2194cd3d4880STomas Melin 2195cd3d4880STomas Melin if (!(ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1)) { 2196cd3d4880STomas Melin puts("Background operations not supported on device\n"); 2197cd3d4880STomas Melin return -EMEDIUMTYPE; 2198cd3d4880STomas Melin } 2199cd3d4880STomas Melin 2200cd3d4880STomas Melin if (ext_csd[EXT_CSD_BKOPS_EN] & 0x1) { 2201cd3d4880STomas Melin puts("Background operations already enabled\n"); 2202cd3d4880STomas Melin return 0; 2203cd3d4880STomas Melin } 2204cd3d4880STomas Melin 2205cd3d4880STomas Melin err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BKOPS_EN, 1); 2206cd3d4880STomas Melin if (err) { 2207cd3d4880STomas Melin puts("Failed to enable manual background operations\n"); 2208cd3d4880STomas Melin return err; 2209cd3d4880STomas Melin } 2210cd3d4880STomas Melin 2211cd3d4880STomas Melin puts("Enabled manual background operations\n"); 2212cd3d4880STomas Melin 2213cd3d4880STomas Melin return 0; 2214cd3d4880STomas Melin } 2215cd3d4880STomas Melin #endif 2216