199d6bdf3SSudeep Holla // SPDX-License-Identifier: GPL-2.0 299d6bdf3SSudeep Holla /* 399d6bdf3SSudeep Holla * System Control and Power Interface (SCMI) based CPUFreq Interface driver 499d6bdf3SSudeep Holla * 599d6bdf3SSudeep Holla * Copyright (C) 2018 ARM Ltd. 699d6bdf3SSudeep Holla * Sudeep Holla <sudeep.holla@arm.com> 799d6bdf3SSudeep Holla */ 899d6bdf3SSudeep Holla 999d6bdf3SSudeep Holla #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1099d6bdf3SSudeep Holla 1199d6bdf3SSudeep Holla #include <linux/cpu.h> 1299d6bdf3SSudeep Holla #include <linux/cpufreq.h> 1399d6bdf3SSudeep Holla #include <linux/cpumask.h> 1499d6bdf3SSudeep Holla #include <linux/cpu_cooling.h> 1599d6bdf3SSudeep Holla #include <linux/export.h> 1699d6bdf3SSudeep Holla #include <linux/module.h> 1799d6bdf3SSudeep Holla #include <linux/pm_opp.h> 1899d6bdf3SSudeep Holla #include <linux/slab.h> 1999d6bdf3SSudeep Holla #include <linux/scmi_protocol.h> 2099d6bdf3SSudeep Holla #include <linux/types.h> 2199d6bdf3SSudeep Holla 2299d6bdf3SSudeep Holla struct scmi_data { 2399d6bdf3SSudeep Holla int domain_id; 2499d6bdf3SSudeep Holla struct device *cpu_dev; 2599d6bdf3SSudeep Holla struct thermal_cooling_device *cdev; 2699d6bdf3SSudeep Holla }; 2799d6bdf3SSudeep Holla 2899d6bdf3SSudeep Holla static const struct scmi_handle *handle; 2999d6bdf3SSudeep Holla 3099d6bdf3SSudeep Holla static unsigned int scmi_cpufreq_get_rate(unsigned int cpu) 3199d6bdf3SSudeep Holla { 3299d6bdf3SSudeep Holla struct cpufreq_policy *policy = cpufreq_cpu_get_raw(cpu); 3399d6bdf3SSudeep Holla struct scmi_perf_ops *perf_ops = handle->perf_ops; 3499d6bdf3SSudeep Holla struct scmi_data *priv = policy->driver_data; 3599d6bdf3SSudeep Holla unsigned long rate; 3699d6bdf3SSudeep Holla int ret; 3799d6bdf3SSudeep Holla 3899d6bdf3SSudeep Holla ret = perf_ops->freq_get(handle, priv->domain_id, &rate, false); 3999d6bdf3SSudeep Holla if (ret) 4099d6bdf3SSudeep Holla return 0; 4199d6bdf3SSudeep Holla return rate / 1000; 4299d6bdf3SSudeep Holla } 4399d6bdf3SSudeep Holla 4499d6bdf3SSudeep Holla /* 4599d6bdf3SSudeep Holla * perf_ops->freq_set is not a synchronous, the actual OPP change will 4699d6bdf3SSudeep Holla * happen asynchronously and can get notified if the events are 4799d6bdf3SSudeep Holla * subscribed for by the SCMI firmware 4899d6bdf3SSudeep Holla */ 4999d6bdf3SSudeep Holla static int 5099d6bdf3SSudeep Holla scmi_cpufreq_set_target(struct cpufreq_policy *policy, unsigned int index) 5199d6bdf3SSudeep Holla { 5299d6bdf3SSudeep Holla int ret; 5399d6bdf3SSudeep Holla struct scmi_data *priv = policy->driver_data; 5499d6bdf3SSudeep Holla struct scmi_perf_ops *perf_ops = handle->perf_ops; 5599d6bdf3SSudeep Holla u64 freq = policy->freq_table[index].frequency * 1000; 5699d6bdf3SSudeep Holla 5799d6bdf3SSudeep Holla ret = perf_ops->freq_set(handle, priv->domain_id, freq, false); 5899d6bdf3SSudeep Holla if (!ret) 5999d6bdf3SSudeep Holla arch_set_freq_scale(policy->related_cpus, freq, 6099d6bdf3SSudeep Holla policy->cpuinfo.max_freq); 6199d6bdf3SSudeep Holla return ret; 6299d6bdf3SSudeep Holla } 6399d6bdf3SSudeep Holla 6402f208c5SSudeep Holla static unsigned int scmi_cpufreq_fast_switch(struct cpufreq_policy *policy, 6502f208c5SSudeep Holla unsigned int target_freq) 6602f208c5SSudeep Holla { 6702f208c5SSudeep Holla struct scmi_data *priv = policy->driver_data; 6802f208c5SSudeep Holla struct scmi_perf_ops *perf_ops = handle->perf_ops; 6902f208c5SSudeep Holla 7002f208c5SSudeep Holla if (!perf_ops->freq_set(handle, priv->domain_id, 7102f208c5SSudeep Holla target_freq * 1000, true)) { 7202f208c5SSudeep Holla arch_set_freq_scale(policy->related_cpus, target_freq, 7302f208c5SSudeep Holla policy->cpuinfo.max_freq); 7402f208c5SSudeep Holla return target_freq; 7502f208c5SSudeep Holla } 7602f208c5SSudeep Holla 7702f208c5SSudeep Holla return 0; 7802f208c5SSudeep Holla } 7902f208c5SSudeep Holla 8099d6bdf3SSudeep Holla static int 8199d6bdf3SSudeep Holla scmi_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask) 8299d6bdf3SSudeep Holla { 8399d6bdf3SSudeep Holla int cpu, domain, tdomain; 8499d6bdf3SSudeep Holla struct device *tcpu_dev; 8599d6bdf3SSudeep Holla 8699d6bdf3SSudeep Holla domain = handle->perf_ops->device_domain_id(cpu_dev); 8799d6bdf3SSudeep Holla if (domain < 0) 8899d6bdf3SSudeep Holla return domain; 8999d6bdf3SSudeep Holla 9099d6bdf3SSudeep Holla for_each_possible_cpu(cpu) { 9199d6bdf3SSudeep Holla if (cpu == cpu_dev->id) 9299d6bdf3SSudeep Holla continue; 9399d6bdf3SSudeep Holla 9499d6bdf3SSudeep Holla tcpu_dev = get_cpu_device(cpu); 9599d6bdf3SSudeep Holla if (!tcpu_dev) 9699d6bdf3SSudeep Holla continue; 9799d6bdf3SSudeep Holla 9899d6bdf3SSudeep Holla tdomain = handle->perf_ops->device_domain_id(tcpu_dev); 9999d6bdf3SSudeep Holla if (tdomain == domain) 10099d6bdf3SSudeep Holla cpumask_set_cpu(cpu, cpumask); 10199d6bdf3SSudeep Holla } 10299d6bdf3SSudeep Holla 10399d6bdf3SSudeep Holla return 0; 10499d6bdf3SSudeep Holla } 10599d6bdf3SSudeep Holla 10699d6bdf3SSudeep Holla static int scmi_cpufreq_init(struct cpufreq_policy *policy) 10799d6bdf3SSudeep Holla { 10899d6bdf3SSudeep Holla int ret; 10999d6bdf3SSudeep Holla unsigned int latency; 11099d6bdf3SSudeep Holla struct device *cpu_dev; 11199d6bdf3SSudeep Holla struct scmi_data *priv; 11299d6bdf3SSudeep Holla struct cpufreq_frequency_table *freq_table; 11399d6bdf3SSudeep Holla 11499d6bdf3SSudeep Holla cpu_dev = get_cpu_device(policy->cpu); 11599d6bdf3SSudeep Holla if (!cpu_dev) { 11699d6bdf3SSudeep Holla pr_err("failed to get cpu%d device\n", policy->cpu); 11799d6bdf3SSudeep Holla return -ENODEV; 11899d6bdf3SSudeep Holla } 11999d6bdf3SSudeep Holla 120*7859e08cSSudeep Holla ret = handle->perf_ops->device_opps_add(handle, cpu_dev); 12199d6bdf3SSudeep Holla if (ret) { 12299d6bdf3SSudeep Holla dev_warn(cpu_dev, "failed to add opps to the device\n"); 12399d6bdf3SSudeep Holla return ret; 12499d6bdf3SSudeep Holla } 12599d6bdf3SSudeep Holla 12699d6bdf3SSudeep Holla ret = scmi_get_sharing_cpus(cpu_dev, policy->cpus); 12799d6bdf3SSudeep Holla if (ret) { 12899d6bdf3SSudeep Holla dev_warn(cpu_dev, "failed to get sharing cpumask\n"); 12999d6bdf3SSudeep Holla return ret; 13099d6bdf3SSudeep Holla } 13199d6bdf3SSudeep Holla 13299d6bdf3SSudeep Holla ret = dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus); 13399d6bdf3SSudeep Holla if (ret) { 13499d6bdf3SSudeep Holla dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n", 13599d6bdf3SSudeep Holla __func__, ret); 13699d6bdf3SSudeep Holla return ret; 13799d6bdf3SSudeep Holla } 13899d6bdf3SSudeep Holla 13999d6bdf3SSudeep Holla ret = dev_pm_opp_get_opp_count(cpu_dev); 14099d6bdf3SSudeep Holla if (ret <= 0) { 14199d6bdf3SSudeep Holla dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n"); 14299d6bdf3SSudeep Holla ret = -EPROBE_DEFER; 14399d6bdf3SSudeep Holla goto out_free_opp; 14499d6bdf3SSudeep Holla } 14599d6bdf3SSudeep Holla 14699d6bdf3SSudeep Holla priv = kzalloc(sizeof(*priv), GFP_KERNEL); 14799d6bdf3SSudeep Holla if (!priv) { 14899d6bdf3SSudeep Holla ret = -ENOMEM; 14999d6bdf3SSudeep Holla goto out_free_opp; 15099d6bdf3SSudeep Holla } 15199d6bdf3SSudeep Holla 15299d6bdf3SSudeep Holla ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); 15399d6bdf3SSudeep Holla if (ret) { 15499d6bdf3SSudeep Holla dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret); 15599d6bdf3SSudeep Holla goto out_free_priv; 15699d6bdf3SSudeep Holla } 15799d6bdf3SSudeep Holla 15899d6bdf3SSudeep Holla priv->cpu_dev = cpu_dev; 15999d6bdf3SSudeep Holla priv->domain_id = handle->perf_ops->device_domain_id(cpu_dev); 16099d6bdf3SSudeep Holla 16199d6bdf3SSudeep Holla policy->driver_data = priv; 162d983af98SViresh Kumar policy->freq_table = freq_table; 16399d6bdf3SSudeep Holla 16499d6bdf3SSudeep Holla /* SCMI allows DVFS request for any domain from any CPU */ 16599d6bdf3SSudeep Holla policy->dvfs_possible_from_any_cpu = true; 16699d6bdf3SSudeep Holla 167*7859e08cSSudeep Holla latency = handle->perf_ops->transition_latency_get(handle, cpu_dev); 16899d6bdf3SSudeep Holla if (!latency) 16999d6bdf3SSudeep Holla latency = CPUFREQ_ETERNAL; 17099d6bdf3SSudeep Holla 17199d6bdf3SSudeep Holla policy->cpuinfo.transition_latency = latency; 17299d6bdf3SSudeep Holla 17302f208c5SSudeep Holla policy->fast_switch_possible = true; 17499d6bdf3SSudeep Holla return 0; 17599d6bdf3SSudeep Holla 17699d6bdf3SSudeep Holla out_free_priv: 17799d6bdf3SSudeep Holla kfree(priv); 17899d6bdf3SSudeep Holla out_free_opp: 17999d6bdf3SSudeep Holla dev_pm_opp_cpumask_remove_table(policy->cpus); 18099d6bdf3SSudeep Holla 18199d6bdf3SSudeep Holla return ret; 18299d6bdf3SSudeep Holla } 18399d6bdf3SSudeep Holla 18499d6bdf3SSudeep Holla static int scmi_cpufreq_exit(struct cpufreq_policy *policy) 18599d6bdf3SSudeep Holla { 18699d6bdf3SSudeep Holla struct scmi_data *priv = policy->driver_data; 18799d6bdf3SSudeep Holla 18899d6bdf3SSudeep Holla cpufreq_cooling_unregister(priv->cdev); 18999d6bdf3SSudeep Holla dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table); 19099d6bdf3SSudeep Holla kfree(priv); 19199d6bdf3SSudeep Holla dev_pm_opp_cpumask_remove_table(policy->related_cpus); 19299d6bdf3SSudeep Holla 19399d6bdf3SSudeep Holla return 0; 19499d6bdf3SSudeep Holla } 19599d6bdf3SSudeep Holla 19699d6bdf3SSudeep Holla static void scmi_cpufreq_ready(struct cpufreq_policy *policy) 19799d6bdf3SSudeep Holla { 19899d6bdf3SSudeep Holla struct scmi_data *priv = policy->driver_data; 19999d6bdf3SSudeep Holla 20099d6bdf3SSudeep Holla priv->cdev = of_cpufreq_cooling_register(policy); 20199d6bdf3SSudeep Holla } 20299d6bdf3SSudeep Holla 20399d6bdf3SSudeep Holla static struct cpufreq_driver scmi_cpufreq_driver = { 20499d6bdf3SSudeep Holla .name = "scmi", 20599d6bdf3SSudeep Holla .flags = CPUFREQ_STICKY | CPUFREQ_HAVE_GOVERNOR_PER_POLICY | 20699d6bdf3SSudeep Holla CPUFREQ_NEED_INITIAL_FREQ_CHECK, 20799d6bdf3SSudeep Holla .verify = cpufreq_generic_frequency_table_verify, 20899d6bdf3SSudeep Holla .attr = cpufreq_generic_attr, 20999d6bdf3SSudeep Holla .target_index = scmi_cpufreq_set_target, 21002f208c5SSudeep Holla .fast_switch = scmi_cpufreq_fast_switch, 21199d6bdf3SSudeep Holla .get = scmi_cpufreq_get_rate, 21299d6bdf3SSudeep Holla .init = scmi_cpufreq_init, 21399d6bdf3SSudeep Holla .exit = scmi_cpufreq_exit, 21499d6bdf3SSudeep Holla .ready = scmi_cpufreq_ready, 21599d6bdf3SSudeep Holla }; 21699d6bdf3SSudeep Holla 21799d6bdf3SSudeep Holla static int scmi_cpufreq_probe(struct scmi_device *sdev) 21899d6bdf3SSudeep Holla { 21999d6bdf3SSudeep Holla int ret; 22099d6bdf3SSudeep Holla 22199d6bdf3SSudeep Holla handle = sdev->handle; 22299d6bdf3SSudeep Holla 22399d6bdf3SSudeep Holla if (!handle || !handle->perf_ops) 22499d6bdf3SSudeep Holla return -ENODEV; 22599d6bdf3SSudeep Holla 22699d6bdf3SSudeep Holla ret = cpufreq_register_driver(&scmi_cpufreq_driver); 22799d6bdf3SSudeep Holla if (ret) { 22899d6bdf3SSudeep Holla dev_err(&sdev->dev, "%s: registering cpufreq failed, err: %d\n", 22999d6bdf3SSudeep Holla __func__, ret); 23099d6bdf3SSudeep Holla } 23199d6bdf3SSudeep Holla 23299d6bdf3SSudeep Holla return ret; 23399d6bdf3SSudeep Holla } 23499d6bdf3SSudeep Holla 23599d6bdf3SSudeep Holla static void scmi_cpufreq_remove(struct scmi_device *sdev) 23699d6bdf3SSudeep Holla { 23799d6bdf3SSudeep Holla cpufreq_unregister_driver(&scmi_cpufreq_driver); 23899d6bdf3SSudeep Holla } 23999d6bdf3SSudeep Holla 24099d6bdf3SSudeep Holla static const struct scmi_device_id scmi_id_table[] = { 24199d6bdf3SSudeep Holla { SCMI_PROTOCOL_PERF }, 24299d6bdf3SSudeep Holla { }, 24399d6bdf3SSudeep Holla }; 24499d6bdf3SSudeep Holla MODULE_DEVICE_TABLE(scmi, scmi_id_table); 24599d6bdf3SSudeep Holla 24699d6bdf3SSudeep Holla static struct scmi_driver scmi_cpufreq_drv = { 24799d6bdf3SSudeep Holla .name = "scmi-cpufreq", 24899d6bdf3SSudeep Holla .probe = scmi_cpufreq_probe, 24999d6bdf3SSudeep Holla .remove = scmi_cpufreq_remove, 25099d6bdf3SSudeep Holla .id_table = scmi_id_table, 25199d6bdf3SSudeep Holla }; 25299d6bdf3SSudeep Holla module_scmi_driver(scmi_cpufreq_drv); 25399d6bdf3SSudeep Holla 25499d6bdf3SSudeep Holla MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>"); 25599d6bdf3SSudeep Holla MODULE_DESCRIPTION("ARM SCMI CPUFreq interface driver"); 25699d6bdf3SSudeep Holla MODULE_LICENSE("GPL v2"); 257