diff options
Diffstat (limited to 'drivers/cpufreq/cpufreq.c')
-rw-r--r-- | drivers/cpufreq/cpufreq.c | 260 |
1 files changed, 167 insertions, 93 deletions
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 6f23ebb395f1..7ea217c88c2e 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -19,6 +19,7 @@ #include <linux/cpu.h> #include <linux/cpufreq.h> +#include <linux/cpu_cooling.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/init.h> @@ -33,11 +34,6 @@ static LIST_HEAD(cpufreq_policy_list); -static inline bool policy_is_inactive(struct cpufreq_policy *policy) -{ - return cpumask_empty(policy->cpus); -} - /* Macros to iterate over CPU policies */ #define for_each_suitable_policy(__policy, __active) \ list_for_each_entry(__policy, &cpufreq_policy_list, policy_list) \ @@ -205,17 +201,15 @@ unsigned int cpufreq_generic_get(unsigned int cpu) EXPORT_SYMBOL_GPL(cpufreq_generic_get); /** - * cpufreq_cpu_get: returns policy for a cpu and marks it busy. + * cpufreq_cpu_get - Return policy for a CPU and mark it as busy. + * @cpu: CPU to find the policy for. * - * @cpu: cpu to find policy for. + * Call cpufreq_cpu_get_raw() to obtain a cpufreq policy for @cpu and increment + * the kobject reference counter of that policy. Return a valid policy on + * success or NULL on failure. * - * This returns policy for 'cpu', returns NULL if it doesn't exist. - * It also increments the kobject reference count to mark it busy and so would - * require a corresponding call to cpufreq_cpu_put() to decrement it back. - * If corresponding call cpufreq_cpu_put() isn't made, the policy wouldn't be - * freed as that depends on the kobj count. - * - * Return: A valid policy on success, otherwise NULL on failure. + * The policy returned by this function has to be released with the help of + * cpufreq_cpu_put() to balance its kobject reference counter properly. */ struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu) { @@ -242,12 +236,8 @@ struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu) EXPORT_SYMBOL_GPL(cpufreq_cpu_get); /** - * cpufreq_cpu_put: Decrements the usage count of a policy - * - * @policy: policy earlier returned by cpufreq_cpu_get(). - * - * This decrements the kobject reference count incremented earlier by calling - * cpufreq_cpu_get(). + * cpufreq_cpu_put - Decrement kobject usage counter for cpufreq policy. + * @policy: cpufreq policy returned by cpufreq_cpu_get(). */ void cpufreq_cpu_put(struct cpufreq_policy *policy) { @@ -255,6 +245,51 @@ void cpufreq_cpu_put(struct cpufreq_policy *policy) } EXPORT_SYMBOL_GPL(cpufreq_cpu_put); +/** + * cpufreq_cpu_release - Unlock a policy and decrement its usage counter. + * @policy: cpufreq policy returned by cpufreq_cpu_acquire(). + */ +void cpufreq_cpu_release(struct cpufreq_policy *policy) +{ + if (WARN_ON(!policy)) + return; + + lockdep_assert_held(&policy->rwsem); + + up_write(&policy->rwsem); + + cpufreq_cpu_put(policy); +} + +/** + * cpufreq_cpu_acquire - Find policy for a CPU, mark it as busy and lock it. + * @cpu: CPU to find the policy for. + * + * Call cpufreq_cpu_get() to get a reference on the cpufreq policy for @cpu and + * if the policy returned by it is not NULL, acquire its rwsem for writing. + * Return the policy if it is active or release it and return NULL otherwise. + * + * The policy returned by this function has to be released with the help of + * cpufreq_cpu_release() in order to release its rwsem and balance its usage + * counter properly. + */ +struct cpufreq_policy *cpufreq_cpu_acquire(unsigned int cpu) +{ + struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); + + if (!policy) + return NULL; + + down_write(&policy->rwsem); + + if (policy_is_inactive(policy)) { + cpufreq_cpu_release(policy); + return NULL; + } + + return policy; +} + /********************************************************************* * EXTERNALLY AFFECTING FREQUENCY CHANGES * *********************************************************************/ @@ -545,13 +580,13 @@ EXPORT_SYMBOL_GPL(cpufreq_policy_transition_delay_us); * SYSFS INTERFACE * *********************************************************************/ static ssize_t show_boost(struct kobject *kobj, - struct attribute *attr, char *buf) + struct kobj_attribute *attr, char *buf) { return sprintf(buf, "%d\n", cpufreq_driver->boost_enabled); } -static ssize_t store_boost(struct kobject *kobj, struct attribute *attr, - const char *buf, size_t count) +static ssize_t store_boost(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) { int ret, enable; @@ -674,9 +709,6 @@ static ssize_t show_scaling_cur_freq(struct cpufreq_policy *policy, char *buf) return ret; } -static int cpufreq_set_policy(struct cpufreq_policy *policy, - struct cpufreq_policy *new_policy); - /** * cpufreq_per_cpu_attr_write() / store_##file_name() - sysfs write access */ @@ -862,11 +894,9 @@ static ssize_t show_bios_limit(struct cpufreq_policy *policy, char *buf) { unsigned int limit; int ret; - if (cpufreq_driver->bios_limit) { - ret = cpufreq_driver->bios_limit(policy->cpu, &limit); - if (!ret) - return sprintf(buf, "%u\n", limit); - } + ret = cpufreq_driver->bios_limit(policy->cpu, &limit); + if (!ret) + return sprintf(buf, "%u\n", limit); return sprintf(buf, "%u\n", policy->cpuinfo.max_freq); } @@ -1103,6 +1133,7 @@ static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu) cpufreq_global_kobject, "policy%u", cpu); if (ret) { pr_err("%s: failed to init policy->kobj: %d\n", __func__, ret); + kobject_put(&policy->kobj); goto err_free_real_cpus; } @@ -1200,28 +1231,39 @@ static int cpufreq_online(unsigned int cpu) return -ENOMEM; } - cpumask_copy(policy->cpus, cpumask_of(cpu)); + if (!new_policy && cpufreq_driver->online) { + ret = cpufreq_driver->online(policy); + if (ret) { + pr_debug("%s: %d: initialization failed\n", __func__, + __LINE__); + goto out_exit_policy; + } - /* call driver. From then on the cpufreq must be able - * to accept all calls to ->verify and ->setpolicy for this CPU - */ - ret = cpufreq_driver->init(policy); - if (ret) { - pr_debug("initialization failed\n"); - goto out_free_policy; - } + /* Recover policy->cpus using related_cpus */ + cpumask_copy(policy->cpus, policy->related_cpus); + } else { + cpumask_copy(policy->cpus, cpumask_of(cpu)); - ret = cpufreq_table_validate_and_sort(policy); - if (ret) - goto out_exit_policy; + /* + * Call driver. From then on the cpufreq must be able + * to accept all calls to ->verify and ->setpolicy for this CPU. + */ + ret = cpufreq_driver->init(policy); + if (ret) { + pr_debug("%s: %d: initialization failed\n", __func__, + __LINE__); + goto out_free_policy; + } - down_write(&policy->rwsem); + ret = cpufreq_table_validate_and_sort(policy); + if (ret) + goto out_exit_policy; - if (new_policy) { /* related_cpus should at least include policy->cpus. */ cpumask_copy(policy->related_cpus, policy->cpus); } + down_write(&policy->rwsem); /* * affected cpus must always be the one, which are online. We aren't * managing offline cpus here. @@ -1305,8 +1347,6 @@ static int cpufreq_online(unsigned int cpu) if (ret) { pr_err("%s: Failed to initialize policy for cpu: %d (%d)\n", __func__, cpu, ret); - /* cpufreq_policy_free() will notify based on this */ - new_policy = false; goto out_destroy_policy; } @@ -1318,6 +1358,10 @@ static int cpufreq_online(unsigned int cpu) if (cpufreq_driver->ready) cpufreq_driver->ready(policy); + if (IS_ENABLED(CONFIG_CPU_THERMAL) && + cpufreq_driver->flags & CPUFREQ_IS_COOLING_DEV) + policy->cdev = of_cpufreq_cooling_register(policy); + pr_debug("initialization complete\n"); return 0; @@ -1405,6 +1449,12 @@ static int cpufreq_offline(unsigned int cpu) goto unlock; } + if (IS_ENABLED(CONFIG_CPU_THERMAL) && + cpufreq_driver->flags & CPUFREQ_IS_COOLING_DEV) { + cpufreq_cooling_unregister(policy->cdev); + policy->cdev = NULL; + } + if (cpufreq_driver->stop_cpu) cpufreq_driver->stop_cpu(policy); @@ -1412,11 +1462,12 @@ static int cpufreq_offline(unsigned int cpu) cpufreq_exit_governor(policy); /* - * Perform the ->exit() even during light-weight tear-down, - * since this is a core component, and is essential for the - * subsequent light-weight ->init() to succeed. + * Perform the ->offline() during light-weight tear-down, as + * that allows fast recovery when the CPU comes back. */ - if (cpufreq_driver->exit) { + if (cpufreq_driver->offline) { + cpufreq_driver->offline(policy); + } else if (cpufreq_driver->exit) { cpufreq_driver->exit(policy); policy->freq_table = NULL; } @@ -1445,8 +1496,13 @@ static void cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif) cpumask_clear_cpu(cpu, policy->real_cpus); remove_cpu_dev_symlink(policy, dev); - if (cpumask_empty(policy->real_cpus)) + if (cpumask_empty(policy->real_cpus)) { + /* We did light-weight exit earlier, do full tear down now */ + if (cpufreq_driver->offline) + cpufreq_driver->exit(policy); + cpufreq_policy_free(policy); + } } /** @@ -1530,17 +1586,16 @@ static unsigned int __cpufreq_get(struct cpufreq_policy *policy) { unsigned int ret_freq = 0; - if (!cpufreq_driver->get) + if (unlikely(policy_is_inactive(policy))) return ret_freq; ret_freq = cpufreq_driver->get(policy->cpu); /* - * Updating inactive policies is invalid, so avoid doing that. Also - * if fast frequency switching is used with the given policy, the check + * If fast frequency switching is used with the given policy, the check * against policy->cur is pointless, so skip it in that case too. */ - if (unlikely(policy_is_inactive(policy)) || policy->fast_switch_enabled) + if (policy->fast_switch_enabled) return ret_freq; if (ret_freq && policy->cur && @@ -1569,10 +1624,8 @@ unsigned int cpufreq_get(unsigned int cpu) if (policy) { down_read(&policy->rwsem); - - if (!policy_is_inactive(policy)) + if (cpufreq_driver->get) ret_freq = __cpufreq_get(policy); - up_read(&policy->rwsem); cpufreq_cpu_put(policy); @@ -2196,12 +2249,25 @@ int cpufreq_get_policy(struct cpufreq_policy *policy, unsigned int cpu) } EXPORT_SYMBOL(cpufreq_get_policy); -/* - * policy : current policy. - * new_policy: policy to be set. +/** + * cpufreq_set_policy - Modify cpufreq policy parameters. + * @policy: Policy object to modify. + * @new_policy: New policy data. + * + * Pass @new_policy to the cpufreq driver's ->verify() callback, run the + * installed policy notifiers for it with the CPUFREQ_ADJUST value, pass it to + * the driver's ->verify() callback again and run the notifiers for it again + * with the CPUFREQ_NOTIFY value. Next, copy the min and max parameters + * of @new_policy to @policy and either invoke the driver's ->setpolicy() + * callback (if present) or carry out a governor update for @policy. That is, + * run the current governor's ->limits() callback (if the governor field in + * @new_policy points to the same object as the one in @policy) or replace the + * governor for @policy with the new one stored in @new_policy. + * + * The cpuinfo part of @policy is not updated by this function. */ -static int cpufreq_set_policy(struct cpufreq_policy *policy, - struct cpufreq_policy *new_policy) +int cpufreq_set_policy(struct cpufreq_policy *policy, + struct cpufreq_policy *new_policy) { struct cpufreq_governor *old_gov; int ret; @@ -2251,11 +2317,11 @@ static int cpufreq_set_policy(struct cpufreq_policy *policy, if (cpufreq_driver->setpolicy) { policy->policy = new_policy->policy; pr_debug("setting range\n"); - return cpufreq_driver->setpolicy(new_policy); + return cpufreq_driver->setpolicy(policy); } if (new_policy->governor == policy->governor) { - pr_debug("cpufreq: governor limits update\n"); + pr_debug("governor limits update\n"); cpufreq_governor_limits(policy); return 0; } @@ -2276,7 +2342,7 @@ static int cpufreq_set_policy(struct cpufreq_policy *policy, if (!ret) { ret = cpufreq_start_governor(policy); if (!ret) { - pr_debug("cpufreq: governor change\n"); + pr_debug("governor change\n"); sched_cpufreq_governor_change(policy, old_gov); return 0; } @@ -2297,23 +2363,29 @@ static int cpufreq_set_policy(struct cpufreq_policy *policy, } /** - * cpufreq_update_policy - re-evaluate an existing cpufreq policy - * @cpu: CPU which shall be re-evaluated + * cpufreq_update_policy - Re-evaluate an existing cpufreq policy. + * @cpu: CPU to re-evaluate the policy for. * - * Useful for policy notifiers which have different necessities - * at different times. + * Update the current frequency for the cpufreq policy of @cpu and use + * cpufreq_set_policy() to re-apply the min and max limits saved in the + * user_policy sub-structure of that policy, which triggers the evaluation + * of policy notifiers and the cpufreq driver's ->verify() callback for the + * policy in question, among other things. */ void cpufreq_update_policy(unsigned int cpu) { - struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); + struct cpufreq_policy *policy = cpufreq_cpu_acquire(cpu); struct cpufreq_policy new_policy; if (!policy) return; - down_write(&policy->rwsem); - - if (policy_is_inactive(policy)) + /* + * BIOS might change freq behind our back + * -> ask driver for current freq and notify governors about a change + */ + if (cpufreq_driver->get && !cpufreq_driver->setpolicy && + (cpufreq_suspended || WARN_ON(!cpufreq_update_current_freq(policy)))) goto unlock; pr_debug("updating policy for CPU %u\n", cpu); @@ -2321,28 +2393,29 @@ void cpufreq_update_policy(unsigned int cpu) new_policy.min = policy->user_policy.min; new_policy.max = policy->user_policy.max; - /* - * BIOS might change freq behind our back - * -> ask driver for current freq and notify governors about a change - */ - if (cpufreq_driver->get && !cpufreq_driver->setpolicy) { - if (cpufreq_suspended) - goto unlock; - - new_policy.cur = cpufreq_update_current_freq(policy); - if (WARN_ON(!new_policy.cur)) - goto unlock; - } - cpufreq_set_policy(policy, &new_policy); unlock: - up_write(&policy->rwsem); - - cpufreq_cpu_put(policy); + cpufreq_cpu_release(policy); } EXPORT_SYMBOL(cpufreq_update_policy); +/** + * cpufreq_update_limits - Update policy limits for a given CPU. + * @cpu: CPU to update the policy limits for. + * + * Invoke the driver's ->update_limits callback if present or call + * cpufreq_update_policy() for @cpu. + */ +void cpufreq_update_limits(unsigned int cpu) +{ + if (cpufreq_driver->update_limits) + cpufreq_driver->update_limits(cpu); + else + cpufreq_update_policy(cpu); +} +EXPORT_SYMBOL_GPL(cpufreq_update_limits); + /********************************************************************* * BOOST * *********************************************************************/ @@ -2399,7 +2472,7 @@ int cpufreq_boost_trigger_state(int state) static bool cpufreq_boost_supported(void) { - return likely(cpufreq_driver) && cpufreq_driver->set_boost; + return cpufreq_driver->set_boost; } static int create_boost_sysfs_file(void) @@ -2483,7 +2556,8 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) driver_data->target) || (driver_data->setpolicy && (driver_data->target_index || driver_data->target)) || - (!!driver_data->get_intermediate != !!driver_data->target_intermediate)) + (!driver_data->get_intermediate != !driver_data->target_intermediate) || + (!driver_data->online != !driver_data->offline)) return -EINVAL; pr_debug("trying to register driver %s\n", driver_data->name); |