1253 lines
28 KiB
C
1253 lines
28 KiB
C
|
|
// SPDX-License-Identifier: GPL-2.0
|
||
|
|
/*
|
||
|
|
* Copyright (c) 2020 MediaTek Inc.
|
||
|
|
*/
|
||
|
|
|
||
|
|
/*
|
||
|
|
*=============================================================
|
||
|
|
* Include files
|
||
|
|
*=============================================================
|
||
|
|
*/
|
||
|
|
|
||
|
|
/* system includes */
|
||
|
|
#include <linux/pm_qos.h>
|
||
|
|
#include <linux/mutex.h>
|
||
|
|
#include <linux/timekeeping.h>
|
||
|
|
#include <linux/slab.h>
|
||
|
|
#include <linux/delay.h>
|
||
|
|
#include <linux/workqueue.h>
|
||
|
|
#include <linux/platform_device.h>
|
||
|
|
#include <linux/soc/mediatek/mtk-pm-qos.h>
|
||
|
|
#include <linux/seq_file.h>
|
||
|
|
|
||
|
|
#include "mnoc_drv.h"
|
||
|
|
#include "mnoc_hw.h"
|
||
|
|
#include "mnoc_option.h"
|
||
|
|
#include "mnoc_qos.h"
|
||
|
|
|
||
|
|
#if MNOC_TIME_PROFILE
|
||
|
|
unsigned long sum_start, sum_suspend, sum_end, sum_work_func;
|
||
|
|
unsigned int cnt_start, cnt_suspend, cnt_end, cnt_work_func;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#if MNOC_QOS_ENABLE
|
||
|
|
#include <mtk_qos_bound.h>
|
||
|
|
#include <mtk_qos_sram.h>
|
||
|
|
#include "apusys_power.h"
|
||
|
|
|
||
|
|
|
||
|
|
#define DEFAUTL_QOS_POLLING_TIME (16)
|
||
|
|
/* define in mtk_qos_bound.h */
|
||
|
|
#define MTK_QOS_BUF_SIZE QOS_BOUND_BUF_SIZE
|
||
|
|
|
||
|
|
/* assume QOS_SMIBM_VPU0 is the first entry in qos_smibm_type for APUSYS */
|
||
|
|
#define APUSYS_QOSBOUND_START (QOS_SMIBM_VPU0)
|
||
|
|
#define get_qosbound_enum(x) (APUSYS_QOSBOUND_START + x)
|
||
|
|
|
||
|
|
#ifdef APU_QOS_IPUIF_ADJUST
|
||
|
|
#ifdef CONFIG_MACH_MT6853
|
||
|
|
#define NR_APU_VCORE_OPP (4)
|
||
|
|
static unsigned int apu_vcore_bw_opp_tab[NR_APU_VCORE_OPP] = {
|
||
|
|
10200, /* 4266 Mhz -> 0.725v */
|
||
|
|
7600, /* 3200 Mhz -> 0.65v */
|
||
|
|
5100, /* 1866 Mhz -> 0.6v */
|
||
|
|
0, /* 800~1600 Mhz -> 0.55v */
|
||
|
|
};
|
||
|
|
enum DVFS_VOLTAGE vcore_opp_map[NR_APU_VCORE_OPP] = {
|
||
|
|
DVFS_VOLT_00_725000_V, // VCORE_OPP_0
|
||
|
|
DVFS_VOLT_00_650000_V, // VCORE_OPP_1
|
||
|
|
DVFS_VOLT_00_600000_V, // VCORE_OPP_2
|
||
|
|
DVFS_VOLT_00_550000_V // VCORE_OPP_3
|
||
|
|
};
|
||
|
|
#endif /* CONFIG_MACH_MT6853 */
|
||
|
|
#ifdef CONFIG_MACH_MT6873
|
||
|
|
#define NR_APU_VCORE_OPP (4)
|
||
|
|
static unsigned int apu_vcore_bw_opp_tab[NR_APU_VCORE_OPP] = {
|
||
|
|
10200, /* 4266 Mhz -> 0.725v */
|
||
|
|
7600, /* 3200 Mhz -> 0.65v */
|
||
|
|
5120, /* 1866 Mhz -> 0.6v */
|
||
|
|
0, /* 800~1600 Mhz -> 0.575v */
|
||
|
|
};
|
||
|
|
enum DVFS_VOLTAGE vcore_opp_map[NR_APU_VCORE_OPP] = {
|
||
|
|
DVFS_VOLT_00_725000_V, // VCORE_OPP_0
|
||
|
|
DVFS_VOLT_00_650000_V, // VCORE_OPP_1
|
||
|
|
DVFS_VOLT_00_600000_V, // VCORE_OPP_2
|
||
|
|
DVFS_VOLT_00_575000_V // VCORE_OPP_3
|
||
|
|
};
|
||
|
|
#endif /* CONFIG_MACH_MT6873 */
|
||
|
|
#if defined(CONFIG_MACH_MT6885)
|
||
|
|
#define NR_APU_VCORE_OPP (4)
|
||
|
|
static unsigned int apu_vcore_bw_opp_tab[NR_APU_VCORE_OPP] = {
|
||
|
|
20400, /* 3733 Mhz -> 0.725v */
|
||
|
|
15300, /* 3068 Mhz -> 0.65v */
|
||
|
|
11900, /* 2366 Mhz -> 0.6v */
|
||
|
|
0, /* 800~1866 Mhz -> 0.575v */
|
||
|
|
};
|
||
|
|
enum DVFS_VOLTAGE vcore_opp_map[NR_APU_VCORE_OPP] = {
|
||
|
|
DVFS_VOLT_00_725000_V, // VCORE_OPP_0
|
||
|
|
DVFS_VOLT_00_650000_V, // VCORE_OPP_1
|
||
|
|
DVFS_VOLT_00_600000_V, // VCORE_OPP_2
|
||
|
|
DVFS_VOLT_00_575000_V // VCORE_OPP_3
|
||
|
|
};
|
||
|
|
#elif defined(CONFIG_MACH_MT6893) || defined(CONFIG_MTK_DVFSRC_MT6893_PRETEST)
|
||
|
|
#define NR_APU_VCORE_OPP (5)
|
||
|
|
static unsigned int apu_vcore_bw_opp_tab[NR_APU_VCORE_OPP] = {
|
||
|
|
23800, /* 4266 Mhz -> 0.75v */
|
||
|
|
20400, /* 3733 Mhz -> 0.725v */
|
||
|
|
15300, /* 3068 Mhz -> 0.65v */
|
||
|
|
11900, /* 2366 Mhz -> 0.6v */
|
||
|
|
0, /* 800~1866 Mhz -> 0.575v */
|
||
|
|
};
|
||
|
|
enum DVFS_VOLTAGE vcore_opp_map[NR_APU_VCORE_OPP] = {
|
||
|
|
DVFS_VOLT_00_750000_V, // VCORE_OPP_0
|
||
|
|
DVFS_VOLT_00_725000_V, // VCORE_OPP_1
|
||
|
|
DVFS_VOLT_00_650000_V, // VCORE_OPP_2
|
||
|
|
DVFS_VOLT_00_600000_V, // VCORE_OPP_3
|
||
|
|
DVFS_VOLT_00_575000_V // VCORE_OPP_4
|
||
|
|
};
|
||
|
|
#endif /* CONFIG_MACH_MT6893 */
|
||
|
|
|
||
|
|
unsigned int apu_bw_vcore_opp;
|
||
|
|
#endif /* APU_QOS_IPUIF_ADJUST */
|
||
|
|
|
||
|
|
enum apu_qos_cmd_status {
|
||
|
|
CMD_RUNNING,
|
||
|
|
CMD_BLOCKED,
|
||
|
|
|
||
|
|
NR_APU_QOS_CMD_STATUS
|
||
|
|
};
|
||
|
|
|
||
|
|
struct qos_counter {
|
||
|
|
struct timer_list qos_timer;
|
||
|
|
struct list_head list;
|
||
|
|
struct mutex list_mtx;
|
||
|
|
|
||
|
|
int wait_ms;
|
||
|
|
};
|
||
|
|
|
||
|
|
struct engine_pm_qos_counter {
|
||
|
|
struct mtk_pm_qos_request qos_req;
|
||
|
|
|
||
|
|
int32_t last_report_bw;
|
||
|
|
unsigned int last_idx;
|
||
|
|
unsigned int core;
|
||
|
|
};
|
||
|
|
|
||
|
|
struct cmd_qos {
|
||
|
|
uint64_t cmd_id;
|
||
|
|
uint64_t sub_cmd_id;
|
||
|
|
unsigned int core;
|
||
|
|
unsigned int status; /* running/blocked */
|
||
|
|
|
||
|
|
int total_bw;
|
||
|
|
unsigned int last_idx;
|
||
|
|
unsigned int count;
|
||
|
|
|
||
|
|
struct list_head list;
|
||
|
|
struct mutex mtx;
|
||
|
|
};
|
||
|
|
|
||
|
|
static struct qos_counter qos_counter;
|
||
|
|
static struct work_struct qos_work;
|
||
|
|
static struct engine_pm_qos_counter engine_pm_qos_counter[NR_APU_QOS_ENGINE];
|
||
|
|
|
||
|
|
/* indicate engine running or not based on cmd cntr for pm qos */
|
||
|
|
/* increase 1 when cmd enque, decrease 1 when cmd dequeue */
|
||
|
|
static int engine_cmd_cntr[NR_APU_QOS_ENGINE];
|
||
|
|
static int engine_boost_val[NR_APU_QOS_ENGINE];
|
||
|
|
static bool qos_timer_exist;
|
||
|
|
|
||
|
|
#if MNOC_QOS_BOOST_ENABLE
|
||
|
|
bool apu_qos_boost_flag;
|
||
|
|
static bool apusys_on_flag;
|
||
|
|
static unsigned int apu_qos_boost_ddr_opp;
|
||
|
|
static struct mtk_pm_qos_request apu_qos_ddr_req;
|
||
|
|
static struct pm_qos_request apu_qos_cpu_dma_req;
|
||
|
|
struct mutex apu_qos_boost_mtx;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/* register to apusys power on callback */
|
||
|
|
static void notify_sspm_apusys_on(void)
|
||
|
|
{
|
||
|
|
LOG_DEBUG("+\n");
|
||
|
|
|
||
|
|
qos_sram_write(APU_CLK, 1);
|
||
|
|
|
||
|
|
LOG_DEBUG("-\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
/* register to apusys power off callback(before power off) */
|
||
|
|
static void notify_sspm_apusys_off(void)
|
||
|
|
{
|
||
|
|
int bw_nord = 0;
|
||
|
|
|
||
|
|
LOG_DEBUG("+\n");
|
||
|
|
|
||
|
|
qos_sram_write(APU_CLK, 0);
|
||
|
|
while (bw_nord == 0) {
|
||
|
|
bw_nord = qos_sram_read(APU_BW_NORD);
|
||
|
|
udelay(500);
|
||
|
|
LOG_DEBUG("wait SSPM bw_nord");
|
||
|
|
}
|
||
|
|
|
||
|
|
LOG_DEBUG("-\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline void enque_qos_wq(struct work_struct *work)
|
||
|
|
{
|
||
|
|
schedule_work(work);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int add_qos_request(struct mtk_pm_qos_request *req)
|
||
|
|
{
|
||
|
|
#ifndef APU_QOS_DVFSRC
|
||
|
|
mtk_pm_qos_add_request(req, PM_QOS_APU_MEMORY_BANDWIDTH,
|
||
|
|
PM_QOS_DEFAULT_VALUE);
|
||
|
|
#endif
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void update_qos_request(struct mtk_pm_qos_request *req, uint32_t val)
|
||
|
|
{
|
||
|
|
LOG_DEBUG("bw = %d\n", val);
|
||
|
|
#ifndef APU_QOS_DVFSRC
|
||
|
|
mtk_pm_qos_update_request(req, val);
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
static int destroy_qos_request(struct mtk_pm_qos_request *req)
|
||
|
|
{
|
||
|
|
#ifndef APU_QOS_DVFSRC
|
||
|
|
mtk_pm_qos_update_request(req, PM_QOS_APU_MEMORY_BANDWIDTH_DEFAULT_VALUE);
|
||
|
|
mtk_pm_qos_remove_request(req);
|
||
|
|
#endif
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void qos_timer_func(struct timer_list *t)
|
||
|
|
{
|
||
|
|
struct qos_counter *counter = &qos_counter;
|
||
|
|
|
||
|
|
LOG_DETAIL("+\n");
|
||
|
|
|
||
|
|
/* queue work because mutex sleep must be happened */
|
||
|
|
enque_qos_wq(&qos_work);
|
||
|
|
mod_timer(&counter->qos_timer,
|
||
|
|
jiffies + msecs_to_jiffies(DEFAUTL_QOS_POLLING_TIME));
|
||
|
|
|
||
|
|
LOG_DETAIL("-\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* create timer to count current bandwidth of apu engines each 16ms
|
||
|
|
* timer will schedule work to wq when time's up
|
||
|
|
* must call with list_mtx locked
|
||
|
|
*/
|
||
|
|
static void apu_qos_timer_start(void)
|
||
|
|
{
|
||
|
|
struct qos_counter *counter = &qos_counter;
|
||
|
|
struct qos_bound *qos_info = NULL;
|
||
|
|
int i;
|
||
|
|
|
||
|
|
LOG_DEBUG("+\n");
|
||
|
|
|
||
|
|
/* get qos bound */
|
||
|
|
qos_info = get_qos_bound();
|
||
|
|
if (qos_info == NULL) {
|
||
|
|
LOG_ERR("get qos_info fail\n");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (i = 0; i < NR_APU_QOS_ENGINE; i++) {
|
||
|
|
engine_pm_qos_counter[i].last_report_bw = 0;
|
||
|
|
engine_pm_qos_counter[i].last_idx = qos_info->idx;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* setup timer */
|
||
|
|
timer_setup(&counter->qos_timer, &qos_timer_func, 0);
|
||
|
|
|
||
|
|
/* record wait time in counter */
|
||
|
|
counter->wait_ms = DEFAUTL_QOS_POLLING_TIME;
|
||
|
|
mod_timer(&counter->qos_timer, jiffies + msecs_to_jiffies(DEFAUTL_QOS_POLLING_TIME));
|
||
|
|
|
||
|
|
qos_timer_exist = true;
|
||
|
|
|
||
|
|
LOG_DEBUG("-\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* delete timer
|
||
|
|
* update pm qos request to default value
|
||
|
|
* must call with list_mtx locked
|
||
|
|
*/
|
||
|
|
static void apu_qos_timer_end(void)
|
||
|
|
{
|
||
|
|
struct qos_counter *counter = &qos_counter;
|
||
|
|
|
||
|
|
LOG_DEBUG("+\n");
|
||
|
|
|
||
|
|
if (qos_timer_exist) {
|
||
|
|
qos_timer_exist = false;
|
||
|
|
del_timer_sync(&counter->qos_timer);
|
||
|
|
}
|
||
|
|
|
||
|
|
LOG_DEBUG("-\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
void apu_qos_on(void)
|
||
|
|
{
|
||
|
|
LOG_DEBUG("+\n");
|
||
|
|
|
||
|
|
notify_sspm_apusys_on();
|
||
|
|
#ifdef MNOC_QOS_DEBOUNCE
|
||
|
|
mutex_lock(&(qos_counter.list_mtx));
|
||
|
|
apu_qos_timer_start();
|
||
|
|
mutex_unlock((&qos_counter.list_mtx));
|
||
|
|
#endif
|
||
|
|
#if MNOC_QOS_BOOST_ENABLE
|
||
|
|
mutex_lock(&apu_qos_boost_mtx);
|
||
|
|
apusys_on_flag = true;
|
||
|
|
apu_qos_boost_start();
|
||
|
|
mutex_unlock(&apu_qos_boost_mtx);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
LOG_DEBUG("-\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
void apu_qos_off(void)
|
||
|
|
{
|
||
|
|
#ifdef MNOC_QOS_DEBOUNCE
|
||
|
|
int i = 0;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
LOG_DEBUG("+\n");
|
||
|
|
|
||
|
|
#ifdef MNOC_QOS_DEBOUNCE
|
||
|
|
mutex_lock(&(qos_counter.list_mtx));
|
||
|
|
apu_qos_timer_end();
|
||
|
|
mutex_unlock(&(qos_counter.list_mtx));
|
||
|
|
/* make sure no work_func running after timer delete */
|
||
|
|
cancel_work_sync(&qos_work);
|
||
|
|
for (i = 0; i < NR_APU_QOS_ENGINE; i++) {
|
||
|
|
update_qos_request(&(engine_pm_qos_counter[i].qos_req),
|
||
|
|
PM_QOS_APU_MEMORY_BANDWIDTH_DEFAULT_VALUE);
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
#if MNOC_QOS_BOOST_ENABLE
|
||
|
|
mutex_lock(&apu_qos_boost_mtx);
|
||
|
|
apu_qos_boost_end();
|
||
|
|
apusys_on_flag = false;
|
||
|
|
mutex_unlock(&apu_qos_boost_mtx);
|
||
|
|
#endif
|
||
|
|
#ifdef APU_QOS_IPUIF_ADJUST
|
||
|
|
apu_bw_vcore_opp = NR_APU_VCORE_OPP - 1;
|
||
|
|
apu_qos_set_vcore(vcore_opp_map[apu_bw_vcore_opp]);
|
||
|
|
#endif
|
||
|
|
notify_sspm_apusys_off();
|
||
|
|
|
||
|
|
LOG_DEBUG("-\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
static void update_cmd_qos(struct qos_bound *qos_info, struct cmd_qos *cmd_qos)
|
||
|
|
{
|
||
|
|
unsigned int idx = 0, qos_smi_idx = 0;
|
||
|
|
|
||
|
|
/* sample device has no BW */
|
||
|
|
if (cmd_qos->core < NR_APU_QOS_ENGINE)
|
||
|
|
qos_smi_idx = get_qosbound_enum(cmd_qos->core);
|
||
|
|
|
||
|
|
/* sum current bw value to cmd_qos */
|
||
|
|
mutex_lock(&cmd_qos->mtx);
|
||
|
|
idx = cmd_qos->last_idx;
|
||
|
|
|
||
|
|
if (idx >= QOS_BOUND_BUF_SIZE) {
|
||
|
|
LOG_ERR("idx(%d) out of bound\n", idx);
|
||
|
|
idx = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
while (idx != ((qos_info->idx + 1) % MTK_QOS_BUF_SIZE)) {
|
||
|
|
if (cmd_qos->core < NR_APU_QOS_ENGINE)
|
||
|
|
cmd_qos->total_bw +=
|
||
|
|
qos_info->stats[idx].smibw_mon[qos_smi_idx];
|
||
|
|
cmd_qos->count++;
|
||
|
|
idx = (idx + 1) % MTK_QOS_BUF_SIZE;
|
||
|
|
}
|
||
|
|
LOG_DETAIL("(0x%llx/0x%llx)idx(%d ~ %d)\n", cmd_qos->cmd_id,
|
||
|
|
cmd_qos->sub_cmd_id, cmd_qos->last_idx, idx);
|
||
|
|
/* update last idx */
|
||
|
|
cmd_qos->last_idx = idx;
|
||
|
|
mutex_unlock(&cmd_qos->mtx);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* called by timer up, update average bw according to idx/last_idx */
|
||
|
|
static int update_cmd_qos_list_locked(struct qos_bound *qos_info)
|
||
|
|
{
|
||
|
|
struct cmd_qos *cmd_qos = NULL;
|
||
|
|
struct qos_counter *counter = &qos_counter;
|
||
|
|
|
||
|
|
LOG_DETAIL("+\n");
|
||
|
|
|
||
|
|
/* mutex_lock(&counter->list_mtx); */
|
||
|
|
|
||
|
|
list_for_each_entry(cmd_qos, &counter->list, list) {
|
||
|
|
if (cmd_qos->status == CMD_RUNNING)
|
||
|
|
update_cmd_qos(qos_info, cmd_qos);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* mutex_unlock(&counter->list_mtx); */
|
||
|
|
|
||
|
|
LOG_DETAIL("-\n");
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int enque_cmd_qos(uint64_t cmd_id,
|
||
|
|
uint64_t sub_cmd_id, int core, uint32_t boost_val)
|
||
|
|
{
|
||
|
|
struct qos_counter *counter = &qos_counter;
|
||
|
|
struct qos_bound *qos_info = NULL;
|
||
|
|
struct cmd_qos *cmd_qos = NULL;
|
||
|
|
|
||
|
|
LOG_DEBUG("+\n");
|
||
|
|
|
||
|
|
LOG_DEBUG("cmd_qos(0x%llx/0x%llx/%d/%d)\n",
|
||
|
|
cmd_id, sub_cmd_id, core, boost_val);
|
||
|
|
|
||
|
|
/* sample device has no BW */
|
||
|
|
if (core < NR_APU_QOS_ENGINE) {
|
||
|
|
engine_cmd_cntr[core] += 1;
|
||
|
|
/* only allow boost val ascendance for work_func processing */
|
||
|
|
if (engine_cmd_cntr[core] == 0 ||
|
||
|
|
boost_val > engine_boost_val[core])
|
||
|
|
engine_boost_val[core] = boost_val;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* alloc cmd_qos */
|
||
|
|
cmd_qos = kzalloc(sizeof(struct cmd_qos), GFP_KERNEL);
|
||
|
|
if (cmd_qos == NULL) {
|
||
|
|
LOG_ERR("alloc cmd_qos(0x%llx/0x%llx) fail\n",
|
||
|
|
cmd_id, sub_cmd_id);
|
||
|
|
return -1;
|
||
|
|
};
|
||
|
|
|
||
|
|
/* get qos information */
|
||
|
|
qos_info = get_qos_bound();
|
||
|
|
if (qos_info == NULL) {
|
||
|
|
LOG_ERR("get info fail\n");
|
||
|
|
kfree(cmd_qos);
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* init cmd_qos */
|
||
|
|
mutex_init(&cmd_qos->mtx);
|
||
|
|
|
||
|
|
/* critical session, modify cmd_qos's idx */
|
||
|
|
mutex_lock(&cmd_qos->mtx);
|
||
|
|
cmd_qos->cmd_id = cmd_id;
|
||
|
|
cmd_qos->sub_cmd_id = sub_cmd_id;
|
||
|
|
cmd_qos->core = core;
|
||
|
|
cmd_qos->status = CMD_RUNNING;
|
||
|
|
cmd_qos->last_idx = qos_info->idx;
|
||
|
|
cmd_qos->total_bw = 0;
|
||
|
|
mutex_unlock(&cmd_qos->mtx);
|
||
|
|
|
||
|
|
/* add to counter's list */
|
||
|
|
/* mutex_lock(&counter->list_mtx); */
|
||
|
|
list_add_tail(&cmd_qos->list, &counter->list);
|
||
|
|
/* mutex_unlock(&counter->list_mtx); */
|
||
|
|
|
||
|
|
LOG_DEBUG("-\n");
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int deque_cmd_qos(struct cmd_qos *cmd_qos)
|
||
|
|
{
|
||
|
|
/* struct qos_counter *counter = &qos_counter; */
|
||
|
|
int avg_bw = 0;
|
||
|
|
|
||
|
|
LOG_DEBUG("+\n");
|
||
|
|
|
||
|
|
/* delete cmd_qos from counter's list */
|
||
|
|
/* mutex_lock(&counter->list_mtx); */
|
||
|
|
list_del(&cmd_qos->list);
|
||
|
|
/* mutex_unlock(&counter->list_mtx); */
|
||
|
|
|
||
|
|
LOG_DEBUG("cmd_qos = %p\n", cmd_qos);
|
||
|
|
|
||
|
|
/* average bw */
|
||
|
|
if (cmd_qos->count != 0)
|
||
|
|
avg_bw = cmd_qos->total_bw / cmd_qos->count;
|
||
|
|
else
|
||
|
|
avg_bw = cmd_qos->total_bw;
|
||
|
|
|
||
|
|
LOG_DEBUG("cmd(0x%llx/0x%llx):bw(%d/%d)\n", cmd_qos->cmd_id,
|
||
|
|
cmd_qos->sub_cmd_id, avg_bw, cmd_qos->total_bw);
|
||
|
|
|
||
|
|
/* sample device has no BW */
|
||
|
|
if (cmd_qos->core < NR_APU_QOS_ENGINE) {
|
||
|
|
engine_cmd_cntr[cmd_qos->core] -= 1;
|
||
|
|
/*
|
||
|
|
* if (engine_cmd_cntr[cmd_qos->core] == 0)
|
||
|
|
* engine_boost_val[cmd_qos->core] = 0;
|
||
|
|
*/
|
||
|
|
}
|
||
|
|
|
||
|
|
/* free cmd_qos */
|
||
|
|
kfree(cmd_qos);
|
||
|
|
|
||
|
|
LOG_DEBUG("-\n");
|
||
|
|
|
||
|
|
return avg_bw;
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifdef APU_QOS_IPUIF_ADJUST
|
||
|
|
static unsigned int apu_bw_round_down(unsigned int bw)
|
||
|
|
{
|
||
|
|
unsigned int ret;
|
||
|
|
|
||
|
|
/* PMQoS will do rounding to SW BW */
|
||
|
|
if (bw % 100 == 0)
|
||
|
|
ret = bw;
|
||
|
|
else
|
||
|
|
ret = (bw / 100) * 100;
|
||
|
|
/* ret = ((bw / 100) + 1) * 100; */
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
static void qos_work_func(struct work_struct *work)
|
||
|
|
{
|
||
|
|
struct qos_bound *qos_info = NULL;
|
||
|
|
struct engine_pm_qos_counter *counter = NULL;
|
||
|
|
int qos_smi_idx = 0;
|
||
|
|
int i = 0, idx = 0, current_idx;
|
||
|
|
unsigned int peak_bw = 0, total_bw = 0, avg_bw = 0;
|
||
|
|
unsigned int cnt = 0, bw = 0, report_bw = 0;
|
||
|
|
#ifdef APU_QOS_IPUIF_ADJUST
|
||
|
|
unsigned int total_apu_bw = 0, new_apu_vcore_opp = 0;
|
||
|
|
#endif
|
||
|
|
#if MNOC_TIME_PROFILE
|
||
|
|
struct timeval begin, end;
|
||
|
|
unsigned long val;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
LOG_DETAIL("+\n");
|
||
|
|
|
||
|
|
#if MNOC_TIME_PROFILE
|
||
|
|
do_gettimeofday(&begin);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/* get qos bound */
|
||
|
|
qos_info = get_qos_bound();
|
||
|
|
if (qos_info == NULL) {
|
||
|
|
LOG_ERR("get qos_info fail\n");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
current_idx = qos_info->idx;
|
||
|
|
|
||
|
|
for (i = 0; i < NR_APU_QOS_ENGINE; i++) {
|
||
|
|
peak_bw = 0;
|
||
|
|
total_bw = 0;
|
||
|
|
cnt = 0;
|
||
|
|
counter = &engine_pm_qos_counter[i];
|
||
|
|
qos_smi_idx = get_qosbound_enum(i);
|
||
|
|
/* find peak bandwidth consumption */
|
||
|
|
idx = counter->last_idx;
|
||
|
|
/* prevent overflow */
|
||
|
|
if (idx == current_idx)
|
||
|
|
continue;
|
||
|
|
do {
|
||
|
|
idx = (idx + 1) % MTK_QOS_BUF_SIZE;
|
||
|
|
bw = qos_info->stats[idx].smibw_mon[qos_smi_idx];
|
||
|
|
total_bw += bw;
|
||
|
|
cnt++;
|
||
|
|
peak_bw = peak_bw > bw ? peak_bw : bw;
|
||
|
|
} while (idx != qos_info->idx);
|
||
|
|
|
||
|
|
LOG_DETAIL("idx[%d](%d ~ %d)\n", i, counter->last_idx, idx);
|
||
|
|
|
||
|
|
counter->last_idx = idx;
|
||
|
|
avg_bw = total_bw/cnt;
|
||
|
|
|
||
|
|
#ifdef MNOC_QOS_DEBOUNCE
|
||
|
|
if (engine_boost_val[i] == 0)
|
||
|
|
report_bw = avg_bw;
|
||
|
|
else
|
||
|
|
report_bw = peak_bw;
|
||
|
|
#else
|
||
|
|
report_bw = peak_bw;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifdef APU_QOS_IPUIF_ADJUST
|
||
|
|
report_bw = apu_bw_round_down(report_bw);
|
||
|
|
total_apu_bw += report_bw;
|
||
|
|
#endif
|
||
|
|
/* update peak bw */
|
||
|
|
if (counter->last_report_bw != report_bw) {
|
||
|
|
counter->last_report_bw = report_bw;
|
||
|
|
update_qos_request(&counter->qos_req, report_bw);
|
||
|
|
}
|
||
|
|
|
||
|
|
LOG_DETAIL("%d: boost_val = %d, bw(%d/%d/%d)\n",
|
||
|
|
i, engine_boost_val[i], report_bw, peak_bw, avg_bw);
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifdef APU_QOS_IPUIF_ADJUST
|
||
|
|
new_apu_vcore_opp = 0;
|
||
|
|
for (i = NR_APU_VCORE_OPP - 1; i >= 0 ; i--) {
|
||
|
|
if (total_apu_bw >= apu_vcore_bw_opp_tab[i])
|
||
|
|
new_apu_vcore_opp = i;
|
||
|
|
else
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
#if MNOC_QOS_BOOST_ENABLE
|
||
|
|
mutex_lock(&apu_qos_boost_mtx);
|
||
|
|
if (new_apu_vcore_opp != apu_bw_vcore_opp &&
|
||
|
|
apu_qos_boost_flag == false) {
|
||
|
|
apu_bw_vcore_opp = new_apu_vcore_opp;
|
||
|
|
apu_qos_set_vcore(vcore_opp_map[apu_bw_vcore_opp]);
|
||
|
|
}
|
||
|
|
mutex_unlock(&apu_qos_boost_mtx);
|
||
|
|
#else
|
||
|
|
if (new_apu_vcore_opp != apu_bw_vcore_opp) {
|
||
|
|
apu_bw_vcore_opp = new_apu_vcore_opp;
|
||
|
|
apu_qos_set_vcore(vcore_opp_map[apu_bw_vcore_opp]);
|
||
|
|
}
|
||
|
|
#endif /* MNOC_QOS_BOOST_ENABLE */
|
||
|
|
#endif /* APU_QOS_IPUIF_ADJUST */
|
||
|
|
|
||
|
|
mutex_lock(&(qos_counter.list_mtx));
|
||
|
|
update_cmd_qos_list_locked(qos_info);
|
||
|
|
mutex_unlock(&(qos_counter.list_mtx));
|
||
|
|
|
||
|
|
#if MNOC_TIME_PROFILE
|
||
|
|
do_gettimeofday(&end);
|
||
|
|
val = (end.tv_sec - begin.tv_sec) * 1000000;
|
||
|
|
val += (end.tv_usec - begin.tv_usec);
|
||
|
|
/* LOG_DEBUG("val = %d us\n", val); */
|
||
|
|
sum_work_func += val;
|
||
|
|
cnt_work_func += 1;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
LOG_DETAIL("-\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* called when apusys enter suspend
|
||
|
|
*/
|
||
|
|
void apu_qos_suspend(void)
|
||
|
|
{
|
||
|
|
struct qos_counter *counter = &qos_counter;
|
||
|
|
struct cmd_qos *pos;
|
||
|
|
struct qos_bound *qos_info = NULL;
|
||
|
|
|
||
|
|
LOG_DEBUG("+\n");
|
||
|
|
|
||
|
|
/* get qos information */
|
||
|
|
qos_info = get_qos_bound();
|
||
|
|
if (qos_info == NULL) {
|
||
|
|
LOG_ERR("get info fail\n");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
mutex_lock(&counter->list_mtx);
|
||
|
|
|
||
|
|
/* no need to do suspend if no cmd exist */
|
||
|
|
if (list_empty(&counter->list)) {
|
||
|
|
mutex_unlock(&counter->list_mtx);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
apu_qos_timer_end();
|
||
|
|
|
||
|
|
list_for_each_entry(pos, &counter->list, list) {
|
||
|
|
/* update running cmd to latest state before enter suspend */
|
||
|
|
if (pos->status == CMD_RUNNING)
|
||
|
|
update_cmd_qos(qos_info, pos);
|
||
|
|
}
|
||
|
|
|
||
|
|
mutex_unlock(&counter->list_mtx);
|
||
|
|
|
||
|
|
LOG_DEBUG("-\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* called when apusys resume
|
||
|
|
*/
|
||
|
|
void apu_qos_resume(void)
|
||
|
|
{
|
||
|
|
struct qos_counter *counter = &qos_counter;
|
||
|
|
struct cmd_qos *pos;
|
||
|
|
struct qos_bound *qos_info = NULL;
|
||
|
|
|
||
|
|
LOG_DEBUG("+\n");
|
||
|
|
|
||
|
|
/* get qos information */
|
||
|
|
qos_info = get_qos_bound();
|
||
|
|
if (qos_info == NULL) {
|
||
|
|
LOG_ERR("get info fail\n");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
mutex_lock(&counter->list_mtx);
|
||
|
|
|
||
|
|
/* no need to do suspend if no cmd exist */
|
||
|
|
if (list_empty(&counter->list)) {
|
||
|
|
mutex_unlock(&counter->list_mtx);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
list_for_each_entry(pos, &counter->list, list) {
|
||
|
|
if (pos->status == CMD_RUNNING) {
|
||
|
|
/* update last_idx to current pm qos idx */
|
||
|
|
mutex_lock(&pos->mtx);
|
||
|
|
pos->last_idx = qos_info->idx;
|
||
|
|
mutex_unlock(&pos->mtx);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
apu_qos_timer_start();
|
||
|
|
|
||
|
|
mutex_unlock(&counter->list_mtx);
|
||
|
|
|
||
|
|
LOG_DEBUG("-\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* enque cmd to qos_counter's linked list
|
||
|
|
* if list is empty before enqueue, start qos timer
|
||
|
|
*/
|
||
|
|
int apu_cmd_qos_start(uint64_t cmd_id, uint64_t sub_cmd_id,
|
||
|
|
int dev_type, int dev_core, uint32_t boost_val)
|
||
|
|
{
|
||
|
|
struct qos_counter *counter = &qos_counter;
|
||
|
|
struct cmd_qos *pos;
|
||
|
|
struct qos_bound *qos_info = NULL;
|
||
|
|
int core;
|
||
|
|
#if MNOC_TIME_PROFILE
|
||
|
|
struct timeval begin, end;
|
||
|
|
unsigned long val;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
LOG_DEBUG("+\n");
|
||
|
|
|
||
|
|
#if MNOC_TIME_PROFILE
|
||
|
|
do_gettimeofday(&begin);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
core = apusys_dev_to_core_id(dev_type, dev_core);
|
||
|
|
|
||
|
|
if (core == -1) {
|
||
|
|
LOG_ERR("Invalid device(%d/%d)", dev_type, dev_core);
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* get qos information */
|
||
|
|
qos_info = get_qos_bound();
|
||
|
|
if (qos_info == NULL) {
|
||
|
|
LOG_ERR("get info fail\n");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
mutex_lock(&counter->list_mtx);
|
||
|
|
|
||
|
|
#ifndef MNOC_QOS_DEBOUNCE
|
||
|
|
/* start timer if cmd list empty */
|
||
|
|
if (list_empty(&counter->list))
|
||
|
|
apu_qos_timer_start();
|
||
|
|
#endif
|
||
|
|
|
||
|
|
list_for_each_entry(pos, &counter->list, list) {
|
||
|
|
/* search if cmd already exist */
|
||
|
|
if (pos->cmd_id == cmd_id && pos->sub_cmd_id == sub_cmd_id
|
||
|
|
&& pos->core == core) {
|
||
|
|
LOG_DEBUG("resume cmd(0x%llx/0x%llx/%d)\n",
|
||
|
|
cmd_id, sub_cmd_id, core);
|
||
|
|
mutex_lock(&pos->mtx);
|
||
|
|
pos->status = CMD_RUNNING;
|
||
|
|
mutex_unlock(&pos->mtx);
|
||
|
|
|
||
|
|
mutex_unlock(&counter->list_mtx);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* enque cmd to counter's list */
|
||
|
|
if (enque_cmd_qos(cmd_id, sub_cmd_id, core, boost_val)) {
|
||
|
|
LOG_ERR("enque cmd qos fail\n");
|
||
|
|
mutex_unlock(&counter->list_mtx);
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
mutex_unlock(&counter->list_mtx);
|
||
|
|
|
||
|
|
#if MNOC_TIME_PROFILE
|
||
|
|
do_gettimeofday(&end);
|
||
|
|
mutex_lock(&counter->list_mtx);
|
||
|
|
val = (end.tv_sec - begin.tv_sec) * 1000000;
|
||
|
|
val += (end.tv_usec - begin.tv_usec);
|
||
|
|
/* LOG_INFO("val = %d us\n", val); */
|
||
|
|
sum_start += val;
|
||
|
|
cnt_start += 1;
|
||
|
|
mutex_unlock(&counter->list_mtx);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
LOG_DEBUG("-\n");
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
EXPORT_SYMBOL(apu_cmd_qos_start);
|
||
|
|
|
||
|
|
/*
|
||
|
|
* suspend cmd due to preemption
|
||
|
|
* set cmd status from CMD_RUNNING to CMD_BLOCKED
|
||
|
|
*/
|
||
|
|
int apu_cmd_qos_suspend(uint64_t cmd_id, uint64_t sub_cmd_id,
|
||
|
|
int dev_type, int dev_core)
|
||
|
|
{
|
||
|
|
struct qos_counter *counter = &qos_counter;
|
||
|
|
struct cmd_qos *cmd_qos = NULL, *pos;
|
||
|
|
struct qos_bound *qos_info = NULL;
|
||
|
|
int core;
|
||
|
|
#if MNOC_TIME_PROFILE
|
||
|
|
struct timeval begin, end;
|
||
|
|
unsigned long val;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
LOG_DEBUG("+\n");
|
||
|
|
|
||
|
|
#if MNOC_TIME_PROFILE
|
||
|
|
do_gettimeofday(&begin);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
core = apusys_dev_to_core_id(dev_type, dev_core);
|
||
|
|
|
||
|
|
/* get qos information */
|
||
|
|
qos_info = get_qos_bound();
|
||
|
|
if (qos_info == NULL) {
|
||
|
|
LOG_ERR("get info fail\n");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
mutex_lock(&counter->list_mtx);
|
||
|
|
|
||
|
|
list_for_each_entry(pos, &counter->list, list) {
|
||
|
|
if (pos->cmd_id == cmd_id && pos->sub_cmd_id == sub_cmd_id
|
||
|
|
&& pos->core == core) {
|
||
|
|
cmd_qos = pos;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (cmd_qos == NULL) {
|
||
|
|
LOG_ERR("Can not find cmd(0x%llx/0x%llx/%d)\n",
|
||
|
|
cmd_id, sub_cmd_id, core);
|
||
|
|
mutex_unlock(&counter->list_mtx);
|
||
|
|
return -1;
|
||
|
|
} else if (cmd_qos->status == CMD_BLOCKED) {
|
||
|
|
LOG_ERR("cmd(0x%llx/0x%llx/%d) already in suspend\n",
|
||
|
|
cmd_id, sub_cmd_id, core);
|
||
|
|
mutex_unlock(&counter->list_mtx);
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
mutex_lock(&cmd_qos->mtx);
|
||
|
|
cmd_qos->status = CMD_BLOCKED;
|
||
|
|
mutex_unlock(&cmd_qos->mtx);
|
||
|
|
|
||
|
|
/* update cmd qos of preempted cmd to latest status */
|
||
|
|
update_cmd_qos(qos_info, cmd_qos);
|
||
|
|
|
||
|
|
mutex_unlock(&counter->list_mtx);
|
||
|
|
|
||
|
|
LOG_DEBUG("-\n");
|
||
|
|
|
||
|
|
#if MNOC_TIME_PROFILE
|
||
|
|
do_gettimeofday(&end);
|
||
|
|
mutex_lock(&counter->list_mtx);
|
||
|
|
val = (end.tv_sec - begin.tv_sec) * 1000000;
|
||
|
|
val += (end.tv_usec - begin.tv_usec);
|
||
|
|
/* LOG_INFO("val = %d us\n", val); */
|
||
|
|
sum_suspend += val;
|
||
|
|
cnt_suspend += 1;
|
||
|
|
mutex_unlock(&counter->list_mtx);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
EXPORT_SYMBOL(apu_cmd_qos_suspend);
|
||
|
|
|
||
|
|
/*
|
||
|
|
* deque cmd from qos_counter's linked list
|
||
|
|
* if list becomes empty after dequeue, delete qos timer
|
||
|
|
*/
|
||
|
|
int apu_cmd_qos_end(uint64_t cmd_id, uint64_t sub_cmd_id,
|
||
|
|
int dev_type, int dev_core)
|
||
|
|
{
|
||
|
|
struct qos_counter *counter = &qos_counter;
|
||
|
|
struct cmd_qos *cmd_qos = NULL, *pos;
|
||
|
|
struct qos_bound *qos_info = NULL;
|
||
|
|
int core;
|
||
|
|
int bw = 0, total_bw = 0, total_count = 0;
|
||
|
|
#ifndef MNOC_QOS_DEBOUNCE
|
||
|
|
int i;
|
||
|
|
#endif
|
||
|
|
#if MNOC_TIME_PROFILE
|
||
|
|
struct timeval begin, end;
|
||
|
|
unsigned long val;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
LOG_DEBUG("+\n");
|
||
|
|
|
||
|
|
#if MNOC_TIME_PROFILE
|
||
|
|
do_gettimeofday(&begin);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
core = apusys_dev_to_core_id(dev_type, dev_core);
|
||
|
|
|
||
|
|
/* get qos information */
|
||
|
|
qos_info = get_qos_bound();
|
||
|
|
if (qos_info == NULL) {
|
||
|
|
LOG_ERR("get info fail\n");
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
mutex_lock(&counter->list_mtx);
|
||
|
|
|
||
|
|
list_for_each_entry(pos, &counter->list, list) {
|
||
|
|
if (pos->cmd_id == cmd_id && pos->sub_cmd_id == sub_cmd_id
|
||
|
|
&& pos->core == core) {
|
||
|
|
cmd_qos = pos;
|
||
|
|
/* core = cmd_qos->core; */
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (cmd_qos == NULL) {
|
||
|
|
LOG_ERR("Can not find cmd(0x%llx/0x%llx/%d)\n",
|
||
|
|
cmd_id, sub_cmd_id, core);
|
||
|
|
mutex_unlock(&counter->list_mtx);
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* update all cmd qos info */
|
||
|
|
update_cmd_qos_list_locked(qos_info);
|
||
|
|
|
||
|
|
total_bw = cmd_qos->total_bw;
|
||
|
|
total_count = cmd_qos->count;
|
||
|
|
|
||
|
|
/* deque cmd to counter's list */
|
||
|
|
bw = deque_cmd_qos(cmd_qos);
|
||
|
|
|
||
|
|
#ifndef MNOC_QOS_DEBOUNCE
|
||
|
|
/* delete timer if cmd list empty */
|
||
|
|
if (list_empty(&counter->list))
|
||
|
|
apu_qos_timer_end();
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#if 0
|
||
|
|
/* due to preemption,
|
||
|
|
* there may be multiple cmds running on the same core,
|
||
|
|
* need to subtract total_bw and total_count from all cmds
|
||
|
|
* running on the same core to prevent recalculation
|
||
|
|
*/
|
||
|
|
list_for_each_entry(cmd_qos, &counter->list, list) {
|
||
|
|
if (cmd_qos->core == core) {
|
||
|
|
mutex_lock(&cmd_qos->mtx);
|
||
|
|
|
||
|
|
if (cmd_qos->total_bw < total_bw) {
|
||
|
|
/* ignore sample device */
|
||
|
|
if (cmd_qos->core < NR_APU_QOS_ENGINE)
|
||
|
|
LOG_ERR(
|
||
|
|
"cmd(0x%llx/0x%llx/%d) total_bw(%d) < %d",
|
||
|
|
cmd_qos->cmd_id,
|
||
|
|
cmd_qos->sub_cmd_id,
|
||
|
|
cmd_qos->core,
|
||
|
|
cmd_qos->total_bw, total_bw);
|
||
|
|
cmd_qos->total_bw = 0;
|
||
|
|
} else
|
||
|
|
cmd_qos->total_bw -= total_bw;
|
||
|
|
|
||
|
|
if (cmd_qos->count < total_count) {
|
||
|
|
/* ignore sample device */
|
||
|
|
if (cmd_qos->core < NR_APU_QOS_ENGINE)
|
||
|
|
LOG_ERR(
|
||
|
|
"cmd(0x%llx/0x%llx/%d) count(%d) < %d",
|
||
|
|
cmd_qos->cmd_id,
|
||
|
|
cmd_qos->sub_cmd_id,
|
||
|
|
cmd_qos->core,
|
||
|
|
cmd_qos->count, total_count);
|
||
|
|
cmd_qos->count = 0;
|
||
|
|
} else
|
||
|
|
cmd_qos->count -= total_count;
|
||
|
|
|
||
|
|
/* workaround to prevent including last
|
||
|
|
* cmd's bw due to monitor delay 1.26 ms
|
||
|
|
*/
|
||
|
|
cmd_qos->last_idx =
|
||
|
|
(qos_info->idx + 1) % MTK_QOS_BUF_SIZE;
|
||
|
|
|
||
|
|
mutex_unlock(&cmd_qos->mtx);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
mutex_unlock(&counter->list_mtx);
|
||
|
|
|
||
|
|
#ifndef MNOC_QOS_DEBOUNCE
|
||
|
|
if (!qos_timer_exist) {
|
||
|
|
/* make sure no work_func running after timer delete */
|
||
|
|
cancel_work_sync(&qos_work);
|
||
|
|
for (i = 0; i < NR_APU_QOS_ENGINE; i++) {
|
||
|
|
update_qos_request(&(engine_pm_qos_counter[i].qos_req),
|
||
|
|
PM_QOS_APU_MEMORY_BANDWIDTH_DEFAULT_VALUE);
|
||
|
|
}
|
||
|
|
#if MNOC_QOS_BOOST_ENABLE
|
||
|
|
mutex_lock(&apu_qos_boost_mtx);
|
||
|
|
if (apu_qos_boost_flag == false) {
|
||
|
|
apu_bw_vcore_opp = NR_APU_VCORE_OPP - 1;
|
||
|
|
apu_qos_set_vcore(vcore_opp_map[apu_bw_vcore_opp]);
|
||
|
|
}
|
||
|
|
mutex_unlock(&apu_qos_boost_mtx);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
LOG_DEBUG("-\n");
|
||
|
|
|
||
|
|
#if MNOC_TIME_PROFILE
|
||
|
|
do_gettimeofday(&end);
|
||
|
|
mutex_lock(&counter->list_mtx);
|
||
|
|
val = (end.tv_sec - begin.tv_sec) * 1000000;
|
||
|
|
val += (end.tv_usec - begin.tv_usec);
|
||
|
|
/* LOG_INFO("val = %d us\n", val); */
|
||
|
|
sum_end += val;
|
||
|
|
cnt_end += 1;
|
||
|
|
mutex_unlock(&counter->list_mtx);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/* return 1 if bw = 0 (eara requirement) */
|
||
|
|
return bw == 0 ? 1 : bw;
|
||
|
|
}
|
||
|
|
EXPORT_SYMBOL(apu_cmd_qos_end);
|
||
|
|
|
||
|
|
void apu_qos_boost_start(void)
|
||
|
|
{
|
||
|
|
LOG_DEBUG("+\n");
|
||
|
|
#if MNOC_QOS_BOOST_ENABLE
|
||
|
|
/* 6885: ~16G, 6873/6853: ~8G */
|
||
|
|
if (apu_qos_boost_flag == true && apusys_on_flag == true &&
|
||
|
|
apu_qos_boost_ddr_opp ==
|
||
|
|
MTK_PM_QOS_DDR_OPP_DEFAULT_VALUE) {
|
||
|
|
apu_qos_boost_ddr_opp = 0;
|
||
|
|
mtk_pm_qos_update_request(&apu_qos_ddr_req, 1);
|
||
|
|
pm_qos_update_request(&apu_qos_cpu_dma_req, 2);
|
||
|
|
#ifdef APU_QOS_IPUIF_ADJUST
|
||
|
|
apu_bw_vcore_opp = 2;
|
||
|
|
apu_qos_set_vcore(vcore_opp_map[apu_bw_vcore_opp]);
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
LOG_DEBUG("-\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
void apu_qos_boost_end(void)
|
||
|
|
{
|
||
|
|
LOG_DEBUG("+\n");
|
||
|
|
#if MNOC_QOS_BOOST_ENABLE
|
||
|
|
if (apu_qos_boost_ddr_opp == 0 && apusys_on_flag == true) {
|
||
|
|
#ifdef APU_QOS_IPUIF_ADJUST
|
||
|
|
apu_bw_vcore_opp = NR_APU_VCORE_OPP - 1;
|
||
|
|
apu_qos_set_vcore(vcore_opp_map[apu_bw_vcore_opp]);
|
||
|
|
#endif
|
||
|
|
apu_qos_boost_ddr_opp =
|
||
|
|
MTK_PM_QOS_DDR_OPP_DEFAULT_VALUE;
|
||
|
|
mtk_pm_qos_update_request(&apu_qos_ddr_req,
|
||
|
|
MTK_PM_QOS_DDR_OPP_DEFAULT_VALUE);
|
||
|
|
pm_qos_update_request(&apu_qos_cpu_dma_req,
|
||
|
|
PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE);
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
LOG_DEBUG("-\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* create qos workqueue for count bandwidth
|
||
|
|
* @call at module init
|
||
|
|
*/
|
||
|
|
void apu_qos_counter_init(struct device *dev)
|
||
|
|
{
|
||
|
|
int i = 0;
|
||
|
|
struct engine_pm_qos_counter *counter = NULL;
|
||
|
|
|
||
|
|
LOG_DEBUG("+\n");
|
||
|
|
|
||
|
|
qos_timer_exist = false;
|
||
|
|
|
||
|
|
/* init counter's list */
|
||
|
|
INIT_LIST_HEAD(&(qos_counter.list));
|
||
|
|
mutex_init(&(qos_counter.list_mtx));
|
||
|
|
|
||
|
|
/* init work and pm_qos_request */
|
||
|
|
INIT_WORK(&qos_work, &qos_work_func);
|
||
|
|
for (i = 0; i < NR_APU_QOS_ENGINE; i++) {
|
||
|
|
/* init engine_cmd_cntr for each engine */
|
||
|
|
engine_cmd_cntr[i] = 0;
|
||
|
|
counter = &engine_pm_qos_counter[i];
|
||
|
|
if (counter == NULL) {
|
||
|
|
LOG_ERR("get counter(%d) fail\n", i);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
counter->last_report_bw = 0;
|
||
|
|
counter->last_idx = 0;
|
||
|
|
counter->core = i;
|
||
|
|
add_qos_request(&counter->qos_req);
|
||
|
|
}
|
||
|
|
#if MNOC_QOS_BOOST_ENABLE
|
||
|
|
apu_qos_boost_flag = false;
|
||
|
|
apusys_on_flag = false;
|
||
|
|
mutex_init(&apu_qos_boost_mtx);
|
||
|
|
mtk_pm_qos_add_request(&apu_qos_ddr_req, MTK_PM_QOS_DDR_OPP,
|
||
|
|
MTK_PM_QOS_DDR_OPP_DEFAULT_VALUE);
|
||
|
|
pm_qos_add_request(&apu_qos_cpu_dma_req, PM_QOS_CPU_DMA_LATENCY,
|
||
|
|
PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE);
|
||
|
|
apu_qos_boost_ddr_opp = MTK_PM_QOS_DDR_OPP_DEFAULT_VALUE;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifdef APU_QOS_IPUIF_ADJUST
|
||
|
|
apu_bw_vcore_opp = NR_APU_VCORE_OPP - 1;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#if MNOC_TIME_PROFILE
|
||
|
|
sum_start = 0;
|
||
|
|
sum_suspend = 0;
|
||
|
|
sum_end = 0;
|
||
|
|
sum_work_func = 0;
|
||
|
|
cnt_start = 0;
|
||
|
|
cnt_suspend = 0;
|
||
|
|
cnt_end = 0;
|
||
|
|
cnt_work_func = 0;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
LOG_DEBUG("-\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* delete qos request
|
||
|
|
* @call at module exit
|
||
|
|
*/
|
||
|
|
void apu_qos_counter_destroy(struct device *dev)
|
||
|
|
{
|
||
|
|
int i = 0;
|
||
|
|
struct engine_pm_qos_counter *counter = NULL;
|
||
|
|
struct cmd_qos *cmd_qos, *pos;
|
||
|
|
|
||
|
|
LOG_DEBUG("+\n");
|
||
|
|
|
||
|
|
mutex_lock(&(qos_counter.list_mtx));
|
||
|
|
|
||
|
|
apu_qos_timer_end();
|
||
|
|
|
||
|
|
list_for_each_entry_safe(cmd_qos, pos, &(qos_counter.list), list) {
|
||
|
|
deque_cmd_qos(cmd_qos);
|
||
|
|
}
|
||
|
|
|
||
|
|
mutex_unlock(&(qos_counter.list_mtx));
|
||
|
|
|
||
|
|
/* make sure no work_func running after module exit */
|
||
|
|
cancel_work_sync(&qos_work);
|
||
|
|
|
||
|
|
/* remove pm_qos_request */
|
||
|
|
for (i = 0; i < NR_APU_QOS_ENGINE; i++) {
|
||
|
|
counter = &engine_pm_qos_counter[i];
|
||
|
|
if (counter == NULL) {
|
||
|
|
LOG_ERR("get counter(%d) fail\n", i);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
destroy_qos_request(&counter->qos_req);
|
||
|
|
}
|
||
|
|
#if MNOC_QOS_BOOST_ENABLE
|
||
|
|
mtk_pm_qos_update_request(&apu_qos_ddr_req,
|
||
|
|
MTK_PM_QOS_DDR_OPP_DEFAULT_VALUE);
|
||
|
|
mtk_pm_qos_remove_request(&apu_qos_ddr_req);
|
||
|
|
pm_qos_update_request(&apu_qos_cpu_dma_req,
|
||
|
|
PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE);
|
||
|
|
pm_qos_remove_request(&apu_qos_cpu_dma_req);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
LOG_DEBUG("-\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
/* ==================== for debug ==================== */
|
||
|
|
|
||
|
|
void print_cmd_qos_list(struct seq_file *m)
|
||
|
|
{
|
||
|
|
struct qos_counter *counter = &qos_counter;
|
||
|
|
struct cmd_qos *cmd_qos;
|
||
|
|
|
||
|
|
mutex_lock(&(qos_counter.list_mtx));
|
||
|
|
list_for_each_entry(cmd_qos, &counter->list, list) {
|
||
|
|
seq_printf(m, "cmd(0x%llx/0x%llx):\n",
|
||
|
|
cmd_qos->cmd_id, cmd_qos->sub_cmd_id);
|
||
|
|
seq_printf(m, "core = %d, status = %d\n",
|
||
|
|
cmd_qos->core, cmd_qos->status);
|
||
|
|
seq_printf(m, "total_bw = %d, last_idx = %d, count = %d\n",
|
||
|
|
cmd_qos->total_bw, cmd_qos->last_idx, cmd_qos->count);
|
||
|
|
}
|
||
|
|
mutex_unlock(&(qos_counter.list_mtx));
|
||
|
|
}
|
||
|
|
|
||
|
|
#else
|
||
|
|
|
||
|
|
void apu_qos_on(void)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
void apu_qos_off(void)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
void apu_qos_suspend(void)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
void apu_qos_resume(void)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
int apu_cmd_qos_start(uint64_t cmd_id, uint64_t sub_cmd_id,
|
||
|
|
int dev_type, int dev_core, uint32_t boost_val)
|
||
|
|
{
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
EXPORT_SYMBOL(apu_cmd_qos_start);
|
||
|
|
|
||
|
|
int apu_cmd_qos_suspend(uint64_t cmd_id,
|
||
|
|
uint64_t sub_cmd_id)
|
||
|
|
{
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
EXPORT_SYMBOL(apu_cmd_qos_suspend);
|
||
|
|
|
||
|
|
int apu_cmd_qos_end(uint64_t cmd_id, uint64_t sub_cmd_id)
|
||
|
|
{
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
EXPORT_SYMBOL(apu_cmd_qos_end);
|
||
|
|
|
||
|
|
void apu_qos_counter_init(struct device *dev)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
void apu_qos_counter_destroy(struct device *dev)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
void print_cmd_qos_list(struct seq_file *m)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
#endif /* MNOC_QOS_ENABLE */
|