Merge branch 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux-2.6
[deliverable/linux.git] / arch / x86 / kernel / cpu / cpufreq / sc520_freq.c
1 /*
2 * sc520_freq.c: cpufreq driver for the AMD Elan sc520
3 *
4 * Copyright (C) 2005 Sean Young <sean@mess.org>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 * Based on elanfreq.c
12 *
13 * 2005-03-30: - initial revision
14 */
15
16 #include <linux/kernel.h>
17 #include <linux/module.h>
18 #include <linux/init.h>
19
20 #include <linux/delay.h>
21 #include <linux/cpufreq.h>
22
23 #include <asm/msr.h>
24 #include <asm/timex.h>
25 #include <asm/io.h>
26
27 #define MMCR_BASE 0xfffef000 /* The default base address */
28 #define OFFS_CPUCTL 0x2 /* CPU Control Register */
29
30 static __u8 __iomem *cpuctl;
31
32 #define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "sc520_freq", msg)
33
34 static struct cpufreq_frequency_table sc520_freq_table[] = {
35 {0x01, 100000},
36 {0x02, 133000},
37 {0, CPUFREQ_TABLE_END},
38 };
39
40 static unsigned int sc520_freq_get_cpu_frequency(unsigned int cpu)
41 {
42 u8 clockspeed_reg = *cpuctl;
43
44 switch (clockspeed_reg & 0x03) {
45 default:
46 printk(KERN_ERR "sc520_freq: error: cpuctl register has unexpected value %02x\n", clockspeed_reg);
47 case 0x01:
48 return 100000;
49 case 0x02:
50 return 133000;
51 }
52 }
53
54 static void sc520_freq_set_cpu_state (unsigned int state)
55 {
56
57 struct cpufreq_freqs freqs;
58 u8 clockspeed_reg;
59
60 freqs.old = sc520_freq_get_cpu_frequency(0);
61 freqs.new = sc520_freq_table[state].frequency;
62 freqs.cpu = 0; /* AMD Elan is UP */
63
64 cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
65
66 dprintk("attempting to set frequency to %i kHz\n",
67 sc520_freq_table[state].frequency);
68
69 local_irq_disable();
70
71 clockspeed_reg = *cpuctl & ~0x03;
72 *cpuctl = clockspeed_reg | sc520_freq_table[state].index;
73
74 local_irq_enable();
75
76 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
77 };
78
79 static int sc520_freq_verify (struct cpufreq_policy *policy)
80 {
81 return cpufreq_frequency_table_verify(policy, &sc520_freq_table[0]);
82 }
83
84 static int sc520_freq_target (struct cpufreq_policy *policy,
85 unsigned int target_freq,
86 unsigned int relation)
87 {
88 unsigned int newstate = 0;
89
90 if (cpufreq_frequency_table_target(policy, sc520_freq_table, target_freq, relation, &newstate))
91 return -EINVAL;
92
93 sc520_freq_set_cpu_state(newstate);
94
95 return 0;
96 }
97
98
99 /*
100 * Module init and exit code
101 */
102
103 static int sc520_freq_cpu_init(struct cpufreq_policy *policy)
104 {
105 struct cpuinfo_x86 *c = cpu_data;
106 int result;
107
108 /* capability check */
109 if (c->x86_vendor != X86_VENDOR_AMD ||
110 c->x86 != 4 || c->x86_model != 9)
111 return -ENODEV;
112
113 /* cpuinfo and default policy values */
114 policy->cpuinfo.transition_latency = 1000000; /* 1ms */
115 policy->cur = sc520_freq_get_cpu_frequency(0);
116
117 result = cpufreq_frequency_table_cpuinfo(policy, sc520_freq_table);
118 if (result)
119 return (result);
120
121 cpufreq_frequency_table_get_attr(sc520_freq_table, policy->cpu);
122
123 return 0;
124 }
125
126
127 static int sc520_freq_cpu_exit(struct cpufreq_policy *policy)
128 {
129 cpufreq_frequency_table_put_attr(policy->cpu);
130 return 0;
131 }
132
133
134 static struct freq_attr* sc520_freq_attr[] = {
135 &cpufreq_freq_attr_scaling_available_freqs,
136 NULL,
137 };
138
139
140 static struct cpufreq_driver sc520_freq_driver = {
141 .get = sc520_freq_get_cpu_frequency,
142 .verify = sc520_freq_verify,
143 .target = sc520_freq_target,
144 .init = sc520_freq_cpu_init,
145 .exit = sc520_freq_cpu_exit,
146 .name = "sc520_freq",
147 .owner = THIS_MODULE,
148 .attr = sc520_freq_attr,
149 };
150
151
152 static int __init sc520_freq_init(void)
153 {
154 struct cpuinfo_x86 *c = cpu_data;
155 int err;
156
157 /* Test if we have the right hardware */
158 if(c->x86_vendor != X86_VENDOR_AMD ||
159 c->x86 != 4 || c->x86_model != 9) {
160 dprintk("no Elan SC520 processor found!\n");
161 return -ENODEV;
162 }
163 cpuctl = ioremap((unsigned long)(MMCR_BASE + OFFS_CPUCTL), 1);
164 if(!cpuctl) {
165 printk(KERN_ERR "sc520_freq: error: failed to remap memory\n");
166 return -ENOMEM;
167 }
168
169 err = cpufreq_register_driver(&sc520_freq_driver);
170 if (err)
171 iounmap(cpuctl);
172
173 return err;
174 }
175
176
177 static void __exit sc520_freq_exit(void)
178 {
179 cpufreq_unregister_driver(&sc520_freq_driver);
180 iounmap(cpuctl);
181 }
182
183
184 MODULE_LICENSE("GPL");
185 MODULE_AUTHOR("Sean Young <sean@mess.org>");
186 MODULE_DESCRIPTION("cpufreq driver for AMD's Elan sc520 CPU");
187
188 module_init(sc520_freq_init);
189 module_exit(sc520_freq_exit);
190
This page took 0.052497 seconds and 5 git commands to generate.