cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

scpi-cpufreq.c (5640B)


      1/*
      2 * System Control and Power Interface (SCPI) based CPUFreq Interface driver
      3 *
      4 * Copyright (C) 2015 ARM Ltd.
      5 * Sudeep Holla <sudeep.holla@arm.com>
      6 *
      7 * This program is free software; you can redistribute it and/or modify
      8 * it under the terms of the GNU General Public License version 2 as
      9 * published by the Free Software Foundation.
     10 *
     11 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
     12 * kind, whether express or implied; without even the implied warranty
     13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     14 * GNU General Public License for more details.
     15 */
     16
     17#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
     18
     19#include <linux/clk.h>
     20#include <linux/cpu.h>
     21#include <linux/cpufreq.h>
     22#include <linux/cpumask.h>
     23#include <linux/export.h>
     24#include <linux/module.h>
     25#include <linux/of_platform.h>
     26#include <linux/pm_opp.h>
     27#include <linux/scpi_protocol.h>
     28#include <linux/slab.h>
     29#include <linux/types.h>
     30
     31struct scpi_data {
     32	struct clk *clk;
     33	struct device *cpu_dev;
     34};
     35
     36static struct scpi_ops *scpi_ops;
     37
     38static unsigned int scpi_cpufreq_get_rate(unsigned int cpu)
     39{
     40	struct cpufreq_policy *policy = cpufreq_cpu_get_raw(cpu);
     41	struct scpi_data *priv = policy->driver_data;
     42	unsigned long rate = clk_get_rate(priv->clk);
     43
     44	return rate / 1000;
     45}
     46
     47static int
     48scpi_cpufreq_set_target(struct cpufreq_policy *policy, unsigned int index)
     49{
     50	u64 rate = policy->freq_table[index].frequency * 1000;
     51	struct scpi_data *priv = policy->driver_data;
     52	int ret;
     53
     54	ret = clk_set_rate(priv->clk, rate);
     55
     56	if (ret)
     57		return ret;
     58
     59	if (clk_get_rate(priv->clk) != rate)
     60		return -EIO;
     61
     62	return 0;
     63}
     64
     65static int
     66scpi_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask)
     67{
     68	int cpu, domain, tdomain;
     69	struct device *tcpu_dev;
     70
     71	domain = scpi_ops->device_domain_id(cpu_dev);
     72	if (domain < 0)
     73		return domain;
     74
     75	for_each_possible_cpu(cpu) {
     76		if (cpu == cpu_dev->id)
     77			continue;
     78
     79		tcpu_dev = get_cpu_device(cpu);
     80		if (!tcpu_dev)
     81			continue;
     82
     83		tdomain = scpi_ops->device_domain_id(tcpu_dev);
     84		if (tdomain == domain)
     85			cpumask_set_cpu(cpu, cpumask);
     86	}
     87
     88	return 0;
     89}
     90
     91static int scpi_cpufreq_init(struct cpufreq_policy *policy)
     92{
     93	int ret;
     94	unsigned int latency;
     95	struct device *cpu_dev;
     96	struct scpi_data *priv;
     97	struct cpufreq_frequency_table *freq_table;
     98
     99	cpu_dev = get_cpu_device(policy->cpu);
    100	if (!cpu_dev) {
    101		pr_err("failed to get cpu%d device\n", policy->cpu);
    102		return -ENODEV;
    103	}
    104
    105	ret = scpi_ops->add_opps_to_device(cpu_dev);
    106	if (ret) {
    107		dev_warn(cpu_dev, "failed to add opps to the device\n");
    108		return ret;
    109	}
    110
    111	ret = scpi_get_sharing_cpus(cpu_dev, policy->cpus);
    112	if (ret) {
    113		dev_warn(cpu_dev, "failed to get sharing cpumask\n");
    114		return ret;
    115	}
    116
    117	ret = dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus);
    118	if (ret) {
    119		dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n",
    120			__func__, ret);
    121		return ret;
    122	}
    123
    124	ret = dev_pm_opp_get_opp_count(cpu_dev);
    125	if (ret <= 0) {
    126		dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n");
    127		ret = -EPROBE_DEFER;
    128		goto out_free_opp;
    129	}
    130
    131	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
    132	if (!priv) {
    133		ret = -ENOMEM;
    134		goto out_free_opp;
    135	}
    136
    137	ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
    138	if (ret) {
    139		dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
    140		goto out_free_priv;
    141	}
    142
    143	priv->cpu_dev = cpu_dev;
    144	priv->clk = clk_get(cpu_dev, NULL);
    145	if (IS_ERR(priv->clk)) {
    146		dev_err(cpu_dev, "%s: Failed to get clk for cpu: %d\n",
    147			__func__, cpu_dev->id);
    148		ret = PTR_ERR(priv->clk);
    149		goto out_free_cpufreq_table;
    150	}
    151
    152	policy->driver_data = priv;
    153	policy->freq_table = freq_table;
    154
    155	/* scpi allows DVFS request for any domain from any CPU */
    156	policy->dvfs_possible_from_any_cpu = true;
    157
    158	latency = scpi_ops->get_transition_latency(cpu_dev);
    159	if (!latency)
    160		latency = CPUFREQ_ETERNAL;
    161
    162	policy->cpuinfo.transition_latency = latency;
    163
    164	policy->fast_switch_possible = false;
    165
    166	return 0;
    167
    168out_free_cpufreq_table:
    169	dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
    170out_free_priv:
    171	kfree(priv);
    172out_free_opp:
    173	dev_pm_opp_remove_all_dynamic(cpu_dev);
    174
    175	return ret;
    176}
    177
    178static int scpi_cpufreq_exit(struct cpufreq_policy *policy)
    179{
    180	struct scpi_data *priv = policy->driver_data;
    181
    182	clk_put(priv->clk);
    183	dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
    184	dev_pm_opp_remove_all_dynamic(priv->cpu_dev);
    185	kfree(priv);
    186
    187	return 0;
    188}
    189
    190static struct cpufreq_driver scpi_cpufreq_driver = {
    191	.name	= "scpi-cpufreq",
    192	.flags	= CPUFREQ_HAVE_GOVERNOR_PER_POLICY |
    193		  CPUFREQ_NEED_INITIAL_FREQ_CHECK |
    194		  CPUFREQ_IS_COOLING_DEV,
    195	.verify	= cpufreq_generic_frequency_table_verify,
    196	.attr	= cpufreq_generic_attr,
    197	.get	= scpi_cpufreq_get_rate,
    198	.init	= scpi_cpufreq_init,
    199	.exit	= scpi_cpufreq_exit,
    200	.target_index	= scpi_cpufreq_set_target,
    201	.register_em	= cpufreq_register_em_with_opp,
    202};
    203
    204static int scpi_cpufreq_probe(struct platform_device *pdev)
    205{
    206	int ret;
    207
    208	scpi_ops = get_scpi_ops();
    209	if (!scpi_ops)
    210		return -EIO;
    211
    212	ret = cpufreq_register_driver(&scpi_cpufreq_driver);
    213	if (ret)
    214		dev_err(&pdev->dev, "%s: registering cpufreq failed, err: %d\n",
    215			__func__, ret);
    216	return ret;
    217}
    218
    219static int scpi_cpufreq_remove(struct platform_device *pdev)
    220{
    221	cpufreq_unregister_driver(&scpi_cpufreq_driver);
    222	scpi_ops = NULL;
    223	return 0;
    224}
    225
    226static struct platform_driver scpi_cpufreq_platdrv = {
    227	.driver = {
    228		.name	= "scpi-cpufreq",
    229	},
    230	.probe		= scpi_cpufreq_probe,
    231	.remove		= scpi_cpufreq_remove,
    232};
    233module_platform_driver(scpi_cpufreq_platdrv);
    234
    235MODULE_ALIAS("platform:scpi-cpufreq");
    236MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
    237MODULE_DESCRIPTION("ARM SCPI CPUFreq interface driver");
    238MODULE_LICENSE("GPL v2");