Blame view

drivers/cpufreq/s3c24xx-cpufreq.c 15.5 KB
d2912cb15   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
09ec1d7ea   Kukjin Kim   ARM: S3C24XX: Rem...
2
  /*
e02f86645   Ben Dooks   ARM: S3C: Update ...
3
   * Copyright (c) 2006-2008 Simtec Electronics
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
4
5
6
7
   *	http://armlinux.simtec.co.uk/
   *	Ben Dooks <ben@simtec.co.uk>
   *
   * S3C24XX CPU Frequency scaling
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
8
  */
1c5864e26   Joe Perches   cpufreq: Use cons...
9
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
10
11
12
13
14
15
16
17
18
  #include <linux/init.h>
  #include <linux/module.h>
  #include <linux/interrupt.h>
  #include <linux/ioport.h>
  #include <linux/cpufreq.h>
  #include <linux/cpu.h>
  #include <linux/clk.h>
  #include <linux/err.h>
  #include <linux/io.h>
edbaa603e   Kay Sievers   driver-core: remo...
19
  #include <linux/device.h>
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
20
  #include <linux/sysfs.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
21
  #include <linux/slab.h>
81b11a6a0   Arnd Bergmann   ARM: s3c: remove ...
22
23
  #include <linux/soc/samsung/s3c-cpufreq-core.h>
  #include <linux/soc/samsung/s3c-pm.h>
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
24
25
26
  
  #include <asm/mach/arch.h>
  #include <asm/mach/map.h>
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
27
  /* note, cpufreq support deals in kHz, no Hz */
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
28
29
30
31
32
33
34
35
36
37
38
39
40
41
  static struct cpufreq_driver s3c24xx_driver;
  static struct s3c_cpufreq_config cpu_cur;
  static struct s3c_iotimings s3c24xx_iotiming;
  static struct cpufreq_frequency_table *pll_reg;
  static unsigned int last_target = ~0;
  static unsigned int ftab_size;
  static struct cpufreq_frequency_table *ftab;
  
  static struct clk *_clk_mpll;
  static struct clk *_clk_xtal;
  static struct clk *clk_fclk;
  static struct clk *clk_hclk;
  static struct clk *clk_pclk;
  static struct clk *clk_arm;
4a6c41083   Paul Bolle   cpufreq: s3c24xx:...
42
  #ifdef CONFIG_ARM_S3C24XX_CPUFREQ_DEBUGFS
e6d197a69   Ben Dooks   ARM: S3C: CPUFREQ...
43
44
45
46
47
48
49
50
51
  struct s3c_cpufreq_config *s3c_cpufreq_getconfig(void)
  {
  	return &cpu_cur;
  }
  
  struct s3c_iotimings *s3c_cpufreq_getiotimings(void)
  {
  	return &s3c24xx_iotiming;
  }
4a6c41083   Paul Bolle   cpufreq: s3c24xx:...
52
  #endif /* CONFIG_ARM_S3C24XX_CPUFREQ_DEBUGFS */
e6d197a69   Ben Dooks   ARM: S3C: CPUFREQ...
53

