GPU DVFS源码解析

——关于GPU Mali-T628

Posted by yuchen on March 6, 2017

一. GPU DVFS调频过程

ODROID源码目录/kernel/hardkernel/odroidxu3/drivers/gpu/arm/midgard/platform/gpu_dvfs_handler.c文件中定义了gpu_dvfs_event_proc函数,其作用即是对GPU进行调频,其内容如下:

static void gpu_dvfs_event_proc(struct work_struct *q)
{
	int freq = 0;
	struct kbase_device *kbdev = pkbdev;
	struct exynos_context *platform;

	platform = (struct exynos_context *) kbdev->platform_context;
	if (!platform)
		return;

	mutex_lock(&platform->gpu_dvfs_handler_lock);
	if (gpu_control_state_set(kbdev, GPU_CONTROL_IS_POWER_ON, 0)) {
		freq = gpu_get_target_freq();
		gpu_set_target_freq(freq);
	}
	mutex_unlock(&platform->gpu_dvfs_handler_lock);
}
//gpu_dvfs_handler.c:108
//将gpu_dvfs_event_proc定义成名称为gpu_dvfs_work的work
static DECLARE_WORK(gpu_dvfs_work, gpu_dvfs_event_proc);
//gpu_dvfs_handler.c:149
//将gpu_dvfs_work放到cpu0上执行,platform->dvfs_wq:workqueue to use,gpu_dvfs_work:work to queue
if (platform->dvfs_wq)
		queue_work_on(0, platform->dvfs_wq, &gpu_dvfs_work);

可以看出,gpu_dvfs_event_proc函数先调用gpu_get_target_freq()获取将要调至的频率,再使用gpu_set_target_freq(freq)进行调频。

另外,在ODROID源码目录/kernel/hardkernel/odroidxu3/drivers/gpu/arm/midgard/platform/gpu_dvfs_handler.c文件中也定义了gpu_get_target_freq函数,其定义如下:

static int gpu_get_target_freq(void)
{
	int freq;
	struct kbase_device *kbdev = pkbdev;
	struct exynos_context *platform;

	platform = (struct exynos_context *) kbdev->platform_context;
	if (!platform)
		return -ENODEV;

#ifdef CONFIG_MALI_T6XX_DVFS
	gpu_dvfs_decide_next_level(kbdev, platform->utilization);
#endif /* CONFIG_MALI_T6XX_DVFS */

	freq = platform->table[platform->step].clock;
#ifdef CONFIG_MALI_T6XX_DVFS
	if ((platform->max_lock > 0) && (freq > platform->max_lock))
		freq = platform->max_lock;
	else if ((platform->min_lock > 0) && (freq < platform->min_lock))
		freq = platform->min_lock;
#endif /* CONFIG_MALI_T6XX_DVFS */

	return freq;
}

gpu_get_target_freq函数中调用了gpu_dvfs_decide_next_level函数,其定义在ODROID源码目录/kernel/hardkernel/odroidxu3/drivers/gpu/arm/midgard/platform/gpu_dvfs_governor.c文件中,内容如下:

int gpu_dvfs_decide_next_level(struct kbase_device *kbdev, int utilization)
{
	unsigned long flags;
	struct exynos_context *platform = (struct exynos_context *) kbdev->platform_context;
	if (!platform)
		return -ENODEV;

	spin_lock_irqsave(&platform->gpu_dvfs_spinlock, flags);
	gpu_dvfs_get_next_freq(kbdev, utilization);
	spin_unlock_irqrestore(&platform->gpu_dvfs_spinlock, flags);

	return 0;
}

gpu_dvfs_decide_next_level函数中调用了gpu_dvfs_get_next_freq函数,gpu_dvfs_get_next_freq是一个函数指针变量,其也定义在ODROID源码目录/kernel/hardkernel/odroidxu3/drivers/gpu/arm/midgard/platform/gpu_dvfs_governor.c文件中,其内容如下:

typedef void (*GET_NEXT_FREQ)(struct kbase_device *kbdev, int utilization);
GET_NEXT_FREQ gpu_dvfs_get_next_freq;

gpu_dvfs_governor.c文件中的gpu_dvfs_governor_init函数内,我们可以看到如下语句:

