18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Debugfs support for hosts and cards 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2008 Atmel Corporation 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 88c2ecf20Sopenharmony_ci#include <linux/export.h> 98c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 108c2ecf20Sopenharmony_ci#include <linux/fs.h> 118c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/stat.h> 148c2ecf20Sopenharmony_ci#include <linux/fault-inject.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/mmc/card.h> 178c2ecf20Sopenharmony_ci#include <linux/mmc/host.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "core.h" 208c2ecf20Sopenharmony_ci#include "card.h" 218c2ecf20Sopenharmony_ci#include "host.h" 228c2ecf20Sopenharmony_ci#include "mmc_ops.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#ifdef CONFIG_FAIL_MMC_REQUEST 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic DECLARE_FAULT_ATTR(fail_default_attr); 278c2ecf20Sopenharmony_cistatic char *fail_request; 288c2ecf20Sopenharmony_cimodule_param(fail_request, charp, 0); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#endif /* CONFIG_FAIL_MMC_REQUEST */ 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* The debugfs functions are optimized away when CONFIG_DEBUG_FS isn't set. */ 338c2ecf20Sopenharmony_cistatic int mmc_ios_show(struct seq_file *s, void *data) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci static const char *vdd_str[] = { 368c2ecf20Sopenharmony_ci [8] = "2.0", 378c2ecf20Sopenharmony_ci [9] = "2.1", 388c2ecf20Sopenharmony_ci [10] = "2.2", 398c2ecf20Sopenharmony_ci [11] = "2.3", 408c2ecf20Sopenharmony_ci [12] = "2.4", 418c2ecf20Sopenharmony_ci [13] = "2.5", 428c2ecf20Sopenharmony_ci [14] = "2.6", 438c2ecf20Sopenharmony_ci [15] = "2.7", 448c2ecf20Sopenharmony_ci [16] = "2.8", 458c2ecf20Sopenharmony_ci [17] = "2.9", 468c2ecf20Sopenharmony_ci [18] = "3.0", 478c2ecf20Sopenharmony_ci [19] = "3.1", 488c2ecf20Sopenharmony_ci [20] = "3.2", 498c2ecf20Sopenharmony_ci [21] = "3.3", 508c2ecf20Sopenharmony_ci [22] = "3.4", 518c2ecf20Sopenharmony_ci [23] = "3.5", 528c2ecf20Sopenharmony_ci [24] = "3.6", 538c2ecf20Sopenharmony_ci }; 548c2ecf20Sopenharmony_ci struct mmc_host *host = s->private; 558c2ecf20Sopenharmony_ci struct mmc_ios *ios = &host->ios; 568c2ecf20Sopenharmony_ci const char *str; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci seq_printf(s, "clock:\t\t%u Hz\n", ios->clock); 598c2ecf20Sopenharmony_ci if (host->actual_clock) 608c2ecf20Sopenharmony_ci seq_printf(s, "actual clock:\t%u Hz\n", host->actual_clock); 618c2ecf20Sopenharmony_ci seq_printf(s, "vdd:\t\t%u ", ios->vdd); 628c2ecf20Sopenharmony_ci if ((1 << ios->vdd) & MMC_VDD_165_195) 638c2ecf20Sopenharmony_ci seq_printf(s, "(1.65 - 1.95 V)\n"); 648c2ecf20Sopenharmony_ci else if (ios->vdd < (ARRAY_SIZE(vdd_str) - 1) 658c2ecf20Sopenharmony_ci && vdd_str[ios->vdd] && vdd_str[ios->vdd + 1]) 668c2ecf20Sopenharmony_ci seq_printf(s, "(%s ~ %s V)\n", vdd_str[ios->vdd], 678c2ecf20Sopenharmony_ci vdd_str[ios->vdd + 1]); 688c2ecf20Sopenharmony_ci else 698c2ecf20Sopenharmony_ci seq_printf(s, "(invalid)\n"); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci switch (ios->bus_mode) { 728c2ecf20Sopenharmony_ci case MMC_BUSMODE_OPENDRAIN: 738c2ecf20Sopenharmony_ci str = "open drain"; 748c2ecf20Sopenharmony_ci break; 758c2ecf20Sopenharmony_ci case MMC_BUSMODE_PUSHPULL: 768c2ecf20Sopenharmony_ci str = "push-pull"; 778c2ecf20Sopenharmony_ci break; 788c2ecf20Sopenharmony_ci default: 798c2ecf20Sopenharmony_ci str = "invalid"; 808c2ecf20Sopenharmony_ci break; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci seq_printf(s, "bus mode:\t%u (%s)\n", ios->bus_mode, str); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci switch (ios->chip_select) { 858c2ecf20Sopenharmony_ci case MMC_CS_DONTCARE: 868c2ecf20Sopenharmony_ci str = "don't care"; 878c2ecf20Sopenharmony_ci break; 888c2ecf20Sopenharmony_ci case MMC_CS_HIGH: 898c2ecf20Sopenharmony_ci str = "active high"; 908c2ecf20Sopenharmony_ci break; 918c2ecf20Sopenharmony_ci case MMC_CS_LOW: 928c2ecf20Sopenharmony_ci str = "active low"; 938c2ecf20Sopenharmony_ci break; 948c2ecf20Sopenharmony_ci default: 958c2ecf20Sopenharmony_ci str = "invalid"; 968c2ecf20Sopenharmony_ci break; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci seq_printf(s, "chip select:\t%u (%s)\n", ios->chip_select, str); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci switch (ios->power_mode) { 1018c2ecf20Sopenharmony_ci case MMC_POWER_OFF: 1028c2ecf20Sopenharmony_ci str = "off"; 1038c2ecf20Sopenharmony_ci break; 1048c2ecf20Sopenharmony_ci case MMC_POWER_UP: 1058c2ecf20Sopenharmony_ci str = "up"; 1068c2ecf20Sopenharmony_ci break; 1078c2ecf20Sopenharmony_ci case MMC_POWER_ON: 1088c2ecf20Sopenharmony_ci str = "on"; 1098c2ecf20Sopenharmony_ci break; 1108c2ecf20Sopenharmony_ci default: 1118c2ecf20Sopenharmony_ci str = "invalid"; 1128c2ecf20Sopenharmony_ci break; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci seq_printf(s, "power mode:\t%u (%s)\n", ios->power_mode, str); 1158c2ecf20Sopenharmony_ci seq_printf(s, "bus width:\t%u (%u bits)\n", 1168c2ecf20Sopenharmony_ci ios->bus_width, 1 << ios->bus_width); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci switch (ios->timing) { 1198c2ecf20Sopenharmony_ci case MMC_TIMING_LEGACY: 1208c2ecf20Sopenharmony_ci str = "legacy"; 1218c2ecf20Sopenharmony_ci break; 1228c2ecf20Sopenharmony_ci case MMC_TIMING_MMC_HS: 1238c2ecf20Sopenharmony_ci str = "mmc high-speed"; 1248c2ecf20Sopenharmony_ci break; 1258c2ecf20Sopenharmony_ci case MMC_TIMING_SD_HS: 1268c2ecf20Sopenharmony_ci str = "sd high-speed"; 1278c2ecf20Sopenharmony_ci break; 1288c2ecf20Sopenharmony_ci case MMC_TIMING_UHS_SDR12: 1298c2ecf20Sopenharmony_ci str = "sd uhs SDR12"; 1308c2ecf20Sopenharmony_ci break; 1318c2ecf20Sopenharmony_ci case MMC_TIMING_UHS_SDR25: 1328c2ecf20Sopenharmony_ci str = "sd uhs SDR25"; 1338c2ecf20Sopenharmony_ci break; 1348c2ecf20Sopenharmony_ci case MMC_TIMING_UHS_SDR50: 1358c2ecf20Sopenharmony_ci str = "sd uhs SDR50"; 1368c2ecf20Sopenharmony_ci break; 1378c2ecf20Sopenharmony_ci case MMC_TIMING_UHS_SDR104: 1388c2ecf20Sopenharmony_ci str = "sd uhs SDR104"; 1398c2ecf20Sopenharmony_ci break; 1408c2ecf20Sopenharmony_ci case MMC_TIMING_UHS_DDR50: 1418c2ecf20Sopenharmony_ci str = "sd uhs DDR50"; 1428c2ecf20Sopenharmony_ci break; 1438c2ecf20Sopenharmony_ci case MMC_TIMING_MMC_DDR52: 1448c2ecf20Sopenharmony_ci str = "mmc DDR52"; 1458c2ecf20Sopenharmony_ci break; 1468c2ecf20Sopenharmony_ci case MMC_TIMING_MMC_HS200: 1478c2ecf20Sopenharmony_ci str = "mmc HS200"; 1488c2ecf20Sopenharmony_ci break; 1498c2ecf20Sopenharmony_ci case MMC_TIMING_MMC_HS400: 1508c2ecf20Sopenharmony_ci str = mmc_card_hs400es(host->card) ? 1518c2ecf20Sopenharmony_ci "mmc HS400 enhanced strobe" : "mmc HS400"; 1528c2ecf20Sopenharmony_ci break; 1538c2ecf20Sopenharmony_ci default: 1548c2ecf20Sopenharmony_ci str = "invalid"; 1558c2ecf20Sopenharmony_ci break; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci seq_printf(s, "timing spec:\t%u (%s)\n", ios->timing, str); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci switch (ios->signal_voltage) { 1608c2ecf20Sopenharmony_ci case MMC_SIGNAL_VOLTAGE_330: 1618c2ecf20Sopenharmony_ci str = "3.30 V"; 1628c2ecf20Sopenharmony_ci break; 1638c2ecf20Sopenharmony_ci case MMC_SIGNAL_VOLTAGE_180: 1648c2ecf20Sopenharmony_ci str = "1.80 V"; 1658c2ecf20Sopenharmony_ci break; 1668c2ecf20Sopenharmony_ci case MMC_SIGNAL_VOLTAGE_120: 1678c2ecf20Sopenharmony_ci str = "1.20 V"; 1688c2ecf20Sopenharmony_ci break; 1698c2ecf20Sopenharmony_ci default: 1708c2ecf20Sopenharmony_ci str = "invalid"; 1718c2ecf20Sopenharmony_ci break; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci seq_printf(s, "signal voltage:\t%u (%s)\n", ios->signal_voltage, str); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci switch (ios->drv_type) { 1768c2ecf20Sopenharmony_ci case MMC_SET_DRIVER_TYPE_A: 1778c2ecf20Sopenharmony_ci str = "driver type A"; 1788c2ecf20Sopenharmony_ci break; 1798c2ecf20Sopenharmony_ci case MMC_SET_DRIVER_TYPE_B: 1808c2ecf20Sopenharmony_ci str = "driver type B"; 1818c2ecf20Sopenharmony_ci break; 1828c2ecf20Sopenharmony_ci case MMC_SET_DRIVER_TYPE_C: 1838c2ecf20Sopenharmony_ci str = "driver type C"; 1848c2ecf20Sopenharmony_ci break; 1858c2ecf20Sopenharmony_ci case MMC_SET_DRIVER_TYPE_D: 1868c2ecf20Sopenharmony_ci str = "driver type D"; 1878c2ecf20Sopenharmony_ci break; 1888c2ecf20Sopenharmony_ci default: 1898c2ecf20Sopenharmony_ci str = "invalid"; 1908c2ecf20Sopenharmony_ci break; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci seq_printf(s, "driver type:\t%u (%s)\n", ios->drv_type, str); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci return 0; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(mmc_ios); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic int mmc_clock_opt_get(void *data, u64 *val) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct mmc_host *host = data; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci *val = host->ios.clock; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic int mmc_clock_opt_set(void *data, u64 val) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct mmc_host *host = data; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci /* We need this check due to input value is u64 */ 2128c2ecf20Sopenharmony_ci if (val != 0 && (val > host->f_max || val < host->f_min)) 2138c2ecf20Sopenharmony_ci return -EINVAL; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci mmc_claim_host(host); 2168c2ecf20Sopenharmony_ci mmc_set_clock(host, (unsigned int) val); 2178c2ecf20Sopenharmony_ci mmc_release_host(host); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci return 0; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(mmc_clock_fops, mmc_clock_opt_get, mmc_clock_opt_set, 2238c2ecf20Sopenharmony_ci "%llu\n"); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_civoid mmc_add_host_debugfs(struct mmc_host *host) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci struct dentry *root; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci root = debugfs_create_dir(mmc_hostname(host), NULL); 2308c2ecf20Sopenharmony_ci host->debugfs_root = root; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci debugfs_create_file("ios", S_IRUSR, root, host, &mmc_ios_fops); 2338c2ecf20Sopenharmony_ci debugfs_create_x32("caps", S_IRUSR, root, &host->caps); 2348c2ecf20Sopenharmony_ci debugfs_create_x32("caps2", S_IRUSR, root, &host->caps2); 2358c2ecf20Sopenharmony_ci debugfs_create_file_unsafe("clock", S_IRUSR | S_IWUSR, root, host, 2368c2ecf20Sopenharmony_ci &mmc_clock_fops); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci#ifdef CONFIG_FAIL_MMC_REQUEST 2398c2ecf20Sopenharmony_ci if (fail_request) 2408c2ecf20Sopenharmony_ci setup_fault_attr(&fail_default_attr, fail_request); 2418c2ecf20Sopenharmony_ci host->fail_mmc_request = fail_default_attr; 2428c2ecf20Sopenharmony_ci fault_create_debugfs_attr("fail_mmc_request", root, 2438c2ecf20Sopenharmony_ci &host->fail_mmc_request); 2448c2ecf20Sopenharmony_ci#endif 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_civoid mmc_remove_host_debugfs(struct mmc_host *host) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci debugfs_remove_recursive(host->debugfs_root); 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_civoid mmc_add_card_debugfs(struct mmc_card *card) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci struct mmc_host *host = card->host; 2558c2ecf20Sopenharmony_ci struct dentry *root; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (!host->debugfs_root) 2588c2ecf20Sopenharmony_ci return; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci root = debugfs_create_dir(mmc_card_id(card), host->debugfs_root); 2618c2ecf20Sopenharmony_ci card->debugfs_root = root; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci debugfs_create_x32("state", S_IRUSR, root, &card->state); 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_civoid mmc_remove_card_debugfs(struct mmc_card *card) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci debugfs_remove_recursive(card->debugfs_root); 2698c2ecf20Sopenharmony_ci card->debugfs_root = NULL; 2708c2ecf20Sopenharmony_ci} 271