Blame view

drivers/cpufreq/omap-cpufreq.c 6.42 KB
ec6bced6c   Tony Lindgren   [PATCH] ARM: 2803...
1
  /*
ffe4f0f11   Nishanth Menon   cpufreq: OMAP: on...
2
   *  CPU frequency scaling for OMAP using OPP information
ec6bced6c   Tony Lindgren   [PATCH] ARM: 2803...
3
4
5
6
7
8
   *
   *  Copyright (C) 2005 Nokia Corporation
   *  Written by Tony Lindgren <tony@atomide.com>
   *
   *  Based on cpu-sa1110.c, Copyright (C) 2001 Russell King
   *
731e0cc63   Santosh Shilimkar   cpufreq: OMAP: cl...
9
10
11
   * Copyright (C) 2007-2011 Texas Instruments, Inc.
   * - OMAP3/4 support by Rajendra Nayak, Santosh Shilimkar
   *
ec6bced6c   Tony Lindgren   [PATCH] ARM: 2803...
12
13
14
15
16
17
18
19
20
21
22
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   */
  #include <linux/types.h>
  #include <linux/kernel.h>
  #include <linux/sched.h>
  #include <linux/cpufreq.h>
  #include <linux/delay.h>
  #include <linux/init.h>
  #include <linux/err.h>
f8ce25476   Russell King   [ARM] Move asm/ha...
23
  #include <linux/clk.h>
fced80c73   Russell King   [ARM] Convert asm...
24
  #include <linux/io.h>
731e0cc63   Santosh Shilimkar   cpufreq: OMAP: cl...
25
  #include <linux/opp.h>
46c12216c   Russell King   cpufreq: OMAP: Ad...
26
  #include <linux/cpu.h>
c1b547bc2   Kevin Hilman   cpufreq: OMAP: fi...
27
  #include <linux/module.h>
ec6bced6c   Tony Lindgren   [PATCH] ARM: 2803...
28

ec6bced6c   Tony Lindgren   [PATCH] ARM: 2803...
29
  #include <asm/system.h>
731e0cc63   Santosh Shilimkar   cpufreq: OMAP: cl...
30
  #include <asm/smp_plat.h>
46c12216c   Russell King   cpufreq: OMAP: Ad...
31
  #include <asm/cpu.h>
ec6bced6c   Tony Lindgren   [PATCH] ARM: 2803...
32

731e0cc63   Santosh Shilimkar   cpufreq: OMAP: cl...
33
34
35
  #include <plat/clock.h>
  #include <plat/omap-pm.h>
  #include <plat/common.h>
c1b547bc2   Kevin Hilman   cpufreq: OMAP: fi...
36
  #include <plat/omap_device.h>
a7ca9d2b0   Tony Lindgren   ARM: OMAP: Update...
37

731e0cc63   Santosh Shilimkar   cpufreq: OMAP: cl...
38
  #include <mach/hardware.h>
aeec29901   Kevin Hilman   [ARM] OMAP2: Impl...
39

46c12216c   Russell King   cpufreq: OMAP: Ad...
40
41
42
43
44
45
46
47
48
  #ifdef CONFIG_SMP
  struct lpj_info {
  	unsigned long	ref;
  	unsigned int	freq;
  };
  
  static DEFINE_PER_CPU(struct lpj_info, lpj_ref);
  static struct lpj_info global_lpj_ref;
  #endif
731e0cc63   Santosh Shilimkar   cpufreq: OMAP: cl...
49
  static struct cpufreq_frequency_table *freq_table;
1c78217fc   Nishanth Menon   cpufreq: OMAP: fi...
50
  static atomic_t freq_table_users = ATOMIC_INIT(0);
b8488fbe6   Hiroshi DOYU   ARM: OMAP: Fix sl...
51
  static struct clk *mpu_clk;
08ca3e3b8   Nishanth Menon   cpufreq: OMAP: mo...
52
  static char *mpu_clk_name;
a820ffa8f   Nishanth Menon   cpufreq: OMAP: de...
53
  static struct device *mpu_dev;
b8488fbe6   Hiroshi DOYU   ARM: OMAP: Fix sl...
54

b0a330dc5   Manjunath Kondaiah G   OMAP: plat-omap: ...
55
  static int omap_verify_speed(struct cpufreq_policy *policy)
ec6bced6c   Tony Lindgren   [PATCH] ARM: 2803...
56
  {
bf2a359d5   Nishanth Menon   cpufreq: OMAP: do...
57
  	if (!freq_table)
ec6bced6c   Tony Lindgren   [PATCH] ARM: 2803...
58
  		return -EINVAL;
bf2a359d5   Nishanth Menon   cpufreq: OMAP: do...
59
  	return cpufreq_frequency_table_verify(policy, freq_table);
ec6bced6c   Tony Lindgren   [PATCH] ARM: 2803...
60
  }
