aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorOlof Johansson <olof@lixom.net>2007-04-27 01:46:01 -0400
committerPaul Mackerras <paulus@samba.org>2007-04-27 01:51:57 -0400
commit2e0c3370b3ecf831ef209f1094df36a808865845 (patch)
tree770811bacfb792089406c797bf706748d16f170f /arch
parent116ab40df8decf8c519368e31825e9584c86adf2 (diff)
[POWERPC] pasemi: Cpufreq driver
Cpufreq driver for PA Semi PWRficient processors. Signed-off-by: Egor Martovetsky <egor@pasemi.com> Signed-off-by: Olof Johansson <olof@lixom.net> Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/powerpc/platforms/Kconfig10
-rw-r--r--arch/powerpc/platforms/pasemi/Makefile1
-rw-r--r--arch/powerpc/platforms/pasemi/cpufreq.c307
-rw-r--r--arch/powerpc/platforms/pasemi/idle.c9
-rw-r--r--arch/powerpc/platforms/pasemi/pasemi.h8
5 files changed, 335 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig
index ac62fa08f5e6..102a44e3b7ea 100644
--- a/arch/powerpc/platforms/Kconfig
+++ b/arch/powerpc/platforms/Kconfig
@@ -169,6 +169,16 @@ config CPU_FREQ_PMAC64
169 help 169 help
170 This adds support for frequency switching on Apple iMac G5, 170 This adds support for frequency switching on Apple iMac G5,
171 and some of the more recent desktop G5 machines as well. 171 and some of the more recent desktop G5 machines as well.
172
173config PPC_PASEMI_CPUFREQ
174 bool "Support for PA Semi PWRficient"
175 depends on CPU_FREQ && PPC_PASEMI
176 default y
177 select CPU_FREQ_TABLE
178 help
179 This adds the support for frequency switching on PA Semi
180 PWRficient processors.
181
172endmenu 182endmenu
173 183
174config PPC601_SYNC_FIX 184config PPC601_SYNC_FIX
diff --git a/arch/powerpc/platforms/pasemi/Makefile b/arch/powerpc/platforms/pasemi/Makefile
index 7ffd43b43e02..2cd2a4f26a48 100644
--- a/arch/powerpc/platforms/pasemi/Makefile
+++ b/arch/powerpc/platforms/pasemi/Makefile
@@ -1,2 +1,3 @@
1obj-y += setup.o pci.o time.o idle.o powersave.o iommu.o 1obj-y += setup.o pci.o time.o idle.o powersave.o iommu.o
2obj-$(CONFIG_PPC_PASEMI_MDIO) += gpio_mdio.o 2obj-$(CONFIG_PPC_PASEMI_MDIO) += gpio_mdio.o
3obj-$(CONFIG_PPC_PASEMI_CPUFREQ) += cpufreq.o
diff --git a/arch/powerpc/platforms/pasemi/cpufreq.c b/arch/powerpc/platforms/pasemi/cpufreq.c
new file mode 100644
index 000000000000..4dd5c512f869
--- /dev/null
+++ b/arch/powerpc/platforms/pasemi/cpufreq.c
@@ -0,0 +1,307 @@
1/*
2 * Copyright (C) 2007 PA Semi, Inc
3 *
4 * Authors: Egor Martovetsky <egor@pasemi.com>
5 * Olof Johansson <olof@lixom.net>
6 *
7 * Maintained by: Olof Johansson <olof@lixom.net>
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
31#include <asm/hw_irq.h>
32#include <asm/io.h>
33#include <asm/prom.h>
34
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
40
41/* SDCPWR_GIZTIME_REG fields */
42#define SDCPWR_GIZTIME_GR 0x80000000
43#define SDCPWR_GIZTIME_LONGLOCK 0x000000ff
44
45/* Offset of ASR registers from SDC base */
46#define SDCASR_OFFSET 0x120000
47
48static void __iomem *sdcpwr_mapbase;
49static void __iomem *sdcasr_mapbase;
50
51static DEFINE_MUTEX(pas_switch_mutex);
52
53/* Current astate, is used when waking up from power savings on
54 * one core, in case the other core has switched states during
55 * the idle time.
56 */
57static int current_astate;
58
59/* We support 5(A0-A4) power states excluding turbo(A5-A6) modes */
60static struct cpufreq_frequency_table pas_freqs[] = {
61 {0, 0},
62 {1, 0},
63 {2, 0},
64 {3, 0},
65 {4, 0},
66 {0, CPUFREQ_TABLE_END},
67};
68
69static struct freq_attr *pas_cpu_freqs_attr[] = {
70 &cpufreq_freq_attr_scaling_available_freqs,
71 NULL,
72};
73
74/*
75 * hardware specific functions
76 */
77
78static int get_astate_freq(int astate)
79{
80 u32 ret;
81 ret = in_le32(sdcpwr_mapbase + SDCPWR_CFGA0_REG + (astate * 0x10));
82
83 return ret & 0x3f;
84}
85
86static int get_cur_astate(int cpu)
87{
88 u32 ret;
89
90 ret = in_le32(sdcpwr_mapbase + SDCPWR_PWST0_REG);
91 ret = (ret >> (cpu * 4)) & 0x7;
92
93 return ret;
94}
95
96static int get_gizmo_latency(void)
97{
98 u32 giztime, ret;
99
100 giztime = in_le32(sdcpwr_mapbase + SDCPWR_GIZTIME_REG);
101
102 /* just provide the upper bound */
103 if (giztime & SDCPWR_GIZTIME_GR)
104 ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 128000;
105 else
106 ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 1000;
107
108 return ret;
109}
110
111static void set_astate(int cpu, unsigned int astate)
112{
113 u64 flags;
114
115 /* Return if called before init has run */
116 if (unlikely(!sdcasr_mapbase))
117 return;
118
119 local_irq_save(flags);
120
121 out_le32(sdcasr_mapbase + SDCASR_REG + SDCASR_REG_STRIDE*cpu, astate);
122
123 local_irq_restore(flags);
124}
125
126void restore_astate(int cpu)
127{
128 set_astate(cpu, current_astate);
129}
130
131/*
132 * cpufreq functions
133 */
134
135static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy)
136{
137 u32 *max_freq;
138 int i, cur_astate;
139 struct resource res;
140 struct device_node *cpu, *dn;
141 int err = -ENODEV;
142
143 cpu = of_get_cpu_node(policy->cpu, NULL);
144
145 if (!cpu)
146 goto out;
147
148 dn = of_find_compatible_node(NULL, "sdc", "1682m-sdc");
149 if (!dn)
150 goto out;
151 err = of_address_to_resource(dn, 0, &res);
152 of_node_put(dn);
153 if (err)
154 goto out;
155 sdcasr_mapbase = ioremap(res.start + SDCASR_OFFSET, 0x2000);
156 if (!sdcasr_mapbase) {
157 err = -EINVAL;
158 goto out;
159 }
160
161 dn = of_find_compatible_node(NULL, "gizmo", "1682m-gizmo");
162 if (!dn) {
163 err = -ENODEV;
164 goto out_unmap_sdcasr;
165 }
166 err = of_address_to_resource(dn, 0, &res);
167 of_node_put(dn);
168 if (err)
169 goto out_unmap_sdcasr;
170 sdcpwr_mapbase = ioremap(res.start, 0x1000);
171 if (!sdcpwr_mapbase) {
172 err = -EINVAL;
173 goto out_unmap_sdcasr;
174 }
175
176 pr_debug("init cpufreq on CPU %d\n", policy->cpu);
177
178 max_freq = (u32*) get_property(cpu, "clock-frequency", NULL);
179 if (!max_freq) {
180 err = -EINVAL;
181 goto out_unmap_sdcpwr;
182 }
183
184 /* we need the freq in kHz */
185 *max_freq /= 1000;
186
187 pr_debug("max clock-frequency is at %u kHz\n", *max_freq);
188 pr_debug("initializing frequency table\n");
189
190 /* initialize frequency table */
191 for (i=0; pas_freqs[i].frequency!=CPUFREQ_TABLE_END; i++) {
192 pas_freqs[i].frequency = get_astate_freq(pas_freqs[i].index) * 100000;
193 pr_debug("%d: %d\n", i, pas_freqs[i].frequency);
194 }
195
196 policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
197
198 policy->cpuinfo.transition_latency = get_gizmo_latency();
199
200 cur_astate = get_cur_astate(policy->cpu);
201 pr_debug("current astate is at %d\n",cur_astate);
202
203 policy->cur = pas_freqs[cur_astate].frequency;
204 policy->cpus = cpu_online_map;
205
206 cpufreq_frequency_table_get_attr(pas_freqs, policy->cpu);
207
208 /* this ensures that policy->cpuinfo_min and policy->cpuinfo_max
209 * are set correctly
210 */
211 return cpufreq_frequency_table_cpuinfo(policy, pas_freqs);
212
213out_unmap_sdcpwr:
214 iounmap(sdcpwr_mapbase);
215
216out_unmap_sdcasr:
217 iounmap(sdcasr_mapbase);
218out:
219 return err;
220}
221
222static int pas_cpufreq_cpu_exit(struct cpufreq_policy *policy)
223{
224 if (sdcasr_mapbase)
225 iounmap(sdcasr_mapbase);
226 if (sdcpwr_mapbase)
227 iounmap(sdcpwr_mapbase);
228
229 cpufreq_frequency_table_put_attr(policy->cpu);
230 return 0;
231}
232
233static int pas_cpufreq_verify(struct cpufreq_policy *policy)
234{
235 return cpufreq_frequency_table_verify(policy, pas_freqs);
236}
237
238static int pas_cpufreq_target(struct cpufreq_policy *policy,
239 unsigned int target_freq,
240 unsigned int relation)
241{
242 struct cpufreq_freqs freqs;
243 int pas_astate_new;
244 int i;
245
246 cpufreq_frequency_table_target(policy,
247 pas_freqs,
248 target_freq,
249 relation,
250 &pas_astate_new);
251
252 freqs.old = policy->cur;
253 freqs.new = pas_freqs[pas_astate_new].frequency;
254 freqs.cpu = policy->cpu;
255
256 mutex_lock(&pas_switch_mutex);
257 cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
258
259 pr_debug("setting frequency for cpu %d to %d kHz, 1/%d of max frequency\n",
260 policy->cpu,
261 pas_freqs[pas_astate_new].frequency,
262 pas_freqs[pas_astate_new].index);
263
264 current_astate = pas_astate_new;
265
266 for_each_online_cpu(i)
267 set_astate(i, pas_astate_new);
268
269 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
270 mutex_unlock(&pas_switch_mutex);
271
272 return 0;
273}
274
275static struct cpufreq_driver pas_cpufreq_driver = {
276 .name = "pas-cpufreq",
277 .owner = THIS_MODULE,
278 .flags = CPUFREQ_CONST_LOOPS,
279 .init = pas_cpufreq_cpu_init,
280 .exit = pas_cpufreq_cpu_exit,
281 .verify = pas_cpufreq_verify,
282 .target = pas_cpufreq_target,
283 .attr = pas_cpu_freqs_attr,
284};
285
286/*
287 * module init and destoy
288 */
289
290static int __init pas_cpufreq_init(void)
291{
292 if (!machine_is_compatible("PA6T-1682M"))
293 return -ENODEV;
294
295 return cpufreq_register_driver(&pas_cpufreq_driver);
296}
297
298static void __exit pas_cpufreq_exit(void)
299{
300 cpufreq_unregister_driver(&pas_cpufreq_driver);
301}
302
303module_init(pas_cpufreq_init);
304module_exit(pas_cpufreq_exit);
305
306MODULE_LICENSE("GPL");
307MODULE_AUTHOR("Egor Martovetsky <egor@pasemi.com>, Olof Johansson <olof@lixom.net>");
diff --git a/arch/powerpc/platforms/pasemi/idle.c b/arch/powerpc/platforms/pasemi/idle.c
index 1ca3ff381591..5985ce0c5c48 100644
--- a/arch/powerpc/platforms/pasemi/idle.c
+++ b/arch/powerpc/platforms/pasemi/idle.c
@@ -61,6 +61,10 @@ static int pasemi_system_reset_exception(struct pt_regs *regs)
61 /* do system reset */ 61 /* do system reset */
62 return 0; 62 return 0;
63 } 63 }
64
65 /* Set higher astate since we come out of power savings at 0 */
66 restore_astate(hard_smp_processor_id());
67
64 /* everything handled */ 68 /* everything handled */
65 regs->msr |= MSR_RI; 69 regs->msr |= MSR_RI;
66 return 1; 70 return 1;
@@ -68,6 +72,11 @@ static int pasemi_system_reset_exception(struct pt_regs *regs)
68 72
69void __init pasemi_idle_init(void) 73void __init pasemi_idle_init(void)
70{ 74{
75#ifndef CONFIG_PPC_PASEMI_CPUFREQ
76 printk(KERN_WARNING "No cpufreq driver, powersavings modes disabled\n");
77 current_mode = 0;
78#endif
79
71 ppc_md.system_reset_exception = pasemi_system_reset_exception; 80 ppc_md.system_reset_exception = pasemi_system_reset_exception;
72 ppc_md.power_save = modes[current_mode].entry; 81 ppc_md.power_save = modes[current_mode].entry;
73 printk(KERN_INFO "Using PA6T idle loop (%s)\n", modes[current_mode].name); 82 printk(KERN_INFO "Using PA6T idle loop (%s)\n", modes[current_mode].name);
diff --git a/arch/powerpc/platforms/pasemi/pasemi.h b/arch/powerpc/platforms/pasemi/pasemi.h
index 2d3927e6edb0..be8495497611 100644
--- a/arch/powerpc/platforms/pasemi/pasemi.h
+++ b/arch/powerpc/platforms/pasemi/pasemi.h
@@ -14,6 +14,14 @@ extern void __init pasemi_idle_init(void);
14extern void idle_spin(void); 14extern void idle_spin(void);
15extern void idle_doze(void); 15extern void idle_doze(void);
16 16
17/* Restore astate to last set */
18#ifdef CONFIG_PPC_PASEMI_CPUFREQ
19extern void restore_astate(int cpu);
20#else
21static inline void restore_astate(int cpu)
22{
23}
24#endif
17 25
18 26
19#endif /* _PASEMI_PASEMI_H */ 27#endif /* _PASEMI_PASEMI_H */