1/* $OpenBSD: ichpcib.c,v 1.33 2023/02/04 19:19:36 cheloha 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#include <sys/timetc.h> 30 31#include <machine/bus.h> 32 33#include <dev/pci/pcireg.h> 34#include <dev/pci/pcivar.h> 35#include <dev/pci/pcidevs.h> 36 37#include <dev/pci/ichreg.h> 38 39struct ichpcib_softc { 40 struct device sc_dev; 41 42 bus_space_tag_t sc_pm_iot; 43 bus_space_handle_t sc_pm_ioh; 44}; 45 46int ichpcib_match(struct device *, void *, void *); 47void ichpcib_attach(struct device *, struct device *, void *); 48 49int ichss_present(struct pci_attach_args *); 50void ichss_setperf(int); 51 52/* arch/i386/pci/pcib.c */ 53void pcibattach(struct device *, struct device *, void *); 54 55u_int ichpcib_get_timecount(struct timecounter *tc); 56 57struct timecounter ichpcib_timecounter = { 58 .tc_get_timecount = ichpcib_get_timecount, 59 .tc_counter_mask = 0xffffff, 60 .tc_frequency = 3579545, 61 .tc_name = "ICHPM", 62 .tc_quality = 1000, 63 .tc_priv = NULL, 64 .tc_user = 0, 65}; 66 67const struct cfattach ichpcib_ca = { 68 sizeof(struct ichpcib_softc), ichpcib_match, ichpcib_attach 69}; 70 71struct cfdriver ichpcib_cd = { 72 NULL, "ichpcib", DV_DULL 73}; 74 75#ifndef SMALL_KERNEL 76static const char p4hint[] = "Mobile Intel(R) Pentium(R) 4"; 77struct ichpcib_softc *ichss_sc; 78extern int setperf_prio; 79#endif /* !SMALL_KERNEL */ 80 81const struct pci_matchid ichpcib_devices[] = { 82 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_6300ESB_LPC }, 83 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_6321ESB_LPC }, 84 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801AA_LPC }, 85 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801AB_LPC }, 86 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801BA_LPC }, 87 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801BAM_LPC }, 88 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801CA_LPC }, 89 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801CAM_LPC }, 90 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801DB_LPC }, 91 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801DBM_LPC }, 92 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801E_LPC }, 93 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801EB_LPC }, 94 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801FB_LPC }, 95 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801FBM_LPC }, 96 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GB_LPC }, 97 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GBM_LPC }, 98 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GH_LPC }, 99 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GHM_LPC }, 100 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801H_LPC }, 101 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801HBM_LPC }, 102 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IB_LPC }, 103 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IH_LPC }, 104 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IO_LPC }, 105 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IR_LPC } 106}; 107 108int 109ichpcib_match(struct device *parent, void *match, void *aux) 110{ 111 if (pci_matchbyid((struct pci_attach_args *)aux, ichpcib_devices, 112 sizeof(ichpcib_devices) / sizeof(ichpcib_devices[0]))) 113 return (2); /* supersede pcib(4) */ 114 return (0); 115} 116 117void 118ichpcib_attach(struct device *parent, struct device *self, void *aux) 119{ 120 struct ichpcib_softc *sc = (struct ichpcib_softc *)self; 121 struct pci_attach_args *pa = aux; 122 pcireg_t cntl, pmbase; 123 124 /* Check if power management I/O space is enabled */ 125 cntl = pci_conf_read(pa->pa_pc, pa->pa_tag, ICH_ACPI_CNTL); 126 if ((cntl & ICH_ACPI_CNTL_ACPI_EN) == 0) { 127 printf(": PM disabled"); 128 goto corepcib; 129 } 130 131 /* Map power management I/O space */ 132 sc->sc_pm_iot = pa->pa_iot; 133 pmbase = pci_conf_read(pa->pa_pc, pa->pa_tag, ICH_PMBASE); 134 if (bus_space_map(sc->sc_pm_iot, PCI_MAPREG_IO_ADDR(pmbase), 135 ICH_PMSIZE, 0, &sc->sc_pm_ioh) != 0) 136 goto corepcib; 137 138 /* Register new timecounter */ 139 ichpcib_timecounter.tc_priv = sc; 140 tc_init(&ichpcib_timecounter); 141 142 printf(": %s-bit timer at %lluHz", 143 (ichpcib_timecounter.tc_counter_mask == 0xffffffff ? "32" : "24"), 144 (unsigned long long)ichpcib_timecounter.tc_frequency); 145 146#ifndef SMALL_KERNEL 147 /* Check for SpeedStep */ 148 if (ichss_present(pa)) { 149 printf(": SpeedStep"); 150 151 /* Enable SpeedStep */ 152 pci_conf_write(pa->pa_pc, pa->pa_tag, ICH_GEN_PMCON1, 153 pci_conf_read(pa->pa_pc, pa->pa_tag, ICH_GEN_PMCON1) | 154 ICH_GEN_PMCON1_SS_EN); 155 156 /* Hook into hw.setperf sysctl */ 157 ichss_sc = sc; 158 cpu_setperf = ichss_setperf; 159 setperf_prio = 2; 160 } 161#endif /* !SMALL_KERNEL */ 162 163corepcib: 164 /* Provide core pcib(4) functionality */ 165 pcibattach(parent, self, aux); 166} 167 168#ifndef SMALL_KERNEL 169int 170ichss_present(struct pci_attach_args *pa) 171{ 172 pcitag_t br_tag; 173 pcireg_t br_id, br_class; 174 struct cpu_info *ci; 175 int family, model, stepping, brandid, ret; 176 177 ret = 0; 178 if (setperf_prio > 2) 179 return (ret); 180 181 ci = curcpu(); 182 family = (ci->ci_signature >> 8) & 15; 183 model = (ci->ci_signature >> 4) & 15; 184 stepping = ci->ci_signature & 15; 185 brandid = cpu_miscinfo & 0xff; /* XXX should put this in ci */ 186 187 /* 188 * This form of SpeedStep works only with certain Intel processors. 189 * However, other processors can be coupled with these ICH southbridges 190 * causing false positives. This heuristic comes partly from the 191 * Linux speedstep-ich driver. 192 */ 193 if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801DBM_LPC || 194 PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801CAM_LPC || 195 PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801BAM_LPC) { 196 if (family == 15 && model == 2) { 197 switch(stepping) { 198 case 4: 199 if (brandid == 14 || brandid == 15) 200 ret = 1; 201 break; 202 case 7: 203 if (brandid == 14) 204 ret = 1; 205 break; 206 case 9: 207 if (brandid == 14 && strncasecmp(cpu_model, 208 p4hint, sizeof(p4hint)-1) == 0) { 209 ret = 1; 210 } 211 break; 212 } 213 } else if (family == 6 && model == 11) { 214 if (stepping == 1) 215 ret = 1; 216 } 217 218 /* 219 * Old revisions of the 82815 hostbridge found on 220 * Dell Inspirons 8000 and 8100 don't support 221 * SpeedStep. 222 */ 223 if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801BAM_LPC) { 224 /* 225 * XXX: dev 0 func 0 is not always a hostbridge, 226 * should be converted to use pchb(4) hook. 227 */ 228 br_tag = pci_make_tag(pa->pa_pc, pa->pa_bus, 0, 0); 229 br_id = pci_conf_read(pa->pa_pc, br_tag, PCI_ID_REG); 230 br_class = pci_conf_read(pa->pa_pc, br_tag, PCI_CLASS_REG); 231 232 if (PCI_PRODUCT(br_id) == PCI_PRODUCT_INTEL_82815_HB && 233 PCI_REVISION(br_class) < 5) { 234 ret = 0; 235 } 236 } 237 } 238 239 return (ret); 240} 241 242void 243ichss_setperf(int level) 244{ 245 struct ichpcib_softc *sc = ichss_sc; 246 u_int8_t state, ostate, cntl; 247 int s; 248 249#ifdef DIAGNOSTIC 250 if (sc == NULL) { 251 printf("%s: no ichss_sc", __func__); 252 return; 253 } 254#endif 255 256 s = splhigh(); 257 state = bus_space_read_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_SS_CNTL); 258 ostate = state; 259 260 /* Only two states are available */ 261 if (level <= 50) 262 state |= ICH_PM_SS_STATE_LOW; 263 else 264 state &= ~ICH_PM_SS_STATE_LOW; 265 266 /* 267 * An Intel SpeedStep technology transition _always_ occur on 268 * writes to the ICH_PM_SS_CNTL register, even if the value 269 * written is the same as the previous value. So do the write 270 * only if the state has changed. 271 */ 272 if (state != ostate) { 273 /* Disable bus mastering arbitration */ 274 cntl = bus_space_read_1(sc->sc_pm_iot, sc->sc_pm_ioh, 275 ICH_PM_CNTL); 276 bus_space_write_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_CNTL, 277 cntl | ICH_PM_ARB_DIS); 278 279 /* Do the transition */ 280 bus_space_write_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_SS_CNTL, 281 state); 282 283 /* Restore bus mastering arbitration state */ 284 bus_space_write_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_CNTL, 285 cntl); 286 287 if (update_cpuspeed != NULL) 288 update_cpuspeed(); 289 } 290 splx(s); 291} 292#endif /* !SMALL_KERNEL */ 293 294u_int 295ichpcib_get_timecount(struct timecounter *tc) 296{ 297 struct ichpcib_softc *sc = tc->tc_priv; 298 u_int u1, u2, u3; 299 300 u2 = bus_space_read_4(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_TMR); 301 u3 = bus_space_read_4(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_TMR); 302 do { 303 u1 = u2; 304 u2 = u3; 305 u3 = bus_space_read_4(sc->sc_pm_iot, sc->sc_pm_ioh, 306 ICH_PM_TMR); 307 } while (u1 > u2 || u2 > u3); 308 309 return (u2); 310} 311