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/osfmk/ppc/pms.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) 2005 Apple Computer, Inc. All rights reserved.
    3  *
    4  * @APPLE_LICENSE_HEADER_START@
    5  * 
    6  * The contents of this file constitute Original Code as defined in and
    7  * are subject to the Apple Public Source License Version 1.1 (the
    8  * "License").  You may not use this file except in compliance with the
    9  * License.  Please obtain a copy of the License at
   10  * http://www.apple.com/publicsource and read it before using this file.
   11  * 
   12  * This Original Code and all software distributed under the License are
   13  * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
   14  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
   15  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
   16  * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
   17  * License for the specific language governing rights and limitations
   18  * under the License.
   19  * 
   20  * @APPLE_LICENSE_HEADER_END@
   21  */
   22 #include <ppc/machine_routines.h>
   23 #include <ppc/machine_cpu.h>
   24 #include <ppc/exception.h>
   25 #include <ppc/misc_protos.h>
   26 #include <ppc/Firmware.h>
   27 #include <ppc/pmap.h>
   28 #include <ppc/proc_reg.h>
   29 #include <ppc/pms.h>
   30 #include <ppc/savearea.h>
   31 #include <ppc/exception.h>
   32 #include <kern/processor.h>
   33 
   34 extern int real_ncpus;
   35 
   36 static uint32_t pmsSyncrolator = 0;                                     /* Only one control operation at a time please */
   37 uint32_t pmsBroadcastWait = 0;                                          /* Number of outstanding broadcasts */
   38 
   39 int pmsInstalled = 0;                                                           /* Power Management Stepper can run and has table installed */
   40 int pmsExperimental = 0;                                                        /* Power Management Stepper in experimental mode */
   41 decl_simple_lock_data(,pmsBuildLock)                            /* Make sure only one guy can replace table  at the same time */
   42 
   43 static pmsDef *altDpmsTab = 0;                                          /* Alternate step definition table */
   44 static uint32_t altDpmsTabSize = 0;                                     /* Size of alternate step definition table */
   45 
   46 pmsDef pmsDummy = {                                                                     /* This is the dummy step for initialization.  All it does is to park */
   47         .pmsLimit = 0,                                                                  /* Time doesn't matter for a park */
   48         .pmsStepID = pmsMaxStates - 1,                                  /* Use the very last ID number for the dummy */
   49         .pmsSetCmd = pmsParkIt,                                                 /* Force us to be parked */
   50         .sf.pmsSetFuncInd = 0,                                                  /* No platform call for this one */
   51         .pmsDown = pmsPrepSleep,                                                /* We always park */
   52         .pmsNext = pmsPrepSleep                                                 /* We always park */
   53 };
   54 
   55 pmsStat pmsStatsd[4][pmsMaxStates];                                     /* Generate enough statistics blocks for 4 processors */
   56 
   57 pmsCtl pmsCtls = {                                                                      /* Power Management Stepper control */
   58         .pmsStats = &pmsStatsd
   59 };
   60 
   61 pmsSetFunc_t pmsFuncTab[pmsSetFuncMax] = {0};           /* This is the function index table */
   62 pmsQueryFunc_t pmsQueryFunc = 0;                                        /* Pointer to pmsQuery function */
   63 uint32_t pmsPlatformData = 0;                                           /* Data provided by and passed to platform functions */
   64 
   65 
   66 /*
   67  *      Do any initialization needed
   68  */
   69  
   70 void pmsInit(void) {
   71 
   72         int i;
   73         
   74         simple_lock_init(&pmsBuildLock, 0);                             /* Initialize the build lock */
   75         for(i = 0; i < pmsMaxStates; i++) pmsCtls.pmsDefs[i] = &pmsDummy;       /* Initialize the table to dummy steps */
   76 
   77         return;
   78 }
   79 
   80 
   81 /*
   82  *      Start the power management stepper on all processors
   83  *
   84  *      All processors must be parked.  This should be called when the hardware
   85  *      is ready to step.  Probably only at boot and after wake from sleep.
   86  *
   87  */
   88  
   89  void pmsStart(void) {
   90 
   91         boolean_t       intr;
   92 
   93         if(!pmsInstalled) return;                                               /* We can't do this if no table installed */
   94 
   95         intr = ml_set_interrupts_enabled(FALSE);                /* No interruptions in here */
   96         pmsRun(pmsStartUp);                                                             /* Start running the stepper everywhere */
   97         (void)ml_set_interrupts_enabled(intr);                  /* Restore interruptions */
   98 
   99         return;
  100  
  101  }
  102  
  103 
  104 /*
  105  *      Park the stepper execution.  This will force the stepper on this
  106  *      processor to abandon its current step and stop.  No changes to the
  107  *      hardware state is made and any previous step is lost.
  108  *      
  109  *      This is used as the initial state at startup and when the step table
  110  *      is being changed.
  111  *
  112  */
  113  
  114 void pmsPark(void) {
  115 
  116         boolean_t       intr;
  117 
  118         if(!pmsInstalled) return;                                               /* We can't do this if no table installed */
  119 
  120         intr = ml_set_interrupts_enabled(FALSE);                /* No interruptions in here */
  121         pmsSetStep(pmsParked, 0);                                               /* Park the stepper */
  122         (void)ml_set_interrupts_enabled(intr);                  /* Restore interruptions */
  123         
  124         return;
  125 
  126 }
  127  
  128 
  129 /*
  130  *      Steps down to a lower power.
  131  *      Interrupts must be off...
  132  */
  133 
  134 void pmsDown(void) {
  135 
  136         struct per_proc_info *pp;
  137         uint32_t nstate;
  138         
  139         pp = getPerProc();                                                              /* Get our per_proc */
  140         
  141         if(!pmsInstalled || pp->pms.pmsState == pmsParked) return;              /* No stepping if parked or not installed */
  142         
  143         nstate = pmsCtls.pmsDefs[pp->pms.pmsState]->pmsDown;    /* Get the downward step */
  144         pmsSetStep(nstate, 0);                                                  /* Step to it */
  145         return;
  146 }
  147 
  148 
  149 /*
  150  *      Steps up to a higher power.  The "timer" parameter is true if the
  151  *      step was driven due to the pms timer expiring.
  152  *
  153  *      Interrupts must be off...
  154  */
  155  
  156 void pmsStep(int timer) {
  157 
  158         struct per_proc_info *pp;
  159         uint32_t nstate;
  160         int dir;
  161         
  162         pp = getPerProc();                                                              /* Get our per_proc */
  163 
  164         if(!pmsInstalled || pp->pms.pmsState == pmsParked) return;      /* No stepping if parked or not installed */
  165         
  166         nstate = pmsCtls.pmsDefs[pp->pms.pmsState]->pmsNext;    /* Assume a normal step */
  167         dir = 1;                                                                                /* A normal step is a step up */
  168         
  169         if(timer && (pmsCtls.pmsDefs[pp->pms.pmsState]->pmsSetCmd == pmsDelay)) {       /* If the timer expired and we are in a delay step, use the delay branch */
  170                 nstate = pmsCtls.pmsDefs[pp->pms.pmsState]->pmsTDelay;  /* Get the delayed step */
  171                 dir = 0;                                                                        /* Delayed steps are a step down for accounting purposes. */
  172         }
  173 
  174         pmsSetStep(nstate, dir);                                                /* Step to it  */
  175         return;
  176 }
  177 
  178 
  179 /*
  180  *      Set a specific step
  181  *
  182  *      We do not do statistics if exiting park
  183  *      Interrupts must be off...
  184  *
  185  */
  186 
  187 void pmsSetStep(uint32_t nstep, int dir) {
  188 
  189         struct per_proc_info *pp;
  190         uint32_t pstate, ret, nCSetCmd, mCSetCmd;
  191         pmsDef *pnstate, *pcstate;
  192         uint64_t tb, nt, dur;
  193         int cpu, frompark;
  194 
  195         pp = getPerProc();                                                              /* Get our per_proc */
  196         cpu = cpu_number();                                                             /* Get our processor */
  197         
  198         while(1) {                                                                              /* Keep stepping until we get a delay */
  199                 
  200                 if(pp->pms.pmsCSetCmd & pmsMustCmp) {           /* Do we have to finish the delay before changing? */
  201                         while(mach_absolute_time() < pp->pms.pmsPop);   /* Yes, spin here... */
  202                 }
  203                 
  204                 if((nstep == pmsParked) || ((uint32_t)pmsCtls.pmsDefs[nstep]->pmsSetCmd == pmsParkIt)) {        /* Are we parking? */
  205                         
  206                         tb = mach_absolute_time();                              /* What time is it? */
  207                         pp->pms.pmsStamp = tb;                                  /* Show transition now */
  208                         pp->pms.pmsPop = HalfwayToForever;              /* Set the pop way into the future */
  209                         pp->pms.pmsState = pmsParked;                   /* Make sure we are parked */
  210                         setTimerReq();                                                  /* Cancel our timer if going */
  211                         return;
  212                 }
  213 
  214                 pnstate = pmsCtls.pmsDefs[nstep];                       /* Point to the state definition */ 
  215                 pstate = pp->pms.pmsState;                                      /* Save the current step */
  216                 pp->pms.pmsState = nstep;                                       /* Set the current to the next step */
  217 
  218                 if(pnstate->pmsSetCmd != pmsDelay) {            /* If this is not a delayed state, change the actual hardware now */
  219                         if(pnstate->pmsSetCmd & pmsCngCPU) pmsCPUSet(pnstate->pmsSetCmd);       /* We have some CPU work to do... */
  220                         if((uint32_t)pnstate->sf.pmsSetFunc) pnstate->sf.pmsSetFunc(pnstate->pmsSetCmd, cpu, pmsPlatformData);  /* Tell the platform to set power mode */
  221         
  222                         mCSetCmd = pnstate->pmsSetCmd & (pmsCngXClk | pmsCngCPU | pmsCngVolt);  /* Isolate just the change flags */
  223                         mCSetCmd = (mCSetCmd - (mCSetCmd >> 7)) | pmsSync | pmsMustCmp | pmsPowerID;    /* Form mask of bits that come from new command */
  224                         nCSetCmd = pp->pms.pmsCSetCmd & ~mCSetCmd;      /* Clear changing bits */
  225                         nCSetCmd = nCSetCmd | (pnstate->pmsSetCmd & mCSetCmd);  /* Flip on the changing bits and the always copy bits */
  226         
  227                         pp->pms.pmsCSetCmd = nCSetCmd;                  /* Set it for real */
  228                 }
  229         
  230                 tb = mach_absolute_time();                                      /* What time is it? */
  231                 pp->pms.pmsPop = tb + pnstate->pmsLimit;        /* Set the next pop */
  232         
  233                 if((pnstate->pmsSetCmd != pmsDelay) && (pp->pms.pmsCSetCmd & pmsSync) && (pnstate->pmsLimit != 0)) {    /* Is this a synchronous command with a delay? */
  234                         while(mach_absolute_time() < pp->pms.pmsPop);   /* Yes, spin here and wait it out... */
  235                 }
  236 
  237 /*
  238  *              Gather some statistics
  239  */
  240           
  241                 dur = tb - pp->pms.pmsStamp;                            /* Get the amount of time we were in the old step */
  242                 pp->pms.pmsStamp = tb;                                          /* Set the new timestamp */
  243                 if(!(pstate == pmsParked)) {                            /* Only take stats if we were not parked */
  244                         pcstate = pmsCtls.pmsDefs[pstate];              /* Get the previous step */
  245                         pmsCtls.pmsStats[cpu][pcstate->pmsStepID].stTime[dir] += dur;   /* Accumulate the total time in the old step */ 
  246                         pmsCtls.pmsStats[cpu][pcstate->pmsStepID].stCnt[dir] += 1;      /* Count transitions */
  247                 }
  248 
  249 /*
  250  *              See if we are done chaining steps
  251  */
  252  
  253                 if((pnstate->pmsSetCmd == pmsDelay) 
  254                         || (!(pp->pms.pmsCSetCmd & pmsSync) && (pnstate->pmsLimit != 0))) {     /* Is this not syncronous and a non-zero delay or a delayed step? */
  255                         setTimerReq();                                                  /* Start the timers ticking */
  256                         break;                                                                  /* We've stepped as far as we're going to... */
  257                 }
  258                 
  259                 nstep = pnstate->pmsNext;                                       /* Chain on to the next */
  260         }
  261 
  262         return;
  263 
  264 }
  265 
  266 /*
  267  *      Either park the stepper or force the step on a parked stepper for local processor only
  268  *
  269  */
  270  
  271 void pmsRunLocal(uint32_t nstep) {
  272 
  273         struct per_proc_info *pp;
  274         uint32_t cstate, ret, lastState;
  275         pmsDef *pnstate, *pcstate;
  276         uint64_t tb, nt, dur;
  277         int cpu, i, j;
  278         boolean_t       intr;
  279 
  280         if(!pmsInstalled) return;                                               /* Ignore this if no step programs installed... */
  281 
  282         intr = ml_set_interrupts_enabled(FALSE);                /* No interruptions in here */
  283 
  284         pp = getPerProc();                                                              /* Get our per_proc */
  285 
  286         if(nstep == pmsStartUp) {                                               /* Should we start up? */
  287                 pmsCPUInit();                                                           /* Get us up to full with high voltage and park */
  288                 nstep = pmsNormHigh;                                            /* Change request to transition to normal high */
  289         }
  290 
  291         lastState = pp->pms.pmsState;                                   /* Remember if we are parked now */
  292 
  293         pmsSetStep(nstep, 1);                                                   /* Step to the new state */
  294         
  295         if((lastState == pmsParked) && (pp->pms.pmsState != pmsParked)) {       /* Did we just unpark? */
  296                 cpu = cpu_number();                                                     /* Get our processor */
  297                 for(i = 0; i < pmsMaxStates; i++) {                     /* Step through the steps and clear the statistics since we were parked */
  298                         pmsCtls.pmsStats[cpu][i].stTime[0] = 0; /* Clear accumulated time - downward */ 
  299                         pmsCtls.pmsStats[cpu][i].stTime[1] = 0; /* Clear accumulated time - forward */  
  300                         pmsCtls.pmsStats[cpu][i].stCnt[0] = 0;  /* Clear transition count - downward */
  301                         pmsCtls.pmsStats[cpu][i].stCnt[1] = 0;  /* Clear transition count - forward */
  302                 }
  303         }
  304 
  305         (void)ml_set_interrupts_enabled(intr);                  /* Restore interruptions */
  306 
  307         return;
  308 
  309 }
  310 
  311 /*
  312  *      Control the Power Management Stepper.
  313  *      Called from user state by the superuser via a ppc system call.
  314  *      Interruptions disabled.
  315  *
  316  */
  317 
  318 int pmsCntrl(struct savearea *save) {
  319 
  320         uint32_t request, nstep, reqsize, result, presult;
  321         int ret, cpu;
  322         kern_return_t kret;
  323         pmsDef *ndefs;
  324         struct per_proc_info *pp;
  325 
  326         pp = getPerProc();                                                              /* Get our per_proc */
  327         cpu = cpu_number();                                                             /* Get our processor */
  328         
  329         if(!is_suser()) {                                                               /* We are better than most, */
  330                 save->save_r3 = KERN_FAILURE;                           /* so we will only talk to the superuser. */
  331                 return 1;                                                                       /* Turn up our noses, say "harrumph," and walk away... */
  332         }
  333         
  334         if(save->save_r3 >= pmsCFree) {                                 /* Can we understand the request? */
  335                 save->save_r3 = KERN_INVALID_ARGUMENT;          /* What language are these guys talking in, anyway? */
  336                 return 1;                                                                       /* Cock head like a confused puppy and run away... */
  337         }
  338         
  339         request = (int)save->save_r3;                                   /* Remember the request */
  340         reqsize = (uint32_t)save->save_r5;                              /* Get the size of the config table */
  341 
  342         if(request == pmsCQuery) {                                              /* Are we just checking? */
  343                 result = pmsCPUquery() & pmsCPU;                        /* Get the processor data and make sure there is no slop */
  344                 presult = 0;                                                            /* Assume nothing */
  345                 if((uint32_t)pmsQueryFunc) presult = pmsQueryFunc(cpu, pmsPlatformData);        /* Go get the platform state */
  346                 result = result | (presult & (pmsXClk | pmsVoltage | pmsPowerID));      /* Merge the platform state with no slop */
  347                 save->save_r3 = result;                                         /* Tell 'em... */
  348                 return 1;
  349         }
  350         
  351         if(request == pmsCExperimental) {                               /* Enter experimental mode? */
  352         
  353                 if(pmsInstalled || (pmsExperimental & 1)) {     /* Are we already running or in experimental? */
  354                         save->save_r3 = KERN_FAILURE;                   /* Fail, since we are already running */
  355                         return 1;
  356                 }
  357                 
  358                 pmsExperimental |= 1;                                           /* Flip us into experimental but don't change other flags */
  359                 
  360                 pmsCPUConf();                                                           /* Configure for this machine */
  361                 pmsStart();                                                                     /* Start stepping */
  362                 save->save_r3 = KERN_SUCCESS;                           /* We are victorious... */
  363                 return 1;
  364         
  365         }
  366 
  367         if(request == pmsCCnfg) {                                               /* Do some up-front checking before we commit to doing this */
  368                 if((reqsize > (pmsMaxStates * sizeof(pmsDef))) || (reqsize < (pmsFree * sizeof(pmsDef)))) {     /* Check that the size is reasonable */
  369                         save->save_r3 = KERN_NO_SPACE;                  /* Tell them that they messed up */
  370                         return 1;                                                               /* l8r... */
  371                 }
  372         }
  373 
  374 
  375 /*
  376  *      We are committed after here.  If there are any errors detected, we shouldn't die, but we
  377  *      will be stuck in park.
  378  *
  379  *      Also, we can possibly end up on another processor after the broadcast.
  380  *
  381  */
  382                 
  383         if(!hw_compare_and_store(0, 1, &pmsSyncrolator)) {      /* Are we already doing this? */
  384                 save->save_r3 = KERN_RESOURCE_SHORTAGE;         /* Tell them that we are already busy and to try again */
  385                 return 1;                                                                       /* G'wan away and don't bother me... */
  386         }
  387         save->save_r3 = KERN_SUCCESS;                                   /* Assume success */
  388 
  389 //      NOTE:  We will block in the following code until everyone has finished the prepare
  390 
  391         pmsRun(pmsPrepCng);                                                             /* Get everyone parked and in a proper state for step table changes, including me */
  392         
  393         if(request == pmsCPark) {                                               /* Is all we're supposed to do park? */
  394                 pmsSyncrolator = 0;                                                     /* Free us up */
  395                 return 1;                                                                       /* Well, then we're done... */
  396         }
  397         
  398         switch(request) {                                                               /* Select the routine */
  399 
  400                 case pmsCStart:                                                         /* Starts normal steppping */
  401                         nstep = pmsNormHigh;                                    /* Set the request */
  402                         break;
  403 
  404                 case pmsCFLow:                                                          /* Forces low power */
  405                         nstep = pmsLow;                                                 /* Set request */
  406                         break;
  407 
  408                 case pmsCFHigh:                                                         /* Forces high power */
  409                         nstep = pmsHigh;                                                /* Set request */
  410                         break;
  411 
  412                 case pmsCCnfg:                                                          /* Loads new stepper program */
  413                         
  414                         if(!(ndefs = (pmsDef *)kalloc(reqsize))) {      /* Get memory for the whole thing */
  415                                 save->save_r3 = KERN_INVALID_ADDRESS;   /* Return invalid address */
  416                                 pmsSyncrolator = 0;                                     /* Free us up */
  417                                 return 1;                                                       /* All done... */
  418                         }
  419                         
  420                         ret = copyin((user_addr_t)((unsigned int)(save->save_r4)), (void *)ndefs, reqsize);     /* Get the new config table */
  421                         if(ret) {                                                               /* Hmmm, something went wrong with the copyin */
  422                                 save->save_r3 = KERN_INVALID_ADDRESS;   /* Return invalid address */
  423                                 kfree((vm_offset_t)ndefs, reqsize);     /* Free up the copied in data */
  424                                 pmsSyncrolator = 0;                                     /* Free us up */
  425                                 return 1;                                                       /* All done... */
  426                         }
  427 
  428                         kret = pmsBuild(ndefs, reqsize, 0, 0, 0);       /* Go build and replace the tables.  Make sure we keep the old platform stuff */
  429                         if(kret) {                                                              /* Hmmm, something went wrong with the compilation */
  430                                 save->save_r3 = kret;                           /* Pass back the passed back return code */
  431                                 kfree((vm_offset_t)ndefs, reqsize);     /* Free up the copied in data */
  432                                 pmsSyncrolator = 0;                                     /* Free us up */
  433                                 return 1;                                                       /* All done... */
  434                         }
  435 
  436                         nstep = pmsNormHigh;                                    /* Set the request */
  437                         break;
  438 
  439                 default:
  440                         panic("pmsCntrl: stepper control is so very, very confused = %08X\n", request);
  441         
  442         }
  443 
  444         pmsRun(nstep);                                                                  /* Get everyone into step */
  445         pmsSyncrolator = 0;                                                             /* Free us up */
  446         return 1;                                                                               /* All done... */
  447 
  448 }
  449 
  450 /*
  451  *      Broadcast a change to all processors including ourselves.
  452  *      This must transition before broadcasting because we may block and end up on a different processor.
  453  *
  454  *      This will block until all processors have transitioned, so
  455  *      obviously, this can block.
  456  *
  457  *      Called with interruptions disabled.
  458  *
  459  */
  460  
  461 void pmsRun(uint32_t nstep) {
  462 
  463         pmsRunLocal(nstep);                                                             /* If we aren't parking (we are already parked), transition ourselves */
  464         (void)cpu_broadcast(&pmsBroadcastWait, pmsRemote, nstep);       /* Tell everyone else to do it too */
  465 
  466         return;
  467         
  468 }
  469 
  470 /*
  471  *      Receive a broadcast and react.
  472  *      This is called from the interprocessor signal handler.
  473  *      We wake up the initiator after we are finished.
  474  *
  475  */
  476         
  477 void pmsRemote(uint32_t nstep) {
  478 
  479         pmsRunLocal(nstep);                                                             /* Go set the step */
  480         if(!hw_atomic_sub(&pmsBroadcastWait, 1)) {              /* Drop the wait count */
  481                 thread_wakeup((event_t)&pmsBroadcastWait);      /* If we were the last, wake up the signaller */
  482         }
  483         return;
  484 }       
  485 
  486 
  487 /*
  488  *      Build the tables needed for the stepper.  This includes both the step definitions and the step control table.
  489  *
  490  *      We most absolutely need to be parked before this happens because we're gonna change the table.
  491  *      We're going to have to be pretty complete about checking for errors.
  492  *      Also, a copy is always made because we don't want to be crippled by not being able to change
  493  *      the table or description formats.
  494  *
  495  *      We pass in a table of external functions and the new stepper def uses the corresponding 
  496  *      indexes rather than actual function addresses.  This is done so that a proper table can be
  497  *      built with the control syscall.  It can't supply addresses, so the index has to do.  We
  498  *      internalize the table so our caller does not need to keep it.  Note that passing in a 0
  499  *      will use the current function table.  Also note that entry 0 is reserved and must be 0,
  500  *      we will check and fail the build.
  501  *
  502  *      The platformData parameter is a 32-bit word of data that is passed unaltered to the set function.
  503  *
  504  *      The queryFunc parameter is the address of a function that will return the current state of the platform.
  505  *      The format of the data returned is the same as the platform specific portions of pmsSetCmd, i.e., pmsXClk,
  506  *      pmsVoltage, and any part of pmsPowerID that is maintained by the platform hardware (an example would be
  507  *      the values of the gpios that correspond to pmsPowerID).  The value should be constructed by querying
  508  *      hardware rather than returning a value cached by software. One of the intents of this function is to 
  509  *      help recover lost or determine initial power states.
  510  *
  511  */
  512  
  513 kern_return_t pmsBuild(pmsDef *pd, uint32_t pdsize, pmsSetFunc_t *functab, uint32_t platformData, pmsQueryFunc_t queryFunc) {
  514 
  515         int steps, newsize, i, cstp, nstps, oldAltSize, xdsply;
  516         uint32_t setf;
  517         uint64_t nlimit;
  518         pmsDef *newpd, *oldAlt;
  519         boolean_t intr;
  520 
  521         xdsply = (pmsExperimental & 3) != 0;                    /* Turn on kprintfs if requested or in experimental mode */
  522 
  523         if(pdsize % sizeof(pmsDef)) return KERN_INVALID_ARGUMENT;       /* Length not multiple of definition size */
  524         
  525         steps = pdsize / sizeof(pmsDef);                                /* Get the number of steps supplied */
  526 
  527         if((steps >= pmsMaxStates) || (steps < pmsFree))        /* Complain if too big or too small */
  528                         return KERN_INVALID_ARGUMENT;                   /* Squeak loudly!!! */
  529                         
  530         if((uint32_t)functab && (uint32_t)functab[0])   /* Verify that if they supplied a new function table, entry 0 is 0 */
  531                 return KERN_INVALID_ARGUMENT;                           /* Fail because they didn't reserve entry 0 */
  532                         
  533         if(xdsply) kprintf("\n  StepID   Down   Next    HWSel  HWfun                Limit\n");
  534 
  535         for(i = 0; i < steps; i++) {                                    /* Step through and verify the definitions */
  536 
  537                 if(xdsply) kprintf("  %6d %6d %6d %08X %6d %20lld\n", pd[i].pmsStepID, pd[i].pmsDown, 
  538                         pd[i].pmsNext, pd[i].pmsSetCmd,
  539                         pd[i].sf.pmsSetFuncInd, pd[i].pmsLimit);
  540 
  541                 if((pd[i].pmsLimit != 0) && (pd[i].pmsLimit < 100ULL)) {
  542                         if(xdsply) kprintf("error step %3d: pmsLimit too small/n", i);
  543                         return KERN_INVALID_ARGUMENT;   /* Has to be 100µS or more */
  544                 }
  545                 
  546                 if((pd[i].pmsLimit != 0xFFFFFFFFFFFFFFFFULL) && (pd[i].pmsLimit > (HalfwayToForever / 1000ULL))) {
  547                         if(xdsply) kprintf("error step %3d: pmsLimit too big\n", i);
  548                         return KERN_INVALID_ARGUMENT;                   /* Can't be too big */
  549                 }
  550                 
  551                 if(pd[i].pmsStepID != i) {
  552                         if(xdsply) kprintf("error step %3d: step ID does not match (%d)\n", i, pd[i].pmsStepID);
  553                         return KERN_INVALID_ARGUMENT;   /* ID must match */
  554                 }
  555 
  556                 if(pd[i].sf.pmsSetFuncInd >= pmsSetFuncMax) {
  557                         if(xdsply) kprintf("error step %3d: function invalid (%d)\n", i, pd[i].sf.pmsSetFuncInd);
  558                         return KERN_INVALID_ARGUMENT;   /* Fail if this function is not in the table */
  559                 }
  560                 
  561                 if((pd[i].pmsDown != pmsParked) && pd[i].pmsDown >= steps) {
  562                         if(xdsply) kprintf("error step %3d: pmsDown out of range (%d)\n", i, pd[i].pmsDown);
  563                         return KERN_INVALID_ARGUMENT;   /* Step down must be in the table or park */
  564                 }
  565                 
  566                 if((pd[i].pmsNext != pmsParked) && pd[i].pmsNext >= steps) {
  567                         if(xdsply) kprintf("error step %3d: pmsNext out of range (%d)\n", i, pd[i].pmsNext);
  568                         return KERN_INVALID_ARGUMENT;   /* Step up must be in the table or park */
  569                 }
  570                 
  571                 if((pd[i].pmsSetCmd == pmsDelay) && (pd[i].pmsTDelay >= steps)) {
  572                         if(xdsply) kprintf("error step %3d: pmsTDelay out of range (%d)\n", i, pd[i].pmsTDelay);
  573                         return KERN_INVALID_ARGUMENT;   /* Delayed step must be in the table */
  574                 }
  575                 
  576                 if((pd[i].pmsSetCmd == pmsDelay) && (pd[i].pmsLimit == 0xFFFFFFFFFFFFFFFFULL)) {
  577                         if(xdsply) kprintf("error step %3d: delay time limit must not be infinite\n", i);
  578                         return KERN_INVALID_ARGUMENT;   /* Delayed step must have a time limit */
  579                 }
  580                 
  581         }
  582         
  583 /*
  584  *      Verify that there are no infinite synchronous forward loops in the table
  585  */
  586  
  587         if(xdsply) kprintf("\nInitial scan passed, start in loop check\n");
  588         for(i = 0; i < steps; i++) {                                    /* Start with each step. Inefficient, but who cares */
  589  
  590                 cstp = i;                                                                       /* Set starting point */
  591                 nstps = 0;                                                                      /* Initialize chain length counter */
  592                 while(1) {                                                                      /* Do until we hit the end */
  593                         if(pd[cstp].pmsSetCmd == pmsParkIt) break;      /* Parking always terminates a chain so no endless loop here */
  594                         if(pd[cstp].pmsSetCmd == pmsDelay) break;       /* Delayed steps always terminate a chain so no endless loop here */
  595                         if((pd[cstp].pmsLimit != 0) && ((pd[cstp].pmsSetCmd & pmsSync) != pmsSync)) break;      /* If time limit is not 0 and not synchrouous, no endless loop */
  596                         if(pd[cstp].pmsNext == pmsParked) break;        /* If the next step is parked, no endless loop */
  597                         
  598                         cstp = pd[cstp].pmsNext;                                /* Chain to the next */
  599                         nstps = nstps + 1;                                              /* Count this step */
  600                         if(nstps >= steps) {                                    /* We've stepped for more steps than we have, must be an endless loop! */
  601                                 if(xdsply) kprintf("error step %3d: infinite pmsNext loop\n", i);
  602                                 return KERN_INVALID_ARGUMENT;           /* Suggest to our caller that they can't program... */
  603                         }
  604                 }
  605         }
  606         
  607         if((pmsExperimental & 4) && (pmsInstalled) && ((uint32_t)functab != 0)) {       /* If we are already initted and experimental is locked in, and we are doing first */
  608                 if(xdsply) kprintf("Experimental locked, ignoring driver pmsBuild\n");
  609                 return KERN_RESOURCE_SHORTAGE;                          /* Just ignore the request. */
  610         }
  611         
  612         
  613         
  614 /*
  615  *      Well, things look ok, let's do it to it...
  616  */
  617 
  618         if(xdsply) kprintf("Loop check passed, building and installing table\n");
  619 
  620         newsize = steps * sizeof(pmsDef);                               /* Get the size needed for the definition blocks */
  621 
  622         if(!(newpd = (pmsDef *)kalloc(newsize))) {              /* Get memory for the whole thing */
  623                 return KERN_RESOURCE_SHORTAGE;                          /* No storage... */
  624         }
  625         
  626         bzero((void *)newpd, newsize);                                  /* Make it pretty */
  627         
  628 /*
  629  *      Ok, this is it, finish intitializing, switch the tables, and pray...
  630  *      We want no interruptions at all and we need to lock the table.  Everybody should be parked,
  631  *      so no one should ever touch this.  The lock is to keep multiple builders safe.  It probably
  632  *      will never ever happen, but paranoia is a good thing...
  633  */
  634  
  635         intr = ml_set_interrupts_enabled(FALSE);                /* No interruptions in here */
  636         simple_lock(&pmsBuildLock);                                             /* Lock out everyone... */
  637         
  638         if(platformData) pmsPlatformData = platformData;        /* Remember the platform data word passed in if any was... */
  639         if((uint32_t)queryFunc) pmsQueryFunc = queryFunc;       /* Remember the query function passed in, if it was... */
  640         
  641         oldAlt = altDpmsTab;                                                    /* Remember any old alternate we had */
  642         oldAltSize = altDpmsTabSize;                                    /* Remember its size */
  643 
  644         altDpmsTab = newpd;                                                             /* Point to the new table */
  645         altDpmsTabSize = newsize;                                               /* Set the size */
  646         
  647         if((uint32_t)functab) {                                                 /* Did we get a new function table? */
  648                 for(i = 0; i < pmsSetFuncMax; i++) pmsFuncTab[i] = functab[i];  /* Copy in the new table */
  649         }
  650 
  651         for(i = 0; i < pmsMaxStates; i++) pmsCtls.pmsDefs[i] = &pmsDummy;       /* Initialize the table to point to the dummy step */
  652 
  653         for(i = 0; i < steps; i++) {                                    /* Replace the step table entries */
  654                 if(pd[i].pmsLimit == 0xFFFFFFFFFFFFFFFFULL) nlimit = century;   /* Default to 100 years */
  655                 else nlimit = pd[i].pmsLimit;                           /* Otherwise use what was supplied */
  656                 
  657                 nanoseconds_to_absolutetime(nlimit * 1000ULL, &newpd[i].pmsLimit);      /* Convert microseconds to nanoseconds and then to ticks */
  658         
  659                 setf = pd[i].sf.pmsSetFuncInd;                                  /* Make convienient */
  660                 newpd[i].sf.pmsSetFunc = pmsFuncTab[setf];              /* Replace the index with the function address */
  661          
  662                 newpd[i].pmsStepID  = pd[i].pmsStepID;          /* Set the step ID */ 
  663                 newpd[i].pmsSetCmd  = pd[i].pmsSetCmd;          /* Set the hardware selector ID */
  664                 newpd[i].pmsDown    = pd[i].pmsDown;            /* Set the downward step */
  665                 newpd[i].pmsNext    = pd[i].pmsNext;            /* Set the next setp */
  666                 newpd[i].pmsTDelay  = pd[i].pmsTDelay;          /* Set the delayed setp */
  667                 pmsCtls.pmsDefs[i]  = &newpd[i];                        /* Copy it in */
  668         }
  669         
  670         pmsCtlp = (uint32_t)&pmsCtls;                                   /* Point to the new pms table */
  671         
  672         pmsInstalled = 1;                                                               /* The stepper has been born or born again... */
  673 
  674         simple_unlock(&pmsBuildLock);                                   /* Free play! */
  675         (void)ml_set_interrupts_enabled(intr);                  /* Interrupts back the way there were */
  676 
  677         if((uint32_t)oldAlt) kfree((vm_offset_t)oldAlt, oldAltSize);    /* If we already had an alternate, free it */
  678 
  679         if(xdsply) kprintf("Stepper table installed\n");
  680         
  681         return KERN_SUCCESS;                                                    /* We're in fate's hands now... */
  682 }

Cache object: 6114b923838480c0948126355ab3885c


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