xref: /openbsd-src/sys/arch/i386/i386/powernow-k7.c (revision f4e7063748a2ac72b2bab4389c0a7efc72d82189)
1 /* $OpenBSD: powernow-k7.c,v 1.44 2023/01/30 10:49:05 jsg Exp $ */
2 
3 /*
4  * Copyright (c) 2004 Martin V�giard.
5  * Copyright (c) 2004-2005 Bruno Ducrot
6  * Copyright (c) 2004 FUKUDA Nobuhiko <nfukuda@spa.is.uec.ac.jp>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /* AMD POWERNOW K7 driver */
30 
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/malloc.h>
34 #include <sys/sysctl.h>
35 
36 #include <machine/cpufunc.h>
37 #include <machine/bus.h>
38 
39 #include <dev/isa/isareg.h>
40 #include <i386/isa/isa_machdep.h>
41 
42 #include "acpicpu.h"
43 
44 #if NACPICPU > 0
45 #include <dev/acpi/acpidev.h>
46 #endif
47 
48 #define BIOS_START			0xe0000
49 #define	BIOS_LEN			0x20000
50 #define BIOS_STEP			16
51 
52 /*
53  * MSRs and bits used by PowerNow! technology
54  */
55 #define MSR_AMDK7_FIDVID_CTL		0xc0010041
56 #define MSR_AMDK7_FIDVID_STATUS		0xc0010042
57 #define AMD_PN_FID_VID			0x06
58 #define AMD_ERRATA_A0_CPUSIG		0x660
59 
60 #define PN7_FLAG_ERRATA_A0		0x01
61 #define PN7_FLAG_DESKTOP_VRM		0x02
62 
63 /* Bitfields used by K7 */
64 #define PN7_PSB_VERSION			0x12
65 #define PN7_CTR_FID(x)			((x) & 0x1f)
66 #define PN7_CTR_VID(x)			(((x) & 0x1f) << 8)
67 #define PN7_CTR_FIDC			0x00010000
68 #define PN7_CTR_VIDC			0x00020000
69 #define PN7_CTR_FIDCHRATIO		0x00100000
70 #define PN7_CTR_SGTC(x)			(((uint64_t)(x) & 0x000fffff) << 32)
71 
72 #define PN7_STA_CFID(x)			((x) & 0x1f)
73 #define PN7_STA_SFID(x)			(((x) >> 8) & 0x1f)
74 #define PN7_STA_MFID(x)			(((x) >> 16) & 0x1f)
75 #define PN7_STA_CVID(x)			(((x) >> 32) & 0x1f)
76 #define PN7_STA_SVID(x)			(((x) >> 40) & 0x1f)
77 #define PN7_STA_MVID(x)			(((x) >> 48) & 0x1f)
78 
79 /*
80  * ACPI ctr_val status register to powernow k7 configuration
81  */
82 #define PN7_ACPI_CTRL_TO_FID(x)		((x) & 0x1f)
83 #define PN7_ACPI_CTRL_TO_VID(x)		(((x) >> 5) & 0x1f)
84 #define PN7_ACPI_CTRL_TO_SGTC(x)	(((x) >> 10) & 0xffff)
85 
86 #define WRITE_FIDVID(fid, vid, ctrl)	\
87 	wrmsr(MSR_AMDK7_FIDVID_CTL,	\
88 	    (((ctrl) << 32) | (1ULL << 16) | ((vid) << 8) | (fid)))
89 
90 /*
91  * Divide each value by 10 to get the processor multiplier.
92  * Taken from powernow-k7.c/Linux by Dave Jones
93  */
94 static int k7pnow_fid_to_mult[32] = {
95 	110, 115, 120, 125, 50, 55, 60, 65,
96 	70, 75, 80, 85, 90, 95, 100, 105,
97 	30, 190, 40, 200, 130, 135, 140, 210,
98 	150, 225, 160, 165, 170, 180, -1, -1
99 };
100 
101 #define POWERNOW_MAX_STATES		16
102 
103 struct k7pnow_state {
104 	int freq;
105 	int fid;
106 	int vid;
107 };
108 
109 struct k7pnow_cpu_state {
110 	unsigned int fsb;
111 	unsigned int sgtc;
112 	struct k7pnow_state state_table[POWERNOW_MAX_STATES];
113 	unsigned int n_states;
114 	int flags;
115 };
116 
117 struct psb_s {
118 	char signature[10];	/* AMDK7PNOW! */
119 	uint8_t version;
120 	uint8_t flags;
121 	uint16_t ttime;		/* Min Settling time */
122 	uint8_t reserved;
123 	uint8_t n_pst;
124 };
125 
126 struct pst_s {
127 	uint32_t signature;
128 	uint8_t fsb;		/* Front Side Bus frequency (MHz) */
129 	uint8_t fid;		/* Max Frequency code */
130 	uint8_t vid;		/* Max Voltage code */
131 	uint8_t n_states;	/* Number of states */
132 };
133 
134 struct k7pnow_cpu_state *k7pnow_current_state;
135 extern int setperf_prio;
136 
137 int k7pnow_decode_pst(struct k7pnow_cpu_state *, uint8_t *, int);
138 int k7pnow_states(struct k7pnow_cpu_state *, uint32_t, unsigned int,
139     unsigned int);
140 
141 #if NACPICPU > 0
142 int k7pnow_acpi_init(struct k7pnow_cpu_state * cstate, uint64_t status);
143 int k7pnow_acpi_states(struct k7pnow_cpu_state * cstate,
144     struct acpicpu_pss *pss, int nstates, uint64_t status);
145 void k7pnow_acpi_pss_changed(struct acpicpu_pss *pss, int npss);
146 #endif
147 
148 void
k7_powernow_setperf(int level)149 k7_powernow_setperf(int level)
150 {
151 	unsigned int i;
152 	int cvid, cfid, vid = 0, fid = 0;
153 	uint64_t status, ctl;
154 	struct k7pnow_cpu_state * cstate;
155 	u_long s;
156 
157 	cstate = k7pnow_current_state;
158 
159 	i = ((level * cstate->n_states) + 1) / 101;
160 	if (i >= cstate->n_states)
161 		i = cstate->n_states - 1;
162 	fid = cstate->state_table[i].fid;
163 	vid = cstate->state_table[i].vid;
164 
165 	if (fid == 0 || vid == 0)
166 		return;
167 
168 	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
169 	cfid = PN7_STA_CFID(status);
170 	cvid = PN7_STA_CVID(status);
171 
172 	/*
173 	 * We're already at the requested level.
174 	 */
175 	if (fid == cfid && vid == cvid)
176 		return;
177 
178 	ctl = rdmsr(MSR_AMDK7_FIDVID_CTL) & PN7_CTR_FIDCHRATIO;
179 
180 	ctl |= PN7_CTR_FID(fid);
181 	ctl |= PN7_CTR_VID(vid);
182 	ctl |= PN7_CTR_SGTC(cstate->sgtc);
183 
184 	if (cstate->flags & PN7_FLAG_ERRATA_A0)
185 		s = intr_disable();
186 
187 	if (k7pnow_fid_to_mult[fid] < k7pnow_fid_to_mult[cfid]) {
188 		wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC);
189 		if (vid != cvid)
190 			wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC);
191 	} else {
192 		wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC);
193 		if (fid != cfid)
194 			wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC);
195 	}
196 
197 	if (cstate->flags & PN7_FLAG_ERRATA_A0)
198 		intr_restore(s);
199 
200 	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
201 	cfid = PN7_STA_CFID(status);
202 	cvid = PN7_STA_CVID(status);
203 	if (cfid == fid || cvid == vid)
204 		cpuspeed = cstate->state_table[i].freq;
205 }
206 
207 /*
208  * Given a set of pair of fid/vid, and number of performance states,
209  * compute state_table via an insertion sort.
210  */
211 int
k7pnow_decode_pst(struct k7pnow_cpu_state * cstate,uint8_t * p,int npst)212 k7pnow_decode_pst(struct k7pnow_cpu_state * cstate, uint8_t *p, int npst)
213 {
214 	int i, j, n;
215 	struct k7pnow_state state;
216 
217 	for (n = 0, i = 0; i < npst; ++i) {
218 		state.fid = *p++;
219 		state.vid = *p++;
220 		state.freq = k7pnow_fid_to_mult[state.fid]/10 * cstate->fsb;
221 		if ((cstate->flags & PN7_FLAG_ERRATA_A0) &&
222 		    (k7pnow_fid_to_mult[state.fid] % 10) == 5)
223 			continue;
224 
225 		j = n;
226 		while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
227 			memcpy(&cstate->state_table[j],
228 			    &cstate->state_table[j - 1],
229 			    sizeof(struct k7pnow_state));
230 			--j;
231 		}
232 		memcpy(&cstate->state_table[j], &state,
233 		    sizeof(struct k7pnow_state));
234 		++n;
235 	}
236 	/*
237 	 * Fix powernow_max_states, if errata_a0 give us less states
238 	 * than expected.
239 	 */
240 	cstate->n_states = n;
241 	return 1;
242 }
243 
244 int
k7pnow_states(struct k7pnow_cpu_state * cstate,uint32_t cpusig,unsigned int fid,unsigned int vid)245 k7pnow_states(struct k7pnow_cpu_state *cstate, uint32_t cpusig,
246     unsigned int fid, unsigned int vid)
247 {
248 	int maxpst;
249 	struct psb_s *psb;
250 	struct pst_s *pst;
251 	uint8_t *p;
252 
253 	/*
254 	 * Look in the 0xe0000 - 0x100000 physical address
255 	 * range for the pst tables; 16 byte blocks. End 10 bytes
256 	 * before the end of the range to avoid memcmp across a
257 	 * page boundary into unmapped memory.
258 	 */
259 	for (p = (u_int8_t *)ISA_HOLE_VADDR(BIOS_START);
260 	    p < (u_int8_t *)ISA_HOLE_VADDR(BIOS_START + BIOS_LEN) - 10;
261 	    p += BIOS_STEP) {
262 		if (memcmp(p, "AMDK7PNOW!", 10) == 0) {
263 			psb = (struct psb_s *)p;
264 			if (psb->version != PN7_PSB_VERSION)
265 				return 0;
266 
267 			cstate->sgtc = psb->ttime * cstate->fsb;
268 			if (cstate->sgtc < 100 * cstate->fsb)
269 				cstate->sgtc = 100 * cstate->fsb;
270 			if (psb->flags & 1)
271 				cstate->flags |= PN7_FLAG_DESKTOP_VRM;
272 			p += sizeof(struct psb_s);
273 
274 			for (maxpst = 0; maxpst < psb->n_pst; maxpst++) {
275 				pst = (struct pst_s*) p;
276 
277 				if (cpusig == pst->signature && fid == pst->fid
278 				    && vid == pst->vid) {
279 
280 					if (abs(cstate->fsb - pst->fsb) > 5)
281 						continue;
282 					cstate->n_states = pst->n_states;
283 					return (k7pnow_decode_pst(cstate,
284 					    p + sizeof(struct pst_s),
285 					    cstate->n_states));
286 				}
287 				p += sizeof(struct pst_s) +
288 				    (2 * pst->n_states);
289 			}
290 		}
291 	}
292 
293 	return 0;
294 }
295 
296 #if NACPICPU > 0
297 
298 int
k7pnow_acpi_states(struct k7pnow_cpu_state * cstate,struct acpicpu_pss * pss,int nstates,uint64_t status)299 k7pnow_acpi_states(struct k7pnow_cpu_state * cstate, struct acpicpu_pss *pss,
300     int nstates, uint64_t status)
301 {
302 	struct k7pnow_state state;
303 	int j, k, n;
304 	uint32_t ctrl;
305 
306 	k = -1;
307 	for (n = 0; n < cstate->n_states; n++) {
308 		if (status == pss[n].pss_status)
309 			k = n;
310 		ctrl = pss[n].pss_ctrl;
311 		state.fid = PN7_ACPI_CTRL_TO_FID(ctrl);
312 		state.vid = PN7_ACPI_CTRL_TO_VID(ctrl);
313 
314 		if ((cstate->flags & PN7_FLAG_ERRATA_A0) &&
315 		    (k7pnow_fid_to_mult[state.fid] % 10) == 5)
316 			continue;
317 
318 		state.freq = pss[n].pss_core_freq;
319 		j = n;
320 		while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
321 			memcpy(&cstate->state_table[j],
322 			    &cstate->state_table[j - 1],
323 			sizeof(struct k7pnow_state));
324 			--j;
325 		}
326 		memcpy(&cstate->state_table[j], &state,
327 		    sizeof(struct k7pnow_state));
328 	}
329 	return k;
330 }
331 
332 void
k7pnow_acpi_pss_changed(struct acpicpu_pss * pss,int npss)333 k7pnow_acpi_pss_changed(struct acpicpu_pss *pss, int npss)
334 {
335 	int curs;
336 	struct k7pnow_cpu_state *cstate;
337 	uint32_t ctrl;
338 	uint64_t status;
339 
340 	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
341 	cstate = k7pnow_current_state;
342 
343 	curs = k7pnow_acpi_states(cstate, pss, npss, status);
344 	ctrl = pss[curs].pss_ctrl;
345 	cstate->sgtc = PN7_ACPI_CTRL_TO_SGTC(ctrl);
346 	cstate->n_states = npss;
347 }
348 
349 int
k7pnow_acpi_init(struct k7pnow_cpu_state * cstate,uint64_t status)350 k7pnow_acpi_init(struct k7pnow_cpu_state *cstate, uint64_t status)
351 {
352 	int curs;
353 	uint32_t ctrl;
354 	struct acpicpu_pss *pss;
355 	int mfid;
356 
357 	cstate->n_states = acpicpu_fetch_pss(&pss);
358 	if (cstate->n_states == 0)
359 		return 0;
360 
361 	curs = k7pnow_acpi_states(cstate, pss, cstate->n_states, status);
362 	/*
363 	 * XXX: Some BIOS supplied _PSS implementations have the wrong
364 	 * maximum frequency, if we encounter one of these punt and
365 	 * hope the legacy tables have correct values.
366 	 */
367 	mfid = PN7_STA_MFID(status);
368 	if (mfid != cstate->state_table[cstate->n_states - 1].fid) {
369 		return 0;
370 	}
371 
372 	acpicpu_set_notify(k7pnow_acpi_pss_changed);
373 	ctrl = pss[curs].pss_ctrl;
374 	cstate->sgtc = PN7_ACPI_CTRL_TO_SGTC(ctrl);
375 
376 	return 1;
377 }
378 
379 #endif /* NACPICPU */
380 
381 void
k7_powernow_init(void)382 k7_powernow_init(void)
383 {
384 	u_int regs[4];
385 	uint64_t status;
386 	u_int maxfid, startvid, currentfid;
387 	struct k7pnow_cpu_state *cstate;
388 	struct k7pnow_state *state;
389 	struct cpu_info *ci;
390 	char *techname = NULL;
391 	int i;
392 
393 	if (setperf_prio > 1)
394 		return;
395 
396 	ci = curcpu();
397 
398 	cpuid(0x80000000, regs);
399 	if (regs[0] < 0x80000007)
400 		return;
401 
402 	cpuid(0x80000007, regs);
403 	if (!(regs[3] & AMD_PN_FID_VID))
404 		return;
405 
406 	/* Extended CPUID signature value */
407 	cpuid(0x80000001, regs);
408 
409 	cstate = malloc(sizeof(struct k7pnow_cpu_state), M_DEVBUF, M_NOWAIT);
410 	if (!cstate)
411 		return;
412 
413 	cstate->flags = cstate->n_states = 0;
414 	if (ci->ci_signature == AMD_ERRATA_A0_CPUSIG)
415 		cstate->flags |= PN7_FLAG_ERRATA_A0;
416 
417 	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
418 	maxfid = PN7_STA_MFID(status);
419 	startvid = PN7_STA_SVID(status);
420 	currentfid = PN7_STA_CFID(status);
421 
422 	cstate->fsb = cpuspeed / (k7pnow_fid_to_mult[currentfid]/10);
423 
424 	if (!k7pnow_states(cstate, ci->ci_signature, maxfid, startvid))
425 		if (!k7pnow_states(cstate, regs[0], maxfid, startvid)) {
426 #if NACPICPU > 0
427 			/* If we have it try ACPI */
428 			k7pnow_acpi_init(cstate, status);
429 #endif
430 	}
431 
432 	if (cstate->n_states) {
433 		if (cstate->flags & PN7_FLAG_DESKTOP_VRM)
434 			techname = "Cool'n'Quiet K7";
435 		else
436 			techname = "PowerNow! K7";
437 		printf("%s: %s %d MHz: speeds:",
438 		    ci->ci_dev->dv_xname, techname, cpuspeed);
439 		for (i = cstate->n_states; i > 0; i--) {
440 			state = &cstate->state_table[i-1];
441 			printf(" %d", state->freq);
442 		}
443 		printf(" MHz\n");
444 
445 		k7pnow_current_state = cstate;
446 		cpu_setperf = k7_powernow_setperf;
447 		setperf_prio = 1;
448 		return;
449 	}
450 	free(cstate, M_DEVBUF, sizeof(*cstate));
451 }
452