PM / devfreq: devbw: Add suspend/resume APIs

Absence of traffic is guaranteed when the device sitting behind a devbw
device is suspended. In such cases, it is a waste of power to make non-zero
bandwidth votes or to scale the devbw device. So, provide APIs to
suspend/resume the devbw device as needed.

Change-Id: Id58072aec7a9710eb917f248d9b9bd08d3a1ec6a
Signed-off-by: Saravana Kannan <skannan@codeaurora.org>
diff --git a/drivers/devfreq/bimc-bwmon.c b/drivers/devfreq/bimc-bwmon.c
index 726a3f7..e49ff92 100644
--- a/drivers/devfreq/bimc-bwmon.c
+++ b/drivers/devfreq/bimc-bwmon.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2015, 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
@@ -237,7 +237,6 @@ static void stop_bw_hwmon(struct bw_hwmon *hw)
 {
 	struct bwmon *m = to_bwmon(hw);
 
-	disable_irq(m->irq);
 	free_irq(m->irq, m);
 	mon_disable(m);
 	mon_irq_disable(m);
@@ -249,7 +248,7 @@ static int suspend_bw_hwmon(struct bw_hwmon *hw)
 {
 	struct bwmon *m = to_bwmon(hw);
 
-	disable_irq(m->irq);
+	free_irq(m->irq, m);
 	mon_disable(m);
 	mon_irq_disable(m);
 	mon_irq_clear(m);
@@ -260,11 +259,19 @@ static int suspend_bw_hwmon(struct bw_hwmon *hw)
 static int resume_bw_hwmon(struct bw_hwmon *hw)
 {
 	struct bwmon *m = to_bwmon(hw);
+	int ret;
 
 	mon_clear(m);
 	mon_irq_enable(m);
 	mon_enable(m);
-	enable_irq(m->irq);
+	ret = request_threaded_irq(m->irq, NULL, bwmon_intr_handler,
+				  IRQF_ONESHOT | IRQF_SHARED,
+				  dev_name(m->dev), m);
+	if (ret) {
+		dev_err(m->dev, "Unable to register interrupt handler! (%d)\n",
+				ret);
+		return ret;
+	}
 
 	return 0;
 }
diff --git a/drivers/devfreq/devfreq_devbw.c b/drivers/devfreq/devfreq_devbw.c
index f6c660a..5c7959c 100644
--- a/drivers/devfreq/devfreq_devbw.c
+++ b/drivers/devfreq/devfreq_devbw.c
@@ -214,11 +214,6 @@ int devfreq_add_devbw(struct device *dev)
 	return 0;
 }
 
-static int devfreq_devbw_probe(struct platform_device *pdev)
-{
-	return devfreq_add_devbw(&pdev->dev);
-}
-
 int devfreq_remove_devbw(struct device *dev)
 {
 	struct dev_data *d = dev_get_drvdata(dev);
@@ -228,6 +223,25 @@ int devfreq_remove_devbw(struct device *dev)
 	return 0;
 }
 
+int devfreq_suspend_devbw(struct device *dev)
+{
+	struct dev_data *d = dev_get_drvdata(dev);
+
+	return devfreq_suspend_device(d->df);
+}
+
+int devfreq_resume_devbw(struct device *dev)
+{
+	struct dev_data *d = dev_get_drvdata(dev);
+
+	return devfreq_resume_device(d->df);
+}
+
+static int devfreq_devbw_probe(struct platform_device *pdev)
+{
+	return devfreq_add_devbw(&pdev->dev);
+}
+
 static int devfreq_devbw_remove(struct platform_device *pdev)
 {
 	return devfreq_remove_devbw(&pdev->dev);
diff --git a/include/soc/qcom/devfreq_devbw.h b/include/soc/qcom/devfreq_devbw.h
index 77f816c..7edb2ab 100644
--- a/include/soc/qcom/devfreq_devbw.h
+++ b/include/soc/qcom/devfreq_devbw.h
@@ -19,6 +19,8 @@
 #ifdef CONFIG_MSM_DEVFREQ_DEVBW
 int devfreq_add_devbw(struct device *dev);
 int devfreq_remove_devbw(struct device *dev);
+int devfreq_suspend_devbw(struct device *dev);
+int devfreq_resume_devbw(struct device *dev);
 #else
 static inline int devfreq_add_devbw(struct device *dev)
 {
@@ -28,6 +30,14 @@ static inline int devfreq_remove_devbw(struct device *dev)
 {
 	return 0;
 }
+static inline int devfreq_suspend_devbw(struct device *dev)
+{
+	return 0;
+}
+static inline int devfreq_resume_devbw(struct device *dev)
+{
+	return 0;
+}
 #endif
 
 #endif /* _DEVFREQ_DEVBW_H */