summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSidath Senanayake <sidaths@google.com>2020-08-28 18:10:52 +0100
committerSidath Senanayake <sidaths@google.com>2020-09-02 21:01:38 +0100
commitb72a786a61dd97ca1733d2cb4091fe7fa41f7633 (patch)
treefcd10ced767e57a09be2d9657f39744bf97bee7f
parent01278baa0e6506d197c44bf09d2032f24dd9a1e4 (diff)
downloadgpu-b72a786a61dd97ca1733d2cb4091fe7fa41f7633.tar.gz
mali_kbase: platform: Add GPU TMU event support
Adds support for providing frequency and voltage data to GPU cooling as well as handling thermal throttling events. Bug: 156057140 Bug: 158091247 Signed-off-by: Sidath Senanayake <sidaths@google.com> Change-Id: If4a1eb2170072a24d86eea0e31731db9c4cfe72d
-rw-r--r--mali_kbase/Kbuild3
-rw-r--r--mali_kbase/Makefile1
-rw-r--r--mali_kbase/platform/pixel/Kbuild3
-rw-r--r--mali_kbase/platform/pixel/Kconfig9
-rw-r--r--mali_kbase/platform/pixel/mali_kbase_config_platform.h3
-rw-r--r--mali_kbase/platform/pixel/pixel_gpu_dvfs.c31
-rw-r--r--mali_kbase/platform/pixel/pixel_gpu_dvfs.h8
-rw-r--r--mali_kbase/platform/pixel/pixel_gpu_dvfs_governor.c26
-rw-r--r--mali_kbase/platform/pixel/pixel_gpu_sysfs.c16
-rw-r--r--mali_kbase/platform/pixel/pixel_gpu_tmu.c246
10 files changed, 339 insertions, 7 deletions
diff --git a/mali_kbase/Kbuild b/mali_kbase/Kbuild
index d85f71c..c5014ca 100644
--- a/mali_kbase/Kbuild
+++ b/mali_kbase/Kbuild
@@ -62,7 +62,8 @@ DEFINES = \
DEFINES += \
-DCONFIG_MALI_MIDGARD_DVFS=y \
-DCONFIG_MALI_PIXEL_GPU_QOS=y \
- -DCONFIG_MALI_PIXEL_GPU_BTS=y
+ -DCONFIG_MALI_PIXEL_GPU_BTS=y \
+ -DCONFIG_MALI_PIXEL_GPU_THERMAL=y
ifeq ($(KBUILD_EXTMOD),)
# in-tree
diff --git a/mali_kbase/Makefile b/mali_kbase/Makefile
index 31ceb51..a543973 100644
--- a/mali_kbase/Makefile
+++ b/mali_kbase/Makefile
@@ -36,6 +36,7 @@ KBUILD_OPTIONS += CONFIG_MALI_PLATFORM_NAME="pixel"
KBUILD_OPTIONS += CONFIG_MALI_MIDGARD_DVFS=y
KBUILD_OPTIONS += CONFIG_MALI_PIXEL_GPU_QOS=y
KBUILD_OPTIONS += CONFIG_MALI_PIXEL_GPU_QOS=y
+KBUILD_OPTIONS += CONFIG_MALI_PIXEL_GPU_THERMAL=y
KBUILD_OPTIONS += $(KBUILD_EXTRA) # Extra config if any
diff --git a/mali_kbase/platform/pixel/Kbuild b/mali_kbase/platform/pixel/Kbuild
index 866ce1b..84e1b73 100644
--- a/mali_kbase/platform/pixel/Kbuild
+++ b/mali_kbase/platform/pixel/Kbuild
@@ -30,3 +30,6 @@ mali_kbase-$(CONFIG_MALI_MIDGARD_DVFS) += \
mali_kbase-$(CONFIG_MALI_PIXEL_GPU_QOS) += \
$(MALI_PLATFORM_DIR)/pixel_gpu_dvfs_qos.o
+
+mali_kbase-$(CONFIG_MALI_PIXEL_GPU_THERMAL) += \
+ $(MALI_PLATFORM_DIR)/pixel_gpu_tmu.o
diff --git a/mali_kbase/platform/pixel/Kconfig b/mali_kbase/platform/pixel/Kconfig
index 5f9048a..3c5fd8a 100644
--- a/mali_kbase/platform/pixel/Kconfig
+++ b/mali_kbase/platform/pixel/Kconfig
@@ -26,3 +26,12 @@ config MALI_PIXEL_GPU_BTS
this feature is specified on the GPU device tree entry and
allows for setting the BTS scenario to use, and the GPU OPP
at which it is triggered.
+
+config MALI_PIXEL_GPU_THERMAL
+ bool "Enable support for constraining GPU clocks due to thermal events"
+ depends on MALI_MIDGARD_DVFS && GPU_THERMAL && EXYNOS_THERMAL_V2
+ default y
+ help
+ Sets up the GPU driver to register for thermal events that
+ require it to clock down the GPU to remain within thermal
+ bounds.
diff --git a/mali_kbase/platform/pixel/mali_kbase_config_platform.h b/mali_kbase/platform/pixel/mali_kbase_config_platform.h
index 687ad1b..8563f1b 100644
--- a/mali_kbase/platform/pixel/mali_kbase_config_platform.h
+++ b/mali_kbase/platform/pixel/mali_kbase_config_platform.h
@@ -246,6 +246,9 @@ struct pixel_context {
int level_min;
int level_scaling_max;
int level_scaling_min;
+#ifdef CONFIG_MALI_PIXEL_GPU_THERMAL
+ int level_tmu_max;
+#endif /* CONFIG_MALI_PIXEL_GPU_THERMAL */
unsigned int clock_down_hysteresis;
unsigned int clock_down_delay;
diff --git a/mali_kbase/platform/pixel/pixel_gpu_dvfs.c b/mali_kbase/platform/pixel/pixel_gpu_dvfs.c
index a54d6cc..bafd45e 100644
--- a/mali_kbase/platform/pixel/pixel_gpu_dvfs.c
+++ b/mali_kbase/platform/pixel/pixel_gpu_dvfs.c
@@ -226,6 +226,12 @@ void gpu_dvfs_update_level_locks(struct kbase_device *kbdev)
pc->dvfs.level_target = pc->dvfs.level_scaling_max;
else if (pc->dvfs.level > pc->dvfs.level_scaling_min)
pc->dvfs.level_target = pc->dvfs.level_scaling_min;
+
+#ifdef CONFIG_MALI_PIXEL_GPU_THERMAL
+ /* Check if a TMU limit needs to be applied */
+ if (pc->dvfs.level < pc->dvfs.level_tmu_max)
+ pc->dvfs.level_target = pc->dvfs.level_tmu_max;
+#endif /* CONFIG_MALI_PIXEL_GPU_THERMAL */
}
/**
@@ -518,7 +524,8 @@ static int gpu_dvfs_get_initial_level(struct kbase_device *kbdev)
*
* @kbdev: The &struct kbase_device for the GPU.
*
- * This function calls initializers for the subsystems in DVFS: governors, metrics & qos.
+ * Depending on the compile time options set, this function calls initializers for the subsystems
+ * related to GPU DVFS: governors, metrics, qos & tmu.
*
* Return: On success, returns 0. -EINVAL on error.
*/
@@ -550,6 +557,9 @@ int gpu_dvfs_init(struct kbase_device *kbdev)
pc->dvfs.level_min = pc->dvfs.table_size - 1;
pc->dvfs.level_scaling_max = pc->dvfs.level_max;
pc->dvfs.level_scaling_min = pc->dvfs.level_min;
+#ifdef CONFIG_MALI_PIXEL_GPU_THERMAL
+ pc->dvfs.level_tmu_max = pc->dvfs.level_max;
+#endif /* CONFIG_MALI_PIXEL_GPU_THERMAL */
/* Determine initial state */
pc->dvfs.level_start = gpu_dvfs_get_initial_level(kbdev);
@@ -594,6 +604,15 @@ int gpu_dvfs_init(struct kbase_device *kbdev)
}
#endif /* CONFIG_MALI_PIXEL_GPU_QOS */
+#ifdef CONFIG_MALI_PIXEL_GPU_THERMAL
+ /* Initialize thermal framework */
+ ret = gpu_tmu_init(kbdev);
+ if (ret) {
+ GPU_LOG(LOG_ERROR, kbdev, "DVFS thermal init failed\n");
+ goto fail_tmu_init;
+ }
+#endif /* CONFIG_MALI_PIXEL_GPU_THERMAL */
+
/* Initialize workqueue */
pc->dvfs.wq = create_singlethread_workqueue("gpu-dvfs");
INIT_WORK(&pc->dvfs.work, gpu_dvfs_worker);
@@ -601,6 +620,13 @@ int gpu_dvfs_init(struct kbase_device *kbdev)
/* Initialization was successful */
goto done;
+#ifdef CONFIG_MALI_PIXEL_GPU_THERMAL
+fail_tmu_init:
+#ifdef CONFIG_MALI_PIXEL_GPU_QOS
+ gpu_dvfs_qos_term(kbdev);
+#endif /* CONFIG_MALI_PIXEL_GPU_QOS */
+#endif /* CONFIG_MALI_PIXEL_GPU_THERMAL*/
+
#ifdef CONFIG_MALI_PIXEL_GPU_QOS
fail_qos_init:
#endif /* CONFIG_MALI_PIXEL_GPU_QOS */
@@ -624,6 +650,9 @@ void gpu_dvfs_term(struct kbase_device *kbdev)
destroy_workqueue(pc->dvfs.wq);
+#ifdef CONFIG_MALI_PIXEL_GPU_THERMAL
+ gpu_tmu_term(kbdev);
+#endif /* CONFIG_MALI_PIXEL_GPU_THERMAL */
#ifdef CONFIG_MALI_PIXEL_GPU_QOS
gpu_dvfs_qos_term(kbdev);
#endif /* CONFIG_MALI_PIXEL_GPU_QOS */
diff --git a/mali_kbase/platform/pixel/pixel_gpu_dvfs.h b/mali_kbase/platform/pixel/pixel_gpu_dvfs.h
index d20bac1..c426623 100644
--- a/mali_kbase/platform/pixel/pixel_gpu_dvfs.h
+++ b/mali_kbase/platform/pixel/pixel_gpu_dvfs.h
@@ -14,6 +14,9 @@
* @kbdev: The &struct kbase_device of the GPU.
* @util: The integer utilization percentage the GPU is running at.
*
+ * This function is not expected to take any clock limits into consideration when
+ * recommending the next level.
+ *
* Context: Expects the DVFS lock to be held by the caller.
*
* Return: The index of the next recommended level.
@@ -77,4 +80,9 @@ int gpu_dvfs_qos_init(struct kbase_device *kbdev);
void gpu_dvfs_qos_term(struct kbase_device *kbdev);
#endif /* CONFIG_MALI_PIXEL_GPU_QOS */
+#ifdef CONFIG_MALI_PIXEL_GPU_THERMAL
+int gpu_tmu_init(struct kbase_device *kbdev);
+void gpu_tmu_term(struct kbase_device *kbdev);
+#endif /* CONFIG_MALI_PIXEL_GPU_THERMAL*/
+
#endif /* _PIXEL_GPU_DVFS_H_ */
diff --git a/mali_kbase/platform/pixel/pixel_gpu_dvfs_governor.c b/mali_kbase/platform/pixel/pixel_gpu_dvfs_governor.c
index ae1a229..6f38229 100644
--- a/mali_kbase/platform/pixel/pixel_gpu_dvfs_governor.c
+++ b/mali_kbase/platform/pixel/pixel_gpu_dvfs_governor.c
@@ -29,8 +29,8 @@ static int gpu_dvfs_governor_basic(struct kbase_device *kbdev, int util)
struct pixel_context *pc = kbdev->platform_context;
struct gpu_dvfs_opp *tbl = pc->dvfs.table;
int level = pc->dvfs.level;
- int level_max = pc->dvfs.level_scaling_max;
- int level_min = pc->dvfs.level_scaling_min;
+ int level_max = pc->dvfs.level_max;
+ int level_min = pc->dvfs.level_min;
lockdep_assert_held(&pc->dvfs.lock);
@@ -75,6 +75,9 @@ static struct gpu_dvfs_governor_info governors[GPU_DVFS_GOVERNOR_COUNT] = {
* @kbdev: The &struct kbase_device for the GPU.
* util: The utilization percentage on the GPU.
*
+ * This function ensures that the recommended level conforms to any extant
+ * clock limits.
+ *
* Return: Returns the level the GPU should run at.
*
* Context: Process context. Expects the caller to hold the DVFS lock.
@@ -82,9 +85,26 @@ static struct gpu_dvfs_governor_info governors[GPU_DVFS_GOVERNOR_COUNT] = {
int gpu_dvfs_governor_get_next_level(struct kbase_device *kbdev, int util)
{
struct pixel_context *pc = kbdev->platform_context;
+ int level, level_min, level_max;
lockdep_assert_held(&pc->dvfs.lock);
- return governors[pc->dvfs.governor.curr].evaluate(kbdev, util);
+
+ level_min = pc->dvfs.level_scaling_min;
+ level_max = pc->dvfs.level_scaling_max;
+
+#ifdef CONFIG_MALI_PIXEL_GPU_THERMAL
+ /*
+ * If we have a TMU limit enforced, we restrict what the recommended
+ * level will be. However, we do allow overriding the TMU limit by
+ * setting scaling_min_level. Therefore thre is no adjustment to
+ * level_min below.
+ */
+ level_max = max(level_max, pc->dvfs.level_tmu_max);
+#endif /* CONFIG_MALI_PIXEL_GPU_THERMAL */
+
+ level = governors[pc->dvfs.governor.curr].evaluate(kbdev, util);
+
+ return clamp(level, level_max, level_min);
}
/**
diff --git a/mali_kbase/platform/pixel/pixel_gpu_sysfs.c b/mali_kbase/platform/pixel/pixel_gpu_sysfs.c
index 1b0a2b3..a81ad32 100644
--- a/mali_kbase/platform/pixel/pixel_gpu_sysfs.c
+++ b/mali_kbase/platform/pixel/pixel_gpu_sysfs.c
@@ -29,10 +29,9 @@ static int get_level_from_clock(struct kbase_device *kbdev, int clock)
struct pixel_context *pc = kbdev->platform_context;
int i;
- for (i = 0; i < pc->dvfs.table_size; i++) {
+ for (i = 0; i < pc->dvfs.table_size; i++)
if (pc->dvfs.table[i].clk0 == clock)
return i;
- }
return -1;
}
@@ -178,9 +177,21 @@ static ssize_t power_stats_show(struct device *dev, struct device_attribute *att
return ret;
}
+static ssize_t tmu_max_freq_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct kbase_device *kbdev = dev->driver_data;
+ struct pixel_context *pc = kbdev->platform_context;
+
+ if (!pc)
+ return -ENODEV;
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", pc->dvfs.table[pc->dvfs.level_tmu_max].clk0);
+}
+
DEVICE_ATTR_RO(clock_info);
DEVICE_ATTR_RO(dvfs_table);
DEVICE_ATTR_RO(power_stats);
+DEVICE_ATTR_RO(tmu_max_freq);
/* devfreq-like attributes */
@@ -403,6 +414,7 @@ static struct {
{"clock_info", &dev_attr_clock_info},
{"dvfs_table", &dev_attr_dvfs_table},
{"power_stats", &dev_attr_power_stats},
+ {"tmu_max_freq", &dev_attr_tmu_max_freq},
{"available_frequencies", &dev_attr_available_frequencies},
{"cur_freq", &dev_attr_cur_freq},
{"max_freq", &dev_attr_max_freq},
diff --git a/mali_kbase/platform/pixel/pixel_gpu_tmu.c b/mali_kbase/platform/pixel/pixel_gpu_tmu.c
new file mode 100644
index 0000000..94fe85a
--- /dev/null
+++ b/mali_kbase/platform/pixel/pixel_gpu_tmu.c
@@ -0,0 +1,246 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2020 Google LLC.
+ *
+ * Author: Sidath Senanayake <sidaths@google.com>
+ */
+
+/* Linux includes */
+#include <linux/pm_qos.h>
+
+/* SOC includes */
+#include <soc/google/tmu.h>
+
+/* Mali core includes */
+#include <mali_kbase.h>
+
+/* Pixel integration includes */
+#include "mali_kbase_config_platform.h"
+#include "pixel_gpu_control.h"
+#include "pixel_gpu_debug.h"
+#include "pixel_gpu_dvfs.h"
+
+/**
+ * gpu_tmu_get_num_levels() - Returns the number of DVFS OPPs
+ *
+ * @kbdev: The &struct kbase_device for the GPU.
+ *
+ * Return: The number of DVFS operating points.
+ */
+int gpu_tmu_get_num_levels(struct kbase_device *kbdev)
+{
+ struct pixel_context *pc = kbdev->platform_context;
+
+ return pc->dvfs.table_size;
+}
+
+/**
+ * gpu_tmu_get_freqs_for_level() - Returns the frequencies for a DVFS OPP
+ *
+ * @kbdev: The &struct kbase_device for the GPU.
+ * @level: The level of the DVFS OPP table to query.
+ * @clk0: Pointer to write the gpu0 clock into. Set to NULL if not required.
+ * @clk1: Pointer to write the gpu1 clock into. Set to NULL if not required.
+ *
+ * Return: If an invalid level is provided, returns -1, otherwise 0. Values
+ * returned in &clk0 and &clk1 are in kHZ.
+ */
+int gpu_tmu_get_freqs_for_level(struct kbase_device *kbdev, int level, int *clk0, int *clk1)
+{
+ struct pixel_context *pc = kbdev->platform_context;
+
+ if (level < 0 || level >= pc->dvfs.table_size)
+ return -1;
+
+ if (clk0)
+ *clk0 = pc->dvfs.table[level].clk0;
+
+ if (clk1)
+ *clk1 = pc->dvfs.table[level].clk1;
+
+ return 0;
+}
+
+/**
+ * gpu_tmu_get_vols_for_level() - Returns the frequencies for a DVFS OPP
+ *
+ * @kbdev: The &struct kbase_device for the GPU.
+ * @level: The level of the DVFS OPP table to query.
+ * @vol0: Pointer to write the gpu0 voltage into. Set to NULL if not required.
+ * @vol1: Pointer to write the gpu1 voltage into. Set to NULL if not required.
+ *
+ * Return: If an invalid level is provided, returns -1, otherwise 0. Values
+ * returned in &vol0 and &vol1 are in mV.
+ */
+int gpu_tmu_get_vols_for_level(struct kbase_device *kbdev, int level, int *vol0, int *vol1)
+{
+ struct pixel_context *pc = kbdev->platform_context;
+
+ if (level < 0 || level >= pc->dvfs.table_size)
+ return -1;
+
+ if (vol0)
+ *vol0 = pc->dvfs.table[level].vol0;
+
+ if (vol1)
+ *vol1 = pc->dvfs.table[level].vol1;
+
+ return 0;
+}
+
+/**
+ * gpu_tmu_get_cur_level() - Returns current DVFS OPP level
+ *
+ * @kbdev: The &struct kbase_device for the GPU.
+ *
+ * Context: Process context. Takes and releases the DVFS lock.
+ *
+ * Return: The current DVFS operating point level.
+ */
+int gpu_tmu_get_cur_level(struct kbase_device *kbdev)
+{
+ struct pixel_context *pc = kbdev->platform_context;
+ int level;
+
+ mutex_lock(&pc->dvfs.lock);
+ level = pc->dvfs.level;
+ mutex_unlock(&pc->dvfs.lock);
+
+ return level;
+}
+
+/**
+ * gpu_tmu_get_cur_util() - Returns the utilization of the GPU
+ *
+ * @kbdev: The &struct kbase_device for the GPU.
+ *
+ * Return: The utilization level of the GPU. This is an integer percentage.
+ */
+int gpu_tmu_get_cur_util(struct kbase_device *kbdev)
+{
+ struct pixel_context *pc = kbdev->platform_context;
+ int util = 0;
+
+ if (gpu_power_status(kbdev))
+ util = atomic_read(&pc->dvfs.util);
+
+ return util;
+}
+
+/**
+ * get_level_from_tmu_data() - Translates GPU cooling data to a target DVFS level
+ *
+ * @kbdev: The &struct kbase_device for the GPU.
+ * @data: Integer value passed by the GPU cooling driver.
+ *
+ * Return: The target DVFS operating point level indicated by the GPU cooling
+ * driver.
+ *
+ * This function is written to work with data known to be provided by the GPU
+ * cooling device on GS101 which is a target OPP level. This function simply
+ * validates that this is a valid level.
+ */
+static int get_level_from_tmu_data(struct kbase_device *kbdev, int data)
+{
+ struct pixel_context *pc = kbdev->platform_context;
+
+ if (data >= 0 && data < pc->dvfs.table_size)
+ return data;
+
+ return -1;
+}
+
+/**
+ * struct gpu_tmu_notification_data - data to store TMU data for GPU driver
+ *
+ * @gpu_drv_data: Pointer to GPU driver data.
+ * @data: Payload of this event.
+ */
+struct gpu_tmu_notification_data {
+ void *gpu_drv_data;
+ int data;
+};
+
+/**
+ * gpu_tmu_notifier() - Processes incoming TMU notifications.
+ *
+ * @notifier: The &struct notifier_block. Currently unused.
+ * @event: Event id.
+ * @v: Notification block struct.
+ *
+ * Context: Process context. Takes and releases the DVFS lock.
+ *
+ * Return: NOTIFY_OK on a valid event. NOTIFY_BAD if the notification data is
+ * invalid and the GPU driver intends to veto the action.
+ */
+static int gpu_tmu_notifier(struct notifier_block *notifier, unsigned long event, void *v)
+{
+ struct gpu_tmu_notification_data *nd = v;
+ struct kbase_device *kbdev = nd->gpu_drv_data;
+ struct pixel_context *pc = kbdev->platform_context;
+ int level;
+
+ switch (event) {
+ case GPU_COLD:
+ GPU_LOG(LOG_DEBUG, kbdev, "%s: GPU_COLD event received\n", __func__);
+ level = pc->dvfs.level_max;
+ break;
+ case GPU_NORMAL:
+ GPU_LOG(LOG_DEBUG, kbdev, "%s: GPU_NORMAL event received\n", __func__);
+ level = pc->dvfs.level_max;
+ break;
+ case GPU_THROTTLING:
+ level = get_level_from_tmu_data(kbdev, nd->data);
+ if (level < 0) {
+ GPU_LOG(LOG_WARN, kbdev,
+ "%s: GPU_THROTTLING event received with invalid level: %d\n",
+ __func__, nd->data);
+ return NOTIFY_BAD;
+ }
+ GPU_LOG(LOG_INFO, kbdev,
+ "%s: GPU_THROTTLING event received, limiting clocks to level %d\n",
+ __func__, nd->data);
+ break;
+ default:
+ GPU_LOG(LOG_WARN, kbdev, "%s: Unexpected TMU event received\n", __func__);
+ goto done;
+ }
+
+ /* Update the TMU lock level */
+ mutex_lock(&pc->dvfs.lock);
+ pc->dvfs.level_tmu_max = level;
+ gpu_dvfs_update_level_locks(kbdev);
+ mutex_unlock(&pc->dvfs.lock);
+
+done:
+ return NOTIFY_OK;
+}
+
+static struct notifier_block gpu_tmu_nb = {
+ .notifier_call = gpu_tmu_notifier,
+};
+
+/**
+ * gpu_tmu_init() - Initializes the Pixel TMU handling subsystem
+ *
+ * @kbdev: The &struct kbase_device for the GPU.
+ *
+ * Return: Currently always returns 0.
+ */
+int gpu_tmu_init(struct kbase_device *kbdev)
+{
+ exynos_gpu_add_notifier(&gpu_tmu_nb);
+ return 0;
+}
+
+/**
+ * gpu_tmu_term() - Terminates the Pixel GPU TMU handling subsystem.
+ *
+ * @kbdev: The &struct kbase_device for the GPU.
+ *
+ * Note that this function currently doesn't do anything.
+ */
+void gpu_tmu_term(struct kbase_device *kbdev)
+{
+}
+