| /* SPDX-License-Identifier: GPL-2.0-or-later */ |
| /* |
| * MultiMediaCard (MMC), eMMC and Secure Digital (SD) common initialization |
| * code which brings the card into the standby state. This code is controller |
| * independent. |
| */ |
| |
| #include <commonlib/storage.h> |
| #include <delay.h> |
| #include <endian.h> |
| #include <string.h> |
| |
| #include "mmc.h" |
| #include "sd_mmc.h" |
| #include "storage.h" |
| |
| uint64_t sd_mmc_extract_uint32_bits(const uint32_t *array, int start, int count) |
| { |
| int i; |
| uint64_t value = 0; |
| |
| for (i = 0; i < count; i++, start++) { |
| value <<= 1; |
| value |= (array[start / 32] >> (31 - (start % 32))) & 0x1; |
| } |
| return value; |
| } |
| |
| static uint32_t sd_mmc_calculate_transfer_speed(uint32_t csd0) |
| { |
| uint32_t mult, freq; |
| |
| /* frequency bases, divided by 10 to be nice to platforms without |
| * floating point */ |
| static const int fbase[] = { |
| 10000, |
| 100000, |
| 1000000, |
| 10000000, |
| }; |
| /* Multiplier values for TRAN_SPEED. Multiplied by 10 to be nice |
| * to platforms without floating point. */ |
| static const int multipliers[] = { |
| 0, // reserved |
| 10, |
| 12, |
| 13, |
| 15, |
| 20, |
| 25, |
| 30, |
| 35, |
| 40, |
| 45, |
| 50, |
| 55, |
| 60, |
| 70, |
| 80, |
| }; |
| |
| /* divide frequency by 10, since the mults are 10x bigger */ |
| freq = fbase[csd0 & 0x7]; |
| mult = multipliers[(csd0 >> 3) & 0xf]; |
| return freq * mult; |
| } |
| |
| int sd_mmc_go_idle(struct storage_media *media) |
| { |
| struct sd_mmc_ctrlr *ctrlr = media->ctrlr; |
| |
| // Some cards can't accept idle commands without delay. |
| if (ctrlr->mdelay_before_cmd0) |
| mdelay(ctrlr->mdelay_before_cmd0); |
| |
| struct mmc_command cmd; |
| cmd.cmdidx = MMC_CMD_GO_IDLE_STATE; |
| cmd.cmdarg = 0; |
| cmd.resp_type = CARD_RSP_NONE; |
| cmd.flags = 0; |
| |
| int err = ctrlr->send_cmd(ctrlr, &cmd, NULL); |
| if (err) |
| return err; |
| |
| // Some cards need more than half second to respond to next command (ex, |
| // SEND_OP_COND). |
| if (ctrlr->mdelay_after_cmd0) |
| mdelay(ctrlr->mdelay_after_cmd0); |
| |
| return 0; |
| } |
| |
| int sd_mmc_send_status(struct storage_media *media, ssize_t tries) |
| { |
| struct mmc_command cmd; |
| struct sd_mmc_ctrlr *ctrlr = media->ctrlr; |
| |
| cmd.cmdidx = MMC_CMD_SEND_STATUS; |
| cmd.resp_type = CARD_RSP_R1; |
| cmd.cmdarg = media->rca << 16; |
| cmd.flags = 0; |
| |
| while (tries--) { |
| int err = ctrlr->send_cmd(ctrlr, &cmd, NULL); |
| if (err) |
| return err; |
| else if (cmd.response[0] & MMC_STATUS_RDY_FOR_DATA) |
| break; |
| else if (cmd.response[0] & MMC_STATUS_MASK) { |
| sd_mmc_error("Status Error: %#8.8x\n", cmd.response[0]); |
| return CARD_COMM_ERR; |
| } |
| |
| udelay(100); |
| } |
| |
| sd_mmc_trace("CURR STATE:%d\n", |
| (cmd.response[0] & MMC_STATUS_CURR_STATE) >> 9); |
| |
| if (tries < 0) { |
| sd_mmc_error("Timeout waiting card ready\n"); |
| return CARD_TIMEOUT; |
| } |
| return 0; |
| } |
| |
| int sd_mmc_set_blocklen(struct sd_mmc_ctrlr *ctrlr, int len) |
| { |
| struct mmc_command cmd; |
| cmd.cmdidx = MMC_CMD_SET_BLOCKLEN; |
| cmd.resp_type = CARD_RSP_R1; |
| cmd.cmdarg = len; |
| cmd.flags = 0; |
| |
| return ctrlr->send_cmd(ctrlr, &cmd, NULL); |
| } |
| |
| int sd_mmc_enter_standby(struct storage_media *media) |
| { |
| struct mmc_command cmd; |
| struct sd_mmc_ctrlr *ctrlr = media->ctrlr; |
| int err; |
| |
| SET_BUS_WIDTH(ctrlr, 1); |
| SET_CLOCK(ctrlr, 1); |
| |
| /* Reset the Card */ |
| err = sd_mmc_go_idle(media); |
| if (err) |
| return err; |
| |
| /* Test for SD version 2 */ |
| err = CARD_TIMEOUT; |
| if (CONFIG(COMMONLIB_STORAGE_SD)) { |
| err = sd_send_if_cond(media); |
| |
| /* Get SD card operating condition */ |
| if (!err) |
| err = sd_send_op_cond(media); |
| } |
| |
| /* If the command timed out, we check for an MMC card */ |
| if (CONFIG(COMMONLIB_STORAGE_MMC) && (err == CARD_TIMEOUT)) { |
| /* Some cards seem to need this */ |
| sd_mmc_go_idle(media); |
| |
| err = mmc_send_op_cond(media); |
| if (err == CARD_IN_PROGRESS) |
| err = mmc_complete_op_cond(media); |
| } |
| |
| if (err) { |
| sd_mmc_error( |
| "Card did not respond to voltage select!\n"); |
| return CARD_UNUSABLE_ERR; |
| } |
| |
| /* Put the Card in Identify Mode */ |
| cmd.cmdidx = MMC_CMD_ALL_SEND_CID; |
| cmd.resp_type = CARD_RSP_R2; |
| cmd.cmdarg = 0; |
| cmd.flags = 0; |
| err = ctrlr->send_cmd(ctrlr, &cmd, NULL); |
| if (err) |
| return err; |
| memcpy(media->cid, cmd.response, sizeof(media->cid)); |
| |
| /* |
| * For MMC cards, set the Relative Address. |
| * For SD cards, get the Relative Address. |
| * This also puts the cards into Standby State |
| */ |
| cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR; |
| cmd.cmdarg = media->rca << 16; |
| cmd.resp_type = CARD_RSP_R6; |
| cmd.flags = 0; |
| err = ctrlr->send_cmd(ctrlr, &cmd, NULL); |
| if (err) |
| return err; |
| if (IS_SD(media)) |
| media->rca = (cmd.response[0] >> 16) & 0xffff; |
| |
| /* Get the Card-Specific Data */ |
| cmd.cmdidx = MMC_CMD_SEND_CSD; |
| cmd.resp_type = CARD_RSP_R2; |
| cmd.cmdarg = media->rca << 16; |
| cmd.flags = 0; |
| err = ctrlr->send_cmd(ctrlr, &cmd, NULL); |
| |
| /* Waiting for the ready status */ |
| sd_mmc_send_status(media, SD_MMC_IO_RETRIES); |
| if (err) |
| return err; |
| |
| memcpy(media->csd, cmd.response, sizeof(media->csd)); |
| if (media->version == MMC_VERSION_UNKNOWN) { |
| int version = sd_mmc_extract_uint32_bits(media->csd, 2, 4); |
| switch (version) { |
| case 0: |
| media->version = MMC_VERSION_1_2; |
| break; |
| case 1: |
| media->version = MMC_VERSION_1_4; |
| break; |
| case 2: |
| media->version = MMC_VERSION_2_2; |
| break; |
| case 3: |
| media->version = MMC_VERSION_3; |
| break; |
| case 4: |
| media->version = MMC_VERSION_4; |
| break; |
| default: |
| media->version = MMC_VERSION_1_2; |
| break; |
| } |
| } |
| media->tran_speed = sd_mmc_calculate_transfer_speed(media->csd[0]); |
| |
| /* Determine the read and write block lengths */ |
| media->read_bl_len = 1 << sd_mmc_extract_uint32_bits(media->csd, 44, 4); |
| if (IS_SD(media)) |
| media->write_bl_len = media->read_bl_len; |
| else |
| media->write_bl_len = |
| 1 << sd_mmc_extract_uint32_bits(media->csd, 102, 4); |
| |
| sd_mmc_debug("mmc media info: version=%#x, tran_speed=%d\n", |
| media->version, (int)media->tran_speed); |
| |
| return 0; |
| } |