b0a330dc5   Manjunath Kondaiah G   OMAP: plat-omap: ...
61
  static unsigned int omap_getspeed(unsigned int cpu)
ec6bced6c   Tony Lindgren   [PATCH] ARM: 2803...
62
  {
ec6bced6c   Tony Lindgren   [PATCH] ARM: 2803...
63
  	unsigned long rate;
46c12216c   Russell King   cpufreq: OMAP: Ad...
64
  	if (cpu >= NR_CPUS)
ec6bced6c   Tony Lindgren   [PATCH] ARM: 2803...
65
  		return 0;
ec6bced6c   Tony Lindgren   [PATCH] ARM: 2803...
66
  	rate = clk_get_rate(mpu_clk) / 1000;
ec6bced6c   Tony Lindgren   [PATCH] ARM: 2803...
67
68
69
70
71
72
73
  	return rate;
  }
  
  static int omap_target(struct cpufreq_policy *policy,
  		       unsigned int target_freq,
  		       unsigned int relation)
  {
bf2a359d5   Nishanth Menon   cpufreq: OMAP: do...
74
75
  	unsigned int i;
  	int ret = 0;
731e0cc63   Santosh Shilimkar   cpufreq: OMAP: cl...
76
  	struct cpufreq_freqs freqs;
ec6bced6c   Tony Lindgren   [PATCH] ARM: 2803...
77

bf2a359d5   Nishanth Menon   cpufreq: OMAP: do...
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
  	if (!freq_table) {
  		dev_err(mpu_dev, "%s: cpu%d: no freq table!
  ", __func__,
  				policy->cpu);
  		return -EINVAL;
  	}
  
  	ret = cpufreq_frequency_table_target(policy, freq_table, target_freq,
  			relation, &i);
  	if (ret) {
  		dev_dbg(mpu_dev, "%s: cpu%d: no freq match for %d(ret=%d)
  ",
  			__func__, policy->cpu, target_freq, ret);
  		return ret;
  	}
  	freqs.new = freq_table[i].frequency;
  	if (!freqs.new) {
  		dev_err(mpu_dev, "%s: cpu%d: no match for freq %d
  ", __func__,
  			policy->cpu, target_freq);
  		return -EINVAL;
  	}
aeec29901   Kevin Hilman   [ARM] OMAP2: Impl...
100

46c12216c   Russell King   cpufreq: OMAP: Ad...
101
  	freqs.old = omap_getspeed(policy->cpu);
46c12216c   Russell King   cpufreq: OMAP: Ad...
102
  	freqs.cpu = policy->cpu;
ec6bced6c   Tony Lindgren   [PATCH] ARM: 2803...
103

022ac03b4   Colin Cross   cpufreq: OMAP: no...
104
  	if (freqs.old == freqs.new && policy->cur == freqs.new)
aeec29901   Kevin Hilman   [ARM] OMAP2: Impl...
105
  		return ret;
46c12216c   Russell King   cpufreq: OMAP: Ad...
106
107
108
109
110
  	/* notifiers */
  	for_each_cpu(i, policy->cpus) {
  		freqs.cpu = i;
  		cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
  	}
731e0cc63   Santosh Shilimkar   cpufreq: OMAP: cl...
111

aeec29901   Kevin Hilman   [ARM] OMAP2: Impl...
112
  #ifdef CONFIG_CPU_FREQ_DEBUG
731e0cc63   Santosh Shilimkar   cpufreq: OMAP: cl...
113
114
  	pr_info("cpufreq-omap: transition: %u --> %u
  ", freqs.old, freqs.new);
aeec29901   Kevin Hilman   [ARM] OMAP2: Impl...
115
  #endif
731e0cc63   Santosh Shilimkar   cpufreq: OMAP: cl...
116

aeec29901   Kevin Hilman   [ARM] OMAP2: Impl...
117
  	ret = clk_set_rate(mpu_clk, freqs.new * 1000);
46c12216c   Russell King   cpufreq: OMAP: Ad...
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
  	freqs.new = omap_getspeed(policy->cpu);
  
  #ifdef CONFIG_SMP
  	/*
  	 * Note that loops_per_jiffy is not updated on SMP systems in
  	 * cpufreq driver. So, update the per-CPU loops_per_jiffy value
  	 * on frequency transition. We need to update all dependent CPUs.
  	 */
  	for_each_cpu(i, policy->cpus) {
  		struct lpj_info *lpj = &per_cpu(lpj_ref, i);
  		if (!lpj->freq) {
  			lpj->ref = per_cpu(cpu_data, i).loops_per_jiffy;
  			lpj->freq = freqs.old;
  		}
  
  		per_cpu(cpu_data, i).loops_per_jiffy =
  			cpufreq_scale(lpj->ref, lpj->freq, freqs.new);
  	}
731e0cc63   Santosh Shilimkar   cpufreq: OMAP: cl...
136

46c12216c   Russell King   cpufreq: OMAP: Ad...
137
138
139
140
141
142
143
144
145
146
147
148
149
150
  	/* And don't forget to adjust the global one */
  	if (!global_lpj_ref.freq) {
  		global_lpj_ref.ref = loops_per_jiffy;
  		global_lpj_ref.freq = freqs.old;
  	}
  	loops_per_jiffy = cpufreq_scale(global_lpj_ref.ref, global_lpj_ref.freq,
  					freqs.new);
  #endif
  
  	/* notifiers */
  	for_each_cpu(i, policy->cpus) {
  		freqs.cpu = i;
  		cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
  	}
ec6bced6c   Tony Lindgren   [PATCH] ARM: 2803...
151
152
153
  
  	return ret;
  }
1c78217fc   Nishanth Menon   cpufreq: OMAP: fi...
154
155
156
157
158
  static inline void freq_table_free(void)
  {
  	if (atomic_dec_and_test(&freq_table_users))
  		opp_free_cpufreq_table(mpu_dev, &freq_table);
  }
790ab7e92   Ming Lei   arm: omap: fix se...
159
  static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy)
