diff options
author | Sidath Senanayake <sidaths@google.com> | 2020-08-28 18:10:52 +0100 |
---|---|---|
committer | Sidath Senanayake <sidaths@google.com> | 2020-09-02 21:01:38 +0100 |
commit | b72a786a61dd97ca1733d2cb4091fe7fa41f7633 (patch) | |
tree | fcd10ced767e57a09be2d9657f39744bf97bee7f | |
parent | 01278baa0e6506d197c44bf09d2032f24dd9a1e4 (diff) | |
download | gpu-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/Kbuild | 3 | ||||
-rw-r--r-- | mali_kbase/Makefile | 1 | ||||
-rw-r--r-- | mali_kbase/platform/pixel/Kbuild | 3 | ||||
-rw-r--r-- | mali_kbase/platform/pixel/Kconfig | 9 | ||||
-rw-r--r-- | mali_kbase/platform/pixel/mali_kbase_config_platform.h | 3 | ||||
-rw-r--r-- | mali_kbase/platform/pixel/pixel_gpu_dvfs.c | 31 | ||||
-rw-r--r-- | mali_kbase/platform/pixel/pixel_gpu_dvfs.h | 8 | ||||
-rw-r--r-- | mali_kbase/platform/pixel/pixel_gpu_dvfs_governor.c | 26 | ||||
-rw-r--r-- | mali_kbase/platform/pixel/pixel_gpu_sysfs.c | 16 | ||||
-rw-r--r-- | mali_kbase/platform/pixel/pixel_gpu_tmu.c | 246 |
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) +{ +} + |