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 <machine/machine_routines.h>
23 #include <machine/machine_cpu.h>
25 # include <ppc/exception.h>
26 # include <ppc/misc_protos.h>
28 # include <i386/cpu_data.h>
29 # include <i386/misc_protos.h>
31 #include <machine/pmap.h>
33 #include <kern/processor.h>
34 #include <kern/kalloc.h>
35 #include <vm/vm_protos.h>
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 */
67 # define PER_PROC_INFO struct per_proc_info
68 # define GET_PER_PROC_INFO() getPerProc()
70 # define PER_PROC_INFO cpu_data_t
71 # define GET_PER_PROC_INFO() current_cpu_datap()
77 * Do any initialization needed
84 simple_lock_init(&pmsBuildLock
, 0); /* Initialize the build lock */
85 for(i
= 0; i
< pmsMaxStates
; i
++) pmsCtls
.pmsDefs
[i
] = &pmsDummy
; /* Initialize the table to dummy steps */
94 * Start the power management stepper on all processors
96 * All processors must be parked. This should be called when the hardware
97 * is ready to step. Probably only at boot and after wake from sleep.
101 void pmsStart(void) {
105 if(!pmsInstalled
) return; /* We can't do this if no table installed */
107 intr
= ml_set_interrupts_enabled(FALSE
); /* No interruptions in here */
108 pmsRun(pmsStartUp
); /* Start running the stepper everywhere */
109 (void)ml_set_interrupts_enabled(intr
); /* Restore interruptions */
117 * Park the stepper execution. This will force the stepper on this
118 * processor to abandon its current step and stop. No changes to the
119 * hardware state is made and any previous step is lost.
121 * This is used as the initial state at startup and when the step table
130 if(!pmsInstalled
) return; /* We can't do this if no table installed */
132 intr
= ml_set_interrupts_enabled(FALSE
); /* No interruptions in here */
133 pmsSetStep(pmsParked
, 0); /* Park the stepper */
134 (void)ml_set_interrupts_enabled(intr
); /* Restore interruptions */
142 * Steps down to a lower power.
143 * Interrupts must be off...
151 pp
= GET_PER_PROC_INFO(); /* Get our per_proc */
153 if(!pmsInstalled
|| pp
->pms
.pmsState
== pmsParked
) return; /* No stepping if parked or not installed */
155 nstate
= pmsCtls
.pmsDefs
[pp
->pms
.pmsState
]->pmsDown
; /* Get the downward step */
156 pmsSetStep(nstate
, 0); /* Step to it */
162 * Steps up to a higher power. The "timer" parameter is true if the
163 * step was driven due to the pms timer expiring.
165 * Interrupts must be off...
168 int pmsStepIdleSneaks
;
169 int pmsStepIdleTries
;
171 void pmsStep(int timer
) {
180 pp
= GET_PER_PROC_INFO(); /* Get our per_proc */
182 if(!pmsInstalled
|| pp
->pms
.pmsState
== pmsParked
)
183 return; /* No stepping if parked or not installed */
186 * Assume a normal step.
188 nstate
= pmsCtls
.pmsDefs
[pp
->pms
.pmsState
]->pmsNext
;
191 * If we are idling and being asked to step up, check to see whether
192 * the package we're in is already at a non-idle power state. If so,
193 * attempt to work out what state that is, and go there directly to
194 * avoid wasting time ramping up.
196 if ((pp
->pms
.pmsState
== pmsIdle
)
197 && ((pkgstate
= pmsCPUPackageQuery()) != ~(uint32_t)0)) {
199 * Search forward through the stepper program,
200 * avoid looping for too long.
204 for (i
= 0; i
< 32; i
++) {
206 * Compare command with current package state
208 if ((pmsCtls
.pmsDefs
[tstate
]->pmsSetCmd
& pmsCPU
) == pkgstate
) {
215 * Advance to the next step in the program.
217 if (pmsCtls
.pmsDefs
[tstate
]->pmsNext
== tstate
)
218 break; /* infinite loop */
219 tstate
= pmsCtls
.pmsDefs
[tstate
]->pmsNext
;
224 * Default to a step up.
229 * If we are stepping as a consequence of timer expiry, select the
230 * alternate exit path and note this as downward step for accounting
234 && (pmsCtls
.pmsDefs
[pp
->pms
.pmsState
]->pmsSetCmd
== pmsDelay
)) {
235 nstate
= pmsCtls
.pmsDefs
[pp
->pms
.pmsState
]->pmsTDelay
;
238 * Delayed steps are a step down for accounting purposes.
243 pmsSetStep(nstate
, dir
);
249 * Set a specific step
251 * We do not do statistics if exiting park
252 * Interrupts must be off...
256 void pmsSetStep(uint32_t nstep
, int dir
) {
259 uint32_t pstate
, nCSetCmd
, mCSetCmd
;
260 pmsDef
*pnstate
, *pcstate
;
264 pp
= GET_PER_PROC_INFO(); /* Get our per_proc */
265 cpu
= cpu_number(); /* Get our processor */
267 while(1) { /* Keep stepping until we get a delay */
269 if(pp
->pms
.pmsCSetCmd
& pmsMustCmp
) { /* Do we have to finish the delay before changing? */
270 while(mach_absolute_time() < pp
->pms
.pmsPop
); /* Yes, spin here... */
273 if((nstep
== pmsParked
) || ((uint32_t)pmsCtls
.pmsDefs
[nstep
]->pmsSetCmd
== pmsParkIt
)) { /* Are we parking? */
275 tb
= mach_absolute_time(); /* What time is it? */
276 pp
->pms
.pmsStamp
= tb
; /* Show transition now */
277 pp
->pms
.pmsPop
= HalfwayToForever
; /* Set the pop way into the future */
278 pp
->pms
.pmsState
= pmsParked
; /* Make sure we are parked */
279 etimer_resync_deadlines(); /* Cancel our timer if going */
283 pnstate
= pmsCtls
.pmsDefs
[nstep
]; /* Point to the state definition */
284 pstate
= pp
->pms
.pmsState
; /* Save the current step */
285 pp
->pms
.pmsState
= nstep
; /* Set the current to the next step */
287 if(pnstate
->pmsSetCmd
!= pmsDelay
) { /* If this is not a delayed state, change the actual hardware now */
288 if(pnstate
->pmsSetCmd
& pmsCngCPU
) pmsCPUSet(pnstate
->pmsSetCmd
); /* We have some CPU work to do... */
289 if((uint32_t)pnstate
->sf
.pmsSetFunc
) pnstate
->sf
.pmsSetFunc(pnstate
->pmsSetCmd
, cpu
, pmsPlatformData
); /* Tell the platform to set power mode */
291 mCSetCmd
= pnstate
->pmsSetCmd
& (pmsCngXClk
| pmsCngCPU
| pmsCngVolt
); /* Isolate just the change flags */
292 mCSetCmd
= (mCSetCmd
- (mCSetCmd
>> 7)) | pmsSync
| pmsMustCmp
| pmsPowerID
; /* Form mask of bits that come from new command */
293 nCSetCmd
= pp
->pms
.pmsCSetCmd
& ~mCSetCmd
; /* Clear changing bits */
294 nCSetCmd
= nCSetCmd
| (pnstate
->pmsSetCmd
& mCSetCmd
); /* Flip on the changing bits and the always copy bits */
296 pp
->pms
.pmsCSetCmd
= nCSetCmd
; /* Set it for real */
299 tb
= mach_absolute_time(); /* What time is it? */
300 pp
->pms
.pmsPop
= tb
+ pnstate
->pmsLimit
; /* Set the next pop */
302 if((pnstate
->pmsSetCmd
!= pmsDelay
) && (pp
->pms
.pmsCSetCmd
& pmsSync
) && (pnstate
->pmsLimit
!= 0)) { /* Is this a synchronous command with a delay? */
303 while(mach_absolute_time() < pp
->pms
.pmsPop
); /* Yes, spin here and wait it out... */
307 * Gather some statistics
310 dur
= tb
- pp
->pms
.pmsStamp
; /* Get the amount of time we were in the old step */
311 pp
->pms
.pmsStamp
= tb
; /* Set the new timestamp */
312 if(!(pstate
== pmsParked
)) { /* Only take stats if we were not parked */
313 pcstate
= pmsCtls
.pmsDefs
[pstate
]; /* Get the previous step */
314 pmsCtls
.pmsStats
[cpu
][pcstate
->pmsStepID
].stTime
[dir
] += dur
; /* Accumulate the total time in the old step */
315 pmsCtls
.pmsStats
[cpu
][pcstate
->pmsStepID
].stCnt
[dir
] += 1; /* Count transitions */
319 * See if we are done chaining steps
322 if((pnstate
->pmsSetCmd
== pmsDelay
)
323 || (!(pp
->pms
.pmsCSetCmd
& pmsSync
) && (pnstate
->pmsLimit
!= 0))) { /* Is this not syncronous and a non-zero delay or a delayed step? */
324 etimer_resync_deadlines(); /* Start the timers ticking */
325 break; /* We've stepped as far as we're going to... */
328 nstep
= pnstate
->pmsNext
; /* Chain on to the next */
336 * Either park the stepper or force the step on a parked stepper for local processor only
340 void pmsRunLocal(uint32_t nstep
) {
347 if(!pmsInstalled
) return; /* Ignore this if no step programs installed... */
349 intr
= ml_set_interrupts_enabled(FALSE
); /* No interruptions in here */
351 pp
= GET_PER_PROC_INFO(); /* Get our per_proc */
353 if(nstep
== pmsStartUp
) { /* Should we start up? */
354 pmsCPUInit(); /* Get us up to full with high voltage and park */
355 nstep
= pmsNormHigh
; /* Change request to transition to normal high */
358 lastState
= pp
->pms
.pmsState
; /* Remember if we are parked now */
360 pmsSetStep(nstep
, 1); /* Step to the new state */
362 if((lastState
== pmsParked
) && (pp
->pms
.pmsState
!= pmsParked
)) { /* Did we just unpark? */
363 cpu
= cpu_number(); /* Get our processor */
364 for(i
= 0; i
< pmsMaxStates
; i
++) { /* Step through the steps and clear the statistics since we were parked */
365 pmsCtls
.pmsStats
[cpu
][i
].stTime
[0] = 0; /* Clear accumulated time - downward */
366 pmsCtls
.pmsStats
[cpu
][i
].stTime
[1] = 0; /* Clear accumulated time - forward */
367 pmsCtls
.pmsStats
[cpu
][i
].stCnt
[0] = 0; /* Clear transition count - downward */
368 pmsCtls
.pmsStats
[cpu
][i
].stCnt
[1] = 0; /* Clear transition count - forward */
372 (void)ml_set_interrupts_enabled(intr
); /* Restore interruptions */
379 * Control the Power Management Stepper.
380 * Called from user state by the superuser.
381 * Interruptions disabled.
384 kern_return_t
pmsControl(uint32_t request
, user_addr_t reqaddr
, uint32_t reqsize
) {
386 uint32_t nstep
, result
, presult
;
392 pp
= GET_PER_PROC_INFO(); /* Get our per_proc */
393 cpu
= cpu_number(); /* Get our processor */
395 if(!is_suser()) { /* We are better than most, */
396 return KERN_FAILURE
; /* so we will only talk to the superuser. */
399 if(request
>= pmsCFree
) { /* Can we understand the request? */
400 return KERN_INVALID_ARGUMENT
; /* What language are these guys talking in, anyway? */
403 if(request
== pmsCQuery
) { /* Are we just checking? */
404 result
= pmsCPUQuery() & pmsCPU
; /* Get the processor data and make sure there is no slop */
405 presult
= 0; /* Assume nothing */
406 if((uint32_t)pmsQueryFunc
) presult
= pmsQueryFunc(cpu
, pmsPlatformData
); /* Go get the platform state */
407 result
= result
| (presult
& (pmsXClk
| pmsVoltage
| pmsPowerID
)); /* Merge the platform state with no slop */
408 return result
; /* Tell 'em... */
411 if(request
== pmsCExperimental
) { /* Enter experimental mode? */
413 if(pmsInstalled
|| (pmsExperimental
& 1)) { /* Are we already running or in experimental? */
414 return KERN_FAILURE
; /* Fail, since we are already running */
417 pmsExperimental
|= 1; /* Flip us into experimental but don't change other flags */
419 pmsCPUConf(); /* Configure for this machine */
420 pmsStart(); /* Start stepping */
421 return KERN_SUCCESS
; /* We are victorious... */
425 if(request
== pmsCCnfg
) { /* Do some up-front checking before we commit to doing this */
426 if((reqsize
> (pmsMaxStates
* sizeof(pmsDef
))) || (reqsize
< (pmsFree
* sizeof(pmsDef
)))) { /* Check that the size is reasonable */
427 return KERN_NO_SPACE
; /* Tell them that they messed up */
431 if (request
== pmsGCtls
) {
432 if (reqsize
!= sizeof(pmsCtls
))
433 return(KERN_FAILURE
);
434 ret
= copyout(&pmsCtls
, reqaddr
, reqsize
);
438 if (request
== pmsGStats
) {
439 if (reqsize
!= sizeof(pmsStatsd
)) /* request size is fixed */
441 ret
= copyout(&pmsStatsd
, reqaddr
, reqsize
);
442 return kret
; /* All done... */
446 * We are committed after here. If there are any errors detected, we shouldn't die, but we
447 * will be stuck in park.
449 * Also, we can possibly end up on another processor after the broadcast.
453 if(!hw_compare_and_store(0, 1, &pmsSyncrolator
)) { /* Are we already doing this? */
454 return KERN_RESOURCE_SHORTAGE
; /* Tell them that we are already busy and to try again */
457 // NOTE: We will block in the following code until everyone has finished the prepare
459 pmsRun(pmsPrepCng
); /* Get everyone parked and in a proper state for step table changes, including me */
461 if(request
== pmsCPark
) { /* Is all we're supposed to do park? */
462 pmsSyncrolator
= 0; /* Free us up */
463 return KERN_SUCCESS
; /* Well, then we're done... */
466 switch(request
) { /* Select the routine */
468 case pmsCStart
: /* Starts normal steppping */
469 nstep
= pmsNormHigh
; /* Set the request */
472 case pmsCFLow
: /* Forces low power */
473 nstep
= pmsLow
; /* Set request */
476 case pmsCFHigh
: /* Forces high power */
477 nstep
= pmsHigh
; /* Set request */
480 case pmsCCnfg
: /* Loads new stepper program */
482 if(!(ndefs
= (pmsDef
*)kalloc(reqsize
))) { /* Get memory for the whole thing */
483 pmsSyncrolator
= 0; /* Free us up */
484 return KERN_INVALID_ADDRESS
; /* All done... */
487 ret
= copyin(reqaddr
, (void *)ndefs
, reqsize
); /* Get the new config table */
488 if(ret
) { /* Hmmm, something went wrong with the copyin */
489 kfree((vm_offset_t
)ndefs
, reqsize
); /* Free up the copied in data */
490 pmsSyncrolator
= 0; /* Free us up */
491 return KERN_INVALID_ADDRESS
; /* All done... */
494 kret
= pmsBuild(ndefs
, reqsize
, 0, 0, 0); /* Go build and replace the tables. Make sure we keep the old platform stuff */
495 if(kret
) { /* Hmmm, something went wrong with the compilation */
496 kfree((vm_offset_t
)ndefs
, reqsize
); /* Free up the copied in data */
497 pmsSyncrolator
= 0; /* Free us up */
498 return kret
; /* All done... */
501 nstep
= pmsNormHigh
; /* Set the request */
505 panic("pmsCntrl: stepper control is so very, very confused = %08X\n", request
);
509 pmsRun(nstep
); /* Get everyone into step */
510 pmsSyncrolator
= 0; /* Free us up */
511 return KERN_SUCCESS
; /* All done... */
516 * Broadcast a change to all processors including ourselves.
518 * Interruptions are disabled.
521 void pmsRun(uint32_t nstep
) {
528 * Build the tables needed for the stepper. This includes both the step definitions and the step control table.
530 * We most absolutely need to be parked before this happens because we're gonna change the table.
531 * We're going to have to be pretty complete about checking for errors.
532 * Also, a copy is always made because we don't want to be crippled by not being able to change
533 * the table or description formats.
535 * We pass in a table of external functions and the new stepper def uses the corresponding
536 * indexes rather than actual function addresses. This is done so that a proper table can be
537 * built with the control syscall. It can't supply addresses, so the index has to do. We
538 * internalize the table so our caller does not need to keep it. Note that passing in a 0
539 * will use the current function table. Also note that entry 0 is reserved and must be 0,
540 * we will check and fail the build.
542 * The platformData parameter is a 32-bit word of data that is passed unaltered to the set function.
544 * The queryFunc parameter is the address of a function that will return the current state of the platform.
545 * The format of the data returned is the same as the platform specific portions of pmsSetCmd, i.e., pmsXClk,
546 * pmsVoltage, and any part of pmsPowerID that is maintained by the platform hardware (an example would be
547 * the values of the gpios that correspond to pmsPowerID). The value should be constructed by querying
548 * hardware rather than returning a value cached by software. One of the intents of this function is to
549 * help recover lost or determine initial power states.
553 kern_return_t
pmsBuild(pmsDef
*pd
, uint32_t pdsize
, pmsSetFunc_t
*functab
, uint32_t platformData
, pmsQueryFunc_t queryFunc
) {
555 int steps
, newsize
, i
, cstp
, nstps
, oldAltSize
, xdsply
;
558 pmsDef
*newpd
, *oldAlt
;
561 xdsply
= (pmsExperimental
& 3) != 0; /* Turn on kprintfs if requested or in experimental mode */
563 if(pdsize
% sizeof(pmsDef
)) return KERN_INVALID_ARGUMENT
; /* Length not multiple of definition size */
565 steps
= pdsize
/ sizeof(pmsDef
); /* Get the number of steps supplied */
567 if((steps
>= pmsMaxStates
) || (steps
< pmsFree
)) /* Complain if too big or too small */
568 return KERN_INVALID_ARGUMENT
; /* Squeak loudly!!! */
570 if((uint32_t)functab
&& (uint32_t)functab
[0]) /* Verify that if they supplied a new function table, entry 0 is 0 */
571 return KERN_INVALID_ARGUMENT
; /* Fail because they didn't reserve entry 0 */
573 if(xdsply
) kprintf("\n StepID Down Next HWSel HWfun Limit\n");
575 for(i
= 0; i
< steps
; i
++) { /* Step through and verify the definitions */
577 if(xdsply
) kprintf(" %6d %6d %6d %08X %6d %20lld\n", pd
[i
].pmsStepID
, pd
[i
].pmsDown
,
578 pd
[i
].pmsNext
, pd
[i
].pmsSetCmd
,
579 pd
[i
].sf
.pmsSetFuncInd
, pd
[i
].pmsLimit
);
581 if((pd
[i
].pmsLimit
!= 0) && (pd
[i
].pmsLimit
< 100ULL)) {
582 if(xdsply
) kprintf("error step %3d: pmsLimit too small/n", i
);
583 return KERN_INVALID_ARGUMENT
; /* Has to be 100µS or more */
586 if((pd
[i
].pmsLimit
!= 0xFFFFFFFFFFFFFFFFULL
) && (pd
[i
].pmsLimit
> (HalfwayToForever
/ 1000ULL))) {
587 if(xdsply
) kprintf("error step %3d: pmsLimit too big\n", i
);
588 return KERN_INVALID_ARGUMENT
; /* Can't be too big */
591 if(pd
[i
].pmsStepID
!= i
) {
592 if(xdsply
) kprintf("error step %3d: step ID does not match (%d)\n", i
, pd
[i
].pmsStepID
);
593 return KERN_INVALID_ARGUMENT
; /* ID must match */
596 if(pd
[i
].sf
.pmsSetFuncInd
>= pmsSetFuncMax
) {
597 if(xdsply
) kprintf("error step %3d: function invalid (%d)\n", i
, pd
[i
].sf
.pmsSetFuncInd
);
598 return KERN_INVALID_ARGUMENT
; /* Fail if this function is not in the table */
601 if((pd
[i
].pmsDown
!= pmsParked
) && pd
[i
].pmsDown
>= steps
) {
602 if(xdsply
) kprintf("error step %3d: pmsDown out of range (%d)\n", i
, pd
[i
].pmsDown
);
603 return KERN_INVALID_ARGUMENT
; /* Step down must be in the table or park */
606 if((pd
[i
].pmsNext
!= pmsParked
) && pd
[i
].pmsNext
>= steps
) {
607 if(xdsply
) kprintf("error step %3d: pmsNext out of range (%d)\n", i
, pd
[i
].pmsNext
);
608 return KERN_INVALID_ARGUMENT
; /* Step up must be in the table or park */
611 if((pd
[i
].pmsSetCmd
== pmsDelay
) && (pd
[i
].pmsTDelay
>= steps
)) {
612 if(xdsply
) kprintf("error step %3d: pmsTDelay out of range (%d)\n", i
, pd
[i
].pmsTDelay
);
613 return KERN_INVALID_ARGUMENT
; /* Delayed step must be in the table */
616 if((pd
[i
].pmsSetCmd
== pmsDelay
) && (pd
[i
].pmsLimit
== 0xFFFFFFFFFFFFFFFFULL
)) {
617 if(xdsply
) kprintf("error step %3d: delay time limit must not be infinite\n", i
);
618 return KERN_INVALID_ARGUMENT
; /* Delayed step must have a time limit */
624 * Verify that there are no infinite synchronous forward loops in the table
627 if(xdsply
) kprintf("\nInitial scan passed, start in loop check\n");
628 for(i
= 0; i
< steps
; i
++) { /* Start with each step. Inefficient, but who cares */
630 cstp
= i
; /* Set starting point */
631 nstps
= 0; /* Initialize chain length counter */
632 while(1) { /* Do until we hit the end */
633 if(pd
[cstp
].pmsSetCmd
== pmsParkIt
) break; /* Parking always terminates a chain so no endless loop here */
634 if(pd
[cstp
].pmsSetCmd
== pmsDelay
) break; /* Delayed steps always terminate a chain so no endless loop here */
635 if((pd
[cstp
].pmsLimit
!= 0) && ((pd
[cstp
].pmsSetCmd
& pmsSync
) != pmsSync
)) break; /* If time limit is not 0 and not synchrouous, no endless loop */
636 if(pd
[cstp
].pmsNext
== pmsParked
) break; /* If the next step is parked, no endless loop */
638 cstp
= pd
[cstp
].pmsNext
; /* Chain to the next */
639 nstps
= nstps
+ 1; /* Count this step */
640 if(nstps
>= steps
) { /* We've stepped for more steps than we have, must be an endless loop! */
641 if(xdsply
) kprintf("error step %3d: infinite pmsNext loop\n", i
);
642 return KERN_INVALID_ARGUMENT
; /* Suggest to our caller that they can't program... */
647 if((pmsExperimental
& 4) && (pmsInstalled
) && ((uint32_t)functab
!= 0)) { /* If we are already initted and experimental is locked in, and we are doing first */
648 if(xdsply
) kprintf("Experimental locked, ignoring driver pmsBuild\n");
649 return KERN_RESOURCE_SHORTAGE
; /* Just ignore the request. */
655 * Well, things look ok, let's do it to it...
658 if(xdsply
) kprintf("Loop check passed, building and installing table\n");
660 newsize
= steps
* sizeof(pmsDef
); /* Get the size needed for the definition blocks */
662 if(!(newpd
= (pmsDef
*)kalloc(newsize
))) { /* Get memory for the whole thing */
663 return KERN_RESOURCE_SHORTAGE
; /* No storage... */
666 bzero((void *)newpd
, newsize
); /* Make it pretty */
669 * Ok, this is it, finish intitializing, switch the tables, and pray...
670 * We want no interruptions at all and we need to lock the table. Everybody should be parked,
671 * so no one should ever touch this. The lock is to keep multiple builders safe. It probably
672 * will never ever happen, but paranoia is a good thing...
675 intr
= ml_set_interrupts_enabled(FALSE
); /* No interruptions in here */
676 simple_lock(&pmsBuildLock
); /* Lock out everyone... */
678 if(platformData
) pmsPlatformData
= platformData
; /* Remember the platform data word passed in if any was... */
679 if((uint32_t)queryFunc
) pmsQueryFunc
= queryFunc
; /* Remember the query function passed in, if it was... */
681 oldAlt
= altDpmsTab
; /* Remember any old alternate we had */
682 oldAltSize
= altDpmsTabSize
; /* Remember its size */
684 altDpmsTab
= newpd
; /* Point to the new table */
685 altDpmsTabSize
= newsize
; /* Set the size */
687 if((uint32_t)functab
) { /* Did we get a new function table? */
688 for(i
= 0; i
< pmsSetFuncMax
; i
++) pmsFuncTab
[i
] = functab
[i
]; /* Copy in the new table */
691 for(i
= 0; i
< pmsMaxStates
; i
++) pmsCtls
.pmsDefs
[i
] = &pmsDummy
; /* Initialize the table to point to the dummy step */
693 for(i
= 0; i
< steps
; i
++) { /* Replace the step table entries */
694 if(pd
[i
].pmsLimit
== 0xFFFFFFFFFFFFFFFFULL
) nlimit
= century
; /* Default to 100 years */
695 else nlimit
= pd
[i
].pmsLimit
; /* Otherwise use what was supplied */
697 nanoseconds_to_absolutetime(nlimit
* 1000ULL, &newpd
[i
].pmsLimit
); /* Convert microseconds to nanoseconds and then to ticks */
699 setf
= pd
[i
].sf
.pmsSetFuncInd
; /* Make convienient */
700 newpd
[i
].sf
.pmsSetFunc
= pmsFuncTab
[setf
]; /* Replace the index with the function address */
702 newpd
[i
].pmsStepID
= pd
[i
].pmsStepID
; /* Set the step ID */
703 newpd
[i
].pmsSetCmd
= pd
[i
].pmsSetCmd
; /* Set the hardware selector ID */
704 newpd
[i
].pmsDown
= pd
[i
].pmsDown
; /* Set the downward step */
705 newpd
[i
].pmsNext
= pd
[i
].pmsNext
; /* Set the next setp */
706 newpd
[i
].pmsTDelay
= pd
[i
].pmsTDelay
; /* Set the delayed setp */
707 pmsCtls
.pmsDefs
[i
] = &newpd
[i
]; /* Copy it in */
710 pmsCtlp
= (uint32_t)&pmsCtls
; /* Point to the new pms table */
712 pmsInstalled
= 1; /* The stepper has been born or born again... */
714 simple_unlock(&pmsBuildLock
); /* Free play! */
715 (void)ml_set_interrupts_enabled(intr
); /* Interrupts back the way there were */
717 if((uint32_t)oldAlt
) kfree(oldAlt
, oldAltSize
); /* If we already had an alternate, free it */
719 if(xdsply
) kprintf("Stepper table installed\n");
721 return KERN_SUCCESS
; /* We're in fate's hands now... */