xref: /openbmc/linux/drivers/mmc/core/debugfs.c (revision 4800cd83)
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 static int mmc_clock_opt_get(void *data, u64 *val)
138 {
139 	struct mmc_host *host = data;
140 
141 	*val = host->ios.clock;
142 
143 	return 0;
144 }
145 
146 static int mmc_clock_opt_set(void *data, u64 val)
147 {
148 	struct mmc_host *host = data;
149 
150 	/* We need this check due to input value is u64 */
151 	if (val > host->f_max)
152 		return -EINVAL;
153 
154 	mmc_claim_host(host);
155 	mmc_set_clock(host, (unsigned int) val);
156 	mmc_release_host(host);
157 
158 	return 0;
159 }
160 
161 DEFINE_SIMPLE_ATTRIBUTE(mmc_clock_fops, mmc_clock_opt_get, mmc_clock_opt_set,
162 	"%llu\n");
163 
164 void mmc_add_host_debugfs(struct mmc_host *host)
165 {
166 	struct dentry *root;
167 
168 	root = debugfs_create_dir(mmc_hostname(host), NULL);
169 	if (IS_ERR(root))
170 		/* Don't complain -- debugfs just isn't enabled */
171 		return;
172 	if (!root)
173 		/* Complain -- debugfs is enabled, but it failed to
174 		 * create the directory. */
175 		goto err_root;
176 
177 	host->debugfs_root = root;
178 
179 	if (!debugfs_create_file("ios", S_IRUSR, root, host, &mmc_ios_fops))
180 		goto err_node;
181 
182 	if (!debugfs_create_file("clock", S_IRUSR | S_IWUSR, root, host,
183 			&mmc_clock_fops))
184 		goto err_node;
185 
186 #ifdef CONFIG_MMC_CLKGATE
187 	if (!debugfs_create_u32("clk_delay", (S_IRUSR | S_IWUSR),
188 				root, &host->clk_delay))
189 		goto err_node;
190 #endif
191 	return;
192 
193 err_node:
194 	debugfs_remove_recursive(root);
195 	host->debugfs_root = NULL;
196 err_root:
197 	dev_err(&host->class_dev, "failed to initialize debugfs\n");
198 }
199 
200 void mmc_remove_host_debugfs(struct mmc_host *host)
201 {
202 	debugfs_remove_recursive(host->debugfs_root);
203 }
204 
205 static int mmc_dbg_card_status_get(void *data, u64 *val)
206 {
207 	struct mmc_card	*card = data;
208 	u32		status;
209 	int		ret;
210 
211 	mmc_claim_host(card->host);
212 
213 	ret = mmc_send_status(data, &status);
214 	if (!ret)
215 		*val = status;
216 
217 	mmc_release_host(card->host);
218 
219 	return ret;
220 }
221 DEFINE_SIMPLE_ATTRIBUTE(mmc_dbg_card_status_fops, mmc_dbg_card_status_get,
222 		NULL, "%08llx\n");
223 
224 #define EXT_CSD_STR_LEN 1025
225 
226 static int mmc_ext_csd_open(struct inode *inode, struct file *filp)
227 {
228 	struct mmc_card *card = inode->i_private;
229 	char *buf;
230 	ssize_t n = 0;
231 	u8 *ext_csd;
232 	int err, i;
233 
234 	buf = kmalloc(EXT_CSD_STR_LEN + 1, GFP_KERNEL);
235 	if (!buf)
236 		return -ENOMEM;
237 
238 	ext_csd = kmalloc(512, GFP_KERNEL);
239 	if (!ext_csd) {
240 		err = -ENOMEM;
241 		goto out_free;
242 	}
243 
244 	mmc_claim_host(card->host);
245 	err = mmc_send_ext_csd(card, ext_csd);
246 	mmc_release_host(card->host);
247 	if (err)
248 		goto out_free;
249 
250 	for (i = 511; i >= 0; i--)
251 		n += sprintf(buf + n, "%02x", ext_csd[i]);
252 	n += sprintf(buf + n, "\n");
253 	BUG_ON(n != EXT_CSD_STR_LEN);
254 
255 	filp->private_data = buf;
256 	kfree(ext_csd);
257 	return 0;
258 
259 out_free:
260 	kfree(buf);
261 	kfree(ext_csd);
262 	return err;
263 }
264 
265 static ssize_t mmc_ext_csd_read(struct file *filp, char __user *ubuf,
266 				size_t cnt, loff_t *ppos)
267 {
268 	char *buf = filp->private_data;
269 
270 	return simple_read_from_buffer(ubuf, cnt, ppos,
271 				       buf, EXT_CSD_STR_LEN);
272 }
273 
274 static int mmc_ext_csd_release(struct inode *inode, struct file *file)
275 {
276 	kfree(file->private_data);
277 	return 0;
278 }
279 
280 static const struct file_operations mmc_dbg_ext_csd_fops = {
281 	.open		= mmc_ext_csd_open,
282 	.read		= mmc_ext_csd_read,
283 	.release	= mmc_ext_csd_release,
284 	.llseek		= default_llseek,
285 };
286 
287 void mmc_add_card_debugfs(struct mmc_card *card)
288 {
289 	struct mmc_host	*host = card->host;
290 	struct dentry	*root;
291 
292 	if (!host->debugfs_root)
293 		return;
294 
295 	root = debugfs_create_dir(mmc_card_id(card), host->debugfs_root);
296 	if (IS_ERR(root))
297 		/* Don't complain -- debugfs just isn't enabled */
298 		return;
299 	if (!root)
300 		/* Complain -- debugfs is enabled, but it failed to
301 		 * create the directory. */
302 		goto err;
303 
304 	card->debugfs_root = root;
305 
306 	if (!debugfs_create_x32("state", S_IRUSR, root, &card->state))
307 		goto err;
308 
309 	if (mmc_card_mmc(card) || mmc_card_sd(card))
310 		if (!debugfs_create_file("status", S_IRUSR, root, card,
311 					&mmc_dbg_card_status_fops))
312 			goto err;
313 
314 	if (mmc_card_mmc(card))
315 		if (!debugfs_create_file("ext_csd", S_IRUSR, root, card,
316 					&mmc_dbg_ext_csd_fops))
317 			goto err;
318 
319 	return;
320 
321 err:
322 	debugfs_remove_recursive(root);
323 	card->debugfs_root = NULL;
324 	dev_err(&card->dev, "failed to initialize debugfs\n");
325 }
326 
327 void mmc_remove_card_debugfs(struct mmc_card *card)
328 {
329 	debugfs_remove_recursive(card->debugfs_root);
330 }
331