cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

freq_table.c (9028B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * linux/drivers/cpufreq/freq_table.c
      4 *
      5 * Copyright (C) 2002 - 2003 Dominik Brodowski
      6 */
      7
      8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
      9
     10#include <linux/cpufreq.h>
     11#include <linux/module.h>
     12
     13/*********************************************************************
     14 *                     FREQUENCY TABLE HELPERS                       *
     15 *********************************************************************/
     16
     17bool policy_has_boost_freq(struct cpufreq_policy *policy)
     18{
     19	struct cpufreq_frequency_table *pos, *table = policy->freq_table;
     20
     21	if (!table)
     22		return false;
     23
     24	cpufreq_for_each_valid_entry(pos, table)
     25		if (pos->flags & CPUFREQ_BOOST_FREQ)
     26			return true;
     27
     28	return false;
     29}
     30EXPORT_SYMBOL_GPL(policy_has_boost_freq);
     31
     32int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
     33				    struct cpufreq_frequency_table *table)
     34{
     35	struct cpufreq_frequency_table *pos;
     36	unsigned int min_freq = ~0;
     37	unsigned int max_freq = 0;
     38	unsigned int freq;
     39
     40	cpufreq_for_each_valid_entry(pos, table) {
     41		freq = pos->frequency;
     42
     43		if (!cpufreq_boost_enabled()
     44		    && (pos->flags & CPUFREQ_BOOST_FREQ))
     45			continue;
     46
     47		pr_debug("table entry %u: %u kHz\n", (int)(pos - table), freq);
     48		if (freq < min_freq)
     49			min_freq = freq;
     50		if (freq > max_freq)
     51			max_freq = freq;
     52	}
     53
     54	policy->min = policy->cpuinfo.min_freq = min_freq;
     55	policy->max = max_freq;
     56	/*
     57	 * If the driver has set its own cpuinfo.max_freq above max_freq, leave
     58	 * it as is.
     59	 */
     60	if (policy->cpuinfo.max_freq < max_freq)
     61		policy->max = policy->cpuinfo.max_freq = max_freq;
     62
     63	if (policy->min == ~0)
     64		return -EINVAL;
     65	else
     66		return 0;
     67}
     68
     69int cpufreq_frequency_table_verify(struct cpufreq_policy_data *policy,
     70				   struct cpufreq_frequency_table *table)
     71{
     72	struct cpufreq_frequency_table *pos;
     73	unsigned int freq, next_larger = ~0;
     74	bool found = false;
     75
     76	pr_debug("request for verification of policy (%u - %u kHz) for cpu %u\n",
     77					policy->min, policy->max, policy->cpu);
     78
     79	cpufreq_verify_within_cpu_limits(policy);
     80
     81	cpufreq_for_each_valid_entry(pos, table) {
     82		freq = pos->frequency;
     83
     84		if ((freq >= policy->min) && (freq <= policy->max)) {
     85			found = true;
     86			break;
     87		}
     88
     89		if ((next_larger > freq) && (freq > policy->max))
     90			next_larger = freq;
     91	}
     92
     93	if (!found) {
     94		policy->max = next_larger;
     95		cpufreq_verify_within_cpu_limits(policy);
     96	}
     97
     98	pr_debug("verification lead to (%u - %u kHz) for cpu %u\n",
     99				policy->min, policy->max, policy->cpu);
    100
    101	return 0;
    102}
    103EXPORT_SYMBOL_GPL(cpufreq_frequency_table_verify);
    104
    105/*
    106 * Generic routine to verify policy & frequency table, requires driver to set
    107 * policy->freq_table prior to it.
    108 */
    109int cpufreq_generic_frequency_table_verify(struct cpufreq_policy_data *policy)
    110{
    111	if (!policy->freq_table)
    112		return -ENODEV;
    113
    114	return cpufreq_frequency_table_verify(policy, policy->freq_table);
    115}
    116EXPORT_SYMBOL_GPL(cpufreq_generic_frequency_table_verify);
    117
    118int cpufreq_table_index_unsorted(struct cpufreq_policy *policy,
    119				 unsigned int target_freq,
    120				 unsigned int relation)
    121{
    122	struct cpufreq_frequency_table optimal = {
    123		.driver_data = ~0,
    124		.frequency = 0,
    125	};
    126	struct cpufreq_frequency_table suboptimal = {
    127		.driver_data = ~0,
    128		.frequency = 0,
    129	};
    130	struct cpufreq_frequency_table *pos;
    131	struct cpufreq_frequency_table *table = policy->freq_table;
    132	unsigned int freq, diff, i = 0;
    133	int index;
    134
    135	pr_debug("request for target %u kHz (relation: %u) for cpu %u\n",
    136					target_freq, relation, policy->cpu);
    137
    138	switch (relation) {
    139	case CPUFREQ_RELATION_H:
    140		suboptimal.frequency = ~0;
    141		break;
    142	case CPUFREQ_RELATION_L:
    143	case CPUFREQ_RELATION_C:
    144		optimal.frequency = ~0;
    145		break;
    146	}
    147
    148	cpufreq_for_each_valid_entry_idx(pos, table, i) {
    149		freq = pos->frequency;
    150
    151		if ((freq < policy->min) || (freq > policy->max))
    152			continue;
    153		if (freq == target_freq) {
    154			optimal.driver_data = i;
    155			break;
    156		}
    157		switch (relation) {
    158		case CPUFREQ_RELATION_H:
    159			if (freq < target_freq) {
    160				if (freq >= optimal.frequency) {
    161					optimal.frequency = freq;
    162					optimal.driver_data = i;
    163				}
    164			} else {
    165				if (freq <= suboptimal.frequency) {
    166					suboptimal.frequency = freq;
    167					suboptimal.driver_data = i;
    168				}
    169			}
    170			break;
    171		case CPUFREQ_RELATION_L:
    172			if (freq > target_freq) {
    173				if (freq <= optimal.frequency) {
    174					optimal.frequency = freq;
    175					optimal.driver_data = i;
    176				}
    177			} else {
    178				if (freq >= suboptimal.frequency) {
    179					suboptimal.frequency = freq;
    180					suboptimal.driver_data = i;
    181				}
    182			}
    183			break;
    184		case CPUFREQ_RELATION_C:
    185			diff = abs(freq - target_freq);
    186			if (diff < optimal.frequency ||
    187			    (diff == optimal.frequency &&
    188			     freq > table[optimal.driver_data].frequency)) {
    189				optimal.frequency = diff;
    190				optimal.driver_data = i;
    191			}
    192			break;
    193		}
    194	}
    195	if (optimal.driver_data > i) {
    196		if (suboptimal.driver_data > i) {
    197			WARN(1, "Invalid frequency table: %d\n", policy->cpu);
    198			return 0;
    199		}
    200
    201		index = suboptimal.driver_data;
    202	} else
    203		index = optimal.driver_data;
    204
    205	pr_debug("target index is %u, freq is:%u kHz\n", index,
    206		 table[index].frequency);
    207	return index;
    208}
    209EXPORT_SYMBOL_GPL(cpufreq_table_index_unsorted);
    210
    211int cpufreq_frequency_table_get_index(struct cpufreq_policy *policy,
    212		unsigned int freq)
    213{
    214	struct cpufreq_frequency_table *pos, *table = policy->freq_table;
    215	int idx;
    216
    217	if (unlikely(!table)) {
    218		pr_debug("%s: Unable to find frequency table\n", __func__);
    219		return -ENOENT;
    220	}
    221
    222	cpufreq_for_each_valid_entry_idx(pos, table, idx)
    223		if (pos->frequency == freq)
    224			return idx;
    225
    226	return -EINVAL;
    227}
    228EXPORT_SYMBOL_GPL(cpufreq_frequency_table_get_index);
    229
    230/*
    231 * show_available_freqs - show available frequencies for the specified CPU
    232 */
    233static ssize_t show_available_freqs(struct cpufreq_policy *policy, char *buf,
    234				    bool show_boost)
    235{
    236	ssize_t count = 0;
    237	struct cpufreq_frequency_table *pos, *table = policy->freq_table;
    238
    239	if (!table)
    240		return -ENODEV;
    241
    242	cpufreq_for_each_valid_entry(pos, table) {
    243		/*
    244		 * show_boost = true and driver_data = BOOST freq
    245		 * display BOOST freqs
    246		 *
    247		 * show_boost = false and driver_data = BOOST freq
    248		 * show_boost = true and driver_data != BOOST freq
    249		 * continue - do not display anything
    250		 *
    251		 * show_boost = false and driver_data != BOOST freq
    252		 * display NON BOOST freqs
    253		 */
    254		if (show_boost ^ (pos->flags & CPUFREQ_BOOST_FREQ))
    255			continue;
    256
    257		count += sprintf(&buf[count], "%d ", pos->frequency);
    258	}
    259	count += sprintf(&buf[count], "\n");
    260
    261	return count;
    262
    263}
    264
    265#define cpufreq_attr_available_freq(_name)	  \
    266struct freq_attr cpufreq_freq_attr_##_name##_freqs =     \
    267__ATTR_RO(_name##_frequencies)
    268
    269/*
    270 * scaling_available_frequencies_show - show available normal frequencies for
    271 * the specified CPU
    272 */
    273static ssize_t scaling_available_frequencies_show(struct cpufreq_policy *policy,
    274						  char *buf)
    275{
    276	return show_available_freqs(policy, buf, false);
    277}
    278cpufreq_attr_available_freq(scaling_available);
    279EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_available_freqs);
    280
    281/*
    282 * scaling_boost_frequencies_show - show available boost frequencies for
    283 * the specified CPU
    284 */
    285static ssize_t scaling_boost_frequencies_show(struct cpufreq_policy *policy,
    286					      char *buf)
    287{
    288	return show_available_freqs(policy, buf, true);
    289}
    290cpufreq_attr_available_freq(scaling_boost);
    291EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_boost_freqs);
    292
    293struct freq_attr *cpufreq_generic_attr[] = {
    294	&cpufreq_freq_attr_scaling_available_freqs,
    295	NULL,
    296};
    297EXPORT_SYMBOL_GPL(cpufreq_generic_attr);
    298
    299static int set_freq_table_sorted(struct cpufreq_policy *policy)
    300{
    301	struct cpufreq_frequency_table *pos, *table = policy->freq_table;
    302	struct cpufreq_frequency_table *prev = NULL;
    303	int ascending = 0;
    304
    305	policy->freq_table_sorted = CPUFREQ_TABLE_UNSORTED;
    306
    307	cpufreq_for_each_valid_entry(pos, table) {
    308		if (!prev) {
    309			prev = pos;
    310			continue;
    311		}
    312
    313		if (pos->frequency == prev->frequency) {
    314			pr_warn("Duplicate freq-table entries: %u\n",
    315				pos->frequency);
    316			return -EINVAL;
    317		}
    318
    319		/* Frequency increased from prev to pos */
    320		if (pos->frequency > prev->frequency) {
    321			/* But frequency was decreasing earlier */
    322			if (ascending < 0) {
    323				pr_debug("Freq table is unsorted\n");
    324				return 0;
    325			}
    326
    327			ascending++;
    328		} else {
    329			/* Frequency decreased from prev to pos */
    330
    331			/* But frequency was increasing earlier */
    332			if (ascending > 0) {
    333				pr_debug("Freq table is unsorted\n");
    334				return 0;
    335			}
    336
    337			ascending--;
    338		}
    339
    340		prev = pos;
    341	}
    342
    343	if (ascending > 0)
    344		policy->freq_table_sorted = CPUFREQ_TABLE_SORTED_ASCENDING;
    345	else
    346		policy->freq_table_sorted = CPUFREQ_TABLE_SORTED_DESCENDING;
    347
    348	pr_debug("Freq table is sorted in %s order\n",
    349		 ascending > 0 ? "ascending" : "descending");
    350
    351	return 0;
    352}
    353
    354int cpufreq_table_validate_and_sort(struct cpufreq_policy *policy)
    355{
    356	int ret;
    357
    358	if (!policy->freq_table)
    359		return 0;
    360
    361	ret = cpufreq_frequency_table_cpuinfo(policy, policy->freq_table);
    362	if (ret)
    363		return ret;
    364
    365	return set_freq_table_sorted(policy);
    366}
    367
    368MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");
    369MODULE_DESCRIPTION("CPUfreq frequency table helpers");
    370MODULE_LICENSE("GPL");