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/powerpc/cpufreq/pcr.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  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2009 Nathan Whitehorn
    5  * All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  *
   16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
   23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   26  * SUCH DAMAGE.
   27  */
   28 
   29 #include <sys/cdefs.h>
   30 __FBSDID("$FreeBSD$");
   31 
   32 #include <sys/param.h>
   33 #include <sys/systm.h>
   34 #include <sys/bus.h>
   35 #include <sys/cpu.h>
   36 #include <sys/kernel.h>
   37 #include <sys/module.h>
   38 
   39 #include <dev/ofw/ofw_bus.h>
   40 
   41 #include "cpufreq_if.h"
   42 
   43 struct pcr_softc {
   44         device_t dev;
   45         uint32_t pcr_vals[3];
   46         int nmodes;
   47 };
   48 
   49 static void     pcr_identify(driver_t *driver, device_t parent);
   50 static int      pcr_probe(device_t dev);
   51 static int      pcr_attach(device_t dev);
   52 static int      pcr_settings(device_t dev, struct cf_setting *sets, int *count);
   53 static int      pcr_set(device_t dev, const struct cf_setting *set);
   54 static int      pcr_get(device_t dev, struct cf_setting *set);
   55 static int      pcr_type(device_t dev, int *type);
   56 
   57 static device_method_t pcr_methods[] = {
   58         /* Device interface */
   59         DEVMETHOD(device_identify,      pcr_identify),
   60         DEVMETHOD(device_probe,         pcr_probe),
   61         DEVMETHOD(device_attach,        pcr_attach),
   62 
   63         /* cpufreq interface */
   64         DEVMETHOD(cpufreq_drv_set,      pcr_set),
   65         DEVMETHOD(cpufreq_drv_get,      pcr_get),
   66         DEVMETHOD(cpufreq_drv_type,     pcr_type),
   67         DEVMETHOD(cpufreq_drv_settings, pcr_settings),
   68         {0, 0}
   69 };
   70 
   71 static driver_t pcr_driver = {
   72         "pcr",
   73         pcr_methods,
   74         sizeof(struct pcr_softc)
   75 };
   76 
   77 DRIVER_MODULE(pcr, cpu, pcr_driver, 0, 0);
   78 
   79 /*
   80  * States
   81  */
   82 
   83 #define PCR_TO_FREQ(a)  ((a >> 17) & 3)
   84 
   85 #define PCR_FULL        0
   86 #define PCR_HALF        1
   87 #define PCR_QUARTER     2               /* Only on 970MP */
   88 
   89 #define PSR_RECEIVED    (1ULL << 61)
   90 #define PSR_COMPLETED   (1ULL << 61)
   91 
   92 /*
   93  * SCOM addresses
   94  */
   95 
   96 #define SCOM_PCR        0x0aa00100      /* Power Control Register */
   97 #define SCOM_PCR_BIT    0x80000000      /* Data bit for PCR */
   98 #define SCOM_PSR        0x40800100      /* Power Status Register */
   99 
  100 /*
  101  * SCOM Glue
  102  */
  103 
  104 #define SCOMC_READ      0x00008000
  105 #define SCOMC_WRITE     0x00000000 
  106 
  107 static void
  108 write_scom(register_t address, uint64_t value)
  109 {
  110         register_t msr;
  111         #ifndef __powerpc64__
  112         register_t hi, lo, scratch;
  113         #endif
  114 
  115         msr = mfmsr();
  116         mtmsr(msr & ~PSL_EE); isync();
  117 
  118         #ifdef __powerpc64__
  119         mtspr(SPR_SCOMD, value);
  120         #else
  121         hi = (value >> 32) & 0xffffffff;
  122         lo = value & 0xffffffff;
  123         mtspr64(SPR_SCOMD, hi, lo, scratch); 
  124         #endif
  125         isync();
  126         mtspr(SPR_SCOMC, address | SCOMC_WRITE);
  127         isync();
  128 
  129         mtmsr(msr); isync();
  130 }
  131 
  132 static uint64_t
  133 read_scom(register_t address)
  134 {
  135         register_t msr;
  136         uint64_t ret;
  137 
  138         msr = mfmsr();
  139         mtmsr(msr & ~PSL_EE); isync();
  140 
  141         mtspr(SPR_SCOMC, address | SCOMC_READ);
  142         isync();
  143 
  144         __asm __volatile ("mfspr %0,%1;"
  145             " mr %0+1, %0; srdi %0,%0,32" : "=r" (ret) : "K" (SPR_SCOMD));
  146 
  147         (void)mfspr(SPR_SCOMC); /* Complete transcation */
  148 
  149         mtmsr(msr); isync();
  150 
  151         return (ret);
  152 }
  153 
  154 static void
  155 pcr_identify(driver_t *driver, device_t parent)
  156 {
  157         uint16_t vers;
  158         vers = mfpvr() >> 16;
  159 
  160         /* Check for an IBM 970-class CPU */
  161         switch (vers) {
  162                 case IBM970FX:
  163                 case IBM970GX:
  164                 case IBM970MP:
  165                         break;
  166                 default:
  167                         return;
  168         }
  169 
  170         /* Make sure we're not being doubly invoked. */
  171         if (device_find_child(parent, "pcr", -1) != NULL)
  172                 return;
  173 
  174         /*
  175          * We attach a child for every CPU since settings need to
  176          * be performed on every CPU in the SMP case.
  177          */
  178         if (BUS_ADD_CHILD(parent, 10, "pcr", -1) == NULL)
  179                 device_printf(parent, "add pcr child failed\n");
  180 }
  181 
  182 static int
  183 pcr_probe(device_t dev)
  184 {
  185         if (resource_disabled("pcr", 0))
  186                 return (ENXIO);
  187 
  188         device_set_desc(dev, "PPC 970 Power Control Register");
  189         return (0);
  190 }
  191 
  192 static int
  193 pcr_attach(device_t dev)
  194 {
  195         struct pcr_softc *sc;
  196         phandle_t cpu;
  197         uint32_t modes[3];
  198         int i;
  199 
  200         sc = device_get_softc(dev);
  201         sc->dev = dev;
  202 
  203         cpu = ofw_bus_get_node(device_get_parent(dev));
  204 
  205         if (cpu <= 0) {
  206                 device_printf(dev,"No CPU device tree node!\n");
  207                 return (ENXIO);
  208         }
  209 
  210         if (OF_getproplen(cpu, "power-mode-data") <= 0) {
  211                 /* Use the first CPU's node */
  212                 cpu = OF_child(OF_parent(cpu));
  213         }
  214 
  215         /*
  216          * Collect the PCR values for each mode from the device tree.
  217          * These include bus timing information, and so cannot be
  218          * directly computed.
  219          */
  220         sc->nmodes = OF_getproplen(cpu, "power-mode-data");
  221         if (sc->nmodes <= 0 || sc->nmodes > sizeof(sc->pcr_vals)) {
  222                 device_printf(dev,"No power mode data in device tree!\n");
  223                 return (ENXIO);
  224         }
  225         OF_getprop(cpu, "power-mode-data", modes, sc->nmodes);
  226         sc->nmodes /= sizeof(modes[0]);
  227 
  228         /* Sort the modes */
  229         for (i = 0; i < sc->nmodes; i++)
  230                 sc->pcr_vals[PCR_TO_FREQ(modes[i])] = modes[i];
  231 
  232         cpufreq_register(dev);
  233         return (0);
  234 }
  235 
  236 static int
  237 pcr_settings(device_t dev, struct cf_setting *sets, int *count)
  238 {
  239         struct pcr_softc *sc;
  240 
  241         sc = device_get_softc(dev);
  242         if (sets == NULL || count == NULL)
  243                 return (EINVAL);
  244         if (*count < sc->nmodes)
  245                 return (E2BIG);
  246 
  247         /* Return a list of valid settings for this driver. */
  248         memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * sc->nmodes);
  249 
  250         sets[0].freq = 10000; sets[0].dev = dev;
  251         sets[1].freq = 5000; sets[1].dev = dev;
  252         if (sc->nmodes > 2) {
  253                 sets[2].freq = 2500;
  254                 sets[2].dev = dev;
  255         }
  256         *count = sc->nmodes;
  257 
  258         return (0);
  259 }
  260 
  261 static int
  262 pcr_set(device_t dev, const struct cf_setting *set)
  263 {
  264         struct pcr_softc *sc;
  265         register_t pcr, msr;
  266         uint64_t psr;
  267 
  268         if (set == NULL)
  269                 return (EINVAL);
  270         sc = device_get_softc(dev);
  271 
  272         /* Construct the new PCR */
  273 
  274         pcr = SCOM_PCR_BIT;
  275 
  276         if (set->freq == 10000)
  277                 pcr |= sc->pcr_vals[0];
  278         else if (set->freq == 5000)
  279                 pcr |= sc->pcr_vals[1];
  280         else if (set->freq == 2500)
  281                 pcr |= sc->pcr_vals[2];
  282 
  283         msr = mfmsr(); 
  284         mtmsr(msr & ~PSL_EE); isync();
  285 
  286         /* 970MP requires PCR and PCRH to be cleared first */
  287 
  288         write_scom(SCOM_PCR,0);                 /* Clear PCRH */
  289         write_scom(SCOM_PCR,SCOM_PCR_BIT);      /* Clear PCR */
  290 
  291         /* Set PCR */
  292 
  293         write_scom(SCOM_PCR, pcr);
  294 
  295         /* Wait for completion */
  296 
  297         do {
  298                 DELAY(100);
  299                 psr = read_scom(SCOM_PSR);
  300         } while ((psr & PSR_RECEIVED) && !(psr & PSR_COMPLETED));
  301 
  302         mtmsr(msr); isync();
  303 
  304         return (0);
  305 }
  306 
  307 static int
  308 pcr_get(device_t dev, struct cf_setting *set)
  309 {
  310         uint64_t psr;
  311 
  312         if (set == NULL)
  313                 return (EINVAL);
  314 
  315         memset(set, CPUFREQ_VAL_UNKNOWN, sizeof(*set));
  316 
  317         psr = read_scom(SCOM_PSR);
  318 
  319         /* We want bits 6 and 7 */
  320         psr = (psr >> 56) & 3;
  321 
  322         set->freq = 10000;
  323         if (psr == PCR_HALF)
  324                 set->freq = 5000;
  325         else if (psr == PCR_QUARTER)
  326                 set->freq = 2500;
  327 
  328         set->dev = dev;
  329 
  330         return (0);
  331 }
  332 
  333 static int
  334 pcr_type(device_t dev, int *type)
  335 {
  336 
  337         if (type == NULL)
  338                 return (EINVAL);
  339 
  340         *type = CPUFREQ_TYPE_RELATIVE;
  341         return (0);
  342 }

Cache object: dc6e6bcfadd999f6030771a9364a46db


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