Merge "qup_i2c: Add runtime PM support to I2C QUP controller"
diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index 085b632..92d162b 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -164,7 +164,6 @@
 	struct msm_i2c_platform_data *pdata;
 	int                          suspended;
 	int                          clk_state;
-	struct timer_list            pwr_timer;
 	struct mutex                 mlock;
 	void                         *complete;
 	int                          i2c_gpios[ARRAY_SIZE(i2c_rsrcs)];
@@ -197,6 +196,9 @@
 	uint32_t op_flgs = readl_relaxed(dev->base + QUP_OPERATIONAL);
 	int err = 0;
 
+	if (pm_runtime_suspended(dev->dev))
+		return IRQ_NONE;
+
 	if (!dev->msg || !dev->complete) {
 		/* Clear Error interrupt if it's a level triggered interrupt*/
 		if (dev->num_irqs == 1) {
@@ -327,27 +329,18 @@
 {
 	dev->clk_state = state;
 	if (state != 0) {
-		clk_enable(dev->clk);
+		clk_prepare_enable(dev->clk);
 		if (!dev->pdata->keep_ahb_clk_on)
-			clk_enable(dev->pclk);
+			clk_prepare_enable(dev->pclk);
 	} else {
 		qup_update_state(dev, QUP_RESET_STATE);
-		clk_disable(dev->clk);
+		clk_disable_unprepare(dev->clk);
 		qup_config_core_on_en(dev);
 		if (!dev->pdata->keep_ahb_clk_on)
-			clk_disable(dev->pclk);
+			clk_disable_unprepare(dev->pclk);
 	}
 }
 
-static void
-qup_i2c_pwr_timer(unsigned long data)
-{
-	struct qup_i2c_dev *dev = (struct qup_i2c_dev *) data;
-	dev_dbg(dev->dev, "QUP_Power: Inactivity based power management\n");
-	if (dev->clk_state == 1)
-		qup_i2c_pwr_mgmt(dev, 0);
-}
-
 static int
 qup_i2c_poll_writeready(struct qup_i2c_dev *dev, int rem)
 {
@@ -756,7 +749,7 @@
 	long timeout;
 	int err;
 
-	del_timer_sync(&dev->pwr_timer);
+	pm_runtime_get_sync(dev->dev);
 	mutex_lock(&dev->mlock);
 
 	if (dev->suspended) {
@@ -764,9 +757,6 @@
 		return -EIO;
 	}
 
-	if (dev->clk_state == 0)
-		qup_i2c_pwr_mgmt(dev, 1);
-
 	/* Initialize QUP registers during first transfer */
 	if (dev->clk_ctl == 0) {
 		int fs_div;
@@ -1068,9 +1058,9 @@
 	dev->pos = 0;
 	dev->err = 0;
 	dev->cnt = 0;
-	dev->pwr_timer.expires = jiffies + 3*HZ;
-	add_timer(&dev->pwr_timer);
 	mutex_unlock(&dev->mlock);
+	pm_runtime_mark_last_busy(dev->dev);
+	pm_runtime_put_autosuspend(dev->dev);
 	return ret;
 }
 
@@ -1322,21 +1312,14 @@
 	if (pdata->msm_i2c_config_gpio)
 		pdata->msm_i2c_config_gpio(dev->adapter.nr, 1);
 
-	dev->suspended = 0;
 	mutex_init(&dev->mlock);
 	dev->clk_state = 0;
-	clk_prepare(dev->clk);
-	clk_prepare(dev->pclk);
 	/* If the same AHB clock is used on Modem side
 	 * switch it on here itself and don't switch it
 	 * on and off during suspend and resume.
 	 */
 	if (dev->pdata->keep_ahb_clk_on)
-		clk_enable(dev->pclk);
-	setup_timer(&dev->pwr_timer, qup_i2c_pwr_timer, (unsigned long) dev);
-
-	pm_runtime_set_active(&pdev->dev);
-	pm_runtime_enable(&pdev->dev);
+		clk_prepare_enable(dev->pclk);
 
 	ret = i2c_add_numbered_adapter(&dev->adapter);
 	if (ret) {
@@ -1351,6 +1334,10 @@
 			dev->adapter.dev.of_node = pdev->dev.of_node;
 			of_i2c_register_devices(&dev->adapter);
 		}
+
+		pm_runtime_set_autosuspend_delay(&pdev->dev, MSEC_PER_SEC);
+		pm_runtime_use_autosuspend(&pdev->dev);
+		pm_runtime_enable(&pdev->dev);
 		return 0;
 	}
 
@@ -1393,7 +1380,6 @@
 	dev->suspended = 1;
 	mutex_unlock(&dev->mlock);
 	mutex_destroy(&dev->mlock);
-	del_timer_sync(&dev->pwr_timer);
 	if (dev->clk_state != 0)
 		qup_i2c_pwr_mgmt(dev, 0);
 	platform_set_drvdata(pdev, NULL);
@@ -1403,9 +1389,7 @@
 	}
 	free_irq(dev->err_irq, dev);
 	i2c_del_adapter(&dev->adapter);
-	clk_unprepare(dev->clk);
 	if (!dev->pdata->keep_ahb_clk_on) {
-		clk_unprepare(dev->pclk);
 		clk_put(dev->pclk);
 	}
 	clk_put(dev->clk);
@@ -1415,6 +1399,7 @@
 	iounmap(dev->base);
 
 	pm_runtime_disable(&pdev->dev);
+	pm_runtime_set_suspended(&pdev->dev);
 
 	if (!(dev->pdata->use_gsbi_shared_mode)) {
 		gsbi_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
@@ -1431,67 +1416,63 @@
 }
 
 #ifdef CONFIG_PM
-static int qup_i2c_suspend(struct device *device)
+static int i2c_qup_pm_suspend_runtime(struct device *device)
 {
 	struct platform_device *pdev = to_platform_device(device);
 	struct qup_i2c_dev *dev = platform_get_drvdata(pdev);
-
+	dev_dbg(device, "pm_runtime: suspending...\n");
 	/* Grab mutex to ensure ongoing transaction is over */
 	mutex_lock(&dev->mlock);
 	dev->suspended = 1;
 	mutex_unlock(&dev->mlock);
-	del_timer_sync(&dev->pwr_timer);
 	if (dev->clk_state != 0)
 		qup_i2c_pwr_mgmt(dev, 0);
-	clk_unprepare(dev->clk);
-	if (!dev->pdata->keep_ahb_clk_on)
-		clk_unprepare(dev->pclk);
 	qup_i2c_free_gpios(dev);
 	return 0;
 }
 
+static int i2c_qup_pm_resume_runtime(struct device *device)
+{
+	struct platform_device *pdev = to_platform_device(device);
+	struct qup_i2c_dev *dev = platform_get_drvdata(pdev);
+	dev_dbg(device, "pm_runtime: resuming...\n");
+	BUG_ON(qup_i2c_request_gpios(dev) != 0);
+	if (dev->clk_state == 0)
+		qup_i2c_pwr_mgmt(dev, 1);
+	dev->suspended = 0;
+	return 0;
+}
+
+static int qup_i2c_suspend(struct device *device)
+{
+	if (!pm_runtime_enabled(device) || !pm_runtime_suspended(device)) {
+		dev_dbg(device, "system suspend");
+		i2c_qup_pm_suspend_runtime(device);
+	}
+	return 0;
+}
+
 static int qup_i2c_resume(struct device *device)
 {
-	struct platform_device *pdev = to_platform_device(device);
-	struct qup_i2c_dev *dev = platform_get_drvdata(pdev);
-	BUG_ON(qup_i2c_request_gpios(dev) != 0);
-	clk_prepare(dev->clk);
-	if (!dev->pdata->keep_ahb_clk_on)
-		clk_prepare(dev->pclk);
-	dev->suspended = 0;
+	if (!pm_runtime_enabled(device) || !pm_runtime_suspended(device)) {
+		dev_dbg(device, "system resume");
+		i2c_qup_pm_resume_runtime(device);
+		pm_runtime_mark_last_busy(device);
+		pm_request_autosuspend(device);
+	}
 	return 0;
 }
 #endif /* CONFIG_PM */
 
-#ifdef CONFIG_PM_RUNTIME
-static int i2c_qup_runtime_idle(struct device *dev)
-{
-	dev_dbg(dev, "pm_runtime: idle...\n");
-	return 0;
-}
-
-static int i2c_qup_runtime_suspend(struct device *dev)
-{
-	dev_dbg(dev, "pm_runtime: suspending...\n");
-	return 0;
-}
-
-static int i2c_qup_runtime_resume(struct device *dev)
-{
-	dev_dbg(dev, "pm_runtime: resuming...\n");
-	return 0;
-}
-#endif
-
 static const struct dev_pm_ops i2c_qup_dev_pm_ops = {
 	SET_SYSTEM_SLEEP_PM_OPS(
 		qup_i2c_suspend,
 		qup_i2c_resume
 	)
 	SET_RUNTIME_PM_OPS(
-		i2c_qup_runtime_suspend,
-		i2c_qup_runtime_resume,
-		i2c_qup_runtime_idle
+		i2c_qup_pm_suspend_runtime,
+		i2c_qup_pm_resume_runtime,
+		NULL
 	)
 };