• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/drivers/mmc/core/
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. */
23static 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
125static int mmc_ios_open(struct inode *inode, struct file *file)
126{
127	return single_open(file, mmc_ios_show, inode->i_private);
128}
129
130static 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
137void 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
157err_ios:
158	debugfs_remove_recursive(root);
159	host->debugfs_root = NULL;
160err_root:
161	dev_err(&host->class_dev, "failed to initialize debugfs\n");
162}
163
164void mmc_remove_host_debugfs(struct mmc_host *host)
165{
166	debugfs_remove_recursive(host->debugfs_root);
167}
168
169static 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}
185DEFINE_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
190static 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
223out_free:
224	kfree(buf);
225	kfree(ext_csd);
226	return err;
227}
228
229static 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
238static int mmc_ext_csd_release(struct inode *inode, struct file *file)
239{
240	kfree(file->private_data);
241	return 0;
242}
243
244static 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
250void 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
284err:
285	debugfs_remove_recursive(root);
286	card->debugfs_root = NULL;
287	dev_err(&card->dev, "failed to initialize debugfs\n");
288}
289
290void mmc_remove_card_debugfs(struct mmc_card *card)
291{
292	debugfs_remove_recursive(card->debugfs_root);
293}
294