Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit da0c6dc0 authored by Viresh Kumar's avatar Viresh Kumar Committed by Rafael J. Wysocki
Browse files

cpufreq: Handle sorted frequency tables more efficiently



cpufreq drivers aren't required to provide a sorted frequency table
today, and even the ones which provide a sorted table aren't handled
efficiently by cpufreq core.

This patch adds infrastructure to verify if the freq-table provided by
the drivers is sorted or not, and use efficient helpers if they are
sorted.

Signed-off-by: default avatarViresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 8d540ea7
Loading
Loading
Loading
Loading
+65 −8
Original line number Diff line number Diff line
@@ -113,7 +113,7 @@ int cpufreq_generic_frequency_table_verify(struct cpufreq_policy *policy)
}
EXPORT_SYMBOL_GPL(cpufreq_generic_frequency_table_verify);

int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
int cpufreq_table_index_unsorted(struct cpufreq_policy *policy,
				 unsigned int target_freq,
				 unsigned int relation)
{
@@ -205,7 +205,7 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
		 table[index].frequency);
	return index;
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_target);
EXPORT_SYMBOL_GPL(cpufreq_table_index_unsorted);

int cpufreq_frequency_table_get_index(struct cpufreq_policy *policy,
		unsigned int freq)
@@ -297,15 +297,72 @@ struct freq_attr *cpufreq_generic_attr[] = {
};
EXPORT_SYMBOL_GPL(cpufreq_generic_attr);

static int set_freq_table_sorted(struct cpufreq_policy *policy)
{
	struct cpufreq_frequency_table *pos, *table = policy->freq_table;
	struct cpufreq_frequency_table *prev = NULL;
	int ascending = 0;

	policy->freq_table_sorted = CPUFREQ_TABLE_UNSORTED;

	cpufreq_for_each_valid_entry(pos, table) {
		if (!prev) {
			prev = pos;
			continue;
		}

		if (pos->frequency == prev->frequency) {
			pr_warn("Duplicate freq-table entries: %u\n",
				pos->frequency);
			return -EINVAL;
		}

		/* Frequency increased from prev to pos */
		if (pos->frequency > prev->frequency) {
			/* But frequency was decreasing earlier */
			if (ascending < 0) {
				pr_debug("Freq table is unsorted\n");
				return 0;
			}

			ascending++;
		} else {
			/* Frequency decreased from prev to pos */

			/* But frequency was increasing earlier */
			if (ascending > 0) {
				pr_debug("Freq table is unsorted\n");
				return 0;
			}

			ascending--;
		}

		prev = pos;
	}

	if (ascending > 0)
		policy->freq_table_sorted = CPUFREQ_TABLE_SORTED_ASCENDING;
	else
		policy->freq_table_sorted = CPUFREQ_TABLE_SORTED_DESCENDING;

	pr_debug("Freq table is sorted in %s order\n",
		 ascending > 0 ? "ascending" : "descending");

	return 0;
}

int cpufreq_table_validate_and_show(struct cpufreq_policy *policy,
				      struct cpufreq_frequency_table *table)
{
	int ret = cpufreq_frequency_table_cpuinfo(policy, table);

	if (!ret)
		policy->freq_table = table;
	int ret;

	ret = cpufreq_frequency_table_cpuinfo(policy, table);
	if (ret)
		return ret;

	policy->freq_table = table;
	return set_freq_table_sorted(policy);
}
EXPORT_SYMBOL_GPL(cpufreq_table_validate_and_show);

+231 −3
Original line number Diff line number Diff line
@@ -36,6 +36,12 @@

struct cpufreq_governor;

enum cpufreq_table_sorting {
	CPUFREQ_TABLE_UNSORTED,
	CPUFREQ_TABLE_SORTED_ASCENDING,
	CPUFREQ_TABLE_SORTED_DESCENDING
};

struct cpufreq_freqs {
	unsigned int cpu;	/* cpu nr */
	unsigned int old;
@@ -87,6 +93,7 @@ struct cpufreq_policy {

	struct cpufreq_user_policy user_policy;
	struct cpufreq_frequency_table	*freq_table;
	enum cpufreq_table_sorting freq_table_sorted;

	struct list_head        policy_list;
	struct kobject		kobj;
@@ -597,7 +604,7 @@ int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
				   struct cpufreq_frequency_table *table);
int cpufreq_generic_frequency_table_verify(struct cpufreq_policy *policy);

int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
int cpufreq_table_index_unsorted(struct cpufreq_policy *policy,
				 unsigned int target_freq,
				 unsigned int relation);
int cpufreq_frequency_table_get_index(struct cpufreq_policy *policy,
@@ -610,6 +617,227 @@ int cpufreq_boost_trigger_state(int state);
int cpufreq_boost_enabled(void);
int cpufreq_enable_boost_support(void);
bool policy_has_boost_freq(struct cpufreq_policy *policy);

/* Find lowest freq at or above target in a table in ascending order */
static inline int cpufreq_table_find_index_al(struct cpufreq_policy *policy,
					      unsigned int target_freq)
{
	struct cpufreq_frequency_table *table = policy->freq_table;
	unsigned int freq;
	int i, best = -1;

	for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
		freq = table[i].frequency;

		if (freq >= target_freq)
			return i;

		best = i;
	}

	return best;
}

/* Find lowest freq at or above target in a table in descending order */
static inline int cpufreq_table_find_index_dl(struct cpufreq_policy *policy,
					      unsigned int target_freq)
{
	struct cpufreq_frequency_table *table = policy->freq_table;
	unsigned int freq;
	int i, best = -1;

	for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
		freq = table[i].frequency;

		if (freq == target_freq)
			return i;

		if (freq > target_freq) {
			best = i;
			continue;
		}

		/* No freq found above target_freq */
		if (best == -1)
			return i;

		return best;
	}

