From dcc3e906a036f53a5bef89f473e5e930b29f0113 Mon Sep 17 00:00:00 2001 From: Arto Merilainen Date: Fri, 7 Nov 2014 19:47:30 +0200 Subject: [PATCH] devfreq: Rename watermark governor This patch renames watermark governor from "watermark" to "wmark_simple". This allows having more complex watermark based scaling algorithms available. Change-Id: If6a5653300f964173e6581bf256f7bf65041beb9 Signed-off-by: Arto Merilainen Reviewed-on: http://git-master/r/598799 Signed-off-by: Shridhar Rasal Reviewed-on: http://git-master/r/1160006 (cherry picked from linux-4.9 commit a47423f57343b9609341065350e399ada5d767d0) [talho: removed Kconfig and Makefile changes from patch] Signed-off-by: Timo Alho Reviewed-on: https://git-master.nvidia.com/r/1770138 Reviewed-by: Mikko Perttunen GVS: Gerrit_Virtual_Submit Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/devfreq/governor_wmark_simple.c | 202 ++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 drivers/devfreq/governor_wmark_simple.c diff --git a/drivers/devfreq/governor_wmark_simple.c b/drivers/devfreq/governor_wmark_simple.c new file mode 100644 index 00000000..ab521ff5 --- /dev/null +++ b/drivers/devfreq/governor_wmark_simple.c @@ -0,0 +1,202 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +enum watermark_type { + NO_WATERMARK_EVENT = 0, + HIGH_WATERMARK_EVENT = 1, + LOW_WATERMARK_EVENT = 2 +}; + +struct wmark_gov_info { + unsigned long *freqlist; + int freq_count; + unsigned long last_request; + + enum watermark_type event; + + struct devfreq *df; + struct platform_device *pdev; +}; + +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 long 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, 100); + + /* 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, 600); + + /* 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 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; + + 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); + if (df->profile->set_low_wmark) + df->profile->set_low_wmark(df->dev.parent, 100); + if (df->profile->set_high_wmark) + df->profile->set_high_wmark(df->dev.parent, 600); + break; + case DEVFREQ_GOV_STOP: + 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, 100); + if (df->profile->set_high_wmark) + df->profile->set_high_wmark(df->dev.parent, 600); + 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);