/* Copyright Statement: * * This software/firmware and related documentation ("MediaTek Software") are * protected under relevant copyright laws. The information contained herein * is confidential and proprietary to MediaTek Inc. and/or its licensors. * Without the prior written permission of MediaTek inc. and/or its licensors, * any reproduction, modification, use or disclosure of MediaTek Software, * and information contained herein, in whole or in part, shall be strictly prohibited. */ /* MediaTek Inc. (C) 2015. All rights reserved. * * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE") * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE, * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE, * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE. */ /*=======================================================================*/ /* HEADER FILES */ /*=======================================================================*/ #include "msdc.h" #include "mmc_common_inter.h" #include "mmc_core.h" #define NR_MMC (MSDC_MAX_NUM) static struct mmc_host sd_host[NR_MMC]; static struct mmc_card sd_card[NR_MMC]; 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 char mmc_tran_mant[] = { 0, 10, 12, 13, 15, 20, 26, 30, 35, 40, 45, 52, 55, 60, 70, 80, }; static const unsigned int tacc_exp[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, }; static const unsigned int tacc_mant[] = { 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, }; /**** valid size: 1~32 ****/ static u32 unstuff_bits(u32 *resp, u32 start, u32 size) { const u32 __mask = (~(u32)0) >> (32 - size); const int __off = 3 - ((start) / 32); const int __shft = (start) & 31; u32 __res; __res = resp[__off] >> __shft; if ((size) + __shft >= 32) __res |= resp[__off-1] << (32 - __shft); return __res & __mask; } #ifdef MMC_PROFILING static void mmc_prof_card_init(void *data, ulong id, ulong counts) { int err = (int)data; if (!err) { msdc_pr_info("[SD%d] Init Card, %d counts, %d us\n", id, counts, counts * 30 + counts * 16960 / 32768); } } static void mmc_prof_read(void *data, ulong id, ulong counts) { struct mmc_op_perf *perf = (struct mmc_op_perf *)data; struct mmc_op_report *rpt; u32 blksz = perf->host->blklen; u32 blkcnt = (u32)id; if (blkcnt > 1) rpt = &perf->multi_blks_read; else rpt = &perf->single_blk_read; rpt->count++; rpt->total_size += blkcnt * blksz; rpt->total_time += counts; if ((counts < rpt->min_time) || (rpt->min_time == 0)) rpt->min_time = counts; if ((counts > rpt->max_time) || (rpt->max_time == 0)) rpt->max_time = counts; msdc_pr_info("[SD%d] Read %d bytes, %d counts, %d us, %d KB/s, Avg: %d KB/s\n", perf->host->id, blkcnt * blksz, counts, counts * 30 + counts * 16960 / 32768, blkcnt * blksz * 32 / (counts ? counts : 1), ((rpt->total_size / 1024) * 32768) / rpt->total_time); } static void mmc_prof_write(void *data, ulong id, ulong counts) { struct mmc_op_perf *perf = (struct mmc_op_perf *)data; struct mmc_op_report *rpt; u32 blksz = perf->host->blklen; u32 blkcnt = (u32)id; if (blkcnt > 1) rpt = &perf->multi_blks_write; else rpt = &perf->single_blk_write; rpt->count++; rpt->total_size += blkcnt * blksz; rpt->total_time += counts; if ((counts < rpt->min_time) || (rpt->min_time == 0)) rpt->min_time = counts; if ((counts > rpt->max_time) || (rpt->max_time == 0)) rpt->max_time = counts; msdc_pr_info("[SD%d] Write %d bytes, %d counts, %d us, %d KB/s, Avg: %d KB/s\n", perf->host->id, blkcnt * blksz, counts, counts * 30 + counts * 16960 / 32768, blkcnt * blksz * 32 / (counts ? counts : 1), ((rpt->total_size / 1024) * 32768) / rpt->total_time); } #endif #if MMC_DEBUG void mmc_dump_card_status(u32 card_status) { msdc_dump_card_status(card_status); } static void mmc_dump_ocr_reg(u32 resp) { msdc_dump_ocr_reg(resp); } static void mmc_dump_rca_resp(u32 resp) { msdc_dump_rca_resp(resp); } static void mmc_dump_tuning_blk(u8 *buf) { int i; for (i = 0; i < 16; i++) { msdc_pr_info("[TBLK%d] %x%x%x%x%x%x%x%x\n", i, (buf[(i<<2)] >> 4) & 0xF, buf[(i<<2)] & 0xF, (buf[(i<<2)+1] >> 4) & 0xF, buf[(i<<2)+1] & 0xF, (buf[(i<<2)+2] >> 4) & 0xF, buf[(i<<2)+2] & 0xF, (buf[(i<<2)+3] >> 4) & 0xF, buf[(i<<2)+3] & 0xF); } } static void mmc_dump_csd(struct mmc_card *card) { struct mmc_csd *csd = &card->csd; u32 *resp = card->raw_csd; int i; unsigned int csd_struct; static char *sd_csd_ver[] = {"v1.0", "v2.0"}; static char *mmc_csd_ver[] = {"v1.0", "v1.1", "v1.2", "Ver. in EXT_CSD"}; static char *mmc_cmd_cls[] = {"basic", "stream read", "block read", "stream write", "block write", "erase", "write prot", "lock card", "app-spec", "I/O", "rsv.", "rsv." }; static char *sd_cmd_cls[] = {"basic", "rsv.", "block read", "rsv.", "block write", "erase", "write prot", "lock card", "app-spec", "I/O", "switch", "rsv." }; if (mmc_card_sd(card)) { csd_struct = unstuff_bits(resp, 126, 2); msdc_pr_info("[CSD] CSD %s\n", sd_csd_ver[csd_struct]); msdc_pr_info("[CSD] TACC_NS: %d ns, TACC_CLKS: %d clks\n", csd->tacc_ns, csd->tacc_clks); if (csd_struct == 1) { msdc_pr_info("[CSD] Read/Write Blk Len = 512bytes\n"); } else { msdc_pr_info("[CSD] Read Blk Len = %d, Write Blk Len = %d\n", 1 << csd->read_blkbits, 1 << csd->write_blkbits); } msdc_pr_info("[CSD] CMD Class:"); for (i = 0; i < 12; i++) { if ((csd->cmdclass >> i) & 0x1) msdc_pr_info("'%s' ", sd_cmd_cls[i]); } msdc_pr_info("\n"); } else { csd_struct = unstuff_bits(resp, 126, 2); msdc_pr_info("[CSD] CSD %s\n", mmc_csd_ver[csd_struct]); msdc_pr_info("[CSD] MMCA Spec v%d\n", csd->mmca_vsn); msdc_pr_info("[CSD] TACC_NS: %d ns, TACC_CLKS: %d clks\n", csd->tacc_ns, csd->tacc_clks); msdc_pr_info("[CSD] Read Blk Len = %d, Write Blk Len = %d\n", 1 << csd->read_blkbits, 1 << csd->write_blkbits); msdc_pr_info("[CSD] CMD Class:"); for (i = 0; i < 12; i++) { if ((csd->cmdclass >> i) & 0x1) msdc_pr_info("'%s' ", mmc_cmd_cls[i]); } msdc_pr_info("\n"); } } void mmc_dump_ext_csd(struct mmc_card *card) { u8 *ext_csd = &card->raw_ext_csd[0]; u32 tmp; char *rev[] = {"4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0", "5.1"}; msdc_pr_info("===========================================================\n"); msdc_pr_info("[EXT_CSD] EXT_CSD rev. : v1.%d (MMCv%s)\n", ext_csd[EXT_CSD_REV], rev[ext_csd[EXT_CSD_REV]]); msdc_pr_info("[EXT_CSD] CSD struct rev. : v1.%d\n", ext_csd[EXT_CSD_STRUCT]); msdc_pr_info("[EXT_CSD] Supported command sets : %xh\n", ext_csd[EXT_CSD_S_CMD_SET]); msdc_pr_info("[EXT_CSD] HPI features : %xh\n", ext_csd[EXT_CSD_HPI_FEATURE]); msdc_pr_info("[EXT_CSD] BG operations support : %xh\n", ext_csd[EXT_CSD_BKOPS_SUPP]); msdc_pr_info("[EXT_CSD] BG operations status : %xh\n", ext_csd[EXT_CSD_BKOPS_STATUS]); memcpy(&tmp, &ext_csd[EXT_CSD_CORRECT_PRG_SECTS_NUM], 4); msdc_pr_info("[EXT_CSD] Correct prg. sectors : %xh\n", tmp); msdc_pr_info("[EXT_CSD] 1st init time after part. : %d ms\n", ext_csd[EXT_CSD_INI_TIMEOUT_AP] * 100); msdc_pr_info("[EXT_CSD] Min. write perf.(DDR,52MH,8b): %xh\n", ext_csd[EXT_CSD_MIN_PERF_DDR_W_8_52]); msdc_pr_info("[EXT_CSD] Min. read perf. (DDR,52MH,8b): %xh\n", ext_csd[EXT_CSD_MIN_PERF_DDR_R_8_52]); msdc_pr_info("[EXT_CSD] TRIM timeout: %d ms\n", ext_csd[EXT_CSD_TRIM_MULT] & 0xFF * 300); msdc_pr_info("[EXT_CSD] Secure feature support: %xh\n", ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT]); msdc_pr_info("[EXT_CSD] Secure erase timeout : %d ms\n", 300 * ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT] * ext_csd[EXT_CSD_SEC_ERASE_MULT]); msdc_pr_info("[EXT_CSD] Secure trim timeout : %d ms\n", 300 * ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT] * ext_csd[EXT_CSD_SEC_TRIM_MULT]); msdc_pr_info("[EXT_CSD] Access size : %d bytes\n", ext_csd[EXT_CSD_ACC_SIZE] * 512); msdc_pr_info("[EXT_CSD] HC erase unit size : %d kbytes\n", ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 512); msdc_pr_info("[EXT_CSD] HC erase timeout : %d ms\n", ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT] * 300); msdc_pr_info("[EXT_CSD] HC write prot grp size: %d kbytes\n", 512 * ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * ext_csd[EXT_CSD_HC_WP_GPR_SIZE]); msdc_pr_info("[EXT_CSD] HC erase grp def. : %xh\n", ext_csd[EXT_CSD_ERASE_GRP_DEF]); msdc_pr_info("[EXT_CSD] Reliable write sect count: %xh\n", ext_csd[EXT_CSD_REL_WR_SEC_C]); msdc_pr_info("[EXT_CSD] Sleep current (VCC) : %xh\n", ext_csd[EXT_CSD_S_C_VCC]); msdc_pr_info("[EXT_CSD] Sleep current (VCCQ): %xh\n", ext_csd[EXT_CSD_S_C_VCCQ]); msdc_pr_info("[EXT_CSD] Sleep/awake timeout : %d ns\n", 100 * (2 << ext_csd[EXT_CSD_S_A_TIMEOUT])); memcpy(&tmp, &ext_csd[EXT_CSD_SEC_CNT], 4); msdc_pr_info("[EXT_CSD] Sector count : %xh\n", tmp); msdc_pr_info("[EXT_CSD] Min. WR Perf. (52MH,8b): %xh\n", ext_csd[EXT_CSD_MIN_PERF_W_8_52]); msdc_pr_info("[EXT_CSD] Min. Read Perf.(52MH,8b): %xh\n", ext_csd[EXT_CSD_MIN_PERF_R_8_52]); msdc_pr_info("[EXT_CSD] Min. WR Perf. (26MH,8b,52MH,4b): %xh\n", ext_csd[EXT_CSD_MIN_PERF_W_8_26_4_25]); msdc_pr_info("[EXT_CSD] Min. Read Perf.(26MH,8b,52MH,4b): %xh\n", ext_csd[EXT_CSD_MIN_PERF_R_8_26_4_25]); msdc_pr_info("[EXT_CSD] Min. WR Perf. (26MH,4b): %xh\n", ext_csd[EXT_CSD_MIN_PERF_W_4_26]); msdc_pr_info("[EXT_CSD] Min. Read Perf.(26MH,4b): %xh\n", ext_csd[EXT_CSD_MIN_PERF_R_4_26]); msdc_pr_info("[EXT_CSD] Power class: %x\n", ext_csd[EXT_CSD_PWR_CLASS]); msdc_pr_info("[EXT_CSD] Power class(DDR,52MH,3.6V): %xh\n", ext_csd[EXT_CSD_PWR_CL_DDR_52_360]); msdc_pr_info("[EXT_CSD] Power class(DDR,52MH,1.9V): %xh\n", ext_csd[EXT_CSD_PWR_CL_DDR_52_195]); msdc_pr_info("[EXT_CSD] Power class(26MH,3.6V) : %xh\n", ext_csd[EXT_CSD_PWR_CL_26_360]); msdc_pr_info("[EXT_CSD] Power class(52MH,3.6V) : %xh\n", ext_csd[EXT_CSD_PWR_CL_52_360]); msdc_pr_info("[EXT_CSD] Power class(26MH,1.9V) : %xh\n", ext_csd[EXT_CSD_PWR_CL_26_195]); msdc_pr_info("[EXT_CSD] Power class(52MH,1.9V) : %xh\n", ext_csd[EXT_CSD_PWR_CL_52_195]); msdc_pr_info("[EXT_CSD] Part. switch timing : %xh\n", ext_csd[EXT_CSD_PART_SWITCH_TIME]); msdc_pr_info("[EXT_CSD] Out-of-INTR busy timing: %xh\n", ext_csd[EXT_CSD_OUT_OF_INTR_TIME]); msdc_pr_info("[EXT_CSD] Card type : %xh\n", ext_csd[EXT_CSD_CARD_TYPE]); msdc_pr_info("[EXT_CSD] Command set : %xh\n", ext_csd[EXT_CSD_CMD_SET]); msdc_pr_info("[EXT_CSD] Command set rev.: %xh\n", ext_csd[EXT_CSD_CMD_SET_REV]); msdc_pr_info("[EXT_CSD] HS timing : %xh\n", ext_csd[EXT_CSD_HS_TIMING]); msdc_pr_info("[EXT_CSD] Bus width : %xh\n", ext_csd[EXT_CSD_BUS_WIDTH]); msdc_pr_info("[EXT_CSD] Erase memory content : %xh\n", ext_csd[EXT_CSD_ERASED_MEM_CONT]); msdc_pr_info("[EXT_CSD] Partition config : %xh\n", ext_csd[EXT_CSD_PART_CFG]); msdc_pr_info("[EXT_CSD] Boot partition size : %d kbytes\n", ext_csd[EXT_CSD_BOOT_SIZE_MULT] * 128); msdc_pr_info("[EXT_CSD] Boot information : %xh\n", ext_csd[EXT_CSD_BOOT_INFO]); msdc_pr_info("[EXT_CSD] Boot config protection: %xh\n", ext_csd[EXT_CSD_BOOT_CONFIG_PROT]); msdc_pr_info("[EXT_CSD] Boot bus width : %xh\n", ext_csd[EXT_CSD_BOOT_BUS_WIDTH]); msdc_pr_info("[EXT_CSD] Boot area write prot : %xh\n", ext_csd[EXT_CSD_BOOT_WP]); msdc_pr_info("[EXT_CSD] User area write prot : %xh\n", ext_csd[EXT_CSD_USR_WP]); msdc_pr_info("[EXT_CSD] FW configuration : %xh\n", ext_csd[EXT_CSD_FW_CONFIG]); msdc_pr_info("[EXT_CSD] RPMB size : %d kbytes\n", ext_csd[EXT_CSD_RPMB_SIZE_MULT] * 128); msdc_pr_info("[EXT_CSD] Write rel. setting : %xh\n", ext_csd[EXT_CSD_WR_REL_SET]); msdc_pr_info("[EXT_CSD] Write rel. parameter: %xh\n", ext_csd[EXT_CSD_WR_REL_PARAM]); msdc_pr_info("[EXT_CSD] Start background ops : %xh\n", ext_csd[EXT_CSD_BKOPS_START]); msdc_pr_info("[EXT_CSD] Enable background ops: %xh\n", ext_csd[EXT_CSD_BKOPS_EN]); msdc_pr_info("[EXT_CSD] H/W reset function : %xh\n", ext_csd[EXT_CSD_RST_N_FUNC]); msdc_pr_info("[EXT_CSD] HPI management : %xh\n", ext_csd[EXT_CSD_HPI_MGMT]); memcpy(&tmp, &ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT], 4); msdc_pr_info("[EXT_CSD] Max. enhanced area size : %xh (%d kbytes)\n", tmp & 0x00FFFFFF, (tmp & 0x00FFFFFF) * 512 * ext_csd[EXT_CSD_HC_WP_GPR_SIZE] * ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]); msdc_pr_info("[EXT_CSD] Part. support : %xh\n", ext_csd[EXT_CSD_PART_SUPPORT]); msdc_pr_info("[EXT_CSD] Part. attribute: %xh\n", ext_csd[EXT_CSD_PART_ATTR]); msdc_pr_info("[EXT_CSD] Part. setting : %xh\n", ext_csd[EXT_CSD_PART_SET_COMPL]); msdc_pr_info("[EXT_CSD] General purpose 1 size : %xh (%d kbytes)\n", (ext_csd[EXT_CSD_GP1_SIZE_MULT + 0] | ext_csd[EXT_CSD_GP1_SIZE_MULT + 1] << 8 | ext_csd[EXT_CSD_GP1_SIZE_MULT + 2] << 16), (ext_csd[EXT_CSD_GP1_SIZE_MULT + 0] | ext_csd[EXT_CSD_GP1_SIZE_MULT + 1] << 8 | ext_csd[EXT_CSD_GP1_SIZE_MULT + 2] << 16) * 512 * ext_csd[EXT_CSD_HC_WP_GPR_SIZE] * ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]); msdc_pr_info("[EXT_CSD] General purpose 2 size : %xh (%d kbytes)\n", (ext_csd[EXT_CSD_GP2_SIZE_MULT + 0] | ext_csd[EXT_CSD_GP2_SIZE_MULT + 1] << 8 | ext_csd[EXT_CSD_GP2_SIZE_MULT + 2] << 16), (ext_csd[EXT_CSD_GP2_SIZE_MULT + 0] | ext_csd[EXT_CSD_GP2_SIZE_MULT + 1] << 8 | ext_csd[EXT_CSD_GP2_SIZE_MULT + 2] << 16) * 512 * ext_csd[EXT_CSD_HC_WP_GPR_SIZE] * ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]); msdc_pr_info("[EXT_CSD] General purpose 3 size : %xh (%d kbytes)\n", (ext_csd[EXT_CSD_GP3_SIZE_MULT + 0] | ext_csd[EXT_CSD_GP3_SIZE_MULT + 1] << 8 | ext_csd[EXT_CSD_GP3_SIZE_MULT + 2] << 16), (ext_csd[EXT_CSD_GP3_SIZE_MULT + 0] | ext_csd[EXT_CSD_GP3_SIZE_MULT + 1] << 8 | ext_csd[EXT_CSD_GP3_SIZE_MULT + 2] << 16) * 512 * ext_csd[EXT_CSD_HC_WP_GPR_SIZE] * ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]); msdc_pr_info("[EXT_CSD] General purpose 4 size : %xh (%d kbytes)\n", (ext_csd[EXT_CSD_GP4_SIZE_MULT + 0] | ext_csd[EXT_CSD_GP4_SIZE_MULT + 1] << 8 | ext_csd[EXT_CSD_GP4_SIZE_MULT + 2] << 16), (ext_csd[EXT_CSD_GP4_SIZE_MULT + 0] | ext_csd[EXT_CSD_GP4_SIZE_MULT + 1] << 8 | ext_csd[EXT_CSD_GP4_SIZE_MULT + 2] << 16) * 512 * ext_csd[EXT_CSD_HC_WP_GPR_SIZE] * ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]); msdc_pr_info("[EXT_CSD] Enh. user area size : %xh (%d kbytes)\n", (ext_csd[EXT_CSD_ENH_SIZE_MULT + 0] | ext_csd[EXT_CSD_ENH_SIZE_MULT + 1] << 8 | ext_csd[EXT_CSD_ENH_SIZE_MULT + 2] << 16), (ext_csd[EXT_CSD_ENH_SIZE_MULT + 0] | ext_csd[EXT_CSD_ENH_SIZE_MULT + 1] << 8 | ext_csd[EXT_CSD_ENH_SIZE_MULT + 2] << 16) * 512 * ext_csd[EXT_CSD_HC_WP_GPR_SIZE] * ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]); msdc_pr_info("[EXT_CSD] Enh. user area start: %xh\n", (ext_csd[EXT_CSD_ENH_START_ADDR + 0] | ext_csd[EXT_CSD_ENH_START_ADDR + 1] << 8 | ext_csd[EXT_CSD_ENH_START_ADDR + 2] << 16 | ext_csd[EXT_CSD_ENH_START_ADDR + 3]) << 24); msdc_pr_info("[EXT_CSD] Bad block mgmt mode: %xh\n", ext_csd[EXT_CSD_BADBLK_MGMT]); msdc_pr_info("===========================================================\n"); } #endif #if defined(FEATURE_MMC_CARD_DETECT) int mmc_card_avail(struct mmc_host *host) { return msdc_card_avail(host); } #endif #if defined(MMC_MSDC_DRV_CTP) int mmc_card_protected(struct mmc_host *host) { return msdc_card_protected(host); } #endif struct mmc_host *mmc_get_host(int id) { return &sd_host[id]; } struct mmc_card *mmc_get_card(int id) { return &sd_card[id]; } int mmc_cmd(struct mmc_host *host, struct mmc_command *cmd) { int err; int retry = cmd->retries; if (cmd->opcode == MMC_CMD_APP_CMD ) { host->app_cmd = 1; host->app_cmd_arg = cmd->arg; } else { host->app_cmd = 0; } do { err = msdc_cmd(host, cmd); if (err == MMC_ERR_NONE) break; /* Break retry, just retrun erase seq fail */ if (err == MMC_ERR_ERASE_SEQ) break; } while (retry--); return err; } static int mmc_app_cmd(struct mmc_host *host, struct mmc_command *cmd, u32 rca, int retries) { int err = MMC_ERR_FAILED; struct mmc_command appcmd; appcmd.opcode = MMC_CMD_APP_CMD; appcmd.arg = rca << 16; appcmd.rsptyp = RESP_R1; appcmd.retries = CMD_RETRIES; appcmd.timeout = CMD_TIMEOUT; do { err = mmc_cmd(host, &appcmd); if (err == MMC_ERR_NONE) err = mmc_cmd(host, cmd); if (err == MMC_ERR_NONE) break; } while (retries--); return err; } u32 mmc_select_voltage(struct mmc_host *host, u32 ocr) { int bit; ocr &= host->ocr_avail; bit = uffs(ocr); if (bit) { bit -= 1; ocr &= 3 << bit; } else { ocr = 0; } return ocr; } int mmc_go_idle(struct mmc_host *host) { struct mmc_command cmd; cmd.opcode = MMC_CMD_GO_IDLE_STATE; cmd.rsptyp = RESP_NONE; cmd.arg = 0; cmd.retries = CMD_RETRIES; cmd.timeout = CMD_TIMEOUT; return mmc_cmd(host, &cmd); } #if defined(MMC_MSDC_DRV_CTP) int mmc_go_irq_state(struct mmc_host *host, struct mmc_card *card) { struct mmc_command cmd; if (!(card->csd.cmdclass & CCC_IO_MODE)) { msdc_pr_err("[SD%d] Card doesn't support I/O mode for IRQ state\n", host->id); return MMC_ERR_FAILED; } cmd.opcode = MMC_CMD_GO_IRQ_STATE; cmd.rsptyp = RESP_R5; cmd.arg = 0; cmd.retries = CMD_RETRIES; cmd.timeout = CMD_TIMEOUT; return mmc_cmd(host, &cmd); } static int mmc_go_inactive(struct mmc_host *host, struct mmc_card *card) { struct mmc_command cmd; cmd.opcode = MMC_CMD_GO_INACTIVE_STATE; cmd.rsptyp = RESP_NONE; cmd.arg = 0; cmd.retries = CMD_RETRIES; cmd.timeout = CMD_TIMEOUT; return mmc_cmd(host, &cmd); } static int mmc_go_pre_idle(struct mmc_host *host, struct mmc_card *card) { struct mmc_command cmd; cmd.opcode = MMC_CMD_GO_IDLE_STATE; cmd.rsptyp = RESP_NONE; cmd.arg = 0xF0F0F0F0; cmd.retries = CMD_RETRIES; cmd.timeout = CMD_TIMEOUT; return mmc_cmd(host, &cmd); } #endif int mmc_sleep_awake(struct mmc_host *host, struct mmc_card *card, int sleep) { struct mmc_command cmd; u32 timeout; if (card->raw_ext_csd[EXT_CSD_S_A_TIMEOUT]) { timeout = ((1 << card->raw_ext_csd[EXT_CSD_S_A_TIMEOUT]) * 100) / 1000000; } else { timeout = CMD_TIMEOUT; } cmd.opcode = MMC_CMD_SLEEP_AWAKE; cmd.rsptyp = RESP_R1B; cmd.arg = (card->rca << 16) | (sleep << 15); cmd.retries = CMD_RETRIES; cmd.timeout = timeout; return mmc_cmd(host, &cmd); } int mmc_send_status(struct mmc_host *host, struct mmc_card *card, u32 *status) { int err; struct mmc_command cmd; cmd.opcode = MMC_CMD_SEND_STATUS; cmd.arg = card->rca << 16; cmd.rsptyp = RESP_R1; cmd.retries = CMD_RETRIES; cmd.timeout = CMD_TIMEOUT; err = mmc_cmd(host, &cmd); if (err == MMC_ERR_NONE) { *status = cmd.resp[0]; #if MMC_DEBUG mmc_dump_card_status(*status); #endif } return err; } static int mmc_send_if_cond(struct mmc_host *host, u32 ocr) { struct mmc_command cmd; int err; static const u8 test_pattern = 0xAA; u8 result_pattern; /* * To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND * before SD_APP_OP_COND. This command will harmlessly fail for * SD 1.0 cards. */ memset(&cmd, 0, sizeof(struct mmc_command)); cmd.opcode = SD_CMD_SEND_IF_COND; cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern; cmd.rsptyp = RESP_R1; cmd.retries = 0; cmd.timeout = CMD_TIMEOUT; err = mmc_cmd(host, &cmd); if (err != MMC_ERR_NONE) return err; result_pattern = cmd.resp[0] & 0xFF; if (result_pattern != test_pattern) return MMC_ERR_INVALID; return MMC_ERR_NONE; } static int mmc_sd_get_write_blocks(struct mmc_host *host, struct mmc_card *card, u32* num) { u32 base = host->base; struct mmc_command cmd; int err; int result = MMC_ERR_NONE; u8 buf[4]; cmd.opcode = SD_ACMD_SEND_NR_WR_BLOCKS; cmd.arg = 0; cmd.rsptyp = RESP_R1; cmd.retries = 3; cmd.timeout = CMD_TIMEOUT; #if defined(FEATURE_MMC_RD_TUNING) msdc_reset_tune_counter(host); do { #endif msdc_set_blknum(host, 1); msdc_set_blklen(host, 4); msdc_set_timeout(host, 100000000, 0); MSDC_SET_BIT32(MSDC_CFG, MSDC_CFG_PIO); err = mmc_app_cmd(host, &cmd, card->rca, CMD_RETRIES); if (err != MMC_ERR_NONE) return err; /* 32bits = 4 byte */ err = msdc_pio_read(host, (u32*)buf, 4); //msdc_pr_info("[%s:%d]err = %d\n", __func__, __LINE__, err); if (err != MMC_ERR_NONE) { msdc_abort_handler(host, 1); #if defined(FEATURE_MMC_RD_TUNING) result = msdc_tune_read(host); #else goto out; #endif } #if defined(FEATURE_MMC_RD_TUNING) } while (err && (result != MMC_ERR_READTUNEFAIL)); msdc_reset_tune_counter(host); #endif msdc_set_blklen(host, 512); if (err != MMC_ERR_NONE) { return err; } *num = buf[3] | buf[2] << 8 | buf[1] << 16 | buf[0] << 24; #if !defined(FEATURE_MMC_RD_TUNING) out: #endif return MMC_ERR_NONE; } static int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) { struct mmc_command cmd; int i, err = 0; cmd.opcode = MMC_CMD_SEND_OP_COND; cmd.arg = ocr; cmd.rsptyp = RESP_R3; cmd.retries = 0; cmd.timeout = CMD_TIMEOUT; for (i = 100; i; i--) { err = mmc_cmd(host, &cmd); if (err) break; /* if we're just probing, do a single pass */ if (ocr == 0) break; if (cmd.resp[0] & MMC_CARD_BUSY) break; err = MMC_ERR_TIMEOUT; mdelay(10); } if (!err && rocr) *rocr = cmd.resp[0]; return err; } static int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) { struct mmc_command cmd; int i, err = 0; cmd.opcode = SD_ACMD_SEND_OP_COND; cmd.arg = ocr; cmd.rsptyp = RESP_R3; cmd.retries = CMD_RETRIES; cmd.timeout = CMD_TIMEOUT; for (i = 100; i; i--) { err = mmc_app_cmd(host, &cmd, 0, CMD_RETRIES); if (err != MMC_ERR_NONE) break; if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0) break; err = MMC_ERR_TIMEOUT; mdelay(10); } if (rocr) *rocr = cmd.resp[0]; return err; } static int mmc_all_send_cid(struct mmc_host *host, u32 *cid) { int err; struct mmc_command cmd; /* send cid */ cmd.opcode = MMC_CMD_ALL_SEND_CID; cmd.arg = 0; cmd.rsptyp = RESP_R2; cmd.retries = CMD_RETRIES; cmd.timeout = CMD_TIMEOUT; err = mmc_cmd(host, &cmd); if (err != MMC_ERR_NONE) return err; memcpy(cid, cmd.resp, sizeof(u32) * 4); return MMC_ERR_NONE; } /* code size add 1KB*/ static void mmc_decode_cid(struct mmc_card *card) { u32 *resp = card->raw_cid; memset(&card->cid, 0, sizeof(struct mmc_cid)); card->cid.prod_name[4] = unstuff_bits(resp, 64, 8); card->cid.prod_name[3] = unstuff_bits(resp, 72, 8); card->cid.prod_name[2] = unstuff_bits(resp, 80, 8); card->cid.prod_name[1] = unstuff_bits(resp, 88, 8); card->cid.prod_name[0] = unstuff_bits(resp, 96, 8); if (mmc_card_sd(card)) { /* * SD doesn't currently have a version field so we will * have to assume we can parse this. */ card->cid.month = unstuff_bits(resp, 8, 4); card->cid.year = unstuff_bits(resp, 12, 8); card->cid.serial = unstuff_bits(resp, 24, 32); card->cid.fwrev = unstuff_bits(resp, 56, 4); card->cid.hwrev = unstuff_bits(resp, 60, 4); card->cid.oemid = unstuff_bits(resp, 104, 16); card->cid.manfid = unstuff_bits(resp, 120, 8); card->cid.year += 2000; /* SD cards year offset */ } else { /* * The selection of the format here is based upon published * specs from sandisk and from what people have reported. */ card->cid.year = unstuff_bits(resp, 8, 4) + 1997; card->cid.month = unstuff_bits(resp, 12, 4); card->cid.prod_name[5] = unstuff_bits(resp, 56, 8); switch (card->csd.mmca_vsn) { case 0: /* MMC v1.0 - v1.2 */ case 1: /* MMC v1.4 */ card->cid.serial = unstuff_bits(resp, 16, 24); card->cid.fwrev = unstuff_bits(resp, 40, 4); card->cid.hwrev = unstuff_bits(resp, 44, 4); card->cid.prod_name[6] = unstuff_bits(resp, 48, 8); card->cid.manfid = unstuff_bits(resp, 104, 24); break; case 2: /* MMC v2.0 - v2.2 */ case 3: /* MMC v3.1 - v3.3 */ case 4: /* MMC v4 */ card->cid.serial = unstuff_bits(resp, 16, 32); card->cid.oemid = unstuff_bits(resp, 104, 16); //card->cid.cbx = unstuff_bits(resp, 112, 2); card->cid.manfid = unstuff_bits(resp, 120, 8); break; default: msdc_pr_err("[SD%d] Unknown MMCA version %d\n", mmc_card_id(card), card->csd.mmca_vsn); break; } } } static int mmc_decode_csd(struct mmc_card *card) { struct mmc_csd *csd = &card->csd; unsigned int e, m, csd_struct; u32 *resp = card->raw_csd; /* common part; some part are updated later according to spec. */ csd_struct = unstuff_bits(resp, 126, 2); csd->csd_struct = csd_struct; /* For MMC * We only understand CSD structure v1.1 and v1.2. * v1.2 has extra information in bits 15, 11 and 10. */ if ( ( mmc_card_mmc(card) && ( csd_struct != CSD_STRUCT_VER_1_0 && csd_struct != CSD_STRUCT_VER_1_1 && csd_struct != CSD_STRUCT_VER_1_2 && csd_struct != CSD_STRUCT_EXT_CSD ) ) || ( mmc_card_sd(card) && ( csd_struct != 0 && csd_struct!=1 ) ) ) { msdc_pr_err("[SD%d] Unknown CSD ver %d\n", mmc_card_id(card), csd_struct); return MMC_ERR_INVALID; } m = unstuff_bits(resp, 99, 4); e = unstuff_bits(resp, 96, 3); csd->max_dtr = tran_exp[e] * tran_mant[m]; /* update later according to spec. */ csd->read_blkbits = unstuff_bits(resp, 80, 4); csd->write_prot_grpsz = unstuff_bits(resp, 32, 7); csd->write_prot_grp = unstuff_bits(resp, 31, 1); csd->write_blkbits = unstuff_bits(resp, 22, 4); #if !defined(FEATURE_MMC_SLIM) csd->cmdclass = unstuff_bits(resp, 84, 12); csd->read_partial = unstuff_bits(resp, 79, 1); csd->write_misalign = unstuff_bits(resp, 78, 1); csd->read_misalign = unstuff_bits(resp, 77, 1); csd->dsr = unstuff_bits(resp, 76, 1); csd->r2w_factor = unstuff_bits(resp, 26, 3); csd->write_partial = unstuff_bits(resp, 21, 1); csd->copy = unstuff_bits(resp, 14, 1); csd->perm_wr_prot = unstuff_bits(resp, 13, 1); csd->tmp_wr_prot = unstuff_bits(resp, 12, 1); m = unstuff_bits(resp, 115, 4); e = unstuff_bits(resp, 112, 3); csd->tacc_ns = (tacc_exp[e] * tacc_mant[m] + 9) / 10; csd->tacc_clks = unstuff_bits(resp, 104, 8) * 100; #endif e = unstuff_bits(resp, 47, 3); m = unstuff_bits(resp, 62, 12); csd->capacity = (1 + m) << (e + 2); //Specific part if (mmc_card_sd(card)) { csd->erase_blk_en = unstuff_bits(resp, 46, 1); csd->erase_sctsz = unstuff_bits(resp, 39, 7) + 1; switch (csd_struct) { case 0: break; case 1: /* * This is a block-addressed SDHC card. Most * interesting fields are unused and have fixed * values. To avoid getting tripped by buggy cards, * we assume those fixed values ourselves. */ mmc_card_set_blockaddr(card); m = unstuff_bits(resp, 48, 22); csd->capacity = (1 + m) << 10; csd->read_blkbits = 9; csd->write_blkbits = 9; #if !defined(FEATURE_MMC_SLIM) csd->tacc_ns = 0; /* Unused */ csd->tacc_clks = 0; /* Unused */ csd->read_partial = 0; csd->write_misalign = 0; csd->read_misalign = 0; csd->r2w_factor = 4; /* Unused */ csd->write_partial = 0; #endif break; } } else { csd->mmca_vsn = unstuff_bits(resp, 122, 4); csd->write_prot_grpsz = unstuff_bits(resp, 32, 5); csd->erase_sctsz = (unstuff_bits(resp, 42, 5) + 1) * (unstuff_bits(resp, 37, 5) + 1); } #if MMC_DEBUG mmc_dump_csd(card); #endif return 0; } static void mmc_decode_ext_csd(struct mmc_card *card) { u8 *ext_csd = &card->raw_ext_csd[0]; card->ext_csd.sectors = ext_csd[EXT_CSD_SEC_CNT + 0] << 0 | ext_csd[EXT_CSD_SEC_CNT + 1] << 8 | ext_csd[EXT_CSD_SEC_CNT + 2] << 16 | ext_csd[EXT_CSD_SEC_CNT + 3] << 24; card->ext_csd.hc_erase_grp_sz = ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 512 * 1024; card->ext_csd.hc_wp_grp_sz = ext_csd[EXT_CSD_HC_WP_GPR_SIZE]; card->ext_csd.boot_part_sz = ext_csd[EXT_CSD_BOOT_SIZE_MULT] * 128 * 1024; card->ext_csd.rpmb_sz = ext_csd[EXT_CSD_RPMB_SIZE_MULT] * 128 * 1024; card->ext_csd.gp_sz_mult[0] = ext_csd[EXT_CSD_GP1_SIZE_MULT + 2] * 256 * 256 + ext_csd[EXT_CSD_GP1_SIZE_MULT + 1] * 256 + ext_csd[EXT_CSD_GP1_SIZE_MULT + 0]; card->ext_csd.gp_sz_mult[1] = ext_csd[EXT_CSD_GP2_SIZE_MULT + 2] * 256 * 256 + ext_csd[EXT_CSD_GP2_SIZE_MULT + 1] * 256 + ext_csd[EXT_CSD_GP2_SIZE_MULT + 0]; card->ext_csd.gp_sz_mult[2] = ext_csd[EXT_CSD_GP3_SIZE_MULT + 2] * 256 * 256 + ext_csd[EXT_CSD_GP3_SIZE_MULT + 1] * 256 + ext_csd[EXT_CSD_GP3_SIZE_MULT + 0]; card->ext_csd.gp_sz_mult[3] = ext_csd[EXT_CSD_GP4_SIZE_MULT + 2] * 256 * 256 + ext_csd[EXT_CSD_GP4_SIZE_MULT + 1] * 256 + ext_csd[EXT_CSD_GP4_SIZE_MULT + 0]; #if !defined(FEATURE_MMC_SLIM) card->ext_csd.rev = ext_csd[EXT_CSD_REV]; card->ext_csd.trim_tmo_ms = ext_csd[EXT_CSD_TRIM_MULT] * 300; card->ext_csd.boot_info = ext_csd[EXT_CSD_BOOT_INFO]; card->ext_csd.access_sz = (ext_csd[EXT_CSD_ACC_SIZE] & 0xf) * 512; card->ext_csd.erased_mem_cont = ext_csd[EXT_CSD_ERASED_MEM_CONT]; card->ext_csd.part_en = ext_csd[EXT_CSD_PART_SUPPORT] & EXT_CSD_PART_SUPPORT_PART_EN ? 1 : 0; card->ext_csd.enh_attr_en = ext_csd[EXT_CSD_PART_SUPPORT] & EXT_CSD_PART_SUPPORT_ENH_ATTR_EN ? 1 : 0; card->ext_csd.enh_start_addr = (ext_csd[EXT_CSD_ENH_START_ADDR + 0] | ext_csd[EXT_CSD_ENH_START_ADDR + 1] << 8 | ext_csd[EXT_CSD_ENH_START_ADDR + 2] << 16 | ext_csd[EXT_CSD_ENH_START_ADDR + 3] << 24); card->ext_csd.enh_sz = (ext_csd[EXT_CSD_ENH_SIZE_MULT + 0] | ext_csd[EXT_CSD_ENH_SIZE_MULT + 1] << 8 | ext_csd[EXT_CSD_ENH_SIZE_MULT + 2] << 16) * 512 * 1024 * ext_csd[EXT_CSD_HC_WP_GPR_SIZE] * ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; #endif if (card->ext_csd.sectors) mmc_card_set_blockaddr(card); card->ext_csd.hs_max_dtr = 0; if ((ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_HS400_1_2V) || (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_HS400_1_8V)) { card->ext_csd.hs_max_dtr = 200000000; card->ext_csd.ddr_support = 1; card->version = EMMC_VER_50; } else if ((ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_HS200_1_2V) || (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_HS200_1_8V)) { card->ext_csd.hs_max_dtr = 200000000; if ((ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_DDR_52_1_2V) || (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_DDR_52)) { card->ext_csd.ddr_support = 1; } card->version = EMMC_VER_45; } else if ((ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_DDR_52_1_2V) || (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_DDR_52)) { card->ext_csd.ddr_support = 1; card->ext_csd.hs_max_dtr = 52000000; card->version = EMMC_VER_44; } else if (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_52) { card->ext_csd.hs_max_dtr = 52000000; card->version = EMMC_VER_43; } else if ((ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_26)) { card->ext_csd.hs_max_dtr = 26000000; card->version = EMMC_VER_42; } else { /* MMC v4 spec says this cannot happen */ msdc_pr_err("[SD%d] MMCv4 but HS unsupported\n", card->host->id); } #ifdef FEATURE_MMC_CMDQ mmc_decode_ext_csd_for_cmdq(card); #endif card->ext_csd.usr_wp = ext_csd[EXT_CSD_USR_WP]; card->ext_csd.boot_wp = ext_csd[EXT_CSD_BOOT_WP]; //compute wp_size if (ext_csd[EXT_CSD_ERASE_GRP_DEF] & EXT_CSD_ERASE_GRP_DEF_EN) { //should be the sector size card->wp_size = (card->ext_csd.hc_wp_grp_sz * card->ext_csd.hc_erase_grp_sz) >> 9; msdc_pr_info("[SD%d] EXT_CSD_ERASE_GRP_DEF is On, wp_size = %dKB\n", card->host->id, card->wp_size/2); } else { card->wp_size = (card->csd.write_prot_grpsz + 1) * card->csd.erase_sctsz; msdc_pr_info("[SD%d] EXT_CSD_ERASE_GRP_DEF is Off, wp_size = %dKB\n" "[SD%d] csd.write_prot_grpsz = %d, csd.erase_sctsz = %d\n", card->host->id, card->wp_size/2, card->host->id, card->csd.write_prot_grpsz, card->csd.erase_sctsz); } #if MMC_DEBUG mmc_dump_ext_csd(card); #endif return; } //Note: 1. Neither preloader or LK define this function #if defined(MMC_MSDC_DRV_CTP) int mmc_deselect_all_card(struct mmc_host *host) { int err; struct mmc_command cmd; cmd.opcode = MMC_CMD_SELECT_CARD; cmd.arg = 0; cmd.rsptyp = RESP_NONE; cmd.retries = CMD_RETRIES; cmd.timeout = CMD_TIMEOUT; err = mmc_cmd(host, &cmd); return err; } #endif int mmc_select_card(struct mmc_host *host, struct mmc_card *card) { int err; struct mmc_command cmd; cmd.opcode = MMC_CMD_SELECT_CARD; cmd.arg = card->rca << 16; cmd.rsptyp = RESP_R1B; cmd.retries = CMD_RETRIES; cmd.timeout = CMD_TIMEOUT; err = mmc_cmd(host, &cmd); return err; } int mmc_send_relative_addr(struct mmc_host *host, struct mmc_card *card, unsigned int *rca) { int err; struct mmc_command cmd; memset(&cmd, 0, sizeof(struct mmc_command)); if (mmc_card_mmc(card)) { /* set rca */ cmd.opcode = MMC_CMD_SET_RELATIVE_ADDR; cmd.arg = *rca << 16; cmd.rsptyp = RESP_R1; cmd.retries = CMD_RETRIES; cmd.timeout = CMD_TIMEOUT; } else { /* send rca */ cmd.opcode = SD_CMD_SEND_RELATIVE_ADDR; cmd.arg = 0; cmd.rsptyp = RESP_R6; cmd.retries = CMD_RETRIES; cmd.timeout = CMD_TIMEOUT; } err = mmc_cmd(host, &cmd); if ((err == MMC_ERR_NONE) && !mmc_card_mmc(card)) *rca = cmd.resp[0] >> 16; return err; } int mmc_send_tuning_blk(struct mmc_host *host, struct mmc_card *card, u32 *buf) { int err; struct mmc_command cmd; cmd.opcode = SD_CMD_SEND_TUNING_BLOCK; cmd.arg = 0; cmd.rsptyp = RESP_R1; cmd.retries = CMD_RETRIES; cmd.timeout = CMD_TIMEOUT; msdc_set_blknum(host, 1); msdc_set_blklen(host, 64); msdc_set_timeout(host, 100000000, 0); err = mmc_cmd(host, &cmd); if (err != MMC_ERR_NONE) goto out; err = msdc_pio_read(host, buf, 64); if (err != MMC_ERR_NONE) goto out; #if MMC_DEBUG mmc_dump_tuning_blk((u8*)buf); #endif out: return err; } int mmc_switch(struct mmc_host *host, struct mmc_card *card, u8 set, u8 index, u8 value) { int err; u32 status = 0; uint count = 0; struct mmc_command cmd; cmd.opcode = MMC_CMD_SWITCH; cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (index << 16) | (value << 8) | set; cmd.rsptyp = RESP_R1B; cmd.retries = CMD_RETRIES; cmd.timeout = CMD_TIMEOUT; err = mmc_cmd(host, &cmd); if (err != MMC_ERR_NONE) return err; do { err = mmc_send_status(host, card, &status); if (err) { msdc_pr_err("[SD%d] Fail to send status %d\n", host->id, err); break; } if (status & R1_SWITCH_ERROR) { msdc_pr_err("[SD%d] switch error. arg(0x%x)\n", host->id, cmd.arg); return MMC_ERR_FAILED; } if (count++ >= 600000) { msdc_pr_err("[%s]: timeout happend, count=%d, status=0x%x\n", __func__, count, status); break; } } while (!(status & R1_READY_FOR_DATA) || (R1_CURRENT_STATE(status) == 7)); return err; } int mmc_switch_security(struct mmc_host *host, struct mmc_card *card, u8 set, u8 index, u8 value, u8 mode) { int err; u32 status = 0; uint count = 0; struct mmc_command cmd; cmd.opcode = MMC_CMD_SWITCH; cmd.arg = (mode << 24) | (index << 16) | (value << 8) | set; cmd.rsptyp = RESP_R1B; cmd.retries = CMD_RETRIES; cmd.timeout = CMD_TIMEOUT; err = mmc_cmd(host, &cmd); if (err != MMC_ERR_NONE) return err; do { err = mmc_send_status(host, card, &status); if (err) { msdc_pr_err("[SD%d] Fail to send status %d\n", host->id, err); break; } if (status & R1_SWITCH_ERROR) { msdc_pr_err("[SD%d] switch error. arg(0x%x)\n", host->id, cmd.arg); return MMC_ERR_FAILED; } if (count++ >= 600000) { msdc_pr_err("[%s]: timeout happend, count=%d, status=0x%x\n", __func__, count, status); break; } } while (!(status & R1_READY_FOR_DATA) || (R1_CURRENT_STATE(status) == 7)); return err; } static int mmc_sd_switch(struct mmc_host *host, struct mmc_card *card, int mode, int group, u8 value, mmc_switch_t *resp) { int err = MMC_ERR_FAILED; int result = 0; struct mmc_command cmd; u32 *sts = (u32 *)resp; mode = !!mode; value &= 0xF; /* argument: mode[31]= 0 (for check func.) and 1 (for switch func) */ cmd.opcode = SD_CMD_SWITCH; cmd.arg = mode << 31 | 0x00FFFFFF; cmd.arg &= ~(0xF << (group * 4)); cmd.arg |= value << (group * 4); cmd.rsptyp = RESP_R1; cmd.retries = CMD_RETRIES; cmd.timeout = 100; /* 100ms */ #if defined(FEATURE_MMC_RD_TUNING) //Note: 1. CTP does not perform tuning msdc_reset_tune_counter(host); do { #endif msdc_set_blknum(host, 1); msdc_set_blklen(host, 64); msdc_set_timeout(host, 100000000, 0); err = mmc_cmd(host, &cmd); if (err != MMC_ERR_NONE) goto out; /* 512 bits = 64 bytes = 16 words */ err = msdc_pio_read(host, sts, 64); if (err != MMC_ERR_NONE) { msdc_abort_handler(host, 1); #if defined(FEATURE_MMC_RD_TUNING) result = msdc_tune_read(host); #else goto out; #endif } #if defined(FEATURE_MMC_RD_TUNING) } while (err && result != MMC_ERR_READTUNEFAIL); msdc_reset_tune_counter(host); #endif #if MMC_DEBUG { int i; u8 *byte = (u8*)&sts[0]; /* Status: B0 B1 ... * Bits : 511-504 503-495 ... */ for (i = 0; i < 4; i++) { MSG(RSP, " [%d-%d] %xh %xh %xh %xh\n", ((3 - i + 1) << 7) - 1, (3 - i) << 7, sts[(i << 2) + 0], sts[(i << 2) + 1], sts[(i << 2) + 2], sts[(i << 2) + 3]); } for (i = 0; i < 8; i++) { MSG(RSP, " [%d-%d] %xh %xh %xh %xh %xh %xh %xh %xh\n", ((8 - i) << 6) - 1, (8 - i - 1) << 6, byte[(i << 3) + 0], byte[(i << 3) + 1], byte[(i << 3) + 2], byte[(i << 3) + 3], byte[(i << 3) + 4], byte[(i << 3) + 5], byte[(i << 3) + 6], byte[(i << 3) + 7]); } } #endif out: return err; } #if defined(FEATURE_MMC_UHS1) int mmc_ctrl_speed_class(struct mmc_host *host, u32 scc) { struct mmc_command cmd; cmd.opcode = SD_CMD_SPEED_CLASS_CTRL; cmd.arg = scc << 28; cmd.rsptyp = RESP_R1B; cmd.retries = CMD_RETRIES; cmd.timeout = CMD_TIMEOUT; return mmc_cmd(host, &cmd); } int mmc_switch_volt(struct mmc_host *host, struct mmc_card *card) { int err; struct mmc_command cmd; cmd.opcode = SD_CMD_VOL_SWITCH; cmd.arg = 0; cmd.rsptyp = RESP_R1; cmd.retries = CMD_RETRIES; cmd.timeout = CMD_TIMEOUT; err = mmc_cmd(host, &cmd); #ifndef USE_SDIO_1V8 //3.3V only device (e.g., standalone 6630 EVB) does not support voltage switch if ( mmc_card_sdio(card) ) return err; #endif if (err == MMC_ERR_NONE) err = msdc_switch_volt(host, MMC_VDD_18_19); return err; } #endif int mmc_switch_hs(struct mmc_host *host, struct mmc_card *card) { int err; u8 status[64]; int val = MMC_SWITCH_MODE_SDR25; err = mmc_sd_switch(host, card, 1, 0, val, (mmc_switch_t*)&status[0]); if (err != MMC_ERR_NONE) goto out; if ((status[16] & 0xF) != 1) { msdc_pr_err("[SD%d] HS mode not supported!\n", host->id); err = MMC_ERR_FAILED; } else { msdc_pr_info("[SD%d] Switch to HS mode!\n", host->id); mmc_card_set_highspeed(card); } out: return err; } #if defined(FEATURE_MMC_UHS1) int mmc_switch_uhs1(struct mmc_host *host, struct mmc_card *card, unsigned int mode) { int err; u8 status[64]; int val; const char *smode[] = { "SDR12", "SDR25", "SDR50", "SDR104", "DDR50" }; err = mmc_sd_switch(host, card, 1, 0, mode, (mmc_switch_t*)&status[0]); if (err != MMC_ERR_NONE) goto out; if ((status[16] & 0xF) != mode) { msdc_pr_err("[SD%d] UHS-1 %s mode not supported!\n", host->id, smode[mode]); err = MMC_ERR_FAILED; } else { card->uhs_mode = mode; mmc_card_set_uhs1(card); msdc_pr_info("[SD%d] Switch to UHS-1 %s mode!\n", host->id, smode[mode]); if (mode == MMC_SWITCH_MODE_DDR50) { mmc_card_set_ddr(card); } } out: return err; } int mmc_switch_drv_type(struct mmc_host *host, struct mmc_card *card, int val) { int err; u8 status[64]; const char *type[] = { "TYPE-B", "TYPE-A", "TYPE-C", "TYPE-D" }; err = mmc_sd_switch(host, card, 1, 2, val, (mmc_switch_t*)&status[0]); if (err != MMC_ERR_NONE) goto out; if ((status[15] & 0xF) != val) { msdc_pr_err("[SD%d] UHS-1 %s drv not supported!\n", host->id, type[val]); err = MMC_ERR_FAILED; } else { msdc_pr_info("[SD%d] Switch to UHS-1 %s drv!\n", host->id, type[val]); } out: return err; } int mmc_switch_max_cur(struct mmc_host *host, struct mmc_card *card, int val) { int err; u8 status[64]; const char *curr[] = { "200mA", "400mA", "600mA", "800mA" }; err = mmc_sd_switch(host, card, 1, 3, val, (mmc_switch_t*)&status[0]); if (err != MMC_ERR_NONE) goto out; if (((status[15] >> 4) & 0xF) != val) { msdc_pr_err("[SD%d] UHS-1 %s max. current not supported!\n", host->id, curr[val]); err = MMC_ERR_FAILED; } else { msdc_pr_info("[SD%d] Switch to UHS-1 %s max. current!\n", host->id, curr[val]); } out: return err; } #endif static int mmc_read_csds(struct mmc_host *host, struct mmc_card *card) { int err; struct mmc_command cmd; cmd.opcode = MMC_CMD_SEND_CSD; cmd.arg = card->rca << 16; cmd.rsptyp = RESP_R2; cmd.retries = CMD_RETRIES; cmd.timeout = CMD_TIMEOUT * 100; err = mmc_cmd(host, &cmd); if (err == MMC_ERR_NONE) memcpy(&card->raw_csd, &cmd.resp[0], sizeof(u32) * 4); return err; } #if !defined(FEATURE_MMC_SLIM) static int mmc_read_scrs(struct mmc_host *host, struct mmc_card *card) { int err = MMC_ERR_NONE; int result=0; struct mmc_command cmd; struct sd_scr *scr = &card->scr; u32 resp[4]; u32 tmp; u8 buf[8]; msdc_set_blknum(host, 1); msdc_set_blklen(host, 8); msdc_set_timeout(host, 100000000, 0); memset(buf, 0, 8); cmd.opcode = SD_ACMD_SEND_SCR; cmd.arg = 0; cmd.rsptyp = RESP_R1; cmd.retries = CMD_RETRIES; cmd.timeout = CMD_TIMEOUT; #if defined(FEATURE_MMC_RD_TUNING) msdc_reset_tune_counter(host); do { #endif mmc_app_cmd(host, &cmd, card->rca, CMD_RETRIES); if ((err != MMC_ERR_NONE) || !(cmd.resp[0] & R1_APP_CMD)) return MMC_ERR_FAILED; /* 8 bytes = 2 words */ err = msdc_pio_read(host, (u32 *)buf, 8); if (err != MMC_ERR_NONE) { msdc_abort_handler(host, 1); #if defined(FEATURE_MMC_RD_TUNING) result = msdc_tune_read(host); #else return err; #endif } #if defined(FEATURE_MMC_RD_TUNING) } while (err && result != MMC_ERR_READTUNEFAIL); msdc_reset_tune_counter(host); #endif if ( (err==MMC_ERR_NONE) && (result != MMC_ERR_READTUNEFAIL) ) { memcpy(card->raw_scr, buf, 8); } MSG(INF, "[SD%d] SCR: %x %x (raw)\n", host->id, card->raw_scr[0], card->raw_scr[1]); tmp = ntohl(card->raw_scr[0]); card->raw_scr[0] = ntohl(card->raw_scr[1]); card->raw_scr[1] = tmp; MSG(INF, "[SD%d] SCR: %x %x (ntohl)\n", host->id, card->raw_scr[0], card->raw_scr[1]); resp[2] = card->raw_scr[1]; resp[3] = card->raw_scr[0]; if (unstuff_bits(resp, 60, 4) != 0) { msdc_pr_err("[SD%d] Unknown SCR ver %d\n", mmc_card_id(card), unstuff_bits(resp, 60, 4)); return MMC_ERR_INVALID; } scr->scr_struct = unstuff_bits(resp, 60, 4); scr->sda_vsn = unstuff_bits(resp, 56, 4); scr->data_bit_after_erase = unstuff_bits(resp, 55, 1); scr->security = unstuff_bits(resp, 52, 3); scr->bus_widths = unstuff_bits(resp, 48, 4); scr->sda_vsn3 = unstuff_bits(resp, 47, 1); scr->ex_security = unstuff_bits(resp, 43, 4); scr->cmd_support = unstuff_bits(resp, 32, 2); msdc_pr_info("[SD%d] SD_SPEC(%d) SD_SPEC3(%d) SD_BUS_WIDTH=%d\n", mmc_card_id(card), scr->sda_vsn, scr->sda_vsn3, scr->bus_widths); msdc_pr_info("[SD%d] SD_SECU(%d) EX_SECU(%d), CMD_SUPP(%d): CMD23(%d), CMD20(%d)\n", mmc_card_id(card), scr->security, scr->ex_security, scr->cmd_support, (scr->cmd_support >> 1) & 0x1, scr->cmd_support & 0x1); return err; } #endif /* Read and decode extended CSD. */ int mmc_read_ext_csd(struct mmc_host *host, struct mmc_card *card) { int err = MMC_ERR_NONE; u32 *ptr; int result = MMC_ERR_NONE; struct mmc_command cmd; u8 buf[512]; if (card->csd.mmca_vsn < CSD_SPEC_VER_4) { msdc_pr_info("[SD%d] MMCA_VSN: %d. Skip EXT_CSD\n", host->id, card->csd.mmca_vsn); return MMC_ERR_NONE; } memset(buf, 0, 512); //memset(&card->raw_ext_csd[0], 0, 512); ptr = (u32 *)buf; //ptr = (u32*)&card->raw_ext_csd[0]; cmd.opcode = MMC_CMD_SEND_EXT_CSD; cmd.arg = 0; cmd.rsptyp = RESP_R1; cmd.retries = CMD_RETRIES; cmd.timeout = CMD_TIMEOUT; #if defined(FEATURE_MMC_RD_TUNING) msdc_reset_tune_counter(host); do { #endif msdc_set_blknum(host, 1); msdc_set_blklen(host, 512); msdc_set_timeout(host, 100000000, 0); err = mmc_cmd(host, &cmd); if (err != MMC_ERR_NONE) goto out; err = msdc_pio_read(host, ptr, 512); if (err != MMC_ERR_NONE) { host->card = card; // host->card not set will assert msdc_abort_handler(host, 1); #if defined(FEATURE_MMC_RD_TUNING) result = msdc_tune_read(host); #else goto out; #endif } #if defined(FEATURE_MMC_RD_TUNING) } while (err && result != MMC_ERR_READTUNEFAIL); msdc_reset_tune_counter(host); #endif if ( (err==MMC_ERR_NONE) && (result != MMC_ERR_READTUNEFAIL) ) { memcpy(card->raw_ext_csd, buf, 512); mmc_decode_ext_csd(card); } out: return err; } /* Fetches and decodes switch information */ static int mmc_read_switch(struct mmc_host *host, struct mmc_card *card) { int err; u8 status[64]; err = mmc_sd_switch(host, card, 0, 0, 1, (mmc_switch_t*)&status[0]); if (err != MMC_ERR_NONE) { /* Card not supporting high-speed will ignore the command. */ err = MMC_ERR_NONE; goto out; } /* bit 511:480 in status[0]. bit 415:400 in status[13] */ if (status[13] & 0x01) { msdc_pr_info("[SD%d] Support: Default/SDR12\n", host->id); card->sw_caps.hs_max_dtr = 25000000; /* default or sdr12 */ } if (status[13] & 0x02) { msdc_pr_info("[SD%d] Support: HS/SDR25\n", host->id); card->sw_caps.hs_max_dtr = 50000000; /* high-speed or sdr25 */ } if (status[13] & 0x10) { msdc_pr_info("[SD%d] Support: DDR50\n", host->id); card->sw_caps.hs_max_dtr = 50000000; /* ddr50 */ card->sw_caps.ddr = 1; } #if defined(FEATURE_MMC_UHS1) if (status[13] & 0x04) { msdc_pr_info("[SD%d] Support: SDR50\n", host->id); card->sw_caps.hs_max_dtr = 100000000; /* sdr50 */ } if (status[13] & 0x08) { msdc_pr_info("[SD%d] Support: SDR104\n", host->id); card->sw_caps.hs_max_dtr = 208000000; /* sdr104 */ } if (status[9] & 0x01) { msdc_pr_info("[SD%d] Support: Type-B Drv\n", host->id); } if (status[9] & 0x02) { msdc_pr_info("[SD%d] Support: Type-A Drv\n", host->id); } if (status[9] & 0x04) { msdc_pr_info("[SD%d] Support: Type-C Drv\n", host->id); } if (status[9] & 0x08) { msdc_pr_info("[SD%d] Support: Type-D Drv\n", host->id); } if (status[7] & 0x01) { msdc_pr_info("[SD%d] Support: 200mA current limit\n", host->id); } if (status[7] & 0x02) { msdc_pr_info("[SD%d] Support: 400mA current limit\n", host->id); } if (status[7] & 0x04) { msdc_pr_info("[SD%d] Support: 600mA current limit\n", host->id); } if (status[7] & 0x08) { msdc_pr_info("[SD%d] Support: 800mA current limit\n", host->id); } #endif out: return err; } #if 0 static int mmc_deselect_cards(struct mmc_host *host) { struct mmc_command cmd; cmd.opcode = MMC_CMD_SELECT_CARD; cmd.arg = 0; cmd.rsptyp = RESP_NONE; cmd.retries = CMD_RETRIES; cmd.timeout = CMD_TIMEOUT; return mmc_cmd(host, &cmd); } int mmc_lock_unlock(struct mmc_host *host) { struct mmc_command cmd; cmd.opcode = MMC_CMD_LOCK_UNLOCK; cmd.rsptyp = RESP_R1; cmd.arg = 0; cmd.retries = 3; cmd.timeout = CMD_TIMEOUT; return mmc_cmd(host, &cmd); } #endif int mmc_set_write_prot(struct mmc_host *host, u32 addr) { struct mmc_command cmd; if (!(host->card->csd.cmdclass & CCC_WRITE_PROT)) return MMC_ERR_INVALID; cmd.opcode = MMC_CMD_SET_WRITE_PROT; cmd.rsptyp = RESP_R1B; cmd.arg = addr; cmd.retries = 3; cmd.timeout = CMD_TIMEOUT; return mmc_cmd(host, &cmd); } int mmc_clr_write_prot(struct mmc_host *host, u32 addr) { struct mmc_command cmd; if (!(host->card->csd.cmdclass & CCC_WRITE_PROT)) return MMC_ERR_INVALID; cmd.opcode = MMC_CMD_CLR_WRITE_PROT; cmd.rsptyp = RESP_R1B; cmd.arg = addr; cmd.retries = 3; cmd.timeout = CMD_TIMEOUT; return mmc_cmd(host, &cmd); } //SDHC and SDXC does not support this command int mmc_send_write_prot(struct mmc_card *card, u32 wp_addr, u32 *wp_status) { int err; struct mmc_command cmd; struct mmc_host *host = card->host; u8 *buf = (u8*)wp_status; if (!(card->csd.cmdclass & CCC_WRITE_PROT)) return MMC_ERR_INVALID; cmd.opcode = MMC_CMD_SEND_WRITE_PROT; cmd.rsptyp = RESP_R1; cmd.arg = wp_addr; cmd.retries = 3; cmd.timeout = CMD_TIMEOUT; msdc_set_blknum(host, 1); msdc_set_blklen(host, 4); msdc_set_timeout(host, 100000000, 0); err = mmc_cmd(host, &cmd); if (err != MMC_ERR_NONE) goto out; err = msdc_pio_read(host, (u32*)buf, 4); if (err != MMC_ERR_NONE) goto out; out: return err; } #if defined(FEATURE_ERASE) int mmc_erase_start(struct mmc_card *card, u64 addr) { struct mmc_command cmd; if (!(card->csd.cmdclass & CCC_ERASE)) { msdc_pr_err("[SD%d] Card doesn't support Erase commands\n", card->host->id); return MMC_ERR_INVALID; } if (mmc_card_highcaps(card)) addr /= MMC_BLOCK_SIZE; /* in sector unit */ if (mmc_card_mmc(card)) { cmd.opcode = MMC_CMD_ERASE_GROUP_START; } else { cmd.opcode = MMC_CMD_ERASE_WR_BLK_START; } cmd.rsptyp = RESP_R1; cmd.arg = addr; cmd.retries = 3; cmd.timeout = CMD_TIMEOUT; return mmc_cmd(card->host, &cmd); } int mmc_erase_end(struct mmc_card *card, u64 addr) { struct mmc_command cmd; if (!(card->csd.cmdclass & CCC_ERASE)) { msdc_pr_err("[SD%d] Erase isn't supported\n", card->host->id); return MMC_ERR_INVALID; } if (mmc_card_highcaps(card)) addr /= MMC_BLOCK_SIZE; /* in sector unit */ if (mmc_card_mmc(card)) { cmd.opcode = MMC_CMD_ERASE_GROUP_END; } else { cmd.opcode = MMC_CMD_ERASE_WR_BLK_END; } cmd.rsptyp = RESP_R1; cmd.arg = addr; cmd.retries = 3; cmd.timeout = CMD_TIMEOUT; return mmc_cmd(card->host, &cmd); } int mmc_erase(struct mmc_card *card, u32 arg) { int err; u32 status; struct mmc_command cmd; if (!(card->csd.cmdclass & CCC_ERASE)) { msdc_pr_err("[SD%d] Erase isn't supported\n", card->host->id); return MMC_ERR_INVALID; } if (arg & MMC_ERASE_SECURE_REQ) { if (!(card->raw_ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT] & EXT_CSD_SEC_FEATURE_ER_EN)) { return MMC_ERR_INVALID; } } if ((arg & MMC_ERASE_GC_REQ) || (arg & MMC_ERASE_TRIM)) { if (!(card->raw_ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT] & EXT_CSD_SEC_FEATURE_GB_CL_EN)) { return MMC_ERR_INVALID; } } cmd.opcode = MMC_CMD_ERASE; cmd.rsptyp = RESP_R1B; cmd.arg = arg; cmd.retries = 3; cmd.timeout = CMD_TIMEOUT; err = mmc_cmd(card->host, &cmd); if (!err) { do { err = mmc_send_status(card->host, card, &status); if (err) break; #if MMC_DEBUG mmc_dump_card_status(status); #endif if (R1_STATUS(status) != 0) break; } while (R1_CURRENT_STATE(status) == 7); } return err; } #endif #if defined(FEATURE_MMC_UHS1) int mmc_tune_timing(struct mmc_host *host, struct mmc_card *card) { int err = MMC_ERR_NONE; if (mmc_card_sd(card) && mmc_card_uhs1(card) && !mmc_card_ddr(card)) { err = msdc_tune_uhs1(host, card); } else if (mmc_card_mmc(card) && mmc_card_hs200(card)) { err = msdc_tune_hs200(host, card); } else if (mmc_card_mmc(card) && mmc_card_hs400(card)) { err = msdc_tune_hs400(host, card); } return err; } #endif u64 mmc_get_wpg_size(struct mmc_card *card) { u64 size; if (mmc_card_mmc(card)) { size = card->wp_size * 512; } else { if (card->csd.write_prot_grp) { /* SDSC could support write protect group */ size = (card->csd.write_prot_grpsz + 1) * (1 << card->csd.write_blkbits); } else { /* SDHC and SDXC don't support write protect group */ size = 0; } } return size; } #if defined(MMC_MSDC_DRV_CTP) /* need reset some register while switch to hs400 mode with emmc50 */ int mmc_register_partial_reset(struct mmc_host* host) { if (host->id != 0) { return 1; } /* back up, then reset to default value */ msdc_register_partial_backup_and_reset(host); return 0; } int mmc_register_partial_restore(struct mmc_host* host) { if (host->id != 0) { return 1; } /* back up, then reset to default value */ msdc_register_partial_restore(host); return 0; } #endif #if defined(MMC_MSDC_DRV_CTP) void mmc_switch_card_timing(struct mmc_host *host, unsigned int clkhz) { int id = host->id; struct mmc_card *card = host->card; int result = 0; if (card && mmc_card_mmc(card)) { if ((clkhz > 52000000) && (host->caps & MMC_CAP_EMMC_HS400)) { if ((mmc_switch(host, card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1) == MMC_ERR_NONE) && (mmc_switch(host, card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_8_DDR) == MMC_ERR_NONE) && (mmc_switch(host, card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 3) == MMC_ERR_NONE)) { msdc_pr_info("[SD%d] Switch to HS400 mode!\n", id); mmc_card_clr_speed_mode(card); mmc_card_set_hs400(card); /* hs400 used ddr mode */ mmc_card_set_ddr(card); } else { result = -__LINE__; goto failure; } } else if ((clkhz > 52000000) && (host->caps & MMC_CAP_EMMC_HS200)) { if ((mmc_switch(host, card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1) == MMC_ERR_NONE) && (mmc_switch(host, card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_8) == MMC_ERR_NONE) && (mmc_switch(host, card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 2) == MMC_ERR_NONE)) { msdc_pr_info("[SD%d] Switch to HS200 mode!\n", id); mmc_card_clr_speed_mode(card); mmc_card_set_hs200(card); /*if ddr enable, disable it */ if (host->caps & MMC_CAP_DDR) { host->caps &= ~MMC_CAP_DDR; mmc_card_clr_ddr(card); } } else { result = -__LINE__; goto failure; } } else if ((clkhz > 26000000) && (clkhz <= 52000000) && (host->caps & MMC_CAP_DDR)) { if ((mmc_switch(host, card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1) == MMC_ERR_NONE) && (mmc_switch(host, card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_8_DDR) == MMC_ERR_NONE)) { msdc_pr_info("[SD%d] Switch to DDR50 mode!\n", id); mmc_card_clr_speed_mode(card); mmc_card_set_highspeed(card); mmc_card_set_ddr(card); } else { result = -__LINE__; goto failure; } } else if ((clkhz > 26000000) && (host->caps & MMC_CAP_MMC_HIGHSPEED)) { if (mmc_switch(host, card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1) == MMC_ERR_NONE) { msdc_pr_info("[SD%d] Switch to High-Speed mode!\n", id); mmc_card_clr_speed_mode(card); mmc_card_set_highspeed(card); } else { result = -__LINE__; goto failure; } } else if (clkhz > 0) { if (mmc_switch(host, card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 0) == MMC_ERR_NONE) { msdc_pr_info("[SD%d] Switch to Default mode!\n", id); mmc_card_clr_ddr(card); mmc_card_clr_speed_mode(card); mmc_card_set_backyard(card); } else { result = -__LINE__; goto failure; } } } #if defined(MMC_MSDC_DRV_CTP) else if (card && mmc_card_sd(card) && (card->version > SD_VER_10)) { int uhsmode = 0; if ((clkhz > 100000000) && (host->caps & MMC_CAP_SD_UHS1)) { uhsmode = MMC_SWITCH_MODE_SDR104; } else if (clkhz > 50000000) { if (card->sw_caps.ddr && msdc_cap[id].flags & MSDC_DDR) { uhsmode = MMC_SWITCH_MODE_DDR50; } else { uhsmode = MMC_SWITCH_MODE_SDR50; } } else if ((clkhz > 25000000) && (host->caps & MMC_CAP_SD_HIGHSPEED)) { uhsmode = MMC_SWITCH_MODE_SDR25; /*if ddr enable, disable it */ if (host->caps & MMC_CAP_DDR) { host->caps &= ~MMC_CAP_DDR; mmc_card_clr_ddr(card); } } else if (clkhz > 0) { uhsmode = MMC_SWITCH_MODE_SDR12; } if (mmc_switch_uhs1(host, card, uhsmode) != 0) { result = -__LINE__; goto failure; } } #endif failure: if (result) msdc_pr_err("[%s]: result=%d\n", __func__, result); return; } #endif void mmc_set_clock(struct mmc_host *host, int ddr, u32 hz) { unsigned int hs_timing = 0; if (hz >= host->f_max) { hz = host->f_max; } else if (hz < host->f_min) { hz = host->f_min; } //mmc_switch_card_timing(host, hz); if (host->card) { if (mmc_card_hs400(host->card)) { hs_timing |= EXT_CSD_HS_TIMEING_HS400; ddr = 0; } else if (mmc_card_ddr(host->card) && (host->caps & MMC_CAP_DDR)) { ddr = 1; } else { ddr = 0; } } msdc_config_clock(host, ddr, hz, hs_timing); } int mmc_set_ext_csd(struct mmc_card *card, uint8 addr, uint8 value) { int err; u8 *ext_csd; /* can't write */ if (192 <= addr || !card || !mmc_card_mmc(card)) return MMC_ERR_INVALID; err = mmc_switch(card->host, card, EXT_CSD_CMD_SET_NORMAL, addr, value); if (err == MMC_ERR_NONE) { err = mmc_read_ext_csd(card->host, card); if (err == MMC_ERR_NONE) { ext_csd = &card->raw_ext_csd[0]; if (ext_csd[addr] != value) err = MMC_ERR_FAILED; } } return err; } int mmc_set_card_detect(struct mmc_host *host, struct mmc_card *card, int connect) { int err; struct mmc_command cmd; cmd.opcode = SD_ACMD_SET_CLR_CD; cmd.arg = connect; cmd.rsptyp = RESP_R1; /* CHECKME */ cmd.retries = CMD_RETRIES; cmd.timeout = CMD_TIMEOUT; err = mmc_app_cmd(host, &cmd, card->rca, CMD_RETRIES); return err; } int mmc_set_blk_length(struct mmc_host *host, u32 blklen) { int err; struct mmc_command cmd; /* set block len */ cmd.opcode = MMC_CMD_SET_BLOCKLEN; cmd.rsptyp = RESP_R1; cmd.arg = blklen; cmd.retries = 3; cmd.timeout = CMD_TIMEOUT; err = mmc_cmd(host, &cmd); if (err == MMC_ERR_NONE) msdc_set_blklen(host, blklen); return err; } #if defined(MMC_MSDC_DRV_CTP) int mmc_set_blk_count(struct mmc_host *host, u32 blkcnt) { int err; struct mmc_command cmd; /* set block count */ cmd.opcode = MMC_CMD_SET_BLOCK_COUNT; cmd.rsptyp = RESP_R1; cmd.arg = blkcnt; /* bit31 is for reliable write request */ #if MSDC_USE_DATA_TAG cmd.arg |= (1 << 29); cmd.arg &= ~(1 << 30); #endif #if MSDC_USE_RELIABLE_WRITE cmd.arg |= (1 << 31); cmd.arg &= ~(1 << 30); #endif #if MSDC_USE_FORCE_FLUSH cmd.arg |= (1 << 24); cmd.arg &= ~(1 << 30); #endif #if MSDC_USE_PACKED_CMD cmd.arg &= ~0xffff; cmd.arg |= (1 << 30); #endif cmd.retries = 3; cmd.timeout = CMD_TIMEOUT; err = mmc_cmd(host, &cmd); return err; } #endif int mmc_set_bus_width(struct mmc_host *host, struct mmc_card *card, int width) { int err = MMC_ERR_NONE; u32 arg = 0; struct mmc_command cmd; if (mmc_card_sd(card)) { if (width == HOST_BUS_WIDTH_8) { WARN_ON(width == HOST_BUS_WIDTH_8); arg = SD_BUS_WIDTH_4; width = HOST_BUS_WIDTH_4; } if ((width == HOST_BUS_WIDTH_4) && (host->caps & MMC_CAP_4_BIT_DATA)) { arg = SD_BUS_WIDTH_4; } else { arg = SD_BUS_WIDTH_1; width = HOST_BUS_WIDTH_1; } cmd.opcode = SD_ACMD_SET_BUSWIDTH; cmd.arg = arg; cmd.rsptyp = RESP_R1; cmd.retries = CMD_RETRIES; cmd.timeout = CMD_TIMEOUT; err = mmc_app_cmd(host, &cmd, card->rca, 0); if (err != MMC_ERR_NONE) goto out; msdc_config_bus(host, width); } else if (mmc_card_mmc(card)) { if (card->csd.mmca_vsn < CSD_SPEC_VER_4) goto out; if (width == HOST_BUS_WIDTH_8) { if (host->caps & MMC_CAP_8_BIT_DATA) { /* need make sure card current bus mode */ if (mmc_card_hs400(card)) { arg = (host->caps & MMC_CAP_EMMC_HS400) ? EXT_CSD_BUS_WIDTH_8_DDR : EXT_CSD_BUS_WIDTH_8; } else if (mmc_card_highspeed(card)) { arg = ((host->caps & MMC_CAP_DDR) && card->ext_csd.ddr_support) ? EXT_CSD_BUS_WIDTH_8_DDR : EXT_CSD_BUS_WIDTH_8; } else if (mmc_card_hs200(card) || mmc_card_backyard(card)) { arg = EXT_CSD_BUS_WIDTH_8; } else { width = HOST_BUS_WIDTH_4; } } else { width = HOST_BUS_WIDTH_4; } } if (width == HOST_BUS_WIDTH_4) { if (host->caps & MMC_CAP_4_BIT_DATA) { /* need make sure card current bus mode */ if (mmc_card_hs400(card)) { arg = (host->caps & MMC_CAP_EMMC_HS400) ? EXT_CSD_BUS_WIDTH_8_DDR : EXT_CSD_BUS_WIDTH_8; } else if (mmc_card_highspeed(card)) { arg = ((host->caps & MMC_CAP_DDR) && card->ext_csd.ddr_support) ? EXT_CSD_BUS_WIDTH_4_DDR : EXT_CSD_BUS_WIDTH_4; } else if (mmc_card_hs200(card) || mmc_card_backyard(card)) { arg = EXT_CSD_BUS_WIDTH_4; } else { width = HOST_BUS_WIDTH_1; } } else { width = HOST_BUS_WIDTH_1; } } if (width == HOST_BUS_WIDTH_1) arg = EXT_CSD_BUS_WIDTH_1; err = mmc_switch(host, card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, arg); if (err != MMC_ERR_NONE) { msdc_pr_err("[SD%d] Switch to bus width(%d) failed\n", host->id, arg); goto out; } if (arg == EXT_CSD_BUS_WIDTH_8_DDR || arg == EXT_CSD_BUS_WIDTH_4_DDR) { msdc_pr_info("[SD%d] Switch to DDR buswidth\n", host->id); mmc_card_set_ddr(card); } else { msdc_pr_info("[SD%d] Switch to SDR buswidth\n", host->id); mmc_card_clr_ddr(card); } mmc_set_clock(host, mmc_card_ddr(card), host->cur_bus_clk); msdc_config_bus(host, width); } else { BUG_ON(1); /* card is not recognized */ } out: #if 0 if (mmc_card_sd(card)) { msdc_pr_info("[info][%s %d] switch to %dbit bus width, arg=0x%x, err = %d\n", __func__, __LINE__, (arg == SD_BUS_WIDTH_4) ? 4 : 1, arg, err); } else { switch (arg) { case EXT_CSD_BUS_WIDTH_1: msdc_pr_info("[info][%s %d] switch to 1bit bus width, err = %d\n", __func__, __LINE__, err); break; case EXT_CSD_BUS_WIDTH_4: msdc_pr_info("[info][%s %d] switch to 4bit bus width, err = %d\n", __func__, __LINE__, err); break; case EXT_CSD_BUS_WIDTH_8: msdc_pr_info("[info][%s %d] switch to 8bit bus width, err = %d\n", __func__, __LINE__, err); break; case EXT_CSD_BUS_WIDTH_4_DDR: msdc_pr_info("[info][%s %d] switch to 4bit bus width(DDR), err = %d\n", __func__, __LINE__, err); break; case EXT_CSD_BUS_WIDTH_8_DDR: msdc_pr_info("[info][%s %d] switch to 8bit bus width(DDR), err = %d\n", __func__, __LINE__, err); break; default: msdc_pr_info("[info][%s %d] switch to ?bit bus width(DDR), err = %d\n", __func__, __LINE__, err); break; } } #endif return err; } int mmc_set_erase_grp_def(struct mmc_card *card, int enable) { int err = MMC_ERR_FAILED; if (mmc_card_sd(card) || !mmc_card_highcaps(card)) goto out; if (card->csd.mmca_vsn < CSD_SPEC_VER_4) goto out; err = mmc_set_ext_csd(card, EXT_CSD_ERASE_GRP_DEF, EXT_CSD_ERASE_GRP_DEF_EN & enable); out: return err; } int mmc_set_gp_size(struct mmc_card *card, u8 id, u32 size) { int i; int err = MMC_ERR_FAILED; u8 gp[] = { EXT_CSD_GP1_SIZE_MULT, EXT_CSD_GP2_SIZE_MULT, EXT_CSD_GP3_SIZE_MULT, EXT_CSD_GP4_SIZE_MULT}; u8 arg; u8 *ext_csd = &card->raw_ext_csd[0]; if (mmc_card_sd(card) || !mmc_card_highcaps(card)) goto out; if (card->csd.mmca_vsn < CSD_SPEC_VER_4) goto out; id--; size /= 512 * 1024; size /= (ext_csd[EXT_CSD_HC_WP_GPR_SIZE] * ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]); /* 143-144: GP_SIZE_MULT_X_0-GP_SIZE_MULT_X_2 */ for (i = 0; i < 3; i++) { arg = (u8)(size & 0xFF); size = size >> 8; err = mmc_set_ext_csd(card, gp[id] + i, arg); if (err) goto out; } out: return err; } int mmc_set_enh_size(struct mmc_card *card, u32 size) { int i; int err = MMC_ERR_FAILED; u8 arg; u8 *ext_csd = &card->raw_ext_csd[0]; if (mmc_card_sd(card) || !mmc_card_highcaps(card)) goto out; if (card->csd.mmca_vsn < CSD_SPEC_VER_4) goto out; /* need to set ERASE_GRP_DEF first?? */ if (0 == (card->raw_ext_csd[EXT_CSD_ERASE_GRP_DEF] & EXT_CSD_ERASE_GRP_DEF_EN)) goto out; size /= (512 * 1024); size /= (ext_csd[EXT_CSD_HC_WP_GPR_SIZE] * ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]); /* 140-142: ENH_SIZE_MULT0-ENH_SIZE_MULT2 */ for (i = 0; i < 3; i++) { arg = (u8)(size & 0xFF); size = size >> 8; err = mmc_set_ext_csd(card, EXT_CSD_ENH_SIZE_MULT + i, arg); if (err) goto out; } out: return err; } int mmc_set_enh_start_addr(struct mmc_card *card, u32 addr) { int i; int err = MMC_ERR_FAILED; u8 arg; if (mmc_card_sd(card)) goto out; if (card->csd.mmca_vsn < CSD_SPEC_VER_4) goto out; /* need to set ERASE_GRP_DEF first?? */ if (0 == (card->raw_ext_csd[EXT_CSD_ERASE_GRP_DEF] & EXT_CSD_ERASE_GRP_DEF_EN)) goto out; /* start address would be round to protect group aligned. */ if (mmc_card_highcaps(card)) addr = addr / 512; /* in sector unit. otherwise in byte unit */ /* 136-139: ENH_START_ADDR0-ENH_START_ADDR3 */ for (i = 0; i < 4; i++) { arg = (u8)(addr & 0xFF); addr = addr >> 8; err = mmc_set_ext_csd(card, EXT_CSD_ENH_START_ADDR + i, arg); if (err) goto out; } out: return err; } int mmc_set_boot_bus(struct mmc_card *card, u8 rst_bwidth, u8 mode, u8 bwidth) { int err = MMC_ERR_FAILED; u8 arg; if (mmc_card_sd(card)) goto out; if (card->csd.mmca_vsn < CSD_SPEC_VER_4) goto out; arg = mode | rst_bwidth | bwidth; err = mmc_set_ext_csd(card, EXT_CSD_BOOT_BUS_WIDTH, arg); out: return err; } int mmc_set_part_config(struct mmc_card *card, u8 cfg) { int err = MMC_ERR_FAILED; if (mmc_card_sd(card)) goto out; if (card->csd.mmca_vsn < CSD_SPEC_VER_4) goto out; err = mmc_set_ext_csd(card, EXT_CSD_PART_CFG, cfg); out: return err; } int mmc_set_part_attr(struct mmc_card *card, u8 attr) { int err = MMC_ERR_FAILED; if (mmc_card_sd(card)) goto out; if (card->csd.mmca_vsn < CSD_SPEC_VER_4) goto out; if (!card->ext_csd.enh_attr_en) { err = MMC_ERR_INVALID; goto out; } attr &= 0x1F; attr |= (card->raw_ext_csd[EXT_CSD_PART_ATTR] & 0x1F); err = mmc_set_ext_csd(card, EXT_CSD_PART_ATTR, attr); out: return err; } int mmc_set_part_compl(struct mmc_card *card) { int err = MMC_ERR_FAILED; if (mmc_card_sd(card)) goto out; if (card->csd.mmca_vsn < CSD_SPEC_VER_4) goto out; err = mmc_set_ext_csd(card, EXT_CSD_PART_SET_COMPL, EXT_CSD_PART_SET_COMPL_BIT); out: return err; } #if defined(MMC_MSDC_DRV_CTP) int mmc_set_reset_func(struct mmc_card *card, u8 enable) { int err = MMC_ERR_FAILED; u8 *ext_csd = &card->raw_ext_csd[0]; if (mmc_card_sd(card)) goto out; if (card->csd.mmca_vsn < CSD_SPEC_VER_4) goto out; if (ext_csd[EXT_CSD_RST_N_FUNC] == 0) { err = mmc_set_ext_csd(card, EXT_CSD_RST_N_FUNC, enable); } else { /* no need set */ return MMC_ERR_NONE; } out: return err; } int mmc_boot_config(struct mmc_card *card, u8 acken, u8 enpart, u8 buswidth, u8 busmode) { int err = MMC_ERR_FAILED; u8 val; u8 rst_bwidth = 0; u8 *ext_csd = &card->raw_ext_csd[0]; if (mmc_card_sd(card) || card->csd.mmca_vsn < CSD_SPEC_VER_4 || !card->ext_csd.boot_info || card->ext_csd.rev < 3) goto out; if (card->ext_csd.rev > 3 && !card->ext_csd.part_en) goto out; /* configure boot partition */ val = acken | enpart | (ext_csd[EXT_CSD_PART_CFG] & 0x7); err = mmc_set_part_config(card, val); if (err != MMC_ERR_NONE) goto out; /* update ext_csd information */ ext_csd[EXT_CSD_PART_CFG] = val; /* configure boot bus mode and width */ rst_bwidth = ((buswidth == EXT_CSD_BOOT_BUS_WIDTH_1) ? 0 : 1) << 2; msdc_pr_info("=====Set boot Bus Width<%d>=======\n", buswidth); msdc_pr_info("=====Set boot Bus mode<%d>=======\n", busmode); err = mmc_set_boot_bus(card, rst_bwidth, busmode, buswidth); out: return err; } #if defined(FEATURE_MMC_BOOT_MODE) int mmc_part_read(struct mmc_card *card, u8 partno, unsigned long blknr, u32 blkcnt, unsigned long *dst) { int err = MMC_ERR_FAILED; u8 val; u8 *ext_csd = &card->raw_ext_csd[0]; struct mmc_host *host = card->host; if (mmc_card_sd(card) || card->csd.mmca_vsn < CSD_SPEC_VER_4 || !card->ext_csd.boot_info || card->ext_csd.rev < 3) goto out; if (card->ext_csd.rev > 3 && !card->ext_csd.part_en) goto out; /* configure to specified partition */ val = (ext_csd[EXT_CSD_PART_CFG] & ~0x7) | (partno & 0x7); err = mmc_set_part_config(card, val); if (err != MMC_ERR_NONE) goto out; /* write block to this partition */ err = mmc_block_read(host->id, blknr, blkcnt, dst); out: /* configure to user partition */ val = (ext_csd[EXT_CSD_PART_CFG] & ~0x7) | EXT_CSD_PART_CFG_DEFT_PART; mmc_set_part_config(card, val); return err; } int mmc_part_write(struct mmc_card *card, u8 partno, unsigned long blknr, u32 blkcnt, unsigned long *src) { int err = MMC_ERR_FAILED; u8 val; u8 *ext_csd = &card->raw_ext_csd[0]; struct mmc_host *host = card->host; if (mmc_card_sd(card) || card->csd.mmca_vsn < CSD_SPEC_VER_4 || !card->ext_csd.boot_info || card->ext_csd.rev < 3) goto out; if (card->ext_csd.rev > 3 && !card->ext_csd.part_en) goto out; /* configure to specified partition */ val = (ext_csd[EXT_CSD_PART_CFG] & ~0x7) | (partno & 0x7); err = mmc_set_part_config(card, val); if (err != MMC_ERR_NONE) goto out; /* write block to this partition */ err = mmc_block_write(host->id, blknr, blkcnt, src); out: /* configure to user partition */ val = (ext_csd[EXT_CSD_PART_CFG] & ~0x7) | EXT_CSD_PART_CFG_DEFT_PART; mmc_set_part_config(card, val); return err; } #endif #endif #if defined(FEATURE_MMC_POWER_ON_WP) || defined(MTK_EMMC_SUPPORT_OTP) #define MMC_CCC_WRITE_PROT (1<<6) static int mmc_send_write_prot_type(struct mmc_card *card, u32 wp_addr, u32 *wp_type) { int err; int result = MMC_ERR_NONE; struct mmc_command cmd; struct mmc_host *host = card->host; u8 *buf = (u8*)wp_type; if (!(card->csd.cmdclass & MMC_CCC_WRITE_PROT)) { msdc_pr_err("emmc device doesn't support Class6 CMD.\n"); return MMC_ERR_INVALID; } u32 buf_size = 8; u32 origin_blklen = host->blklen; cmd.opcode = MMC_CMD_SEND_WRITE_PROT_TYPE; cmd.rsptyp = RESP_R1; cmd.arg = wp_addr; cmd.retries = 3; cmd.timeout = CMD_TIMEOUT; msdc_reset_tune_counter(host); #if defined(FEATURE_MMC_RD_TUNING) do { #endif msdc_set_blknum(host, 1); msdc_set_blklen(host, buf_size); msdc_set_timeout(host, 100000000, 0); err = mmc_cmd(host, &cmd); if (err != MMC_ERR_NONE) goto out; err = msdc_pio_read(host, (u32*)buf, buf_size); if (err != MMC_ERR_NONE){ if (msdc_abort_handler(host, 1)) msdc_pr_err("[SD%d] data abort failed\n", host->id); result = msdc_tune_read(host); } #if defined(FEATURE_MMC_RD_TUNING) } while (err && result != MMC_ERR_READTUNEFAIL); #endif //need restore the host->blklen msdc_set_blklen(host, origin_blklen); msdc_reset_tune_counter(host); out: return err; } int mmc_check_write_prot_type_by_groups(struct mmc_card *card, unsigned long blknr, u32 blkcnt, u32 *type) { int err = MMC_ERR_NONE; u32 count_wp; u32 count_wp_rest; u32 count_wp_group; u32 i, j, z; //memset(bd, 0, sizeof(bd_t) * MAX_BD_POOL_SZ); u8 verified_status_bit = 1;// 1 means none; 2 mean other proc type status bit; u8 status[8] = {0}; ;//64 bit u8 status_should; if (blkcnt%card->wp_size) { msdc_pr_err("[%s]: error: blkcnt=%d\n", __func__,blkcnt); err = MMC_ERR_FAILED; goto out; } // Check the WP status count_wp = blkcnt/card->wp_size; count_wp_group = count_wp / 32; count_wp_rest = count_wp % 32; for (i = 0; i < count_wp_group; i++) { err = mmc_send_write_prot_type(card, blknr + i * 32 * card->wp_size, (u32 *) status); if (err) { msdc_pr_err("[%s]: mmc_send_write_prot_type err %d \n", __func__,err); goto out; } //check each for (j = 0; j < 8; j++) { if ((status[j] != verified_status_bit) && (1 != verified_status_bit)) { //prot_type = WP_OTHER_TYPE; verified_status_bit = 2; goto done; } verified_status_bit = status[j]; } //msdc_pr_info(" wp_type %d\n", status[j]); } //verify the rest bit err = mmc_send_write_prot_type(card, blknr + count_wp_group * 32 * card->wp_size, (u32 *) status); if (err) { msdc_pr_err("[%s]: mmc_send_write_prot_type err %d \n", __func__,err); goto out; } for (z = 0; z < 8; z++) { if (count_wp_rest >= 4) { if (status[7 - z] != verified_status_bit && (1 != verified_status_bit)) { //prot_type = WP_OTHER_TYPE; verified_status_bit = 2; goto done; } verified_status_bit = status[7 - z]; count_wp_rest -= 4; } else if (count_wp_rest >= 1) { u8 status_select = status[7 - z] & (0xFF >> (8 - count_wp_rest * 2)); if (verified_status_bit != 1) { status_should = verified_status_bit >> (8 - count_wp_rest * 2); if(status_select != status_should) { verified_status_bit = 2; goto done; } // pass } else { if (status_select == (0x55 >> (8 - count_wp_rest * 2))) verified_status_bit = 0x55; else if (status_select == (0xAA >> (8 - count_wp_rest * 2))) verified_status_bit = 0xAA; else if (status_select == (0xFF >> (8 - count_wp_rest * 2))) verified_status_bit = 0xFF; else if (status_select == (0 >> (8 - count_wp_rest * 2))) verified_status_bit = 0; else verified_status_bit = 2; } break; } else break; } done: if (verified_status_bit == 0x55) { *type = WP_TEMPORARY; } else if (verified_status_bit == 0xAA) { *type = WP_POWER_ON; } else if (verified_status_bit == 0xFF) { *type = WP_PERMANENT; } else if (verified_status_bit == 0) { *type = WP_DISABLE; } else { msdc_pr_err("[msdc] unknown protect type\n"); } out: return err; } static int mmc_set_write_prot_by_groups(struct mmc_card *card, unsigned long blknr, u32 blkcnt) { int err = MMC_ERR_NONE; u32 count_wp; u32 index; if (blkcnt%card->wp_size) { msdc_pr_err("[%s]: error: blkcnt=%d\n", __func__, blkcnt); err = MMC_ERR_FAILED; goto out; } count_wp = blkcnt/card->wp_size; for (index = 0; index < count_wp; index++) { err = mmc_set_write_prot(card->host, blknr + (index * card->wp_size)); if (err) { msdc_pr_err("[%s]: mmc_set_write_prot err %d\n", __func__,err); goto out; } } out: return err; } static int mmc_clr_write_prot_by_groups(struct mmc_card *card, unsigned long blknr, u32 blkcnt){ int err = MMC_ERR_NONE; u32 count_wp; u32 index; if (blkcnt%card->wp_size) { msdc_pr_err("[%s]: error: blkcnt=%d\n", __func__, blkcnt); err = MMC_ERR_FAILED; goto out; } count_wp = blkcnt/card->wp_size; for (index = 0; index < count_wp; index++) { err = mmc_clr_write_prot(card->host, blknr + (index * card->wp_size)); if (err) { msdc_pr_err("[%s]: mmc_set_write_prot err %d\n", __func__,err); goto out; } } out: return err; } unsigned int mmc_set_write_protect(int dev_num, Region partition, unsigned long blknr, u32 blkcnt, STORAGE_WP_TYPE type) { int err = MMC_ERR_NONE; struct mmc_card *card = mmc_get_card(dev_num); if (partition == EMMC_PART_USER) { /* check the alignment and capility */ if (blknr % card->wp_size || blkcnt % card->wp_size || (blknr + blkcnt > card->nblks)) { msdc_pr_err("[%s]: alignment or capility error \n", __func__); err = MMC_ERR_FAILED; goto out; } err = mmc_set_user_wp(card, type, blknr, blkcnt); if (err) { msdc_pr_err("[%s]: mmc_set_user_wp err%d\n", __func__,err); goto out; } } else if (type==WP_POWER_ON && (partition == EMMC_PART_BOOT1 || partition == EMMC_PART_BOOT2)) { err = mmc_set_boot_wp(card, WP_POWER_ON, partition); if (err) { msdc_pr_err("[%s]: mmc_set_boot_wp err%d\n", __func__,err); goto out; } } else { msdc_pr_err("[%s]: partition Not Support or other error\n", __func__); err = MMC_ERR_FAILED; goto out; } out: return err; } int mmc_set_boot_wp(struct mmc_card *card, STORAGE_WP_TYPE type, Region partition) { int err = MMC_ERR_FAILED; u8 *ext_csd = &card->raw_ext_csd[0]; u8 value; if (type == WP_DISABLE || type == WP_PERMANENT || type == WP_TEMPORARY) { return 0; } if (type > WP_DISABLE || type < 0) { goto out; } if (!mmc_card_mmc(card)) goto out; WARN_ON(card->csd.mmca_vsn < CSD_SPEC_VER_4); if (card->csd.mmca_vsn < CSD_SPEC_VER_4) goto out; if (card->ext_csd.boot_wp & EXT_CSD_BOOT_WP_DIS_PWR_WP) { msdc_pr_info("[%s]: EXT_CSD_BOOT_WP_DIS_PWR_WP is err set \n", __func__); goto out; } //check by read err = mmc_read_ext_csd(card->host, card); if (err) { msdc_pr_err("[%s]: read ext_csd err %d\n", __func__, err); return MMC_ERR_FAILED; } value = ext_csd[EXT_CSD_PART_CFG] & 0x7; if (value != EXT_CSD_PART_CFG_DEFT_PART) { value = (ext_csd[EXT_CSD_PART_CFG] & ~0x7) | EXT_CSD_PART_CFG_DEFT_PART; err = mmc_set_part_config(card, value); if (err) { msdc_pr_err("%s, mmc_set_part_config failed!! (%x)\n", __func__, err); goto out; } } //Enable power-on protect value = card->ext_csd.boot_wp | EXT_CSD_BOOT_WP_EN_PWR_WP; //Set boot1 or boot2 protect if (partition == EMMC_PART_BOOT1) value &= ~EXT_CSD_BOOT_WP_WP_SEC_SEL; else value |= EXT_CSD_BOOT_WP_WP_SEC_SEL; //Set only 1 protect according EXT_CSD_BOOT_WP_WP_SEC_SEL value |= EXT_CSD_BOOT_WP_SEL; //check if already set if (card->ext_csd.boot_wp == value) { msdc_pr_info("[%s]: EXT_CSD_BOOT_WP alread set ", __func__); return 0; } err = mmc_switch(card->host, card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BOOT_WP, value); if (err) { msdc_pr_err("[%s]: mmc_switch err %d\n", __func__, err); goto out; } //check by read err = mmc_read_ext_csd(card->host, card); if (err) { msdc_pr_err("[%s]: read ext_csd err %d\n", __func__, err); } out: return err; } int mmc_set_user_wp(struct mmc_card *card, STORAGE_WP_TYPE type, unsigned long blknr, u32 blkcnt) { int err = MMC_ERR_FAILED; u8 *ext_csd = &card->raw_ext_csd[0]; u8 value; if (type > WP_DISABLE || type < 0) { msdc_pr_err("[%s]: arguments is valid\n", __func__); goto out; } /* check wp support in this emmc */ if (card->csd.write_prot_grp == 0) { msdc_pr_err("group write project not support in this emmc!\n"); return MMC_ERR_FAILED; } //check by read err = mmc_read_ext_csd(card->host, card); if (err) { msdc_pr_err("[%s]: read ext_csd err %d\n", __func__, err); return MMC_ERR_FAILED; } value = ext_csd[EXT_CSD_PART_CFG] & 0x7; if (value != EXT_CSD_PART_CFG_DEFT_PART) { value = (ext_csd[EXT_CSD_PART_CFG] & ~0x7) | EXT_CSD_PART_CFG_DEFT_PART; err = mmc_set_part_config(card, value); if (err) { msdc_pr_err("%s, mmc_set_part_config failed!! (%x)\n", __func__, err); goto out; } } //prepare for real settting //firstly disable perm_en, pwr_en if (card->ext_csd.usr_wp & US_PERM_WP_EN) { card->ext_csd.usr_wp &= ~US_PERM_WP_EN; err = mmc_switch_security(card->host, card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_USR_WP, US_PERM_WP_EN, MMC_SWITCH_MODE_CLEAR_BITS); if (err) { msdc_pr_err("[%s]: mmc_switch err %d\n", __func__, err); goto out; } } if (card->ext_csd.usr_wp & US_PWR_WP_EN) { card->ext_csd.usr_wp &= ~US_PWR_WP_EN; err = mmc_switch_security(card->host, card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_USR_WP, US_PWR_WP_EN, MMC_SWITCH_MODE_CLEAR_BITS); if (err) { msdc_pr_err("[%s]: mmc_switch err %d\n", __func__, err); goto out; } } if (type == WP_POWER_ON) { if ((type == WP_POWER_ON) && (card->ext_csd.usr_wp & US_PWR_WP_DIS)) { msdc_pr_err("[%s]: US_PWR_WP_DIS or US_PERM_WP_DIS is err set \n", __func__); goto out; } err = mmc_switch_security(card->host, card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_USR_WP, US_PWR_WP_EN, MMC_SWITCH_MODE_SET_BITS); if (err) { msdc_pr_err("[%s]: mmc_switch err %d\n", __func__, err); goto out; } //start to set the group wp err = mmc_set_write_prot_by_groups(card, blknr, blkcnt); if (err) { msdc_pr_err("[%s]: mmc_set_write_protect_by_group err%d\n", __func__, err); goto out; } //clear WP_EN err = mmc_switch_security(card->host, card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_USR_WP, US_PWR_WP_EN, MMC_SWITCH_MODE_CLEAR_BITS); if (err) { msdc_pr_err("[%s]: clear WP_EN err %d\n", __func__, err); goto out; } } else if (type == WP_PERMANENT) { if ((type == WP_PERMANENT) && (card->ext_csd.usr_wp & US_PERM_WP_DIS)) { msdc_pr_err("[%s]: US_PWR_WP_DIS or US_PERM_WP_DIS is err set\n", __func__); goto out; } #ifdef MTK_EMMC_SUPPORT_OTP_FOR_CUSTOMER /* WARNING: ONLY CAN ENABLE THIS OPTION ON CUSTOMER SIDE AFTER UT */ err = mmc_switch_security(card->host, card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_USR_WP, US_PERM_WP_EN, MMC_SWITCH_MODE_SET_BITS); #endif if (err) { msdc_pr_err("[%s]: mmc_switch err %d\n", __func__, err); goto out; } //start to set the group wp err = mmc_set_write_prot_by_groups(card, blknr, blkcnt); if (err) { msdc_pr_err("[%s]: mmc_set_write_protect_by_group err%d\n", __func__, err); goto out; } //clear WP_EN err = mmc_switch_security(card->host, card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_USR_WP, US_PERM_WP_EN, MMC_SWITCH_MODE_CLEAR_BITS); if (err) { msdc_pr_err("[%s]: clear WP_EN err %d\n", __func__, err); goto out; } } else if (type == WP_TEMPORARY) { //start to set the group wp err = mmc_set_write_prot_by_groups(card, blknr, blkcnt); if (err) { msdc_pr_err("[%s]: mmc_set_write_protect_by_group err%d\n", __func__, err); goto out; } } else if (type == WP_DISABLE) { //start to set the group wp err = mmc_clr_write_prot_by_groups(card, blknr, blkcnt); if (err) { msdc_pr_err("[%s]: mmc_clr_write_protect_by_group err%d\n", __func__, err); goto out; } } out: //check by read err = mmc_read_ext_csd(card->host, card); if (err) { msdc_pr_err("[%s]: read ext_csd err %d\n", __func__, err); } return err; } #endif #if defined(MTK_EMMC_SUPPORT_OTP) uint32 mmc_otp_start(int dev_num, unsigned long otpStart) { struct mmc_card *card; uint32 l_addr = otpStart; card = mmc_get_card(dev_num); if (!card || !mmc_card_mmc(card)) return 0; if (l_addr % card->wp_size){ l_addr += card->wp_size - l_addr % card->wp_size; } return l_addr; } int mmc_otp_lock(int dev_num, unsigned long blknr, ulong blkcnt, unsigned int part_id) { uint32 l_blkno; uint32 err = MMC_ERR_FAILED; u32 count_wp = 0; struct mmc_card *card; card = mmc_get_card(dev_num); if (!card || !mmc_card_mmc(card)) goto out; if (card->csd.mmca_vsn < CSD_SPEC_VER_4){ msdc_pr_err("emmc version is below 4!\n"); goto out; } /* check wp support in this emmc */ if (card->csd.write_prot_grp == 0){ msdc_pr_err("write project not support in this emmc!\n"); goto out; } l_blkno = mmc_otp_start(dev_num, blknr); if (blkcnt%card->wp_size) count_wp = blkcnt/card->wp_size + 1; else count_wp = blkcnt/card->wp_size; return mmc_set_write_protect(dev_num, part_id, l_blkno, card->wp_size * count_wp, WP_PERMANENT); out: return err; } int mmc_otp_status(int dev_num, unsigned long blknr, ulong blkcnt, unsigned int *status) { int err = MMC_ERR_FAILED; uint32 l_blkno; u32 wp_status; u32 count_wp = 0; struct mmc_card *card; card = mmc_get_card(dev_num); if (!card || !mmc_card_mmc(card)) goto out; if (card->csd.mmca_vsn < CSD_SPEC_VER_4) { msdc_pr_err("emmc version is below 4!\n"); goto out; } /* change to OTP physic address */ l_blkno = mmc_otp_start(dev_num, blknr); if (blkcnt%card->wp_size) count_wp = blkcnt/card->wp_size + 1; else count_wp = blkcnt/card->wp_size; /* check lock status */ if (mmc_check_write_prot_type_by_groups(card, l_blkno, card->wp_size * count_wp, &wp_status)) { msdc_pr_err("error: mmc_check_write_prot_type_by_groups faild!\n"); goto out; } #ifndef MTK_EMMC_SUPPORT_OTP_FOR_CUSTOMER if (wp_status == WP_TEMPORARY) #else if (wp_status == WP_PERMANENT) #endif { *status = EMMC_OTP_LOCKED; msdc_pr_err("[OTP]Status: EMMC_OTP_LOCKED\n"); } else if (wp_status == WP_DISABLE) { *status = EMMC_OTP_UNLOCKED; msdc_pr_err("[OTP]Status: EMMC_OTP_UNLOCKED\n"); } else { *status = EMMC_OTP_OTHER_STATUS; msdc_pr_err("[OTP]Status: EMMC_OTP_OTHER_STATUS\n"); } return MMC_ERR_NONE; out: return err; } #endif int mmc_dev_bread(struct mmc_card *card, unsigned long blknr, u32 blkcnt, u8 *dst) { struct mmc_host *host = card->host; u32 blksz = host->blklen; int tune = 0; #if defined(FEATURE_MMC_RD_TUNING) || defined(MSDC_SLT) int retry = 1; #else int retry = 3; #endif int err; unsigned long src; u8 *oridst = dst; src = mmc_card_highcaps(card) ? blknr : blknr * blksz; do { mmc_prof_start(); if (!tune) { err = host->blk_read(host, (uchar *)dst, src, blkcnt); } else { #ifdef FEATURE_MMC_RD_TUNING if (mmc_card_mmc(card) && mmc_card_hs400(card)) { msdc_pr_err("[tune][%s:%d] start hs400 read tune\n", __func__, __LINE__); err = msdc_tune_rw_hs400(host, (uchar *)oridst, src, blkcnt, 0); } else { err = msdc_tune_bread(host, (uchar *)oridst, src, blkcnt); } #endif if (err && (host->cur_bus_clk > (host->f_max >> 4))) { mmc_set_clock(host, mmc_card_ddr(card), host->cur_bus_clk >> 1); err = host->blk_read(host, (uchar *)oridst, src, blkcnt); } } mmc_prof_stop(); if (err == MMC_ERR_NONE) { mmc_prof_update(mmc_prof_read, blkcnt, mmc_prof_handle(host->id)); break; } #if defined(FEATURE_MMC_CM_TUNING) || defined(FEATURE_MMC_RD_TUNING) if (err == MMC_ERR_BADCRC || err == MMC_ERR_ACMD_RSPCRC || err == MMC_ERR_CMD_RSPCRC || err == MMC_ERR_TIMEOUT) { if ( tune ) break; tune = 1; } else if (err == MMC_ERR_READTUNEFAIL || err == MMC_ERR_CMDTUNEFAIL) { msdc_pr_err("[SD%d] Fail to tuning,%s",host->id,(err == MMC_ERR_CMDTUNEFAIL)?"cmd tune failed!\n":"read tune failed!\n"); break; } #elif defined(MSDC_SLT_ANALYSIS) if (err == MMC_ERR_BADCRC || err == MMC_ERR_ACMD_RSPCRC || err == MMC_ERR_CMD_RSPCRC || err == MMC_ERR_TIMEOUT){ err = MMC_ERR_NONE; break; } #endif } while (retry--); return err; } static int mmc_dev_bwrite(struct mmc_card *card, unsigned long blknr, u32 blkcnt, u8 *src) { struct mmc_host *host = card->host; u32 blksz = host->blklen; u32 status; int tune = 0; #if defined(FEATURE_MMC_WR_TUNING) || defined(MSDC_SLT) int retry = 1; #else int retry = 3; #endif int err; unsigned long dst; dst = mmc_card_highcaps(card) ? blknr : blknr * blksz; do { mmc_prof_start(); if (!tune) { err = host->blk_write(host, dst, (uchar *)src, blkcnt); } else { #if defined(FEATURE_MMC_WR_TUNING) if (mmc_card_mmc(card) && mmc_card_hs400(card)) { err = msdc_tune_rw_hs400(host, (uchar *) dst, (ulong) src, blkcnt, 1); } else { err = msdc_tune_bwrite(host, dst, (uchar *)src, blkcnt); } #endif if (err && (host->cur_bus_clk > (host->f_max >> 4))) { mmc_set_clock(host, mmc_card_ddr(card), host->cur_bus_clk >> 1); err = host->blk_write(host, dst, (uchar *)src, blkcnt); } } if (err == MMC_ERR_NONE) { do { err = mmc_send_status(host, card, &status); if (err) { msdc_pr_err("[SD%d] Fail to send status %d\n", host->id, err); break; } } while (!(status & R1_READY_FOR_DATA) || (R1_CURRENT_STATE(status) == 7) || (R1_CURRENT_STATE(status) == 6)); mmc_prof_stop(); mmc_prof_update(mmc_prof_write, blkcnt, mmc_prof_handle(host->id)); MSG(OPS, "[SD%d] Write %d bytes (DONE)\n", host->id, blkcnt * blksz); break; } #if defined(FEATURE_MMC_WR_TUNING) if (err == MMC_ERR_BADCRC || err == MMC_ERR_ACMD_RSPCRC || err == MMC_ERR_CMD_RSPCRC || err == MMC_ERR_TIMEOUT) { if ( tune ) break; tune = 1; } #elif defined(MSDC_SLT_ANALYSIS) if (err == MMC_ERR_BADCRC || err == MMC_ERR_ACMD_RSPCRC || err == MMC_ERR_CMD_RSPCRC || err == MMC_ERR_TIMEOUT){ err = MMC_ERR_NONE; break; } #endif } while (retry--); return err; } int mmc_block_read(int dev_num, unsigned long blknr, u32 blkcnt, unsigned long *dst) { struct mmc_host *host = mmc_get_host(dev_num); struct mmc_card *card = mmc_get_card(dev_num); u32 blksz = host->blklen; u32 maxblks = host->max_phys_segs; //u32 xfercnt = blkcnt / maxblks; //u32 leftblks = blkcnt % maxblks; u32 leftblks; u8 *buf = (u8*)dst; int ret; #ifdef FEATURE_NONBLOCKING_RW if ( blkcnt> maxblks ) { msdc_pr_err("[SD%d] Non-blocking RW does not allow blkcnt(%d) > maxblks(%d)\n", host->id, blkcnt, maxblks); BUG_ON(1); } #endif #ifdef FEATURE_MMC_CMDQ if ( host->mmc_cmdq_enable ) { if ( host->mmc_cmdq_first_task == (host->mmc_cmdq_last_task+1)%MMC_MAX_QUEUE_DEPTH_USER ) { //msdc_pr_err("\n[SD%d] CMDQ Full: mmc_cmdq_first_task %d, mmc_cmdq_last_task %d\n",host->id, host->mmc_cmdq_first_task, host->mmc_cmdq_last_task); BUG_ON(1); } else { host->mmc_cmdq_req[host->mmc_cmdq_last_task].blknr=blknr; host->mmc_cmdq_req[host->mmc_cmdq_last_task].blkcnt=blkcnt; host->mmc_cmdq_req[host->mmc_cmdq_last_task].task_id=0; host->mmc_cmdq_req[host->mmc_cmdq_last_task].buf=buf; host->mmc_cmdq_last_task++; if ( host->mmc_cmdq_last_task==MMC_MAX_QUEUE_DEPTH_USER ) { host->mmc_cmdq_last_task=0; } //msdc_pr_err("\n[SD%d] Task ID1: mmc_cmdq_first_task %d, mmc_cmdq_last_task %d\n",host->id, host->mmc_cmdq_first_task, host->mmc_cmdq_last_task); } //msdc_set_autocmd(host, MSDC_AUTOCMD23, 1); //Temp for emulation. To do: remove it later } #endif if (!blkcnt) return MMC_ERR_NONE; if (blknr * (blksz / MMC_BLOCK_SIZE) > card->nblks) { msdc_pr_err("[SD%d] Out of block range: blknr(%d) > sd_blknr(%d)\n", host->id, (unsigned int)blknr, card->nblks); return MMC_ERR_INVALID; } do { leftblks=((blkcnt> maxblks) ? maxblks : blkcnt); ret = mmc_dev_bread(card, (unsigned long)blknr, leftblks, buf); if (ret) return ret; blknr += leftblks; buf += maxblks * blksz; blkcnt -= leftblks; } while ( blkcnt ); return ret; } int mmc_block_write(int dev_num, unsigned long blknr, u32 blkcnt, unsigned long *src) { struct mmc_host *host = mmc_get_host(dev_num); struct mmc_card *card = mmc_get_card(dev_num); u32 blksz = host->blklen; u32 maxblks = host->max_phys_segs; //u32 xfercnt = blkcnt / maxblks; //u32 leftblks = blkcnt % maxblks; u32 leftblks; u8 *buf = (u8*)src; int ret; #ifdef FEATURE_NONBLOCKING_RW if ( blkcnt> maxblks ) { msdc_pr_err("[SD%d] Non-blocking RW does not allow blkcnt(%d) > maxblks(%d)\n", host->id, blkcnt, maxblks); BUG_ON(1); } #endif if (!blkcnt) return MMC_ERR_NONE; if (blknr * (blksz / MMC_BLOCK_SIZE) > card->nblks) { msdc_pr_err("[SD%d] Out of block range: blknr(%d) > sd_blknr(%d)\n", host->id, (unsigned int)blknr, card->nblks); return MMC_ERR_INVALID; } do { leftblks=((blkcnt> maxblks) ? maxblks : blkcnt); ret = mmc_dev_bwrite(card, (unsigned long)blknr, leftblks, buf); if (ret) return ret; blknr += leftblks; buf += maxblks * blksz; blkcnt -= leftblks; } while ( blkcnt ); return ret; } #ifdef FEATURE_NONBLOCKING_RW int mmc_block_rw_nonblocking_check_done(int dev_num) { int ret=MMC_ERR_NONE; struct mmc_host *host = mmc_get_host(dev_num); ret = msdc_dma_nonblocking_check_done(host); //To do: Add code for PIO return ret; } #endif #if defined(MMC_MSDC_DRV_PRELOADER) void mmc_stuff_buff(u8* buf) { memset(buf,0,512); buf[0] = 0x10; buf[1] = 0x06; buf[2] = 0x01; buf[3] = 0xF0; buf[11]= 0xAA; buf[12]= 0xA9; buf[13]= 0x87; buf[14]= 0x74; buf[15]= 0x3C; buf[16]= 0x71; buf[17]= 0xFB; buf[18]= 0xD4; } #if defined(FEATURE_EMCP) int mmc_get_sandisk_fwid(int id, u8* buf) { struct mmc_host *host; struct mmc_card *card; struct mmc_command stop; int err = MMC_ERR_NONE; u32 status; u32 state = 0; host = &sd_host[id]; card = &sd_card[id]; while (state != 4) { err = mmc_send_status(host, card, &status); if (err) { msdc_pr_err("[SD%d] Fail to send status %d\n", host->id, err); return err; } state = R1_CURRENT_STATE(status); msdc_pr_info("check card state<%d>\n", state); if (state == 5 || state == 6) { msdc_pr_info("state<%d> need cmd12 to stop\n", state); stop.opcode = MMC_CMD_STOP_TRANSMISSION; stop.rsptyp = RESP_R1B; stop.arg = 0; stop.retries = CMD_RETRIES; stop.timeout = CMD_TIMEOUT; msdc_send_cmd(host, &stop); msdc_wait_rsp(host, &stop); // don't tuning } else if (state == 7) { // busy in programing msdc_pr_info("state<%d> card is busy\n", state); mdelay(100); } else if (state != 4) { msdc_pr_err("state<%d> ??? \n", state); return MMC_ERR_INVALID; } } mmc_stuff_buff(buf); #if defined(MSDC_ENABLE_DMA_MODE) err = msdc_dma_send_sandisk_fwid(host, buf,MMC_CMD50,1); if (err) { msdc_pr_err("[SD%d] Fail to send(CMD50) sandisk fwid %d\n", host->id, err); return err; } err = msdc_dma_send_sandisk_fwid(host, buf,MMC_CMD21,1); if (err) { msdc_pr_err("[SD%d] Fail to get(CMD21) sandisk fwid %d\n", host->id, err); return err; } #else err = msdc_pio_send_sandisk_fwid(host, buf); if (err) { msdc_pr_err("[SD%d] Fail to send(CMD50) sandisk fwid %d\n", host->id, err); return err; } err = msdc_pio_get_sandisk_fwid(host, buf); if (err) { msdc_pr_err("[SD%d] Fail to get(CMD21) sandisk fwid %d\n", host->id, err); return err; } #endif return err; } #endif #endif #ifdef FEATURE_MMC_BOOT_MODE void mmc_boot_reset(struct mmc_host *host, int reset) { msdc_emmc_boot_reset(host, reset); } int mmc_boot_up(struct mmc_host *host, int ddr, int mode, u8 hostbuswidth, int ackdis, u32 *to, u64 size, int read_mode) { int err; ERR_EXIT(msdc_emmc_boot_start(host, host->cur_bus_clk, ddr, mode, ackdis, hostbuswidth, size), err, MMC_ERR_NONE); ERR_EXIT(msdc_emmc_boot_read(host, size, to, read_mode), err, MMC_ERR_NONE); exit: msdc_emmc_boot_stop(host); return err; } #endif int mmc_init_mem_card(struct mmc_host *host, struct mmc_card *card, u32 ocr) { int err = MMC_ERR_NONE, id = host->id; #if defined(FEATURE_MMC_UHS1) int s18a = 0; #endif /* * Sanity check the voltages that the card claims to * support. */ if (ocr & 0x7F) { msdc_pr_err("card claims to support voltages " "below the defined range. These will be ignored.\n"); ocr &= ~0x7F; } ocr = host->ocr = mmc_select_voltage(host, ocr); /* * Can we support the voltage(s) of the card(s)? */ if (!host->ocr) { err = MMC_ERR_FAILED; goto out; } mmc_go_idle(host); /* send interface condition */ if (mmc_card_sd(card)) err = mmc_send_if_cond(host, ocr); /* host support HCS[30] */ ocr |= (1 << 30); #if defined(FEATURE_MMC_UHS1) if (!err) { /* host support S18A[24] and XPC[28]=1 to support speed class */ if (host->caps & MMC_CAP_SD_UHS1) ocr |= ((1 << 28) | (1 << 24)); card->version = SD_VER_20; } else { card->version = SD_VER_10; } #else card->version = SD_VER_10; #endif /* send operation condition */ if (mmc_card_sd(card)) { err = mmc_send_app_op_cond(host, ocr, &card->ocr); } else { /* The extra bit indicates that we support high capacity */ err = mmc_send_op_cond(host, ocr, &card->ocr); } if (err != MMC_ERR_NONE) { msdc_pr_err("[SD%d] Fail in SEND_OP_COND cmd\n", id); goto out; } /* set hcs bit if a high-capacity card */ card->state |= ((card->ocr >> 30) & 0x1) ? MMC_STATE_HIGHCAPS : 0; #if defined(FEATURE_MMC_UHS1) s18a = (card->ocr >> 24) & 0x1; msdc_pr_info("[SD%d] ocr = 0x%X, card->ocr=0x%X, s18a = %d \n", id, ocr, card->ocr, s18a); /* S18A support by card. switch to 1.8V signal */ if (s18a) { card->version = SD_VER_30; err = mmc_switch_volt(host, card); if (err != MMC_ERR_NONE) { msdc_pr_err("[SD%d] Fail in SWITCH_VOLT cmd\n", id); goto out; } } #endif /* send cid */ err = mmc_all_send_cid(host, card->raw_cid); if (err != MMC_ERR_NONE) { msdc_pr_err("[SD%d] Fail in SEND_CID cmd\n", id); goto out; } if (mmc_card_mmc(card)) card->rca = 0x1; /* assign a rca */ /* set/send rca */ err = mmc_send_relative_addr(host, card, &card->rca); if (err != MMC_ERR_NONE) { msdc_pr_err("[SD%d] Fail in SEND_RCA cmd\n", id); goto out; } /* send csd */ err = mmc_read_csds(host, card); if (err != MMC_ERR_NONE) { msdc_pr_err("[SD%d] Fail in SEND_CSD cmd\n", id); goto out; } /* decode csd */ err = mmc_decode_csd(card); if (err != MMC_ERR_NONE) { msdc_pr_err("[SD%d] Fail in decode csd\n", id); goto out; } //CID shall be decoded after decoding CSD mmc_decode_cid(card); /* select this card */ err = mmc_select_card(host, card); if (err != MMC_ERR_NONE) { msdc_pr_err("[SD%d] Fail in select card cmd\n", id); goto out; } if (mmc_card_sd(card)) { #if !defined(FEATURE_MMC_SLIM) /* send scr */ err = mmc_read_scrs(host, card); if (err != MMC_ERR_NONE) { msdc_pr_err("[SD%d] Fail in SEND_SCR cmd\n", id); goto out; } #endif if ((card->csd.cmdclass & CCC_SWITCH) && (mmc_read_switch(host, card) == MMC_ERR_NONE)) { do { #if defined(FEATURE_MMC_UHS1) if (s18a && (host->caps & MMC_CAP_SD_UHS1)) { /* TODO: Switch driver strength first then current limit * and access mode */ unsigned int freq, uhs_mode, drv_type, max_curr; freq = min(host->f_max, card->sw_caps.hs_max_dtr); if (freq > 100000000) { uhs_mode = MMC_SWITCH_MODE_SDR104; } else if (freq <= 100000000 && freq > 50000000) { if (card->sw_caps.ddr && (host->caps & MMC_CAP_DDR)) { uhs_mode = MMC_SWITCH_MODE_DDR50; } else { uhs_mode = MMC_SWITCH_MODE_SDR50; } } else if (freq <= 50000000 && freq > 25000000) { uhs_mode = MMC_SWITCH_MODE_SDR25; } else { uhs_mode = MMC_SWITCH_MODE_SDR12; } drv_type = MMC_SWITCH_MODE_DRV_TYPE_B; max_curr = MMC_SWITCH_MODE_CL_200MA; if (mmc_switch_drv_type(host, card, drv_type) == MMC_ERR_NONE && mmc_switch_max_cur(host, card, max_curr) == MMC_ERR_NONE && mmc_switch_uhs1(host, card, uhs_mode) == MMC_ERR_NONE) { break; } else { mmc_switch_drv_type(host, card, MMC_SWITCH_MODE_DRV_TYPE_B); mmc_switch_max_cur(host, card, MMC_SWITCH_MODE_CL_200MA); } } #endif if (host->caps & MMC_CAP_SD_HIGHSPEED) { mmc_switch_hs(host, card); break; } } while (0); } /* set bus width */ mmc_set_bus_width(host, card, HOST_BUS_WIDTH_4); /* compute bus speed. */ card->maxhz = (unsigned int)-1; if (mmc_card_highspeed(card) || mmc_card_uhs1(card)) { if (card->maxhz > card->sw_caps.hs_max_dtr) card->maxhz = card->sw_caps.hs_max_dtr; } else if (card->maxhz > card->csd.max_dtr) { card->maxhz = card->csd.max_dtr; } } else { /* at the begin, the emmc card is under backward mode, this mode can support 1/4/8 buswidth */ mmc_card_set_backyard(card); /* send ext csd */ err = mmc_read_ext_csd(host, card); if (err != MMC_ERR_NONE) { msdc_pr_err("[SD%d] Fail in SEND_EXT_CSD cmd\n", id); goto out; } if ((card->ext_csd.hs_max_dtr > 52000000) && (host->caps & MMC_CAP_EMMC_HS400)) { /* activate hs400 (if supported) */ err = mmc_set_blk_length(host, MMC_BLOCK_SIZE); if ( err != MMC_ERR_NONE ) goto out; //Step1. switch to High-speed and becomes 1-bit@High-speed err = mmc_switch(host, card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1); if ( err != MMC_ERR_NONE ) { msdc_pr_err("[SD%d] Switch to High-speed mode failed!\n", host->id); goto out; } //Step2. switch to 8-bit DDR and becomes 8-bit@DDR50 err = mmc_switch(host, card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_8_DDR); if (err != MMC_ERR_NONE) { msdc_pr_err("[SD%d] Switch to 8-bit failed!\n", host->id); goto out; } msdc_config_bus(host, HOST_BUS_WIDTH_8); //Step3. switch to HS400 and becomes 8-bit@HS400 err = mmc_switch(host, card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 3); if ( err != MMC_ERR_NONE ) { msdc_pr_err("[SD%d] Switch to HS400 mode failed!\n", host->id); goto out; } mmc_card_set_hs400(card); } else if ((card->ext_csd.hs_max_dtr > 52000000) && (host->caps & MMC_CAP_EMMC_HS200)) { /* activate hs200 (if supported) */ //Step1. switch to 8-bit and becomes 8-bit@legacy-speed err = mmc_switch(host, card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_8); if (err != MMC_ERR_NONE) { msdc_pr_err("[SD%d] Switch to 8-bit failed!\n", host->id); goto out; } //Step2. switch to HS200 and becomes 8-bit@HS200 err = mmc_switch(host, card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 2); if (err == MMC_ERR_NONE) { msdc_pr_info("[SD%d] Switch to HS200 mode!\n", host->id); mmc_card_set_hs200(card); } mmc_set_bus_width(host, card, HOST_BUS_WIDTH_8); } else if ((card->ext_csd.hs_max_dtr != 0) && (host->caps & MMC_CAP_MMC_HIGHSPEED)) { /* activate hs (if supported) */ //Step1. switch to High-speed and becomes 1-bit@High-speed err = mmc_switch(host, card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1); if (err == MMC_ERR_NONE) { msdc_pr_info("[SD%d] Switch to High-Speed mode!\n", host->id); mmc_card_set_highspeed(card); } //Step2. switch to 8-bit(SDR or DDR) and becomes 8-bit@DDR50 or 8-bit@High-speed //Note: mmc_set_bus_width will determine switching to 8-bit SDR or 8-bit DDR according to extcsd mmc_set_bus_width(host, card, HOST_BUS_WIDTH_8); } else { mmc_set_bus_width(host, card, HOST_BUS_WIDTH_8); } /* compute bus speed. */ card->maxhz = (unsigned int)-1; if (mmc_card_highspeed(card)) { card->maxhz = 52000000; } else if ( mmc_card_hs200(card) || mmc_card_hs400(card)) { if (card->maxhz > card->ext_csd.hs_max_dtr) card->maxhz = card->ext_csd.hs_max_dtr; } else if (card->maxhz > card->csd.max_dtr) { card->maxhz = card->csd.max_dtr; } } /* set block len. note that cmd16 is illegal while mmc card is in ddr mode */ if (!(mmc_card_mmc(card) && (mmc_card_ddr(card) || mmc_card_hs400(card)))) { err = mmc_set_blk_length(host, MMC_BLOCK_SIZE); if (err != MMC_ERR_NONE) { msdc_pr_err("[SD%d] Fail in set blklen cmd, card state=0x%x\n", id, card->state); goto out; } } /* set clear card detect */ if (mmc_card_sd(card)) mmc_set_card_detect(host, card, 0); if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) { /* The EXT_CSD sector count is in number or 512 byte sectors. */ card->blklen = MMC_BLOCK_SIZE; card->nblks = card->ext_csd.sectors; } else { /* The CSD capacity field is in units of read_blkbits. * set_capacity takes units of 512 bytes. */ card->blklen = MMC_BLOCK_SIZE; card->nblks = card->csd.capacity << (card->csd.read_blkbits - 9); } msdc_pr_err("[SD%d] Size: %d MB, Max.Speed: %d kHz, blklen(%d), nblks(%d)\n", id, ((card->nblks / 1024) * card->blklen) / 1024 , card->maxhz / 1000, card->blklen, card->nblks); card->ready = 1; msdc_pr_err("[SD%d] Initialized, %s%d\n", id, mmc_card_sd(card)?"SD":"eMMC", card->version); out: return err; } int mmc_init_card(struct mmc_host *host, struct mmc_card *card) { int err, id = host->id; u32 ocr; msdc_pr_info("[%s]: start\n", __func__); memset(card, 0, sizeof(struct mmc_card)); mmc_prof_init(id, host, card); mmc_prof_start(); #ifdef FEATURE_MMC_CARD_DETECT if (!msdc_card_avail(host)) { err = MMC_ERR_INVALID; goto out; } #endif #if 0 if (msdc_card_protected(host)) mmc_card_set_readonly(card); #endif mmc_card_set_present(card); mmc_card_set_host(card, host); mmc_card_set_unknown(card); mmc_go_idle(host); #ifdef FEATURE_MMC_SDCARD /* send interface condition */ mmc_send_if_cond(host, host->ocr_avail); #endif #if defined(FEATURE_MMC_SDIO) if (mmc_send_io_op_cond(host, 0, &ocr) == MMC_ERR_NONE) { mmc_card_set_sdio(card); err = mmc_init_sdio_card(host, card, ocr); if (err != MMC_ERR_NONE) { msdc_pr_err("[SD%d] Fail in init sdio card\n", id); goto out; } /* no memory present */ if ((ocr & 0x08000000) == 0) { goto out; } } #endif #ifdef FEATURE_MMC_SDCARD /* query operation condition */ err = mmc_send_app_op_cond(host, 0, &ocr); if (err != MMC_ERR_NONE) { #endif err = mmc_send_op_cond(host, 0, &ocr); if (err != MMC_ERR_NONE) { msdc_pr_err("[SD%d] Fail in MMC_CMD_SEND_OP_COND/SD_ACMD_SEND_OP_COND cmd\n", id); goto out; } mmc_card_set_mmc(card); #ifdef FEATURE_MMC_SDCARD } else { mmc_card_set_sd(card); } #endif err = mmc_init_mem_card(host, card, ocr); if (err) goto out; /* change clock */ host->card = card; mmc_set_clock(host, mmc_card_ddr(card), card->maxhz); #if defined(FEATURE_MMC_UHS1) /* tune timing */ //mmc_tune_timing(host, card); #endif out: mmc_prof_stop(); mmc_prof_update(mmc_prof_card_init, (ulong)id, (void*)err); if (err) { //msdc_power(host, MMC_POWER_OFF); msdc_pr_err("[%s]: failed, err=%d\n", __func__, err); return err; } host->card = card; // HS400 change again, previous mmc_set_clock host->card is null. // host is not set to HS400 if (mmc_card_hs400(card)) { mmc_set_clock(host, mmc_card_ddr(card), card->maxhz); } msdc_pr_info("[%s]: finish successfully\n", __func__); return 0; } int mmc_init_host(struct mmc_host *host, int id, int clksrc, u32 mode) { memset(host, 0, sizeof(struct mmc_host)); return msdc_init(id, host, clksrc, mode); } int mmc_init(int id, u32 trans_mode) { int err = MMC_ERR_NONE; struct mmc_host *host; struct mmc_card *card; BUG_ON(id >= NR_MMC); host = &sd_host[id]; card = &sd_card[id]; err = mmc_init_host(host, id, -1, trans_mode); if (err != MMC_ERR_NONE) return err; msdc_pr_info("[%s]: msdc%d start mmc_init_card()\n", __func__, id); err = mmc_init_card(host, card); if (err != MMC_ERR_NONE) return err; #ifdef MTK_EMMC_SUPPORT_OTP msdc_pr_info("[%s]: msdc%d, use hc erase size\n", __func__, id); if (mmc_card_mmc(card)) err = mmc_set_erase_grp_def(card, 1); #endif #ifdef MMC_TEST //mmc_test(0, NULL); #endif #ifdef FEATURE_MMC_POWER_ON_WP //mmc_wp_test(); #endif #ifdef MTK_MSDC_PL_TEST msdc_pr_info("[%s]: start r/w compare test \n", __func__); emmc_r_w_compare_test(); #endif return err; } #if defined(MMC_MSDC_DRV_CTP) int mmc_polling_CD_INT(struct mmc_host * host) { return msdc_polling_CD_interrupt(host); } #endif