cregit-Linux how code gets into the kernel

Release 4.18 drivers/cpufreq/scmi-cpufreq.c

Directory: drivers/cpufreq
// SPDX-License-Identifier: GPL-2.0
/*
 * System Control and Power Interface (SCMI) based CPUFreq Interface driver
 *
 * Copyright (C) 2018 ARM Ltd.
 * Sudeep Holla <sudeep.holla@arm.com>
 */


#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/cpu.h>
#include <linux/cpufreq.h>
#include <linux/cpumask.h>
#include <linux/cpu_cooling.h>
#include <linux/export.h>
#include <linux/module.h>
#include <linux/pm_opp.h>
#include <linux/slab.h>
#include <linux/scmi_protocol.h>
#include <linux/types.h>


struct scmi_data {
	
int domain_id;
	
struct device *cpu_dev;
	
struct thermal_cooling_device *cdev;
};


static const struct scmi_handle *handle;


static unsigned int scmi_cpufreq_get_rate(unsigned int cpu) { struct cpufreq_policy *policy = cpufreq_cpu_get_raw(cpu); struct scmi_perf_ops *perf_ops = handle->perf_ops; struct scmi_data *priv = policy->driver_data; unsigned long rate; int ret; ret = perf_ops->freq_get(handle, priv->domain_id, &rate, false); if (ret) return 0; return rate / 1000; }

Contributors

PersonTokensPropCommitsCommitProp
Sudeep Holla76100.00%1100.00%
Total76100.00%1100.00%

