From e0a896b88f2e517b10c67a4f85f3846a0312041d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 22 Sep 2020 12:44:56 -0600 Subject: acpi: Add support for writing a _PRW A 'Power Resource for Wake' list the resources a device depends on for wake. Add a function to generate this. Signed-off-by: Simon Glass --- test/dm/acpigen.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'test') diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index 1b2767e732d..1dc064ffbcf 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -1097,3 +1097,33 @@ static int dm_test_acpi_write_name(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_write_name, 0); + +/* Test emitting a _PRW component */ +static int dm_test_acpi_write_prw(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_write_prw(ctx, 5, 3); + ut_asserteq(NAME_OP, *ptr++); + + ut_asserteq_strn("_PRW", (char *)ptr); + ptr += 4; + ut_asserteq(PACKAGE_OP, *ptr++); + ut_asserteq(8, acpi_test_get_length(ptr)); + ptr += 3; + ut_asserteq(2, *ptr++); + ut_asserteq(BYTE_PREFIX, *ptr++); + ut_asserteq(5, *ptr++); + ut_asserteq(BYTE_PREFIX, *ptr++); + ut_asserteq(3, *ptr++); + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_write_prw, 0); -- cgit v1.3.1 From da7cff338f08cf43706a96bcd8cf398b7fb6ae2d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 22 Sep 2020 12:44:57 -0600 Subject: acpi: Add support for conditions and return values Add functions to support generating ACPI code for condition checks and return values. Signed-off-by: Simon Glass --- include/acpi/acpigen.h | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++ lib/acpi/acpigen.c | 68 ++++++++++++++++++++++++++++++++++++ test/dm/acpigen.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 252 insertions(+) (limited to 'test') diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index a9b70123c0a..fa9409e3528 100644 --- a/include/acpi/acpigen.h +++ b/include/acpi/acpigen.h @@ -52,12 +52,24 @@ enum { LOCAL5_OP = 0x65, LOCAL6_OP = 0x66, LOCAL7_OP = 0x67, + ARG0_OP = 0x68, + ARG1_OP = 0x69, + ARG2_OP = 0x6a, + ARG3_OP = 0x6b, + ARG4_OP = 0x6c, + ARG5_OP = 0x6d, + ARG6_OP = 0x6e, STORE_OP = 0x70, AND_OP = 0x7b, OR_OP = 0x7d, NOT_OP = 0x80, DEVICE_OP = 0x82, POWER_RES_OP = 0x84, + LEQUAL_OP = 0x93, + TO_BUFFER_OP = 0x96, + TO_INTEGER_OP = 0x99, + IF_OP = 0xa0, + ELSE_OP = 0xa1, RETURN_OP = 0xa4, }; @@ -573,4 +585,85 @@ int acpigen_set_enable_tx_gpio(struct acpi_ctx *ctx, u32 tx_state_val, */ void acpigen_write_prw(struct acpi_ctx *ctx, uint wake, uint level); +/** + * acpigen_write_if() - Write an If block + * + * This requires a call to acpigen_pop_len() to complete the block + * + * @ctx: ACPI context pointer + */ +void acpigen_write_if(struct acpi_ctx *ctx); + +/** + * acpigen_write_if_lequal_op_int() - Write comparison between op and integer + * + * Generates ACPI code for checking if operand1 and operand2 are equal + * + * If (Lequal (op, val)) + * + * @ctx: ACPI context pointer + * @op: Operand to check + * @val: Value to check against + */ +void acpigen_write_if_lequal_op_int(struct acpi_ctx *ctx, uint op, u64 val); + +/** + * acpigen_write_else() - Write an Ef block + * + * This requires a call to acpigen_pop_len() to complete the block + * + * @ctx: ACPI context pointer + */ +void acpigen_write_else(struct acpi_ctx *ctx); + +/** + * acpigen_write_to_buffer() - Write a ToBuffer operation + * + * E.g.: to generate: ToBuffer (Arg0, Local0) + * use acpigen_write_to_buffer(ctx, ARG0_OP, LOCAL0_OP) + * + * @ctx: ACPI context pointer + * @src: Source argument + * @dst: Destination argument + */ +void acpigen_write_to_buffer(struct acpi_ctx *ctx, uint src, uint dst); + +/** + * acpigen_write_to_integer() - Write a ToInteger operation + * + * E.g.: to generate: ToInteger (Arg0, Local0) + * use acpigen_write_to_integer(ctx, ARG0_OP, LOCAL0_OP) + * + * @ctx: ACPI context pointer + * @src: Source argument + * @dst: Destination argument + */ +void acpigen_write_to_integer(struct acpi_ctx *ctx, uint src, uint dst); + +/** + * acpigen_write_return_byte_buffer() - Write a return of a byte buffer + * + * @ctx: ACPI context pointer + * @arr: Array of bytes to return + * @size: Number of bytes + */ +void acpigen_write_return_byte_buffer(struct acpi_ctx *ctx, u8 *arr, + size_t size); + +/** + * acpigen_write_return_singleton_buffer() - Write a return of a 1-byte buffer + * + * @ctx: ACPI context pointer + * @arg: Byte to return + */ +void acpigen_write_return_singleton_buffer(struct acpi_ctx *ctx, uint arg); + +/** + * acpigen_write_return_byte() - Write a return of a byte + * + * @ctx: ACPI context pointer + * @arg: Byte to return + */ +void acpigen_write_return_byte(struct acpi_ctx *ctx, uint arg); + #endif diff --git a/lib/acpi/acpigen.c b/lib/acpi/acpigen.c index 527de89b1e1..2518bf83dda 100644 --- a/lib/acpi/acpigen.c +++ b/lib/acpi/acpigen.c @@ -541,6 +541,74 @@ void acpigen_write_debug_string(struct acpi_ctx *ctx, const char *str) acpigen_emit_ext_op(ctx, DEBUG_OP); } +void acpigen_write_if(struct acpi_ctx *ctx) +{ + acpigen_emit_byte(ctx, IF_OP); + acpigen_write_len_f(ctx); +} + +void acpigen_write_if_lequal_op_int(struct acpi_ctx *ctx, uint op, u64 val) +{ + acpigen_write_if(ctx); + acpigen_emit_byte(ctx, LEQUAL_OP); + acpigen_emit_byte(ctx, op); + acpigen_write_integer(ctx, val); +} + +void acpigen_write_else(struct acpi_ctx *ctx) +{ + acpigen_emit_byte(ctx, ELSE_OP); + acpigen_write_len_f(ctx); +} + +void acpigen_write_to_buffer(struct acpi_ctx *ctx, uint src, uint dst) +{ + acpigen_emit_byte(ctx, TO_BUFFER_OP); + acpigen_emit_byte(ctx, src); + acpigen_emit_byte(ctx, dst); +} + +void acpigen_write_to_integer(struct acpi_ctx *ctx, uint src, uint dst) +{ + acpigen_emit_byte(ctx, TO_INTEGER_OP); + acpigen_emit_byte(ctx, src); + acpigen_emit_byte(ctx, dst); +} + +void acpigen_write_byte_buffer(struct acpi_ctx *ctx, u8 *arr, size_t size) +{ + size_t i; + + acpigen_emit_byte(ctx, BUFFER_OP); + acpigen_write_len_f(ctx); + acpigen_write_integer(ctx, size); + + for (i = 0; i < size; i++) + acpigen_emit_byte(ctx, arr[i]); + + acpigen_pop_len(ctx); +} + +void acpigen_write_return_byte_buffer(struct acpi_ctx *ctx, u8 *arr, + size_t size) +{ + acpigen_emit_byte(ctx, RETURN_OP); + acpigen_write_byte_buffer(ctx, arr, size); +} + +void acpigen_write_return_singleton_buffer(struct acpi_ctx *ctx, uint arg) +{ + u8 buf = arg; + + acpigen_write_return_byte_buffer(ctx, &buf, 1); +} + +void acpigen_write_return_byte(struct acpi_ctx *ctx, uint arg) +{ + acpigen_emit_byte(ctx, RETURN_OP); + acpigen_write_byte(ctx, arg); +} + /** * acpigen_get_dw0_in_local5() - Generate code to put dw0 cfg0 in local5 * diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index 1dc064ffbcf..cce19f11209 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -1127,3 +1127,94 @@ static int dm_test_acpi_write_prw(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_write_prw, 0); + +/* Test emitting writing conditionals */ +static int dm_test_acpi_write_cond(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_write_if(ctx); + acpigen_pop_len(ctx); + ut_asserteq(IF_OP, *ptr++); + ut_asserteq(3, acpi_test_get_length(ptr)); + ptr += 3; + + acpigen_write_else(ctx); + acpigen_pop_len(ctx); + ut_asserteq(ELSE_OP, *ptr++); + ut_asserteq(3, acpi_test_get_length(ptr)); + ptr += 3; + + acpigen_write_if_lequal_op_int(ctx, LOCAL1_OP, 5); + acpigen_pop_len(ctx); + ut_asserteq(IF_OP, *ptr++); + ut_asserteq(7, acpi_test_get_length(ptr)); + ptr += 3; + ut_asserteq(LEQUAL_OP, *ptr++); + ut_asserteq(LOCAL1_OP, *ptr++); + ut_asserteq(BYTE_PREFIX, *ptr++); + ut_asserteq(5, *ptr++); + + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_write_cond, 0); + +/* Test emitting writing return values and ToBuffer/ToInteger */ +static int dm_test_acpi_write_return(struct unit_test_state *uts) +{ + int len = sizeof(TEST_STRING); + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_write_to_buffer(ctx, ARG0_OP, LOCAL0_OP); + ut_asserteq(TO_BUFFER_OP, *ptr++); + ut_asserteq(ARG0_OP, *ptr++); + ut_asserteq(LOCAL0_OP, *ptr++); + + acpigen_write_to_integer(ctx, ARG0_OP, LOCAL0_OP); + ut_asserteq(TO_INTEGER_OP, *ptr++); + ut_asserteq(ARG0_OP, *ptr++); + ut_asserteq(LOCAL0_OP, *ptr++); + + acpigen_write_return_byte_buffer(ctx, (u8 *)TEST_STRING, len); + ut_asserteq(RETURN_OP, *ptr++); + ut_asserteq(BUFFER_OP, *ptr++); + ut_asserteq(5 + len, acpi_test_get_length(ptr)); + ptr += 3; + ut_asserteq(BYTE_PREFIX, *ptr++); + ut_asserteq(len, *ptr++); + ut_asserteq_mem(TEST_STRING, ptr, len); + ptr += len; + + acpigen_write_return_singleton_buffer(ctx, 123); + len = 1; + ut_asserteq(RETURN_OP, *ptr++); + ut_asserteq(BUFFER_OP, *ptr++); + ut_asserteq(4 + len, acpi_test_get_length(ptr)); + ptr += 3; + ut_asserteq(ONE_OP, *ptr++); + ut_asserteq(123, *ptr++); + + acpigen_write_return_byte(ctx, 43); + ut_asserteq(RETURN_OP, *ptr++); + ut_asserteq(BYTE_PREFIX, *ptr++); + ut_asserteq(43, *ptr++); + + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_write_return, 0); -- cgit v1.3.1 From 88490e197903f6961cf2ee79d0ff8f0727155f27 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 22 Sep 2020 12:44:58 -0600 Subject: acpi: Support generating a multi-function _DSM for devices Add a function to generate ACPI code for a _DSM method for a device. This includes functions for starting and ending each part of the _DSM. Signed-off-by: Simon Glass [bmeng: fix the "new blank line at EOF" git warning] Signed-off-by: Bin Meng --- include/acpi/acpi_device.h | 14 +++++ include/acpi/acpigen.h | 99 +++++++++++++++++++++++++++++++++++ lib/acpi/acpi_device.c | 43 ++++++++++++++++ lib/acpi/acpigen.c | 54 ++++++++++++++++++++ test/dm/acpigen.c | 125 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 335 insertions(+) (limited to 'test') diff --git a/include/acpi/acpi_device.h b/include/acpi/acpi_device.h index 11461e168d3..a5b12217820 100644 --- a/include/acpi/acpi_device.h +++ b/include/acpi/acpi_device.h @@ -28,6 +28,9 @@ struct udevice; /* Length of a full path to an ACPI device */ #define ACPI_PATH_MAX 30 +/* UUID for an I2C _DSM method */ +#define ACPI_DSM_I2C_HID_UUID "3cdff6f7-4267-4555-ad05-b30a3d8938de" + /* Values that can be returned for ACPI device _STA method */ enum acpi_dev_status { ACPI_DSTATUS_PRESENT = BIT(0), @@ -319,6 +322,17 @@ int acpi_device_write_gpio_desc(struct acpi_ctx *ctx, int acpi_device_write_interrupt_or_gpio(struct acpi_ctx *ctx, struct udevice *dev, const char *prop); +/** + * acpi_device_write_dsm_i2c_hid() - Write a device-specific method for HID + * + * This writes a DSM for an I2C Human-Interface Device based on the config + * provided + * + * @hid_desc_reg_offset: HID register offset + */ +int acpi_device_write_dsm_i2c_hid(struct acpi_ctx *ctx, + int hid_desc_reg_offset); + /** * acpi_device_write_i2c_dev() - Write an I2C device to ACPI * diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index fa9409e3528..34b3115bc9c 100644 --- a/include/acpi/acpigen.h +++ b/include/acpi/acpigen.h @@ -666,4 +666,103 @@ void acpigen_write_return_singleton_buffer(struct acpi_ctx *ctx, uint arg); */ void acpigen_write_return_byte(struct acpi_ctx *ctx, uint arg); +/** + * acpigen_write_dsm_start() - Start a _DSM method + * + * Generate ACPI AML code to start the _DSM method. + * + * The functions need to be called in the correct sequence as below. + * + * Within the region, Local0 and Local1 must be are left + * untouched, but Local2-Local7 can be used + * + * Arguments passed into _DSM method: + * Arg0 = UUID + * Arg1 = Revision + * Arg2 = Function index + * Arg3 = Function-specific arguments + * + * AML code generated looks like this: + * Method (_DSM, 4, Serialized) { -- acpigen_write_dsm_start) + * ToBuffer (Arg0, Local0) + * If (LEqual (Local0, ToUUID(uuid))) { -- acpigen_write_dsm_uuid_start + * ToInteger (Arg2, Local1) + * If (LEqual (Local1, 0)) { -- acpigen_write_dsm_uuid_start_cond + * + * } -- acpigen_write_dsm_uuid_end_cond + * ... + * If (LEqual (Local1, n)) { -- acpigen_write_dsm_uuid_start_cond + * + * } -- acpigen_write_dsm_uuid_end_cond + * Return (Buffer (One) { 0x0 }) + * } -- acpigen_write_dsm_uuid_end + * ... + * If (LEqual (Local0, ToUUID(uuidn))) { + * ... + * } + * Return (Buffer (One) { 0x0 }) -- acpigen_write_dsm_end + * } + * + * @ctx: ACPI context pointer + */ +void acpigen_write_dsm_start(struct acpi_ctx *ctx); + +/** + * acpigen_write_dsm_uuid_start() - Start a new UUID block + * + * This starts generation of code to handle a particular UUID: + * + * If (LEqual (Local0, ToUUID(uuid))) { + * ToInteger (Arg2, Local1) + * + * @ctx: ACPI context pointer + */ +int acpigen_write_dsm_uuid_start(struct acpi_ctx *ctx, const char *uuid); + +/** + * acpigen_write_dsm_uuid_start_cond() - Start a new condition block + * + * This starts generation of condition-checking code to handle a particular + * function: + * + * If (LEqual (Local1, i)) + * + * @ctx: ACPI context pointer + */ +void acpigen_write_dsm_uuid_start_cond(struct acpi_ctx *ctx, int seq); + +/** + * acpigen_write_dsm_uuid_end_cond() - Start a new condition block + * + * This ends generation of condition-checking code to handle a particular + * function: + * + * } + * + * @ctx: ACPI context pointer + */ +void acpigen_write_dsm_uuid_end_cond(struct acpi_ctx *ctx); + +/** + * acpigen_write_dsm_uuid_end() - End a UUID block + * + * This ends generation of code to handle a particular UUID: + * + * Return (Buffer (One) { 0x0 }) + * + * @ctx: ACPI context pointer + */ +void acpigen_write_dsm_uuid_end(struct acpi_ctx *ctx); + +/** + * acpigen_write_dsm_end() - End a _DSM method + * + * This ends generates of the _DSM block: + * + * Return (Buffer (One) { 0x0 }) + * + * @ctx: ACPI context pointer + */ +void acpigen_write_dsm_end(struct acpi_ctx *ctx); + #endif diff --git a/lib/acpi/acpi_device.c b/lib/acpi/acpi_device.c index 3c75b6d9629..8248664a10a 100644 --- a/lib/acpi/acpi_device.c +++ b/lib/acpi/acpi_device.c @@ -487,6 +487,49 @@ int acpi_device_add_power_res(struct acpi_ctx *ctx, u32 tx_state_val, return 0; } +int acpi_device_write_dsm_i2c_hid(struct acpi_ctx *ctx, + int hid_desc_reg_offset) +{ + int ret; + + acpigen_write_dsm_start(ctx); + ret = acpigen_write_dsm_uuid_start(ctx, ACPI_DSM_I2C_HID_UUID); + if (ret) + return log_ret(ret); + + acpigen_write_dsm_uuid_start_cond(ctx, 0); + /* ToInteger (Arg1, Local2) */ + acpigen_write_to_integer(ctx, ARG1_OP, LOCAL2_OP); + /* If (LEqual (Local2, 0x0)) */ + acpigen_write_if_lequal_op_int(ctx, LOCAL2_OP, 0x0); + /* Return (Buffer (One) { 0x1f }) */ + acpigen_write_return_singleton_buffer(ctx, 0x1f); + acpigen_pop_len(ctx); /* Pop : If */ + /* Else */ + acpigen_write_else(ctx); + /* If (LEqual (Local2, 0x1)) */ + acpigen_write_if_lequal_op_int(ctx, LOCAL2_OP, 0x1); + /* Return (Buffer (One) { 0x3f }) */ + acpigen_write_return_singleton_buffer(ctx, 0x3f); + acpigen_pop_len(ctx); /* Pop : If */ + /* Else */ + acpigen_write_else(ctx); + /* Return (Buffer (One) { 0x0 }) */ + acpigen_write_return_singleton_buffer(ctx, 0x0); + acpigen_pop_len(ctx); /* Pop : Else */ + acpigen_pop_len(ctx); /* Pop : Else */ + acpigen_write_dsm_uuid_end_cond(ctx); + + acpigen_write_dsm_uuid_start_cond(ctx, 1); + acpigen_write_return_byte(ctx, hid_desc_reg_offset); + acpigen_write_dsm_uuid_end_cond(ctx); + + acpigen_write_dsm_uuid_end(ctx); + acpigen_write_dsm_end(ctx); + + return 0; +} + /* ACPI 6.3 section 6.4.3.8.2.1 - I2cSerialBus() */ static void acpi_device_write_i2c(struct acpi_ctx *ctx, const struct acpi_i2c *i2c) diff --git a/lib/acpi/acpigen.c b/lib/acpi/acpigen.c index 2518bf83dda..d859f378413 100644 --- a/lib/acpi/acpigen.c +++ b/lib/acpi/acpigen.c @@ -609,6 +609,60 @@ void acpigen_write_return_byte(struct acpi_ctx *ctx, uint arg) acpigen_write_byte(ctx, arg); } +void acpigen_write_dsm_start(struct acpi_ctx *ctx) +{ + /* Method (_DSM, 4, Serialized) */ + acpigen_write_method_serialized(ctx, "_DSM", 4); + + /* ToBuffer (Arg0, Local0) */ + acpigen_write_to_buffer(ctx, ARG0_OP, LOCAL0_OP); +} + +int acpigen_write_dsm_uuid_start(struct acpi_ctx *ctx, const char *uuid) +{ + int ret; + + /* If (LEqual (Local0, ToUUID(uuid))) */ + acpigen_write_if(ctx); + acpigen_emit_byte(ctx, LEQUAL_OP); + acpigen_emit_byte(ctx, LOCAL0_OP); + ret = acpigen_write_uuid(ctx, uuid); + if (ret) + return log_msg_ret("uuid", ret); + + /* ToInteger (Arg2, Local1) */ + acpigen_write_to_integer(ctx, ARG2_OP, LOCAL1_OP); + + return 0; +} + +void acpigen_write_dsm_uuid_start_cond(struct acpi_ctx *ctx, int seq) +{ + /* If (LEqual (Local1, i)) */ + acpigen_write_if_lequal_op_int(ctx, LOCAL1_OP, seq); +} + +void acpigen_write_dsm_uuid_end_cond(struct acpi_ctx *ctx) +{ + acpigen_pop_len(ctx); /* If */ +} + +void acpigen_write_dsm_uuid_end(struct acpi_ctx *ctx) +{ + /* Default case: Return (Buffer (One) { 0x0 }) */ + acpigen_write_return_singleton_buffer(ctx, 0x0); + + acpigen_pop_len(ctx); /* If (LEqual (Local0, ToUUID(uuid))) */ +} + +void acpigen_write_dsm_end(struct acpi_ctx *ctx) +{ + /* Return (Buffer (One) { 0x0 }) */ + acpigen_write_return_singleton_buffer(ctx, 0x0); + + acpigen_pop_len(ctx); /* Method _DSM */ +} + /** * acpigen_get_dw0_in_local5() - Generate code to put dw0 cfg0 in local5 * diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index cce19f11209..62d48dc419c 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -1218,3 +1219,127 @@ static int dm_test_acpi_write_return(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_write_return, 0); + +/* Test emitting a DSM for an I2C HID */ +static int dm_test_acpi_write_i2c_dsm(struct unit_test_state *uts) +{ + char uuid_str[UUID_STR_LEN + 1]; + const int reg_offset = 0x20; + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + ut_assertok(acpi_device_write_dsm_i2c_hid(ctx, reg_offset)); + + /* acpigen_write_dsm_start() */ + ut_asserteq(METHOD_OP, *ptr++); + ut_asserteq(0x78, acpi_test_get_length(ptr)); + ptr += 3; + ut_asserteq_strn("_DSM", (char *)ptr); + ptr += 4; + ut_asserteq(ACPI_METHOD_SERIALIZED_MASK | 4, *ptr++); + + ut_asserteq(TO_BUFFER_OP, *ptr++); + ut_asserteq(ARG0_OP, *ptr++); + ut_asserteq(LOCAL0_OP, *ptr++); + + /* acpigen_write_dsm_uuid_start() */ + ut_asserteq(IF_OP, *ptr++); + ut_asserteq(0x65, acpi_test_get_length(ptr)); + ptr += 3; + ut_asserteq(LEQUAL_OP, *ptr++); + ut_asserteq(LOCAL0_OP, *ptr++); + + ut_asserteq(BUFFER_OP, *ptr++); + ut_asserteq(UUID_BIN_LEN + 6, acpi_test_get_length(ptr)); + ptr += 3; + ut_asserteq(WORD_PREFIX, *ptr++); + ut_asserteq(UUID_BIN_LEN, get_unaligned((u16 *)ptr)); + ptr += 2; + uuid_bin_to_str(ptr, uuid_str, UUID_STR_FORMAT_GUID); + ut_asserteq_str(ACPI_DSM_I2C_HID_UUID, uuid_str); + ptr += UUID_BIN_LEN; + + ut_asserteq(TO_INTEGER_OP, *ptr++); + ut_asserteq(ARG2_OP, *ptr++); + ut_asserteq(LOCAL1_OP, *ptr++); + + /* acpigen_write_dsm_uuid_start_cond() */ + ut_asserteq(IF_OP, *ptr++); + ut_asserteq(0x34, acpi_test_get_length(ptr)); + ptr += 3; + ut_asserteq(LEQUAL_OP, *ptr++); + ut_asserteq(LOCAL1_OP, *ptr++); + ut_asserteq(ZERO_OP, *ptr++); + + /* + * See code from acpi_device_write_dsm_i2c_hid(). We don't check every + * piece + */ + ut_asserteq(TO_INTEGER_OP, *ptr++); + ut_asserteq(ARG1_OP, *ptr++); + ut_asserteq(LOCAL2_OP, *ptr++); + + ut_asserteq(IF_OP, *ptr++); + ut_asserteq(0xd, acpi_test_get_length(ptr)); + ptr += 3; + ut_asserteq(LEQUAL_OP, *ptr++); + ut_asserteq(LOCAL2_OP, *ptr++); + ut_asserteq(ZERO_OP, *ptr++); /* function 0 */ + + ut_asserteq(RETURN_OP, *ptr++); + ut_asserteq(BUFFER_OP, *ptr++); + ptr += 5; + + ut_asserteq(ELSE_OP, *ptr++); + ptr += 3; + + ut_asserteq(IF_OP, *ptr++); + ut_asserteq(0xd, acpi_test_get_length(ptr)); + ptr += 3; + ut_asserteq(LEQUAL_OP, *ptr++); + ut_asserteq(LOCAL2_OP, *ptr++); + ut_asserteq(ONE_OP, *ptr++); + + ut_asserteq(RETURN_OP, *ptr++); + ut_asserteq(BUFFER_OP, *ptr++); + ptr += 5; + + ut_asserteq(ELSE_OP, *ptr++); + ptr += 3; + + ut_asserteq(RETURN_OP, *ptr++); + ut_asserteq(BUFFER_OP, *ptr++); + ptr += 5; + + /* acpigen_write_dsm_uuid_start_cond() */ + ut_asserteq(IF_OP, *ptr++); + ut_asserteq(9, acpi_test_get_length(ptr)); + ptr += 3; + ut_asserteq(LEQUAL_OP, *ptr++); + ut_asserteq(LOCAL1_OP, *ptr++); + ut_asserteq(ONE_OP, *ptr++); /* function 1 */ + + ut_asserteq(RETURN_OP, *ptr++); + ut_asserteq(BYTE_PREFIX, *ptr++); + ut_asserteq(reg_offset, *ptr++); + + /* acpigen_write_dsm_uuid_end() */ + ut_asserteq(RETURN_OP, *ptr++); + ut_asserteq(BUFFER_OP, *ptr++); + ptr += 5; + + /* acpigen_write_dsm_end */ + ut_asserteq(RETURN_OP, *ptr++); + ut_asserteq(BUFFER_OP, *ptr++); + ptr += 5; + + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_write_i2c_dsm, 0); -- cgit v1.3.1 From 23dd0ea4c7edbde5bc05a1567ba0bf41734ea524 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 22 Sep 2020 12:44:59 -0600 Subject: dm: acpi: Use correct GPIO polarity type in acpi_dp_add_gpio() This function currently accepts the IRQ-polarity type. Fix it to use the GPIO type instead. Signed-off-by: Simon Glass --- drivers/sound/max98357a.c | 2 +- include/acpi/acpi_dp.h | 2 +- lib/acpi/acpi_dp.c | 4 ++-- test/dm/acpi_dp.c | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'test') diff --git a/drivers/sound/max98357a.c b/drivers/sound/max98357a.c index 841bc6ef682..827262d235c 100644 --- a/drivers/sound/max98357a.c +++ b/drivers/sound/max98357a.c @@ -81,7 +81,7 @@ static int max98357a_acpi_fill_ssdt(const struct udevice *dev, dp = acpi_dp_new_table("_DSD"); acpi_dp_add_gpio(dp, "sdmode-gpio", path, 0, 0, priv->sdmode_gpio.flags & GPIOD_ACTIVE_LOW ? - ACPI_IRQ_ACTIVE_LOW : ACPI_IRQ_ACTIVE_HIGH); + ACPI_GPIO_ACTIVE_LOW : ACPI_GPIO_ACTIVE_HIGH); acpi_dp_add_integer(dp, "sdmode-delay", dev_read_u32_default(dev, "sdmode-delay", 0)); acpi_dp_write(ctx, dp); diff --git a/include/acpi/acpi_dp.h b/include/acpi/acpi_dp.h index 0b514bce59c..5e539b1d218 100644 --- a/include/acpi/acpi_dp.h +++ b/include/acpi/acpi_dp.h @@ -221,7 +221,7 @@ struct acpi_dp *acpi_dp_add_child(struct acpi_dp *dp, const char *name, */ struct acpi_dp *acpi_dp_add_gpio(struct acpi_dp *dp, const char *name, const char *ref, int index, int pin, - enum acpi_irq_polarity polarity); + enum acpi_gpio_polarity polarity); /** * acpi_dp_write() - Write Device Property hierarchy and clean up resources diff --git a/lib/acpi/acpi_dp.c b/lib/acpi/acpi_dp.c index 579cab47715..7e3e3259d8d 100644 --- a/lib/acpi/acpi_dp.c +++ b/lib/acpi/acpi_dp.c @@ -324,7 +324,7 @@ struct acpi_dp *acpi_dp_add_integer_array(struct acpi_dp *dp, const char *name, struct acpi_dp *acpi_dp_add_gpio(struct acpi_dp *dp, const char *name, const char *ref, int index, int pin, - enum acpi_irq_polarity polarity) + enum acpi_gpio_polarity polarity) { struct acpi_dp *gpio; @@ -336,7 +336,7 @@ struct acpi_dp *acpi_dp_add_gpio(struct acpi_dp *dp, const char *name, if (!acpi_dp_add_reference(gpio, NULL, ref) || !acpi_dp_add_integer(gpio, NULL, index) || !acpi_dp_add_integer(gpio, NULL, pin) || - !acpi_dp_add_integer(gpio, NULL, polarity == ACPI_IRQ_ACTIVE_LOW)) + !acpi_dp_add_integer(gpio, NULL, polarity == ACPI_GPIO_ACTIVE_LOW)) return NULL; if (!acpi_dp_add_array(dp, gpio)) diff --git a/test/dm/acpi_dp.c b/test/dm/acpi_dp.c index e0fa61263c8..44bcabda6bc 100644 --- a/test/dm/acpi_dp.c +++ b/test/dm/acpi_dp.c @@ -398,9 +398,9 @@ static int dm_test_acpi_dp_gpio(struct unit_test_state *uts) /* Try a few different parameters */ ut_assertnonnull(acpi_dp_add_gpio(dp, "reset", TEST_REF, 0x23, 0x24, - ACPI_IRQ_ACTIVE_HIGH)); + ACPI_GPIO_ACTIVE_HIGH)); ut_assertnonnull(acpi_dp_add_gpio(dp, "allow", TEST_REF, 0, 0, - ACPI_IRQ_ACTIVE_LOW)); + ACPI_GPIO_ACTIVE_LOW)); ptr = acpigen_get_current(ctx); ut_assertok(acpi_dp_write(ctx, dp)); -- cgit v1.3.1 From 15403289e5ddb0d777f95f5836add1316e2d9ae2 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 22 Sep 2020 12:45:11 -0600 Subject: acpi: Add support for generating processor tables ACPI has a number of CPU-related tables. Add utility functions to write out the basic packages. Signed-off-by: Simon Glass --- include/acpi/acpigen.h | 39 ++++++++++++++++++ lib/acpi/acpigen.c | 55 +++++++++++++++++++++++++ test/dm/acpigen.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 201 insertions(+) (limited to 'test') diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index c412898169e..3a2c6339d5e 100644 --- a/include/acpi/acpigen.h +++ b/include/acpi/acpigen.h @@ -64,7 +64,9 @@ enum { OR_OP = 0x7d, NOT_OP = 0x80, DEVICE_OP = 0x82, + PROCESSOR_OP = 0x83, POWER_RES_OP = 0x84, + NOTIFY_OP = 0x86, LEQUAL_OP = 0x93, TO_BUFFER_OP = 0x96, TO_INTEGER_OP = 0x99, @@ -777,4 +779,41 @@ void acpigen_write_dsm_uuid_end(struct acpi_ctx *ctx); */ void acpigen_write_dsm_end(struct acpi_ctx *ctx); +/** + * acpigen_write_processor() - Write a Processor package + * + * This emits a Processor package header with the required information. The + * caller must complete the information and call acpigen_pop_len() at the end + * + * @ctx: ACPI context pointer + * @cpuindex: CPU number + * @pblock_addr: PBlk system IO address + * @pblock_len: PBlk length + */ +void acpigen_write_processor(struct acpi_ctx *ctx, uint cpuindex, + u32 pblock_addr, uint pblock_len); + +/** + * acpigen_write_processor_package() - Write a package containing the processors + * + * The package containins the name of each processor in the SoC + * + * @ctx: ACPI context pointer + * @name: Package name (.e.g "PPKG") + * @first_core: Number of the first core (e.g. 0) + * @core_count: Number of cores (e.g. 4) + */ +void acpigen_write_processor_package(struct acpi_ctx *ctx, const char *name, + uint first_core, uint core_count); + +/** + * acpigen_write_processor_cnot() - Write a processor notification method + * + * This writes a method that notifies all CPU cores + * + * @ctx: ACPI context pointer + * @num_cores: Number of CPU cores + */ +void acpigen_write_processor_cnot(struct acpi_ctx *ctx, const uint num_cores); + #endif diff --git a/lib/acpi/acpigen.c b/lib/acpi/acpigen.c index d859f378413..b9985075cde 100644 --- a/lib/acpi/acpigen.c +++ b/lib/acpi/acpigen.c @@ -17,6 +17,9 @@ #include #include +/* CPU path format */ +#define ACPI_CPU_STRING "\\_PR.CP%02d" + u8 *acpigen_get_current(struct acpi_ctx *ctx) { return ctx->current; @@ -340,6 +343,58 @@ void acpigen_write_method_serialized(struct acpi_ctx *ctx, const char *name, ACPI_METHOD_SERIALIZED_MASK); } +void acpigen_write_processor(struct acpi_ctx *ctx, uint cpuindex, + u32 pblock_addr, uint pblock_len) +{ + /* + * Processor (\_PR.CPnn, cpuindex, pblock_addr, pblock_len) + * { + */ + char pscope[16]; + + acpigen_emit_ext_op(ctx, PROCESSOR_OP); + acpigen_write_len_f(ctx); + + snprintf(pscope, sizeof(pscope), ACPI_CPU_STRING, cpuindex); + acpigen_emit_namestring(ctx, pscope); + acpigen_emit_byte(ctx, cpuindex); + acpigen_emit_dword(ctx, pblock_addr); + acpigen_emit_byte(ctx, pblock_len); +} + +void acpigen_write_processor_package(struct acpi_ctx *ctx, + const char *const name, + const uint first_core, + const uint core_count) +{ + uint i; + char pscope[16]; + + acpigen_write_name(ctx, name); + acpigen_write_package(ctx, core_count); + for (i = first_core; i < first_core + core_count; ++i) { + snprintf(pscope, sizeof(pscope), ACPI_CPU_STRING, i); + acpigen_emit_namestring(ctx, pscope); + } + acpigen_pop_len(ctx); +} + +void acpigen_write_processor_cnot(struct acpi_ctx *ctx, const uint num_cores) +{ + int core_id; + + acpigen_write_method(ctx, "\\_PR.CNOT", 1); + for (core_id = 0; core_id < num_cores; core_id++) { + char buffer[30]; + + snprintf(buffer, sizeof(buffer), ACPI_CPU_STRING, core_id); + acpigen_emit_byte(ctx, NOTIFY_OP); + acpigen_emit_namestring(ctx, buffer); + acpigen_emit_byte(ctx, ARG0_OP); + } + acpigen_pop_len(ctx); +} + void acpigen_write_device(struct acpi_ctx *ctx, const char *name) { acpigen_emit_ext_op(ctx, DEVICE_OP); diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index 62d48dc419c..74b7e23aab3 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -1343,3 +1343,110 @@ static int dm_test_acpi_write_i2c_dsm(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_write_i2c_dsm, 0); + +/* Test emitting a processor */ +static int dm_test_acpi_write_processor(struct unit_test_state *uts) +{ + const int cpuindex = 6; + const u32 pblock_addr = 0x12345600; + const u32 pblock_len = 0x60; + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_write_processor(ctx, cpuindex, pblock_addr, pblock_len); + acpigen_pop_len(ctx); + + ut_asserteq(EXT_OP_PREFIX, *ptr++); + ut_asserteq(PROCESSOR_OP, *ptr++); + ut_asserteq(0x13, acpi_test_get_length(ptr)); + ptr += 3; + ut_asserteq_strn("\\._PR_CP06", (char *)ptr); + ptr += 10; + ut_asserteq(cpuindex, *ptr++); + ut_asserteq(pblock_addr, get_unaligned((u32 *)ptr)); + ptr += 4; + ut_asserteq(pblock_len, *ptr++); + + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_write_processor, 0); + +/* Test emitting a processor package */ +static int dm_test_acpi_write_processor_package(struct unit_test_state *uts) +{ + const int core_count = 3; + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_write_processor_package(ctx, "XCPU", 0, core_count); + + ut_asserteq(NAME_OP, *ptr++); + ut_asserteq_strn("XCPU", (char *)ptr); + ptr += 4; + ut_asserteq(PACKAGE_OP, *ptr++); + ptr += 3; /* skip length */ + ut_asserteq(core_count, *ptr++); + + ut_asserteq_strn("\\._PR_CP00", (char *)ptr); + ptr += 10; + ut_asserteq_strn("\\._PR_CP01", (char *)ptr); + ptr += 10; + ut_asserteq_strn("\\._PR_CP02", (char *)ptr); + ptr += 10; + + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_write_processor_package, 0); + +/* Test emitting a processor notification package */ +static int dm_test_acpi_write_processor_cnot(struct unit_test_state *uts) +{ + const int core_count = 3; + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_write_processor_cnot(ctx, core_count); + + ut_asserteq(METHOD_OP, *ptr++); + ptr += 3; /* skip length */ + ut_asserteq_strn("\\._PR_CNOT", (char *)ptr); + ptr += 10; + ut_asserteq(1, *ptr++); + + ut_asserteq(NOTIFY_OP, *ptr++); + ut_asserteq_strn("\\._PR_CP00", (char *)ptr); + ptr += 10; + ut_asserteq(ARG0_OP, *ptr++); + ut_asserteq(NOTIFY_OP, *ptr++); + ut_asserteq_strn("\\._PR_CP01", (char *)ptr); + ptr += 10; + ut_asserteq(ARG0_OP, *ptr++); + ut_asserteq(NOTIFY_OP, *ptr++); + ut_asserteq_strn("\\._PR_CP02", (char *)ptr); + ptr += 10; + ut_asserteq(ARG0_OP, *ptr++); + + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_write_processor_cnot, 0); -- cgit v1.3.1 From 350c7f52b93c5612dd3d53966bd54cb68b94d80b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 22 Sep 2020 12:45:13 -0600 Subject: acpi: Add more support for generating processor tables This adds tables relating to P-States and C-States. Signed-off-by: Simon Glass --- include/acpi/acpigen.h | 162 +++++++++++++++++++++++++++ lib/acpi/acpigen.c | 167 ++++++++++++++++++++++++++++ test/dm/acpigen.c | 294 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 623 insertions(+) (limited to 'test') diff --git a/include/acpi/acpigen.h b/include/acpi/acpigen.h index 3a2c6339d5e..976f4dbb9af 100644 --- a/include/acpi/acpigen.h +++ b/include/acpi/acpigen.h @@ -10,8 +10,10 @@ #ifndef __ACPI_ACPIGEN_H #define __ACPI_ACPIGEN_H +#include #include +struct acpi_cstate; struct acpi_ctx; struct acpi_gen_regaddr; struct acpi_gpio; @@ -87,6 +89,53 @@ enum psd_coord { HW_ALL = 0xfe }; +/** + * enum csd_coord - Coordination types for C-states + * + * The type of coordination that exists (hardware) or is required (software) as + * a result of the underlying hardware dependency + */ +enum csd_coord { + CSD_HW_ALL = 0xfe, +}; + +/** + * struct acpi_cstate - Information about a C-State + * + * @ctype: C State type (1=C1, 2=C2, 3=C3) + * @latency: Worst-case latency to enter and exit the C State (in uS) + * @power: Average power consumption of the processor when in this C-State (mW) + * @resource: Register to read to place the processor in this state + */ +struct acpi_cstate { + uint ctype; + uint latency; + uint power; + struct acpi_gen_regaddr resource; +}; + +/** + * struct acpi_tstate - Information about a Throttling Supported State + * + * See ACPI v6.3 section 8.4.5.2: _TSS (Throttling Supported States) + * + * @percent: Percent of the core CPU operating frequency that will be + * available when this throttling state is invoked + * @power: Throttling state’s maximum power dissipation (mw) + * @latency: Worst-case latency (uS) that the CPU is unavailable during a + * transition from any throttling state to this throttling state + * @control: Value to be written to the Processor Control Register + * (THROTTLE_CTRL) to initiate a transition to this throttling state + * @status: Value in THROTTLE_STATUS when in this state + */ +struct acpi_tstate { + uint percent; + uint power; + uint latency; + uint control; + uint status; +}; + /** * acpigen_get_current() - Get the current ACPI code output pointer * @@ -816,4 +865,117 @@ void acpigen_write_processor_package(struct acpi_ctx *ctx, const char *name, */ void acpigen_write_processor_cnot(struct acpi_ctx *ctx, const uint num_cores); +/** + * acpigen_write_ppc() - generates a function returning max P-states + * + * @ctx: ACPI context pointer + * @num_pstates: Number of pstates to return + */ +void acpigen_write_ppc(struct acpi_ctx *ctx, uint num_pstates); + +/** + * acpigen_write_ppc() - generates a function returning PPCM + * + * This returns the maximum number of supported P-states, as saved in the + * variable PPCM + * + * @ctx: ACPI context pointer + */ +void acpigen_write_ppc_nvs(struct acpi_ctx *ctx); + +/** + * acpigen_write_tpc() - Write a _TPC method that returns the TPC limit + * + * @ctx: ACPI context pointer + * @gnvs_tpc_limit: Variable that holds the TPC limit + */ +void acpigen_write_tpc(struct acpi_ctx *ctx, const char *gnvs_tpc_limit); + +/** + * acpigen_write_pss_package() - Write a PSS package + * + * See ACPI v6.3 section 8.4.6: Processor Performance Control + * + * @ctx: ACPI context pointer + * @corefreq: CPU core frequency in MHz + * @translat: worst-case latency in uS that the CPU is unavailable during a + * transition from any performance state to this performance state + * @busmlat: worst-case latency in microseconds that Bus Masters are prevented + * from accessing memory during a transition from any performance state to + * this performance state + * @control: Value to write to PERF_CTRL to move to this performance state + * @status: Expected PERF_STATUS value when in this state + */ +void acpigen_write_pss_package(struct acpi_ctx *ctx, uint corefreq, uint power, + uint translat, uint busmlat, uint control, + uint status); + +/** + * acpigen_write_psd_package() - Write a PSD package + * + * Writes a P-State dependency package + * + * See ACPI v6.3 section 8.4.6.5: _PSD (P-State Dependency) + * + * @ctx: ACPI context pointer + * @domain: Dependency domain number to which this P state entry belongs + * @numprocs: Number of processors belonging to the domain for this logical + * processor’s P-states + * @coordtype: Coordination type + */ +void acpigen_write_psd_package(struct acpi_ctx *ctx, uint domain, uint numprocs, + enum psd_coord coordtype); + +/** + * acpigen_write_cst_package() - Write a _CST package + * + * See ACPI v6.3 section 8.4.2.1: _CST (C States) + * + * @ctx: ACPI context pointer + * @entry: Array of entries + * @nentries; Number of entries + */ +void acpigen_write_cst_package(struct acpi_ctx *ctx, + const struct acpi_cstate *entry, int nentries); + +/** + * acpigen_write_csd_package() - Write a _CSD Package + * + * See ACPI v6.3 section 8.4.2.2: _CSD (C-State Dependency) + * + * @ctx: ACPI context pointer + * @domain: dependency domain number to which this C state entry belongs + * @numprocs: number of processors belonging to the domain for the particular + * C-state + * @coordtype: Co-ordination type + * @index: Index of the C-State entry in the _CST object for which the + * dependency applies + */ +void acpigen_write_csd_package(struct acpi_ctx *ctx, uint domain, uint numprocs, + enum csd_coord coordtype, uint index); + +/** + * acpigen_write_tss_package() - Write a _TSS package + * + * @ctx: ACPI context pointer + * @entry: Entries to write + * @nentries: Number of entries to write + */ +void acpigen_write_tss_package(struct acpi_ctx *ctx, + struct acpi_tstate *entry, int nentries); + +/** + * acpigen_write_tsd_package() - Write a _TSD package + * + * See ACPI v6.3 section 8.4.5.4: _TSD (T-State Dependency) + * + * @ctx: ACPI context pointer + * @domain: dependency domain number to which this T state entry belongs + * @numprocs: Number of processors belonging to the domain for this logical + * processor’s T-states + * @coordtype: Coordination type + */ +void acpigen_write_tsd_package(struct acpi_ctx *ctx, uint domain, uint numprocs, + enum psd_coord coordtype); + #endif diff --git a/lib/acpi/acpigen.c b/lib/acpi/acpigen.c index b9985075cde..e395226e3de 100644 --- a/lib/acpi/acpigen.c +++ b/lib/acpi/acpigen.c @@ -481,6 +481,53 @@ void acpigen_write_register_resource(struct acpi_ctx *ctx, acpigen_write_resourcetemplate_footer(ctx); } +void acpigen_write_ppc(struct acpi_ctx *ctx, uint num_pstates) +{ + /* + * Method (_PPC, 0, NotSerialized) + * { + * Return (num_pstates) + * } + */ + acpigen_write_method(ctx, "_PPC", 0); + acpigen_emit_byte(ctx, RETURN_OP); + acpigen_write_byte(ctx, num_pstates); + acpigen_pop_len(ctx); +} + +/* + * Generates a func with max supported P-states saved + * in the variable PPCM. + */ +void acpigen_write_ppc_nvs(struct acpi_ctx *ctx) +{ + /* + * Method (_PPC, 0, NotSerialized) + * { + * Return (PPCM) + * } + */ + acpigen_write_method(ctx, "_PPC", 0); + acpigen_emit_byte(ctx, RETURN_OP); + acpigen_emit_namestring(ctx, "PPCM"); + acpigen_pop_len(ctx); +} + +void acpigen_write_tpc(struct acpi_ctx *ctx, const char *gnvs_tpc_limit) +{ + /* + * // Sample _TPC method + * Method (_TPC, 0, NotSerialized) + * { + * Return (\TLVL) + * } + */ + acpigen_write_method(ctx, "_TPC", 0); + acpigen_emit_byte(ctx, RETURN_OP); + acpigen_emit_namestring(ctx, gnvs_tpc_limit); + acpigen_pop_len(ctx); +} + void acpigen_write_prw(struct acpi_ctx *ctx, uint wake, uint level) { /* Name (_PRW, Package () { wake, level } */ @@ -491,6 +538,126 @@ void acpigen_write_prw(struct acpi_ctx *ctx, uint wake, uint level) acpigen_pop_len(ctx); } +void acpigen_write_pss_package(struct acpi_ctx *ctx, u32 core_freq, u32 power, + u32 trans_lat, u32 busm_lat, u32 control, + u32 status) +{ + acpigen_write_package(ctx, 6); + acpigen_write_dword(ctx, core_freq); + acpigen_write_dword(ctx, power); + acpigen_write_dword(ctx, trans_lat); + acpigen_write_dword(ctx, busm_lat); + acpigen_write_dword(ctx, control); + acpigen_write_dword(ctx, status); + acpigen_pop_len(ctx); + + log_debug("PSS: %uMHz power %u control 0x%x status 0x%x\n", + core_freq, power, control, status); +} + +void acpigen_write_psd_package(struct acpi_ctx *ctx, uint domain, uint numprocs, + enum psd_coord coordtype) +{ + acpigen_write_name(ctx, "_PSD"); + acpigen_write_package(ctx, 1); + acpigen_write_package(ctx, 5); + acpigen_write_byte(ctx, 5); // 5 values + acpigen_write_byte(ctx, 0); // revision 0 + acpigen_write_dword(ctx, domain); + acpigen_write_dword(ctx, coordtype); + acpigen_write_dword(ctx, numprocs); + acpigen_pop_len(ctx); + acpigen_pop_len(ctx); +} + +static void acpigen_write_cst_package_entry(struct acpi_ctx *ctx, + const struct acpi_cstate *cstate) +{ + acpigen_write_package(ctx, 4); + acpigen_write_register_resource(ctx, &cstate->resource); + acpigen_write_dword(ctx, cstate->ctype); + acpigen_write_dword(ctx, cstate->latency); + acpigen_write_dword(ctx, cstate->power); + acpigen_pop_len(ctx); +} + +void acpigen_write_cst_package(struct acpi_ctx *ctx, + const struct acpi_cstate *cstate, int nentries) +{ + int i; + + acpigen_write_name(ctx, "_CST"); + acpigen_write_package(ctx, nentries + 1); + acpigen_write_dword(ctx, nentries); + + for (i = 0; i < nentries; i++) + acpigen_write_cst_package_entry(ctx, cstate + i); + + acpigen_pop_len(ctx); +} + +void acpigen_write_csd_package(struct acpi_ctx *ctx, uint domain, uint numprocs, + enum csd_coord coordtype, uint index) +{ + acpigen_write_name(ctx, "_CSD"); + acpigen_write_package(ctx, 1); + acpigen_write_package(ctx, 6); + acpigen_write_byte(ctx, 6); // 6 values + acpigen_write_byte(ctx, 0); // revision 0 + acpigen_write_dword(ctx, domain); + acpigen_write_dword(ctx, coordtype); + acpigen_write_dword(ctx, numprocs); + acpigen_write_dword(ctx, index); + acpigen_pop_len(ctx); + acpigen_pop_len(ctx); +} + +void acpigen_write_tss_package(struct acpi_ctx *ctx, + struct acpi_tstate *entry, int nentries) +{ + /* + * Sample _TSS package with 100% and 50% duty cycles + * Name (_TSS, Package (0x02) + * { + * Package(){100, 1000, 0, 0x00, 0) + * Package(){50, 520, 0, 0x18, 0) + * }) + */ + struct acpi_tstate *tstate = entry; + int i; + + acpigen_write_name(ctx, "_TSS"); + acpigen_write_package(ctx, nentries); + + for (i = 0; i < nentries; i++) { + acpigen_write_package(ctx, 5); + acpigen_write_dword(ctx, tstate->percent); + acpigen_write_dword(ctx, tstate->power); + acpigen_write_dword(ctx, tstate->latency); + acpigen_write_dword(ctx, tstate->control); + acpigen_write_dword(ctx, tstate->status); + acpigen_pop_len(ctx); + tstate++; + } + + acpigen_pop_len(ctx); +} + +void acpigen_write_tsd_package(struct acpi_ctx *ctx, u32 domain, u32 numprocs, + enum psd_coord coordtype) +{ + acpigen_write_name(ctx, "_TSD"); + acpigen_write_package(ctx, 1); + acpigen_write_package(ctx, 5); + acpigen_write_byte(ctx, 5); // 5 values + acpigen_write_byte(ctx, 0); // revision 0 + acpigen_write_dword(ctx, domain); + acpigen_write_dword(ctx, coordtype); + acpigen_write_dword(ctx, numprocs); + acpigen_pop_len(ctx); + acpigen_pop_len(ctx); +} + /* * ToUUID(uuid) * diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index 74b7e23aab3..3ec2743af9f 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -1450,3 +1450,297 @@ static int dm_test_acpi_write_processor_cnot(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_write_processor_cnot, 0); + +/* Test acpigen_write_tpc */ +static int dm_test_acpi_write_tpc(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_write_tpc(ctx, "\\TLVL"); + + ut_asserteq(METHOD_OP, *ptr++); + ptr += 3; /* skip length */ + ut_asserteq_strn("_TPC", (char *)ptr); + ptr += 4; + ut_asserteq(0, *ptr++); + ut_asserteq(RETURN_OP, *ptr++); + ut_asserteq_strn("\\TLVL", (char *)ptr); + ptr += 5; + + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_write_tpc, 0); + +/* Test acpigen_write_pss_package(), etc. */ +static int dm_test_acpi_write_pss_psd(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_write_pss_package(ctx, 1, 2, 3, 4, 5, 6); + ut_asserteq(PACKAGE_OP, *ptr++); + ptr += 3; /* skip length */ + ut_asserteq(6, *ptr++); + + ut_asserteq(DWORD_PREFIX, *ptr++); + ut_asserteq(1, get_unaligned((u32 *)ptr)); + ptr += 5; + + ut_asserteq(2, get_unaligned((u32 *)ptr)); + ptr += 5; + + ut_asserteq(3, get_unaligned((u32 *)ptr)); + ptr += 5; + + ut_asserteq(4, get_unaligned((u32 *)ptr)); + ptr += 5; + + ut_asserteq(5, get_unaligned((u32 *)ptr)); + ptr += 5; + + ut_asserteq(6, get_unaligned((u32 *)ptr)); + ptr += 4; + + acpigen_write_psd_package(ctx, 6, 7, HW_ALL); + ut_asserteq(NAME_OP, *ptr++); + ut_asserteq_strn("_PSD", (char *)ptr); + ptr += 4; + ut_asserteq(PACKAGE_OP, *ptr++); + ptr += 3; /* skip length */ + ut_asserteq(1, *ptr++); + ut_asserteq(PACKAGE_OP, *ptr++); + ptr += 3; /* skip length */ + ut_asserteq(5, *ptr++); + + ut_asserteq(BYTE_PREFIX, *ptr++); + ut_asserteq(5, *ptr++); + ut_asserteq(BYTE_PREFIX, *ptr++); + ut_asserteq(0, *ptr++); + + ut_asserteq(DWORD_PREFIX, *ptr++); + ut_asserteq(6, get_unaligned((u32 *)ptr)); + ptr += 5; + + ut_asserteq(HW_ALL, get_unaligned((u32 *)ptr)); + ptr += 5; + + ut_asserteq(7, get_unaligned((u32 *)ptr)); + ptr += 4; + + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_write_pss_psd, 0); + +/* Test acpi_write_cst_package() */ +static int dm_test_acpi_write_cst(struct unit_test_state *uts) +{ + static struct acpi_cstate cstate_map[] = { + { + /* C1 */ + .ctype = 1, /* ACPI C1 */ + .latency = 1, + .power = 1000, + .resource = { + .space_id = ACPI_ADDRESS_SPACE_FIXED, + }, + }, { + .ctype = 2, /* ACPI C2 */ + .latency = 50, + .power = 10, + .resource = { + .space_id = ACPI_ADDRESS_SPACE_IO, + .bit_width = 8, + .addrl = 0x415, + }, + }, + }; + int nentries = ARRAY_SIZE(cstate_map); + struct acpi_ctx *ctx; + u8 *ptr; + int i; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_write_cst_package(ctx, cstate_map, nentries); + + ut_asserteq(NAME_OP, *ptr++); + ut_asserteq_strn("_CST", (char *)ptr); + ptr += 4; + ut_asserteq(PACKAGE_OP, *ptr++); + ptr += 3; /* skip length */ + ut_asserteq(nentries + 1, *ptr++); + ut_asserteq(DWORD_PREFIX, *ptr++); + ut_asserteq(nentries, get_unaligned((u32 *)ptr)); + ptr += 4; + + for (i = 0; i < nentries; i++) { + ut_asserteq(PACKAGE_OP, *ptr++); + ptr += 3; /* skip length */ + ut_asserteq(4, *ptr++); + ut_asserteq(BUFFER_OP, *ptr++); + ptr += 0x17; + ut_asserteq(DWORD_PREFIX, *ptr++); + ut_asserteq(cstate_map[i].ctype, get_unaligned((u32 *)ptr)); + ptr += 5; + ut_asserteq(cstate_map[i].latency, get_unaligned((u32 *)ptr)); + ptr += 5; + ut_asserteq(cstate_map[i].power, get_unaligned((u32 *)ptr)); + ptr += 4; + } + + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_write_cst, 0); + +/* Test acpi_write_cst_package() */ +static int dm_test_acpi_write_csd(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_write_csd_package(ctx, 12, 34, CSD_HW_ALL, 56); + + ut_asserteq(NAME_OP, *ptr++); + ut_asserteq_strn("_CSD", (char *)ptr); + ptr += 4; + ut_asserteq(PACKAGE_OP, *ptr++); + ptr += 3; /* skip length */ + ut_asserteq(1, *ptr++); + ut_asserteq(PACKAGE_OP, *ptr++); + ptr += 3; /* skip length */ + ut_asserteq(6, *ptr++); + + ut_asserteq(BYTE_PREFIX, *ptr++); + ut_asserteq(6, *ptr++); + ut_asserteq(BYTE_PREFIX, *ptr++); + ut_asserteq(0, *ptr++); + ut_asserteq(DWORD_PREFIX, *ptr++); + ut_asserteq(12, get_unaligned((u32 *)ptr)); + ptr += 5; + ut_asserteq(CSD_HW_ALL, get_unaligned((u32 *)ptr)); + ptr += 5; + ut_asserteq(34, get_unaligned((u32 *)ptr)); + ptr += 5; + ut_asserteq(56, get_unaligned((u32 *)ptr)); + ptr += 4; + + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_write_csd, 0); + +/* Test acpigen_write_tss_package() */ +static int dm_test_acpi_write_tss(struct unit_test_state *uts) +{ + static struct acpi_tstate tstate_list[] = { + { 1, 2, 3, 4, 5, }, + { 6, 7, 8, 9, 10, }, + }; + int nentries = ARRAY_SIZE(tstate_list); + struct acpi_ctx *ctx; + u8 *ptr; + int i; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_write_tss_package(ctx, tstate_list, nentries); + + ut_asserteq(NAME_OP, *ptr++); + ut_asserteq_strn("_TSS", (char *)ptr); + ptr += 4; + ut_asserteq(PACKAGE_OP, *ptr++); + ptr += 3; /* skip length */ + ut_asserteq(nentries, *ptr++); + + for (i = 0; i < nentries; i++) { + ut_asserteq(PACKAGE_OP, *ptr++); + ptr += 3; /* skip length */ + ut_asserteq(5, *ptr++); + ut_asserteq(DWORD_PREFIX, *ptr++); + ut_asserteq(tstate_list[i].percent, get_unaligned((u32 *)ptr)); + ptr += 5; + ut_asserteq(tstate_list[i].power, get_unaligned((u32 *)ptr)); + ptr += 5; + ut_asserteq(tstate_list[i].latency, get_unaligned((u32 *)ptr)); + ptr += 5; + ut_asserteq(tstate_list[i].control, get_unaligned((u32 *)ptr)); + ptr += 5; + ut_asserteq(tstate_list[i].status, get_unaligned((u32 *)ptr)); + ptr += 4; + } + + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_write_tss, 0); + +/* Test acpigen_write_tsd_package() */ +static int dm_test_acpi_write_tsd_package(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_write_tsd_package(ctx, 12, 34, HW_ALL); + + ut_asserteq(NAME_OP, *ptr++); + ut_asserteq_strn("_TSD", (char *)ptr); + ptr += 4; + ut_asserteq(PACKAGE_OP, *ptr++); + ptr += 3; /* skip length */ + ut_asserteq(1, *ptr++); + ut_asserteq(PACKAGE_OP, *ptr++); + ptr += 3; /* skip length */ + ut_asserteq(5, *ptr++); + + ut_asserteq(BYTE_PREFIX, *ptr++); + ut_asserteq(5, *ptr++); + ut_asserteq(BYTE_PREFIX, *ptr++); + ut_asserteq(0, *ptr++); + ut_asserteq(DWORD_PREFIX, *ptr++); + ut_asserteq(12, get_unaligned((u32 *)ptr)); + ptr += 5; + ut_asserteq(CSD_HW_ALL, get_unaligned((u32 *)ptr)); + ptr += 5; + ut_asserteq(34, get_unaligned((u32 *)ptr)); + ptr += 4; + + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_write_tsd_package, 0); -- cgit v1.3.1