root/arch/i386/i386/powernow-k7.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. k7_powernow_setperf
  2. k7pnow_decode_pst
  3. k7pnow_states
  4. k7pnow_acpi_states
  5. k7pnow_acpi_pss_changed
  6. k7pnow_acpi_init
  7. k7_powernow_init

    1 /* $OpenBSD: powernow-k7.c,v 1.32 2007/07/27 03:03:37 gwk 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/types.h>
   32 #include <sys/param.h>
   33 #include <sys/systm.h>
   34 #include <sys/malloc.h>
   35 #include <sys/sysctl.h>
   36 
   37 #include <machine/cpu.h>
   38 #include <machine/cpufunc.h>
   39 #include <machine/bus.h>
   40 
   41 #include <dev/isa/isareg.h>
   42 #include <i386/isa/isa_machdep.h>
   43 
   44 #include "acpicpu.h"
   45 
   46 #if NACPICPU > 0
   47 #include <dev/acpi/acpidev.h>
   48 #include <dev/acpi/acpivar.h>
   49 #endif
   50 
   51 #define BIOS_START                      0xe0000
   52 #define BIOS_LEN                        0x20000
   53 #define BIOS_STEP                       16
   54 
   55 /*
   56  * MSRs and bits used by PowerNow! technology
   57  */
   58 #define MSR_AMDK7_FIDVID_CTL            0xc0010041
   59 #define MSR_AMDK7_FIDVID_STATUS         0xc0010042
   60 #define AMD_PN_FID_VID                  0x06
   61 #define AMD_ERRATA_A0_CPUSIG            0x660
   62 
   63 #define PN7_FLAG_ERRATA_A0              0x01
   64 #define PN7_FLAG_DESKTOP_VRM            0x02
   65 
   66 /* Bitfields used by K7 */
   67 #define PN7_PSB_VERSION                 0x12
   68 #define PN7_CTR_FID(x)                  ((x) & 0x1f)
   69 #define PN7_CTR_VID(x)                  (((x) & 0x1f) << 8)
   70 #define PN7_CTR_FIDC                    0x00010000
   71 #define PN7_CTR_VIDC                    0x00020000
   72 #define PN7_CTR_FIDCHRATIO              0x00100000
   73 #define PN7_CTR_SGTC(x)                 (((uint64_t)(x) & 0x000fffff) << 32)
   74 
   75 #define PN7_STA_CFID(x)                 ((x) & 0x1f)
   76 #define PN7_STA_SFID(x)                 (((x) >> 8) & 0x1f)
   77 #define PN7_STA_MFID(x)                 (((x) >> 16) & 0x1f)
   78 #define PN7_STA_CVID(x)                 (((x) >> 32) & 0x1f)
   79 #define PN7_STA_SVID(x)                 (((x) >> 40) & 0x1f)
   80 #define PN7_STA_MVID(x)                 (((x) >> 48) & 0x1f)
   81 
   82 /*
   83  * ACPI ctr_val status register to powernow k7 configuration
   84  */
   85 #define PN7_ACPI_CTRL_TO_FID(x)         ((x) & 0x1f)
   86 #define PN7_ACPI_CTRL_TO_VID(x)         (((x) >> 5) & 0x1f)
   87 #define PN7_ACPI_CTRL_TO_SGTC(x)        (((x) >> 10) & 0xffff)
   88 
   89 #define WRITE_FIDVID(fid, vid, ctrl)    \
   90         wrmsr(MSR_AMDK7_FIDVID_CTL,     \
   91             (((ctrl) << 32) | (1ULL << 16) | ((vid) << 8) | (fid)))
   92 
   93 /*
   94  * Divide each value by 10 to get the processor multiplier.
   95  * Taken from powernow-k7.c/Linux by Dave Jones
   96  */
   97 static int k7pnow_fid_to_mult[32] = {
   98         110, 115, 120, 125, 50, 55, 60, 65,
   99         70, 75, 80, 85, 90, 95, 100, 105,
  100         30, 190, 40, 200, 130, 135, 140, 210,
  101         150, 225, 160, 165, 170, 180, -1, -1
  102 };
  103 
  104 #define POWERNOW_MAX_STATES             16
  105 
  106 struct k7pnow_state {
  107         int freq;
  108         int fid;
  109         int vid;
  110 };
  111 
  112 struct k7pnow_cpu_state {
  113         unsigned int fsb;
  114         unsigned int sgtc;
  115         struct k7pnow_state state_table[POWERNOW_MAX_STATES];
  116         unsigned int n_states;
  117         int flags;
  118 };
  119 
  120 struct psb_s {
  121         char signature[10];     /* AMDK7PNOW! */
  122         uint8_t version;
  123         uint8_t flags;
  124         uint16_t ttime;         /* Min Settling time */
  125         uint8_t reserved;
  126         uint8_t n_pst;
  127 };
  128 
  129 struct pst_s {
  130         uint32_t signature;
  131         uint8_t fsb;            /* Front Side Bus frequency (MHz) */
  132         uint8_t fid;            /* Max Frequency code */
  133         uint8_t vid;            /* Max Voltage code */
  134         uint8_t n_states;       /* Number of states */
  135 };
  136 
  137 struct k7pnow_cpu_state *k7pnow_current_state;
  138 extern int setperf_prio;
  139 
  140 int k7pnow_decode_pst(struct k7pnow_cpu_state *, uint8_t *, int);
  141 int k7pnow_states(struct k7pnow_cpu_state *, uint32_t, unsigned int,
  142     unsigned int);
  143 
  144 #if NACPICPU > 0
  145 int k7pnow_acpi_init(struct k7pnow_cpu_state * cstate, uint64_t status);
  146 int k7pnow_acpi_states(struct k7pnow_cpu_state * cstate,
  147     struct acpicpu_pss *pss, int nstates, uint64_t status);
  148 void k7pnow_acpi_pss_changed(struct acpicpu_pss *pss, int npss);
  149 #endif
  150 
  151 void
  152 k7_powernow_setperf(int level)
  153 {
  154         unsigned int i;
  155         int cvid, cfid, vid = 0, fid = 0;
  156         uint64_t status, ctl;
  157         struct k7pnow_cpu_state * cstate;
  158 
  159         cstate = k7pnow_current_state;
  160 
  161         i = ((level * cstate->n_states) + 1) / 101;
  162         if (i >= cstate->n_states)
  163                 i = cstate->n_states - 1;
  164         fid = cstate->state_table[i].fid;
  165         vid = cstate->state_table[i].vid;
  166 
  167         if (fid == 0 || vid == 0)
  168                 return;
  169 
  170         status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
  171         cfid = PN7_STA_CFID(status);
  172         cvid = PN7_STA_CVID(status);
  173 
  174         /*
  175          * We're already at the requested level.
  176          */
  177         if (fid == cfid && vid == cvid)
  178                 return;
  179 
  180         ctl = rdmsr(MSR_AMDK7_FIDVID_CTL) & PN7_CTR_FIDCHRATIO;
  181 
  182         ctl |= PN7_CTR_FID(fid);
  183         ctl |= PN7_CTR_VID(vid);
  184         ctl |= PN7_CTR_SGTC(cstate->sgtc);
  185 
  186         if (cstate->flags & PN7_FLAG_ERRATA_A0)
  187                 disable_intr();
  188 
  189         if (k7pnow_fid_to_mult[fid] < k7pnow_fid_to_mult[cfid]) {
  190                 wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC);
  191                 if (vid != cvid)
  192                         wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC);
  193         } else {
  194                 wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC);
  195                 if (fid != cfid)
  196                         wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC);
  197         }
  198 
  199         if (cstate->flags & PN7_FLAG_ERRATA_A0)
  200                 enable_intr();
  201 
  202         status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
  203         cfid = PN7_STA_CFID(status);
  204         cvid = PN7_STA_CVID(status);
  205         if (cfid == fid || cvid == vid)
  206                 cpuspeed = cstate->state_table[i].freq;
  207 }
  208 
  209 /*
  210  * Given a set of pair of fid/vid, and number of performance states,
  211  * compute state_table via an insertion sort.
  212  */
  213 int
  214 k7pnow_decode_pst(struct k7pnow_cpu_state * cstate, uint8_t *p, int npst)
  215 {
  216         int i, j, n;
  217         struct k7pnow_state state;
  218 
  219         for (n = 0, i = 0; i < npst; ++i) {
  220                 state.fid = *p++;
  221                 state.vid = *p++;
  222                 state.freq = k7pnow_fid_to_mult[state.fid]/10 * cstate->fsb;
  223                 if ((cstate->flags & PN7_FLAG_ERRATA_A0) &&
  224                     (k7pnow_fid_to_mult[state.fid] % 10) == 5)
  225                         continue;
  226 
  227                 j = n;
  228                 while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
  229                         memcpy(&cstate->state_table[j],
  230                             &cstate->state_table[j - 1],
  231                             sizeof(struct k7pnow_state));
  232                         --j;
  233                 }
  234                 memcpy(&cstate->state_table[j], &state,
  235                     sizeof(struct k7pnow_state));
  236                 ++n;
  237         }
  238         /*
  239          * Fix powernow_max_states, if errata_a0 give us less states
  240          * than expected.
  241          */
  242         cstate->n_states = n;
  243         return 1;
  244 }
  245 
  246 int
  247 k7pnow_states(struct k7pnow_cpu_state *cstate, uint32_t cpusig,
  248     unsigned int fid, unsigned int vid)
  249 {
  250         int maxpst;
  251         struct psb_s *psb;
  252         struct pst_s *pst;
  253         uint8_t *p;
  254 
  255         /*
  256          * Look in the 0xe0000 - 0x100000 physical address
  257          * range for the pst tables; 16 byte blocks
  258          */
  259         for (p = (u_int8_t *)ISA_HOLE_VADDR(BIOS_START);
  260             p < (u_int8_t *)ISA_HOLE_VADDR(BIOS_START + BIOS_LEN); p+=
  261             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
  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                 state.freq = pss[n].pss_core_freq;
  315                 j = n;
  316                 while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
  317                         memcpy(&cstate->state_table[j],
  318                             &cstate->state_table[j - 1],
  319                         sizeof(struct k7pnow_state));
  320                         --j;
  321                 }
  322                 memcpy(&cstate->state_table[j], &state,
  323                     sizeof(struct k7pnow_state));
  324         }
  325         return k;
  326 }
  327 
  328 void
  329 k7pnow_acpi_pss_changed(struct acpicpu_pss *pss, int npss)
  330 {
  331         int curs;
  332         struct k7pnow_cpu_state *cstate;
  333         uint32_t ctrl;
  334         uint64_t status;
  335 
  336         status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
  337         cstate = k7pnow_current_state;
  338 
  339         curs = k7pnow_acpi_states(cstate, pss, npss, status);
  340         ctrl = pss[curs].pss_ctrl;
  341         cstate->sgtc = PN7_ACPI_CTRL_TO_SGTC(ctrl);
  342         cstate->n_states = npss;
  343 }
  344 
  345 int
  346 k7pnow_acpi_init(struct k7pnow_cpu_state *cstate, uint64_t status)
  347 {
  348         int curs;
  349         uint32_t ctrl;
  350         struct acpicpu_pss *pss;
  351         int mfid;
  352 
  353         cstate->n_states = acpicpu_fetch_pss(&pss);
  354         if (cstate->n_states == 0)
  355                 return 0;
  356 
  357         /* 
  358          * XXX: Some BIOS supplied _PSS implementations have the wrong
  359          * maximum frequency, if we encounter one of these punt and 
  360          * hope the legacy tables have correct values.
  361          */
  362         mfid = PN7_STA_MFID(status);
  363         if (mfid != cstate->state_table[cstate->n_states - 1].fid) {
  364                 return 0;
  365         }
  366         curs = k7pnow_acpi_states(cstate, pss, cstate->n_states, status);
  367 
  368         acpicpu_set_notify(k7pnow_acpi_pss_changed);
  369         ctrl = pss[curs].pss_ctrl;
  370         cstate->sgtc = PN7_ACPI_CTRL_TO_SGTC(ctrl);
  371 
  372         return 1;
  373 }
  374 
  375 #endif /* NACPICPU */
  376 
  377 void
  378 k7_powernow_init(void)
  379 {
  380         u_int regs[4];
  381         uint64_t status;
  382         u_int maxfid, startvid, currentfid;
  383         struct k7pnow_cpu_state *cstate;
  384         struct k7pnow_state *state;
  385         struct cpu_info *ci;
  386         char *techname = NULL;
  387         int i;
  388 
  389         if (setperf_prio > 1)
  390                 return;
  391 
  392         ci = curcpu();
  393 
  394         cpuid(0x80000000, regs);
  395         if (regs[0] < 0x80000007)
  396                 return;
  397 
  398         cpuid(0x80000007, regs);
  399         if (!(regs[3] & AMD_PN_FID_VID))
  400                 return;
  401 
  402         /* Extended CPUID signature value */
  403         cpuid(0x80000001, regs);
  404 
  405         cstate = malloc(sizeof(struct k7pnow_cpu_state), M_DEVBUF, M_NOWAIT);
  406         if (!cstate)
  407                 return;
  408 
  409         cstate->flags = cstate->n_states = 0;
  410         if (ci->ci_signature == AMD_ERRATA_A0_CPUSIG)
  411                 cstate->flags |= PN7_FLAG_ERRATA_A0;
  412 
  413         status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
  414         maxfid = PN7_STA_MFID(status);
  415         startvid = PN7_STA_SVID(status);
  416         currentfid = PN7_STA_CFID(status);
  417 
  418         cstate->fsb = cpuspeed / (k7pnow_fid_to_mult[currentfid]/10);
  419 
  420 #if NACPICPU > 0
  421         /* If we have it try ACPI */
  422         if (!k7pnow_acpi_init(cstate, status))
  423 #endif
  424         {
  425                 /* if the base CPUID signature fails to match try, the extended one */
  426                 if (!k7pnow_states(cstate, ci->ci_signature, maxfid, startvid))
  427                         k7pnow_states(cstate, regs[0], maxfid, startvid);
  428         }
  429 
  430         if (cstate->n_states) {
  431                 if (cstate->flags & PN7_FLAG_DESKTOP_VRM)
  432                         techname = "Cool'n'Quiet K7";
  433                 else
  434                         techname = "PowerNow! K7";
  435                 printf("%s: %s %d MHz: speeds:",
  436                     ci->ci_dev.dv_xname, techname, cpuspeed);
  437                 for (i = cstate->n_states; i > 0; i--) {
  438                         state = &cstate->state_table[i-1];
  439                         printf(" %d", state->freq);
  440                 }
  441                 printf(" MHz\n");
  442 
  443                 k7pnow_current_state = cstate;
  444                 cpu_setperf = k7_powernow_setperf;
  445                 setperf_prio = 1;
  446                 return;
  447         }
  448         free(cstate, M_DEVBUF);
  449 }

/* [<][>][^][v][top][bottom][index][help] */