]>
Commit | Line | Data |
---|---|---|
0c530ab8 | 1 | /* |
e2fac8b1 | 2 | * Copyright (c) 2004-2009 Apple Inc. All rights reserved. |
0c530ab8 | 3 | * |
2d21ac55 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
0c530ab8 | 5 | * |
2d21ac55 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. | |
0c530ab8 | 14 | * |
2d21ac55 A |
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 | |
0c530ab8 A |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
2d21ac55 A |
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. | |
0c530ab8 | 25 | * |
2d21ac55 | 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
0c530ab8 A |
27 | */ |
28 | ||
29 | /* | |
30 | * CPU-specific power management support. | |
31 | * | |
32 | * Implements the "wrappers" to the KEXT. | |
33 | */ | |
0c530ab8 | 34 | #include <i386/asm.h> |
b0d623f7 | 35 | #include <i386/machine_cpu.h> |
0c530ab8 | 36 | #include <i386/mp.h> |
b0d623f7 | 37 | #include <i386/machine_routines.h> |
0c530ab8 | 38 | #include <i386/proc_reg.h> |
b0d623f7 A |
39 | #include <i386/pmap.h> |
40 | #include <i386/misc_protos.h> | |
41 | #include <kern/machine.h> | |
0c530ab8 A |
42 | #include <kern/pms.h> |
43 | #include <kern/processor.h> | |
7e4a7d39 | 44 | #include <kern/etimer.h> |
2d21ac55 | 45 | #include <i386/cpu_threads.h> |
0c530ab8 A |
46 | #include <i386/pmCPU.h> |
47 | #include <i386/cpuid.h> | |
48 | #include <i386/rtclock.h> | |
593a1d5f | 49 | #include <kern/sched_prim.h> |
b0d623f7 | 50 | #include <i386/lapic.h> |
593a1d5f A |
51 | |
52 | /* | |
53 | * Kernel parameter determining whether threads are halted unconditionally | |
54 | * in the idle state. This is the default behavior. | |
55 | * See machine_idle() for use. | |
56 | */ | |
57 | int idlehalt = 1; | |
0c530ab8 A |
58 | |
59 | extern int disableConsoleOutput; | |
60 | ||
61 | decl_simple_lock_data(,pm_init_lock); | |
62 | ||
63 | /* | |
64 | * The following is set when the KEXT loads and initializes. | |
65 | */ | |
66 | pmDispatch_t *pmDispatch = NULL; | |
67 | ||
2d21ac55 A |
68 | static uint32_t pmInitDone = 0; |
69 | ||
0c530ab8 A |
70 | |
71 | /* | |
72 | * Initialize the Cstate change code. | |
73 | */ | |
74 | void | |
75 | power_management_init(void) | |
76 | { | |
2d21ac55 | 77 | static boolean_t initialized = FALSE; |
0c530ab8 A |
78 | |
79 | /* | |
80 | * Initialize the lock for the KEXT initialization. | |
81 | */ | |
2d21ac55 A |
82 | if (!initialized) { |
83 | simple_lock_init(&pm_init_lock, 0); | |
84 | initialized = TRUE; | |
85 | } | |
0c530ab8 A |
86 | |
87 | if (pmDispatch != NULL && pmDispatch->cstateInit != NULL) | |
88 | (*pmDispatch->cstateInit)(); | |
89 | } | |
90 | ||
0c530ab8 | 91 | /* |
593a1d5f A |
92 | * Called when the CPU is idle. It calls into the power management kext |
93 | * to determine the best way to idle the CPU. | |
0c530ab8 | 94 | */ |
593a1d5f A |
95 | void |
96 | machine_idle(void) | |
0c530ab8 | 97 | { |
593a1d5f | 98 | cpu_data_t *my_cpu = current_cpu_datap(); |
0c530ab8 | 99 | |
593a1d5f A |
100 | if (my_cpu == NULL) |
101 | goto out; | |
0c530ab8 | 102 | |
593a1d5f A |
103 | /* |
104 | * If idlehalt isn't set, then don't do any power management related | |
105 | * idle handling. | |
106 | */ | |
107 | if (!idlehalt) | |
108 | goto out; | |
109 | ||
110 | my_cpu->lcpu.state = LCPU_IDLE; | |
111 | DBGLOG(cpu_handle, cpu_number(), MP_IDLE); | |
112 | MARK_CPU_IDLE(cpu_number()); | |
0c530ab8 | 113 | |
593a1d5f A |
114 | if (pmInitDone |
115 | && pmDispatch != NULL | |
116 | && pmDispatch->cstateMachineIdle != NULL) | |
117 | (*pmDispatch->cstateMachineIdle)(0x7FFFFFFFFFFFFFFFULL); | |
118 | else { | |
119 | /* | |
120 | * If no power management, re-enable interrupts and halt. | |
121 | * This will keep the CPU from spinning through the scheduler | |
122 | * and will allow at least some minimal power savings (but it | |
123 | * cause problems in some MP configurations w.r.t. the APIC | |
124 | * stopping during a GV3 transition). | |
125 | */ | |
126 | __asm__ volatile ("sti; hlt"); | |
0c530ab8 | 127 | } |
0c530ab8 | 128 | |
593a1d5f A |
129 | /* |
130 | * Mark the CPU as running again. | |
131 | */ | |
132 | MARK_CPU_ACTIVE(cpu_number()); | |
133 | DBGLOG(cpu_handle, cpu_number(), MP_UNIDLE); | |
134 | my_cpu->lcpu.state = LCPU_RUN; | |
135 | ||
136 | /* | |
137 | * Re-enable interrupts. | |
138 | */ | |
139 | out: | |
140 | __asm__ volatile("sti"); | |
2d21ac55 A |
141 | } |
142 | ||
143 | /* | |
144 | * Called when the CPU is to be halted. It will choose the best C-State | |
145 | * to be in. | |
146 | */ | |
147 | void | |
148 | pmCPUHalt(uint32_t reason) | |
149 | { | |
593a1d5f | 150 | cpu_data_t *cpup = current_cpu_datap(); |
2d21ac55 A |
151 | |
152 | switch (reason) { | |
153 | case PM_HALT_DEBUG: | |
593a1d5f | 154 | cpup->lcpu.state = LCPU_PAUSE; |
2d21ac55 A |
155 | __asm__ volatile ("wbinvd; hlt"); |
156 | break; | |
157 | ||
158 | case PM_HALT_PANIC: | |
593a1d5f | 159 | cpup->lcpu.state = LCPU_PAUSE; |
2d21ac55 A |
160 | __asm__ volatile ("cli; wbinvd; hlt"); |
161 | break; | |
162 | ||
163 | case PM_HALT_NORMAL: | |
164 | default: | |
165 | __asm__ volatile ("cli"); | |
166 | ||
b0d623f7 | 167 | if (pmInitDone |
2d21ac55 A |
168 | && pmDispatch != NULL |
169 | && pmDispatch->pmCPUHalt != NULL) { | |
593a1d5f A |
170 | /* |
171 | * Halt the CPU (and put it in a low power state. | |
172 | */ | |
2d21ac55 | 173 | (*pmDispatch->pmCPUHalt)(); |
2d21ac55 | 174 | |
593a1d5f A |
175 | /* |
176 | * We've exited halt, so get the the CPU schedulable again. | |
177 | */ | |
178 | i386_init_slave_fast(); | |
179 | ||
180 | panic("init_slave_fast returned"); | |
181 | } else { | |
2d21ac55 A |
182 | /* |
183 | * If no power managment and a processor is taken off-line, | |
184 | * then invalidate the cache and halt it (it will not be able | |
185 | * to be brought back on-line without resetting the CPU). | |
186 | */ | |
187 | __asm__ volatile ("wbinvd"); | |
593a1d5f | 188 | cpup->lcpu.state = LCPU_HALT; |
2d21ac55 | 189 | __asm__ volatile ( "wbinvd; hlt" ); |
593a1d5f A |
190 | |
191 | panic("back from Halt"); | |
2d21ac55 A |
192 | } |
193 | break; | |
0c530ab8 A |
194 | } |
195 | } | |
196 | ||
2d21ac55 | 197 | void |
593a1d5f | 198 | pmMarkAllCPUsOff(void) |
2d21ac55 | 199 | { |
593a1d5f A |
200 | if (pmInitDone |
201 | && pmDispatch != NULL | |
202 | && pmDispatch->markAllCPUsOff != NULL) | |
203 | (*pmDispatch->markAllCPUsOff)(); | |
2d21ac55 A |
204 | } |
205 | ||
206 | static void | |
207 | pmInitComplete(void) | |
208 | { | |
209 | pmInitDone = 1; | |
210 | } | |
211 | ||
212 | static x86_lcpu_t * | |
213 | pmGetLogicalCPU(int cpu) | |
0c530ab8 | 214 | { |
2d21ac55 A |
215 | return(cpu_to_lcpu(cpu)); |
216 | } | |
217 | ||
218 | static x86_lcpu_t * | |
219 | pmGetMyLogicalCPU(void) | |
220 | { | |
221 | cpu_data_t *cpup = current_cpu_datap(); | |
0c530ab8 | 222 | |
2d21ac55 A |
223 | return(&cpup->lcpu); |
224 | } | |
225 | ||
226 | static x86_core_t * | |
227 | pmGetCore(int cpu) | |
228 | { | |
229 | return(cpu_to_core(cpu)); | |
0c530ab8 A |
230 | } |
231 | ||
2d21ac55 A |
232 | static x86_core_t * |
233 | pmGetMyCore(void) | |
0c530ab8 | 234 | { |
2d21ac55 | 235 | cpu_data_t *cpup = current_cpu_datap(); |
0c530ab8 | 236 | |
2d21ac55 | 237 | return(cpup->lcpu.core); |
0c530ab8 A |
238 | } |
239 | ||
593a1d5f A |
240 | static x86_die_t * |
241 | pmGetDie(int cpu) | |
242 | { | |
243 | return(cpu_to_die(cpu)); | |
244 | } | |
245 | ||
246 | static x86_die_t * | |
247 | pmGetMyDie(void) | |
248 | { | |
249 | cpu_data_t *cpup = current_cpu_datap(); | |
250 | ||
251 | return(cpup->lcpu.die); | |
252 | } | |
253 | ||
2d21ac55 A |
254 | static x86_pkg_t * |
255 | pmGetPackage(int cpu) | |
0c530ab8 | 256 | { |
2d21ac55 A |
257 | return(cpu_to_package(cpu)); |
258 | } | |
259 | ||
260 | static x86_pkg_t * | |
261 | pmGetMyPackage(void) | |
262 | { | |
263 | cpu_data_t *cpup = current_cpu_datap(); | |
264 | ||
593a1d5f | 265 | return(cpup->lcpu.package); |
2d21ac55 A |
266 | } |
267 | ||
268 | static void | |
269 | pmLockCPUTopology(int lock) | |
270 | { | |
271 | if (lock) { | |
272 | simple_lock(&x86_topo_lock); | |
273 | } else { | |
274 | simple_unlock(&x86_topo_lock); | |
275 | } | |
0c530ab8 A |
276 | } |
277 | ||
278 | /* | |
2d21ac55 A |
279 | * Called to get the next deadline that has been set by the |
280 | * power management code. | |
0c530ab8 | 281 | */ |
2d21ac55 A |
282 | uint64_t |
283 | pmCPUGetDeadline(cpu_data_t *cpu) | |
284 | { | |
285 | uint64_t deadline = EndOfAllTime; | |
0c530ab8 | 286 | |
b0d623f7 | 287 | if (pmInitDone |
2d21ac55 A |
288 | && pmDispatch != NULL |
289 | && pmDispatch->GetDeadline != NULL) | |
290 | deadline = (*pmDispatch->GetDeadline)(&cpu->lcpu); | |
291 | ||
292 | return(deadline); | |
0c530ab8 A |
293 | } |
294 | ||
295 | /* | |
2d21ac55 A |
296 | * Called to determine if the supplied deadline or the power management |
297 | * deadline is sooner. Returns which ever one is first. | |
0c530ab8 | 298 | */ |
2d21ac55 A |
299 | uint64_t |
300 | pmCPUSetDeadline(cpu_data_t *cpu, uint64_t deadline) | |
0c530ab8 | 301 | { |
b0d623f7 | 302 | if (pmInitDone |
2d21ac55 A |
303 | && pmDispatch != NULL |
304 | && pmDispatch->SetDeadline != NULL) | |
305 | deadline = (*pmDispatch->SetDeadline)(&cpu->lcpu, deadline); | |
306 | ||
307 | return(deadline); | |
0c530ab8 A |
308 | } |
309 | ||
0c530ab8 | 310 | /* |
2d21ac55 | 311 | * Called when a power management deadline expires. |
0c530ab8 A |
312 | */ |
313 | void | |
2d21ac55 | 314 | pmCPUDeadline(cpu_data_t *cpu) |
0c530ab8 | 315 | { |
2d21ac55 A |
316 | if (pmInitDone |
317 | && pmDispatch != NULL | |
318 | && pmDispatch->Deadline != NULL) | |
319 | (*pmDispatch->Deadline)(&cpu->lcpu); | |
320 | } | |
321 | ||
322 | /* | |
323 | * Called to get a CPU out of idle. | |
324 | */ | |
325 | boolean_t | |
326 | pmCPUExitIdle(cpu_data_t *cpu) | |
327 | { | |
328 | boolean_t do_ipi; | |
329 | ||
330 | if (pmInitDone | |
331 | && pmDispatch != NULL | |
332 | && pmDispatch->exitIdle != NULL) | |
333 | do_ipi = (*pmDispatch->exitIdle)(&cpu->lcpu); | |
334 | else | |
335 | do_ipi = TRUE; | |
336 | ||
337 | return(do_ipi); | |
0c530ab8 A |
338 | } |
339 | ||
593a1d5f A |
340 | kern_return_t |
341 | pmCPUExitHalt(int cpu) | |
342 | { | |
343 | kern_return_t rc = KERN_INVALID_ARGUMENT; | |
344 | ||
345 | if (pmInitDone | |
346 | && pmDispatch != NULL | |
347 | && pmDispatch->exitHalt != NULL) | |
348 | rc = pmDispatch->exitHalt(cpu_to_lcpu(cpu)); | |
349 | ||
350 | return(rc); | |
351 | } | |
352 | ||
e2fac8b1 A |
353 | kern_return_t |
354 | pmCPUExitHaltToOff(int cpu) | |
355 | { | |
356 | kern_return_t rc = KERN_INVALID_ARGUMENT; | |
357 | ||
358 | if (pmInitDone | |
359 | && pmDispatch != NULL | |
360 | && pmDispatch->exitHaltToOff != NULL) | |
361 | rc = pmDispatch->exitHaltToOff(cpu_to_lcpu(cpu)); | |
362 | ||
363 | return(rc); | |
364 | } | |
365 | ||
2d21ac55 | 366 | /* |
593a1d5f | 367 | * Called to initialize the power management structures for the CPUs. |
2d21ac55 | 368 | */ |
0c530ab8 | 369 | void |
593a1d5f | 370 | pmCPUStateInit(void) |
0c530ab8 | 371 | { |
593a1d5f A |
372 | if (pmDispatch != NULL && pmDispatch->pmCPUStateInit != NULL) |
373 | (*pmDispatch->pmCPUStateInit)(); | |
0c530ab8 A |
374 | } |
375 | ||
2d21ac55 | 376 | /* |
593a1d5f | 377 | * Called when a CPU is being restarted after being powered off (as in S3). |
2d21ac55 | 378 | */ |
0c530ab8 | 379 | void |
593a1d5f | 380 | pmCPUMarkRunning(cpu_data_t *cpu) |
0c530ab8 | 381 | { |
593a1d5f A |
382 | cpu_data_t *cpup = current_cpu_datap(); |
383 | ||
2d21ac55 A |
384 | if (pmInitDone |
385 | && pmDispatch != NULL | |
593a1d5f A |
386 | && pmDispatch->markCPURunning != NULL) |
387 | (*pmDispatch->markCPURunning)(&cpu->lcpu); | |
388 | else | |
389 | cpup->lcpu.state = LCPU_RUN; | |
2d21ac55 A |
390 | } |
391 | ||
392 | /* | |
393 | * Called to get/set CPU power management state. | |
394 | */ | |
395 | int | |
396 | pmCPUControl(uint32_t cmd, void *datap) | |
397 | { | |
398 | int rc = -1; | |
399 | ||
400 | if (pmDispatch != NULL | |
401 | && pmDispatch->pmCPUControl != NULL) | |
402 | rc = (*pmDispatch->pmCPUControl)(cmd, datap); | |
403 | ||
404 | return(rc); | |
0c530ab8 A |
405 | } |
406 | ||
593a1d5f A |
407 | /* |
408 | * Called to save the timer state used by power management prior | |
409 | * to "sleeping". | |
410 | */ | |
411 | void | |
412 | pmTimerSave(void) | |
413 | { | |
414 | if (pmDispatch != NULL | |
415 | && pmDispatch->pmTimerStateSave != NULL) | |
416 | (*pmDispatch->pmTimerStateSave)(); | |
417 | } | |
418 | ||
419 | /* | |
420 | * Called to restore the timer state used by power management after | |
421 | * waking from "sleep". | |
422 | */ | |
423 | void | |
424 | pmTimerRestore(void) | |
425 | { | |
426 | if (pmDispatch != NULL | |
427 | && pmDispatch->pmTimerStateRestore != NULL) | |
428 | (*pmDispatch->pmTimerStateRestore)(); | |
429 | } | |
430 | ||
2d21ac55 A |
431 | /* |
432 | * Set the worst-case time for the C4 to C2 transition. | |
433 | * No longer does anything. | |
434 | */ | |
0c530ab8 | 435 | void |
2d21ac55 | 436 | ml_set_maxsnoop(__unused uint32_t maxdelay) |
0c530ab8 | 437 | { |
0c530ab8 A |
438 | } |
439 | ||
2d21ac55 A |
440 | |
441 | /* | |
442 | * Get the worst-case time for the C4 to C2 transition. Returns nanoseconds. | |
443 | */ | |
444 | unsigned | |
445 | ml_get_maxsnoop(void) | |
446 | { | |
447 | uint64_t max_snoop = 0; | |
448 | ||
449 | if (pmDispatch != NULL | |
450 | && pmDispatch->getMaxSnoop != NULL) | |
451 | max_snoop = pmDispatch->getMaxSnoop(); | |
452 | ||
453 | return((unsigned)(max_snoop & 0xffffffff)); | |
454 | } | |
455 | ||
456 | ||
457 | uint32_t | |
458 | ml_get_maxbusdelay(void) | |
459 | { | |
460 | uint64_t max_delay = 0; | |
461 | ||
462 | if (pmDispatch != NULL | |
463 | && pmDispatch->getMaxBusDelay != NULL) | |
464 | max_delay = pmDispatch->getMaxBusDelay(); | |
465 | ||
466 | return((uint32_t)(max_delay & 0xffffffff)); | |
467 | } | |
468 | ||
469 | /* | |
470 | * Set the maximum delay time allowed for snoop on the bus. | |
471 | * | |
472 | * Note that this value will be compared to the amount of time that it takes | |
473 | * to transition from a non-snooping power state (C4) to a snooping state (C2). | |
474 | * If maxBusDelay is less than C4C2SnoopDelay, | |
475 | * we will not enter the lowest power state. | |
476 | */ | |
0c530ab8 | 477 | void |
2d21ac55 | 478 | ml_set_maxbusdelay(uint32_t mdelay) |
0c530ab8 | 479 | { |
2d21ac55 A |
480 | uint64_t maxdelay = mdelay; |
481 | ||
482 | if (pmDispatch != NULL | |
483 | && pmDispatch->setMaxBusDelay != NULL) | |
484 | pmDispatch->setMaxBusDelay(maxdelay); | |
593a1d5f A |
485 | } |
486 | ||
487 | uint64_t | |
488 | ml_get_maxintdelay(void) | |
489 | { | |
490 | uint64_t max_delay = 0; | |
491 | ||
492 | if (pmDispatch != NULL | |
493 | && pmDispatch->getMaxIntDelay != NULL) | |
494 | max_delay = pmDispatch->getMaxIntDelay(); | |
495 | ||
496 | return(max_delay); | |
497 | } | |
498 | ||
499 | /* | |
500 | * Set the maximum delay allowed for an interrupt. | |
501 | */ | |
502 | void | |
503 | ml_set_maxintdelay(uint64_t mdelay) | |
504 | { | |
505 | if (pmDispatch != NULL | |
506 | && pmDispatch->setMaxIntDelay != NULL) | |
507 | pmDispatch->setMaxIntDelay(mdelay); | |
0c530ab8 A |
508 | } |
509 | ||
2d21ac55 A |
510 | /* |
511 | * Put a CPU into "safe" mode with respect to power. | |
512 | * | |
513 | * Some systems cannot operate at a continuous "normal" speed without | |
514 | * exceeding the thermal design. This is called per-CPU to place the | |
515 | * CPUs into a "safe" operating mode. | |
516 | */ | |
0c530ab8 | 517 | void |
2d21ac55 A |
518 | pmSafeMode(x86_lcpu_t *lcpu, uint32_t flags) |
519 | { | |
520 | if (pmDispatch != NULL | |
521 | && pmDispatch->pmCPUSafeMode != NULL) | |
522 | pmDispatch->pmCPUSafeMode(lcpu, flags); | |
523 | else { | |
524 | /* | |
525 | * Do something reasonable if the KEXT isn't present. | |
526 | * | |
527 | * We only look at the PAUSE and RESUME flags. The other flag(s) | |
528 | * will not make any sense without the KEXT, so just ignore them. | |
529 | * | |
593a1d5f A |
530 | * We set the CPU's state to indicate that it's halted. If this |
531 | * is the CPU we're currently running on, then spin until the | |
532 | * state becomes non-halted. | |
2d21ac55 A |
533 | */ |
534 | if (flags & PM_SAFE_FL_PAUSE) { | |
593a1d5f | 535 | lcpu->state = LCPU_PAUSE; |
2d21ac55 | 536 | if (lcpu == x86_lcpu()) { |
593a1d5f | 537 | while (lcpu->state == LCPU_PAUSE) |
2d21ac55 A |
538 | cpu_pause(); |
539 | } | |
540 | } | |
541 | ||
542 | /* | |
543 | * Clear the halted flag for the specified CPU, that will | |
544 | * get it out of it's spin loop. | |
545 | */ | |
546 | if (flags & PM_SAFE_FL_RESUME) { | |
593a1d5f | 547 | lcpu->state = LCPU_RUN; |
2d21ac55 A |
548 | } |
549 | } | |
550 | } | |
551 | ||
c910b4d9 A |
552 | static uint32_t saved_run_count = 0; |
553 | ||
554 | void | |
555 | machine_run_count(uint32_t count) | |
556 | { | |
557 | if (pmDispatch != NULL | |
558 | && pmDispatch->pmSetRunCount != NULL) | |
559 | pmDispatch->pmSetRunCount(count); | |
560 | else | |
561 | saved_run_count = count; | |
562 | } | |
563 | ||
564 | boolean_t | |
565 | machine_cpu_is_inactive(int cpu) | |
566 | { | |
567 | if (pmDispatch != NULL | |
568 | && pmDispatch->pmIsCPUUnAvailable != NULL) | |
569 | return(pmDispatch->pmIsCPUUnAvailable(cpu_to_lcpu(cpu))); | |
570 | else | |
571 | return(FALSE); | |
572 | } | |
573 | ||
574 | static uint32_t | |
575 | pmGetSavedRunCount(void) | |
576 | { | |
577 | return(saved_run_count); | |
578 | } | |
579 | ||
2d21ac55 A |
580 | /* |
581 | * Returns the root of the package tree. | |
582 | */ | |
583 | static x86_pkg_t * | |
584 | pmGetPkgRoot(void) | |
585 | { | |
586 | return(x86_pkgs); | |
587 | } | |
588 | ||
589 | static boolean_t | |
590 | pmCPUGetHibernate(int cpu) | |
0c530ab8 | 591 | { |
2d21ac55 | 592 | return(cpu_datap(cpu)->cpu_hibernate); |
0c530ab8 A |
593 | } |
594 | ||
2d21ac55 A |
595 | static processor_t |
596 | pmLCPUtoProcessor(int lcpu) | |
597 | { | |
598 | return(cpu_datap(lcpu)->cpu_processor); | |
599 | } | |
600 | ||
c910b4d9 A |
601 | static void |
602 | pmReSyncDeadlines(int cpu) | |
603 | { | |
604 | static boolean_t registered = FALSE; | |
605 | ||
606 | if (!registered) { | |
607 | PM_interrupt_register(&etimer_resync_deadlines); | |
608 | registered = TRUE; | |
609 | } | |
610 | ||
611 | if ((uint32_t)cpu == current_cpu_datap()->lcpu.cpu_num) | |
612 | etimer_resync_deadlines(); | |
613 | else | |
614 | cpu_PM_interrupt(cpu); | |
615 | } | |
616 | ||
b0d623f7 A |
617 | static void |
618 | pmSendIPI(int cpu) | |
619 | { | |
620 | lapic_send_ipi(cpu, LAPIC_PM_INTERRUPT); | |
621 | } | |
622 | ||
7e4a7d39 A |
623 | static rtc_nanotime_t * |
624 | pmGetNanotimeInfo(void) | |
625 | { | |
626 | return(&rtc_nanotime_info); | |
627 | } | |
628 | ||
2d21ac55 A |
629 | /* |
630 | * Called by the power management kext to register itself and to get the | |
631 | * callbacks it might need into other kernel functions. This interface | |
632 | * is versioned to allow for slight mis-matches between the kext and the | |
633 | * kernel. | |
634 | */ | |
0c530ab8 | 635 | void |
2d21ac55 A |
636 | pmKextRegister(uint32_t version, pmDispatch_t *cpuFuncs, |
637 | pmCallBacks_t *callbacks) | |
0c530ab8 | 638 | { |
2d21ac55 | 639 | if (callbacks != NULL && version == PM_DISPATCH_VERSION) { |
c910b4d9 A |
640 | callbacks->setRTCPop = setPop; |
641 | callbacks->resyncDeadlines = pmReSyncDeadlines; | |
642 | callbacks->initComplete = pmInitComplete; | |
643 | callbacks->GetLCPU = pmGetLogicalCPU; | |
644 | callbacks->GetCore = pmGetCore; | |
645 | callbacks->GetDie = pmGetDie; | |
646 | callbacks->GetPackage = pmGetPackage; | |
647 | callbacks->GetMyLCPU = pmGetMyLogicalCPU; | |
648 | callbacks->GetMyCore = pmGetMyCore; | |
649 | callbacks->GetMyDie = pmGetMyDie; | |
650 | callbacks->GetMyPackage = pmGetMyPackage; | |
651 | callbacks->GetPkgRoot = pmGetPkgRoot; | |
652 | callbacks->LockCPUTopology = pmLockCPUTopology; | |
653 | callbacks->GetHibernate = pmCPUGetHibernate; | |
654 | callbacks->LCPUtoProcessor = pmLCPUtoProcessor; | |
655 | callbacks->ThreadBind = thread_bind; | |
656 | callbacks->GetSavedRunCount = pmGetSavedRunCount; | |
b0d623f7 | 657 | callbacks->pmSendIPI = pmSendIPI; |
7e4a7d39 | 658 | callbacks->GetNanotimeInfo = pmGetNanotimeInfo; |
c910b4d9 A |
659 | callbacks->topoParms = &topoParms; |
660 | } else { | |
661 | panic("Version mis-match between Kernel and CPU PM"); | |
2d21ac55 A |
662 | } |
663 | ||
664 | if (cpuFuncs != NULL) { | |
665 | pmDispatch = cpuFuncs; | |
b0d623f7 A |
666 | |
667 | if (pmDispatch->pmIPIHandler != NULL) { | |
668 | lapic_set_pm_func((i386_intr_func_t)pmDispatch->pmIPIHandler); | |
669 | } | |
2d21ac55 | 670 | } |
0c530ab8 A |
671 | } |
672 | ||
2d21ac55 A |
673 | /* |
674 | * Unregisters the power management functions from the kext. | |
675 | */ | |
0c530ab8 | 676 | void |
2d21ac55 | 677 | pmUnRegister(pmDispatch_t *cpuFuncs) |
0c530ab8 | 678 | { |
2d21ac55 A |
679 | if (cpuFuncs != NULL && pmDispatch == cpuFuncs) { |
680 | pmDispatch = NULL; | |
681 | } | |
0c530ab8 | 682 | } |
2d21ac55 | 683 | |
593a1d5f A |
684 | /****************************************************************************** |
685 | * | |
686 | * All of the following are deprecated interfaces and no longer used. | |
687 | * | |
688 | ******************************************************************************/ | |
689 | kern_return_t | |
690 | pmsControl(__unused uint32_t request, __unused user_addr_t reqaddr, | |
691 | __unused uint32_t reqsize) | |
692 | { | |
693 | return(KERN_SUCCESS); | |
694 | } | |
695 | ||
696 | void | |
697 | pmsInit(void) | |
698 | { | |
699 | } | |
700 | ||
701 | void | |
702 | pmsStart(void) | |
703 | { | |
704 | } | |
705 | ||
706 | void | |
707 | pmsPark(void) | |
708 | { | |
709 | } | |
710 | ||
711 | void | |
712 | pmsRun(__unused uint32_t nstep) | |
713 | { | |
714 | } | |
715 | ||
716 | kern_return_t | |
717 | pmsBuild(__unused pmsDef *pd, __unused uint32_t pdsize, | |
718 | __unused pmsSetFunc_t *functab, | |
719 | __unused uint32_t platformData, __unused pmsQueryFunc_t queryFunc) | |
720 | { | |
721 | return(KERN_SUCCESS); | |
722 | } |