brintos

brintos / linux-shallow public Read only

0
0
Text · 8.9 KiB · 10e80d9 Raw
375 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * linux/drivers/cpufreq/freq_table.c4 *5 * Copyright (C) 2002 - 2003 Dominik Brodowski6 */7 8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt9 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() || !policy->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, leave58	 * 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	else66		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, prev_smaller = 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 ((prev_smaller < freq) && (freq <= policy->max))90			prev_smaller = freq;91	}92 93	if (!found) {94		policy->max = prev_smaller;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 set107 * 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: %u\n", policy->cpu);198			return 0;199		}200 201		index = suboptimal.driver_data;202	} else203		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 CPU232 */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 freq245		 * display BOOST freqs246		 *247		 * show_boost = false and driver_data = BOOST freq248		 * show_boost = true and driver_data != BOOST freq249		 * continue - do not display anything250		 *251		 * show_boost = false and driver_data != BOOST freq252		 * display NON BOOST freqs253		 */254		if (show_boost ^ (pos->flags & CPUFREQ_BOOST_FREQ))255			continue;256 257		count += sprintf(&buf[count], "%u ", 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 for271 * the specified CPU272 */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 for283 * the specified CPU284 */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	else346		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		/* Freq table must be passed by drivers with target_index() */360		if (has_target_index())361			return -EINVAL;362 363		return 0;364	}365 366	ret = cpufreq_frequency_table_cpuinfo(policy, policy->freq_table);367	if (ret)368		return ret;369 370	return set_freq_table_sorted(policy);371}372 373MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");374MODULE_DESCRIPTION("CPUfreq frequency table helpers");375