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