aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSaravana Kannan <skannan@codeaurora.org>2014-08-07 19:38:02 -0700
committerSteve Kondik <shade@chemlab.org>2014-10-12 01:56:55 -0700
commit3b903fc99e651cda7f7121bec2f513a142054d49 (patch)
treebdd55096892f3efb00508713d4ccd7996d65a4b3
parent242d353ab4fabcec782d16288a87539ef326949e (diff)
PM / devfreq: governor_bw_hwmon: Add suspend/resume supportkitkat
Some devfreq devices using this governor might need suspend/resume support. When suspended, those devices won't need any bandwidth votes and there is no point in monitoring their bandwidth either. Therefore, upon suspend, vote for zero bandwidth and stop the HW monitor. Upon resume, vote for the previous bandwidth and start the HW monitor. Change-Id: I318449995d714959f0ebfe91961bc23fa8edbd04 Signed-off-by: Saravana Kannan <skannan@codeaurora.org> 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>
-rw-r--r--drivers/devfreq/bimc-bwmon.c29
-rw-r--r--drivers/devfreq/devfreq_devbw.c22
-rw-r--r--drivers/devfreq/governor_bw_hwmon.c161
-rw-r--r--drivers/devfreq/governor_bw_hwmon.h2
-rw-r--r--include/soc/qcom/devfreq_devbw.h10
5 files changed, 197 insertions, 27 deletions
diff --git a/drivers/devfreq/bimc-bwmon.c b/drivers/devfreq/bimc-bwmon.c
index eda68a063a3..a10b538da9c 100644
--- a/drivers/devfreq/bimc-bwmon.c
+++ b/drivers/devfreq/bimc-bwmon.c
@@ -196,7 +196,8 @@ static int start_bw_hwmon(struct bw_hwmon *hw, unsigned long mbps)
IRQF_ONESHOT | IRQF_SHARED,
dev_name(m->dev), m);
if (ret) {
- dev_err(m->dev, "Unable to register interrupt handler!\n");
+ dev_err(m->dev, "Unable to register interrupt handler! (%d)\n",
+ ret);
return ret;
}
@@ -225,6 +226,30 @@ static void stop_bw_hwmon(struct bw_hwmon *hw)
mon_clear(m);
}
+static int suspend_bw_hwmon(struct bw_hwmon *hw)
+{
+ struct bwmon *m = to_bwmon(hw);
+
+ disable_irq(m->irq);
+ mon_disable(m);
+ mon_irq_disable(m);
+ mon_irq_clear(m);
+
+ return 0;
+}
+
+static int resume_bw_hwmon(struct bw_hwmon *hw)
+{
+ struct bwmon *m = to_bwmon(hw);
+
+ mon_clear(m);
+ mon_irq_enable(m);
+ mon_enable(m);
+ enable_irq(m->irq);
+
+ return 0;
+}
+
/*************************************************************************/
static int bimc_bwmon_driver_probe(struct platform_device *pdev)
@@ -280,6 +305,8 @@ static int bimc_bwmon_driver_probe(struct platform_device *pdev)
return -EINVAL;
m->hw.start_hwmon = &start_bw_hwmon,
m->hw.stop_hwmon = &stop_bw_hwmon,
+ m->hw.suspend_hwmon = &suspend_bw_hwmon,
+ m->hw.resume_hwmon = &resume_bw_hwmon,
m->hw.meas_bw_and_set_irq = &meas_bw_and_set_irq,
ret = register_bw_hwmon(dev, &m->hw);
diff --git a/drivers/devfreq/devfreq_devbw.c b/drivers/devfreq/devfreq_devbw.c
index 4f5d850aecc..f01b6908ebb 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);
@@ -227,6 +222,23 @@ 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/drivers/devfreq/governor_bw_hwmon.c b/drivers/devfreq/governor_bw_hwmon.c
index fe733318d16..f1ddf62ed52 100644
--- a/drivers/devfreq/governor_bw_hwmon.c
+++ b/drivers/devfreq/governor_bw_hwmon.c
@@ -39,6 +39,8 @@ struct hwmon_node {
unsigned int bw_step;
unsigned long prev_ab;
unsigned long *dev_ab;
+ unsigned long resume_freq;
+ unsigned long resume_ab;
ktime_t prev_ts;
bool mon_started;
struct list_head list;
@@ -209,10 +211,61 @@ int update_bw_hwmon(struct bw_hwmon *hwmon)
return 0;
}
-static int start_monitoring(struct devfreq *df)
+static int start_monitor(struct devfreq *df, bool init)
{
- int ret = 0;
+ struct hwmon_node *node = df->data;
+ struct bw_hwmon *hw = node->hw;
+ struct device *dev = df->dev.parent;
unsigned long mbps;
+ int ret;
+
+ node->prev_ts = ktime_get();
+
+ if (init) {
+ node->prev_ab = 0;
+ node->resume_freq = 0;
+ node->resume_ab = 0;
+ mbps = (df->previous_freq * node->io_percent) / 100;
+ ret = hw->start_hwmon(hw, mbps);
+ } else {
+ ret = hw->resume_hwmon(hw);
+ }
+
+ if (ret) {
+ dev_err(dev, "Unable to start HW monitor! (%d)\n", ret);
+ return ret;
+ }
+
+ if (init)
+ devfreq_monitor_start(df);
+ else
+ devfreq_monitor_resume(df);
+
+ node->mon_started = true;
+
+ return 0;
+}
+
+static void stop_monitor(struct devfreq *df, bool init)
+{
+ struct hwmon_node *node = df->data;
+ struct bw_hwmon *hw = node->hw;
+
+ node->mon_started = false;
+
+ if (init) {
+ devfreq_monitor_stop(df);
+ hw->stop_hwmon(hw);
+ } else {
+ devfreq_monitor_suspend(df);
+ hw->suspend_hwmon(hw);
+ }
+
+}
+
+static int gov_start(struct devfreq *df)
+{
+ int ret = 0;
struct device *dev = df->dev.parent;
struct hwmon_node *node;
struct bw_hwmon *hw;
@@ -237,17 +290,8 @@ static int start_monitoring(struct devfreq *df)
node->orig_data = df->data;
df->data = node;
- node->prev_ts = ktime_get();
- node->prev_ab = 0;
- mbps = (df->previous_freq * node->io_percent) / 100;
- ret = hw->start_hwmon(hw, mbps);
- if (ret) {
- dev_err(dev, "Unable to start HW monitor!\n");
+ if (start_monitor(df, true))
goto err_start;
- }
-
- devfreq_monitor_start(df);
- node->mon_started = true;
ret = sysfs_create_group(&df->dev.kobj, node->attr_grp);
if (ret)
@@ -256,9 +300,7 @@ static int start_monitoring(struct devfreq *df)
return 0;
err_sysfs:
- node->mon_started = false;
- devfreq_monitor_stop(df);
- hw->stop_hwmon(hw);
+ stop_monitor(df, true);
err_start:
df->data = node->orig_data;
node->orig_data = NULL;
@@ -267,15 +309,13 @@ err_start:
return ret;
}
-static void stop_monitoring(struct devfreq *df)
+static void gov_stop(struct devfreq *df)
{
struct hwmon_node *node = df->data;
struct bw_hwmon *hw = node->hw;
sysfs_remove_group(&df->dev.kobj, node->attr_grp);
- node->mon_started = false;
- devfreq_monitor_stop(df);
- hw->stop_hwmon(hw);
+ stop_monitor(df, true);
df->data = node->orig_data;
node->orig_data = NULL;
hw->df = NULL;
@@ -290,6 +330,54 @@ static void stop_monitoring(struct devfreq *df)
node->dev_ab = NULL;
}
+static int gov_suspend(struct devfreq *df)
+{
+ struct hwmon_node *node = df->data;
+ unsigned long resume_freq = df->previous_freq;
+ unsigned long resume_ab = *node->dev_ab;
+
+ if (!node->hw->suspend_hwmon)
+ return -ENOSYS;
+
+ if (node->resume_freq) {
+ dev_warn(df->dev.parent, "Governor already suspended!\n");
+ return -EBUSY;
+ }
+
+ stop_monitor(df, false);
+
+ mutex_lock(&df->lock);
+ update_devfreq(df);
+ mutex_unlock(&df->lock);
+
+ node->resume_freq = resume_freq;
+ node->resume_ab = resume_ab;
+
+ return 0;
+}
+
+static int gov_resume(struct devfreq *df)
+{
+ struct hwmon_node *node = df->data;
+
+ if (!node->hw->resume_hwmon)
+ return -ENOSYS;
+
+ if (!node->resume_freq) {
+ dev_warn(df->dev.parent, "Governor already resumed!\n");
+ return -EBUSY;
+ }
+
+ mutex_lock(&df->lock);
+ update_devfreq(df);
+ mutex_unlock(&df->lock);
+
+ node->resume_freq = 0;
+ node->resume_ab = 0;
+
+ return start_monitor(df, false);
+}
+
static int devfreq_bw_hwmon_get_freq(struct devfreq *df,
unsigned long *freq,
u32 *flag)
@@ -297,6 +385,13 @@ static int devfreq_bw_hwmon_get_freq(struct devfreq *df,
unsigned long mbps;
struct hwmon_node *node = df->data;
+ /* Suspend/resume sequence */
+ if (!node->mon_started) {
+ *freq = node->resume_freq;
+ *node->dev_ab = node->resume_ab;
+ return 0;
+ }
+
mbps = measure_bw_and_set_irq(node);
compute_bw(node, mbps, freq, node->dev_ab);
@@ -336,7 +431,7 @@ static int devfreq_bw_hwmon_ev_handler(struct devfreq *df,
sample_ms = min(MAX_MS, sample_ms);
df->profile->polling_ms = sample_ms;
- ret = start_monitoring(df);
+ ret = gov_start(df);
if (ret)
return ret;
@@ -345,7 +440,7 @@ static int devfreq_bw_hwmon_ev_handler(struct devfreq *df,
break;
case DEVFREQ_GOV_STOP:
- stop_monitoring(df);
+ gov_stop(df);
dev_dbg(df->dev.parent,
"Disabled dev BW HW monitor governor\n");
break;
@@ -356,6 +451,30 @@ static int devfreq_bw_hwmon_ev_handler(struct devfreq *df,
sample_ms = min(MAX_MS, sample_ms);
devfreq_interval_update(df, &sample_ms);
break;
+
+ case DEVFREQ_GOV_SUSPEND:
+ ret = gov_suspend(df);
+ if (ret) {
+ dev_err(df->dev.parent,
+ "Unable to suspend BW HW mon governor (%d)\n",
+ ret);
+ return ret;
+ }
+
+ dev_dbg(df->dev.parent, "Suspended BW HW mon governor\n");
+ break;
+
+ case DEVFREQ_GOV_RESUME:
+ ret = gov_resume(df);
+ if (ret) {
+ dev_err(df->dev.parent,
+ "Unable to resume BW HW mon governor (%d)\n",
+ ret);
+ return ret;
+ }
+
+ dev_dbg(df->dev.parent, "Resumed BW HW mon governor\n");
+ break;
}
return 0;
diff --git a/drivers/devfreq/governor_bw_hwmon.h b/drivers/devfreq/governor_bw_hwmon.h
index 3a4c766b907..30de542444c 100644
--- a/drivers/devfreq/governor_bw_hwmon.h
+++ b/drivers/devfreq/governor_bw_hwmon.h
@@ -47,6 +47,8 @@
struct bw_hwmon {
int (*start_hwmon)(struct bw_hwmon *hw, unsigned long mbps);
void (*stop_hwmon)(struct bw_hwmon *hw);
+ int (*suspend_hwmon)(struct bw_hwmon *hw);
+ int (*resume_hwmon)(struct bw_hwmon *hw);
unsigned long (*meas_bw_and_set_irq)(struct bw_hwmon *hw,
unsigned int tol, unsigned int us);
struct device *dev;
diff --git a/include/soc/qcom/devfreq_devbw.h b/include/soc/qcom/devfreq_devbw.h
index 77f816cddf6..7edb2abba48 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 */