powernow.c revision 144194
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 144194 2005-03-27 21:44:21Z 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:
639144194Snjl			if (sc->pn_type != PN8_TYPE || psb->numpst != 1)
640144194Snjl				return (EINVAL);
641144194Snjl			sc->vst = psb->settlingtime;
642144194Snjl			sc->rvo = PN8_PSB_TO_RVO(psb->res1),
643144194Snjl			sc->irt = PN8_PSB_TO_IRT(psb->res1),
644144194Snjl			sc->mvs = PN8_PSB_TO_MVS(psb->res1),
645144194Snjl			sc->low = PN8_PSB_TO_BATT(psb->res1);
646144194Snjl			if (bootverbose) {
647144194Snjl				device_printf(dev, "PSB: VST: %d\n",
648144194Snjl				    psb->settlingtime);
649144194Snjl				device_printf(dev, "PSB: RVO %x IRT %d "
650144194Snjl				    "MVS %d BATT %d\n",
651144194Snjl				    sc->rvo,
652144194Snjl				    sc->irt,
653144194Snjl				    sc->mvs,
654144194Snjl				    sc->low);
655144194Snjl			}
656144194Snjl			break;
657144194Snjl		case 0x12:
658144194Snjl			if (sc->pn_type != PN7_TYPE)
659144194Snjl				return (EINVAL);
660144194Snjl			sc->sgtc = psb->settlingtime * sc->fsb;
661144194Snjl			if (sc->sgtc < 100 * sc->fsb)
662144194Snjl				sc->sgtc = 100 * sc->fsb;
663144194Snjl			break;
664144194Snjl		}
665144194Snjl
666144194Snjl		p = ((uint8_t *) psb) + sizeof(struct psb_header);
667144194Snjl		pst = (struct pst_header*) p;
668144194Snjl
669144194Snjl		maxpst = 200;
670144194Snjl
671144194Snjl		do {
672144194Snjl			struct pst_header *pst = (struct pst_header*) p;
673144194Snjl
674144194Snjl			if (cpuid == pst->cpuid &&
675144194Snjl			    maxfid == pst->maxfid &&
676144194Snjl			    startvid == pst->startvid) {
677144194Snjl				sc->powernow_max_states = pst->numpstates;
678144194Snjl				switch (sc->pn_type) {
679144194Snjl				case PN7_TYPE:
680144194Snjl					if (abs(sc->fsb - pst->fsb) > 5)
681144194Snjl						continue;
682144194Snjl					break;
683144194Snjl				case PN8_TYPE:
684144194Snjl					break;
685144194Snjl				}
686144194Snjl				return (decode_pst(sc,
687144194Snjl				    p + sizeof(struct pst_header),
688144194Snjl				    sc->powernow_max_states));
689144194Snjl			}
690144194Snjl
691144194Snjl			p += sizeof(struct pst_header) + (2 * pst->numpstates);
692144194Snjl		} while (cpuid_is_k7(pst->cpuid) && maxpst--);
693144194Snjl
694144194Snjl		device_printf(dev, "no match for extended cpuid %.3x\n", cpuid);
695144194Snjl	}
696144194Snjl
697144194Snjl	return (ENODEV);
698144194Snjl}
699144194Snjl
700144194Snjl/*
701144194Snjl * TODO: this should be done in sys/ARCH/ARCH/identcpu.c
702144194Snjl */
703144194Snjlstatic int
704144194Snjlcpu_is_powernow_capable(void)
705144194Snjl{
706144194Snjl	u_int regs[4];
707144194Snjl
708144194Snjl	if (strcmp(cpu_vendor, "AuthenticAMD") != 0 ||
709144194Snjl	    cpu_exthigh < 0x80000007)
710144194Snjl		return (FALSE);
711144194Snjl
712144194Snjl	do_cpuid(0x80000007, regs);
713144194Snjl	return (regs[3] & 0x6);
714144194Snjl}
715144194Snjl
716144194Snjlstatic int
717144194Snjlpn_decode_acpi(device_t dev, device_t perf_dev)
718144194Snjl{
719144194Snjl	int i, j, n;
720144194Snjl	uint64_t status;
721144194Snjl	uint32_t ctrl;
722144194Snjl	u_int cpuid;
723144194Snjl	u_int regs[4];
724144194Snjl	struct pn_softc *sc;
725144194Snjl	struct powernow_state state;
726144194Snjl	struct cf_setting sets[POWERNOW_MAX_STATES];
727144194Snjl	int count = POWERNOW_MAX_STATES;
728144194Snjl	int type;
729144194Snjl	int rv;
730144194Snjl
731144194Snjl	if (perf_dev == NULL)
732144194Snjl		return (ENXIO);
733144194Snjl
734144194Snjl	rv = CPUFREQ_DRV_SETTINGS(perf_dev, sets, &count);
735144194Snjl	if (rv)
736144194Snjl		return (ENXIO);
737144194Snjl	rv = CPUFREQ_DRV_TYPE(perf_dev, &type);
738144194Snjl	if (rv || (type & CPUFREQ_FLAG_INFO_ONLY) == 0)
739144194Snjl		return (ENXIO);
740144194Snjl
741144194Snjl	sc = device_get_softc(dev);
742144194Snjl
743144194Snjl	do_cpuid(0x80000001, regs);
744144194Snjl	cpuid = regs[0];
745144194Snjl	if ((cpuid & 0xfff) == 0x760)
746144194Snjl		sc->errata_a0 = TRUE;
747144194Snjl
748144194Snjl	ctrl = 0;
749144194Snjl	sc->sgtc = 0;
750144194Snjl	for (n = 0, i = 0; i < count; ++i) {
751144194Snjl		ctrl = sets[i].spec[PX_SPEC_CONTROL];
752144194Snjl		switch (sc->pn_type) {
753144194Snjl		case PN7_TYPE:
754144194Snjl			state.fid = ACPI_PN7_CTRL_TO_FID(ctrl);
755144194Snjl			state.vid = ACPI_PN7_CTRL_TO_VID(ctrl);
756144194Snjl			if (sc->errata_a0 &&
757144194Snjl			    (pn7_fid_to_mult[state.fid] % 10) == 5)
758144194Snjl				continue;
759144194Snjl			state.freq = 100 * pn7_fid_to_mult[state.fid] * sc->fsb;
760144194Snjl			break;
761144194Snjl		case PN8_TYPE:
762144194Snjl			state.fid = ACPI_PN8_CTRL_TO_FID(ctrl);
763144194Snjl			state.vid = ACPI_PN8_CTRL_TO_VID(ctrl);
764144194Snjl			state.freq = 100 * pn8_fid_to_mult[state.fid >> 1] *
765144194Snjl			    sc->fsb;
766144194Snjl			break;
767144194Snjl		}
768144194Snjl
769144194Snjl		state.power = sets[i].power;
770144194Snjl
771144194Snjl		j = n;
772144194Snjl		while (j > 0 && sc->powernow_states[j - 1].freq < state.freq) {
773144194Snjl			memcpy(&sc->powernow_states[j],
774144194Snjl			    &sc->powernow_states[j - 1],
775144194Snjl			    sizeof(struct powernow_state));
776144194Snjl			--j;
777144194Snjl		}
778144194Snjl		memcpy(&sc->powernow_states[j], &state,
779144194Snjl		    sizeof(struct powernow_state));
780144194Snjl		++n;
781144194Snjl	}
782144194Snjl
783144194Snjl	sc->powernow_max_states = n;
784144194Snjl	state = sc->powernow_states[0];
785144194Snjl	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
786144194Snjl
787144194Snjl	switch (sc->pn_type) {
788144194Snjl	case PN7_TYPE:
789144194Snjl		sc->sgtc = ACPI_PN7_CTRL_TO_SGTC(ctrl);
790144194Snjl		/*
791144194Snjl		 * XXX Some bios forget the max frequency!
792144194Snjl		 * This maybe indicates we have the wrong tables.  Therefore,
793144194Snjl		 * don't implement a quirk, but fallback to BIOS legacy
794144194Snjl		 * tables instead.
795144194Snjl		 */
796144194Snjl		if (PN7_STA_MFID(status) != state.fid) {
797144194Snjl			device_printf(dev, "ACPI MAX frequency not found\n");
798144194Snjl			return (EINVAL);
799144194Snjl		}
800144194Snjl		break;
801144194Snjl	case PN8_TYPE:
802144194Snjl		sc->vst = ACPI_PN8_CTRL_TO_VST(ctrl),
803144194Snjl		sc->mvs = ACPI_PN8_CTRL_TO_MVS(ctrl),
804144194Snjl		sc->pll = ACPI_PN8_CTRL_TO_PLL(ctrl),
805144194Snjl		sc->rvo = ACPI_PN8_CTRL_TO_RVO(ctrl),
806144194Snjl		sc->irt = ACPI_PN8_CTRL_TO_IRT(ctrl);
807144194Snjl		sc->low = 0; /* XXX */
808144194Snjl
809144194Snjl		/*
810144194Snjl		 * powernow k8 supports only one low frequency.
811144194Snjl		 */
812144194Snjl		if (sc->powernow_max_states >= 2 &&
813144194Snjl		    (sc->powernow_states[sc->powernow_max_states - 2].fid < 8))
814144194Snjl			return (EINVAL);
815144194Snjl		break;
816144194Snjl	}
817144194Snjl
818144194Snjl	return (0);
819144194Snjl}
820144194Snjl
821144194Snjlstatic void
822144194Snjlpn_identify(driver_t *driver, device_t parent)
823144194Snjl{
824144194Snjl	device_t child;
825144194Snjl
826144194Snjl	if (cpu_is_powernow_capable() == 0)
827144194Snjl		return;
828144194Snjl	switch (cpu_id & 0xf00) {
829144194Snjl	case 0x600:
830144194Snjl	case 0xf00:
831144194Snjl		break;
832144194Snjl	default:
833144194Snjl		return;
834144194Snjl	}
835144194Snjl	if (device_find_child(parent, "powernow", -1) != NULL)
836144194Snjl		return;
837144194Snjl	if ((child = BUS_ADD_CHILD(parent, 0, "powernow", -1)) == NULL)
838144194Snjl		device_printf(parent, "powernow: add child failed\n");
839144194Snjl}
840144194Snjl
841144194Snjlstatic int
842144194Snjlpn_probe(device_t dev)
843144194Snjl{
844144194Snjl	struct pn_softc *sc;
845144194Snjl	uint64_t status;
846144194Snjl	uint64_t rate;
847144194Snjl	struct pcpu *pc;
848144194Snjl	u_int sfid, mfid, cfid;
849144194Snjl
850144194Snjl	sc = device_get_softc(dev);
851144194Snjl	sc->errata_a0 = FALSE;
852144194Snjl	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
853144194Snjl
854144194Snjl	pc = cpu_get_pcpu(dev);
855144194Snjl	if (pc == NULL)
856144194Snjl		return (ENODEV);
857144194Snjl
858144194Snjl	cpu_est_clockrate(pc->pc_cpuid, &rate);
859144194Snjl
860144194Snjl	switch (cpu_id & 0xf00) {
861144194Snjl	case 0x600:
862144194Snjl		sfid = PN7_STA_SFID(status);
863144194Snjl		mfid = PN7_STA_MFID(status);
864144194Snjl		cfid = PN7_STA_CFID(status);
865144194Snjl		sc->pn_type = PN7_TYPE;
866144194Snjl		sc->fsb = rate / 100000 / pn7_fid_to_mult[cfid];
867144194Snjl
868144194Snjl		/*
869144194Snjl		 * If start FID is different to max FID, then it is a
870144194Snjl		 * mobile processor.  If not, it is a low powered desktop
871144194Snjl		 * processor.
872144194Snjl		 */
873144194Snjl		if (PN7_STA_SFID(status) != PN7_STA_MFID(status)) {
874144194Snjl			sc->vid_to_volts = pn7_mobile_vid_to_volts;
875144194Snjl			device_set_desc(dev, "PowerNow! K7");
876144194Snjl		} else {
877144194Snjl			sc->vid_to_volts = pn7_desktop_vid_to_volts;
878144194Snjl			device_set_desc(dev, "Cool`n'Quiet K7");
879144194Snjl		}
880144194Snjl		break;
881144194Snjl
882144194Snjl	case 0xf00:
883144194Snjl		sfid = PN8_STA_SFID(status);
884144194Snjl		mfid = PN8_STA_MFID(status);
885144194Snjl		cfid = PN8_STA_CFID(status);
886144194Snjl		sc->pn_type = PN8_TYPE;
887144194Snjl		sc->vid_to_volts = pn8_vid_to_volts;
888144194Snjl		sc->fsb = rate / 100000 / pn8_fid_to_mult[cfid >> 1];
889144194Snjl
890144194Snjl		if (PN8_STA_SFID(status) != PN8_STA_MFID(status))
891144194Snjl			device_set_desc(dev, "PowerNow! K8");
892144194Snjl		else
893144194Snjl			device_set_desc(dev, "Cool`n'Quiet K8");
894144194Snjl		break;
895144194Snjl	default:
896144194Snjl		return (ENODEV);
897144194Snjl	}
898144194Snjl
899144194Snjl	return (0);
900144194Snjl}
901144194Snjl
902144194Snjlstatic int
903144194Snjlpn_attach(device_t dev)
904144194Snjl{
905144194Snjl	int rv;
906144194Snjl	device_t child;
907144194Snjl
908144194Snjl	child = device_find_child(device_get_parent(dev), "acpi_perf", -1);
909144194Snjl	if (child) {
910144194Snjl		rv = pn_decode_acpi(dev, child);
911144194Snjl		if (rv)
912144194Snjl			rv = pn_decode_pst(dev);
913144194Snjl	} else
914144194Snjl		rv = pn_decode_pst(dev);
915144194Snjl
916144194Snjl	if (rv != 0)
917144194Snjl		return (ENXIO);
918144194Snjl	cpufreq_register(dev);
919144194Snjl	return (0);
920144194Snjl}
921144194Snjl
922144194Snjlstatic int
923144194Snjlpn_detach(device_t dev)
924144194Snjl{
925144194Snjl
926144194Snjl	cpufreq_unregister(dev);
927144194Snjl	return (0);
928144194Snjl}
929