xref: /openbmc/linux/drivers/mmc/core/debugfs.c (revision 1fa6ac37)
1 /*
2  * Debugfs support for hosts and cards
3  *
4  * Copyright (C) 2008 Atmel Corporation
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  */
10 #include <linux/debugfs.h>
11 #include <linux/fs.h>
12 #include <linux/seq_file.h>
13 #include <linux/slab.h>
14 #include <linux/stat.h>
15 
16 #include <linux/mmc/card.h>
17 #include <linux/mmc/host.h>
18 
19 #include "core.h"
20 #include "mmc_ops.h"
21 
22 /* The debugfs functions are optimized away when CONFIG_DEBUG_FS isn't set. */
23 static int mmc_ios_show(struct seq_file *s, void *data)
24 {
25 	static const char *vdd_str[] = {
26 		[8]	= "2.0",
27 		[9]	= "2.1",
28 		[10]	= "2.2",
29 		[11]	= "2.3",
30 		[12]	= "2.4",
31 		[13]	= "2.5",
32 		[14]	= "2.6",
33 		[15]	= "2.7",
34 		[16]	= "2.8",
35 		[17]	= "2.9",
36 		[18]	= "3.0",
37 		[19]	= "3.1",
38 		[20]	= "3.2",
39 		[21]	= "3.3",
40 		[22]	= "3.4",
41 		[23]	= "3.5",
42 		[24]	= "3.6",
43 	};
44 	struct mmc_host	*host = s->private;
45 	struct mmc_ios	*ios = &host->ios;
46 	const char *str;
47 
48 	seq_printf(s, "clock:\t\t%u Hz\n", ios->clock);
49 	seq_printf(s, "vdd:\t\t%u ", ios->vdd);
50 	if ((1 << ios->vdd) & MMC_VDD_165_195)
51 		seq_printf(s, "(1.65 - 1.95 V)\n");
52 	else if (ios->vdd < (ARRAY_SIZE(vdd_str) - 1)
53 			&& vdd_str[ios->vdd] && vdd_str[ios->vdd + 1])
54 		seq_printf(s, "(%s ~ %s V)\n", vdd_str[ios->vdd],
55 				vdd_str[ios->vdd + 1]);
56 	else
57 		seq_printf(s, "(invalid)\n");
58 
59 	switch (ios->bus_mode) {
60 	case MMC_BUSMODE_OPENDRAIN:
61 		str = "open drain";
62 		break;
63 	case MMC_BUSMODE_PUSHPULL:
64 		str = "push-pull";
65 		break;
66 	default:
67 		str = "invalid";
68 		break;
69 	}
70 	seq_printf(s, "bus mode:\t%u (%s)\n", ios->bus_mode, str);
71 
72 	switch (ios->chip_select) {
73 	case MMC_CS_DONTCARE:
74 		str = "don't care";
75 		break;
76 	case MMC_CS_HIGH:
77 		str = "active high";
78 		break;
79 	case MMC_CS_LOW:
80 		str = "active low";
81 		break;
82 	default:
83 		str = "invalid";
84 		break;
85 	}
86 	seq_printf(s, "chip select:\t%u (%s)\n", ios->chip_select, str);
87 
88 	switch (ios->power_mode) {
89 	case MMC_POWER_OFF:
90 		str = "off";
91 		break;
92 	case MMC_POWER_UP:
93 		str = "up";
94 		break;
95 	case MMC_POWER_ON:
96 		str = "on";
97 		break;
98 	default:
99 		str = "invalid";
100 		break;
101 	}
102 	seq_printf(s, "power mode:\t%u (%s)\n", ios->power_mode, str);
103 	seq_printf(s, "bus width:\t%u (%u bits)\n",
104 			ios->bus_width, 1 << ios->bus_width);
105 
106 	switch (ios->timing) {
107 	case MMC_TIMING_LEGACY:
108 		str = "legacy";
109 		break;
110 	case MMC_TIMING_MMC_HS:
111 		str = "mmc high-speed";
112 		break;
113 	case MMC_TIMING_SD_HS:
114 		str = "sd high-speed";
115 		break;
116 	default:
117 		str = "invalid";
118 		break;
119 	}
120 	seq_printf(s, "timing spec:\t%u (%s)\n", ios->timing, str);
121 
122 	return 0;
123 }
124 
125 static int mmc_ios_open(struct inode *inode, struct file *file)
126 {
127 	return single_open(file, mmc_ios_show, inode->i_private);
128 }
129 
130 static const struct file_operations mmc_ios_fops = {
131 	.open		= mmc_ios_open,
132 	.read		= seq_read,
133 	.llseek		= seq_lseek,
134 	.release	= single_release,
135 };
136 
137 void mmc_add_host_debugfs(struct mmc_host *host)
138 {
139 	struct dentry *root;
140 
141 	root = debugfs_create_dir(mmc_hostname(host), NULL);
142 	if (IS_ERR(root))
143 		/* Don't complain -- debugfs just isn't enabled */
144 		return;
145 	if (!root)
146 		/* Complain -- debugfs is enabled, but it failed to
147 		 * create the directory. */
148 		goto err_root;
149 
150 	host->debugfs_root = root;
151 
152 	if (!debugfs_create_file("ios", S_IRUSR, root, host, &mmc_ios_fops))
153 		goto err_ios;
154 
155 	return;
156 
157 err_ios:
158 	debugfs_remove_recursive(root);
159 	host->debugfs_root = NULL;
160 err_root:
161 	dev_err(&host->class_dev, "failed to initialize debugfs\n");
162 }
163 
164 void mmc_remove_host_debugfs(struct mmc_host *host)
165 {
166 	debugfs_remove_recursive(host->debugfs_root);
167 }
168 
169 static int mmc_dbg_card_status_get(void *data, u64 *val)
170 {
171 	struct mmc_card	*card = data;
172 	u32		status;
173 	int		ret;
174 
175 	mmc_claim_host(card->host);
176 
177 	ret = mmc_send_status(data, &status);
178 	if (!ret)
179 		*val = status;
180 
181 	mmc_release_host(card->host);
182 
183 	return ret;
184 }
185 DEFINE_SIMPLE_ATTRIBUTE(mmc_dbg_card_status_fops, mmc_dbg_card_status_get,
186 		NULL, "%08llx\n");
187 
188 #define EXT_CSD_STR_LEN 1025
189 
190 static int mmc_ext_csd_open(struct inode *inode, struct file *filp)
191 {
192 	struct mmc_card *card = inode->i_private;
193 	char *buf;
194 	ssize_t n = 0;
195 	u8 *ext_csd;
196 	int err, i;
197 
198 	buf = kmalloc(EXT_CSD_STR_LEN + 1, GFP_KERNEL);
199 	if (!buf)
200 		return -ENOMEM;
201 
202 	ext_csd = kmalloc(512, GFP_KERNEL);
203 	if (!ext_csd) {
204 		err = -ENOMEM;
205 		goto out_free;
206 	}
207 
208 	mmc_claim_host(card->host);
209 	err = mmc_send_ext_csd(card, ext_csd);
210 	mmc_release_host(card->host);
211 	if (err)
212 		goto out_free;
213 
214 	for (i = 511; i >= 0; i--)
215 		n += sprintf(buf + n, "%02x", ext_csd[i]);
216 	n += sprintf(buf + n, "\n");
217 	BUG_ON(n != EXT_CSD_STR_LEN);
218 
219 	filp->private_data = buf;
220 	kfree(ext_csd);
221 	return 0;
222 
223 out_free:
224 	kfree(buf);
225 	kfree(ext_csd);
226 	return err;
227 }
228 
229 static ssize_t mmc_ext_csd_read(struct file *filp, char __user *ubuf,
230 				size_t cnt, loff_t *ppos)
231 {
232 	char *buf = filp->private_data;
233 
234 	return simple_read_from_buffer(ubuf, cnt, ppos,
235 				       buf, EXT_CSD_STR_LEN);
236 }
237 
238 static int mmc_ext_csd_release(struct inode *inode, struct file *file)
239 {
240 	kfree(file->private_data);
241 	return 0;
242 }
243 
244 static const struct file_operations mmc_dbg_ext_csd_fops = {
245 	.open		= mmc_ext_csd_open,
246 	.read		= mmc_ext_csd_read,
247 	.release	= mmc_ext_csd_release,
248 };
249 
250 void mmc_add_card_debugfs(struct mmc_card *card)
251 {
252 	struct mmc_host	*host = card->host;
253 	struct dentry	*root;
254 
255 	if (!host->debugfs_root)
256 		return;
257 
258 	root = debugfs_create_dir(mmc_card_id(card), host->debugfs_root);
259 	if (IS_ERR(root))
260 		/* Don't complain -- debugfs just isn't enabled */
261 		return;
262 	if (!root)
263 		/* Complain -- debugfs is enabled, but it failed to
264 		 * create the directory. */
265 		goto err;
266 
267 	card->debugfs_root = root;
268 
269 	if (!debugfs_create_x32("state", S_IRUSR, root, &card->state))
270 		goto err;
271 
272 	if (mmc_card_mmc(card) || mmc_card_sd(card))
273 		if (!debugfs_create_file("status", S_IRUSR, root, card,
274 					&mmc_dbg_card_status_fops))
275 			goto err;
276 
277 	if (mmc_card_mmc(card))
278 		if (!debugfs_create_file("ext_csd", S_IRUSR, root, card,
279 					&mmc_dbg_ext_csd_fops))
280 			goto err;
281 
282 	return;
283 
284 err:
285 	debugfs_remove_recursive(root);
286 	card->debugfs_root = NULL;
287 	dev_err(&card->dev, "failed to initialize debugfs\n");
288 }
289 
290 void mmc_remove_card_debugfs(struct mmc_card *card)
291 {
292 	debugfs_remove_recursive(card->debugfs_root);
293 }
294