powernow.c

polachok, 05/10/2009 09:38 PM

Download (15.7 KB)

 
1
/*
2
 * Copyright (c) 2004 Martin V\xe9giard.
3
 * Copyright (c) 2004-2005 Bruno Ducrot
4
 * Copyright (c) 2004 FUKUDA Nobuhiko <nfukuda@spa.is.uec.ac.jp>
5
 * Copyright (c) 2004, 2006 The NetBSD Foundation, Inc.
6
 * All rights reserved.
7
 *
8
 * This code is derived from software contributed to The NetBSD Foundation
9
 * by Juan Romero Pardines and Martin Vegiard.
10
 *
11
 * Redistribution and use in source and binary forms, with or without
12
 * modification, are permitted provided that the following conditions
13
 * are met:
14
 * 1. Redistributions of source code must retain the above copyright
15
 *    notice, this list of conditions and the following disclaimer.
16
 * 2. Redistributions in binary form must reproduce the above copyright
17
 *    notice, this list of conditions and the following disclaimer in the
18
 *    documentation and/or other materials provided with the distribution.
19
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
 */
30
/* AMD POWERNOW K8 driver */
31

    
32
#include <sys/param.h>
33
#include <sys/systm.h>
34
#include <sys/malloc.h>
35
#include <sys/kernel.h>
36
#include <sys/module.h>
37
#include <sys/sysctl.h>
38
#include <bus/isa/isa.h>
39
#include <machine/cpu.h>
40
#include <machine/pmap.h>
41
#include <machine/pc/bios.h>
42
#include <machine/cpufunc.h>
43
#include <machine/md_var.h>
44
#include <machine/specialreg.h>
45

    
46
#define PN8_STA_MFID(x)                 (((x) >> 16) & 0x3f)
47
#define PN8_STA_MVID(x)                 (((x) >> 48) & 0x1f)
48
#define PN8_STA_SFID(x)                 (((x) >> 8) & 0x3f)
49

    
50
/*
51
 * MSRs and bits used by PowerNow! technology
52
 */
53
#define MSR_AMDK7_FIDVID_CTL            0xc0010041
54
#define MSR_AMDK7_FIDVID_STATUS         0xc0010042
55
#define AMD_PN_FID_VID                  0x06
56

    
57
#define BIOS_START                      0xe0000
58
#define BIOS_LEN                        0x20000
59
#define BIOS_STEP                       16
60

    
61
#define PN8_PSB_VERSION                 0x14
62
#define PN8_PSB_TO_RVO(x)               ((x) & 0x03)
63
#define PN8_PSB_TO_IRT(x)               (((x) >> 2) & 0x03)
64
#define PN8_PSB_TO_MVS(x)               (((x) >> 4) & 0x03)
65
#define PN8_PSB_TO_BATT(x)              (((x) >> 6) & 0x03)
66
/* Bitfields used by K8 */
67
#define PN8_CTR_FID(x)                  ((x) & 0x3f)
68
#define PN8_CTR_VID(x)                  (((x) & 0x1f) << 8)
69
#define PN8_CTR_PENDING(x)              (((x) & 1) << 32)
70
#define PN8_STA_CFID(x)                 ((x) & 0x3f)
71
#define PN8_STA_SFID(x)                 (((x) >> 8) & 0x3f)
72
#define PN8_STA_MFID(x)                 (((x) >> 16) & 0x3f)
73
#define PN8_STA_PENDING(x)              (((x) >> 31) & 0x01)
74
#define PN8_STA_CVID(x)                 (((x) >> 32) & 0x1f)
75
#define PN8_STA_SVID(x)                 (((x) >> 40) & 0x1f)
76
#define PN8_STA_MVID(x)                 (((x) >> 48) & 0x1f)
77
#define PN8_PLL_LOCK(x)                 ((x) * 1000/5)
78
#define WRITE_FIDVID(fid, vid, ctrl)    \
79
        wrmsr(MSR_AMDK7_FIDVID_CTL,     \
80
            (((ctrl) << 32) | (1ULL << 16) | ((vid) << 8) | (fid)))
81
#define COUNT_OFF_IRT(irt)              DELAY(10 * (1 << (irt)))
82
#define COUNT_OFF_VST(vst)              DELAY(20 * (vst))
83
#define FID_TO_VCO_FID(fid)             \
84
        (((fid) < 8) ? (8 + ((fid) << 1)) : (fid))
85

    
86
#define READ_PENDING_WAIT(status)                               \
87
        do {                                                    \
88
                (status) = rdmsr(MSR_AMDK7_FIDVID_STATUS);      \
89
        } while (PN8_STA_PENDING(status))
