2 * Copyright (c) 2004 Martin V\xe9giard. Copyright (c) 2004-2005 Bruno Ducrot
3 * Copyright (c) 2004 FUKUDA Nobuhiko <nfukuda@spa.is.uec.ac.jp> Copyright
4 * (c) 2004, 2006 The NetBSD Foundation, Inc. All rights reserved.
6 * This code is derived from software contributed to The NetBSD Foundation by
7 * Juan Romero Pardines and Martin Vegiard.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions are
11 * met: 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer. 2.
13 * Redistributions in binary form must reproduce the above copyright notice,
14 * this list of conditions and the following disclaimer in the documentation
15 * and/or other materials provided with the distribution. THIS SOFTWARE IS
16 * PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
17 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
18 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
20 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
27 /* AMD POWERNOW K8 driver */
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/malloc.h>
32 #include <sys/kernel.h>
33 #include <sys/module.h>
34 #include <sys/sysctl.h>
35 #include <bus/isa/isa.h>
36 #include <machine/cpu.h>
37 #include <machine/pmap.h>
38 #include <machine/pc/bios.h>
39 #include <machine/cpufunc.h>
40 #include <machine/md_var.h>
41 #include <machine/specialreg.h>
42 #include <machine/vmparam.h>
44 #define PN8_STA_MFID(x) (((x) >> 16) & 0x3f)
45 #define PN8_STA_MVID(x) (((x) >> 48) & 0x1f)
46 #define PN8_STA_SFID(x) (((x) >> 8) & 0x3f)
49 * MSRs and bits used by PowerNow! technology
51 #define MSR_AMDK7_FIDVID_CTL 0xc0010041
52 #define MSR_AMDK7_FIDVID_STATUS 0xc0010042
53 #define AMD_PN_FID_VID 0x06
55 #define BIOS_START 0xe0000
56 #define BIOS_LEN 0x20000
59 #define PN8_PSB_VERSION 0x14
60 #define PN8_PSB_TO_RVO(x) ((x) & 0x03)
61 #define PN8_PSB_TO_IRT(x) (((x) >> 2) & 0x03)
62 #define PN8_PSB_TO_MVS(x) (((x) >> 4) & 0x03)
63 #define PN8_PSB_TO_BATT(x) (((x) >> 6) & 0x03)
64 /* Bitfields used by K8 */
65 #define PN8_CTR_FID(x) ((x) & 0x3f)
66 #define PN8_CTR_VID(x) (((x) & 0x1f) << 8)
67 #define PN8_CTR_PENDING(x) (((x) & 1) << 32)
68 #define PN8_STA_CFID(x) ((x) & 0x3f)
69 #define PN8_STA_SFID(x) (((x) >> 8) & 0x3f)
70 #define PN8_STA_MFID(x) (((x) >> 16) & 0x3f)
71 #define PN8_STA_PENDING(x) (((x) >> 31) & 0x01)
72 #define PN8_STA_CVID(x) (((x) >> 32) & 0x1f)
73 #define PN8_STA_SVID(x) (((x) >> 40) & 0x1f)
74 #define PN8_STA_MVID(x) (((x) >> 48) & 0x1f)
75 #define PN8_PLL_LOCK(x) ((x) * 1000/5)
76 #define WRITE_FIDVID(fid, vid, ctrl) \
77 wrmsr(MSR_AMDK7_FIDVID_CTL, \
78 (((ctrl) << 32) | (1ULL << 16) | ((vid) << 8) | (fid)))
79 #define COUNT_OFF_IRT(irt) DELAY(10 * (1 << (irt)))
80 #define COUNT_OFF_VST(vst) DELAY(20 * (vst))
81 #define FID_TO_VCO_FID(fid) \
82 (((fid) < 8) ? (8 + ((fid) << 1)) : (fid))
84 #define READ_PENDING_WAIT(status) \
86 (status) = rdmsr(MSR_AMDK7_FIDVID_STATUS); \
87 } while (PN8_STA_PENDING(status))
88 #define abs(x) ( (x) < 0 ? -(x) : (x) )
90 #define POWERNOW_MAX_STATES 16
98 struct k8pnow_cpu_state {
99 struct k8pnow_state state_table[POWERNOW_MAX_STATES];
100 unsigned int n_states;
110 char signature [10]; /* AMDK7PNOW! */
113 uint16_t ttime; /* Min Settling time */
125 static struct k8pnow_cpu_state *k8pnow_current_state = NULL;
129 k8pnow_states(struct k8pnow_cpu_state *cstate, uint32_t cpusig,
130 unsigned int fid, unsigned int vid);
132 k8pnow_decode_pst(struct k8pnow_cpu_state *cstate, uint8_t * p);
135 * Given a set of pair of fid/vid, and number of performance states, compute
136 * state_table via an insertion sort.
139 k8pnow_decode_pst(struct k8pnow_cpu_state *cstate, uint8_t * p)
142 struct k8pnow_state state;
143 for (n = 0, i = 0; i < cstate->n_states; i++) {
148 * The minimum supported frequency per the data sheet is
149 * 800MHz The maximum supported frequency is 5000MHz.
151 state.freq = 800 + state.fid * 100;
153 while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
154 memcpy(&cstate->state_table[j],
155 &cstate->state_table[j - 1],
156 sizeof(struct k8pnow_state));
159 memcpy(&cstate->state_table[j], &state,
160 sizeof(struct k8pnow_state));
167 k8pnow_states(struct k8pnow_cpu_state *cstate, uint32_t cpusig,
168 unsigned int fid, unsigned int vid)
174 for (p = (u_int8_t *) BIOS_PADDRTOVADDR(BIOS_START);
175 p < (u_int8_t *) BIOS_PADDRTOVADDR(BIOS_START + BIOS_LEN); p +=
177 if (memcmp(p, "AMDK7PNOW!", 10) == 0) {
178 psb = (struct psb_s *)p;
179 if (psb->version != PN8_PSB_VERSION)
181 cstate->vst = psb->ttime;
182 cstate->rvo = PN8_PSB_TO_RVO(psb->reserved);
183 cstate->irt = PN8_PSB_TO_IRT(psb->reserved);
184 cstate->mvs = PN8_PSB_TO_MVS(psb->reserved);
185 cstate->low = PN8_PSB_TO_BATT(psb->reserved);
186 p += sizeof(struct psb_s);
187 for (i = 0; i < psb->n_pst; ++i) {
188 pst = (struct pst_s *)p;
189 cstate->pll = pst->pll;
190 cstate->n_states = pst->n_states;
191 if (cpusig == pst->cpuid &&
192 pst->fid == fid && pst->vid == vid) {
193 return (k8pnow_decode_pst(cstate,
194 p += sizeof(struct pst_s)));
196 p += sizeof(struct pst_s) + 2
209 int cfid , cvid, fid = 0, vid = 0;
210 struct k8pnow_cpu_state *cstate;
211 status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
212 if (PN8_STA_PENDING(status))
214 cfid = PN8_STA_CFID(status);
215 cvid = PN8_STA_CVID(status);
216 cstate = k8pnow_current_state;
217 for (i = 0; i < cstate->n_states; i++) {
218 if (cstate->state_table[i].fid == cfid &&
219 cstate->state_table[i].vid == cvid) {
220 fid = cstate->state_table[i].fid;
221 vid = cstate->state_table[i].vid;
222 return (cstate->state_table[i].freq);
230 k8_powernow_setperf(unsigned int freq)
235 int cfid , cvid, fid = 0, vid = 0;
237 struct k8pnow_cpu_state *cstate;
239 * We dont do a k8pnow_read_pending_wait here, need to ensure that
240 * the change pending bit isn't stuck,
242 status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
243 if (PN8_STA_PENDING(status))
245 cfid = PN8_STA_CFID(status);
246 cvid = PN8_STA_CVID(status);
247 cstate = k8pnow_current_state;
248 for (i = 0; i < cstate->n_states; i++) {
249 if (cstate->state_table[i].freq >= freq) {
250 fid = cstate->state_table[i].fid;
251 vid = cstate->state_table[i].vid;
255 if (fid == cfid && vid == cvid) {
260 * Phase 1: Raise core voltage to requested VID if frequency is going
264 val = cvid - (1 << cstate->mvs);
265 WRITE_FIDVID(cfid, (val > 0) ? val : 0, 1ULL);
266 READ_PENDING_WAIT(status);
267 cvid = PN8_STA_CVID(status);
268 COUNT_OFF_VST(cstate->vst);
271 /* ... then raise to voltage + RVO (if required) */
272 for (rvo = cstate->rvo; rvo > 0 && cvid > 0; --rvo) {
274 * XXX It's not clear from spec if we have to do that in 0.25
275 * step or in MVS. Therefore do it as it's done under Linux
277 WRITE_FIDVID(cfid, cvid - 1, 1ULL);
278 READ_PENDING_WAIT(status);
279 cvid = PN8_STA_CVID(status);
280 COUNT_OFF_VST(cstate->vst);
282 /* Phase 2: change to requested core frequency */
284 uint32_t vco_fid, vco_cfid;
285 vco_fid = FID_TO_VCO_FID(fid);
286 vco_cfid = FID_TO_VCO_FID(cfid);
287 while (abs(vco_fid - vco_cfid) > 2) {
292 val = FID_TO_VCO_FID(cfid) + 2;
295 WRITE_FIDVID(val, cvid, (uint64_t) cstate->pll * 1000 / 5);
296 READ_PENDING_WAIT(status);
297 cfid = PN8_STA_CFID(status);
298 COUNT_OFF_IRT(cstate->irt);
299 vco_cfid = FID_TO_VCO_FID(cfid);
301 WRITE_FIDVID(fid, cvid, (uint64_t) cstate->pll * 1000 / 5);
302 READ_PENDING_WAIT(status);
303 cfid = PN8_STA_CFID(status);
304 COUNT_OFF_IRT(cstate->irt);
306 /* Phase 3: change to requested voltage */
308 WRITE_FIDVID(cfid, vid, 1ULL);
309 READ_PENDING_WAIT(status);
310 cvid = PN8_STA_CVID(status);
311 COUNT_OFF_VST(cstate->vst);
313 if (cfid == fid || cvid == vid)
314 cpuspeed = cstate->state_table[i].freq;
319 powernow_sysctl_helper(SYSCTL_HANDLER_ARGS)
323 struct k8pnow_cpu_state *cstate;
324 struct k8pnow_state *state;
325 cstate = k8pnow_current_state;
326 if (req->newptr != NULL) {
327 err = SYSCTL_IN(req, &fq, sizeof(fq));
330 if (fq != cpuspeed) {
331 for (i = cstate->n_states; i > 0; i--) {
332 state = &cstate->state_table[i - 1];
333 if (fq == state->freq) {
334 k8_powernow_setperf(fq);
340 err = SYSCTL_OUT(req, &cpuspeed, sizeof(cpuspeed));
345 static struct sysctl_ctx_list machdep_powernow_ctx;
346 static char freqs_available[80];
352 size_t len , freq_len;
353 uint32_t maxfid, maxvid, i;
354 struct k8pnow_cpu_state *cstate;
355 struct k8pnow_state *state;
356 const char *techname;
359 struct sysctl_oid *oid, *leaf;
361 do_cpuid(0x80000000, regs);
362 if (regs[0] < 0x80000007)
364 do_cpuid(0x80000007, regs);
365 if (!(regs[3] & AMD_PN_FID_VID))
367 /* Extended CPUID signature value */
368 do_cpuid(0x80000001, regs);
369 cstate = kmalloc(sizeof(struct k8pnow_cpu_state), M_DEVBUF, M_WAITOK);
370 cstate->n_states = 0;
372 status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
373 maxfid = PN8_STA_MFID(status);
374 maxvid = PN8_STA_MVID(status);
376 if (PN8_STA_SFID(status) != PN8_STA_MFID(status))
377 techname = "PowerNow!";
379 techname = "Cool`n'Quiet";
380 k8pnow_states(cstate, regs[0], maxfid, maxvid);
382 if (cstate->n_states) {
383 freq_len = cstate->n_states * (sizeof("9999 ") - 1) + 1;
384 kprintf("%s speeds:",
386 for (i = cstate->n_states; i > 0; i--) {
387 state = &cstate->state_table[i - 1];
388 kprintf(" %d", state->freq);
389 len += ksnprintf(freqs_available + len, freq_len - len, "%d%s",
394 k8pnow_current_state = cstate;
395 k8_powernow_setperf(k8_get_curfreq());
397 kfree(cstate, M_DEVBUF);
398 kprintf("powernow: no power states found\n");
403 * Setup the sysctl sub-tree machdep.powernow.*
405 oid = SYSCTL_ADD_NODE(&machdep_powernow_ctx,
406 SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, "powernow",
407 CTLFLAG_RD, NULL, "");
410 oid = SYSCTL_ADD_NODE(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid),
411 OID_AUTO, "frequency", CTLFLAG_RD, NULL, "");
414 leaf = SYSCTL_ADD_PROC(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid),
415 OID_AUTO, "target", CTLTYPE_INT | CTLFLAG_RW, NULL, 0,
416 powernow_sysctl_helper, "I",
417 "Target CPU frequency for AMD PowerNow!");
420 leaf = SYSCTL_ADD_PROC(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid),
421 OID_AUTO, "current", CTLTYPE_INT | CTLFLAG_RD, NULL, 0,
422 powernow_sysctl_helper, "I",
423 "Current CPU frequency for AMD PowerNow!");
426 leaf = SYSCTL_ADD_STRING(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid),
427 OID_AUTO, "available", CTLFLAG_RD, freqs_available,
428 sizeof(freqs_available),
429 "CPU frequencies supported by AMD PowerNow!");
436 powernow_modevh(struct module *m, int what, void *arg __unused)
442 error = sysctl_ctx_init(&machdep_powernow_ctx);
445 error = powernow_init();
448 if (k8pnow_current_state)
449 kfree(k8pnow_current_state, M_DEVBUF);
450 error = sysctl_ctx_free(&machdep_powernow_ctx);
458 static moduledata_t powernow_mod = {
464 DECLARE_MODULE(powernow, powernow_mod, SI_BOOT2_KLD, SI_ORDER_ANY);