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