1/*
2 * S3C2416/2450 CPUfreq Support
3 *
4 * Copyright 2011 Heiko Stuebner <heiko@sntech.de>
5 *
6 * based on s3c64xx_cpufreq.c
7 *
8 * Copyright 2009 Wolfson Microelectronics plc
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 */
14
15#include <linux/kernel.h>
16#include <linux/types.h>
17#include <linux/init.h>
18#include <linux/cpufreq.h>
19#include <linux/clk.h>
20#include <linux/err.h>
21#include <linux/regulator/consumer.h>
22#include <linux/reboot.h>
23#include <linux/module.h>
24
25static DEFINE_MUTEX(cpufreq_lock);
26
27struct s3c2416_data {
28	struct clk *armdiv;
29	struct clk *armclk;
30	struct clk *hclk;
31
32	unsigned long regulator_latency;
33#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
34	struct regulator *vddarm;
35#endif
36
37	struct cpufreq_frequency_table *freq_table;
38
39	bool is_dvs;
40	bool disable_dvs;
41};
42
43static struct s3c2416_data s3c2416_cpufreq;
44
45struct s3c2416_dvfs {
46	unsigned int vddarm_min;
47	unsigned int vddarm_max;
48};
49
50/* pseudo-frequency for dvs mode */
51#define FREQ_DVS	132333
52
53/* frequency to sleep and reboot in
54 * it's essential to leave dvs, as some boards do not reconfigure the
55 * regulator on reboot
56 */
57#define FREQ_SLEEP	133333
58
59/* Sources for the ARMCLK */
60#define SOURCE_HCLK	0
61#define SOURCE_ARMDIV	1
62
63#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
64/* S3C2416 only supports changing the voltage in the dvs-mode.
65 * Voltages down to 1.0V seem to work, so we take what the regulator
66 * can get us.
67 */
68static struct s3c2416_dvfs s3c2416_dvfs_table[] = {
69	[SOURCE_HCLK] = {  950000, 1250000 },
70	[SOURCE_ARMDIV] = { 1250000, 1350000 },
71};
72#endif
73
74static struct cpufreq_frequency_table s3c2416_freq_table[] = {
75	{ 0, SOURCE_HCLK, FREQ_DVS },
76	{ 0, SOURCE_ARMDIV, 133333 },
77	{ 0, SOURCE_ARMDIV, 266666 },
78	{ 0, SOURCE_ARMDIV, 400000 },
79	{ 0, 0, CPUFREQ_TABLE_END },
80};
81
82static struct cpufreq_frequency_table s3c2450_freq_table[] = {
83	{ 0, SOURCE_HCLK, FREQ_DVS },
84	{ 0, SOURCE_ARMDIV, 133500 },
85	{ 0, SOURCE_ARMDIV, 267000 },
86	{ 0, SOURCE_ARMDIV, 534000 },
87	{ 0, 0, CPUFREQ_TABLE_END },
88};
89
90static unsigned int s3c2416_cpufreq_get_speed(unsigned int cpu)
91{
92	struct s3c2416_data *s3c_freq = &s3c2416_cpufreq;
93
94	if (cpu != 0)
95		return 0;
96
97	/* return our pseudo-frequency when in dvs mode */
98	if (s3c_freq->is_dvs)
99		return FREQ_DVS;
100
101	return clk_get_rate(s3c_freq->armclk) / 1000;
102}
103
104static int s3c2416_cpufreq_set_armdiv(struct s3c2416_data *s3c_freq,
105				      unsigned int freq)
106{
107	int ret;
108
109	if (clk_get_rate(s3c_freq->armdiv) / 1000 != freq) {
110		ret = clk_set_rate(s3c_freq->armdiv, freq * 1000);
111		if (ret < 0) {
112			pr_err("cpufreq: Failed to set armdiv rate %dkHz: %d\n",
113			       freq, ret);
114			return ret;
115		}
116	}
117
118	return 0;
119}
120
121static int s3c2416_cpufreq_enter_dvs(struct s3c2416_data *s3c_freq, int idx)
122{
123#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
124	struct s3c2416_dvfs *dvfs;
125#endif
126	int ret;
127
128	if (s3c_freq->is_dvs) {
129		pr_debug("cpufreq: already in dvs mode, nothing to do\n");
130		return 0;
131	}
132
133	pr_debug("cpufreq: switching armclk to hclk (%lukHz)\n",
134		 clk_get_rate(s3c_freq->hclk) / 1000);
135	ret = clk_set_parent(s3c_freq->armclk, s3c_freq->hclk);
136	if (ret < 0) {
137		pr_err("cpufreq: Failed to switch armclk to hclk: %d\n", ret);
138		return ret;
139	}
140
141#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
142	/* changing the core voltage is only allowed when in dvs mode */
143	if (s3c_freq->vddarm) {
144		dvfs = &s3c2416_dvfs_table[idx];
145
146		pr_debug("cpufreq: setting regulator to %d-%d\n",
147			 dvfs->vddarm_min, dvfs->vddarm_max);
148		ret = regulator_set_voltage(s3c_freq->vddarm,
149					    dvfs->vddarm_min,
150					    dvfs->vddarm_max);
151
152		/* when lowering the voltage failed, there is nothing to do */
153		if (ret != 0)
154			pr_err("cpufreq: Failed to set VDDARM: %d\n", ret);
155	}
156#endif
157
158	s3c_freq->is_dvs = 1;
159
160	return 0;
161}
162
163static int s3c2416_cpufreq_leave_dvs(struct s3c2416_data *s3c_freq, int idx)
164{
165#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
166	struct s3c2416_dvfs *dvfs;
167#endif
168	int ret;
169
170	if (!s3c_freq->is_dvs) {
171		pr_debug("cpufreq: not in dvs mode, so can't leave\n");
172		return 0;
173	}
174
175#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
176	if (s3c_freq->vddarm) {
177		dvfs = &s3c2416_dvfs_table[idx];
178
179		pr_debug("cpufreq: setting regulator to %d-%d\n",
180			 dvfs->vddarm_min, dvfs->vddarm_max);
181		ret = regulator_set_voltage(s3c_freq->vddarm,
182					    dvfs->vddarm_min,
183					    dvfs->vddarm_max);
184		if (ret != 0) {
185			pr_err("cpufreq: Failed to set VDDARM: %d\n", ret);
186			return ret;
187		}
188	}
189#endif
190
191	/* force armdiv to hclk frequency for transition from dvs*/
192	if (clk_get_rate(s3c_freq->armdiv) > clk_get_rate(s3c_freq->hclk)) {
193		pr_debug("cpufreq: force armdiv to hclk frequency (%lukHz)\n",
194			 clk_get_rate(s3c_freq->hclk) / 1000);
195		ret = s3c2416_cpufreq_set_armdiv(s3c_freq,
196					clk_get_rate(s3c_freq->hclk) / 1000);
197		if (ret < 0) {
198			pr_err("cpufreq: Failed to set the armdiv to %lukHz: %d\n",
199			       clk_get_rate(s3c_freq->hclk) / 1000, ret);
200			return ret;
201		}
202	}
203
204	pr_debug("cpufreq: switching armclk parent to armdiv (%lukHz)\n",
205			clk_get_rate(s3c_freq->armdiv) / 1000);
206
207	ret = clk_set_parent(s3c_freq->armclk, s3c_freq->armdiv);
208	if (ret < 0) {
209		pr_err("cpufreq: Failed to switch armclk clock parent to armdiv: %d\n",
210		       ret);
211		return ret;
212	}
213
214	s3c_freq->is_dvs = 0;
215
216	return 0;
217}
218
219static int s3c2416_cpufreq_set_target(struct cpufreq_policy *policy,
220				      unsigned int index)
221{
222	struct s3c2416_data *s3c_freq = &s3c2416_cpufreq;
223	unsigned int new_freq;
224	int idx, ret, to_dvs = 0;
225
226	mutex_lock(&cpufreq_lock);
227
228	idx = s3c_freq->freq_table[index].driver_data;
229
230	if (idx == SOURCE_HCLK)
231		to_dvs = 1;
232
233	/* switching to dvs when it's not allowed */
234	if (to_dvs && s3c_freq->disable_dvs) {
235		pr_debug("cpufreq: entering dvs mode not allowed\n");
236		ret = -EINVAL;
237		goto out;
238	}
239
240	/* When leavin dvs mode, always switch the armdiv to the hclk rate
241	 * The S3C2416 has stability issues when switching directly to
242	 * higher frequencies.
243	 */
244	new_freq = (s3c_freq->is_dvs && !to_dvs)
245				? clk_get_rate(s3c_freq->hclk) / 1000
246				: s3c_freq->freq_table[index].frequency;
247
248	if (to_dvs) {
249		pr_debug("cpufreq: enter dvs\n");
250		ret = s3c2416_cpufreq_enter_dvs(s3c_freq, idx);
251	} else if (s3c_freq->is_dvs) {
252		pr_debug("cpufreq: leave dvs\n");
253		ret = s3c2416_cpufreq_leave_dvs(s3c_freq, idx);
254	} else {
255		pr_debug("cpufreq: change armdiv to %dkHz\n", new_freq);
256		ret = s3c2416_cpufreq_set_armdiv(s3c_freq, new_freq);
257	}
258
259out:
260	mutex_unlock(&cpufreq_lock);
261
262	return ret;
263}
264
265#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
266static void __init s3c2416_cpufreq_cfg_regulator(struct s3c2416_data *s3c_freq)
267{
268	int count, v, i, found;
269	struct cpufreq_frequency_table *pos;
270	struct s3c2416_dvfs *dvfs;
271
272	count = regulator_count_voltages(s3c_freq->vddarm);
273	if (count < 0) {
274		pr_err("cpufreq: Unable to check supported voltages\n");
275		return;
276	}
277
278	if (!count)
279		goto out;
280
281	cpufreq_for_each_valid_entry(pos, s3c_freq->freq_table) {
282		dvfs = &s3c2416_dvfs_table[pos->driver_data];
283		found = 0;
284
285		/* Check only the min-voltage, more is always ok on S3C2416 */
286		for (i = 0; i < count; i++) {
287			v = regulator_list_voltage(s3c_freq->vddarm, i);
288			if (v >= dvfs->vddarm_min)
289				found = 1;
290		}
291
292		if (!found) {
293			pr_debug("cpufreq: %dkHz unsupported by regulator\n",
294				 pos->frequency);
295			pos->frequency = CPUFREQ_ENTRY_INVALID;
296		}
297	}
298
299out:
300	/* Guessed */
301	s3c_freq->regulator_latency = 1 * 1000 * 1000;
302}
303#endif
304
305static int s3c2416_cpufreq_reboot_notifier_evt(struct notifier_block *this,
306					       unsigned long event, void *ptr)
307{
308	struct s3c2416_data *s3c_freq = &s3c2416_cpufreq;
309	int ret;
310
311	mutex_lock(&cpufreq_lock);
312
313	/* disable further changes */
314	s3c_freq->disable_dvs = 1;
315
316	mutex_unlock(&cpufreq_lock);
317
318	/* some boards don't reconfigure the regulator on reboot, which
319	 * could lead to undervolting the cpu when the clock is reset.
320	 * Therefore we always leave the DVS mode on reboot.
321	 */
322	if (s3c_freq->is_dvs) {
323		pr_debug("cpufreq: leave dvs on reboot\n");
324		ret = cpufreq_driver_target(cpufreq_cpu_get(0), FREQ_SLEEP, 0);
325		if (ret < 0)
326			return NOTIFY_BAD;
327	}
328
329	return NOTIFY_DONE;
330}
331
332static struct notifier_block s3c2416_cpufreq_reboot_notifier = {
333	.notifier_call = s3c2416_cpufreq_reboot_notifier_evt,
334};
335
336static int __init s3c2416_cpufreq_driver_init(struct cpufreq_policy *policy)
337{
338	struct s3c2416_data *s3c_freq = &s3c2416_cpufreq;
339	struct cpufreq_frequency_table *pos;
340	struct clk *msysclk;
341	unsigned long rate;
342	int ret;
343
344	if (policy->cpu != 0)
345		return -EINVAL;
346
347	msysclk = clk_get(NULL, "msysclk");
348	if (IS_ERR(msysclk)) {
349		ret = PTR_ERR(msysclk);
350		pr_err("cpufreq: Unable to obtain msysclk: %d\n", ret);
351		return ret;
352	}
353
354	/*
355	 * S3C2416 and S3C2450 share the same processor-ID and also provide no
356	 * other means to distinguish them other than through the rate of
357	 * msysclk. On S3C2416 msysclk runs at 800MHz and on S3C2450 at 533MHz.
358	 */
359	rate = clk_get_rate(msysclk);
360	if (rate == 800 * 1000 * 1000) {
361		pr_info("cpufreq: msysclk running at %lukHz, using S3C2416 frequency table\n",
362			rate / 1000);
363		s3c_freq->freq_table = s3c2416_freq_table;
364		policy->cpuinfo.max_freq = 400000;
365	} else if (rate / 1000 == 534000) {
366		pr_info("cpufreq: msysclk running at %lukHz, using S3C2450 frequency table\n",
367			rate / 1000);
368		s3c_freq->freq_table = s3c2450_freq_table;
369		policy->cpuinfo.max_freq = 534000;
370	}
371
372	/* not needed anymore */
373	clk_put(msysclk);
374
375	if (s3c_freq->freq_table == NULL) {
376		pr_err("cpufreq: No frequency information for this CPU, msysclk at %lukHz\n",
377		       rate / 1000);
378		return -ENODEV;
379	}
380
381	s3c_freq->is_dvs = 0;
382
383	s3c_freq->armdiv = clk_get(NULL, "armdiv");
384	if (IS_ERR(s3c_freq->armdiv)) {
385		ret = PTR_ERR(s3c_freq->armdiv);
386		pr_err("cpufreq: Unable to obtain ARMDIV: %d\n", ret);
387		return ret;
388	}
389
390	s3c_freq->hclk = clk_get(NULL, "hclk");
391	if (IS_ERR(s3c_freq->hclk)) {
392		ret = PTR_ERR(s3c_freq->hclk);
393		pr_err("cpufreq: Unable to obtain HCLK: %d\n", ret);
394		goto err_hclk;
395	}
396
397	/* chech hclk rate, we only support the common 133MHz for now
398	 * hclk could also run at 66MHz, but this not often used
399	 */
400	rate = clk_get_rate(s3c_freq->hclk);
401	if (rate < 133 * 1000 * 1000) {
402		pr_err("cpufreq: HCLK not at 133MHz\n");
403		clk_put(s3c_freq->hclk);
404		ret = -EINVAL;
405		goto err_armclk;
406	}
407
408	s3c_freq->armclk = clk_get(NULL, "armclk");
409	if (IS_ERR(s3c_freq->armclk)) {
410		ret = PTR_ERR(s3c_freq->armclk);
411		pr_err("cpufreq: Unable to obtain ARMCLK: %d\n", ret);
412		goto err_armclk;
413	}
414
415#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
416	s3c_freq->vddarm = regulator_get(NULL, "vddarm");
417	if (IS_ERR(s3c_freq->vddarm)) {
418		ret = PTR_ERR(s3c_freq->vddarm);
419		pr_err("cpufreq: Failed to obtain VDDARM: %d\n", ret);
420		goto err_vddarm;
421	}
422
423	s3c2416_cpufreq_cfg_regulator(s3c_freq);
424#else
425	s3c_freq->regulator_latency = 0;
426#endif
427
428	cpufreq_for_each_entry(pos, s3c_freq->freq_table) {
429		/* special handling for dvs mode */
430		if (pos->driver_data == 0) {
431			if (!s3c_freq->hclk) {
432				pr_debug("cpufreq: %dkHz unsupported as it would need unavailable dvs mode\n",
433					 pos->frequency);
434				pos->frequency = CPUFREQ_ENTRY_INVALID;
435			} else {
436				continue;
437			}
438		}
439
440		/* Check for frequencies we can generate */
441		rate = clk_round_rate(s3c_freq->armdiv,
442				      pos->frequency * 1000);
443		rate /= 1000;
444		if (rate != pos->frequency) {
445			pr_debug("cpufreq: %dkHz unsupported by clock (clk_round_rate return %lu)\n",
446				pos->frequency, rate);
447			pos->frequency = CPUFREQ_ENTRY_INVALID;
448		}
449	}
450
451	/* Datasheet says PLL stabalisation time must be at least 300us,
452	 * so but add some fudge. (reference in LOCKCON0 register description)
453	 */
454	ret = cpufreq_generic_init(policy, s3c_freq->freq_table,
455			(500 * 1000) + s3c_freq->regulator_latency);
456	if (ret)
457		goto err_freq_table;
458
459	register_reboot_notifier(&s3c2416_cpufreq_reboot_notifier);
460
461	return 0;
462
463err_freq_table:
464#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
465	regulator_put(s3c_freq->vddarm);
466err_vddarm:
467#endif
468	clk_put(s3c_freq->armclk);
469err_armclk:
470	clk_put(s3c_freq->hclk);
471err_hclk:
472	clk_put(s3c_freq->armdiv);
473
474	return ret;
475}
476
477static struct cpufreq_driver s3c2416_cpufreq_driver = {
478	.flags		= CPUFREQ_NEED_INITIAL_FREQ_CHECK,
479	.verify		= cpufreq_generic_frequency_table_verify,
480	.target_index	= s3c2416_cpufreq_set_target,
481	.get		= s3c2416_cpufreq_get_speed,
482	.init		= s3c2416_cpufreq_driver_init,
483	.name		= "s3c2416",
484	.attr		= cpufreq_generic_attr,
485};
486
487static int __init s3c2416_cpufreq_init(void)
488{
489	return cpufreq_register_driver(&s3c2416_cpufreq_driver);
490}
491module_init(s3c2416_cpufreq_init);
492