amdpm.c revision 1.6
1/* $OpenBSD: amdpm.c,v 1.6 2006/01/02 04:01:43 brad Exp $ */ 2 3/*- 4 * Copyright (c) 2002 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Enami Tsugutomo. 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#include <sys/param.h> 40#include <sys/systm.h> 41#include <sys/kernel.h> 42#include <sys/device.h> 43#include <sys/timeout.h> 44#ifdef __HAVE_TIMECOUNTER 45#include <sys/timetc.h> 46#endif 47 48#include <dev/pci/pcivar.h> 49#include <dev/pci/pcireg.h> 50#include <dev/pci/pcidevs.h> 51 52#include <dev/rndvar.h> 53#include <dev/pci/amdpmreg.h> 54 55#ifdef __HAVE_TIMECOUNTER 56u_int amdpm_get_timecount(struct timecounter *tc); 57 58#ifndef AMDPM_FREQUENCY 59#define AMDPM_FREQUENCY 3579545 60#endif 61 62static struct timecounter amdpm_timecounter = { 63 amdpm_get_timecount, /* get_timecount */ 64 0, /* no poll_pps */ 65 0xffffff, /* counter_mask */ 66 AMDPM_FREQUENCY, /* frequency */ 67 "AMDPM", /* name */ 68 1000 /* quality */ 69}; 70#endif 71 72struct amdpm_softc { 73 struct device sc_dev; 74 75 pci_chipset_tag_t sc_pc; 76 pcitag_t sc_tag; 77 78 bus_space_tag_t sc_iot; 79 bus_space_handle_t sc_ioh; /* PMxx space */ 80 81 struct timeout sc_rnd_ch; 82#ifdef AMDPM_RND_COUNTERS 83 struct evcnt sc_rnd_hits; 84 struct evcnt sc_rnd_miss; 85 struct evcnt sc_rnd_data[256]; 86#endif 87}; 88 89int amdpm_match(struct device *, void *, void *); 90void amdpm_attach(struct device *, struct device *, void *); 91void amdpm_rnd_callout(void *); 92 93struct cfattach amdpm_ca = { 94 sizeof(struct amdpm_softc), amdpm_match, amdpm_attach 95}; 96 97struct cfdriver amdpm_cd = { 98 NULL, "amdpm", DV_DULL 99}; 100 101#ifdef AMDPM_RND_COUNTERS 102#define AMDPM_RNDCNT_INCR(ev) (ev)->ev_count++ 103#else 104#define AMDPM_RNDCNT_INCR(ev) /* nothing */ 105#endif 106 107const struct pci_matchid amdpm_ids[] = { 108 { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_766_PMC }, 109 { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_PBC768_PMC }, 110}; 111 112int 113amdpm_match(struct device *parent, void *match, void *aux) 114{ 115 return (pci_matchbyid(aux, amdpm_ids, 116 sizeof(amdpm_ids) / sizeof(amdpm_ids[0]))); 117} 118 119void 120amdpm_attach(struct device *parent, struct device *self, void *aux) 121{ 122 struct amdpm_softc *sc = (struct amdpm_softc *) self; 123 struct pci_attach_args *pa = aux; 124 struct timeval tv1, tv2; 125 pcireg_t cfg_reg, reg; 126 int i; 127 128 sc->sc_pc = pa->pa_pc; 129 sc->sc_tag = pa->pa_tag; 130 sc->sc_iot = pa->pa_iot; 131 132 cfg_reg = pci_conf_read(pa->pa_pc, pa->pa_tag, AMDPM_CONFREG); 133 if ((cfg_reg & AMDPM_PMIOEN) == 0) { 134 printf(": PMxx space isn't enabled\n"); 135 return; 136 } 137 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, AMDPM_PMPTR); 138 if (bus_space_map(sc->sc_iot, AMDPM_PMBASE(reg), AMDPM_PMSIZE, 139 0, &sc->sc_ioh)) { 140 printf(": failed to map PMxx space\n"); 141 return; 142 } 143 144#ifdef __HAVE_TIMECOUNTER 145 if ((cfg_reg & AMDPM_TMRRST) == 0 && 146 (cfg_reg & AMDPM_STOPTMR) == 0 && 147 PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_AMD_PBC768_PMC) { 148 printf(": %d-bit timer at %dHz", 149 (cfg_reg & AMDPM_TMR32) ? 32 : 24, 150 amdpm_timecounter.tc_frequency); 151 152 amdpm_timecounter.tc_priv = sc; 153 if (cfg_reg & AMDPM_TMR32) 154 amdpm_timecounter.tc_counter_mask = 0xffffffffu; 155 tc_init(&amdpm_timecounter); 156 } 157#endif 158 159 if (cfg_reg & AMDPM_RNGEN) { 160 /* Check to see if we can read data from the RNG. */ 161 (void) bus_space_read_4(sc->sc_iot, sc->sc_ioh, 162 AMDPM_RNGDATA); 163 /* benchmark the RNG */ 164 microtime(&tv1); 165 for (i = 2 * 1024; i--; ) { 166 while(!(bus_space_read_1(sc->sc_iot, sc->sc_ioh, 167 AMDPM_RNGSTAT) & AMDPM_RNGDONE)) 168 ; 169 (void) bus_space_read_4(sc->sc_iot, sc->sc_ioh, 170 AMDPM_RNGDATA); 171 } 172 microtime(&tv2); 173 174 timersub(&tv2, &tv1, &tv1); 175 if (tv1.tv_sec) 176 tv1.tv_usec += 1000000 * tv1.tv_sec; 177 printf(": rng active, %dKb/sec", 8 * 1000000 / tv1.tv_usec); 178 179#ifdef AMDPM_RND_COUNTERS 180 evcnt_attach_dynamic(&sc->sc_rnd_hits, EVCNT_TYPE_MISC, 181 NULL, sc->sc_dev.dv_xname, "rnd hits"); 182 evcnt_attach_dynamic(&sc->sc_rnd_miss, EVCNT_TYPE_MISC, 183 NULL, sc->sc_dev.dv_xname, "rnd miss"); 184 for (i = 0; i < 256; i++) { 185 evcnt_attach_dynamic(&sc->sc_rnd_data[i], 186 EVCNT_TYPE_MISC, NULL, sc->sc_dev.dv_xname, 187 "rnd data"); 188 } 189#endif 190 timeout_set(&sc->sc_rnd_ch, amdpm_rnd_callout, sc); 191 amdpm_rnd_callout(sc); 192 } 193 194 printf("\n"); 195} 196 197void 198amdpm_rnd_callout(void *v) 199{ 200 struct amdpm_softc *sc = v; 201 u_int32_t reg; 202#ifdef AMDPM_RND_COUNTERS 203 int i; 204#endif 205 206 if ((bus_space_read_4(sc->sc_iot, sc->sc_ioh, AMDPM_RNGSTAT) & 207 AMDPM_RNGDONE) != 0) { 208 reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, AMDPM_RNGDATA); 209 add_true_randomness(reg); 210#ifdef AMDPM_RND_COUNTERS 211 AMDPM_RNDCNT_INCR(&sc->sc_rnd_hits); 212 for (i = 0; i < sizeof(reg); i++, reg >>= NBBY) 213 AMDPM_RNDCNT_INCR(&sc->sc_rnd_data[reg & 0xff]); 214#endif 215 } else 216 AMDPM_RNDCNT_INCR(&sc->sc_rnd_miss); 217 timeout_add(&sc->sc_rnd_ch, 1); 218} 219 220#ifdef __HAVE_TIMECOUNTER 221u_int 222amdpm_get_timecount(struct timecounter *tc) 223{ 224 struct amdpm_softc *sc = tc->tc_priv; 225 u_int u2; 226#if 0 227 u_int u1, u3; 228#endif 229 230 u2 = bus_space_read_4(sc->sc_iot, sc->sc_ioh, AMDPM_TMR); 231#if 0 232 u3 = bus_space_read_4(sc->sc_iot, sc->sc_ioh, AMDPM_TMR); 233 do { 234 u1 = u2; 235 u2 = u3; 236 u3 = bus_space_read_4(sc->sc_iot, sc->sc_ioh, AMDPM_TMR); 237 } while (u1 > u2 || u2 > u3); 238#endif 239 return (u2); 240} 241#endif 242