]> git.saurik.com Git - apple/xnu.git/blame - osfmk/kern/pms.c
xnu-792.18.15.tar.gz
[apple/xnu.git] / osfmk / kern / pms.c
CommitLineData
3a60a9f5
A
1/*
2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
3 *
8f6c56a5 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
3a60a9f5 5 *
8f6c56a5
A
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
8ad349bb 24 * limitations under the License.
8f6c56a5
A
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
3a60a9f5 27 */
89b3af67
A
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#else
34# include <i386/cpu_data.h>
35# include <i386/misc_protos.h>
36#endif
37#include <machine/pmap.h>
38#include <kern/pms.h>
3a60a9f5 39#include <kern/processor.h>
89b3af67
A
40#include <kern/kalloc.h>
41#include <vm/vm_protos.h>
3a60a9f5
A
42
43static uint32_t pmsSyncrolator = 0; /* Only one control operation at a time please */
44uint32_t pmsBroadcastWait = 0; /* Number of outstanding broadcasts */
45
46int pmsInstalled = 0; /* Power Management Stepper can run and has table installed */
47int pmsExperimental = 0; /* Power Management Stepper in experimental mode */
48decl_simple_lock_data(,pmsBuildLock) /* Make sure only one guy can replace table at the same time */
49
50static pmsDef *altDpmsTab = 0; /* Alternate step definition table */
51static uint32_t altDpmsTabSize = 0; /* Size of alternate step definition table */
52
53pmsDef pmsDummy = { /* This is the dummy step for initialization. All it does is to park */
54 .pmsLimit = 0, /* Time doesn't matter for a park */
55 .pmsStepID = pmsMaxStates - 1, /* Use the very last ID number for the dummy */
56 .pmsSetCmd = pmsParkIt, /* Force us to be parked */
57 .sf.pmsSetFuncInd = 0, /* No platform call for this one */
58 .pmsDown = pmsPrepSleep, /* We always park */
59 .pmsNext = pmsPrepSleep /* We always park */
60};
61
62pmsStat pmsStatsd[4][pmsMaxStates]; /* Generate enough statistics blocks for 4 processors */
63
64pmsCtl pmsCtls = { /* Power Management Stepper control */
65 .pmsStats = &pmsStatsd
66};
67
68pmsSetFunc_t pmsFuncTab[pmsSetFuncMax] = {0}; /* This is the function index table */
69pmsQueryFunc_t pmsQueryFunc = 0; /* Pointer to pmsQuery function */
70uint32_t pmsPlatformData = 0; /* Data provided by and passed to platform functions */
71
89b3af67
A
72#ifdef __ppc__
73# define PER_PROC_INFO struct per_proc_info
74# define GET_PER_PROC_INFO() getPerProc()
75#else
76# define PER_PROC_INFO cpu_data_t
77# define GET_PER_PROC_INFO() current_cpu_datap()
78#endif
79
80
3a60a9f5
A
81
82/*
83 * Do any initialization needed
84 */
85
86void pmsInit(void) {
87
88 int i;
89
90 simple_lock_init(&pmsBuildLock, 0); /* Initialize the build lock */
91 for(i = 0; i < pmsMaxStates; i++) pmsCtls.pmsDefs[i] = &pmsDummy; /* Initialize the table to dummy steps */
92
89b3af67
A
93 pmsCPUMachineInit();
94
3a60a9f5
A
95 return;
96}
97
98
99/*
100 * Start the power management stepper on all processors
101 *
102 * All processors must be parked. This should be called when the hardware
103 * is ready to step. Probably only at boot and after wake from sleep.
104 *
105 */
106
107 void pmsStart(void) {
108
109 boolean_t intr;
110
111 if(!pmsInstalled) return; /* We can't do this if no table installed */
112
113 intr = ml_set_interrupts_enabled(FALSE); /* No interruptions in here */
114 pmsRun(pmsStartUp); /* Start running the stepper everywhere */
115 (void)ml_set_interrupts_enabled(intr); /* Restore interruptions */
116
117 return;
118
119 }
120
121
122/*
123 * Park the stepper execution. This will force the stepper on this
124 * processor to abandon its current step and stop. No changes to the
125 * hardware state is made and any previous step is lost.
126 *
127 * This is used as the initial state at startup and when the step table
128 * is being changed.
129 *
130 */
131
132void pmsPark(void) {
133
134 boolean_t intr;
135
136 if(!pmsInstalled) return; /* We can't do this if no table installed */
137
138 intr = ml_set_interrupts_enabled(FALSE); /* No interruptions in here */
139 pmsSetStep(pmsParked, 0); /* Park the stepper */
140 (void)ml_set_interrupts_enabled(intr); /* Restore interruptions */
141
142 return;
143
144}
145
146
147/*
148 * Steps down to a lower power.
149 * Interrupts must be off...
150 */
151
152void pmsDown(void) {
153
89b3af67 154 PER_PROC_INFO *pp;
3a60a9f5
A
155 uint32_t nstate;
156
89b3af67 157 pp = GET_PER_PROC_INFO(); /* Get our per_proc */
3a60a9f5
A
158
159 if(!pmsInstalled || pp->pms.pmsState == pmsParked) return; /* No stepping if parked or not installed */
160
161 nstate = pmsCtls.pmsDefs[pp->pms.pmsState]->pmsDown; /* Get the downward step */
162 pmsSetStep(nstate, 0); /* Step to it */
163 return;
164}
165
166
167/*
168 * Steps up to a higher power. The "timer" parameter is true if the
169 * step was driven due to the pms timer expiring.
170 *
171 * Interrupts must be off...
172 */
89b3af67
A
173
174int pmsStepIdleSneaks;
175int pmsStepIdleTries;
3a60a9f5
A
176
177void pmsStep(int timer) {
178
89b3af67
A
179 PER_PROC_INFO *pp;
180 uint32_t nstate;
181 uint32_t tstate;
182 uint32_t pkgstate;
183 int dir;
184 int i;
185
186 pp = GET_PER_PROC_INFO(); /* Get our per_proc */
187
188 if(!pmsInstalled || pp->pms.pmsState == pmsParked)
189 return; /* No stepping if parked or not installed */
190
191 /*
192 * Assume a normal step.
193 */
194 nstate = pmsCtls.pmsDefs[pp->pms.pmsState]->pmsNext;
195
196 /*
197 * If we are idling and being asked to step up, check to see whether
198 * the package we're in is already at a non-idle power state. If so,
199 * attempt to work out what state that is, and go there directly to
200 * avoid wasting time ramping up.
201 */
202 if ((pp->pms.pmsState == pmsIdle)
203 && ((pkgstate = pmsCPUPackageQuery()) != ~(uint32_t)0)) {
204 /*
205 * Search forward through the stepper program,
206 * avoid looping for too long.
207 */
208 tstate = nstate;
209 pmsStepIdleTries++;
210 for (i = 0; i < 32; i++) {
211 /*
212 * Compare command with current package state
213 */
214 if ((pmsCtls.pmsDefs[tstate]->pmsSetCmd & pmsCPU) == pkgstate) {
215 nstate = tstate;
216 pmsStepIdleSneaks++;
217 break;
218 }
219
220 /*
221 * Advance to the next step in the program.
222 */
223 if (pmsCtls.pmsDefs[tstate]->pmsNext == tstate)
224 break; /* infinite loop */
225 tstate = pmsCtls.pmsDefs[tstate]->pmsNext;
226 }
227 }
228
229 /*
230 * Default to a step up.
231 */
232 dir = 1;
233
234 /*
235 * If we are stepping as a consequence of timer expiry, select the
236 * alternate exit path and note this as downward step for accounting
237 * purposes.
238 */
239 if (timer
240 && (pmsCtls.pmsDefs[pp->pms.pmsState]->pmsSetCmd == pmsDelay)) {
241 nstate = pmsCtls.pmsDefs[pp->pms.pmsState]->pmsTDelay;
242
243 /*
244 * Delayed steps are a step down for accounting purposes.
245 */
246 dir = 0;
3a60a9f5
A
247 }
248
89b3af67 249 pmsSetStep(nstate, dir);
3a60a9f5
A
250 return;
251}
252
253
254/*
255 * Set a specific step
256 *
257 * We do not do statistics if exiting park
258 * Interrupts must be off...
259 *
260 */
261
262void pmsSetStep(uint32_t nstep, int dir) {
263
89b3af67
A
264 PER_PROC_INFO *pp;
265 uint32_t pstate, nCSetCmd, mCSetCmd;
3a60a9f5 266 pmsDef *pnstate, *pcstate;
89b3af67
A
267 uint64_t tb, dur;
268 int cpu;
3a60a9f5 269
89b3af67 270 pp = GET_PER_PROC_INFO(); /* Get our per_proc */
3a60a9f5
A
271 cpu = cpu_number(); /* Get our processor */
272
273 while(1) { /* Keep stepping until we get a delay */
274
275 if(pp->pms.pmsCSetCmd & pmsMustCmp) { /* Do we have to finish the delay before changing? */
276 while(mach_absolute_time() < pp->pms.pmsPop); /* Yes, spin here... */
277 }
278
279 if((nstep == pmsParked) || ((uint32_t)pmsCtls.pmsDefs[nstep]->pmsSetCmd == pmsParkIt)) { /* Are we parking? */
280
281 tb = mach_absolute_time(); /* What time is it? */
282 pp->pms.pmsStamp = tb; /* Show transition now */
283 pp->pms.pmsPop = HalfwayToForever; /* Set the pop way into the future */
284 pp->pms.pmsState = pmsParked; /* Make sure we are parked */
89b3af67 285 etimer_resync_deadlines(); /* Cancel our timer if going */
3a60a9f5
A
286 return;
287 }
288
289 pnstate = pmsCtls.pmsDefs[nstep]; /* Point to the state definition */
290 pstate = pp->pms.pmsState; /* Save the current step */
291 pp->pms.pmsState = nstep; /* Set the current to the next step */
292
293 if(pnstate->pmsSetCmd != pmsDelay) { /* If this is not a delayed state, change the actual hardware now */
294 if(pnstate->pmsSetCmd & pmsCngCPU) pmsCPUSet(pnstate->pmsSetCmd); /* We have some CPU work to do... */
295 if((uint32_t)pnstate->sf.pmsSetFunc) pnstate->sf.pmsSetFunc(pnstate->pmsSetCmd, cpu, pmsPlatformData); /* Tell the platform to set power mode */
296
297 mCSetCmd = pnstate->pmsSetCmd & (pmsCngXClk | pmsCngCPU | pmsCngVolt); /* Isolate just the change flags */
298 mCSetCmd = (mCSetCmd - (mCSetCmd >> 7)) | pmsSync | pmsMustCmp | pmsPowerID; /* Form mask of bits that come from new command */
299 nCSetCmd = pp->pms.pmsCSetCmd & ~mCSetCmd; /* Clear changing bits */
300 nCSetCmd = nCSetCmd | (pnstate->pmsSetCmd & mCSetCmd); /* Flip on the changing bits and the always copy bits */
301
302 pp->pms.pmsCSetCmd = nCSetCmd; /* Set it for real */
303 }
304
305 tb = mach_absolute_time(); /* What time is it? */
306 pp->pms.pmsPop = tb + pnstate->pmsLimit; /* Set the next pop */
307
308 if((pnstate->pmsSetCmd != pmsDelay) && (pp->pms.pmsCSetCmd & pmsSync) && (pnstate->pmsLimit != 0)) { /* Is this a synchronous command with a delay? */
309 while(mach_absolute_time() < pp->pms.pmsPop); /* Yes, spin here and wait it out... */
310 }
311
312/*
313 * Gather some statistics
314 */
315
316 dur = tb - pp->pms.pmsStamp; /* Get the amount of time we were in the old step */
317 pp->pms.pmsStamp = tb; /* Set the new timestamp */
318 if(!(pstate == pmsParked)) { /* Only take stats if we were not parked */
319 pcstate = pmsCtls.pmsDefs[pstate]; /* Get the previous step */
320 pmsCtls.pmsStats[cpu][pcstate->pmsStepID].stTime[dir] += dur; /* Accumulate the total time in the old step */
321 pmsCtls.pmsStats[cpu][pcstate->pmsStepID].stCnt[dir] += 1; /* Count transitions */
322 }
323
324/*
325 * See if we are done chaining steps
326 */
327
328 if((pnstate->pmsSetCmd == pmsDelay)
329 || (!(pp->pms.pmsCSetCmd & pmsSync) && (pnstate->pmsLimit != 0))) { /* Is this not syncronous and a non-zero delay or a delayed step? */
89b3af67 330 etimer_resync_deadlines(); /* Start the timers ticking */
3a60a9f5
A
331 break; /* We've stepped as far as we're going to... */
332 }
333
334 nstep = pnstate->pmsNext; /* Chain on to the next */
335 }
336
337 return;
338
339}
340
341/*
342 * Either park the stepper or force the step on a parked stepper for local processor only
343 *
344 */
345
346void pmsRunLocal(uint32_t nstep) {
347
89b3af67
A
348 PER_PROC_INFO *pp;
349 uint32_t lastState;
350 int cpu, i;
3a60a9f5
A
351 boolean_t intr;
352
353 if(!pmsInstalled) return; /* Ignore this if no step programs installed... */
354
355 intr = ml_set_interrupts_enabled(FALSE); /* No interruptions in here */
356
89b3af67 357 pp = GET_PER_PROC_INFO(); /* Get our per_proc */
3a60a9f5
A
358
359 if(nstep == pmsStartUp) { /* Should we start up? */
360 pmsCPUInit(); /* Get us up to full with high voltage and park */
361 nstep = pmsNormHigh; /* Change request to transition to normal high */
362 }
363
364 lastState = pp->pms.pmsState; /* Remember if we are parked now */
365
366 pmsSetStep(nstep, 1); /* Step to the new state */
367
368 if((lastState == pmsParked) && (pp->pms.pmsState != pmsParked)) { /* Did we just unpark? */
369 cpu = cpu_number(); /* Get our processor */
370 for(i = 0; i < pmsMaxStates; i++) { /* Step through the steps and clear the statistics since we were parked */
371 pmsCtls.pmsStats[cpu][i].stTime[0] = 0; /* Clear accumulated time - downward */
372 pmsCtls.pmsStats[cpu][i].stTime[1] = 0; /* Clear accumulated time - forward */
373 pmsCtls.pmsStats[cpu][i].stCnt[0] = 0; /* Clear transition count - downward */
374 pmsCtls.pmsStats[cpu][i].stCnt[1] = 0; /* Clear transition count - forward */
375 }
376 }
377
378 (void)ml_set_interrupts_enabled(intr); /* Restore interruptions */
379
380 return;
381
382}
383
384/*
385 * Control the Power Management Stepper.
89b3af67 386 * Called from user state by the superuser.
3a60a9f5
A
387 * Interruptions disabled.
388 *
389 */
89b3af67 390kern_return_t pmsControl(uint32_t request, user_addr_t reqaddr, uint32_t reqsize) {
3a60a9f5 391
89b3af67 392 uint32_t nstep, result, presult;
3a60a9f5
A
393 int ret, cpu;
394 kern_return_t kret;
395 pmsDef *ndefs;
89b3af67 396 PER_PROC_INFO *pp;
3a60a9f5 397
89b3af67 398 pp = GET_PER_PROC_INFO(); /* Get our per_proc */
3a60a9f5
A
399 cpu = cpu_number(); /* Get our processor */
400
401 if(!is_suser()) { /* We are better than most, */
89b3af67 402 return KERN_FAILURE; /* so we will only talk to the superuser. */
3a60a9f5 403 }
89b3af67
A
404
405 if(request >= pmsCFree) { /* Can we understand the request? */
406 return KERN_INVALID_ARGUMENT; /* What language are these guys talking in, anyway? */
3a60a9f5
A
407 }
408
3a60a9f5 409 if(request == pmsCQuery) { /* Are we just checking? */
89b3af67 410 result = pmsCPUQuery() & pmsCPU; /* Get the processor data and make sure there is no slop */
3a60a9f5
A
411 presult = 0; /* Assume nothing */
412 if((uint32_t)pmsQueryFunc) presult = pmsQueryFunc(cpu, pmsPlatformData); /* Go get the platform state */
413 result = result | (presult & (pmsXClk | pmsVoltage | pmsPowerID)); /* Merge the platform state with no slop */
89b3af67 414 return result; /* Tell 'em... */
3a60a9f5
A
415 }
416
417 if(request == pmsCExperimental) { /* Enter experimental mode? */
418
419 if(pmsInstalled || (pmsExperimental & 1)) { /* Are we already running or in experimental? */
89b3af67 420 return KERN_FAILURE; /* Fail, since we are already running */
3a60a9f5
A
421 }
422
423 pmsExperimental |= 1; /* Flip us into experimental but don't change other flags */
424
425 pmsCPUConf(); /* Configure for this machine */
426 pmsStart(); /* Start stepping */
89b3af67 427 return KERN_SUCCESS; /* We are victorious... */
3a60a9f5
A
428
429 }
430
431 if(request == pmsCCnfg) { /* Do some up-front checking before we commit to doing this */
432 if((reqsize > (pmsMaxStates * sizeof(pmsDef))) || (reqsize < (pmsFree * sizeof(pmsDef)))) { /* Check that the size is reasonable */
89b3af67 433 return KERN_NO_SPACE; /* Tell them that they messed up */
3a60a9f5
A
434 }
435 }
436
89b3af67
A
437 if (request == pmsGCtls) {
438 if (reqsize != sizeof(pmsCtls))
439 return(KERN_FAILURE);
440 ret = copyout(&pmsCtls, reqaddr, reqsize);
441 return kret;
442 }
443
444 if (request == pmsGStats) {
445 if (reqsize != sizeof(pmsStatsd)) /* request size is fixed */
446 return KERN_FAILURE;
447 ret = copyout(&pmsStatsd, reqaddr, reqsize);
448 return kret; /* All done... */
449 }
3a60a9f5
A
450
451/*
452 * We are committed after here. If there are any errors detected, we shouldn't die, but we
453 * will be stuck in park.
454 *
455 * Also, we can possibly end up on another processor after the broadcast.
456 *
457 */
458
459 if(!hw_compare_and_store(0, 1, &pmsSyncrolator)) { /* Are we already doing this? */
89b3af67 460 return KERN_RESOURCE_SHORTAGE; /* Tell them that we are already busy and to try again */
3a60a9f5 461 }
3a60a9f5
A
462
463// NOTE: We will block in the following code until everyone has finished the prepare
464
465 pmsRun(pmsPrepCng); /* Get everyone parked and in a proper state for step table changes, including me */
466
467 if(request == pmsCPark) { /* Is all we're supposed to do park? */
468 pmsSyncrolator = 0; /* Free us up */
89b3af67 469 return KERN_SUCCESS; /* Well, then we're done... */
3a60a9f5
A
470 }
471
472 switch(request) { /* Select the routine */
473
474 case pmsCStart: /* Starts normal steppping */
475 nstep = pmsNormHigh; /* Set the request */
476 break;
477
478 case pmsCFLow: /* Forces low power */
479 nstep = pmsLow; /* Set request */
480 break;
481
482 case pmsCFHigh: /* Forces high power */
483 nstep = pmsHigh; /* Set request */
484 break;
485
486 case pmsCCnfg: /* Loads new stepper program */
487
488 if(!(ndefs = (pmsDef *)kalloc(reqsize))) { /* Get memory for the whole thing */
3a60a9f5 489 pmsSyncrolator = 0; /* Free us up */
89b3af67 490 return KERN_INVALID_ADDRESS; /* All done... */
3a60a9f5
A
491 }
492
89b3af67 493 ret = copyin(reqaddr, (void *)ndefs, reqsize); /* Get the new config table */
3a60a9f5 494 if(ret) { /* Hmmm, something went wrong with the copyin */
3a60a9f5
A
495 kfree((vm_offset_t)ndefs, reqsize); /* Free up the copied in data */
496 pmsSyncrolator = 0; /* Free us up */
89b3af67 497 return KERN_INVALID_ADDRESS; /* All done... */
3a60a9f5
A
498 }
499
500 kret = pmsBuild(ndefs, reqsize, 0, 0, 0); /* Go build and replace the tables. Make sure we keep the old platform stuff */
501 if(kret) { /* Hmmm, something went wrong with the compilation */
3a60a9f5
A
502 kfree((vm_offset_t)ndefs, reqsize); /* Free up the copied in data */
503 pmsSyncrolator = 0; /* Free us up */
89b3af67 504 return kret; /* All done... */
3a60a9f5
A
505 }
506
507 nstep = pmsNormHigh; /* Set the request */
508 break;
509
510 default:
511 panic("pmsCntrl: stepper control is so very, very confused = %08X\n", request);
512
513 }
514
515 pmsRun(nstep); /* Get everyone into step */
516 pmsSyncrolator = 0; /* Free us up */
89b3af67 517 return KERN_SUCCESS; /* All done... */
3a60a9f5
A
518
519}
520
521/*
522 * Broadcast a change to all processors including ourselves.
3a60a9f5 523 *
89b3af67 524 * Interruptions are disabled.
3a60a9f5
A
525 */
526
527void pmsRun(uint32_t nstep) {
528
89b3af67 529 pmsCPURun(nstep);
3a60a9f5
A
530}
531
3a60a9f5
A
532
533/*
534 * Build the tables needed for the stepper. This includes both the step definitions and the step control table.
535 *
536 * We most absolutely need to be parked before this happens because we're gonna change the table.
537 * We're going to have to be pretty complete about checking for errors.
538 * Also, a copy is always made because we don't want to be crippled by not being able to change
539 * the table or description formats.
540 *
541 * We pass in a table of external functions and the new stepper def uses the corresponding
542 * indexes rather than actual function addresses. This is done so that a proper table can be
543 * built with the control syscall. It can't supply addresses, so the index has to do. We
544 * internalize the table so our caller does not need to keep it. Note that passing in a 0
545 * will use the current function table. Also note that entry 0 is reserved and must be 0,
546 * we will check and fail the build.
547 *
548 * The platformData parameter is a 32-bit word of data that is passed unaltered to the set function.
549 *
550 * The queryFunc parameter is the address of a function that will return the current state of the platform.
551 * The format of the data returned is the same as the platform specific portions of pmsSetCmd, i.e., pmsXClk,
552 * pmsVoltage, and any part of pmsPowerID that is maintained by the platform hardware (an example would be
553 * the values of the gpios that correspond to pmsPowerID). The value should be constructed by querying
554 * hardware rather than returning a value cached by software. One of the intents of this function is to
555 * help recover lost or determine initial power states.
556 *
557 */
558
559kern_return_t pmsBuild(pmsDef *pd, uint32_t pdsize, pmsSetFunc_t *functab, uint32_t platformData, pmsQueryFunc_t queryFunc) {
560
561 int steps, newsize, i, cstp, nstps, oldAltSize, xdsply;
562 uint32_t setf;
563 uint64_t nlimit;
564 pmsDef *newpd, *oldAlt;
565 boolean_t intr;
566
567 xdsply = (pmsExperimental & 3) != 0; /* Turn on kprintfs if requested or in experimental mode */
568
569 if(pdsize % sizeof(pmsDef)) return KERN_INVALID_ARGUMENT; /* Length not multiple of definition size */
570
571 steps = pdsize / sizeof(pmsDef); /* Get the number of steps supplied */
572
573 if((steps >= pmsMaxStates) || (steps < pmsFree)) /* Complain if too big or too small */
89b3af67 574 return KERN_INVALID_ARGUMENT; /* Squeak loudly!!! */
3a60a9f5
A
575
576 if((uint32_t)functab && (uint32_t)functab[0]) /* Verify that if they supplied a new function table, entry 0 is 0 */
577 return KERN_INVALID_ARGUMENT; /* Fail because they didn't reserve entry 0 */
578
579 if(xdsply) kprintf("\n StepID Down Next HWSel HWfun Limit\n");
580
581 for(i = 0; i < steps; i++) { /* Step through and verify the definitions */
582
583 if(xdsply) kprintf(" %6d %6d %6d %08X %6d %20lld\n", pd[i].pmsStepID, pd[i].pmsDown,
584 pd[i].pmsNext, pd[i].pmsSetCmd,
585 pd[i].sf.pmsSetFuncInd, pd[i].pmsLimit);
586
587 if((pd[i].pmsLimit != 0) && (pd[i].pmsLimit < 100ULL)) {
588 if(xdsply) kprintf("error step %3d: pmsLimit too small/n", i);
589