Blame view

drivers/cpufreq/spear-cpufreq.c 5.88 KB
420993221   Deepak Sikri   cpufreq: SPEAr: A...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  /*
   * drivers/cpufreq/spear-cpufreq.c
   *
   * CPU Frequency Scaling for SPEAr platform
   *
   * Copyright (C) 2012 ST Microelectronics
   * Deepak Sikri <deepak.sikri@st.com>
   *
   * This file is licensed under the terms of the GNU General Public
   * License version 2. This program is licensed "as is" without any
   * warranty of any kind, whether express or implied.
   */
  
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  
  #include <linux/clk.h>
  #include <linux/cpufreq.h>
  #include <linux/err.h>
  #include <linux/init.h>
  #include <linux/module.h>
c0e469487   Sudeep KarkadaNagesha   cpufreq: spear-cp...
21
  #include <linux/of_device.h>
2449d33a4   Viresh Kumar   cpufreq: SPEAr: I...
22
  #include <linux/platform_device.h>
420993221   Deepak Sikri   cpufreq: SPEAr: A...
23
24
25
26
27
28
29
30
31
32
  #include <linux/slab.h>
  #include <linux/types.h>
  
  /* SPEAr CPUFreq driver data structure */
  static struct {
  	struct clk *clk;
  	unsigned int transition_latency;
  	struct cpufreq_frequency_table *freq_tbl;
  	u32 cnt;
  } spear_cpufreq;
