1241675Suqs/*
2241675Suqs * Copyright 2012, Haiku, Inc. All Rights Reserved.
3241675Suqs *
4241675Suqs * Distributed under the terms of the MIT License.
5241675Suqs *
6241675Suqs * Authors:
7241675Suqs *		Yongcong Du <ycdu.vmcore@gmail.com>
8241675Suqs */
9241675Suqs
10241675Suqs
11241675Suqs#include <stdio.h>
12241675Suqs#include <stdlib.h>
13241675Suqs#include <string.h>
14241675Suqs
15241675Suqs#include <ACPI.h>
16241675Suqs#include <Drivers.h>
17241675Suqs#include <Errors.h>
18241675Suqs#include <KernelExport.h>
19241675Suqs
20241675Suqs#include <arch_system_info.h>
21241675Suqs#include <cpu.h>
22241675Suqs#include <cpuidle.h>
23241675Suqs#include <smp.h>
24241675Suqs
25241675Suqs#include "x86_cpuidle.h"
26241675Suqs
27241675Suqs
28241675Suqs#define ACPI_PDC_REVID		0x1
29241675Suqs#define ACPI_OSC_QUERY		(1 << 0)
30241675Suqs
31241675Suqs#define ACPI_PDC_P_FFH		(1 << 0)
32241675Suqs#define ACPI_PDC_C_C1_HALT	(1 << 1)
33241675Suqs#define ACPI_PDC_T_FFH		(1 << 2)
34241675Suqs#define ACPI_PDC_SMP_C1PT	(1 << 3)
35241675Suqs#define ACPI_PDC_SMP_C2C3	(1 << 4)
36241675Suqs#define ACPI_PDC_SMP_P_SW	(1 << 5)
37241675Suqs#define ACPI_PDC_SMP_C_SW	(1 << 6)
38241675Suqs#define ACPI_PDC_SMP_T_SW	(1 << 7)
39241675Suqs#define ACPI_PDC_C_C1_FFH	(1 << 8)
40241675Suqs#define ACPI_PDC_C_C2C3_FFH	(1 << 9)
41241675Suqs#define ACPI_PDC_P_HWCOORD	(1 << 11)
42241675Suqs
43241675Suqs// Bus Master check required
44241675Suqs#define ACPI_PDC_GAS_BM		(1 << 1)
45241675Suqs
46241675Suqs#define ACPI_CSTATE_HALT	0x1
47241675Suqs#define ACPI_CSTATE_SYSIO	0x2
48241675Suqs#define ACPI_CSTATE_FFH		0x3
49241675Suqs
50241675Suqs// Bus Master Check
51241675Suqs#define ACPI_FLAG_C_BM		(1 << 0)
52241675Suqs// Bus master arbitration
53241675Suqs#define ACPI_FLAG_C_ARB		(1 << 1)
54241675Suqs
55241675Suqs// Copied from acpica's actypes.h, where's the best place to put?
56241675Suqs#define ACPI_BITREG_BUS_MASTER_STATUS           0x01
57241675Suqs#define ACPI_BITREG_BUS_MASTER_RLD              0x0F
58241675Suqs#define ACPI_BITREG_ARB_DISABLE                 0x13
59241675Suqs
60241675Suqs#define ACPI_STATE_C0                   (uint8) 0
61241675Suqs#define ACPI_STATE_C1                   (uint8) 1
62241675Suqs#define ACPI_STATE_C2                   (uint8) 2
63241675Suqs#define ACPI_STATE_C3                   (uint8) 3
64241675Suqs#define ACPI_C_STATES_MAX               ACPI_STATE_C3
65241675Suqs#define ACPI_C_STATE_COUNT              4
66241675Suqs
67241675Suqs
68241675Suqs#define ACPI_CPUIDLE_MODULE_NAME "drivers/power/x86_cpuidle/acpi/driver_v1"
69241675Suqs
70241675Suqs
71241675Suqsstruct acpicpu_reg {
72241675Suqs	uint8	reg_desc;
73241675Suqs	uint16	reg_reslen;
74241675Suqs	uint8	reg_spaceid;
75241675Suqs	uint8	reg_bitwidth;
76241675Suqs	uint8	reg_bitoffset;
77241675Suqs	uint8	reg_accesssize;
78241675Suqs	uint64	reg_addr;
79241675Suqs} __attribute__((packed));
80241675Suqs
81241675Suqsstruct acpi_cpuidle_driver_info {
82241675Suqs	device_node *node;
83241675Suqs	acpi_device_module_info *acpi;
84241675Suqs	acpi_device acpi_cookie;
85241675Suqs	uint32 flags;
86241675Suqs	int32 cpuIndex;
87241675Suqs};
88241675Suqs
89241675Suqsstruct acpi_cstate_info {
90241675Suqs	uint32 address;
91241675Suqs	uint8 skip_bm_sts;
92241675Suqs	uint8 method;
93241675Suqs	uint8 type;
94241675Suqs};
95241675Suqs
96241675Suqs
97241675Suqsstatic acpi_cpuidle_driver_info *sAcpiProcessor[SMP_MAX_CPUS];
98241675Suqsstatic CpuidleDevice sAcpiDevice;
99241675Suqsstatic device_manager_info *sDeviceManager;
100241675Suqsstatic acpi_module_info *sAcpi;
101241675Suqs
102241675SuqsCpuidleModuleInfo *gIdle;
103241675Suqs
104241675Suqs
105241675Suqsstatic status_t
106241675Suqsacpi_eval_pdc(acpi_cpuidle_driver_info *device)
107241675Suqs{
108241675Suqs	acpi_objects arg;
109241675Suqs	acpi_object_type obj;
110241675Suqs	uint32 cap[3];
111241675Suqs
112241675Suqs	arg.count = 1;
113241675Suqs	arg.pointer = &obj;
114241675Suqs	cap[0] = 1;
115241675Suqs	cap[1] = 1;
116241675Suqs	cap[2] = ACPI_PDC_C_C1_HALT | ACPI_PDC_SMP_C1PT | ACPI_PDC_SMP_C2C3;
117241675Suqs	cap[2] |= ACPI_PDC_SMP_P_SW | ACPI_PDC_SMP_C_SW | ACPI_PDC_SMP_T_SW;
118241675Suqs	cap[2] |= ACPI_PDC_C_C1_FFH | ACPI_PDC_C_C2C3_FFH;
119241675Suqs	cap[2] |= ACPI_PDC_SMP_T_SW | ACPI_PDC_P_FFH | ACPI_PDC_P_HWCOORD
120241675Suqs		| ACPI_PDC_T_FFH;
121241675Suqs	obj.object_type = ACPI_TYPE_BUFFER;
122241675Suqs	obj.data.buffer.length = sizeof(cap);
123241675Suqs	obj.data.buffer.buffer = cap;
124241675Suqs	status_t status = device->acpi->evaluate_method(device->acpi_cookie, "_PDC",
125241675Suqs		&arg, NULL);
126241675Suqs	return status;
127241675Suqs}
128241675Suqs
129241675Suqs
130241675Suqsstatic status_t
131241675Suqsacpi_eval_osc(acpi_cpuidle_driver_info *device)
132241675Suqs{
133241675Suqs	// guid for intel platform
134241675Suqs	dprintf("%s@%p\n", __func__, device->acpi_cookie);
135241675Suqs	static uint8 uuid[] = {
136241675Suqs		0x16, 0xA6, 0x77, 0x40, 0x0C, 0x29, 0xBE, 0x47,
137241675Suqs		0x9E, 0xBD, 0xD8, 0x70, 0x58, 0x71, 0x39, 0x53
138241675Suqs	};
139241675Suqs	uint32 cap[2];
140241675Suqs	cap[0] = 0;
141241675Suqs	cap[1] = ACPI_PDC_C_C1_HALT | ACPI_PDC_SMP_C1PT | ACPI_PDC_SMP_C2C3;
142241675Suqs	cap[1] |= ACPI_PDC_SMP_P_SW | ACPI_PDC_SMP_C_SW | ACPI_PDC_SMP_T_SW;
143241675Suqs	cap[1] |= ACPI_PDC_C_C1_FFH | ACPI_PDC_C_C2C3_FFH;
144241675Suqs	cap[1] |= ACPI_PDC_SMP_T_SW | ACPI_PDC_P_FFH | ACPI_PDC_P_HWCOORD
145241675Suqs		| ACPI_PDC_T_FFH;
146241675Suqs
147241675Suqs	acpi_objects arg;
148241675Suqs	acpi_object_type obj[4];
149241675Suqs
150241675Suqs	arg.count = 4;
151241675Suqs	arg.pointer = obj;
152241675Suqs
153241675Suqs	obj[0].object_type = ACPI_TYPE_BUFFER;
154241675Suqs	obj[0].data.buffer.length = sizeof(uuid);
155241675Suqs	obj[0].data.buffer.buffer = uuid;
156241675Suqs	obj[1].object_type = ACPI_TYPE_INTEGER;
157241675Suqs	obj[1].data.integer = ACPI_PDC_REVID;
158241675Suqs	obj[2].object_type = ACPI_TYPE_INTEGER;
159241675Suqs	obj[2].data.integer = sizeof(cap)/sizeof(cap[0]);
160241675Suqs	obj[3].object_type = ACPI_TYPE_BUFFER;
161241675Suqs	obj[3].data.buffer.length = sizeof(cap);
162241675Suqs	obj[3].data.buffer.buffer = (void *)cap;
163241675Suqs
164241675Suqs	acpi_data buf;
165241675Suqs	buf.pointer = NULL;
166241675Suqs	buf.length = ACPI_ALLOCATE_LOCAL_BUFFER;
167241675Suqs	status_t status = device->acpi->evaluate_method(device->acpi_cookie, "_OSC",
168241675Suqs		&arg, &buf);
169241675Suqs	if (status != B_OK)
170241675Suqs		return status;
171241675Suqs	acpi_object_type *osc = (acpi_object_type *)buf.pointer;
172241675Suqs	if (osc->object_type != ACPI_TYPE_BUFFER)
173241675Suqs		return B_BAD_TYPE;
174241675Suqs	if (osc->data.buffer.length != sizeof(cap))
175241675Suqs		return B_BUFFER_OVERFLOW;
176241675Suqs	return status;
177241675Suqs}
178241675Suqs
179241675Suqs
180241675Suqsstatic inline bool
181241675Suqsacpi_cstate_bm_check(void)
182241675Suqs{
183241675Suqs	uint32 val;
184241675Suqs	sAcpi->read_bit_register(ACPI_BITREG_BUS_MASTER_STATUS, &val);
185241675Suqs	if (!val)
186241675Suqs		return false;
187241675Suqs	sAcpi->write_bit_register(ACPI_BITREG_BUS_MASTER_STATUS, 1);
188241675Suqs
189241675Suqs	return true;
190241675Suqs}
191241675Suqs
192241675Suqs
193241675Suqsstatic inline void
194241675Suqsacpi_cstate_ffh_enter(CpuidleCstate *cState)
195241675Suqs{
196241675Suqs	cpu_ent *cpu = get_cpu_struct();
197241675Suqs	if (cpu->invoke_scheduler)
198241675Suqs		return;
199241675Suqs
200241675Suqs	x86_monitor((void *)&cpu->invoke_scheduler, 0, 0);
201241675Suqs	if (!cpu->invoke_scheduler)
202241675Suqs		x86_mwait((unsigned long)cState->pData, 1);
203241675Suqs}
204241675Suqs
205241675Suqs
206241675Suqsstatic inline void
207241675Suqsacpi_cstate_halt(void)
208241675Suqs{
209241675Suqs	cpu_ent *cpu = get_cpu_struct();
210241675Suqs	if (cpu->invoke_scheduler)
211241675Suqs		return;
212241675Suqs	asm("hlt");
213241675Suqs}
214241675Suqs
215241675Suqs
216241675Suqsstatic void
217241675Suqsacpi_cstate_enter(CpuidleCstate *cState)
218241675Suqs{
219241675Suqs	acpi_cstate_info *ci = (acpi_cstate_info *)cState->pData;
220241675Suqs	if (ci->method == ACPI_CSTATE_FFH)
221241675Suqs		acpi_cstate_ffh_enter(cState);
222241675Suqs	else if (ci->method == ACPI_CSTATE_SYSIO)
223241675Suqs		in8(ci->address);
224241675Suqs	else
225241675Suqs		acpi_cstate_halt();
226241675Suqs}
227241675Suqs
228241675Suqs
229241675Suqsstatic int32
230241675Suqsacpi_cstate_idle(int32 state, CpuidleDevice *device)
231241675Suqs{
232241675Suqs	CpuidleCstate *cState = &device->cStates[state];
233241675Suqs	acpi_cstate_info *ci = (acpi_cstate_info *)cState->pData;
234241675Suqs	if (!ci->skip_bm_sts) {
235241675Suqs		// we fall back to C1 if there's bus master activity
236241675Suqs		if (acpi_cstate_bm_check())
237241675Suqs			state = 1;
238241675Suqs	}
239241675Suqs	if (ci->type != ACPI_STATE_C3)
240241675Suqs		acpi_cstate_enter(cState);
241241675Suqs
242241675Suqs	// set BM_RLD for Bus Master to activity to wake the system from C3
243241675Suqs	// With Newer chipsets BM_RLD is a NOP Since DMA is automatically handled
244241675Suqs	// during C3 State
245241675Suqs	acpi_cpuidle_driver_info *pi = sAcpiProcessor[smp_get_current_cpu()];
246241675Suqs	if (pi->flags & ACPI_FLAG_C_BM)
247241675Suqs		sAcpi->write_bit_register(ACPI_BITREG_BUS_MASTER_RLD, 1);
248241675Suqs
249241675Suqs	// disable bus master arbitration during C3
250241675Suqs	if (pi->flags & ACPI_FLAG_C_ARB)
251241675Suqs		sAcpi->write_bit_register(ACPI_BITREG_ARB_DISABLE, 1);
252241675Suqs
253241675Suqs	acpi_cstate_enter(cState);
254241675Suqs
255241675Suqs	// clear BM_RLD and re-enable the arbiter
256241675Suqs	if (pi->flags & ACPI_FLAG_C_BM)
257241675Suqs		sAcpi->write_bit_register(ACPI_BITREG_BUS_MASTER_RLD, 0);
258241675Suqs
259241675Suqs	if (pi->flags & ACPI_FLAG_C_ARB)
260241675Suqs		sAcpi->write_bit_register(ACPI_BITREG_ARB_DISABLE, 0);
261241675Suqs
262241675Suqs	return state;
263241675Suqs}
264241675Suqs
265241675Suqs
266241675Suqsstatic status_t
267241675Suqsacpi_cstate_add(acpi_object_type *object, CpuidleCstate *cState)
268241675Suqs{
269241675Suqs	acpi_cstate_info *ci = (acpi_cstate_info *)malloc(sizeof(acpi_cstate_info));
270241675Suqs	if (!ci)
271241675Suqs		return B_NO_MEMORY;
272241675Suqs
273241675Suqs	if (object->object_type != ACPI_TYPE_PACKAGE) {
274241675Suqs		dprintf("invalid _CST object\n");
275241675Suqs		goto error;
276241675Suqs	}
277241675Suqs
278241675Suqs	if (object->data.package.count != 4) {
279241675Suqs		dprintf("invalid _CST number\n");
280241675Suqs		goto error;
281241675Suqs	}
282241675Suqs
283241675Suqs	// type
284241675Suqs	acpi_object_type * pointer = &object->data.package.objects[1];
285241675Suqs	if (pointer->object_type != ACPI_TYPE_INTEGER) {
286241675Suqs		dprintf("invalid _CST elem type\n");
287241675Suqs		goto error;
288241675Suqs	}
289241675Suqs	uint32 n = pointer->data.integer;
290241675Suqs	if (n < 1 || n > 3) {
291241675Suqs		dprintf("invalid _CST elem value\n");
292241675Suqs		goto error;
293241675Suqs	}
294241675Suqs	ci->type = n;
295241675Suqs	dprintf("C%" B_PRId32 "\n", n);
296241675Suqs	snprintf(cState->name, sizeof(cState->name), "C%" B_PRId32, n);
297241675Suqs
298241675Suqs	// Latency
299241675Suqs	pointer = &object->data.package.objects[2];
300241675Suqs	if (pointer->object_type != ACPI_TYPE_INTEGER) {
301241675Suqs		dprintf("invalid _CST elem type\n");
302241675Suqs		goto error;
303241675Suqs	}
304241675Suqs	n = pointer->data.integer;
305241675Suqs	cState->latency = n;
306241675Suqs	dprintf("Latency: %" B_PRId32 "\n", n);
307241675Suqs
308241675Suqs	// power
309241675Suqs	pointer = &object->data.package.objects[3];
310241675Suqs	if (pointer->object_type != ACPI_TYPE_INTEGER) {
311241675Suqs		dprintf("invalid _CST elem type\n");
312241675Suqs		goto error;
313241675Suqs	}
314241675Suqs	n = pointer->data.integer;
315241675Suqs	dprintf("power: %" B_PRId32 "\n", n);
316241675Suqs
317241675Suqs	// register
318241675Suqs	pointer = &object->data.package.objects[0];
319241675Suqs	if (pointer->object_type != ACPI_TYPE_BUFFER) {
320241675Suqs		dprintf("invalid _CST elem type\n");
321241675Suqs		goto error;
322241675Suqs	}
323241675Suqs	if (pointer->data.buffer.length < 15) {
324241675Suqs		dprintf("invalid _CST elem length\n");
325241675Suqs		goto error;
326241675Suqs	}
327241675Suqs
328241675Suqs	struct acpicpu_reg *reg = (struct acpicpu_reg *)pointer->data.buffer.buffer;
329241675Suqs	switch (reg->reg_spaceid) {
330241675Suqs		case ACPI_ADR_SPACE_SYSTEM_IO:
331241675Suqs			dprintf("IO method\n");
332241675Suqs			if (reg->reg_addr == 0) {
333241675Suqs				dprintf("illegal address\n");
334241675Suqs				goto error;
335241675Suqs			}
336241675Suqs			if (reg->reg_bitwidth != 8) {
337241675Suqs				dprintf("invalid source length\n");
338241675Suqs				goto error;
339241675Suqs			}
340241675Suqs			ci->address = reg->reg_addr;
341241675Suqs			ci->method = ACPI_CSTATE_SYSIO;
342241675Suqs			break;
343241675Suqs		case ACPI_ADR_SPACE_FIXED_HARDWARE:
344241675Suqs		{
345241675Suqs			dprintf("FFH method\n");
346241675Suqs			ci->method = ACPI_CSTATE_FFH;
347241675Suqs			ci->address = reg->reg_addr;
348241675Suqs
349241675Suqs			// skip checking BM_STS if ACPI_PDC_GAS_BM is cleared
350241675Suqs			cpu_ent *cpu = get_cpu_struct();
351241675Suqs			if ((cpu->arch.vendor == VENDOR_INTEL) &&
352241675Suqs				!(reg->reg_accesssize & ACPI_PDC_GAS_BM))
353241675Suqs				ci->skip_bm_sts = 1;
354241675Suqs			break;
355241675Suqs		}
356241675Suqs		default:
357241675Suqs			dprintf("invalid spaceid %" B_PRId8 "\n", reg->reg_spaceid);
358241675Suqs			break;
359241675Suqs	}
360241675Suqs	cState->pData = ci;
361241675Suqs	cState->EnterIdle = acpi_cstate_idle;
362241675Suqs
363241675Suqs	return B_OK;
364241675Suqserror:
365241675Suqs	free(ci);
366241675Suqs	return B_ERROR;
367241675Suqs}
368241675Suqs
369241675Suqs
370241675Suqsstatic void
371241675Suqsacpi_cstate_quirks(acpi_cpuidle_driver_info *device)
372241675Suqs{
373241675Suqs	cpu_ent *cpu = get_cpu_struct();
374241675Suqs	// Calculated Model Value: M = (Extended Model << 4) + Model
375241675Suqs	uint32 model = (cpu->arch.extended_model << 4) + cpu->arch.model;
376241675Suqs
377241675Suqs	// On all recent Intel platforms, ARB_DIS is not necessary
378241675Suqs	if (cpu->arch.vendor != VENDOR_INTEL)
379241675Suqs		return;
380241675Suqs	if (cpu->arch.family > 0xf || (cpu->arch.family == 6 && model >= 0xf))
381241675Suqs		device->flags &= ~ACPI_FLAG_C_ARB;
382241675Suqs}
383241675Suqs
384241675Suqs
385241675Suqsstatic status_t
386241675Suqsacpi_cpuidle_setup(acpi_cpuidle_driver_info *device)
387241675Suqs{
388241675Suqs	// _PDC is deprecated in the ACPI 3.0, we will try _OSC firstly
389241675Suqs	// and fall back to _PDC if _OSC fail
390241675Suqs	status_t status = acpi_eval_osc(device);
391241675Suqs	if (status != B_OK)
392241675Suqs		status = acpi_eval_pdc(device);
393241675Suqs	if (status != B_OK) {
394241675Suqs		dprintf("failed to eval _OSC and _PDC\n");
395241675Suqs		return status;
396241675Suqs	}
397241675Suqs
398241675Suqs	acpi_data buffer;
399241675Suqs	buffer.pointer = NULL;
400241675Suqs	buffer.length = ACPI_ALLOCATE_BUFFER;
401241675Suqs
402241675Suqs	dprintf("evaluate _CST @%p\n", device->acpi_cookie);
403241675Suqs	status = device->acpi->evaluate_method(device->acpi_cookie, "_CST", NULL,
404241675Suqs		&buffer);
405241675Suqs	if (status != B_OK) {
406241675Suqs		dprintf("failed to get _CST\n");
407241675Suqs		return B_IO_ERROR;
408241675Suqs	}
409241675Suqs
410241675Suqs	acpi_object_type *object = (acpi_object_type *)buffer.pointer;
411241675Suqs	if (object->object_type != ACPI_TYPE_PACKAGE)
412241675Suqs		dprintf("invalid _CST type\n");
413241675Suqs	if (object->data.package.count < 2)
414241675Suqs		dprintf("invalid _CST count\n");
415241675Suqs
416241675Suqs	acpi_object_type *pointer = object->data.package.objects;
417241675Suqs	if (pointer[0].object_type != ACPI_TYPE_INTEGER)
418241675Suqs		dprintf("invalid _CST type 2\n");
419241675Suqs	uint32 n = pointer[0].data.integer;
420241675Suqs	if (n != object->data.package.count - 1)
421241675Suqs		dprintf("invalid _CST count 2\n");
422241675Suqs	if (n > 8)
423241675Suqs		dprintf("_CST has too many states\n");
424241675Suqs	dprintf("cpuidle found %" B_PRId32 " cstates\n", n);
425241675Suqs	uint32 count = 1;
426241675Suqs	for (uint32 i = 1; i <= n; i++) {
427241675Suqs		pointer = &object->data.package.objects[i];
428241675Suqs		if (acpi_cstate_add(pointer, &sAcpiDevice.cStates[count]) == B_OK)
429241675Suqs			++count;
430241675Suqs	}
431241675Suqs	sAcpiDevice.cStateCount = count;
432241675Suqs	free(buffer.pointer);
433241675Suqs
434241675Suqs	// TODO we assume BM is a must and ARB_DIS is always available
435241675Suqs	device->flags |= ACPI_FLAG_C_ARB | ACPI_FLAG_C_BM;
436241675Suqs
437241675Suqs	acpi_cstate_quirks(device);
438241675Suqs
439241675Suqs	return B_OK;
440241675Suqs}
441241675Suqs
442241675Suqs
443241675Suqsstatic status_t
444241675Suqsacpi_cpuidle_init(void)
445241675Suqs{
446241675Suqs	dprintf("acpi_cpuidle_init\n");
447241675Suqs
448241675Suqs	for (int32 i = 0; i < smp_get_num_cpus(); i++)
449241675Suqs		if (acpi_cpuidle_setup(sAcpiProcessor[i]) != B_OK)
450241675Suqs			return B_ERROR;
451241675Suqs
452241675Suqs	status_t status = gIdle->AddDevice(&sAcpiDevice);
453241675Suqs	if (status == B_OK)
454241675Suqs		dprintf("using acpi idle\n");
455241675Suqs	return status;
456241675Suqs}
457241675Suqs
458241675Suqs
459241675Suqsstatic status_t
460241675Suqsacpi_processor_init(acpi_cpuidle_driver_info *device)
461241675Suqs{
462241675Suqs	// get the CPU index
463241675Suqs	dprintf("get acpi processor @%p\n", device->acpi_cookie);
464241675Suqs
465241675Suqs	acpi_data buffer;
466241675Suqs	buffer.pointer = NULL;
467241675Suqs	buffer.length = ACPI_ALLOCATE_BUFFER;
468241675Suqs	status_t status = device->acpi->evaluate_method(device->acpi_cookie, NULL,
469241675Suqs		NULL, &buffer);
470241675Suqs	if (status != B_OK) {
471241675Suqs		dprintf("failed to get processor obj\n");
472241675Suqs		return status;
473241675Suqs	}
474241675Suqs
475241675Suqs	acpi_object_type *object = (acpi_object_type *)buffer.pointer;
476241675Suqs	dprintf("acpi cpu%" B_PRId32 ": P_BLK at %#x/%lu\n",
477241675Suqs		object->data.processor.cpu_id,
478241675Suqs		object->data.processor.pblk_address,
479241675Suqs		object->data.processor.pblk_length);
480241675Suqs
481241675Suqs	int32 cpuIndex = object->data.processor.cpu_id;
482241675Suqs	free(buffer.pointer);
483241675Suqs
484241675Suqs	if (cpuIndex < 0 || cpuIndex >= smp_get_num_cpus())
485241675Suqs		return B_ERROR;
486241675Suqs
487241675Suqs	device->cpuIndex = cpuIndex;
488241675Suqs	sAcpiProcessor[cpuIndex] = device;
489241675Suqs
490241675Suqs	// If nodes for all processors have been registered, init the idle callback.
491241675Suqs	for (int32 i = smp_get_num_cpus() - 1; i >= 0; i--) {
492241675Suqs		if (sAcpiProcessor[i] == NULL)
493241675Suqs			return B_OK;
494241675Suqs	}
495241675Suqs
496241675Suqs	if (intel_cpuidle_init() == B_OK)
497241675Suqs		return B_OK;
498241675Suqs
499241675Suqs	status = acpi_cpuidle_init();
500241675Suqs	if (status != B_OK)
501241675Suqs		sAcpiProcessor[cpuIndex] = NULL;
502241675Suqs
503241675Suqs	return status;
504241675Suqs}
505241675Suqs
506241675Suqs
507241675Suqsstatic float
508241675Suqsacpi_cpuidle_support(device_node *parent)
509241675Suqs{
510241675Suqs	const char *bus;
511241675Suqs	uint32 device_type;
512241675Suqs
513241675Suqs	dprintf("acpi_cpuidle_support\n");
514241675Suqs	// make sure parent is really the ACPI bus manager
515241675Suqs	if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
516241675Suqs		return -1;
517241675Suqs
518241675Suqs	if (strcmp(bus, "acpi") != 0)
519241675Suqs		return 0.0;
520241675Suqs
521241675Suqs	// check whether it's really a cpu Device
522241675Suqs	if (sDeviceManager->get_attr_uint32(parent, ACPI_DEVICE_TYPE_ITEM,
523241675Suqs			&device_type, false) != B_OK
524241675Suqs		|| device_type != ACPI_TYPE_PROCESSOR) {
525241675Suqs		return 0.0;
526241675Suqs	}
527241675Suqs
528241675Suqs	return 0.6;
529241675Suqs}
530241675Suqs
531241675Suqs
532241675Suqsstatic status_t
533241675Suqsacpi_cpuidle_register_device(device_node *node)
534241675Suqs{
535241675Suqs	device_attr attrs[] = {
536241675Suqs		{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { .string = "ACPI CPU IDLE" }},
537241675Suqs		{ NULL }
538241675Suqs	};
539241675Suqs
540241675Suqs	dprintf("acpi_cpuidle_register_device\n");
541241675Suqs	return sDeviceManager->register_node(node, ACPI_CPUIDLE_MODULE_NAME, attrs,
542241675Suqs		NULL, NULL);
543241675Suqs}
544241675Suqs
545241675Suqs
546241675Suqsstatic status_t
547241675Suqsacpi_cpuidle_init_driver(device_node *node, void **driverCookie)
548241675Suqs{
549241675Suqs	dprintf("acpi_cpuidle_init_driver\n");
550241675Suqs	acpi_cpuidle_driver_info *device;
551241675Suqs	device = (acpi_cpuidle_driver_info *)calloc(1, sizeof(*device));
552241675Suqs	if (device == NULL)
553241675Suqs		return B_NO_MEMORY;
554241675Suqs
555241675Suqs	device->node = node;
556241675Suqs
557241675Suqs	device_node *parent;
558241675Suqs	parent = sDeviceManager->get_parent_node(node);
559241675Suqs	sDeviceManager->get_driver(parent, (driver_module_info **)&device->acpi,
560241675Suqs		(void **)&device->acpi_cookie);
561241675Suqs	sDeviceManager->put_node(parent);
562241675Suqs
563241675Suqs	status_t status = acpi_processor_init(device);
564241675Suqs	if (status != B_OK) {
565241675Suqs		free(device);
566241675Suqs		return status;
567241675Suqs	}
568241675Suqs
569241675Suqs	*driverCookie = device;
570241675Suqs	return B_OK;
571241675Suqs}
572241675Suqs
573241675Suqs
574241675Suqsstatic void
575241675Suqsacpi_cpuidle_uninit_driver(void *driverCookie)
576241675Suqs{
577241675Suqs	dprintf("acpi_cpuidle_uninit_driver");
578241675Suqs	acpi_cpuidle_driver_info *device = (acpi_cpuidle_driver_info *)driverCookie;
579241675Suqs	// TODO: When the first device to be unregistered, we'd need to balance the
580241675Suqs	// gIdle->AddDevice() call, but ATM isn't any API for that.
581241675Suqs	sAcpiProcessor[device->cpuIndex] = NULL;
582241675Suqs	free(device);
583241675Suqs}
584241675Suqs
585241675Suqs
586241675Suqsmodule_dependency module_dependencies[] = {
587241675Suqs	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager },
588241675Suqs	{ B_ACPI_MODULE_NAME, (module_info **)&sAcpi},
589241675Suqs	{ B_CPUIDLE_MODULE_NAME, (module_info **)&gIdle },
590241675Suqs	{}
591241675Suqs};
592241675Suqs
593241675Suqs
594241675Suqsstatic driver_module_info sAcpiidleModule = {
595241675Suqs	{
596241675Suqs		ACPI_CPUIDLE_MODULE_NAME,
597241675Suqs		0,
598241675Suqs		NULL
599241675Suqs	},
600241675Suqs
601241675Suqs	acpi_cpuidle_support,
602241675Suqs	acpi_cpuidle_register_device,
603241675Suqs	acpi_cpuidle_init_driver,
604241675Suqs	acpi_cpuidle_uninit_driver,
605241675Suqs	NULL,
606241675Suqs	NULL,	// rescan
607241675Suqs	NULL,	// removed
608241675Suqs};
609241675Suqs
610241675Suqs
611241675Suqsmodule_info *modules[] = {
612241675Suqs	(module_info *)&sAcpiidleModule,
613241675Suqs	NULL
614241675Suqs};
615241675Suqs