| .. | .. |
|---|
| 8 | 8 | |
|---|
| 9 | 9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
|---|
| 10 | 10 | |
|---|
| 11 | +#include <linux/clk-provider.h> |
|---|
| 11 | 12 | #include <linux/cpu.h> |
|---|
| 12 | 13 | #include <linux/cpufreq.h> |
|---|
| 13 | 14 | #include <linux/cpumask.h> |
|---|
| 14 | | -#include <linux/cpu_cooling.h> |
|---|
| 15 | 15 | #include <linux/energy_model.h> |
|---|
| 16 | 16 | #include <linux/export.h> |
|---|
| 17 | 17 | #include <linux/module.h> |
|---|
| .. | .. |
|---|
| 23 | 23 | struct scmi_data { |
|---|
| 24 | 24 | int domain_id; |
|---|
| 25 | 25 | struct device *cpu_dev; |
|---|
| 26 | | - struct thermal_cooling_device *cdev; |
|---|
| 27 | 26 | }; |
|---|
| 28 | 27 | |
|---|
| 29 | | -static const struct scmi_handle *handle; |
|---|
| 28 | +static struct scmi_protocol_handle *ph; |
|---|
| 29 | +static const struct scmi_perf_proto_ops *perf_ops; |
|---|
| 30 | 30 | |
|---|
| 31 | 31 | static unsigned int scmi_cpufreq_get_rate(unsigned int cpu) |
|---|
| 32 | 32 | { |
|---|
| 33 | 33 | struct cpufreq_policy *policy = cpufreq_cpu_get_raw(cpu); |
|---|
| 34 | | - struct scmi_perf_ops *perf_ops = handle->perf_ops; |
|---|
| 35 | 34 | struct scmi_data *priv = policy->driver_data; |
|---|
| 36 | 35 | unsigned long rate; |
|---|
| 37 | 36 | int ret; |
|---|
| 38 | 37 | |
|---|
| 39 | | - ret = perf_ops->freq_get(handle, priv->domain_id, &rate, false); |
|---|
| 38 | + ret = perf_ops->freq_get(ph, priv->domain_id, &rate, false); |
|---|
| 40 | 39 | if (ret) |
|---|
| 41 | 40 | return 0; |
|---|
| 42 | 41 | return rate / 1000; |
|---|
| .. | .. |
|---|
| 50 | 49 | static int |
|---|
| 51 | 50 | scmi_cpufreq_set_target(struct cpufreq_policy *policy, unsigned int index) |
|---|
| 52 | 51 | { |
|---|
| 53 | | - int ret; |
|---|
| 54 | 52 | struct scmi_data *priv = policy->driver_data; |
|---|
| 55 | | - struct scmi_perf_ops *perf_ops = handle->perf_ops; |
|---|
| 56 | 53 | u64 freq = policy->freq_table[index].frequency; |
|---|
| 57 | 54 | |
|---|
| 58 | | - ret = perf_ops->freq_set(handle, priv->domain_id, freq * 1000, false); |
|---|
| 59 | | - if (!ret) |
|---|
| 60 | | - arch_set_freq_scale(policy->related_cpus, freq, |
|---|
| 61 | | - policy->cpuinfo.max_freq); |
|---|
| 62 | | - return ret; |
|---|
| 55 | + return perf_ops->freq_set(ph, priv->domain_id, freq * 1000, false); |
|---|
| 63 | 56 | } |
|---|
| 64 | 57 | |
|---|
| 65 | 58 | static unsigned int scmi_cpufreq_fast_switch(struct cpufreq_policy *policy, |
|---|
| 66 | 59 | unsigned int target_freq) |
|---|
| 67 | 60 | { |
|---|
| 68 | 61 | struct scmi_data *priv = policy->driver_data; |
|---|
| 69 | | - struct scmi_perf_ops *perf_ops = handle->perf_ops; |
|---|
| 70 | 62 | |
|---|
| 71 | | - if (!perf_ops->freq_set(handle, priv->domain_id, |
|---|
| 72 | | - target_freq * 1000, true)) { |
|---|
| 73 | | - arch_set_freq_scale(policy->related_cpus, target_freq, |
|---|
| 74 | | - policy->cpuinfo.max_freq); |
|---|
| 63 | + if (!perf_ops->freq_set(ph, priv->domain_id, |
|---|
| 64 | + target_freq * 1000, true)) |
|---|
| 75 | 65 | return target_freq; |
|---|
| 76 | | - } |
|---|
| 77 | 66 | |
|---|
| 78 | 67 | return 0; |
|---|
| 79 | 68 | } |
|---|
| .. | .. |
|---|
| 84 | 73 | int cpu, domain, tdomain; |
|---|
| 85 | 74 | struct device *tcpu_dev; |
|---|
| 86 | 75 | |
|---|
| 87 | | - domain = handle->perf_ops->device_domain_id(cpu_dev); |
|---|
| 76 | + domain = perf_ops->device_domain_id(cpu_dev); |
|---|
| 88 | 77 | if (domain < 0) |
|---|
| 89 | 78 | return domain; |
|---|
| 90 | 79 | |
|---|
| .. | .. |
|---|
| 96 | 85 | if (!tcpu_dev) |
|---|
| 97 | 86 | continue; |
|---|
| 98 | 87 | |
|---|
| 99 | | - tdomain = handle->perf_ops->device_domain_id(tcpu_dev); |
|---|
| 88 | + tdomain = perf_ops->device_domain_id(tcpu_dev); |
|---|
| 100 | 89 | if (tdomain == domain) |
|---|
| 101 | 90 | cpumask_set_cpu(cpu, cpumask); |
|---|
| 102 | 91 | } |
|---|
| .. | .. |
|---|
| 105 | 94 | } |
|---|
| 106 | 95 | |
|---|
| 107 | 96 | static int __maybe_unused |
|---|
| 108 | | -scmi_get_cpu_power(unsigned long *power, unsigned long *KHz, int cpu) |
|---|
| 97 | +scmi_get_cpu_power(unsigned long *power, unsigned long *KHz, |
|---|
| 98 | + struct device *cpu_dev) |
|---|
| 109 | 99 | { |
|---|
| 110 | | - struct device *cpu_dev = get_cpu_device(cpu); |
|---|
| 111 | 100 | unsigned long Hz; |
|---|
| 112 | 101 | int ret, domain; |
|---|
| 113 | 102 | |
|---|
| 114 | | - if (!cpu_dev) { |
|---|
| 115 | | - pr_err("failed to get cpu%d device\n", cpu); |
|---|
| 116 | | - return -ENODEV; |
|---|
| 117 | | - } |
|---|
| 118 | | - |
|---|
| 119 | | - domain = handle->perf_ops->device_domain_id(cpu_dev); |
|---|
| 103 | + domain = perf_ops->device_domain_id(cpu_dev); |
|---|
| 120 | 104 | if (domain < 0) |
|---|
| 121 | 105 | return domain; |
|---|
| 122 | 106 | |
|---|
| 123 | 107 | /* Get the power cost of the performance domain. */ |
|---|
| 124 | 108 | Hz = *KHz * 1000; |
|---|
| 125 | | - ret = handle->perf_ops->est_power_get(handle, domain, &Hz, power); |
|---|
| 109 | + ret = perf_ops->est_power_get(ph, domain, &Hz, power); |
|---|
| 126 | 110 | if (ret) |
|---|
| 127 | 111 | return ret; |
|---|
| 128 | 112 | |
|---|
| .. | .. |
|---|
| 140 | 124 | struct scmi_data *priv; |
|---|
| 141 | 125 | struct cpufreq_frequency_table *freq_table; |
|---|
| 142 | 126 | struct em_data_callback em_cb = EM_DATA_CB(scmi_get_cpu_power); |
|---|
| 127 | + bool power_scale_mw; |
|---|
| 143 | 128 | |
|---|
| 144 | 129 | cpu_dev = get_cpu_device(policy->cpu); |
|---|
| 145 | 130 | if (!cpu_dev) { |
|---|
| .. | .. |
|---|
| 147 | 132 | return -ENODEV; |
|---|
| 148 | 133 | } |
|---|
| 149 | 134 | |
|---|
| 150 | | - ret = handle->perf_ops->device_opps_add(handle, cpu_dev); |
|---|
| 135 | + ret = perf_ops->device_opps_add(ph, cpu_dev); |
|---|
| 151 | 136 | if (ret) { |
|---|
| 152 | 137 | dev_warn(cpu_dev, "failed to add opps to the device\n"); |
|---|
| 153 | 138 | return ret; |
|---|
| .. | .. |
|---|
| 166 | 151 | return ret; |
|---|
| 167 | 152 | } |
|---|
| 168 | 153 | |
|---|
| 169 | | - ret = dev_pm_opp_get_opp_count(cpu_dev); |
|---|
| 170 | | - if (ret <= 0) { |
|---|
| 154 | + nr_opp = dev_pm_opp_get_opp_count(cpu_dev); |
|---|
| 155 | + if (nr_opp <= 0) { |
|---|
| 171 | 156 | dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n"); |
|---|
| 172 | 157 | ret = -EPROBE_DEFER; |
|---|
| 173 | 158 | goto out_free_opp; |
|---|
| 174 | 159 | } |
|---|
| 175 | | - nr_opp = ret; |
|---|
| 176 | 160 | |
|---|
| 177 | 161 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); |
|---|
| 178 | 162 | if (!priv) { |
|---|
| .. | .. |
|---|
| 187 | 171 | } |
|---|
| 188 | 172 | |
|---|
| 189 | 173 | priv->cpu_dev = cpu_dev; |
|---|
| 190 | | - priv->domain_id = handle->perf_ops->device_domain_id(cpu_dev); |
|---|
| 174 | + priv->domain_id = perf_ops->device_domain_id(cpu_dev); |
|---|
| 191 | 175 | |
|---|
| 192 | 176 | policy->driver_data = priv; |
|---|
| 193 | 177 | policy->freq_table = freq_table; |
|---|
| .. | .. |
|---|
| 195 | 179 | /* SCMI allows DVFS request for any domain from any CPU */ |
|---|
| 196 | 180 | policy->dvfs_possible_from_any_cpu = true; |
|---|
| 197 | 181 | |
|---|
| 198 | | - latency = handle->perf_ops->transition_latency_get(handle, cpu_dev); |
|---|
| 182 | + latency = perf_ops->transition_latency_get(ph, cpu_dev); |
|---|
| 199 | 183 | if (!latency) |
|---|
| 200 | 184 | latency = CPUFREQ_ETERNAL; |
|---|
| 201 | 185 | |
|---|
| 202 | 186 | policy->cpuinfo.transition_latency = latency; |
|---|
| 203 | 187 | |
|---|
| 204 | | - policy->fast_switch_possible = true; |
|---|
| 188 | + policy->fast_switch_possible = |
|---|
| 189 | + perf_ops->fast_switch_possible(ph, cpu_dev); |
|---|
| 205 | 190 | |
|---|
| 206 | | - em_register_perf_domain(policy->cpus, nr_opp, &em_cb); |
|---|
| 191 | + power_scale_mw = perf_ops->power_scale_mw_get(ph); |
|---|
| 192 | + em_dev_register_perf_domain(cpu_dev, nr_opp, &em_cb, policy->cpus, |
|---|
| 193 | + power_scale_mw); |
|---|
| 207 | 194 | |
|---|
| 208 | 195 | return 0; |
|---|
| 209 | 196 | |
|---|
| 210 | 197 | out_free_priv: |
|---|
| 211 | 198 | kfree(priv); |
|---|
| 212 | 199 | out_free_opp: |
|---|
| 213 | | - dev_pm_opp_cpumask_remove_table(policy->cpus); |
|---|
| 200 | + dev_pm_opp_remove_all_dynamic(cpu_dev); |
|---|
| 214 | 201 | |
|---|
| 215 | 202 | return ret; |
|---|
| 216 | 203 | } |
|---|
| .. | .. |
|---|
| 219 | 206 | { |
|---|
| 220 | 207 | struct scmi_data *priv = policy->driver_data; |
|---|
| 221 | 208 | |
|---|
| 222 | | - cpufreq_cooling_unregister(priv->cdev); |
|---|
| 223 | 209 | dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table); |
|---|
| 210 | + dev_pm_opp_remove_all_dynamic(priv->cpu_dev); |
|---|
| 224 | 211 | kfree(priv); |
|---|
| 225 | | - dev_pm_opp_cpumask_remove_table(policy->related_cpus); |
|---|
| 226 | 212 | |
|---|
| 227 | 213 | return 0; |
|---|
| 228 | | -} |
|---|
| 229 | | - |
|---|
| 230 | | -static void scmi_cpufreq_ready(struct cpufreq_policy *policy) |
|---|
| 231 | | -{ |
|---|
| 232 | | - struct scmi_data *priv = policy->driver_data; |
|---|
| 233 | | - |
|---|
| 234 | | - priv->cdev = of_cpufreq_cooling_register(policy); |
|---|
| 235 | 214 | } |
|---|
| 236 | 215 | |
|---|
| 237 | 216 | static struct cpufreq_driver scmi_cpufreq_driver = { |
|---|
| 238 | 217 | .name = "scmi", |
|---|
| 239 | 218 | .flags = CPUFREQ_STICKY | CPUFREQ_HAVE_GOVERNOR_PER_POLICY | |
|---|
| 240 | | - CPUFREQ_NEED_INITIAL_FREQ_CHECK, |
|---|
| 219 | + CPUFREQ_NEED_INITIAL_FREQ_CHECK | |
|---|
| 220 | + CPUFREQ_IS_COOLING_DEV, |
|---|
| 241 | 221 | .verify = cpufreq_generic_frequency_table_verify, |
|---|
| 242 | 222 | .attr = cpufreq_generic_attr, |
|---|
| 243 | 223 | .target_index = scmi_cpufreq_set_target, |
|---|
| .. | .. |
|---|
| 245 | 225 | .get = scmi_cpufreq_get_rate, |
|---|
| 246 | 226 | .init = scmi_cpufreq_init, |
|---|
| 247 | 227 | .exit = scmi_cpufreq_exit, |
|---|
| 248 | | - .ready = scmi_cpufreq_ready, |
|---|
| 249 | 228 | }; |
|---|
| 250 | 229 | |
|---|
| 251 | 230 | static int scmi_cpufreq_probe(struct scmi_device *sdev) |
|---|
| 252 | 231 | { |
|---|
| 253 | 232 | int ret; |
|---|
| 233 | + struct device *dev = &sdev->dev; |
|---|
| 234 | + const struct scmi_handle *handle; |
|---|
| 254 | 235 | |
|---|
| 255 | 236 | handle = sdev->handle; |
|---|
| 256 | 237 | |
|---|
| 257 | | - if (!handle || !handle->perf_ops) |
|---|
| 238 | + if (!handle) |
|---|
| 258 | 239 | return -ENODEV; |
|---|
| 240 | + |
|---|
| 241 | + perf_ops = handle->devm_get_protocol(sdev, SCMI_PROTOCOL_PERF, &ph); |
|---|
| 242 | + if (IS_ERR(perf_ops)) |
|---|
| 243 | + return PTR_ERR(perf_ops); |
|---|
| 244 | + |
|---|
| 245 | +#ifdef CONFIG_COMMON_CLK |
|---|
| 246 | + /* dummy clock provider as needed by OPP if clocks property is used */ |
|---|
| 247 | + if (of_find_property(dev->of_node, "#clock-cells", NULL)) |
|---|
| 248 | + devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, NULL); |
|---|
| 249 | +#endif |
|---|
| 259 | 250 | |
|---|
| 260 | 251 | ret = cpufreq_register_driver(&scmi_cpufreq_driver); |
|---|
| 261 | 252 | if (ret) { |
|---|
| 262 | | - dev_err(&sdev->dev, "%s: registering cpufreq failed, err: %d\n", |
|---|
| 253 | + dev_err(dev, "%s: registering cpufreq failed, err: %d\n", |
|---|
| 263 | 254 | __func__, ret); |
|---|
| 264 | 255 | } |
|---|
| 265 | 256 | |
|---|
| .. | .. |
|---|
| 272 | 263 | } |
|---|
| 273 | 264 | |
|---|
| 274 | 265 | static const struct scmi_device_id scmi_id_table[] = { |
|---|
| 275 | | - { SCMI_PROTOCOL_PERF }, |
|---|
| 266 | + { SCMI_PROTOCOL_PERF, "cpufreq" }, |
|---|
| 276 | 267 | { }, |
|---|
| 277 | 268 | }; |
|---|
| 278 | 269 | MODULE_DEVICE_TABLE(scmi, scmi_id_table); |
|---|