]>
Commit | Line | Data |
---|---|---|
89b3af67 A |
1 | /* |
2 | * Copyright (c) 2004-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 | ||
29 | /* | |
30 | * CPU-specific power management support. | |
31 | * | |
32 | * Implements the "wrappers" to the KEXT. | |
33 | */ | |
34 | #include <i386/machine_routines.h> | |
35 | #include <i386/machine_cpu.h> | |
36 | #include <i386/misc_protos.h> | |
37 | #include <i386/pmap.h> | |
38 | #include <i386/asm.h> | |
39 | #include <i386/mp.h> | |
40 | #include <i386/proc_reg.h> | |
41 | #include <kern/pms.h> | |
42 | #include <kern/processor.h> | |
43 | #include <i386/pmCPU.h> | |
44 | #include <i386/cpuid.h> | |
45 | #include <i386/rtclock.h> | |
46 | #if MACH_KDB | |
47 | #include <i386/db_machdep.h> | |
48 | #include <ddb/db_aout.h> | |
49 | #include <ddb/db_access.h> | |
50 | #include <ddb/db_sym.h> | |
51 | #include <ddb/db_variables.h> | |
52 | #include <ddb/db_command.h> | |
53 | #include <ddb/db_output.h> | |
54 | #include <ddb/db_expr.h> | |
55 | #endif | |
56 | ||
57 | extern int disableConsoleOutput; | |
58 | ||
59 | decl_simple_lock_data(,pm_init_lock); | |
60 | ||
61 | /* | |
62 | * The following is set when the KEXT loads and initializes. | |
63 | */ | |
64 | pmDispatch_t *pmDispatch = NULL; | |
65 | ||
66 | /* | |
67 | * Current power management states (for use until KEXT is loaded). | |
68 | */ | |
69 | static pmInitState_t pmInitState; | |
70 | ||
71 | /* | |
72 | * Nap control variables: | |
73 | */ | |
74 | uint32_t napCtl = 0; /* Defaults to neither napping | |
75 | nor halting */ | |
76 | uint32_t forcenap = 0; /* Force nap (fn) boot-arg controls */ | |
77 | uint32_t maxBusDelay = 0xFFFFFFFF; /* Maximum memory bus delay that | |
78 | I/O devices can tolerate | |
79 | before errors (nanoseconds) */ | |
80 | uint32_t C4C2SnoopDelay = 0; /* C4 to C2 transition time - | |
81 | time before a C4 system | |
82 | can snoop (nanoseconds) */ | |
83 | ||
84 | /* | |
85 | * We are being asked to set PState (sel). | |
86 | */ | |
87 | void | |
88 | pmsCPUSet(uint32_t sel) | |
89 | { | |
90 | if (pmDispatch != NULL && pmDispatch->pmsCPUSet != NULL) | |
91 | (*pmDispatch->pmsCPUSet)(sel); | |
92 | else | |
93 | pmInitState.PState = sel; | |
94 | } | |
95 | ||
96 | /* | |
97 | * This code configures the initial step tables. It should be called after | |
98 | * the timebase frequency is initialized. | |
99 | * | |
100 | * Note that this is not used in normal operation. It is strictly for | |
101 | * debugging/testing purposes. | |
102 | */ | |
103 | void | |
104 | pmsCPUConf(void) | |
105 | { | |
106 | ||
107 | if (pmDispatch != NULL && pmDispatch->pmsCPUConf != NULL) | |
108 | (*pmDispatch->pmsCPUConf)(); | |
109 | } | |
110 | ||
111 | /* | |
112 | * Machine-dependent initialization. | |
113 | */ | |
114 | void | |
115 | pmsCPUMachineInit(void) | |
116 | { | |
117 | /* | |
118 | * Initialize some of the initial state to "uninitialized" until | |
119 | * it gets set with something more useful. This allows the KEXT | |
120 | * to determine if the initial value was actually set to something. | |
121 | */ | |
122 | pmInitState.PState = -1; | |
123 | pmInitState.PLimit = -1; | |
124 | ||
125 | if (pmDispatch != NULL && pmDispatch->pmsCPUMachineInit != NULL) | |
126 | (*pmDispatch->pmsCPUMachineInit)(); | |
127 | } | |
128 | ||
129 | /* | |
130 | * This function should be called once for each processor to force the | |
131 | * processor to the correct initial voltage and frequency. | |
132 | */ | |
133 | void | |
134 | pmsCPUInit(void) | |
135 | { | |
136 | pmsCPUMachineInit(); | |
137 | if (pmDispatch != NULL && pmDispatch->pmsCPUInit != NULL) | |
138 | (*pmDispatch->pmsCPUInit)(); | |
139 | } | |
140 | ||
141 | /* | |
142 | * Broadcast a change to all processing including ourselves. | |
143 | */ | |
144 | void | |
145 | pmsCPURun(uint32_t nstep) | |
146 | { | |
147 | if (pmDispatch != NULL && pmDispatch->pmsCPURun != NULL) | |
148 | (*pmDispatch->pmsCPURun)(nstep); | |
149 | } | |
150 | ||
151 | /* | |
152 | * Return the current state of a core. | |
153 | */ | |
154 | uint32_t | |
155 | pmsCPUQuery(void) | |
156 | { | |
157 | if (pmDispatch != NULL && pmDispatch->pmsCPUQuery != NULL) | |
158 | return((*pmDispatch->pmsCPUQuery)()); | |
159 | ||
160 | /* | |
161 | * Return a non-sense value. | |
162 | */ | |
163 | return((~0) << 16); | |
164 | } | |
165 | ||
166 | /* | |
167 | * Return the current state of the package. | |
168 | */ | |
169 | uint32_t | |
170 | pmsCPUPackageQuery(void) | |
171 | { | |
172 | if (pmDispatch != NULL && pmDispatch->pmsCPUPackageQuery != NULL) | |
173 | return((*pmDispatch->pmsCPUPackageQuery)()); | |
174 | ||
175 | /* | |
176 | * Return a non-sense value. | |
177 | */ | |
178 | return((~0) << 16); | |
179 | } | |
180 | ||
181 | /* | |
182 | * Force the CPU package to the lowest power level. This is a low-level | |
183 | * interface meant to be called from the panic or debugger code to bring | |
184 | * the CPU to a safe power level for unmanaged operation. | |
185 | * | |
186 | * Note that while this will bring an entire package to a safe level, it | |
187 | * cannot affect other packages. As a general rule, this should be run on | |
188 | * every code as part of entering the debugger or on the panic path. | |
189 | */ | |
190 | void | |
191 | pmsCPUYellowFlag(void) | |
192 | { | |
193 | if (pmDispatch != NULL && pmDispatch->pmsCPUYellowFlag != NULL) | |
194 | (*pmDispatch->pmsCPUYellowFlag)(); | |
195 | } | |
196 | ||
197 | /* | |
198 | * Restore the CPU to the power state it was in before a yellow flag. | |
199 | */ | |
200 | void | |
201 | pmsCPUGreenFlag(void) | |
202 | { | |
203 | if (pmDispatch != NULL && pmDispatch->pmsCPUGreenFlag != NULL) | |
204 | (*pmDispatch->pmsCPUGreenFlag)(); | |
205 | } | |
206 | ||
207 | /* | |
208 | * Load a new ratio/VID table. | |
209 | * | |
210 | * Note that this interface is specific to the Intel SpeedStep implementation. | |
211 | * It is expected that this will only be called once to override the default | |
212 | * ratio/VID table when the platform starts. | |
213 | * | |
214 | * Normally, the table will need to be replaced at the same time that the | |
215 | * stepper program proper is replaced, as the PState indices from an old | |
216 | * program may no longer be valid. When replacing the default program this | |
217 | * should not be a problem as any new table will have at least two PState | |
218 | * entries and the default program only references P0 and P1. | |
219 | */ | |
220 | kern_return_t | |
221 | pmsCPULoadVIDTable(uint16_t *tablep, int nstates) | |
222 | { | |
223 | if (pmDispatch != NULL && pmDispatch->pmsCPULoadVIDTable != NULL) | |
224 | return((*pmDispatch->pmsCPULoadVIDTable)(tablep, nstates)); | |
225 | else { | |
226 | int i; | |
227 | ||
228 | if (nstates > MAX_PSTATES) | |
229 | return(KERN_FAILURE); | |
230 | ||
231 | for (i = 0; i < nstates; i += 1) | |
232 | pmInitState.VIDTable[i] = tablep[i]; | |
233 | } | |
234 | return(KERN_SUCCESS); | |
235 | } | |
236 | ||
237 | /* | |
238 | * Set the (global) PState limit. CPUs will not be permitted to run at | |
239 | * a lower (more performant) PState than this. | |
240 | */ | |
241 | kern_return_t | |
242 | pmsCPUSetPStateLimit(uint32_t limit) | |
243 | { | |
244 | if (pmDispatch != NULL && pmDispatch->pmsCPUSetPStateLimit != NULL) | |
245 | return((*pmDispatch->pmsCPUSetPStateLimit)(limit)); | |
246 | ||
247 | pmInitState.PLimit = limit; | |
248 | return(KERN_SUCCESS); | |
249 | } | |
250 | ||
251 | /* | |
252 | * Initialize the Cstate change code. | |
253 | */ | |
254 | void | |
255 | power_management_init(void) | |
256 | { | |
257 | uint32_t cpuModel; | |
258 | uint32_t cpuFamily; | |
259 | uint32_t xcpuid[4]; | |
260 | ||
261 | /* | |
262 | * Initialize the lock for the KEXT initialization. | |
263 | */ | |
264 | simple_lock_init(&pm_init_lock, 0); | |
265 | ||
266 | /* | |
267 | * XXX | |
268 | * | |
269 | * The following is a hack to disable power management on some systems | |
270 | * until the KEXT is done. This is strictly temporary!!! | |
271 | */ | |
272 | do_cpuid(1, xcpuid); | |
273 | cpuFamily = (xcpuid[eax] >> 8) & 0xf; | |
274 | cpuModel = (xcpuid[eax] >> 4) & 0xf; | |
275 | ||
276 | if (cpuFamily != 0x6 || cpuModel < 0xe) | |
277 | pmDispatch = NULL; | |
278 | ||
279 | if (pmDispatch != NULL && pmDispatch->cstateInit != NULL) | |
280 | (*pmDispatch->cstateInit)(); | |
281 | } | |
282 | ||
283 | /* | |
284 | * This function will update the system nap policy. It should be called | |
285 | * whenever conditions change: when the system is ready to being napping | |
286 | * and if something changes the rules (e.g. a sysctl altering the policy | |
287 | * for debugging). | |
288 | */ | |
289 | void | |
290 | machine_nap_policy(void) | |
291 | { | |
292 | if (pmDispatch != NULL && pmDispatch->cstateNapPolicy != NULL) | |
293 | napCtl = (*pmDispatch->cstateNapPolicy)(forcenap, napCtl); | |
294 | } | |
295 | ||
296 | /* | |
297 | * ACPI calls the following routine to set/update mwait hints. A table | |
298 | * (possibly null) specifies the available Cstates and their hints, all | |
299 | * other states are assumed to be invalid. ACPI may update available | |
300 | * states to change the nap policy (for example, while AC power is | |
301 | * available). | |
302 | */ | |
303 | kern_return_t | |
304 | Cstate_table_set(Cstate_hint_t *tablep, unsigned int nstates) | |
305 | { | |
306 | if (forcenap) | |
307 | return(KERN_SUCCESS); | |
308 | ||
309 | if (pmDispatch != NULL && pmDispatch->cstateTableSet != NULL) | |
310 | return((*pmDispatch->cstateTableSet)(tablep, nstates)); | |
311 | else { | |
312 | unsigned int i; | |
313 | ||
314 | for (i = 0; i < nstates; i += 1) { | |
315 | pmInitState.CStates[i].number = tablep[i].number; | |
316 | pmInitState.CStates[i].hint = tablep[i].hint; | |
317 | } | |
318 | ||
319 | pmInitState.CStatesCount = nstates; | |
320 | } | |
321 | return(KERN_SUCCESS); | |
322 | } | |
323 | ||
324 | static inline void | |
325 | sti(void) { | |
326 | __asm__ volatile ( "sti" : : : "memory"); | |
327 | } | |
328 | ||
329 | /* | |
330 | * Called when the CPU is idle. It will choose the best C state to | |
331 | * be in. | |
332 | */ | |
333 | void | |
334 | machine_idle_cstate(void) | |
335 | { | |
336 | if (pmDispatch != NULL && pmDispatch->cstateMachineIdle != NULL) | |
337 | (*pmDispatch->cstateMachineIdle)(napCtl); | |
338 | else { | |
339 | sti(); | |
340 | } | |
341 | } | |
342 | ||
343 | static pmStats_t * | |
344 | pmsCPUStats(void) | |
345 | { | |
346 | cpu_data_t *pp; | |
347 | ||
348 | pp = current_cpu_datap(); | |
349 | return(&pp->cpu_pmStats); | |
350 | } | |
351 | ||
352 | static pmsd * | |
353 | pmsCPUStepperData(void) | |
354 | { | |
355 | cpu_data_t *pp; | |
356 | ||
357 | pp = current_cpu_datap(); | |
358 | return(&pp->pms); | |
359 | } | |
360 | ||
361 | static uint64_t * | |
362 | CPUHPETAddr(void) | |
363 | { | |
364 | cpu_data_t *pp; | |
365 | pp = current_cpu_datap(); | |
366 | return(pp->cpu_pmHpet); | |
367 | } | |
368 | ||
369 | /* | |
370 | * Called by the power management kext to register itself and to get the | |
371 | * callbacks it might need into other power management functions. | |
372 | */ | |
373 | void | |
374 | pmRegister(pmDispatch_t *cpuFuncs, pmCallBacks_t *callbacks) | |
375 | { | |
376 | if (callbacks != NULL) { | |
377 | callbacks->Park = pmsPark; | |
378 | callbacks->Run = pmsRun; | |
379 | callbacks->RunLocal = pmsRunLocal; | |
380 | callbacks->SetStep = pmsSetStep; | |
381 | callbacks->NapPolicy = machine_nap_policy; | |
382 | callbacks->Build = pmsBuild; | |
383 | callbacks->Stats = pmsCPUStats; | |
384 | callbacks->StepperData = pmsCPUStepperData; | |
385 | callbacks->HPETAddr = CPUHPETAddr; | |
386 | callbacks->InitState = &pmInitState; | |
387 | callbacks->resetPop = resetPop; | |
388 | } | |
389 | ||
390 | if (cpuFuncs != NULL) | |
391 | pmDispatch = cpuFuncs; | |
392 | } | |
393 | ||
394 | /* | |
395 | * Unregisters the power management functions from the kext. | |
396 | */ | |
397 | void | |
398 | pmUnRegister(pmDispatch_t *cpuFuncs) | |
399 | { | |
400 | if (cpuFuncs != NULL && pmDispatch == cpuFuncs) | |
401 | pmDispatch = NULL; | |
402 | } | |
403 | ||
404 | #if MACH_KDB | |
405 | /* | |
406 | * XXX stubs for now | |
407 | */ | |
408 | void | |
409 | db_cfg(__unused db_expr_t addr, | |
410 | __unused int have_addr, | |
411 | __unused db_expr_t count, | |
412 | __unused char *modif) | |
413 | { | |
414 | return; | |
415 | } | |
416 | ||
417 | void | |
418 | db_display_iokit(__unused db_expr_t addr, | |
419 | __unused int have_addr, | |
420 | __unused db_expr_t count, | |
421 | __unused char *modif) | |
422 | { | |
423 | return; | |
424 | } | |
425 | ||
426 | void | |
427 | db_dtimers(__unused db_expr_t addr, | |
428 | __unused int have_addr, | |
429 | __unused db_expr_t count, | |
430 | __unused char *modif) | |
431 | { | |
432 | return; | |
433 | } | |
434 | ||
435 | void | |
436 | db_intcnt(__unused db_expr_t addr, | |
437 | __unused int have_addr, | |
438 | __unused db_expr_t count, | |
439 | __unused char *modif) | |
440 | { | |
441 | return; | |
442 | } | |
443 | ||
444 | void | |
445 | db_nap(__unused db_expr_t addr, | |
446 | __unused int have_addr, | |
447 | __unused db_expr_t count, | |
448 | __unused char *modif) | |
449 | { | |
450 | return; | |
451 | } | |
452 | ||
453 | void | |
454 | db_pmgr(__unused db_expr_t addr, | |
455 | __unused int have_addr, | |
456 | __unused db_expr_t count, | |
457 | __unused char *modif) | |
458 | { | |
459 | return; | |
460 | } | |
461 | ||
462 | void | |
463 | db_test(__unused db_expr_t addr, | |
464 | __unused int have_addr, | |
465 | __unused db_expr_t count, | |
466 | __unused char *modif) | |
467 | { | |
468 | return; | |
469 | } | |
470 | ||
471 | void | |
472 | db_getpmgr(__unused pmData_t *pmj) | |
473 | { | |
474 | } | |
475 | #endif |