Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
cpufreq.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2007 PA Semi, Inc
3  *
4  * Authors: Egor Martovetsky <[email protected]>
5  * Olof Johansson <[email protected]>
6  *
7  * Maintained by: Olof Johansson <[email protected]>
8  *
9  * Based on arch/powerpc/platforms/cell/cbe_cpufreq.c:
10  * (C) Copyright IBM Deutschland Entwicklung GmbH 2005
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2, or (at your option)
15  * any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25  *
26  */
27 
28 #include <linux/cpufreq.h>
29 #include <linux/timer.h>
30 #include <linux/module.h>
31 
32 #include <asm/hw_irq.h>
33 #include <asm/io.h>
34 #include <asm/prom.h>
35 #include <asm/time.h>
36 #include <asm/smp.h>
37 
38 #define SDCASR_REG 0x0100
39 #define SDCASR_REG_STRIDE 0x1000
40 #define SDCPWR_CFGA0_REG 0x0100
41 #define SDCPWR_PWST0_REG 0x0000
42 #define SDCPWR_GIZTIME_REG 0x0440
43 
44 /* SDCPWR_GIZTIME_REG fields */
45 #define SDCPWR_GIZTIME_GR 0x80000000
46 #define SDCPWR_GIZTIME_LONGLOCK 0x000000ff
47 
48 /* Offset of ASR registers from SDC base */
49 #define SDCASR_OFFSET 0x120000
50 
51 static void __iomem *sdcpwr_mapbase;
52 static void __iomem *sdcasr_mapbase;
53 
54 static DEFINE_MUTEX(pas_switch_mutex);
55 
56 /* Current astate, is used when waking up from power savings on
57  * one core, in case the other core has switched states during
58  * the idle time.
59  */
60 static int current_astate;
61 
62 /* We support 5(A0-A4) power states excluding turbo(A5-A6) modes */
63 static struct cpufreq_frequency_table pas_freqs[] = {
64  {0, 0},
65  {1, 0},
66  {2, 0},
67  {3, 0},
68  {4, 0},
69  {0, CPUFREQ_TABLE_END},
70 };
71 
72 static struct freq_attr *pas_cpu_freqs_attr[] = {
74  NULL,
75 };
76 
77 /*
78  * hardware specific functions
79  */
80 
81 static int get_astate_freq(int astate)
82 {
83  u32 ret;
84  ret = in_le32(sdcpwr_mapbase + SDCPWR_CFGA0_REG + (astate * 0x10));
85 
86  return ret & 0x3f;
87 }
88 
89 static int get_cur_astate(int cpu)
90 {
91  u32 ret;
92 
93  ret = in_le32(sdcpwr_mapbase + SDCPWR_PWST0_REG);
94  ret = (ret >> (cpu * 4)) & 0x7;
95 
96  return ret;
97 }
98 
99 static int get_gizmo_latency(void)
100 {
101  u32 giztime, ret;
102 
103  giztime = in_le32(sdcpwr_mapbase + SDCPWR_GIZTIME_REG);
104 
105  /* just provide the upper bound */
106  if (giztime & SDCPWR_GIZTIME_GR)
107  ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 128000;
108  else
109  ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 1000;
110 
111  return ret;
112 }
113 
114 static void set_astate(int cpu, unsigned int astate)
115 {
116  unsigned long flags;
117 
118  /* Return if called before init has run */
119  if (unlikely(!sdcasr_mapbase))
120  return;
121 
122  local_irq_save(flags);
123 
124  out_le32(sdcasr_mapbase + SDCASR_REG + SDCASR_REG_STRIDE*cpu, astate);
125 
126  local_irq_restore(flags);
127 }
128 
129 int check_astate(void)
130 {
131  return get_cur_astate(hard_smp_processor_id());
132 }
133 
134 void restore_astate(int cpu)
135 {
136  set_astate(cpu, current_astate);
137 }
138 
139 /*
140  * cpufreq functions
141  */
142 
143 static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy)
144 {
145  const u32 *max_freqp;
146  u32 max_freq;
147  int i, cur_astate;
148  struct resource res;
149  struct device_node *cpu, *dn;
150  int err = -ENODEV;
151 
152  cpu = of_get_cpu_node(policy->cpu, NULL);
153 
154  if (!cpu)
155  goto out;
156 
157  dn = of_find_compatible_node(NULL, NULL, "1682m-sdc");
158  if (!dn)
160  "pasemi,pwrficient-sdc");
161  if (!dn)
162  goto out;
163  err = of_address_to_resource(dn, 0, &res);
164  of_node_put(dn);
165  if (err)
166  goto out;
167  sdcasr_mapbase = ioremap(res.start + SDCASR_OFFSET, 0x2000);
168  if (!sdcasr_mapbase) {
169  err = -EINVAL;
170  goto out;
171  }
172 
173  dn = of_find_compatible_node(NULL, NULL, "1682m-gizmo");
174  if (!dn)
176  "pasemi,pwrficient-gizmo");
177  if (!dn) {
178  err = -ENODEV;
179  goto out_unmap_sdcasr;
180  }
181  err = of_address_to_resource(dn, 0, &res);
182  of_node_put(dn);
183  if (err)
184  goto out_unmap_sdcasr;
185  sdcpwr_mapbase = ioremap(res.start, 0x1000);
186  if (!sdcpwr_mapbase) {
187  err = -EINVAL;
188  goto out_unmap_sdcasr;
189  }
190 
191  pr_debug("init cpufreq on CPU %d\n", policy->cpu);
192 
193  max_freqp = of_get_property(cpu, "clock-frequency", NULL);
194  if (!max_freqp) {
195  err = -EINVAL;
196  goto out_unmap_sdcpwr;
197  }
198 
199  /* we need the freq in kHz */
200  max_freq = *max_freqp / 1000;
201 
202  pr_debug("max clock-frequency is at %u kHz\n", max_freq);
203  pr_debug("initializing frequency table\n");
204 
205  /* initialize frequency table */
206  for (i=0; pas_freqs[i].frequency!=CPUFREQ_TABLE_END; i++) {
207  pas_freqs[i].frequency = get_astate_freq(pas_freqs[i].index) * 100000;
208  pr_debug("%d: %d\n", i, pas_freqs[i].frequency);
209  }
210 
211  policy->cpuinfo.transition_latency = get_gizmo_latency();
212 
213  cur_astate = get_cur_astate(policy->cpu);
214  pr_debug("current astate is at %d\n",cur_astate);
215 
216  policy->cur = pas_freqs[cur_astate].frequency;
217  cpumask_copy(policy->cpus, cpu_online_mask);
218 
219  ppc_proc_freq = policy->cur * 1000ul;
220 
221  cpufreq_frequency_table_get_attr(pas_freqs, policy->cpu);
222 
223  /* this ensures that policy->cpuinfo_min and policy->cpuinfo_max
224  * are set correctly
225  */
226  return cpufreq_frequency_table_cpuinfo(policy, pas_freqs);
227 
228 out_unmap_sdcpwr:
229  iounmap(sdcpwr_mapbase);
230 
231 out_unmap_sdcasr:
232  iounmap(sdcasr_mapbase);
233 out:
234  return err;
235 }
236 
237 static int pas_cpufreq_cpu_exit(struct cpufreq_policy *policy)
238 {
239  if (sdcasr_mapbase)
240  iounmap(sdcasr_mapbase);
241  if (sdcpwr_mapbase)
242  iounmap(sdcpwr_mapbase);
243 
245  return 0;
246 }
247 
248 static int pas_cpufreq_verify(struct cpufreq_policy *policy)
249 {
250  return cpufreq_frequency_table_verify(policy, pas_freqs);
251 }
252 
253 static int pas_cpufreq_target(struct cpufreq_policy *policy,
254  unsigned int target_freq,
255  unsigned int relation)
256 {
257  struct cpufreq_freqs freqs;
258  int pas_astate_new;
259  int i;
260 
262  pas_freqs,
263  target_freq,
264  relation,
265  &pas_astate_new);
266 
267  freqs.old = policy->cur;
268  freqs.new = pas_freqs[pas_astate_new].frequency;
269  freqs.cpu = policy->cpu;
270 
271  mutex_lock(&pas_switch_mutex);
273 
274  pr_debug("setting frequency for cpu %d to %d kHz, 1/%d of max frequency\n",
275  policy->cpu,
276  pas_freqs[pas_astate_new].frequency,
277  pas_freqs[pas_astate_new].index);
278 
279  current_astate = pas_astate_new;
280 
282  set_astate(i, pas_astate_new);
283 
285  mutex_unlock(&pas_switch_mutex);
286 
287  ppc_proc_freq = freqs.new * 1000ul;
288  return 0;
289 }
290 
291 static struct cpufreq_driver pas_cpufreq_driver = {
292  .name = "pas-cpufreq",
293  .owner = THIS_MODULE,
294  .flags = CPUFREQ_CONST_LOOPS,
295  .init = pas_cpufreq_cpu_init,
296  .exit = pas_cpufreq_cpu_exit,
297  .verify = pas_cpufreq_verify,
298  .target = pas_cpufreq_target,
299  .attr = pas_cpu_freqs_attr,
300 };
301 
302 /*
303  * module init and destoy
304  */
305 
306 static int __init pas_cpufreq_init(void)
307 {
308  if (!of_machine_is_compatible("PA6T-1682M") &&
309  !of_machine_is_compatible("pasemi,pwrficient"))
310  return -ENODEV;
311 
312  return cpufreq_register_driver(&pas_cpufreq_driver);
313 }
314 
315 static void __exit pas_cpufreq_exit(void)
316 {
317  cpufreq_unregister_driver(&pas_cpufreq_driver);
318 }
319 
320 module_init(pas_cpufreq_init);
321 module_exit(pas_cpufreq_exit);
322 
323 MODULE_LICENSE("GPL");
324 MODULE_AUTHOR("Egor Martovetsky <[email protected]>, Olof Johansson <[email protected]>");