diff options
author | Thorsten Lockert <tholo@cvs.openbsd.org> | 2004-07-28 17:15:13 +0000 |
---|---|---|
committer | Thorsten Lockert <tholo@cvs.openbsd.org> | 2004-07-28 17:15:13 +0000 |
commit | c65eb6dac1a01e8aedf3be76a030db86be9264f4 (patch) | |
tree | a775e61964a48db2019d60bb802076883a9b047b /sys/dev | |
parent | 953666701c83d79c01d0ce862cd14f2120a66b7c (diff) |
This touches only MI code, and adds new time keeping code. The
code is all conditionalized on __HAVE_TIMECOUNTER, and not
enabled on any platforms.
adjtime(2) support exists, courtesy of nordin@, sysctl(2) support
and a concept of quality for each time source attached exists.
High quality time sources exists for PIIX4 ACPI timer as well as
some AMD power management chips. This will have to be redone
once we actually add ACPI support (at that time we need to use
the ACPI interfaces to get at these clocks).
ok art@ ken@ miod@ jmc@ and many more
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/pci/amdpm.c | 74 | ||||
-rw-r--r-- | sys/dev/pci/amdpmreg.h | 5 | ||||
-rw-r--r-- | sys/dev/pci/files.pci | 7 | ||||
-rw-r--r-- | sys/dev/pci/piixpm.c | 171 |
4 files changed, 249 insertions, 8 deletions
diff --git a/sys/dev/pci/amdpm.c b/sys/dev/pci/amdpm.c index 8f9d51e54aa..dec9a1d8b75 100644 --- a/sys/dev/pci/amdpm.c +++ b/sys/dev/pci/amdpm.c @@ -1,4 +1,4 @@ -/* $OpenBSD: amdpm.c,v 1.3 2002/11/04 17:12:34 fgsch Exp $ */ +/* $OpenBSD: amdpm.c,v 1.4 2004/07/28 17:15:12 tholo Exp $ */ /*- * Copyright (c) 2002 The NetBSD Foundation, Inc. @@ -41,6 +41,9 @@ #include <sys/kernel.h> #include <sys/device.h> #include <sys/timeout.h> +#ifdef __HAVE_TIMECOUNTER +#include <sys/timetc.h> +#endif #include <dev/pci/pcivar.h> #include <dev/pci/pcireg.h> @@ -49,6 +52,23 @@ #include <dev/rndvar.h> #include <dev/pci/amdpmreg.h> +#ifdef __HAVE_TIMECOUNTER +unsigned amdpm_get_timecount(struct timecounter *tc); + +#ifndef AMDPM_FREQUENCY +#define AMDPM_FREQUENCY 3579545 +#endif + +static struct timecounter amdpm_timecounter = { + amdpm_get_timecount, /* get_timecount */ + 0, /* no poll_pps */ + 0xffffff, /* counter_mask */ + AMDPM_FREQUENCY, /* frequency */ + "AMDPM", /* name */ + 1000 /* quality */ +}; +#endif + struct amdpm_softc { struct device sc_dev; @@ -90,7 +110,8 @@ amdpm_match(struct device *parent, void *match, void *aux) struct pci_attach_args *pa = aux; if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_AMD && - PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_AMD_PBC768_PMC) + (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_AMD_PBC768_PMC || + PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_AMD_766_PMC)) return (1); return (0); } @@ -101,15 +122,15 @@ amdpm_attach(struct device *parent, struct device *self, void *aux) struct amdpm_softc *sc = (struct amdpm_softc *) self; struct pci_attach_args *pa = aux; struct timeval tv1, tv2; - pcireg_t reg; + pcireg_t cfg_reg, reg; int i; sc->sc_pc = pa->pa_pc; sc->sc_tag = pa->pa_tag; sc->sc_iot = pa->pa_iot; - reg = pci_conf_read(pa->pa_pc, pa->pa_tag, AMDPM_CONFREG); - if ((reg & AMDPM_PMIOEN) == 0) { + cfg_reg = pci_conf_read(pa->pa_pc, pa->pa_tag, AMDPM_CONFREG); + if ((cfg_reg & AMDPM_PMIOEN) == 0) { printf(": PMxx space isn't enabled\n"); return; } @@ -120,8 +141,22 @@ amdpm_attach(struct device *parent, struct device *self, void *aux) return; } - reg = pci_conf_read(pa->pa_pc, pa->pa_tag, AMDPM_CONFREG); - if (reg & AMDPM_RNGEN) { +#ifdef __HAVE_TIMECOUNTER + if ((cfg_reg & AMDPM_TMRRST) == 0 && + (cfg_reg & AMDPM_STOPTMR) == 0 && + PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_AMD_PBC768_PMC) { + printf(": %d-bit timer at %dHz", + (cfg_reg & AMDPM_TMR32) ? 32 : 24, + amdpm_timecounter.tc_frequency); + + amdpm_timecounter.tc_priv = sc; + if (cfg_reg & AMDPM_TMR32) + amdpm_timecounter.tc_counter_mask = 0xffffffffu; + tc_init(&amdpm_timecounter); + } +#endif + + if (cfg_reg & AMDPM_RNGEN) { /* Check to see if we can read data from the RNG. */ (void) bus_space_read_4(sc->sc_iot, sc->sc_ioh, AMDPM_RNGDATA); @@ -155,6 +190,8 @@ amdpm_attach(struct device *parent, struct device *self, void *aux) timeout_set(&sc->sc_rnd_ch, amdpm_rnd_callout, sc); amdpm_rnd_callout(sc); } + + printf("\n"); } void @@ -179,3 +216,26 @@ amdpm_rnd_callout(void *v) AMDPM_RNDCNT_INCR(&sc->sc_rnd_miss); timeout_add(&sc->sc_rnd_ch, 1); } + +#ifdef __HAVE_TIMECOUNTER +unsigned +amdpm_get_timecount(struct timecounter *tc) +{ + struct amdpm_softc *sc = tc->tc_priv; + unsigned u2; +#if 0 + unsigned u1, u3; +#endif + + u2 = bus_space_read_4(sc->sc_iot, sc->sc_ioh, AMDPM_TMR); +#if 0 + u3 = bus_space_read_4(sc->sc_iot, sc->sc_ioh, AMDPM_TMR); + do { + u1 = u2; + u2 = u3; + u3 = bus_space_read_4(sc->sc_iot, sc->sc_ioh, AMDPM_TMR); + } while (u1 > u2 || u2 > u3); +#endif + return (u2); +} +#endif diff --git a/sys/dev/pci/amdpmreg.h b/sys/dev/pci/amdpmreg.h index f44a2db31c4..d16658af4fd 100644 --- a/sys/dev/pci/amdpmreg.h +++ b/sys/dev/pci/amdpmreg.h @@ -40,9 +40,12 @@ /* 0x40: General Configuration 1 Register */ #define AMDPM_RNGEN 0x00000080 /* random number generator enable */ +#define AMDPM_STOPTMR 0x00000040 /* stop free-running timer */ /* 0x41: General Configuration 2 Register */ #define AMDPM_PMIOEN 0x00008000 /* system management IO space enable */ +#define AMDPM_TMRRST 0x00004000 /* reset free-running timer */ +#define AMDPM_TMR32 0x00000800 /* extended (32 bit) timer enable */ /* 0x42: SCI Interrupt Configuration Register */ /* 0x43: Previous Power State Register */ @@ -53,6 +56,8 @@ #define AMDPM_PMSIZE 256 /* PMxx space size */ /* Registers in PMxx space */ +#define AMDPM_TMR 0x08 /* 24/32 bit timer register */ + #define AMDPM_RNGDATA 0xf0 /* 32 bit random data register */ #define AMDPM_RNGSTAT 0xf4 /* RNG status register */ #define AMDPM_RNGDONE 0x00000001 /* Random number generation complete */ diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci index 3b1d18c7bba..21c5c6d6d08 100644 --- a/sys/dev/pci/files.pci +++ b/sys/dev/pci/files.pci @@ -1,4 +1,4 @@ -# $OpenBSD: files.pci,v 1.156 2004/06/26 06:43:14 alex Exp $ +# $OpenBSD: files.pci,v 1.157 2004/07/28 17:15:12 tholo Exp $ # $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $ # # Config file and device description for machine-independent PCI code. @@ -505,3 +505,8 @@ file dev/pci/if_san_common.c san file dev/pci/if_san_obsd.c san file dev/pci/if_san_te1.c san file dev/pci/if_san_xilinx.c san + +# PIIX4 power management controller +device piixpm {} +attach piixpm at pci +file dev/pci/piixpm.c piixpm diff --git a/sys/dev/pci/piixpm.c b/sys/dev/pci/piixpm.c new file mode 100644 index 00000000000..7d9b10bf3fc --- /dev/null +++ b/sys/dev/pci/piixpm.c @@ -0,0 +1,171 @@ +/*- + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp + * ---------------------------------------------------------------------------- + * + * $OpenBSD: piixpm.c,v 1.1 2004/07/28 17:15:12 tholo Exp $ + * $FreeBSD: /repoman/r/ncvs/src/sys/i386/i386/mp_clock.c,v 1.19 2004/05/30 20:34:57 phk Exp $ + */ + +/*- + * Just when we thought life were beautiful, reality pops its grim face over + * the edge again: + * + * ] 20. ACPI Timer Errata + * ] + * ] Problem: The power management timer may return improper result when + * ] read. Although the timer value settles properly after incrementing, + * ] while incrementing there is a 3nS window every 69.8nS where the + * ] timer value is indeterminate (a 4.2% chance that the data will be + * ] incorrect when read). As a result, the ACPI free running count up + * ] timer specification is violated due to erroneous reads. Implication: + * ] System hangs due to the "inaccuracy" of the timer when used by + * ] software for time critical events and delays. + * ] + * ] Workaround: Read the register twice and compare. + * ] Status: This will not be fixed in the PIIX4 or PIIX4E. + * + * The counter is in other words not latched to the PCI bus clock when + * read. Notice the workaround isn't: We need to read until we have + * three monotonic samples and then use the middle one, otherwise we are + * not protected against the fact that the bits can be wrong in two + * directions. If we only cared about monosity two reads would be enough. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/kernel.h> +#include <sys/sysctl.h> +#ifdef __HAVE_TIMECOUNTER +#include <sys/timetc.h> +#endif + +#include <machine/bus.h> + +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> +#include <dev/pci/pcidevs.h> + +#define PIIX_PMPTR 0x40 /* PIIX PM address ptr */ + +#define PIIX_PMBASE(x) ((x) & 0xffc0) /* PIIX PM base address */ +#define PIIX_PMSIZE 56 /* PIIX PM space size */ + +struct piixpm_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; +}; + +int piixpm_probe(struct device *, void *, void *); +void piixpm_attach(struct device *, struct device *, void *); + +#ifdef __HAVE_TIMECOUNTER +unsigned piix_get_timecount(struct timecounter *tc); + +static u_int piix_freq = 14318182/4; + +static struct timecounter piix_timecounter = { + piix_get_timecount, /* get_timecount */ + 0, /* no poll_pps */ + 0xffffff, /* counter_mask */ + 0, /* frequency */ + "PIIX", /* name */ + 1000 /* quality */ +}; +#endif + +struct cfattach piixpm_ca = { + sizeof(struct piixpm_softc), piixpm_probe, piixpm_attach +}; + +struct cfdriver piixpm_cd = { + NULL, "piixpm", DV_DULL +}; + +#if 0 +int +sysctl_machdep_piix_freq(SYSCTL_HANDLER_ARGS) +{ + int error; + u_int freq; + + if (piix_timecounter.tc_frequency == 0) + return (EOPNOTSUPP); + freq = piix_freq; + error = sysctl_handle_int(oidp, &freq, sizeof(freq), req); + if (error == 0 && req->newptr != NULL) { + piix_freq = freq; + piix_timecounter.tc_frequency = piix_freq; + } + return (error); +} + +SYSCTL_PROC(_machdep, OID_AUTO, piix_freq, CTLTYPE_INT | CTLFLAG_RW, + 0, sizeof(u_int), sysctl_machdep_piix_freq, "I", ""); +#endif + +#ifdef __HAVE_TIMECOUNTER +unsigned +piix_get_timecount(struct timecounter *tc) +{ + struct piixpm_softc *sc = (struct piixpm_softc *) tc->tc_priv; + unsigned u1, u2, u3; + + u2 = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 8); + u3 = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 8); + do { + u1 = u2; + u2 = u3; + u3 = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 8); + } while (u1 > u2 || u2 > u3); + return (u2); +} +#endif + +/* + * XXX - this has to be redone if we ever do real ACPI + */ +int +piixpm_probe(struct device *parent, void *match, void *aux) +{ + struct pci_attach_args *pa = (struct pci_attach_args *) aux; + pcireg_t reg; + + if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_INTEL || + PCI_PRODUCT(pa->pa_id) != PCI_PRODUCT_INTEL_82371AB_PMC) + return (0); + + reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG); + if ((reg & PCI_COMMAND_IO_ENABLE) == 0) + return (0); + return (1); +} + +void +piixpm_attach(struct device *parent, struct device *self, void *aux) +{ + struct piixpm_softc *sc = (struct piixpm_softc *) self; + struct pci_attach_args *pa = aux; + pci_chipset_tag_t pc = pa->pa_pc; + pcireg_t reg; + + reg = pci_conf_read(pc, pa->pa_tag, PIIX_PMPTR); + if (bus_space_map(pa->pa_iot, PIIX_PMBASE(reg), PIIX_PMSIZE, + 0, &sc->sc_ioh)) { + printf(": can't map i/o space\n"); + return; + } + + sc->sc_iot = pa->pa_iot; + printf("\n"); +#ifdef __HAVE_TIMECOUNTER + piix_timecounter.tc_frequency = piix_freq; + piix_timecounter.tc_priv = sc; + tc_init(&piix_timecounter); +#endif +} |