mmc: msm_sdcc: Add debugfs attribute for updating idle timeout

Recommended runtime PM idle timout should be 5sec. But to address any
PM related issues, add a debugfs entry to set this timeout less than
5sec.

Once debugfs is mounted, it will be available under path:
/sys/kernel/debug/msm_sdcc/mmc%/idle_tout

CRs-Fixed: 391067
Change-Id: I1f1c9e8cbb371cc975e71cf66fcbee43142b3bc7
Signed-off-by: Pratibhasagar V <pratibha@codeaurora.org>
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index b153b27..c30bf96 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -79,7 +79,6 @@
 #if defined(CONFIG_DEBUG_FS)
 static void msmsdcc_dbg_createhost(struct msmsdcc_host *);
 static struct dentry *debugfs_dir;
-static struct dentry *debugfs_file;
 static int  msmsdcc_dbg_init(void);
 #endif
 
@@ -4799,7 +4798,7 @@
 	struct msmsdcc_host *host = mmc_priv(mmc);
 
 	return snprintf(buf, PAGE_SIZE, "%u (Min 5 sec)\n",
-		host->idle_tout_ms / 1000);
+		host->idle_tout / 1000);
 }
 
 static ssize_t
@@ -4814,7 +4813,7 @@
 	if (!kstrtou32(buf, 0, &timeout)
 			&& (timeout > MSM_MMC_DEFAULT_IDLE_TIMEOUT / 1000)) {
 		spin_lock_irqsave(&host->lock, flags);
-		host->idle_tout_ms = timeout * 1000;
+		host->idle_tout = timeout * 1000;
 		spin_unlock_irqrestore(&host->lock, flags);
 	}
 	return count;
@@ -5942,7 +5941,7 @@
 		pm_runtime_enable(&(pdev)->dev);
 	}
 #endif
-	host->idle_tout_ms = MSM_MMC_DEFAULT_IDLE_TIMEOUT;
+	host->idle_tout = MSM_MMC_DEFAULT_IDLE_TIMEOUT;
 	setup_timer(&host->req_tout_timer, msmsdcc_req_tout_timer_hdlr,
 			(unsigned long)host);
 
@@ -6121,6 +6120,16 @@
 	return ret;
 }
 
+#ifdef CONFIG_DEBUG_FS
+static void msmsdcc_remove_debugfs(struct msmsdcc_host *host)
+{
+	debugfs_remove_recursive(host->debugfs_host_dir);
+	host->debugfs_host_dir = NULL;
+}
+#else
+static void msmsdcc_remove_debugfs(msmsdcc_host *host) {}
+#endif
+
 static int msmsdcc_remove(struct platform_device *pdev)
 {
 	struct mmc_host *mmc = mmc_get_drvdata(pdev);
@@ -6147,6 +6156,8 @@
 		device_remove_file(&pdev->dev, &host->polling);
 	device_remove_file(&pdev->dev, &host->idle_timeout);
 
+	msmsdcc_remove_debugfs(host);
+
 	del_timer_sync(&host->req_tout_timer);
 	tasklet_kill(&host->dma_tlet);
 	tasklet_kill(&host->sps.tlet);
@@ -6444,7 +6455,7 @@
 		return 0;
 
 	/* Idle timeout is not configurable for now */
-	pm_schedule_suspend(dev, host->idle_tout_ms);
+	pm_schedule_suspend(dev, host->idle_tout);
 
 	return -EAGAIN;
 }
@@ -6590,7 +6601,6 @@
 	platform_driver_unregister(&msmsdcc_driver);
 
 #if defined(CONFIG_DEBUG_FS)
-	debugfs_remove(debugfs_file);
 	debugfs_remove(debugfs_dir);
 #endif
 }
@@ -6602,59 +6612,57 @@
 MODULE_LICENSE("GPL");
 
 #if defined(CONFIG_DEBUG_FS)
-
-static int
-msmsdcc_dbg_state_open(struct inode *inode, struct file *file)
+static int msmsdcc_dbg_idle_tout_get(void *data, u64 *val)
 {
-	file->private_data = inode->i_private;
+	struct msmsdcc_host *host = data;
+
+	*val = host->idle_tout / 1000L;
 	return 0;
 }
 
