acpi: Add support for writing a GPIO power sequence
Power to some devices is controlled by GPIOs. Add a way to generate ACPI
code to enable and disable a GPIO so that this can be handled within an
ACPI method.
Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Wolfgang Wallner <wolfgang.wallner@br-automation.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
diff --git a/lib/acpi/acpigen.c b/lib/acpi/acpigen.c
index 70d7342..45216c1 100644
--- a/lib/acpi/acpigen.c
+++ b/lib/acpi/acpigen.c
@@ -13,6 +13,7 @@
#include <log.h>
#include <uuid.h>
#include <acpi/acpigen.h>
+#include <acpi/acpi_device.h>
#include <dm/acpi.h>
u8 *acpigen_get_current(struct acpi_ctx *ctx)
@@ -395,3 +396,87 @@
acpigen_write_string(ctx, str);
acpigen_emit_ext_op(ctx, DEBUG_OP);
}
+
+/**
+ * acpigen_get_dw0_in_local5() - Generate code to put dw0 cfg0 in local5
+ *
+ * Store (\_SB.GPC0 (addr), Local5)
+ *
+ * \_SB.GPC0 is used to read cfg0 value from dw0. It is typically defined in
+ * the board's gpiolib.asl
+ *
+ * The value needs to be stored in a local variable so that it can be used in
+ * expressions in the ACPI code.
+ *
+ * @ctx: ACPI context pointer
+ * @dw0_read: Name to use to read dw0, e.g. "\\_SB.GPC0"
+ * @addr: GPIO pin configuration register address
+ *
+ */
+static void acpigen_get_dw0_in_local5(struct acpi_ctx *ctx,
+ const char *dw0_read, ulong addr)
+{
+ acpigen_write_store(ctx);
+ acpigen_emit_namestring(ctx, dw0_read);
+ acpigen_write_integer(ctx, addr);
+ acpigen_emit_byte(ctx, LOCAL5_OP);
+}
+
+/**
+ * acpigen_set_gpio_val() - Emit code to set value of TX GPIO to on/off
+ *
+ * @ctx: ACPI context pointer
+ * @dw0_read: Method name to use to read dw0, e.g. "\\_SB.GPC0"
+ * @dw0_write: Method name to use to read dw0, e.g. "\\_SB.SPC0"
+ * @gpio_num: GPIO number to adjust
+ * @vaL: true to set on, false to set off
+ */
+static int acpigen_set_gpio_val(struct acpi_ctx *ctx, u32 tx_state_val,
+ const char *dw0_read, const char *dw0_write,
+ struct acpi_gpio *gpio, bool val)
+{
+ acpigen_get_dw0_in_local5(ctx, dw0_read, gpio->pin0_addr);
+
+ /* Store (0x40, Local0) */
+ acpigen_write_store(ctx);
+ acpigen_write_integer(ctx, tx_state_val);
+ acpigen_emit_byte(ctx, LOCAL0_OP);
+
+ if (val) {
+ /* Or (Local5, PAD_CFG0_TX_STATE, Local5) */
+ acpigen_write_or(ctx, LOCAL5_OP, LOCAL0_OP, LOCAL5_OP);
+ } else {
+ /* Not (PAD_CFG0_TX_STATE, Local6) */
+ acpigen_write_not(ctx, LOCAL0_OP, LOCAL6_OP);
+
+ /* And (Local5, Local6, Local5) */
+ acpigen_write_and(ctx, LOCAL5_OP, LOCAL6_OP, LOCAL5_OP);
+ }
+
+ /*
+ * \_SB.SPC0 (addr, Local5)
+ * \_SB.SPC0 is used to write cfg0 value in dw0. It is defined in
+ * gpiolib.asl.
+ */
+ acpigen_emit_namestring(ctx, dw0_write);
+ acpigen_write_integer(ctx, gpio->pin0_addr);
+ acpigen_emit_byte(ctx, LOCAL5_OP);
+
+ return 0;
+}
+
+int acpigen_set_enable_tx_gpio(struct acpi_ctx *ctx, u32 tx_state_val,
+ const char *dw0_read, const char *dw0_write,
+ struct acpi_gpio *gpio, bool enable)
+{
+ bool set;
+ int ret;
+
+ set = gpio->polarity == ACPI_GPIO_ACTIVE_HIGH ? enable : !enable;
+ ret = acpigen_set_gpio_val(ctx, tx_state_val, dw0_read, dw0_write, gpio,
+ set);
+ if (ret)
+ return log_msg_ret("call", ret);
+
+ return 0;
+}