powernow.c revision 184104
1139749Simp/*-
253790Sobrien * Copyright (c) 2004-2005 Bruno Ducrot
353790Sobrien * Copyright (c) 2004 FUKUDA Nobuhiko <nfukuda@spa.is.uec.ac.jp>
453790Sobrien *
586266Sgroudier * Redistribution and use in source and binary forms, with or without
653790Sobrien * modification, are permitted provided that the following conditions
753790Sobrien * are met:
859743Sgroudier * 1. Redistributions of source code must retain the above copyright
959743Sgroudier *    notice, this list of conditions and the following disclaimer.
1053790Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1153790Sobrien *    notice, this list of conditions and the following disclaimer in the
1253790Sobrien *    documentation and/or other materials provided with the distribution.
1353790Sobrien *
1453790Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1553790Sobrien * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1653790Sobrien * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1753790Sobrien * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1853790Sobrien * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1953790Sobrien * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2053790Sobrien * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2153790Sobrien * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2253790Sobrien * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2353790Sobrien * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2453790Sobrien */
2553790Sobrien
2653790Sobrien/*
2753790Sobrien * Many thanks to Nate Lawson for his helpful comments on this driver and
2853790Sobrien * to Jung-uk Kim for testing.
2953790Sobrien */
3053790Sobrien
3153790Sobrien#include <sys/cdefs.h>
3253790Sobrien__FBSDID("$FreeBSD: head/sys/i386/cpufreq/powernow.c 184104 2008-10-21 00:52:20Z jkim $");
3353790Sobrien
3453790Sobrien#include <sys/param.h>
3553790Sobrien#include <sys/bus.h>
3653790Sobrien#include <sys/cpu.h>
3753790Sobrien#include <sys/kernel.h>
3853790Sobrien#include <sys/malloc.h>
3953790Sobrien#include <sys/module.h>
4053790Sobrien#include <sys/pcpu.h>
4153790Sobrien#include <sys/systm.h>
4253790Sobrien
4353790Sobrien#include <machine/pc/bios.h>
4453790Sobrien#include <machine/md_var.h>
4553790Sobrien#include <machine/specialreg.h>
4653790Sobrien#include <machine/cputypes.h>
4753790Sobrien#include <machine/vmparam.h>
4853790Sobrien#include <sys/rman.h>
4953790Sobrien
5053790Sobrien#include <vm/vm.h>
5153790Sobrien#include <vm/pmap.h>
5253790Sobrien
5353790Sobrien#include "cpufreq_if.h"
5453790Sobrien
5553790Sobrien#define PN7_TYPE	0
5653790Sobrien#define PN8_TYPE	1
5755258Sobrien
5855258Sobrien/* Flags for some hardware bugs. */
5955258Sobrien#define A0_ERRATA	0x1	/* Bugs for the rev. A0 of Athlon (K7):
6053790Sobrien				 * Interrupts must be disabled and no half
6153790Sobrien				 * multipliers are allowed */
6253790Sobrien#define PENDING_STUCK	0x2	/* With some buggy chipset and some newer AMD64
6353790Sobrien				 * processor (Rev. G?):
6453790Sobrien				 * the pending bit from the msr FIDVID_STATUS
6553790Sobrien				 * is set forever.  No workaround :( */
6653790Sobrien
6753790Sobrien/* Legacy configuration via BIOS table PSB. */
6853790Sobrien#define PSB_START	0
6959743Sgroudier#define PSB_STEP	0x10
7059743Sgroudier#define PSB_SIG		"AMDK7PNOW!"
7159743Sgroudier#define PSB_LEN		10
7259743Sgroudier#define PSB_OFF		0
7359743Sgroudier
7453790Sobrienstruct psb_header {
7553790Sobrien	char		 signature[10];
7654690Sobrien	uint8_t		 version;
7753790Sobrien	uint8_t		 flags;
7853790Sobrien	uint16_t	 settlingtime;
7953790Sobrien	uint8_t		 res1;
8053790Sobrien	uint8_t		 numpst;
8153790Sobrien} __packed;
8253790Sobrien
8354690Sobrienstruct pst_header {
8453790Sobrien	uint32_t	 cpuid;
8553790Sobrien	uint8_t		 fsb;
86236061Smarius	uint8_t		 maxfid;
87236061Smarius	uint8_t		 startvid;
88236061Smarius	uint8_t		 numpstates;
89236061Smarius} __packed;
90236061Smarius
91236061Smarius/*
92236061Smarius * MSRs and bits used by Powernow technology
93237101Smarius */
9453790Sobrien#define MSR_AMDK7_FIDVID_CTL		0xc0010041
95237101Smarius#define MSR_AMDK7_FIDVID_STATUS		0xc0010042
96237101Smarius
9753790Sobrien/* Bitfields used by K7 */
98237101Smarius
9953790Sobrien#define PN7_CTR_FID(x)			((x) & 0x1f)
10053790Sobrien#define PN7_CTR_VID(x)			(((x) & 0x1f) << 8)
10153790Sobrien#define PN7_CTR_FIDC			0x00010000
10253790Sobrien#define PN7_CTR_VIDC			0x00020000
10353790Sobrien#define PN7_CTR_FIDCHRATIO		0x00100000
10454690Sobrien#define PN7_CTR_SGTC(x)			(((uint64_t)(x) & 0x000fffff) << 32)
10553790Sobrien
10653790Sobrien#define PN7_STA_CFID(x)			((x) & 0x1f)
10753790Sobrien#define PN7_STA_SFID(x)			(((x) >> 8) & 0x1f)
10853790Sobrien#define PN7_STA_MFID(x)			(((x) >> 16) & 0x1f)
10953790Sobrien#define PN7_STA_CVID(x)			(((x) >> 32) & 0x1f)
11053790Sobrien#define PN7_STA_SVID(x)			(((x) >> 40) & 0x1f)
11153790Sobrien#define PN7_STA_MVID(x)			(((x) >> 48) & 0x1f)
11253790Sobrien
11354690Sobrien/* ACPI ctr_val status register to powernow k7 configuration */
11453790Sobrien#define ACPI_PN7_CTRL_TO_FID(x)		((x) & 0x1f)
11553790Sobrien#define ACPI_PN7_CTRL_TO_VID(x)		(((x) >> 5) & 0x1f)
11653790Sobrien#define ACPI_PN7_CTRL_TO_SGTC(x)	(((x) >> 10) & 0xffff)
11753790Sobrien
11853790Sobrien/* Bitfields used by K8 */
11953790Sobrien
12053790Sobrien#define PN8_CTR_FID(x)			((x) & 0x3f)
12153790Sobrien#define PN8_CTR_VID(x)			(((x) & 0x1f) << 8)
12253790Sobrien#define PN8_CTR_PENDING(x)		(((x) & 1) << 32)
12353790Sobrien
12454690Sobrien#define PN8_STA_CFID(x)			((x) & 0x3f)
12553790Sobrien#define PN8_STA_SFID(x)			(((x) >> 8) & 0x3f)
12653790Sobrien#define PN8_STA_MFID(x)			(((x) >> 16) & 0x3f)
12753790Sobrien#define PN8_STA_PENDING(x)		(((x) >> 31) & 0x01)
12853790Sobrien#define PN8_STA_CVID(x)			(((x) >> 32) & 0x1f)
12954690Sobrien#define PN8_STA_SVID(x)			(((x) >> 40) & 0x1f)
13059743Sgroudier#define PN8_STA_MVID(x)			(((x) >> 48) & 0x1f)
13153790Sobrien
13253790Sobrien/* Reserved1 to powernow k8 configuration */
13353790Sobrien#define PN8_PSB_TO_RVO(x)		((x) & 0x03)
13453790Sobrien#define PN8_PSB_TO_IRT(x)		(((x) >> 2) & 0x03)
13553790Sobrien#define PN8_PSB_TO_MVS(x)		(((x) >> 4) & 0x03)
13654690Sobrien#define PN8_PSB_TO_BATT(x)		(((x) >> 6) & 0x03)
13753790Sobrien
13853790Sobrien/* ACPI ctr_val status register to powernow k8 configuration */
13953790Sobrien#define ACPI_PN8_CTRL_TO_FID(x)		((x) & 0x3f)
14053790Sobrien#define ACPI_PN8_CTRL_TO_VID(x)		(((x) >> 6) & 0x1f)
14153790Sobrien#define ACPI_PN8_CTRL_TO_VST(x)		(((x) >> 11) & 0x1f)
14253790Sobrien#define ACPI_PN8_CTRL_TO_MVS(x)		(((x) >> 18) & 0x03)
14353790Sobrien#define ACPI_PN8_CTRL_TO_PLL(x)		(((x) >> 20) & 0x7f)
14453790Sobrien#define ACPI_PN8_CTRL_TO_RVO(x)		(((x) >> 28) & 0x03)
14553790Sobrien#define ACPI_PN8_CTRL_TO_IRT(x)		(((x) >> 30) & 0x03)
14653790Sobrien
14754690Sobrien
14853790Sobrien#define WRITE_FIDVID(fid, vid, ctrl)	\
14953790Sobrien	wrmsr(MSR_AMDK7_FIDVID_CTL,	\
15053790Sobrien	    (((ctrl) << 32) | (1ULL << 16) | ((vid) << 8) | (fid)))
15153790Sobrien
15254690Sobrien#define COUNT_OFF_IRT(irt)	DELAY(10 * (1 << (irt)))
15353790Sobrien#define COUNT_OFF_VST(vst)	DELAY(20 * (vst))
15453790Sobrien
15553790Sobrien#define FID_TO_VCO_FID(fid)	\
15653790Sobrien	(((fid) < 8) ? (8 + ((fid) << 1)) : (fid))
15754690Sobrien
15853790Sobrien/*
15953790Sobrien * Divide each value by 10 to get the processor multiplier.
16053790Sobrien * Some of those tables are the same as the Linux powernow-k7
16153790Sobrien * implementation by Dave Jones.
16255628Sgroudier */
16354690Sobrienstatic int pn7_fid_to_mult[32] = {
16453790Sobrien	110, 115, 120, 125, 50, 55, 60, 65,
16553790Sobrien	70, 75, 80, 85, 90, 95, 100, 105,
16653790Sobrien	30, 190, 40, 200, 130, 135, 140, 210,
16754690Sobrien	150, 225, 160, 165, 170, 180, 0, 0,
16853790Sobrien};
16953790Sobrien
17053790Sobrien
17153790Sobrienstatic int pn8_fid_to_mult[64] = {
17254690Sobrien	40, 45, 50, 55, 60, 65, 70, 75,
17353790Sobrien	80, 85, 90, 95, 100, 105, 110, 115,
17453790Sobrien	120, 125, 130, 135, 140, 145, 150, 155,
17553790Sobrien	160, 165, 170, 175, 180, 185, 190, 195,
17653790Sobrien	200, 205, 210, 215, 220, 225, 230, 235,
17754690Sobrien	240, 245, 250, 255, 260, 265, 270, 275,
17853790Sobrien	280, 285, 290, 295, 300, 305, 310, 315,
17953790Sobrien	320, 325, 330, 335, 340, 345, 350, 355,
18053790Sobrien};
18154690Sobrien
18254690Sobrien/*
18354690Sobrien * Units are in mV.
18454690Sobrien */
18554690Sobrien/* Mobile VRM (K7) */
18653790Sobrienstatic int pn7_mobile_vid_to_volts[] = {
18754690Sobrien	2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650,
18854690Sobrien	1600, 1550, 1500, 1450, 1400, 1350, 1300, 0,
18954690Sobrien	1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100,
19053790Sobrien	1075, 1050, 1025, 1000, 975, 950, 925, 0,
19153790Sobrien};
19253790Sobrien/* Desktop VRM (K7) */
19353790Sobrienstatic int pn7_desktop_vid_to_volts[] = {
19454690Sobrien	2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650,
19553790Sobrien	1600, 1550, 1500, 1450, 1400, 1350, 1300, 0,
19653790Sobrien	1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100,
19753790Sobrien	1075, 1050, 1025, 1000, 975, 950, 925, 0,
19853790Sobrien};
19954690Sobrien/* Desktop and Mobile VRM (K8) */
20053790Sobrienstatic int pn8_vid_to_volts[] = {
20153790Sobrien	1550, 1525, 1500, 1475, 1450, 1425, 1400, 1375,
20254690Sobrien	1350, 1325, 1300, 1275, 1250, 1225, 1200, 1175,
20354690Sobrien	1150, 1125, 1100, 1075, 1050, 1025, 1000, 975,
20454690Sobrien	950, 925, 900, 875, 850, 825, 800, 0,
20554690Sobrien};
20654690Sobrien
20754690Sobrien#define POWERNOW_MAX_STATES		16
20854690Sobrien
20954690Sobrienstruct powernow_state {
21054690Sobrien	int freq;
21154690Sobrien	int power;
21254690Sobrien	int fid;
21354690Sobrien	int vid;
21454690Sobrien};
21554690Sobrien
21654690Sobrienstruct pn_softc {
21754690Sobrien	device_t		 dev;
21854690Sobrien	int			 pn_type;
21954690Sobrien	struct powernow_state	 powernow_states[POWERNOW_MAX_STATES];
22054690Sobrien	u_int			 fsb;
22154690Sobrien	u_int			 sgtc;
22254690Sobrien	u_int			 vst;
22354690Sobrien	u_int			 mvs;
22454690Sobrien	u_int			 pll;
22553790Sobrien	u_int			 rvo;
22654690Sobrien	u_int			 irt;
22754690Sobrien	int			 low;
22854690Sobrien	int			 powernow_max_states;
22953790Sobrien	u_int			 powernow_state;
23053790Sobrien	u_int			 errata;
23153790Sobrien	int			*vid_to_volts;
23253790Sobrien};
23354690Sobrien
23453790Sobrien/*
23553790Sobrien * Offsets in struct cf_setting array for private values given by
23653790Sobrien * acpi_perf driver.
23753790Sobrien */
23854690Sobrien#define PX_SPEC_CONTROL		0
23953790Sobrien#define PX_SPEC_STATUS		1
24053790Sobrien
24153790Sobrienstatic void	pn_identify(driver_t *driver, device_t parent);
24253790Sobrienstatic int	pn_probe(device_t dev);
24353790Sobrienstatic int	pn_attach(device_t dev);
24454690Sobrienstatic int	pn_detach(device_t dev);
24553790Sobrienstatic int	pn_set(device_t dev, const struct cf_setting *cf);
24653790Sobrienstatic int	pn_get(device_t dev, struct cf_setting *cf);
24753790Sobrienstatic int	pn_settings(device_t dev, struct cf_setting *sets,
24853790Sobrien		    int *count);
24953790Sobrienstatic int	pn_type(device_t dev, int *type);
25053790Sobrien
25154690Sobrienstatic device_method_t pn_methods[] = {
25254690Sobrien	/* Device interface */
25353790Sobrien	DEVMETHOD(device_identify, pn_identify),
25453790Sobrien	DEVMETHOD(device_probe, pn_probe),
25553790Sobrien	DEVMETHOD(device_attach, pn_attach),
25653790Sobrien	DEVMETHOD(device_detach, pn_detach),
25753790Sobrien
25853790Sobrien	/* cpufreq interface */
25953790Sobrien	DEVMETHOD(cpufreq_drv_set, pn_set),
26054690Sobrien	DEVMETHOD(cpufreq_drv_get, pn_get),
26153790Sobrien	DEVMETHOD(cpufreq_drv_settings, pn_settings),
26254690Sobrien	DEVMETHOD(cpufreq_drv_type, pn_type),
26354690Sobrien
26454690Sobrien	{0, 0}
26554690Sobrien};
26654690Sobrien
26754690Sobrienstatic devclass_t pn_devclass;
26854690Sobrienstatic driver_t pn_driver = {
26954690Sobrien	"powernow",
27054690Sobrien	pn_methods,
27154690Sobrien	sizeof(struct pn_softc),
27254690Sobrien};
27354690Sobrien
27454690SobrienDRIVER_MODULE(powernow, cpu, pn_driver, pn_devclass, 0, 0);
27554690Sobrien
27654690Sobrienstatic int
27754690Sobrienpn7_setfidvid(struct pn_softc *sc, int fid, int vid)
27854690Sobrien{
27954690Sobrien	int cfid, cvid;
28054690Sobrien	uint64_t status, ctl;
28154690Sobrien
28254690Sobrien	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
28354690Sobrien	cfid = PN7_STA_CFID(status);
28454690Sobrien	cvid = PN7_STA_CVID(status);
28554690Sobrien
28654690Sobrien	/* We're already at the requested level. */
28759743Sgroudier	if (fid == cfid && vid == cvid)
28859743Sgroudier		return (0);
28959743Sgroudier
29059743Sgroudier	ctl = rdmsr(MSR_AMDK7_FIDVID_CTL) & PN7_CTR_FIDCHRATIO;
29159743Sgroudier
29254690Sobrien	ctl |= PN7_CTR_FID(fid);
29354690Sobrien	ctl |= PN7_CTR_VID(vid);
29454690Sobrien	ctl |= PN7_CTR_SGTC(sc->sgtc);
29554690Sobrien
29654690Sobrien	if (sc->errata & A0_ERRATA)
29754690Sobrien		disable_intr();
29854690Sobrien
29954690Sobrien	if (pn7_fid_to_mult[fid] < pn7_fid_to_mult[cfid]) {
30054690Sobrien		wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC);
30153790Sobrien		if (vid != cvid)
302			wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC);
303	} else {
304		wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC);
305		if (fid != cfid)
306			wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC);
307	}
308
309	if (sc->errata & A0_ERRATA)
310		enable_intr();
311
312	return (0);
313}
314
315static int
316pn8_read_pending_wait(uint64_t *status)
317{
318	int i = 10000;
319
320	do
321		*status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
322	while (PN8_STA_PENDING(*status) && --i);
323
324	return (i == 0 ? ENXIO : 0);
325}
326
327static int
328pn8_write_fidvid(u_int fid, u_int vid, uint64_t ctrl, uint64_t *status)
329{
330	int i = 100;
331
332	do
333		WRITE_FIDVID(fid, vid, ctrl);
334	while (pn8_read_pending_wait(status) && --i);
335
336	return (i == 0 ? ENXIO : 0);
337}
338
339static int
340pn8_setfidvid(struct pn_softc *sc, int fid, int vid)
341{
342	uint64_t status;
343	int cfid, cvid;
344	int rvo;
345	int rv;
346	u_int val;
347
348	rv = pn8_read_pending_wait(&status);
349	if (rv)
350		return (rv);
351
352	cfid = PN8_STA_CFID(status);
353	cvid = PN8_STA_CVID(status);
354
355	if (fid == cfid && vid == cvid)
356		return (0);
357
358	/*
359	 * Phase 1: Raise core voltage to requested VID if frequency is
360	 * going up.
361	 */
362	while (cvid > vid) {
363		val = cvid - (1 << sc->mvs);
364		rv = pn8_write_fidvid(cfid, (val > 0) ? val : 0, 1ULL, &status);
365		if (rv) {
366			sc->errata |= PENDING_STUCK;
367			return (rv);
368		}
369		cvid = PN8_STA_CVID(status);
370		COUNT_OFF_VST(sc->vst);
371	}
372
373	/* ... then raise to voltage + RVO (if required) */
374	for (rvo = sc->rvo; rvo > 0 && cvid > 0; --rvo) {
375		/* XXX It's not clear from spec if we have to do that
376		 * in 0.25 step or in MVS.  Therefore do it as it's done
377		 * under Linux */
378		rv = pn8_write_fidvid(cfid, cvid - 1, 1ULL, &status);
379		if (rv) {
380			sc->errata |= PENDING_STUCK;
381			return (rv);
382		}
383		cvid = PN8_STA_CVID(status);
384		COUNT_OFF_VST(sc->vst);
385	}
386
387	/* Phase 2: change to requested core frequency */
388	if (cfid != fid) {
389		u_int vco_fid, vco_cfid, fid_delta;
390
391		vco_fid = FID_TO_VCO_FID(fid);
392		vco_cfid = FID_TO_VCO_FID(cfid);
393
394		while (abs(vco_fid - vco_cfid) > 2) {
395			fid_delta = (vco_cfid & 1) ? 1 : 2;
396			if (fid > cfid) {
397				if (cfid > 7)
398					val = cfid + fid_delta;
399				else
400					val = FID_TO_VCO_FID(cfid) + fid_delta;
401			} else
402				val = cfid - fid_delta;
403			rv = pn8_write_fidvid(val, cvid,
404			    sc->pll * (uint64_t) sc->fsb,
405			    &status);
406			if (rv) {
407				sc->errata |= PENDING_STUCK;
408				return (rv);
409			}
410			cfid = PN8_STA_CFID(status);
411			COUNT_OFF_IRT(sc->irt);
412
413			vco_cfid = FID_TO_VCO_FID(cfid);
414		}
415
416		rv = pn8_write_fidvid(fid, cvid,
417		    sc->pll * (uint64_t) sc->fsb,
418		    &status);
419		if (rv) {
420			sc->errata |= PENDING_STUCK;
421			return (rv);
422		}
423		cfid = PN8_STA_CFID(status);
424		COUNT_OFF_IRT(sc->irt);
425	}
426
427	/* Phase 3: change to requested voltage */
428	if (cvid != vid) {
429		rv = pn8_write_fidvid(cfid, vid, 1ULL, &status);
430		cvid = PN8_STA_CVID(status);
431		COUNT_OFF_VST(sc->vst);
432	}
433
434	/* Check if transition failed. */
435	if (cfid != fid || cvid != vid)
436		rv = ENXIO;
437
438	return (rv);
439}
440
441static int
442pn_set(device_t dev, const struct cf_setting *cf)
443{
444	struct pn_softc *sc;
445	int fid, vid;
446	int i;
447	int rv;
448
449	if (cf == NULL)
450		return (EINVAL);
451	sc = device_get_softc(dev);
452
453	if (sc->errata & PENDING_STUCK)
454		return (ENXIO);
455
456	for (i = 0; i < sc->powernow_max_states; ++i)
457		if (CPUFREQ_CMP(sc->powernow_states[i].freq / 1000, cf->freq))
458			break;
459
460	fid = sc->powernow_states[i].fid;
461	vid = sc->powernow_states[i].vid;
462
463	rv = ENODEV;
464
465	switch (sc->pn_type) {
466	case PN7_TYPE:
467		rv = pn7_setfidvid(sc, fid, vid);
468		break;
469	case PN8_TYPE:
470		rv = pn8_setfidvid(sc, fid, vid);
471		break;
472	}
473
474	return (rv);
475}
476
477static int
478pn_get(device_t dev, struct cf_setting *cf)
479{
480	struct pn_softc *sc;
481	u_int cfid = 0, cvid = 0;
482	int i;
483	uint64_t status;
484
485	if (cf == NULL)
486		return (EINVAL);
487	sc = device_get_softc(dev);
488	if (sc->errata & PENDING_STUCK)
489		return (ENXIO);
490
491	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
492
493	switch (sc->pn_type) {
494	case PN7_TYPE:
495		cfid = PN7_STA_CFID(status);
496		cvid = PN7_STA_CVID(status);
497		break;
498	case PN8_TYPE:
499		cfid = PN8_STA_CFID(status);
500		cvid = PN8_STA_CVID(status);
501		break;
502	}
503	for (i = 0; i < sc->powernow_max_states; ++i)
504		if (cfid == sc->powernow_states[i].fid &&
505		    cvid == sc->powernow_states[i].vid)
506			break;
507
508	if (i < sc->powernow_max_states) {
509		cf->freq = sc->powernow_states[i].freq / 1000;
510		cf->power = sc->powernow_states[i].power;
511		cf->lat = 200;
512		cf->volts = sc->vid_to_volts[cvid];
513		cf->dev = dev;
514	} else {
515		memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf));
516		cf->dev = NULL;
517	}
518
519	return (0);
520}
521
522static int
523pn_settings(device_t dev, struct cf_setting *sets, int *count)
524{
525	struct pn_softc *sc;
526	int i;
527
528	if (sets == NULL|| count == NULL)
529		return (EINVAL);
530	sc = device_get_softc(dev);
531	if (*count < sc->powernow_max_states)
532		return (E2BIG);
533	for (i = 0; i < sc->powernow_max_states; ++i) {
534		sets[i].freq = sc->powernow_states[i].freq / 1000;
535		sets[i].power = sc->powernow_states[i].power;
536		sets[i].lat = 200;
537		sets[i].volts = sc->vid_to_volts[sc->powernow_states[i].vid];
538		sets[i].dev = dev;
539	}
540	*count = sc->powernow_max_states;
541
542	return (0);
543}
544
545static int
546pn_type(device_t dev, int *type)
547{
548	if (type == NULL)
549		return (EINVAL);
550
551	*type = CPUFREQ_TYPE_ABSOLUTE;
552
553	return (0);
554}
555
556/*
557 * Given a set of pair of fid/vid, and number of performance states,
558 * compute powernow_states via an insertion sort.
559 */
560static int
561decode_pst(struct pn_softc *sc, uint8_t *p, int npstates)
562{
563	int i, j, n;
564	struct powernow_state state;
565
566	for (i = 0; i < POWERNOW_MAX_STATES; ++i)
567		sc->powernow_states[i].freq = CPUFREQ_VAL_UNKNOWN;
568
569	for (n = 0, i = 0; i < npstates; ++i) {
570		state.fid = *p++;
571		state.vid = *p++;
572		state.power = CPUFREQ_VAL_UNKNOWN;
573
574		switch (sc->pn_type) {
575		case PN7_TYPE:
576			state.freq = 100 * pn7_fid_to_mult[state.fid] * sc->fsb;
577			if ((sc->errata & A0_ERRATA) &&
578			    (pn7_fid_to_mult[state.fid] % 10) == 5)
579				continue;
580			break;
581		case PN8_TYPE:
582			state.freq = 100 * pn8_fid_to_mult[state.fid] * sc->fsb;
583			break;
584		}
585
586		j = n;
587		while (j > 0 && sc->powernow_states[j - 1].freq < state.freq) {
588			memcpy(&sc->powernow_states[j],
589			    &sc->powernow_states[j - 1],
590			    sizeof(struct powernow_state));
591			--j;
592		}
593		memcpy(&sc->powernow_states[j], &state,
594		    sizeof(struct powernow_state));
595		++n;
596	}
597
598	/*
599	 * Fix powernow_max_states, if errata a0 give us less states
600	 * than expected.
601	 */
602	sc->powernow_max_states = n;
603
604	if (bootverbose)
605		for (i = 0; i < sc->powernow_max_states; ++i) {
606			int fid = sc->powernow_states[i].fid;
607			int vid = sc->powernow_states[i].vid;
608
609			printf("powernow: %2i %8dkHz FID %02x VID %02x\n",
610			    i,
611			    sc->powernow_states[i].freq,
612			    fid,
613			    vid);
614		}
615
616	return (0);
617}
618
619static int
620cpuid_is_k7(u_int cpuid)
621{
622
623	switch (cpuid) {
624	case 0x760:
625	case 0x761:
626	case 0x762:
627	case 0x770:
628	case 0x771:
629	case 0x780:
630	case 0x781:
631	case 0x7a0:
632		return (TRUE);
633	}
634	return (FALSE);
635}
636
637static int
638pn_decode_pst(device_t dev)
639{
640	int maxpst;
641	struct pn_softc *sc;
642	u_int cpuid, maxfid, startvid;
643	u_long sig;
644	struct psb_header *psb;
645	uint8_t *p;
646	u_int regs[4];
647	uint64_t status;
648
649	sc = device_get_softc(dev);
650
651	do_cpuid(0x80000001, regs);
652	cpuid = regs[0];
653
654	if ((cpuid & 0xfff) == 0x760)
655		sc->errata |= A0_ERRATA;
656
657	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
658
659	switch (sc->pn_type) {
660	case PN7_TYPE:
661		maxfid = PN7_STA_MFID(status);
662		startvid = PN7_STA_SVID(status);
663		break;
664	case PN8_TYPE:
665		maxfid = PN8_STA_MFID(status);
666		/*
667		 * we should actually use a variable named 'maxvid' if K8,
668		 * but why introducing a new variable for that?
669		 */
670		startvid = PN8_STA_MVID(status);
671		break;
672	default:
673		return (ENODEV);
674	}
675
676	if (bootverbose) {
677		device_printf(dev, "STATUS: 0x%jx\n", status);
678		device_printf(dev, "STATUS: maxfid: 0x%02x\n", maxfid);
679		device_printf(dev, "STATUS: %s: 0x%02x\n",
680		    sc->pn_type == PN7_TYPE ? "startvid" : "maxvid",
681		    startvid);
682	}
683
684	sig = bios_sigsearch(PSB_START, PSB_SIG, PSB_LEN, PSB_STEP, PSB_OFF);
685	if (sig) {
686		struct pst_header *pst;
687
688		psb = (struct psb_header*)(uintptr_t)BIOS_PADDRTOVADDR(sig);
689
690		switch (psb->version) {
691		default:
692			return (ENODEV);
693		case 0x14:
694			/*
695			 * We can't be picky about numpst since at least
696			 * some systems have a value of 1 and some have 2.
697			 * We trust that cpuid_is_k7() will be better at
698			 * catching that we're on a K8 anyway.
699			 */
700			if (sc->pn_type != PN8_TYPE)
701				return (EINVAL);
702			sc->vst = psb->settlingtime;
703			sc->rvo = PN8_PSB_TO_RVO(psb->res1),
704			sc->irt = PN8_PSB_TO_IRT(psb->res1),
705			sc->mvs = PN8_PSB_TO_MVS(psb->res1),
706			sc->low = PN8_PSB_TO_BATT(psb->res1);
707			if (bootverbose) {
708				device_printf(dev, "PSB: VST: %d\n",
709				    psb->settlingtime);
710				device_printf(dev, "PSB: RVO %x IRT %d "
711				    "MVS %d BATT %d\n",
712				    sc->rvo,
713				    sc->irt,
714				    sc->mvs,
715				    sc->low);
716			}
717			break;
718		case 0x12:
719			if (sc->pn_type != PN7_TYPE)
720				return (EINVAL);
721			sc->sgtc = psb->settlingtime * sc->fsb;
722			if (sc->sgtc < 100 * sc->fsb)
723				sc->sgtc = 100 * sc->fsb;
724			break;
725		}
726
727		p = ((uint8_t *) psb) + sizeof(struct psb_header);
728		pst = (struct pst_header*) p;
729
730		maxpst = 200;
731
732		do {
733			struct pst_header *pst = (struct pst_header*) p;
734
735			if (cpuid == pst->cpuid &&
736			    maxfid == pst->maxfid &&
737			    startvid == pst->startvid) {
738				sc->powernow_max_states = pst->numpstates;
739				switch (sc->pn_type) {
740				case PN7_TYPE:
741					if (abs(sc->fsb - pst->fsb) > 5)
742						continue;
743					break;
744				case PN8_TYPE:
745					break;
746				}
747				return (decode_pst(sc,
748				    p + sizeof(struct pst_header),
749				    sc->powernow_max_states));
750			}
751
752			p += sizeof(struct pst_header) + (2 * pst->numpstates);
753		} while (cpuid_is_k7(pst->cpuid) && maxpst--);
754
755		device_printf(dev, "no match for extended cpuid %.3x\n", cpuid);
756	}
757
758	return (ENODEV);
759}
760
761static int
762pn_decode_acpi(device_t dev, device_t perf_dev)
763{
764	int i, j, n;
765	uint64_t status;
766	uint32_t ctrl;
767	u_int cpuid;
768	u_int regs[4];
769	struct pn_softc *sc;
770	struct powernow_state state;
771	struct cf_setting sets[POWERNOW_MAX_STATES];
772	int count = POWERNOW_MAX_STATES;
773	int type;
774	int rv;
775
776	if (perf_dev == NULL)
777		return (ENXIO);
778
779	rv = CPUFREQ_DRV_SETTINGS(perf_dev, sets, &count);
780	if (rv)
781		return (ENXIO);
782	rv = CPUFREQ_DRV_TYPE(perf_dev, &type);
783	if (rv || (type & CPUFREQ_FLAG_INFO_ONLY) == 0)
784		return (ENXIO);
785
786	sc = device_get_softc(dev);
787
788	do_cpuid(0x80000001, regs);
789	cpuid = regs[0];
790	if ((cpuid & 0xfff) == 0x760)
791		sc->errata |= A0_ERRATA;
792
793	ctrl = 0;
794	sc->sgtc = 0;
795	for (n = 0, i = 0; i < count; ++i) {
796		ctrl = sets[i].spec[PX_SPEC_CONTROL];
797		switch (sc->pn_type) {
798		case PN7_TYPE:
799			state.fid = ACPI_PN7_CTRL_TO_FID(ctrl);
800			state.vid = ACPI_PN7_CTRL_TO_VID(ctrl);
801			if ((sc->errata & A0_ERRATA) &&
802			    (pn7_fid_to_mult[state.fid] % 10) == 5)
803				continue;
804			state.freq = 100 * pn7_fid_to_mult[state.fid] * sc->fsb;
805			break;
806		case PN8_TYPE:
807			state.fid = ACPI_PN8_CTRL_TO_FID(ctrl);
808			state.vid = ACPI_PN8_CTRL_TO_VID(ctrl);
809			state.freq = 100 * pn8_fid_to_mult[state.fid] * sc->fsb;
810			break;
811		}
812
813		state.power = sets[i].power;
814
815		j = n;
816		while (j > 0 && sc->powernow_states[j - 1].freq < state.freq) {
817			memcpy(&sc->powernow_states[j],
818			    &sc->powernow_states[j - 1],
819			    sizeof(struct powernow_state));
820			--j;
821		}
822		memcpy(&sc->powernow_states[j], &state,
823		    sizeof(struct powernow_state));
824		++n;
825	}
826
827	sc->powernow_max_states = n;
828	state = sc->powernow_states[0];
829	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
830
831	switch (sc->pn_type) {
832	case PN7_TYPE:
833		sc->sgtc = ACPI_PN7_CTRL_TO_SGTC(ctrl);
834		/*
835		 * XXX Some bios forget the max frequency!
836		 * This maybe indicates we have the wrong tables.  Therefore,
837		 * don't implement a quirk, but fallback to BIOS legacy
838		 * tables instead.
839		 */
840		if (PN7_STA_MFID(status) != state.fid) {
841			device_printf(dev, "ACPI MAX frequency not found\n");
842			return (EINVAL);
843		}
844		break;
845	case PN8_TYPE:
846		sc->vst = ACPI_PN8_CTRL_TO_VST(ctrl),
847		sc->mvs = ACPI_PN8_CTRL_TO_MVS(ctrl),
848		sc->pll = ACPI_PN8_CTRL_TO_PLL(ctrl),
849		sc->rvo = ACPI_PN8_CTRL_TO_RVO(ctrl),
850		sc->irt = ACPI_PN8_CTRL_TO_IRT(ctrl);
851		sc->low = 0; /* XXX */
852
853		/*
854		 * powernow k8 supports only one low frequency.
855		 */
856		if (sc->powernow_max_states >= 2 &&
857		    (sc->powernow_states[sc->powernow_max_states - 2].fid < 8))
858			return (EINVAL);
859		break;
860	}
861
862	return (0);
863}
864
865static void
866pn_identify(driver_t *driver, device_t parent)
867{
868	device_t child;
869
870	if ((amd_pminfo & AMDPM_FID) == 0 || (amd_pminfo & AMDPM_VID) == 0)
871		return;
872	switch (cpu_id & 0xf00) {
873	case 0x600:
874	case 0xf00:
875		break;
876	default:
877		return;
878	}
879	if (device_find_child(parent, "powernow", -1) != NULL)
880		return;
881	if ((child = BUS_ADD_CHILD(parent, 10, "powernow", -1)) == NULL)
882		device_printf(parent, "powernow: add child failed\n");
883}
884
885static int
886pn_probe(device_t dev)
887{
888	struct pn_softc *sc;
889	uint64_t status;
890	uint64_t rate;
891	struct pcpu *pc;
892	u_int sfid, mfid, cfid;
893
894	sc = device_get_softc(dev);
895	sc->errata = 0;
896	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
897
898	pc = cpu_get_pcpu(dev);
899	if (pc == NULL)
900		return (ENODEV);
901
902	cpu_est_clockrate(pc->pc_cpuid, &rate);
903
904	switch (cpu_id & 0xf00) {
905	case 0x600:
906		sfid = PN7_STA_SFID(status);
907		mfid = PN7_STA_MFID(status);
908		cfid = PN7_STA_CFID(status);
909		sc->pn_type = PN7_TYPE;
910		sc->fsb = rate / 100000 / pn7_fid_to_mult[cfid];
911
912		/*
913		 * If start FID is different to max FID, then it is a
914		 * mobile processor.  If not, it is a low powered desktop
915		 * processor.
916		 */
917		if (PN7_STA_SFID(status) != PN7_STA_MFID(status)) {
918			sc->vid_to_volts = pn7_mobile_vid_to_volts;
919			device_set_desc(dev, "PowerNow! K7");
920		} else {
921			sc->vid_to_volts = pn7_desktop_vid_to_volts;
922			device_set_desc(dev, "Cool`n'Quiet K7");
923		}
924		break;
925
926	case 0xf00:
927		sfid = PN8_STA_SFID(status);
928		mfid = PN8_STA_MFID(status);
929		cfid = PN8_STA_CFID(status);
930		sc->pn_type = PN8_TYPE;
931		sc->vid_to_volts = pn8_vid_to_volts;
932		sc->fsb = rate / 100000 / pn8_fid_to_mult[cfid];
933
934		if (PN8_STA_SFID(status) != PN8_STA_MFID(status))
935			device_set_desc(dev, "PowerNow! K8");
936		else
937			device_set_desc(dev, "Cool`n'Quiet K8");
938		break;
939	default:
940		return (ENODEV);
941	}
942
943	return (0);
944}
945
946static int
947pn_attach(device_t dev)
948{
949	int rv;
950	device_t child;
951
952	child = device_find_child(device_get_parent(dev), "acpi_perf", -1);
953	if (child) {
954		rv = pn_decode_acpi(dev, child);
955		if (rv)
956			rv = pn_decode_pst(dev);
957	} else
958		rv = pn_decode_pst(dev);
959
960	if (rv != 0)
961		return (ENXIO);
962	cpufreq_register(dev);
963	return (0);
964}
965
966static int
967pn_detach(device_t dev)
968{
969
970	return (cpufreq_unregister(dev));
971}
972