acpi/pstate: Fix the long standing P-State detection problem on Intel CPUs
[dragonfly.git] / sys / dev / acpica5 / acpi_cpu.c
CommitLineData
5ed44076 1/*-
5ebadb2c 2 * Copyright (c) 2003-2005 Nate Lawson (SDG)
5ed44076
MD
3 * Copyright (c) 2001 Michael Smith
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
5ebadb2c 27 * $FreeBSD: src/sys/dev/acpica/acpi_cpu.c,v 1.72 2008/04/12 12:06:00 rpaulo Exp $
5ed44076
MD
28 */
29
30#include "opt_acpi.h"
cc6e2b6e 31
5ed44076
MD
32#include <sys/param.h>
33#include <sys/bus.h>
34#include <sys/kernel.h>
cc6e2b6e 35#include <sys/sysctl.h>
5ed44076 36
5ed44076
MD
37#include <machine/globaldata.h>
38#include <machine/smp.h>
5ed44076
MD
39
40#include "acpi.h"
41#include "acpivar.h"
cc6e2b6e 42#include "acpi_cpu.h"
5ed44076 43
323b7a65
SZ
44#define ACPI_NOTIFY_CX_STATES 0x81 /* _CST changed. */
45
5ed44076
MD
46static int acpi_cpu_probe(device_t dev);
47static int acpi_cpu_attach(device_t dev);
caf65562
SZ
48static struct resource_list *
49 acpi_cpu_get_rlist(device_t, device_t);
50static struct resource *
51 acpi_cpu_alloc_resource(device_t, device_t,
4f7fe8c7 52 int, int *, u_long, u_long, u_long, u_int, int);
caf65562
SZ
53static int acpi_cpu_release_resource(device_t, device_t,
54 int, int, struct resource *);
5ebadb2c 55
cc6e2b6e 56static int acpi_cpu_get_id(uint32_t, uint32_t *, uint32_t *);
323b7a65 57static void acpi_cpu_notify(ACPI_HANDLE, UINT32, void *);
5ed44076
MD
58
59static device_method_t acpi_cpu_methods[] = {
60 /* Device interface */
cc6e2b6e
SZ
61 DEVMETHOD(device_probe, acpi_cpu_probe),
62 DEVMETHOD(device_attach, acpi_cpu_attach),
cc6e2b6e 63 DEVMETHOD(device_detach, bus_generic_detach),
caf65562 64 DEVMETHOD(device_shutdown, bus_generic_shutdown),
cc6e2b6e
SZ
65 DEVMETHOD(device_suspend, bus_generic_suspend),
66 DEVMETHOD(device_resume, bus_generic_resume),
5ebadb2c
HT
67
68 /* Bus interface */
cc6e2b6e
SZ
69 DEVMETHOD(bus_add_child, bus_generic_add_child),
70 DEVMETHOD(bus_print_child, bus_generic_print_child),
71 DEVMETHOD(bus_read_ivar, bus_generic_read_ivar),
72 DEVMETHOD(bus_write_ivar, bus_generic_write_ivar),
caf65562 73 DEVMETHOD(bus_get_resource_list, acpi_cpu_get_rlist),
cc6e2b6e
SZ
74 DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource),
75 DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource),
caf65562
SZ
76 DEVMETHOD(bus_alloc_resource, acpi_cpu_alloc_resource),
77 DEVMETHOD(bus_release_resource, acpi_cpu_release_resource),
cc6e2b6e
SZ
78 DEVMETHOD(bus_driver_added, bus_generic_driver_added),
79 DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
80 DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
81 DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
82 DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
caf65562 83
cc6e2b6e 84 { 0, 0 }
5ed44076
MD
85};
86
87static driver_t acpi_cpu_driver = {
f9d8cd12 88 "cpu",
5ed44076 89 acpi_cpu_methods,
cc6e2b6e 90 sizeof(struct acpi_cpux_softc)
5ed44076
MD
91};
92
93static devclass_t acpi_cpu_devclass;
aa2b9d05 94DRIVER_MODULE(cpu, acpi, acpi_cpu_driver, acpi_cpu_devclass, NULL, NULL);
f9d8cd12 95MODULE_DEPEND(cpu, acpi, 1, 1, 1);
5ed44076
MD
96
97static int
98acpi_cpu_probe(device_t dev)
99{
cc6e2b6e
SZ
100 int acpi_id, cpu_id;
101 ACPI_BUFFER buf;
102 ACPI_HANDLE handle;
103 ACPI_STATUS status;
104 ACPI_OBJECT *obj;
5ed44076 105
f9d8cd12 106 if (acpi_disabled("cpu") || acpi_get_type(dev) != ACPI_TYPE_PROCESSOR)
cc6e2b6e 107 return ENXIO;
5ed44076 108
f9d8cd12 109 handle = acpi_get_handle(dev);
5ed44076 110
cc6e2b6e
SZ
111 /*
112 * Get our Processor object.
113 */
f9d8cd12
MD
114 buf.Pointer = NULL;
115 buf.Length = ACPI_ALLOCATE_BUFFER;
116 status = AcpiEvaluateObject(handle, NULL, NULL, &buf);
5ed44076 117 if (ACPI_FAILURE(status)) {
f9d8cd12 118 device_printf(dev, "probe failed to get Processor obj - %s\n",
5ed44076 119 AcpiFormatException(status));
cc6e2b6e 120 return ENXIO;
5ed44076 121 }
cc6e2b6e 122
f9d8cd12
MD
123 obj = (ACPI_OBJECT *)buf.Pointer;
124 if (obj->Type != ACPI_TYPE_PROCESSOR) {
125 device_printf(dev, "Processor object has bad type %d\n", obj->Type);
126 AcpiOsFree(obj);
cc6e2b6e 127 return ENXIO;
5ed44076
MD
128 }
129
f9d8cd12
MD
130 acpi_id = obj->Processor.ProcId;
131 AcpiOsFree(obj);
5ed44076
MD
132
133 /*
cc6e2b6e
SZ
134 * Find the processor associated with our unit. We could use the
135 * ProcId as a key, however, some boxes do not have the same values
136 * in their Processor object as the ProcId values in the MADT.
5ed44076 137 */
cc6e2b6e
SZ
138 if (acpi_cpu_get_id(device_get_unit(dev), &acpi_id, &cpu_id) != 0)
139 return ENXIO;
5ed44076 140
f9d8cd12 141 acpi_set_magic(dev, cpu_id);
5ebadb2c 142 device_set_desc(dev, "ACPI CPU");
f9d8cd12 143
cc6e2b6e 144 return 0;
f9d8cd12
MD
145}
146
147static int
148acpi_cpu_attach(device_t dev)
149{
cc6e2b6e
SZ
150 struct acpi_cpux_softc *sc = device_get_softc(dev);
151 ACPI_HANDLE handle;
152 device_t child;
47cf4819 153 int cpu_id, cpu_features;
cc6e2b6e 154 struct acpi_softc *acpi_sc;
f9d8cd12 155
cc6e2b6e 156 handle = acpi_get_handle(dev);
5ebadb2c 157 cpu_id = acpi_get_magic(dev);
f9d8cd12 158
cc6e2b6e
SZ
159 acpi_sc = acpi_device_get_parent_softc(dev);
160 if (cpu_id == 0) {
161 sysctl_ctx_init(&sc->glob_sysctl_ctx);
162 sc->glob_sysctl_tree = SYSCTL_ADD_NODE(&sc->glob_sysctl_ctx,
163 SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
164 OID_AUTO, "cpu", CTLFLAG_RD, 0,
165 "node for CPU global settings");
166 if (sc->glob_sysctl_tree == NULL)
167 return ENOMEM;
f9d8cd12 168 }
5ebadb2c 169
cc6e2b6e
SZ
170 sysctl_ctx_init(&sc->pcpu_sysctl_ctx);
171 sc->pcpu_sysctl_tree = SYSCTL_ADD_NODE(&sc->pcpu_sysctl_ctx,
172 SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
173 OID_AUTO, device_get_nameunit(dev), CTLFLAG_RD, 0,
174 "node for per-CPU settings");
175 if (sc->pcpu_sysctl_tree == NULL) {
176 sysctl_ctx_free(&sc->glob_sysctl_ctx);
177 return ENOMEM;
5ebadb2c 178 }
5ed44076 179
47cf4819
SZ
180 /*
181 * Before calling any CPU methods, collect child driver feature hints
182 * and notify ACPI of them. We support unified SMP power control
183 * so advertise this ourselves. Note this is not the same as independent
184 * SMP control where each CPU can have different settings.
185 */
186 cpu_features = ACPI_PDC_MP_C1PXTX | ACPI_PDC_MP_C2C3;
187 cpu_features |= acpi_cpu_md_features();
188
189 /*
190 * CPU capabilities are specified as a buffer of 32-bit integers:
191 * revision, count, and one or more capabilities.
192 */
193 if (cpu_features) {
194 ACPI_OBJECT_LIST arglist;
195 uint32_t cap_set[3];
196 ACPI_OBJECT arg[4];
197 ACPI_STATUS status;
198
199 /* UUID needed by _OSC evaluation */
200 static uint8_t cpu_oscuuid[16] = {
201 0x16, 0xA6, 0x77, 0x40, 0x0C, 0x29, 0xBE, 0x47,
202 0x9E, 0xBD, 0xD8, 0x70, 0x58, 0x71, 0x39, 0x53
203 };
204
205 arglist.Pointer = arg;
206 arglist.Count = 4;
207 arg[0].Type = ACPI_TYPE_BUFFER;
208 arg[0].Buffer.Length = sizeof(cpu_oscuuid);
209 arg[0].Buffer.Pointer = cpu_oscuuid; /* UUID */
210 arg[1].Type = ACPI_TYPE_INTEGER;
211 arg[1].Integer.Value = 1; /* revision */
212 arg[2].Type = ACPI_TYPE_INTEGER;
213 arg[2].Integer.Value = 2; /* # of capabilities integers */
214 arg[3].Type = ACPI_TYPE_BUFFER;
215 arg[3].Buffer.Length = sizeof(cap_set[0]) * 2; /* capabilities buffer */
216 arg[3].Buffer.Pointer = (uint8_t *)cap_set;
217 cap_set[0] = 0;
218 cap_set[1] = cpu_features;
219 status = AcpiEvaluateObject(handle, "_OSC", &arglist, NULL);
220
221 if (!ACPI_SUCCESS(status)) {
222 if (bootverbose)
223 device_printf(dev, "_OSC failed, use _PDC\n");
224
225 arglist.Pointer = arg;
226 arglist.Count = 1;
227 arg[0].Type = ACPI_TYPE_BUFFER;
228 arg[0].Buffer.Length = sizeof(cap_set);
229 arg[0].Buffer.Pointer = (uint8_t *)cap_set;
230 cap_set[0] = 1; /* revision */
231 cap_set[1] = 1; /* # of capabilities integers */
232 cap_set[2] = cpu_features;
233 AcpiEvaluateObject(handle, "_PDC", &arglist, NULL);
234 }
235 }
236
cc6e2b6e
SZ
237 child = BUS_ADD_CHILD(dev, dev, 0, "cpu_cst", -1);
238 if (child == NULL)
239 return ENXIO;
cc6e2b6e
SZ
240 acpi_set_handle(child, handle);
241 acpi_set_magic(child, cpu_id);
323b7a65 242 sc->cpux_cst = child;
5ebadb2c 243
c3a2c0df
SZ
244 child = BUS_ADD_CHILD(dev, dev, 0, "cpu_pst", -1);
245 if (child == NULL)
246 return ENXIO;
247 acpi_set_handle(child, handle);
248 acpi_set_magic(child, cpu_id);
249
5ebadb2c
HT
250 bus_generic_attach(dev);
251
323b7a65
SZ
252 AcpiInstallNotifyHandler(handle, ACPI_DEVICE_NOTIFY, acpi_cpu_notify, sc);
253
cc6e2b6e 254 return 0;
5ed44076
MD
255}
256
257/*
caf65562
SZ
258 * All resources are assigned directly to us by acpi,
259 * so 'child' is bypassed here.
260 */
261static struct resource_list *
262acpi_cpu_get_rlist(device_t dev, device_t child __unused)
263{
264 return BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev);
265}
266
267static struct resource *
268acpi_cpu_alloc_resource(device_t dev, device_t child __unused,
269 int type, int *rid, u_long start, u_long end,
4f7fe8c7 270 u_long count, u_int flags, int cpuid)
caf65562
SZ
271{
272 return BUS_ALLOC_RESOURCE(device_get_parent(dev), dev, type, rid,
4f7fe8c7 273 start, end, count, flags, cpuid);
caf65562
SZ
274}
275
276static int
277acpi_cpu_release_resource(device_t dev, device_t child __unused,
278 int type, int rid, struct resource *r)
279{
280 return BUS_RELEASE_RESOURCE(device_get_parent(dev), dev, type, rid, r);
281}
282
283/*
5ed44076
MD
284 * Find the nth present CPU and return its pc_cpuid as well as set the
285 * pc_acpi_id from the most reliable source.
286 */
287static int
cc6e2b6e 288acpi_cpu_get_id(uint32_t idx, uint32_t *acpi_id, uint32_t *cpu_id)
5ed44076
MD
289{
290 struct mdglobaldata *md;
cc6e2b6e 291 uint32_t i;
5ed44076
MD
292
293 KASSERT(acpi_id != NULL, ("Null acpi_id"));
294 KASSERT(cpu_id != NULL, ("Null cpu_id"));
1fee1154 295 for (i = 0; i < ncpus; i++) {
da23a592 296 if ((smp_active_mask & CPUMASK(i)) == 0)
5ed44076
MD
297 continue;
298 md = (struct mdglobaldata *)globaldata_find(i);
299 KASSERT(md != NULL, ("no pcpu data for %d", i));
300 if (idx-- == 0) {
301 /*
302 * If pc_acpi_id was not initialized (e.g., a non-APIC UP box)
303 * override it with the value from the ASL. Otherwise, if the
304 * two don't match, prefer the MADT-derived value. Finally,
305 * return the pc_cpuid to reference this processor.
306 */
307 if (md->gd_acpi_id == 0xffffffff)
5ebadb2c 308 md->gd_acpi_id = *acpi_id;
5ed44076
MD
309 else if (md->gd_acpi_id != *acpi_id)
310 *acpi_id = md->gd_acpi_id;
311 *cpu_id = md->mi.gd_cpuid;
cc6e2b6e 312 return 0;
911afb96 313 }
5ebadb2c 314 }
cc6e2b6e 315 return ESRCH;
5ed44076 316}
323b7a65
SZ
317
318static void
319acpi_cpu_notify(ACPI_HANDLE handler __unused, UINT32 notify, void *xsc)
320{
321 struct acpi_cpux_softc *sc = xsc;
322
323 switch (notify) {
324 case ACPI_NOTIFY_CX_STATES:
325 if (sc->cpux_cst_notify != NULL)
326 sc->cpux_cst_notify(sc->cpux_cst);
327 break;
328 }
329}