diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index e54523b0043c..3bea99043b92 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -89,6 +89,15 @@ config MSM_BIMC_BWMON has the capability to raise an IRQ when the count exceeds a programmable limit. +config DEVFREQ_GOV_QCOM_GPUBW_MON + tristate "GPU BW voting governor" + depends on DEVFREQ_GOV_QCOM_ADRENO_TZ + help + QTI GPU governor for GPU bus bandwidth voting. + This governor works together with QTI Adreno Trustzone governor, + and select bus frequency votes using an "on-demand" alorithm. + This governor is unlikely to be useful for non-QTI devices. + config ARMBW_HWMON tristate "ARM PMU Bandwidth monitor hardware" depends on ARCH_QCOM diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index 5adb9492c5e1..73992be83c23 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -14,6 +14,8 @@ obj-$(CONFIG_MSM_M4M_HWMON) += m4m-hwmon.o obj-$(CONFIG_DEVFREQ_GOV_MSM_BW_HWMON) += governor_bw_hwmon.o obj-$(CONFIG_DEVFREQ_GOV_MSM_CACHE_HWMON) += governor_cache_hwmon.o obj-$(CONFIG_DEVFREQ_GOV_SPDM_HYP) += governor_spdm_bw_hyp.o +obj-$(CONFIG_DEVFREQ_GOV_QCOM_GPUBW_MON) += governor_gpubw_mon.o +obj-$(CONFIG_DEVFREQ_GOV_QCOM_GPUBW_MON) += governor_bw_vbif.o obj-$(CONFIG_DEVFREQ_GOV_MEMLAT) += governor_memlat.o # DEVFREQ Drivers diff --git a/drivers/devfreq/devfreq_trace.h b/drivers/devfreq/devfreq_trace.h new file mode 100644 index 000000000000..865698336cc4 --- /dev/null +++ b/drivers/devfreq/devfreq_trace.h @@ -0,0 +1,44 @@ +/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that 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. + * + */ + +#if !defined(_DEVFREQ_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _DEVFREQ_TRACE_H + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM devfreq +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE devfreq_trace + +#include + +TRACE_EVENT(devfreq_msg, + TP_PROTO(const char *msg), + TP_ARGS(msg), + TP_STRUCT__entry( + __string(msg, msg) + ), + TP_fast_assign( + __assign_str(msg, msg); + ), + TP_printk( + "%s", __get_str(msg) + ) +); + +#endif /* _DEVFREQ_TRACE_H */ + +/* This part must be outside protection */ +#include + diff --git a/drivers/devfreq/governor_bw_vbif.c b/drivers/devfreq/governor_bw_vbif.c new file mode 100644 index 000000000000..da1eefb8c94e --- /dev/null +++ b/drivers/devfreq/governor_bw_vbif.c @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that 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. + */ + +#include +#include +#include "governor.h" + +unsigned long (*extern_get_bw)(void) = NULL; +unsigned long *dev_ab; +static unsigned long dev_ib; + +DEFINE_MUTEX(df_lock); +static struct devfreq *df; + +/* + * This function is 'get_target_freq' API for the governor. + * It just calls an external function that should be registered + * by KGSL driver to get and return a value for frequency. + */ +static int devfreq_vbif_get_freq(struct devfreq *df, + unsigned long *freq, + u32 *flag) +{ + /* If the IB isn't set yet, check if it should be non-zero. */ + if (!dev_ib && extern_get_bw) { + dev_ib = extern_get_bw(); + if (dev_ab) + *dev_ab = dev_ib / 4; + } + + *freq = dev_ib; + return 0; +} + +/* + * Registers a function to be used to request a frequency + * value from legacy vbif based bus bandwidth governor. + * This function is called by KGSL driver. + */ +void devfreq_vbif_register_callback(void *p) +{ + extern_get_bw = p; +} + +int devfreq_vbif_update_bw(unsigned long ib, unsigned long ab) +{ + int ret = 0; + + mutex_lock(&df_lock); + if (df) { + mutex_lock(&df->lock); + dev_ib = ib; + *dev_ab = ab; + ret = update_devfreq(df); + mutex_unlock(&df->lock); + } + mutex_unlock(&df_lock); + return ret; +} + +static int devfreq_vbif_ev_handler(struct devfreq *devfreq, + unsigned int event, void *data) +{ + int ret; + struct devfreq_dev_status stat; + + switch (event) { + case DEVFREQ_GOV_START: + mutex_lock(&df_lock); + df = devfreq; + if (df->profile->get_dev_status) + ret = df->profile->get_dev_status(df->dev.parent, + &stat); + else + ret = 0; + if (ret || !stat.private_data) + pr_warn("Device doesn't take AB votes!\n"); + else + dev_ab = stat.private_data; + mutex_unlock(&df_lock); + + ret = devfreq_vbif_update_bw(0, 0); + if (ret) { + pr_err("Unable to update BW! Gov start failed!\n"); + return ret; + } + /* + * Normally at this point governors start the polling with + * devfreq_monitor_start(df); + * This governor doesn't poll, but expect external calls + * of its devfreq_vbif_update_bw() function + */ + pr_debug("Enabled MSM VBIF governor\n"); + break; + + case DEVFREQ_GOV_STOP: + mutex_lock(&df_lock); + df = NULL; + mutex_unlock(&df_lock); + + pr_debug("Disabled MSM VBIF governor\n"); + break; + } + + return 0; +} + +static struct devfreq_governor devfreq_vbif = { + .name = "bw_vbif", + .get_target_freq = devfreq_vbif_get_freq, + .event_handler = devfreq_vbif_ev_handler, +}; + +static int __init devfreq_vbif_init(void) +{ + return devfreq_add_governor(&devfreq_vbif); +} +subsys_initcall(devfreq_vbif_init); + +static void __exit devfreq_vbif_exit(void) +{ + int ret; + + ret = devfreq_remove_governor(&devfreq_vbif); + if (ret) + pr_err("%s: failed remove governor %d\n", __func__, ret); + +} +module_exit(devfreq_vbif_exit); + +MODULE_DESCRIPTION("VBIF based GPU bus BW voting governor"); +MODULE_LICENSE("GPL v2"); + + diff --git a/drivers/devfreq/governor_gpubw_mon.c b/drivers/devfreq/governor_gpubw_mon.c new file mode 100644 index 000000000000..8234d30dc644 --- /dev/null +++ b/drivers/devfreq/governor_gpubw_mon.c @@ -0,0 +1,255 @@ +/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that 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. + * + */ + +#include +#include +#include +#include + +#include "devfreq_trace.h" +#include "governor.h" + +#define MIN_BUSY 1000 +#define LONG_FLOOR 50000 +#define HIST 5 +#define TARGET 80 +#define CAP 75 +/* AB vote is in multiple of BW_STEP Mega bytes */ +#define BW_STEP 160 + +static void _update_cutoff(struct devfreq_msm_adreno_tz_data *priv, + unsigned int norm_max) +{ + int i; + + priv->bus.max = norm_max; + for (i = 0; i < priv->bus.num; i++) { + priv->bus.up[i] = priv->bus.p_up[i] * norm_max / 100; + priv->bus.down[i] = priv->bus.p_down[i] * norm_max / 100; + } +} + +static int devfreq_gpubw_get_target(struct devfreq *df, + unsigned long *freq, + u32 *flag) +{ + + struct devfreq_msm_adreno_tz_data *priv = df->data; + struct msm_busmon_extended_profile *bus_profile = container_of( + (df->profile), + struct msm_busmon_extended_profile, + profile); + struct devfreq_dev_status stats; + struct xstats b; + int result; + int level = 0; + int act_level; + int norm_cycles; + int gpu_percent; + /* + * Normalized AB should at max usage be the gpu_bimc frequency in MHz. + * Start with a reasonable value and let the system push it up to max. + */ + static int norm_ab_max = 300; + int norm_ab; + unsigned long ab_mbytes = 0; + + if (priv == NULL) + return 0; + + stats.private_data = &b; + + result = df->profile->get_dev_status(df->dev.parent, &stats); + + *freq = stats.current_frequency; + + priv->bus.total_time += stats.total_time; + priv->bus.gpu_time += stats.busy_time; + priv->bus.ram_time += b.ram_time; + priv->bus.ram_wait += b.ram_wait; + + level = devfreq_get_freq_level(df, stats.current_frequency); + + if (priv->bus.total_time < LONG_FLOOR) + return result; + + norm_cycles = (unsigned int)(priv->bus.ram_time + priv->bus.ram_wait) / + (unsigned int) priv->bus.total_time; + gpu_percent = (100 * (unsigned int)priv->bus.gpu_time) / + (unsigned int) priv->bus.total_time; + + /* + * If there's a new high watermark, update the cutoffs and send the + * FAST hint. Otherwise check the current value against the current + * cutoffs. + */ + if (norm_cycles > priv->bus.max) { + _update_cutoff(priv, norm_cycles); + bus_profile->flag = DEVFREQ_FLAG_FAST_HINT; + } else { + /* GPU votes for IB not AB so don't under vote the system */ + norm_cycles = (100 * norm_cycles) / TARGET; + act_level = priv->bus.index[level] + b.mod; + act_level = (act_level < 0) ? 0 : act_level; + act_level = (act_level >= priv->bus.num) ? + (priv->bus.num - 1) : act_level; + if (norm_cycles > priv->bus.up[act_level] && + gpu_percent > CAP) + bus_profile->flag = DEVFREQ_FLAG_FAST_HINT; + else if (norm_cycles < priv->bus.down[act_level] && level) + bus_profile->flag = DEVFREQ_FLAG_SLOW_HINT; + } + + /* Calculate the AB vote based on bus width if defined */ + if (priv->bus.width) { + norm_ab = (unsigned int)priv->bus.ram_time / + (unsigned int) priv->bus.total_time; + /* Calculate AB in Mega Bytes and roundup in BW_STEP */ + ab_mbytes = (norm_ab * priv->bus.width * 1000000ULL) >> 20; + bus_profile->ab_mbytes = roundup(ab_mbytes, BW_STEP); + } else if (bus_profile->flag) { + /* Re-calculate the AB percentage for a new IB vote */ + norm_ab = (unsigned int)priv->bus.ram_time / + (unsigned int) priv->bus.total_time; + if (norm_ab > norm_ab_max) + norm_ab_max = norm_ab; + bus_profile->percent_ab = (100 * norm_ab) / norm_ab_max; + } + + priv->bus.total_time = 0; + priv->bus.gpu_time = 0; + priv->bus.ram_time = 0; + priv->bus.ram_wait = 0; + + return result; +} + +static int gpubw_start(struct devfreq *devfreq) +{ + struct devfreq_msm_adreno_tz_data *priv; + + struct msm_busmon_extended_profile *bus_profile = container_of( + (devfreq->profile), + struct msm_busmon_extended_profile, + profile); + unsigned int t1, t2 = 2 * HIST; + int i, bus_size; + + + devfreq->data = bus_profile->private_data; + priv = devfreq->data; + + bus_size = sizeof(u32) * priv->bus.num; + priv->bus.up = kzalloc(bus_size, GFP_KERNEL); + priv->bus.down = kzalloc(bus_size, GFP_KERNEL); + priv->bus.p_up = kzalloc(bus_size, GFP_KERNEL); + priv->bus.p_down = kzalloc(bus_size, GFP_KERNEL); + if (priv->bus.up == NULL || priv->bus.down == NULL || + priv->bus.p_up == NULL || priv->bus.p_down == NULL) + return -ENOMEM; + + /* Set up the cut-over percentages for the bus calculation. */ + for (i = 0; i < priv->bus.num; i++) { + t1 = (u32)(100 * priv->bus.ib[i]) / + (u32)priv->bus.ib[priv->bus.num - 1]; + priv->bus.p_up[i] = t1 - HIST; + priv->bus.p_down[i] = t2 - 2 * HIST; + t2 = t1; + } + /* Set the upper-most and lower-most bounds correctly. */ + priv->bus.p_down[0] = 0; + priv->bus.p_down[1] = (priv->bus.p_down[1] > (2 * HIST)) ? + priv->bus.p_down[1] : (2 * HIST); + if (priv->bus.num >= 1) + priv->bus.p_up[priv->bus.num - 1] = 100; + _update_cutoff(priv, priv->bus.max); + + return 0; +} + +static int gpubw_stop(struct devfreq *devfreq) +{ + struct devfreq_msm_adreno_tz_data *priv = devfreq->data; + + if (priv) { + kfree(priv->bus.up); + kfree(priv->bus.down); + kfree(priv->bus.p_up); + kfree(priv->bus.p_down); + } + devfreq->data = NULL; + return 0; +} + +static int devfreq_gpubw_event_handler(struct devfreq *devfreq, + unsigned int event, void *data) +{ + int result = 0; + unsigned long freq; + + mutex_lock(&devfreq->lock); + freq = devfreq->previous_freq; + switch (event) { + case DEVFREQ_GOV_START: + result = gpubw_start(devfreq); + break; + case DEVFREQ_GOV_STOP: + result = gpubw_stop(devfreq); + break; + case DEVFREQ_GOV_RESUME: + /* TODO ..... */ + /* ret = update_devfreq(devfreq); */ + break; + case DEVFREQ_GOV_SUSPEND: + { + struct devfreq_msm_adreno_tz_data *priv = devfreq->data; + + priv->bus.total_time = 0; + priv->bus.gpu_time = 0; + priv->bus.ram_time = 0; + } + break; + default: + result = 0; + break; + } + mutex_unlock(&devfreq->lock); + return result; +} + +static struct devfreq_governor devfreq_gpubw = { + .name = "gpubw_mon", + .get_target_freq = devfreq_gpubw_get_target, + .event_handler = devfreq_gpubw_event_handler, +}; + +static int __init devfreq_gpubw_init(void) +{ + return devfreq_add_governor(&devfreq_gpubw); +} +subsys_initcall(devfreq_gpubw_init); + +static void __exit devfreq_gpubw_exit(void) +{ + int ret; + + ret = devfreq_remove_governor(&devfreq_gpubw); + if (ret) + pr_err("%s: failed remove governor %d\n", __func__, ret); + +} +module_exit(devfreq_gpubw_exit); + +MODULE_DESCRIPTION("GPU bus bandwidth voting driver. Uses VBIF counters"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index 927841a7ad3b..08f7b29fe96a 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -116,7 +116,7 @@ static void _record_pwrevent(struct kgsl_device *device, } } -#ifdef CONFIG_DEVFREQ_GOV_MSM_GPUBW_MON +#ifdef CONFIG_DEVFREQ_GOV_QCOM_GPUBW_MON #include /** @@ -199,7 +199,7 @@ static unsigned int _adjust_pwrlevel(struct kgsl_pwrctrl *pwr, int level, return level; } -#ifdef CONFIG_DEVFREQ_GOV_MSM_GPUBW_MON +#ifdef CONFIG_DEVFREQ_GOV_QCOM_GPUBW_MON static void kgsl_pwrctrl_vbif_update(unsigned long ab) { /* ask a governor to vote on behalf of us */ @@ -1398,7 +1398,7 @@ static void kgsl_pwrctrl_clk(struct kgsl_device *device, int state, } } -#ifdef CONFIG_DEVFREQ_GOV_MSM_GPUBW_MON +#ifdef CONFIG_DEVFREQ_GOV_QCOM_GPUBW_MON static void kgsl_pwrctrl_suspend_devbw(struct kgsl_pwrctrl *pwr) { if (pwr->devbw) @@ -1596,7 +1596,7 @@ void kgsl_deep_nap_timer(unsigned long data) } } -#ifdef CONFIG_DEVFREQ_GOV_MSM_GPUBW_MON +#ifdef CONFIG_DEVFREQ_GOV_QCOM_GPUBW_MON static int kgsl_pwrctrl_vbif_init(void) { devfreq_vbif_register_callback(kgsl_get_bw); diff --git a/include/linux/msm_adreno_devfreq.h b/include/linux/msm_adreno_devfreq.h index 2d1a585e9945..19acfb4447b2 100644 --- a/include/linux/msm_adreno_devfreq.h +++ b/include/linux/msm_adreno_devfreq.h @@ -71,7 +71,7 @@ struct msm_busmon_extended_profile { struct devfreq_dev_profile profile; }; -#ifdef CONFIG_DEVFREQ_GOV_MSM_GPUBW_MON +#ifdef CONFIG_DEVFREQ_GOV_QCOM_GPUBW_MON int devfreq_vbif_update_bw(unsigned long ib, unsigned long ab); int devfreq_vbif_register_callback(void *); #endif