Linux Audio

Check our new training course

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