Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 161c0fa4 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: kgsl: Add GPU bus governor from the msm-4.9 to msm-4.14" into msm-4.14

parents deee853a aee92c8a
Loading
Loading
Loading
Loading
+53 −0
Original line number Diff line number Diff line
@@ -73,6 +73,14 @@ config DEVFREQ_GOV_PASSIVE
	  through sysfs entries. The passive governor recommends that
	  devfreq device uses the OPP table to get the frequency/voltage.

config DEVFREQ_GOV_CPUFREQ
	tristate "CPUfreq"
	depends on CPU_FREQ
	help
	  Chooses frequency based on the online CPUs' current frequency and a
	  CPU frequency to device frequency mapping table(s). This governor
	  can be useful for controlling devices such as DDR, cache, CCI, etc.

config QCOM_BIMC_BWMON
	tristate "QCOM BIMC Bandwidth monitor hardware"
	depends on ARCH_QCOM
@@ -128,6 +136,15 @@ config DEVFREQ_GOV_QCOM_CACHE_HWMON
	  it can conflict with existing profiling tools. This governor is
	  unlikely to be useful for other devices.

config DEVFREQ_GOV_SPDM_HYP
	bool "QTI SPDM Hypervisor Governor"
	depends on ARCH_QCOM
	help
	  Hypervisor based governor for CPU bandwidth voting
	  for QTI chipsets.
	  Sets the frequency using a "on-demand" algorithm.
	  This governor is unlikely to be useful for other devices.

config DEVFREQ_GOV_MEMLAT
	tristate "HW monitor based governor for device BW"
	depends on ARM_MEMLAT_MON
@@ -140,6 +157,24 @@ config DEVFREQ_GOV_MEMLAT

comment "DEVFREQ Drivers"

config DEVFREQ_GOV_QCOM_ADRENO_TZ
	tristate "Qualcomm Technologies Inc Adreno Trustzone"
	depends on QCOM_KGSL && QCOM_SCM
	help
	   Trustzone based governor for the Adreno GPU. Sets
	   the frequency using a "on-demand" algorithm. This
	   governor is unlikely to be useful for other
	   devices.

config DEVFREQ_GOV_QCOM_GPUBW_MON
	tristate "GPU BW voting governor"
	depends on DEVFREQ_GOV_QCOM_ADRENO_TZ
	help
	  This governor works together with Adreno Trustzone governor,
	  and select bus frequency votes using an "on-demand" alorithm.
	  This governor is unlikely to be useful for non-Adreno
	  devices.

config ARM_EXYNOS_BUS_DEVFREQ
	tristate "ARM EXYNOS Generic Memory Bus DEVFREQ Driver"
	depends on ARCH_EXYNOS || COMPILE_TEST
@@ -202,6 +237,24 @@ config QCOM_DEVFREQ_DEVBW
	  agnostic interface to so that some of the devfreq governors can be
	  shared across SoCs.

config SPDM_SCM
	bool "QTI SPDM SCM based call support"
	depends on DEVFREQ_SPDM
	help
	  SPDM driver support the dcvs algorithm logic being accessed via
	  scm or hvc calls. This adds the support for SPDM interaction to
          tz via SCM based call. If not selected then Hypervisor interaction
          will be activated.

config DEVFREQ_SPDM
	bool "QTI SPDM based bandwidth voting"
	depends on ARCH_QCOM
	select DEVFREQ_GOV_SPDM_HYP
	help
	  This adds the support for SPDM based bandwidth voting on QTI chipsets.
	  This driver allows any SPDM based client to vote for bandwidth.
	  Used with the QTI SPDM Hypervisor Governor.

source "drivers/devfreq/event/Kconfig"

endif # PM_DEVFREQ
+6 −0
Original line number Diff line number Diff line
@@ -6,12 +6,17 @@ 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_PASSIVE)	+= governor_passive.o
#obj-$(CONFIG_DEVFREQ_GOV_CPUFREQ)	+= governor_cpufreq.o
obj-$(CONFIG_DEVFREQ_GOV_QCOM_ADRENO_TZ) += governor_msm_adreno_tz.o
obj-$(CONFIG_DEVFREQ_GOV_QCOM_GPUBW_MON) += governor_bw_vbif.o
obj-$(CONFIG_DEVFREQ_GOV_QCOM_GPUBW_MON) += governor_gpubw_mon.o
obj-$(CONFIG_QCOM_BIMC_BWMON)		+= bimc-bwmon.o
obj-$(CONFIG_ARM_MEMLAT_MON)		+= arm-memlat-mon.o
obj-$(CONFIG_QCOMCCI_HWMON)		+= msmcci-hwmon.o
obj-$(CONFIG_QCOM_M4M_HWMON)		+= m4m-hwmon.o
obj-$(CONFIG_DEVFREQ_GOV_QCOM_BW_HWMON)	+= governor_bw_hwmon.o
obj-$(CONFIG_DEVFREQ_GOV_QCOM_CACHE_HWMON)	+= governor_cache_hwmon.o
obj-$(CONFIG_DEVFREQ_GOV_SPDM_HYP)	+= governor_spdm_bw_hyp.o
obj-$(CONFIG_DEVFREQ_GOV_MEMLAT)       += governor_memlat.o

# DEVFREQ Drivers
@@ -20,6 +25,7 @@ obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o
obj-$(CONFIG_ARM_TEGRA_DEVFREQ)		+= tegra-devfreq.o
obj-$(CONFIG_QCOM_DEVFREQ_DEVBW)		+= devfreq_devbw.o
obj-$(CONFIG_DEVFREQ_SIMPLE_DEV)	+= devfreq_simple_dev.o
obj-$(CONFIG_DEVFREQ_SPDM)		+= devfreq_spdm.o devfreq_spdm_debugfs.o

# DEVFREQ Event Drivers
obj-$(CONFIG_PM_DEVFREQ_EVENT)		+= event/
+44 −0
Original line number Diff line number Diff line
/* Copyright (c) 2014-2017, 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 <linux/tracepoint.h>

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 <trace/define_trace.h>
+141 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2014-2017, 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 <linux/devfreq.h>
#include <linux/module.h>
#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)
{
	/* 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 &&
			!df->profile->get_dev_status(df->dev.parent, &stat) &&
			stat.private_data)
			dev_ab = stat.private_data;
		else
			pr_warn("Device doesn't take AB votes!\n");

		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");

+266 −0
Original line number Diff line number Diff line
/* Copyright (c) 2014-2017, 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 <linux/devfreq.h>
#include <linux/module.h>
#include <linux/msm_adreno_devfreq.h>
#include <linux/slab.h>

#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 inline int devfreq_get_freq_level(struct devfreq *devfreq,
	unsigned long freq)
{
	int lev;

	for (lev = 0; lev < devfreq->profile->max_state; lev++)
		if (freq == devfreq->profile->freq_table[lev])
			return lev;

	return -EINVAL;
}

static int devfreq_gpubw_get_target(struct devfreq *df,
				unsigned long *freq)
{

	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");
Loading