amd76x_pm port to 2.5.70

amd76x_pm port to 2.5.70

Post by Pasi Savolaine » Sun, 01 Jun 2003 20:30:08



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, &regdword);
+       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, &regdword);
+       regdword |= (1 << 17);
+       pci_write_config_dword(pdev_nb, 0x68, regdword);
+
+       /* DRAM refresh enable */
+       pci_read_config_dword(pdev_nb, 0x58, &regdword);
+       regdword &= ~(1 << 19);
+       pci_write_config_dword(pdev_nb, 0x58, regdword);
+
+       /* Self refresh enable */
+       pci_read_config_dword(pdev_nb, 0x70, &regdword);
+       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, &regdword);
+       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, &regbyte);
+       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, &regdword);
+       if(enable) {
+               regdword &= ~((DCSTOP_EN | CPUSTP_EN | PCISTP_EN | SUSPND_EN |
+                                       CPURST_EN) << C2_REGS);
+               regdword |= (STPCLK_EN
...

read more »

 
 
 

amd76x_pm port to 2.5.70

Post by Zwane Mwaikamb » Sun, 01 Jun 2003 20:50:07



> 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.

pm_idle = NULL_or_previous_handler;
synchronize_kernel();
unload_module;

Quote:> +amd76x_up_idle(void)
> +{
> +  // FIXME: Optionally add non-smp idle loop here
> +}
> +#endif

This is going to really suck power wise, at least do a rep_nop or some
such otherwise you spin in a tight polling loop around need_resched.

Quote:> +static void
> +amd76x_smp_idle(void)
> +{
> +  /*
> +   * Exit idle mode immediately if the CPU does not change.
> +   * Usually that means that we have some load on another CPU.
> +   */
> +
> +  if (prs[0].idle && prs[1].idle && amd76x_pm_cfg.last_pr == smp_processor_id()) {
> +          prs[0].idle = 0;
> +          prs[1].idle = 0;
> +          /* This looks redundent as it was just checked in the if() */
> +          /* amd76x_pm_cfg.last_pr = smp_processor_id(); */

One is a comparison the other an assignment, what's going on here?

Quote:> +static void __exit
> +amd76x_pm_cleanup(void)
> +{
> +#ifndef AMD76X_NTH
> +  pm_idle = amd76x_pm_cfg.orig_idle;
> +  wmb();
> +  //__asm__ __volatile__ ("wbinvd;"); // propagate through SMP

synchronize_kernel() here, then you should be safe to unload.

--
function.linuxpower.ca
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in

More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

 
 
 

amd76x_pm port to 2.5.70

Post by Dave Jone » Sun, 01 Jun 2003 23:50:08


 > + /* 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, &regbyte);
 > + if(enable) {
 > +         regbyte |= ((0 << 0) | (is_766?1:0 << 1) | (1 << 7));
                         ^^^^^^

This looks totally bogus. If you want that bit clearing, you need
to AND its inverse, not OR it.  The second statement also looks a
bit funny to the eye.

 > + }
 > + else {
 > +         regbyte |= (0 << 7);

Ditto.

 > +/*
 > + * Idle loop for SMP systems, supports currently only 2 processors.
 > + *
 > + * Note; for 2.5 folks - not pre-empt safe

Indeed.

 > +static void
 > +amd76x_smp_idle(void)
 > +{
 > + /*
 > +  * Exit idle mode immediately if the CPU does not change.
 > +  * Usually that means that we have some load on another CPU.
 > +  */
 > +
 > + if (prs[0].idle && prs[1].idle && amd76x_pm_cfg.last_pr == smp_processor_id()) {
 > +         prs[0].idle = 0;
 > +         prs[1].idle = 0;
 > +         /* This looks redundent as it was just checked in the if() */
 > +         /* amd76x_pm_cfg.last_pr = smp_processor_id(); */

except with preemption, it may have changed. This needs to be fixed,
as noted in the function header. Either get_cpu(),put_cpu() or explicit
preempt_enable/disable()                

 > +
 > +/*
 > + *    Info exported through "/proc/driver/amd76x_pm"

This should really be using sysfs. Adding extra junk to /proc is
somewhat frowned upon, and with the other PM stuff now living in
sysfs, that seems to be the way forward.

 > +static void __exit
 > +amd76x_pm_cleanup(void)
 > +{
 > +#ifndef AMD76X_NTH
 > + pm_idle = amd76x_pm_cfg.orig_idle;
 > + wmb();
 > + //__asm__ __volatile__ ("wbinvd;"); // propagate through SMP

Not sure why this was there, is it noted in the 766 spec to flush
the cache ? if so, it needs to be reenabled, and used with on_each_cpu()

 > + /*
 > +  * FIXME: We want to wait until all CPUs have set the new
 > +  * idle function, otherwise we will oops. This may not be
 > +  * the right way to do it, but seems to work.
 > +  *
 > +  * - Best answer is going to be to ban unload, but when its debugged
 > +  *   --- Alan
 > +  */
 > + schedule();
 > + mdelay(1000);

Ummmm, oh-kay. That's a little funky. Look at how the MTRR driver
does its synchronisation, it maybe sucky, but it looks a little
cleaner than this IMO.

 > +#define LAZY_IDLE_DELAY  800     /* 0: Best savings,  3000: More responsive */

Sounds like possible candidate for something that should be exposed
via sysfs.

                Dave

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in

More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

 
 
 

amd76x_pm port to 2.5.70

Post by Pasi Savolaine » Mon, 02 Jun 2003 04:20:07




> > +     /* 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, &regbyte);
> > +     if(enable) {
> > +             regbyte |= ((0 << 0) | (is_766?1:0 << 1) | (1 << 7));
>                          ^^^^^^

> This looks totally bogus. If you want that bit clearing, you need
> to AND its inverse, not OR it.  The second statement also looks a
> bit funny to the eye.

AFAICT, it's 'for future expansion', NMC. I'll trim it down. Either way,
that enable seems to work, at least on my machine. As I don't know much
about it, I can't say what's right/wrong.
(I don't have the spec. Only code that does desired thing)

Quote:> > +static void
> > +amd76x_smp_idle(void)
> > +{
> > +     /*
> > +      * Exit idle mode immediately if the CPU does not change.
> > +      * Usually that means that we have some load on another CPU.
> > +      */
> > +
> > +     if (prs[0].idle && prs[1].idle && amd76x_pm_cfg.last_pr == smp_processor_id()) {
> > +             prs[0].idle = 0;
> > +             prs[1].idle = 0;
> > +             /* This looks redundent as it was just checked in the if() */
> > +             /* amd76x_pm_cfg.last_pr = smp_processor_id(); */

> except with preemption, it may have changed. This needs to be fixed,
> as noted in the function header. Either get_cpu(),put_cpu() or explicit
> preempt_enable/disable()          

This is idle loop, like we're not doing anything, and if somebody is to
preempt us, then it sure means we're not supposed to idle.

Second point is (I'm _really_ on thin ground here..) that if we get_cpu()
here, and other CPU is sleeping, then it'd be woken up sooner, for cache
will need syncing.

Quote:> > +/*
> > + *    Info exported through "/proc/driver/amd76x_pm"

> This should really be using sysfs. Adding extra junk to /proc is
> somewhat frowned upon, and with the other PM stuff now living in
> sysfs, that seems to be the way forward.

done.

Quote:

> > +static void __exit
> > +amd76x_pm_cleanup(void)
> > +{
> > +#ifndef AMD76X_NTH
> > +     pm_idle = amd76x_pm_cfg.orig_idle;
> > +     wmb();
> > +     //__asm__ __volatile__ ("wbinvd;"); // propagate through SMP

> Not sure why this was there, is it noted in the 766 spec to flush
> the cache ? if so, it needs to be reenabled, and used with on_each_cpu()

Zwane told me to use synchronize_kernel(); it cured my crashes on rmmod
amd76x_pm. -> hack removed.

Quote:> Sounds like possible candidate for something that should be exposed
> via sysfs.

done.

Iteration n at
<http://varg.dyndns.org/psi/files/misc/amd76x_pm-2.5.70.patch.bz2>

/sys/devices/pci0/00:00.0/C2_cnt  (ro) number of C2 calls made.
/sys/devices/pci0/00:00.0/lazy_idle (rw) number of successfull idle
 calls to be made before entering C2.

--
   Psi -- <http://www.iki.fi/pasi.savolainen>
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in

More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

 
 
 

amd76x_pm port to 2.5.70

Post by Jason Lun » Mon, 02 Jun 2003 17:40:07



Quote:> +++ linux-2.5.70-mod/drivers/char/amd76x_pm.c      2003-05-31 20:55:37.000000000 +0300

What is this thing doing in drivers/char? It has nothing whatsoever to
do with char devices. Couldn't this be handled by the ACPI idle loop?

Jason

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in

More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

 
 
 

amd76x_pm port to 2.5.70

Post by Alan Co » Mon, 02 Jun 2003 23:20:07




> > +++ linux-2.5.70-mod/drivers/char/amd76x_pm.c 2003-05-31 20:55:37.000000000 +0300

> What is this thing doing in drivers/char? It has nothing whatsoever to
> do with char devices. Couldn't this be handled by the ACPI idle loop?

RTFS 8)

Its an alternative driver to the ACPI driver. I'm not sure where it
should go but in 2.4 its in drivers/char. 2.5.x puts the other PM
drivers in arch/i386 so maybe that is where it belongs

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in

More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

 
 
 

1. [2.5.70][ANNOUNCE] kexec for 2.5.70 available

A patch set for kexec for 2.5.70 is now available. This patch set is
based upon the stable 2.5.{67,68,69} versions of kexec. The most recent
set of kexec patches do not yet patch cleanly into 2.5.70.

This patch was tested to work on a dual-proc P4-1.7GHz Xeon system. The
only known strangeness I have observed (YMMV) is that the VESA
framebuffer driver did not reinitialize correctly after kexec-ing a new
kernel, but the system rebooted correctly.

The stable patches for 2.5.70 are available for download from OSDL's
patch life-cycle manager (PLM ) in pieces, or as a single unified patch.

More info here:
http://www.osdl.org/archive/andyp/bloom/Code/Linux/Kexec/index.html

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in

More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

2. Forged Devflash e-mail renew notice?

3. 2.5.70 and 2.5.70-mm3 hang on bootup

4. flock(), Files left locked after program terminates

5. ALSA Segmentation fault load modules 2.5.70 and 2.5.70mm1

6. Telnet, logging sesions

7. 2.5.70-mm3 with contest

8. Smile Floormats

9. 2.4.18, 2.4.21-rc3/6, 2.5.70-mm1 Interactivity test + script

10. software suspend in 2.5.70-mm3.

11. [2.5.70] ALSA help text correction

12. glibc compiling with kernel 2.5.70-bk17

13. 2.5.70-bk12 Oops at boot