1 /* $NetBSD: piixpcib.c,v 1.16 2008/07/20 16:52:33 martin Exp $ */
4 * Copyright (c) 2004, 2006 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Minoura Makoto, Matthew R. Green, and Jared D. McNeill.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
33 * Intel PIIX4 PCI-ISA bridge device driver with CPU frequency scaling support
35 * Based on the FreeBSD 'smist' cpufreq driver by Bruno Ducrot
38 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: piixpcib.c,v 1.16 2008/07/20 16:52:33 martin Exp $");
41 #include <sys/types.h>
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/device.h>
45 #include <sys/sysctl.h>
46 #include <machine/bus.h>
48 #include <machine/frame.h>
49 #include <machine/bioscall.h>
51 #include <dev/pci/pcivar.h>
52 #include <dev/pci/pcireg.h>
53 #include <dev/pci/pcidevs.h>
55 #include <i386/pci/piixreg.h>
56 #include <x86/pci/pcibvar.h>
58 #define PIIX4_PIRQRC 0x60
60 struct piixpcib_softc
{
61 /* we call pcibattach() which assumes our softc starts like this: */
63 struct pcib_softc sc_pcib
;
72 bus_space_tag_t sc_iot
;
73 bus_space_handle_t sc_ioh
;
79 static int piixpcibmatch(device_t
, cfdata_t
, void *);
80 static void piixpcibattach(device_t
, device_t
, void *);
82 static bool piixpcib_suspend(device_t
, pmf_qual_t
);
83 static bool piixpcib_resume(device_t
, pmf_qual_t
);
85 static void speedstep_configure(struct piixpcib_softc
*,
86 struct pci_attach_args
*);
87 static int speedstep_sysctl_helper(SYSCTLFN_ARGS
);
89 static struct piixpcib_softc
*speedstep_cookie
; /* XXX */
91 CFATTACH_DECL_NEW(piixpcib
, sizeof(struct piixpcib_softc
),
92 piixpcibmatch
, piixpcibattach
, NULL
, NULL
);
98 piixpcibmatch(device_t parent
, cfdata_t match
, void *aux
)
100 struct pci_attach_args
*pa
= aux
;
102 /* We are ISA bridge, of course */
103 if (PCI_CLASS(pa
->pa_class
) != PCI_CLASS_BRIDGE
||
104 (PCI_SUBCLASS(pa
->pa_class
) != PCI_SUBCLASS_BRIDGE_ISA
&&
105 PCI_SUBCLASS(pa
->pa_class
) != PCI_SUBCLASS_BRIDGE_MISC
)) {
109 /* Matches only Intel PIIX4 */
110 if (PCI_VENDOR(pa
->pa_id
) == PCI_VENDOR_INTEL
) {
111 switch (PCI_PRODUCT(pa
->pa_id
)) {
112 case PCI_PRODUCT_INTEL_82371AB_ISA
: /* PIIX4 */
113 case PCI_PRODUCT_INTEL_82440MX_PMC
: /* PIIX4 in MX440 */
122 piixpcibattach(device_t parent
, device_t self
, void *aux
)
124 struct pci_attach_args
*pa
= aux
;
125 struct piixpcib_softc
*sc
= device_private(self
);
128 sc
->sc_iot
= pa
->pa_iot
;
130 pcibattach(parent
, self
, aux
);
132 /* Set up SpeedStep. */
133 speedstep_configure(sc
, pa
);
135 /* Map edge/level control registers */
136 if (bus_space_map(sc
->sc_iot
, PIIX_REG_ELCR
, PIIX_REG_ELCR_SIZE
, 0,
138 aprint_error_dev(self
, "can't map edge/level control registers\n");
142 if (!pmf_device_register(self
, piixpcib_suspend
, piixpcib_resume
))
143 aprint_error_dev(self
, "couldn't establish power handler\n");
147 piixpcib_suspend(device_t dv
, pmf_qual_t qual
)
149 struct piixpcib_softc
*sc
= device_private(dv
);
151 /* capture PIRQX route control registers */
152 sc
->sc_pirqrc
= pci_conf_read(sc
->sc_pcib
.sc_pc
, sc
->sc_pcib
.sc_tag
,
155 /* capture edge/level control registers */
156 sc
->sc_elcr
[0] = bus_space_read_1(sc
->sc_iot
, sc
->sc_ioh
, 0);
157 sc
->sc_elcr
[1] = bus_space_read_1(sc
->sc_iot
, sc
->sc_ioh
, 1);
163 piixpcib_resume(device_t dv
, pmf_qual_t qual
)
165 struct piixpcib_softc
*sc
= device_private(dv
);
167 /* restore PIRQX route control registers */
168 pci_conf_write(sc
->sc_pcib
.sc_pc
, sc
->sc_pcib
.sc_tag
, PIIX4_PIRQRC
,
171 /* restore edge/level control registers */
172 bus_space_write_1(sc
->sc_iot
, sc
->sc_ioh
, 0, sc
->sc_elcr
[0]);
173 bus_space_write_1(sc
->sc_iot
, sc
->sc_ioh
, 1, sc
->sc_elcr
[1]);
179 * Intel PIIX4 (SMI) SpeedStep support.
182 #define PIIXPCIB_GSIC 0x47534943
183 #define PIIXPCIB_GETOWNER 0
184 #define PIIXPCIB_GETSTATE 1
185 #define PIIXPCIB_SETSTATE 2
186 #define PIIXPCIB_GETFREQS 4
188 #define PIIXPCIB_SPEEDSTEP_HIGH 0
189 #define PIIXPCIB_SPEEDSTEP_LOW 1
192 piixpcib_int15_gsic_call(int *sig
, int *smicmd
, int *cmd
, int *smidata
, int *flags
)
194 struct bioscallregs regs
;
196 memset(®s
, 0, sizeof(struct bioscallregs
));
197 regs
.EAX
= 0x0000e980; /* IST support */
198 regs
.EDX
= PIIXPCIB_GSIC
;
199 bioscall(0x15, ®s
);
201 if (regs
.EAX
== PIIXPCIB_GSIC
) {
203 *smicmd
= regs
.EBX
& 0xff;
204 *cmd
= (regs
.EBX
>> 16) & 0xff;
208 *sig
= *smicmd
= *cmd
= *smidata
= *flags
= -1;
214 piixpcib_set_ownership(struct piixpcib_softc
*sc
)
218 static char magic
[] = "Copyright (c) 1999 Intel Corporation";
220 pmagic
= vtophys((vaddr_t
)magic
);
222 __asm__
__volatile__(
226 : "a" (sc
->sc_command
),
229 "d" (sc
->sc_smi_cmd
),
233 return (rv
? ENXIO
: 0);
237 piixpcib_getset_state(struct piixpcib_softc
*sc
, int *state
, int function
)
244 if (function
!= PIIXPCIB_GETSTATE
&&
245 function
!= PIIXPCIB_SETSTATE
) {
246 aprint_error_dev(sc
->sc_dev
, "GSI called with invalid function %d\n",
252 __asm__
__volatile__(
258 : "a" (sc
->sc_command
),
261 "d" (sc
->sc_smi_cmd
),
268 case PIIXPCIB_GETSTATE
:
272 case PIIXPCIB_SETSTATE
:
282 piixpcib_get(struct piixpcib_softc
*sc
)
287 state
= 0; /* XXX gcc */
289 rv
= piixpcib_getset_state(sc
, &state
, PIIXPCIB_GETSTATE
);
297 piixpcib_set(struct piixpcib_softc
*sc
, int state
)
302 if (state
!= PIIXPCIB_SPEEDSTEP_HIGH
&&
303 state
!= PIIXPCIB_SPEEDSTEP_LOW
)
305 if (piixpcib_get(sc
) == state
)
313 rv
= piixpcib_getset_state(sc
, &state
, PIIXPCIB_SETSTATE
);
316 } while (rv
&& --try);
324 speedstep_configure(struct piixpcib_softc
*sc
,
325 struct pci_attach_args
*pa
)
327 const struct sysctlnode
*node
, *ssnode
;
328 int sig
, smicmd
, cmd
, smidata
, flags
;
331 piixpcib_int15_gsic_call(&sig
, &smicmd
, &cmd
, &smidata
, &flags
);
334 sc
->sc_smi_cmd
= smicmd
;
335 sc
->sc_smi_data
= smidata
;
337 aprint_debug_dev(sc
->sc_dev
, "GSIC returned cmd 0x80, should be 0x82\n");
340 sc
->sc_command
= (sig
& 0xffffff00) | (cmd
& 0xff);
341 sc
->sc_flags
= flags
;
343 /* setup some defaults */
344 sc
->sc_smi_cmd
= 0xb2;
345 sc
->sc_smi_data
= 0xb3;
346 sc
->sc_command
= 0x47534982;
350 if (piixpcib_set_ownership(sc
) != 0) {
351 aprint_error_dev(sc
->sc_dev
, "unable to claim ownership from the BIOS\n");
352 return; /* If we can't claim ownership from the BIOS, bail */
355 /* Put in machdep.speedstep_state (0 for low, 1 for high). */
356 if ((rv
= sysctl_createv(NULL
, 0, NULL
, &node
,
357 CTLFLAG_PERMANENT
, CTLTYPE_NODE
, "machdep", NULL
,
358 NULL
, 0, NULL
, 0, CTL_MACHDEP
, CTL_EOL
)) != 0)
361 /* CTLFLAG_ANYWRITE? kernel option like EST? */
362 if ((rv
= sysctl_createv(NULL
, 0, &node
, &ssnode
,
363 CTLFLAG_READWRITE
, CTLTYPE_INT
, "speedstep_state", NULL
,
364 speedstep_sysctl_helper
, 0, NULL
, 0, CTL_CREATE
,
368 /* XXX save the sc for IO tag/handle */
369 speedstep_cookie
= sc
;
371 aprint_verbose_dev(sc
->sc_dev
, "SpeedStep SMI enabled\n");
375 aprint_normal("%s: sysctl_createv failed (rv = %d)\n", __func__
, rv
);
379 * get/set the SpeedStep state: 0 == low power, 1 == high power.
382 speedstep_sysctl_helper(SYSCTLFN_ARGS
)
384 struct sysctlnode node
;
385 struct piixpcib_softc
*sc
;
386 uint8_t state
, state2
;
387 int ostate
, nstate
, error
;
389 sc
= speedstep_cookie
;
392 state
= piixpcib_get(sc
);
393 if (state
== PIIXPCIB_SPEEDSTEP_HIGH
)
400 node
.sysctl_data
= &nstate
;
402 error
= sysctl_lookup(SYSCTLFN_CALL(&node
));
403 if (error
|| newp
== NULL
)
406 /* Only two states are available */
407 if (nstate
!= 0 && nstate
!= 1) {
412 state2
= piixpcib_get(sc
);
413 if (state2
== PIIXPCIB_SPEEDSTEP_HIGH
)
418 if (ostate
!= nstate
)
421 state2
= PIIXPCIB_SPEEDSTEP_LOW
;
423 state2
= PIIXPCIB_SPEEDSTEP_HIGH
;
425 error
= piixpcib_set(sc
, state2
);