1 /* $OpenBSD: ichpcib.c,v 1.23 2008/01/04 00:23:25 kettenis 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 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IB_LPC }, 111 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IH_LPC }, 112 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IO_LPC }, 113 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801IR_LPC } 114 }; 115 116 int 117 ichpcib_match(struct device *parent, void *match, void *aux) 118 { 119 if (pci_matchbyid((struct pci_attach_args *)aux, ichpcib_devices, 120 sizeof(ichpcib_devices) / sizeof(ichpcib_devices[0]))) 121 return (2); /* supersede pcib(4) */ 122 return (0); 123 } 124 125 void 126 ichpcib_attach(struct device *parent, struct device *self, void *aux) 127 { 128 struct ichpcib_softc *sc = (struct ichpcib_softc *)self; 129 struct pci_attach_args *pa = aux; 130 pcireg_t cntl, pmbase; 131 132 /* Check if power management I/O space is enabled */ 133 cntl = pci_conf_read(pa->pa_pc, pa->pa_tag, ICH_ACPI_CNTL); 134 if ((cntl & ICH_ACPI_CNTL_ACPI_EN) == 0) { 135 printf(": PM disabled"); 136 goto corepcib; 137 } 138 139 /* Map power management I/O space */ 140 sc->sc_pm_iot = pa->pa_iot; 141 pmbase = pci_conf_read(pa->pa_pc, pa->pa_tag, ICH_PMBASE); 142 if (bus_space_map(sc->sc_pm_iot, PCI_MAPREG_IO_ADDR(pmbase), 143 ICH_PMSIZE, 0, &sc->sc_pm_ioh) != 0) 144 goto corepcib; 145 146 #ifdef __HAVE_TIMECOUNTER 147 /* Register new timecounter */ 148 ichpcib_timecounter.tc_priv = sc; 149 tc_init(&ichpcib_timecounter); 150 151 printf(": %s-bit timer at %lluHz", 152 (ichpcib_timecounter.tc_counter_mask == 0xffffffff ? "32" : "24"), 153 (unsigned long long)ichpcib_timecounter.tc_frequency); 154 #endif /* __HAVE_TIMECOUNTER */ 155 156 #ifndef SMALL_KERNEL 157 /* Check for SpeedStep */ 158 if (ichss_present(pa)) { 159 printf(": SpeedStep"); 160 161 /* Enable SpeedStep */ 162 pci_conf_write(pa->pa_pc, pa->pa_tag, ICH_GEN_PMCON1, 163 pci_conf_read(pa->pa_pc, pa->pa_tag, ICH_GEN_PMCON1) | 164 ICH_GEN_PMCON1_SS_EN); 165 166 /* Hook into hw.setperf sysctl */ 167 ichss_sc = sc; 168 cpu_setperf = ichss_setperf; 169 setperf_prio = 2; 170 } 171 #endif /* !SMALL_KERNEL */ 172 173 corepcib: 174 /* Provide core pcib(4) functionality */ 175 pcibattach(parent, self, aux); 176 } 177 178 #ifndef SMALL_KERNEL 179 int 180 ichss_present(struct pci_attach_args *pa) 181 { 182 pcitag_t br_tag; 183 pcireg_t br_id, br_class; 184 struct cpu_info *ci; 185 int family, model, stepping, brandid, ret; 186 187 ret = 0; 188 if (setperf_prio > 2) 189 return (ret); 190 191 ci = curcpu(); 192 family = (ci->ci_signature >> 8) & 15; 193 model = (ci->ci_signature >> 4) & 15; 194 stepping = ci->ci_signature & 15; 195 brandid = cpu_miscinfo & 0xff; /* XXX should put this in ci */ 196 197 /* 198 * This form of SpeedStep works only with certain Intel processors. 199 * However, other processors can be coupled with these ICH southbridges 200 * causing false positives. This heuristic comes partly from the 201 * Linux speedstep-ich driver. 202 */ 203 if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801DBM_LPC || 204 PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801CAM_LPC || 205 PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801BAM_LPC) { 206 if (family == 15 && model == 2) { 207 switch(stepping) { 208 case 4: 209 if (brandid == 14 || brandid == 15) 210 ret = 1; 211 break; 212 case 7: 213 if (brandid == 14) 214 ret = 1; 215 break; 216 case 9: 217 if (brandid == 14 && strncasecmp(cpu_model, 218 p4hint, sizeof(p4hint)-1) == 0) { 219 ret = 1; 220 } 221 break; 222 } 223 } else if (family == 6 && model == 11) { 224 if (stepping == 1) 225 ret = 1; 226 } 227 228 /* 229 * Old revisions of the 82815 hostbridge found on 230 * Dell Inspirons 8000 and 8100 don't support 231 * SpeedStep. 232 */ 233 if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82801BAM_LPC) { 234 /* 235 * XXX: dev 0 func 0 is not always a hostbridge, 236 * should be converted to use pchb(4) hook. 237 */ 238 br_tag = pci_make_tag(pa->pa_pc, pa->pa_bus, 0, 0); 239 br_id = pci_conf_read(pa->pa_pc, br_tag, PCI_ID_REG); 240 br_class = pci_conf_read(pa->pa_pc, br_tag, PCI_CLASS_REG); 241 242 if (PCI_PRODUCT(br_id) == PCI_PRODUCT_INTEL_82815_HB && 243 PCI_REVISION(br_class) < 5) { 244 ret = 0; 245 } 246 } 247 } 248 249 return (ret); 250 } 251 252 void 253 ichss_setperf(int level) 254 { 255 struct ichpcib_softc *sc = ichss_sc; 256 u_int8_t state, ostate, cntl; 257 int s; 258 259 #ifdef DIAGNOSTIC 260 if (sc == NULL) { 261 printf("%s: no ichss_sc", __func__); 262 return; 263 } 264 #endif 265 266 s = splhigh(); 267 state = bus_space_read_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_SS_CNTL); 268 ostate = state; 269 270 /* Only two states are available */ 271 if (level <= 50) 272 state |= ICH_PM_SS_STATE_LOW; 273 else 274 state &= ~ICH_PM_SS_STATE_LOW; 275 276 /* 277 * An Intel SpeedStep technology transition _always_ occur on 278 * writes to the ICH_PM_SS_CNTL register, even if the value 279 * written is the same as the previous value. So do the write 280 * only if the state has changed. 281 */ 282 if (state != ostate) { 283 /* Disable bus mastering arbitration */ 284 cntl = bus_space_read_1(sc->sc_pm_iot, sc->sc_pm_ioh, 285 ICH_PM_CNTL); 286 bus_space_write_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_CNTL, 287 cntl | ICH_PM_ARB_DIS); 288 289 /* Do the transition */ 290 bus_space_write_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_SS_CNTL, 291 state); 292 293 /* Restore bus mastering arbitration state */ 294 bus_space_write_1(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_CNTL, 295 cntl); 296 297 if (update_cpuspeed != NULL) 298 update_cpuspeed(); 299 } 300 splx(s); 301 } 302 #endif /* !SMALL_KERNEL */ 303 304 #ifdef __HAVE_TIMECOUNTER 305 u_int 306 ichpcib_get_timecount(struct timecounter *tc) 307 { 308 struct ichpcib_softc *sc = tc->tc_priv; 309 u_int u1, u2, u3; 310 311 u2 = bus_space_read_4(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_TMR); 312 u3 = bus_space_read_4(sc->sc_pm_iot, sc->sc_pm_ioh, ICH_PM_TMR); 313 do { 314 u1 = u2; 315 u2 = u3; 316 u3 = bus_space_read_4(sc->sc_pm_iot, sc->sc_pm_ioh, 317 ICH_PM_TMR); 318 } while (u1 > u2 || u2 > u3); 319 320 return (u2); 321 } 322 #endif /* __HAVE_TIMECOUNTER */ 323