90
#define abs(x) ( x < 0 ? -x : x )
91

    
92
#define POWERNOW_MAX_STATES             16
93

    
94
struct k8pnow_state {
95
        int freq;
96
        uint8_t fid;
97
        uint8_t vid;
98
};
99

    
100
struct k8pnow_cpu_state {
101
        struct k8pnow_state state_table[POWERNOW_MAX_STATES];
102
        unsigned int n_states;
103
        unsigned int sgtc;
104
        unsigned int vst;
105
        unsigned int mvs;
106
        unsigned int pll;
107
        unsigned int rvo;
108
        unsigned int irt;
109
        int low;
110
};
111

    
112
struct psb_s {
113
        char signature[10];     /* AMDK7PNOW! */
114
        uint8_t version;
115
        uint8_t flags;
116
        uint16_t ttime;         /* Min Settling time */
117
        uint8_t reserved;
118
        uint8_t n_pst;
119
};
120
struct pst_s {
121
        uint32_t cpuid;
122
        uint8_t pll;
123
        uint8_t fid;
124
        uint8_t vid;
125
        uint8_t n_states;
126
};
127

    
128
static struct k8pnow_cpu_state *k8pnow_current_state = NULL;
129
int cpuspeed;
130

    
131
int
132
k8pnow_states(struct k8pnow_cpu_state *cstate, uint32_t cpusig,
133
    unsigned int fid, unsigned int vid);
134
int
135
k8pnow_decode_pst(struct k8pnow_cpu_state *cstate, uint8_t *p);
136
/*
137
 * Given a set of pair of fid/vid, and number of performance states,
138
 * compute state_table via an insertion sort.
139
 */
140
int
141
k8pnow_decode_pst(struct k8pnow_cpu_state *cstate, uint8_t *p)
142
{
143
        int i, j, n;
144
        struct k8pnow_state state;
145
        for (n = 0, i = 0; i < cstate->n_states; i++) {
146
                state.fid = *p++;
147
                state.vid = *p++;
148
                /*
149
                 * The minimum supported frequency per the data sheet is 800MHz
150
                 * The maximum supported frequency is 5000MHz.
151
                 */
152
                state.freq = 800 + state.fid * 100;
153
                j = n;
154
                while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
155
                        memcpy(&cstate->state_table[j],
156
                            &cstate->state_table[j - 1],
157
                            sizeof(struct k8pnow_state));
158
                        --j;
159
                }
160
                memcpy(&cstate->state_table[j], &state,
161
                         sizeof(struct k8pnow_state));
162
                n++;
163
        }
164
        return 1;
165
}
166

    
167
int
168
k8pnow_states(struct k8pnow_cpu_state *cstate, uint32_t cpusig,
169
    unsigned int fid, unsigned int vid)
