// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. */ #ifdef pr_fmt #undef pr_fmt #endif #define pr_fmt(fmt) "["KBUILD_MODNAME"]" fmt #include #include #include #include #include #include #include //#include #include #include #include #include #include #include "mtk_sd.h" #include #include #include #include "dbg.h" #include "autok_dvfs.h" #if !defined(FPGA_PLATFORM) && !defined(CONFIG_MTK_MSDC_BRING_UP_BYPASS) #include #endif #ifdef MTK_MSDC_BRINGUP_DEBUG #include #endif #ifdef MTK_IO_PERFORMANCE_DEBUG unsigned int g_mtk_mmc_perf_dbg; unsigned int g_mtk_mmc_dbg_range; unsigned int g_dbg_range_start; unsigned int g_dbg_range_end; unsigned int g_mtk_mmc_dbg_flag; unsigned int g_dbg_req_count; unsigned int g_dbg_raw_count; unsigned int g_dbg_write_count; unsigned int g_dbg_raw_count_old; unsigned int g_mtk_mmc_clear; int g_check_read_write; int g_i; unsigned long long g_req_buf[4000][30] = { {0} }; unsigned long long g_req_write_buf[4000][30] = { {0} }; unsigned long long g_req_write_count[4000] = { 0 }; unsigned long long g_mmcqd_buf[400][300] = { {0} }; char *g_time_mark[] = { "--start fetch request", "--end fetch request", "--start dma map this request", "--end dma map this request", "--start request", "--DMA start", "--DMA transfer done", "--start dma unmap request", "--end dma unmap request", "--end of request", }; char *g_time_mark_vfs_write[] = { "--in vfs_write", "--before generic_segment_checks", "--after generic_segment_checks", "--after vfs_check_frozen", "--after generic_write_checks", "--after file_remove_suid", "--after file_update_time", "--after generic_file_direct_write", "--after generic_file_buffered_write", "--after filemap_write_and_wait_range", "--after invalidate_mapping_pages", "--after 2nd generic_file_buffered_write", "--before generic_write_sync", "--after generic_write_sync", "--out vfs_write" }; #endif /* for get transfer time with each trunk size, default not open */ #ifdef MTK_MMC_PERFORMANCE_TEST unsigned int g_mtk_mmc_perf_test; #endif static char cmd_buf[256]; #ifdef MTK_MMC_SDIO_DEBUG /* for a type command, e.g. CMD53, 2 blocks */ struct cmd_profile { u32 max_tc; /* Max tick count */ u32 min_tc; u32 tot_tc; /* total tick count */ u32 tot_bytes; u32 count; /* the counts of the command */ }; /* dump when total_tc and total_bytes */ struct sdio_profile { u32 total_tc; /* total tick count of CMD52 and CMD53 */ u32 total_tx_bytes; /* total bytes of CMD53 Tx */ u32 total_rx_bytes; /* total bytes of CMD53 Rx */ /*CMD52 */ struct cmd_profile cmd52_tx; struct cmd_profile cmd52_rx; /*CMD53 in byte unit */ struct cmd_profile cmd53_tx_byte[512]; struct cmd_profile cmd53_rx_byte[512]; /*CMD53 in block unit */ struct cmd_profile cmd53_tx_blk[100]; struct cmd_profile cmd53_rx_blk[100]; }; /* for driver profile */ #define TICKS_ONE_MS (1000000) u32 sdio_pro_enable; static unsigned long long sdio_pro_time = 30; /* no more than 30s */ static unsigned long long sdio_profiling_start; struct sdio_profile sdio_performance = { 0 }; #endif /*#define MTK_MSDC_ERROR_TUNE_DEBUG*/ #ifdef MSDC_DMA_ADDR_DEBUG struct dma_addr msdc_latest_dma_address[MAX_BD_PER_GPD]; static struct dma_addr *msdc_get_dma_address(int host_id) { struct bd_t *bd; int i = 0; int mode = -1; struct msdc_host *host; void __iomem *base; if (host_id < 0 || host_id >= HOST_MAX_NUM || !mtk_msdc_host[host_id]) { pr_notice("[%s] invalid host_id %d\n", __func__, host_id); return NULL; } host = mtk_msdc_host[host_id]; base = host->base; /* spin_lock(&host->lock); */ MSDC_GET_FIELD(MSDC_DMA_CTRL, MSDC_DMA_CTRL_MODE, mode); if (mode == 1) { pr_info("Desc.DMA\n"); bd = host->dma.bd; i = 0; while (i < MAX_BD_PER_GPD) { msdc_latest_dma_address[i].start_address = (u32) bd[i].ptr; msdc_latest_dma_address[i].size = bd[i].buflen; msdc_latest_dma_address[i].end = bd[i].eol; if (i > 0) msdc_latest_dma_address[i - 1].next = &msdc_latest_dma_address[i]; if (bd[i].eol) break; i++; } } else if (mode == 0) { pr_info("Basic DMA\n"); msdc_latest_dma_address[0].start_address = MSDC_READ32(MSDC_DMA_SA); msdc_latest_dma_address[0].size = MSDC_READ32(MSDC_DMA_LEN); msdc_latest_dma_address[0].end = 1; } /* spin_unlock(&host->lock); */ return msdc_latest_dma_address; } static void msdc_init_dma_latest_address(void) { struct dma_addr *ptr, *prev; int bdlen = MAX_BD_PER_GPD; memset(msdc_latest_dma_address, 0, sizeof(struct dma_addr) * bdlen); ptr = msdc_latest_dma_address + bdlen - 1; while (ptr != msdc_latest_dma_address) { prev = ptr - 1; prev->next = (void *)(msdc_latest_dma_address + (ptr - msdc_latest_dma_address)); ptr = prev; } } #endif #define dbg_max_cnt (4000) #ifdef CONFIG_MTK_MMC_DEBUG #ifdef MTK_MSDC_LOW_IO_DEBUG #define dbg_max_cnt_low_io (5000) #define criterion_low_io (10 * 1024) /* unit: KB/s */ #endif #define MSDC_AEE_BUFFER_SIZE (300 * 1024) struct dbg_run_host_log { unsigned long long time_sec; unsigned long long time_usec; int type; int cmd; int arg; int cpu; unsigned long active_reqs; int skip; }; #ifdef MTK_MSDC_LOW_IO_DEBUG struct dbg_run_host_log_low_io { int cmd; u32 address; unsigned long long size; unsigned long long time; unsigned long long time_diff; int continuous_count; }; #endif struct dbg_task_log { u32 address; unsigned long long size; }; struct dbg_dma_cmd_log { unsigned long long time; int cmd; int arg; }; static struct dbg_run_host_log dbg_run_host_log_dat[dbg_max_cnt]; #ifdef MTK_MSDC_LOW_IO_DEBUG static struct dbg_run_host_log_low_io dbg_run_host_log_dat_low_io[dbg_max_cnt_low_io]; static int dbg_host_cnt_low_io; #endif static struct dbg_dma_cmd_log dbg_dma_cmd_log_dat; static struct dbg_task_log dbg_task_log_dat[32]; char msdc_aee_buffer[MSDC_AEE_BUFFER_SIZE]; static int dbg_host_cnt; static unsigned int print_cpu_test = UINT_MAX; /* * type 0: cmd; type 1: rsp; type 3: dma end * when type 3: arg 0: no data crc error; arg 1: data crc error * @cpu, current CPU ID * @reserved, userd for softirq dump "data_active_reqs" */ inline void __dbg_add_host_log(struct mmc_host *mmc, int type, int cmd, int arg, int cpu, unsigned long reserved) { unsigned long long t, tn; unsigned long long nanosec_rem; #ifdef CONFIG_MTK_EMMC_HW_CQ unsigned long flags; #endif static int last_cmd, last_arg, skip; int l_skip = 0; struct msdc_host *host = mmc_priv(mmc); static int tag = -1; #ifdef MTK_MSDC_LOW_IO_DEBUG static int continuous_count_low_io; #endif /* only log msdc0 */ if (!host || host->id != 0) return; t = cpu_clock(print_cpu_test); #ifdef CONFIG_MTK_EMMC_HW_CQ spin_lock_irqsave(&host->cmd_dump_lock, flags); #endif switch (type) { case 0: /* normal - cmd */ tn = t; nanosec_rem = do_div(t, 1000000000)/1000; if (cmd == 44) { tag = (arg >> 16) & 0x1f; dbg_task_log_dat[tag].size = arg & 0xffff; } else if (cmd == 45) { dbg_task_log_dat[tag].address = arg; } else if (cmd == 46 || cmd == 47) { dbg_dma_cmd_log_dat.time = tn; dbg_dma_cmd_log_dat.cmd = cmd; dbg_dma_cmd_log_dat.arg = arg; } dbg_run_host_log_dat[dbg_host_cnt].time_sec = t; dbg_run_host_log_dat[dbg_host_cnt].time_usec = nanosec_rem; dbg_run_host_log_dat[dbg_host_cnt].type = type; dbg_run_host_log_dat[dbg_host_cnt].cmd = cmd; dbg_run_host_log_dat[dbg_host_cnt].arg = arg; dbg_run_host_log_dat[dbg_host_cnt].skip = l_skip; dbg_host_cnt++; if (dbg_host_cnt >= dbg_max_cnt) dbg_host_cnt = 0; break; case 1: /* normal -rsp */ case 5: /* cqhci - data */ case 60: /* cqhci - dcmd */ case 61: /* cqhci - dcmd resp */ nanosec_rem = do_div(t, 1000000000)/1000; /*skip log if last cmd rsp are the same*/ if (last_cmd == cmd && last_arg == arg && cmd == 13) { skip++; if (dbg_host_cnt == 0) dbg_host_cnt = dbg_max_cnt; /*remove type = 0, command*/ dbg_host_cnt--; break; } last_cmd = cmd; last_arg = arg; l_skip = skip; skip = 0; dbg_run_host_log_dat[dbg_host_cnt].time_sec = t; dbg_run_host_log_dat[dbg_host_cnt].time_usec = nanosec_rem; dbg_run_host_log_dat[dbg_host_cnt].type = type; dbg_run_host_log_dat[dbg_host_cnt].cmd = cmd; dbg_run_host_log_dat[dbg_host_cnt].arg = arg; dbg_run_host_log_dat[dbg_host_cnt].skip = l_skip; dbg_host_cnt++; if (dbg_host_cnt >= dbg_max_cnt) dbg_host_cnt = 0; break; #ifdef MTK_MSDC_LOW_IO_DEBUG case 3: /* * try to reduce executing time in case 3 to keep performance * not to drop. */ if (dbg_dma_cmd_log_dat.cmd) { dbg_run_host_log_dat_low_io[dbg_host_cnt_low_io].cmd = dbg_dma_cmd_log_dat.cmd; dbg_dma_cmd_log_dat.cmd = 0; } else break; dbg_run_host_log_dat_low_io[dbg_host_cnt_low_io].time = t; dbg_run_host_log_dat_low_io[dbg_host_cnt_low_io].time_diff = t - dbg_dma_cmd_log_dat.time; tag = (dbg_dma_cmd_log_dat.arg >> 16) & 0x1f; dbg_run_host_log_dat_low_io[dbg_host_cnt_low_io].address = dbg_task_log_dat[tag].address; dbg_run_host_log_dat_low_io[dbg_host_cnt_low_io].size = dbg_task_log_dat[tag].size; /* if speed < criterion_low_io, record it */ if ((dbg_run_host_log_dat_low_io[dbg_host_cnt_low_io].size * 1000000000 >> 1) < (criterion_low_io * dbg_run_host_log_dat_low_io[dbg_host_cnt_low_io].time_diff)) { dbg_run_host_log_dat_low_io[dbg_host_cnt_low_io] .continuous_count = ++continuous_count_low_io; dbg_host_cnt_low_io++; if (dbg_host_cnt_low_io >= dbg_max_cnt_low_io) dbg_host_cnt_low_io = 0; } else continuous_count_low_io = 0; break; #endif /* add softirq record */ case MAGIC_CQHCI_DBG_TYPE_SIRQ: tn = t; nanosec_rem = do_div(t, 1000000000)/1000; dbg_run_host_log_dat[dbg_host_cnt].time_sec = t; dbg_run_host_log_dat[dbg_host_cnt].time_usec = nanosec_rem; dbg_run_host_log_dat[dbg_host_cnt].type = type; dbg_run_host_log_dat[dbg_host_cnt].cmd = cmd; dbg_run_host_log_dat[dbg_host_cnt].arg = arg; dbg_run_host_log_dat[dbg_host_cnt].skip = l_skip; dbg_run_host_log_dat[dbg_host_cnt].cpu = cpu; dbg_run_host_log_dat[dbg_host_cnt].active_reqs = reserved; dbg_host_cnt++; if (dbg_host_cnt >= dbg_max_cnt) dbg_host_cnt = 0; break; default: break; } #ifdef CONFIG_MTK_EMMC_HW_CQ spin_unlock_irqrestore(&host->cmd_dump_lock, flags); #endif } /* all cases which except softirq of IO */ void dbg_add_host_log(struct mmc_host *mmc, int type, int cmd, int arg) { __dbg_add_host_log(mmc, type, cmd, arg, -1, 0); } /* only used in softirq of IO (end io) */ void dbg_add_sirq_log(struct mmc_host *mmc, int type, int cmd, int arg, int cpu, unsigned long active_reqs) { __dbg_add_host_log(mmc, type, cmd, arg, cpu, active_reqs); } #ifdef MTK_MSDC_LOW_IO_DEBUG void mmc_low_io_dump(char **buff, unsigned long *size, struct seq_file *m, struct mmc_host *mmc) { int i, j; unsigned long long t, nanosec_rem, speed; char dir; if (!mmc || !mmc->card) return; SPREAD_PRINTF(buff, size, m, "\nLow IO (<%dKB/s):\n", criterion_low_io); SPREAD_PRINTF(buff, size, m, "index time direction address size speed continuous_count\n"); i = dbg_host_cnt_low_io - 1; if (i < 0) i = dbg_max_cnt_low_io - 1; for (j = 0; j < dbg_max_cnt_low_io; j++) { t = dbg_run_host_log_dat_low_io[i].time; nanosec_rem = do_div(t, 1000000000)/1000; speed = dbg_run_host_log_dat_low_io[i].size * 1000000000; if (dbg_run_host_log_dat_low_io[i].time_diff != 0) do_div(speed, dbg_run_host_log_dat_low_io[i].time_diff); else speed = 0; if (dbg_run_host_log_dat_low_io[i].cmd == 46) dir = 'R'; else if (dbg_run_host_log_dat_low_io[i].cmd == 47) dir = 'W'; else dir = 'N'; SPREAD_PRINTF(buff, size, m, "%05d[%5llu.%06llu]%c,0x%08x,%4lluKB,%6lluKB/s,%d\n", j, t, nanosec_rem, dir, dbg_run_host_log_dat_low_io[i].address, dbg_run_host_log_dat_low_io[i].size >> 1, speed >> 1, dbg_run_host_log_dat_low_io[i].continuous_count); if (--i < 0) i = dbg_max_cnt_low_io - 1; } } #else void mmc_low_io_dump(char **buff, unsigned long *size, struct seq_file *m, struct mmc_host *mmc) { } #endif void mmc_cmd_dump(char **buff, unsigned long *size, struct seq_file *m, struct mmc_host *mmc, u32 latest_cnt) { int i, j; int tag = -1; int is_read, is_rel, is_fprg; unsigned long long time_sec, time_usec; int type, cmd, arg, skip, cnt, cpu; unsigned long active_reqs; struct msdc_host *host; u32 dump_cnt; #ifdef CONFIG_MTK_EMMC_HW_CQ unsigned long curr_state; #endif if (!mmc || !mmc->card) return; /* only dump msdc0 */ host = mmc_priv(mmc); if (!host || host->id != 0) return; dump_cnt = min_t(u32, latest_cnt, dbg_max_cnt); i = dbg_host_cnt - 1; if (i < 0) i = dbg_max_cnt - 1; for (j = 0; j < dump_cnt; j++) { time_sec = dbg_run_host_log_dat[i].time_sec; time_usec = dbg_run_host_log_dat[i].time_usec; type = dbg_run_host_log_dat[i].type; cmd = dbg_run_host_log_dat[i].cmd; arg = dbg_run_host_log_dat[i].arg; skip = dbg_run_host_log_dat[i].skip; if (dbg_run_host_log_dat[i].type == 70) { cpu = dbg_run_host_log_dat[i].cpu; active_reqs = dbg_run_host_log_dat[i].active_reqs; } else { cpu = -1; active_reqs = 0; } if (cmd == 44 && !type) { cnt = arg & 0xffff; tag = (arg >> 16) & 0x1f; is_read = (arg >> 30) & 0x1; is_rel = (arg >> 31) & 0x1; is_fprg = (arg >> 24) & 0x1; SPREAD_PRINTF(buff, size, m, "%03d [%5llu.%06llu]%2d %3d %08x id=%02d %s cnt=%d %d %d\n", j, time_sec, time_usec, type, cmd, arg, tag, is_read ? "R" : "W", cnt, is_rel, is_fprg); } else if ((cmd == 46 || cmd == 47) && !type) { tag = (arg >> 16) & 0x1f; SPREAD_PRINTF(buff, size, m, "%03d [%5llu.%06llu]%2d %3d %08x id=%02d\n", j, time_sec, time_usec, type, cmd, arg, tag); } else SPREAD_PRINTF(buff, size, m, "%03d [%5llu.%06llu]%2d %3d %08x (%d) (0x%08lx) (%d)\n", j, time_sec, time_usec, type, cmd, arg, skip, active_reqs, cpu); i--; if (i < 0) i = dbg_max_cnt - 1; } #ifdef CONFIG_MTK_EMMC_CQ_SUPPORT SPREAD_PRINTF(buff, size, m, "areq_cnt:%d, task_id_index %08lx, cq_wait_rdy:%d, cq_rdy_cnt:%d\n", atomic_read(&mmc->areq_cnt), mmc->task_id_index, atomic_read(&mmc->cq_wait_rdy), atomic_read(&mmc->cq_rdy_cnt)); #endif #ifdef CONFIG_MTK_EMMC_HW_CQ curr_state = mmc->cmdq_ctx.curr_state; SPREAD_PRINTF(buff, size, m, "active_reqs : 0x%lx\n", mmc->cmdq_ctx.active_reqs); SPREAD_PRINTF(buff, size, m, "curr_state : 0x%lx\n", curr_state); SPREAD_PRINTF(buff, size, m, "%s %s %s %s %s\n", curr_state & (1 << CMDQ_STATE_ERR) ? "ERR":"", curr_state & (1 << CMDQ_STATE_DCMD_ACTIVE) ? "DCMD_ACTIVE":"", curr_state & (1 << CMDQ_STATE_HALT) ? "HALT":"", curr_state & (1 << CMDQ_STATE_CQ_DISABLE) ? "CQ_DISABLE":"", curr_state & (1 << CMDQ_STATE_REQ_TIMED_OUT) ? "REQ_TIMED_OUT":""); SPREAD_PRINTF(buff, size, m, "part_curr : %d\n", mmc->card->part_curr); #endif SPREAD_PRINTF(buff, size, m, "claimed(%d), claim_cnt(%d), claimer pid(%d), comm %s\n", mmc->claimed, mmc->claim_cnt, mmc->claimer && mmc->claimer->task ? mmc->claimer->task->pid : 0, mmc->claimer && mmc->claimer->task ? mmc->claimer->task->comm : "NULL"); } void msdc_dump_host_state(char **buff, unsigned long *size, struct seq_file *m, struct msdc_host *host) { void __iomem *base = host->base; SPREAD_PRINTF(buff, size, m, "Accumulated dma cnt: %u\n", host->dma_cnt); if (host->start_dma_time < host->stop_dma_time) SPREAD_PRINTF(buff, size, m, "No pending dma: last start %llu, last stop %llu\n", host->start_dma_time, host->stop_dma_time); if (host->tuning_in_progress) SPREAD_PRINTF(buff, size, m, "tuning_in_progress %d\n", host->tuning_in_progress); if (host->start_dma_time > host->stop_dma_time) { SPREAD_PRINTF(buff, size, m, "DMA pending DMA_CFG_SATUS(%d): start %llu, stop %llu\n", MSDC_READ32(MSDC_DMA_CFG) & MSDC_DMA_CFG_STS, host->start_dma_time, host->stop_dma_time); } /* add log description*/ SPREAD_PRINTF(buff, size, m, "column 1 : log number(Reverse order);\n"); SPREAD_PRINTF(buff, size, m, "column 2 : kernel time\n"); SPREAD_PRINTF(buff, size, m, "column 3 : type(0-cmd, 1-resp, 5-cqhci cmd, 60-cqhci dcmd doorbell,"); SPREAD_PRINTF(buff, size, m, "61-cqhci dcmd complete(irq in), 70-cqhci softirq in);\n"); SPREAD_PRINTF(buff, size, m, "column 4&5 : cmd index&arg(1XX-task XX's task descriptor low 32bit, "); SPREAD_PRINTF(buff, size, m, "2XX-task XX's task descriptor high 32bit, "); SPREAD_PRINTF(buff, size, m, "5XX-task XX's task completion(irq in), "); SPREAD_PRINTF(buff, size, m, "others index-command index(non 70 type) or cmd/data error(70 type)) "); SPREAD_PRINTF(buff, size, m, "others arg-command arg(non 70 type) or cmdq_req->tag(70 type));\n"); SPREAD_PRINTF(buff, size, m, "column 6 : repeat count(The role of problem analysis is low);\n"); SPREAD_PRINTF(buff, size, m, "column 7 : record data_active_reqs;\n"); SPREAD_PRINTF(buff, size, m, "column 8 : only record softirq's running CPU id(only for 70 type);\n"); } static void msdc_proc_dump(struct seq_file *m, u32 id) { struct msdc_host *host = mtk_msdc_host[id]; if (host == NULL) { pr_info("====== Null msdc%d, dump skipped ======\n", id); return; } msdc_dump_host_state(NULL, NULL, m, host); mmc_cmd_dump(NULL, NULL, m, host->mmc, dbg_max_cnt); mmc_low_io_dump(NULL, NULL, m, host->mmc); } void get_msdc_aee_buffer(unsigned long *vaddr, unsigned long *size) { struct msdc_host *host = mtk_msdc_host[0]; unsigned long free_size = MSDC_AEE_BUFFER_SIZE; char *buff; if (host == NULL) { pr_info("====== Null msdc, dump skipped ======\n"); return; } buff = msdc_aee_buffer; msdc_dump_host_state(&buff, &free_size, NULL, host); mmc_cmd_dump(&buff, &free_size, NULL, host->mmc, dbg_max_cnt); mmc_low_io_dump(&buff, &free_size, NULL, host->mmc); /* retrun start location */ *vaddr = (unsigned long)msdc_aee_buffer; *size = MSDC_AEE_BUFFER_SIZE - free_size; } EXPORT_SYMBOL(get_msdc_aee_buffer); #else inline void dbg_add_host_log(struct mmc_host *mmc, int type, int cmd, int arg) { //pr_info("config MTK_MMC_DEBUG is not set: %s!\n",__func__); } void mmc_cmd_dump(char **buff, unsigned long *size, struct seq_file *m, struct mmc_host *mmc, u32 latest_cnt) { //pr_info("config MTK_MMC_DEBUG is not set: %s!\n",__func__); } void msdc_dump_host_state(char **buff, unsigned long *size, struct seq_file *m, struct msdc_host *host) { //pr_info("config MTK_MMC_DEBUG is not set: %s!\n",__func__); } static void msdc_proc_dump(struct seq_file *m, u32 id) { //pr_info("config MTK_MMC_DEBUG is not set : %s!\n",__func__); } void get_msdc_aee_buffer(unsigned long *vaddr, unsigned long *size) { //pr_info("config MTK_MMC_DEBUG is not set : %s!\n",__func__); } #endif void msdc_cmdq_status_print(struct msdc_host *host, struct seq_file *m) { #if defined(CONFIG_MTK_EMMC_CQ_SUPPORT) || defined(CONFIG_MTK_EMMC_HW_CQ) struct mmc_host *mmc = host->mmc; #if defined(CONFIG_MTK_EMMC_HW_CQ) unsigned long curr_state; #endif if (!mmc || !mmc->card) return; seq_printf(m, "host->need_tune : %d\n", host->need_tune); seq_puts(m, "===============================\n"); seq_printf(m, "cmdq support : %s\n", mmc->card->ext_csd.cmdq_support ? "yes":"no"); seq_printf(m, "cmdq mode : %s\n", mmc->card->ext_csd.cmdq_en ? "enable" : "disable"); seq_printf(m, "cmdq depth : %d\n", mmc->card->ext_csd.cmdq_depth); seq_puts(m, "===============================\n"); #if defined(CONFIG_MTK_EMMC_CQ_SUPPORT) seq_printf(m, "areq_cnt : %d\n", atomic_read(&mmc->areq_cnt)); seq_printf(m, "task_id_index: %08lx\n", mmc->task_id_index); seq_printf(m, "cq_wait_rdy : %d\n", atomic_read(&mmc->cq_wait_rdy)); seq_printf(m, "cq_rdy_cnt : %d\n", atomic_read(&mmc->cq_rdy_cnt)); seq_printf(m, "cq_tuning_now: %d\n", atomic_read(&mmc->cq_tuning_now)); #endif seq_printf(m, "host claimed : %d\n", mmc->claimed); seq_printf(m, "host claim cnt : %d\n", mmc->claim_cnt); seq_printf(m, "host claimer pid : %d\n", mmc->claimer && mmc->claimer->task ? mmc->claimer->task->pid : 0); seq_printf(m, "host claimer comm : %s\n", mmc->claimer && mmc->claimer->task ? mmc->claimer->task->comm : "NULL"); #if defined(CONFIG_MTK_EMMC_HW_CQ) curr_state = mmc->cmdq_ctx.curr_state; seq_printf(m, "active_reqs : 0x%lx\n", mmc->cmdq_ctx.active_reqs); seq_printf(m, "curr_state : 0x%lx\n", curr_state); seq_printf(m, "%s %s %s %s %s\n", curr_state & (1 << CMDQ_STATE_ERR) ? "ERR":"", curr_state & (1 << CMDQ_STATE_DCMD_ACTIVE) ? "DCMD_ACTIVE":"", curr_state & (1 << CMDQ_STATE_HALT) ? "HALT":"", curr_state & (1 << CMDQ_STATE_CQ_DISABLE) ? "CQ_DISABLE":"", curr_state & (1 << CMDQ_STATE_REQ_TIMED_OUT) ? "REQ_TIMED_OUT":""); seq_printf(m, "part_curr : %d\n", mmc->card->part_curr); seq_puts(m, "hardware cq support\n"); #endif #else seq_puts(m, "driver not supported\n"); #endif #if defined(CONFIG_MTK_HW_FDE) && !defined(CONFIG_MTK_HW_FDE_AES) seq_puts(m, "hardware fde support\n"); #endif #if defined(CONFIG_MMC_CRYPTO) seq_puts(m, "hardware inline crypto\n"); #endif } void msdc_cmdq_func(struct msdc_host *host, const int num, struct seq_file *m) { void __iomem *base; if (!host || !host->mmc || !host->mmc->card) return; base = host->base; switch (num) { case 0: msdc_cmdq_status_print(host, m); break; case 2: mmc_cmd_dump(NULL, NULL, m, host->mmc, dbg_max_cnt); break; #ifdef CONFIG_MTK_EMMC_HW_CQ case 32: if (host->mmc->cmdq_ops && host->mmc->cmdq_ops->dumpstate) host->mmc->cmdq_ops->dumpstate(host->mmc, true); break; #endif default: seq_printf(m, "unknown function id %d\n", num); break; } } /* Clone from core/mmc.c since it is static in mmc.c */ static void msdc_select_card_type(struct mmc_host *host) { struct mmc_card *card = host->card; u8 card_type = card->ext_csd.raw_card_type; u32 caps = host->caps, caps2 = host->caps2; unsigned int hs_max_dtr = 0, hs200_max_dtr = 0; unsigned int avail_type = 0; if (caps & MMC_CAP_MMC_HIGHSPEED && card_type & EXT_CSD_CARD_TYPE_HS_26) { hs_max_dtr = MMC_HIGH_26_MAX_DTR; avail_type |= EXT_CSD_CARD_TYPE_HS_26; } if (caps & MMC_CAP_MMC_HIGHSPEED && card_type & EXT_CSD_CARD_TYPE_HS_52) { hs_max_dtr = MMC_HIGH_52_MAX_DTR; avail_type |= EXT_CSD_CARD_TYPE_HS_52; } if (caps & MMC_CAP_1_8V_DDR && card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) { hs_max_dtr = MMC_HIGH_DDR_MAX_DTR; avail_type |= EXT_CSD_CARD_TYPE_DDR_1_8V; } if (caps & MMC_CAP_1_2V_DDR && card_type & EXT_CSD_CARD_TYPE_DDR_1_2V) { hs_max_dtr = MMC_HIGH_DDR_MAX_DTR; avail_type |= EXT_CSD_CARD_TYPE_DDR_1_2V; } if (caps2 & MMC_CAP2_HS200_1_8V_SDR && card_type & EXT_CSD_CARD_TYPE_HS200_1_8V) { hs200_max_dtr = MMC_HS200_MAX_DTR; avail_type |= EXT_CSD_CARD_TYPE_HS200_1_8V; } if (caps2 & MMC_CAP2_HS200_1_2V_SDR && card_type & EXT_CSD_CARD_TYPE_HS200_1_2V) { hs200_max_dtr = MMC_HS200_MAX_DTR; avail_type |= EXT_CSD_CARD_TYPE_HS200_1_2V; } if (caps2 & MMC_CAP2_HS400_1_8V && card_type & EXT_CSD_CARD_TYPE_HS400_1_8V) { hs200_max_dtr = MMC_HS200_MAX_DTR; avail_type |= EXT_CSD_CARD_TYPE_HS400_1_8V; } if (caps2 & MMC_CAP2_HS400_1_2V && card_type & EXT_CSD_CARD_TYPE_HS400_1_2V) { hs200_max_dtr = MMC_HS200_MAX_DTR; avail_type |= EXT_CSD_CARD_TYPE_HS400_1_2V; } card->ext_csd.hs_max_dtr = hs_max_dtr; card->ext_csd.hs200_max_dtr = hs200_max_dtr; card->mmc_avail_type = avail_type; } void msdc_get_host_mode_speed(struct seq_file *m, struct mmc_host *mmc) { struct msdc_host *host = mmc_priv(mmc); seq_printf(m, "[SD_Debug]msdc[%d] supports:\n", host->id); if (mmc->caps & MMC_CAP_MMC_HIGHSPEED) seq_puts(m, "[SD_Debug] MMC_TIMING_MMC_HS\n"); if (mmc->caps & MMC_CAP_SD_HIGHSPEED) seq_puts(m, "[SD_Debug] MMC_TIMING_SD_HS\n"); if (mmc->caps & MMC_CAP_UHS_SDR12) seq_puts(m, "[SD_Debug] MMC_CAP_UHS_SDR12\n"); if (mmc->caps & MMC_CAP_UHS_SDR25) seq_puts(m, "[SD_Debug] MMC_CAP_UHS_SDR25\n"); if (mmc->caps & MMC_CAP_UHS_SDR104) seq_puts(m, "[SD_Debug] MMC_CAP_UHS_SDR104\n"); if (mmc->caps & MMC_CAP_UHS_DDR50) seq_puts(m, "[SD_Debug] MMC_CAP_UHS_DDR50\n"); if (mmc->caps & MMC_CAP_1_8V_DDR) seq_puts(m, "[SD_Debug] MMC_CAP_MMC_DDR52\n"); if (mmc->caps2 & MMC_CAP2_HS200_1_8V_SDR) seq_puts(m, "[SD_Debug] MMC_TIMING_MMC_HS200\n"); if (mmc->caps2 & MMC_CAP2_HS400_1_8V) seq_puts(m, "[SD_Debug] MMC_TIMING_MMC_HS400\n"); seq_puts(m, "[SD_Debug] Command queue feature is disable\n"); } void msdc_set_host_mode_speed(struct seq_file *m, struct mmc_host *mmc, int spd_mode) { struct msdc_host *host = mmc_priv(mmc); /* Clear HS400, HS200 timing */ mmc->caps2 &= ~(MMC_CAP2_HS400_1_8V | MMC_CAP2_HS200_1_8V_SDR); /* Clear other timing */ mmc->caps &= ~(MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR); switch (spd_mode) { case MMC_TIMING_LEGACY: break; case MMC_TIMING_MMC_HS: mmc->caps |= MMC_CAP_MMC_HIGHSPEED; break; case MMC_TIMING_SD_HS: mmc->caps |= MMC_CAP_SD_HIGHSPEED; break; case MMC_TIMING_UHS_SDR12: mmc->caps |= MMC_CAP_UHS_SDR12; break; case MMC_TIMING_UHS_SDR25: mmc->caps |= MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR12; break; case MMC_TIMING_UHS_SDR50: mmc->caps |= MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR12; break; case MMC_TIMING_UHS_SDR104: mmc->caps |= MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50 | MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR12; break; case MMC_TIMING_UHS_DDR50: mmc->caps |= MMC_CAP_UHS_DDR50 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR12; break; case MMC_TIMING_MMC_DDR52: mmc->caps |= MMC_CAP_1_8V_DDR | MMC_CAP_MMC_HIGHSPEED; break; case MMC_TIMING_MMC_HS200: mmc->caps2 |= MMC_CAP2_HS200_1_8V_SDR; mmc->caps |= MMC_CAP_1_8V_DDR | MMC_CAP_MMC_HIGHSPEED; break; case MMC_TIMING_MMC_HS400: mmc->caps2 |= MMC_CAP2_HS400_1_8V | MMC_CAP2_HS200_1_8V_SDR; mmc->caps |= MMC_CAP_1_8V_DDR | MMC_CAP_MMC_HIGHSPEED; break; default: seq_printf(m, "[SD_Debug]invalid speed mode:%d\n", spd_mode); break; } if (!mmc->card || mmc_card_sd(mmc->card)) return; msdc_select_card_type(mmc); /* For suppressing msdc_dump_info() caused by * cmd13 do in mmc_reset()@mmc.c */ g_emmc_mode_switch = 1; if (mmc->card->ext_csd.cache_ctrl) { mmc_flush_cache(mmc->card); msdc_cache_ctrl(host, 0, NULL); } /* Since only mmc_hw_reset() is public, * so use mmc_hw_reset() to reinit card */ mmc->caps |= MMC_CAP_HW_RESET; /* Must set mmc_host ios.time = MMC_TIMING_LEGACY, * or clock will not be setted to 400K before mmc_init_card * CMD1 will timeout */ mmc->ios.timing = MMC_TIMING_LEGACY; mmc->ios.clock = 260000; msdc_ops_set_ios(mmc, &mmc->ios); if (mmc_hw_reset(mmc)) seq_puts(m, "[SD_Debug] Reinit card failed, Can not switch speed mode\n"); g_emmc_mode_switch = 0; mmc->caps &= ~MMC_CAP_HW_RESET; } static void msdc_set_field(struct seq_file *m, void __iomem *address, unsigned int start_bit, unsigned int len, unsigned int value) { unsigned long field; if (start_bit > 31 || start_bit < 0 || len > 31 || len <= 0 || (start_bit + len > 32)) { seq_puts(m, "[SD_Debug]invalid reg field range or length\n"); } else { field = ((1 << len) - 1) << start_bit; value &= (1 << len) - 1; seq_printf(m, "[SD_Debug]Original:0x%p (0x%x)\n", address, MSDC_READ32(address)); MSDC_SET_FIELD(address, field, value); seq_printf(m, "[SD_Debug]Modified:0x%p (0x%x)\n", address, MSDC_READ32(address)); } } static void msdc_get_field(struct seq_file *m, void __iomem *address, unsigned int start_bit, unsigned int len, unsigned int value) { unsigned long field; if (start_bit > 31 || start_bit < 0 || len > 31 || len <= 0 || (start_bit + len > 32)) { seq_puts(m, "[SD_Debug]invalid reg field range or length\n"); } else { field = ((1 << len) - 1) << start_bit; MSDC_GET_FIELD(address, field, value); seq_printf(m, "[SD_Debug]Reg:0x%p start_bit(%d)len(%d)(0x%x)\n", address, start_bit, len, value); } } #ifdef MTK_MMC_SDIO_DEBUG void msdc_sdio_profile(struct sdio_profile *result) { struct cmd_profile *cmd; u32 i; pr_debug("sdio === performance dump ===\n"); pr_debug("sdio === total execute tick<%d> time<%dms> Tx<%dB> Rx<%dB>\n", result->total_tc, result->total_tc / TICKS_ONE_MS, result->total_tx_bytes, result->total_rx_bytes); /* CMD52 Dump */ cmd = &result->cmd52_rx; pr_debug( "sdio === CMD52 Rx <%d>times tick<%d> Max<%d> Min<%d> Aver<%d>\n", cmd->count, cmd->tot_tc, cmd->max_tc, cmd->min_tc, cmd->tot_tc / cmd->count); cmd = &result->cmd52_tx; pr_debug( "sdio === CMD52 Tx <%d>times tick<%d> Max<%d> Min<%d> Aver<%d>\n", cmd->count, cmd->tot_tc, cmd->max_tc, cmd->min_tc, cmd->tot_tc / cmd->count); /* CMD53 Rx bytes + block mode */ for (i = 0; i < 512; i++) { cmd = &result->cmd53_rx_byte[i]; if (cmd->count == 0) continue; pr_debug( "sdio<%6d><%3dB>_Rx_<%9d><%9d><%6d><%6d>_<%9dB><%2dM>\n", cmd->count, i, cmd->tot_tc, cmd->max_tc, cmd->min_tc, cmd->tot_tc / cmd->count, cmd->tot_bytes, (cmd->tot_bytes / 10) * 13 / (cmd->tot_tc / 10)); } for (i = 0; i < 100; i++) { cmd = &result->cmd53_rx_blk[i]; if (cmd->count == 0) continue; pr_debug( "sdio<%6d><%3d>B_Rx_<%9d><%9d><%6d><%6d>_<%9dB><%2dM>\n", cmd->count, i, cmd->tot_tc, cmd->max_tc, cmd->min_tc, cmd->tot_tc / cmd->count, cmd->tot_bytes, (cmd->tot_bytes / 10) * 13 / (cmd->tot_tc / 10)); } /* CMD53 Tx bytes + block mode */ for (i = 0; i < 512; i++) { cmd = &result->cmd53_tx_byte[i]; if (cmd->count == 0) continue; pr_debug( "sdio<%6d><%3dB>_Tx_<%9d><%9d><%6d><%6d>_<%9dB><%2dM>\n", cmd->count, i, cmd->tot_tc, cmd->max_tc, cmd->min_tc, cmd->tot_tc / cmd->count, cmd->tot_bytes, (cmd->tot_bytes / 10) * 13 / (cmd->tot_tc / 10)); } for (i = 0; i < 100; i++) { cmd = &result->cmd53_tx_blk[i]; if (cmd->count == 0) continue; pr_debug( "sdio<%6d><%3d>B_Tx_<%9d><%9d><%6d><%6d>_<%9dB><%2dM>\n", cmd->count, i, cmd->tot_tc, cmd->max_tc, cmd->min_tc, cmd->tot_tc / cmd->count, cmd->tot_bytes, (cmd->tot_bytes / 10) * 13 / (cmd->tot_tc / 10)); } pr_debug("sdio === performance dump done ===\n"); } /* ========= sdio command table =========== */ void msdc_performance(u32 opcode, u32 sizes, u32 bRx, u32 ticks) { struct sdio_profile *result = &sdio_performance; struct cmd_profile *cmd; u32 block; long long endtime; if (sdio_pro_enable == 0) return; if (opcode == 52) { cmd = bRx ? &result->cmd52_rx : &result->cmd52_tx; } else if (opcode == 53) { if (sizes < 512) { cmd = bRx ? &result->cmd53_rx_byte[sizes] : &result->cmd53_tx_byte[sizes]; } else { block = sizes / 512; if (block >= 99) { pr_notice("cmd53 error blocks\n"); while (1) ; } cmd = bRx ? &result->cmd53_rx_blk[block] : &result->cmd53_tx_blk[block]; } } else { return; } /* update the members */ if (ticks > cmd->max_tc) cmd->max_tc = ticks; if (cmd->min_tc == 0 || ticks < cmd->min_tc) cmd->min_tc = ticks; cmd->tot_tc += ticks; cmd->tot_bytes += sizes; cmd->count++; if (bRx) result->total_rx_bytes += sizes; else result->total_tx_bytes += sizes; result->total_tc += ticks; endtime = sched_clock(); if ((endtime - sdio_profiling_start) >= sdio_pro_time * 1000000000) { msdc_sdio_profile(result); memset(result, 0, sizeof(struct sdio_profile)); sdio_profiling_start = endtime; } } void sdio_get_time(struct mmc_request *mrq, struct timespec *time_now) { if (mrq->cmd->opcode == 52 || mrq->cmd->opcode == 53) get_monotonic_boottime(time_now); } void sdio_calc_time(struct mmc_request *mrq, struct timespec *time_start) { struct timespec time_end; u32 ticks = 0, sizes = 0, bRx = 0; if (mrq->cmd->opcode == 52 || mrq->cmd->opcode == 53) { get_monotonic_boottime(&time_end); if (time_end.tv_sec == time_start->tv_sec) { ticks = time_end.tv_nsec == time_start->tv_nsec; } else if (time_end.tv_sec > time_start->tv_sec) { ticks = (time_end.tv_sec - time_start->tv_sec) * 1000000000UL + time_end.tv_nsec - time_start->tv_nsec; } else { pr_notice("Shall not happen\n"); return; } if (mrq->cmd->data) { sizes = mrq->cmd->data->blocks * mrq->cmd->data->blksz; bRx = mrq->cmd->data->flags & MMC_DATA_READ ? 1 : 0; } else { bRx = mrq->cmd->arg & 0x80000000 ? 1 : 0; } if (!mrq->cmd->error) msdc_performance(mrq->cmd->opcode, sizes, bRx, ticks); } } #endif #define COMPARE_ADDRESS_MMC 0x402000 #define COMPARE_ADDRESS_SD 0x2000 #define COMPARE_ADDRESS_SD_COMBO 0x2000 #define MSDC_MULTI_BUF_LEN (4*4*1024) /*16KB write/read/compare*/ static DEFINE_MUTEX(sd_lock); static DEFINE_MUTEX(emmc_lock); u8 read_write_state; /* 0:stop, 1:read, 2:write */ static u8 wData_emmc[16] = { 0x67, 0x45, 0x23, 0x01, 0xef, 0xcd, 0xab, 0x89, 0xce, 0x8a, 0x46, 0x02, 0xde, 0x9b, 0x57, 0x13 }; static u8 wData_sd[200] = { 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, /*worst1*/ 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, /*worst2*/ 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, /*worst3*/ 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, /*worst4*/ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, /*worst5*/ 0x80, 0x80, 0x80, 0x7f, 0x80, 0x80, 0x80, 0x7f, 0x7f, 0x7f, 0x80, 0x7f, 0x7f, 0x7f, 0x40, 0x40, 0x04, 0xfb, 0x04, 0x04, 0x04, 0xfb, 0xfb, 0xfb, 0x04, 0xfb, 0xfb, 0xfb, 0x02, 0x02, 0x02, 0xfd, 0x02, 0x02, 0x02, 0xfd, 0xfd, 0xfd, 0x02, 0xfd, 0xfd, 0xfd, 0x01, 0x01, 0x01, 0xfe, 0x01, 0x01, 0x01, 0xfe, 0xfe, 0xfe, 0x01, 0xfe, 0xfe, 0xfe, 0x80, 0x80, 0x80, 0x7f, 0x80, 0x80, 0x80, 0x7f, 0x7f, 0x7f, 0x80, 0x7f, 0x7f, 0x7f, 0x40, 0x40, 0x40, 0x40, 0x80, 0x7f, 0x7f, 0x7f, 0x40, 0x40, 0x20, 0xdf, 0x20, 0x20, 0x20, 0xdf, 0xdf, 0xdf, 0x10, 0x10, 0x10, 0xef, 0xef, 0x10, 0xef, 0xef, }; /* * @read, bit0: 1:read/0:write; bit1: 0:compare/1:not compare */ static int multi_rw_compare_core(int host_num, int read, uint address, uint type, uint compare) { struct scatterlist msdc_sg; struct mmc_data msdc_data; struct mmc_command msdc_cmd; struct mmc_command msdc_stop; u32 *multi_rwbuf = NULL; u8 *wPtr = NULL, *rPtr = NULL; struct mmc_request msdc_mrq; struct msdc_host *host_ctl; struct mmc_host *mmc; int result = 0, forIndex = 0; #if defined(CONFIG_MTK_EMMC_CQ_SUPPORT) || defined(CONFIG_MTK_EMMC_HW_CQ) int cmdq_en; int ret; #endif u8 wData_len; u8 *wData; if (host_num >= HOST_MAX_NUM || host_num < 0) { pr_notice("[%s]invalid host id: %d\n", __func__, host_num); return -1; } host_ctl = mtk_msdc_host[host_num]; if (!host_ctl || !host_ctl->mmc || !host_ctl->mmc->card) { pr_notice(" No card initialized in host[%d]\n", host_num); result = -1; goto free; } if (type == MMC_TYPE_MMC) { wData = wData_emmc; wData_len = 16; } else if (type == MMC_TYPE_SD) { wData = wData_sd; wData_len = 200; } else return -1; /*allock memory for test buf*/ multi_rwbuf = kzalloc((MSDC_MULTI_BUF_LEN), GFP_KERNEL); if (multi_rwbuf == NULL) { result = -1; return result; } rPtr = wPtr = (u8 *)multi_rwbuf; if (!is_card_present(host_ctl)) { pr_info(" [%s]: card is removed!\n", __func__); result = -1; goto free; } mmc = host_ctl->mmc; mmc_get_card(mmc->card, NULL); #if defined(CONFIG_MTK_EMMC_CQ_SUPPORT) || defined(CONFIG_MTK_EMMC_HW_CQ) cmdq_en = !!mmc_card_cmdq(mmc->card); if (cmdq_en) { /* cmdq enabled, turn it off first */ pr_debug("[MSDC_DBG] cmdq enabled, turn it off\n"); ret = mmc_cmdq_disable(host_ctl->mmc->card); if (ret) { pr_notice("[MSDC_DBG] turn off cmdq en failed\n"); mmc_put_card(host_ctl->mmc->card, NULL); result = -1; goto free; } } #endif if (host_ctl->hw->host_function == MSDC_EMMC) msdc_switch_part(host_ctl, 0); memset(&msdc_data, 0, sizeof(struct mmc_data)); memset(&msdc_mrq, 0, sizeof(struct mmc_request)); memset(&msdc_cmd, 0, sizeof(struct mmc_command)); memset(&msdc_stop, 0, sizeof(struct mmc_command)); msdc_mrq.cmd = &msdc_cmd; msdc_mrq.data = &msdc_data; msdc_data.blocks = MSDC_MULTI_BUF_LEN / 512; if (read) { /* init read command */ msdc_data.flags = MMC_DATA_READ; msdc_cmd.opcode = MMC_READ_MULTIPLE_BLOCK; } else { /* init write command */ msdc_data.flags = MMC_DATA_WRITE; msdc_cmd.opcode = MMC_WRITE_MULTIPLE_BLOCK; /* init write buffer */ for (forIndex = 0; forIndex < MSDC_MULTI_BUF_LEN; forIndex++) *(wPtr + forIndex) = wData[forIndex % wData_len]; } #if defined(CONFIG_MTK_HW_FDE) && defined(CONFIG_MTK_HW_FDE_AES) if (fde_aes_check_cmd(FDE_AES_EN_RAW, fde_aes_get_raw(), host_num)) { fde_aes_set_msdc_id(host_num & 0xff); if (read) fde_aes_set_fde(0); else fde_aes_set_fde(1); } #endif msdc_cmd.arg = address; msdc_stop.opcode = MMC_STOP_TRANSMISSION; msdc_stop.arg = 0; msdc_stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; msdc_data.stop = &msdc_stop; if (!mmc_card_blockaddr(host_ctl->mmc->card)) { /* pr_info("this device use byte address!!\n"); */ msdc_cmd.arg <<= 9; } msdc_cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; msdc_data.blksz = 512; msdc_data.sg = &msdc_sg; msdc_data.sg_len = 1; sg_init_one(&msdc_sg, multi_rwbuf, MSDC_MULTI_BUF_LEN); mmc_set_data_timeout(&msdc_data, mmc->card); mmc_wait_for_req(mmc, &msdc_mrq); if (compare == 0 || !read) goto skip_check; /* compare */ for (forIndex = 0; forIndex < MSDC_MULTI_BUF_LEN; forIndex++) { if (rPtr[forIndex] != wData[forIndex % wData_len]) { pr_info("index[%d]\tW_buffer[0x%x]\tR_buffer[0x%x]\tfailed\n", forIndex, wData[forIndex % wData_len], rPtr[forIndex]); result = -1; } } skip_check: #if defined(CONFIG_MTK_EMMC_CQ_SUPPORT) || defined(CONFIG_MTK_EMMC_HW_CQ) if (cmdq_en) { pr_debug("[MSDC_DBG] turn on cmdq\n"); ret = mmc_cmdq_enable(host_ctl->mmc->card); if (ret) pr_notice("[MSDC_DBG] turn on cmdq en failed\n"); } #endif mmc_put_card(host_ctl->mmc->card, NULL); if (msdc_cmd.error) result = msdc_cmd.error; if (msdc_data.error) result = msdc_data.error; free: kfree(multi_rwbuf); return result; } int multi_rw_compare(struct seq_file *m, int host_num, uint address, int count, uint type, int multi_thread) { int i = 0; int error = 0; struct mutex *rw_mutex; if (type == MMC_TYPE_SD) rw_mutex = &sd_lock; else if (type == MMC_TYPE_MMC) rw_mutex = &emmc_lock; else return 0; for (i = 0; i < count; i++) { mutex_lock(rw_mutex); /* write */ error = multi_rw_compare_core(host_num, 0, address, type, 0); if (error) { if (!multi_thread) seq_printf(m, "[%s]: failed to write data, error=%d\n", __func__, error); else pr_info("[%s]: failed to write data, error=%d\n", __func__, error); goto skip_read; } /* read */ error = multi_rw_compare_core(host_num, 1, address, type, 1); if (error) { if (!multi_thread) seq_printf(m, "[%s]: failed to read data, error=%d\n", __func__, error); else pr_notice("[%s]: failed to read data, error=%d\n", __func__, error); } skip_read: if (!multi_thread) seq_printf(m, "== cpu[%d] pid[%d]: %s %d time compare ==\n", task_cpu(current), current->pid, (error ? "FAILED" : "FINISH"), i); else pr_info("== cpu[%d] pid[%d]: %s %d time compare ==\n", task_cpu(current), current->pid, (error ? "FAILED" : "FINISH"), i); mutex_unlock(rw_mutex); if (error) break; } if (i == count) { if (!multi_thread) seq_printf(m, "pid[%d]: success to compare data for %d times\n", current->pid, count); else pr_info("pid[%d]: success to compare data for %d times\n", current->pid, count); } return error; } #define MAX_THREAD_NUM_FOR_SMP 20 /* make the test can run on 4GB card */ static uint smp_address_on_sd[MAX_THREAD_NUM_FOR_SMP] = { 0x2000, 0x80000, 0x100000, 0x180000, 0x200000, /* 1GB */ 0x202000, 0x280000, 0x300000, 0x380000, 0x400000, /* 2GB */ 0x402000, 0x480000, 0x500000, 0x580000, 0x600000, 0x602000, /* 3GB */ 0x660000, /* real total size of 4GB sd card is < 4GB */ 0x680000, 0x6a0000, 0x6b0000, }; /* cause the system run on the emmc storage, * so do not to access the first 2GB region */ static uint smp_address_on_mmc[MAX_THREAD_NUM_FOR_SMP] = { 0x402000, 0x410000, 0x520000, 0x530000, 0x640000, 0x452000, 0x460000, 0x470000, 0x480000, 0x490000, 0x4a2000, 0x4b0000, 0x5c0000, 0x5d0000, 0x6e0000, 0x602000, 0x660000, /* real total size of 4GB sd card is < 4GB */ 0x680000, 0x6a0000, 0x6b0000, }; struct write_read_data { int host_id; /* target host you want to do SMP test on. */ uint start_address; /* Address of memcard you want to write/read */ int count; /* times you want to do read after write */ struct seq_file *m; }; static struct write_read_data wr_data[HOST_MAX_NUM][MAX_THREAD_NUM_FOR_SMP]; /* 2012-03-25 * the SMP thread function * do read after write the memory card, and bit by bit comparison */ struct task_struct *rw_thread; static int write_read_thread(void *ptr) { struct write_read_data *data = (struct write_read_data *)ptr; struct seq_file *m = data->m; if (data->host_id == 1) { pr_info("sd thread start\n"); multi_rw_compare(m, data->host_id, data->start_address, data->count, MMC_TYPE_SD, 1); pr_info("sd thread %d end\n", current->pid); } else if (data->host_id == 0) { pr_info("emmc thread %d start\n", current->pid); multi_rw_compare(m, data->host_id, data->start_address, data->count, MMC_TYPE_MMC, 1); pr_info("emmc thread %d end\n", current->pid); } return 0; } /* * 2012-03-25 * function: do SMP test on all MSDC hosts * thread_num: number of thread to be triggerred on this host. * count: times you want to do read after writein each thread. * multi_address: whether do read/write the same/different address of * the memory card in each thread. */ static int smp_test_on_hosts(struct seq_file *m, int thread_num, int host_id, int count, int multi_address) { int i = 0, j = 0; int id_start, id_end; int ret = 0; uint start_address, type; char thread_name[128]; struct msdc_host *host_ctl; seq_printf(m, "==================[%s] start ===================\n\n", __func__); seq_printf(m, " each host run %d thread, each thread run %d RW comparison\n", thread_num, count); if (thread_num > MAX_THREAD_NUM_FOR_SMP) { seq_printf(m, " too much thread for SMP test, thread_num=%d\n", thread_num); ret = -1; goto out; } if (host_id > HOST_MAX_NUM || host_id < 0) { seq_printf(m, " Invalid host id %d\n", host_id); ret = -1; goto out; } if (host_id == HOST_MAX_NUM) { id_start = 0; id_end = HOST_MAX_NUM; } else { id_start = host_id; id_end = host_id+1; } for (i = id_start; i < id_end; i++) { host_ctl = mtk_msdc_host[i]; if (!host_ctl || !host_ctl->mmc || !host_ctl->mmc->card) { seq_printf(m, " MSDC[%d], no card is initialized\n", i); continue; } type = host_ctl->mmc->card->type; if (type == MMC_TYPE_MMC) { if (!multi_address) start_address = COMPARE_ADDRESS_MMC; else start_address = smp_address_on_mmc[i]; } else if (type == MMC_TYPE_SD) { if (!multi_address) start_address = COMPARE_ADDRESS_MMC; else start_address = smp_address_on_sd[i]; } #ifdef MTK_MMC_SDIO_DEBUG else if (type == MMC_TYPE_SDIO) { seq_printf(m, " MSDC[%d], SDIO:\n", i); seq_puts(m, " please manually run wifi application instead of write/read SDIO card\n"); continue; } #endif else { seq_printf(m, " MSDC[%d], unkwonn card type\n ", i); continue; } for (j = 0; j < thread_num; j++) { wr_data[i][j].host_id = i; wr_data[i][j].count = count; wr_data[i][j].start_address = start_address; wr_data[i][j].m = m; sprintf(thread_name, "msdc_H%d_T%d", i, j); kthread_run(write_read_thread, &wr_data[i][j], thread_name); seq_printf(m, " start thread: %s, at address: 0x%x\n", thread_name, wr_data[i][j].start_address); } } out: seq_printf(m, "====================[%s] end ===================\n\n", __func__); return ret; } static int msdc_help_proc_show(struct seq_file *m, void *v) { seq_puts(m, "\n===============[msdc_help]================\n"); seq_printf(m, "\n LOG control: echo %x [host_id] [debug_zone] > msdc_debug\n", SD_TOOL_ZONE); seq_printf(m, " [debug_zone] DMA:0x%x, CMD:0x%x, RSP:0x%x, INT:0x%x, CFG:0x%x, FUC:0x%x,\n", DBG_EVT_DMA, DBG_EVT_CMD, DBG_EVT_RSP, DBG_EVT_INT, DBG_EVT_CFG, DBG_EVT_FUC); seq_printf(m, " OPS:0x%x, FIO:0x%x, WRN:0x%x, PWR:0x%x, CLK:0x%x, RW:0x%x, NRW:0x%x, CHE:0x%x\n", DBG_EVT_OPS, DBG_EVT_FIO, DBG_EVT_WRN, DBG_EVT_PWR, DBG_EVT_CLK, DBG_EVT_RW, DBG_EVT_NRW, DBG_EVT_CHE); seq_puts(m, "\n DMA mode:\n"); seq_printf(m, " *set DMA mode: echo %x 0 [host_id] [dma_mode] [dma_size] > msdc_debug\n", SD_TOOL_DMA_SIZE); seq_printf(m, " *get DMA mode: echo %x 1 [host_id] > msdc_debug\n", SD_TOOL_DMA_SIZE); seq_puts(m, " [dma_mode] 0:PIO, 1:DMA, 2:SIZE_DEP\n"); seq_puts(m, " [dma_size] valid for SIZE_DEP mode, the min size can trigger the DMA mode\n"); #ifdef MTK_MMC_SDIO_DEBUG seq_printf(m, "\n SDIO profile: echo %x [enable] [time] > msdc_debug\n", SD_TOOL_SDIO_PROFILE); #endif seq_puts(m, "\n REGISTER control:\n"); seq_printf(m, " write register: echo %x 0 [host_id] [reg_offset] [value] > msdc_debug\n", SD_TOOL_REG_ACCESS); seq_printf(m, " read register: echo %x 1 [host_id] [reg_offset] > msdc_debug\n", SD_TOOL_REG_ACCESS); seq_printf(m, " write mask: echo %x 2 [host_id] [reg_offset] [start_bit] [len] [value] > msdc_debug\n", SD_TOOL_REG_ACCESS); seq_printf(m, " read mask: echo %x 3 [host_id] [reg_offset] [start_bit] [len] > msdc_debug\n", SD_TOOL_REG_ACCESS); seq_printf(m, " dump all: echo %x 4 [host_id] > msdc_debug\n", SD_TOOL_REG_ACCESS); seq_puts(m, "\n DRVING control:\n"); seq_printf(m, " set driving: echo %x [host_id] [clk] [cmd] [dat] [rst] [ds] [voltage] > msdc_debug\n", SD_TOOL_SET_DRIVING); seq_puts(m, " [voltage] 1: 18v, 0: 33v\n"); seq_printf(m, " get driving: echo %x 0 [host_id] > msdc_debug\n", SD_TOOL_SET_DRIVING); seq_printf(m, "\n RW_COMPARE test: echo %x [host_id] [compare_count] > msdc_debug\n", RW_BIT_BY_BIT_COMPARE); seq_puts(m, " [compare_count] how many time you want to \"write=>read=>compare\"\n"); seq_printf(m, "\n SMP_ON_ONE_HOST test: echo %x [host_id] [thread_num] [compare_count] [multi_address] ", SMP_TEST_ON_ONE_HOST); seq_puts(m, "> msdc_debug\n"); seq_puts(m, " [thread_num] how many R/W comparision thread you want to run at host_id\n"); seq_puts(m, " [compare_count] how many time you want to \"write=>read=>compare\" in each thread\n"); seq_puts(m, " [multi_address] whether read/write different address in each thread, 0:No, 1:Yes\n"); seq_printf(m, "\n SMP_ON_ALL_HOST test: echo %x [thread_num][compare_count] [multi_address] ", SMP_TEST_ON_ALL_HOST); seq_puts(m, "> msdc_debug\n"); seq_puts(m, " [thread_num] how many R/W comparision thread you want to run at each host\n"); seq_puts(m, " [compare_count] how many time you want to \"write=>read=>compare\" in each thread\n"); seq_puts(m, " [multi_address] whether read/write different address in each thread, 0:No, 1:Yes\n"); seq_puts(m, "\n SPEED_MODE control:\n"); seq_printf(m, " set speed mode: echo %x 1 [host_id] [speed_mode] [cmdq]> msdc_debug\n", SD_TOOL_MSDC_HOST_MODE); seq_printf(m, " get speed mode: echo %x 0 [host_id]\n", SD_TOOL_MSDC_HOST_MODE); seq_puts(m, " [speed_mode] 0: MMC_TIMING_LEGACY 1: MMC_TIMING_MMC_HS\n"); seq_puts(m, " 2: MMC_TIMING_SD_HS 3: MMC_TIMING_UHS_SDR12\n"); seq_puts(m, " 4: MMC_TIMING_UHS_SDR25 5: MMC_TIMING_UHS_SDR50\n"); seq_puts(m, " 6: MMC_TIMING_UHS_SDR104 7: MMC_TIMING_UHS_DDR50\n"); seq_puts(m, " 8: MMC_TIMING_MMC_DDR52 9: MMC_TIMING_MMC_HS200\n"); seq_puts(m, " A: MMC_TIMING_MMC_HS400\n"); seq_printf(m, "\n DMA viloation: echo %x [host_id] [ops]> msdc_debug\n", SD_TOOL_DMA_STATUS); seq_puts(m, " [ops] 0:get latest dma address 1:start violation test\n"); seq_printf(m, "\n SET Slew Rate: echo %x [host_id] [clk] [cmd] [dat] [rst] [ds]> msdc_debug\n", SD_TOOL_ENABLE_SLEW_RATE); seq_puts(m, "\n TD/RD SEL:\n"); seq_printf(m, " set rdsel: echo %x [host_id] 0 [value] > msdc_debug\n", SD_TOOL_SET_RDTDSEL); seq_printf(m, " set tdsel: echo %x [host_id] 1 [value] > msdc_debug\n", SD_TOOL_SET_RDTDSEL); seq_printf(m, " get tdsel/rdsel: echo %x [host_id] 2 > msdc_debug\n", SD_TOOL_SET_RDTDSEL); seq_puts(m, " [value] rdsel: 0x0<<4 ~ 0x3f<<4 tdsel: 0x0~0xf\n"); seq_printf(m, "\n EMMC/SD RW test: echo %x [host_id] [mode] > msdc_debug\n", MSDC_READ_WRITE); seq_puts(m, " [mode] mode 0:stop, 1:read, 2:write\n"); seq_printf(m, "\n Error tune debug: echo %x [host_id] [cmd_id] [arg] [error_type] [count] > msdc_debug\n", MMC_ERROR_TUNE); seq_puts(m, " [cmd_id] 0: CMD0, 1: CMD1, 2: CMD2......\n"); seq_puts(m, " [arg] for CMD6, arg means ext_csd index......\n"); seq_puts(m, " [error] 0: disable error tune debug, 1: cmd timeout, 2: cmd crc, 4: dat timeout"); seq_puts(m, " 8: dat crc, 16: acmd timeout, 32: acmd crc\n"); seq_puts(m, " [count] error count\n"); seq_printf(m, "\n eMMC Cache Control: echo %x [host_id] [action_id] > /proc/msdc_debug\n", MMC_EDC_EMMC_CACHE); seq_puts(m, " [action_id] 0:Disable cache 1:Enable cache 2:check cache status\n"); seq_printf(m, "\n eMMC Dump GPD/BD: echo %x [host_id] > /proc/msdc_debug\n", MMC_DUMP_GPD); seq_puts(m, " [type] 0:tune cmd 1:tune read 2:tune write 3:tune HS400\n"); seq_puts(m, " [start_voltage] ?mV\n"); seq_puts(m, " [end_voltage] ?mV, we try ETT from higher voltage to lower voltage\n"); seq_printf(m, "\n CRC Stress Test: echo %x [action_id]> /proc/msdc_debug\n", MMC_CRC_STRESS); seq_puts(m, " [action_id] 0:disable 1:enable\n"); #ifdef MTK_MMC_SDIO_DEBUG seq_printf(m, "\n SDIO AutoK Result : echo %x [host_id][vcore] [rw]> /proc/msdc_debug\n", SDIO_AUTOK_RESULT); seq_puts(m, " [host_id] 2:sdio\n"); seq_puts(m, " [vcore] 0:low 1:high\n"); seq_puts(m, " [rw] 0:read 1:write\n"); #endif seq_puts(m, "\n NOTE: All input data is Hex number!\n"); seq_puts(m, "\n=============================================\n\n"); return 0; } /* * data: bit0~4:id, bit4~7: mode */ static int rwThread(void *data) { int error, i = 0; ulong p = (ulong) data; int id = p & 0x3; int mode = (p >> 4) & 0x3; int read; uint type = 0, address = 0; pr_info("[****SD_rwThread****]id=%d, mode=%d\n", id, mode); if (mode == 1) read = 1; else if (mode == 2) read = 0; else return -1; while (read_write_state != 0) { if (read_write_state == 1) p = 0x3; else if (read_write_state == 2) p = 0; if (id == 0) { type = MMC_TYPE_MMC; address = COMPARE_ADDRESS_MMC; } else if (id < HOST_MAX_NUM) { type = MMC_TYPE_SD; address = COMPARE_ADDRESS_SD; } error = multi_rw_compare_core(id, read, address, type, 0); if (error) { pr_notice("[%s]: failed data id0, error=%d\n", __func__, error); break; } i++; if (i == 10000) { pr_info("[***%s: %s***]", __func__, read_write_state == 1 ? "read" : "write"); i = 0; } } pr_info("[SD_Debug]%s exit\n", __func__); return 0; } void msdc_dump_gpd_bd(int id) { struct msdc_host *host; u32 i, j, k; u8 *cptr; struct gpd_t *gpd; struct bd_t *bd; if (id < 0 || id >= HOST_MAX_NUM) { pr_notice("[%s]: invalid host id: %d\n", __func__, id); return; } host = mtk_msdc_host[id]; if (host == NULL) { pr_notice("[%s]: host0 or host0->dma is NULL\n", __func__); return; } gpd = host->dma.gpd; bd = host->dma.bd; pr_notice("================MSDC GPD INFO ==================\n"); if (gpd == NULL) { pr_notice("GPD is NULL\n"); } else { #ifdef CONFIG_ARM64 pr_notice(" GPD ADDR HO BD RS CS INT RS NH4 PH4 NEXT PTR BUFLEN EXTL\n"); pr_notice("================ == == == == === == === === ======== ======== ====== ====\n"); pr_notice("%16llx %2x %2x %2x %2x %3x %2x %3x %3x %8x %8x %6x %4x\n", host->dma.gpd_addr, gpd->hwo, gpd->bdp, gpd->rsv0, gpd->chksum, gpd->intr, gpd->rsv1, (unsigned int)gpd->nexth4, (unsigned int)gpd->ptrh4, (unsigned int)gpd->next, (unsigned int)gpd->ptr, gpd->buflen, gpd->extlen); #else pr_notice(" ADDR HO BD RS CS INT RS NH4 PH4 NEXT PTR BUFLEN EXTL\n"); pr_notice("======== == == == == === == === === ======== ======== ====== ====\n"); pr_notice("%8x %2x %2x %2x %2x %3x %2x %3x %3x %8x %8x %6x %4x\n", (unsigned int)host->dma.gpd_addr, gpd->hwo, gpd->bdp, gpd->rsv0, gpd->chksum, gpd->intr, gpd->rsv1, (unsigned int)gpd->nexth4, (unsigned int)gpd->ptrh4, (unsigned int)gpd->next, (unsigned int)gpd->ptr, gpd->buflen, gpd->extlen); #endif } pr_notice("================MSDC BD INFO ===================\n"); if (bd == NULL) { pr_notice("BD is NULL\n"); } else { #ifdef CONFIG_ARM64 pr_notice("BD# BD ADDR EL RS CS RS PAD RS NH4 PH4 NEXT PTR BUFLEN RS\n"); pr_notice("==== ================ == == == == === == === === ======== ======== ====== ==\n"); #else pr_notice("BD# BD ADDR EL RS CS RS PAD RS NH4 PH4 NEXT PTR BUFLEN RS\n"); pr_notice("==== ======== == == == == === == === === ======== ======== ====== ==\n"); #endif for (i = 0; i < host->dma.sglen; i++) { cptr = (u8 *)bd; k = 0; for (j = 0; j < 16; j++) { if (j != 1) /* skip chksum field */ k += cptr[j]; } k &= 0xff; k ^= 0xff; if (k == bd->chksum) continue; #ifdef CONFIG_ARM64 pr_notice("%4d %16llx %2x %2x %2x %2x %x,%x %2x %3x %3x %8x %8x %6x %2x -> cs error: %2x\n", i, host->dma.bd_addr, bd->eol, bd->rsv0, bd->chksum, bd->rsv1, bd->blkpad, bd->dwpad, bd->rsv2, (unsigned int)bd->nexth4, (unsigned int)bd->ptrh4, (unsigned int)bd->next, (unsigned int)bd->ptr, bd->buflen, bd->rsv3, k); #else pr_notice("%4d %8x %2x %2x %2x %2x %x,%x %2x %3x %3x %8x %8x %6x %2x -> cs error: %2x\n", i, (unsigned int)host->dma.bd_addr, bd->eol, bd->rsv0, bd->chksum, bd->rsv1, bd->blkpad, bd->dwpad, bd->rsv2, (unsigned int)bd->nexth4, (unsigned int)bd->ptrh4, (unsigned int)bd->next, (unsigned int)bd->ptr, bd->buflen, bd->rsv3, k); #endif bd++; } } } /* FIX ME: Move to user space */ static int msdc_check_emmc_cache_status(struct seq_file *m, struct msdc_host *host) { struct mmc_card *card; if (!host || !host->mmc || !host->mmc->card) { seq_puts(m, "host or mmc or card is NULL\n"); return -1; } card = host->mmc->card; if (!mmc_card_mmc(card)) { seq_printf(m, "msdc%d: is not a eMMC card\n", host->id); return -2; } if (card->ext_csd.cache_size == 0) { seq_printf(m, "msdc%d:card don't support cache feature\n", host->id); return -1; } seq_printf(m, "msdc%d: Current eMMC Cache status: %s, Cache size:%dKB\n", host->id, host->mmc->card->ext_csd.cache_ctrl ? "Enable" : "Disable", host->mmc->card->ext_csd.cache_size/8); return card->ext_csd.cache_ctrl; } /* FIX ME: Move to user space */ static void msdc_enable_emmc_cache(struct seq_file *m, struct msdc_host *host, int enable) { int err; u8 c_ctrl; struct mmc_card *card; if (msdc_check_emmc_cache_status(m, host) < 0) return; card = host->mmc->card; (void)mmc_get_card(card, NULL); c_ctrl = card->ext_csd.cache_ctrl; if (c_ctrl == enable) seq_printf(m, "msdc%d:cache has already been %s state,\n", host->id, enable ? "enable" : "disable"); else { err = msdc_cache_ctrl(host, enable, NULL); if (err) seq_printf(m, "msdc%d: Cache is supported, but %s failed\n", host->id, enable ? "enable" : "disable"); else seq_printf(m, "msdc%d: %s cache successfully\n", host->id, enable ? "enable" : "disable"); } mmc_put_card(card, NULL); } #ifdef MTK_MSDC_ERROR_TUNE_DEBUG #define MTK_MSDC_ERROR_NONE (0) #define MTK_MSDC_ERROR_CMD_TMO (0x1) #define MTK_MSDC_ERROR_CMD_CRC (0x1 << 1) #define MTK_MSDC_ERROR_DAT_TMO (0x1 << 2) #define MTK_MSDC_ERROR_DAT_CRC (0x1 << 3) #define MTK_MSDC_ERROR_ACMD_TMO (0x1 << 4) #define MTK_MSDC_ERROR_ACMD_CRC (0x1 << 5) unsigned int g_err_tune_dbg_count; unsigned int g_err_tune_dbg_host; unsigned int g_err_tune_dbg_cmd; unsigned int g_err_tune_dbg_arg; unsigned int g_err_tune_dbg_error = MTK_MSDC_ERROR_NONE; static void msdc_error_tune_debug_print(struct seq_file *m, int p1, int p2, int p3, int p4, int p5) { g_err_tune_dbg_host = p1; g_err_tune_dbg_cmd = p2; g_err_tune_dbg_arg = p3; g_err_tune_dbg_error = p4; g_err_tune_dbg_count = p5; if (g_err_tune_dbg_count && (g_err_tune_dbg_error != MTK_MSDC_ERROR_NONE)) { seq_puts(m, "===================MSDC error debug start =======================\n"); seq_printf(m, "host:%d, cmd=%d, arg=%d, error=%d, count=%d\n", g_err_tune_dbg_host, g_err_tune_dbg_cmd, g_err_tune_dbg_arg, g_err_tune_dbg_error, g_err_tune_dbg_count); } else { g_err_tune_dbg_host = 0; g_err_tune_dbg_cmd = 0; g_err_tune_dbg_arg = 0; g_err_tune_dbg_error = MTK_MSDC_ERROR_NONE; g_err_tune_dbg_count = 0; seq_printf(m, "host:%d, cmd=%d, arg=%d, error=%d, count=%d\n", g_err_tune_dbg_host, g_err_tune_dbg_cmd, g_err_tune_dbg_arg, g_err_tune_dbg_error, g_err_tune_dbg_count); seq_puts(m, "=====================MSDC error debug end =======================\n"); } } void msdc_error_tune_debug1(struct msdc_host *host, struct mmc_command *cmd, struct mmc_command *sbc, u32 *intsts) { if (!g_err_tune_dbg_error || (g_err_tune_dbg_count <= 0) || (g_err_tune_dbg_host != host->id)) return; #ifdef MTK_MSDC_USE_CMD23 if (g_err_tune_dbg_cmd != cmd->opcode) goto check_sbc; #endif if ((g_err_tune_dbg_cmd != MMC_SWITCH) || ((g_err_tune_dbg_cmd == MMC_SWITCH) && (g_err_tune_dbg_arg == ((cmd->arg >> 16) & 0xff)))) { if (g_err_tune_dbg_error & MTK_MSDC_ERROR_CMD_TMO) { *intsts = MSDC_INT_CMDTMO; g_err_tune_dbg_count--; } else if (g_err_tune_dbg_error & MTK_MSDC_ERROR_CMD_CRC) { *intsts = MSDC_INT_RSPCRCERR; g_err_tune_dbg_count--; } pr_notice("[%s]: got the error cmd:%d, arg=%d, dbg error=%d, cmd->error=%d, count=%d\n", __func__, g_err_tune_dbg_cmd, g_err_tune_dbg_arg, g_err_tune_dbg_error, cmd->error, g_err_tune_dbg_count); } #ifdef MTK_MSDC_USE_CMD23 check_sbc: if ((g_err_tune_dbg_cmd == MMC_SET_BLOCK_COUNT) && sbc && (host->autocmd & MSDC_AUTOCMD23)) { if (g_err_tune_dbg_error & MTK_MSDC_ERROR_ACMD_TMO) *intsts = MSDC_INT_ACMDTMO; else if (g_err_tune_dbg_error & MTK_MSDC_ERROR_ACMD_CRC) *intsts = MSDC_INT_ACMDCRCERR; else return; pr_notice("[%s]: got the error cmd:%d, dbg error=%d, sbc->error=%d, count=%d\n", __func__, g_err_tune_dbg_cmd, g_err_tune_dbg_error, sbc->error, g_err_tune_dbg_count); } #endif } void msdc_error_tune_debug2(struct msdc_host *host, struct mmc_command *stop, u32 *intsts) { void __iomem *base = host->base; if (!g_err_tune_dbg_error || (g_err_tune_dbg_count <= 0) || (g_err_tune_dbg_host != host->id)) return; if (g_err_tune_dbg_cmd == (MSDC_READ32(SDC_CMD) & 0x3f)) { if (g_err_tune_dbg_error & MTK_MSDC_ERROR_DAT_TMO) { *intsts = MSDC_INT_DATTMO; g_err_tune_dbg_count--; } else if (g_err_tune_dbg_error & MTK_MSDC_ERROR_DAT_CRC) { *intsts = MSDC_INT_DATCRCERR; g_err_tune_dbg_count--; } pr_notice("[%s]: got the error cmd:%d, dbg error 0x%x, data->error=%d, count=%d\n", __func__, g_err_tune_dbg_cmd, g_err_tune_dbg_error, host->data->error, g_err_tune_dbg_count); } if ((g_err_tune_dbg_cmd == MMC_STOP_TRANSMISSION) && stop && (host->autocmd & MSDC_AUTOCMD12)) { if (g_err_tune_dbg_error & MTK_MSDC_ERROR_ACMD_TMO) { *intsts = MSDC_INT_ACMDTMO; g_err_tune_dbg_count--; } else if (g_err_tune_dbg_error & MTK_MSDC_ERROR_ACMD_CRC) { *intsts = MSDC_INT_ACMDCRCERR; g_err_tune_dbg_count--; } pr_notice("[%s]: got the error cmd:%d, dbg error 0x%x, stop->error=%d, host->error=%d, count=%d\n", __func__, g_err_tune_dbg_cmd, g_err_tune_dbg_error, stop->error, host->error, g_err_tune_dbg_count); } } #endif /*ifdef MTK_MSDC_ERROR_TUNE_DEBUG*/ #ifdef MTK_MMC_SDIO_DEBUG static u16 sdio_setting_offsets[] = { OFFSET_MSDC_CFG, OFFSET_SDC_STS, OFFSET_MSDC_IOCON, OFFSET_MSDC_PATCH_BIT0, OFFSET_MSDC_PATCH_BIT1, OFFSET_MSDC_PATCH_BIT2, OFFSET_MSDC_PAD_TUNE0, OFFSET_MSDC_PAD_TUNE1, OFFSET_MSDC_DAT_RDDLY0, OFFSET_MSDC_DAT_RDDLY1, OFFSET_MSDC_DAT_RDDLY2, OFFSET_MSDC_DAT_RDDLY3, OFFSET_MSDC_IOCON_1, OFFSET_MSDC_PATCH_BIT0_1, OFFSET_MSDC_PATCH_BIT1_1, OFFSET_MSDC_PATCH_BIT2_1, OFFSET_MSDC_PAD_TUNE0_1, OFFSET_MSDC_PAD_TUNE1_1, OFFSET_MSDC_DAT_RDDLY0_1, OFFSET_MSDC_DAT_RDDLY1_1, OFFSET_MSDC_DAT_RDDLY2_1, OFFSET_MSDC_DAT_RDDLY3_1, OFFSET_MSDC_IOCON_2, OFFSET_MSDC_PATCH_BIT0_2, OFFSET_MSDC_PATCH_BIT1_2, OFFSET_MSDC_PATCH_BIT2_2, OFFSET_MSDC_PAD_TUNE0_2, OFFSET_MSDC_PAD_TUNE1_2, OFFSET_MSDC_DAT_RDDLY0_2, OFFSET_MSDC_DAT_RDDLY1_2, OFFSET_MSDC_DAT_RDDLY2_2, OFFSET_MSDC_DAT_RDDLY3_2, 0xFFFF /*as mark of end */ }; static void msdc_dump_sdio_setting(struct msdc_host *host, struct seq_file *m) { void __iomem *base = host->base; int i; MSDC_GET_FIELD(SDC_STS, SDC_STS_DVFS_LEVEL, i); seq_printf(m, "SDIO : DVFS_LEVEL : %d\n", i); for (i = 0; sdio_setting_offsets[i] != 0xFFFF; i++) { seq_printf(m, "R[%x]=0x%.8x\n", sdio_setting_offsets[i], MSDC_READ32(base + sdio_setting_offsets[i])); } } #endif int g_count; #define MSDC_REGISTER_MAP_OFFSET 0x2000 #define MSDC_TOP_REGISTER_MAP_OFFSET 0x1000 static int msdc_check_register_offset(struct msdc_host *host, struct seq_file *m, unsigned int offset, unsigned int map_offset) { if ((map_offset == MSDC_REGISTER_MAP_OFFSET && offset > map_offset) || (map_offset == MSDC_TOP_REGISTER_MAP_OFFSET && offset > map_offset)) { seq_puts(m, "invalid register offset\n"); return 1; } if (offset % 4) { seq_puts(m, "register offset not align by 0x4\n"); return 1; } return 0; } /* ========== driver proc interface =========== */ static int msdc_debug_proc_show(struct seq_file *m, void *v) { int cmd = -1; int sscanf_num; int p1, p2, p3, p4, p5, p6, p7, p8; int id, zone; int mode; int thread_num, compare_count, multi_address; void __iomem *base = NULL; ulong data_for_wr; unsigned int offset = 0, msdc_map_offset = 0; unsigned int reg_value; int spd_mode = MMC_TIMING_LEGACY; struct msdc_host *host = NULL; #ifdef MSDC_DMA_ADDR_DEBUG struct dma_addr *dma_address, *p_dma_address; #endif int dma_status; #ifdef MTK_MMC_SDIO_DEBUG u8 *res; int vcore; #endif p1 = p2 = p3 = p4 = p5 = p6 = p7 = p8 = -1; cmd_buf[g_count] = '\0'; seq_printf(m, "Debug Command: %s\n", cmd_buf); sscanf_num = sscanf(cmd_buf, "%x %x %x %x %x %x %x %x %x", &cmd, &p1, &p2, &p3, &p4, &p5, &p6, &p7, &p8); /* clear buffer */ g_count = 0; cmd_buf[g_count] = '\0'; if (cmd == SD_TOOL_ZONE) { id = p1; if (id >= HOST_MAX_NUM || id < 0 || mtk_msdc_host[id] == NULL) goto invalid_host_id; host = mtk_msdc_host[id]; zone = p2; /* zone &= 0x3ff; */ seq_printf(m, "[SD_Debug] msdc host<%d> debug zone<0x%.8x>\n", id, zone); sd_debug_zone[id] = zone; if (host) { seq_printf(m, "[SD_Debug] msdc host<%d> caps<0x%.8x>\n", id, host->mmc->caps); seq_printf(m, "[SD_Debug] msdc host<%d> caps2<0x%.8x>\n", id, host->mmc->caps2); } } else if (cmd == SD_TOOL_DMA_SIZE) { id = p2; if (id >= HOST_MAX_NUM || id < 0 || mtk_msdc_host[id] == NULL) goto invalid_host_id; if (p1 == 0) { drv_mode[id] = p3; dma_size[id] = p4; } seq_printf(m, "[SD_Debug] msdc host[%d] mode<%d> size<%d>\n", id, drv_mode[id], dma_size[id]); } else if ((cmd == SD_TOOL_REG_ACCESS) || (cmd == SD_TOOL_TOP_REG_ACCESS)) { id = p2; offset = (unsigned int)p3; if (id >= HOST_MAX_NUM || id < 0 || mtk_msdc_host[id] == NULL) goto invalid_host_id; host = mtk_msdc_host[id]; mmc_claim_host(host->mmc); if (cmd == SD_TOOL_REG_ACCESS) { base = host->base; msdc_map_offset = MSDC_REGISTER_MAP_OFFSET; if ((offset == 0x18 || offset == 0x1C) && p1 != 4) { seq_puts(m, "[SD_Debug] Err: Accessing TXDATA and RXDATA is forbidden\n"); mmc_release_host(host->mmc); goto out; } } else { msdc_map_offset = MSDC_TOP_REGISTER_MAP_OFFSET; base = host->base_top; } if (p1 >= 0 && p1 <= 3) { if (msdc_check_register_offset(host, m, offset, msdc_map_offset)) { mmc_release_host(host->mmc); goto out; } } if (p1 == 0) { reg_value = p4; seq_printf(m, "[SD_Debug][MSDC Reg]Original:0x%p+0x%x (0x%x)\n", base, offset, MSDC_READ32(base + offset)); MSDC_WRITE32(base + offset, reg_value); seq_printf(m, "[SD_Debug][MSDC Reg]Modified:0x%p+0x%x (0x%x)\n", base, offset, MSDC_READ32(base + offset)); } else if (p1 == 1) { seq_printf(m, "[SD_Debug][MSDC Reg]Reg:0x%p+0x%x (0x%x)\n", base, offset, MSDC_READ32(base + offset)); } else if (p1 == 2) { msdc_set_field(m, base + offset, p4, p5, p6); } else if (p1 == 3) { msdc_get_field(m, base + offset, p4, p5, p6); } else if (p1 == 4) { msdc_dump_register_core(NULL, 0, m, host); } else if (p1 == 5) { msdc_dump_info(NULL, 0, NULL, host->id); } mmc_release_host(host->mmc); } else if (cmd == SD_TOOL_SET_DRIVING) { char *device_str, *get_set_str; id = p2; if (id >= HOST_MAX_NUM || id < 0 || mtk_msdc_host[id] == NULL) goto invalid_host_id; host = mtk_msdc_host[id]; if (id == 0) { device_str = "eMMC"; } else if (id == 1) { if (p8 == 1) device_str = "SD30"; else device_str = "SD20"; } #ifdef MTK_MMC_SDIO_DEBUG else device_str = "SDIO"; #endif if (p1 == 1) { get_set_str = "set"; if ((unsigned char)p3 > 7 || (unsigned char)p4 > 7 || (unsigned char)p5 > 7 || (unsigned char)p6 > 7 || (unsigned char)p7 > 7) { seq_puts(m, "[SD_Debug]Some drving value was invalid (valid:0~7)\n"); goto out; } host->hw->driving_applied->clk_drv = (unsigned char)p3; host->hw->driving_applied->cmd_drv = (unsigned char)p4; host->hw->driving_applied->dat_drv = (unsigned char)p5; host->hw->driving_applied->rst_drv = (unsigned char)p6; host->hw->driving_applied->ds_drv = (unsigned char)p7; msdc_set_driving(host, host->hw->driving_applied); } else { get_set_str = "get"; msdc_get_driving(host, host->hw->driving_applied); } seq_printf(m, "[SD_Debug] %s %s driving: clk_drv=%d, cmd_drv=%d, dat_drv=%d, rst_drv=%d, ds_drv=%d\n", device_str, get_set_str, host->hw->driving_applied->clk_drv, host->hw->driving_applied->cmd_drv, host->hw->driving_applied->dat_drv, host->hw->driving_applied->rst_drv, host->hw->driving_applied->ds_drv); } else if (cmd == SD_TOOL_ENABLE_SLEW_RATE) { id = p1; if (id >= HOST_MAX_NUM || id < 0 || mtk_msdc_host[id] == NULL) goto invalid_host_id; host = mtk_msdc_host[id]; if ((unsigned char)p2 > 1 || (unsigned char)p3 > 1 || (unsigned char)p4 > 1 || (unsigned char)p5 > 1 || (unsigned char)p6 > 1) { seq_puts(m, "[SD_Debug]Some sr value was invalid(correct:0(disable),1(enable))\n"); } else { msdc_set_sr(host, p2, p3, p4, p5, p6); seq_printf(m, "[SD_Debug]msdc%d, clk_sr=%d, cmd_sr=%d, dat_sr=%d, rst=%d, ds=%d\n", id, p2, p3, p4, p5, p6); } } else if (cmd == SD_TOOL_SET_RDTDSEL) { id = p1; if (id >= HOST_MAX_NUM || id < 0 || mtk_msdc_host[id] == NULL) goto invalid_host_id; host = mtk_msdc_host[id]; if ((p2 < 0) || (p2 > 2)) { seq_puts(m, "[SD_Debug]invalid option ( set rd:0, set td:1, get td/rd: 2)\n"); } else { /* p3 is checked in msdc_set_tdsel/msdc_set_rdsel */ if (p2 == 0) { msdc_set_rdsel(host, MSDC_TDRDSEL_CUST, p3); seq_printf(m, "[SD_Debug]msdc%d, set rd=%d\n", id, p3); } else if (p2 == 1) { /* set td:1 */ msdc_set_tdsel(host, MSDC_TDRDSEL_CUST, p3); seq_printf(m, "[SD_Debug]msdc%d, set td=%d\n", id, p3); } else if (p2 == 2) { /* get td/rd:2 */ msdc_get_rdsel(host, &p3); /* get rd */ msdc_get_tdsel(host, &p4); /* get td */ seq_printf(m, "[SD_Debug]msdc%d, rd : 0x%x, td : 0x%x\n", id, p3, p4); } } } else if (cmd == SD_TOOL_ENABLE_SMT) { id = p1; if (id >= HOST_MAX_NUM || id < 0 || mtk_msdc_host[id] == NULL) goto invalid_host_id; host = mtk_msdc_host[id]; msdc_set_smt(host, p2); seq_printf(m, "[SD_Debug]smt=%d\n", p2); } else if (cmd == RW_BIT_BY_BIT_COMPARE) { id = p1; compare_count = p2; if (id >= HOST_MAX_NUM || id < 0 || mtk_msdc_host[id] == NULL) goto invalid_host_id; if (compare_count < 0) { seq_printf(m, "[SD_Debug]: bad compare count: %d\n", compare_count); goto out; } if (id == 0) { /* for msdc0 */ multi_rw_compare(m, 0, COMPARE_ADDRESS_MMC, compare_count, MMC_TYPE_MMC, 0); /* test the address 0 of eMMC */ } else { multi_rw_compare(m, id, COMPARE_ADDRESS_SD, compare_count, MMC_TYPE_SD, 0); } } else if (cmd == MSDC_READ_WRITE) { id = p1; mode = p2; /* 0:stop, 1:read, 2:write */ if (id >= HOST_MAX_NUM || id < 0 || mtk_msdc_host[id] == NULL) goto invalid_host_id; if (mode > 2 || mode < 0) { seq_printf(m, "[SD_Debug]: bad mode: %d\n", mode); goto out; } if (mode == read_write_state) { seq_printf(m, "[SD_Debug]: same operation mode=%d.\n", read_write_state); goto out; } if (mode == 1 && read_write_state == 2) { seq_puts(m, "[SD_Debug]: cannot read in write state, please stop first\n"); goto out; } if (mode == 2 && read_write_state == 1) { seq_puts(m, "[SD_Debug]: cannot write in read state, please stop first\n"); goto out; } read_write_state = mode; seq_printf(m, "[SD_Debug]: host id: %d, mode: %d.\n", id, mode); if ((mode == 0) && (rw_thread)) { kthread_stop(rw_thread); rw_thread = NULL; seq_puts(m, "[SD_Debug]: stop read/write thread\n"); } else { seq_puts(m, "[SD_Debug]: start read/write thread\n"); data_for_wr = (id & 0x3) | ((mode & 0x3) << 4); rw_thread = kthread_create(rwThread, (void *)data_for_wr, "msdc_rw_thread"); wake_up_process(rw_thread); } } else if (cmd == SD_TOOL_MSDC_HOST_MODE) { id = p2; spd_mode = p3; if (id >= HOST_MAX_NUM || id < 0 || mtk_msdc_host[id] == NULL) goto invalid_host_id; host = mtk_msdc_host[id]; if (p1 == 1) { mmc_get_card(host->mmc->card, NULL); msdc_set_host_mode_speed(m, host->mmc, spd_mode); mmc_put_card(host->mmc->card, NULL); } msdc_get_host_mode_speed(m, host->mmc); } else if (cmd == SD_TOOL_DMA_STATUS) { id = p1; if (id >= HOST_MAX_NUM || id < 0 || mtk_msdc_host[id] == NULL) goto invalid_host_id; if (p2 == 0) { static char const * const str[] = { "No data transaction or the device is not present until now\n", "DMA mode is disabled Now\n", "Write from SD to DRAM in DMA mode\n", "Write from DRAM to SD in DMA mode\n" }; dma_status = msdc_get_dma_status(id); seq_printf(m, ">>>> msdc%d: dma_status=%d, ", id, dma_status); seq_printf(m, "%s", str[dma_status+1]); if (dma_status == -1) goto out; #ifdef MSDC_DMA_ADDR_DEBUG dma_address = msdc_get_dma_address(id); if (dma_address) { seq_printf(m, ">>>> msdc%d:\n", id); p_dma_address = dma_address; while (p_dma_address) { seq_printf(m, ">>>> addr=0x%x, size=%d\n", p_dma_address->start_address, p_dma_address->size); if (p_dma_address->end) break; p_dma_address = p_dma_address->next; } } else { seq_printf(m, ">>>> msdc%d: BD count=0\n", id); } #else seq_puts(m, "please enable MSDC_DMA_ADDR_DEBUG at mtk_sd.h if you want dump dma address\n"); #endif } else if (p2 == 1) { seq_printf(m, ">>>> msdc%d: start dma violation test\n", id); g_dma_debug[id] = 1; multi_rw_compare(m, id, COMPARE_ADDRESS_SD, 3, MMC_TYPE_SD, 0); } } else if (cmd == MMC_EDC_EMMC_CACHE) { seq_puts(m, "==== MSDC Cache Feature Test ====\n"); id = p1; if (id >= HOST_MAX_NUM || id < 0 || mtk_msdc_host[id] == NULL) goto invalid_host_id; host = mtk_msdc_host[id]; switch (p2) { case 0: msdc_enable_emmc_cache(m, host, 0); break; case 1: msdc_enable_emmc_cache(m, host, 1); break; case 2: default: msdc_check_emmc_cache_status(m, host); break; } } else if (cmd == MMC_DUMP_GPD) { seq_puts(m, "==== MSDC DUMP GPD/BD ====\n"); id = p1; if (id >= HOST_MAX_NUM || id < 0 || mtk_msdc_host[id] == NULL) goto invalid_host_id; else msdc_dump_gpd_bd(id); } #ifdef MTK_MMC_SDIO_DEBUG else if (cmd == SD_TOOL_SDIO_PROFILE) { if (p1 == 1) { /* enable profile */ sdio_pro_enable = 1; if (p2 == 0) p2 = 1; if (p2 >= 30) p2 = 30; sdio_pro_time = p2; } else if (p1 == 0) { /* todo */ sdio_pro_enable = 0; } } #endif else if (cmd == SMP_TEST_ON_ONE_HOST) { if (p2 > 0) { id = p1; thread_num = p2; compare_count = p3; multi_address = p4; smp_test_on_hosts(m, thread_num, id, compare_count, multi_address); } } else if (cmd == SMP_TEST_ON_ALL_HOST) { if (p1 > 0) { thread_num = p1; compare_count = p2; multi_address = p3; smp_test_on_hosts(m, thread_num, HOST_MAX_NUM, compare_count, multi_address); } #ifdef MTK_IO_PERFORMANCE_DEBUG } else if (cmd == MMC_PERF_DEBUG) { /* 1 enable; 0 disable */ g_mtk_mmc_perf_dbg = p1; g_mtk_mmc_dbg_range = p2; if (g_mtk_mmc_dbg_range == 2) { g_dbg_range_start = p3; g_dbg_range_end = p3 + p4; g_check_read_write = p5; } seq_printf(m, "g_mtk_mmc_perf_dbg = 0x%x, g_mtk_mmc_dbg_range = 0x%x, start = 0x%x, end = 0x%x\n", g_mtk_mmc_perf_dbg, g_mtk_mmc_dbg_range, g_dbg_range_start, g_dbg_range_end); } else if (cmd == MMC_PERF_DEBUG_PRINT) { int i, j, k, num = 0; if (p1 == 0) { g_mtk_mmc_clear = 0; goto out; } seq_printf(m, "msdc g_dbg_req_count<%d>\n", g_dbg_req_count); for (i = 1; i <= g_dbg_req_count; i++) { seq_printf(m, "analysis: %s 0x%x %d block, PGh %d\n", (g_check_read_write == 18 ? "read" : "write"), (unsigned int)g_mmcqd_buf[i][298], (unsigned int)g_mmcqd_buf[i][299], (unsigned int)(g_mmcqd_buf[i][297] * 2)); if (g_check_read_write == 18) { for (j = 1; j <= g_mmcqd_buf[i][296] * 2; j++) { seq_printf(m, "page %d:\n", num+1); for (k = 0; k < 5; k++) { seq_printf(m, "%d %llu\n", k, g_req_buf[num][k]); } num += 1; } } seq_puts(m, "----------------------------------\n"); for (j = 0; j < sizeof(g_time_mark)/sizeof(char *); j++) { seq_printf(m, "%d. %llu %s\n", j, g_mmcqd_buf[i][j], g_time_mark[j]); } seq_puts(m, "==================================\n"); } if (g_check_read_write == 25) { seq_printf(m, "msdc g_dbg_write_count<%d>\n", g_dbg_write_count); for (i = 1; i <= g_dbg_write_count; i++) { seq_puts(m, "**********************************\n"); seq_printf(m, "write count: %llu\n", g_req_write_count[i]); for (j = 0; j < sizeof(g_time_mark_vfs_write)/ sizeof(char *); j++) { seq_printf(m, "%d. %llu %s\n", j, g_req_write_buf[i][j], g_time_mark_vfs_write[j]); } } seq_puts(m, "**********************************\n"); } g_mtk_mmc_clear = 0; #endif #ifdef MTK_MMC_PERFORMANCE_TEST } else if (cmd == MMC_PERF_TEST) { /* 1 enable; 0 disable */ g_mtk_mmc_perf_test = p1; #endif #ifdef MTK_MSDC_ERROR_TUNE_DEBUG } else if (cmd == MMC_ERROR_TUNE) { msdc_error_tune_debug_print(m, p1, p2, p3, p4, p5); #endif } else if (cmd == MMC_CRC_STRESS) { /* FIX ME, move to user space */ seq_puts(m, "==== CRC Stress Test ====\n"); id = 0; host = mtk_msdc_host[id]; if (!host) goto invalid_host_id; base = mtk_msdc_host[0]->base; if (p1 == 0) { /* do nothing */ } else if (p1 == 1) { if (host->base_top) { void __iomem *base_top = host->base_top; MSDC_SET_FIELD(TOP_EMMC50_PAD_DS_TUNE, PAD_DS_DLY1, 0x1c); MSDC_SET_FIELD(TOP_EMMC50_PAD_DS_TUNE, PAD_DS_DLY3, 0xe); } else { MSDC_SET_FIELD(EMMC50_PAD_DS_TUNE, MSDC_EMMC50_PAD_DS_TUNE_DLY1, 0x1c); MSDC_SET_FIELD(EMMC50_PAD_DS_TUNE, MSDC_EMMC50_PAD_DS_TUNE_DLY3, 0xe); } } else if (p1 == 2) { MSDC_SET_FIELD(MSDC_IOCON, MSDC_IOCON_RSPL, 1); MSDC_SET_FIELD(MSDC_PAD_TUNE0, MSDC_PAD_TUNE0_CMDRDLY, p2); pr_notice("[****MMC_CRC_STRESS****] CMDRDLY<%d>\n", p2); } } #ifdef MTK_MMC_SDIO_DEBUG else if (cmd == SDIO_AUTOK_RESULT) { id = p1; vcore = p2; mode = p3; if (id >= HOST_MAX_NUM || id < 0 || mtk_msdc_host[id] == NULL) goto invalid_host_id; host = mtk_msdc_host[id]; /* pr_info("[****AutoK test****]msdc host_id<%d> * vcore<%d> mode<%d>\n", id, vcore, mode); */ if ((vcore < 0) || (vcore >= AUTOK_VCORE_NUM)) vcore = AUTOK_VCORE_LEVEL0; res = host->autok_res[vcore]; if (mode == 0) { if (sdio_autok_res_apply(host, vcore) != 0) pr_notice("sdio autok result not exist!\n"); } else if (mode == 1) { sdio_autok_res_save(host, vcore, res); } else if (mode == 2) { msdc_dump_sdio_setting(host, m); } else if (mode == 3) { msdc_dump_autok(NULL, 0, m, host); } } #endif else if (cmd == MMC_CMDQ_STATUS) { seq_puts(m, "==== eMMC CMDQ Feature ====\n"); id = p1; if (id >= HOST_MAX_NUM || id < 0 || mtk_msdc_host[id] == NULL) goto invalid_host_id; host = mtk_msdc_host[id]; msdc_cmdq_func(host, p2, m); } else { /* default dump info for aee */ seq_puts(m, "==== msdc debug info for aee ====\n"); #ifndef CONFIG_MTK_MMC_DEBUG seq_puts(m, "no debug info\n==== CONFIG_MTK_MMC_DEBUG_DISABLE=y ====\n"); #endif msdc_proc_dump(m, 0); msdc_proc_dump(m, 1); } out: return 0; invalid_host_id: seq_printf(m, "[SD_Debug]invalid host id: %d\n", id); return 1; } static ssize_t msdc_debug_proc_write(struct file *file, const char *buf, size_t count, loff_t *data) { int ret; if (count == 0) return -1; if (count > 255) count = 255; g_count = count; ret = copy_from_user(cmd_buf, buf, count); if (ret < 0) return -1; return count; } static int msdc_proc_open(struct inode *inode, struct file *file) { return single_open(file, msdc_debug_proc_show, inode->i_private); } static const struct file_operations msdc_proc_fops = { .open = msdc_proc_open, .write = msdc_debug_proc_write, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static int msdc_help_proc_open(struct inode *inode, struct file *file) { return single_open(file, msdc_help_proc_show, inode->i_private); } static const struct file_operations msdc_help_fops = { .open = msdc_help_proc_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static int msdc_sdcard_intr_gpio_value_show(struct seq_file *m, void *v) { seq_printf(m, "%d\n", gpio_get_value(cd_gpio) ? 0 : 1); return 0; } static int msdc_sdcard_intr_gpio_value_open(struct inode *inode, struct file *file) { return single_open(file, msdc_sdcard_intr_gpio_value_show, inode->i_private); } static const struct file_operations sdcard_intr_gpio_value_fops = { .open = msdc_sdcard_intr_gpio_value_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; #define MSDC_PROC_SHOW(name, fmt, args...) \ static int msdc_##name##_show(struct seq_file *m, void *v) \ { \ struct msdc_host *host; \ struct mmc_card *card; \ host = mtk_msdc_host[0]; \ card = host->mmc->card; \ seq_printf(m, fmt, args); \ return 0; \ } \ static int msdc_##name##_open(struct inode *inode, struct file *file) \ { \ return single_open(file, msdc_##name##_show, inode->i_private); \ } \ static const struct file_operations name##_fops = { \ .open = msdc_##name##_open, \ .read = seq_read, \ .llseek = seq_lseek, \ .release = single_release, \ } static int msdc_fw_version_show(struct seq_file *m, void *v) { struct msdc_host *host; struct mmc_card *card; host = mtk_msdc_host[0]; card = host->mmc->card; if (card->ext_csd.rev < 7) { seq_printf(m, "0x%x\n", card->cid.fwrev); } else { seq_printf(m, "0x%*phN\n", 8, card->ext_csd.fwrev); } return 0; } static int msdc_fw_version_open(struct inode *inode, struct file *file) { return single_open(file, msdc_fw_version_show, inode->i_private); } static const struct file_operations fw_version_fops = { .open = msdc_fw_version_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static int msdc_ext_csd_show(struct seq_file *m, void *v) { #define EXT_CSD_STR_LEN 1025 struct msdc_host *host; struct mmc_card *card; u8 *ext_csd = NULL; int err, i; #if defined(CONFIG_MTK_EMMC_CQ_SUPPORT) || defined(CONFIG_MTK_EMMC_HW_CQ) int cmdq_en; int ret; #endif host = mtk_msdc_host[0]; if (!host || !host->mmc || !host->mmc->card) return 0; card = host->mmc->card; mmc_get_card(card, NULL); #if defined(CONFIG_MTK_EMMC_CQ_SUPPORT) || defined(CONFIG_MTK_EMMC_HW_CQ) cmdq_en = !!mmc_card_cmdq(card); if (cmdq_en) { /* cmdq enabled, turn it off first */ pr_debug("[%s] cmdq enabled, turn it off\n", __func__); ret = mmc_cmdq_disable(card); if (ret) { pr_notice("[%s] turn off cmdq en failed\n", __func__); mmc_put_card(card, NULL); return 0; } } #endif err = mmc_get_ext_csd(card, &ext_csd); #if defined(CONFIG_MTK_EMMC_CQ_SUPPORT) || defined(CONFIG_MTK_EMMC_HW_CQ) if (cmdq_en) { pr_debug("[%s] turn on cmdq\n", __func__); ret = mmc_cmdq_enable(card); if (ret) pr_notice("[%s] turn on cmdq en failed\n", __func__); } #endif mmc_put_card(card, NULL); if (err) { pr_notice("[%s ]mmc_get_ext_csd failed!\n", __func__); kfree(ext_csd); return 0; } for (i = 0; i < 512; i++) seq_printf(m, "%02x", ext_csd[i]); seq_puts(m, "\n"); kfree(ext_csd); return 0; } static int mmc_ext_csd_open(struct inode *inode, struct file *file) { return single_open(file, msdc_ext_csd_show, inode->i_private); } static const struct file_operations ext_csd_fops = { .open = mmc_ext_csd_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static const char * const msdc_proc_list[] = { "cid", "life_time_est_typ_a", "life_time_est_typ_b", "pre_eol_info", "type", "name", "product_name", "size", "manfid", "fw_version", "ext_csd" }; /*These two TYPES are requested by HUIWEI normalized project*/ #define EMMC_TYPE 0 #define UFS_TYPE 1 #ifdef MTK_BOOT #define BOOTTYPE (get_boot_type()&0x11 \ ? (get_boot_type() == 2 ? UFS_TYPE : EMMC_TYPE):0xff) #else #define BOOTTYPE EMMC_TYPE #endif //static int boot_type = get_boot_type(); // 0 nand, 1 mmc, 2 ufs MSDC_PROC_SHOW(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1], card->raw_cid[2], card->raw_cid[3]); MSDC_PROC_SHOW(life_time_est_typ_a, "0x%02x\n", card->ext_csd.device_life_time_est_typ_a); MSDC_PROC_SHOW(life_time_est_typ_b, "0x%02x\n", card->ext_csd.device_life_time_est_typ_b); MSDC_PROC_SHOW(pre_eol_info, "0x%02x\n", card->ext_csd.pre_eol_info); MSDC_PROC_SHOW(type, "%d\n", BOOTTYPE); MSDC_PROC_SHOW(product_name, "%s\n", card->cid.prod_name); MSDC_PROC_SHOW(manfid, "0x%06x\n", card->cid.manfid); MSDC_PROC_SHOW(name, "%s\n", host->mmc->parent->driver->name); MSDC_PROC_SHOW(size, "%d\n", ((u32)card->ext_csd.raw_sectors[3]<<24) + ((u32)card->ext_csd.raw_sectors[2]<<16) + ((u32)card->ext_csd.raw_sectors[1]<<8)+((u32)card->ext_csd.raw_sectors[0])); #ifdef CONFIG_MTK_MMC_DEBUG static const struct file_operations *proc_fops_list[] = { &cid_fops, &life_time_est_typ_a_fops, &life_time_est_typ_b_fops, &pre_eol_info_fops, &type_fops, &name_fops, &product_name_fops, &size_fops, &manfid_fops, &fw_version_fops, &ext_csd_fops, }; #ifndef USER_BUILD_KERNEL #define PROC_PERM 0660 #else #define PROC_PERM 0440 #endif int msdc_debug_proc_init_bootdevice(void) { struct proc_dir_entry *prEntry; struct proc_dir_entry *bootdevice_dir; int i, num; bootdevice_dir = proc_mkdir("bootdevice", NULL); if (!bootdevice_dir) { pr_notice("[%s]: failed to create /proc/bootdevice\n", __func__); return -1; } num = ARRAY_SIZE(msdc_proc_list); for (i = 0; i < num; i++) { prEntry = proc_create(msdc_proc_list[i], 0440, bootdevice_dir, proc_fops_list[i]); if (prEntry) continue; pr_notice( "[%s]: failed to create /proc/bootdevice/%s\n", __func__, msdc_proc_list[i]); } return 0; } int msdc_debug_proc_init(void) { kuid_t uid; kgid_t gid; struct proc_dir_entry *prEntry; uid = make_kuid(&init_user_ns, 0); gid = make_kgid(&init_user_ns, 1001); prEntry = proc_create("msdc_debug", PROC_PERM, NULL, &msdc_proc_fops); if (prEntry) proc_set_user(prEntry, uid, gid); else pr_notice("[%s]: failed to create /proc/msdc_debug\n", __func__); prEntry = proc_create("msdc_help", PROC_PERM, NULL, &msdc_help_fops); if (!prEntry) pr_notice("[%s]: failed to create /proc/msdc_help\n", __func__); prEntry = proc_create("sdcard_intr_gpio_value", 0440, NULL, &sdcard_intr_gpio_value_fops); if (!prEntry) pr_notice("[%s]: failed to create /proc/sdcard_intr_gpio_value\n", __func__); #ifdef MSDC_DMA_ADDR_DEBUG msdc_init_dma_latest_address(); #endif return 0; } #else int msdc_debug_proc_init_bootdevice(void) { pr_notice("[%s]: CONFIG_MTK_MMC_DEBUG is not set\n", __func__); return 0; } int msdc_debug_proc_init(void) { pr_notice("[%s]: CONFIG_MTK_MMC_DEBUG is not set\n", __func__); return 0; } #endif EXPORT_SYMBOL_GPL(msdc_debug_proc_init);