mirror of
				https://github.com/Atmosphere-NX/Atmosphere.git
				synced 2025-10-31 11:15:51 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1543 lines
		
	
	
		
			46 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1543 lines
		
	
	
		
			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"
 | |
| #include "../timers.h"
 | |
| 
 | |
| #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_UHS_SDR104))
 | |
|             return 0;
 | |
| 
 | |
|         /* Run tuning. */
 | |
|         if (!sdmmc_execute_tuning(device->sdmmc, SDMMC_SPEED_UHS_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_UHS_SDR50))
 | |
|             return 0;
 | |
| 
 | |
|         /* Run tuning. */
 | |
|         if (!sdmmc_execute_tuning(device->sdmmc, SDMMC_SPEED_UHS_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_UHS_SDR12))
 | |
|             return 0;
 | |
| 
 | |
|         /* Run tuning. */
 | |
|         if (!sdmmc_execute_tuning(device->sdmmc, SDMMC_SPEED_UHS_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_UHS_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_INIT))
 | |
|     {
 | |
|         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_UHS_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_LEGACY))
 | |
|         {
 | |
|             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_LEGACY)))
 | |
|     {
 | |
|         /* 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_INIT))
 | |
|     {
 | |
|         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;
 | |
| } |