1 /* $OpenBSD: piixpcib.c,v 1.2 2007/05/29 02:40:24 tom Exp $ */
2
3 /*
4 * Copyright (c) 2007 Stefan Sperling <stsp@stsp.in-berlin.de>
5 * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 *
19 *-
20 * Copyright (c) 2004, 2006 The NetBSD Foundation, Inc.
21 * All rights reserved.
22 *
23 * This code is derived from software contributed to The NetBSD Foundation
24 * by Minoura Makoto, Matthew R. Green, and Jared D. McNeill.
25 *
26 * Redistribution and use in source and binary forms, with or without
27 * modification, are permitted provided that the following conditions
28 * are met:
29 * 1. Redistributions of source code must retain the above copyright
30 * notice, this list of conditions and the following disclaimer.
31 * 2. Redistributions in binary form must reproduce the above copyright
32 * notice, this list of conditions and the following disclaimer in the
33 * documentation and/or other materials provided with the distribution.
34 * 3. All advertising materials mentioning features or use of this software
35 * must display the following acknowledgement:
36 * This product includes software developed by the NetBSD
37 * Foundation, Inc. and its contributors.
38 * 4. Neither the name of The NetBSD Foundation nor the names of its
39 * contributors may be used to endorse or promote products derived
40 * from this software without specific prior written permission.
41 *
42 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
43 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
44 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
45 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
46 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
47 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
48 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
49 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
50 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
51 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
52 * POSSIBILITY OF SUCH DAMAGE.
53 */
54
55 /*
56 * Special driver for the Intel PIIX4 bridges that attaches
57 * instead of pcib(4). In addition to the core pcib(4) functionality this
58 * driver provides support for Intel SpeedStep technology present in
59 * some Pentium III CPUs.
60 */
61
62 #include <sys/param.h>
63 #include <sys/systm.h>
64 #include <sys/device.h>
65 #include <sys/sysctl.h>
66
67 #include <machine/bus.h>
68
69 #include <dev/pci/pcireg.h>
70 #include <dev/pci/pcivar.h>
71 #include <dev/pci/pcidevs.h>
72
73 #include <machine/cpu.h>
74 #include <machine/cpufunc.h>
75 #include <machine/kvm86.h>
76
77 /* 0x47534943 == "ISGE" ('Intel Speedstep Gate E') */
78 #define PIIXPCIB_ISGE 0x47534943
79 #define PIIXPCIB_IST_CALL 0x0000e980
80 #define PIIXPCIB_GSIC_CMD 0x82
81 #define PIIXPCIB_DEFAULT_COMMAND \
82 ((PIIXPCIB_ISGE & 0xffffff00) | (PIIXPCIB_GSIC_CMD & 0xff))
83
84 #define PIIXPCIB_DEFAULT_SMI_PORT 0xb2
85 #define PIIXPCIB_DEFAULT_SMI_DATA 0xb3
86
87 #define PIIXPCIB_GETSTATE 1
88 #define PIIXPCIB_SETSTATE 2
89 #define PIIXPCIB_SPEEDSTEP_HIGH 0
90 #define PIIXPCIB_SPEEDSTEP_LOW 1
91
92 struct piixpcib_softc {
93 struct device sc_dev;
94
95 int sc_sig;
96 int sc_smi_port;
97 int sc_smi_data;
98 int sc_command;
99 int sc_flags;
100
101 int state;
102 };
103
104 int piixpcib_match(struct device *, void *, void *);
105 void piixpcib_attach(struct device *, struct device *, void *);
106
107 void piixpcib_setperf(int);
108 int piixpcib_cpuspeed(int *);
109
110 int piixpcib_set_ownership(struct piixpcib_softc *);
111 int piixpcib_configure_speedstep(struct piixpcib_softc *);
112 int piixpcib_getset_state(struct piixpcib_softc *, int *, int);
113 void piixpcib_int15_gsic_call(struct piixpcib_softc *);
114
115 /* arch/i386/pci/pcib.c */
116 extern void pcibattach(struct device *, struct device *, void *);
117
118 /* arch/i386/i386/machdep.c */
119 #if !defined(SMALL_KERNEL) && defined(I686_CPU)
120 extern void p3_update_cpuspeed(void);
121 #endif
122
123 struct cfattach piixpcib_ca = {
124 sizeof(struct piixpcib_softc),
125 piixpcib_match,
126 piixpcib_attach
127 };
128
129 struct cfdriver piixpcib_cd = {
130 NULL, "piixpcib", DV_DULL
131 };
132
133 struct piixpcib_softc *piixpcib_sc;
134 extern int setperf_prio;
135
136 const struct pci_matchid piixpcib_devices[] = {
137 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82371AB_ISA}, /* PIIX4 */
138 { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82440MX_PM}, /* PIIX4 in MX440 */
139 };
140
141 void
142 piixpcib_int15_gsic_call(struct piixpcib_softc *sc)
143 {
144 struct kvm86regs regs;
145 int cmd;
146
147 memset(®s, 0, sizeof(struct kvm86regs));
148 regs.eax = PIIXPCIB_IST_CALL;
149 regs.edx = PIIXPCIB_ISGE;
150 kvm86_simplecall(0x15, ®s);
151
152 if (regs.eax == PIIXPCIB_ISGE) {
153 sc->sc_sig = regs.eax;
154 sc->sc_smi_port = regs.ebx & 0xff;
155
156 cmd = (regs.ebx >> 16) & 0xff;
157 /* GSIC may return cmd 0x80, should be PIIXPCIB_GSIC_CMD */
158 if (cmd == 0x80)
159 cmd = PIIXPCIB_GSIC_CMD;
160 sc->sc_command = (sc->sc_sig & 0xffffff00) | (cmd & 0xff);
161
162 sc->sc_smi_data = regs.ecx;
163 sc->sc_flags = regs.edx;
164 }
165 }
166
167 int
168 piixpcib_set_ownership(struct piixpcib_softc *sc)
169 {
170 int rv;
171 paddr_t pmagic;
172 char magic[] = "Copyright (c) 1999 Intel Corporation";
173
174 pmap_extract(pmap_kernel(), (vaddr_t)magic, &pmagic);
175
176 __asm __volatile(
177 "movl $0, %%edi\n\t"
178 "out %%al, (%%dx)\n"
179 : "=D" (rv)
180 : "a" (sc->sc_command),
181 "b" (0),
182 "c" (0),
183 "d" (sc->sc_smi_port),
184 "S" (pmagic)
185 );
186
187 return (rv ? ENXIO : 0);
188 }
189
190 int
191 piixpcib_configure_speedstep(struct piixpcib_softc *sc)
192 {
193 int rv;
194
195 sc->sc_sig = -1;
196
197 /* setup some defaults */
198 sc->sc_smi_port = PIIXPCIB_DEFAULT_SMI_PORT;
199 sc->sc_smi_data = PIIXPCIB_DEFAULT_SMI_DATA;
200 sc->sc_command = PIIXPCIB_DEFAULT_COMMAND;
201 sc->sc_flags = 0;
202
203 piixpcib_int15_gsic_call(sc);
204
205 /* If signature doesn't match, bail out */
206 if (sc->sc_sig != PIIXPCIB_ISGE)
207 return ENODEV;
208
209 if (piixpcib_set_ownership(sc) != 0) {
210 printf(": unable to claim ownership from BIOS, "
211 "SpeedStep disabled");
212 return ENXIO;
213 }
214
215 rv = piixpcib_getset_state(sc, &sc->state, PIIXPCIB_GETSTATE);
216 if (rv != 0) {
217 printf(": cannot determine CPU power state, "
218 "SpeedStep disabled");
219 return ENXIO;
220 }
221
222 /* save the sc for IO tag/handle */
223 piixpcib_sc = sc;
224
225 return 0;
226 }
227
228 int
229 piixpcib_match(struct device *parent, void *match, void *aux)
230 {
231 if (pci_matchbyid((struct pci_attach_args *)aux, piixpcib_devices,
232 sizeof(piixpcib_devices) / sizeof(piixpcib_devices[0])))
233 return (2); /* supersede pcib(4) */
234 return (0);
235 }
236
237 void
238 piixpcib_attach(struct device *parent, struct device *self, void *aux)
239 {
240 struct piixpcib_softc *sc = (struct piixpcib_softc *)self;
241
242 if (setperf_prio < 2) {
243 /* Set up SpeedStep. */
244 if (piixpcib_configure_speedstep(sc) == 0) {
245 printf(": SpeedStep");
246
247 /* Hook into hw.setperf sysctl */
248 cpu_setperf = piixpcib_setperf;
249 setperf_prio = 2;
250 }
251 }
252
253 /* Provide core pcib(4) functionality */
254 pcibattach(parent, self, aux);
255 }
256
257 int
258 piixpcib_getset_state(struct piixpcib_softc *sc, int *state, int function)
259 {
260 int new_state;
261 int rv;
262 int eax;
263
264 #ifdef DIAGNOSTIC
265 if (function != PIIXPCIB_GETSTATE &&
266 function != PIIXPCIB_SETSTATE) {
267 printf("%s: %s called with invalid function %d\n",
268 sc->sc_dev.dv_xname, __func__, function);
269 return EINVAL;
270 }
271 #endif
272
273 __asm __volatile(
274 "movl $0, %%edi\n\t"
275 "out %%al, (%%dx)\n"
276 : "=a" (eax),
277 "=b" (new_state),
278 "=D" (rv)
279 : "a" (sc->sc_command),
280 "b" (function),
281 "c" (*state),
282 "d" (sc->sc_smi_port),
283 "S" (0)
284 );
285
286 *state = new_state & 1;
287
288 switch (function) {
289 case PIIXPCIB_GETSTATE:
290 if (eax)
291 return ENXIO;
292 break;
293 case PIIXPCIB_SETSTATE:
294 if (rv)
295 return ENXIO;
296 break;
297 }
298
299 return 0;
300 }
301
302 void
303 piixpcib_setperf(int level)
304 {
305 struct piixpcib_softc *sc;
306 int new_state;
307 int tries, rv, s;
308
309 sc = piixpcib_sc;
310
311 #ifdef DIAGNOSTIC
312 if (sc == NULL) {
313 printf("%s: no cookie", __func__);
314 return;
315 }
316 #endif
317
318 /* Only two states are available */
319 if (level <= 50)
320 new_state = PIIXPCIB_SPEEDSTEP_LOW;
321 else
322 new_state = PIIXPCIB_SPEEDSTEP_HIGH;
323
324 if (sc->state == new_state)
325 return;
326
327 tries = 5;
328 s = splhigh();
329
330 do {
331 rv = piixpcib_getset_state(sc, &new_state,
332 PIIXPCIB_SETSTATE);
333 if (rv)
334 delay(200);
335 } while (rv && --tries);
336
337 splx(s);
338
339 #ifdef DIAGNOSTIC
340 if (rv)
341 printf("%s: setting CPU power state failed",
342 sc->sc_dev.dv_xname);
343 #endif
344
345 sc->state = new_state;
346
347 /* Force update of hw.cpuspeed.
348 *
349 * XXX: First generation SpeedStep is only present in some
350 * Pentium III CPUs and we are lacking a reliable method to
351 * determine CPU freqs corresponding to low and high power state.
352 *
353 * And yes, I've tried it the way the Linux speedstep-smi
354 * driver does it, thank you very much. It doesn't work
355 * half the time (my machine has more than 4Mhz ;-) and
356 * even crashes some machines without specific workarounds.
357 *
358 * So call p3_update_cpuspeed() from arch/i386/i386/machdep.c
359 * instead. Works fine on my Thinkpad X21.
360 *
361 * BUT: Apparently, if the bus is busy, the transition may be
362 * delayed and retried under control of evil SMIs we cannot
363 * control. So busy-wait a short while before updating hw.cpuspeed
364 * to decrease chances of picking up the old CPU speed.
365 * There seems to be no reliable fix for this.
366 */
367 delay(200);
368 #if !defined(SMALL_KERNEL) && defined(I686_CPU)
369 p3_update_cpuspeed();
370 #endif
371 }