public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* amd-smp-idle module avail for testing max 90 W power savings
@ 2002-07-02 19:14 Tony Lindgren
  2002-07-02 19:27 ` Sebastian Droege
  0 siblings, 1 reply; 5+ messages in thread
From: Tony Lindgren @ 2002-07-02 19:14 UTC (permalink / raw)
  To: linux-kernel

[-- Attachment #1: Type: text/plain, Size: 957 bytes --]

Hi all,

I just posted a module to cut down on the energy bill for people with
dual Athlon systems, well currently only tested on Tyan S2460, which is
based on AMD-760MP chipset.

Amd-smp-idle enables the power savings mode like VCool and LVCool, but
amd-smp-idle uses the Linux PCI features, and supports currently SMP
only. So far I've squeezed out maximum 90 Watt power savings out of my
system :)

Adding support for other chipsets, and maybe merging the LVCool
functionality should be easy. Hopefully the core functionality will
eventually make it to the ACPI to provide C2 support.

Please note that there's a bug where loading the module for the first
time after rebooting causes the system to go sleep mode instead of
the idle mode. To wake up the system, just hit the power button once.
So, don't try this out on a remote server :)

The code is following, or you can also download it from:

http://www.muru.com/linux/amd-smp-idle/

Cheers,

Tony



[-- Attachment #2: amd-smp-idle.c --]
[-- Type: text/x-csrc, Size: 8672 bytes --]

/*
 * Processor idle mode module for AMD SMP systems
 *
 * Copyright (C) 2002 Tony Lindgren <tony@atomide.com>
 *
 * 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 TYAN S2460 (760MP) system. 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.

 Compile command:
   gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes \
   -Wno-trigraphs -O2 -fomit-frame-pointer -fno-strict-aliasing -fno-common \
   -pipe -mpreferred-stack-boundary=2 -march=athlon -DMODULE -DMODVERSIONS \
   -include /usr/src/linux/include/linux/modversions.h -c amd-idle.c

 */

#include <linux/config.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/delay.h>

extern void default_idle(void);
static void amd_smp_idle(void);
static int amd_idle_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 int __devinit amd_sb_init(struct pci_dev *pdev,
				 const struct pci_device_id *ent);
static void amd_sb_remove(struct pci_dev *pdev);

#define DEBUG 1
#define VERSION	"20020702"
#define AMD762		0x700c
#define AMD765_766	0x7413
#define CUR_PR	smp_processor_id()

struct pci_dev *pdev_nb;
struct pci_dev *pdev_sb;

struct idle_cfg {
	unsigned int status_reg;
	unsigned int idle_reg;
	unsigned int slp_reg;
	unsigned int resume_reg;
	void (*orig_idle) (void);
	void (*curr_idle) (void);
	int last_pr;
};
static struct idle_cfg amd_idle_cfg;

struct cpu_idle_state {
	int idle;
	int count;
};
static struct cpu_idle_state prs[2];

static struct pci_device_id amd_nb_tbl[] __devinitdata = {
	{PCI_VENDOR_ID_AMD, AMD762, PCI_ANY_ID, PCI_ANY_ID,},
	{0,}
};

static struct pci_device_id amd_sb_tbl[] __devinitdata = {
	{PCI_VENDOR_ID_AMD, AMD765_766, PCI_ANY_ID, PCI_ANY_ID,},
	{0,}
};

static struct pci_driver amd_nb_driver = {
	name:"amd-smp-idle-nb",
	id_table:amd_nb_tbl,
	probe:amd_nb_init,
	remove:__devexit_p(amd_nb_remove),
};

static struct pci_driver amd_sb_driver = {
	name:"amd-smp-idle-sb",
	id_table:amd_sb_tbl,
	probe:amd_sb_init,
	remove:__devexit_p(amd_sb_remove),
};

static int __devinit
amd_nb_init(struct pci_dev *pdev, const struct pci_device_id *ent)
{
	pdev_nb = pdev;
	printk(KERN_INFO "amd-smp-idle: Initializing northbridge %s\n",
	       pdev_nb->name);

	return 0;
}

static void __devexit
amd_nb_remove(struct pci_dev *pdev)
{
}

static int __devinit
amd_sb_init(struct pci_dev *pdev, const struct pci_device_id *ent)
{
	pdev_sb = pdev;
	printk(KERN_INFO "amd-smp-idle: Initializing southbridge %s\n",
	       pdev_sb->name);

	return 0;
}

static void __devexit
amd_sb_remove(struct pci_dev *pdev)
{
}

/*
 * Configures the southbridge to support idle calls, and gets
 * the processor idle call register location.
 */
static int
sb_idle_amd_766(int enable)
{
	unsigned int regdword;
	unsigned short regshort;
	unsigned char regbyte;

#define DCSTOP_EN	(1 << 1)
#define STPCLK_EN	(1 << 2)
#define CPUSTP_EN	(1 << 3)
#define PCISTP_EN	(1 << 4)
#define CPUSLP_EN	(1 << 5)
#define SUSPND_EN	(1 << 6)

#define C2_REGS		0
#define C3_REGS		8
#define POS_REGS	16	

	/* Get the address for pm status, P_LVL2, etc */
	pci_read_config_dword(pdev_sb, 0x58, &regdword);
	regdword &= 0xff80;
	amd_idle_cfg.status_reg = regdword + 0x00;
	amd_idle_cfg.slp_reg = regdword + 0x04;
	amd_idle_cfg.idle_reg = regdword + 0x14;
	amd_idle_cfg.resume_reg = regdword + 0x16;

	/* Set C2 options in C3A50, page 63 in AMD-766 doc */
	pci_read_config_dword(pdev_sb, 0x50, &regdword);

	regdword &= ~((DCSTOP_EN | CPUSTP_EN | PCISTP_EN | 
		       SUSPND_EN) << C2_REGS);

	regdword |= (STPCLK_EN << C2_REGS);	/* ~ 20 Watt savings max */
	regdword |= (CPUSLP_EN << C2_REGS);	/* Additional ~ 70 Watts max! */

	pci_write_config_dword(pdev_sb, 0x50, regdword);

	/* Clear W4SG, set STPGNT and PMIOEN at C3A41 */
	pci_read_config_byte(pdev_sb, 0x41, &regbyte);
	regbyte &= ~(1 << 0);
	regbyte |= ((1 << 1) | (1 << 7));
	pci_write_config_byte(pdev_sb, 0x41, regbyte);

	return 0;
}

/*
 * Configures the northbridge to support idle calls
 */
static int
nb_idle_amd_762(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;
}

/*
 * Idle loop for single processor systems
 */
void
amd_idle(void)
{
	// FIXME: Optionally add non-smp idle loop here
}

/*
 * Idle loop for SMP systems, supports currently only 2 processors.
 */
static void
amd_smp_idle(void)
{

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

	/*
	 * 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 && amd_idle_cfg.last_pr == CUR_PR) {
		prs[0].idle = 0;
		prs[1].idle = 0;
		amd_idle_cfg.last_pr = CUR_PR;
		return;
	}

	prs[CUR_PR].count++;

	/* Don't start the idle mode immediately */
	if (prs[CUR_PR].count >= LAZY_IDLE_DELAY) {

		/* Put the current processor into idle mode */
		prs[CUR_PR].idle = 1;

		/* Only idle if both processors are idle */
		if (prs[0].idle && prs[1].idle)
			inb(amd_idle_cfg.idle_reg);

		prs[CUR_PR].count = 0;

	}
	amd_idle_cfg.last_pr = CUR_PR;
}

/*
 * Finds and initializes the bridges, and then sets the idle function
 */
static int
amd_idle_main(void)
{
	int found;

	/* Find northbridge */
	found = pci_module_init(&amd_nb_driver);
	if (found < 0) {
		printk(KERN_ERR "amd-smp-idle: Could not find northbridge\n");
		return 1;
	}

	/* Find southbridge */
	found = pci_module_init(&amd_sb_driver);
	if (found < 0) {
		printk(KERN_ERR "amd-smp-idle: Could not find southbridge\n");
		pci_unregister_driver(&amd_nb_driver);
		return 1;
	}

	/* Init southbridge */
	switch (pdev_sb->device) {
	case AMD765_766:	/* AMD-765 or 766 */
		sb_idle_amd_766(1);
		break;
	default:
		printk(KERN_ERR "amd-smp-idle: No southbridge to initialize\n");
		break;
	}

	/* Init northbridge and queue the new idle function */
	switch (pdev_nb->device) {
	case AMD762:
		nb_idle_amd_762(1);
		amd_idle_cfg.curr_idle = amd_smp_idle;
		break;
	default:
		printk(KERN_ERR "amd-smp-idle: No northbridge to initialize\n");
		break;
	}

	if (!amd_idle_cfg.curr_idle) {
		printk(KERN_ERR "amd-smp-idle: Idle function not changed\n");
		return 1;
	}

	amd_idle_cfg.orig_idle = pm_idle;
	pm_idle = amd_idle_cfg.curr_idle;

	return 0;
}

static int __init
amd_idle_init(void)
{
	printk(KERN_INFO "amd-smp-idle: AMD processor idle module version %s\n",
	       VERSION);
	return amd_idle_main();
}

static void __exit
amd_idle_cleanup(void)
{
	pm_idle = amd_idle_cfg.orig_idle;

	/* 
	 * 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.
	 */
	schedule();
	mdelay(1000);

	pci_unregister_driver(&amd_nb_driver);
	pci_unregister_driver(&amd_sb_driver);

}

MODULE_LICENSE("GPL");
module_init(amd_idle_init);
module_exit(amd_idle_cleanup);

^ permalink raw reply	[flat|nested] 5+ messages in thread
* Re: amd-smp-idle module avail for testing max 90 W power savings
@ 2002-07-03  5:50 Dieter Nützel
  0 siblings, 0 replies; 5+ messages in thread
From: Dieter Nützel @ 2002-07-03  5:50 UTC (permalink / raw)
  To: Tony Lindgren; +Cc: Sebastian Droege, nofftz, Linux Kernel List

On Tue, 2 Jul 2002 19:41, Tony Lindgren wrote:
> * Sebastian Droege <sebastian.droege@gmx.de> [020702 12:29]:
> > On Tue, 2 Jul 2002 12:14:54 -0700
> > Tony Lindgren <tony@atomide.com> wrote:
> >
> > > Amd-smp-idle enables the power savings mode like VCool and LVCool, but
> > > amd-smp-idle uses the Linux PCI features, and supports currently SMP
> > > only. So far I've squeezed out maximum 90 Watt power savings out of my
> > > system :)
> > >
> > > http://www.muru.com/linux/amd-smp-idle/
> >
> > Is it possible to do something similar for AMD-751

Hardly, there is NO chipset dokumentation for the AMD 750 (760) available.

> > or VIA-686a (or other UP Athlon chipsets)?

See below.

> Yes, you could use LVCool program, or merge the LVCool functionality to
> amd-smp-idle. I added place holders for enabling other chips.
>
> Just add functions for enabling north and southbridge, and then fill in 
> the idle function. I kind of thought of modifying LVCool, but it was not 
> using the Linux PCI API, and was not really suitable for SMP systems.
>
> LVCool is at:
>
> http://mpet.freeservers.com/LVCool.html

Have a look here, too:

http://cip.uni-trier.de/nofftz/linux/

I'll give your code a shot on my brand new
dual Athlon MP 1900+ (MP unlocked XPs, 4th bridge of L5 is closed)
MSI K7D Master-L (MS-6501 v1.0) AMD 760MPX
1 GB DDR266-SDRAM CL2

But it doesn't go hot...;-)

/home/nuetzel> sensors
eeprom-i2c-0-50
Adapter: SMBus AMD768 adapter at 06e0
Algorithm: Non-I2C SMBus adapter

eeprom-i2c-0-51
Adapter: SMBus AMD768 adapter at 06e0
Algorithm: Non-I2C SMBus adapter

w83627hf-isa-0290
Adapter: ISA adapter
Algorithm: ISA algorithm
VCore 1:   +1.72 V  (min =  +4.08 V, max =  +4.08 V)
VCore 2:   +2.46 V  (min =  +4.08 V, max =  +4.08 V)
+3.3V:     +3.36 V  (min =  +3.13 V, max =  +3.45 V)
+5V:       +4.94 V  (min =  +4.72 V, max =  +5.24 V)
+12V:     +12.16 V  (min = +10.79 V, max = +13.19 V)
-12V:     -12.65 V  (min = -13.21 V, max = -10.90 V)
-5V:       -5.10 V  (min =  -5.26 V, max =  -4.76 V)
V5SB:      +5.39 V  (min =  +4.72 V, max =  +5.24 V)
VBat:      +3.42 V  (min =  +2.40 V, max =  +3.60 V)
U160:        0 RPM  (min = 3000 RPM, div = 2)
CPU 0:    4470 RPM  (min = 3000 RPM, div = 2)
CPU 1:    4299 RPM  (min = 3000 RPM, div = 2)
System:   +30.0°C   (limit = +60°C, hysteresis = +50°C) sensor = thermistor
CPU 1:    +34.0°C   (limit = +60°C, hysteresis = +50°C) sensor = 3904 
transistor
CPU 0:    +36.5°C   (limit = +60°C, hysteresis = +50°C) sensor = 3904 
transistor
vid:      +18.50 V
alarms:   Chassis intrusion detection
beep_enable:
          Sound alarm disabled
-- 
Dieter Nützel
Graduate Student, Computer Science

University of Hamburg
Department of Computer Science
@home: Dieter.Nuetzel@hamburg.de


^ permalink raw reply	[flat|nested] 5+ messages in thread
* Re: amd-smp-idle module avail for testing max 90 W power savings
@ 2002-07-04  1:31 Dieter Nützel
  0 siblings, 0 replies; 5+ messages in thread
From: Dieter Nützel @ 2002-07-04  1:31 UTC (permalink / raw)
  To: Tony Lindgren; +Cc: Sebastian Droege, Daniel Nofftz, Linux Kernel List

On Wednesday 3 July 2002 05:50, Dieter Nützel wrote:
> I'll give your code a shot on my brand new
> dual Athlon MP 1900+ (MP unlocked XPs, 4th bridge of L5 is closed)
> MSI K7D Master-L (MS-6501 v1.0) AMD 760MPX
> 1 GB DDR266-SDRAM CL2
>
> But it doesn't go hot...;-)

The module could not be loaded:

SunWave1 src/patches# insmod amd-smp-idle.o
amd-smp-idle.o: init_module: Device or resource busy
Hint: insmod errors can be caused by incorrect module parameters, including 
invalid IO or IRQ parameters.
      You may find more information in syslog or the output from dmesg

amd-smp-idle: AMD processor idle module version 20020702
amd-smp-idle: Initializing northbridge Advanced Micro Devices [AMD] AMD-760 MP 
[IGD4-2P] System Controller
amd-smp-idle: Could not find southbridge

PCI devices found:
  Bus  0, device   0, function  0:
    Host bridge: Advanced Micro Devices [AMD] AMD-760 MP [IGD4-2P] System 
Controller (rev 17).
      Master Capable.  Latency=32.
      Prefetchable 32 bit memory at 0xe8000000 [0xebffffff].
      Prefetchable 32 bit memory at 0xee100000 [0xee100fff].
      I/O at 0xc400 [0xc403].
  Bus  0, device   1, function  0:
    PCI bridge: Advanced Micro Devices [AMD] AMD-760 MP [IGD4-2P] AGP Bridge 
(rev 0).
      Master Capable.  Latency=32.  Min Gnt=14.
  Bus  0, device   7, function  0:
    ISA bridge: Advanced Micro Devices [AMD] AMD-768 [Opus] ISA (rev 5).
  Bus  0, device   7, function  1:
    IDE interface: Advanced Micro Devices [AMD] AMD-768 [Opus] IDE (rev 4).
      Master Capable.  Latency=32.
      I/O at 0xc000 [0xc00f].
  Bus  0, device   7, function  3:
    Bridge: Advanced Micro Devices [AMD] AMD-768 [Opus] ACPI (rev 3).
      Master Capable.  Latency=32.
  Bus  0, device  16, function  0:
    PCI bridge: Advanced Micro Devices [AMD] AMD-768 [Opus] PCI (rev 5).
      Master Capable.  Latency=32.  Min Gnt=6.
  Bus  1, device   5, function  0:
    VGA compatible controller: 3Dfx Interactive, Inc. Voodoo 4 / Voodoo 5 (rev 
1).
      IRQ 17.
      Non-prefetchable 32 bit memory at 0xe0000000 [0xe3ffffff].
      Prefetchable 32 bit memory at 0xd8000000 [0xdfffffff].
      I/O at 0xb000 [0xb0ff].
  Bus  2, device   0, function  0:
    USB Controller: Advanced Micro Devices [AMD] AMD-768 [Opus] USB (rev 7).
      IRQ 19.
      Master Capable.  Latency=32.  Max Lat=80.
      Non-prefetchable 32 bit memory at 0xed122000 [0xed122fff].
  Bus  2, device   4, function  0:
    Multimedia audio controller: Creative Labs SB Live! EMU10k1 (rev 8).
      IRQ 16.
      Master Capable.  Latency=32.  Min Gnt=2.Max Lat=20.
      I/O at 0x9000 [0x901f].
  Bus  2, device   4, function  1:
    Input device controller: Creative Labs SB Live! MIDI/Game Port (rev 8).
      Master Capable.  Latency=32.
      I/O at 0x9400 [0x9407].
  Bus  2, device   5, function  0:
    Ethernet controller: Intel Corp. 82557/8/9 [Ethernet Pro 100] (rev 4).
      IRQ 17.
      Master Capable.  Latency=32.  Min Gnt=8.Max Lat=56.
      Prefetchable 32 bit memory at 0xee000000 [0xee000fff].
      I/O at 0x9800 [0x981f].
      Non-prefetchable 32 bit memory at 0xed000000 [0xed0fffff].
  Bus  2, device   6, function  0:
    SCSI storage controller: Adaptec AHA-2940U/UW/D / AIC-7881U (rev 1).
      IRQ 18.
      Master Capable.  Latency=32.  Min Gnt=8.Max Lat=8.
      I/O at 0x9c00 [0x9cff].
      Non-prefetchable 32 bit memory at 0xed120000 [0xed120fff].
  Bus  2, device   9, function  0:
    Ethernet controller: Intel Corp. 82559ER (rev 9).
      IRQ 17.
      Master Capable.  Latency=32.  Min Gnt=8.Max Lat=56.
      Non-prefetchable 32 bit memory at 0xed121000 [0xed121fff].
      I/O at 0xa000 [0xa03f].
      Non-prefetchable 32 bit memory at 0xed100000 [0xed11ffff].

Thanks,
	Dieter

-- 
Dieter Nützel
Graduate Student, Computer Science

University of Hamburg
Department of Computer Science
@home: Dieter.Nuetzel@hamburg.de

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2002-07-04  1:28 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2002-07-02 19:14 amd-smp-idle module avail for testing max 90 W power savings Tony Lindgren
2002-07-02 19:27 ` Sebastian Droege
2002-07-02 19:41   ` Tony Lindgren
  -- strict thread matches above, loose matches on Subject: below --
2002-07-03  5:50 Dieter Nützel
2002-07-04  1:31 Dieter Nützel

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox