Blame view

drivers/cpufreq/longhaul.c 25.9 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /*
f4432c5ca   Dave Jones   Update email addr...
2
   *  (C) 2001-2004  Dave Jones. <davej@redhat.com>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
3
4
5
6
7
8
9
10
   *  (C) 2002  Padraig Brady. <padraig@antefacto.com>
   *
   *  Licensed under the terms of the GNU GPL License version 2.
   *  Based upon datasheets & sample CPUs kindly provided by VIA.
   *
   *  VIA have currently 3 different versions of Longhaul.
   *  Version 1 (Longhaul) uses the BCR2 MSR at 0x1147.
   *   It is present only in Samuel 1 (C5A), Samuel 2 (C5B) stepping 0.
2b8c0e130   Rafa³ Bilski   [CPUFREQ] Longhau...
11
12
13
   *  Version 2 of longhaul is backward compatible with v1, but adds
   *   LONGHAUL MSR for purpose of both frequency and voltage scaling.
   *   Present in Samuel 2 (steppings 1-7 only) (C5B), and Ezra (C5C).
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
   *  Version 3 of longhaul got renamed to Powersaver and redesigned
2b8c0e130   Rafa³ Bilski   [CPUFREQ] Longhau...
15
   *   to use only the POWERSAVER MSR at 0x110a.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16
17
18
19
20
21
22
23
24
25
26
27
28
   *   It is present in Ezra-T (C5M), Nehemiah (C5X) and above.
   *   It's pretty much the same feature wise to longhaul v2, though
   *   there is provision for scaling FSB too, but this doesn't work
   *   too well in practice so we don't even try to use this.
   *
   *  BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
   */
  
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/moduleparam.h>
  #include <linux/init.h>
  #include <linux/cpufreq.h>
179da8e6e   Rafa³ Bilski   [CPUFREQ] Longhau...
29
  #include <linux/pci.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
31
  #include <linux/slab.h>
  #include <linux/string.h>
73e107d4a   Rafał Bilski   [CPUFREQ] Longhau...
32
  #include <linux/delay.h>
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
33
34
35
  #include <linux/timex.h>
  #include <linux/io.h>
  #include <linux/acpi.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
37
  
  #include <asm/msr.h>
dadb49d87   Rafa³ Bilski   [CPUFREQ] Longhau...
38
  #include <acpi/processor.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
40
41
42
43
44
45
46
47
48
49
50
51
52
  
  #include "longhaul.h"
  
  #define PFX "longhaul: "
  
  #define TYPE_LONGHAUL_V1	1
  #define TYPE_LONGHAUL_V2	2
  #define TYPE_POWERSAVER		3
  
  #define	CPU_SAMUEL	1
  #define	CPU_SAMUEL2	2
  #define	CPU_EZRA	3
  #define	CPU_EZRA_T	4
  #define	CPU_NEHEMIAH	5
980342a7e   Rafa³ Bilski   [CPUFREQ] Longhau...
53
  #define	CPU_NEHEMIAH_C	6
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
54

264166e60   Rafa³ Bilski   [CPUFREQ] Longhau...
55
56
57
  /* Flags */
  #define USE_ACPI_C3		(1 << 1)
  #define USE_NORTHBRIDGE		(1 << 2)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
58
  static int cpu_model;
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
59
  static unsigned int numscales = 16;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60
  static unsigned int fsb;
db44aaf3a   Rafa³ Bilski   [CPUFREQ] Longhau...
61

bd5ab26a7   Dave Jones   [CPUFREQ] constif...
62
63
  static const struct mV_pos *vrm_mV_table;
  static const unsigned char *mV_vrm_table;
db44aaf3a   Rafa³ Bilski   [CPUFREQ] Longhau...
64
65
  
  static unsigned int highest_speed, lowest_speed; /* kHz */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
66
67
  static unsigned int minmult, maxmult;
  static int can_scale_voltage;
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
68
69
  static struct acpi_processor *pr;
  static struct acpi_processor_cx *cx;
275bc6b7f   Rafał Bilski   [CPUFREQ] Longhau...
70
  static u32 acpi_regs_addr;
264166e60   Rafa³ Bilski   [CPUFREQ] Longhau...
71
  static u8 longhaul_flags;
73e107d4a   Rafał Bilski   [CPUFREQ] Longhau...
72
  static unsigned int longhaul_index;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
74
  
  /* Module parameters */
db44aaf3a   Rafa³ Bilski   [CPUFREQ] Longhau...
75
  static int scale_voltage;
905497c4b   Rafał Bilski   [CPUFREQ] Longhau...
76
  static int disable_acpi_c3;
52a2638bf   Rafal Bilski   Longhaul: add aut...
77
  static int revid_errata;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
78

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
79

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
  /* Clock ratios multiplied by 10 */
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
81
82
  static int mults[32];
  static int eblcr[32];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83
84
  static int longhaul_version;
  static struct cpufreq_frequency_table *longhaul_table;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
86
87
88
  static char speedbuffer[8];
  
  static char *print_speed(int speed)
  {
e2aa8732a   Dave Jones   [CPUFREQ] Clean u...
89
  	if (speed < 1000) {
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
90
  		snprintf(speedbuffer, sizeof(speedbuffer), "%dMHz", speed);
e2aa8732a   Dave Jones   [CPUFREQ] Clean u...
91
92
93
94
95
96
97
98
99
  		return speedbuffer;
  	}
  
  	if (speed%1000 == 0)
  		snprintf(speedbuffer, sizeof(speedbuffer),
  			"%dGHz", speed/1000);
  	else
  		snprintf(speedbuffer, sizeof(speedbuffer),
  			"%d.%dGHz", speed/1000, (speed%1000)/100);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
101
102
  
  	return speedbuffer;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
  
  
  static unsigned int calc_speed(int mult)
  {
  	int khz;
  	khz = (mult/10)*fsb;
  	if (mult%10)
  		khz += fsb/2;
  	khz *= 1000;
  	return khz;
  }
  
  
  static int longhaul_get_cpu_mult(void)
  {
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
118
  	unsigned long invalue = 0, lo, hi;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
119

ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
120
121
122
123
  	rdmsr(MSR_IA32_EBL_CR_POWERON, lo, hi);
  	invalue = (lo & (1<<22|1<<23|1<<24|1<<25))>>22;
  	if (longhaul_version == TYPE_LONGHAUL_V2 ||
  	    longhaul_version == TYPE_POWERSAVER) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
  		if (lo & (1<<27))
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
125
  			invalue += 16;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
126
  	}
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
127
  	return eblcr[invalue];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
  }
dadb49d87   Rafa³ Bilski   [CPUFREQ] Longhau...
129
  /* For processor with BCR2 MSR */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130

ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
131
  static void do_longhaul1(unsigned int mults_index)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
  {
dadb49d87   Rafa³ Bilski   [CPUFREQ] Longhau...
133
  	union msr_bcr2 bcr2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
134

dadb49d87   Rafa³ Bilski   [CPUFREQ] Longhau...
135
136
137
  	rdmsrl(MSR_VIA_BCR2, bcr2.val);
  	/* Enable software clock multiplier */
  	bcr2.bits.ESOFTBF = 1;
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
138
  	bcr2.bits.CLOCKMUL = mults_index & 0xff;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
139

dadb49d87   Rafa³ Bilski   [CPUFREQ] Longhau...
140
141
  	/* Sync to timer tick */
  	safe_halt();
dadb49d87   Rafa³ Bilski   [CPUFREQ] Longhau...
142
143
  	/* Change frequency on next halt or sleep */
  	wrmsrl(MSR_VIA_BCR2, bcr2.val);
179da8e6e   Rafa³ Bilski   [CPUFREQ] Longhau...
144
145
146
  	/* Invoke transition */
  	ACPI_FLUSH_CPU_CACHE();
  	halt();
dadb49d87   Rafa³ Bilski   [CPUFREQ] Longhau...
147
148
149
150
151
152
153
  
  	/* Disable software clock multiplier */
  	local_irq_disable();
  	rdmsrl(MSR_VIA_BCR2, bcr2.val);
  	bcr2.bits.ESOFTBF = 0;
  	wrmsrl(MSR_VIA_BCR2, bcr2.val);
  }
