Branch data Line data Source code
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/moduleparam.h>
11 : : #include <linux/export.h>
12 : : #include <linux/debugfs.h>
13 : : #include <linux/fs.h>
14 : : #include <linux/seq_file.h>
15 : : #include <linux/slab.h>
16 : : #include <linux/stat.h>
17 : : #include <linux/fault-inject.h>
18 : :
19 : : #include <linux/mmc/card.h>
20 : : #include <linux/mmc/host.h>
21 : :
22 : : #include "core.h"
23 : : #include "mmc_ops.h"
24 : :
25 : : #ifdef CONFIG_FAIL_MMC_REQUEST
26 : :
27 : : static DECLARE_FAULT_ATTR(fail_default_attr);
28 : : static char *fail_request;
29 : : module_param(fail_request, charp, 0);
30 : :
31 : : #endif /* CONFIG_FAIL_MMC_REQUEST */
32 : :
33 : : /* The debugfs functions are optimized away when CONFIG_DEBUG_FS isn't set. */
34 : 0 : static int mmc_ios_show(struct seq_file *s, void *data)
35 : : {
36 : : static const char *vdd_str[] = {
37 : : [8] = "2.0",
38 : : [9] = "2.1",
39 : : [10] = "2.2",
40 : : [11] = "2.3",
41 : : [12] = "2.4",
42 : : [13] = "2.5",
43 : : [14] = "2.6",
44 : : [15] = "2.7",
45 : : [16] = "2.8",
46 : : [17] = "2.9",
47 : : [18] = "3.0",
48 : : [19] = "3.1",
49 : : [20] = "3.2",
50 : : [21] = "3.3",
51 : : [22] = "3.4",
52 : : [23] = "3.5",
53 : : [24] = "3.6",
54 : : };
55 : 0 : struct mmc_host *host = s->private;
56 : : struct mmc_ios *ios = &host->ios;
57 : : const char *str;
58 : :
59 : 0 : seq_printf(s, "clock:\t\t%u Hz\n", ios->clock);
60 [ # # ]: 0 : if (host->actual_clock)
61 : 0 : seq_printf(s, "actual clock:\t%u Hz\n", host->actual_clock);
62 : 0 : seq_printf(s, "vdd:\t\t%u ", ios->vdd);
63 [ # # ]: 0 : if ((1 << ios->vdd) & MMC_VDD_165_195)
64 : 0 : seq_printf(s, "(1.65 - 1.95 V)\n");
65 [ # # ]: 0 : else if (ios->vdd < (ARRAY_SIZE(vdd_str) - 1)
66 [ # # ][ # # ]: 0 : && vdd_str[ios->vdd] && vdd_str[ios->vdd + 1])
67 : 0 : seq_printf(s, "(%s ~ %s V)\n", vdd_str[ios->vdd],
68 : : vdd_str[ios->vdd + 1]);
69 : : else
70 : 0 : seq_printf(s, "(invalid)\n");
71 : :
72 [ # # # ]: 0 : switch (ios->bus_mode) {
73 : : case MMC_BUSMODE_OPENDRAIN:
74 : : str = "open drain";
75 : : break;
76 : : case MMC_BUSMODE_PUSHPULL:
77 : : str = "push-pull";
78 : 0 : break;
79 : : default:
80 : : str = "invalid";
81 : 0 : break;
82 : : }
83 : 0 : seq_printf(s, "bus mode:\t%u (%s)\n", ios->bus_mode, str);
84 : :
85 [ # # ]: 0 : switch (ios->chip_select) {
86 : : case MMC_CS_DONTCARE:
87 : : str = "don't care";
88 : : break;
89 : : case MMC_CS_HIGH:
90 : : str = "active high";
91 : : break;
92 : : case MMC_CS_LOW:
93 : : str = "active low";
94 : : break;
95 : : default:
96 : : str = "invalid";
97 : : break;
98 : : }
99 : 0 : seq_printf(s, "chip select:\t%u (%s)\n", ios->chip_select, str);
100 : :
101 [ # # ]: 0 : switch (ios->power_mode) {
102 : : case MMC_POWER_OFF:
103 : : str = "off";
104 : : break;
105 : : case MMC_POWER_UP:
106 : : str = "up";
107 : : break;
108 : : case MMC_POWER_ON:
109 : : str = "on";
110 : : break;
111 : : default:
112 : : str = "invalid";
113 : : break;
114 : : }
115 : 0 : seq_printf(s, "power mode:\t%u (%s)\n", ios->power_mode, str);
116 : 0 : seq_printf(s, "bus width:\t%u (%u bits)\n",
117 : 0 : ios->bus_width, 1 << ios->bus_width);
118 : :
119 [ # # ]: 0 : switch (ios->timing) {
120 : : case MMC_TIMING_LEGACY:
121 : : str = "legacy";
122 : : break;
123 : : case MMC_TIMING_MMC_HS:
124 : : str = "mmc high-speed";
125 : : break;
126 : : case MMC_TIMING_SD_HS:
127 : : str = "sd high-speed";
128 : : break;
129 : : case MMC_TIMING_UHS_SDR50:
130 : : str = "sd uhs SDR50";
131 : : break;
132 : : case MMC_TIMING_UHS_SDR104:
133 : : str = "sd uhs SDR104";
134 : : break;
135 : : case MMC_TIMING_UHS_DDR50:
136 : : str = "sd uhs DDR50";
137 : : break;
138 : : case MMC_TIMING_MMC_HS200:
139 : : str = "mmc high-speed SDR200";
140 : : break;
141 : : default:
142 : : str = "invalid";
143 : : break;
144 : : }
145 : 0 : seq_printf(s, "timing spec:\t%u (%s)\n", ios->timing, str);
146 : :
147 [ # # ]: 0 : switch (ios->signal_voltage) {
148 : : case MMC_SIGNAL_VOLTAGE_330:
149 : : str = "3.30 V";
150 : : break;
151 : : case MMC_SIGNAL_VOLTAGE_180:
152 : : str = "1.80 V";
153 : : break;
154 : : case MMC_SIGNAL_VOLTAGE_120:
155 : : str = "1.20 V";
156 : : break;
157 : : default:
158 : : str = "invalid";
159 : : break;
160 : : }
161 : 0 : seq_printf(s, "signal voltage:\t%u (%s)\n", ios->chip_select, str);
162 : :
163 : 0 : return 0;
164 : : }
165 : :
166 : 0 : static int mmc_ios_open(struct inode *inode, struct file *file)
167 : : {
168 : 0 : return single_open(file, mmc_ios_show, inode->i_private);
169 : : }
170 : :
171 : : static const struct file_operations mmc_ios_fops = {
172 : : .open = mmc_ios_open,
173 : : .read = seq_read,
174 : : .llseek = seq_lseek,
175 : : .release = single_release,
176 : : };
177 : :
178 : 0 : static int mmc_clock_opt_get(void *data, u64 *val)
179 : : {
180 : : struct mmc_host *host = data;
181 : :
182 : 0 : *val = host->ios.clock;
183 : :
184 : 0 : return 0;
185 : : }
186 : :
187 : 0 : static int mmc_clock_opt_set(void *data, u64 val)
188 : : {
189 : : struct mmc_host *host = data;
190 : :
191 : : /* We need this check due to input value is u64 */
192 [ # # ]: 0 : if (val > host->f_max)
193 : : return -EINVAL;
194 : :
195 : : mmc_claim_host(host);
196 : 0 : mmc_set_clock(host, (unsigned int) val);
197 : 0 : mmc_release_host(host);
198 : :
199 : 0 : return 0;
200 : : }
201 : :
202 : 0 : DEFINE_SIMPLE_ATTRIBUTE(mmc_clock_fops, mmc_clock_opt_get, mmc_clock_opt_set,
203 : : "%llu\n");
204 : :
205 : 0 : void mmc_add_host_debugfs(struct mmc_host *host)
206 : : {
207 : : struct dentry *root;
208 : :
209 : 0 : root = debugfs_create_dir(mmc_hostname(host), NULL);
210 [ # # ]: 0 : if (IS_ERR(root))
211 : : /* Don't complain -- debugfs just isn't enabled */
212 : : return;
213 [ # # ]: 0 : if (!root)
214 : : /* Complain -- debugfs is enabled, but it failed to
215 : : * create the directory. */
216 : : goto err_root;
217 : :
218 : 0 : host->debugfs_root = root;
219 : :
220 [ # # ]: 0 : if (!debugfs_create_file("ios", S_IRUSR, root, host, &mmc_ios_fops))
221 : : goto err_node;
222 : :
223 [ # # ]: 0 : if (!debugfs_create_file("clock", S_IRUSR | S_IWUSR, root, host,
224 : : &mmc_clock_fops))
225 : : goto err_node;
226 : :
227 : : #ifdef CONFIG_MMC_CLKGATE
228 : : if (!debugfs_create_u32("clk_delay", (S_IRUSR | S_IWUSR),
229 : : root, &host->clk_delay))
230 : : goto err_node;
231 : : #endif
232 : : #ifdef CONFIG_FAIL_MMC_REQUEST
233 : : if (fail_request)
234 : : setup_fault_attr(&fail_default_attr, fail_request);
235 : : host->fail_mmc_request = fail_default_attr;
236 : : if (IS_ERR(fault_create_debugfs_attr("fail_mmc_request",
237 : : root,
238 : : &host->fail_mmc_request)))
239 : : goto err_node;
240 : : #endif
241 : : return;
242 : :
243 : : err_node:
244 : 0 : debugfs_remove_recursive(root);
245 : 0 : host->debugfs_root = NULL;
246 : : err_root:
247 : 0 : dev_err(&host->class_dev, "failed to initialize debugfs\n");
248 : : }
249 : :
250 : 0 : void mmc_remove_host_debugfs(struct mmc_host *host)
251 : : {
252 : 0 : debugfs_remove_recursive(host->debugfs_root);
253 : 0 : }
254 : :
255 : 0 : static int mmc_dbg_card_status_get(void *data, u64 *val)
256 : : {
257 : : struct mmc_card *card = data;
258 : : u32 status;
259 : : int ret;
260 : :
261 : 0 : mmc_get_card(card);
262 : :
263 : 0 : ret = mmc_send_status(data, &status);
264 [ # # ]: 0 : if (!ret)
265 : 0 : *val = status;
266 : :
267 : 0 : mmc_put_card(card);
268 : :
269 : 0 : return ret;
270 : : }
271 : 0 : DEFINE_SIMPLE_ATTRIBUTE(mmc_dbg_card_status_fops, mmc_dbg_card_status_get,
272 : : NULL, "%08llx\n");
273 : :
274 : : #define EXT_CSD_STR_LEN 1025
275 : :
276 : 0 : static int mmc_ext_csd_open(struct inode *inode, struct file *filp)
277 : : {
278 : 0 : struct mmc_card *card = inode->i_private;
279 : : char *buf;
280 : : ssize_t n = 0;
281 : : u8 *ext_csd;
282 : : int err, i;
283 : :
284 : : buf = kmalloc(EXT_CSD_STR_LEN + 1, GFP_KERNEL);
285 [ # # ]: 0 : if (!buf)
286 : : return -ENOMEM;
287 : :
288 : : ext_csd = kmalloc(512, GFP_KERNEL);
289 [ # # ]: 0 : if (!ext_csd) {
290 : : err = -ENOMEM;
291 : : goto out_free;
292 : : }
293 : :
294 : 0 : mmc_get_card(card);
295 : 0 : err = mmc_send_ext_csd(card, ext_csd);
296 : 0 : mmc_put_card(card);
297 [ # # ]: 0 : if (err)
298 : : goto out_free;
299 : :
300 [ # # ]: 0 : for (i = 0; i < 512; i++)
301 : 0 : n += sprintf(buf + n, "%02x", ext_csd[i]);
302 : 0 : n += sprintf(buf + n, "\n");
303 [ # # ]: 0 : BUG_ON(n != EXT_CSD_STR_LEN);
304 : :
305 : 0 : filp->private_data = buf;
306 : 0 : kfree(ext_csd);
307 : 0 : return 0;
308 : :
309 : : out_free:
310 : 0 : kfree(buf);
311 : 0 : kfree(ext_csd);
312 : 0 : return err;
313 : : }
314 : :
315 : 0 : static ssize_t mmc_ext_csd_read(struct file *filp, char __user *ubuf,
316 : : size_t cnt, loff_t *ppos)
317 : : {
318 : 0 : char *buf = filp->private_data;
319 : :
320 : 0 : return simple_read_from_buffer(ubuf, cnt, ppos,
321 : : buf, EXT_CSD_STR_LEN);
322 : : }
323 : :
324 : 0 : static int mmc_ext_csd_release(struct inode *inode, struct file *file)
325 : : {
326 : 0 : kfree(file->private_data);
327 : 0 : return 0;
328 : : }
329 : :
330 : : static const struct file_operations mmc_dbg_ext_csd_fops = {
331 : : .open = mmc_ext_csd_open,
332 : : .read = mmc_ext_csd_read,
333 : : .release = mmc_ext_csd_release,
334 : : .llseek = default_llseek,
335 : : };
336 : :
337 : 0 : void mmc_add_card_debugfs(struct mmc_card *card)
338 : : {
339 : 0 : struct mmc_host *host = card->host;
340 : : struct dentry *root;
341 : :
342 [ # # ]: 0 : if (!host->debugfs_root)
343 : : return;
344 : :
345 : 0 : root = debugfs_create_dir(mmc_card_id(card), host->debugfs_root);
346 [ # # ]: 0 : if (IS_ERR(root))
347 : : /* Don't complain -- debugfs just isn't enabled */
348 : : return;
349 [ # # ]: 0 : if (!root)
350 : : /* Complain -- debugfs is enabled, but it failed to
351 : : * create the directory. */
352 : : goto err;
353 : :
354 : 0 : card->debugfs_root = root;
355 : :
356 [ # # ]: 0 : if (!debugfs_create_x32("state", S_IRUSR, root, &card->state))
357 : : goto err;
358 : :
359 [ # # ]: 0 : if (mmc_card_mmc(card) || mmc_card_sd(card))
360 [ # # ]: 0 : if (!debugfs_create_file("status", S_IRUSR, root, card,
361 : : &mmc_dbg_card_status_fops))
362 : : goto err;
363 : :
364 [ # # ]: 0 : if (mmc_card_mmc(card))
365 [ # # ]: 0 : if (!debugfs_create_file("ext_csd", S_IRUSR, root, card,
366 : : &mmc_dbg_ext_csd_fops))
367 : : goto err;
368 : :
369 : : return;
370 : :
371 : : err:
372 : 0 : debugfs_remove_recursive(root);
373 : 0 : card->debugfs_root = NULL;
374 : 0 : dev_err(&card->dev, "failed to initialize debugfs\n");
375 : : }
376 : :
377 : 0 : void mmc_remove_card_debugfs(struct mmc_card *card)
378 : : {
379 : 0 : debugfs_remove_recursive(card->debugfs_root);
380 : 0 : }
|