mirror of
https://github.com/u-boot/u-boot.git
synced 2025-04-16 09:54:35 +00:00
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 <sjg@chromium.org> [bmeng: fix the "new blank line at EOF" git warning] Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
This commit is contained in:
parent
da7cff338f
commit
88490e1979
5 changed files with 335 additions and 0 deletions
|
@ -28,6 +28,9 @@ struct udevice;
|
||||||
/* Length of a full path to an ACPI device */
|
/* Length of a full path to an ACPI device */
|
||||||
#define ACPI_PATH_MAX 30
|
#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 */
|
/* Values that can be returned for ACPI device _STA method */
|
||||||
enum acpi_dev_status {
|
enum acpi_dev_status {
|
||||||
ACPI_DSTATUS_PRESENT = BIT(0),
|
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,
|
int acpi_device_write_interrupt_or_gpio(struct acpi_ctx *ctx,
|
||||||
struct udevice *dev, const char *prop);
|
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
|
* acpi_device_write_i2c_dev() - Write an I2C device to ACPI
|
||||||
*
|
*
|
||||||
|
|
|
@ -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);
|
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 <generate-code-here> 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
|
||||||
|
* <generate-code-here>
|
||||||
|
* } -- acpigen_write_dsm_uuid_end_cond
|
||||||
|
* ...
|
||||||
|
* If (LEqual (Local1, n)) { -- acpigen_write_dsm_uuid_start_cond
|
||||||
|
* <generate-code-here>
|
||||||
|
* } -- 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
|
#endif
|
||||||
|
|
|
@ -487,6 +487,49 @@ int acpi_device_add_power_res(struct acpi_ctx *ctx, u32 tx_state_val,
|
||||||
return 0;
|
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() */
|
/* ACPI 6.3 section 6.4.3.8.2.1 - I2cSerialBus() */
|
||||||
static void acpi_device_write_i2c(struct acpi_ctx *ctx,
|
static void acpi_device_write_i2c(struct acpi_ctx *ctx,
|
||||||
const struct acpi_i2c *i2c)
|
const struct acpi_i2c *i2c)
|
||||||
|
|
|
@ -609,6 +609,60 @@ void acpigen_write_return_byte(struct acpi_ctx *ctx, uint arg)
|
||||||
acpigen_write_byte(ctx, 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
|
* acpigen_get_dw0_in_local5() - Generate code to put dw0 cfg0 in local5
|
||||||
*
|
*
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <dm.h>
|
#include <dm.h>
|
||||||
#include <irq.h>
|
#include <irq.h>
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
|
#include <uuid.h>
|
||||||
#include <acpi/acpigen.h>
|
#include <acpi/acpigen.h>
|
||||||
#include <acpi/acpi_device.h>
|
#include <acpi/acpi_device.h>
|
||||||
#include <acpi/acpi_table.h>
|
#include <acpi/acpi_table.h>
|
||||||
|
@ -1218,3 +1219,127 @@ static int dm_test_acpi_write_return(struct unit_test_state *uts)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
DM_TEST(dm_test_acpi_write_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);
|
||||||
|
|
Loading…
Add table
Reference in a new issue