0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0012
0013 #include <linux/cpu.h>
0014 #include <linux/cpufreq.h>
0015 #include <linux/err.h>
0016 #include <linux/errno.h>
0017 #include <linux/export.h>
0018 #include <linux/slab.h>
0019
0020 #include "opp.h"
0021
0022 #ifdef CONFIG_CPU_FREQ
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043 int dev_pm_opp_init_cpufreq_table(struct device *dev,
0044 struct cpufreq_frequency_table **opp_table)
0045 {
0046 struct dev_pm_opp *opp;
0047 struct cpufreq_frequency_table *freq_table = NULL;
0048 int i, max_opps, ret = 0;
0049 unsigned long rate;
0050
0051 max_opps = dev_pm_opp_get_opp_count(dev);
0052 if (max_opps <= 0)
0053 return max_opps ? max_opps : -ENODATA;
0054
0055 freq_table = kcalloc((max_opps + 1), sizeof(*freq_table), GFP_KERNEL);
0056 if (!freq_table)
0057 return -ENOMEM;
0058
0059 for (i = 0, rate = 0; i < max_opps; i++, rate++) {
0060
0061 opp = dev_pm_opp_find_freq_ceil(dev, &rate);
0062 if (IS_ERR(opp)) {
0063 ret = PTR_ERR(opp);
0064 goto out;
0065 }
0066 freq_table[i].driver_data = i;
0067 freq_table[i].frequency = rate / 1000;
0068
0069
0070 if (dev_pm_opp_is_turbo(opp))
0071 freq_table[i].flags = CPUFREQ_BOOST_FREQ;
0072
0073 dev_pm_opp_put(opp);
0074 }
0075
0076 freq_table[i].driver_data = i;
0077 freq_table[i].frequency = CPUFREQ_TABLE_END;
0078
0079 *opp_table = &freq_table[0];
0080
0081 out:
0082 if (ret)
0083 kfree(freq_table);
0084
0085 return ret;
0086 }
0087 EXPORT_SYMBOL_GPL(dev_pm_opp_init_cpufreq_table);
0088
0089
0090
0091
0092
0093
0094
0095
0096 void dev_pm_opp_free_cpufreq_table(struct device *dev,
0097 struct cpufreq_frequency_table **opp_table)
0098 {
0099 if (!opp_table)
0100 return;
0101
0102 kfree(*opp_table);
0103 *opp_table = NULL;
0104 }
0105 EXPORT_SYMBOL_GPL(dev_pm_opp_free_cpufreq_table);
0106 #endif
0107
0108 void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask,
0109 int last_cpu)
0110 {
0111 struct device *cpu_dev;
0112 int cpu;
0113
0114 WARN_ON(cpumask_empty(cpumask));
0115
0116 for_each_cpu(cpu, cpumask) {
0117 if (cpu == last_cpu)
0118 break;
0119
0120 cpu_dev = get_cpu_device(cpu);
0121 if (!cpu_dev) {
0122 pr_err("%s: failed to get cpu%d device\n", __func__,
0123 cpu);
0124 continue;
0125 }
0126
0127 dev_pm_opp_remove_table(cpu_dev);
0128 }
0129 }
0130
0131
0132
0133
0134
0135
0136
0137
0138
0139 void dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask)
0140 {
0141 _dev_pm_opp_cpumask_remove_table(cpumask, -1);
0142 }
0143 EXPORT_SYMBOL_GPL(dev_pm_opp_cpumask_remove_table);
0144
0145
0146
0147
0148
0149
0150
0151
0152
0153
0154
0155 int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev,
0156 const struct cpumask *cpumask)
0157 {
0158 struct opp_device *opp_dev;
0159 struct opp_table *opp_table;
0160 struct device *dev;
0161 int cpu, ret = 0;
0162
0163 opp_table = _find_opp_table(cpu_dev);
0164 if (IS_ERR(opp_table))
0165 return PTR_ERR(opp_table);
0166
0167 for_each_cpu(cpu, cpumask) {
0168 if (cpu == cpu_dev->id)
0169 continue;
0170
0171 dev = get_cpu_device(cpu);
0172 if (!dev) {
0173 dev_err(cpu_dev, "%s: failed to get cpu%d device\n",
0174 __func__, cpu);
0175 continue;
0176 }
0177
0178 opp_dev = _add_opp_dev(dev, opp_table);
0179 if (!opp_dev) {
0180 dev_err(dev, "%s: failed to add opp-dev for cpu%d device\n",
0181 __func__, cpu);
0182 continue;
0183 }
0184
0185
0186 opp_table->shared_opp = OPP_TABLE_ACCESS_SHARED;
0187 }
0188
0189 dev_pm_opp_put_opp_table(opp_table);
0190
0191 return ret;
0192 }
0193 EXPORT_SYMBOL_GPL(dev_pm_opp_set_sharing_cpus);
0194
0195
0196
0197
0198
0199
0200
0201
0202
0203
0204
0205 int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask)
0206 {
0207 struct opp_device *opp_dev;
0208 struct opp_table *opp_table;
0209 int ret = 0;
0210
0211 opp_table = _find_opp_table(cpu_dev);
0212 if (IS_ERR(opp_table))
0213 return PTR_ERR(opp_table);
0214
0215 if (opp_table->shared_opp == OPP_TABLE_ACCESS_UNKNOWN) {
0216 ret = -EINVAL;
0217 goto put_opp_table;
0218 }
0219
0220 cpumask_clear(cpumask);
0221
0222 if (opp_table->shared_opp == OPP_TABLE_ACCESS_SHARED) {
0223 mutex_lock(&opp_table->lock);
0224 list_for_each_entry(opp_dev, &opp_table->dev_list, node)
0225 cpumask_set_cpu(opp_dev->dev->id, cpumask);
0226 mutex_unlock(&opp_table->lock);
0227 } else {
0228 cpumask_set_cpu(cpu_dev->id, cpumask);
0229 }
0230
0231 put_opp_table:
0232 dev_pm_opp_put_opp_table(opp_table);
0233
0234 return ret;
0235 }
0236 EXPORT_SYMBOL_GPL(dev_pm_opp_get_sharing_cpus);