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
)
};