--- linux-2.6.12-jo1/drivers/acpi/amd76x_pm.c	2005-06-21 22:57:09.000000000 +0200
+++ linux-2.6.12-jo1/drivers/acpi/amd76x_pm.c.orig	2005-06-21 23:03:31.000000000 +0200
@@ -47,8 +47,9 @@
  *        use _smp_processor_id()
  *        use cpu_idle_wait()
  *        NTH and POS code not touched and not tested.
- *   20050621: jo
- *        separated C3, NTH and POS code into extra patch
+ *
+ *   20050621-extra: jo
+ *        C3, NTH and POS code
  *
  * TODO: Thermal throttling (TTH).
  * 	 /proc interface for normal throttling level.
@@ -105,10 +106,16 @@
 
 #include <linux/amd76x_pm.h>
 
-#define VERSION	"20050621"
+#define VERSION	"20050621-extra"
+
+// #define AMD76X_NTH 1
+// #define AMD76X_POS 1
+// #define AMD76X_C3 1
 
 extern void default_idle(void);
+#ifndef AMD76X_NTH
 static void amd76x_smp_idle(void);
+#endif
 static int amd76x_pm_main(void);
 
 static unsigned long lazy_idle = 0;
@@ -128,6 +135,12 @@
 	unsigned int status_reg;
 	unsigned int tmr_reg;
 	unsigned int C2_reg;
+#ifdef AMD76X_C3
+	unsigned int C3_reg;
+#endif
+#ifdef AMD76X_NTH
+	unsigned int NTH_reg;
+#endif
 	unsigned int slp_reg;
 	unsigned int resume_reg;
 	void (*orig_idle) (void);
@@ -137,12 +150,18 @@
 struct idle_stat {
 	atomic_t num_idle;
 	atomic_t num_c2;
+#ifdef AMD76X_C3
+	atomic_t num_c3;
+#endif
 	atomic_t leave_idle;
 };
 
 static struct idle_stat amd76x_stat __cacheline_aligned_in_smp = {
 	.num_idle = ATOMIC_INIT(0),
 	.num_c2 = ATOMIC_INIT(0),
+#ifdef AMD76X_C3
+	.num_c3 = ATOMIC_INIT(0),
+#endif
 	.leave_idle = ATOMIC_INIT(0),
 };
 static atomic_t stat_sem __cacheline_aligned_in_smp = ATOMIC_INIT(1);
@@ -153,10 +172,18 @@
 	int c2_active;
 	int idle_count;
 	unsigned int C2_cnt;
+#ifdef AMD76X_C3
+	int c3_active;
+	unsigned int C3_cnt;
+	int _fill[3];
+#else
 	int _fill;
+#endif
 };
 
+#ifndef AMD76X_NTH
 static struct cpu_stat prs[NR_CPUS] __cacheline_aligned_in_smp;
+#endif
 
 static struct pci_device_id  __devinitdata amd_nb_tbl[] = {
 	{PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_700C, PCI_ANY_ID,
@@ -218,7 +245,13 @@
 	amd76x_pm_cfg.status_reg = (regdword + 0x00);
 	amd76x_pm_cfg.slp_reg =    (regdword + 0x04);
 	amd76x_pm_cfg.tmr_reg =    (regdword + 0x08);
+#ifdef AMD76X_NTH
+	amd76x_pm_cfg.NTH_reg =    (regdword + 0x10);
+#endif
 	amd76x_pm_cfg.C2_reg =     (regdword + 0x14);
+#ifdef AMD76X_C3
+	amd76x_pm_cfg.C3_reg =     (regdword + 0x15);
+#endif
 	amd76x_pm_cfg.resume_reg = (regdword + 0x16); /* N/A for 768 */
 }
 
@@ -263,6 +296,51 @@
 		regdword &= ~((STPCLK_EN | CPUSLP_EN) << C2_REGS);
 	pci_write_config_dword(pdev_sb, 0x50, regdword);
 }
