2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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.
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
20 * @APPLE_LICENSE_HEADER_END@
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>
28 #include <ppc/proc_reg.h>
30 #include <ppc/savearea.h>
31 #include <ppc/exception.h>
32 #include <kern/processor.h>
34 extern int real_ncpus
;
36 static uint32_t pmsSyncrolator
= 0; /* Only one control operation at a time please */
37 uint32_t pmsBroadcastWait
= 0; /* Number of outstanding broadcasts */
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 */
43 static pmsDef
*altDpmsTab
= 0; /* Alternate step definition table */
44 static uint32_t altDpmsTabSize
= 0; /* Size of alternate step definition table */
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 */
55 pmsStat pmsStatsd
[4][pmsMaxStates
]; /* Generate enough statistics blocks for 4 processors */
57 pmsCtl pmsCtls
= { /* Power Management Stepper control */
58 .pmsStats
= &pmsStatsd
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 */
67 * Do any initialization needed
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 */
82 * Start the power management stepper on all processors
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.
93 if(!pmsInstalled
) return; /* We can't do this if no table installed */
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 */
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.
109 * This is used as the initial state at startup and when the step table
118 if(!pmsInstalled
) return; /* We can't do this if no table installed */
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 */
130 * Steps down to a lower power.
131 * Interrupts must be off...
136 struct per_proc_info
*pp
;
139 pp
= getPerProc(); /* Get our per_proc */
141 if(!pmsInstalled
|| pp
->pms
.pmsState
== pmsParked
) return; /* No stepping if parked or not installed */
143 nstate
= pmsCtls
.pmsDefs
[pp
->pms
.pmsState
]->pmsDown
; /* Get the downward step */
144 pmsSetStep(nstate
, 0); /* Step to it */
150 * Steps up to a higher power. The "timer" parameter is true if the
151 * step was driven due to the pms timer expiring.
153 * Interrupts must be off...
156 void pmsStep(int timer
) {
158 struct per_proc_info
*pp
;
162 pp
= getPerProc(); /* Get our per_proc */
164 if(!pmsInstalled
|| pp
->pms
.pmsState
== pmsParked
) return; /* No stepping if parked or not installed */
166 nstate
= pmsCtls
.pmsDefs
[pp
->pms
.pmsState
]->pmsNext
; /* Assume a normal step */
167 dir
= 1; /* A normal step is a step up */
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. */
174 pmsSetStep(nstate
, dir
); /* Step to it */
180 * Set a specific step
182 * We do not do statistics if exiting park
183 * Interrupts must be off...
187 void pmsSetStep(uint32_t nstep
, int dir
) {
189 struct per_proc_info
*pp
;
190 uint32_t pstate
, ret
, nCSetCmd
, mCSetCmd
;
191 pmsDef
*pnstate
, *pcstate
;
192 uint64_t tb
, nt
, dur
;
195 pp
= getPerProc(); /* Get our per_proc */
196 cpu
= cpu_number(); /* Get our processor */
198 while(1) { /* Keep stepping until we get a delay */
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... */
204 if((nstep
== pmsParked
) || ((uint32_t)pmsCtls
.pmsDefs
[nstep
]->pmsSetCmd
== pmsParkIt
)) { /* Are we parking? */
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 */
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 */
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 */
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 */
227 pp
->pms
.pmsCSetCmd
= nCSetCmd
; /* Set it for real */
230 tb
= mach_absolute_time(); /* What time is it? */
231 pp
->pms
.pmsPop
= tb
+ pnstate
->pmsLimit
; /* Set the next pop */
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... */
238 * Gather some statistics
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 */
250 * See if we are done chaining steps
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... */
259 nstep
= pnstate
->pmsNext
; /* Chain on to the next */
267 * Either park the stepper or force the step on a parked stepper for local processor only
271 void pmsRunLocal(uint32_t nstep
) {
273 struct per_proc_info
*pp
;
274 uint32_t cstate
, ret
, lastState
;
275 pmsDef
*pnstate
, *pcstate
;
276 uint64_t tb
, nt
, dur
;
280 if(!pmsInstalled
) return; /* Ignore this if no step programs installed... */
282 intr
= ml_set_interrupts_enabled(FALSE
); /* No interruptions in here */
284 pp
= getPerProc(); /* Get our per_proc */
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 */
291 lastState
= pp
->pms
.pmsState
; /* Remember if we are parked now */
293 pmsSetStep(nstep
, 1); /* Step to the new state */
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 */
305 (void)ml_set_interrupts_enabled(intr
); /* Restore interruptions */
312 * Control the Power Management Stepper.
313 * Called from user state by the superuser via a ppc system call.
314 * Interruptions disabled.
318 int pmsCntrl(struct savearea
*save
) {
320 uint32_t request
, nstep
, reqsize
, result
, presult
;
324 struct per_proc_info
*pp
;
326 pp
= getPerProc(); /* Get our per_proc */
327 cpu
= cpu_number(); /* Get our processor */
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... */
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... */
339 request
= (int)save
->save_r3
; /* Remember the request */
340 reqsize
= (uint32_t)save
->save_r5
; /* Get the size of the config table */
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... */
351 if(request
== pmsCExperimental
) { /* Enter experimental mode? */
353 if(pmsInstalled
|| (pmsExperimental
& 1)) { /* Are we already running or in experimental? */
354 save
->save_r3
= KERN_FAILURE
; /* Fail, since we are already running */
358 pmsExperimental
|= 1; /* Flip us into experimental but don't change other flags */
360 pmsCPUConf(); /* Configure for this machine */
361 pmsStart(); /* Start stepping */
362 save
->save_r3
= KERN_SUCCESS
; /* We are victorious... */
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... */
376 * We are committed after here. If there are any errors detected, we shouldn't die, but we
377 * will be stuck in park.
379 * Also, we can possibly end up on another processor after the broadcast.
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... */
387 save
->save_r3
= KERN_SUCCESS
; /* Assume success */
389 // NOTE: We will block in the following code until everyone has finished the prepare
391 pmsRun(pmsPrepCng
); /* Get everyone parked and in a proper state for step table changes, including me */
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... */
398 switch(request
) { /* Select the routine */
400 case pmsCStart
: /* Starts normal steppping */
401 nstep
= pmsNormHigh
; /* Set the request */
404 case pmsCFLow
: /* Forces low power */
405 nstep
= pmsLow
; /* Set request */
408 case pmsCFHigh
: /* Forces high power */
409 nstep
= pmsHigh
; /* Set request */
412 case pmsCCnfg
: /* Loads new stepper program */
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... */
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... */
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... */
436 nstep
= pmsNormHigh
; /* Set the request */
440 panic("pmsCntrl: stepper control is so very, very confused = %08X\n", request
);
444 pmsRun(nstep
); /* Get everyone into step */
445 pmsSyncrolator
= 0; /* Free us up */
446 return 1; /* All done... */
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.
454 * This will block until all processors have transitioned, so
455 * obviously, this can block.
457 * Called with interruptions disabled.
461 void pmsRun(uint32_t nstep
) {
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 */
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.
477 void pmsRemote(uint32_t nstep
) {
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 */
488 * Build the tables needed for the stepper. This includes both the step definitions and the step control table.
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.
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.
502 * The platformData parameter is a 32-bit word of data that is passed unaltered to the set function.
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.
513 kern_return_t
pmsBuild(pmsDef
*pd
, uint32_t pdsize
, pmsSetFunc_t
*functab
, uint32_t platformData
, pmsQueryFunc_t queryFunc
) {
515 int steps
, newsize
, i
, cstp
, nstps
, oldAltSize
, xdsply
;
518 pmsDef
*newpd
, *oldAlt
;
521 xdsply
= (pmsExperimental
& 3) != 0; /* Turn on kprintfs if requested or in experimental mode */
523 if(pdsize
% sizeof(pmsDef
)) return KERN_INVALID_ARGUMENT
; /* Length not multiple of definition size */
525 steps
= pdsize
/ sizeof(pmsDef
); /* Get the number of steps supplied */
527 if((steps
>= pmsMaxStates
) || (steps
< pmsFree
)) /* Complain if too big or too small */
528 return KERN_INVALID_ARGUMENT
; /* Squeak loudly!!! */
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 */
533 if(xdsply
) kprintf("\n StepID Down Next HWSel HWfun Limit\n");
535 for(i
= 0; i
< steps
; i
++) { /* Step through and verify the definitions */
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
);
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
584 * Verify that there are no infinite synchronous forward loops in the table
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 */
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 */
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... */
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. */
615 * Well, things look ok, let's do it to it...
618 if(xdsply
) kprintf("Loop check passed, building and installing table\n");
620 newsize
= steps
* sizeof(pmsDef
); /* Get the size needed for the definition blocks */
622 if(!(newpd
= (pmsDef
*)kalloc(newsize
))) { /* Get memory for the whole thing */
623 return KERN_RESOURCE_SHORTAGE
; /* No storage... */
626 bzero((void *)newpd
, newsize
); /* Make it pretty */
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...
635 intr
= ml_set_interrupts_enabled(FALSE
); /* No interruptions in here */
636 simple_lock(&pmsBuildLock
); /* Lock out everyone... */
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... */
641 oldAlt
= altDpmsTab
; /* Remember any old alternate we had */
642 oldAltSize
= altDpmsTabSize
; /* Remember its size */
644 altDpmsTab
= newpd
; /* Point to the new table */
645 altDpmsTabSize
= newsize
; /* Set the size */
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 */
651 for(i
= 0; i
< pmsMaxStates
; i
++) pmsCtls
.pmsDefs
[i
] = &pmsDummy
; /* Initialize the table to point to the dummy step */
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 */
657 nanoseconds_to_absolutetime(nlimit
* 1000ULL, &newpd
[i
].pmsLimit
); /* Convert microseconds to nanoseconds and then to ticks */
659 setf
= pd
[i
].sf
.pmsSetFuncInd
; /* Make convienient */
660 newpd
[i
].sf
.pmsSetFunc
= pmsFuncTab
[setf
]; /* Replace the index with the function address */
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 */
670 pmsCtlp
= (uint32_t)&pmsCtls
; /* Point to the new pms table */
672 pmsInstalled
= 1; /* The stepper has been born or born again... */
674 simple_unlock(&pmsBuildLock
); /* Free play! */
675 (void)ml_set_interrupts_enabled(intr
); /* Interrupts back the way there were */
677 if((uint32_t)oldAlt
) kfree((vm_offset_t
)oldAlt
, oldAltSize
); /* If we already had an alternate, free it */
679 if(xdsply
) kprintf("Stepper table installed\n");
681 return KERN_SUCCESS
; /* We're in fate's hands now... */