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