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> 13272cc70bSAndy Fleming #include <mmc.h> 14272cc70bSAndy Fleming #include <part.h> 15272cc70bSAndy Fleming #include <malloc.h> 16272cc70bSAndy Fleming #include <linux/list.h> 179b1f942cSRabin Vincent #include <div64.h> 18da61fa5fSPaul Burton #include "mmc_private.h" 19272cc70bSAndy Fleming 20272cc70bSAndy Fleming static struct list_head mmc_devices; 21272cc70bSAndy Fleming static int cur_dev_num = -1; 22272cc70bSAndy Fleming 23d23d8d7eSNikita Kiryanov int __weak board_mmc_getwp(struct mmc *mmc) 24d23d8d7eSNikita Kiryanov { 25d23d8d7eSNikita Kiryanov return -1; 26d23d8d7eSNikita Kiryanov } 27d23d8d7eSNikita Kiryanov 28d23d8d7eSNikita Kiryanov int mmc_getwp(struct mmc *mmc) 29d23d8d7eSNikita Kiryanov { 30d23d8d7eSNikita Kiryanov int wp; 31d23d8d7eSNikita Kiryanov 32d23d8d7eSNikita Kiryanov wp = board_mmc_getwp(mmc); 33d23d8d7eSNikita Kiryanov 34d4e1da4eSPeter Korsgaard if (wp < 0) { 3593bfd616SPantelis Antoniou if (mmc->cfg->ops->getwp) 3693bfd616SPantelis Antoniou wp = mmc->cfg->ops->getwp(mmc); 37d4e1da4eSPeter Korsgaard else 38d4e1da4eSPeter Korsgaard wp = 0; 39d4e1da4eSPeter Korsgaard } 40d23d8d7eSNikita Kiryanov 41d23d8d7eSNikita Kiryanov return wp; 42d23d8d7eSNikita Kiryanov } 43d23d8d7eSNikita Kiryanov 44314284b1SThierry Reding int __board_mmc_getcd(struct mmc *mmc) { 4511fdade2SStefano Babic return -1; 4611fdade2SStefano Babic } 4711fdade2SStefano Babic 48314284b1SThierry Reding int board_mmc_getcd(struct mmc *mmc)__attribute__((weak, 4911fdade2SStefano Babic alias("__board_mmc_getcd"))); 5011fdade2SStefano Babic 51da61fa5fSPaul Burton int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) 52272cc70bSAndy Fleming { 535db2fe3aSRaffaele Recalcati int ret; 548635ff9eSMarek Vasut 558635ff9eSMarek Vasut #ifdef CONFIG_MMC_TRACE 565db2fe3aSRaffaele Recalcati int i; 575db2fe3aSRaffaele Recalcati u8 *ptr; 585db2fe3aSRaffaele Recalcati 595db2fe3aSRaffaele Recalcati printf("CMD_SEND:%d\n", cmd->cmdidx); 605db2fe3aSRaffaele Recalcati printf("\t\tARG\t\t\t 0x%08X\n", cmd->cmdarg); 6193bfd616SPantelis Antoniou ret = mmc->cfg->ops->send_cmd(mmc, cmd, data); 625db2fe3aSRaffaele Recalcati switch (cmd->resp_type) { 635db2fe3aSRaffaele Recalcati case MMC_RSP_NONE: 645db2fe3aSRaffaele Recalcati printf("\t\tMMC_RSP_NONE\n"); 655db2fe3aSRaffaele Recalcati break; 665db2fe3aSRaffaele Recalcati case MMC_RSP_R1: 675db2fe3aSRaffaele Recalcati printf("\t\tMMC_RSP_R1,5,6,7 \t 0x%08X \n", 685db2fe3aSRaffaele Recalcati cmd->response[0]); 695db2fe3aSRaffaele Recalcati break; 705db2fe3aSRaffaele Recalcati case MMC_RSP_R1b: 715db2fe3aSRaffaele Recalcati printf("\t\tMMC_RSP_R1b\t\t 0x%08X \n", 725db2fe3aSRaffaele Recalcati cmd->response[0]); 735db2fe3aSRaffaele Recalcati break; 745db2fe3aSRaffaele Recalcati case MMC_RSP_R2: 755db2fe3aSRaffaele Recalcati printf("\t\tMMC_RSP_R2\t\t 0x%08X \n", 765db2fe3aSRaffaele Recalcati cmd->response[0]); 775db2fe3aSRaffaele Recalcati printf("\t\t \t\t 0x%08X \n", 785db2fe3aSRaffaele Recalcati cmd->response[1]); 795db2fe3aSRaffaele Recalcati printf("\t\t \t\t 0x%08X \n", 805db2fe3aSRaffaele Recalcati cmd->response[2]); 815db2fe3aSRaffaele Recalcati printf("\t\t \t\t 0x%08X \n", 825db2fe3aSRaffaele Recalcati cmd->response[3]); 835db2fe3aSRaffaele Recalcati printf("\n"); 845db2fe3aSRaffaele Recalcati printf("\t\t\t\t\tDUMPING DATA\n"); 855db2fe3aSRaffaele Recalcati for (i = 0; i < 4; i++) { 865db2fe3aSRaffaele Recalcati int j; 875db2fe3aSRaffaele Recalcati printf("\t\t\t\t\t%03d - ", i*4); 88146bec79SDirk Behme ptr = (u8 *)&cmd->response[i]; 895db2fe3aSRaffaele Recalcati ptr += 3; 905db2fe3aSRaffaele Recalcati for (j = 0; j < 4; j++) 915db2fe3aSRaffaele Recalcati printf("%02X ", *ptr--); 925db2fe3aSRaffaele Recalcati printf("\n"); 935db2fe3aSRaffaele Recalcati } 945db2fe3aSRaffaele Recalcati break; 955db2fe3aSRaffaele Recalcati case MMC_RSP_R3: 965db2fe3aSRaffaele Recalcati printf("\t\tMMC_RSP_R3,4\t\t 0x%08X \n", 975db2fe3aSRaffaele Recalcati cmd->response[0]); 985db2fe3aSRaffaele Recalcati break; 995db2fe3aSRaffaele Recalcati default: 1005db2fe3aSRaffaele Recalcati printf("\t\tERROR MMC rsp not supported\n"); 1015db2fe3aSRaffaele Recalcati break; 1025db2fe3aSRaffaele Recalcati } 1035db2fe3aSRaffaele Recalcati #else 10493bfd616SPantelis Antoniou ret = mmc->cfg->ops->send_cmd(mmc, cmd, data); 1055db2fe3aSRaffaele Recalcati #endif 1068635ff9eSMarek Vasut return ret; 107272cc70bSAndy Fleming } 108272cc70bSAndy Fleming 109da61fa5fSPaul Burton int mmc_send_status(struct mmc *mmc, int timeout) 1105d4fc8d9SRaffaele Recalcati { 1115d4fc8d9SRaffaele Recalcati struct mmc_cmd cmd; 112d617c426SJan Kloetzke int err, retries = 5; 1135d4fc8d9SRaffaele Recalcati #ifdef CONFIG_MMC_TRACE 1145d4fc8d9SRaffaele Recalcati int status; 1155d4fc8d9SRaffaele Recalcati #endif 1165d4fc8d9SRaffaele Recalcati 1175d4fc8d9SRaffaele Recalcati cmd.cmdidx = MMC_CMD_SEND_STATUS; 1185d4fc8d9SRaffaele Recalcati cmd.resp_type = MMC_RSP_R1; 119aaf3d41aSMarek Vasut if (!mmc_host_is_spi(mmc)) 120aaf3d41aSMarek Vasut cmd.cmdarg = mmc->rca << 16; 1215d4fc8d9SRaffaele Recalcati 1225d4fc8d9SRaffaele Recalcati do { 1235d4fc8d9SRaffaele Recalcati err = mmc_send_cmd(mmc, &cmd, NULL); 124d617c426SJan Kloetzke if (!err) { 125d617c426SJan Kloetzke if ((cmd.response[0] & MMC_STATUS_RDY_FOR_DATA) && 126d617c426SJan Kloetzke (cmd.response[0] & MMC_STATUS_CURR_STATE) != 127d617c426SJan Kloetzke MMC_STATE_PRG) 1285d4fc8d9SRaffaele Recalcati break; 129d617c426SJan Kloetzke else if (cmd.response[0] & MMC_STATUS_MASK) { 13056196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) 131d617c426SJan Kloetzke printf("Status Error: 0x%08X\n", 132d617c426SJan Kloetzke cmd.response[0]); 13356196826SPaul Burton #endif 134d617c426SJan Kloetzke return COMM_ERR; 135d617c426SJan Kloetzke } 136d617c426SJan Kloetzke } else if (--retries < 0) 137d617c426SJan Kloetzke return err; 1385d4fc8d9SRaffaele Recalcati 1395d4fc8d9SRaffaele Recalcati udelay(1000); 1405d4fc8d9SRaffaele Recalcati 1415d4fc8d9SRaffaele Recalcati } while (timeout--); 1425d4fc8d9SRaffaele Recalcati 1435db2fe3aSRaffaele Recalcati #ifdef CONFIG_MMC_TRACE 1445db2fe3aSRaffaele Recalcati status = (cmd.response[0] & MMC_STATUS_CURR_STATE) >> 9; 1455db2fe3aSRaffaele Recalcati printf("CURR STATE:%d\n", status); 1465db2fe3aSRaffaele Recalcati #endif 1475b0c942fSJongman Heo if (timeout <= 0) { 14856196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) 1495d4fc8d9SRaffaele Recalcati printf("Timeout waiting card ready\n"); 15056196826SPaul Burton #endif 1515d4fc8d9SRaffaele Recalcati return TIMEOUT; 1525d4fc8d9SRaffaele Recalcati } 1535d4fc8d9SRaffaele Recalcati 1545d4fc8d9SRaffaele Recalcati return 0; 1555d4fc8d9SRaffaele Recalcati } 1565d4fc8d9SRaffaele Recalcati 157da61fa5fSPaul Burton int mmc_set_blocklen(struct mmc *mmc, int len) 158272cc70bSAndy Fleming { 159272cc70bSAndy Fleming struct mmc_cmd cmd; 160272cc70bSAndy Fleming 161*d22e3d46SJaehoon Chung if (mmc->card_caps & MMC_MODE_DDR_52MHz) 162*d22e3d46SJaehoon Chung return 0; 163*d22e3d46SJaehoon Chung 164272cc70bSAndy Fleming cmd.cmdidx = MMC_CMD_SET_BLOCKLEN; 165272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_R1; 166272cc70bSAndy Fleming cmd.cmdarg = len; 167272cc70bSAndy Fleming 168272cc70bSAndy Fleming return mmc_send_cmd(mmc, &cmd, NULL); 169272cc70bSAndy Fleming } 170272cc70bSAndy Fleming 171272cc70bSAndy Fleming struct mmc *find_mmc_device(int dev_num) 172272cc70bSAndy Fleming { 173272cc70bSAndy Fleming struct mmc *m; 174272cc70bSAndy Fleming struct list_head *entry; 175272cc70bSAndy Fleming 176272cc70bSAndy Fleming list_for_each(entry, &mmc_devices) { 177272cc70bSAndy Fleming m = list_entry(entry, struct mmc, link); 178272cc70bSAndy Fleming 179272cc70bSAndy Fleming if (m->block_dev.dev == dev_num) 180272cc70bSAndy Fleming return m; 181272cc70bSAndy Fleming } 182272cc70bSAndy Fleming 18356196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) 184272cc70bSAndy Fleming printf("MMC Device %d not found\n", dev_num); 18556196826SPaul Burton #endif 186272cc70bSAndy Fleming 187272cc70bSAndy Fleming return NULL; 188272cc70bSAndy Fleming } 189272cc70bSAndy Fleming 190ff8fef56SSascha Silbe static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start, 191fdbb873eSKim Phillips lbaint_t blkcnt) 192272cc70bSAndy Fleming { 193272cc70bSAndy Fleming struct mmc_cmd cmd; 194272cc70bSAndy Fleming struct mmc_data data; 195272cc70bSAndy Fleming 1964a1a06bcSAlagu Sankar if (blkcnt > 1) 1974a1a06bcSAlagu Sankar cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK; 1984a1a06bcSAlagu Sankar else 199272cc70bSAndy Fleming cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK; 200272cc70bSAndy Fleming 201272cc70bSAndy Fleming if (mmc->high_capacity) 2024a1a06bcSAlagu Sankar cmd.cmdarg = start; 203272cc70bSAndy Fleming else 2044a1a06bcSAlagu Sankar cmd.cmdarg = start * mmc->read_bl_len; 205272cc70bSAndy Fleming 206272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_R1; 207272cc70bSAndy Fleming 208272cc70bSAndy Fleming data.dest = dst; 2094a1a06bcSAlagu Sankar data.blocks = blkcnt; 210272cc70bSAndy Fleming data.blocksize = mmc->read_bl_len; 211272cc70bSAndy Fleming data.flags = MMC_DATA_READ; 212272cc70bSAndy Fleming 2134a1a06bcSAlagu Sankar if (mmc_send_cmd(mmc, &cmd, &data)) 2144a1a06bcSAlagu Sankar return 0; 2154a1a06bcSAlagu Sankar 2164a1a06bcSAlagu Sankar if (blkcnt > 1) { 2174a1a06bcSAlagu Sankar cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; 2184a1a06bcSAlagu Sankar cmd.cmdarg = 0; 2194a1a06bcSAlagu Sankar cmd.resp_type = MMC_RSP_R1b; 2204a1a06bcSAlagu Sankar if (mmc_send_cmd(mmc, &cmd, NULL)) { 22156196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) 2224a1a06bcSAlagu Sankar printf("mmc fail to send stop cmd\n"); 22356196826SPaul Burton #endif 2244a1a06bcSAlagu Sankar return 0; 2254a1a06bcSAlagu Sankar } 226272cc70bSAndy Fleming } 227272cc70bSAndy Fleming 2284a1a06bcSAlagu Sankar return blkcnt; 229272cc70bSAndy Fleming } 230272cc70bSAndy Fleming 231ff8fef56SSascha Silbe static ulong mmc_bread(int dev_num, lbaint_t start, lbaint_t blkcnt, void *dst) 232272cc70bSAndy Fleming { 2334a1a06bcSAlagu Sankar lbaint_t cur, blocks_todo = blkcnt; 234272cc70bSAndy Fleming 2354a1a06bcSAlagu Sankar if (blkcnt == 0) 2364a1a06bcSAlagu Sankar return 0; 2374a1a06bcSAlagu Sankar 2384a1a06bcSAlagu Sankar struct mmc *mmc = find_mmc_device(dev_num); 239272cc70bSAndy Fleming if (!mmc) 240272cc70bSAndy Fleming return 0; 241272cc70bSAndy Fleming 242d2bf29e3SLei Wen if ((start + blkcnt) > mmc->block_dev.lba) { 24356196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) 244ff8fef56SSascha Silbe printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n", 245d2bf29e3SLei Wen start + blkcnt, mmc->block_dev.lba); 24656196826SPaul Burton #endif 247d2bf29e3SLei Wen return 0; 248d2bf29e3SLei Wen } 249272cc70bSAndy Fleming 2504a1a06bcSAlagu Sankar if (mmc_set_blocklen(mmc, mmc->read_bl_len)) 251272cc70bSAndy Fleming return 0; 252272cc70bSAndy Fleming 2534a1a06bcSAlagu Sankar do { 25493bfd616SPantelis Antoniou cur = (blocks_todo > mmc->cfg->b_max) ? 25593bfd616SPantelis Antoniou mmc->cfg->b_max : blocks_todo; 2564a1a06bcSAlagu Sankar if(mmc_read_blocks(mmc, dst, start, cur) != cur) 2574a1a06bcSAlagu Sankar return 0; 2584a1a06bcSAlagu Sankar blocks_todo -= cur; 2594a1a06bcSAlagu Sankar start += cur; 2604a1a06bcSAlagu Sankar dst += cur * mmc->read_bl_len; 2614a1a06bcSAlagu Sankar } while (blocks_todo > 0); 262272cc70bSAndy Fleming 263272cc70bSAndy Fleming return blkcnt; 264272cc70bSAndy Fleming } 265272cc70bSAndy Fleming 266fdbb873eSKim Phillips static int mmc_go_idle(struct mmc *mmc) 267272cc70bSAndy Fleming { 268272cc70bSAndy Fleming struct mmc_cmd cmd; 269272cc70bSAndy Fleming int err; 270272cc70bSAndy Fleming 271272cc70bSAndy Fleming udelay(1000); 272272cc70bSAndy Fleming 273272cc70bSAndy Fleming cmd.cmdidx = MMC_CMD_GO_IDLE_STATE; 274272cc70bSAndy Fleming cmd.cmdarg = 0; 275272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_NONE; 276272cc70bSAndy Fleming 277272cc70bSAndy Fleming err = mmc_send_cmd(mmc, &cmd, NULL); 278272cc70bSAndy Fleming 279272cc70bSAndy Fleming if (err) 280272cc70bSAndy Fleming return err; 281272cc70bSAndy Fleming 282272cc70bSAndy Fleming udelay(2000); 283272cc70bSAndy Fleming 284272cc70bSAndy Fleming return 0; 285272cc70bSAndy Fleming } 286272cc70bSAndy Fleming 287fdbb873eSKim Phillips static int sd_send_op_cond(struct mmc *mmc) 288272cc70bSAndy Fleming { 289272cc70bSAndy Fleming int timeout = 1000; 290272cc70bSAndy Fleming int err; 291272cc70bSAndy Fleming struct mmc_cmd cmd; 292272cc70bSAndy Fleming 293272cc70bSAndy Fleming do { 294272cc70bSAndy Fleming cmd.cmdidx = MMC_CMD_APP_CMD; 295272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_R1; 296272cc70bSAndy Fleming cmd.cmdarg = 0; 297272cc70bSAndy Fleming 298272cc70bSAndy Fleming err = mmc_send_cmd(mmc, &cmd, NULL); 299272cc70bSAndy Fleming 300272cc70bSAndy Fleming if (err) 301272cc70bSAndy Fleming return err; 302272cc70bSAndy Fleming 303272cc70bSAndy Fleming cmd.cmdidx = SD_CMD_APP_SEND_OP_COND; 304272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_R3; 305250de12bSStefano Babic 306250de12bSStefano Babic /* 307250de12bSStefano Babic * Most cards do not answer if some reserved bits 308250de12bSStefano Babic * in the ocr are set. However, Some controller 309250de12bSStefano Babic * can set bit 7 (reserved for low voltages), but 310250de12bSStefano Babic * how to manage low voltages SD card is not yet 311250de12bSStefano Babic * specified. 312250de12bSStefano Babic */ 313d52ebf10SThomas Chou cmd.cmdarg = mmc_host_is_spi(mmc) ? 0 : 31493bfd616SPantelis Antoniou (mmc->cfg->voltages & 0xff8000); 315272cc70bSAndy Fleming 316272cc70bSAndy Fleming if (mmc->version == SD_VERSION_2) 317272cc70bSAndy Fleming cmd.cmdarg |= OCR_HCS; 318272cc70bSAndy Fleming 319272cc70bSAndy Fleming err = mmc_send_cmd(mmc, &cmd, NULL); 320272cc70bSAndy Fleming 321272cc70bSAndy Fleming if (err) 322272cc70bSAndy Fleming return err; 323272cc70bSAndy Fleming 324272cc70bSAndy Fleming udelay(1000); 325272cc70bSAndy Fleming } while ((!(cmd.response[0] & OCR_BUSY)) && timeout--); 326272cc70bSAndy Fleming 327272cc70bSAndy Fleming if (timeout <= 0) 328272cc70bSAndy Fleming return UNUSABLE_ERR; 329272cc70bSAndy Fleming 330272cc70bSAndy Fleming if (mmc->version != SD_VERSION_2) 331272cc70bSAndy Fleming mmc->version = SD_VERSION_1_0; 332272cc70bSAndy Fleming 333d52ebf10SThomas Chou if (mmc_host_is_spi(mmc)) { /* read OCR for spi */ 334d52ebf10SThomas Chou cmd.cmdidx = MMC_CMD_SPI_READ_OCR; 335d52ebf10SThomas Chou cmd.resp_type = MMC_RSP_R3; 336d52ebf10SThomas Chou cmd.cmdarg = 0; 337d52ebf10SThomas Chou 338d52ebf10SThomas Chou err = mmc_send_cmd(mmc, &cmd, NULL); 339d52ebf10SThomas Chou 340d52ebf10SThomas Chou if (err) 341d52ebf10SThomas Chou return err; 342d52ebf10SThomas Chou } 343d52ebf10SThomas Chou 344998be3ddSRabin Vincent mmc->ocr = cmd.response[0]; 345272cc70bSAndy Fleming 346272cc70bSAndy Fleming mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS); 347272cc70bSAndy Fleming mmc->rca = 0; 348272cc70bSAndy Fleming 349272cc70bSAndy Fleming return 0; 350272cc70bSAndy Fleming } 351272cc70bSAndy Fleming 352e9550449SChe-Liang Chiou /* We pass in the cmd since otherwise the init seems to fail */ 353e9550449SChe-Liang Chiou static int mmc_send_op_cond_iter(struct mmc *mmc, struct mmc_cmd *cmd, 354e9550449SChe-Liang Chiou int use_arg) 355272cc70bSAndy Fleming { 356272cc70bSAndy Fleming int err; 357272cc70bSAndy Fleming 358e9550449SChe-Liang Chiou cmd->cmdidx = MMC_CMD_SEND_OP_COND; 359e9550449SChe-Liang Chiou cmd->resp_type = MMC_RSP_R3; 360e9550449SChe-Liang Chiou cmd->cmdarg = 0; 361e9550449SChe-Liang Chiou if (use_arg && !mmc_host_is_spi(mmc)) { 362e9550449SChe-Liang Chiou cmd->cmdarg = 36393bfd616SPantelis Antoniou (mmc->cfg->voltages & 364e9550449SChe-Liang Chiou (mmc->op_cond_response & OCR_VOLTAGE_MASK)) | 365e9550449SChe-Liang Chiou (mmc->op_cond_response & OCR_ACCESS_MODE); 366e9550449SChe-Liang Chiou 36793bfd616SPantelis Antoniou if (mmc->cfg->host_caps & MMC_MODE_HC) 368e9550449SChe-Liang Chiou cmd->cmdarg |= OCR_HCS; 369e9550449SChe-Liang Chiou } 370e9550449SChe-Liang Chiou err = mmc_send_cmd(mmc, cmd, NULL); 371e9550449SChe-Liang Chiou if (err) 372e9550449SChe-Liang Chiou return err; 373e9550449SChe-Liang Chiou mmc->op_cond_response = cmd->response[0]; 374e9550449SChe-Liang Chiou return 0; 375e9550449SChe-Liang Chiou } 376e9550449SChe-Liang Chiou 377e9550449SChe-Liang Chiou int mmc_send_op_cond(struct mmc *mmc) 378e9550449SChe-Liang Chiou { 379e9550449SChe-Liang Chiou struct mmc_cmd cmd; 380e9550449SChe-Liang Chiou int err, i; 381e9550449SChe-Liang Chiou 382272cc70bSAndy Fleming /* Some cards seem to need this */ 383272cc70bSAndy Fleming mmc_go_idle(mmc); 384272cc70bSAndy Fleming 38531cacbabSRaffaele Recalcati /* Asking to the card its capabilities */ 386e9550449SChe-Liang Chiou mmc->op_cond_pending = 1; 387e9550449SChe-Liang Chiou for (i = 0; i < 2; i++) { 388e9550449SChe-Liang Chiou err = mmc_send_op_cond_iter(mmc, &cmd, i != 0); 38931cacbabSRaffaele Recalcati if (err) 39031cacbabSRaffaele Recalcati return err; 39131cacbabSRaffaele Recalcati 392e9550449SChe-Liang Chiou /* exit if not busy (flag seems to be inverted) */ 393e9550449SChe-Liang Chiou if (mmc->op_cond_response & OCR_BUSY) 394e9550449SChe-Liang Chiou return 0; 395e9550449SChe-Liang Chiou } 396e9550449SChe-Liang Chiou return IN_PROGRESS; 397e9550449SChe-Liang Chiou } 39831cacbabSRaffaele Recalcati 399e9550449SChe-Liang Chiou int mmc_complete_op_cond(struct mmc *mmc) 400e9550449SChe-Liang Chiou { 401e9550449SChe-Liang Chiou struct mmc_cmd cmd; 402e9550449SChe-Liang Chiou int timeout = 1000; 403e9550449SChe-Liang Chiou uint start; 404e9550449SChe-Liang Chiou int err; 405e9550449SChe-Liang Chiou 406e9550449SChe-Liang Chiou mmc->op_cond_pending = 0; 407e9550449SChe-Liang Chiou start = get_timer(0); 408272cc70bSAndy Fleming do { 409e9550449SChe-Liang Chiou err = mmc_send_op_cond_iter(mmc, &cmd, 1); 410272cc70bSAndy Fleming if (err) 411272cc70bSAndy Fleming return err; 412e9550449SChe-Liang Chiou if (get_timer(start) > timeout) 413272cc70bSAndy Fleming return UNUSABLE_ERR; 414e9550449SChe-Liang Chiou udelay(100); 415e9550449SChe-Liang Chiou } while (!(mmc->op_cond_response & OCR_BUSY)); 416272cc70bSAndy Fleming 417d52ebf10SThomas Chou if (mmc_host_is_spi(mmc)) { /* read OCR for spi */ 418d52ebf10SThomas Chou cmd.cmdidx = MMC_CMD_SPI_READ_OCR; 419d52ebf10SThomas Chou cmd.resp_type = MMC_RSP_R3; 420d52ebf10SThomas Chou cmd.cmdarg = 0; 421d52ebf10SThomas Chou 422d52ebf10SThomas Chou err = mmc_send_cmd(mmc, &cmd, NULL); 423d52ebf10SThomas Chou 424d52ebf10SThomas Chou if (err) 425d52ebf10SThomas Chou return err; 426d52ebf10SThomas Chou } 427d52ebf10SThomas Chou 428272cc70bSAndy Fleming mmc->version = MMC_VERSION_UNKNOWN; 429998be3ddSRabin Vincent mmc->ocr = cmd.response[0]; 430272cc70bSAndy Fleming 431272cc70bSAndy Fleming mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS); 432def816a2SStephen Warren mmc->rca = 1; 433272cc70bSAndy Fleming 434272cc70bSAndy Fleming return 0; 435272cc70bSAndy Fleming } 436272cc70bSAndy Fleming 437272cc70bSAndy Fleming 438fdbb873eSKim Phillips static int mmc_send_ext_csd(struct mmc *mmc, u8 *ext_csd) 439272cc70bSAndy Fleming { 440272cc70bSAndy Fleming struct mmc_cmd cmd; 441272cc70bSAndy Fleming struct mmc_data data; 442272cc70bSAndy Fleming int err; 443272cc70bSAndy Fleming 444272cc70bSAndy Fleming /* Get the Card Status Register */ 445272cc70bSAndy Fleming cmd.cmdidx = MMC_CMD_SEND_EXT_CSD; 446272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_R1; 447272cc70bSAndy Fleming cmd.cmdarg = 0; 448272cc70bSAndy Fleming 449cdfd1ac6SYoshihiro Shimoda data.dest = (char *)ext_csd; 450272cc70bSAndy Fleming data.blocks = 1; 4518bfa195eSSimon Glass data.blocksize = MMC_MAX_BLOCK_LEN; 452272cc70bSAndy Fleming data.flags = MMC_DATA_READ; 453272cc70bSAndy Fleming 454272cc70bSAndy Fleming err = mmc_send_cmd(mmc, &cmd, &data); 455272cc70bSAndy Fleming 456272cc70bSAndy Fleming return err; 457272cc70bSAndy Fleming } 458272cc70bSAndy Fleming 459272cc70bSAndy Fleming 460fdbb873eSKim Phillips static int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value) 461272cc70bSAndy Fleming { 462272cc70bSAndy Fleming struct mmc_cmd cmd; 4635d4fc8d9SRaffaele Recalcati int timeout = 1000; 4645d4fc8d9SRaffaele Recalcati int ret; 465272cc70bSAndy Fleming 466272cc70bSAndy Fleming cmd.cmdidx = MMC_CMD_SWITCH; 467272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_R1b; 468272cc70bSAndy Fleming cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | 469272cc70bSAndy Fleming (index << 16) | 470272cc70bSAndy Fleming (value << 8); 471272cc70bSAndy Fleming 4725d4fc8d9SRaffaele Recalcati ret = mmc_send_cmd(mmc, &cmd, NULL); 4735d4fc8d9SRaffaele Recalcati 4745d4fc8d9SRaffaele Recalcati /* Waiting for the ready status */ 47593ad0d18SJan Kloetzke if (!ret) 47693ad0d18SJan Kloetzke ret = mmc_send_status(mmc, timeout); 4775d4fc8d9SRaffaele Recalcati 4785d4fc8d9SRaffaele Recalcati return ret; 4795d4fc8d9SRaffaele Recalcati 480272cc70bSAndy Fleming } 481272cc70bSAndy Fleming 482fdbb873eSKim Phillips static int mmc_change_freq(struct mmc *mmc) 483272cc70bSAndy Fleming { 4848bfa195eSSimon Glass ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); 485272cc70bSAndy Fleming char cardtype; 486272cc70bSAndy Fleming int err; 487272cc70bSAndy Fleming 488272cc70bSAndy Fleming mmc->card_caps = 0; 489272cc70bSAndy Fleming 490d52ebf10SThomas Chou if (mmc_host_is_spi(mmc)) 491d52ebf10SThomas Chou return 0; 492d52ebf10SThomas Chou 493272cc70bSAndy Fleming /* Only version 4 supports high-speed */ 494272cc70bSAndy Fleming if (mmc->version < MMC_VERSION_4) 495272cc70bSAndy Fleming return 0; 496272cc70bSAndy Fleming 497272cc70bSAndy Fleming err = mmc_send_ext_csd(mmc, ext_csd); 498272cc70bSAndy Fleming 499272cc70bSAndy Fleming if (err) 500272cc70bSAndy Fleming return err; 501272cc70bSAndy Fleming 5020560db18SLei Wen cardtype = ext_csd[EXT_CSD_CARD_TYPE] & 0xf; 503272cc70bSAndy Fleming 504272cc70bSAndy Fleming err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1); 505272cc70bSAndy Fleming 506272cc70bSAndy Fleming if (err) 507272cc70bSAndy Fleming return err; 508272cc70bSAndy Fleming 509272cc70bSAndy Fleming /* Now check to see that it worked */ 510272cc70bSAndy Fleming err = mmc_send_ext_csd(mmc, ext_csd); 511272cc70bSAndy Fleming 512272cc70bSAndy Fleming if (err) 513272cc70bSAndy Fleming return err; 514272cc70bSAndy Fleming 515272cc70bSAndy Fleming /* No high-speed support */ 5160560db18SLei Wen if (!ext_csd[EXT_CSD_HS_TIMING]) 517272cc70bSAndy Fleming return 0; 518272cc70bSAndy Fleming 519272cc70bSAndy Fleming /* High Speed is set, there are two types: 52MHz and 26MHz */ 520*d22e3d46SJaehoon Chung if (cardtype & EXT_CSD_CARD_TYPE_52) { 521*d22e3d46SJaehoon Chung if (cardtype & EXT_CSD_CARD_TYPE_DDR_52) 522*d22e3d46SJaehoon Chung mmc->card_caps |= MMC_MODE_DDR_52MHz; 523272cc70bSAndy Fleming mmc->card_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; 524*d22e3d46SJaehoon Chung } else { 525272cc70bSAndy Fleming mmc->card_caps |= MMC_MODE_HS; 526*d22e3d46SJaehoon Chung } 527272cc70bSAndy Fleming 528272cc70bSAndy Fleming return 0; 529272cc70bSAndy Fleming } 530272cc70bSAndy Fleming 531f866a46dSStephen Warren static int mmc_set_capacity(struct mmc *mmc, int part_num) 532f866a46dSStephen Warren { 533f866a46dSStephen Warren switch (part_num) { 534f866a46dSStephen Warren case 0: 535f866a46dSStephen Warren mmc->capacity = mmc->capacity_user; 536f866a46dSStephen Warren break; 537f866a46dSStephen Warren case 1: 538f866a46dSStephen Warren case 2: 539f866a46dSStephen Warren mmc->capacity = mmc->capacity_boot; 540f866a46dSStephen Warren break; 541f866a46dSStephen Warren case 3: 542f866a46dSStephen Warren mmc->capacity = mmc->capacity_rpmb; 543f866a46dSStephen Warren break; 544f866a46dSStephen Warren case 4: 545f866a46dSStephen Warren case 5: 546f866a46dSStephen Warren case 6: 547f866a46dSStephen Warren case 7: 548f866a46dSStephen Warren mmc->capacity = mmc->capacity_gp[part_num - 4]; 549f866a46dSStephen Warren break; 550f866a46dSStephen Warren default: 551f866a46dSStephen Warren return -1; 552f866a46dSStephen Warren } 553f866a46dSStephen Warren 554f866a46dSStephen Warren mmc->block_dev.lba = lldiv(mmc->capacity, mmc->read_bl_len); 555f866a46dSStephen Warren 556f866a46dSStephen Warren return 0; 557f866a46dSStephen Warren } 558f866a46dSStephen Warren 559bc897b1dSLei Wen int mmc_switch_part(int dev_num, unsigned int part_num) 560bc897b1dSLei Wen { 561bc897b1dSLei Wen struct mmc *mmc = find_mmc_device(dev_num); 562f866a46dSStephen Warren int ret; 563bc897b1dSLei Wen 564bc897b1dSLei Wen if (!mmc) 565bc897b1dSLei Wen return -1; 566bc897b1dSLei Wen 567f866a46dSStephen Warren ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF, 568bc897b1dSLei Wen (mmc->part_config & ~PART_ACCESS_MASK) 569bc897b1dSLei Wen | (part_num & PART_ACCESS_MASK)); 570f866a46dSStephen Warren if (ret) 571f866a46dSStephen Warren return ret; 572f866a46dSStephen Warren 573f866a46dSStephen Warren return mmc_set_capacity(mmc, part_num); 574bc897b1dSLei Wen } 575bc897b1dSLei Wen 57648972d90SThierry Reding int mmc_getcd(struct mmc *mmc) 57748972d90SThierry Reding { 57848972d90SThierry Reding int cd; 57948972d90SThierry Reding 58048972d90SThierry Reding cd = board_mmc_getcd(mmc); 58148972d90SThierry Reding 582d4e1da4eSPeter Korsgaard if (cd < 0) { 58393bfd616SPantelis Antoniou if (mmc->cfg->ops->getcd) 58493bfd616SPantelis Antoniou cd = mmc->cfg->ops->getcd(mmc); 585d4e1da4eSPeter Korsgaard else 586d4e1da4eSPeter Korsgaard cd = 1; 587d4e1da4eSPeter Korsgaard } 58848972d90SThierry Reding 58948972d90SThierry Reding return cd; 59048972d90SThierry Reding } 59148972d90SThierry Reding 592fdbb873eSKim Phillips static int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp) 593272cc70bSAndy Fleming { 594272cc70bSAndy Fleming struct mmc_cmd cmd; 595272cc70bSAndy Fleming struct mmc_data data; 596272cc70bSAndy Fleming 597272cc70bSAndy Fleming /* Switch the frequency */ 598272cc70bSAndy Fleming cmd.cmdidx = SD_CMD_SWITCH_FUNC; 599272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_R1; 600272cc70bSAndy Fleming cmd.cmdarg = (mode << 31) | 0xffffff; 601272cc70bSAndy Fleming cmd.cmdarg &= ~(0xf << (group * 4)); 602272cc70bSAndy Fleming cmd.cmdarg |= value << (group * 4); 603272cc70bSAndy Fleming 604272cc70bSAndy Fleming data.dest = (char *)resp; 605272cc70bSAndy Fleming data.blocksize = 64; 606272cc70bSAndy Fleming data.blocks = 1; 607272cc70bSAndy Fleming data.flags = MMC_DATA_READ; 608272cc70bSAndy Fleming 609272cc70bSAndy Fleming return mmc_send_cmd(mmc, &cmd, &data); 610272cc70bSAndy Fleming } 611272cc70bSAndy Fleming 612272cc70bSAndy Fleming 613fdbb873eSKim Phillips static int sd_change_freq(struct mmc *mmc) 614272cc70bSAndy Fleming { 615272cc70bSAndy Fleming int err; 616272cc70bSAndy Fleming struct mmc_cmd cmd; 617f781dd38SAnton staaf ALLOC_CACHE_ALIGN_BUFFER(uint, scr, 2); 618f781dd38SAnton staaf ALLOC_CACHE_ALIGN_BUFFER(uint, switch_status, 16); 619272cc70bSAndy Fleming struct mmc_data data; 620272cc70bSAndy Fleming int timeout; 621272cc70bSAndy Fleming 622272cc70bSAndy Fleming mmc->card_caps = 0; 623272cc70bSAndy Fleming 624d52ebf10SThomas Chou if (mmc_host_is_spi(mmc)) 625d52ebf10SThomas Chou return 0; 626d52ebf10SThomas Chou 627272cc70bSAndy Fleming /* Read the SCR to find out if this card supports higher speeds */ 628272cc70bSAndy Fleming cmd.cmdidx = MMC_CMD_APP_CMD; 629272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_R1; 630272cc70bSAndy Fleming cmd.cmdarg = mmc->rca << 16; 631272cc70bSAndy Fleming 632272cc70bSAndy Fleming err = mmc_send_cmd(mmc, &cmd, NULL); 633272cc70bSAndy Fleming 634272cc70bSAndy Fleming if (err) 635272cc70bSAndy Fleming return err; 636272cc70bSAndy Fleming 637272cc70bSAndy Fleming cmd.cmdidx = SD_CMD_APP_SEND_SCR; 638272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_R1; 639272cc70bSAndy Fleming cmd.cmdarg = 0; 640272cc70bSAndy Fleming 641272cc70bSAndy Fleming timeout = 3; 642272cc70bSAndy Fleming 643272cc70bSAndy Fleming retry_scr: 644f781dd38SAnton staaf data.dest = (char *)scr; 645272cc70bSAndy Fleming data.blocksize = 8; 646272cc70bSAndy Fleming data.blocks = 1; 647272cc70bSAndy Fleming data.flags = MMC_DATA_READ; 648272cc70bSAndy Fleming 649272cc70bSAndy Fleming err = mmc_send_cmd(mmc, &cmd, &data); 650272cc70bSAndy Fleming 651272cc70bSAndy Fleming if (err) { 652272cc70bSAndy Fleming if (timeout--) 653272cc70bSAndy Fleming goto retry_scr; 654272cc70bSAndy Fleming 655272cc70bSAndy Fleming return err; 656272cc70bSAndy Fleming } 657272cc70bSAndy Fleming 6584e3d89baSYauhen Kharuzhy mmc->scr[0] = __be32_to_cpu(scr[0]); 6594e3d89baSYauhen Kharuzhy mmc->scr[1] = __be32_to_cpu(scr[1]); 660272cc70bSAndy Fleming 661272cc70bSAndy Fleming switch ((mmc->scr[0] >> 24) & 0xf) { 662272cc70bSAndy Fleming case 0: 663272cc70bSAndy Fleming mmc->version = SD_VERSION_1_0; 664272cc70bSAndy Fleming break; 665272cc70bSAndy Fleming case 1: 666272cc70bSAndy Fleming mmc->version = SD_VERSION_1_10; 667272cc70bSAndy Fleming break; 668272cc70bSAndy Fleming case 2: 669272cc70bSAndy Fleming mmc->version = SD_VERSION_2; 6701741c64dSJaehoon Chung if ((mmc->scr[0] >> 15) & 0x1) 6711741c64dSJaehoon Chung mmc->version = SD_VERSION_3; 672272cc70bSAndy Fleming break; 673272cc70bSAndy Fleming default: 674272cc70bSAndy Fleming mmc->version = SD_VERSION_1_0; 675272cc70bSAndy Fleming break; 676272cc70bSAndy Fleming } 677272cc70bSAndy Fleming 678b44c7083SAlagu Sankar if (mmc->scr[0] & SD_DATA_4BIT) 679b44c7083SAlagu Sankar mmc->card_caps |= MMC_MODE_4BIT; 680b44c7083SAlagu Sankar 681272cc70bSAndy Fleming /* Version 1.0 doesn't support switching */ 682272cc70bSAndy Fleming if (mmc->version == SD_VERSION_1_0) 683272cc70bSAndy Fleming return 0; 684272cc70bSAndy Fleming 685272cc70bSAndy Fleming timeout = 4; 686272cc70bSAndy Fleming while (timeout--) { 687272cc70bSAndy Fleming err = sd_switch(mmc, SD_SWITCH_CHECK, 0, 1, 688f781dd38SAnton staaf (u8 *)switch_status); 689272cc70bSAndy Fleming 690272cc70bSAndy Fleming if (err) 691272cc70bSAndy Fleming return err; 692272cc70bSAndy Fleming 693272cc70bSAndy Fleming /* The high-speed function is busy. Try again */ 6944e3d89baSYauhen Kharuzhy if (!(__be32_to_cpu(switch_status[7]) & SD_HIGHSPEED_BUSY)) 695272cc70bSAndy Fleming break; 696272cc70bSAndy Fleming } 697272cc70bSAndy Fleming 698272cc70bSAndy Fleming /* If high-speed isn't supported, we return */ 6994e3d89baSYauhen Kharuzhy if (!(__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED)) 700272cc70bSAndy Fleming return 0; 701272cc70bSAndy Fleming 7022c3fbf4cSMacpaul Lin /* 7032c3fbf4cSMacpaul Lin * If the host doesn't support SD_HIGHSPEED, do not switch card to 7042c3fbf4cSMacpaul Lin * HIGHSPEED mode even if the card support SD_HIGHSPPED. 7052c3fbf4cSMacpaul Lin * This can avoid furthur problem when the card runs in different 7062c3fbf4cSMacpaul Lin * mode between the host. 7072c3fbf4cSMacpaul Lin */ 70893bfd616SPantelis Antoniou if (!((mmc->cfg->host_caps & MMC_MODE_HS_52MHz) && 70993bfd616SPantelis Antoniou (mmc->cfg->host_caps & MMC_MODE_HS))) 7102c3fbf4cSMacpaul Lin return 0; 7112c3fbf4cSMacpaul Lin 712f781dd38SAnton staaf err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, 1, (u8 *)switch_status); 713272cc70bSAndy Fleming 714272cc70bSAndy Fleming if (err) 715272cc70bSAndy Fleming return err; 716272cc70bSAndy Fleming 7174e3d89baSYauhen Kharuzhy if ((__be32_to_cpu(switch_status[4]) & 0x0f000000) == 0x01000000) 718272cc70bSAndy Fleming mmc->card_caps |= MMC_MODE_HS; 719272cc70bSAndy Fleming 720272cc70bSAndy Fleming return 0; 721272cc70bSAndy Fleming } 722272cc70bSAndy Fleming 723272cc70bSAndy Fleming /* frequency bases */ 724272cc70bSAndy Fleming /* divided by 10 to be nice to platforms without floating point */ 7255f837c2cSMike Frysinger static const int fbase[] = { 726272cc70bSAndy Fleming 10000, 727272cc70bSAndy Fleming 100000, 728272cc70bSAndy Fleming 1000000, 729272cc70bSAndy Fleming 10000000, 730272cc70bSAndy Fleming }; 731272cc70bSAndy Fleming 732272cc70bSAndy Fleming /* Multiplier values for TRAN_SPEED. Multiplied by 10 to be nice 733272cc70bSAndy Fleming * to platforms without floating point. 734272cc70bSAndy Fleming */ 7355f837c2cSMike Frysinger static const int multipliers[] = { 736272cc70bSAndy Fleming 0, /* reserved */ 737272cc70bSAndy Fleming 10, 738272cc70bSAndy Fleming 12, 739272cc70bSAndy Fleming 13, 740272cc70bSAndy Fleming 15, 741272cc70bSAndy Fleming 20, 742272cc70bSAndy Fleming 25, 743272cc70bSAndy Fleming 30, 744272cc70bSAndy Fleming 35, 745272cc70bSAndy Fleming 40, 746272cc70bSAndy Fleming 45, 747272cc70bSAndy Fleming 50, 748272cc70bSAndy Fleming 55, 749272cc70bSAndy Fleming 60, 750272cc70bSAndy Fleming 70, 751272cc70bSAndy Fleming 80, 752272cc70bSAndy Fleming }; 753272cc70bSAndy Fleming 754fdbb873eSKim Phillips static void mmc_set_ios(struct mmc *mmc) 755272cc70bSAndy Fleming { 75693bfd616SPantelis Antoniou if (mmc->cfg->ops->set_ios) 75793bfd616SPantelis Antoniou mmc->cfg->ops->set_ios(mmc); 758272cc70bSAndy Fleming } 759272cc70bSAndy Fleming 760272cc70bSAndy Fleming void mmc_set_clock(struct mmc *mmc, uint clock) 761272cc70bSAndy Fleming { 76293bfd616SPantelis Antoniou if (clock > mmc->cfg->f_max) 76393bfd616SPantelis Antoniou clock = mmc->cfg->f_max; 764272cc70bSAndy Fleming 76593bfd616SPantelis Antoniou if (clock < mmc->cfg->f_min) 76693bfd616SPantelis Antoniou clock = mmc->cfg->f_min; 767272cc70bSAndy Fleming 768272cc70bSAndy Fleming mmc->clock = clock; 769272cc70bSAndy Fleming 770272cc70bSAndy Fleming mmc_set_ios(mmc); 771272cc70bSAndy Fleming } 772272cc70bSAndy Fleming 773fdbb873eSKim Phillips static void mmc_set_bus_width(struct mmc *mmc, uint width) 774272cc70bSAndy Fleming { 775272cc70bSAndy Fleming mmc->bus_width = width; 776272cc70bSAndy Fleming 777272cc70bSAndy Fleming mmc_set_ios(mmc); 778272cc70bSAndy Fleming } 779272cc70bSAndy Fleming 780fdbb873eSKim Phillips static int mmc_startup(struct mmc *mmc) 781272cc70bSAndy Fleming { 782f866a46dSStephen Warren int err, i; 783272cc70bSAndy Fleming uint mult, freq; 784639b7827SYoshihiro Shimoda u64 cmult, csize, capacity; 785272cc70bSAndy Fleming struct mmc_cmd cmd; 7868bfa195eSSimon Glass ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); 7878bfa195eSSimon Glass ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN); 7885d4fc8d9SRaffaele Recalcati int timeout = 1000; 789272cc70bSAndy Fleming 790d52ebf10SThomas Chou #ifdef CONFIG_MMC_SPI_CRC_ON 791d52ebf10SThomas Chou if (mmc_host_is_spi(mmc)) { /* enable CRC check for spi */ 792d52ebf10SThomas Chou cmd.cmdidx = MMC_CMD_SPI_CRC_ON_OFF; 793d52ebf10SThomas Chou cmd.resp_type = MMC_RSP_R1; 794d52ebf10SThomas Chou cmd.cmdarg = 1; 795d52ebf10SThomas Chou err = mmc_send_cmd(mmc, &cmd, NULL); 796d52ebf10SThomas Chou 797d52ebf10SThomas Chou if (err) 798d52ebf10SThomas Chou return err; 799d52ebf10SThomas Chou } 800d52ebf10SThomas Chou #endif 801d52ebf10SThomas Chou 802272cc70bSAndy Fleming /* Put the Card in Identify Mode */ 803d52ebf10SThomas Chou cmd.cmdidx = mmc_host_is_spi(mmc) ? MMC_CMD_SEND_CID : 804d52ebf10SThomas Chou MMC_CMD_ALL_SEND_CID; /* cmd not supported in spi */ 805272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_R2; 806272cc70bSAndy Fleming cmd.cmdarg = 0; 807272cc70bSAndy Fleming 808272cc70bSAndy Fleming err = mmc_send_cmd(mmc, &cmd, NULL); 809272cc70bSAndy Fleming 810272cc70bSAndy Fleming if (err) 811272cc70bSAndy Fleming return err; 812272cc70bSAndy Fleming 813272cc70bSAndy Fleming memcpy(mmc->cid, cmd.response, 16); 814272cc70bSAndy Fleming 815272cc70bSAndy Fleming /* 816272cc70bSAndy Fleming * For MMC cards, set the Relative Address. 817272cc70bSAndy Fleming * For SD cards, get the Relatvie Address. 818272cc70bSAndy Fleming * This also puts the cards into Standby State 819272cc70bSAndy Fleming */ 820d52ebf10SThomas Chou if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */ 821272cc70bSAndy Fleming cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR; 822272cc70bSAndy Fleming cmd.cmdarg = mmc->rca << 16; 823272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_R6; 824272cc70bSAndy Fleming 825272cc70bSAndy Fleming err = mmc_send_cmd(mmc, &cmd, NULL); 826272cc70bSAndy Fleming 827272cc70bSAndy Fleming if (err) 828272cc70bSAndy Fleming return err; 829272cc70bSAndy Fleming 830272cc70bSAndy Fleming if (IS_SD(mmc)) 831998be3ddSRabin Vincent mmc->rca = (cmd.response[0] >> 16) & 0xffff; 832d52ebf10SThomas Chou } 833272cc70bSAndy Fleming 834272cc70bSAndy Fleming /* Get the Card-Specific Data */ 835272cc70bSAndy Fleming cmd.cmdidx = MMC_CMD_SEND_CSD; 836272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_R2; 837272cc70bSAndy Fleming cmd.cmdarg = mmc->rca << 16; 838272cc70bSAndy Fleming 839272cc70bSAndy Fleming err = mmc_send_cmd(mmc, &cmd, NULL); 840272cc70bSAndy Fleming 8415d4fc8d9SRaffaele Recalcati /* Waiting for the ready status */ 8425d4fc8d9SRaffaele Recalcati mmc_send_status(mmc, timeout); 8435d4fc8d9SRaffaele Recalcati 844272cc70bSAndy Fleming if (err) 845272cc70bSAndy Fleming return err; 846272cc70bSAndy Fleming 847998be3ddSRabin Vincent mmc->csd[0] = cmd.response[0]; 848998be3ddSRabin Vincent mmc->csd[1] = cmd.response[1]; 849998be3ddSRabin Vincent mmc->csd[2] = cmd.response[2]; 850998be3ddSRabin Vincent mmc->csd[3] = cmd.response[3]; 851272cc70bSAndy Fleming 852272cc70bSAndy Fleming if (mmc->version == MMC_VERSION_UNKNOWN) { 8530b453ffeSRabin Vincent int version = (cmd.response[0] >> 26) & 0xf; 854272cc70bSAndy Fleming 855272cc70bSAndy Fleming switch (version) { 856272cc70bSAndy Fleming case 0: 857272cc70bSAndy Fleming mmc->version = MMC_VERSION_1_2; 858272cc70bSAndy Fleming break; 859272cc70bSAndy Fleming case 1: 860272cc70bSAndy Fleming mmc->version = MMC_VERSION_1_4; 861272cc70bSAndy Fleming break; 862272cc70bSAndy Fleming case 2: 863272cc70bSAndy Fleming mmc->version = MMC_VERSION_2_2; 864272cc70bSAndy Fleming break; 865272cc70bSAndy Fleming case 3: 866272cc70bSAndy Fleming mmc->version = MMC_VERSION_3; 867272cc70bSAndy Fleming break; 868272cc70bSAndy Fleming case 4: 869272cc70bSAndy Fleming mmc->version = MMC_VERSION_4; 870272cc70bSAndy Fleming break; 871272cc70bSAndy Fleming default: 872272cc70bSAndy Fleming mmc->version = MMC_VERSION_1_2; 873272cc70bSAndy Fleming break; 874272cc70bSAndy Fleming } 875272cc70bSAndy Fleming } 876272cc70bSAndy Fleming 877272cc70bSAndy Fleming /* divide frequency by 10, since the mults are 10x bigger */ 8780b453ffeSRabin Vincent freq = fbase[(cmd.response[0] & 0x7)]; 8790b453ffeSRabin Vincent mult = multipliers[((cmd.response[0] >> 3) & 0xf)]; 880272cc70bSAndy Fleming 881272cc70bSAndy Fleming mmc->tran_speed = freq * mult; 882272cc70bSAndy Fleming 883ab71188cSMarkus Niebel mmc->dsr_imp = ((cmd.response[1] >> 12) & 0x1); 884998be3ddSRabin Vincent mmc->read_bl_len = 1 << ((cmd.response[1] >> 16) & 0xf); 885272cc70bSAndy Fleming 886272cc70bSAndy Fleming if (IS_SD(mmc)) 887272cc70bSAndy Fleming mmc->write_bl_len = mmc->read_bl_len; 888272cc70bSAndy Fleming else 889998be3ddSRabin Vincent mmc->write_bl_len = 1 << ((cmd.response[3] >> 22) & 0xf); 890272cc70bSAndy Fleming 891272cc70bSAndy Fleming if (mmc->high_capacity) { 892272cc70bSAndy Fleming csize = (mmc->csd[1] & 0x3f) << 16 893272cc70bSAndy Fleming | (mmc->csd[2] & 0xffff0000) >> 16; 894272cc70bSAndy Fleming cmult = 8; 895272cc70bSAndy Fleming } else { 896272cc70bSAndy Fleming csize = (mmc->csd[1] & 0x3ff) << 2 897272cc70bSAndy Fleming | (mmc->csd[2] & 0xc0000000) >> 30; 898272cc70bSAndy Fleming cmult = (mmc->csd[2] & 0x00038000) >> 15; 899272cc70bSAndy Fleming } 900272cc70bSAndy Fleming 901f866a46dSStephen Warren mmc->capacity_user = (csize + 1) << (cmult + 2); 902f866a46dSStephen Warren mmc->capacity_user *= mmc->read_bl_len; 903f866a46dSStephen Warren mmc->capacity_boot = 0; 904f866a46dSStephen Warren mmc->capacity_rpmb = 0; 905f866a46dSStephen Warren for (i = 0; i < 4; i++) 906f866a46dSStephen Warren mmc->capacity_gp[i] = 0; 907272cc70bSAndy Fleming 9088bfa195eSSimon Glass if (mmc->read_bl_len > MMC_MAX_BLOCK_LEN) 9098bfa195eSSimon Glass mmc->read_bl_len = MMC_MAX_BLOCK_LEN; 910272cc70bSAndy Fleming 9118bfa195eSSimon Glass if (mmc->write_bl_len > MMC_MAX_BLOCK_LEN) 9128bfa195eSSimon Glass mmc->write_bl_len = MMC_MAX_BLOCK_LEN; 913272cc70bSAndy Fleming 914ab71188cSMarkus Niebel if ((mmc->dsr_imp) && (0xffffffff != mmc->dsr)) { 915ab71188cSMarkus Niebel cmd.cmdidx = MMC_CMD_SET_DSR; 916ab71188cSMarkus Niebel cmd.cmdarg = (mmc->dsr & 0xffff) << 16; 917ab71188cSMarkus Niebel cmd.resp_type = MMC_RSP_NONE; 918ab71188cSMarkus Niebel if (mmc_send_cmd(mmc, &cmd, NULL)) 919ab71188cSMarkus Niebel printf("MMC: SET_DSR failed\n"); 920ab71188cSMarkus Niebel } 921ab71188cSMarkus Niebel 922272cc70bSAndy Fleming /* Select the card, and put it into Transfer Mode */ 923d52ebf10SThomas Chou if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */ 924272cc70bSAndy Fleming cmd.cmdidx = MMC_CMD_SELECT_CARD; 925fe8f7066SAjay Bhargav cmd.resp_type = MMC_RSP_R1; 926272cc70bSAndy Fleming cmd.cmdarg = mmc->rca << 16; 927272cc70bSAndy Fleming err = mmc_send_cmd(mmc, &cmd, NULL); 928272cc70bSAndy Fleming 929272cc70bSAndy Fleming if (err) 930272cc70bSAndy Fleming return err; 931d52ebf10SThomas Chou } 932272cc70bSAndy Fleming 933e6f99a56SLei Wen /* 934e6f99a56SLei Wen * For SD, its erase group is always one sector 935e6f99a56SLei Wen */ 936e6f99a56SLei Wen mmc->erase_grp_size = 1; 937bc897b1dSLei Wen mmc->part_config = MMCPART_NOAVAILABLE; 938d23e2c09SSukumar Ghorai if (!IS_SD(mmc) && (mmc->version >= MMC_VERSION_4)) { 939d23e2c09SSukumar Ghorai /* check ext_csd version and capacity */ 940d23e2c09SSukumar Ghorai err = mmc_send_ext_csd(mmc, ext_csd); 941fdbb873eSKim Phillips if (!err && (ext_csd[EXT_CSD_REV] >= 2)) { 942639b7827SYoshihiro Shimoda /* 943639b7827SYoshihiro Shimoda * According to the JEDEC Standard, the value of 944639b7827SYoshihiro Shimoda * ext_csd's capacity is valid if the value is more 945639b7827SYoshihiro Shimoda * than 2GB 946639b7827SYoshihiro Shimoda */ 9470560db18SLei Wen capacity = ext_csd[EXT_CSD_SEC_CNT] << 0 9480560db18SLei Wen | ext_csd[EXT_CSD_SEC_CNT + 1] << 8 9490560db18SLei Wen | ext_csd[EXT_CSD_SEC_CNT + 2] << 16 9500560db18SLei Wen | ext_csd[EXT_CSD_SEC_CNT + 3] << 24; 9518bfa195eSSimon Glass capacity *= MMC_MAX_BLOCK_LEN; 952b1f1e821SŁukasz Majewski if ((capacity >> 20) > 2 * 1024) 953f866a46dSStephen Warren mmc->capacity_user = capacity; 954d23e2c09SSukumar Ghorai } 955bc897b1dSLei Wen 95664f4a619SJaehoon Chung switch (ext_csd[EXT_CSD_REV]) { 95764f4a619SJaehoon Chung case 1: 95864f4a619SJaehoon Chung mmc->version = MMC_VERSION_4_1; 95964f4a619SJaehoon Chung break; 96064f4a619SJaehoon Chung case 2: 96164f4a619SJaehoon Chung mmc->version = MMC_VERSION_4_2; 96264f4a619SJaehoon Chung break; 96364f4a619SJaehoon Chung case 3: 96464f4a619SJaehoon Chung mmc->version = MMC_VERSION_4_3; 96564f4a619SJaehoon Chung break; 96664f4a619SJaehoon Chung case 5: 96764f4a619SJaehoon Chung mmc->version = MMC_VERSION_4_41; 96864f4a619SJaehoon Chung break; 96964f4a619SJaehoon Chung case 6: 97064f4a619SJaehoon Chung mmc->version = MMC_VERSION_4_5; 97164f4a619SJaehoon Chung break; 97264f4a619SJaehoon Chung } 97364f4a619SJaehoon Chung 974e6f99a56SLei Wen /* 9751937e5aaSOliver Metz * Host needs to enable ERASE_GRP_DEF bit if device is 9761937e5aaSOliver Metz * partitioned. This bit will be lost every time after a reset 9771937e5aaSOliver Metz * or power off. This will affect erase size. 978e6f99a56SLei Wen */ 9791937e5aaSOliver Metz if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) && 9801937e5aaSOliver Metz (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & PART_ENH_ATTRIB)) { 9811937e5aaSOliver Metz err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 9821937e5aaSOliver Metz EXT_CSD_ERASE_GROUP_DEF, 1); 9831937e5aaSOliver Metz 9841937e5aaSOliver Metz if (err) 9851937e5aaSOliver Metz return err; 9861937e5aaSOliver Metz 9871937e5aaSOliver Metz /* Read out group size from ext_csd */ 9880560db18SLei Wen mmc->erase_grp_size = 9898bfa195eSSimon Glass ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 9908bfa195eSSimon Glass MMC_MAX_BLOCK_LEN * 1024; 9918bfa195eSSimon Glass } else { 9921937e5aaSOliver Metz /* Calculate the group size from the csd value. */ 993e6f99a56SLei Wen int erase_gsz, erase_gmul; 994e6f99a56SLei Wen erase_gsz = (mmc->csd[2] & 0x00007c00) >> 10; 995e6f99a56SLei Wen erase_gmul = (mmc->csd[2] & 0x000003e0) >> 5; 996e6f99a56SLei Wen mmc->erase_grp_size = (erase_gsz + 1) 997e6f99a56SLei Wen * (erase_gmul + 1); 998e6f99a56SLei Wen } 999e6f99a56SLei Wen 1000bc897b1dSLei Wen /* store the partition info of emmc */ 10018948ea83SStephen Warren if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) || 10028948ea83SStephen Warren ext_csd[EXT_CSD_BOOT_MULT]) 10030560db18SLei Wen mmc->part_config = ext_csd[EXT_CSD_PART_CONF]; 1004f866a46dSStephen Warren 1005f866a46dSStephen Warren mmc->capacity_boot = ext_csd[EXT_CSD_BOOT_MULT] << 17; 1006f866a46dSStephen Warren 1007f866a46dSStephen Warren mmc->capacity_rpmb = ext_csd[EXT_CSD_RPMB_MULT] << 17; 1008f866a46dSStephen Warren 1009f866a46dSStephen Warren for (i = 0; i < 4; i++) { 1010f866a46dSStephen Warren int idx = EXT_CSD_GP_SIZE_MULT + i * 3; 1011f866a46dSStephen Warren mmc->capacity_gp[i] = (ext_csd[idx + 2] << 16) + 1012f866a46dSStephen Warren (ext_csd[idx + 1] << 8) + ext_csd[idx]; 1013f866a46dSStephen Warren mmc->capacity_gp[i] *= 1014f866a46dSStephen Warren ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; 1015f866a46dSStephen Warren mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; 1016d23e2c09SSukumar Ghorai } 1017f866a46dSStephen Warren } 1018f866a46dSStephen Warren 1019f866a46dSStephen Warren err = mmc_set_capacity(mmc, mmc->part_num); 1020f866a46dSStephen Warren if (err) 1021f866a46dSStephen Warren return err; 1022d23e2c09SSukumar Ghorai 1023272cc70bSAndy Fleming if (IS_SD(mmc)) 1024272cc70bSAndy Fleming err = sd_change_freq(mmc); 1025272cc70bSAndy Fleming else 1026272cc70bSAndy Fleming err = mmc_change_freq(mmc); 1027272cc70bSAndy Fleming 1028272cc70bSAndy Fleming if (err) 1029272cc70bSAndy Fleming return err; 1030272cc70bSAndy Fleming 1031272cc70bSAndy Fleming /* Restrict card's capabilities by what the host can do */ 103293bfd616SPantelis Antoniou mmc->card_caps &= mmc->cfg->host_caps; 1033272cc70bSAndy Fleming 1034272cc70bSAndy Fleming if (IS_SD(mmc)) { 1035272cc70bSAndy Fleming if (mmc->card_caps & MMC_MODE_4BIT) { 1036272cc70bSAndy Fleming cmd.cmdidx = MMC_CMD_APP_CMD; 1037272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_R1; 1038272cc70bSAndy Fleming cmd.cmdarg = mmc->rca << 16; 1039272cc70bSAndy Fleming 1040272cc70bSAndy Fleming err = mmc_send_cmd(mmc, &cmd, NULL); 1041272cc70bSAndy Fleming if (err) 1042272cc70bSAndy Fleming return err; 1043272cc70bSAndy Fleming 1044272cc70bSAndy Fleming cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH; 1045272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_R1; 1046272cc70bSAndy Fleming cmd.cmdarg = 2; 1047272cc70bSAndy Fleming err = mmc_send_cmd(mmc, &cmd, NULL); 1048272cc70bSAndy Fleming if (err) 1049272cc70bSAndy Fleming return err; 1050272cc70bSAndy Fleming 1051272cc70bSAndy Fleming mmc_set_bus_width(mmc, 4); 1052272cc70bSAndy Fleming } 1053272cc70bSAndy Fleming 1054272cc70bSAndy Fleming if (mmc->card_caps & MMC_MODE_HS) 1055ad5fd922SJaehoon Chung mmc->tran_speed = 50000000; 1056272cc70bSAndy Fleming else 1057ad5fd922SJaehoon Chung mmc->tran_speed = 25000000; 1058272cc70bSAndy Fleming } else { 10597798f6dbSAndy Fleming int idx; 10607798f6dbSAndy Fleming 10617798f6dbSAndy Fleming /* An array of possible bus widths in order of preference */ 10627798f6dbSAndy Fleming static unsigned ext_csd_bits[] = { 1063*d22e3d46SJaehoon Chung EXT_CSD_DDR_BUS_WIDTH_8, 1064*d22e3d46SJaehoon Chung EXT_CSD_DDR_BUS_WIDTH_4, 10657798f6dbSAndy Fleming EXT_CSD_BUS_WIDTH_8, 10667798f6dbSAndy Fleming EXT_CSD_BUS_WIDTH_4, 10677798f6dbSAndy Fleming EXT_CSD_BUS_WIDTH_1, 10687798f6dbSAndy Fleming }; 10697798f6dbSAndy Fleming 10707798f6dbSAndy Fleming /* An array to map CSD bus widths to host cap bits */ 10717798f6dbSAndy Fleming static unsigned ext_to_hostcaps[] = { 1072*d22e3d46SJaehoon Chung [EXT_CSD_DDR_BUS_WIDTH_4] = MMC_MODE_DDR_52MHz, 1073*d22e3d46SJaehoon Chung [EXT_CSD_DDR_BUS_WIDTH_8] = MMC_MODE_DDR_52MHz, 10747798f6dbSAndy Fleming [EXT_CSD_BUS_WIDTH_4] = MMC_MODE_4BIT, 10757798f6dbSAndy Fleming [EXT_CSD_BUS_WIDTH_8] = MMC_MODE_8BIT, 10767798f6dbSAndy Fleming }; 10777798f6dbSAndy Fleming 10787798f6dbSAndy Fleming /* An array to map chosen bus width to an integer */ 10797798f6dbSAndy Fleming static unsigned widths[] = { 1080*d22e3d46SJaehoon Chung 8, 4, 8, 4, 1, 10817798f6dbSAndy Fleming }; 10827798f6dbSAndy Fleming 10837798f6dbSAndy Fleming for (idx=0; idx < ARRAY_SIZE(ext_csd_bits); idx++) { 10847798f6dbSAndy Fleming unsigned int extw = ext_csd_bits[idx]; 10857798f6dbSAndy Fleming 10867798f6dbSAndy Fleming /* 10877798f6dbSAndy Fleming * Check to make sure the controller supports 10887798f6dbSAndy Fleming * this bus width, if it's more than 1 10897798f6dbSAndy Fleming */ 10907798f6dbSAndy Fleming if (extw != EXT_CSD_BUS_WIDTH_1 && 109193bfd616SPantelis Antoniou !(mmc->cfg->host_caps & ext_to_hostcaps[extw])) 10927798f6dbSAndy Fleming continue; 10937798f6dbSAndy Fleming 1094272cc70bSAndy Fleming err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, 10957798f6dbSAndy Fleming EXT_CSD_BUS_WIDTH, extw); 1096272cc70bSAndy Fleming 1097272cc70bSAndy Fleming if (err) 10984137894eSLei Wen continue; 1099272cc70bSAndy Fleming 11007798f6dbSAndy Fleming mmc_set_bus_width(mmc, widths[idx]); 1101272cc70bSAndy Fleming 11024137894eSLei Wen err = mmc_send_ext_csd(mmc, test_csd); 11034137894eSLei Wen if (!err && ext_csd[EXT_CSD_PARTITIONING_SUPPORT] \ 11044137894eSLei Wen == test_csd[EXT_CSD_PARTITIONING_SUPPORT] 11054137894eSLei Wen && ext_csd[EXT_CSD_ERASE_GROUP_DEF] \ 11064137894eSLei Wen == test_csd[EXT_CSD_ERASE_GROUP_DEF] \ 11074137894eSLei Wen && ext_csd[EXT_CSD_REV] \ 11084137894eSLei Wen == test_csd[EXT_CSD_REV] 11094137894eSLei Wen && ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] \ 11104137894eSLei Wen == test_csd[EXT_CSD_HC_ERASE_GRP_SIZE] 11114137894eSLei Wen && memcmp(&ext_csd[EXT_CSD_SEC_CNT], \ 11124137894eSLei Wen &test_csd[EXT_CSD_SEC_CNT], 4) == 0) { 1113272cc70bSAndy Fleming 11147798f6dbSAndy Fleming mmc->card_caps |= ext_to_hostcaps[extw]; 11154137894eSLei Wen break; 11164137894eSLei Wen } 1117272cc70bSAndy Fleming } 1118272cc70bSAndy Fleming 1119272cc70bSAndy Fleming if (mmc->card_caps & MMC_MODE_HS) { 1120272cc70bSAndy Fleming if (mmc->card_caps & MMC_MODE_HS_52MHz) 1121ad5fd922SJaehoon Chung mmc->tran_speed = 52000000; 1122272cc70bSAndy Fleming else 1123ad5fd922SJaehoon Chung mmc->tran_speed = 26000000; 1124272cc70bSAndy Fleming } 1125ad5fd922SJaehoon Chung } 1126ad5fd922SJaehoon Chung 1127ad5fd922SJaehoon Chung mmc_set_clock(mmc, mmc->tran_speed); 1128272cc70bSAndy Fleming 1129272cc70bSAndy Fleming /* fill in device description */ 1130272cc70bSAndy Fleming mmc->block_dev.lun = 0; 1131272cc70bSAndy Fleming mmc->block_dev.type = 0; 1132272cc70bSAndy Fleming mmc->block_dev.blksz = mmc->read_bl_len; 11330472fbfdSEgbert Eich mmc->block_dev.log2blksz = LOG2(mmc->block_dev.blksz); 11349b1f942cSRabin Vincent mmc->block_dev.lba = lldiv(mmc->capacity, mmc->read_bl_len); 113556196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) 1136babce5f6STaylor Hutt sprintf(mmc->block_dev.vendor, "Man %06x Snr %04x%04x", 1137babce5f6STaylor Hutt mmc->cid[0] >> 24, (mmc->cid[2] & 0xffff), 1138babce5f6STaylor Hutt (mmc->cid[3] >> 16) & 0xffff); 1139babce5f6STaylor Hutt sprintf(mmc->block_dev.product, "%c%c%c%c%c%c", mmc->cid[0] & 0xff, 11400b453ffeSRabin Vincent (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff, 1141babce5f6STaylor Hutt (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff, 1142babce5f6STaylor Hutt (mmc->cid[2] >> 24) & 0xff); 1143babce5f6STaylor Hutt sprintf(mmc->block_dev.revision, "%d.%d", (mmc->cid[2] >> 20) & 0xf, 1144babce5f6STaylor Hutt (mmc->cid[2] >> 16) & 0xf); 114556196826SPaul Burton #else 114656196826SPaul Burton mmc->block_dev.vendor[0] = 0; 114756196826SPaul Burton mmc->block_dev.product[0] = 0; 114856196826SPaul Burton mmc->block_dev.revision[0] = 0; 114956196826SPaul Burton #endif 1150122efd43SMikhail Kshevetskiy #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBDISK_SUPPORT) 1151272cc70bSAndy Fleming init_part(&mmc->block_dev); 1152122efd43SMikhail Kshevetskiy #endif 1153272cc70bSAndy Fleming 1154272cc70bSAndy Fleming return 0; 1155272cc70bSAndy Fleming } 1156272cc70bSAndy Fleming 1157fdbb873eSKim Phillips static int mmc_send_if_cond(struct mmc *mmc) 1158272cc70bSAndy Fleming { 1159272cc70bSAndy Fleming struct mmc_cmd cmd; 1160272cc70bSAndy Fleming int err; 1161272cc70bSAndy Fleming 1162272cc70bSAndy Fleming cmd.cmdidx = SD_CMD_SEND_IF_COND; 1163272cc70bSAndy Fleming /* We set the bit if the host supports voltages between 2.7 and 3.6 V */ 116493bfd616SPantelis Antoniou cmd.cmdarg = ((mmc->cfg->voltages & 0xff8000) != 0) << 8 | 0xaa; 1165272cc70bSAndy Fleming cmd.resp_type = MMC_RSP_R7; 1166272cc70bSAndy Fleming 1167272cc70bSAndy Fleming err = mmc_send_cmd(mmc, &cmd, NULL); 1168272cc70bSAndy Fleming 1169272cc70bSAndy Fleming if (err) 1170272cc70bSAndy Fleming return err; 1171272cc70bSAndy Fleming 1172998be3ddSRabin Vincent if ((cmd.response[0] & 0xff) != 0xaa) 1173272cc70bSAndy Fleming return UNUSABLE_ERR; 1174272cc70bSAndy Fleming else 1175272cc70bSAndy Fleming mmc->version = SD_VERSION_2; 1176272cc70bSAndy Fleming 1177272cc70bSAndy Fleming return 0; 1178272cc70bSAndy Fleming } 1179272cc70bSAndy Fleming 118093bfd616SPantelis Antoniou /* not used any more */ 118193bfd616SPantelis Antoniou int __deprecated mmc_register(struct mmc *mmc) 1182272cc70bSAndy Fleming { 118393bfd616SPantelis Antoniou #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) 118493bfd616SPantelis Antoniou printf("%s is deprecated! use mmc_create() instead.\n", __func__); 118593bfd616SPantelis Antoniou #endif 118693bfd616SPantelis Antoniou return -1; 118793bfd616SPantelis Antoniou } 118893bfd616SPantelis Antoniou 118993bfd616SPantelis Antoniou struct mmc *mmc_create(const struct mmc_config *cfg, void *priv) 119093bfd616SPantelis Antoniou { 119193bfd616SPantelis Antoniou struct mmc *mmc; 119293bfd616SPantelis Antoniou 119393bfd616SPantelis Antoniou /* quick validation */ 119493bfd616SPantelis Antoniou if (cfg == NULL || cfg->ops == NULL || cfg->ops->send_cmd == NULL || 119593bfd616SPantelis Antoniou cfg->f_min == 0 || cfg->f_max == 0 || cfg->b_max == 0) 119693bfd616SPantelis Antoniou return NULL; 119793bfd616SPantelis Antoniou 119893bfd616SPantelis Antoniou mmc = calloc(1, sizeof(*mmc)); 119993bfd616SPantelis Antoniou if (mmc == NULL) 120093bfd616SPantelis Antoniou return NULL; 120193bfd616SPantelis Antoniou 120293bfd616SPantelis Antoniou mmc->cfg = cfg; 120393bfd616SPantelis Antoniou mmc->priv = priv; 120493bfd616SPantelis Antoniou 120593bfd616SPantelis Antoniou /* the following chunk was mmc_register() */ 120693bfd616SPantelis Antoniou 1207ab71188cSMarkus Niebel /* Setup dsr related values */ 1208ab71188cSMarkus Niebel mmc->dsr_imp = 0; 1209ab71188cSMarkus Niebel mmc->dsr = 0xffffffff; 1210272cc70bSAndy Fleming /* Setup the universal parts of the block interface just once */ 1211272cc70bSAndy Fleming mmc->block_dev.if_type = IF_TYPE_MMC; 1212272cc70bSAndy Fleming mmc->block_dev.dev = cur_dev_num++; 1213272cc70bSAndy Fleming mmc->block_dev.removable = 1; 1214272cc70bSAndy Fleming mmc->block_dev.block_read = mmc_bread; 1215272cc70bSAndy Fleming mmc->block_dev.block_write = mmc_bwrite; 1216e6f99a56SLei Wen mmc->block_dev.block_erase = mmc_berase; 121793bfd616SPantelis Antoniou 121893bfd616SPantelis Antoniou /* setup initial part type */ 121993bfd616SPantelis Antoniou mmc->block_dev.part_type = mmc->cfg->part_type; 1220272cc70bSAndy Fleming 1221272cc70bSAndy Fleming INIT_LIST_HEAD(&mmc->link); 1222272cc70bSAndy Fleming 1223272cc70bSAndy Fleming list_add_tail(&mmc->link, &mmc_devices); 1224272cc70bSAndy Fleming 122593bfd616SPantelis Antoniou return mmc; 122693bfd616SPantelis Antoniou } 122793bfd616SPantelis Antoniou 122893bfd616SPantelis Antoniou void mmc_destroy(struct mmc *mmc) 122993bfd616SPantelis Antoniou { 123093bfd616SPantelis Antoniou /* only freeing memory for now */ 123193bfd616SPantelis Antoniou free(mmc); 1232272cc70bSAndy Fleming } 1233272cc70bSAndy Fleming 1234df3fc526SMatthew McClintock #ifdef CONFIG_PARTITIONS 1235272cc70bSAndy Fleming block_dev_desc_t *mmc_get_dev(int dev) 1236272cc70bSAndy Fleming { 1237272cc70bSAndy Fleming struct mmc *mmc = find_mmc_device(dev); 12386bb4b4bcSBenoît Thébaudeau if (!mmc || mmc_init(mmc)) 123940242bc3SŁukasz Majewski return NULL; 1240272cc70bSAndy Fleming 124140242bc3SŁukasz Majewski return &mmc->block_dev; 1242272cc70bSAndy Fleming } 1243df3fc526SMatthew McClintock #endif 1244272cc70bSAndy Fleming 1245e9550449SChe-Liang Chiou int mmc_start_init(struct mmc *mmc) 1246272cc70bSAndy Fleming { 1247afd5932bSMacpaul Lin int err; 1248272cc70bSAndy Fleming 1249ab769f22SPantelis Antoniou /* we pretend there's no card when init is NULL */ 125093bfd616SPantelis Antoniou if (mmc_getcd(mmc) == 0 || mmc->cfg->ops->init == NULL) { 125148972d90SThierry Reding mmc->has_init = 0; 125256196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) 125348972d90SThierry Reding printf("MMC: no card present\n"); 125456196826SPaul Burton #endif 125548972d90SThierry Reding return NO_CARD_ERR; 125648972d90SThierry Reding } 125748972d90SThierry Reding 1258bc897b1dSLei Wen if (mmc->has_init) 1259bc897b1dSLei Wen return 0; 1260bc897b1dSLei Wen 1261ab769f22SPantelis Antoniou /* made sure it's not NULL earlier */ 126293bfd616SPantelis Antoniou err = mmc->cfg->ops->init(mmc); 1263272cc70bSAndy Fleming 1264272cc70bSAndy Fleming if (err) 1265272cc70bSAndy Fleming return err; 1266272cc70bSAndy Fleming 1267b86b85e2SIlya Yanok mmc_set_bus_width(mmc, 1); 1268b86b85e2SIlya Yanok mmc_set_clock(mmc, 1); 1269b86b85e2SIlya Yanok 1270272cc70bSAndy Fleming /* Reset the Card */ 1271272cc70bSAndy Fleming err = mmc_go_idle(mmc); 1272272cc70bSAndy Fleming 1273272cc70bSAndy Fleming if (err) 1274272cc70bSAndy Fleming return err; 1275272cc70bSAndy Fleming 1276bc897b1dSLei Wen /* The internal partition reset to user partition(0) at every CMD0*/ 1277bc897b1dSLei Wen mmc->part_num = 0; 1278bc897b1dSLei Wen 1279272cc70bSAndy Fleming /* Test for SD version 2 */ 1280272cc70bSAndy Fleming err = mmc_send_if_cond(mmc); 1281272cc70bSAndy Fleming 1282272cc70bSAndy Fleming /* Now try to get the SD card's operating condition */ 1283272cc70bSAndy Fleming err = sd_send_op_cond(mmc); 1284272cc70bSAndy Fleming 1285272cc70bSAndy Fleming /* If the command timed out, we check for an MMC card */ 1286272cc70bSAndy Fleming if (err == TIMEOUT) { 1287272cc70bSAndy Fleming err = mmc_send_op_cond(mmc); 1288272cc70bSAndy Fleming 1289e9550449SChe-Liang Chiou if (err && err != IN_PROGRESS) { 129056196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) 1291272cc70bSAndy Fleming printf("Card did not respond to voltage select!\n"); 129256196826SPaul Burton #endif 1293272cc70bSAndy Fleming return UNUSABLE_ERR; 1294272cc70bSAndy Fleming } 1295272cc70bSAndy Fleming } 1296272cc70bSAndy Fleming 1297e9550449SChe-Liang Chiou if (err == IN_PROGRESS) 1298e9550449SChe-Liang Chiou mmc->init_in_progress = 1; 1299e9550449SChe-Liang Chiou 1300e9550449SChe-Liang Chiou return err; 1301e9550449SChe-Liang Chiou } 1302e9550449SChe-Liang Chiou 1303e9550449SChe-Liang Chiou static int mmc_complete_init(struct mmc *mmc) 1304e9550449SChe-Liang Chiou { 1305e9550449SChe-Liang Chiou int err = 0; 1306e9550449SChe-Liang Chiou 1307e9550449SChe-Liang Chiou if (mmc->op_cond_pending) 1308e9550449SChe-Liang Chiou err = mmc_complete_op_cond(mmc); 1309e9550449SChe-Liang Chiou 1310e9550449SChe-Liang Chiou if (!err) 1311bc897b1dSLei Wen err = mmc_startup(mmc); 1312bc897b1dSLei Wen if (err) 1313bc897b1dSLei Wen mmc->has_init = 0; 1314bc897b1dSLei Wen else 1315bc897b1dSLei Wen mmc->has_init = 1; 1316e9550449SChe-Liang Chiou mmc->init_in_progress = 0; 1317e9550449SChe-Liang Chiou return err; 1318e9550449SChe-Liang Chiou } 1319e9550449SChe-Liang Chiou 1320e9550449SChe-Liang Chiou int mmc_init(struct mmc *mmc) 1321e9550449SChe-Liang Chiou { 1322e9550449SChe-Liang Chiou int err = IN_PROGRESS; 1323e9550449SChe-Liang Chiou unsigned start = get_timer(0); 1324e9550449SChe-Liang Chiou 1325e9550449SChe-Liang Chiou if (mmc->has_init) 1326e9550449SChe-Liang Chiou return 0; 1327e9550449SChe-Liang Chiou if (!mmc->init_in_progress) 1328e9550449SChe-Liang Chiou err = mmc_start_init(mmc); 1329e9550449SChe-Liang Chiou 1330e9550449SChe-Liang Chiou if (!err || err == IN_PROGRESS) 1331e9550449SChe-Liang Chiou err = mmc_complete_init(mmc); 1332e9550449SChe-Liang Chiou debug("%s: %d, time %lu\n", __func__, err, get_timer(start)); 1333bc897b1dSLei Wen return err; 1334272cc70bSAndy Fleming } 1335272cc70bSAndy Fleming 1336ab71188cSMarkus Niebel int mmc_set_dsr(struct mmc *mmc, u16 val) 1337ab71188cSMarkus Niebel { 1338ab71188cSMarkus Niebel mmc->dsr = val; 1339ab71188cSMarkus Niebel return 0; 1340ab71188cSMarkus Niebel } 1341ab71188cSMarkus Niebel 1342272cc70bSAndy Fleming /* 1343272cc70bSAndy Fleming * CPU and board-specific MMC initializations. Aliased function 1344272cc70bSAndy Fleming * signals caller to move on 1345272cc70bSAndy Fleming */ 1346272cc70bSAndy Fleming static int __def_mmc_init(bd_t *bis) 1347272cc70bSAndy Fleming { 1348272cc70bSAndy Fleming return -1; 1349272cc70bSAndy Fleming } 1350272cc70bSAndy Fleming 1351f9a109b3SPeter Tyser int cpu_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init"))); 1352f9a109b3SPeter Tyser int board_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init"))); 1353272cc70bSAndy Fleming 135456196826SPaul Burton #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) 135556196826SPaul Burton 1356272cc70bSAndy Fleming void print_mmc_devices(char separator) 1357272cc70bSAndy Fleming { 1358272cc70bSAndy Fleming struct mmc *m; 1359272cc70bSAndy Fleming struct list_head *entry; 1360272cc70bSAndy Fleming 1361272cc70bSAndy Fleming list_for_each(entry, &mmc_devices) { 1362272cc70bSAndy Fleming m = list_entry(entry, struct mmc, link); 1363272cc70bSAndy Fleming 136493bfd616SPantelis Antoniou printf("%s: %d", m->cfg->name, m->block_dev.dev); 1365272cc70bSAndy Fleming 1366272cc70bSAndy Fleming if (entry->next != &mmc_devices) 1367272cc70bSAndy Fleming printf("%c ", separator); 1368272cc70bSAndy Fleming } 1369272cc70bSAndy Fleming 1370272cc70bSAndy Fleming printf("\n"); 1371272cc70bSAndy Fleming } 1372272cc70bSAndy Fleming 137356196826SPaul Burton #else 137456196826SPaul Burton void print_mmc_devices(char separator) { } 137556196826SPaul Burton #endif 137656196826SPaul Burton 1377ea6ebe21SLei Wen int get_mmc_num(void) 1378ea6ebe21SLei Wen { 1379ea6ebe21SLei Wen return cur_dev_num; 1380ea6ebe21SLei Wen } 1381ea6ebe21SLei Wen 1382e9550449SChe-Liang Chiou void mmc_set_preinit(struct mmc *mmc, int preinit) 1383e9550449SChe-Liang Chiou { 1384e9550449SChe-Liang Chiou mmc->preinit = preinit; 1385e9550449SChe-Liang Chiou } 1386e9550449SChe-Liang Chiou 1387e9550449SChe-Liang Chiou static void do_preinit(void) 1388e9550449SChe-Liang Chiou { 1389e9550449SChe-Liang Chiou struct mmc *m; 1390e9550449SChe-Liang Chiou struct list_head *entry; 1391e9550449SChe-Liang Chiou 1392e9550449SChe-Liang Chiou list_for_each(entry, &mmc_devices) { 1393e9550449SChe-Liang Chiou m = list_entry(entry, struct mmc, link); 1394e9550449SChe-Liang Chiou 1395e9550449SChe-Liang Chiou if (m->preinit) 1396e9550449SChe-Liang Chiou mmc_start_init(m); 1397e9550449SChe-Liang Chiou } 1398e9550449SChe-Liang Chiou } 1399e9550449SChe-Liang Chiou 1400e9550449SChe-Liang Chiou 1401272cc70bSAndy Fleming int mmc_initialize(bd_t *bis) 1402272cc70bSAndy Fleming { 1403272cc70bSAndy Fleming INIT_LIST_HEAD (&mmc_devices); 1404272cc70bSAndy Fleming cur_dev_num = 0; 1405272cc70bSAndy Fleming 1406272cc70bSAndy Fleming if (board_mmc_init(bis) < 0) 1407272cc70bSAndy Fleming cpu_mmc_init(bis); 1408272cc70bSAndy Fleming 1409bb0dc108SYing Zhang #ifndef CONFIG_SPL_BUILD 1410272cc70bSAndy Fleming print_mmc_devices(','); 1411bb0dc108SYing Zhang #endif 1412272cc70bSAndy Fleming 1413e9550449SChe-Liang Chiou do_preinit(); 1414272cc70bSAndy Fleming return 0; 1415272cc70bSAndy Fleming } 14163690d6d6SAmar 14173690d6d6SAmar #ifdef CONFIG_SUPPORT_EMMC_BOOT 14183690d6d6SAmar /* 14193690d6d6SAmar * This function changes the size of boot partition and the size of rpmb 14203690d6d6SAmar * partition present on EMMC devices. 14213690d6d6SAmar * 14223690d6d6SAmar * Input Parameters: 14233690d6d6SAmar * struct *mmc: pointer for the mmc device strcuture 14243690d6d6SAmar * bootsize: size of boot partition 14253690d6d6SAmar * rpmbsize: size of rpmb partition 14263690d6d6SAmar * 14273690d6d6SAmar * Returns 0 on success. 14283690d6d6SAmar */ 14293690d6d6SAmar 14303690d6d6SAmar int mmc_boot_partition_size_change(struct mmc *mmc, unsigned long bootsize, 14313690d6d6SAmar unsigned long rpmbsize) 14323690d6d6SAmar { 14333690d6d6SAmar int err; 14343690d6d6SAmar struct mmc_cmd cmd; 14353690d6d6SAmar 14363690d6d6SAmar /* Only use this command for raw EMMC moviNAND. Enter backdoor mode */ 14373690d6d6SAmar cmd.cmdidx = MMC_CMD_RES_MAN; 14383690d6d6SAmar cmd.resp_type = MMC_RSP_R1b; 14393690d6d6SAmar cmd.cmdarg = MMC_CMD62_ARG1; 14403690d6d6SAmar 14413690d6d6SAmar err = mmc_send_cmd(mmc, &cmd, NULL); 14423690d6d6SAmar if (err) { 14433690d6d6SAmar debug("mmc_boot_partition_size_change: Error1 = %d\n", err); 14443690d6d6SAmar return err; 14453690d6d6SAmar } 14463690d6d6SAmar 14473690d6d6SAmar /* Boot partition changing mode */ 14483690d6d6SAmar cmd.cmdidx = MMC_CMD_RES_MAN; 14493690d6d6SAmar cmd.resp_type = MMC_RSP_R1b; 14503690d6d6SAmar cmd.cmdarg = MMC_CMD62_ARG2; 14513690d6d6SAmar 14523690d6d6SAmar err = mmc_send_cmd(mmc, &cmd, NULL); 14533690d6d6SAmar if (err) { 14543690d6d6SAmar debug("mmc_boot_partition_size_change: Error2 = %d\n", err); 14553690d6d6SAmar return err; 14563690d6d6SAmar } 14573690d6d6SAmar /* boot partition size is multiple of 128KB */ 14583690d6d6SAmar bootsize = (bootsize * 1024) / 128; 14593690d6d6SAmar 14603690d6d6SAmar /* Arg: boot partition size */ 14613690d6d6SAmar cmd.cmdidx = MMC_CMD_RES_MAN; 14623690d6d6SAmar cmd.resp_type = MMC_RSP_R1b; 14633690d6d6SAmar cmd.cmdarg = bootsize; 14643690d6d6SAmar 14653690d6d6SAmar err = mmc_send_cmd(mmc, &cmd, NULL); 14663690d6d6SAmar if (err) { 14673690d6d6SAmar debug("mmc_boot_partition_size_change: Error3 = %d\n", err); 14683690d6d6SAmar return err; 14693690d6d6SAmar } 14703690d6d6SAmar /* RPMB partition size is multiple of 128KB */ 14713690d6d6SAmar rpmbsize = (rpmbsize * 1024) / 128; 14723690d6d6SAmar /* Arg: RPMB partition size */ 14733690d6d6SAmar cmd.cmdidx = MMC_CMD_RES_MAN; 14743690d6d6SAmar cmd.resp_type = MMC_RSP_R1b; 14753690d6d6SAmar cmd.cmdarg = rpmbsize; 14763690d6d6SAmar 14773690d6d6SAmar err = mmc_send_cmd(mmc, &cmd, NULL); 14783690d6d6SAmar if (err) { 14793690d6d6SAmar debug("mmc_boot_partition_size_change: Error4 = %d\n", err); 14803690d6d6SAmar return err; 14813690d6d6SAmar } 14823690d6d6SAmar return 0; 14833690d6d6SAmar } 14843690d6d6SAmar 14853690d6d6SAmar /* 14865a99b9deSTom Rini * Modify EXT_CSD[177] which is BOOT_BUS_WIDTH 14875a99b9deSTom Rini * based on the passed in values for BOOT_BUS_WIDTH, RESET_BOOT_BUS_WIDTH 14885a99b9deSTom Rini * and BOOT_MODE. 14895a99b9deSTom Rini * 14905a99b9deSTom Rini * Returns 0 on success. 14915a99b9deSTom Rini */ 14925a99b9deSTom Rini int mmc_set_boot_bus_width(struct mmc *mmc, u8 width, u8 reset, u8 mode) 14935a99b9deSTom Rini { 14945a99b9deSTom Rini int err; 14955a99b9deSTom Rini 14965a99b9deSTom Rini err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BOOT_BUS_WIDTH, 14975a99b9deSTom Rini EXT_CSD_BOOT_BUS_WIDTH_MODE(mode) | 14985a99b9deSTom Rini EXT_CSD_BOOT_BUS_WIDTH_RESET(reset) | 14995a99b9deSTom Rini EXT_CSD_BOOT_BUS_WIDTH_WIDTH(width)); 15005a99b9deSTom Rini 15015a99b9deSTom Rini if (err) 15025a99b9deSTom Rini return err; 15035a99b9deSTom Rini return 0; 15045a99b9deSTom Rini } 15055a99b9deSTom Rini 15065a99b9deSTom Rini /* 1507792970b0STom Rini * Modify EXT_CSD[179] which is PARTITION_CONFIG (formerly BOOT_CONFIG) 1508792970b0STom Rini * based on the passed in values for BOOT_ACK, BOOT_PARTITION_ENABLE and 1509792970b0STom Rini * PARTITION_ACCESS. 1510792970b0STom Rini * 1511792970b0STom Rini * Returns 0 on success. 1512792970b0STom Rini */ 1513792970b0STom Rini int mmc_set_part_conf(struct mmc *mmc, u8 ack, u8 part_num, u8 access) 1514792970b0STom Rini { 1515792970b0STom Rini int err; 1516792970b0STom Rini 1517792970b0STom Rini err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF, 1518792970b0STom Rini EXT_CSD_BOOT_ACK(ack) | 1519792970b0STom Rini EXT_CSD_BOOT_PART_NUM(part_num) | 1520792970b0STom Rini EXT_CSD_PARTITION_ACCESS(access)); 1521792970b0STom Rini 1522792970b0STom Rini if (err) 1523792970b0STom Rini return err; 1524792970b0STom Rini return 0; 1525792970b0STom Rini } 152633ace362STom Rini 152733ace362STom Rini /* 152833ace362STom Rini * Modify EXT_CSD[162] which is RST_n_FUNCTION based on the given value 152933ace362STom Rini * for enable. Note that this is a write-once field for non-zero values. 153033ace362STom Rini * 153133ace362STom Rini * Returns 0 on success. 153233ace362STom Rini */ 153333ace362STom Rini int mmc_set_rst_n_function(struct mmc *mmc, u8 enable) 153433ace362STom Rini { 153533ace362STom Rini return mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_RST_N_FUNCTION, 153633ace362STom Rini enable); 153733ace362STom Rini } 15383690d6d6SAmar #endif 1539