Loading drivers/thermal/cpu_cooling.c +151 −12 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ #include <linux/slab.h> #include <linux/cpu.h> #include <linux/cpu_cooling.h> #include <linux/of_device.h> #include <trace/events/thermal.h> Loading Loading @@ -80,6 +81,10 @@ struct time_in_idle { * cooling devices. * @clipped_freq: integer value representing the absolute value of the clipped * frequency. * @cpufreq_floor_state: integer value representing the frequency floor state * of cpufreq cooling devices. * @floor_freq: integer value representing the absolute value of the floor * frequency. * @max_level: maximum cooling level. One less than total number of valid * cpufreq frequencies. * @freq_table: Freq table in descending order of frequencies Loading @@ -97,12 +102,15 @@ struct cpufreq_cooling_device { u32 last_load; unsigned int cpufreq_state; unsigned int clipped_freq; unsigned int cpufreq_floor_state; unsigned int floor_freq; unsigned int max_level; struct freq_table *freq_table; /* In descending order */ struct thermal_cooling_device *cdev; struct cpufreq_policy *policy; struct list_head node; struct time_in_idle *idle_time; struct cpu_cooling_ops *plat_ops; }; static DEFINE_IDA(cpufreq_ida); Loading Loading @@ -165,7 +173,7 @@ static int cpufreq_thermal_notifier(struct notifier_block *nb, unsigned long event, void *data) { struct cpufreq_policy *policy = data; unsigned long clipped_freq; unsigned long clipped_freq = ULONG_MAX, floor_freq = 0; struct cpufreq_cooling_device *cpufreq_cdev; if (event != CPUFREQ_ADJUST) Loading @@ -190,13 +198,18 @@ static int cpufreq_thermal_notifier(struct notifier_block *nb, * * But, if clipped_freq is greater than policy->max, we don't * need to do anything. * * Similarly, if policy minimum set by the user is less than * the floor_frequency, then adjust the policy->min. */ clipped_freq = cpufreq_cdev->clipped_freq; if (policy->max > clipped_freq) cpufreq_verify_within_limits(policy, 0, clipped_freq); floor_freq = cpufreq_cdev->floor_freq; if (policy->max > clipped_freq || policy->min < floor_freq) cpufreq_verify_within_limits(policy, floor_freq, clipped_freq); break; } mutex_unlock(&cooling_list_lock); return NOTIFY_OK; Loading Loading @@ -372,6 +385,67 @@ static int cpufreq_get_max_state(struct thermal_cooling_device *cdev, return 0; } /** * cpufreq_get_min_state - callback function to get the device floor state. * @cdev: thermal cooling device pointer. * @state: fill this variable with the cooling device floor. * * Callback for the thermal cooling device to return the cpufreq * floor state. * * Return: 0 on success, an error code otherwise. */ static int cpufreq_get_min_state(struct thermal_cooling_device *cdev, unsigned long *state) { struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata; *state = cpufreq_cdev->cpufreq_floor_state; return 0; } /** * cpufreq_set_min_state - callback function to set the device floor state. * @cdev: thermal cooling device pointer. * @state: set this variable to the current cooling state. * * Callback for the thermal cooling device to change the cpufreq * floor state. * * Return: 0 on success, an error code otherwise. */ static int cpufreq_set_min_state(struct thermal_cooling_device *cdev, unsigned long state) { struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata; unsigned int cpu = cpufreq_cdev->policy->cpu; unsigned int floor_freq; if (state > cpufreq_cdev->max_level) state = cpufreq_cdev->max_level; if (cpufreq_cdev->cpufreq_floor_state == state) return 0; cpufreq_cdev->cpufreq_floor_state = state; floor_freq = cpufreq_cdev->freq_table[state].frequency; cpufreq_cdev->floor_freq = floor_freq; /* * Check if the device has a platform mitigation function that * can handle the CPU freq mitigation, if not, notify cpufreq * framework. */ if (cpufreq_cdev->plat_ops && cpufreq_cdev->plat_ops->floor_limit) cpufreq_cdev->plat_ops->floor_limit(cpu, floor_freq); else cpufreq_update_policy(cpu); return 0; } /** * cpufreq_get_cur_state - callback function to get the current cooling state. * @cdev: thermal cooling device pointer. Loading Loading @@ -420,6 +494,15 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev, cpufreq_cdev->cpufreq_state = state; cpufreq_cdev->clipped_freq = clip_freq; /* Check if the device has a platform mitigation function that * can handle the CPU freq mitigation, if not, notify cpufreq * framework. */ if (cpufreq_cdev->plat_ops && cpufreq_cdev->plat_ops->ceil_limit) cpufreq_cdev->plat_ops->ceil_limit(cpufreq_cdev->policy->cpu, clip_freq); else cpufreq_update_policy(cpufreq_cdev->policy->cpu); return 0; Loading Loading @@ -577,6 +660,8 @@ static struct thermal_cooling_device_ops cpufreq_cooling_ops = { .get_max_state = cpufreq_get_max_state, .get_cur_state = cpufreq_get_cur_state, .set_cur_state = cpufreq_set_cur_state, .set_min_state = cpufreq_set_min_state, .get_min_state = cpufreq_get_min_state, }; static struct thermal_cooling_device_ops cpufreq_power_cooling_ops = { Loading Loading @@ -613,6 +698,9 @@ static unsigned int find_next_max(struct cpufreq_frequency_table *table, * @policy: cpufreq policy * Normally this should be same as cpufreq policy->related_cpus. * @capacitance: dynamic power coefficient for these cpus * @plat_ops: function that does the mitigation by changing the * frequencies (Optional). By default, cpufreq framework will * be notified of the new limits. * * This interface function registers the cpufreq cooling device with the name * "thermal-cpufreq-%x". This api can support multiple instances of cpufreq Loading @@ -624,7 +712,8 @@ static unsigned int find_next_max(struct cpufreq_frequency_table *table, */ static struct thermal_cooling_device * __cpufreq_cooling_register(struct device_node *np, struct cpufreq_policy *policy, u32 capacitance) struct cpufreq_policy *policy, u32 capacitance, struct cpu_cooling_ops *plat_ops) { struct thermal_cooling_device *cdev; struct cpufreq_cooling_device *cpufreq_cdev; Loading Loading @@ -705,12 +794,17 @@ __cpufreq_cooling_register(struct device_node *np, cooling_ops = &cpufreq_cooling_ops; } cpufreq_cdev->plat_ops = plat_ops; cdev = thermal_of_cooling_device_register(np, dev_name, cpufreq_cdev, cooling_ops); if (IS_ERR(cdev)) goto remove_ida; cpufreq_cdev->clipped_freq = cpufreq_cdev->freq_table[0].frequency; cpufreq_cdev->floor_freq = cpufreq_cdev->freq_table[cpufreq_cdev->max_level].frequency; cpufreq_cdev->cpufreq_floor_state = cpufreq_cdev->max_level; cpufreq_cdev->cdev = cdev; mutex_lock(&cooling_list_lock); Loading @@ -719,7 +813,7 @@ __cpufreq_cooling_register(struct device_node *np, list_add(&cpufreq_cdev->node, &cpufreq_cdev_list); mutex_unlock(&cooling_list_lock); if (first) if (first && !cpufreq_cdev->plat_ops) cpufreq_register_notifier(&thermal_cpufreq_notifier_block, CPUFREQ_POLICY_NOTIFIER); Loading Loading @@ -750,7 +844,7 @@ __cpufreq_cooling_register(struct device_node *np, struct thermal_cooling_device * cpufreq_cooling_register(struct cpufreq_policy *policy) { return __cpufreq_cooling_register(NULL, policy, 0); return __cpufreq_cooling_register(NULL, policy, 0, NULL); } EXPORT_SYMBOL_GPL(cpufreq_cooling_register); Loading Loading @@ -790,7 +884,8 @@ of_cpufreq_cooling_register(struct cpufreq_policy *policy) of_property_read_u32(np, "dynamic-power-coefficient", &capacitance); cdev = __cpufreq_cooling_register(np, policy, capacitance); cdev = __cpufreq_cooling_register(np, policy, capacitance, NULL); if (IS_ERR(cdev)) { pr_err("cpu_cooling: cpu%d is not running as cooling device: %ld\n", policy->cpu, PTR_ERR(cdev)); Loading @@ -803,6 +898,47 @@ of_cpufreq_cooling_register(struct cpufreq_policy *policy) } EXPORT_SYMBOL_GPL(of_cpufreq_cooling_register); /** * cpufreq_platform_cooling_register() - create cpufreq cooling device with * additional platform specific mitigation function. * * @policy: cpufreq policy * @plat_ops: the platform mitigation functions that will be called insted of * cpufreq, if provided. * * Return: a valid struct thermal_cooling_device pointer on success, * on failure, it returns a corresponding ERR_PTR(). */ struct thermal_cooling_device * cpufreq_platform_cooling_register(struct cpufreq_policy *policy, struct cpu_cooling_ops *plat_ops) { struct device_node *cpu_node = NULL; u32 capacitance = 0; struct thermal_cooling_device *cdev = NULL; cpu_node = of_cpu_device_node_get(policy->cpu); if (!cpu_node) { pr_err("No cpu node\n"); return ERR_PTR(-EINVAL); } if (of_find_property(cpu_node, "#cooling-cells", NULL)) { of_property_read_u32(cpu_node, "dynamic-power-coefficient", &capacitance); cdev = __cpufreq_cooling_register(cpu_node, policy, capacitance, plat_ops); if (IS_ERR(cdev)) pr_err("cpu_cooling: cpu%d cooling device err: %ld\n", policy->cpu, PTR_ERR(cdev)); } of_node_put(cpu_node); return cdev; } EXPORT_SYMBOL(cpufreq_platform_cooling_register); /** * cpufreq_cooling_unregister - function to remove cpufreq cooling device. * @cdev: thermal cooling device pointer. Loading @@ -825,9 +961,12 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) last = list_empty(&cpufreq_cdev_list); mutex_unlock(&cooling_list_lock); if (last) cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block, if (last) { if (!cpufreq_cdev->plat_ops) cpufreq_unregister_notifier( &thermal_cpufreq_notifier_block, CPUFREQ_POLICY_NOTIFIER); } thermal_cooling_device_unregister(cpufreq_cdev->cdev); ida_simple_remove(&cpufreq_ida, cpufreq_cdev->id); Loading include/linux/cpu_cooling.h +17 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,12 @@ struct cpufreq_policy; typedef int (*plat_mitig_t)(int cpu, u32 clip_freq); struct cpu_cooling_ops { plat_mitig_t ceil_limit, floor_limit; }; #ifdef CONFIG_CPU_THERMAL /** * cpufreq_cooling_register - function to create cpufreq cooling device. Loading @@ -38,6 +44,10 @@ struct cpufreq_policy; struct thermal_cooling_device * cpufreq_cooling_register(struct cpufreq_policy *policy); struct thermal_cooling_device * cpufreq_platform_cooling_register(struct cpufreq_policy *policy, struct cpu_cooling_ops *ops); /** * cpufreq_cooling_unregister - function to remove cpufreq cooling device. * @cdev: thermal cooling device pointer. Loading Loading @@ -74,6 +84,13 @@ of_cpufreq_cooling_register(struct cpufreq_policy *policy) { return NULL; } static inline struct thermal_cooling_device * cpufreq_platform_cooling_register(struct cpufreq_policy *policy, struct cpu_cooling_ops *ops) { return NULL; } #endif /* defined(CONFIG_THERMAL_OF) && defined(CONFIG_CPU_THERMAL) */ #endif /* __CPU_COOLING_H__ */ Loading
drivers/thermal/cpu_cooling.c +151 −12 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ #include <linux/slab.h> #include <linux/cpu.h> #include <linux/cpu_cooling.h> #include <linux/of_device.h> #include <trace/events/thermal.h> Loading Loading @@ -80,6 +81,10 @@ struct time_in_idle { * cooling devices. * @clipped_freq: integer value representing the absolute value of the clipped * frequency. * @cpufreq_floor_state: integer value representing the frequency floor state * of cpufreq cooling devices. * @floor_freq: integer value representing the absolute value of the floor * frequency. * @max_level: maximum cooling level. One less than total number of valid * cpufreq frequencies. * @freq_table: Freq table in descending order of frequencies Loading @@ -97,12 +102,15 @@ struct cpufreq_cooling_device { u32 last_load; unsigned int cpufreq_state; unsigned int clipped_freq; unsigned int cpufreq_floor_state; unsigned int floor_freq; unsigned int max_level; struct freq_table *freq_table; /* In descending order */ struct thermal_cooling_device *cdev; struct cpufreq_policy *policy; struct list_head node; struct time_in_idle *idle_time; struct cpu_cooling_ops *plat_ops; }; static DEFINE_IDA(cpufreq_ida); Loading Loading @@ -165,7 +173,7 @@ static int cpufreq_thermal_notifier(struct notifier_block *nb, unsigned long event, void *data) { struct cpufreq_policy *policy = data; unsigned long clipped_freq; unsigned long clipped_freq = ULONG_MAX, floor_freq = 0; struct cpufreq_cooling_device *cpufreq_cdev; if (event != CPUFREQ_ADJUST) Loading @@ -190,13 +198,18 @@ static int cpufreq_thermal_notifier(struct notifier_block *nb, * * But, if clipped_freq is greater than policy->max, we don't * need to do anything. * * Similarly, if policy minimum set by the user is less than * the floor_frequency, then adjust the policy->min. */ clipped_freq = cpufreq_cdev->clipped_freq; if (policy->max > clipped_freq) cpufreq_verify_within_limits(policy, 0, clipped_freq); floor_freq = cpufreq_cdev->floor_freq; if (policy->max > clipped_freq || policy->min < floor_freq) cpufreq_verify_within_limits(policy, floor_freq, clipped_freq); break; } mutex_unlock(&cooling_list_lock); return NOTIFY_OK; Loading Loading @@ -372,6 +385,67 @@ static int cpufreq_get_max_state(struct thermal_cooling_device *cdev, return 0; } /** * cpufreq_get_min_state - callback function to get the device floor state. * @cdev: thermal cooling device pointer. * @state: fill this variable with the cooling device floor. * * Callback for the thermal cooling device to return the cpufreq * floor state. * * Return: 0 on success, an error code otherwise. */ static int cpufreq_get_min_state(struct thermal_cooling_device *cdev, unsigned long *state) { struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata; *state = cpufreq_cdev->cpufreq_floor_state; return 0; } /** * cpufreq_set_min_state - callback function to set the device floor state. * @cdev: thermal cooling device pointer. * @state: set this variable to the current cooling state. * * Callback for the thermal cooling device to change the cpufreq * floor state. * * Return: 0 on success, an error code otherwise. */ static int cpufreq_set_min_state(struct thermal_cooling_device *cdev, unsigned long state) { struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata; unsigned int cpu = cpufreq_cdev->policy->cpu; unsigned int floor_freq; if (state > cpufreq_cdev->max_level) state = cpufreq_cdev->max_level; if (cpufreq_cdev->cpufreq_floor_state == state) return 0; cpufreq_cdev->cpufreq_floor_state = state; floor_freq = cpufreq_cdev->freq_table[state].frequency; cpufreq_cdev->floor_freq = floor_freq; /* * Check if the device has a platform mitigation function that * can handle the CPU freq mitigation, if not, notify cpufreq * framework. */ if (cpufreq_cdev->plat_ops && cpufreq_cdev->plat_ops->floor_limit) cpufreq_cdev->plat_ops->floor_limit(cpu, floor_freq); else cpufreq_update_policy(cpu); return 0; } /** * cpufreq_get_cur_state - callback function to get the current cooling state. * @cdev: thermal cooling device pointer. Loading Loading @@ -420,6 +494,15 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev, cpufreq_cdev->cpufreq_state = state; cpufreq_cdev->clipped_freq = clip_freq; /* Check if the device has a platform mitigation function that * can handle the CPU freq mitigation, if not, notify cpufreq * framework. */ if (cpufreq_cdev->plat_ops && cpufreq_cdev->plat_ops->ceil_limit) cpufreq_cdev->plat_ops->ceil_limit(cpufreq_cdev->policy->cpu, clip_freq); else cpufreq_update_policy(cpufreq_cdev->policy->cpu); return 0; Loading Loading @@ -577,6 +660,8 @@ static struct thermal_cooling_device_ops cpufreq_cooling_ops = { .get_max_state = cpufreq_get_max_state, .get_cur_state = cpufreq_get_cur_state, .set_cur_state = cpufreq_set_cur_state, .set_min_state = cpufreq_set_min_state, .get_min_state = cpufreq_get_min_state, }; static struct thermal_cooling_device_ops cpufreq_power_cooling_ops = { Loading Loading @@ -613,6 +698,9 @@ static unsigned int find_next_max(struct cpufreq_frequency_table *table, * @policy: cpufreq policy * Normally this should be same as cpufreq policy->related_cpus. * @capacitance: dynamic power coefficient for these cpus * @plat_ops: function that does the mitigation by changing the * frequencies (Optional). By default, cpufreq framework will * be notified of the new limits. * * This interface function registers the cpufreq cooling device with the name * "thermal-cpufreq-%x". This api can support multiple instances of cpufreq Loading @@ -624,7 +712,8 @@ static unsigned int find_next_max(struct cpufreq_frequency_table *table, */ static struct thermal_cooling_device * __cpufreq_cooling_register(struct device_node *np, struct cpufreq_policy *policy, u32 capacitance) struct cpufreq_policy *policy, u32 capacitance, struct cpu_cooling_ops *plat_ops) { struct thermal_cooling_device *cdev; struct cpufreq_cooling_device *cpufreq_cdev; Loading Loading @@ -705,12 +794,17 @@ __cpufreq_cooling_register(struct device_node *np, cooling_ops = &cpufreq_cooling_ops; } cpufreq_cdev->plat_ops = plat_ops; cdev = thermal_of_cooling_device_register(np, dev_name, cpufreq_cdev, cooling_ops); if (IS_ERR(cdev)) goto remove_ida; cpufreq_cdev->clipped_freq = cpufreq_cdev->freq_table[0].frequency; cpufreq_cdev->floor_freq = cpufreq_cdev->freq_table[cpufreq_cdev->max_level].frequency; cpufreq_cdev->cpufreq_floor_state = cpufreq_cdev->max_level; cpufreq_cdev->cdev = cdev; mutex_lock(&cooling_list_lock); Loading @@ -719,7 +813,7 @@ __cpufreq_cooling_register(struct device_node *np, list_add(&cpufreq_cdev->node, &cpufreq_cdev_list); mutex_unlock(&cooling_list_lock); if (first) if (first && !cpufreq_cdev->plat_ops) cpufreq_register_notifier(&thermal_cpufreq_notifier_block, CPUFREQ_POLICY_NOTIFIER); Loading Loading @@ -750,7 +844,7 @@ __cpufreq_cooling_register(struct device_node *np, struct thermal_cooling_device * cpufreq_cooling_register(struct cpufreq_policy *policy) { return __cpufreq_cooling_register(NULL, policy, 0); return __cpufreq_cooling_register(NULL, policy, 0, NULL); } EXPORT_SYMBOL_GPL(cpufreq_cooling_register); Loading Loading @@ -790,7 +884,8 @@ of_cpufreq_cooling_register(struct cpufreq_policy *policy) of_property_read_u32(np, "dynamic-power-coefficient", &capacitance); cdev = __cpufreq_cooling_register(np, policy, capacitance); cdev = __cpufreq_cooling_register(np, policy, capacitance, NULL); if (IS_ERR(cdev)) { pr_err("cpu_cooling: cpu%d is not running as cooling device: %ld\n", policy->cpu, PTR_ERR(cdev)); Loading @@ -803,6 +898,47 @@ of_cpufreq_cooling_register(struct cpufreq_policy *policy) } EXPORT_SYMBOL_GPL(of_cpufreq_cooling_register); /** * cpufreq_platform_cooling_register() - create cpufreq cooling device with * additional platform specific mitigation function. * * @policy: cpufreq policy * @plat_ops: the platform mitigation functions that will be called insted of * cpufreq, if provided. * * Return: a valid struct thermal_cooling_device pointer on success, * on failure, it returns a corresponding ERR_PTR(). */ struct thermal_cooling_device * cpufreq_platform_cooling_register(struct cpufreq_policy *policy, struct cpu_cooling_ops *plat_ops) { struct device_node *cpu_node = NULL; u32 capacitance = 0; struct thermal_cooling_device *cdev = NULL; cpu_node = of_cpu_device_node_get(policy->cpu); if (!cpu_node) { pr_err("No cpu node\n"); return ERR_PTR(-EINVAL); } if (of_find_property(cpu_node, "#cooling-cells", NULL)) { of_property_read_u32(cpu_node, "dynamic-power-coefficient", &capacitance); cdev = __cpufreq_cooling_register(cpu_node, policy, capacitance, plat_ops); if (IS_ERR(cdev)) pr_err("cpu_cooling: cpu%d cooling device err: %ld\n", policy->cpu, PTR_ERR(cdev)); } of_node_put(cpu_node); return cdev; } EXPORT_SYMBOL(cpufreq_platform_cooling_register); /** * cpufreq_cooling_unregister - function to remove cpufreq cooling device. * @cdev: thermal cooling device pointer. Loading @@ -825,9 +961,12 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) last = list_empty(&cpufreq_cdev_list); mutex_unlock(&cooling_list_lock); if (last) cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block, if (last) { if (!cpufreq_cdev->plat_ops) cpufreq_unregister_notifier( &thermal_cpufreq_notifier_block, CPUFREQ_POLICY_NOTIFIER); } thermal_cooling_device_unregister(cpufreq_cdev->cdev); ida_simple_remove(&cpufreq_ida, cpufreq_cdev->id); Loading
include/linux/cpu_cooling.h +17 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,12 @@ struct cpufreq_policy; typedef int (*plat_mitig_t)(int cpu, u32 clip_freq); struct cpu_cooling_ops { plat_mitig_t ceil_limit, floor_limit; }; #ifdef CONFIG_CPU_THERMAL /** * cpufreq_cooling_register - function to create cpufreq cooling device. Loading @@ -38,6 +44,10 @@ struct cpufreq_policy; struct thermal_cooling_device * cpufreq_cooling_register(struct cpufreq_policy *policy); struct thermal_cooling_device * cpufreq_platform_cooling_register(struct cpufreq_policy *policy, struct cpu_cooling_ops *ops); /** * cpufreq_cooling_unregister - function to remove cpufreq cooling device. * @cdev: thermal cooling device pointer. Loading Loading @@ -74,6 +84,13 @@ of_cpufreq_cooling_register(struct cpufreq_policy *policy) { return NULL; } static inline struct thermal_cooling_device * cpufreq_platform_cooling_register(struct cpufreq_policy *policy, struct cpu_cooling_ops *ops) { return NULL; } #endif /* defined(CONFIG_THERMAL_OF) && defined(CONFIG_CPU_THERMAL) */ #endif /* __CPU_COOLING_H__ */