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