diff options
Diffstat (limited to 'drivers/cpufreq/cpufreq.c')
| -rw-r--r-- | drivers/cpufreq/cpufreq.c | 145 | 
1 files changed, 90 insertions, 55 deletions
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index b02824d092e..1b8a48eaf90 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -45,7 +45,7 @@ static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data);  /* This one keeps track of the previously set governor of a removed CPU */  static DEFINE_PER_CPU(char[CPUFREQ_NAME_LEN], cpufreq_cpu_governor);  #endif -static DEFINE_SPINLOCK(cpufreq_driver_lock); +static DEFINE_RWLOCK(cpufreq_driver_lock);  /*   * cpu_policy_rwsem is a per CPU reader-writer semaphore designed to cure @@ -128,6 +128,11 @@ void disable_cpufreq(void)  static LIST_HEAD(cpufreq_governor_list);  static DEFINE_MUTEX(cpufreq_governor_mutex); +bool have_governor_per_policy(void) +{ +	return cpufreq_driver->have_governor_per_policy; +} +  static struct cpufreq_policy *__cpufreq_cpu_get(unsigned int cpu, bool sysfs)  {  	struct cpufreq_policy *data; @@ -137,7 +142,7 @@ static struct cpufreq_policy *__cpufreq_cpu_get(unsigned int cpu, bool sysfs)  		goto err_out;  	/* get the cpufreq driver */ -	spin_lock_irqsave(&cpufreq_driver_lock, flags); +	read_lock_irqsave(&cpufreq_driver_lock, flags);  	if (!cpufreq_driver)  		goto err_out_unlock; @@ -155,13 +160,13 @@ static struct cpufreq_policy *__cpufreq_cpu_get(unsigned int cpu, bool sysfs)  	if (!sysfs && !kobject_get(&data->kobj))  		goto err_out_put_module; -	spin_unlock_irqrestore(&cpufreq_driver_lock, flags); +	read_unlock_irqrestore(&cpufreq_driver_lock, flags);  	return data;  err_out_put_module:  	module_put(cpufreq_driver->owner);  err_out_unlock: -	spin_unlock_irqrestore(&cpufreq_driver_lock, flags); +	read_unlock_irqrestore(&cpufreq_driver_lock, flags);  err_out:  	return NULL;  } @@ -244,19 +249,9 @@ static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci)  #endif -/** - * cpufreq_notify_transition - call notifier chain and adjust_jiffies - * on frequency transition. - * - * This function calls the transition notifiers and the "adjust_jiffies" - * function. It is called twice on all CPU frequency changes that have - * external effects. - */ -void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state) +void __cpufreq_notify_transition(struct cpufreq_policy *policy, +		struct cpufreq_freqs *freqs, unsigned int state)  { -	struct cpufreq_policy *policy; -	unsigned long flags; -  	BUG_ON(irqs_disabled());  	if (cpufreq_disabled()) @@ -266,10 +261,6 @@ void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state)  	pr_debug("notification %u of frequency transition to %u kHz\n",  		state, freqs->new); -	spin_lock_irqsave(&cpufreq_driver_lock, flags); -	policy = per_cpu(cpufreq_cpu_data, freqs->cpu); -	spin_unlock_irqrestore(&cpufreq_driver_lock, flags); -  	switch (state) {  	case CPUFREQ_PRECHANGE: @@ -303,6 +294,20 @@ void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state)  		break;  	}  } +/** + * cpufreq_notify_transition - call notifier chain and adjust_jiffies + * on frequency transition. + * + * This function calls the transition notifiers and the "adjust_jiffies" + * function. It is called twice on all CPU frequency changes that have + * external effects. + */ +void cpufreq_notify_transition(struct cpufreq_policy *policy, +		struct cpufreq_freqs *freqs, unsigned int state) +{ +	for_each_cpu(freqs->cpu, policy->cpus) +		__cpufreq_notify_transition(policy, freqs, state); +}  EXPORT_SYMBOL_GPL(cpufreq_notify_transition); @@ -765,12 +770,12 @@ static int cpufreq_add_dev_interface(unsigned int cpu,  			goto err_out_kobj_put;  	} -	spin_lock_irqsave(&cpufreq_driver_lock, flags); +	write_lock_irqsave(&cpufreq_driver_lock, flags);  	for_each_cpu(j, policy->cpus) {  		per_cpu(cpufreq_cpu_data, j) = policy;  		per_cpu(cpufreq_policy_cpu, j) = policy->cpu;  	} -	spin_unlock_irqrestore(&cpufreq_driver_lock, flags); +	write_unlock_irqrestore(&cpufreq_driver_lock, flags);  	ret = cpufreq_add_dev_symlink(cpu, policy);  	if (ret) @@ -803,27 +808,30 @@ static int cpufreq_add_policy_cpu(unsigned int cpu, unsigned int sibling,  				  struct device *dev)  {  	struct cpufreq_policy *policy; -	int ret = 0; +	int ret = 0, has_target = !!cpufreq_driver->target;  	unsigned long flags;  	policy = cpufreq_cpu_get(sibling);  	WARN_ON(!policy); -	__cpufreq_governor(policy, CPUFREQ_GOV_STOP); +	if (has_target) +		__cpufreq_governor(policy, CPUFREQ_GOV_STOP);  	lock_policy_rwsem_write(sibling); -	spin_lock_irqsave(&cpufreq_driver_lock, flags); +	write_lock_irqsave(&cpufreq_driver_lock, flags);  	cpumask_set_cpu(cpu, policy->cpus);  	per_cpu(cpufreq_policy_cpu, cpu) = policy->cpu;  	per_cpu(cpufreq_cpu_data, cpu) = policy; -	spin_unlock_irqrestore(&cpufreq_driver_lock, flags); +	write_unlock_irqrestore(&cpufreq_driver_lock, flags);  	unlock_policy_rwsem_write(sibling); -	__cpufreq_governor(policy, CPUFREQ_GOV_START); -	__cpufreq_governor(policy, CPUFREQ_GOV_LIMITS); +	if (has_target) { +		__cpufreq_governor(policy, CPUFREQ_GOV_START); +		__cpufreq_governor(policy, CPUFREQ_GOV_LIMITS); +	}  	ret = sysfs_create_link(&dev->kobj, &policy->kobj, "cpufreq");  	if (ret) { @@ -871,15 +879,15 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)  #ifdef CONFIG_HOTPLUG_CPU  	/* Check if this cpu was hot-unplugged earlier and has siblings */ -	spin_lock_irqsave(&cpufreq_driver_lock, flags); +	read_lock_irqsave(&cpufreq_driver_lock, flags);  	for_each_online_cpu(sibling) {  		struct cpufreq_policy *cp = per_cpu(cpufreq_cpu_data, sibling);  		if (cp && cpumask_test_cpu(cpu, cp->related_cpus)) { -			spin_unlock_irqrestore(&cpufreq_driver_lock, flags); +			read_unlock_irqrestore(&cpufreq_driver_lock, flags);  			return cpufreq_add_policy_cpu(cpu, sibling, dev);  		}  	} -	spin_unlock_irqrestore(&cpufreq_driver_lock, flags); +	read_unlock_irqrestore(&cpufreq_driver_lock, flags);  #endif  #endif @@ -952,10 +960,10 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)  	return 0;  err_out_unregister: -	spin_lock_irqsave(&cpufreq_driver_lock, flags); +	write_lock_irqsave(&cpufreq_driver_lock, flags);  	for_each_cpu(j, policy->cpus)  		per_cpu(cpufreq_cpu_data, j) = NULL; -	spin_unlock_irqrestore(&cpufreq_driver_lock, flags); +	write_unlock_irqrestore(&cpufreq_driver_lock, flags);  	kobject_put(&policy->kobj);  	wait_for_completion(&policy->kobj_unregister); @@ -1008,12 +1016,12 @@ static int __cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif  	pr_debug("%s: unregistering CPU %u\n", __func__, cpu); -	spin_lock_irqsave(&cpufreq_driver_lock, flags); +	write_lock_irqsave(&cpufreq_driver_lock, flags);  	data = per_cpu(cpufreq_cpu_data, cpu);  	per_cpu(cpufreq_cpu_data, cpu) = NULL; -	spin_unlock_irqrestore(&cpufreq_driver_lock, flags); +	write_unlock_irqrestore(&cpufreq_driver_lock, flags);  	if (!data) {  		pr_debug("%s: No cpu_data found\n", __func__); @@ -1031,7 +1039,9 @@ static int __cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif  	WARN_ON(lock_policy_rwsem_write(cpu));  	cpus = cpumask_weight(data->cpus); -	cpumask_clear_cpu(cpu, data->cpus); + +	if (cpus > 1) +		cpumask_clear_cpu(cpu, data->cpus);  	unlock_policy_rwsem_write(cpu);  	if (cpu != data->cpu) { @@ -1047,9 +1057,9 @@ static int __cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif  			WARN_ON(lock_policy_rwsem_write(cpu));  			cpumask_set_cpu(cpu, data->cpus); -			spin_lock_irqsave(&cpufreq_driver_lock, flags); +			write_lock_irqsave(&cpufreq_driver_lock, flags);  			per_cpu(cpufreq_cpu_data, cpu) = data; -			spin_unlock_irqrestore(&cpufreq_driver_lock, flags); +			write_unlock_irqrestore(&cpufreq_driver_lock, flags);  			unlock_policy_rwsem_write(cpu); @@ -1070,6 +1080,9 @@ static int __cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif  	/* If cpu is last user of policy, free policy */  	if (cpus == 1) { +		if (cpufreq_driver->target) +			__cpufreq_governor(data, CPUFREQ_GOV_POLICY_EXIT); +  		lock_policy_rwsem_read(cpu);  		kobj = &data->kobj;  		cmp = &data->kobj_unregister; @@ -1134,16 +1147,23 @@ static void handle_update(struct work_struct *work)  static void cpufreq_out_of_sync(unsigned int cpu, unsigned int old_freq,  				unsigned int new_freq)  { +	struct cpufreq_policy *policy;  	struct cpufreq_freqs freqs; +	unsigned long flags; +  	pr_debug("Warning: CPU frequency out of sync: cpufreq and timing "  	       "core thinks of %u, is %u kHz.\n", old_freq, new_freq); -	freqs.cpu = cpu;  	freqs.old = old_freq;  	freqs.new = new_freq; -	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); -	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + +	read_lock_irqsave(&cpufreq_driver_lock, flags); +	policy = per_cpu(cpufreq_cpu_data, cpu); +	read_unlock_irqrestore(&cpufreq_driver_lock, flags); + +	cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); +	cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);  } @@ -1544,10 +1564,12 @@ static int __cpufreq_governor(struct cpufreq_policy *policy,  						policy->cpu, event);  	ret = policy->governor->governor(policy, event); -	if (event == CPUFREQ_GOV_START) -		policy->governor->initialized++; -	else if (event == CPUFREQ_GOV_STOP) -		policy->governor->initialized--; +	if (!ret) { +		if (event == CPUFREQ_GOV_POLICY_INIT) +			policy->governor->initialized++; +		else if (event == CPUFREQ_GOV_POLICY_EXIT) +			policy->governor->initialized--; +	}  	/* we keep one module reference alive for  			each CPU governed by this CPU */ @@ -1651,7 +1673,7 @@ EXPORT_SYMBOL(cpufreq_get_policy);  static int __cpufreq_set_policy(struct cpufreq_policy *data,  				struct cpufreq_policy *policy)  { -	int ret = 0; +	int ret = 0, failed = 1;  	pr_debug("setting new policy for CPU %u: %u - %u kHz\n", policy->cpu,  		policy->min, policy->max); @@ -1705,18 +1727,31 @@ static int __cpufreq_set_policy(struct cpufreq_policy *data,  			pr_debug("governor switch\n");  			/* end old governor */ -			if (data->governor) +			if (data->governor) {  				__cpufreq_governor(data, CPUFREQ_GOV_STOP); +				__cpufreq_governor(data, +						CPUFREQ_GOV_POLICY_EXIT); +			}  			/* start new governor */  			data->governor = policy->governor; -			if (__cpufreq_governor(data, CPUFREQ_GOV_START)) { +			if (!__cpufreq_governor(data, CPUFREQ_GOV_POLICY_INIT)) { +				if (!__cpufreq_governor(data, CPUFREQ_GOV_START)) +					failed = 0; +				else +					__cpufreq_governor(data, +							CPUFREQ_GOV_POLICY_EXIT); +			} + +			if (failed) {  				/* new governor failed, so re-start old one */  				pr_debug("starting governor %s failed\n",  							data->governor->name);  				if (old_gov) {  					data->governor = old_gov;  					__cpufreq_governor(data, +							CPUFREQ_GOV_POLICY_INIT); +					__cpufreq_governor(data,  							   CPUFREQ_GOV_START);  				}  				ret = -EINVAL; @@ -1848,13 +1883,13 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data)  	if (driver_data->setpolicy)  		driver_data->flags |= CPUFREQ_CONST_LOOPS; -	spin_lock_irqsave(&cpufreq_driver_lock, flags); +	write_lock_irqsave(&cpufreq_driver_lock, flags);  	if (cpufreq_driver) { -		spin_unlock_irqrestore(&cpufreq_driver_lock, flags); +		write_unlock_irqrestore(&cpufreq_driver_lock, flags);  		return -EBUSY;  	}  	cpufreq_driver = driver_data; -	spin_unlock_irqrestore(&cpufreq_driver_lock, flags); +	write_unlock_irqrestore(&cpufreq_driver_lock, flags);  	ret = subsys_interface_register(&cpufreq_interface);  	if (ret) @@ -1886,9 +1921,9 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data)  err_if_unreg:  	subsys_interface_unregister(&cpufreq_interface);  err_null_driver: -	spin_lock_irqsave(&cpufreq_driver_lock, flags); +	write_lock_irqsave(&cpufreq_driver_lock, flags);  	cpufreq_driver = NULL; -	spin_unlock_irqrestore(&cpufreq_driver_lock, flags); +	write_unlock_irqrestore(&cpufreq_driver_lock, flags);  	return ret;  }  EXPORT_SYMBOL_GPL(cpufreq_register_driver); @@ -1914,9 +1949,9 @@ int cpufreq_unregister_driver(struct cpufreq_driver *driver)  	subsys_interface_unregister(&cpufreq_interface);  	unregister_hotcpu_notifier(&cpufreq_cpu_notifier); -	spin_lock_irqsave(&cpufreq_driver_lock, flags); +	write_lock_irqsave(&cpufreq_driver_lock, flags);  	cpufreq_driver = NULL; -	spin_unlock_irqrestore(&cpufreq_driver_lock, flags); +	write_unlock_irqrestore(&cpufreq_driver_lock, flags);  	return 0;  }  |