3be6a48f3   Dave Jones   [CPUFREQ] longhau...
154

dadb49d87   Rafa³ Bilski   [CPUFREQ] Longhau...
155
  /* For processor with Longhaul MSR */
3be6a48f3   Dave Jones   [CPUFREQ] longhau...
156

ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
157
  static void do_powersaver(int cx_address, unsigned int mults_index,
73e107d4a   Rafał Bilski   [CPUFREQ] Longhau...
158
  			  unsigned int dir)
dadb49d87   Rafa³ Bilski   [CPUFREQ] Longhau...
159
160
161
  {
  	union msr_longhaul longhaul;
  	u32 t;
3be6a48f3   Dave Jones   [CPUFREQ] longhau...
162

dadb49d87   Rafa³ Bilski   [CPUFREQ] Longhau...
163
  	rdmsrl(MSR_VIA_LONGHAUL, longhaul.val);
348f31ed2   Rafa³ Bilski   [CPUFREQ] Longhau...
164
  	/* Setup new frequency */
52a2638bf   Rafal Bilski   Longhaul: add aut...
165
166
167
168
  	if (!revid_errata)
  		longhaul.bits.RevisionKey = longhaul.bits.RevisionID;
  	else
  		longhaul.bits.RevisionKey = 0;
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
169
170
  	longhaul.bits.SoftBusRatio = mults_index & 0xf;
  	longhaul.bits.SoftBusRatio4 = (mults_index & 0x10) >> 4;
348f31ed2   Rafa³ Bilski   [CPUFREQ] Longhau...
171
172
  	/* Setup new voltage */
  	if (can_scale_voltage)
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
173
  		longhaul.bits.SoftVID = (mults_index >> 8) & 0x1f;
348f31ed2   Rafa³ Bilski   [CPUFREQ] Longhau...
174
175
176
  	/* Sync to timer tick */
  	safe_halt();
  	/* Raise voltage if necessary */
73e107d4a   Rafał Bilski   [CPUFREQ] Longhau...
177
  	if (can_scale_voltage && dir) {
db44aaf3a   Rafa³ Bilski   [CPUFREQ] Longhau...
178
  		longhaul.bits.EnableSoftVID = 1;
348f31ed2   Rafa³ Bilski   [CPUFREQ] Longhau...
179
180
181
182
183
184
185
186
187
188
189
  		wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
  		/* Change voltage */
  		if (!cx_address) {
  			ACPI_FLUSH_CPU_CACHE();
  			halt();
  		} else {
  			ACPI_FLUSH_CPU_CACHE();
  			/* Invoke C3 */
  			inb(cx_address);
  			/* Dummy op - must do something useless after P_LVL3
  			 * read */
bd0561c9d   Dave Jones   [CPUFREQ] Fix up ...
190
  			t = inl(acpi_gbl_FADT.xpm_timer_block.address);
348f31ed2   Rafa³ Bilski   [CPUFREQ] Longhau...
191
192
193
  		}
  		longhaul.bits.EnableSoftVID = 0;
  		wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
db44aaf3a   Rafa³ Bilski   [CPUFREQ] Longhau...
194
  	}
dadb49d87   Rafa³ Bilski   [CPUFREQ] Longhau...
195
  	/* Change frequency on next halt or sleep */
348f31ed2   Rafa³ Bilski   [CPUFREQ] Longhau...
196
  	longhaul.bits.EnableSoftBusRatio = 1;
dadb49d87   Rafa³ Bilski   [CPUFREQ] Longhau...
197
  	wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
264166e60   Rafa³ Bilski   [CPUFREQ] Longhau...
198
  	if (!cx_address) {
7f1be8924   rafalbilski@interia.pl   [CPUFREQ] Longhau...
199
  		ACPI_FLUSH_CPU_CACHE();
7f1be8924   rafalbilski@interia.pl   [CPUFREQ] Longhau...
200
201
202
203
204
205
  		halt();
  	} else {
  		ACPI_FLUSH_CPU_CACHE();
  		/* Invoke C3 */
  		inb(cx_address);
  		/* Dummy op - must do something useless after P_LVL3 read */
cee324b14   Alexey Starikovskiy   ACPICA: use new A...
206
  		t = inl(acpi_gbl_FADT.xpm_timer_block.address);
7f1be8924   rafalbilski@interia.pl   [CPUFREQ] Longhau...
207
  	}
dadb49d87   Rafa³ Bilski   [CPUFREQ] Longhau...
208
  	/* Disable bus ratio bit */
dadb49d87   Rafa³ Bilski   [CPUFREQ] Longhau...
209
  	longhaul.bits.EnableSoftBusRatio = 0;
dadb49d87   Rafa³ Bilski   [CPUFREQ] Longhau...
210
  	wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
348f31ed2   Rafa³ Bilski   [CPUFREQ] Longhau...
211
212
  
  	/* Reduce voltage if necessary */
73e107d4a   Rafał Bilski   [CPUFREQ] Longhau...
213
  	if (can_scale_voltage && !dir) {
348f31ed2   Rafa³ Bilski   [CPUFREQ] Longhau...
214
215
216
217
218
219
220
221
222
223
224
225
  		longhaul.bits.EnableSoftVID = 1;
  		wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
  		/* Change voltage */
  		if (!cx_address) {
  			ACPI_FLUSH_CPU_CACHE();
  			halt();
  		} else {
  			ACPI_FLUSH_CPU_CACHE();
  			/* Invoke C3 */
  			inb(cx_address);
  			/* Dummy op - must do something useless after P_LVL3
  			 * read */
bd0561c9d   Dave Jones   [CPUFREQ] Fix up ...
226
  			t = inl(acpi_gbl_FADT.xpm_timer_block.address);
348f31ed2   Rafa³ Bilski   [CPUFREQ] Longhau...
227
228
229
  		}
  		longhaul.bits.EnableSoftVID = 0;
  		wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
348f31ed2   Rafa³ Bilski   [CPUFREQ] Longhau...
230
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
231
232
233
234
  }
  
  /**
   * longhaul_set_cpu_frequency()
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
235
   * @mults_index : bitpattern of the new multiplier.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236
237
238
   *
   * Sets a new clock ratio.
   */
73e107d4a   Rafał Bilski   [CPUFREQ] Longhau...
239
  static void longhaul_setstate(unsigned int table_index)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240
  {
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
241
  	unsigned int mults_index;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
242
243
  	int speed, mult;
  	struct cpufreq_freqs freqs;
dadb49d87   Rafa³ Bilski   [CPUFREQ] Longhau...
244
245
  	unsigned long flags;
  	unsigned int pic1_mask, pic2_mask;
689eba77c   Rafał Bilski   [CPUFREQ] Longhau...
246
  	u16 bm_status = 0;
275bc6b7f   Rafał Bilski   [CPUFREQ] Longhau...
247
  	u32 bm_timeout = 1000;
73e107d4a   Rafał Bilski   [CPUFREQ] Longhau...
248
  	unsigned int dir = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
249

ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
250
  	mults_index = longhaul_table[table_index].index;
73e107d4a   Rafał Bilski   [CPUFREQ] Longhau...
251
  	/* Safety precautions */
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
252
  	mult = mults[mults_index & 0x1f];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
253
254
  	if (mult == -1)
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255
256
257
  	speed = calc_speed(mult);
  	if ((speed > highest_speed) || (speed < lowest_speed))
  		return;
73e107d4a   Rafał Bilski   [CPUFREQ] Longhau...
258
259
260
  	/* Voltage transition before frequency transition? */
  	if (can_scale_voltage && longhaul_index < table_index)
  		dir = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261
262
263
264
265
266
  
  	freqs.old = calc_speed(longhaul_get_cpu_mult());
  	freqs.new = speed;
  	freqs.cpu = 0; /* longhaul.c is UP only driver */
  
  	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
2d06d8c49   Dominik Brodowski   [CPUFREQ] use dyn...
267
268
  	pr_debug("Setting to FSB:%dMHz Mult:%d.%dx (%s)
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
  			fsb, mult/10, mult%10, print_speed(speed/1000));
52a2638bf   Rafal Bilski   Longhaul: add aut...
270
  retry_loop:
dadb49d87   Rafa³ Bilski   [CPUFREQ] Longhau...
271
272
273
274
275
  	preempt_disable();
  	local_irq_save(flags);
  
  	pic2_mask = inb(0xA1);
  	pic1_mask = inb(0x21);	/* works on C3. save mask. */
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
276
277
  	outb(0xFF, 0xA1);	/* Overkill */
  	outb(0xFE, 0x21);	/* TMR0 only */
dadb49d87   Rafa³ Bilski   [CPUFREQ] Longhau...
278

489dc5cb1   Rafał Bilski   [CPUFREQ] Longhau...
279
  	/* Wait while PCI bus is busy. */
689eba77c   Rafał Bilski   [CPUFREQ] Longhau...
280
281
282
  	if (acpi_regs_addr && (longhaul_flags & USE_NORTHBRIDGE
  	    || ((pr != NULL) && pr->flags.bm_control))) {
  		bm_status = inw(acpi_regs_addr);
275bc6b7f   Rafał Bilski   [CPUFREQ] Longhau...
283
  		bm_status &= 1 << 4;
489dc5cb1   Rafał Bilski   [CPUFREQ] Longhau...
284
  		while (bm_status && bm_timeout) {
689eba77c   Rafał Bilski   [CPUFREQ] Longhau...
285
  			outw(1 << 4, acpi_regs_addr);
489dc5cb1   Rafał Bilski   [CPUFREQ] Longhau...
286
  			bm_timeout--;
689eba77c   Rafał Bilski   [CPUFREQ] Longhau...
287
  			bm_status = inw(acpi_regs_addr);
275bc6b7f   Rafał Bilski   [CPUFREQ] Longhau...
288
  			bm_status &= 1 << 4;
489dc5cb1   Rafał Bilski   [CPUFREQ] Longhau...
289
290
  		}
  	}
264166e60   Rafa³ Bilski   [CPUFREQ] Longhau...
291
292
293
294
  	if (longhaul_flags & USE_NORTHBRIDGE) {
  		/* Disable AGP and PCI arbiters */
  		outb(3, 0x22);
  	} else if ((pr != NULL) && pr->flags.bm_control) {
73e107d4a   Rafał Bilski   [CPUFREQ] Longhau...
295
  		/* Disable bus master arbitration */
fb318cbff   Lin Ming   ACPI: cpufreq: us...
296
  		acpi_write_bit_register(ACPI_BITREG_ARB_DISABLE, 1);
dadb49d87   Rafa³ Bilski   [CPUFREQ] Longhau...
297
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298
299
300
301
302
  	switch (longhaul_version) {
  
  	/*
  	 * Longhaul v1. (Samuel[C5A] and Samuel2 stepping 0[C5B])
  	 * Software controlled multipliers only.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
303
304
  	 */
  	case TYPE_LONGHAUL_V1:
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
305
  		do_longhaul1(mults_index);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
306
307
308
  		break;
  
  	/*
2b8c0e130   Rafa³ Bilski   [CPUFREQ] Longhau...
309
310
  	 * Longhaul v2 appears in Samuel2 Steppings 1->7 [C5B] and Ezra [C5C]
  	 *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
311
  	 * Longhaul v3 (aka Powersaver). (Ezra-T [C5M] & Nehemiah [C5N])
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
312
313
314
  	 * Nehemiah can do FSB scaling too, but this has never been proven
  	 * to work in practice.
  	 */
2b8c0e130   Rafa³ Bilski   [CPUFREQ] Longhau...
315
  	case TYPE_LONGHAUL_V2:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
316
  	case TYPE_POWERSAVER:
264166e60   Rafa³ Bilski   [CPUFREQ] Longhau...
317
318
  		if (longhaul_flags & USE_ACPI_C3) {
  			/* Don't allow wakeup */
fb318cbff   Lin Ming   ACPI: cpufreq: us...
319
  			acpi_write_bit_register(ACPI_BITREG_BUS_MASTER_RLD, 0);
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
320
  			do_powersaver(cx->address, mults_index, dir);
264166e60   Rafa³ Bilski   [CPUFREQ] Longhau...
321
  		} else {
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
322
  			do_powersaver(0, mults_index, dir);
264166e60   Rafa³ Bilski   [CPUFREQ] Longhau...
323
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
324
325
  		break;
  	}
264166e60   Rafa³ Bilski   [CPUFREQ] Longhau...
326
327
328
329
  	if (longhaul_flags & USE_NORTHBRIDGE) {
  		/* Enable arbiters */
  		outb(0, 0x22);
  	} else if ((pr != NULL) && pr->flags.bm_control) {
179da8e6e   Rafa³ Bilski   [CPUFREQ] Longhau...
330
  		/* Enable bus master arbitration */
fb318cbff   Lin Ming   ACPI: cpufreq: us...
331
  		acpi_write_bit_register(ACPI_BITREG_ARB_DISABLE, 0);
dadb49d87   Rafa³ Bilski   [CPUFREQ] Longhau...
332
  	}
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
333
334
  	outb(pic2_mask, 0xA1);	/* restore mask */
  	outb(pic1_mask, 0x21);
dadb49d87   Rafa³ Bilski   [CPUFREQ] Longhau...
335
336
337
  
  	local_irq_restore(flags);
  	preempt_enable();
2b8c0e130   Rafa³ Bilski   [CPUFREQ] Longhau...
338
  	freqs.new = calc_speed(longhaul_get_cpu_mult());
52a2638bf   Rafal Bilski   Longhaul: add aut...
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
  	/* Check if requested frequency is set. */
  	if (unlikely(freqs.new != speed)) {
  		printk(KERN_INFO PFX "Failed to set requested frequency!
  ");
  		/* Revision ID = 1 but processor is expecting revision key
  		 * equal to 0. Jumpers at the bottom of processor will change
  		 * multiplier and FSB, but will not change bits in Longhaul
  		 * MSR nor enable voltage scaling. */
  		if (!revid_errata) {
  			printk(KERN_INFO PFX "Enabling \"Ignore Revision ID\" "
  						"option.
  ");
  			revid_errata = 1;
  			msleep(200);
  			goto retry_loop;
  		}
  		/* Why ACPI C3 sometimes doesn't work is a mystery for me.
  		 * But it does happen. Processor is entering ACPI C3 state,
  		 * but it doesn't change frequency. I tried poking various
  		 * bits in northbridge registers, but without success. */
  		if (longhaul_flags & USE_ACPI_C3) {
  			printk(KERN_INFO PFX "Disabling ACPI C3 support.
  ");
  			longhaul_flags &= ~USE_ACPI_C3;
  			if (revid_errata) {
  				printk(KERN_INFO PFX "Disabling \"Ignore "
  						"Revision ID\" option.
  ");
  				revid_errata = 0;
  			}
  			msleep(200);
  			goto retry_loop;
  		}
  		/* This shouldn't happen. Longhaul ver. 2 was reported not
  		 * working on processors without voltage scaling, but with
  		 * RevID = 1. RevID errata will make things right. Just
  		 * to be 100% sure. */
  		if (longhaul_version == TYPE_LONGHAUL_V2) {
  			printk(KERN_INFO PFX "Switching to Longhaul ver. 1
  ");
  			longhaul_version = TYPE_LONGHAUL_V1;
  			msleep(200);
  			goto retry_loop;
  		}
  	}
  	/* Report true CPU frequency */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
385
  	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
489dc5cb1   Rafał Bilski   [CPUFREQ] Longhau...
386
387
  
  	if (!bm_timeout)
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
388
389
390
  		printk(KERN_INFO PFX "Warning: Timeout while waiting for "
  				"idle PCI bus.
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
391
392
393
394
395
396
397
398
399
400
401
402
  }
  
  /*
   * Centaur decided to make life a little more tricky.
   * Only longhaul v1 is allowed to read EBLCR BSEL[0:1].
   * Samuel2 and above have to try and guess what the FSB is.
   * We do this by assuming we booted at maximum multiplier, and interpolate
   * between that value multiplied by possible FSBs and cpu_mhz which
   * was calculated at boot time. Really ugly, but no other way to do this.
   */
  
  #define ROUNDING	0xf
24ebead82   Rafa³ Bilski   [CPUFREQ] Longhau...
403
  static int guess_fsb(int mult)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
404
  {
46ef955f5   Rafa³ Bilski   [CPUFREQ] Longhau...
405
  	int speed = cpu_khz / 1000;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
406
  	int i;
46ef955f5   Rafa³ Bilski   [CPUFREQ] Longhau...
407
408
409
410
411
412
413
414
415
  	int speeds[] = { 666, 1000, 1333, 2000 };
  	int f_max, f_min;
  
  	for (i = 0; i < 4; i++) {
  		f_max = ((speeds[i] * mult) + 50) / 100;
  		f_max += (ROUNDING / 2);
  		f_min = f_max - ROUNDING;
  		if ((speed <= f_max) && (speed >= f_min))
  			return speeds[i] / 10;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
416
417
418
  	}
  	return 0;
  }
2530573e4   Holger Freyther   [CPUFREQ] Fix sec...
419
  static int __cpuinit longhaul_get_ranges(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
420
  {
73e107d4a   Rafał Bilski   [CPUFREQ] Longhau...
421
422
  	unsigned int i, j, k = 0;
  	unsigned int ratio;
980342a7e   Rafa³ Bilski   [CPUFREQ] Longhau...
423
  	int mult;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
424

980342a7e   Rafa³ Bilski   [CPUFREQ] Longhau...
425
426
427
428
429
430
431
432
433
434
435
436
437
438
  	/* Get current frequency */
  	mult = longhaul_get_cpu_mult();
  	if (mult == -1) {
  		printk(KERN_INFO PFX "Invalid (reserved) multiplier!
  ");
  		return -EINVAL;
  	}
  	fsb = guess_fsb(mult);
  	if (fsb == 0) {
  		printk(KERN_INFO PFX "Invalid (reserved) FSB!
  ");
  		return -EINVAL;
  	}
  	/* Get max multiplier - as we always did.
0d2eb44f6   Lucas De Marchi   x86: Fix common m...
439
  	 * Longhaul MSR is useful only when voltage scaling is enabled.
980342a7e   Rafa³ Bilski   [CPUFREQ] Longhau...
440
441
442
  	 * C3 is booting at max anyway. */
  	maxmult = mult;
  	/* Get min multiplier */
9addf3b63   Rafa³ Bilski   [CPUFREQ] Longhau...
443
444
445
  	switch (cpu_model) {
  	case CPU_NEHEMIAH:
  		minmult = 50;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
446
  		break;
9addf3b63   Rafa³ Bilski   [CPUFREQ] Longhau...
447
448
449
450
451
  	case CPU_NEHEMIAH_C:
  		minmult = 40;
  		break;
  	default:
  		minmult = 30;
980342a7e   Rafa³ Bilski   [CPUFREQ] Longhau...
452
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
453
  	}
2d06d8c49   Dominik Brodowski   [CPUFREQ] use dyn...
454
455
  	pr_debug("MinMult:%d.%dx MaxMult:%d.%dx
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
456
  		 minmult/10, minmult%10, maxmult/10, maxmult%10);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
457
458
  	highest_speed = calc_speed(maxmult);
  	lowest_speed = calc_speed(minmult);
2d06d8c49   Dominik Brodowski   [CPUFREQ] use dyn...
459
460
  	pr_debug("FSB:%dMHz  Lowest speed: %s   Highest speed:%s
  ", fsb,
cee324b14   Alexey Starikovskiy   ACPICA: use new A...
461
  		 print_speed(lowest_speed/1000),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
462
463
464
  		 print_speed(highest_speed/1000));
  
  	if (lowest_speed == highest_speed) {
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
465
466
  		printk(KERN_INFO PFX "highestspeed == lowest, aborting.
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
467
468
469
  		return -EINVAL;
  	}
  	if (lowest_speed > highest_speed) {
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
470
471
  		printk(KERN_INFO PFX "nonsense! lowest (%d > %d) !
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
472
473
474
  			lowest_speed, highest_speed);
  		return -EINVAL;
  	}
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
475
476
477
  	longhaul_table = kmalloc((numscales + 1) * sizeof(*longhaul_table),
  			GFP_KERNEL);
  	if (!longhaul_table)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
478
  		return -ENOMEM;
73e107d4a   Rafał Bilski   [CPUFREQ] Longhau...
479
  	for (j = 0; j < numscales; j++) {
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
480
  		ratio = mults[j];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
481
482
483
484
485
486
487
488
  		if (ratio == -1)
  			continue;
  		if (ratio > maxmult || ratio < minmult)
  			continue;
  		longhaul_table[k].frequency = calc_speed(ratio);
  		longhaul_table[k].index	= j;
  		k++;
  	}
73e107d4a   Rafał Bilski   [CPUFREQ] Longhau...
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
  	if (k <= 1) {
  		kfree(longhaul_table);
  		return -ENODEV;
  	}
  	/* Sort */
  	for (j = 0; j < k - 1; j++) {
  		unsigned int min_f, min_i;
  		min_f = longhaul_table[j].frequency;
  		min_i = j;
  		for (i = j + 1; i < k; i++) {
  			if (longhaul_table[i].frequency < min_f) {
  				min_f = longhaul_table[i].frequency;
  				min_i = i;
  			}
  		}
  		if (min_i != j) {
91420220d   Dave Jones   [CPUFREQ] Use swa...
505
506
507
508
  			swap(longhaul_table[j].frequency,
  			     longhaul_table[min_i].frequency);
  			swap(longhaul_table[j].index,
  			     longhaul_table[min_i].index);
73e107d4a   Rafał Bilski   [CPUFREQ] Longhau...
509
510
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
511
512
  
  	longhaul_table[k].frequency = CPUFREQ_TABLE_END;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
513

73e107d4a   Rafał Bilski   [CPUFREQ] Longhau...
514
515
  	/* Find index we are running on */
  	for (j = 0; j < k; j++) {
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
516
  		if (mults[longhaul_table[j].index & 0x1f] == mult) {
73e107d4a   Rafał Bilski   [CPUFREQ] Longhau...
517
518
519
520
  			longhaul_index = j;
  			break;
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
521
522
  	return 0;
  }
2530573e4   Holger Freyther   [CPUFREQ] Fix sec...
523
  static void __cpuinit longhaul_setup_voltagescaling(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
524
525
  {
  	union msr_longhaul longhaul;
73e107d4a   Rafał Bilski   [CPUFREQ] Longhau...
526
  	struct mV_pos minvid, maxvid, vid;
db44aaf3a   Rafa³ Bilski   [CPUFREQ] Longhau...
527
  	unsigned int j, speed, pos, kHz_step, numvscales;
348f31ed2   Rafa³ Bilski   [CPUFREQ] Longhau...
528
  	int min_vid_speed;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
529

db44aaf3a   Rafa³ Bilski   [CPUFREQ] Longhau...
530
531
532
533
  	rdmsrl(MSR_VIA_LONGHAUL, longhaul.val);
  	if (!(longhaul.bits.RevisionID & 1)) {
  		printk(KERN_INFO PFX "Voltage scaling not supported by CPU.
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
534
  		return;
db44aaf3a   Rafa³ Bilski   [CPUFREQ] Longhau...
535
536
537
  	}
  
  	if (!longhaul.bits.VRMRev) {
73e107d4a   Rafał Bilski   [CPUFREQ] Longhau...
538
539
  		printk(KERN_INFO PFX "VRM 8.5
  ");
db44aaf3a   Rafa³ Bilski   [CPUFREQ] Longhau...
540
541
542
  		vrm_mV_table = &vrm85_mV[0];
  		mV_vrm_table = &mV_vrm85[0];
  	} else {
73e107d4a   Rafał Bilski   [CPUFREQ] Longhau...
543
544
  		printk(KERN_INFO PFX "Mobile VRM
  ");
2b8c0e130   Rafa³ Bilski   [CPUFREQ] Longhau...
545
546
  		if (cpu_model < CPU_NEHEMIAH)
  			return;
db44aaf3a   Rafa³ Bilski   [CPUFREQ] Longhau...
547
548
549
  		vrm_mV_table = &mobilevrm_mV[0];
  		mV_vrm_table = &mV_mobilevrm[0];
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
550

db44aaf3a   Rafa³ Bilski   [CPUFREQ] Longhau...
551
552
  	minvid = vrm_mV_table[longhaul.bits.MinimumVID];
  	maxvid = vrm_mV_table[longhaul.bits.MaximumVID];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
553

db44aaf3a   Rafa³ Bilski   [CPUFREQ] Longhau...
554
  	if (minvid.mV == 0 || maxvid.mV == 0 || minvid.mV > maxvid.mV) {
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
555
  		printk(KERN_INFO PFX "Bogus values Min:%d.%03d Max:%d.%03d. "
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
556
557
  					"Voltage scaling disabled.
  ",
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
558
559
  					minvid.mV/1000, minvid.mV%1000,
  					maxvid.mV/1000, maxvid.mV%1000);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
560
561
  		return;
  	}
db44aaf3a   Rafa³ Bilski   [CPUFREQ] Longhau...
562
  	if (minvid.mV == maxvid.mV) {
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
563
564
565
566
  		printk(KERN_INFO PFX "Claims to support voltage scaling but "
  				"min & max are both %d.%03d. "
  				"Voltage scaling disabled
  ",
db44aaf3a   Rafa³ Bilski   [CPUFREQ] Longhau...
567
  				maxvid.mV/1000, maxvid.mV%1000);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
568
569
  		return;
  	}
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
570
  	/* How many voltage steps*/
348f31ed2   Rafa³ Bilski   [CPUFREQ] Longhau...
571
572
573
574
575
576
  	numvscales = maxvid.pos - minvid.pos + 1;
  	printk(KERN_INFO PFX
  		"Max VID=%d.%03d  "
  		"Min VID=%d.%03d, "
  		"%d possible voltage scales
  ",
db44aaf3a   Rafa³ Bilski   [CPUFREQ] Longhau...
577
578
579
  		maxvid.mV/1000, maxvid.mV%1000,
  		minvid.mV/1000, minvid.mV%1000,
  		numvscales);
348f31ed2   Rafa³ Bilski   [CPUFREQ] Longhau...
580
581
582
583
584
  
  	/* Calculate max frequency at min voltage */
  	j = longhaul.bits.MinMHzBR;
  	if (longhaul.bits.MinMHzBR4)
  		j += 16;
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
585
  	min_vid_speed = eblcr[j];
348f31ed2   Rafa³ Bilski   [CPUFREQ] Longhau...
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
  	if (min_vid_speed == -1)
  		return;
  	switch (longhaul.bits.MinMHzFSB) {
  	case 0:
  		min_vid_speed *= 13333;
  		break;
  	case 1:
  		min_vid_speed *= 10000;
  		break;
  	case 3:
  		min_vid_speed *= 6666;
  		break;
  	default:
  		return;
  		break;
  	}
  	if (min_vid_speed >= highest_speed)
  		return;
  	/* Calculate kHz for one voltage step */
  	kHz_step = (highest_speed - min_vid_speed) / numvscales;
db44aaf3a   Rafa³ Bilski   [CPUFREQ] Longhau...
606
607
608
  	j = 0;
  	while (longhaul_table[j].frequency != CPUFREQ_TABLE_END) {
  		speed = longhaul_table[j].frequency;
348f31ed2   Rafa³ Bilski   [CPUFREQ] Longhau...
609
610
611
612
  		if (speed > min_vid_speed)
  			pos = (speed - min_vid_speed) / kHz_step + minvid.pos;
  		else
  			pos = minvid.pos;
73e107d4a   Rafał Bilski   [CPUFREQ] Longhau...
613
614
  		longhaul_table[j].index |= mV_vrm_table[pos] << 8;
  		vid = vrm_mV_table[mV_vrm_table[pos]];
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
615
616
617
  		printk(KERN_INFO PFX "f: %d kHz, index: %d, vid: %d mV
  ",
  				speed, j, vid.mV);
db44aaf3a   Rafa³ Bilski   [CPUFREQ] Longhau...
618
  		j++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
619
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
620
  	can_scale_voltage = 1;
73e107d4a   Rafał Bilski   [CPUFREQ] Longhau...
621
622
  	printk(KERN_INFO PFX "Voltage scaling enabled.
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
623
624
625
626
627
628
629
630
631
632
633
634
635
  }
  
  
  static int longhaul_verify(struct cpufreq_policy *policy)
  {
  	return cpufreq_frequency_table_verify(policy, longhaul_table);
  }
  
  
  static int longhaul_target(struct cpufreq_policy *policy,
  			    unsigned int target_freq, unsigned int relation)
  {
  	unsigned int table_index = 0;
73e107d4a   Rafał Bilski   [CPUFREQ] Longhau...
636
637
638
  	unsigned int i;
  	unsigned int dir = 0;
  	u8 vid, current_vid;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
639

ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
640
641
  	if (cpufreq_frequency_table_target(policy, longhaul_table, target_freq,
  				relation, &table_index))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
642
  		return -EINVAL;
73e107d4a   Rafał Bilski   [CPUFREQ] Longhau...
643
644
645
  	/* Don't set same frequency again */
  	if (longhaul_index == table_index)
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
646

73e107d4a   Rafał Bilski   [CPUFREQ] Longhau...
647
648
649
650
651
652
653
654
655
  	if (!can_scale_voltage)
  		longhaul_setstate(table_index);
  	else {
  		/* On test system voltage transitions exceeding single
  		 * step up or down were turning motherboard off. Both
  		 * "ondemand" and "userspace" are unsafe. C7 is doing
  		 * this in hardware, C3 is old and we need to do this
  		 * in software. */
  		i = longhaul_index;
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
656
657
  		current_vid = (longhaul_table[longhaul_index].index >> 8);
  		current_vid &= 0x1f;
73e107d4a   Rafał Bilski   [CPUFREQ] Longhau...
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
  		if (table_index > longhaul_index)
  			dir = 1;
  		while (i != table_index) {
  			vid = (longhaul_table[i].index >> 8) & 0x1f;
  			if (vid != current_vid) {
  				longhaul_setstate(i);
  				current_vid = vid;
  				msleep(200);
  			}
  			if (dir)
  				i++;
  			else
  				i--;
  		}
  		longhaul_setstate(table_index);
  	}
  	longhaul_index = table_index;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
675
676
677
678
679
680
681
682
683
684
  	return 0;
  }
  
  
  static unsigned int longhaul_get(unsigned int cpu)
  {
  	if (cpu)
  		return 0;
  	return calc_speed(longhaul_get_cpu_mult());
  }
c4a96c1eb   Adrian Bunk   [CPUFREQ] Make lo...
685
686
687
  static acpi_status longhaul_walk_callback(acpi_handle obj_handle,
  					  u32 nesting_level,
  					  void *context, void **return_value)
dadb49d87   Rafa³ Bilski   [CPUFREQ] Longhau...
688
689
  {
  	struct acpi_device *d;
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
690
  	if (acpi_bus_get_device(obj_handle, &d))
dadb49d87   Rafa³ Bilski   [CPUFREQ] Longhau...
691
  		return 0;
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
692

ade1af771   Jan Engelhardt   x86: remove unned...
693
  	*return_value = acpi_driver_data(d);
dadb49d87   Rafa³ Bilski   [CPUFREQ] Longhau...
694
695
  	return 1;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
696

179da8e6e   Rafa³ Bilski   [CPUFREQ] Longhau...
697
698
699
700
  /* VIA don't support PM2 reg, but have something similar */
  static int enable_arbiter_disable(void)
  {
  	struct pci_dev *dev;
73e107d4a   Rafał Bilski   [CPUFREQ] Longhau...
701
  	int status = 1;
7f1be8924   rafalbilski@interia.pl   [CPUFREQ] Longhau...
702
  	int reg;
179da8e6e   Rafa³ Bilski   [CPUFREQ] Longhau...
703
704
705
  	u8 pci_cmd;
  
  	/* Find PLE133 host bridge */
7f1be8924   rafalbilski@interia.pl   [CPUFREQ] Longhau...
706
  	reg = 0x78;
fb48e1564   Rafał Bilski   [CPUFREQ] Longhau...
707
708
  	dev = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8601_0,
  			     NULL);
a09d60a62   Rafał Bilski   [CPUFREQ] Longhau...
709
710
711
712
  	/* Find PM133/VT8605 host bridge */
  	if (dev == NULL)
  		dev = pci_get_device(PCI_VENDOR_ID_VIA,
  				     PCI_DEVICE_ID_VIA_8605_0, NULL);
7f1be8924   rafalbilski@interia.pl   [CPUFREQ] Longhau...
713
714
  	/* Find CLE266 host bridge */
  	if (dev == NULL) {
7f1be8924   rafalbilski@interia.pl   [CPUFREQ] Longhau...
715
  		reg = 0x76;
fb48e1564   Rafał Bilski   [CPUFREQ] Longhau...
716
717
  		dev = pci_get_device(PCI_VENDOR_ID_VIA,
  				     PCI_DEVICE_ID_VIA_862X_0, NULL);
db2fb9db5   Rafa³ Bilski   [CPUFREQ] Longhau...
718
719
  		/* Find CN400 V-Link host bridge */
  		if (dev == NULL)
fb48e1564   Rafał Bilski   [CPUFREQ] Longhau...
720
  			dev = pci_get_device(PCI_VENDOR_ID_VIA, 0x7259, NULL);
7f1be8924   rafalbilski@interia.pl   [CPUFREQ] Longhau...
721
  	}
179da8e6e   Rafa³ Bilski   [CPUFREQ] Longhau...
722
723
  	if (dev != NULL) {
  		/* Enable access to port 0x22 */
7f1be8924   rafalbilski@interia.pl   [CPUFREQ] Longhau...
724
  		pci_read_config_byte(dev, reg, &pci_cmd);
786f46b26   Rafa³ Bilski   [CPUFREQ] Longhau...
725
  		if (!(pci_cmd & 1<<7)) {
179da8e6e   Rafa³ Bilski   [CPUFREQ] Longhau...
726
  			pci_cmd |= 1<<7;
7f1be8924   rafalbilski@interia.pl   [CPUFREQ] Longhau...
727
  			pci_write_config_byte(dev, reg, pci_cmd);
786f46b26   Rafa³ Bilski   [CPUFREQ] Longhau...
728
729
730
731
732
  			pci_read_config_byte(dev, reg, &pci_cmd);
  			if (!(pci_cmd & 1<<7)) {
  				printk(KERN_ERR PFX
  					"Can't enable access to port 0x22.
  ");
fb48e1564   Rafał Bilski   [CPUFREQ] Longhau...
733
  				status = 0;
786f46b26   Rafa³ Bilski   [CPUFREQ] Longhau...
734
  			}
179da8e6e   Rafa³ Bilski   [CPUFREQ] Longhau...
735
  		}
fb48e1564   Rafał Bilski   [CPUFREQ] Longhau...
736
737
  		pci_dev_put(dev);
  		return status;
179da8e6e   Rafa³ Bilski   [CPUFREQ] Longhau...
738
739
740
  	}
  	return 0;
  }
7d5edcc02   Rafał Bilski   [CPUFREQ] Longhau...
741
  static int longhaul_setup_southbridge(void)
786f46b26   Rafa³ Bilski   [CPUFREQ] Longhau...
742
743
744
745
746
  {
  	struct pci_dev *dev;
  	u8 pci_cmd;
  
  	/* Find VT8235 southbridge */
fb48e1564   Rafał Bilski   [CPUFREQ] Longhau...
747
  	dev = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8235, NULL);
920dd0fbb   Rafał Bilski   [CPUFREQ] Longhau...
748
  	if (dev == NULL)
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
749
  		/* Find VT8237 southbridge */
920dd0fbb   Rafał Bilski   [CPUFREQ] Longhau...
750
751
  		dev = pci_get_device(PCI_VENDOR_ID_VIA,
  				     PCI_DEVICE_ID_VIA_8237, NULL);
786f46b26   Rafa³ Bilski   [CPUFREQ] Longhau...
752
753
754
755
756
757
758
759
760
761
762
  	if (dev != NULL) {
  		/* Set transition time to max */
  		pci_read_config_byte(dev, 0xec, &pci_cmd);
  		pci_cmd &= ~(1 << 2);
  		pci_write_config_byte(dev, 0xec, pci_cmd);
  		pci_read_config_byte(dev, 0xe4, &pci_cmd);
  		pci_cmd &= ~(1 << 7);
  		pci_write_config_byte(dev, 0xe4, pci_cmd);
  		pci_read_config_byte(dev, 0xe5, &pci_cmd);
  		pci_cmd |= 1 << 7;
  		pci_write_config_byte(dev, 0xe5, pci_cmd);
275bc6b7f   Rafał Bilski   [CPUFREQ] Longhau...
763
764
765
766
767
  		/* Get address of ACPI registers block*/
  		pci_read_config_byte(dev, 0x81, &pci_cmd);
  		if (pci_cmd & 1 << 7) {
  			pci_read_config_dword(dev, 0x88, &acpi_regs_addr);
  			acpi_regs_addr &= 0xff00;
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
768
769
770
  			printk(KERN_INFO PFX "ACPI I/O at 0x%x
  ",
  					acpi_regs_addr);
275bc6b7f   Rafał Bilski   [CPUFREQ] Longhau...
771
  		}
fb48e1564   Rafał Bilski   [CPUFREQ] Longhau...
772
  		pci_dev_put(dev);
786f46b26   Rafa³ Bilski   [CPUFREQ] Longhau...
773
774
775
776
  		return 1;
  	}
  	return 0;
  }
2530573e4   Holger Freyther   [CPUFREQ] Fix sec...
777
  static int __cpuinit longhaul_cpu_init(struct cpufreq_policy *policy)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
778
  {
92cb7612a   Mike Travis   x86: convert cpui...
779
  	struct cpuinfo_x86 *c = &cpu_data(0);
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
780
  	char *cpuname = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
781
  	int ret;
2b8c0e130   Rafa³ Bilski   [CPUFREQ] Longhau...
782
  	u32 lo, hi;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
783

179da8e6e   Rafa³ Bilski   [CPUFREQ] Longhau...
784
  	/* Check what we have on this motherboard */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
785
786
787
788
789
  	switch (c->x86_model) {
  	case 6:
  		cpu_model = CPU_SAMUEL;
  		cpuname = "C3 'Samuel' [C5A]";
  		longhaul_version = TYPE_LONGHAUL_V1;
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
790
791
  		memcpy(mults, samuel1_mults, sizeof(samuel1_mults));
  		memcpy(eblcr, samuel1_eblcr, sizeof(samuel1_eblcr));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
792
793
794
  		break;
  
  	case 7:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
795
796
  		switch (c->x86_mask) {
  		case 0:
2b8c0e130   Rafa³ Bilski   [CPUFREQ] Longhau...
797
  			longhaul_version = TYPE_LONGHAUL_V1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
798
799
  			cpu_model = CPU_SAMUEL2;
  			cpuname = "C3 'Samuel 2' [C5B]";
2b8c0e130   Rafa³ Bilski   [CPUFREQ] Longhau...
800
801
  			/* Note, this is not a typo, early Samuel2's had
  			 * Samuel1 ratios. */
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
802
803
  			memcpy(mults, samuel1_mults, sizeof(samuel1_mults));
  			memcpy(eblcr, samuel2_eblcr, sizeof(samuel2_eblcr));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
804
805
  			break;
  		case 1 ... 15:
f7f3cad06   Krzysztof Helt   [CPUFREQ] longhau...
806
  			longhaul_version = TYPE_LONGHAUL_V2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
807
808
809
810
811
812
813
  			if (c->x86_mask < 8) {
  				cpu_model = CPU_SAMUEL2;
  				cpuname = "C3 'Samuel 2' [C5B]";
  			} else {
  				cpu_model = CPU_EZRA;
  				cpuname = "C3 'Ezra' [C5C]";
  			}
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
814
815
  			memcpy(mults, ezra_mults, sizeof(ezra_mults));
  			memcpy(eblcr, ezra_eblcr, sizeof(ezra_eblcr));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
816
817
818
819
820
821
822
823
  			break;
  		}
  		break;
  
  	case 8:
  		cpu_model = CPU_EZRA_T;
  		cpuname = "C3 'Ezra-T' [C5M]";
  		longhaul_version = TYPE_POWERSAVER;
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
824
825
826
  		numscales = 32;
  		memcpy(mults, ezrat_mults, sizeof(ezrat_mults));
  		memcpy(eblcr, ezrat_eblcr, sizeof(ezrat_eblcr));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
827
828
829
  		break;
  
  	case 9:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
830
  		longhaul_version = TYPE_POWERSAVER;
0d44b2ba2   Rafa³ Bilski   [CPUFREQ] Longhau...
831
  		numscales = 32;
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
832
833
  		memcpy(mults, nehemiah_mults, sizeof(nehemiah_mults));
  		memcpy(eblcr, nehemiah_eblcr, sizeof(nehemiah_eblcr));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
834
835
  		switch (c->x86_mask) {
  		case 0 ... 1:
980342a7e   Rafa³ Bilski   [CPUFREQ] Longhau...
836
  			cpu_model = CPU_NEHEMIAH;
e57501c15   Rafa³ Bilski   [CPUFREQ] Longhau...
837
  			cpuname = "C3 'Nehemiah A' [C5XLOE]";
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
838
839
  			break;
  		case 2 ... 4:
980342a7e   Rafa³ Bilski   [CPUFREQ] Longhau...
840
  			cpu_model = CPU_NEHEMIAH;
e57501c15   Rafa³ Bilski   [CPUFREQ] Longhau...
841
  			cpuname = "C3 'Nehemiah B' [C5XLOH]";
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
842
843
  			break;
  		case 5 ... 15:
980342a7e   Rafa³ Bilski   [CPUFREQ] Longhau...
844
  			cpu_model = CPU_NEHEMIAH_C;
e57501c15   Rafa³ Bilski   [CPUFREQ] Longhau...
845
  			cpuname = "C3 'Nehemiah C' [C5P]";
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
846
847
848
849
850
851
852
853
  			break;
  		}
  		break;
  
  	default:
  		cpuname = "Unknown";
  		break;
  	}
2b8c0e130   Rafa³ Bilski   [CPUFREQ] Longhau...
854
855
856
857
858
859
860
  	/* Check Longhaul ver. 2 */
  	if (longhaul_version == TYPE_LONGHAUL_V2) {
  		rdmsr(MSR_VIA_LONGHAUL, lo, hi);
  		if (lo == 0 && hi == 0)
  			/* Looks like MSR isn't present */
  			longhaul_version = TYPE_LONGHAUL_V1;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
861

ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
862
  	printk(KERN_INFO PFX "VIA %s CPU detected.  ", cpuname);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
863
864
865
  	switch (longhaul_version) {
  	case TYPE_LONGHAUL_V1:
  	case TYPE_LONGHAUL_V2:
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
866
867
  		printk(KERN_CONT "Longhaul v%d supported.
  ", longhaul_version);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
868
869
  		break;
  	case TYPE_POWERSAVER:
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
870
871
  		printk(KERN_CONT "Powersaver supported.
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
872
873
  		break;
  	};
786f46b26   Rafa³ Bilski   [CPUFREQ] Longhau...
874
  	/* Doesn't hurt */
7d5edcc02   Rafał Bilski   [CPUFREQ] Longhau...
875
  	longhaul_setup_southbridge();
786f46b26   Rafa³ Bilski   [CPUFREQ] Longhau...
876

179da8e6e   Rafa³ Bilski   [CPUFREQ] Longhau...
877
  	/* Find ACPI data for processor */
786f46b26   Rafa³ Bilski   [CPUFREQ] Longhau...
878
  	acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT,
2263576cf   Lin Ming   ACPICA: Add post-...
879
  				ACPI_UINT32_MAX, &longhaul_walk_callback, NULL,
786f46b26   Rafa³ Bilski   [CPUFREQ] Longhau...
880
  				NULL, (void *)&pr);
179da8e6e   Rafa³ Bilski   [CPUFREQ] Longhau...
881

264166e60   Rafa³ Bilski   [CPUFREQ] Longhau...
882
  	/* Check ACPI support for C3 state */
7ab77e03c   Dave Jones   Longhaul - Revert...
883
  	if (pr != NULL && longhaul_version == TYPE_POWERSAVER) {
179da8e6e   Rafa³ Bilski   [CPUFREQ] Longhau...
884
  		cx = &pr->power.states[ACPI_STATE_C3];
7d5edcc02   Rafał Bilski   [CPUFREQ] Longhau...
885
  		if (cx->address > 0 && cx->latency <= 1000)
264166e60   Rafa³ Bilski   [CPUFREQ] Longhau...
886
  			longhaul_flags |= USE_ACPI_C3;
eed7d4125   Rafa³ Bilski   [CPUFREQ] longhau...
887
  	}
905497c4b   Rafał Bilski   [CPUFREQ] Longhau...
888
889
890
  	/* Disable if it isn't working */
  	if (disable_acpi_c3)
  		longhaul_flags &= ~USE_ACPI_C3;
264166e60   Rafa³ Bilski   [CPUFREQ] Longhau...
891
  	/* Check if northbridge is friendly */
7d5edcc02   Rafał Bilski   [CPUFREQ] Longhau...
892
  	if (enable_arbiter_disable())
264166e60   Rafa³ Bilski   [CPUFREQ] Longhau...
893
  		longhaul_flags |= USE_NORTHBRIDGE;
7d5edcc02   Rafał Bilski   [CPUFREQ] Longhau...
894

eed7d4125   Rafa³ Bilski   [CPUFREQ] longhau...
895
  	/* Check ACPI support for bus master arbiter disable */
7d5edcc02   Rafał Bilski   [CPUFREQ] Longhau...
896
897
898
  	if (!(longhaul_flags & USE_ACPI_C3
  	     || longhaul_flags & USE_NORTHBRIDGE)
  	    && ((pr == NULL) || !(pr->flags.bm_control))) {
264166e60   Rafa³ Bilski   [CPUFREQ] Longhau...
899
900
901
902
  		printk(KERN_ERR PFX
  			"No ACPI support. Unsupported northbridge.
  ");
  		return -ENODEV;
179da8e6e   Rafa³ Bilski   [CPUFREQ] Longhau...
903
  	}
264166e60   Rafa³ Bilski   [CPUFREQ] Longhau...
904

786f46b26   Rafa³ Bilski   [CPUFREQ] Longhau...
905
  	if (longhaul_flags & USE_NORTHBRIDGE)
7d5edcc02   Rafał Bilski   [CPUFREQ] Longhau...
906
907
908
909
910
  		printk(KERN_INFO PFX "Using northbridge support.
  ");
  	if (longhaul_flags & USE_ACPI_C3)
  		printk(KERN_INFO PFX "Using ACPI support.
  ");
179da8e6e   Rafa³ Bilski   [CPUFREQ] Longhau...
911

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
912
913
914
  	ret = longhaul_get_ranges();
  	if (ret != 0)
  		return ret;
786f46b26   Rafa³ Bilski   [CPUFREQ] Longhau...
915
  	if ((longhaul_version != TYPE_LONGHAUL_V1) && (scale_voltage != 0))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
916
  		longhaul_setup_voltagescaling();
6778bae0f   Dave Jones   [CPUFREQ] longhau...
917
  	policy->cpuinfo.transition_latency = 200000;	/* nsec */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
  	policy->cur = calc_speed(longhaul_get_cpu_mult());
  
  	ret = cpufreq_frequency_table_cpuinfo(policy, longhaul_table);
  	if (ret)
  		return ret;
  
  	cpufreq_frequency_table_get_attr(longhaul_table, policy->cpu);
  
  	return 0;
  }
  
  static int __devexit longhaul_cpu_exit(struct cpufreq_policy *policy)
  {
  	cpufreq_frequency_table_put_attr(policy->cpu);
  	return 0;
  }
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
934
  static struct freq_attr *longhaul_attr[] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
935
936
937
  	&cpufreq_freq_attr_scaling_available_freqs,
  	NULL,
  };
221dee285   Linus Torvalds   Revert "[CPUFREQ]...
938
  static struct cpufreq_driver longhaul_driver = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
939
940
941
942
943
944
945
946
947
948
949
950
951
  	.verify	= longhaul_verify,
  	.target	= longhaul_target,
  	.get	= longhaul_get,
  	.init	= longhaul_cpu_init,
  	.exit	= __devexit_p(longhaul_cpu_exit),
  	.name	= "longhaul",
  	.owner	= THIS_MODULE,
  	.attr	= longhaul_attr,
  };
  
  
  static int __init longhaul_init(void)
  {
92cb7612a   Mike Travis   x86: convert cpui...
952
  	struct cpuinfo_x86 *c = &cpu_data(0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
953
954
955
  
  	if (c->x86_vendor != X86_VENDOR_CENTAUR || c->x86 != 6)
  		return -ENODEV;
48b7bde0f   Rafa³ Bilski   [CPUFREQ] Longhau...
956
957
  #ifdef CONFIG_SMP
  	if (num_online_cpus() > 1) {
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
958
959
960
  		printk(KERN_ERR PFX "More than 1 CPU detected, "
  				"longhaul disabled.
  ");
1cfe20142   Dave Jones   [CPUFREQ] longhau...
961
  		return -ENODEV;
48b7bde0f   Rafa³ Bilski   [CPUFREQ] Longhau...
962
963
964
965
  	}
  #endif
  #ifdef CONFIG_X86_IO_APIC
  	if (cpu_has_apic) {
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
966
967
968
  		printk(KERN_ERR PFX "APIC detected. Longhaul is currently "
  				"broken in this configuration.
  ");
48b7bde0f   Rafa³ Bilski   [CPUFREQ] Longhau...
969
970
971
  		return -ENODEV;
  	}
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
972
973
974
  	switch (c->x86_model) {
  	case 6 ... 9:
  		return cpufreq_register_driver(&longhaul_driver);
8ec9822dd   Dave Jones   [CPUFREQ] Advise ...
975
976
977
  	case 10:
  		printk(KERN_ERR PFX "Use acpi-cpufreq driver for VIA C7
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
978
  	default:
c19a28e11   Fernando Carrijo   remove lots of do...
979
  		;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
980
981
982
983
984
985
986
987
  	}
  
  	return -ENODEV;
  }
  
  
  static void __exit longhaul_exit(void)
  {
8eebf1a4c   Dave Jones   [CPUFREQ] Remove ...
988
  	int i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
989

ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
990
991
  	for (i = 0; i < numscales; i++) {
  		if (mults[i] == maxmult) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
992
993
994
995
996
997
998
999
  			longhaul_setstate(i);
  			break;
  		}
  	}
  
  	cpufreq_unregister_driver(&longhaul_driver);
  	kfree(longhaul_table);
  }
52a2638bf   Rafal Bilski   Longhaul: add aut...
1000
1001
1002
  /* Even if BIOS is exporting ACPI C3 state, and it is used
   * with success when CPU is idle, this state doesn't
   * trigger frequency transition in some cases. */
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
1003
  module_param(disable_acpi_c3, int, 0644);
905497c4b   Rafał Bilski   [CPUFREQ] Longhau...
1004
  MODULE_PARM_DESC(disable_acpi_c3, "Don't use ACPI C3 support");
0d2eb44f6   Lucas De Marchi   x86: Fix common m...
1005
  /* Change CPU voltage with frequency. Very useful to save
52a2638bf   Rafal Bilski   Longhaul: add aut...
1006
   * power, but most VIA C3 processors aren't supporting it. */
ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
1007
  module_param(scale_voltage, int, 0644);
db44aaf3a   Rafa³ Bilski   [CPUFREQ] Longhau...
1008
  MODULE_PARM_DESC(scale_voltage, "Scale voltage of processor");
52a2638bf   Rafal Bilski   Longhaul: add aut...
1009
1010
1011
1012
1013
  /* Force revision key to 0 for processors which doesn't
   * support voltage scaling, but are introducing itself as
   * such. */
  module_param(revid_errata, int, 0644);
  MODULE_PARM_DESC(revid_errata, "Ignore CPU Revision ID");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1014

ac617bd0f   Dave Jones   [CPUFREQ] checkpa...
1015
1016
1017
  MODULE_AUTHOR("Dave Jones <davej@redhat.com>");
  MODULE_DESCRIPTION("Longhaul driver for VIA Cyrix processors.");
  MODULE_LICENSE("GPL");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1018

0d6daba5f   Rafa³ Bilski   [CPUFREQ] Longhau...
1019
  late_initcall(longhaul_init);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1020
  module_exit(longhaul_exit);