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