aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 */