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