+#ifdef AMD76X_C3
+/*
+ * Untested C3 idle support for AMD-766.
+ */
+static void
+config_amd766_C3(int enable)
+{
+        unsigned int regdword;
+
+        /* Set C3 options in C3A50, page 63 in AMD-766 doc */
+        pci_read_config_dword(pdev_sb, 0x50, &regdword);
+        if(enable) {
+                regdword &= ~((DCSTOP_EN | PCISTP_EN | SUSPND_EN | CPURST_EN)
+                                << C3_REGS);
+                regdword |= (STPCLK_EN  /* ~ 20 Watt savings max */
+                         |  CPUSLP_EN   /* Additional ~ 70 Watts max! */
+                         |  CPUSTP_EN)  /* yet more savings! */
+                         << C3_REGS;
+        }
+        else
+                regdword &= ~((STPCLK_EN | CPUSLP_EN | CPUSTP_EN) << C3_REGS);
+        pci_write_config_dword(pdev_sb, 0x50, regdword);
+}
+#endif
+
+
+#ifdef AMD76X_POS
+static void
+config_amd766_POS(int enable)
+{
+	unsigned int regdword;
+
+	/* Set C3 options in C3A50, page 63 in AMD-766 doc */
+	pci_read_config_dword(pdev_sb, 0x50, &regdword);
+	if(enable) {
+		regdword &= ~((ZZ_CACHE_EN | CPURST_EN) << POS_REGS);
+		regdword |= ((DCSTOP_EN | STPCLK_EN | CPUSTP_EN | PCISTP_EN |
+					CPUSLP_EN | SUSPND_EN) << POS_REGS);
+	}
+	else
+		regdword ^= (0xff << POS_REGS);
+	pci_write_config_dword(pdev_sb, 0x50, regdword);
+}
+#endif
+
 
 /*
  * Configures the 765 & 766 southbridges.
@@ -273,6 +351,13 @@
 	amd76x_get_PM();
 	config_PMIO_amd76x(1, 1);
 	config_amd766_C2(enable);
+#ifdef AMD76X_C3
+        config_amd766_C3(enable);
+#endif
+
+#ifdef AMD76X_POS
+	config_amd766_POS(enable);
+#endif
 
 	return 0;
 }
@@ -295,6 +380,69 @@
 	pci_write_config_byte(pdev_sb, 0x4F, regbyte);
 }
 
+#ifdef AMD76X_C3
+/*
+ * C3 idle support for AMD-768. The idle loop would need some extra
+ * handling for C3, but it would make more sense for ACPI to handle CX level
+ * transitions like it is supposed to. Unfortunately ACPI doesn't do CX
+ * levels on SMP systems yet.
+ */
+static void
+config_amd768_C3(int enable)
+{
+        unsigned char regbyte;
+
+        /* Set C3 options in DevB:3x4F, page 100 in AMD-768 doc */
+        pci_read_config_byte(pdev_sb, 0x4F, &regbyte);
+        if(enable)
+                regbyte |= (C3EN /* | ZZ_C3EN | CSLP_C3EN | CSTP_C3EN */);
+        else
+                regbyte ^= C3EN;
+        pci_write_config_byte(pdev_sb, 0x4F, regbyte);
+}
+#endif
+
+#ifdef AMD76X_POS
+/*
+ * Untested Power On Suspend support for AMD-768. This should also be handled
+ * by ACPI.
+ */
+static void
+config_amd768_POS(int enable)
+{
+	unsigned int regdword;
+
+	/* Set POS options in DevB:3x50, page 101 in AMD-768 doc */
+	pci_read_config_dword(pdev_sb, 0x50, &regdword);
+	if(enable) 
+		regdword |= (POSEN | CSTP | PSTP | ASTP | DCSTP | CSLP | SUSP);
+	else
+		regdword ^= POSEN;
+	pci_write_config_dword(pdev_sb, 0x50, regdword);
+}
+#endif
+
+
+#ifdef AMD76X_NTH
+/*
+ * Normal Throttling support for AMD-768. There are several settings
+ * that can be set depending on how long you want some of the delays to be.
+ * I'm not sure if this is even neccessary at all as the 766 doesn't need this.
+ */
+static void
+config_amd768_NTH(int enable, int ntper, int thminen)
+{
+	unsigned char regbyte;
+
+	/* DevB:3x40, pg 93 of 768 doc */
+	pci_read_config_byte(pdev_sb, 0x40, &regbyte);
+	/* Is it neccessary to use THMINEN at ANY time? */
+	regbyte |= (NTPER(ntper) | THMINEN(thminen));
+	pci_write_config_byte(pdev_sb, 0x40, regbyte);
+}
+#endif
+
+
 /*
  * Configures the 768 southbridge to support idle calls, and gets
  * the processor idle call register location.
@@ -307,10 +455,67 @@
 	config_PMIO_amd76x(0, 1);
 
 	config_amd768_C2(enable);
+#ifdef AMD76X_C3
+        config_amd768_C3(enable);
+#endif
+#ifdef AMD76X_POS
+	config_amd768_POS(enable);
+#endif
+#ifdef AMD76X_NTH
+	config_amd768_NTH(enable, 1, 2);
+#endif
 
 	return 0;
 }
 
+
+#ifdef AMD76X_NTH
+/*
+ * Activate normal throttling via its ACPI register (P_CNT).
+ */
+static void
+activate_amd76x_NTH(int enable, int ratio)
+{
+	unsigned int regdword;
+
+	/* PM10, pg 110 of 768 doc, pg 70 of 766 doc */
+	regdword=inl(amd76x_pm_cfg.NTH_reg);
+	if(enable)
+		regdword |= (NTH_EN | NTH_RATIO(ratio));
+	else
+		regdword ^= NTH_EN;
+	outl(regdword, amd76x_pm_cfg.NTH_reg);
+}
+#endif
+
+#ifdef AMD76X_SLP
+/*
+ * Activate sleep state via its ACPI register (PM1_CNT).
+ */
+static void
+activate_amd76x_SLP(int type)
+{
+	unsigned short regshort;
+
+	/* PM04, pg 109 of 768 doc, pg 69 of 766 doc */
+	regshort=inw(amd76x_pm_cfg.slp_reg);
+	regshort |= (SLP_EN | SLP_TYP(type)) ;
+	outw(regshort, amd76x_pm_cfg.slp_reg);
+}
+#endif /* AMD76X_SLP */
+
+#ifdef AMD76X_POS
+/*
+ * Wrapper function to activate POS sleep state.
+ */
+static void
+activate_amd76x_POS(void)
+{
+	activate_amd76x_SLP(1);
+}
+#endif
+
+
 /*
  * Idle loop for single processor systems
  */
