Merge branch 'x86-platform-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[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 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         return;
187
188 err_node:
189         debugfs_remove_recursive(root);
190         host->debugfs_root = NULL;
191 err_root:
192         dev_err(&host->class_dev, "failed to initialize debugfs\n");
193 }
194
195 void mmc_remove_host_debugfs(struct mmc_host *host)
196 {
197         debugfs_remove_recursive(host->debugfs_root);
198 }
199
200 static int mmc_dbg_card_status_get(void *data, u64 *val)
201 {
202         struct mmc_card *card = data;
203         u32             status;
204         int             ret;
205
206         mmc_claim_host(card->host);
207
208         ret = mmc_send_status(data, &status);
209         if (!ret)
210                 *val = status;
211
212         mmc_release_host(card->host);
213
214         return ret;
215 }
216 DEFINE_SIMPLE_ATTRIBUTE(mmc_dbg_card_status_fops, mmc_dbg_card_status_get,
217                 NULL, "%08llx\n");
218
219 #define EXT_CSD_STR_LEN 1025
220
221 static int mmc_ext_csd_open(struct inode *inode, struct file *filp)
222 {
223         struct mmc_card *card = inode->i_private;
224         char *buf;
225         ssize_t n = 0;
226         u8 *ext_csd;
227         int err, i;
228
229         buf = kmalloc(EXT_CSD_STR_LEN + 1, GFP_KERNEL);
230         if (!buf)
231                 return -ENOMEM;
232
233         ext_csd = kmalloc(512, GFP_KERNEL);
234         if (!ext_csd) {
235                 err = -ENOMEM;
236                 goto out_free;
237         }
238
239         mmc_claim_host(card->host);
240         err = mmc_send_ext_csd(card, ext_csd);
241         mmc_release_host(card->host);
242         if (err)
243                 goto out_free;
244
245         for (i = 511; i >= 0; i--)
246                 n += sprintf(buf + n, "%02x", ext_csd[i]);
247         n += sprintf(buf + n, "\n");
248         BUG_ON(n != EXT_CSD_STR_LEN);
249
250         filp->private_data = buf;
251         kfree(ext_csd);
252         return 0;
253
254 out_free:
255         kfree(buf);
256         kfree(ext_csd);
257         return err;
258 }
259
260 static ssize_t mmc_ext_csd_read(struct file *filp, char __user *ubuf,
261                                 size_t cnt, loff_t *ppos)
262 {
263         char *buf = filp->private_data;
264
265         return simple_read_from_buffer(ubuf, cnt, ppos,
266                                        buf, EXT_CSD_STR_LEN);
267 }
268
269 static int mmc_ext_csd_release(struct inode *inode, struct file *file)
270 {
271         kfree(file->private_data);
272         return 0;
273 }
274
275 static const struct file_operations mmc_dbg_ext_csd_fops = {
276         .open           = mmc_ext_csd_open,
277         .read           = mmc_ext_csd_read,
278         .release        = mmc_ext_csd_release,
279         .llseek         = default_llseek,
280 };
281
282 void mmc_add_card_debugfs(struct mmc_card *card)
283 {
284         struct mmc_host *host = card->host;
285         struct dentry   *root;
286
287         if (!host->debugfs_root)
288                 return;
289
290         root = debugfs_create_dir(mmc_card_id(card), host->debugfs_root);
291         if (IS_ERR(root))
292                 /* Don't complain -- debugfs just isn't enabled */
293                 return;
294         if (!root)
295                 /* Complain -- debugfs is enabled, but it failed to
296                  * create the directory. */
297                 goto err;
298
299         card->debugfs_root = root;
300
301         if (!debugfs_create_x32("state", S_IRUSR, root, &card->state))
302                 goto err;
303
304         if (mmc_card_mmc(card) || mmc_card_sd(card))
305                 if (!debugfs_create_file("status", S_IRUSR, root, card,
306                                         &mmc_dbg_card_status_fops))
307                         goto err;
308
309         if (mmc_card_mmc(card))
310                 if (!debugfs_create_file("ext_csd", S_IRUSR, root, card,
311                                         &mmc_dbg_ext_csd_fops))
312                         goto err;
313
314         return;
315
316 err:
317         debugfs_remove_recursive(root);
318         card->debugfs_root = NULL;
319         dev_err(&card->dev, "failed to initialize debugfs\n");
320 }
321
322 void mmc_remove_card_debugfs(struct mmc_card *card)
323 {
324         debugfs_remove_recursive(card->debugfs_root);
325 }