xref: /freebsd/sys/x86/cpufreq/powernow.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
132580301SAttilio Rao /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3ebf5747bSPedro F. Giffuni  *
432580301SAttilio Rao  * Copyright (c) 2004-2005 Bruno Ducrot
532580301SAttilio Rao  * Copyright (c) 2004 FUKUDA Nobuhiko <nfukuda@spa.is.uec.ac.jp>
632580301SAttilio Rao  *
732580301SAttilio Rao  * Redistribution and use in source and binary forms, with or without
832580301SAttilio Rao  * modification, are permitted provided that the following conditions
932580301SAttilio Rao  * are met:
1032580301SAttilio Rao  * 1. Redistributions of source code must retain the above copyright
1132580301SAttilio Rao  *    notice, this list of conditions and the following disclaimer.
1232580301SAttilio Rao  * 2. Redistributions in binary form must reproduce the above copyright
1332580301SAttilio Rao  *    notice, this list of conditions and the following disclaimer in the
1432580301SAttilio Rao  *    documentation and/or other materials provided with the distribution.
1532580301SAttilio Rao  *
1632580301SAttilio Rao  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1732580301SAttilio Rao  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1832580301SAttilio Rao  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1932580301SAttilio Rao  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2032580301SAttilio Rao  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2132580301SAttilio Rao  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2232580301SAttilio Rao  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2332580301SAttilio Rao  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2432580301SAttilio Rao  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2532580301SAttilio Rao  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2632580301SAttilio Rao  */
2732580301SAttilio Rao 
2832580301SAttilio Rao /*
2932580301SAttilio Rao  * Many thanks to Nate Lawson for his helpful comments on this driver and
3032580301SAttilio Rao  * to Jung-uk Kim for testing.
3132580301SAttilio Rao  */
3232580301SAttilio Rao 
3332580301SAttilio Rao #include <sys/param.h>
3432580301SAttilio Rao #include <sys/bus.h>
3532580301SAttilio Rao #include <sys/cpu.h>
3632580301SAttilio Rao #include <sys/kernel.h>
3732580301SAttilio Rao #include <sys/malloc.h>
3832580301SAttilio Rao #include <sys/module.h>
3932580301SAttilio Rao #include <sys/pcpu.h>
4032580301SAttilio Rao #include <sys/systm.h>
4132580301SAttilio Rao 
4232580301SAttilio Rao #include <machine/pc/bios.h>
4332580301SAttilio Rao #include <machine/md_var.h>
4432580301SAttilio Rao #include <machine/specialreg.h>
4532580301SAttilio Rao #include <machine/cputypes.h>
4632580301SAttilio Rao #include <machine/vmparam.h>
4732580301SAttilio Rao #include <sys/rman.h>
4832580301SAttilio Rao 
4932580301SAttilio Rao #include <vm/vm.h>
5032580301SAttilio Rao #include <vm/pmap.h>
5132580301SAttilio Rao 
5232580301SAttilio Rao #include "cpufreq_if.h"
5332580301SAttilio Rao 
5432580301SAttilio Rao #define PN7_TYPE	0
5532580301SAttilio Rao #define PN8_TYPE	1
5632580301SAttilio Rao 
5732580301SAttilio Rao /* Flags for some hardware bugs. */
5832580301SAttilio Rao #define A0_ERRATA	0x1	/* Bugs for the rev. A0 of Athlon (K7):
5932580301SAttilio Rao 				 * Interrupts must be disabled and no half
6032580301SAttilio Rao 				 * multipliers are allowed */
6132580301SAttilio Rao #define PENDING_STUCK	0x2	/* With some buggy chipset and some newer AMD64
6232580301SAttilio Rao 				 * processor (Rev. G?):
6332580301SAttilio Rao 				 * the pending bit from the msr FIDVID_STATUS
6432580301SAttilio Rao 				 * is set forever.  No workaround :( */
6532580301SAttilio Rao 
6632580301SAttilio Rao /* Legacy configuration via BIOS table PSB. */
6732580301SAttilio Rao #define PSB_START	0
6832580301SAttilio Rao #define PSB_STEP	0x10
6932580301SAttilio Rao #define PSB_SIG		"AMDK7PNOW!"
7032580301SAttilio Rao #define PSB_LEN		10
7132580301SAttilio Rao #define PSB_OFF		0
7232580301SAttilio Rao 
7332580301SAttilio Rao struct psb_header {
7432580301SAttilio Rao 	char		 signature[10];
7532580301SAttilio Rao 	uint8_t		 version;
7632580301SAttilio Rao 	uint8_t		 flags;
7732580301SAttilio Rao 	uint16_t	 settlingtime;
7832580301SAttilio Rao 	uint8_t		 res1;
7932580301SAttilio Rao 	uint8_t		 numpst;
8032580301SAttilio Rao } __packed;
8132580301SAttilio Rao 
8232580301SAttilio Rao struct pst_header {
8332580301SAttilio Rao 	uint32_t	 cpuid;
8432580301SAttilio Rao 	uint8_t		 fsb;
8532580301SAttilio Rao 	uint8_t		 maxfid;
8632580301SAttilio Rao 	uint8_t		 startvid;
8732580301SAttilio Rao 	uint8_t		 numpstates;
8832580301SAttilio Rao } __packed;
8932580301SAttilio Rao 
9032580301SAttilio Rao /*
9132580301SAttilio Rao  * MSRs and bits used by Powernow technology
9232580301SAttilio Rao  */
9332580301SAttilio Rao #define MSR_AMDK7_FIDVID_CTL		0xc0010041
9432580301SAttilio Rao #define MSR_AMDK7_FIDVID_STATUS		0xc0010042
9532580301SAttilio Rao 
9632580301SAttilio Rao /* Bitfields used by K7 */
9732580301SAttilio Rao 
9832580301SAttilio Rao #define PN7_CTR_FID(x)			((x) & 0x1f)
9932580301SAttilio Rao #define PN7_CTR_VID(x)			(((x) & 0x1f) << 8)
10032580301SAttilio Rao #define PN7_CTR_FIDC			0x00010000
10132580301SAttilio Rao #define PN7_CTR_VIDC			0x00020000
10232580301SAttilio Rao #define PN7_CTR_FIDCHRATIO		0x00100000
10332580301SAttilio Rao #define PN7_CTR_SGTC(x)			(((uint64_t)(x) & 0x000fffff) << 32)
10432580301SAttilio Rao 
10532580301SAttilio Rao #define PN7_STA_CFID(x)			((x) & 0x1f)
10632580301SAttilio Rao #define PN7_STA_SFID(x)			(((x) >> 8) & 0x1f)
10732580301SAttilio Rao #define PN7_STA_MFID(x)			(((x) >> 16) & 0x1f)
10832580301SAttilio Rao #define PN7_STA_CVID(x)			(((x) >> 32) & 0x1f)
10932580301SAttilio Rao #define PN7_STA_SVID(x)			(((x) >> 40) & 0x1f)
11032580301SAttilio Rao #define PN7_STA_MVID(x)			(((x) >> 48) & 0x1f)
11132580301SAttilio Rao 
11232580301SAttilio Rao /* ACPI ctr_val status register to powernow k7 configuration */
11332580301SAttilio Rao #define ACPI_PN7_CTRL_TO_FID(x)		((x) & 0x1f)
11432580301SAttilio Rao #define ACPI_PN7_CTRL_TO_VID(x)		(((x) >> 5) & 0x1f)
11532580301SAttilio Rao #define ACPI_PN7_CTRL_TO_SGTC(x)	(((x) >> 10) & 0xffff)
11632580301SAttilio Rao 
11732580301SAttilio Rao /* Bitfields used by K8 */
11832580301SAttilio Rao 
11932580301SAttilio Rao #define PN8_CTR_FID(x)			((x) & 0x3f)
12032580301SAttilio Rao #define PN8_CTR_VID(x)			(((x) & 0x1f) << 8)
12132580301SAttilio Rao #define PN8_CTR_PENDING(x)		(((x) & 1) << 32)
12232580301SAttilio Rao 
12332580301SAttilio Rao #define PN8_STA_CFID(x)			((x) & 0x3f)
12432580301SAttilio Rao #define PN8_STA_SFID(x)			(((x) >> 8) & 0x3f)
12532580301SAttilio Rao #define PN8_STA_MFID(x)			(((x) >> 16) & 0x3f)
12632580301SAttilio Rao #define PN8_STA_PENDING(x)		(((x) >> 31) & 0x01)
12732580301SAttilio Rao #define PN8_STA_CVID(x)			(((x) >> 32) & 0x1f)
12832580301SAttilio Rao #define PN8_STA_SVID(x)			(((x) >> 40) & 0x1f)
12932580301SAttilio Rao #define PN8_STA_MVID(x)			(((x) >> 48) & 0x1f)
13032580301SAttilio Rao 
13132580301SAttilio Rao /* Reserved1 to powernow k8 configuration */
13232580301SAttilio Rao #define PN8_PSB_TO_RVO(x)		((x) & 0x03)
13332580301SAttilio Rao #define PN8_PSB_TO_IRT(x)		(((x) >> 2) & 0x03)
13432580301SAttilio Rao #define PN8_PSB_TO_MVS(x)		(((x) >> 4) & 0x03)
13532580301SAttilio Rao #define PN8_PSB_TO_BATT(x)		(((x) >> 6) & 0x03)
13632580301SAttilio Rao 
13732580301SAttilio Rao /* ACPI ctr_val status register to powernow k8 configuration */
13832580301SAttilio Rao #define ACPI_PN8_CTRL_TO_FID(x)		((x) & 0x3f)
13932580301SAttilio Rao #define ACPI_PN8_CTRL_TO_VID(x)		(((x) >> 6) & 0x1f)
14032580301SAttilio Rao #define ACPI_PN8_CTRL_TO_VST(x)		(((x) >> 11) & 0x1f)
14132580301SAttilio Rao #define ACPI_PN8_CTRL_TO_MVS(x)		(((x) >> 18) & 0x03)
14232580301SAttilio Rao #define ACPI_PN8_CTRL_TO_PLL(x)		(((x) >> 20) & 0x7f)
14332580301SAttilio Rao #define ACPI_PN8_CTRL_TO_RVO(x)		(((x) >> 28) & 0x03)
14432580301SAttilio Rao #define ACPI_PN8_CTRL_TO_IRT(x)		(((x) >> 30) & 0x03)
14532580301SAttilio Rao 
14632580301SAttilio Rao #define WRITE_FIDVID(fid, vid, ctrl)	\
14732580301SAttilio Rao 	wrmsr(MSR_AMDK7_FIDVID_CTL,	\
14832580301SAttilio Rao 	    (((ctrl) << 32) | (1ULL << 16) | ((vid) << 8) | (fid)))
14932580301SAttilio Rao 
15032580301SAttilio Rao #define COUNT_OFF_IRT(irt)	DELAY(10 * (1 << (irt)))
15132580301SAttilio Rao #define COUNT_OFF_VST(vst)	DELAY(20 * (vst))
15232580301SAttilio Rao 
15332580301SAttilio Rao #define FID_TO_VCO_FID(fid)	\
15432580301SAttilio Rao 	(((fid) < 8) ? (8 + ((fid) << 1)) : (fid))
15532580301SAttilio Rao 
15632580301SAttilio Rao /*
15732580301SAttilio Rao  * Divide each value by 10 to get the processor multiplier.
15832580301SAttilio Rao  * Some of those tables are the same as the Linux powernow-k7
15932580301SAttilio Rao  * implementation by Dave Jones.
16032580301SAttilio Rao  */
16132580301SAttilio Rao static int pn7_fid_to_mult[32] = {
16232580301SAttilio Rao 	110, 115, 120, 125, 50, 55, 60, 65,
16332580301SAttilio Rao 	70, 75, 80, 85, 90, 95, 100, 105,
16432580301SAttilio Rao 	30, 190, 40, 200, 130, 135, 140, 210,
16532580301SAttilio Rao 	150, 225, 160, 165, 170, 180, 0, 0,
16632580301SAttilio Rao };
16732580301SAttilio Rao 
16832580301SAttilio Rao static int pn8_fid_to_mult[64] = {
16932580301SAttilio Rao 	40, 45, 50, 55, 60, 65, 70, 75,
17032580301SAttilio Rao 	80, 85, 90, 95, 100, 105, 110, 115,
17132580301SAttilio Rao 	120, 125, 130, 135, 140, 145, 150, 155,
17232580301SAttilio Rao 	160, 165, 170, 175, 180, 185, 190, 195,
17332580301SAttilio Rao 	200, 205, 210, 215, 220, 225, 230, 235,
17432580301SAttilio Rao 	240, 245, 250, 255, 260, 265, 270, 275,
17532580301SAttilio Rao 	280, 285, 290, 295, 300, 305, 310, 315,
17632580301SAttilio Rao 	320, 325, 330, 335, 340, 345, 350, 355,
17732580301SAttilio Rao };
17832580301SAttilio Rao 
17932580301SAttilio Rao /*
18032580301SAttilio Rao  * Units are in mV.
18132580301SAttilio Rao  */
18232580301SAttilio Rao /* Mobile VRM (K7) */
18332580301SAttilio Rao static int pn7_mobile_vid_to_volts[] = {
18432580301SAttilio Rao 	2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650,
18532580301SAttilio Rao 	1600, 1550, 1500, 1450, 1400, 1350, 1300, 0,
18632580301SAttilio Rao 	1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100,
18732580301SAttilio Rao 	1075, 1050, 1025, 1000, 975, 950, 925, 0,
18832580301SAttilio Rao };
18932580301SAttilio Rao /* Desktop VRM (K7) */
19032580301SAttilio Rao static int pn7_desktop_vid_to_volts[] = {
19132580301SAttilio Rao 	2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650,
19232580301SAttilio Rao 	1600, 1550, 1500, 1450, 1400, 1350, 1300, 0,
19332580301SAttilio Rao 	1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100,
19432580301SAttilio Rao 	1075, 1050, 1025, 1000, 975, 950, 925, 0,
19532580301SAttilio Rao };
19632580301SAttilio Rao /* Desktop and Mobile VRM (K8) */
19732580301SAttilio Rao static int pn8_vid_to_volts[] = {
19832580301SAttilio Rao 	1550, 1525, 1500, 1475, 1450, 1425, 1400, 1375,
19932580301SAttilio Rao 	1350, 1325, 1300, 1275, 1250, 1225, 1200, 1175,
20032580301SAttilio Rao 	1150, 1125, 1100, 1075, 1050, 1025, 1000, 975,
20132580301SAttilio Rao 	950, 925, 900, 875, 850, 825, 800, 0,
20232580301SAttilio Rao };
20332580301SAttilio Rao 
20432580301SAttilio Rao #define POWERNOW_MAX_STATES		16
20532580301SAttilio Rao 
20632580301SAttilio Rao struct powernow_state {
20732580301SAttilio Rao 	int freq;
20832580301SAttilio Rao 	int power;
20932580301SAttilio Rao 	int fid;
21032580301SAttilio Rao 	int vid;
21132580301SAttilio Rao };
21232580301SAttilio Rao 
21332580301SAttilio Rao struct pn_softc {
21432580301SAttilio Rao 	device_t		 dev;
21532580301SAttilio Rao 	int			 pn_type;
21632580301SAttilio Rao 	struct powernow_state	 powernow_states[POWERNOW_MAX_STATES];
21732580301SAttilio Rao 	u_int			 fsb;
21832580301SAttilio Rao 	u_int			 sgtc;
21932580301SAttilio Rao 	u_int			 vst;
22032580301SAttilio Rao 	u_int			 mvs;
22132580301SAttilio Rao 	u_int			 pll;
22232580301SAttilio Rao 	u_int			 rvo;
22332580301SAttilio Rao 	u_int			 irt;
22432580301SAttilio Rao 	int			 low;
22532580301SAttilio Rao 	int			 powernow_max_states;
22632580301SAttilio Rao 	u_int			 powernow_state;
22732580301SAttilio Rao 	u_int			 errata;
22832580301SAttilio Rao 	int			*vid_to_volts;
22932580301SAttilio Rao };
23032580301SAttilio Rao 
23132580301SAttilio Rao /*
23232580301SAttilio Rao  * Offsets in struct cf_setting array for private values given by
23332580301SAttilio Rao  * acpi_perf driver.
23432580301SAttilio Rao  */
23532580301SAttilio Rao #define PX_SPEC_CONTROL		0
23632580301SAttilio Rao #define PX_SPEC_STATUS		1
23732580301SAttilio Rao 
23832580301SAttilio Rao static void	pn_identify(driver_t *driver, device_t parent);
23932580301SAttilio Rao static int	pn_probe(device_t dev);
24032580301SAttilio Rao static int	pn_attach(device_t dev);
24132580301SAttilio Rao static int	pn_detach(device_t dev);
24232580301SAttilio Rao static int	pn_set(device_t dev, const struct cf_setting *cf);
24332580301SAttilio Rao static int	pn_get(device_t dev, struct cf_setting *cf);
24432580301SAttilio Rao static int	pn_settings(device_t dev, struct cf_setting *sets,
24532580301SAttilio Rao 		    int *count);
24632580301SAttilio Rao static int	pn_type(device_t dev, int *type);
24732580301SAttilio Rao 
24832580301SAttilio Rao static device_method_t pn_methods[] = {
24932580301SAttilio Rao 	/* Device interface */
25032580301SAttilio Rao 	DEVMETHOD(device_identify, pn_identify),
25132580301SAttilio Rao 	DEVMETHOD(device_probe, pn_probe),
25232580301SAttilio Rao 	DEVMETHOD(device_attach, pn_attach),
25332580301SAttilio Rao 	DEVMETHOD(device_detach, pn_detach),
25432580301SAttilio Rao 
25532580301SAttilio Rao 	/* cpufreq interface */
25632580301SAttilio Rao 	DEVMETHOD(cpufreq_drv_set, pn_set),
25732580301SAttilio Rao 	DEVMETHOD(cpufreq_drv_get, pn_get),
25832580301SAttilio Rao 	DEVMETHOD(cpufreq_drv_settings, pn_settings),
25932580301SAttilio Rao 	DEVMETHOD(cpufreq_drv_type, pn_type),
26032580301SAttilio Rao 	{0, 0}
26132580301SAttilio Rao };
26232580301SAttilio Rao 
26332580301SAttilio Rao static driver_t pn_driver = {
26432580301SAttilio Rao 	"powernow",
26532580301SAttilio Rao 	pn_methods,
26632580301SAttilio Rao 	sizeof(struct pn_softc),
26732580301SAttilio Rao };
26832580301SAttilio Rao 
269b3407dccSJohn Baldwin DRIVER_MODULE(powernow, cpu, pn_driver, 0, 0);
27032580301SAttilio Rao 
27132580301SAttilio Rao static int
pn7_setfidvid(struct pn_softc * sc,int fid,int vid)27232580301SAttilio Rao pn7_setfidvid(struct pn_softc *sc, int fid, int vid)
27332580301SAttilio Rao {
27432580301SAttilio Rao 	int cfid, cvid;
27532580301SAttilio Rao 	uint64_t status, ctl;
27632580301SAttilio Rao 
27732580301SAttilio Rao 	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
27832580301SAttilio Rao 	cfid = PN7_STA_CFID(status);
27932580301SAttilio Rao 	cvid = PN7_STA_CVID(status);
28032580301SAttilio Rao 
28132580301SAttilio Rao 	/* We're already at the requested level. */
28232580301SAttilio Rao 	if (fid == cfid && vid == cvid)
28332580301SAttilio Rao 		return (0);
28432580301SAttilio Rao 
28532580301SAttilio Rao 	ctl = rdmsr(MSR_AMDK7_FIDVID_CTL) & PN7_CTR_FIDCHRATIO;
28632580301SAttilio Rao 
28732580301SAttilio Rao 	ctl |= PN7_CTR_FID(fid);
28832580301SAttilio Rao 	ctl |= PN7_CTR_VID(vid);
28932580301SAttilio Rao 	ctl |= PN7_CTR_SGTC(sc->sgtc);
29032580301SAttilio Rao 
29132580301SAttilio Rao 	if (sc->errata & A0_ERRATA)
29232580301SAttilio Rao 		disable_intr();
29332580301SAttilio Rao 
29432580301SAttilio Rao 	if (pn7_fid_to_mult[fid] < pn7_fid_to_mult[cfid]) {
29532580301SAttilio Rao 		wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC);
29632580301SAttilio Rao 		if (vid != cvid)
29732580301SAttilio Rao 			wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC);
29832580301SAttilio Rao 	} else {
29932580301SAttilio Rao 		wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC);
30032580301SAttilio Rao 		if (fid != cfid)
30132580301SAttilio Rao 			wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC);
30232580301SAttilio Rao 	}
30332580301SAttilio Rao 
30432580301SAttilio Rao 	if (sc->errata & A0_ERRATA)
30532580301SAttilio Rao 		enable_intr();
30632580301SAttilio Rao 
30732580301SAttilio Rao 	return (0);
30832580301SAttilio Rao }
30932580301SAttilio Rao 
31032580301SAttilio Rao static int
pn8_read_pending_wait(uint64_t * status)31132580301SAttilio Rao pn8_read_pending_wait(uint64_t *status)
31232580301SAttilio Rao {
31332580301SAttilio Rao 	int i = 10000;
31432580301SAttilio Rao 
31532580301SAttilio Rao 	do
31632580301SAttilio Rao 		*status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
31732580301SAttilio Rao 	while (PN8_STA_PENDING(*status) && --i);
31832580301SAttilio Rao 
31932580301SAttilio Rao 	return (i == 0 ? ENXIO : 0);
32032580301SAttilio Rao }
32132580301SAttilio Rao 
32232580301SAttilio Rao static int
pn8_write_fidvid(u_int fid,u_int vid,uint64_t ctrl,uint64_t * status)32332580301SAttilio Rao pn8_write_fidvid(u_int fid, u_int vid, uint64_t ctrl, uint64_t *status)
32432580301SAttilio Rao {
32532580301SAttilio Rao 	int i = 100;
32632580301SAttilio Rao 
32732580301SAttilio Rao 	do
32832580301SAttilio Rao 		WRITE_FIDVID(fid, vid, ctrl);
32932580301SAttilio Rao 	while (pn8_read_pending_wait(status) && --i);
33032580301SAttilio Rao 
33132580301SAttilio Rao 	return (i == 0 ? ENXIO : 0);
33232580301SAttilio Rao }
33332580301SAttilio Rao 
33432580301SAttilio Rao static int
pn8_setfidvid(struct pn_softc * sc,int fid,int vid)33532580301SAttilio Rao pn8_setfidvid(struct pn_softc *sc, int fid, int vid)
33632580301SAttilio Rao {
33732580301SAttilio Rao 	uint64_t status;
33832580301SAttilio Rao 	int cfid, cvid;
33932580301SAttilio Rao 	int rvo;
34032580301SAttilio Rao 	int rv;
34132580301SAttilio Rao 	u_int val;
34232580301SAttilio Rao 
34332580301SAttilio Rao 	rv = pn8_read_pending_wait(&status);
34432580301SAttilio Rao 	if (rv)
34532580301SAttilio Rao 		return (rv);
34632580301SAttilio Rao 
34732580301SAttilio Rao 	cfid = PN8_STA_CFID(status);
34832580301SAttilio Rao 	cvid = PN8_STA_CVID(status);
34932580301SAttilio Rao 
35032580301SAttilio Rao 	if (fid == cfid && vid == cvid)
35132580301SAttilio Rao 		return (0);
35232580301SAttilio Rao 
35332580301SAttilio Rao 	/*
35432580301SAttilio Rao 	 * Phase 1: Raise core voltage to requested VID if frequency is
35532580301SAttilio Rao 	 * going up.
35632580301SAttilio Rao 	 */
35732580301SAttilio Rao 	while (cvid > vid) {
35832580301SAttilio Rao 		val = cvid - (1 << sc->mvs);
35932580301SAttilio Rao 		rv = pn8_write_fidvid(cfid, (val > 0) ? val : 0, 1ULL, &status);
36032580301SAttilio Rao 		if (rv) {
36132580301SAttilio Rao 			sc->errata |= PENDING_STUCK;
36232580301SAttilio Rao 			return (rv);
36332580301SAttilio Rao 		}
36432580301SAttilio Rao 		cvid = PN8_STA_CVID(status);
36532580301SAttilio Rao 		COUNT_OFF_VST(sc->vst);
36632580301SAttilio Rao 	}
36732580301SAttilio Rao 
36832580301SAttilio Rao 	/* ... then raise to voltage + RVO (if required) */
36932580301SAttilio Rao 	for (rvo = sc->rvo; rvo > 0 && cvid > 0; --rvo) {
37032580301SAttilio Rao 		/* XXX It's not clear from spec if we have to do that
37132580301SAttilio Rao 		 * in 0.25 step or in MVS.  Therefore do it as it's done
37232580301SAttilio Rao 		 * under Linux */
37332580301SAttilio Rao 		rv = pn8_write_fidvid(cfid, cvid - 1, 1ULL, &status);
37432580301SAttilio Rao 		if (rv) {
37532580301SAttilio Rao 			sc->errata |= PENDING_STUCK;
37632580301SAttilio Rao 			return (rv);
37732580301SAttilio Rao 		}
37832580301SAttilio Rao 		cvid = PN8_STA_CVID(status);
37932580301SAttilio Rao 		COUNT_OFF_VST(sc->vst);
38032580301SAttilio Rao 	}
38132580301SAttilio Rao 
38232580301SAttilio Rao 	/* Phase 2: change to requested core frequency */
38332580301SAttilio Rao 	if (cfid != fid) {
38432580301SAttilio Rao 		u_int vco_fid, vco_cfid, fid_delta;
38532580301SAttilio Rao 
38632580301SAttilio Rao 		vco_fid = FID_TO_VCO_FID(fid);
38732580301SAttilio Rao 		vco_cfid = FID_TO_VCO_FID(cfid);
38832580301SAttilio Rao 
38932580301SAttilio Rao 		while (abs(vco_fid - vco_cfid) > 2) {
39032580301SAttilio Rao 			fid_delta = (vco_cfid & 1) ? 1 : 2;
39132580301SAttilio Rao 			if (fid > cfid) {
39232580301SAttilio Rao 				if (cfid > 7)
39332580301SAttilio Rao 					val = cfid + fid_delta;
39432580301SAttilio Rao 				else
39532580301SAttilio Rao 					val = FID_TO_VCO_FID(cfid) + fid_delta;
39632580301SAttilio Rao 			} else
39732580301SAttilio Rao 				val = cfid - fid_delta;
39832580301SAttilio Rao 			rv = pn8_write_fidvid(val, cvid,
39932580301SAttilio Rao 			    sc->pll * (uint64_t) sc->fsb,
40032580301SAttilio Rao 			    &status);
40132580301SAttilio Rao 			if (rv) {
40232580301SAttilio Rao 				sc->errata |= PENDING_STUCK;
40332580301SAttilio Rao 				return (rv);
40432580301SAttilio Rao 			}
40532580301SAttilio Rao 			cfid = PN8_STA_CFID(status);
40632580301SAttilio Rao 			COUNT_OFF_IRT(sc->irt);
40732580301SAttilio Rao 
40832580301SAttilio Rao 			vco_cfid = FID_TO_VCO_FID(cfid);
40932580301SAttilio Rao 		}
41032580301SAttilio Rao 
41132580301SAttilio Rao 		rv = pn8_write_fidvid(fid, cvid,
41232580301SAttilio Rao 		    sc->pll * (uint64_t) sc->fsb,
41332580301SAttilio Rao 		    &status);
41432580301SAttilio Rao 		if (rv) {
41532580301SAttilio Rao 			sc->errata |= PENDING_STUCK;
41632580301SAttilio Rao 			return (rv);
41732580301SAttilio Rao 		}
41832580301SAttilio Rao 		cfid = PN8_STA_CFID(status);
41932580301SAttilio Rao 		COUNT_OFF_IRT(sc->irt);
42032580301SAttilio Rao 	}
42132580301SAttilio Rao 
42232580301SAttilio Rao 	/* Phase 3: change to requested voltage */
42332580301SAttilio Rao 	if (cvid != vid) {
42432580301SAttilio Rao 		rv = pn8_write_fidvid(cfid, vid, 1ULL, &status);
42532580301SAttilio Rao 		cvid = PN8_STA_CVID(status);
42632580301SAttilio Rao 		COUNT_OFF_VST(sc->vst);
42732580301SAttilio Rao 	}
42832580301SAttilio Rao 
42932580301SAttilio Rao 	/* Check if transition failed. */
43032580301SAttilio Rao 	if (cfid != fid || cvid != vid)
43132580301SAttilio Rao 		rv = ENXIO;
43232580301SAttilio Rao 
43332580301SAttilio Rao 	return (rv);
43432580301SAttilio Rao }
43532580301SAttilio Rao 
43632580301SAttilio Rao static int
pn_set(device_t dev,const struct cf_setting * cf)43732580301SAttilio Rao pn_set(device_t dev, const struct cf_setting *cf)
43832580301SAttilio Rao {
43932580301SAttilio Rao 	struct pn_softc *sc;
44032580301SAttilio Rao 	int fid, vid;
44132580301SAttilio Rao 	int i;
44232580301SAttilio Rao 	int rv;
44332580301SAttilio Rao 
44432580301SAttilio Rao 	if (cf == NULL)
44532580301SAttilio Rao 		return (EINVAL);
44632580301SAttilio Rao 	sc = device_get_softc(dev);
44732580301SAttilio Rao 
44832580301SAttilio Rao 	if (sc->errata & PENDING_STUCK)
44932580301SAttilio Rao 		return (ENXIO);
45032580301SAttilio Rao 
45132580301SAttilio Rao 	for (i = 0; i < sc->powernow_max_states; ++i)
45232580301SAttilio Rao 		if (CPUFREQ_CMP(sc->powernow_states[i].freq / 1000, cf->freq))
45332580301SAttilio Rao 			break;
45432580301SAttilio Rao 
45532580301SAttilio Rao 	fid = sc->powernow_states[i].fid;
45632580301SAttilio Rao 	vid = sc->powernow_states[i].vid;
45732580301SAttilio Rao 
45832580301SAttilio Rao 	rv = ENODEV;
45932580301SAttilio Rao 
46032580301SAttilio Rao 	switch (sc->pn_type) {
46132580301SAttilio Rao 	case PN7_TYPE:
46232580301SAttilio Rao 		rv = pn7_setfidvid(sc, fid, vid);
46332580301SAttilio Rao 		break;
46432580301SAttilio Rao 	case PN8_TYPE:
46532580301SAttilio Rao 		rv = pn8_setfidvid(sc, fid, vid);
46632580301SAttilio Rao 		break;
46732580301SAttilio Rao 	}
46832580301SAttilio Rao 
46932580301SAttilio Rao 	return (rv);
47032580301SAttilio Rao }
47132580301SAttilio Rao 
47232580301SAttilio Rao static int
pn_get(device_t dev,struct cf_setting * cf)47332580301SAttilio Rao pn_get(device_t dev, struct cf_setting *cf)
47432580301SAttilio Rao {
47532580301SAttilio Rao 	struct pn_softc *sc;
47632580301SAttilio Rao 	u_int cfid = 0, cvid = 0;
47732580301SAttilio Rao 	int i;
47832580301SAttilio Rao 	uint64_t status;
47932580301SAttilio Rao 
48032580301SAttilio Rao 	if (cf == NULL)
48132580301SAttilio Rao 		return (EINVAL);
48232580301SAttilio Rao 	sc = device_get_softc(dev);
48332580301SAttilio Rao 	if (sc->errata & PENDING_STUCK)
48432580301SAttilio Rao 		return (ENXIO);
48532580301SAttilio Rao 
48632580301SAttilio Rao 	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
48732580301SAttilio Rao 
48832580301SAttilio Rao 	switch (sc->pn_type) {
48932580301SAttilio Rao 	case PN7_TYPE:
49032580301SAttilio Rao 		cfid = PN7_STA_CFID(status);
49132580301SAttilio Rao 		cvid = PN7_STA_CVID(status);
49232580301SAttilio Rao 		break;
49332580301SAttilio Rao 	case PN8_TYPE:
49432580301SAttilio Rao 		cfid = PN8_STA_CFID(status);
49532580301SAttilio Rao 		cvid = PN8_STA_CVID(status);
49632580301SAttilio Rao 		break;
49732580301SAttilio Rao 	}
49832580301SAttilio Rao 	for (i = 0; i < sc->powernow_max_states; ++i)
49932580301SAttilio Rao 		if (cfid == sc->powernow_states[i].fid &&
50032580301SAttilio Rao 		    cvid == sc->powernow_states[i].vid)
50132580301SAttilio Rao 			break;
50232580301SAttilio Rao 
50332580301SAttilio Rao 	if (i < sc->powernow_max_states) {
50432580301SAttilio Rao 		cf->freq = sc->powernow_states[i].freq / 1000;
50532580301SAttilio Rao 		cf->power = sc->powernow_states[i].power;
50632580301SAttilio Rao 		cf->lat = 200;
50732580301SAttilio Rao 		cf->volts = sc->vid_to_volts[cvid];
50832580301SAttilio Rao 		cf->dev = dev;
50932580301SAttilio Rao 	} else {
51032580301SAttilio Rao 		memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf));
51132580301SAttilio Rao 		cf->dev = NULL;
51232580301SAttilio Rao 	}
51332580301SAttilio Rao 
51432580301SAttilio Rao 	return (0);
51532580301SAttilio Rao }
51632580301SAttilio Rao 
51732580301SAttilio Rao static int
pn_settings(device_t dev,struct cf_setting * sets,int * count)51832580301SAttilio Rao pn_settings(device_t dev, struct cf_setting *sets, int *count)
51932580301SAttilio Rao {
52032580301SAttilio Rao 	struct pn_softc *sc;
52132580301SAttilio Rao 	int i;
52232580301SAttilio Rao 
52332580301SAttilio Rao 	if (sets == NULL|| count == NULL)
52432580301SAttilio Rao 		return (EINVAL);
52532580301SAttilio Rao 	sc = device_get_softc(dev);
52632580301SAttilio Rao 	if (*count < sc->powernow_max_states)
52732580301SAttilio Rao 		return (E2BIG);
52832580301SAttilio Rao 	for (i = 0; i < sc->powernow_max_states; ++i) {
52932580301SAttilio Rao 		sets[i].freq = sc->powernow_states[i].freq / 1000;
53032580301SAttilio Rao 		sets[i].power = sc->powernow_states[i].power;
53132580301SAttilio Rao 		sets[i].lat = 200;
53232580301SAttilio Rao 		sets[i].volts = sc->vid_to_volts[sc->powernow_states[i].vid];
53332580301SAttilio Rao 		sets[i].dev = dev;
53432580301SAttilio Rao 	}
53532580301SAttilio Rao 	*count = sc->powernow_max_states;
53632580301SAttilio Rao 
53732580301SAttilio Rao 	return (0);
53832580301SAttilio Rao }
53932580301SAttilio Rao 
54032580301SAttilio Rao static int
pn_type(device_t dev,int * type)54132580301SAttilio Rao pn_type(device_t dev, int *type)
54232580301SAttilio Rao {
54332580301SAttilio Rao 	if (type == NULL)
54432580301SAttilio Rao 		return (EINVAL);
54532580301SAttilio Rao 
54632580301SAttilio Rao 	*type = CPUFREQ_TYPE_ABSOLUTE;
54732580301SAttilio Rao 
54832580301SAttilio Rao 	return (0);
54932580301SAttilio Rao }
55032580301SAttilio Rao 
55132580301SAttilio Rao /*
55232580301SAttilio Rao  * Given a set of pair of fid/vid, and number of performance states,
55332580301SAttilio Rao  * compute powernow_states via an insertion sort.
55432580301SAttilio Rao  */
55532580301SAttilio Rao static int
decode_pst(struct pn_softc * sc,uint8_t * p,int npstates)55632580301SAttilio Rao decode_pst(struct pn_softc *sc, uint8_t *p, int npstates)
55732580301SAttilio Rao {
55832580301SAttilio Rao 	int i, j, n;
55932580301SAttilio Rao 	struct powernow_state state;
56032580301SAttilio Rao 
56132580301SAttilio Rao 	for (i = 0; i < POWERNOW_MAX_STATES; ++i)
56232580301SAttilio Rao 		sc->powernow_states[i].freq = CPUFREQ_VAL_UNKNOWN;
56332580301SAttilio Rao 
56432580301SAttilio Rao 	for (n = 0, i = 0; i < npstates; ++i) {
56532580301SAttilio Rao 		state.fid = *p++;
56632580301SAttilio Rao 		state.vid = *p++;
56732580301SAttilio Rao 		state.power = CPUFREQ_VAL_UNKNOWN;
56832580301SAttilio Rao 
56932580301SAttilio Rao 		switch (sc->pn_type) {
57032580301SAttilio Rao 		case PN7_TYPE:
57132580301SAttilio Rao 			state.freq = 100 * pn7_fid_to_mult[state.fid] * sc->fsb;
57232580301SAttilio Rao 			if ((sc->errata & A0_ERRATA) &&
57332580301SAttilio Rao 			    (pn7_fid_to_mult[state.fid] % 10) == 5)
57432580301SAttilio Rao 				continue;
57532580301SAttilio Rao 			break;
57632580301SAttilio Rao 		case PN8_TYPE:
57732580301SAttilio Rao 			state.freq = 100 * pn8_fid_to_mult[state.fid] * sc->fsb;
57832580301SAttilio Rao 			break;
57932580301SAttilio Rao 		}
58032580301SAttilio Rao 
58132580301SAttilio Rao 		j = n;
58232580301SAttilio Rao 		while (j > 0 && sc->powernow_states[j - 1].freq < state.freq) {
58332580301SAttilio Rao 			memcpy(&sc->powernow_states[j],
58432580301SAttilio Rao 			    &sc->powernow_states[j - 1],
58532580301SAttilio Rao 			    sizeof(struct powernow_state));
58632580301SAttilio Rao 			--j;
58732580301SAttilio Rao 		}
58832580301SAttilio Rao 		memcpy(&sc->powernow_states[j], &state,
58932580301SAttilio Rao 		    sizeof(struct powernow_state));
59032580301SAttilio Rao 		++n;
59132580301SAttilio Rao 	}
59232580301SAttilio Rao 
59332580301SAttilio Rao 	/*
59432580301SAttilio Rao 	 * Fix powernow_max_states, if errata a0 give us less states
59532580301SAttilio Rao 	 * than expected.
59632580301SAttilio Rao 	 */
59732580301SAttilio Rao 	sc->powernow_max_states = n;
59832580301SAttilio Rao 
59932580301SAttilio Rao 	if (bootverbose)
60032580301SAttilio Rao 		for (i = 0; i < sc->powernow_max_states; ++i) {
60132580301SAttilio Rao 			int fid = sc->powernow_states[i].fid;
60232580301SAttilio Rao 			int vid = sc->powernow_states[i].vid;
60332580301SAttilio Rao 
60432580301SAttilio Rao 			printf("powernow: %2i %8dkHz FID %02x VID %02x\n",
60532580301SAttilio Rao 			    i,
60632580301SAttilio Rao 			    sc->powernow_states[i].freq,
60732580301SAttilio Rao 			    fid,
60832580301SAttilio Rao 			    vid);
60932580301SAttilio Rao 		}
61032580301SAttilio Rao 
61132580301SAttilio Rao 	return (0);
61232580301SAttilio Rao }
61332580301SAttilio Rao 
61432580301SAttilio Rao static int
cpuid_is_k7(u_int cpuid)61532580301SAttilio Rao cpuid_is_k7(u_int cpuid)
61632580301SAttilio Rao {
61732580301SAttilio Rao 
61832580301SAttilio Rao 	switch (cpuid) {
61932580301SAttilio Rao 	case 0x760:
62032580301SAttilio Rao 	case 0x761:
62132580301SAttilio Rao 	case 0x762:
62232580301SAttilio Rao 	case 0x770:
62332580301SAttilio Rao 	case 0x771:
62432580301SAttilio Rao 	case 0x780:
62532580301SAttilio Rao 	case 0x781:
62632580301SAttilio Rao 	case 0x7a0:
62732580301SAttilio Rao 		return (TRUE);
62832580301SAttilio Rao 	}
62932580301SAttilio Rao 	return (FALSE);
63032580301SAttilio Rao }
63132580301SAttilio Rao 
63232580301SAttilio Rao static int
pn_decode_pst(device_t dev)63332580301SAttilio Rao pn_decode_pst(device_t dev)
63432580301SAttilio Rao {
63532580301SAttilio Rao 	int maxpst;
63632580301SAttilio Rao 	struct pn_softc *sc;
63732580301SAttilio Rao 	u_int cpuid, maxfid, startvid;
63832580301SAttilio Rao 	u_long sig;
63932580301SAttilio Rao 	struct psb_header *psb;
64032580301SAttilio Rao 	uint8_t *p;
64132580301SAttilio Rao 	u_int regs[4];
64232580301SAttilio Rao 	uint64_t status;
64332580301SAttilio Rao 
64432580301SAttilio Rao 	sc = device_get_softc(dev);
64532580301SAttilio Rao 
64632580301SAttilio Rao 	do_cpuid(0x80000001, regs);
64732580301SAttilio Rao 	cpuid = regs[0];
64832580301SAttilio Rao 
64932580301SAttilio Rao 	if ((cpuid & 0xfff) == 0x760)
65032580301SAttilio Rao 		sc->errata |= A0_ERRATA;
65132580301SAttilio Rao 
65232580301SAttilio Rao 	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
65332580301SAttilio Rao 
65432580301SAttilio Rao 	switch (sc->pn_type) {
65532580301SAttilio Rao 	case PN7_TYPE:
65632580301SAttilio Rao 		maxfid = PN7_STA_MFID(status);
65732580301SAttilio Rao 		startvid = PN7_STA_SVID(status);
65832580301SAttilio Rao 		break;
65932580301SAttilio Rao 	case PN8_TYPE:
66032580301SAttilio Rao 		maxfid = PN8_STA_MFID(status);
66132580301SAttilio Rao 		/*
66232580301SAttilio Rao 		 * we should actually use a variable named 'maxvid' if K8,
66332580301SAttilio Rao 		 * but why introducing a new variable for that?
66432580301SAttilio Rao 		 */
66532580301SAttilio Rao 		startvid = PN8_STA_MVID(status);
66632580301SAttilio Rao 		break;
66732580301SAttilio Rao 	default:
66832580301SAttilio Rao 		return (ENODEV);
66932580301SAttilio Rao 	}
67032580301SAttilio Rao 
67132580301SAttilio Rao 	if (bootverbose) {
67232580301SAttilio Rao 		device_printf(dev, "STATUS: 0x%jx\n", status);
67332580301SAttilio Rao 		device_printf(dev, "STATUS: maxfid: 0x%02x\n", maxfid);
67432580301SAttilio Rao 		device_printf(dev, "STATUS: %s: 0x%02x\n",
67532580301SAttilio Rao 		    sc->pn_type == PN7_TYPE ? "startvid" : "maxvid",
67632580301SAttilio Rao 		    startvid);
67732580301SAttilio Rao 	}
67832580301SAttilio Rao 
67932580301SAttilio Rao 	sig = bios_sigsearch(PSB_START, PSB_SIG, PSB_LEN, PSB_STEP, PSB_OFF);
68032580301SAttilio Rao 	if (sig) {
68132580301SAttilio Rao 		struct pst_header *pst;
68232580301SAttilio Rao 
68332580301SAttilio Rao 		psb = (struct psb_header*)(uintptr_t)BIOS_PADDRTOVADDR(sig);
68432580301SAttilio Rao 
68532580301SAttilio Rao 		switch (psb->version) {
68632580301SAttilio Rao 		default:
68732580301SAttilio Rao 			return (ENODEV);
68832580301SAttilio Rao 		case 0x14:
68932580301SAttilio Rao 			/*
69032580301SAttilio Rao 			 * We can't be picky about numpst since at least
69132580301SAttilio Rao 			 * some systems have a value of 1 and some have 2.
69232580301SAttilio Rao 			 * We trust that cpuid_is_k7() will be better at
69332580301SAttilio Rao 			 * catching that we're on a K8 anyway.
69432580301SAttilio Rao 			 */
69532580301SAttilio Rao 			if (sc->pn_type != PN8_TYPE)
69632580301SAttilio Rao 				return (EINVAL);
69732580301SAttilio Rao 			sc->vst = psb->settlingtime;
698a061aa46SPedro F. Giffuni 			sc->rvo = PN8_PSB_TO_RVO(psb->res1);
699a061aa46SPedro F. Giffuni 			sc->irt = PN8_PSB_TO_IRT(psb->res1);
700a061aa46SPedro F. Giffuni 			sc->mvs = PN8_PSB_TO_MVS(psb->res1);
70132580301SAttilio Rao 			sc->low = PN8_PSB_TO_BATT(psb->res1);
70232580301SAttilio Rao 			if (bootverbose) {
70332580301SAttilio Rao 				device_printf(dev, "PSB: VST: %d\n",
70432580301SAttilio Rao 				    psb->settlingtime);
70532580301SAttilio Rao 				device_printf(dev, "PSB: RVO %x IRT %d "
70632580301SAttilio Rao 				    "MVS %d BATT %d\n",
70732580301SAttilio Rao 				    sc->rvo,
70832580301SAttilio Rao 				    sc->irt,
70932580301SAttilio Rao 				    sc->mvs,
71032580301SAttilio Rao 				    sc->low);
71132580301SAttilio Rao 			}
71232580301SAttilio Rao 			break;
71332580301SAttilio Rao 		case 0x12:
71432580301SAttilio Rao 			if (sc->pn_type != PN7_TYPE)
71532580301SAttilio Rao 				return (EINVAL);
71632580301SAttilio Rao 			sc->sgtc = psb->settlingtime * sc->fsb;
71732580301SAttilio Rao 			if (sc->sgtc < 100 * sc->fsb)
71832580301SAttilio Rao 				sc->sgtc = 100 * sc->fsb;
71932580301SAttilio Rao 			break;
72032580301SAttilio Rao 		}
72132580301SAttilio Rao 
72232580301SAttilio Rao 		p = ((uint8_t *) psb) + sizeof(struct psb_header);
72332580301SAttilio Rao 		pst = (struct pst_header*) p;
72432580301SAttilio Rao 
72532580301SAttilio Rao 		maxpst = 200;
72632580301SAttilio Rao 
72732580301SAttilio Rao 		do {
72832580301SAttilio Rao 			struct pst_header *pst = (struct pst_header*) p;
72932580301SAttilio Rao 
73032580301SAttilio Rao 			if (cpuid == pst->cpuid &&
73132580301SAttilio Rao 			    maxfid == pst->maxfid &&
73232580301SAttilio Rao 			    startvid == pst->startvid) {
73332580301SAttilio Rao 				sc->powernow_max_states = pst->numpstates;
73432580301SAttilio Rao 				switch (sc->pn_type) {
73532580301SAttilio Rao 				case PN7_TYPE:
73632580301SAttilio Rao 					if (abs(sc->fsb - pst->fsb) > 5)
73732580301SAttilio Rao 						continue;
73832580301SAttilio Rao 					break;
73932580301SAttilio Rao 				case PN8_TYPE:
74032580301SAttilio Rao 					break;
74132580301SAttilio Rao 				}
74232580301SAttilio Rao 				return (decode_pst(sc,
74332580301SAttilio Rao 				    p + sizeof(struct pst_header),
74432580301SAttilio Rao 				    sc->powernow_max_states));
74532580301SAttilio Rao 			}
74632580301SAttilio Rao 
74732580301SAttilio Rao 			p += sizeof(struct pst_header) + (2 * pst->numpstates);
74832580301SAttilio Rao 		} while (cpuid_is_k7(pst->cpuid) && maxpst--);
74932580301SAttilio Rao 
75032580301SAttilio Rao 		device_printf(dev, "no match for extended cpuid %.3x\n", cpuid);
75132580301SAttilio Rao 	}
75232580301SAttilio Rao 
75332580301SAttilio Rao 	return (ENODEV);
75432580301SAttilio Rao }
75532580301SAttilio Rao 
75632580301SAttilio Rao static int
pn_decode_acpi(device_t dev,device_t perf_dev)75732580301SAttilio Rao pn_decode_acpi(device_t dev, device_t perf_dev)
75832580301SAttilio Rao {
75932580301SAttilio Rao 	int i, j, n;
76032580301SAttilio Rao 	uint64_t status;
76132580301SAttilio Rao 	uint32_t ctrl;
76232580301SAttilio Rao 	u_int cpuid;
76332580301SAttilio Rao 	u_int regs[4];
76432580301SAttilio Rao 	struct pn_softc *sc;
76532580301SAttilio Rao 	struct powernow_state state;
76632580301SAttilio Rao 	struct cf_setting sets[POWERNOW_MAX_STATES];
76732580301SAttilio Rao 	int count = POWERNOW_MAX_STATES;
76832580301SAttilio Rao 	int type;
76932580301SAttilio Rao 	int rv;
77032580301SAttilio Rao 
77132580301SAttilio Rao 	if (perf_dev == NULL)
77232580301SAttilio Rao 		return (ENXIO);
77332580301SAttilio Rao 
77432580301SAttilio Rao 	rv = CPUFREQ_DRV_SETTINGS(perf_dev, sets, &count);
77532580301SAttilio Rao 	if (rv)
77632580301SAttilio Rao 		return (ENXIO);
77732580301SAttilio Rao 	rv = CPUFREQ_DRV_TYPE(perf_dev, &type);
77832580301SAttilio Rao 	if (rv || (type & CPUFREQ_FLAG_INFO_ONLY) == 0)
77932580301SAttilio Rao 		return (ENXIO);
78032580301SAttilio Rao 
78132580301SAttilio Rao 	sc = device_get_softc(dev);
78232580301SAttilio Rao 
78332580301SAttilio Rao 	do_cpuid(0x80000001, regs);
78432580301SAttilio Rao 	cpuid = regs[0];
78532580301SAttilio Rao 	if ((cpuid & 0xfff) == 0x760)
78632580301SAttilio Rao 		sc->errata |= A0_ERRATA;
78732580301SAttilio Rao 
78832580301SAttilio Rao 	ctrl = 0;
78932580301SAttilio Rao 	sc->sgtc = 0;
79032580301SAttilio Rao 	for (n = 0, i = 0; i < count; ++i) {
79132580301SAttilio Rao 		ctrl = sets[i].spec[PX_SPEC_CONTROL];
79232580301SAttilio Rao 		switch (sc->pn_type) {
79332580301SAttilio Rao 		case PN7_TYPE:
79432580301SAttilio Rao 			state.fid = ACPI_PN7_CTRL_TO_FID(ctrl);
79532580301SAttilio Rao 			state.vid = ACPI_PN7_CTRL_TO_VID(ctrl);
79632580301SAttilio Rao 			if ((sc->errata & A0_ERRATA) &&
79732580301SAttilio Rao 			    (pn7_fid_to_mult[state.fid] % 10) == 5)
79832580301SAttilio Rao 				continue;
79932580301SAttilio Rao 			break;
80032580301SAttilio Rao 		case PN8_TYPE:
80132580301SAttilio Rao 			state.fid = ACPI_PN8_CTRL_TO_FID(ctrl);
80232580301SAttilio Rao 			state.vid = ACPI_PN8_CTRL_TO_VID(ctrl);
80332580301SAttilio Rao 			break;
80432580301SAttilio Rao 		}
80543d645f9SJung-uk Kim 		state.freq = sets[i].freq * 1000;
80632580301SAttilio Rao 		state.power = sets[i].power;
80732580301SAttilio Rao 
80832580301SAttilio Rao 		j = n;
80932580301SAttilio Rao 		while (j > 0 && sc->powernow_states[j - 1].freq < state.freq) {
81032580301SAttilio Rao 			memcpy(&sc->powernow_states[j],
81132580301SAttilio Rao 			    &sc->powernow_states[j - 1],
81232580301SAttilio Rao 			    sizeof(struct powernow_state));
81332580301SAttilio Rao 			--j;
81432580301SAttilio Rao 		}
81532580301SAttilio Rao 		memcpy(&sc->powernow_states[j], &state,
81632580301SAttilio Rao 		    sizeof(struct powernow_state));
81732580301SAttilio Rao 		++n;
81832580301SAttilio Rao 	}
81932580301SAttilio Rao 
82032580301SAttilio Rao 	sc->powernow_max_states = n;
82132580301SAttilio Rao 	state = sc->powernow_states[0];
82232580301SAttilio Rao 	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
82332580301SAttilio Rao 
82432580301SAttilio Rao 	switch (sc->pn_type) {
82532580301SAttilio Rao 	case PN7_TYPE:
82632580301SAttilio Rao 		sc->sgtc = ACPI_PN7_CTRL_TO_SGTC(ctrl);
82732580301SAttilio Rao 		/*
82832580301SAttilio Rao 		 * XXX Some bios forget the max frequency!
82932580301SAttilio Rao 		 * This maybe indicates we have the wrong tables.  Therefore,
83032580301SAttilio Rao 		 * don't implement a quirk, but fallback to BIOS legacy
83132580301SAttilio Rao 		 * tables instead.
83232580301SAttilio Rao 		 */
83332580301SAttilio Rao 		if (PN7_STA_MFID(status) != state.fid) {
83432580301SAttilio Rao 			device_printf(dev, "ACPI MAX frequency not found\n");
83532580301SAttilio Rao 			return (EINVAL);
83632580301SAttilio Rao 		}
83743d645f9SJung-uk Kim 		sc->fsb = state.freq / 100 / pn7_fid_to_mult[state.fid];
83832580301SAttilio Rao 		break;
83932580301SAttilio Rao 	case PN8_TYPE:
84032580301SAttilio Rao 		sc->vst = ACPI_PN8_CTRL_TO_VST(ctrl),
84132580301SAttilio Rao 		sc->mvs = ACPI_PN8_CTRL_TO_MVS(ctrl),
84232580301SAttilio Rao 		sc->pll = ACPI_PN8_CTRL_TO_PLL(ctrl),
84332580301SAttilio Rao 		sc->rvo = ACPI_PN8_CTRL_TO_RVO(ctrl),
84432580301SAttilio Rao 		sc->irt = ACPI_PN8_CTRL_TO_IRT(ctrl);
84532580301SAttilio Rao 		sc->low = 0; /* XXX */
84632580301SAttilio Rao 
84732580301SAttilio Rao 		/*
84832580301SAttilio Rao 		 * powernow k8 supports only one low frequency.
84932580301SAttilio Rao 		 */
85032580301SAttilio Rao 		if (sc->powernow_max_states >= 2 &&
85132580301SAttilio Rao 		    (sc->powernow_states[sc->powernow_max_states - 2].fid < 8))
85232580301SAttilio Rao 			return (EINVAL);
85343d645f9SJung-uk Kim 		sc->fsb = state.freq / 100 / pn8_fid_to_mult[state.fid];
85432580301SAttilio Rao 		break;
85532580301SAttilio Rao 	}
85632580301SAttilio Rao 
85732580301SAttilio Rao 	return (0);
85832580301SAttilio Rao }
85932580301SAttilio Rao 
86032580301SAttilio Rao static void
pn_identify(driver_t * driver,device_t parent)86132580301SAttilio Rao pn_identify(driver_t *driver, device_t parent)
86232580301SAttilio Rao {
86332580301SAttilio Rao 
86432580301SAttilio Rao 	if ((amd_pminfo & AMDPM_FID) == 0 || (amd_pminfo & AMDPM_VID) == 0)
86532580301SAttilio Rao 		return;
86632580301SAttilio Rao 	switch (cpu_id & 0xf00) {
86732580301SAttilio Rao 	case 0x600:
86832580301SAttilio Rao 	case 0xf00:
86932580301SAttilio Rao 		break;
87032580301SAttilio Rao 	default:
87132580301SAttilio Rao 		return;
87232580301SAttilio Rao 	}
87332580301SAttilio Rao 	if (device_find_child(parent, "powernow", -1) != NULL)
87432580301SAttilio Rao 		return;
875d3a8f98aSAlexander Motin 	if (BUS_ADD_CHILD(parent, 10, "powernow", device_get_unit(parent))
876d3a8f98aSAlexander Motin 	    == NULL)
87732580301SAttilio Rao 		device_printf(parent, "powernow: add child failed\n");
87832580301SAttilio Rao }
87932580301SAttilio Rao 
88032580301SAttilio Rao static int
pn_probe(device_t dev)88132580301SAttilio Rao pn_probe(device_t dev)
88232580301SAttilio Rao {
88332580301SAttilio Rao 	struct pn_softc *sc;
88432580301SAttilio Rao 	uint64_t status;
88532580301SAttilio Rao 	uint64_t rate;
88632580301SAttilio Rao 	struct pcpu *pc;
88732580301SAttilio Rao 	u_int sfid, mfid, cfid;
88832580301SAttilio Rao 
88932580301SAttilio Rao 	sc = device_get_softc(dev);
89032580301SAttilio Rao 	sc->errata = 0;
89132580301SAttilio Rao 	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
89232580301SAttilio Rao 
89332580301SAttilio Rao 	pc = cpu_get_pcpu(dev);
89432580301SAttilio Rao 	if (pc == NULL)
89532580301SAttilio Rao 		return (ENODEV);
89632580301SAttilio Rao 
89732580301SAttilio Rao 	cpu_est_clockrate(pc->pc_cpuid, &rate);
89832580301SAttilio Rao 
89932580301SAttilio Rao 	switch (cpu_id & 0xf00) {
90032580301SAttilio Rao 	case 0x600:
90132580301SAttilio Rao 		sfid = PN7_STA_SFID(status);
90232580301SAttilio Rao 		mfid = PN7_STA_MFID(status);
90332580301SAttilio Rao 		cfid = PN7_STA_CFID(status);
90432580301SAttilio Rao 		sc->pn_type = PN7_TYPE;
90532580301SAttilio Rao 		sc->fsb = rate / 100000 / pn7_fid_to_mult[cfid];
90632580301SAttilio Rao 
90732580301SAttilio Rao 		/*
90832580301SAttilio Rao 		 * If start FID is different to max FID, then it is a
90932580301SAttilio Rao 		 * mobile processor.  If not, it is a low powered desktop
91032580301SAttilio Rao 		 * processor.
91132580301SAttilio Rao 		 */
912354ef278SJohn Baldwin 		if (sfid != mfid) {
91332580301SAttilio Rao 			sc->vid_to_volts = pn7_mobile_vid_to_volts;
91432580301SAttilio Rao 			device_set_desc(dev, "PowerNow! K7");
91532580301SAttilio Rao 		} else {
91632580301SAttilio Rao 			sc->vid_to_volts = pn7_desktop_vid_to_volts;
91732580301SAttilio Rao 			device_set_desc(dev, "Cool`n'Quiet K7");
91832580301SAttilio Rao 		}
91932580301SAttilio Rao 		break;
92032580301SAttilio Rao 
92132580301SAttilio Rao 	case 0xf00:
92232580301SAttilio Rao 		sfid = PN8_STA_SFID(status);
92332580301SAttilio Rao 		mfid = PN8_STA_MFID(status);
92432580301SAttilio Rao 		cfid = PN8_STA_CFID(status);
92532580301SAttilio Rao 		sc->pn_type = PN8_TYPE;
92632580301SAttilio Rao 		sc->vid_to_volts = pn8_vid_to_volts;
92732580301SAttilio Rao 		sc->fsb = rate / 100000 / pn8_fid_to_mult[cfid];
92832580301SAttilio Rao 
929354ef278SJohn Baldwin 		if (sfid != mfid)
93032580301SAttilio Rao 			device_set_desc(dev, "PowerNow! K8");
93132580301SAttilio Rao 		else
93232580301SAttilio Rao 			device_set_desc(dev, "Cool`n'Quiet K8");
93332580301SAttilio Rao 		break;
93432580301SAttilio Rao 	default:
93532580301SAttilio Rao 		return (ENODEV);
93632580301SAttilio Rao 	}
93732580301SAttilio Rao 
93832580301SAttilio Rao 	return (0);
93932580301SAttilio Rao }
94032580301SAttilio Rao 
94132580301SAttilio Rao static int
pn_attach(device_t dev)94232580301SAttilio Rao pn_attach(device_t dev)
94332580301SAttilio Rao {
94432580301SAttilio Rao 	int rv;
94532580301SAttilio Rao 	device_t child;
94632580301SAttilio Rao 
94732580301SAttilio Rao 	child = device_find_child(device_get_parent(dev), "acpi_perf", -1);
94832580301SAttilio Rao 	if (child) {
94932580301SAttilio Rao 		rv = pn_decode_acpi(dev, child);
95032580301SAttilio Rao 		if (rv)
95132580301SAttilio Rao 			rv = pn_decode_pst(dev);
95232580301SAttilio Rao 	} else
95332580301SAttilio Rao 		rv = pn_decode_pst(dev);
95432580301SAttilio Rao 
95532580301SAttilio Rao 	if (rv != 0)
95632580301SAttilio Rao 		return (ENXIO);
95732580301SAttilio Rao 	cpufreq_register(dev);
95832580301SAttilio Rao 	return (0);
95932580301SAttilio Rao }
96032580301SAttilio Rao 
96132580301SAttilio Rao static int
pn_detach(device_t dev)96232580301SAttilio Rao pn_detach(device_t dev)
96332580301SAttilio Rao {
96432580301SAttilio Rao 
96532580301SAttilio Rao 	return (cpufreq_unregister(dev));
96632580301SAttilio Rao }
967