]> git.saurik.com Git - apple/xnu.git/blob - osfmk/ppc/pms.c
xnu-1228.3.13.tar.gz
[apple/xnu.git] / osfmk / ppc / pms.c
1 /*
2 * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_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 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.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
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.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28 #include <machine/machine_routines.h>
29 #include <machine/machine_cpu.h>
30 #ifdef __ppc__
31 # include <ppc/exception.h>
32 # include <ppc/misc_protos.h>
33 # include <ppc/cpu_internal.h>
34 #else
35 # include <i386/cpu_data.h>
36 # include <i386/misc_protos.h>
37 #endif
38 #include <machine/pmap.h>
39 #include <kern/pms.h>
40 #include <kern/processor.h>
41 #include <kern/kalloc.h>
42 #include <vm/vm_protos.h>
43
44 extern int is_suser(void);
45
46 static uint32_t pmsSyncrolator = 0; /* Only one control operation at a time please */
47 uint32_t pmsBroadcastWait = 0; /* Number of outstanding broadcasts */
48
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 */
52
53 static pmsDef *altDpmsTab; /* Alternate step definition table */
54 static uint32_t altDpmsTabSize = 0; /* Size of alternate step definition table */
55
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 */
63 };
64
65 pmsStat pmsStatsd[4][pmsMaxStates]; /* Generate enough statistics blocks for 4 processors */
66
67 pmsCtl pmsCtls = { /* Power Management Stepper control */
68 .pmsStats = pmsStatsd,
69 };
70
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 */
74
75 #ifdef __ppc__
76 # define PER_PROC_INFO struct per_proc_info
77 # define GET_PER_PROC_INFO() getPerProc()
78 #else
79 # define PER_PROC_INFO cpu_data_t
80 # define GET_PER_PROC_INFO() current_cpu_datap()
81 #endif
82
83
84
85 /*
86 * Do any initialization needed
87 */
88
89 void
90 pmsInit(void)
91 {
92 int i;
93
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 */
96
97 pmsCPUMachineInit();
98 }
99
100
101 /*
102 * Start the power management stepper on all processors
103 *
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.
106 *
107 */
108
109 void
110 pmsStart(void)
111 {
112 boolean_t intr;
113
114 if(!pmsInstalled)
115 return; /* We can't do this if no table installed */
116
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 */
120 }
121
122
123 /*
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.
127 *
128 * This is used as the initial state at startup and when the step table
129 * is being changed.
130 *
131 */
132
133 void
134 pmsPark(void)
135 {
136 boolean_t intr;
137
138 if(!pmsInstalled)
139 return; /* We can't do this if no table installed */
140
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 */
144 }
145
146
147 /*
148 * Steps down to a lower power.
149 * Interrupts must be off...
150 */
151
152 void
153 pmsDown(void)
154 {
155 PER_PROC_INFO *pp;
156 uint32_t nstate;
157
158 pp = GET_PER_PROC_INFO(); /* Get our per_proc */
159
160 if(!pmsInstalled || pp->pms.pmsState == pmsParked)
161 return; /* No stepping if parked or not installed */
162
163 nstate = pmsCtls.pmsDefs[pp->pms.pmsState]->pmsDown; /* Get the downward step */
164 pmsSetStep(nstate, 0); /* Step to it */
165 }
166
167
168 /*
169 * Steps up to a higher power. The "timer" parameter is true if the
170 * step was driven due to the pms timer expiring.
171 *
172 * Interrupts must be off...
173 */
174
175 int pmsStepIdleSneaks;
176 int pmsStepIdleTries;
177
178 void
179 pmsStep(int timer)
180 {
181 PER_PROC_INFO *pp;
182 uint32_t nstate;
183 uint32_t tstate;
184 uint32_t pkgstate;
185 int dir;
186 int i;
187
188 pp = GET_PER_PROC_INFO(); /* Get our per_proc */
189
190 if(!pmsInstalled || pp->pms.pmsState == pmsParked)
191 return; /* No stepping if parked or not installed */
192
193 /*
194 * Assume a normal step.
195 */
196 nstate = pmsCtls.pmsDefs[pp->pms.pmsState]->pmsNext;
197
198 /*
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.
203 */
204 if ((pp->pms.pmsState == pmsIdle)
205 && ((pkgstate = pmsCPUPackageQuery()) != ~(uint32_t)0)) {
206 /*
207 * Search forward through the stepper program,
208 * avoid looping for too long.
209 */
210 tstate = nstate;
211 pmsStepIdleTries++;
212 for (i = 0; i < 32; i++) {
213 /*
214 * Compare command with current package state
215 */
216 if ((pmsCtls.pmsDefs[tstate]->pmsSetCmd & pmsCPU) == pkgstate) {
217 nstate = tstate;
218 pmsStepIdleSneaks++;
219 break;
220 }
221
222 /*
223 * Advance to the next step in the program.
224 */
225 if (pmsCtls.pmsDefs[tstate]->pmsNext == tstate)
226 break; /* infinite loop */
227 tstate = pmsCtls.pmsDefs[tstate]->pmsNext;
228 }
229 }
230
231 /*
232 * Default to a step up.
233 */
234 dir = 1;
235
236 /*
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
239 * purposes.
240 */
241 if (timer
242 && (pmsCtls.pmsDefs[pp->pms.pmsState]->pmsSetCmd == pmsDelay)) {
243 nstate = pmsCtls.pmsDefs[pp->pms.pmsState]->pmsTDelay;
244
245 /*
246 * Delayed steps are a step down for accounting purposes.
247 */
248 dir = 0;
249 }
250
251 pmsSetStep(nstate, dir);
252 }
253
254
255 /*
256 * Set a specific step
257 *
258 * We do not do statistics if exiting park
259 * Interrupts must be off...
260 *
261 */
262
263 void
264 pmsSetStep(uint32_t nstep, int dir)
265 {
266 PER_PROC_INFO *pp;
267 uint32_t pstate, nCSetCmd, mCSetCmd;
268 pmsDef *pnstate, *pcstate;
269 uint64_t tb, dur;
270 int cpu;
271
272 pp = GET_PER_PROC_INFO(); /* Get our per_proc */
273 cpu = cpu_number(); /* Get our processor */
274
275 while(1) { /* Keep stepping until we get a delay */
276
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... */
279 }
280
281 if((nstep == pmsParked) || ((uint32_t)pmsCtls.pmsDefs[nstep]->pmsSetCmd == pmsParkIt)) { /* Are we parking? */
282
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 */
288 return;
289 }
290
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 */
294
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 */
298
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 */
303
304 pp->pms.pmsCSetCmd = nCSetCmd; /* Set it for real */
305 }
306
307 tb = mach_absolute_time(); /* What time is it? */
308 pp->pms.pmsPop = tb + pnstate->pmsLimit; /* Set the next pop */
309
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... */
312 }
313
314 /*
315 * Gather some statistics
316 */
317
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 */
324 }
325
326 /*
327 * See if we are done chaining steps
328 */
329
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... */
334 }
335
336 nstep = pnstate->pmsNext; /* Chain on to the next */
337 }
338 }
339
340 /*
341 * Either park the stepper or force the step on a parked stepper for local processor only
342 *
343 */
344
345 void
346 pmsRunLocal(uint32_t nstep)
347 {
348 PER_PROC_INFO *pp;
349 uint32_t lastState;
350 int cpu, i;
351 boolean_t intr;
352
353 if(!pmsInstalled) /* Ignore this if no step programs installed... */
354 return;
355
356 intr = ml_set_interrupts_enabled(FALSE); /* No interruptions in here */
357
358 pp = GET_PER_PROC_INFO(); /* Get our per_proc */
359
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 */
363 }
364
365 lastState = pp->pms.pmsState; /* Remember if we are parked now */
366
367 pmsSetStep(nstep, 1); /* Step to the new state */
368
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 */
376 }
377 }
378
379 (void)ml_set_interrupts_enabled(intr); /* Restore interruptions */
380 }
381
382 /*
383 * Control the Power Management Stepper.
384 * Called from user state by the superuser.
385 * Interruptions disabled.
386 *
387 */
388 kern_return_t
389 pmsControl(uint32_t request, user_addr_t reqaddr, uint32_t reqsize)
390 {
391 uint32_t nstep = 0, result, presult;
392 int ret, cpu;
393 kern_return_t kret = KERN_SUCCESS;
394 pmsDef *ndefs;
395 PER_PROC_INFO *pp;
396
397 pp = GET_PER_PROC_INFO(); /* Get our per_proc */
398 cpu = cpu_number(); /* Get our processor */
399
400 if(!is_suser()) { /* We are better than most, */
401 kret = KERN_FAILURE;
402 goto out;
403 }
404
405 if(request >= pmsCFree) { /* Can we understand the request? */
406 kret = KERN_INVALID_ARGUMENT;
407 goto out;
408 }
409
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 */
416 goto out;
417 }
418
419 if(request == pmsCExperimental) { /* Enter experimental mode? */
420
421 if(pmsInstalled || (pmsExperimental & 1)) { /* Are we already running or in experimental? */
422 kret = KERN_FAILURE;
423 goto out;
424 }
425
426 pmsExperimental |= 1; /* Flip us into experimental but don't change other flags */
427
428 pmsCPUConf(); /* Configure for this machine */
429 pmsStart(); /* Start stepping */
430 goto out;
431 }
432
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;
436 goto out;
437 }
438 }
439
440 if (request == pmsGCtls) {
441 if (reqsize != sizeof(pmsCtls)) {
442 kret = KERN_FAILURE;
443 goto out;
444 }
445 ret = copyout(&pmsCtls, reqaddr, reqsize);
446 goto out;
447 }
448
449 if (request == pmsGStats) {
450 if (reqsize != sizeof(pmsStatsd)) { /* request size is fixed */
451 kret = KERN_FAILURE;
452 goto out;
453 }
454 ret = copyout(&pmsStatsd, reqaddr, reqsize);
455 goto out;
456 }
457
458 /*
459 * We are committed after here. If there are any errors detected, we shouldn't die, but we
460 * will be stuck in park.
461 *
462 * Also, we can possibly end up on another processor after the broadcast.
463 *
464 */
465
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;
469 goto out;
470 }
471
472 // NOTE: We will block in the following code until everyone has finished the prepare
473
474 pmsRun(pmsPrepCng); /* Get everyone parked and in a proper state for step table changes, including me */
475
476 if(request == pmsCPark) { /* Is all we're supposed to do park? */
477 pmsSyncrolator = 0; /* Free us up */
478 goto out;
479 }
480
481 switch(request) { /* Select the routine */
482
483 case pmsCStart: /* Starts normal steppping */
484 nstep = pmsNormHigh; /* Set the request */
485 break;
486
487 case pmsCFLow: /* Forces low power */
488 nstep = pmsLow; /* Set request */
489 break;
490
491 case pmsCFHigh: /* Forces high power */
492 nstep = pmsHigh; /* Set request */
493 break;
494
495 case pmsCCnfg: /* Loads new stepper program */
496
497 if(!(ndefs = (pmsDef *)kalloc(reqsize))) { /* Get memory for the whole thing */
498 pmsSyncrolator = 0; /* Free us up */
499 kret = KERN_INVALID_ADDRESS;
500 goto out;
501 }
502
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;
508 goto out;
509 }
510
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 */
515 goto out;
516 }
517
518 nstep = pmsNormHigh; /* Set the request */
519 break;
520
521 default:
522 panic("pmsCntrl: stepper control is so very, very confused = %08X\n", request);
523
524 }
525
526 pmsRun(nstep); /* Get everyone into step */
527 pmsSyncrolator = 0; /* Free us up */
528 out:
529 return kret;
530
531 }
532
533 /*
534 * Broadcast a change to all processors including ourselves.
535 *
536 * Interruptions are disabled.
537 */
538
539 void
540 pmsRun(uint32_t nstep)
541 {
542 pmsCPURun(nstep);
543 }
544
545
546 /*
547 * Build the tables needed for the stepper. This includes both the step definitions and the step control table.
548 *
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.
553 *
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.
560 *
561 * The platformData parameter is a 32-bit word of data that is passed unaltered to the set function.
562 *
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.
569 *
570 */
571
572 kern_return_t pmsBuild(pmsDef *pd, uint32_t pdsize, pmsSetFunc_t *functab, uint32_t platformData, pmsQueryFunc_t queryFunc) {
573
574 int newsize, cstp, oldAltSize, xdsply;
575 uint32_t setf, steps, i, nstps;
576 uint64_t nlimit;
577 pmsDef *newpd, *oldAlt;
578 boolean_t intr;
579
580 xdsply = (pmsExperimental & 3) != 0; /* Turn on kprintfs if requested or in experimental mode */
581
582 if(pdsize % sizeof(pmsDef))
583 return KERN_INVALID_ARGUMENT; /* Length not multiple of definition size */
584
585 steps = pdsize / sizeof(pmsDef); /* Get the number of steps supplied */
586
587 if((steps >= pmsMaxStates) || (steps < pmsFree)) /* Complain if too big or too small */
588 return KERN_INVALID_ARGUMENT; /* Squeak loudly!!! */
589
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 */
592
593 if(xdsply) kprintf("\n StepID Down Next HWSel HWfun Limit\n");
594
595 for(i = 0; i < steps; i++) { /* Step through and verify the definitions */
596
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);
600
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 */
604 }
605
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 */
609 }
610
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 */
614 }
615
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 */
619 }
620
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 */
624 }
625
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 */
629 }
630
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 */
634 }
635
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 */
639 }
640
641 }
642
643 /*
644 * Verify that there are no infinite synchronous forward loops in the table
645 */
646
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 */
649
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 */
657
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... */
663 }
664 }
665 }
666
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. */
670 }
671
672
673
674 /*
675 * Well, things look ok, let's do it to it...
676 */
677
678 if(xdsply) kprintf("Loop check passed, building and installing table\n");
679
680 newsize = steps * sizeof(pmsDef); /* Get the size needed for the definition blocks */
681
682 if(!(newpd = (pmsDef *)kalloc(newsize))) { /* Get memory for the whole thing */
683 return KERN_RESOURCE_SHORTAGE; /* No storage... */
684 }
685
686 bzero((void *)newpd, newsize); /* Make it pretty */
687
688 /*
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...
693 */
694
695 intr = ml_set_interrupts_enabled(FALSE); /* No interruptions in here */
696 simple_lock(&pmsBuildLock); /* Lock out everyone... */
697
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... */
700
701 oldAlt = altDpmsTab; /* Remember any old alternate we had */
702 oldAltSize = altDpmsTabSize; /* Remember its size */
703
704 altDpmsTab = newpd; /* Point to the new table */
705 altDpmsTabSize = newsize; /* Set the size */
706
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 */
709 }
710
711 for(i = 0; i < pmsMaxStates; i++) pmsCtls.pmsDefs[i] = &pmsDummy; /* Initialize the table to point to the dummy step */
712
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 */
716
717 nanoseconds_to_absolutetime(nlimit * 1000ULL, &newpd[i].pmsLimit); /* Convert microseconds to nanoseconds and then to ticks */
718
719 setf = pd[i].sf.pmsSetFuncInd; /* Make convienient */
720 newpd[i].sf.pmsSetFunc = pmsFuncTab[setf]; /* Replace the index with the function address */
721
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 */
728 }
729 #ifdef __ppc__
730 pmsCtlp = (uint32_t)&pmsCtls; /* Point to the new pms table */
731 #endif
732 pmsInstalled = 1; /* The stepper has been born or born again... */
733
734 simple_unlock(&pmsBuildLock); /* Free play! */
735 (void)ml_set_interrupts_enabled(intr); /* Interrupts back the way there were */
736
737 if((uint32_t)oldAlt) /* If we already had an alternate, free it */
738 kfree(oldAlt, oldAltSize);
739
740 if(xdsply) kprintf("Stepper table installed\n");
741
742 return KERN_SUCCESS; /* We're in fate's hands now... */
743 }