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