170
{
171
        struct psb_s *psb;
172
        struct pst_s *pst;
173
        uint8_t *p;
174
        int i;
175
        for (p = (u_int8_t *)BIOS_PADDRTOVADDR(BIOS_START);
176
            p < (u_int8_t *)BIOS_PADDRTOVADDR(BIOS_START + BIOS_LEN); p +=
177
            BIOS_STEP) {
178
                if (memcmp(p, "AMDK7PNOW!", 10) == 0) {
179
                        kprintf("K7PNOW label found\n");
180
                        psb = (struct psb_s *)p;
181
                        if (psb->version != PN8_PSB_VERSION)
182
                                return 0;
183
                        cstate->vst = psb->ttime;
184
                        cstate->rvo = PN8_PSB_TO_RVO(psb->reserved);
185
                        cstate->irt = PN8_PSB_TO_IRT(psb->reserved);
186
                        cstate->mvs = PN8_PSB_TO_MVS(psb->reserved);
187
                        cstate->low = PN8_PSB_TO_BATT(psb->reserved);
188
                        p+= sizeof(struct psb_s);
189
                        for (i = 0; i < psb->n_pst; ++i) {
190
                                pst = (struct pst_s *) p;
191
                                cstate->pll = pst->pll;
192
                                cstate->n_states = pst->n_states;
193
                                if (cpusig == pst->cpuid &&
194
                                 pst->fid == fid && pst->vid == vid) {
195
                                        return (k8pnow_decode_pst(cstate,
196
                                            p+= sizeof (struct pst_s)));
197
                                }
198
                                p += sizeof(struct pst_s) + 2
199
                                     * cstate->n_states;
200
                        }
201
                }
202
        }
203
        return 0;
204
}
205

    
206
static int
207
k8_powernow_setperf(unsigned int freq)
208
{
209
        unsigned int i;
210
        uint64_t status;
211
        uint32_t val;
212
        int cfid, cvid, fid = 0, vid = 0;
213
        int rvo;
214
        struct k8pnow_cpu_state *cstate;
215
        /*
216
         * We dont do a k8pnow_read_pending_wait here, need to ensure that the
217
         * change pending bit isn't stuck,
218
         */
219
        status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
220
        if (PN8_STA_PENDING(status))
221
                return 1;
222
        cfid = PN8_STA_CFID(status);
223
        cvid = PN8_STA_CVID(status);
224
        cstate = k8pnow_current_state;
225
        //kprintf("%s: cstate->n_states=%d\n", __func__, cstate->n_states);
226
        for (i = 0; i < cstate->n_states; i++) {
227
                if (cstate->state_table[i].freq >= freq) {
228
                        //kprintf("%s: freq=%d\n", __func__, freq);
229
                        fid = cstate->state_table[i].fid;
230
                        vid = cstate->state_table[i].vid;
231
                        //kprintf("%s: fid=%d vid=%d\n", __func__, fid, vid);
232
                        break;
233
                }
234
        }
235
        if (fid == cfid && vid == cvid) {
236
                cpuspeed = freq;
237
                return 0;
238
        }
239
        /*
240
         * Phase 1: Raise core voltage to requested VID if frequency is
241
         * going up.
242
         */
243
        while (cvid > vid) {
244
                val = cvid - (1 << cstate->mvs);
245
                WRITE_FIDVID(cfid, (val > 0) ? val : 0, 1ULL);
246
                READ_PENDING_WAIT(status);
247
                cvid = PN8_STA_CVID(status);
248
                COUNT_OFF_VST(cstate->vst);
249
        }
250

    
251
        /* ... then raise to voltage + RVO (if required) */
252
        for (rvo = cstate->rvo; rvo > 0 && cvid > 0; --rvo) {
253
                /* XXX It's not clear from spec if we have to do that
254
                 * in 0.25 step or in MVS.  Therefore do it as it's done
255
                 * under Linux */
256
                WRITE_FIDVID(cfid, cvid - 1, 1ULL);
257
                READ_PENDING_WAIT(status);
258
                cvid = PN8_STA_CVID(status);
259
                COUNT_OFF_VST(cstate->vst);
260
        }
261
         /* Phase 2: change to requested core frequency */
262
        if (cfid != fid) {
263
                uint32_t vco_fid, vco_cfid;
264
                vco_fid = FID_TO_VCO_FID(fid);
265
                vco_cfid = FID_TO_VCO_FID(cfid);
266
                while (abs(vco_fid - vco_cfid) > 2) {
267
                        if (fid > cfid) {
268
                                if (cfid > 6)
269
                                        val = cfid + 2;
270
                                else
271
                                        val = FID_TO_VCO_FID(cfid) + 2;
272
                        } else
273
                                val = cfid - 2;
274
                        WRITE_FIDVID(val, cvid, (uint64_t)cstate->pll * 1000 / 5);
275
                        READ_PENDING_WAIT(status);
276
                        cfid = PN8_STA_CFID(status);
277
                        COUNT_OFF_IRT(cstate->irt);
278
                        vco_cfid = FID_TO_VCO_FID(cfid);
279
                }
280
                WRITE_FIDVID(fid, cvid, (uint64_t) cstate->pll * 1000 / 5);
281
                READ_PENDING_WAIT(status);
282
                cfid = PN8_STA_CFID(status);
283
                COUNT_OFF_IRT(cstate->irt);
284
        }
285
         /* Phase 3: change to requested voltage */
286
        if (cvid != vid) {
287
                WRITE_FIDVID(cfid, vid, 1ULL);
288
                READ_PENDING_WAIT(status);
289
                cvid = PN8_STA_CVID(status);
290
                COUNT_OFF_VST(cstate->vst);
291
        }
292
        if (cfid == fid || cvid == vid)
293
                cpuspeed = cstate->state_table[i].freq;
294
        return 0;
295
}
296

    
297
static int
298
powernow_sysctl_helper(SYSCTL_HANDLER_ARGS)
299
{
300
        int      fq, err = 0;
301
        int      i;
302
        struct k8pnow_cpu_state *cstate;
303
        struct k8pnow_state *state;
304
        cstate = k8pnow_current_state;
305
        if (req->newptr != NULL) {
306
                err = SYSCTL_IN(req, &fq, sizeof(fq));
307
                if (err)
308
                        return err;
309
                if (fq != cpuspeed) {
310
                        for (i = cstate->n_states; i > 0; i--) {
311
                                state = &cstate->state_table[i-1];
312
                                if(fq == state->freq) {
313
                                        k8_powernow_setperf(fq);
314
                                        break;
315
                                }
316
                        }
317
                }
318
        } else {
319
                err = SYSCTL_OUT(req, &cpuspeed, sizeof(cpuspeed));
320
        }
321
        return err;
322
}
323

    
324
static struct sysctl_ctx_list   machdep_powernow_ctx;
325
static char freqs_available[80];
326

    
327
static int
328
powernow_init(void)
329
{
330
        uint64_t status;
331
        size_t len, freq_len;
332
        uint32_t maxfid, maxvid, i; 
333
        struct k8pnow_cpu_state *cstate;
334
        struct k8pnow_state *state;
335
        const char *techname;
336
        u_int32_t regs[4];
337
        cpuspeed = 0;
338
        struct sysctl_oid       *oid, *leaf;
339
        do_cpuid(0x80000000, regs);
340
        if (regs[0] < 0x80000007)
341
                return 1;
342
        do_cpuid(0x80000007, regs);
343
        if (!(regs[3] & AMD_PN_FID_VID))
344
                return 2;
345
        /* Extended CPUID signature value */
346
        do_cpuid(0x80000001, regs);
347
        cstate = kmalloc(sizeof(struct k8pnow_cpu_state), M_DEVBUF, M_WAITOK);
348
        cstate->n_states = 0;
349

    
350
        status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
351
        maxfid = PN8_STA_MFID(status);
352
        maxvid = PN8_STA_MVID(status);
353

    
354
        if (PN8_STA_SFID(status) != PN8_STA_MFID(status))
355
                techname = "PowerNow!";
356
        else
357
                techname = "Cool`n'Quiet";
358

    
359
        k8pnow_states(cstate, regs[0], maxfid, maxvid);
360

    
361
        len = 0;
362
        if (cstate->n_states) {
363
                freq_len = cstate->n_states * (sizeof("9999 ")-1) + 1;
364
                kprintf("%s speeds:",
365
                    techname);
366
                for (i = cstate->n_states; i > 0; i--) {
367
                        state = &cstate->state_table[i-1];
368
                        kprintf(" %d", state->freq);
369
                        len += ksnprintf(freqs_available + len, freq_len - len, "%d%s",
370
                            state->freq,
371
                            i > 1 ? " " : "");
372

    
373
                }
374
                kprintf(" MHz\n");
375
                k8pnow_current_state = cstate;
376
                k8_powernow_setperf(cstate->state_table[0].freq);
377
        } else {
378
                kfree(cstate, M_DEVBUF);
379
                kprintf("powernow: no power states found\n");
380
                return 3;
381
        }
382

    
383
         /*
384
         * Setup the sysctl sub-tree machdep.powernow.*
385
         */
386
        oid = SYSCTL_ADD_NODE(&machdep_powernow_ctx,
387
            SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, "powernow",
388
            CTLFLAG_RD, NULL, "");
389
        if (oid == NULL)
390
                return(EOPNOTSUPP);
391
        oid = SYSCTL_ADD_NODE(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid),
392
            OID_AUTO, "frequency", CTLFLAG_RD, NULL, "");
