powernow: AMD K8 frequency control.
[dragonfly.git] / sys / dev / powermng / powernow / powernow.c
1 /*
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.
5  * 
6  * This code is derived from software contributed to The NetBSD Foundation by
7  * Juan Romero Pardines and Martin Vegiard.
8  * 
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.
26  */
27 /* AMD POWERNOW K8 driver */
28
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
43 #define PN8_STA_MFID(x)                 (((x) >> 16) & 0x3f)
44 #define PN8_STA_MVID(x)                 (((x) >> 48) & 0x1f)
45 #define PN8_STA_SFID(x)                 (((x) >> 8) & 0x3f)
46
47 /*
48  * MSRs and bits used by PowerNow! technology
49  */
50 #define MSR_AMDK7_FIDVID_CTL            0xc0010041
51 #define MSR_AMDK7_FIDVID_STATUS         0xc0010042
52 #define AMD_PN_FID_VID                  0x06
53
54 #define BIOS_START                      0xe0000
55 #define BIOS_LEN                        0x20000
56 #define BIOS_STEP                       16
57
58 #define PN8_PSB_VERSION                 0x14
59 #define PN8_PSB_TO_RVO(x)               ((x) & 0x03)
60 #define PN8_PSB_TO_IRT(x)               (((x) >> 2) & 0x03)
61 #define PN8_PSB_TO_MVS(x)               (((x) >> 4) & 0x03)
62 #define PN8_PSB_TO_BATT(x)              (((x) >> 6) & 0x03)
63 /* Bitfields used by K8 */
64 #define PN8_CTR_FID(x)                  ((x) & 0x3f)
65 #define PN8_CTR_VID(x)                  (((x) & 0x1f) << 8)
66 #define PN8_CTR_PENDING(x)              (((x) & 1) << 32)
67 #define PN8_STA_CFID(x)                 ((x) & 0x3f)
68 #define PN8_STA_SFID(x)                 (((x) >> 8) & 0x3f)
69 #define PN8_STA_MFID(x)                 (((x) >> 16) & 0x3f)
70 #define PN8_STA_PENDING(x)              (((x) >> 31) & 0x01)
71 #define PN8_STA_CVID(x)                 (((x) >> 32) & 0x1f)
72 #define PN8_STA_SVID(x)                 (((x) >> 40) & 0x1f)
73 #define PN8_STA_MVID(x)                 (((x) >> 48) & 0x1f)
74 #define PN8_PLL_LOCK(x)                 ((x) * 1000/5)
75 #define WRITE_FIDVID(fid, vid, ctrl)    \
76         wrmsr(MSR_AMDK7_FIDVID_CTL,     \
77             (((ctrl) << 32) | (1ULL << 16) | ((vid) << 8) | (fid)))
78 #define COUNT_OFF_IRT(irt)              DELAY(10 * (1 << (irt)))
79 #define COUNT_OFF_VST(vst)              DELAY(20 * (vst))
80 #define FID_TO_VCO_FID(fid)             \
81         (((fid) < 8) ? (8 + ((fid) << 1)) : (fid))
82
83 #define READ_PENDING_WAIT(status)                               \
84         do {                                                    \
85                 (status) = rdmsr(MSR_AMDK7_FIDVID_STATUS);      \
86         } while (PN8_STA_PENDING(status))
87 #define abs(x) ( x < 0 ? -x : x )
88
89 #define POWERNOW_MAX_STATES             16
90
91 struct k8pnow_state {
92         int             freq;
93         uint8_t         fid;
94         uint8_t         vid;
95 };
96
97 struct k8pnow_cpu_state {
98         struct k8pnow_state state_table[POWERNOW_MAX_STATES];
99         unsigned int    n_states;
100         unsigned int    vst;
101         unsigned int    mvs;
102         unsigned int    pll;
103         unsigned int    rvo;
104         unsigned int    irt;
105         int             low;
106 };
107
108 struct psb_s {
109         char            signature [10]; /* AMDK7PNOW! */
110         uint8_t         version;
111         uint8_t         flags;
112         uint16_t        ttime;  /* Min Settling time */
113         uint8_t         reserved;
114         uint8_t         n_pst;
115 };
116 struct pst_s {
117         uint32_t        cpuid;
118         uint8_t         pll;
119         uint8_t         fid;
120         uint8_t         vid;
121         uint8_t         n_states;
122 };
123
124 static struct k8pnow_cpu_state *k8pnow_current_state = NULL;
125 int             cpuspeed;
126
127 int
128 k8pnow_states(struct k8pnow_cpu_state *cstate, uint32_t cpusig,
129               unsigned int fid, unsigned int vid);
130 int
131                 k8pnow_decode_pst(struct k8pnow_cpu_state *cstate, uint8_t * p);
132
133 /*
134  * Given a set of pair of fid/vid, and number of performance states, compute
135  * state_table via an insertion sort.
136  */
137 int
138 k8pnow_decode_pst(struct k8pnow_cpu_state *cstate, uint8_t * p)
139 {
140         int             i         , j, n;
141         struct k8pnow_state state;
142         for (n = 0, i = 0; i < cstate->n_states; i++) {
143                 state.fid = *p++;
144                 state.vid = *p++;
145
146                 /*
147                  * The minimum supported frequency per the data sheet is
148                  * 800MHz The maximum supported frequency is 5000MHz.
149                  */
150                 state.freq = 800 + state.fid * 100;
151                 j = n;
152                 while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
153                         memcpy(&cstate->state_table[j],
154                                &cstate->state_table[j - 1],
155                                sizeof(struct k8pnow_state));
156                         --j;
157                 }
158                 memcpy(&cstate->state_table[j], &state,
159                        sizeof(struct k8pnow_state));
160                 n++;
161         }
162         return 1;
163 }
164
165 int
166 k8pnow_states(struct k8pnow_cpu_state *cstate, uint32_t cpusig,
167               unsigned int fid, unsigned int vid)
168 {
169         struct psb_s   *psb;
170         struct pst_s   *pst;
171         uint8_t        *p;
172         int             i;
173         for (p = (u_int8_t *) BIOS_PADDRTOVADDR(BIOS_START);
174              p < (u_int8_t *) BIOS_PADDRTOVADDR(BIOS_START + BIOS_LEN); p +=
175              BIOS_STEP) {
176                 if (memcmp(p, "AMDK7PNOW!", 10) == 0) {
177                         psb = (struct psb_s *)p;
178                         if (psb->version != PN8_PSB_VERSION)
179                                 return 0;
180                         cstate->vst = psb->ttime;
181                         cstate->rvo = PN8_PSB_TO_RVO(psb->reserved);
182                         cstate->irt = PN8_PSB_TO_IRT(psb->reserved);
183                         cstate->mvs = PN8_PSB_TO_MVS(psb->reserved);
184                         cstate->low = PN8_PSB_TO_BATT(psb->reserved);
185                         p += sizeof(struct psb_s);
186                         for (i = 0; i < psb->n_pst; ++i) {
187                                 pst = (struct pst_s *)p;
188                                 cstate->pll = pst->pll;
189                                 cstate->n_states = pst->n_states;
190                                 if (cpusig == pst->cpuid &&
191                                     pst->fid == fid && pst->vid == vid) {
192                                         return (k8pnow_decode_pst(cstate,
193                                                 p += sizeof(struct pst_s)));
194                                 }
195                                 p += sizeof(struct pst_s) + 2
196                                         * cstate->n_states;
197                         }
198                 }
199         }
200         return 0;
201 }
202
203 static int
204 k8_get_curfreq(void)
205 {
206         unsigned int    i;
207         uint64_t        status;
208         int             cfid      , cvid, fid = 0, vid = 0;
209         struct k8pnow_cpu_state *cstate;
210         status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
211         if (PN8_STA_PENDING(status))
212                 return 1;
213         cfid = PN8_STA_CFID(status);
214         cvid = PN8_STA_CVID(status);
215         cstate = k8pnow_current_state;
216         for (i = 0; i < cstate->n_states; i++) {
217                 if (cstate->state_table[i].fid == cfid &&
218                     cstate->state_table[i].vid == cvid) {
219                         fid = cstate->state_table[i].fid;
220                         vid = cstate->state_table[i].vid;
221                         return (cstate->state_table[i].freq);
222                 }
223         }
224         /* Not reached */
225         return -1;
226 }
227
228 static int
229 k8_powernow_setperf(unsigned int freq)
230 {
231         unsigned int    i;
232         uint64_t        status;
233         uint32_t        val;
234         int             cfid      , cvid, fid = 0, vid = 0;
235         int             rvo;
236         struct k8pnow_cpu_state *cstate;
237         /*
238          * We dont do a k8pnow_read_pending_wait here, need to ensure that
239          * the change pending bit isn't stuck,
240          */
241         status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
242         if (PN8_STA_PENDING(status))
243                 return 1;
244         cfid = PN8_STA_CFID(status);
245         cvid = PN8_STA_CVID(status);
246         cstate = k8pnow_current_state;
247         for (i = 0; i < cstate->n_states; i++) {
248                 if (cstate->state_table[i].freq >= freq) {
249                         fid = cstate->state_table[i].fid;
250                         vid = cstate->state_table[i].vid;
251                         break;
252                 }
253         }
254         if (fid == cfid && vid == cvid) {
255                 cpuspeed = freq;
256                 return 0;
257         }
258         /*
259          * Phase 1: Raise core voltage to requested VID if frequency is going
260          * up.
261          */
262         while (cvid > vid) {
263                 val = cvid - (1 << cstate->mvs);
264                 WRITE_FIDVID(cfid, (val > 0) ? val : 0, 1ULL);
265                 READ_PENDING_WAIT(status);
266                 cvid = PN8_STA_CVID(status);
267                 COUNT_OFF_VST(cstate->vst);
268         }
269
270         /* ... then raise to voltage + RVO (if required) */
271         for (rvo = cstate->rvo; rvo > 0 && cvid > 0; --rvo) {
272                 /*
273                  * XXX It's not clear from spec if we have to do that in 0.25
274                  * step or in MVS.  Therefore do it as it's done under Linux
275                  */
276                 WRITE_FIDVID(cfid, cvid - 1, 1ULL);
277                 READ_PENDING_WAIT(status);
278                 cvid = PN8_STA_CVID(status);
279                 COUNT_OFF_VST(cstate->vst);
280         }
281         /* Phase 2: change to requested core frequency */
282         if (cfid != fid) {
283                 uint32_t        vco_fid, vco_cfid;
284                 vco_fid = FID_TO_VCO_FID(fid);
285                 vco_cfid = FID_TO_VCO_FID(cfid);
286                 while (abs(vco_fid - vco_cfid) > 2) {
287                         if (fid > cfid) {
288                                 if (cfid > 6)
289                                         val = cfid + 2;
290                                 else
291                                         val = FID_TO_VCO_FID(cfid) + 2;
292                         } else
293                                 val = cfid - 2;
294                         WRITE_FIDVID(val, cvid, (uint64_t) cstate->pll * 1000 / 5);
295                         READ_PENDING_WAIT(status);
296                         cfid = PN8_STA_CFID(status);
297                         COUNT_OFF_IRT(cstate->irt);
298                         vco_cfid = FID_TO_VCO_FID(cfid);
299                 }
300                 WRITE_FIDVID(fid, cvid, (uint64_t) cstate->pll * 1000 / 5);
301                 READ_PENDING_WAIT(status);
302                 cfid = PN8_STA_CFID(status);
303                 COUNT_OFF_IRT(cstate->irt);
304         }
305         /* Phase 3: change to requested voltage */
306         if (cvid != vid) {
307                 WRITE_FIDVID(cfid, vid, 1ULL);
308                 READ_PENDING_WAIT(status);
309                 cvid = PN8_STA_CVID(status);
310                 COUNT_OFF_VST(cstate->vst);
311         }
312         if (cfid == fid || cvid == vid)
313                 cpuspeed = cstate->state_table[i].freq;
314         return 0;
315 }
316
317 static int
318 powernow_sysctl_helper(SYSCTL_HANDLER_ARGS)
319 {
320         int             fq        , err = 0;
321         int             i;
322         struct k8pnow_cpu_state *cstate;
323         struct k8pnow_state *state;
324         cstate = k8pnow_current_state;
325         if (req->newptr != NULL) {
326                 err = SYSCTL_IN(req, &fq, sizeof(fq));
327                 if (err)
328                         return err;
329                 if (fq != cpuspeed) {
330                         for (i = cstate->n_states; i > 0; i--) {
331                                 state = &cstate->state_table[i - 1];
332                                 if (fq == state->freq) {
333                                         k8_powernow_setperf(fq);
334                                         break;
335                                 }
336                         }
337                 }
338         } else {
339                 err = SYSCTL_OUT(req, &cpuspeed, sizeof(cpuspeed));
340         }
341         return err;
342 }
343
344 static struct sysctl_ctx_list machdep_powernow_ctx;
345 static char     freqs_available[80];
346
347 static int
348 powernow_init(void)
349 {
350         uint64_t        status;
351         size_t          len    , freq_len;
352         uint32_t        maxfid, maxvid, i;
353         struct k8pnow_cpu_state *cstate;
354         struct k8pnow_state *state;
355         const char     *techname;
356         u_int32_t       regs  [4];
357         cpuspeed = 0;
358         struct sysctl_oid *oid, *leaf;
359
360         do_cpuid(0x80000000, regs);
361         if (regs[0] < 0x80000007)
362                 return 1;
363         do_cpuid(0x80000007, regs);
364         if (!(regs[3] & AMD_PN_FID_VID))
365                 return 2;
366         /* Extended CPUID signature value */
367         do_cpuid(0x80000001, regs);
368         cstate = kmalloc(sizeof(struct k8pnow_cpu_state), M_DEVBUF, M_WAITOK);
369         cstate->n_states = 0;
370
371         status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
372         maxfid = PN8_STA_MFID(status);
373         maxvid = PN8_STA_MVID(status);
374
375         if (PN8_STA_SFID(status) != PN8_STA_MFID(status))
376                 techname = "PowerNow!";
377         else
378                 techname = "Cool`n'Quiet";
379         k8pnow_states(cstate, regs[0], maxfid, maxvid);
380         len = 0;
381         if (cstate->n_states) {
382                 freq_len = cstate->n_states * (sizeof("9999 ") - 1) + 1;
383                 kprintf("%s speeds:",
384                         techname);
385                 for (i = cstate->n_states; i > 0; i--) {
386                         state = &cstate->state_table[i - 1];
387                         kprintf(" %d", state->freq);
388                         len += ksnprintf(freqs_available + len, freq_len - len, "%d%s",
389                                          state->freq,
390                                          i > 1 ? " " : "");
391                 }
392                 kprintf(" MHz\n");
393                 k8pnow_current_state = cstate;
394                 k8_powernow_setperf(k8_get_curfreq());
395         } else {
396                 kfree(cstate, M_DEVBUF);
397                 kprintf("powernow: no power states found\n");
398                 return 3;
399         }
400
401         /*
402          * Setup the sysctl sub-tree machdep.powernow.*
403          */
404         oid = SYSCTL_ADD_NODE(&machdep_powernow_ctx,
405                      SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, "powernow",
406                               CTLFLAG_RD, NULL, "");
407         if (oid == NULL)
408                 return (EOPNOTSUPP);
409         oid = SYSCTL_ADD_NODE(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid),
410                               OID_AUTO, "frequency", CTLFLAG_RD, NULL, "");
411         if (oid == NULL)
412                 return (EOPNOTSUPP);
413         leaf = SYSCTL_ADD_PROC(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid),
414                       OID_AUTO, "target", CTLTYPE_INT | CTLFLAG_RW, NULL, 0,
415                                powernow_sysctl_helper, "I",
416                                "Target CPU frequency for AMD PowerNow!");
417         if (leaf == NULL)
418                 return (EOPNOTSUPP);
419         leaf = SYSCTL_ADD_PROC(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid),
420                      OID_AUTO, "current", CTLTYPE_INT | CTLFLAG_RD, NULL, 0,
421                                powernow_sysctl_helper, "I",
422                                "Current CPU frequency for AMD PowerNow!");
423         if (leaf == NULL)
424                 return (EOPNOTSUPP);
425         leaf = SYSCTL_ADD_STRING(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid),
426                          OID_AUTO, "available", CTLFLAG_RD, freqs_available,
427                                  sizeof(freqs_available),
428                               "CPU frequencies supported by AMD PowerNow!");
429         if (leaf == NULL)
430                 return (EOPNOTSUPP);
431         return (0);
432 }
433
434 static int
435 powernow_modevh(struct module *m, int what, void *arg __unused)
436 {
437         int             error;
438
439         switch (what) {
440         case MOD_LOAD:
441                 error = sysctl_ctx_init(&machdep_powernow_ctx);
442                 if (error != 0)
443                         break;
444                 error = powernow_init();
445                 break;
446         case MOD_UNLOAD:
447                 if (k8pnow_current_state)
448                         kfree(k8pnow_current_state, M_DEVBUF);
449                 error = sysctl_ctx_free(&machdep_powernow_ctx);
450                 break;
451         default:
452                 error = EINVAL;
453                 break;
454         }
455         return (error);
456 }
457 static moduledata_t powernow_mod = {
458         "powernow",
459         powernow_modevh,
460         NULL,
461 };
462
463 DECLARE_MODULE(powernow, powernow_mod, SI_BOOT2_KLD, SI_ORDER_ANY);