162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Debugfs support for hosts and cards 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2008 Atmel Corporation 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/moduleparam.h> 862306a36Sopenharmony_ci#include <linux/export.h> 962306a36Sopenharmony_ci#include <linux/debugfs.h> 1062306a36Sopenharmony_ci#include <linux/fs.h> 1162306a36Sopenharmony_ci#include <linux/seq_file.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/stat.h> 1462306a36Sopenharmony_ci#include <linux/fault-inject.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/mmc/card.h> 1762306a36Sopenharmony_ci#include <linux/mmc/host.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "core.h" 2062306a36Sopenharmony_ci#include "card.h" 2162306a36Sopenharmony_ci#include "host.h" 2262306a36Sopenharmony_ci#include "mmc_ops.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#ifdef CONFIG_FAIL_MMC_REQUEST 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic DECLARE_FAULT_ATTR(fail_default_attr); 2762306a36Sopenharmony_cistatic char *fail_request; 2862306a36Sopenharmony_cimodule_param(fail_request, charp, 0); 2962306a36Sopenharmony_ciMODULE_PARM_DESC(fail_request, "default fault injection attributes"); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#endif /* CONFIG_FAIL_MMC_REQUEST */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* The debugfs functions are optimized away when CONFIG_DEBUG_FS isn't set. */ 3462306a36Sopenharmony_cistatic int mmc_ios_show(struct seq_file *s, void *data) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci static const char *vdd_str[] = { 3762306a36Sopenharmony_ci [8] = "2.0", 3862306a36Sopenharmony_ci [9] = "2.1", 3962306a36Sopenharmony_ci [10] = "2.2", 4062306a36Sopenharmony_ci [11] = "2.3", 4162306a36Sopenharmony_ci [12] = "2.4", 4262306a36Sopenharmony_ci [13] = "2.5", 4362306a36Sopenharmony_ci [14] = "2.6", 4462306a36Sopenharmony_ci [15] = "2.7", 4562306a36Sopenharmony_ci [16] = "2.8", 4662306a36Sopenharmony_ci [17] = "2.9", 4762306a36Sopenharmony_ci [18] = "3.0", 4862306a36Sopenharmony_ci [19] = "3.1", 4962306a36Sopenharmony_ci [20] = "3.2", 5062306a36Sopenharmony_ci [21] = "3.3", 5162306a36Sopenharmony_ci [22] = "3.4", 5262306a36Sopenharmony_ci [23] = "3.5", 5362306a36Sopenharmony_ci [24] = "3.6", 5462306a36Sopenharmony_ci }; 5562306a36Sopenharmony_ci struct mmc_host *host = s->private; 5662306a36Sopenharmony_ci struct mmc_ios *ios = &host->ios; 5762306a36Sopenharmony_ci const char *str; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci seq_printf(s, "clock:\t\t%u Hz\n", ios->clock); 6062306a36Sopenharmony_ci if (host->actual_clock) 6162306a36Sopenharmony_ci seq_printf(s, "actual clock:\t%u Hz\n", host->actual_clock); 6262306a36Sopenharmony_ci seq_printf(s, "vdd:\t\t%u ", ios->vdd); 6362306a36Sopenharmony_ci if ((1 << ios->vdd) & MMC_VDD_165_195) 6462306a36Sopenharmony_ci seq_printf(s, "(1.65 - 1.95 V)\n"); 6562306a36Sopenharmony_ci else if (ios->vdd < (ARRAY_SIZE(vdd_str) - 1) 6662306a36Sopenharmony_ci && vdd_str[ios->vdd] && vdd_str[ios->vdd + 1]) 6762306a36Sopenharmony_ci seq_printf(s, "(%s ~ %s V)\n", vdd_str[ios->vdd], 6862306a36Sopenharmony_ci vdd_str[ios->vdd + 1]); 6962306a36Sopenharmony_ci else 7062306a36Sopenharmony_ci seq_printf(s, "(invalid)\n"); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci switch (ios->bus_mode) { 7362306a36Sopenharmony_ci case MMC_BUSMODE_OPENDRAIN: 7462306a36Sopenharmony_ci str = "open drain"; 7562306a36Sopenharmony_ci break; 7662306a36Sopenharmony_ci case MMC_BUSMODE_PUSHPULL: 7762306a36Sopenharmony_ci str = "push-pull"; 7862306a36Sopenharmony_ci break; 7962306a36Sopenharmony_ci default: 8062306a36Sopenharmony_ci str = "invalid"; 8162306a36Sopenharmony_ci break; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci seq_printf(s, "bus mode:\t%u (%s)\n", ios->bus_mode, str); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci switch (ios->chip_select) { 8662306a36Sopenharmony_ci case MMC_CS_DONTCARE: 8762306a36Sopenharmony_ci str = "don't care"; 8862306a36Sopenharmony_ci break; 8962306a36Sopenharmony_ci case MMC_CS_HIGH: 9062306a36Sopenharmony_ci str = "active high"; 9162306a36Sopenharmony_ci break; 9262306a36Sopenharmony_ci case MMC_CS_LOW: 9362306a36Sopenharmony_ci str = "active low"; 9462306a36Sopenharmony_ci break; 9562306a36Sopenharmony_ci default: 9662306a36Sopenharmony_ci str = "invalid"; 9762306a36Sopenharmony_ci break; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci seq_printf(s, "chip select:\t%u (%s)\n", ios->chip_select, str); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci switch (ios->power_mode) { 10262306a36Sopenharmony_ci case MMC_POWER_OFF: 10362306a36Sopenharmony_ci str = "off"; 10462306a36Sopenharmony_ci break; 10562306a36Sopenharmony_ci case MMC_POWER_UP: 10662306a36Sopenharmony_ci str = "up"; 10762306a36Sopenharmony_ci break; 10862306a36Sopenharmony_ci case MMC_POWER_ON: 10962306a36Sopenharmony_ci str = "on"; 11062306a36Sopenharmony_ci break; 11162306a36Sopenharmony_ci default: 11262306a36Sopenharmony_ci str = "invalid"; 11362306a36Sopenharmony_ci break; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci seq_printf(s, "power mode:\t%u (%s)\n", ios->power_mode, str); 11662306a36Sopenharmony_ci seq_printf(s, "bus width:\t%u (%u bits)\n", 11762306a36Sopenharmony_ci ios->bus_width, 1 << ios->bus_width); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci switch (ios->timing) { 12062306a36Sopenharmony_ci case MMC_TIMING_LEGACY: 12162306a36Sopenharmony_ci str = "legacy"; 12262306a36Sopenharmony_ci break; 12362306a36Sopenharmony_ci case MMC_TIMING_MMC_HS: 12462306a36Sopenharmony_ci str = "mmc high-speed"; 12562306a36Sopenharmony_ci break; 12662306a36Sopenharmony_ci case MMC_TIMING_SD_HS: 12762306a36Sopenharmony_ci str = "sd high-speed"; 12862306a36Sopenharmony_ci break; 12962306a36Sopenharmony_ci case MMC_TIMING_UHS_SDR12: 13062306a36Sopenharmony_ci str = "sd uhs SDR12"; 13162306a36Sopenharmony_ci break; 13262306a36Sopenharmony_ci case MMC_TIMING_UHS_SDR25: 13362306a36Sopenharmony_ci str = "sd uhs SDR25"; 13462306a36Sopenharmony_ci break; 13562306a36Sopenharmony_ci case MMC_TIMING_UHS_SDR50: 13662306a36Sopenharmony_ci str = "sd uhs SDR50"; 13762306a36Sopenharmony_ci break; 13862306a36Sopenharmony_ci case MMC_TIMING_UHS_SDR104: 13962306a36Sopenharmony_ci str = "sd uhs SDR104"; 14062306a36Sopenharmony_ci break; 14162306a36Sopenharmony_ci case MMC_TIMING_UHS_DDR50: 14262306a36Sopenharmony_ci str = "sd uhs DDR50"; 14362306a36Sopenharmony_ci break; 14462306a36Sopenharmony_ci case MMC_TIMING_MMC_DDR52: 14562306a36Sopenharmony_ci str = "mmc DDR52"; 14662306a36Sopenharmony_ci break; 14762306a36Sopenharmony_ci case MMC_TIMING_MMC_HS200: 14862306a36Sopenharmony_ci str = "mmc HS200"; 14962306a36Sopenharmony_ci break; 15062306a36Sopenharmony_ci case MMC_TIMING_MMC_HS400: 15162306a36Sopenharmony_ci str = mmc_card_hs400es(host->card) ? 15262306a36Sopenharmony_ci "mmc HS400 enhanced strobe" : "mmc HS400"; 15362306a36Sopenharmony_ci break; 15462306a36Sopenharmony_ci default: 15562306a36Sopenharmony_ci str = "invalid"; 15662306a36Sopenharmony_ci break; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci seq_printf(s, "timing spec:\t%u (%s)\n", ios->timing, str); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci switch (ios->signal_voltage) { 16162306a36Sopenharmony_ci case MMC_SIGNAL_VOLTAGE_330: 16262306a36Sopenharmony_ci str = "3.30 V"; 16362306a36Sopenharmony_ci break; 16462306a36Sopenharmony_ci case MMC_SIGNAL_VOLTAGE_180: 16562306a36Sopenharmony_ci str = "1.80 V"; 16662306a36Sopenharmony_ci break; 16762306a36Sopenharmony_ci case MMC_SIGNAL_VOLTAGE_120: 16862306a36Sopenharmony_ci str = "1.20 V"; 16962306a36Sopenharmony_ci break; 17062306a36Sopenharmony_ci default: 17162306a36Sopenharmony_ci str = "invalid"; 17262306a36Sopenharmony_ci break; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci seq_printf(s, "signal voltage:\t%u (%s)\n", ios->signal_voltage, str); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci switch (ios->drv_type) { 17762306a36Sopenharmony_ci case MMC_SET_DRIVER_TYPE_A: 17862306a36Sopenharmony_ci str = "driver type A"; 17962306a36Sopenharmony_ci break; 18062306a36Sopenharmony_ci case MMC_SET_DRIVER_TYPE_B: 18162306a36Sopenharmony_ci str = "driver type B"; 18262306a36Sopenharmony_ci break; 18362306a36Sopenharmony_ci case MMC_SET_DRIVER_TYPE_C: 18462306a36Sopenharmony_ci str = "driver type C"; 18562306a36Sopenharmony_ci break; 18662306a36Sopenharmony_ci case MMC_SET_DRIVER_TYPE_D: 18762306a36Sopenharmony_ci str = "driver type D"; 18862306a36Sopenharmony_ci break; 18962306a36Sopenharmony_ci default: 19062306a36Sopenharmony_ci str = "invalid"; 19162306a36Sopenharmony_ci break; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci seq_printf(s, "driver type:\t%u (%s)\n", ios->drv_type, str); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci return 0; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(mmc_ios); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic int mmc_clock_opt_get(void *data, u64 *val) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci struct mmc_host *host = data; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci *val = host->ios.clock; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci return 0; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic int mmc_clock_opt_set(void *data, u64 val) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci struct mmc_host *host = data; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* We need this check due to input value is u64 */ 21362306a36Sopenharmony_ci if (val != 0 && (val > host->f_max || val < host->f_min)) 21462306a36Sopenharmony_ci return -EINVAL; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci mmc_claim_host(host); 21762306a36Sopenharmony_ci mmc_set_clock(host, (unsigned int) val); 21862306a36Sopenharmony_ci mmc_release_host(host); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci return 0; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(mmc_clock_fops, mmc_clock_opt_get, mmc_clock_opt_set, 22462306a36Sopenharmony_ci "%llu\n"); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic int mmc_err_state_get(void *data, u64 *val) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci struct mmc_host *host = data; 22962306a36Sopenharmony_ci int i; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (!host) 23262306a36Sopenharmony_ci return -EINVAL; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci *val = 0; 23562306a36Sopenharmony_ci for (i = 0; i < MMC_ERR_MAX; i++) { 23662306a36Sopenharmony_ci if (host->err_stats[i]) { 23762306a36Sopenharmony_ci *val = 1; 23862306a36Sopenharmony_ci break; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci return 0; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ciDEFINE_DEBUGFS_ATTRIBUTE(mmc_err_state, mmc_err_state_get, NULL, "%llu\n"); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic int mmc_err_stats_show(struct seq_file *file, void *data) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci struct mmc_host *host = file->private; 25062306a36Sopenharmony_ci const char *desc[MMC_ERR_MAX] = { 25162306a36Sopenharmony_ci [MMC_ERR_CMD_TIMEOUT] = "Command Timeout Occurred", 25262306a36Sopenharmony_ci [MMC_ERR_CMD_CRC] = "Command CRC Errors Occurred", 25362306a36Sopenharmony_ci [MMC_ERR_DAT_TIMEOUT] = "Data Timeout Occurred", 25462306a36Sopenharmony_ci [MMC_ERR_DAT_CRC] = "Data CRC Errors Occurred", 25562306a36Sopenharmony_ci [MMC_ERR_AUTO_CMD] = "Auto-Cmd Error Occurred", 25662306a36Sopenharmony_ci [MMC_ERR_ADMA] = "ADMA Error Occurred", 25762306a36Sopenharmony_ci [MMC_ERR_TUNING] = "Tuning Error Occurred", 25862306a36Sopenharmony_ci [MMC_ERR_CMDQ_RED] = "CMDQ RED Errors", 25962306a36Sopenharmony_ci [MMC_ERR_CMDQ_GCE] = "CMDQ GCE Errors", 26062306a36Sopenharmony_ci [MMC_ERR_CMDQ_ICCE] = "CMDQ ICCE Errors", 26162306a36Sopenharmony_ci [MMC_ERR_REQ_TIMEOUT] = "Request Timedout", 26262306a36Sopenharmony_ci [MMC_ERR_CMDQ_REQ_TIMEOUT] = "CMDQ Request Timedout", 26362306a36Sopenharmony_ci [MMC_ERR_ICE_CFG] = "ICE Config Errors", 26462306a36Sopenharmony_ci [MMC_ERR_CTRL_TIMEOUT] = "Controller Timedout errors", 26562306a36Sopenharmony_ci [MMC_ERR_UNEXPECTED_IRQ] = "Unexpected IRQ errors", 26662306a36Sopenharmony_ci }; 26762306a36Sopenharmony_ci int i; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci for (i = 0; i < MMC_ERR_MAX; i++) { 27062306a36Sopenharmony_ci if (desc[i]) 27162306a36Sopenharmony_ci seq_printf(file, "# %s:\t %d\n", 27262306a36Sopenharmony_ci desc[i], host->err_stats[i]); 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci return 0; 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic int mmc_err_stats_open(struct inode *inode, struct file *file) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci return single_open(file, mmc_err_stats_show, inode->i_private); 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic ssize_t mmc_err_stats_write(struct file *filp, const char __user *ubuf, 28462306a36Sopenharmony_ci size_t cnt, loff_t *ppos) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci struct mmc_host *host = filp->f_mapping->host->i_private; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci pr_debug("%s: Resetting MMC error statistics\n", __func__); 28962306a36Sopenharmony_ci memset(host->err_stats, 0, sizeof(host->err_stats)); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci return cnt; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic const struct file_operations mmc_err_stats_fops = { 29562306a36Sopenharmony_ci .open = mmc_err_stats_open, 29662306a36Sopenharmony_ci .read = seq_read, 29762306a36Sopenharmony_ci .write = mmc_err_stats_write, 29862306a36Sopenharmony_ci .release = single_release, 29962306a36Sopenharmony_ci}; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_civoid mmc_add_host_debugfs(struct mmc_host *host) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci struct dentry *root; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci root = debugfs_create_dir(mmc_hostname(host), NULL); 30662306a36Sopenharmony_ci host->debugfs_root = root; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci debugfs_create_file("ios", S_IRUSR, root, host, &mmc_ios_fops); 30962306a36Sopenharmony_ci debugfs_create_x32("caps", S_IRUSR, root, &host->caps); 31062306a36Sopenharmony_ci debugfs_create_x32("caps2", S_IRUSR, root, &host->caps2); 31162306a36Sopenharmony_ci debugfs_create_file_unsafe("clock", S_IRUSR | S_IWUSR, root, host, 31262306a36Sopenharmony_ci &mmc_clock_fops); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci debugfs_create_file_unsafe("err_state", 0600, root, host, 31562306a36Sopenharmony_ci &mmc_err_state); 31662306a36Sopenharmony_ci debugfs_create_file("err_stats", 0600, root, host, 31762306a36Sopenharmony_ci &mmc_err_stats_fops); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci#ifdef CONFIG_FAIL_MMC_REQUEST 32062306a36Sopenharmony_ci if (fail_request) 32162306a36Sopenharmony_ci setup_fault_attr(&fail_default_attr, fail_request); 32262306a36Sopenharmony_ci host->fail_mmc_request = fail_default_attr; 32362306a36Sopenharmony_ci fault_create_debugfs_attr("fail_mmc_request", root, 32462306a36Sopenharmony_ci &host->fail_mmc_request); 32562306a36Sopenharmony_ci#endif 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_civoid mmc_remove_host_debugfs(struct mmc_host *host) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci debugfs_remove_recursive(host->debugfs_root); 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_civoid mmc_add_card_debugfs(struct mmc_card *card) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci struct mmc_host *host = card->host; 33662306a36Sopenharmony_ci struct dentry *root; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (!host->debugfs_root) 33962306a36Sopenharmony_ci return; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci root = debugfs_create_dir(mmc_card_id(card), host->debugfs_root); 34262306a36Sopenharmony_ci card->debugfs_root = root; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci debugfs_create_x32("state", S_IRUSR, root, &card->state); 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_civoid mmc_remove_card_debugfs(struct mmc_card *card) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci debugfs_remove_recursive(card->debugfs_root); 35062306a36Sopenharmony_ci card->debugfs_root = NULL; 35162306a36Sopenharmony_ci} 352