diff options
-rw-r--r-- | drivers/thermal/cpu_cooling.c | 153 |
1 files changed, 67 insertions, 86 deletions
diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 94e7121817bf..67ec52d5f7fc 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -49,14 +49,14 @@ */ /** - * struct power_table - frequency to power conversion + * struct freq_table - frequency table along with power entries * @frequency: frequency in KHz * @power: power in mW * * This structure is built when the cooling device registers and helps - * in translating frequency to power and viceversa. + * in translating frequency to power and vice versa. */ -struct power_table { +struct freq_table { u32 frequency; u32 power; }; @@ -79,9 +79,6 @@ struct power_table { * @time_in_idle: previous reading of the absolute time that this cpu was idle * @time_in_idle_timestamp: wall time of the last invocation of * get_cpu_idle_time_us() - * @dyn_power_table: array of struct power_table for frequency to power - * conversion, sorted in ascending order. - * @dyn_power_table_entries: number of entries in the @dyn_power_table array * @cpu_dev: the cpu_device of policy->cpu. * @plat_get_static_power: callback to calculate the static power * @@ -95,13 +92,11 @@ struct cpufreq_cooling_device { unsigned int cpufreq_state; unsigned int clipped_freq; unsigned int max_level; - unsigned int *freq_table; /* In descending order */ + struct freq_table *freq_table; /* In descending order */ struct list_head node; u32 last_load; u64 *time_in_idle; u64 *time_in_idle_timestamp; - struct power_table *dyn_power_table; - int dyn_power_table_entries; struct device *cpu_dev; get_static_t plat_get_static_power; }; @@ -125,10 +120,10 @@ static unsigned long get_level(struct cpufreq_cooling_device *cpufreq_cdev, unsigned long level; for (level = 0; level <= cpufreq_cdev->max_level; level++) { - if (freq == cpufreq_cdev->freq_table[level]) + if (freq == cpufreq_cdev->freq_table[level].frequency) return level; - if (freq > cpufreq_cdev->freq_table[level]) + if (freq > cpufreq_cdev->freq_table[level].frequency) break; } @@ -189,28 +184,25 @@ static int cpufreq_thermal_notifier(struct notifier_block *nb, } /** - * build_dyn_power_table() - create a dynamic power to frequency table - * @cpufreq_cdev: the cpufreq cooling device in which to store the table + * update_freq_table() - Update the freq table with power numbers + * @cpufreq_cdev: the cpufreq cooling device in which to update the table * @capacitance: dynamic power coefficient for these cpus * - * Build a dynamic power to frequency table for this cpu and store it - * in @cpufreq_cdev. This table will be used in cpu_power_to_freq() and - * cpu_freq_to_power() to convert between power and frequency - * efficiently. Power is stored in mW, frequency in KHz. The - * resulting table is in ascending order. + * Update the freq table with power numbers. This table will be used in + * cpu_power_to_freq() and cpu_freq_to_power() to convert between power and + * frequency efficiently. Power is stored in mW, frequency in KHz. The + * resulting table is in descending order. * * Return: 0 on success, -EINVAL if there are no OPPs for any CPUs, - * -ENOMEM if we run out of memory or -EAGAIN if an OPP was - * added/enabled while the function was executing. + * or -ENOMEM if we run out of memory. */ -static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_cdev, - u32 capacitance) +static int update_freq_table(struct cpufreq_cooling_device *cpufreq_cdev, + u32 capacitance) { - struct power_table *power_table; + struct freq_table *freq_table = cpufreq_cdev->freq_table; struct dev_pm_opp *opp; struct device *dev = NULL; - int num_opps = 0, cpu = cpufreq_cdev->policy->cpu, i, ret = 0; - unsigned long freq; + int num_opps = 0, cpu = cpufreq_cdev->policy->cpu, i; dev = get_cpu_device(cpu); if (unlikely(!dev)) { @@ -223,25 +215,32 @@ static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_cdev, if (num_opps < 0) return num_opps; - if (num_opps == 0) + /* + * The cpufreq table is also built from the OPP table and so the count + * should match. + */ + if (num_opps != cpufreq_cdev->max_level + 1) { + dev_warn(dev, "Number of OPPs not matching with max_levels\n"); return -EINVAL; + } - power_table = kcalloc(num_opps, sizeof(*power_table), GFP_KERNEL); - if (!power_table) - return -ENOMEM; - - for (freq = 0, i = 0; - opp = dev_pm_opp_find_freq_ceil(dev, &freq), !IS_ERR(opp); - freq++, i++) { - u32 freq_mhz, voltage_mv; + for (i = 0; i <= cpufreq_cdev->max_level; i++) { + unsigned long freq = freq_table[i].frequency * 1000; + u32 freq_mhz = freq_table[i].frequency / 1000; u64 power; + u32 voltage_mv; - if (i >= num_opps) { - ret = -EAGAIN; - goto free_power_table; + /* + * Find ceil frequency as 'freq' may be slightly lower than OPP + * freq due to truncation while converting to kHz. + */ + opp = dev_pm_opp_find_freq_ceil(dev, &freq); + if (IS_ERR(opp)) { + dev_err(dev, "failed to get opp for %lu frequency\n", + freq); + return -EINVAL; } - freq_mhz = freq / 1000000; voltage_mv = dev_pm_opp_get_voltage(opp) / 1000; dev_pm_opp_put(opp); @@ -252,54 +251,39 @@ static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_cdev, power = (u64)capacitance * freq_mhz * voltage_mv * voltage_mv; do_div(power, 1000000000); - /* frequency is stored in power_table in KHz */ - power_table[i].frequency = freq / 1000; - /* power is stored in mW */ - power_table[i].power = power; - } - - if (i != num_opps) { - ret = PTR_ERR(opp); - goto free_power_table; + freq_table[i].power = power; } cpufreq_cdev->cpu_dev = dev; - cpufreq_cdev->dyn_power_table = power_table; - cpufreq_cdev->dyn_power_table_entries = i; return 0; - -free_power_table: - kfree(power_table); - - return ret; } static u32 cpu_freq_to_power(struct cpufreq_cooling_device *cpufreq_cdev, u32 freq) { int i; - struct power_table *pt = cpufreq_cdev->dyn_power_table; + struct freq_table *freq_table = cpufreq_cdev->freq_table; - for (i = 1; i < cpufreq_cdev->dyn_power_table_entries; i++) - if (freq < pt[i].frequency) + for (i = 1; i <= cpufreq_cdev->max_level; i++) + if (freq > freq_table[i].frequency) break; - return pt[i - 1].power; + return freq_table[i - 1].power; } static u32 cpu_power_to_freq(struct cpufreq_cooling_device *cpufreq_cdev, u32 power) { int i; - struct power_table *pt = cpufreq_cdev->dyn_power_table; + struct freq_table *freq_table = cpufreq_cdev->freq_table; - for (i = 1; i < cpufreq_cdev->dyn_power_table_entries; i++) - if (power < pt[i].power) + for (i = 1; i <= cpufreq_cdev->max_level; i++) + if (power > freq_table[i].power) break; - return pt[i - 1].frequency; + return freq_table[i - 1].frequency; } /** @@ -466,7 +450,7 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev, if (cpufreq_cdev->cpufreq_state == state) return 0; - clip_freq = cpufreq_cdev->freq_table[state]; + clip_freq = cpufreq_cdev->freq_table[state].frequency; cpufreq_cdev->cpufreq_state = state; cpufreq_cdev->clipped_freq = clip_freq; @@ -579,7 +563,7 @@ static int cpufreq_state2power(struct thermal_cooling_device *cdev, num_cpus = cpumask_weight(cpufreq_cdev->policy->cpus); - freq = cpufreq_cdev->freq_table[state]; + freq = cpufreq_cdev->freq_table[state].frequency; if (!freq) return -EINVAL; @@ -757,31 +741,20 @@ __cpufreq_cooling_register(struct device_node *np, goto free_time_in_idle_timestamp; } - if (capacitance) { - cpufreq_cdev->plat_get_static_power = plat_static_func; - - ret = build_dyn_power_table(cpufreq_cdev, capacitance); - if (ret) { - cdev = ERR_PTR(ret); - goto free_table; - } - - cooling_ops = &cpufreq_power_cooling_ops; - } else { - cooling_ops = &cpufreq_cooling_ops; - } - ret = ida_simple_get(&cpufreq_ida, 0, 0, GFP_KERNEL); if (ret < 0) { cdev = ERR_PTR(ret); - goto free_power_table; + goto free_table; } cpufreq_cdev->id = ret; + snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d", + cpufreq_cdev->id); + /* Fill freq-table in descending order of frequencies */ for (i = 0, freq = -1; i <= cpufreq_cdev->max_level; i++) { freq = find_next_max(policy->freq_table, freq); - cpufreq_cdev->freq_table[i] = freq; + cpufreq_cdev->freq_table[i].frequency = freq; /* Warn for duplicate entries */ if (!freq) @@ -790,15 +763,26 @@ __cpufreq_cooling_register(struct device_node *np, pr_debug("%s: freq:%u KHz\n", __func__, freq); } - snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d", - cpufreq_cdev->id); + if (capacitance) { + cpufreq_cdev->plat_get_static_power = plat_static_func; + + ret = update_freq_table(cpufreq_cdev, capacitance); + if (ret) { + cdev = ERR_PTR(ret); + goto remove_ida; + } + + cooling_ops = &cpufreq_power_cooling_ops; + } else { + cooling_ops = &cpufreq_cooling_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]; + cpufreq_cdev->clipped_freq = cpufreq_cdev->freq_table[0].frequency; cpufreq_cdev->cdev = cdev; mutex_lock(&cooling_list_lock); @@ -815,8 +799,6 @@ __cpufreq_cooling_register(struct device_node *np, remove_ida: ida_simple_remove(&cpufreq_ida, cpufreq_cdev->id); -free_power_table: - kfree(cpufreq_cdev->dyn_power_table); free_table: kfree(cpufreq_cdev->freq_table); free_time_in_idle_timestamp: @@ -965,7 +947,6 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) thermal_cooling_device_unregister(cpufreq_cdev->cdev); ida_simple_remove(&cpufreq_ida, cpufreq_cdev->id); - kfree(cpufreq_cdev->dyn_power_table); kfree(cpufreq_cdev->time_in_idle_timestamp); kfree(cpufreq_cdev->time_in_idle); kfree(cpufreq_cdev->freq_table); |