brintos

brintos / linux-shallow public Read only

0
0
Text · 5.8 KiB · d8ab5b0 Raw
244 lines · c
1/*2 * drivers/cpufreq/spear-cpufreq.c3 *4 * CPU Frequency Scaling for SPEAr platform5 *6 * Copyright (C) 2012 ST Microelectronics7 * Deepak Sikri <deepak.sikri@st.com>8 *9 * This file is licensed under the terms of the GNU General Public10 * License version 2. This program is licensed "as is" without any11 * warranty of any kind, whether express or implied.12 */13 14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt15 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.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 from40	 * following sources41	 */42	static const char * const 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 range51	 * limitation so we choose possible sources accordingly52	 */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	else60		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 actually72 * 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 switching74 * 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 possibility115		 * of multiple clock sources for cpu clk's parent we can have116		 * different clock source for different frequency of cpu clk.117		 * Hence we need to choose one from amongst these possible clock118		 * 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 we131		 * can directly work on cpu clk132		 */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	else145		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	struct cpufreq_frequency_table *freq_tbl;175	u32 val;176	int cnt, ret, i = 0;177 178	np = of_cpu_device_node_get(0);179	if (!np) {180		pr_err("No cpu node found\n");181		return -ENODEV;182	}183 184	if (of_property_read_u32(np, "clock-latency",185				&spear_cpufreq.transition_latency))186		spear_cpufreq.transition_latency = CPUFREQ_ETERNAL;187 188	cnt = of_property_count_u32_elems(np, "cpufreq_tbl");189	if (cnt <= 0) {190		pr_err("Invalid cpufreq_tbl\n");191		ret = -ENODEV;192		goto out_put_node;193	}194 195	freq_tbl = kcalloc(cnt + 1, sizeof(*freq_tbl), GFP_KERNEL);196	if (!freq_tbl) {197		ret = -ENOMEM;198		goto out_put_node;199	}200 201	of_property_for_each_u32(np, "cpufreq_tbl", val)202		freq_tbl[i++].frequency = val;203 204	freq_tbl[cnt].frequency = CPUFREQ_TABLE_END;205 206	spear_cpufreq.freq_tbl = freq_tbl;207 208	of_node_put(np);209 210	spear_cpufreq.clk = clk_get(NULL, "cpu_clk");211	if (IS_ERR(spear_cpufreq.clk)) {212		pr_err("Unable to get CPU clock\n");213		ret = PTR_ERR(spear_cpufreq.clk);214		goto out_put_mem;215	}216 217	ret = cpufreq_register_driver(&spear_cpufreq_driver);218	if (!ret)219		return 0;220 221	pr_err("failed register driver: %d\n", ret);222	clk_put(spear_cpufreq.clk);223 224out_put_mem:225	kfree(freq_tbl);226	return ret;227 228out_put_node:229	of_node_put(np);230	return ret;231}232 233static struct platform_driver spear_cpufreq_platdrv = {234	.driver = {235		.name	= "spear-cpufreq",236	},237	.probe		= spear_cpufreq_probe,238};239module_platform_driver(spear_cpufreq_platdrv);240 241MODULE_AUTHOR("Deepak Sikri <deepak.sikri@st.com>");242MODULE_DESCRIPTION("SPEAr CPUFreq driver");243MODULE_LICENSE("GPL");244