ec6bced6c   Tony Lindgren   [PATCH] ARM: 2803...
160
  {
aeec29901   Kevin Hilman   [ARM] OMAP2: Impl...
161
  	int result = 0;
731e0cc63   Santosh Shilimkar   cpufreq: OMAP: cl...
162

08ca3e3b8   Nishanth Menon   cpufreq: OMAP: mo...
163
  	mpu_clk = clk_get(NULL, mpu_clk_name);
ec6bced6c   Tony Lindgren   [PATCH] ARM: 2803...
164
165
  	if (IS_ERR(mpu_clk))
  		return PTR_ERR(mpu_clk);
11e04fdd9   Nishanth Menon   cpufreq: OMAP: pu...
166
167
168
169
  	if (policy->cpu >= NR_CPUS) {
  		result = -EINVAL;
  		goto fail_ck;
  	}
aeec29901   Kevin Hilman   [ARM] OMAP2: Impl...
170

46c12216c   Russell King   cpufreq: OMAP: Ad...
171
  	policy->cur = policy->min = policy->max = omap_getspeed(policy->cpu);
1c78217fc   Nishanth Menon   cpufreq: OMAP: fi...
172
173
174
  
  	if (atomic_inc_return(&freq_table_users) == 1)
  		result = opp_init_cpufreq_table(mpu_dev, &freq_table);
bf2a359d5   Nishanth Menon   cpufreq: OMAP: do...
175
176
177
178
179
  
  	if (result) {
  		dev_err(mpu_dev, "%s: cpu%d: failed creating freq table[%d]
  ",
  				__func__, policy->cpu, result);
11e04fdd9   Nishanth Menon   cpufreq: OMAP: pu...
180
  		goto fail_ck;
aeec29901   Kevin Hilman   [ARM] OMAP2: Impl...
181
  	}
bf2a359d5   Nishanth Menon   cpufreq: OMAP: do...
182
  	result = cpufreq_frequency_table_cpuinfo(policy, freq_table);
1c78217fc   Nishanth Menon   cpufreq: OMAP: fi...
183
184
185
186
  	if (result)
  		goto fail_table;
  
  	cpufreq_frequency_table_get_attr(freq_table, policy->cpu);
bf2a359d5   Nishanth Menon   cpufreq: OMAP: do...
187

731e0cc63   Santosh Shilimkar   cpufreq: OMAP: cl...
188
189
  	policy->min = policy->cpuinfo.min_freq;
  	policy->max = policy->cpuinfo.max_freq;
46c12216c   Russell King   cpufreq: OMAP: Ad...
190
191
192
193
194
195
196
197
198
199
200
  	policy->cur = omap_getspeed(policy->cpu);
  
  	/*
  	 * On OMAP SMP configuartion, both processors share the voltage
  	 * and clock. So both CPUs needs to be scaled together and hence
  	 * needs software co-ordination. Use cpufreq affected_cpus
  	 * interface to handle this scenario. Additional is_smp() check
  	 * is to keep SMP_ON_UP build working.
  	 */
  	if (is_smp()) {
  		policy->shared_type = CPUFREQ_SHARED_TYPE_ANY;
ed8ce00c5   Todd Poynor   cpufreq: OMAP: En...
201
  		cpumask_setall(policy->cpus);
46c12216c   Russell King   cpufreq: OMAP: Ad...
202
  	}
731e0cc63   Santosh Shilimkar   cpufreq: OMAP: cl...
203

aeec29901   Kevin Hilman   [ARM] OMAP2: Impl...
204
  	/* FIXME: what's the actual transition time? */
b029839cf   Mike Turquette   omap3: Decrease c...
205
  	policy->cpuinfo.transition_latency = 300 * 1000;
ec6bced6c   Tony Lindgren   [PATCH] ARM: 2803...
206
207
  
  	return 0;
11e04fdd9   Nishanth Menon   cpufreq: OMAP: pu...
208

1c78217fc   Nishanth Menon   cpufreq: OMAP: fi...
209
210
  fail_table:
  	freq_table_free();
11e04fdd9   Nishanth Menon   cpufreq: OMAP: pu...
211
212
213
  fail_ck:
  	clk_put(mpu_clk);
  	return result;
ec6bced6c   Tony Lindgren   [PATCH] ARM: 2803...
214
  }
