Loading drivers/oneplus/coretech/Kconfig +6 −0 Original line number Diff line number Diff line Loading @@ -92,3 +92,9 @@ config ZWB_HANDLE bool "zram writeback feature" help zram writeback feature config CPUFREQ_BOUNCING default n bool "A power optimization feature to control excessive heating of devices" config OPLUS_FEATURE_SUGOV_TL default n bool "A power optimization feature target loads" drivers/oneplus/coretech/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -34,3 +34,4 @@ obj-$(CONFIG_TPP) += tpp/ obj-$(CONFIG_MEMEX_STANDALONE) += memex_standalone_support/core/ obj-$(CONFIG_RATP) += ratp/ obj-$(CONFIG_ZWB_HANDLE) += zwb_handle/ obj-$(CONFIG_CPUFREQ_BOUNCING) += cpufreq_bouncing/ drivers/oneplus/coretech/cpufreq_bouncing/Makefile 0 → 100644 +1 −0 Original line number Diff line number Diff line obj-$(CONFIG_CPUFREQ_BOUNCING) += cpufreq_bouncing.o drivers/oneplus/coretech/cpufreq_bouncing/cpufreq_bouncing.c 0 → 100644 +495 −0 Original line number Diff line number Diff line #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/jiffies.h> #include <linux/cpufreq.h> #include <linux/sched/sysctl.h> #define NSEC_TO_MSEC(val) (val / NSEC_PER_MSEC) #define MSEC_TO_NSEC(val) (val * NSEC_PER_MSEC) #define CLUS_MAX 3 struct cpufreq_bouncing { /* statistics */ u64 last_ts; u64 last_freq_update_ts; u64 acc; /* restriction */ int limit_freq; int limit_level; u64 limit_thres; /* freqs */ int freq_sorting; int freq_levels; unsigned int *freqs; // quick mapping /* config */ bool enable; int cur_level; /* config: ceil */ int max_level; int down_speed; s64 down_limit_ns; unsigned int max_freq; /* config: floor */ int min_level; int up_speed; s64 up_limit_ns; unsigned int min_freq; } cb_stuff[CLUS_MAX] = { /* silver */ { }, /* gold */ { .enable = true, .down_limit_ns = 50 * NSEC_PER_MSEC, .up_limit_ns = 50 * NSEC_PER_MSEC, .limit_thres = 30 * NSEC_PER_MSEC, .limit_freq = 2092800, .limit_level = 6, .down_speed = 1, .up_speed = 1, }, /* gold prime */ { .enable = true, .down_limit_ns = 50 * NSEC_PER_MSEC, .up_limit_ns = 50 * NSEC_PER_MSEC, .limit_thres = 30 * NSEC_PER_MSEC, .limit_freq = 2304000 , .limit_level = 6, .down_speed = 1, .up_speed = 1, } }; /* init config */ static bool enable = false; module_param_named(enable, enable, bool, 0644); static bool debug = false; module_param_named(debug, debug, bool, 0644); static unsigned int decay = 80; module_param_named(decay, decay, uint, 0644); static DEFINE_PER_CPU(struct cpufreq_bouncing*, cbs); static int cb_pol_idx = 0; static bool cb_switch = false; static inline struct cpufreq_bouncing* cb_get(int cpu) { if (likely(cb_switch)) return per_cpu(cbs, cpu); return NULL; } /* module parameters */ static int cb_config_store(const char *buf, const struct kernel_param *kp) { /* for limit_thres, down/up limit will use ms as unit format: echo "2,0,5,3000,3,2000,3,2000" > config */ struct pack { int clus; bool enable; int limit_level; u64 limit_thres_ms; int down_speed; s64 down_limit_ms; int up_speed; s64 up_limit_ms; } v; struct cpufreq_bouncing *cb; if (debug) pr_info("%s\n", buf); if (sscanf(buf, "%d,%d,%d,%llu,%d,%lld,%d,%lld\n", &v.clus, &v.enable, &v.limit_level, &v.limit_thres_ms, &v.down_speed, &v.down_limit_ms, &v.up_speed, &v.up_limit_ms) != 8) goto out; if (v.clus < 0 || v.clus >= cb_pol_idx) goto out; cb = &cb_stuff[v.clus]; if (v.limit_level < 0 || v.limit_level > cb->max_level) goto out; if (v.down_speed < 0 || v.down_speed > cb->freq_levels) goto out; if (v.up_speed < 0 || v.up_speed > cb->freq_levels) goto out; if (v.down_limit_ms < 0 || v.up_limit_ms < 0 || v.limit_thres_ms < 0) goto out; /* begin update config */ cb->enable = false; cb->last_ts = 0; cb->last_freq_update_ts = 0; cb->acc = 0; cb->limit_level = v.limit_level; if (cb->freqs) cb->limit_freq = cb->freqs[cb->limit_level]; cb->limit_thres = MSEC_TO_NSEC(v.limit_thres_ms); cb->down_speed = v.down_speed; cb->down_limit_ns = MSEC_TO_NSEC(v.down_limit_ms); cb->up_speed = v.up_speed; cb->up_limit_ns = MSEC_TO_NSEC(v.up_limit_ms); cb->enable = v.enable; return 0; out: pr_warn("config: invalid:%s\n", buf); return 0; } static struct kernel_param_ops cb_config_ops = { .set = cb_config_store, }; module_param_cb(config, &cb_config_ops, NULL, 0220); static int cb_dump_show(char *buf, const struct kernel_param *kp) { struct cpufreq_bouncing *cb; int i, j, cnt = 0; for (i = 0; i < min(CLUS_MAX, cb_pol_idx); ++i) { cb = &cb_stuff[i]; cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "=====\n"); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "clus %d (switch %d)\n", i, cb_switch); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "last_ts: %llu\n", cb->last_ts); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "last_freq_update_ts: %llu\n", cb->last_freq_update_ts); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "acc: %llu\n", cb->acc); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "limit_freq: %d\n", cb->limit_freq); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "limit_level: %d\n", cb->limit_level); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "limit_thres: %llu\n", cb->limit_thres); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "enable: %d\n", cb->enable); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "cur_level: %d\n", cb->cur_level); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "max_freq: %u %d\n", cb->max_freq, cb->max_level); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "down_speed: %d\n", cb->down_speed); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "down_limit_ns: %lld\n", cb->down_limit_ns); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "min_freq: %u %d\n", cb->min_freq, cb->min_level); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "up_speed: %d\n", cb->up_speed); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "up_limit_ns: %lld\n", cb->up_limit_ns); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "freq_sorting: %d\n", cb->freq_sorting); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "freq_levels: %d\n", cb->freq_levels); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "freq idx:"); for (j = 0; j < cb->freq_levels; ++j) cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "\t%u", j); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "\n"); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "freq clk:"); for (j = 0; j < cb->freq_levels; ++j) cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "\t%u", cb->freqs[j]); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "\n"); } return cnt; } static struct kernel_param_ops cb_dump_ops = { .get = cb_dump_show, }; module_param_cb(dump, &cb_dump_ops, NULL, 0444); unsigned int cb_get_cap(int cpu) { struct cpufreq_bouncing *cb = cb_get(cpu); /* return UINT_MAX as no limited */ if (unlikely(!cb)) return UINT_MAX; if (unlikely(cb->freqs)) return UINT_MAX; return cb->freqs[cb->cur_level]; } unsigned int cb_cap(struct cpufreq_policy *pol, unsigned int freq) { struct cpufreq_bouncing *cb; unsigned int capped = freq; int cpu; if (!enable) return freq; /* can remove since we're calling from sugov_fast_switch path */ if (!pol->fast_switch_enabled) return freq; cpu = cpumask_first(pol->related_cpus); cb = cb_get(cpu); if (unlikely(!cb)) return freq; if (!cb->enable) return freq; /* don't have quick mapping */ if (unlikely(!cb->freqs)) return freq; capped = min(freq, cb->freqs[cb->cur_level]); if (debug) pr_info("cpu %d, orig %u, capped %d\n", cpu, freq, capped); return capped; } static inline bool clus_isolated(int cpu) { struct cpufreq_policy *pol = cpufreq_cpu_get_raw(cpu); int i; if (unlikely(!pol)) return true; for_each_cpu(i, pol->related_cpus) if (!cpu_isolated(i)) return false; return true; } void cb_reset(int cpu, u64 time) { struct cpufreq_bouncing *cb; if (!enable) return; cb = cb_get(cpu); if (!cb) return; /* reset only when cluster has no active cpu */ if (!clus_isolated(cpu)) return; cb->last_ts = time; cb->last_freq_update_ts = time; cb->acc = 0; cb->cur_level = cb->max_level; } void cb_update(struct cpufreq_policy *pol, u64 time) { // TODO can skip a bit? struct cpufreq_bouncing *cb; u64 delta, update_delta; int cpu; if (!enable) return; cpu = cpumask_first(pol->related_cpus); cb = cb_get(cpu); if (unlikely(!cb)) return; if (!cb->enable) return; if (unlikely(!pol->fast_switch_enabled)) return; /* for first update */ if (unlikely(!cb->last_ts)) cb->last_ts = cb->last_freq_update_ts = time; // FIXME check overflow // NOTE pol->cur should always updated delta = time - cb->last_ts; update_delta = time - cb->last_freq_update_ts; /* check cpufreq */ if (pol->cur >= cb->limit_freq) { /* accumulate delta time */ cb->acc += delta; } else { /* decay accumulate time */ cb->acc = cb->acc * decay / 100;; } /* check if need to update limitation */ if (cb->acc >= cb->limit_thres) { /* check last update */ if (update_delta >= cb->down_limit_ns) { cb->cur_level -= cb->down_speed; cb->last_freq_update_ts = time; } } else { /* check last update */ if (update_delta >= cb->up_limit_ns) { cb->cur_level += cb->up_speed; cb->last_freq_update_ts = time; } } /* cap level */ cb->cur_level = max(cb->limit_level, min(cb->cur_level, cb->max_level)); if (debug) pr_info("cpu %d update: ts now %llu last %llu last_update %llu delta %llu update_d %llu cur %u acc %llu, cur_level %d\n", cpu, NSEC_TO_MSEC(time), NSEC_TO_MSEC(cb->last_ts), NSEC_TO_MSEC(cb->last_freq_update_ts), NSEC_TO_MSEC(delta), NSEC_TO_MSEC(update_delta), pol->cur, NSEC_TO_MSEC(cb->acc), cb->cur_level); cb->last_ts = time; } static int __cpufreq_policy_parser(int cpu, int cb_idx) { struct cpufreq_policy *pol = cpufreq_cpu_get_raw(cpu); struct cpufreq_frequency_table *table, *pos; struct cpufreq_bouncing* cb; unsigned int freq = 0, max_freq = 0, min_freq = UINT_MAX; int idx, freq_levels = 0; if (unlikely(!pol)) { pr_err("cpu %d can't find realted cpufreq policy\n", cpu); return -1; } if (cb_idx >= CLUS_MAX) { pr_err("clus %d out of limit\n", cb_idx); return -1; } cb = &cb_stuff[cb_idx]; table = pol->freq_table; cb->freq_sorting = pol->freq_table_sorted; /* get freq_levels */ cpufreq_for_each_valid_entry_idx(pos, table, idx) { ++freq_levels; freq = pos->frequency; if (freq > max_freq) { max_freq = freq; cb->max_level = idx; cb->cur_level = idx; cb->max_freq = max_freq; } if (freq < min_freq) { min_freq = freq; cb->min_level = idx; cb->min_freq = min_freq; } } cb->freq_levels = freq_levels; cb->freqs = (unsigned int *) kmalloc(sizeof(unsigned int) * freq_levels, GFP_KERNEL); if (cb->freqs) { /* setup freqs */ idx = 0; table = pol->freq_table; cpufreq_for_each_valid_entry_idx(pos, table, idx) { freq = pos->frequency; cb->freqs[idx] = freq; } } else { pr_err("can't alloc memory for freqs\n"); cb->enable = false; } return cpu + cpumask_weight(pol->related_cpus); } static void cb_parse_cpufreq(void) { int i = 0, j, prev = 0; bool valid = true; while (i != nr_cpu_ids && cb_pol_idx != CLUS_MAX) { i = __cpufreq_policy_parser(i, cb_pol_idx); if (i < 0) break; for (j = prev; j < i; ++j) per_cpu(cbs, j) = &cb_stuff[cb_pol_idx]; prev = i; ++cb_pol_idx; } for (i = 0; i < nr_cpu_ids && valid; ++i) { if (!per_cpu(cbs, i)) { pr_warn("break on cpu%d\n", i); valid = false; } } cb_switch = valid; smp_wmb(); } static void cb_clean_up(void) { struct cpufreq_bouncing *cb; int i; enable = false; smp_wmb(); for (i = 0; i < CLUS_MAX; ++i) { cb = &cb_stuff[i]; if (cb->freqs) { kfree(cb->freqs); cb->freqs = NULL; } } } static int __init cb_init(void) { pr_info("cpufreq bouncing init\n"); cb_parse_cpufreq(); return 0; } static void __exit cb_exit(void) { cb_clean_up(); pr_info("cpufreq bouncing exit\n"); } device_initcall(cb_init); drivers/oneplus/include/linux/oem/cpufreq_bouncing.h 0 → 100644 +19 −0 Original line number Diff line number Diff line #ifndef __CPUFREQ_BOUNCING_H__ #define __CPUFREQ_BOUNCING_H__ #include <linux/cpufreq.h> #ifdef CONFIG_CPUFREQ_BOUNCING void cb_update(struct cpufreq_policy *pol, u64 time); void cb_reset(int cpu, u64 time); unsigned int cb_cap(struct cpufreq_policy *pol, unsigned int freq); unsigned int cb_get_cap(int cpu); #else static inline void cb_reset(int cpu, u64 time) {} static inline void cb_update(struct cpufreq_policy *pol, u64 time) {} static inline unsigned int cb_cap(struct cpufreq_policy *pol, unsigned int freq) { return freq; } static inline unsigned int cb_get_cap(int cpu) { return UINT_MAX; } #endif #endif Loading
drivers/oneplus/coretech/Kconfig +6 −0 Original line number Diff line number Diff line Loading @@ -92,3 +92,9 @@ config ZWB_HANDLE bool "zram writeback feature" help zram writeback feature config CPUFREQ_BOUNCING default n bool "A power optimization feature to control excessive heating of devices" config OPLUS_FEATURE_SUGOV_TL default n bool "A power optimization feature target loads"
drivers/oneplus/coretech/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -34,3 +34,4 @@ obj-$(CONFIG_TPP) += tpp/ obj-$(CONFIG_MEMEX_STANDALONE) += memex_standalone_support/core/ obj-$(CONFIG_RATP) += ratp/ obj-$(CONFIG_ZWB_HANDLE) += zwb_handle/ obj-$(CONFIG_CPUFREQ_BOUNCING) += cpufreq_bouncing/
drivers/oneplus/coretech/cpufreq_bouncing/Makefile 0 → 100644 +1 −0 Original line number Diff line number Diff line obj-$(CONFIG_CPUFREQ_BOUNCING) += cpufreq_bouncing.o
drivers/oneplus/coretech/cpufreq_bouncing/cpufreq_bouncing.c 0 → 100644 +495 −0 Original line number Diff line number Diff line #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/jiffies.h> #include <linux/cpufreq.h> #include <linux/sched/sysctl.h> #define NSEC_TO_MSEC(val) (val / NSEC_PER_MSEC) #define MSEC_TO_NSEC(val) (val * NSEC_PER_MSEC) #define CLUS_MAX 3 struct cpufreq_bouncing { /* statistics */ u64 last_ts; u64 last_freq_update_ts; u64 acc; /* restriction */ int limit_freq; int limit_level; u64 limit_thres; /* freqs */ int freq_sorting; int freq_levels; unsigned int *freqs; // quick mapping /* config */ bool enable; int cur_level; /* config: ceil */ int max_level; int down_speed; s64 down_limit_ns; unsigned int max_freq; /* config: floor */ int min_level; int up_speed; s64 up_limit_ns; unsigned int min_freq; } cb_stuff[CLUS_MAX] = { /* silver */ { }, /* gold */ { .enable = true, .down_limit_ns = 50 * NSEC_PER_MSEC, .up_limit_ns = 50 * NSEC_PER_MSEC, .limit_thres = 30 * NSEC_PER_MSEC, .limit_freq = 2092800, .limit_level = 6, .down_speed = 1, .up_speed = 1, }, /* gold prime */ { .enable = true, .down_limit_ns = 50 * NSEC_PER_MSEC, .up_limit_ns = 50 * NSEC_PER_MSEC, .limit_thres = 30 * NSEC_PER_MSEC, .limit_freq = 2304000 , .limit_level = 6, .down_speed = 1, .up_speed = 1, } }; /* init config */ static bool enable = false; module_param_named(enable, enable, bool, 0644); static bool debug = false; module_param_named(debug, debug, bool, 0644); static unsigned int decay = 80; module_param_named(decay, decay, uint, 0644); static DEFINE_PER_CPU(struct cpufreq_bouncing*, cbs); static int cb_pol_idx = 0; static bool cb_switch = false; static inline struct cpufreq_bouncing* cb_get(int cpu) { if (likely(cb_switch)) return per_cpu(cbs, cpu); return NULL; } /* module parameters */ static int cb_config_store(const char *buf, const struct kernel_param *kp) { /* for limit_thres, down/up limit will use ms as unit format: echo "2,0,5,3000,3,2000,3,2000" > config */ struct pack { int clus; bool enable; int limit_level; u64 limit_thres_ms; int down_speed; s64 down_limit_ms; int up_speed; s64 up_limit_ms; } v; struct cpufreq_bouncing *cb; if (debug) pr_info("%s\n", buf); if (sscanf(buf, "%d,%d,%d,%llu,%d,%lld,%d,%lld\n", &v.clus, &v.enable, &v.limit_level, &v.limit_thres_ms, &v.down_speed, &v.down_limit_ms, &v.up_speed, &v.up_limit_ms) != 8) goto out; if (v.clus < 0 || v.clus >= cb_pol_idx) goto out; cb = &cb_stuff[v.clus]; if (v.limit_level < 0 || v.limit_level > cb->max_level) goto out; if (v.down_speed < 0 || v.down_speed > cb->freq_levels) goto out; if (v.up_speed < 0 || v.up_speed > cb->freq_levels) goto out; if (v.down_limit_ms < 0 || v.up_limit_ms < 0 || v.limit_thres_ms < 0) goto out; /* begin update config */ cb->enable = false; cb->last_ts = 0; cb->last_freq_update_ts = 0; cb->acc = 0; cb->limit_level = v.limit_level; if (cb->freqs) cb->limit_freq = cb->freqs[cb->limit_level]; cb->limit_thres = MSEC_TO_NSEC(v.limit_thres_ms); cb->down_speed = v.down_speed; cb->down_limit_ns = MSEC_TO_NSEC(v.down_limit_ms); cb->up_speed = v.up_speed; cb->up_limit_ns = MSEC_TO_NSEC(v.up_limit_ms); cb->enable = v.enable; return 0; out: pr_warn("config: invalid:%s\n", buf); return 0; } static struct kernel_param_ops cb_config_ops = { .set = cb_config_store, }; module_param_cb(config, &cb_config_ops, NULL, 0220); static int cb_dump_show(char *buf, const struct kernel_param *kp) { struct cpufreq_bouncing *cb; int i, j, cnt = 0; for (i = 0; i < min(CLUS_MAX, cb_pol_idx); ++i) { cb = &cb_stuff[i]; cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "=====\n"); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "clus %d (switch %d)\n", i, cb_switch); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "last_ts: %llu\n", cb->last_ts); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "last_freq_update_ts: %llu\n", cb->last_freq_update_ts); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "acc: %llu\n", cb->acc); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "limit_freq: %d\n", cb->limit_freq); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "limit_level: %d\n", cb->limit_level); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "limit_thres: %llu\n", cb->limit_thres); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "enable: %d\n", cb->enable); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "cur_level: %d\n", cb->cur_level); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "max_freq: %u %d\n", cb->max_freq, cb->max_level); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "down_speed: %d\n", cb->down_speed); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "down_limit_ns: %lld\n", cb->down_limit_ns); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "min_freq: %u %d\n", cb->min_freq, cb->min_level); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "up_speed: %d\n", cb->up_speed); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "up_limit_ns: %lld\n", cb->up_limit_ns); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "freq_sorting: %d\n", cb->freq_sorting); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "freq_levels: %d\n", cb->freq_levels); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "freq idx:"); for (j = 0; j < cb->freq_levels; ++j) cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "\t%u", j); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "\n"); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "freq clk:"); for (j = 0; j < cb->freq_levels; ++j) cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "\t%u", cb->freqs[j]); cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "\n"); } return cnt; } static struct kernel_param_ops cb_dump_ops = { .get = cb_dump_show, }; module_param_cb(dump, &cb_dump_ops, NULL, 0444); unsigned int cb_get_cap(int cpu) { struct cpufreq_bouncing *cb = cb_get(cpu); /* return UINT_MAX as no limited */ if (unlikely(!cb)) return UINT_MAX; if (unlikely(cb->freqs)) return UINT_MAX; return cb->freqs[cb->cur_level]; } unsigned int cb_cap(struct cpufreq_policy *pol, unsigned int freq) { struct cpufreq_bouncing *cb; unsigned int capped = freq; int cpu; if (!enable) return freq; /* can remove since we're calling from sugov_fast_switch path */ if (!pol->fast_switch_enabled) return freq; cpu = cpumask_first(pol->related_cpus); cb = cb_get(cpu); if (unlikely(!cb)) return freq; if (!cb->enable) return freq; /* don't have quick mapping */ if (unlikely(!cb->freqs)) return freq; capped = min(freq, cb->freqs[cb->cur_level]); if (debug) pr_info("cpu %d, orig %u, capped %d\n", cpu, freq, capped); return capped; } static inline bool clus_isolated(int cpu) { struct cpufreq_policy *pol = cpufreq_cpu_get_raw(cpu); int i; if (unlikely(!pol)) return true; for_each_cpu(i, pol->related_cpus) if (!cpu_isolated(i)) return false; return true; } void cb_reset(int cpu, u64 time) { struct cpufreq_bouncing *cb; if (!enable) return; cb = cb_get(cpu); if (!cb) return; /* reset only when cluster has no active cpu */ if (!clus_isolated(cpu)) return; cb->last_ts = time; cb->last_freq_update_ts = time; cb->acc = 0; cb->cur_level = cb->max_level; } void cb_update(struct cpufreq_policy *pol, u64 time) { // TODO can skip a bit? struct cpufreq_bouncing *cb; u64 delta, update_delta; int cpu; if (!enable) return; cpu = cpumask_first(pol->related_cpus); cb = cb_get(cpu); if (unlikely(!cb)) return; if (!cb->enable) return; if (unlikely(!pol->fast_switch_enabled)) return; /* for first update */ if (unlikely(!cb->last_ts)) cb->last_ts = cb->last_freq_update_ts = time; // FIXME check overflow // NOTE pol->cur should always updated delta = time - cb->last_ts; update_delta = time - cb->last_freq_update_ts; /* check cpufreq */ if (pol->cur >= cb->limit_freq) { /* accumulate delta time */ cb->acc += delta; } else { /* decay accumulate time */ cb->acc = cb->acc * decay / 100;; } /* check if need to update limitation */ if (cb->acc >= cb->limit_thres) { /* check last update */ if (update_delta >= cb->down_limit_ns) { cb->cur_level -= cb->down_speed; cb->last_freq_update_ts = time; } } else { /* check last update */ if (update_delta >= cb->up_limit_ns) { cb->cur_level += cb->up_speed; cb->last_freq_update_ts = time; } } /* cap level */ cb->cur_level = max(cb->limit_level, min(cb->cur_level, cb->max_level)); if (debug) pr_info("cpu %d update: ts now %llu last %llu last_update %llu delta %llu update_d %llu cur %u acc %llu, cur_level %d\n", cpu, NSEC_TO_MSEC(time), NSEC_TO_MSEC(cb->last_ts), NSEC_TO_MSEC(cb->last_freq_update_ts), NSEC_TO_MSEC(delta), NSEC_TO_MSEC(update_delta), pol->cur, NSEC_TO_MSEC(cb->acc), cb->cur_level); cb->last_ts = time; } static int __cpufreq_policy_parser(int cpu, int cb_idx) { struct cpufreq_policy *pol = cpufreq_cpu_get_raw(cpu); struct cpufreq_frequency_table *table, *pos; struct cpufreq_bouncing* cb; unsigned int freq = 0, max_freq = 0, min_freq = UINT_MAX; int idx, freq_levels = 0; if (unlikely(!pol)) { pr_err("cpu %d can't find realted cpufreq policy\n", cpu); return -1; } if (cb_idx >= CLUS_MAX) { pr_err("clus %d out of limit\n", cb_idx); return -1; } cb = &cb_stuff[cb_idx]; table = pol->freq_table; cb->freq_sorting = pol->freq_table_sorted; /* get freq_levels */ cpufreq_for_each_valid_entry_idx(pos, table, idx) { ++freq_levels; freq = pos->frequency; if (freq > max_freq) { max_freq = freq; cb->max_level = idx; cb->cur_level = idx; cb->max_freq = max_freq; } if (freq < min_freq) { min_freq = freq; cb->min_level = idx; cb->min_freq = min_freq; } } cb->freq_levels = freq_levels; cb->freqs = (unsigned int *) kmalloc(sizeof(unsigned int) * freq_levels, GFP_KERNEL); if (cb->freqs) { /* setup freqs */ idx = 0; table = pol->freq_table; cpufreq_for_each_valid_entry_idx(pos, table, idx) { freq = pos->frequency; cb->freqs[idx] = freq; } } else { pr_err("can't alloc memory for freqs\n"); cb->enable = false; } return cpu + cpumask_weight(pol->related_cpus); } static void cb_parse_cpufreq(void) { int i = 0, j, prev = 0; bool valid = true; while (i != nr_cpu_ids && cb_pol_idx != CLUS_MAX) { i = __cpufreq_policy_parser(i, cb_pol_idx); if (i < 0) break; for (j = prev; j < i; ++j) per_cpu(cbs, j) = &cb_stuff[cb_pol_idx]; prev = i; ++cb_pol_idx; } for (i = 0; i < nr_cpu_ids && valid; ++i) { if (!per_cpu(cbs, i)) { pr_warn("break on cpu%d\n", i); valid = false; } } cb_switch = valid; smp_wmb(); } static void cb_clean_up(void) { struct cpufreq_bouncing *cb; int i; enable = false; smp_wmb(); for (i = 0; i < CLUS_MAX; ++i) { cb = &cb_stuff[i]; if (cb->freqs) { kfree(cb->freqs); cb->freqs = NULL; } } } static int __init cb_init(void) { pr_info("cpufreq bouncing init\n"); cb_parse_cpufreq(); return 0; } static void __exit cb_exit(void) { cb_clean_up(); pr_info("cpufreq bouncing exit\n"); } device_initcall(cb_init);
drivers/oneplus/include/linux/oem/cpufreq_bouncing.h 0 → 100644 +19 −0 Original line number Diff line number Diff line #ifndef __CPUFREQ_BOUNCING_H__ #define __CPUFREQ_BOUNCING_H__ #include <linux/cpufreq.h> #ifdef CONFIG_CPUFREQ_BOUNCING void cb_update(struct cpufreq_policy *pol, u64 time); void cb_reset(int cpu, u64 time); unsigned int cb_cap(struct cpufreq_policy *pol, unsigned int freq); unsigned int cb_get_cap(int cpu); #else static inline void cb_reset(int cpu, u64 time) {} static inline void cb_update(struct cpufreq_policy *pol, u64 time) {} static inline unsigned int cb_cap(struct cpufreq_policy *pol, unsigned int freq) { return freq; } static inline unsigned int cb_get_cap(int cpu) { return UINT_MAX; } #endif #endif