]> git.saurik.com Git - apple/xnu.git/blob - osfmk/ppc/pms.c
ccdd50b2df2f7a09764e64cdf01758763c29123f
[apple/xnu.git] / osfmk / ppc / pms.c
1 /*
2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
14 * agreement.
15 *
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
18 * file.
19 *
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
27 *
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
29 */
30 #include <ppc/machine_routines.h>
31 #include <ppc/machine_cpu.h>
32 #include <ppc/exception.h>
33 #include <ppc/misc_protos.h>
34 #include <ppc/Firmware.h>
35 #include <ppc/pmap.h>
36 #include <ppc/proc_reg.h>
37 #include <ppc/pms.h>
38 #include <ppc/savearea.h>
39 #include <ppc/exception.h>
40 #include <kern/processor.h>
41
42 extern int real_ncpus;
43
44 static uint32_t pmsSyncrolator = 0; /* Only one control operation at a time please */
45 uint32_t pmsBroadcastWait = 0; /* Number of outstanding broadcasts */
46
47 int pmsInstalled = 0; /* Power Management Stepper can run and has table installed */
48 int pmsExperimental = 0; /* Power Management Stepper in experimental mode */
49 decl_simple_lock_data(,pmsBuildLock) /* Make sure only one guy can replace table at the same time */
50
51 static pmsDef *altDpmsTab = 0; /* Alternate step definition table */
52 static uint32_t altDpmsTabSize = 0; /* Size of alternate step definition table */
53
54 pmsDef pmsDummy = { /* This is the dummy step for initialization. All it does is to park */
55 .pmsLimit = 0, /* Time doesn't matter for a park */
56 .pmsStepID = pmsMaxStates - 1, /* Use the very last ID number for the dummy */
57 .pmsSetCmd = pmsParkIt, /* Force us to be parked */
58 .sf.pmsSetFuncInd = 0, /* No platform call for this one */
59 .pmsDown = pmsPrepSleep, /* We always park */
60 .pmsNext = pmsPrepSleep /* We always park */
61 };
62
63 pmsStat pmsStatsd[4][pmsMaxStates]; /* Generate enough statistics blocks for 4 processors */
64
65 pmsCtl pmsCtls = { /* Power Management Stepper control */
66 .pmsStats = &pmsStatsd
67 };
68
69 pmsSetFunc_t pmsFuncTab[pmsSetFuncMax] = {0}; /* This is the function index table */
70 pmsQueryFunc_t pmsQueryFunc = 0; /* Pointer to pmsQuery function */
71 uint32_t pmsPlatformData = 0; /* Data provided by and passed to platform functions */
72
73
74 /*
75 * Do any initialization needed
76 */
77
78 void pmsInit(void) {
79
80 int i;
81
82 simple_lock_init(&pmsBuildLock, 0); /* Initialize the build lock */
83 for(i = 0; i < pmsMaxStates; i++) pmsCtls.pmsDefs[i] = &pmsDummy; /* Initialize the table to dummy steps */
84
85 return;
86 }
87
88
89 /*
90 * Start the power management stepper on all processors
91 *
92 * All processors must be parked. This should be called when the hardware
93 * is ready to step. Probably only at boot and after wake from sleep.
94 *
95 */
96
97 void pmsStart(void) {
98
99 boolean_t intr;
100
101 if(!pmsInstalled) return; /* We can't do this if no table installed */
102
103 intr = ml_set_interrupts_enabled(FALSE); /* No interruptions in here */
104 pmsRun(pmsStartUp); /* Start running the stepper everywhere */
105 (void)ml_set_interrupts_enabled(intr); /* Restore interruptions */
106
107 return;
108
109 }
110
111
112 /*
113 * Park the stepper execution. This will force the stepper on this
114 * processor to abandon its current step and stop. No changes to the
115 * hardware state is made and any previous step is lost.
116 *
117 * This is used as the initial state at startup and when the step table
118 * is being changed.
119 *
120 */
121
122 void pmsPark(void) {
123
124 boolean_t intr;
125
126 if(!pmsInstalled) return; /* We can't do this if no table installed */
127
128 intr = ml_set_interrupts_enabled(FALSE); /* No interruptions in here */
129 pmsSetStep(pmsParked, 0); /* Park the stepper */
130 (void)ml_set_interrupts_enabled(intr); /* Restore interruptions */
131
132 return;
133
134 }
135
136
137 /*
138 * Steps down to a lower power.
139 * Interrupts must be off...
140 */
141
142 void pmsDown(void) {
143
144 struct per_proc_info *pp;
145 uint32_t nstate;
146
147 pp = getPerProc(); /* Get our per_proc */
148
149 if(!pmsInstalled || pp->pms.pmsState == pmsParked) return; /* No stepping if parked or not installed */
150
151 nstate = pmsCtls.pmsDefs[pp->pms.pmsState]->pmsDown; /* Get the downward step */
152 pmsSetStep(nstate, 0); /* Step to it */
153 return;
154 }
155
156
157 /*
158 * Steps up to a higher power. The "timer" parameter is true if the
159 * step was driven due to the pms timer expiring.
160 *
161 * Interrupts must be off...
162 */
163
164 void pmsStep(int timer) {
165
166 struct per_proc_info *pp;
167 uint32_t nstate;
168 int dir;
169
170 pp = getPerProc(); /* Get our per_proc */
171
172 if(!pmsInstalled || pp->pms.pmsState == pmsParked) return; /* No stepping if parked or not installed */
173
174 nstate = pmsCtls.pmsDefs[pp->pms.pmsState]->pmsNext; /* Assume a normal step */
175 dir = 1; /* A normal step is a step up */
176
177 if(timer && (pmsCtls.pmsDefs[pp->pms.pmsState]->pmsSetCmd == pmsDelay)) { /* If the timer expired and we are in a delay step, use the delay branch */
178 nstate = pmsCtls.pmsDefs[pp->pms.pmsState]->pmsTDelay; /* Get the delayed step */
179 dir = 0; /* Delayed steps are a step down for accounting purposes. */
180 }
181
182 pmsSetStep(nstate, dir); /* Step to it */
183 return;
184 }
185
186
187 /*
188 * Set a specific step
189 *
190 * We do not do statistics if exiting park
191 * Interrupts must be off...
192 *
193 */
194
195 void pmsSetStep(uint32_t nstep, int dir) {
196
197 struct per_proc_info *pp;
198 uint32_t pstate, ret, nCSetCmd, mCSetCmd;
199 pmsDef *pnstate, *pcstate;
200 uint64_t tb, nt, dur;
201 int cpu, frompark;
202
203 pp = getPerProc(); /* Get our per_proc */
204 cpu = cpu_number(); /* Get our processor */
205
206 while(1) { /* Keep stepping until we get a delay */
207
208 if(pp->pms.pmsCSetCmd & pmsMustCmp) { /* Do we have to finish the delay before changing? */
209 while(mach_absolute_time() < pp->pms.pmsPop); /* Yes, spin here... */
210 }
211
212 if((nstep == pmsParked) || ((uint32_t)pmsCtls.pmsDefs[nstep]->pmsSetCmd == pmsParkIt)) { /* Are we parking? */
213
214 tb = mach_absolute_time(); /* What time is it? */
215 pp->pms.pmsStamp = tb; /* Show transition now */
216 pp->pms.pmsPop = HalfwayToForever; /* Set the pop way into the future */
217 pp->pms.pmsState = pmsParked; /* Make sure we are parked */
218 setTimerReq(); /* Cancel our timer if going */
219 return;
220 }
221
222 pnstate = pmsCtls.pmsDefs[nstep]; /* Point to the state definition */
223 pstate = pp->pms.pmsState; /* Save the current step */
224 pp->pms.pmsState = nstep; /* Set the current to the next step */
225
226 if(pnstate->pmsSetCmd != pmsDelay) { /* If this is not a delayed state, change the actual hardware now */
227 if(pnstate->pmsSetCmd & pmsCngCPU) pmsCPUSet(pnstate->pmsSetCmd); /* We have some CPU work to do... */
228 if((uint32_t)pnstate->sf.pmsSetFunc) pnstate->sf.pmsSetFunc(pnstate->pmsSetCmd, cpu, pmsPlatformData); /* Tell the platform to set power mode */
229
230 mCSetCmd = pnstate->pmsSetCmd & (pmsCngXClk | pmsCngCPU | pmsCngVolt); /* Isolate just the change flags */
231 mCSetCmd = (mCSetCmd - (mCSetCmd >> 7)) | pmsSync | pmsMustCmp | pmsPowerID; /* Form mask of bits that come from new command */
232 nCSetCmd = pp->pms.pmsCSetCmd & ~mCSetCmd; /* Clear changing bits */
233 nCSetCmd = nCSetCmd | (pnstate->pmsSetCmd & mCSetCmd); /* Flip on the changing bits and the always copy bits */
234
235 pp->pms.pmsCSetCmd = nCSetCmd; /* Set it for real */
236 }
237
238 tb = mach_absolute_time(); /* What time is it? */
239 pp->pms.pmsPop = tb + pnstate->pmsLimit; /* Set the next pop */
240
241 if((pnstate->pmsSetCmd != pmsDelay) && (pp->pms.pmsCSetCmd & pmsSync) && (pnstate->pmsLimit != 0)) { /* Is this a synchronous command with a delay? */
242 while(mach_absolute_time() < pp->pms.pmsPop); /* Yes, spin here and wait it out... */
243 }
244
245 /*
246 * Gather some statistics
247 */
248
249 dur = tb - pp->pms.pmsStamp; /* Get the amount of time we were in the old step */
250 pp->pms.pmsStamp = tb; /* Set the new timestamp */
251 if(!(pstate == pmsParked)) { /* Only take stats if we were not parked */
252 pcstate = pmsCtls.pmsDefs[pstate]; /* Get the previous step */
253 pmsCtls.pmsStats[cpu][pcstate->pmsStepID].stTime[dir] += dur; /* Accumulate the total time in the old step */
254 pmsCtls.pmsStats[cpu][pcstate->pmsStepID].stCnt[dir] += 1; /* Count transitions */
255 }
256
257 /*
258 * See if we are done chaining steps
259 */
260
261 if((pnstate->pmsSetCmd == pmsDelay)
262 || (!(pp->pms.pmsCSetCmd & pmsSync) && (pnstate->pmsLimit != 0))) { /* Is this not syncronous and a non-zero delay or a delayed step? */
263 setTimerReq(); /* Start the timers ticking */
264 break; /* We've stepped as far as we're going to... */
265 }
266
267 nstep = pnstate->pmsNext; /* Chain on to the next */
268 }
269
270 return;
271
272 }
273
274 /*
275 * Either park the stepper or force the step on a parked stepper for local processor only
276 *
277 */
278
279 void pmsRunLocal(uint32_t nstep) {
280
281 struct per_proc_info *pp;
282 uint32_t cstate, ret, lastState;
283 pmsDef *pnstate, *pcstate;
284 uint64_t tb, nt, dur;
285 int cpu, i, j;
286 boolean_t intr;
287
288 if(!pmsInstalled) return; /* Ignore this if no step programs installed... */
289
290 intr = ml_set_interrupts_enabled(FALSE); /* No interruptions in here */
291
292 pp = getPerProc(); /* Get our per_proc */
293
294 if(nstep == pmsStartUp) { /* Should we start up? */
295 pmsCPUInit(); /* Get us up to full with high voltage and park */
296 nstep = pmsNormHigh; /* Change request to transition to normal high */
297 }
298
299 lastState = pp->pms.pmsState; /* Remember if we are parked now */
300
301 pmsSetStep(nstep, 1); /* Step to the new state */
302
303 if((lastState == pmsParked) && (pp->pms.pmsState != pmsParked)) { /* Did we just unpark? */
304 cpu = cpu_number(); /* Get our processor */
305 for(i = 0; i < pmsMaxStates; i++) { /* Step through the steps and clear the statistics since we were parked */
306 pmsCtls.pmsStats[cpu][i].stTime[0] = 0; /* Clear accumulated time - downward */
307 pmsCtls.pmsStats[cpu][i].stTime[1] = 0; /* Clear accumulated time - forward */
308 pmsCtls.pmsStats[cpu][i].stCnt[0] = 0; /* Clear transition count - downward */
309 pmsCtls.pmsStats[cpu][i].stCnt[1] = 0; /* Clear transition count - forward */
310 }
311 }
312
313 (void)ml_set_interrupts_enabled(intr); /* Restore interruptions */
314
315 return;
316
317 }
318
319 /*
320 * Control the Power Management Stepper.
321 * Called from user state by the superuser via a ppc system call.
322 * Interruptions disabled.
323 *
324 */
325
326 int pmsCntrl(struct savearea *save) {
327
328 uint32_t request, nstep, reqsize, result, presult;
329 int ret, cpu;
330 kern_return_t kret;
331 pmsDef *ndefs;
332 struct per_proc_info *pp;
333
334 pp = getPerProc(); /* Get our per_proc */
335 cpu = cpu_number(); /* Get our processor */
336
337 if(!is_suser()) { /* We are better than most, */
338 save->save_r3 = KERN_FAILURE; /* so we will only talk to the superuser. */
339 return 1; /* Turn up our noses, say "harrumph," and walk away... */
340 }
341
342 if(save->save_r3 >= pmsCFree) { /* Can we understand the request? */
343 save->save_r3 = KERN_INVALID_ARGUMENT; /* What language are these guys talking in, anyway? */
344 return 1; /* Cock head like a confused puppy and run away... */
345 }
346
347 request = (int)save->save_r3; /* Remember the request */
348 reqsize = (uint32_t)save->save_r5; /* Get the size of the config table */
349
350 if(request == pmsCQuery) { /* Are we just checking? */
351 result = pmsCPUquery() & pmsCPU; /* Get the processor data and make sure there is no slop */
352 presult = 0; /* Assume nothing */
353 if((uint32_t)pmsQueryFunc) presult = pmsQueryFunc(cpu, pmsPlatformData); /* Go get the platform state */
354 result = result | (presult & (pmsXClk | pmsVoltage | pmsPowerID)); /* Merge the platform state with no slop */
355 save->save_r3 = result; /* Tell 'em... */
356 return 1;
357 }
358
359 if(request == pmsCExperimental) { /* Enter experimental mode? */
360
361 if(pmsInstalled || (pmsExperimental & 1)) { /* Are we already running or in experimental? */
362 save->save_r3 = KERN_FAILURE; /* Fail, since we are already running */
363 return 1;
364 }
365
366 pmsExperimental |= 1; /* Flip us into experimental but don't change other flags */
367
368 pmsCPUConf(); /* Configure for this machine */
369 pmsStart(); /* Start stepping */
370 save->save_r3 = KERN_SUCCESS; /* We are victorious... */
371 return 1;
372
373 }
374
375 if(request == pmsCCnfg) { /* Do some up-front checking before we commit to doing this */
376 if((reqsize > (pmsMaxStates * sizeof(pmsDef))) || (reqsize < (pmsFree * sizeof(pmsDef)))) { /* Check that the size is reasonable */
377 save->save_r3 = KERN_NO_SPACE; /* Tell them that they messed up */
378 return 1; /* l8r... */
379 }
380 }
381
382
383 /*
384 * We are committed after here. If there are any errors detected, we shouldn't die, but we
385 * will be stuck in park.
386 *
387 * Also, we can possibly end up on another processor after the broadcast.
388 *
389 */
390
391 if(!hw_compare_and_store(0, 1, &pmsSyncrolator)) { /* Are we already doing this? */
392 save->save_r3 = KERN_RESOURCE_SHORTAGE; /* Tell them that we are already busy and to try again */
393 return 1; /* G'wan away and don't bother me... */
394 }
395 save->save_r3 = KERN_SUCCESS; /* Assume success */
396
397 // NOTE: We will block in the following code until everyone has finished the prepare
398
399 pmsRun(pmsPrepCng); /* Get everyone parked and in a proper state for step table changes, including me */
400
401 if(request == pmsCPark) { /* Is all we're supposed to do park? */
402 pmsSyncrolator = 0; /* Free us up */
403 return 1; /* Well, then we're done... */
404 }
405
406 switch(request) { /* Select the routine */
407
408 case pmsCStart: /* Starts normal steppping */
409 nstep = pmsNormHigh; /* Set the request */
410 break;
411
412 case pmsCFLow: /* Forces low power */
413 nstep = pmsLow; /* Set request */
414 break;
415
416 case pmsCFHigh: /* Forces high power */
417 nstep = pmsHigh; /* Set request */
418 break;
419
420 case pmsCCnfg: /* Loads new stepper program */
421
422 if(!(ndefs = (pmsDef *)kalloc(reqsize))) { /* Get memory for the whole thing */
423 save->save_r3 = KERN_INVALID_ADDRESS; /* Return invalid address */
424 pmsSyncrolator = 0; /* Free us up */
425 return 1; /* All done... */
426 }
427
428 ret = copyin((user_addr_t)((unsigned int)(save->save_r4)), (void *)ndefs, reqsize); /* Get the new config table */
429 if(ret) { /* Hmmm, something went wrong with the copyin */
430 save->save_r3 = KERN_INVALID_ADDRESS; /* Return invalid address */
431 kfree((vm_offset_t)ndefs, reqsize); /* Free up the copied in data */
432 pmsSyncrolator = 0; /* Free us up */
433 return 1; /* All done... */
434 }
435
436 kret = pmsBuild(ndefs, reqsize, 0, 0, 0); /* Go build and replace the tables. Make sure we keep the old platform stuff */
437 if(kret) { /* Hmmm, something went wrong with the compilation */
438 save->save_r3 = kret; /* Pass back the passed back return code */
439 kfree((vm_offset_t)ndefs, reqsize); /* Free up the copied in data */
440 pmsSyncrolator = 0; /* Free us up */
441 return 1; /* All done... */
442 }
443
444 nstep = pmsNormHigh; /* Set the request */
445 break;
446
447 default:
448 panic("pmsCntrl: stepper control is so very, very confused = %08X\n", request);
449
450 }
451
452 pmsRun(nstep); /* Get everyone into step */
453 pmsSyncrolator = 0; /* Free us up */
454 return 1; /* All done... */
455
456 }
457
458 /*
459 * Broadcast a change to all processors including ourselves.
460 * This must transition before broadcasting because we may block and end up on a different processor.
461 *
462 * This will block until all processors have transitioned, so
463 * obviously, this can block.
464 *
465 * Called with interruptions disabled.
466 *
467 */
468
469 void pmsRun(uint32_t nstep) {
470
471 pmsRunLocal(nstep); /* If we aren't parking (we are already parked), transition ourselves */
472 (void)cpu_broadcast(&pmsBroadcastWait, pmsRemote, nstep); /* Tell everyone else to do it too */
473
474 return;
475
476 }
477
478 /*
479 * Receive a broadcast and react.
480 * This is called from the interprocessor signal handler.
481 * We wake up the initiator after we are finished.
482 *
483 */
484
485 void pmsRemote(uint32_t nstep) {
486
487 pmsRunLocal(nstep); /* Go set the step */
488 if(!hw_atomic_sub(&pmsBroadcastWait, 1)) { /* Drop the wait count */
489 thread_wakeup((event_t)&pmsBroadcastWait); /* If we were the last, wake up the signaller */
490 }
491 return;
492 }
493
494
495 /*
496 * Build the tables needed for the stepper. This includes both the step definitions and the step control table.
497 *
498 * We most absolutely need to be parked before this happens because we're gonna change the table.
499 * We're going to have to be pretty complete about checking for errors.
500 * Also, a copy is always made because we don't want to be crippled by not being able to change
501 * the table or description formats.
502 *
503 * We pass in a table of external functions and the new stepper def uses the corresponding
504 * indexes rather than actual function addresses. This is done so that a proper table can be
505 * built with the control syscall. It can't supply addresses, so the index has to do. We
506 * internalize the table so our caller does not need to keep it. Note that passing in a 0
507 * will use the current function table. Also note that entry 0 is reserved and must be 0,
508 * we will check and fail the build.
509 *
510 * The platformData parameter is a 32-bit word of data that is passed unaltered to the set function.
511 *
512 * The queryFunc parameter is the address of a function that will return the current state of the platform.
513 * The format of the data returned is the same as the platform specific portions of pmsSetCmd, i.e., pmsXClk,
514 * pmsVoltage, and any part of pmsPowerID that is maintained by the platform hardware (an example would be
515 * the values of the gpios that correspond to pmsPowerID). The value should be constructed by querying
516 * hardware rather than returning a value cached by software. One of the intents of this function is to
517 * help recover lost or determine initial power states.
518 *
519 */
520
521 kern_return_t pmsBuild(pmsDef *pd, uint32_t pdsize, pmsSetFunc_t *functab, uint32_t platformData, pmsQueryFunc_t queryFunc) {
522
523 int steps, newsize, i, cstp, nstps, oldAltSize, xdsply;
524 uint32_t setf;
525 uint64_t nlimit;
526 pmsDef *newpd, *oldAlt;
527 boolean_t intr;
528
529 xdsply = (pmsExperimental & 3) != 0; /* Turn on kprintfs if requested or in experimental mode */
530
531 if(pdsize % sizeof(pmsDef)) return KERN_INVALID_ARGUMENT; /* Length not multiple of definition size */
532
533 steps = pdsize / sizeof(pmsDef); /* Get the number of steps supplied */
534
535 if((steps >= pmsMaxStates) || (steps < pmsFree)) /* Complain if too big or too small */
536 return KERN_INVALID_ARGUMENT; /* Squeak loudly!!! */
537
538 if((uint32_t)functab && (uint32_t)functab[0]) /* Verify that if they supplied a new function table, entry 0 is 0 */
539 return KERN_INVALID_ARGUMENT; /* Fail because they didn't reserve entry 0 */
540
541 if(xdsply) kprintf("\n StepID Down Next HWSel HWfun Limit\n");
542
543 for(i = 0; i < steps; i++) { /* Step through and verify the definitions */
544
545 if(xdsply) kprintf(" %6d %6d %6d %08X %6d %20lld\n", pd[i].pmsStepID, pd[i].pmsDown,
546 pd[i].pmsNext, pd[i].pmsSetCmd,
547 pd[i].sf.pmsSetFuncInd, pd[i].pmsLimit);
548
549 if((pd[i].pmsLimit != 0) && (pd[i].pmsLimit < 100ULL)) {
550 if(xdsply) kprintf("error step %3d: pmsLimit too small/n", i);
551 return KERN_INVALID_ARGUMENT; /* Has to be 100µS or more */
552 }
553
554 if((pd[i].pmsLimit != 0xFFFFFFFFFFFFFFFFULL) && (pd[i].pmsLimit > (HalfwayToForever / 1000ULL))) {
555 if(xdsply) kprintf("error step %3d: pmsLimit too big\n", i);
556 return KERN_INVALID_ARGUMENT; /* Can't be too big */
557 }
558
559 if(pd[i].pmsStepID != i) {
560 if(xdsply) kprintf("error step %3d: step ID does not match (%d)\n", i, pd[i].pmsStepID);
561 return KERN_INVALID_ARGUMENT; /* ID must match */
562 }
563
564 if(pd[i].sf.pmsSetFuncInd >= pmsSetFuncMax) {
565 if(xdsply) kprintf("error step %3d: function invalid (%d)\n", i, pd[i].sf.pmsSetFuncInd);
566 return KERN_INVALID_ARGUMENT; /* Fail if this function is not in the table */
567 }
568
569 if((pd[i].pmsDown != pmsParked) && pd[i].pmsDown >= steps) {
570 if(xdsply) kprintf("error step %3d: pmsDown out of range (%d)\n", i, pd[i].pmsDown);
571 return KERN_INVALID_ARGUMENT; /* Step down must be in the table or park */
572 }
573
574 if((pd[i].pmsNext != pmsParked) && pd[i].pmsNext >= steps) {
575 if(xdsply) kprintf("error step %3d: pmsNext out of range (%d)\n", i, pd[i].pmsNext);
576 return KERN_INVALID_ARGUMENT; /* Step up must be in the table or park */
577 }
578
579 if((pd[i].pmsSetCmd == pmsDelay) && (pd[i].pmsTDelay >= steps)) {
580 if(xdsply) kprintf("error step %3d: pmsTDelay out of range (%d)\n", i, pd[i].pmsTDelay);
581 return KERN_INVALID_ARGUMENT; /* Delayed step must be in the table */
582 }
583
584 if((pd[i].pmsSetCmd == pmsDelay) && (pd[i].pmsLimit == 0xFFFFFFFFFFFFFFFFULL)) {
585 if(xdsply) kprintf("error step %3d: delay time limit must not be infinite\n", i);
586 return KERN_INVALID_ARGUMENT; /* Delayed step must have a time limit */
587 }
588
589 }
590
591 /*
592 * Verify that there are no infinite synchronous forward loops in the table
593 */
594
595 if(xdsply) kprintf("\nInitial scan passed, start in loop check\n");
596 for(i = 0; i < steps; i++) { /* Start with each step. Inefficient, but who cares */
597
598 cstp = i; /* Set starting point */
599 nstps = 0; /* Initialize chain length counter */
600 while(1) { /* Do until we hit the end */
601 if(pd[cstp].pmsSetCmd == pmsParkIt) break; /* Parking always terminates a chain so no endless loop here */
602 if(pd[cstp].pmsSetCmd == pmsDelay) break; /* Delayed steps always terminate a chain so no endless loop here */
603 if((pd[cstp].pmsLimit != 0) && ((pd[cstp].pmsSetCmd & pmsSync) != pmsSync)) break; /* If time limit is not 0 and not synchrouous, no endless loop */
604 if(pd[cstp].pmsNext == pmsParked) break; /* If the next step is parked, no endless loop */
605
606 cstp = pd[cstp].pmsNext; /* Chain to the next */
607 nstps = nstps + 1; /* Count this step */
608 if(nstps >= steps) { /* We've stepped for more steps than we have, must be an endless loop! */
609 if(xdsply) kprintf("error step %3d: infinite pmsNext loop\n", i);
610 return KERN_INVALID_ARGUMENT; /* Suggest to our caller that they can't program... */
611 }
612 }
613 }
614
615 if((pmsExperimental & 4) && (pmsInstalled) && ((uint32_t)functab != 0)) { /* If we are already initted and experimental is locked in, and we are doing first */
616 if(xdsply) kprintf("Experimental locked, ignoring driver pmsBuild\n");
617 return KERN_RESOURCE_SHORTAGE; /* Just ignore the request. */
618 }
619
620
621
622 /*
623 * Well, things look ok, let's do it to it...
624 */
625
626 if(xdsply) kprintf("Loop check passed, building and installing table\n");
627
628 newsize = steps * sizeof(pmsDef); /* Get the size needed for the definition blocks */
629
630 if(!(newpd = (pmsDef *)kalloc(newsize))) { /* Get memory for the whole thing */
631 return KERN_RESOURCE_SHORTAGE; /* No storage... */
632 }
633
634 bzero((void *)newpd, newsize); /* Make it pretty */
635
636 /*
637 * Ok, this is it, finish intitializing, switch the tables, and pray...
638 * We want no interruptions at all and we need to lock the table. Everybody should be parked,
639 * so no one should ever touch this. The lock is to keep multiple builders safe. It probably
640 * will never ever happen, but paranoia is a good thing...
641 */
642
643 intr = ml_set_interrupts_enabled(FALSE); /* No interruptions in here */
644 simple_lock(&pmsBuildLock); /* Lock out everyone... */
645
646 if(platformData) pmsPlatformData = platformData; /* Remember the platform data word passed in if any was... */
647 if((uint32_t)queryFunc) pmsQueryFunc = queryFunc; /* Remember the query function passed in, if it was... */
648
649 oldAlt = altDpmsTab; /* Remember any old alternate we had */
650 oldAltSize = altDpmsTabSize; /* Remember its size */
651
652 altDpmsTab = newpd; /* Point to the new table */
653 altDpmsTabSize = newsize; /* Set the size */
654
655 if((uint32_t)functab) { /* Did we get a new function table? */
656 for(i = 0; i < pmsSetFuncMax; i++) pmsFuncTab[i] = functab[i]; /* Copy in the new table */
657 }
658
659 for(i = 0; i < pmsMaxStates; i++) pmsCtls.pmsDefs[i] = &pmsDummy; /* Initialize the table to point to the dummy step */
660
661 for(i = 0; i < steps; i++) { /* Replace the step table entries */
662 if(pd[i].pmsLimit == 0xFFFFFFFFFFFFFFFFULL) nlimit = century; /* Default to 100 years */
663 else nlimit = pd[i].pmsLimit; /* Otherwise use what was supplied */
664
665 nanoseconds_to_absolutetime(nlimit * 1000ULL, &newpd[i].pmsLimit); /* Convert microseconds to nanoseconds and then to ticks */
666
667 setf = pd[i].sf.pmsSetFuncInd; /* Make convienient */
668 newpd[i].sf.pmsSetFunc = pmsFuncTab[setf]; /* Replace the index with the function address */
669
670 newpd[i].pmsStepID = pd[i].pmsStepID; /* Set the step ID */
671 newpd[i].pmsSetCmd = pd[i].pmsSetCmd; /* Set the hardware selector ID */
672 newpd[i].pmsDown = pd[i].pmsDown; /* Set the downward step */
673 newpd[i].pmsNext = pd[i].pmsNext; /* Set the next setp */
674 newpd[i].pmsTDelay = pd[i].pmsTDelay; /* Set the delayed setp */
675 pmsCtls.pmsDefs[i] = &newpd[i]; /* Copy it in */
676 }
677
678 pmsCtlp = (uint32_t)&pmsCtls; /* Point to the new pms table */
679
680 pmsInstalled = 1; /* The stepper has been born or born again... */
681
682 simple_unlock(&pmsBuildLock); /* Free play! */
683 (void)ml_set_interrupts_enabled(intr); /* Interrupts back the way there were */
684
685 if((uint32_t)oldAlt) kfree((vm_offset_t)oldAlt, oldAltSize); /* If we already had an alternate, free it */
686
687 if(xdsply) kprintf("Stepper table installed\n");
688
689 return KERN_SUCCESS; /* We're in fate's hands now... */
690 }