2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
23 #include <ppc/machine_routines.h>
24 #include <ppc/machine_cpu.h>
25 #include <ppc/exception.h>
26 #include <ppc/misc_protos.h>
27 #include <ppc/Firmware.h>
29 #include <ppc/proc_reg.h>
31 #include <ppc/savearea.h>
32 #include <ppc/exception.h>
33 #include <kern/processor.h>
35 extern int real_ncpus
;
37 static uint32_t pmsSyncrolator
= 0; /* Only one control operation at a time please */
38 uint32_t pmsBroadcastWait
= 0; /* Number of outstanding broadcasts */
40 int pmsInstalled
= 0; /* Power Management Stepper can run and has table installed */
41 int pmsExperimental
= 0; /* Power Management Stepper in experimental mode */
42 decl_simple_lock_data(,pmsBuildLock
) /* Make sure only one guy can replace table at the same time */
44 static pmsDef
*altDpmsTab
= 0; /* Alternate step definition table */
45 static uint32_t altDpmsTabSize
= 0; /* Size of alternate step definition table */
47 pmsDef pmsDummy
= { /* This is the dummy step for initialization. All it does is to park */
48 .pmsLimit
= 0, /* Time doesn't matter for a park */
49 .pmsStepID
= pmsMaxStates
- 1, /* Use the very last ID number for the dummy */
50 .pmsSetCmd
= pmsParkIt
, /* Force us to be parked */
51 .sf
.pmsSetFuncInd
= 0, /* No platform call for this one */
52 .pmsDown
= pmsPrepSleep
, /* We always park */
53 .pmsNext
= pmsPrepSleep
/* We always park */
56 pmsStat pmsStatsd
[4][pmsMaxStates
]; /* Generate enough statistics blocks for 4 processors */
58 pmsCtl pmsCtls
= { /* Power Management Stepper control */
59 .pmsStats
= &pmsStatsd
62 pmsSetFunc_t pmsFuncTab
[pmsSetFuncMax
] = {0}; /* This is the function index table */
63 pmsQueryFunc_t pmsQueryFunc
= 0; /* Pointer to pmsQuery function */
64 uint32_t pmsPlatformData
= 0; /* Data provided by and passed to platform functions */
68 * Do any initialization needed
75 simple_lock_init(&pmsBuildLock
, 0); /* Initialize the build lock */
76 for(i
= 0; i
< pmsMaxStates
; i
++) pmsCtls
.pmsDefs
[i
] = &pmsDummy
; /* Initialize the table to dummy steps */
83 * Start the power management stepper on all processors
85 * All processors must be parked. This should be called when the hardware
86 * is ready to step. Probably only at boot and after wake from sleep.
94 if(!pmsInstalled
) return; /* We can't do this if no table installed */
96 intr
= ml_set_interrupts_enabled(FALSE
); /* No interruptions in here */
97 pmsRun(pmsStartUp
); /* Start running the stepper everywhere */
98 (void)ml_set_interrupts_enabled(intr
); /* Restore interruptions */
106 * Park the stepper execution. This will force the stepper on this
107 * processor to abandon its current step and stop. No changes to the
108 * hardware state is made and any previous step is lost.
110 * This is used as the initial state at startup and when the step table
119 if(!pmsInstalled
) return; /* We can't do this if no table installed */
121 intr
= ml_set_interrupts_enabled(FALSE
); /* No interruptions in here */
122 pmsSetStep(pmsParked
, 0); /* Park the stepper */
123 (void)ml_set_interrupts_enabled(intr
); /* Restore interruptions */
131 * Steps down to a lower power.
132 * Interrupts must be off...
137 struct per_proc_info
*pp
;
140 pp
= getPerProc(); /* Get our per_proc */
142 if(!pmsInstalled
|| pp
->pms
.pmsState
== pmsParked
) return; /* No stepping if parked or not installed */
144 nstate
= pmsCtls
.pmsDefs
[pp
->pms
.pmsState
]->pmsDown
; /* Get the downward step */
145 pmsSetStep(nstate
, 0); /* Step to it */
151 * Steps up to a higher power. The "timer" parameter is true if the
152 * step was driven due to the pms timer expiring.
154 * Interrupts must be off...
157 void pmsStep(int timer
) {
159 struct per_proc_info
*pp
;
163 pp
= getPerProc(); /* Get our per_proc */
165 if(!pmsInstalled
|| pp
->pms
.pmsState
== pmsParked
) return; /* No stepping if parked or not installed */
167 nstate
= pmsCtls
.pmsDefs
[pp
->pms
.pmsState
]->pmsNext
; /* Assume a normal step */
168 dir
= 1; /* A normal step is a step up */
170 if(timer
&& (pmsCtls
.pmsDefs
[pp
->pms
.pmsState
]->pmsSetCmd
== pmsDelay
)) { /* If the timer expired and we are in a delay step, use the delay branch */
171 nstate
= pmsCtls
.pmsDefs
[pp
->pms
.pmsState
]->pmsTDelay
; /* Get the delayed step */
172 dir
= 0; /* Delayed steps are a step down for accounting purposes. */
175 pmsSetStep(nstate
, dir
); /* Step to it */
181 * Set a specific step
183 * We do not do statistics if exiting park
184 * Interrupts must be off...
188 void pmsSetStep(uint32_t nstep
, int dir
) {
190 struct per_proc_info
*pp
;
191 uint32_t pstate
, ret
, nCSetCmd
, mCSetCmd
;
192 pmsDef
*pnstate
, *pcstate
;
193 uint64_t tb
, nt
, dur
;
196 pp
= getPerProc(); /* Get our per_proc */
197 cpu
= cpu_number(); /* Get our processor */
199 while(1) { /* Keep stepping until we get a delay */
201 if(pp
->pms
.pmsCSetCmd
& pmsMustCmp
) { /* Do we have to finish the delay before changing? */
202 while(mach_absolute_time() < pp
->pms
.pmsPop
); /* Yes, spin here... */
205 if((nstep
== pmsParked
) || ((uint32_t)pmsCtls
.pmsDefs
[nstep
]->pmsSetCmd
== pmsParkIt
)) { /* Are we parking? */
207 tb
= mach_absolute_time(); /* What time is it? */
208 pp
->pms
.pmsStamp
= tb
; /* Show transition now */
209 pp
->pms
.pmsPop
= HalfwayToForever
; /* Set the pop way into the future */
210 pp
->pms
.pmsState
= pmsParked
; /* Make sure we are parked */
211 setTimerReq(); /* Cancel our timer if going */
215 pnstate
= pmsCtls
.pmsDefs
[nstep
]; /* Point to the state definition */
216 pstate
= pp
->pms
.pmsState
; /* Save the current step */
217 pp
->pms
.pmsState
= nstep
; /* Set the current to the next step */
219 if(pnstate
->pmsSetCmd
!= pmsDelay
) { /* If this is not a delayed state, change the actual hardware now */
220 if(pnstate
->pmsSetCmd
& pmsCngCPU
) pmsCPUSet(pnstate
->pmsSetCmd
); /* We have some CPU work to do... */
221 if((uint32_t)pnstate
->sf
.pmsSetFunc
) pnstate
->sf
.pmsSetFunc(pnstate
->pmsSetCmd
, cpu
, pmsPlatformData
); /* Tell the platform to set power mode */
223 mCSetCmd
= pnstate
->pmsSetCmd
& (pmsCngXClk
| pmsCngCPU
| pmsCngVolt
); /* Isolate just the change flags */
224 mCSetCmd
= (mCSetCmd
- (mCSetCmd
>> 7)) | pmsSync
| pmsMustCmp
| pmsPowerID
; /* Form mask of bits that come from new command */
225 nCSetCmd
= pp
->pms
.pmsCSetCmd
& ~mCSetCmd
; /* Clear changing bits */
226 nCSetCmd
= nCSetCmd
| (pnstate
->pmsSetCmd
& mCSetCmd
); /* Flip on the changing bits and the always copy bits */
228 pp
->pms
.pmsCSetCmd
= nCSetCmd
; /* Set it for real */
231 tb
= mach_absolute_time(); /* What time is it? */
232 pp
->pms
.pmsPop
= tb
+ pnstate
->pmsLimit
; /* Set the next pop */
234 if((pnstate
->pmsSetCmd
!= pmsDelay
) && (pp
->pms
.pmsCSetCmd
& pmsSync
) && (pnstate
->pmsLimit
!= 0)) { /* Is this a synchronous command with a delay? */
235 while(mach_absolute_time() < pp
->pms
.pmsPop
); /* Yes, spin here and wait it out... */
239 * Gather some statistics
242 dur
= tb
- pp
->pms
.pmsStamp
; /* Get the amount of time we were in the old step */
243 pp
->pms
.pmsStamp
= tb
; /* Set the new timestamp */
244 if(!(pstate
== pmsParked
)) { /* Only take stats if we were not parked */
245 pcstate
= pmsCtls
.pmsDefs
[pstate
]; /* Get the previous step */
246 pmsCtls
.pmsStats
[cpu
][pcstate
->pmsStepID
].stTime
[dir
] += dur
; /* Accumulate the total time in the old step */
247 pmsCtls
.pmsStats
[cpu
][pcstate
->pmsStepID
].stCnt
[dir
] += 1; /* Count transitions */
251 * See if we are done chaining steps
254 if((pnstate
->pmsSetCmd
== pmsDelay
)
255 || (!(pp
->pms
.pmsCSetCmd
& pmsSync
) && (pnstate
->pmsLimit
!= 0))) { /* Is this not syncronous and a non-zero delay or a delayed step? */
256 setTimerReq(); /* Start the timers ticking */
257 break; /* We've stepped as far as we're going to... */
260 nstep
= pnstate
->pmsNext
; /* Chain on to the next */
268 * Either park the stepper or force the step on a parked stepper for local processor only
272 void pmsRunLocal(uint32_t nstep
) {
274 struct per_proc_info
*pp
;
275 uint32_t cstate
, ret
, lastState
;
276 pmsDef
*pnstate
, *pcstate
;
277 uint64_t tb
, nt
, dur
;
281 if(!pmsInstalled
) return; /* Ignore this if no step programs installed... */
283 intr
= ml_set_interrupts_enabled(FALSE
); /* No interruptions in here */
285 pp
= getPerProc(); /* Get our per_proc */
287 if(nstep
== pmsStartUp
) { /* Should we start up? */
288 pmsCPUInit(); /* Get us up to full with high voltage and park */
289 nstep
= pmsNormHigh
; /* Change request to transition to normal high */
292 lastState
= pp
->pms
.pmsState
; /* Remember if we are parked now */
294 pmsSetStep(nstep
, 1); /* Step to the new state */
296 if((lastState
== pmsParked
) && (pp
->pms
.pmsState
!= pmsParked
)) { /* Did we just unpark? */
297 cpu
= cpu_number(); /* Get our processor */
298 for(i
= 0; i
< pmsMaxStates
; i
++) { /* Step through the steps and clear the statistics since we were parked */
299 pmsCtls
.pmsStats
[cpu
][i
].stTime
[0] = 0; /* Clear accumulated time - downward */
300 pmsCtls
.pmsStats
[cpu
][i
].stTime
[1] = 0; /* Clear accumulated time - forward */
301 pmsCtls
.pmsStats
[cpu
][i
].stCnt
[0] = 0; /* Clear transition count - downward */
302 pmsCtls
.pmsStats
[cpu
][i
].stCnt
[1] = 0; /* Clear transition count - forward */
306 (void)ml_set_interrupts_enabled(intr
); /* Restore interruptions */
313 * Control the Power Management Stepper.
314 * Called from user state by the superuser via a ppc system call.
315 * Interruptions disabled.
319 int pmsCntrl(struct savearea
*save
) {
321 uint32_t request
, nstep
, reqsize
, result
, presult
;
325 struct per_proc_info
*pp
;
327 pp
= getPerProc(); /* Get our per_proc */
328 cpu
= cpu_number(); /* Get our processor */
330 if(!is_suser()) { /* We are better than most, */
331 save
->save_r3
= KERN_FAILURE
; /* so we will only talk to the superuser. */
332 return 1; /* Turn up our noses, say "harrumph," and walk away... */
335 if(save
->save_r3
>= pmsCFree
) { /* Can we understand the request? */
336 save
->save_r3
= KERN_INVALID_ARGUMENT
; /* What language are these guys talking in, anyway? */
337 return 1; /* Cock head like a confused puppy and run away... */
340 request
= (int)save
->save_r3
; /* Remember the request */
341 reqsize
= (uint32_t)save
->save_r5
; /* Get the size of the config table */
343 if(request
== pmsCQuery
) { /* Are we just checking? */
344 result
= pmsCPUquery() & pmsCPU
; /* Get the processor data and make sure there is no slop */
345 presult
= 0; /* Assume nothing */
346 if((uint32_t)pmsQueryFunc
) presult
= pmsQueryFunc(cpu
, pmsPlatformData
); /* Go get the platform state */
347 result
= result
| (presult
& (pmsXClk
| pmsVoltage
| pmsPowerID
)); /* Merge the platform state with no slop */
348 save
->save_r3
= result
; /* Tell 'em... */
352 if(request
== pmsCExperimental
) { /* Enter experimental mode? */
354 if(pmsInstalled
|| (pmsExperimental
& 1)) { /* Are we already running or in experimental? */
355 save
->save_r3
= KERN_FAILURE
; /* Fail, since we are already running */
359 pmsExperimental
|= 1; /* Flip us into experimental but don't change other flags */
361 pmsCPUConf(); /* Configure for this machine */
362 pmsStart(); /* Start stepping */
363 save
->save_r3
= KERN_SUCCESS
; /* We are victorious... */
368 if(request
== pmsCCnfg
) { /* Do some up-front checking before we commit to doing this */
369 if((reqsize
> (pmsMaxStates
* sizeof(pmsDef
))) || (reqsize
< (pmsFree
* sizeof(pmsDef
)))) { /* Check that the size is reasonable */
370 save
->save_r3
= KERN_NO_SPACE
; /* Tell them that they messed up */
371 return 1; /* l8r... */
377 * We are committed after here. If there are any errors detected, we shouldn't die, but we
378 * will be stuck in park.
380 * Also, we can possibly end up on another processor after the broadcast.
384 if(!hw_compare_and_store(0, 1, &pmsSyncrolator
)) { /* Are we already doing this? */
385 save
->save_r3
= KERN_RESOURCE_SHORTAGE
; /* Tell them that we are already busy and to try again */
386 return 1; /* G'wan away and don't bother me... */
388 save
->save_r3
= KERN_SUCCESS
; /* Assume success */
390 // NOTE: We will block in the following code until everyone has finished the prepare
392 pmsRun(pmsPrepCng
); /* Get everyone parked and in a proper state for step table changes, including me */
394 if(request
== pmsCPark
) { /* Is all we're supposed to do park? */
395 pmsSyncrolator
= 0; /* Free us up */
396 return 1; /* Well, then we're done... */
399 switch(request
) { /* Select the routine */
401 case pmsCStart
: /* Starts normal steppping */
402 nstep
= pmsNormHigh
; /* Set the request */
405 case pmsCFLow
: /* Forces low power */
406 nstep
= pmsLow
; /* Set request */
409 case pmsCFHigh
: /* Forces high power */
410 nstep
= pmsHigh
; /* Set request */
413 case pmsCCnfg
: /* Loads new stepper program */
415 if(!(ndefs
= (pmsDef
*)kalloc(reqsize
))) { /* Get memory for the whole thing */
416 save
->save_r3
= KERN_INVALID_ADDRESS
; /* Return invalid address */
417 pmsSyncrolator
= 0; /* Free us up */
418 return 1; /* All done... */
421 ret
= copyin((user_addr_t
)((unsigned int)(save
->save_r4
)), (void *)ndefs
, reqsize
); /* Get the new config table */
422 if(ret
) { /* Hmmm, something went wrong with the copyin */
423 save
->save_r3
= KERN_INVALID_ADDRESS
; /* Return invalid address */
424 kfree((vm_offset_t
)ndefs
, reqsize
); /* Free up the copied in data */
425 pmsSyncrolator
= 0; /* Free us up */
426 return 1; /* All done... */
429 kret
= pmsBuild(ndefs
, reqsize
, 0, 0, 0); /* Go build and replace the tables. Make sure we keep the old platform stuff */
430 if(kret
) { /* Hmmm, something went wrong with the compilation */
431 save
->save_r3
= kret
; /* Pass back the passed back return code */
432 kfree((vm_offset_t
)ndefs
, reqsize
); /* Free up the copied in data */
433 pmsSyncrolator
= 0; /* Free us up */
434 return 1; /* All done... */
437 nstep
= pmsNormHigh
; /* Set the request */
441 panic("pmsCntrl: stepper control is so very, very confused = %08X\n", request
);
445 pmsRun(nstep
); /* Get everyone into step */
446 pmsSyncrolator
= 0; /* Free us up */
447 return 1; /* All done... */
452 * Broadcast a change to all processors including ourselves.
453 * This must transition before broadcasting because we may block and end up on a different processor.
455 * This will block until all processors have transitioned, so
456 * obviously, this can block.
458 * Called with interruptions disabled.
462 void pmsRun(uint32_t nstep
) {
464 pmsRunLocal(nstep
); /* If we aren't parking (we are already parked), transition ourselves */
465 (void)cpu_broadcast(&pmsBroadcastWait
, pmsRemote
, nstep
); /* Tell everyone else to do it too */
472 * Receive a broadcast and react.
473 * This is called from the interprocessor signal handler.
474 * We wake up the initiator after we are finished.
478 void pmsRemote(uint32_t nstep
) {
480 pmsRunLocal(nstep
); /* Go set the step */
481 if(!hw_atomic_sub(&pmsBroadcastWait
, 1)) { /* Drop the wait count */
482 thread_wakeup((event_t
)&pmsBroadcastWait
); /* If we were the last, wake up the signaller */
489 * Build the tables needed for the stepper. This includes both the step definitions and the step control table.
491 * We most absolutely need to be parked before this happens because we're gonna change the table.
492 * We're going to have to be pretty complete about checking for errors.
493 * Also, a copy is always made because we don't want to be crippled by not being able to change
494 * the table or description formats.
496 * We pass in a table of external functions and the new stepper def uses the corresponding
497 * indexes rather than actual function addresses. This is done so that a proper table can be
498 * built with the control syscall. It can't supply addresses, so the index has to do. We
499 * internalize the table so our caller does not need to keep it. Note that passing in a 0
500 * will use the current function table. Also note that entry 0 is reserved and must be 0,
501 * we will check and fail the build.
503 * The platformData parameter is a 32-bit word of data that is passed unaltered to the set function.
505 * The queryFunc parameter is the address of a function that will return the current state of the platform.
506 * The format of the data returned is the same as the platform specific portions of pmsSetCmd, i.e., pmsXClk,
507 * pmsVoltage, and any part of pmsPowerID that is maintained by the platform hardware (an example would be
508 * the values of the gpios that correspond to pmsPowerID). The value should be constructed by querying
509 * hardware rather than returning a value cached by software. One of the intents of this function is to
510 * help recover lost or determine initial power states.
514 kern_return_t
pmsBuild(pmsDef
*pd
, uint32_t pdsize
, pmsSetFunc_t
*functab
, uint32_t platformData
, pmsQueryFunc_t queryFunc
) {
516 int steps
, newsize
, i
, cstp
, nstps
, oldAltSize
, xdsply
;
519 pmsDef
*newpd
, *oldAlt
;
522 xdsply
= (pmsExperimental
& 3) != 0; /* Turn on kprintfs if requested or in experimental mode */
524 if(pdsize
% sizeof(pmsDef
)) return KERN_INVALID_ARGUMENT
; /* Length not multiple of definition size */
526 steps
= pdsize
/ sizeof(pmsDef
); /* Get the number of steps supplied */
528 if((steps
>= pmsMaxStates
) || (steps
< pmsFree
)) /* Complain if too big or too small */
529 return KERN_INVALID_ARGUMENT
; /* Squeak loudly!!! */
531 if((uint32_t)functab
&& (uint32_t)functab
[0]) /* Verify that if they supplied a new function table, entry 0 is 0 */
532 return KERN_INVALID_ARGUMENT
; /* Fail because they didn't reserve entry 0 */
534 if(xdsply
) kprintf("\n StepID Down Next HWSel HWfun Limit\n");
536 for(i
= 0; i
< steps
; i
++) { /* Step through and verify the definitions */
538 if(xdsply
) kprintf(" %6d %6d %6d %08X %6d %20lld\n", pd
[i
].pmsStepID
, pd
[i
].pmsDown
,
539 pd
[i
].pmsNext
, pd
[i
].pmsSetCmd
,
540 pd
[i
].sf
.pmsSetFuncInd
, pd
[i
].pmsLimit
);
542 if((pd
[i
].pmsLimit
!= 0) && (pd
[i
].pmsLimit
< 100ULL)) {
543 if(xdsply
) kprintf("error step %3d: pmsLimit too small/n", i
);
544 return KERN_INVALID_ARGUMENT
; /* Has to be 100µS or more */
547 if((pd
[i
].pmsLimit
!= 0xFFFFFFFFFFFFFFFFULL
) && (pd
[i
].pmsLimit
> (HalfwayToForever
/ 1000ULL))) {
548 if(xdsply
) kprintf("error step %3d: pmsLimit too big\n", i
);
549 return KERN_INVALID_ARGUMENT
; /* Can't be too big */
552 if(pd
[i
].pmsStepID
!= i
) {
553 if(xdsply
) kprintf("error step %3d: step ID does not match (%d)\n", i
, pd
[i
].pmsStepID
);
554 return KERN_INVALID_ARGUMENT
; /* ID must match */
557 if(pd
[i
].sf
.pmsSetFuncInd
>= pmsSetFuncMax
) {
558 if(xdsply
) kprintf("error step %3d: function invalid (%d)\n", i
, pd
[i
].sf
.pmsSetFuncInd
);
559 return KERN_INVALID_ARGUMENT
; /* Fail if this function is not in the table */
562 if((pd
[i
].pmsDown
!= pmsParked
) && pd
[i
].pmsDown
>= steps
) {
563 if(xdsply
) kprintf("error step %3d: pmsDown out of range (%d)\n", i
, pd
[i
].pmsDown
);
564 return KERN_INVALID_ARGUMENT
; /* Step down must be in the table or park */
567 if((pd
[i
].pmsNext
!= pmsParked
) && pd
[i
].pmsNext
>= steps
) {
568 if(xdsply
) kprintf("error step %3d: pmsNext out of range (%d)\n", i
, pd
[i
].pmsNext
);
569 return KERN_INVALID_ARGUMENT
; /* Step up must be in the table or park */
572 if((pd
[i
].pmsSetCmd
== pmsDelay
) && (pd
[i
].pmsTDelay
>= steps
)) {
573 if(xdsply
) kprintf("error step %3d: pmsTDelay out of range (%d)\n", i
, pd
[i
].pmsTDelay
);
574 return KERN_INVALID_ARGUMENT
; /* Delayed step must be in the table */
577 if((pd
[i
].pmsSetCmd
== pmsDelay
) && (pd
[i
].pmsLimit
== 0xFFFFFFFFFFFFFFFFULL
)) {
578 if(xdsply
) kprintf("error step %3d: delay time limit must not be infinite\n", i
);
579 return KERN_INVALID_ARGUMENT
; /* Delayed step must have a time limit */
585 * Verify that there are no infinite synchronous forward loops in the table
588 if(xdsply
) kprintf("\nInitial scan passed, start in loop check\n");
589 for(i
= 0; i
< steps
; i
++) { /* Start with each step. Inefficient, but who cares */
591 cstp
= i
; /* Set starting point */
592 nstps
= 0; /* Initialize chain length counter */
593 while(1) { /* Do until we hit the end */
594 if(pd
[cstp
].pmsSetCmd
== pmsParkIt
) break; /* Parking always terminates a chain so no endless loop here */
595 if(pd
[cstp
].pmsSetCmd
== pmsDelay
) break; /* Delayed steps always terminate a chain so no endless loop here */
596 if((pd
[cstp
].pmsLimit
!= 0) && ((pd
[cstp
].pmsSetCmd
& pmsSync
) != pmsSync
)) break; /* If time limit is not 0 and not synchrouous, no endless loop */
597 if(pd
[cstp
].pmsNext
== pmsParked
) break; /* If the next step is parked, no endless loop */
599 cstp
= pd
[cstp
].pmsNext
; /* Chain to the next */
600 nstps
= nstps
+ 1; /* Count this step */
601 if(nstps
>= steps
) { /* We've stepped for more steps than we have, must be an endless loop! */
602 if(xdsply
) kprintf("error step %3d: infinite pmsNext loop\n", i
);
603 return KERN_INVALID_ARGUMENT
; /* Suggest to our caller that they can't program... */
608 if((pmsExperimental
& 4) && (pmsInstalled
) && ((uint32_t)functab
!= 0)) { /* If we are already initted and experimental is locked in, and we are doing first */
609 if(xdsply
) kprintf("Experimental locked, ignoring driver pmsBuild\n");
610 return KERN_RESOURCE_SHORTAGE
; /* Just ignore the request. */
616 * Well, things look ok, let's do it to it...
619 if(xdsply
) kprintf("Loop check passed, building and installing table\n");
621 newsize
= steps
* sizeof(pmsDef
); /* Get the size needed for the definition blocks */
623 if(!(newpd
= (pmsDef
*)kalloc(newsize
))) { /* Get memory for the whole thing */
624 return KERN_RESOURCE_SHORTAGE
; /* No storage... */
627 bzero((void *)newpd
, newsize
); /* Make it pretty */
630 * Ok, this is it, finish intitializing, switch the tables, and pray...
631 * We want no interruptions at all and we need to lock the table. Everybody should be parked,
632 * so no one should ever touch this. The lock is to keep multiple builders safe. It probably
633 * will never ever happen, but paranoia is a good thing...
636 intr
= ml_set_interrupts_enabled(FALSE
); /* No interruptions in here */
637 simple_lock(&pmsBuildLock
); /* Lock out everyone... */
639 if(platformData
) pmsPlatformData
= platformData
; /* Remember the platform data word passed in if any was... */
640 if((uint32_t)queryFunc
) pmsQueryFunc
= queryFunc
; /* Remember the query function passed in, if it was... */
642 oldAlt
= altDpmsTab
; /* Remember any old alternate we had */
643 oldAltSize
= altDpmsTabSize
; /* Remember its size */
645 altDpmsTab
= newpd
; /* Point to the new table */
646 altDpmsTabSize
= newsize
; /* Set the size */
648 if((uint32_t)functab
) { /* Did we get a new function table? */
649 for(i
= 0; i
< pmsSetFuncMax
; i
++) pmsFuncTab
[i
] = functab
[i
]; /* Copy in the new table */
652 for(i
= 0; i
< pmsMaxStates
; i
++) pmsCtls
.pmsDefs
[i
] = &pmsDummy
; /* Initialize the table to point to the dummy step */
654 for(i
= 0; i
< steps
; i
++) { /* Replace the step table entries */
655 if(pd
[i
].pmsLimit
== 0xFFFFFFFFFFFFFFFFULL
) nlimit
= century
; /* Default to 100 years */
656 else nlimit
= pd
[i
].pmsLimit
; /* Otherwise use what was supplied */
658 nanoseconds_to_absolutetime(nlimit
* 1000ULL, &newpd
[i
].pmsLimit
); /* Convert microseconds to nanoseconds and then to ticks */
660 setf
= pd
[i
].sf
.pmsSetFuncInd
; /* Make convienient */
661 newpd
[i
].sf
.pmsSetFunc
= pmsFuncTab
[setf
]; /* Replace the index with the function address */
663 newpd
[i
].pmsStepID
= pd
[i
].pmsStepID
; /* Set the step ID */
664 newpd
[i
].pmsSetCmd
= pd
[i
].pmsSetCmd
; /* Set the hardware selector ID */
665 newpd
[i
].pmsDown
= pd
[i
].pmsDown
; /* Set the downward step */
666 newpd
[i
].pmsNext
= pd
[i
].pmsNext
; /* Set the next setp */
667 newpd
[i
].pmsTDelay
= pd
[i
].pmsTDelay
; /* Set the delayed setp */
668 pmsCtls
.pmsDefs
[i
] = &newpd
[i
]; /* Copy it in */
671 pmsCtlp
= (uint32_t)&pmsCtls
; /* Point to the new pms table */
673 pmsInstalled
= 1; /* The stepper has been born or born again... */
675 simple_unlock(&pmsBuildLock
); /* Free play! */
676 (void)ml_set_interrupts_enabled(intr
); /* Interrupts back the way there were */
678 if((uint32_t)oldAlt
) kfree((vm_offset_t
)oldAlt
, oldAltSize
); /* If we already had an alternate, free it */
680 if(xdsply
) kprintf("Stepper table installed\n");
682 return KERN_SUCCESS
; /* We're in fate's hands now... */