piixpcib.c revision 1.6 1 /* $NetBSD: piixpcib.c,v 1.6 2006/06/19 10:08:16 jmcneill Exp $ */
2
3 /*-
4 * Copyright (c) 2004, 2006 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Minoura Makoto, Matthew R. Green, and Jared D. McNeill.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /*
40 * Intel PIIX4 PCI-ISA bridge device driver with CPU frequency scaling support
41 *
42 * Based on the FreeBSD 'smist' cpufreq driver by Bruno Ducrot
43 */
44
45 #include <sys/cdefs.h>
46 __KERNEL_RCSID(0, "$NetBSD: piixpcib.c,v 1.6 2006/06/19 10:08:16 jmcneill Exp $");
47
48 #include <sys/types.h>
49 #include <sys/param.h>
50 #include <sys/systm.h>
51 #include <sys/device.h>
52 #include <sys/sysctl.h>
53 #include <machine/bus.h>
54
55 #include <machine/frame.h>
56 #include <machine/bioscall.h>
57
58 #include <dev/pci/pcivar.h>
59 #include <dev/pci/pcireg.h>
60 #include <dev/pci/pcidevs.h>
61
62 #define PIIX4_PIRQRCA 0x60
63 #define PIIX4_PIRQRCB 0x61
64 #define PIIX4_PIRQRCC 0x62
65 #define PIIX4_PIRQRCD 0x63
66
67 struct piixpcib_softc {
68 struct device sc_dev;
69
70 pci_chipset_tag_t sc_pc;
71 pcitag_t sc_pcitag;
72
73 int sc_smi_cmd;
74 int sc_smi_data;
75 int sc_command;
76 int sc_flags;
77
78 void *sc_powerhook;
79 struct pci_conf_state sc_pciconf;
80
81 pcireg_t sc_pirqrc[4];
82 };
83
84 static int piixpcibmatch(struct device *, struct cfdata *, void *);
85 static void piixpcibattach(struct device *, struct device *, void *);
86
87 static void piixpcib_powerhook(int, void *);
88
89 static void speedstep_configure(struct piixpcib_softc *,
90 struct pci_attach_args *);
91 static int speedstep_sysctl_helper(SYSCTLFN_ARGS);
92
93 struct piixpcib_softc *speedstep_cookie; /* XXX */
94
95 /* Defined in arch/i386/pci/pcib.c. */
96 extern void pcibattach(struct device *, struct device *, void *);
97
98 CFATTACH_DECL(piixpcib, sizeof(struct piixpcib_softc),
99 piixpcibmatch, piixpcibattach, NULL, NULL);
100
101 /*
102 * Autoconf callbacks.
103 */
104 static int
105 piixpcibmatch(struct device *parent, struct cfdata *match, void *aux)
106 {
107 struct pci_attach_args *pa;
108
109 pa = (struct pci_attach_args *)aux;
110
111 /* We are ISA bridge, of course */
112 if (PCI_CLASS(pa->pa_class) != PCI_CLASS_BRIDGE ||
113 (PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_BRIDGE_ISA &&
114 PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_BRIDGE_MISC)) {
115 return 0;
116 }
117
118 /* Matches only Intel PIIX4 */
119 if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_INTEL) {
120 switch (PCI_PRODUCT(pa->pa_id)) {
121 case PCI_PRODUCT_INTEL_82371AB_ISA: /* PIIX4 */
122 case PCI_PRODUCT_INTEL_82440MX_PMC: /* PIIX4 in MX440 */
123 return 10;
124 }
125 }
126
127 return 0;
128 }
129
130 static void
131 piixpcibattach(struct device *parent, struct device *self, void *aux)
132 {
133 struct pci_attach_args *pa;
134 struct piixpcib_softc *sc;
135
136 pa = (struct pci_attach_args *)aux;
137 sc = (struct piixpcib_softc *)self;
138
139 sc->sc_pc = pa->pa_pc;
140 sc->sc_pcitag = pa->pa_tag;
141
142 pcibattach(parent, self, aux);
143
144 /* Set up SpeedStep. */
145 speedstep_configure(sc, pa);
146
147 sc->sc_powerhook = powerhook_establish(piixpcib_powerhook, sc);
148 if (sc->sc_powerhook == NULL)
149 aprint_error("%s: can't establish powerhook\n",
150 sc->sc_dev.dv_xname);
151
152 return;
153 }
154
155 static void
156 piixpcib_powerhook(int why, void *opaque)
157 {
158 struct piixpcib_softc *sc;
159 pci_chipset_tag_t pc;
160 pcitag_t tag;
161
162 sc = (struct piixpcib_softc *)opaque;
163 pc = sc->sc_pc;
164 tag = sc->sc_pcitag;
165
166 switch (why) {
167 case PWR_SUSPEND:
168 pci_conf_capture(pc, tag, &sc->sc_pciconf);
169
170 /* capture PIRQX route control registers */
171 sc->sc_pirqrc[0] = pci_conf_read(pc, tag, PIIX4_PIRQRCA);
172 sc->sc_pirqrc[1] = pci_conf_read(pc, tag, PIIX4_PIRQRCB);
173 sc->sc_pirqrc[2] = pci_conf_read(pc, tag, PIIX4_PIRQRCC);
174 sc->sc_pirqrc[3] = pci_conf_read(pc, tag, PIIX4_PIRQRCD);
175 break;
176 case PWR_RESUME:
177 pci_conf_restore(pc, tag, &sc->sc_pciconf);
178
179 /* restore PIRQX route control registers */
180 pci_conf_write(pc, tag, PIIX4_PIRQRCA, sc->sc_pirqrc[0]);
181 pci_conf_write(pc, tag, PIIX4_PIRQRCB, sc->sc_pirqrc[1]);
182 pci_conf_write(pc, tag, PIIX4_PIRQRCC, sc->sc_pirqrc[2]);
183 pci_conf_write(pc, tag, PIIX4_PIRQRCD, sc->sc_pirqrc[3]);
184 break;
185 }
186
187 return;
188 }
189
190 /*
191 * Intel PIIX4 (SMI) SpeedStep support.
192 */
193
194 #define PIIXPCIB_GSIC 0x47534943
195 #define PIIXPCIB_GETOWNER 0
196 #define PIIXPCIB_GETSTATE 1
197 #define PIIXPCIB_SETSTATE 2
198 #define PIIXPCIB_GETFREQS 4
199
200 #define PIIXPCIB_SPEEDSTEP_HIGH 0
201 #define PIIXPCIB_SPEEDSTEP_LOW 1
202
203 static void
204 piixpcib_int15_gsic_call(int *sig, int *smicmd, int *cmd, int *smidata, int *flags)
205 {
206 struct bioscallregs regs;
207
208 memset(®s, 0, sizeof(struct bioscallregs));
209 regs.EAX = 0x0000e980; /* IST support */
210 regs.EDX = PIIXPCIB_GSIC;
211 bioscall(0x15, ®s);
212
213 if (regs.EAX == PIIXPCIB_GSIC) {
214 *sig = regs.EAX;
215 *smicmd = regs.EBX & 0xff;
216 *cmd = (regs.EBX >> 16) & 0xff;
217 *smidata = regs.ECX;
218 *flags = regs.EDX;
219 } else
220 *sig = *smicmd = *cmd = *smidata = *flags = -1;
221
222 return;
223 }
224
225 static int
226 piixpcib_set_ownership(struct piixpcib_softc *sc)
227 {
228 int rv;
229 paddr_t pmagic;
230 static char magic[] = "Copyright (c) 1999 Intel Corporation";
231
232 pmagic = vtophys((vaddr_t)magic);
233
234 __asm__ __volatile__(
235 "movl $0, %%edi\n\t"
236 "out %%al, (%%dx)\n"
237 : "=D" (rv)
238 : "a" (sc->sc_command),
239 "b" (0),
240 "c" (0),
241 "d" (sc->sc_smi_cmd),
242 "S" (pmagic)
243 );
244
245 return (rv ? ENXIO : 0);
246 }
247
248 static int
249 piixpcib_getset_state(struct piixpcib_softc *sc, int *state, int function)
250 {
251 int new;
252 int rv;
253 int eax;
254
255 #ifdef DIAGNOSTIC
256 if (function != PIIXPCIB_GETSTATE &&
257 function != PIIXPCIB_SETSTATE) {
258 aprint_error("%s: GSI called with invalid function %d\n",
259 sc->sc_dev.dv_xname, function);
260 return EINVAL;
261 }
262 #endif
263
264 __asm__ __volatile__(
265 "movl $0, %%edi\n\t"
266 "out %%al, (%%dx)\n"
267 : "=a" (eax),
268 "=b" (new),
269 "=D" (rv)
270 : "a" (sc->sc_command),
271 "b" (function),
272 "c" (*state),
273 "d" (sc->sc_smi_cmd),
274 "S" (0)
275 );
276
277 *state = new & 1;
278
279 switch (function) {
280 case PIIXPCIB_GETSTATE:
281 if (eax)
282 return ENXIO;
283 break;
284 case PIIXPCIB_SETSTATE:
285 if (rv)
286 return ENXIO;
287 break;
288 }
289
290 return 0;
291 }
292
293 static int
294 piixpcib_get(struct piixpcib_softc *sc)
295 {
296 int rv;
297 int state;
298
299 state = 0; /* XXX gcc */
300
301 rv = piixpcib_getset_state(sc, &state, PIIXPCIB_GETSTATE);
302 if (rv)
303 return rv;
304
305 return state & 1;
306 }
307
308 static int
309 piixpcib_set(struct piixpcib_softc *sc, int state)
310 {
311 int rv, s;
312 int try;
313
314 if (state != PIIXPCIB_SPEEDSTEP_HIGH &&
315 state != PIIXPCIB_SPEEDSTEP_LOW)
316 return ENXIO;
317 if (piixpcib_get(sc) == state)
318 return 0;
319
320 try = 5;
321
322 s = splhigh();
323
324 do {
325 rv = piixpcib_getset_state(sc, &state, PIIXPCIB_SETSTATE);
326 if (rv)
327 delay(200);
328 } while (rv && --try);
329
330 splx(s);
331
332 return rv;
333 }
334
335 static void
336 speedstep_configure(struct piixpcib_softc *sc, struct pci_attach_args *pa)
337 {
338 const struct sysctlnode *node, *ssnode;
339 int sig, smicmd, cmd, smidata, flags;
340 int rv;
341
342 piixpcib_int15_gsic_call(&sig, &smicmd, &cmd, &smidata, &flags);
343
344 if (sig != -1) {
345 sc->sc_smi_cmd = smicmd;
346 sc->sc_smi_data = smidata;
347 if (cmd == 0x80) {
348 aprint_debug("%s: GSIC returned cmd 0x80, should be 0x82\n",
349 sc->sc_dev.dv_xname);
350 cmd = 0x82;
351 }
352 sc->sc_command = (sig & 0xffffff00) | (cmd & 0xff);
353 sc->sc_flags = flags;
354 } else {
355 /* setup some defaults */
356 sc->sc_smi_cmd = 0xb2;
357 sc->sc_smi_data = 0xb3;
358 sc->sc_command = 0x47534982;
359 sc->sc_flags = 0;
360 }
361
362 if (piixpcib_set_ownership(sc) != 0) {
363 aprint_error("%s: unable to claim ownership from the BIOS\n",
364 sc->sc_dev.dv_xname);
365 return; /* If we can't claim ownership from the BIOS, bail */
366 }
367
368 /* Put in machdep.speedstep_state (0 for low, 1 for high). */
369 if ((rv = sysctl_createv(NULL, 0, NULL, &node,
370 CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
371 NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL)) != 0)
372 goto err;
373
374 /* CTLFLAG_ANYWRITE? kernel option like EST? */
375 if ((rv = sysctl_createv(NULL, 0, &node, &ssnode,
376 CTLFLAG_READWRITE, CTLTYPE_INT, "speedstep_state", NULL,
377 speedstep_sysctl_helper, 0, NULL, 0, CTL_CREATE,
378 CTL_EOL)) != 0)
379 goto err;
380
381 /* XXX save the sc for IO tag/handle */
382 speedstep_cookie = sc;
383
384 aprint_verbose("%s: SpeedStep SMI enabled\n", sc->sc_dev.dv_xname);
385
386 return;
387
388 err:
389 aprint_normal("%s: sysctl_createv failed (rv = %d)\n", __func__, rv);
390
391 return;
392 }
393
394 /*
395 * get/set the SpeedStep state: 0 == low power, 1 == high power.
396 */
397 static int
398 speedstep_sysctl_helper(SYSCTLFN_ARGS)
399 {
400 struct sysctlnode node;
401 struct piixpcib_softc *sc;
402 uint8_t state, state2;
403 int ostate, nstate, error;
404
405 sc = speedstep_cookie;
406 error = 0;
407
408 state = piixpcib_get(sc);
409 if (state == PIIXPCIB_SPEEDSTEP_HIGH)
410 ostate = 1;
411 else
412 ostate = 0;
413 nstate = ostate;
414
415 node = *rnode;
416 node.sysctl_data = &nstate;
417
418 error = sysctl_lookup(SYSCTLFN_CALL(&node));
419 if (error || newp == NULL)
420 goto out;
421
422 /* Only two states are available */
423 if (nstate != 0 && nstate != 1) {
424 error = EINVAL;
425 goto out;
426 }
427
428 state2 = piixpcib_get(sc);
429 if (state2 == PIIXPCIB_SPEEDSTEP_HIGH)
430 ostate = 1;
431 else
432 ostate = 0;
433
434 if (ostate != nstate)
435 {
436 if (nstate == 0)
437 state2 = PIIXPCIB_SPEEDSTEP_LOW;
438 else
439 state2 = PIIXPCIB_SPEEDSTEP_HIGH;
440
441 error = piixpcib_set(sc, state2);
442 }
443 out:
444 return (error);
445 }
446