|
@@ -36,6 +36,7 @@
|
|
|
#include <linux/compat.h>
|
|
|
#include <linux/pm_runtime.h>
|
|
|
#include <linux/idr.h>
|
|
|
+#include <linux/debugfs.h>
|
|
|
|
|
|
#include <linux/mmc/ioctl.h>
|
|
|
#include <linux/mmc/card.h>
|
|
@@ -1177,6 +1178,8 @@ static void mmc_blk_issue_drv_op(struct mmc_queue *mq, struct request *req)
|
|
|
struct mmc_card *card = mq->card;
|
|
|
struct mmc_blk_data *md = mq->blkdata;
|
|
|
struct mmc_blk_ioc_data **idata;
|
|
|
+ u8 **ext_csd;
|
|
|
+ u32 status;
|
|
|
int ret;
|
|
|
int i;
|
|
|
|
|
@@ -1206,6 +1209,15 @@ static void mmc_blk_issue_drv_op(struct mmc_queue *mq, struct request *req)
|
|
|
card->ext_csd.boot_ro_lock |=
|
|
|
EXT_CSD_BOOT_WP_B_PWR_WP_EN;
|
|
|
break;
|
|
|
+ case MMC_DRV_OP_GET_CARD_STATUS:
|
|
|
+ ret = mmc_send_status(card, &status);
|
|
|
+ if (!ret)
|
|
|
+ ret = status;
|
|
|
+ break;
|
|
|
+ case MMC_DRV_OP_GET_EXT_CSD:
|
|
|
+ ext_csd = mq_rq->drv_op_data;
|
|
|
+ ret = mmc_get_ext_csd(card, ext_csd);
|
|
|
+ break;
|
|
|
default:
|
|
|
pr_err("%s: unknown driver specific operation\n",
|
|
|
md->disk->disk_name);
|
|
@@ -2283,6 +2295,134 @@ force_ro_fail:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+#ifdef CONFIG_DEBUG_FS
|
|
|
+
|
|
|
+static int mmc_dbg_card_status_get(void *data, u64 *val)
|
|
|
+{
|
|
|
+ struct mmc_card *card = data;
|
|
|
+ struct mmc_blk_data *md = dev_get_drvdata(&card->dev);
|
|
|
+ struct mmc_queue *mq = &md->queue;
|
|
|
+ struct request *req;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /* Ask the block layer about the card status */
|
|
|
+ req = blk_get_request(mq->queue, REQ_OP_DRV_IN, __GFP_RECLAIM);
|
|
|
+ req_to_mmc_queue_req(req)->drv_op = MMC_DRV_OP_GET_CARD_STATUS;
|
|
|
+ blk_execute_rq(mq->queue, NULL, req, 0);
|
|
|
+ ret = req_to_mmc_queue_req(req)->drv_op_result;
|
|
|
+ if (ret >= 0) {
|
|
|
+ *val = ret;
|
|
|
+ ret = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+DEFINE_SIMPLE_ATTRIBUTE(mmc_dbg_card_status_fops, mmc_dbg_card_status_get,
|
|
|
+ NULL, "%08llx\n");
|
|
|
+
|
|
|
+/* That is two digits * 512 + 1 for newline */
|
|
|
+#define EXT_CSD_STR_LEN 1025
|
|
|
+
|
|
|
+static int mmc_ext_csd_open(struct inode *inode, struct file *filp)
|
|
|
+{
|
|
|
+ struct mmc_card *card = inode->i_private;
|
|
|
+ struct mmc_blk_data *md = dev_get_drvdata(&card->dev);
|
|
|
+ struct mmc_queue *mq = &md->queue;
|
|
|
+ struct request *req;
|
|
|
+ char *buf;
|
|
|
+ ssize_t n = 0;
|
|
|
+ u8 *ext_csd;
|
|
|
+ int err, i;
|
|
|
+
|
|
|
+ buf = kmalloc(EXT_CSD_STR_LEN + 1, GFP_KERNEL);
|
|
|
+ if (!buf)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ /* Ask the block layer for the EXT CSD */
|
|
|
+ req = blk_get_request(mq->queue, REQ_OP_DRV_IN, __GFP_RECLAIM);
|
|
|
+ req_to_mmc_queue_req(req)->drv_op = MMC_DRV_OP_GET_EXT_CSD;
|
|
|
+ req_to_mmc_queue_req(req)->drv_op_data = &ext_csd;
|
|
|
+ blk_execute_rq(mq->queue, NULL, req, 0);
|
|
|
+ err = req_to_mmc_queue_req(req)->drv_op_result;
|
|
|
+ if (err) {
|
|
|
+ pr_err("FAILED %d\n", err);
|
|
|
+ goto out_free;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; i < 512; i++)
|
|
|
+ n += sprintf(buf + n, "%02x", ext_csd[i]);
|
|
|
+ n += sprintf(buf + n, "\n");
|
|
|
+
|
|
|
+ if (n != EXT_CSD_STR_LEN) {
|
|
|
+ err = -EINVAL;
|
|
|
+ goto out_free;
|
|
|
+ }
|
|
|
+
|
|
|
+ filp->private_data = buf;
|
|
|
+ kfree(ext_csd);
|
|
|
+ return 0;
|
|
|
+
|
|
|
+out_free:
|
|
|
+ kfree(buf);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t mmc_ext_csd_read(struct file *filp, char __user *ubuf,
|
|
|
+ size_t cnt, loff_t *ppos)
|
|
|
+{
|
|
|
+ char *buf = filp->private_data;
|
|
|
+
|
|
|
+ return simple_read_from_buffer(ubuf, cnt, ppos,
|
|
|
+ buf, EXT_CSD_STR_LEN);
|
|
|
+}
|
|
|
+
|
|
|
+static int mmc_ext_csd_release(struct inode *inode, struct file *file)
|
|
|
+{
|
|
|
+ kfree(file->private_data);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static const struct file_operations mmc_dbg_ext_csd_fops = {
|
|
|
+ .open = mmc_ext_csd_open,
|
|
|
+ .read = mmc_ext_csd_read,
|
|
|
+ .release = mmc_ext_csd_release,
|
|
|
+ .llseek = default_llseek,
|
|
|
+};
|
|
|
+
|
|
|
+static int mmc_blk_add_debugfs(struct mmc_card *card)
|
|
|
+{
|
|
|
+ struct dentry *root;
|
|
|
+
|
|
|
+ if (!card->debugfs_root)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ root = card->debugfs_root;
|
|
|
+
|
|
|
+ if (mmc_card_mmc(card) || mmc_card_sd(card)) {
|
|
|
+ if (!debugfs_create_file("status", S_IRUSR, root, card,
|
|
|
+ &mmc_dbg_card_status_fops))
|
|
|
+ return -EIO;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (mmc_card_mmc(card)) {
|
|
|
+ if (!debugfs_create_file("ext_csd", S_IRUSR, root, card,
|
|
|
+ &mmc_dbg_ext_csd_fops))
|
|
|
+ return -EIO;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+#else
|
|
|
+
|
|
|
+static int mmc_blk_add_debugfs(struct mmc_card *card)
|
|
|
+{
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+#endif /* CONFIG_DEBUG_FS */
|
|
|
+
|
|
|
static int mmc_blk_probe(struct mmc_card *card)
|
|
|
{
|
|
|
struct mmc_blk_data *md, *part_md;
|
|
@@ -2319,6 +2459,9 @@ static int mmc_blk_probe(struct mmc_card *card)
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
+ /* Add two debugfs entries */
|
|
|
+ mmc_blk_add_debugfs(card);
|
|
|
+
|
|
|
pm_runtime_set_autosuspend_delay(&card->dev, 3000);
|
|
|
pm_runtime_use_autosuspend(&card->dev);
|
|
|
|