Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.13.7.
  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 *freq;
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	freq = s3c_freq->freq_table;
279	while (count > 0 && freq->frequency != CPUFREQ_TABLE_END) {
280		if (freq->frequency == CPUFREQ_ENTRY_INVALID)
281			continue;
282
283		dvfs = &s3c2416_dvfs_table[freq->driver_data];
284		found = 0;
285
286		/* Check only the min-voltage, more is always ok on S3C2416 */
287		for (i = 0; i < count; i++) {
288			v = regulator_list_voltage(s3c_freq->vddarm, i);
289			if (v >= dvfs->vddarm_min)
290				found = 1;
291		}
292
293		if (!found) {
294			pr_debug("cpufreq: %dkHz unsupported by regulator\n",
295				 freq->frequency);
296			freq->frequency = CPUFREQ_ENTRY_INVALID;
297		}
298
299		freq++;
300	}
301
302	/* Guessed */
303	s3c_freq->regulator_latency = 1 * 1000 * 1000;
304}
305#endif
306
307static int s3c2416_cpufreq_reboot_notifier_evt(struct notifier_block *this,
308					       unsigned long event, void *ptr)
309{
310	struct s3c2416_data *s3c_freq = &s3c2416_cpufreq;
311	int ret;
312
313	mutex_lock(&cpufreq_lock);
314
315	/* disable further changes */
316	s3c_freq->disable_dvs = 1;
317
318	mutex_unlock(&cpufreq_lock);
319
320	/* some boards don't reconfigure the regulator on reboot, which
321	 * could lead to undervolting the cpu when the clock is reset.
322	 * Therefore we always leave the DVS mode on reboot.
323	 */
324	if (s3c_freq->is_dvs) {
325		pr_debug("cpufreq: leave dvs on reboot\n");
326		ret = cpufreq_driver_target(cpufreq_cpu_get(0), FREQ_SLEEP, 0);
327		if (ret < 0)
328			return NOTIFY_BAD;
329	}
330
331	return NOTIFY_DONE;
332}
333
334static struct notifier_block s3c2416_cpufreq_reboot_notifier = {
335	.notifier_call = s3c2416_cpufreq_reboot_notifier_evt,
336};
337
338static int __init s3c2416_cpufreq_driver_init(struct cpufreq_policy *policy)
339{
340	struct s3c2416_data *s3c_freq = &s3c2416_cpufreq;
341	struct cpufreq_frequency_table *freq;
342	struct clk *msysclk;
343	unsigned long rate;
344	int ret;
345
346	if (policy->cpu != 0)
347		return -EINVAL;
348
349	msysclk = clk_get(NULL, "msysclk");
350	if (IS_ERR(msysclk)) {
351		ret = PTR_ERR(msysclk);
352		pr_err("cpufreq: Unable to obtain msysclk: %d\n", ret);
353		return ret;
354	}
355
356	/*
357	 * S3C2416 and S3C2450 share the same processor-ID and also provide no
358	 * other means to distinguish them other than through the rate of
359	 * msysclk. On S3C2416 msysclk runs at 800MHz and on S3C2450 at 533MHz.
360	 */
361	rate = clk_get_rate(msysclk);
362	if (rate == 800 * 1000 * 1000) {
363		pr_info("cpufreq: msysclk running at %lukHz, using S3C2416 frequency table\n",
364			rate / 1000);
365		s3c_freq->freq_table = s3c2416_freq_table;
366		policy->cpuinfo.max_freq = 400000;
367	} else if (rate / 1000 == 534000) {
368		pr_info("cpufreq: msysclk running at %lukHz, using S3C2450 frequency table\n",
369			rate / 1000);
370		s3c_freq->freq_table = s3c2450_freq_table;
371		policy->cpuinfo.max_freq = 534000;
372	}
373
374	/* not needed anymore */
375	clk_put(msysclk);
376
377	if (s3c_freq->freq_table == NULL) {
378		pr_err("cpufreq: No frequency information for this CPU, msysclk at %lukHz\n",
379		       rate / 1000);
380		return -ENODEV;
381	}
382
383	s3c_freq->is_dvs = 0;
384
385	s3c_freq->armdiv = clk_get(NULL, "armdiv");
386	if (IS_ERR(s3c_freq->armdiv)) {
387		ret = PTR_ERR(s3c_freq->armdiv);
388		pr_err("cpufreq: Unable to obtain ARMDIV: %d\n", ret);
389		return ret;
390	}
391
392	s3c_freq->hclk = clk_get(NULL, "hclk");
393	if (IS_ERR(s3c_freq->hclk)) {
394		ret = PTR_ERR(s3c_freq->hclk);
395		pr_err("cpufreq: Unable to obtain HCLK: %d\n", ret);
396		goto err_hclk;
397	}
398
399	/* chech hclk rate, we only support the common 133MHz for now
400	 * hclk could also run at 66MHz, but this not often used
401	 */
402	rate = clk_get_rate(s3c_freq->hclk);
403	if (rate < 133 * 1000 * 1000) {
404		pr_err("cpufreq: HCLK not at 133MHz\n");
405		clk_put(s3c_freq->hclk);
406		ret = -EINVAL;
407		goto err_armclk;
408	}
409
410	s3c_freq->armclk = clk_get(NULL, "armclk");
411	if (IS_ERR(s3c_freq->armclk)) {
412		ret = PTR_ERR(s3c_freq->armclk);
413		pr_err("cpufreq: Unable to obtain ARMCLK: %d\n", ret);
414		goto err_armclk;
415	}
416
417#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
418	s3c_freq->vddarm = regulator_get(NULL, "vddarm");
419	if (IS_ERR(s3c_freq->vddarm)) {
420		ret = PTR_ERR(s3c_freq->vddarm);
421		pr_err("cpufreq: Failed to obtain VDDARM: %d\n", ret);
422		goto err_vddarm;
423	}
424
425	s3c2416_cpufreq_cfg_regulator(s3c_freq);
426#else
427	s3c_freq->regulator_latency = 0;
428#endif
429
430	freq = s3c_freq->freq_table;
431	while (freq->frequency != CPUFREQ_TABLE_END) {
432		/* special handling for dvs mode */
433		if (freq->driver_data == 0) {
434			if (!s3c_freq->hclk) {
435				pr_debug("cpufreq: %dkHz unsupported as it would need unavailable dvs mode\n",
436					 freq->frequency);
437				freq->frequency = CPUFREQ_ENTRY_INVALID;
438			} else {
439				freq++;
440				continue;
441			}
442		}
443
444		/* Check for frequencies we can generate */
445		rate = clk_round_rate(s3c_freq->armdiv,
446				      freq->frequency * 1000);
447		rate /= 1000;
448		if (rate != freq->frequency) {
449			pr_debug("cpufreq: %dkHz unsupported by clock (clk_round_rate return %lu)\n",
450				 freq->frequency, rate);
451			freq->frequency = CPUFREQ_ENTRY_INVALID;
452		}
453
454		freq++;
455	}
456
457	/* Datasheet says PLL stabalisation time must be at least 300us,
458	 * so but add some fudge. (reference in LOCKCON0 register description)
459	 */
460	ret = cpufreq_generic_init(policy, s3c_freq->freq_table,
461			(500 * 1000) + s3c_freq->regulator_latency);
462	if (ret)
463		goto err_freq_table;
464
465	register_reboot_notifier(&s3c2416_cpufreq_reboot_notifier);
466
467	return 0;
468
469err_freq_table:
470#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
471	regulator_put(s3c_freq->vddarm);
472err_vddarm:
473#endif
474	clk_put(s3c_freq->armclk);
475err_armclk:
476	clk_put(s3c_freq->hclk);
477err_hclk:
478	clk_put(s3c_freq->armdiv);
479
480	return ret;
481}
482
483static struct cpufreq_driver s3c2416_cpufreq_driver = {
484	.flags		= CPUFREQ_NEED_INITIAL_FREQ_CHECK,
485	.verify		= cpufreq_generic_frequency_table_verify,
486	.target_index	= s3c2416_cpufreq_set_target,
487	.get		= s3c2416_cpufreq_get_speed,
488	.init		= s3c2416_cpufreq_driver_init,
489	.name		= "s3c2416",
490	.attr		= cpufreq_generic_attr,
491};
492
493static int __init s3c2416_cpufreq_init(void)
494{
495	return cpufreq_register_driver(&s3c2416_cpufreq_driver);
496}
497module_init(s3c2416_cpufreq_init);