2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
54
55
56
57
58
59
60
61
  static void s3c_cpufreq_getcur(struct s3c_cpufreq_config *cfg)
  {
  	unsigned long fclk, pclk, hclk, armclk;
  
  	cfg->freq.fclk = fclk = clk_get_rate(clk_fclk);
  	cfg->freq.hclk = hclk = clk_get_rate(clk_hclk);
  	cfg->freq.pclk = pclk = clk_get_rate(clk_pclk);
  	cfg->freq.armclk = armclk = clk_get_rate(clk_arm);
c38758e3d   Arnd Bergmann   cpufreq: s3c24xx:...
62
  	cfg->pll.driver_data = s3c24xx_read_mpllcon();
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
  	cfg->pll.frequency = fclk;
  
  	cfg->freq.hclk_tns = 1000000000 / (cfg->freq.hclk / 10);
  
  	cfg->divs.h_divisor = fclk / hclk;
  	cfg->divs.p_divisor = fclk / pclk;
  }
  
  static inline void s3c_cpufreq_calc(struct s3c_cpufreq_config *cfg)
  {
  	unsigned long pll = cfg->pll.frequency;
  
  	cfg->freq.fclk = pll;
  	cfg->freq.hclk = pll / cfg->divs.h_divisor;
  	cfg->freq.pclk = pll / cfg->divs.p_divisor;
  
  	/* convert hclk into 10ths of nanoseconds for io calcs */
  	cfg->freq.hclk_tns = 1000000000 / (cfg->freq.hclk / 10);
  }
  
  static inline int closer(unsigned int target, unsigned int n, unsigned int c)
  {
  	int diff_cur = abs(target - c);
  	int diff_new = abs(target - n);
  
  	return (diff_new < diff_cur);
  }
  
  static void s3c_cpufreq_show(const char *pfx,
  				 struct s3c_cpufreq_config *cfg)
  {
  	s3c_freq_dbg("%s: Fvco=%u, F=%lu, A=%lu, H=%lu (%u), P=%lu (%u)
  ",
  		     pfx, cfg->pll.frequency, cfg->freq.fclk, cfg->freq.armclk,
  		     cfg->freq.hclk, cfg->divs.h_divisor,
  		     cfg->freq.pclk, cfg->divs.p_divisor);
  }
  
  /* functions to wrapper the driver info calls to do the cpu specific work */
  
  static void s3c_cpufreq_setio(struct s3c_cpufreq_config *cfg)
  {
  	if (cfg->info->set_iotiming)
  		(cfg->info->set_iotiming)(cfg, &s3c24xx_iotiming);
  }
  
  static int s3c_cpufreq_calcio(struct s3c_cpufreq_config *cfg)
  {
  	if (cfg->info->calc_iotiming)
  		return (cfg->info->calc_iotiming)(cfg, &s3c24xx_iotiming);
  
  	return 0;
  }
  
  static void s3c_cpufreq_setrefresh(struct s3c_cpufreq_config *cfg)
  {
  	(cfg->info->set_refresh)(cfg);
  }
  
  static void s3c_cpufreq_setdivs(struct s3c_cpufreq_config *cfg)
  {
  	(cfg->info->set_divs)(cfg);
  }
  
  static int s3c_cpufreq_calcdivs(struct s3c_cpufreq_config *cfg)
  {
  	return (cfg->info->calc_divs)(cfg);
  }
  
  static void s3c_cpufreq_setfvco(struct s3c_cpufreq_config *cfg)
  {
d8b532578   Heiko Stuebner   ARM: S3C24XX: cpu...
134
  	cfg->mpll = _clk_mpll;
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
135
136
  	(cfg->info->set_fvco)(cfg);
  }
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
  static inline void s3c_cpufreq_updateclk(struct clk *clk,
  					 unsigned int freq)
  {
  	clk_set_rate(clk, freq);
  }
  
  static int s3c_cpufreq_settarget(struct cpufreq_policy *policy,
  				 unsigned int target_freq,
  				 struct cpufreq_frequency_table *pll)
  {
  	struct s3c_cpufreq_freqs freqs;
  	struct s3c_cpufreq_config cpu_new;
  	unsigned long flags;
  
  	cpu_new = cpu_cur;  /* copy new from current */
  
  	s3c_cpufreq_show("cur", &cpu_cur);
  
  	/* TODO - check for DMA currently outstanding */
  
  	cpu_new.pll = pll ? *pll : cpu_cur.pll;
  
  	if (pll)
  		freqs.pll_changing = 1;
  
  	/* update our frequencies */
  
  	cpu_new.freq.armclk = target_freq;
  	cpu_new.freq.fclk = cpu_new.pll.frequency;
  
  	if (s3c_cpufreq_calcdivs(&cpu_new) < 0) {
b49c22a6c   Joe Perches   cpufreq: Convert ...
168
169
  		pr_err("no divisors for %d
  ", target_freq);
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
170
171
172
173
174
175
176
177
178
179
180
181
182
  		goto err_notpossible;
  	}
  
  	s3c_freq_dbg("%s: got divs
  ", __func__);
  
  	s3c_cpufreq_calc(&cpu_new);
  
  	s3c_freq_dbg("%s: calculated frequencies for new
  ", __func__);
  
  	if (cpu_new.freq.hclk != cpu_cur.freq.hclk) {
  		if (s3c_cpufreq_calcio(&cpu_new) < 0) {
b49c22a6c   Joe Perches   cpufreq: Convert ...
183
184
  			pr_err("%s: no IO timings
  ", __func__);
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
185
186
187
188
189
190
191
192
193
194
  			goto err_notpossible;
  		}
  	}
  
  	s3c_cpufreq_show("new", &cpu_new);
  
  	/* setup our cpufreq parameters */
  
  	freqs.old = cpu_cur.freq;
  	freqs.new = cpu_new.freq;
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
195
196
197
198
199
200
201
202
203
204
205
206
207
  	freqs.freqs.old = cpu_cur.freq.armclk / 1000;
  	freqs.freqs.new = cpu_new.freq.armclk / 1000;
  
  	/* update f/h/p clock settings before we issue the change
  	 * notification, so that drivers do not need to do anything
  	 * special if they want to recalculate on CPUFREQ_PRECHANGE. */
  
  	s3c_cpufreq_updateclk(_clk_mpll, cpu_new.pll.frequency);
  	s3c_cpufreq_updateclk(clk_fclk, cpu_new.freq.fclk);
  	s3c_cpufreq_updateclk(clk_hclk, cpu_new.freq.hclk);
  	s3c_cpufreq_updateclk(clk_pclk, cpu_new.freq.pclk);
  
  	/* start the frequency change */
8fec051ee   Viresh Kumar   cpufreq: Convert ...
208
  	cpufreq_freq_transition_begin(policy, &freqs.freqs);
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
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
238
239
240
241
242
243
244
245
246
247
248
249
250
251
  
  	/* If hclk is staying the same, then we do not need to
  	 * re-write the IO or the refresh timings whilst we are changing
  	 * speed. */
  
  	local_irq_save(flags);
  
  	/* is our memory clock slowing down? */
  	if (cpu_new.freq.hclk < cpu_cur.freq.hclk) {
  		s3c_cpufreq_setrefresh(&cpu_new);
  		s3c_cpufreq_setio(&cpu_new);
  	}
  
  	if (cpu_new.freq.fclk == cpu_cur.freq.fclk) {
  		/* not changing PLL, just set the divisors */
  
  		s3c_cpufreq_setdivs(&cpu_new);
  	} else {
  		if (cpu_new.freq.fclk < cpu_cur.freq.fclk) {
  			/* slow the cpu down, then set divisors */
  
  			s3c_cpufreq_setfvco(&cpu_new);
  			s3c_cpufreq_setdivs(&cpu_new);
  		} else {
  			/* set the divisors, then speed up */
  
  			s3c_cpufreq_setdivs(&cpu_new);
  			s3c_cpufreq_setfvco(&cpu_new);
  		}
  	}
  
  	/* did our memory clock speed up */
  	if (cpu_new.freq.hclk > cpu_cur.freq.hclk) {
  		s3c_cpufreq_setrefresh(&cpu_new);
  		s3c_cpufreq_setio(&cpu_new);
  	}
  
  	/* update our current settings */
  	cpu_cur = cpu_new;
  
  	local_irq_restore(flags);
  
  	/* notify everyone we've done this */
8fec051ee   Viresh Kumar   cpufreq: Convert ...
252
  	cpufreq_freq_transition_end(policy, &freqs.freqs, 0);
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
253
254
255
256
257
258
  
  	s3c_freq_dbg("%s: finished
  ", __func__);
  	return 0;
  
   err_notpossible:
b49c22a6c   Joe Perches   cpufreq: Convert ...
259
260
  	pr_err("no compatible settings for %d
  ", target_freq);
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
  	return -EINVAL;
  }
  
  /* s3c_cpufreq_target
   *
   * called by the cpufreq core to adjust the frequency that the CPU
   * is currently running at.
   */
  
  static int s3c_cpufreq_target(struct cpufreq_policy *policy,
  			      unsigned int target_freq,
  			      unsigned int relation)
  {
  	struct cpufreq_frequency_table *pll;
  	unsigned int index;
  
  	/* avoid repeated calls which cause a needless amout of duplicated
  	 * logging output (and CPU time as the calculation process is
  	 * done) */
  	if (target_freq == last_target)
  		return 0;
  
  	last_target = target_freq;
  
  	s3c_freq_dbg("%s: policy %p, target %u, relation %u
  ",
  		     __func__, policy, target_freq, relation);
  
  	if (ftab) {
d218ed773   Viresh Kumar   cpufreq: Return i...
290
291
  		index = cpufreq_frequency_table_target(policy, target_freq,
  						       relation);
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
  
  		s3c_freq_dbg("%s: adjust %d to entry %d (%u)
  ", __func__,
  			     target_freq, index, ftab[index].frequency);
  		target_freq = ftab[index].frequency;
  	}
  
  	target_freq *= 1000;  /* convert target to Hz */
  
  	/* find the settings for our new frequency */
  
  	if (!pll_reg || cpu_cur.lock_pll) {
  		/* either we've not got any PLL values, or we've locked
  		 * to the current one. */
  		pll = NULL;
  	} else {
  		struct cpufreq_policy tmp_policy;
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
309
310
311
312
313
314
315
  
  		/* we keep the cpu pll table in Hz, to ensure we get an
  		 * accurate value for the PLL output. */
  
  		tmp_policy.min = policy->min * 1000;
  		tmp_policy.max = policy->max * 1000;
  		tmp_policy.cpu = policy->cpu;
7ab4aabba   Viresh Kumar   cpufreq: Drop fre...
316
  		tmp_policy.freq_table = pll_reg;
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
317

d218ed773   Viresh Kumar   cpufreq: Return i...
318
319
  		/* cpufreq_frequency_table_target returns the index
  		 * of the table entry, not the value of
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
320
  		 * the table entry's index field. */
d218ed773   Viresh Kumar   cpufreq: Return i...
321
322
  		index = cpufreq_frequency_table_target(&tmp_policy, target_freq,
  						       relation);
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
323
324
325
326
327
328
329
330
331
332
  		pll = pll_reg + index;
  
  		s3c_freq_dbg("%s: target %u => %u
  ",
  			     __func__, target_freq, pll->frequency);
  
  		target_freq = pll->frequency;
  	}
  
  	return s3c_cpufreq_settarget(policy, target_freq, pll);
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
333
  }
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
334
335
336
337
338
339
  struct clk *s3c_cpufreq_clk_get(struct device *dev, const char *name)
  {
  	struct clk *clk;
  
  	clk = clk_get(dev, name);
  	if (IS_ERR(clk))
1c5864e26   Joe Perches   cpufreq: Use cons...
340
341
  		pr_err("failed to get clock '%s'
  ", name);
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
342
343
344
345
346
347
  
  	return clk;
  }
  
  static int s3c_cpufreq_init(struct cpufreq_policy *policy)
  {
652ed95d5   Viresh Kumar   cpufreq: introduc...
348
  	policy->clk = clk_arm;
0373ca748   Viresh Kumar   cpufreq: s3c24xx:...
349
  	policy->cpuinfo.transition_latency = cpu_cur.info->latency;
09e390f30   Viresh Kumar   cpufreq: s3c24xx:...
350
  	policy->freq_table = ftab;
0373ca748   Viresh Kumar   cpufreq: s3c24xx:...
351
352
  
  	return 0;
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
353
  }
21b4c415e   Hanjun Guo   cpufreq / s3c24xx...
354
  static int __init s3c_cpufreq_initclks(void)
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
355
356
357
358
359
360
361
362
363
364
  {
  	_clk_mpll = s3c_cpufreq_clk_get(NULL, "mpll");
  	_clk_xtal = s3c_cpufreq_clk_get(NULL, "xtal");
  	clk_fclk = s3c_cpufreq_clk_get(NULL, "fclk");
  	clk_hclk = s3c_cpufreq_clk_get(NULL, "hclk");
  	clk_pclk = s3c_cpufreq_clk_get(NULL, "pclk");
  	clk_arm = s3c_cpufreq_clk_get(NULL, "armclk");
  
  	if (IS_ERR(clk_fclk) || IS_ERR(clk_hclk) || IS_ERR(clk_pclk) ||
  	    IS_ERR(_clk_mpll) || IS_ERR(clk_arm) || IS_ERR(_clk_xtal)) {
b49c22a6c   Joe Perches   cpufreq: Convert ...
365
366
  		pr_err("%s: could not get clock(s)
  ", __func__);
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
367
368
  		return -ENOENT;
  	}
b49c22a6c   Joe Perches   cpufreq: Convert ...
369
370
371
372
373
374
375
  	pr_info("%s: clocks f=%lu,h=%lu,p=%lu,a=%lu
  ",
  		__func__,
  		clk_get_rate(clk_fclk) / 1000,
  		clk_get_rate(clk_hclk) / 1000,
  		clk_get_rate(clk_pclk) / 1000,
  		clk_get_rate(clk_arm) / 1000);
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
376
377
378
  
  	return 0;
  }
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
379
380
381
  #ifdef CONFIG_PM
  static struct cpufreq_frequency_table suspend_pll;
  static unsigned int suspend_freq;
7ca64e2d2   Rafael J. Wysocki   [CPUFREQ] Remove ...
382
  static int s3c_cpufreq_suspend(struct cpufreq_policy *policy)
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
383
384
  {
  	suspend_pll.frequency = clk_get_rate(_clk_mpll);
c38758e3d   Arnd Bergmann   cpufreq: s3c24xx:...
385
  	suspend_pll.driver_data = s3c24xx_read_mpllcon();
652ed95d5   Viresh Kumar   cpufreq: introduc...
386
  	suspend_freq = clk_get_rate(clk_arm);
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
387
388
389
390
391
392
393
394
395
396
397
398
  
  	return 0;
  }
  
  static int s3c_cpufreq_resume(struct cpufreq_policy *policy)
  {
  	int ret;
  
  	s3c_freq_dbg("%s: resuming with policy %p
  ", __func__, policy);
  
  	last_target = ~0;	/* invalidate last_target setting */
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
399
400
  	/* whilst we will be called later on, we try and re-set the
  	 * cpu frequencies as soon as possible so that we do not end
25985edce   Lucas De Marchi   Fix common misspe...
401
  	 * up resuming devices and then immediately having to re-set
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
402
403
404
405
406
407
408
409
410
  	 * a number of settings once these devices have restarted.
  	 *
  	 * as a note, it is expected devices are not used until they
  	 * have been un-suspended and at that time they should have
  	 * used the updated clock settings.
  	 */
  
  	ret = s3c_cpufreq_settarget(NULL, suspend_freq, &suspend_pll);
  	if (ret) {
b49c22a6c   Joe Perches   cpufreq: Convert ...
411
412
  		pr_err("%s: failed to reset pll/freq
  ", __func__);
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
413
414
415
416
417
418
419
420
421
422
423
  		return ret;
  	}
  
  	return 0;
  }
  #else
  #define s3c_cpufreq_resume NULL
  #define s3c_cpufreq_suspend NULL
  #endif
  
  static struct cpufreq_driver s3c24xx_driver = {
ae6b42713   Viresh Kumar   cpufreq: Mark ARM...
424
  	.flags		= CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
425
  	.target		= s3c_cpufreq_target,
652ed95d5   Viresh Kumar   cpufreq: introduc...
426
  	.get		= cpufreq_generic_get,
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
427
428
429
430
431
  	.init		= s3c_cpufreq_init,
  	.suspend	= s3c_cpufreq_suspend,
  	.resume		= s3c_cpufreq_resume,
  	.name		= "s3c24xx",
  };
61882b631   Arnd Bergmann   cpufreq: s3c: rem...
432
  int s3c_cpufreq_register(struct s3c_cpufreq_info *info)
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
433
434
  {
  	if (!info || !info->name) {
b49c22a6c   Joe Perches   cpufreq: Convert ...
435
436
  		pr_err("%s: failed to pass valid information
  ", __func__);
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
437
438
  		return -EINVAL;
  	}
b49c22a6c   Joe Perches   cpufreq: Convert ...
439
440
441
  	pr_info("S3C24XX CPU Frequency driver, %s cpu support
  ",
  		info->name);
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
  
  	/* check our driver info has valid data */
  
  	BUG_ON(info->set_refresh == NULL);
  	BUG_ON(info->set_divs == NULL);
  	BUG_ON(info->calc_divs == NULL);
  
  	/* info->set_fvco is optional, depending on whether there
  	 * is a need to set the clock code. */
  
  	cpu_cur.info = info;
  
  	/* Note, driver registering should probably update locktime */
  
  	return 0;
  }
  
  int __init s3c_cpufreq_setboard(struct s3c_cpufreq_board *board)
  {
  	struct s3c_cpufreq_board *ours;
  
  	if (!board) {
b49c22a6c   Joe Perches   cpufreq: Convert ...
464
465
  		pr_info("%s: no board data
  ", __func__);
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
466
467
468
469
470
  		return -EINVAL;
  	}
  
  	/* Copy the board information so that each board can make this
  	 * initdata. */
d5b73cd87   Viresh Kumar   cpufreq: Use size...
471
  	ours = kzalloc(sizeof(*ours), GFP_KERNEL);
d430fb998   Markus Elfring   cpufreq: s3c24xx:...
472
  	if (!ours)
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
473
  		return -ENOMEM;
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
474
475
476
477
478
479
  
  	*ours = *board;
  	cpu_cur.board = ours;
  
  	return 0;
  }
87ae97f10   Sachin Kamat   cpufreq: s3c24xx:...
480
  static int __init s3c_cpufreq_auto_io(void)
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
481
482
483
484
  {
  	int ret;
  
  	if (!cpu_cur.info->get_iotiming) {
b49c22a6c   Joe Perches   cpufreq: Convert ...
485
486
  		pr_err("%s: get_iotiming undefined
  ", __func__);
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
487
488
  		return -ENOENT;
  	}
b49c22a6c   Joe Perches   cpufreq: Convert ...
489
490
  	pr_info("%s: working out IO settings
  ", __func__);
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
491
492
493
  
  	ret = (cpu_cur.info->get_iotiming)(&cpu_cur, &s3c24xx_iotiming);
  	if (ret)
b49c22a6c   Joe Perches   cpufreq: Convert ...
494
495
  		pr_err("%s: failed to get timings
  ", __func__);
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
  
  	return ret;
  }
  
  /* if one or is zero, then return the other, otherwise return the min */
  #define do_min(_a, _b) ((_a) == 0 ? (_b) : (_b) == 0 ? (_a) : min(_a, _b))
  
  /**
   * s3c_cpufreq_freq_min - find the minimum settings for the given freq.
   * @dst: The destination structure
   * @a: One argument.
   * @b: The other argument.
   *
   * Create a minimum of each frequency entry in the 'struct s3c_freq',
   * unless the entry is zero when it is ignored and the non-zero argument
   * used.
   */
  static void s3c_cpufreq_freq_min(struct s3c_freq *dst,
  				 struct s3c_freq *a, struct s3c_freq *b)
  {
  	dst->fclk = do_min(a->fclk, b->fclk);
  	dst->hclk = do_min(a->hclk, b->hclk);
  	dst->pclk = do_min(a->pclk, b->pclk);
  	dst->armclk = do_min(a->armclk, b->armclk);
  }
  
  static inline u32 calc_locktime(u32 freq, u32 time_us)
  {
  	u32 result;
  
  	result = freq * time_us;
  	result = DIV_ROUND_UP(result, 1000 * 1000);
  
  	return result;
  }
  
  static void s3c_cpufreq_update_loctkime(void)
  {
  	unsigned int bits = cpu_cur.info->locktime_bits;
  	u32 rate = (u32)clk_get_rate(_clk_xtal);
  	u32 val;
  
  	if (bits == 0) {
  		WARN_ON(1);
  		return;
  	}
  
  	val = calc_locktime(rate, cpu_cur.info->locktime_u) << bits;
  	val |= calc_locktime(rate, cpu_cur.info->locktime_m);
b49c22a6c   Joe Perches   cpufreq: Convert ...
545
546
  	pr_info("%s: new locktime is 0x%08x
  ", __func__, val);
c38758e3d   Arnd Bergmann   cpufreq: s3c24xx:...
547
  	s3c24xx_write_locktime(val);
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
548
549
550
551
552
  }
  
  static int s3c_cpufreq_build_freq(void)
  {
  	int size, ret;
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
553
  	kfree(ftab);
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
554
555
556
  
  	size = cpu_cur.info->calc_freqtable(&cpu_cur, NULL, 0);
  	size++;
6396bb221   Kees Cook   treewide: kzalloc...
557
  	ftab = kcalloc(size, sizeof(*ftab), GFP_KERNEL);
d430fb998   Markus Elfring   cpufreq: s3c24xx:...
558
  	if (!ftab)
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
559
  		return -ENOMEM;
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
  
  	ftab_size = size;
  
  	ret = cpu_cur.info->calc_freqtable(&cpu_cur, ftab, size);
  	s3c_cpufreq_addfreq(ftab, ret, size, CPUFREQ_TABLE_END);
  
  	return 0;
  }
  
  static int __init s3c_cpufreq_initcall(void)
  {
  	int ret = 0;
  
  	if (cpu_cur.info && cpu_cur.board) {
  		ret = s3c_cpufreq_initclks();
  		if (ret)
  			goto out;
  
  		/* get current settings */
  		s3c_cpufreq_getcur(&cpu_cur);
  		s3c_cpufreq_show("cur", &cpu_cur);
  
  		if (cpu_cur.board->auto_io) {
  			ret = s3c_cpufreq_auto_io();
  			if (ret) {
b49c22a6c   Joe Perches   cpufreq: Convert ...
585
586
  				pr_err("%s: failed to get io timing
  ",
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
587
588
589
590
591
592
  				       __func__);
  				goto out;
  			}
  		}
  
  		if (cpu_cur.board->need_io && !cpu_cur.info->set_iotiming) {
b49c22a6c   Joe Perches   cpufreq: Convert ...
593
594
  			pr_err("%s: no IO support registered
  ", __func__);
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
  			ret = -EINVAL;
  			goto out;
  		}
  
  		if (!cpu_cur.info->need_pll)
  			cpu_cur.lock_pll = 1;
  
  		s3c_cpufreq_update_loctkime();
  
  		s3c_cpufreq_freq_min(&cpu_cur.max, &cpu_cur.board->max,
  				     &cpu_cur.info->max);
  
  		if (cpu_cur.info->calc_freqtable)
  			s3c_cpufreq_build_freq();
  
  		ret = cpufreq_register_driver(&s3c24xx_driver);
  	}
  
   out:
  	return ret;
  }
  
  late_initcall(s3c_cpufreq_initcall);
  
  /**
   * s3c_plltab_register - register CPU PLL table.
   * @plls: The list of PLL entries.
   * @plls_no: The size of the PLL entries @plls.
   *
   * Register the given set of PLLs with the system.
   */
62f49ee26   Arnd Bergmann   cpufreq: s3c24xx:...
626
  int s3c_plltab_register(struct cpufreq_frequency_table *plls,
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
627
628
629
630
  			       unsigned int plls_no)
  {
  	struct cpufreq_frequency_table *vals;
  	unsigned int size;
d5b73cd87   Viresh Kumar   cpufreq: Use size...
631
  	size = sizeof(*vals) * (plls_no + 1);
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
632

71508a1f4   Viresh Kumar   cpufreq: use kzal...
633
  	vals = kzalloc(size, GFP_KERNEL);
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
634
635
636
637
638
639
640
641
  	if (vals) {
  		memcpy(vals, plls, size);
  		pll_reg = vals;
  
  		/* write a terminating entry, we don't store it in the
  		 * table that is stored in the kernel */
  		vals += plls_no;
  		vals->frequency = CPUFREQ_TABLE_END;
1c5864e26   Joe Perches   cpufreq: Use cons...
642
643
  		pr_info("%d PLL entries
  ", plls_no);
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
644
  	} else
1c5864e26   Joe Perches   cpufreq: Use cons...
645
646
  		pr_err("no memory for PLL tables
  ");
2e4ea6e82   Ben Dooks   ARM: S3C24XX: CPU...
647
648
649
  
  	return vals ? 0 : -ENOMEM;
  }