// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2020 MediaTek Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "mtk_upower.h" #include #include #include "pob_qos.h" #include #include "mtk_ppm_api.h" #include "mtk_gpu_utility.h" #include #include "rs_usage.h" #include "rs_pfm.h" #include "rs_trace.h" #include "rs_base.h" #define TAG "RSUsage" //#define __BW_USE_EMIALL__ 1 enum { RSU_DEBUG_MANDATORY = 1U << 0, RSU_DEBUG_QOS = 1U << 1, RSU_DEBUG_CPU = 1U << 2, RSU_DEBUG_GPU = 1U << 3, RSU_DEBUG_APU = 1U << 4, RSU_DEBUG_MDLA = 1U << 5, RSU_DEBUG_VPU = 1U << 6, RSU_DEBUG_MAX = 1U << 7, }; static const char * const mask_string[] = { "MANDATORY", "QOS", "CPU", "GPU", "APU", "MDLA", "VPU", "MAX", }; #define rsu_systrace_c(mask, pid, val, fmt...) \ do { \ if (rsu_systrace_mask & mask) \ __rs_systrace_c(pid, val, fmt); \ } while (0) #define rsu_systrace_c_uint64(mask, pid, val, fmt...) \ do { \ if (rsu_systrace_mask & mask) \ __rs_systrace_c_uint64(pid, val, fmt); \ } while (0) #define rsu_systrace_b(mask, tgid, fmt, ...) \ do { \ if (rsu_systrace_mask & mask) \ __rs_systrace_b(tgid, fmt); \ } while (0) #define rsu_systrace_e(mask) \ do { \ if (rsu_systrace_mask & mask) \ __rs_systrace_e(); \ } while (0) #define rsu_systrace_c_log(pid, val, fmt...) \ rsu_systrace_c(RSU_DEBUG_MANDATORY, pid, val, fmt) #define rsu_systrace_c_uint64_log(pid, val, fmt...) \ rsu_systrace_c_uint64(RSU_DEBUG_MANDATORY, pid, val, fmt) #ifdef NR_FREQ_CPU struct rsu_cpu_cluster_info { unsigned int power[NR_FREQ_CPU]; unsigned int capacity_ratio[NR_FREQ_CPU]; unsigned int lastfreq; unsigned int ceiling_obv; }; struct rsu_cpu_info { struct rsu_cpu_cluster_info *cluster; u64 prev_idle_time; u64 prev_wall_time; }; #endif #ifdef __BW_USE_EMIALL__ enum RSU_NTF_PUSH_TYPE { RSU_NTF_QOSONOFF_SWITCH = 0x00, }; struct RSU_NTF_PUSH_TAG { enum RSU_NTF_PUSH_TYPE ePushType; int value; struct work_struct sWork; }; #endif static int nr_cpuclusters; static int nr_cpus; static struct rsu_cpu_info *cpu_info; static struct rsu_cpu_cluster_info *cpucluster_info; static struct kobject *rsu_kobj; static DEFINE_SPINLOCK(freq_slock); #ifdef __BW_USE_EMIALL__ static long long fpsgo_time_us; static long long nn_time_us; static DEFINE_MUTEX(rsu_qos_ntf_mutex); static int fpsgo_active; static int nn_active; static atomic_t last_bw_usage; static struct workqueue_struct *_gpRSUNotifyWorkQueue; static int rsu_qos_isON; #endif static DEFINE_MUTEX(rsu_vpu_ntf_mutex); static int *vpu_opp; static DEFINE_MUTEX(rsu_mdla_ntf_mutex); static int *mdla_opp; static uint32_t rsu_systrace_mask; static int arch_get_nr_clusters(void) { int cpu; int nr_clusters = 0; int cluster_id, prev_cluster_id = -1; for_each_possible_cpu(cpu) { cluster_id = topology_physical_package_id(cpu); if (cluster_id != prev_cluster_id) { nr_clusters++; prev_cluster_id = cluster_id; } } return nr_clusters; //return arch_nr_clusters(); } #ifdef CONFIG_ARM64 static void arch_get_cluster_cpus(struct cpumask *cpus, int cluster_id) { unsigned int cpu; cpumask_clear(cpus); for_each_possible_cpu(cpu) { struct cpu_topology *cpu_topo = &cpu_topology[cpu]; if (cpu_topo->package_id == cluster_id) cpumask_set_cpu(cpu, cpus); } } # else static void arch_get_cluster_cpus(struct cpumask *cpus, int socket_id) { unsigned int cpu; cpumask_clear(cpus); for_each_possible_cpu(cpu) { struct cputopo_arm *cpu_topo = &cpu_topology[cpu]; if (cpu_topo->socket_id == socket_id) cpumask_set_cpu(cpu, cpus); } } #endif static void rsu_cpu_update_pwd_tbl(void) { int cluster, opp; int cpu; unsigned int temp; struct cpumask cluster_cpus; unsigned long long cap = 0ULL; unsigned long long cap_orig = 0ULL; for (cluster = 0; cluster < nr_cpuclusters; cluster++) { for_each_possible_cpu(cpu) { if (arch_cpu_cluster_id(cpu) == cluster) cap_orig = capacity_orig_of(cpu); } for (opp = 0; opp < NR_FREQ_CPU; opp++) { cpucluster_info[cluster].power[opp] = mt_cpufreq_get_freq_by_idx(cluster, opp); arch_get_cluster_cpus(&cluster_cpus, cluster); for_each_cpu(cpu, &cluster_cpus) { cpu_info[cpu].cluster = &cpucluster_info[cluster]; } cap = cap_orig * cpucluster_info[cluster].power[opp]; if (cpucluster_info[cluster].power[0]) do_div(cap, cpucluster_info[cluster].power[0]); cap = (cap * 100) >> 10; temp = (unsigned int)cap; temp = clamp(temp, 1U, 100U); cpucluster_info[cluster].capacity_ratio[opp] = temp; } } } void rsu_notify_cpufreq(int cid, unsigned long freq) { unsigned long flags; int cpu; spin_lock_irqsave(&freq_slock, flags); cpucluster_info[cid].lastfreq = freq; for_each_possible_cpu(cpu) { cpu_info[cpu].prev_idle_time = get_cpu_idle_time(cpu, &(cpu_info[cpu].prev_wall_time), 1); } spin_unlock_irqrestore(&freq_slock, flags); rsu_systrace_c(RSU_DEBUG_CPU, 0, freq, "RSU_CPUFREQ_CB[%d]", cid); } static int rsu_get_cpu_usage(__u32 pid) { unsigned long flags; int cpu_cnt = 0; int cpu; u64 cur_idle_time_i, cur_wall_time_i; u64 cpu_idle_time = 0, cpu_wall_time = 0; u64 curr_obv; unsigned int *ceiling_idx; int clus_max_idx; int i; int opp; int ret = 0; struct cpufreq_policy *policy; struct cpumask cpus_mask; ceiling_idx = kcalloc(nr_cpuclusters, sizeof(unsigned int), GFP_KERNEL); if (!ceiling_idx) return -1; for (i = 0; i < nr_cpuclusters; i++) { if (mt_ppm_userlimit_freq_limit_by_others) { clus_max_idx = mt_ppm_userlimit_freq_limit_by_others(i); } else { arch_get_cluster_cpus(&cpus_mask, i); policy = cpufreq_cpu_get( cpumask_first(&cpus_mask)); for (opp = 0; opp < NR_FREQ_CPU; opp++) if (policy->max == mt_cpufreq_get_freq_by_idx(i, opp)) { clus_max_idx = opp; break; } cpufreq_cpu_put(policy); } clus_max_idx = clamp(clus_max_idx, 0, NR_FREQ_CPU - 1); ceiling_idx[i] = cpucluster_info[i].capacity_ratio[clus_max_idx]; rsu_systrace_c(RSU_DEBUG_CPU, pid, ceiling_idx[i], "RSU_CEILING_IDX[%d]", i); } spin_lock_irqsave(&freq_slock, flags); for (i = 0; i < nr_cpuclusters; i++) { rsu_systrace_c_log(pid, cpucluster_info[i].lastfreq, "LASTFREQ[%d]", i); for (opp = (NR_FREQ_CPU - 1); opp > 0; opp--) { if (cpucluster_info[i].power[opp] >= cpucluster_info[i].lastfreq) break; } curr_obv = ((u64) cpucluster_info[i].capacity_ratio[opp]) * 100; rsu_systrace_c(RSU_DEBUG_CPU, pid, (unsigned int) curr_obv, "CURR_OBV[%d]", i); do_div(curr_obv, ceiling_idx[i]); cpucluster_info[i].ceiling_obv = (unsigned int) clamp(((unsigned int) curr_obv), 0U, 100U); rsu_systrace_c(RSU_DEBUG_CPU, pid, (unsigned int) curr_obv, "CEILING_OBV[%d]", i); } for_each_online_cpu(cpu) { cur_idle_time_i = get_cpu_idle_time(cpu, &cur_wall_time_i, 1); cpu_idle_time = cur_idle_time_i - cpu_info[cpu].prev_idle_time; cpu_wall_time = cur_wall_time_i - cpu_info[cpu].prev_wall_time; if (cpu_wall_time > 0 && cpu_wall_time >= cpu_idle_time) { ret += div_u64((cpu_wall_time - cpu_idle_time) * 100, cpu_wall_time) * cpu_info[cpu].cluster->ceiling_obv; cpu_cnt++; } rsu_systrace_c(RSU_DEBUG_CPU, pid, cpu_idle_time, "CPU_IDLE_TIME[%d]", cpu); rsu_systrace_c(RSU_DEBUG_CPU, pid, cpu_wall_time, "CPU_WALL_TIME[%d]", cpu); rsu_systrace_c(RSU_DEBUG_CPU, pid, ret, "CPU_USAGE_TOTAL[%d]", cpu); } spin_unlock_irqrestore(&freq_slock, flags); kfree(ceiling_idx); rsu_systrace_c(RSU_DEBUG_CPU, pid, cpu_cnt, "CPU_CNT"); if (cpu_cnt) ret /= (cpu_cnt * 100); else ret = 100; ret = clamp(ret, 0, 100); rsu_systrace_c_log(pid, ret, "RSU_CPUUSAGE"); return ret; } static int rsu_get_gpu_usage(__u32 pid) { unsigned int gloading = 0; unsigned int cur = 0; unsigned int ceiling = 0; mtk_get_gpu_loading(&gloading); cur = mt_gpufreq_get_cur_freq(); ceiling = mt_gpufreq_get_freq_by_idx(mt_gpufreq_get_cur_ceiling_idx()); rsu_systrace_c(RSU_DEBUG_GPU, pid, gloading, "gpu_loading"); rsu_systrace_c(RSU_DEBUG_GPU, pid, cur, "gpu_cur"); rsu_systrace_c(RSU_DEBUG_GPU, pid, ceiling, "gpu_ceiling"); cur *= 100; if (ceiling) { cur /= ceiling; cur *= gloading; cur /= 100; cur = clamp(cur, 0U, 100U); } else cur = -1; rsu_systrace_c_log(pid, cur, "RSU_GPUUSAGE"); return cur; } static int rsu_get_mdla_usage(__u32 pid) { int ret = -1; if (!rs_mdla_support_idletime()) { int ceiling; int curr; int core_num; int i; int vRet = 0; core_num = rs_get_mdla_core_num(); if (!core_num) goto final; for (i = 0; i < core_num; i++) { mutex_lock(&rsu_mdla_ntf_mutex); curr = mdla_opp[i]; mutex_unlock(&rsu_mdla_ntf_mutex); if (curr == -1) continue; do { ceiling = rs_get_mdla_ceiling_opp(i); curr = rs_get_mdla_curr_opp(i); } while (ceiling > rs_get_mdla_opp_max(i) || curr > rs_get_mdla_opp_max(i)); rsu_systrace_c(RSU_DEBUG_MDLA, pid, ceiling, "RSU_MDLA_CEILING_OPP[%d]", i); rsu_systrace_c(RSU_DEBUG_MDLA, pid, curr, "RSU_MDLA_CURR_OPP[%d]", i); ceiling = rs_mdla_opp_to_freq(i, ceiling); curr = rs_mdla_opp_to_freq(i, curr); rsu_systrace_c(RSU_DEBUG_MDLA, pid, ceiling, "RSU_MDLA_CEILING_FREQ[%d]", i); rsu_systrace_c(RSU_DEBUG_MDLA, pid, curr, "RSU_MDLA_CURR_FREQ[%d]", i); if (!ceiling) continue; curr *= 100; vRet += (curr / ceiling); } ret = vRet / core_num; } final: rsu_systrace_c_log(pid, ret, "RSU_MDLAUSAGE"); return ret; } static int rsu_get_vpu_usage(__u32 pid) { int ret = -1; if (!rs_vpu_support_idletime()) { int ceiling; int curr; int core_num; int i; int vRet = 0; core_num = rs_get_vpu_core_num(); if (!core_num) goto final; for (i = 0; i < core_num; i++) { mutex_lock(&rsu_vpu_ntf_mutex); curr = vpu_opp[i]; mutex_unlock(&rsu_vpu_ntf_mutex); if (curr == -1) continue; do { ceiling = rs_get_vpu_ceiling_opp(i); curr = rs_get_vpu_curr_opp(i); } while (ceiling > rs_get_vpu_opp_max(i) || curr > rs_get_vpu_opp_max(i)); rsu_systrace_c(RSU_DEBUG_VPU, pid, ceiling, "RSU_VPU_CEILING_OPP[%d]", i); rsu_systrace_c(RSU_DEBUG_VPU, pid, curr, "RSU_VPU_CURR_OPP[%d]", i); ceiling = rs_vpu_opp_to_freq(i, ceiling); curr = rs_vpu_opp_to_freq(i, curr); rsu_systrace_c(RSU_DEBUG_VPU, pid, ceiling, "RSU_VPU_CEILING_FREQ[%d]", i); rsu_systrace_c(RSU_DEBUG_VPU, pid, curr, "RSU_VPU_CURR_FREQ[%d]", i); if (!ceiling) continue; curr *= 100; vRet += (curr / ceiling); } ret = vRet / core_num; } final: rsu_systrace_c_log(pid, ret, "RSU_VPUUSAGE"); return ret; } static int rsu_get_apu_usage(__u32 pid) { int ret = -1; int mdlal = -1; int vpul = -1; vpul = rsu_get_vpu_usage(pid); mdlal = rsu_get_mdla_usage(pid); ret = vpul; if (mdlal > ret) ret = mdlal; rsu_systrace_c_log(pid, ret, "RSU_APUUSAGE"); return ret; } static int rsu_get_bw_usage(__u32 pid) { #ifdef __BW_USE_EMIALL__ int ret = atomic_read(&last_bw_usage); #else int threshold = pob_qos_get_max_bw_threshold(); int ret = pob_qosbm_get_last_avg(32, PQBT_TOTAL, PQBP_EMI, PQBS_MON, 0, 0); rsu_systrace_c(RSU_DEBUG_QOS, 0, ret, "BW Avg"); rsu_systrace_c(RSU_DEBUG_QOS, 0, threshold, "BW th"); if (!threshold) return 0; if (ret > 0) { ret *= 100; ret /= threshold; ret = clamp(ret, 0, 100); } #endif rsu_systrace_c_log(pid, ret, "RSU_BWUSAGE"); return ret; } void rsu_getusage(__s32 *devusage, __u32 *bwusage, __u32 pid) { switch (*devusage) { case USAGE_DEVTYPE_CPU: *devusage = rsu_get_cpu_usage(pid); break; case USAGE_DEVTYPE_GPU: *devusage = rsu_get_gpu_usage(pid); break; case USAGE_DEVTYPE_APU: *devusage = rsu_get_apu_usage(pid); break; case USAGE_DEVTYPE_MDLA: *devusage = rsu_get_mdla_usage(pid); break; case USAGE_DEVTYPE_VPU: *devusage = rsu_get_vpu_usage(pid); break; default: pr_debug(TAG "unknown cmd %d\n", *devusage); *devusage = -1; break; } *bwusage = rsu_get_bw_usage(pid); } #ifdef __BW_USE_EMIALL__ static int rsu_pob_qos_cb(struct notifier_block *nb, unsigned long val, void *data); static struct notifier_block rsu_qos_notifier = { .notifier_call = rsu_pob_qos_cb, }; static void rsu_ntf_wq_cb(struct work_struct *psWork) { struct RSU_NTF_PUSH_TAG *vpPush = RS_CONTAINER_OF(psWork, struct RSU_NTF_PUSH_TAG, sWork); if (!psWork) return; switch (vpPush->ePushType) { case RSU_NTF_QOSONOFF_SWITCH: if (rsu_qos_isON && !vpPush->value) pob_qos_unregister_client(&rsu_qos_notifier); else if (!rsu_qos_isON && vpPush->value) pob_qos_register_client(&rsu_qos_notifier); rsu_qos_isON = vpPush->value; break; default: break; } rs_free(vpPush); } static void rsu_qosenable_switch(int value) { struct RSU_NTF_PUSH_TAG *vpPush = NULL; if (!_gpRSUNotifyWorkQueue) return; vpPush = (struct RSU_NTF_PUSH_TAG *) rs_alloc_atomic(sizeof(struct RSU_NTF_PUSH_TAG)); if (!vpPush) return; vpPush->ePushType = RSU_NTF_QOSONOFF_SWITCH; vpPush->value = value; INIT_WORK(&vpPush->sWork, rsu_ntf_wq_cb); queue_work(_gpRSUNotifyWorkQueue, &vpPush->sWork); } static inline void rsu_pob_qos_enable(void) { rsu_qosenable_switch(1); } static inline void rsu_pob_qos_disable(void) { rsu_qosenable_switch(0); } static void rsu_qos_check_active(void) { ktime_t cur_time; long long cur_time_us; long long last_time_us; /*get current time*/ cur_time = ktime_get(); cur_time_us = ktime_to_us(cur_time); mutex_lock(&rsu_qos_ntf_mutex); if (fpsgo_active || nn_active) { last_time_us = fpsgo_time_us; if (last_time_us < nn_time_us) last_time_us = nn_time_us; if (cur_time_us > last_time_us && cur_time_us - last_time_us > 1100000) { fpsgo_active = 0; nn_active = 0; rsu_pob_qos_disable(); atomic_set(&last_bw_usage, 0); } } mutex_unlock(&rsu_qos_ntf_mutex); } static int rsu_pob_qos_cb(struct notifier_block *nb, unsigned long val, void *data) { int total = 0; int threshold = pob_qos_get_max_bw_threshold(); int item; rsu_systrace_c(RSU_DEBUG_QOS, 0, threshold, "BW Thres"); if (threshold == -1) return NOTIFY_OK; switch (val) { case POB_QOS_EMI_ALL: if (fpsgo_active || nn_active) { int i; struct pob_qos_info *pqi = (struct pob_qos_info *) data; for (i = 0; i < pqi->size; i++) { item = pob_qosbm_get_stat(pqi->pstats, i, PQBT_TOTAL, PQBP_EMI, PQBS_MON, 0, 0); if (item < 0) item = 0; else item *= 100; rsu_systrace_c(RSU_DEBUG_QOS, 0, item, "BW item"); do_div(item, threshold); total += clamp(item, 0, 100); rsu_systrace_c(RSU_DEBUG_QOS, 0, total, "BW Total"); } do_div(total, pqi->size); atomic_set(&last_bw_usage, total); rsu_qos_check_active(); } break; default: break; } return NOTIFY_OK; } static void rsu_fpsgo_active(long long cur_time_us) { mutex_lock(&rsu_qos_ntf_mutex); fpsgo_time_us = cur_time_us; if (!fpsgo_active) { fpsgo_active = 1; if (!nn_active) rsu_pob_qos_enable(); } mutex_unlock(&rsu_qos_ntf_mutex); } static void rsu_nn_active(long long cur_time_us) { mutex_lock(&rsu_qos_ntf_mutex); nn_time_us = cur_time_us; if (!nn_active) { nn_active = 1; if (!fpsgo_active) rsu_pob_qos_enable(); } mutex_unlock(&rsu_qos_ntf_mutex); } static int rsu_pob_fpsgo_cb(struct notifier_block *nb, unsigned long val, void *data) { ktime_t cur_time; long long cur_time_us; switch (val) { case POB_FPSGO_QTSK_DELALL: mutex_lock(&rsu_qos_ntf_mutex); fpsgo_active = 0; if (!nn_active) { rsu_pob_qos_disable(); atomic_set(&last_bw_usage, 0); } mutex_unlock(&rsu_qos_ntf_mutex); break; default: /*get current time*/ cur_time = ktime_get(); cur_time_us = ktime_to_us(cur_time); rsu_fpsgo_active(cur_time_us); break; } return NOTIFY_OK; } static struct notifier_block rsu_pob_fpsgo_notifier = { .notifier_call = rsu_pob_fpsgo_cb, }; static int rsu_pob_nn_cb(struct notifier_block *nb, unsigned long val, void *data) { ktime_t cur_time; long long cur_time_us; switch (val) { case POB_NN_BEGIN: case POB_NN_END: /*get current time*/ cur_time = ktime_get(); cur_time_us = ktime_to_us(cur_time); rsu_nn_active(cur_time_us); break; default: break; } return NOTIFY_OK; } static struct notifier_block rsu_pob_nn_notifier = { .notifier_call = rsu_pob_nn_cb, }; #endif static int rsu_pob_xpufreq_cb(struct notifier_block *nb, unsigned long val, void *data) { switch (val) { case POB_XPUFREQ_VPU: { struct pob_xpufreq_info *pxi = (struct pob_xpufreq_info *) data; if (pxi->id >= rs_get_vpu_core_num()) break; mutex_lock(&rsu_vpu_ntf_mutex); vpu_opp[pxi->id] = pxi->opp; mutex_unlock(&rsu_vpu_ntf_mutex); rsu_systrace_c(RSU_DEBUG_VPU, 0, pxi->opp, "RSU_VPUFREQ_CB[%d]", pxi->id); } break; case POB_XPUFREQ_MDLA: { struct pob_xpufreq_info *pxi = (struct pob_xpufreq_info *) data; if (pxi->id >= rs_get_mdla_core_num()) break; mutex_lock(&rsu_mdla_ntf_mutex); mdla_opp[pxi->id] = pxi->opp; mutex_unlock(&rsu_mdla_ntf_mutex); rsu_systrace_c(RSU_DEBUG_MDLA, 0, pxi->opp, "RSU_MDLAFREQ_CB[%d]", pxi->id); } break; default: break; } return NOTIFY_OK; } static struct notifier_block rsu_pob_xpufreq_notifier = { .notifier_call = rsu_pob_xpufreq_cb, }; static ssize_t rs_usage_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { char temp[RS_SYSFS_MAX_BUFF_SIZE] = ""; int posi = 0; int length; length = scnprintf(temp + posi, RS_SYSFS_MAX_BUFF_SIZE - posi, "cpu\tgpu\tbw\tapu\tvpu\tmdla\n"); posi += length; length = scnprintf(temp + posi, RS_SYSFS_MAX_BUFF_SIZE - posi, "%d\t%d\t%d\t%d\t%d\t%d\n", rsu_get_cpu_usage(0), rsu_get_gpu_usage(0), rsu_get_bw_usage(0), rsu_get_apu_usage(0), rsu_get_vpu_usage(0), rsu_get_mdla_usage(0)); posi += length; return scnprintf(buf, PAGE_SIZE, "%s\n", temp); } KOBJ_ATTR_RO(rs_usage); static ssize_t rs_mask_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { int i; char temp[RS_SYSFS_MAX_BUFF_SIZE]; int pos = 0; int length; length = scnprintf(temp + pos, RS_SYSFS_MAX_BUFF_SIZE - pos, " Current enabled systrace:\n"); pos += length; for (i = 0; (1U << i) < RSU_DEBUG_MAX; i++) { length = scnprintf(temp + pos, RS_SYSFS_MAX_BUFF_SIZE - pos, " %-*s ... %s\n", 12, mask_string[i], rsu_systrace_mask & (1U << i) ? "On" : "Off"); pos += length; } return scnprintf(buf, PAGE_SIZE, "%s", temp); } static ssize_t rs_mask_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { int val = -1; char acBuffer[RS_SYSFS_MAX_BUFF_SIZE]; int arg; if ((count > 0) && (count < RS_SYSFS_MAX_BUFF_SIZE)) { if (scnprintf(acBuffer, RS_SYSFS_MAX_BUFF_SIZE, "%s", buf)) { if (kstrtoint(acBuffer, 0, &arg) == 0) val = arg; else return count; } } val = val & (RSU_DEBUG_MAX - 1U); rsu_systrace_mask = val; return count; } KOBJ_ATTR_RW(rs_mask); int __init rs_usage_init(void) { int i; #ifdef __BW_USE_EMIALL__ _gpRSUNotifyWorkQueue = create_singlethread_workqueue("rsu_ntf_wq"); if (_gpRSUNotifyWorkQueue == NULL) return -EFAULT; #endif nr_cpuclusters = arch_get_nr_clusters(); nr_cpus = num_possible_cpus(); cpucluster_info = kcalloc(nr_cpuclusters, sizeof(struct rsu_cpu_cluster_info), GFP_KERNEL); cpu_info = kcalloc(nr_cpus, sizeof(struct rsu_cpu_info), GFP_KERNEL); rsu_cpu_update_pwd_tbl(); if (rs_get_vpu_core_num()) { vpu_opp = kcalloc(rs_get_vpu_core_num(), sizeof(int), GFP_KERNEL); for (i = 0; i < rs_get_vpu_core_num(); i++) vpu_opp[i] = -1; } if (rs_get_mdla_core_num()) { mdla_opp = kcalloc(rs_get_mdla_core_num(), sizeof(int), GFP_KERNEL); for (i = 0; i < rs_get_mdla_core_num(); i++) mdla_opp[i] = -1; } rsu_systrace_mask = RSU_DEBUG_MANDATORY; #ifdef CONFIG_MTK_FPSGO_V3 rsu_cpufreq_notifier_fp = rsu_notify_cpufreq; #endif rsu_getusage_fp = rsu_getusage; #ifdef __BW_USE_EMIALL__ pob_fpsgo_register_client(&rsu_pob_fpsgo_notifier); pob_nn_register_client(&rsu_pob_nn_notifier); #endif pob_xpufreq_register_client(&rsu_pob_xpufreq_notifier); if (rs_sysfs_create_dir(NULL, "usage", &rsu_kobj)) return -ENODEV; rs_sysfs_create_file(rsu_kobj, &kobj_attr_rs_usage); rs_sysfs_create_file(rsu_kobj, &kobj_attr_rs_mask); return 0; } void __exit rs_usage_exit(void) { rs_sysfs_remove_file(rsu_kobj, &kobj_attr_rs_usage); rs_sysfs_remove_file(rsu_kobj, &kobj_attr_rs_mask); rs_sysfs_remove_dir(&rsu_kobj); }