@@ -323,6 +528,7 @@
 	amd76x_pm_cfg.orig_idle();
 }
 
+#ifndef AMD76X_NTH
 /*
  * Idle loop for SMP systems
  *
@@ -352,6 +558,9 @@
 		if (prs[cpu].c2_active) {
 			prs[cpu].idle_count = 0;
 			prs[cpu].c2_active = 0;
+	#ifdef AMD76X_C3
+			prs[cpu].c3_active = 0;
+	#endif
 			atomic_dec(&amd76x_stat.leave_idle);
 			atomic_dec(&amd76x_stat.num_idle);
 		}
@@ -367,6 +576,9 @@
 					atomic_set(&amd76x_stat.leave_idle,
 							num_online_cpus());
 				atomic_set(&amd76x_stat.num_c2, 0);
+			#ifdef AMD76X_C3
+				atomic_set(&amd76x_stat.num_c3, 0);
+			#endif
 			}
 			atomic_inc(&stat_sem);
 			atomic_dec(&amd76x_stat.num_idle);
@@ -379,6 +591,17 @@
 	if (prs[cpu].idle_count > lazy_idle) {
 
 		prs[cpu].idle_count = 0;
+	#ifdef AMD76X_C3
+		/*
+		 * Mark this CPU for C3 if it is already marked
+		 * for C2
+		 */
+		if (prs[cpu].c2_active) {
+			prs[cpu].c3_active = 1;
+			atomic_inc(&amd76x_stat.num_c3);
+		}
+
+	#endif
 
 		if (!prs[cpu].c2_active) {
 			prs[cpu].c2_active = 1;
@@ -386,6 +609,19 @@
 			atomic_inc(&amd76x_stat.num_c2);
 		}
 
+	#ifdef AMD76X_C3
+		/* If all CPUs are marked for C3 mode: enter C3 */
+		if (atomic_read(&amd76x_stat.num_c3) == num_online_cpus()) {
+			prs[cpu].C3_cnt++;
+
+			/* Invoke C3 */
+			inb(amd76x_pm_cfg.C3_reg);
+
+			atomic_dec(&amd76x_stat.num_idle);
+			local_irq_enable();
+			return;
+		}
+	#endif
 		/* Test if all CPUs are marked for C2 */
 		if (atomic_read(&amd76x_stat.num_c2) == num_online_cpus()) {
 			prs[cpu].C2_cnt++;
@@ -402,10 +638,12 @@
 		atomic_dec(&amd76x_stat.num_idle);
 	local_irq_enable();
 }
+#endif
 
 /*
  *   sysfs support, RW
  */
+#ifndef AMD76X_NTH
 static ssize_t 
 show_lazy_idle (struct device *dev, char *buf)
 {
@@ -429,18 +667,36 @@
 	return sprintf(buf,"%u\n", C2_cnt);  
 }
 
