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

spear-cpufreq.c (6013B)


      1/*
      2 * drivers/cpufreq/spear-cpufreq.c
      3 *
      4 * CPU Frequency Scaling for SPEAr platform
      5 *
      6 * Copyright (C) 2012 ST Microelectronics
      7 * Deepak Sikri <deepak.sikri@st.com>
      8 *
      9 * This file is licensed under the terms of the GNU General Public
     10 * License version 2. This program is licensed "as is" without any
     11 * warranty of any kind, whether express or implied.
     12 */
     13
     14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
     15
     16#include <linux/clk.h>
     17#include <linux/cpufreq.h>
     18#include <linux/err.h>
     19#include <linux/init.h>
     20#include <linux/module.h>
     21#include <linux/of_device.h>
     22#include <linux/platform_device.h>
     23#include <linux/slab.h>
     24#include <linux/types.h>
     25
     26/* SPEAr CPUFreq driver data structure */
     27static struct {
     28	struct clk *clk;
     29	unsigned int transition_latency;
     30	struct cpufreq_frequency_table *freq_tbl;
     31	u32 cnt;
     32} spear_cpufreq;
     33
     34static struct clk *spear1340_cpu_get_possible_parent(unsigned long newfreq)
     35{
     36	struct clk *sys_pclk;
     37	int pclk;
     38	/*
     39	 * In SPEAr1340, cpu clk's parent sys clk can take input from
     40	 * following sources
     41	 */
     42	const char *sys_clk_src[] = {
     43		"sys_syn_clk",
     44		"pll1_clk",
     45		"pll2_clk",
     46		"pll3_clk",
     47	};
     48
     49	/*
     50	 * As sys clk can have multiple source with their own range
     51	 * limitation so we choose possible sources accordingly
     52	 */
     53	if (newfreq <= 300000000)
     54		pclk = 0; /* src is sys_syn_clk */
     55	else if (newfreq > 300000000 && newfreq <= 500000000)
     56		pclk = 3; /* src is pll3_clk */
     57	else if (newfreq == 600000000)
     58		pclk = 1; /* src is pll1_clk */
     59	else
     60		return ERR_PTR(-EINVAL);
     61
     62	/* Get parent to sys clock */
     63	sys_pclk = clk_get(NULL, sys_clk_src[pclk]);
     64	if (IS_ERR(sys_pclk))
     65		pr_err("Failed to get %s clock\n", sys_clk_src[pclk]);
     66
     67	return sys_pclk;
     68}
     69
     70/*
     71 * In SPEAr1340, we cannot use newfreq directly because we need to actually
     72 * access a source clock (clk) which might not be ancestor of cpu at present.
     73 * Hence in SPEAr1340 we would operate on source clock directly before switching
     74 * cpu clock to it.
     75 */
     76static int spear1340_set_cpu_rate(struct clk *sys_pclk, unsigned long newfreq)
     77{
     78	struct clk *sys_clk;
     79	int ret = 0;
     80
     81	sys_clk = clk_get_parent(spear_cpufreq.clk);
     82	if (IS_ERR(sys_clk)) {
     83		pr_err("failed to get cpu's parent (sys) clock\n");
     84		return PTR_ERR(sys_clk);
     85	}
     86
     87	/* Set the rate of the source clock before changing the parent */
     88	ret = clk_set_rate(sys_pclk, newfreq);
     89	if (ret) {
     90		pr_err("Failed to set sys clk rate to %lu\n", newfreq);
     91		return ret;
     92	}
     93
     94	ret = clk_set_parent(sys_clk, sys_pclk);
     95	if (ret) {
     96		pr_err("Failed to set sys clk parent\n");
     97		return ret;
     98	}
     99
    100	return 0;
    101}
    102
    103static int spear_cpufreq_target(struct cpufreq_policy *policy,
    104		unsigned int index)
    105{
    106	long newfreq;
    107	struct clk *srcclk;
    108	int ret, mult = 1;
    109
    110	newfreq = spear_cpufreq.freq_tbl[index].frequency * 1000;
    111
    112	if (of_machine_is_compatible("st,spear1340")) {
    113		/*
    114		 * SPEAr1340 is special in the sense that due to the possibility
    115		 * of multiple clock sources for cpu clk's parent we can have
    116		 * different clock source for different frequency of cpu clk.
    117		 * Hence we need to choose one from amongst these possible clock
    118		 * sources.
    119		 */
    120		srcclk = spear1340_cpu_get_possible_parent(newfreq);
    121		if (IS_ERR(srcclk)) {
    122			pr_err("Failed to get src clk\n");
    123			return PTR_ERR(srcclk);
    124		}
    125
    126		/* SPEAr1340: src clk is always 2 * intended cpu clk */
    127		mult = 2;
    128	} else {
    129		/*
    130		 * src clock to be altered is ancestor of cpu clock. Hence we
    131		 * can directly work on cpu clk
    132		 */
    133		srcclk = spear_cpufreq.clk;
    134	}
    135
    136	newfreq = clk_round_rate(srcclk, newfreq * mult);
    137	if (newfreq <= 0) {
    138		pr_err("clk_round_rate failed for cpu src clock\n");
    139		return newfreq;
    140	}
    141
    142	if (mult == 2)
    143		ret = spear1340_set_cpu_rate(srcclk, newfreq);
    144	else
    145		ret = clk_set_rate(spear_cpufreq.clk, newfreq);
    146
    147	if (ret)
    148		pr_err("CPU Freq: cpu clk_set_rate failed: %d\n", ret);
    149
    150	return ret;
    151}
    152
    153static int spear_cpufreq_init(struct cpufreq_policy *policy)
    154{
    155	policy->clk = spear_cpufreq.clk;
    156	cpufreq_generic_init(policy, spear_cpufreq.freq_tbl,
    157			spear_cpufreq.transition_latency);
    158	return 0;
    159}
    160
    161static struct cpufreq_driver spear_cpufreq_driver = {
    162	.name		= "cpufreq-spear",
    163	.flags		= CPUFREQ_NEED_INITIAL_FREQ_CHECK,
    164	.verify		= cpufreq_generic_frequency_table_verify,
    165	.target_index	= spear_cpufreq_target,
    166	.get		= cpufreq_generic_get,
    167	.init		= spear_cpufreq_init,
    168	.attr		= cpufreq_generic_attr,
    169};
    170
    171static int spear_cpufreq_probe(struct platform_device *pdev)
    172{
    173	struct device_node *np;
    174	const struct property *prop;
    175	struct cpufreq_frequency_table *freq_tbl;
    176	const __be32 *val;
    177	int cnt, i, ret;
    178
    179	np = of_cpu_device_node_get(0);
    180	if (!np) {
    181		pr_err("No cpu node found\n");
    182		return -ENODEV;
    183	}
    184
    185	if (of_property_read_u32(np, "clock-latency",
    186				&spear_cpufreq.transition_latency))
    187		spear_cpufreq.transition_latency = CPUFREQ_ETERNAL;
    188
    189	prop = of_find_property(np, "cpufreq_tbl", NULL);
    190	if (!prop || !prop->value) {
    191		pr_err("Invalid cpufreq_tbl\n");
    192		ret = -ENODEV;
    193		goto out_put_node;
    194	}
    195
    196	cnt = prop->length / sizeof(u32);
    197	val = prop->value;
    198
    199	freq_tbl = kcalloc(cnt + 1, sizeof(*freq_tbl), GFP_KERNEL);
    200	if (!freq_tbl) {
    201		ret = -ENOMEM;
    202		goto out_put_node;
    203	}
    204
    205	for (i = 0; i < cnt; i++)
    206		freq_tbl[i].frequency = be32_to_cpup(val++);
    207
    208	freq_tbl[i].frequency = CPUFREQ_TABLE_END;
    209
    210	spear_cpufreq.freq_tbl = freq_tbl;
    211
    212	of_node_put(np);
    213
    214	spear_cpufreq.clk = clk_get(NULL, "cpu_clk");
    215	if (IS_ERR(spear_cpufreq.clk)) {
    216		pr_err("Unable to get CPU clock\n");
    217		ret = PTR_ERR(spear_cpufreq.clk);
    218		goto out_put_mem;
    219	}
    220
    221	ret = cpufreq_register_driver(&spear_cpufreq_driver);
    222	if (!ret)
    223		return 0;
    224
    225	pr_err("failed register driver: %d\n", ret);
    226	clk_put(spear_cpufreq.clk);
    227
    228out_put_mem:
    229	kfree(freq_tbl);
    230	return ret;
    231
    232out_put_node:
    233	of_node_put(np);
    234	return ret;
    235}
    236
    237static struct platform_driver spear_cpufreq_platdrv = {
    238	.driver = {
    239		.name	= "spear-cpufreq",
    240	},
    241	.probe		= spear_cpufreq_probe,
    242};
    243module_platform_driver(spear_cpufreq_platdrv);
    244
    245MODULE_AUTHOR("Deepak Sikri <deepak.sikri@st.com>");
    246MODULE_DESCRIPTION("SPEAr CPUFreq driver");
    247MODULE_LICENSE("GPL");