458 lines
9.6 KiB
C
Executable file
458 lines
9.6 KiB
C
Executable file
/* SPDX-License-Identifier: GPL-2.0 */
|
|
|
|
/*
|
|
* (C) COPYRIGHT 2021 Samsung Electronics Inc. All rights reserved.
|
|
*
|
|
* This program is free software and is provided to you under the terms of the
|
|
* GNU General Public License version 2 as published by the Free Software
|
|
* Foundation, and any use by you of this program is subject to the terms
|
|
* of such GNU licence.
|
|
*
|
|
* 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, you can access it online at
|
|
* http://www.gnu.org/licenses/gpl-2.0.html.
|
|
*/
|
|
|
|
#include <gpex_tsg.h>
|
|
|
|
#include <gpex_utils.h>
|
|
#include <gpex_dvfs.h>
|
|
#include <gpex_pm.h>
|
|
#include <gpex_clock.h>
|
|
#include <gpexbe_devicetree.h>
|
|
#include <gpexbe_utilization.h>
|
|
|
|
#include "gpex_tsg_internal.h"
|
|
#include "gpex_dvfs_internal.h"
|
|
#include "gpu_dvfs_governor.h"
|
|
|
|
static struct _tsg_info tsg;
|
|
|
|
void gpex_tsg_input_nr_acc_cnt(void)
|
|
{
|
|
tsg.input_job_nr_acc += tsg.input_job_nr;
|
|
}
|
|
|
|
void gpex_tsg_reset_acc_count(void)
|
|
{
|
|
tsg.input_job_nr_acc = 0;
|
|
gpex_tsg_set_queued_time_tick(0, 0);
|
|
gpex_tsg_set_queued_time_tick(1, 0);
|
|
}
|
|
|
|
/* SETTER */
|
|
void gpex_tsg_set_migov_mode(int mode)
|
|
{
|
|
tsg.migov_mode = mode;
|
|
return;
|
|
}
|
|
|
|
void gpex_tsg_set_freq_margin(int margin)
|
|
{
|
|
tsg.freq_margin = margin;
|
|
return;
|
|
}
|
|
|
|
void gpex_tsg_set_util_history(int idx, int order, int input)
|
|
{
|
|
tsg.prediction.util_history[idx][order] = input;
|
|
return;
|
|
}
|
|
|
|
void gpex_tsg_set_weight_util(int idx, int input)
|
|
{
|
|
tsg.prediction.weight_util[idx] = input;
|
|
return;
|
|
}
|
|
|
|
void gpex_tsg_set_weight_freq(int input)
|
|
{
|
|
tsg.prediction.weight_freq = input;
|
|
return;
|
|
}
|
|
|
|
void gpex_tsg_set_en_signal(bool input)
|
|
{
|
|
tsg.prediction.en_signal = input;
|
|
return;
|
|
}
|
|
|
|
void gpex_tsg_set_pmqos(bool input)
|
|
{
|
|
tsg.is_pm_qos_tsg = input;
|
|
return;
|
|
}
|
|
|
|
void gpex_tsg_set_weight_table_idx(int idx, int input)
|
|
{
|
|
if (idx == 0)
|
|
tsg.weight_table_idx_0 = input;
|
|
else
|
|
tsg.weight_table_idx_1 = input;
|
|
return;
|
|
}
|
|
|
|
void gpex_tsg_set_is_gov_set(int input)
|
|
{
|
|
tsg.is_gov_set = input;
|
|
return;
|
|
}
|
|
|
|
void gpex_tsg_set_saved_polling_speed(int input)
|
|
{
|
|
tsg.migov_saved_polling_speed = input;
|
|
return;
|
|
}
|
|
|
|
void gpex_tsg_set_governor_type_init(int input)
|
|
{
|
|
tsg.governor_type_init = input;
|
|
return;
|
|
}
|
|
|
|
void gpex_tsg_set_amigo_flags(int input)
|
|
{
|
|
tsg.amigo_flags = input;
|
|
return;
|
|
}
|
|
|
|
void gpex_tsg_set_queued_threshold(int idx, uint32_t input)
|
|
{
|
|
tsg.queue.queued_threshold[idx] = input;
|
|
return;
|
|
}
|
|
|
|
void gpex_tsg_set_queued_time_tick(int idx, ktime_t input)
|
|
{
|
|
tsg.queue.queued_time_tick[idx] = input;
|
|
return;
|
|
}
|
|
|
|
void gpex_tsg_set_queued_time(int idx, ktime_t input)
|
|
{
|
|
tsg.queue.queued_time[idx] = input;
|
|
}
|
|
|
|
void gpex_tsg_set_queued_last_updated(ktime_t input)
|
|
{
|
|
tsg.queue.last_updated = input;
|
|
}
|
|
|
|
void gpex_tsg_set_queue_nr(int type, int variation)
|
|
{
|
|
/* GPEX_TSG_QUEUE_JOB/IN/OUT: 0, 1, 2*/
|
|
/* GPEX_TSG_QUEUE_INC/DEC/RST: 3, 4, 5*/
|
|
|
|
if (variation == GPEX_TSG_RST)
|
|
atomic_set(&tsg.queue.nr[type], 0);
|
|
else
|
|
(variation == GPEX_TSG_INC) ? atomic_inc(&tsg.queue.nr[type]) :
|
|
atomic_dec(&tsg.queue.nr[type]);
|
|
}
|
|
|
|
/* GETTER */
|
|
int gpex_tsg_get_migov_mode(void)
|
|
{
|
|
return tsg.migov_mode;
|
|
}
|
|
|
|
int gpex_tsg_get_freq_margin(void)
|
|
{
|
|
return tsg.freq_margin;
|
|
}
|
|
|
|
int gpex_tsg_get_util_history(int idx, int order)
|
|
{
|
|
return tsg.prediction.util_history[idx][order];
|
|
}
|
|
|
|
int gpex_tsg_get_weight_util(int idx)
|
|
{
|
|
return tsg.prediction.weight_util[idx];
|
|
}
|
|
|
|
int gpex_tsg_get_weight_freq(void)
|
|
{
|
|
return tsg.prediction.weight_freq;
|
|
}
|
|
|
|
bool gpex_tsg_get_en_signal(void)
|
|
{
|
|
return tsg.prediction.en_signal;
|
|
}
|
|
|
|
bool gpex_tsg_get_pmqos(void)
|
|
{
|
|
return tsg.is_pm_qos_tsg;
|
|
}
|
|
|
|
int gpex_tsg_get_weight_table_idx(int idx)
|
|
{
|
|
return (idx == 0) ? tsg.weight_table_idx_0 : tsg.weight_table_idx_1;
|
|
}
|
|
|
|
int gpex_tsg_get_is_gov_set(void)
|
|
{
|
|
return tsg.is_gov_set;
|
|
}
|
|
|
|
int gpex_tsg_get_saved_polling_speed(void)
|
|
{
|
|
return tsg.migov_saved_polling_speed;
|
|
}
|
|
|
|
int gpex_tsg_get_governor_type_init(void)
|
|
{
|
|
return tsg.governor_type_init;
|
|
}
|
|
|
|
int gpex_tsg_get_amigo_flags(void)
|
|
{
|
|
return tsg.amigo_flags;
|
|
}
|
|
|
|
uint32_t gpex_tsg_get_queued_threshold(int idx)
|
|
{
|
|
return tsg.queue.queued_threshold[idx];
|
|
}
|
|
|
|
ktime_t gpex_tsg_get_queued_time_tick(int idx)
|
|
{
|
|
return tsg.queue.queued_time_tick[idx];
|
|
}
|
|
|
|
ktime_t gpex_tsg_get_queued_time(int idx)
|
|
{
|
|
return tsg.queue.queued_time[idx];
|
|
}
|
|
|
|
ktime_t *gpex_tsg_get_queued_time_array(void)
|
|
{
|
|
return tsg.queue.queued_time;
|
|
}
|
|
|
|
ktime_t gpex_tsg_get_queued_last_updated(void)
|
|
{
|
|
return tsg.queue.last_updated;
|
|
}
|
|
|
|
int gpex_tsg_get_queue_nr(int type)
|
|
{
|
|
return atomic_read(&tsg.queue.nr[type]);
|
|
}
|
|
|
|
struct blocking_notifier_head *gpex_tsg_get_frag_utils_change_notifier_list(void)
|
|
{
|
|
return &(tsg.frag_utils_change_notifier_list);
|
|
}
|
|
|
|
int gpex_tsg_notify_frag_utils_change(u32 js0_utils)
|
|
{
|
|
int ret = 0;
|
|
ret = blocking_notifier_call_chain(gpex_tsg_get_frag_utils_change_notifier_list(), 0,
|
|
(void *)&js0_utils);
|
|
return ret;
|
|
}
|
|
|
|
int gpex_tsg_spin_lock(void)
|
|
{
|
|
return raw_spin_trylock(&tsg.spinlock);
|
|
}
|
|
|
|
void gpex_tsg_spin_unlock(void)
|
|
{
|
|
raw_spin_unlock(&tsg.spinlock);
|
|
}
|
|
|
|
/* Function of Kbase */
|
|
|
|
static inline bool both_q_active(int in_nr, int out_nr)
|
|
{
|
|
return in_nr > 0 && out_nr > 0;
|
|
}
|
|
|
|
static inline bool hw_q_active(int out_nr)
|
|
{
|
|
return out_nr > 0;
|
|
}
|
|
|
|
static void accum_queued_time(ktime_t current_time, bool accum0, bool accum1)
|
|
{
|
|
int time_diff = 0;
|
|
time_diff = current_time - gpex_tsg_get_queued_last_updated();
|
|
|
|
if (accum0 == true)
|
|
gpex_tsg_set_queued_time_tick(0, gpex_tsg_get_queued_time_tick(0) + time_diff);
|
|
if (accum1 == true)
|
|
gpex_tsg_set_queued_time_tick(1, gpex_tsg_get_queued_time_tick(1) + time_diff);
|
|
|
|
gpex_tsg_set_queued_last_updated(current_time);
|
|
}
|
|
|
|
int gpex_tsg_reset_count(int powered)
|
|
{
|
|
if (powered)
|
|
return 0;
|
|
|
|
gpex_tsg_set_queue_nr(GPEX_TSG_QUEUE_JOB, GPEX_TSG_RST);
|
|
gpex_tsg_set_queue_nr(GPEX_TSG_QUEUE_IN, GPEX_TSG_RST);
|
|
gpex_tsg_set_queue_nr(GPEX_TSG_QUEUE_OUT, GPEX_TSG_RST);
|
|
gpex_tsg_set_queued_last_updated(0);
|
|
|
|
tsg.input_job_nr = 0;
|
|
|
|
return !powered;
|
|
}
|
|
|
|
int gpex_tsg_set_count(u32 status, bool stop)
|
|
{
|
|
int prev_out_nr, prev_in_nr;
|
|
int cur_out_nr, cur_in_nr;
|
|
bool need_update = false;
|
|
ktime_t current_time = 0;
|
|
|
|
if (gpex_tsg_get_queued_last_updated() == 0)
|
|
gpex_tsg_set_queued_last_updated(ktime_get());
|
|
|
|
prev_out_nr = gpex_tsg_get_queue_nr(GPEX_TSG_QUEUE_OUT);
|
|
prev_in_nr = gpex_tsg_get_queue_nr(GPEX_TSG_QUEUE_IN);
|
|
|
|
if (stop) {
|
|
gpex_tsg_set_queue_nr(GPEX_TSG_QUEUE_IN, GPEX_TSG_INC);
|
|
gpex_tsg_set_queue_nr(GPEX_TSG_QUEUE_OUT, GPEX_TSG_DEC);
|
|
tsg.input_job_nr++;
|
|
} else {
|
|
switch (status) {
|
|
case GPEX_TSG_ATOM_STATE_QUEUED:
|
|
gpex_tsg_set_queue_nr(GPEX_TSG_QUEUE_IN, GPEX_TSG_INC);
|
|
tsg.input_job_nr++;
|
|
break;
|
|
case GPEX_TSG_ATOM_STATE_IN_JS:
|
|
gpex_tsg_set_queue_nr(GPEX_TSG_QUEUE_IN, GPEX_TSG_DEC);
|
|
gpex_tsg_set_queue_nr(GPEX_TSG_QUEUE_OUT, GPEX_TSG_INC);
|
|
tsg.input_job_nr--;
|
|
if (tsg.input_job_nr < 0)
|
|
tsg.input_job_nr = 0;
|
|
break;
|
|
case GPEX_TSG_ATOM_STATE_HW_COMPLETED:
|
|
gpex_tsg_set_queue_nr(GPEX_TSG_QUEUE_OUT, GPEX_TSG_DEC);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
cur_in_nr = gpex_tsg_get_queue_nr(GPEX_TSG_QUEUE_IN);
|
|
cur_out_nr = gpex_tsg_get_queue_nr(GPEX_TSG_QUEUE_OUT);
|
|
|
|
if ((!both_q_active(prev_in_nr, prev_out_nr) && both_q_active(cur_in_nr, cur_out_nr)) ||
|
|
(!hw_q_active(prev_out_nr) && hw_q_active(cur_out_nr)))
|
|
need_update = true;
|
|
else if ((both_q_active(prev_in_nr, prev_out_nr) &&
|
|
!both_q_active(cur_in_nr, cur_out_nr)) ||
|
|
(hw_q_active(prev_out_nr) && !hw_q_active(cur_out_nr)))
|
|
need_update = true;
|
|
else if (prev_out_nr > cur_out_nr) {
|
|
current_time = ktime_get();
|
|
need_update =
|
|
current_time - (gpex_tsg_get_queued_last_updated() > 2) * GPEX_TSG_PERIOD;
|
|
}
|
|
|
|
if (need_update) {
|
|
current_time = (current_time == 0) ? ktime_get() : current_time;
|
|
if (gpex_tsg_spin_lock()) {
|
|
accum_queued_time(current_time, both_q_active(prev_in_nr, prev_out_nr),
|
|
hw_q_active(prev_out_nr));
|
|
gpex_tsg_spin_unlock();
|
|
}
|
|
}
|
|
|
|
if ((cur_in_nr + cur_out_nr) < 0)
|
|
gpex_tsg_reset_count(0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void gpex_tsg_context_init(void)
|
|
{
|
|
gpex_tsg_set_weight_table_idx(0, gpexbe_devicetree_get_int(gpu_weight_table_idx_0));
|
|
gpex_tsg_set_weight_table_idx(1, gpexbe_devicetree_get_int(gpu_weight_table_idx_1));
|
|
gpex_tsg_set_governor_type_init(gpex_dvfs_get_governor_type());
|
|
gpex_tsg_set_queued_threshold(0, 0);
|
|
gpex_tsg_set_queued_threshold(1, 0);
|
|
gpex_tsg_set_queued_time_tick(0, 0);
|
|
gpex_tsg_set_queued_time_tick(1, 0);
|
|
gpex_tsg_set_queued_time(0, 0);
|
|
gpex_tsg_set_queued_time(1, 0);
|
|
gpex_tsg_set_queued_last_updated(0);
|
|
gpex_tsg_set_queue_nr(GPEX_TSG_QUEUE_JOB, GPEX_TSG_RST);
|
|
gpex_tsg_set_queue_nr(GPEX_TSG_QUEUE_IN, GPEX_TSG_RST);
|
|
gpex_tsg_set_queue_nr(GPEX_TSG_QUEUE_OUT, GPEX_TSG_RST);
|
|
|
|
tsg.input_job_nr = 0;
|
|
tsg.input_job_nr_acc = 0;
|
|
}
|
|
|
|
int gpex_tsg_init(struct device **dev)
|
|
{
|
|
raw_spin_lock_init(&tsg.spinlock);
|
|
tsg.kbdev = container_of(dev, struct kbase_device, dev);
|
|
|
|
gpex_tsg_context_init();
|
|
gpex_tsg_external_init(&tsg);
|
|
gpex_tsg_sysfs_init(&tsg);
|
|
|
|
gpex_utils_get_exynos_context()->tsg = &tsg;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int gpex_tsg_term(void)
|
|
{
|
|
gpex_tsg_sysfs_term();
|
|
gpex_tsg_external_term();
|
|
tsg.kbdev = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void gpex_tsg_update_firstjob_time(void)
|
|
{
|
|
}
|
|
|
|
void gpex_tsg_update_lastjob_time(int slot_nr)
|
|
{
|
|
CSTD_UNUSED(slot_nr);
|
|
}
|
|
|
|
void gpex_tsg_update_jobsubmit_time(void)
|
|
{
|
|
}
|
|
|
|
void gpex_tsg_sum_jobs_time(int slot_nr)
|
|
{
|
|
CSTD_UNUSED(slot_nr);
|
|
}
|
|
|
|
int gpex_tsg_amigo_interframe_sw_update(ktime_t start, ktime_t end)
|
|
{
|
|
CSTD_UNUSED(start);
|
|
CSTD_UNUSED(end);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int gpex_tsg_amigo_interframe_hw_update_eof(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int gpex_tsg_amigo_interframe_hw_update(void)
|
|
{
|
|
return 0;
|
|
}
|