summaryrefslogtreecommitdiff
path: root/src/drivers/storage/sd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers/storage/sd.c')
-rw-r--r--src/drivers/storage/sd.c289
1 files changed, 289 insertions, 0 deletions
diff --git a/src/drivers/storage/sd.c b/src/drivers/storage/sd.c
new file mode 100644
index 0000000000..18d2c0e245
--- /dev/null
+++ b/src/drivers/storage/sd.c
@@ -0,0 +1,289 @@
+/*
+ * Copyright 2008, Freescale Semiconductor, Inc
+ * Andy Fleming
+ *
+ * Copyright 2013 Google Inc. All rights reserved.
+ * Copyright 2017 Intel Corporation
+ *
+ * Secure Digital (SD) card specific support code
+ * 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 <delay.h>
+#include <device/sd_mmc_ctrlr.h>
+#include <device/storage.h>
+#include <endian.h>
+#include "sd_mmc.h"
+#include "storage.h"
+#include <string.h>
+#include <timer.h>
+
+int sd_send_if_cond(struct storage_media *media)
+{
+ struct mmc_command cmd;
+ struct sd_mmc_ctrlr *ctrlr = media->ctrlr;
+
+ cmd.cmdidx = SD_CMD_SEND_IF_COND;
+ /* Set if controller supports voltages between 2.7 and 3.6 V. */
+ cmd.cmdarg = ((ctrlr->voltages & 0xff8000) != 0) << 8 | 0xaa;
+ cmd.resp_type = CARD_RSP_R7;
+ cmd.flags = 0;
+ int err = ctrlr->send_cmd(ctrlr, &cmd, NULL);
+ if (err)
+ return err;
+
+ if ((cmd.response[0] & 0xff) != 0xaa)
+ return CARD_UNUSABLE_ERR;
+ media->version = SD_VERSION_2;
+ return 0;
+}
+
+int sd_send_op_cond(struct storage_media *media)
+{
+ int err;
+ struct mmc_command cmd;
+ struct sd_mmc_ctrlr *ctrlr = media->ctrlr;
+
+ int tries = SD_MMC_IO_RETRIES;
+ while (tries--) {
+ cmd.cmdidx = MMC_CMD_APP_CMD;
+ cmd.resp_type = CARD_RSP_R1;
+ cmd.cmdarg = 0;
+ cmd.flags = 0;
+
+ err = ctrlr->send_cmd(ctrlr, &cmd, NULL);
+ if (err)
+ return err;
+
+ cmd.cmdidx = SD_CMD_APP_SEND_OP_COND;
+ cmd.resp_type = CARD_RSP_R3;
+
+ /*
+ * Most cards do not answer if some reserved bits
+ * in the ocr are set. However, Some controller
+ * can set bit 7 (reserved for low voltages), but
+ * how to manage low voltages SD card is not yet
+ * specified.
+ */
+ cmd.cmdarg = (ctrlr->voltages & 0xff8000);
+
+ if (media->version == SD_VERSION_2)
+ cmd.cmdarg |= OCR_HCS;
+
+ err = ctrlr->send_cmd(ctrlr, &cmd, NULL);
+ if (err)
+ return err;
+
+ // OCR_BUSY means "initialization complete".
+ if (cmd.response[0] & OCR_BUSY)
+ break;
+
+ udelay(100);
+ }
+ if (tries < 0)
+ return CARD_UNUSABLE_ERR;
+
+ if (media->version != SD_VERSION_2)
+ media->version = SD_VERSION_1_0;
+
+ media->ocr = cmd.response[0];
+ media->high_capacity = ((media->ocr & OCR_HCS) == OCR_HCS);
+ media->rca = 0;
+ return 0;
+}
+
+static int sd_switch(struct sd_mmc_ctrlr *ctrlr, int mode, int group,
+ uint8_t value, uint8_t *resp)
+{
+ /* Switch the frequency */
+ struct mmc_command cmd;
+ cmd.cmdidx = SD_CMD_SWITCH_FUNC;
+ cmd.resp_type = CARD_RSP_R1;
+ cmd.cmdarg = (mode << 31) | (0xffffff & ~(0xf << (group * 4))) |
+ (value << (group * 4));
+ cmd.flags = 0;
+
+ struct mmc_data data;
+ data.dest = (char *)resp;
+ data.blocksize = 64;
+ data.blocks = 1;
+ data.flags = DATA_FLAG_READ;
+
+ return ctrlr->send_cmd(ctrlr, &cmd, &data);
+}
+
+static void sd_recalculate_clock(struct storage_media *media)
+{
+ uint32_t clock = 1;
+
+ if (media->caps & DRVR_CAP_HS)
+ clock = CLOCK_50MHZ;
+ else
+ clock = CLOCK_25MHZ;
+ SET_CLOCK(media->ctrlr, clock);
+}
+
+int sd_change_freq(struct storage_media *media)
+{
+ int err, timeout;
+ struct mmc_command cmd;
+ struct sd_mmc_ctrlr *ctrlr = media->ctrlr;
+ struct mmc_data data;
+ ALLOC_CACHE_ALIGN_BUFFER(uint32_t, scr, 2);
+ ALLOC_CACHE_ALIGN_BUFFER(uint32_t, switch_status, 16);
+
+ media->caps = 0;
+
+ /* Read the SCR to find out if this card supports higher speeds */
+ cmd.cmdidx = MMC_CMD_APP_CMD;
+ cmd.resp_type = CARD_RSP_R1;
+ cmd.cmdarg = media->rca << 16;
+ cmd.flags = 0;
+
+ err = ctrlr->send_cmd(ctrlr, &cmd, NULL);
+ if (err)
+ return err;
+
+ cmd.cmdidx = SD_CMD_APP_SEND_SCR;
+ cmd.resp_type = CARD_RSP_R1;
+ cmd.cmdarg = 0;
+ cmd.flags = 0;
+
+ timeout = 3;
+ while (timeout--) {
+ data.dest = (char *)scr;
+ data.blocksize = 8;
+ data.blocks = 1;
+ data.flags = DATA_FLAG_READ;
+ err = ctrlr->send_cmd(ctrlr, &cmd, &data);
+ if (!err)
+ break;
+ }
+ if (err) {
+ sd_mmc_error("%s returning %d\n", __func__, err);
+ return err;
+ }
+
+ media->scr[0] = be32toh(scr[0]);
+ media->scr[1] = be32toh(scr[1]);
+
+ switch ((media->scr[0] >> 24) & 0xf) {
+ case 0:
+ media->version = SD_VERSION_1_0;
+ break;
+ case 1:
+ media->version = SD_VERSION_1_10;
+ break;
+ case 2:
+ media->version = SD_VERSION_2;
+ break;
+ default:
+ media->version = SD_VERSION_1_0;
+ break;
+ }
+
+ if (media->scr[0] & SD_DATA_4BIT)
+ media->caps |= DRVR_CAP_4BIT;
+
+ /* Version 1.0 doesn't support switching */
+ if (media->version == SD_VERSION_1_0)
+ goto out;
+
+ timeout = 4;
+ while (timeout--) {
+ err = sd_switch(ctrlr, SD_SWITCH_CHECK, 0, 1,
+ (uint8_t *)switch_status);
+ if (err)
+ return err;
+
+ /* The high-speed function is busy. Try again */
+ if (!(ntohl(switch_status[7]) & SD_HIGHSPEED_BUSY))
+ break;
+ }
+
+ /* If high-speed isn't supported, we return */
+ if (!(ntohl(switch_status[3]) & SD_HIGHSPEED_SUPPORTED))
+ goto out;
+
+ /*
+ * If the controller doesn't support SD_HIGHSPEED, do not switch the
+ * card to HIGHSPEED mode even if the card support SD_HIGHSPPED.
+ * This can avoid a further problem when the card runs in different
+ * mode than the controller.
+ */
+ if (!((ctrlr->caps & DRVR_CAP_HS52) && (ctrlr->caps & DRVR_CAP_HS)))
+ goto out;
+
+ err = sd_switch(ctrlr, SD_SWITCH_SWITCH, 0, 1,
+ (uint8_t *)switch_status);
+ if (err)
+ return err;
+
+ if ((ntohl(switch_status[4]) & 0x0f000000) == 0x01000000) {
+ media->caps |= DRVR_CAP_HS;
+ SET_TIMING(ctrlr, BUS_TIMING_SD_HS);
+ }
+
+out:
+ sd_recalculate_clock(media);
+ return 0;
+}
+
+int sd_set_bus_width(struct storage_media *media)
+{
+ int err;
+ struct mmc_command cmd;
+ struct sd_mmc_ctrlr *ctrlr = media->ctrlr;
+
+ if (media->caps & DRVR_CAP_4BIT) {
+ cmd.cmdidx = MMC_CMD_APP_CMD;
+ cmd.resp_type = CARD_RSP_R1;
+ cmd.cmdarg = media->rca << 16;
+ cmd.flags = 0;
+
+ err = ctrlr->send_cmd(ctrlr, &cmd, NULL);
+ if (err)
+ return err;
+
+ cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH;
+ cmd.resp_type = CARD_RSP_R1;
+ cmd.cmdarg = 2;
+ cmd.flags = 0;
+ err = ctrlr->send_cmd(ctrlr, &cmd, NULL);
+ if (err)
+ return err;
+
+ SET_BUS_WIDTH(ctrlr, 4);
+ }
+ return 0;
+}
+
+
+int sd_set_partition(struct storage_media *media,
+ unsigned int partition_number)
+{
+ /* Validate the partition number */
+ if (partition_number)
+ return -1;
+
+ /* Update the partition number */
+ media->partition_config = partition_number;
+ return 0;
+}
+
+const char *sd_partition_name(struct storage_media *media,
+ unsigned int partition_number)
+{
+ return "";
+}