2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_OSREFERENCE_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. The rights granted to you under the
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
30 #include <ppc/machine_routines.h>
31 #include <ppc/machine_cpu.h>
32 #include <ppc/exception.h>
33 #include <ppc/misc_protos.h>
34 #include <ppc/Firmware.h>
36 #include <ppc/proc_reg.h>
38 #include <ppc/savearea.h>
39 #include <ppc/exception.h>
40 #include <kern/processor.h>
42 extern int real_ncpus
;
44 static uint32_t pmsSyncrolator
= 0; /* Only one control operation at a time please */
45 uint32_t pmsBroadcastWait
= 0; /* Number of outstanding broadcasts */
47 int pmsInstalled
= 0; /* Power Management Stepper can run and has table installed */
48 int pmsExperimental
= 0; /* Power Management Stepper in experimental mode */
49 decl_simple_lock_data(,pmsBuildLock
) /* Make sure only one guy can replace table at the same time */
51 static pmsDef
*altDpmsTab
= 0; /* Alternate step definition table */
52 static uint32_t altDpmsTabSize
= 0; /* Size of alternate step definition table */
54 pmsDef pmsDummy
= { /* This is the dummy step for initialization. All it does is to park */
55 .pmsLimit
= 0, /* Time doesn't matter for a park */
56 .pmsStepID
= pmsMaxStates
- 1, /* Use the very last ID number for the dummy */
57 .pmsSetCmd
= pmsParkIt
, /* Force us to be parked */
58 .sf
.pmsSetFuncInd
= 0, /* No platform call for this one */
59 .pmsDown
= pmsPrepSleep
, /* We always park */
60 .pmsNext
= pmsPrepSleep
/* We always park */
63 pmsStat pmsStatsd
[4][pmsMaxStates
]; /* Generate enough statistics blocks for 4 processors */
65 pmsCtl pmsCtls
= { /* Power Management Stepper control */
66 .pmsStats
= &pmsStatsd
69 pmsSetFunc_t pmsFuncTab
[pmsSetFuncMax
] = {0}; /* This is the function index table */
70 pmsQueryFunc_t pmsQueryFunc
= 0; /* Pointer to pmsQuery function */
71 uint32_t pmsPlatformData
= 0; /* Data provided by and passed to platform functions */
75 * Do any initialization needed
82 simple_lock_init(&pmsBuildLock
, 0); /* Initialize the build lock */
83 for(i
= 0; i
< pmsMaxStates
; i
++) pmsCtls
.pmsDefs
[i
] = &pmsDummy
; /* Initialize the table to dummy steps */
90 * Start the power management stepper on all processors
92 * All processors must be parked. This should be called when the hardware
93 * is ready to step. Probably only at boot and after wake from sleep.
101 if(!pmsInstalled
) return; /* We can't do this if no table installed */
103 intr
= ml_set_interrupts_enabled(FALSE
); /* No interruptions in here */
104 pmsRun(pmsStartUp
); /* Start running the stepper everywhere */
105 (void)ml_set_interrupts_enabled(intr
); /* Restore interruptions */
113 * Park the stepper execution. This will force the stepper on this
114 * processor to abandon its current step and stop. No changes to the
115 * hardware state is made and any previous step is lost.
117 * This is used as the initial state at startup and when the step table
126 if(!pmsInstalled
) return; /* We can't do this if no table installed */
128 intr
= ml_set_interrupts_enabled(FALSE
); /* No interruptions in here */
129 pmsSetStep(pmsParked
, 0); /* Park the stepper */
130 (void)ml_set_interrupts_enabled(intr
); /* Restore interruptions */
138 * Steps down to a lower power.
139 * Interrupts must be off...
144 struct per_proc_info
*pp
;
147 pp
= getPerProc(); /* Get our per_proc */
149 if(!pmsInstalled
|| pp
->pms
.pmsState
== pmsParked
) return; /* No stepping if parked or not installed */
151 nstate
= pmsCtls
.pmsDefs
[pp
->pms
.pmsState
]->pmsDown
; /* Get the downward step */
152 pmsSetStep(nstate
, 0); /* Step to it */
158 * Steps up to a higher power. The "timer" parameter is true if the
159 * step was driven due to the pms timer expiring.
161 * Interrupts must be off...
164 void pmsStep(int timer
) {
166 struct per_proc_info
*pp
;
170 pp
= getPerProc(); /* Get our per_proc */
172 if(!pmsInstalled
|| pp
->pms
.pmsState
== pmsParked
) return; /* No stepping if parked or not installed */
174 nstate
= pmsCtls
.pmsDefs
[pp
->pms
.pmsState
]->pmsNext
; /* Assume a normal step */
175 dir
= 1; /* A normal step is a step up */
177 if(timer
&& (pmsCtls
.pmsDefs
[pp
->pms
.pmsState
]->pmsSetCmd
== pmsDelay
)) { /* If the timer expired and we are in a delay step, use the delay branch */
178 nstate
= pmsCtls
.pmsDefs
[pp
->pms
.pmsState
]->pmsTDelay
; /* Get the delayed step */
179 dir
= 0; /* Delayed steps are a step down for accounting purposes. */
182 pmsSetStep(nstate
, dir
); /* Step to it */
188 * Set a specific step
190 * We do not do statistics if exiting park
191 * Interrupts must be off...
195 void pmsSetStep(uint32_t nstep
, int dir
) {
197 struct per_proc_info
*pp
;
198 uint32_t pstate
, ret
, nCSetCmd
, mCSetCmd
;
199 pmsDef
*pnstate
, *pcstate
;
200 uint64_t tb
, nt
, dur
;
203 pp
= getPerProc(); /* Get our per_proc */
204 cpu
= cpu_number(); /* Get our processor */
206 while(1) { /* Keep stepping until we get a delay */
208 if(pp
->pms
.pmsCSetCmd
& pmsMustCmp
) { /* Do we have to finish the delay before changing? */
209 while(mach_absolute_time() < pp
->pms
.pmsPop
); /* Yes, spin here... */
212 if((nstep
== pmsParked
) || ((uint32_t)pmsCtls
.pmsDefs
[nstep
]->pmsSetCmd
== pmsParkIt
)) { /* Are we parking? */
214 tb
= mach_absolute_time(); /* What time is it? */
215 pp
->pms
.pmsStamp
= tb
; /* Show transition now */
216 pp
->pms
.pmsPop
= HalfwayToForever
; /* Set the pop way into the future */
217 pp
->pms
.pmsState
= pmsParked
; /* Make sure we are parked */
218 setTimerReq(); /* Cancel our timer if going */
222 pnstate
= pmsCtls
.pmsDefs
[nstep
]; /* Point to the state definition */
223 pstate
= pp
->pms
.pmsState
; /* Save the current step */
224 pp
->pms
.pmsState
= nstep
; /* Set the current to the next step */
226 if(pnstate
->pmsSetCmd
!= pmsDelay
) { /* If this is not a delayed state, change the actual hardware now */
227 if(pnstate
->pmsSetCmd
& pmsCngCPU
) pmsCPUSet(pnstate
->pmsSetCmd
); /* We have some CPU work to do... */
228 if((uint32_t)pnstate
->sf
.pmsSetFunc
) pnstate
->sf
.pmsSetFunc(pnstate
->pmsSetCmd
, cpu
, pmsPlatformData
); /* Tell the platform to set power mode */
230 mCSetCmd
= pnstate
->pmsSetCmd
& (pmsCngXClk
| pmsCngCPU
| pmsCngVolt
); /* Isolate just the change flags */
231 mCSetCmd
= (mCSetCmd
- (mCSetCmd
>> 7)) | pmsSync
| pmsMustCmp
| pmsPowerID
; /* Form mask of bits that come from new command */
232 nCSetCmd
= pp
->pms
.pmsCSetCmd
& ~mCSetCmd
; /* Clear changing bits */
233 nCSetCmd
= nCSetCmd
| (pnstate
->pmsSetCmd
& mCSetCmd
); /* Flip on the changing bits and the always copy bits */
235 pp
->pms
.pmsCSetCmd
= nCSetCmd
; /* Set it for real */
238 tb
= mach_absolute_time(); /* What time is it? */
239 pp
->pms
.pmsPop
= tb
+ pnstate
->pmsLimit
; /* Set the next pop */
241 if((pnstate
->pmsSetCmd
!= pmsDelay
) && (pp
->pms
.pmsCSetCmd
& pmsSync
) && (pnstate
->pmsLimit
!= 0)) { /* Is this a synchronous command with a delay? */
242 while(mach_absolute_time() < pp
->pms
.pmsPop
); /* Yes, spin here and wait it out... */
246 * Gather some statistics
249 dur
= tb
- pp
->pms
.pmsStamp
; /* Get the amount of time we were in the old step */
250 pp
->pms
.pmsStamp
= tb
; /* Set the new timestamp */
251 if(!(pstate
== pmsParked
)) { /* Only take stats if we were not parked */
252 pcstate
= pmsCtls
.pmsDefs
[pstate
]; /* Get the previous step */
253 pmsCtls
.pmsStats
[cpu
][pcstate
->pmsStepID
].stTime
[dir
] += dur
; /* Accumulate the total time in the old step */
254 pmsCtls
.pmsStats
[cpu
][pcstate
->pmsStepID
].stCnt
[dir
] += 1; /* Count transitions */
258 * See if we are done chaining steps
261 if((pnstate
->pmsSetCmd
== pmsDelay
)
262 || (!(pp
->pms
.pmsCSetCmd
& pmsSync
) && (pnstate
->pmsLimit
!= 0))) { /* Is this not syncronous and a non-zero delay or a delayed step? */
263 setTimerReq(); /* Start the timers ticking */
264 break; /* We've stepped as far as we're going to... */
267 nstep
= pnstate
->pmsNext
; /* Chain on to the next */
275 * Either park the stepper or force the step on a parked stepper for local processor only
279 void pmsRunLocal(uint32_t nstep
) {
281 struct per_proc_info
*pp
;
282 uint32_t cstate
, ret
, lastState
;
283 pmsDef
*pnstate
, *pcstate
;
284 uint64_t tb
, nt
, dur
;
288 if(!pmsInstalled
) return; /* Ignore this if no step programs installed... */
290 intr
= ml_set_interrupts_enabled(FALSE
); /* No interruptions in here */
292 pp
= getPerProc(); /* Get our per_proc */
294 if(nstep
== pmsStartUp
) { /* Should we start up? */
295 pmsCPUInit(); /* Get us up to full with high voltage and park */
296 nstep
= pmsNormHigh
; /* Change request to transition to normal high */
299 lastState
= pp
->pms
.pmsState
; /* Remember if we are parked now */
301 pmsSetStep(nstep
, 1); /* Step to the new state */
303 if((lastState
== pmsParked
) && (pp
->pms
.pmsState
!= pmsParked
)) { /* Did we just unpark? */
304 cpu
= cpu_number(); /* Get our processor */
305 for(i
= 0; i
< pmsMaxStates
; i
++) { /* Step through the steps and clear the statistics since we were parked */
306 pmsCtls
.pmsStats
[cpu
][i
].stTime
[0] = 0; /* Clear accumulated time - downward */
307 pmsCtls
.pmsStats
[cpu
][i
].stTime
[1] = 0; /* Clear accumulated time - forward */
308 pmsCtls
.pmsStats
[cpu
][i
].stCnt
[0] = 0; /* Clear transition count - downward */
309 pmsCtls
.pmsStats
[cpu
][i
].stCnt
[1] = 0; /* Clear transition count - forward */
313 (void)ml_set_interrupts_enabled(intr
); /* Restore interruptions */
320 * Control the Power Management Stepper.
321 * Called from user state by the superuser via a ppc system call.
322 * Interruptions disabled.
326 int pmsCntrl(struct savearea
*save
) {
328 uint32_t request
, nstep
, reqsize
, result
, presult
;
332 struct per_proc_info
*pp
;
334 pp
= getPerProc(); /* Get our per_proc */
335 cpu
= cpu_number(); /* Get our processor */
337 if(!is_suser()) { /* We are better than most, */
338 save
->save_r3
= KERN_FAILURE
; /* so we will only talk to the superuser. */
339 return 1; /* Turn up our noses, say "harrumph," and walk away... */
342 if(save
->save_r3
>= pmsCFree
) { /* Can we understand the request? */
343 save
->save_r3
= KERN_INVALID_ARGUMENT
; /* What language are these guys talking in, anyway? */
344 return 1; /* Cock head like a confused puppy and run away... */
347 request
= (int)save
->save_r3
; /* Remember the request */
348 reqsize
= (uint32_t)save
->save_r5
; /* Get the size of the config table */
350 if(request
== pmsCQuery
) { /* Are we just checking? */
351 result
= pmsCPUquery() & pmsCPU
; /* Get the processor data and make sure there is no slop */
352 presult
= 0; /* Assume nothing */
353 if((uint32_t)pmsQueryFunc
) presult
= pmsQueryFunc(cpu
, pmsPlatformData
); /* Go get the platform state */
354 result
= result
| (presult
& (pmsXClk
| pmsVoltage
| pmsPowerID
)); /* Merge the platform state with no slop */
355 save
->save_r3
= result
; /* Tell 'em... */
359 if(request
== pmsCExperimental
) { /* Enter experimental mode? */
361 if(pmsInstalled
|| (pmsExperimental
& 1)) { /* Are we already running or in experimental? */
362 save
->save_r3
= KERN_FAILURE
; /* Fail, since we are already running */
366 pmsExperimental
|= 1; /* Flip us into experimental but don't change other flags */
368 pmsCPUConf(); /* Configure for this machine */
369 pmsStart(); /* Start stepping */
370 save
->save_r3
= KERN_SUCCESS
; /* We are victorious... */
375 if(request
== pmsCCnfg
) { /* Do some up-front checking before we commit to doing this */
376 if((reqsize
> (pmsMaxStates
* sizeof(pmsDef
))) || (reqsize
< (pmsFree
* sizeof(pmsDef
)))) { /* Check that the size is reasonable */
377 save
->save_r3
= KERN_NO_SPACE
; /* Tell them that they messed up */
378 return 1; /* l8r... */
384 * We are committed after here. If there are any errors detected, we shouldn't die, but we
385 * will be stuck in park.
387 * Also, we can possibly end up on another processor after the broadcast.
391 if(!hw_compare_and_store(0, 1, &pmsSyncrolator
)) { /* Are we already doing this? */
392 save
->save_r3
= KERN_RESOURCE_SHORTAGE
; /* Tell them that we are already busy and to try again */
393 return 1; /* G'wan away and don't bother me... */
395 save
->save_r3
= KERN_SUCCESS
; /* Assume success */
397 // NOTE: We will block in the following code until everyone has finished the prepare
399 pmsRun(pmsPrepCng
); /* Get everyone parked and in a proper state for step table changes, including me */
401 if(request
== pmsCPark
) { /* Is all we're supposed to do park? */
402 pmsSyncrolator
= 0; /* Free us up */
403 return 1; /* Well, then we're done... */
406 switch(request
) { /* Select the routine */
408 case pmsCStart
: /* Starts normal steppping */
409 nstep
= pmsNormHigh
; /* Set the request */
412 case pmsCFLow
: /* Forces low power */
413 nstep
= pmsLow
; /* Set request */
416 case pmsCFHigh
: /* Forces high power */
417 nstep
= pmsHigh
; /* Set request */
420 case pmsCCnfg
: /* Loads new stepper program */
422 if(!(ndefs
= (pmsDef
*)kalloc(reqsize
))) { /* Get memory for the whole thing */
423 save
->save_r3
= KERN_INVALID_ADDRESS
; /* Return invalid address */
424 pmsSyncrolator
= 0; /* Free us up */
425 return 1; /* All done... */
428 ret
= copyin((user_addr_t
)((unsigned int)(save
->save_r4
)), (void *)ndefs
, reqsize
); /* Get the new config table */
429 if(ret
) { /* Hmmm, something went wrong with the copyin */
430 save
->save_r3
= KERN_INVALID_ADDRESS
; /* Return invalid address */
431 kfree((vm_offset_t
)ndefs
, reqsize
); /* Free up the copied in data */
432 pmsSyncrolator
= 0; /* Free us up */
433 return 1; /* All done... */
436 kret
= pmsBuild(ndefs
, reqsize
, 0, 0, 0); /* Go build and replace the tables. Make sure we keep the old platform stuff */
437 if(kret
) { /* Hmmm, something went wrong with the compilation */
438 save
->save_r3
= kret
; /* Pass back the passed back return code */
439 kfree((vm_offset_t
)ndefs
, reqsize
); /* Free up the copied in data */
440 pmsSyncrolator
= 0; /* Free us up */
441 return 1; /* All done... */
444 nstep
= pmsNormHigh
; /* Set the request */
448 panic("pmsCntrl: stepper control is so very, very confused = %08X\n", request
);
452 pmsRun(nstep
); /* Get everyone into step */
453 pmsSyncrolator
= 0; /* Free us up */
454 return 1; /* All done... */
459 * Broadcast a change to all processors including ourselves.
460 * This must transition before broadcasting because we may block and end up on a different processor.
462 * This will block until all processors have transitioned, so
463 * obviously, this can block.
465 * Called with interruptions disabled.
469 void pmsRun(uint32_t nstep
) {
471 pmsRunLocal(nstep
); /* If we aren't parking (we are already parked), transition ourselves */
472 (void)cpu_broadcast(&pmsBroadcastWait
, pmsRemote
, nstep
); /* Tell everyone else to do it too */
479 * Receive a broadcast and react.
480 * This is called from the interprocessor signal handler.
481 * We wake up the initiator after we are finished.
485 void pmsRemote(uint32_t nstep
) {
487 pmsRunLocal(nstep
); /* Go set the step */
488 if(!hw_atomic_sub(&pmsBroadcastWait
, 1)) { /* Drop the wait count */
489 thread_wakeup((event_t
)&pmsBroadcastWait
); /* If we were the last, wake up the signaller */
496 * Build the tables needed for the stepper. This includes both the step definitions and the step control table.
498 * We most absolutely need to be parked before this happens because we're gonna change the table.
499 * We're going to have to be pretty complete about checking for errors.
500 * Also, a copy is always made because we don't want to be crippled by not being able to change
501 * the table or description formats.
503 * We pass in a table of external functions and the new stepper def uses the corresponding
504 * indexes rather than actual function addresses. This is done so that a proper table can be
505 * built with the control syscall. It can't supply addresses, so the index has to do. We
506 * internalize the table so our caller does not need to keep it. Note that passing in a 0
507 * will use the current function table. Also note that entry 0 is reserved and must be 0,
508 * we will check and fail the build.
510 * The platformData parameter is a 32-bit word of data that is passed unaltered to the set function.
512 * The queryFunc parameter is the address of a function that will return the current state of the platform.
513 * The format of the data returned is the same as the platform specific portions of pmsSetCmd, i.e., pmsXClk,
514 * pmsVoltage, and any part of pmsPowerID that is maintained by the platform hardware (an example would be
515 * the values of the gpios that correspond to pmsPowerID). The value should be constructed by querying
516 * hardware rather than returning a value cached by software. One of the intents of this function is to
517 * help recover lost or determine initial power states.
521 kern_return_t
pmsBuild(pmsDef
*pd
, uint32_t pdsize
, pmsSetFunc_t
*functab
, uint32_t platformData
, pmsQueryFunc_t queryFunc
) {
523 int steps
, newsize
, i
, cstp
, nstps
, oldAltSize
, xdsply
;
526 pmsDef
*newpd
, *oldAlt
;
529 xdsply
= (pmsExperimental
& 3) != 0; /* Turn on kprintfs if requested or in experimental mode */
531 if(pdsize
% sizeof(pmsDef
)) return KERN_INVALID_ARGUMENT
; /* Length not multiple of definition size */
533 steps
= pdsize
/ sizeof(pmsDef
); /* Get the number of steps supplied */
535 if((steps
>= pmsMaxStates
) || (steps
< pmsFree
)) /* Complain if too big or too small */
536 return KERN_INVALID_ARGUMENT
; /* Squeak loudly!!! */
538 if((uint32_t)functab
&& (uint32_t)functab
[0]) /* Verify that if they supplied a new function table, entry 0 is 0 */
539 return KERN_INVALID_ARGUMENT
; /* Fail because they didn't reserve entry 0 */
541 if(xdsply
) kprintf("\n StepID Down Next HWSel HWfun Limit\n");
543 for(i
= 0; i
< steps
; i
++) { /* Step through and verify the definitions */
545 if(xdsply
) kprintf(" %6d %6d %6d %08X %6d %20lld\n", pd
[i
].pmsStepID
, pd
[i
].pmsDown
,
546 pd
[i
].pmsNext
, pd
[i
].pmsSetCmd
,
547 pd
[i
].sf
.pmsSetFuncInd
, pd
[i
].pmsLimit
);
549 if((pd
[i
].pmsLimit
!= 0) && (pd
[i
].pmsLimit
< 100ULL)) {
550 if(xdsply
) kprintf("error step %3d: pmsLimit too small/n", i
);
551 return KERN_INVALID_ARGUMENT
; /* Has to be 100µS or more */
554 if((pd
[i
].pmsLimit
!= 0xFFFFFFFFFFFFFFFFULL
) && (pd
[i
].pmsLimit
> (HalfwayToForever
/ 1000ULL))) {
555 if(xdsply
) kprintf("error step %3d: pmsLimit too big\n", i
);
556 return KERN_INVALID_ARGUMENT
; /* Can't be too big */
559 if(pd
[i
].pmsStepID
!= i
) {
560 if(xdsply
) kprintf("error step %3d: step ID does not match (%d)\n", i
, pd
[i
].pmsStepID
);
561 return KERN_INVALID_ARGUMENT
; /* ID must match */
564 if(pd
[i
].sf
.pmsSetFuncInd
>= pmsSetFuncMax
) {
565 if(xdsply
) kprintf("error step %3d: function invalid (%d)\n", i
, pd
[i
].sf
.pmsSetFuncInd
);
566 return KERN_INVALID_ARGUMENT
; /* Fail if this function is not in the table */
569 if((pd
[i
].pmsDown
!= pmsParked
) && pd
[i
].pmsDown
>= steps
) {
570 if(xdsply
) kprintf("error step %3d: pmsDown out of range (%d)\n", i
, pd
[i
].pmsDown
);
571 return KERN_INVALID_ARGUMENT
; /* Step down must be in the table or park */
574 if((pd
[i
].pmsNext
!= pmsParked
) && pd
[i
].pmsNext
>= steps
) {
575 if(xdsply
) kprintf("error step %3d: pmsNext out of range (%d)\n", i
, pd
[i
].pmsNext
);
576 return KERN_INVALID_ARGUMENT
; /* Step up must be in the table or park */
579 if((pd
[i
].pmsSetCmd
== pmsDelay
) && (pd
[i
].pmsTDelay
>= steps
)) {
580 if(xdsply
) kprintf("error step %3d: pmsTDelay out of range (%d)\n", i
, pd
[i
].pmsTDelay
);
581 return KERN_INVALID_ARGUMENT
; /* Delayed step must be in the table */
584 if((pd
[i
].pmsSetCmd
== pmsDelay
) && (pd
[i
].pmsLimit
== 0xFFFFFFFFFFFFFFFFULL
)) {
585 if(xdsply
) kprintf("error step %3d: delay time limit must not be infinite\n", i
);
586 return KERN_INVALID_ARGUMENT
; /* Delayed step must have a time limit */
592 * Verify that there are no infinite synchronous forward loops in the table
595 if(xdsply
) kprintf("\nInitial scan passed, start in loop check\n");
596 for(i
= 0; i
< steps
; i
++) { /* Start with each step. Inefficient, but who cares */
598 cstp
= i
; /* Set starting point */
599 nstps
= 0; /* Initialize chain length counter */
600 while(1) { /* Do until we hit the end */
601 if(pd
[cstp
].pmsSetCmd
== pmsParkIt
) break; /* Parking always terminates a chain so no endless loop here */
602 if(pd
[cstp
].pmsSetCmd
== pmsDelay
) break; /* Delayed steps always terminate a chain so no endless loop here */
603 if((pd
[cstp
].pmsLimit
!= 0) && ((pd
[cstp
].pmsSetCmd
& pmsSync
) != pmsSync
)) break; /* If time limit is not 0 and not synchrouous, no endless loop */
604 if(pd
[cstp
].pmsNext
== pmsParked
) break; /* If the next step is parked, no endless loop */
606 cstp
= pd
[cstp
].pmsNext
; /* Chain to the next */
607 nstps
= nstps
+ 1; /* Count this step */
608 if(nstps
>= steps
) { /* We've stepped for more steps than we have, must be an endless loop! */
609 if(xdsply
) kprintf("error step %3d: infinite pmsNext loop\n", i
);
610 return KERN_INVALID_ARGUMENT
; /* Suggest to our caller that they can't program... */
615 if((pmsExperimental
& 4) && (pmsInstalled
) && ((uint32_t)functab
!= 0)) { /* If we are already initted and experimental is locked in, and we are doing first */
616 if(xdsply
) kprintf("Experimental locked, ignoring driver pmsBuild\n");
617 return KERN_RESOURCE_SHORTAGE
; /* Just ignore the request. */
623 * Well, things look ok, let's do it to it...
626 if(xdsply
) kprintf("Loop check passed, building and installing table\n");
628 newsize
= steps
* sizeof(pmsDef
); /* Get the size needed for the definition blocks */
630 if(!(newpd
= (pmsDef
*)kalloc(newsize
))) { /* Get memory for the whole thing */
631 return KERN_RESOURCE_SHORTAGE
; /* No storage... */
634 bzero((void *)newpd
, newsize
); /* Make it pretty */
637 * Ok, this is it, finish intitializing, switch the tables, and pray...
638 * We want no interruptions at all and we need to lock the table. Everybody should be parked,
639 * so no one should ever touch this. The lock is to keep multiple builders safe. It probably
640 * will never ever happen, but paranoia is a good thing...
643 intr
= ml_set_interrupts_enabled(FALSE
); /* No interruptions in here */
644 simple_lock(&pmsBuildLock
); /* Lock out everyone... */
646 if(platformData
) pmsPlatformData
= platformData
; /* Remember the platform data word passed in if any was... */
647 if((uint32_t)queryFunc
) pmsQueryFunc
= queryFunc
; /* Remember the query function passed in, if it was... */
649 oldAlt
= altDpmsTab
; /* Remember any old alternate we had */
650 oldAltSize
= altDpmsTabSize
; /* Remember its size */
652 altDpmsTab
= newpd
; /* Point to the new table */
653 altDpmsTabSize
= newsize
; /* Set the size */
655 if((uint32_t)functab
) { /* Did we get a new function table? */
656 for(i
= 0; i
< pmsSetFuncMax
; i
++) pmsFuncTab
[i
] = functab
[i
]; /* Copy in the new table */
659 for(i
= 0; i
< pmsMaxStates
; i
++) pmsCtls
.pmsDefs
[i
] = &pmsDummy
; /* Initialize the table to point to the dummy step */
661 for(i
= 0; i
< steps
; i
++) { /* Replace the step table entries */
662 if(pd
[i
].pmsLimit
== 0xFFFFFFFFFFFFFFFFULL
) nlimit
= century
; /* Default to 100 years */
663 else nlimit
= pd
[i
].pmsLimit
; /* Otherwise use what was supplied */
665 nanoseconds_to_absolutetime(nlimit
* 1000ULL, &newpd
[i
].pmsLimit
); /* Convert microseconds to nanoseconds and then to ticks */
667 setf
= pd
[i
].sf
.pmsSetFuncInd
; /* Make convienient */
668 newpd
[i
].sf
.pmsSetFunc
= pmsFuncTab
[setf
]; /* Replace the index with the function address */
670 newpd
[i
].pmsStepID
= pd
[i
].pmsStepID
; /* Set the step ID */
671 newpd
[i
].pmsSetCmd
= pd
[i
].pmsSetCmd
; /* Set the hardware selector ID */
672 newpd
[i
].pmsDown
= pd
[i
].pmsDown
; /* Set the downward step */
673 newpd
[i
].pmsNext
= pd
[i
].pmsNext
; /* Set the next setp */
674 newpd
[i
].pmsTDelay
= pd
[i
].pmsTDelay
; /* Set the delayed setp */
675 pmsCtls
.pmsDefs
[i
] = &newpd
[i
]; /* Copy it in */
678 pmsCtlp
= (uint32_t)&pmsCtls
; /* Point to the new pms table */
680 pmsInstalled
= 1; /* The stepper has been born or born again... */
682 simple_unlock(&pmsBuildLock
); /* Free play! */
683 (void)ml_set_interrupts_enabled(intr
); /* Interrupts back the way there were */
685 if((uint32_t)oldAlt
) kfree((vm_offset_t
)oldAlt
, oldAltSize
); /* If we already had an alternate, free it */
687 if(xdsply
) kprintf("Stepper table installed\n");
689 return KERN_SUCCESS
; /* We're in fate's hands now... */