Home | History | Annotate | Line # | Download | only in x86
      1 /*	$NetBSD: powernow.c,v 1.12 2021/10/07 12:52:27 msaitoh Exp $ */
      2 /*	$OpenBSD: powernow-k8.c,v 1.8 2006/06/16 05:58:50 gwk Exp $ */
      3 
      4 /*-
      5  * Copyright (c) 2004, 2006, 2008 The NetBSD Foundation, Inc.
      6  * All rights reserved.
      7  *
      8  * This code is derived from software contributed to The NetBSD Foundation
      9  * by Juan Romero Pardines and Martin Vegiard.
     10  *
     11  * Redistribution and use in source and binary forms, with or without
     12  * modification, are permitted provided that the following conditions
     13  * are met:
     14  * 1. Redistributions of source code must retain the above copyright
     15  *    notice, this list of conditions and the following disclaimer.
     16  * 2. Redistributions in binary form must reproduce the above copyright
     17  *    notice, this list of conditions and the following disclaimer in the
     18  *    documentation and/or other materials provided with the distribution.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     30  * POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 /*
     34  * Copyright (c) 2004-2005 Bruno Ducrot
     35  * Copyright (c) 2004 FUKUDA Nobuhiko <nfukuda (at) spa.is.uec.ac.jp>
     36  * Copyright (c) 2004 Martin Vegiard
     37  *
     38  * Redistribution and use in source and binary forms, with or without
     39  * modification, are permitted provided that the following conditions
     40  * are met:
     41  * 1. Redistributions of source code must retain the above copyright
     42  *    notice, this list of conditions and the following disclaimer.
     43  * 2. Redistributions in binary form must reproduce the above copyright
     44  *    notice, this list of conditions and the following disclaimer in the
     45  *    documentation and/or other materials provided with the distribution.
     46  *
     47  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     48  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     49  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     50  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     51  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     52  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     53  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     54  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     55  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     56  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     57  */
     58 
     59 #include <sys/cdefs.h>
     60 __KERNEL_RCSID(0, "$NetBSD: powernow.c,v 1.12 2021/10/07 12:52:27 msaitoh Exp $");
     61 
     62 #include <sys/param.h>
     63 #include <sys/device.h>
     64 #include <sys/kmem.h>
     65 #include <sys/module.h>
     66 #include <sys/sysctl.h>
     67 #include <sys/xcall.h>
     68 
     69 #include <dev/isa/isareg.h>
     70 
     71 #include <x86/cpuvar.h>
     72 #include <x86/cputypes.h>
     73 #include <x86/cpu_msr.h>
     74 #include <x86/isa_machdep.h>
     75 #include <x86/powernow.h>
     76 #include <x86/specialreg.h>
     77 
     78 #ifdef __i386__
     79 /*
     80  * Divide each value by 10 to get the processor multiplier.
     81  * Taken from powernow-k7.c/Linux by Dave Jones.
     82  */
     83 static int powernow_k7_fidtomult[32] = {
     84 	110, 115, 120, 125, 50, 55, 60, 65,
     85 	70, 75, 80, 85, 90, 95, 100, 105,
     86 	30, 190, 40, 200, 130, 135, 140, 210,
     87 	150, 225, 160, 165, 170, 180, -1, -1
     88 };
     89 
     90 /*
     91  * The following CPUs do not have same CPUID signature
     92  * in the PST tables, but they have correct FID/VID values.
     93  */
     94 static struct powernow_k7_quirk {
     95 	uint32_t rcpusig;	/* Correct cpu signature */
     96 	uint32_t pcpusig;	/* PST cpu signature */
     97 } powernow_k7_quirk[] = {
     98 	{ 0x680, 0x780 },	/* Reported by Olaf 'Rhialto' Seibert */
     99 	{ 0x6a0, 0x781 },	/* Reported by Eric Schnoebelen */
    100 	{ 0x6a0, 0x7a0 },	/* Reported by Nino Dehne */
    101 	{ 0, 0 }
    102 };
    103 
    104 #endif	/* __i386__ */
    105 
    106 struct powernow_softc {
    107 	device_t		   sc_dev;
    108 	struct cpu_info		  *sc_ci;
    109 	struct sysctllog	  *sc_log;
    110 	struct powernow_cpu_state *sc_state;
    111 	const char		  *sc_tech;
    112 	char			  *sc_freqs;
    113 	size_t			   sc_freqs_len;
    114 	unsigned int		   sc_freqs_cur;
    115 	int			   sc_node_target;
    116 	int			   sc_node_current;
    117 	int			   sc_flags;
    118 };
    119 
    120 static int	powernow_match(device_t, cfdata_t, void *);
    121 static void	powernow_attach(device_t, device_t, void *);
    122 static int	powernow_detach(device_t, int);
    123 static int	powernow_sysctl(device_t);
    124 static int	powernow_sysctl_helper(SYSCTLFN_PROTO);
    125 
    126 #ifdef __i386__
    127 static int	powernow_k7_init(device_t);
    128 static int	powernow_k7_states(device_t, unsigned int, unsigned int);
    129 static int	powernow_k7_setperf(device_t, unsigned int);
    130 static bool	powernow_k7_check(uint32_t, uint32_t);
    131 static int	powernow_k7_decode_pst(struct powernow_softc *, uint8_t *,int);
    132 #endif
    133 
    134 static int	powernow_k8_init(device_t);
    135 static int	powernow_k8_states(device_t, unsigned int, unsigned int);
    136 static int	powernow_k8_setperf(device_t, unsigned int);
    137 static uint64_t powernow_k8_fidvid(u_int fid, uint64_t vid, uint64_t ctrl);
    138 static int	powernow_k8_decode_pst(struct powernow_cpu_state *, uint8_t *);
    139 
    140 CFATTACH_DECL_NEW(powernow, sizeof(struct powernow_softc),
    141     powernow_match, powernow_attach, powernow_detach, NULL);
    142 
    143 static int
    144 powernow_match(device_t parent, cfdata_t cf, void *aux)
    145 {
    146 	struct cpufeature_attach_args *cfaa = aux;
    147 	struct cpu_info *ci = cfaa->ci;
    148 	uint32_t family, regs[4];
    149 
    150 	if (strcmp(cfaa->name, "frequency") != 0)
    151 		return 0;
    152 
    153 	if (cpu_vendor != CPUVENDOR_AMD)
    154 		return 0;
    155 
    156 	family = CPUID_TO_BASEFAMILY(ci->ci_signature);
    157 
    158 	if (family != 0x06 && family != 0x0f)
    159 		return 0;
    160 	else {
    161 		x86_cpuid(0x80000000, regs);
    162 
    163 		if (regs[0] < 0x80000007)
    164 			return 0;
    165 
    166 		x86_cpuid(0x80000007, regs);
    167 
    168 		if ((regs[3] & AMD_PN_FID_VID) == AMD_PN_FID_VID)
    169 			return 5;
    170 	}
    171 
    172 	return 0;
    173 }
    174 
    175 static void
    176 powernow_attach(device_t parent, device_t self, void *aux)
    177 {
    178 	struct powernow_softc *sc = device_private(self);
    179 	struct cpufeature_attach_args *cfaa = aux;
    180 	struct cpu_info *ci = cfaa->ci;
    181 	uint32_t family;
    182 	int rv;
    183 
    184 	sc->sc_ci = ci;
    185 	sc->sc_dev = self;
    186 
    187 	sc->sc_flags = 0;
    188 	sc->sc_log = NULL;
    189 	sc->sc_tech = NULL;
    190 	sc->sc_state = NULL;
    191 	sc->sc_freqs = NULL;
    192 
    193 	family = CPUID_TO_BASEFAMILY(ci->ci_signature);
    194 
    195 	switch (family) {
    196 
    197 #ifdef __i386__
    198 	case 0x06:
    199 		rv = powernow_k7_init(self);
    200 		break;
    201 #endif
    202 	case 0x0f:
    203 		rv = powernow_k8_init(self);
    204 		break;
    205 
    206 	default:
    207 		rv = ENODEV;
    208 	}
    209 
    210 	if (rv != 0) {
    211 		aprint_normal(": failed to initialize (err %d)\n", rv);
    212 		return;
    213 	}
    214 
    215 	rv = powernow_sysctl(self);
    216 
    217 	if (rv != 0) {
    218 		aprint_normal(": failed to initialize sysctl (err %d)\n", rv);
    219 		return;
    220 	}
    221 
    222 	KASSERT(sc->sc_tech != NULL);
    223 
    224 	aprint_naive("\n");
    225 	aprint_normal(": AMD %s\n", sc->sc_tech);
    226 
    227 	(void)pmf_device_register(self, NULL, NULL);
    228 }
    229 
    230 static int
    231 powernow_detach(device_t self, int flags)
    232 {
    233 	struct powernow_softc *sc = device_private(self);
    234 
    235 	if (sc->sc_log != NULL)
    236 		sysctl_teardown(&sc->sc_log);
    237 
    238 	if (sc->sc_state != NULL)
    239 		kmem_free(sc->sc_state, sizeof(*sc->sc_state));
    240 
    241 	if (sc->sc_freqs != NULL)
    242 		kmem_free(sc->sc_freqs, sc->sc_freqs_len);
    243 
    244 	pmf_device_deregister(self);
    245 
    246 	return 0;
    247 }
    248 
    249 static int
    250 powernow_sysctl(device_t self)
    251 {
    252 	const struct sysctlnode *freqnode, *node, *cpunode;
    253 	struct powernow_softc *sc = device_private(self);
    254 	int rv;
    255 
    256 	/*
    257 	 * Setup the sysctl sub-tree machdep.powernow.
    258 	 */
    259 	rv = sysctl_createv(&sc->sc_log, 0, NULL, &node,
    260 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
    261 	    NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL);
    262 
    263 	if (rv != 0)
    264 		goto fail;
    265 
    266 	rv = sysctl_createv(&sc->sc_log, 0, &node, &cpunode,
    267 	    0, CTLTYPE_NODE, "cpu", NULL,
    268 	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
    269 
    270 	if (rv != 0)
    271 		goto fail;
    272 
    273 	rv = sysctl_createv(&sc->sc_log, 0, &cpunode, &freqnode,
    274 	    0, CTLTYPE_NODE, "frequency", NULL,
    275 	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
    276 
    277 	if (rv != 0)
    278 		goto fail;
    279 
    280 	rv = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
    281 	    CTLFLAG_READWRITE, CTLTYPE_INT, "target", NULL,
    282 	    powernow_sysctl_helper, 0, (void *)sc, 0, CTL_CREATE, CTL_EOL);
    283 
    284 	if (rv != 0)
    285 		goto fail;
    286 
    287 	sc->sc_node_target = node->sysctl_num;
    288 
    289 	rv = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
    290 	    0, CTLTYPE_INT, "current", NULL,
    291 	    powernow_sysctl_helper, 0, (void *)sc, 0, CTL_CREATE, CTL_EOL);
    292 
    293 	if (rv != 0)
    294 		goto fail;
    295 
    296 	sc->sc_node_current = node->sysctl_num;
    297 
    298 	rv = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
    299 	    0, CTLTYPE_STRING, "available", NULL,
    300 	    NULL, 0, sc->sc_freqs, sc->sc_freqs_len, CTL_CREATE, CTL_EOL);
    301 
    302 	if (rv != 0)
    303 		goto fail;
    304 
    305 	return 0;
    306 
    307 fail:
    308 	sysctl_teardown(&sc->sc_log);
    309 	sc->sc_log = NULL;
    310 
    311 	return rv;
    312 }
    313 
    314 static int
    315 powernow_sysctl_helper(SYSCTLFN_ARGS)
    316 {
    317 	struct powernow_softc *sc;
    318 	struct sysctlnode node;
    319 	int fq, oldfq, error;
    320 	uint32_t family;
    321 	int rv;
    322 
    323 	fq = oldfq = 0;
    324 
    325 	node = *rnode;
    326 	sc = node.sysctl_data;
    327 
    328 	if (sc->sc_state == NULL)
    329 		return EOPNOTSUPP;
    330 
    331 	node.sysctl_data = &fq;
    332 
    333 	if (rnode->sysctl_num == sc->sc_node_target)
    334 		fq = oldfq = sc->sc_freqs_cur;
    335 	else if (rnode->sysctl_num == sc->sc_node_current)
    336 		fq = sc->sc_freqs_cur;
    337 	else
    338 		return EOPNOTSUPP;
    339 
    340 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    341 
    342 	if (error || newp == NULL)
    343 		return error;
    344 
    345 	family = CPUID_TO_BASEFAMILY(sc->sc_ci->ci_signature);
    346 
    347 	if (rnode->sysctl_num == sc->sc_node_target && fq != oldfq) {
    348 
    349 		switch (family) {
    350 
    351 #ifdef __i386__
    352 		case 0x06:
    353 			rv = powernow_k7_setperf(sc->sc_dev, fq);
    354 			break;
    355 #endif
    356 		case 0x0f:
    357 			rv = powernow_k8_setperf(sc->sc_dev, fq);
    358 			break;
    359 
    360 		default:
    361 			return ENODEV;
    362 		}
    363 
    364 		if (rv != 0)
    365 			return EINVAL;
    366 
    367 		sc->sc_freqs_cur = fq;
    368 	}
    369 
    370 	return 0;
    371 }
    372 
    373 #ifdef __i386__
    374 
    375 static int
    376 powernow_k7_init(device_t self)
    377 {
    378 	struct powernow_softc *sc = device_private(self);
    379 	uint32_t currentfid, maxfid, mhz, startvid;
    380 	uint64_t status;
    381 	int i, rv;
    382 	char tmp[6];
    383 
    384 	sc->sc_state = kmem_alloc(sizeof(*sc->sc_state), KM_SLEEP);
    385 
    386 	if (sc->sc_ci->ci_signature == AMD_ERRATA_A0_CPUSIG)
    387 		sc->sc_flags |= PN7_FLAG_ERRATA_A0;
    388 
    389 	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
    390 	maxfid = PN7_STA_MFID(status);
    391 	startvid = PN7_STA_SVID(status);
    392 	currentfid = PN7_STA_CFID(status);
    393 
    394 	mhz = sc->sc_ci->ci_data.cpu_cc_freq / 1000000;
    395 	sc->sc_state->fsb = mhz / (powernow_k7_fidtomult[currentfid] / 10);
    396 
    397 	if (powernow_k7_states(self, maxfid, startvid) == 0) {
    398 		rv = ENXIO;
    399 		goto fail;
    400 	}
    401 
    402 	if (sc->sc_state->n_states == 0) {
    403 		rv = ENODEV;
    404 		goto fail;
    405 	}
    406 
    407 	sc->sc_freqs_len = sc->sc_state->n_states * (sizeof("9999 ") - 1) + 1;
    408 	sc->sc_freqs = kmem_zalloc(sc->sc_freqs_len, KM_SLEEP);
    409 
    410 	for (i = 0; i < sc->sc_state->n_states; i++) {
    411 
    412 		/* Skip duplicated matches. */
    413 		if (i > 0 && (sc->sc_state->state_table[i].freq ==
    414 		    sc->sc_state->state_table[i - 1].freq))
    415 			continue;
    416 
    417 		DPRINTF(("%s: cstate->state_table.freq=%d\n",
    418 			__func__, sc->sc_state->state_table[i].freq));
    419 		DPRINTF(("%s: fid=%d vid=%d\n", __func__,
    420 			sc->sc_state->state_table[i].fid,
    421 			sc->sc_state->state_table[i].vid));
    422 
    423 		snprintf(tmp, sizeof(tmp), "%d%s",
    424 		    sc->sc_state->state_table[i].freq,
    425 		    i < sc->sc_state->n_states - 1 ? " " : "");
    426 
    427 		DPRINTF(("%s: tmp=%s\n", __func__, tmp));
    428 
    429 		(void)strlcat(sc->sc_freqs, tmp, sc->sc_freqs_len);
    430 	}
    431 
    432 	sc->sc_tech = ((sc->sc_flags & PN7_FLAG_DESKTOP_VRM) != 0) ?
    433 	    "Cool`n'Quiet K7" : "Powernow! K7";
    434 
    435 	return 0;
    436 
    437 fail:
    438 	kmem_free(sc->sc_state, sizeof(*sc->sc_state));
    439 	sc->sc_state = NULL;
    440 
    441 	return rv;
    442 }
    443 
    444 static int
    445 powernow_k7_states(device_t self, unsigned int fid, unsigned int vid)
    446 {
    447 	struct powernow_softc *sc = device_private(self);
    448 	struct powernow_cpu_state *cstate;
    449 	struct powernow_psb_s *psb;
    450 	struct powernow_pst_s *pst;
    451 	uint32_t cpusig, maxpst;
    452 	bool cpusig_ok;
    453 	uint8_t *p;
    454 
    455 	cstate = sc->sc_state;
    456 	cpusig = sc->sc_ci->ci_signature;
    457 
    458 	/*
    459 	 * Look in the 0xe0000 - 0x100000 physical address
    460 	 * range for the pst tables; 16 byte blocks
    461 	 */
    462 	for (p = (uint8_t *)ISA_HOLE_VADDR(BIOS_START);
    463 	     p < (uint8_t *)ISA_HOLE_VADDR(BIOS_START + BIOS_LEN);
    464 	     p+= BIOS_STEP) {
    465 
    466 		if (memcmp(p, "AMDK7PNOW!", 10) == 0) {
    467 
    468 			psb = (struct powernow_psb_s *)p;
    469 
    470 			if (psb->version != PN7_PSB_VERSION)
    471 				return 0;
    472 
    473 			cstate->sgtc = psb->ttime * cstate->fsb;
    474 
    475 			if (cstate->sgtc < 100 * cstate->fsb)
    476 				cstate->sgtc = 100 * cstate->fsb;
    477 
    478 			if (psb->flags & 1)
    479 				sc->sc_flags |= PN7_FLAG_DESKTOP_VRM;
    480 
    481 			p += sizeof(struct powernow_psb_s);
    482 
    483 			for (maxpst = 0; maxpst < psb->n_pst; maxpst++) {
    484 				pst = (struct powernow_pst_s*) p;
    485 
    486 				/*
    487 				 * Some models do not have same CPUID
    488 				 * signature in the PST table. Accept to
    489 				 * report frequencies via a quirk table
    490 				 * and fid/vid match.
    491 				 */
    492 				DPRINTF(("%s: cpusig=0x%x pst->sig:0x%x\n",
    493 				    __func__, cpusig, pst->signature));
    494 
    495 				cpusig_ok = powernow_k7_check(cpusig,
    496 							      pst->signature);
    497 
    498 				if ((cpusig_ok != false &&
    499 				    (fid == pst->fid && vid == pst->vid))) {
    500 					DPRINTF(("%s: pst->signature ok\n",
    501 					    __func__));
    502 					if (abs(cstate->fsb - pst->pll) > 5)
    503 						continue;
    504 					cstate->n_states = pst->n_states;
    505 					return (powernow_k7_decode_pst(sc,
    506 					    p + sizeof(struct powernow_pst_s),
    507 					    cstate->n_states));
    508 
    509 				}
    510 
    511 				p += sizeof(struct powernow_pst_s) +
    512 				    (2 * pst->n_states);
    513 			}
    514 		}
    515 	}
    516 
    517 	return 0;
    518 }
    519 
    520 static int
    521 powernow_k7_setperf(device_t self, unsigned int freq)
    522 {
    523 	struct powernow_softc *sc = device_private(self);
    524 	int cvid, cfid, vid = 0, fid = 0;
    525 	uint64_t status, ctl;
    526 	unsigned int i;
    527 
    528 	DPRINTF(("%s: cstate->n_states=%d\n",
    529 		__func__, sc->sc_state->n_states));
    530 
    531 	for (i = 0; i < sc->sc_state->n_states; i++) {
    532 
    533 		if (sc->sc_state->state_table[i].freq >= freq) {
    534 			DPRINTF(("%s: freq=%d\n", __func__, freq));
    535 			fid = sc->sc_state->state_table[i].fid;
    536 			vid = sc->sc_state->state_table[i].vid;
    537 			DPRINTF(("%s: fid=%d vid=%d\n", __func__, fid, vid));
    538 			break;
    539 		}
    540 	}
    541 
    542 	if (fid == 0 || vid == 0)
    543 		return 0;
    544 
    545 	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
    546 	cfid = PN7_STA_CFID(status);
    547 	cvid = PN7_STA_CVID(status);
    548 
    549 	/*
    550 	 * We're already at the requested level.
    551 	 */
    552 	if (fid == cfid && vid == cvid)
    553 		return 0;
    554 
    555 	ctl = rdmsr(MSR_AMDK7_FIDVID_CTL) & PN7_CTR_FIDCHRATIO;
    556 
    557 	ctl |= PN7_CTR_FID(fid);
    558 	ctl |= PN7_CTR_VID(vid);
    559 	ctl |= PN7_CTR_SGTC(sc->sc_state->sgtc);
    560 
    561 	if ((sc->sc_flags & PN7_FLAG_ERRATA_A0) != 0)
    562 		x86_disable_intr();
    563 
    564 	if (powernow_k7_fidtomult[fid] < powernow_k7_fidtomult[cfid]) {
    565 
    566 		wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC);
    567 
    568 		if (vid != cvid)
    569 			wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC);
    570 
    571 	} else {
    572 
    573 		wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC);
    574 
    575 		if (fid != cfid)
    576 			wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC);
    577 	}
    578 
    579 	if ((sc->sc_flags & PN7_FLAG_ERRATA_A0) != 0)
    580 		x86_enable_intr();
    581 
    582 	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
    583 
    584 	cfid = PN7_STA_CFID(status);
    585 	cvid = PN7_STA_CVID(status);
    586 
    587 	if (cfid == fid || cvid == vid)
    588 		freq = sc->sc_state->state_table[i].freq;
    589 
    590 	return 0;
    591 }
    592 
    593 static bool
    594 powernow_k7_check(uint32_t real_cpusig, uint32_t pst_cpusig)
    595 {
    596 	int j;
    597 
    598 	if (real_cpusig == pst_cpusig)
    599 		return true;
    600 
    601 	for (j = 0; powernow_k7_quirk[j].rcpusig != 0; j++) {
    602 
    603 		if ((real_cpusig == powernow_k7_quirk[j].rcpusig) &&
    604 		    (pst_cpusig == powernow_k7_quirk[j].pcpusig))
    605 			return true;
    606 	}
    607 
    608 	return false;
    609 }
    610 
    611 static int
    612 powernow_k7_decode_pst(struct powernow_softc *sc, uint8_t *p, int npst)
    613 {
    614 	struct powernow_cpu_state *cstate = sc->sc_state;
    615 	struct powernow_state state;
    616 	int i, j, n;
    617 
    618 	for (n = 0, i = 0; i < npst; ++i) {
    619 
    620 		state.fid = *p++;
    621 		state.vid = *p++;
    622 		state.freq = powernow_k7_fidtomult[state.fid]/10 * cstate->fsb;
    623 
    624 		if ((sc->sc_flags & PN7_FLAG_ERRATA_A0) != 0 &&
    625 		    (powernow_k7_fidtomult[state.fid] % 10) == 5)
    626 			continue;
    627 
    628 		j = n;
    629 
    630 		while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
    631 
    632 			(void)memcpy(&cstate->state_table[j],
    633 			    &cstate->state_table[j - 1],
    634 			    sizeof(struct powernow_state));
    635 
    636 			--j;
    637 		}
    638 
    639 		(void)memcpy(&cstate->state_table[j], &state,
    640 		    sizeof(struct powernow_state));
    641 
    642 		++n;
    643 	}
    644 
    645 	/*
    646 	 * Fix powernow_max_states, if errata_a0 gave
    647 	 * us less states than expected.
    648 	 */
    649 	cstate->n_states = n;
    650 
    651 	return 1;
    652 }
    653 
    654 #endif	/* __i386__ */
    655 
    656 static int
    657 powernow_k8_init(device_t self)
    658 {
    659 	struct powernow_softc *sc = device_private(self);
    660 	uint32_t i, maxfid, maxvid;
    661 	uint64_t status;
    662 	int rv;
    663 	char tmp[6];
    664 
    665 	sc->sc_state = kmem_alloc(sizeof(*sc->sc_state), KM_SLEEP);
    666 
    667 	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
    668 	maxfid = PN8_STA_MFID(status);
    669 	maxvid = PN8_STA_MVID(status);
    670 
    671 	if (powernow_k8_states(self, maxfid, maxvid) == 0) {
    672 		rv = ENXIO;
    673 		goto fail;
    674 	}
    675 
    676 	if (sc->sc_state->n_states == 0) {
    677 		rv = ENODEV;
    678 		goto fail;
    679 	}
    680 
    681 	sc->sc_freqs_len = sc->sc_state->n_states * (sizeof("9999 ") - 1) + 1;
    682 	sc->sc_freqs = kmem_zalloc(sc->sc_freqs_len, KM_SLEEP);
    683 
    684 	for (i = 0; i < sc->sc_state->n_states; i++) {
    685 
    686 		DPRINTF(("%s: cstate->state_table.freq=%d\n",
    687 			__func__, sc->sc_state->state_table[i].freq));
    688 
    689 		DPRINTF(("%s: fid=%d vid=%d\n", __func__,
    690 			sc->sc_state->state_table[i].fid,
    691 			sc->sc_state->state_table[i].vid));
    692 
    693 		snprintf(tmp, sizeof(tmp), "%d%s",
    694 		    sc->sc_state->state_table[i].freq,
    695 		    i < sc->sc_state->n_states - 1 ? " " : "");
    696 
    697 		DPRINTF(("%s: tmp=%s\n", __func__, tmp));
    698 
    699 		(void)strlcat(sc->sc_freqs, tmp, sc->sc_freqs_len);
    700 	}
    701 
    702 	/*
    703 	 * If start FID is different to max FID, then it is a mobile
    704 	 * processor. If not, it is a low powered desktop processor.
    705 	 */
    706 	sc->sc_tech = (PN8_STA_SFID(status) != PN8_STA_MFID(status)) ?
    707 	    "PowerNow!" : "Cool`n'Quiet";
    708 
    709 	return 0;
    710 
    711 fail:
    712 	kmem_free(sc->sc_state, sizeof(*sc->sc_state));
    713 	sc->sc_state = NULL;
    714 
    715 	return rv;
    716 }
    717 
    718 static int
    719 powernow_k8_states(device_t self, unsigned int fid, unsigned int vid)
    720 {
    721 	struct powernow_softc *sc = device_private(self);
    722 	struct powernow_cpu_state *cstate;
    723 	struct powernow_psb_s *psb;
    724 	struct powernow_pst_s *pst;
    725 	uint32_t cpusig;
    726 	uint8_t *p;
    727 	int i;
    728 
    729 	cstate = sc->sc_state;
    730 	cpusig = sc->sc_ci->ci_signature;
    731 
    732 	DPRINTF(("%s: before the for loop\n", __func__));
    733 
    734 	for (p = (uint8_t *)ISA_HOLE_VADDR(BIOS_START);
    735 	     p < (uint8_t *)ISA_HOLE_VADDR(BIOS_START + BIOS_LEN); p += 16) {
    736 
    737 		if (memcmp(p, "AMDK7PNOW!", 10) != 0)
    738 			continue;
    739 
    740 		DPRINTF(("%s: inside the for loop\n", __func__));
    741 
    742 		psb = (struct powernow_psb_s *)p;
    743 
    744 		if (psb->version != 0x14) {
    745 			DPRINTF(("%s: psb->version != 0x14\n", __func__));
    746 			return 0;
    747 		}
    748 
    749 		cstate->vst = psb->ttime;
    750 		cstate->rvo = PN8_PSB_TO_RVO(psb->reserved);
    751 		cstate->irt = PN8_PSB_TO_IRT(psb->reserved);
    752 		cstate->mvs = PN8_PSB_TO_MVS(psb->reserved);
    753 		cstate->low = PN8_PSB_TO_BATT(psb->reserved);
    754 
    755 		p += sizeof(struct powernow_psb_s);
    756 
    757 		for (i = 0; i < psb->n_pst; ++i) {
    758 
    759 			pst = (struct powernow_pst_s *)p;
    760 
    761 			cstate->pll = pst->pll;
    762 			cstate->n_states = pst->n_states;
    763 
    764 			if (cpusig == pst->signature &&
    765 			    pst->fid == fid && pst->vid == vid) {
    766 
    767 				DPRINTF(("%s: cpusig = sig\n", __func__));
    768 
    769 				return (powernow_k8_decode_pst(cstate,
    770 				    p+= sizeof(struct powernow_pst_s)));
    771 			}
    772 
    773 			p += sizeof(struct powernow_pst_s) +
    774 			    2 * cstate->n_states;
    775 		}
    776 	}
    777 
    778 	DPRINTF(("%s: returns 0!\n", __func__));
    779 
    780 	return 0;
    781 }
    782 
    783 static int
    784 powernow_k8_setperf(device_t self, unsigned int freq)
    785 {
    786 	struct powernow_softc *sc = device_private(self);
    787 	int cfid, cvid, fid = 0, vid = 0;
    788 	uint64_t status;
    789 	unsigned int i;
    790 	uint32_t val;
    791 	int rvo;
    792 
    793 	/*
    794 	 * We dont do a "pending wait" here, given
    795 	 * that we need to ensure that the change
    796 	 * pending bit is not stuck.
    797 	 */
    798 	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
    799 
    800 	if (PN8_STA_PENDING(status))
    801 		return 1;
    802 
    803 	cfid = PN8_STA_CFID(status);
    804 	cvid = PN8_STA_CVID(status);
    805 
    806 	DPRINTF(("%s: sc->sc_state->n_states=%d\n",
    807 		__func__, sc->sc_state->n_states));
    808 
    809 	for (i = 0; i < sc->sc_state->n_states; i++) {
    810 
    811 		if (sc->sc_state->state_table[i].freq >= freq) {
    812 			DPRINTF(("%s: freq=%d\n", __func__, freq));
    813 			fid = sc->sc_state->state_table[i].fid;
    814 			vid = sc->sc_state->state_table[i].vid;
    815 			DPRINTF(("%s: fid=%d vid=%d\n", __func__, fid, vid));
    816 			break;
    817 		}
    818 
    819 	}
    820 
    821 	if (fid == cfid && vid == cvid)
    822 		return 0;
    823 
    824 	/*
    825 	 * Phase 1: raise the core voltage to the
    826 	 * requested VID if frequency is going up.
    827 	 */
    828 	while (cvid > vid) {
    829 		val = cvid - (1 << sc->sc_state->mvs);
    830 		status = powernow_k8_fidvid(cfid, (val > 0) ? val : 0, 1ULL);
    831 		cvid = PN8_STA_CVID(status);
    832 		COUNT_OFF_VST(sc->sc_state->vst);
    833 	}
    834 
    835 	/*
    836 	 * Then raise to voltage + RVO (if required).
    837 	 */
    838 	for (rvo = sc->sc_state->rvo; rvo > 0 && cvid > 0; --rvo) {
    839 		/*
    840 		 * XXX:	It is not clear from the specification if we have
    841 		 *	to do that in 0.25 step or in MVS. Therefore do it
    842 		 *	as it is done under Linux.
    843 		 */
    844 		status = powernow_k8_fidvid(cfid, cvid - 1, 1ULL);
    845 		cvid = PN8_STA_CVID(status);
    846 		COUNT_OFF_VST(sc->sc_state->vst);
    847 	}
    848 
    849 	/*
    850 	 * Phase 2: change to requested core frequency.
    851 	 */
    852 	if (cfid != fid) {
    853 		uint32_t vco_fid, vco_cfid;
    854 
    855 		vco_fid = FID_TO_VCO_FID(fid);
    856 		vco_cfid = FID_TO_VCO_FID(cfid);
    857 
    858 		while (abs(vco_fid - vco_cfid) > 2) {
    859 
    860 			if (fid > cfid) {
    861 				if (cfid > 6)
    862 					val = cfid + 2;
    863 				else
    864 					val = FID_TO_VCO_FID(cfid) + 2;
    865 			} else
    866 				val = cfid - 2;
    867 
    868 			status = powernow_k8_fidvid(val, cvid,
    869 			    (uint64_t)sc->sc_state->pll * 1000 / 5);
    870 
    871 			cfid = PN8_STA_CFID(status);
    872 			COUNT_OFF_IRT(sc->sc_state->irt);
    873 			vco_cfid = FID_TO_VCO_FID(cfid);
    874 		}
    875 
    876 
    877 		status = powernow_k8_fidvid(fid, cvid,
    878 		    (uint64_t)sc->sc_state->pll * 1000 / 5);
    879 
    880 		cfid = PN8_STA_CFID(status);
    881 		COUNT_OFF_IRT(sc->sc_state->irt);
    882 	}
    883 
    884 	/*
    885 	 * Phase 3: change to requested voltage.
    886 	 */
    887 	if (cvid != vid) {
    888 		status = powernow_k8_fidvid(cfid, vid, 1ULL);
    889 		cvid = PN8_STA_CVID(status);
    890 		COUNT_OFF_VST(sc->sc_state->vst);
    891 	}
    892 
    893 #if 0
    894 	if (cfid == fid || cvid == vid)
    895 		freq = sc->sc_state->state_table[i].freq;
    896 #endif
    897 
    898 	return 0;
    899 }
    900 
    901 static uint64_t
    902 powernow_k8_fidvid(u_int fid, uint64_t vid, uint64_t ctrl)
    903 {
    904 	struct msr_rw_info msr;
    905 	uint64_t status, xc;
    906 
    907 	msr.msr_read = false;
    908 	msr.msr_value = (ctrl << 32) | (1ULL << 16) | (vid << 8) | fid;
    909 	msr.msr_type = MSR_AMDK7_FIDVID_CTL;
    910 
    911 	xc = xc_broadcast(0, (xcfunc_t)x86_msr_xcall, &msr, NULL);
    912 	xc_wait(xc);
    913 
    914 	do {
    915 		status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
    916 
    917 	} while (PN8_STA_PENDING(status));
    918 
    919 	return status;
    920 }
    921 
    922 /*
    923  * Given a set of pair of FID/VID, and a number of performance
    924  * states, compute a state table via an insertion sort.
    925  */
    926 static int
    927 powernow_k8_decode_pst(struct powernow_cpu_state *cstate, uint8_t *p)
    928 {
    929 	struct powernow_state state;
    930 	int i, j, n;
    931 
    932 	for (n = 0, i = 0; i < cstate->n_states; i++) {
    933 
    934 		state.fid = *p++;
    935 		state.vid = *p++;
    936 
    937 		/*
    938 		 * The minimum supported frequency per the data sheet is
    939 		 * 800 MHz. The maximum supported frequency is 5000 MHz.
    940 		 */
    941 		state.freq = 800 + state.fid * 100;
    942 		j = n;
    943 
    944 		while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
    945 
    946 			(void)memcpy(&cstate->state_table[j],
    947 			    &cstate->state_table[j - 1],
    948 			    sizeof(struct powernow_state));
    949 
    950 			--j;
    951 		}
    952 
    953 		(void)memcpy(&cstate->state_table[j], &state,
    954 		    sizeof(struct powernow_state));
    955 
    956 		n++;
    957 	}
    958 
    959 	return 1;
    960 }
    961 
    962 MODULE(MODULE_CLASS_DRIVER, powernow, NULL);
    963 
    964 #ifdef _MODULE
    965 #include "ioconf.c"
    966 #endif
    967 
    968 static int
    969 powernow_modcmd(modcmd_t cmd, void *aux)
    970 {
    971 	int error = 0;
    972 
    973 	switch (cmd) {
    974 	case MODULE_CMD_INIT:
    975 #ifdef _MODULE
    976 		error = config_init_component(cfdriver_ioconf_powernow,
    977 		    cfattach_ioconf_powernow, cfdata_ioconf_powernow);
    978 #endif
    979 		return error;
    980 	case MODULE_CMD_FINI:
    981 #ifdef _MODULE
    982 		error = config_fini_component(cfdriver_ioconf_powernow,
    983 		    cfattach_ioconf_powernow, cfdata_ioconf_powernow);
    984 #endif
    985 		return error;
    986 	default:
    987 		return ENOTTY;
    988 	}
    989 }
    990