summaryrefslogtreecommitdiff
path: root/lib/acpi
diff options
context:
space:
mode:
authorSimon Glass <sjg@chromium.org>2020-07-07 13:12:02 -0600
committerBin Meng <bmeng.cn@gmail.com>2020-07-17 14:32:24 +0800
commit740630ba73768667a2f87326f2a237d373a5093d (patch)
tree65f543df914a485cc6f0ce3e5ebdeaaa34559fcc /lib/acpi
parentf8054dd8ba533be0e2ebd7965b95446a1f573953 (diff)
downloadu-boot-740630ba73768667a2f87326f2a237d373a5093d.tar.gz
u-boot-740630ba73768667a2f87326f2a237d373a5093d.tar.bz2
u-boot-740630ba73768667a2f87326f2a237d373a5093d.zip
acpi: Add support for a generic power sequence
Add a way for devices to enable and disable themselves using ACPI code that updates GPIOs. This takes several timing parameters and supports enable, reset and stop. 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>
Diffstat (limited to 'lib/acpi')
-rw-r--r--lib/acpi/acpi_device.c99
1 files changed, 99 insertions, 0 deletions
diff --git a/lib/acpi/acpi_device.c b/lib/acpi/acpi_device.c
index b628fa1337..c66cafcfee 100644
--- a/lib/acpi/acpi_device.c
+++ b/lib/acpi/acpi_device.c
@@ -386,6 +386,105 @@ int acpi_device_write_interrupt_or_gpio(struct acpi_ctx *ctx,
return pin;
}
+/* PowerResource() with Enable and/or Reset control */
+int acpi_device_add_power_res(struct acpi_ctx *ctx, u32 tx_state_val,
+ const char *dw0_read, const char *dw0_write,
+ const struct gpio_desc *reset_gpio,
+ uint reset_delay_ms, uint reset_off_delay_ms,
+ const struct gpio_desc *enable_gpio,
+ uint enable_delay_ms, uint enable_off_delay_ms,
+ const struct gpio_desc *stop_gpio,
+ uint stop_delay_ms, uint stop_off_delay_ms)
+{
+ static const char *const power_res_dev_states[] = { "_PR0", "_PR3" };
+ struct acpi_gpio reset, enable, stop;
+ bool has_reset, has_enable, has_stop;
+ int ret;
+
+ gpio_get_acpi(reset_gpio, &reset);
+ gpio_get_acpi(enable_gpio, &enable);
+ gpio_get_acpi(stop_gpio, &stop);
+ has_reset = reset.pins[0];
+ has_enable = enable.pins[0];
+ has_stop = stop.pins[0];
+
+ if (!has_reset && !has_enable && !has_stop)
+ return -EINVAL;
+
+ /* PowerResource (PRIC, 0, 0) */
+ acpigen_write_power_res(ctx, "PRIC", 0, 0, power_res_dev_states,
+ ARRAY_SIZE(power_res_dev_states));
+
+ /* Method (_STA, 0, NotSerialized) { Return (0x1) } */
+ acpigen_write_sta(ctx, 0x1);
+
+ /* Method (_ON, 0, Serialized) */
+ acpigen_write_method_serialized(ctx, "_ON", 0);
+ if (reset_gpio) {
+ ret = acpigen_set_enable_tx_gpio(ctx, tx_state_val, dw0_read,
+ dw0_write, &reset, true);
+ if (ret)
+ return log_msg_ret("reset1", ret);
+ }
+ if (has_enable) {
+ ret = acpigen_set_enable_tx_gpio(ctx, tx_state_val, dw0_read,
+ dw0_write, &enable, true);
+ if (ret)
+ return log_msg_ret("enable1", ret);
+ if (enable_delay_ms)
+ acpigen_write_sleep(ctx, enable_delay_ms);
+ }
+ if (has_reset) {
+ ret = acpigen_set_enable_tx_gpio(ctx, tx_state_val, dw0_read,
+ dw0_write, &reset, false);
+ if (ret)
+ return log_msg_ret("reset2", ret);
+ if (reset_delay_ms)
+ acpigen_write_sleep(ctx, reset_delay_ms);
+ }
+ if (has_stop) {
+ ret = acpigen_set_enable_tx_gpio(ctx, tx_state_val, dw0_read,
+ dw0_write, &stop, false);
+ if (ret)
+ return log_msg_ret("stop1", ret);
+ if (stop_delay_ms)
+ acpigen_write_sleep(ctx, stop_delay_ms);
+ }
+ acpigen_pop_len(ctx); /* _ON method */
+
+ /* Method (_OFF, 0, Serialized) */
+ acpigen_write_method_serialized(ctx, "_OFF", 0);
+ if (has_stop) {
+ ret = acpigen_set_enable_tx_gpio(ctx, tx_state_val, dw0_read,
+ dw0_write, &stop, true);
+ if (ret)
+ return log_msg_ret("stop2", ret);
+ if (stop_off_delay_ms)
+ acpigen_write_sleep(ctx, stop_off_delay_ms);
+ }
+ if (has_reset) {
+ ret = acpigen_set_enable_tx_gpio(ctx, tx_state_val, dw0_read,
+ dw0_write, &reset, true);
+ if (ret)
+ return log_msg_ret("reset3", ret);
+ if (reset_off_delay_ms)
+ acpigen_write_sleep(ctx, reset_off_delay_ms);
+ }
+ if (has_enable) {
+ ret = acpigen_set_enable_tx_gpio(ctx, tx_state_val, dw0_read,
+ dw0_write, &enable, false);
+ if (ret)
+ return log_msg_ret("enable2", ret);
+ if (enable_off_delay_ms)
+ acpigen_write_sleep(ctx, enable_off_delay_ms);
+ }
+ acpigen_pop_len(ctx); /* _OFF method */
+
+ acpigen_pop_len(ctx); /* PowerResource PRIC */
+
+ 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)