162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/drivers/thermal/cpufreq_cooling.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012 Samsung Electronics Co., Ltd(http://www.samsung.com) 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2012-2018 Linaro Limited. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Authors: Amit Daniel <amit.kachhap@linaro.org> 1062306a36Sopenharmony_ci * Viresh Kumar <viresh.kumar@linaro.org> 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci#include <linux/cpu.h> 1462306a36Sopenharmony_ci#include <linux/cpufreq.h> 1562306a36Sopenharmony_ci#include <linux/cpu_cooling.h> 1662306a36Sopenharmony_ci#include <linux/device.h> 1762306a36Sopenharmony_ci#include <linux/energy_model.h> 1862306a36Sopenharmony_ci#include <linux/err.h> 1962306a36Sopenharmony_ci#include <linux/export.h> 2062306a36Sopenharmony_ci#include <linux/pm_opp.h> 2162306a36Sopenharmony_ci#include <linux/pm_qos.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci#include <linux/thermal.h> 2462306a36Sopenharmony_ci#include <linux/units.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "thermal_trace.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* 2962306a36Sopenharmony_ci * Cooling state <-> CPUFreq frequency 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * Cooling states are translated to frequencies throughout this driver and this 3262306a36Sopenharmony_ci * is the relation between them. 3362306a36Sopenharmony_ci * 3462306a36Sopenharmony_ci * Highest cooling state corresponds to lowest possible frequency. 3562306a36Sopenharmony_ci * 3662306a36Sopenharmony_ci * i.e. 3762306a36Sopenharmony_ci * level 0 --> 1st Max Freq 3862306a36Sopenharmony_ci * level 1 --> 2nd Max Freq 3962306a36Sopenharmony_ci * ... 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/** 4362306a36Sopenharmony_ci * struct time_in_idle - Idle time stats 4462306a36Sopenharmony_ci * @time: previous reading of the absolute time that this cpu was idle 4562306a36Sopenharmony_ci * @timestamp: wall time of the last invocation of get_cpu_idle_time_us() 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_cistruct time_in_idle { 4862306a36Sopenharmony_ci u64 time; 4962306a36Sopenharmony_ci u64 timestamp; 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/** 5362306a36Sopenharmony_ci * struct cpufreq_cooling_device - data for cooling device with cpufreq 5462306a36Sopenharmony_ci * @last_load: load measured by the latest call to cpufreq_get_requested_power() 5562306a36Sopenharmony_ci * @cpufreq_state: integer value representing the current state of cpufreq 5662306a36Sopenharmony_ci * cooling devices. 5762306a36Sopenharmony_ci * @max_level: maximum cooling level. One less than total number of valid 5862306a36Sopenharmony_ci * cpufreq frequencies. 5962306a36Sopenharmony_ci * @em: Reference on the Energy Model of the device 6062306a36Sopenharmony_ci * @cdev: thermal_cooling_device pointer to keep track of the 6162306a36Sopenharmony_ci * registered cooling device. 6262306a36Sopenharmony_ci * @policy: cpufreq policy. 6362306a36Sopenharmony_ci * @cooling_ops: cpufreq callbacks to thermal cooling device ops 6462306a36Sopenharmony_ci * @idle_time: idle time stats 6562306a36Sopenharmony_ci * @qos_req: PM QoS contraint to apply 6662306a36Sopenharmony_ci * 6762306a36Sopenharmony_ci * This structure is required for keeping information of each registered 6862306a36Sopenharmony_ci * cpufreq_cooling_device. 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_cistruct cpufreq_cooling_device { 7162306a36Sopenharmony_ci u32 last_load; 7262306a36Sopenharmony_ci unsigned int cpufreq_state; 7362306a36Sopenharmony_ci unsigned int max_level; 7462306a36Sopenharmony_ci struct em_perf_domain *em; 7562306a36Sopenharmony_ci struct cpufreq_policy *policy; 7662306a36Sopenharmony_ci struct thermal_cooling_device_ops cooling_ops; 7762306a36Sopenharmony_ci#ifndef CONFIG_SMP 7862306a36Sopenharmony_ci struct time_in_idle *idle_time; 7962306a36Sopenharmony_ci#endif 8062306a36Sopenharmony_ci struct freq_qos_request qos_req; 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci#ifdef CONFIG_THERMAL_GOV_POWER_ALLOCATOR 8462306a36Sopenharmony_ci/** 8562306a36Sopenharmony_ci * get_level: Find the level for a particular frequency 8662306a36Sopenharmony_ci * @cpufreq_cdev: cpufreq_cdev for which the property is required 8762306a36Sopenharmony_ci * @freq: Frequency 8862306a36Sopenharmony_ci * 8962306a36Sopenharmony_ci * Return: level corresponding to the frequency. 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_cistatic unsigned long get_level(struct cpufreq_cooling_device *cpufreq_cdev, 9262306a36Sopenharmony_ci unsigned int freq) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci int i; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci for (i = cpufreq_cdev->max_level - 1; i >= 0; i--) { 9762306a36Sopenharmony_ci if (freq > cpufreq_cdev->em->table[i].frequency) 9862306a36Sopenharmony_ci break; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci return cpufreq_cdev->max_level - i - 1; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic u32 cpu_freq_to_power(struct cpufreq_cooling_device *cpufreq_cdev, 10562306a36Sopenharmony_ci u32 freq) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci unsigned long power_mw; 10862306a36Sopenharmony_ci int i; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci for (i = cpufreq_cdev->max_level - 1; i >= 0; i--) { 11162306a36Sopenharmony_ci if (freq > cpufreq_cdev->em->table[i].frequency) 11262306a36Sopenharmony_ci break; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci power_mw = cpufreq_cdev->em->table[i + 1].power; 11662306a36Sopenharmony_ci power_mw /= MICROWATT_PER_MILLIWATT; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci return power_mw; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic u32 cpu_power_to_freq(struct cpufreq_cooling_device *cpufreq_cdev, 12262306a36Sopenharmony_ci u32 power) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci unsigned long em_power_mw; 12562306a36Sopenharmony_ci int i; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci for (i = cpufreq_cdev->max_level; i > 0; i--) { 12862306a36Sopenharmony_ci /* Convert EM power to milli-Watts to make safe comparison */ 12962306a36Sopenharmony_ci em_power_mw = cpufreq_cdev->em->table[i].power; 13062306a36Sopenharmony_ci em_power_mw /= MICROWATT_PER_MILLIWATT; 13162306a36Sopenharmony_ci if (power >= em_power_mw) 13262306a36Sopenharmony_ci break; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci return cpufreq_cdev->em->table[i].frequency; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci/** 13962306a36Sopenharmony_ci * get_load() - get load for a cpu 14062306a36Sopenharmony_ci * @cpufreq_cdev: struct cpufreq_cooling_device for the cpu 14162306a36Sopenharmony_ci * @cpu: cpu number 14262306a36Sopenharmony_ci * @cpu_idx: index of the cpu in time_in_idle array 14362306a36Sopenharmony_ci * 14462306a36Sopenharmony_ci * Return: The average load of cpu @cpu in percentage since this 14562306a36Sopenharmony_ci * function was last called. 14662306a36Sopenharmony_ci */ 14762306a36Sopenharmony_ci#ifdef CONFIG_SMP 14862306a36Sopenharmony_cistatic u32 get_load(struct cpufreq_cooling_device *cpufreq_cdev, int cpu, 14962306a36Sopenharmony_ci int cpu_idx) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci unsigned long util = sched_cpu_util(cpu); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci return (util * 100) / arch_scale_cpu_capacity(cpu); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci#else /* !CONFIG_SMP */ 15662306a36Sopenharmony_cistatic u32 get_load(struct cpufreq_cooling_device *cpufreq_cdev, int cpu, 15762306a36Sopenharmony_ci int cpu_idx) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci u32 load; 16062306a36Sopenharmony_ci u64 now, now_idle, delta_time, delta_idle; 16162306a36Sopenharmony_ci struct time_in_idle *idle_time = &cpufreq_cdev->idle_time[cpu_idx]; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci now_idle = get_cpu_idle_time(cpu, &now, 0); 16462306a36Sopenharmony_ci delta_idle = now_idle - idle_time->time; 16562306a36Sopenharmony_ci delta_time = now - idle_time->timestamp; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (delta_time <= delta_idle) 16862306a36Sopenharmony_ci load = 0; 16962306a36Sopenharmony_ci else 17062306a36Sopenharmony_ci load = div64_u64(100 * (delta_time - delta_idle), delta_time); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci idle_time->time = now_idle; 17362306a36Sopenharmony_ci idle_time->timestamp = now; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return load; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci#endif /* CONFIG_SMP */ 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci/** 18062306a36Sopenharmony_ci * get_dynamic_power() - calculate the dynamic power 18162306a36Sopenharmony_ci * @cpufreq_cdev: &cpufreq_cooling_device for this cdev 18262306a36Sopenharmony_ci * @freq: current frequency 18362306a36Sopenharmony_ci * 18462306a36Sopenharmony_ci * Return: the dynamic power consumed by the cpus described by 18562306a36Sopenharmony_ci * @cpufreq_cdev. 18662306a36Sopenharmony_ci */ 18762306a36Sopenharmony_cistatic u32 get_dynamic_power(struct cpufreq_cooling_device *cpufreq_cdev, 18862306a36Sopenharmony_ci unsigned long freq) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci u32 raw_cpu_power; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci raw_cpu_power = cpu_freq_to_power(cpufreq_cdev, freq); 19362306a36Sopenharmony_ci return (raw_cpu_power * cpufreq_cdev->last_load) / 100; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci/** 19762306a36Sopenharmony_ci * cpufreq_get_requested_power() - get the current power 19862306a36Sopenharmony_ci * @cdev: &thermal_cooling_device pointer 19962306a36Sopenharmony_ci * @power: pointer in which to store the resulting power 20062306a36Sopenharmony_ci * 20162306a36Sopenharmony_ci * Calculate the current power consumption of the cpus in milliwatts 20262306a36Sopenharmony_ci * and store it in @power. This function should actually calculate 20362306a36Sopenharmony_ci * the requested power, but it's hard to get the frequency that 20462306a36Sopenharmony_ci * cpufreq would have assigned if there were no thermal limits. 20562306a36Sopenharmony_ci * Instead, we calculate the current power on the assumption that the 20662306a36Sopenharmony_ci * immediate future will look like the immediate past. 20762306a36Sopenharmony_ci * 20862306a36Sopenharmony_ci * We use the current frequency and the average load since this 20962306a36Sopenharmony_ci * function was last called. In reality, there could have been 21062306a36Sopenharmony_ci * multiple opps since this function was last called and that affects 21162306a36Sopenharmony_ci * the load calculation. While it's not perfectly accurate, this 21262306a36Sopenharmony_ci * simplification is good enough and works. REVISIT this, as more 21362306a36Sopenharmony_ci * complex code may be needed if experiments show that it's not 21462306a36Sopenharmony_ci * accurate enough. 21562306a36Sopenharmony_ci * 21662306a36Sopenharmony_ci * Return: 0 on success, this function doesn't fail. 21762306a36Sopenharmony_ci */ 21862306a36Sopenharmony_cistatic int cpufreq_get_requested_power(struct thermal_cooling_device *cdev, 21962306a36Sopenharmony_ci u32 *power) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci unsigned long freq; 22262306a36Sopenharmony_ci int i = 0, cpu; 22362306a36Sopenharmony_ci u32 total_load = 0; 22462306a36Sopenharmony_ci struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata; 22562306a36Sopenharmony_ci struct cpufreq_policy *policy = cpufreq_cdev->policy; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci freq = cpufreq_quick_get(policy->cpu); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci for_each_cpu(cpu, policy->related_cpus) { 23062306a36Sopenharmony_ci u32 load; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (cpu_online(cpu)) 23362306a36Sopenharmony_ci load = get_load(cpufreq_cdev, cpu, i); 23462306a36Sopenharmony_ci else 23562306a36Sopenharmony_ci load = 0; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci total_load += load; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci cpufreq_cdev->last_load = total_load; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci *power = get_dynamic_power(cpufreq_cdev, freq); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci trace_thermal_power_cpu_get_power_simple(policy->cpu, *power); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci return 0; 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci/** 25062306a36Sopenharmony_ci * cpufreq_state2power() - convert a cpu cdev state to power consumed 25162306a36Sopenharmony_ci * @cdev: &thermal_cooling_device pointer 25262306a36Sopenharmony_ci * @state: cooling device state to be converted 25362306a36Sopenharmony_ci * @power: pointer in which to store the resulting power 25462306a36Sopenharmony_ci * 25562306a36Sopenharmony_ci * Convert cooling device state @state into power consumption in 25662306a36Sopenharmony_ci * milliwatts assuming 100% load. Store the calculated power in 25762306a36Sopenharmony_ci * @power. 25862306a36Sopenharmony_ci * 25962306a36Sopenharmony_ci * Return: 0 on success, -EINVAL if the cooling device state is bigger 26062306a36Sopenharmony_ci * than maximum allowed. 26162306a36Sopenharmony_ci */ 26262306a36Sopenharmony_cistatic int cpufreq_state2power(struct thermal_cooling_device *cdev, 26362306a36Sopenharmony_ci unsigned long state, u32 *power) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci unsigned int freq, num_cpus, idx; 26662306a36Sopenharmony_ci struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* Request state should be less than max_level */ 26962306a36Sopenharmony_ci if (state > cpufreq_cdev->max_level) 27062306a36Sopenharmony_ci return -EINVAL; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci num_cpus = cpumask_weight(cpufreq_cdev->policy->cpus); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci idx = cpufreq_cdev->max_level - state; 27562306a36Sopenharmony_ci freq = cpufreq_cdev->em->table[idx].frequency; 27662306a36Sopenharmony_ci *power = cpu_freq_to_power(cpufreq_cdev, freq) * num_cpus; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci return 0; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci/** 28262306a36Sopenharmony_ci * cpufreq_power2state() - convert power to a cooling device state 28362306a36Sopenharmony_ci * @cdev: &thermal_cooling_device pointer 28462306a36Sopenharmony_ci * @power: power in milliwatts to be converted 28562306a36Sopenharmony_ci * @state: pointer in which to store the resulting state 28662306a36Sopenharmony_ci * 28762306a36Sopenharmony_ci * Calculate a cooling device state for the cpus described by @cdev 28862306a36Sopenharmony_ci * that would allow them to consume at most @power mW and store it in 28962306a36Sopenharmony_ci * @state. Note that this calculation depends on external factors 29062306a36Sopenharmony_ci * such as the CPUs load. Calling this function with the same power 29162306a36Sopenharmony_ci * as input can yield different cooling device states depending on those 29262306a36Sopenharmony_ci * external factors. 29362306a36Sopenharmony_ci * 29462306a36Sopenharmony_ci * Return: 0 on success, this function doesn't fail. 29562306a36Sopenharmony_ci */ 29662306a36Sopenharmony_cistatic int cpufreq_power2state(struct thermal_cooling_device *cdev, 29762306a36Sopenharmony_ci u32 power, unsigned long *state) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci unsigned int target_freq; 30062306a36Sopenharmony_ci u32 last_load, normalised_power; 30162306a36Sopenharmony_ci struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata; 30262306a36Sopenharmony_ci struct cpufreq_policy *policy = cpufreq_cdev->policy; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci last_load = cpufreq_cdev->last_load ?: 1; 30562306a36Sopenharmony_ci normalised_power = (power * 100) / last_load; 30662306a36Sopenharmony_ci target_freq = cpu_power_to_freq(cpufreq_cdev, normalised_power); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci *state = get_level(cpufreq_cdev, target_freq); 30962306a36Sopenharmony_ci trace_thermal_power_cpu_limit(policy->related_cpus, target_freq, *state, 31062306a36Sopenharmony_ci power); 31162306a36Sopenharmony_ci return 0; 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic inline bool em_is_sane(struct cpufreq_cooling_device *cpufreq_cdev, 31562306a36Sopenharmony_ci struct em_perf_domain *em) { 31662306a36Sopenharmony_ci struct cpufreq_policy *policy; 31762306a36Sopenharmony_ci unsigned int nr_levels; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (!em || em_is_artificial(em)) 32062306a36Sopenharmony_ci return false; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci policy = cpufreq_cdev->policy; 32362306a36Sopenharmony_ci if (!cpumask_equal(policy->related_cpus, em_span_cpus(em))) { 32462306a36Sopenharmony_ci pr_err("The span of pd %*pbl is misaligned with cpufreq policy %*pbl\n", 32562306a36Sopenharmony_ci cpumask_pr_args(em_span_cpus(em)), 32662306a36Sopenharmony_ci cpumask_pr_args(policy->related_cpus)); 32762306a36Sopenharmony_ci return false; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci nr_levels = cpufreq_cdev->max_level + 1; 33162306a36Sopenharmony_ci if (em_pd_nr_perf_states(em) != nr_levels) { 33262306a36Sopenharmony_ci pr_err("The number of performance states in pd %*pbl (%u) doesn't match the number of cooling levels (%u)\n", 33362306a36Sopenharmony_ci cpumask_pr_args(em_span_cpus(em)), 33462306a36Sopenharmony_ci em_pd_nr_perf_states(em), nr_levels); 33562306a36Sopenharmony_ci return false; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci return true; 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci#endif /* CONFIG_THERMAL_GOV_POWER_ALLOCATOR */ 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci#ifdef CONFIG_SMP 34362306a36Sopenharmony_cistatic inline int allocate_idle_time(struct cpufreq_cooling_device *cpufreq_cdev) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci return 0; 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic inline void free_idle_time(struct cpufreq_cooling_device *cpufreq_cdev) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci#else 35262306a36Sopenharmony_cistatic int allocate_idle_time(struct cpufreq_cooling_device *cpufreq_cdev) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci unsigned int num_cpus = cpumask_weight(cpufreq_cdev->policy->related_cpus); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci cpufreq_cdev->idle_time = kcalloc(num_cpus, 35762306a36Sopenharmony_ci sizeof(*cpufreq_cdev->idle_time), 35862306a36Sopenharmony_ci GFP_KERNEL); 35962306a36Sopenharmony_ci if (!cpufreq_cdev->idle_time) 36062306a36Sopenharmony_ci return -ENOMEM; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci return 0; 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic void free_idle_time(struct cpufreq_cooling_device *cpufreq_cdev) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci kfree(cpufreq_cdev->idle_time); 36862306a36Sopenharmony_ci cpufreq_cdev->idle_time = NULL; 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci#endif /* CONFIG_SMP */ 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic unsigned int get_state_freq(struct cpufreq_cooling_device *cpufreq_cdev, 37362306a36Sopenharmony_ci unsigned long state) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci struct cpufreq_policy *policy; 37662306a36Sopenharmony_ci unsigned long idx; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci#ifdef CONFIG_THERMAL_GOV_POWER_ALLOCATOR 37962306a36Sopenharmony_ci /* Use the Energy Model table if available */ 38062306a36Sopenharmony_ci if (cpufreq_cdev->em) { 38162306a36Sopenharmony_ci idx = cpufreq_cdev->max_level - state; 38262306a36Sopenharmony_ci return cpufreq_cdev->em->table[idx].frequency; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci#endif 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci /* Otherwise, fallback on the CPUFreq table */ 38762306a36Sopenharmony_ci policy = cpufreq_cdev->policy; 38862306a36Sopenharmony_ci if (policy->freq_table_sorted == CPUFREQ_TABLE_SORTED_ASCENDING) 38962306a36Sopenharmony_ci idx = cpufreq_cdev->max_level - state; 39062306a36Sopenharmony_ci else 39162306a36Sopenharmony_ci idx = state; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci return policy->freq_table[idx].frequency; 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci/* cpufreq cooling device callback functions are defined below */ 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci/** 39962306a36Sopenharmony_ci * cpufreq_get_max_state - callback function to get the max cooling state. 40062306a36Sopenharmony_ci * @cdev: thermal cooling device pointer. 40162306a36Sopenharmony_ci * @state: fill this variable with the max cooling state. 40262306a36Sopenharmony_ci * 40362306a36Sopenharmony_ci * Callback for the thermal cooling device to return the cpufreq 40462306a36Sopenharmony_ci * max cooling state. 40562306a36Sopenharmony_ci * 40662306a36Sopenharmony_ci * Return: 0 on success, this function doesn't fail. 40762306a36Sopenharmony_ci */ 40862306a36Sopenharmony_cistatic int cpufreq_get_max_state(struct thermal_cooling_device *cdev, 40962306a36Sopenharmony_ci unsigned long *state) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci *state = cpufreq_cdev->max_level; 41462306a36Sopenharmony_ci return 0; 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci/** 41862306a36Sopenharmony_ci * cpufreq_get_cur_state - callback function to get the current cooling state. 41962306a36Sopenharmony_ci * @cdev: thermal cooling device pointer. 42062306a36Sopenharmony_ci * @state: fill this variable with the current cooling state. 42162306a36Sopenharmony_ci * 42262306a36Sopenharmony_ci * Callback for the thermal cooling device to return the cpufreq 42362306a36Sopenharmony_ci * current cooling state. 42462306a36Sopenharmony_ci * 42562306a36Sopenharmony_ci * Return: 0 on success, this function doesn't fail. 42662306a36Sopenharmony_ci */ 42762306a36Sopenharmony_cistatic int cpufreq_get_cur_state(struct thermal_cooling_device *cdev, 42862306a36Sopenharmony_ci unsigned long *state) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci *state = cpufreq_cdev->cpufreq_state; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci return 0; 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci/** 43862306a36Sopenharmony_ci * cpufreq_set_cur_state - callback function to set the current cooling state. 43962306a36Sopenharmony_ci * @cdev: thermal cooling device pointer. 44062306a36Sopenharmony_ci * @state: set this variable to the current cooling state. 44162306a36Sopenharmony_ci * 44262306a36Sopenharmony_ci * Callback for the thermal cooling device to change the cpufreq 44362306a36Sopenharmony_ci * current cooling state. 44462306a36Sopenharmony_ci * 44562306a36Sopenharmony_ci * Return: 0 on success, an error code otherwise. 44662306a36Sopenharmony_ci */ 44762306a36Sopenharmony_cistatic int cpufreq_set_cur_state(struct thermal_cooling_device *cdev, 44862306a36Sopenharmony_ci unsigned long state) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata; 45162306a36Sopenharmony_ci struct cpumask *cpus; 45262306a36Sopenharmony_ci unsigned int frequency; 45362306a36Sopenharmony_ci int ret; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci /* Request state should be less than max_level */ 45662306a36Sopenharmony_ci if (state > cpufreq_cdev->max_level) 45762306a36Sopenharmony_ci return -EINVAL; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci /* Check if the old cooling action is same as new cooling action */ 46062306a36Sopenharmony_ci if (cpufreq_cdev->cpufreq_state == state) 46162306a36Sopenharmony_ci return 0; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci frequency = get_state_freq(cpufreq_cdev, state); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci ret = freq_qos_update_request(&cpufreq_cdev->qos_req, frequency); 46662306a36Sopenharmony_ci if (ret >= 0) { 46762306a36Sopenharmony_ci cpufreq_cdev->cpufreq_state = state; 46862306a36Sopenharmony_ci cpus = cpufreq_cdev->policy->related_cpus; 46962306a36Sopenharmony_ci arch_update_thermal_pressure(cpus, frequency); 47062306a36Sopenharmony_ci ret = 0; 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci return ret; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci/** 47762306a36Sopenharmony_ci * __cpufreq_cooling_register - helper function to create cpufreq cooling device 47862306a36Sopenharmony_ci * @np: a valid struct device_node to the cooling device tree node 47962306a36Sopenharmony_ci * @policy: cpufreq policy 48062306a36Sopenharmony_ci * Normally this should be same as cpufreq policy->related_cpus. 48162306a36Sopenharmony_ci * @em: Energy Model of the cpufreq policy 48262306a36Sopenharmony_ci * 48362306a36Sopenharmony_ci * This interface function registers the cpufreq cooling device with the name 48462306a36Sopenharmony_ci * "cpufreq-%s". This API can support multiple instances of cpufreq 48562306a36Sopenharmony_ci * cooling devices. It also gives the opportunity to link the cooling device 48662306a36Sopenharmony_ci * with a device tree node, in order to bind it via the thermal DT code. 48762306a36Sopenharmony_ci * 48862306a36Sopenharmony_ci * Return: a valid struct thermal_cooling_device pointer on success, 48962306a36Sopenharmony_ci * on failure, it returns a corresponding ERR_PTR(). 49062306a36Sopenharmony_ci */ 49162306a36Sopenharmony_cistatic struct thermal_cooling_device * 49262306a36Sopenharmony_ci__cpufreq_cooling_register(struct device_node *np, 49362306a36Sopenharmony_ci struct cpufreq_policy *policy, 49462306a36Sopenharmony_ci struct em_perf_domain *em) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci struct thermal_cooling_device *cdev; 49762306a36Sopenharmony_ci struct cpufreq_cooling_device *cpufreq_cdev; 49862306a36Sopenharmony_ci unsigned int i; 49962306a36Sopenharmony_ci struct device *dev; 50062306a36Sopenharmony_ci int ret; 50162306a36Sopenharmony_ci struct thermal_cooling_device_ops *cooling_ops; 50262306a36Sopenharmony_ci char *name; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (IS_ERR_OR_NULL(policy)) { 50562306a36Sopenharmony_ci pr_err("%s: cpufreq policy isn't valid: %p\n", __func__, policy); 50662306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci dev = get_cpu_device(policy->cpu); 51062306a36Sopenharmony_ci if (unlikely(!dev)) { 51162306a36Sopenharmony_ci pr_warn("No cpu device for cpu %d\n", policy->cpu); 51262306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci i = cpufreq_table_count_valid_entries(policy); 51662306a36Sopenharmony_ci if (!i) { 51762306a36Sopenharmony_ci pr_debug("%s: CPUFreq table not found or has no valid entries\n", 51862306a36Sopenharmony_ci __func__); 51962306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci cpufreq_cdev = kzalloc(sizeof(*cpufreq_cdev), GFP_KERNEL); 52362306a36Sopenharmony_ci if (!cpufreq_cdev) 52462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci cpufreq_cdev->policy = policy; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci ret = allocate_idle_time(cpufreq_cdev); 52962306a36Sopenharmony_ci if (ret) { 53062306a36Sopenharmony_ci cdev = ERR_PTR(ret); 53162306a36Sopenharmony_ci goto free_cdev; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci /* max_level is an index, not a counter */ 53562306a36Sopenharmony_ci cpufreq_cdev->max_level = i - 1; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci cooling_ops = &cpufreq_cdev->cooling_ops; 53862306a36Sopenharmony_ci cooling_ops->get_max_state = cpufreq_get_max_state; 53962306a36Sopenharmony_ci cooling_ops->get_cur_state = cpufreq_get_cur_state; 54062306a36Sopenharmony_ci cooling_ops->set_cur_state = cpufreq_set_cur_state; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci#ifdef CONFIG_THERMAL_GOV_POWER_ALLOCATOR 54362306a36Sopenharmony_ci if (em_is_sane(cpufreq_cdev, em)) { 54462306a36Sopenharmony_ci cpufreq_cdev->em = em; 54562306a36Sopenharmony_ci cooling_ops->get_requested_power = cpufreq_get_requested_power; 54662306a36Sopenharmony_ci cooling_ops->state2power = cpufreq_state2power; 54762306a36Sopenharmony_ci cooling_ops->power2state = cpufreq_power2state; 54862306a36Sopenharmony_ci } else 54962306a36Sopenharmony_ci#endif 55062306a36Sopenharmony_ci if (policy->freq_table_sorted == CPUFREQ_TABLE_UNSORTED) { 55162306a36Sopenharmony_ci pr_err("%s: unsorted frequency tables are not supported\n", 55262306a36Sopenharmony_ci __func__); 55362306a36Sopenharmony_ci cdev = ERR_PTR(-EINVAL); 55462306a36Sopenharmony_ci goto free_idle_time; 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci ret = freq_qos_add_request(&policy->constraints, 55862306a36Sopenharmony_ci &cpufreq_cdev->qos_req, FREQ_QOS_MAX, 55962306a36Sopenharmony_ci get_state_freq(cpufreq_cdev, 0)); 56062306a36Sopenharmony_ci if (ret < 0) { 56162306a36Sopenharmony_ci pr_err("%s: Failed to add freq constraint (%d)\n", __func__, 56262306a36Sopenharmony_ci ret); 56362306a36Sopenharmony_ci cdev = ERR_PTR(ret); 56462306a36Sopenharmony_ci goto free_idle_time; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci cdev = ERR_PTR(-ENOMEM); 56862306a36Sopenharmony_ci name = kasprintf(GFP_KERNEL, "cpufreq-%s", dev_name(dev)); 56962306a36Sopenharmony_ci if (!name) 57062306a36Sopenharmony_ci goto remove_qos_req; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci cdev = thermal_of_cooling_device_register(np, name, cpufreq_cdev, 57362306a36Sopenharmony_ci cooling_ops); 57462306a36Sopenharmony_ci kfree(name); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci if (IS_ERR(cdev)) 57762306a36Sopenharmony_ci goto remove_qos_req; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci return cdev; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ciremove_qos_req: 58262306a36Sopenharmony_ci freq_qos_remove_request(&cpufreq_cdev->qos_req); 58362306a36Sopenharmony_cifree_idle_time: 58462306a36Sopenharmony_ci free_idle_time(cpufreq_cdev); 58562306a36Sopenharmony_cifree_cdev: 58662306a36Sopenharmony_ci kfree(cpufreq_cdev); 58762306a36Sopenharmony_ci return cdev; 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci/** 59162306a36Sopenharmony_ci * cpufreq_cooling_register - function to create cpufreq cooling device. 59262306a36Sopenharmony_ci * @policy: cpufreq policy 59362306a36Sopenharmony_ci * 59462306a36Sopenharmony_ci * This interface function registers the cpufreq cooling device with the name 59562306a36Sopenharmony_ci * "cpufreq-%s". This API can support multiple instances of cpufreq cooling 59662306a36Sopenharmony_ci * devices. 59762306a36Sopenharmony_ci * 59862306a36Sopenharmony_ci * Return: a valid struct thermal_cooling_device pointer on success, 59962306a36Sopenharmony_ci * on failure, it returns a corresponding ERR_PTR(). 60062306a36Sopenharmony_ci */ 60162306a36Sopenharmony_cistruct thermal_cooling_device * 60262306a36Sopenharmony_cicpufreq_cooling_register(struct cpufreq_policy *policy) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci return __cpufreq_cooling_register(NULL, policy, NULL); 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cpufreq_cooling_register); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci/** 60962306a36Sopenharmony_ci * of_cpufreq_cooling_register - function to create cpufreq cooling device. 61062306a36Sopenharmony_ci * @policy: cpufreq policy 61162306a36Sopenharmony_ci * 61262306a36Sopenharmony_ci * This interface function registers the cpufreq cooling device with the name 61362306a36Sopenharmony_ci * "cpufreq-%s". This API can support multiple instances of cpufreq cooling 61462306a36Sopenharmony_ci * devices. Using this API, the cpufreq cooling device will be linked to the 61562306a36Sopenharmony_ci * device tree node provided. 61662306a36Sopenharmony_ci * 61762306a36Sopenharmony_ci * Using this function, the cooling device will implement the power 61862306a36Sopenharmony_ci * extensions by using the Energy Model (if present). The cpus must have 61962306a36Sopenharmony_ci * registered their OPPs using the OPP library. 62062306a36Sopenharmony_ci * 62162306a36Sopenharmony_ci * Return: a valid struct thermal_cooling_device pointer on success, 62262306a36Sopenharmony_ci * and NULL on failure. 62362306a36Sopenharmony_ci */ 62462306a36Sopenharmony_cistruct thermal_cooling_device * 62562306a36Sopenharmony_ciof_cpufreq_cooling_register(struct cpufreq_policy *policy) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci struct device_node *np = of_get_cpu_node(policy->cpu, NULL); 62862306a36Sopenharmony_ci struct thermal_cooling_device *cdev = NULL; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci if (!np) { 63162306a36Sopenharmony_ci pr_err("cpufreq_cooling: OF node not available for cpu%d\n", 63262306a36Sopenharmony_ci policy->cpu); 63362306a36Sopenharmony_ci return NULL; 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci if (of_property_present(np, "#cooling-cells")) { 63762306a36Sopenharmony_ci struct em_perf_domain *em = em_cpu_get(policy->cpu); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci cdev = __cpufreq_cooling_register(np, policy, em); 64062306a36Sopenharmony_ci if (IS_ERR(cdev)) { 64162306a36Sopenharmony_ci pr_err("cpufreq_cooling: cpu%d failed to register as cooling device: %ld\n", 64262306a36Sopenharmony_ci policy->cpu, PTR_ERR(cdev)); 64362306a36Sopenharmony_ci cdev = NULL; 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci of_node_put(np); 64862306a36Sopenharmony_ci return cdev; 64962306a36Sopenharmony_ci} 65062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_cpufreq_cooling_register); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci/** 65362306a36Sopenharmony_ci * cpufreq_cooling_unregister - function to remove cpufreq cooling device. 65462306a36Sopenharmony_ci * @cdev: thermal cooling device pointer. 65562306a36Sopenharmony_ci * 65662306a36Sopenharmony_ci * This interface function unregisters the "cpufreq-%x" cooling device. 65762306a36Sopenharmony_ci */ 65862306a36Sopenharmony_civoid cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci struct cpufreq_cooling_device *cpufreq_cdev; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci if (!cdev) 66362306a36Sopenharmony_ci return; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci cpufreq_cdev = cdev->devdata; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci thermal_cooling_device_unregister(cdev); 66862306a36Sopenharmony_ci freq_qos_remove_request(&cpufreq_cdev->qos_req); 66962306a36Sopenharmony_ci free_idle_time(cpufreq_cdev); 67062306a36Sopenharmony_ci kfree(cpufreq_cdev); 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cpufreq_cooling_unregister); 673