b8488fbe6   Hiroshi DOYU   ARM: OMAP: Fix sl...
215
216
  static int omap_cpu_exit(struct cpufreq_policy *policy)
  {
1c78217fc   Nishanth Menon   cpufreq: OMAP: fi...
217
  	freq_table_free();
b8488fbe6   Hiroshi DOYU   ARM: OMAP: Fix sl...
218
219
220
  	clk_put(mpu_clk);
  	return 0;
  }
aeec29901   Kevin Hilman   [ARM] OMAP2: Impl...
221
222
223
224
  static struct freq_attr *omap_cpufreq_attr[] = {
  	&cpufreq_freq_attr_scaling_available_freqs,
  	NULL,
  };
ec6bced6c   Tony Lindgren   [PATCH] ARM: 2803...
225
226
227
228
229
230
  static struct cpufreq_driver omap_driver = {
  	.flags		= CPUFREQ_STICKY,
  	.verify		= omap_verify_speed,
  	.target		= omap_target,
  	.get		= omap_getspeed,
  	.init		= omap_cpu_init,
b8488fbe6   Hiroshi DOYU   ARM: OMAP: Fix sl...
231
  	.exit		= omap_cpu_exit,
ec6bced6c   Tony Lindgren   [PATCH] ARM: 2803...
232
  	.name		= "omap",
aeec29901   Kevin Hilman   [ARM] OMAP2: Impl...
233
  	.attr		= omap_cpufreq_attr,
ec6bced6c   Tony Lindgren   [PATCH] ARM: 2803...
234
235
236
237
  };
  
  static int __init omap_cpufreq_init(void)
  {
08ca3e3b8   Nishanth Menon   cpufreq: OMAP: mo...
238
239
240
241
242
243
244
245
246
247
248
249
  	if (cpu_is_omap24xx())
  		mpu_clk_name = "virt_prcm_set";
  	else if (cpu_is_omap34xx())
  		mpu_clk_name = "dpll1_ck";
  	else if (cpu_is_omap44xx())
  		mpu_clk_name = "dpll_mpu_ck";
  
  	if (!mpu_clk_name) {
  		pr_err("%s: unsupported Silicon?
  ", __func__);
  		return -EINVAL;
  	}
a820ffa8f   Nishanth Menon   cpufreq: OMAP: de...
250

c1b547bc2   Kevin Hilman   cpufreq: OMAP: fi...
251
  	mpu_dev = omap_device_get_by_hwmod_name("mpu");
a820ffa8f   Nishanth Menon   cpufreq: OMAP: de...
252
253
254
255
256
  	if (!mpu_dev) {
  		pr_warning("%s: unable to get the mpu device
  ", __func__);
  		return -EINVAL;
  	}
ec6bced6c   Tony Lindgren   [PATCH] ARM: 2803...
257
258
  	return cpufreq_register_driver(&omap_driver);
  }
731e0cc63   Santosh Shilimkar   cpufreq: OMAP: cl...
259
260
261
262
  static void __exit omap_cpufreq_exit(void)
  {
  	cpufreq_unregister_driver(&omap_driver);
  }
aeec29901   Kevin Hilman   [ARM] OMAP2: Impl...
263

731e0cc63   Santosh Shilimkar   cpufreq: OMAP: cl...
264
265
266
267
  MODULE_DESCRIPTION("cpufreq driver for OMAP SoCs");
  MODULE_LICENSE("GPL");
  module_init(omap_cpufreq_init);
  module_exit(omap_cpufreq_exit);