aboutsummaryrefslogtreecommitdiff
path: root/src/commonlib/storage/sd_mmc.c
diff options
context:
space:
mode:
authorLee Leahy <leroy.p.leahy@intel.com>2017-05-08 16:56:03 -0700
committerLee Leahy <leroy.p.leahy@intel.com>2017-05-12 18:20:33 +0200
commit48dbc663d75e6b7e45e50cd099acb88b35e65a0a (patch)
treef60c19148b8be447c5350f939ac9d832e379579c /src/commonlib/storage/sd_mmc.c
parentf542aca0908ead68314a6d9603dde8849abcff19 (diff)
commonlib: Move drivers/storage into commonlib/storage
Move drivers/storage into commonlib/storage to enable access by libpayload and indirectly by payloads. * Remove SD/MMC specific include files from include/device * Remove files from drivers/storage * Add SD/MMC specific include files to commonlib/include * Add files to commonlib/storage * Fix header file references * Add subdir entry in commonlib/Makefile.inc to build the SD/MMC driver * Add Kconfig source for commonlib/storage * Rename *DEVICE* to *COMMONLIB* * Rename *DRIVERS_STORAGE* to *COMMONLIB_STORAGE* TEST=Build and run on Galileo Gen2 Change-Id: I4339e4378491db9a0da1f2dc34e1906a5ba31ad6 Signed-off-by: Lee Leahy <Leroy.P.Leahy@intel.com> Reviewed-on: https://review.coreboot.org/19672 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Patrick Georgi <pgeorgi@google.com>
Diffstat (limited to 'src/commonlib/storage/sd_mmc.c')
-rw-r--r--src/commonlib/storage/sd_mmc.c270
1 files changed, 270 insertions, 0 deletions
diff --git a/src/commonlib/storage/sd_mmc.c b/src/commonlib/storage/sd_mmc.c
new file mode 100644
index 0000000000..0ca53b7d20
--- /dev/null
+++ b/src/commonlib/storage/sd_mmc.c
@@ -0,0 +1,270 @@
+/*
+ * Copyright 2008, Freescale Semiconductor, Inc
+ * Andy Fleming
+ *
+ * Copyright 2013 Google Inc. All rights reserved.
+ * Copyright 2017 Intel Corporation
+ *
+ * MultiMediaCard (MMC), eMMC and Secure Digital (SD) common initialization
+ * code which brings the card into the standby state. This code is controller
+ * independent.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <assert.h>
+#include <commonlib/storage.h>
+#include <delay.h>
+#include <endian.h>
+#include "mmc.h"
+#include "sd_mmc.h"
+#include "storage.h"
+#include <string.h>
+#include <timer.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;
+}
+
+static 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 (IS_ENABLED(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 (IS_ENABLED(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 Relatvie 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;
+}