]> git.saurik.com Git - apple/xnu.git/blob - osfmk/kern/pms.c
c8dd6521669efb53ff69020f76cdeb07a36fa577
[apple/xnu.git] / osfmk / kern / pms.c
1 /*
2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
11 *
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
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 #include <machine/machine_routines.h>
23 #include <machine/machine_cpu.h>
24 #ifdef __ppc__
25 # include <ppc/exception.h>
26 # include <ppc/misc_protos.h>
27 #else
28 # include <i386/cpu_data.h>
29 # include <i386/misc_protos.h>
30 #endif
31 #include <machine/pmap.h>
32 #include <kern/pms.h>
33 #include <kern/processor.h>
34 #include <kern/kalloc.h>
35 #include <vm/vm_protos.h>
36
37 static uint32_t pmsSyncrolator = 0; /* Only one control operation at a time please */
38 uint32_t pmsBroadcastWait = 0; /* Number of outstanding broadcasts */
39
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 */
43
44 static pmsDef *altDpmsTab = 0; /* Alternate step definition table */
45 static uint32_t altDpmsTabSize = 0; /* Size of alternate step definition table */
46
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 */
54 };
55
56 pmsStat pmsStatsd[4][pmsMaxStates]; /* Generate enough statistics blocks for 4 processors */
57
58 pmsCtl pmsCtls = { /* Power Management Stepper control */
59 .pmsStats = &pmsStatsd
60 };
61
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 */
65
66 #ifdef __ppc__
67 # define PER_PROC_INFO struct per_proc_info
68 # define GET_PER_PROC_INFO() getPerProc()
69 #else
70 # define PER_PROC_INFO cpu_data_t
71 # define GET_PER_PROC_INFO() current_cpu_datap()
72 #endif
73
74
75
76 /*
77 * Do any initialization needed
78 */
79
80 void pmsInit(void) {
81
82 int i;
83
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 */
86
87 pmsCPUMachineInit();
88
89 return;
90 }
91
92
93 /*
94 * Start the power management stepper on all processors
95 *
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.
98 *
99 */
100
101 void pmsStart(void) {
102
103 boolean_t intr;
104
105 if(!pmsInstalled) return; /* We can't do this if no table installed */
106
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 */
110
111 return;
112
113 }
114
115
116 /*
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.
120 *
121 * This is used as the initial state at startup and when the step table
122 * is being changed.
123 *
124 */
125
126 void pmsPark(void) {
127
128 boolean_t intr;
129
130 if(!pmsInstalled) return; /* We can't do this if no table installed */
131
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 */
135
136 return;
137
138 }
139
140
141 /*
142 * Steps down to a lower power.
143 * Interrupts must be off...
144 */
145
146 void pmsDown(void) {
147
148 PER_PROC_INFO *pp;
149 uint32_t nstate;
150
151 pp = GET_PER_PROC_INFO(); /* Get our per_proc */
152
153 if(!pmsInstalled || pp->pms.pmsState == pmsParked) return; /* No stepping if parked or not installed */
154
155 nstate = pmsCtls.pmsDefs[pp->pms.pmsState]->pmsDown; /* Get the downward step */
156 pmsSetStep(nstate, 0); /* Step to it */
157 return;
158 }
159
160
161 /*
162 * Steps up to a higher power. The "timer" parameter is true if the
163 * step was driven due to the pms timer expiring.
164 *
165 * Interrupts must be off...
166 */
167
168 int pmsStepIdleSneaks;
169 int pmsStepIdleTries;
170
171 void pmsStep(int timer) {
172
173 PER_PROC_INFO *pp;
174 uint32_t nstate;
175 uint32_t tstate;
176 uint32_t pkgstate;
177 int dir;
178 int i;
179
180 pp = GET_PER_PROC_INFO(); /* Get our per_proc */
181
182 if(!pmsInstalled || pp->pms.pmsState == pmsParked)
183 return; /* No stepping if parked or not installed */
184
185 /*
186 * Assume a normal step.
187 */
188 nstate = pmsCtls.pmsDefs[pp->pms.pmsState]->pmsNext;
189
190 /*
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.
195 */
196 if ((pp->pms.pmsState == pmsIdle)
197 && ((pkgstate = pmsCPUPackageQuery()) != ~(uint32_t)0)) {
198 /*
199 * Search forward through the stepper program,
200 * avoid looping for too long.
201 */
202 tstate = nstate;
203 pmsStepIdleTries++;
204 for (i = 0; i < 32; i++) {
205 /*
206 * Compare command with current package state
207 */
208 if ((pmsCtls.pmsDefs[tstate]->pmsSetCmd & pmsCPU) == pkgstate) {
209 nstate = tstate;
210 pmsStepIdleSneaks++;
211 break;
212 }
213
214 /*
215 * Advance to the next step in the program.
216 */
217 if (pmsCtls.pmsDefs[tstate]->pmsNext == tstate)
218 break; /* infinite loop */
219 tstate = pmsCtls.pmsDefs[tstate]->pmsNext;
220 }
221 }
222
223 /*
224 * Default to a step up.
225 */
226 dir = 1;
227
228 /*
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
231 * purposes.
232 */
233 if (timer
234 && (pmsCtls.pmsDefs[pp->pms.pmsState]->pmsSetCmd == pmsDelay)) {
235 nstate = pmsCtls.pmsDefs[pp->pms.pmsState]->pmsTDelay;
236
237 /*
238 * Delayed steps are a step down for accounting purposes.
239 */
240 dir = 0;
241 }
242
243 pmsSetStep(nstate, dir);
244 return;
245 }
246
247
248 /*
249 * Set a specific step
250 *
251 * We do not do statistics if exiting park
252 * Interrupts must be off...
253 *
254 */
255
256 void pmsSetStep(uint32_t nstep, int dir) {
257
258 PER_PROC_INFO *pp;
259 uint32_t pstate, nCSetCmd, mCSetCmd;
260 pmsDef *pnstate, *pcstate;
261 uint64_t tb, dur;
262 int cpu;
263
264 pp = GET_PER_PROC_INFO(); /* Get our per_proc */
265 cpu = cpu_number(); /* Get our processor */
266
267 while(1) { /* Keep stepping until we get a delay */
268
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... */
271 }
272
273 if((nstep == pmsParked) || ((uint32_t)pmsCtls.pmsDefs[nstep]->pmsSetCmd == pmsParkIt)) { /* Are we parking? */
274
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 */
280 return;
281 }
282
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 */
286
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 */
290
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 */
295
296 pp->pms.pmsCSetCmd = nCSetCmd; /* Set it for real */
297 }
298
299 tb = mach_absolute_time(); /* What time is it? */
300 pp->pms.pmsPop = tb + pnstate->pmsLimit; /* Set the next pop */
301
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... */
304 }
305
306 /*
307 * Gather some statistics
308 */
309
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 */
316 }
317
318 /*
319 * See if we are done chaining steps
320 */
321
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... */
326 }
327
328 nstep = pnstate->pmsNext; /* Chain on to the next */
329 }
330
331 return;
332
333 }
334
335 /*
336 * Either park the stepper or force the step on a parked stepper for local processor only
337 *
338 */
339
340 void pmsRunLocal(uint32_t nstep) {
341
342 PER_PROC_INFO *pp;
343 uint32_t lastState;
344 int cpu, i;
345 boolean_t intr;
346
347 if(!pmsInstalled) return; /* Ignore this if no step programs installed... */
348
349 intr = ml_set_interrupts_enabled(FALSE); /* No interruptions in here */
350
351 pp = GET_PER_PROC_INFO(); /* Get our per_proc */
352
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 */
356 }
357
358 lastState = pp->pms.pmsState; /* Remember if we are parked now */
359
360 pmsSetStep(nstep, 1); /* Step to the new state */
361
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 */
369 }
370 }
371
372 (void)ml_set_interrupts_enabled(intr); /* Restore interruptions */
373
374 return;
375
376 }
377
378 /*
379 * Control the Power Management Stepper.
380 * Called from user state by the superuser.
381 * Interruptions disabled.
382 *
383 */
384 kern_return_t pmsControl(uint32_t request, user_addr_t reqaddr, uint32_t reqsize) {
385
386 uint32_t nstep, result, presult;
387 int ret, cpu;
388 kern_return_t kret;
389 pmsDef *ndefs;
390 PER_PROC_INFO *pp;
391
392 pp = GET_PER_PROC_INFO(); /* Get our per_proc */
393 cpu = cpu_number(); /* Get our processor */
394
395 if(!is_suser()) { /* We are better than most, */
396 return KERN_FAILURE; /* so we will only talk to the superuser. */
397 }
398
399 if(request >= pmsCFree) { /* Can we understand the request? */
400 return KERN_INVALID_ARGUMENT; /* What language are these guys talking in, anyway? */
401 }
402
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... */
409 }
410
411 if(request == pmsCExperimental) { /* Enter experimental mode? */
412
413 if(pmsInstalled || (pmsExperimental & 1)) { /* Are we already running or in experimental? */
414 return KERN_FAILURE; /* Fail, since we are already running */
415 }
416
417 pmsExperimental |= 1; /* Flip us into experimental but don't change other flags */
418
419 pmsCPUConf(); /* Configure for this machine */
420 pmsStart(); /* Start stepping */
421 return KERN_SUCCESS; /* We are victorious... */
422
423 }
424
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 */
428 }
429 }
430
431 if (request == pmsGCtls) {
432 if (reqsize != sizeof(pmsCtls))
433 return(KERN_FAILURE);
434 ret = copyout(&pmsCtls, reqaddr, reqsize);
435 return kret;
436 }
437
438 if (request == pmsGStats) {
439 if (reqsize != sizeof(pmsStatsd)) /* request size is fixed */
440 return KERN_FAILURE;
441 ret = copyout(&pmsStatsd, reqaddr, reqsize);
442 return kret; /* All done... */
443 }
444
445 /*
446 * We are committed after here. If there are any errors detected, we shouldn't die, but we
447 * will be stuck in park.
448 *
449 * Also, we can possibly end up on another processor after the broadcast.
450 *
451 */
452
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 */
455 }
456
457 // NOTE: We will block in the following code until everyone has finished the prepare
458
459 pmsRun(pmsPrepCng); /* Get everyone parked and in a proper state for step table changes, including me */
460
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... */
464 }
465
466 switch(request) { /* Select the routine */
467
468 case pmsCStart: /* Starts normal steppping */
469 nstep = pmsNormHigh; /* Set the request */
470 break;
471
472 case pmsCFLow: /* Forces low power */
473 nstep = pmsLow; /* Set request */
474 break;
475
476 case pmsCFHigh: /* Forces high power */
477 nstep = pmsHigh; /* Set request */
478 break;
479
480 case pmsCCnfg: /* Loads new stepper program */
481
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... */
485 }
486
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... */
492 }
493
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... */
499 }
500
501 nstep = pmsNormHigh; /* Set the request */
502 break;
503
504 default:
505 panic("pmsCntrl: stepper control is so very, very confused = %08X\n", request);
506
507 }
508
509 pmsRun(nstep); /* Get everyone into step */
510 pmsSyncrolator = 0; /* Free us up */
511 return KERN_SUCCESS; /* All done... */
512
513 }
514
515 /*
516 * Broadcast a change to all processors including ourselves.
517 *
518 * Interruptions are disabled.
519 */
520
521 void pmsRun(uint32_t nstep) {
522
523 pmsCPURun(nstep);
524 }
525
526
527 /*
528 * Build the tables needed for the stepper. This includes both the step definitions and the step control table.
529 *
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.
534 *
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.
541 *
542 * The platformData parameter is a 32-bit word of data that is passed unaltered to the set function.
543 *
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.
550 *
551 */
552
553 kern_return_t pmsBuild(pmsDef *pd, uint32_t pdsize, pmsSetFunc_t *functab, uint32_t platformData, pmsQueryFunc_t queryFunc) {
554
555 int steps, newsize, i, cstp, nstps, oldAltSize, xdsply;
556 uint32_t setf;
557 uint64_t nlimit;
558 pmsDef *newpd, *oldAlt;
559 boolean_t intr;
560
561 xdsply = (pmsExperimental & 3) != 0; /* Turn on kprintfs if requested or in experimental mode */
562
563 if(pdsize % sizeof(pmsDef)) return KERN_INVALID_ARGUMENT; /* Length not multiple of definition size */
564
565 steps = pdsize / sizeof(pmsDef); /* Get the number of steps supplied */
566
567 if((steps >= pmsMaxStates) || (steps < pmsFree)) /* Complain if too big or too small */
568 return KERN_INVALID_ARGUMENT; /* Squeak loudly!!! */
569
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 */
572
573 if(xdsply) kprintf("\n StepID Down Next HWSel HWfun Limit\n");
574
575 for(i = 0; i < steps; i++) { /* Step through and verify the definitions */
576
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);
580
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 */
584 }
585
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 */
589 }
590
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 */
594 }
595
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 */
599 }
600
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 */
604 }
605
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 */
609 }
610
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 */
614 }
615
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 */
619 }
620
621 }
622
623 /*
624 * Verify that there are no infinite synchronous forward loops in the table
625 */
626
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 */
629
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 */
637
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... */
643 }
644 }
645 }
646
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. */
650 }
651
652
653
654 /*
655 * Well, things look ok, let's do it to it...
656 */
657
658 if(xdsply) kprintf("Loop check passed, building and installing table\n");
659
660 newsize = steps * sizeof(pmsDef); /* Get the size needed for the definition blocks */
661
662 if(!(newpd = (pmsDef *)kalloc(newsize))) { /* Get memory for the whole thing */
663 return KERN_RESOURCE_SHORTAGE; /* No storage... */
664 }
665
666 bzero((void *)newpd, newsize); /* Make it pretty */
667
668 /*
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...
673 */
674
675 intr = ml_set_interrupts_enabled(FALSE); /* No interruptions in here */
676 simple_lock(&pmsBuildLock); /* Lock out everyone... */
677
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... */
680
681 oldAlt = altDpmsTab; /* Remember any old alternate we had */
682 oldAltSize = altDpmsTabSize; /* Remember its size */
683
684 altDpmsTab = newpd; /* Point to the new table */
685 altDpmsTabSize = newsize; /* Set the size */
686
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 */
689 }
690
691 for(i = 0; i < pmsMaxStates; i++) pmsCtls.pmsDefs[i] = &pmsDummy; /* Initialize the table to point to the dummy step */
692
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 */
696
697 nanoseconds_to_absolutetime(nlimit * 1000ULL, &newpd[i].pmsLimit); /* Convert microseconds to nanoseconds and then to ticks */
698
699 setf = pd[i].sf.pmsSetFuncInd; /* Make convienient */
700 newpd[i].sf.pmsSetFunc = pmsFuncTab[setf]; /* Replace the index with the function address */
701
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 */
708 }
709 #ifdef __ppc__
710 pmsCtlp = (uint32_t)&pmsCtls; /* Point to the new pms table */
711 #endif
712 pmsInstalled = 1; /* The stepper has been born or born again... */
713
714 simple_unlock(&pmsBuildLock); /* Free play! */
715 (void)ml_set_interrupts_enabled(intr); /* Interrupts back the way there were */
716
717 if((uint32_t)oldAlt) kfree(oldAlt, oldAltSize); /* If we already had an alternate, free it */
718
719 if(xdsply) kprintf("Stepper table installed\n");
720
721 return KERN_SUCCESS; /* We're in fate's hands now... */
722 }