Blame view
drivers/cpufreq/elanfreq.c
5.73 KB
1da177e4c
|
1 |
/* |
32ee8c3e4
|
2 |
* elanfreq: cpufreq driver for the AMD ELAN family |
1da177e4c
|
3 4 5 |
* * (c) Copyright 2002 Robert Schwebel <r.schwebel@pengutronix.de> * |
32ee8c3e4
|
6 |
* Parts of this code are (c) Sven Geggus <sven@geggus.net> |
1da177e4c
|
7 |
* |
32ee8c3e4
|
8 |
* All Rights Reserved. |
1da177e4c
|
9 10 11 12 |
* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version |
32ee8c3e4
|
13 |
* 2 of the License, or (at your option) any later version. |
1da177e4c
|
14 15 16 17 18 19 20 21 |
* * 2002-02-13: - initial revision for 2.4.18-pre9 by Robert Schwebel * */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> |
1da177e4c
|
22 23 |
#include <linux/delay.h> #include <linux/cpufreq.h> |
fa8031aef
|
24 |
#include <asm/cpu_device_id.h> |
1da177e4c
|
25 |
#include <asm/msr.h> |
18c6faa96
|
26 27 |
#include <linux/timex.h> #include <linux/io.h> |
1da177e4c
|
28 |
|
32ee8c3e4
|
29 |
#define REG_CSCIR 0x22 /* Chip Setup and Control Index Register */ |
1da177e4c
|
30 31 32 33 34 35 36 37 38 39 40 41 |
#define REG_CSCDR 0x23 /* Chip Setup and Control Data Register */ /* Module parameter */ static int max_freq; struct s_elan_multiplier { int clock; /* frequency in kHz */ int val40h; /* PMU Force Mode register */ int val80h; /* CPU Clock Speed Register */ }; /* |
32ee8c3e4
|
42 |
* It is important that the frequencies |
1da177e4c
|
43 44 |
* are listed in ascending order here! */ |
460f5ef28
|
45 |
static struct s_elan_multiplier elan_multiplier[] = { |
1da177e4c
|
46 47 48 49 50 51 52 53 54 55 56 |
{1000, 0x02, 0x18}, {2000, 0x02, 0x10}, {4000, 0x02, 0x08}, {8000, 0x00, 0x00}, {16000, 0x00, 0x02}, {33000, 0x00, 0x04}, {66000, 0x01, 0x04}, {99000, 0x01, 0x05} }; static struct cpufreq_frequency_table elanfreq_table[] = { |
7f4b04614
|
57 58 59 60 61 62 63 64 65 |
{0, 0, 1000}, {0, 1, 2000}, {0, 2, 4000}, {0, 3, 8000}, {0, 4, 16000}, {0, 5, 33000}, {0, 6, 66000}, {0, 7, 99000}, {0, 0, CPUFREQ_TABLE_END}, |
1da177e4c
|
66 67 68 69 70 71 72 |
}; /** * elanfreq_get_cpu_frequency: determine current cpu speed * * Finds out at which frequency the CPU of the Elan SOC runs |
32ee8c3e4
|
73 |
* at the moment. Frequencies from 1 to 33 MHz are generated |
1da177e4c
|
74 |
* the normal way, 66 and 99 MHz are called "Hyperspeed Mode" |
32ee8c3e4
|
75 |
* and have the rest of the chip running with 33 MHz. |
1da177e4c
|
76 77 78 79 |
*/ static unsigned int elanfreq_get_cpu_frequency(unsigned int cpu) { |
32ee8c3e4
|
80 |
u8 clockspeed_reg; /* Clock Speed Register */ |
1da177e4c
|
81 |
local_irq_disable(); |
18c6faa96
|
82 |
outb_p(0x80, REG_CSCIR); |
32ee8c3e4
|
83 |
clockspeed_reg = inb_p(REG_CSCDR); |
1da177e4c
|
84 |
local_irq_enable(); |
32ee8c3e4
|
85 86 |
if ((clockspeed_reg & 0xE0) == 0xE0) return 0; |
1da177e4c
|
87 |
|
32ee8c3e4
|
88 89 90 |
/* Are we in CPU clock multiplied mode (66/99 MHz)? */ if ((clockspeed_reg & 0xE0) == 0xC0) { if ((clockspeed_reg & 0x01) == 0) |
1da177e4c
|
91 |
return 66000; |
32ee8c3e4
|
92 93 94 |
else return 99000; } |
1da177e4c
|
95 96 |
/* 33 MHz is not 32 MHz... */ |
18c6faa96
|
97 |
if ((clockspeed_reg & 0xE0) == 0xA0) |
1da177e4c
|
98 |
return 33000; |
18c6faa96
|
99 |
return (1<<((clockspeed_reg & 0xE0) >> 5)) * 1000; |
1da177e4c
|
100 |
} |
9c0ebcf78
|
101 102 |
static int elanfreq_target(struct cpufreq_policy *policy, unsigned int state) |
32ee8c3e4
|
103 |
{ |
32ee8c3e4
|
104 105 106 107 |
/* * Access to the Elan's internal registers is indexed via * 0x22: Chip Setup & Control Register Index Register (CSCI) * 0x23: Chip Setup & Control Register Data Register (CSCD) |
1da177e4c
|
108 109 |
* */ |
32ee8c3e4
|
110 111 |
/* * 0x40 is the Power Management Unit's Force Mode Register. |
1da177e4c
|
112 113 114 115 |
* Bit 6 enables Hyperspeed Mode (66/100 MHz core frequency) */ local_irq_disable(); |
18c6faa96
|
116 117 |
outb_p(0x40, REG_CSCIR); /* Disable hyperspeed mode */ outb_p(0x00, REG_CSCDR); |
1da177e4c
|
118 119 120 121 122 123 |
local_irq_enable(); /* wait till internal pipelines and */ udelay(1000); /* buffers have cleaned up */ local_irq_disable(); /* now, set the CPU clock speed register (0x80) */ |
18c6faa96
|
124 125 |
outb_p(0x80, REG_CSCIR); outb_p(elan_multiplier[state].val80h, REG_CSCDR); |
1da177e4c
|
126 127 |
/* now, the hyperspeed bit in PMU Force Mode Register (0x40) */ |
18c6faa96
|
128 129 |
outb_p(0x40, REG_CSCIR); outb_p(elan_multiplier[state].val40h, REG_CSCDR); |
1da177e4c
|
130 131 |
udelay(10000); local_irq_enable(); |
1da177e4c
|
132 133 |
return 0; } |
1da177e4c
|
134 135 136 137 138 139 |
/* * Module init and exit code */ static int elanfreq_cpu_init(struct cpufreq_policy *policy) { |
92cb7612a
|
140 |
struct cpuinfo_x86 *c = &cpu_data(0); |
041526f91
|
141 |
struct cpufreq_frequency_table *pos; |
1da177e4c
|
142 143 144 |
/* capability check */ if ((c->x86_vendor != X86_VENDOR_AMD) || |
18c6faa96
|
145 |
(c->x86 != 4) || (c->x86_model != 10)) |
1da177e4c
|
146 147 148 149 150 151 152 |
return -ENODEV; /* max freq */ if (!max_freq) max_freq = elanfreq_get_cpu_frequency(0); /* table init */ |
041526f91
|
153 154 155 |
cpufreq_for_each_entry(pos, elanfreq_table) if (pos->frequency > max_freq) pos->frequency = CPUFREQ_ENTRY_INVALID; |
1da177e4c
|
156 157 |
/* cpuinfo and default policy values */ |
1da177e4c
|
158 |
policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; |
1da177e4c
|
159 |
|
55bb85b7a
|
160 |
return cpufreq_table_validate_and_show(policy, elanfreq_table); |
1da177e4c
|
161 |
} |
1da177e4c
|
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
#ifndef MODULE /** * elanfreq_setup - elanfreq command line parameter parsing * * elanfreq command line parameter. Use: * elanfreq=66000 * to set the maximum CPU frequency to 66 MHz. Note that in * case you do not give this boot parameter, the maximum * frequency will fall back to _current_ CPU frequency which * might be lower. If you build this as a module, use the * max_freq module parameter instead. */ static int __init elanfreq_setup(char *str) { max_freq = simple_strtoul(str, &str, 0); printk(KERN_WARNING "You're using the deprecated elanfreq command line option. Use elanfreq.max_freq instead, please! "); return 1; } __setup("elanfreq=", elanfreq_setup); #endif |
221dee285
|
183 |
static struct cpufreq_driver elanfreq_driver = { |
32ee8c3e4
|
184 |
.get = elanfreq_get_cpu_frequency, |
06494eb78
|
185 |
.verify = cpufreq_generic_frequency_table_verify, |
9c0ebcf78
|
186 |
.target_index = elanfreq_target, |
1da177e4c
|
187 |
.init = elanfreq_cpu_init, |
1da177e4c
|
188 |
.name = "elanfreq", |
06494eb78
|
189 |
.attr = cpufreq_generic_attr, |
1da177e4c
|
190 |
}; |
fa8031aef
|
191 192 193 194 195 |
static const struct x86_cpu_id elan_id[] = { { X86_VENDOR_AMD, 4, 10, }, {} }; MODULE_DEVICE_TABLE(x86cpu, elan_id); |
1da177e4c
|
196 |
|
32ee8c3e4
|
197 198 |
static int __init elanfreq_init(void) { |
fa8031aef
|
199 |
if (!x86_match_cpu(elan_id)) |
18c6faa96
|
200 |
return -ENODEV; |
1da177e4c
|
201 202 |
return cpufreq_register_driver(&elanfreq_driver); } |
32ee8c3e4
|
203 |
static void __exit elanfreq_exit(void) |
1da177e4c
|
204 205 206 |
{ cpufreq_unregister_driver(&elanfreq_driver); } |
18c6faa96
|
207 |
module_param(max_freq, int, 0444); |
1da177e4c
|
208 209 |
MODULE_LICENSE("GPL"); |
04cd1a99d
|
210 211 |
MODULE_AUTHOR("Robert Schwebel <r.schwebel@pengutronix.de>, " "Sven Geggus <sven@geggus.net>"); |
1da177e4c
|
212 213 214 215 |
MODULE_DESCRIPTION("cpufreq driver for AMD's Elan CPUs"); module_init(elanfreq_init); module_exit(elanfreq_exit); |