The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/powermng/powernow/powernow.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: f01ba4be7065bd0d74c6d870787188e8


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.