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