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