+#ifdef AMD76X_C3
+static ssize_t 
+show_C3_cnt (struct device *dev, char *buf)
+{
+	unsigned int C3_cnt = 0;
+	int i;
+	for_each_online_cpu(i)
+		C3_cnt += prs[i].C3_cnt;
+	return sprintf(buf,"%u\n", C3_cnt);  
+}
+#endif
+
 static DEVICE_ATTR(lazy_idle, S_IRUGO | S_IWUSR,
 		   show_lazy_idle, set_lazy_idle);
 static DEVICE_ATTR(C2_cnt, S_IRUGO,
 		   show_C2_cnt, NULL);
-
+#ifdef AMD76X_C3
+static DEVICE_ATTR(C3_cnt, S_IRUGO,
+		   show_C3_cnt, NULL);
+#endif
+#endif
 /*
  * Finds and initializes the bridges, and then sets the idle function
  */
 static int
 amd76x_pm_main(void)
 {
+#ifndef AMD76X_NTH
 	int i;
+#endif
 
 	amd76x_pm_cfg.orig_idle = 0;
 	if(lazy_idle == 0)
@@ -489,13 +745,16 @@
 	switch (pdev_nb->device) {
 	case PCI_DEVICE_ID_AMD_FE_GATE_700C:	/* AMD-762 */
 		config_amd762(1);
+#ifndef AMD76X_NTH
 		amd76x_pm_cfg.curr_idle = amd76x_smp_idle;
+#endif
 		break;
 	default:
 		printk(KERN_ERR "amd76x_pm: No northbridge to initialize\n");
 		break;
 	}
 
+#ifndef AMD76X_NTH
 	if(num_online_cpus() == 1) {
 		amd76x_pm_cfg.curr_idle = amd76x_up_idle;
 		printk(KERN_ERR "amd76x_pm: UP machine detected. ACPI is your friend.\n");
@@ -509,6 +768,10 @@
 		prs[i].idle_count = 0;
 		prs[i].c2_active = 0;
 		prs[i].C2_cnt = 0;
+	#ifdef AMD76X_C3
+		prs[i].c3_active = 0;
+		prs[i].C3_cnt = 0;
+	#endif
 	}
 
 	amd76x_pm_cfg.orig_idle = pm_idle;
@@ -519,6 +782,20 @@
 	/* sysfs */
 	device_create_file(&pdev_nb->dev, &dev_attr_lazy_idle);
 	device_create_file(&pdev_nb->dev, &dev_attr_C2_cnt);
+#ifdef AMD76X_C3
+	device_create_file(&pdev_nb->dev, &dev_attr_C3_cnt);
+#endif
+#endif
+
+#ifdef AMD76X_NTH
+	/* Turn NTH on with maxium throttling for testing. */
+	activate_amd76x_NTH(1, 1);
+#endif
+
+#ifdef AMD76X_POS
+	/* Testing here only. */
+	activate_amd76x_POS();
+#endif
 
 	return 0;
 }
@@ -537,7 +814,11 @@
 {
 	int i;
 	unsigned int C2_cnt = 0;
+#ifdef AMD76X_C3
+	unsigned int C3_cnt = 0;
+#endif
 	
+#ifndef AMD76X_NTH
 	pm_idle = amd76x_pm_cfg.orig_idle;
 
 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,10))
@@ -550,12 +831,29 @@
 	/* This isn't really needed. */
 	for_each_online_cpu(i) {
 		C2_cnt += prs[i].C2_cnt;
+#ifdef AMD76X_C3
+		C3_cnt += prs[i].C3_cnt;
+#endif
 	}
+#ifdef AMD76X_C3
+	printk(KERN_INFO "amd76x_pm: %u C2 calls, %u C3 calls\n",
+			C2_cnt, C3_cnt);
+#else
 	printk(KERN_INFO "amd76x_pm: %u C2 calls\n", C2_cnt);
+#endif
 	
 	/* remove sysfs */
 	device_remove_file(&pdev_nb->dev, &dev_attr_lazy_idle);
 	device_remove_file(&pdev_nb->dev, &dev_attr_C2_cnt);
+#ifdef AMD76X_C3
+	device_remove_file(&pdev_nb->dev, &dev_attr_C3_cnt);
+#endif
+#endif
+
+#ifdef AMD76X_NTH
+	/* Turn NTH off*/
+	activate_amd76x_NTH(0, 0);
+#endif
 
 }
 