	return best;
}

/* Works only on sorted freq-tables */
static inline int cpufreq_table_find_index_l(struct cpufreq_policy *policy,
					     unsigned int target_freq)
{
	target_freq = clamp_val(target_freq, policy->min, policy->max);

	if (policy->freq_table_sorted == CPUFREQ_TABLE_SORTED_ASCENDING)
		return cpufreq_table_find_index_al(policy, target_freq);
	else
		return cpufreq_table_find_index_dl(policy, target_freq);
}

/* Find highest freq at or below target in a table in ascending order */
static inline int cpufreq_table_find_index_ah(struct cpufreq_policy *policy,
					      unsigned int target_freq)
{
	struct cpufreq_frequency_table *table = policy->freq_table;
	unsigned int freq;
	int i, best = -1;

	for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
		freq = table[i].frequency;

		if (freq == target_freq)
			return i;

		if (freq < target_freq) {
			best = i;
			continue;
		}

		/* No freq found below target_freq */
		if (best == -1)
			return i;

		return best;
	}

	return best;
}

/* Find highest freq at or below target in a table in descending order */
static inline int cpufreq_table_find_index_dh(struct cpufreq_policy *policy,
					      unsigned int target_freq)
{
	struct cpufreq_frequency_table *table = policy->freq_table;
	unsigned int freq;
	int i, best = -1;

	for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
		freq = table[i].frequency;

		if (freq <= target_freq)
			return i;

		best = i;
	}

	return best;
}

/* Works only on sorted freq-tables */
static inline int cpufreq_table_find_index_h(struct cpufreq_policy *policy,
					     unsigned int target_freq)
{
	target_freq = clamp_val(target_freq, policy->min, policy->max);

	if (policy->freq_table_sorted == CPUFREQ_TABLE_SORTED_ASCENDING)
		return cpufreq_table_find_index_ah(policy, target_freq);
	else
		return cpufreq_table_find_index_dh(policy, target_freq);
}

/* Find closest freq to target in a table in ascending order */
static inline int cpufreq_table_find_index_ac(struct cpufreq_policy *policy,
					      unsigned int target_freq)
{
	struct cpufreq_frequency_table *table = policy->freq_table;
	unsigned int freq;
	int i, best = -1;

	for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
		freq = table[i].frequency;

		if (freq == target_freq)
			return i;

		if (freq < target_freq) {
			best = i;
			continue;
		}

		/* No freq found below target_freq */
		if (best == -1)
			return i;

		/* Choose the closest freq */
		if (target_freq - table[best].frequency > freq - target_freq)
			return i;

		return best;
	}

	return best;
}

/* Find closest freq to target in a table in descending order */
static inline int cpufreq_table_find_index_dc(struct cpufreq_policy *policy,
					      unsigned int target_freq)
{
	struct cpufreq_frequency_table *table = policy->freq_table;
	unsigned int freq;
	int i, best = -1;

	for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
		freq = table[i].frequency;

		if (freq == target_freq)
			return i;

		if (freq > target_freq) {
			best = i;
			continue;
		}

		/* No freq found above target_freq */
		if (best == -1)
			return i;

		/* Choose the closest freq */
		if (table[best].frequency - target_freq > target_freq - freq)
			return i;

		return best;
	}

	return best;
}

/* Works only on sorted freq-tables */
static inline int cpufreq_table_find_index_c(struct cpufreq_policy *policy,
					     unsigned int target_freq)
{
	target_freq = clamp_val(target_freq, policy->min, policy->max);

	if (policy->freq_table_sorted == CPUFREQ_TABLE_SORTED_ASCENDING)
		return cpufreq_table_find_index_ac(policy, target_freq);
	else
		return cpufreq_table_find_index_dc(policy, target_freq);
}

static inline int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
						 unsigned int target_freq,
						 unsigned int relation)
{
	if (unlikely(policy->freq_table_sorted == CPUFREQ_TABLE_UNSORTED))
		return cpufreq_table_index_unsorted(policy, target_freq,
						    relation);

	switch (relation) {
	case CPUFREQ_RELATION_L:
		return cpufreq_table_find_index_l(policy, target_freq);
	case CPUFREQ_RELATION_H:
		return cpufreq_table_find_index_h(policy, target_freq);
	case CPUFREQ_RELATION_C:
		return cpufreq_table_find_index_c(policy, target_freq);
	default:
		pr_err("%s: Invalid relation: %d\n", __func__, relation);
		return -EINVAL;
	}
}
#else
static inline int cpufreq_boost_trigger_state(int state)
{