/* * perf_ops->freq_set is not a synchronous, the actual OPP change will * happen asynchronously and can get notified if the events are * subscribed for by the SCMI firmware */
static int scmi_cpufreq_set_target(struct cpufreq_policy *policy, unsigned int index) { int ret; struct scmi_data *priv = policy->driver_data; struct scmi_perf_ops *perf_ops = handle->perf_ops; u64 freq = policy->freq_table[index].frequency * 1000; ret = perf_ops->freq_set(handle, priv->domain_id, freq, false); if (!ret) arch_set_freq_scale(policy->related_cpus, freq, policy->cpuinfo.max_freq); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Sudeep Holla90100.00%1100.00%
Total90100.00%1100.00%


static unsigned int scmi_cpufreq_fast_switch(struct cpufreq_policy *policy, unsigned int target_freq) { struct scmi_data *priv = policy->driver_data; struct scmi_perf_ops *perf_ops = handle->perf_ops; if (!perf_ops->freq_set(handle, priv->domain_id, target_freq * 1000, true)) { arch_set_freq_scale(policy->related_cpus, target_freq, policy->cpuinfo.max_freq); return target_freq; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Sudeep Holla77100.00%1100.00%
Total77100.00%1100.00%


static int scmi_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask) { int cpu, domain, tdomain; struct device *tcpu_dev; domain = handle->perf_ops->device_domain_id(cpu_dev); if (domain < 0) return domain; for_each_possible_cpu(cpu) { if (cpu == cpu_dev->id) continue; tcpu_dev = get_cpu_device(cpu); if (!tcpu_dev) continue; tdomain = handle->perf_ops->device_domain_id(tcpu_dev); if (tdomain == domain) cpumask_set_cpu(cpu, cpumask); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Sudeep Holla103100.00%1100.00%
Total103100.00%1100.00%


static int scmi_cpufreq_init(struct cpufreq_policy *policy) { int ret; unsigned int latency; struct device *cpu_dev; struct scmi_data *priv; struct cpufreq_frequency_table *freq_table; cpu_dev = get_cpu_device(policy->cpu); if (!cpu_dev) { pr_err("failed to get cpu%d device\n", policy->cpu); return -ENODEV; } ret = handle->perf_ops->device_opps_add(handle, cpu_dev); if (ret) { dev_warn(cpu_dev, "failed to add opps to the device\n"); return ret; } ret = scmi_get_sharing_cpus(cpu_dev, policy->cpus); if (ret) { dev_warn(cpu_dev, "failed to get sharing cpumask\n"); return ret; } ret = dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus); if (ret) { dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n", __func__, ret); return ret; } ret = dev_pm_opp_get_opp_count(cpu_dev); if (ret <= 0) { dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n"); ret = -EPROBE_DEFER; goto out_free_opp; } priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { ret = -ENOMEM; goto out_free_opp; } ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); if (ret) { dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret); goto out_free_priv; } priv->cpu_dev = cpu_dev; priv->domain_id = handle->perf_ops->device_domain_id(cpu_dev); policy->driver_data = priv; policy->freq_table = freq_table; /* SCMI allows DVFS request for any domain from any CPU */ policy->dvfs_possible_from_any_cpu = true; latency = handle->perf_ops->transition_latency_get(handle, cpu_dev); if (!latency) latency = CPUFREQ_ETERNAL; policy->cpuinfo.transition_latency = latency; policy->fast_switch_possible = true; return 0; out_free_priv: kfree(priv); out_free_opp: dev_pm_opp_cpumask_remove_table(policy->cpus); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Sudeep Holla32899.09%375.00%
Viresh Kumar30.91%125.00%
Total331100.00%4100.00%


static int scmi_cpufreq_exit(struct cpufreq_policy *policy) { struct scmi_data *priv = policy->driver_data; cpufreq_cooling_unregister(priv->cdev); dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table); kfree(priv); dev_pm_opp_cpumask_remove_table(policy->related_cpus); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Sudeep Holla54100.00%1100.00%
Total54100.00%1100.00%


static void scmi_cpufreq_ready(struct cpufreq_policy *policy) { struct scmi_data *priv = policy->driver_data; priv->cdev = of_cpufreq_cooling_register(policy); }

Contributors

PersonTokensPropCommitsCommitProp
Sudeep Holla29100.00%1100.00%
Total29100.00%1100.00%

static struct cpufreq_driver scmi_cpufreq_driver = { .name = "scmi", .flags = CPUFREQ_STICKY | CPUFREQ_HAVE_GOVERNOR_PER_POLICY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .attr = cpufreq_generic_attr, .target_index = scmi_cpufreq_set_target, .fast_switch = scmi_cpufreq_fast_switch, .get = scmi_cpufreq_get_rate, .init = scmi_cpufreq_init, .exit = scmi_cpufreq_exit, .ready = scmi_cpufreq_ready, };
static int scmi_cpufreq_probe(struct scmi_device *sdev) { int ret; handle = sdev->handle; if (!handle || !handle->perf_ops) return -ENODEV; ret = cpufreq_register_driver(&scmi_cpufreq_driver); if (ret) { dev_err(&sdev->dev, "%s: registering cpufreq failed, err: %d\n", __func__, ret); } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Sudeep Holla65100.00%1100.00%
Total65100.00%1100.00%


static void scmi_cpufreq_remove(struct scmi_device *sdev) { cpufreq_unregister_driver(&scmi_cpufreq_driver); }

Contributors

PersonTokensPropCommitsCommitProp
Sudeep Holla17100.00%1100.00%
Total17100.00%1100.00%

static const struct scmi_device_id scmi_id_table[] = { { SCMI_PROTOCOL_PERF }, { }, }; MODULE_DEVICE_TABLE(scmi, scmi_id_table); static struct scmi_driver scmi_cpufreq_drv = { .name = "scmi-cpufreq", .probe = scmi_cpufreq_probe, .remove = scmi_cpufreq_remove, .id_table = scmi_id_table, }; module_scmi_driver(scmi_cpufreq_drv); MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>"); MODULE_DESCRIPTION("ARM SCMI CPUFreq interface driver"); MODULE_LICENSE("GPL v2");

Overall Contributors

PersonTokensPropCommitsCommitProp
Sudeep Holla103499.71%375.00%
Viresh Kumar30.29%125.00%
Total1037100.00%4100.00%
Directory: drivers/cpufreq
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.