diff options
| -rw-r--r-- | drivers/devfreq/bimc-bwmon.c | 29 | ||||
| -rw-r--r-- | drivers/devfreq/devfreq_devbw.c | 22 | ||||
| -rw-r--r-- | drivers/devfreq/governor_bw_hwmon.c | 161 | ||||
| -rw-r--r-- | drivers/devfreq/governor_bw_hwmon.h | 2 | ||||
| -rw-r--r-- | include/soc/qcom/devfreq_devbw.h | 10 |
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 */ |
