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