sdio: allow non-standard SDIO cards
[pandora-kernel.git] / drivers / mmc / core / debugfs.c
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 }