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/i386/cpufreq/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-2005 Bruno Ducrot
    3  * Copyright (c) 2004 FUKUDA Nobuhiko <nfukuda@spa.is.uec.ac.jp>
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   24  */
   25 
   26 /*
   27  * Many thanks to Nate Lawson for his helpful comments on this driver and
   28  * to Jung-uk Kim for testing.
   29  */
   30 
   31 #include <sys/cdefs.h>
   32 __FBSDID("$FreeBSD: releng/6.0/sys/i386/cpufreq/powernow.c 144380 2005-03-31 06:11:04Z njl $");
   33 
   34 #include <sys/param.h>
   35 #include <sys/bus.h>
   36 #include <sys/cpu.h>
   37 #include <sys/kernel.h>
   38 #include <sys/malloc.h>
   39 #include <sys/module.h>
   40 #include <sys/pcpu.h>
   41 #include <sys/systm.h>
   42 
   43 #include <machine/pc/bios.h>
   44 #include <machine/md_var.h>
   45 #include <machine/specialreg.h>
   46 #include <machine/cputypes.h>
   47 #include <machine/clock.h>
   48 #include <machine/vmparam.h>
   49 #include <sys/rman.h>
   50 
   51 #include <vm/vm.h>
   52 #include <vm/pmap.h>
   53 
   54 #include "cpufreq_if.h"
   55 
   56 #define PN7_TYPE        0
   57 #define PN8_TYPE        1
   58 
   59 /* Legacy configuration via BIOS table PSB. */
   60 #define PSB_START       0
   61 #define PSB_STEP        0x10
   62 #define PSB_SIG         "AMDK7PNOW!"
   63 #define PSB_LEN         10
   64 #define PSB_OFF         0
   65 
   66 struct psb_header {
   67         char             signature[10];
   68         uint8_t          version;
   69         uint8_t          flags;
   70         uint16_t         settlingtime;
   71         uint8_t          res1;
   72         uint8_t          numpst;
   73 } __packed;
   74 
   75 struct pst_header {
   76         uint32_t         cpuid;
   77         uint8_t          fsb;
   78         uint8_t          maxfid;
   79         uint8_t          startvid;
   80         uint8_t          numpstates;
   81 } __packed;
   82 
   83 /*
   84  * MSRs and bits used by Powernow technology
   85  */
   86 #define MSR_AMDK7_FIDVID_CTL            0xc0010041
   87 #define MSR_AMDK7_FIDVID_STATUS         0xc0010042
   88 
   89 /* Bitfields used by K7 */
   90 
   91 #define PN7_CTR_FID(x)                  ((x) & 0x1f)
   92 #define PN7_CTR_VID(x)                  (((x) & 0x1f) << 8)
   93 #define PN7_CTR_FIDC                    0x00010000
   94 #define PN7_CTR_VIDC                    0x00020000
   95 #define PN7_CTR_FIDCHRATIO              0x00100000
   96 #define PN7_CTR_SGTC(x)                 (((uint64_t)(x) & 0x000fffff) << 32)
   97 
   98 #define PN7_STA_CFID(x)                 ((x) & 0x1f)
   99 #define PN7_STA_SFID(x)                 (((x) >> 8) & 0x1f)
  100 #define PN7_STA_MFID(x)                 (((x) >> 16) & 0x1f)
  101 #define PN7_STA_CVID(x)                 (((x) >> 32) & 0x1f)
  102 #define PN7_STA_SVID(x)                 (((x) >> 40) & 0x1f)
  103 #define PN7_STA_MVID(x)                 (((x) >> 48) & 0x1f)
  104 
  105 /* ACPI ctr_val status register to powernow k7 configuration */
  106 #define ACPI_PN7_CTRL_TO_FID(x)         ((x) & 0x1f)
  107 #define ACPI_PN7_CTRL_TO_VID(x)         (((x) >> 5) & 0x1f)
  108 #define ACPI_PN7_CTRL_TO_SGTC(x)        (((x) >> 10) & 0xffff)
  109 
  110 /* Bitfields used by K8 */
  111 
  112 #define PN8_CTR_FID(x)                  ((x) & 0x3f)
  113 #define PN8_CTR_VID(x)                  (((x) & 0x1f) << 8)
  114 #define PN8_CTR_PENDING(x)              (((x) & 1) << 32)
  115 
  116 #define PN8_STA_CFID(x)                 ((x) & 0x3f)
  117 #define PN8_STA_SFID(x)                 (((x) >> 8) & 0x3f)
  118 #define PN8_STA_MFID(x)                 (((x) >> 16) & 0x3f)
  119 #define PN8_STA_PENDING(x)              (((x) >> 31) & 0x01)
  120 #define PN8_STA_CVID(x)                 (((x) >> 32) & 0x1f)
  121 #define PN8_STA_SVID(x)                 (((x) >> 40) & 0x1f)
  122 #define PN8_STA_MVID(x)                 (((x) >> 48) & 0x1f)
  123 
  124 /* Reserved1 to powernow k8 configuration */
  125 #define PN8_PSB_TO_RVO(x)               ((x) & 0x03)
  126 #define PN8_PSB_TO_IRT(x)               (((x) >> 2) & 0x03)
  127 #define PN8_PSB_TO_MVS(x)               (((x) >> 4) & 0x03)
  128 #define PN8_PSB_TO_BATT(x)              (((x) >> 6) & 0x03)
  129 
  130 /* ACPI ctr_val status register to powernow k8 configuration */
  131 #define ACPI_PN8_CTRL_TO_FID(x)         ((x) & 0x3f)
  132 #define ACPI_PN8_CTRL_TO_VID(x)         (((x) >> 6) & 0x1f)
  133 #define ACPI_PN8_CTRL_TO_VST(x)         (((x) >> 11) & 0x1f)
  134 #define ACPI_PN8_CTRL_TO_MVS(x)         (((x) >> 18) & 0x03)
  135 #define ACPI_PN8_CTRL_TO_PLL(x)         (((x) >> 20) & 0x7f)
  136 #define ACPI_PN8_CTRL_TO_RVO(x)         (((x) >> 28) & 0x03)
  137 #define ACPI_PN8_CTRL_TO_IRT(x)         (((x) >> 30) & 0x03)
  138 
  139 
  140 #define WRITE_FIDVID(fid, vid, ctrl)    \
  141         wrmsr(MSR_AMDK7_FIDVID_CTL,     \
  142             (((ctrl) << 32) | (1ULL << 16) | ((vid) << 8) | (fid)))
  143 
  144 #define READ_PENDING_WAIT(status)       \
  145         do {                    \
  146                 (status) = rdmsr(MSR_AMDK7_FIDVID_STATUS);      \
  147         } while (PN8_STA_PENDING(status))
  148 
  149 #define COUNT_OFF_IRT(irt)      DELAY(10 * (1 << (irt)))
  150 #define COUNT_OFF_VST(vst)      DELAY(20 * (vst))
  151 
  152 #define FID_TO_VCO_FID(fid)     \
  153         (((fid) < 8) ? (8 + ((fid) << 1)) : (fid))
  154 
  155 /*
  156  * Divide each value by 10 to get the processor multiplier.
  157  * Some of those tables are the same as the Linux powernow-k7
  158  * implementation by Dave Jones.
  159  */
  160 static int pn7_fid_to_mult[32] = {
  161         110, 115, 120, 125, 50, 55, 60, 65,
  162         70, 75, 80, 85, 90, 95, 100, 105,
  163         30, 190, 40, 200, 130, 135, 140, 210,
  164         150, 225, 160, 165, 170, 180, 0, 0,
  165 };
  166 
  167 
  168 static int pn8_fid_to_mult[32] = {
  169         40, 50, 60, 70, 80, 90, 100, 110,
  170         120, 130, 140, 150, 160, 170, 180, 190,
  171         220, 230, 240, 250, 260, 270, 280, 290,
  172         300, 310, 320, 330, 340, 350,
  173 };
  174 
  175 /*
  176  * Units are in mV.
  177  */
  178 /* Mobile VRM (K7) */
  179 static int pn7_mobile_vid_to_volts[] = {
  180         2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650,
  181         1600, 1550, 1500, 1450, 1400, 1350, 1300, 0,
  182         1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100,
  183         1075, 1050, 1025, 1000, 975, 950, 925, 0,
  184 };
  185 /* Desktop VRM (K7) */
  186 static int pn7_desktop_vid_to_volts[] = {
  187         2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650,
  188         1600, 1550, 1500, 1450, 1400, 1350, 1300, 0,
  189         1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100,
  190         1075, 1050, 1025, 1000, 975, 950, 925, 0,
  191 };
  192 /* Desktop and Mobile VRM (K8) */
  193 static int pn8_vid_to_volts[] = {
  194         1550, 1525, 1500, 1475, 1450, 1425, 1400, 1375,
  195         1350, 1325, 1300, 1275, 1250, 1225, 1200, 1175,
  196         1150, 1125, 1100, 1075, 1050, 1025, 1000, 975,
  197         950, 925, 900, 875, 850, 825, 800, 0,
  198 };
  199 
  200 #define POWERNOW_MAX_STATES             16
  201 
  202 struct powernow_state {
  203         int freq;
  204         int power;
  205         int fid;
  206         int vid;
  207 };
  208 
  209 struct pn_softc {
  210         device_t                 dev;
  211         int                      pn_type;
  212         struct powernow_state    powernow_states[POWERNOW_MAX_STATES];
  213         u_int                    fsb;
  214         u_int                    sgtc;
  215         u_int                    vst;
  216         u_int                    mvs;
  217         u_int                    pll;
  218         u_int                    rvo;
  219         u_int                    irt;
  220         int                      low;
  221         int                      powernow_max_states;
  222         u_int                    powernow_state;
  223         int                      errata_a0;
  224         int                     *vid_to_volts;
  225 };
  226 
  227 /*
  228  * Offsets in struct cf_setting array for private values given by
  229  * acpi_perf driver.
  230  */
  231 #define PX_SPEC_CONTROL         0
  232 #define PX_SPEC_STATUS          1
  233 
  234 static void     pn_identify(driver_t *driver, device_t parent);
  235 static int      pn_probe(device_t dev);
  236 static int      pn_attach(device_t dev);
  237 static int      pn_detach(device_t dev);
  238 static int      pn_set(device_t dev, const struct cf_setting *cf);
  239 static int      pn_get(device_t dev, struct cf_setting *cf);
  240 static int      pn_settings(device_t dev, struct cf_setting *sets,
  241                     int *count);
  242 static int      pn_type(device_t dev, int *type);
  243 
  244 static device_method_t pn_methods[] = {
  245         /* Device interface */
  246         DEVMETHOD(device_identify, pn_identify),
  247         DEVMETHOD(device_probe, pn_probe),
  248         DEVMETHOD(device_attach, pn_attach),
  249         DEVMETHOD(device_detach, pn_detach),
  250 
  251         /* cpufreq interface */
  252         DEVMETHOD(cpufreq_drv_set, pn_set),
  253         DEVMETHOD(cpufreq_drv_get, pn_get),
  254         DEVMETHOD(cpufreq_drv_settings, pn_settings),
  255         DEVMETHOD(cpufreq_drv_type, pn_type),
  256 
  257         {0, 0}
  258 };
  259 
  260 static devclass_t pn_devclass;
  261 static driver_t pn_driver = {
  262         "powernow",
  263         pn_methods,
  264         sizeof(struct pn_softc),
  265 };
  266 
  267 DRIVER_MODULE(powernow, cpu, pn_driver, pn_devclass, 0, 0);
  268 
  269 static int
  270 pn7_setfidvid(struct pn_softc *sc, int fid, int vid)
  271 {
  272         int cfid, cvid;
  273         uint64_t status, ctl;
  274 
  275         status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
  276         cfid = PN7_STA_CFID(status);
  277         cvid = PN7_STA_CVID(status);
  278 
  279         /* We're already at the requested level. */
  280         if (fid == cfid && vid == cvid)
  281                 return (0);
  282 
  283         ctl = rdmsr(MSR_AMDK7_FIDVID_CTL) & PN7_CTR_FIDCHRATIO;
  284 
  285         ctl |= PN7_CTR_FID(fid);
  286         ctl |= PN7_CTR_VID(vid);
  287         ctl |= PN7_CTR_SGTC(sc->sgtc);
  288 
  289         if (sc->errata_a0)
  290                 disable_intr();
  291 
  292         if (pn7_fid_to_mult[fid] < pn7_fid_to_mult[cfid]) {
  293                 wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC);
  294                 if (vid != cvid)
  295                         wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC);
  296         } else {
  297                 wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC);
  298                 if (fid != cfid)
  299                         wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC);
  300         }
  301 
  302         if (sc->errata_a0)
  303                 enable_intr();
  304 
  305         return (0);
  306 }
  307 
  308 static int
  309 pn8_setfidvid(struct pn_softc *sc, int fid, int vid)
  310 {
  311         uint64_t status;
  312         int cfid, cvid;
  313         int rvo;
  314         u_int val;
  315 
  316         READ_PENDING_WAIT(status);
  317         cfid = PN8_STA_CFID(status);
  318         cvid = PN8_STA_CVID(status);
  319 
  320         if (fid == cfid && vid == cvid)
  321                 return (0);
  322 
  323         /*
  324          * Phase 1: Raise core voltage to requested VID if frequency is
  325          * going up.
  326          */
  327         while (cvid > vid) {
  328                 val = cvid - (1 << sc->mvs);
  329                 WRITE_FIDVID(cfid, (val > 0) ? val : 0, 1ULL);
  330                 READ_PENDING_WAIT(status);
  331                 cvid = PN8_STA_CVID(status);
  332                 COUNT_OFF_VST(sc->vst);
  333         }
  334 
  335         /* ... then raise to voltage + RVO (if required) */
  336         for (rvo = sc->rvo; rvo > 0 && cvid > 0; --rvo) {
  337                 /* XXX It's not clear from spec if we have to do that
  338                  * in 0.25 step or in MVS.  Therefore do it as it's done
  339                  * under Linux */
  340                 WRITE_FIDVID(cfid, cvid - 1, 1ULL);
  341                 READ_PENDING_WAIT(status);
  342                 cvid = PN8_STA_CVID(status);
  343                 COUNT_OFF_VST(sc->vst);
  344         }
  345 
  346         /* Phase 2: change to requested core frequency */
  347         if (cfid != fid) {
  348                 u_int vco_fid, vco_cfid;
  349 
  350                 vco_fid = FID_TO_VCO_FID(fid);
  351                 vco_cfid = FID_TO_VCO_FID(cfid);
  352 
  353                 while (abs(vco_fid - vco_cfid) > 2) {
  354                         if (fid > cfid) {
  355                                 if (cfid > 6)
  356                                         val = cfid + 2;
  357                                 else
  358                                         val = FID_TO_VCO_FID(cfid) + 2;
  359                         } else
  360                                 val = cfid - 2;
  361                         WRITE_FIDVID(val, cvid, sc->pll * (uint64_t) sc->fsb);
  362                         READ_PENDING_WAIT(status);
  363                         cfid = PN8_STA_CFID(status);
  364                         COUNT_OFF_IRT(sc->irt);
  365 
  366                         vco_cfid = FID_TO_VCO_FID(cfid);
  367                 }
  368 
  369                 WRITE_FIDVID(fid, cvid, sc->pll * (uint64_t) sc->fsb);
  370                 READ_PENDING_WAIT(status);
  371                 cfid = PN8_STA_CFID(status);
  372                 COUNT_OFF_IRT(sc->irt);
  373         }
  374 
  375         /* Phase 3: change to requested voltage */
  376         if (cvid != vid) {
  377                 WRITE_FIDVID(cfid, vid, 1ULL);
  378                 READ_PENDING_WAIT(status);
  379                 cvid = PN8_STA_CVID(status);
  380                 COUNT_OFF_VST(sc->vst);
  381         }
  382 
  383         /* Check if transition failed. */
  384         if (cfid != fid || cvid != vid)
  385                 return (ENXIO);
  386 
  387         return (0);
  388 }
  389 
  390 static int
  391 pn_set(device_t dev, const struct cf_setting *cf)
  392 {
  393         struct pn_softc *sc;
  394         int fid, vid;
  395         int i;
  396         int rv;
  397 
  398         if (cf == NULL)
  399                 return (EINVAL);
  400         sc = device_get_softc(dev);
  401 
  402         for (i = 0; i < sc->powernow_max_states; ++i)
  403                 if (CPUFREQ_CMP(sc->powernow_states[i].freq / 1000, cf->freq))
  404                         break;
  405 
  406         fid = sc->powernow_states[i].fid;
  407         vid = sc->powernow_states[i].vid;
  408 
  409         rv = ENODEV;
  410 
  411         switch (sc->pn_type) {
  412         case PN7_TYPE:
  413                 rv = pn7_setfidvid(sc, fid, vid);
  414                 break;
  415         case PN8_TYPE:
  416                 rv = pn8_setfidvid(sc, fid, vid);
  417                 break;
  418         }
  419 
  420         return (rv);
  421 }
  422 
  423 static int
  424 pn_get(device_t dev, struct cf_setting *cf)
  425 {
  426         struct pn_softc *sc;
  427         u_int cfid = 0, cvid = 0;
  428         int i;
  429         uint64_t status;
  430 
  431         if (cf == NULL)
  432                 return (EINVAL);
  433         sc = device_get_softc(dev);
  434 
  435         status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
  436 
  437         switch (sc->pn_type) {
  438         case PN7_TYPE:
  439                 cfid = PN7_STA_CFID(status);
  440                 cvid = PN7_STA_CVID(status);
  441                 break;
  442         case PN8_TYPE:
  443                 cfid = PN8_STA_CFID(status);
  444                 cvid = PN8_STA_CVID(status);
  445                 break;
  446         }
  447         for (i = 0; i < sc->powernow_max_states; ++i)
  448                 if (cfid == sc->powernow_states[i].fid &&
  449                     cvid == sc->powernow_states[i].vid)
  450                         break;
  451 
  452         if (i < sc->powernow_max_states) {
  453                 cf->freq = sc->powernow_states[i].freq / 1000;
  454                 cf->power = sc->powernow_states[i].power;
  455                 cf->lat = 200;
  456                 cf->volts = sc->vid_to_volts[cvid];
  457                 cf->dev = dev;
  458         } else {
  459                 memset(cf, CPUFREQ_VAL_UNKNOWN, sizeof(*cf));
  460                 cf->dev = NULL;
  461         }
  462 
  463         return (0);
  464 }
  465 
  466 static int
  467 pn_settings(device_t dev, struct cf_setting *sets, int *count)
  468 {
  469         struct pn_softc *sc;
  470         int i;
  471 
  472         if (sets == NULL|| count == NULL)
  473                 return (EINVAL);
  474         sc = device_get_softc(dev);
  475         if (*count < sc->powernow_max_states)
  476                 return (E2BIG);
  477         for (i = 0; i < sc->powernow_max_states; ++i) {
  478                 sets[i].freq = sc->powernow_states[i].freq / 1000;
  479                 sets[i].power = sc->powernow_states[i].power;
  480                 sets[i].lat = 200;
  481                 sets[i].volts = sc->vid_to_volts[sc->powernow_states[i].vid];
  482                 sets[i].dev = dev;
  483         }
  484         *count = sc->powernow_max_states;
  485 
  486         return (0);
  487 }
  488 
  489 static int
  490 pn_type(device_t dev, int *type)
  491 {
  492         if (type == NULL)
  493                 return (EINVAL);
  494 
  495         *type = CPUFREQ_TYPE_ABSOLUTE;
  496 
  497         return (0);
  498 }
  499 
  500 /*
  501  * Given a set of pair of fid/vid, and number of performance states,
  502  * compute powernow_states via an insertion sort.
  503  */
  504 static int
  505 decode_pst(struct pn_softc *sc, uint8_t *p, int npstates)
  506 {
  507         int i, j, n;
  508         struct powernow_state state;
  509 
  510         for (i = 0; i < POWERNOW_MAX_STATES; ++i)
  511                 sc->powernow_states[i].freq = CPUFREQ_VAL_UNKNOWN;
  512 
  513         for (n = 0, i = 0; i < npstates; ++i) {
  514                 state.fid = *p++;
  515                 state.vid = *p++;
  516                 state.power = CPUFREQ_VAL_UNKNOWN;
  517 
  518                 switch (sc->pn_type) {
  519                 case PN7_TYPE:
  520                         state.freq = 100 * pn7_fid_to_mult[state.fid] * sc->fsb;
  521                         if (sc->errata_a0 &&
  522                             (pn7_fid_to_mult[state.fid] % 10) == 5)
  523                                 continue;
  524                         break;
  525                 case PN8_TYPE:
  526                         state.freq = 100 * pn8_fid_to_mult[state.fid >> 1] *
  527                             sc->fsb;
  528                         break;
  529                 }
  530 
  531                 j = n;
  532                 while (j > 0 && sc->powernow_states[j - 1].freq < state.freq) {
  533                         memcpy(&sc->powernow_states[j],
  534                             &sc->powernow_states[j - 1],
  535                             sizeof(struct powernow_state));
  536                         --j;
  537                 }
  538                 memcpy(&sc->powernow_states[j], &state,
  539                     sizeof(struct powernow_state));
  540                 ++n;
  541         }
  542 
  543         /*
  544          * Fix powernow_max_states, if errata_a0 give us less states
  545          * than expected.
  546          */
  547         sc->powernow_max_states = n;
  548 
  549         if (bootverbose)
  550                 for (i = 0; i < sc->powernow_max_states; ++i) {
  551                         int fid = sc->powernow_states[i].fid;
  552                         int vid = sc->powernow_states[i].vid;
  553 
  554                         printf("powernow: %2i %8dkHz FID %02x VID %02x\n",
  555                             i,
  556                             sc->powernow_states[i].freq,
  557                             fid,
  558                             vid);
  559                 }
  560 
  561         return (0);
  562 }
  563 
  564 static int
  565 cpuid_is_k7(u_int cpuid)
  566 {
  567 
  568         switch (cpuid) {
  569         case 0x760:
  570         case 0x761:
  571         case 0x762:
  572         case 0x770:
  573         case 0x771:
  574         case 0x780:
  575         case 0x781:
  576         case 0x7a0:
  577                 return (TRUE);
  578         }
  579         return (FALSE);
  580 }
  581 
  582 static int
  583 pn_decode_pst(device_t dev)
  584 {
  585         int maxpst;
  586         struct pn_softc *sc;
  587         u_int cpuid, maxfid, startvid;
  588         u_long sig;
  589         struct psb_header *psb;
  590         uint8_t *p;
  591         u_int regs[4];
  592         uint64_t status;
  593 
  594         sc = device_get_softc(dev);
  595 
  596         do_cpuid(0x80000001, regs);
  597         cpuid = regs[0];
  598 
  599         if ((cpuid & 0xfff) == 0x760)
  600                 sc->errata_a0 = TRUE;
  601 
  602         status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
  603 
  604         switch (sc->pn_type) {
  605         case PN7_TYPE:
  606                 maxfid = PN7_STA_MFID(status);
  607                 startvid = PN7_STA_SVID(status);
  608                 break;
  609         case PN8_TYPE:
  610                 maxfid = PN8_STA_MFID(status);
  611                 /*
  612                  * we should actually use a variable named 'maxvid' if K8,
  613                  * but why introducing a new variable for that?
  614                  */
  615                 startvid = PN8_STA_MVID(status);
  616                 break;
  617         default:
  618                 return (ENODEV);
  619         }
  620 
  621         if (bootverbose) {
  622                 device_printf(dev, "STATUS: 0x%jx\n", status);
  623                 device_printf(dev, "STATUS: maxfid: 0x%02x\n", maxfid);
  624                 device_printf(dev, "STATUS: %s: 0x%02x\n",
  625                     sc->pn_type == PN7_TYPE ? "startvid" : "maxvid",
  626                     startvid);
  627         }
  628 
  629         sig = bios_sigsearch(PSB_START, PSB_SIG, PSB_LEN, PSB_STEP, PSB_OFF);
  630         if (sig) {
  631                 struct pst_header *pst;
  632 
  633                 psb = (struct psb_header*)(uintptr_t)BIOS_PADDRTOVADDR(sig);
  634 
  635                 switch (psb->version) {
  636                 default:
  637                         return (ENODEV);
  638                 case 0x14:
  639                         /*
  640                          * We can't be picky about numpst since at least
  641                          * some systems have a value of 1 and some have 2.
  642                          * We trust that cpuid_is_k7() will be better at
  643                          * catching that we're on a K8 anyway.
  644                          */
  645                         if (sc->pn_type != PN8_TYPE)
  646                                 return (EINVAL);
  647                         sc->vst = psb->settlingtime;
  648                         sc->rvo = PN8_PSB_TO_RVO(psb->res1),
  649                         sc->irt = PN8_PSB_TO_IRT(psb->res1),
  650                         sc->mvs = PN8_PSB_TO_MVS(psb->res1),
  651                         sc->low = PN8_PSB_TO_BATT(psb->res1);
  652                         if (bootverbose) {
  653                                 device_printf(dev, "PSB: VST: %d\n",
  654                                     psb->settlingtime);
  655                                 device_printf(dev, "PSB: RVO %x IRT %d "
  656                                     "MVS %d BATT %d\n",
  657                                     sc->rvo,
  658                                     sc->irt,
  659                                     sc->mvs,
  660                                     sc->low);
  661                         }
  662                         break;
  663                 case 0x12:
  664                         if (sc->pn_type != PN7_TYPE)
  665                                 return (EINVAL);
  666                         sc->sgtc = psb->settlingtime * sc->fsb;
  667                         if (sc->sgtc < 100 * sc->fsb)
  668                                 sc->sgtc = 100 * sc->fsb;
  669                         break;
  670                 }
  671 
  672                 p = ((uint8_t *) psb) + sizeof(struct psb_header);
  673                 pst = (struct pst_header*) p;
  674 
  675                 maxpst = 200;
  676 
  677                 do {
  678                         struct pst_header *pst = (struct pst_header*) p;
  679 
  680                         if (cpuid == pst->cpuid &&
  681                             maxfid == pst->maxfid &&
  682                             startvid == pst->startvid) {
  683                                 sc->powernow_max_states = pst->numpstates;
  684                                 switch (sc->pn_type) {
  685                                 case PN7_TYPE:
  686                                         if (abs(sc->fsb - pst->fsb) > 5)
  687                                                 continue;
  688                                         break;
  689                                 case PN8_TYPE:
  690                                         break;
  691                                 }
  692                                 return (decode_pst(sc,
  693                                     p + sizeof(struct pst_header),
  694                                     sc->powernow_max_states));
  695                         }
  696 
  697                         p += sizeof(struct pst_header) + (2 * pst->numpstates);
  698                 } while (cpuid_is_k7(pst->cpuid) && maxpst--);
  699 
  700                 device_printf(dev, "no match for extended cpuid %.3x\n", cpuid);
  701         }
  702 
  703         return (ENODEV);
  704 }
  705 
  706 /*
  707  * TODO: this should be done in sys/ARCH/ARCH/identcpu.c
  708  */
  709 static int
  710 cpu_is_powernow_capable(void)
  711 {
  712         u_int regs[4];
  713 
  714         if (strcmp(cpu_vendor, "AuthenticAMD") != 0 ||
  715             cpu_exthigh < 0x80000007)
  716                 return (FALSE);
  717 
  718         do_cpuid(0x80000007, regs);
  719         return (regs[3] & 0x6);
  720 }
  721 
  722 static int
  723 pn_decode_acpi(device_t dev, device_t perf_dev)
  724 {
  725         int i, j, n;
  726         uint64_t status;
  727         uint32_t ctrl;
  728         u_int cpuid;
  729         u_int regs[4];
  730         struct pn_softc *sc;
  731         struct powernow_state state;
  732         struct cf_setting sets[POWERNOW_MAX_STATES];
  733         int count = POWERNOW_MAX_STATES;
  734         int type;
  735         int rv;
  736 
  737         if (perf_dev == NULL)
  738                 return (ENXIO);
  739 
  740         rv = CPUFREQ_DRV_SETTINGS(perf_dev, sets, &count);
  741         if (rv)
  742                 return (ENXIO);
  743         rv = CPUFREQ_DRV_TYPE(perf_dev, &type);
  744         if (rv || (type & CPUFREQ_FLAG_INFO_ONLY) == 0)
  745                 return (ENXIO);
  746 
  747         sc = device_get_softc(dev);
  748 
  749         do_cpuid(0x80000001, regs);
  750         cpuid = regs[0];
  751         if ((cpuid & 0xfff) == 0x760)
  752                 sc->errata_a0 = TRUE;
  753 
  754         ctrl = 0;
  755         sc->sgtc = 0;
  756         for (n = 0, i = 0; i < count; ++i) {
  757                 ctrl = sets[i].spec[PX_SPEC_CONTROL];
  758                 switch (sc->pn_type) {
  759                 case PN7_TYPE:
  760                         state.fid = ACPI_PN7_CTRL_TO_FID(ctrl);
  761                         state.vid = ACPI_PN7_CTRL_TO_VID(ctrl);
  762                         if (sc->errata_a0 &&
  763                             (pn7_fid_to_mult[state.fid] % 10) == 5)
  764                                 continue;
  765                         state.freq = 100 * pn7_fid_to_mult[state.fid] * sc->fsb;
  766                         break;
  767                 case PN8_TYPE:
  768                         state.fid = ACPI_PN8_CTRL_TO_FID(ctrl);
  769                         state.vid = ACPI_PN8_CTRL_TO_VID(ctrl);
  770                         state.freq = 100 * pn8_fid_to_mult[state.fid >> 1] *
  771                             sc->fsb;
  772                         break;
  773                 }
  774 
  775                 state.power = sets[i].power;
  776 
  777                 j = n;
  778                 while (j > 0 && sc->powernow_states[j - 1].freq < state.freq) {
  779                         memcpy(&sc->powernow_states[j],
  780                             &sc->powernow_states[j - 1],
  781                             sizeof(struct powernow_state));
  782                         --j;
  783                 }
  784                 memcpy(&sc->powernow_states[j], &state,
  785                     sizeof(struct powernow_state));
  786                 ++n;
  787         }
  788 
  789         sc->powernow_max_states = n;
  790         state = sc->powernow_states[0];
  791         status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
  792 
  793         switch (sc->pn_type) {
  794         case PN7_TYPE:
  795                 sc->sgtc = ACPI_PN7_CTRL_TO_SGTC(ctrl);
  796                 /*
  797                  * XXX Some bios forget the max frequency!
  798                  * This maybe indicates we have the wrong tables.  Therefore,
  799                  * don't implement a quirk, but fallback to BIOS legacy
  800                  * tables instead.
  801                  */
  802                 if (PN7_STA_MFID(status) != state.fid) {
  803                         device_printf(dev, "ACPI MAX frequency not found\n");
  804                         return (EINVAL);
  805                 }
  806                 break;
  807         case PN8_TYPE:
  808                 sc->vst = ACPI_PN8_CTRL_TO_VST(ctrl),
  809                 sc->mvs = ACPI_PN8_CTRL_TO_MVS(ctrl),
  810                 sc->pll = ACPI_PN8_CTRL_TO_PLL(ctrl),
  811                 sc->rvo = ACPI_PN8_CTRL_TO_RVO(ctrl),
  812                 sc->irt = ACPI_PN8_CTRL_TO_IRT(ctrl);
  813                 sc->low = 0; /* XXX */
  814 
  815                 /*
  816                  * powernow k8 supports only one low frequency.
  817                  */
  818                 if (sc->powernow_max_states >= 2 &&
  819                     (sc->powernow_states[sc->powernow_max_states - 2].fid < 8))
  820                         return (EINVAL);
  821                 break;
  822         }
  823 
  824         return (0);
  825 }
  826 
  827 static void
  828 pn_identify(driver_t *driver, device_t parent)
  829 {
  830         device_t child;
  831 
  832         if (cpu_is_powernow_capable() == 0)
  833                 return;
  834         switch (cpu_id & 0xf00) {
  835         case 0x600:
  836         case 0xf00:
  837                 break;
  838         default:
  839                 return;
  840         }
  841         if (device_find_child(parent, "powernow", -1) != NULL)
  842                 return;
  843         if ((child = BUS_ADD_CHILD(parent, 0, "powernow", -1)) == NULL)
  844                 device_printf(parent, "powernow: add child failed\n");
  845 }
  846 
  847 static int
  848 pn_probe(device_t dev)
  849 {
  850         struct pn_softc *sc;
  851         uint64_t status;
  852         uint64_t rate;
  853         struct pcpu *pc;
  854         u_int sfid, mfid, cfid;
  855 
  856         sc = device_get_softc(dev);
  857         sc->errata_a0 = FALSE;
  858         status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
  859 
  860         pc = cpu_get_pcpu(dev);
  861         if (pc == NULL)
  862                 return (ENODEV);
  863 
  864         cpu_est_clockrate(pc->pc_cpuid, &rate);
  865 
  866         switch (cpu_id & 0xf00) {
  867         case 0x600:
  868                 sfid = PN7_STA_SFID(status);
  869                 mfid = PN7_STA_MFID(status);
  870                 cfid = PN7_STA_CFID(status);
  871                 sc->pn_type = PN7_TYPE;
  872                 sc->fsb = rate / 100000 / pn7_fid_to_mult[cfid];
  873 
  874                 /*
  875                  * If start FID is different to max FID, then it is a
  876                  * mobile processor.  If not, it is a low powered desktop
  877                  * processor.
  878                  */
  879                 if (PN7_STA_SFID(status) != PN7_STA_MFID(status)) {
  880                         sc->vid_to_volts = pn7_mobile_vid_to_volts;
  881                         device_set_desc(dev, "PowerNow! K7");
  882                 } else {
  883                         sc->vid_to_volts = pn7_desktop_vid_to_volts;
  884                         device_set_desc(dev, "Cool`n'Quiet K7");
  885                 }
  886                 break;
  887 
  888         case 0xf00:
  889                 sfid = PN8_STA_SFID(status);
  890                 mfid = PN8_STA_MFID(status);
  891                 cfid = PN8_STA_CFID(status);
  892                 sc->pn_type = PN8_TYPE;
  893                 sc->vid_to_volts = pn8_vid_to_volts;
  894                 sc->fsb = rate / 100000 / pn8_fid_to_mult[cfid >> 1];
  895 
  896                 if (PN8_STA_SFID(status) != PN8_STA_MFID(status))
  897                         device_set_desc(dev, "PowerNow! K8");
  898                 else
  899                         device_set_desc(dev, "Cool`n'Quiet K8");
  900                 break;
  901         default:
  902                 return (ENODEV);
  903         }
  904 
  905         return (0);
  906 }
  907 
  908 static int
  909 pn_attach(device_t dev)
  910 {
  911         int rv;
  912         device_t child;
  913 
  914         child = device_find_child(device_get_parent(dev), "acpi_perf", -1);
  915         if (child) {
  916                 rv = pn_decode_acpi(dev, child);
  917                 if (rv)
  918                         rv = pn_decode_pst(dev);
  919         } else
  920                 rv = pn_decode_pst(dev);
  921 
  922         if (rv != 0)
  923                 return (ENXIO);
  924         cpufreq_register(dev);
  925         return (0);
  926 }
  927 
  928 static int
  929 pn_detach(device_t dev)
  930 {
  931 
  932         cpufreq_unregister(dev);
  933         return (0);
  934 }

Cache object: faa4db4745c9b499ce1112898174de2d


[ 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.