#ifdef CONFIG_MALI_T6XX_DVFS
	switch (governor_type) {
	case G3D_DVFS_GOVERNOR_DEFAULT:
		gpu_dvfs_get_next_freq = (GET_NEXT_FREQ)&gpu_dvfs_governor_default;
		platform->table = gpu_dvfs_infotbl_default;
		platform->table_size = GPU_DVFS_TABLE_SIZE(gpu_dvfs_infotbl_default);
#ifdef CONFIG_DYNIMIC_ABB
		platform->devfreq_g3d_asv_abb = gpu_abb_infobl_default;
#endif

由此可知,实际决定GPU频率的函数是gpu_dvfs_governor_default函数(因为gpu_dvfs_get_next_freq指向gpu_dvfs_governor_default),其也定义在gpu_dvfs_governor.c中,内容如下:

#ifdef CONFIG_MALI_T6XX_DVFS
static int gpu_dvfs_governor_default(struct kbase_device *kbdev, int utilization)
{
	struct exynos_context *platform;

	platform = (struct exynos_context *) kbdev->platform_context;
	if (!platform)
		return -ENODEV;

	if ((platform->step < platform->table_size-1) &&
			(utilization > platform->table[platform->step].max_threshold)) {
		platform->step++;
		platform->down_requirement = platform->table[platform->step].stay_count;
		DVFS_ASSERT(platform->step < platform->table_size);
	} else if ((platform->step > 0) && (utilization < platform->table[platform->step].min_threshold)) {
		DVFS_ASSERT(platform->step > 0);
		platform->down_requirement--;
		if (platform->down_requirement == 0) {
			platform->step--;
			platform->down_requirement = platform->table[platform->step].stay_count;
		}
	} else {
		platform->down_requirement = platform->table[platform->step].stay_count;
	}

	return 0;
}

因此我们也只需要在gpu_dvfs_governor.c中写一个类似于static int gpu_dvfs_governor_custom(struct kbase_device *kbdev, int utilization)的函数,并将gpu_dvfs_get_next_freq函数指针指向我们自己所定义的调频函数即可。

二. 关于GPU调节频率表

gpu_dvfs_governor_init函数中,有如下定义:

case G3D_DVFS_GOVERNOR_DEFAULT:
		gpu_dvfs_get_next_freq = (GET_NEXT_FREQ)&gpu_dvfs_governor_default;
		platform->table = gpu_dvfs_infotbl_default;
		platform->table_size = GPU_DVFS_TABLE_SIZE(gpu_dvfs_infotbl_default);

补充:

#define GPU_DVFS_TABLE_SIZE(X)  ARRAY_SIZE(X)

gpu_dvfs_infotbl_default的定义如下:

static gpu_dvfs_info gpu_dvfs_infotbl_default[] = {
/*  vol,clk,min,max,down stay, pm_qos mem, pm_qos int, pm_qos cpu_kfc_min, pm_qos cpu_egl_max */
#if SOC_NAME == 5422
	{812500,  177,  0,  90, 2, 0, 275000, 222000,       0, CPU_MAX},
	{862500,  266, 60,  90, 1, 0, 413000, 222000,       0, CPU_MAX},
	{912500,  350, 70,  90, 1, 0, 728000, 333000,       0, CPU_MAX},
	{962500,  420, 78,  90, 1, 0, 825000, 400000,       0, CPU_MAX},
	{1000000, 480, 90,  99, 1, 0, 825000, 400000, 1000000, 1600000},
#ifdef CONFIG_SOC_EXYNOS5422_REV_0
	{1037500, 543, 99, 100, 1, 0, 825000, 400000, 1000000, 1600000},
#else
	{1037500, 533, 99, 100, 1, 0, 825000, 400000, 1000000, 1600000},
#endif /* CONFIG_SOC_EXYNOS5422_REV_0 */

gpu_dvfs_info的定义如下:

typedef struct _gpu_dvfs_info {
	unsigned int voltage;
	unsigned int clock;
	int min_threshold;
	int max_threshold;
	int stay_count;
	unsigned long long time;
	int mem_freq;
	int int_freq;
	int cpu_freq;
	int cpu_max_freq;
} gpu_dvfs_info;

三. 关于GPU_DVFS定时器

//gpu_dvfs_handler.c:214
hrtimer_start(&kbdev->pm.metrics.timer, HR_TIMER_DELAY_MSEC(platform->polling_speed), HRTIMER_MODE_REL);
//mali_kbase_platform.c:75
platform->polling_speed = 100;

故而,每隔100毫秒进行一次GPU DVFS调频操作。


本文总阅读量