6684208 mirror mounted mountpoints don't want to umount when idle
[illumos-gate.git] / usr / src / uts / i86pc / io / cpu_acpi.c
blob11fe37561f8dc354ebc77968311c2cf432183492
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #pragma ident "%Z%%M% %I% %E% SMI"
28 #include <sys/cpu_acpi.h>
30 #define CPU_ACPI_PSTATES_SIZE(cnt) (cnt * sizeof (cpu_acpi_pstate_t))
31 #define CPU_ACPI_PSS_SIZE (sizeof (cpu_acpi_pstate_t) / sizeof (uint32_t))
34 * Map the dip to an ACPI handle for the device.
36 cpu_acpi_handle_t
37 cpu_acpi_init(dev_info_t *dip)
39 cpu_acpi_handle_t handle;
41 handle = kmem_zalloc(sizeof (cpu_acpi_state_t), KM_SLEEP);
43 if (ACPI_FAILURE(acpica_get_handle(dip, &handle->cs_handle))) {
44 kmem_free(handle, sizeof (cpu_acpi_state_t));
45 return (NULL);
47 handle->cs_dip = dip;
48 return (handle);
52 * Free any resources.
54 void
55 cpu_acpi_fini(cpu_acpi_handle_t handle)
57 if (handle->cs_pstates != NULL) {
58 if (CPU_ACPI_PSTATES(handle) != NULL)
59 kmem_free(CPU_ACPI_PSTATES(handle),
60 CPU_ACPI_PSTATES_SIZE(
61 CPU_ACPI_PSTATES_COUNT(handle)));
62 kmem_free(handle->cs_pstates, sizeof (cpu_acpi_pstates_t));
64 kmem_free(handle, sizeof (cpu_acpi_state_t));
68 * Cache the ACPI _PCT data. The _PCT data defines the interface to use
69 * when making power level transitions (i.e., system IO ports, fixed
70 * hardware port, etc).
72 int
73 cpu_acpi_cache_pct(cpu_acpi_handle_t handle)
75 ACPI_BUFFER abuf;
76 ACPI_OBJECT *obj;
77 AML_RESOURCE_GENERIC_REGISTER *greg;
78 cpu_acpi_pct_t *pct;
79 int ret = -1;
80 int i;
83 * Fetch the _PCT (if present) for the CPU node. Since the PCT is
84 * optional, non-existence is not a failure (we just consider
85 * it a fixed hardware case).
87 CPU_ACPI_OBJ_IS_NOT_CACHED(handle, CPU_ACPI_PCT_CACHED);
88 abuf.Length = ACPI_ALLOCATE_BUFFER;
89 abuf.Pointer = NULL;
90 if (ACPI_FAILURE(AcpiEvaluateObjectTyped(handle->cs_handle, "_PCT",
91 NULL, &abuf, ACPI_TYPE_PACKAGE))) {
92 CPU_ACPI_PCT(handle)[0].pc_addrspace_id =
93 ACPI_ADR_SPACE_FIXED_HARDWARE;
94 CPU_ACPI_PCT(handle)[1].pc_addrspace_id =
95 ACPI_ADR_SPACE_FIXED_HARDWARE;
96 return (1);
99 obj = abuf.Pointer;
100 if (obj->Package.Count != 2) {
101 cmn_err(CE_NOTE, "!cpu_acpi: _PCT package bad count %d.",
102 obj->Package.Count);
103 goto out;
107 * Does the package look coherent?
109 for (i = 0; i < obj->Package.Count; i++) {
110 if (obj->Package.Elements[i].Type != ACPI_TYPE_BUFFER) {
111 cmn_err(CE_NOTE, "!cpu_acpi: "
112 "Unexpected data in _PCT package.");
113 goto out;
116 greg = (AML_RESOURCE_GENERIC_REGISTER *)
117 obj->Package.Elements[i].Buffer.Pointer;
118 if (greg->DescriptorType !=
119 ACPI_RESOURCE_NAME_GENERIC_REGISTER) {
120 cmn_err(CE_NOTE, "!cpu_acpi: "
121 "_PCT package has format error.");
122 goto out;
124 if (greg->ResourceLength !=
125 ACPI_AML_SIZE_LARGE(AML_RESOURCE_GENERIC_REGISTER)) {
126 cmn_err(CE_NOTE, "!cpu_acpi: "
127 "_PCT package not right size.");
128 goto out;
130 if (greg->AddressSpaceId != ACPI_ADR_SPACE_FIXED_HARDWARE &&
131 greg->AddressSpaceId != ACPI_ADR_SPACE_SYSTEM_IO) {
132 cmn_err(CE_NOTE, "!cpu_apci: _PCT contains unsupported "
133 "address space type %x", greg->AddressSpaceId);
134 goto out;
139 * Looks good!
141 for (i = 0; i < obj->Package.Count; i++) {
142 greg = (AML_RESOURCE_GENERIC_REGISTER *)
143 obj->Package.Elements[i].Buffer.Pointer;
144 pct = &CPU_ACPI_PCT(handle)[i];
145 pct->pc_addrspace_id = greg->AddressSpaceId;
146 pct->pc_width = greg->BitWidth;
147 pct->pc_offset = greg->BitOffset;
148 pct->pc_asize = greg->AccessSize;
149 pct->pc_address = greg->Address;
151 CPU_ACPI_OBJ_IS_CACHED(handle, CPU_ACPI_PCT_CACHED);
152 ret = 0;
153 out:
154 AcpiOsFree(abuf.Pointer);
155 return (ret);
159 * Cache the ACPI _PSD data. The _PSD data defines CPU dependencies
160 * (think CPU domains).
163 cpu_acpi_cache_psd(cpu_acpi_handle_t handle)
165 ACPI_BUFFER abuf;
166 ACPI_OBJECT *pkg, *elements;
167 cpu_acpi_psd_t *psd;
168 int ret = -1;
171 * Fetch the _PSD (if present) for the CPU node. Since the PSD is
172 * optional, non-existence is not a failure (it's up to the caller
173 * to determine how to handle non-existence).
175 CPU_ACPI_OBJ_IS_NOT_CACHED(handle, CPU_ACPI_PSD_CACHED);
176 abuf.Length = ACPI_ALLOCATE_BUFFER;
177 abuf.Pointer = NULL;
178 if (ACPI_FAILURE(AcpiEvaluateObjectTyped(handle->cs_handle, "_PSD",
179 NULL, &abuf, ACPI_TYPE_PACKAGE))) {
180 return (1);
183 pkg = abuf.Pointer;
184 if (pkg->Package.Count != 1) {
185 cmn_err(CE_NOTE, "!cpu_acpi: _PSD unsupported package "
186 "count %d.", pkg->Package.Count);
187 goto out;
190 if (pkg->Package.Elements[0].Type != ACPI_TYPE_PACKAGE ||
191 pkg->Package.Elements[0].Package.Count != 5) {
192 cmn_err(CE_NOTE, "!cpu_acpi: Unexpected data in _PSD package.");
193 goto out;
195 elements = pkg->Package.Elements[0].Package.Elements;
196 if (elements[0].Integer.Value != 5 || elements[1].Integer.Value != 0) {
197 cmn_err(CE_NOTE, "!cpu_acpi: Unexpected _PSD revision.");
198 goto out;
200 psd = &CPU_ACPI_PSD(handle);
202 psd->pd_entries = elements[0].Integer.Value;
203 psd->pd_revision = elements[1].Integer.Value;
204 psd->pd_domain = elements[2].Integer.Value;
205 psd->pd_type = elements[3].Integer.Value;
206 psd->pd_num = elements[4].Integer.Value;
207 CPU_ACPI_OBJ_IS_CACHED(handle, CPU_ACPI_PSD_CACHED);
208 ret = 0;
209 out:
210 AcpiOsFree(abuf.Pointer);
211 return (ret);
215 * Cache the _PSS data. The _PSS data defines the different power levels
216 * supported by the CPU and the attributes associated with each power level
217 * (i.e., frequency, voltage, etc.). The power levels are number from
218 * highest to lowest. That is, the highest power level is _PSS entry 0
219 * and the lowest power level is the last _PSS entry.
222 cpu_acpi_cache_pstates(cpu_acpi_handle_t handle)
224 ACPI_BUFFER abuf;
225 ACPI_OBJECT *obj, *q, *l;
226 cpu_acpi_pstate_t *pstate;
227 boolean_t eot = B_FALSE;
228 int ret = -1;
229 int cnt;
230 int i, j;
233 * Fetch the _PSS (if present) for the CPU node. If there isn't
234 * one, then CPU power management will not be possible.
236 CPU_ACPI_OBJ_IS_NOT_CACHED(handle, CPU_ACPI_PSS_CACHED);
237 abuf.Length = ACPI_ALLOCATE_BUFFER;
238 abuf.Pointer = NULL;
239 if (ACPI_FAILURE(AcpiEvaluateObjectTyped(handle->cs_handle, "_PSS",
240 NULL, &abuf, ACPI_TYPE_PACKAGE))) {
241 cmn_err(CE_NOTE, "!cpu_acpi: _PSS package not found.");
242 return (1);
244 obj = abuf.Pointer;
245 if (obj->Package.Count < 2) {
246 cmn_err(CE_NOTE, "!cpu_acpi: _PSS package bad count %d.",
247 obj->Package.Count);
248 goto out;
252 * Does the package look coherent?
254 cnt = 0;
255 for (i = 0, l = NULL; i < obj->Package.Count; i++, l = q) {
256 if (obj->Package.Elements[i].Type != ACPI_TYPE_PACKAGE ||
257 obj->Package.Elements[i].Package.Count !=
258 CPU_ACPI_PSS_SIZE) {
259 cmn_err(CE_NOTE, "!cpu_acpi: "
260 "Unexpected data in _PSS package.");
261 goto out;
264 q = obj->Package.Elements[i].Package.Elements;
265 for (j = 0; j < CPU_ACPI_PSS_SIZE; j++) {
266 if (q[j].Type != ACPI_TYPE_INTEGER) {
267 cmn_err(CE_NOTE, "!cpu_acpi: "
268 "_PSS element invalid (type)");
269 goto out;
274 * Ignore duplicate entries.
276 if (l != NULL && l[0].Integer.Value == q[0].Integer.Value)
277 continue;
280 * Some _PSS tables are larger than required
281 * and unused elements are filled with patterns
282 * of 0xff. Simply check here for frequency = 0xffff
283 * and stop counting if found.
285 if (q[0].Integer.Value == 0xffff) {
286 eot = B_TRUE;
287 continue;
291 * We should never find a valid entry after we've hit
292 * an end-of-table entry.
294 if (eot) {
295 cmn_err(CE_NOTE, "!cpu_acpi: "
296 "Unexpected data in _PSS package after eot.");
297 goto out;
301 * pstates must be defined in order from highest to lowest.
303 if (l != NULL && l[0].Integer.Value < q[0].Integer.Value) {
304 cmn_err(CE_NOTE, "!cpu_acpi: "
305 "_PSS package pstate definitions out of order.");
306 goto out;
310 * This entry passes.
312 cnt++;
314 if (cnt == 0)
315 goto out;
318 * Yes, fill in pstate structure.
320 handle->cs_pstates = kmem_zalloc(sizeof (cpu_acpi_pstates_t), KM_SLEEP);
321 CPU_ACPI_PSTATES_COUNT(handle) = cnt;
322 CPU_ACPI_PSTATES(handle) = kmem_zalloc(CPU_ACPI_PSTATES_SIZE(cnt),
323 KM_SLEEP);
324 pstate = CPU_ACPI_PSTATES(handle);
325 for (i = 0, l = NULL; i < obj->Package.Count && cnt > 0; i++, l = q) {
326 uint32_t *up;
328 q = obj->Package.Elements[i].Package.Elements;
331 * Skip duplicate entries.
333 if (l != NULL && l[0].Integer.Value == q[0].Integer.Value)
334 continue;
336 up = (uint32_t *)pstate;
337 for (j = 0; j < CPU_ACPI_PSS_SIZE; j++)
338 up[j] = q[j].Integer.Value;
339 pstate++;
340 cnt--;
342 CPU_ACPI_OBJ_IS_CACHED(handle, CPU_ACPI_PSS_CACHED);
343 ret = 0;
344 out:
345 AcpiOsFree(abuf.Pointer);
346 return (ret);
350 * Cache the _PPC data. The _PPC simply contains an integer value which
351 * represents the highest power level that a CPU should transition to.
352 * That is, it's an index into the array of _PSS entries and will be
353 * greater than or equal to zero.
355 void
356 cpu_acpi_cache_ppc(cpu_acpi_handle_t handle)
358 ACPI_BUFFER abuf;
359 ACPI_OBJECT *obj;
362 * Fetch the _PPC (if present) for the CPU node. Since the PPC is
363 * optional (I think), non-existence is not a failure.
365 CPU_ACPI_OBJ_IS_NOT_CACHED(handle, CPU_ACPI_PPC_CACHED);
366 abuf.Length = ACPI_ALLOCATE_BUFFER;
367 abuf.Pointer = NULL;
368 if (ACPI_FAILURE(AcpiEvaluateObject(handle->cs_handle, "_PPC",
369 NULL, &abuf))) {
370 CPU_ACPI_PPC(handle) = 0;
371 return;
374 obj = (ACPI_OBJECT *)abuf.Pointer;
375 CPU_ACPI_PPC(handle) = obj->Integer.Value;
376 CPU_ACPI_OBJ_IS_CACHED(handle, CPU_ACPI_PPC_CACHED);
377 AcpiOsFree(abuf.Pointer);
381 * Cache the _PCT, _PSS, _PSD and _PPC data.
384 cpu_acpi_cache_data(cpu_acpi_handle_t handle)
386 if (cpu_acpi_cache_pct(handle) < 0) {
387 cmn_err(CE_WARN, "!cpu_acpi: error parsing _PCT for "
388 "CPU instance %d", ddi_get_instance(handle->cs_dip));
389 return (-1);
392 if (cpu_acpi_cache_pstates(handle) != 0) {
393 cmn_err(CE_WARN, "!cpu_acpi: error parsing _PSS for "
394 "CPU instance %d", ddi_get_instance(handle->cs_dip));
395 return (-1);
398 if (cpu_acpi_cache_psd(handle) < 0) {
399 cmn_err(CE_WARN, "!cpu_acpi: error parsing _PSD for "
400 "CPU instance %d", ddi_get_instance(handle->cs_dip));
401 return (-1);
404 cpu_acpi_cache_ppc(handle);
406 return (0);
410 * Register a handler for _PPC change notifications. The _PPC
411 * change notification is the means by which _P
413 void
414 cpu_acpi_install_ppc_handler(cpu_acpi_handle_t handle,
415 ACPI_NOTIFY_HANDLER handler, dev_info_t *dip)
417 char path[MAXNAMELEN];
418 if (ACPI_FAILURE(AcpiInstallNotifyHandler(handle->cs_handle,
419 ACPI_DEVICE_NOTIFY, handler, dip)))
420 cmn_err(CE_NOTE, "!cpu_acpi: Unable to register _PPC "
421 "notify handler for %s", ddi_pathname(dip, path));
425 * Write _PDC.
428 cpu_acpi_write_pdc(cpu_acpi_handle_t handle, uint32_t revision, uint32_t count,
429 uint32_t *capabilities)
431 ACPI_OBJECT obj;
432 ACPI_OBJECT_LIST list = { 1, &obj};
433 uint32_t *buffer;
434 uint32_t *bufptr;
435 uint32_t bufsize;
436 int i;
438 bufsize = (count + 2) * sizeof (uint32_t);
439 buffer = kmem_zalloc(bufsize, KM_SLEEP);
440 buffer[0] = revision;
441 buffer[1] = count;
442 bufptr = &buffer[2];
443 for (i = 0; i < count; i++)
444 *bufptr++ = *capabilities++;
446 obj.Type = ACPI_TYPE_BUFFER;
447 obj.Buffer.Length = bufsize;
448 obj.Buffer.Pointer = (void *)buffer;
451 * _PDC is optional, so don't log failure.
453 if (ACPI_FAILURE(AcpiEvaluateObject(handle->cs_handle, "_PDC",
454 &list, NULL))) {
455 kmem_free(buffer, bufsize);
456 return (-1);
459 kmem_free(buffer, bufsize);
460 return (0);
464 * Write to system IO port.
467 cpu_acpi_write_port(ACPI_IO_ADDRESS address, uint32_t value, uint32_t width)
469 if (ACPI_FAILURE(AcpiOsWritePort(address, value, width))) {
470 cmn_err(CE_NOTE, "cpu_acpi: error writing system IO port "
471 "%lx.", (long)address);
472 return (-1);
474 return (0);
478 * Read from a system IO port.
481 cpu_acpi_read_port(ACPI_IO_ADDRESS address, uint32_t *value, uint32_t width)
483 if (ACPI_FAILURE(AcpiOsReadPort(address, value, width))) {
484 cmn_err(CE_NOTE, "cpu_acpi: error reading system IO port "
485 "%lx.", (long)address);
486 return (-1);
488 return (0);
492 * Return supported frequencies.
494 uint_t
495 cpu_acpi_get_speeds(cpu_acpi_handle_t handle, int **speeds)
497 cpu_acpi_pstate_t *pstate;
498 int *hspeeds;
499 uint_t nspeeds;
500 int i;
502 nspeeds = CPU_ACPI_PSTATES_COUNT(handle);
503 hspeeds = kmem_zalloc(nspeeds * sizeof (int), KM_SLEEP);
504 for (i = 0; i < nspeeds; i++) {
505 pstate = CPU_ACPI_PSTATE(handle, i);
506 hspeeds[i] = CPU_ACPI_FREQ(pstate);
508 *speeds = hspeeds;
509 return (nspeeds);
513 * Free resources allocated by cpu_acpi_get_speeds().
515 void
516 cpu_acpi_free_speeds(int *speeds, uint_t nspeeds)
518 kmem_free(speeds, nspeeds * sizeof (int));