1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555 |
- /*
- * drivers/cpufreq/cpufreq_limit.c
- *
- * Remade according to cpufreq change
- * (refer to commit df0eea4488081e0698b0b58ccd1e8c8823e22841
- * 18c49926c4bf4915e5194d1de3299c0537229f9f)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
- #include <linux/module.h>
- #include <linux/sysfs.h>
- #include <linux/cpufreq.h>
- #include <linux/cpufreq_limit.h>
- #include <linux/err.h>
- #include <linux/suspend.h>
- #include <linux/cpu.h>
- #include <linux/kobject.h>
- #include <linux/timer.h>
- #include <linux/platform_device.h>
- #ifdef CONFIG_OF
- #include <linux/of.h>
- #endif
- #include <trace/hooks/cpufreq.h>
- #define MAX_BUF_SIZE 1024
- #define MIN(a, b) (((a) < (b)) ? (a) : (b))
- #define MAX(a, b) (((a) > (b)) ? (a) : (b))
- /* adaptive boost from walt */
- extern int cpufreq_walt_set_adaptive_freq(unsigned int cpu, unsigned int adaptive_low_freq,
- unsigned int adaptive_high_freq);
- extern int cpufreq_walt_get_adaptive_freq(unsigned int cpu, unsigned int *adaptive_low_freq,
- unsigned int *adaptive_high_freq);
- extern int cpufreq_walt_reset_adaptive_freq(unsigned int cpu);
- static unsigned int __read_mostly lpcharge;
- module_param(lpcharge, uint, 0444);
- /* voltage based freq table */
- #if IS_ENABLED(CONFIG_QTI_CPU_VOLTAGE_COOLING_DEVICE)
- extern struct freq_voltage_base cflm_vbf;
- #else
- static struct freq_voltage_base cflm_vbf;
- #endif
- static DEFINE_MUTEX(cflm_mutex);
- #define LIMIT_RELEASE -1
- /* boosted state */
- #define BOOSTED 1
- #define NOT_BOOSTED 0
- #define NUM_CPUS 8
- static unsigned int cflm_req_init[NUM_CPUS];
- static struct freq_qos_request max_req[NUM_CPUS][CFLM_MAX_ITEM];
- static struct freq_qos_request min_req[NUM_CPUS][CFLM_MAX_ITEM];
- static struct kobject *cflm_kobj;
- struct freq_map {
- unsigned int in;
- unsigned int out;
- };
- /* adaptive boost threshold - high - low freq table */
- struct aboost_th_table {
- int threshold;
- int high;
- int low;
- };
- /* input info: freq, time(TBD) */
- struct input_info {
- int boosted;
- int min;
- int max;
- u64 time_in_min_limit;
- u64 time_in_max_limit;
- u64 time_in_over_limit;
- ktime_t last_min_limit_time;
- ktime_t last_max_limit_time;
- ktime_t last_over_limit_time;
- };
- static struct input_info freq_input[CFLM_MAX_ITEM];
- struct cflm_parameter {
- /* to make virtual freq table */
- struct cpufreq_frequency_table *cpuftbl_L;
- struct cpufreq_frequency_table *cpuftbl_b;
- unsigned int unified_cpuftbl[50];
- unsigned int freq_count;
- bool table_initialized;
- /* cpu info: silver/gold/prime */
- unsigned int s_first;
- unsigned int s_fmin;
- unsigned int s_fmax;
- unsigned int g_first;
- unsigned int g_fmin;
- unsigned int g_fmax;
- unsigned int p_first;
- unsigned int p_fmin;
- unsigned int p_fmax;
- /* 4 policy arch: sm8650, titanium, same clock table as gold */
- bool titanium;
- unsigned int t_first;
- unsigned int t_fmin;
- unsigned int t_fmax;
- /* exceptional case */
- unsigned int g_fmin_up; /* fixed gold clock for performance */
- /* in virtual table little(silver)/big(gold & prime) */
- unsigned int big_min_freq;
- unsigned int big_max_freq;
- unsigned int ltl_min_freq;
- unsigned int ltl_max_freq;
- /* pre-defined value */
- struct freq_map *silver_boost_map;
- unsigned int boost_map_size;
- struct freq_map *silver_limit_map;
- unsigned int limit_map_size;
- unsigned int silver_divider;
- /* current freq in virtual table */
- unsigned int min_limit_val;
- unsigned int max_limit_val;
- /* sched boost type */
- int sched_boost_type;
- bool sched_boost_cond;
- bool sched_boost_enabled;
- /* over limit */
- unsigned int over_limit;
- /* voltage based clock */
- bool vol_based_clk;
- int vbf_offset; /* gold clock offset for perf */
- /* adaptive boost */
- bool ab_enabled;
- struct aboost_th_table *ab_table;
- };
- /* TODO: move to dtsi? */
- static struct cflm_parameter param = {
- .freq_count = 0,
- .table_initialized = false,
- .s_first = 0,
- .g_first = 2,
- .p_first = 7,
- .titanium = 0,
- .t_first = 5,
- .g_fmin_up = 0, /* fixed gold clock for performance */
- .ltl_min_freq = 0, /* will be auto updated */
- .ltl_max_freq = 0, /* will be auto updated */
- .big_min_freq = 0, /* will be auto updated */
- .big_max_freq = 0, /* will be auto updated */
- .boost_map_size = 0,
- .limit_map_size = 0,
- .silver_divider = 2,
- .min_limit_val = -1,
- .max_limit_val = -1,
- .sched_boost_type = CONSERVATIVE_BOOST,
- .sched_boost_cond = false,
- .sched_boost_enabled = false,
- .over_limit = 0,
- .vol_based_clk = false,
- };
- static bool cflm_make_table(void)
- {
- int i, count = 0;
- int freq_count = 0;
- unsigned int freq;
- bool ret = false;
- /* big cluster table */
- if (!param.cpuftbl_b)
- goto little;
- for (i = 0; param.cpuftbl_b[i].frequency != CPUFREQ_TABLE_END; i++)
- count = i;
- for (i = count; i >= 0; i--) {
- freq = param.cpuftbl_b[i].frequency;
- if (freq == CPUFREQ_ENTRY_INVALID)
- continue;
- if (freq < param.big_min_freq ||
- freq > param.big_max_freq)
- continue;
- param.unified_cpuftbl[freq_count++] = freq;
- }
- little:
- /* LITTLE cluster table */
- if (!param.cpuftbl_L)
- goto done;
- for (i = 0; param.cpuftbl_L[i].frequency != CPUFREQ_TABLE_END; i++)
- count = i;
- for (i = count; i >= 0; i--) {
- freq = param.cpuftbl_L[i].frequency / param.silver_divider;
- if (freq == CPUFREQ_ENTRY_INVALID)
- continue;
- if (freq < param.ltl_min_freq ||
- freq > param.ltl_max_freq)
- continue;
- param.unified_cpuftbl[freq_count++] = freq;
- }
- done:
- if (freq_count) {
- pr_debug("%s: unified table is made\n", __func__);
- param.freq_count = freq_count;
- ret = true;
- } else {
- pr_err("%s: cannot make unified table\n", __func__);
- }
- return ret;
- }
- /**
- * cflm_set_table - cpufreq table from dt via qcom-cpufreq
- */
- static void cflm_set_table(int cpu, struct cpufreq_frequency_table *ftbl)
- {
- int i, count = 0;
- unsigned int max_freq_b = 0, min_freq_b = UINT_MAX;
- unsigned int max_freq_l = 0, min_freq_l = UINT_MAX;
- if (param.table_initialized)
- return;
- if (cpu == param.s_first)
- param.cpuftbl_L = ftbl;
- else if (cpu == param.p_first)
- param.cpuftbl_b = ftbl;
- if (!param.cpuftbl_L)
- return;
- if (!param.cpuftbl_b)
- return;
- pr_info("%s: freq table is ready, update config\n", __func__);
- /* update little config */
- for (i = 0; param.cpuftbl_L[i].frequency != CPUFREQ_TABLE_END; i++)
- count = i;
- for (i = count; i >= 0; i--) {
- if (param.cpuftbl_L[i].frequency == CPUFREQ_ENTRY_INVALID)
- continue;
- if (param.cpuftbl_L[i].frequency < min_freq_l)
- min_freq_l = param.cpuftbl_L[i].frequency;
- if (param.cpuftbl_L[i].frequency > max_freq_l)
- max_freq_l = param.cpuftbl_L[i].frequency;
- }
- if (!param.ltl_min_freq)
- param.ltl_min_freq = min_freq_l / param.silver_divider;
- if (!param.ltl_max_freq)
- param.ltl_max_freq = max_freq_l / param.silver_divider;
- /* update big config */
- for (i = 0; param.cpuftbl_b[i].frequency != CPUFREQ_TABLE_END; i++)
- count = i;
- for (i = count; i >= 0; i--) {
- if (param.cpuftbl_b[i].frequency == CPUFREQ_ENTRY_INVALID)
- continue;
- if ((param.cpuftbl_b[i].frequency < min_freq_b) &&
- (param.cpuftbl_b[i].frequency > param.ltl_max_freq))
- min_freq_b = param.cpuftbl_b[i].frequency;
- if (param.cpuftbl_b[i].frequency > max_freq_b)
- max_freq_b = param.cpuftbl_b[i].frequency;
- }
- if (!param.big_min_freq)
- param.big_min_freq = min_freq_b;
- if (!param.big_max_freq)
- param.big_max_freq = max_freq_b;
- pr_info("%s: updated: little(%u-%u), big(%u-%u)\n", __func__,
- param.ltl_min_freq, param.ltl_max_freq,
- param.big_min_freq, param.big_max_freq);
- param.table_initialized = cflm_make_table();
- }
- /**
- * cflm_get_table - fill the cpufreq table to support HMP
- * @buf a buf that has been requested to fill the cpufreq table
- */
- static ssize_t cflm_get_table(char *buf)
- {
- ssize_t len = 0;
- int i = 0;
- if (!param.freq_count)
- return len;
- for (i = 0; i < param.freq_count; i++)
- len += snprintf(buf + len, MAX_BUF_SIZE, "%u ",
- param.unified_cpuftbl[i]);
- len--;
- len += snprintf(buf + len, MAX_BUF_SIZE, "\n");
- pr_info("%s: %s\n", __func__, buf);
- return len;
- }
- static void cflm_update_boost(void)
- {
- int i;
- bool boost_condition = false;
- /* sched boost */
- param.sched_boost_cond = false;
- for (i = 0; i < CFLM_MAX_ITEM; i++) {
- if (freq_input[i].min > (int)param.ltl_max_freq) {
- param.sched_boost_cond = true;
- boost_condition = true;
- break;
- }
- }
- if (boost_condition) {
- if (!param.sched_boost_enabled) {
- pr_debug("%s: sched boost on, type(%d)\n", __func__, param.sched_boost_type);
- sched_set_boost(param.sched_boost_type);
- param.sched_boost_enabled = true;
- } else {
- pr_debug("%s: sched boost already on, do nothing\n", __func__);
- }
- } else {
- if (param.sched_boost_enabled) {
- pr_debug("%s: sched boost off(%d)\n", __func__, (param.sched_boost_type * -1));
- sched_set_boost(param.sched_boost_type * -1);
- param.sched_boost_enabled = false;
- } else {
- pr_debug("%s: sched boost already off, do nothing\n", __func__);
- }
- }
- }
- static s32 cflm_freq_qos_read_value(struct freq_constraints *qos,
- enum freq_qos_req_type type)
- {
- s32 ret;
- switch (type) {
- case FREQ_QOS_MIN:
- ret = IS_ERR_OR_NULL(qos) ?
- FREQ_QOS_MIN_DEFAULT_VALUE :
- READ_ONCE(qos->min_freq.target_value);
- break;
- case FREQ_QOS_MAX:
- ret = IS_ERR_OR_NULL(qos) ?
- FREQ_QOS_MAX_DEFAULT_VALUE :
- READ_ONCE(qos->max_freq.target_value);
- break;
- default:
- WARN_ON(1);
- ret = 0;
- }
- return ret;
- }
- static void cflm_current_qos(void)
- {
- struct cpufreq_policy *policy;
- int s_min = 0, s_max = 0;
- int g_min = 0, g_max = 0;
- int p_min = 0, p_max = 0;
- int t_min = 0, t_max = 0;
- unsigned int a_low = 0, a_high = 0;
- policy = cpufreq_cpu_get(param.s_first);
- if (policy) {
- s_min = cflm_freq_qos_read_value(&policy->constraints, FREQ_QOS_MIN);
- s_max = cflm_freq_qos_read_value(&policy->constraints, FREQ_QOS_MAX);
- cpufreq_cpu_put(policy);
- }
- if (param.ab_enabled) {
- cpufreq_walt_get_adaptive_freq(param.s_first, &a_low, &a_high);
- pr_cont("%s: s[%d(%d, %d)-%d]", __func__, s_min, a_low, a_high, s_max);
- } else {
- pr_cont("%s: s[%d-%d]", __func__, s_min, s_max);
- }
- policy = cpufreq_cpu_get(param.g_first);
- if (policy) {
- g_min = cflm_freq_qos_read_value(&policy->constraints, FREQ_QOS_MIN);
- g_max = cflm_freq_qos_read_value(&policy->constraints, FREQ_QOS_MAX);
- cpufreq_cpu_put(policy);
- }
- if (param.ab_enabled) {
- cpufreq_walt_get_adaptive_freq(param.g_first, &a_low, &a_high);
- pr_cont(", g[%d(%d, %d)-%d]", g_min, a_low, a_high, g_max);
- } else {
- pr_cont(", g[%d-%d]", g_min, g_max);
- }
- /* now, not use adaptive boost for titanium and prime */
- if (param.titanium) {
- policy = cpufreq_cpu_get(param.t_first);
- if (policy) {
- t_min = cflm_freq_qos_read_value(&policy->constraints, FREQ_QOS_MIN);
- t_max = cflm_freq_qos_read_value(&policy->constraints, FREQ_QOS_MAX);
- cpufreq_cpu_put(policy);
- }
- pr_cont(", t[%d-%d]", t_min, t_max);
- }
- policy = cpufreq_cpu_get(param.p_first);
- if (policy) {
- p_min = cflm_freq_qos_read_value(&policy->constraints, FREQ_QOS_MIN);
- p_max = cflm_freq_qos_read_value(&policy->constraints, FREQ_QOS_MAX);
- cpufreq_cpu_put(policy);
- }
- pr_cont(", p[%d-%d]", p_min, p_max);
- pr_cont("\n");
- }
- static bool cflm_max_lock_need_restore(void)
- {
- if ((int)param.over_limit <= 0)
- return false;
- if (freq_input[CFLM_USERSPACE].min > 0) {
- if (freq_input[CFLM_USERSPACE].min > (int)param.ltl_max_freq) {
- pr_debug("%s: userspace minlock (%d) > ltl max (%d)\n",
- __func__, freq_input[CFLM_USERSPACE].min, param.ltl_max_freq);
- return false;
- }
- }
- if (freq_input[CFLM_TOUCH].min > 0) {
- if (freq_input[CFLM_TOUCH].min > (int)param.ltl_max_freq) {
- pr_debug("%s: touch minlock (%d) > ltl max (%d)\n",
- __func__, freq_input[CFLM_TOUCH].min, param.ltl_max_freq);
- return false;
- }
- }
- return true;
- }
- static bool cflm_high_pri_min_lock_required(void)
- {
- if ((int)param.over_limit <= 0)
- return false;
- if (freq_input[CFLM_USERSPACE].min > 0) {
- if (freq_input[CFLM_USERSPACE].min > (int)param.ltl_max_freq) {
- pr_debug("%s: userspace minlock (%d) > ltl max (%d)\n",
- __func__, freq_input[CFLM_USERSPACE].min, param.ltl_max_freq);
- return true;
- }
- }
- if (freq_input[CFLM_TOUCH].min > 0) {
- if (freq_input[CFLM_TOUCH].min > (int)param.ltl_max_freq) {
- pr_debug("%s: touch minlock (%d) > ltl max (%d)\n",
- __func__, freq_input[CFLM_TOUCH].min, param.ltl_max_freq);
- return true;
- }
- }
- return false;
- }
- static unsigned int cflm_get_vol_matched_freq(unsigned int in_freq)
- {
- int i;
- unsigned int out_freq = in_freq;
- if (param.vbf_offset > cflm_vbf.count || param.vbf_offset < 0) {
- pr_err("%s: bad condition(off(%d), cnt(%d))",
- __func__, param.vbf_offset, cflm_vbf.count);
- return out_freq;
- }
- /* start from offset */
- for (i = param.vbf_offset; i < cflm_vbf.count; i++) {
- if (cflm_vbf.table[PRIME_CPU][i] <= in_freq) {
- out_freq = cflm_vbf.table[GOLD_CPU][i - param.vbf_offset];
- break;
- }
- }
- pr_debug("%s: in(%d), out(%d)\n", __func__, in_freq, out_freq);
- return out_freq;
- }
- static int cflm_get_silver_boost(int freq)
- {
- int i;
- for (i = 0; i < param.boost_map_size; i++)
- if (freq >= param.silver_boost_map[i].in)
- return param.silver_boost_map[i].out;
- return freq * param.silver_divider;
- }
- static int cflm_get_silver_limit(int freq)
- {
- int i;
- /* prime limit condition */
- for (i = 0; i < param.limit_map_size; i++)
- if (freq >= param.silver_limit_map[i].in)
- return MIN(param.silver_limit_map[i].out, param.s_fmax);
- /* silver limit condition */
- return freq * param.silver_divider;
- }
- static int cflm_adaptive_boost(int first_cpu, int min)
- {
- struct cpufreq_policy *policy;
- int cpu = 0;
- int ret = 0;
- int aboost_low;
- pr_debug("%s: cpu%d: %d\n", __func__, first_cpu, min);
- if (!param.ab_enabled)
- return -EINVAL;
- if (!param.ab_table)
- return -EINVAL;
- if (!param.ab_table[first_cpu].threshold)
- return -EINVAL;
- policy = cpufreq_cpu_get(first_cpu);
- if (!policy) {
- pr_err("%s: no policy for cpu%d\n", __func__, first_cpu);
- return -EFAULT;
- }
- if (strcmp((policy->governor->name), "walt")) {
- pr_err("%s: not supported gov(%s)\n", __func__, policy->governor->name);
- return -EFAULT;
- }
- if (min >= param.ab_table[first_cpu].threshold)
- aboost_low = param.ab_table[first_cpu].high;
- else
- aboost_low = param.ab_table[first_cpu].low;
- if ((min > 0) && (min < aboost_low)) {
- pr_err("%s: cpu%d boost min(%d) is lower than adaptive low(%d)\n",
- __func__, first_cpu, min, aboost_low);
- aboost_low = min;
- }
- for_each_cpu(cpu, policy->related_cpus) {
- if (min > 0) {
- pr_debug("%s: set aboost: cpu%d: %d, %d\n", __func__, cpu, aboost_low, min);
- ret = cpufreq_walt_set_adaptive_freq(cpu, aboost_low, min);
- } else {
- pr_debug("%s: clear aboost: cpu%d\n", __func__, cpu);
- ret = cpufreq_walt_reset_adaptive_freq(cpu);
- }
- }
- cpufreq_cpu_put(policy);
- return ret;
- }
- static void cflm_freq_decision(int type, int new_min, int new_max)
- {
- int cpu = 0;
- int s_min = param.s_fmin;
- int s_max = param.s_fmax;
- int g_min = param.g_fmin;
- int g_max = param.g_fmax;
- int p_min = param.p_fmin;
- int p_max = param.p_fmax;
- int t_max = param.t_fmax;
- bool need_update_user_max = false;
- int new_user_max = FREQ_QOS_MAX_DEFAULT_VALUE;
- pr_info("%s: input: type(%d), min(%d), max(%d)\n",
- __func__, type, new_min, new_max);
- /* update input freq */
- if (new_min != 0) {
- freq_input[type].min = new_min;
- if ((new_min == LIMIT_RELEASE || new_min == param.ltl_min_freq) &&
- freq_input[type].last_min_limit_time != 0) {
- freq_input[type].time_in_min_limit += ktime_to_ms(ktime_get()-
- freq_input[type].last_min_limit_time);
- freq_input[type].last_min_limit_time = 0;
- freq_input[type].boosted = NOT_BOOSTED;
- pr_debug("%s: type(%d), released(%d)\n", __func__, type, freq_input[type].boosted);
- }
- if (new_min != LIMIT_RELEASE && new_min != param.ltl_min_freq &&
- freq_input[type].last_min_limit_time == 0) {
- freq_input[type].last_min_limit_time = ktime_get();
- freq_input[type].boosted = BOOSTED;
- pr_debug("%s: type(%d), boosted(%d)\n", __func__, type, freq_input[type].boosted);
- }
- }
- if (new_max != 0) {
- freq_input[type].max = new_max;
- if ((new_max == LIMIT_RELEASE || new_max == param.big_max_freq) &&
- freq_input[type].last_max_limit_time != 0) {
- freq_input[type].time_in_max_limit += ktime_to_ms(ktime_get() -
- freq_input[type].last_max_limit_time);
- freq_input[type].last_max_limit_time = 0;
- }
- if (new_max != LIMIT_RELEASE && new_max != param.big_max_freq &&
- freq_input[type].last_max_limit_time == 0) {
- freq_input[type].last_max_limit_time = ktime_get();
- }
- }
- if (new_min > 0) {
- if (new_min < param.ltl_min_freq) {
- pr_err("%s: too low freq(%d), set to %d\n",
- __func__, new_min, param.ltl_min_freq);
- new_min = param.ltl_min_freq;
- }
- pr_debug("%s: new_min=%d, ltl_max=%d, over_limit=%d\n", __func__,
- new_min, param.ltl_max_freq, param.over_limit);
- if ((type == CFLM_USERSPACE || type == CFLM_TOUCH) &&
- cflm_high_pri_min_lock_required()) {
- if (freq_input[CFLM_USERSPACE].max > 0) {
- need_update_user_max = true;
- new_user_max = MAX((int)param.over_limit, freq_input[CFLM_USERSPACE].max);
- pr_debug("%s: override new_max %d => %d, userspace_min=%d, touch_min=%d, ltl_max=%d\n",
- __func__, freq_input[CFLM_USERSPACE].max, new_user_max, freq_input[CFLM_USERSPACE].min,
- freq_input[CFLM_TOUCH].min, param.ltl_max_freq);
- }
- }
- /* boost @gold/prime */
- s_min = cflm_get_silver_boost(new_min);
- if (new_min > param.ltl_max_freq) {
- g_min = MIN(new_min, param.g_fmax);
- p_min = MIN(new_min, param.p_fmax);
- } else {
- g_min = param.g_fmin;
- p_min = param.p_fmin;
- }
- if (cflm_adaptive_boost(param.s_first, s_min) < 0)
- freq_qos_update_request(&min_req[param.s_first][type], s_min); /* prevent adaptive boost fail */
- if (cflm_adaptive_boost(param.g_first, g_min) < 0)
- freq_qos_update_request(&min_req[param.g_first][type], g_min);
- freq_qos_update_request(&min_req[param.p_first][type], p_min);
- /* TEMP??: no boost for titanium
- *if (param.titanium)
- * freq_qos_update_request(&min_req[param.t_first][type], g_min);
- */
- } else if (new_min == LIMIT_RELEASE) {
- for_each_possible_cpu(cpu) {
- freq_qos_update_request(&min_req[cpu][type],
- FREQ_QOS_MIN_DEFAULT_VALUE);
- }
- if (param.ab_enabled) {
- int i;
- int aggr_state = 0;
- for (i = 0; i < CFLM_MAX_ITEM; i++)
- aggr_state += freq_input[i].boosted;
- if (aggr_state == 0) {
- cflm_adaptive_boost(param.s_first, 0);
- cflm_adaptive_boost(param.g_first, 0);
- pr_debug("%s: aboost: clear\n", __func__);
- }
- }
- if ((type == CFLM_USERSPACE || type == CFLM_TOUCH) &&
- cflm_max_lock_need_restore()) { // if there is no high priority min lock and over limit is set
- if (freq_input[CFLM_USERSPACE].max > 0) {
- need_update_user_max = true;
- new_user_max = freq_input[CFLM_USERSPACE].max;
- pr_debug("%s: restore new_max => %d\n",
- __func__, new_user_max);
- }
- }
- }
- if (new_max > 0) {
- if (new_max > param.big_max_freq) {
- pr_err("%s: too high freq(%d), set to %d\n",
- __func__, new_max, param.big_max_freq);
- new_max = param.big_max_freq;
- }
- if ((type == CFLM_USERSPACE) && // if userspace maxlock is being set
- cflm_high_pri_min_lock_required()) {
- need_update_user_max = true;
- new_user_max = MAX((int)param.over_limit, freq_input[CFLM_USERSPACE].max);
- pr_debug("%s: force up new_max %d => %d, userspace_min=%d, touch_min=%d, ltl_max=%d\n",
- __func__, new_max, new_user_max, freq_input[CFLM_USERSPACE].min,
- freq_input[CFLM_TOUCH].min, param.ltl_max_freq);
- }
- s_max = cflm_get_silver_limit(new_max);
- if (new_max < param.big_min_freq) {
- /* if silver clock is limited as fmax,
- * set promised clock for gold cluster
- */
- if ((new_max == param.s_fmax / param.silver_divider) && (param.g_fmin_up > 0)) {
- g_max = param.g_fmin_up;
- t_max = param.g_fmin_up;
- } else {
- g_max = param.g_fmin;
- t_max = param.t_fmin;
- }
- p_max = param.p_fmin;
- } else {
- p_max = MIN(new_max, param.p_fmax);
- g_max = MIN(new_max, param.g_fmax);
- if (param.vol_based_clk == true && cflm_vbf.count > 0)
- t_max = MIN(cflm_get_vol_matched_freq(p_max), param.t_fmax);
- else
- t_max = MIN(new_max, param.t_fmax);
- }
- freq_qos_update_request(&max_req[param.s_first][type], s_max);
- freq_qos_update_request(&max_req[param.g_first][type], g_max);
- freq_qos_update_request(&max_req[param.p_first][type], p_max);
- if (param.titanium)
- freq_qos_update_request(&max_req[param.t_first][type], t_max);
- } else if (new_max == LIMIT_RELEASE) {
- for_each_possible_cpu(cpu)
- freq_qos_update_request(&max_req[cpu][type],
- FREQ_QOS_MAX_DEFAULT_VALUE);
- }
- if ((freq_input[type].min <= (int)param.ltl_max_freq || new_user_max != (int)param.over_limit) &&
- freq_input[type].last_over_limit_time != 0) {
- freq_input[type].time_in_over_limit += ktime_to_ms(ktime_get() -
- freq_input[type].last_over_limit_time);
- freq_input[type].last_over_limit_time = 0;
- }
- if (freq_input[type].min > (int)param.ltl_max_freq && new_user_max == (int)param.over_limit &&
- freq_input[type].last_over_limit_time == 0) {
- freq_input[type].last_over_limit_time = ktime_get();
- }
- if (need_update_user_max) {
- pr_debug("%s: update_user_max is true\n", __func__);
- if (new_user_max > param.big_max_freq) {
- pr_debug("%s: too high freq(%d), set to %d\n",
- __func__, new_user_max, param.big_max_freq);
- new_user_max = param.big_max_freq;
- }
- s_max = cflm_get_silver_limit(new_user_max);
- if (new_user_max < param.big_min_freq) {
- /* if silver clock is limited as fmax,
- * set promised clock for gold cluster
- */
- if ((new_user_max == param.s_fmax / param.silver_divider) && (param.g_fmin_up > 0)) {
- g_max = param.g_fmin_up;
- t_max = param.g_fmin_up; /* use same freq with gold */
- } else {
- g_max = param.g_fmin;
- t_max = param.t_fmin;
- }
- p_max = param.p_fmin;
- } else {
- p_max = MIN(new_user_max, param.p_fmax);
- g_max = MIN(new_user_max, param.g_fmax);
- if (param.vol_based_clk == true && cflm_vbf.count > 0)
- t_max = MIN(cflm_get_vol_matched_freq(p_max), param.t_fmax);
- else
- t_max = MIN(new_user_max, param.t_fmax);
- }
- pr_info("%s: freq_update_request : new userspace max %d %d %d %d\n", __func__, s_max, g_max, t_max, p_max);
- freq_qos_update_request(&max_req[param.s_first][CFLM_USERSPACE], s_max);
- freq_qos_update_request(&max_req[param.g_first][CFLM_USERSPACE], g_max);
- freq_qos_update_request(&max_req[param.p_first][CFLM_USERSPACE], p_max);
- if (param.titanium)
- freq_qos_update_request(&max_req[param.t_first][CFLM_USERSPACE], g_max);
- }
- cflm_update_boost();
- cflm_current_qos();
- }
- static ssize_t cpufreq_table_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *buf)
- {
- ssize_t len = 0;
- len = cflm_get_table(buf);
- return len;
- }
- static ssize_t cpufreq_max_limit_show(struct kobject *kobj,
- struct kobj_attribute *attr,
- char *buf)
- {
- return snprintf(buf, MAX_BUF_SIZE, "%d\n", param.max_limit_val);
- }
- static ssize_t cpufreq_max_limit_store(struct kobject *kobj,
- struct kobj_attribute *attr,
- const char *buf, size_t n)
- {
- int freq;
- int ret = -EINVAL;
- ret = kstrtoint(buf, 10, &freq);
- if (ret < 0) {
- pr_err("%s: cflm: Invalid cpufreq format\n", __func__);
- goto out;
- }
- mutex_lock(&cflm_mutex);
- param.max_limit_val = freq;
- cflm_freq_decision(CFLM_USERSPACE, 0, freq);
- mutex_unlock(&cflm_mutex);
- ret = n;
- out:
- return ret;
- }
- static ssize_t cpufreq_min_limit_show(struct kobject *kobj,
- struct kobj_attribute *attr,
- char *buf)
- {
- return snprintf(buf, MAX_BUF_SIZE, "%d\n", param.min_limit_val);
- }
- static ssize_t cpufreq_min_limit_store(struct kobject *kobj,
- struct kobj_attribute *attr,
- const char *buf, size_t n)
- {
- int freq;
- int ret = -EINVAL;
- ret = kstrtoint(buf, 10, &freq);
- if (ret < 0) {
- pr_err("%s: cflm: Invalid cpufreq format\n", __func__);
- goto out;
- }
- mutex_lock(&cflm_mutex);
- cflm_freq_decision(CFLM_USERSPACE, freq, 0);
- param.min_limit_val = freq;
- mutex_unlock(&cflm_mutex);
- ret = n;
- out:
- return ret;
- }
- static ssize_t over_limit_show(struct kobject *kobj,
- struct kobj_attribute *attr,
- char *buf)
- {
- return snprintf(buf, MAX_BUF_SIZE, "%d\n", param.over_limit);
- }
- static ssize_t over_limit_store(struct kobject *kobj,
- struct kobj_attribute *attr,
- const char *buf, size_t n)
- {
- int freq;
- int ret = -EINVAL;
- ret = kstrtoint(buf, 10, &freq);
- if (ret < 0) {
- pr_err("%s: cflm: Invalid cpufreq format\n", __func__);
- goto out;
- }
- mutex_lock(&cflm_mutex);
- if (param.over_limit != freq) {
- param.over_limit = freq;
- if ((int)param.max_limit_val > 0)
- cflm_freq_decision(CFLM_USERSPACE, 0, param.max_limit_val);
- }
- mutex_unlock(&cflm_mutex);
- ret = n;
- out:
- return ret;
- }
- static ssize_t limit_stat_show(struct kobject *kobj,
- struct kobj_attribute *attr,
- char *buf)
- {
- ssize_t len = 0;
- int i, j = 0;
- mutex_lock(&cflm_mutex);
- for (i = 0; i < CFLM_MAX_ITEM; i++) {
- if (freq_input[i].last_min_limit_time != 0) {
- freq_input[i].time_in_min_limit += ktime_to_ms(ktime_get() -
- freq_input[i].last_min_limit_time);
- freq_input[i].last_min_limit_time = ktime_get();
- }
- if (freq_input[i].last_max_limit_time != 0) {
- freq_input[i].time_in_max_limit += ktime_to_ms(ktime_get() -
- freq_input[i].last_max_limit_time);
- freq_input[i].last_max_limit_time = ktime_get();
- }
- if (freq_input[i].last_over_limit_time != 0) {
- freq_input[i].time_in_over_limit += ktime_to_ms(ktime_get() -
- freq_input[i].last_over_limit_time);
- freq_input[i].last_over_limit_time = ktime_get();
- }
- }
- for (j = 0; j < CFLM_MAX_ITEM; j++) {
- len += snprintf(buf + len, MAX_BUF_SIZE - len, "%llu %llu %llu\n",
- freq_input[j].time_in_min_limit, freq_input[j].time_in_max_limit,
- freq_input[j].time_in_over_limit);
- }
- mutex_unlock(&cflm_mutex);
- return len;
- }
- static unsigned int cflm_get_table_freq(struct cpufreq_policy *policy,
- unsigned int target_freq, unsigned int relation)
- {
- unsigned int idx;
- target_freq = clamp_val(target_freq, policy->min, policy->max);
- if (!policy->freq_table)
- return target_freq;
- idx = cpufreq_frequency_table_target(policy, target_freq, relation);
- return policy->freq_table[idx].frequency;
- }
- static ssize_t vtable_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *buf)
- {
- ssize_t len = 0;
- int i = 0;
- struct cpufreq_policy *policy = cpufreq_cpu_get(param.g_first);
- unsigned int virt_clk = 0;
- if (!cflm_vbf.count)
- return len;
- if (param.vbf_offset > cflm_vbf.count) {
- pr_err("%s: bad condition(off(%d), cnt(%d))",
- __func__, param.vbf_offset, cflm_vbf.count);
- return len;
- }
- if (param.max_limit_val != LIMIT_RELEASE)
- len += snprintf(buf + len, MAX_BUF_SIZE, "!!!) please, read table again when no limit state\n");
- len += snprintf(buf + len, MAX_BUF_SIZE, "========================max===============================min================\n");
- len += snprintf(buf + len, MAX_BUF_SIZE, " virt | prime titan gold silver | prime titan gold silver\n");
- for (i = 0; i < param.freq_count; i++) {
- virt_clk = param.unified_cpuftbl[i];
- if (virt_clk > param.ltl_max_freq) {
- len += snprintf(buf + len, MAX_BUF_SIZE, " %7u | %7u %7u %7u %7u | %7u %7u %7u %7u\n",
- virt_clk,
- /* max = limit */
- virt_clk,
- cflm_get_vol_matched_freq(virt_clk),
- cflm_get_table_freq(policy, virt_clk, CPUFREQ_RELATION_H),
- cflm_get_silver_limit(virt_clk),
- /* min = boost */
- virt_clk,
- 0,
- cflm_get_table_freq(policy, virt_clk, CPUFREQ_RELATION_L),
- cflm_get_silver_boost(virt_clk));
- } else {
- len += snprintf(buf + len, MAX_BUF_SIZE, " %7u | %7u %7u %7u %7u | %7u %7u %7u %7u\n",
- virt_clk,
- /* max = limit */
- param.p_fmin,
- param.t_fmin,
- param.g_fmin,
- cflm_get_silver_limit(virt_clk),
- /* min = boost */
- 0,
- 0,
- 0,
- cflm_get_silver_boost(virt_clk));
- }
- }
- len += snprintf(buf + len, MAX_BUF_SIZE, "=============================================================================\n");
- cpufreq_cpu_put(policy);
- pr_info("%s: %s\n", __func__, buf);
- return len;
- }
- static ssize_t sched_boost_type_show(struct kobject *kobj,
- struct kobj_attribute *attr,
- char *buf)
- {
- return snprintf(buf, MAX_BUF_SIZE, "%d\n", param.sched_boost_type);
- }
- static ssize_t sched_boost_type_store(struct kobject *kobj,
- struct kobj_attribute *attr,
- const char *buf, size_t n)
- {
- int boost_type;
- int ret = -EINVAL;
- ret = kstrtoint(buf, 10, &boost_type);
- if (ret < 0) {
- pr_err("%s: cflm: Invalid cpufreq format\n", __func__);
- goto out;
- }
- mutex_lock(&cflm_mutex);
- if ((param.sched_boost_enabled) && (param.sched_boost_type != boost_type)) {
- pr_info("%s: sched boost is enabled(%d), reset(%d)\n", __func__, param.sched_boost_type, boost_type);
- sched_set_boost(param.sched_boost_type * -1);
- sched_set_boost(boost_type);
- }
- param.sched_boost_type = boost_type;
- pr_info("%s: sched boost type is changed to %d\n", __func__, param.sched_boost_type);
- mutex_unlock(&cflm_mutex);
- ret = n;
- out:
- return ret;
- }
- /* sysfs in /sys/power */
- static struct kobj_attribute cpufreq_table = {
- .attr = {
- .name = "cpufreq_table",
- .mode = 0444
- },
- .show = cpufreq_table_show,
- .store = NULL,
- };
- static struct kobj_attribute cpufreq_min_limit = {
- .attr = {
- .name = "cpufreq_min_limit",
- .mode = 0644
- },
- .show = cpufreq_min_limit_show,
- .store = cpufreq_min_limit_store,
- };
- static struct kobj_attribute cpufreq_max_limit = {
- .attr = {
- .name = "cpufreq_max_limit",
- .mode = 0644
- },
- .show = cpufreq_max_limit_show,
- .store = cpufreq_max_limit_store,
- };
- static struct kobj_attribute over_limit = {
- .attr = {
- .name = "over_limit",
- .mode = 0644
- },
- .show = over_limit_show,
- .store = over_limit_store,
- };
- static struct kobj_attribute limit_stat = {
- .attr = {
- .name = "limit_stat",
- .mode = 0644
- },
- .show = limit_stat_show,
- };
- static struct kobj_attribute vtable = {
- .attr = {
- .name = "vtable",
- .mode = 0444
- },
- .show = vtable_show,
- .store = NULL,
- };
- static struct kobj_attribute sched_boost_type = {
- .attr = {
- .name = "sched_boost_type",
- .mode = 0644
- },
- .show = sched_boost_type_show,
- .store = sched_boost_type_store,
- };
- int set_freq_limit(unsigned int id, unsigned int freq)
- {
- if (lpcharge) {
- pr_err("%s: not allowed in LPM\n", __func__);
- return 0;
- }
- mutex_lock(&cflm_mutex);
- pr_info("%s: cflm: id(%d) freq(%d)\n", __func__, (int)id, freq);
- cflm_freq_decision(id, freq, 0);
- mutex_unlock(&cflm_mutex);
- return 0;
- }
- EXPORT_SYMBOL_GPL(set_freq_limit);
- #define cflm_attr_rw(_name) \
- static struct kobj_attribute _name##_attr = \
- __ATTR(_name, 0644, show_##_name, store_##_name)
- #define show_one(file_name) \
- static ssize_t show_##file_name \
- (struct kobject *kobj, struct kobj_attribute *attr, char *buf) \
- { \
- return scnprintf(buf, PAGE_SIZE, "%u\n", param.file_name); \
- }
- #define store_one(file_name) \
- static ssize_t store_##file_name \
- (struct kobject *kobj, struct kobj_attribute *attr, \
- const char *buf, size_t count) \
- { \
- int ret; \
- \
- ret = sscanf(buf, "%u", ¶m.file_name); \
- if (ret != 1) \
- return -EINVAL; \
- \
- return count; \
- }
- /* votlage based */
- show_one(vol_based_clk);
- store_one(vol_based_clk);
- cflm_attr_rw(vol_based_clk);
- show_one(vbf_offset);
- store_one(vbf_offset);
- cflm_attr_rw(vbf_offset);
- /* adaptive boost */
- show_one(ab_enabled);
- store_one(ab_enabled);
- cflm_attr_rw(ab_enabled);
- static ssize_t show_cflm_info(struct kobject *kobj,
- struct kobj_attribute *attr, char *buf)
- {
- ssize_t len = 0;
- int i = 0;
- mutex_lock(&cflm_mutex);
- len += snprintf(buf, MAX_BUF_SIZE, "[basic info]\n");
- len += snprintf(buf + len, MAX_BUF_SIZE - len,
- "real: silver(%d ~ %d), gold(%d ~ %d), prime(%d ~ %d)\n",
- param.s_fmin, param.s_fmax,
- param.g_fmin, param.g_fmax,
- param.p_fmin, param.p_fmax);
- len += snprintf(buf + len, MAX_BUF_SIZE - len,
- "virt: little(%d ~ %d), big(%d ~ %d)\n",
- param.ltl_min_freq, param.ltl_max_freq,
- param.big_min_freq, param.big_max_freq);
- len += snprintf(buf + len, MAX_BUF_SIZE - len,
- "param: div(%d), sched boost(%d)\n",
- param.silver_divider, param.sched_boost_type);
- len += snprintf(buf + len, MAX_BUF_SIZE - len,
- "param: vbf(%d), offset(%d), aboost(%d)\n",
- param.vol_based_clk, param.vbf_offset, param.ab_enabled);
- len += snprintf(buf + len, MAX_BUF_SIZE - len, "[requested info]\n");
- for (i = 0; i < CFLM_MAX_ITEM; i++) {
- len += snprintf(buf + len, MAX_BUF_SIZE - len,
- "requested: [%d] min(%d), max(%d)\n",
- i, freq_input[i].min, freq_input[i].max);
- }
- len += snprintf(buf + len, MAX_BUF_SIZE - len, "[aboost table]\n");
- if ((param.ab_enabled) && (param.ab_table)) {
- for (i = 0; i < NUM_CPUS; i++) {
- len += snprintf(buf + len, MAX_BUF_SIZE - len, "cpu%d: %d %d %d\n",
- i, param.ab_table[i].threshold,
- param.ab_table[i].high, param.ab_table[i].low);
- }
- }
- mutex_unlock(&cflm_mutex);
- return len;
- }
- static struct kobj_attribute cflm_info =
- __ATTR(info, 0444, show_cflm_info, NULL);
- static struct attribute *cflm_attributes[] = {
- &cpufreq_table.attr,
- &cpufreq_min_limit.attr,
- &cpufreq_max_limit.attr,
- &over_limit.attr,
- &limit_stat.attr,
- &cflm_info.attr,
- &vtable.attr,
- &sched_boost_type.attr,
- &vol_based_clk_attr.attr,
- &vbf_offset_attr.attr,
- &ab_enabled_attr.attr,
- NULL,
- };
- static struct attribute_group cflm_attr_group = {
- .attrs = cflm_attributes,
- };
- #ifdef CONFIG_OF
- static void cflm_parse_dt(struct platform_device *pdev)
- {
- int size = 0;
- if (!pdev->dev.of_node) {
- pr_info("%s: no device tree\n", __func__);
- return;
- }
- /* voltage based */
- param.vol_based_clk = of_property_read_bool(pdev->dev.of_node, "limit,vol_based_clk");
- of_property_read_u32(pdev->dev.of_node, "limit,vbf_offset", ¶m.vbf_offset);
- pr_info("%s: param: voltage based clock: %s(offset %d)\n",
- __func__, param.vol_based_clk ? "true" : "false", param.vbf_offset);
- /* boost table */
- of_get_property(pdev->dev.of_node, "limit,silver_boost_table", &size);
- if (size) {
- param.silver_boost_map = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
- of_property_read_u32_array(pdev->dev.of_node, "limit,silver_boost_table",
- (u32 *)param.silver_boost_map, size / sizeof(u32));
- param.boost_map_size = size / sizeof(*param.silver_boost_map);
- }
- pr_info("%s: param: boost map size(%d)\n", __func__, param.boost_map_size);
- /* limit table */
- size = 0;
- of_get_property(pdev->dev.of_node, "limit,silver_limit_table", &size);
- if (size) {
- param.silver_limit_map = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
- of_property_read_u32_array(pdev->dev.of_node, "limit,silver_limit_table",
- (u32 *)param.silver_limit_map, size / sizeof(u32));
- param.limit_map_size = size / sizeof(*param.silver_limit_map);
- }
- pr_info("%s: param: limit map size(%d)\n", __func__, param.limit_map_size);
- /* adaptive boost */
- size = 0;
- of_get_property(pdev->dev.of_node, "limit,ab_table", &size);
- if (size) {
- param.ab_table = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
- of_property_read_u32_array(pdev->dev.of_node, "limit,ab_table",
- (u32 *)param.ab_table, size / sizeof(u32));
- param.ab_enabled = 1;
- pr_info("%s: param: aboost enabled(%d)\n", __func__, size);
- } else {
- param.ab_enabled = 0;
- pr_info("%s: param: no aboost table(%d)\n", __func__, size);
- }
- /* lowest freq */
- of_property_read_u32(pdev->dev.of_node, "limit,gold_fmin", ¶m.g_fmin);
- of_property_read_u32(pdev->dev.of_node, "limit,titanium_fmin", ¶m.t_fmin);
- of_property_read_u32(pdev->dev.of_node, "limit,prime_fmin", ¶m.p_fmin);
- /* etc */
- of_property_read_u32(pdev->dev.of_node, "limit,gold_fmin_up", ¶m.g_fmin_up);
- of_property_read_u32(pdev->dev.of_node, "limit,little_max_freq", ¶m.ltl_max_freq);
- of_property_read_u32(pdev->dev.of_node, "limit,big_min_freq", ¶m.big_min_freq);
- /* sm8650 4 cluster(silver, gold, titanium, prime) */
- param.titanium = of_property_read_bool(pdev->dev.of_node, "limit,support_titanium");
- of_node_put(pdev->dev.of_node);
- pr_info("%s: param: g_fmin_up(%d), ltl_max_freq(%d), big_min_freq(%d), titanium(%d)\n", __func__,
- param.g_fmin_up, param.ltl_max_freq, param.big_min_freq, param.titanium);
- };
- #endif
- int cflm_add_qos(void)
- {
- struct cpufreq_policy *policy;
- unsigned int i = 0;
- unsigned int j = 0;
- int ret = 0;
- for_each_possible_cpu(i) {
- policy = cpufreq_cpu_get(i);
- if (!policy) {
- pr_err("no policy for cpu%d\n", i);
- ret = -EPROBE_DEFER;
- break;
- }
- for (j = 0; j < CFLM_MAX_ITEM; j++) {
- ret = freq_qos_add_request(&policy->constraints,
- &min_req[i][j],
- FREQ_QOS_MIN, policy->cpuinfo.min_freq);
- if (ret < 0) {
- pr_err("%s: failed to add min req(%d)\n", __func__, ret);
- break;
- }
- cflm_req_init[i] |= BIT(j*2);
- ret = freq_qos_add_request(&policy->constraints,
- &max_req[i][j],
- FREQ_QOS_MAX, policy->cpuinfo.max_freq);
- if (ret < 0) {
- pr_err("%s: failed to add max req(%d)\n", __func__, ret);
- break;
- }
- cflm_req_init[i] |= BIT(j*2+1);
- }
- if (ret < 0) {
- cpufreq_cpu_put(policy);
- break;
- }
- if (i == param.s_first) {
- if (!param.s_fmin)
- param.s_fmin = policy->cpuinfo.min_freq;
- param.s_fmax = policy->cpuinfo.max_freq;
- }
- if (i == param.g_first) {
- if (!param.g_fmin)
- param.g_fmin = policy->cpuinfo.min_freq;
- param.g_fmax = policy->cpuinfo.max_freq;
- }
- if (i == param.p_first) {
- if (!param.p_fmin)
- param.p_fmin = policy->cpuinfo.min_freq;
- param.p_fmax = policy->cpuinfo.max_freq;
- }
- if (i == param.t_first) {
- if (!param.t_fmin)
- param.t_fmin = policy->cpuinfo.min_freq;
- param.t_fmax = policy->cpuinfo.max_freq;
- }
- cflm_set_table(policy->cpu, policy->freq_table);
- cpufreq_cpu_put(policy);
- }
- return ret;
- }
- void cflm_remove_qos(void)
- {
- unsigned int i = 0;
- unsigned int j = 0;
- int ret = 0;
- pr_info("%s\n", __func__);
- for_each_possible_cpu(i) {
- for (j = 0; j < CFLM_MAX_ITEM; j++) {
- if (cflm_req_init[i] & BIT(j*2)) {
- //pr_info("%s: try to remove min[%d][%d] req\n", __func__, i, j);
- ret = freq_qos_remove_request(&min_req[i][j]);
- if (ret < 0)
- pr_err("%s: failed to remove min_req (%d)\n", __func__, ret);
- }
- if (cflm_req_init[i] & BIT(j*2+1)) {
- //pr_info("%s: try to remove max[%d][%d] req\n", __func__, i, j);
- ret = freq_qos_remove_request(&max_req[i][j]);
- if (ret < 0)
- pr_err("%s: failed to remove max_req (%d)\n", __func__, ret);
- }
- }
- cflm_req_init[i] = 0U;
- }
- }
- int cflm_probe(struct platform_device *pdev)
- {
- int ret;
- pr_info("%s\n", __func__);
- if (lpcharge) {
- pr_info("%s: dummy for LPM\n", __func__);
- return 0;
- }
- #ifdef CONFIG_OF
- cflm_parse_dt(pdev);
- #endif
- ret = cflm_add_qos();
- if (ret < 0)
- goto policy_not_ready;
- cflm_kobj = kobject_create_and_add("cpufreq_limit",
- &cpu_subsys.dev_root->kobj);
- if (!cflm_kobj) {
- pr_err("Unable to cread cflm_kobj\n");
- goto object_create_failed;
- }
- ret = sysfs_create_group(cflm_kobj, &cflm_attr_group);
- if (ret) {
- pr_err("Unable to create cflm group\n");
- goto group_create_failed;
- }
- pr_info("%s done\n", __func__);
- return ret;
- group_create_failed:
- kobject_put(cflm_kobj);
- object_create_failed:
- cflm_kobj = NULL;
- policy_not_ready:
- cflm_remove_qos();
- return ret;
- }
- static int cflm_remove(struct platform_device *pdev)
- {
- pr_info("%s\n", __func__);
- if (!lpcharge && cflm_kobj) {
- cflm_remove_qos();
- sysfs_remove_group(cflm_kobj, &cflm_attr_group);
- kobject_put(cflm_kobj);
- cflm_kobj = NULL;
- }
- return 0;
- }
- static const struct of_device_id cflm_match_table[] = {
- { .compatible = "cpufreq_limit" },
- {}
- };
- static struct platform_driver cflm_driver = {
- .driver = {
- .name = "cpufreq_limit",
- .of_match_table = cflm_match_table,
- },
- .probe = cflm_probe,
- .remove = cflm_remove,
- };
- static int __init cflm_init(void)
- {
- return platform_driver_register(&cflm_driver);
- }
- static void __exit cflm_exit(void)
- {
- platform_driver_unregister(&cflm_driver);
- }
- MODULE_AUTHOR("Sangyoung Son <[email protected]");
- MODULE_DESCRIPTION("'cpufreq_limit' - A driver to limit cpu frequency");
- MODULE_LICENSE("GPL");
- late_initcall(cflm_init);
- module_exit(cflm_exit);
|