1 /*
2  * ACPI sytle PM for SMP AMD-760MP(X) based systems.
3  * For use until the ACPI project catches up. :-)
4  *
5  * Copyright (C) 2002 Johnathan Hicks <thetech@folkwolf.net>
6  *
7  * History:
8  *
9  *   20020702 - amd-smp-idle: Tony Lindgren <tony@atomide.com>
10  *	Influenced by Vcool, and LVCool. Rewrote everything from scratch to
11  *	use the PCI features in Linux, and to support SMP systems. Provides
12  *	C2 idling on SMP AMD-760MP systems.
13  *
14  *   20020722: JH
15  *   	I adapted Tony's code for the AMD-765/766 southbridge and adapted it
16  *   	according to the AMD-768 data sheet to provide the same capability for
17  *   	SMP AMD-760MPX systems. Posted to acpi-devel list.
18  *
19  *   20020722: Alan Cox
20  *   	Replaces non-functional amd76x_pm code in -ac tree.
21  *
22  *   20020730: JH
23  *   	Added ability to do normal throttling (the non-thermal kind), C3 idling
24  *   	and Power On Suspend (S1 sleep). It would be very easy to tie swsusp
25  *   	into activate_amd76x_SLP(). C3 idling doesn't happen yet; see my note
26  *   	in amd76x_smp_idle(). I've noticed that when NTH and idling are both
27  *   	enabled, my hardware locks and requires a hard reset, so I have
28  *   	#ifndefed around the idle loop setting to prevent this. POS locks it up
29  *   	too, both ought to be fixable. I've also noticed that idling and NTH
30  *   	make some interference that is picked up by the onboard sound chip on
31  *   	my ASUS A7M266-D motherboard.
32  *
33  *
34  * TODO: Thermal throttling (TTH).
35  * 	 /proc interface for normal throttling level.
36  * 	 /proc interface for POS.
37  *
38  *
39  *    <Notes from 20020722-ac revision>
40  *
41  * Processor idle mode module for AMD SMP 760MP(X) based systems
42  *
43  * Copyright (C) 2002 Tony Lindgren <tony@atomide.com>
44  *                    Johnathan Hicks (768 support)
45  *
46  * Using this module saves about 70 - 90W of energy in the idle mode compared
47  * to the default idle mode. Waking up from the idle mode is fast to keep the
48  * system response time good. Currently no CPU load calculation is done, the
49  * system exits the idle mode if the idle function runs twice on the same
50  * processor in a row. This only works on SMP systems, but maybe the idle mode
51  * enabling can be integrated to ACPI to provide C2 mode at some point.
52  *
53  * NOTE: Currently there's a bug somewhere where the reading the
54  *       P_LVL2 for the first time causes the system to sleep instead of
55  *       idling. This means that you need to hit the power button once to
56  *       wake the system after loading the module for the first time after
57  *       reboot. After that the system idles as supposed.
58  *
59  *
60  * Influenced by Vcool, and LVCool. Rewrote everything from scratch to
61  * use the PCI features in Linux, and to support SMP systems.
62  *
63  * Currently only tested on a TYAN S2460 (760MP) system (Tony) and an
64  * ASUS A7M266-D (760MPX) system (Johnathan). Adding support for other Athlon
65  * SMP or single processor systems should be easy if desired.
66  *
67  * This software is licensed under GNU General Public License Version 2
68  * as specified in file COPYING in the Linux kernel source tree main
69  * directory.
70  *
71  *   </Notes from 20020722-ac revision>
72  */
73 
74 
75 #include <linux/config.h>
76 #include <linux/module.h>
77 #include <linux/slab.h>
78 #include <linux/pci.h>
79 #include <linux/delay.h>
80 #include <linux/pm.h>
81 #include <linux/init.h>
82 
83 #include "amd76x_pm.h"
84 
85 #define VERSION	"20020730"
86 
87 // #define AMD76X_C3  1
88 // #define AMD76X_NTH 1
89 // #define AMD76X_POS 1
90 
91 
92 extern void default_idle(void);
93 static void amd76x_smp_idle(void);
94 static int amd76x_pm_main(void);
95 static int __devinit amd_nb_init(struct pci_dev *pdev,
96 				 const struct pci_device_id *ent);
97 static void amd_nb_remove(struct pci_dev *pdev);
98 static int __devinit amd_sb_init(struct pci_dev *pdev,
99 				 const struct pci_device_id *ent);
100 static void amd_sb_remove(struct pci_dev *pdev);
101 
102 
103 static struct pci_dev *pdev_nb;
104 static struct pci_dev *pdev_sb;
105 
106 struct PM_cfg {
107 	unsigned int status_reg;
108 	unsigned int C2_reg;
109 	unsigned int C3_reg;
110 	unsigned int NTH_reg;
111 	unsigned int slp_reg;
112 	unsigned int resume_reg;
113 	void (*orig_idle) (void);
114 	void (*curr_idle) (void);
115 	unsigned long C2_cnt, C3_cnt;
116 	int last_pr;
117 };
118 static struct PM_cfg amd76x_pm_cfg;
119 
120 struct cpu_idle_state {
121 	int idle;
122 	int count;
123 };
124 static struct cpu_idle_state prs[2];
125 
126 static struct pci_device_id amd_nb_tbl[] __devinitdata = {
127 	{PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_700C, PCI_ANY_ID, PCI_ANY_ID,},
128 	{0,}
129 };
130 
131 static struct pci_device_id amd_sb_tbl[] __devinitdata = {
132 	{PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7413, PCI_ANY_ID, PCI_ANY_ID,},
133 	{PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7443, PCI_ANY_ID, PCI_ANY_ID,},
134 	{0,}
135 };
136 
137 static struct pci_driver amd_nb_driver = {
138 	name:"amd76x_pm-nb",
139 	id_table:amd_nb_tbl,
140 	probe:amd_nb_init,
141 	remove:__devexit_p(amd_nb_remove),
142 };
143 
144 static struct pci_driver amd_sb_driver = {
145 	name:"amd76x_pm-sb",
146 	id_table:amd_sb_tbl,
147 	probe:amd_sb_init,
148 	remove:__devexit_p(amd_sb_remove),
149 };
150 
151 
152 static int __devinit
amd_nb_init(struct pci_dev * pdev,const struct pci_device_id * ent)153 amd_nb_init(struct pci_dev *pdev, const struct pci_device_id *ent)
154 {
155 	pdev_nb = pdev;
156 	printk(KERN_INFO "amd76x_pm: Initializing northbridge %s\n",
157 	       pdev_nb->name);
158 
159 	return 0;
160 }
161 
162 
163 static void __devexit
amd_nb_remove(struct pci_dev * pdev)164 amd_nb_remove(struct pci_dev *pdev)
165 {
166 }
167 
168 
169 static int __devinit
amd_sb_init(struct pci_dev * pdev,const struct pci_device_id * ent)170 amd_sb_init(struct pci_dev *pdev, const struct pci_device_id *ent)
171 {
172 	pdev_sb = pdev;
173 	printk(KERN_INFO "amd76x_pm: Initializing southbridge %s\n",
174 	       pdev_sb->name);
175 
176 	return 0;
177 }
178 
179 
180 static void __devexit
amd_sb_remove(struct pci_dev * pdev)181 amd_sb_remove(struct pci_dev *pdev)
182 {
183 }
184 
185 
186 /*
187  * Configures the AMD-762 northbridge to support PM calls
188  */
189 static int
config_amd762(int enable)190 config_amd762(int enable)
191 {
192 	unsigned int regdword;
193 
194 	/* Enable STPGNT in BIU Status/Control for cpu0 */
195 	pci_read_config_dword(pdev_nb, 0x60, &regdword);
196 	regdword |= (1 << 17);
197 	pci_write_config_dword(pdev_nb, 0x60, regdword);
198 
199 	/* Enable STPGNT in BIU Status/Control for cpu1 */
200 	pci_read_config_dword(pdev_nb, 0x68, &regdword);
201 	regdword |= (1 << 17);
202 	pci_write_config_dword(pdev_nb, 0x68, regdword);
203 
204 	/* DRAM refresh enable */
205 	pci_read_config_dword(pdev_nb, 0x58, &regdword);
206 	regdword &= ~(1 << 19);
207 	pci_write_config_dword(pdev_nb, 0x58, regdword);
208 
209 	/* Self refresh enable */
210 	pci_read_config_dword(pdev_nb, 0x70, &regdword);
211 	regdword |= (1 << 18);
212 	pci_write_config_dword(pdev_nb, 0x70, regdword);
213 
214 	return 0;
215 }
216 
217 
218 /*
219  * Get the base PMIO address and set the pm registers in amd76x_pm_cfg.
220  */
221 static void
amd76x_get_PM(void)222 amd76x_get_PM(void)
223 {
224 	unsigned int regdword;
225 
226 	/* Get the address for pm status, P_LVL2, etc */
227 	pci_read_config_dword(pdev_sb, 0x58, &regdword);
228 	regdword &= 0xff80;
229 	amd76x_pm_cfg.status_reg = (regdword + 0x00);
230 	amd76x_pm_cfg.slp_reg =    (regdword + 0x04);
231 	amd76x_pm_cfg.NTH_reg =    (regdword + 0x10);
232 	amd76x_pm_cfg.C2_reg =     (regdword + 0x14);
233 	amd76x_pm_cfg.C3_reg =     (regdword + 0x15);
234 	amd76x_pm_cfg.resume_reg = (regdword + 0x16); /* N/A for 768 */
235 }
236 
237 
238 /*
239  * En/Disable PMIO and configure W4SG & STPGNT.
240  */
241 static int
config_PMIO_amd76x(int is_766,int enable)242 config_PMIO_amd76x(int is_766, int enable)
243 {
244 	unsigned char regbyte;
245 
246 	/* Clear W4SG, and set PMIOEN, if using a 765/766 set STPGNT as well.
247 	 * AMD-766: C3A41; page 59 in AMD-766 doc
248 	 * AMD-768: DevB:3x41C; page 94 in AMD-768 doc */
249 	pci_read_config_byte(pdev_sb, 0x41, &regbyte);
250 	if(enable) {
251 		regbyte |= ((0 << 0) | (is_766?1:0 << 1) | (1 << 7));
252 	}
253 	else {
254 		regbyte |= (0 << 7);
255 	}
256 	pci_write_config_byte(pdev_sb, 0x41, regbyte);
257 
258 	return 0;
259 }
260 
261 /*
262  * C2 idle support for AMD-766.
263  */
264 static void
config_amd766_C2(int enable)265 config_amd766_C2(int enable)
266 {
267 	unsigned int regdword;
268 
269 	/* Set C2 options in C3A50, page 63 in AMD-766 doc */
270 	pci_read_config_dword(pdev_sb, 0x50, &regdword);
271 	if(enable) {
272 		regdword &= ~((DCSTOP_EN | CPUSTP_EN | PCISTP_EN | SUSPND_EN |
273 					CPURST_EN) << C2_REGS);
274 		regdword |= (STPCLK_EN	/* ~ 20 Watt savings max */
275 			 |  CPUSLP_EN)	/* Additional ~ 70 Watts max! */
276 			 << C2_REGS;
277 	}
278 	else
279 		regdword &= ~((STPCLK_EN | CPUSLP_EN) << C2_REGS);
280 	pci_write_config_dword(pdev_sb, 0x50, regdword);
281 }
282 
283 
284 #ifdef AMD76X_C3
285 /*
286  * Untested C3 idle support for AMD-766.
287  */
288 static void
config_amd766_C3(int enable)289 config_amd766_C3(int enable)
290 {
291 	unsigned int regdword;
292 
293 	/* Set C3 options in C3A50, page 63 in AMD-766 doc */
294 	pci_read_config_dword(pdev_sb, 0x50, &regdword);
295 	if(enable) {
296 		regdword &= ~((DCSTOP_EN | PCISTP_EN | SUSPND_EN | CPURST_EN)
297 				<< C3_REGS);
298 		regdword |= (STPCLK_EN	/* ~ 20 Watt savings max */
299 			 |  CPUSLP_EN	/* Additional ~ 70 Watts max! */
300 			 |  CPUSTP_EN)	/* yet more savings! */
301 			 << C3_REGS;
302 	}
303 	else
304 		regdword &= ~((STPCLK_EN | CPUSLP_EN | CPUSTP_EN) << C3_REGS);
305 	pci_write_config_dword(pdev_sb, 0x50, regdword);
306 }
307 #endif
308 
309 
310 #ifdef AMD76X_POS
311 static void
config_amd766_POS(int enable)312 config_amd766_POS(int enable)
313 {
314 	unsigned int regdword;
315 
316 	/* Set C3 options in C3A50, page 63 in AMD-766 doc */
317 	pci_read_config_dword(pdev_sb, 0x50, &regdword);
318 	if(enable) {
319 		regdword &= ~((ZZ_CACHE_EN | CPURST_EN) << POS_REGS);
320 		regdword |= ((DCSTOP_EN | STPCLK_EN | CPUSTP_EN | PCISTP_EN |
321 					CPUSLP_EN | SUSPND_EN) << POS_REGS);
322 	}
323 	else
324 		regdword ^= (0xff << POS_REGS);
325 	pci_write_config_dword(pdev_sb, 0x50, regdword);
326 }
327 #endif
328 
329 
330 /*
331  * Configures the 765 & 766 southbridges.
332  */
333 static int
config_amd766(int enable)334 config_amd766(int enable)
335 {
336 	amd76x_get_PM();
337 	config_PMIO_amd76x(1, 1);
338 
339 	config_amd766_C2(enable);
340 #ifdef AMD76X_C3
341 	config_amd766_C3(enable);
342 #endif
343 #ifdef AMD76X_POS
344 	config_amd766_POS(enable);
345 #endif
346 
347 	return 0;
348 }
349 
350 
351 /*
352  * C2 idling support for AMD-768.
353  */
354 static void
config_amd768_C2(int enable)355 config_amd768_C2(int enable)
356 {
357 	unsigned char regbyte;
358 
359 	/* Set C2 options in DevB:3x4F, page 100 in AMD-768 doc */
360 	pci_read_config_byte(pdev_sb, 0x4F, &regbyte);
361 	if(enable)
362 		regbyte |= C2EN;
363 	else
364 		regbyte ^= C2EN;
365 	pci_write_config_byte(pdev_sb, 0x4F, regbyte);
366 }
367 
368 
369 #ifdef AMD76X_C3
370 /*
371  * C3 idle support for AMD-768. The idle loop would need some extra
372  * handling for C3, but it would make more sense for ACPI to handle CX level
373  * transitions like it is supposed to. Unfortunately ACPI doesn't do CX
374  * levels on SMP systems yet.
375  */
376 static void
config_amd768_C3(int enable)377 config_amd768_C3(int enable)
378 {
379 	unsigned char regbyte;
380 
381 	/* Set C3 options in DevB:3x4F, page 100 in AMD-768 doc */
382 	pci_read_config_byte(pdev_sb, 0x4F, &regbyte);
383 	if(enable)
384 		regbyte |= (C3EN /* | ZZ_C3EN | CSLP_C3EN | CSTP_C3EN */);
385 	else
386 		regbyte ^= C3EN;
387 	pci_write_config_byte(pdev_sb, 0x4F, regbyte);
388 }
389 #endif
390 
391 
392 #ifdef AMD76X_POS
393 /*
394  * Untested Power On Suspend support for AMD-768. This should also be handled
395  * by ACPI.
396  */
397 static void
config_amd768_POS(int enable)398 config_amd768_POS(int enable)
399 {
400 	unsigned int regdword;
401 
402 	/* Set POS options in DevB:3x50, page 101 in AMD-768 doc */
403 	pci_read_config_dword(pdev_sb, 0x50, &regdword);
404 	if(enable)
405 		regdword |= (POSEN | CSTP | PSTP | ASTP | DCSTP | CSLP | SUSP);
406 	else
407 		regdword ^= POSEN;
408 	pci_write_config_dword(pdev_sb, 0x50, regdword);
409 }
410 #endif
411 
412 
413 #ifdef AMD76X_NTH
414 /*
415  * Normal Throttling support for AMD-768. There are several settings
416  * that can be set depending on how long you want some of the delays to be.
417  * I'm not sure if this is even neccessary at all as the 766 doesn't need this.
418  */
419 static void
config_amd768_NTH(int enable,int ntper,int thminen)420 config_amd768_NTH(int enable, int ntper, int thminen)
421 {
422 	unsigned char regbyte;
423 
424 	/* DevB:3x40, pg 93 of 768 doc */
425 	pci_read_config_byte(pdev_sb, 0x40, &regbyte);
426 	/* Is it neccessary to use THMINEN at ANY time? */
427 	regbyte |= (NTPER(ntper) | THMINEN(thminen));
428 	pci_write_config_byte(pdev_sb, 0x40, regbyte);
429 }
430 #endif
431 
432 
433 /*
434  * Configures the 768 southbridge to support idle calls, and gets
435  * the processor idle call register location.
436  */
437 static int
config_amd768(int enable)438 config_amd768(int enable)
439 {
440 	amd76x_get_PM();
441 	config_PMIO_amd76x(0, 1);
442 
443 	config_amd768_C2(enable);
444 #ifdef AMD76X_C3
445 	config_amd768_C3(enable);
446 #endif
447 #ifdef AMD76X_POS
448 	config_amd768_POS(enable);
449 #endif
450 #ifdef AMD76X_NTH
451 	config_amd768_NTH(enable, 1, 2);
452 #endif
453 
454 	return 0;
455 }
456 
457 
458 #ifdef AMD76X_NTH
459 /*
460  * Activate normal throttling via its ACPI register (P_CNT).
461  */
462 static void
activate_amd76x_NTH(int enable,int ratio)463 activate_amd76x_NTH(int enable, int ratio)
464 {
465 	unsigned int regdword;
466 
467 	/* PM10, pg 110 of 768 doc, pg 70 of 766 doc */
468 	regdword=inl(amd76x_pm_cfg.NTH_reg);
469 	if(enable)
470 		regdword |= (NTH_EN | NTH_RATIO(ratio));
471 	else
472 		regdword ^= NTH_EN;
473 	outl(regdword, amd76x_pm_cfg.NTH_reg);
474 }
475 #endif
476 
477 #ifdef AMD76X_POS
478 /*
479  * Activate sleep state via its ACPI register (PM1_CNT).
480  */
481 static void
activate_amd76x_SLP(int type)482 activate_amd76x_SLP(int type)
483 {
484 	unsigned short regshort;
485 
486 	/* PM04, pg 109 of 768 doc, pg 69 of 766 doc */
487 	regshort=inw(amd76x_pm_cfg.slp_reg);
488 	regshort |= (SLP_EN | SLP_TYP(type)) ;
489 	outw(regshort, amd76x_pm_cfg.slp_reg);
490 }
491 
492 /*
493  * Wrapper function to activate POS sleep state.
494  */
495 static void
activate_amd76x_POS(void)496 activate_amd76x_POS(void)
497 {
498 	activate_amd76x_SLP(1);
499 }
500 #endif
501 
502 
503 #if 0
504 /*
505  * Idle loop for single processor systems
506  */
507 void
508 amd76x_up_idle(void)
509 {
510 	// FIXME: Optionally add non-smp idle loop here
511 }
512 #endif
513 
514 
515 /*
516  * Idle loop for SMP systems, supports currently only 2 processors.
517  *
518  * Note; for 2.5 folks - not pre-empt safe
519  */
520 static void
amd76x_smp_idle(void)521 amd76x_smp_idle(void)
522 {
523 
524 	/*
525 	 * Exit idle mode immediately if the CPU does not change.
526 	 * Usually that means that we have some load on another CPU.
527 	 */
528 	if (prs[0].idle && prs[1].idle && amd76x_pm_cfg.last_pr == smp_processor_id()) {
529 		prs[0].idle = 0;
530 		prs[1].idle = 0;
531 		/* This looks redundent as it was just checked in the if() */
532 		/* amd76x_pm_cfg.last_pr = smp_processor_id(); */
533 		return;
534 	}
535 
536 	prs[smp_processor_id()].count++;
537 
538 	/* Don't start the idle mode immediately */
539 	if (prs[smp_processor_id()].count >= LAZY_IDLE_DELAY) {
540 
541 		/* Put the current processor into idle mode */
542 		prs[smp_processor_id()].idle =
543 			(prs[smp_processor_id()].idle ? 2 : 1);
544 
545 		/* Only idle if both processors are idle */
546 		if ((prs[0].idle==1) && (prs[1].idle==1)) {
547 			amd76x_pm_cfg.C2_cnt++;
548 			inb(amd76x_pm_cfg.C2_reg);
549 		}
550 	#ifdef AMD76X_C3
551 		/*
552 		 * JH: I've not been able to get into here. Could this have
553 		 * something to do with the way the kernel handles the idle
554 		 * loop, or and error that I've made?
555 		 */
556 		else if ((prs[0].idle==2) && (prs[1].idle==2)) {
557 			amd76x_pm_cfg.C3_cnt++;
558 			inb(amd76x_pm_cfg.C3_reg);
559 		}
560 	#endif
561 
562 		prs[smp_processor_id()].count = 0;
563 
564 	}
565 	amd76x_pm_cfg.last_pr = smp_processor_id();
566 }
567 
568 
569 /*
570  * Finds and initializes the bridges, and then sets the idle function
571  */
572 static int
amd76x_pm_main(void)573 amd76x_pm_main(void)
574 {
575 	int found;
576 
577 	/* Find northbridge */
578 	found = pci_register_driver(&amd_nb_driver);
579 	if (found <= 0) {
580 		printk(KERN_ERR "amd76x_pm: Could not find northbridge\n");
581 		pci_unregister_driver(&amd_nb_driver);
582 		return 1;
583 	}
584 
585 	/* Find southbridge */
586 	found = pci_register_driver(&amd_sb_driver);
587 	if (found <= 0) {
588 		printk(KERN_ERR "amd76x_pm: Could not find southbridge\n");
589 		pci_unregister_driver(&amd_sb_driver);
590 		pci_unregister_driver(&amd_nb_driver);
591 		return 1;
592 	}
593 
594 	/* Init southbridge */
595 	switch (pdev_sb->device) {
596 	case PCI_DEVICE_ID_AMD_VIPER_7413:	/* AMD-765 or 766 */
597 		config_amd766(1);
598 		break;
599 	case PCI_DEVICE_ID_AMD_VIPER_7443:	/* AMD-768 */
600 		config_amd768(1);
601 		break;
602 	default:
603 		printk(KERN_ERR "amd76x_pm: No southbridge to initialize\n");
604 		break;
605 	}
606 
607 	/* Init northbridge and queue the new idle function */
608 	switch (pdev_nb->device) {
609 	case PCI_DEVICE_ID_AMD_FE_GATE_700C:	/* AMD-762 */
610 		config_amd762(1);
611 #ifndef AMD76X_NTH
612 		amd76x_pm_cfg.curr_idle = amd76x_smp_idle;
613 #endif
614 		break;
615 	default:
616 		printk(KERN_ERR "amd76x_pm: No northbridge to initialize\n");
617 		break;
618 	}
619 
620 #ifndef AMD76X_NTH
621 	if (!amd76x_pm_cfg.curr_idle) {
622 		printk(KERN_ERR "amd76x_pm: Idle function not changed\n");
623 		pci_unregister_driver(&amd_nb_driver);
624 		pci_unregister_driver(&amd_sb_driver);
625 		return 1;
626 	}
627 
628 	amd76x_pm_cfg.orig_idle = pm_idle;
629 	pm_idle = amd76x_pm_cfg.curr_idle;
630 #endif
631 
632 #ifdef AMD76X_NTH
633 	/* Turn NTH on with maxium throttling for testing. */
634 	activate_amd76x_NTH(1, 1);
635 #endif
636 
637 #ifdef AMD76X_POS
638 	/* Testing here only. */
639 	activate_amd76x_POS();
640 #endif
641 
642 	return 0;
643 }
644 
645 
646 static int __init
amd76x_pm_init(void)647 amd76x_pm_init(void)
648 {
649 	printk(KERN_INFO "amd76x_pm: Version %s\n", VERSION);
650 	return amd76x_pm_main();
651 }
652 
653 
654 static void __exit
amd76x_pm_cleanup(void)655 amd76x_pm_cleanup(void)
656 {
657 #ifndef AMD76X_NTH
658 	pm_idle = amd76x_pm_cfg.orig_idle;
659 
660 	/* This isn't really needed. */
661 	printk(KERN_INFO "amd76x_pm: %lu C2 calls\n", amd76x_pm_cfg.C2_cnt);
662 #ifdef AMD76X_C3
663 	printk(KERN_INFO "amd76x_pm: %lu C3 calls\n", amd76x_pm_cfg.C3_cnt);
664 #endif
665 
666 	/*
667 	 * FIXME: We want to wait until all CPUs have set the new
668 	 * idle function, otherwise we will oops. This may not be
669 	 * the right way to do it, but seems to work.
670 	 *
671 	 * - Best answer is going to be to ban unload, but when its debugged
672 	 *   --- Alan
673 	 */
674 	schedule();
675 	mdelay(1000);
676 #endif
677 
678 #ifdef AMD76X_NTH
679 	/* Turn NTH off*/
680 	activate_amd76x_NTH(0, 0);
681 #endif
682 
683 	pci_unregister_driver(&amd_nb_driver);
684 	pci_unregister_driver(&amd_sb_driver);
685 
686 }
687 
688 
689 MODULE_LICENSE("GPL");
690 module_init(amd76x_pm_init);
691 module_exit(amd76x_pm_cleanup);
692