Attached is a simplistic port of amd76x_pm to 2.5.70.
There is a mention about being non-preempt safe, and I can't tell
spinlock from semaphore, so can do nada about it.
zwane told me on #kernelnewbies that
pm_idle = old_pm_idle; wmb();
should take care about the processor syncing, but I do get total hangs
on rmmod.
There's also the thing that in 2.5.70-mm2 pm_idle doesn't get called at
all unless there's some load. eg. if I load this on boot, before
starting X, /proc/drivers/amd76x_pm stays at 0.
diff -Nru linux-2.5.70/drivers/char/Kconfig linux-2.5.70-mod/drivers/char/Kconfig
--- linux-2.5.70/drivers/char/Kconfig 2003-05-27 04:00:21.000000000 +0300
+++ linux-2.5.70-mod/drivers/char/Kconfig 2003-05-31 20:33:09.000000000 +0300
@@ -737,6 +737,21 @@
If unsure, say N.
+config AMD76X_PM
+ tristate "AMD76x Native Power Management support"
+ depends on X86 && PCI
+ ---help---
+ This driver enables Power Management on AMD760MP & AMD760MPX chipsets.
+ This is about same as ACPI C2, except that ACPI folks don't do SMP ATM.
+ A /proc/drivers/amd76x_pm -statistics file will be created at runtime.
+
+ To compile this driver as a module ( = code which can be inserted in
+ and removed from the running kernel whenever you want), say M here
+ and read <file:Documentation/modules.txt>. The module will be called
+ amd76x_pm, it cannot be unloaded.
+
+ If unsure, say N.
+
config NVRAM
tristate "/dev/nvram support"
---help---
diff -Nru linux-2.5.70/drivers/char/Makefile linux-2.5.70-mod/drivers/char/Makefile
--- linux-2.5.70/drivers/char/Makefile 2003-05-27 04:00:59.000000000 +0300
+++ linux-2.5.70-mod/drivers/char/Makefile 2003-05-31 20:35:43.000000000 +0300
@@ -61,6 +61,7 @@
obj-$(CONFIG_I8K) += i8k.o
obj-$(CONFIG_DS1620) += ds1620.o
obj-$(CONFIG_HW_RANDOM) += hw_random.o
+obj-$(CONFIG_AMD76X_PM) += amd76x_pm.o
obj-$(CONFIG_QIC02_TAPE) += tpqic02.o
obj-$(CONFIG_FTAPE) += ftape/
obj-$(CONFIG_H8) += h8.o
diff -Nru linux-2.5.70/drivers/char/amd76x_pm.c linux-2.5.70-mod/drivers/char/amd76x_pm.c
--- linux-2.5.70/drivers/char/amd76x_pm.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.5.70-mod/drivers/char/amd76x_pm.c 2003-05-31 20:55:37.000000000 +0300
@@ -0,0 +1,711 @@
+/*
+ * ACPI style PM for SMP AMD-760MP(X) based systems.
+ * For use until the ACPI project catches up. :-)
+ *
+ * Copyright (C) 2002 Johnathan Hicks <thet...@folkwolf.net>
+ *
+ * History:
+ *
+ * 20020702 - amd-smp-idle: Tony Lindgren <t...@atomide.com>
+ * Influenced by Vcool, and LVCool. Rewrote everything from scratch to
+ * use the PCI features in Linux, and to support SMP systems. Provides
+ * C2 idling on SMP AMD-760MP systems.
+ *
+ * 20020722: JH
+ * I adapted Tony's code for the AMD-765/766 southbridge and adapted it
+ * according to the AMD-768 data sheet to provide the same capability for
+ * SMP AMD-760MPX systems. Posted to acpi-devel list.
+ *
+ * 20020722: Alan Cox
+ * Replaces non-functional amd76x_pm code in -ac tree.
+ *
+ * 20020730: JH
+ * Added ability to do normal throttling (the non-thermal kind), C3 idling
+ * and Power On Suspend (S1 sleep). It would be very easy to tie swsusp
+ * into activate_amd76x_SLP(). C3 idling doesn't happen yet; see my note
+ * in amd76x_smp_idle(). I've noticed that when NTH and idling are both
+ * enabled, my hardware locks and requires a hard reset, so I have
+ * #ifndefed around the idle loop setting to prevent this. POS locks it up
+ * too, both ought to be fixable. I've also noticed that idling and NTH
+ * make some interference that is picked up by the onboard sound chip on
+ * my ASUS A7M266-D motherboard.
+ *
+ * 20030531: Pasi Savolainen
+ * Simple port to 2.5
+ * Added /proc/driver/amd76x_pm for making nice graphs with mrtg.
+ * Cannot unload this without crash.
+ *
+ *
+ * TODO: Thermal throttling (TTH).
+ * /proc interface for normal throttling level.
+ * /proc interface for POS.
+ *
+ *
+ * <Notes from 20020722-ac revision>
+ *
+ * Processor idle mode module for AMD SMP 760MP(X) based systems
+ *
+ * Copyright (C) 2002 Tony Lindgren <t...@atomide.com>
+ * Johnathan Hicks (768 support)
+ *
+ * Using this module saves about 70 - 90W of energy in the idle mode compared
+ * to the default idle mode. Waking up from the idle mode is fast to keep the
+ * system response time good. Currently no CPU load calculation is done, the
+ * system exits the idle mode if the idle function runs twice on the same
+ * processor in a row. This only works on SMP systems, but maybe the idle mode
+ * enabling can be integrated to ACPI to provide C2 mode at some point.
+ *
+ * NOTE: Currently there's a bug somewhere where the reading the
+ * P_LVL2 for the first time causes the system to sleep instead of
+ * idling. This means that you need to hit the power button once to
+ * wake the system after loading the module for the first time after
+ * reboot. After that the system idles as supposed.
+ *
+ *
+ * Influenced by Vcool, and LVCool. Rewrote everything from scratch to
+ * use the PCI features in Linux, and to support SMP systems.
+ *
+ * Currently only tested on a TYAN S2460 (760MP) system (Tony) and an
+ * ASUS A7M266-D (760MPX) system (Johnathan). Adding support for other Athlon
+ * SMP or single processor systems should be easy if desired.
+ *
+ * This software is licensed under GNU General Public License Version 2
+ * as specified in file COPYING in the Linux kernel source tree main
+ * directory.
+ *
+ * </Notes from 20020722-ac revision>
+ */
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+
+#include <linux/amd76x_pm.h>
+
+#define VERSION "20030531"
+
+// #define AMD76X_C3 1
+// #define AMD76X_NTH 1
+// #define AMD76X_POS 1
+
+
+extern void default_idle(void);
+static void amd76x_smp_idle(void);
+static int amd76x_pm_main(void);
+static int __devinit amd_nb_init(struct pci_dev *pdev,
+ const struct pci_device_id *ent);
+
+static void amd_nb_remove(struct pci_dev *pdev);
+
+
+static struct pci_dev *pdev_nb;
+static struct pci_dev *pdev_sb;
+static spinlock_t amd76x_pm_lock; /* for device access */
+
+struct PM_cfg {
+ unsigned int status_reg;
+ unsigned int C2_reg;
+ unsigned int C3_reg;
+ unsigned int NTH_reg;
+ unsigned int slp_reg;
+ unsigned int resume_reg;
+ void (*orig_idle) (void);
+ void (*curr_idle) (void);
+ unsigned long C2_cnt, C3_cnt;
+ int last_pr;
+ unsigned long lazy_idle;
+};
+static struct PM_cfg amd76x_pm_cfg;
+
+struct cpu_idle_state {
+ int idle;
+ int count;
+};
+static struct cpu_idle_state prs[2];
+
+static struct pci_device_id __devinitdata amd_nb_tbl[] = {
+ {PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_700C, PCI_ANY_ID, PCI_ANY_ID,},
+ {0,}
+};
+
+static struct pci_device_id __devinitdata amd_sb_tbl[] = {
+ {PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7413, PCI_ANY_ID, PCI_ANY_ID,},
+ {PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7443, PCI_ANY_ID, PCI_ANY_ID,},
+ {0,}
+};
+
+static struct pci_driver amd_nb_driver = {
+ .name = "amd76x_pm-nb",
+ .id_table = amd_nb_tbl,
+ .probe = amd_nb_init,
+ .remove = __devexit_p(amd_nb_remove),
+};
+
+static int __devinit
+amd_nb_init(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ pdev_nb = pdev;
+ printk(KERN_INFO "amd76x_pm: Initializing northbridge %s\n",
+ pdev_nb->dev.name);
+ return 0;
+}
+
+static void __devexit
+amd_nb_remove(struct pci_dev *pdev)
+{
+}
+
+
+
+
+/*
+ * Configures the AMD-762 northbridge to support PM calls
+ */
+static int
+config_amd762(int enable)
+{
+ unsigned int regdword;
+
+ /* Enable STPGNT in BIU Status/Control for cpu0 */
+ pci_read_config_dword(pdev_nb, 0x60, ®dword);
+ regdword |= (1 << 17);
+ pci_write_config_dword(pdev_nb, 0x60, regdword);
+
+ /* Enable STPGNT in BIU Status/Control for cpu1 */
+ pci_read_config_dword(pdev_nb, 0x68, ®dword);
+ regdword |= (1 << 17);
+ pci_write_config_dword(pdev_nb, 0x68, regdword);
+
+ /* DRAM refresh enable */
+ pci_read_config_dword(pdev_nb, 0x58, ®dword);
+ regdword &= ~(1 << 19);
+ pci_write_config_dword(pdev_nb, 0x58, regdword);
+
+ /* Self refresh enable */
+ pci_read_config_dword(pdev_nb, 0x70, ®dword);
+ regdword |= (1 << 18);
+ pci_write_config_dword(pdev_nb, 0x70, regdword);
+
+ return 0;
+}
+
+
+/*
+ * Get the base PMIO address and set the pm registers in amd76x_pm_cfg.
+ */
+static void
+amd76x_get_PM(void)
+{
+ unsigned int regdword;
+
+ /* Get the address for pm status, P_LVL2, etc */
+ pci_read_config_dword(pdev_sb, 0x58, ®dword);
+ regdword &= 0xff80;
+ amd76x_pm_cfg.status_reg = (regdword + 0x00);
+ amd76x_pm_cfg.slp_reg = (regdword + 0x04);
+ amd76x_pm_cfg.NTH_reg = (regdword + 0x10);
+ amd76x_pm_cfg.C2_reg = (regdword + 0x14);
+ amd76x_pm_cfg.C3_reg = (regdword + 0x15);
+ amd76x_pm_cfg.resume_reg = (regdword + 0x16); /* N/A for 768 */
+}
+
+
+/*
+ * En/Disable PMIO and configure W4SG & STPGNT.
+ */
+static int
+config_PMIO_amd76x(int is_766, int enable)
+{
+ unsigned char regbyte;
+
+ /* Clear W4SG, and set PMIOEN, if using a 765/766 set STPGNT as well.
+ * AMD-766: C3A41; page 59 in AMD-766 doc
+ * AMD-768: DevB:3x41C; page 94 in AMD-768 doc */
+ pci_read_config_byte(pdev_sb, 0x41, ®byte);
+ if(enable) {
+ regbyte |= ((0 << 0) | (is_766?1:0 << 1) | (1 << 7));
+ }
+ else {
+ regbyte |= (0 << 7);
+ }
+ pci_write_config_byte(pdev_sb, 0x41, regbyte);
+ return 0;
+}
+
+/*
+ * C2 idle support for AMD-766.
+ */
+static void
+config_amd766_C2(int enable)
+{
+ unsigned int regdword;
+
+ /* Set C2 options in C3A50, page 63 in AMD-766 doc */
+ pci_read_config_dword(pdev_sb, 0x50, ®dword);
+ if(enable) {
+ regdword &= ~((DCSTOP_EN | CPUSTP_EN | PCISTP_EN | SUSPND_EN |
+ CPURST_EN) << C2_REGS);
+ regdword |= (STPCLK_EN
...
read more »