Annotation of sys/arch/i386/pci/ichpcib.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: ichpcib.c,v 1.19 2007/06/02 18:39:57 jsg Exp $ */
! 2: /*
! 3: * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org>
! 4: *
! 5: * Permission to use, copy, modify, and distribute this software for any
! 6: * purpose with or without fee is hereby granted, provided that the above
! 7: * copyright notice and this permission notice appear in all copies.
! 8: *
! 9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! 15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 16: */
! 17:
! 18: /*
! 19: * Special driver for the Intel ICHx/ICHx-M LPC bridges that attaches
! 20: * instead of pcib(4). In addition to the core pcib(4) functionality this
! 21: * driver provides support for the Intel SpeedStep technology and
! 22: * power management timer.
! 23: */
! 24:
! 25: #include <sys/param.h>
! 26: #include <sys/systm.h>
! 27: #include <sys/device.h>
! 28: #include <sys/sysctl.h>
! 29: #ifdef __HAVE_TIMECOUNTER
! 30: #include <sys/timetc.h>
! 31: #endif
! 32:
! 33: #include <machine/bus.h>
! 34:
! 35: #include <dev/pci/pcireg.h>
! 36: #include <dev/pci/pcivar.h>
! 37: #include <dev/pci/pcidevs.h>
! 38:
! 39: #include <dev/pci/ichreg.h>
! 40:
! 41: #include <machine/cpu.h>
! 42: #include <machine/cpufunc.h>
! 43:
! 44: struct ichpcib_softc {
! 45: struct device sc_dev;
! 46:
! 47: bus_space_tag_t sc_pm_iot;
! 48: bus_space_handle_t sc_pm_ioh;
! 49: };
! 50:
! 51: int ichpcib_match(struct device *, void *, void *);
! 52: void ichpcib_attach(struct device *, struct device *, void *);
! 53:
! 54: int ichss_present(struct pci_attach_args *);
! 55: void ichss_setperf(int);
! 56:
! 57: /* arch/i386/pci/pcib.c */
! 58: void pcibattach(struct device *, struct device *, void *);
! 59:
! 60: #ifdef __HAVE_TIMECOUNTER
! 61: u_int ichpcib_get_timecount(struct timecounter *tc);
! 62:
! 63: struct timecounter ichpcib_timecounter = {
! 64: ichpcib_get_timecount, /* get_timecount */
! 65: 0, /* no poll_pps */
! 66: 0xffffff, /* counter_mask */
! 67: 3579545, /* frequency */
! 68: "ICHPM", /* name */
! 69: 1000 /* quality */
! 70: };
! 71: #endif /* __HAVE_TIMECOUNTER */
! 72:
! 73: struct cfattach ichpcib_ca = {
! 74: sizeof(struct ichpcib_softc),
! 75: ichpcib_match,
! 76: ichpcib_attach
! 77: };
! 78:
! 79: struct cfdriver ichpcib_cd = {
! 80: NULL, "ichpcib", DV_DULL
! 81: };
! 82:
! 83: #ifndef SMALL_KERNEL
! 84: static const char p4hint[] = "Mobile Intel(R) Pentium(R) 4";
! 85: struct ichpcib_softc *ichss_sc;
! 86: extern int setperf_prio;
! 87: #endif /* !SMALL_KERNEL */
! 88:
! 89: const struct pci_matchid ichpcib_devices[] = {
! 90: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_6300ESB_LPC },
! 91: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_6321ESB_LPC },
! 92: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801AA_LPC },
! 93: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801AB_LPC },
! 94: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801BA_LPC },
! 95: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801BAM_LPC },
! 96: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801CA_LPC },
! 97: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801CAM_LPC },
! 98: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801DB_LPC },
! 99: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801DBM_LPC },
! 100: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801E_LPC },
! 101: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801EB_LPC },
! 102: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801FB_LPC },
! 103: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801FBM_LPC },
! 104: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GB_LPC },
! 105: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GBM_LPC },
! 106: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GH_LPC },
! 107: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GHM_LPC },
! 108: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801H_LPC },
! 109: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801HBM_LPC }
! 110: };
! 111:
! 112: int
! 113: ichpcib_match(struct device *parent, void *match, void *aux)
! 114: {
! 115: if (pci_matchbyid((struct pci_attach_args *)aux, ichpcib_devices,
! 116: sizeof(ichpcib_devices) / sizeof(ichpcib_devices[0])))
! 117: return (2); /* supersede pcib(4) */
! 118: return (0);
! 119: }
! 120:
! 121: void
! 122: ichpcib_attach(struct device *parent, struct device *self, void *aux)
! 123: {
! 124: struct ichpcib_softc *sc = (struct ichpcib_softc *)self;
! 125: struct pci_attach_args *pa = aux;
! 126: pcireg_t cntl, pmbase;
! 127:
! 128: /* Check if power management I/O space is enabled */
! 129: cntl = pci_conf_read(pa->pa_pc, pa->pa_tag, ICH_ACPI_CNTL);
! 130: if ((cntl & ICH_ACPI_CNTL_ACPI_EN) == 0) {
! 131: printf(": PM disabled");
! 132: goto corepcib;
! 133: }
! 134:
! 135: /* Map power management I/O space */
! 136: sc->sc_pm_iot = pa->pa_iot;
! 137: pmbase = pci_conf_read(pa->pa_pc, pa->pa_tag, ICH_PMBASE);
! 138: if (bus_space_map(sc->sc_pm_iot, PCI_MAPREG_IO_ADDR(pmbase),
! 139: ICH_PMSIZE, 0, &sc->sc_pm_ioh) != 0)
! 140: goto corepcib;
! 141:
! 142: #ifdef __HAVE_TIMECOUNTER
! 143: /* Register new timecounter */
! 144: ichpcib_timecounter.tc_priv = sc;
! 145: tc_init(&ichpcib_timecounter);
! 146:
! 147: printf(": %s-bit timer at %lluHz",
! 148: (ichpcib_timecounter.tc_counter_mask == 0xffffffff ? "32" : "24"),
! 149: (unsigned long long)ichpcib_timecounter.tc_frequency);
! 150: #endif /* __HAVE_TIMECOUNTER */
! 151:
! 152: #ifndef SMALL_KERNEL
! 153: /* Check for SpeedStep */
! 154: if (ichss_present(pa)) {
! 155: printf(": SpeedStep");
! 156:
! 157: /* Enable SpeedStep */
! 158: pci_conf_write(pa->pa_pc, pa->pa_tag, ICH_GEN_PMCON1,
! 159: pci_conf_read(pa->pa_pc, pa->pa_tag, ICH_GEN_PMCON1) |
! 160: ICH_GEN_PMCON1_SS_EN);
! 161:
! 162: /* Hook into hw.setperf sysctl */
! 163: ichss_sc = sc;
! 164: cpu_setperf = ichss_setperf;
! 165: setperf_prio = 2;
! 166: }
! 167: #endif /* !SMALL_KERNEL */
! 168:
! 169: corepcib:
! 170: /* Provide core pcib(4) functionality */
! 171: pcibattach(parent, self, aux);
! 172: }
! 173:
! 174: #ifndef SMALL_KERNEL
! 175: int
! 176: ichss_present(struct pci_attach_args *pa)
! 177: {
! 178: pcitag_t br_tag;
! 179: pcireg_t br_id, br_class;
! 180: struct cpu_info *ci;
! 181: int family, model, stepping, brandid;
! 182:
! 183: if (setperf_prio > 2)
! 184: return (0);
! 185:
! 186: ci = curcpu();
! 187: family = (ci->ci_signature >> 8) & 15;
! 188: model = (ci->ci_signature >> 4) & 15;
! 189: stepping = ci->ci_signature & 15;
! 190: brandid = cpu_miscinfo & 0xff; /* XXX should put this in ci */
! 191:
! 192: /*
! 193: * This form of SpeedStep works only on Intel Mobile Pentium 4.
! 194: * Intel Celeron processors don't support it. However, they
! 195: * can be coupled with ICH southbridges that do, causing false
! 196: * positives. So we ensure that we are running on Intel Mobile
! 197: * Pentium 4.
! 198: * This heuristic comes from the Linux speedstep-ich driver.
! 199: */
! 200: if (!(family == 15 && model == 2 &&
! 201: ((stepping == 4 && (brandid == 14 || brandid == 15)) ||
! 202: (stepping == 7 && brandid == 14) ||
! 203: (stepping == 9 && (brandid == 14 || strncasecmp(cpu_model, p4hint,
! 204: sizeof(p4hint) - 1) == 0)))))
! 205: return (0);
! 206:
! 207: if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801DBM_LPC ||
! 208: PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801CAM_LPC)
! 209: return (1);
! 210: if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801BAM_LPC) {
! 211: /*
! 212: * Old revisions of the 82815 hostbridge found on
! 213: * Dell Inspirons 8000 and 8100 don't support
! 214: * SpeedStep.
! 215: */
! 216: /*
! 217: * XXX: dev 0 func 0 is not always a hostbridge,
! 218: * should be converted to use pchb(4) hook.
! 219: */
! 220: br_tag = pci_make_tag(pa->pa_pc, pa->pa_bus, 0, 0);
! 221: br_id = pci_conf_read(pa->pa_pc, br_tag, PCI_ID_REG);
! 222: br_class = pci_conf_read(pa->pa_pc, br_tag, PCI_CLASS_REG);
! 223:
! 224: if (PCI_PRODUCT(br_id) == PCI_PRODUCT_INTEL_82815_FULL_HUB &&
! 225: PCI_REVISION(br_class) < 5)
! 226: return (0);
! 227: return (1);
! 228: }
! 229:
! 230: return (0);
! 231: }
! 232:
! 233: void
! 234: ichss_setperf(int level)
! 235: {
! 236: struct ichpcib_softc *sc = ichss_sc;
! 237: u_int8_t state, ostate, cntl;
! 238: int s;
! 239:
! 240: #ifdef DIAGNOSTIC
! 241: if (sc == NULL) {
! 242: printf("%s: no ichss_sc", __func__);
! 243: return;
! 244: }
! 245: #endif
! 246:
! 247: s = splhigh();
! 248: state = bus_space_read_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_SS_CNTL);
! 249: ostate = state;
! 250:
! 251: /* Only two states are available */
! 252: if (level <= 50)
! 253: state |= ICH_PM_SS_STATE_LOW;
! 254: else
! 255: state &= ~ICH_PM_SS_STATE_LOW;
! 256:
! 257: /*
! 258: * An Intel SpeedStep technology transition _always_ occur on
! 259: * writes to the ICH_PM_SS_CNTL register, even if the value
! 260: * written is the same as the previous value. So do the write
! 261: * only if the state has changed.
! 262: */
! 263: if (state != ostate) {
! 264: /* Disable bus mastering arbitration */
! 265: cntl = bus_space_read_1(sc->sc_pm_iot, sc->sc_pm_ioh,
! 266: ICH_PM_CNTL);
! 267: bus_space_write_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_CNTL,
! 268: cntl | ICH_PM_ARB_DIS);
! 269:
! 270: /* Do the transition */
! 271: bus_space_write_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_SS_CNTL,
! 272: state);
! 273:
! 274: /* Restore bus mastering arbitration state */
! 275: bus_space_write_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_CNTL,
! 276: cntl);
! 277:
! 278: #ifdef I686_CPU
! 279: if (update_cpuspeed != NULL)
! 280: update_cpuspeed();
! 281: #endif
! 282: }
! 283: splx(s);
! 284: }
! 285: #endif /* !SMALL_KERNEL */
! 286:
! 287: #ifdef __HAVE_TIMECOUNTER
! 288: u_int
! 289: ichpcib_get_timecount(struct timecounter *tc)
! 290: {
! 291: struct ichpcib_softc *sc = tc->tc_priv;
! 292: u_int u1, u2, u3;
! 293:
! 294: u2 = bus_space_read_4(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_TMR);
! 295: u3 = bus_space_read_4(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_TMR);
! 296: do {
! 297: u1 = u2;
! 298: u2 = u3;
! 299: u3 = bus_space_read_4(sc->sc_pm_iot, sc->sc_pm_ioh,
! 300: ICH_PM_TMR);
! 301: } while (u1 > u2 || u2 > u3);
! 302:
! 303: return (u2);
! 304: }
! 305: #endif /* __HAVE_TIMECOUNTER */
CVSweb