2 * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_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. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
28 #include <machine/machine_routines.h>
29 #include <machine/machine_cpu.h>
31 # include <ppc/exception.h>
32 # include <ppc/misc_protos.h>
33 # include <ppc/cpu_internal.h>
35 # include <i386/cpu_data.h>
36 # include <i386/misc_protos.h>
38 #include <machine/pmap.h>
40 #include <kern/processor.h>
41 #include <kern/kalloc.h>
42 #include <vm/vm_protos.h>
44 extern int is_suser(void);
46 static uint32_t pmsSyncrolator
= 0; /* Only one control operation at a time please */
47 uint32_t pmsBroadcastWait
= 0; /* Number of outstanding broadcasts */
49 int pmsInstalled
= 0; /* Power Management Stepper can run and has table installed */
50 int pmsExperimental
= 0; /* Power Management Stepper in experimental mode */
51 decl_simple_lock_data(,pmsBuildLock
) /* Make sure only one guy can replace table at the same time */
53 static pmsDef
*altDpmsTab
; /* Alternate step definition table */
54 static uint32_t altDpmsTabSize
= 0; /* Size of alternate step definition table */
56 pmsDef pmsDummy
= { /* This is the dummy step for initialization. All it does is to park */
57 .pmsLimit
= 0, /* Time doesn't matter for a park */
58 .pmsStepID
= pmsMaxStates
- 1, /* Use the very last ID number for the dummy */
59 .pmsSetCmd
= pmsParkIt
, /* Force us to be parked */
60 .sf
.pmsSetFuncInd
= 0, /* No platform call for this one */
61 .pmsDown
= pmsPrepSleep
, /* We always park */
62 .pmsNext
= pmsPrepSleep
/* We always park */
65 pmsStat pmsStatsd
[4][pmsMaxStates
]; /* Generate enough statistics blocks for 4 processors */
67 pmsCtl pmsCtls
= { /* Power Management Stepper control */
68 .pmsStats
= pmsStatsd
,
71 pmsSetFunc_t pmsFuncTab
[pmsSetFuncMax
] = {NULL
}; /* This is the function index table */
72 pmsQueryFunc_t pmsQueryFunc
; /* Pointer to pmsQuery function */
73 uint32_t pmsPlatformData
= 0; /* Data provided by and passed to platform functions */
76 # define PER_PROC_INFO struct per_proc_info
77 # define GET_PER_PROC_INFO() getPerProc()
79 # define PER_PROC_INFO cpu_data_t
80 # define GET_PER_PROC_INFO() current_cpu_datap()
86 * Do any initialization needed
94 simple_lock_init(&pmsBuildLock
, 0); /* Initialize the build lock */
95 for(i
= 0; i
< pmsMaxStates
; i
++) pmsCtls
.pmsDefs
[i
] = &pmsDummy
; /* Initialize the table to dummy steps */
102 * Start the power management stepper on all processors
104 * All processors must be parked. This should be called when the hardware
105 * is ready to step. Probably only at boot and after wake from sleep.
115 return; /* We can't do this if no table installed */
117 intr
= ml_set_interrupts_enabled(FALSE
); /* No interruptions in here */
118 pmsRun(pmsStartUp
); /* Start running the stepper everywhere */
119 (void)ml_set_interrupts_enabled(intr
); /* Restore interruptions */
124 * Park the stepper execution. This will force the stepper on this
125 * processor to abandon its current step and stop. No changes to the
126 * hardware state is made and any previous step is lost.
128 * This is used as the initial state at startup and when the step table
139 return; /* We can't do this if no table installed */
141 intr
= ml_set_interrupts_enabled(FALSE
); /* No interruptions in here */
142 pmsSetStep(pmsParked
, 0); /* Park the stepper */
143 (void)ml_set_interrupts_enabled(intr
); /* Restore interruptions */
148 * Steps down to a lower power.
149 * Interrupts must be off...
158 pp
= GET_PER_PROC_INFO(); /* Get our per_proc */
160 if(!pmsInstalled
|| pp
->pms
.pmsState
== pmsParked
)
161 return; /* No stepping if parked or not installed */
163 nstate
= pmsCtls
.pmsDefs
[pp
->pms
.pmsState
]->pmsDown
; /* Get the downward step */
164 pmsSetStep(nstate
, 0); /* Step to it */
169 * Steps up to a higher power. The "timer" parameter is true if the
170 * step was driven due to the pms timer expiring.
172 * Interrupts must be off...
175 int pmsStepIdleSneaks
;
176 int pmsStepIdleTries
;
188 pp
= GET_PER_PROC_INFO(); /* Get our per_proc */
190 if(!pmsInstalled
|| pp
->pms
.pmsState
== pmsParked
)
191 return; /* No stepping if parked or not installed */
194 * Assume a normal step.
196 nstate
= pmsCtls
.pmsDefs
[pp
->pms
.pmsState
]->pmsNext
;
199 * If we are idling and being asked to step up, check to see whether
200 * the package we're in is already at a non-idle power state. If so,
201 * attempt to work out what state that is, and go there directly to
202 * avoid wasting time ramping up.
204 if ((pp
->pms
.pmsState
== pmsIdle
)
205 && ((pkgstate
= pmsCPUPackageQuery()) != ~(uint32_t)0)) {
207 * Search forward through the stepper program,
208 * avoid looping for too long.
212 for (i
= 0; i
< 32; i
++) {
214 * Compare command with current package state
216 if ((pmsCtls
.pmsDefs
[tstate
]->pmsSetCmd
& pmsCPU
) == pkgstate
) {
223 * Advance to the next step in the program.
225 if (pmsCtls
.pmsDefs
[tstate
]->pmsNext
== tstate
)
226 break; /* infinite loop */
227 tstate
= pmsCtls
.pmsDefs
[tstate
]->pmsNext
;
232 * Default to a step up.
237 * If we are stepping as a consequence of timer expiry, select the
238 * alternate exit path and note this as downward step for accounting
242 && (pmsCtls
.pmsDefs
[pp
->pms
.pmsState
]->pmsSetCmd
== pmsDelay
)) {
243 nstate
= pmsCtls
.pmsDefs
[pp
->pms
.pmsState
]->pmsTDelay
;
246 * Delayed steps are a step down for accounting purposes.
251 pmsSetStep(nstate
, dir
);
256 * Set a specific step
258 * We do not do statistics if exiting park
259 * Interrupts must be off...
264 pmsSetStep(uint32_t nstep
, int dir
)
267 uint32_t pstate
, nCSetCmd
, mCSetCmd
;
268 pmsDef
*pnstate
, *pcstate
;
272 pp
= GET_PER_PROC_INFO(); /* Get our per_proc */
273 cpu
= cpu_number(); /* Get our processor */
275 while(1) { /* Keep stepping until we get a delay */
277 if(pp
->pms
.pmsCSetCmd
& pmsMustCmp
) { /* Do we have to finish the delay before changing? */
278 while(mach_absolute_time() < pp
->pms
.pmsPop
); /* Yes, spin here... */
281 if((nstep
== pmsParked
) || ((uint32_t)pmsCtls
.pmsDefs
[nstep
]->pmsSetCmd
== pmsParkIt
)) { /* Are we parking? */
283 tb
= mach_absolute_time(); /* What time is it? */
284 pp
->pms
.pmsStamp
= tb
; /* Show transition now */
285 pp
->pms
.pmsPop
= HalfwayToForever
; /* Set the pop way into the future */
286 pp
->pms
.pmsState
= pmsParked
; /* Make sure we are parked */
287 etimer_resync_deadlines(); /* Cancel our timer if going */
291 pnstate
= pmsCtls
.pmsDefs
[nstep
]; /* Point to the state definition */
292 pstate
= pp
->pms
.pmsState
; /* Save the current step */
293 pp
->pms
.pmsState
= nstep
; /* Set the current to the next step */
295 if(pnstate
->pmsSetCmd
!= pmsDelay
) { /* If this is not a delayed state, change the actual hardware now */
296 if(pnstate
->pmsSetCmd
& pmsCngCPU
) pmsCPUSet(pnstate
->pmsSetCmd
); /* We have some CPU work to do... */
297 if((uint32_t)pnstate
->sf
.pmsSetFunc
) pnstate
->sf
.pmsSetFunc(pnstate
->pmsSetCmd
, cpu
, pmsPlatformData
); /* Tell the platform to set power mode */
299 mCSetCmd
= pnstate
->pmsSetCmd
& (pmsCngXClk
| pmsCngCPU
| pmsCngVolt
); /* Isolate just the change flags */
300 mCSetCmd
= (mCSetCmd
- (mCSetCmd
>> 7)) | pmsSync
| pmsMustCmp
| pmsPowerID
; /* Form mask of bits that come from new command */
301 nCSetCmd
= pp
->pms
.pmsCSetCmd
& ~mCSetCmd
; /* Clear changing bits */
302 nCSetCmd
= nCSetCmd
| (pnstate
->pmsSetCmd
& mCSetCmd
); /* Flip on the changing bits and the always copy bits */
304 pp
->pms
.pmsCSetCmd
= nCSetCmd
; /* Set it for real */
307 tb
= mach_absolute_time(); /* What time is it? */
308 pp
->pms
.pmsPop
= tb
+ pnstate
->pmsLimit
; /* Set the next pop */
310 if((pnstate
->pmsSetCmd
!= pmsDelay
) && (pp
->pms
.pmsCSetCmd
& pmsSync
) && (pnstate
->pmsLimit
!= 0)) { /* Is this a synchronous command with a delay? */
311 while(mach_absolute_time() < pp
->pms
.pmsPop
); /* Yes, spin here and wait it out... */
315 * Gather some statistics
318 dur
= tb
- pp
->pms
.pmsStamp
; /* Get the amount of time we were in the old step */
319 pp
->pms
.pmsStamp
= tb
; /* Set the new timestamp */
320 if(!(pstate
== pmsParked
)) { /* Only take stats if we were not parked */
321 pcstate
= pmsCtls
.pmsDefs
[pstate
]; /* Get the previous step */
322 pmsCtls
.pmsStats
[cpu
][pcstate
->pmsStepID
].stTime
[dir
] += dur
; /* Accumulate the total time in the old step */
323 pmsCtls
.pmsStats
[cpu
][pcstate
->pmsStepID
].stCnt
[dir
] += 1; /* Count transitions */
327 * See if we are done chaining steps
330 if((pnstate
->pmsSetCmd
== pmsDelay
)
331 || (!(pp
->pms
.pmsCSetCmd
& pmsSync
) && (pnstate
->pmsLimit
!= 0))) { /* Is this not syncronous and a non-zero delay or a delayed step? */
332 etimer_resync_deadlines(); /* Start the timers ticking */
333 break; /* We've stepped as far as we're going to... */
336 nstep
= pnstate
->pmsNext
; /* Chain on to the next */
341 * Either park the stepper or force the step on a parked stepper for local processor only
346 pmsRunLocal(uint32_t nstep
)
353 if(!pmsInstalled
) /* Ignore this if no step programs installed... */
356 intr
= ml_set_interrupts_enabled(FALSE
); /* No interruptions in here */
358 pp
= GET_PER_PROC_INFO(); /* Get our per_proc */
360 if(nstep
== pmsStartUp
) { /* Should we start up? */
361 pmsCPUInit(); /* Get us up to full with high voltage and park */
362 nstep
= pmsNormHigh
; /* Change request to transition to normal high */
365 lastState
= pp
->pms
.pmsState
; /* Remember if we are parked now */
367 pmsSetStep(nstep
, 1); /* Step to the new state */
369 if((lastState
== pmsParked
) && (pp
->pms
.pmsState
!= pmsParked
)) { /* Did we just unpark? */
370 cpu
= cpu_number(); /* Get our processor */
371 for(i
= 0; i
< pmsMaxStates
; i
++) { /* Step through the steps and clear the statistics since we were parked */
372 pmsCtls
.pmsStats
[cpu
][i
].stTime
[0] = 0; /* Clear accumulated time - downward */
373 pmsCtls
.pmsStats
[cpu
][i
].stTime
[1] = 0; /* Clear accumulated time - forward */
374 pmsCtls
.pmsStats
[cpu
][i
].stCnt
[0] = 0; /* Clear transition count - downward */
375 pmsCtls
.pmsStats
[cpu
][i
].stCnt
[1] = 0; /* Clear transition count - forward */
379 (void)ml_set_interrupts_enabled(intr
); /* Restore interruptions */
383 * Control the Power Management Stepper.
384 * Called from user state by the superuser.
385 * Interruptions disabled.
389 pmsControl(uint32_t request
, user_addr_t reqaddr
, uint32_t reqsize
)
391 uint32_t nstep
= 0, result
, presult
;
393 kern_return_t kret
= KERN_SUCCESS
;
397 pp
= GET_PER_PROC_INFO(); /* Get our per_proc */
398 cpu
= cpu_number(); /* Get our processor */
400 if(!is_suser()) { /* We are better than most, */
405 if(request
>= pmsCFree
) { /* Can we understand the request? */
406 kret
= KERN_INVALID_ARGUMENT
;
410 if(request
== pmsCQuery
) { /* Are we just checking? */
411 result
= pmsCPUQuery() & pmsCPU
; /* Get the processor data and make sure there is no slop */
412 presult
= 0; /* Assume nothing */
413 if((uint32_t)pmsQueryFunc
)
414 presult
= pmsQueryFunc(cpu
, pmsPlatformData
); /* Go get the platform state */
415 kret
= result
| (presult
& (pmsXClk
| pmsVoltage
| pmsPowerID
)); /* Merge the platform state with no slop */
419 if(request
== pmsCExperimental
) { /* Enter experimental mode? */
421 if(pmsInstalled
|| (pmsExperimental
& 1)) { /* Are we already running or in experimental? */
426 pmsExperimental
|= 1; /* Flip us into experimental but don't change other flags */
428 pmsCPUConf(); /* Configure for this machine */
429 pmsStart(); /* Start stepping */
433 if(request
== pmsCCnfg
) { /* Do some up-front checking before we commit to doing this */
434 if((reqsize
> (pmsMaxStates
* sizeof(pmsDef
))) || (reqsize
< (pmsFree
* sizeof(pmsDef
)))) { /* Check that the size is reasonable */
435 kret
= KERN_NO_SPACE
;
440 if (request
== pmsGCtls
) {
441 if (reqsize
!= sizeof(pmsCtls
)) {
445 ret
= copyout(&pmsCtls
, reqaddr
, reqsize
);
449 if (request
== pmsGStats
) {
450 if (reqsize
!= sizeof(pmsStatsd
)) { /* request size is fixed */
454 ret
= copyout(&pmsStatsd
, reqaddr
, reqsize
);
459 * We are committed after here. If there are any errors detected, we shouldn't die, but we
460 * will be stuck in park.
462 * Also, we can possibly end up on another processor after the broadcast.
466 if(!hw_compare_and_store(0, 1, &pmsSyncrolator
)) { /* Are we already doing this? */
467 /* Tell them that we are already busy and to try again */
468 kret
= KERN_RESOURCE_SHORTAGE
;
472 // NOTE: We will block in the following code until everyone has finished the prepare
474 pmsRun(pmsPrepCng
); /* Get everyone parked and in a proper state for step table changes, including me */
476 if(request
== pmsCPark
) { /* Is all we're supposed to do park? */
477 pmsSyncrolator
= 0; /* Free us up */
481 switch(request
) { /* Select the routine */
483 case pmsCStart
: /* Starts normal steppping */
484 nstep
= pmsNormHigh
; /* Set the request */
487 case pmsCFLow
: /* Forces low power */
488 nstep
= pmsLow
; /* Set request */
491 case pmsCFHigh
: /* Forces high power */
492 nstep
= pmsHigh
; /* Set request */
495 case pmsCCnfg
: /* Loads new stepper program */
497 if(!(ndefs
= (pmsDef
*)kalloc(reqsize
))) { /* Get memory for the whole thing */
498 pmsSyncrolator
= 0; /* Free us up */
499 kret
= KERN_INVALID_ADDRESS
;
503 ret
= copyin(reqaddr
, (void *)ndefs
, reqsize
); /* Get the new config table */
504 if(ret
) { /* Hmmm, something went wrong with the copyin */
505 kfree(ndefs
, reqsize
); /* Free up the copied in data */
506 pmsSyncrolator
= 0; /* Free us up */
507 kret
= KERN_INVALID_ADDRESS
;
511 kret
= pmsBuild(ndefs
, reqsize
, NULL
, 0, NULL
); /* Go build and replace the tables. Make sure we keep the old platform stuff */
512 if(kret
) { /* Hmmm, something went wrong with the compilation */
513 kfree(ndefs
, reqsize
); /* Free up the copied in data */
514 pmsSyncrolator
= 0; /* Free us up */
518 nstep
= pmsNormHigh
; /* Set the request */
522 panic("pmsCntrl: stepper control is so very, very confused = %08X\n", request
);
526 pmsRun(nstep
); /* Get everyone into step */
527 pmsSyncrolator
= 0; /* Free us up */
534 * Broadcast a change to all processors including ourselves.
536 * Interruptions are disabled.
540 pmsRun(uint32_t nstep
)
547 * Build the tables needed for the stepper. This includes both the step definitions and the step control table.
549 * We most absolutely need to be parked before this happens because we're gonna change the table.
550 * We're going to have to be pretty complete about checking for errors.
551 * Also, a copy is always made because we don't want to be crippled by not being able to change
552 * the table or description formats.
554 * We pass in a table of external functions and the new stepper def uses the corresponding
555 * indexes rather than actual function addresses. This is done so that a proper table can be
556 * built with the control syscall. It can't supply addresses, so the index has to do. We
557 * internalize the table so our caller does not need to keep it. Note that passing in a 0
558 * will use the current function table. Also note that entry 0 is reserved and must be 0,
559 * we will check and fail the build.
561 * The platformData parameter is a 32-bit word of data that is passed unaltered to the set function.
563 * The queryFunc parameter is the address of a function that will return the current state of the platform.
564 * The format of the data returned is the same as the platform specific portions of pmsSetCmd, i.e., pmsXClk,
565 * pmsVoltage, and any part of pmsPowerID that is maintained by the platform hardware (an example would be
566 * the values of the gpios that correspond to pmsPowerID). The value should be constructed by querying
567 * hardware rather than returning a value cached by software. One of the intents of this function is to
568 * help recover lost or determine initial power states.
572 kern_return_t
pmsBuild(pmsDef
*pd
, uint32_t pdsize
, pmsSetFunc_t
*functab
, uint32_t platformData
, pmsQueryFunc_t queryFunc
) {
574 int newsize
, cstp
, oldAltSize
, xdsply
;
575 uint32_t setf
, steps
, i
, nstps
;
577 pmsDef
*newpd
, *oldAlt
;
580 xdsply
= (pmsExperimental
& 3) != 0; /* Turn on kprintfs if requested or in experimental mode */
582 if(pdsize
% sizeof(pmsDef
))
583 return KERN_INVALID_ARGUMENT
; /* Length not multiple of definition size */
585 steps
= pdsize
/ sizeof(pmsDef
); /* Get the number of steps supplied */
587 if((steps
>= pmsMaxStates
) || (steps
< pmsFree
)) /* Complain if too big or too small */
588 return KERN_INVALID_ARGUMENT
; /* Squeak loudly!!! */
590 if((uint32_t)functab
&& (uint32_t)functab
[0]) /* Verify that if they supplied a new function table, entry 0 is 0 */
591 return KERN_INVALID_ARGUMENT
; /* Fail because they didn't reserve entry 0 */
593 if(xdsply
) kprintf("\n StepID Down Next HWSel HWfun Limit\n");
595 for(i
= 0; i
< steps
; i
++) { /* Step through and verify the definitions */
597 if(xdsply
) kprintf(" %6d %6d %6d %08X %6d %20lld\n", pd
[i
].pmsStepID
, pd
[i
].pmsDown
,
598 pd
[i
].pmsNext
, pd
[i
].pmsSetCmd
,
599 pd
[i
].sf
.pmsSetFuncInd
, pd
[i
].pmsLimit
);
601 if((pd
[i
].pmsLimit
!= 0) && (pd
[i
].pmsLimit
< 100ULL)) {
602 if(xdsply
) kprintf("error step %3d: pmsLimit too small/n", i
);
603 return KERN_INVALID_ARGUMENT
; /* Has to be 100µS or more */
606 if((pd
[i
].pmsLimit
!= 0xFFFFFFFFFFFFFFFFULL
) && (pd
[i
].pmsLimit
> (HalfwayToForever
/ 1000ULL))) {
607 if(xdsply
) kprintf("error step %3d: pmsLimit too big\n", i
);
608 return KERN_INVALID_ARGUMENT
; /* Can't be too big */
611 if(pd
[i
].pmsStepID
!= i
) {
612 if(xdsply
) kprintf("error step %3d: step ID does not match (%d)\n", i
, pd
[i
].pmsStepID
);
613 return KERN_INVALID_ARGUMENT
; /* ID must match */
616 if(pd
[i
].sf
.pmsSetFuncInd
>= pmsSetFuncMax
) {
617 if(xdsply
) kprintf("error step %3d: function invalid (%d)\n", i
, pd
[i
].sf
.pmsSetFuncInd
);
618 return KERN_INVALID_ARGUMENT
; /* Fail if this function is not in the table */
621 if((pd
[i
].pmsDown
!= pmsParked
) && pd
[i
].pmsDown
>= steps
) {
622 if(xdsply
) kprintf("error step %3d: pmsDown out of range (%d)\n", i
, pd
[i
].pmsDown
);
623 return KERN_INVALID_ARGUMENT
; /* Step down must be in the table or park */
626 if((pd
[i
].pmsNext
!= pmsParked
) && pd
[i
].pmsNext
>= steps
) {
627 if(xdsply
) kprintf("error step %3d: pmsNext out of range (%d)\n", i
, pd
[i
].pmsNext
);
628 return KERN_INVALID_ARGUMENT
; /* Step up must be in the table or park */
631 if((pd
[i
].pmsSetCmd
== pmsDelay
) && (pd
[i
].pmsTDelay
>= steps
)) {
632 if(xdsply
) kprintf("error step %3d: pmsTDelay out of range (%d)\n", i
, pd
[i
].pmsTDelay
);
633 return KERN_INVALID_ARGUMENT
; /* Delayed step must be in the table */
636 if((pd
[i
].pmsSetCmd
== pmsDelay
) && (pd
[i
].pmsLimit
== 0xFFFFFFFFFFFFFFFFULL
)) {
637 if(xdsply
) kprintf("error step %3d: delay time limit must not be infinite\n", i
);
638 return KERN_INVALID_ARGUMENT
; /* Delayed step must have a time limit */
644 * Verify that there are no infinite synchronous forward loops in the table
647 if(xdsply
) kprintf("\nInitial scan passed, start in loop check\n");
648 for(i
= 0; i
< steps
; i
++) { /* Start with each step. Inefficient, but who cares */
650 cstp
= i
; /* Set starting point */
651 nstps
= 0; /* Initialize chain length counter */
652 while(1) { /* Do until we hit the end */
653 if(pd
[cstp
].pmsSetCmd
== pmsParkIt
) break; /* Parking always terminates a chain so no endless loop here */
654 if(pd
[cstp
].pmsSetCmd
== pmsDelay
) break; /* Delayed steps always terminate a chain so no endless loop here */
655 if((pd
[cstp
].pmsLimit
!= 0) && ((pd
[cstp
].pmsSetCmd
& pmsSync
) != pmsSync
)) break; /* If time limit is not 0 and not synchrouous, no endless loop */
656 if(pd
[cstp
].pmsNext
== pmsParked
) break; /* If the next step is parked, no endless loop */
658 cstp
= pd
[cstp
].pmsNext
; /* Chain to the next */
659 nstps
= nstps
+ 1; /* Count this step */
660 if(nstps
>= steps
) { /* We've stepped for more steps than we have, must be an endless loop! */
661 if(xdsply
) kprintf("error step %3d: infinite pmsNext loop\n", i
);
662 return KERN_INVALID_ARGUMENT
; /* Suggest to our caller that they can't program... */
667 if((pmsExperimental
& 4) && (pmsInstalled
) && ((uint32_t)functab
!= 0)) { /* If we are already initted and experimental is locked in, and we are doing first */
668 if(xdsply
) kprintf("Experimental locked, ignoring driver pmsBuild\n");
669 return KERN_RESOURCE_SHORTAGE
; /* Just ignore the request. */
675 * Well, things look ok, let's do it to it...
678 if(xdsply
) kprintf("Loop check passed, building and installing table\n");
680 newsize
= steps
* sizeof(pmsDef
); /* Get the size needed for the definition blocks */
682 if(!(newpd
= (pmsDef
*)kalloc(newsize
))) { /* Get memory for the whole thing */
683 return KERN_RESOURCE_SHORTAGE
; /* No storage... */
686 bzero((void *)newpd
, newsize
); /* Make it pretty */
689 * Ok, this is it, finish intitializing, switch the tables, and pray...
690 * We want no interruptions at all and we need to lock the table. Everybody should be parked,
691 * so no one should ever touch this. The lock is to keep multiple builders safe. It probably
692 * will never ever happen, but paranoia is a good thing...
695 intr
= ml_set_interrupts_enabled(FALSE
); /* No interruptions in here */
696 simple_lock(&pmsBuildLock
); /* Lock out everyone... */
698 if(platformData
) pmsPlatformData
= platformData
; /* Remember the platform data word passed in if any was... */
699 if((uint32_t)queryFunc
) pmsQueryFunc
= queryFunc
; /* Remember the query function passed in, if it was... */
701 oldAlt
= altDpmsTab
; /* Remember any old alternate we had */
702 oldAltSize
= altDpmsTabSize
; /* Remember its size */
704 altDpmsTab
= newpd
; /* Point to the new table */
705 altDpmsTabSize
= newsize
; /* Set the size */
707 if((uint32_t)functab
) { /* Did we get a new function table? */
708 for(i
= 0; i
< pmsSetFuncMax
; i
++) pmsFuncTab
[i
] = functab
[i
]; /* Copy in the new table */
711 for(i
= 0; i
< pmsMaxStates
; i
++) pmsCtls
.pmsDefs
[i
] = &pmsDummy
; /* Initialize the table to point to the dummy step */
713 for(i
= 0; i
< steps
; i
++) { /* Replace the step table entries */
714 if(pd
[i
].pmsLimit
== 0xFFFFFFFFFFFFFFFFULL
) nlimit
= century
; /* Default to 100 years */
715 else nlimit
= pd
[i
].pmsLimit
; /* Otherwise use what was supplied */
717 nanoseconds_to_absolutetime(nlimit
* 1000ULL, &newpd
[i
].pmsLimit
); /* Convert microseconds to nanoseconds and then to ticks */
719 setf
= pd
[i
].sf
.pmsSetFuncInd
; /* Make convienient */
720 newpd
[i
].sf
.pmsSetFunc
= pmsFuncTab
[setf
]; /* Replace the index with the function address */
722 newpd
[i
].pmsStepID
= pd
[i
].pmsStepID
; /* Set the step ID */
723 newpd
[i
].pmsSetCmd
= pd
[i
].pmsSetCmd
; /* Set the hardware selector ID */
724 newpd
[i
].pmsDown
= pd
[i
].pmsDown
; /* Set the downward step */
725 newpd
[i
].pmsNext
= pd
[i
].pmsNext
; /* Set the next setp */
726 newpd
[i
].pmsTDelay
= pd
[i
].pmsTDelay
; /* Set the delayed setp */
727 pmsCtls
.pmsDefs
[i
] = &newpd
[i
]; /* Copy it in */
730 pmsCtlp
= (uint32_t)&pmsCtls
; /* Point to the new pms table */
732 pmsInstalled
= 1; /* The stepper has been born or born again... */
734 simple_unlock(&pmsBuildLock
); /* Free play! */
735 (void)ml_set_interrupts_enabled(intr
); /* Interrupts back the way there were */
737 if((uint32_t)oldAlt
) /* If we already had an alternate, free it */
738 kfree(oldAlt
, oldAltSize
);
740 if(xdsply
) kprintf("Stepper table installed\n");
742 return KERN_SUCCESS
; /* We're in fate's hands now... */