393
        if (oid == NULL)
394
                return(EOPNOTSUPP);
395
        leaf = SYSCTL_ADD_PROC(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid),
396
            OID_AUTO, "target", CTLTYPE_INT | CTLFLAG_RW, NULL, 0,
397
            powernow_sysctl_helper, "I",
398
            "Target CPU frequency for AMD PowerNow!");
399
        if (leaf == NULL)
400
                return(EOPNOTSUPP);
401
        leaf = SYSCTL_ADD_PROC(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid),
402
            OID_AUTO, "current", CTLTYPE_INT | CTLFLAG_RD, NULL, 0,
403
            powernow_sysctl_helper, "I",
404
            "Current CPU frequency for AMD PowerNow!");
405
        if (leaf == NULL)
406
                return(EOPNOTSUPP);
407
        leaf = SYSCTL_ADD_STRING(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid),
408
            OID_AUTO, "available", CTLFLAG_RD, freqs_available,
409
            sizeof(freqs_available),
410
            "CPU frequencies supported by AMD PowerNow!");
411
        if (leaf == NULL)
412
                return(EOPNOTSUPP);
413
        return(0);
414
}
415

    
416
static int
417
powernow_modevh(struct module *m, int what, void *arg __unused) 
418
{
419
        int error;
420

    
421
        switch(what) {
422
        case MOD_LOAD:
423
                error = sysctl_ctx_init(&machdep_powernow_ctx);
424
                if (error != 0)
425
                        break;
426
                error = powernow_init();
427
                break;
428
        case MOD_UNLOAD:
429
                if(k8pnow_current_state)
430
                        kfree(k8pnow_current_state, M_DEVBUF);
431
                error = sysctl_ctx_free(&machdep_powernow_ctx);
432
                break;
433
        default:
434
                error = EINVAL;
435
                break;
436
        }
437
        return(error);
438
}
439
static moduledata_t powernow_mod = {
440
        "powernow",
441
        powernow_modevh,
442
        NULL,
443
};
444

    
445
DECLARE_MODULE(powernow, powernow_mod, SI_BOOT2_KLD, SI_ORDER_ANY);