420993221   Deepak Sikri   cpufreq: SPEAr: A...
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
  static struct clk *spear1340_cpu_get_possible_parent(unsigned long newfreq)
  {
  	struct clk *sys_pclk;
  	int pclk;
  	/*
  	 * In SPEAr1340, cpu clk's parent sys clk can take input from
  	 * following sources
  	 */
  	const char *sys_clk_src[] = {
  		"sys_syn_clk",
  		"pll1_clk",
  		"pll2_clk",
  		"pll3_clk",
  	};
  
  	/*
  	 * As sys clk can have multiple source with their own range
  	 * limitation so we choose possible sources accordingly
  	 */
  	if (newfreq <= 300000000)
  		pclk = 0; /* src is sys_syn_clk */
  	else if (newfreq > 300000000 && newfreq <= 500000000)
  		pclk = 3; /* src is pll3_clk */
  	else if (newfreq == 600000000)
  		pclk = 1; /* src is pll1_clk */
  	else
  		return ERR_PTR(-EINVAL);
  
  	/* Get parent to sys clock */
  	sys_pclk = clk_get(NULL, sys_clk_src[pclk]);
  	if (IS_ERR(sys_pclk))
  		pr_err("Failed to get %s clock
  ", sys_clk_src[pclk]);
  
  	return sys_pclk;
  }
  
  /*
   * In SPEAr1340, we cannot use newfreq directly because we need to actually
   * access a source clock (clk) which might not be ancestor of cpu at present.
   * Hence in SPEAr1340 we would operate on source clock directly before switching
   * cpu clock to it.
   */
  static int spear1340_set_cpu_rate(struct clk *sys_pclk, unsigned long newfreq)
  {
  	struct clk *sys_clk;
  	int ret = 0;
  
  	sys_clk = clk_get_parent(spear_cpufreq.clk);
  	if (IS_ERR(sys_clk)) {
  		pr_err("failed to get cpu's parent (sys) clock
  ");
  		return PTR_ERR(sys_clk);
  	}
  
  	/* Set the rate of the source clock before changing the parent */
  	ret = clk_set_rate(sys_pclk, newfreq);
  	if (ret) {
  		pr_err("Failed to set sys clk rate to %lu
  ", newfreq);
  		return ret;
  	}
  
  	ret = clk_set_parent(sys_clk, sys_pclk);
  	if (ret) {
  		pr_err("Failed to set sys clk parent
  ");
  		return ret;
  	}
  
  	return 0;
  }
  
  static int spear_cpufreq_target(struct cpufreq_policy *policy,
9c0ebcf78   Viresh Kumar   cpufreq: Implemen...
107
  		unsigned int index)
420993221   Deepak Sikri   cpufreq: SPEAr: A...
108
  {
bb25f13ae   Sachin Kamat   cpufreq: SPEAr: F...
109
  	long newfreq;
420993221   Deepak Sikri   cpufreq: SPEAr: A...
110
  	struct clk *srcclk;
9c0ebcf78   Viresh Kumar   cpufreq: Implemen...
111
  	int ret, mult = 1;
420993221   Deepak Sikri   cpufreq: SPEAr: A...
112

420993221   Deepak Sikri   cpufreq: SPEAr: A...
113
  	newfreq = spear_cpufreq.freq_tbl[index].frequency * 1000;
9c0ebcf78   Viresh Kumar   cpufreq: Implemen...
114

420993221   Deepak Sikri   cpufreq: SPEAr: A...
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
  	if (of_machine_is_compatible("st,spear1340")) {
  		/*
  		 * SPEAr1340 is special in the sense that due to the possibility
  		 * of multiple clock sources for cpu clk's parent we can have
  		 * different clock source for different frequency of cpu clk.
  		 * Hence we need to choose one from amongst these possible clock
  		 * sources.
  		 */
  		srcclk = spear1340_cpu_get_possible_parent(newfreq);
  		if (IS_ERR(srcclk)) {
  			pr_err("Failed to get src clk
  ");
  			return PTR_ERR(srcclk);
  		}
  
  		/* SPEAr1340: src clk is always 2 * intended cpu clk */
  		mult = 2;
  	} else {
  		/*
  		 * src clock to be altered is ancestor of cpu clock. Hence we
  		 * can directly work on cpu clk
  		 */
  		srcclk = spear_cpufreq.clk;
  	}
  
  	newfreq = clk_round_rate(srcclk, newfreq * mult);
189c9b8d9   Paul Walmsley   cpufreq: SPEAr: c...
141
  	if (newfreq <= 0) {
420993221   Deepak Sikri   cpufreq: SPEAr: A...
142
143
144
145
  		pr_err("clk_round_rate failed for cpu src clock
  ");
  		return newfreq;
  	}
420993221   Deepak Sikri   cpufreq: SPEAr: A...
146
147
148
149
  	if (mult == 2)
  		ret = spear1340_set_cpu_rate(srcclk, newfreq);
  	else
  		ret = clk_set_rate(spear_cpufreq.clk, newfreq);
d4019f0a9   Viresh Kumar   cpufreq: move fre...
150
  	if (ret)
420993221   Deepak Sikri   cpufreq: SPEAr: A...
151
152
  		pr_err("CPU Freq: cpu clk_set_rate failed: %d
  ", ret);
420993221   Deepak Sikri   cpufreq: SPEAr: A...
153

420993221   Deepak Sikri   cpufreq: SPEAr: A...
154
155
156
157
158
  	return ret;
  }
  
  static int spear_cpufreq_init(struct cpufreq_policy *policy)
  {
652ed95d5   Viresh Kumar   cpufreq: introduc...
159
  	policy->clk = spear_cpufreq.clk;
7a936bd08   Viresh Kumar   cpufreq: spear: u...
160
161
  	return cpufreq_generic_init(policy, spear_cpufreq.freq_tbl,
  			spear_cpufreq.transition_latency);
420993221   Deepak Sikri   cpufreq: SPEAr: A...
162
  }
420993221   Deepak Sikri   cpufreq: SPEAr: A...
163
164
  static struct cpufreq_driver spear_cpufreq_driver = {
  	.name		= "cpufreq-spear",
ae6b42713   Viresh Kumar   cpufreq: Mark ARM...
165
  	.flags		= CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
e2132fa66   Viresh Kumar   cpufreq: spear: U...
166
  	.verify		= cpufreq_generic_frequency_table_verify,
9c0ebcf78   Viresh Kumar   cpufreq: Implemen...
167
  	.target_index	= spear_cpufreq_target,
652ed95d5   Viresh Kumar   cpufreq: introduc...
168
  	.get		= cpufreq_generic_get,
420993221   Deepak Sikri   cpufreq: SPEAr: A...
169
  	.init		= spear_cpufreq_init,
e2132fa66   Viresh Kumar   cpufreq: spear: U...
170
  	.attr		= cpufreq_generic_attr,
420993221   Deepak Sikri   cpufreq: SPEAr: A...
171
  };
2449d33a4   Viresh Kumar   cpufreq: SPEAr: I...
172
  static int spear_cpufreq_probe(struct platform_device *pdev)
420993221   Deepak Sikri   cpufreq: SPEAr: A...
173
174
175
176
177
178
  {
  	struct device_node *np;
  	const struct property *prop;
  	struct cpufreq_frequency_table *freq_tbl;
  	const __be32 *val;
  	int cnt, i, ret;
c0e469487   Sudeep KarkadaNagesha   cpufreq: spear-cp...
179
  	np = of_cpu_device_node_get(0);
420993221   Deepak Sikri   cpufreq: SPEAr: A...
180
  	if (!np) {
699b52528   Arvind Yadav   cpufreq: SPEAr: p...
181
182
  		pr_err("No cpu node found
  ");
420993221   Deepak Sikri   cpufreq: SPEAr: A...
183
184
185
186
187
188
189
190
191
  		return -ENODEV;
  	}
  
  	if (of_property_read_u32(np, "clock-latency",
  				&spear_cpufreq.transition_latency))
  		spear_cpufreq.transition_latency = CPUFREQ_ETERNAL;
  
  	prop = of_find_property(np, "cpufreq_tbl", NULL);
  	if (!prop || !prop->value) {
699b52528   Arvind Yadav   cpufreq: SPEAr: p...
192
193
  		pr_err("Invalid cpufreq_tbl
  ");
420993221   Deepak Sikri   cpufreq: SPEAr: A...
194
195
196
197
198
199
  		ret = -ENODEV;
  		goto out_put_node;
  	}
  
  	cnt = prop->length / sizeof(u32);
  	val = prop->value;
6396bb221   Kees Cook   treewide: kzalloc...
200
  	freq_tbl = kcalloc(cnt + 1, sizeof(*freq_tbl), GFP_KERNEL);
420993221   Deepak Sikri   cpufreq: SPEAr: A...
201
202
203
204
  	if (!freq_tbl) {
  		ret = -ENOMEM;
  		goto out_put_node;
  	}
71508a1f4   Viresh Kumar   cpufreq: use kzal...
205
  	for (i = 0; i < cnt; i++)
420993221   Deepak Sikri   cpufreq: SPEAr: A...
206
  		freq_tbl[i].frequency = be32_to_cpup(val++);
420993221   Deepak Sikri   cpufreq: SPEAr: A...
207

420993221   Deepak Sikri   cpufreq: SPEAr: A...
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
  	freq_tbl[i].frequency = CPUFREQ_TABLE_END;
  
  	spear_cpufreq.freq_tbl = freq_tbl;
  
  	of_node_put(np);
  
  	spear_cpufreq.clk = clk_get(NULL, "cpu_clk");
  	if (IS_ERR(spear_cpufreq.clk)) {
  		pr_err("Unable to get CPU clock
  ");
  		ret = PTR_ERR(spear_cpufreq.clk);
  		goto out_put_mem;
  	}
  
  	ret = cpufreq_register_driver(&spear_cpufreq_driver);
  	if (!ret)
  		return 0;
  
  	pr_err("failed register driver: %d
  ", ret);
  	clk_put(spear_cpufreq.clk);
  
  out_put_mem:
  	kfree(freq_tbl);
  	return ret;
  
  out_put_node:
  	of_node_put(np);
  	return ret;
  }
2449d33a4   Viresh Kumar   cpufreq: SPEAr: I...
238
239
240
241
  
  static struct platform_driver spear_cpufreq_platdrv = {
  	.driver = {
  		.name	= "spear-cpufreq",
2449d33a4   Viresh Kumar   cpufreq: SPEAr: I...
242
243
244
245
  	},
  	.probe		= spear_cpufreq_probe,
  };
  module_platform_driver(spear_cpufreq_platdrv);
420993221   Deepak Sikri   cpufreq: SPEAr: A...
246
247
248
249
  
  MODULE_AUTHOR("Deepak Sikri <deepak.sikri@st.com>");
  MODULE_DESCRIPTION("SPEAr CPUFreq driver");
  MODULE_LICENSE("GPL");