powernow.c revision 144380
1144194Snjl/*-
2144194Snjl * Copyright (c) 2004-2005 Bruno Ducrot
3144194Snjl * Copyright (c) 2004 FUKUDA Nobuhiko <nfukuda@spa.is.uec.ac.jp>
4144194Snjl *
5144194Snjl * Redistribution and use in source and binary forms, with or without
6144194Snjl * modification, are permitted provided that the following conditions
7144194Snjl * are met:
8144194Snjl * 1. Redistributions of source code must retain the above copyright
9144194Snjl *    notice, this list of conditions and the following disclaimer.
10144194Snjl * 2. Redistributions in binary form must reproduce the above copyright
11144194Snjl *    notice, this list of conditions and the following disclaimer in the
12144194Snjl *    documentation and/or other materials provided with the distribution.
13144194Snjl *
14144194Snjl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15144194Snjl * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16144194Snjl * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17144194Snjl * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18144194Snjl * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19144194Snjl * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20144194Snjl * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21144194Snjl * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22144194Snjl * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23144194Snjl * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24144194Snjl */
25144194Snjl
26144194Snjl/*
27144194Snjl * Many thanks to Nate Lawson for his helpful comments on this driver and
28144194Snjl * to Jung-uk Kim for testing.
29144194Snjl */
30144194Snjl
31144194Snjl#include <sys/cdefs.h>
32144194Snjl__FBSDID("$FreeBSD: head/sys/i386/cpufreq/powernow.c 144380 2005-03-31 06:11:04Z njl $");
33144194Snjl
34144194Snjl#include <sys/param.h>
35144194Snjl#include <sys/bus.h>
36144194Snjl#include <sys/cpu.h>
37144194Snjl#include <sys/kernel.h>
38144194Snjl#include <sys/malloc.h>
39144194Snjl#include <sys/module.h>
40144194Snjl#include <sys/pcpu.h>
41144194Snjl#include <sys/systm.h>
42144194Snjl
43144194Snjl#include <machine/pc/bios.h>
44144194Snjl#include <machine/md_var.h>
45144194Snjl#include <machine/specialreg.h>
46144194Snjl#include <machine/cputypes.h>
47144194Snjl#include <machine/clock.h>
48144194Snjl#include <machine/vmparam.h>
49144194Snjl#include <sys/rman.h>
50144194Snjl
51144194Snjl#include <vm/vm.h>
52144194Snjl#include <vm/pmap.h>
53144194Snjl
54144194Snjl#include "cpufreq_if.h"
55144194Snjl
56144194Snjl#define PN7_TYPE	0
57144194Snjl#define PN8_TYPE	1
58144194Snjl
59144194Snjl/* Legacy configuration via BIOS table PSB. */
60144194Snjl#define PSB_START	0
61144194Snjl#define PSB_STEP	0x10
62144194Snjl#define PSB_SIG		"AMDK7PNOW!"
63144194Snjl#define PSB_LEN		10
64144194Snjl#define PSB_OFF		0
65144194Snjl
66144194Snjlstruct psb_header {
67144194Snjl	char		 signature[10];
68144194Snjl	uint8_t		 version;
69144194Snjl	uint8_t		 flags;
70144194Snjl	uint16_t	 settlingtime;
71144194Snjl	uint8_t		 res1;
72144194Snjl	uint8_t		 numpst;
73144194Snjl} __packed;
74144194Snjl
75144194Snjlstruct pst_header {
76144194Snjl	uint32_t	 cpuid;
77144194Snjl	uint8_t		 fsb;
78144194Snjl	uint8_t		 maxfid;
79144194Snjl	uint8_t		 startvid;
80144194Snjl	uint8_t		 numpstates;
81144194Snjl} __packed;
82144194Snjl
83144194Snjl/*
84144194Snjl * MSRs and bits used by Powernow technology
85144194Snjl */
86144194Snjl#define MSR_AMDK7_FIDVID_CTL		0xc0010041
87144194Snjl#define MSR_AMDK7_FIDVID_STATUS		0xc0010042
88144194Snjl
89144194Snjl/* Bitfields used by K7 */
90144194Snjl
91144194Snjl#define PN7_CTR_FID(x)			((x) & 0x1f)
92144194Snjl#define PN7_CTR_VID(x)			(((x) & 0x1f) << 8)
93144194Snjl#define PN7_CTR_FIDC			0x00010000
94144194Snjl#define PN7_CTR_VIDC			0x00020000
95144194Snjl#define PN7_CTR_FIDCHRATIO		0x00100000
96144194Snjl#define PN7_CTR_SGTC(x)			(((uint64_t)(x) & 0x000fffff) << 32)
97144194Snjl
98144194Snjl#define PN7_STA_CFID(x)			((x) & 0x1f)
99144194Snjl#define PN7_STA_SFID(x)			(((x) >> 8) & 0x1f)
100144194Snjl#define PN7_STA_MFID(x)			(((x) >> 16) & 0x1f)
101144194Snjl#define PN7_STA_CVID(x)			(((x) >> 32) & 0x1f)
102144194Snjl#define PN7_STA_SVID(x)			(((x) >> 40) & 0x1f)
103144194Snjl#define PN7_STA_MVID(x)			(((x) >> 48) & 0x1f)
104144194Snjl
105144194Snjl/* ACPI ctr_val status register to powernow k7 configuration */
106144194Snjl#define ACPI_PN7_CTRL_TO_FID(x)		((x) & 0x1f)
107144194Snjl#define ACPI_PN7_CTRL_TO_VID(x)		(((x) >> 5) & 0x1f)
108144194Snjl#define ACPI_PN7_CTRL_TO_SGTC(x)	(((x) >> 10) & 0xffff)
109144194Snjl
110144194Snjl/* Bitfields used by K8 */
111144194Snjl
112144194Snjl#define PN8_CTR_FID(x)			((x) & 0x3f)
113144194Snjl#define PN8_CTR_VID(x)			(((x) & 0x1f) << 8)
114144194Snjl#define PN8_CTR_PENDING(x)		(((x) & 1) << 32)
115144194Snjl
116144194Snjl#define PN8_STA_CFID(x)			((x) & 0x3f)
117144194Snjl#define PN8_STA_SFID(x)			(((x) >> 8) & 0x3f)
118144194Snjl#define PN8_STA_MFID(x)			(((x) >> 16) & 0x3f)
119144194Snjl#define PN8_STA_PENDING(x)		(((x) >> 31) & 0x01)
120144194Snjl#define PN8_STA_CVID(x)			(((x) >> 32) & 0x1f)
121144194Snjl#define PN8_STA_SVID(x)			(((x) >> 40) & 0x1f)
122144194Snjl#define PN8_STA_MVID(x)			(((x) >> 48) & 0x1f)
123144194Snjl
124144194Snjl/* Reserved1 to powernow k8 configuration */
125144194Snjl#define PN8_PSB_TO_RVO(x)		((x) & 0x03)
126144194Snjl#define PN8_PSB_TO_IRT(x)		(((x) >> 2) & 0x03)
127144194Snjl#define PN8_PSB_TO_MVS(x)		(((x) >> 4) & 0x03)
128144194Snjl#define PN8_PSB_TO_BATT(x)		(((x) >> 6) & 0x03)
129144194Snjl
130144194Snjl/* ACPI ctr_val status register to powernow k8 configuration */
131144194Snjl#define ACPI_PN8_CTRL_TO_FID(x)		((x) & 0x3f)
132144194Snjl#define ACPI_PN8_CTRL_TO_VID(x)		(((x) >> 6) & 0x1f)
133144194Snjl#define ACPI_PN8_CTRL_TO_VST(x)		(((x) >> 11) & 0x1f)
134144194Snjl#define ACPI_PN8_CTRL_TO_MVS(x)		(((x) >> 18) & 0x03)
135144194Snjl#define ACPI_PN8_CTRL_TO_PLL(x)		(((x) >> 20) & 0x7f)
136144194Snjl#define ACPI_PN8_CTRL_TO_RVO(x)		(((x) >> 28) & 0x03)
137144194Snjl#define ACPI_PN8_CTRL_TO_IRT(x)		(((x) >> 30) & 0x03)
138144194Snjl
139144194Snjl
140144194Snjl#define WRITE_FIDVID(fid, vid, ctrl)	\
141144194Snjl	wrmsr(MSR_AMDK7_FIDVID_CTL,	\
142144194Snjl	    (((ctrl) << 32) | (1ULL << 16) | ((vid) << 8) | (fid)))
143144194Snjl
144144194Snjl#define READ_PENDING_WAIT(status)	\
145144194Snjl	do {			\
146144194Snjl		(status) = rdmsr(MSR_AMDK7_FIDVID_STATUS);	\
147144194Snjl	} while (PN8_STA_PENDING(status))
148144194Snjl
149144194Snjl#define COUNT_OFF_IRT(irt)	DELAY(10 * (1 << (irt)))
150144194Snjl#define COUNT_OFF_VST(vst)	DELAY(20 * (vst))
151144194Snjl
152144194Snjl#define FID_TO_VCO_FID(fid)	\
153144194Snjl	(((fid) < 8) ? (8 + ((fid) << 1)) : (fid))
154144194Snjl
155144194Snjl/*
156144194Snjl * Divide each value by 10 to get the processor multiplier.
157144194Snjl * Some of those tables are the same as the Linux powernow-k7
158144194Snjl * implementation by Dave Jones.
159144194Snjl */
160144194Snjlstatic int pn7_fid_to_mult[32] = {
161144194Snjl	110, 115, 120, 125, 50, 55, 60, 65,
162144194Snjl	70, 75, 80, 85, 90, 95, 100, 105,
163144194Snjl	30, 190, 40, 200, 130, 135, 140, 210,
164144194Snjl	150, 225, 160, 165, 170, 180, 0, 0,
165144194Snjl};
166144194Snjl
167144194Snjl
168144194Snjlstatic int pn8_fid_to_mult[32] = {
169144194Snjl	40, 50, 60, 70, 80, 90, 100, 110,
170144194Snjl	120, 130, 140, 150, 160, 170, 180, 190,
171144194Snjl	220, 230, 240, 250, 260, 270, 280, 290,
172144194Snjl	300, 310, 320, 330, 340, 350,
173144194Snjl};
174144194Snjl
175144194Snjl/*
176144194Snjl * Units are in mV.
177144194Snjl */
178144194Snjl/* Mobile VRM (K7) */
179144194Snjlstatic int pn7_mobile_vid_to_volts[] = {
180144194Snjl	2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650,
181144194Snjl	1600, 1550, 1500, 1450, 1400, 1350, 1300, 0,
182144194Snjl	1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100,
183144194Snjl	1075, 1050, 1025, 1000, 975, 950, 925, 0,
184144194Snjl};
185144194Snjl/* Desktop VRM (K7) */
186144194Snjlstatic int pn7_desktop_vid_to_volts[] = {
187144194Snjl	2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650,
188144194Snjl	1600, 1550, 1500, 1450, 1400, 1350, 1300, 0,
189144194Snjl	1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100,
190144194Snjl	1075, 1050, 1025, 1000, 975, 950, 925, 0,
191144194Snjl};
192144194Snjl/* Desktop and Mobile VRM (K8) */
193144194Snjlstatic int pn8_vid_to_volts[] = {
194144194Snjl	1550, 1525, 1500, 1475, 1450, 1425, 1400, 1375,
195144194Snjl	1350, 1325, 1300, 1275, 1250, 1225, 1200, 1175,
196144194Snjl	1150, 1125, 1100, 1075, 1050, 1025, 1000, 975,
197144194Snjl	950, 925, 900, 875, 850, 825, 800, 0,
198144194Snjl};
199144194Snjl
200144194Snjl#define POWERNOW_MAX_STATES		16
201144194Snjl
202144194Snjlstruct powernow_state {
203144194Snjl	int freq;
204144194Snjl	int power;
205144194Snjl	int fid;
206144194Snjl	int vid;
207144194Snjl};
208144194Snjl
209144194Snjlstruct pn_softc {
210144194Snjl	device_t		 dev;
211144194Snjl	int			 pn_type;
212144194Snjl	struct powernow_state	 powernow_states[POWERNOW_MAX_STATES];
213144194Snjl	u_int			 fsb;
214144194Snjl	u_int			 sgtc;
215144194Snjl	u_int			 vst;
216144194Snjl	u_int			 mvs;
217144194Snjl	u_int			 pll;
218144194Snjl	u_int			 rvo;
219144194Snjl	u_int			 irt;
220144194Snjl	int			 low;
221144194Snjl	int			 powernow_max_states;
222144194Snjl	u_int			 powernow_state;
223144194Snjl	int			 errata_a0;
224144194Snjl	int			*vid_to_volts;
225144194Snjl};
226144194Snjl
227144194Snjl/*
228144194Snjl * Offsets in struct cf_setting array for private values given by
229144194Snjl * acpi_perf driver.
230144194Snjl */
231144194Snjl#define PX_SPEC_CONTROL		0
232144194Snjl#define PX_SPEC_STATUS		1
233144194Snjl
234144194Snjlstatic void	pn_identify(driver_t *driver, device_t parent);
235144194Snjlstatic int	pn_probe(device_t dev);
236144194Snjlstatic int	pn_attach(device_t dev);
237144194Snjlstatic int	pn_detach(device_t dev);
238144194Snjlstatic int	pn_set(device_t dev, const struct cf_setting *cf);
239144194Snjlstatic int	pn_get(device_t dev, struct cf_setting *cf);
240144194Snjlstatic int	pn_settings(device_t dev, struct cf_setting *sets,
241144194Snjl		    int *count);
242144194Snjlstatic int	pn_type(device_t dev, int *type);
243144194Snjl
244144194Snjlstatic device_method_t pn_methods[] = {
245144194Snjl	/* Device interface */
246144194Snjl	DEVMETHOD(device_identify, pn_identify),
247144194Snjl	DEVMETHOD(device_probe, pn_probe),
248144194Snjl	DEVMETHOD(device_attach, pn_attach),
249144194Snjl	DEVMETHOD(device_detach, pn_detach),
250144194Snjl
251144194Snjl	/* cpufreq interface */
252144194Snjl	DEVMETHOD(cpufreq_drv_set, pn_set),
253144194Snjl	DEVMETHOD(cpufreq_drv_get, pn_get),
254144194Snjl	DEVMETHOD(cpufreq_drv_settings, pn_settings),
255144194Snjl	DEVMETHOD(cpufreq_drv_type, pn_type),
256144194Snjl
257144194Snjl	{0, 0}
258144194Snjl};
259144194Snjl
260144194Snjlstatic devclass_t pn_devclass;
261144194Snjlstatic driver_t pn_driver = {
262144194Snjl	"powernow",
263144194Snjl	pn_methods,
264144194Snjl	sizeof(struct pn_softc),
265144194Snjl};
266144194Snjl
267144194SnjlDRIVER_MODULE(powernow, cpu, pn_driver, pn_devclass, 0, 0);
268144194Snjl
269144194Snjlstatic int
270144194Snjlpn7_setfidvid(struct pn_softc *sc, int fid, int vid)
271144194Snjl{
272144194Snjl	int cfid, cvid;
273144194Snjl	uint64_t status, ctl;
274144194Snjl
275144194Snjl	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
276144194Snjl	cfid = PN7_STA_CFID(status);
277144194Snjl	cvid = PN7_STA_CVID(status);
278144194Snjl
279144194Snjl	/* We're already at the requested level. */
280144194Snjl	if (fid == cfid && vid == cvid)
281144194Snjl		return (0);
282144194Snjl
283144194Snjl	ctl = rdmsr(MSR_AMDK7_FIDVID_CTL) & PN7_CTR_FIDCHRATIO;
284144194Snjl
285144194Snjl	ctl |= PN7_CTR_FID(fid);
286144194Snjl	ctl |= PN7_CTR_VID(vid);
287144194Snjl	ctl |= PN7_CTR_SGTC(sc->sgtc);
288144194Snjl
289144194Snjl	if (sc->errata_a0)
290144194Snjl		disable_intr();
291144194Snjl
292144194Snjl	if (pn7_fid_to_mult[fid] < pn7_fid_to_mult[cfid]) {
293144194Snjl		wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC);
294144194Snjl		if (vid != cvid)
295144194Snjl			wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC);
296144194Snjl	} else {
297144194Snjl		wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC);
298144194Snjl		if (fid != cfid)
299144194Snjl			wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC);
300144194Snjl	}
301144194Snjl
302144194Snjl	if (sc->errata_a0)
303144194Snjl		enable_intr();
304144194Snjl
305144194Snjl	return (0);
306144194Snjl}
307144194Snjl
308144194Snjlstatic int
309144194Snjlpn8_setfidvid(struct pn_softc *sc, int fid, int vid)
310144194Snjl{
311144194Snjl	uint64_t status;
312144194Snjl	int cfid, cvid;
313144194Snjl	int rvo;
314144194Snjl	u_int val;
315144194Snjl
316144194Snjl	READ_PENDING_WAIT(status);
317144194Snjl	cfid = PN8_STA_CFID(status);
318144194Snjl	cvid = PN8_STA_CVID(status);
319144194Snjl
320144194Snjl	if (fid == cfid && vid == cvid)
321144194Snjl		return (0);
322144194Snjl
323144194Snjl	/*
324144194Snjl	 * Phase 1: Raise core voltage to requested VID if frequency is
325144194Snjl	 * going up.
326144194Snjl	 */
327144194Snjl	while (cvid > vid) {
328144194Snjl		val = cvid - (1 << sc->mvs);
329144194Snjl		WRITE_FIDVID(cfid, (val > 0) ? val : 0, 1ULL);
330144194Snjl		READ_PENDING_WAIT(status);
331144194Snjl		cvid = PN8_STA_CVID(status);
332144194Snjl		COUNT_OFF_VST(sc->vst);
333144194Snjl	}
334144194Snjl
335144194Snjl	/* ... then raise to voltage + RVO (if required) */
336144194Snjl	for (rvo = sc->rvo; rvo > 0 && cvid > 0; --rvo) {
337144194Snjl		/* XXX It's not clear from spec if we have to do that
338144194Snjl		 * in 0.25 step or in MVS.  Therefore do it as it's done
339144194Snjl		 * under Linux */
340144194Snjl		WRITE_FIDVID(cfid, cvid - 1, 1ULL);
341144194Snjl		READ_PENDING_WAIT(status);
342144194Snjl		cvid = PN8_STA_CVID(status);
343144194Snjl		COUNT_OFF_VST(sc->vst);
344144194Snjl	}
345144194Snjl
346144194Snjl	/* Phase 2: change to requested core frequency */
347144194Snjl	if (cfid != fid) {
348144194Snjl		u_int vco_fid, vco_cfid;
349144194Snjl
350144194Snjl		vco_fid = FID_TO_VCO_FID(fid);
351144194Snjl		vco_cfid = FID_TO_VCO_FID(cfid);
352144194Snjl
353144194Snjl		while (abs(vco_fid - vco_cfid) > 2) {
354144194Snjl			if (fid > cfid) {
355144194Snjl				if (cfid > 6)
356144194Snjl					val = cfid + 2;
357144194Snjl				else
358144194Snjl					val = FID_TO_VCO_FID(cfid) + 2;
359144194Snjl			} else
360144194Snjl				val = cfid - 2;
361144194Snjl			WRITE_FIDVID(val, cvid, sc->pll * (uint64_t) sc->fsb);
362144194Snjl			READ_PENDING_WAIT(status);
363144194Snjl			cfid = PN8_STA_CFID(status);
364144194Snjl			COUNT_OFF_IRT(sc->irt);
365144194Snjl
366144194Snjl			vco_cfid = FID_TO_VCO_FID(cfid);
367144194Snjl		}
368144194Snjl
369144194Snjl		WRITE_FIDVID(fid, cvid, sc->pll * (uint64_t) sc->fsb);
370144194Snjl		READ_PENDING_WAIT(status);
371144194Snjl		cfid = PN8_STA_CFID(status);
372144194Snjl		COUNT_OFF_IRT(sc->irt);
373144194Snjl	}
374144194Snjl
375144194Snjl	/* Phase 3: change to requested voltage */
376144194Snjl	if (cvid != vid) {
377144194Snjl		WRITE_FIDVID(cfid, vid, 1ULL);
378144194Snjl		READ_PENDING_WAIT(status);
379144194Snjl		cvid = PN8_STA_CVID(status);
380144194Snjl		COUNT_OFF_VST(sc->vst);
381144194Snjl	}
382144194Snjl
383144194Snjl	/* Check if transition failed. */
384144194Snjl	if (cfid != fid || cvid != vid)
385144194Snjl		return (ENXIO);
386144194Snjl
387144194Snjl	return (0);
388144194Snjl}
389144194Snjl
390144194Snjlstatic int
391144194Snjlpn_set(device_t dev, const struct cf_setting *cf)
392144194Snjl{
393144194Snjl	struct pn_softc *sc;
394144194Snjl	int fid, vid;
395144194Snjl	int i;
396144194Snjl	int rv;
397144194Snjl
398144194Snjl	if (cf == NULL)
399144194Snjl		return (EINVAL);
400144194Snjl	sc = device_get_softc(dev);
401144194Snjl
402144194Snjl	for (i = 0; i < sc->powernow_max_states; ++i)
403144194Snjl		if (CPUFREQ_CMP(sc->powernow_states[i].freq / 1000, cf->freq))
404144194Snjl			break;
405144194Snjl
406144194Snjl	fid = sc->powernow_states[i].fid;
407144194Snjl	vid = sc->powernow_states[i].vid;
408144194Snjl
409144194Snjl	rv = ENODEV;
410144194Snjl
411144194Snjl	switch (sc->pn_type) {
412144194Snjl	case PN7_TYPE:
413144194Snjl		rv = pn7_setfidvid(sc, fid, vid);
414144194Snjl		break;
415144194Snjl	case PN8_TYPE:
416144194Snjl		rv = pn8_setfidvid(sc, fid, vid);
417144194Snjl		break;
418144194Snjl	}
419144194Snjl
420144194Snjl	return (rv);
421144194Snjl}
422144194Snjl
423144194Snjlstatic int
424144194Snjlpn_get(device_t dev, struct cf_setting *cf)
425144194Snjl{
426144194Snjl	struct pn_softc *sc;
427144194Snjl	u_int cfid = 0, cvid = 0;
428144194Snjl	int i;
429144194Snjl	uint64_t status;
430144194Snjl
431144194Snjl	if (cf == NULL)
432144194Snjl		return (EINVAL);
433144194Snjl	sc = device_get_softc(dev);
434144194Snjl
435144194Snjl	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
436144194Snjl
437144194Snjl	switch (sc->pn_type) {
438144194Snjl	case PN7_TYPE:
439144194Snjl		cfid = PN7_STA_CFID(status);
440144194Snjl		cvid = PN7_STA_CVID(status);
441144194Snjl		break;
442144194Snjl	case PN8_TYPE:
443144194Snjl		cfid = PN8_STA_CFID(status);
444144194Snjl		cvid = PN8_STA_CVID(status);
445144194Snjl		break;
446144194Snjl	}
447144194Snjl	for (i = 0; i < sc->powernow_max_states; ++i)
448144194Snjl		if (cfid == sc->powernow_states[i].fid &&
449144194Snjl		    cvid == sc->powernow_states[i].vid)
450144194Snjl			break;
451144194Snjl
452144194Snjl	if (i < sc->powernow_max_states) {
453144194Snjl		cf->freq = sc->powernow_states[i].freq / 1000;
454144194Snjl		cf->power = sc->powernow_states[i].power;
455144194Snjl		cf->lat = 200;
456144194Snjl		cf->volts = sc->vid_to_volts[cvid];
457144194Snjl		cf->dev = dev;
458144194Snjl	} else {
459144194Snjl		memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf));
460144194Snjl		cf->dev = NULL;
461144194Snjl	}
462144194Snjl
463144194Snjl	return (0);
464144194Snjl}
465144194Snjl
466144194Snjlstatic int
467144194Snjlpn_settings(device_t dev, struct cf_setting *sets, int *count)
468144194Snjl{
469144194Snjl	struct pn_softc *sc;
470144194Snjl	int i;
471144194Snjl
472144194Snjl	if (sets == NULL|| count == NULL)
473144194Snjl		return (EINVAL);
474144194Snjl	sc = device_get_softc(dev);
475144194Snjl	if (*count < sc->powernow_max_states)
476144194Snjl		return (E2BIG);
477144194Snjl	for (i = 0; i < sc->powernow_max_states; ++i) {
478144194Snjl		sets[i].freq = sc->powernow_states[i].freq / 1000;
479144194Snjl		sets[i].power = sc->powernow_states[i].power;
480144194Snjl		sets[i].lat = 200;
481144194Snjl		sets[i].volts = sc->vid_to_volts[sc->powernow_states[i].vid];
482144194Snjl		sets[i].dev = dev;
483144194Snjl	}
484144194Snjl	*count = sc->powernow_max_states;
485144194Snjl
486144194Snjl	return (0);
487144194Snjl}
488144194Snjl
489144194Snjlstatic int
490144194Snjlpn_type(device_t dev, int *type)
491144194Snjl{
492144194Snjl	if (type == NULL)
493144194Snjl		return (EINVAL);
494144194Snjl
495144194Snjl	*type = CPUFREQ_TYPE_ABSOLUTE;
496144194Snjl
497144194Snjl	return (0);
498144194Snjl}
499144194Snjl
500144194Snjl/*
501144194Snjl * Given a set of pair of fid/vid, and number of performance states,
502144194Snjl * compute powernow_states via an insertion sort.
503144194Snjl */
504144194Snjlstatic int
505144194Snjldecode_pst(struct pn_softc *sc, uint8_t *p, int npstates)
506144194Snjl{
507144194Snjl	int i, j, n;
508144194Snjl	struct powernow_state state;
509144194Snjl
510144194Snjl	for (i = 0; i < POWERNOW_MAX_STATES; ++i)
511144194Snjl		sc->powernow_states[i].freq = CPUFREQ_VAL_UNKNOWN;
512144194Snjl
513144194Snjl	for (n = 0, i = 0; i < npstates; ++i) {
514144194Snjl		state.fid = *p++;
515144194Snjl		state.vid = *p++;
516144194Snjl		state.power = CPUFREQ_VAL_UNKNOWN;
517144194Snjl
518144194Snjl		switch (sc->pn_type) {
519144194Snjl		case PN7_TYPE:
520144194Snjl			state.freq = 100 * pn7_fid_to_mult[state.fid] * sc->fsb;
521144194Snjl			if (sc->errata_a0 &&
522144194Snjl			    (pn7_fid_to_mult[state.fid] % 10) == 5)
523144194Snjl				continue;
524144194Snjl			break;
525144194Snjl		case PN8_TYPE:
526144194Snjl			state.freq = 100 * pn8_fid_to_mult[state.fid >> 1] *
527144194Snjl			    sc->fsb;
528144194Snjl			break;
529144194Snjl		}
530144194Snjl
531144194Snjl		j = n;
532144194Snjl		while (j > 0 && sc->powernow_states[j - 1].freq < state.freq) {
533144194Snjl			memcpy(&sc->powernow_states[j],
534144194Snjl			    &sc->powernow_states[j - 1],
535144194Snjl			    sizeof(struct powernow_state));
536144194Snjl			--j;
537144194Snjl		}
538144194Snjl		memcpy(&sc->powernow_states[j], &state,
539144194Snjl		    sizeof(struct powernow_state));
540144194Snjl		++n;
541144194Snjl	}
542144194Snjl
543144194Snjl	/*
544144194Snjl	 * Fix powernow_max_states, if errata_a0 give us less states
545144194Snjl	 * than expected.
546144194Snjl	 */
547144194Snjl	sc->powernow_max_states = n;
548144194Snjl
549144194Snjl	if (bootverbose)
550144194Snjl		for (i = 0; i < sc->powernow_max_states; ++i) {
551144194Snjl			int fid = sc->powernow_states[i].fid;
552144194Snjl			int vid = sc->powernow_states[i].vid;
553144194Snjl
554144194Snjl			printf("powernow: %2i %8dkHz FID %02x VID %02x\n",
555144194Snjl			    i,
556144194Snjl			    sc->powernow_states[i].freq,
557144194Snjl			    fid,
558144194Snjl			    vid);
559144194Snjl		}
560144194Snjl
561144194Snjl	return (0);
562144194Snjl}
563144194Snjl
564144194Snjlstatic int
565144194Snjlcpuid_is_k7(u_int cpuid)
566144194Snjl{
567144194Snjl
568144194Snjl	switch (cpuid) {
569144194Snjl	case 0x760:
570144194Snjl	case 0x761:
571144194Snjl	case 0x762:
572144194Snjl	case 0x770:
573144194Snjl	case 0x771:
574144194Snjl	case 0x780:
575144194Snjl	case 0x781:
576144194Snjl	case 0x7a0:
577144194Snjl		return (TRUE);
578144194Snjl	}
579144194Snjl	return (FALSE);
580144194Snjl}
581144194Snjl
582144194Snjlstatic int
583144194Snjlpn_decode_pst(device_t dev)
584144194Snjl{
585144194Snjl	int maxpst;
586144194Snjl	struct pn_softc *sc;
587144194Snjl	u_int cpuid, maxfid, startvid;
588144194Snjl	u_long sig;
589144194Snjl	struct psb_header *psb;
590144194Snjl	uint8_t *p;
591144194Snjl	u_int regs[4];
592144194Snjl	uint64_t status;
593144194Snjl
594144194Snjl	sc = device_get_softc(dev);
595144194Snjl
596144194Snjl	do_cpuid(0x80000001, regs);
597144194Snjl	cpuid = regs[0];
598144194Snjl
599144194Snjl	if ((cpuid & 0xfff) == 0x760)
600144194Snjl		sc->errata_a0 = TRUE;
601144194Snjl
602144194Snjl	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
603144194Snjl
604144194Snjl	switch (sc->pn_type) {
605144194Snjl	case PN7_TYPE:
606144194Snjl		maxfid = PN7_STA_MFID(status);
607144194Snjl		startvid = PN7_STA_SVID(status);
608144194Snjl		break;
609144194Snjl	case PN8_TYPE:
610144194Snjl		maxfid = PN8_STA_MFID(status);
611144194Snjl		/*
612144194Snjl		 * we should actually use a variable named 'maxvid' if K8,
613144194Snjl		 * but why introducing a new variable for that?
614144194Snjl		 */
615144194Snjl		startvid = PN8_STA_MVID(status);
616144194Snjl		break;
617144194Snjl	default:
618144194Snjl		return (ENODEV);
619144194Snjl	}
620144194Snjl
621144194Snjl	if (bootverbose) {
622144194Snjl		device_printf(dev, "STATUS: 0x%jx\n", status);
623144194Snjl		device_printf(dev, "STATUS: maxfid: 0x%02x\n", maxfid);
624144194Snjl		device_printf(dev, "STATUS: %s: 0x%02x\n",
625144194Snjl		    sc->pn_type == PN7_TYPE ? "startvid" : "maxvid",
626144194Snjl		    startvid);
627144194Snjl	}
628144194Snjl
629144194Snjl	sig = bios_sigsearch(PSB_START, PSB_SIG, PSB_LEN, PSB_STEP, PSB_OFF);
630144194Snjl	if (sig) {
631144194Snjl		struct pst_header *pst;
632144194Snjl
633144194Snjl		psb = (struct psb_header*)(uintptr_t)BIOS_PADDRTOVADDR(sig);
634144194Snjl
635144194Snjl		switch (psb->version) {
636144194Snjl		default:
637144194Snjl			return (ENODEV);
638144194Snjl		case 0x14:
639144380Snjl			/*
640144380Snjl			 * We can't be picky about numpst since at least
641144380Snjl			 * some systems have a value of 1 and some have 2.
642144380Snjl			 * We trust that cpuid_is_k7() will be better at
643144380Snjl			 * catching that we're on a K8 anyway.
644144380Snjl			 */
645144380Snjl			if (sc->pn_type != PN8_TYPE)
646144194Snjl				return (EINVAL);
647144194Snjl			sc->vst = psb->settlingtime;
648144194Snjl			sc->rvo = PN8_PSB_TO_RVO(psb->res1),
649144194Snjl			sc->irt = PN8_PSB_TO_IRT(psb->res1),
650144194Snjl			sc->mvs = PN8_PSB_TO_MVS(psb->res1),
651144194Snjl			sc->low = PN8_PSB_TO_BATT(psb->res1);
652144194Snjl			if (bootverbose) {
653144194Snjl				device_printf(dev, "PSB: VST: %d\n",
654144194Snjl				    psb->settlingtime);
655144194Snjl				device_printf(dev, "PSB: RVO %x IRT %d "
656144194Snjl				    "MVS %d BATT %d\n",
657144194Snjl				    sc->rvo,
658144194Snjl				    sc->irt,
659144194Snjl				    sc->mvs,
660144194Snjl				    sc->low);
661144194Snjl			}
662144194Snjl			break;
663144194Snjl		case 0x12:
664144194Snjl			if (sc->pn_type != PN7_TYPE)
665144194Snjl				return (EINVAL);
666144194Snjl			sc->sgtc = psb->settlingtime * sc->fsb;
667144194Snjl			if (sc->sgtc < 100 * sc->fsb)
668144194Snjl				sc->sgtc = 100 * sc->fsb;
669144194Snjl			break;
670144194Snjl		}
671144194Snjl
672144194Snjl		p = ((uint8_t *) psb) + sizeof(struct psb_header);
673144194Snjl		pst = (struct pst_header*) p;
674144194Snjl
675144194Snjl		maxpst = 200;
676144194Snjl
677144194Snjl		do {
678144194Snjl			struct pst_header *pst = (struct pst_header*) p;
679144194Snjl
680144194Snjl			if (cpuid == pst->cpuid &&
681144194Snjl			    maxfid == pst->maxfid &&
682144194Snjl			    startvid == pst->startvid) {
683144194Snjl				sc->powernow_max_states = pst->numpstates;
684144194Snjl				switch (sc->pn_type) {
685144194Snjl				case PN7_TYPE:
686144194Snjl					if (abs(sc->fsb - pst->fsb) > 5)
687144194Snjl						continue;
688144194Snjl					break;
689144194Snjl				case PN8_TYPE:
690144194Snjl					break;
691144194Snjl				}
692144194Snjl				return (decode_pst(sc,
693144194Snjl				    p + sizeof(struct pst_header),
694144194Snjl				    sc->powernow_max_states));
695144194Snjl			}
696144194Snjl
697144194Snjl			p += sizeof(struct pst_header) + (2 * pst->numpstates);
698144194Snjl		} while (cpuid_is_k7(pst->cpuid) && maxpst--);
699144194Snjl
700144194Snjl		device_printf(dev, "no match for extended cpuid %.3x\n", cpuid);
701144194Snjl	}
702144194Snjl
703144194Snjl	return (ENODEV);
704144194Snjl}
705144194Snjl
706144194Snjl/*
707144194Snjl * TODO: this should be done in sys/ARCH/ARCH/identcpu.c
708144194Snjl */
709144194Snjlstatic int
710144194Snjlcpu_is_powernow_capable(void)
711144194Snjl{
712144194Snjl	u_int regs[4];
713144194Snjl
714144194Snjl	if (strcmp(cpu_vendor, "AuthenticAMD") != 0 ||
715144194Snjl	    cpu_exthigh < 0x80000007)
716144194Snjl		return (FALSE);
717144194Snjl
718144194Snjl	do_cpuid(0x80000007, regs);
719144194Snjl	return (regs[3] & 0x6);
720144194Snjl}
721144194Snjl
722144194Snjlstatic int
723144194Snjlpn_decode_acpi(device_t dev, device_t perf_dev)
724144194Snjl{
725144194Snjl	int i, j, n;
726144194Snjl	uint64_t status;
727144194Snjl	uint32_t ctrl;
728144194Snjl	u_int cpuid;
729144194Snjl	u_int regs[4];
730144194Snjl	struct pn_softc *sc;
731144194Snjl	struct powernow_state state;
732144194Snjl	struct cf_setting sets[POWERNOW_MAX_STATES];
733144194Snjl	int count = POWERNOW_MAX_STATES;
734144194Snjl	int type;
735144194Snjl	int rv;
736144194Snjl
737144194Snjl	if (perf_dev == NULL)
738144194Snjl		return (ENXIO);
739144194Snjl
740144194Snjl	rv = CPUFREQ_DRV_SETTINGS(perf_dev, sets, &count);
741144194Snjl	if (rv)
742144194Snjl		return (ENXIO);
743144194Snjl	rv = CPUFREQ_DRV_TYPE(perf_dev, &type);
744144194Snjl	if (rv || (type & CPUFREQ_FLAG_INFO_ONLY) == 0)
745144194Snjl		return (ENXIO);
746144194Snjl
747144194Snjl	sc = device_get_softc(dev);
748144194Snjl
749144194Snjl	do_cpuid(0x80000001, regs);
750144194Snjl	cpuid = regs[0];
751144194Snjl	if ((cpuid & 0xfff) == 0x760)
752144194Snjl		sc->errata_a0 = TRUE;
753144194Snjl
754144194Snjl	ctrl = 0;
755144194Snjl	sc->sgtc = 0;
756144194Snjl	for (n = 0, i = 0; i < count; ++i) {
757144194Snjl		ctrl = sets[i].spec[PX_SPEC_CONTROL];
758144194Snjl		switch (sc->pn_type) {
759144194Snjl		case PN7_TYPE:
760144194Snjl			state.fid = ACPI_PN7_CTRL_TO_FID(ctrl);
761144194Snjl			state.vid = ACPI_PN7_CTRL_TO_VID(ctrl);
762144194Snjl			if (sc->errata_a0 &&
763144194Snjl			    (pn7_fid_to_mult[state.fid] % 10) == 5)
764144194Snjl				continue;
765144194Snjl			state.freq = 100 * pn7_fid_to_mult[state.fid] * sc->fsb;
766144194Snjl			break;
767144194Snjl		case PN8_TYPE:
768144194Snjl			state.fid = ACPI_PN8_CTRL_TO_FID(ctrl);
769144194Snjl			state.vid = ACPI_PN8_CTRL_TO_VID(ctrl);
770144194Snjl			state.freq = 100 * pn8_fid_to_mult[state.fid >> 1] *
771144194Snjl			    sc->fsb;
772144194Snjl			break;
773144194Snjl		}
774144194Snjl
775144194Snjl		state.power = sets[i].power;
776144194Snjl
777144194Snjl		j = n;
778144194Snjl		while (j > 0 && sc->powernow_states[j - 1].freq < state.freq) {
779144194Snjl			memcpy(&sc->powernow_states[j],
780144194Snjl			    &sc->powernow_states[j - 1],
781144194Snjl			    sizeof(struct powernow_state));
782144194Snjl			--j;
783144194Snjl		}
784144194Snjl		memcpy(&sc->powernow_states[j], &state,
785144194Snjl		    sizeof(struct powernow_state));
786144194Snjl		++n;
787144194Snjl	}
788144194Snjl
789144194Snjl	sc->powernow_max_states = n;
790144194Snjl	state = sc->powernow_states[0];
791144194Snjl	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
792144194Snjl
793144194Snjl	switch (sc->pn_type) {
794144194Snjl	case PN7_TYPE:
795144194Snjl		sc->sgtc = ACPI_PN7_CTRL_TO_SGTC(ctrl);
796144194Snjl		/*
797144194Snjl		 * XXX Some bios forget the max frequency!
798144194Snjl		 * This maybe indicates we have the wrong tables.  Therefore,
799144194Snjl		 * don't implement a quirk, but fallback to BIOS legacy
800144194Snjl		 * tables instead.
801144194Snjl		 */
802144194Snjl		if (PN7_STA_MFID(status) != state.fid) {
803144194Snjl			device_printf(dev, "ACPI MAX frequency not found\n");
804144194Snjl			return (EINVAL);
805144194Snjl		}
806144194Snjl		break;
807144194Snjl	case PN8_TYPE:
808144194Snjl		sc->vst = ACPI_PN8_CTRL_TO_VST(ctrl),
809144194Snjl		sc->mvs = ACPI_PN8_CTRL_TO_MVS(ctrl),
810144194Snjl		sc->pll = ACPI_PN8_CTRL_TO_PLL(ctrl),
811144194Snjl		sc->rvo = ACPI_PN8_CTRL_TO_RVO(ctrl),
812144194Snjl		sc->irt = ACPI_PN8_CTRL_TO_IRT(ctrl);
813144194Snjl		sc->low = 0; /* XXX */
814144194Snjl
815144194Snjl		/*
816144194Snjl		 * powernow k8 supports only one low frequency.
817144194Snjl		 */
818144194Snjl		if (sc->powernow_max_states >= 2 &&
819144194Snjl		    (sc->powernow_states[sc->powernow_max_states - 2].fid < 8))
820144194Snjl			return (EINVAL);
821144194Snjl		break;
822144194Snjl	}
823144194Snjl
824144194Snjl	return (0);
825144194Snjl}
826144194Snjl
827144194Snjlstatic void
828144194Snjlpn_identify(driver_t *driver, device_t parent)
829144194Snjl{
830144194Snjl	device_t child;
831144194Snjl
832144194Snjl	if (cpu_is_powernow_capable() == 0)
833144194Snjl		return;
834144194Snjl	switch (cpu_id & 0xf00) {
835144194Snjl	case 0x600:
836144194Snjl	case 0xf00:
837144194Snjl		break;
838144194Snjl	default:
839144194Snjl		return;
840144194Snjl	}
841144194Snjl	if (device_find_child(parent, "powernow", -1) != NULL)
842144194Snjl		return;
843144194Snjl	if ((child = BUS_ADD_CHILD(parent, 0, "powernow", -1)) == NULL)
844144194Snjl		device_printf(parent, "powernow: add child failed\n");
845144194Snjl}
846144194Snjl
847144194Snjlstatic int
848144194Snjlpn_probe(device_t dev)
849144194Snjl{
850144194Snjl	struct pn_softc *sc;
851144194Snjl	uint64_t status;
852144194Snjl	uint64_t rate;
853144194Snjl	struct pcpu *pc;
854144194Snjl	u_int sfid, mfid, cfid;
855144194Snjl
856144194Snjl	sc = device_get_softc(dev);
857144194Snjl	sc->errata_a0 = FALSE;
858144194Snjl	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
859144194Snjl
860144194Snjl	pc = cpu_get_pcpu(dev);
861144194Snjl	if (pc == NULL)
862144194Snjl		return (ENODEV);
863144194Snjl
864144194Snjl	cpu_est_clockrate(pc->pc_cpuid, &rate);
865144194Snjl
866144194Snjl	switch (cpu_id & 0xf00) {
867144194Snjl	case 0x600:
868144194Snjl		sfid = PN7_STA_SFID(status);
869144194Snjl		mfid = PN7_STA_MFID(status);
870144194Snjl		cfid = PN7_STA_CFID(status);
871144194Snjl		sc->pn_type = PN7_TYPE;
872144194Snjl		sc->fsb = rate / 100000 / pn7_fid_to_mult[cfid];
873144194Snjl
874144194Snjl		/*
875144194Snjl		 * If start FID is different to max FID, then it is a
876144194Snjl		 * mobile processor.  If not, it is a low powered desktop
877144194Snjl		 * processor.
878144194Snjl		 */
879144194Snjl		if (PN7_STA_SFID(status) != PN7_STA_MFID(status)) {
880144194Snjl			sc->vid_to_volts = pn7_mobile_vid_to_volts;
881144194Snjl			device_set_desc(dev, "PowerNow! K7");
882144194Snjl		} else {
883144194Snjl			sc->vid_to_volts = pn7_desktop_vid_to_volts;
884144194Snjl			device_set_desc(dev, "Cool`n'Quiet K7");
885144194Snjl		}
886144194Snjl		break;
887144194Snjl
888144194Snjl	case 0xf00:
889144194Snjl		sfid = PN8_STA_SFID(status);
890144194Snjl		mfid = PN8_STA_MFID(status);
891144194Snjl		cfid = PN8_STA_CFID(status);
892144194Snjl		sc->pn_type = PN8_TYPE;
893144194Snjl		sc->vid_to_volts = pn8_vid_to_volts;
894144194Snjl		sc->fsb = rate / 100000 / pn8_fid_to_mult[cfid >> 1];
895144194Snjl
896144194Snjl		if (PN8_STA_SFID(status) != PN8_STA_MFID(status))
897144194Snjl			device_set_desc(dev, "PowerNow! K8");
898144194Snjl		else
899144194Snjl			device_set_desc(dev, "Cool`n'Quiet K8");
900144194Snjl		break;
901144194Snjl	default:
902144194Snjl		return (ENODEV);
903144194Snjl	}
904144194Snjl
905144194Snjl	return (0);
906144194Snjl}
907144194Snjl
908144194Snjlstatic int
909144194Snjlpn_attach(device_t dev)
910144194Snjl{
911144194Snjl	int rv;
912144194Snjl	device_t child;
913144194Snjl
914144194Snjl	child = device_find_child(device_get_parent(dev), "acpi_perf", -1);
915144194Snjl	if (child) {
916144194Snjl		rv = pn_decode_acpi(dev, child);
917144194Snjl		if (rv)
918144194Snjl			rv = pn_decode_pst(dev);
919144194Snjl	} else
920144194Snjl		rv = pn_decode_pst(dev);
921144194Snjl
922144194Snjl	if (rv != 0)
923144194Snjl		return (ENXIO);
924144194Snjl	cpufreq_register(dev);
925144194Snjl	return (0);
926144194Snjl}
927144194Snjl
928144194Snjlstatic int
929144194Snjlpn_detach(device_t dev)
930144194Snjl{
931144194Snjl
932144194Snjl	cpufreq_unregister(dev);
933144194Snjl	return (0);
934144194Snjl}
935