1 /* $NetBSD: amdpm.c,v 1.32 2009/05/06 10:34:32 cegger Exp $ */
4 * Copyright (c) 2002 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
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.
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: amdpm.c,v 1.32 2009/05/06 10:34:32 cegger Exp $");
35 #include "opt_amdpm.h"
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
40 #include <sys/device.h>
41 #include <sys/callout.h>
45 #include <dev/ic/acpipmtimer.h>
47 #include <dev/i2c/i2cvar.h>
49 #include <dev/pci/pcivar.h>
50 #include <dev/pci/pcireg.h>
51 #include <dev/pci/pcidevs.h>
53 #include <dev/pci/amdpmreg.h>
54 #include <dev/pci/amdpmvar.h>
55 #include <dev/pci/amdpm_smbusreg.h>
57 static void amdpm_rnd_callout(void *);
59 #ifdef AMDPM_RND_COUNTERS
60 #define AMDPM_RNDCNT_INCR(ev) (ev)->ev_count++
64 amdpm_match(device_t parent
, cfdata_t match
, void *aux
)
66 struct pci_attach_args
*pa
= aux
;
68 if (PCI_VENDOR(pa
->pa_id
) == PCI_VENDOR_AMD
) {
69 switch (PCI_PRODUCT(pa
->pa_id
)) {
70 case PCI_PRODUCT_AMD_PBC768_PMC
:
71 case PCI_PRODUCT_AMD_PBC8111_ACPI
:
75 if (PCI_VENDOR(pa
->pa_id
) == PCI_VENDOR_NVIDIA
) {
76 switch (PCI_PRODUCT(pa
->pa_id
)) {
77 case PCI_PRODUCT_NVIDIA_XBOX_SMBUS
:
86 amdpm_attach(device_t parent
, device_t self
, void *aux
)
88 struct amdpm_softc
*sc
= device_private(self
);
89 struct pci_attach_args
*pa
= aux
;
91 pcireg_t confreg
, pmptrreg
;
96 pci_devinfo(pa
->pa_id
, pa
->pa_class
, 0, devinfo
, sizeof(devinfo
));
97 aprint_normal(": %s (rev. 0x%02x)\n", devinfo
,
98 PCI_REVISION(pa
->pa_class
));
100 if (PCI_PRODUCT(pa
->pa_id
) == PCI_PRODUCT_NVIDIA_XBOX_SMBUS
)
105 sc
->sc_pc
= pa
->pa_pc
;
106 sc
->sc_tag
= pa
->pa_tag
;
107 sc
->sc_iot
= pa
->pa_iot
;
111 aprint_normal_dev(&sc
->sc_dev
, "");
112 pci_conf_print(pa
->pa_pc
, pa
->pa_tag
, NULL
);
115 confreg
= pci_conf_read(pa
->pa_pc
, pa
->pa_tag
, AMDPM_CONFREG
);
116 /* enable pm i/o space for AMD-8111 and nForce */
117 if (PCI_PRODUCT(pa
->pa_id
) == PCI_PRODUCT_AMD_PBC8111_ACPI
||
119 confreg
|= AMDPM_PMIOEN
;
121 /* Enable random number generation for everyone */
122 pci_conf_write(pa
->pa_pc
, pa
->pa_tag
, AMDPM_CONFREG
,
123 confreg
| AMDPM_RNGEN
);
124 confreg
= pci_conf_read(pa
->pa_pc
, pa
->pa_tag
, AMDPM_CONFREG
);
126 if ((confreg
& AMDPM_PMIOEN
) == 0) {
127 aprint_error_dev(&sc
->sc_dev
, "PMxx space isn't enabled\n");
132 pmptrreg
= pci_conf_read(pa
->pa_pc
, pa
->pa_tag
, NFORCE_PMPTR
);
133 aprint_normal_dev(&sc
->sc_dev
, "power management at 0x%04x\n",
134 NFORCE_PMBASE(pmptrreg
));
135 if (bus_space_map(sc
->sc_iot
, NFORCE_PMBASE(pmptrreg
),
136 AMDPM_PMSIZE
, 0, &sc
->sc_ioh
)) {
137 aprint_error_dev(&sc
->sc_dev
, "failed to map PMxx space\n");
141 pmptrreg
= pci_conf_read(pa
->pa_pc
, pa
->pa_tag
, AMDPM_PMPTR
);
142 if (bus_space_map(sc
->sc_iot
, AMDPM_PMBASE(pmptrreg
),
143 AMDPM_PMSIZE
, 0, &sc
->sc_ioh
)) {
144 aprint_error_dev(&sc
->sc_dev
, "failed to map PMxx space\n");
149 /* don't attach a timecounter on nforce boards */
150 if ((confreg
& AMDPM_TMRRST
) == 0 && (confreg
& AMDPM_STOPTMR
) == 0 &&
152 acpipmtimer_attach(&sc
->sc_dev
, sc
->sc_iot
, sc
->sc_ioh
,
153 AMDPM_TMR
, ((confreg
& AMDPM_TMR32
) ? ACPIPMT_32BIT
: 0));
156 /* try to attach devices on the smbus */
157 if (PCI_PRODUCT(pa
->pa_id
) == PCI_PRODUCT_AMD_PBC8111_ACPI
||
159 amdpm_smbus_attach(sc
);
162 if (confreg
& AMDPM_RNGEN
) {
163 /* Check to see if we can read data from the RNG. */
164 (void) bus_space_read_4(sc
->sc_iot
, sc
->sc_ioh
,
166 for (i
= 0; i
< 1000; i
++) {
167 pmreg
= bus_space_read_4(sc
->sc_iot
,
168 sc
->sc_ioh
, AMDPM_RNGSTAT
);
169 if (pmreg
& AMDPM_RNGDONE
)
173 if ((pmreg
& AMDPM_RNGDONE
) != 0) {
174 aprint_normal_dev(&sc
->sc_dev
, ""
175 "random number generator enabled (apprx. %dms)\n",
177 callout_init(&sc
->sc_rnd_ch
, 0);
178 rnd_attach_source(&sc
->sc_rnd_source
,
179 device_xname(&sc
->sc_dev
), RND_TYPE_RNG
,
181 * XXX Careful! The use of RND_FLAG_NO_ESTIMATE
182 * XXX here is unobvious: we later feed raw bits
183 * XXX into the "entropy pool" with rnd_add_data,
184 * XXX explicitly supplying an entropy estimate.
185 * XXX In this context, NO_ESTIMATE serves only
186 * XXX to prevent rnd_add_data from trying to
187 * XXX use the *time at which we added the data*
188 * XXX as entropy, which is not a good idea since
189 * XXX we add data periodically from a callout.
191 RND_FLAG_NO_ESTIMATE
);
192 #ifdef AMDPM_RND_COUNTERS
193 evcnt_attach_dynamic(&sc
->sc_rnd_hits
, EVCNT_TYPE_MISC
,
194 NULL
, device_xname(&sc
->sc_dev
), "rnd hits");
195 evcnt_attach_dynamic(&sc
->sc_rnd_miss
, EVCNT_TYPE_MISC
,
196 NULL
, device_xname(&sc
->sc_dev
), "rnd miss");
197 for (i
= 0; i
< 256; i
++) {
198 evcnt_attach_dynamic(&sc
->sc_rnd_data
[i
],
199 EVCNT_TYPE_MISC
, NULL
, device_xname(&sc
->sc_dev
),
203 amdpm_rnd_callout(sc
);
208 CFATTACH_DECL(amdpm
, sizeof(struct amdpm_softc
),
209 amdpm_match
, amdpm_attach
, NULL
, NULL
);
212 amdpm_rnd_callout(void *v
)
214 struct amdpm_softc
*sc
= v
;
216 #ifdef AMDPM_RND_COUNTERS
220 if ((bus_space_read_4(sc
->sc_iot
, sc
->sc_ioh
, AMDPM_RNGSTAT
) &
221 AMDPM_RNGDONE
) != 0) {
222 rngreg
= bus_space_read_4(sc
->sc_iot
, sc
->sc_ioh
,
224 rnd_add_data(&sc
->sc_rnd_source
, &rngreg
,
225 sizeof(rngreg
), sizeof(rngreg
) * NBBY
);
226 #ifdef AMDPM_RND_COUNTERS
227 AMDPM_RNDCNT_INCR(&sc
->sc_rnd_hits
);
228 for (i
= 0; i
< sizeof(rngreg
); i
++, rngreg
>>= NBBY
)
229 AMDPM_RNDCNT_INCR(&sc
->sc_rnd_data
[rngreg
& 0xff]);
232 #ifdef AMDPM_RND_COUNTERS
234 AMDPM_RNDCNT_INCR(&sc
->sc_rnd_miss
);
236 callout_reset(&sc
->sc_rnd_ch
, 1, amdpm_rnd_callout
, sc
);