1
0
Fork 0
mirror of https://github.com/Atmosphere-NX/Atmosphere.git synced 2024-11-23 20:32:10 +00:00
Atmosphere/fusee/common/sdmmc/sdmmc.c

1514 lines
No EOL
46 KiB
C

/*
* Copyright (c) 2018 naehrwert
* Copyright (c) 2018 CTCaer
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include <errno.h>
#include <inttypes.h>
#include "sdmmc.h"
#include "mmc.h"
#include "sd.h"
#if defined(FUSEE_STAGE1_SRC)
#include "../../../fusee/fusee-primary/fusee-primary-main/src/timers.h"
#elif defined(FUSEE_STAGE2_SRC)
#include "../../../fusee/fusee-secondary/src/timers.h"
#elif defined(SEPT_STAGE2_SRC)
#include "../../../sept/sept-secondary/src/timers.h"
#endif
#define UNSTUFF_BITS(resp,start,size) \
({ \
const int __size = size; \
const uint32_t __mask = (__size < 32 ? 1 << __size : 0) - 1; \
const int __off = 3 - ((start) / 32); \
const int __shft = (start) & 31; \
uint32_t __res; \
\
__res = resp[__off] >> __shft; \
if (__size + __shft > 32) \
__res |= resp[__off-1] << ((32 - __shft) % 32); \
__res & __mask; \
})
static const unsigned int tran_exp[] = {
10000, 100000, 1000000, 10000000,
0, 0, 0, 0
};
static const unsigned char tran_mant[] = {
0, 10, 12, 13, 15, 20, 25, 30,
35, 40, 45, 50, 55, 60, 70, 80,
};
static const unsigned int taac_exp[] = {
1, 10, 100, 1000, 10000, 100000, 1000000, 10000000,
};
static const unsigned int taac_mant[] = {
0, 10, 12, 13, 15, 20, 25, 30,
35, 40, 45, 50, 55, 60, 70, 80,
};
/*
Common SDMMC device functions.
*/
static bool is_sdmmc_device_r1_error(uint32_t status) {
return (status & (R1_OUT_OF_RANGE | R1_ADDRESS_ERROR | R1_BLOCK_LEN_ERROR
| R1_ERASE_SEQ_ERROR | R1_ERASE_PARAM | R1_WP_VIOLATION
| R1_LOCK_UNLOCK_FAILED | R1_COM_CRC_ERROR | R1_ILLEGAL_COMMAND
| R1_CARD_ECC_FAILED | R1_CC_ERROR | R1_ERROR | R1_CID_CSD_OVERWRITE
| R1_WP_ERASE_SKIP | R1_ERASE_RESET | R1_SWITCH_ERROR));
}
static int sdmmc_device_send_r1_cmd(sdmmc_device_t *device, uint32_t opcode, uint32_t arg, bool is_busy, uint32_t resp_mask, uint32_t resp_state) {
sdmmc_command_t cmd = {};
cmd.opcode = opcode;
cmd.arg = arg;
cmd.flags = (is_busy ? SDMMC_RSP_R1B : SDMMC_RSP_R1);
/* Try to send the command. */
if (!sdmmc_send_cmd(device->sdmmc, &cmd, 0, 0)) {
return 0;
}
uint32_t resp = 0;
/* Try to load back the response. */
if (!sdmmc_load_response(device->sdmmc, SDMMC_RSP_R1, &resp)) {
return 0;
}
/* Mask the response, if necessary. */
if (resp_mask) {
resp &= ~(resp_mask);
}
/* We got an error state. */
if (is_sdmmc_device_r1_error(resp)) {
return 0;
}
/* We need to check for the desired state. */
if (resp_state != 0xFFFFFFFF) {
/* We didn't get the expected state. */
if (R1_CURRENT_STATE(resp) != resp_state) {
return 0;
}
}
return 1;
}
static int sdmmc_device_go_idle(sdmmc_device_t *device) {
sdmmc_command_t cmd = {};
cmd.opcode = MMC_GO_IDLE_STATE;
cmd.arg = 0;
cmd.flags = SDMMC_RSP_NONE;
return sdmmc_send_cmd(device->sdmmc, &cmd, 0, 0);
}
static int sdmmc_device_send_cid(sdmmc_device_t *device, uint32_t *cid) {
sdmmc_command_t cmd = {};
cmd.opcode = MMC_ALL_SEND_CID;
cmd.arg = 0;
cmd.flags = SDMMC_RSP_R2;
/* Try to send the command. */
if (!sdmmc_send_cmd(device->sdmmc, &cmd, 0, 0)) {
return 0;
}
/* Try to load back the response. */
return sdmmc_load_response(device->sdmmc, SDMMC_RSP_R2, cid);
}
static int sdmmc_device_send_csd(sdmmc_device_t *device, uint32_t *csd) {
sdmmc_command_t cmd = {};
cmd.opcode = MMC_SEND_CSD;
cmd.arg = (device->rca << 16);
cmd.flags = SDMMC_RSP_R2;
/* Try to send the command. */
if (!sdmmc_send_cmd(device->sdmmc, &cmd, 0, 0)) {
return 0;
}
/* Try to load back the response. */
return sdmmc_load_response(device->sdmmc, SDMMC_RSP_R2, csd);
}
static int sdmmc_device_select_card(sdmmc_device_t *device) {
/* Try to send the command. */
return sdmmc_device_send_r1_cmd(device, MMC_SELECT_CARD, (device->rca << 16), true, 0, 0xFFFFFFFF);
}
static int sdmmc_device_set_blocklen(sdmmc_device_t *device, uint32_t blocklen) {
/* Try to send the command. */
return sdmmc_device_send_r1_cmd(device, MMC_SET_BLOCKLEN, blocklen, false, 0, R1_STATE_TRAN);
}
static int sdmmc_device_send_status(sdmmc_device_t *device) {
/* Try to send the command. */
return sdmmc_device_send_r1_cmd(device, MMC_SEND_STATUS, (device->rca << 16), false, 0, R1_STATE_TRAN);
}
static int sdmmc_device_rw(sdmmc_device_t *device, uint32_t sector, uint32_t num_sectors, void *data, bool is_read) {
uint8_t *buf = (uint8_t *)data;
sdmmc_command_t cmd = {};
sdmmc_request_t req = {};
while (num_sectors) {
uint32_t num_blocks_out = 0;
uint32_t num_retries = 10;
for (; num_retries > 0; num_retries--) {
cmd.opcode = is_read ? MMC_READ_MULTIPLE_BLOCK : MMC_WRITE_MULTIPLE_BLOCK;
cmd.arg = sector;
cmd.flags = SDMMC_RSP_R1;
req.data = buf;
req.blksz = 512;
req.num_blocks = num_sectors;
req.is_read = is_read;
req.is_multi_block = true;
req.is_auto_cmd12 = true;
/* Try to send the command. */
if (!sdmmc_send_cmd(device->sdmmc, &cmd, &req, &num_blocks_out)) {
/* Abort the transmission. */
sdmmc_abort(device->sdmmc, MMC_STOP_TRANSMISSION);
/* Peek the SD card's status. */
sdmmc_device_send_status(device);
/* Wait for a while. */
mdelay(100);
} else {
break;
}
}
/* Failed to read/write on all attempts. */
if (!num_retries) {
return 0;
}
/* Advance to next sector. */
sector += num_blocks_out;
num_sectors -= num_blocks_out;
buf += (512 * num_blocks_out);
}
return 1;
}
int sdmmc_device_read(sdmmc_device_t *device, uint32_t sector, uint32_t num_sectors, void *data) {
return sdmmc_device_rw(device, sector, num_sectors, data, true);
}
int sdmmc_device_write(sdmmc_device_t *device, uint32_t sector, uint32_t num_sectors, void *data) {
return sdmmc_device_rw(device, sector, num_sectors, data, false);
}
int sdmmc_device_finish(sdmmc_device_t *device) {
/* Place the device in idle state. */
if (!sdmmc_device_go_idle(device)) {
return 0;
}
/* Terminate the device. */
sdmmc_finish(device->sdmmc);
return 1;
}
/*
SD device functions.
*/
static void sdmmc_sd_decode_cid(sdmmc_device_t *device, uint32_t *cid) {
device->cid.manfid = UNSTUFF_BITS(cid, 120, 8);
device->cid.oemid = UNSTUFF_BITS(cid, 104, 16);
device->cid.prod_name[0] = UNSTUFF_BITS(cid, 96, 8);
device->cid.prod_name[1] = UNSTUFF_BITS(cid, 88, 8);
device->cid.prod_name[2] = UNSTUFF_BITS(cid, 80, 8);
device->cid.prod_name[3] = UNSTUFF_BITS(cid, 72, 8);
device->cid.prod_name[4] = UNSTUFF_BITS(cid, 64, 8);
device->cid.hwrev = UNSTUFF_BITS(cid, 60, 4);
device->cid.fwrev = UNSTUFF_BITS(cid, 56, 4);
device->cid.serial = UNSTUFF_BITS(cid, 24, 32);
device->cid.year = UNSTUFF_BITS(cid, 12, 8) + 2000; /* SD cards year offset */
device->cid.month = UNSTUFF_BITS(cid, 8, 4);
}
static int sdmmc_sd_decode_csd(sdmmc_device_t *device, uint32_t *csd) {
unsigned int e, m;
device->csd.structure = UNSTUFF_BITS(csd, 126, 2);
switch (device->csd.structure) {
case 0:
m = UNSTUFF_BITS(csd, 115, 4);
e = UNSTUFF_BITS(csd, 112, 3);
device->csd.taac_ns = (taac_exp[e] * taac_mant[m] + 9) / 10;
device->csd.taac_clks = UNSTUFF_BITS(csd, 104, 8) * 100;
m = UNSTUFF_BITS(csd, 99, 4);
e = UNSTUFF_BITS(csd, 96, 3);
device->csd.max_dtr = tran_exp[e] * tran_mant[m];
device->csd.cmdclass = UNSTUFF_BITS(csd, 84, 12);
e = UNSTUFF_BITS(csd, 47, 3);
m = UNSTUFF_BITS(csd, 62, 12);
device->csd.capacity = ((1 + m) << (e + 2));
device->csd.read_blkbits = UNSTUFF_BITS(csd, 80, 4);
device->csd.read_partial = UNSTUFF_BITS(csd, 79, 1);
device->csd.write_misalign = UNSTUFF_BITS(csd, 78, 1);
device->csd.read_misalign = UNSTUFF_BITS(csd, 77, 1);
device->csd.dsr_imp = UNSTUFF_BITS(csd, 76, 1);
device->csd.r2w_factor = UNSTUFF_BITS(csd, 26, 3);
device->csd.write_blkbits = UNSTUFF_BITS(csd, 22, 4);
device->csd.write_partial = UNSTUFF_BITS(csd, 21, 1);
if (UNSTUFF_BITS(csd, 46, 1)) {
device->csd.erase_size = 1;
} else if (device->csd.write_blkbits >= 9) {
device->csd.erase_size = UNSTUFF_BITS(csd, 39, 7) + 1;
device->csd.erase_size <<= (device->csd.write_blkbits - 9);
}
break;
case 1:
device->csd.taac_ns = 0; /* Unused */
device->csd.taac_clks = 0; /* Unused */
m = UNSTUFF_BITS(csd, 99, 4);
e = UNSTUFF_BITS(csd, 96, 3);
device->csd.max_dtr = tran_exp[e] * tran_mant[m];
device->csd.cmdclass = UNSTUFF_BITS(csd, 84, 12);
device->csd.c_size = UNSTUFF_BITS(csd, 48, 22);
m = UNSTUFF_BITS(csd, 48, 22);
device->csd.capacity = ((1 + m) << 10);
device->csd.read_blkbits = 9;
device->csd.read_partial = 0;
device->csd.write_misalign = 0;
device->csd.read_misalign = 0;
device->csd.r2w_factor = 4; /* Unused */
device->csd.write_blkbits = 9;
device->csd.write_partial = 0;
device->csd.erase_size = 1;
break;
default:
return 0;
}
return 1;
}
static int sdmmc_sd_decode_scr(sdmmc_device_t *device, uint8_t *scr) {
uint8_t tmp[8];
uint32_t resp[4];
/* This must be swapped. */
for (int i = 0; i < 8; i += 4) {
tmp[i + 3] = scr[i];
tmp[i + 2] = scr[i + 1];
tmp[i + 1] = scr[i + 2];
tmp[i] = scr[i + 3];
}
resp[3] = *(uint32_t *)&tmp[4];
resp[2] = *(uint32_t *)&tmp[0];
device->scr.sda_vsn = UNSTUFF_BITS(resp, 56, 4);
device->scr.bus_widths = UNSTUFF_BITS(resp, 48, 4);
/* Check if Physical Layer Spec v3.0 is supported. */
if (device->scr.sda_vsn == SD_SCR_SPEC_VER_2) {
device->scr.sda_spec3 = UNSTUFF_BITS(resp, 47, 1);
}
if (device->scr.sda_spec3) {
device->scr.cmds = UNSTUFF_BITS(resp, 32, 2);
}
/* Unknown SCR structure version. */
if (UNSTUFF_BITS(resp, 60, 4)) {
return 0;
} else {
return 1;
}
}
static void sdmmc_sd_decode_ssr(sdmmc_device_t *device, uint8_t *ssr) {
uint8_t tmp[64];
uint32_t resp1[4];
uint32_t resp2[4];
/* This must be swapped. */
for (int i = 0; i < 64; i += 4) {
tmp[i + 3] = ssr[i];
tmp[i + 2] = ssr[i + 1];
tmp[i + 1] = ssr[i + 2];
tmp[i] = ssr[i + 3];
}
resp1[3] = *(uint32_t *)&tmp[12];
resp1[2] = *(uint32_t *)&tmp[8];
resp1[1] = *(uint32_t *)&tmp[4];
resp1[0] = *(uint32_t *)&tmp[0];
resp2[3] = *(uint32_t *)&tmp[28];
resp2[2] = *(uint32_t *)&tmp[24];
resp2[1] = *(uint32_t *)&tmp[20];
resp2[0] = *(uint32_t *)&tmp[16];
device->ssr.dat_bus_width = ((UNSTUFF_BITS(resp1, 126, 2) & SD_BUS_WIDTH_4) ? 4 : 1);
device->ssr.speed_class = UNSTUFF_BITS(resp1, 56, 8);
if (device->ssr.speed_class < 4) {
device->ssr.speed_class <<= 1;
} else if (device->ssr.speed_class == 4) {
device->ssr.speed_class = 10;
}
device->ssr.uhs_speed_grade = UNSTUFF_BITS(resp1, 12, 4);
device->ssr.video_speed_class = UNSTUFF_BITS(resp1, 0, 8);
device->ssr.app_perf_class = UNSTUFF_BITS(resp2, 80, 4);
}
static int sdmmc_sd_send_app_cmd(sdmmc_device_t *device, sdmmc_command_t *cmd, sdmmc_request_t *req, uint32_t resp_mask, uint32_t resp_state) {
/* Try to send the APP command. */
if (!sdmmc_device_send_r1_cmd(device, MMC_APP_CMD, (device->rca << 16), false, resp_mask, resp_state)) {
return 0;
}
/* Send the actual command. */
if (!sdmmc_send_cmd(device->sdmmc, cmd, req, 0)) {
return 0;
}
return 1;
}
static int sdmmc_sd_send_if_cond(sdmmc_device_t *device, bool *is_sd_ver2) {
sdmmc_command_t cmd = {};
cmd.opcode = SD_SEND_IF_COND;
/* We set the bit if the host supports voltages between 2.7 and 3.6 V */
cmd.arg = 0x1AA;
cmd.flags = SDMMC_RSP_R7;
/* Command failed, this means SD Card is not version 2. */
if (!sdmmc_send_cmd(device->sdmmc, &cmd, 0, 0)) {
*is_sd_ver2 = false;
return 1;
}
uint32_t resp = 0;
/* Try to load back the response. */
if (!sdmmc_load_response(device->sdmmc, SDMMC_RSP_R7, &resp)) {
return 0;
}
/* Check if we got a valid response. */
if ((resp & 0xFF) == 0xAA) {
*is_sd_ver2 = true;
return 1;
}
return 0;
}
static int sdmmc_sd_send_op_cond(sdmmc_device_t *device, bool is_sd_ver2, bool is_uhs_en) {
sdmmc_command_t cmd = {};
/* Program a large timeout. */
uint32_t timebase = get_time();
bool is_timeout = false;
while (!is_timeout) {
/* Set this since most cards do not answer if some reserved bits in the OCR are set. */
uint32_t arg = SD_OCR_VDD_32_33;
/* Request support for SDXC power control and SDHC block mode cards. */
if (is_sd_ver2) {
arg |= SD_OCR_XPC;
arg |= SD_OCR_CCS;
}
/* Request support 1.8V switching. */
if (is_uhs_en) {
arg |= SD_OCR_S18R;
}
cmd.opcode = SD_APP_OP_COND;
cmd.arg = arg;
cmd.flags = SDMMC_RSP_R3;
/* Try to send the command. */
if (!sdmmc_sd_send_app_cmd(device, &cmd, 0, is_sd_ver2 ? 0 : 0x400000, 0xFFFFFFFF)) {
return 0;
}
uint32_t resp = 0;
/* Try to load back the response. */
if (!sdmmc_load_response(device->sdmmc, SDMMC_RSP_R3, &resp)) {
return 0;
}
/* Card Power up bit is set. */
if (resp & MMC_CARD_BUSY) {
/* We have a SDHC block mode card. */
if (resp & SD_OCR_CCS) {
device->is_block_sdhc = true;
}
/* We asked for low voltage support and the card accepted. */
if (is_uhs_en && (resp & SD_ROCR_S18A)) {
/* Voltage switching is only valid for SDMMC1. */
if (device->sdmmc->controller == SDMMC_1) {
/* Failed to issue voltage switching command. */
if (!sdmmc_device_send_r1_cmd(device, SD_SWITCH_VOLTAGE, 0, false, 0, R1_STATE_READY)) {
return 0;
}
/* Delay a bit before asking for the voltage switch. */
mdelay(100);
/* Tell the driver to switch the voltage. */
if (!sdmmc_switch_voltage(device->sdmmc)) {
return 0;
}
/* We are now running at 1.8V. */
device->is_180v = true;
}
}
return 1;
}
/* Keep checking if timeout expired. */
is_timeout = (get_time_since(timebase) > 2000000);
/* Delay for a minimum of 10 milliseconds. */
mdelay(10);
}
return 0;
}
static int sdmmc_sd_send_relative_addr(sdmmc_device_t *device) {
sdmmc_command_t cmd = {};
cmd.opcode = SD_SEND_RELATIVE_ADDR;
cmd.arg = 0;
cmd.flags = SDMMC_RSP_R6;
/* Program a large timeout. */
uint32_t timebase = get_time();
bool is_timeout = false;
while (!is_timeout) {
/* Try to send the command. */
if (!sdmmc_send_cmd(device->sdmmc, &cmd, 0, 0)) {
return 0;
}
uint32_t resp = 0;
/* Try to load back the response. */
if (!sdmmc_load_response(device->sdmmc, SDMMC_RSP_R6, &resp)) {
return 0;
}
/* Save the RCA. */
if (resp >> 16) {
device->rca = (resp >> 16);
return 1;
}
/* Keep checking if timeout expired. */
is_timeout = (get_time_since(timebase) > 2000000);
/* Delay for an appropriate period. */
udelay(1000);
}
return 0;
}
static int sdmmc_sd_send_scr(sdmmc_device_t *device, uint8_t *scr) {
sdmmc_command_t cmd = {};
sdmmc_request_t req = {};
cmd.opcode = SD_APP_SEND_SCR;
cmd.arg = 0;
cmd.flags = SDMMC_RSP_R1;
req.data = scr;
req.blksz = 8;
req.num_blocks = 1;
req.is_read = true;
req.is_multi_block = false;
req.is_auto_cmd12 = false;
/* Try to send the APP command. */
if (!sdmmc_sd_send_app_cmd(device, &cmd, &req, 0, R1_STATE_TRAN)) {
return 0;
}
uint32_t resp = 0;
/* Try to load back the response. */
if (!sdmmc_load_response(device->sdmmc, SDMMC_RSP_R1, &resp)) {
return 0;
}
/* Evaluate the response. */
if (is_sdmmc_device_r1_error(resp)) {
return 0;
} else {
return 1;
}
}
static int sdmmc_sd_set_clr_card_detect(sdmmc_device_t *device) {
/* Try to send the APP command. */
if (!sdmmc_device_send_r1_cmd(device, MMC_APP_CMD, (device->rca << 16), false, 0, R1_STATE_TRAN)) {
return 0;
}
/* Try to send the command. */
return sdmmc_device_send_r1_cmd(device, SD_APP_SET_CLR_CARD_DETECT, 0, false, 0, R1_STATE_TRAN);
}
static int sdmmc_sd_set_bus_width(sdmmc_device_t *device) {
/* Try to send the APP command. */
if (!sdmmc_device_send_r1_cmd(device, MMC_APP_CMD, (device->rca << 16), false, 0, R1_STATE_TRAN)) {
return 0;
}
/* Try to send the command. */
return sdmmc_device_send_r1_cmd(device, SD_APP_SET_BUS_WIDTH, SD_BUS_WIDTH_4, false, 0, R1_STATE_TRAN);
}
static int sdmmc_sd_switch(sdmmc_device_t *device, uint32_t mode, uint32_t group, uint8_t value, uint8_t *data) {
sdmmc_command_t cmd = {};
sdmmc_request_t req = {};
cmd.opcode = SD_SWITCH;
cmd.arg = ((mode << 31) | 0x00FFFFFF);
cmd.arg &= ~(0xF << (group * 4));
cmd.arg |= (value << (group * 4));
cmd.flags = SDMMC_RSP_R1;
req.data = data;
req.blksz = 64;
req.num_blocks = 1;
req.is_read = true;
req.is_multi_block = false;
req.is_auto_cmd12 = false;
/* Try to send the command. */
if (!sdmmc_send_cmd(device->sdmmc, &cmd, &req, 0)) {
return 0;
}
uint32_t resp = 0;
/* Try to load back the response. */
if (!sdmmc_load_response(device->sdmmc, SDMMC_RSP_R1, &resp)) {
return 0;
}
/* Evaluate the response. */
if (is_sdmmc_device_r1_error(resp)) {
return 0;
} else {
return 1;
}
}
static int sdmmc_sd_set_current_limit(sdmmc_device_t *device, uint8_t *status) {
/* Start with the highest possible limit. */
int32_t current_limit = SD_SET_CURRENT_LIMIT_800;
/* Try each limit. */
while (current_limit > SD_SET_CURRENT_NO_CHANGE) {
/* Switch the current limit. */
if (!sdmmc_sd_switch(device, SD_SWITCH_SET, 3, current_limit, status)) {
return 0;
}
/* Current limit was set successfully. */
if (((status[15] >> 4) & 0x0F) == current_limit) {
break;
}
current_limit--;
}
return 1;
}
static int sdmmc_sd_switch_hs(sdmmc_device_t *device, uint32_t type, uint8_t *status) {
/* Test if the card supports high-speed mode. */
if (!sdmmc_sd_switch(device, 0, 0, type, status)) {
return 0;
}
uint32_t res_type = (status[16] & 0xF);
/* This high-speed mode type is not supported. */
if (res_type != type) {
return 0;
}
if ((((uint16_t)status[0] << 8) | status[1]) < 0x320) {
/* Try to switch to high-speed mode. */
if (!sdmmc_sd_switch(device, 1, 0, type, status)) {
return 0;
}
/* Something failed when switching to high-speed mode. */
if ((status[16] & 0xF) != res_type) {
return 0;
}
}
return 1;
}
static int sdmmc_sd_switch_hs_low(sdmmc_device_t *device, uint8_t *status) {
/* Adjust the current limit. */
if (!sdmmc_sd_set_current_limit(device, status)) {
return 0;
}
/* Invalid bus width. */
if (device->sdmmc->bus_width != SDMMC_BUS_WIDTH_4BIT) {
return 0;
}
/* Get the supported high-speed type. */
if (!sdmmc_sd_switch(device, 0, 0, 0xF, status)) {
return 0;
}
/* High-speed SDR104 is supported. */
if (status[13] & SD_MODE_UHS_SDR104) {
/* Switch to high-speed. */
if (!sdmmc_sd_switch_hs(device, UHS_SDR104_BUS_SPEED, status)) {
return 0;
}
/* Reconfigure the internal clock. */
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_SD_SDR104)) {
return 0;
}
/* Run tuning. */
if (!sdmmc_execute_tuning(device->sdmmc, SDMMC_SPEED_SD_SDR104, MMC_SEND_TUNING_BLOCK)) {
return 0;
}
} else if (status[13] & SD_MODE_UHS_SDR50) { /* High-speed SDR50 is supported. */
/* Switch to high-speed. */
if (!sdmmc_sd_switch_hs(device, UHS_SDR50_BUS_SPEED, status)) {
return 0;
}
/* Reconfigure the internal clock. */
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_SD_SDR50)) {
return 0;
}
/* Run tuning. */
if (!sdmmc_execute_tuning(device->sdmmc, SDMMC_SPEED_SD_SDR50, MMC_SEND_TUNING_BLOCK)) {
return 0;
}
} else if (status[13] & SD_MODE_UHS_SDR12) { /* High-speed SDR12 is supported. */
/* Switch to high-speed. */
if (!sdmmc_sd_switch_hs(device, UHS_SDR12_BUS_SPEED, status)) {
return 0;
}
/* Reconfigure the internal clock. */
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_SD_SDR12)) {
return 0;
}
/* Run tuning. */
if (!sdmmc_execute_tuning(device->sdmmc, SDMMC_SPEED_SD_SDR12, MMC_SEND_TUNING_BLOCK)) {
return 0;
}
} else {
return 0;
}
/* Peek the SD card's status. */
return sdmmc_device_send_status(device);
}
static int sdmmc_sd_switch_hs_high(sdmmc_device_t *device, uint8_t *status) {
/* Get the supported high-speed type. */
if (!sdmmc_sd_switch(device, 0, 0, 0xF, status)) {
return 0;
}
/* High-speed is supported. */
if (status[13] & 2) {
/* Switch to high-speed. */
if (!sdmmc_sd_switch_hs(device, SDHCI_CTRL_UHS_SDR25, status)) {
return 0;
}
/* Reconfigure the internal clock. */
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_SD_SDR25)) {
return 0;
}
/* Peek the SD card's status. */
return sdmmc_device_send_status(device);
}
/* Nothing to do. */
return 1;
}
static int sdmmc_sd_status(sdmmc_device_t *device, uint8_t *ssr) {
sdmmc_command_t cmd = {};
sdmmc_request_t req = {};
cmd.opcode = SD_APP_SD_STATUS;
cmd.arg = 0;
cmd.flags = SDMMC_RSP_R1;
req.data = ssr;
req.blksz = 64;
req.num_blocks = 1;
req.is_read = true;
req.is_multi_block = false;
req.is_auto_cmd12 = false;
/* Try to send the APP command. */
if (!sdmmc_sd_send_app_cmd(device, &cmd, &req, 0, R1_STATE_TRAN)) {
return 0;
}
uint32_t resp = 0;
/* Try to load back the response. */
if (!sdmmc_load_response(device->sdmmc, SDMMC_RSP_R1, &resp)) {
return 0;
}
/* Evaluate the response. */
if (is_sdmmc_device_r1_error(resp)) {
return 0;
} else {
return 1;
}
}
int sdmmc_device_sd_init(sdmmc_device_t *device, sdmmc_t *sdmmc, SdmmcBusWidth bus_width, SdmmcBusSpeed bus_speed) {
bool is_sd_ver2 = false;
uint32_t cid[4] = {0};
uint32_t csd[4] = {0};
uint8_t scr[8] = {0};
uint8_t ssr[64] = {0};
uint8_t switch_status[512] = {0};
/* Initialize our device's struct. */
memset(device, 0, sizeof(sdmmc_device_t));
/* Try to initialize the driver. */
if (!sdmmc_init(sdmmc, SDMMC_1, SDMMC_VOLTAGE_3V3, SDMMC_BUS_WIDTH_1BIT, SDMMC_SPEED_SD_IDENT)) {
sdmmc_error(sdmmc, "Failed to initialize the SDMMC driver!");
return 0;
}
/* Bind the underlying driver. */
device->sdmmc = sdmmc;
sdmmc_info(sdmmc, "SDMMC driver was successfully initialized for SD!");
/* Apply at least 74 clock cycles. The card should be ready afterwards. */
udelay((74000 + sdmmc->internal_divider - 1) / sdmmc->internal_divider);
/* Instruct the SD card to go idle. */
if (!sdmmc_device_go_idle(device)) {
sdmmc_error(sdmmc, "Failed to go idle!");
return 0;
}
sdmmc_info(sdmmc, "SD card went idle!");
/* Get the SD card's interface operating condition. */
if (!sdmmc_sd_send_if_cond(device, &is_sd_ver2)) {
sdmmc_error(sdmmc, "Failed to send if cond!");
return 0;
}
sdmmc_info(sdmmc, "Sent if cond to SD card!");
/* Get the SD card's operating conditions. */
if (!sdmmc_sd_send_op_cond(device, is_sd_ver2, (bus_width == SDMMC_BUS_WIDTH_4BIT) && (bus_speed == SDMMC_SPEED_SD_SDR104))) {
sdmmc_error(sdmmc, "Failed to send op cond!");
return 0;
}
sdmmc_info(sdmmc, "Sent op cond to SD card!");
/* Get the SD card's CID. */
if (!sdmmc_device_send_cid(device, cid)) {
sdmmc_error(sdmmc, "Failed to get CID!");
return 0;
}
sdmmc_info(sdmmc, "Got CID from SD card!");
/* Decode and save the CID. */
sdmmc_sd_decode_cid(device, cid);
/* Get the SD card's RCA. */
if (!sdmmc_sd_send_relative_addr(device)) {
sdmmc_error(sdmmc, "Failed to get RCA!");
return 0;
}
sdmmc_info(sdmmc, "Got RCA (0x%08x) from SD card!", device->rca);
/* Get the SD card's CSD. */
if (!sdmmc_device_send_csd(device, csd)) {
sdmmc_error(sdmmc, "Failed to get CSD!");
return 0;
}
sdmmc_info(sdmmc, "Got CSD from SD card!");
/* Decode and save the CSD. */
if (!sdmmc_sd_decode_csd(device, csd)) {
sdmmc_warn(sdmmc, "Got unknown CSD structure (0x%08x)!", device->csd.structure);
}
/* If we never switched to 1.8V, change the bus speed mode. */
if (!device->is_180v) {
/* Reconfigure the internal clock. */
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_SD_DS)) {
sdmmc_error(sdmmc, "Failed to apply the correct bus speed!");
return 0;
}
sdmmc_info(sdmmc, "Speed mode has been adjusted!");
}
/* Select the SD card. */
if (!sdmmc_device_select_card(device)) {
sdmmc_error(sdmmc, "Failed to select SD card!");
return 0;
}
sdmmc_info(sdmmc, "SD card is now selected!");
/* Change the SD card's block length. */
if (!sdmmc_device_set_blocklen(device, 512)) {
sdmmc_error(sdmmc, "Failed to set SD card's block length!");
return 0;
}
sdmmc_info(sdmmc, "SD card's block length is now 512!");
/* It's a good practice to disconnect the pull-up resistor with ACMD42. */
if (!sdmmc_sd_set_clr_card_detect(device)) {
sdmmc_error(sdmmc, "Failed to disconnect the pull-up resistor!");
return 0;
}
sdmmc_info(sdmmc, "Pull-up resistor is now disconnected!");
/* Get the SD card's SCR. */
if (!sdmmc_sd_send_scr(device, scr)) {
sdmmc_error(sdmmc, "Failed to get SCR!");
return 0;
}
sdmmc_info(sdmmc, "Got SCR from SD card!");
/* Decode and save the SCR. */
if (!sdmmc_sd_decode_scr(device, scr)) {
sdmmc_error(sdmmc, "Got unknown SCR structure!");
return 0;
}
/* Switch to wider bus (if supported). */
if ((bus_width == SDMMC_BUS_WIDTH_4BIT)
&& (device->scr.bus_widths & SD_SCR_BUS_WIDTH_4)
&& (device->scr.sda_vsn & (SD_SCR_SPEC_VER_1 | SD_SCR_SPEC_VER_2))) {
if (!sdmmc_sd_set_bus_width(device)) {
sdmmc_error(sdmmc, "Failed to switch to wider bus!");
return 0;
}
sdmmc_select_bus_width(device->sdmmc, SDMMC_BUS_WIDTH_4BIT);
sdmmc_info(sdmmc, "Switched to wider bus!");
}
if (device->is_180v) {
/* Switch to high-speed from low voltage (if possible). */
if (!sdmmc_sd_switch_hs_low(device, switch_status)) {
sdmmc_error(sdmmc, "Failed to switch to high-speed from low voltage!");
return 0;
}
sdmmc_info(sdmmc, "Switched to high-speed from low voltage!");
} else if ((device->scr.sda_vsn & (SD_SCR_SPEC_VER_1 | SD_SCR_SPEC_VER_2)) && ((bus_speed != SDMMC_SPEED_SD_DS))) {
/* Switch to high-speed from high voltage (if possible). */
if (!sdmmc_sd_switch_hs_high(device, switch_status)) {
sdmmc_error(sdmmc, "Failed to switch to high-speed from high voltage!");
return 0;
}
sdmmc_info(sdmmc, "Switched to high-speed from high voltage!");
}
/* Correct any inconsistent states. */
sdmmc_adjust_sd_clock(sdmmc);
/* Get the SD card's SSR. */
if (!sdmmc_sd_status(device, ssr)) {
sdmmc_error(sdmmc, "Failed to get SSR!");
return 0;
}
sdmmc_info(sdmmc, "Got SSR from SD card!");
/* Decode and save the SSR. */
sdmmc_sd_decode_ssr(device, scr);
return 1;
}
/*
MMC device functions.
*/
static void sdmmc_mmc_decode_cid(sdmmc_device_t *device, uint32_t *cid) {
switch (device->csd.mmca_vsn) {
case 0: /* MMC v1.0 - v1.2 */
case 1: /* MMC v1.4 */
device->cid.prod_name[6] = UNSTUFF_BITS(cid, 48, 8);
device->cid.manfid = UNSTUFF_BITS(cid, 104, 24);
device->cid.hwrev = UNSTUFF_BITS(cid, 44, 4);
device->cid.fwrev = UNSTUFF_BITS(cid, 40, 4);
device->cid.serial = UNSTUFF_BITS(cid, 16, 24);
break;
case 2: /* MMC v2.0 - v2.2 */
case 3: /* MMC v3.1 - v3.3 */
case 4: /* MMC v4 */
device->cid.manfid = UNSTUFF_BITS(cid, 120, 8);
device->cid.oemid = UNSTUFF_BITS(cid, 104, 8);
device->cid.prv = UNSTUFF_BITS(cid, 48, 8);
device->cid.serial = UNSTUFF_BITS(cid, 16, 32);
break;
default:
break;
}
device->cid.prod_name[0] = UNSTUFF_BITS(cid, 96, 8);
device->cid.prod_name[1] = UNSTUFF_BITS(cid, 88, 8);
device->cid.prod_name[2] = UNSTUFF_BITS(cid, 80, 8);
device->cid.prod_name[3] = UNSTUFF_BITS(cid, 72, 8);
device->cid.prod_name[4] = UNSTUFF_BITS(cid, 64, 8);
device->cid.prod_name[5] = UNSTUFF_BITS(cid, 56, 8);
device->cid.month = UNSTUFF_BITS(cid, 12, 4);
device->cid.year = (UNSTUFF_BITS(cid, 8, 4) + 1997);
if ((device->ext_csd.rev >= 5) && (device->cid.year < 2010)) {
device->cid.year += 16;
}
}
static int sdmmc_mmc_decode_csd(sdmmc_device_t *device, uint32_t *csd) {
unsigned int e, m, a, b;
device->csd.structure = UNSTUFF_BITS(csd, 126, 2);
if (!device->csd.structure) {
return 0;
}
device->csd.mmca_vsn = UNSTUFF_BITS(csd, 122, 4);
m = UNSTUFF_BITS(csd, 115, 4);
e = UNSTUFF_BITS(csd, 112, 3);
device->csd.taac_ns = ((taac_exp[e] * taac_mant[m] + 9) / 10);
device->csd.taac_clks = (UNSTUFF_BITS(csd, 104, 8) * 100);
m = UNSTUFF_BITS(csd, 99, 4);
e = UNSTUFF_BITS(csd, 96, 3);
device->csd.max_dtr = (tran_exp[e] * tran_mant[m]);
device->csd.cmdclass = UNSTUFF_BITS(csd, 84, 12);
e = UNSTUFF_BITS(csd, 47, 3);
m = UNSTUFF_BITS(csd, 62, 12);
device->csd.capacity = ((1 + m) << (e + 2));
device->csd.read_blkbits = UNSTUFF_BITS(csd, 80, 4);
device->csd.read_partial = UNSTUFF_BITS(csd, 79, 1);
device->csd.write_misalign = UNSTUFF_BITS(csd, 78, 1);
device->csd.read_misalign = UNSTUFF_BITS(csd, 77, 1);
device->csd.dsr_imp = UNSTUFF_BITS(csd, 76, 1);
device->csd.r2w_factor = UNSTUFF_BITS(csd, 26, 3);
device->csd.write_blkbits = UNSTUFF_BITS(csd, 22, 4);
device->csd.write_partial = UNSTUFF_BITS(csd, 21, 1);
if (device->csd.write_blkbits >= 9) {
a = UNSTUFF_BITS(csd, 42, 5);
b = UNSTUFF_BITS(csd, 37, 5);
device->csd.erase_size = ((a + 1) * (b + 1));
device->csd.erase_size <<= (device->csd.write_blkbits - 9);
}
return 1;
}
static void sdmmc_mmc_decode_ext_csd(sdmmc_device_t *device, uint8_t *ext_csd) {
device->ext_csd.rev = ext_csd[EXT_CSD_REV];
device->ext_csd.raw_ext_csd_structure = ext_csd[EXT_CSD_STRUCTURE];
device->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE];
device->ext_csd.raw_rpmb_size_mult = ext_csd[EXT_CSD_RPMB_MULT];
device->ext_csd.raw_sectors[0] = ext_csd[EXT_CSD_SEC_CNT + 0];
device->ext_csd.raw_sectors[1] = ext_csd[EXT_CSD_SEC_CNT + 1];
device->ext_csd.raw_sectors[2] = ext_csd[EXT_CSD_SEC_CNT + 2];
device->ext_csd.raw_sectors[3] = ext_csd[EXT_CSD_SEC_CNT + 3];
device->ext_csd.bkops = ext_csd[EXT_CSD_BKOPS_SUPPORT];
device->ext_csd.man_bkops_en = ext_csd[EXT_CSD_BKOPS_EN];
device->ext_csd.raw_bkops_status = ext_csd[EXT_CSD_BKOPS_STATUS];
}
static int sdmmc_mmc_send_op_cond(sdmmc_device_t *device, SdmmcBusVoltage bus_voltage) {
sdmmc_command_t cmd = {};
/* Program a large timeout. */
uint32_t timebase = get_time();
bool is_timeout = false;
while (!is_timeout) {
/* Set high capacity bit. */
uint32_t arg = SD_OCR_CCS;
/* Set voltage bits. */
if (bus_voltage == SDMMC_VOLTAGE_1V8) {
arg |= MMC_VDD_165_195;
} else if (bus_voltage == SDMMC_VOLTAGE_3V3) {
arg |= (MMC_VDD_33_34 | MMC_VDD_32_33 | MMC_VDD_31_32 | MMC_VDD_30_31 | MMC_VDD_29_30 | MMC_VDD_28_29 | MMC_VDD_27_28);
} else {
return 0;
}
cmd.opcode = MMC_SEND_OP_COND;
cmd.arg = arg;
cmd.flags = SDMMC_RSP_R3;
/* Try to send the command. */
if (!sdmmc_send_cmd(device->sdmmc, &cmd, 0, 0)) {
return 0;
}
uint32_t resp = 0;
/* Try to load back the response. */
if (!sdmmc_load_response(device->sdmmc, SDMMC_RSP_R3, &resp)) {
return 0;
}
/* Card Power up bit is set. */
if (resp & MMC_CARD_BUSY) {
/* We have a SDHC block mode card. */
if (resp & SD_OCR_CCS) {
device->is_block_sdhc = true;
}
return 1;
}
/* Keep checking if timeout expired. */
is_timeout = (get_time_since(timebase) > 2000000);
/* Delay for a minimum of 10 milliseconds. */
mdelay(10);
}
return 0;
}
static int sdmmc_mmc_send_ext_csd(sdmmc_device_t *device, uint8_t *ext_csd) {
sdmmc_command_t cmd = {};
sdmmc_request_t req = {};
cmd.opcode = MMC_SEND_EXT_CSD;
cmd.arg = 0;
cmd.flags = SDMMC_RSP_R1;
req.data = ext_csd;
req.blksz = 512;
req.num_blocks = 1;
req.is_read = true;
req.is_multi_block = false;
req.is_auto_cmd12 = false;
/* Try to send the command. */
if (!sdmmc_send_cmd(device->sdmmc, &cmd, &req, 0)) {
return 0;
}
uint32_t resp = 0;
/* Try to load back the response. */
if (!sdmmc_load_response(device->sdmmc, SDMMC_RSP_R1, &resp)) {
return 0;
}
/* Evaluate the response. */
if (is_sdmmc_device_r1_error(resp)) {
return 0;
} else {
return 1;
}
}
static int sdmmc_mmc_set_relative_addr(sdmmc_device_t *device) {
/* Try to send the command. */
return sdmmc_device_send_r1_cmd(device, MMC_SET_RELATIVE_ADDR, (device->rca << 16), false, 0, 0xFFFFFFFF);
}
static int sdmmc_mmc_switch(sdmmc_device_t *device, uint32_t arg) {
/* Try to send the command. */
return sdmmc_device_send_r1_cmd(device, MMC_SWITCH, arg, true, 0, 0xFFFFFFFF);
}
static int sdmmc_mmc_select_bus_width(sdmmc_device_t *device, SdmmcBusWidth bus_width) {
uint32_t arg = 0;
/* Choose the argument for the switch command. */
switch (bus_width) {
case SDMMC_BUS_WIDTH_1BIT:
return 1;
case SDMMC_BUS_WIDTH_4BIT:
arg = (((MMC_SWITCH_MODE_WRITE_BYTE) << 24) | ((EXT_CSD_BUS_WIDTH) << 16) | ((EXT_CSD_BUS_WIDTH_4) << 8));
break;
case SDMMC_BUS_WIDTH_8BIT:
arg = (((MMC_SWITCH_MODE_WRITE_BYTE) << 24) | ((EXT_CSD_BUS_WIDTH) << 16) | ((EXT_CSD_BUS_WIDTH_8) << 8));
break;
default:
return 0;
}
/* Try to switch the bus width. */
if (sdmmc_mmc_switch(device, arg) && sdmmc_device_send_status(device)) {
sdmmc_select_bus_width(device->sdmmc, bus_width);
return 1;
}
return 0;
}
static int sdmmc_mmc_select_hs(sdmmc_device_t *device, bool ignore_status) {
uint32_t arg = (((MMC_SWITCH_MODE_WRITE_BYTE) << 24) | ((EXT_CSD_HS_TIMING) << 16) | ((EXT_CSD_TIMING_HS) << 8));
/* Try to switch to HS. */
if (!sdmmc_mmc_switch(device, arg)) {
return 0;
}
/* Check the status if necessary. */
if (!ignore_status && !sdmmc_device_send_status(device)) {
return 0;
}
/* Reconfigure the internal clock. */
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_MMC_HS)) {
return 0;
}
/* Check the status if necessary. */
if (!ignore_status && !sdmmc_device_send_status(device)) {
return 0;
}
return 1;
}
static int sdmmc_mmc_select_hs200(sdmmc_device_t *device) {
uint32_t arg = (((MMC_SWITCH_MODE_WRITE_BYTE) << 24) | ((EXT_CSD_HS_TIMING) << 16) | ((EXT_CSD_TIMING_HS200) << 8));
/* Try to switch to HS200. */
if (!sdmmc_mmc_switch(device, arg)) {
return 0;
}
/* Reconfigure the internal clock. */
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_MMC_HS200)) {
return 0;
}
/* Execute tuning procedure. */
if (!sdmmc_execute_tuning(device->sdmmc, SDMMC_SPEED_MMC_HS200, MMC_SEND_TUNING_BLOCK_HS200)) {
return 0;
}
/* Peek the current status. */
return sdmmc_device_send_status(device);
}
static int sdmmc_mmc_select_hs400(sdmmc_device_t *device) {
uint32_t arg = 0;
/* Switch to HS200 first. */
if (!sdmmc_mmc_select_hs200(device)) {
return 0;
}
/* Fetch and set the tuning tap value. */
sdmmc_set_tuning_tap_val(device->sdmmc);
/* Switch to HS. */
if (!sdmmc_mmc_select_hs(device, true)) {
return 0;
}
arg = (((MMC_SWITCH_MODE_WRITE_BYTE) << 24) | ((EXT_CSD_BUS_WIDTH) << 16) | ((EXT_CSD_DDR_BUS_WIDTH_8) << 8));
/* Try to switch to 8bit bus. */
if (!sdmmc_mmc_switch(device, arg)) {
return 0;
}
arg = (((MMC_SWITCH_MODE_WRITE_BYTE) << 24) | ((EXT_CSD_HS_TIMING) << 16) | ((EXT_CSD_TIMING_HS400) << 8));
/* Try to switch to HS400. */
if (!sdmmc_mmc_switch(device, arg)) {
return 0;
}
/* Reconfigure the internal clock. */
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_MMC_HS400)) {
return 0;
}
/* Peek the current status. */
return sdmmc_device_send_status(device);
}
static int sdmmc_mmc_select_timing(sdmmc_device_t *device, SdmmcBusSpeed bus_speed) {
if ((bus_speed == SDMMC_SPEED_MMC_HS400) &&
(device->sdmmc->bus_width == SDMMC_BUS_WIDTH_8BIT) &&
(device->ext_csd.raw_card_type & EXT_CSD_CARD_TYPE_HS400_1_8V)) {
/* Switch to HS400. */
return sdmmc_mmc_select_hs400(device);
} else if (((bus_speed == SDMMC_SPEED_MMC_HS400) || (bus_speed == SDMMC_SPEED_MMC_HS200)) &&
((device->sdmmc->bus_width == SDMMC_BUS_WIDTH_8BIT) || (device->sdmmc->bus_width == SDMMC_BUS_WIDTH_4BIT)) &&
(device->ext_csd.raw_card_type & EXT_CSD_CARD_TYPE_HS200_1_8V)) {
/* Switch to HS200. */
return sdmmc_mmc_select_hs200(device);
} else if (device->ext_csd.raw_card_type & EXT_CSD_CARD_TYPE_HS_52) {
/* Switch to HS. */
return sdmmc_mmc_select_hs(device, false);
}
return 0;
}
static int sdmmc_mmc_select_bkops(sdmmc_device_t *device) {
uint32_t arg = (((MMC_SWITCH_MODE_SET_BITS) << 24) | ((EXT_CSD_BKOPS_EN) << 16) | ((EXT_CSD_BKOPS_LEVEL_2) << 8));
/* Try to enable bkops. */
if (!sdmmc_mmc_switch(device, arg)) {
return 0;
}
/* Peek the current status. */
return sdmmc_device_send_status(device);
}
int sdmmc_mmc_select_partition(sdmmc_device_t *device, SdmmcPartitionNum partition) {
uint32_t arg = (((MMC_SWITCH_MODE_WRITE_BYTE) << 24) | ((EXT_CSD_PART_CONFIG) << 16) | ((partition) << 8));
/* Try to change the active partition. */
if (!sdmmc_mmc_switch(device, arg)) {
return 0;
}
/* Peek the current status. */
return sdmmc_device_send_status(device);
}
int sdmmc_device_mmc_init(sdmmc_device_t *device, sdmmc_t *sdmmc, SdmmcBusWidth bus_width, SdmmcBusSpeed bus_speed) {
uint32_t cid[4] = {0};
uint32_t csd[4] = {0};
uint8_t ext_csd[512] = {0};
/* Initialize our device's struct. */
memset(device, 0, sizeof(sdmmc_device_t));
/* Try to initialize the driver. */
if (!sdmmc_init(sdmmc, SDMMC_4, SDMMC_VOLTAGE_1V8, SDMMC_BUS_WIDTH_1BIT, SDMMC_SPEED_MMC_IDENT)) {
sdmmc_error(sdmmc, "Failed to initialize the SDMMC driver!");
return 0;
}
/* Bind the underlying driver. */
device->sdmmc = sdmmc;
/* Set RCA. */
device->rca = 0x01;
sdmmc_info(sdmmc, "SDMMC driver was successfully initialized for eMMC!");
/* Apply at least 74 clock cycles. eMMC should be ready afterwards. */
udelay((74000 + sdmmc->internal_divider - 1) / sdmmc->internal_divider);
/* Instruct the eMMC to go idle. */
if (!sdmmc_device_go_idle(device)) {
sdmmc_error(sdmmc, "Failed to go idle!");
return 0;
}
sdmmc_info(sdmmc, "eMMC went idle!");
/* Get the eMMC's operating conditions. */
if (!sdmmc_mmc_send_op_cond(device, SDMMC_VOLTAGE_1V8)) {
sdmmc_error(sdmmc, "Failed to send op cond!");
return 0;
}
sdmmc_info(sdmmc, "Sent op cond to eMMC!");
/* Get the eMMC's CID. */
if (!sdmmc_device_send_cid(device, cid)) {
sdmmc_error(sdmmc, "Failed to get CID!");
return 0;
}
sdmmc_info(sdmmc, "Got CID from eMMC!");
/* Set the eMMC's RCA. */
if (!sdmmc_mmc_set_relative_addr(device)) {
sdmmc_error(sdmmc, "Failed to set RCA!");
return 0;
}
sdmmc_info(sdmmc, "RCA is now set in eMMC!");
/* Get the eMMC card's CSD. */
if (!sdmmc_device_send_csd(device, csd)) {
sdmmc_error(sdmmc, "Failed to get CSD!");
return 0;
}
sdmmc_info(sdmmc, "Got CSD from eMMC!");
/* Decode and save the CSD. */
if (!sdmmc_mmc_decode_csd(device, csd)) {
sdmmc_warn(sdmmc, "Got unknown CSD structure (0x%08x)!", device->csd.structure);
}
/* Reconfigure the internal clock. */
if (!sdmmc_select_speed(device->sdmmc, SDMMC_SPEED_MMC_LEGACY)) {
sdmmc_error(sdmmc, "Failed to apply the correct bus speed!");
return 0;
}
sdmmc_info(sdmmc, "Speed mode has been adjusted!");
/* Select the eMMC card. */
if (!sdmmc_device_select_card(device)) {
sdmmc_error(sdmmc, "Failed to select eMMC card!");
return 0;
}
sdmmc_info(sdmmc, "eMMC card is now selected!");
/* Change the eMMC's block length. */
if (!sdmmc_device_set_blocklen(device, 512)) {
sdmmc_error(sdmmc, "Failed to set eMMC's block length!");
return 0;
}
sdmmc_info(sdmmc, "eMMC's block length is now 512!");
/* Only specification version 4 and later support the next features. */
if (device->csd.mmca_vsn < CSD_SPEC_VER_4) {
return 1;
}
/* Change the eMMC's bus width. */
if (!sdmmc_mmc_select_bus_width(device, bus_width)) {
sdmmc_error(sdmmc, "Failed to set eMMC's bus width!");
return 0;
}
sdmmc_info(sdmmc, "eMMC's bus width has been adjusted!");
/* Get the eMMC's extended CSD. */
if (!sdmmc_mmc_send_ext_csd(device, ext_csd)) {
sdmmc_error(sdmmc, "Failed to get EXT_CSD!");
return 0;
}
sdmmc_info(sdmmc, "Got EXT_CSD from eMMC!");
/* Decode and save the extended CSD. */
sdmmc_mmc_decode_ext_csd(device, ext_csd);
/* Decode and save the CID. */
sdmmc_mmc_decode_cid(device, cid);
/* TODO: Handle automatic BKOPS properly. Leave it disabled for now. */
if (false && device->ext_csd.bkops && !(device->ext_csd.auto_bkops_en & EXT_CSD_AUTO_BKOPS_MASK)) {
sdmmc_mmc_select_bkops(device);
sdmmc_info(sdmmc, "BKOPS is enabled!");
} else {
sdmmc_info(sdmmc, "BKOPS is disabled!");
}
/* Switch to high speed mode. */
if (!sdmmc_mmc_select_timing(device, bus_speed)) {
sdmmc_error(sdmmc, "Failed to switch to high speed mode!");
return 0;
}
sdmmc_info(sdmmc, "Switched to high speed mode!");
/* Correct any inconsistent states. */
sdmmc_adjust_sd_clock(sdmmc);
return 1;
}