diff options
Diffstat (limited to 'include/linux/cpufreq.h')
| -rw-r--r-- | include/linux/cpufreq.h | 289 | 
1 files changed, 272 insertions, 17 deletions
| diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 4e81e08db752..631ba33bbe9f 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -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; @@ -113,6 +120,10 @@ struct cpufreq_policy {  	bool			fast_switch_possible;  	bool			fast_switch_enabled; +	 /* Cached frequency lookup from cpufreq_driver_resolve_freq. */ +	unsigned int cached_target_freq; +	int cached_resolved_idx; +  	/* Synchronization for frequency transitions */  	bool			transition_ongoing; /* Tracks transition status */  	spinlock_t		transition_lock; @@ -185,6 +196,18 @@ static inline unsigned int cpufreq_quick_get_max(unsigned int cpu)  static inline void disable_cpufreq(void) { }  #endif +#ifdef CONFIG_CPU_FREQ_STAT +void cpufreq_stats_create_table(struct cpufreq_policy *policy); +void cpufreq_stats_free_table(struct cpufreq_policy *policy); +void cpufreq_stats_record_transition(struct cpufreq_policy *policy, +				     unsigned int new_freq); +#else +static inline void cpufreq_stats_create_table(struct cpufreq_policy *policy) { } +static inline void cpufreq_stats_free_table(struct cpufreq_policy *policy) { } +static inline void cpufreq_stats_record_transition(struct cpufreq_policy *policy, +						   unsigned int new_freq) { } +#endif /* CONFIG_CPU_FREQ_STAT */ +  /*********************************************************************   *                      CPUFREQ DRIVER INTERFACE                     *   *********************************************************************/ @@ -251,6 +274,16 @@ struct cpufreq_driver {  					unsigned int index);  	unsigned int	(*fast_switch)(struct cpufreq_policy *policy,  				       unsigned int target_freq); + +	/* +	 * Caches and returns the lowest driver-supported frequency greater than +	 * or equal to the target frequency, subject to any driver limitations. +	 * Does not set the frequency. Only to be implemented for drivers with +	 * target(). +	 */ +	unsigned int	(*resolve_freq)(struct cpufreq_policy *policy, +					unsigned int target_freq); +  	/*  	 * Only for drivers with target_index() and CPUFREQ_ASYNC_NOTIFICATION  	 * unset. @@ -455,18 +488,13 @@ static inline unsigned long cpufreq_scale(unsigned long old, u_int div,  #define MIN_LATENCY_MULTIPLIER		(20)  #define TRANSITION_LATENCY_LIMIT	(10 * 1000 * 1000) -/* Governor Events */ -#define CPUFREQ_GOV_START	1 -#define CPUFREQ_GOV_STOP	2 -#define CPUFREQ_GOV_LIMITS	3 -#define CPUFREQ_GOV_POLICY_INIT	4 -#define CPUFREQ_GOV_POLICY_EXIT	5 -  struct cpufreq_governor {  	char	name[CPUFREQ_NAME_LEN]; -	int	initialized; -	int	(*governor)	(struct cpufreq_policy *policy, -				 unsigned int event); +	int	(*init)(struct cpufreq_policy *policy); +	void	(*exit)(struct cpufreq_policy *policy); +	int	(*start)(struct cpufreq_policy *policy); +	void	(*stop)(struct cpufreq_policy *policy); +	void	(*limits)(struct cpufreq_policy *policy);  	ssize_t	(*show_setspeed)	(struct cpufreq_policy *policy,  					 char *buf);  	int	(*store_setspeed)	(struct cpufreq_policy *policy, @@ -487,12 +515,22 @@ int cpufreq_driver_target(struct cpufreq_policy *policy,  int __cpufreq_driver_target(struct cpufreq_policy *policy,  				   unsigned int target_freq,  				   unsigned int relation); +unsigned int cpufreq_driver_resolve_freq(struct cpufreq_policy *policy, +					 unsigned int target_freq);  int cpufreq_register_governor(struct cpufreq_governor *governor);  void cpufreq_unregister_governor(struct cpufreq_governor *governor);  struct cpufreq_governor *cpufreq_default_governor(void);  struct cpufreq_governor *cpufreq_fallback_governor(void); +static inline void cpufreq_policy_apply_limits(struct cpufreq_policy *policy) +{ +	if (policy->max < policy->cur) +		__cpufreq_driver_target(policy, policy->max, CPUFREQ_RELATION_H); +	else if (policy->min > policy->cur) +		__cpufreq_driver_target(policy, policy->min, CPUFREQ_RELATION_L); +} +  /* Governor attribute set */  struct gov_attr_set {  	struct kobject kobj; @@ -582,11 +620,9 @@ 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, -				   struct cpufreq_frequency_table *table, -				   unsigned int target_freq, -				   unsigned int relation, -				   unsigned int *index); +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,  		unsigned int freq); @@ -597,6 +633,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)  { @@ -617,8 +874,6 @@ static inline bool policy_has_boost_freq(struct cpufreq_policy *policy)  	return false;  }  #endif -/* the following funtion is for cpufreq core use only */ -struct cpufreq_frequency_table *cpufreq_frequency_get_table(unsigned int cpu);  /* the following are really really optional */  extern struct freq_attr cpufreq_freq_attr_scaling_available_freqs; |