-static ssize_t
-msmsdcc_dbg_state_read(struct file *file, char __user *ubuf,
-		       size_t count, loff_t *ppos)
+static int msmsdcc_dbg_idle_tout_set(void *data, u64 val)
 {
-	struct msmsdcc_host *host = (struct msmsdcc_host *) file->private_data;
-	char buf[200];
-	int max, i;
+	struct msmsdcc_host *host = data;
+	unsigned long flags;
 
-	i = 0;
-	max = sizeof(buf) - 1;
+	spin_lock_irqsave(&host->lock, flags);
+	host->idle_tout = (u32)val * 1000;
+	spin_unlock_irqrestore(&host->lock, flags);
 
-	i += scnprintf(buf + i, max - i, "STAT: %p %p %p\n", host->curr.mrq,
-		       host->curr.cmd, host->curr.data);
-	if (host->curr.cmd) {
-		struct mmc_command *cmd = host->curr.cmd;
-
-		i += scnprintf(buf + i, max - i, "CMD : %.8x %.8x %.8x\n",
-			      cmd->opcode, cmd->arg, cmd->flags);
-	}
-	if (host->curr.data) {
-		struct mmc_data *data = host->curr.data;
-		i += scnprintf(buf + i, max - i,
-			      "DAT0: %.8x %.8x %.8x %.8x %.8x %.8x\n",
-			      data->timeout_ns, data->timeout_clks,
-			      data->blksz, data->blocks, data->error,
-			      data->flags);
-		i += scnprintf(buf + i, max - i, "DAT1: %.8x %.8x %.8x %p\n",
-			      host->curr.xfer_size, host->curr.xfer_remain,
-			      host->curr.data_xfered, host->dma.sg);
-	}
-
-	return simple_read_from_buffer(ubuf, count, ppos, buf, i);
+	return 0;
 }
 
-static const struct file_operations msmsdcc_dbg_state_ops = {
-	.read	= msmsdcc_dbg_state_read,
-	.open	= msmsdcc_dbg_state_open,
-};
+DEFINE_SIMPLE_ATTRIBUTE(msmsdcc_dbg_idle_tout_ops,
+			msmsdcc_dbg_idle_tout_get,
+			msmsdcc_dbg_idle_tout_set,
+			"%llu\n");
 
 static void msmsdcc_dbg_createhost(struct msmsdcc_host *host)
 {
-	if (debugfs_dir) {
-		debugfs_file = debugfs_create_file(mmc_hostname(host->mmc),
-							0644, debugfs_dir, host,
-							&msmsdcc_dbg_state_ops);
+	int err = 0;
+
+	if (!debugfs_dir)
+		return;
+
+	host->debugfs_host_dir = debugfs_create_dir(
+			mmc_hostname(host->mmc), debugfs_dir);
+	if (IS_ERR(host->debugfs_host_dir)) {
+		err = PTR_ERR(host->debugfs_host_dir);
+		host->debugfs_host_dir = NULL;
+		pr_err("%s: Failed to create debugfs dir for host with err=%d\n",
+			mmc_hostname(host->mmc), err);
+		return;
+	}
+
+	host->debugfs_idle_tout = debugfs_create_file("idle_tout",
+		S_IRUSR | S_IWUSR, host->debugfs_host_dir, host,
+		&msmsdcc_dbg_idle_tout_ops);
+
+	if (IS_ERR(host->debugfs_idle_tout)) {
+		err = PTR_ERR(host->debugfs_idle_tout);
+		host->debugfs_idle_tout = NULL;
+		pr_err("%s: Failed to create idle_tout debugfs entry with err=%d\n",
+			mmc_hostname(host->mmc), err);
 	}
 }
 
@@ -6662,7 +6670,7 @@
 {
 	int err;
 
-	debugfs_dir = debugfs_create_dir("msmsdcc", 0);
+	debugfs_dir = debugfs_create_dir("msm_sdcc", 0);
 	if (IS_ERR(debugfs_dir)) {
 		err = PTR_ERR(debugfs_dir);
 		debugfs_dir = NULL;
diff --git a/drivers/mmc/host/msm_sdcc.h b/drivers/mmc/host/msm_sdcc.h
index 5779491..37b9423 100644
--- a/drivers/mmc/host/msm_sdcc.h
+++ b/drivers/mmc/host/msm_sdcc.h
@@ -414,7 +414,7 @@
 	bool sdio_wakeupirq_disabled;
 	struct mutex clk_mutex;
 	bool pending_resume;
-	unsigned int idle_tout_ms;			/* Timeout in msecs */
+	unsigned int idle_tout;			/* Timeout in msecs */
 	bool pending_dpsm_reset;
 	struct msmsdcc_msm_bus_vote msm_bus_vote;
 	struct device_attribute	max_bus_bw;
@@ -422,6 +422,8 @@
 	struct device_attribute idle_timeout;
 	struct device_attribute auto_cmd19_attr;
 	struct device_attribute auto_cmd21_attr;
+	struct dentry *debugfs_host_dir;
+	struct dentry *debugfs_idle_tout;
 };
 
 #define MSMSDCC_VERSION_STEP_MASK	0x0000FFFF