2 * Copyright (C) 2007 PA Semi, Inc
4 * Authors: Egor Martovetsky <egor@pasemi.com>
5 * Olof Johansson <olof@lixom.net>
7 * Maintained by: Olof Johansson <olof@lixom.net>
9 * Based on arch/powerpc/platforms/cell/cbe_cpufreq.c:
10 * (C) Copyright IBM Deutschland Entwicklung GmbH 2005
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)
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.
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.
28 #include <linux/cpufreq.h>
29 #include <linux/timer.h>
31 #include <asm/hw_irq.h>
35 #define SDCASR_REG 0x0100
36 #define SDCASR_REG_STRIDE 0x1000
37 #define SDCPWR_CFGA0_REG 0x0100
38 #define SDCPWR_PWST0_REG 0x0000
39 #define SDCPWR_GIZTIME_REG 0x0440
41 /* SDCPWR_GIZTIME_REG fields */
42 #define SDCPWR_GIZTIME_GR 0x80000000
43 #define SDCPWR_GIZTIME_LONGLOCK 0x000000ff
45 /* Offset of ASR registers from SDC base */
46 #define SDCASR_OFFSET 0x120000
48 static void __iomem
*sdcpwr_mapbase
;
49 static void __iomem
*sdcasr_mapbase
;
51 static DEFINE_MUTEX(pas_switch_mutex
);
53 /* Current astate, is used when waking up from power savings on
54 * one core, in case the other core has switched states during
57 static int current_astate
;
59 /* We support 5(A0-A4) power states excluding turbo(A5-A6) modes */
60 static struct cpufreq_frequency_table pas_freqs
[] = {
66 {0, CPUFREQ_TABLE_END
},
69 static struct freq_attr
*pas_cpu_freqs_attr
[] = {
70 &cpufreq_freq_attr_scaling_available_freqs
,
75 * hardware specific functions
78 static int get_astate_freq(int astate
)
81 ret
= in_le32(sdcpwr_mapbase
+ SDCPWR_CFGA0_REG
+ (astate
* 0x10));
86 static int get_cur_astate(int cpu
)
90 ret
= in_le32(sdcpwr_mapbase
+ SDCPWR_PWST0_REG
);
91 ret
= (ret
>> (cpu
* 4)) & 0x7;
96 static int get_gizmo_latency(void)
100 giztime
= in_le32(sdcpwr_mapbase
+ SDCPWR_GIZTIME_REG
);
102 /* just provide the upper bound */
103 if (giztime
& SDCPWR_GIZTIME_GR
)
104 ret
= (giztime
& SDCPWR_GIZTIME_LONGLOCK
) * 128000;
106 ret
= (giztime
& SDCPWR_GIZTIME_LONGLOCK
) * 1000;
111 static void set_astate(int cpu
, unsigned int astate
)
115 /* Return if called before init has run */
116 if (unlikely(!sdcasr_mapbase
))
119 local_irq_save(flags
);
121 out_le32(sdcasr_mapbase
+ SDCASR_REG
+ SDCASR_REG_STRIDE
*cpu
, astate
);
123 local_irq_restore(flags
);
126 void restore_astate(int cpu
)
128 set_astate(cpu
, current_astate
);
135 static int pas_cpufreq_cpu_init(struct cpufreq_policy
*policy
)
137 const u32
*max_freqp
;
141 struct device_node
*cpu
, *dn
;
144 cpu
= of_get_cpu_node(policy
->cpu
, NULL
);
149 dn
= of_find_compatible_node(NULL
, "sdc", "1682m-sdc");
152 err
= of_address_to_resource(dn
, 0, &res
);
156 sdcasr_mapbase
= ioremap(res
.start
+ SDCASR_OFFSET
, 0x2000);
157 if (!sdcasr_mapbase
) {
162 dn
= of_find_compatible_node(NULL
, "gizmo", "1682m-gizmo");
165 goto out_unmap_sdcasr
;
167 err
= of_address_to_resource(dn
, 0, &res
);
170 goto out_unmap_sdcasr
;
171 sdcpwr_mapbase
= ioremap(res
.start
, 0x1000);
172 if (!sdcpwr_mapbase
) {
174 goto out_unmap_sdcasr
;
177 pr_debug("init cpufreq on CPU %d\n", policy
->cpu
);
179 max_freqp
= of_get_property(cpu
, "clock-frequency", NULL
);
182 goto out_unmap_sdcpwr
;
185 /* we need the freq in kHz */
186 max_freq
= *max_freqp
/ 1000;
188 pr_debug("max clock-frequency is at %u kHz\n", max_freq
);
189 pr_debug("initializing frequency table\n");
191 /* initialize frequency table */
192 for (i
=0; pas_freqs
[i
].frequency
!=CPUFREQ_TABLE_END
; i
++) {
193 pas_freqs
[i
].frequency
= get_astate_freq(pas_freqs
[i
].index
) * 100000;
194 pr_debug("%d: %d\n", i
, pas_freqs
[i
].frequency
);
197 policy
->governor
= CPUFREQ_DEFAULT_GOVERNOR
;
199 policy
->cpuinfo
.transition_latency
= get_gizmo_latency();
201 cur_astate
= get_cur_astate(policy
->cpu
);
202 pr_debug("current astate is at %d\n",cur_astate
);
204 policy
->cur
= pas_freqs
[cur_astate
].frequency
;
205 policy
->cpus
= cpu_online_map
;
207 cpufreq_frequency_table_get_attr(pas_freqs
, policy
->cpu
);
209 /* this ensures that policy->cpuinfo_min and policy->cpuinfo_max
212 return cpufreq_frequency_table_cpuinfo(policy
, pas_freqs
);
215 iounmap(sdcpwr_mapbase
);
218 iounmap(sdcasr_mapbase
);
223 static int pas_cpufreq_cpu_exit(struct cpufreq_policy
*policy
)
226 iounmap(sdcasr_mapbase
);
228 iounmap(sdcpwr_mapbase
);
230 cpufreq_frequency_table_put_attr(policy
->cpu
);
234 static int pas_cpufreq_verify(struct cpufreq_policy
*policy
)
236 return cpufreq_frequency_table_verify(policy
, pas_freqs
);
239 static int pas_cpufreq_target(struct cpufreq_policy
*policy
,
240 unsigned int target_freq
,
241 unsigned int relation
)
243 struct cpufreq_freqs freqs
;
247 cpufreq_frequency_table_target(policy
,
253 freqs
.old
= policy
->cur
;
254 freqs
.new = pas_freqs
[pas_astate_new
].frequency
;
255 freqs
.cpu
= policy
->cpu
;
257 mutex_lock(&pas_switch_mutex
);
258 cpufreq_notify_transition(&freqs
, CPUFREQ_PRECHANGE
);
260 pr_debug("setting frequency for cpu %d to %d kHz, 1/%d of max frequency\n",
262 pas_freqs
[pas_astate_new
].frequency
,
263 pas_freqs
[pas_astate_new
].index
);
265 current_astate
= pas_astate_new
;
267 for_each_online_cpu(i
)
268 set_astate(i
, pas_astate_new
);
270 cpufreq_notify_transition(&freqs
, CPUFREQ_POSTCHANGE
);
271 mutex_unlock(&pas_switch_mutex
);
276 static struct cpufreq_driver pas_cpufreq_driver
= {
277 .name
= "pas-cpufreq",
278 .owner
= THIS_MODULE
,
279 .flags
= CPUFREQ_CONST_LOOPS
,
280 .init
= pas_cpufreq_cpu_init
,
281 .exit
= pas_cpufreq_cpu_exit
,
282 .verify
= pas_cpufreq_verify
,
283 .target
= pas_cpufreq_target
,
284 .attr
= pas_cpu_freqs_attr
,
288 * module init and destoy
291 static int __init
pas_cpufreq_init(void)
293 if (!machine_is_compatible("PA6T-1682M"))
296 return cpufreq_register_driver(&pas_cpufreq_driver
);
299 static void __exit
pas_cpufreq_exit(void)
301 cpufreq_unregister_driver(&pas_cpufreq_driver
);
304 module_init(pas_cpufreq_init
);
305 module_exit(pas_cpufreq_exit
);
307 MODULE_LICENSE("GPL");
308 MODULE_AUTHOR("Egor Martovetsky <egor@pasemi.com>, Olof Johansson <olof@lixom.net>");