Add parsing for _CST Signed-off-by: Dominik Brodowski drivers/acpi/processor_idle.c | 159 +++++++++++++++++++++++++++++++++++++++--- include/acpi/processor.h | 15 +++ 2 files changed, 163 insertions(+), 11 deletions(-) diff -ruN linux-original/drivers/acpi/processor_idle.c linux/drivers/acpi/processor_idle.c --- linux-original/drivers/acpi/processor_idle.c 2004-11-27 20:41:10.889249904 +0100 +++ linux/drivers/acpi/processor_idle.c 2004-11-27 21:20:40.852960656 +0100 @@ -429,6 +429,8 @@ static int acpi_processor_get_power_info_fadt (struct acpi_processor *pr) { + int i; + ACPI_FUNCTION_TRACE("acpi_processor_get_power_info_fadt"); if (!pr) @@ -437,6 +439,9 @@ if (!pr->pblk) return_VALUE(-ENODEV); + for (i = 0; i < ACPI_PROCESSOR_MAX_POWER; i++) + memset(pr->power.states, 0, sizeof(struct acpi_processor_cx)); + /* if info is obtained from pblk/fadt, type equals state */ pr->power.states[ACPI_STATE_C1].type = ACPI_STATE_C1; pr->power.states[ACPI_STATE_C2].type = ACPI_STATE_C2; @@ -464,6 +469,129 @@ } +static int acpi_processor_get_power_info_cst (struct acpi_processor *pr) +{ + acpi_status status = 0; + acpi_integer count; + int i; + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + union acpi_object *cst; + + ACPI_FUNCTION_TRACE("acpi_processor_get_power_info_cst"); + + if (errata.smp) + return_VALUE(-ENODEV); + pr->power.count = 0; + for (i = 0; i < ACPI_PROCESSOR_MAX_POWER; i++) + memset(pr->power.states, 0, sizeof(struct acpi_processor_cx)); + + status = acpi_evaluate_object(pr->handle, "_CST", NULL, &buffer); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No _CST, giving up\n")); + return_VALUE(-ENODEV); + } + + cst = (union acpi_object *) buffer.pointer; + + /* There must be at least 2 elements */ + if (!cst || (cst->type != ACPI_TYPE_PACKAGE) || cst->package.count < 2) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "not enough elements in _CST\n")); + status = -EFAULT; + goto end; + } + + count = cst->package.elements[0].integer.value; + + /* Validate number of power states. */ + if (count < 1 || count != cst->package.count - 1) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "count given by _CST is not valid\n")); + status = -EFAULT; + goto end; + } + + /* We support up to ACPI_PROCESSOR_MAX_POWER. */ + if (count > ACPI_PROCESSOR_MAX_POWER) { + printk(KERN_WARNING "Limiting number of power states to max (%d)\n", ACPI_PROCESSOR_MAX_POWER); + printk(KERN_WARNING "Please increase ACPI_PROCESSOR_MAX_POWER if needed.\n"); + count = ACPI_PROCESSOR_MAX_POWER; + } + + /* Tell driver that at least _CST is supported. */ + pr->flags.has_cst = 1; + + for (i = 1; i <= count; i++) { + union acpi_object *element; + union acpi_object *obj; + struct acpi_power_register *reg; + struct acpi_processor_cx cx; + + memset(&cx, 0, sizeof(cx)); + + element = (union acpi_object *) &(cst->package.elements[i]); + if (element->type != ACPI_TYPE_PACKAGE) + continue; + + if (element->package.count != 4) + continue; + + obj = (union acpi_object *) &(element->package.elements[0]); + + if (obj->type != ACPI_TYPE_BUFFER) + continue; + + reg = (struct acpi_power_register *) obj->buffer.pointer; + + if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO && + (reg->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE)) + continue; + + cx.address = (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) ? + 0 : reg->address; + + /* There should be an easy way to extract an integer... */ + obj = (union acpi_object *) &(element->package.elements[1]); + if (obj->type != ACPI_TYPE_INTEGER) + continue; + + cx.type = obj->integer.value; + + if ((cx.type != ACPI_STATE_C1) && + (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO)) + continue; + + if ((cx.type < ACPI_STATE_C1) || + (cx.type > ACPI_STATE_C3)) + continue; + + obj = (union acpi_object *) &(element->package.elements[2]); + if (obj->type != ACPI_TYPE_INTEGER) + continue; + + cx.latency = obj->integer.value; + + obj = (union acpi_object *) &(element->package.elements[3]); + if (obj->type != ACPI_TYPE_INTEGER) + continue; + + cx.power = obj->integer.value; + + (pr->power.count)++; + memcpy(&(pr->power.states[pr->power.count]), &cx, sizeof(cx)); + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d power states\n", pr->power.count)); + + /* Validate number of power states discovered */ + if (pr->power.count < 2) + status = -ENODEV; + +end: + acpi_os_free(buffer.pointer); + + return_VALUE(status); +} + + static void acpi_processor_power_verify_c2(struct acpi_processor_cx *cx) { ACPI_FUNCTION_TRACE("acpi_processor_get_power_verify_c2"); @@ -576,10 +704,15 @@ static int acpi_processor_power_verify(struct acpi_processor *pr) { unsigned int i; + unsigned int working = 0; + for (i=1; i < ACPI_PROCESSOR_MAX_POWER; i++) { struct acpi_processor_cx *cx = &pr->power.states[i]; switch (cx->type) { + case ACPI_STATE_C1: + cx->valid = 1; + break; case ACPI_STATE_C2: acpi_processor_power_verify_c2(cx); @@ -589,9 +722,12 @@ acpi_processor_power_verify_c3(pr, cx); break; } + + if (cx->valid) + working++; } - return 0; + return (working); } int acpi_processor_get_power_info ( @@ -605,13 +741,15 @@ /* NOTE: the idle thread may not be running while calling * this function */ - for (i = 0; i < ACPI_PROCESSOR_MAX_POWER; i++) - memset(pr->power.states, 0, sizeof(struct acpi_processor_cx)); - - acpi_processor_get_power_info_fadt(pr); - - acpi_processor_power_verify(pr); + result = acpi_processor_get_power_info_cst(pr); + if ((result) || (acpi_processor_power_verify(pr) < 2)) { + result = acpi_processor_get_power_info_fadt(pr); + if (result) + return_VALUE(result); + if (acpi_processor_power_verify(pr) < 2) + return_VALUE(-ENODEV); + } /* * Set Default Policy @@ -629,8 +767,9 @@ * if one state of type C2 or C3 is available, mark this * CPU as being "idle manageable" */ - - for (i = 0; i < ACPI_PROCESSOR_MAX_POWER; i++) { + for (i = 1; i < ACPI_PROCESSOR_MAX_POWER; i++) { + if (pr->power.states[i].valid) + pr->power.count = i; if ((pr->power.states[i].valid) && (pr->power.states[i].type >= ACPI_STATE_C2)) pr->flags.power = 1; @@ -659,7 +798,7 @@ seq_puts(seq, "states:\n"); - for (i = 1; i < ACPI_C_STATE_COUNT; i++) { + for (i = 1; i <= pr->power.count; i++) { seq_printf(seq, " %c%d: ", (&pr->power.states[i] == pr->power.state?'*':' '), i); diff -ruN linux-original/include/acpi/processor.h linux/include/acpi/processor.h --- linux-original/include/acpi/processor.h 2004-11-27 20:41:10.890249752 +0100 +++ linux/include/acpi/processor.h 2004-11-27 21:01:45.845508016 +0100 @@ -6,7 +6,7 @@ #define ACPI_PROCESSOR_BUSY_METRIC 10 -#define ACPI_PROCESSOR_MAX_POWER ACPI_C_STATE_COUNT +#define ACPI_PROCESSOR_MAX_POWER 8 #define ACPI_PROCESSOR_MAX_C2_LATENCY 100 #define ACPI_PROCESSOR_MAX_C3_LATENCY 1000 @@ -18,6 +18,17 @@ struct acpi_processor_cx; +struct acpi_power_register { + u8 descriptor; + u16 length; + u8 space_id; + u8 bit_width; + u8 bit_offset; + u8 reserved; + u64 address; +} __attribute__ ((packed)); + + struct acpi_processor_cx_policy { u32 count; struct acpi_processor_cx *state; @@ -44,6 +55,7 @@ struct acpi_processor_power { struct acpi_processor_cx *state; u32 bm_activity; + int count; struct acpi_processor_cx states[ACPI_PROCESSOR_MAX_POWER]; }; @@ -121,6 +133,7 @@ u8 limit:1; u8 bm_control:1; u8 bm_check:1; + u8 has_cst:1; u8 reserved:2; };