diff options
author | Gordon Willem Klok <gwk@cvs.openbsd.org> | 2007-04-26 01:43:15 +0000 |
---|---|---|
committer | Gordon Willem Klok <gwk@cvs.openbsd.org> | 2007-04-26 01:43:15 +0000 |
commit | 6835027787b1c56cc2627f83137576f8aeba1718 (patch) | |
tree | 5af5ad38671dca641bdeb34fcfb21291e04a72aa | |
parent | 3ae16c2db95f91ec3051765f4d11fdf745aa091a (diff) |
Add a setperf mechanism for ich speedstep controlled by SMI on certain
parings of the Intel Pentium 3 and the ich southbridges. Written by
Stefan Sperling <stsp AT tsp DOT in-berlin DOT de> based on a driver in
NetBSD and sys/arch/i386/pci/ichpcib.c.
Tested my mpf@ among others,
ok tedu
-rw-r--r-- | share/man/man4/man4.i386/Makefile | 4 | ||||
-rw-r--r-- | share/man/man4/man4.i386/piixpcib.4 | 73 | ||||
-rw-r--r-- | sys/arch/i386/conf/GENERIC | 4 | ||||
-rw-r--r-- | sys/arch/i386/conf/files.i386 | 9 | ||||
-rw-r--r-- | sys/arch/i386/pci/piixpcib.c | 368 |
5 files changed, 453 insertions, 5 deletions
diff --git a/share/man/man4/man4.i386/Makefile b/share/man/man4/man4.i386/Makefile index a3dbae4884c..e2e576de085 100644 --- a/share/man/man4/man4.i386/Makefile +++ b/share/man/man4/man4.i386/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.59 2006/10/27 17:10:48 jmc Exp $ +# $OpenBSD: Makefile,v 1.60 2007/04/26 01:43:14 gwk Exp $ # from: @(#)Makefile 5.1 (Berkeley) 2/12/91 # Id: Makefile,v 1.4 1995/12/14 05:41:38 deraadt Exp $ @@ -6,7 +6,7 @@ MAN= apm.4 autoconf.4 bios.4 cpu.4 elansc.4 esm.4 \ geodesc.4 glxsb.4 gscpcib.4 gscpm.4 gus.4 ie.4 \ ichpcib.4 intro.4 ioapic.4 \ joy.4 le.4 lms.4 mcd.4 mem.4 mms.4 mtrr.4 npx.4 nvram.4 pas.4 \ - pcibios.4 pctr.4 pss.4 sb.4 \ + pcibios.4 pctr.4 piixpcib.4 pss.4 sb.4 \ sea.4 uha.4 wds.4 wdt.4 wss.4 wt.4 MLINKS+= mem.4 kmem.4 diff --git a/share/man/man4/man4.i386/piixpcib.4 b/share/man/man4/man4.i386/piixpcib.4 new file mode 100644 index 00000000000..ca2501605c3 --- /dev/null +++ b/share/man/man4/man4.i386/piixpcib.4 @@ -0,0 +1,73 @@ +.\" $OpenBSD: piixpcib.4,v 1.1 2007/04/26 01:43:14 gwk Exp $ +.\" +.\" Copyright (c) 2007 Stefan Sperling <stsp@stsp.in-berlin.de> +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd March 20, 2007 +.Dt PIIXPCIB 4 i386 +.Os +.Sh NAME +.Nm piixpcib +.Nd Intel PIIX4 ISA bridges +.Sh SYNOPSIS +.Cd "piixpcib* at pci?" +.Cd "isa* at piixpcib?" +.Sh DESCRIPTION +The +.Nm +driver supports Intel +.Tn 82371AB +and +.Tn 82440MX +PIIX4 ISA bridges. + +Besides the core +.Xr pcib 4 +functionality, the +.Nm +driver provides support for the first generation of Intel's SpeedStep +technology present in some Pentium III CPUs. +It allows the user to manually control CPU frequency with the +.Xr sysctl 8 +program. +The +.Xr apmd 8 +daemon can be used to automatically control CPU frequency. +SpeedStep provides two CPU power states, low and high. +The driver will switch the CPU into low power state if the +hw.setperf sysctl is smaller or equal 50, and into high +power state if hw.setperf is greater 50. +.Sh SEE ALSO +.Xr cpu 4 , +.Xr intro 4 , +.Xr isa 4 , +.Xr pci 4 , +.Xr pcib 4 , +.Xr ichpcib 4 , +.Xr sysctl 8 , +.Xr apmd 8 +.Sh HISTORY +The +.Nm +driver first appeared in +.Ox 4.2 . +.Sh BUGS +The driver sometimes fails to update the hw.cpuspeed sysctl correctly +after switching power state. The reason is that there seems to be +no reliable way to determine CPU frequencies corresponding to low +and high power states, so the driver has to rely on the p3_update_cpuspeed +function to dynamically determine CPU speed after switching power state. +This seems to fail occasionally, especially if interrupt load of the system +is high. Note that this bug is purely cosmetic. Switching power state still +works even if the CPU speed is displayed incorrectly. diff --git a/sys/arch/i386/conf/GENERIC b/sys/arch/i386/conf/GENERIC index f95ccd838fb..6473ec7415f 100644 --- a/sys/arch/i386/conf/GENERIC +++ b/sys/arch/i386/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.554 2007/04/26 00:21:39 dlg Exp $ +# $OpenBSD: GENERIC,v 1.555 2007/04/26 01:43:13 gwk Exp $ # # For further information on compiling OpenBSD kernels, see the config(8) # man page. @@ -53,6 +53,7 @@ esm0 at mainbus? # Dell Embedded Server Management isa0 at mainbus0 isa0 at pcib? isa0 at ichpcib? +isa0 at piixpcib? isa0 at gscpcib? eisa0 at mainbus0 pci* at mainbus0 @@ -82,6 +83,7 @@ pci* at ppb? pci* at pchb? pcib* at pci? # PCI-ISA bridge ichpcib* at pci? # Intel ICHx/ICHx-M LPC bridges +piixpcib* at pci? # Intel PIIX4 PCI-ISA bridge gscpcib* at pci? # NS Geode SC1100 PCI-ISA bridge gpio* at gscpcib? diff --git a/sys/arch/i386/conf/files.i386 b/sys/arch/i386/conf/files.i386 index e19df5e86f8..9643dfe28c7 100644 --- a/sys/arch/i386/conf/files.i386 +++ b/sys/arch/i386/conf/files.i386 @@ -1,4 +1,4 @@ -# $OpenBSD: files.i386,v 1.158 2007/04/21 21:06:15 gwk Exp $ +# $OpenBSD: files.i386,v 1.159 2007/04/26 01:43:13 gwk Exp $ # # new style config file for i386 architecture # @@ -136,13 +136,18 @@ file arch/i386/pci/gscpm.c gscpm # PCI-ISA bridge chipsets device pcib: isabus attach pcib at pci -file arch/i386/pci/pcib.c pcib | ichpcib | gscpcib +file arch/i386/pci/pcib.c pcib | ichpcib | gscpcib | piixpcib # Intel ICHx/ICHx-M LPC bridges device ichpcib: isabus attach ichpcib at pci file arch/i386/pci/ichpcib.c ichpcib +# Intel PIIX4 PCI-ISA bridge +device piixpcib: isabus +attach piixpcib at pci +file arch/i386/pci/piixpcib.c piixpcib & i686_cpu + # National Semiconductor Geode SC1100 PCI-ISA bridge device gscpcib: isabus, gpiobus attach gscpcib at pci diff --git a/sys/arch/i386/pci/piixpcib.c b/sys/arch/i386/pci/piixpcib.c new file mode 100644 index 00000000000..34f570be520 --- /dev/null +++ b/sys/arch/i386/pci/piixpcib.c @@ -0,0 +1,368 @@ +/* $OpenBSD: piixpcib.c,v 1.1 2007/04/26 01:43:13 gwk Exp $ */ + +/* + * Copyright (c) 2007 Stefan Sperling <stsp@stsp.in-berlin.de> + * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + *- + * Copyright (c) 2004, 2006 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Minoura Makoto, Matthew R. Green, and Jared D. McNeill. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Special driver for the Intel PIIX4 bridges that attaches + * instead of pcib(4). In addition to the core pcib(4) functionality this + * driver provides support for Intel SpeedStep technology present in + * some Pentium III CPUs. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/sysctl.h> + +#include <machine/bus.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcidevs.h> + +#include <machine/cpu.h> +#include <machine/cpufunc.h> +#include <machine/kvm86.h> + +/* 0x47534943 == "ISGE" ('Intel Speedstep Gate E') */ +#define PIIXPCIB_ISGE 0x47534943 +#define PIIXPCIB_IST_CALL 0x0000e980 +#define PIIXPCIB_GSIC_CMD 0x82 +#define PIIXPCIB_DEFAULT_COMMAND \ + ((PIIXPCIB_ISGE & 0xffffff00) | (PIIXPCIB_GSIC_CMD & 0xff)) + +#define PIIXPCIB_DEFAULT_SMI_PORT 0xb2 +#define PIIXPCIB_DEFAULT_SMI_DATA 0xb3 + +#define PIIXPCIB_GETSTATE 1 +#define PIIXPCIB_SETSTATE 2 +#define PIIXPCIB_SPEEDSTEP_HIGH 0 +#define PIIXPCIB_SPEEDSTEP_LOW 1 + +struct piixpcib_softc { + struct device sc_dev; + + int sc_sig; + int sc_smi_port; + int sc_smi_data; + int sc_command; + int sc_flags; + + int state; +}; + +int piixpcib_match(struct device *, void *, void *); +void piixpcib_attach(struct device *, struct device *, void *); + +void piixpcib_setperf(int); +int piixpcib_cpuspeed(int *); + +int piixpcib_set_ownership(struct piixpcib_softc *); +int piixpcib_configure_speedstep(struct piixpcib_softc *); +int piixpcib_getset_state(struct piixpcib_softc *, int *, int); +void piixpcib_int15_gsic_call(struct piixpcib_softc *); + +/* arch/i386/pci/pcib.c */ +extern void pcibattach(struct device *, struct device *, void *); + +/* arch/i386/i386/machdep.c */ +extern void p3_update_cpuspeed(void); + +struct cfattach piixpcib_ca = { + sizeof(struct piixpcib_softc), + piixpcib_match, + piixpcib_attach +}; + +struct cfdriver piixpcib_cd = { + NULL, "piixpcib", DV_DULL +}; + +struct piixpcib_softc *piixpcib_sc; +extern int setperf_prio; + +const struct pci_matchid piixpcib_devices[] = { + { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82371AB_ISA}, /* PIIX4 */ + { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82440MX_PM}, /* PIIX4 in MX440 */ +}; + +void +piixpcib_int15_gsic_call(struct piixpcib_softc *sc) +{ + struct kvm86regs regs; + int cmd; + + memset(®s, 0, sizeof(struct kvm86regs)); + regs.eax = PIIXPCIB_IST_CALL; + regs.edx = PIIXPCIB_ISGE; + kvm86_simplecall(0x15, ®s); + + if (regs.eax == PIIXPCIB_ISGE) { + sc->sc_sig = regs.eax; + sc->sc_smi_port = regs.ebx & 0xff; + + cmd = (regs.ebx >> 16) & 0xff; + /* GSIC may return cmd 0x80, should be PIIXPCIB_GSIC_CMD */ + if (cmd == 0x80) + cmd = PIIXPCIB_GSIC_CMD; + sc->sc_command = (sc->sc_sig & 0xffffff00) | (cmd & 0xff); + + sc->sc_smi_data = regs.ecx; + sc->sc_flags = regs.edx; + } +} + +int +piixpcib_set_ownership(struct piixpcib_softc *sc) +{ + int rv; + paddr_t pmagic; + char magic[] = "Copyright (c) 1999 Intel Corporation"; + + pmap_extract(pmap_kernel(), (vaddr_t)magic, &pmagic); + + __asm __volatile( + "movl $0, %%edi\n\t" + "out %%al, (%%dx)\n" + : "=D" (rv) + : "a" (sc->sc_command), + "b" (0), + "c" (0), + "d" (sc->sc_smi_port), + "S" (pmagic) + ); + + return (rv ? ENXIO : 0); +} + +int +piixpcib_configure_speedstep(struct piixpcib_softc *sc) +{ + int rv; + + sc->sc_sig = -1; + + /* setup some defaults */ + sc->sc_smi_port = PIIXPCIB_DEFAULT_SMI_PORT; + sc->sc_smi_data = PIIXPCIB_DEFAULT_SMI_DATA; + sc->sc_command = PIIXPCIB_DEFAULT_COMMAND; + sc->sc_flags = 0; + + piixpcib_int15_gsic_call(sc); + + /* If signature doesn't match, bail out */ + if (sc->sc_sig != PIIXPCIB_ISGE) + return ENODEV; + + if (piixpcib_set_ownership(sc) != 0) { + printf(": unable to claim ownership from BIOS, " + "SpeedStep disabled"); + return ENXIO; + } + + rv = piixpcib_getset_state(sc, &sc->state, PIIXPCIB_GETSTATE); + if (rv != 0) { + printf(": cannot determine CPU power state, " + "SpeedStep disabled"); + return ENXIO; + } + + /* save the sc for IO tag/handle */ + piixpcib_sc = sc; + + return 0; +} + +int +piixpcib_match(struct device *parent, void *match, void *aux) +{ + if (pci_matchbyid((struct pci_attach_args *)aux, piixpcib_devices, + sizeof(piixpcib_devices) / sizeof(piixpcib_devices[0]))) + return (2); /* supersede pcib(4) */ + return (0); +} + +void +piixpcib_attach(struct device *parent, struct device *self, void *aux) +{ + struct piixpcib_softc *sc = (struct piixpcib_softc *)self; + + if (setperf_prio < 2) { + /* Set up SpeedStep. */ + if (piixpcib_configure_speedstep(sc) == 0) { + printf(": SpeedStep"); + + /* Hook into hw.setperf sysctl */ + cpu_setperf = piixpcib_setperf; + setperf_prio = 2; + } + } + + /* Provide core pcib(4) functionality */ + pcibattach(parent, self, aux); +} + +int +piixpcib_getset_state(struct piixpcib_softc *sc, int *state, int function) +{ + int new_state; + int rv; + int eax; + +#ifdef DIAGNOSTIC + if (function != PIIXPCIB_GETSTATE && + function != PIIXPCIB_SETSTATE) { + printf("%s: %s called with invalid function %d\n", + sc->sc_dev.dv_xname, __func__, function); + return EINVAL; + } +#endif + + __asm __volatile( + "movl $0, %%edi\n\t" + "out %%al, (%%dx)\n" + : "=a" (eax), + "=b" (new_state), + "=D" (rv) + : "a" (sc->sc_command), + "b" (function), + "c" (*state), + "d" (sc->sc_smi_port), + "S" (0) + ); + + *state = new_state & 1; + + switch (function) { + case PIIXPCIB_GETSTATE: + if (eax) + return ENXIO; + break; + case PIIXPCIB_SETSTATE: + if (rv) + return ENXIO; + break; + } + + return 0; +} + +void +piixpcib_setperf(int level) +{ + struct piixpcib_softc *sc; + int new_state; + int tries, rv, s; + + sc = piixpcib_sc; + +#ifdef DIAGNOSTIC + if (sc == NULL) { + printf("%s: no cookie", __func__); + return; + } +#endif + + /* Only two states are available */ + if (level <= 50) + new_state = PIIXPCIB_SPEEDSTEP_LOW; + else + new_state = PIIXPCIB_SPEEDSTEP_HIGH; + + if (sc->state == new_state) + return; + + tries = 5; + s = splhigh(); + + do { + rv = piixpcib_getset_state(sc, &new_state, + PIIXPCIB_SETSTATE); + if (rv) + delay(200); + } while (rv && --tries); + + splx(s); + +#ifdef DIAGNOSTIC + if (rv) + printf("%s: setting CPU power state failed", + sc->sc_dev.dv_xname); +#endif + + sc->state = new_state; + + /* Force update of hw.cpuspeed. + * + * XXX: First generation SpeedStep is only present in some + * Pentium III CPUs and we are lacking a reliable method to + * determine CPU freqs corresponding to low and high power state. + * + * And yes, I've tried it the way the Linux speedstep-smi + * driver does it, thank you very much. It doesn't work + * half the time (my machine has more than 4Mhz ;-) and + * even crashes some machines without specific workarounds. + * + * So call p3_update_cpuspeed() from arch/i386/i386/machdep.c + * instead. Works fine on my Thinkpad X21. + * + * BUT: Apparently, if the bus is busy, the transition may be + * delayed and retried under control of evil SMIs we cannot + * control. So busy-wait a short while before updating hw.cpuspeed + * to decrease chances of picking up the old CPU speed. + * There seems to be no reliable fix for this. + */ + delay(200); + p3_update_cpuspeed(); +} + |