[RFC RESEND 2/3] PM / devfreq: Add watermark simple governor

From: Arto Merilainen
Date: Fri Dec 05 2014 - 08:42:48 EST


From: Shridhar Rasal <srasal@xxxxxxxxxx>

This patch adds a new devfreq governor, watermark simple. The
governor decides next frequency naively by selecting the previous
frequency in the frequency table if we received low watermark
- or the next frequency in case we received high watermark.

Signed-off-by: Shridhar Rasal <srasal@xxxxxxxxxx>
Signed-off-by: Arto Merilainen <amerilainen@xxxxxxxxxx>
---
drivers/devfreq/Kconfig | 7 +
drivers/devfreq/Makefile | 1 +
drivers/devfreq/governor_wmark_simple.c | 245 ++++++++++++++++++++++++++++++++
3 files changed, 253 insertions(+)
create mode 100644 drivers/devfreq/governor_wmark_simple.c

diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
index faf4e70c42e0..37710d999ffe 100644
--- a/drivers/devfreq/Kconfig
+++ b/drivers/devfreq/Kconfig
@@ -63,6 +63,13 @@ config DEVFREQ_GOV_USERSPACE
Otherwise, the governor does not change the frequnecy
given at the initialization.

+config DEVFREQ_GOV_WMARK_SIMPLE
+ tristate "Simple Watermark"
+ help
+ Sets the frequency based on monitor watermark events.
+ This governor returns the next frequency from frequency table
+ based on type watermark event.
+
comment "DEVFREQ Drivers"

config ARM_EXYNOS4_BUS_DEVFREQ
diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile
index 16138c9e0d58..92024eeb73b6 100644
--- a/drivers/devfreq/Makefile
+++ b/drivers/devfreq/Makefile
@@ -3,6 +3,7 @@ obj-$(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND) += governor_simpleondemand.o
obj-$(CONFIG_DEVFREQ_GOV_PERFORMANCE) += governor_performance.o
obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE) += governor_powersave.o
obj-$(CONFIG_DEVFREQ_GOV_USERSPACE) += governor_userspace.o
+obj-$(CONFIG_DEVFREQ_GOV_WMARK_SIMPLE) += governor_wmark_simple.o

# DEVFREQ Drivers
obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ) += exynos/
diff --git a/drivers/devfreq/governor_wmark_simple.c b/drivers/devfreq/governor_wmark_simple.c
new file mode 100644
index 000000000000..bd14adcc84cb
--- /dev/null
+++ b/drivers/devfreq/governor_wmark_simple.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/devfreq.h>
+#include <linux/debugfs.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/notifier.h>
+#include <linux/platform_device.h>
+
+#include "governor.h"
+
+struct wmark_gov_info {
+ /* probed from the devfreq */
+ unsigned int *freqlist;
+ int freq_count;
+
+ /* algorithm parameters */
+ unsigned int p_high_wmark;
+ unsigned int p_low_wmark;
+
+ /* dynamically changing data */
+ enum watermark_type event;
+ unsigned int last_request;
+
+ /* common data */
+ struct devfreq *df;
+ struct platform_device *pdev;
+ struct dentry *debugdir;
+};
+
+static unsigned long freqlist_up(struct wmark_gov_info *wmarkinfo,
+ unsigned long curr_freq)
+{
+ int i, pos;
+
+ for (i = 0; i < wmarkinfo->freq_count; i++)
+ if (wmarkinfo->freqlist[i] > curr_freq)
+ break;
+
+ pos = min(wmarkinfo->freq_count - 1, i);
+
+ return wmarkinfo->freqlist[pos];
+}
+
+static unsigned int freqlist_down(struct wmark_gov_info *wmarkinfo,
+ unsigned long curr_freq)
+{
+ int i, pos;
+
+ for (i = wmarkinfo->freq_count - 1; i >= 0; i--)
+ if (wmarkinfo->freqlist[i] < curr_freq)
+ break;
+
+ pos = max(0, i);
+ return wmarkinfo->freqlist[pos];
+}
+
+static int devfreq_watermark_target_freq(struct devfreq *df,
+ unsigned long *freq)
+{
+ struct wmark_gov_info *wmarkinfo = df->data;
+ struct devfreq_dev_status dev_stat;
+ int err;
+
+ err = df->profile->get_dev_status(df->dev.parent, &dev_stat);
+ if (err < 0)
+ return err;
+
+ switch (wmarkinfo->event) {
+ case HIGH_WATERMARK_EVENT:
+ *freq = freqlist_up(wmarkinfo, dev_stat.current_frequency);
+
+ /* always enable low watermark */
+ df->profile->set_low_wmark(df->dev.parent,
+ wmarkinfo->p_low_wmark);
+
+ /* disable high watermark if no change */
+ if (*freq == wmarkinfo->last_request)
+ df->profile->set_high_wmark(df->dev.parent, 1000);
+ break;
+ case LOW_WATERMARK_EVENT:
+ *freq = freqlist_down(wmarkinfo, dev_stat.current_frequency);
+
+ /* always enable high watermark */
+ df->profile->set_high_wmark(df->dev.parent,
+ wmarkinfo->p_high_wmark);
+
+ /* disable low watermark if no change */
+ if (*freq == wmarkinfo->last_request)
+ df->profile->set_low_wmark(df->dev.parent, 0);
+ break;
+ default:
+ break;
+ }
+
+ /* Mark that you handled event */
+ wmarkinfo->event = NO_WATERMARK_EVENT;
+ wmarkinfo->last_request = *freq;
+
+ return 0;
+}
+
+static void devfreq_watermark_debug_start(struct devfreq *df)
+{
+ struct wmark_gov_info *wmarkinfo = df->data;
+ char dirname[128];
+
+ snprintf(dirname, sizeof(dirname), "%s_scaling",
+ to_platform_device(df->dev.parent)->name);
+
+ if (!wmarkinfo)
+ return;
+
+ wmarkinfo->debugdir = debugfs_create_dir(dirname, NULL);
+ if (!wmarkinfo->debugdir)
+ return;
+
+ debugfs_create_u32("low_wmark", S_IRUGO | S_IWUSR,
+ wmarkinfo->debugdir, &wmarkinfo->p_low_wmark);
+ debugfs_create_u32("high_wmark", S_IRUGO | S_IWUSR,
+ wmarkinfo->debugdir, &wmarkinfo->p_high_wmark);
+}
+
+static void devfreq_watermark_debug_stop(struct devfreq *df)
+{
+ struct wmark_gov_info *wmarkinfo = df->data;
+
+ debugfs_remove_recursive(wmarkinfo->debugdir);
+}
+
+static int devfreq_watermark_start(struct devfreq *df)
+{
+ struct wmark_gov_info *wmarkinfo;
+ struct platform_device *pdev = to_platform_device(df->dev.parent);
+
+ if (!df->profile->freq_table) {
+ dev_err(&pdev->dev, "Frequency table missing\n");
+ return -EINVAL;
+ }
+
+ wmarkinfo = kzalloc(sizeof(struct wmark_gov_info), GFP_KERNEL);
+ if (!wmarkinfo)
+ return -ENOMEM;
+
+ df->data = (void *)wmarkinfo;
+ wmarkinfo->freqlist = df->profile->freq_table;
+ wmarkinfo->freq_count = df->profile->max_state;
+ wmarkinfo->event = NO_WATERMARK_EVENT;
+ wmarkinfo->df = df;
+ wmarkinfo->pdev = pdev;
+ wmarkinfo->p_low_wmark = 100;
+ wmarkinfo->p_high_wmark = 600;
+
+ devfreq_watermark_debug_start(df);
+
+ return 0;
+}
+
+static int devfreq_watermark_event_handler(struct devfreq *df,
+ unsigned int event,
+ void *wmark_type)
+{
+ int ret = 0;
+ struct wmark_gov_info *wmarkinfo = df->data;
+ enum watermark_type *type = wmark_type;
+
+ switch (event) {
+ case DEVFREQ_GOV_START:
+ devfreq_watermark_start(df);
+ wmarkinfo = df->data;
+ if (df->profile->set_low_wmark)
+ df->profile->set_low_wmark(df->dev.parent,
+ wmarkinfo->p_low_wmark);
+ if (df->profile->set_high_wmark)
+ df->profile->set_high_wmark(df->dev.parent,
+ wmarkinfo->p_high_wmark);
+ break;
+ case DEVFREQ_GOV_STOP:
+ devfreq_watermark_debug_stop(df);
+ break;
+ case DEVFREQ_GOV_SUSPEND:
+ devfreq_monitor_suspend(df);
+ break;
+
+ case DEVFREQ_GOV_RESUME:
+ if (df->profile->set_low_wmark)
+ df->profile->set_low_wmark(df->dev.parent,
+ wmarkinfo->p_low_wmark);
+ if (df->profile->set_high_wmark)
+ df->profile->set_high_wmark(df->dev.parent,
+ wmarkinfo->p_high_wmark);
+ devfreq_monitor_resume(df);
+ break;
+
+ case DEVFREQ_GOV_WMARK:
+ /* Set watermark interrupt type */
+ wmarkinfo->event = *type;
+
+ mutex_lock(&df->lock);
+ update_devfreq(df);
+ mutex_unlock(&df->lock);
+
+ break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static struct devfreq_governor devfreq_watermark = {
+ .name = "wmark_simple",
+ .get_target_freq = devfreq_watermark_target_freq,
+ .event_handler = devfreq_watermark_event_handler,
+};
+
+
+static int __init devfreq_watermark_init(void)
+{
+ return devfreq_add_governor(&devfreq_watermark);
+}
+
+static void __exit devfreq_watermark_exit(void)
+{
+ devfreq_remove_governor(&devfreq_watermark);
+}
+
+rootfs_initcall(devfreq_watermark_init);
+module_exit(devfreq_watermark_exit);
--
1.8.1.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/