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/mpc85xx_jog.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) 2017 Justin Hibbits
    3  * All rights reserved.
    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,
   19  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
   21  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   22  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24  * SUCH DAMAGE.
   25  */
   26 
   27 #include <sys/cdefs.h>
   28 __FBSDID("$FreeBSD$");
   29 
   30 #include <sys/param.h>
   31 #include <sys/systm.h>
   32 #include <sys/bus.h>
   33 #include <sys/cpu.h>
   34 #include <sys/kernel.h>
   35 #include <sys/module.h>
   36 #include <sys/smp.h>
   37 
   38 #include <dev/ofw/ofw_bus.h>
   39 #include <dev/ofw/ofw_bus_subr.h>
   40 
   41 #include <machine/cpu.h>
   42 
   43 #include <powerpc/mpc85xx/mpc85xx.h>
   44 
   45 #include "cpufreq_if.h"
   46 
   47 /* No worries about uint32_t math overflow in here, because the highest
   48  * multiplier supported is 4, and the highest speed part is still well below
   49  * 2GHz.
   50  */
   51 
   52 #define GUTS_PORPLLSR           (CCSRBAR_VA + 0xe0000)
   53 #define GUTS_PMJCR              (CCSRBAR_VA + 0xe007c)
   54 #define   PMJCR_RATIO_M           0x3f
   55 #define   PMJCR_CORE_MULT(x,y)    ((x) << (16 + ((y) * 8)))
   56 #define   PMJCR_GET_CORE_MULT(x,y)        (((x) >> (16 + ((y) * 8))) & 0x3f)
   57 #define GUTS_POWMGTCSR          (CCSRBAR_VA + 0xe0080)
   58 #define   POWMGTCSR_JOG           0x00200000
   59 #define   POWMGTCSR_INT_MASK      0x00000f00
   60 
   61 #define MHZ     1000000
   62 
   63 struct mpc85xx_jog_softc {
   64         device_t dev;
   65         int     cpu;
   66         int     low;
   67         int     high;
   68         int     min_freq;
   69 };
   70 
   71 static struct ofw_compat_data *mpc85xx_jog_devcompat(void);
   72 static void     mpc85xx_jog_identify(driver_t *driver, device_t parent);
   73 static int      mpc85xx_jog_probe(device_t dev);
   74 static int      mpc85xx_jog_attach(device_t dev);
   75 static int      mpc85xx_jog_settings(device_t dev, struct cf_setting *sets, int *count);
   76 static int      mpc85xx_jog_set(device_t dev, const struct cf_setting *set);
   77 static int      mpc85xx_jog_get(device_t dev, struct cf_setting *set);
   78 static int      mpc85xx_jog_type(device_t dev, int *type);
   79 
   80 static device_method_t mpc85xx_jog_methods[] = {
   81         /* Device interface */
   82         DEVMETHOD(device_identify,      mpc85xx_jog_identify),
   83         DEVMETHOD(device_probe,         mpc85xx_jog_probe),
   84         DEVMETHOD(device_attach,        mpc85xx_jog_attach),
   85 
   86         /* cpufreq interface */
   87         DEVMETHOD(cpufreq_drv_set,      mpc85xx_jog_set),
   88         DEVMETHOD(cpufreq_drv_get,      mpc85xx_jog_get),
   89         DEVMETHOD(cpufreq_drv_type,     mpc85xx_jog_type),
   90         DEVMETHOD(cpufreq_drv_settings, mpc85xx_jog_settings),
   91 
   92         {0, 0}
   93 };
   94 
   95 static driver_t mpc85xx_jog_driver = {
   96         "jog",
   97         mpc85xx_jog_methods,
   98         sizeof(struct mpc85xx_jog_softc)
   99 };
  100 
  101 static devclass_t mpc85xx_jog_devclass;
  102 DRIVER_MODULE(mpc85xx_jog, cpu, mpc85xx_jog_driver, mpc85xx_jog_devclass, 0, 0);
  103 
  104 struct mpc85xx_constraints {
  105         int threshold; /* Threshold frequency, in MHz, for setting CORE_SPD bit. */
  106         int min_mult;  /* Minimum PLL multiplier. */
  107 };
  108 
  109 static struct mpc85xx_constraints mpc8536_constraints = {
  110         800,
  111         3
  112 };
  113 
  114 static struct mpc85xx_constraints p1022_constraints = {
  115         500,
  116         2
  117 };
  118 
  119 static struct ofw_compat_data jog_compat[] = {
  120     {"fsl,mpc8536-guts", (uintptr_t)&mpc8536_constraints},
  121     {"fsl,p1022-guts", (uintptr_t)&p1022_constraints},
  122     {NULL, 0}
  123 };
  124 
  125 static struct ofw_compat_data *
  126 mpc85xx_jog_devcompat()
  127 {
  128         phandle_t node;
  129         int i;
  130 
  131         node = OF_finddevice("/soc");
  132         if (node == -1)
  133                 return (NULL);
  134 
  135         for (i = 0; jog_compat[i].ocd_str != NULL; i++)
  136                 if (ofw_bus_find_compatible(node, jog_compat[i].ocd_str) > 0)
  137                         break;
  138 
  139         if (jog_compat[i].ocd_str == NULL)
  140                 return (NULL);
  141 
  142         return (&jog_compat[i]);
  143 }
  144 
  145 static void
  146 mpc85xx_jog_identify(driver_t *driver, device_t parent)
  147 {
  148         struct ofw_compat_data *compat;
  149 
  150         /* Make sure we're not being doubly invoked. */
  151         if (device_find_child(parent, "mpc85xx_jog", -1) != NULL)
  152                 return;
  153 
  154         compat = mpc85xx_jog_devcompat();
  155         if (compat == NULL)
  156                 return;
  157         
  158         /*
  159          * We attach a child for every CPU since settings need to
  160          * be performed on every CPU in the SMP case.
  161          */
  162         if (BUS_ADD_CHILD(parent, 10, "jog", -1) == NULL)
  163                 device_printf(parent, "add jog child failed\n");
  164 }
  165 
  166 static int
  167 mpc85xx_jog_probe(device_t dev)
  168 {
  169         struct ofw_compat_data *compat;
  170 
  171         compat = mpc85xx_jog_devcompat();
  172         if (compat == NULL || compat->ocd_str == NULL)
  173                 return (ENXIO);
  174 
  175         device_set_desc(dev, "Freescale CPU Jogger");
  176         return (0);
  177 }
  178 
  179 static int
  180 mpc85xx_jog_attach(device_t dev)
  181 {
  182         struct ofw_compat_data *compat;
  183         struct mpc85xx_jog_softc *sc;
  184         struct mpc85xx_constraints *constraints;
  185         phandle_t cpu;
  186         uint32_t reg;
  187 
  188         sc = device_get_softc(dev);
  189         sc->dev = dev;
  190 
  191         compat = mpc85xx_jog_devcompat();
  192         constraints = (struct mpc85xx_constraints *)compat->ocd_data;
  193         cpu = ofw_bus_get_node(device_get_parent(dev));
  194 
  195         if (cpu <= 0) {
  196                 device_printf(dev,"No CPU device tree node!\n");
  197                 return (ENXIO);
  198         }
  199 
  200         OF_getencprop(cpu, "reg", &sc->cpu, sizeof(sc->cpu));
  201 
  202         reg = ccsr_read4(GUTS_PORPLLSR);
  203         
  204         /*
  205          * Assume power-on PLL is the highest PLL config supported on the
  206          * board.
  207          */
  208         sc->high = PMJCR_GET_CORE_MULT(reg, sc->cpu);
  209         sc->min_freq = constraints->threshold;
  210         sc->low = constraints->min_mult;
  211 
  212         cpufreq_register(dev);
  213         return (0);
  214 }
  215 
  216 static int
  217 mpc85xx_jog_settings(device_t dev, struct cf_setting *sets, int *count)
  218 {
  219         struct mpc85xx_jog_softc *sc;
  220         uint32_t sysclk;
  221         int i;
  222 
  223         sc = device_get_softc(dev);
  224         if (sets == NULL || count == NULL)
  225                 return (EINVAL);
  226         if (*count < sc->high - 1)
  227                 return (E2BIG);
  228 
  229         sysclk = mpc85xx_get_system_clock();
  230         /* Return a list of valid settings for this driver. */
  231         memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * sc->high);
  232 
  233         for (i = sc->high; i >= sc->low; --i) {
  234                 sets[sc->high - i].freq = sysclk * i / MHZ;
  235                 sets[sc->high - i].dev = dev;
  236                 sets[sc->high - i].spec[0] = i;
  237         }
  238         *count = sc->high - sc->low + 1;
  239 
  240         return (0);
  241 }
  242 
  243 struct jog_rv_args {
  244         int cpu;
  245         int mult;
  246         int slow;
  247         volatile int inprogress;
  248 };
  249 
  250 static void
  251 mpc85xx_jog_set_int(void *arg)
  252 {
  253         struct jog_rv_args *args = arg;
  254         uint32_t reg;
  255 
  256         if (PCPU_GET(cpuid) == args->cpu) {
  257                 reg = ccsr_read4(GUTS_PMJCR);
  258                 reg &= ~PMJCR_CORE_MULT(PMJCR_RATIO_M, args->cpu);
  259                 reg |= PMJCR_CORE_MULT(args->mult, args->cpu);
  260                 if (args->slow)
  261                         reg &= ~(1 << (12 + args->cpu));
  262                 else
  263                         reg |= (1 << (12 + args->cpu));
  264 
  265                 ccsr_write4(GUTS_PMJCR, reg);
  266 
  267                 reg = ccsr_read4(GUTS_POWMGTCSR);
  268                 reg |= POWMGTCSR_JOG | POWMGTCSR_INT_MASK;
  269                 ccsr_write4(GUTS_POWMGTCSR, reg);
  270 
  271                 /* Wait for completion */
  272                 do {
  273                         DELAY(100);
  274                         reg = ccsr_read4(GUTS_POWMGTCSR);
  275                 } while (reg & POWMGTCSR_JOG);
  276 
  277                 reg = ccsr_read4(GUTS_POWMGTCSR);
  278                 ccsr_write4(GUTS_POWMGTCSR, reg & ~POWMGTCSR_INT_MASK);
  279                 ccsr_read4(GUTS_POWMGTCSR);
  280 
  281                 args->inprogress = 0;
  282         } else {
  283                 while (args->inprogress)
  284                         cpu_spinwait();
  285         }
  286 }
  287 
  288 static int
  289 mpc85xx_jog_set(device_t dev, const struct cf_setting *set)
  290 {
  291         struct mpc85xx_jog_softc *sc;
  292         struct jog_rv_args args;
  293         
  294         if (set == NULL)
  295                 return (EINVAL);
  296 
  297         sc = device_get_softc(dev);
  298 
  299         args.slow = (set->freq <= sc->min_freq);
  300         args.mult = set->spec[0];
  301         args.cpu = PCPU_GET(cpuid);
  302         args.inprogress = 1;
  303         smp_rendezvous(smp_no_rendezvous_barrier, mpc85xx_jog_set_int,
  304             smp_no_rendezvous_barrier, &args);
  305 
  306         return (0);
  307 }
  308 
  309 static int
  310 mpc85xx_jog_get(device_t dev, struct cf_setting *set)
  311 {
  312         struct mpc85xx_jog_softc *sc;
  313         uint32_t pmjcr;
  314         uint32_t freq;
  315 
  316         if (set == NULL)
  317                 return (EINVAL);
  318 
  319         sc = device_get_softc(dev);
  320         memset(set, CPUFREQ_VAL_UNKNOWN, sizeof(*set));
  321 
  322         pmjcr = ccsr_read4(GUTS_PORPLLSR);
  323         freq = PMJCR_GET_CORE_MULT(pmjcr, sc->cpu);
  324         freq *= mpc85xx_get_system_clock();
  325         freq /= MHZ;
  326         
  327         set->freq = freq;
  328         set->dev = dev;
  329 
  330         return (0);
  331 }
  332 
  333 static int
  334 mpc85xx_jog_type(device_t dev, int *type)
  335 {
  336 
  337         if (type == NULL)
  338                 return (EINVAL);
  339 
  340         *type = CPUFREQ_TYPE_ABSOLUTE;
  341         return (0);
  342 }
  343 

Cache object: 4edafe44150815b5a9ebbb73bf8aa1f2


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